pi-subagents 0.52.1 → 0.54.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 (67) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/README.md +4 -0
  3. package/docs/configuration.md +12 -2
  4. package/docs/extension-api.md +3 -1
  5. package/docs/models.md +17 -3
  6. package/docs/workflows.md +2 -0
  7. package/index.ts +10 -1
  8. package/package.json +2 -1
  9. package/prompts/council.md +51 -0
  10. package/skills/council-mode/SKILL.md +231 -0
  11. package/skills/pi-subagents/SKILL.md +5 -1
  12. package/skills/pi-subagents/references/constraints-and-recipes.md +1 -0
  13. package/skills/pi-subagents/references/execution-controls.md +13 -0
  14. package/skills/pi-subagents/references/prompting-and-roles.md +7 -0
  15. package/src/agents/agent-management.ts +107 -8
  16. package/src/agents/agent-serializer.ts +2 -0
  17. package/src/agents/agents.ts +96 -37
  18. package/src/agents/builtin-names.ts +9 -0
  19. package/src/agents/runtime-agent-registry.ts +418 -0
  20. package/src/api/agents.ts +7 -0
  21. package/src/api/preflight.ts +8 -3
  22. package/src/extension/config.ts +3 -0
  23. package/src/extension/doctor.ts +11 -0
  24. package/src/extension/fanout-child.ts +3 -2
  25. package/src/extension/index.ts +20 -4
  26. package/src/extension/public-execution.ts +1 -1
  27. package/src/extension/rpc.ts +41 -1
  28. package/src/extension/schemas.ts +6 -3
  29. package/src/extension/tool-description.ts +2 -2
  30. package/src/extension/tool-result.ts +19 -0
  31. package/src/inspectors/herdr/client.ts +3 -3
  32. package/src/runs/background/async-execution.ts +12 -6
  33. package/src/runs/background/async-job-tracker.ts +4 -3
  34. package/src/runs/background/async-resume.ts +2 -1
  35. package/src/runs/background/async-retention.ts +1 -1
  36. package/src/runs/background/async-status-snapshot.ts +14 -5
  37. package/src/runs/background/auto-drain.ts +1 -0
  38. package/src/runs/background/chain-root-attachment.ts +5 -0
  39. package/src/runs/background/result-watcher.ts +9 -0
  40. package/src/runs/background/stale-run-reconciler.ts +3 -0
  41. package/src/runs/background/subagent-runner.ts +32 -5
  42. package/src/runs/background/subagent-wait.ts +9 -5
  43. package/src/runs/background/terminal-run-index.ts +15 -6
  44. package/src/runs/background/wait-completions.ts +2 -0
  45. package/src/runs/background/wait-tool.ts +5 -3
  46. package/src/runs/foreground/execution.ts +34 -2
  47. package/src/runs/foreground/subagent-executor.ts +196 -52
  48. package/src/runs/foreground/workflow-detach-reconcile.ts +83 -15
  49. package/src/runs/shared/acceptance.ts +44 -1
  50. package/src/runs/shared/model-exclusions.ts +242 -0
  51. package/src/runs/shared/model-fallback.ts +72 -16
  52. package/src/runs/shared/model-scope.ts +106 -39
  53. package/src/runs/shared/pi-args.ts +34 -1
  54. package/src/runs/shared/subagent-control.ts +25 -3
  55. package/src/runs/shared/subagent-prompt-runtime.ts +36 -9
  56. package/src/shared/fork-context.ts +17 -1
  57. package/src/shared/model-info.ts +20 -0
  58. package/src/shared/settings.ts +2 -2
  59. package/src/shared/types.ts +47 -2
  60. package/src/slash/slash-commands.ts +20 -6
  61. package/src/slash/slash-live-state.ts +3 -3
  62. package/src/tui/fleet-status.ts +86 -1
  63. package/src/tui/fleet.ts +55 -2
  64. package/src/tui/render.ts +73 -3
  65. package/src/watchdog/permission-arbiter.ts +59 -51
  66. package/src/workflows/scripted-workflow.ts +100 -12
  67. package/src/workflows/workflow-receipt.ts +140 -0
