From c401ebabab27490eeb717f9b48221515eac888c3 Mon Sep 17 00:00:00 2001 From: Mark Buckaway Date: Fri, 11 Sep 2026 14:23:05 -0400 Subject: [PATCH 1/3] feat: make AgentSwarm the preferred parallel path A prompt-only nudge (v0.39.1-MB.1.41) did not change tool selection. The bias is structural: AgentSwarm could only express homogeneous item batches, its own description routed differently-shaped work to Agent, and the swarm-mode workflow reminder was off by default. - Reword the swarm choice as a MUST in agent.md, the explore profile, and both system prompts; drop the AgentSwarm "use separate Agent calls" redirect. - Add a `prompts` input to AgentSwarm so differently-shaped subtasks run as one swarm. - Default `default_swarm_mode` to true so new sessions start in swarm mode. - Drop the AgentSwarm sole-tool-call veto; it conflicted with the system prompt's parallel-tool-call directive. --- .changeset/swarm-preference-must.md | 5 + docs/en/configuration/config-files.md | 2 +- docs/en/reference/tools.md | 4 +- docs/zh/configuration/config-files.md | 2 +- docs/zh/reference/tools.md | 4 +- fork/PATCHES.md | 1 + packages/agent-core-v2/docs/Permission.md | 17 +- .../agent-core-v2/docs/config-manifest.toml | 2 +- .../src/agent/tools/agent/agent.md | 2 + .../src/app/agentProfileCatalog/system.md | 2 +- .../src/features/swarm/agent/swarmService.ts | 42 --- .../src/features/swarm/configSection.ts | 2 +- .../swarm/tools/agent-swarm/agent-swarm.md | 10 +- .../swarm/tools/agent-swarm/agent-swarm.ts | 7 + .../swarm/tools/agent-swarm/agentSwarmTool.ts | 52 +++- .../agentLifecycle/profile/profiles.ts | 2 +- .../fullCompaction/fullCompaction.test.ts | 16 +- .../test/agent/loop/loop.test.ts | 4 +- .../test/features/swarm/swarm.test.ts | 240 ++++++++++++++++-- packages/agent-core-v2/test/tool/tool.test.ts | 10 +- .../sessionLifecycle/sessionLifecycle.test.ts | 40 ++- .../policies/agent-swarm-exclusive-deny.ts | 45 ---- .../src/agent/permission/policies/index.ts | 3 - .../src/profile/default/explore.yaml | 2 +- .../agent-core/src/profile/default/system.md | 2 +- packages/agent-core/src/rpc/core-impl.ts | 4 +- .../builtin/collaboration/agent-swarm.md | 10 +- .../builtin/collaboration/agent-swarm.ts | 50 +++- .../src/tools/builtin/collaboration/agent.md | 2 + .../agent-core/test/agent/permission.test.ts | 132 ---------- .../test/harness/skill-session.test.ts | 15 +- .../test/tools/builtin-current.test.ts | 209 +++++++++++++++ 32 files changed, 628 insertions(+), 312 deletions(-) create mode 100644 .changeset/swarm-preference-must.md delete mode 100644 packages/agent-core/src/agent/permission/policies/agent-swarm-exclusive-deny.ts diff --git a/.changeset/swarm-preference-must.md b/.changeset/swarm-preference-must.md new file mode 100644 index 00000000000..885e237b70d --- /dev/null +++ b/.changeset/swarm-preference-must.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": minor +--- + +Start new sessions in Swarm mode by default, and let `AgentSwarm` take a list of distinct prompts so differently-shaped subtasks can run as one parallel swarm. diff --git a/docs/en/configuration/config-files.md b/docs/en/configuration/config-files.md index c3ba3354e21..0b957b29cb8 100644 --- a/docs/en/configuration/config-files.md +++ b/docs/en/configuration/config-files.md @@ -104,7 +104,7 @@ Fields in the config file fall into two categories: **top-level scalars** that d | `planning_model` | `string` | — | Model alias used while Plan mode is active; must be defined in `models` and have the same context window as `default_model` | | `default_permission_mode` | `string` | `manual` | Default permission mode for new sessions; one of `manual` (prompt each time), `yolo` (auto-approve tool actions, but the agent may still ask questions), or `auto` (fully autonomous — the agent decides everything without asking) | | `default_plan_mode` | `boolean` | `false` | Whether new sessions start in Plan mode (produce a plan before executing) by default | -| `default_swarm_mode` | `boolean` | `false` | Whether new sessions start in Swarm mode (prefer parallel `AgentSwarm` subagents) by default | +| `default_swarm_mode` | `boolean` | `true` | Whether new sessions start in Swarm mode (prefer parallel `AgentSwarm` subagents) by default | | `merge_all_available_skills` | `boolean` | `true` | Whether to merge Agent Skills from all available directories | | `extra_skill_dirs` | `array` | — | Extra skill search directories, layered on top of the default directories | | `extra_agent_dirs` | `array` | — | Extra custom agent search directories, layered on top of the default directories | diff --git a/docs/en/reference/tools.md b/docs/en/reference/tools.md index 88a4a87d928..a1b3ddd4d9f 100644 --- a/docs/en/reference/tools.md +++ b/docs/en/reference/tools.md @@ -114,13 +114,13 @@ Collaboration tools handle inter-Agent coordination, user interaction, and Skill | Tool | Default Approval | Description | | --- | --- | --- | | `Agent` | Auto-allow | Spawn a sub-Agent to execute a subtask | -| `AgentSwarm` | Auto-allow in swarm mode; otherwise requires approval | Launch item-based subagents or resume existing subagents | +| `AgentSwarm` | Auto-allow in swarm mode; otherwise requires approval | Launch item- or prompt-based subagents, or resume existing subagents | | `AskUserQuestion` | Auto-allow | Ask the user a question to gather structured input | | `Skill` | Auto-allow | Invoke a registered inline Skill | **`Agent`** delegates a subtask to a sub-Agent. Required parameters: `prompt` (complete task description) and `description` (a 3–5 word short summary). Optional parameters: `subagent_type` (defaults to `coder`), `resume` (ID of an existing Agent to resume; mutually exclusive with `subagent_type`), `run_in_background` (defaults to false), and `model` (available only when the [subagent model pool](../configuration/config-files.md#subagent-model-pool) experiment is enabled and a pool is configured — a `[secondary_model.models]` table or a lone `default_model`: a pool alias, or `"primary"` for the model the caller itself is running; ignored when resuming). Without it, the subagent binds the pool's `default_model`; without a configured pool, subagents always inherit the caller's model. Agent tasks time out after 2 hours by default; the limit is configurable via `[subagent] timeout_ms` in `config.toml` (`0` = no timeout, or the `KIMI_SUBAGENT_TIMEOUT_MS` env var), and defaults to no timeout in print mode (`kimi -p`). In foreground mode the parent Agent waits for the sub-Agent to complete before continuing; in background mode a task ID is returned immediately and the result is automatically delivered back to the main Agent via a synthetic User message when done. When several foreground `Agent` calls run in the same step, the TUI groups them and shows each subagent's running, waiting, completed, or failed status with elapsed time. See [Agent & Sub-Agents](../customization/agents.md) for details. -**`AgentSwarm`** launches subagents from a shared `prompt_template` and an `items` array, resumes existing subagents through `resume_agent_ids`, or combines both in one call. The template must contain the `{{item}}` placeholder; each item replaces that placeholder and launches one new subagent. Pass `subagent_type` to choose the profile used by every spawned subagent in the swarm, or omit it to use `coder`. Pass `model` (available only when the [subagent model pool](../configuration/config-files.md#subagent-model-pool) experiment is enabled and a pool is configured — a `[secondary_model.models]` table or a lone `default_model`) to run item-spawned subagents on a pool alias or on the caller's own model (`"primary"`). Without it, item-spawned subagents bind the pool's `default_model`; without a configured pool, they inherit the caller's model. Resumed subagents keep their own model. Without `resume_agent_ids`, the tool requires at least 2 items; with `resume_agent_ids`, it can resume one or more existing subagents. The tool supports up to 128 total subagents, waits for all subagents to finish, and returns an aggregated report. Each subagent times out after 2 hours by default; the limit is configurable via [`[swarm] timeout_ms`](../configuration/config-files.md#swarm) in `config.toml` (`0` = no timeout, or the `KIMI_CODE_SWARM_TIMEOUT_MS` env var), and defaults to no timeout in print mode (`kimi -p`). A timed-out subagent is aborted and marked as failed in the aggregated report. In the TUI, foreground swarms show a live `Agent swarm` progress panel above the input box. If a model response calls `AgentSwarm`, that call must be the only tool call in the response; to run multiple swarms, call one `AgentSwarm`, wait for its result, then call the next, or combine the work into one swarm when a single template can cover it. In `manual` permission mode, `AgentSwarm` calls outside active swarm mode request approval unless a permission rule allows them; while swarm mode is active, `AgentSwarm` itself is auto-approved. Permission rules match `AgentSwarm` by tool name only — argument patterns such as `AgentSwarm(swarm)` are not supported. By default the tool ramps up concurrency without an upper limit (5 subagents start immediately, then 1 more every 700 ms); set `KIMI_CODE_AGENT_SWARM_MAX_CONCURRENCY` to a positive integer to cap how many subagents run at the same time during that ramp, or leave it unset for no cap. If it is set to a value that is not a positive integer, the AgentSwarm call fails fast. +**`AgentSwarm`** launches subagents from a shared `prompt_template` and an `items` array, resumes existing subagents through `resume_agent_ids`, or combines both in one call. Alternatively, pass `prompts` — an array of at least 2 distinct full prompts, one subagent per prompt — instead of `items` and `prompt_template`; `prompts` cannot be combined with either. The template must contain the `{{item}}` placeholder; each item replaces that placeholder and launches one new subagent. Pass `subagent_type` to choose the profile used by every spawned subagent in the swarm, or omit it to use `coder`. Pass `model` (available only when the [subagent model pool](../configuration/config-files.md#subagent-model-pool) experiment is enabled and a pool is configured — a `[secondary_model.models]` table or a lone `default_model`) to run item-spawned subagents on a pool alias or on the caller's own model (`"primary"`). Without it, item-spawned subagents bind the pool's `default_model`; without a configured pool, they inherit the caller's model. Resumed subagents keep their own model. Without `resume_agent_ids`, the tool requires at least 2 items or prompts; with `resume_agent_ids`, it can resume one or more existing subagents. The tool supports up to 128 total subagents, waits for all subagents to finish, and returns an aggregated report. Each subagent times out after 2 hours by default; the limit is configurable via [`[swarm] timeout_ms`](../configuration/config-files.md#swarm) in `config.toml` (`0` = no timeout, or the `KIMI_CODE_SWARM_TIMEOUT_MS` env var), and defaults to no timeout in print mode (`kimi -p`). A timed-out subagent is aborted and marked as failed in the aggregated report. In the TUI, foreground swarms show a live `Agent swarm` progress panel above the input box. In `manual` permission mode, `AgentSwarm` calls outside active swarm mode request approval unless a permission rule allows them; while swarm mode is active, `AgentSwarm` itself is auto-approved. Permission rules match `AgentSwarm` by tool name only — argument patterns such as `AgentSwarm(swarm)` are not supported. By default the tool ramps up concurrency without an upper limit (5 subagents start immediately, then 1 more every 700 ms); set `KIMI_CODE_AGENT_SWARM_MAX_CONCURRENCY` to a positive integer to cap how many subagents run at the same time during that ramp, or leave it unset for no cap. If it is set to a value that is not a positive integer, the AgentSwarm call fails fast. **`AskUserQuestion`** asks the user a structured multiple-choice question — useful for disambiguation or option selection. The `questions` parameter accepts 1–4 questions; each question requires `question` (ending with `?`), `options` (2–4 choices, each with a `label` and `description`), and optional `header` (max 12 characters) and `multi_select` (defaults to false). An "Other" option is appended automatically. Setting `background` to true starts a background question task and returns a task ID immediately. When the host does not support interactive questioning, a failure message is returned and the Agent should ask the user directly in a text reply instead. diff --git a/docs/zh/configuration/config-files.md b/docs/zh/configuration/config-files.md index dc2f740c10a..7aa2ad2c524 100644 --- a/docs/zh/configuration/config-files.md +++ b/docs/zh/configuration/config-files.md @@ -104,7 +104,7 @@ timeout = 5 | `planning_model` | `string` | — | Plan 模式激活时使用的模型别名,必须在 `models` 中定义,且上下文窗口需与 `default_model` 相同 | | `default_permission_mode` | `string` | `manual` | 新会话的默认权限模式,可选 `manual`(逐次询问)、`yolo`(自动批准工具操作,Agent 仍可能提问)、`auto`(完全自主,Agent 自己做决定,不再提问) | | `default_plan_mode` | `boolean` | `false` | 新会话是否默认以 Plan 模式(先出计划再执行)启动 | -| `default_swarm_mode` | `boolean` | `false` | 新会话是否默认以 Swarm 模式(优先使用并行的 `AgentSwarm` 子代理)启动 | +| `default_swarm_mode` | `boolean` | `true` | 新会话是否默认以 Swarm 模式(优先使用并行的 `AgentSwarm` 子代理)启动 | | `merge_all_available_skills` | `boolean` | `true` | 是否合并所有目录中的 Agent Skills | | `extra_skill_dirs` | `array` | — | 额外 Skill 搜索目录,叠加到默认目录之上 | | `extra_agent_dirs` | `array` | — | 额外自定义 Agent 搜索目录,叠加到默认目录之上 | diff --git a/docs/zh/reference/tools.md b/docs/zh/reference/tools.md index e4295abdea4..344bf821470 100644 --- a/docs/zh/reference/tools.md +++ b/docs/zh/reference/tools.md @@ -114,13 +114,13 @@ Supermoon 模式是最大严谨度的工作状态:开启后,Agent 默认用 | 工具 | 默认审批 | 说明 | | --- | --- | --- | | `Agent` | 自动放行 | 派生 subagent 执行子任务 | -| `AgentSwarm` | swarm mode 中自动放行,否则需审批 | 启动基于 item 的 subagent,或恢复已有 subagent | +| `AgentSwarm` | swarm mode 中自动放行,否则需审批 | 从 item 或 prompt 启动 subagent,或恢复已有 subagent | | `AskUserQuestion` | 自动放行 | 向用户提问以获取结构化输入 | | `Skill` | 自动放行 | 调用已注册的 inline Skill | **`Agent`** 将子任务委托给 subagent 执行。必填参数:`prompt`(完整任务描述)和 `description`(3–5 个词的简短说明)。可选参数:`subagent_type`(默认 `coder`)、`resume`(恢复已有 Agent 的 ID,与 `subagent_type` 互斥)、`run_in_background`(默认 false)和 `model`(仅在启用 [subagent 模型池](../configuration/config-files.md#subagent-模型池) 实验功能并配置模型池后可用——`[secondary_model.models]` 表或仅一行 `default_model`:池中别名,或 `"primary"` 表示调用方自己运行的模型;resume 时无效)。未传入时 subagent 绑定池的 `default_model`;未配置模型池时,subagent 一律继承调用方模型。Agent 任务默认 2 小时超时,可通过 `config.toml` 的 `[subagent] timeout_ms`(`0` = 无超时,或 `KIMI_SUBAGENT_TIMEOUT_MS` 环境变量)配置,且在 print 模式(`kimi -p`)下默认无超时。前台模式下父 Agent 等待 subagent 完成再继续;后台模式立即返回任务 ID,完成时通过合成 User 消息自动回到 main agent。多个前台 `Agent` 调用在同一步运行时,TUI 会合并展示,并为每个 subagent 显示运行、等待、完成或失败状态以及已耗时长。subagent 体系细节见 [Agent 与 subagent](../customization/agents.md)。 -**`AgentSwarm`** 可以从共享的 `prompt_template` 和 `items` 数组启动 subagent,也可以通过 `resume_agent_ids` 恢复已有 subagent,或在一次调用中同时使用两者。模板必须包含 `{{item}}` 占位符;每个 item 会替换该占位符,并启动一个新的 subagent。传入 `subagent_type` 可以指定整个 swarm 中所有新启动的 subagent 使用的 profile;省略时默认使用 `coder`。传入 `model`(仅在启用 [subagent 模型池](../configuration/config-files.md#subagent-模型池) 实验功能并配置模型池后可用——`[secondary_model.models]` 表或仅一行 `default_model`)可以让新启动的 subagent 运行在池中别名指定的模型或调用方自己的模型(`"primary"`)上。未传入时新启动的 subagent 绑定池的 `default_model`;未配置模型池时则继承调用方模型。恢复的 subagent 保持其原有模型。不传 `resume_agent_ids` 时,本工具要求至少 2 个 item;传入 `resume_agent_ids` 时,可以恢复 1 个或多个已有 subagent。本工具最多支持 128 个 subagent,会等待全部 subagent 完成,并返回聚合报告。每个 subagent 默认 2 小时超时,可通过 `config.toml` 的 [`[swarm] timeout_ms`](../configuration/config-files.md#swarm)(`0` = 无超时,或 `KIMI_CODE_SWARM_TIMEOUT_MS` 环境变量)配置,且在 print 模式(`kimi -p`)下默认无超时;超时的 subagent 会被中止,并在聚合报告中标记为失败。在 TUI 中,前台 swarm 会在输入框上方显示实时 `Agent swarm` 进度面板。若一次模型响应调用 `AgentSwarm`,该调用必须是该响应中的唯一工具调用;如需运行多个 swarm,应先调用一个 `AgentSwarm` 并等待结果,再调用下一个,若单个模板可以覆盖这些工作,也可以合并为一个 swarm。在 `manual` 权限模式下,未处于 swarm mode 时调用 `AgentSwarm` 会触发审批,除非已有权限规则允许;swarm mode 已开启时,`AgentSwarm` 本身会自动放行。权限规则只能按工具名 `AgentSwarm` 匹配,不支持 `AgentSwarm(swarm)` 这类参数模式。默认情况下,本工具会逐步提升并发且不设上限(立即启动 5 个 subagent,之后每 700 毫秒再启动 1 个);将 `KIMI_CODE_AGENT_SWARM_MAX_CONCURRENCY` 设为正整数可限制该阶段同时运行的 subagent 数量,不设置则表示不限制。若设置为非正整数的值,本次 AgentSwarm 调用会立即失败。 +**`AgentSwarm`** 可以从共享的 `prompt_template` 和 `items` 数组启动 subagent,也可以通过 `resume_agent_ids` 恢复已有 subagent,或在一次调用中同时使用两者。也可以改用 `prompts`(由至少 2 条互不相同的完整提示词组成的数组,每条对应一个 subagent)来代替 `items` 和 `prompt_template`;`prompts` 不能与二者同时传入。模板必须包含 `{{item}}` 占位符;每个 item 会替换该占位符,并启动一个新的 subagent。传入 `subagent_type` 可以指定整个 swarm 中所有新启动的 subagent 使用的 profile;省略时默认使用 `coder`。传入 `model`(仅在启用 [subagent 模型池](../configuration/config-files.md#subagent-模型池) 实验功能并配置模型池后可用——`[secondary_model.models]` 表或仅一行 `default_model`)可以让新启动的 subagent 运行在池中别名指定的模型或调用方自己的模型(`"primary"`)上。未传入时新启动的 subagent 绑定池的 `default_model`;未配置模型池时则继承调用方模型。恢复的 subagent 保持其原有模型。不传 `resume_agent_ids` 时,本工具要求至少 2 个 item 或 prompt;传入 `resume_agent_ids` 时,可以恢复 1 个或多个已有 subagent。本工具最多支持 128 个 subagent,会等待全部 subagent 完成,并返回聚合报告。每个 subagent 默认 2 小时超时,可通过 `config.toml` 的 [`[swarm] timeout_ms`](../configuration/config-files.md#swarm)(`0` = 无超时,或 `KIMI_CODE_SWARM_TIMEOUT_MS` 环境变量)配置,且在 print 模式(`kimi -p`)下默认无超时;超时的 subagent 会被中止,并在聚合报告中标记为失败。在 TUI 中,前台 swarm 会在输入框上方显示实时 `Agent swarm` 进度面板。在 `manual` 权限模式下,未处于 swarm mode 时调用 `AgentSwarm` 会触发审批,除非已有权限规则允许;swarm mode 已开启时,`AgentSwarm` 本身会自动放行。权限规则只能按工具名 `AgentSwarm` 匹配,不支持 `AgentSwarm(swarm)` 这类参数模式。默认情况下,本工具会逐步提升并发且不设上限(立即启动 5 个 subagent,之后每 700 毫秒再启动 1 个);将 `KIMI_CODE_AGENT_SWARM_MAX_CONCURRENCY` 设为正整数可限制该阶段同时运行的 subagent 数量,不设置则表示不限制。若设置为非正整数的值,本次 AgentSwarm 调用会立即失败。 **`AskUserQuestion`** 以结构化多选题的形式向用户提问,适用于需要消歧或选择方案的场景。`questions` 参数接受 1–4 道题,每道题需提供 `question`(以 `?` 结尾)、`options`(2–4 个选项,每项含 `label` 和 `description`)以及可选的 `header`(最多 12 字符)和 `multi_select`(默认 false)。系统自动附加"其他"选项。`background` 为 true 时启动后台问题任务并立即返回任务 ID。宿主未实现交互式提问能力时返回失败提示,Agent 应改为在文本回复中直接提问。 diff --git a/fork/PATCHES.md b/fork/PATCHES.md index fed23785dbe..c9c1191cdab 100644 --- a/fork/PATCHES.md +++ b/fork/PATCHES.md @@ -39,6 +39,7 @@ mark the row `merged-upstream` (keep the row as history). | disable kimi OAuth by default | `feat/disable-kimi-oauth-by-default` | Kimi OAuth login (kimi.com/kimi.ai device-code) disabled by default; opt-in via `[experimental] kimi_oauth` / `KIMI_CODE_EXPERIMENTAL_KIMI_OAUTH` / `kimi web --enable-kimi-oauth`; token/apiKey auth stays the default; web + TUI hide the login unless enabled | — | — | not-submitting | | Windows native installer | `feat/windows-installer` | Fork-style `install.ps1` (win32-x64/arm64, GitHub Releases + gh-pages channel); publish `install.ps1` to the update channel; point `README.md` + `README.zh-CN.md` + `fork/README.md` at the fork install commands | — | — | not-submitting | | swarm-task preference + default_swarm_mode | `fix/swarm-task-preference` | Restore the #3028-removed proactiveness/action directives and add explicit swarm-preference + subagent-rules guidance to both engines' system prompts; add a `default_swarm_mode` config (v1 + v2 + protocol) so new sessions can start in swarm mode | — | [#3433](https://github.com/MoonshotAI/kimi-code/issues/3433) | local | +| swarm preference round 2 (MUST + un-gate) | `fix/swarm-must` | Make the swarm choice a MUST in `agent.md`, the `explore` profile, and both system prompts; delete the AgentSwarm "use separate `Agent` calls" redirect; add a `prompts` heterogeneous encoding to AgentSwarm; flip `default_swarm_mode` to true; drop the AgentSwarm sole-tool-call veto | — | [#3433](https://github.com/MoonshotAI/kimi-code/issues/3433) | local | Changesets under `.changeset/` deliberately name the upstream package `@moonshot-ai/kimi-code`, not the fork's `@mbuckaway/kimi-code`, so a change can diff --git a/packages/agent-core-v2/docs/Permission.md b/packages/agent-core-v2/docs/Permission.md index 546fb97b356..07e16c6185d 100644 --- a/packages/agent-core-v2/docs/Permission.md +++ b/packages/agent-core-v2/docs/Permission.md @@ -4,7 +4,7 @@ > **权限系统应是一个「可组合、可注册的责任链(微内核)」**:内核只负责按顺序跑链、首个命中赢;具体权限维度(policy)由各自的 Domain Service 通过注册表插入;工具只需在 `resolveExecution` 里声明标准化的资源访问(`accesses`),通用维度集中消费这份元数据。 > -> **链只裁决危险程度**。policy 节点回答的是「这个调用有多危险、用户能否逐次豁免这个判断」——它产出的 `ask`/`deny` 永远可被用户豁免。**Harness 约束不是权限**:运行机制为自身正确性施加的限制(plan 模式禁写、AgentSwarm 批量排他、btw side-question fork 禁工具、goal 预算拒绝)产出的是无 ask 通道、用户无法逐次豁免的硬 deny,它们以 `onBeforeExecuteTool` veto 监听器挂在各自 domain,用 `event.veto(...)` 表态(先例:`goalService.ts` 的预算/过期拒绝)。产物审批(plan review、goal-start review)同样不是权限:由 owning domain 用 cold 的 `event.waitUntil(factory)` 拦截自己的工具、直接驱动共享的 `IAgentToolApprovalService` 审批往返——审批只可能在没有任何监听器 veto 该调用之后才开始。 +> **链只裁决危险程度**。policy 节点回答的是「这个调用有多危险、用户能否逐次豁免这个判断」——它产出的 `ask`/`deny` 永远可被用户豁免。**Harness 约束不是权限**:运行机制为自身正确性施加的限制(plan 模式禁写、btw side-question fork 禁工具、goal 预算拒绝)产出的是无 ask 通道、用户无法逐次豁免的硬 deny,它们以 `onBeforeExecuteTool` veto 监听器挂在各自 domain,用 `event.veto(...)` 表态(先例:`goalService.ts` 的预算/过期拒绝)。产物审批(plan review、goal-start review)同样不是权限:由 owning domain 用 cold 的 `event.waitUntil(factory)` 拦截自己的工具、直接驱动共享的 `IAgentToolApprovalService` 审批往返——审批只可能在没有任何监听器 veto 该调用之后才开始。 > > **不引入 Casbin**——因为这里「难的是决策行为」(续体、副作用、RPC、状态机),不是「匹配 + 标量决策」。 @@ -17,7 +17,7 @@ 这个决策有三个特点,决定了它的架构取向: 1. **决策携带行为**。返回 `ask` 不是一个枚举值,而是一条含 RPC 往返、hook、telemetry、状态写入、续体的工作流;返回 `deny` 可能是执行了一段外部 hook 的结果。 -2. **策略异质**。有的查工具名集合,有的数同批 AgentSwarm 个数,有的跑 hook,有的检查 plan 状态机——没有统一的 `(sub, obj, act)` 形状。 +2. **策略异质**。有的查工具名集合,有的按运行模式放行,有的跑 hook,有的检查 plan 状态机——没有统一的 `(sub, obj, act)` 形状。 3. **多 agent × 多 mode × 外部扩展**。不同 agent / mode 需要不同权限,且要允许外部(组织管理员、插件)解耦地贡献规则或行为。 --- @@ -48,14 +48,14 @@ type PermissionPolicyResult = | { kind: 'ask'; reason?; resolveApproval?; resolveError? }; ``` -### 2.2 11 个权限维度(19 个 policy) +### 2.2 11 个权限维度(18 个 policy) -链目前在 `policies/index.ts#createPermissionDecisionPolicies()` 中**硬编码**,顺序即优先级。19 个 policy 可归并为 11 个权限维度: +链目前在 `policies/index.ts#createPermissionDecisionPolicies()` 中**硬编码**,顺序即优先级。18 个 policy 可归并为 11 个权限维度: | # | 维度 | 对应 policy | 决策看什么 | |---|---|---|---| | 1 | 外部钩子否决 | `pre-tool-call-hook` | 用户 `PreToolUse` hook 是否返回 block | -| 2 | 工具批量排他 | `agent-swarm-exclusive-deny`、`swarm-mode-agent-swarm-approve` | 同批工具结构(AgentSwarm 须单独)+ swarm 模式 | +| 2 | swarm 模式放行 | `swarm-mode-agent-swarm-approve` | swarm 模式是否激活 | | 3 | 运行模式姿态 | `auto-mode-approve`、`yolo-mode-approve`、`auto-mode-ask-user-question-deny` | `permission.mode` | | 4 | Plan 模式约束 | `plan-mode-guard-deny`、`plan-mode-tool-approve`、`exit-plan-mode-review-ask` | `planMode.isActive` + plan 文件路径 + review 状态 | | 5 | Goal 启动审批 | `goal-start-review-ask` | `tool === CreateGoal` 且非 auto | @@ -91,7 +91,7 @@ interface RunnableToolExecution { ### 2.5 痛点 -1. **链硬编码**。19 个 policy 在一个函数里 `new`,外部无法贡献。 +1. **链硬编码**。18 个 policy 在一个函数里 `new`,外部无法贡献。 2. **mode 是 policy 内部的 `if`**。`YoloModeApprove` / `AutoModeApprove` 各自 `if (mode !== 'x') return`,"不同 mode 不同链"只能靠塞更多 self-guard 的 policy。 3. **没有按 agent 区分链的入口**(只有散落的 `agent.type === 'sub'` 判断)。 4. **没有外部扩展点**。唯一的外部介入是 `PreToolUse` hook(占 guard 一个固定槽位)。 @@ -274,7 +274,6 @@ type ToolResourceAccess = | 维度 | 拥有者 | 类型 | |---|---|---| | 外部钩子否决 | `externalHooks` domain | 通用 | -| 工具批量排他 | `swarm` domain —— `onBeforeExecuteTool` veto 监听器 | Harness 约束(链外) | | Plan 写守卫 | `plan` domain —— `onBeforeExecuteTool` veto 监听器 | Harness 约束(链外) | | Plan 审批 | `plan` domain —— 同监听器的 `waitUntil` + `toolApproval` | 产物审批(链外) | | Goal 启动审批 | `goal` domain —— veto 监听器的 `waitUntil` + `toolApproval` | 产物审批(链外) | @@ -297,7 +296,7 @@ type ToolResourceAccess = | 方面 | 现状(v1) | 目标方案 | |---|---|---| -| 链的构造 | `policies/index.ts` 硬编码 19 个 `new` | `IPermissionPolicyRegistry` 收集,`compose(agent, mode)` 组装 | +| 链的构造 | `policies/index.ts` 硬编码 18 个 `new` | `IPermissionPolicyRegistry` 收集,`compose(agent, mode)` 组装 | | mode 处理 | policy 内部 `if (mode !== 'x') return` | 声明式 `modes` 元数据,compose 时过滤 | | 按 agent 区分 | 散落 `agent.type === 'sub'` | 声明式 `agentTypes` 元数据 | | 外部扩展 | 仅 `PreToolUse` hook 一个固定槽 | 注册表开放注册 policy(代码)+ rule(数据) | @@ -316,7 +315,7 @@ type ToolResourceAccess = 渐进式,避免一步到位: -1. ~~**Domain 维度下沉**~~(已完成)。plan guard/review、goal-start review、swarm 批量排他、btw deny-all 已从链上移出,以 `onBeforeExecuteTool` veto 监听器挂在各自 domain(即时 `veto`/`allow`/`pass` 表态 + cold `waitUntil` factory 承载审批往返);审批往返提取为共享的 `IAgentToolApprovalService`;`registerPolicy` 机制删除(btw 是唯一生产用例)。链上只剩 12 个危险度判定节点。 +1. ~~**Domain 维度下沉**~~(已完成)。plan guard/review、goal-start review、btw deny-all 已从链上移出,以 `onBeforeExecuteTool` veto 监听器挂在各自 domain(即时 `veto`/`allow`/`pass` 表态 + cold `waitUntil` factory 承载审批往返);审批往返提取为共享的 `IAgentToolApprovalService`;`registerPolicy` 机制删除(btw 是唯一生产用例)。链上只剩 12 个危险度判定节点。 2. **档位 × 路由拆分**。把「危险度档位」(只读/读写/yolo——`yolo-mode-approve` 的实质)与「交互路由」(`auto-mode-approve` / `auto-mode-ask-user-question-deny` 的实质:不经用户地路由 ask 与 review)拆开;路由层落在 `session/approval` broker 上,剩余 3 个 mode policy 在此步离开链。 3. **注册表 + Composer(行为零变化)**。把 `PermissionPolicyService` 构造函数里硬编码的 `new`,改为从 `IPermissionPolicyRegistry` 读取并组装;mode 守门提升为 `modes` 元数据。获得多 agent/mode 可选链与外部注册入口。 4. **第四步(按需):扩展资源类型**。当非文件资源(网络/DB/shell)需要结构化维度时,扩展 `ToolResourceAccess` 联合。 diff --git a/packages/agent-core-v2/docs/config-manifest.toml b/packages/agent-core-v2/docs/config-manifest.toml index c1e06ebaeb1..b02e79af120 100644 --- a/packages/agent-core-v2/docs/config-manifest.toml +++ b/packages/agent-core-v2/docs/config-manifest.toml @@ -117,7 +117,7 @@ default_plan_mode = false # scope: core # ########################################################################## -default_swarm_mode = false +default_swarm_mode = true # ########################################################################## # experimental diff --git a/packages/agent-core-v2/src/agent/tools/agent/agent.md b/packages/agent-core-v2/src/agent/tools/agent/agent.md index d8b65d7c0dc..69f7e3c1916 100644 --- a/packages/agent-core-v2/src/agent/tools/agent/agent.md +++ b/packages/agent-core-v2/src/agent/tools/agent/agent.md @@ -11,6 +11,8 @@ Usage notes: - A subagent's result is only visible to you, not to the user. When the user needs to see what a subagent produced, summarize the relevant parts yourself in your own reply. - Subagents use a fixed 2-hour timeout. If one times out, resume the same agent instead of starting over. +When the work decomposes into independent parts, you MUST use `AgentSwarm` — not a single sequential `Agent` call. Partition the work and run it as one swarm. + When NOT to use Agent: skip delegation for trivial work you can do directly — reading a file whose path you already know, searching a small known set of files, or any task that takes only a step or two. Delegation has a context-handoff cost; it pays off only when the task is substantial enough to outweigh it. Once a subagent is running, leave that scope to it: do not redo its searches or reads in parallel, and do not abandon it midway and finish the job manually. Both undo the context savings the delegation was meant to buy. diff --git a/packages/agent-core-v2/src/app/agentProfileCatalog/system.md b/packages/agent-core-v2/src/app/agentProfileCatalog/system.md index 98e6a77ef70..ba566ab9050 100644 --- a/packages/agent-core-v2/src/app/agentProfileCatalog/system.md +++ b/packages/agent-core-v2/src/app/agentProfileCatalog/system.md @@ -127,7 +127,7 @@ At any time, you should be HELPFUL, CONCISE, ACCURATE, and CANDID. Be thorough i - When you have evidence the user is wrong, say so and show the evidence — agreeing to be agreeable wastes their time and can break their code. Defer once they've decided; until then, an honest objection is the helpful answer. - When the task requires creating or modifying files, always use tools to do so. Never treat displaying code in your response as a substitute for actually writing it to the file system. - Deliver the complete change. Never stub out code with placeholders like `// ... rest unchanged` or leave the user to fill in the gaps; write out every line you mean to change. -- Prefer the `AgentSwarm` tool (parallel swarm of subagents) over a single sequential `Agent` call whenever the work decomposes into independent parts: partition it, run the subagents in parallel, and cross-check their results. +- You MUST use the `AgentSwarm` tool (parallel swarm of subagents) instead of a single sequential `Agent` call whenever the work decomposes into independent parts: partition it, run the subagents in parallel, and cross-check their results. - When delegating to any subagent, include in its prompt the requirement to read and follow the project's coding standards and `AGENTS.md` before writing code. - After a change, sweep for comments and docstrings that now describe the old behavior, and bring them in line with what the code actually does. - Before calling a task done, verify it: run the checks that cover your change and look at the result instead of assuming. Don't mark work complete while tests are red or the implementation is still partial — this holds whether or not you are tracking the work in a todo list. diff --git a/packages/agent-core-v2/src/features/swarm/agent/swarmService.ts b/packages/agent-core-v2/src/features/swarm/agent/swarmService.ts index ad7565228b8..16069f80ac5 100644 --- a/packages/agent-core-v2/src/features/swarm/agent/swarmService.ts +++ b/packages/agent-core-v2/src/features/swarm/agent/swarmService.ts @@ -3,9 +3,6 @@ import { activateReminderWhenReady } from '#/features/reminder/internal/reminder import { IAgentLifecycleService } from '#/session/agentLifecycle/agentLifecycle'; import { IAgentContextMemoryService } from '#/agent/contextMemory/contextMemory'; import { TurnEnded } from '#/agent/loop/turnOps'; -import { IAgentToolApprovalService } from '#/agent/toolApproval/toolApproval'; -import { denyToolExecution } from '#/agent/toolExecutor/beforeToolExecuteEvent'; -import { IAgentToolExecutorService } from '#/agent/toolExecutor/toolExecutor'; import { IEventBus } from '#/app/event/eventBus'; import { IAgentScopeContext } from '#/agent/scopeContext/scopeContext'; import { IAgentStateService } from '#/agent/state/agentState'; @@ -23,8 +20,6 @@ export class AgentSwarmService extends Service implements IAgentSwarmService { @IAgentLifecycleService agentLifecycle: IAgentLifecycleService, @IEventBus eventBus: IEventBus, @IAgentContextMemoryService private readonly context: IAgentContextMemoryService, - @IAgentToolApprovalService private readonly toolApproval: IAgentToolApprovalService, - @IAgentToolExecutorService toolExecutor: IAgentToolExecutorService, @IAgentScopeContext private readonly agentCtx: IAgentScopeContext, @IAgentStateService private readonly agentState: IAgentStateService, ) { @@ -46,25 +41,6 @@ export class AgentSwarmService extends Service implements IAgentSwarmService { } }), ); - this._register( - toolExecutor.onBeforeExecuteTool((event) => { - const agentSwarmCount = event.toolCalls.filter( - (toolCall) => toolCall.name === 'AgentSwarm', - ).length; - if (agentSwarmCount === 0 || (agentSwarmCount === 1 && event.toolCalls.length === 1)) { - return; - } - event.veto( - denyToolExecution( - this.toolApproval.formatDenyMessage( - agentSwarmCount > 1 - ? multipleAgentSwarmDeniedMessage(event.toolCalls.length > agentSwarmCount) - : mixedAgentSwarmDeniedMessage(), - ), - ), - ); - }), - ); } enter(trigger: SwarmModeTrigger): void { @@ -88,21 +64,3 @@ export class AgentSwarmService extends Service implements IAgentSwarmService { return trigger === 'task' || trigger === 'tool'; } } - -function multipleAgentSwarmDeniedMessage(hasOtherToolCalls: boolean): string { - const suffix = hasOtherToolCalls - ? ' AgentSwarm also must not be combined with other tools in the same response.' - : ''; - return ( - 'AgentSwarm must be called one swarm at a time. Multiple AgentSwarm calls are not forbidden, ' + - 'but issue them sequentially: call one AgentSwarm, wait for its result, then call the next; ' + - `or merge the work into a single AgentSwarm when one swarm can cover it.${suffix}` - ); -} - -function mixedAgentSwarmDeniedMessage(): string { - return ( - 'AgentSwarm must be the only tool call in a model response. Retry with a single AgentSwarm ' + - 'call by itself, then call any other tools after it returns.' - ); -} diff --git a/packages/agent-core-v2/src/features/swarm/configSection.ts b/packages/agent-core-v2/src/features/swarm/configSection.ts index ab6fe9cca20..a319c7fe7f8 100644 --- a/packages/agent-core-v2/src/features/swarm/configSection.ts +++ b/packages/agent-core-v2/src/features/swarm/configSection.ts @@ -50,5 +50,5 @@ export const DefaultSwarmModeSchema = z.boolean().optional(); export type DefaultSwarmMode = z.infer; registerConfigSection(DEFAULT_SWARM_MODE_SECTION, DefaultSwarmModeSchema, { - defaultValue: false, + defaultValue: true, }); diff --git a/packages/agent-core-v2/src/features/swarm/tools/agent-swarm/agent-swarm.md b/packages/agent-core-v2/src/features/swarm/tools/agent-swarm/agent-swarm.md index 62e9ccecd7c..3b1c8a4fd33 100644 --- a/packages/agent-core-v2/src/features/swarm/tools/agent-swarm/agent-swarm.md +++ b/packages/agent-core-v2/src/features/swarm/tools/agent-swarm/agent-swarm.md @@ -1,11 +1,11 @@ Launch multiple subagents from one prompt template, existing agent resumes, or both. -Use AgentSwarm when many subagents should run the same kind of task over different inputs. The placeholder is exactly `{{item}}`. For example, with `prompt_template` set to `Review {{item}} for likely regressions.` and `items` set to `["src/a.ts", "src/b.ts"]`, AgentSwarm launches two new subagents with those two concrete prompts. For a few differently-shaped tasks, make separate `Agent` calls in one message instead. +Use AgentSwarm when many subagents should run the same kind of task over different inputs. The placeholder is exactly `{{item}}`. For example, with `prompt_template` set to `Review {{item}} for likely regressions.` and `items` set to `["src/a.ts", "src/b.ts"]`, AgentSwarm launches two new subagents with those two concrete prompts. -Use `resume_agent_ids` to continue subagents that already exist from earlier work, such as ones that failed or timed out: map each agent id to the prompt for that resumed subagent (usually `continue` if no extra information is needed). You may combine `resume_agent_ids` with `items` in the same call to resume existing subagents and launch new ones. Do not duplicate resumed work in `items`. +For 2 or more differently-shaped tasks, pass `prompts` instead: each entry is one subagent's full prompt, with no `{{item}}` placeholder. Do not combine `prompts` with `items` or `prompt_template`. -Each of these is enforced — a violation is rejected before any subagent starts: provide at least 2 `items` unless you pass `resume_agent_ids`; whenever `items` are present, `prompt_template` is required and must contain `{{item}}`; and the filled-in prompts must be distinct (two items that expand to the same prompt are rejected). +Use `resume_agent_ids` to continue subagents that already exist from earlier work, such as ones that failed or timed out: map each agent id to the prompt for that resumed subagent (usually `continue` if no extra information is needed). You may combine `resume_agent_ids` with `items` or `prompts` in the same call to resume existing subagents and launch new ones. Do not duplicate resumed work in `items` or `prompts`. -Use enough subagents to keep the work focused and parallel. AgentSwarm supports up to 128 subagents, and launches are queued automatically, so it is safe to split large tasks into many clear, independent items. +Each of these is enforced — a violation is rejected before any subagent starts: provide at least 2 `items` or `prompts` unless you pass `resume_agent_ids`; `prompts` cannot be combined with `items` or `prompt_template`; whenever `items` are present, `prompt_template` is required and must contain `{{item}}`; and the resulting prompts must be distinct (two items that expand to the same prompt, or two identical `prompts` entries, are rejected). -If `AgentSwarm` is called, that call must be the only tool call in the response. +Use enough subagents to keep the work focused and parallel. AgentSwarm supports up to 128 subagents, and launches are queued automatically, so it is safe to split large tasks into many clear, independent items. diff --git a/packages/agent-core-v2/src/features/swarm/tools/agent-swarm/agent-swarm.ts b/packages/agent-core-v2/src/features/swarm/tools/agent-swarm/agent-swarm.ts index 0cc0aca8e1d..0758875dd81 100644 --- a/packages/agent-core-v2/src/features/swarm/tools/agent-swarm/agent-swarm.ts +++ b/packages/agent-core-v2/src/features/swarm/tools/agent-swarm/agent-swarm.ts @@ -36,6 +36,13 @@ export const AgentSwarmToolInputSchema = z .describe( `Values used to fill ${PROMPT_TEMPLATE_PLACEHOLDER}. Each item launches one new subagent.`, ), + prompts: z + .array(z.string().trim().min(1)) + .max(MAX_AGENT_SWARM_SUBAGENTS) + .optional() + .describe( + 'Full prompts for distinct subagents, one subagent per prompt. Use this instead of items and prompt_template. Provide at least 2 prompts unless you pass resume_agent_ids, and every prompt must be distinct.', + ), fork: z .boolean() .optional() diff --git a/packages/agent-core-v2/src/features/swarm/tools/agent-swarm/agentSwarmTool.ts b/packages/agent-core-v2/src/features/swarm/tools/agent-swarm/agentSwarmTool.ts index 997fd27549e..740fb5b6bc9 100644 --- a/packages/agent-core-v2/src/features/swarm/tools/agent-swarm/agentSwarmTool.ts +++ b/packages/agent-core-v2/src/features/swarm/tools/agent-swarm/agentSwarmTool.ts @@ -45,7 +45,7 @@ const AGENT_SWARM_PARAMETERS_NO_MODEL = stripSubagentModelParameter(AGENT_SWARM_ interface AgentSwarmSpawnSpec { readonly kind: 'spawn'; readonly index: number; - readonly item: string; + readonly item?: string; readonly prompt: string; } @@ -109,7 +109,10 @@ export class AgentSwarmTool implements IAgentSwarmTool { } resolveExecution(args: AgentSwarmToolInput): ToolExecution { - const agentCount = (args.items?.length ?? 0) + Object.keys(args.resume_agent_ids ?? {}).length; + const agentCount = + (args.items?.length ?? 0) + + (args.prompts?.length ?? 0) + + Object.keys(args.resume_agent_ids ?? {}).length; return { accesses: ToolAccesses.all(), description: `Launching agent swarm: ${args.description}`, @@ -154,7 +157,7 @@ export class AgentSwarmTool implements IAgentSwarmTool { throw new Error2(ErrorCodes.VALIDATION_FAILED, FORK_WITH_RESUME_UNAVAILABLE); } let plan: SubagentSpawnPlan | undefined; - if ((args.items?.length ?? 0) > 0) { + if ((args.items?.length ?? 0) > 0 || (args.prompts?.length ?? 0) > 0) { if (fork) { const incompatible = forkIncompatibility( { subagent_type: args.subagent_type, model: args.model }, @@ -222,24 +225,34 @@ async function createAgentSwarmSpecs( prompt: prompt.trim(), })); const items = (args.items ?? []).map((item) => item.trim()); + const prompts = (args.prompts ?? []).map((prompt) => prompt.trim()); + const promptTemplate = normalizeOptionalString(args.prompt_template); const itemCount = items.length; + const promptCount = prompts.length; const resumeCount = resumeEntries.length; - const totalCount = resumeCount + itemCount; - if (!hasMinimumAgentSwarmInputs(itemCount, resumeCount)) { + if (promptCount > 0 && (itemCount > 0 || promptTemplate !== undefined)) { + throw new Error2( + ErrorCodes.VALIDATION_FAILED, + 'prompts cannot be combined with items or prompt_template.', + ); + } + const newCount = promptCount + itemCount; + if (!hasMinimumAgentSwarmInputs(newCount, resumeCount)) { throw new Error2( ErrorCodes.VALIDATION_FAILED, - 'AgentSwarm requires at least 2 items unless resume_agent_ids is provided.', + promptCount > 0 + ? 'AgentSwarm requires at least 2 prompts unless resume_agent_ids is provided.' + : 'AgentSwarm requires at least 2 items unless resume_agent_ids is provided.', ); } - if (totalCount > MAX_AGENT_SWARM_SUBAGENTS) { + if (resumeCount + newCount > MAX_AGENT_SWARM_SUBAGENTS) { throw new Error2( ErrorCodes.VALIDATION_FAILED, `AgentSwarm supports at most ${String(MAX_AGENT_SWARM_SUBAGENTS)} subagents.`, - { details: { total: totalCount, max: MAX_AGENT_SWARM_SUBAGENTS } }, + { details: { total: resumeCount + newCount, max: MAX_AGENT_SWARM_SUBAGENTS } }, ); } - const promptTemplate = normalizeOptionalString(args.prompt_template); - if (items.length > 0 && promptTemplate === undefined) { + if (itemCount > 0 && promptTemplate === undefined) { throw new Error2( ErrorCodes.VALIDATION_FAILED, 'prompt_template is required when items are provided.', @@ -264,7 +277,24 @@ async function createAgentSwarmSpecs( prompt: entry.prompt, }); } - if (items.length > 0) { + if (promptCount > 0) { + prompts.forEach((prompt, index) => { + const previousIndex = seenPrompts.get(prompt); + if (previousIndex !== undefined) { + throw new Error2( + ErrorCodes.VALIDATION_FAILED, + 'Duplicate subagent prompts. AgentSwarm requires distinct subagents.', + { details: { previousIndex, index: index + 1 } }, + ); + } + seenPrompts.set(prompt, index + 1); + specs.push({ + kind: 'spawn', + index: specs.length + 1, + prompt, + }); + }); + } else if (items.length > 0) { const itemPromptTemplate = promptTemplate!; items.forEach((item, index) => { const prompt = itemPromptTemplate.split(PROMPT_TEMPLATE_PLACEHOLDER).join(item); diff --git a/packages/agent-core-v2/src/session/agentLifecycle/profile/profiles.ts b/packages/agent-core-v2/src/session/agentLifecycle/profile/profiles.ts index 07d88c58ae3..0a1fad2482e 100644 --- a/packages/agent-core-v2/src/session/agentLifecycle/profile/profiles.ts +++ b/packages/agent-core-v2/src/session/agentLifecycle/profile/profiles.ts @@ -123,7 +123,7 @@ registerAgentProfile({ name: 'explore', description: 'Fast codebase exploration with prompt-enforced read-only behavior.', whenToUse: - 'Fast agent specialized for exploring codebases. Use this when you need to quickly find files by patterns (e.g. "src/**/*.yaml"), search code for keywords (e.g. "database connection"), or answer questions about the codebase (e.g. "how does the auth module work?"). When calling this agent, specify the desired thoroughness level: "quick" for basic searches, "medium" for moderate exploration, or "thorough" for comprehensive analysis across multiple locations and naming conventions. Use this agent for any read-only exploration that will clearly require more than 3 search queries. Prefer launching multiple explore agents concurrently when investigating independent questions.', + 'Fast agent specialized for exploring codebases. Use this when you need to quickly find files by patterns (e.g. "src/**/*.yaml"), search code for keywords (e.g. "database connection"), or answer questions about the codebase (e.g. "how does the auth module work?"). When calling this agent, specify the desired thoroughness level: "quick" for basic searches, "medium" for moderate exploration, or "thorough" for comprehensive analysis across multiple locations and naming conventions. Use this agent for any read-only exploration that will clearly require more than 3 search queries. You must launch multiple explore agents concurrently when investigating independent questions. For a homogeneous batch, use `AgentSwarm`.', tools: EXPLORE_TOOLS, renderSystemPrompt: (context) => renderSystemPromptResult(EXPLORE_ROLE, context, { skillActive: skillActiveFor(EXPLORE_TOOLS) }), diff --git a/packages/agent-core-v2/test/agent/fullCompaction/fullCompaction.test.ts b/packages/agent-core-v2/test/agent/fullCompaction/fullCompaction.test.ts index d2e460a88f3..643e4b03c1e 100644 --- a/packages/agent-core-v2/test/agent/fullCompaction/fullCompaction.test.ts +++ b/packages/agent-core-v2/test/agent/fullCompaction/fullCompaction.test.ts @@ -298,7 +298,7 @@ describe('FullCompaction', () => { properties: expect.objectContaining({ agent_id: 'main', source: 'manual', - tokens_before: 6_092, + tokens_before: 6_262, tokens_after: expect.any(Number), duration_ms: expect.any(Number), compacted_count: 6, @@ -572,7 +572,7 @@ describe('FullCompaction', () => { session_id: 'test-session', cwd: dir, trigger: 'auto', - token_count: 6_092, + token_count: 6_262, }); expect(post).toMatchObject({ hook_event_name: 'PostCompact', @@ -658,7 +658,7 @@ describe('FullCompaction', () => { event: 'compaction_finished', properties: expect.objectContaining({ source: 'manual', - tokens_before: 18_513, + tokens_before: 18_683, retry_count: 1, trace_id: 'trace-compact-1', }), @@ -1125,7 +1125,7 @@ describe('FullCompaction', () => { properties: expect.objectContaining({ agent_id: 'main', source: 'manual', - tokens_before: 18_513, + tokens_before: 18_683, duration_ms: expect.any(Number), round: 1, retry_count: 0, @@ -1350,7 +1350,7 @@ describe('FullCompaction', () => { event: 'compaction_failed', properties: expect.objectContaining({ source: 'manual', - tokens_before: 18_513, + tokens_before: 18_683, duration_ms: expect.any(Number), retry_count: 4, error_type: 'APIConnectionError', @@ -1547,7 +1547,7 @@ describe('FullCompaction', () => { }); it('auto-compacts very large context in one full-history round when the summarizer accepts it', async () => { - const maxContextTokens = 22_000; + const maxContextTokens = 24_000; const ctx = testAgent(); ctx.configure({ provider: CATALOGUED_PROVIDER, @@ -1723,8 +1723,8 @@ describe('FullCompaction', () => { event: 'compaction_finished', properties: expect.objectContaining({ source: 'auto', - tokens_before: 6_099, - tokens_after: 6_083, + tokens_before: 6_269, + tokens_after: 6_253, compacted_count: 7, retry_count: 0, }), diff --git a/packages/agent-core-v2/test/agent/loop/loop.test.ts b/packages/agent-core-v2/test/agent/loop/loop.test.ts index ea054845ff6..5788a2c9ad5 100644 --- a/packages/agent-core-v2/test/agent/loop/loop.test.ts +++ b/packages/agent-core-v2/test/agent/loop/loop.test.ts @@ -187,8 +187,8 @@ describe('Agent loop', () => { [emit] turn.step.started { "time": "