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.
- package/CHANGELOG.md +69 -0
- package/README.md +4 -0
- package/docs/configuration.md +12 -2
- package/docs/extension-api.md +3 -1
- package/docs/models.md +17 -3
- package/docs/workflows.md +2 -0
- package/index.ts +10 -1
- package/package.json +2 -1
- package/prompts/council.md +51 -0
- package/skills/council-mode/SKILL.md +231 -0
- package/skills/pi-subagents/SKILL.md +5 -1
- package/skills/pi-subagents/references/constraints-and-recipes.md +1 -0
- package/skills/pi-subagents/references/execution-controls.md +13 -0
- package/skills/pi-subagents/references/prompting-and-roles.md +7 -0
- package/src/agents/agent-management.ts +107 -8
- package/src/agents/agent-serializer.ts +2 -0
- package/src/agents/agents.ts +96 -37
- package/src/agents/builtin-names.ts +9 -0
- package/src/agents/runtime-agent-registry.ts +418 -0
- package/src/api/agents.ts +7 -0
- package/src/api/preflight.ts +8 -3
- package/src/extension/config.ts +3 -0
- package/src/extension/doctor.ts +11 -0
- package/src/extension/fanout-child.ts +3 -2
- package/src/extension/index.ts +20 -4
- package/src/extension/public-execution.ts +1 -1
- package/src/extension/rpc.ts +41 -1
- package/src/extension/schemas.ts +6 -3
- package/src/extension/tool-description.ts +2 -2
- package/src/extension/tool-result.ts +19 -0
- package/src/inspectors/herdr/client.ts +3 -3
- package/src/runs/background/async-execution.ts +12 -6
- package/src/runs/background/async-job-tracker.ts +4 -3
- package/src/runs/background/async-resume.ts +2 -1
- package/src/runs/background/async-retention.ts +1 -1
- package/src/runs/background/async-status-snapshot.ts +14 -5
- package/src/runs/background/auto-drain.ts +1 -0
- package/src/runs/background/chain-root-attachment.ts +5 -0
- package/src/runs/background/result-watcher.ts +9 -0
- package/src/runs/background/stale-run-reconciler.ts +3 -0
- package/src/runs/background/subagent-runner.ts +32 -5
- package/src/runs/background/subagent-wait.ts +9 -5
- package/src/runs/background/terminal-run-index.ts +15 -6
- package/src/runs/background/wait-completions.ts +2 -0
- package/src/runs/background/wait-tool.ts +5 -3
- package/src/runs/foreground/execution.ts +34 -2
- package/src/runs/foreground/subagent-executor.ts +196 -52
- package/src/runs/foreground/workflow-detach-reconcile.ts +83 -15
- package/src/runs/shared/acceptance.ts +44 -1
- package/src/runs/shared/model-exclusions.ts +242 -0
- package/src/runs/shared/model-fallback.ts +72 -16
- package/src/runs/shared/model-scope.ts +106 -39
- package/src/runs/shared/pi-args.ts +34 -1
- package/src/runs/shared/subagent-control.ts +25 -3
- package/src/runs/shared/subagent-prompt-runtime.ts +36 -9
- package/src/shared/fork-context.ts +17 -1
- package/src/shared/model-info.ts +20 -0
- package/src/shared/settings.ts +2 -2
- package/src/shared/types.ts +47 -2
- package/src/slash/slash-commands.ts +20 -6
- package/src/slash/slash-live-state.ts +3 -3
- package/src/tui/fleet-status.ts +86 -1
- package/src/tui/fleet.ts +55 -2
- package/src/tui/render.ts +73 -3
- package/src/watchdog/permission-arbiter.ts +59 -51
- package/src/workflows/scripted-workflow.ts +100 -12
- 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 {
|
|
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
|
|
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
|
|
802
|
-
const
|
|
803
|
-
|
|
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(
|
|
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:
|
|
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:
|
|
1934
|
-
childIntercomTarget:
|
|
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:
|
|
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
|
-
):
|
|
2252
|
-
if (params.context
|
|
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:
|
|
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
|
-
|
|
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
|
|
2814
|
-
const childIntercomTarget = intercomBridge
|
|
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,
|
|
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
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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(
|
|
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
|
-
...(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
5693
|
+
const leafIntercomTarget = agentsForSummary[0] && intercomBridgeAppliesToAgent(intercomBridge, contextPolicy, agentsForSummary[0])
|
|
5550
5694
|
? resolveSubagentIntercomTarget(runId, agentsForSummary[0], 0)
|
|
5551
5695
|
: undefined;
|
|
5552
5696
|
try {
|