@@ -7,7 +7,7 @@ import { findBlockingAgentDiagnostic, resolveAgentName, type AgentConfig, type A
7
7
  import { getArtifactsDir, getChainRunsDir, getProjectArtifactPackagingWarning, getProjectSubagentsDir } from "../../shared/artifacts.ts";
8
8
  import { writeAtomicJson } from "../../shared/atomic-json.ts";
9
9
  import { createCapacityResilientJsonWriter } from "../../shared/capacity-resilient-json.ts";
10
- import { isRetryableFileSystemError, isStorageCapacityError } from "../../shared/file-system-retry.ts";
10
+ import { isStorageCapacityError } from "../../shared/file-system-retry.ts";
11
11
  import { resolveEffectiveThinking, toModelInfo, type ModelInfo } from "../../shared/model-info.ts";
12
12
  import {
13
13
  beginForegroundChild,
@@ -30,7 +30,7 @@ import { handleWatchdogToolAction, WATCHDOG_TOOL_ACTIONS } from "../../watchdog/
30
30
  import type { MainWatchdogRuntime } from "../../watchdog/runtime.ts";
31
31
  import { buildModelCandidates, inheritsParentModel, normalizeParentModel, resolveEffectiveSubagentModel, resolveModelCandidate, type ParentModel } from "../shared/model-fallback.ts";
32
32
  import { formatRetainedChildren, listRetainedChildren } from "../background/retained-children.ts";
33
- import type { ModelScopeConfig } from "../shared/model-scope.ts";
33
+ import { resolveModelScopesForAgent, type ModelScopeConfig } from "../shared/model-scope.ts";
34
34
  import { aggregateParallelOutputs } from "../shared/parallel-utils.ts";
35
35
  import { recordRun } from "../shared/run-history.ts";
36
36
  import {
@@ -118,7 +118,8 @@ import { createMissionWorkflowState } from "../../missions/workflow-state.ts";
118
118
  import { resolveAuthorityDecision } from "../../policy/authority.ts";
119
119
  import { handleHerdrInspectorAction, HERDR_INSPECTOR_ACTIONS } from "../../inspectors/herdr/actions.ts";
120
120
  import { handleHerdrProjectPaneAction, HERDR_PROJECT_PANE_ACTIONS } from "../../inspectors/herdr/project-panes.ts";
121
- import { previewSimpleWorkflowRun, runWorkflowScript, WorkflowScriptError, type WorkflowScriptChildResult, type WorkflowSteerOptions, type WorkflowSteerResult } from "../../workflows/scripted-workflow.ts";
121
+ import { previewSimpleWorkflowRun, runWorkflowScript, WorkflowScriptError, type WorkflowReceiptResumeReference, type WorkflowScriptChildResult, type WorkflowSteerOptions, type WorkflowSteerResult } from "../../workflows/scripted-workflow.ts";
122
+ import { buildWorkflowReceipt, resolveWorkflowReceiptResumeEntry, writeWorkflowReceipt, type WorkflowReceipt, type WorkflowReceiptState } from "../../workflows/workflow-receipt.ts";
122
123
  import { resolveWorkflowChatProgress, type WorkflowChatProgressProjection } from "../../workflows/chat-progress.ts";
123
124
  import {
124
125
  cleanupWorktrees,
@@ -308,7 +309,7 @@ export interface SubagentParamsLike {
308
309
  tasks?: TaskParam[];
309
310
  concurrency?: number;
310
311
  worktree?: boolean;
311
- context?: "fresh" | "fork";
312
+ context?: "fresh" | "fork" | "profile";
312
313
  /** Per-run intercom bridge config. It replaces the global config for this launch only. */
313
314
  intercomBridge?: IntercomBridgeConfig;
314
315
  async?: boolean;
@@ -795,12 +796,13 @@ function updateRememberedForegroundChild(state: SubagentState, input: { runId: s
795
796
  });
796
797
  }
797
798
 
798
- function resolveForegroundResumeTarget(params: SubagentParamsLike, state: SubagentState): { runId: string; mode: SubagentRunMode; state: "complete"; agent: string; index: number; cwd: string; sessionFile: string; model?: string; thinking?: string; launchContractDigest?: string; capabilityCeiling?: ResolvedSubagentCapabilityCeiling } | undefined {
799
+ function resolveForegroundResumeTarget(params: SubagentParamsLike, state: SubagentState, options: { exactOnly?: boolean } = {}): { runId: string; mode: SubagentRunMode; state: "complete"; agent: string; index: number; cwd: string; sessionFile: string; model?: string; thinking?: string; launchContractDigest?: string; capabilityCeiling?: ResolvedSubagentCapabilityCeiling } | undefined {
799
800
  const requested = (params.id ?? params.runId)?.trim();
800
801
  if (!requested || !state.foregroundRuns?.size || !state.currentSessionId) return undefined;
801
- const sessionRuns = [...state.foregroundRuns.values()].filter((run) => run.sessionId === state.currentSessionId);
802
- const direct = sessionRuns.find((run) => run.runId === requested);
803
- const matches = direct ? [direct] : sessionRuns.filter((run) => run.runId.startsWith(requested));
802
+ const direct = state.foregroundRuns.get(requested);
803
+ const matches = direct?.sessionId === state.currentSessionId
804
+ ? [direct]
805
+ : options.exactOnly ? [] : [...state.foregroundRuns.values()].filter((run) => run.sessionId === state.currentSessionId && run.runId.startsWith(requested));
804
806
  if (matches.length === 0) return undefined;
805
807
  if (matches.length > 1) throw new Error(`Ambiguous foreground run id prefix '${requested}' matched: ${matches.map((run) => run.runId).join(", ")}. Provide a longer id.`);
806
808
  const run = matches[0]!;
@@ -851,6 +853,10 @@ function isAsyncRunNotFound(error: unknown): boolean {
851
853
  return error instanceof Error && error.message.startsWith("Async run not found.");
852
854
  }
853
855
 
856
+ function isMissingExactAsyncStatusFile(error: unknown, requested: string): boolean {
857
+ return error instanceof Error && error.message === `Status file not found for async run '${requested}'.`;
858
+ }
859
+
854
860
  function isResumeAmbiguity(error: unknown): boolean {
855
861
  return error instanceof Error && /Ambiguous .*run id prefix/.test(error.message);
856
862
  }
@@ -868,7 +874,7 @@ function isExactResumeError(error: unknown, source: "async" | "foreground", requ
868
874
  return new RegExp(`\\b${source} run '${escapeRegExp(requested)}'`, "i").test(error.message);
869
875
  }
870
876
 
871
- function resolveResumeTarget(params: SubagentParamsLike, state: SubagentState, options: { asyncRequireSessionFile?: boolean } = {}): ResumeSourceTarget {
877
+ function resolveResumeTarget(params: SubagentParamsLike, state: SubagentState, options: { asyncRequireSessionFile?: boolean; exactOnly?: boolean } = {}): ResumeSourceTarget {
872
878
  const requested = (params.id ?? params.runId)?.trim() ?? "";
873
879
  let foregroundTarget: ForegroundResumeSourceTarget | undefined;
874
880
  let foregroundError: unknown;
@@ -876,15 +882,18 @@ function resolveResumeTarget(params: SubagentParamsLike, state: SubagentState, o
876
882
  let asyncError: unknown;
877
883
 
878
884
  try {
879
- const target = resolveForegroundResumeTarget(params, state);
885
+ const target = resolveForegroundResumeTarget(params, state, options);
880
886
  if (target) foregroundTarget = { kind: "revive", source: "foreground", ...target };
881
887
  } catch (error) {
882
888
  foregroundError = error;
883
889
  }
884
890
  try {
891
+ const asyncParams = options.exactOnly && requested && !params.dir
892
+ ? { ...params, dir: path.join(DIRS.async, requested) }
893
+ : params;
885
894
  asyncTarget = {
886
895
  source: "async",
887
- ...resolveAsyncResumeTarget(params, {}, compactOptional<NonNullable<Parameters<typeof resolveAsyncResumeTarget>[2]>>({
896
+ ...resolveAsyncResumeTarget(asyncParams, {}, compactOptional<NonNullable<Parameters<typeof resolveAsyncResumeTarget>[2]>>({
888
897
  requireSessionFile: options.asyncRequireSessionFile,
889
898
  sessionId: state.currentSessionId ?? undefined,
890
899
  })),
@@ -901,7 +910,7 @@ function resolveResumeTarget(params: SubagentParamsLike, state: SubagentState, o
901
910
  throw new Error(`Resume id '${requested}' is ambiguous between foreground run '${foregroundTarget.runId}' and async run '${asyncTarget.runId}'. Provide a full run id.`);
902
911
  }
903
912
  if (foregroundTarget) {
904
- if (isExactResumeError(asyncError, "async", requested)) throw asyncError;
913
+ if (isExactResumeError(asyncError, "async", requested) && !isMissingExactAsyncStatusFile(asyncError, requested)) throw asyncError;
905
914
  if (isResumeAmbiguity(asyncError) && !resumeTargetExact(foregroundTarget, requested)) throw asyncError;
906
915
  return foregroundTarget;
907
916
  }
@@ -1566,10 +1575,11 @@ async function resumeAsyncRun(input: {
1566
1575
  const modelScope = discovered.modelScope;
1567
1576
  const sessionName = resolveIntercomSessionTarget(input.deps.pi.getSessionName(), input.ctx.sessionManager.getSessionId());
1568
1577
  const recoveryDescriptor = "recoveryDescriptor" in target ? target.recoveryDescriptor : undefined;
1578
+ const recoveryContext = recoveryDescriptor?.context ?? (input.params.context === "profile" ? undefined : input.params.context);
1569
1579
  const intercomBridge = resolveIntercomBridge({
1570
1580
  config: input.deps.config.intercomBridge,
1571
1581
  override: input.params.intercomBridge ?? recoveryDescriptor?.intercomBridge,
1572
- context: input.params.context,
1582
+ context: recoveryContext,
1573
1583
  orchestratorTarget: sessionName,
1574
1584
  });
1575
1585
  const agents = intercomBridge.active
@@ -1765,6 +1775,7 @@ async function resumeAsyncRun(input: {
1765
1775
  sourceRunId: target.runId,
1766
1776
  ...(input.deps.state.currentSessionId ? { parentSessionId: input.deps.state.currentSessionId } : {}),
1767
1777
  },
1778
+ context: recoveryContext,
1768
1779
  modelOverride: recoveryDescriptor?.model ?? target.model,
1769
1780
  modelOverrideFromParent: recoveryDescriptor?.modelOverrideFromParent,
1770
1781
  thinkingOverride: recoveryDescriptor?.thinking ?? target.thinking,
@@ -1859,6 +1870,7 @@ async function resumeAsyncRun(input: {
1859
1870
  ...(completed.model ? { model: completed.model } : {}),
1860
1871
  ...(completed.attemptedModels ? { attemptedModels: completed.attemptedModels } : {}),
1861
1872
  ...(completed.modelAttempts ? { modelAttempts: completed.modelAttempts } : {}),
1873
+ ...(completed.contextOverflow ? { contextOverflow: true } : {}),
1862
1874
  ...(completed.structuredOutput !== undefined ? { structuredOutput: completed.structuredOutput } : {}),
1863
1875
  ...(completed.structuredOutputPath ? { structuredOutputPath: completed.structuredOutputPath } : {}),
1864
1876
  ...(completed.structuredOutputSchemaPath ? { structuredOutputSchemaPath: completed.structuredOutputSchemaPath } : {}),
@@ -1916,9 +1928,12 @@ function formatFailedSingleRunOutput(result: SingleResult, displayOutput: string
1916
1928
  return lines.join("\n");
1917
1929
  }
1918
1930
 
1919
- function createForegroundControlNotifier(data: Pick<ExecutionContextData, "controlConfig" | "intercomBridge" | "params">, deps: Pick<ExecutorDeps, "pi" | "state">): (event: ControlEvent) => void {
1931
+ function createForegroundControlNotifier(data: Pick<ExecutionContextData, "controlConfig" | "contextPolicy" | "intercomBridge" | "params">, deps: Pick<ExecutorDeps, "pi" | "state">): (event: ControlEvent) => void {
1920
1932
  return (event) => {
1921
1933
  applyControlEventToRememberedForegroundRun(deps.state, event);
1934
+ const eventBridge = intercomBridgeAppliesToAgent(data.intercomBridge, data.contextPolicy, event.agent)
1935
+ ? data.intercomBridge
1936
+ : { ...data.intercomBridge, active: false };
1922
1937
  const parentWorkflowRunId = data.params.workflowParentRunId;
1923
1938
  const asyncWorkflow = typeof parentWorkflowRunId === "string" ? deps.state.asyncJobs.get(parentWorkflowRunId) : undefined;
1924
1939
  const workflowKey = typeof data.params.workflowKey === "string" && data.params.workflowKey.trim()
@@ -1930,8 +1945,8 @@ function createForegroundControlNotifier(data: Pick<ExecutionContextData, "contr
1930
1945
  job: asyncWorkflow,
1931
1946
  event: enriched,
1932
1947
  controlConfig: data.controlConfig,
1933
- intercomBridge: data.intercomBridge,
1934
- childIntercomTarget: data.intercomBridge.active
1948
+ intercomBridge: eventBridge,
1949
+ childIntercomTarget: eventBridge.active
1935
1950
  ? resolveSubagentIntercomTarget(enriched.runId, enriched.agent, enriched.index)
1936
1951
  : undefined,
1937
1952
  });
@@ -1939,7 +1954,7 @@ function createForegroundControlNotifier(data: Pick<ExecutionContextData, "contr
1939
1954
  emitControlNotification({
1940
1955
  pi: deps.pi,
1941
1956
  controlConfig: data.controlConfig,
1942
- intercomBridge: data.intercomBridge,
1957
+ intercomBridge: eventBridge,
1943
1958
  event: enriched,
1944
1959
  source: asyncWorkflow ? "async" : "foreground",
1945
1960
  });
@@ -2243,17 +2258,40 @@ interface AgentDefaultContextPolicy {
2243
2258
  usesFork: boolean;
2244
2259
  }
2245
2260
 
2261
+ type AgentDefaultContextPolicyResult = AgentDefaultContextPolicy | { error: string };
2262
+
2246
2263
  function resolveAgentDefaultContextPolicy(
2247
2264
  params: SubagentParamsLike,
2248
2265
  agents: AgentConfig[],
2249
2266
  defaultSubagentContext: ExtensionConfig["defaultSubagentContext"],
2250
2267
  canUseDefaultFork = false,
2251
- ): AgentDefaultContextPolicy {
2252
- if (params.context !== undefined) return resolveExplicitContextPolicy(params);
2268
+ ): AgentDefaultContextPolicyResult {
2269
+ if (params.context === "profile") {
2270
+ const byName = new Map(agents.map((agent) => [agent.name, agent]));
2271
+ for (const agentName of collectRequestedAgentNames(params)) {
2272
+ const agent = byName.get(agentName);
2273
+ if (agent && agent.defaultContext === undefined) {
2274
+ return { error: `context: "profile" requires agent '${agentName}' to declare defaultContext.` };
2275
+ }
2276
+ }
2277
+ const contextForAgent = (agentName: string): ContextMode => {
2278
+ const context = byName.get(agentName)?.defaultContext;
2279
+ if (context === undefined) throw new Error(`context: "profile" requires agent '${agentName}' to declare defaultContext.`);
2280
+ return context;
2281
+ };
2282
+ const contextSummary = summarizeContextModes(collectRequestedAgentNames(params).map(contextForAgent));
2283
+ return {
2284
+ params,
2285
+ contextForAgent,
2286
+ contextSummary,
2287
+ usesFork: contextSummary === "fork" || contextSummary === "mixed",
2288
+ };
2289
+ }
2290
+ if (params.context === "fresh" || params.context === "fork") return resolveExplicitContextPolicy(params);
2253
2291
  const byName = new Map(agents.map((agent) => [agent.name, agent]));
2254
2292
  const contextForAgent = (agentName: string): ContextMode =>
2255
2293
  resolveSubagentLaunchContext({
2256
- explicitContext: params.context,
2294
+ explicitContext: undefined,
2257
2295
  agentDefaultContext: byName.get(agentName)?.defaultContext,
2258
2296
  defaultSubagentContext,
2259
2297
  canUseImplicitFork: canUseDefaultFork,
@@ -2271,7 +2309,7 @@ function resolveAgentDefaultContextPolicy(
2271
2309
 
2272
2310
  function resolveExplicitContextPolicy(params: SubagentParamsLike): AgentDefaultContextPolicy {
2273
2311
  const context = resolveSubagentLaunchContext({
2274
- explicitContext: params.context,
2312
+ explicitContext: params.context === "profile" ? undefined : params.context,
2275
2313
  canUseImplicitFork: false,
2276
2314
  });
2277
2315
  return {
@@ -2294,6 +2332,31 @@ function shouldForkAgent(contextPolicy: AgentDefaultContextPolicy, agentName: st
2294
2332
  return contextPolicy.contextForAgent(agentName) === "fork";
2295
2333
  }
2296
2334
 
2335
+ function intercomBridgeAppliesToAgent(bridge: IntercomBridgeState, contextPolicy: AgentDefaultContextPolicy, agentName: string): boolean {
2336
+ if (!bridge.active) return false;
2337
+ return bridge.mode !== "fork-only" || shouldForkAgent(contextPolicy, agentName);
2338
+ }
2339
+
2340
+ function applyScopedIntercomBridgeToAgents(agents: AgentConfig[], bridge: IntercomBridgeState, contextPolicy: AgentDefaultContextPolicy): AgentConfig[] {
2341
+ if (!bridge.active) return agents;
2342
+ return agents.map((agent) => intercomBridgeAppliesToAgent(bridge, contextPolicy, agent.name)
2343
+ ? applyIntercomBridgeToAgent(agent, bridge)
2344
+ : agent);
2345
+ }
2346
+
2347
+ function resolveChildIntercomTargetFactory(bridge: IntercomBridgeState, contextPolicy: AgentDefaultContextPolicy, runId: string): ((agent: string, index: number) => string | undefined) | undefined {
2348
+ if (!bridge.active) return undefined;
2349
+ return (agent, index) => intercomBridgeAppliesToAgent(bridge, contextPolicy, agent)
2350
+ ? resolveSubagentIntercomTarget(runId, agent, index)
2351
+ : undefined;
2352
+ }
2353
+
2354
+ function resolveRunLevelIntercomTarget(bridge: IntercomBridgeState, contextPolicy: AgentDefaultContextPolicy): string | undefined {
2355
+ if (!bridge.active) return undefined;
2356
+ if (bridge.mode === "fork-only" && contextPolicy.contextSummary === "mixed") return undefined;
2357
+ return bridge.orchestratorTarget;
2358
+ }
2359
+
2297
2360
  function summarizeResultContext(details: Details, fallback: ContextSummary | undefined): ContextSummary | undefined {
2298
2361
  return summarizeContextModes(details.results.map((result) => result.context)) ?? fallback;
2299
2362
  }
@@ -2305,7 +2368,7 @@ function buildRequestedModeError(params: SubagentParamsLike, message: string): A
2305
2368
  isError: true,
2306
2369
  details: { mode: getRequestedModeLabel(params), results: [] },
2307
2370
  },
2308
- params.context,
2371
+ params.context === "profile" ? undefined : params.context,
2309
2372
  );
2310
2373
  }
2311
2374
 
@@ -2594,6 +2657,7 @@ function resolveStaticLaunchSummary(input: {
2594
2657
  }): StaticLaunchSummary {
2595
2658
  const agentConfig = input.agents.find((agent) => agent.name === input.agent);
2596
2659
  const externalRunner = agentConfig?.runner?.type === "external-cli" || agentConfig?.runner?.type === "external-job";
2660
+ const modelScopes = resolveModelScopesForAgent(input.modelScope, input.agent, input.parentModel);
2597
2661
  const model = externalRunner
2598
2662
  ? undefined
2599
2663
  : resolveEffectiveSubagentModel(
@@ -2602,7 +2666,7 @@ function resolveStaticLaunchSummary(input: {
2602
2666
  input.parentModel,
2603
2667
  input.availableModels,
2604
2668
  input.currentProvider,
2605
- input.modelScope === undefined ? {} : { scope: input.modelScope },
2669
+ modelScopes.length === 0 ? {} : { scope: modelScopes },
2606
2670
  );
2607
2671
  const thinkingOverride = externalRunner ? undefined : input.thinkingOverrideForTask(input.agent, input.index, model);
2608
2672
  const thinking = externalRunner ? undefined : resolveEffectiveThinking(model, thinkingOverride ?? agentConfig?.thinking);
@@ -2810,8 +2874,8 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
2810
2874
  const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
2811
2875
  const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth);
2812
2876
  const currentProvider = parentModel?.provider;
2813
- const controlIntercomTarget = intercomBridge.active ? intercomBridge.orchestratorTarget : undefined;
2814
- const childIntercomTarget = intercomBridge.active ? (agent: string, index: number) => resolveSubagentIntercomTarget(id, agent, index) : undefined;
2877
+ const controlIntercomTarget = resolveRunLevelIntercomTarget(intercomBridge, contextPolicy);
2878
+ const childIntercomTarget = resolveChildIntercomTargetFactory(intercomBridge, contextPolicy, id);
2815
2879
 
2816
2880
 
2817
2881
  if (hasSingle) {
@@ -2825,16 +2889,17 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
2825
2889
  }
2826
2890
  const rawOutput = params.output !== undefined ? params.output : a.output;
2827
2891
  const effectiveOutput = normalizeSingleOutputOverride(rawOutput, a.output);
2828
- const effectiveOutputMode = params.outputMode ?? "inline";
2892
+ const effectiveOutputMode = params.outputMode ?? a.outputMode ?? "inline";
2829
2893
  const normalizedSkills = normalizeSkillInput(params.skill);
2830
2894
  const skills = normalizedSkills === false ? [] : normalizedSkills;
2831
2895
  const maxSubagentDepth = resolveChildMaxSubagentDepth(currentMaxSubagentDepth, a.maxSubagentDepth);
2832
2896
  const externalRunnerWithoutExplicitModel = (a.runner?.type === "external-cli" || a.runner?.type === "external-job")
2833
2897
  && params.model === undefined
2834
2898
  && (a.model === undefined || (a.modelSource?.type === "subagents.defaultModel" && a.model === a.modelSource.model));
2899
+ const modelScopes = resolveModelScopesForAgent(data.modelScope, a.name, parentModel);
2835
2900
  const modelOverride = a.runner?.type === "external-cli" || a.runner?.type === "external-job"
2836
2901
  ? params.model ?? (externalRunnerWithoutExplicitModel ? undefined : a.model)
2837
- : resolveEffectiveSubagentModel(params.model as string | undefined, a.model, parentModel, availableModels, currentProvider, data.modelScope === undefined ? {} : { scope: data.modelScope });
2902
+ : resolveEffectiveSubagentModel(params.model as string | undefined, a.model, parentModel, availableModels, currentProvider, modelScopes.length === 0 ? {} : { scope: modelScopes });
2838
2903
  const modelOverrideFromParent = inheritsParentModel(params.model as string | undefined, a.model, parentModel);
2839
2904
  return executeAsyncSingle(id, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
2840
2905
  agent: params.agent!,
@@ -3158,7 +3223,8 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3158
3223
  contextPolicy,
3159
3224
  } = data;
3160
3225
  const onControlEvent = createForegroundControlNotifier(data, deps);
3161
- const childIntercomTarget = data.intercomBridge.active ? resolveSubagentIntercomTarget(runId, params.agent!, 0) : undefined;
3226
+ const childBridgeActive = intercomBridgeAppliesToAgent(data.intercomBridge, contextPolicy, params.agent!);
3227
+ const childIntercomTarget = childBridgeActive ? resolveSubagentIntercomTarget(runId, params.agent!, 0) : undefined;
3162
3228
  const allProgress: AgentProgress[] = [];
3163
3229
  const allArtifactPaths: ArtifactPaths[] = [];
3164
3230
  const agentConfig = agents.find((a) => a.name === params.agent);
@@ -3175,6 +3241,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3175
3241
  const parentModel = data.parentModel;
3176
3242
  const currentProvider = parentModel?.provider;
3177
3243
  const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
3244
+ const modelScopes = resolveModelScopesForAgent(data.modelScope, agentConfig.name, parentModel);
3178
3245
  let task = params.task ?? "";
3179
3246
  let modelOverride: string | undefined = resolveEffectiveSubagentModel(
3180
3247
  params.model as string | undefined,
@@ -3182,14 +3249,14 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3182
3249
  parentModel,
3183
3250
  availableModels,
3184
3251
  currentProvider,
3185
- data.modelScope === undefined ? {} : { scope: data.modelScope },
3252
+ modelScopes.length === 0 ? {} : { scope: modelScopes },
3186
3253
  );
3187
3254
  const modelOverrideFromParent = inheritsParentModel(params.model as string | undefined, agentConfig.model, parentModel);
3188
3255
  let skillOverride: string[] | false | undefined = normalizeSkillInput(params.skill);
3189
3256
  let readsOverride: string[] | false | undefined = params.reads;
3190
3257
  const rawOutput = params.output !== undefined ? params.output : agentConfig.output;
3191
3258
  let effectiveOutput = normalizeSingleOutputOverride(rawOutput, agentConfig.output);
3192
- const effectiveOutputMode = params.outputMode ?? "inline";
3259
+ const effectiveOutputMode = params.outputMode ?? agentConfig.outputMode ?? "inline";
3193
3260
  const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth);
3194
3261
  const maxSubagentDepth = resolveChildMaxSubagentDepth(currentMaxSubagentDepth, agentConfig.maxSubagentDepth);
3195
3262
 
@@ -3317,7 +3384,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3317
3384
  controlConfig,
3318
3385
  onControlEvent,
3319
3386
  intercomSessionName: childIntercomTarget,
3320
- orchestratorIntercomTarget: data.intercomBridge.active ? data.intercomBridge.orchestratorTarget : undefined,
3387
+ orchestratorIntercomTarget: childBridgeActive ? data.intercomBridge.orchestratorTarget : undefined,
3321
3388
  nestedRoute: foregroundControl?.nestedRoute,
3322
3389
  index: 0,
3323
3390
  modelOverride,
@@ -3325,7 +3392,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3325
3392
  thinkingOverride: thinkingOverrideForTask(params.agent!, 0, modelOverride, modelOverrideFromParent),
3326
3393
  availableModels,
3327
3394
  preferredModelProvider: currentProvider,
3328
- modelScope: data.modelScope,
3395
+ modelScope: modelScopes,
3329
3396
  skills: effectiveSkills,
3330
3397
  structuredOutput: structuredRuntime,
3331
3398
  agentContract: params.agentContract,
@@ -3579,7 +3646,12 @@ export function bindMissionWorkflowChildAsyncLaunch(
3579
3646
  return { ...params, workflowChildAsyncId: id };
3580
3647
  }
3581
3648
 
3582
- function workflowChildResult(key: string, result: AgentToolResult<Details>): WorkflowScriptChildResult {
3649
+ function workflowChildResult(
3650
+ key: string,
3651
+ result: AgentToolResult<Details>,
3652
+ childParams: Record<string, unknown> = {},
3653
+ resumeState?: SubagentState,
3654
+ ): WorkflowScriptChildResult {
3583
3655
  const receiptOutput = result.content.map((part) => part.type === "text" ? part.text : "").filter(Boolean).join("\n");
3584
3656
  const output = result.details.results.length === 1 && result.details.results[0]?.finalOutput !== undefined
3585
3657
  ? result.details.results[0].finalOutput
@@ -3596,15 +3668,41 @@ function workflowChildResult(key: string, result: AgentToolResult<Details>): Wor
3596
3668
  }
3597
3669
  const structured = result.details.results.map((child) => child.structuredOutput).filter((value) => value !== undefined);
3598
3670
  const resolvedAgents = [...new Set(result.details.results.map((child) => child.agent).filter((agent): agent is string => Boolean(agent)))];
3671
+ const resolvedContexts = [...new Set(result.details.results.map((child) => child.context).filter((context): context is "fresh" | "fork" => context === "fresh" || context === "fork"))];
3672
+ const runId = result.details.runId ?? result.details.asyncId;
3673
+ let resumability: WorkflowScriptChildResult["resumability"];
3674
+ if (!runId || !resumeState) {
3675
+ resumability = { state: "not-resumable", reason: runId ? "resumability was not inspected" : "child produced no run id" };
3676
+ } else {
3677
+ try {
3678
+ const target = resolveResumeTarget({ id: runId }, resumeState, { asyncRequireSessionFile: true, exactOnly: true });
3679
+ resumability = target.kind === "revive"
3680
+ ? { state: "resumable" }
3681
+ : { state: "not-resumable", reason: "child is still running" };
3682
+ } catch (error) {
3683
+ resumability = { state: "not-resumable", reason: error instanceof Error ? error.message : String(error) };
3684
+ }
3685
+ }
3686
+ const requestedContext = childParams.context === "fresh" || childParams.context === "fork" ? childParams.context : undefined;
3687
+ const resolvedContext = result.details.context ?? (resolvedContexts.length === 1 ? resolvedContexts[0] : resolvedContexts.length > 1 ? "mixed" : undefined);
3688
+ const resumeSourceRunId = typeof childParams.resume === "string" && childParams.resume.trim() ? childParams.resume.trim() : undefined;
3689
+ const continuationRunIds = [...new Set([resumeSourceRunId, runId].filter((value): value is string => Boolean(value)))];
3690
+ const outputReference = result.details.results.find((child) => child.savedOutputPath)?.savedOutputPath
3691
+ ?? result.details.results.find((child) => child.outputReference?.path)?.outputReference?.path;
3599
3692
  return {
3600
3693
  key,
3601
3694
  ok,
3602
3695
  ...(resolvedAgents.length === 1 ? { agent: resolvedAgents[0] } : {}),
3603
- ...(result.details.runId || result.details.asyncId ? { runId: result.details.runId ?? result.details.asyncId } : {}),
3696
+ ...(runId ? { runId } : {}),
3604
3697
  output,
3605
3698
  ...(!ok ? { error: receiptOutput || output || "Child run failed." } : {}),
3606
3699
  ...(detached ? { detached: true } : {}),
3607
3700
  ...(structured.length === 1 ? { structuredOutput: structured[0] } : structured.length > 1 ? { structuredOutput: structured } : {}),
3701
+ ...(requestedContext ? { requestedContext } : {}),
3702
+ ...(resolvedContext ? { resolvedContext } : {}),
3703
+ ...(outputReference ? { outputReference } : {}),
3704
+ resumability,
3705
+ continuation: { runIds: continuationRunIds },
3608
3706
  artifactPaths: [...artifactPaths],
3609
3707
  results: result.details.results,
3610
3708
  };
@@ -3627,6 +3725,31 @@ function workflowSteerReceipt(key: string, result: AgentToolResult<Details>): Wo
3627
3725
  };
3628
3726
  }
3629
3727
 
3728
+ function resolveKeyedWorkflowResume(
3729
+ reference: WorkflowReceiptResumeReference,
3730
+ state: SubagentState,
3731
+ ): { runId: string; runIds: string[] } {
3732
+ const entry = resolveWorkflowReceiptResumeEntry({
3733
+ reference,
3734
+ asyncDirRoot: DIRS.async,
3735
+ assertResumable(runId) {
3736
+ const target = resolveResumeTarget({ id: runId }, state, { asyncRequireSessionFile: true, exactOnly: true });
3737
+ if (target.kind !== "revive") throw new Error(`Workflow receipt child '${reference.key}' latest run '${runId}' is still running.`);
3738
+ },
3739
+ });
3740
+ const runId = entry.latestRunId;
3741
+ if (!runId) throw new Error(`Workflow receipt child '${reference.key}' has no retained run id.`);
3742
+ return { runId, runIds: entry.continuation.runIds };
3743
+ }
3744
+
3745
+ function terminalWorkflowReceipt(
3746
+ workflowRunId: string,
3747
+ state: WorkflowReceiptState,
3748
+ children: WorkflowScriptChildResult[],
3749
+ ): WorkflowReceipt {
3750
+ return buildWorkflowReceipt({ workflowRunId, state, children });
3751
+ }
3752
+
3630
3753
  export async function steerWorkflowChildByKey(input: {
3631
3754
  state: SubagentState;
3632
3755
  workflowRunId: string;
@@ -4018,7 +4141,6 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4018
4141
  // The event log is a journal, not workflow truth. Callers append from
4019
4142
  // inside run-result handling, so losing an entry to a full disk or to a
4020
4143
  // transient Windows lock must not fail the run being recorded.
4021
- if (!isStorageCapacityError(error) && !isRetryableFileSystemError(error)) throw error;
4022
4144
  console.error(`Failed to append async workflow event '${eventsPath}':`, error);
4023
4145
  }
4024
4146
  };
@@ -4248,9 +4370,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4248
4370
  appendWorkflowEvent({ type: "subagent.workflow.emit", value: emits.at(-1) });
4249
4371
  },
4250
4372
  launch: async (key, childParams, workflowSignal, admission) => {
4251
- if (workflowUsageBudget.budget && childParams.async === true) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, "workflow usageBudget does not support async runs.run launches."));
4373
+ if (workflowUsageBudget.budget && childParams.async === true) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, "workflow usageBudget does not support async runs.run launches."), childParams, deps.state);
4252
4374
  const budgetState = usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults));
4253
- if (budgetState?.exhausted) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, usageBudgetExceededMessage(budgetState)));
4375
+ if (budgetState?.exhausted) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, usageBudgetExceededMessage(budgetState)), childParams, deps.state);
4254
4376
  const childPhase = typeof childParams.phase === "string" && childParams.phase.trim() ? childParams.phase.trim() : undefined;
4255
4377
  const childLabel = typeof childParams.label === "string" && childParams.label.trim() ? childParams.label.trim() : undefined;
4256
4378
  recordMissionWorkflowChild(missionBinding, workflowRunId, key, {
@@ -4307,7 +4429,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4307
4429
  for (const childResult of result.details.results) {
4308
4430
  if (childResult.savedOutputPath) producedChildOutputPaths.add(childResult.savedOutputPath);
4309
4431
  }
4310
- const child = workflowChildResult(key, result);
4432
+ const child = workflowChildResult(key, result, childParams, deps.state);
4311
4433
  if (child.runId) workflowChildRunIds.set(key, child.runId);
4312
4434
  const step = status.steps?.find((candidate) => candidate.workflowKey === key);
4313
4435
  if (step) {
@@ -4332,6 +4454,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4332
4454
  return child;
4333
4455
  },
4334
4456
  status: async (keyOrRunId, workflowSignal) => workflowChildResult(keyOrRunId, await execute(randomUUID(), { action: "status", id: keyOrRunId }, workflowSignal, undefined, ctx, preserveActiveSession)),
4457
+ resolveResume: (reference) => resolveKeyedWorkflowResume(reference, deps.state),
4335
4458
  steer: (key, message, options, workflowSignal) => steerWorkflowChildByKey({ state: deps.state, workflowRunId, key, message, options, signal: workflowSignal, resolveRunId: () => workflowChildRunIds.get(key) }),
4336
4459
  });
4337
4460
  const returnPreview = formatWorkflowValue(workflow.value).slice(0, 1_000);
@@ -4341,7 +4464,14 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4341
4464
  const resultSummary = appendWorkflowOutputWarning(summary, outputWarning);
4342
4465
  const workflowUsage = sumResultsUsage(workflowResults);
4343
4466
  status = { ...status, state: "complete", endedAt: Date.now(), workflow: { value: workflow.value, trace: workflow.trace, emits: workflow.emits, console: workflow.console }, totalTokens: { input: workflowUsage.input, output: workflowUsage.output, total: workflowUsage.input + workflowUsage.output }, totalCost: sumResultsCost(workflowResults) };
4344
- if (!writeWorkflowResult({ id: workflowRunId, runId: workflowRunId, toolCallId, agent: "workflow", mode: "workflow", success: true, state: "complete", summary: resultSummary, output: resultSummary, results: workflow.children.map((child) => ({ workflowKey: child.key, ...(child.agent ? { agent: child.agent } : {}), ...(child.runId ? { runId: child.runId } : {}), output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, success: child.ok, ...(child.artifactPaths[0] ? { artifactPaths: { outputPath: child.artifactPaths[0] } } : {}) })), workflow: status.workflow, asyncDir, cwd: workflowCwd, sessionId: currentSessionId, completionOwnerId, ...(requestParams.scheduleOrigin ? { scheduleOrigin: requestParams.scheduleOrigin } : {}), timestamp: Date.now(), durationMs: Date.now() - startedAt })) return;
4467
+ const receipt = terminalWorkflowReceipt(workflowRunId, "complete", workflow.children);
4468
+ let workflowReceipt: { path: string; receipt: WorkflowReceipt } | undefined;
4469
+ try {
4470
+ workflowReceipt = { path: writeWorkflowReceipt(asyncDir, receipt), receipt };
4471
+ } catch (receiptError) {
4472
+ appendWorkflowEvent({ type: "subagent.workflow.receipt_write_failed", error: `Failed to persist async workflow receipt: ${receiptError instanceof Error ? receiptError.message : String(receiptError)}` });
4473
+ }
4474
+ if (!writeWorkflowResult({ id: workflowRunId, runId: workflowRunId, toolCallId, agent: "workflow", mode: "workflow", success: true, state: "complete", summary: resultSummary, output: resultSummary, results: workflow.children.map((child) => ({ workflowKey: child.key, ...(child.agent ? { agent: child.agent } : {}), ...(child.runId ? { runId: child.runId } : {}), output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, success: child.ok, ...(child.artifactPaths[0] ? { artifactPaths: { outputPath: child.artifactPaths[0] } } : {}) })), workflow: status.workflow, ...(workflowReceipt ? { workflowReceipt } : {}), asyncDir, cwd: workflowCwd, sessionId: currentSessionId, completionOwnerId, ...(requestParams.scheduleOrigin ? { scheduleOrigin: requestParams.scheduleOrigin } : {}), timestamp: Date.now(), durationMs: Date.now() - startedAt })) return;
4345
4475
  persist();
4346
4476
  persistClosed = true;
4347
4477
  appendWorkflowEvent({ type: "subagent.workflow.completed", state: status.state, ...(status.error ? { error: status.error } : {}) });
@@ -4373,7 +4503,15 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4373
4503
  : status.error ?? (pauseForDetached ? "Workflow paused." : "Workflow failed.");
4374
4504
  const outputWarning = writeWorkflowAggregateOutput(workflowAggregateOutputPath, terminalSummary, producedChildOutputPaths);
4375
4505
  const resultSummary = appendWorkflowOutputWarning(terminalSummary, outputWarning);
4376
- if (!writeWorkflowResult({ id: workflowRunId, runId: workflowRunId, toolCallId, agent: "workflow", mode: "workflow", success: status.state === "complete", state: status.state, summary: resultSummary, error: status.state === "complete" ? undefined : status.error, stopped: status.stopped, activityState: status.activityState, results: partial.children.map((child) => ({ workflowKey: child.key, ...(child.agent ? { agent: child.agent } : {}), ...(child.runId ? { runId: child.runId } : {}), output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, success: child.ok, ...(child.detached && status.state !== "complete" ? { detached: true } : {}), ...(child.artifactPaths[0] ? { artifactPaths: { outputPath: child.artifactPaths[0] } } : {}) })), workflow: status.workflow, asyncDir, cwd: workflowCwd, sessionId: currentSessionId, completionOwnerId, ...(requestParams.scheduleOrigin ? { scheduleOrigin: requestParams.scheduleOrigin } : {}), timestamp: Date.now(), durationMs: Date.now() - startedAt })) return;
4506
+ const receiptState: WorkflowReceiptState = status.state === "complete" ? "complete" : status.state === "paused" ? "paused" : status.state === "stopped" ? "stopped" : "failed";
4507
+ const receipt = terminalWorkflowReceipt(workflowRunId, receiptState, partial.children);
4508
+ let workflowReceipt: { path: string; receipt: WorkflowReceipt } | undefined;
4509
+ try {
4510
+ workflowReceipt = { path: writeWorkflowReceipt(asyncDir, receipt), receipt };
4511
+ } catch (receiptError) {
4512
+ appendWorkflowEvent({ type: "subagent.workflow.receipt_write_failed", error: `Failed to persist async workflow receipt: ${receiptError instanceof Error ? receiptError.message : String(receiptError)}` });
4513
+ }
4514
+ if (!writeWorkflowResult({ id: workflowRunId, runId: workflowRunId, toolCallId, agent: "workflow", mode: "workflow", success: status.state === "complete", state: status.state, summary: resultSummary, error: status.state === "complete" ? undefined : status.error, stopped: status.stopped, activityState: status.activityState, results: partial.children.map((child) => ({ workflowKey: child.key, ...(child.agent ? { agent: child.agent } : {}), ...(child.runId ? { runId: child.runId } : {}), output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, success: child.ok, ...(child.detached && status.state !== "complete" ? { detached: true } : {}), ...(child.artifactPaths[0] ? { artifactPaths: { outputPath: child.artifactPaths[0] } } : {}) })), workflow: status.workflow, ...(workflowReceipt ? { workflowReceipt } : {}), asyncDir, cwd: workflowCwd, sessionId: currentSessionId, completionOwnerId, ...(requestParams.scheduleOrigin ? { scheduleOrigin: requestParams.scheduleOrigin } : {}), timestamp: Date.now(), durationMs: Date.now() - startedAt })) return;
4377
4515
  persist();
4378
4516
  persistClosed = true;
4379
4517
  appendWorkflowEvent({ type: "subagent.workflow.completed", state: status.state, ...(status.error ? { error: status.error } : {}), ...(status.activityState ? { activityState: status.activityState } : {}) });
@@ -4426,9 +4564,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4426
4564
  sendWorkflowProgress();
4427
4565
  },
4428
4566
  launch: async (key, childParams, workflowSignal, admission) => {
4429
- if (workflowUsageBudget.budget && childParams.async === true) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, "workflow usageBudget does not support async runs.run launches."));
4567
+ if (workflowUsageBudget.budget && childParams.async === true) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, "workflow usageBudget does not support async runs.run launches."), childParams, deps.state);
4430
4568
  const budgetState = usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults));
4431
- if (budgetState?.exhausted) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, usageBudgetExceededMessage(budgetState)));
4569
+ if (budgetState?.exhausted) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, usageBudgetExceededMessage(budgetState)), childParams, deps.state);
4432
4570
  const childPhase = typeof childParams.phase === "string" && childParams.phase.trim() ? childParams.phase.trim() : undefined;
4433
4571
  const childLabel = typeof childParams.label === "string" && childParams.label.trim() ? childParams.label.trim() : undefined;
4434
4572
  recordMissionWorkflowChild(missionBinding, _id, key, {
@@ -4464,7 +4602,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4464
4602
  if (childResult.savedOutputPath) producedChildOutputPaths.add(childResult.savedOutputPath);
4465
4603
  }
4466
4604
  if (result.details.asyncDir && missionBinding) writeMissionAsyncBinding(result.details.asyncDir, missionBinding);
4467
- const child = workflowChildResult(key, result);
4605
+ const child = workflowChildResult(key, result, childParams, deps.state);
4468
4606
  if (child.runId) workflowChildRunIds.set(key, child.runId);
4469
4607
  const childStatus = missionWorkflowChildStatus(result);
4470
4608
  recordMissionWorkflowChild(missionBinding, _id, key, {
@@ -4479,8 +4617,10 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4479
4617
  return child;
4480
4618
  },
4481
4619
  status: async (keyOrRunId, workflowSignal) => workflowChildResult(keyOrRunId, await execute(randomUUID(), { action: "status", id: keyOrRunId }, workflowSignal, undefined, ctx, preserveActiveSession)),
4620
+ resolveResume: (reference) => resolveKeyedWorkflowResume(reference, deps.state),
4482
4621
  steer: (key, message, options, workflowSignal) => steerWorkflowChildByKey({ state: deps.state, workflowRunId: _id, key, message, options, signal: workflowSignal, resolveRunId: () => workflowChildRunIds.get(key) }),
4483
4622
  });
4623
+ const receipt = terminalWorkflowReceipt(_id, "complete", workflow.children);
4484
4624
  const traceLines = workflow.trace.map((entry) => `- ${entry.operation} ${entry.key}: ${entry.state}${entry.runId ? ` (${entry.runId})` : ""}${entry.durationMs !== undefined ? ` in ${entry.durationMs}ms` : ""}${entry.error ? ` — ${entry.error}` : ""}`);
4485
4625
  const sections = ["Workflow completed.", `Return:\n${formatWorkflowValue(workflow.value)}`];
4486
4626
  if (workflow.emits.length > 0) sections.push(`Emitted:\n${workflow.emits.map(formatWorkflowValue).join("\n")}`);
@@ -4491,7 +4631,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4491
4631
  const displayText = appendWorkflowOutputWarning(workflowText, outputWarning);
4492
4632
  return attachWorkflowMission(withRunFanoutBudget({
4493
4633
  content: [{ type: "text", text: displayText }],
4494
- details: compactOptional<Details>({ mode: "workflow", runId: _id, results: workflow.children.flatMap((child) => (child.results ?? []) as SingleResult[]), totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { value: workflow.value, trace: workflow.trace, emits: workflow.emits, console: workflow.console }, chatProgress }),
4634
+ details: compactOptional<Details>({ mode: "workflow", runId: _id, results: workflow.children.flatMap((child) => (child.results ?? []) as SingleResult[]), totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { value: workflow.value, trace: workflow.trace, emits: workflow.emits, console: workflow.console, receipt }, chatProgress }),
4495
4635
  }, workflowFanoutBudget));
4496
4636
  } catch (error) {
4497
4637
  const partial = error instanceof WorkflowScriptError ? error.partial : { trace: [], emits: [], console: [], children: [] };
@@ -4504,10 +4644,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4504
4644
  const workflowText = sections.join("\n\n");
4505
4645
  const outputWarning = writeWorkflowAggregateOutput(workflowAggregateOutputPath, workflowText, producedChildOutputPaths);
4506
4646
  const displayText = appendWorkflowOutputWarning(workflowText, outputWarning);
4647
+ const receipt = terminalWorkflowReceipt(_id, "failed", partial.children);
4507
4648
  return attachWorkflowMission(withRunFanoutBudget({
4508
4649
  content: [{ type: "text", text: displayText }],
4509
4650
  isError: true,
4510
- details: compactOptional<Details>({ mode: "workflow", runId: _id, results: partial.children.flatMap((child) => (child.results ?? []) as SingleResult[]), totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { trace: partial.trace, emits: partial.emits, console: partial.console }, chatProgress }),
4651
+ details: compactOptional<Details>({ mode: "workflow", runId: _id, results: partial.children.flatMap((child) => (child.results ?? []) as SingleResult[]), totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { trace: partial.trace, emits: partial.emits, console: partial.console, receipt }, chatProgress }),
4511
4652
  }, workflowFanoutBudget));
4512
4653
  }
4513
4654
  }
@@ -4757,7 +4898,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4757
4898
  cwd: requestCwd,
4758
4899
  config: deps.config,
4759
4900
  state: deps.state,
4760
- context: paramsWithResolvedCwd.context,
4901
+ context: paramsWithResolvedCwd.context === "profile" ? undefined : paramsWithResolvedCwd.context,
4761
4902
  requestedSessionDir: paramsWithResolvedCwd.sessionDir,
4762
4903
  currentSessionFile,
4763
4904
  currentSessionId,
@@ -5071,6 +5212,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5071
5212
  cwd: requestCwd,
5072
5213
  config: deps.config,
5073
5214
  currentSessionId: deps.state.currentSessionId ?? ctx.sessionManager.getSessionId() ?? undefined,
5215
+ runtimeAgentOwner: deps.pi,
5074
5216
  });
5075
5217
  }
