pi-subagents 0.36.0 → 0.37.1
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 +38 -0
- package/README.md +111 -16
- package/agents/delegate.md +2 -0
- package/agents/planner.md +2 -1
- package/agents/worker.md +2 -0
- package/package.json +6 -4
- package/skills/pi-subagents/SKILL.md +18 -973
- package/skills/pi-subagents/references/constraints-and-recipes.md +256 -0
- package/skills/pi-subagents/references/execution-controls.md +411 -0
- package/skills/pi-subagents/references/management-authoring-rpc.md +140 -0
- package/skills/pi-subagents/references/prompting-and-roles.md +268 -0
- package/src/agents/agent-management.ts +7 -1
- package/src/agents/agents.ts +99 -12
- package/src/api/capability-ceiling.ts +17 -0
- package/src/api/delegation.ts +6 -1
- package/src/api/preflight.ts +399 -0
- package/src/extension/index.ts +16 -7
- package/src/extension/rpc.ts +29 -2
- package/src/extension/schemas.ts +10 -4
- package/src/extension/tool-description.ts +4 -2
- package/src/intercom/intercom-bridge.ts +5 -2
- package/src/runs/background/async-execution.ts +125 -14
- package/src/runs/background/async-job-tracker.ts +19 -12
- package/src/runs/background/async-resume.ts +27 -9
- package/src/runs/background/async-status.ts +18 -0
- package/src/runs/background/notify.ts +3 -0
- package/src/runs/background/process-terminal.ts +280 -0
- package/src/runs/background/result-watcher.ts +9 -3
- package/src/runs/background/run-status.ts +7 -1
- package/src/runs/background/scheduled-runs.ts +6 -1
- package/src/runs/background/stale-run-reconciler.ts +6 -0
- package/src/runs/background/subagent-runner.ts +182 -12
- package/src/runs/foreground/chain-execution.ts +5 -0
- package/src/runs/foreground/execution.ts +59 -13
- package/src/runs/foreground/subagent-executor.ts +142 -47
- package/src/runs/shared/acceptance.ts +41 -30
- package/src/runs/shared/capability-ceiling.ts +177 -0
- package/src/runs/shared/dynamic-fanout.ts +1 -1
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +56 -17
- package/src/runs/shared/model-fallback.ts +8 -0
- package/src/runs/shared/nested-events.ts +8 -1
- package/src/runs/shared/parallel-utils.ts +5 -0
- package/src/runs/shared/pi-args.ts +141 -58
- package/src/runs/shared/session-lease.ts +25 -5
- package/src/runs/shared/subagent-prompt-runtime.ts +14 -1
- package/src/runs/shared/task-intent.ts +1 -1
- package/src/runs/shared/tool-availability.ts +18 -2
- package/src/shared/launch-contract.ts +123 -0
- package/src/shared/types.ts +118 -8
- package/src/shared/utils.ts +17 -42
- package/src/slash/delegation-adapters.ts +6 -1
- package/src/slash/slash-commands.ts +1 -1
- package/src/tui/fleet-status.ts +62 -16
- package/src/tui/fleet.ts +1 -1
|
@@ -16,7 +16,7 @@ import { clearPendingForegroundControlNotices } from "../../extension/control-no
|
|
|
16
16
|
import { runSync } from "./execution.ts";
|
|
17
17
|
import { handleWatchdogToolAction, WATCHDOG_TOOL_ACTIONS } from "../../watchdog/tool-actions.ts";
|
|
18
18
|
import type { MainWatchdogRuntime } from "../../watchdog/runtime.ts";
|
|
19
|
-
import { buildModelCandidates, resolveEffectiveSubagentModel, resolveModelCandidate } from "../shared/model-fallback.ts";
|
|
19
|
+
import { buildModelCandidates, normalizeParentModel, resolveEffectiveSubagentModel, resolveModelCandidate, type ParentModel } from "../shared/model-fallback.ts";
|
|
20
20
|
import type { ModelScopeConfig } from "../shared/model-scope.ts";
|
|
21
21
|
import { aggregateParallelOutputs } from "../shared/parallel-utils.ts";
|
|
22
22
|
import { recordRun } from "../shared/run-history.ts";
|
|
@@ -47,6 +47,7 @@ import { formatControlIntercomMessage, formatControlNoticeMessage, resolveContro
|
|
|
47
47
|
import { resolveTurnBudgetConfig } from "../shared/turn-budget.ts";
|
|
48
48
|
import { formatSpawnBudget, getSpawnBudgetSnapshot, grantSpawnBudget, preflightSpawnBudget, preflightSpawnBudgetGrant, reserveSpawnBudget } from "../shared/spawn-budget.ts";
|
|
49
49
|
import { validateToolBudgetConfig } from "../shared/tool-budget.ts";
|
|
50
|
+
import { intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling, type ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
|
|
50
51
|
import { isAgentContractV1 } from "../shared/agent-contract.ts";
|
|
51
52
|
import { finalizeSingleOutput, injectSingleOutputInstruction, normalizeSingleOutputOverride, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
52
53
|
import { cleanupStructuredOutputRuntime, createStructuredOutputRuntime } from "../shared/structured-output.ts";
|
|
@@ -191,6 +192,15 @@ export interface SubagentParamsLike {
|
|
|
191
192
|
additional?: number;
|
|
192
193
|
}
|
|
193
194
|
|
|
195
|
+
function rememberParentModel(state: { currentSessionId?: string | null; lastParentModel?: ParentModel }, sessionId: string | null, model: unknown): ParentModel | undefined {
|
|
196
|
+
if (state.currentSessionId !== sessionId) state.lastParentModel = undefined;
|
|
197
|
+
state.currentSessionId = sessionId;
|
|
198
|
+
const parentModel = normalizeParentModel(model);
|
|
199
|
+
if (!sessionId) return parentModel;
|
|
200
|
+
if (parentModel) state.lastParentModel = parentModel;
|
|
201
|
+
return parentModel ?? state.lastParentModel;
|
|
202
|
+
}
|
|
203
|
+
|
|
194
204
|
interface ExecutorDeps {
|
|
195
205
|
pi: ExtensionAPI;
|
|
196
206
|
state: SubagentState;
|
|
@@ -239,7 +249,9 @@ interface ExecutionContextData {
|
|
|
239
249
|
configToolBudget?: ResolvedToolBudget;
|
|
240
250
|
contextPolicy: AgentDefaultContextPolicy;
|
|
241
251
|
modelScope?: ModelScopeConfig;
|
|
252
|
+
parentModel?: ParentModel;
|
|
242
253
|
parentSessionId: string | null;
|
|
254
|
+
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
243
255
|
}
|
|
244
256
|
|
|
245
257
|
function resolveRequestedCwd(runtimeCwd: string, requestedCwd: string | undefined): string {
|
|
@@ -405,11 +417,16 @@ function rememberForegroundRun(state: SubagentState, input: { runId: string; mod
|
|
|
405
417
|
...(result.savedOutputPath ? { savedOutputPath: result.savedOutputPath } : {}),
|
|
406
418
|
...(result.outputSaveError ? { outputSaveError: result.outputSaveError } : {}),
|
|
407
419
|
...(result.sessionFile ? { sessionFile: result.sessionFile } : {}),
|
|
420
|
+
...(result.model ? { model: result.model } : {}),
|
|
421
|
+
...(result.thinking ? { thinking: result.thinking } : {}),
|
|
408
422
|
...(result.artifactPaths ? { artifactPaths: result.artifactPaths } : {}),
|
|
409
423
|
...(result.transcriptPath ? { transcriptPath: result.transcriptPath } : {}),
|
|
410
424
|
...(result.transcriptError ? { transcriptError: result.transcriptError } : {}),
|
|
411
425
|
...(result.detachedReason ? { detachedReason: result.detachedReason } : {}),
|
|
412
426
|
...(result.acceptance ? { acceptance: result.acceptance } : {}),
|
|
427
|
+
...(result.launchContractDigest ? { launchContractDigest: result.launchContractDigest } : {}),
|
|
428
|
+
...(result.capabilityCeiling ? { capabilityCeiling: result.capabilityCeiling } : {}),
|
|
429
|
+
...(result.capabilityAudit ? { capabilityAudit: result.capabilityAudit } : {}),
|
|
413
430
|
};
|
|
414
431
|
const recovered = previous?.children[index];
|
|
415
432
|
return child.status === "detached" && recovered && recovered.status !== "detached" ? recovered : child;
|
|
@@ -468,11 +485,16 @@ function updateRememberedForegroundChild(state: SubagentState, input: { runId: s
|
|
|
468
485
|
savedOutputPath: input.result.savedOutputPath,
|
|
469
486
|
outputSaveError: input.result.outputSaveError,
|
|
470
487
|
...(input.result.sessionFile ? { sessionFile: input.result.sessionFile } : {}),
|
|
488
|
+
...(input.result.model ? { model: input.result.model } : {}),
|
|
489
|
+
...(input.result.thinking ? { thinking: input.result.thinking } : {}),
|
|
471
490
|
...(input.result.artifactPaths ? { artifactPaths: input.result.artifactPaths } : {}),
|
|
472
491
|
...(input.result.transcriptPath ? { transcriptPath: input.result.transcriptPath } : {}),
|
|
473
492
|
...(input.result.transcriptError ? { transcriptError: input.result.transcriptError } : {}),
|
|
474
493
|
...(input.result.detachedReason ? { detachedReason: input.result.detachedReason } : {}),
|
|
475
494
|
...(input.result.acceptance ? { acceptance: input.result.acceptance } : {}),
|
|
495
|
+
...(input.result.launchContractDigest ? { launchContractDigest: input.result.launchContractDigest } : {}),
|
|
496
|
+
...(input.result.capabilityCeiling ? { capabilityCeiling: input.result.capabilityCeiling } : {}),
|
|
497
|
+
...(input.result.capabilityAudit ? { capabilityAudit: input.result.capabilityAudit } : {}),
|
|
476
498
|
};
|
|
477
499
|
trimRememberedForegroundRuns(state);
|
|
478
500
|
const output = getSingleResultOutput(input.result).trim();
|
|
@@ -501,7 +523,7 @@ function updateRememberedForegroundChild(state: SubagentState, input: { runId: s
|
|
|
501
523
|
});
|
|
502
524
|
}
|
|
503
525
|
|
|
504
|
-
function resolveForegroundResumeTarget(params: SubagentParamsLike, state: SubagentState): { runId: string; mode: "single" | "parallel" | "chain"; state: "complete"; agent: string; index: number; cwd: string; sessionFile: string } | undefined {
|
|
526
|
+
function resolveForegroundResumeTarget(params: SubagentParamsLike, state: SubagentState): { runId: string; mode: "single" | "parallel" | "chain"; state: "complete"; agent: string; index: number; cwd: string; sessionFile: string; model?: string; thinking?: string; launchContractDigest?: string; capabilityCeiling?: ResolvedSubagentCapabilityCeiling } | undefined {
|
|
505
527
|
const requested = (params.id ?? params.runId)?.trim();
|
|
506
528
|
if (!requested || !state.foregroundRuns?.size || !state.currentSessionId) return undefined;
|
|
507
529
|
const sessionRuns = [...state.foregroundRuns.values()].filter((run) => run.sessionId === state.currentSessionId);
|
|
@@ -520,7 +542,19 @@ function resolveForegroundResumeTarget(params: SubagentParamsLike, state: Subage
|
|
|
520
542
|
if (path.extname(child.sessionFile) !== ".jsonl") throw new Error(`Foreground run '${run.runId}' child ${index} session file must be a .jsonl file: ${child.sessionFile}`);
|
|
521
543
|
const sessionFile = path.resolve(child.sessionFile);
|
|
522
544
|
if (!fs.existsSync(sessionFile)) throw new Error(`Foreground run '${run.runId}' child ${index} session file does not exist: ${child.sessionFile}`);
|
|
523
|
-
return {
|
|
545
|
+
return {
|
|
546
|
+
runId: run.runId,
|
|
547
|
+
mode: run.mode,
|
|
548
|
+
state: "complete",
|
|
549
|
+
agent: child.agent,
|
|
550
|
+
index,
|
|
551
|
+
cwd: run.cwd,
|
|
552
|
+
sessionFile,
|
|
553
|
+
...(child.model ? { model: child.model } : {}),
|
|
554
|
+
...(child.thinking ? { thinking: child.thinking } : {}),
|
|
555
|
+
...(child.launchContractDigest ? { launchContractDigest: child.launchContractDigest } : {}),
|
|
556
|
+
...(child.capabilityCeiling ? { capabilityCeiling: child.capabilityCeiling } : {}),
|
|
557
|
+
};
|
|
524
558
|
}
|
|
525
559
|
|
|
526
560
|
type AsyncResumeSourceTarget = ReturnType<typeof resolveAsyncResumeTarget> & { source: "async" };
|
|
@@ -534,6 +568,7 @@ type NestedResumeSourceTarget = {
|
|
|
534
568
|
index: number;
|
|
535
569
|
cwd?: string;
|
|
536
570
|
sessionFile: string;
|
|
571
|
+
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
537
572
|
};
|
|
538
573
|
type ResumeSourceTarget = AsyncResumeSourceTarget | ForegroundResumeSourceTarget | NestedResumeSourceTarget;
|
|
539
574
|
|
|
@@ -757,6 +792,7 @@ function appendStepToAsyncChain(input: {
|
|
|
757
792
|
requestCwd: string;
|
|
758
793
|
ctx: ExtensionContext;
|
|
759
794
|
deps: ExecutorDeps;
|
|
795
|
+
parentModel?: ParentModel;
|
|
760
796
|
}): AgentToolResult<Details> {
|
|
761
797
|
const targetRunId = input.params.id ?? input.params.runId;
|
|
762
798
|
if (!targetRunId) {
|
|
@@ -861,13 +897,14 @@ function appendStepToAsyncChain(input: {
|
|
|
861
897
|
const contextPolicy = resolveExplicitContextPolicy(input.params);
|
|
862
898
|
const chainSkillInput = normalizeSkillInput(input.params.skill);
|
|
863
899
|
const chainSkills = chainSkillInput === false ? [] : (chainSkillInput ?? []);
|
|
900
|
+
const parentModel = input.parentModel;
|
|
864
901
|
const asyncCtx = {
|
|
865
902
|
pi: input.deps.pi,
|
|
866
903
|
cwd: input.ctx.cwd,
|
|
867
904
|
currentSessionId: resolveCurrentSessionId(input.ctx.sessionManager),
|
|
868
905
|
parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
|
|
869
|
-
currentModelProvider:
|
|
870
|
-
currentModel:
|
|
906
|
+
currentModelProvider: parentModel?.provider,
|
|
907
|
+
currentModel: parentModel,
|
|
871
908
|
modelScope: discoveredForAppend.modelScope,
|
|
872
909
|
interactive: input.ctx.hasUI,
|
|
873
910
|
};
|
|
@@ -886,6 +923,7 @@ function appendStepToAsyncChain(input: {
|
|
|
886
923
|
contextForAgent: contextPolicy.contextForAgent,
|
|
887
924
|
asyncDir: resolved.location.asyncDir,
|
|
888
925
|
validateOutputBindings: false,
|
|
926
|
+
capabilityCeiling: intersectSubagentCapabilityCeilings(status.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(asyncCtx.currentSessionId)),
|
|
889
927
|
});
|
|
890
928
|
if ("error" in built) {
|
|
891
929
|
return {
|
|
@@ -990,6 +1028,7 @@ function resolveNestedResumeTarget(match: ResolvedSubagentRunId & { kind: "neste
|
|
|
990
1028
|
index: 0,
|
|
991
1029
|
cwd: asyncDir ? path.dirname(asyncDir) : undefined,
|
|
992
1030
|
sessionFile: validateNestedSessionFile(run, trustedSessionRoots),
|
|
1031
|
+
...(run.capabilityCeiling ? { capabilityCeiling: run.capabilityCeiling } : {}),
|
|
993
1032
|
};
|
|
994
1033
|
}
|
|
995
1034
|
|
|
@@ -1103,6 +1142,7 @@ async function resumeAsyncRun(input: {
|
|
|
1103
1142
|
requestCwd: string;
|
|
1104
1143
|
ctx: ExtensionContext;
|
|
1105
1144
|
deps: ExecutorDeps;
|
|
1145
|
+
parentModel?: ParentModel;
|
|
1106
1146
|
absoluteDeadlineAt?: number;
|
|
1107
1147
|
}): Promise<AgentToolResult<Details>> {
|
|
1108
1148
|
const followUp = (input.params.message ?? input.params.task ?? "").trim();
|
|
@@ -1114,6 +1154,13 @@ async function resumeAsyncRun(input: {
|
|
|
1114
1154
|
details: { mode: "management", results: [] },
|
|
1115
1155
|
};
|
|
1116
1156
|
}
|
|
1157
|
+
if (input.params.model !== undefined) {
|
|
1158
|
+
return {
|
|
1159
|
+
content: [{ type: "text", text: "action='resume' reuses the persisted child model and does not accept a model override." }],
|
|
1160
|
+
isError: true,
|
|
1161
|
+
details: { mode: "management", results: [] },
|
|
1162
|
+
};
|
|
1163
|
+
}
|
|
1117
1164
|
const acceptanceErrors = validateExecutionAcceptance(input.params);
|
|
1118
1165
|
if (acceptanceErrors.length > 0) {
|
|
1119
1166
|
return {
|
|
@@ -1241,6 +1288,7 @@ async function resumeAsyncRun(input: {
|
|
|
1241
1288
|
const goal = resolveAsyncEventGoal(workflowTask, attachChain);
|
|
1242
1289
|
const chain = wrapChainTasksForFork(attachChain, contextPolicy);
|
|
1243
1290
|
const normalized = normalizeSkillInput(input.params.skill);
|
|
1291
|
+
const parentModel = input.parentModel;
|
|
1244
1292
|
const result = executeAsyncChain(runId, {
|
|
1245
1293
|
chain,
|
|
1246
1294
|
task: workflowTask,
|
|
@@ -1259,8 +1307,8 @@ async function resumeAsyncRun(input: {
|
|
|
1259
1307
|
cwd: input.requestCwd,
|
|
1260
1308
|
currentSessionId: input.deps.state.currentSessionId,
|
|
1261
1309
|
parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
|
|
1262
|
-
currentModelProvider:
|
|
1263
|
-
currentModel:
|
|
1310
|
+
currentModelProvider: parentModel?.provider,
|
|
1311
|
+
currentModel: parentModel,
|
|
1264
1312
|
modelScope,
|
|
1265
1313
|
interactive: input.ctx.hasUI,
|
|
1266
1314
|
},
|
|
@@ -1283,6 +1331,7 @@ async function resumeAsyncRun(input: {
|
|
|
1283
1331
|
controlIntercomTarget: intercomBridge.active ? intercomBridge.orchestratorTarget : undefined,
|
|
1284
1332
|
childIntercomTarget: intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(runId, agent, index) : undefined,
|
|
1285
1333
|
globalConcurrencyLimit: input.deps.config.globalConcurrencyLimit,
|
|
1334
|
+
capabilityCeiling: intersectSubagentCapabilityCeilings("capabilityCeiling" in target ? target.capabilityCeiling : undefined, resolveCurrentSubagentCapabilityCeiling(input.deps.state.currentSessionId)),
|
|
1286
1335
|
});
|
|
1287
1336
|
if (result.isError) return result;
|
|
1288
1337
|
const attachedId = result.details.asyncId ?? runId;
|
|
@@ -1301,6 +1350,7 @@ async function resumeAsyncRun(input: {
|
|
|
1301
1350
|
const artifactConfig: ArtifactConfig = recoveryDescriptor?.artifactConfig ?? { ...DEFAULT_ARTIFACT_CONFIG, enabled: input.params.artifacts !== false, dir: input.deps.config.artifactDir ?? DEFAULT_ARTIFACT_CONFIG.dir };
|
|
1302
1351
|
const artifactsDir = recoveryDescriptor?.artifactsDir ?? getArtifactsDir(parentSessionFile, effectiveCwd, artifactConfig.dir);
|
|
1303
1352
|
const availableModels = input.ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
1353
|
+
const parentModel = input.parentModel;
|
|
1304
1354
|
const result = executeAsyncSingle(runId, {
|
|
1305
1355
|
agent: target.agent,
|
|
1306
1356
|
task: buildRevivedAsyncTask(target, followUp),
|
|
@@ -1311,8 +1361,8 @@ async function resumeAsyncRun(input: {
|
|
|
1311
1361
|
cwd: input.requestCwd,
|
|
1312
1362
|
currentSessionId: input.deps.state.currentSessionId,
|
|
1313
1363
|
parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
|
|
1314
|
-
currentModelProvider:
|
|
1315
|
-
currentModel:
|
|
1364
|
+
currentModelProvider: parentModel?.provider,
|
|
1365
|
+
currentModel: parentModel,
|
|
1316
1366
|
modelScope,
|
|
1317
1367
|
interactive: input.ctx.hasUI,
|
|
1318
1368
|
},
|
|
@@ -1330,8 +1380,8 @@ async function resumeAsyncRun(input: {
|
|
|
1330
1380
|
sourceRunId: target.runId,
|
|
1331
1381
|
...(input.deps.state.currentSessionId ? { parentSessionId: input.deps.state.currentSessionId } : {}),
|
|
1332
1382
|
},
|
|
1333
|
-
modelOverride:
|
|
1334
|
-
thinkingOverride:
|
|
1383
|
+
modelOverride: recoveryDescriptor?.model ?? target.model,
|
|
1384
|
+
thinkingOverride: recoveryDescriptor?.thinking ?? target.thinking,
|
|
1335
1385
|
outputBaseDir: resolveSingleRunOutputBaseDir(input.deps, artifactsDir, runId),
|
|
1336
1386
|
maxSubagentDepth: recoveryDescriptor?.maxSubagentDepth ?? resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
|
|
1337
1387
|
waitToolEnabled: input.deps.waitToolEnabled,
|
|
@@ -1342,8 +1392,8 @@ async function resumeAsyncRun(input: {
|
|
|
1342
1392
|
controlIntercomTarget: intercomBridge.active ? intercomBridge.orchestratorTarget : undefined,
|
|
1343
1393
|
childIntercomTarget: intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(runId, agent, index) : undefined,
|
|
1344
1394
|
availableModels,
|
|
1345
|
-
output: recoveryDescriptor?.outputPath,
|
|
1346
|
-
outputMode: recoveryDescriptor?.outputMode,
|
|
1395
|
+
output: typeof input.params.output === "string" ? input.params.output : recoveryDescriptor?.outputPath,
|
|
1396
|
+
outputMode: input.params.outputMode ?? recoveryDescriptor?.outputMode,
|
|
1347
1397
|
...(recoveryDescriptor?.agentContract ? { agentContract: recoveryDescriptor.agentContract } : {}),
|
|
1348
1398
|
...(recoveryDescriptor?.structuredOutputSchema ? { structuredOutputSchema: recoveryDescriptor.structuredOutputSchema } : {}),
|
|
1349
1399
|
...(recoveryDescriptor?.skills ? { skills: [...recoveryDescriptor.skills] } : {}),
|
|
@@ -1352,6 +1402,7 @@ async function resumeAsyncRun(input: {
|
|
|
1352
1402
|
...(input.absoluteDeadlineAt !== undefined ? { absoluteDeadlineAt: input.absoluteDeadlineAt } : {}),
|
|
1353
1403
|
...(input.params.turnBudget !== undefined ? { turnBudget: input.params.turnBudget } : {}),
|
|
1354
1404
|
...(input.params.toolBudget !== undefined ? { toolBudget: input.params.toolBudget } : {}),
|
|
1405
|
+
capabilityCeiling: intersectSubagentCapabilityCeilings("capabilityCeiling" in target ? target.capabilityCeiling : undefined, recoveryDescriptor?.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(input.deps.state.currentSessionId)),
|
|
1355
1406
|
});
|
|
1356
1407
|
if (result.isError) return result;
|
|
1357
1408
|
|
|
@@ -1367,7 +1418,13 @@ async function resumeAsyncRun(input: {
|
|
|
1367
1418
|
revivedTarget ? `Intercom target: ${revivedTarget} (if registered)` : undefined,
|
|
1368
1419
|
`Status if needed: subagent({ action: "status", id: "${revivedId}" })`,
|
|
1369
1420
|
].filter((line): line is string => Boolean(line));
|
|
1370
|
-
return {
|
|
1421
|
+
return {
|
|
1422
|
+
content: [{ type: "text", text: formatAsyncStartedMessage(lines.join("\n"), input.ctx.hasUI) }],
|
|
1423
|
+
details: {
|
|
1424
|
+
...result.details,
|
|
1425
|
+
...(target.launchContractDigest ? { sourceLaunchContractDigest: target.launchContractDigest } : {}),
|
|
1426
|
+
},
|
|
1427
|
+
};
|
|
1371
1428
|
}
|
|
1372
1429
|
|
|
1373
1430
|
function resultSummaryForIntercom(result: SingleResult): string {
|
|
@@ -1413,7 +1470,7 @@ async function emitForegroundResultIntercom(input: {
|
|
|
1413
1470
|
nestedChildren?: NestedRunSummary[];
|
|
1414
1471
|
parallelHandoff?: Details["parallelHandoff"];
|
|
1415
1472
|
}): Promise<ReturnType<typeof buildSubagentResultIntercomPayload> | null> {
|
|
1416
|
-
if (!input.intercomBridge.active || !input.intercomBridge.orchestratorTarget) return null;
|
|
1473
|
+
if (!input.intercomBridge.active || !input.intercomBridge.resultDelivery || !input.intercomBridge.orchestratorTarget) return null;
|
|
1417
1474
|
const children = input.results.flatMap((result, index) => result.detached ? [] : [{
|
|
1418
1475
|
agent: result.agent,
|
|
1419
1476
|
status: resolveSubagentResultStatus({
|
|
@@ -1679,10 +1736,14 @@ function applySingleAgentLaunchDefaults(params: SubagentParamsLike, agents: Agen
|
|
|
1679
1736
|
};
|
|
1680
1737
|
}
|
|
1681
1738
|
|
|
1682
|
-
|
|
1739
|
+
export const DEFAULT_FOREGROUND_TIMEOUT_MS = 30 * 60 * 1000;
|
|
1740
|
+
|
|
1741
|
+
function resolveForegroundTimeout(params: SubagentParamsLike, defaultTimeoutMs?: number): { timeoutMs?: number; error?: string } {
|
|
1683
1742
|
const rawTimeout = params.timeoutMs;
|
|
1684
1743
|
const rawMaxRuntime = params.maxRuntimeMs;
|
|
1685
|
-
if (rawTimeout === undefined && rawMaxRuntime === undefined)
|
|
1744
|
+
if (rawTimeout === undefined && rawMaxRuntime === undefined) {
|
|
1745
|
+
return defaultTimeoutMs === undefined ? {} : { timeoutMs: defaultTimeoutMs };
|
|
1746
|
+
}
|
|
1686
1747
|
for (const [name, value] of [["timeoutMs", rawTimeout], ["maxRuntimeMs", rawMaxRuntime]] as const) {
|
|
1687
1748
|
if (value === undefined) continue;
|
|
1688
1749
|
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
|
|
@@ -2024,19 +2085,20 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2024
2085
|
};
|
|
2025
2086
|
}
|
|
2026
2087
|
const id = randomUUID();
|
|
2088
|
+
const parentModel = data.parentModel;
|
|
2027
2089
|
const asyncCtx = {
|
|
2028
2090
|
pi: deps.pi,
|
|
2029
2091
|
cwd: ctx.cwd,
|
|
2030
2092
|
currentSessionId: deps.state.currentSessionId!,
|
|
2031
2093
|
parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
|
|
2032
|
-
currentModelProvider:
|
|
2033
|
-
currentModel:
|
|
2094
|
+
currentModelProvider: parentModel?.provider,
|
|
2095
|
+
currentModel: parentModel,
|
|
2034
2096
|
modelScope: data.modelScope,
|
|
2035
2097
|
interactive: ctx.hasUI,
|
|
2036
2098
|
};
|
|
2037
2099
|
const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
2038
2100
|
const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth);
|
|
2039
|
-
const currentProvider =
|
|
2101
|
+
const currentProvider = parentModel?.provider;
|
|
2040
2102
|
const controlIntercomTarget = intercomBridge.active ? intercomBridge.orchestratorTarget : undefined;
|
|
2041
2103
|
const childIntercomTarget = intercomBridge.active ? (agent: string, index: number) => resolveSubagentIntercomTarget(id, agent, index) : undefined;
|
|
2042
2104
|
|
|
@@ -2092,6 +2154,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2092
2154
|
turnBudget: data.turnBudget,
|
|
2093
2155
|
toolBudget: data.toolBudget,
|
|
2094
2156
|
configToolBudget: data.configToolBudget,
|
|
2157
|
+
capabilityCeiling: data.capabilityCeiling,
|
|
2095
2158
|
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
2096
2159
|
});
|
|
2097
2160
|
}
|
|
@@ -2133,6 +2196,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2133
2196
|
turnBudget: data.turnBudget,
|
|
2134
2197
|
toolBudget: data.toolBudget,
|
|
2135
2198
|
configToolBudget: data.configToolBudget,
|
|
2199
|
+
capabilityCeiling: data.capabilityCeiling,
|
|
2136
2200
|
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
2137
2201
|
});
|
|
2138
2202
|
}
|
|
@@ -2152,7 +2216,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2152
2216
|
const normalizedSkills = normalizeSkillInput(params.skill);
|
|
2153
2217
|
const skills = normalizedSkills === false ? [] : normalizedSkills;
|
|
2154
2218
|
const maxSubagentDepth = resolveChildMaxSubagentDepth(currentMaxSubagentDepth, a.maxSubagentDepth);
|
|
2155
|
-
const modelOverride = resolveEffectiveSubagentModel(params.model as string | undefined, a.model,
|
|
2219
|
+
const modelOverride = resolveEffectiveSubagentModel(params.model as string | undefined, a.model, parentModel, availableModels, currentProvider, { scope: data.modelScope });
|
|
2156
2220
|
return executeAsyncSingle(id, {
|
|
2157
2221
|
agent: params.agent!,
|
|
2158
2222
|
task: shouldForkAgent(contextPolicy, params.agent!) ? wrapForkTask(params.task ?? "") : (params.task ?? ""),
|
|
@@ -2190,6 +2254,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2190
2254
|
turnBudget: data.turnBudget,
|
|
2191
2255
|
toolBudget: data.toolBudget,
|
|
2192
2256
|
configToolBudget: data.configToolBudget,
|
|
2257
|
+
capabilityCeiling: data.capabilityCeiling,
|
|
2193
2258
|
});
|
|
2194
2259
|
}
|
|
2195
2260
|
|
|
@@ -2223,11 +2288,12 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
2223
2288
|
const chainSkills = normalized === false ? [] : (normalized ?? []);
|
|
2224
2289
|
const chain = wrapChainTasksForFork(params.chain as ChainStep[], contextPolicy);
|
|
2225
2290
|
const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth);
|
|
2291
|
+
const chainCtx = normalizeParentModel(ctx.model) || !data.parentModel ? ctx : { ...ctx, model: data.parentModel };
|
|
2226
2292
|
const chainResult = await executeChain({
|
|
2227
2293
|
chain,
|
|
2228
2294
|
task: params.task,
|
|
2229
2295
|
agents,
|
|
2230
|
-
ctx,
|
|
2296
|
+
ctx: chainCtx,
|
|
2231
2297
|
modelScope: data.modelScope,
|
|
2232
2298
|
intercomEvents: deps.pi.events,
|
|
2233
2299
|
signal,
|
|
@@ -2265,6 +2331,7 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
2265
2331
|
toolBudget: data.toolBudget,
|
|
2266
2332
|
configToolBudget: data.configToolBudget,
|
|
2267
2333
|
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
2334
|
+
capabilityCeiling: data.capabilityCeiling,
|
|
2268
2335
|
});
|
|
2269
2336
|
|
|
2270
2337
|
if (chainResult.requestedAsync) {
|
|
@@ -2276,13 +2343,14 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
2276
2343
|
};
|
|
2277
2344
|
}
|
|
2278
2345
|
const id = randomUUID();
|
|
2346
|
+
const parentModel = data.parentModel;
|
|
2279
2347
|
const asyncCtx = {
|
|
2280
2348
|
pi: deps.pi,
|
|
2281
2349
|
cwd: ctx.cwd,
|
|
2282
2350
|
currentSessionId: deps.state.currentSessionId!,
|
|
2283
2351
|
parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
|
|
2284
|
-
currentModelProvider:
|
|
2285
|
-
currentModel:
|
|
2352
|
+
currentModelProvider: parentModel?.provider,
|
|
2353
|
+
currentModel: parentModel,
|
|
2286
2354
|
modelScope: data.modelScope,
|
|
2287
2355
|
interactive: ctx.hasUI,
|
|
2288
2356
|
};
|
|
@@ -2321,11 +2389,12 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
2321
2389
|
turnBudget: data.turnBudget,
|
|
2322
2390
|
toolBudget: data.toolBudget,
|
|
2323
2391
|
configToolBudget: data.configToolBudget,
|
|
2392
|
+
capabilityCeiling: data.capabilityCeiling,
|
|
2324
2393
|
globalConcurrencyLimit: deps.config.globalConcurrencyLimit,
|
|
2325
2394
|
});
|
|
2326
2395
|
}
|
|
2327
2396
|
|
|
2328
|
-
const rawChainDetails = chainResult.details ? { ...chainResult.details, runId } : undefined;
|
|
2397
|
+
const rawChainDetails = chainResult.details ? { ...chainResult.details, runId, timeoutMs: data.timeoutMs } : undefined;
|
|
2329
2398
|
if (foregroundControl && rawChainDetails) {
|
|
2330
2399
|
updateForegroundNestedProjection(foregroundControl);
|
|
2331
2400
|
attachRootChildrenToSteps(runId, rawChainDetails.results, foregroundControl.nestedChildren);
|
|
@@ -2363,6 +2432,7 @@ interface ForegroundParallelRunInput {
|
|
|
2363
2432
|
state: SubagentState;
|
|
2364
2433
|
intercomEvents: IntercomEventBus;
|
|
2365
2434
|
parentSessionId: string | null;
|
|
2435
|
+
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
2366
2436
|
signal: AbortSignal;
|
|
2367
2437
|
runId: string;
|
|
2368
2438
|
sessionDirForIndex: (idx?: number) => string | undefined;
|
|
@@ -2380,6 +2450,7 @@ interface ForegroundParallelRunInput {
|
|
|
2380
2450
|
waitToolEnabled?: boolean;
|
|
2381
2451
|
availableModels: ModelInfo[];
|
|
2382
2452
|
modelScope?: ModelScopeConfig;
|
|
2453
|
+
parentModel?: ParentModel;
|
|
2383
2454
|
modelOverrides: (string | undefined)[];
|
|
2384
2455
|
behaviors: Array<ReturnType<typeof resolveStepBehavior>>;
|
|
2385
2456
|
firstProgressIndex: number;
|
|
@@ -2605,6 +2676,7 @@ async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Pr
|
|
|
2605
2676
|
outputMode: behavior?.outputMode,
|
|
2606
2677
|
maxSubagentDepth: input.maxSubagentDepths[index],
|
|
2607
2678
|
waitToolEnabled: input.waitToolEnabled,
|
|
2679
|
+
capabilityCeiling: input.capabilityCeiling,
|
|
2608
2680
|
controlConfig: input.controlConfig,
|
|
2609
2681
|
onControlEvent: input.onControlEvent,
|
|
2610
2682
|
onDetachedExit: (result) => updateRememberedForegroundChild(input.state, { runId: input.runId, mode: "parallel", cwd: taskCwd, sessionId: input.parentSessionId, index, result, events: input.intercomEvents }),
|
|
@@ -2614,7 +2686,7 @@ async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Pr
|
|
|
2614
2686
|
modelOverride: input.modelOverrides[index],
|
|
2615
2687
|
thinkingOverride: input.thinkingOverrideForTask(task.agent, index, input.modelOverrides[index]),
|
|
2616
2688
|
availableModels: input.availableModels,
|
|
2617
|
-
preferredModelProvider: input.
|
|
2689
|
+
preferredModelProvider: input.parentModel?.provider,
|
|
2618
2690
|
modelScope: input.modelScope,
|
|
2619
2691
|
skills: effectiveSkills === false ? [] : effectiveSkills,
|
|
2620
2692
|
structuredOutput: structuredRuntime,
|
|
@@ -2719,7 +2791,8 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2719
2791
|
if (worktreeTaskCwdError) return buildParallelModeError(worktreeTaskCwdError);
|
|
2720
2792
|
}
|
|
2721
2793
|
|
|
2722
|
-
const
|
|
2794
|
+
const parentModel = data.parentModel;
|
|
2795
|
+
const currentProvider = parentModel?.provider;
|
|
2723
2796
|
const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
2724
2797
|
let taskTexts = tasks.map((t) => t.task);
|
|
2725
2798
|
const skillOverrides: (string[] | false | undefined)[] = tasks.map((t) =>
|
|
@@ -2734,7 +2807,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2734
2807
|
...(task.model !== undefined ? { model: task.model } : {}),
|
|
2735
2808
|
}));
|
|
2736
2809
|
const modelOverrides: (string | undefined)[] = tasks.map((_, i) =>
|
|
2737
|
-
resolveEffectiveSubagentModel(behaviorOverrides[i]?.model, agentConfigs[i]?.model,
|
|
2810
|
+
resolveEffectiveSubagentModel(behaviorOverrides[i]?.model, agentConfigs[i]?.model, parentModel, availableModels, currentProvider, { scope: data.modelScope }),
|
|
2738
2811
|
);
|
|
2739
2812
|
|
|
2740
2813
|
if (params.clarify === true && ctx.hasUI) {
|
|
@@ -2769,7 +2842,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2769
2842
|
for (let i = 0; i < result.behaviorOverrides.length; i++) {
|
|
2770
2843
|
const override = result.behaviorOverrides[i];
|
|
2771
2844
|
if (override?.model !== undefined) {
|
|
2772
|
-
modelOverrides[i] = resolveEffectiveSubagentModel(override.model, agentConfigs[i]?.model,
|
|
2845
|
+
modelOverrides[i] = resolveEffectiveSubagentModel(override.model, agentConfigs[i]?.model, parentModel, availableModels, currentProvider, { scope: data.modelScope });
|
|
2773
2846
|
behaviorOverrides[i]!.model = override.model;
|
|
2774
2847
|
}
|
|
2775
2848
|
if (override?.output !== undefined) behaviorOverrides[i]!.output = override.output;
|
|
@@ -2795,8 +2868,8 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2795
2868
|
cwd: ctx.cwd,
|
|
2796
2869
|
currentSessionId: deps.state.currentSessionId!,
|
|
2797
2870
|
parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
|
|
2798
|
-
currentModelProvider:
|
|
2799
|
-
currentModel:
|
|
2871
|
+
currentModelProvider: parentModel?.provider,
|
|
2872
|
+
currentModel: parentModel,
|
|
2800
2873
|
modelScope: data.modelScope,
|
|
2801
2874
|
interactive: ctx.hasUI,
|
|
2802
2875
|
};
|
|
@@ -2915,6 +2988,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2915
2988
|
state: deps.state,
|
|
2916
2989
|
intercomEvents: deps.pi.events,
|
|
2917
2990
|
parentSessionId: data.parentSessionId,
|
|
2991
|
+
capabilityCeiling: data.capabilityCeiling,
|
|
2918
2992
|
signal,
|
|
2919
2993
|
runId,
|
|
2920
2994
|
sessionDirForIndex,
|
|
@@ -2930,6 +3004,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2930
3004
|
progressDir: parallelProgressDir,
|
|
2931
3005
|
availableModels,
|
|
2932
3006
|
modelScope: data.modelScope,
|
|
3007
|
+
parentModel,
|
|
2933
3008
|
modelOverrides,
|
|
2934
3009
|
behaviors,
|
|
2935
3010
|
firstProgressIndex: parallelProgressPrecreated ? -1 : firstProgressIndex,
|
|
@@ -2976,6 +3051,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2976
3051
|
const details = compactForegroundDetails({
|
|
2977
3052
|
mode: "parallel",
|
|
2978
3053
|
runId,
|
|
3054
|
+
timeoutMs: data.timeoutMs,
|
|
2979
3055
|
results,
|
|
2980
3056
|
progress: params.includeProgress ? allProgress : undefined,
|
|
2981
3057
|
artifacts: allArtifactPaths.length ? { dir: artifactsDir, files: allArtifactPaths } : undefined,
|
|
@@ -3077,13 +3153,14 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3077
3153
|
const effectiveToolBudget = resolveEffectiveToolBudget({ runBudget: data.toolBudget, agentBudget: agentConfig.toolBudget, configBudget: data.configToolBudget });
|
|
3078
3154
|
if (effectiveToolBudget.error) return toExecutionErrorResult(params, new Error(effectiveToolBudget.error), data.contextPolicy.contextSummary);
|
|
3079
3155
|
|
|
3080
|
-
const
|
|
3156
|
+
const parentModel = data.parentModel;
|
|
3157
|
+
const currentProvider = parentModel?.provider;
|
|
3081
3158
|
const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
3082
3159
|
let task = params.task ?? "";
|
|
3083
3160
|
let modelOverride: string | undefined = resolveEffectiveSubagentModel(
|
|
3084
3161
|
params.model as string | undefined,
|
|
3085
3162
|
agentConfig.model,
|
|
3086
|
-
|
|
3163
|
+
parentModel,
|
|
3087
3164
|
availableModels,
|
|
3088
3165
|
currentProvider,
|
|
3089
3166
|
{ scope: data.modelScope },
|
|
@@ -3123,7 +3200,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3123
3200
|
|
|
3124
3201
|
task = result.templates[0]!;
|
|
3125
3202
|
const override = result.behaviorOverrides[0];
|
|
3126
|
-
if (override?.model !== undefined) modelOverride = resolveEffectiveSubagentModel(override.model, agentConfig.model,
|
|
3203
|
+
if (override?.model !== undefined) modelOverride = resolveEffectiveSubagentModel(override.model, agentConfig.model, parentModel, availableModels, currentProvider, { scope: data.modelScope });
|
|
3127
3204
|
if (override?.output !== undefined) effectiveOutput = normalizeSingleOutputOverride(override.output, agentConfig.output);
|
|
3128
3205
|
if (override?.skills !== undefined) skillOverride = override.skills;
|
|
3129
3206
|
|
|
@@ -3141,8 +3218,8 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3141
3218
|
cwd: ctx.cwd,
|
|
3142
3219
|
currentSessionId: deps.state.currentSessionId!,
|
|
3143
3220
|
parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
|
|
3144
|
-
currentModelProvider:
|
|
3145
|
-
currentModel:
|
|
3221
|
+
currentModelProvider: parentModel?.provider,
|
|
3222
|
+
currentModel: parentModel,
|
|
3146
3223
|
modelScope: data.modelScope,
|
|
3147
3224
|
interactive: ctx.hasUI,
|
|
3148
3225
|
};
|
|
@@ -3274,6 +3351,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3274
3351
|
turnBudget: data.turnBudget,
|
|
3275
3352
|
enforceHardTurnLimit: params.enforceHardTurnLimit,
|
|
3276
3353
|
toolBudget: effectiveToolBudget.toolBudget,
|
|
3354
|
+
capabilityCeiling: data.capabilityCeiling,
|
|
3277
3355
|
allowZeroToolBudget: data.allowZeroToolBudget && effectiveToolBudget.toolBudget === data.toolBudget,
|
|
3278
3356
|
});
|
|
3279
3357
|
} finally {
|
|
@@ -3303,6 +3381,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3303
3381
|
const details = compactForegroundDetails({
|
|
3304
3382
|
mode: "single",
|
|
3305
3383
|
runId,
|
|
3384
|
+
timeoutMs: data.timeoutMs,
|
|
3306
3385
|
results: [r],
|
|
3307
3386
|
...(data.turnBudget ? { turnBudget: data.turnBudget } : {}),
|
|
3308
3387
|
...(effectiveToolBudget.toolBudget ? { toolBudget: effectiveToolBudget.toolBudget } : {}),
|
|
@@ -3431,6 +3510,13 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3431
3510
|
const requestCwd = resolveRequestedCwd(ctx.cwd, requestParams.cwd);
|
|
3432
3511
|
const paramsWithResolvedCwd = requestParams.cwd === undefined ? requestParams : { ...requestParams, cwd: requestCwd };
|
|
3433
3512
|
const action = paramsWithResolvedCwd.action;
|
|
3513
|
+
let requestParentModel: ParentModel | undefined;
|
|
3514
|
+
try {
|
|
3515
|
+
requestParentModel = rememberParentModel(deps.state, resolveCurrentSessionId(ctx.sessionManager), ctx.model);
|
|
3516
|
+
} catch (error) {
|
|
3517
|
+
if (action?.toLowerCase() !== "doctor") throw error;
|
|
3518
|
+
requestParentModel = normalizeParentModel(ctx.model);
|
|
3519
|
+
}
|
|
3434
3520
|
if (action) {
|
|
3435
3521
|
if ((WATCHDOG_TOOL_ACTIONS as readonly string[]).includes(action)) {
|
|
3436
3522
|
if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
|
|
@@ -3588,7 +3674,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3588
3674
|
return withBudget(inspectSubagentStatus(paramsWithResolvedCwd, { state: deps.state, nested: nestedScope, sessionRoots }));
|
|
3589
3675
|
}
|
|
3590
3676
|
if (action === "resume") {
|
|
3591
|
-
return resumeAsyncRun({ params: paramsWithResolvedCwd, requestCwd, ctx, deps });
|
|
3677
|
+
return resumeAsyncRun({ params: paramsWithResolvedCwd, requestCwd, ctx, deps, parentModel: requestParentModel });
|
|
3592
3678
|
}
|
|
3593
3679
|
if (action === "steer") {
|
|
3594
3680
|
deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
@@ -3611,7 +3697,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3611
3697
|
? {}
|
|
3612
3698
|
: {
|
|
3613
3699
|
recover: ({ absoluteDeadlineAt, ...limits }) =>
|
|
3614
|
-
resumeAsyncRun({ params: { ...limits, action: "resume", id: runId, message }, requestCwd, ctx, deps, absoluteDeadlineAt }),
|
|
3700
|
+
resumeAsyncRun({ params: { ...limits, action: "resume", id: runId, message }, requestCwd, ctx, deps, parentModel: requestParentModel, absoluteDeadlineAt }),
|
|
3615
3701
|
}
|
|
3616
3702
|
),
|
|
3617
3703
|
});
|
|
@@ -3648,6 +3734,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3648
3734
|
requestCwd,
|
|
3649
3735
|
ctx,
|
|
3650
3736
|
deps,
|
|
3737
|
+
parentModel: requestParentModel,
|
|
3651
3738
|
absoluteDeadlineAt,
|
|
3652
3739
|
}),
|
|
3653
3740
|
}
|
|
@@ -3655,7 +3742,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3655
3742
|
});
|
|
3656
3743
|
}
|
|
3657
3744
|
if (action === "append-step") {
|
|
3658
|
-
return appendStepToAsyncChain({ params: paramsWithResolvedCwd, requestCwd, ctx, deps });
|
|
3745
|
+
return appendStepToAsyncChain({ params: paramsWithResolvedCwd, requestCwd, ctx, deps, parentModel: requestParentModel });
|
|
3659
3746
|
}
|
|
3660
3747
|
if (action === "schedule" || action === "schedule-list" || action === "schedule-status" || action === "schedule-cancel") {
|
|
3661
3748
|
if (!deps.handleScheduledRunAction) {
|
|
@@ -3802,13 +3889,10 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3802
3889
|
const scope: AgentScope = resolveExecutionAgentScope(effectiveParams.agentScope);
|
|
3803
3890
|
const effectiveCwd = effectiveParams.cwd ?? ctx.cwd;
|
|
3804
3891
|
const parentSessionFile = ctx.sessionManager.getSessionFile() ?? null;
|
|
3805
|
-
deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
3806
3892
|
const discovered = deps.discoverAgents(effectiveCwd, scope);
|
|
3807
3893
|
const discoveredAgents = discovered.agents;
|
|
3808
3894
|
const modelScope = discovered.modelScope;
|
|
3809
3895
|
effectiveParams = applySingleAgentLaunchDefaults(effectiveParams, discoveredAgents);
|
|
3810
|
-
const foregroundTimeout = resolveForegroundTimeout(effectiveParams);
|
|
3811
|
-
if (foregroundTimeout.error) return buildRequestedModeError(effectiveParams, foregroundTimeout.error);
|
|
3812
3896
|
const turnBudget = resolveTurnBudgetConfig(effectiveParams.turnBudget ?? deps.config.turnBudget);
|
|
3813
3897
|
if (turnBudget.error) return buildRequestedModeError(effectiveParams, turnBudget.error);
|
|
3814
3898
|
const contextPolicy = resolveAgentDefaultContextPolicy(effectiveParams, discoveredAgents);
|
|
@@ -3862,25 +3946,26 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3862
3946
|
const forkThinkingDowngrades = new Map<number, string>();
|
|
3863
3947
|
try {
|
|
3864
3948
|
const forkAvailableModels = contextPolicy.usesFork ? ctx.modelRegistry.getAvailable().map(toModelInfo) : [];
|
|
3949
|
+
const parentModel = requestParentModel;
|
|
3865
3950
|
prepareForkThinking = (agentName, index, modelOverride) => {
|
|
3866
3951
|
const agentConfig = agents.find((agent) => agent.name === agentName);
|
|
3867
3952
|
const primaryModel = resolveEffectiveSubagentModel(
|
|
3868
3953
|
modelOverride,
|
|
3869
3954
|
agentConfig?.model,
|
|
3870
|
-
|
|
3955
|
+
parentModel,
|
|
3871
3956
|
forkAvailableModels,
|
|
3872
|
-
|
|
3957
|
+
parentModel?.provider,
|
|
3873
3958
|
);
|
|
3874
3959
|
const candidates = buildModelCandidates(
|
|
3875
3960
|
primaryModel,
|
|
3876
3961
|
agentConfig?.fallbackModels,
|
|
3877
3962
|
forkAvailableModels,
|
|
3878
|
-
|
|
3963
|
+
parentModel?.provider,
|
|
3879
3964
|
);
|
|
3880
3965
|
forkThinkingRequirements.set(
|
|
3881
3966
|
index,
|
|
3882
3967
|
candidates.length === 0
|
|
3883
|
-
|| candidates.some((candidate) => forkedChildRequiresThinkingOff(candidate, forkAvailableModels,
|
|
3968
|
+
|| candidates.some((candidate) => forkedChildRequiresThinkingOff(candidate, forkAvailableModels, parentModel?.provider)),
|
|
3884
3969
|
);
|
|
3885
3970
|
};
|
|
3886
3971
|
const forkContextResolver = createForkContextResolver(ctx.sessionManager, contextPolicy.usesFork ? "fork" : undefined, {
|
|
@@ -3894,6 +3979,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3894
3979
|
const requestedAsync = effectiveParams.async ?? deps.asyncByDefault;
|
|
3895
3980
|
const backgroundRequestedWhileClarifying = (hasChain || hasTasks) && requestedAsync && effectiveParams.clarify === true;
|
|
3896
3981
|
const effectiveAsync = requestedAsync && effectiveParams.clarify !== true;
|
|
3982
|
+
const foregroundTimeout = resolveForegroundTimeout(
|
|
3983
|
+
effectiveParams,
|
|
3984
|
+
effectiveAsync ? undefined : DEFAULT_FOREGROUND_TIMEOUT_MS,
|
|
3985
|
+
);
|
|
3986
|
+
if (foregroundTimeout.error) return buildRequestedModeError(effectiveParams, foregroundTimeout.error);
|
|
3897
3987
|
const controlConfig = resolveControlConfig(deps.config.control, effectiveParams.control);
|
|
3898
3988
|
|
|
3899
3989
|
const artifactConfig: ArtifactConfig = {
|
|
@@ -3951,7 +4041,10 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3951
4041
|
if (chainBindingsError) return withResolvedContext(chainBindingsError, contextPolicy.contextSummary);
|
|
3952
4042
|
|
|
3953
4043
|
const onUpdateWithContext = onUpdate
|
|
3954
|
-
? (r: AgentToolResult<Details>) => onUpdate(withResolvedContext(
|
|
4044
|
+
? (r: AgentToolResult<Details>) => onUpdate(withResolvedContext({
|
|
4045
|
+
...r,
|
|
4046
|
+
details: { ...r.details, runId },
|
|
4047
|
+
}, contextPolicy.contextSummary))
|
|
3955
4048
|
: undefined;
|
|
3956
4049
|
|
|
3957
4050
|
const reservation = reserveSpawnBudget(
|
|
@@ -3990,7 +4083,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3990
4083
|
configToolBudget: configToolBudget.toolBudget,
|
|
3991
4084
|
contextPolicy,
|
|
3992
4085
|
modelScope,
|
|
4086
|
+
parentModel: requestParentModel,
|
|
3993
4087
|
parentSessionId: deps.state.currentSessionId,
|
|
4088
|
+
capabilityCeiling: resolveCurrentSubagentCapabilityCeiling(deps.state.currentSessionId ?? undefined),
|
|
3994
4089
|
};
|
|
3995
4090
|
|
|
3996
4091
|
const foregroundDescription = effectiveParams.task?.trim()
|