pi-subagents 0.37.0 → 0.37.2
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 +29 -0
- package/README.md +18 -8
- package/agents/planner.md +2 -1
- package/package.json +3 -3
- package/skills/pi-subagents/SKILL.md +18 -989
- 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/skills.ts +14 -12
- package/src/api/delegation.ts +3 -0
- package/src/extension/index.ts +14 -7
- package/src/extension/rpc.ts +25 -2
- package/src/extension/schemas.ts +2 -2
- package/src/extension/tool-description.ts +2 -2
- package/src/intercom/intercom-bridge.ts +5 -2
- package/src/runs/background/async-job-tracker.ts +19 -12
- package/src/runs/background/async-resume.ts +6 -5
- package/src/runs/background/notify.ts +3 -0
- package/src/runs/background/result-watcher.ts +9 -3
- package/src/runs/foreground/subagent-executor.ts +111 -46
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +44 -11
- package/src/runs/shared/model-fallback.ts +8 -0
- package/src/runs/shared/pi-args.ts +3 -0
- package/src/runs/shared/task-intent.ts +1 -1
- package/src/shared/types.ts +14 -1
- package/src/slash/delegation-adapters.ts +5 -0
- package/src/tui/fleet-status.ts +62 -16
- package/src/tui/fleet.ts +1 -1
- package/src/tui/render.ts +23 -26
|
@@ -256,6 +256,7 @@ function validateStatusForResume(status: AsyncStatus | null, source: string): vo
|
|
|
256
256
|
if (stepRecord.sessionFile !== undefined && typeof stepRecord.sessionFile !== "string") throw new Error(`Invalid async status '${source}': steps[${index}].sessionFile must be a string.`);
|
|
257
257
|
if (stepRecord.model !== undefined && typeof stepRecord.model !== "string") throw new Error(`Invalid async status '${source}': steps[${index}].model must be a string.`);
|
|
258
258
|
if (stepRecord.thinking !== undefined && typeof stepRecord.thinking !== "string") throw new Error(`Invalid async status '${source}': steps[${index}].thinking must be a string.`);
|
|
259
|
+
if (stepRecord.launchContractDigest !== undefined && typeof stepRecord.launchContractDigest !== "string") throw new Error(`Invalid async status '${source}': steps[${index}].launchContractDigest must be a string.`);
|
|
259
260
|
if (stepRecord.capabilityCeiling !== undefined) stepRecord.capabilityCeiling = parseSubagentCapabilityCeiling(stepRecord.capabilityCeiling, `async status '${source}' steps[${index}].capabilityCeiling`);
|
|
260
261
|
});
|
|
261
262
|
}
|
|
@@ -285,7 +286,7 @@ export function readAsyncRecoveryDescriptor(asyncDir: string | undefined): Steer
|
|
|
285
286
|
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`Invalid async recovery descriptor '${descriptorPath}': expected an object.`);
|
|
286
287
|
const parsed = value as Record<string, unknown>;
|
|
287
288
|
const allowedFields = new Set([
|
|
288
|
-
"version", "sourceRunId", "agentContract", "agent", "sessionFile", "cwd", "model", "fallbackModels", "thinking", "tools", "extensions",
|
|
289
|
+
"version", "launchContractDigest", "sourceRunId", "agentContract", "agent", "sessionFile", "cwd", "model", "fallbackModels", "thinking", "tools", "extensions",
|
|
289
290
|
"subagentOnlyExtensions", "mcpDirectTools", "systemPrompt", "systemPromptMode", "inheritProjectContext", "inheritSkills", "skills",
|
|
290
291
|
"skillPath", "agentFilePath", "completionGuard", "memory", "outputPath", "outputMode", "structuredOutputSchema", "acceptance", "sessionDir", "artifactConfig",
|
|
291
292
|
"artifactsDir", "maxOutput", "controlConfig", "absoluteDeadlineAt", "initialTurnBudget", "initialToolBudget", "maxSubagentDepth", "share", "capabilityCeiling",
|
|
@@ -315,7 +316,7 @@ export function readAsyncRecoveryDescriptor(asyncDir: string | undefined): Steer
|
|
|
315
316
|
if (item !== undefined && (!Array.isArray(item) || item.some((entry) => typeof entry !== "string" || !entry.trim()))) throw new Error(`Invalid async recovery descriptor '${descriptorPath}': ${field} must contain non-empty strings.`);
|
|
316
317
|
}
|
|
317
318
|
if (parsed.systemPrompt !== undefined && typeof parsed.systemPrompt !== "string") throw new Error(`Invalid async recovery descriptor '${descriptorPath}': systemPrompt must be a string.`);
|
|
318
|
-
for (const field of ["sessionFile", "model", "thinking", "agentFilePath", "outputPath", "sessionDir", "artifactsDir"] as const) {
|
|
319
|
+
for (const field of ["launchContractDigest", "sessionFile", "model", "thinking", "agentFilePath", "outputPath", "sessionDir", "artifactsDir"] as const) {
|
|
319
320
|
if (parsed[field] !== undefined && (typeof parsed[field] !== "string" || !(parsed[field] as string).trim())) throw new Error(`Invalid async recovery descriptor '${descriptorPath}': ${field} must be a non-empty string.`);
|
|
320
321
|
}
|
|
321
322
|
if (parsed.completionGuard !== undefined && typeof parsed.completionGuard !== "boolean") throw new Error(`Invalid async recovery descriptor '${descriptorPath}': completionGuard must be a boolean.`);
|
|
@@ -427,7 +428,7 @@ export function resolveAsyncResumeTarget(params: AsyncResumeParams, deps: AsyncR
|
|
|
427
428
|
sessionFile: selectedStep.sessionFile ?? status?.sessionFile ?? result?.sessionFile,
|
|
428
429
|
model: selectedStep.model,
|
|
429
430
|
thinking: selectedStep.thinking,
|
|
430
|
-
launchContractDigest: selectedStep.launchContractDigest ?? result?.results?.[requestedIndex]?.launchContractDigest ?? result?.launchContractDigest,
|
|
431
|
+
launchContractDigest: selectedStep.launchContractDigest ?? result?.results?.[requestedIndex]?.launchContractDigest ?? result?.launchContractDigest ?? recoveryDescriptor?.launchContractDigest,
|
|
431
432
|
...(capabilityCeiling ? { capabilityCeiling } : {}),
|
|
432
433
|
...(recoveryDescriptor ? { recoveryDescriptor } : {}),
|
|
433
434
|
};
|
|
@@ -454,7 +455,7 @@ export function resolveAsyncResumeTarget(params: AsyncResumeParams, deps: AsyncR
|
|
|
454
455
|
sessionFile: selected.step.sessionFile ?? status?.sessionFile ?? result?.sessionFile,
|
|
455
456
|
model: selected.step.model,
|
|
456
457
|
thinking: selected.step.thinking,
|
|
457
|
-
launchContractDigest: selected.step.launchContractDigest ?? result?.results?.[selected.index]?.launchContractDigest ?? result?.launchContractDigest,
|
|
458
|
+
launchContractDigest: selected.step.launchContractDigest ?? result?.results?.[selected.index]?.launchContractDigest ?? result?.launchContractDigest ?? recoveryDescriptor?.launchContractDigest,
|
|
458
459
|
...(capabilityCeiling ? { capabilityCeiling } : {}),
|
|
459
460
|
...(recoveryDescriptor ? { recoveryDescriptor } : {}),
|
|
460
461
|
};
|
|
@@ -490,7 +491,7 @@ export function resolveAsyncResumeTarget(params: AsyncResumeParams, deps: AsyncR
|
|
|
490
491
|
...(resolvedSessionFile ? { sessionFile: resolvedSessionFile } : {}),
|
|
491
492
|
...(stepModel ? { model: stepModel } : {}),
|
|
492
493
|
...(stepThinking ? { thinking: stepThinking } : {}),
|
|
493
|
-
launchContractDigest: statusSteps[index]?.launchContractDigest ?? resultSteps[index]?.launchContractDigest ?? result?.launchContractDigest,
|
|
494
|
+
launchContractDigest: statusSteps[index]?.launchContractDigest ?? resultSteps[index]?.launchContractDigest ?? result?.launchContractDigest ?? recoveryDescriptor?.launchContractDigest,
|
|
494
495
|
...(capabilityCeiling ? { capabilityCeiling } : {}),
|
|
495
496
|
...(recoveryDescriptor ? { recoveryDescriptor } : {}),
|
|
496
497
|
};
|
|
@@ -48,6 +48,8 @@ export interface CompletionNotification {
|
|
|
48
48
|
totalTasks?: number;
|
|
49
49
|
sessionId?: string | null;
|
|
50
50
|
triggerTurn?: boolean;
|
|
51
|
+
/** True when an acknowledged grouped intercom relay already delivered this run. */
|
|
52
|
+
intercomDelivered?: boolean;
|
|
51
53
|
parallelHandoff?: ParallelHandoffReference;
|
|
52
54
|
}
|
|
53
55
|
|
|
@@ -250,6 +252,7 @@ export default function registerSubagentNotify(
|
|
|
250
252
|
|
|
251
253
|
const deliver = (result: CompletionNotification): Promise<boolean> => {
|
|
252
254
|
if (disposed || typeof result.sessionId !== "string" || result.sessionId !== state.currentSessionId) return Promise.resolve(false);
|
|
255
|
+
if (result.intercomDelivered === true) return Promise.resolve(true);
|
|
253
256
|
const key = buildCompletionKey(result, "notify");
|
|
254
257
|
const seenAt = seen.get(key);
|
|
255
258
|
if (seenAt !== undefined && now() - seenAt <= ttlMs) return Promise.resolve(true);
|
|
@@ -38,6 +38,8 @@ type ResultWatcherDeps = {
|
|
|
38
38
|
fs?: ResultWatcherFs;
|
|
39
39
|
timers?: ResultWatcherTimers;
|
|
40
40
|
notifier?: Pick<CompletionNotifier, "deliver">;
|
|
41
|
+
/** External grouped-result transport. Disable when native completion notifications own delivery. */
|
|
42
|
+
deliverIntercomResults?: boolean;
|
|
41
43
|
};
|
|
42
44
|
|
|
43
45
|
type ResultFileChild = {
|
|
@@ -108,6 +110,7 @@ export function createResultWatcher(
|
|
|
108
110
|
const fsApi = deps.fs ?? fs;
|
|
109
111
|
const timers = deps.timers ?? { setTimeout, clearTimeout, setInterval, clearInterval };
|
|
110
112
|
const notifier = deps.notifier ?? { deliver: async () => true };
|
|
113
|
+
const deliverIntercomResults = deps.deliverIntercomResults !== false;
|
|
111
114
|
const pendingTriggerTurn = new Map<string, boolean>();
|
|
112
115
|
const processing = new Set<string>();
|
|
113
116
|
let deliveryActive = true;
|
|
@@ -201,11 +204,12 @@ export function createResultWatcher(
|
|
|
201
204
|
}), nestedChildren);
|
|
202
205
|
|
|
203
206
|
const intercomTarget = data.intercomTarget?.trim();
|
|
204
|
-
|
|
207
|
+
let intercomDelivered = false;
|
|
208
|
+
if (deliverIntercomResults && intercomTarget && triggerTurn) {
|
|
205
209
|
const mode = data.mode === "single" || data.mode === "parallel" || data.mode === "chain"
|
|
206
210
|
? data.mode
|
|
207
211
|
: resultChildren.length > 1 ? "chain" : "single";
|
|
208
|
-
|
|
212
|
+
intercomDelivered = await deliverSubagentResultIntercomEvent(pi.events, buildSubagentResultIntercomPayload({
|
|
209
213
|
to: intercomTarget,
|
|
210
214
|
runId,
|
|
211
215
|
mode,
|
|
@@ -216,7 +220,7 @@ export function createResultWatcher(
|
|
|
216
220
|
...(data.parallelHandoff ? { parallelHandoff: data.parallelHandoff } : {}),
|
|
217
221
|
}));
|
|
218
222
|
if (!ownsSession(data.sessionId, epoch)) return;
|
|
219
|
-
if (!
|
|
223
|
+
if (!intercomDelivered) console.error(`Subagent async grouped result intercom delivery was not acknowledged for '${resultPath}'.`);
|
|
220
224
|
}
|
|
221
225
|
|
|
222
226
|
const accepted = await notifier.deliver({
|
|
@@ -224,6 +228,7 @@ export function createResultWatcher(
|
|
|
224
228
|
id: data.id ?? runId,
|
|
225
229
|
runId,
|
|
226
230
|
triggerTurn,
|
|
231
|
+
intercomDelivered,
|
|
227
232
|
...(nestedChildren?.length ? { nestedChildren } : {}),
|
|
228
233
|
...(Array.isArray(data.results) ? {
|
|
229
234
|
results: hasResultChildren ? normalizedChildren.map((child, index) => ({
|
|
@@ -249,6 +254,7 @@ export function createResultWatcher(
|
|
|
249
254
|
...data,
|
|
250
255
|
runId,
|
|
251
256
|
triggerTurn,
|
|
257
|
+
intercomDelivered,
|
|
252
258
|
...(nestedChildren?.length ? { nestedChildren } : {}),
|
|
253
259
|
...(Array.isArray(data.results) ? {
|
|
254
260
|
results: hasResultChildren ? normalizedChildren.map((child, index) => ({
|
|
@@ -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";
|
|
@@ -192,6 +192,15 @@ export interface SubagentParamsLike {
|
|
|
192
192
|
additional?: number;
|
|
193
193
|
}
|
|
194
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
|
+
|
|
195
204
|
interface ExecutorDeps {
|
|
196
205
|
pi: ExtensionAPI;
|
|
197
206
|
state: SubagentState;
|
|
@@ -240,6 +249,7 @@ interface ExecutionContextData {
|
|
|
240
249
|
configToolBudget?: ResolvedToolBudget;
|
|
241
250
|
contextPolicy: AgentDefaultContextPolicy;
|
|
242
251
|
modelScope?: ModelScopeConfig;
|
|
252
|
+
parentModel?: ParentModel;
|
|
243
253
|
parentSessionId: string | null;
|
|
244
254
|
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
245
255
|
}
|
|
@@ -407,11 +417,14 @@ function rememberForegroundRun(state: SubagentState, input: { runId: string; mod
|
|
|
407
417
|
...(result.savedOutputPath ? { savedOutputPath: result.savedOutputPath } : {}),
|
|
408
418
|
...(result.outputSaveError ? { outputSaveError: result.outputSaveError } : {}),
|
|
409
419
|
...(result.sessionFile ? { sessionFile: result.sessionFile } : {}),
|
|
420
|
+
...(result.model ? { model: result.model } : {}),
|
|
421
|
+
...(result.thinking ? { thinking: result.thinking } : {}),
|
|
410
422
|
...(result.artifactPaths ? { artifactPaths: result.artifactPaths } : {}),
|
|
411
423
|
...(result.transcriptPath ? { transcriptPath: result.transcriptPath } : {}),
|
|
412
424
|
...(result.transcriptError ? { transcriptError: result.transcriptError } : {}),
|
|
413
425
|
...(result.detachedReason ? { detachedReason: result.detachedReason } : {}),
|
|
414
426
|
...(result.acceptance ? { acceptance: result.acceptance } : {}),
|
|
427
|
+
...(result.launchContractDigest ? { launchContractDigest: result.launchContractDigest } : {}),
|
|
415
428
|
...(result.capabilityCeiling ? { capabilityCeiling: result.capabilityCeiling } : {}),
|
|
416
429
|
...(result.capabilityAudit ? { capabilityAudit: result.capabilityAudit } : {}),
|
|
417
430
|
};
|
|
@@ -472,11 +485,14 @@ function updateRememberedForegroundChild(state: SubagentState, input: { runId: s
|
|
|
472
485
|
savedOutputPath: input.result.savedOutputPath,
|
|
473
486
|
outputSaveError: input.result.outputSaveError,
|
|
474
487
|
...(input.result.sessionFile ? { sessionFile: input.result.sessionFile } : {}),
|
|
488
|
+
...(input.result.model ? { model: input.result.model } : {}),
|
|
489
|
+
...(input.result.thinking ? { thinking: input.result.thinking } : {}),
|
|
475
490
|
...(input.result.artifactPaths ? { artifactPaths: input.result.artifactPaths } : {}),
|
|
476
491
|
...(input.result.transcriptPath ? { transcriptPath: input.result.transcriptPath } : {}),
|
|
477
492
|
...(input.result.transcriptError ? { transcriptError: input.result.transcriptError } : {}),
|
|
478
493
|
...(input.result.detachedReason ? { detachedReason: input.result.detachedReason } : {}),
|
|
479
494
|
...(input.result.acceptance ? { acceptance: input.result.acceptance } : {}),
|
|
495
|
+
...(input.result.launchContractDigest ? { launchContractDigest: input.result.launchContractDigest } : {}),
|
|
480
496
|
...(input.result.capabilityCeiling ? { capabilityCeiling: input.result.capabilityCeiling } : {}),
|
|
481
497
|
...(input.result.capabilityAudit ? { capabilityAudit: input.result.capabilityAudit } : {}),
|
|
482
498
|
};
|
|
@@ -507,7 +523,7 @@ function updateRememberedForegroundChild(state: SubagentState, input: { runId: s
|
|
|
507
523
|
});
|
|
508
524
|
}
|
|
509
525
|
|
|
510
|
-
function resolveForegroundResumeTarget(params: SubagentParamsLike, state: SubagentState): { runId: string; mode: "single" | "parallel" | "chain"; state: "complete"; agent: string; index: number; cwd: string; sessionFile: string; capabilityCeiling?: ResolvedSubagentCapabilityCeiling } | 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 {
|
|
511
527
|
const requested = (params.id ?? params.runId)?.trim();
|
|
512
528
|
if (!requested || !state.foregroundRuns?.size || !state.currentSessionId) return undefined;
|
|
513
529
|
const sessionRuns = [...state.foregroundRuns.values()].filter((run) => run.sessionId === state.currentSessionId);
|
|
@@ -534,6 +550,9 @@ function resolveForegroundResumeTarget(params: SubagentParamsLike, state: Subage
|
|
|
534
550
|
index,
|
|
535
551
|
cwd: run.cwd,
|
|
536
552
|
sessionFile,
|
|
553
|
+
...(child.model ? { model: child.model } : {}),
|
|
554
|
+
...(child.thinking ? { thinking: child.thinking } : {}),
|
|
555
|
+
...(child.launchContractDigest ? { launchContractDigest: child.launchContractDigest } : {}),
|
|
537
556
|
...(child.capabilityCeiling ? { capabilityCeiling: child.capabilityCeiling } : {}),
|
|
538
557
|
};
|
|
539
558
|
}
|
|
@@ -773,6 +792,7 @@ function appendStepToAsyncChain(input: {
|
|
|
773
792
|
requestCwd: string;
|
|
774
793
|
ctx: ExtensionContext;
|
|
775
794
|
deps: ExecutorDeps;
|
|
795
|
+
parentModel?: ParentModel;
|
|
776
796
|
}): AgentToolResult<Details> {
|
|
777
797
|
const targetRunId = input.params.id ?? input.params.runId;
|
|
778
798
|
if (!targetRunId) {
|
|
@@ -877,13 +897,14 @@ function appendStepToAsyncChain(input: {
|
|
|
877
897
|
const contextPolicy = resolveExplicitContextPolicy(input.params);
|
|
878
898
|
const chainSkillInput = normalizeSkillInput(input.params.skill);
|
|
879
899
|
const chainSkills = chainSkillInput === false ? [] : (chainSkillInput ?? []);
|
|
900
|
+
const parentModel = input.parentModel;
|
|
880
901
|
const asyncCtx = {
|
|
881
902
|
pi: input.deps.pi,
|
|
882
903
|
cwd: input.ctx.cwd,
|
|
883
904
|
currentSessionId: resolveCurrentSessionId(input.ctx.sessionManager),
|
|
884
905
|
parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
|
|
885
|
-
currentModelProvider:
|
|
886
|
-
currentModel:
|
|
906
|
+
currentModelProvider: parentModel?.provider,
|
|
907
|
+
currentModel: parentModel,
|
|
887
908
|
modelScope: discoveredForAppend.modelScope,
|
|
888
909
|
interactive: input.ctx.hasUI,
|
|
889
910
|
};
|
|
@@ -1121,6 +1142,7 @@ async function resumeAsyncRun(input: {
|
|
|
1121
1142
|
requestCwd: string;
|
|
1122
1143
|
ctx: ExtensionContext;
|
|
1123
1144
|
deps: ExecutorDeps;
|
|
1145
|
+
parentModel?: ParentModel;
|
|
1124
1146
|
absoluteDeadlineAt?: number;
|
|
1125
1147
|
}): Promise<AgentToolResult<Details>> {
|
|
1126
1148
|
const followUp = (input.params.message ?? input.params.task ?? "").trim();
|
|
@@ -1132,6 +1154,13 @@ async function resumeAsyncRun(input: {
|
|
|
1132
1154
|
details: { mode: "management", results: [] },
|
|
1133
1155
|
};
|
|
1134
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
|
+
}
|
|
1135
1164
|
const acceptanceErrors = validateExecutionAcceptance(input.params);
|
|
1136
1165
|
if (acceptanceErrors.length > 0) {
|
|
1137
1166
|
return {
|
|
@@ -1259,6 +1288,7 @@ async function resumeAsyncRun(input: {
|
|
|
1259
1288
|
const goal = resolveAsyncEventGoal(workflowTask, attachChain);
|
|
1260
1289
|
const chain = wrapChainTasksForFork(attachChain, contextPolicy);
|
|
1261
1290
|
const normalized = normalizeSkillInput(input.params.skill);
|
|
1291
|
+
const parentModel = input.parentModel;
|
|
1262
1292
|
const result = executeAsyncChain(runId, {
|
|
1263
1293
|
chain,
|
|
1264
1294
|
task: workflowTask,
|
|
@@ -1277,8 +1307,8 @@ async function resumeAsyncRun(input: {
|
|
|
1277
1307
|
cwd: input.requestCwd,
|
|
1278
1308
|
currentSessionId: input.deps.state.currentSessionId,
|
|
1279
1309
|
parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
|
|
1280
|
-
currentModelProvider:
|
|
1281
|
-
currentModel:
|
|
1310
|
+
currentModelProvider: parentModel?.provider,
|
|
1311
|
+
currentModel: parentModel,
|
|
1282
1312
|
modelScope,
|
|
1283
1313
|
interactive: input.ctx.hasUI,
|
|
1284
1314
|
},
|
|
@@ -1320,6 +1350,7 @@ async function resumeAsyncRun(input: {
|
|
|
1320
1350
|
const artifactConfig: ArtifactConfig = recoveryDescriptor?.artifactConfig ?? { ...DEFAULT_ARTIFACT_CONFIG, enabled: input.params.artifacts !== false, dir: input.deps.config.artifactDir ?? DEFAULT_ARTIFACT_CONFIG.dir };
|
|
1321
1351
|
const artifactsDir = recoveryDescriptor?.artifactsDir ?? getArtifactsDir(parentSessionFile, effectiveCwd, artifactConfig.dir);
|
|
1322
1352
|
const availableModels = input.ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
1353
|
+
const parentModel = input.parentModel;
|
|
1323
1354
|
const result = executeAsyncSingle(runId, {
|
|
1324
1355
|
agent: target.agent,
|
|
1325
1356
|
task: buildRevivedAsyncTask(target, followUp),
|
|
@@ -1330,8 +1361,8 @@ async function resumeAsyncRun(input: {
|
|
|
1330
1361
|
cwd: input.requestCwd,
|
|
1331
1362
|
currentSessionId: input.deps.state.currentSessionId,
|
|
1332
1363
|
parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
|
|
1333
|
-
currentModelProvider:
|
|
1334
|
-
currentModel:
|
|
1364
|
+
currentModelProvider: parentModel?.provider,
|
|
1365
|
+
currentModel: parentModel,
|
|
1335
1366
|
modelScope,
|
|
1336
1367
|
interactive: input.ctx.hasUI,
|
|
1337
1368
|
},
|
|
@@ -1349,8 +1380,8 @@ async function resumeAsyncRun(input: {
|
|
|
1349
1380
|
sourceRunId: target.runId,
|
|
1350
1381
|
...(input.deps.state.currentSessionId ? { parentSessionId: input.deps.state.currentSessionId } : {}),
|
|
1351
1382
|
},
|
|
1352
|
-
modelOverride:
|
|
1353
|
-
thinkingOverride:
|
|
1383
|
+
modelOverride: recoveryDescriptor?.model ?? target.model,
|
|
1384
|
+
thinkingOverride: recoveryDescriptor?.thinking ?? target.thinking,
|
|
1354
1385
|
outputBaseDir: resolveSingleRunOutputBaseDir(input.deps, artifactsDir, runId),
|
|
1355
1386
|
maxSubagentDepth: recoveryDescriptor?.maxSubagentDepth ?? resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
|
|
1356
1387
|
waitToolEnabled: input.deps.waitToolEnabled,
|
|
@@ -1361,8 +1392,8 @@ async function resumeAsyncRun(input: {
|
|
|
1361
1392
|
controlIntercomTarget: intercomBridge.active ? intercomBridge.orchestratorTarget : undefined,
|
|
1362
1393
|
childIntercomTarget: intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(runId, agent, index) : undefined,
|
|
1363
1394
|
availableModels,
|
|
1364
|
-
output: recoveryDescriptor?.outputPath,
|
|
1365
|
-
outputMode: recoveryDescriptor?.outputMode,
|
|
1395
|
+
output: typeof input.params.output === "string" ? input.params.output : recoveryDescriptor?.outputPath,
|
|
1396
|
+
outputMode: input.params.outputMode ?? recoveryDescriptor?.outputMode,
|
|
1366
1397
|
...(recoveryDescriptor?.agentContract ? { agentContract: recoveryDescriptor.agentContract } : {}),
|
|
1367
1398
|
...(recoveryDescriptor?.structuredOutputSchema ? { structuredOutputSchema: recoveryDescriptor.structuredOutputSchema } : {}),
|
|
1368
1399
|
...(recoveryDescriptor?.skills ? { skills: [...recoveryDescriptor.skills] } : {}),
|
|
@@ -1387,7 +1418,13 @@ async function resumeAsyncRun(input: {
|
|
|
1387
1418
|
revivedTarget ? `Intercom target: ${revivedTarget} (if registered)` : undefined,
|
|
1388
1419
|
`Status if needed: subagent({ action: "status", id: "${revivedId}" })`,
|
|
1389
1420
|
].filter((line): line is string => Boolean(line));
|
|
1390
|
-
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
|
+
};
|
|
1391
1428
|
}
|
|
1392
1429
|
|
|
1393
1430
|
function resultSummaryForIntercom(result: SingleResult): string {
|
|
@@ -1433,7 +1470,7 @@ async function emitForegroundResultIntercom(input: {
|
|
|
1433
1470
|
nestedChildren?: NestedRunSummary[];
|
|
1434
1471
|
parallelHandoff?: Details["parallelHandoff"];
|
|
1435
1472
|
}): Promise<ReturnType<typeof buildSubagentResultIntercomPayload> | null> {
|
|
1436
|
-
if (!input.intercomBridge.active || !input.intercomBridge.orchestratorTarget) return null;
|
|
1473
|
+
if (!input.intercomBridge.active || !input.intercomBridge.resultDelivery || !input.intercomBridge.orchestratorTarget) return null;
|
|
1437
1474
|
const children = input.results.flatMap((result, index) => result.detached ? [] : [{
|
|
1438
1475
|
agent: result.agent,
|
|
1439
1476
|
status: resolveSubagentResultStatus({
|
|
@@ -1699,10 +1736,14 @@ function applySingleAgentLaunchDefaults(params: SubagentParamsLike, agents: Agen
|
|
|
1699
1736
|
};
|
|
1700
1737
|
}
|
|
1701
1738
|
|
|
1702
|
-
|
|
1739
|
+
export const DEFAULT_FOREGROUND_TIMEOUT_MS = 30 * 60 * 1000;
|
|
1740
|
+
|
|
1741
|
+
function resolveForegroundTimeout(params: SubagentParamsLike, defaultTimeoutMs?: number): { timeoutMs?: number; error?: string } {
|
|
1703
1742
|
const rawTimeout = params.timeoutMs;
|
|
1704
1743
|
const rawMaxRuntime = params.maxRuntimeMs;
|
|
1705
|
-
if (rawTimeout === undefined && rawMaxRuntime === undefined)
|
|
1744
|
+
if (rawTimeout === undefined && rawMaxRuntime === undefined) {
|
|
1745
|
+
return defaultTimeoutMs === undefined ? {} : { timeoutMs: defaultTimeoutMs };
|
|
1746
|
+
}
|
|
1706
1747
|
for (const [name, value] of [["timeoutMs", rawTimeout], ["maxRuntimeMs", rawMaxRuntime]] as const) {
|
|
1707
1748
|
if (value === undefined) continue;
|
|
1708
1749
|
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
|
|
@@ -2044,19 +2085,20 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2044
2085
|
};
|
|
2045
2086
|
}
|
|
2046
2087
|
const id = randomUUID();
|
|
2088
|
+
const parentModel = data.parentModel;
|
|
2047
2089
|
const asyncCtx = {
|
|
2048
2090
|
pi: deps.pi,
|
|
2049
2091
|
cwd: ctx.cwd,
|
|
2050
2092
|
currentSessionId: deps.state.currentSessionId!,
|
|
2051
2093
|
parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
|
|
2052
|
-
currentModelProvider:
|
|
2053
|
-
currentModel:
|
|
2094
|
+
currentModelProvider: parentModel?.provider,
|
|
2095
|
+
currentModel: parentModel,
|
|
2054
2096
|
modelScope: data.modelScope,
|
|
2055
2097
|
interactive: ctx.hasUI,
|
|
2056
2098
|
};
|
|
2057
2099
|
const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
2058
2100
|
const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth);
|
|
2059
|
-
const currentProvider =
|
|
2101
|
+
const currentProvider = parentModel?.provider;
|
|
2060
2102
|
const controlIntercomTarget = intercomBridge.active ? intercomBridge.orchestratorTarget : undefined;
|
|
2061
2103
|
const childIntercomTarget = intercomBridge.active ? (agent: string, index: number) => resolveSubagentIntercomTarget(id, agent, index) : undefined;
|
|
2062
2104
|
|
|
@@ -2174,7 +2216,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2174
2216
|
const normalizedSkills = normalizeSkillInput(params.skill);
|
|
2175
2217
|
const skills = normalizedSkills === false ? [] : normalizedSkills;
|
|
2176
2218
|
const maxSubagentDepth = resolveChildMaxSubagentDepth(currentMaxSubagentDepth, a.maxSubagentDepth);
|
|
2177
|
-
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 });
|
|
2178
2220
|
return executeAsyncSingle(id, {
|
|
2179
2221
|
agent: params.agent!,
|
|
2180
2222
|
task: shouldForkAgent(contextPolicy, params.agent!) ? wrapForkTask(params.task ?? "") : (params.task ?? ""),
|
|
@@ -2246,11 +2288,12 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
2246
2288
|
const chainSkills = normalized === false ? [] : (normalized ?? []);
|
|
2247
2289
|
const chain = wrapChainTasksForFork(params.chain as ChainStep[], contextPolicy);
|
|
2248
2290
|
const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth);
|
|
2291
|
+
const chainCtx = normalizeParentModel(ctx.model) || !data.parentModel ? ctx : { ...ctx, model: data.parentModel };
|
|
2249
2292
|
const chainResult = await executeChain({
|
|
2250
2293
|
chain,
|
|
2251
2294
|
task: params.task,
|
|
2252
2295
|
agents,
|
|
2253
|
-
ctx,
|
|
2296
|
+
ctx: chainCtx,
|
|
2254
2297
|
modelScope: data.modelScope,
|
|
2255
2298
|
intercomEvents: deps.pi.events,
|
|
2256
2299
|
signal,
|
|
@@ -2300,13 +2343,14 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
2300
2343
|
};
|
|
2301
2344
|
}
|
|
2302
2345
|
const id = randomUUID();
|
|
2346
|
+
const parentModel = data.parentModel;
|
|
2303
2347
|
const asyncCtx = {
|
|
2304
2348
|
pi: deps.pi,
|
|
2305
2349
|
cwd: ctx.cwd,
|
|
2306
2350
|
currentSessionId: deps.state.currentSessionId!,
|
|
2307
2351
|
parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
|
|
2308
|
-
currentModelProvider:
|
|
2309
|
-
currentModel:
|
|
2352
|
+
currentModelProvider: parentModel?.provider,
|
|
2353
|
+
currentModel: parentModel,
|
|
2310
2354
|
modelScope: data.modelScope,
|
|
2311
2355
|
interactive: ctx.hasUI,
|
|
2312
2356
|
};
|
|
@@ -2350,7 +2394,7 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
2350
2394
|
});
|
|
2351
2395
|
}
|
|
2352
2396
|
|
|
2353
|
-
const rawChainDetails = chainResult.details ? { ...chainResult.details, runId } : undefined;
|
|
2397
|
+
const rawChainDetails = chainResult.details ? { ...chainResult.details, runId, timeoutMs: data.timeoutMs } : undefined;
|
|
2354
2398
|
if (foregroundControl && rawChainDetails) {
|
|
2355
2399
|
updateForegroundNestedProjection(foregroundControl);
|
|
2356
2400
|
attachRootChildrenToSteps(runId, rawChainDetails.results, foregroundControl.nestedChildren);
|
|
@@ -2406,6 +2450,7 @@ interface ForegroundParallelRunInput {
|
|
|
2406
2450
|
waitToolEnabled?: boolean;
|
|
2407
2451
|
availableModels: ModelInfo[];
|
|
2408
2452
|
modelScope?: ModelScopeConfig;
|
|
2453
|
+
parentModel?: ParentModel;
|
|
2409
2454
|
modelOverrides: (string | undefined)[];
|
|
2410
2455
|
behaviors: Array<ReturnType<typeof resolveStepBehavior>>;
|
|
2411
2456
|
firstProgressIndex: number;
|
|
@@ -2641,7 +2686,7 @@ async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Pr
|
|
|
2641
2686
|
modelOverride: input.modelOverrides[index],
|
|
2642
2687
|
thinkingOverride: input.thinkingOverrideForTask(task.agent, index, input.modelOverrides[index]),
|
|
2643
2688
|
availableModels: input.availableModels,
|
|
2644
|
-
preferredModelProvider: input.
|
|
2689
|
+
preferredModelProvider: input.parentModel?.provider,
|
|
2645
2690
|
modelScope: input.modelScope,
|
|
2646
2691
|
skills: effectiveSkills === false ? [] : effectiveSkills,
|
|
2647
2692
|
structuredOutput: structuredRuntime,
|
|
@@ -2746,7 +2791,8 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2746
2791
|
if (worktreeTaskCwdError) return buildParallelModeError(worktreeTaskCwdError);
|
|
2747
2792
|
}
|
|
2748
2793
|
|
|
2749
|
-
const
|
|
2794
|
+
const parentModel = data.parentModel;
|
|
2795
|
+
const currentProvider = parentModel?.provider;
|
|
2750
2796
|
const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
2751
2797
|
let taskTexts = tasks.map((t) => t.task);
|
|
2752
2798
|
const skillOverrides: (string[] | false | undefined)[] = tasks.map((t) =>
|
|
@@ -2761,7 +2807,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2761
2807
|
...(task.model !== undefined ? { model: task.model } : {}),
|
|
2762
2808
|
}));
|
|
2763
2809
|
const modelOverrides: (string | undefined)[] = tasks.map((_, i) =>
|
|
2764
|
-
resolveEffectiveSubagentModel(behaviorOverrides[i]?.model, agentConfigs[i]?.model,
|
|
2810
|
+
resolveEffectiveSubagentModel(behaviorOverrides[i]?.model, agentConfigs[i]?.model, parentModel, availableModels, currentProvider, { scope: data.modelScope }),
|
|
2765
2811
|
);
|
|
2766
2812
|
|
|
2767
2813
|
if (params.clarify === true && ctx.hasUI) {
|
|
@@ -2796,7 +2842,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2796
2842
|
for (let i = 0; i < result.behaviorOverrides.length; i++) {
|
|
2797
2843
|
const override = result.behaviorOverrides[i];
|
|
2798
2844
|
if (override?.model !== undefined) {
|
|
2799
|
-
modelOverrides[i] = resolveEffectiveSubagentModel(override.model, agentConfigs[i]?.model,
|
|
2845
|
+
modelOverrides[i] = resolveEffectiveSubagentModel(override.model, agentConfigs[i]?.model, parentModel, availableModels, currentProvider, { scope: data.modelScope });
|
|
2800
2846
|
behaviorOverrides[i]!.model = override.model;
|
|
2801
2847
|
}
|
|
2802
2848
|
if (override?.output !== undefined) behaviorOverrides[i]!.output = override.output;
|
|
@@ -2822,8 +2868,8 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2822
2868
|
cwd: ctx.cwd,
|
|
2823
2869
|
currentSessionId: deps.state.currentSessionId!,
|
|
2824
2870
|
parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
|
|
2825
|
-
currentModelProvider:
|
|
2826
|
-
currentModel:
|
|
2871
|
+
currentModelProvider: parentModel?.provider,
|
|
2872
|
+
currentModel: parentModel,
|
|
2827
2873
|
modelScope: data.modelScope,
|
|
2828
2874
|
interactive: ctx.hasUI,
|
|
2829
2875
|
};
|
|
@@ -2958,6 +3004,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
2958
3004
|
progressDir: parallelProgressDir,
|
|
2959
3005
|
availableModels,
|
|
2960
3006
|
modelScope: data.modelScope,
|
|
3007
|
+
parentModel,
|
|
2961
3008
|
modelOverrides,
|
|
2962
3009
|
behaviors,
|
|
2963
3010
|
firstProgressIndex: parallelProgressPrecreated ? -1 : firstProgressIndex,
|
|
@@ -3004,6 +3051,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
3004
3051
|
const details = compactForegroundDetails({
|
|
3005
3052
|
mode: "parallel",
|
|
3006
3053
|
runId,
|
|
3054
|
+
timeoutMs: data.timeoutMs,
|
|
3007
3055
|
results,
|
|
3008
3056
|
progress: params.includeProgress ? allProgress : undefined,
|
|
3009
3057
|
artifacts: allArtifactPaths.length ? { dir: artifactsDir, files: allArtifactPaths } : undefined,
|
|
@@ -3105,13 +3153,14 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3105
3153
|
const effectiveToolBudget = resolveEffectiveToolBudget({ runBudget: data.toolBudget, agentBudget: agentConfig.toolBudget, configBudget: data.configToolBudget });
|
|
3106
3154
|
if (effectiveToolBudget.error) return toExecutionErrorResult(params, new Error(effectiveToolBudget.error), data.contextPolicy.contextSummary);
|
|
3107
3155
|
|
|
3108
|
-
const
|
|
3156
|
+
const parentModel = data.parentModel;
|
|
3157
|
+
const currentProvider = parentModel?.provider;
|
|
3109
3158
|
const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
3110
3159
|
let task = params.task ?? "";
|
|
3111
3160
|
let modelOverride: string | undefined = resolveEffectiveSubagentModel(
|
|
3112
3161
|
params.model as string | undefined,
|
|
3113
3162
|
agentConfig.model,
|
|
3114
|
-
|
|
3163
|
+
parentModel,
|
|
3115
3164
|
availableModels,
|
|
3116
3165
|
currentProvider,
|
|
3117
3166
|
{ scope: data.modelScope },
|
|
@@ -3151,7 +3200,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3151
3200
|
|
|
3152
3201
|
task = result.templates[0]!;
|
|
3153
3202
|
const override = result.behaviorOverrides[0];
|
|
3154
|
-
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 });
|
|
3155
3204
|
if (override?.output !== undefined) effectiveOutput = normalizeSingleOutputOverride(override.output, agentConfig.output);
|
|
3156
3205
|
if (override?.skills !== undefined) skillOverride = override.skills;
|
|
3157
3206
|
|
|
@@ -3169,8 +3218,8 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3169
3218
|
cwd: ctx.cwd,
|
|
3170
3219
|
currentSessionId: deps.state.currentSessionId!,
|
|
3171
3220
|
parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
|
|
3172
|
-
currentModelProvider:
|
|
3173
|
-
currentModel:
|
|
3221
|
+
currentModelProvider: parentModel?.provider,
|
|
3222
|
+
currentModel: parentModel,
|
|
3174
3223
|
modelScope: data.modelScope,
|
|
3175
3224
|
interactive: ctx.hasUI,
|
|
3176
3225
|
};
|
|
@@ -3332,6 +3381,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3332
3381
|
const details = compactForegroundDetails({
|
|
3333
3382
|
mode: "single",
|
|
3334
3383
|
runId,
|
|
3384
|
+
timeoutMs: data.timeoutMs,
|
|
3335
3385
|
results: [r],
|
|
3336
3386
|
...(data.turnBudget ? { turnBudget: data.turnBudget } : {}),
|
|
3337
3387
|
...(effectiveToolBudget.toolBudget ? { toolBudget: effectiveToolBudget.toolBudget } : {}),
|
|
@@ -3460,6 +3510,13 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3460
3510
|
const requestCwd = resolveRequestedCwd(ctx.cwd, requestParams.cwd);
|
|
3461
3511
|
const paramsWithResolvedCwd = requestParams.cwd === undefined ? requestParams : { ...requestParams, cwd: requestCwd };
|
|
3462
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
|
+
}
|
|
3463
3520
|
if (action) {
|
|
3464
3521
|
if ((WATCHDOG_TOOL_ACTIONS as readonly string[]).includes(action)) {
|
|
3465
3522
|
if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
|
|
@@ -3617,7 +3674,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3617
3674
|
return withBudget(inspectSubagentStatus(paramsWithResolvedCwd, { state: deps.state, nested: nestedScope, sessionRoots }));
|
|
3618
3675
|
}
|
|
3619
3676
|
if (action === "resume") {
|
|
3620
|
-
return resumeAsyncRun({ params: paramsWithResolvedCwd, requestCwd, ctx, deps });
|
|
3677
|
+
return resumeAsyncRun({ params: paramsWithResolvedCwd, requestCwd, ctx, deps, parentModel: requestParentModel });
|
|
3621
3678
|
}
|
|
3622
3679
|
if (action === "steer") {
|
|
3623
3680
|
deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
@@ -3640,7 +3697,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3640
3697
|
? {}
|
|
3641
3698
|
: {
|
|
3642
3699
|
recover: ({ absoluteDeadlineAt, ...limits }) =>
|
|
3643
|
-
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 }),
|
|
3644
3701
|
}
|
|
3645
3702
|
),
|
|
3646
3703
|
});
|
|
@@ -3677,6 +3734,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3677
3734
|
requestCwd,
|
|
3678
3735
|
ctx,
|
|
3679
3736
|
deps,
|
|
3737
|
+
parentModel: requestParentModel,
|
|
3680
3738
|
absoluteDeadlineAt,
|
|
3681
3739
|
}),
|
|
3682
3740
|
}
|
|
@@ -3684,7 +3742,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3684
3742
|
});
|
|
3685
3743
|
}
|
|
3686
3744
|
if (action === "append-step") {
|
|
3687
|
-
return appendStepToAsyncChain({ params: paramsWithResolvedCwd, requestCwd, ctx, deps });
|
|
3745
|
+
return appendStepToAsyncChain({ params: paramsWithResolvedCwd, requestCwd, ctx, deps, parentModel: requestParentModel });
|
|
3688
3746
|
}
|
|
3689
3747
|
if (action === "schedule" || action === "schedule-list" || action === "schedule-status" || action === "schedule-cancel") {
|
|
3690
3748
|
if (!deps.handleScheduledRunAction) {
|
|
@@ -3831,13 +3889,10 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3831
3889
|
const scope: AgentScope = resolveExecutionAgentScope(effectiveParams.agentScope);
|
|
3832
3890
|
const effectiveCwd = effectiveParams.cwd ?? ctx.cwd;
|
|
3833
3891
|
const parentSessionFile = ctx.sessionManager.getSessionFile() ?? null;
|
|
3834
|
-
deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
3835
3892
|
const discovered = deps.discoverAgents(effectiveCwd, scope);
|
|
3836
3893
|
const discoveredAgents = discovered.agents;
|
|
3837
3894
|
const modelScope = discovered.modelScope;
|
|
3838
3895
|
effectiveParams = applySingleAgentLaunchDefaults(effectiveParams, discoveredAgents);
|
|
3839
|
-
const foregroundTimeout = resolveForegroundTimeout(effectiveParams);
|
|
3840
|
-
if (foregroundTimeout.error) return buildRequestedModeError(effectiveParams, foregroundTimeout.error);
|
|
3841
3896
|
const turnBudget = resolveTurnBudgetConfig(effectiveParams.turnBudget ?? deps.config.turnBudget);
|
|
3842
3897
|
if (turnBudget.error) return buildRequestedModeError(effectiveParams, turnBudget.error);
|
|
3843
3898
|
const contextPolicy = resolveAgentDefaultContextPolicy(effectiveParams, discoveredAgents);
|
|
@@ -3891,25 +3946,26 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3891
3946
|
const forkThinkingDowngrades = new Map<number, string>();
|
|
3892
3947
|
try {
|
|
3893
3948
|
const forkAvailableModels = contextPolicy.usesFork ? ctx.modelRegistry.getAvailable().map(toModelInfo) : [];
|
|
3949
|
+
const parentModel = requestParentModel;
|
|
3894
3950
|
prepareForkThinking = (agentName, index, modelOverride) => {
|
|
3895
3951
|
const agentConfig = agents.find((agent) => agent.name === agentName);
|
|
3896
3952
|
const primaryModel = resolveEffectiveSubagentModel(
|
|
3897
3953
|
modelOverride,
|
|
3898
3954
|
agentConfig?.model,
|
|
3899
|
-
|
|
3955
|
+
parentModel,
|
|
3900
3956
|
forkAvailableModels,
|
|
3901
|
-
|
|
3957
|
+
parentModel?.provider,
|
|
3902
3958
|
);
|
|
3903
3959
|
const candidates = buildModelCandidates(
|
|
3904
3960
|
primaryModel,
|
|
3905
3961
|
agentConfig?.fallbackModels,
|
|
3906
3962
|
forkAvailableModels,
|
|
3907
|
-
|
|
3963
|
+
parentModel?.provider,
|
|
3908
3964
|
);
|
|
3909
3965
|
forkThinkingRequirements.set(
|
|
3910
3966
|
index,
|
|
3911
3967
|
candidates.length === 0
|
|
3912
|
-
|| candidates.some((candidate) => forkedChildRequiresThinkingOff(candidate, forkAvailableModels,
|
|
3968
|
+
|| candidates.some((candidate) => forkedChildRequiresThinkingOff(candidate, forkAvailableModels, parentModel?.provider)),
|
|
3913
3969
|
);
|
|
3914
3970
|
};
|
|
3915
3971
|
const forkContextResolver = createForkContextResolver(ctx.sessionManager, contextPolicy.usesFork ? "fork" : undefined, {
|
|
@@ -3923,6 +3979,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3923
3979
|
const requestedAsync = effectiveParams.async ?? deps.asyncByDefault;
|
|
3924
3980
|
const backgroundRequestedWhileClarifying = (hasChain || hasTasks) && requestedAsync && effectiveParams.clarify === true;
|
|
3925
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);
|
|
3926
3987
|
const controlConfig = resolveControlConfig(deps.config.control, effectiveParams.control);
|
|
3927
3988
|
|
|
3928
3989
|
const artifactConfig: ArtifactConfig = {
|
|
@@ -3980,7 +4041,10 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
3980
4041
|
if (chainBindingsError) return withResolvedContext(chainBindingsError, contextPolicy.contextSummary);
|
|
3981
4042
|
|
|
3982
4043
|
const onUpdateWithContext = onUpdate
|
|
3983
|
-
? (r: AgentToolResult<Details>) => onUpdate(withResolvedContext(
|
|
4044
|
+
? (r: AgentToolResult<Details>) => onUpdate(withResolvedContext({
|
|
4045
|
+
...r,
|
|
4046
|
+
details: { ...r.details, runId },
|
|
4047
|
+
}, contextPolicy.contextSummary))
|
|
3984
4048
|
: undefined;
|
|
3985
4049
|
|
|
3986
4050
|
const reservation = reserveSpawnBudget(
|
|
@@ -4019,6 +4083,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4019
4083
|
configToolBudget: configToolBudget.toolBudget,
|
|
4020
4084
|
contextPolicy,
|
|
4021
4085
|
modelScope,
|
|
4086
|
+
parentModel: requestParentModel,
|
|
4022
4087
|
parentSessionId: deps.state.currentSessionId,
|
|
4023
4088
|
capabilityCeiling: resolveCurrentSubagentCapabilityCeiling(deps.state.currentSessionId ?? undefined),
|
|
4024
4089
|
};
|