pi-subagents 0.57.0 → 0.59.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/docs/agents.md +18 -9
  3. package/docs/configuration.md +4 -4
  4. package/docs/extension-api.md +40 -1
  5. package/docs/models.md +27 -4
  6. package/docs/observability.md +1 -1
  7. package/docs/tool-reference.md +92 -6
  8. package/docs/workflows.md +115 -0
  9. package/package.json +3 -1
  10. package/prompts/review-loop.md +2 -2
  11. package/skills/council-mode/SKILL.md +1 -1
  12. package/skills/pi-subagents/SKILL.md +3 -1
  13. package/skills/pi-subagents/references/execution-controls.md +6 -2
  14. package/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
  15. package/skills/pi-subagents/references/prompting-and-roles.md +26 -5
  16. package/src/agents/agent-management.ts +28 -17
  17. package/src/agents/agent-serializer.ts +9 -2
  18. package/src/agents/agents.ts +103 -27
  19. package/src/agents/runtime-agent-events.ts +70 -0
  20. package/src/agents/runtime-agent-registry.ts +19 -18
  21. package/src/api/agents.ts +10 -5
  22. package/src/api/background-work.ts +5 -1
  23. package/src/api/delegation.ts +0 -7
  24. package/src/api/preflight.ts +32 -10
  25. package/src/extension/fanout-child.ts +5 -3
  26. package/src/extension/index.ts +66 -27
  27. package/src/extension/public-execution.ts +15 -2
  28. package/src/extension/rpc.ts +35 -14
  29. package/src/extension/schemas.ts +37 -12
  30. package/src/extension/tool-description.ts +25 -6
  31. package/src/integrations/herdr-status.ts +5 -0
  32. package/src/intercom/result-intercom.ts +2 -0
  33. package/src/profiles/profiles.ts +5 -6
  34. package/src/runs/background/active-async-capacity.ts +2 -2
  35. package/src/runs/background/async-execution.ts +78 -46
  36. package/src/runs/background/async-job-tracker.ts +14 -12
  37. package/src/runs/background/async-resume.ts +29 -27
  38. package/src/runs/background/async-status-snapshot.ts +23 -261
  39. package/src/runs/background/async-status.ts +68 -7
  40. package/src/runs/background/chain-append.ts +8 -3
  41. package/src/runs/background/chain-root-attachment.ts +60 -8
  42. package/src/runs/background/fleet-view.ts +21 -11
  43. package/src/runs/background/notify.ts +184 -9
  44. package/src/runs/background/result-delivery-ownership.ts +45 -0
  45. package/src/runs/background/result-files.ts +2 -1
  46. package/src/runs/background/result-watcher.ts +29 -10
  47. package/src/runs/background/resume-guidance.ts +1 -1
  48. package/src/runs/background/retained-children.ts +1 -1
  49. package/src/runs/background/run-status.ts +18 -8
  50. package/src/runs/background/scheduled-runs.ts +86 -7
  51. package/src/runs/background/stale-run-reconciler.ts +10 -4
  52. package/src/runs/background/steering.ts +4 -14
  53. package/src/runs/background/subagent-runner.ts +389 -350
  54. package/src/runs/background/subagent-wait.ts +58 -10
  55. package/src/runs/background/terminal-run-index.ts +1 -1
  56. package/src/runs/background/wait-completions.ts +22 -1
  57. package/src/runs/background/wait-config.ts +23 -9
  58. package/src/runs/background/wait-tool.ts +9 -2
  59. package/src/runs/foreground/async-steering-action.ts +2 -2
  60. package/src/runs/foreground/execution.ts +134 -121
  61. package/src/runs/foreground/foreground-control.ts +3 -0
  62. package/src/runs/foreground/foreground-history.ts +1 -0
  63. package/src/runs/foreground/subagent-executor.ts +527 -257
  64. package/src/runs/foreground/workflow-detach-reconcile.ts +130 -120
  65. package/src/runs/shared/abort-recovery.ts +119 -0
  66. package/src/runs/shared/async-status-projection.ts +463 -0
  67. package/src/runs/shared/child-identity.ts +19 -4
  68. package/src/runs/shared/child-launch-plan.ts +151 -0
  69. package/src/runs/shared/completion-evidence.ts +89 -0
  70. package/src/runs/shared/completion-guard.ts +5 -4
  71. package/src/runs/shared/dynamic-fanout.ts +3 -3
  72. package/src/runs/shared/fast-mode-extension.ts +5 -5
  73. package/src/runs/shared/host-step-status.ts +230 -0
  74. package/src/runs/shared/lane-metadata.ts +105 -0
  75. package/src/runs/shared/launch-cwd.ts +16 -0
  76. package/src/runs/shared/long-running-guard.ts +2 -1
  77. package/src/runs/shared/mcp-config-sources.ts +422 -0
  78. package/src/runs/shared/mcp-direct-tool-allowlist.ts +221 -158
  79. package/src/runs/shared/mcp-direct-tool-grant.ts +197 -0
  80. package/src/runs/shared/model-exclusions.ts +12 -0
  81. package/src/runs/shared/model-fallback.ts +25 -6
  82. package/src/runs/shared/nested-events.ts +6 -2
  83. package/src/runs/shared/nested-render.ts +7 -3
  84. package/src/runs/shared/parallel-handoff.ts +419 -7
  85. package/src/runs/shared/parallel-utils.ts +15 -1
  86. package/src/runs/shared/pi-args.ts +81 -8
  87. package/src/runs/shared/single-output.ts +44 -4
  88. package/src/runs/shared/subagent-prompt-runtime.ts +98 -13
  89. package/src/runs/shared/worktree-cleanup-plan.ts +847 -0
  90. package/src/runs/shared/worktree.ts +18 -0
  91. package/src/shared/child-session-name.ts +46 -0
  92. package/src/shared/extension-context.ts +24 -0
  93. package/src/shared/formatters.ts +11 -2
  94. package/src/shared/launch-contract.ts +5 -1
  95. package/src/shared/settings.ts +9 -103
  96. package/src/shared/types.ts +240 -30
  97. package/src/shared/utils.ts +41 -84
  98. package/src/slash/delegation-adapters.ts +1 -8
  99. package/src/slash/delegation-request.ts +0 -4
  100. package/src/slash/slash-bridge.ts +1 -2
  101. package/src/slash/slash-commands.ts +369 -90
  102. package/src/slash/slash-live-state.ts +22 -11
  103. package/src/slash/subagents-admin.ts +3 -0
  104. package/src/tui/fleet-status.ts +125 -74
  105. package/src/tui/fleet.ts +11 -5
  106. package/src/tui/render.ts +330 -33
  107. package/src/watchdog/turn-delta.ts +1 -1
  108. package/src/workflows/chat-progress.ts +6 -3
  109. package/src/workflows/host-command.ts +230 -0
  110. package/src/workflows/scripted-workflow.ts +496 -35
  111. package/src/workflows/workflow-child-summary.ts +9 -5
  112. package/src/workflows/workflow-preflight.ts +270 -0
  113. package/src/workflows/workflow-receipt.ts +79 -5
  114. package/src/workflows/workflow-settlement.ts +246 -0
  115. package/src/runs/shared/turn-budget.ts +0 -98
