pi-subagents 0.28.0 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +18 -61
  3. package/package.json +1 -1
  4. package/skills/pi-subagents/SKILL.md +4 -35
  5. package/src/agents/agent-management.ts +10 -20
  6. package/src/agents/agent-selection.ts +2 -0
  7. package/src/agents/agent-serializer.ts +0 -10
  8. package/src/agents/agents.ts +304 -47
  9. package/src/agents/chain-serializer.ts +4 -9
  10. package/src/extension/doctor.ts +4 -3
  11. package/src/extension/fanout-child.ts +0 -2
  12. package/src/extension/index.ts +3 -8
  13. package/src/extension/schemas.ts +32 -22
  14. package/src/intercom/intercom-bridge.ts +11 -1
  15. package/src/intercom/result-intercom.ts +0 -5
  16. package/src/runs/background/async-execution.ts +20 -11
  17. package/src/runs/background/run-status.ts +1 -7
  18. package/src/runs/background/subagent-runner.ts +81 -211
  19. package/src/runs/foreground/chain-execution.ts +62 -58
  20. package/src/runs/foreground/execution.ts +38 -343
  21. package/src/runs/foreground/subagent-executor.ts +28 -99
  22. package/src/runs/shared/acceptance.ts +605 -22
  23. package/src/runs/shared/completion-guard.ts +3 -26
  24. package/src/runs/shared/model-fallback.ts +38 -0
  25. package/src/runs/shared/parallel-utils.ts +6 -10
  26. package/src/runs/shared/subagent-prompt-runtime.ts +3 -2
  27. package/src/runs/shared/workflow-graph.ts +2 -6
  28. package/src/shared/atomic-json.ts +68 -11
  29. package/src/shared/settings.ts +1 -0
  30. package/src/shared/types.ts +10 -48
  31. package/src/shared/utils.ts +2 -8
  32. package/src/tui/render.ts +14 -29
  33. package/src/runs/shared/acceptance-contract.ts +0 -318
  34. package/src/runs/shared/acceptance-evaluation.ts +0 -221
  35. package/src/runs/shared/acceptance-finalization.ts +0 -173
  36. package/src/runs/shared/acceptance-reports.ts +0 -127
@@ -13,7 +13,7 @@ import { handleManagementAction } from "../../agents/agent-management.ts";
13
13
  import { buildDoctorReport } from "../../extension/doctor.ts";
14
14
  import { clearPendingForegroundControlNotices } from "../../extension/control-notices.ts";
15
15
  import { runSync } from "./execution.ts";
16
- import { resolveModelCandidate } from "../shared/model-fallback.ts";
16
+ import { resolveModelCandidate, resolveSubagentModelOverride } from "../shared/model-fallback.ts";
17
17
  import { aggregateParallelOutputs } from "../shared/parallel-utils.ts";
18
18
  import { recordRun } from "../shared/run-history.ts";
