一、subagents 工具
1.1 工具概述
功能:管理已生成的子 agent核心特性:
• 3 个 actions(list/kill/steer) • 支持按标签/序号/会话键定位 • 支持批量终止(target=all) • 消息长度限制(4000 字符) • 最近活跃度过滤(recentMinutes)
1.2 Schema 定义
位置:第 112950 行
constSubagentsToolSchema = Type.Object({action: optionalStringEnum$3(["list", "kill", "steer"]),target: Type.Optional(Type.String()),message: Type.Optional(Type.String()),recentMinutes: Type.Optional(Type.Number({ minimum: 1 }))});1.3 完整执行代码
位置:第 112961 行
functioncreateSubagentsTool(opts) {return {label: "Subagents",name: "subagents",description: "List, kill, or steer spawned sub-agents for this requester session. Use this for sub-agent orchestration.",parameters: SubagentsToolSchema,execute: async (_toolCallId, args) => {const params = args;// 1. 解析 action(默认 list)const action = readStringParam$1(params, "action") ?? "list";const cfg = loadConfig();// 2. 解析子 agent 控制器const controller = resolveSubagentController({ cfg,agentSessionKey: opts?.agentSessionKey });// 3. 获取子 agent 运行列表const runs = listControlledSubagentRuns(controller.controllerSessionKey);const recentMinutesRaw = readNumberParam(params, "recentMinutes");const recentMinutes = recentMinutesRaw ? Math.max(1, Math.min(MAX_RECENT_MINUTES, Math.floor(recentMinutesRaw))) : 30;const pendingDescendantCount = createPendingDescendantCounter();constisActive = (entry) => isActiveSubagentRun(entry, pendingDescendantCount);// === action: list ===if (action === "list") {const list = buildSubagentList({ cfg, runs, recentMinutes });returnjsonResult({status: "ok",action: "list",requesterSessionKey: controller.controllerSessionKey,callerSessionKey: controller.callerSessionKey,callerIsSubagent: controller.callerIsSubagent,total: list.total,active: list.active.map(({ line: _line, ...view }) => view),recent: list.recent.map(({ line: _line, ...view }) => view),text: list.text }); }// === action: kill ===if (action === "kill") {const target = readStringParam$1(params, "target", { required: true });// 批量终止if (target === "all" || target === "*") {const result = awaitkillAllControlledSubagentRuns({ cfg, controller, runs });if (result.status === "forbidden") {returnjsonResult({status: "forbidden",action: "kill",target: "all",error: result.error }); }returnjsonResult({status: "ok",action: "kill",target: "all",killed: result.killed,labels: result.labels,text: result.killed > 0 ? `killed ${result.killed} subagent${result.killed === 1 ? "" : "s"}.` : "no running subagents to kill." }); }// 单个终止const resolved = resolveControlledSubagentTarget(runs, target, { recentMinutes, isActive });if (!resolved.entry) {returnjsonResult({status: "error",action: "kill", target,error: resolved.error ?? "Unknown subagent target." }); }const result = awaitkillControlledSubagentRun({ cfg, controller,entry: resolved.entry });returnjsonResult({status: result.status,action: "kill", target,runId: result.runId,sessionKey: result.sessionKey,label: result.label,cascadeKilled: "cascadeKilled"in result ? result.cascadeKilled : void0,cascadeLabels: "cascadeLabels"in result ? result.cascadeLabels : void0,error: "error"in result ? result.error : void0,text: result.text }); }// === action: steer ===if (action === "steer") {const target = readStringParam$1(params, "target", { required: true });const message = readStringParam$1(params, "message", { required: true });// 检查消息长度if (message.length > 4000) {returnjsonResult({status: "error",action: "steer", target,error: `Message too long (${message.length} chars, max ${MAX_STEER_MESSAGE_CHARS}).` }); }const resolved = resolveControlledSubagentTarget(runs, target, { recentMinutes, isActive });if (!resolved.entry) {returnjsonResult({status: "error",action: "steer", target,error: resolved.error ?? "Unknown subagent target." }); }const result = awaitsteerControlledSubagentRun({ cfg, controller,entry: resolved.entry, message });returnjsonResult({status: result.status,action: "steer", target,runId: result.runId,sessionKey: result.sessionKey,sessionId: result.sessionId,mode: "mode"in result ? result.mode : void0,label: "label"in result ? result.label : void0,error: "error"in result ? result.error : void0,text: result.text }); }returnjsonResult({status: "error",error: "Unsupported action." }); } };}1.4 子 agent 定位逻辑
functionresolveControlledSubagentTarget(runs, target, options) {const { recentMinutes, isActive } = options;// 1. 排序运行列表(按开始时间倒序)const sorted = sortSubagentRuns(runs);// 2. 去重(按 childSessionKey)const deduped = [];const seenChildSessionKeys = newSet();for (const entry of sorted) {if (seenChildSessionKeys.has(entry.childSessionKey)) continue; seenChildSessionKeys.add(entry.childSessionKey); deduped.push(entry); }// 3. 特殊值:lastif (target === "last") {return { entry: deduped[0] }; }// 4. 序号定位(1-based)if (/^\d+$/.test(target)) {const idx = Number.parseInt(target, 10);const numericOrder = [ ...deduped.filter((entry) =>isActive(entry)), ...deduped.filter((entry) => !isActive(entry) && !!entry.endedAt && (entry.endedAt ?? 0) >= recentCutoff) ];if (!Number.isFinite(idx) || idx <= 0 || idx > numericOrder.length) {return { error: `Invalid index: ${target}` }; }return { entry: numericOrder[idx - 1] }; }// 5. 会话键定位if (target.includes(":")) {const bySessionKey = deduped.find((entry) => entry.childSessionKey === target);return bySessionKey ? { entry: bySessionKey } : { error: `Unknown session: ${target}` }; }// 6. 精确标签匹配const lowered = target.toLowerCase();const byExactLabel = deduped.filter((entry) => entry.label.toLowerCase() === lowered);if (byExactLabel.length === 1) {return { entry: byExactLabel[0] }; }if (byExactLabel.length > 1) {return { error: `Ambiguous label: ${target}` }; }// 7. 标签前缀匹配const byLabelPrefix = deduped.filter((entry) => entry.label.toLowerCase().startsWith(lowered));if (byLabelPrefix.length === 1) {return { entry: byLabelPrefix[0] }; }if (byLabelPrefix.length > 1) {return { error: `Ambiguous label prefix: ${target}` }; }// 8. runId 前缀匹配const byRunIdPrefix = deduped.filter((entry) => entry.runId.startsWith(target));if (byRunIdPrefix.length === 1) {return { entry: byRunIdPrefix[0] }; }if (byRunIdPrefix.length > 1) {return { error: `Ambiguous runId prefix: ${target}` }; }return { error: `Unknown target: ${target}` };}1.5 执行流程图
subagents 工具调用 ↓1. 解析 action(list/kill/steer) ↓2. 解析子 agent 控制器 ↓3. 获取子 agent 运行列表 ↓4. 根据 action 执行 ├─ list → 构建列表(活跃 + 最近) ├─ kill → 终止子 agent │ ├─ target=all → 批量终止 │ └─ target=xxx → 定位后终止 └─ steer → 发送消息给子 agent ├─ 检查消息长度(≤4000) ├─ 定位子 agent └─ 发送消息 ↓5. 返回结果1.6 返回结果格式
list 成功:
{"status":"ok","action":"list","total":5,"active":[{"runId":"abc123","label":"文件分析","status":"running","startedAt":1711716000000,"runtimeMs":60000}],"recent":[...],"text":"Active:\n[1] 文件分析 (running, 1m)\n..."}kill 成功:
{"status":"ok","action":"kill","target":"1","killed":1,"labels":["文件分析"],"text":"killed 1 subagent."}steer 成功:
{"status":"ok","action":"steer","target":"文件分析","runId":"abc123","sessionKey":"subagent:abc123","text":"Message sent to subagent."}二、gateway 工具
2.1 工具概述
功能:管理 Gateway 服务核心特性:
• 仅所有者可用(ownerOnly=true) • 6 个 actions(restart/config.get/config.schema.lookup/config.apply/config.patch/update.run) • 配置写入需要 baseHash(乐观锁) • 重启后通知用户(note 参数) • SIGUSR1 信号重启
2.2 Schema 定义
位置:第 25780 行
constGatewayToolSchema = Type.Object({action: stringEnum(["restart","config.get","config.schema.lookup","config.apply","config.patch","update.run" ]),delayMs: Type.Optional(Type.Number()),reason: Type.Optional(Type.String()),gatewayUrl: Type.Optional(Type.String()),gatewayToken: Type.Optional(Type.String()),timeoutMs: Type.Optional(Type.Number()),path: Type.Optional(Type.String()),raw: Type.Optional(Type.String()),baseHash: Type.Optional(Type.String()),sessionKey: Type.Optional(Type.String()),note: Type.Optional(Type.String()),restartDelayMs: Type.Optional(Type.Number())});2.3 完整执行代码
位置:第 25801 行
functioncreateGatewayTool(opts) {return {label: "Gateway",name: "gateway",ownerOnly: true,description: "Restart, inspect a specific config schema path, apply config, or update the gateway in-place (SIGUSR1). Use config.schema.lookup with a targeted dot path before config edits. Use config.patch for safe partial config updates (merges with existing). Use config.apply only when replacing entire config. Both trigger restart after writing. Always pass a human-readable completion message via the `note` parameter so the system can deliver it to the user after restart.",parameters: GatewayToolSchema,execute: async (_toolCallId, args) => {const params = args;// 1. 解析 action(必填)const action = readStringParam$1(params, "action", { required: true });// === action: restart ===if (action === "restart") {// 检查重启是否启用if (!isRestartEnabled(opts?.config)) {thrownewError("Gateway restart is disabled (commands.restart=false)."); }const sessionKey = typeof params.sessionKey === "string" && params.sessionKey.trim() ? params.sessionKey.trim() : opts?.agentSessionKey?.trim() || void0;const delayMs = typeof params.delayMs === "number" && Number.isFinite(params.delayMs) ? Math.floor(params.delayMs) : void0;const reason = typeof params.reason === "string" && params.reason.trim() ? params.reason.trim().slice(0, 200) : void0;const note = typeof params.note === "string" && params.note.trim() ? params.note.trim() : void0;const { deliveryContext, threadId } = extractDeliveryInfo(sessionKey);// 写入重启 sentinelconst payload = {kind: "restart",status: "ok",ts: Date.now(), sessionKey, deliveryContext, threadId,message: note ?? reason ?? null,doctorHint: formatDoctorNonInteractiveHint(),stats: {mode: "gateway.restart", reason } };try {awaitwriteRestartSentinel(payload); } catch {} log$28.info(`gateway tool: restart requested (delayMs=${delayMs ?? "default"}, reason=${reason ?? "none"})`);returnjsonResult(scheduleGatewaySigusr1Restart({ delayMs, reason })); }// 读取 Gateway 调用选项const gatewayOpts = readGatewayCallOptions(params);// 解析 Gateway 写入元数据constresolveGatewayWriteMeta = () => {return {sessionKey: typeof params.sessionKey === "string" && params.sessionKey.trim() ? params.sessionKey.trim() : opts?.agentSessionKey?.trim() || void0,note: typeof params.note === "string" && params.note.trim() ? params.note.trim() : void0,restartDelayMs: typeof params.restartDelayMs === "number" && Number.isFinite(params.restartDelayMs) ? Math.floor(params.restartDelayMs) : void0 }; };// 解析配置写入参数constresolveConfigWriteParams = async () => {const raw = readStringParam$1(params, "raw", { required: true });let baseHash = readStringParam$1(params, "baseHash");if (!baseHash) { baseHash = resolveBaseHashFromSnapshot(awaitcallGatewayTool("config.get", gatewayOpts, {}) ); }if (!baseHash) {thrownewError("Missing baseHash from config snapshot."); }return { raw, baseHash, ...resolveGatewayWriteMeta() }; };// === action: config.get ===if (action === "config.get") {returnjsonResult({ok: true,result: awaitcallGatewayTool("config.get", gatewayOpts, {}) }); }// === action: config.schema.lookup ===if (action === "config.schema.lookup") {returnjsonResult({ok: true,result: awaitcallGatewayTool("config.schema.lookup", gatewayOpts, {path: readStringParam$1(params, "path", {required: true,label: "path" }) }) }); }// === action: config.apply ===if (action === "config.apply") {const { raw, baseHash, sessionKey, note, restartDelayMs } = awaitresolveConfigWriteParams();returnjsonResult({ok: true,result: awaitcallGatewayTool("config.apply", gatewayOpts, { raw, baseHash, sessionKey, note, restartDelayMs }) }); }// === action: config.patch ===if (action === "config.patch") {const { raw, baseHash, sessionKey, note, restartDelayMs } = awaitresolveConfigWriteParams();returnjsonResult({ok: true,result: awaitcallGatewayTool("config.patch", gatewayOpts, { raw, baseHash, sessionKey, note, restartDelayMs }) }); }// === action: update.run ===if (action === "update.run") {const { sessionKey, note, restartDelayMs } = resolveGatewayWriteMeta();const updateTimeoutMs = gatewayOpts.timeoutMs ?? DEFAULT_UPDATE_TIMEOUT_MS;returnjsonResult({ok: true,result: awaitcallGatewayTool("update.run", { ...gatewayOpts,timeoutMs: updateTimeoutMs }, { sessionKey, note, restartDelayMs,timeoutMs: updateTimeoutMs }) }); }thrownewError(`Unknown action: ${action}`); } };}2.5 配置写入流程
// 1. 获取当前配置快照const config = awaitcallGatewayTool("config.get", gatewayOpts, {});// 2. 提取 baseHash(乐观锁)const baseHash = resolveBaseHashFromSnapshot(config);// 3. 准备新配置const raw = JSON.stringify({ ...config, newSetting: "value" });// 4. 写入配置(config.apply 或 config.patch)awaitcallGatewayTool("config.apply", gatewayOpts, { raw, baseHash, sessionKey, note, restartDelayMs});// 5. Gateway 自动重启(SIGUSR1)2.6 执行流程图
gateway 工具调用 ↓1. 解析 action(必填) ↓2. 根据 action 执行 ├─ restart → 写入 sentinel + SIGUSR1 ├─ config.get → 获取配置 ├─ config.schema.lookup → 查询 schema ├─ config.apply → 应用完整配置 ├─ config.patch → 应用部分配置 └─ update.run → 执行更新 ↓3. 返回结果2.7 返回结果格式
restart 成功:
{"ok":true,"result":{"status":"scheduled","delayMs":1000,"reason":"config update"}}config.get 成功:
{"ok":true,"result":{"hash":"abc123","config":{ ... }}}config.apply 失败(baseHash 不匹配):
{"ok":false,"error":"Config hash mismatch. Please refresh and retry."}三、关键机制对比
3.1 权限控制
| 所有者限制 | ||
| A2A 策略 | ||
| 可见性检查 |
3.2 操作类型
| actions | ||
| 定位目标 | ||
| 批量操作 |
3.3 安全限制
| 消息长度 | ||
| 乐观锁 | ||
| 重启限制 |
四、使用示例
4.1 subagents 工具调用
用户:列出所有子 agent
大模型返回:
{"tool_call":{"name":"subagents","arguments":{"action":"list"}}}执行结果:
{"status":"ok","action":"list","total":3,"active":[...],"recent":[...],"text":"Active:\n[1] 文件分析 (running, 1m)\n[2] 数据处理 (running, 5m)\n..."}4.2 gateway 工具调用
用户:重启 Gateway
大模型返回:
{"tool_call":{"name":"gateway","arguments":{"action":"restart","note":"配置更新完成"}}}执行结果:
{"ok":true,"result":{"status":"scheduled","delayMs":1000,"reason":null}}
夜雨聆风