@@ -1,10 +1,9 @@
1
1
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
- import type { AcceptanceInput, AcceptanceRole, AgentRunnerConfig, OutputMode, ToolBudgetConfig, TurnBudgetConfig } from "../shared/types.ts";
2
+ import type { AcceptanceInput, AcceptanceRole, AgentRunnerConfig, OutputMode, ToolBudgetConfig } from "../shared/types.ts";
3
3
  import { CODE_OWNED_EXTERNAL_CLI_ADAPTER_LABEL, isCodeOwnedExternalCliAdapterId, parseExternalCliCapabilityNarrowing, validateCodeOwnedProfileRunner } from "../runs/shared/external-cli-contract.ts";
4
4
  import { validateAcceptanceInput } from "../runs/shared/acceptance.ts";
5
5
  import { validatePermissionRules, type PermissionRules } from "../runs/shared/permissions.ts";
6
6
  import { validateToolBudgetConfig } from "../runs/shared/tool-budget.ts";
7
- import { resolveTurnBudgetConfig } from "../runs/shared/turn-budget.ts";
8
7
  import { BUILTIN_AGENT_NAMES } from "./builtin-names.ts";
9
8
  import type { AgentConfig, AgentDefaultContext, AgentDiscoveryDiagnostic } from "./agents.ts";
10
9
 