5076
5218
 
@@ -5127,23 +5269,25 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5127
5269
  // Prefer fork only when the parent session is persisted and has a current leaf;
5128
5270
  // otherwise use fresh immediately instead of launching a guaranteed-to-fail fork.
5129
5271
  // Explicit context:"fork" remains strict.
5130
- const contextPolicy = resolveAgentDefaultContextPolicy(
5272
+ const contextPolicyResult = resolveAgentDefaultContextPolicy(
5131
5273
  effectiveParams,
5132
5274
  discoveredAgents,
5133
5275
  deps.config.defaultSubagentContext,
5134
5276
  canPreferFork(ctx.sessionManager),
5135
5277
  );
5278
+ if ("error" in contextPolicyResult) return buildRequestedModeError(effectiveParams, contextPolicyResult.error);
5279
+ const contextPolicy = contextPolicyResult;
5136
5280
  effectiveParams = contextPolicy.params;
5137
5281
  const sessionName = resolveIntercomSessionTarget(deps.pi.getSessionName(), ctx.sessionManager.getSessionId());
5138
5282
  const intercomBridge = resolveIntercomBridge({
5139
5283
  config: deps.config.intercomBridge,
5140
5284
  override: effectiveParams.intercomBridge,
5141
- context: effectiveParams.context ?? (contextPolicy.usesFork ? "fork" : undefined),
5285
+ context: effectiveParams.context === "fresh" || effectiveParams.context === "fork"
5286
+ ? effectiveParams.context
5287
+ : contextPolicy.usesFork ? "fork" : undefined,
5142
5288
  orchestratorTarget: sessionName,
5143
5289
  });
5144
- const agents = intercomBridge.active
5145
- ? discoveredAgents.map((agent) => applyIntercomBridgeToAgent(agent, intercomBridge))
5146
- : discoveredAgents;
5290
+ const agents = applyScopedIntercomBridgeToAgents(discoveredAgents, intercomBridge, contextPolicy);
5147
5291
  const runId = randomUUID();
5148
5292
  const inheritedNestedRoute = resolveInheritedNestedRouteFromEnv();
5149
5293
  const nestedParentAddress = inheritedNestedRoute ? resolveNestedParentAddressFromEnv() : undefined;
@@ -5546,7 +5690,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5546
5690
  startedLaunches = selectedAgentNames.map((agent) => ({ agent }));
5547
5691
  }
5548
5692
  const agentsForSummary = startedLaunches.map((launch) => launch.agent);
5549
- const leafIntercomTarget = intercomBridge.active && agentsForSummary[0]
5693
+ const leafIntercomTarget = agentsForSummary[0] && intercomBridgeAppliesToAgent(intercomBridge, contextPolicy, agentsForSummary[0])
5550
5694
  ? resolveSubagentIntercomTarget(runId, agentsForSummary[0], 0)
5551
5695
  : undefined;
5552
5696
  try {