detached 1adbe2f

Merge pull request #87 from bnema/fix/openai-fast-mode-subagents

fix(subagents): wire per-spawn fast mode
commit
1adbe2f3006adb59a0bdf960b190659f03b5ad95
author
bnema · · 2026-08-08 15:46 UTC
committer
GitHub · · 2026-08-08 15:46 UTC
merge of
b9ff687 + cab56f9 · diff against the first parent
signature
GPG UnverifiedB5690EEEBB952194 The key is unknown
refs
tag: v0.12.2
4 files · +76 -6
M README.md +1 -1
@@ -405,7 +405,7 @@ Opens a dialog with every persistent setting. `up`/`down` to navigate, `enter` o
405405 - **render images when supported** — draw screenshots / `read`-returned images inline using the terminal's image protocol, or fall back to a text placeholder. Auto-detected from `TERM_PROGRAM`; the toggle overrides the detection. The row is greyed out and forced off on terminals that don't speak any image protocol.
406406 - **terminal alerts** — emit a terminal bell when the main session stops with work that may need attention, or when an extension raises a structured alert. Enabled by default; changes apply immediately and persist as `terminal_alerts_enabled`. Terminal emulators may render the bell audibly, visually, or not at all.
407407 - **AI terminal titles** — after the first real prompt of a fresh interactive session, make one small hidden request to the active model and set the terminal title to `zut: <title>`. The title is limited to 40 Unicode characters, persisted with the session, restored on resume, and never added to the conversation. Enabled by default; disable it to avoid the extra model request. The toggle applies immediately, but title generation still waits for the first real prompt and never starts from startup context, resumed history, or slash commands. Persists as `terminal_title_enabled`.
408-- **auto-subagents** — enable strict primary-agent orchestrator mode: delegate implementation, debugging/testing, and code-review work to named or general workers; the primary agent coordinates but does not make direct implementation changes. It can spawn background sub-agents via `subagent_spawn` and query live state via `subagent_status` when those tools are permitted. If delegation is unavailable because of launch-time restrictions, the contract remains active, profile metadata is withheld, and the primary reports the limitation rather than implementing directly. Off by default. When off, the permitted canonical subagent tools and profile manifest remain available for user-requested delegation or when an active skill workflow requires it; the primary otherwise works directly without the strict orchestrator contract. The tools accept named profiles, reasoning, timeout, turn limits, and shared/worktree isolation where applicable; lifecycle and result references are persisted. Completion is host-event-driven: `[auto-subagents update]` completion updates are the only signal; do not poll or inspect dashboards, metadata, event logs, or files solely to wait. See `/subagents` and [docs/subagents.md](docs/subagents.md) for details.
408+- **auto-subagents** — enable strict primary-agent orchestrator mode: delegate implementation, debugging/testing, and code-review work to named or general workers; the primary agent coordinates but does not make direct implementation changes. It can spawn background sub-agents via `subagent_spawn` and query live state via `subagent_status` when those tools are permitted. If delegation is unavailable because of launch-time restrictions, the contract remains active, profile metadata is withheld, and the primary reports the limitation rather than implementing directly. Off by default. When off, the permitted canonical subagent tools and profile manifest remain available for user-requested delegation or when an active skill workflow requires it; the primary otherwise works directly without the strict orchestrator contract. The tools accept named profiles, reasoning, per-spawn fast-mode overrides, timeout, turn limits, and shared/worktree isolation where applicable; lifecycle and result references are persisted. Completion is host-event-driven: `[auto-subagents update]` completion updates are the only signal; do not poll or inspect dashboards, metadata, event logs, or files solely to wait. See `/subagents` and [docs/subagents.md](docs/subagents.md) for details.
409409 - **Ponytail coding mode** — include compact engineering guidance in each resolved system prompt; the guidance tells the model to apply it to coding, debugging, and review work rather than ordinary conversation. It favors understanding the real flow, small validated changes, reuse, and preserving safety checks. Enabled by default; changes apply to the next model call and persist as `ponytail_enabled` in `$ZUT_HOME/config.json`. It is included by interactive, print, stream, JSON, RPC, subagent, bot, SDK, and Zutfile agents.
410410 - **fast mode** — request the provider's fast tier where supported. It currently uses OpenAI's Fast service tier for OpenAI, OpenAI Responses, and OpenAI Codex models. Off by default; toggle it with `/fast` or `/settings`; changes apply on the next model call and persist as `fast_mode`. Other providers currently return an unsupported-provider error. Fast mode may cost more and depends on the selected model/account.
411411 - **lsp in main session** — enable the built-in `lsp` tool and code diagnostics for the main agent. Enabled by default; changes persist as `lsp_enabled`.
@@ -405,7 +405,7 @@ Opens a dialog with every persistent setting. `up`/`down` to navigate, `enter` o
405 - **render images when supported** — draw screenshots / `read`-returned images inline using the terminal's image protocol, or fall back to a text placeholder. Auto-detected from `TERM_PROGRAM`; the toggle overrides the detection. The row is greyed out and forced off on terminals that don't speak any image protocol.405 - **render images when supported** — draw screenshots / `read`-returned images inline using the terminal's image protocol, or fall back to a text placeholder. Auto-detected from `TERM_PROGRAM`; the toggle overrides the detection. The row is greyed out and forced off on terminals that don't speak any image protocol.
406 - **terminal alerts** — emit a terminal bell when the main session stops with work that may need attention, or when an extension raises a structured alert. Enabled by default; changes apply immediately and persist as `terminal_alerts_enabled`. Terminal emulators may render the bell audibly, visually, or not at all.406 - **terminal alerts** — emit a terminal bell when the main session stops with work that may need attention, or when an extension raises a structured alert. Enabled by default; changes apply immediately and persist as `terminal_alerts_enabled`. Terminal emulators may render the bell audibly, visually, or not at all.
407 - **AI terminal titles** — after the first real prompt of a fresh interactive session, make one small hidden request to the active model and set the terminal title to `zut: <title>`. The title is limited to 40 Unicode characters, persisted with the session, restored on resume, and never added to the conversation. Enabled by default; disable it to avoid the extra model request. The toggle applies immediately, but title generation still waits for the first real prompt and never starts from startup context, resumed history, or slash commands. Persists as `terminal_title_enabled`.407 - **AI terminal titles** — after the first real prompt of a fresh interactive session, make one small hidden request to the active model and set the terminal title to `zut: <title>`. The title is limited to 40 Unicode characters, persisted with the session, restored on resume, and never added to the conversation. Enabled by default; disable it to avoid the extra model request. The toggle applies immediately, but title generation still waits for the first real prompt and never starts from startup context, resumed history, or slash commands. Persists as `terminal_title_enabled`.
408-- **auto-subagents** — enable strict primary-agent orchestrator mode: delegate implementation, debugging/testing, and code-review work to named or general workers; the primary agent coordinates but does not make direct implementation changes. It can spawn background sub-agents via `subagent_spawn` and query live state via `subagent_status` when those tools are permitted. If delegation is unavailable because of launch-time restrictions, the contract remains active, profile metadata is withheld, and the primary reports the limitation rather than implementing directly. Off by default. When off, the permitted canonical subagent tools and profile manifest remain available for user-requested delegation or when an active skill workflow requires it; the primary otherwise works directly without the strict orchestrator contract. The tools accept named profiles, reasoning, timeout, turn limits, and shared/worktree isolation where applicable; lifecycle and result references are persisted. Completion is host-event-driven: `[auto-subagents update]` completion updates are the only signal; do not poll or inspect dashboards, metadata, event logs, or files solely to wait. See `/subagents` and [docs/subagents.md](docs/subagents.md) for details.408+- **auto-subagents** — enable strict primary-agent orchestrator mode: delegate implementation, debugging/testing, and code-review work to named or general workers; the primary agent coordinates but does not make direct implementation changes. It can spawn background sub-agents via `subagent_spawn` and query live state via `subagent_status` when those tools are permitted. If delegation is unavailable because of launch-time restrictions, the contract remains active, profile metadata is withheld, and the primary reports the limitation rather than implementing directly. Off by default. When off, the permitted canonical subagent tools and profile manifest remain available for user-requested delegation or when an active skill workflow requires it; the primary otherwise works directly without the strict orchestrator contract. The tools accept named profiles, reasoning, per-spawn fast-mode overrides, timeout, turn limits, and shared/worktree isolation where applicable; lifecycle and result references are persisted. Completion is host-event-driven: `[auto-subagents update]` completion updates are the only signal; do not poll or inspect dashboards, metadata, event logs, or files solely to wait. See `/subagents` and [docs/subagents.md](docs/subagents.md) for details.
409 - **Ponytail coding mode** — include compact engineering guidance in each resolved system prompt; the guidance tells the model to apply it to coding, debugging, and review work rather than ordinary conversation. It favors understanding the real flow, small validated changes, reuse, and preserving safety checks. Enabled by default; changes apply to the next model call and persist as `ponytail_enabled` in `$ZUT_HOME/config.json`. It is included by interactive, print, stream, JSON, RPC, subagent, bot, SDK, and Zutfile agents.409 - **Ponytail coding mode** — include compact engineering guidance in each resolved system prompt; the guidance tells the model to apply it to coding, debugging, and review work rather than ordinary conversation. It favors understanding the real flow, small validated changes, reuse, and preserving safety checks. Enabled by default; changes apply to the next model call and persist as `ponytail_enabled` in `$ZUT_HOME/config.json`. It is included by interactive, print, stream, JSON, RPC, subagent, bot, SDK, and Zutfile agents.
410 - **fast mode** — request the provider's fast tier where supported. It currently uses OpenAI's Fast service tier for OpenAI, OpenAI Responses, and OpenAI Codex models. Off by default; toggle it with `/fast` or `/settings`; changes apply on the next model call and persist as `fast_mode`. Other providers currently return an unsupported-provider error. Fast mode may cost more and depends on the selected model/account.410 - **fast mode** — request the provider's fast tier where supported. It currently uses OpenAI's Fast service tier for OpenAI, OpenAI Responses, and OpenAI Codex models. Off by default; toggle it with `/fast` or `/settings`; changes apply on the next model call and persist as `fast_mode`. Other providers currently return an unsupported-provider error. Fast mode may cost more and depends on the selected model/account.
411 - **lsp in main session** — enable the built-in `lsp` tool and code diagnostics for the main agent. Enabled by default; changes persist as `lsp_enabled`.411 - **lsp in main session** — enable the built-in `lsp` tool and code diagnostics for the main agent. Enabled by default; changes persist as `lsp_enabled`.
M docs/subagents.md +5 -4
@@ -85,17 +85,18 @@ This profile metadata is a tool-selection boundary, not a general network sandbo
8585
8686 Completion is host-event-driven. After spawning, never use `bash sleep`, `watch`, `tail -f`, polling loops, repeated `subagent_status`, or dashboard, metadata, event-log, or file checks solely to wait; those are not completion signals. The primary may work on unrelated independent tasks, but otherwise must end or yield its turn until the host injects `[auto-subagents update]`. Completion updates are the only completion signal. Legitimate waits inside user-requested commands, provider flows, extensions, or tests are not prohibited.
8787
88-A per-spawn reasoning override is also accepted:
88+Per-spawn reasoning and fast-mode overrides are also accepted:
8989
9090 ```json
9191 {
9292 "task": "Implement the parser change and add regression tests.",
9393 "agent": "implementer",
94- "reasoning": "high"
94+ "reasoning": "high",
95+ "fast_mode": true
9596 }
9697 ```
9798
98-`thinking` is accepted as an alias for `reasoning`. If neither is supplied, the child inherits the host reasoning level for an unnamed spawn, or the profile's `thinking` value for a named spawn.
99+`thinking` is accepted as an alias for `reasoning`. If neither is supplied, the child inherits the host reasoning level for an unnamed spawn, or the profile's `thinking` value for a named spawn. An explicit `fast_mode` value takes precedence over the selected profile and host setting; omit it to inherit them.
99100
100101 The interactive command also supports the same selection explicitly:
101102
@@ -104,7 +105,7 @@ The interactive command also supports the same selection explicitly:
104105 /subagents new --agent implementer --reasoning high Implement the parser change
105106 ```
106107
107-Shared mode preserves the historical host working directory, so workers there must be coordinated to avoid conflicting edits and to sequence dependent tasks. For parallel coding, pass `isolation:"worktree"` to `subagent_spawn`; zut creates a detached Git worktree and captures changed files and a patch without merging automatically. In orchestrator mode, assign any worktree patch integration to a worker rather than applying it in the primary session. Named profiles change the child's instructions and configuration; they are not a security sandbox. A profile's `systemPromptMode` controls its own body relative to the built-in identity; globally appended instructions, including enabled Ponytail coding guidance, remain present. Child credentials are transferred over stdin rather than argv or persisted metadata, and the active provider endpoint/TLS setting is inherited only when the child uses that provider. Fast mode is inherited by default. A profile with `fastMode: false` opts out, while `fastMode: true` enables fast mode even when the host setting is off; the `subagent_spawn` result warns the parent session when this override occurs. Child providers that do not yet support fast mode return an unsupported-provider error instead of silently ignoring an enabled setting.
108+Shared mode preserves the historical host working directory, so workers there must be coordinated to avoid conflicting edits and to sequence dependent tasks. For parallel coding, pass `isolation:"worktree"` to `subagent_spawn`; zut creates a detached Git worktree and captures changed files and a patch without merging automatically. In orchestrator mode, assign any worktree patch integration to a worker rather than applying it in the primary session. Named profiles change the child's instructions and configuration; they are not a security sandbox. A profile's `systemPromptMode` controls its own body relative to the built-in identity; globally appended instructions, including enabled Ponytail coding guidance, remain present. Child credentials are transferred over stdin rather than argv or persisted metadata, and the active provider endpoint/TLS setting is inherited only when the child uses that provider. Fast mode is inherited by default. An explicit `fast_mode` spawn argument has highest precedence. Otherwise, a profile with `fastMode: false` opts out, while `fastMode: true` enables fast mode even when the host setting is off; the `subagent_spawn` result warns the parent session when this profile override occurs. Child providers that do not yet support fast mode return an unsupported-provider error instead of silently ignoring an enabled setting.
108109
109110 ## Lifecycle, results, and recovery
110111
@@ -85,17 +85,18 @@ This profile metadata is a tool-selection boundary, not a general network sandbo
85 85
86 Completion is host-event-driven. After spawning, never use `bash sleep`, `watch`, `tail -f`, polling loops, repeated `subagent_status`, or dashboard, metadata, event-log, or file checks solely to wait; those are not completion signals. The primary may work on unrelated independent tasks, but otherwise must end or yield its turn until the host injects `[auto-subagents update]`. Completion updates are the only completion signal. Legitimate waits inside user-requested commands, provider flows, extensions, or tests are not prohibited.86 Completion is host-event-driven. After spawning, never use `bash sleep`, `watch`, `tail -f`, polling loops, repeated `subagent_status`, or dashboard, metadata, event-log, or file checks solely to wait; those are not completion signals. The primary may work on unrelated independent tasks, but otherwise must end or yield its turn until the host injects `[auto-subagents update]`. Completion updates are the only completion signal. Legitimate waits inside user-requested commands, provider flows, extensions, or tests are not prohibited.
87 87
88-A per-spawn reasoning override is also accepted:88+Per-spawn reasoning and fast-mode overrides are also accepted:
89 89
90 ```json90 ```json
91 {91 {
92 "task": "Implement the parser change and add regression tests.",92 "task": "Implement the parser change and add regression tests.",
93 "agent": "implementer",93 "agent": "implementer",
94- "reasoning": "high"94+ "reasoning": "high",
95+ "fast_mode": true
95 }96 }
96 ```97 ```
97 98
98-`thinking` is accepted as an alias for `reasoning`. If neither is supplied, the child inherits the host reasoning level for an unnamed spawn, or the profile's `thinking` value for a named spawn.99+`thinking` is accepted as an alias for `reasoning`. If neither is supplied, the child inherits the host reasoning level for an unnamed spawn, or the profile's `thinking` value for a named spawn. An explicit `fast_mode` value takes precedence over the selected profile and host setting; omit it to inherit them.
99 100
100 The interactive command also supports the same selection explicitly:101 The interactive command also supports the same selection explicitly:
101 102
@@ -104,7 +105,7 @@ The interactive command also supports the same selection explicitly:
104 /subagents new --agent implementer --reasoning high Implement the parser change105 /subagents new --agent implementer --reasoning high Implement the parser change
105 ```106 ```
106 107
107-Shared mode preserves the historical host working directory, so workers there must be coordinated to avoid conflicting edits and to sequence dependent tasks. For parallel coding, pass `isolation:"worktree"` to `subagent_spawn`; zut creates a detached Git worktree and captures changed files and a patch without merging automatically. In orchestrator mode, assign any worktree patch integration to a worker rather than applying it in the primary session. Named profiles change the child's instructions and configuration; they are not a security sandbox. A profile's `systemPromptMode` controls its own body relative to the built-in identity; globally appended instructions, including enabled Ponytail coding guidance, remain present. Child credentials are transferred over stdin rather than argv or persisted metadata, and the active provider endpoint/TLS setting is inherited only when the child uses that provider. Fast mode is inherited by default. A profile with `fastMode: false` opts out, while `fastMode: true` enables fast mode even when the host setting is off; the `subagent_spawn` result warns the parent session when this override occurs. Child providers that do not yet support fast mode return an unsupported-provider error instead of silently ignoring an enabled setting.108+Shared mode preserves the historical host working directory, so workers there must be coordinated to avoid conflicting edits and to sequence dependent tasks. For parallel coding, pass `isolation:"worktree"` to `subagent_spawn`; zut creates a detached Git worktree and captures changed files and a patch without merging automatically. In orchestrator mode, assign any worktree patch integration to a worker rather than applying it in the primary session. Named profiles change the child's instructions and configuration; they are not a security sandbox. A profile's `systemPromptMode` controls its own body relative to the built-in identity; globally appended instructions, including enabled Ponytail coding guidance, remain present. Child credentials are transferred over stdin rather than argv or persisted metadata, and the active provider endpoint/TLS setting is inherited only when the child uses that provider. Fast mode is inherited by default. An explicit `fast_mode` spawn argument has highest precedence. Otherwise, a profile with `fastMode: false` opts out, while `fastMode: true` enables fast mode even when the host setting is off; the `subagent_spawn` result warns the parent session when this profile override occurs. Child providers that do not yet support fast mode return an unsupported-provider error instead of silently ignoring an enabled setting.
108 109
109 ## Lifecycle, results, and recovery110 ## Lifecycle, results, and recovery
110 111
M packages/agent/tools/subagent_spawn.go +12 -1
@@ -56,6 +56,7 @@ type subagentSpawnArgs struct {
5656 Provider string `json:"provider,omitempty"`
5757 Reasoning string `json:"reasoning,omitempty"`
5858 Thinking string `json:"thinking,omitempty"`
59+ FastMode *bool `json:"fast_mode,omitempty"`
5960 Isolation string `json:"isolation,omitempty"`
6061 Timeout string `json:"timeout,omitempty"`
6162 MaxTurns *int `json:"max_turns,omitempty"`
@@ -90,6 +91,10 @@ const subagentSpawnSchemaTemplate = `{
9091 "enum": ["off", "minimum", "low", "medium", "high", "xhigh", "max"],
9192 "description": "Alias for reasoning, accepted for compatibility with common agent profile terminology. Prefer reasoning when both are available."
9293 },
94+ "fast_mode": {
95+ "type": "boolean",
96+ "description": "Optional fast-mode override for the child. Omit to inherit the selected profile or host setting."
97+ },
9398 "isolation": {
9499 "type": "string",
95100 "enum": ["shared", "worktree"],
@@ -167,6 +172,7 @@ func (t *SubagentSpawnTool) Execute(ctx context.Context, raw json.RawMessage, pr
167172 agentName := strings.TrimSpace(a.Agent)
168173 var profile *subagents.Profile
169174 var fastModeOverride *bool
175+ fastModeFromProfile := false
170176 if agentName != "" {
171177 if t.ResolveSubagent == nil {
172178 return protocolToolError(prefix + ": named subagent profiles are unavailable")
@@ -181,6 +187,11 @@ func (t *SubagentSpawnTool) Execute(ctx context.Context, raw json.RawMessage, pr
181187 }
182188 agentName = profile.Name
183189 fastModeOverride = profile.FastMode
190+ fastModeFromProfile = profile.FastMode != nil
191+ }
192+ if a.FastMode != nil {
193+ fastModeOverride = a.FastMode
194+ fastModeFromProfile = false
184195 }
185196
186197 model := strings.TrimSpace(a.Model)
@@ -271,7 +282,7 @@ func (t *SubagentSpawnTool) Execute(ctx context.Context, raw json.RawMessage, pr
271282 if agent.FastMode {
272283 sb.WriteString("fast mode: enabled\n")
273284 }
274- if fastModeOverride != nil && *fastModeOverride && agent.FastModeOverridesHost() {
285+ if fastModeFromProfile && fastModeOverride != nil && *fastModeOverride && agent.FastModeOverridesHost() {
275286 sb.WriteString("warning: subagent profile has fast mode enabled, overriding global fast mode off\n")
276287 }
277288 sb.WriteString("\nThe sub-agent is running in the background. Completion is host-event-driven: wait for the injected [auto-subagents update], the only completion signal. ")
@@ -56,6 +56,7 @@ type subagentSpawnArgs struct {
56 Provider string `json:"provider,omitempty"`56 Provider string `json:"provider,omitempty"`
57 Reasoning string `json:"reasoning,omitempty"`57 Reasoning string `json:"reasoning,omitempty"`
58 Thinking string `json:"thinking,omitempty"`58 Thinking string `json:"thinking,omitempty"`
59+ FastMode *bool `json:"fast_mode,omitempty"`
59 Isolation string `json:"isolation,omitempty"`60 Isolation string `json:"isolation,omitempty"`
60 Timeout string `json:"timeout,omitempty"`61 Timeout string `json:"timeout,omitempty"`
61 MaxTurns *int `json:"max_turns,omitempty"`62 MaxTurns *int `json:"max_turns,omitempty"`
@@ -90,6 +91,10 @@ const subagentSpawnSchemaTemplate = `{
90 "enum": ["off", "minimum", "low", "medium", "high", "xhigh", "max"],91 "enum": ["off", "minimum", "low", "medium", "high", "xhigh", "max"],
91 "description": "Alias for reasoning, accepted for compatibility with common agent profile terminology. Prefer reasoning when both are available."92 "description": "Alias for reasoning, accepted for compatibility with common agent profile terminology. Prefer reasoning when both are available."
92 },93 },
94+ "fast_mode": {
95+ "type": "boolean",
96+ "description": "Optional fast-mode override for the child. Omit to inherit the selected profile or host setting."
97+ },
93 "isolation": {98 "isolation": {
94 "type": "string",99 "type": "string",
95 "enum": ["shared", "worktree"],100 "enum": ["shared", "worktree"],
@@ -167,6 +172,7 @@ func (t *SubagentSpawnTool) Execute(ctx context.Context, raw json.RawMessage, pr
167 agentName := strings.TrimSpace(a.Agent)172 agentName := strings.TrimSpace(a.Agent)
168 var profile *subagents.Profile173 var profile *subagents.Profile
169 var fastModeOverride *bool174 var fastModeOverride *bool
175+ fastModeFromProfile := false
170 if agentName != "" {176 if agentName != "" {
171 if t.ResolveSubagent == nil {177 if t.ResolveSubagent == nil {
172 return protocolToolError(prefix + ": named subagent profiles are unavailable")178 return protocolToolError(prefix + ": named subagent profiles are unavailable")
@@ -181,6 +187,11 @@ func (t *SubagentSpawnTool) Execute(ctx context.Context, raw json.RawMessage, pr
181 }187 }
182 agentName = profile.Name188 agentName = profile.Name
183 fastModeOverride = profile.FastMode189 fastModeOverride = profile.FastMode
190+ fastModeFromProfile = profile.FastMode != nil
191+ }
192+ if a.FastMode != nil {
193+ fastModeOverride = a.FastMode
194+ fastModeFromProfile = false
184 }195 }
185 196
186 model := strings.TrimSpace(a.Model)197 model := strings.TrimSpace(a.Model)
@@ -271,7 +282,7 @@ func (t *SubagentSpawnTool) Execute(ctx context.Context, raw json.RawMessage, pr
271 if agent.FastMode {282 if agent.FastMode {
272 sb.WriteString("fast mode: enabled\n")283 sb.WriteString("fast mode: enabled\n")
273 }284 }
274- if fastModeOverride != nil && *fastModeOverride && agent.FastModeOverridesHost() {285+ if fastModeFromProfile && fastModeOverride != nil && *fastModeOverride && agent.FastModeOverridesHost() {
275 sb.WriteString("warning: subagent profile has fast mode enabled, overriding global fast mode off\n")286 sb.WriteString("warning: subagent profile has fast mode enabled, overriding global fast mode off\n")
276 }287 }
277 sb.WriteString("\nThe sub-agent is running in the background. Completion is host-event-driven: wait for the injected [auto-subagents update], the only completion signal. ")288 sb.WriteString("\nThe sub-agent is running in the background. Completion is host-event-driven: wait for the injected [auto-subagents update], the only completion signal. ")
M packages/agent/tools/subagent_spawn_test.go +58 -0
@@ -3,6 +3,7 @@ package tools
33 import (
44 "context"
55 "encoding/json"
6+ "fmt"
67 "path/filepath"
78 "strings"
89 "testing"
@@ -349,6 +350,63 @@ func TestSubagentSpawnAppliesProfileFastModeRestriction(t *testing.T) {
349350 }
350351 }
351352
353+func TestSubagentSpawnSchemaAdvertisesFastModeOverride(t *testing.T) {
354+ var schema struct {
355+ Properties map[string]struct {
356+ Type string `json:"type"`
357+ Description string `json:"description"`
358+ } `json:"properties"`
359+ }
360+ if err := json.Unmarshal((&SubagentSpawnTool{}).Schema(), &schema); err != nil {
361+ t.Fatal(err)
362+ }
363+ fastMode, ok := schema.Properties["fast_mode"]
364+ if !ok {
365+ t.Fatal("subagent_spawn schema does not advertise fast_mode")
366+ }
367+ if fastMode.Type != "boolean" {
368+ t.Fatalf("fast_mode type = %q, want boolean", fastMode.Type)
369+ }
370+ if !strings.Contains(strings.ToLower(fastMode.Description), "inherit") {
371+ t.Fatalf("fast_mode description = %q, want inheritance guidance", fastMode.Description)
372+ }
373+}
374+
375+func TestSubagentSpawnExplicitFastModeOverridesProfile(t *testing.T) {
376+ for _, tc := range []struct {
377+ name string
378+ profileFast bool
379+ spawnFast bool
380+ }{
381+ {name: "enable", profileFast: false, spawnFast: true},
382+ {name: "disable", profileFast: true, spawnFast: false},
383+ } {
384+ t.Run(tc.name, func(t *testing.T) {
385+ profileFast := tc.profileFast
386+ tool := &SubagentSpawnTool{
387+ Supervisor: newTestSupervisor(t),
388+ Enabled: func() bool { return true },
389+ ResolveSubagent: func(name string) (*subagents.Profile, error) {
390+ return &subagents.Profile{Name: name, FastMode: &profileFast}, nil
391+ },
392+ }
393+
394+ raw := fmt.Sprintf(`{"task":"review auth","agent":"worker","fast_mode":%t}`, tc.spawnFast)
395+ res, err := tool.Execute(context.Background(), json.RawMessage(raw), nil)
396+ if err != nil {
397+ t.Fatal(err)
398+ }
399+ if res.IsError {
400+ t.Fatalf("unexpected tool error: %s", textResult(res.Content))
401+ }
402+ agents := tool.Supervisor.List()
403+ if len(agents) != 1 || agents[0].FastMode != tc.spawnFast {
404+ t.Fatalf("agent fast mode = %#v, want %t", agents, tc.spawnFast)
405+ }
406+ })
407+ }
408+}
409+
352410 func TestSubagentSpawnProfileFastModeOverridesGlobalOffWithWarning(t *testing.T) {
353411 root := t.TempDir()
354412 profileFastMode := true
@@ -3,6 +3,7 @@ package tools
3 import (3 import (
4 "context"4 "context"
5 "encoding/json"5 "encoding/json"
6+ "fmt"
6 "path/filepath"7 "path/filepath"
7 "strings"8 "strings"
8 "testing"9 "testing"
@@ -349,6 +350,63 @@ func TestSubagentSpawnAppliesProfileFastModeRestriction(t *testing.T) {
349 }350 }
350 }351 }
351 352
353+func TestSubagentSpawnSchemaAdvertisesFastModeOverride(t *testing.T) {
354+ var schema struct {
355+ Properties map[string]struct {
356+ Type string `json:"type"`
357+ Description string `json:"description"`
358+ } `json:"properties"`
359+ }
360+ if err := json.Unmarshal((&SubagentSpawnTool{}).Schema(), &schema); err != nil {
361+ t.Fatal(err)
362+ }
363+ fastMode, ok := schema.Properties["fast_mode"]
364+ if !ok {
365+ t.Fatal("subagent_spawn schema does not advertise fast_mode")
366+ }
367+ if fastMode.Type != "boolean" {
368+ t.Fatalf("fast_mode type = %q, want boolean", fastMode.Type)
369+ }
370+ if !strings.Contains(strings.ToLower(fastMode.Description), "inherit") {
371+ t.Fatalf("fast_mode description = %q, want inheritance guidance", fastMode.Description)
372+ }
373+}
374+
375+func TestSubagentSpawnExplicitFastModeOverridesProfile(t *testing.T) {
376+ for _, tc := range []struct {
377+ name string
378+ profileFast bool
379+ spawnFast bool
380+ }{
381+ {name: "enable", profileFast: false, spawnFast: true},
382+ {name: "disable", profileFast: true, spawnFast: false},
383+ } {
384+ t.Run(tc.name, func(t *testing.T) {
385+ profileFast := tc.profileFast
386+ tool := &SubagentSpawnTool{
387+ Supervisor: newTestSupervisor(t),
388+ Enabled: func() bool { return true },
389+ ResolveSubagent: func(name string) (*subagents.Profile, error) {
390+ return &subagents.Profile{Name: name, FastMode: &profileFast}, nil
391+ },
392+ }
393+
394+ raw := fmt.Sprintf(`{"task":"review auth","agent":"worker","fast_mode":%t}`, tc.spawnFast)
395+ res, err := tool.Execute(context.Background(), json.RawMessage(raw), nil)
396+ if err != nil {
397+ t.Fatal(err)
398+ }
399+ if res.IsError {
400+ t.Fatalf("unexpected tool error: %s", textResult(res.Content))
401+ }
402+ agents := tool.Supervisor.List()
403+ if len(agents) != 1 || agents[0].FastMode != tc.spawnFast {
404+ t.Fatalf("agent fast mode = %#v, want %t", agents, tc.spawnFast)
405+ }
406+ })
407+ }
408+}
409+
352 func TestSubagentSpawnProfileFastModeOverridesGlobalOffWithWarning(t *testing.T) {410 func TestSubagentSpawnProfileFastModeOverridesGlobalOffWithWarning(t *testing.T) {
353 root := t.TempDir()411 root := t.TempDir()
354 profileFastMode := true412 profileFastMode := true

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 476

↑↓ moveenter openesc close