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.
@@ -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
- if (intercomTarget && triggerTurn) {
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
- const delivered = await deliverSubagentResultIntercomEvent(pi.events, buildSubagentResultIntercomPayload({
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 (!delivered) console.error(`Subagent async grouped result intercom delivery was not acknowledged for '${resultPath}'.`);
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: input.ctx.model?.provider,
886
- currentModel: input.ctx.model,
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: input.ctx.model?.provider,
1281
- currentModel: input.ctx.model,
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: input.ctx.model?.provider,
1334
- currentModel: input.ctx.model,
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: input.params.model ?? recoveryDescriptor?.model ?? target.model,
1353
- thinkingOverride: input.params.model ? undefined : recoveryDescriptor?.thinking ?? target.thinking,
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 { content: [{ type: "text", text: formatAsyncStartedMessage(lines.join("\n"), input.ctx.hasUI) }], details: result.details };
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
- function resolveForegroundTimeout(params: SubagentParamsLike): { timeoutMs?: number; error?: string } {
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) return {};
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: ctx.model?.provider,
2053
- currentModel: ctx.model,
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 = ctx.model?.provider;
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, ctx.model, availableModels, currentProvider, { scope: data.modelScope });
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: ctx.model?.provider,
2309
- currentModel: ctx.model,
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.ctx.model?.provider,
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 currentProvider = ctx.model?.provider;
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, ctx.model, availableModels, currentProvider, { scope: data.modelScope }),
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, ctx.model, availableModels, currentProvider, { scope: data.modelScope });
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: ctx.model?.provider,
2826
- currentModel: ctx.model,
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 currentProvider = ctx.model?.provider;
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
- ctx.model,
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, ctx.model, availableModels, currentProvider, { scope: data.modelScope });
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: ctx.model?.provider,
3173
- currentModel: ctx.model,
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
- ctx.model,
3955
+ parentModel,
3900
3956
  forkAvailableModels,
3901
- ctx.model?.provider,
3957
+ parentModel?.provider,
3902
3958
  );
3903
3959
  const candidates = buildModelCandidates(
3904
3960
  primaryModel,
3905
3961
  agentConfig?.fallbackModels,
3906
3962
  forkAvailableModels,
3907
- ctx.model?.provider,
3963
+ parentModel?.provider,
3908
3964
  );
3909
3965
  forkThinkingRequirements.set(
3910
3966
  index,
3911
3967
  candidates.length === 0
3912
- || candidates.some((candidate) => forkedChildRequiresThinkingOff(candidate, forkAvailableModels, ctx.model?.provider)),
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(r, contextPolicy.contextSummary))
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
  };