summaryrefslogtreecommitdiffhomepage
path: root/packages/api/src
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-02 15:54:39 +0900
committerAdam Malczewski <[email protected]>2026-06-02 15:54:39 +0900
commit3ff2db698c2633023934d8477a9e995f78fa011e (patch)
tree1797ca96cb8fbc8df23a2667037e3a6ddeec2f35 /packages/api/src
parentc0c08720cceb75b5e635e71190ae1f956f535133 (diff)
downloaddispatch-3ff2db698c2633023934d8477a9e995f78fa011e.tar.gz
dispatch-3ff2db698c2633023934d8477a9e995f78fa011e.zip
fix(perm): decouple perm_user_agent from perm_summon for spawning user agents
Granting only the user-agent (top-level) permission without the subagent-summon permission left the agent unable to summon user agents: the whole summon tool was gated behind perm_summon, so perm_user_agent alone produced no summon tool. Register summon when EITHER perm_summon OR perm_user_agent is granted. createSummonTool now takes an independent subagentEnabled flag (mirrors perm_summon) alongside userAgentEnabled (mirrors perm_user_agent): - subagent-only -> ordinary subagents, no top_level - user-agent-only -> spawns ONLY top-level user agents (top_level forced, background/top_level params dropped, user-agent catalog only) - both -> unchanged full behavior retrieve stays bundled with perm_summon (user agents are fire-and-forget). Adds core summon tests (user-agent-only mode + legacy-default regression) and an agent-manager summon/user_agent permission-split suite.
Diffstat (limited to 'packages/api/src')
-rw-r--r--packages/api/src/agent-manager.ts36
1 files changed, 24 insertions, 12 deletions
diff --git a/packages/api/src/agent-manager.ts b/packages/api/src/agent-manager.ts
index 85dd160..9499ce5 100644
--- a/packages/api/src/agent-manager.ts
+++ b/packages/api/src/agent-manager.ts
@@ -575,7 +575,13 @@ export class AgentManager {
});
}
toolEntries.push({ name: "todo", tool: createTaskListTool(tabAgent.taskList) });
- if (permSummon) {
+ // The `summon` tool is registered when EITHER the subagent
+ // permission (`perm_summon`) OR the user-agent permission
+ // (`perm_user_agent`) is granted — the two are independent.
+ // `perm_summon` enables ordinary subagent spawning; granting
+ // only `perm_user_agent` exposes summon in user-agent-only mode
+ // (spawns top-level user agents exclusively).
+ if (permSummon || permUserAgent) {
// Capture parent's allowed tool names for child permission enforcement
const parentAllowedTools = new Set(toolEntries.map((e) => e.name));
const allAgentDefs = loadAgents(workingDirectory);
@@ -609,19 +615,25 @@ export class AgentManager {
availableUserAgents,
agentDirPaths,
permUserAgent,
+ permSummon,
),
});
- toolEntries.push({
- name: "retrieve",
- tool: createRetrieveTool({
- getResult: (id) =>
- tabAgent.shellStore.has(id)
- ? tabAgent.shellStore.getResult(id)
- : tabAgent.transcriptStore.has(id)
- ? tabAgent.transcriptStore.getResult(id)
- : this.getChildResult(id),
- }),
- });
+ // `retrieve` collects subagent results. User agents are
+ // fire-and-forget, so it is bundled with the subagent
+ // permission only — a user-agent-only grant doesn't get it.
+ if (permSummon) {
+ toolEntries.push({
+ name: "retrieve",
+ tool: createRetrieveTool({
+ getResult: (id) =>
+ tabAgent.shellStore.has(id)
+ ? tabAgent.shellStore.getResult(id)
+ : tabAgent.transcriptStore.has(id)
+ ? tabAgent.transcriptStore.getResult(id)
+ : this.getChildResult(id),
+ }),
+ });
+ }
}
if (permSendToTab || permReadTab) {
const tabCommAllowed = new Set<string>();