19
19
  import {
@@ -53,7 +53,6 @@ import { resolveSubagentRunId, type ResolvedSubagentRunId } from "../background/
53
53
  import { formatNestedRunStatusLines } from "../shared/nested-render.ts";
54
54
  import { inspectSubagentStatus } from "../background/run-status.ts";
55
55
  import { applyForceTopLevelAsyncOverride } from "../background/top-level-async.ts";
56
- import { validateAcceptanceInput } from "../shared/acceptance.ts";
57
56
  import {
58
57
  cleanupWorktrees,
59
58
  createWorktrees,
@@ -121,8 +120,6 @@ export interface SubagentParamsLike {
121
120
  chain?: ChainStep[];
122
121
  tasks?: TaskParam[];
123
122
  concurrency?: number;
124
- timeoutMs?: number;
125
- maxRuntimeMs?: number;
126
123
  worktree?: boolean;
127
124
  context?: "fresh" | "fork";
128
125
  async?: boolean;
@@ -171,7 +168,6 @@ interface ExecutionContextData {
171
168
  artifactsDir: string;
172
169
  backgroundRequestedWhileClarifying: boolean;
173
170
  effectiveAsync: boolean;
174
- foregroundTimeoutMs?: number;
175
171
  controlConfig: ResolvedControlConfig;
176
172
  intercomBridge: IntercomBridgeState;
177
173
  nestedRoute?: NestedRouteInfo;
@@ -253,7 +249,7 @@ function rememberForegroundRun(state: SubagentState, input: { runId: string; mod
253
249
  children: input.results.map((result, index) => ({
254
250
  agent: result.agent,
255
251
  index,
256
- status: resolveSubagentResultStatus({ exitCode: result.exitCode, interrupted: result.interrupted, detached: result.detached, timedOut: result.timedOut }),
252
+ status: resolveSubagentResultStatus({ exitCode: result.exitCode, interrupted: result.interrupted, detached: result.detached }),
257
253
  ...(result.sessionFile ? { sessionFile: result.sessionFile } : {}),
258
254
  })),
259
255
  });
@@ -653,6 +649,7 @@ async function resumeAsyncRun(input: {
653
649
  cwd: input.requestCwd,
654
650
  currentSessionId: input.deps.state.currentSessionId,
655
651
  currentModelProvider: input.ctx.model?.provider,
652
+ currentModel: input.ctx.model,
656
653
  },
657
654
  cwd: effectiveCwd,
658
655
  maxOutput: input.params.maxOutput,
@@ -694,6 +691,19 @@ function resultSummaryForIntercom(result: SingleResult): string {
694
691
  return output || result.error || "(no output)";
695
692
  }
696
693
 
694
+ function formatFailedSingleRunOutput(result: SingleResult, displayOutput: string): string {
695
+ const error = result.error || "Failed";
696
+ const output = displayOutput.trim();
697
+ const lines = [error];
698
+ if (output && output !== error.trim()) {
699
+ lines.push("", "Output:", output);
700
+ }
701
+ if (result.artifactPaths?.outputPath) {
702
+ lines.push("", `Output artifact: ${result.artifactPaths.outputPath}`);
703
+ }
704
+ return lines.join("\n");
705
+ }
706
+
697
707
  function createForegroundControlNotifier(data: Pick<ExecutionContextData, "controlConfig" | "intercomBridge">, deps: Pick<ExecutorDeps, "pi">): (event: ControlEvent) => void {
698
708
  return (event) => emitControlNotification({
699
709
  pi: deps.pi,
@@ -719,7 +729,6 @@ async function emitForegroundResultIntercom(input: {
719
729
  exitCode: result.exitCode,
720
730
  interrupted: result.interrupted,
721
731
  detached: result.detached,
722
- timedOut: result.timedOut,
723
732
  }),
724
733
  summary: resultSummaryForIntercom(result),
725
734
  index,
@@ -765,51 +774,6 @@ async function maybeBuildForegroundIntercomReceipt(input: {
765
774
  };
766
775
  }
767
776
 
768
- function validationErrorResult(mode: Details["mode"], text: string): AgentToolResult<Details> {
769
- return { content: [{ type: "text", text }], isError: true, details: { mode, results: [] } };
770
- }
771
-
772
- function resolveForegroundTimeoutMs(params: SubagentParamsLike): { timeoutMs?: number; error?: string } {
773
- const rawTimeout = (params as { timeoutMs?: unknown }).timeoutMs;
774
- const rawMaxRuntime = (params as { maxRuntimeMs?: unknown }).maxRuntimeMs;
775
- for (const [name, value] of [["timeoutMs", rawTimeout], ["maxRuntimeMs", rawMaxRuntime]] as const) {
776
- if (value !== undefined && (typeof value !== "number" || !Number.isInteger(value) || value < 1)) {
777
- return { error: `${name} must be a positive integer.` };
778
- }
779
- }
780
- if (rawTimeout !== undefined && rawMaxRuntime !== undefined && rawTimeout !== rawMaxRuntime) {
781
- return { error: "timeoutMs and maxRuntimeMs are aliases; provide only one or use identical values." };
782
- }
783
- const timeoutMs = rawTimeout ?? rawMaxRuntime;
784
- return timeoutMs === undefined ? {} : { timeoutMs };
785
- }
786
-
787
- function validateAcceptanceForExecution(params: SubagentParamsLike): AgentToolResult<Details> | null {
788
- const topLevelErrors = validateAcceptanceInput(params.acceptance);
789
- if (topLevelErrors.length > 0) return validationErrorResult("single", topLevelErrors.join(" "));
790
- for (const [index, task] of (params.tasks ?? []).entries()) {
791
- const errors = validateAcceptanceInput(task.acceptance, `tasks[${index}].acceptance`);
792
- if (errors.length > 0) return validationErrorResult("parallel", errors.join(" "));
793
- }
794
- for (const [stepIndex, step] of (params.chain ?? []).entries()) {
795
- if (isParallelStep(step)) {
796
- if (Object.hasOwn(step, "acceptance")) return validationErrorResult("chain", `chain[${stepIndex}].acceptance is not supported on static parallel groups; set acceptance on each parallel task.`);
797
- for (const [taskIndex, task] of step.parallel.entries()) {
798
- const errors = validateAcceptanceInput(task.acceptance, `chain[${stepIndex}].parallel[${taskIndex}].acceptance`);
799
- if (errors.length > 0) return validationErrorResult("chain", errors.join(" "));
800
- }
801
- } else if (isDynamicParallelStep(step)) {
802
- if (Object.hasOwn(step, "acceptance")) return validationErrorResult("chain", `chain[${stepIndex}].acceptance is not supported on dynamic fanout groups; set acceptance on chain[${stepIndex}].parallel.acceptance for each materialized child.`);
803
- const errors = validateAcceptanceInput(step.parallel.acceptance, `chain[${stepIndex}].parallel.acceptance`);
804
- if (errors.length > 0) return validationErrorResult("chain", errors.join(" "));
805
- } else {
806
- const stepErrors = validateAcceptanceInput(step.acceptance, `chain[${stepIndex}].acceptance`);
807
- if (stepErrors.length > 0) return validationErrorResult("chain", stepErrors.join(" "));
808
- }
809
- }
810
- return null;
811
- }
812
-
813
777
  function validateExecutionInput(
814
778
  params: SubagentParamsLike,
815
779
  agents: AgentConfig[],
@@ -818,9 +782,6 @@ function validateExecutionInput(
818
782
  hasSingle: boolean,
819
783
  allowClarifyTaskPrompt: boolean,
820
784
  ): AgentToolResult<Details> | null {
821
- const acceptanceError = validateAcceptanceForExecution(params);
822
- if (acceptanceError) return acceptanceError;
823
-
824
785
  if (Number(hasChain) + Number(hasTasks) + Number(hasSingle) !== 1) {
825
786
  return {
826
787
  content: [
@@ -834,9 +795,6 @@ function validateExecutionInput(
834
795
  };
835
796
  }
836
797
 
837
- const timeoutResolution = resolveForegroundTimeoutMs(params);
838
- if (timeoutResolution.error) return validationErrorResult(getRequestedModeLabel(params), timeoutResolution.error);
839
-
840
798
  if (hasSingle && params.agent && !agents.find((agent) => agent.name === params.agent)) {
841
799
  return {
842
800
  content: [{ type: "text", text: `Unknown agent: ${params.agent}` }],
@@ -1138,6 +1096,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
1138
1096
  cwd: ctx.cwd,
1139
1097
  currentSessionId: deps.state.currentSessionId!,
1140
1098
  currentModelProvider: ctx.model?.provider,
1099
+ currentModel: ctx.model,
1141
1100
  };
1142
1101
  const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
1143
1102
  const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth);
@@ -1148,7 +1107,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
1148
1107
  if (hasTasks && params.tasks) {
1149
1108
  const agentConfigs = params.tasks.map((task) => agents.find((agent) => agent.name === task.agent));
1150
1109
  const modelOverrides = params.tasks.map((task, index) =>
1151
- resolveModelCandidate(task.model ?? agentConfigs[index]?.model, availableModels, currentProvider),
1110
+ resolveSubagentModelOverride(task.model ?? agentConfigs[index]?.model, ctx.model, availableModels, currentProvider),
1152
1111
  );
1153
1112
  const skillOverrides = params.tasks.map((task) => normalizeSkillInput(task.skill));
1154
1113
  const parallelTasks = params.tasks.map((task, index) => ({
@@ -1235,7 +1194,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
1235
1194
  const normalizedSkills = normalizeSkillInput(params.skill);
1236
1195
  const skills = normalizedSkills === false ? [] : normalizedSkills;
1237
1196
  const maxSubagentDepth = resolveChildMaxSubagentDepth(currentMaxSubagentDepth, a.maxSubagentDepth);
1238
- const modelOverride = resolveModelCandidate((params.model as string | undefined) ?? a.model, availableModels, currentProvider);
1197
+ const modelOverride = resolveSubagentModelOverride((params.model as string | undefined) ?? a.model, ctx.model, availableModels, currentProvider);
1239
1198
  return executeAsyncSingle(id, {
1240
1199
  agent: params.agent!,
1241
1200
  task: params.context === "fork" ? wrapForkTask(params.task ?? "") : (params.task ?? ""),
@@ -1310,7 +1269,6 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
1310
1269
  onUpdate,
1311
1270
  onControlEvent,
1312
1271
  controlConfig,
1313
- ...(data.foregroundTimeoutMs !== undefined ? { timeoutMs: data.foregroundTimeoutMs } : {}),
1314
1272
  childIntercomTarget: childIntercomTarget ? (agent, index) => childIntercomTarget(runId, agent, index) : undefined,
1315
1273
  orchestratorIntercomTarget: data.intercomBridge.active ? data.intercomBridge.orchestratorTarget : undefined,
1316
1274
  foregroundControl,
@@ -1337,6 +1295,7 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
1337
1295
  cwd: ctx.cwd,
1338
1296
  currentSessionId: deps.state.currentSessionId!,
1339
1297
  currentModelProvider: ctx.model?.provider,
1298
+ currentModel: ctx.model,
1340
1299
  };
1341
1300
  const asyncChain = wrapChainTasksForFork(chainResult.requestedAsync.chain, params.context);
1342
1301
  return executeAsyncChain(id, {
@@ -1367,7 +1326,7 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
1367
1326
  const chainDetails = chainResult.details ? compactForegroundDetails({ ...chainResult.details, runId }) : undefined;
1368
1327
  if (foregroundControl) updateForegroundNestedProjection(foregroundControl);
1369
1328
  if (chainDetails) rememberForegroundRun(deps.state, { runId, mode: "chain", cwd: effectiveCwd, results: chainDetails.results });
1370
- const intercomReceipt = chainDetails && !chainDetails.results.some((result) => result.interrupted || result.detached || result.timedOut)
1329
+ const intercomReceipt = chainDetails && !chainDetails.results.some((result) => result.interrupted || result.detached)
1371
1330
  ? await maybeBuildForegroundIntercomReceipt({
1372
1331
  pi: deps.pi,
1373
1332
  intercomBridge: data.intercomBridge,
@@ -1402,8 +1361,6 @@ interface ForegroundParallelRunInput {
1402
1361
  artifactConfig: ArtifactConfig;
1403
1362
  artifactsDir: string;
1404
1363
  maxOutput?: MaxOutputConfig;
1405
- timeoutMs?: number;
1406
- timeoutAt?: number;
1407
1364
  paramsCwd: string;
1408
1365
  maxSubagentDepths: number[];
1409
1366
  availableModels: ModelInfo[];
@@ -1556,7 +1513,6 @@ async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Pr
1556
1513
  cwd: taskCwd,
1557
1514
  signal: input.signal,
1558
1515
  interruptSignal: interruptController.signal,
1559
- ...(input.timeoutMs !== undefined && input.timeoutAt !== undefined ? { timeoutMs: input.timeoutMs, timeoutAt: input.timeoutAt } : {}),
1560
1516
  allowIntercomDetach: agentConfig?.systemPrompt?.includes(INTERCOM_BRIDGE_MARKER) === true,
1561
1517
  intercomEvents: input.intercomEvents,
1562
1518
  runId: input.runId,
@@ -1570,8 +1526,6 @@ async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Pr
1570
1526
  outputPath,
1571
1527
  outputMode: behavior?.outputMode,
1572
1528
  maxSubagentDepth: input.maxSubagentDepths[index],
1573
- maxExecutionTimeMs: agentConfig?.maxExecutionTimeMs,
1574
- maxTokens: agentConfig?.maxTokens,
1575
1529
  controlConfig: input.controlConfig,
1576
1530
  onControlEvent: input.onControlEvent,
1577
1531
  intercomSessionName: input.childIntercomTarget?.(task.agent, index),
@@ -1697,7 +1651,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
1697
1651
  ...(task.model ? { model: task.model } : {}),
1698
1652
  }));
1699
1653
  const modelOverrides: (string | undefined)[] = tasks.map((_, i) =>
1700
- resolveModelCandidate(behaviorOverrides[i]?.model ?? agentConfigs[i]?.model, availableModels, currentProvider),
1654
+ resolveSubagentModelOverride(behaviorOverrides[i]?.model ?? agentConfigs[i]?.model, ctx.model, availableModels, currentProvider),
1701
1655
  );
1702
1656
 
1703
1657
  if (params.clarify === true && ctx.hasUI) {
@@ -1758,6 +1712,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
1758
1712
  cwd: ctx.cwd,
1759
1713
  currentSessionId: deps.state.currentSessionId!,
1760
1714
  currentModelProvider: ctx.model?.provider,
1715
+ currentModel: ctx.model,
1761
1716
  };
1762
1717
  const parallelTasks = tasks.map((t, i) => {
1763
1718
  const taskText = params.context === "fork" ? wrapForkTask(taskTexts[i]!) : taskTexts[i]!;
@@ -1839,7 +1794,6 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
1839
1794
  }
1840
1795
  }
1841
1796
 
1842
- const timeoutAt = data.foregroundTimeoutMs !== undefined ? Date.now() + data.foregroundTimeoutMs : undefined;
1843
1797
  const results = await runForegroundParallelTasks({
1844
1798
  tasks,
1845
1799
  taskTexts,
@@ -1854,7 +1808,6 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
1854
1808
  artifactConfig,
1855
1809
  artifactsDir,
1856
1810
  maxOutput: params.maxOutput,
1857
- ...(data.foregroundTimeoutMs !== undefined && timeoutAt !== undefined ? { timeoutMs: data.foregroundTimeoutMs, timeoutAt } : {}),
1858
1811
  paramsCwd: effectiveCwd,
1859
1812
  availableModels,
1860
1813
  modelOverrides,
@@ -1882,7 +1835,6 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
1882
1835
  if (result.artifactPaths) allArtifactPaths.push(result.artifactPaths);
1883
1836
  }
1884
1837
 
1885
- const timedOut = results.find((result) => result.timedOut);
1886
1838
  const interrupted = results.find((result) => result.interrupted);
1887
1839
  const details = compactForegroundDetails({
1888
1840
  mode: "parallel",
@@ -1892,13 +1844,6 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
1892
1844
  artifacts: allArtifactPaths.length ? { dir: artifactsDir, files: allArtifactPaths } : undefined,
1893
1845
  });
1894
1846
  rememberForegroundRun(deps.state, { runId, mode: "parallel", cwd: effectiveCwd, results: details.results });
1895
- if (timedOut) {
1896
- return {
1897
- content: [{ type: "text", text: `Parallel run timed out (${timedOut.agent}): ${timedOut.error ?? "timeout expired"}` }],
1898
- details,
1899
- isError: true,
1900
- };
1901
- }
1902
1847
  if (interrupted) {
1903
1848
  return {
1904
1849
  content: [{ type: "text", text: `Parallel run paused after interrupt (${interrupted.agent}). Waiting for explicit next action.` }],
@@ -1990,8 +1935,9 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
1990
1935
  const currentProvider = ctx.model?.provider;
1991
1936
  const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
1992
1937
  let task = params.task ?? "";
1993
- let modelOverride: string | undefined = resolveModelCandidate(
1938
+ let modelOverride: string | undefined = resolveSubagentModelOverride(
1994
1939
  (params.model as string | undefined) ?? agentConfig.model,
1940
+ ctx.model,
1995
1941
  availableModels,
1996
1942
  currentProvider,
1997
1943
  );
@@ -2048,6 +1994,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
2048
1994
  cwd: ctx.cwd,
2049
1995
  currentSessionId: deps.state.currentSessionId!,
2050
1996
  currentModelProvider: ctx.model?.provider,
1997
+ currentModel: ctx.model,
2051
1998
  };
2052
1999
  return executeAsyncSingle(id, {
2053
2000
  agent: params.agent!,
@@ -2129,12 +2076,10 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
2129
2076
  }
2130
2077
  : undefined;
2131
2078
 
2132
- const timeoutAt = data.foregroundTimeoutMs !== undefined ? Date.now() + data.foregroundTimeoutMs : undefined;
2133
2079
  const r = await runSync(ctx.cwd, agents, params.agent!, task, {
2134
2080
  cwd: effectiveCwd,
2135
2081
  signal,
2136
2082
  interruptSignal: interruptController.signal,
2137
- ...(data.foregroundTimeoutMs !== undefined && timeoutAt !== undefined ? { timeoutMs: data.foregroundTimeoutMs, timeoutAt } : {}),
2138
2083
  allowIntercomDetach: agentConfig.systemPrompt?.includes(INTERCOM_BRIDGE_MARKER) === true,
2139
2084
  intercomEvents: deps.pi.events,
2140
2085
  runId,
@@ -2147,8 +2092,6 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
2147
2092
  outputPath,
2148
2093
  outputMode: effectiveOutputMode,
2149
2094
  maxSubagentDepth,
2150
- maxExecutionTimeMs: agentConfig.maxExecutionTimeMs,
2151
- maxTokens: agentConfig.maxTokens,
2152
2095
  onUpdate: forwardSingleUpdate,
2153
2096
  controlConfig,
2154
2097
  onControlEvent,
@@ -2201,7 +2144,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
2201
2144
  });
2202
2145
  rememberForegroundRun(deps.state, { runId, mode: "single", cwd: effectiveCwd, results: details.results });
2203
2146
 
2204
- if (!r.detached && !r.interrupted && !r.timedOut) {
2147
+ if (!r.detached && !r.interrupted) {
2205
2148
  if (foregroundControl) updateForegroundNestedProjection(foregroundControl);
2206
2149
  const intercomReceipt = await maybeBuildForegroundIntercomReceipt({
2207
2150
  pi: deps.pi,
@@ -2227,14 +2170,6 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
2227
2170
  };
2228
2171
  }
2229
2172
 
2230
- if (r.timedOut) {
2231
- return {
2232
- content: [{ type: "text", text: `Run timed out (${params.agent}): ${r.error ?? "timeout expired"}` }],
2233
- details,
2234
- isError: true,
2235
- };
2236
- }
2237
-
2238
2173
  if (r.interrupted) {
2239
2174
  return {
2240
2175
  content: [{ type: "text", text: `Run paused after interrupt (${params.agent}). Waiting for explicit next action.` }],
@@ -2244,7 +2179,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
2244
2179
 
2245
2180
  if (r.exitCode !== 0)
2246
2181
  return {
2247
- content: [{ type: "text", text: r.error || "Failed" }],
2182
+ content: [{ type: "text", text: formatFailedSingleRunOutput(r, finalizedOutput.displayOutput) }],
2248
2183
  details,
2249
2184
  isError: true,
2250
2185
  };
@@ -2462,11 +2397,6 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
2462
2397
  const requestedAsync = effectiveParams.async ?? deps.asyncByDefault;
2463
2398
  const backgroundRequestedWhileClarifying = (hasChain || hasTasks) && requestedAsync && effectiveParams.clarify === true;
2464
2399
  const effectiveAsync = requestedAsync && effectiveParams.clarify !== true;
2465
- const foregroundTimeout = resolveForegroundTimeoutMs(effectiveParams);
2466
- if (foregroundTimeout.error) return buildRequestedModeError(effectiveParams, foregroundTimeout.error);
2467
- if (effectiveAsync && foregroundTimeout.timeoutMs !== undefined) {
2468
- return buildRequestedModeError(effectiveParams, "timeoutMs/maxRuntimeMs only applies to foreground subagent runs. Omit async:true or use action:'interrupt' for background runs.");
2469
- }
2470
2400
  const controlConfig = resolveControlConfig(deps.config.control, effectiveParams.control);
2471
2401
 
2472
2402
  const artifactConfig: ArtifactConfig = {
@@ -2518,7 +2448,6 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
2518
2448
  artifactsDir,
2519
2449
  backgroundRequestedWhileClarifying,
2520
2450
  effectiveAsync,
2521
- ...(foregroundTimeout.timeoutMs !== undefined ? { foregroundTimeoutMs: foregroundTimeout.timeoutMs } : {}),
2522
2451
  controlConfig,
2523
2452
  intercomBridge,
2524
2453
  nestedRoute,