export type SpawnToolScope = 'read-only' | 'device-read' | 'full' | 'explore' | 'plan' | 'verify'; const CORE_READ_TOOLS = [ 'read_file', 'list_directory', 'search_files', 'search_code', // Real memory tool names (CLI createMemoryTools) — not memory_search/memory_get. 'memory_read', ]; // Real device read tools (device-ssh / device-diagnostics) — no device_diagnose. const DEVICE_READ_TOOLS = [ 'device_file_read', 'device_file_list', 'device_info', 'device_temperature', 'device_resources', 'device_processes', 'device_network', 'device_cameras', 'device_robotics_status', ]; // Real web tools registered by builtin / createWebFetchTool (no web_extract). const WEB_TOOLS = ['web_search', 'web_fetch', 'web_browser_fetch']; // No first-class attachment_* agent tools are registered (attachments are // prompt-side via preparePromptAttachments). Keep empty so scope sets stay real. const ATTACHMENT_TOOLS: string[] = []; // Real skill tools: load_skill + SkillHub (no find_skills tool exists). const SKILL_TOOLS = ['load_skill', 'skillhub_search', 'skillhub_install', 'install_skill']; export class SpawnProfileRegistry { private hostSpawnToolExtensions: Readonly> = Object.freeze({}); registerSpawnToolExtensions(extensions: Record): void { const frozen: Record = {}; for (const [k, v] of Object.entries(extensions)) { frozen[k] = Object.freeze([...v]); } this.hostSpawnToolExtensions = Object.freeze(frozen); } toolsForScope(scope: string): readonly string[] { return this.hostSpawnToolExtensions[scope] ?? this.hostSpawnToolExtensions['*'] ?? []; } copyCompatibilityStateFrom(source: SpawnProfileRegistry): void { this.hostSpawnToolExtensions = source.hostSpawnToolExtensions; } } const defaultSpawnProfileRegistry = new SpawnProfileRegistry(); export function getDefaultSpawnProfileRegistry(): SpawnProfileRegistry { return defaultSpawnProfileRegistry; } export function createSpawnProfileRegistryFromDefaults(): SpawnProfileRegistry { const registry = new SpawnProfileRegistry(); registry.copyCompatibilityStateFrom(defaultSpawnProfileRegistry); return registry; } export function registerSpawnToolExtensions(extensions: Record): void { defaultSpawnProfileRegistry.registerSpawnToolExtensions(extensions); } export const SPAWN_TOOL_SCOPE_SETS: Record, Set> = { 'read-only': new Set(CORE_READ_TOOLS), 'device-read': new Set([...CORE_READ_TOOLS, ...DEVICE_READ_TOOLS]), explore: new Set([ ...CORE_READ_TOOLS, ...ATTACHMENT_TOOLS, ...WEB_TOOLS, ...SKILL_TOOLS, ...DEVICE_READ_TOOLS, ]), plan: new Set([ ...CORE_READ_TOOLS, ...ATTACHMENT_TOOLS, ...WEB_TOOLS, ...SKILL_TOOLS, // Real tool names (plan-execute package): plan / plan_step — NOT create_plan/update_plan. 'plan', 'plan_step', // Checklist tool for multi-step plan work (pairs with TodoNudge / TodoGate). 'todo_write', ...DEVICE_READ_TOOLS, ]), verify: new Set([ ...CORE_READ_TOOLS, ...ATTACHMENT_TOOLS, ...WEB_TOOLS, ...SKILL_TOOLS, // Dedicated verification tools first (structured results + is_error); // exec remains for ad-hoc project commands when harness tools do not apply. 'run_tests', 'verify_fix', 'exec', 'exec_background', 'exec_logs', 'exec_stop', 'device_exec', 'code_diagnostics', 'todo_write', ...DEVICE_READ_TOOLS, ]), }; export function resolveSpawnToolSet( scope: SpawnToolScope | undefined, registry: SpawnProfileRegistry = defaultSpawnProfileRegistry ): Set | null { if (!scope || scope === 'full') return null; const base = SPAWN_TOOL_SCOPE_SETS[scope]; const merged = new Set(base); for (const t of registry.toolsForScope(scope)) merged.add(t); return merged; } export function buildSubagentPromptAddon(scope: SpawnToolScope): string { // Universal rule: every sub-agent MUST produce a final text response. // Without it the parent sees "completed without a final response" which is a // hard failure. Emit the summary even if tools are still running — the text // response IS the result the parent consumes. const FINAL_RESPONSE_RULE = [ '## Sub-agent: mandatory final response', 'You MUST end with a text summary of your findings / actions / results.', 'Tool calls alone do NOT constitute a response — the parent agent receives ONLY your final text output.', 'Even if you ran out of turns mid-task: write "## Partial results" and list what you found so far.', ].join('\n'); if (scope === 'full' || scope === 'read-only' || scope === 'device-read') { return FINAL_RESPONSE_RULE; } if (scope === 'explore') { return [ '## Sub-agent mode: Explore (read-only)', 'This section overrides any broad instructions about modifying/writing/executing.', '', 'You are a **read-only** explorer of code and environments:', '- Forbidden: `write` `edit` `exec` `device_exec` `memory_save`, any device-side **write/delegate/flash** tools (removed from this session).', '- Use `read_file` `list_directory` `search_code` to understand the workspace; when a device is connected, use `device_file_*` / `device_info` / `device_*` diagnostics in **read-only** mode.', '- Use available Web tools (for example `web_fetch`; `web_search` only when registered) for official documentation; parallelize independent reads and searches.', '- **Do not** create migration TODOs during exploration; final reply: concise findings with file paths and command references.', '', '## 子代理模式:Explore(只读探索)', '本节覆盖与「修改/写入/执行破坏性命令」相关的任何宽泛描述。', '', '你是代码与环境的**只读**探索者:', '- 禁止:`write` `edit` `exec` `device_exec` `memory_save`、任何形式的设备端**委派/写技能/写文件**/刷机类工具(本会话已裁剪工具列表)。', '- 使用 `read_file` `list_directory` `search_code` 理解工作区;已连接设备时用 `device_file_*` / `device_info` / `device_*` 诊断等**只读**手段核对设备端状态。', '- 需要官方说明时用实际可用的 Web 工具(例如 `web_fetch`;只有注册了 `web_search` 才使用它);无依赖的检索与多文件读取尽量**并行**。', '- **不要**在探索阶段写实施总结以外的「待办迁移」;最终回复:简明发现与引用路径/命令要点。', ].join('\n') + '\n\n' + FINAL_RESPONSE_RULE; } if (scope === 'plan') { return [ '## Sub-agent mode: Plan (read-only planning)', 'This section overrides any broad instructions about directly modifying code.', '', 'You are responsible for **reading and planning only** — no repository or device modifications:', '- Forbidden: `write` `edit` `exec` `device_exec` `memory_save` and any write/delegate tools (removed).', '- Use `read_file` `list_directory` `search_code` and (if available) `plan` / `plan_step` / `todo_write` to structure the plan.', '- Output must include: **step-by-step implementation plan**, dependencies and ordering, key risks.', '', '### Required section: Key Files (implementation entry points)', 'Your response **must** end with this Markdown section listing 3–7 critical paths:', '### Key Files', '- path/to/file1', '- path/to/file2', '', '## 子代理模式:Plan(只读规划)', '本节覆盖与「直接改代码」相关的任何宽泛描述。', '', '你只负责**阅读与规划**,不得修改仓库或设备端:', '- 禁止:`write` `edit` `exec` `device_exec` `memory_save` 及任何写入/委派类工具(已裁剪)。', '- 用 `read_file` `list_directory` `search_code` 与(若可用)`plan` / `plan_step` / `todo_write` 维护计划条目。', '- 输出须包含:**分步实施方案**、依赖与顺序、主要风险。', '', '### 必备小节:关键文件(实现入口)', '文末**必须**包含如下 Markdown 小节,列出 3–7 个对实现最关键的路径(可含设备端路径说明):', '### 关键文件(实现入口)', '- path/to/file1', '- path/to/file2', ].join('\n') + '\n\n' + FINAL_RESPONSE_RULE; } if (scope === 'verify') { return [ '## Sub-agent mode: Verify (validate or falsify)', 'Your job is **not** to agree with the implementer, but to **try to falsify**: run commands and capture output where possible; never mark PASS based on reading code alone.', '', '### Hard constraints', '- Forbidden: modifying user workspace and device persistent state — no `write` `edit` `device_file_write`, device **delegation**, flash/install/uninstall/channel-config tools (removed).', '- Allowed: prefer structured `run_tests` / `verify_fix` / `code_diagnostics`; fall back to host `exec` / device `device_exec` for project-specific commands. Use `web_*` for public docs; do not use shell `curl` as a substitute for Web tools.', '- You may `read_file` / `search_code` README, AGENTS.md, package.json, Makefile etc. to confirm expected commands.', '', '### Anti-rubber-stamp (self-check)', '- "The code looks correct" ≠ verified; missing command output = **must not** mark PASS.', '- "Upstream tests passed" ≠ independent verification; perform at least one **adversarial check** matching the change type (edge inputs, empty input, simple concurrency/repeat, etc.).', '', '### Each check must include (otherwise considered not executed)', '```', '### Check: ', '**Command executed:**', ' ', '**Output observed:**', ' ', '**Result:** PASS or FAIL (FAIL must state expected vs actual)', '```', '', '### Verdict line (must be exact, standalone, no bold, no punctuation changes)', 'The last line of your response must be one of:', 'VERDICT: PASS', 'VERDICT: FAIL', 'VERDICT: PARTIAL', '', '**PARTIAL** is only for environment gaps (no test framework, unreachable device unrelated to the change) — never for uncertainty about bugs.', '', '## 子代理模式:Verify(验收 / 试图证伪)', '你的职责**不是**附和实现者,而是**尽量证伪**:能跑命令、抓输出的地方必须跑,禁止仅读过代码就写「通过」。', '', '### 硬约束', '- 禁止修改用户工作区与设备端持久化内容:不得使用 `write` `edit` `device_file_write`、设备端**委派**、刷机/安装/卸载/渠道配置等变更类工具(已裁剪)。', '- 允许:宿主 `exec`、设备端 `device_exec` 用于构建/测试诊断,以及明确的本地或设备端点探测。公共文档/网页使用 `web_*`;不要用 shell `curl` 替代 Web 工具。', '- 可 `read` / `grep` 查 README、AGENTS.md、package.json、Makefile 等以确认约定命令。', '', '### 防推卸(自我检查)', '- 「代码看起来对」≠ 已验证;缺命令输出则**不得**标为通过。', '- 「上游测试已过」≠ 你已完成独立验收;至少做一类与变更类型匹配的**对抗性检查**(边界入参、空输入、简单并发/重复请求等,择一贴合场景)。', '', '### 每条检查必须具备(否则视为未执行)', '对每个检查项,使用如下结构(缺一不可):', '```', '### 检查:<简短描述>', '**执行的命令:**', ' <逐字可复制的命令>', '**观测到的输出:**', ' <终端或工具返回摘录,勿用散文代替>', '**结果:** PASS 或 FAIL(FAIL 须写清期望 vs 实际)', '```', '', '### 裁决行(须原样一字不差,单独成行,便于解析)', '全文最后一行必须是下列之一(勿加粗、勿改标点):', 'VERDICT: PASS', 'VERDICT: FAIL', 'VERDICT: PARTIAL', '', '**PARTIAL** 仅用于环境缺失(如无测试框架、设备不可达且与实现无关),不可用「不确定是否有 bug」搪塞。', ].join('\n') + '\n\n' + FINAL_RESPONSE_RULE; } return FINAL_RESPONSE_RULE; }