Merge pull request #124 from bnema/fix/orchestrator-read-tool
feat(orchestrator): allow read-only codebase exploration
4 files · +87 -9
M README.md +2 -2
| @@ -425,7 +425,7 @@ The fair global scheduler defaults to six concurrent turns, one active turn per | |||
| 425 | 425 | ||
| 426 | 426 | Worktree isolation captures a patch and changed-file list without applying it. Shared children use the host checkout. The available logical references are `subagent://<id>`, `subagent://<id>/history`, `subagent://<id>/result`, and `subagent://<id>/patch`. | |
| 427 | 427 | ||
| 428 | - | **Subagent Orchestrator.** With `/settings` → **Subagent Orchestrator** on (or `/orchestrator`), the primary delegates implementation, debugging/testing, and code review to named profiles or explicitly described general children. It coordinates and synthesizes completion updates rather than doing direct implementation. The canonical resident tools remain available for user-requested delegation when the setting is off, subject to launch-time permissions. Do not poll `subagent_status` or journal files solely to wait for completion. | |
| 428 | + | **Subagent Orchestrator.** With `/settings` → **Subagent Orchestrator** on (or `/orchestrator`), the primary delegates implementation, debugging/testing, and code review to named profiles or explicitly described general children. It may use `read` and `grep` for codebase exploration and `web_search` for public-web research before dispatching work, but does not implement directly. These research tools remain available after a requested tool allowlist is applied unless tools are disabled with `--no-tools`. The canonical resident tools remain available for user-requested delegation when the setting is off, subject to launch-time permissions. Do not poll `subagent_status` or journal files solely to wait for completion. | |
| 429 | 429 | ||
| 430 | 430 | ### Named subagent profiles | |
| 431 | 431 | ||
| @@ -439,7 +439,7 @@ Opens a dialog with every persistent setting. `up`/`down` to navigate, `enter` o | |||
| 439 | 439 | - **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. | |
| 440 | 440 | - **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. | |
| 441 | 441 | - **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`. | |
| 442 | - | - **Subagent Orchestrator** — 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 optional or required 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, and shared/worktree isolation where applicable; lifecycle and result references are persisted. Completion is host-event-driven through `[auto-subagents update]` for every worker; required work stays asynchronous, persists its outcome, and prevents only the terminal parent response until satisfied or explicitly waived by user removal. 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. | |
| 442 | + | - **Subagent Orchestrator** — enable strict primary-agent orchestrator mode: delegate implementation, debugging/testing, and code-review work to named or general workers; the primary agent may use `read`, `grep`, and `web_search` for read-only research to prepare precise tasks, but does not make direct implementation changes. In orchestrated sessions, these research tools are added after the requested tool allowlist is applied unless tools are disabled with `--no-tools`. It can spawn optional or required 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, and shared/worktree isolation where applicable; lifecycle and result references are persisted. Completion is host-event-driven through `[auto-subagents update]` for every worker; required work stays asynchronous, persists its outcome, and prevents only the terminal parent response until satisfied or explicitly waived by user removal. 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. | |
| 443 | 443 | - **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. | |
| 444 | 444 | - **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. | |
| 445 | 445 | - **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`. | |
| @@ -425,7 +425,7 @@ The fair global scheduler defaults to six concurrent turns, one active turn per | |||||
| 425 | 425 | ||||
| 426 | Worktree isolation captures a patch and changed-file list without applying it. Shared children use the host checkout. The available logical references are `subagent://<id>`, `subagent://<id>/history`, `subagent://<id>/result`, and `subagent://<id>/patch`. | 426 | Worktree isolation captures a patch and changed-file list without applying it. Shared children use the host checkout. The available logical references are `subagent://<id>`, `subagent://<id>/history`, `subagent://<id>/result`, and `subagent://<id>/patch`. | ||
| 427 | 427 | ||||
| 428 | - | **Subagent Orchestrator.** With `/settings` → **Subagent Orchestrator** on (or `/orchestrator`), the primary delegates implementation, debugging/testing, and code review to named profiles or explicitly described general children. It coordinates and synthesizes completion updates rather than doing direct implementation. The canonical resident tools remain available for user-requested delegation when the setting is off, subject to launch-time permissions. Do not poll `subagent_status` or journal files solely to wait for completion. | 428 | + | **Subagent Orchestrator.** With `/settings` → **Subagent Orchestrator** on (or `/orchestrator`), the primary delegates implementation, debugging/testing, and code review to named profiles or explicitly described general children. It may use `read` and `grep` for codebase exploration and `web_search` for public-web research before dispatching work, but does not implement directly. These research tools remain available after a requested tool allowlist is applied unless tools are disabled with `--no-tools`. The canonical resident tools remain available for user-requested delegation when the setting is off, subject to launch-time permissions. Do not poll `subagent_status` or journal files solely to wait for completion. |
| 429 | 429 | ||||
| 430 | ### Named subagent profiles | 430 | ### Named subagent profiles | ||
| 431 | 431 | ||||
| @@ -439,7 +439,7 @@ Opens a dialog with every persistent setting. `up`/`down` to navigate, `enter` o | |||||
| 439 | - **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. | 439 | - **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. | ||
| 440 | - **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. | 440 | - **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. | ||
| 441 | - **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`. | 441 | - **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`. | ||
| 442 | - | - **Subagent Orchestrator** — 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 optional or required 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, and shared/worktree isolation where applicable; lifecycle and result references are persisted. Completion is host-event-driven through `[auto-subagents update]` for every worker; required work stays asynchronous, persists its outcome, and prevents only the terminal parent response until satisfied or explicitly waived by user removal. 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. | 442 | + | - **Subagent Orchestrator** — enable strict primary-agent orchestrator mode: delegate implementation, debugging/testing, and code-review work to named or general workers; the primary agent may use `read`, `grep`, and `web_search` for read-only research to prepare precise tasks, but does not make direct implementation changes. In orchestrated sessions, these research tools are added after the requested tool allowlist is applied unless tools are disabled with `--no-tools`. It can spawn optional or required 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, and shared/worktree isolation where applicable; lifecycle and result references are persisted. Completion is host-event-driven through `[auto-subagents update]` for every worker; required work stays asynchronous, persists its outcome, and prevents only the terminal parent response until satisfied or explicitly waived by user removal. 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. |
| 443 | - **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. | 443 | - **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. | ||
| 444 | - **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. | 444 | - **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. | ||
| 445 | - **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`. | 445 | - **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 packages/agent/build.go +16 -5
| @@ -1247,13 +1247,18 @@ func buildToolRegistry(args Args, cwd string, sandbox *tools.Sandbox, lspEnabled | |||
| 1247 | 1247 | for _, t := range all { | |
| 1248 | 1248 | reg[t.Name()] = t | |
| 1249 | 1249 | } | |
| 1250 | - | return reg | |
| 1251 | - | } | |
| 1252 | - | for _, name := range args.Tools { | |
| 1253 | - | if t, ok := all[name]; ok { | |
| 1254 | - | reg[name] = t | |
| 1250 | + | } else { | |
| 1251 | + | for _, name := range args.Tools { | |
| 1252 | + | if t, ok := all[name]; ok { | |
| 1253 | + | reg[name] = t | |
| 1254 | + | } | |
| 1255 | 1255 | } | |
| 1256 | 1256 | } | |
| 1257 | + | if orchestratorExplorationToolsAllowed(args) { | |
| 1258 | + | reg["read"] = all["read"] | |
| 1259 | + | reg["grep"] = all["grep"] | |
| 1260 | + | reg["web_search"] = tools.NewWebSearchTool() | |
| 1261 | + | } | |
| 1257 | 1262 | return reg | |
| 1258 | 1263 | } | |
| 1259 | 1264 | ||
| @@ -1282,6 +1287,12 @@ func autoSubagentsToolAllowed(args Args) bool { | |||
| 1282 | 1287 | return autoSubagentsToolAllowedFor(args, "subagent_spawn") | |
| 1283 | 1288 | } | |
| 1284 | 1289 | ||
| 1290 | + | // orchestratorExplorationToolsAllowed keeps the primary orchestrator's | |
| 1291 | + | // read-only exploration tools available without enabling implementation tools. | |
| 1292 | + | func orchestratorExplorationToolsAllowed(args Args) bool { | |
| 1293 | + | return args.Orchestrate && !args.NoTools | |
| 1294 | + | } | |
| 1295 | + | ||
| 1285 | 1296 | func autoSubagentsStatusToolAllowed(args Args) bool { | |
| 1286 | 1297 | return autoSubagentsToolAllowedFor(args, "subagent_status") | |
| 1287 | 1298 | } | |
| @@ -1247,13 +1247,18 @@ func buildToolRegistry(args Args, cwd string, sandbox *tools.Sandbox, lspEnabled | |||||
| 1247 | for _, t := range all { | 1247 | for _, t := range all { | ||
| 1248 | reg[t.Name()] = t | 1248 | reg[t.Name()] = t | ||
| 1249 | } | 1249 | } | ||
| 1250 | - | return reg | 1250 | + | } else { |
| 1251 | - | } | 1251 | + | for _, name := range args.Tools { |
| 1252 | - | for _, name := range args.Tools { | 1252 | + | if t, ok := all[name]; ok { |
| 1253 | - | if t, ok := all[name]; ok { | 1253 | + | reg[name] = t |
| 1254 | - | reg[name] = t | 1254 | + | } |
| 1255 | } | 1255 | } | ||
| 1256 | } | 1256 | } | ||
| 1257 | + | if orchestratorExplorationToolsAllowed(args) { | |||
| 1258 | + | reg["read"] = all["read"] | |||
| 1259 | + | reg["grep"] = all["grep"] | |||
| 1260 | + | reg["web_search"] = tools.NewWebSearchTool() | |||
| 1261 | + | } | |||
| 1257 | return reg | 1262 | return reg | ||
| 1258 | } | 1263 | } | ||
| 1259 | 1264 | ||||
| @@ -1282,6 +1287,12 @@ func autoSubagentsToolAllowed(args Args) bool { | |||||
| 1282 | return autoSubagentsToolAllowedFor(args, "subagent_spawn") | 1287 | return autoSubagentsToolAllowedFor(args, "subagent_spawn") | ||
| 1283 | } | 1288 | } | ||
| 1284 | 1289 | ||||
| 1290 | + | // orchestratorExplorationToolsAllowed keeps the primary orchestrator's | |||
| 1291 | + | // read-only exploration tools available without enabling implementation tools. | |||
| 1292 | + | func orchestratorExplorationToolsAllowed(args Args) bool { | |||
| 1293 | + | return args.Orchestrate && !args.NoTools | |||
| 1294 | + | } | |||
| 1295 | + | ||||
| 1285 | func autoSubagentsStatusToolAllowed(args Args) bool { | 1296 | func autoSubagentsStatusToolAllowed(args Args) bool { | ||
| 1286 | return autoSubagentsToolAllowedFor(args, "subagent_status") | 1297 | return autoSubagentsToolAllowedFor(args, "subagent_status") | ||
| 1287 | } | 1298 | } | ||
A packages/agent/build_orchestrator_test.go +64 -0
| @@ -0,0 +1,64 @@ | |||
| 1 | + | package agent | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "testing" | |
| 5 | + | ||
| 6 | + | "github.com/bnema/zut/packages/agent/subagents" | |
| 7 | + | "github.com/bnema/zut/packages/agent/tools" | |
| 8 | + | ) | |
| 9 | + | ||
| 10 | + | func TestOrchestratorAlwaysReceivesResearchTools(t *testing.T) { | |
| 11 | + | for _, tc := range []struct { | |
| 12 | + | name string | |
| 13 | + | args Args | |
| 14 | + | wantResearch bool | |
| 15 | + | wantWriteTool bool | |
| 16 | + | }{ | |
| 17 | + | { | |
| 18 | + | name: "requested allowlist", | |
| 19 | + | args: Args{Orchestrate: true, ToolsSet: true, Tools: []string{"subagent_spawn"}}, | |
| 20 | + | wantResearch: true, | |
| 21 | + | wantWriteTool: false, | |
| 22 | + | }, | |
| 23 | + | { | |
| 24 | + | name: "web search policy disabled", | |
| 25 | + | args: Args{Orchestrate: true, WebSearchPolicy: subagents.WebSearchDeny}, | |
| 26 | + | wantResearch: true, | |
| 27 | + | wantWriteTool: true, | |
| 28 | + | }, | |
| 29 | + | { | |
| 30 | + | name: "tools disabled", | |
| 31 | + | args: Args{Orchestrate: true, NoTools: true, ToolsSet: true, Tools: []string{"write"}}, | |
| 32 | + | wantResearch: false, | |
| 33 | + | wantWriteTool: false, | |
| 34 | + | }, | |
| 35 | + | { | |
| 36 | + | name: "normal session", | |
| 37 | + | args: Args{ToolsSet: true, Tools: []string{"write"}}, | |
| 38 | + | wantResearch: false, | |
| 39 | + | wantWriteTool: true, | |
| 40 | + | }, | |
| 41 | + | } { | |
| 42 | + | t.Run(tc.name, func(t *testing.T) { | |
| 43 | + | registry := buildToolRegistry(tc.args, t.TempDir(), nil, false, false, false) | |
| 44 | + | ||
| 45 | + | if got := orchestratorExplorationToolsAllowed(tc.args); got != tc.wantResearch { | |
| 46 | + | t.Fatalf("orchestratorExplorationToolsAllowed = %v, want %v", got, tc.wantResearch) | |
| 47 | + | } | |
| 48 | + | _, gotRead := registry["read"] | |
| 49 | + | if gotRead != tc.wantResearch { | |
| 50 | + | t.Fatalf("read present = %v, want %v", gotRead, tc.wantResearch) | |
| 51 | + | } | |
| 52 | + | if _, ok := registry["grep"].(*tools.GrepTool); ok != tc.wantResearch { | |
| 53 | + | t.Fatalf("grep = %T, want research tool present = %v", registry["grep"], tc.wantResearch) | |
| 54 | + | } | |
| 55 | + | if _, ok := registry["web_search"].(*tools.WebSearchTool); ok != tc.wantResearch { | |
| 56 | + | t.Fatalf("web_search = %T, want research tool present = %v", registry["web_search"], tc.wantResearch) | |
| 57 | + | } | |
| 58 | + | _, gotWrite := registry["write"] | |
| 59 | + | if gotWrite != tc.wantWriteTool { | |
| 60 | + | t.Fatalf("write present = %v, want %v", gotWrite, tc.wantWriteTool) | |
| 61 | + | } | |
| 62 | + | }) | |
| 63 | + | } | |
| 64 | + | } | |
| @@ -0,0 +1,64 @@ | |||||
| 1 | + | package agent | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "testing" | |||
| 5 | + | ||||
| 6 | + | "github.com/bnema/zut/packages/agent/subagents" | |||
| 7 | + | "github.com/bnema/zut/packages/agent/tools" | |||
| 8 | + | ) | |||
| 9 | + | ||||
| 10 | + | func TestOrchestratorAlwaysReceivesResearchTools(t *testing.T) { | |||
| 11 | + | for _, tc := range []struct { | |||
| 12 | + | name string | |||
| 13 | + | args Args | |||
| 14 | + | wantResearch bool | |||
| 15 | + | wantWriteTool bool | |||
| 16 | + | }{ | |||
| 17 | + | { | |||
| 18 | + | name: "requested allowlist", | |||
| 19 | + | args: Args{Orchestrate: true, ToolsSet: true, Tools: []string{"subagent_spawn"}}, | |||
| 20 | + | wantResearch: true, | |||
| 21 | + | wantWriteTool: false, | |||
| 22 | + | }, | |||
| 23 | + | { | |||
| 24 | + | name: "web search policy disabled", | |||
| 25 | + | args: Args{Orchestrate: true, WebSearchPolicy: subagents.WebSearchDeny}, | |||
| 26 | + | wantResearch: true, | |||
| 27 | + | wantWriteTool: true, | |||
| 28 | + | }, | |||
| 29 | + | { | |||
| 30 | + | name: "tools disabled", | |||
| 31 | + | args: Args{Orchestrate: true, NoTools: true, ToolsSet: true, Tools: []string{"write"}}, | |||
| 32 | + | wantResearch: false, | |||
| 33 | + | wantWriteTool: false, | |||
| 34 | + | }, | |||
| 35 | + | { | |||
| 36 | + | name: "normal session", | |||
| 37 | + | args: Args{ToolsSet: true, Tools: []string{"write"}}, | |||
| 38 | + | wantResearch: false, | |||
| 39 | + | wantWriteTool: true, | |||
| 40 | + | }, | |||
| 41 | + | } { | |||
| 42 | + | t.Run(tc.name, func(t *testing.T) { | |||
| 43 | + | registry := buildToolRegistry(tc.args, t.TempDir(), nil, false, false, false) | |||
| 44 | + | ||||
| 45 | + | if got := orchestratorExplorationToolsAllowed(tc.args); got != tc.wantResearch { | |||
| 46 | + | t.Fatalf("orchestratorExplorationToolsAllowed = %v, want %v", got, tc.wantResearch) | |||
| 47 | + | } | |||
| 48 | + | _, gotRead := registry["read"] | |||
| 49 | + | if gotRead != tc.wantResearch { | |||
| 50 | + | t.Fatalf("read present = %v, want %v", gotRead, tc.wantResearch) | |||
| 51 | + | } | |||
| 52 | + | if _, ok := registry["grep"].(*tools.GrepTool); ok != tc.wantResearch { | |||
| 53 | + | t.Fatalf("grep = %T, want research tool present = %v", registry["grep"], tc.wantResearch) | |||
| 54 | + | } | |||
| 55 | + | if _, ok := registry["web_search"].(*tools.WebSearchTool); ok != tc.wantResearch { | |||
| 56 | + | t.Fatalf("web_search = %T, want research tool present = %v", registry["web_search"], tc.wantResearch) | |||
| 57 | + | } | |||
| 58 | + | _, gotWrite := registry["write"] | |||
| 59 | + | if gotWrite != tc.wantWriteTool { | |||
| 60 | + | t.Fatalf("write present = %v, want %v", gotWrite, tc.wantWriteTool) | |||
| 61 | + | } | |||
| 62 | + | }) | |||
| 63 | + | } | |||
| 64 | + | } | |||
M packages/agent/settings_store.go +5 -2
| @@ -254,9 +254,12 @@ func AutoSubagentsEnabled() bool { | |||
| 254 | 254 | // orchestrator. | |
| 255 | 255 | const AutoSubagentsSystemAddendum = `Auto-subagents are enabled. You are the primary-agent orchestrator, not an implementer. | |
| 256 | 256 | ||
| 257 | - | Delegate all implementation, debugging/testing, and code-review work to an appropriately named subagent profile, or to a clearly described general worker when no profile fits. Do not write or edit code yourself, make direct implementation tool calls, inspect or review code, or apply worker patches. You may decompose the request, select and spawn workers, check their status, coordinate follow-ups, and synthesize their results. | |
| 257 | + | - Use read and grep only for read-only codebase exploration before dispatching work. Use web_search only for public-web research that improves worker task instructions. | |
| 258 | + | - Delegate all implementation, debugging/testing, and code-review work to an appropriately named subagent profile or a clearly described general worker. | |
| 259 | + | - Do not write or edit code yourself, make direct implementation tool calls, inspect or review code through implementation tools, or apply worker patches. You may use read, grep, and web_search for the read-only research above and review worker reports. | |
| 260 | + | - Give every worker a self-contained task and synthesize the results. | |
| 258 | 261 | ||
| 259 | - | Give every worker a self-contained task because workers start without this conversation's context. Shared-worktree workers edit the same working directory, so coordinate dependent work and avoid conflicting edits. Use isolation:"worktree" for parallel coding when workers need separate trees; its changes are returned as a patch for a worker to integrate, not for you to apply. Do not invent feature scope beyond the user's request. Child workers cannot recursively spawn more sub-agents in v1. | |
| 262 | + | Workers start without this conversation's context. Shared-worktree workers edit the same working directory, so coordinate dependent work and avoid conflicting edits. Use isolation:"worktree" for parallel coding when workers need separate trees; its changes are returned as a patch for a worker to integrate, not for you to apply. Do not invent feature scope beyond the user's request. Child workers cannot recursively spawn more sub-agents in v1. | |
| 260 | 263 | ||
| 261 | 264 | Use required:true whenever your answer or a declared workflow depends on that worker, especially for required validation or finalization. Required workers remain asynchronous unless you explicitly set subagent_spawn's wait field: without it, manager calls return immediately, the parent stays free to coordinate or perform independent work, and the host delivers terminal outcomes through completion updates. Failed, canceled, or interrupted required work remains unmet: use an enabled manager follow-up action to retry it and do not claim completion. An indeterminate outcome after host restart must not be retried automatically; the user must inspect durable results and side effects, then explicitly resume, restart, or remove the worker. Only the user can waive required work by explicitly removing the terminal worker with /subagents rm. Keep required:false only for independent work that may safely finish later. | |
| 262 | 265 | ||
| @@ -254,9 +254,12 @@ func AutoSubagentsEnabled() bool { | |||||
| 254 | // orchestrator. | 254 | // orchestrator. | ||
| 255 | const AutoSubagentsSystemAddendum = `Auto-subagents are enabled. You are the primary-agent orchestrator, not an implementer. | 255 | const AutoSubagentsSystemAddendum = `Auto-subagents are enabled. You are the primary-agent orchestrator, not an implementer. | ||
| 256 | 256 | ||||
| 257 | - | Delegate all implementation, debugging/testing, and code-review work to an appropriately named subagent profile, or to a clearly described general worker when no profile fits. Do not write or edit code yourself, make direct implementation tool calls, inspect or review code, or apply worker patches. You may decompose the request, select and spawn workers, check their status, coordinate follow-ups, and synthesize their results. | 257 | + | - Use read and grep only for read-only codebase exploration before dispatching work. Use web_search only for public-web research that improves worker task instructions. |
| 258 | + | - Delegate all implementation, debugging/testing, and code-review work to an appropriately named subagent profile or a clearly described general worker. | |||
| 259 | + | - Do not write or edit code yourself, make direct implementation tool calls, inspect or review code through implementation tools, or apply worker patches. You may use read, grep, and web_search for the read-only research above and review worker reports. | |||
| 260 | + | - Give every worker a self-contained task and synthesize the results. | |||
| 258 | 261 | ||||
| 259 | - | Give every worker a self-contained task because workers start without this conversation's context. Shared-worktree workers edit the same working directory, so coordinate dependent work and avoid conflicting edits. Use isolation:"worktree" for parallel coding when workers need separate trees; its changes are returned as a patch for a worker to integrate, not for you to apply. Do not invent feature scope beyond the user's request. Child workers cannot recursively spawn more sub-agents in v1. | 262 | + | Workers start without this conversation's context. Shared-worktree workers edit the same working directory, so coordinate dependent work and avoid conflicting edits. Use isolation:"worktree" for parallel coding when workers need separate trees; its changes are returned as a patch for a worker to integrate, not for you to apply. Do not invent feature scope beyond the user's request. Child workers cannot recursively spawn more sub-agents in v1. |
| 260 | 263 | ||||
| 261 | Use required:true whenever your answer or a declared workflow depends on that worker, especially for required validation or finalization. Required workers remain asynchronous unless you explicitly set subagent_spawn's wait field: without it, manager calls return immediately, the parent stays free to coordinate or perform independent work, and the host delivers terminal outcomes through completion updates. Failed, canceled, or interrupted required work remains unmet: use an enabled manager follow-up action to retry it and do not claim completion. An indeterminate outcome after host restart must not be retried automatically; the user must inspect durable results and side effects, then explicitly resume, restart, or remove the worker. Only the user can waive required work by explicitly removing the terminal worker with /subagents rm. Keep required:false only for independent work that may safely finish later. | 264 | Use required:true whenever your answer or a declared workflow depends on that worker, especially for required validation or finalization. Required workers remain asynchronous unless you explicitly set subagent_spawn's wait field: without it, manager calls return immediately, the parent stays free to coordinate or perform independent work, and the host delivers terminal outcomes through completion updates. Failed, canceled, or interrupted required work remains unmet: use an enabled manager follow-up action to retry it and do not claim completion. An indeterminate outcome after host restart must not be retried automatically; the user must inspect durable results and side effects, then explicitly resume, restart, or remove the worker. Only the user can waive required work by explicitly removing the terminal worker with /subagents rm. Keep required:false only for independent work that may safely finish later. | ||
| 262 | 265 | ||||