@@ -21,18 +20,19 @@ export interface RuntimeAgentDefinition {
21
20
  systemPrompt: string;
22
21
  aliases?: readonly string[];
23
22
  tools?: readonly string[];
23
+ allowNestedSubagents?: boolean;
24
24
  mcpDirectTools?: readonly string[];
25
25
  model?: string;
26
26
  fallbackModels?: readonly string[];
27
27
  thinking?: string | false;
28
28
  systemPromptMode?: "append" | "replace";
29
29
  inheritProjectContext?: boolean;
30
+ inheritGlobalContext?: boolean;
30
31
  inheritSkills?: boolean;
31
32
  defaultContext?: AgentDefaultContext;
32
33
  defaultAsync?: boolean;
33
34
  defaultTimeoutMs?: number;
34
35
  defaultToolTimeoutMs?: number;
35
- defaultTurnBudget?: TurnBudgetConfig;
36
36
  defaultAcceptance?: AcceptanceInput;
37
37
  acceptanceRole?: AcceptanceRole;
38
38
  runner?: AgentRunnerConfig;
@@ -40,6 +40,7 @@ export interface RuntimeAgentDefinition {
40
40
  skillPath?: readonly string[];
41
41
  extensions?: readonly string[];
42
42
  subagentOnlyExtensions?: readonly string[];
43
+ mutationTools?: readonly string[];
43
44
  output?: string;
44
45
  outputMode?: OutputMode;
45
46
  defaultReads?: readonly string[];
@@ -133,8 +134,8 @@ function validateStringList(value: unknown, field: string): string[] | undefined
133
134
 
134
135
  function validatePositiveInteger(value: unknown, field: string): number | undefined {
135
136
  if (value === undefined) return undefined;
136
- if (!Number.isInteger(value) || (value as number) <= 0) throw new Error(`${field} must be a positive integer when provided.`);
137
- return value as number;
137
+ if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) throw new Error(`${field} must be a positive integer when provided.`);
138
+ return value;
138
139
  }
139
140
 
140
141
  function validateBoolean(value: unknown, field: string): boolean | undefined {
@@ -181,12 +182,6 @@ function validateRunner(value: unknown): AgentRunnerConfig | undefined {
181
182
  return { type: "external-cli", ...(isCodeOwnedExternalCliAdapterId(runner.adapter) ? { adapter: runner.adapter } : {}), command: runner.command.trim(), ...(args?.length ? { args } : {}), ...(runner.promptDelivery ? { promptDelivery: "stdin" as const } : {}), ...(capabilities ? { capabilities } : {}) };
182
183
  }
183
184
 
184
- function validateTurnBudget(value: unknown): TurnBudgetConfig | undefined {
185
- const result = resolveTurnBudgetConfig(value, "Runtime agent definition defaultTurnBudget");
186
- if (result.error) throw new Error(result.error);
187
- return result.turnBudget;
188
- }
189
-
190
185
  function validateAcceptance(value: unknown): AcceptanceInput | undefined {
191
186
  const errors = validateAcceptanceInput(value, "Runtime agent definition defaultAcceptance");
192
187
  if (errors.length > 0) throw new Error(errors.join(" "));
@@ -203,10 +198,10 @@ function validateDefinition(value: unknown): RuntimeAgentDefinition {
203
198
  if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Runtime agent definition must be an object.");
204
199
  const definition = value as Record<string, unknown>;
205
200
  const supported = new Set([
206
- "description", "systemPrompt", "aliases", "tools", "mcpDirectTools", "model", "fallbackModels", "thinking",
207
- "systemPromptMode", "inheritProjectContext", "inheritSkills", "defaultContext", "defaultAsync", "defaultTimeoutMs",
208
- "defaultToolTimeoutMs", "defaultTurnBudget", "defaultAcceptance", "acceptanceRole", "runner", "skills", "skillPath",
209
- "extensions", "subagentOnlyExtensions", "output", "outputMode", "defaultReads", "defaultProgress", "interactive",
201
+ "description", "systemPrompt", "aliases", "tools", "allowNestedSubagents", "mcpDirectTools", "model", "fallbackModels", "thinking",
202
+ "systemPromptMode", "inheritProjectContext", "inheritGlobalContext", "inheritSkills", "defaultContext", "defaultAsync", "defaultTimeoutMs",
203
+ "defaultToolTimeoutMs", "defaultAcceptance", "acceptanceRole", "runner", "skills", "skillPath",
204
+ "extensions", "subagentOnlyExtensions", "mutationTools", "output", "outputMode", "defaultReads", "defaultProgress", "interactive",
210
205
  "maxSubagentDepth", "completionGuard", "toolBudget", "permissions",
211
206
  ]);
212
207
  const unknown = Object.keys(definition).filter((key) => !supported.has(key));
@@ -223,21 +218,23 @@ function validateDefinition(value: unknown): RuntimeAgentDefinition {
223
218
  if (outputMode !== undefined && outputMode !== "inline" && outputMode !== "file-only") throw new Error("Runtime agent definition outputMode must be 'inline' or 'file-only'.");
224
219
  const aliases = validateStringList(definition.aliases, "Runtime agent definition aliases");
225
220
  const tools = validateStringList(definition.tools, "Runtime agent definition tools");
221
+ const allowNestedSubagents = validateBoolean(definition.allowNestedSubagents, "Runtime agent definition allowNestedSubagents");
226
222
  const mcpDirectTools = validateStringList(definition.mcpDirectTools, "Runtime agent definition mcpDirectTools");
227
223
  const model = validateOptionalString(definition.model, "Runtime agent definition model");
228
224
  const fallbackModels = validateStringList(definition.fallbackModels, "Runtime agent definition fallbackModels");
229
225
  const inheritProjectContext = validateBoolean(definition.inheritProjectContext, "Runtime agent definition inheritProjectContext");
226
+ const inheritGlobalContext = validateBoolean(definition.inheritGlobalContext, "Runtime agent definition inheritGlobalContext");
230
227
  const inheritSkills = validateBoolean(definition.inheritSkills, "Runtime agent definition inheritSkills");
231
228
  const defaultAsync = validateBoolean(definition.defaultAsync, "Runtime agent definition defaultAsync");
232
229
  const defaultTimeoutMs = validatePositiveInteger(definition.defaultTimeoutMs, "Runtime agent definition defaultTimeoutMs");
233
230
  const defaultToolTimeoutMs = validatePositiveInteger(definition.defaultToolTimeoutMs, "Runtime agent definition defaultToolTimeoutMs");
234
- const defaultTurnBudget = validateTurnBudget(definition.defaultTurnBudget);
235
231
  const defaultAcceptance = validateAcceptance(definition.defaultAcceptance);
236
232
  const runner = validateRunner(definition.runner);
237
233
  const skills = validateStringList(definition.skills, "Runtime agent definition skills");
238
234
  const skillPath = validateStringList(definition.skillPath, "Runtime agent definition skillPath");
239
235
  const extensions = validateStringList(definition.extensions, "Runtime agent definition extensions");
240
236
  const subagentOnlyExtensions = validateStringList(definition.subagentOnlyExtensions, "Runtime agent definition subagentOnlyExtensions");
237
+ const mutationTools = validateStringList(definition.mutationTools, "Runtime agent definition mutationTools");
241
238
  const output = validateOptionalString(definition.output, "Runtime agent definition output");
242
239
  const defaultReads = validateStringList(definition.defaultReads, "Runtime agent definition defaultReads");
243
240
  const defaultProgress = validateBoolean(definition.defaultProgress, "Runtime agent definition defaultProgress");
@@ -251,18 +248,19 @@ function validateDefinition(value: unknown): RuntimeAgentDefinition {
251
248
  systemPrompt: validateString(definition.systemPrompt, "Runtime agent definition systemPrompt", MAX_SYSTEM_PROMPT_LENGTH),
252
249
  ...(aliases ? { aliases } : {}),
253
250
  ...(tools ? { tools } : {}),
251
+ ...(allowNestedSubagents !== undefined ? { allowNestedSubagents } : {}),
254
252
  ...(mcpDirectTools ? { mcpDirectTools } : {}),
255
253
  ...(model ? { model } : {}),
256
254
  ...(fallbackModels ? { fallbackModels } : {}),
257
255
  ...(thinking !== undefined ? { thinking: thinking as string | false } : {}),
258
256
  ...(systemPromptMode !== undefined ? { systemPromptMode: systemPromptMode as "append" | "replace" } : {}),
259
257
  ...(inheritProjectContext !== undefined ? { inheritProjectContext } : {}),
258
+ ...(inheritGlobalContext !== undefined ? { inheritGlobalContext } : {}),
260
259
  ...(inheritSkills !== undefined ? { inheritSkills } : {}),
261
260
  ...(defaultContext !== undefined ? { defaultContext: defaultContext as AgentDefaultContext } : {}),
262
261
  ...(defaultAsync !== undefined ? { defaultAsync } : {}),
263
262
  ...(defaultTimeoutMs !== undefined ? { defaultTimeoutMs } : {}),
264
263
  ...(defaultToolTimeoutMs !== undefined ? { defaultToolTimeoutMs } : {}),
265
- ...(defaultTurnBudget !== undefined ? { defaultTurnBudget } : {}),
266
264
  ...(defaultAcceptance !== undefined ? { defaultAcceptance } : {}),
267
265
  ...(acceptanceRole !== undefined ? { acceptanceRole: acceptanceRole as AcceptanceRole } : {}),
268
266
  ...(runner !== undefined ? { runner } : {}),
@@ -270,6 +268,7 @@ function validateDefinition(value: unknown): RuntimeAgentDefinition {
270
268
  ...(skillPath ? { skillPath } : {}),
271
269
  ...(extensions ? { extensions } : {}),
272
270
  ...(subagentOnlyExtensions ? { subagentOnlyExtensions } : {}),
271
+ ...(mutationTools ? { mutationTools } : {}),
273
272
  ...(output ? { output } : {}),
274
273
  ...(outputMode !== undefined ? { outputMode: outputMode as OutputMode } : {}),
275
274
  ...(defaultReads ? { defaultReads } : {}),
@@ -328,18 +327,19 @@ function toAgentConfig(name: string, definition: RuntimeAgentDefinition): AgentC
328
327
  ...(aliases ? { aliases } : {}),
329
328
  ...(definition.runner !== undefined ? { runner: definition.runner } : {}),
330
329
  ...(definition.tools !== undefined ? { tools: [...definition.tools] } : {}),
330
+ ...(definition.allowNestedSubagents !== undefined ? { allowNestedSubagents: definition.allowNestedSubagents } : {}),
331
331
  ...(definition.mcpDirectTools !== undefined ? { mcpDirectTools: [...definition.mcpDirectTools] } : {}),
332
332
  ...(definition.model !== undefined ? { model: definition.model } : {}),
333
333
  ...(definition.fallbackModels !== undefined ? { fallbackModels: [...definition.fallbackModels] } : {}),
334
334
  ...(definition.thinking !== undefined ? { thinking: definition.thinking } : {}),
335
335
  systemPromptMode: definition.systemPromptMode ?? defaultSystemPromptMode(name),
336
336
  inheritProjectContext: definition.inheritProjectContext ?? defaultInheritProjectContext(name),
337
+ inheritGlobalContext: definition.inheritGlobalContext ?? false,
337
338
  inheritSkills: definition.inheritSkills ?? defaultInheritSkills(),
338
339
  ...(definition.defaultContext !== undefined ? { defaultContext: definition.defaultContext } : {}),
339
340
  ...(definition.defaultAsync !== undefined ? { defaultAsync: definition.defaultAsync } : {}),
340
341
  ...(definition.defaultTimeoutMs !== undefined ? { defaultTimeoutMs: definition.defaultTimeoutMs } : {}),
341
342
  ...(definition.defaultToolTimeoutMs !== undefined ? { defaultToolTimeoutMs: definition.defaultToolTimeoutMs } : {}),
342
- ...(definition.defaultTurnBudget !== undefined ? { defaultTurnBudget: definition.defaultTurnBudget } : {}),
343
343
  ...(definition.defaultAcceptance !== undefined ? { defaultAcceptance: definition.defaultAcceptance } : {}),
344
344
  ...(definition.acceptanceRole !== undefined ? { acceptanceRole: definition.acceptanceRole } : {}),
345
345
  systemPrompt: definition.systemPrompt,
@@ -349,6 +349,7 @@ function toAgentConfig(name: string, definition: RuntimeAgentDefinition): AgentC
349
349
  ...(definition.skillPath !== undefined ? { skillPath: [...definition.skillPath] } : {}),
350
350
  ...(definition.extensions !== undefined ? { extensions: [...definition.extensions] } : {}),
351
351
  ...(definition.subagentOnlyExtensions !== undefined ? { subagentOnlyExtensions: [...definition.subagentOnlyExtensions] } : {}),
352
+ ...(definition.mutationTools !== undefined ? { mutationTools: [...definition.mutationTools] } : {}),
352
353
  ...(definition.output !== undefined ? { output: definition.output } : {}),
353
354
  ...(definition.outputMode !== undefined ? { outputMode: definition.outputMode } : {}),
354
355
  ...(definition.defaultReads !== undefined ? { defaultReads: [...definition.defaultReads] } : {}),
package/src/api/agents.ts CHANGED
@@ -1,7 +1,12 @@
1
- import { registerRuntimeAgent, type RegisterRuntimeAgentInput, type RuntimeAgentDefinition, type RuntimeAgentRegistration } from "../agents/runtime-agent-registry.ts";
1
+ import type { RegisterRuntimeAgentInput, RuntimeAgentDefinition, RuntimeAgentRegistration } from "../agents/runtime-agent-registry.ts";
2
+ export { registerRuntimeAgent as registerAgent } from "../agents/runtime-agent-registry.ts";
3
+ export {
4
+ RUNTIME_AGENT_REGISTER_EVENT,
5
+ RUNTIME_AGENT_REGISTER_VERSION,
6
+ registerAgentViaEvents,
7
+ type RegisterRuntimeAgentViaEventsInput,
8
+ type RuntimeAgentRegistrationRequest,
9
+ type RuntimeAgentRegistrationResult,
10
+ } from "../agents/runtime-agent-events.ts";
2
11
 
3
12
  export type { RegisterRuntimeAgentInput, RuntimeAgentDefinition, RuntimeAgentRegistration };
4
-
5
- export function registerAgent(input: RegisterRuntimeAgentInput): RuntimeAgentRegistration {
6
- return registerRuntimeAgent(input);
7
- }
@@ -4,7 +4,11 @@ export const BACKGROUND_WORK_REGISTRY_KEY = "pi-subagents.background-work.v1";
4
4
  const MAX_PROVIDER_NAME_LENGTH = 128;
5
5
  const MAX_PROVIDERS = 100;
6
6
  const MAX_ITEM_ID_LENGTH = 256;
7
- const MAX_SESSION_ID_LENGTH = 256;
7
+ /**
8
+ * A Pi session id is the session file path, which routinely exceeds a short
9
+ * identity budget in nested worktrees, so it is bounded like the other paths.
10
+ */
11
+ const MAX_SESSION_ID_LENGTH = 4_096;
8
12
  const MAX_WAKE_CHANNEL_LENGTH = 256;
9
13
  const MAX_ITEMS_PER_PROVIDER = 10_000;
10
14
 
@@ -7,11 +7,6 @@ export const SUBAGENT_DELEGATION_UPDATE_EVENT = "prompt-template:subagent:update
7
7
  export const SUBAGENT_DELEGATION_RESPONSE_EVENT = "prompt-template:subagent:response";
8
8
  export const SUBAGENT_DELEGATION_CANCEL_EVENT = "prompt-template:subagent:cancel";
9
9
 
10
- export interface SubagentDelegationTurnBudget {
11
- maxTurns: number;
12
- graceTurns?: number;
13
- }
14
-
15
10
  export interface SubagentDelegationToolBudget {
16
11
  soft?: number;
17
12
  hard: number;
@@ -37,7 +32,6 @@ export interface SubagentDelegationRequest {
37
32
  model?: string;
38
33
  thinking?: SubagentDelegationThinking;
39
34
  timeoutMs?: number;
40
- turnBudget?: SubagentDelegationTurnBudget;
41
35
  toolBudget?: SubagentDelegationToolBudget;
42
36
  skill?: string | string[] | boolean;
43
37
  artifacts?: boolean;
@@ -69,7 +63,6 @@ export type SubagentDelegationStatus =
69
63
  | "timed_out"
70
64
  | "cancelled"
71
65
  | "interrupted"
72
- | "turn_budget_exhausted"
73
66
  | "tool_budget_exhausted"
74
67
  | "structured_output_failed"
75
68
  | "acceptance_failed"
@@ -14,9 +14,7 @@ import { resolveEffectiveThinking } from "../shared/model-info.ts";
14
14
  import { assertThinkingWithinCeiling, decodeThinkingCeiling, intersectThinkingCeilings, SUBAGENT_THINKING_CEILING_ENV, type ThinkingLevel } from "../shared/thinking-ceiling.ts";
15
15
  import { SUBAGENT_LIFECYCLE_ARTIFACT_VERSION, type ArtifactDirPreference, type ArtifactPaths, type JsonSchemaObject, type OutputMode } from "../shared/types.ts";
16
16
  import { capabilityCeilingAgentRestrictionMessage, intersectSubagentCapabilityCeilings, type ResolvedSubagentCapabilityCeiling, type SubagentCapabilityAudit } from "../runs/shared/capability-ceiling.ts";
17
- import { appendTurnBudgetSystemPrompt } from "../runs/shared/turn-budget.ts";
18
17
  import { resolvePermissionRules } from "../runs/shared/permissions.ts";
19
- import type { ResolvedTurnBudget } from "../shared/types.ts";
20
18
  import type { ResolvedMcpDirectToolSelection } from "../runs/shared/mcp-direct-tool-allowlist.ts";
21
19
  import { resolveStepBehavior } from "../shared/settings.ts";
22
20
  import { canPreferForkFromSnapshot, resolveSubagentLaunchContext } from "../shared/fork-context.ts";
@@ -42,8 +40,10 @@ export type SubagentLaunchContractReasonCode =
42
40
  | "thinking_ceiling"
43
41
  | "invalid_extension_bindings";
44
42
 
43
+ export type SubagentLaunchContractDiagnosticCode = SubagentLaunchContractReasonCode | "host_required" | "snapshot_warning" | "workspace_scope_authority";
44
+
45
45
  export interface SubagentLaunchContractDiagnostic {
46
- code: SubagentLaunchContractReasonCode | "host_required" | "snapshot_warning";
46
+ code: SubagentLaunchContractDiagnosticCode;
47
47
  severity: "error" | "warning" | "host-required";
48
48
  message: string;
49
49
  }
@@ -67,7 +67,6 @@ export interface SubagentLaunchContractInput {
67
67
  outputMode?: OutputMode;
68
68
  outputSchema?: JsonSchemaObject;
69
69
  extensionBindings?: ExtensionBindings;
70
- turnBudget?: ResolvedTurnBudget;
71
70
  artifacts?: boolean;
72
71
  artifactDir?: ArtifactDirPreference;
73
72
  parentSessionFile?: string | null;
@@ -157,6 +156,7 @@ export interface SubagentLaunchContract {
157
156
  thinkingCeiling?: ThinkingLevel;
158
157
  systemPromptMode: AgentConfig["systemPromptMode"];
159
158
  inheritProjectContext: boolean;
159
+ inheritGlobalContext: boolean;
160
160
  inheritSkills: boolean;
161
161
  skills: SubagentLaunchContractSkills;
162
162
  tools: SubagentLaunchContractTools;
@@ -204,8 +204,26 @@ function resolveLaunchContractContext(input: SubagentLaunchContractInput, agent:
204
204
  });
205
205
  }
206
206
 
207
- function candidateList(inputAgent: string, selected: AgentConfig | undefined, cwd: string): SubagentLaunchContractAgentCandidate[] {
208
- const all = discoverAgentsAll(cwd);
207
+ function taskWorkspaceScopeAuthorityDiagnostic(task: string | undefined): SubagentLaunchContractDiagnostic | undefined {
208
+ if (!task) return undefined;
209
+ const text = task.replace(/\s+/g, " ").trim();
210
+ if (!text) return undefined;
211
+ const createsWorkspacePackage = /\b(?:add|create|introduce|make|set up)\b.{0,80}\b(?:new\s+)?(?:workspace\s+)?package\b/i.test(text)
212
+ || /\b(?:new\s+)?package\b.{0,80}\b(?:workspace|monorepo)\b/i.test(text);
213
+ if (!createsWorkspacePackage) return undefined;
214
+ const packageOnlyAuthority = /\b(?:only|solely)\b.{0,40}\b(?:edit|change|modify|touch|write(?:\s+to)?)\b.{0,80}\b(?:package(?:\s+directory)?|packages\/[\w.-]+)\b/i.test(text)
215
+ || /\b(?:do not|don't|must not)\b.{0,40}\b(?:edit|change|modify|touch|write(?:\s+to)?)\b.{0,80}\b(?:root|workspace|lockfile|metadata)\b/i.test(text)
216
+ || /\bwithout\b.{0,40}\b(?:root|workspace|lockfile|metadata)\b.{0,40}\b(?:edit|change|modification|write)s?\b/i.test(text);
217
+ if (!packageOnlyAuthority) return undefined;
218
+ return {
219
+ code: "workspace_scope_authority",
220
+ severity: "warning",
221
+ message: "Task asks for a workspace package change while limiting authority to package-scope edits. New workspace packages often need root workspace metadata or lockfile changes, so confirm that authority before launch.",
222
+ };
223
+ }
224
+
225
+ function candidateList(inputAgent: string, selected: AgentConfig | undefined, cwd: string, provider?: string): SubagentLaunchContractAgentCandidate[] {
226
+ const all = discoverAgentsAll(cwd, provider);
209
227
  return [...all.builtin, ...all.package, ...all.user, ...all.project]
210
228
  .filter((agent) => Boolean(resolveAgentName(inputAgent, [agent]).agent))
211
229
  .map((agent) => ({
@@ -221,6 +239,8 @@ function candidateList(inputAgent: string, selected: AgentConfig | undefined, cw
221
239
 
222
240
  export async function resolveSubagentLaunchContract(input: SubagentLaunchContractInput): Promise<SubagentLaunchContractResult> {
223
241
  const diagnostics: SubagentLaunchContractDiagnostic[] = [];
242
+ const authorityDiagnostic = taskWorkspaceScopeAuthorityDiagnostic(input.task);
243
+ if (authorityDiagnostic) diagnostics.push(authorityDiagnostic);
224
244
  const effectiveCwd = path.resolve(input.cwd);
225
245
  try {
226
246
  if (!fs.statSync(effectiveCwd).isDirectory()) {
@@ -237,7 +257,8 @@ export async function resolveSubagentLaunchContract(input: SubagentLaunchContrac
237
257
  return { ok: false, code: "invalid_artifact_dir", message: `Unsupported artifactDir '${String(input.artifactDir)}'; expected 'project', 'session', or 'temp'.`, diagnostics };
238
258
  }
239
259
  const scope = resolveExecutionAgentScope(input.agentScope);
240
- const discovered = discoverAgents(effectiveCwd, scope);
260
+ const parentProvider = input.preferredProvider ?? input.parentModel?.provider;
261
+ const discovered = discoverAgents(effectiveCwd, scope, parentProvider);
241
262
  const resolvedAgent = resolveAgentName(input.agent, discovered.agents);
242
263
  const ambiguousCandidates = resolvedAgent.error
243
264
  ? discovered.agents.filter((agent) => resolveAgentName(input.agent, [agent]).agent)
@@ -330,6 +351,7 @@ export async function resolveSubagentLaunchContract(input: SubagentLaunchContrac
330
351
  try {
331
352
  toolPlan = resolvePiLaunchToolPlan({
332
353
  tools: agent.tools,
354
+ allowNestedSubagents: agent.allowNestedSubagents,
333
355
  extensions: agent.extensions,
334
356
  subagentOnlyExtensions: agent.subagentOnlyExtensions,
335
357
  mcpDirectTools: agent.mcpDirectTools,
@@ -375,9 +397,7 @@ export async function resolveSubagentLaunchContract(input: SubagentLaunchContrac
375
397
  const memoryInjection = buildAgentMemoryInjection(agent, effectiveCwd);
376
398
  if (memoryInjection) effectiveSystemPrompt = effectiveSystemPrompt ? `${effectiveSystemPrompt}\n\n${memoryInjection}` : memoryInjection;
377
399
  effectiveSystemPrompt = injectOutputPathSystemPrompt(effectiveSystemPrompt, outputPath, agent);
378
- const turnBudget = input.turnBudget ?? agent.defaultTurnBudget;
379
- effectiveSystemPrompt = appendTurnBudgetSystemPrompt(effectiveSystemPrompt, turnBudget);
380
- const candidates = candidateList(input.agent, agent, effectiveCwd);
400
+ const candidates = candidateList(input.agent, agent, effectiveCwd, parentProvider);
381
401
  const shadowedCandidates = candidates.filter((candidate) => !candidate.selected);
382
402
  const definitionDigest = agentDefinitionDigest(agent);
383
403
  const contractBase: Omit<SubagentLaunchContract, "digest"> = {
@@ -400,6 +420,7 @@ export async function resolveSubagentLaunchContract(input: SubagentLaunchContrac
400
420
  ...(thinkingCeiling ? { thinkingCeiling } : {}),
401
421
  systemPromptMode: agent.systemPromptMode,
402
422
  inheritProjectContext: agent.inheritProjectContext,
423
+ inheritGlobalContext: agent.inheritGlobalContext,
403
424
  inheritSkills: agent.inheritSkills,
404
425
  skills: {
405
426
  requested: requestedSkills,
@@ -455,6 +476,7 @@ export async function resolveSubagentLaunchContract(input: SubagentLaunchContrac
455
476
  systemPrompt: effectiveSystemPrompt,
456
477
  systemPromptMode: agent.systemPromptMode,
457
478
  inheritProjectContext: agent.inheritProjectContext,
479
+ inheritGlobalContext: agent.inheritGlobalContext,
458
480
  inheritSkills: agent.inheritSkills,
459
481
  skills: requestedSkills,
460
482
  tools: toolPlan.effectiveToolAllowlist,
@@ -158,13 +158,15 @@ export default function registerFanoutChildSubagentExtension(pi: ExtensionAPI):
158
158
  registeredApis.add(pi);
159
159
 
160
160
  const config = loadConfig();
161
+ const waitToolConfig = resolveWaitToolConfig(config.waitTool);
161
162
  const state = createChildSafeState();
162
163
  const executor = createSubagentExecutor({
163
164
  pi,
164
165
  state,
165
166
  config,
166
167
  asyncByDefault: resolveAsyncByDefault(config),
167
- waitToolEnabled: resolveWaitToolConfig(config.waitTool).enabled,
168
+ waitToolEnabled: waitToolConfig.enabled,
169
+ waitToolDefaultTimeoutMs: waitToolConfig.defaultTimeoutMs,
168
170
  tempArtifactsDir: getArtifactsDir(null),
169
171
  getSubagentSessionRoot,
170
172
  expandTilde,
@@ -178,8 +180,8 @@ export default function registerFanoutChildSubagentExtension(pi: ExtensionAPI):
178
180
  label: "Subagent",
179
181
  description: [
180
182
  "Delegate to subagents from child-safe fanout mode.",
181
- "Allowed management/control actions: list, get, status, interrupt, resume, steer, doctor.",
182
- "Mutating management actions (create, update, delete, eject, disable, enable, reset, grant-spawn-budget) are blocked in this mode.",
183
+ "Allowed management/control actions: list, get, status, lane.status, interrupt, resume, steer, doctor.",
184
+ "Mutating management actions (create, update, delete, eject, disable, enable, reset, grant-spawn-budget, lane.recordMerge, lane.recordSupersession) are blocked in this mode.",
183
185
  ].join("\n"),
184
186
  parameters: params,
185
187
  async execute(id, params, signal, onUpdate, ctx) {