@selesai/code 0.8.6 → 0.8.7
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 +16 -0
- package/dist/core/agent-session.js +13 -1
- package/dist/core/extensions/types.d.ts +2 -0
- package/dist/core/provider-composer.js +17 -4
- package/dist/core/settings-manager.d.ts +2 -0
- package/dist/core/settings-manager.js +3 -0
- package/dist/core/system-prompt.js +2 -8
- package/dist/core/system-prompt.test.js +6 -0
- package/dist/core/tools/index.d.ts +1 -0
- package/dist/core/tools/index.js +1 -0
- package/dist/core/tools/schema-prune.d.ts +16 -0
- package/dist/core/tools/schema-prune.js +39 -0
- package/dist/core/tools/schema-prune.test.d.ts +1 -0
- package/dist/core/tools/schema-prune.test.js +111 -0
- package/dist/defaults/settings.json +1 -0
- package/dist/extensions/model-prompt-injector/config.json +10 -0
- package/dist/extensions/model-prompt-injector/index.test.ts +112 -0
- package/dist/extensions/model-prompt-injector/index.ts +151 -0
- package/dist/extensions/package.json +1 -0
- package/dist/extensions/pi-subagents/CHANGELOG.md +158 -90
- package/dist/extensions/pi-subagents/UPSTREAM-V0.50-MAPPING.md +172 -0
- package/dist/extensions/pi-subagents/agents/oracle.md +7 -5
- package/dist/extensions/pi-subagents/agents/reviewer.md +2 -2
- package/dist/extensions/pi-subagents/agents/scout.md +1 -1
- package/dist/extensions/pi-subagents/agents/worker.md +1 -1
- package/dist/extensions/pi-subagents/docs/configuration.md +78 -19
- package/dist/extensions/pi-subagents/docs/extension-api.md +36 -0
- package/dist/extensions/pi-subagents/docs/missions.md +5 -3
- package/dist/extensions/pi-subagents/docs/models.md +1 -1
- package/dist/extensions/pi-subagents/docs/observability.md +42 -2
- package/dist/extensions/pi-subagents/docs/tool-reference.md +20 -3
- package/dist/extensions/pi-subagents/docs/workflows.md +5 -5
- package/dist/extensions/pi-subagents/install.mjs +2 -2
- package/dist/extensions/pi-subagents/package-lock.json +1470 -1472
- package/dist/extensions/pi-subagents/package.json +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +5 -4
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +15 -14
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +5 -5
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +46 -26
- package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +2 -0
- package/dist/extensions/pi-subagents/src/agents/agents.ts +37 -12
- package/dist/extensions/pi-subagents/src/api/external-runs.ts +174 -84
- package/dist/extensions/pi-subagents/src/api/preflight.ts +13 -7
- package/dist/extensions/pi-subagents/src/extension/config.ts +53 -0
- package/dist/extensions/pi-subagents/src/extension/index.ts +67 -13
- package/dist/extensions/pi-subagents/src/extension/public-execution.ts +34 -6
- package/dist/extensions/pi-subagents/src/extension/rpc.ts +5 -1
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +31 -30
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +10 -9
- package/dist/extensions/pi-subagents/src/inspectors/herdr/actions.ts +13 -8
- package/dist/extensions/pi-subagents/src/inspectors/herdr/inspector-runner.ts +16 -3
- package/dist/extensions/pi-subagents/src/inspectors/herdr/project-panes.ts +2 -6
- package/dist/extensions/pi-subagents/src/inspectors/herdr/shell-command.ts +16 -0
- package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +5 -4
- package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +19 -42
- package/dist/extensions/pi-subagents/src/missions/goal-driver.ts +3 -1
- package/dist/extensions/pi-subagents/src/missions/store.ts +8 -3
- package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +82 -25
- package/dist/extensions/pi-subagents/src/runs/background/active-run-index.ts +71 -1
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +73 -43
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +5 -0
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +14 -6
- package/dist/extensions/pi-subagents/src/runs/background/async-status-snapshot.ts +277 -0
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +8 -3
- package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +2 -2
- package/dist/extensions/pi-subagents/src/runs/background/completion-replay.ts +11 -1
- package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +21 -6
- package/dist/extensions/pi-subagents/src/runs/background/result-files.ts +437 -0
- package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +205 -41
- package/dist/extensions/pi-subagents/src/runs/background/resume-guidance.ts +27 -7
- package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +76 -19
- package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +30 -24
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +101 -4
- package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +54 -28
- package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +27 -13
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +298 -33
- package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +2 -0
- package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +5 -2
- package/dist/extensions/pi-subagents/src/runs/foreground/async-dismiss-action.ts +2 -1
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +16 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +219 -15
- package/dist/extensions/pi-subagents/src/runs/foreground/prompt-audit.ts +4 -3
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +333 -45
- package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +107 -1
- package/dist/extensions/pi-subagents/src/runs/shared/external-cli-runner.ts +4 -0
- package/dist/extensions/pi-subagents/src/runs/shared/llm-intent-arbiter.ts +39 -23
- package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +16 -2
- package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +66 -62
- package/dist/extensions/pi-subagents/src/runs/shared/orca-progress-tabs.ts +376 -0
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +2 -0
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +15 -0
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +1 -9
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +12 -0
- package/dist/extensions/pi-subagents/src/runs/shared/tool-timeout.ts +95 -0
- package/dist/extensions/pi-subagents/src/shared/agent-stream-options.ts +5 -0
- package/dist/extensions/pi-subagents/src/shared/artifacts.ts +0 -4
- package/dist/extensions/pi-subagents/src/shared/display-text.ts +50 -0
- package/dist/extensions/pi-subagents/src/shared/node-executable.ts +21 -0
- package/dist/extensions/pi-subagents/src/shared/types.ts +50 -4
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +34 -25
- package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +4 -0
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +160 -45
- package/dist/extensions/pi-subagents/src/tui/fleet-transcript.ts +1 -48
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +128 -16
- package/dist/extensions/pi-subagents/src/tui/render.ts +122 -44
- package/dist/extensions/pi-subagents/src/watchdog/permission-arbiter.ts +2 -1
- package/dist/extensions/pi-subagents/src/watchdog/review.ts +4 -3
- package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +10 -2
- package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +272 -76
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/types.d.ts +2 -0
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +68 -9
- package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +27 -1
- package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +49 -0
- package/dist/extensions/pi-subagents/test/integration/external-cli-runner.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +26 -1
- package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +144 -0
- package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +26 -0
- package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +369 -98
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +111 -78
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +49 -1
- package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +12 -0
- package/dist/extensions/pi-subagents/test/support/node-command.ts +15 -0
- package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +50 -0
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +28 -0
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +61 -0
- package/dist/extensions/pi-subagents/test/unit/agent-stream-options.test.ts +14 -0
- package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +76 -0
- package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +2 -0
- package/dist/extensions/pi-subagents/test/unit/async-status-snapshot.test.ts +162 -0
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +209 -10
- package/dist/extensions/pi-subagents/test/unit/completion-replay.test.ts +41 -1
- package/dist/extensions/pi-subagents/test/unit/external-runs.test.ts +135 -42
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +252 -1
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +192 -6
- package/dist/extensions/pi-subagents/test/unit/herdr-inspector-bootstrap.test.ts +32 -0
- package/dist/extensions/pi-subagents/test/unit/herdr-shell-command.test.ts +59 -0
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +69 -0
- package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +23 -4
- package/dist/extensions/pi-subagents/test/unit/llm-intent-arbiter.test.ts +56 -1
- package/dist/extensions/pi-subagents/test/unit/mission-goal-driver.test.ts +67 -3
- package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/mission-store.test.ts +14 -0
- package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +18 -0
- package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +25 -2
- package/dist/extensions/pi-subagents/test/unit/node-executable.test.ts +26 -0
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +8 -0
- package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +348 -0
- package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +5 -2
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +34 -0
- package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +6 -1
- package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +92 -0
- package/dist/extensions/pi-subagents/test/unit/result-files.test.ts +175 -0
- package/dist/extensions/pi-subagents/test/unit/retained-children.test.ts +127 -17
- package/dist/extensions/pi-subagents/test/unit/run-id-resolver.test.ts +44 -0
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +247 -0
- package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +107 -2
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +5 -8
- package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +456 -4
- package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +128 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +28 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +16 -7
- package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +15 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +79 -15
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +12 -12
- package/dist/extensions/pi-subagents/test/unit/tool-timeout.test.ts +109 -0
- package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +34 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +94 -1
- package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +42 -2
- package/dist/extensions/question/index.ts +21 -6
- package/dist/extensions/question/question-list.ts +7 -1
- package/dist/extensions/question/row-layout.ts +3 -0
- package/dist/extensions/question/tests/exp.test.ts +70 -0
- package/dist/extensions/question/tests/exp2.test.ts +59 -0
- package/dist/extensions/question/tests/exp3.test.ts +47 -0
- package/dist/extensions/question/tests/helpers.test.ts +7 -0
- package/dist/extensions/question/tests/question-list.test.ts +140 -0
- package/dist/extensions/question/tests/row-layout.test.ts +13 -0
- package/dist/extensions/question/tests/selection-mode.test.ts +54 -0
- package/dist/extensions/question/tests/zz-ig3.test.ts +20 -0
- package/dist/extensions/question/tests/zz-probe.test.ts +29 -0
- package/dist/skills/pi-subagents/SKILL.md +1 -1
- package/dist/skills/pi-subagents/references/constraints-and-recipes.md +5 -4
- package/dist/skills/pi-subagents/references/execution-controls.md +15 -14
- package/dist/skills/pi-subagents/references/management-authoring-rpc.md +5 -5
- package/dist/skills/pi-subagents/references/prompting-and-roles.md +46 -26
- package/docs/settings.md +6 -0
- package/package.json +3 -3
|
@@ -5,6 +5,7 @@ import * as path from "node:path";
|
|
|
5
5
|
import { pathToFileURL } from "node:url";
|
|
6
6
|
import type { Message } from "@earendil-works/pi-ai";
|
|
7
7
|
import { writeAtomicJson } from "../../shared/atomic-json.ts";
|
|
8
|
+
import { writeAsyncResultFile, writePendingAsyncResultFile } from "./result-files.ts";
|
|
8
9
|
import { createFileCoalescer } from "../../shared/file-coalescer.ts";
|
|
9
10
|
import { isActiveAsyncState, updateActiveRunIndex } from "./active-run-index.ts";
|
|
10
11
|
import { createChildTranscriptWriter, type ChildTranscriptWriter } from "../../shared/child-transcript.ts";
|
|
@@ -57,6 +58,7 @@ import {
|
|
|
57
58
|
claimControlNotification,
|
|
58
59
|
formatControlIntercomMessage,
|
|
59
60
|
formatControlNoticeMessage,
|
|
61
|
+
shouldEmitOpenToolAttention,
|
|
60
62
|
} from "../shared/subagent-control.ts";
|
|
61
63
|
import {
|
|
62
64
|
type RunnerSubagentStep as SubagentStep,
|
|
@@ -83,6 +85,7 @@ import { nestedSummaryFromAsyncStatus, projectNestedEvents, resolveNestedAsyncDi
|
|
|
83
85
|
import { formatModelAttemptNote, formatModelRetryNote, isRetryableModelFailure, MODEL_RETRY_DELAYS_MS } from "../shared/model-fallback.ts";
|
|
84
86
|
import {
|
|
85
87
|
SUBAGENT_STARTUP_RETRY_DELAYS_MS,
|
|
88
|
+
formatSubagentExtensionConflictError,
|
|
86
89
|
formatSubagentStartupRetryExhaustedError,
|
|
87
90
|
formatSubagentStartupRetryNote,
|
|
88
91
|
isRetryableSubagentStartupFailure,
|
|
@@ -126,12 +129,14 @@ import { waitForImportedAsyncRoot } from "./chain-root-attachment.ts";
|
|
|
126
129
|
import { appendRunnerStepsToStatus, consumeChainAppendRequests, countPendingChainAppendRequests, statusStepDescription } from "./chain-append.ts";
|
|
127
130
|
import { appendTurnBudgetSystemPrompt, formatTurnBudgetOutput, initialTurnBudgetState, turnBudgetDecision, turnBudgetDeferredNote, turnBudgetDeferredState, turnBudgetExceededMessage, turnBudgetSoftNote, turnBudgetState } from "../shared/turn-budget.ts";
|
|
128
131
|
import { initialToolBudgetState, toolBudgetState } from "../shared/tool-budget.ts";
|
|
132
|
+
import { effectiveToolTimeoutMs, formatToolTimeoutMessage, toolTimeoutCallKey } from "../shared/tool-timeout.ts";
|
|
129
133
|
import { usageBudgetExceededMessage, usageBudgetState } from "../shared/usage-budget.ts";
|
|
130
134
|
import { formatParallelHandoffError, formatParallelHandoffReference, parallelHandoffPath, writeParallelHandoffGroup, writePendingParallelHandoff } from "../shared/parallel-handoff.ts";
|
|
131
135
|
import { resolveWatchdogConfig } from "../../watchdog/settings.ts";
|
|
132
136
|
import { createBoundedByteTail, createBoundedLineReader, formatProtocolOutputLimit, MAX_CHILD_STDERR_BYTES, PI_AGGREGATE_EVENT_PROJECTOR, projectChildLifecycle, type ChildLifecycleAction, type ProtocolOutputLimit } from "../shared/child-protocol.ts";
|
|
133
137
|
import { acquireSessionLease, type SessionLeaseRequest } from "../shared/session-lease.ts";
|
|
134
138
|
import { buildExternalCliPrompt, runExternalCli } from "../shared/external-cli-runner.ts";
|
|
139
|
+
import { createOrcaProgressTab, type OrcaProgressTab } from "../shared/orca-progress-tabs.ts";
|
|
135
140
|
import { decodeSubagentCapabilityCeiling, SUBAGENT_CAPABILITY_CEILING_ENV, type ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
|
|
136
141
|
import {
|
|
137
142
|
CHILD_WATCHDOG_CONFIG_ENV,
|
|
@@ -143,6 +148,8 @@ import {
|
|
|
143
148
|
type ChildWatchdogStateSnapshot,
|
|
144
149
|
} from "../../watchdog/child-status.ts";
|
|
145
150
|
|
|
151
|
+
const INTERCOM_DETACH_RECEIPT = "Detached for intercom coordination before task completion.";
|
|
152
|
+
|
|
146
153
|
interface SubagentRunConfig {
|
|
147
154
|
id: string;
|
|
148
155
|
steps: RunnerStep[];
|
|
@@ -174,6 +181,8 @@ interface SubagentRunConfig {
|
|
|
174
181
|
nestedSelf?: { parentRunId: string; parentStepIndex?: number; depth: number; path?: Array<{ runId: string; stepIndex?: number; agent?: string }> };
|
|
175
182
|
timeoutMs?: number;
|
|
176
183
|
deadlineAt?: number;
|
|
184
|
+
/** Resolved configured hard per-tool-call timeout (ms); fast tools still have a default when undefined. */
|
|
185
|
+
toolTimeoutMs?: number;
|
|
177
186
|
turnBudget?: ResolvedTurnBudget;
|
|
178
187
|
toolBudget?: ResolvedToolBudget;
|
|
179
188
|
usageBudget?: UsageBudgetConfig;
|
|
@@ -539,6 +548,9 @@ function runPiStreaming(
|
|
|
539
548
|
stopMessage?: string,
|
|
540
549
|
registerTurnBudgetAbort?: (abort: ((message: string, state?: TurnBudgetState) => void) | undefined) => void,
|
|
541
550
|
onWriterProcess?: (writer: { state: "none" | "spawning" } | { state: "running"; pid: number }) => void,
|
|
551
|
+
toolTimeoutMs?: number,
|
|
552
|
+
runDeadlineAt?: number,
|
|
553
|
+
orcaProgressTab?: OrcaProgressTab,
|
|
542
554
|
): Promise<RunPiStreamingResult> {
|
|
543
555
|
return new Promise((resolve) => {
|
|
544
556
|
const startedAt = Date.now();
|
|
@@ -595,6 +607,7 @@ function runPiStreaming(
|
|
|
595
607
|
const writeOutputLine = (line: string) => {
|
|
596
608
|
if (!line.trim()) return;
|
|
597
609
|
outputStream.write(`${line}\n`);
|
|
610
|
+
orcaProgressTab?.append(`${line}\n`);
|
|
598
611
|
};
|
|
599
612
|
|
|
600
613
|
const writeOutputText = (text: string) => {
|
|
@@ -672,8 +685,14 @@ function runPiStreaming(
|
|
|
672
685
|
|
|
673
686
|
onChildEvent?.(event);
|
|
674
687
|
|
|
688
|
+
if (event.type === "tool_execution_end") {
|
|
689
|
+
clearActiveToolTimeout(event);
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
|
|
675
693
|
if (event.type === "tool_execution_start" && event.toolName) {
|
|
676
694
|
toolCount += 1;
|
|
695
|
+
armToolTimeout({ toolCallId: (event as { toolCallId?: unknown }).toolCallId, toolName: event.toolName });
|
|
677
696
|
if (event.toolName === "structured_output") {
|
|
678
697
|
structuredOutputToolInvoked = true;
|
|
679
698
|
structuredOutputMessageStartIndex = messages.length;
|
|
@@ -769,6 +788,7 @@ function runPiStreaming(
|
|
|
769
788
|
stderrTail.push(chunk);
|
|
770
789
|
stderrReader.push(chunk);
|
|
771
790
|
outputStream.write(chunk);
|
|
791
|
+
orcaProgressTab?.append(chunk.toString("utf-8"));
|
|
772
792
|
});
|
|
773
793
|
registerInterrupt?.(() => {
|
|
774
794
|
if (settled || timedOut || stopped) return;
|
|
@@ -779,14 +799,60 @@ function runPiStreaming(
|
|
|
779
799
|
if (!settled && !timedOut && !stopped) trySignalChild(child, "SIGTERM");
|
|
780
800
|
}, 1000).unref?.();
|
|
781
801
|
});
|
|
782
|
-
|
|
802
|
+
const terminateForTimeout = (message: string): void => {
|
|
783
803
|
if (settled || timedOut || stopped) return;
|
|
784
804
|
timedOut = true;
|
|
805
|
+
// runPiStreaming's terminal result derives the timeout error from this
|
|
806
|
+
// message, so retain the tool-specific reason through finalization.
|
|
807
|
+
timeoutMessage = message;
|
|
785
808
|
interrupted = false;
|
|
786
|
-
error =
|
|
809
|
+
error = message;
|
|
787
810
|
if (processTreeController) void processTreeController.terminate();
|
|
788
811
|
else trySignalChild(child, "SIGTERM");
|
|
789
|
-
}
|
|
812
|
+
};
|
|
813
|
+
let toolTimeoutSequence = 0;
|
|
814
|
+
const activeToolTimeouts = new Map<string, { toolName: string; timer: ReturnType<typeof setTimeout> }>();
|
|
815
|
+
const activeToolTimeoutKeysByName = new Map<string, string[]>();
|
|
816
|
+
const removeToolTimeoutKey = (key: string): void => {
|
|
817
|
+
const active = activeToolTimeouts.get(key);
|
|
818
|
+
if (!active) return;
|
|
819
|
+
clearTimeout(active.timer);
|
|
820
|
+
activeToolTimeouts.delete(key);
|
|
821
|
+
const keys = activeToolTimeoutKeysByName.get(active.toolName)?.filter((candidate) => candidate !== key) ?? [];
|
|
822
|
+
if (keys.length > 0) activeToolTimeoutKeysByName.set(active.toolName, keys);
|
|
823
|
+
else activeToolTimeoutKeysByName.delete(active.toolName);
|
|
824
|
+
};
|
|
825
|
+
const clearActiveToolTimeout = (event: { toolCallId?: unknown; toolName?: unknown }): void => {
|
|
826
|
+
const key = typeof event.toolCallId === "string" && event.toolCallId.length > 0
|
|
827
|
+
? `id:${event.toolCallId}`
|
|
828
|
+
: typeof event.toolName === "string"
|
|
829
|
+
? activeToolTimeoutKeysByName.get(event.toolName)?.[0]
|
|
830
|
+
: activeToolTimeouts.size === 1
|
|
831
|
+
? [...activeToolTimeouts.keys()][0]
|
|
832
|
+
: undefined;
|
|
833
|
+
if (key) removeToolTimeoutKey(key);
|
|
834
|
+
};
|
|
835
|
+
const clearAllToolTimeouts = (): void => {
|
|
836
|
+
for (const key of [...activeToolTimeouts.keys()]) removeToolTimeoutKey(key);
|
|
837
|
+
};
|
|
838
|
+
const armToolTimeout = (event: { toolCallId?: unknown; toolName: string }): void => {
|
|
839
|
+
const timeoutForTool = effectiveToolTimeoutMs(event.toolName, toolTimeoutMs);
|
|
840
|
+
if (timeoutForTool === undefined) return;
|
|
841
|
+
const runRemaining = runDeadlineAt === undefined ? undefined : Math.max(0, runDeadlineAt - Date.now());
|
|
842
|
+
if (runRemaining !== undefined && timeoutForTool >= runRemaining) return;
|
|
843
|
+
const key = toolTimeoutCallKey(event, ++toolTimeoutSequence);
|
|
844
|
+
const toolName = event.toolName;
|
|
845
|
+
const timer = setTimeout(() => {
|
|
846
|
+
removeToolTimeoutKey(key);
|
|
847
|
+
terminateForTimeout(formatToolTimeoutMessage(toolName, timeoutForTool));
|
|
848
|
+
}, timeoutForTool);
|
|
849
|
+
timer.unref?.();
|
|
850
|
+
activeToolTimeouts.set(key, { toolName, timer });
|
|
851
|
+
const keys = activeToolTimeoutKeysByName.get(toolName) ?? [];
|
|
852
|
+
keys.push(key);
|
|
853
|
+
activeToolTimeoutKeysByName.set(toolName, keys);
|
|
854
|
+
};
|
|
855
|
+
registerTimeout?.(() => terminateForTimeout(timeoutMessage ?? "Subagent timed out."));
|
|
790
856
|
registerStop?.(() => {
|
|
791
857
|
if (settled || timedOut || stopped) return;
|
|
792
858
|
stopped = true;
|
|
@@ -813,6 +879,7 @@ function runPiStreaming(
|
|
|
813
879
|
turnBudgetHardKillTimer.unref?.();
|
|
814
880
|
});
|
|
815
881
|
const clearDrainTimers = () => {
|
|
882
|
+
clearAllToolTimeouts();
|
|
816
883
|
if (finalDrainTimer) {
|
|
817
884
|
clearTimeout(finalDrainTimer);
|
|
818
885
|
finalDrainTimer = undefined;
|
|
@@ -1102,6 +1169,10 @@ interface SingleStepContext {
|
|
|
1102
1169
|
stopSignal?: AbortSignal;
|
|
1103
1170
|
timeoutMessage?: string;
|
|
1104
1171
|
stopMessage?: string;
|
|
1172
|
+
/** Resolved configured hard per-tool-call timeout (ms); fast tools still have a default when undefined. */
|
|
1173
|
+
toolTimeoutMs?: number;
|
|
1174
|
+
/** Effective step deadline (Date.now() + effective timeout) when a run budget exists. */
|
|
1175
|
+
deadlineAt?: number;
|
|
1105
1176
|
turnBudget?: ResolvedTurnBudget;
|
|
1106
1177
|
childIntercomTarget?: string;
|
|
1107
1178
|
orchestratorIntercomTarget?: string;
|
|
@@ -1113,10 +1184,11 @@ interface SingleStepContext {
|
|
|
1113
1184
|
onWriterProcess?: (writer: { state: "none" | "spawning" } | { state: "running"; pid: number }) => void;
|
|
1114
1185
|
onExternalProcess?: (process: ExternalProcessStatus) => void;
|
|
1115
1186
|
skipAcceptance?: () => boolean;
|
|
1187
|
+
orcaProgressTab?: OrcaProgressTab;
|
|
1116
1188
|
}
|
|
1117
1189
|
|
|
1118
1190
|
/** Run a single pi agent step, returning output and metadata */
|
|
1119
|
-
async function
|
|
1191
|
+
async function runSingleStepInner(
|
|
1120
1192
|
step: SubagentStep,
|
|
1121
1193
|
ctx: SingleStepContext,
|
|
1122
1194
|
): Promise<StepResult & { completionGuardTriggered?: boolean }> {
|
|
@@ -1245,6 +1317,8 @@ async function runSingleStep(
|
|
|
1245
1317
|
timeoutMessage: ctx.timeoutMessage,
|
|
1246
1318
|
stopMessage: ctx.stopMessage,
|
|
1247
1319
|
onProcess: ctx.onExternalProcess,
|
|
1320
|
+
onStdout: (chunk) => ctx.orcaProgressTab?.append(chunk.toString("utf-8")),
|
|
1321
|
+
onStderr: (chunk) => ctx.orcaProgressTab?.append(chunk.toString("utf-8")),
|
|
1248
1322
|
}));
|
|
1249
1323
|
try { fs.writeFileSync(ctx.outputFile, external.output, "utf-8"); } catch { /* Observability output is best-effort. */ }
|
|
1250
1324
|
const resolvedOutput = step.outputPath && external.exitCode === 0
|
|
@@ -1432,6 +1506,9 @@ async function runSingleStep(
|
|
|
1432
1506
|
ctx.stopMessage,
|
|
1433
1507
|
ctx.registerTurnBudgetAbort,
|
|
1434
1508
|
ctx.onWriterProcess,
|
|
1509
|
+
ctx.toolTimeoutMs,
|
|
1510
|
+
ctx.deadlineAt,
|
|
1511
|
+
ctx.orcaProgressTab,
|
|
1435
1512
|
);
|
|
1436
1513
|
if (run.processCloseObservedAt !== undefined) {
|
|
1437
1514
|
writerProcesses.push({
|
|
@@ -1539,15 +1616,21 @@ async function runSingleStep(
|
|
|
1539
1616
|
stopped: run.stopped,
|
|
1540
1617
|
turnBudgetExceeded: run.turnBudgetExceeded,
|
|
1541
1618
|
})) ? formatProcessSignalError(run.processSignal!) : undefined;
|
|
1542
|
-
const error =
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
?
|
|
1549
|
-
|
|
1550
|
-
|
|
1619
|
+
const error = formatSubagentExtensionConflictError(
|
|
1620
|
+
toolAvailabilityError
|
|
1621
|
+
?? completionGuardError
|
|
1622
|
+
?? structuredError
|
|
1623
|
+
?? emptyOutputError
|
|
1624
|
+
?? (hiddenError?.hasError
|
|
1625
|
+
? hiddenError.details
|
|
1626
|
+
? `${hiddenError.errorType} failed (exit ${effectiveExitCode}): ${hiddenError.details}`
|
|
1627
|
+
: `${hiddenError.errorType} failed with exit code ${effectiveExitCode}`
|
|
1628
|
+
: run.error || signalError || (run.exitCode !== 0 && run.stderr.trim() ? run.stderr.trim() : undefined)),
|
|
1629
|
+
{
|
|
1630
|
+
agent: step.agent,
|
|
1631
|
+
ambientExtensionsEnabled: launchResolvedExtensions?.disableAmbientExtensions === false,
|
|
1632
|
+
},
|
|
1633
|
+
);
|
|
1551
1634
|
const attempt: ModelAttempt = omitUndefinedProperties({
|
|
1552
1635
|
model: candidate ?? run.model ?? step.model ?? "default",
|
|
1553
1636
|
success: effectiveExitCode === 0 && !error,
|
|
@@ -1716,15 +1799,16 @@ async function runSingleStep(
|
|
|
1716
1799
|
const acceptanceFailure = effectiveAcceptance ? acceptanceFailureMessage(effectiveAcceptance) : undefined;
|
|
1717
1800
|
const acceptanceCanFailRun = acceptanceFailure && effectiveAcceptance?.explicit && (finalResult?.exitCode ?? 1) === 0 && !finalResult?.interrupted && !timedOutAfterAcceptance && !stoppedAfterAcceptance && !turnBudgetExceeded && !isAgentContractV1(step.agentContract);
|
|
1718
1801
|
const effectiveFinalExitCode = timedOutAfterAcceptance || stoppedAfterAcceptance || turnBudgetExceeded ? 1 : acceptanceCanFailRun ? 1 : finalResult?.exitCode ?? 1;
|
|
1802
|
+
const intercomDetachReceipt = finalResult?.finalOutput === INTERCOM_DETACH_RECEIPT;
|
|
1719
1803
|
const effectiveFinalError = stoppedAfterAcceptance
|
|
1720
1804
|
? ctx.stopMessage ?? "Subagent stopped by user."
|
|
1721
1805
|
: timedOutAfterAcceptance
|
|
1722
|
-
? ctx.timeoutMessage ?? "Subagent timed out."
|
|
1806
|
+
? finalResult?.error ?? ctx.timeoutMessage ?? "Subagent timed out."
|
|
1723
1807
|
: turnBudgetExceeded
|
|
1724
1808
|
? finalResult?.error ?? (turnBudget ? turnBudgetExceededMessage(turnBudget, turnBudget.turnCount) : "Subagent exceeded turn budget.")
|
|
1725
1809
|
: acceptanceCanFailRun
|
|
1726
1810
|
? (finalResult?.error ? `${finalResult.error}\n${acceptanceFailure}` : acceptanceFailure)
|
|
1727
|
-
: finalResult?.error;
|
|
1811
|
+
: finalResult?.error ?? (intercomDetachReceipt ? INTERCOM_DETACH_RECEIPT : undefined);
|
|
1728
1812
|
|
|
1729
1813
|
const artifactErrors = artifactPaths && ctx.artifactConfig?.enabled !== false
|
|
1730
1814
|
? persistStepArtifacts({
|
|
@@ -1804,6 +1888,27 @@ async function runSingleStep(
|
|
|
1804
1888
|
return isAgentContractV1(step.agentContract) ? attachContractProjections(result as unknown as import("../../shared/types.ts").SingleResult) as unknown as typeof result : result;
|
|
1805
1889
|
}
|
|
1806
1890
|
|
|
1891
|
+
async function runSingleStep(
|
|
1892
|
+
step: SubagentStep,
|
|
1893
|
+
ctx: SingleStepContext,
|
|
1894
|
+
): Promise<StepResult & { completionGuardTriggered?: boolean }> {
|
|
1895
|
+
if (step.importAsyncRoot) return runSingleStepInner(step, ctx);
|
|
1896
|
+
const orcaProgressTab = createOrcaProgressTab({
|
|
1897
|
+
cwd: step.cwd ?? ctx.cwd,
|
|
1898
|
+
runId: ctx.id,
|
|
1899
|
+
agent: step.agent,
|
|
1900
|
+
index: ctx.flatIndex,
|
|
1901
|
+
});
|
|
1902
|
+
try {
|
|
1903
|
+
const result = await runSingleStepInner(step, { ...ctx, orcaProgressTab });
|
|
1904
|
+
orcaProgressTab?.finish(result.stopped ? "stopped" : result.exitCode === 0 && !result.error ? "completed" : "failed", result.sessionFile);
|
|
1905
|
+
return result;
|
|
1906
|
+
} catch (error) {
|
|
1907
|
+
orcaProgressTab?.finish("failed");
|
|
1908
|
+
throw error;
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1807
1912
|
type RunnerStatusStep = NonNullable<AsyncStatus["steps"]>[number] & {
|
|
1808
1913
|
exitCode?: number | null;
|
|
1809
1914
|
description?: string;
|
|
@@ -2028,6 +2133,7 @@ async function runSingleStepWithTimeout(
|
|
|
2028
2133
|
return await runSingleStep(step, {
|
|
2029
2134
|
...ctx,
|
|
2030
2135
|
registerTimeout,
|
|
2136
|
+
deadlineAt: Date.now() + timeoutMs,
|
|
2031
2137
|
timeoutSignal: combinedAbortSignal([ctx.timeoutSignal, timeoutController.signal]),
|
|
2032
2138
|
timeoutMessage,
|
|
2033
2139
|
});
|
|
@@ -2222,7 +2328,7 @@ async function runSubagent(
|
|
|
2222
2328
|
|
|
2223
2329
|
fs.mkdirSync(asyncDir, { recursive: true });
|
|
2224
2330
|
writeAtomicJson(statusPath, statusPayload);
|
|
2225
|
-
updateActiveRunIndex(asyncDir, statusPayload.state);
|
|
2331
|
+
updateActiveRunIndex(asyncDir, statusPayload.state, statusPayload.toolCallId);
|
|
2226
2332
|
let pendingParallelUsageCost: CostSummary = { inputTokens: 0, outputTokens: 0, costUsd: 0 };
|
|
2227
2333
|
const currentUsageTotals = (): CostSummary => {
|
|
2228
2334
|
const cost = results.reduce<CostSummary>((sum, result) => ({
|
|
@@ -2295,13 +2401,70 @@ async function runSubagent(
|
|
|
2295
2401
|
for (const node of graph.nodes) updateNode(node);
|
|
2296
2402
|
statusPayload.workflowGraph = graph;
|
|
2297
2403
|
};
|
|
2404
|
+
let finalResultCommitted = false;
|
|
2298
2405
|
let lastIndexedActiveState = isActiveAsyncState(statusPayload.state);
|
|
2406
|
+
const statusResultState = (): AsyncStatus["state"] | undefined => {
|
|
2407
|
+
if (statusPayload.state === "running" || statusPayload.state === "queued") return undefined;
|
|
2408
|
+
if (statusPayload.state === "paused" && statusPayload.checkpoint?.status === "pending") return undefined;
|
|
2409
|
+
return statusPayload.state;
|
|
2410
|
+
};
|
|
2411
|
+
const statusResultSummary = (state: AsyncStatus["state"]): string => {
|
|
2412
|
+
if (statusPayload.error) return statusPayload.error;
|
|
2413
|
+
if (state === "paused") return "Paused after interrupt. Waiting for explicit next action.";
|
|
2414
|
+
if (state === "stopped") return stopMessage;
|
|
2415
|
+
if (state === "rejected") return statusPayload.checkpoint?.name ? `Checkpoint '${statusPayload.checkpoint.name}' rejected.` : "Subagent rejected.";
|
|
2416
|
+
return state === "complete" ? "Subagent completed." : "Subagent failed.";
|
|
2417
|
+
};
|
|
2418
|
+
const statusResultSuccess = (state: AsyncStatus["state"], step: RunnerStatusStep): boolean | undefined => {
|
|
2419
|
+
if (step.status === "complete" || step.status === "completed") return true;
|
|
2420
|
+
if (step.status === "failed" || step.status === "stopped" || step.status === "rejected") return false;
|
|
2421
|
+
if (state === "complete") return true;
|
|
2422
|
+
if (state === "failed" || state === "stopped" || state === "rejected") return false;
|
|
2423
|
+
return undefined;
|
|
2424
|
+
};
|
|
2425
|
+
const writeRecoverableStatusResult = (): void => {
|
|
2426
|
+
const state = statusResultState();
|
|
2427
|
+
if (!state || finalResultCommitted || !config.sessionId) return;
|
|
2428
|
+
const now = statusPayload.endedAt ?? statusPayload.lastUpdate ?? Date.now();
|
|
2429
|
+
const summary = statusResultSummary(state);
|
|
2430
|
+
writePendingAsyncResultFile(resultPath, omitUndefinedProperties({
|
|
2431
|
+
lifecycleArtifactVersion: SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
|
|
2432
|
+
id,
|
|
2433
|
+
runId: id,
|
|
2434
|
+
agent: statusPayload.steps.length === 1 ? statusPayload.steps[0]!.agent : statusPayload.mode === "parallel" ? `parallel:${statusPayload.steps.map((step) => step.agent).join("+")}` : `chain:${statusPayload.steps.map((step) => step.agent).join("->")}`,
|
|
2435
|
+
mode: statusPayload.mode,
|
|
2436
|
+
success: state === "complete",
|
|
2437
|
+
state,
|
|
2438
|
+
summary,
|
|
2439
|
+
error: state === "failed" || state === "stopped" || state === "rejected" ? summary : undefined,
|
|
2440
|
+
stopped: state === "stopped" ? true : undefined,
|
|
2441
|
+
checkpoint: statusPayload.checkpoint,
|
|
2442
|
+
results: statusPayload.steps.map((step) => omitUndefinedProperties({
|
|
2443
|
+
agent: step.agent,
|
|
2444
|
+
output: step.status === "complete" || step.status === "completed" ? "" : step.error ?? summary,
|
|
2445
|
+
error: step.error,
|
|
2446
|
+
success: statusResultSuccess(state, step),
|
|
2447
|
+
sessionFile: step.sessionFile,
|
|
2448
|
+
model: step.model,
|
|
2449
|
+
attemptedModels: step.attemptedModels,
|
|
2450
|
+
modelAttempts: step.modelAttempts,
|
|
2451
|
+
})),
|
|
2452
|
+
exitCode: state === "complete" || state === "paused" ? 0 : 1,
|
|
2453
|
+
timestamp: now,
|
|
2454
|
+
durationMs: Math.max(0, now - overallStartTime),
|
|
2455
|
+
asyncDir,
|
|
2456
|
+
cwd,
|
|
2457
|
+
sessionId: config.sessionId,
|
|
2458
|
+
sessionFile: statusPayload.sessionFile ?? latestSessionFile,
|
|
2459
|
+
}));
|
|
2460
|
+
};
|
|
2299
2461
|
const writeStatusPayloadNow = (): void => {
|
|
2300
2462
|
refreshWorkflowGraph();
|
|
2463
|
+
writeRecoverableStatusResult();
|
|
2301
2464
|
writeAtomicJson(statusPath, statusPayload);
|
|
2302
2465
|
const activeState = isActiveAsyncState(statusPayload.state);
|
|
2303
|
-
if (activeState
|
|
2304
|
-
updateActiveRunIndex(asyncDir, statusPayload.state);
|
|
2466
|
+
if (activeState !== lastIndexedActiveState) {
|
|
2467
|
+
updateActiveRunIndex(asyncDir, statusPayload.state, statusPayload.toolCallId);
|
|
2305
2468
|
}
|
|
2306
2469
|
lastIndexedActiveState = activeState;
|
|
2307
2470
|
emitNestedSelfEvent(statusPayload.state === "running" || statusPayload.state === "queued" ? "subagent.nested.updated" : "subagent.nested.completed");
|
|
@@ -2546,6 +2709,72 @@ async function runSubagent(
|
|
|
2546
2709
|
const activeLongRunningSteps = new Set<number>();
|
|
2547
2710
|
const mutatingFailureStates = initialStatusSteps.map(() => createMutatingFailureState());
|
|
2548
2711
|
const pendingToolResults: Array<{ tool: string; path?: string; mutates: boolean; startedAt?: number } | undefined> = initialStatusSteps.map(() => undefined);
|
|
2712
|
+
type ActiveToolCall = { key: string; tool: string; args: string; startedAt: number; path?: string; blocksSupervisor: boolean };
|
|
2713
|
+
const activeToolCalls = initialStatusSteps.map(() => new Map<string, ActiveToolCall>());
|
|
2714
|
+
const activeToolKeysByName = initialStatusSteps.map(() => new Map<string, string[]>());
|
|
2715
|
+
const activeToolSequences = initialStatusSteps.map(() => 0);
|
|
2716
|
+
const latestActiveToolCall = (flatIndex: number): ActiveToolCall | undefined => [...(activeToolCalls[flatIndex]?.values() ?? [])].sort((left, right) => right.startedAt - left.startedAt)[0];
|
|
2717
|
+
const refreshStepCurrentTool = (flatIndex: number): void => {
|
|
2718
|
+
const step = statusPayload.steps[flatIndex];
|
|
2719
|
+
if (!step) return;
|
|
2720
|
+
const active = latestActiveToolCall(flatIndex);
|
|
2721
|
+
if (!active) {
|
|
2722
|
+
delete step.currentTool;
|
|
2723
|
+
delete step.currentToolArgs;
|
|
2724
|
+
delete step.currentToolStartedAt;
|
|
2725
|
+
delete step.currentPath;
|
|
2726
|
+
return;
|
|
2727
|
+
}
|
|
2728
|
+
step.currentTool = active.tool;
|
|
2729
|
+
step.currentToolArgs = active.args;
|
|
2730
|
+
step.currentToolStartedAt = active.startedAt;
|
|
2731
|
+
setOptionalProperty(step, "currentPath", active.path);
|
|
2732
|
+
};
|
|
2733
|
+
const recordActiveToolCall = (flatIndex: number, event: { toolCallId?: unknown; toolName: string }, input: { argsPreview: string; currentPath?: string; blocksSupervisor: boolean; now: number }): ActiveToolCall => {
|
|
2734
|
+
const sequence = (activeToolSequences[flatIndex] ?? 0) + 1;
|
|
2735
|
+
activeToolSequences[flatIndex] = sequence;
|
|
2736
|
+
const key = toolTimeoutCallKey(event, sequence);
|
|
2737
|
+
const active: ActiveToolCall = {
|
|
2738
|
+
key,
|
|
2739
|
+
tool: event.toolName,
|
|
2740
|
+
args: input.argsPreview,
|
|
2741
|
+
startedAt: input.now,
|
|
2742
|
+
blocksSupervisor: input.blocksSupervisor,
|
|
2743
|
+
...(input.currentPath !== undefined ? { path: input.currentPath } : {}),
|
|
2744
|
+
};
|
|
2745
|
+
activeToolCalls[flatIndex]?.set(key, active);
|
|
2746
|
+
const keysByName = activeToolKeysByName[flatIndex];
|
|
2747
|
+
const keys = keysByName?.get(active.tool) ?? [];
|
|
2748
|
+
keys.push(key);
|
|
2749
|
+
keysByName?.set(active.tool, keys);
|
|
2750
|
+
refreshStepCurrentTool(flatIndex);
|
|
2751
|
+
return active;
|
|
2752
|
+
};
|
|
2753
|
+
const removeActiveToolCallKey = (flatIndex: number, key: string): ActiveToolCall | undefined => {
|
|
2754
|
+
const calls = activeToolCalls[flatIndex];
|
|
2755
|
+
const active = calls?.get(key);
|
|
2756
|
+
if (!active) return undefined;
|
|
2757
|
+
calls?.delete(key);
|
|
2758
|
+
const keysByName = activeToolKeysByName[flatIndex];
|
|
2759
|
+
const keys = keysByName?.get(active.tool)?.filter((candidate) => candidate !== key) ?? [];
|
|
2760
|
+
if (keys.length > 0) keysByName?.set(active.tool, keys);
|
|
2761
|
+
else keysByName?.delete(active.tool);
|
|
2762
|
+
return active;
|
|
2763
|
+
};
|
|
2764
|
+
const removeActiveToolCall = (flatIndex: number, event: { toolCallId?: unknown; toolName?: unknown }): ActiveToolCall | undefined => {
|
|
2765
|
+
const calls = activeToolCalls[flatIndex];
|
|
2766
|
+
const key = typeof event.toolCallId === "string" && event.toolCallId.length > 0
|
|
2767
|
+
? `id:${event.toolCallId}`
|
|
2768
|
+
: typeof event.toolName === "string"
|
|
2769
|
+
? activeToolKeysByName[flatIndex]?.get(event.toolName)?.[0]
|
|
2770
|
+
: calls?.size === 1
|
|
2771
|
+
? [...calls.keys()][0]
|
|
2772
|
+
: undefined;
|
|
2773
|
+
return key ? removeActiveToolCallKey(flatIndex, key) : undefined;
|
|
2774
|
+
};
|
|
2775
|
+
const openToolAttentionTarget = (flatIndex: number, now: number): ActiveToolCall | undefined => [...(activeToolCalls[flatIndex]?.values() ?? [])]
|
|
2776
|
+
.filter((active) => shouldEmitOpenToolAttention({ config: controlConfig, currentTool: active.tool, currentToolStartedAt: active.startedAt, now }))
|
|
2777
|
+
.sort((left, right) => left.startedAt - right.startedAt)[0];
|
|
2549
2778
|
const supervisorAttentionSteps = new Map<number, ActivityState | undefined>();
|
|
2550
2779
|
const mutatingFailureWindowMs = 5 * 60_000;
|
|
2551
2780
|
const appendControlEvent = (event: ReturnType<typeof buildControlEvent>) => {
|
|
@@ -2586,6 +2815,34 @@ async function runSubagent(
|
|
|
2586
2815
|
currentActivityState = nextRunState;
|
|
2587
2816
|
setOptionalProperty(statusPayload, "activityState", nextRunState);
|
|
2588
2817
|
};
|
|
2818
|
+
const maybeEmitOpenToolAttention = (flatIndex: number, now: number): boolean => {
|
|
2819
|
+
const step = statusPayload.steps[flatIndex];
|
|
2820
|
+
if (!step || step.status !== "running" || step.activityState === "needs_attention") return false;
|
|
2821
|
+
const target = openToolAttentionTarget(flatIndex, now);
|
|
2822
|
+
if (!target) return false;
|
|
2823
|
+
const previous = step.activityState;
|
|
2824
|
+
step.activityState = "needs_attention";
|
|
2825
|
+
statusPayload.activityState = "needs_attention";
|
|
2826
|
+
const toolDurationMs = Math.max(0, now - target.startedAt);
|
|
2827
|
+
appendControlEvent(buildControlEvent(omitUndefinedProperties({
|
|
2828
|
+
type: "needs_attention",
|
|
2829
|
+
from: previous,
|
|
2830
|
+
to: "needs_attention",
|
|
2831
|
+
runId: id,
|
|
2832
|
+
agent: step.agent,
|
|
2833
|
+
index: flatIndex,
|
|
2834
|
+
ts: now,
|
|
2835
|
+
message: `${step.agent} has had tool '${target.tool}' open for ${Math.floor(toolDurationMs / 1000)}s`,
|
|
2836
|
+
reason: "tool_open_threshold",
|
|
2837
|
+
turns: step.turnCount,
|
|
2838
|
+
tokens: step.tokens?.total,
|
|
2839
|
+
toolCount: step.toolCount,
|
|
2840
|
+
currentTool: target.tool,
|
|
2841
|
+
currentToolDurationMs: toolDurationMs,
|
|
2842
|
+
currentPath: target.path,
|
|
2843
|
+
})));
|
|
2844
|
+
return true;
|
|
2845
|
+
};
|
|
2589
2846
|
const maybeEmitActiveLongRunning = (flatIndex: number, now: number): boolean => {
|
|
2590
2847
|
if (!controlConfig.enabled || activeLongRunningSteps.has(flatIndex)) return false;
|
|
2591
2848
|
const step = statusPayload.steps[flatIndex];
|
|
@@ -2846,20 +3103,19 @@ async function runSubagent(
|
|
|
2846
3103
|
if (event.type === "tool_execution_start" && event.toolName) {
|
|
2847
3104
|
const mutates = isMutatingTool(event.toolName, event.args);
|
|
2848
3105
|
const currentPath = resolveCurrentPath(event.toolName, event.args);
|
|
3106
|
+
const argsPreview = extractToolArgsPreview(event.args ?? {});
|
|
3107
|
+
const blocksSupervisor = isBlockingSupervisorTool(event.toolName, event.args);
|
|
2849
3108
|
step.toolCount = (step.toolCount ?? 0) + 1;
|
|
2850
3109
|
const configuredToolBudget = flatSteps[flatIndex]?.toolBudget;
|
|
2851
3110
|
if (configuredToolBudget) {
|
|
2852
3111
|
step.toolBudget = toolBudgetState(configuredToolBudget, step.toolCount);
|
|
2853
3112
|
statusPayload.toolBudget = step.toolBudget;
|
|
2854
3113
|
}
|
|
2855
|
-
|
|
2856
|
-
step.currentToolArgs = extractToolArgsPreview(event.args ?? {});
|
|
2857
|
-
step.currentToolStartedAt = now;
|
|
2858
|
-
setOptionalProperty(step, "currentPath", currentPath);
|
|
3114
|
+
recordActiveToolCall(flatIndex, { toolCallId: (event as { toolCallId?: unknown }).toolCallId, toolName: event.toolName }, { argsPreview, currentPath, blocksSupervisor, now });
|
|
2859
3115
|
pendingToolResults[flatIndex] = omitUndefinedProperties({ tool: event.toolName, path: currentPath, mutates, startedAt: now });
|
|
2860
3116
|
statusPayload.toolCount = (statusPayload.toolCount ?? 0) + 1;
|
|
2861
3117
|
syncTopLevelCurrentTool();
|
|
2862
|
-
if (controlConfig.enabled &&
|
|
3118
|
+
if (controlConfig.enabled && blocksSupervisor && step.activityState !== "needs_attention") {
|
|
2863
3119
|
const previous = step.activityState;
|
|
2864
3120
|
step.activityState = "needs_attention";
|
|
2865
3121
|
supervisorAttentionSteps.set(flatIndex, previous);
|
|
@@ -2884,16 +3140,15 @@ async function runSubagent(
|
|
|
2884
3140
|
})));
|
|
2885
3141
|
}
|
|
2886
3142
|
} else if (event.type === "tool_execution_end") {
|
|
2887
|
-
|
|
3143
|
+
const endedTool = removeActiveToolCall(flatIndex, event);
|
|
3144
|
+
if (endedTool) {
|
|
2888
3145
|
step.recentTools ??= [];
|
|
2889
|
-
step.recentTools.push({ tool:
|
|
3146
|
+
step.recentTools.push({ tool: endedTool.tool, args: endedTool.args, endMs: now });
|
|
2890
3147
|
}
|
|
3148
|
+
refreshStepCurrentTool(flatIndex);
|
|
2891
3149
|
const supervisorPreviousActivity = supervisorAttentionSteps.get(flatIndex);
|
|
2892
|
-
const
|
|
2893
|
-
delete
|
|
2894
|
-
delete step.currentToolArgs;
|
|
2895
|
-
delete step.currentToolStartedAt;
|
|
2896
|
-
delete step.currentPath;
|
|
3150
|
+
const stillBlockingSupervisor = [...(activeToolCalls[flatIndex]?.values() ?? [])].some((active) => active.blocksSupervisor);
|
|
3151
|
+
const clearedSupervisorAttention = endedTool?.blocksSupervisor && !stillBlockingSupervisor ? supervisorAttentionSteps.delete(flatIndex) : false;
|
|
2897
3152
|
if (clearedSupervisorAttention && step.activityState === "needs_attention") {
|
|
2898
3153
|
setOptionalProperty(step, "activityState", supervisorPreviousActivity);
|
|
2899
3154
|
syncAggregateActivityState();
|
|
@@ -3013,6 +3268,8 @@ async function runSubagent(
|
|
|
3013
3268
|
})));
|
|
3014
3269
|
changed = true;
|
|
3015
3270
|
}
|
|
3271
|
+
} else if (maybeEmitOpenToolAttention(index, now)) {
|
|
3272
|
+
changed = true;
|
|
3016
3273
|
} else if (maybeEmitActiveLongRunning(index, now)) {
|
|
3017
3274
|
changed = true;
|
|
3018
3275
|
}
|
|
@@ -3575,6 +3832,7 @@ async function runSubagent(
|
|
|
3575
3832
|
timeoutMessage,
|
|
3576
3833
|
stopMessage,
|
|
3577
3834
|
turnBudget: config.turnBudget,
|
|
3835
|
+
toolTimeoutMs: task.toolTimeoutMs ?? config.toolTimeoutMs,
|
|
3578
3836
|
onAttemptStart: (attempt) => updateStepModel(fi, attempt.model, attempt.thinking),
|
|
3579
3837
|
onChildEvent: (event) => updateStepFromChildEvent(fi, event),
|
|
3580
3838
|
onWriterProcess,
|
|
@@ -3958,6 +4216,7 @@ async function runSubagent(
|
|
|
3958
4216
|
timeoutMessage,
|
|
3959
4217
|
stopMessage,
|
|
3960
4218
|
turnBudget: config.turnBudget,
|
|
4219
|
+
toolTimeoutMs: taskForRun.toolTimeoutMs ?? config.toolTimeoutMs,
|
|
3961
4220
|
onAttemptStart: (attempt) => updateStepModel(fi, attempt.model, attempt.thinking),
|
|
3962
4221
|
onChildEvent: (event) => updateStepFromChildEvent(fi, event),
|
|
3963
4222
|
onWriterProcess,
|
|
@@ -4248,6 +4507,7 @@ async function runSubagent(
|
|
|
4248
4507
|
timeoutMessage,
|
|
4249
4508
|
stopMessage,
|
|
4250
4509
|
turnBudget: config.turnBudget,
|
|
4510
|
+
toolTimeoutMs: seqStep.toolTimeoutMs ?? config.toolTimeoutMs,
|
|
4251
4511
|
onAttemptStart: (attempt) => updateStepModel(flatIndex, attempt.model, attempt.thinking),
|
|
4252
4512
|
onChildEvent: (event) => updateStepFromChildEvent(flatIndex, event),
|
|
4253
4513
|
onWriterProcess,
|
|
@@ -4545,9 +4805,8 @@ async function runSubagent(
|
|
|
4545
4805
|
statusPayload.error = `Step failed: ${failedStep.agent}`;
|
|
4546
4806
|
}
|
|
4547
4807
|
}
|
|
4548
|
-
writeStatusPayload();
|
|
4549
4808
|
try {
|
|
4550
|
-
|
|
4809
|
+
writeAsyncResultFile(resultPath, {
|
|
4551
4810
|
lifecycleArtifactVersion: SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
|
|
4552
4811
|
id,
|
|
4553
4812
|
agent: agentName,
|
|
@@ -4642,9 +4901,15 @@ async function runSubagent(
|
|
|
4642
4901
|
...(taskIndex !== undefined && { taskIndex }),
|
|
4643
4902
|
...(totalTasks !== undefined && { totalTasks }),
|
|
4644
4903
|
});
|
|
4904
|
+
finalResultCommitted = true;
|
|
4645
4905
|
} catch (err) {
|
|
4646
|
-
|
|
4906
|
+
const message = `Failed to write result file ${resultPath}: ${err instanceof Error ? err.message : String(err)}`;
|
|
4907
|
+
console.error(message, err);
|
|
4908
|
+
statusPayload.state = "failed";
|
|
4909
|
+
statusPayload.error = message;
|
|
4910
|
+
statusPayload.lastUpdate = Date.now();
|
|
4647
4911
|
}
|
|
4912
|
+
writeStatusPayload();
|
|
4648
4913
|
appendJsonl(
|
|
4649
4914
|
eventsPath,
|
|
4650
4915
|
JSON.stringify({
|
|
@@ -267,6 +267,7 @@ function activeRunsForSession(params: SubagentWaitParams, deps: SubagentWaitDeps
|
|
|
267
267
|
resultsDir,
|
|
268
268
|
kill: deps.kill,
|
|
269
269
|
now: deps.now,
|
|
270
|
+
includeNested: false,
|
|
270
271
|
...(params.id ? { runId: params.id } : {}),
|
|
271
272
|
});
|
|
272
273
|
return params.id ? runs.filter((run) => matchesId(run, params.id!)) : runs;
|
|
@@ -286,6 +287,7 @@ function allRunsForSession(params: SubagentWaitParams, deps: SubagentWaitDeps):
|
|
|
286
287
|
resultsDir,
|
|
287
288
|
kill: deps.kill,
|
|
288
289
|
now: deps.now,
|
|
290
|
+
includeNested: false,
|
|
289
291
|
...(params.id ? { runId: params.id } : {}),
|
|
290
292
|
});
|
|
291
293
|
return params.id ? runs.filter((run) => matchesId(run, params.id!)) : runs;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
|
-
import * as path from "node:path";
|
|
3
2
|
import type { ArtifactPaths, SubagentState, WaitCompletion, WaitCompletionChild } from "../../shared/types.ts";
|
|
4
3
|
import type { AsyncRunSummary } from "./async-status.ts";
|
|
5
4
|
import { readCompletionReplay, writeCompletionReplay } from "./completion-replay.ts";
|
|
5
|
+
import { resultFilePath, resultPayloadPathForSessionRun } from "./result-files.ts";
|
|
6
6
|
|
|
7
7
|
function asNonEmptyString(value: unknown): string | undefined {
|
|
8
8
|
return typeof value === "string" && value ? value : undefined;
|
|
@@ -114,7 +114,10 @@ export function collectWaitCompletions(terminal: AsyncRunSummary[], state: Subag
|
|
|
114
114
|
completions.push(recorded.completion);
|
|
115
115
|
continue;
|
|
116
116
|
}
|
|
117
|
-
const
|
|
117
|
+
const publicResultPath = resultFilePath(resultsDir, run.id);
|
|
118
|
+
const resultPath = run.sessionId
|
|
119
|
+
? resultPayloadPathForSessionRun(resultsDir, run.sessionId, run.id) ?? publicResultPath
|
|
120
|
+
: publicResultPath;
|
|
118
121
|
try {
|
|
119
122
|
const raw = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as Record<string, unknown>;
|
|
120
123
|
completions.push(toWaitCompletion(raw, run.id));
|
|
@@ -6,6 +6,7 @@ import { DIRS, type Details, type SubagentState } from "../../shared/types.ts";
|
|
|
6
6
|
import { readStatus } from "../../shared/utils.ts";
|
|
7
7
|
import { updateActiveRunIndex } from "../background/active-run-index.ts";
|
|
8
8
|
import { reconcileAsyncRun } from "../background/stale-run-reconciler.ts";
|
|
9
|
+
import { resultFilePath } from "../background/result-files.ts";
|
|
9
10
|
|
|
10
11
|
export function dismissRecoveredWorkflow(
|
|
11
12
|
state: SubagentState,
|
|
@@ -50,7 +51,7 @@ export function dismissRecoveredWorkflow(
|
|
|
50
51
|
};
|
|
51
52
|
}
|
|
52
53
|
let latestStatus = status;
|
|
53
|
-
const resultPath =
|
|
54
|
+
const resultPath = resultFilePath(DIRS.results, status.runId);
|
|
54
55
|
if (fs.existsSync(resultPath)) {
|
|
55
56
|
const reconciled = reconcileAsyncRun(asyncDir).status;
|
|
56
57
|
if (reconciled && reconciled.state !== "running") {
|