pi-subagents 0.49.0 → 0.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +35 -0
- package/agents/oracle.md +7 -5
- package/agents/researcher.md +1 -1
- package/agents/reviewer.md +2 -2
- package/agents/scout.md +1 -1
- package/agents/worker.md +1 -1
- package/docs/agents.md +2 -0
- package/docs/configuration.md +47 -3
- package/docs/extension-api.md +36 -0
- package/docs/missions.md +1 -1
- package/docs/observability.md +16 -0
- package/docs/tool-reference.md +19 -2
- package/docs/workflows.md +2 -2
- package/package.json +1 -1
- package/skills/pi-subagents/references/constraints-and-recipes.md +3 -2
- package/skills/pi-subagents/references/execution-controls.md +5 -4
- package/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
- package/skills/pi-subagents/references/prompting-and-roles.md +31 -15
- package/src/agents/agent-serializer.ts +2 -0
- package/src/agents/agents.ts +10 -0
- package/src/api/external-runs.ts +174 -84
- package/src/api/preflight.ts +12 -6
- package/src/extension/config.ts +33 -0
- package/src/extension/index.ts +48 -5
- package/src/extension/public-execution.ts +3 -1
- package/src/extension/rpc.ts +5 -1
- package/src/extension/schemas.ts +2 -1
- package/src/extension/tool-description.ts +4 -2
- package/src/inspectors/herdr/actions.ts +3 -7
- package/src/inspectors/herdr/project-panes.ts +2 -6
- package/src/inspectors/herdr/shell-command.ts +16 -0
- package/src/intercom/intercom-bridge.ts +2 -3
- package/src/intercom/native-supervisor-channel.ts +19 -42
- package/src/missions/goal-driver.ts +3 -1
- package/src/runs/background/active-run-index.ts +71 -1
- package/src/runs/background/async-execution.ts +49 -7
- package/src/runs/background/async-job-tracker.ts +1 -0
- package/src/runs/background/async-resume.ts +3 -5
- package/src/runs/background/async-status-snapshot.ts +277 -0
- package/src/runs/background/async-status.ts +8 -3
- package/src/runs/background/chain-root-attachment.ts +2 -2
- package/src/runs/background/completion-replay.ts +11 -1
- package/src/runs/background/fleet-view.ts +3 -1
- package/src/runs/background/result-files.ts +437 -0
- package/src/runs/background/result-watcher.ts +188 -41
- package/src/runs/background/retained-children.ts +69 -20
- package/src/runs/background/run-id-resolver.ts +30 -24
- package/src/runs/background/run-status.ts +7 -2
- package/src/runs/background/scheduled-runs.ts +54 -28
- package/src/runs/background/stale-run-reconciler.ts +27 -13
- package/src/runs/background/subagent-runner.ts +294 -32
- package/src/runs/background/subagent-wait.ts +2 -0
- package/src/runs/background/wait-completions.ts +5 -2
- package/src/runs/foreground/async-dismiss-action.ts +2 -1
- package/src/runs/foreground/chain-execution.ts +16 -0
- package/src/runs/foreground/execution.ts +219 -15
- package/src/runs/foreground/subagent-executor.ts +114 -28
- package/src/runs/shared/completion-guard.ts +96 -6
- package/src/runs/shared/external-cli-runner.ts +4 -0
- package/src/runs/shared/model-fallback.ts +16 -2
- package/src/runs/shared/nested-events.ts +66 -62
- package/src/runs/shared/orca-progress-tabs.ts +375 -0
- package/src/runs/shared/parallel-utils.ts +2 -0
- package/src/runs/shared/subagent-control.ts +15 -0
- package/src/runs/shared/subagent-prompt-runtime.ts +1 -9
- package/src/runs/shared/subagent-startup-retry.ts +12 -0
- package/src/runs/shared/tool-timeout.ts +93 -0
- package/src/shared/types.ts +29 -2
- package/src/slash/slash-commands.ts +34 -25
- package/src/slash/slash-live-state.ts +3 -0
- package/src/tui/fleet-status.ts +160 -45
- package/src/tui/fleet.ts +125 -15
- package/src/tui/render.ts +41 -10
- package/src/workflows/chat-progress.ts +6 -4
- package/src/workflows/scripted-workflow.ts +227 -70
|
@@ -162,6 +162,8 @@ interface ParallelChainRunInput {
|
|
|
162
162
|
nestedRoute?: NestedRouteInfo;
|
|
163
163
|
timeoutMs?: number;
|
|
164
164
|
deadlineAt?: number;
|
|
165
|
+
callToolTimeoutMs?: number;
|
|
166
|
+
configToolTimeoutMs?: number;
|
|
165
167
|
turnBudget?: ResolvedTurnBudget;
|
|
166
168
|
usageBudget?: UsageBudgetConfig;
|
|
167
169
|
onDetachedExit?: (index: number, result: SingleResult) => void;
|
|
@@ -436,6 +438,8 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
|
|
|
436
438
|
onEffectivePrompt: input.foregroundControl ? (prompt) => updateLiveEffectivePrompt(input.foregroundControl!, childIndex, prompt) : undefined,
|
|
437
439
|
timeoutMs: input.timeoutMs,
|
|
438
440
|
deadlineAt: input.deadlineAt,
|
|
441
|
+
toolTimeoutMs: input.callToolTimeoutMs,
|
|
442
|
+
configToolTimeoutMs: input.configToolTimeoutMs,
|
|
439
443
|
turnBudget: input.turnBudget,
|
|
440
444
|
onDetachedExit: (result) => {
|
|
441
445
|
try {
|
|
@@ -548,6 +552,12 @@ interface ChainExecutionParams {
|
|
|
548
552
|
toolBudget?: ResolvedToolBudget;
|
|
549
553
|
usageBudget?: UsageBudgetConfig;
|
|
550
554
|
configToolBudget?: ToolBudgetConfig;
|
|
555
|
+
/** Optional per-call hard toolTimeoutMs override (highest precedence). */
|
|
556
|
+
callToolTimeoutMs?: number;
|
|
557
|
+
/** Global config.toolTimeoutMs (third precedence, after agent frontmatter). */
|
|
558
|
+
configToolTimeoutMs?: number;
|
|
559
|
+
/** PI_SUBAGENT_TOOL_TIMEOUT_MS override (lowest precedence). */
|
|
560
|
+
toolTimeoutMsEnv?: string | undefined;
|
|
551
561
|
permissions?: PermissionConfig;
|
|
552
562
|
/** Global cap on simultaneously-running tasks within this chain. Defaults to DEFAULT_GLOBAL_CONCURRENCY_LIMIT. */
|
|
553
563
|
globalConcurrencyLimit?: number;
|
|
@@ -881,6 +891,8 @@ ${step.message}` : ""}` }],
|
|
|
881
891
|
maxSubagentDepth: params.maxSubagentDepth,
|
|
882
892
|
timeoutMs: params.timeoutMs,
|
|
883
893
|
deadlineAt,
|
|
894
|
+
callToolTimeoutMs: params.callToolTimeoutMs,
|
|
895
|
+
configToolTimeoutMs: params.configToolTimeoutMs,
|
|
884
896
|
turnBudget: params.turnBudget,
|
|
885
897
|
usageBudget: params.usageBudget,
|
|
886
898
|
onDetachedExit,
|
|
@@ -1156,6 +1168,8 @@ ${step.message}` : ""}` }],
|
|
|
1156
1168
|
maxSubagentDepth: params.maxSubagentDepth,
|
|
1157
1169
|
timeoutMs: params.timeoutMs,
|
|
1158
1170
|
deadlineAt,
|
|
1171
|
+
callToolTimeoutMs: params.callToolTimeoutMs,
|
|
1172
|
+
configToolTimeoutMs: params.configToolTimeoutMs,
|
|
1159
1173
|
turnBudget: params.turnBudget,
|
|
1160
1174
|
usageBudget: params.usageBudget,
|
|
1161
1175
|
onDetachedExit,
|
|
@@ -1440,6 +1454,8 @@ ${step.message}` : ""}` }],
|
|
|
1440
1454
|
onEffectivePrompt: foregroundControl ? (prompt) => updateLiveEffectivePrompt(foregroundControl, childIndex, prompt) : undefined,
|
|
1441
1455
|
timeoutMs: params.timeoutMs,
|
|
1442
1456
|
deadlineAt,
|
|
1457
|
+
toolTimeoutMs: params.callToolTimeoutMs,
|
|
1458
|
+
configToolTimeoutMs: params.configToolTimeoutMs,
|
|
1443
1459
|
turnBudget: params.turnBudget,
|
|
1444
1460
|
onDetachedExit: (result) => {
|
|
1445
1461
|
try {
|
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
buildControlEvent,
|
|
39
39
|
claimControlNotification,
|
|
40
40
|
deriveActivityState,
|
|
41
|
+
shouldEmitOpenToolAttention,
|
|
41
42
|
shouldNotifyControlEvent,
|
|
42
43
|
} from "../shared/subagent-control.ts";
|
|
43
44
|
import {
|
|
@@ -53,10 +54,12 @@ import {
|
|
|
53
54
|
} from "../../shared/utils.ts";
|
|
54
55
|
import { buildSkillInjection, resolveSkillsWithFallback } from "../../agents/skills.ts";
|
|
55
56
|
import { buildAgentMemoryInjection } from "../../agents/agent-memory.ts";
|
|
57
|
+
import { effectiveToolTimeoutMs, formatToolTimeoutMessage, resolveToolTimeoutMs, toolTimeoutCallKey, toolTimeoutFromEnv } from "../shared/tool-timeout.ts";
|
|
56
58
|
import { evaluateCompletionMutationGuard } from "../shared/completion-guard.ts";
|
|
57
59
|
import { arbitrateCompletionGuardRescue } from "../shared/llm-intent-arbiter.ts";
|
|
58
60
|
import { getPiSpawnCommand } from "../shared/pi-spawn.ts";
|
|
59
61
|
import { createJsonlWriter } from "../../shared/jsonl-writer.ts";
|
|
62
|
+
import { createOrcaProgressTab, type OrcaProgressTab } from "../shared/orca-progress-tabs.ts";
|
|
60
63
|
import { attachPostExitStdioGuard, trySignalChild } from "../../shared/post-exit-stdio-guard.ts";
|
|
61
64
|
import { resolvePermissionRules } from "../shared/permissions.ts";
|
|
62
65
|
import { applyThinkingSuffix, buildPiArgs, cleanupTempDir, projectLaunchResolvedChildExtensions, resolvePiLaunchToolPlan, type SubagentTaskDelivery } from "../shared/pi-args.ts";
|
|
@@ -74,6 +77,7 @@ import {
|
|
|
74
77
|
} from "../shared/model-fallback.ts";
|
|
75
78
|
import {
|
|
76
79
|
SUBAGENT_STARTUP_RETRY_DELAYS_MS,
|
|
80
|
+
formatSubagentExtensionConflictError,
|
|
77
81
|
formatSubagentStartupRetryExhaustedError,
|
|
78
82
|
formatSubagentStartupRetryNote,
|
|
79
83
|
isRetryableSubagentStartupFailure,
|
|
@@ -302,6 +306,7 @@ async function runSingleAttempt(
|
|
|
302
306
|
outputSnapshot?: SingleOutputSnapshot;
|
|
303
307
|
originalTask?: string;
|
|
304
308
|
taskDelivery?: SubagentTaskDelivery;
|
|
309
|
+
orcaProgressTab?: OrcaProgressTab;
|
|
305
310
|
},
|
|
306
311
|
): Promise<SingleResult> {
|
|
307
312
|
const effectiveThinking = options.thinkingOverride ?? agent.thinking;
|
|
@@ -670,6 +675,7 @@ async function runSingleAttempt(
|
|
|
670
675
|
clearWatchdogTailTimer();
|
|
671
676
|
clearStdioGuard();
|
|
672
677
|
clearTimeoutTimers();
|
|
678
|
+
clearAllToolTimeouts();
|
|
673
679
|
clearTurnBudgetTimers();
|
|
674
680
|
if (protocolHardKillTimer) {
|
|
675
681
|
clearTimeout(protocolHardKillTimer);
|
|
@@ -694,6 +700,64 @@ async function runSingleAttempt(
|
|
|
694
700
|
|
|
695
701
|
let activeLongRunningNotified = false;
|
|
696
702
|
let pendingToolResult: { tool: string; path?: string; mutates: boolean; startedAt?: number } | undefined;
|
|
703
|
+
type ActiveToolCall = { key: string; tool: string; args: string; startedAt: number; path?: string };
|
|
704
|
+
let activeToolSequence = 0;
|
|
705
|
+
const activeToolCalls = new Map<string, ActiveToolCall>();
|
|
706
|
+
const activeToolKeysByName = new Map<string, string[]>();
|
|
707
|
+
const latestActiveToolCall = (): ActiveToolCall | undefined => [...activeToolCalls.values()].sort((left, right) => right.startedAt - left.startedAt)[0];
|
|
708
|
+
const refreshCurrentTool = (): void => {
|
|
709
|
+
const active = latestActiveToolCall();
|
|
710
|
+
if (!active) {
|
|
711
|
+
progress.currentTool = undefined;
|
|
712
|
+
progress.currentToolArgs = undefined;
|
|
713
|
+
progress.currentToolStartedAt = undefined;
|
|
714
|
+
progress.currentPath = undefined;
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
progress.currentTool = active.tool;
|
|
718
|
+
progress.currentToolArgs = active.args;
|
|
719
|
+
progress.currentToolStartedAt = active.startedAt;
|
|
720
|
+
progress.currentPath = active.path;
|
|
721
|
+
};
|
|
722
|
+
const recordActiveToolCall = (event: { toolCallId?: unknown; toolName: string }, args: Record<string, unknown>, now: number): ActiveToolCall => {
|
|
723
|
+
const key = toolTimeoutCallKey(event, ++activeToolSequence);
|
|
724
|
+
const path = resolveCurrentPath(event.toolName, args);
|
|
725
|
+
const active: ActiveToolCall = {
|
|
726
|
+
key,
|
|
727
|
+
tool: event.toolName,
|
|
728
|
+
args: extractToolArgsPreview(args),
|
|
729
|
+
startedAt: now,
|
|
730
|
+
...(path !== undefined ? { path } : {}),
|
|
731
|
+
};
|
|
732
|
+
activeToolCalls.set(key, active);
|
|
733
|
+
const keys = activeToolKeysByName.get(active.tool) ?? [];
|
|
734
|
+
keys.push(key);
|
|
735
|
+
activeToolKeysByName.set(active.tool, keys);
|
|
736
|
+
refreshCurrentTool();
|
|
737
|
+
return active;
|
|
738
|
+
};
|
|
739
|
+
const removeActiveToolCallKey = (key: string): ActiveToolCall | undefined => {
|
|
740
|
+
const active = activeToolCalls.get(key);
|
|
741
|
+
if (!active) return undefined;
|
|
742
|
+
activeToolCalls.delete(key);
|
|
743
|
+
const keys = activeToolKeysByName.get(active.tool)?.filter((candidate) => candidate !== key) ?? [];
|
|
744
|
+
if (keys.length > 0) activeToolKeysByName.set(active.tool, keys);
|
|
745
|
+
else activeToolKeysByName.delete(active.tool);
|
|
746
|
+
return active;
|
|
747
|
+
};
|
|
748
|
+
const removeActiveToolCall = (event: { toolCallId?: unknown; toolName?: unknown }): ActiveToolCall | undefined => {
|
|
749
|
+
const key = typeof event.toolCallId === "string" && event.toolCallId.length > 0
|
|
750
|
+
? `id:${event.toolCallId}`
|
|
751
|
+
: typeof event.toolName === "string"
|
|
752
|
+
? activeToolKeysByName.get(event.toolName)?.[0]
|
|
753
|
+
: activeToolCalls.size === 1
|
|
754
|
+
? [...activeToolCalls.keys()][0]
|
|
755
|
+
: undefined;
|
|
756
|
+
return key ? removeActiveToolCallKey(key) : undefined;
|
|
757
|
+
};
|
|
758
|
+
const openToolAttentionTarget = (now: number): ActiveToolCall | undefined => [...activeToolCalls.values()]
|
|
759
|
+
.filter((active) => shouldEmitOpenToolAttention({ config: controlConfig, currentTool: active.tool, currentToolStartedAt: active.startedAt, now }))
|
|
760
|
+
.sort((left, right) => left.startedAt - right.startedAt)[0];
|
|
697
761
|
const mutatingFailures = createMutatingFailureState();
|
|
698
762
|
const mutatingFailureWindowMs = 5 * 60_000;
|
|
699
763
|
const currentToolDurationMs = (now: number) => progress.currentToolStartedAt ? Math.max(0, now - progress.currentToolStartedAt) : undefined;
|
|
@@ -811,6 +875,17 @@ async function runSingleAttempt(
|
|
|
811
875
|
if (idleState === "needs_attention") {
|
|
812
876
|
return progress.activityState === "needs_attention" ? false : emitNeedsAttention(now);
|
|
813
877
|
}
|
|
878
|
+
const toolAttentionTarget = progress.activityState !== "needs_attention" ? openToolAttentionTarget(now) : undefined;
|
|
879
|
+
if (toolAttentionTarget) {
|
|
880
|
+
const durationMs = Math.max(0, now - toolAttentionTarget.startedAt);
|
|
881
|
+
return emitNeedsAttention(now, {
|
|
882
|
+
message: `${agent.name} has had tool '${toolAttentionTarget.tool}' open for ${Math.floor(durationMs / 1000)}s`,
|
|
883
|
+
reason: "tool_open_threshold",
|
|
884
|
+
currentTool: toolAttentionTarget.tool,
|
|
885
|
+
currentPath: toolAttentionTarget.path,
|
|
886
|
+
currentToolDurationMs: durationMs,
|
|
887
|
+
});
|
|
888
|
+
}
|
|
814
889
|
const activeReason = nextLongRunningTrigger(controlConfig, {
|
|
815
890
|
startedAt: startTime,
|
|
816
891
|
now,
|
|
@@ -848,16 +923,18 @@ async function runSingleAttempt(
|
|
|
848
923
|
const processLine = (line: string) => {
|
|
849
924
|
if (!line.trim()) return;
|
|
850
925
|
jsonlWriter.writeLine(line);
|
|
851
|
-
let evt: { type?: string; message?: Message; toolName?: string; args?: unknown; willRetry?: unknown };
|
|
926
|
+
let evt: { type?: string; message?: Message; toolName?: string; toolCallId?: string; args?: unknown; willRetry?: unknown };
|
|
852
927
|
try {
|
|
853
|
-
evt = JSON.parse(line) as { type?: string; message?: Message; toolName?: string; args?: unknown; willRetry?: unknown };
|
|
928
|
+
evt = JSON.parse(line) as { type?: string; message?: Message; toolName?: string; toolCallId?: string; args?: unknown; willRetry?: unknown };
|
|
854
929
|
} catch {
|
|
855
930
|
rawStdoutTail.push(`${line}\n`);
|
|
856
931
|
shared.transcriptWriter?.writeStdoutLine(line);
|
|
932
|
+
shared.orcaProgressTab?.append(`${line}\n`);
|
|
857
933
|
// Non-JSON stdout lines are expected; only structured events are parsed.
|
|
858
934
|
return;
|
|
859
935
|
}
|
|
860
936
|
shared.transcriptWriter?.writeChildEvent(evt);
|
|
937
|
+
shared.orcaProgressTab?.event(evt);
|
|
861
938
|
if (evt.type === "agent_settled") agentSettledReceived = true;
|
|
862
939
|
applyChildLifecycle(projectChildLifecycle(evt));
|
|
863
940
|
|
|
@@ -892,6 +969,8 @@ async function runSingleAttempt(
|
|
|
892
969
|
const toolArgs = evt.args && typeof evt.args === "object" && !Array.isArray(evt.args)
|
|
893
970
|
? evt.args as Record<string, unknown>
|
|
894
971
|
: {};
|
|
972
|
+
const activeTool = evt.toolName !== undefined ? recordActiveToolCall(evt as { toolCallId?: unknown; toolName: string }, toolArgs, now) : undefined;
|
|
973
|
+
if (evt.toolName !== undefined) armToolTimeout(evt as { toolCallId?: unknown; toolName: string });
|
|
895
974
|
if (options.structuredOutput && evt.toolName === "structured_output") {
|
|
896
975
|
structuredOutputToolInvoked = true;
|
|
897
976
|
structuredOutputMessageStartIndex = result.messages?.length ?? 0;
|
|
@@ -903,28 +982,23 @@ async function runSingleAttempt(
|
|
|
903
982
|
if (options.toolBudget) {
|
|
904
983
|
result.toolBudget = toolBudgetState(options.toolBudget, progress.toolCount);
|
|
905
984
|
}
|
|
906
|
-
progress.currentTool = evt.toolName;
|
|
907
|
-
progress.currentToolArgs = extractToolArgsPreview(toolArgs);
|
|
908
|
-
progress.currentToolStartedAt = now;
|
|
909
|
-
progress.currentPath = resolveCurrentPath(evt.toolName, toolArgs);
|
|
910
985
|
const mutates = isMutatingTool(evt.toolName, toolArgs);
|
|
911
986
|
observedMutationAttempt = observedMutationAttempt || mutates;
|
|
912
|
-
pendingToolResult = { tool: evt.toolName ?? "tool", path:
|
|
987
|
+
pendingToolResult = { tool: evt.toolName ?? "tool", path: activeTool?.path, mutates, startedAt: now };
|
|
913
988
|
fireUpdate();
|
|
914
989
|
}
|
|
915
990
|
|
|
916
991
|
if (evt.type === "tool_execution_end") {
|
|
917
|
-
|
|
992
|
+
clearActiveToolTimeout(evt);
|
|
993
|
+
const endedTool = removeActiveToolCall(evt);
|
|
994
|
+
if (endedTool) {
|
|
918
995
|
progress.recentTools.push({
|
|
919
|
-
tool:
|
|
920
|
-
args:
|
|
996
|
+
tool: endedTool.tool,
|
|
997
|
+
args: endedTool.args,
|
|
921
998
|
endMs: now,
|
|
922
999
|
});
|
|
923
1000
|
}
|
|
924
|
-
|
|
925
|
-
progress.currentToolArgs = undefined;
|
|
926
|
-
progress.currentToolStartedAt = undefined;
|
|
927
|
-
progress.currentPath = undefined;
|
|
1001
|
+
refreshCurrentTool();
|
|
928
1002
|
fireUpdate();
|
|
929
1003
|
}
|
|
930
1004
|
|
|
@@ -1022,6 +1096,7 @@ async function runSingleAttempt(
|
|
|
1022
1096
|
timeoutTimer = setTimeout(() => {
|
|
1023
1097
|
if (processClosed || lifecycleFinished || interruptedByControl) return;
|
|
1024
1098
|
result.timedOut = true;
|
|
1099
|
+
clearAllToolTimeouts();
|
|
1025
1100
|
result.error = attemptTimeout.message;
|
|
1026
1101
|
result.finalOutput = attemptTimeout.message;
|
|
1027
1102
|
progress.status = "failed";
|
|
@@ -1043,6 +1118,81 @@ async function runSingleAttempt(
|
|
|
1043
1118
|
timeoutTimer.unref?.();
|
|
1044
1119
|
}
|
|
1045
1120
|
|
|
1121
|
+
let toolTimeoutSequence = 0;
|
|
1122
|
+
const activeToolTimeouts = new Map<string, { toolName: string; timer: ReturnType<typeof setTimeout> }>();
|
|
1123
|
+
const activeToolTimeoutKeysByName = new Map<string, string[]>();
|
|
1124
|
+
let toolTimeoutTerminationTimer: ReturnType<typeof setTimeout> | undefined;
|
|
1125
|
+
let toolTimeoutHardKillTimer: ReturnType<typeof setTimeout> | undefined;
|
|
1126
|
+
const removeToolTimeoutKey = (key: string): void => {
|
|
1127
|
+
const active = activeToolTimeouts.get(key);
|
|
1128
|
+
if (!active) return;
|
|
1129
|
+
clearTimeout(active.timer);
|
|
1130
|
+
activeToolTimeouts.delete(key);
|
|
1131
|
+
const keys = activeToolTimeoutKeysByName.get(active.toolName)?.filter((candidate) => candidate !== key) ?? [];
|
|
1132
|
+
if (keys.length > 0) activeToolTimeoutKeysByName.set(active.toolName, keys);
|
|
1133
|
+
else activeToolTimeoutKeysByName.delete(active.toolName);
|
|
1134
|
+
};
|
|
1135
|
+
const clearActiveToolTimeout = (event: { toolCallId?: unknown; toolName?: unknown }): void => {
|
|
1136
|
+
const key = typeof event.toolCallId === "string" && event.toolCallId.length > 0
|
|
1137
|
+
? `id:${event.toolCallId}`
|
|
1138
|
+
: typeof event.toolName === "string"
|
|
1139
|
+
? activeToolTimeoutKeysByName.get(event.toolName)?.[0]
|
|
1140
|
+
: activeToolTimeouts.size === 1
|
|
1141
|
+
? [...activeToolTimeouts.keys()][0]
|
|
1142
|
+
: undefined;
|
|
1143
|
+
if (key) removeToolTimeoutKey(key);
|
|
1144
|
+
};
|
|
1145
|
+
const clearAllToolTimeouts = (): void => {
|
|
1146
|
+
for (const key of [...activeToolTimeouts.keys()]) removeToolTimeoutKey(key);
|
|
1147
|
+
if (toolTimeoutTerminationTimer) {
|
|
1148
|
+
clearTimeout(toolTimeoutTerminationTimer);
|
|
1149
|
+
toolTimeoutTerminationTimer = undefined;
|
|
1150
|
+
}
|
|
1151
|
+
if (toolTimeoutHardKillTimer) {
|
|
1152
|
+
clearTimeout(toolTimeoutHardKillTimer);
|
|
1153
|
+
toolTimeoutHardKillTimer = undefined;
|
|
1154
|
+
}
|
|
1155
|
+
};
|
|
1156
|
+
const terminateForToolTimeout = (message: string): void => {
|
|
1157
|
+
if (processClosed || lifecycleFinished || interruptedByControl) return;
|
|
1158
|
+
result.timedOut = true;
|
|
1159
|
+
result.error = message;
|
|
1160
|
+
result.finalOutput = message;
|
|
1161
|
+
progress.status = "failed";
|
|
1162
|
+
progress.error = message;
|
|
1163
|
+
progress.durationMs = Date.now() - startTime;
|
|
1164
|
+
fireUpdate();
|
|
1165
|
+
trySignalChild(proc, "SIGINT");
|
|
1166
|
+
toolTimeoutTerminationTimer = setTimeout(() => {
|
|
1167
|
+
if (processClosed || lifecycleFinished) return;
|
|
1168
|
+
trySignalChild(proc, "SIGTERM");
|
|
1169
|
+
}, 1000);
|
|
1170
|
+
toolTimeoutTerminationTimer.unref?.();
|
|
1171
|
+
toolTimeoutHardKillTimer = setTimeout(() => {
|
|
1172
|
+
if (processClosed || lifecycleFinished) return;
|
|
1173
|
+
trySignalChild(proc, "SIGKILL");
|
|
1174
|
+
}, 4000);
|
|
1175
|
+
toolTimeoutHardKillTimer.unref?.();
|
|
1176
|
+
};
|
|
1177
|
+
const armToolTimeout = (event: { toolCallId?: unknown; toolName: string }): void => {
|
|
1178
|
+
const timeoutForTool = effectiveToolTimeoutMs(event.toolName, options.toolTimeoutMs);
|
|
1179
|
+
if (timeoutForTool === undefined) return;
|
|
1180
|
+
const elapsed = Date.now() - startTime;
|
|
1181
|
+
const runRemaining = attemptTimeout ? Math.max(0, attemptTimeout.remainingMs - elapsed) : undefined;
|
|
1182
|
+
if (runRemaining !== undefined && timeoutForTool >= runRemaining) return;
|
|
1183
|
+
const key = toolTimeoutCallKey(event, ++toolTimeoutSequence);
|
|
1184
|
+
const toolName = event.toolName;
|
|
1185
|
+
const timer = setTimeout(() => {
|
|
1186
|
+
removeToolTimeoutKey(key);
|
|
1187
|
+
terminateForToolTimeout(formatToolTimeoutMessage(toolName, timeoutForTool));
|
|
1188
|
+
}, timeoutForTool);
|
|
1189
|
+
timer.unref?.();
|
|
1190
|
+
activeToolTimeouts.set(key, { toolName, timer });
|
|
1191
|
+
const keys = activeToolTimeoutKeysByName.get(toolName) ?? [];
|
|
1192
|
+
keys.push(key);
|
|
1193
|
+
activeToolTimeoutKeysByName.set(toolName, keys);
|
|
1194
|
+
};
|
|
1195
|
+
|
|
1046
1196
|
const stderrTail = createBoundedByteTail();
|
|
1047
1197
|
const failProtocol = (limit: ProtocolOutputLimit): void => {
|
|
1048
1198
|
if (result.protocolError) return;
|
|
@@ -1077,6 +1227,7 @@ async function runSingleAttempt(
|
|
|
1077
1227
|
proc.stderr.on("data", (chunk: Buffer) => {
|
|
1078
1228
|
stderrTail.push(chunk);
|
|
1079
1229
|
stderrReader.push(chunk);
|
|
1230
|
+
shared.orcaProgressTab?.append(chunk.toString("utf-8"));
|
|
1080
1231
|
});
|
|
1081
1232
|
proc.on("exit", () => {
|
|
1082
1233
|
childExited = true;
|
|
@@ -1156,6 +1307,7 @@ async function runSingleAttempt(
|
|
|
1156
1307
|
if (result.timedOut) return;
|
|
1157
1308
|
interruptedByControl = true;
|
|
1158
1309
|
clearTimeoutTimers();
|
|
1310
|
+
clearAllToolTimeouts();
|
|
1159
1311
|
progress.status = "running";
|
|
1160
1312
|
progress.durationMs = Date.now() - startTime;
|
|
1161
1313
|
result.interrupted = true;
|
|
@@ -1201,6 +1353,10 @@ async function runSingleAttempt(
|
|
|
1201
1353
|
};
|
|
1202
1354
|
return result;
|
|
1203
1355
|
}
|
|
1356
|
+
result.error = formatSubagentExtensionConflictError(result.error, {
|
|
1357
|
+
agent: agent.name,
|
|
1358
|
+
ambientExtensionsEnabled: !launchResolvedExtensions.disableAmbientExtensions,
|
|
1359
|
+
});
|
|
1204
1360
|
if (result.error && result.exitCode === 0) {
|
|
1205
1361
|
result.exitCode = 1;
|
|
1206
1362
|
}
|
|
@@ -1378,7 +1534,7 @@ async function runSingleAttempt(
|
|
|
1378
1534
|
/**
|
|
1379
1535
|
* Run a subagent synchronously (blocking until complete)
|
|
1380
1536
|
*/
|
|
1381
|
-
async function
|
|
1537
|
+
async function runSyncCompletionInner(
|
|
1382
1538
|
runtimeCwd: string,
|
|
1383
1539
|
agents: AgentConfig[],
|
|
1384
1540
|
agentName: string,
|
|
@@ -1427,6 +1583,24 @@ async function runSyncCompletion(
|
|
|
1427
1583
|
error: acceptanceErrors.join(" "),
|
|
1428
1584
|
}, options.context));
|
|
1429
1585
|
}
|
|
1586
|
+
const toolTimeout = resolveToolTimeoutMs({
|
|
1587
|
+
callValue: options.toolTimeoutMs,
|
|
1588
|
+
agentValue: agent.defaultToolTimeoutMs,
|
|
1589
|
+
configValue: options.configToolTimeoutMs,
|
|
1590
|
+
envValue: toolTimeoutFromEnv(),
|
|
1591
|
+
});
|
|
1592
|
+
if (toolTimeout.error) {
|
|
1593
|
+
return redactResultPrompt(withRunContext({
|
|
1594
|
+
index: options.index ?? 0,
|
|
1595
|
+
agent: agentName,
|
|
1596
|
+
task,
|
|
1597
|
+
exitCode: 1,
|
|
1598
|
+
messages: [],
|
|
1599
|
+
usage: emptyUsage(),
|
|
1600
|
+
error: toolTimeout.error,
|
|
1601
|
+
}, options.context));
|
|
1602
|
+
}
|
|
1603
|
+
options = { ...options, toolTimeoutMs: toolTimeout.toolTimeoutMs };
|
|
1430
1604
|
const outputModeValidationError = validateFileOnlyOutputMode(options.outputMode, options.outputPath, `Single run (${agentName})`);
|
|
1431
1605
|
if (outputModeValidationError) {
|
|
1432
1606
|
return redactResultPrompt(withRunContext({
|
|
@@ -1531,6 +1705,14 @@ async function runSyncCompletion(
|
|
|
1531
1705
|
}
|
|
1532
1706
|
}
|
|
1533
1707
|
|
|
1708
|
+
const orcaProgressTab = createOrcaProgressTab({
|
|
1709
|
+
cwd: options.cwd ?? runtimeCwd,
|
|
1710
|
+
runId: options.runId,
|
|
1711
|
+
agent: agentName,
|
|
1712
|
+
index: options.index ?? 0,
|
|
1713
|
+
});
|
|
1714
|
+
if (orcaProgressTab) options.onOrcaProgressTabCreated?.(orcaProgressTab);
|
|
1715
|
+
|
|
1534
1716
|
const persistResultMetadata = (target: SingleResult): void => {
|
|
1535
1717
|
persistSingleResultMetadata({
|
|
1536
1718
|
metadataPath: artifactPathsResult?.metadataPath,
|
|
@@ -1585,6 +1767,7 @@ async function runSyncCompletion(
|
|
|
1585
1767
|
outputSnapshot,
|
|
1586
1768
|
originalTask: task,
|
|
1587
1769
|
taskDelivery: taskDeliveryOverride,
|
|
1770
|
+
orcaProgressTab,
|
|
1588
1771
|
});
|
|
1589
1772
|
lastResult = result;
|
|
1590
1773
|
if (startupAttemptIndex === 0) {
|
|
@@ -1813,6 +1996,27 @@ async function runSyncCompletion(
|
|
|
1813
1996
|
return result;
|
|
1814
1997
|
}
|
|
1815
1998
|
|
|
1999
|
+
async function runSyncCompletion(
|
|
2000
|
+
runtimeCwd: string,
|
|
2001
|
+
agents: AgentConfig[],
|
|
2002
|
+
agentName: string,
|
|
2003
|
+
task: string,
|
|
2004
|
+
options: RunSyncOptions,
|
|
2005
|
+
): Promise<SingleResult> {
|
|
2006
|
+
let orcaProgressTab: OrcaProgressTab | undefined;
|
|
2007
|
+
try {
|
|
2008
|
+
const result = await runSyncCompletionInner(runtimeCwd, agents, agentName, task, {
|
|
2009
|
+
...options,
|
|
2010
|
+
onOrcaProgressTabCreated: (tab) => { orcaProgressTab = tab; },
|
|
2011
|
+
});
|
|
2012
|
+
orcaProgressTab?.finish(result.stopped ? "stopped" : result.exitCode === 0 && !result.error ? "completed" : "failed", result.sessionFile);
|
|
2013
|
+
return result;
|
|
2014
|
+
} catch (error) {
|
|
2015
|
+
orcaProgressTab?.finish("failed");
|
|
2016
|
+
throw error;
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
|
|
1816
2020
|
/**
|
|
1817
2021
|
* Runs the authoritative completion pipeline independently from the foreground
|
|
1818
2022
|
* receipt. Detachment is same-runtime only: it does not adopt or daemonize work.
|