@duckmind/dm-windows-x64 0.60.4 → 0.60.8
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/dm.exe +0 -0
- package/extensions/.dm-extensions.json +70 -123
- package/extensions/dm-9router-ext/src/index.js +410 -5
- package/extensions/dm-caveman/extensions/caveman.js +283 -12
- package/extensions/dm-cliproxy/index.js +182 -2
- package/extensions/dm-cliproxy/scripts/check-config-migration.js +66 -8
- package/extensions/dm-cliproxy/src/apply.js +228 -1
- package/extensions/dm-cliproxy/src/cache.js +42 -1
- package/extensions/dm-cliproxy/src/commands.js +50 -2
- package/extensions/dm-cliproxy/src/compat.js +81 -1
- package/extensions/dm-cliproxy/src/config.js +192 -2
- package/extensions/dm-cliproxy/src/conflicts.js +46 -1
- package/extensions/dm-cliproxy/src/fetch-models.js +190 -1
- package/extensions/dm-cliproxy/src/fetch-usage.js +41 -1
- package/extensions/dm-cliproxy/src/log.js +23 -1
- package/extensions/dm-cliproxy/src/status-quota.js +77 -1
- package/extensions/dm-cliproxy/src/ui-frame.js +50 -1
- package/extensions/dm-cliproxy/src/ui-hub/hub.js +199 -2
- package/extensions/dm-cliproxy/src/ui-hub/index.js +26 -2
- package/extensions/dm-cliproxy/src/ui-hub/shell.js +46 -1
- package/extensions/dm-cliproxy/src/ui-hub/view-diagnostics.js +69 -1
- package/extensions/dm-cliproxy/src/ui-hub/view-models.js +379 -1
- package/extensions/dm-cliproxy/src/ui-hub/view-usage.js +106 -1
- package/extensions/dm-cliproxy/src/ui-picker/catalog.js +41 -1
- package/extensions/dm-cliproxy/src/ui-picker/mutate.js +115 -1
- package/extensions/dm-cliproxy/src/ui-picker/prompt-confirm.js +35 -1
- package/extensions/dm-cliproxy/src/ui-picker/prompt-name.js +62 -1
- package/extensions/dm-cliproxy/src/ui-picker/providers.js +45 -1
- package/extensions/dm-cliproxy/src/ui-picker/render-text.js +34 -1
- package/extensions/dm-cliproxy/src/ui-picker/rows.js +57 -1
- package/extensions/dm-cliproxy/src/ui-setup.js +260 -2
- package/extensions/dm-cliproxy/src/ui-usage.js +151 -1
- package/extensions/dm-cliproxy/src/usage-shared-cache.js +97 -1
- package/extensions/dm-context/src/context.js +144 -1
- package/extensions/dm-context/src/index.js +339 -7
- package/extensions/dm-context/src/utils.js +9 -1
- package/extensions/dm-cua/bin/browser-cua.mjs +73 -8
- package/extensions/dm-cua/index.js +75 -6
- package/extensions/dm-cua/src/browser-cua-lib.mjs +490 -6
- package/extensions/dm-cua/src/browser-install.mjs +331 -2
- package/extensions/dm-fff/src/index.js +688 -12
- package/extensions/dm-fff/src/query.js +60 -1
- package/extensions/dm-goal/src/goal.js +894 -23
- package/extensions/dm-image2/index.js +103 -9
- package/extensions/dm-image2/src/image-lib.mjs +1275 -8
- package/extensions/dm-subagents/install.mjs +62 -8
- package/extensions/dm-subagents/src/agents/agent-management.js +1190 -36
- package/extensions/dm-subagents/src/agents/agent-memory.js +216 -6
- package/extensions/dm-subagents/src/agents/agent-scope.js +5 -1
- package/extensions/dm-subagents/src/agents/agent-selection.js +20 -1
- package/extensions/dm-subagents/src/agents/agent-serializer.js +120 -5
- package/extensions/dm-subagents/src/agents/agents.js +1139 -11
- package/extensions/dm-subagents/src/agents/chain-serializer.js +299 -11
- package/extensions/dm-subagents/src/agents/frontmatter.js +65 -6
- package/extensions/dm-subagents/src/agents/identity.js +29 -1
- package/extensions/dm-subagents/src/agents/proactive-skills.js +141 -1
- package/extensions/dm-subagents/src/agents/skills.js +614 -6
- package/extensions/dm-subagents/src/extension/config.js +35 -2
- package/extensions/dm-subagents/src/extension/control-notices.js +69 -4
- package/extensions/dm-subagents/src/extension/doctor.js +172 -15
- package/extensions/dm-subagents/src/extension/fanout-child.js +158 -231
- package/extensions/dm-subagents/src/extension/index.js +521 -359
- package/extensions/dm-subagents/src/extension/rpc.js +266 -7
- package/extensions/dm-subagents/src/extension/schemas.js +275 -1
- package/extensions/dm-subagents/src/extension/tool-description.js +111 -6
- package/extensions/dm-subagents/src/intercom/intercom-bridge.js +126 -4
- package/extensions/dm-subagents/src/intercom/native-supervisor-channel.js +452 -5
- package/extensions/dm-subagents/src/intercom/result-intercom.js +319 -3
- package/extensions/dm-subagents/src/profiles/profiles.js +458 -3
- package/extensions/dm-subagents/src/runs/background/async-execution.js +834 -40
- package/extensions/dm-subagents/src/runs/background/async-job-tracker.js +435 -14
- package/extensions/dm-subagents/src/runs/background/async-resume.js +334 -8
- package/extensions/dm-subagents/src/runs/background/async-status.js +313 -12
- package/extensions/dm-subagents/src/runs/background/chain-append.js +245 -2
- package/extensions/dm-subagents/src/runs/background/chain-root-attachment.js +136 -1
- package/extensions/dm-subagents/src/runs/background/completion-batcher.js +94 -1
- package/extensions/dm-subagents/src/runs/background/completion-dedupe.js +54 -1
- package/extensions/dm-subagents/src/runs/background/control-channel.js +190 -1
- package/extensions/dm-subagents/src/runs/background/fleet-view.js +483 -17
- package/extensions/dm-subagents/src/runs/background/notify.js +129 -3
- package/extensions/dm-subagents/src/runs/background/parallel-groups.js +34 -1
- package/extensions/dm-subagents/src/runs/background/result-watcher.js +236 -6
- package/extensions/dm-subagents/src/runs/background/run-id-resolver.js +76 -4
- package/extensions/dm-subagents/src/runs/background/run-status.js +427 -23
- package/extensions/dm-subagents/src/runs/background/scheduled-runs.js +487 -4
- package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.js +306 -9
- package/extensions/dm-subagents/src/runs/background/subagent-runner.js +2849 -73
- package/extensions/dm-subagents/src/runs/background/top-level-async.js +5 -1
- package/extensions/dm-subagents/src/runs/background/wait.js +206 -11
- package/extensions/dm-subagents/src/runs/foreground/chain-clarify.js +1013 -12
- package/extensions/dm-subagents/src/runs/foreground/chain-execution.js +980 -101
- package/extensions/dm-subagents/src/runs/foreground/execution.js +1165 -45
- package/extensions/dm-subagents/src/runs/foreground/subagent-executor.js +3157 -222
- package/extensions/dm-subagents/src/runs/shared/acceptance.js +835 -3
- package/extensions/dm-subagents/src/runs/shared/chain-outputs.js +104 -1
- package/extensions/dm-subagents/src/runs/shared/completion-guard.js +116 -3
- package/extensions/dm-subagents/src/runs/shared/dm-args.js +208 -1
- package/extensions/dm-subagents/src/runs/shared/dm-spawn.js +90 -1
- package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.js +282 -1
- package/extensions/dm-subagents/src/runs/shared/long-running-guard.js +148 -1
- package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.js +305 -1
- package/extensions/dm-subagents/src/runs/shared/model-fallback.js +194 -1
- package/extensions/dm-subagents/src/runs/shared/model-scope.js +65 -1
- package/extensions/dm-subagents/src/runs/shared/nested-events.js +851 -8
- package/extensions/dm-subagents/src/runs/shared/nested-path.js +41 -1
- package/extensions/dm-subagents/src/runs/shared/nested-render.js +105 -1
- package/extensions/dm-subagents/src/runs/shared/parallel-utils.js +81 -4
- package/extensions/dm-subagents/src/runs/shared/run-history.js +51 -4
- package/extensions/dm-subagents/src/runs/shared/single-output.js +149 -8
- package/extensions/dm-subagents/src/runs/shared/structured-output.js +58 -1
- package/extensions/dm-subagents/src/runs/shared/subagent-control.js +166 -5
- package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.js +329 -13
- package/extensions/dm-subagents/src/runs/shared/tool-budget.js +73 -1
- package/extensions/dm-subagents/src/runs/shared/turn-budget.js +47 -4
- package/extensions/dm-subagents/src/runs/shared/workflow-graph.js +196 -1
- package/extensions/dm-subagents/src/runs/shared/worktree.js +435 -3
- package/extensions/dm-subagents/src/shared/artifacts.js +92 -2
- package/extensions/dm-subagents/src/shared/atomic-json.js +55 -1
- package/extensions/dm-subagents/src/shared/child-transcript.js +167 -5
- package/extensions/dm-subagents/src/shared/file-coalescer.js +25 -1
- package/extensions/dm-subagents/src/shared/fork-context.js +147 -4
- package/extensions/dm-subagents/src/shared/formatters.js +98 -7
- package/extensions/dm-subagents/src/shared/jsonl-writer.js +56 -2
- package/extensions/dm-subagents/src/shared/model-info.js +62 -1
- package/extensions/dm-subagents/src/shared/post-exit-stdio-guard.js +68 -1
- package/extensions/dm-subagents/src/shared/session-identity.js +6 -1
- package/extensions/dm-subagents/src/shared/session-tokens.js +39 -2
- package/extensions/dm-subagents/src/shared/settings.js +198 -11
- package/extensions/dm-subagents/src/shared/status-format.js +53 -1
- package/extensions/dm-subagents/src/shared/types.js +184 -6
- package/extensions/dm-subagents/src/shared/utils.js +462 -2
- package/extensions/dm-subagents/src/slash/prompt-template-bridge.js +288 -1
- package/extensions/dm-subagents/src/slash/prompt-workflows.js +297 -7
- package/extensions/dm-subagents/src/slash/slash-bridge.js +118 -1
- package/extensions/dm-subagents/src/slash/slash-commands.js +1287 -31
- package/extensions/dm-subagents/src/slash/slash-live-state.js +240 -4
- package/extensions/dm-subagents/src/tui/render-helpers.js +64 -1
- package/extensions/dm-subagents/src/tui/render.js +1542 -4
- package/extensions/dm-usage/index.js +1294 -9
- package/extensions/greedysearch-dm/bin/cdp-greedy.mjs +40 -9
- package/extensions/greedysearch-dm/bin/cdp-headless.mjs +5 -2
- package/extensions/greedysearch-dm/bin/cdp-visible.mjs +5 -2
- package/extensions/greedysearch-dm/bin/cdp.mjs +896 -30
- package/extensions/greedysearch-dm/bin/gschrome.mjs +30 -2
- package/extensions/greedysearch-dm/bin/kill-visible.mjs +7 -2
- package/extensions/greedysearch-dm/bin/launch-visible.mjs +13 -2
- package/extensions/greedysearch-dm/bin/launch.mjs +282 -10
- package/extensions/greedysearch-dm/bin/mcp.mjs +386 -361
- package/extensions/greedysearch-dm/bin/search.mjs +620 -540
- package/extensions/greedysearch-dm/bin/visible.mjs +22 -2
- package/extensions/greedysearch-dm/extractors/bing-copilot.mjs +329 -579
- package/extensions/greedysearch-dm/extractors/chatgpt.mjs +301 -583
- package/extensions/greedysearch-dm/extractors/common.mjs +408 -32
- package/extensions/greedysearch-dm/extractors/consensus.mjs +376 -365
- package/extensions/greedysearch-dm/extractors/consent.mjs +303 -14
- package/extensions/greedysearch-dm/extractors/gemini.mjs +228 -592
- package/extensions/greedysearch-dm/extractors/google-ai.mjs +78 -499
- package/extensions/greedysearch-dm/extractors/logically.mjs +270 -347
- package/extensions/greedysearch-dm/extractors/perplexity.mjs +243 -581
- package/extensions/greedysearch-dm/extractors/selectors.mjs +32 -1
- package/extensions/greedysearch-dm/extractors/semantic-scholar.mjs +130 -317
- package/extensions/greedysearch-dm/index.js +123 -23
- package/extensions/greedysearch-dm/src/fetcher.mjs +576 -2
- package/extensions/greedysearch-dm/src/formatters/results.js +95 -10
- package/extensions/greedysearch-dm/src/formatters/sources.js +57 -1
- package/extensions/greedysearch-dm/src/formatters/synthesis.js +49 -1
- package/extensions/greedysearch-dm/src/github.mjs +222 -7
- package/extensions/greedysearch-dm/src/reddit.mjs +145 -14
- package/extensions/greedysearch-dm/src/search/browser-lifecycle.mjs +340 -9
- package/extensions/greedysearch-dm/src/search/challenge-detect.mjs +112 -4
- package/extensions/greedysearch-dm/src/search/chrome.mjs +486 -285
- package/extensions/greedysearch-dm/src/search/constants.mjs +109 -8
- package/extensions/greedysearch-dm/src/search/defaults.mjs +10 -1
- package/extensions/greedysearch-dm/src/search/engines.mjs +79 -9
- package/extensions/greedysearch-dm/src/search/fetch-source.mjs +441 -349
- package/extensions/greedysearch-dm/src/search/file-sources.mjs +29 -7
- package/extensions/greedysearch-dm/src/search/minimize.mjs +86 -1
- package/extensions/greedysearch-dm/src/search/output.mjs +51 -5
- package/extensions/greedysearch-dm/src/search/paths.mjs +48 -1
- package/extensions/greedysearch-dm/src/search/pdf.mjs +63 -2
- package/extensions/greedysearch-dm/src/search/port-pid.mjs +69 -1
- package/extensions/greedysearch-dm/src/search/progress.mjs +109 -2
- package/extensions/greedysearch-dm/src/search/query.mjs +21 -1
- package/extensions/greedysearch-dm/src/search/recovery.mjs +49 -1
- package/extensions/greedysearch-dm/src/search/research.mjs +2227 -458
- package/extensions/greedysearch-dm/src/search/scale-aware.mjs +61 -11
- package/extensions/greedysearch-dm/src/search/simple-research.mjs +396 -805
- package/extensions/greedysearch-dm/src/search/sources.mjs +412 -1
- package/extensions/greedysearch-dm/src/search/synthesis-runner.mjs +127 -12
- package/extensions/greedysearch-dm/src/search/synthesis.mjs +202 -12
- package/extensions/greedysearch-dm/src/tools/greedy-search-handler.js +209 -23
- package/extensions/greedysearch-dm/src/tools/shared.js +226 -10
- package/extensions/greedysearch-dm/src/utils/content.mjs +35 -4
- package/extensions/greedysearch-dm/src/utils/helpers.js +22 -1
- package/extensions/greedysearch-dm/src/utils/node-runtime.mjs +10 -1
- package/extensions/greedysearch-dm/src/utils/system-cmds.mjs +61 -1
- package/package.json +1 -1
|
@@ -1,80 +1,2856 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
import { writeAtomicJson } from "../../shared/atomic-json.js";
|
|
6
|
+
import { createChildTranscriptWriter } from "../../shared/child-transcript.js";
|
|
7
|
+
import { consumeInterruptRequest, deliverInterruptRequest, deliverTimeoutRequest, enqueueStepSteer, stepSteerInboxDir, watchAsyncControlInbox } from "./control-channel.js";
|
|
8
|
+
import { appendJsonl as appendRawJsonl, getArtifactPaths } from "../../shared/artifacts.js";
|
|
9
|
+
import { PI_CODING_AGENT_PACKAGE, getPiSpawnCommand, resolveInstalledPiPackageRoot } from "../shared/dm-spawn.js";
|
|
10
|
+
import { captureSingleOutputSnapshot, finalizeSingleOutput, formatSavedOutputReference, resolveSingleOutput } from "../shared/single-output.js";
|
|
11
|
+
import {
|
|
12
|
+
DEFAULT_MAX_OUTPUT,
|
|
13
|
+
SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
|
|
14
|
+
truncateOutput,
|
|
15
|
+
getSubagentDepthEnv
|
|
16
|
+
} from "../../shared/types.js";
|
|
17
|
+
import {
|
|
18
|
+
DEFAULT_CONTROL_CONFIG,
|
|
19
|
+
buildControlEvent,
|
|
20
|
+
deriveActivityState,
|
|
21
|
+
claimControlNotification,
|
|
22
|
+
formatControlIntercomMessage,
|
|
23
|
+
formatControlNoticeMessage
|
|
24
|
+
} from "../shared/subagent-control.js";
|
|
25
|
+
import {
|
|
26
|
+
isDynamicRunnerGroup,
|
|
27
|
+
isParallelGroup,
|
|
28
|
+
flattenSteps,
|
|
29
|
+
mapConcurrent,
|
|
30
|
+
aggregateParallelOutputs,
|
|
31
|
+
MAX_PARALLEL_CONCURRENCY,
|
|
32
|
+
DEFAULT_GLOBAL_CONCURRENCY_LIMIT,
|
|
33
|
+
Semaphore
|
|
34
|
+
} from "../shared/parallel-utils.js";
|
|
35
|
+
import { applyThinkingSuffix, buildPiArgs, cleanupTempDir } from "../shared/dm-args.js";
|
|
36
|
+
import { outputEntryFromAsyncResult, resolveOutputReferences } from "../shared/chain-outputs.js";
|
|
37
|
+
import { createStructuredOutputRuntime, readStructuredOutput } from "../shared/structured-output.js";
|
|
38
|
+
import { collectDynamicResults, DynamicFanoutError, materializeDynamicParallelStep, validateDynamicCollection } from "../shared/dynamic-fanout.js";
|
|
39
|
+
import { nestedSummaryFromAsyncStatus, projectNestedEvents, resolveNestedAsyncDir, writeNestedEvent } from "../shared/nested-events.js";
|
|
40
|
+
import { formatModelAttemptNote, isRetryableModelFailure } from "../shared/model-fallback.js";
|
|
41
|
+
import { attachPostExitStdioGuard, trySignalChild } from "../../shared/post-exit-stdio-guard.js";
|
|
42
|
+
import { detectSubagentError, extractTextFromContent, extractToolArgsPreview, getFinalOutput, readStatus } from "../../shared/utils.js";
|
|
43
|
+
import { evaluateCompletionMutationGuard } from "../shared/completion-guard.js";
|
|
44
|
+
import {
|
|
45
|
+
createMutatingFailureState,
|
|
46
|
+
didMutatingToolFail,
|
|
47
|
+
isMutatingTool,
|
|
48
|
+
nextLongRunningTrigger,
|
|
49
|
+
recordMutatingFailure,
|
|
50
|
+
resetMutatingFailureState,
|
|
51
|
+
resolveCurrentPath,
|
|
52
|
+
shouldEscalateMutatingFailures,
|
|
53
|
+
summarizeRecentMutatingFailures
|
|
54
|
+
} from "../shared/long-running-guard.js";
|
|
55
|
+
import { parseSessionTokens } from "../../shared/session-tokens.js";
|
|
56
|
+
import {
|
|
57
|
+
cleanupWorktrees,
|
|
58
|
+
createWorktrees,
|
|
59
|
+
diffWorktrees,
|
|
60
|
+
findWorktreeTaskCwdConflict,
|
|
61
|
+
formatWorktreeDiffSummary,
|
|
62
|
+
formatWorktreeTaskCwdConflict
|
|
63
|
+
} from "../shared/worktree.js";
|
|
64
|
+
import { resolveEffectiveThinking } from "../../shared/model-info.js";
|
|
65
|
+
import { writeInitialProgressFile } from "../../shared/settings.js";
|
|
66
|
+
import { resolveSubagentIntercomTarget } from "../../intercom/intercom-bridge.js";
|
|
67
|
+
import { acceptanceFailureMessage, aggregateAcceptanceReport, evaluateAcceptance, formatAcceptancePrompt, stripAcceptanceReport } from "../shared/acceptance.js";
|
|
68
|
+
import { waitForImportedAsyncRoot } from "./chain-root-attachment.js";
|
|
69
|
+
import { appendRunnerStepsToStatus, consumeChainAppendRequests, countPendingChainAppendRequests } from "./chain-append.js";
|
|
70
|
+
import { appendTurnBudgetSystemPrompt, formatTurnBudgetOutput, initialTurnBudgetState, shouldAbortForTurnBudget, turnBudgetExceededMessage, turnBudgetSoftNote, turnBudgetState } from "../shared/turn-budget.js";
|
|
71
|
+
import { initialToolBudgetState, toolBudgetState } from "../shared/tool-budget.js";
|
|
72
|
+
const ASYNC_INTERRUPT_SIGNAL = process.platform === "win32" ? "SIGBREAK" : "SIGUSR2";
|
|
73
|
+
const DEFAULT_MAX_ASYNC_EVENTS_BYTES = 50 * 1024 * 1024;
|
|
74
|
+
const ASYNC_EVENTS_MAX_BYTES_ENV = "DM_SUBAGENT_ASYNC_EVENTS_MAX_BYTES";
|
|
75
|
+
const TRUNCATED_EVENT_TYPE = "subagent.events.truncated";
|
|
76
|
+
const TRUNCATION_MARKER_RESERVE_BYTES = 512;
|
|
77
|
+
const asyncEventLogStates = new Map;
|
|
78
|
+
function maxAsyncEventsBytes() {
|
|
79
|
+
const raw = process.env[ASYNC_EVENTS_MAX_BYTES_ENV];
|
|
80
|
+
if (!raw)
|
|
81
|
+
return DEFAULT_MAX_ASYNC_EVENTS_BYTES;
|
|
82
|
+
const parsed = Number(raw);
|
|
83
|
+
if (!Number.isFinite(parsed) || parsed < 0)
|
|
84
|
+
return DEFAULT_MAX_ASYNC_EVENTS_BYTES;
|
|
85
|
+
return Math.floor(parsed);
|
|
86
|
+
}
|
|
87
|
+
function eventLogState(filePath) {
|
|
88
|
+
let state = asyncEventLogStates.get(filePath);
|
|
89
|
+
if (state)
|
|
90
|
+
return state;
|
|
91
|
+
let bytes = 0;
|
|
92
|
+
try {
|
|
93
|
+
bytes = fs.statSync(filePath).size;
|
|
94
|
+
} catch (error) {
|
|
95
|
+
if (error.code !== "ENOENT") {}
|
|
96
|
+
}
|
|
97
|
+
state = { bytes, diagnosticsTruncated: false };
|
|
98
|
+
asyncEventLogStates.set(filePath, state);
|
|
99
|
+
return state;
|
|
100
|
+
}
|
|
101
|
+
function appendJsonl(filePath, line) {
|
|
102
|
+
try {
|
|
103
|
+
appendRawJsonl(filePath, line);
|
|
104
|
+
const state = asyncEventLogStates.get(filePath);
|
|
105
|
+
if (state)
|
|
106
|
+
state.bytes += Buffer.byteLength(`${line}
|
|
107
|
+
`, "utf-8");
|
|
108
|
+
} catch {}
|
|
109
|
+
}
|
|
110
|
+
function appendDiagnosticJsonl(filePath, line, droppedEventType) {
|
|
111
|
+
if (!line.trim())
|
|
112
|
+
return;
|
|
113
|
+
const state = eventLogState(filePath);
|
|
114
|
+
if (state.diagnosticsTruncated)
|
|
115
|
+
return;
|
|
116
|
+
const maxBytes = maxAsyncEventsBytes();
|
|
117
|
+
const chunkBytes = Buffer.byteLength(`${line}
|
|
118
|
+
`, "utf-8");
|
|
119
|
+
const diagnosticBudget = Math.max(0, maxBytes - TRUNCATION_MARKER_RESERVE_BYTES);
|
|
120
|
+
if (state.bytes + chunkBytes <= diagnosticBudget) {
|
|
121
|
+
appendJsonl(filePath, line);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const marker = JSON.stringify({
|
|
125
|
+
type: TRUNCATED_EVENT_TYPE,
|
|
126
|
+
ts: Date.now(),
|
|
127
|
+
maxBytes,
|
|
128
|
+
droppedEventType
|
|
129
|
+
});
|
|
130
|
+
if (state.bytes + Buffer.byteLength(`${marker}
|
|
131
|
+
`, "utf-8") <= maxBytes) {
|
|
132
|
+
appendJsonl(filePath, marker);
|
|
133
|
+
}
|
|
134
|
+
state.diagnosticsTruncated = true;
|
|
135
|
+
}
|
|
136
|
+
function shouldPersistChildEvent(event) {
|
|
137
|
+
return event.type !== "message_update";
|
|
138
|
+
}
|
|
139
|
+
function findLatestSessionFile(sessionDir) {
|
|
140
|
+
try {
|
|
141
|
+
const files = fs.readdirSync(sessionDir).filter((f) => f.endsWith(".jsonl")).map((f) => path.join(sessionDir, f));
|
|
142
|
+
if (files.length === 0)
|
|
143
|
+
return null;
|
|
144
|
+
files.sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs);
|
|
145
|
+
return files[0] ?? null;
|
|
146
|
+
} catch {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
function emptyUsage() {
|
|
151
|
+
return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 };
|
|
152
|
+
}
|
|
153
|
+
function tokenUsageFromAttempts(attempts) {
|
|
154
|
+
if (!attempts || attempts.length === 0)
|
|
155
|
+
return null;
|
|
156
|
+
let input = 0;
|
|
157
|
+
let output = 0;
|
|
158
|
+
for (const attempt of attempts) {
|
|
159
|
+
input += attempt.usage?.input ?? 0;
|
|
160
|
+
output += attempt.usage?.output ?? 0;
|
|
161
|
+
}
|
|
162
|
+
const total = input + output;
|
|
163
|
+
return total > 0 ? { input, output, total } : null;
|
|
164
|
+
}
|
|
165
|
+
function costSummaryFromAttempts(attempts) {
|
|
166
|
+
if (!attempts || attempts.length === 0)
|
|
167
|
+
return;
|
|
168
|
+
let inputTokens = 0;
|
|
169
|
+
let outputTokens = 0;
|
|
170
|
+
let costUsd = 0;
|
|
171
|
+
for (const attempt of attempts) {
|
|
172
|
+
inputTokens += attempt.usage?.input ?? 0;
|
|
173
|
+
outputTokens += attempt.usage?.output ?? 0;
|
|
174
|
+
costUsd += attempt.usage?.cost ?? 0;
|
|
175
|
+
}
|
|
176
|
+
return inputTokens > 0 || outputTokens > 0 || costUsd > 0 ? { inputTokens, outputTokens, costUsd } : undefined;
|
|
177
|
+
}
|
|
178
|
+
function appendRecentStepOutput(step, lines) {
|
|
179
|
+
const nonEmpty = lines.filter((line) => line.trim());
|
|
180
|
+
if (nonEmpty.length === 0)
|
|
181
|
+
return;
|
|
182
|
+
step.recentOutput ??= [];
|
|
183
|
+
step.recentOutput.push(...nonEmpty);
|
|
184
|
+
if (step.recentOutput.length > 50) {
|
|
185
|
+
step.recentOutput.splice(0, step.recentOutput.length - 50);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
function isTerminalAssistantStop(message) {
|
|
189
|
+
const stopReason = message.stopReason;
|
|
190
|
+
const hasToolCall = Array.isArray(message.content) && message.content.some((part) => part.type === "toolCall");
|
|
191
|
+
return stopReason === "stop" && !hasToolCall;
|
|
192
|
+
}
|
|
193
|
+
function resetStepLiveDetail(step) {
|
|
194
|
+
step.currentTool = undefined;
|
|
195
|
+
step.currentToolArgs = undefined;
|
|
196
|
+
step.currentToolStartedAt = undefined;
|
|
197
|
+
step.currentPath = undefined;
|
|
198
|
+
step.recentTools = [];
|
|
199
|
+
step.recentOutput = [];
|
|
200
|
+
}
|
|
201
|
+
function runPiStreaming(args, cwd, outputFile, env, piPackageRoot, piArgv1, maxSubagentDepth, childEventContext, registerInterrupt, onChildEvent, transcriptWriter, registerTimeout, timeoutMessage, registerTurnBudgetAbort) {
|
|
202
|
+
return new Promise((resolve) => {
|
|
203
|
+
const outputStream = fs.createWriteStream(outputFile, { flags: "w" });
|
|
204
|
+
const spawnEnv = { ...process.env, ...env ?? {}, ...getSubagentDepthEnv(maxSubagentDepth) };
|
|
205
|
+
const spawnSpec = getPiSpawnCommand(args, {
|
|
206
|
+
...piPackageRoot ? { piPackageRoot } : {},
|
|
207
|
+
...piArgv1 ? { argv1: piArgv1 } : {}
|
|
208
|
+
});
|
|
209
|
+
const child = spawn(spawnSpec.command, spawnSpec.args, {
|
|
210
|
+
cwd,
|
|
211
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
212
|
+
env: spawnEnv,
|
|
213
|
+
windowsHide: true
|
|
214
|
+
});
|
|
215
|
+
let stderr = "";
|
|
216
|
+
let stdoutBuf = "";
|
|
217
|
+
let stderrBuf = "";
|
|
218
|
+
const messages = [];
|
|
219
|
+
const usage = emptyUsage();
|
|
220
|
+
let model;
|
|
221
|
+
let error;
|
|
222
|
+
let assistantError;
|
|
223
|
+
let interrupted = false;
|
|
224
|
+
let timedOut = false;
|
|
225
|
+
let turnBudgetExceeded = false;
|
|
226
|
+
let turnBudgetMessage;
|
|
227
|
+
let turnBudget;
|
|
228
|
+
let observedMutationAttempt = false;
|
|
229
|
+
const rawStdoutLines = [];
|
|
230
|
+
const writeOutputLine = (line) => {
|
|
231
|
+
if (!line.trim())
|
|
232
|
+
return;
|
|
233
|
+
outputStream.write(`${line}
|
|
234
|
+
`);
|
|
235
|
+
};
|
|
236
|
+
const writeOutputText = (text) => {
|
|
237
|
+
for (const line of text.split(`
|
|
238
|
+
`)) {
|
|
239
|
+
writeOutputLine(line);
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
const appendChildEvent = (event) => {
|
|
243
|
+
if (!childEventContext)
|
|
244
|
+
return;
|
|
245
|
+
if (!shouldPersistChildEvent(event))
|
|
246
|
+
return;
|
|
247
|
+
appendDiagnosticJsonl(childEventContext.eventsPath, JSON.stringify({
|
|
248
|
+
...event,
|
|
249
|
+
subagentSource: "child",
|
|
250
|
+
subagentRunId: childEventContext.runId,
|
|
251
|
+
subagentStepIndex: childEventContext.stepIndex,
|
|
252
|
+
subagentAgent: childEventContext.agent,
|
|
253
|
+
observedAt: Date.now()
|
|
254
|
+
}), typeof event.type === "string" ? event.type : undefined);
|
|
255
|
+
};
|
|
256
|
+
const appendChildLine = (type, line) => {
|
|
257
|
+
appendChildEvent({ type, line });
|
|
258
|
+
if (type === "subagent.child.stdout")
|
|
259
|
+
transcriptWriter?.writeStdoutLine(line);
|
|
260
|
+
else
|
|
261
|
+
transcriptWriter?.writeStderrLine(line);
|
|
262
|
+
};
|
|
263
|
+
const processStdoutLine = (line) => {
|
|
264
|
+
if (!line.trim())
|
|
265
|
+
return;
|
|
266
|
+
let event;
|
|
267
|
+
try {
|
|
268
|
+
event = JSON.parse(line);
|
|
269
|
+
} catch {
|
|
270
|
+
rawStdoutLines.push(line);
|
|
271
|
+
writeOutputLine(line);
|
|
272
|
+
appendChildLine("subagent.child.stdout", line);
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
appendChildEvent(event);
|
|
276
|
+
transcriptWriter?.writeChildEvent(event);
|
|
277
|
+
onChildEvent?.(event);
|
|
278
|
+
if (event.type === "tool_execution_start" && event.toolName) {
|
|
279
|
+
observedMutationAttempt = observedMutationAttempt || isMutatingTool(event.toolName, event.args);
|
|
280
|
+
const toolArgs = extractToolArgsPreview(event.args ?? {});
|
|
281
|
+
writeOutputLine(toolArgs ? `${event.toolName}: ${toolArgs}` : event.toolName);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
if ((event.type === "message_end" || event.type === "tool_result_end") && event.message) {
|
|
285
|
+
messages.push(event.message);
|
|
286
|
+
const text = extractTextFromContent(event.message.content);
|
|
287
|
+
if (text)
|
|
288
|
+
writeOutputText(text);
|
|
289
|
+
if (event.type !== "message_end" || event.message.role !== "assistant")
|
|
290
|
+
return;
|
|
291
|
+
if (event.message.model)
|
|
292
|
+
model = event.message.model;
|
|
293
|
+
if (event.message.errorMessage)
|
|
294
|
+
assistantError = event.message.errorMessage;
|
|
295
|
+
const eventUsage = event.message.usage;
|
|
296
|
+
if (eventUsage) {
|
|
297
|
+
usage.turns++;
|
|
298
|
+
usage.input += eventUsage.input ?? eventUsage.inputTokens ?? 0;
|
|
299
|
+
usage.output += eventUsage.output ?? eventUsage.outputTokens ?? 0;
|
|
300
|
+
usage.cacheRead += eventUsage.cacheRead ?? 0;
|
|
301
|
+
usage.cacheWrite += eventUsage.cacheWrite ?? 0;
|
|
302
|
+
usage.cost += eventUsage.cost?.total ?? 0;
|
|
303
|
+
}
|
|
304
|
+
if (isTerminalAssistantStop(event.message)) {
|
|
305
|
+
if (!event.message.errorMessage && extractTextFromContent(event.message.content).trim())
|
|
306
|
+
assistantError = undefined;
|
|
307
|
+
cleanTerminalAssistantStopReceived ||= !event.message.errorMessage;
|
|
308
|
+
startFinalDrain();
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
const processStderrText = (text) => {
|
|
313
|
+
stderr += text;
|
|
314
|
+
stderrBuf += text;
|
|
315
|
+
outputStream.write(text);
|
|
316
|
+
if (!childEventContext)
|
|
317
|
+
return;
|
|
318
|
+
const lines = stderrBuf.split(`
|
|
319
|
+
`);
|
|
320
|
+
stderrBuf = lines.pop() || "";
|
|
321
|
+
for (const line of lines) {
|
|
322
|
+
if (!line.trim())
|
|
323
|
+
continue;
|
|
324
|
+
appendChildLine("subagent.child.stderr", line);
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
const FINAL_STOP_GRACE_MS = 1000;
|
|
328
|
+
const HARD_KILL_MS = 3000;
|
|
329
|
+
const TIMEOUT_HARD_KILL_MS = 3000;
|
|
330
|
+
let childExited = false;
|
|
331
|
+
let forcedTerminationSignal = false;
|
|
332
|
+
let cleanTerminalAssistantStopReceived = false;
|
|
333
|
+
let finalDrainTimer;
|
|
334
|
+
let finalHardKillTimer;
|
|
335
|
+
let timeoutHardKillTimer;
|
|
336
|
+
let turnBudgetTerminationTimer;
|
|
337
|
+
let turnBudgetHardKillTimer;
|
|
338
|
+
let settled = false;
|
|
339
|
+
const clearStdioGuard = attachPostExitStdioGuard(child, { idleMs: 2000, hardMs: 8000 });
|
|
340
|
+
child.stdout.on("data", (chunk) => {
|
|
341
|
+
const text = chunk.toString();
|
|
342
|
+
stdoutBuf += text;
|
|
343
|
+
const lines = stdoutBuf.split(`
|
|
344
|
+
`);
|
|
345
|
+
stdoutBuf = lines.pop() || "";
|
|
346
|
+
for (const line of lines)
|
|
347
|
+
processStdoutLine(line);
|
|
348
|
+
});
|
|
349
|
+
child.stderr.on("data", (chunk) => {
|
|
350
|
+
processStderrText(chunk.toString());
|
|
351
|
+
});
|
|
352
|
+
registerInterrupt?.(() => {
|
|
353
|
+
if (settled || timedOut)
|
|
354
|
+
return;
|
|
355
|
+
interrupted = true;
|
|
356
|
+
if (!error)
|
|
357
|
+
error = "Interrupted. Waiting for explicit next action.";
|
|
358
|
+
trySignalChild(child, "SIGINT");
|
|
359
|
+
setTimeout(() => {
|
|
360
|
+
if (!settled && !timedOut)
|
|
361
|
+
trySignalChild(child, "SIGTERM");
|
|
362
|
+
}, 1000).unref?.();
|
|
363
|
+
});
|
|
364
|
+
registerTimeout?.(() => {
|
|
365
|
+
if (settled || timedOut)
|
|
366
|
+
return;
|
|
367
|
+
timedOut = true;
|
|
368
|
+
interrupted = false;
|
|
369
|
+
error = timeoutMessage ?? "Subagent timed out.";
|
|
370
|
+
trySignalChild(child, "SIGTERM");
|
|
371
|
+
timeoutHardKillTimer = setTimeout(() => {
|
|
372
|
+
if (!settled)
|
|
373
|
+
trySignalChild(child, "SIGKILL");
|
|
374
|
+
}, TIMEOUT_HARD_KILL_MS);
|
|
375
|
+
timeoutHardKillTimer.unref?.();
|
|
376
|
+
});
|
|
377
|
+
registerTurnBudgetAbort?.((message, state) => {
|
|
378
|
+
if (settled || timedOut || turnBudgetExceeded)
|
|
379
|
+
return;
|
|
380
|
+
turnBudgetExceeded = true;
|
|
381
|
+
turnBudgetMessage = message;
|
|
382
|
+
turnBudget = state;
|
|
383
|
+
interrupted = false;
|
|
384
|
+
error = message;
|
|
385
|
+
trySignalChild(child, "SIGINT");
|
|
386
|
+
turnBudgetTerminationTimer = setTimeout(() => {
|
|
387
|
+
if (!settled && !timedOut)
|
|
388
|
+
trySignalChild(child, "SIGTERM");
|
|
389
|
+
}, 1000);
|
|
390
|
+
turnBudgetTerminationTimer.unref?.();
|
|
391
|
+
turnBudgetHardKillTimer = setTimeout(() => {
|
|
392
|
+
if (!settled && !timedOut)
|
|
393
|
+
trySignalChild(child, "SIGKILL");
|
|
394
|
+
}, 4000);
|
|
395
|
+
turnBudgetHardKillTimer.unref?.();
|
|
396
|
+
});
|
|
397
|
+
const clearDrainTimers = () => {
|
|
398
|
+
if (finalDrainTimer) {
|
|
399
|
+
clearTimeout(finalDrainTimer);
|
|
400
|
+
finalDrainTimer = undefined;
|
|
401
|
+
}
|
|
402
|
+
if (finalHardKillTimer) {
|
|
403
|
+
clearTimeout(finalHardKillTimer);
|
|
404
|
+
finalHardKillTimer = undefined;
|
|
405
|
+
}
|
|
406
|
+
if (timeoutHardKillTimer) {
|
|
407
|
+
clearTimeout(timeoutHardKillTimer);
|
|
408
|
+
timeoutHardKillTimer = undefined;
|
|
409
|
+
}
|
|
410
|
+
if (turnBudgetTerminationTimer) {
|
|
411
|
+
clearTimeout(turnBudgetTerminationTimer);
|
|
412
|
+
turnBudgetTerminationTimer = undefined;
|
|
413
|
+
}
|
|
414
|
+
if (turnBudgetHardKillTimer) {
|
|
415
|
+
clearTimeout(turnBudgetHardKillTimer);
|
|
416
|
+
turnBudgetHardKillTimer = undefined;
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
function startFinalDrain() {
|
|
420
|
+
if (childExited || finalDrainTimer || settled)
|
|
421
|
+
return;
|
|
422
|
+
finalDrainTimer = setTimeout(() => {
|
|
423
|
+
if (settled)
|
|
424
|
+
return;
|
|
425
|
+
const termSent = trySignalChild(child, "SIGTERM");
|
|
426
|
+
if (!termSent)
|
|
427
|
+
return;
|
|
428
|
+
forcedTerminationSignal = true;
|
|
429
|
+
if (!cleanTerminalAssistantStopReceived && !error && !assistantError) {
|
|
430
|
+
error = `Subagent process did not exit within ${FINAL_STOP_GRACE_MS}ms after its final message. Forcing termination.`;
|
|
431
|
+
}
|
|
432
|
+
finalHardKillTimer = setTimeout(() => {
|
|
433
|
+
if (settled)
|
|
434
|
+
return;
|
|
435
|
+
forcedTerminationSignal = trySignalChild(child, "SIGKILL") || forcedTerminationSignal;
|
|
436
|
+
}, HARD_KILL_MS);
|
|
437
|
+
finalHardKillTimer.unref?.();
|
|
438
|
+
}, FINAL_STOP_GRACE_MS);
|
|
439
|
+
finalDrainTimer.unref?.();
|
|
440
|
+
}
|
|
441
|
+
child.on("exit", () => {
|
|
442
|
+
childExited = true;
|
|
443
|
+
clearDrainTimers();
|
|
444
|
+
});
|
|
445
|
+
child.on("close", (exitCode, signal) => {
|
|
446
|
+
settled = true;
|
|
447
|
+
registerInterrupt?.(undefined);
|
|
448
|
+
registerTimeout?.(undefined);
|
|
449
|
+
registerTurnBudgetAbort?.(undefined);
|
|
450
|
+
clearDrainTimers();
|
|
451
|
+
clearStdioGuard();
|
|
452
|
+
if (stdoutBuf.trim())
|
|
453
|
+
processStdoutLine(stdoutBuf);
|
|
454
|
+
if (stderrBuf.trim())
|
|
455
|
+
appendChildLine("subagent.child.stderr", stderrBuf);
|
|
456
|
+
outputStream.end();
|
|
457
|
+
const finalOutput = getFinalOutput(messages) || rawStdoutLines.join(`
|
|
458
|
+
`).trim();
|
|
459
|
+
const finalError = error ?? assistantError;
|
|
460
|
+
const forcedDrainAfterFinalSuccess = forcedTerminationSignal && cleanTerminalAssistantStopReceived && !finalError;
|
|
461
|
+
resolve({
|
|
462
|
+
stderr,
|
|
463
|
+
exitCode: timedOut ? 1 : turnBudgetExceeded ? 1 : interrupted || forcedDrainAfterFinalSuccess ? 0 : forcedTerminationSignal || signal ? exitCode ?? 1 : exitCode,
|
|
464
|
+
messages,
|
|
465
|
+
usage,
|
|
466
|
+
model,
|
|
467
|
+
error: timedOut ? timeoutMessage ?? "Subagent timed out." : turnBudgetExceeded ? turnBudgetMessage : interrupted || forcedDrainAfterFinalSuccess ? undefined : finalError,
|
|
468
|
+
finalOutput: timedOut && !finalOutput.trim() ? timeoutMessage ?? "Subagent timed out." : finalOutput,
|
|
469
|
+
interrupted,
|
|
470
|
+
timedOut,
|
|
471
|
+
turnBudget,
|
|
472
|
+
turnBudgetExceeded,
|
|
473
|
+
wrapUpRequested: turnBudget?.outcome === "wrap-up-requested" || turnBudgetExceeded || undefined,
|
|
474
|
+
observedMutationAttempt
|
|
475
|
+
});
|
|
476
|
+
});
|
|
477
|
+
child.on("error", (spawnError) => {
|
|
478
|
+
settled = true;
|
|
479
|
+
registerInterrupt?.(undefined);
|
|
480
|
+
registerTimeout?.(undefined);
|
|
481
|
+
registerTurnBudgetAbort?.(undefined);
|
|
482
|
+
clearDrainTimers();
|
|
483
|
+
clearStdioGuard();
|
|
484
|
+
outputStream.end();
|
|
485
|
+
const finalOutput = getFinalOutput(messages) || rawStdoutLines.join(`
|
|
486
|
+
`).trim();
|
|
487
|
+
const spawnErrorMessage = spawnError instanceof Error ? spawnError.message : String(spawnError);
|
|
488
|
+
resolve({ stderr, exitCode: 1, messages, usage, model, error: timedOut ? timeoutMessage ?? "Subagent timed out." : turnBudgetExceeded ? turnBudgetMessage : error ?? assistantError ?? spawnErrorMessage, finalOutput: timedOut && !finalOutput.trim() ? timeoutMessage ?? "Subagent timed out." : finalOutput, timedOut, turnBudget, turnBudgetExceeded, wrapUpRequested: turnBudget?.outcome === "wrap-up-requested" || turnBudgetExceeded || undefined, observedMutationAttempt });
|
|
489
|
+
});
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
function resolvePiPackageRootFallback() {
|
|
493
|
+
const root = resolveInstalledPiPackageRoot();
|
|
494
|
+
if (root)
|
|
495
|
+
return root;
|
|
496
|
+
throw new Error(`Could not resolve ${PI_CODING_AGENT_PACKAGE} package root`);
|
|
497
|
+
}
|
|
498
|
+
async function exportSessionHtml(sessionFile, outputDir, piPackageRoot) {
|
|
499
|
+
const pkgRoot = piPackageRoot ?? resolvePiPackageRootFallback();
|
|
500
|
+
const exportModulePath = path.join(pkgRoot, "dist", "core", "export-html", "index.js");
|
|
501
|
+
const moduleUrl = pathToFileURL(exportModulePath).href;
|
|
502
|
+
const mod = await import(moduleUrl);
|
|
503
|
+
const exportFromFile = mod.exportFromFile;
|
|
504
|
+
if (typeof exportFromFile !== "function") {
|
|
505
|
+
throw new Error("exportFromFile not available");
|
|
506
|
+
}
|
|
507
|
+
const outputPath = path.join(outputDir, `${path.basename(sessionFile, ".jsonl")}.html`);
|
|
508
|
+
return exportFromFile(sessionFile, { outputPath });
|
|
509
|
+
}
|
|
510
|
+
function createShareLink(htmlPath) {
|
|
511
|
+
try {
|
|
512
|
+
const auth = spawnSync("gh", ["auth", "status"], { encoding: "utf-8" });
|
|
513
|
+
if (auth.status !== 0) {
|
|
514
|
+
return { error: "GitHub CLI is not logged in. Run 'gh auth login' first." };
|
|
515
|
+
}
|
|
516
|
+
} catch {
|
|
517
|
+
return { error: "GitHub CLI (gh) is not installed." };
|
|
518
|
+
}
|
|
519
|
+
try {
|
|
520
|
+
const result = spawnSync("gh", ["gist", "create", htmlPath], { encoding: "utf-8" });
|
|
521
|
+
if (result.status !== 0) {
|
|
522
|
+
const err = (result.stderr || "").trim() || "Failed to create gist.";
|
|
523
|
+
return { error: err };
|
|
524
|
+
}
|
|
525
|
+
const gistUrl = (result.stdout || "").trim();
|
|
526
|
+
const gistId = gistUrl.split("/").pop();
|
|
527
|
+
if (!gistId)
|
|
528
|
+
return { error: "Failed to parse gist ID." };
|
|
529
|
+
const shareUrl = `https://shittycodingagent.ai/session/?${gistId}`;
|
|
530
|
+
return { shareUrl, gistUrl };
|
|
531
|
+
} catch (err) {
|
|
532
|
+
return { error: String(err) };
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
function formatDuration(ms) {
|
|
536
|
+
if (ms < 1000)
|
|
537
|
+
return `${ms}ms`;
|
|
538
|
+
if (ms < 60000)
|
|
539
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
540
|
+
const minutes = Math.floor(ms / 60000);
|
|
541
|
+
const seconds = Math.floor(ms % 60000 / 1000);
|
|
542
|
+
return `${minutes}m${seconds}s`;
|
|
543
|
+
}
|
|
544
|
+
function writeRunLog(logPath, input) {
|
|
545
|
+
const lines = [];
|
|
546
|
+
lines.push(`# Subagent run ${input.id}`);
|
|
547
|
+
lines.push("");
|
|
548
|
+
lines.push(`- **Mode:** ${input.mode}`);
|
|
549
|
+
lines.push(`- **CWD:** ${input.cwd}`);
|
|
550
|
+
lines.push(`- **Started:** ${new Date(input.startedAt).toISOString()}`);
|
|
551
|
+
lines.push(`- **Ended:** ${new Date(input.endedAt).toISOString()}`);
|
|
552
|
+
lines.push(`- **Duration:** ${formatDuration(input.endedAt - input.startedAt)}`);
|
|
553
|
+
if (input.sessionFile)
|
|
554
|
+
lines.push(`- **Session:** ${input.sessionFile}`);
|
|
555
|
+
if (input.shareUrl)
|
|
556
|
+
lines.push(`- **Share:** ${input.shareUrl}`);
|
|
557
|
+
if (input.shareError)
|
|
558
|
+
lines.push(`- **Share error:** ${input.shareError}`);
|
|
559
|
+
if (input.artifactsDir)
|
|
560
|
+
lines.push(`- **Artifacts:** ${input.artifactsDir}`);
|
|
561
|
+
lines.push("");
|
|
562
|
+
lines.push("## Steps");
|
|
563
|
+
lines.push("| Step | Agent | Status | Duration |");
|
|
564
|
+
lines.push("| --- | --- | --- | --- |");
|
|
565
|
+
input.steps.forEach((step, i) => {
|
|
566
|
+
const duration = step.durationMs !== undefined ? formatDuration(step.durationMs) : "-";
|
|
567
|
+
lines.push(`| ${i + 1} | ${step.agent} | ${step.status} | ${duration} |`);
|
|
568
|
+
});
|
|
569
|
+
lines.push("");
|
|
570
|
+
lines.push("## Summary");
|
|
571
|
+
if (input.truncated) {
|
|
572
|
+
lines.push("_Output truncated_");
|
|
573
|
+
lines.push("");
|
|
574
|
+
}
|
|
575
|
+
lines.push(input.summary.trim() || "(no output)");
|
|
576
|
+
lines.push("");
|
|
577
|
+
fs.writeFileSync(logPath, lines.join(`
|
|
578
|
+
`), "utf-8");
|
|
579
|
+
}
|
|
580
|
+
async function runSingleStep(step, ctx) {
|
|
581
|
+
if (step.importAsyncRoot) {
|
|
582
|
+
let importTimedOut = false;
|
|
583
|
+
ctx.registerTimeout?.(() => {
|
|
584
|
+
importTimedOut = true;
|
|
585
|
+
let pid;
|
|
586
|
+
try {
|
|
587
|
+
pid = readStatus(step.importAsyncRoot.asyncDir)?.pid;
|
|
588
|
+
} catch {
|
|
589
|
+
pid = undefined;
|
|
590
|
+
}
|
|
591
|
+
try {
|
|
592
|
+
deliverTimeoutRequest({ asyncDir: step.importAsyncRoot.asyncDir, pid, source: "ancestor-timeout" });
|
|
593
|
+
} catch {}
|
|
594
|
+
});
|
|
595
|
+
try {
|
|
596
|
+
const imported = await waitForImportedAsyncRoot(step.importAsyncRoot, {
|
|
597
|
+
shouldAbort: () => importTimedOut || ctx.timeoutSignal?.aborted === true || ctx.skipAcceptance?.() === true,
|
|
598
|
+
timeoutMessage: ctx.timeoutMessage
|
|
599
|
+
});
|
|
600
|
+
try {
|
|
601
|
+
fs.writeFileSync(ctx.outputFile, imported.output, "utf-8");
|
|
602
|
+
} catch {}
|
|
603
|
+
const timedOut = importTimedOut || imported.timedOut === true || ctx.timeoutSignal?.aborted === true || ctx.skipAcceptance?.() === true;
|
|
604
|
+
return {
|
|
605
|
+
agent: imported.agent,
|
|
606
|
+
output: timedOut ? ctx.timeoutMessage ?? "Subagent timed out." : imported.output,
|
|
607
|
+
exitCode: timedOut ? 1 : imported.exitCode,
|
|
608
|
+
error: timedOut ? ctx.timeoutMessage ?? "Subagent timed out." : imported.error,
|
|
609
|
+
timedOut: timedOut ? true : undefined,
|
|
610
|
+
sessionFile: imported.sessionFile,
|
|
611
|
+
intercomTarget: imported.intercomTarget,
|
|
612
|
+
model: imported.model,
|
|
613
|
+
attemptedModels: imported.attemptedModels,
|
|
614
|
+
modelAttempts: imported.modelAttempts,
|
|
615
|
+
totalCost: imported.totalCost,
|
|
616
|
+
structuredOutput: timedOut ? undefined : imported.structuredOutput,
|
|
617
|
+
structuredOutputPath: timedOut ? undefined : imported.structuredOutputPath,
|
|
618
|
+
structuredOutputSchemaPath: timedOut ? undefined : imported.structuredOutputSchemaPath,
|
|
619
|
+
acceptance: timedOut ? undefined : imported.acceptance
|
|
620
|
+
};
|
|
621
|
+
} finally {
|
|
622
|
+
ctx.registerTimeout?.(undefined);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
const effectiveStructuredOutput = step.structuredOutput ?? (step.structuredOutputSchema ? createStructuredOutputRuntime(step.structuredOutputSchema, path.join(path.dirname(ctx.outputFile), "structured-output")) : undefined);
|
|
626
|
+
const placeholderRegex = new RegExp(ctx.placeholder.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g");
|
|
627
|
+
let task = step.task.replace(placeholderRegex, () => ctx.previousOutput);
|
|
628
|
+
task = resolveOutputReferences(task, ctx.outputs ?? {});
|
|
629
|
+
const taskForCompletionGuard = task;
|
|
630
|
+
if (step.effectiveAcceptance) {
|
|
631
|
+
const acceptancePrompt = formatAcceptancePrompt(step.effectiveAcceptance);
|
|
632
|
+
if (acceptancePrompt)
|
|
633
|
+
task = `${task}
|
|
634
|
+
${acceptancePrompt}`;
|
|
635
|
+
}
|
|
636
|
+
const sessionEnabled = Boolean(step.sessionFile) || ctx.sessionEnabled;
|
|
637
|
+
const sessionDir = step.sessionFile ? undefined : ctx.sessionDir;
|
|
638
|
+
let artifactPaths;
|
|
639
|
+
let transcriptWriter;
|
|
640
|
+
if (ctx.artifactsDir && ctx.artifactConfig?.enabled !== false) {
|
|
641
|
+
const index = ctx.flatStepCount > 1 ? ctx.flatIndex : undefined;
|
|
642
|
+
artifactPaths = getArtifactPaths(ctx.artifactsDir, ctx.id, step.agent, index);
|
|
643
|
+
fs.mkdirSync(ctx.artifactsDir, { recursive: true });
|
|
644
|
+
if (ctx.artifactConfig?.includeInput !== false) {
|
|
645
|
+
fs.writeFileSync(artifactPaths.inputPath, `# Task for ${step.agent}
|
|
8
646
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
`)}import*as b$ from"node:fs";import*as t1 from"node:os";import*as B$ from"node:path";import{fileURLToPath as dJ}from"node:url";import*as CJ from"node:path";var e2=128,$6=4;function n1($){return typeof $==="string"&&$.length>0&&$.length<=e2&&!CJ.isAbsolute($)&&!$.includes("/")&&!$.includes("\\")&&!$.includes("..")}function AJ($){return typeof $==="number"&&Number.isFinite($)?$:void 0}function NJ($,j){return typeof $==="string"&&$.length>0?$.slice(0,j):void 0}function J1($){if(!Array.isArray($))return[];return $.map((j)=>{if(!j||typeof j!=="object")return;let J=j;if(!n1(J.runId))return;return{runId:J.runId,...AJ(J.stepIndex)!==void 0?{stepIndex:AJ(J.stepIndex)}:{},...NJ(J.agent,128)?{agent:NJ(J.agent,128)}:{}}}).filter((j)=>Boolean(j)).slice(0,$6)}function i1($){if(!$)return[];try{return J1(JSON.parse($))}catch{return[]}}function TJ($){let j=J1($);return j.length?JSON.stringify(j):""}import{createHash as j6}from"node:crypto";import*as N0 from"node:fs";import*as m$ from"node:os";import*as O$ from"node:path";var J6=1,Z6=604800000,EJ=new Set(["read","bash","edit","write","grep","find","ls","mcp"]),SJ=O$.join(m$.homedir(),".config","mcp","mcp.json"),RJ={cursor:[O$.join(m$.homedir(),".cursor","mcp.json")],"claude-code":[O$.join(m$.homedir(),".claude","mcp.json"),O$.join(m$.homedir(),".claude.json"),O$.join(m$.homedir(),".claude","claude_desktop_config.json")],"claude-desktop":[O$.join(m$.homedir(),"Library","Application Support","Claude","claude_desktop_config.json")],codex:[O$.join(m$.homedir(),".codex","config.json")],windsurf:[O$.join(m$.homedir(),".windsurf","mcp.json")],vscode:[".vscode/mcp.json"]};function wJ($,j=process.cwd()){if(!$?.length)return[];try{let J=X6(j),Z=Q6();if(!Z)return[];return G6(J,Z,L6(J.settings?.toolPrefix),$)}catch{return[]}}function Q6(){let $=O$.join(L0(),"mcp-cache.json"),j;try{j=JSON.parse(N0.readFileSync($,"utf-8"))}catch{return null}if(!j||typeof j!=="object")return null;let J=j;if(J.version!==J6||!J.servers||typeof J.servers!=="object"||Array.isArray(J.servers))return null;return J}function X6($){let j={mcpServers:{}};for(let J of Y6($)){let Z=W6(J);if(!Z)continue;j=V6(j,K6(Z,$))}return j}function Y6($){let j=O$.join(L0(),"mcp.json"),J=O$.resolve($,".mcp.json"),Z=O$.resolve(w1($),"mcp.json"),Q=[];if(SJ!==j)Q.push(SJ);if(Q.push(j),J!==j)Q.push(J);if(Z!==j&&Z!==J)Q.push(Z);return Q}function W6($){let j;try{j=JSON.parse(N0.readFileSync($,"utf-8"))}catch{return null}return H6(j)}function H6($){if(!$||typeof $!=="object"||Array.isArray($))return{mcpServers:{}};let j=$,J=j.mcpServers??j["mcp-servers"]??{};return{mcpServers:J&&typeof J==="object"&&!Array.isArray(J)?J:{},imports:Array.isArray(j.imports)?j.imports.filter((Z)=>q6(Z)):void 0,settings:j.settings&&typeof j.settings==="object"&&!Array.isArray(j.settings)?j.settings:void 0}}function V6($,j){let J=[...$.imports??[],...j.imports??[]];return{mcpServers:{...$.mcpServers,...j.mcpServers},imports:J.length?[...new Set(J)]:void 0,settings:j.settings?{...$.settings,...j.settings}:$.settings}}function K6($,j){if(!$.imports?.length)return $;let J={};for(let Z of $.imports){let Q=U6(Z,j);if(!Q)continue;let X;try{X=JSON.parse(N0.readFileSync(Q,"utf-8"))}catch{continue}for(let[W,U]of Object.entries(z6(X,Z)))if(!J[W])J[W]=U}return{imports:$.imports,settings:$.settings,mcpServers:{...J,...$.mcpServers}}}function U6($,j){for(let J of RJ[$]){let Z=J.startsWith(".")?O$.resolve(j,J):J;if(N0.existsSync(Z))return Z}return null}function z6($,j){if(!$||typeof $!=="object"||Array.isArray($))return{};let J=$,Z=j==="cursor"||j==="windsurf"||j==="vscode"?J.mcpServers??J["mcp-servers"]:J.mcpServers;return Z&&typeof Z==="object"&&!Array.isArray(Z)?Z:{}}function G6($,j,J,Z){let Q=[],X=new Set,{servers:W,tools:U}=B6(Z);for(let[G,B]of Object.entries($.mcpServers)){let q=j.servers[G];if(!_6(q,B))continue;let O=W.has(G)?!0:U.get(G);if(!O)continue;for(let T of Array.isArray(q.tools)?q.tools:[]){if(typeof T?.name!=="string"||!T.name)continue;if(O!==!0&&!O.has(T.name))continue;if(DJ(T.name,G,J,B.excludeTools))continue;let y=x0(T.name,G,J);if(EJ.has(y)||X.has(y))continue;X.add(y),Q.push(y)}if(B.exposeResources===!1)continue;for(let T of Array.isArray(q.resources)?q.resources:[]){if(typeof T?.name!=="string"||!T.name||typeof T.uri!=="string"||!T.uri)continue;let y=`get_${O6(T.name)}`;if(O!==!0&&!O.has(y))continue;if(DJ(y,G,J,B.excludeTools))continue;let h=x0(y,G,J);if(EJ.has(h)||X.has(h))continue;X.add(h),Q.push(h)}}return Q}function B6($){let j=new Set,J=new Map;for(let Z of $)if(Z=Z.replace(/\/+$/,""),Z.includes("/")){let[Q,X]=Z.split("/",2);if(Q&&X){if(!J.has(Q))J.set(Q,new Set);J.get(Q).add(X)}else if(Q)j.add(Q)}else if(Z)j.add(Z);return{servers:j,tools:J}}function _6($,j){if(!$||$.configHash!==M6(j))return!1;if(!$.cachedAt||typeof $.cachedAt!=="number")return!1;return Date.now()-$.cachedAt<=Z6}function M6($){let j={command:$.command,args:$.args,env:bJ($.env),cwd:A6($.cwd),url:$.url,headers:bJ($.headers),auth:$.auth,bearerToken:N6($),bearerTokenEnv:$.bearerTokenEnv,exposeResources:$.exposeResources,excludeTools:$.excludeTools};return j6("sha256").update(r1(j)).digest("hex")}function L6($){return $==="none"||$==="short"||$==="server"?$:"server"}function q6($){return typeof $==="string"&&Object.hasOwn(RJ,$)}function F6($,j){if(j==="none")return"";if(j==="short")return $.replace(/-?mcp$/i,"").replace(/-/g,"_")||"mcp";return $.replace(/-/g,"_")}function x0($,j,J){let Z=F6(j,J);return Z?`${Z}_${$}`:$}function DJ($,j,J,Z){if(!Array.isArray(Z)||Z.length===0)return!1;let Q=new Set([P0($),P0(x0($,j,J)),P0(x0($,j,"server")),P0(x0($,j,"short"))]);return Z.some((X)=>typeof X==="string"&&Q.has(P0(X)))}function P0($){return $.replace(/-/g,"_")}function O6($){let j=$.replace(/[^a-zA-Z0-9]/g,"_").replace(/_+/g,"_").replace(/^_+/,"").replace(/_+$/,"").toLowerCase();if(!j||/^\d/.test(j))j=`resource${j?`_${j}`:""}`;return j}function bJ($){if(!$||typeof $!=="object"||Array.isArray($))return;let j={};for(let[J,Z]of Object.entries($))if(typeof Z==="string")j[J]=s1(Z);return j}function s1($){return $.replace(/\$\{(\w+)\}/g,(j,J)=>process.env[J]??"").replace(/\$env:(\w+)/g,(j,J)=>process.env[J]??"")}function A6($){if(typeof $!=="string")return;let j=s1($);if(j==="~")return m$.homedir();if(j.startsWith("~/")||j.startsWith("~\\"))return O$.join(m$.homedir(),j.slice(2));return j}function N6($){if(typeof $.bearerToken==="string")return s1($.bearerToken);return typeof $.bearerTokenEnv==="string"?process.env[$.bearerTokenEnv]:void 0}function r1($){if($===null||$===void 0||typeof $!=="object"){let J=JSON.stringify($);return J===void 0?"undefined":J}if(Array.isArray($))return`[${$.map((J)=>r1(J)).join(",")}]`;let j=$;return`{${Object.keys(j).sort().map((J)=>`${JSON.stringify(J)}:${r1(j[J])}`).join(",")}}`}import*as c$ from"node:fs";import*as IJ from"node:os";import*as h0 from"node:path";import{Compile as C6}from"typebox/compile";var yJ="DM_SUBAGENT_STRUCTURED_OUTPUT_SCHEMA",kJ="DM_SUBAGENT_STRUCTURED_OUTPUT_CAPTURE";function T6($,j="outputSchema"){if(!$||typeof $!=="object"||Array.isArray($))throw Error(`${j} must be a JSON Schema object.`)}function fJ($,j){T6($);let J=j??IJ.tmpdir();c$.mkdirSync(J,{recursive:!0});let Z=c$.mkdtempSync(h0.join(J,"dm-subagent-structured-")),Q=h0.join(Z,"schema.json"),X=h0.join(Z,"output.json");return c$.writeFileSync(Q,JSON.stringify($),{mode:384}),{schema:$,schemaPath:Q,outputPath:X}}function a1($,j){let J;try{J=C6($)}catch(Q){return{status:"invalid",message:`invalid outputSchema: ${Q instanceof Error?Q.message:String(Q)}`}}if(J.Check(j))return{status:"valid"};return{status:"invalid",message:[...J.Errors(j)].slice(0,8).map((Q)=>{return`${Q.instancePath?Q.instancePath.replace(/^\//,"").replace(/\//g,"."):"root"}: ${Q.message}`}).join("; ")||"schema validation failed"}}function PJ($){if(!c$.existsSync($.outputPath))return{error:"Missing structured_output call; this step has outputSchema and must finish by calling structured_output."};let j;try{j=JSON.parse(c$.readFileSync($.outputPath,"utf-8"))}catch(Z){return{error:`Failed to read structured output: ${Z instanceof Error?Z.message:String(Z)}`}}let J=a1($.schema,j);if(J.status==="invalid")return{error:`Structured output validation failed: ${J.message}`};return{value:j}}var xJ="DM_SUBAGENT_TOOL_BUDGET";function C0($){return{...$,toolCount:0,outcome:"within-budget"}}function Z1($,j,J){let Z=j>$.hard,Q=$.soft!==void 0&&j>=$.soft;return{...$,toolCount:j,outcome:Z?"hard-blocked":Q?"soft-reached":"within-budget",...Q?{softReachedAt:$.soft}:{},...Z?{hardReachedAt:$.hard,blockedTool:J}:{}}}function hJ($){return $?JSON.stringify($):void 0}var E6=["off","minimal","low","medium","high","xhigh"],S6=8000;function oJ($,j){let J=dJ($),Z=B$.extname(J),Q=[".js",".mjs",".cjs",".ts",".mts",".cts"].includes(Z)?Z:".ts";return B$.join(B$.dirname(J),`${j}${Q}`)}function uJ($){return B$.basename(dJ($)).startsWith("dm-args.")}function D6($=import.meta.url){let j=uJ($)?"subagent-prompt-runtime":B$.join("..","runs","shared","subagent-prompt-runtime");return oJ($,j)}function b6($=import.meta.url){let j=uJ($)?B$.join("..","..","extension","fanout-child"):"fanout-child";return oJ($,j)}var vJ=D6(),R6=b6(),w6="DM_SUBAGENT_CHILD",I6="DM_SUBAGENT_ORCHESTRATOR_TARGET",y6="DM_SUBAGENT_ORCHESTRATOR_SESSION_ID",k6="DM_SUBAGENT_SUPERVISOR_CHANNEL_DIR",gJ="DM_SUBAGENT_RUN_ID",f6="DM_SUBAGENT_CHILD_AGENT",P6="DM_SUBAGENT_CHILD_INDEX",x6="DM_SUBAGENT_FANOUT_CHILD",Q1="DM_SUBAGENT_PARENT_EVENT_SINK",e1="DM_SUBAGENT_PARENT_CONTROL_INBOX",X1="DM_SUBAGENT_PARENT_ROOT_RUN_ID",$j="DM_SUBAGENT_PARENT_RUN_ID",jj="DM_SUBAGENT_PARENT_CHILD_INDEX",Jj="DM_SUBAGENT_PARENT_DEPTH",Zj="DM_SUBAGENT_PARENT_PATH",Y1="DM_SUBAGENT_PARENT_CAPABILITY_TOKEN",mJ="DM_SUBAGENT_PARENT_SESSION",h6="DM_SUBAGENT_STEER_INBOX";function cJ($){return $.trim().replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^-+|-+$/g,"")||"unknown"}function v6($,j,J){return B$.join(r$,"supervisor-channels",`${cJ($)}-${cJ(j)}-${J}`)}function W1($,j,J=!1){if(!$||!j)return $;let Z=$.lastIndexOf(":");if(Z!==-1&&E6.includes($.substring(Z+1)))return J?`${$.slice(0,Z)}:${j}`:$;return`${$}:${j}`}function lJ($){let j=[...$.baseArgs];if($.sessionFile)b$.mkdirSync(B$.dirname($.sessionFile),{recursive:!0}),j.push("--session",$.sessionFile);else{if(!$.sessionEnabled)j.push("--no-session");if($.sessionDir)b$.mkdirSync($.sessionDir,{recursive:!0}),j.push("--session-dir",$.sessionDir)}let J=W1($.model,$.thinking);if(J)j.push("--model",J);let Z=$.tools?.filter((k)=>!(k.includes("/")||k.endsWith(".ts")||k.endsWith(".js")))??[],Q=$.requireReadTool&&$.tools?.length&&!Z.includes("read")?["read",...Z]:Z,X=Q.includes("subagent"),W=[];if($.tools?.length){let k=[...Q];for(let f of $.tools)if(!Q.includes(f)&&(f.includes("/")||f.endsWith(".ts")||f.endsWith(".js")))W.push(f);if(k.length>0){if($.mcpDirectTools?.length)k.push(...wJ($.mcpDirectTools,$.cwd));j.push("--tools",k.join(","))}}let U=X?[vJ,R6]:[vJ];if($.extensions!==void 0){j.push("--no-extensions");for(let k of[...new Set([...U,...W,...$.extensions,...$.subagentOnlyExtensions??[]])])j.push("--extension",k)}else for(let k of[...new Set([...U,...W,...$.subagentOnlyExtensions??[]])])j.push("--extension",k);if(!$.inheritSkills)j.push("--no-skills");let G;if($.systemPrompt!==void 0&&$.systemPrompt!==null){G=b$.mkdtempSync(B$.join(t1.tmpdir(),"dm-subagent-"));let k=($.promptFileStem??"prompt").replace(/[^\w.-]/g,"_"),f=B$.join(G,`${k}.md`);b$.writeFileSync(f,$.systemPrompt,{mode:384}),j.push($.systemPromptMode==="replace"?"--system-prompt":"--append-system-prompt",f)}if($.task.length>S6){if(!G)G=b$.mkdtempSync(B$.join(t1.tmpdir(),"dm-subagent-"));let k=B$.join(G,"task.md");b$.writeFileSync(k,`Task: ${$.task}`,{mode:384}),j.push(`@${k}`)}else j.push(`Task: ${$.task}`);let B={};B[w6]="1",B[x6]=X?"1":"0";let q=Boolean(process.env[Q1]&&process.env[X1]&&process.env[Y1]),O=$.parentRunId??$.runId??(q?process.env[gJ]:void 0)??process.env[$j]??"",T=$.parentChildIndex!==void 0?String($.parentChildIndex):$.childIndex!==void 0?String($.childIndex):process.env[jj]??"",y=Number(process.env[Jj]),h=$.parentDepth??(q&&Number.isFinite(y)?y+1:1),S=$.parentPath??[...i1(process.env[Zj]),...O?[{runId:O,...T&&/^\d+$/.test(T)?{stepIndex:Number(T)}:{},...$.childAgentName?{agent:$.childAgentName}:{}}]:[]];if(B[Q1]=X?$.parentEventSink??process.env[Q1]??"":"",B[e1]=X?$.parentControlInbox??process.env[e1]??"":"",B[X1]=X?$.parentRootRunId??process.env[X1]??$.runId??"":"",B[$j]=X?O:"",B[jj]=X?T:"",B[Jj]=X?String(h):"",B[Zj]=X?TJ(S):"",B[Y1]=X?$.parentCapabilityToken??process.env[Y1]??"":"",B.DM_SUBAGENT_INHERIT_PROJECT_CONTEXT=$.inheritProjectContext?"1":"0",B.DM_SUBAGENT_INHERIT_SKILLS=$.inheritSkills?"1":"0",$.intercomSessionName)B.DM_SUBAGENT_INTERCOM_SESSION_NAME=$.intercomSessionName;if($.orchestratorIntercomTarget)B[I6]=$.orchestratorIntercomTarget;if($.parentSessionId)B[y6]=$.parentSessionId;if($.orchestratorIntercomTarget&&$.parentSessionId&&$.runId&&$.childAgentName){let k=$.childIndex??0,f=v6($.runId,$.childAgentName,k);b$.mkdirSync(B$.join(f,"requests"),{recursive:!0}),b$.mkdirSync(B$.join(f,"replies"),{recursive:!0}),B[k6]=f}if($.runId)B[gJ]=$.runId;if($.childAgentName)B[f6]=$.childAgentName;if($.childIndex!==void 0)B[P6]=String($.childIndex);if($.mcpDirectTools?.length)B.MCP_DIRECT_TOOLS=$.mcpDirectTools.join(",");else B.MCP_DIRECT_TOOLS="__none__";if($.structuredOutput)B[kJ]=$.structuredOutput.outputPath,B[yJ]=$.structuredOutput.schemaPath;if($.steerInboxDir)B[h6]=$.steerInboxDir;let v$=hJ($.toolBudget);if(v$)B[xJ]=v$;return B[mJ]=$.parentSessionId??process.env[mJ]??"",{args:j,env:B,tempDir:G}}function pJ($){if(!$)return;try{b$.rmSync($,{recursive:!0,force:!0})}catch{}}class x extends Error{}var g6=/^[A-Za-z_][A-Za-z0-9_]*$/,m6=/^[A-Za-z_][A-Za-z0-9_]*$/,g0=/\{([A-Za-z_][A-Za-z0-9_]*)(?:\.([^{}]+))?\}/g,c6=new Set(["task","previous","chain_dir","outputs"]),rJ=new Set(["expand","parallel","collect","concurrency","failFast","phase","label","acceptance"]),d6=new Set([...rJ,"effectiveAcceptance","sessionFiles","thinkingOverrides"]),o6=new Set(["from","item","key","maxItems","onEmpty"]),u6=new Set(["output","path"]),sJ=new Set(["agent","task","phase","label","outputSchema","cwd","output","outputMode","reads","progress","skill","model","toolBudget","acceptance"]),l6=new Set([...sJ,"outputName","structured","inheritProjectContext","inheritSkills","skills","outputPath","maxSubagentDepth","structuredOutput","structuredOutputSchema","tools","extensions","subagentOnlyExtensions","mcpDirectTools","completionGuard","systemPrompt","systemPromptMode","thinking","modelCandidates","sessionFile","effectiveAcceptance","parentSessionId"]),p6=new Set(["as","outputSchema"]);function nJ($){return g6.test($)}function Qj($,j){if($==="")return;if(!$.startsWith("/"))throw new x(`${j} must be a JSON Pointer starting with '/'.`);for(let J of $.slice(1).split("/"))if(/~(?![01])/.test(J))throw new x(`${j} contains invalid JSON Pointer escape.`)}function n6($){return $.replace(/~1/g,"/").replace(/~0/g,"~")}function Xj($,j,J){if(Qj(j,J),j==="")return $;let Z=$;for(let Q of j.slice(1).split("/")){let X=n6(Q);if(Array.isArray(Z)){if(!/^(0|[1-9][0-9]*)$/.test(X))throw new x(`${J} segment '${X}' does not address an array index.`);let U=Number(X);if(U>=Z.length)throw new x(`${J} does not exist.`);Z=Z[U];continue}if(!Z||typeof Z!=="object")throw new x(`${J} does not exist.`);let W=Z;if(!Object.prototype.hasOwnProperty.call(W,X))throw new x(`${J} does not exist.`);Z=W[X]}return Z}function i6($,j){if(typeof $==="string"||typeof $==="number"||typeof $==="boolean"){let J=String($);if(!J.trim())throw new x(`${j} resolved to an empty key.`);if(/[\u0000-\u001F\u007F]/.test(J))throw new x(`${j} resolved to an unsafe key.`);if(J.length>200)throw new x(`${j} resolved to a key longer than 200 characters.`);return J}throw new x(`${j} must resolve to a string, number, or boolean.`)}function r6($){return $.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,80)||"item"}function s6($,j){if($===void 0)throw new x(`Unresolved item reference '${j}'.`);if(typeof $==="string")return $;if(typeof $==="number"||typeof $==="boolean"||$===null)return String($);return JSON.stringify($)}function a6($,j,J){if(!j)return $;let Z=`/${j.split(".").map((Q)=>Q.replace(/~/g,"~0").replace(/\//g,"~1")).join("/")}`;return Xj($,Z,J)}function iJ($,j,J){return $.replace(g0,(Z,Q,X)=>{if(Q!==j)return Z;if(X!==void 0&&(!X.trim()||X.includes("..")))throw new x(`Invalid item reference '${Z}'.`);return s6(a6(J,X,Z),Z)})}function v0($,j,J){if(!$||typeof $!=="object"||Array.isArray($))throw new x(`${J} must be an object.`);for(let Z of Object.keys($))if(!j.has(Z))throw new x(`${J} does not support field '${Z}'.`)}function t6($,j,J){for(let Z of $.matchAll(/\{([^{}]*)\}/g)){let Q=Z[0],X=Z[1];if(X===j||X.startsWith(`${j}.`)){if(!g0.test(Q)||X===`${j}.`||X.includes(".."))throw new x(`Invalid item reference '${Q}' in ${J}.`);g0.lastIndex=0;continue}g0.lastIndex=0;let W=X.match(/^[A-Za-z_][A-Za-z0-9_]*/)?.[0];if(W===j)throw new x(`Invalid item reference '${Q}' in ${J}.`);if(W&&c6.has(W))continue;if(W)throw new x(`Unsupported template reference '${Q}' in ${J}.`)}if(g0.lastIndex=0,$.includes(`{${j}.}`)||new RegExp(`\\{${j}(?:\\.|$)[^}]*$`).test($))throw new x(`Invalid item reference in ${J}.`)}function aJ($,j,J={}){let Z=`Dynamic chain step ${j+1}`;if(v0($,J.allowRunnerFields?d6:rJ,Z),!$.expand||!$.expand.from)throw new x(`${Z} requires expand.from.`);if(v0($.expand,o6,`${Z} expand`),v0($.expand.from,u6,`${Z} expand.from`),!nJ($.expand.from.output))throw new x(`${Z} has invalid expand.from.output '${$.expand.from.output}'.`);if(Qj($.expand.from.path,`${Z} expand.from.path`),$.expand.key!==void 0)Qj($.expand.key,`${Z} expand.key`);let Q=$.expand.item??"item";if(!m6.test(Q))throw new x(`${Z} has invalid expand.item '${Q}'.`);if($.expand.maxItems===void 0&&J.maxItems===void 0)throw new x(`${Z} requires expand.maxItems or config.chain.dynamicFanout.maxItems.`);if($.expand.maxItems!==void 0&&(!Number.isInteger($.expand.maxItems)||$.expand.maxItems<0))throw new x(`${Z} expand.maxItems must be an integer >= 0.`);if(J.maxItems!==void 0&&(!Number.isInteger(J.maxItems)||J.maxItems<0))throw new x("config.chain.dynamicFanout.maxItems must be an integer >= 0.");if(!$.parallel||Array.isArray($.parallel))throw new x(`${Z} requires a single parallel template object and cannot mix dynamic expand/collect with static parallel arrays.`);if(v0($.parallel,J.allowRunnerFields?l6:sJ,`${Z} parallel`),"expand"in $.parallel)throw new x(`${Z} does not support nested dynamic fanout.`);if(!$.parallel.agent)throw new x(`${Z} parallel.agent is required.`);if(!$.collect?.as||!nJ($.collect.as))throw new x(`${Z} requires collect.as with a safe output name.`);v0($.collect,p6,`${Z} collect`);for(let[X,W]of[["parallel.task",$.parallel.task],["parallel.label",$.parallel.label]])if(W)t6(W,Q,`${Z} ${X}`)}function e6($,j,J,Z={}){aJ($,J,Z);let Q=$.expand.from.output,X=j[Q];if(!X)throw new x(`Dynamic chain step ${J+1} references unknown output '${Q}'.`);if(X.structured===void 0)throw new x(`Dynamic chain step ${J+1} requires structured output '${Q}'.`);let W=Xj(X.structured,$.expand.from.path,`Dynamic chain step ${J+1} expand.from.path`);if(!Array.isArray(W))throw new x(`Dynamic chain step ${J+1} expand.from.path must resolve to an array.`);let U=$.expand.maxItems??Z.maxItems;if(U===void 0)throw new x(`Dynamic chain step ${J+1} requires an effective maxItems.`);if(W.length>U)throw new x(`Dynamic chain step ${J+1} resolved ${W.length} items, exceeding maxItems ${U}.`);let G=new Set,B=new Set;return W.map((q,O)=>{let T=$.expand.key===void 0?String(O):i6(Xj(q,$.expand.key,`Dynamic chain step ${J+1} expand.key`),`Dynamic chain step ${J+1} expand.key`);if(G.has(T))throw new x(`Dynamic chain step ${J+1} produced duplicate item key '${T}'.`);G.add(T);let y=r6(T);if(B.has(y))throw new x(`Dynamic chain step ${J+1} produced colliding item id '${y}'.`);return B.add(y),{index:O,key:T,idKey:y,item:q}})}function tJ($,j,J,Z={}){let Q=e6($,j,J,Z);if(Q.length===0){if(($.expand.onEmpty??"skip")==="fail")throw new x(`Dynamic chain step ${J+1} source array is empty.`);return{items:Q,parallel:[],collectedOnEmpty:[]}}let X=$.expand.item??"item",W=Q.map((U)=>{let G=iJ($.parallel.task??"{previous}",X,U.item),B=$.parallel.label?iJ($.parallel.label,X,U.item):void 0;return{...$.parallel,task:G,...B!==void 0?{label:B}:{}}});return{items:Q,parallel:W}}function eJ($,j,J){return j.map((Z,Q)=>{let X=J[Q],W=X?"output"in X&&typeof X.output==="string"?X.output:h1(X):"";return{key:Z.key,index:Z.index,item:Z.item,agent:X?.agent??$.parallel.agent,exitCode:X?.exitCode??null,text:W,...X?.structuredOutput!==void 0?{structured:X.structuredOutput}:{},...X?.error?{error:X.error}:{},...X?.timedOut?{timedOut:!0}:{},...X?.savedOutputPath?{outputPath:X.savedOutputPath}:{},...X?.artifactPaths?{artifactPaths:X.artifactPaths}:{}}})}function Yj($,j){if(!$)return;let J=a1($,j);if(J.status==="invalid")throw new x(`Collected output validation failed: ${J.message}`)}var $4=/\{outputs\.([^}]*)\}/g,j4=/^[A-Za-z_][A-Za-z0-9_]*$/;class Wj extends Error{}function $8($,j){return $.replace($4,(J,Z)=>{if(!j4.test(Z))throw new Wj(`Invalid chain output reference '${J}'. Use {outputs.name} with /^[A-Za-z_][A-Za-z0-9_]*$/ names.`);let Q=j[Z];if(!Q)throw new Wj(`Unknown chain output reference '${J}'.`);return Q.text})}function J4($){return JSON.stringify($)}function Hj($,j){return{text:$.structuredOutput!==void 0?J4($.structuredOutput):$.output,...$.structuredOutput!==void 0?{structured:$.structuredOutput}:{},agent:$.agent,stepIndex:j}}import{randomUUID as Z4}from"node:crypto";import*as R$ from"node:fs";import*as Q$ from"node:path";var j8=Q$.join(r$,"nested-subagent-events");var Q4="registry.json",Gj=65536,Vj=12,V1=16,Kj=3;function K1($){return n1($)}function X4($,j){if(!K1(j))throw Error(`${$} must be a non-empty safe id token.`)}function J8($,j){X4($,j)}function Uj($,j){let J=Q$.resolve($),Z=Q$.resolve(j);return Z===J||Z.startsWith(`${J}${Q$.sep}`)}function Q8($){return Q$.dirname(Q$.resolve($.eventSink))}function Bj($){if(J8("rootRunId",$.rootRunId),J8("capabilityToken",$.capabilityToken),!Uj(j8,$.eventSink))throw Error("Nested event sink is outside the subagent nested event root.");if(!Uj(j8,$.controlInbox))throw Error("Nested control inbox is outside the subagent nested event root.");if(Q8($)!==Q$.dirname(Q$.resolve($.controlInbox)))throw Error("Nested event sink and control inbox must share one route root.")}function _j($,j){if(!j.asyncDir)return;let J=Q$.resolve(j.asyncDir),Z=Q$.resolve(r$,"nested-subagent-runs",$,j.id),Q=Q$.relative(Z,J);return J===Z||!Q.startsWith("..")&&!Q$.isAbsolute(Q)?J:void 0}function b($){return typeof $==="number"&&Number.isFinite($)?$:void 0}function m($,j=512){return typeof $==="string"&&$.length>0?$.slice(0,j):void 0}function Y4($){if(!$||typeof $!=="object")return;let j=$,J=b(j.input),Z=b(j.output),Q=b(j.total);return J!==void 0&&Z!==void 0&&Q!==void 0?{input:J,output:Z,total:Q}:void 0}function W4($){if(!$||typeof $!=="object")return;let j=$,J=b(j.inputTokens),Z=b(j.outputTokens),Q=b(j.costUsd);return J!==void 0&&Z!==void 0&&Q!==void 0?{inputTokens:J,outputTokens:Z,costUsd:Q}:void 0}function U1($){if(!$||typeof $!=="object")return;let j=$,J=b(j.maxTurns),Z=b(j.graceTurns),Q=b(j.turnCount),X=j.outcome==="within-budget"||j.outcome==="wrap-up-requested"||j.outcome==="exceeded"?j.outcome:void 0;if(J===void 0||Z===void 0||Q===void 0||!X)return;return{maxTurns:J,graceTurns:Z,turnCount:Q,outcome:X,...b(j.wrapUpRequestedAtTurn)!==void 0?{wrapUpRequestedAtTurn:b(j.wrapUpRequestedAtTurn)}:{},...b(j.exceededAtTurn)!==void 0?{exceededAtTurn:b(j.exceededAtTurn)}:{}}}function H4($,j){return $==="queued"||$==="running"||$==="complete"||$==="failed"||$==="paused"?$:j}function V4($,j){if(!$||typeof $!=="object")return;let J=$,Z=m(J.agent,128);if(!Z)return;let Q=J.status==="pending"||J.status==="running"||J.status==="complete"||J.status==="completed"||J.status==="failed"||J.status==="paused"?J.status:"pending";return{agent:Z,status:Q,...m(J.sessionFile,2048)?{sessionFile:m(J.sessionFile,2048)}:{},...J.activityState==="active_long_running"||J.activityState==="needs_attention"?{activityState:J.activityState}:{},...b(J.lastActivityAt)!==void 0?{lastActivityAt:b(J.lastActivityAt)}:{},...m(J.currentTool,128)?{currentTool:m(J.currentTool,128)}:{},...b(J.currentToolStartedAt)!==void 0?{currentToolStartedAt:b(J.currentToolStartedAt)}:{},...m(J.currentPath,2048)?{currentPath:m(J.currentPath,2048)}:{},...b(J.turnCount)!==void 0?{turnCount:b(J.turnCount)}:{},...b(J.toolCount)!==void 0?{toolCount:b(J.toolCount)}:{},...b(J.startedAt)!==void 0?{startedAt:b(J.startedAt)}:{},...b(J.endedAt)!==void 0?{endedAt:b(J.endedAt)}:{},...m(J.error,1024)?{error:m(J.error,1024)}:{},...J.timedOut===!0?{timedOut:!0}:{},...U1(J.turnBudget)?{turnBudget:U1(J.turnBudget)}:{},...J.turnBudgetExceeded===!0?{turnBudgetExceeded:!0}:{},...J.wrapUpRequested===!0?{wrapUpRequested:!0}:{},...j<Kj&&Array.isArray(J.children)?{children:J.children.map((X)=>z1(X,j+1)).filter((X)=>Boolean(X)).slice(0,V1)}:{}}}function z1($,j=0){if(!$||typeof $!=="object")return;let J=$;if(!K1(J.id)||!K1(J.parentRunId))return;let Z=J1(J.path),Q=Array.isArray(J.steps)?J.steps.map((U)=>V4(U,j+1)).filter((U)=>Boolean(U)).slice(0,Vj):void 0,X=Y4(J.totalTokens),W=W4(J.totalCost);return{id:J.id,parentRunId:J.parentRunId,...b(J.parentStepIndex)!==void 0?{parentStepIndex:b(J.parentStepIndex)}:{},...m(J.parentAgent,128)?{parentAgent:m(J.parentAgent,128)}:{},depth:Math.min(Math.max(0,b(J.depth)??0),Kj),path:Z,state:H4(J.state,"running"),...m(J.asyncDir,2048)?{asyncDir:m(J.asyncDir,2048)}:{},...b(J.pid)!==void 0&&b(J.pid)>0&&Number.isInteger(b(J.pid))?{pid:b(J.pid)}:{},...m(J.sessionId,256)?{sessionId:m(J.sessionId,256)}:{},...m(J.sessionFile,2048)?{sessionFile:m(J.sessionFile,2048)}:{},...m(J.intercomTarget,256)?{intercomTarget:m(J.intercomTarget,256)}:{},...m(J.ownerIntercomTarget,256)?{ownerIntercomTarget:m(J.ownerIntercomTarget,256)}:{},...m(J.leafIntercomTarget,256)?{leafIntercomTarget:m(J.leafIntercomTarget,256)}:{},...J.ownerState==="live"||J.ownerState==="gone"||J.ownerState==="unknown"?{ownerState:J.ownerState}:{},...m(J.controlInbox,2048)?{controlInbox:m(J.controlInbox,2048)}:{},...m(J.capabilityToken,128)?{capabilityToken:m(J.capabilityToken,128)}:{},...J.mode==="single"||J.mode==="parallel"||J.mode==="chain"?{mode:J.mode}:{},...m(J.agent,128)?{agent:m(J.agent,128)}:{},...Array.isArray(J.agents)?{agents:J.agents.map((U)=>m(U,128)).filter((U)=>Boolean(U)).slice(0,Vj)}:{},...b(J.currentStep)!==void 0?{currentStep:b(J.currentStep)}:{},...b(J.chainStepCount)!==void 0?{chainStepCount:b(J.chainStepCount)}:{},...J.activityState==="active_long_running"||J.activityState==="needs_attention"?{activityState:J.activityState}:{},...b(J.lastActivityAt)!==void 0?{lastActivityAt:b(J.lastActivityAt)}:{},...m(J.currentTool,128)?{currentTool:m(J.currentTool,128)}:{},...b(J.currentToolStartedAt)!==void 0?{currentToolStartedAt:b(J.currentToolStartedAt)}:{},...m(J.currentPath,2048)?{currentPath:m(J.currentPath,2048)}:{},...b(J.turnCount)!==void 0?{turnCount:b(J.turnCount)}:{},...b(J.toolCount)!==void 0?{toolCount:b(J.toolCount)}:{},...X?{totalTokens:X}:{},...W?{totalCost:W}:{},...b(J.startedAt)!==void 0?{startedAt:b(J.startedAt)}:{},...b(J.endedAt)!==void 0?{endedAt:b(J.endedAt)}:{},...b(J.lastUpdate)!==void 0?{lastUpdate:b(J.lastUpdate)}:{},...b(J.timeoutMs)!==void 0?{timeoutMs:b(J.timeoutMs)}:{},...b(J.deadlineAt)!==void 0?{deadlineAt:b(J.deadlineAt)}:{},...J.timedOut===!0?{timedOut:!0}:{},...U1(J.turnBudget)?{turnBudget:U1(J.turnBudget)}:{},...J.turnBudgetExceeded===!0?{turnBudgetExceeded:!0}:{},...J.wrapUpRequested===!0?{wrapUpRequested:!0}:{},...m(J.error,1024)?{error:m(J.error,1024)}:{},...Q&&Q.length>0?{steps:Q}:{},...j<Kj&&Array.isArray(J.children)?{children:J.children.map((U)=>z1(U,j+1)).filter((U)=>Boolean(U)).slice(0,V1)}:{}}}function zj($,j){if(Buffer.byteLength($,"utf-8")>Gj)return;let J;try{J=JSON.parse($)}catch{return}if(!J||typeof J!=="object")return;let Z=J;if(Z.type!=="subagent.nested.started"&&Z.type!=="subagent.nested.updated"&&Z.type!=="subagent.nested.completed")return;if(Z.rootRunId!==j.rootRunId||Z.capabilityToken!==j.capabilityToken)return;if(!K1(Z.parentRunId))return;let Q=b(Z.ts);if(Q===void 0)return;let X=z1(Z.child);if(!X||X.id===j.rootRunId)return;let W={...X,controlInbox:j.controlInbox,capabilityToken:j.capabilityToken,ownerState:X.ownerState??"unknown"};return{type:Z.type,ts:Q,rootRunId:j.rootRunId,parentRunId:Z.parentRunId,...b(Z.parentStepIndex)!==void 0?{parentStepIndex:b(Z.parentStepIndex)}:{},capabilityToken:j.capabilityToken,child:W}}function K4($,j){if(!$.includes(`
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
...[truncated]`:j}function o0($){return Z5($.map((j)=>j?.trim()).filter((j)=>Boolean(j)))}function Ej($){let j=$.results.map((Q)=>Q.acceptance?.childReport).filter((Q)=>Boolean(Q)),J=$.results.filter((Q)=>Q.exitCode!==0||Q.acceptance?.status==="rejected"),Z=$.results.length>0&&J.length===0;return{criteriaSatisfied:[{id:"criterion-1",status:Z?"satisfied":"not-satisfied",evidence:Z?`All ${$.results.length} dynamic child run(s) completed without child or acceptance blockers.`:"Dynamic fanout produced no accepted child evidence."},{id:"criterion-2",status:Z?"satisfied":"not-satisfied",evidence:Z?"Collected child acceptance evidence for aggregate review.":"Dynamic fanout produced no aggregate review evidence."},...$.results.map((Q,X)=>({id:`child-${X+1}`,status:Q.exitCode===0&&Q.acceptance?.status!=="rejected"?"satisfied":"not-satisfied",evidence:`${Q.agent}: acceptance ${Q.acceptance?.status??"unreported"}${Q.error?` (${Q.error})`:""}`}))],changedFiles:o0(j.flatMap((Q)=>Q.changedFiles??[])),testsAddedOrUpdated:o0(j.flatMap((Q)=>Q.testsAddedOrUpdated??[])),commandsRun:j.flatMap((Q)=>Q.commandsRun??[]),validationOutput:o0(j.flatMap((Q)=>Q.validationOutput??[])),residualRisks:o0([...j.flatMap((Q)=>Q.residualRisks??[]),...J.map((Q)=>`${Q.agent}: ${Q.error??"child or acceptance gate failed"}`)]),noStagedFiles:j.length>0&&j.every((Q)=>Q.noStagedFiles===!0),reviewFindings:o0(j.flatMap((Q)=>Q.reviewFindings??[])),manualNotes:$.notes??`Aggregated acceptance evidence from ${$.results.length} dynamic fanout child run(s).`,notes:$.notes}}function G5($,j,J={}){return new Promise((Z)=>{let Q=Date.now(),X=$.cwd?w8.resolve(j,$.cwd):j,W="",U="",G=!1,B=!1,q,O=j5($.command,{cwd:X,env:{...process.env,...$.env??{}},shell:!0,stdio:["ignore","pipe","pipe"],windowsHide:!0}),T=(S)=>{if(B)return;if(B=!0,clearTimeout(h),q)clearTimeout(q);J.signal?.removeEventListener("abort",y),Z({id:$.id,command:$.command,cwd:X,durationMs:Date.now()-Q,...S})},y=()=>{if(B||G)return;G=!0,O.kill("SIGTERM"),q=setTimeout(()=>{O.kill("SIGKILL"),T({exitCode:null,status:"timed-out",stdout:d0(W),stderr:d0(U||J.abortMessage||"Acceptance verification timed out.")})},1000),q.unref?.()},h=setTimeout(y,$.timeoutMs??120000);if(h.unref?.(),J.signal?.aborted)y();else J.signal?.addEventListener("abort",y,{once:!0});O.stdout.on("data",(S)=>{W+=S.toString()}),O.stderr.on("data",(S)=>{U+=S.toString()}),O.on("close",(S)=>{T({exitCode:S,status:G?"timed-out":S===0&&!G?"passed":$.allowFailure?"allowed-failure":"failed",stdout:d0(W),stderr:d0(U||(G?J.abortMessage??"":""))})}),O.on("error",(S)=>{T({exitCode:G?null:1,status:G?"timed-out":$.allowFailure?"allowed-failure":"failed",stderr:G?d0(U||J.abortMessage||"Acceptance verification timed out."):S instanceof Error?S.message:String(S)})})})}async function M1($){let j=$.acceptance,J={status:j.level==="none"?"not-required":"claimed",explicit:j.explicit,effectiveAcceptance:j,inferredReason:j.inferredReason,criteria:j.criteria,runtimeChecks:[],verifyRuns:[]};if(j.level==="none")return J;let Z=$.report?{report:$.report}:W5($.output);if(Z.report)J.childReport=Z.report,J.status="attested";else return J.childReportParseError=Z.error,J.runtimeChecks.push({id:"attestation",status:"failed",message:Z.error??"Structured acceptance report missing."}),J.status="rejected",J;if(_1[j.level]>=_1.checked){if(J.runtimeChecks=[...V5(j.criteria,Z.report),...z5(j,Z.report,$.cwd)],J.runtimeChecks.some((Q)=>Q.status==="failed"))return J.status="rejected",J;J.status="checked"}if(_1[j.level]>=_1.verified&&(j.level==="verified"||j.verify.length>0)){if(j.level==="verified"&&j.verify.length===0)return J.runtimeChecks.push({id:"verification-config",status:"failed",message:"verified acceptance requires runtime verify commands."}),J.status="rejected",J;J.verifyRuns=[];for(let Q of j.verify)if(J.verifyRuns.push(await G5(Q,$.cwd,{signal:$.signal,abortMessage:$.abortMessage})),$.signal?.aborted)break;if(J.verifyRuns.some((Q)=>Q.status==="failed"||Q.status==="timed-out"))return J.status="rejected",J;J.status="verified"}if(j.level==="reviewed")if($.reviewResult)J.reviewResult=$.reviewResult,J.status=$.reviewResult.status==="no-blockers"?"reviewed":"rejected";else{let Q=j.review&&j.review!==!1&&j.review.required===!1;if(J.reviewResult={status:"needs-parent-decision",findings:[{severity:j.explicit&&!Q?"blocker":"non-blocking",issue:"Reviewed acceptance requires an independent reviewer result.",rationale:"The run cannot be marked reviewed from child evidence alone."}]},j.review===!1||j.explicit&&!Q)J.status="rejected"}return J}function L1($){if($.status!=="rejected")return;let j=$.runtimeChecks.find((Z)=>Z.status==="failed");if(j)return`Acceptance rejected: ${j.message}`;let J=$.verifyRuns.find((Z)=>Z.status==="failed"||Z.status==="timed-out");if(J)return`Acceptance verification '${J.id}' ${J.status}.`;if($.reviewResult?.status==="needs-parent-decision")return"Acceptance review required but no automatic reviewer result is available.";if($.reviewResult?.status==="blockers")return"Acceptance review found blockers.";return"Acceptance rejected."}import*as q1 from"node:fs";var B5=new Set(["complete","failed","paused"]),_5=new Set(["complete","completed","failed","paused"]);function M5($){try{return JSON.parse(q1.readFileSync($,"utf-8"))}catch(j){if(typeof j==="object"&&j!==null&&"code"in j&&j.code==="ENOENT")return;throw j}}function F1($,j){return $?.steps?.[j]}function L5($,j){if(!$)return!1;let J=F1($,j);if(J&&_5.has(J.status))return!0;return B5.has($.state)}function q5($,j){if(!$)return;if(j?.success===!0)return"complete";if(j?.success===!1)return $.state==="paused"?"paused":"failed";if($.state==="complete"||$.state==="failed"||$.state==="paused")return $.state;if($.success===!0)return"complete";if($.success===!1)return"failed";return}function F5($,j,J){let Z=J?.agent??j.steps?.[$.index]?.agent??"subagent",Q=J?.timedOut===!0||j.timedOut===!0,X=J?.error??j.error??`Attached async root ${$.runId} ended without a result file at ${$.resultPath}.`;return{agent:Z,output:X,success:!1,exitCode:1,error:X,...Q?{timedOut:!0}:{},...J?.sessionFile??j.sessionFile?{sessionFile:J?.sessionFile??j.sessionFile}:{},...J?.model?{model:J.model}:{},...J?.attemptedModels?{attemptedModels:J.attemptedModels}:{},...J?.modelAttempts?{modelAttempts:J.modelAttempts}:{},...J?.totalCost?{totalCost:J.totalCost}:{},...J?.structuredOutput!==void 0?{structuredOutput:J.structuredOutput}:{},...J?.structuredOutputPath?{structuredOutputPath:J.structuredOutputPath}:{},...J?.structuredOutputSchemaPath?{structuredOutputSchemaPath:J.structuredOutputSchemaPath}:{},...J?.acceptance?{acceptance:J.acceptance}:{}}}function O5($,j,J){let Z=F1(j,$.index);return{agent:Z?.agent??j?.steps?.[$.index]?.agent??"subagent",output:J,success:!1,exitCode:1,error:J,timedOut:!0,...Z?.sessionFile??j?.sessionFile?{sessionFile:Z?.sessionFile??j?.sessionFile}:{},...Z?.model?{model:Z.model}:{},...Z?.attemptedModels?{attemptedModels:Z.attemptedModels}:{},...Z?.modelAttempts?{modelAttempts:Z.modelAttempts}:{},...Z?.totalCost?{totalCost:Z.totalCost}:{}}}function A5($,j,J){let Z=J.results?.[$.index],Q=F1(j,$.index),X=q5(J,Z),W=Z?.agent??Q?.agent??j?.steps?.[$.index]?.agent??"subagent",U=Z?.output??J.summary??"",G=Z?.timedOut===!0||Q?.timedOut===!0||J.timedOut===!0||j?.timedOut===!0,B=X==="complete"&&!G,q=Z?.error??(B?void 0:J.error??J.summary??j?.error??`Attached async root ${$.runId} did not complete successfully.`);return{agent:W,output:B?U:U||q||"",success:B,exitCode:B?0:1,...q?{error:q}:{},...G?{timedOut:!0}:{},...Z?.sessionFile??Q?.sessionFile??j?.sessionFile?{sessionFile:Z?.sessionFile??Q?.sessionFile??j?.sessionFile}:{},...Z?.intercomTarget?{intercomTarget:Z.intercomTarget}:{},...Z?.model??Q?.model?{model:Z?.model??Q?.model}:{},...Z?.attemptedModels??Q?.attemptedModels?{attemptedModels:Z?.attemptedModels??Q?.attemptedModels}:{},...Z?.modelAttempts??Q?.modelAttempts?{modelAttempts:Z?.modelAttempts??Q?.modelAttempts}:{},...Z?.totalCost??Q?.totalCost?{totalCost:Z?.totalCost??Q?.totalCost}:{},...Z?.structuredOutput!==void 0?{structuredOutput:Z.structuredOutput}:Q?.structuredOutput!==void 0?{structuredOutput:Q.structuredOutput}:{},...Z?.structuredOutputPath??Q?.structuredOutputPath?{structuredOutputPath:Z?.structuredOutputPath??Q?.structuredOutputPath}:{},...Z?.structuredOutputSchemaPath??Q?.structuredOutputSchemaPath?{structuredOutputSchemaPath:Z?.structuredOutputSchemaPath??Q?.structuredOutputSchemaPath}:{},...Z?.acceptance??Q?.acceptance?{acceptance:Z?.acceptance??Q?.acceptance}:{}}}async function x8($,j={}){let J=j.pollIntervalMs??500,Z=j.terminalResultGraceMs??1000,Q=j.now??Date.now,X;for(;;){let W=R0($.asyncDir);if(j.shouldAbort?.())return O5($,W,j.timeoutMessage??"Subagent timed out.");let U=M5($.resultPath);if(U)return A5($,W,U);if(L5(W,$.index)){if(X??=Q(),Q()-X>=Z)return F5($,W,F1(W,$.index))}else X=void 0;if(!W&&!q1.existsSync($.asyncDir))throw Error(`Attached async root '${$.runId}' directory does not exist: ${$.asyncDir}`);await new Promise((G)=>setTimeout(G,J))}}import*as _0 from"node:fs";import*as Sj from"node:path";var N5="append-requests";function C5($){return Sj.join($,N5)}function v8($){let j=C5($);try{return _0.readdirSync(j).filter((J)=>J.endsWith(".json")).map((J)=>Sj.join(j,J)).sort()}catch(J){if(J.code==="ENOENT")return[];throw J}}function Dj($){return v8($).length}function T5($){let j=JSON.parse(_0.readFileSync($,"utf-8"));if(!j.id||typeof j.id!=="string")return;if(!Number.isFinite(j.createdAt))return;if(!Array.isArray(j.steps)||j.steps.length===0)return;return{id:j.id,createdAt:j.createdAt,steps:j.steps}}function g8($){let j=[];for(let J of v8($)){let Z=T5(J);try{_0.unlinkSync(J)}catch{}if(Z)j.push(Z)}return j.sort((J,Z)=>J.createdAt-Z.createdAt||J.id.localeCompare(Z.id))}function h8($){return{agent:$.agent,phase:$.phase,label:$.label,outputName:$.outputName,structured:$.structured,status:"pending",...$.sessionFile?{sessionFile:$.sessionFile}:{},skills:$.skills,model:$.model,thinking:$.thinking,attemptedModels:$.modelCandidates&&$.modelCandidates.length>0?$.modelCandidates:$.model?[$.model]:void 0,recentTools:[],recentOutput:[]}}function E5($){if($0($))return $.parallel.map(h8);if(j0($))return[{agent:`expand:${$.parallel.agent}`,phase:$.phase??$.parallel.phase,label:$.label??$.parallel.label??`Dynamic fanout (${$.collect.as})`,outputName:$.collect.as,structured:Boolean($.collect.outputSchema),status:"pending",recentTools:[],recentOutput:[]}];return[h8($)]}function m8($,j,J){if(!j)return;let Z=$.phases.find((Q)=>Q.title===j);if(!Z)Z={title:j,nodeIds:[]},$.phases.push(Z);Z.nodeIds.push(J)}function S5($,j,J){return{id:`step-${j}`,kind:"step",agent:$.agent,phase:$.phase,label:$.label?.trim()||$.agent||`Step ${j+1}`,status:"pending",flatIndex:J,stepIndex:j,outputName:$.outputName,structured:$.structured}}function D5($,j,J,Z){let Q=$.parallel.map((X,W)=>{let U=`step-${j}-agent-${W}`;return m8(Z,X.phase,U),{id:U,kind:"agent",agent:X.agent,phase:X.phase,label:X.label?.trim()||X.agent||`Agent ${W+1}`,status:"pending",flatIndex:J+W,stepIndex:j,outputName:X.outputName,structured:X.structured}});return{id:`step-${j}`,kind:"parallel-group",label:$.parallel.length===1?"Parallel task":`Parallel group (${$.parallel.length})`,status:"pending",stepIndex:j,children:Q}}function b5($,j){return{id:`step-${j}`,kind:"dynamic-parallel-group",label:$.label?.trim()||$.parallel.label?.trim()||`Dynamic fanout (${$.collect.as})`,status:"pending",stepIndex:j,outputName:$.collect.as,structured:Boolean($.collect.outputSchema),dynamic:{sourceOutput:$.expand.from.output,sourcePath:$.expand.from.path,itemName:$.expand.item??"item",maxItems:$.expand.maxItems,collectAs:$.collect.as},children:[]}}function R5($,j,J,Z){if(!$)return;if($0(j)){$.nodes.push(D5(j,J,Z,$));return}if(j0(j)){$.nodes.push(b5(j,J));return}let Q=S5(j,J,Z);$.nodes.push(Q),m8($,j.phase,Q.id)}function c8($){let j=0,J=0;for(let Z of $.steps){let Q=$.status.chainStepCount??$.status.steps?.length??0,X=$.status.steps?.length??0,W=E5(Z);if($.status.steps??=[],$.status.steps.push(...W),$0(Z))$.status.parallelGroups??=[],$.status.parallelGroups.push({start:X,count:Z.parallel.length,stepIndex:Q});else if(j0(Z))$.status.parallelGroups??=[],$.status.parallelGroups.push({start:X,count:1,stepIndex:Q});R5($.status.workflowGraph,Z,Q,X),$.status.chainStepCount=Q+1,j++,J+=W.length}return $.status.pendingAppends=$.pendingAppends??0,$.status.lastUpdate=$.now??Date.now(),{addedChainSteps:j,addedFlatSteps:J}}function d8($,j){if(!j)return $;let J=j.graceTurns===1?"1 additional assistant turn":`${j.graceTurns} additional assistant turns`,Z=["## Turn budget",`This child run has a soft budget of ${j.maxTurns} assistant turn${j.maxTurns===1?"":"s"}.`,`After that, ${J} may be allowed only for a final wrap-up.`,"When you approach or reach the soft budget, stop starting new tool work and return the final answer immediately.","This runner uses process-mode execution, so live steering after launch may be unavailable; treat this instruction as the wrap-up request.","If you continue past the soft budget plus grace turns, the supervisor may abort the process and return only partial output."].join(`
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
647
|
+
${task}`, "utf-8");
|
|
648
|
+
}
|
|
649
|
+
if (ctx.artifactConfig?.includeTranscript !== false) {
|
|
650
|
+
transcriptWriter = createChildTranscriptWriter({
|
|
651
|
+
transcriptPath: artifactPaths.transcriptPath,
|
|
652
|
+
source: "async",
|
|
653
|
+
runId: ctx.id,
|
|
654
|
+
agent: step.agent,
|
|
655
|
+
childIndex: ctx.flatIndex,
|
|
656
|
+
cwd: step.cwd ?? ctx.cwd
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
transcriptWriter?.writeInitialUserMessage(task);
|
|
661
|
+
const candidates = step.modelCandidates && step.modelCandidates.length > 0 ? step.modelCandidates : step.model ? [step.model] : [undefined];
|
|
662
|
+
const attemptedModels = [];
|
|
663
|
+
const modelAttempts = [];
|
|
664
|
+
const attemptNotes = [];
|
|
665
|
+
const eventsPath = path.join(path.dirname(ctx.outputFile), "events.jsonl");
|
|
666
|
+
let finalResult;
|
|
667
|
+
let finalOutputSnapshot;
|
|
668
|
+
let completionGuardTriggeredFinal = false;
|
|
669
|
+
let turnBudget = ctx.turnBudget ? initialTurnBudgetState(ctx.turnBudget) : undefined;
|
|
670
|
+
let toolBudget = step.toolBudget ? initialToolBudgetState(step.toolBudget) : undefined;
|
|
671
|
+
let toolBudgetBlocked = false;
|
|
672
|
+
for (let index = 0;index < candidates.length; index++) {
|
|
673
|
+
if (ctx.timeoutSignal?.aborted || ctx.skipAcceptance?.())
|
|
674
|
+
break;
|
|
675
|
+
const candidate = candidates[index];
|
|
676
|
+
ctx.onAttemptStart?.({ model: candidate, thinking: resolveEffectiveThinking(candidate, step.thinking) });
|
|
677
|
+
const outputSnapshot = captureSingleOutputSnapshot(step.outputPath);
|
|
678
|
+
if (effectiveStructuredOutput) {
|
|
679
|
+
try {
|
|
680
|
+
if (fs.existsSync(effectiveStructuredOutput.outputPath))
|
|
681
|
+
fs.unlinkSync(effectiveStructuredOutput.outputPath);
|
|
682
|
+
} catch {}
|
|
683
|
+
}
|
|
684
|
+
const { args, env, tempDir } = buildPiArgs({
|
|
685
|
+
parentSessionId: step.parentSessionId,
|
|
686
|
+
baseArgs: ["--mode", "json", "-p"],
|
|
687
|
+
task,
|
|
688
|
+
sessionEnabled,
|
|
689
|
+
sessionDir,
|
|
690
|
+
sessionFile: step.sessionFile,
|
|
691
|
+
model: candidate,
|
|
692
|
+
inheritProjectContext: step.inheritProjectContext,
|
|
693
|
+
inheritSkills: step.inheritSkills,
|
|
694
|
+
requireReadTool: Boolean(step.skills?.length),
|
|
695
|
+
tools: step.tools,
|
|
696
|
+
extensions: step.extensions,
|
|
697
|
+
subagentOnlyExtensions: step.subagentOnlyExtensions,
|
|
698
|
+
systemPrompt: appendTurnBudgetSystemPrompt(step.systemPrompt ?? "", ctx.turnBudget),
|
|
699
|
+
systemPromptMode: step.systemPromptMode,
|
|
700
|
+
mcpDirectTools: step.mcpDirectTools,
|
|
701
|
+
cwd: step.cwd ?? ctx.cwd,
|
|
702
|
+
promptFileStem: step.agent,
|
|
703
|
+
intercomSessionName: ctx.childIntercomTarget,
|
|
704
|
+
orchestratorIntercomTarget: ctx.orchestratorIntercomTarget,
|
|
705
|
+
runId: ctx.id,
|
|
706
|
+
childAgentName: step.agent,
|
|
707
|
+
childIndex: ctx.flatIndex,
|
|
708
|
+
parentEventSink: ctx.nestedRoute?.eventSink,
|
|
709
|
+
parentControlInbox: ctx.nestedRoute?.controlInbox,
|
|
710
|
+
parentRootRunId: ctx.nestedRoute?.rootRunId,
|
|
711
|
+
parentCapabilityToken: ctx.nestedRoute?.capabilityToken,
|
|
712
|
+
steerInboxDir: ctx.steerInboxDir,
|
|
713
|
+
structuredOutput: effectiveStructuredOutput,
|
|
714
|
+
toolBudget: step.toolBudget
|
|
715
|
+
});
|
|
716
|
+
const run = await runPiStreaming(args, step.cwd ?? ctx.cwd, ctx.outputFile, env, ctx.piPackageRoot, ctx.piArgv1, step.maxSubagentDepth, { eventsPath, runId: ctx.id, stepIndex: ctx.flatIndex, agent: step.agent }, ctx.registerInterrupt, ctx.onChildEvent, transcriptWriter, ctx.registerTimeout, ctx.timeoutMessage, ctx.registerTurnBudgetAbort);
|
|
717
|
+
if (run.turnBudget)
|
|
718
|
+
turnBudget = run.turnBudget;
|
|
719
|
+
else if (ctx.turnBudget) {
|
|
720
|
+
const assistantMessages = run.messages.filter((message) => message.role === "assistant");
|
|
721
|
+
const turnCount = assistantMessages.length;
|
|
722
|
+
const lastAssistantMessage = assistantMessages.at(-1);
|
|
723
|
+
if (turnCount > 0 && turnCount < ctx.turnBudget.maxTurns) {
|
|
724
|
+
turnBudget = { ...ctx.turnBudget, outcome: "within-budget", turnCount };
|
|
725
|
+
} else if (turnCount >= ctx.turnBudget.maxTurns) {
|
|
726
|
+
turnBudget = turnBudgetState(ctx.turnBudget, turnCount, shouldAbortForTurnBudget(ctx.turnBudget, turnCount, lastAssistantMessage ? isTerminalAssistantStop(lastAssistantMessage) : false));
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
cleanupTempDir(tempDir);
|
|
730
|
+
const hiddenError = run.exitCode === 0 && !run.error ? detectSubagentError(run.messages) : null;
|
|
731
|
+
const missingStructuredOutput = effectiveStructuredOutput ? !fs.existsSync(effectiveStructuredOutput.outputPath) : false;
|
|
732
|
+
const emptyOutputError = run.exitCode === 0 && !run.error && !hiddenError?.hasError && !run.finalOutput.trim() && (!effectiveStructuredOutput || missingStructuredOutput) ? "Subagent produced no output (possible model cold-start or empty response)." : undefined;
|
|
733
|
+
let structuredOutput;
|
|
734
|
+
let structuredError;
|
|
735
|
+
if (effectiveStructuredOutput && run.exitCode === 0 && !run.error && !hiddenError?.hasError && !emptyOutputError) {
|
|
736
|
+
const structured = readStructuredOutput({
|
|
737
|
+
schema: effectiveStructuredOutput.schema,
|
|
738
|
+
schemaPath: effectiveStructuredOutput.schemaPath,
|
|
739
|
+
outputPath: effectiveStructuredOutput.outputPath
|
|
740
|
+
});
|
|
741
|
+
if (structured.error)
|
|
742
|
+
structuredError = structured.error;
|
|
743
|
+
else
|
|
744
|
+
structuredOutput = structured.value;
|
|
745
|
+
}
|
|
746
|
+
const completionGuard = run.exitCode === 0 && !run.error && !hiddenError?.hasError && !emptyOutputError && step.completionGuard !== false ? evaluateCompletionMutationGuard({
|
|
747
|
+
agent: step.agent,
|
|
748
|
+
task: taskForCompletionGuard,
|
|
749
|
+
messages: run.messages,
|
|
750
|
+
tools: step.tools,
|
|
751
|
+
mcpDirectTools: step.mcpDirectTools
|
|
752
|
+
}) : undefined;
|
|
753
|
+
const completionGuardTriggered = completionGuard?.triggered === true && !run.observedMutationAttempt;
|
|
754
|
+
const completionGuardError = completionGuardTriggered ? `Subagent completed without making edits for an implementation task.
|
|
755
|
+
It appears to have returned planning or scratchpad output instead of applying changes.` : undefined;
|
|
756
|
+
const effectiveExitCode = completionGuardTriggered ? 1 : structuredError ? 1 : hiddenError?.hasError ? hiddenError.exitCode ?? 1 : emptyOutputError ? 1 : run.error && run.exitCode === 0 ? 1 : run.exitCode;
|
|
757
|
+
const error = completionGuardError ?? structuredError ?? (hiddenError?.hasError ? hiddenError.details ? `${hiddenError.errorType} failed (exit ${effectiveExitCode}): ${hiddenError.details}` : `${hiddenError.errorType} failed with exit code ${effectiveExitCode}` : emptyOutputError ?? (run.error || (run.exitCode !== 0 && run.stderr.trim() ? run.stderr.trim() : undefined)));
|
|
758
|
+
const attempt = {
|
|
759
|
+
model: candidate ?? run.model ?? step.model ?? "default",
|
|
760
|
+
success: effectiveExitCode === 0 && !error,
|
|
761
|
+
exitCode: effectiveExitCode,
|
|
762
|
+
error,
|
|
763
|
+
usage: run.usage
|
|
764
|
+
};
|
|
765
|
+
modelAttempts.push(attempt);
|
|
766
|
+
if (candidate)
|
|
767
|
+
attemptedModels.push(candidate);
|
|
768
|
+
completionGuardTriggeredFinal = completionGuardTriggered;
|
|
769
|
+
finalOutputSnapshot = outputSnapshot;
|
|
770
|
+
if (step.toolBudget) {
|
|
771
|
+
const toolMessages = run.messages.filter((message) => message.role === "toolResult");
|
|
772
|
+
const blockedMessage = toolMessages.find((message) => extractTextFromContent(message.content).includes("Tool budget hard limit reached"));
|
|
773
|
+
toolBudgetBlocked = Boolean(blockedMessage);
|
|
774
|
+
toolBudget = toolBudgetState(step.toolBudget, toolMessages.length, blockedMessage ? blockedMessage.toolName : undefined);
|
|
775
|
+
}
|
|
776
|
+
finalResult = { ...run, exitCode: effectiveExitCode, model: candidate ?? run.model, error, structuredOutput };
|
|
777
|
+
if (run.turnBudgetExceeded)
|
|
778
|
+
break;
|
|
779
|
+
if (run.timedOut || ctx.timeoutSignal?.aborted || ctx.skipAcceptance?.())
|
|
780
|
+
break;
|
|
781
|
+
if (attempt.success || completionGuardTriggered)
|
|
782
|
+
break;
|
|
783
|
+
if (!isRetryableModelFailure(error) || index === candidates.length - 1)
|
|
784
|
+
break;
|
|
785
|
+
attemptNotes.push(formatModelAttemptNote(attempt, candidates[index + 1]));
|
|
786
|
+
}
|
|
787
|
+
const rawOutput = finalResult?.finalOutput ?? "";
|
|
788
|
+
const outputForPersistence = stripAcceptanceReport(rawOutput);
|
|
789
|
+
const resolvedOutput = step.outputPath && finalResult?.exitCode === 0 ? resolveSingleOutput(step.outputPath, outputForPersistence, finalOutputSnapshot) : { fullOutput: outputForPersistence };
|
|
790
|
+
const output = resolvedOutput.fullOutput;
|
|
791
|
+
const outputReference = resolvedOutput.savedPath ? formatSavedOutputReference(resolvedOutput.savedPath, output) : undefined;
|
|
792
|
+
let outputForSummary = output;
|
|
793
|
+
if (attemptNotes.length > 0) {
|
|
794
|
+
outputForSummary = `${attemptNotes.join(`
|
|
65
795
|
`)}
|
|
66
796
|
|
|
67
|
-
${
|
|
797
|
+
${outputForSummary}`.trim();
|
|
798
|
+
}
|
|
799
|
+
if (!finalResult?.timedOut && finalResult?.turnBudgetExceeded && turnBudget) {
|
|
800
|
+
outputForSummary = formatTurnBudgetOutput(turnBudgetExceededMessage(turnBudget, turnBudget.turnCount), outputForSummary);
|
|
801
|
+
} else if (!finalResult?.timedOut && turnBudget?.outcome === "wrap-up-requested") {
|
|
802
|
+
const note = turnBudgetSoftNote(turnBudget, turnBudget.wrapUpRequestedAtTurn ?? turnBudget.turnCount);
|
|
803
|
+
outputForSummary = outputForSummary.trim() ? `${note}
|
|
68
804
|
|
|
69
|
-
${
|
|
70
|
-
|
|
805
|
+
${outputForSummary}` : note;
|
|
806
|
+
}
|
|
807
|
+
const outputForAcceptance = rawOutput;
|
|
808
|
+
const finalizedOutput = finalizeSingleOutput({
|
|
809
|
+
fullOutput: outputForSummary,
|
|
810
|
+
outputPath: step.outputPath,
|
|
811
|
+
outputMode: step.outputMode,
|
|
812
|
+
exitCode: finalResult?.exitCode ?? 1,
|
|
813
|
+
savedPath: resolvedOutput.savedPath,
|
|
814
|
+
outputReference,
|
|
815
|
+
saveError: resolvedOutput.saveError
|
|
816
|
+
});
|
|
817
|
+
outputForSummary = finalizedOutput.displayOutput;
|
|
818
|
+
const acceptance = step.effectiveAcceptance && !finalResult?.turnBudgetExceeded && !ctx.timeoutSignal?.aborted && !ctx.skipAcceptance?.() ? await evaluateAcceptance({
|
|
819
|
+
acceptance: step.effectiveAcceptance,
|
|
820
|
+
output: outputForAcceptance,
|
|
821
|
+
cwd: step.cwd ?? ctx.cwd,
|
|
822
|
+
signal: ctx.timeoutSignal,
|
|
823
|
+
abortMessage: ctx.timeoutMessage ?? "Subagent timed out."
|
|
824
|
+
}) : undefined;
|
|
825
|
+
const timedOutAfterAcceptance = finalResult?.timedOut === true || ctx.timeoutSignal?.aborted === true || ctx.skipAcceptance?.() === true;
|
|
826
|
+
const turnBudgetExceeded = finalResult?.turnBudgetExceeded === true;
|
|
827
|
+
const effectiveAcceptance = timedOutAfterAcceptance || turnBudgetExceeded ? undefined : acceptance;
|
|
828
|
+
const acceptanceFailure = effectiveAcceptance ? acceptanceFailureMessage(effectiveAcceptance) : undefined;
|
|
829
|
+
const acceptanceCanFailRun = acceptanceFailure && effectiveAcceptance?.explicit && (finalResult?.exitCode ?? 1) === 0 && !finalResult?.interrupted && !timedOutAfterAcceptance && !turnBudgetExceeded;
|
|
830
|
+
const effectiveFinalExitCode = timedOutAfterAcceptance || turnBudgetExceeded ? 1 : acceptanceCanFailRun ? 1 : finalResult?.exitCode ?? 1;
|
|
831
|
+
const effectiveFinalError = timedOutAfterAcceptance ? ctx.timeoutMessage ?? "Subagent timed out." : turnBudgetExceeded ? finalResult?.error ?? (turnBudget ? turnBudgetExceededMessage(turnBudget, turnBudget.turnCount) : "Subagent exceeded turn budget.") : acceptanceCanFailRun ? finalResult?.error ? `${finalResult.error}
|
|
832
|
+
${acceptanceFailure}` : acceptanceFailure : finalResult?.error;
|
|
833
|
+
if (artifactPaths && ctx.artifactConfig?.enabled !== false) {
|
|
834
|
+
if (ctx.artifactConfig?.includeOutput !== false) {
|
|
835
|
+
fs.writeFileSync(artifactPaths.outputPath, output, "utf-8");
|
|
836
|
+
}
|
|
837
|
+
if (ctx.artifactConfig?.includeMetadata !== false) {
|
|
838
|
+
fs.writeFileSync(artifactPaths.metadataPath, JSON.stringify({
|
|
839
|
+
runId: ctx.id,
|
|
840
|
+
agent: step.agent,
|
|
841
|
+
task,
|
|
842
|
+
exitCode: effectiveFinalExitCode,
|
|
843
|
+
model: finalResult?.model,
|
|
844
|
+
attemptedModels: attemptedModels.length > 0 ? attemptedModels : undefined,
|
|
845
|
+
modelAttempts,
|
|
846
|
+
...transcriptWriter ? { transcriptPath: artifactPaths.transcriptPath } : {},
|
|
847
|
+
transcriptError: transcriptWriter?.getError(),
|
|
848
|
+
skills: step.skills,
|
|
849
|
+
timestamp: Date.now()
|
|
850
|
+
}, null, 2), "utf-8");
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
return {
|
|
854
|
+
agent: step.agent,
|
|
855
|
+
output: outputForSummary,
|
|
856
|
+
exitCode: effectiveFinalExitCode,
|
|
857
|
+
error: effectiveFinalError,
|
|
858
|
+
sessionFile: step.sessionFile,
|
|
859
|
+
intercomTarget: ctx.childIntercomTarget,
|
|
860
|
+
model: finalResult?.model,
|
|
861
|
+
attemptedModels: attemptedModels.length > 0 ? attemptedModels : undefined,
|
|
862
|
+
modelAttempts,
|
|
863
|
+
totalCost: costSummaryFromAttempts(modelAttempts),
|
|
864
|
+
artifactPaths,
|
|
865
|
+
transcriptPath: transcriptWriter ? artifactPaths?.transcriptPath : undefined,
|
|
866
|
+
transcriptError: transcriptWriter?.getError(),
|
|
867
|
+
interrupted: timedOutAfterAcceptance || turnBudgetExceeded ? false : finalResult?.interrupted,
|
|
868
|
+
timedOut: timedOutAfterAcceptance ? true : finalResult?.timedOut,
|
|
869
|
+
turnBudget,
|
|
870
|
+
turnBudgetExceeded: turnBudgetExceeded || undefined,
|
|
871
|
+
wrapUpRequested: finalResult?.wrapUpRequested || turnBudget?.outcome === "wrap-up-requested" || turnBudgetExceeded || undefined,
|
|
872
|
+
toolBudget,
|
|
873
|
+
toolBudgetBlocked: toolBudgetBlocked || undefined,
|
|
874
|
+
completionGuardTriggered: completionGuardTriggeredFinal,
|
|
875
|
+
structuredOutput: timedOutAfterAcceptance || turnBudgetExceeded ? undefined : finalResult?.structuredOutput,
|
|
876
|
+
structuredOutputPath: timedOutAfterAcceptance || turnBudgetExceeded ? undefined : effectiveStructuredOutput?.outputPath,
|
|
877
|
+
structuredOutputSchemaPath: timedOutAfterAcceptance || turnBudgetExceeded ? undefined : effectiveStructuredOutput?.schemaPath,
|
|
878
|
+
acceptance: effectiveAcceptance
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
function markParallelGroupSetupFailure(input) {
|
|
882
|
+
for (let taskIndex = 0;taskIndex < input.group.parallel.length; taskIndex++) {
|
|
883
|
+
const flatTaskIndex = input.groupStartFlatIndex + taskIndex;
|
|
884
|
+
input.statusPayload.steps[flatTaskIndex].status = "failed";
|
|
885
|
+
input.statusPayload.steps[flatTaskIndex].startedAt = input.failedAt;
|
|
886
|
+
input.statusPayload.steps[flatTaskIndex].endedAt = input.failedAt;
|
|
887
|
+
input.statusPayload.steps[flatTaskIndex].durationMs = 0;
|
|
888
|
+
input.statusPayload.steps[flatTaskIndex].exitCode = 1;
|
|
889
|
+
input.results.push({ agent: input.group.parallel[taskIndex].agent, output: input.setupError, success: false, exitCode: 1, sessionFile: input.group.parallel[taskIndex].sessionFile });
|
|
890
|
+
}
|
|
891
|
+
input.statusPayload.currentStep = input.groupStartFlatIndex;
|
|
892
|
+
input.statusPayload.lastUpdate = input.failedAt;
|
|
893
|
+
input.statusPayload.outputFile = path.join(input.asyncDir, `output-${input.groupStartFlatIndex}.log`);
|
|
894
|
+
writeAtomicJson(input.statusPath, input.statusPayload);
|
|
895
|
+
appendJsonl(input.eventsPath, JSON.stringify({
|
|
896
|
+
type: "subagent.parallel.completed",
|
|
897
|
+
ts: input.failedAt,
|
|
898
|
+
runId: input.runId,
|
|
899
|
+
stepIndex: input.stepIndex,
|
|
900
|
+
success: false
|
|
901
|
+
}));
|
|
902
|
+
}
|
|
903
|
+
function markParallelGroupRunning(input) {
|
|
904
|
+
for (let taskIndex = 0;taskIndex < input.group.parallel.length; taskIndex++) {
|
|
905
|
+
const flatTaskIndex = input.groupStartFlatIndex + taskIndex;
|
|
906
|
+
input.statusPayload.steps[flatTaskIndex].status = "pending";
|
|
907
|
+
input.statusPayload.steps[flatTaskIndex].startedAt = undefined;
|
|
908
|
+
input.statusPayload.steps[flatTaskIndex].endedAt = undefined;
|
|
909
|
+
input.statusPayload.steps[flatTaskIndex].durationMs = undefined;
|
|
910
|
+
input.statusPayload.steps[flatTaskIndex].lastActivityAt = undefined;
|
|
911
|
+
input.statusPayload.steps[flatTaskIndex].activityState = undefined;
|
|
912
|
+
input.statusPayload.steps[flatTaskIndex].error = undefined;
|
|
913
|
+
}
|
|
914
|
+
input.statusPayload.currentStep = input.groupStartFlatIndex;
|
|
915
|
+
input.statusPayload.activityState = undefined;
|
|
916
|
+
input.statusPayload.lastActivityAt = input.groupStartTime;
|
|
917
|
+
input.statusPayload.lastUpdate = input.groupStartTime;
|
|
918
|
+
input.statusPayload.outputFile = path.join(input.asyncDir, `output-${input.groupStartFlatIndex}.log`);
|
|
919
|
+
writeAtomicJson(input.statusPath, input.statusPayload);
|
|
920
|
+
appendJsonl(input.eventsPath, JSON.stringify({
|
|
921
|
+
type: "subagent.parallel.started",
|
|
922
|
+
ts: input.groupStartTime,
|
|
923
|
+
runId: input.runId,
|
|
924
|
+
stepIndex: input.stepIndex,
|
|
925
|
+
agents: input.group.parallel.map((task) => task.agent),
|
|
926
|
+
count: input.group.parallel.length
|
|
927
|
+
}));
|
|
928
|
+
}
|
|
929
|
+
function prepareParallelTaskRun(task, cwd, worktreeSetup, taskIndex) {
|
|
930
|
+
if (!worktreeSetup)
|
|
931
|
+
return { taskForRun: task, taskCwd: cwd };
|
|
932
|
+
return {
|
|
933
|
+
taskForRun: { ...task, cwd: undefined },
|
|
934
|
+
taskCwd: worktreeSetup.worktrees[taskIndex].agentCwd
|
|
935
|
+
};
|
|
936
|
+
}
|
|
937
|
+
function appendParallelWorktreeSummary(previousOutput, worktreeSetup, asyncDir, stepIndex, group) {
|
|
938
|
+
if (!worktreeSetup)
|
|
939
|
+
return previousOutput;
|
|
940
|
+
const diffsDir = path.join(asyncDir, "worktree-diffs", `step-${stepIndex}`);
|
|
941
|
+
const diffs = diffWorktrees(worktreeSetup, group.parallel.map((task) => task.agent), diffsDir);
|
|
942
|
+
const diffSummary = formatWorktreeDiffSummary(diffs);
|
|
943
|
+
if (!diffSummary)
|
|
944
|
+
return previousOutput;
|
|
945
|
+
return `${previousOutput}
|
|
71
946
|
|
|
72
|
-
${U}`}function a5($,j){let J=r.join($,"progress.md");if(!j.parallel.some((Z)=>Z.task.includes(`Update progress at: ${J}`)))return;ej($)}function fj($){if(!$.artifactsDir||$.artifactConfig?.enabled===!1||$.artifactConfig?.includeTranscript===!1)return;return c1($.artifactsDir,$.runId,$.agent,$.flatStepCount>1?$.flatIndex:void 0).transcriptPath}async function r8($){let{id:j,steps:J,resultPath:Z,cwd:Q,placeholder:X,taskIndex:W,totalTasks:U,maxOutput:G,artifactsDir:B,artifactConfig:q}=$,O=new y1($.globalConcurrencyLimit??tj),T="",y={},h=[],S=Date.now(),v$=$.share===!0,k=$.asyncDir,f=r.join(k,"status.json"),n=r.join(k,"events.jsonl"),d$=r.join(k,`subagent-log-${j}.md`),W$=$.controlConfig??LJ,y$=new Map,K$=new Map,g$=new Map,A$=[],s=!1,E$,X$,_$,E=!1,t=!1,p=$.timeoutMs!==void 0?`Subagent timed out after ${$.timeoutMs}ms.`:void 0,q$=new AbortController,Y$={input:0,output:0,total:0},o$,u$=aj(J),j$=u$.length,c=[],H$=[],k$=0;for(let H=0;H<J.length;H++){let V=J[H];if($0(V)){c.push({start:k$,count:V.parallel.length,stepIndex:H});for(let K of V.parallel){let _=k$,z=fj({artifactsDir:B,artifactConfig:q,runId:j,agent:K.agent,flatIndex:_,flatStepCount:j$});H$.push({agent:K.agent,phase:K.phase,label:K.label,outputName:K.outputName,structured:K.structured,status:"pending",...K.toolBudget?{toolBudget:C0(K.toolBudget)}:{},...K.sessionFile?{sessionFile:K.sessionFile}:{},...z?{transcriptPath:z}:{},skills:K.skills,model:K.model,thinking:K.thinking,attemptedModels:K.modelCandidates&&K.modelCandidates.length>0?K.modelCandidates:K.model?[K.model]:void 0,recentTools:[],recentOutput:[]}),k$++}}else if(j0(V))c.push({start:k$,count:1,stepIndex:H}),H$.push({agent:`expand:${V.parallel.agent}`,phase:V.phase??V.parallel.phase,label:V.label??V.parallel.label??`Dynamic fanout (${V.collect.as})`,outputName:V.collect.as,structured:Boolean(V.collect.outputSchema),status:"pending",...V.parallel.toolBudget?{toolBudget:C0(V.parallel.toolBudget)}:{},recentTools:[],recentOutput:[]}),k$++;else{let K=k$,_=fj({artifactsDir:B,artifactConfig:q,runId:j,agent:V.agent,flatIndex:K,flatStepCount:j$});H$.push({agent:V.agent,phase:V.phase,label:V.label,outputName:V.outputName,structured:V.structured,status:"pending",...V.toolBudget?{toolBudget:C0(V.toolBudget)}:{},...V.sessionFile?{sessionFile:V.sessionFile}:{},..._?{transcriptPath:_}:{},skills:V.skills,model:V.model,thinking:V.thinking,attemptedModels:V.modelCandidates&&V.modelCandidates.length>0?V.modelCandidates:V.model?[V.model]:void 0,recentTools:[],recentOutput:[]}),k$++}}let W0=Boolean($.sessionDir)||v$||u$.some((H)=>Boolean(H.sessionFile)),Y={lifecycleArtifactVersion:S0,runId:j,...$.sessionId?{sessionId:$.sessionId}:{},mode:$.resultMode??(u$.length>1?"chain":"single"),state:"running",lastActivityAt:S,startedAt:S,lastUpdate:S,...$.timeoutMs!==void 0?{timeoutMs:$.timeoutMs}:{},...$.deadlineAt!==void 0?{deadlineAt:$.deadlineAt}:{},...$.turnBudget?{turnBudget:Rj($.turnBudget)}:{},...$.toolBudget?{toolBudget:C0($.toolBudget)}:{},pid:process.pid,cwd:Q,currentStep:0,chainStepCount:J.length,parallelGroups:c,workflowGraph:$.workflowGraph,steps:H$,artifactsDir:B,sessionDir:$.sessionDir,outputFile:r.join(k,"output-0.log")};e.mkdirSync(k,{recursive:!0}),D$(f,Y);let u=(H)=>{if(!$.nestedRoute||!$.nestedSelf)return;try{Y8($.nestedRoute,{type:H,ts:Date.now(),parentRunId:$.nestedSelf.parentRunId,parentStepIndex:$.nestedSelf.parentStepIndex,child:W8(Y,k,{id:j,parentRunId:$.nestedSelf.parentRunId,parentStepIndex:$.nestedSelf.parentStepIndex,depth:$.nestedSelf.depth,path:$.nestedSelf.path,mode:Y.mode,ts:Date.now()})})}catch(V){console.error("Failed to emit nested async status event:",V)}},U$=()=>{if(!$.workflowGraph)return;let H=structuredClone(Y.workflowGraph??$.workflowGraph),V=(_)=>{if(_==="complete"||_==="completed")return"completed";if(_==="running"||_==="failed"||_==="paused"||_==="pending")return _;return"pending"},K=(_)=>{if(_.flatIndex!==void 0){let z=Y.steps[_.flatIndex];if(z)_.status=V(z.status),_.error=z.error,_.acceptanceStatus=z.acceptance?.status;if(Y.currentStep===_.flatIndex)H.currentNodeId=_.id}for(let z of _.children??[])K(z);if(_.children?.length){if(_.children.every((z)=>z.status==="completed"))_.status="completed";else if(_.children.some((z)=>z.status==="running"))_.status="running";else if(_.children.some((z)=>z.status==="failed"))_.status="failed";else if(_.children.some((z)=>z.status==="paused"))_.status="paused"}if(_.error)_.status="failed"};for(let _ of H.nodes)K(_);Y.workflowGraph=H},a=()=>{U$(),D$(f,Y),u(Y.state==="running"||Y.state==="queued"?"subagent.nested.updated":"subagent.nested.completed")},N$=(H,V)=>{if(!V){y$.delete(H);return}if(y$.set(H,V),s)V()},f$=(H,V)=>{if(!V){K$.delete(H);return}if(K$.set(H,V),E)V()},S$=(H,V)=>{if(!V){g$.delete(H);return}g$.set(H,V)},t$=()=>{for(let H of[...y$.values()])H()},P$=()=>{for(let H of[...K$.values()])H()},V$=function*(H){for(let V of H??[])yield V,yield*V$(V.children),yield*V$(V.steps?.flatMap((K)=>K.children??[]))},l$=()=>{if(!$.nestedRoute)return;let H;try{H=Mj($.nestedRoute)}catch(V){$$(n,JSON.stringify({type:"subagent.nested.interrupt_failed",ts:Date.now(),runId:j,message:V instanceof Error?V.message:String(V)}));return}for(let V of V$(H.children)){if(V.state!=="running"&&V.state!=="queued")continue;let K=V.asyncDir??_j($.nestedRoute.rootRunId,V);if(!K)continue;try{KJ({asyncDir:K,pid:V.pid,source:"ancestor-interrupt"})}catch(_){$$(n,JSON.stringify({type:"subagent.nested.interrupt_failed",ts:Date.now(),runId:j,targetRunId:V.id,message:_ instanceof Error?_.message:String(_)}))}}},p$=()=>{if(!$.nestedRoute)return;let H;try{H=Mj($.nestedRoute)}catch(V){$$(n,JSON.stringify({type:"subagent.nested.timeout_failed",ts:Date.now(),runId:j,message:V instanceof Error?V.message:String(V)}));return}for(let V of V$(H.children)){if(V.state!=="running"&&V.state!=="queued")continue;let K=V.asyncDir??_j($.nestedRoute.rootRunId,V);if(!K)continue;try{m1({asyncDir:K,pid:V.pid,source:"ancestor-timeout"})}catch(_){$$(n,JSON.stringify({type:"subagent.nested.timeout_failed",ts:Date.now(),runId:j,targetRunId:V.id,message:_ instanceof Error?_.message:String(_)}))}}},e$=(H)=>({agent:H,output:"Paused after interrupt. Waiting for explicit next action.",exitCode:0,interrupted:!0}),P=(H)=>({agent:H,output:p??"Subagent timed out.",error:p??"Subagent timed out.",exitCode:1,timedOut:!0}),D=()=>{if(Y.mode!=="chain"||Y.state!=="running")return;let H=g8(k);if(H.length===0){let N=Dj(k);if((Y.pendingAppends??0)!==N)Y.pendingAppends=N,Y.lastUpdate=Date.now(),a();return}let V=H.flatMap((N)=>N.steps);J.push(...V);let K=Date.now(),_=Dj(k),z=c8({status:Y,steps:V,now:K,pendingAppends:_});if(p0.push(...Array.from({length:z.addedFlatSteps},()=>G1())),T0.push(...Array.from({length:z.addedFlatSteps},()=>{return})),$.childIntercomTargets)$.childIntercomTargets=Y.steps.map((N,w)=>Aj(j,N.agent,w));a();for(let N of H)$$(n,JSON.stringify({type:"subagent.chain.append.accepted",ts:K,runId:j,requestId:N.id,stepCount:N.steps.length,pendingAppends:_}))},i=(H,V,K,_)=>{let z=Y.workflowGraph?.nodes.find((N)=>N.id===`step-${H}`);if(!z)return;z.status=V,z.error=K,z.acceptanceStatus=_?.status??z.acceptanceStatus},M$=(H)=>{let V=Y.steps[H],K=V?.lastActivityAt??V?.startedAt??S,_=r.join(k,`output-${H}.log`);try{K=Math.max(K,e.statSync(_).mtimeMs)}catch(z){if(z.code!=="ENOENT")console.error(`Failed to inspect async output file '${_}':`,z)}return K},l0=new Set,vj=new Set,p0=H$.map(()=>G1()),T0=H$.map(()=>{return}),s8=300000,E0=(H)=>{if(!W$.enabled)return;let V=$.childIntercomTargets?.[H.index??Y.currentStep],K=H.type==="active_long_running"?W$.notifyChannels.filter((_)=>_!=="intercom"):W$.notifyChannels;if(K.length===0||!FJ(W$,H,l0,V))return;$$(n,JSON.stringify({type:"subagent.control",event:H,channels:K,childIntercomTarget:V,noticeText:p1(H,V),...$.controlIntercomTarget&&K.includes("intercom")?{intercom:{to:$.controlIntercomTarget,message:OJ(H,V)}}:{}}))},A1=()=>{let H=Y.steps.filter((V)=>V.status==="running"&&typeof V.currentTool==="string"&&V.currentTool.length>0).sort((V,K)=>(K.currentToolStartedAt??0)-(V.currentToolStartedAt??0))[0];Y.currentTool=H?.currentTool,Y.currentToolStartedAt=H?.currentToolStartedAt,Y.currentPath=H?.currentPath},gj=(H,V)=>{if(!W$.enabled||vj.has(H))return!1;let K=Y.steps[H];if(!K||K.status!=="running"||K.activityState==="needs_attention")return!1;let _=G8(W$,{startedAt:K.startedAt??S,now:V,turns:K.turnCount??0,tokens:K.tokens?.total??0});if(!_)return!1;vj.add(H);let z=K.activityState;K.activityState="active_long_running",Y.activityState=Y.activityState==="needs_attention"?"needs_attention":"active_long_running";let N=A0({type:"active_long_running",from:z,to:"active_long_running",runId:j,agent:K.agent,index:H,ts:V,message:`${K.agent} is still active but long-running`,reason:_,turns:K.turnCount,tokens:K.tokens?.total,toolCount:K.toolCount,currentTool:K.currentTool,currentToolDurationMs:K.currentToolStartedAt?Math.max(0,V-K.currentToolStartedAt):void 0,currentPath:K.currentPath,elapsedMs:V-(K.startedAt??S)});return E0(N),!0},N1=(H)=>{if(Y.state!=="running")return;let V=Y.steps.map((w,d)=>({step:w,index:d})).filter(({step:w})=>w.status==="running").map(({index:w})=>w),K=H.targetIndex!==void 0?[H.targetIndex]:V,_=Date.now(),z=[],N=[];for(let w of K){let d=Y.steps[w];if(!d){N.push({index:w,reason:"child index out of range"});continue}if(d.status!=="running"){N.push({index:w,reason:`child is ${d.status}`});continue}VJ(k,w,H),d.steerCount=(d.steerCount??0)+1,d.lastSteerAt=_,z.push(w)}if(z.length>0)Y.steerCount=(Y.steerCount??0)+z.length,Y.lastSteerAt=_,Y.lastUpdate=_,a();$$(n,JSON.stringify({type:"subagent.steer.requested",ts:_,runId:j,requestId:H.id,message:H.message,...H.source?{source:H.source}:{},...H.targetIndex!==void 0?{targetIndex:H.targetIndex}:{},acceptedIndexes:z,...N.length?{rejected:N}:{}}))},C1=(H)=>{let V=[];for(let K of A$.splice(0))if(K.targetIndex===void 0)N1({...K,targetIndex:H});else if(K.targetIndex===H)N1(K);else V.push(K);A$.push(...V)},T1=(H,V,K,_=Date.now())=>{let z=Y.steps[H];if(!z)return;z.model=V,z.thinking=K,Y.lastUpdate=_,a()},a8=(H,V,K,_)=>{let z=$.turnBudget,N=Y.steps[H];if(!z||!N||E||t||N.turnBudgetExceeded)return;if(V<z.maxTurns){let v={...z,outcome:"within-budget",turnCount:V};N.turnBudget=v,Y.turnBudget=v;return}let w=O1(z,V,!1);if(N.turnBudget=w,Y.turnBudget=w,!N.wrapUpRequested)N.wrapUpRequested=!0,Y.wrapUpRequested=!0,Ij(N,[bj(z,V)]);if(!wj(z,V,_))return;let d=O1(z,V,!0),Z$=u0(z,V);N.turnBudget=d,N.turnBudgetExceeded=!0,N.wrapUpRequested=!0,N.error=Z$,t=!0,Y.turnBudget=d,Y.turnBudgetExceeded=!0,Y.wrapUpRequested=!0,Y.error=Z$,Y.lastUpdate=K,$$(n,JSON.stringify({type:"subagent.step.turn_budget_exceeded",ts:K,runId:j,stepIndex:H,agent:N.agent,turnCount:V,maxTurns:z.maxTurns,graceTurns:z.graceTurns,message:Z$})),g$.get(H)?.(Z$,d)},E1=(H,V)=>{let K=Y.steps[H];if(!K)return;let _=Date.now();if(Y.currentStep=H,V.type==="tool_execution_start"&&V.toolName){let z=qj(V.toolName,V.args),N=U8(V.toolName,V.args);K.toolCount=(K.toolCount??0)+1;let w=u$[H]?.toolBudget;if(w)K.toolBudget=Z1(w,K.toolCount),Y.toolBudget=K.toolBudget;K.currentTool=V.toolName,K.currentToolArgs=w0(V.args??{}),K.currentToolStartedAt=_,K.currentPath=N,T0[H]={tool:V.toolName,path:N,mutates:z,startedAt:_},Y.toolCount=(Y.toolCount??0)+1,A1()}else if(V.type==="tool_execution_end"){if(K.currentTool)K.recentTools??=[],K.recentTools.push({tool:K.currentTool,args:K.currentToolArgs||"",endMs:_});K.currentTool=void 0,K.currentToolArgs=void 0,K.currentToolStartedAt=void 0,K.currentPath=void 0,A1()}else if(V.type==="tool_result_end"&&V.message){let z=T0[H];T0[H]=void 0;let N=s$(V.message.content);if(z&&N.includes("Tool budget hard limit reached")){let w=u$[H]?.toolBudget;if(w)K.toolBudget=Z1(w,K.toolCount??0,z.tool),K.toolBudgetBlocked=!0,Y.toolBudget=K.toolBudget,Y.toolBudgetBlocked=!0}if(Ij(K,N.split(`
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
`).slice(-10)),K.turnCount=(K.turnCount??0)+1;let z=V.message.usage;if(z){let N=z.input??z.inputTokens??0,w=z.output??z.outputTokens??0,d=K.tokens?.input??0,Z$=K.tokens?.output??0;K.tokens={input:d+N,output:Z$+w,total:d+Z$+N+w};let v=Y.totalTokens?.input??0,F=Y.totalTokens?.output??0;Y.totalTokens={input:v+N,output:F+w,total:v+F+N+w}}Y.turnCount=Math.max(Y.turnCount??0,K.turnCount),a8(H,K.turnCount,_,hj(V.message))}A1(),K.lastActivityAt=_,Y.lastActivityAt=_,Y.lastUpdate=_,gj(H,_),a()},t8=(H)=>{if(!W$.enabled)return!1;let V=!1,K=Y.lastActivityAt??S;for(let z=0;z<Y.steps.length;z++){let N=Y.steps[z];if(N.status!=="running")continue;let w=M$(z);if(K=Math.max(K,w),N.lastActivityAt!==w)N.lastActivityAt=w,V=!0;if(qJ({config:W$,startedAt:N.startedAt??S,lastActivityAt:w,now:H})==="needs_attention"){let Z$=N.activityState;if(N.activityState="needs_attention",Z$!=="needs_attention")E0(A0({from:Z$,to:"needs_attention",runId:j,agent:N.agent,index:z,ts:H,lastActivityAt:w})),V=!0}else if(gj(z,H))V=!0}if(Y.lastActivityAt!==K)Y.lastActivityAt=K,V=!0;let _=Y.steps.some((z)=>z.activityState==="needs_attention")?"needs_attention":Y.steps.some((z)=>z.activityState==="active_long_running")?"active_long_running":void 0;if(_!==E$)E$=_,Y.activityState=_,V=!0;if(Y.lastUpdate=H,V)a();return V};if(W$.enabled)X$=setInterval(()=>{if(Y.state!=="running")return;let H=Date.now();t8(H)},1000),X$.unref?.();let mj=()=>{if(g1(k),s||Y.state!=="running")return;s=!0;let H=Date.now();Y.state="paused",E$=void 0,Y.activityState=void 0,Y.lastUpdate=H;for(let V of Y.steps)if(V.status==="running")V.status="paused",V.activityState=void 0,V.endedAt=H,V.durationMs=V.startedAt?H-V.startedAt:void 0,V.lastActivityAt=H;a(),$$(n,JSON.stringify({type:"subagent.run.paused",ts:H,runId:j})),l$(),t$()},cj=()=>{if(E||s||Y.state!=="running")return;E=!0;let H=Date.now(),V=p??"Subagent timed out.";Y.state="failed",Y.timedOut=!0,Y.error=V,E$=void 0,Y.activityState=void 0,Y.lastUpdate=H;for(let K of Y.steps){if(K.status!=="running"&&K.status!=="pending")continue;K.status="failed",K.error=V,K.exitCode=1,K.timedOut=!0,K.activityState=void 0,K.endedAt=H,K.durationMs=K.startedAt?H-K.startedAt:0,K.lastActivityAt=H}a(),$$(n,JSON.stringify({type:"subagent.run.timed_out",ts:H,runId:j,timeoutMs:$.timeoutMs,deadlineAt:$.deadlineAt,message:V})),q$.abort(),p$(),P$()};process.on(y5,mj);let e8=UJ(k,{onInterrupt:mj,onTimeout:cj,onSteer:(H)=>{if((H.targetIndex!==void 0?Y.steps[H.targetIndex]:void 0)?.status==="pending")A$.push(H);else if(H.targetIndex!==void 0||Y.steps.some((K)=>K.status==="running"))N1(H);else A$.push(H)}});if($.deadlineAt!==void 0){let H=Math.max(0,$.deadlineAt-Date.now());_$=setTimeout(cj,H),_$.unref?.()}$$(n,JSON.stringify({type:"subagent.run.started",lifecycleArtifactVersion:S0,ts:S,runId:j,mode:Y.mode,cwd:Q,pid:process.pid}));let R=0,dj=0;while(!0){if(s||E||t)break;if(D(),dj>=J.length)break;let H=dj++,V=J[H];if(j0(V)){let K=R,_;try{if(_=tJ(V,y,H,{maxItems:$.dynamicFanoutMaxItems,allowRunnerFields:!0}),_.collectedOnEmpty)Yj(V.collect.outputSchema,_.collectedOnEmpty)}catch(M){let o=Date.now(),L=M instanceof x?M.message:M instanceof Error?M.message:String(M);Y.state="failed",Y.error=L,Y.currentStep=R;let A=Y.steps[K];if(A)A.status="failed",A.error=L,A.startedAt=o,A.endedAt=o,A.durationMs=0,A.exitCode=1;Y.lastUpdate=o,i(H,"failed",L),a(),h.push({agent:V.parallel.agent,output:L,error:L,success:!1,exitCode:1});break}if(_.parallel.length===0){let M=Date.now(),o=_.collectedOnEmpty??[];y[V.collect.as]={text:JSON.stringify(o),structured:o,agent:V.parallel.agent,stepIndex:H},Y.outputs=y;let L=Y.steps[K];if(L)L.status="complete",L.startedAt=M,L.endedAt=M,L.durationMs=0;T="Dynamic fanout produced 0 results.";let A=V.effectiveAcceptance?.explicit&&!E?await M1({acceptance:V.effectiveAcceptance,output:"",report:Ej({results:[],notes:"Dynamic fanout produced 0 results."}),cwd:Q,signal:q$.signal,abortMessage:p??"Subagent timed out."}):void 0,I=E||q$.signal.aborted,G$=I?void 0:A;if(L&&G$)L.acceptance=G$;let x$=G$?L1(G$):void 0;if(I||x$){let g=I?p??"Subagent timed out.":x$;if(Y.state="failed",Y.error=g,L)L.status="failed",L.error=g,L.exitCode=1,L.timedOut=I?!0:void 0;i(H,"failed",g,G$),Y.lastUpdate=Date.now(),a(),h.push({agent:V.parallel.agent,output:g,error:g,success:!1,exitCode:1,timedOut:I?!0:void 0,acceptance:G$});break}R++,Y.lastUpdate=M,i(H,"completed",void 0,G$),a();continue}let z=_.parallel.map((M,o)=>{let L=V.thinkingOverrides?.[o],A=L?W1(V.parallel.model,L,!0):V.parallel.model,I=L?O0(A,L):void 0;return{...V.parallel,task:M.task??V.parallel.task,label:M.label??V.parallel.label,...V.sessionFiles?.[o]?{sessionFile:V.sessionFiles[o]}:{},...L?{...A?{model:A}:{},...I?{thinking:I}:{},...V.parallel.modelCandidates?{modelCandidates:V.parallel.modelCandidates.map((G$)=>W1(G$,L,!0))}:{}}:{},structuredOutput:void 0,structuredOutputSchema:V.parallel.structuredOutputSchema??V.parallel.structuredOutput?.schema}}),N=Math.max(Y.steps.length-1+z.length,1),w=z.map((M,o)=>{let L=fj({artifactsDir:B,artifactConfig:q,runId:j,agent:M.agent,flatIndex:K+o,flatStepCount:N});return{agent:M.agent,phase:M.phase??V.phase,label:M.label,outputName:void 0,structured:Boolean(M.structuredOutputSchema),status:"pending",...M.sessionFile?{sessionFile:M.sessionFile}:{},...L?{transcriptPath:L}:{},skills:M.skills,model:M.model,thinking:M.thinking,attemptedModels:M.modelCandidates&&M.modelCandidates.length>0?M.modelCandidates:M.model?[M.model]:void 0,recentTools:[],recentOutput:[]}});if(Y.steps.splice(K,1,...w),$.childIntercomTargets)$.childIntercomTargets=Y.steps.map((M,o)=>Aj(j,M.agent,o));p0.splice(K,1,...w.map(()=>G1())),T0.splice(K,1,...w.map(()=>{return}));let d=w.length-1;for(let M of Y.parallelGroups)if(M.stepIndex===H)M.start=K,M.count=w.length;else if(M.start>K)M.start+=d;if(Y.workflowGraph){let M=(L)=>{for(let A of L){if(A.stepIndex!==void 0&&A.stepIndex>H&&A.flatIndex!==void 0&&A.flatIndex>=K)A.flatIndex+=w.length;if(A.children)M(A.children)}};M(Y.workflowGraph.nodes);let o=Y.workflowGraph.nodes.find((L)=>L.id===`step-${H}`);if(o)o.children=_.items.map((L,A)=>({id:`step-${H}-item-${L.idKey}`,kind:"agent",agent:V.parallel.agent,phase:z[A]?.phase??V.phase,label:z[A]?.label?.trim()||`${V.parallel.agent} ${L.key}`,status:"pending",flatIndex:K+A,stepIndex:H,itemKey:L.key,structured:Boolean(z[A]?.structuredOutputSchema)}))}a();let Z$=V.concurrency??k1,v=V.failFast??!1,F=!1,F$=await t0(z,Z$,async(M,o)=>{let L=K+o;if(E)return P(M.agent);if(s)return e$(M.agent);if(F&&v){let g=Date.now();return Y.steps[L].status="failed",Y.steps[L].error="Skipped due to fail-fast",Y.steps[L].startedAt=g,Y.steps[L].endedAt=g,Y.steps[L].durationMs=0,Y.steps[L].exitCode=-1,Y.lastUpdate=g,a(),{agent:M.agent,output:"(skipped — fail-fast)",exitCode:-1,skipped:!0}}let A=Date.now();Y.currentStep=L,Y.steps[L].status="running",Y.steps[L].error=void 0,Y.steps[L].activityState=void 0,yj(Y.steps[L]),Y.steps[L].startedAt=A,Y.steps[L].lastActivityAt=A,Y.outputFile=r.join(k,`output-${L}.log`),Y.lastActivityAt=A,Y.lastUpdate=A,a(),$$(n,JSON.stringify({type:"subagent.step.started",ts:A,runId:j,stepIndex:L,agent:M.agent})),C1(L);let I=await kj(M,{previousOutput:T,placeholder:X,cwd:Q,sessionEnabled:W0,outputs:y,sessionDir:$.sessionDir?r.join($.sessionDir,`dynamic-${H}-${o}`):void 0,artifactsDir:B,artifactConfig:q,id:j,flatIndex:L,flatStepCount:Math.max(Y.steps.length,1),outputFile:r.join(k,`output-${L}.log`),steerInboxDir:y0(k,L),piPackageRoot:$.piPackageRoot,piArgv1:$.piArgv1,childIntercomTarget:$.childIntercomTargets?.[L],orchestratorIntercomTarget:$.controlIntercomTarget,nestedRoute:$.nestedRoute,registerInterrupt:(g)=>N$(L,g),registerTimeout:(g)=>f$(L,g),registerTurnBudgetAbort:(g)=>S$(L,g),timeoutSignal:q$.signal,timeoutMessage:p,turnBudget:$.turnBudget,onAttemptStart:(g)=>T1(L,g.model,g.thinking),onChildEvent:(g)=>E1(L,g),skipAcceptance:()=>E}),G$=Date.now(),x$=I.interrupted===!0;if(Y.steps[L].status=E?"failed":x$?"paused":I.exitCode===0?"complete":"failed",Y.steps[L].endedAt=G$,Y.steps[L].durationMs=G$-A,Y.steps[L].exitCode=E?1:x$?0:I.exitCode,Y.steps[L].timedOut=E||I.timedOut?!0:void 0,Y.steps[L].turnBudget=I.turnBudget,Y.steps[L].turnBudgetExceeded=I.turnBudgetExceeded,Y.steps[L].wrapUpRequested=I.wrapUpRequested,Y.steps[L].toolBudget=I.toolBudget,Y.steps[L].toolBudgetBlocked=I.toolBudgetBlocked,I.toolBudget)Y.toolBudget=I.toolBudget;if(I.toolBudgetBlocked)Y.toolBudgetBlocked=!0;if(I.turnBudget)Y.turnBudget=I.turnBudget;if(I.turnBudgetExceeded)Y.turnBudgetExceeded=!0;if(I.wrapUpRequested)Y.wrapUpRequested=!0;if(Y.steps[L].model=I.model,Y.steps[L].thinking=O0(I.model,Y.steps[L].thinking),Y.steps[L].attemptedModels=I.attemptedModels,Y.steps[L].modelAttempts=I.modelAttempts,Y.steps[L].totalCost=I.totalCost,Y.steps[L].error=E?p??"Subagent timed out.":I.error,Y.steps[L].transcriptPath=I.transcriptPath??Y.steps[L].transcriptPath,Y.steps[L].transcriptError=I.transcriptError,Y.steps[L].structuredOutput=I.structuredOutput,Y.steps[L].structuredOutputPath=I.structuredOutputPath,Y.steps[L].structuredOutputSchemaPath=I.structuredOutputSchemaPath,Y.steps[L].acceptance=I.acceptance,Y.lastUpdate=G$,a(),$$(n,JSON.stringify({type:E?"subagent.step.failed":x$?"subagent.step.paused":I.exitCode===0?"subagent.step.completed":"subagent.step.failed",ts:G$,runId:j,stepIndex:L,agent:M.agent,exitCode:E?1:x$?0:I.exitCode,durationMs:G$-A})),I.exitCode!==0&&v)F=!0;return E?{...I,output:p??"Subagent timed out.",error:p??"Subagent timed out.",exitCode:1,interrupted:!1,timedOut:!0,skipped:!1}:{...I,skipped:!1}},O);R+=z.length;for(let M of F$)h.push({agent:M.agent,output:M.output,error:M.error,success:M.interrupted!==!0&&M.exitCode===0,exitCode:M.interrupted===!0?0:M.exitCode,skipped:M.skipped,interrupted:M.interrupted,timedOut:M.timedOut,turnBudget:M.turnBudget,turnBudgetExceeded:M.turnBudgetExceeded,wrapUpRequested:M.wrapUpRequested,toolBudget:M.toolBudget,toolBudgetBlocked:M.toolBudgetBlocked,sessionFile:M.sessionFile,intercomTarget:M.intercomTarget,model:M.model,attemptedModels:M.attemptedModels,modelAttempts:M.modelAttempts,totalCost:M.totalCost,artifactPaths:M.artifactPaths,transcriptPath:M.transcriptPath,transcriptError:M.transcriptError,structuredOutput:M.structuredOutput,structuredOutputPath:M.structuredOutputPath,structuredOutputSchemaPath:M.structuredOutputSchemaPath,acceptance:M.acceptance});let C=eJ(V,_.items,F$),z$=F$.filter((M)=>M.exitCode!==0&&M.exitCode!==-1);if(z$.length===0)try{Yj(V.collect.outputSchema,C),y[V.collect.as]={text:JSON.stringify(C),structured:C,agent:V.parallel.agent,stepIndex:H},Y.outputs=y;let M=V.effectiveAcceptance&&!E?await M1({acceptance:V.effectiveAcceptance,output:"",report:Ej({results:F$,notes:`Dynamic fanout collected ${C.length} result(s) into ${V.collect.as}.`}),cwd:Q,signal:q$.signal,abortMessage:p??"Subagent timed out."}):void 0,o=E||q$.signal.aborted,L=o?void 0:M,A=L?L1(L):void 0,I=o?p??"Subagent timed out.":A;if(i(H,I?"failed":"completed",I,L),I)h.push({agent:V.parallel.agent,output:I,error:I,success:!1,exitCode:1,timedOut:o?!0:void 0,structuredOutput:C,acceptance:L}),Y.error=I}catch(M){let o=M instanceof x?M.message:M instanceof Error?M.message:String(M);h.push({agent:V.parallel.agent,output:o,error:o,success:!1,exitCode:1,structuredOutput:C}),Y.error=o,i(H,"failed",o)}if(T=e0(F$.map((M,o)=>({agent:M.agent,taskIndex:o,output:M.output,exitCode:M.exitCode,error:M.error})),(M,o)=>`=== Dynamic Item ${M+1} (${o}, key ${_.items[M]?.key??M}) ===`),$$(n,JSON.stringify({type:"subagent.dynamic.completed",ts:Date.now(),runId:j,stepIndex:H,success:z$.length===0})),z$.length>0)i(H,"failed",z$[0]?.error??"Dynamic fanout child failed.");if(Y.lastUpdate=Date.now(),a(),z$.length>0||Y.error)break;continue}if($0(V)){let K=V,_=K.concurrency??k1,z=K.failFast??!1,N=R,w=!1,d;if(K.worktree){let Z$=N8(K.parallel,Q);if(Z$){let v=Date.now();i8({statusPayload:Y,results:h,group:K,groupStartFlatIndex:N,setupError:C8(Z$,Q),failedAt:v,statusPath:f,eventsPath:n,asyncDir:k,runId:j,stepIndex:H}),R+=K.parallel.length;break}try{d=E8(Q,`${j}-s${H}`,K.parallel.length,{agents:K.parallel.map((v)=>v.agent),setupHook:$.worktreeSetupHook?{hookPath:$.worktreeSetupHook,timeoutMs:$.worktreeSetupHookTimeoutMs}:void 0,baseDir:$.worktreeBaseDir})}catch(v){let F=v instanceof Error?v.message:String(v),F$=Date.now();i8({statusPayload:Y,results:h,group:K,groupStartFlatIndex:N,setupError:F,failedAt:F$,statusPath:f,eventsPath:n,asyncDir:k,runId:j,stepIndex:H}),R+=K.parallel.length;break}}try{if(K.worktree)a5(Q,K);let Z$=Date.now();i5({statusPayload:Y,group:K,groupStartFlatIndex:N,groupStartTime:Z$,statusPath:f,eventsPath:n,asyncDir:k,runId:j,stepIndex:H});let v=await t0(K.parallel,_,async(F,F$)=>{let C=N+F$;if(E)return P(F.agent);if(s)return e$(F.agent);if(w&&z){let g=Date.now();return Y.steps[C].status="failed",Y.steps[C].error="Skipped due to fail-fast",Y.steps[C].startedAt=g,Y.steps[C].endedAt=g,Y.steps[C].durationMs=0,Y.steps[C].exitCode=-1,Y.steps[C].activityState=void 0,Y.lastUpdate=g,a(),$$(n,JSON.stringify({type:"subagent.step.failed",ts:g,runId:j,stepIndex:C,agent:F.agent,exitCode:-1,durationMs:0})),{agent:F.agent,output:"(skipped — fail-fast)",exitCode:-1,skipped:!0}}let z$=Date.now();Y.currentStep=C,Y.steps[C].status="running",Y.steps[C].error=void 0,Y.steps[C].activityState=void 0,yj(Y.steps[C]),Y.steps[C].startedAt=z$,Y.steps[C].endedAt=void 0,Y.steps[C].durationMs=void 0,Y.steps[C].lastActivityAt=z$,Y.outputFile=r.join(k,`output-${C}.log`),Y.lastActivityAt=z$,Y.lastUpdate=z$,a(),$$(n,JSON.stringify({type:"subagent.step.started",ts:z$,runId:j,stepIndex:C,agent:F.agent}));let M=$.sessionDir?r.join($.sessionDir,`parallel-${F$}`):void 0,{taskForRun:o,taskCwd:L}=r5(F,Q,d,F$);C1(C);let A=await kj(o,{previousOutput:T,placeholder:X,cwd:L,sessionEnabled:W0,outputs:y,sessionDir:M,artifactsDir:B,artifactConfig:q,id:j,flatIndex:C,flatStepCount:Math.max(Y.steps.length,1),outputFile:r.join(k,`output-${C}.log`),steerInboxDir:y0(k,C),piPackageRoot:$.piPackageRoot,piArgv1:$.piArgv1,childIntercomTarget:$.childIntercomTargets?.[C],orchestratorIntercomTarget:$.controlIntercomTarget,nestedRoute:$.nestedRoute,registerInterrupt:(g)=>N$(C,g),registerTimeout:(g)=>f$(C,g),registerTurnBudgetAbort:(g)=>S$(C,g),timeoutSignal:q$.signal,timeoutMessage:p,turnBudget:$.turnBudget,onAttemptStart:(g)=>T1(C,g.model,g.thinking),onChildEvent:(g)=>E1(C,g),skipAcceptance:()=>E});if(F.sessionFile)o$=F.sessionFile;let I=Date.now(),G$=I-z$,x$=A.interrupted===!0;if(Y.steps[C].status=E?"failed":x$?"paused":A.exitCode===0?"complete":"failed",Y.steps[C].endedAt=I,Y.steps[C].durationMs=G$,Y.steps[C].exitCode=E?1:x$?0:A.exitCode,Y.steps[C].timedOut=E||A.timedOut?!0:void 0,Y.steps[C].turnBudget=A.turnBudget,Y.steps[C].turnBudgetExceeded=A.turnBudgetExceeded,Y.steps[C].wrapUpRequested=A.wrapUpRequested,Y.steps[C].toolBudget=A.toolBudget,Y.steps[C].toolBudgetBlocked=A.toolBudgetBlocked,A.toolBudget)Y.toolBudget=A.toolBudget;if(A.toolBudgetBlocked)Y.toolBudgetBlocked=!0;if(A.turnBudget)Y.turnBudget=A.turnBudget;if(A.turnBudgetExceeded)Y.turnBudgetExceeded=!0;if(A.wrapUpRequested)Y.wrapUpRequested=!0;if(Y.steps[C].model=A.model,Y.steps[C].thinking=O0(A.model,Y.steps[C].thinking),Y.steps[C].attemptedModels=A.attemptedModels,Y.steps[C].modelAttempts=A.modelAttempts,Y.steps[C].totalCost=A.totalCost,Y.steps[C].error=E?p??"Subagent timed out.":A.error,Y.steps[C].transcriptPath=A.transcriptPath??Y.steps[C].transcriptPath,Y.steps[C].transcriptError=A.transcriptError,Y.steps[C].structuredOutput=A.structuredOutput,Y.steps[C].structuredOutputPath=A.structuredOutputPath,Y.steps[C].structuredOutputSchemaPath=A.structuredOutputSchemaPath,Y.steps[C].acceptance=A.acceptance,Y.lastUpdate=I,a(),$$(n,JSON.stringify({type:E?"subagent.step.failed":x$?"subagent.step.paused":A.exitCode===0?"subagent.step.completed":"subagent.step.failed",ts:I,runId:j,stepIndex:C,agent:F.agent,exitCode:E?1:x$?0:A.exitCode,durationMs:G$})),A.completionGuardTriggered){let g=A0({from:Y.steps[C].activityState,to:"needs_attention",runId:j,agent:F.agent,index:C,ts:I,message:`${F.agent} completed without making edits for an implementation task`,reason:"completion_guard"});E0(g)}if(A.exitCode!==0&&z)w=!0;return E?{...A,output:p??"Subagent timed out.",error:p??"Subagent timed out.",exitCode:1,interrupted:!1,timedOut:!0,skipped:!1}:{...A,skipped:!1}},O);R+=K.parallel.length;for(let F=0;F<K.parallel.length;F++){let F$=N+F,z$=($.sessionDir?Fj(r.join($.sessionDir,`parallel-${F}`)):null)??p8(v[F]?.modelAttempts);if(!z$)continue;Y.steps[F$].tokens=z$,Y$={input:Y$.input+z$.input,output:Y$.output+z$.output,total:Y$.total+z$.total}}Y.totalTokens={...Y$},Y.lastUpdate=Date.now(),a();for(let F of v)h.push({agent:F.agent,output:F.output,error:F.error,success:F.interrupted!==!0&&F.exitCode===0,exitCode:F.interrupted===!0?0:F.exitCode,skipped:F.skipped,interrupted:F.interrupted,timedOut:F.timedOut,turnBudget:F.turnBudget,turnBudgetExceeded:F.turnBudgetExceeded,wrapUpRequested:F.wrapUpRequested,toolBudget:F.toolBudget,toolBudgetBlocked:F.toolBudgetBlocked,sessionFile:F.sessionFile,intercomTarget:F.intercomTarget,model:F.model,attemptedModels:F.attemptedModels,modelAttempts:F.modelAttempts,totalCost:F.totalCost,artifactPaths:F.artifactPaths,transcriptPath:F.transcriptPath,transcriptError:F.transcriptError,structuredOutput:F.structuredOutput,structuredOutputPath:F.structuredOutputPath,structuredOutputSchemaPath:F.structuredOutputSchemaPath,acceptance:F.acceptance});for(let F=0;F<K.parallel.length;F++){let F$=K.parallel[F]?.outputName;if(F$)y[F$]=Hj({agent:v[F].agent,output:v[F].output,structuredOutput:v[F].structuredOutput},H)}if(Y.outputs=y,T=e0(v.map((F)=>({agent:F.agent,output:F.output,exitCode:F.exitCode,error:F.error,model:F.model,attemptedModels:F.attemptedModels}))),T=s5(T,d,k,H,K),$$(n,JSON.stringify({type:"subagent.parallel.completed",ts:Date.now(),runId:j,stepIndex:H,success:v.every((F)=>F.exitCode===0||F.exitCode===-1)})),v.some((F)=>F.exitCode!==0&&F.exitCode!==-1))break}finally{if(d)Oj(d)}}else{let K=V,_=Date.now();Y.currentStep=R,Y.steps[R].status="running",Y.steps[R].activityState=void 0,Y.activityState=void 0,yj(Y.steps[R]),Y.steps[R].skills=K.skills,Y.steps[R].startedAt=_,Y.steps[R].lastActivityAt=_,Y.lastActivityAt=_,Y.lastUpdate=_,Y.outputFile=r.join(k,`output-${R}.log`),a(),$$(n,JSON.stringify({type:"subagent.step.started",ts:_,runId:j,stepIndex:R,agent:K.agent})),C1(R);let z=await kj(K,{previousOutput:T,placeholder:X,cwd:Q,sessionEnabled:W0,outputs:y,sessionDir:$.sessionDir,artifactsDir:B,artifactConfig:q,id:j,flatIndex:R,flatStepCount:Math.max(Y.steps.length,1),outputFile:r.join(k,`output-${R}.log`),steerInboxDir:y0(k,R),piPackageRoot:$.piPackageRoot,piArgv1:$.piArgv1,childIntercomTarget:$.childIntercomTargets?.[R],orchestratorIntercomTarget:$.controlIntercomTarget,nestedRoute:$.nestedRoute,registerInterrupt:(v)=>N$(R,v),registerTimeout:(v)=>f$(R,v),registerTurnBudgetAbort:(v)=>S$(R,v),timeoutSignal:q$.signal,timeoutMessage:p,turnBudget:$.turnBudget,onAttemptStart:(v)=>T1(R,v.model,v.thinking),onChildEvent:(v)=>E1(R,v),skipAcceptance:()=>E});if(K.sessionFile)o$=K.sessionFile;if(T=z.output,h.push({agent:z.agent,output:E?p??"Subagent timed out.":z.output,error:E?p??"Subagent timed out.":z.error,success:!E&&z.interrupted!==!0&&z.exitCode===0,exitCode:E?1:z.interrupted===!0?0:z.exitCode,sessionFile:z.sessionFile,intercomTarget:z.intercomTarget,model:z.model,attemptedModels:z.attemptedModels,modelAttempts:z.modelAttempts,totalCost:z.totalCost,artifactPaths:z.artifactPaths,transcriptPath:z.transcriptPath,transcriptError:z.transcriptError,structuredOutput:z.structuredOutput,structuredOutputPath:z.structuredOutputPath,structuredOutputSchemaPath:z.structuredOutputSchemaPath,acceptance:z.acceptance,interrupted:z.interrupted,timedOut:E||z.timedOut?!0:void 0,turnBudget:z.turnBudget,turnBudgetExceeded:z.turnBudgetExceeded,wrapUpRequested:z.wrapUpRequested,toolBudget:z.toolBudget,toolBudgetBlocked:z.toolBudgetBlocked}),K.outputName)y[K.outputName]=Hj({agent:z.agent,output:z.output,structuredOutput:z.structuredOutput},H);Y.outputs=y;let N=$.sessionDir?Fj($.sessionDir):null,w=N?{input:N.input-Y$.input,output:N.output-Y$.output,total:N.total-Y$.total}:null;if(N)Y$=N;else if(w=p8(z.modelAttempts),w)Y$={input:Y$.input+w.input,output:Y$.output+w.output,total:Y$.total+w.total};let d=Date.now(),Z$=z.interrupted===!0;if(Y.steps[R].status=E?"failed":Z$?"paused":z.exitCode===0?"complete":"failed",Y.steps[R].endedAt=d,Y.steps[R].durationMs=d-_,Y.steps[R].exitCode=E?1:Z$?0:z.exitCode,Y.steps[R].timedOut=E||z.timedOut?!0:void 0,Y.steps[R].turnBudget=z.turnBudget,Y.steps[R].turnBudgetExceeded=z.turnBudgetExceeded,Y.steps[R].wrapUpRequested=z.wrapUpRequested,Y.steps[R].toolBudget=z.toolBudget,Y.steps[R].toolBudgetBlocked=z.toolBudgetBlocked,z.toolBudget)Y.toolBudget=z.toolBudget;if(z.toolBudgetBlocked)Y.toolBudgetBlocked=!0;if(z.turnBudget)Y.turnBudget=z.turnBudget;if(z.turnBudgetExceeded)Y.turnBudgetExceeded=!0;if(z.wrapUpRequested)Y.wrapUpRequested=!0;if(Y.steps[R].model=z.model,Y.steps[R].thinking=O0(z.model,Y.steps[R].thinking),Y.steps[R].attemptedModels=z.attemptedModels,Y.steps[R].modelAttempts=z.modelAttempts,Y.steps[R].totalCost=z.totalCost,Y.steps[R].error=E?p??"Subagent timed out.":z.error,Y.steps[R].transcriptPath=z.transcriptPath??Y.steps[R].transcriptPath,Y.steps[R].transcriptError=z.transcriptError,Y.steps[R].structuredOutput=z.structuredOutput,Y.steps[R].structuredOutputPath=z.structuredOutputPath,Y.steps[R].structuredOutputSchemaPath=z.structuredOutputSchemaPath,Y.steps[R].acceptance=z.acceptance,w)Y.steps[R].tokens=w,Y.totalTokens={...Y$};if(Y.lastUpdate=d,a(),$$(n,JSON.stringify({type:E?"subagent.step.failed":Z$?"subagent.step.paused":z.exitCode===0?"subagent.step.completed":"subagent.step.failed",ts:d,runId:j,stepIndex:R,agent:K.agent,exitCode:E?1:Z$?0:z.exitCode,durationMs:d-_,tokens:w})),z.completionGuardTriggered){let v=A0({from:Y.steps[R].activityState,to:"needs_attention",runId:j,agent:K.agent,index:R,ts:d,message:`${K.agent} completed without making edits for an implementation task`,reason:"completion_guard"});E0(v)}if(R++,z.exitCode!==0)break}}let n0=h.map((H)=>{let V=H.output.trim(),K=V?H.error?`${V}
|
|
947
|
+
${diffSummary}`;
|
|
948
|
+
}
|
|
949
|
+
function ensureParallelProgressFile(cwd, group) {
|
|
950
|
+
const progressPath = path.join(cwd, "progress.md");
|
|
951
|
+
if (!group.parallel.some((task) => task.task.includes(`Update progress at: ${progressPath}`)))
|
|
952
|
+
return;
|
|
953
|
+
writeInitialProgressFile(cwd);
|
|
954
|
+
}
|
|
955
|
+
function resolveAsyncStepTranscriptPath(input) {
|
|
956
|
+
if (!input.artifactsDir || input.artifactConfig?.enabled === false || input.artifactConfig?.includeTranscript === false)
|
|
957
|
+
return;
|
|
958
|
+
return getArtifactPaths(input.artifactsDir, input.runId, input.agent, input.flatStepCount > 1 ? input.flatIndex : undefined).transcriptPath;
|
|
959
|
+
}
|
|
960
|
+
async function runSubagent(config) {
|
|
961
|
+
const { id, steps, resultPath, cwd, placeholder, taskIndex, totalTasks, maxOutput, artifactsDir, artifactConfig } = config;
|
|
962
|
+
const globalSemaphore = new Semaphore(config.globalConcurrencyLimit ?? DEFAULT_GLOBAL_CONCURRENCY_LIMIT);
|
|
963
|
+
let previousOutput = "";
|
|
964
|
+
const outputs = {};
|
|
965
|
+
const results = [];
|
|
966
|
+
const overallStartTime = Date.now();
|
|
967
|
+
const shareEnabled = config.share === true;
|
|
968
|
+
const asyncDir = config.asyncDir;
|
|
969
|
+
const statusPath = path.join(asyncDir, "status.json");
|
|
970
|
+
const eventsPath = path.join(asyncDir, "events.jsonl");
|
|
971
|
+
const logPath = path.join(asyncDir, `subagent-log-${id}.md`);
|
|
972
|
+
const controlConfig = config.controlConfig ?? DEFAULT_CONTROL_CONFIG;
|
|
973
|
+
const activeChildInterrupts = new Map;
|
|
974
|
+
const activeChildTimeouts = new Map;
|
|
975
|
+
const activeChildTurnBudgetAborts = new Map;
|
|
976
|
+
const pendingStepSteers = [];
|
|
977
|
+
let interrupted = false;
|
|
978
|
+
let currentActivityState;
|
|
979
|
+
let activityTimer;
|
|
980
|
+
let timeoutTimer;
|
|
981
|
+
let timedOut = false;
|
|
982
|
+
let turnBudgetExceeded = false;
|
|
983
|
+
const timeoutMessage = config.timeoutMs !== undefined ? `Subagent timed out after ${config.timeoutMs}ms.` : undefined;
|
|
984
|
+
const timeoutAbortController = new AbortController;
|
|
985
|
+
let previousCumulativeTokens = { input: 0, output: 0, total: 0 };
|
|
986
|
+
let latestSessionFile;
|
|
987
|
+
const flatSteps = flattenSteps(steps);
|
|
988
|
+
const initialFlatStepCount = flatSteps.length;
|
|
989
|
+
const parallelGroups = [];
|
|
990
|
+
const initialStatusSteps = [];
|
|
991
|
+
let flatStepCount = 0;
|
|
992
|
+
for (let stepIndex = 0;stepIndex < steps.length; stepIndex++) {
|
|
993
|
+
const step = steps[stepIndex];
|
|
994
|
+
if (isParallelGroup(step)) {
|
|
995
|
+
parallelGroups.push({ start: flatStepCount, count: step.parallel.length, stepIndex });
|
|
996
|
+
for (const task of step.parallel) {
|
|
997
|
+
const taskFlatIndex = flatStepCount;
|
|
998
|
+
const transcriptPath = resolveAsyncStepTranscriptPath({ artifactsDir, artifactConfig, runId: id, agent: task.agent, flatIndex: taskFlatIndex, flatStepCount: initialFlatStepCount });
|
|
999
|
+
initialStatusSteps.push({
|
|
1000
|
+
agent: task.agent,
|
|
1001
|
+
phase: task.phase,
|
|
1002
|
+
label: task.label,
|
|
1003
|
+
outputName: task.outputName,
|
|
1004
|
+
structured: task.structured,
|
|
1005
|
+
status: "pending",
|
|
1006
|
+
...task.toolBudget ? { toolBudget: initialToolBudgetState(task.toolBudget) } : {},
|
|
1007
|
+
...task.sessionFile ? { sessionFile: task.sessionFile } : {},
|
|
1008
|
+
...transcriptPath ? { transcriptPath } : {},
|
|
1009
|
+
skills: task.skills,
|
|
1010
|
+
model: task.model,
|
|
1011
|
+
thinking: task.thinking,
|
|
1012
|
+
attemptedModels: task.modelCandidates && task.modelCandidates.length > 0 ? task.modelCandidates : task.model ? [task.model] : undefined,
|
|
1013
|
+
recentTools: [],
|
|
1014
|
+
recentOutput: []
|
|
1015
|
+
});
|
|
1016
|
+
flatStepCount++;
|
|
1017
|
+
}
|
|
1018
|
+
} else if (isDynamicRunnerGroup(step)) {
|
|
1019
|
+
parallelGroups.push({ start: flatStepCount, count: 1, stepIndex });
|
|
1020
|
+
initialStatusSteps.push({
|
|
1021
|
+
agent: `expand:${step.parallel.agent}`,
|
|
1022
|
+
phase: step.phase ?? step.parallel.phase,
|
|
1023
|
+
label: step.label ?? step.parallel.label ?? `Dynamic fanout (${step.collect.as})`,
|
|
1024
|
+
outputName: step.collect.as,
|
|
1025
|
+
structured: Boolean(step.collect.outputSchema),
|
|
1026
|
+
status: "pending",
|
|
1027
|
+
...step.parallel.toolBudget ? { toolBudget: initialToolBudgetState(step.parallel.toolBudget) } : {},
|
|
1028
|
+
recentTools: [],
|
|
1029
|
+
recentOutput: []
|
|
1030
|
+
});
|
|
1031
|
+
flatStepCount++;
|
|
1032
|
+
} else {
|
|
1033
|
+
const stepFlatIndex = flatStepCount;
|
|
1034
|
+
const transcriptPath = resolveAsyncStepTranscriptPath({ artifactsDir, artifactConfig, runId: id, agent: step.agent, flatIndex: stepFlatIndex, flatStepCount: initialFlatStepCount });
|
|
1035
|
+
initialStatusSteps.push({
|
|
1036
|
+
agent: step.agent,
|
|
1037
|
+
phase: step.phase,
|
|
1038
|
+
label: step.label,
|
|
1039
|
+
outputName: step.outputName,
|
|
1040
|
+
structured: step.structured,
|
|
1041
|
+
status: "pending",
|
|
1042
|
+
...step.toolBudget ? { toolBudget: initialToolBudgetState(step.toolBudget) } : {},
|
|
1043
|
+
...step.sessionFile ? { sessionFile: step.sessionFile } : {},
|
|
1044
|
+
...transcriptPath ? { transcriptPath } : {},
|
|
1045
|
+
skills: step.skills,
|
|
1046
|
+
model: step.model,
|
|
1047
|
+
thinking: step.thinking,
|
|
1048
|
+
attemptedModels: step.modelCandidates && step.modelCandidates.length > 0 ? step.modelCandidates : step.model ? [step.model] : undefined,
|
|
1049
|
+
recentTools: [],
|
|
1050
|
+
recentOutput: []
|
|
1051
|
+
});
|
|
1052
|
+
flatStepCount++;
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
const sessionEnabled = Boolean(config.sessionDir) || shareEnabled || flatSteps.some((step) => Boolean(step.sessionFile));
|
|
1056
|
+
const statusPayload = {
|
|
1057
|
+
lifecycleArtifactVersion: SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
|
|
1058
|
+
runId: id,
|
|
1059
|
+
...config.sessionId ? { sessionId: config.sessionId } : {},
|
|
1060
|
+
mode: config.resultMode ?? (flatSteps.length > 1 ? "chain" : "single"),
|
|
1061
|
+
state: "running",
|
|
1062
|
+
lastActivityAt: overallStartTime,
|
|
1063
|
+
startedAt: overallStartTime,
|
|
1064
|
+
lastUpdate: overallStartTime,
|
|
1065
|
+
...config.timeoutMs !== undefined ? { timeoutMs: config.timeoutMs } : {},
|
|
1066
|
+
...config.deadlineAt !== undefined ? { deadlineAt: config.deadlineAt } : {},
|
|
1067
|
+
...config.turnBudget ? { turnBudget: initialTurnBudgetState(config.turnBudget) } : {},
|
|
1068
|
+
...config.toolBudget ? { toolBudget: initialToolBudgetState(config.toolBudget) } : {},
|
|
1069
|
+
pid: process.pid,
|
|
1070
|
+
cwd,
|
|
1071
|
+
currentStep: 0,
|
|
1072
|
+
chainStepCount: steps.length,
|
|
1073
|
+
parallelGroups,
|
|
1074
|
+
workflowGraph: config.workflowGraph,
|
|
1075
|
+
steps: initialStatusSteps,
|
|
1076
|
+
artifactsDir,
|
|
1077
|
+
sessionDir: config.sessionDir,
|
|
1078
|
+
outputFile: path.join(asyncDir, "output-0.log")
|
|
1079
|
+
};
|
|
1080
|
+
fs.mkdirSync(asyncDir, { recursive: true });
|
|
1081
|
+
writeAtomicJson(statusPath, statusPayload);
|
|
1082
|
+
const emitNestedSelfEvent = (type) => {
|
|
1083
|
+
if (!config.nestedRoute || !config.nestedSelf)
|
|
1084
|
+
return;
|
|
1085
|
+
try {
|
|
1086
|
+
writeNestedEvent(config.nestedRoute, {
|
|
1087
|
+
type,
|
|
1088
|
+
ts: Date.now(),
|
|
1089
|
+
parentRunId: config.nestedSelf.parentRunId,
|
|
1090
|
+
parentStepIndex: config.nestedSelf.parentStepIndex,
|
|
1091
|
+
child: nestedSummaryFromAsyncStatus(statusPayload, asyncDir, {
|
|
1092
|
+
id,
|
|
1093
|
+
parentRunId: config.nestedSelf.parentRunId,
|
|
1094
|
+
parentStepIndex: config.nestedSelf.parentStepIndex,
|
|
1095
|
+
depth: config.nestedSelf.depth,
|
|
1096
|
+
path: config.nestedSelf.path,
|
|
1097
|
+
mode: statusPayload.mode,
|
|
1098
|
+
ts: Date.now()
|
|
1099
|
+
})
|
|
1100
|
+
});
|
|
1101
|
+
} catch (error) {
|
|
1102
|
+
console.error("Failed to emit nested async status event:", error);
|
|
1103
|
+
}
|
|
1104
|
+
};
|
|
1105
|
+
const refreshWorkflowGraph = () => {
|
|
1106
|
+
if (!config.workflowGraph)
|
|
1107
|
+
return;
|
|
1108
|
+
const graph = structuredClone(statusPayload.workflowGraph ?? config.workflowGraph);
|
|
1109
|
+
const normalize = (status) => {
|
|
1110
|
+
if (status === "complete" || status === "completed")
|
|
1111
|
+
return "completed";
|
|
1112
|
+
if (status === "running" || status === "failed" || status === "paused" || status === "pending")
|
|
1113
|
+
return status;
|
|
1114
|
+
return "pending";
|
|
1115
|
+
};
|
|
1116
|
+
const updateNode = (node) => {
|
|
1117
|
+
if (node.flatIndex !== undefined) {
|
|
1118
|
+
const step = statusPayload.steps[node.flatIndex];
|
|
1119
|
+
if (step) {
|
|
1120
|
+
node.status = normalize(step.status);
|
|
1121
|
+
node.error = step.error;
|
|
1122
|
+
node.acceptanceStatus = step.acceptance?.status;
|
|
1123
|
+
}
|
|
1124
|
+
if (statusPayload.currentStep === node.flatIndex)
|
|
1125
|
+
graph.currentNodeId = node.id;
|
|
1126
|
+
}
|
|
1127
|
+
for (const child of node.children ?? [])
|
|
1128
|
+
updateNode(child);
|
|
1129
|
+
if (node.children?.length) {
|
|
1130
|
+
if (node.children.every((child) => child.status === "completed"))
|
|
1131
|
+
node.status = "completed";
|
|
1132
|
+
else if (node.children.some((child) => child.status === "running"))
|
|
1133
|
+
node.status = "running";
|
|
1134
|
+
else if (node.children.some((child) => child.status === "failed"))
|
|
1135
|
+
node.status = "failed";
|
|
1136
|
+
else if (node.children.some((child) => child.status === "paused"))
|
|
1137
|
+
node.status = "paused";
|
|
1138
|
+
}
|
|
1139
|
+
if (node.error)
|
|
1140
|
+
node.status = "failed";
|
|
1141
|
+
};
|
|
1142
|
+
for (const node of graph.nodes)
|
|
1143
|
+
updateNode(node);
|
|
1144
|
+
statusPayload.workflowGraph = graph;
|
|
1145
|
+
};
|
|
1146
|
+
const writeStatusPayload = () => {
|
|
1147
|
+
refreshWorkflowGraph();
|
|
1148
|
+
writeAtomicJson(statusPath, statusPayload);
|
|
1149
|
+
emitNestedSelfEvent(statusPayload.state === "running" || statusPayload.state === "queued" ? "subagent.nested.updated" : "subagent.nested.completed");
|
|
1150
|
+
};
|
|
1151
|
+
const registerStepInterrupt = (flatIndex, interrupt) => {
|
|
1152
|
+
if (!interrupt) {
|
|
1153
|
+
activeChildInterrupts.delete(flatIndex);
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
activeChildInterrupts.set(flatIndex, interrupt);
|
|
1157
|
+
if (interrupted)
|
|
1158
|
+
interrupt();
|
|
1159
|
+
};
|
|
1160
|
+
const registerStepTimeout = (flatIndex, interrupt) => {
|
|
1161
|
+
if (!interrupt) {
|
|
1162
|
+
activeChildTimeouts.delete(flatIndex);
|
|
1163
|
+
return;
|
|
1164
|
+
}
|
|
1165
|
+
activeChildTimeouts.set(flatIndex, interrupt);
|
|
1166
|
+
if (timedOut)
|
|
1167
|
+
interrupt();
|
|
1168
|
+
};
|
|
1169
|
+
const registerStepTurnBudgetAbort = (flatIndex, abort) => {
|
|
1170
|
+
if (!abort) {
|
|
1171
|
+
activeChildTurnBudgetAborts.delete(flatIndex);
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
1174
|
+
activeChildTurnBudgetAborts.set(flatIndex, abort);
|
|
1175
|
+
};
|
|
1176
|
+
const interruptActiveChildren = () => {
|
|
1177
|
+
for (const interrupt of [...activeChildInterrupts.values()])
|
|
1178
|
+
interrupt();
|
|
1179
|
+
};
|
|
1180
|
+
const timeoutActiveChildren = () => {
|
|
1181
|
+
for (const interrupt of [...activeChildTimeouts.values()])
|
|
1182
|
+
interrupt();
|
|
1183
|
+
};
|
|
1184
|
+
const nestedRuns = function* (children) {
|
|
1185
|
+
for (const child of children ?? []) {
|
|
1186
|
+
yield child;
|
|
1187
|
+
yield* nestedRuns(child.children);
|
|
1188
|
+
yield* nestedRuns(child.steps?.flatMap((step) => step.children ?? []));
|
|
1189
|
+
}
|
|
1190
|
+
};
|
|
1191
|
+
const interruptNestedAsyncDescendants = () => {
|
|
1192
|
+
if (!config.nestedRoute)
|
|
1193
|
+
return;
|
|
1194
|
+
let registry;
|
|
1195
|
+
try {
|
|
1196
|
+
registry = projectNestedEvents(config.nestedRoute);
|
|
1197
|
+
} catch (error) {
|
|
1198
|
+
appendJsonl(eventsPath, JSON.stringify({
|
|
1199
|
+
type: "subagent.nested.interrupt_failed",
|
|
1200
|
+
ts: Date.now(),
|
|
1201
|
+
runId: id,
|
|
1202
|
+
message: error instanceof Error ? error.message : String(error)
|
|
1203
|
+
}));
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1206
|
+
for (const run of nestedRuns(registry.children)) {
|
|
1207
|
+
if (run.state !== "running" && run.state !== "queued")
|
|
1208
|
+
continue;
|
|
1209
|
+
const nestedAsyncDir = run.asyncDir ?? resolveNestedAsyncDir(config.nestedRoute.rootRunId, run);
|
|
1210
|
+
if (!nestedAsyncDir)
|
|
1211
|
+
continue;
|
|
1212
|
+
try {
|
|
1213
|
+
deliverInterruptRequest({ asyncDir: nestedAsyncDir, pid: run.pid, source: "ancestor-interrupt" });
|
|
1214
|
+
} catch (error) {
|
|
1215
|
+
appendJsonl(eventsPath, JSON.stringify({
|
|
1216
|
+
type: "subagent.nested.interrupt_failed",
|
|
1217
|
+
ts: Date.now(),
|
|
1218
|
+
runId: id,
|
|
1219
|
+
targetRunId: run.id,
|
|
1220
|
+
message: error instanceof Error ? error.message : String(error)
|
|
1221
|
+
}));
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
};
|
|
1225
|
+
const timeoutNestedAsyncDescendants = () => {
|
|
1226
|
+
if (!config.nestedRoute)
|
|
1227
|
+
return;
|
|
1228
|
+
let registry;
|
|
1229
|
+
try {
|
|
1230
|
+
registry = projectNestedEvents(config.nestedRoute);
|
|
1231
|
+
} catch (error) {
|
|
1232
|
+
appendJsonl(eventsPath, JSON.stringify({
|
|
1233
|
+
type: "subagent.nested.timeout_failed",
|
|
1234
|
+
ts: Date.now(),
|
|
1235
|
+
runId: id,
|
|
1236
|
+
message: error instanceof Error ? error.message : String(error)
|
|
1237
|
+
}));
|
|
1238
|
+
return;
|
|
1239
|
+
}
|
|
1240
|
+
for (const run of nestedRuns(registry.children)) {
|
|
1241
|
+
if (run.state !== "running" && run.state !== "queued")
|
|
1242
|
+
continue;
|
|
1243
|
+
const nestedAsyncDir = run.asyncDir ?? resolveNestedAsyncDir(config.nestedRoute.rootRunId, run);
|
|
1244
|
+
if (!nestedAsyncDir)
|
|
1245
|
+
continue;
|
|
1246
|
+
try {
|
|
1247
|
+
deliverTimeoutRequest({ asyncDir: nestedAsyncDir, pid: run.pid, source: "ancestor-timeout" });
|
|
1248
|
+
} catch (error) {
|
|
1249
|
+
appendJsonl(eventsPath, JSON.stringify({
|
|
1250
|
+
type: "subagent.nested.timeout_failed",
|
|
1251
|
+
ts: Date.now(),
|
|
1252
|
+
runId: id,
|
|
1253
|
+
targetRunId: run.id,
|
|
1254
|
+
message: error instanceof Error ? error.message : String(error)
|
|
1255
|
+
}));
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
};
|
|
1259
|
+
const pausedStepResult = (agent) => ({
|
|
1260
|
+
agent,
|
|
1261
|
+
output: "Paused after interrupt. Waiting for explicit next action.",
|
|
1262
|
+
exitCode: 0,
|
|
1263
|
+
interrupted: true
|
|
1264
|
+
});
|
|
1265
|
+
const timedOutStepResult = (agent) => ({
|
|
1266
|
+
agent,
|
|
1267
|
+
output: timeoutMessage ?? "Subagent timed out.",
|
|
1268
|
+
error: timeoutMessage ?? "Subagent timed out.",
|
|
1269
|
+
exitCode: 1,
|
|
1270
|
+
timedOut: true
|
|
1271
|
+
});
|
|
1272
|
+
const consumePendingAppendRequests = () => {
|
|
1273
|
+
if (statusPayload.mode !== "chain" || statusPayload.state !== "running")
|
|
1274
|
+
return;
|
|
1275
|
+
const requests = consumeChainAppendRequests(asyncDir);
|
|
1276
|
+
if (requests.length === 0) {
|
|
1277
|
+
const pendingAppends = countPendingChainAppendRequests(asyncDir);
|
|
1278
|
+
if ((statusPayload.pendingAppends ?? 0) !== pendingAppends) {
|
|
1279
|
+
statusPayload.pendingAppends = pendingAppends;
|
|
1280
|
+
statusPayload.lastUpdate = Date.now();
|
|
1281
|
+
writeStatusPayload();
|
|
1282
|
+
}
|
|
1283
|
+
return;
|
|
1284
|
+
}
|
|
1285
|
+
const appendedSteps = requests.flatMap((request) => request.steps);
|
|
1286
|
+
steps.push(...appendedSteps);
|
|
1287
|
+
const now = Date.now();
|
|
1288
|
+
const pendingAppends = countPendingChainAppendRequests(asyncDir);
|
|
1289
|
+
const added = appendRunnerStepsToStatus({
|
|
1290
|
+
status: statusPayload,
|
|
1291
|
+
steps: appendedSteps,
|
|
1292
|
+
now,
|
|
1293
|
+
pendingAppends
|
|
1294
|
+
});
|
|
1295
|
+
mutatingFailureStates.push(...Array.from({ length: added.addedFlatSteps }, () => createMutatingFailureState()));
|
|
1296
|
+
pendingToolResults.push(...Array.from({ length: added.addedFlatSteps }, () => {
|
|
1297
|
+
return;
|
|
1298
|
+
}));
|
|
1299
|
+
if (config.childIntercomTargets) {
|
|
1300
|
+
config.childIntercomTargets = statusPayload.steps.map((statusStep, index) => resolveSubagentIntercomTarget(id, statusStep.agent, index));
|
|
1301
|
+
}
|
|
1302
|
+
writeStatusPayload();
|
|
1303
|
+
for (const request of requests) {
|
|
1304
|
+
appendJsonl(eventsPath, JSON.stringify({
|
|
1305
|
+
type: "subagent.chain.append.accepted",
|
|
1306
|
+
ts: now,
|
|
1307
|
+
runId: id,
|
|
1308
|
+
requestId: request.id,
|
|
1309
|
+
stepCount: request.steps.length,
|
|
1310
|
+
pendingAppends
|
|
1311
|
+
}));
|
|
1312
|
+
}
|
|
1313
|
+
};
|
|
1314
|
+
const markDynamicGraphGroup = (stepIndex, status, error, acceptance) => {
|
|
1315
|
+
const groupNode = statusPayload.workflowGraph?.nodes.find((node) => node.id === `step-${stepIndex}`);
|
|
1316
|
+
if (!groupNode)
|
|
1317
|
+
return;
|
|
1318
|
+
groupNode.status = status;
|
|
1319
|
+
groupNode.error = error;
|
|
1320
|
+
groupNode.acceptanceStatus = acceptance?.status ?? groupNode.acceptanceStatus;
|
|
1321
|
+
};
|
|
1322
|
+
const stepOutputActivityAt = (index) => {
|
|
1323
|
+
const step = statusPayload.steps[index];
|
|
1324
|
+
let lastActivityAt = step?.lastActivityAt ?? step?.startedAt ?? overallStartTime;
|
|
1325
|
+
const outputPath = path.join(asyncDir, `output-${index}.log`);
|
|
1326
|
+
try {
|
|
1327
|
+
lastActivityAt = Math.max(lastActivityAt, fs.statSync(outputPath).mtimeMs);
|
|
1328
|
+
} catch (error) {
|
|
1329
|
+
if (error.code !== "ENOENT") {
|
|
1330
|
+
console.error(`Failed to inspect async output file '${outputPath}':`, error);
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
return lastActivityAt;
|
|
1334
|
+
};
|
|
1335
|
+
const emittedControlEventKeys = new Set;
|
|
1336
|
+
const activeLongRunningSteps = new Set;
|
|
1337
|
+
const mutatingFailureStates = initialStatusSteps.map(() => createMutatingFailureState());
|
|
1338
|
+
const pendingToolResults = initialStatusSteps.map(() => {
|
|
1339
|
+
return;
|
|
1340
|
+
});
|
|
1341
|
+
const mutatingFailureWindowMs = 300000;
|
|
1342
|
+
const appendControlEvent = (event) => {
|
|
1343
|
+
if (!controlConfig.enabled)
|
|
1344
|
+
return;
|
|
1345
|
+
const childIntercomTarget = config.childIntercomTargets?.[event.index ?? statusPayload.currentStep];
|
|
1346
|
+
const channels = event.type === "active_long_running" ? controlConfig.notifyChannels.filter((channel) => channel !== "intercom") : controlConfig.notifyChannels;
|
|
1347
|
+
if (channels.length === 0 || !claimControlNotification(controlConfig, event, emittedControlEventKeys, childIntercomTarget))
|
|
1348
|
+
return;
|
|
1349
|
+
appendJsonl(eventsPath, JSON.stringify({
|
|
1350
|
+
type: "subagent.control",
|
|
1351
|
+
event,
|
|
1352
|
+
channels,
|
|
1353
|
+
childIntercomTarget,
|
|
1354
|
+
noticeText: formatControlNoticeMessage(event, childIntercomTarget),
|
|
1355
|
+
...config.controlIntercomTarget && channels.includes("intercom") ? {
|
|
1356
|
+
intercom: {
|
|
1357
|
+
to: config.controlIntercomTarget,
|
|
1358
|
+
message: formatControlIntercomMessage(event, childIntercomTarget)
|
|
1359
|
+
}
|
|
1360
|
+
} : {}
|
|
1361
|
+
}));
|
|
1362
|
+
};
|
|
1363
|
+
const syncTopLevelCurrentTool = () => {
|
|
1364
|
+
const activeStep = statusPayload.steps.filter((step) => step.status === "running" && typeof step.currentTool === "string" && step.currentTool.length > 0).sort((left, right) => (right.currentToolStartedAt ?? 0) - (left.currentToolStartedAt ?? 0))[0];
|
|
1365
|
+
statusPayload.currentTool = activeStep?.currentTool;
|
|
1366
|
+
statusPayload.currentToolStartedAt = activeStep?.currentToolStartedAt;
|
|
1367
|
+
statusPayload.currentPath = activeStep?.currentPath;
|
|
1368
|
+
};
|
|
1369
|
+
const maybeEmitActiveLongRunning = (flatIndex, now) => {
|
|
1370
|
+
if (!controlConfig.enabled || activeLongRunningSteps.has(flatIndex))
|
|
1371
|
+
return false;
|
|
1372
|
+
const step = statusPayload.steps[flatIndex];
|
|
1373
|
+
if (!step || step.status !== "running" || step.activityState === "needs_attention")
|
|
1374
|
+
return false;
|
|
1375
|
+
const reason = nextLongRunningTrigger(controlConfig, {
|
|
1376
|
+
startedAt: step.startedAt ?? overallStartTime,
|
|
1377
|
+
now,
|
|
1378
|
+
turns: step.turnCount ?? 0,
|
|
1379
|
+
tokens: step.tokens?.total ?? 0
|
|
1380
|
+
});
|
|
1381
|
+
if (!reason)
|
|
1382
|
+
return false;
|
|
1383
|
+
activeLongRunningSteps.add(flatIndex);
|
|
1384
|
+
const previous = step.activityState;
|
|
1385
|
+
step.activityState = "active_long_running";
|
|
1386
|
+
statusPayload.activityState = statusPayload.activityState === "needs_attention" ? "needs_attention" : "active_long_running";
|
|
1387
|
+
const event = buildControlEvent({
|
|
1388
|
+
type: "active_long_running",
|
|
1389
|
+
from: previous,
|
|
1390
|
+
to: "active_long_running",
|
|
1391
|
+
runId: id,
|
|
1392
|
+
agent: step.agent,
|
|
1393
|
+
index: flatIndex,
|
|
1394
|
+
ts: now,
|
|
1395
|
+
message: `${step.agent} is still active but long-running`,
|
|
1396
|
+
reason,
|
|
1397
|
+
turns: step.turnCount,
|
|
1398
|
+
tokens: step.tokens?.total,
|
|
1399
|
+
toolCount: step.toolCount,
|
|
1400
|
+
currentTool: step.currentTool,
|
|
1401
|
+
currentToolDurationMs: step.currentToolStartedAt ? Math.max(0, now - step.currentToolStartedAt) : undefined,
|
|
1402
|
+
currentPath: step.currentPath,
|
|
1403
|
+
elapsedMs: now - (step.startedAt ?? overallStartTime)
|
|
1404
|
+
});
|
|
1405
|
+
appendControlEvent(event);
|
|
1406
|
+
return true;
|
|
1407
|
+
};
|
|
1408
|
+
const deliverSteerRequest = (request) => {
|
|
1409
|
+
if (statusPayload.state !== "running")
|
|
1410
|
+
return;
|
|
1411
|
+
const runningIndexes = statusPayload.steps.map((step, index) => ({ step, index })).filter(({ step }) => step.status === "running").map(({ index }) => index);
|
|
1412
|
+
const targets = request.targetIndex !== undefined ? [request.targetIndex] : runningIndexes;
|
|
1413
|
+
const now = Date.now();
|
|
1414
|
+
const accepted = [];
|
|
1415
|
+
const rejected = [];
|
|
1416
|
+
for (const index of targets) {
|
|
1417
|
+
const step = statusPayload.steps[index];
|
|
1418
|
+
if (!step) {
|
|
1419
|
+
rejected.push({ index, reason: "child index out of range" });
|
|
1420
|
+
continue;
|
|
1421
|
+
}
|
|
1422
|
+
if (step.status !== "running") {
|
|
1423
|
+
rejected.push({ index, reason: `child is ${step.status}` });
|
|
1424
|
+
continue;
|
|
1425
|
+
}
|
|
1426
|
+
enqueueStepSteer(asyncDir, index, request);
|
|
1427
|
+
step.steerCount = (step.steerCount ?? 0) + 1;
|
|
1428
|
+
step.lastSteerAt = now;
|
|
1429
|
+
accepted.push(index);
|
|
1430
|
+
}
|
|
1431
|
+
if (accepted.length > 0) {
|
|
1432
|
+
statusPayload.steerCount = (statusPayload.steerCount ?? 0) + accepted.length;
|
|
1433
|
+
statusPayload.lastSteerAt = now;
|
|
1434
|
+
statusPayload.lastUpdate = now;
|
|
1435
|
+
writeStatusPayload();
|
|
1436
|
+
}
|
|
1437
|
+
appendJsonl(eventsPath, JSON.stringify({
|
|
1438
|
+
type: "subagent.steer.requested",
|
|
1439
|
+
ts: now,
|
|
1440
|
+
runId: id,
|
|
1441
|
+
requestId: request.id,
|
|
1442
|
+
message: request.message,
|
|
1443
|
+
...request.source ? { source: request.source } : {},
|
|
1444
|
+
...request.targetIndex !== undefined ? { targetIndex: request.targetIndex } : {},
|
|
1445
|
+
acceptedIndexes: accepted,
|
|
1446
|
+
...rejected.length ? { rejected } : {}
|
|
1447
|
+
}));
|
|
1448
|
+
};
|
|
1449
|
+
const flushPendingStepSteers = (flatIndex) => {
|
|
1450
|
+
const remaining = [];
|
|
1451
|
+
for (const request of pendingStepSteers.splice(0)) {
|
|
1452
|
+
if (request.targetIndex === undefined)
|
|
1453
|
+
deliverSteerRequest({ ...request, targetIndex: flatIndex });
|
|
1454
|
+
else if (request.targetIndex === flatIndex)
|
|
1455
|
+
deliverSteerRequest(request);
|
|
1456
|
+
else
|
|
1457
|
+
remaining.push(request);
|
|
1458
|
+
}
|
|
1459
|
+
pendingStepSteers.push(...remaining);
|
|
1460
|
+
};
|
|
1461
|
+
const updateStepModel = (flatIndex, model, thinking, now = Date.now()) => {
|
|
1462
|
+
const step = statusPayload.steps[flatIndex];
|
|
1463
|
+
if (!step)
|
|
1464
|
+
return;
|
|
1465
|
+
step.model = model;
|
|
1466
|
+
step.thinking = thinking;
|
|
1467
|
+
statusPayload.lastUpdate = now;
|
|
1468
|
+
writeStatusPayload();
|
|
1469
|
+
};
|
|
1470
|
+
const updateStepTurnBudget = (flatIndex, turnCount, now, terminalAssistantStop) => {
|
|
1471
|
+
const budget = config.turnBudget;
|
|
1472
|
+
const step = statusPayload.steps[flatIndex];
|
|
1473
|
+
if (!budget || !step || timedOut || turnBudgetExceeded || step.turnBudgetExceeded)
|
|
1474
|
+
return;
|
|
1475
|
+
if (turnCount < budget.maxTurns) {
|
|
1476
|
+
const state = { ...budget, outcome: "within-budget", turnCount };
|
|
1477
|
+
step.turnBudget = state;
|
|
1478
|
+
statusPayload.turnBudget = state;
|
|
1479
|
+
return;
|
|
1480
|
+
}
|
|
1481
|
+
const state = turnBudgetState(budget, turnCount, false);
|
|
1482
|
+
step.turnBudget = state;
|
|
1483
|
+
statusPayload.turnBudget = state;
|
|
1484
|
+
if (!step.wrapUpRequested) {
|
|
1485
|
+
step.wrapUpRequested = true;
|
|
1486
|
+
statusPayload.wrapUpRequested = true;
|
|
1487
|
+
appendRecentStepOutput(step, [turnBudgetSoftNote(budget, turnCount)]);
|
|
1488
|
+
}
|
|
1489
|
+
if (!shouldAbortForTurnBudget(budget, turnCount, terminalAssistantStop))
|
|
1490
|
+
return;
|
|
1491
|
+
const exceededState = turnBudgetState(budget, turnCount, true);
|
|
1492
|
+
const message = turnBudgetExceededMessage(budget, turnCount);
|
|
1493
|
+
step.turnBudget = exceededState;
|
|
1494
|
+
step.turnBudgetExceeded = true;
|
|
1495
|
+
step.wrapUpRequested = true;
|
|
1496
|
+
step.error = message;
|
|
1497
|
+
turnBudgetExceeded = true;
|
|
1498
|
+
statusPayload.turnBudget = exceededState;
|
|
1499
|
+
statusPayload.turnBudgetExceeded = true;
|
|
1500
|
+
statusPayload.wrapUpRequested = true;
|
|
1501
|
+
statusPayload.error = message;
|
|
1502
|
+
statusPayload.lastUpdate = now;
|
|
1503
|
+
appendJsonl(eventsPath, JSON.stringify({ type: "subagent.step.turn_budget_exceeded", ts: now, runId: id, stepIndex: flatIndex, agent: step.agent, turnCount, maxTurns: budget.maxTurns, graceTurns: budget.graceTurns, message }));
|
|
1504
|
+
activeChildTurnBudgetAborts.get(flatIndex)?.(message, exceededState);
|
|
1505
|
+
};
|
|
1506
|
+
const updateStepFromChildEvent = (flatIndex, event) => {
|
|
1507
|
+
const step = statusPayload.steps[flatIndex];
|
|
1508
|
+
if (!step)
|
|
1509
|
+
return;
|
|
1510
|
+
const now = Date.now();
|
|
1511
|
+
statusPayload.currentStep = flatIndex;
|
|
1512
|
+
if (event.type === "tool_execution_start" && event.toolName) {
|
|
1513
|
+
const mutates = isMutatingTool(event.toolName, event.args);
|
|
1514
|
+
const currentPath = resolveCurrentPath(event.toolName, event.args);
|
|
1515
|
+
step.toolCount = (step.toolCount ?? 0) + 1;
|
|
1516
|
+
const configuredToolBudget = flatSteps[flatIndex]?.toolBudget;
|
|
1517
|
+
if (configuredToolBudget) {
|
|
1518
|
+
step.toolBudget = toolBudgetState(configuredToolBudget, step.toolCount);
|
|
1519
|
+
statusPayload.toolBudget = step.toolBudget;
|
|
1520
|
+
}
|
|
1521
|
+
step.currentTool = event.toolName;
|
|
1522
|
+
step.currentToolArgs = extractToolArgsPreview(event.args ?? {});
|
|
1523
|
+
step.currentToolStartedAt = now;
|
|
1524
|
+
step.currentPath = currentPath;
|
|
1525
|
+
pendingToolResults[flatIndex] = { tool: event.toolName, path: currentPath, mutates, startedAt: now };
|
|
1526
|
+
statusPayload.toolCount = (statusPayload.toolCount ?? 0) + 1;
|
|
1527
|
+
syncTopLevelCurrentTool();
|
|
1528
|
+
} else if (event.type === "tool_execution_end") {
|
|
1529
|
+
if (step.currentTool) {
|
|
1530
|
+
step.recentTools ??= [];
|
|
1531
|
+
step.recentTools.push({ tool: step.currentTool, args: step.currentToolArgs || "", endMs: now });
|
|
1532
|
+
}
|
|
1533
|
+
step.currentTool = undefined;
|
|
1534
|
+
step.currentToolArgs = undefined;
|
|
1535
|
+
step.currentToolStartedAt = undefined;
|
|
1536
|
+
step.currentPath = undefined;
|
|
1537
|
+
syncTopLevelCurrentTool();
|
|
1538
|
+
} else if (event.type === "tool_result_end" && event.message) {
|
|
1539
|
+
const toolSnapshot = pendingToolResults[flatIndex];
|
|
1540
|
+
pendingToolResults[flatIndex] = undefined;
|
|
1541
|
+
const resultText = extractTextFromContent(event.message.content);
|
|
1542
|
+
if (toolSnapshot && resultText.includes("Tool budget hard limit reached")) {
|
|
1543
|
+
const configuredToolBudget = flatSteps[flatIndex]?.toolBudget;
|
|
1544
|
+
if (configuredToolBudget) {
|
|
1545
|
+
step.toolBudget = toolBudgetState(configuredToolBudget, step.toolCount ?? 0, toolSnapshot.tool);
|
|
1546
|
+
step.toolBudgetBlocked = true;
|
|
1547
|
+
statusPayload.toolBudget = step.toolBudget;
|
|
1548
|
+
statusPayload.toolBudgetBlocked = true;
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
appendRecentStepOutput(step, resultText.split(`
|
|
1552
|
+
`).slice(-10));
|
|
1553
|
+
if (toolSnapshot?.mutates && didMutatingToolFail(resultText)) {
|
|
1554
|
+
const state = mutatingFailureStates[flatIndex];
|
|
1555
|
+
recordMutatingFailure(state, {
|
|
1556
|
+
tool: toolSnapshot.tool,
|
|
1557
|
+
path: toolSnapshot.path,
|
|
1558
|
+
error: resultText.split(`
|
|
1559
|
+
`).find((line) => line.trim())?.trim().slice(0, 180) ?? "mutating tool failed",
|
|
1560
|
+
ts: now
|
|
1561
|
+
}, mutatingFailureWindowMs);
|
|
1562
|
+
if (controlConfig.enabled && shouldEscalateMutatingFailures(state, controlConfig.failedToolAttemptsBeforeAttention) && step.activityState !== "needs_attention") {
|
|
1563
|
+
const previous = step.activityState;
|
|
1564
|
+
step.activityState = "needs_attention";
|
|
1565
|
+
statusPayload.activityState = "needs_attention";
|
|
1566
|
+
appendControlEvent(buildControlEvent({
|
|
1567
|
+
type: "needs_attention",
|
|
1568
|
+
from: previous,
|
|
1569
|
+
to: "needs_attention",
|
|
1570
|
+
runId: id,
|
|
1571
|
+
agent: step.agent,
|
|
1572
|
+
index: flatIndex,
|
|
1573
|
+
ts: now,
|
|
1574
|
+
message: `${step.agent} needs attention after repeated mutating tool failures`,
|
|
1575
|
+
reason: "tool_failures",
|
|
1576
|
+
turns: step.turnCount,
|
|
1577
|
+
tokens: step.tokens?.total,
|
|
1578
|
+
toolCount: step.toolCount,
|
|
1579
|
+
currentTool: toolSnapshot.tool,
|
|
1580
|
+
currentToolDurationMs: toolSnapshot.startedAt ? Math.max(0, now - toolSnapshot.startedAt) : undefined,
|
|
1581
|
+
currentPath: toolSnapshot.path,
|
|
1582
|
+
recentFailureSummary: summarizeRecentMutatingFailures(state)
|
|
1583
|
+
}));
|
|
1584
|
+
}
|
|
1585
|
+
} else if (toolSnapshot?.mutates) {
|
|
1586
|
+
resetMutatingFailureState(mutatingFailureStates[flatIndex]);
|
|
1587
|
+
}
|
|
1588
|
+
} else if (event.type === "message_end" && event.message?.role === "assistant") {
|
|
1589
|
+
appendRecentStepOutput(step, stripAcceptanceReport(extractTextFromContent(event.message.content)).split(`
|
|
1590
|
+
`).slice(-10));
|
|
1591
|
+
step.turnCount = (step.turnCount ?? 0) + 1;
|
|
1592
|
+
const usage = event.message.usage;
|
|
1593
|
+
if (usage) {
|
|
1594
|
+
const input = usage.input ?? usage.inputTokens ?? 0;
|
|
1595
|
+
const output = usage.output ?? usage.outputTokens ?? 0;
|
|
1596
|
+
const previousInput = step.tokens?.input ?? 0;
|
|
1597
|
+
const previousOutput = step.tokens?.output ?? 0;
|
|
1598
|
+
step.tokens = { input: previousInput + input, output: previousOutput + output, total: previousInput + previousOutput + input + output };
|
|
1599
|
+
const totalInput = statusPayload.totalTokens?.input ?? 0;
|
|
1600
|
+
const totalOutput = statusPayload.totalTokens?.output ?? 0;
|
|
1601
|
+
statusPayload.totalTokens = { input: totalInput + input, output: totalOutput + output, total: totalInput + totalOutput + input + output };
|
|
1602
|
+
}
|
|
1603
|
+
statusPayload.turnCount = Math.max(statusPayload.turnCount ?? 0, step.turnCount);
|
|
1604
|
+
updateStepTurnBudget(flatIndex, step.turnCount, now, isTerminalAssistantStop(event.message));
|
|
1605
|
+
}
|
|
1606
|
+
syncTopLevelCurrentTool();
|
|
1607
|
+
step.lastActivityAt = now;
|
|
1608
|
+
statusPayload.lastActivityAt = now;
|
|
1609
|
+
statusPayload.lastUpdate = now;
|
|
1610
|
+
maybeEmitActiveLongRunning(flatIndex, now);
|
|
1611
|
+
writeStatusPayload();
|
|
1612
|
+
};
|
|
1613
|
+
const updateRunnerActivityState = (now) => {
|
|
1614
|
+
if (!controlConfig.enabled)
|
|
1615
|
+
return false;
|
|
1616
|
+
let changed = false;
|
|
1617
|
+
let runLastActivityAt = statusPayload.lastActivityAt ?? overallStartTime;
|
|
1618
|
+
for (let index = 0;index < statusPayload.steps.length; index++) {
|
|
1619
|
+
const step = statusPayload.steps[index];
|
|
1620
|
+
if (step.status !== "running")
|
|
1621
|
+
continue;
|
|
1622
|
+
const lastActivityAt = stepOutputActivityAt(index);
|
|
1623
|
+
runLastActivityAt = Math.max(runLastActivityAt, lastActivityAt);
|
|
1624
|
+
if (step.lastActivityAt !== lastActivityAt) {
|
|
1625
|
+
step.lastActivityAt = lastActivityAt;
|
|
1626
|
+
changed = true;
|
|
1627
|
+
}
|
|
1628
|
+
const idleState = deriveActivityState({
|
|
1629
|
+
config: controlConfig,
|
|
1630
|
+
startedAt: step.startedAt ?? overallStartTime,
|
|
1631
|
+
lastActivityAt,
|
|
1632
|
+
now
|
|
1633
|
+
});
|
|
1634
|
+
if (idleState === "needs_attention") {
|
|
1635
|
+
const previous = step.activityState;
|
|
1636
|
+
step.activityState = "needs_attention";
|
|
1637
|
+
if (previous !== "needs_attention") {
|
|
1638
|
+
appendControlEvent(buildControlEvent({
|
|
1639
|
+
from: previous,
|
|
1640
|
+
to: "needs_attention",
|
|
1641
|
+
runId: id,
|
|
1642
|
+
agent: step.agent,
|
|
1643
|
+
index,
|
|
1644
|
+
ts: now,
|
|
1645
|
+
lastActivityAt
|
|
1646
|
+
}));
|
|
1647
|
+
changed = true;
|
|
1648
|
+
}
|
|
1649
|
+
} else if (maybeEmitActiveLongRunning(index, now)) {
|
|
1650
|
+
changed = true;
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
if (statusPayload.lastActivityAt !== runLastActivityAt) {
|
|
1654
|
+
statusPayload.lastActivityAt = runLastActivityAt;
|
|
1655
|
+
changed = true;
|
|
1656
|
+
}
|
|
1657
|
+
const nextRunState = statusPayload.steps.some((step) => step.activityState === "needs_attention") ? "needs_attention" : statusPayload.steps.some((step) => step.activityState === "active_long_running") ? "active_long_running" : undefined;
|
|
1658
|
+
if (nextRunState !== currentActivityState) {
|
|
1659
|
+
currentActivityState = nextRunState;
|
|
1660
|
+
statusPayload.activityState = nextRunState;
|
|
1661
|
+
changed = true;
|
|
1662
|
+
}
|
|
1663
|
+
statusPayload.lastUpdate = now;
|
|
1664
|
+
if (changed)
|
|
1665
|
+
writeStatusPayload();
|
|
1666
|
+
return changed;
|
|
1667
|
+
};
|
|
1668
|
+
if (controlConfig.enabled) {
|
|
1669
|
+
activityTimer = setInterval(() => {
|
|
1670
|
+
if (statusPayload.state !== "running")
|
|
1671
|
+
return;
|
|
1672
|
+
const now = Date.now();
|
|
1673
|
+
updateRunnerActivityState(now);
|
|
1674
|
+
}, 1000);
|
|
1675
|
+
activityTimer.unref?.();
|
|
1676
|
+
}
|
|
1677
|
+
const interruptRunner = () => {
|
|
1678
|
+
consumeInterruptRequest(asyncDir);
|
|
1679
|
+
if (interrupted || statusPayload.state !== "running")
|
|
1680
|
+
return;
|
|
1681
|
+
interrupted = true;
|
|
1682
|
+
const now = Date.now();
|
|
1683
|
+
statusPayload.state = "paused";
|
|
1684
|
+
currentActivityState = undefined;
|
|
1685
|
+
statusPayload.activityState = undefined;
|
|
1686
|
+
statusPayload.lastUpdate = now;
|
|
1687
|
+
for (const step of statusPayload.steps) {
|
|
1688
|
+
if (step.status === "running") {
|
|
1689
|
+
step.status = "paused";
|
|
1690
|
+
step.activityState = undefined;
|
|
1691
|
+
step.endedAt = now;
|
|
1692
|
+
step.durationMs = step.startedAt ? now - step.startedAt : undefined;
|
|
1693
|
+
step.lastActivityAt = now;
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
writeStatusPayload();
|
|
1697
|
+
appendJsonl(eventsPath, JSON.stringify({
|
|
1698
|
+
type: "subagent.run.paused",
|
|
1699
|
+
ts: now,
|
|
1700
|
+
runId: id
|
|
1701
|
+
}));
|
|
1702
|
+
interruptNestedAsyncDescendants();
|
|
1703
|
+
interruptActiveChildren();
|
|
1704
|
+
};
|
|
1705
|
+
const timeoutRunner = () => {
|
|
1706
|
+
if (timedOut || interrupted || statusPayload.state !== "running")
|
|
1707
|
+
return;
|
|
1708
|
+
timedOut = true;
|
|
1709
|
+
const now = Date.now();
|
|
1710
|
+
const message = timeoutMessage ?? "Subagent timed out.";
|
|
1711
|
+
statusPayload.state = "failed";
|
|
1712
|
+
statusPayload.timedOut = true;
|
|
1713
|
+
statusPayload.error = message;
|
|
1714
|
+
currentActivityState = undefined;
|
|
1715
|
+
statusPayload.activityState = undefined;
|
|
1716
|
+
statusPayload.lastUpdate = now;
|
|
1717
|
+
for (const step of statusPayload.steps) {
|
|
1718
|
+
if (step.status !== "running" && step.status !== "pending")
|
|
1719
|
+
continue;
|
|
1720
|
+
step.status = "failed";
|
|
1721
|
+
step.error = message;
|
|
1722
|
+
step.exitCode = 1;
|
|
1723
|
+
step.timedOut = true;
|
|
1724
|
+
step.activityState = undefined;
|
|
1725
|
+
step.endedAt = now;
|
|
1726
|
+
step.durationMs = step.startedAt ? now - step.startedAt : 0;
|
|
1727
|
+
step.lastActivityAt = now;
|
|
1728
|
+
}
|
|
1729
|
+
writeStatusPayload();
|
|
1730
|
+
appendJsonl(eventsPath, JSON.stringify({
|
|
1731
|
+
type: "subagent.run.timed_out",
|
|
1732
|
+
ts: now,
|
|
1733
|
+
runId: id,
|
|
1734
|
+
timeoutMs: config.timeoutMs,
|
|
1735
|
+
deadlineAt: config.deadlineAt,
|
|
1736
|
+
message
|
|
1737
|
+
}));
|
|
1738
|
+
timeoutAbortController.abort();
|
|
1739
|
+
timeoutNestedAsyncDescendants();
|
|
1740
|
+
timeoutActiveChildren();
|
|
1741
|
+
};
|
|
1742
|
+
process.on(ASYNC_INTERRUPT_SIGNAL, interruptRunner);
|
|
1743
|
+
const disposeControlInbox = watchAsyncControlInbox(asyncDir, {
|
|
1744
|
+
onInterrupt: interruptRunner,
|
|
1745
|
+
onTimeout: timeoutRunner,
|
|
1746
|
+
onSteer: (request) => {
|
|
1747
|
+
const targetStep = request.targetIndex !== undefined ? statusPayload.steps[request.targetIndex] : undefined;
|
|
1748
|
+
if (targetStep?.status === "pending")
|
|
1749
|
+
pendingStepSteers.push(request);
|
|
1750
|
+
else if (request.targetIndex !== undefined || statusPayload.steps.some((step) => step.status === "running"))
|
|
1751
|
+
deliverSteerRequest(request);
|
|
1752
|
+
else
|
|
1753
|
+
pendingStepSteers.push(request);
|
|
1754
|
+
}
|
|
1755
|
+
});
|
|
1756
|
+
if (config.deadlineAt !== undefined) {
|
|
1757
|
+
const remainingMs = Math.max(0, config.deadlineAt - Date.now());
|
|
1758
|
+
timeoutTimer = setTimeout(timeoutRunner, remainingMs);
|
|
1759
|
+
timeoutTimer.unref?.();
|
|
1760
|
+
}
|
|
1761
|
+
appendJsonl(eventsPath, JSON.stringify({
|
|
1762
|
+
type: "subagent.run.started",
|
|
1763
|
+
lifecycleArtifactVersion: SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
|
|
1764
|
+
ts: overallStartTime,
|
|
1765
|
+
runId: id,
|
|
1766
|
+
mode: statusPayload.mode,
|
|
1767
|
+
cwd,
|
|
1768
|
+
pid: process.pid
|
|
1769
|
+
}));
|
|
1770
|
+
let flatIndex = 0;
|
|
1771
|
+
let stepCursor = 0;
|
|
1772
|
+
while (true) {
|
|
1773
|
+
if (interrupted || timedOut || turnBudgetExceeded)
|
|
1774
|
+
break;
|
|
1775
|
+
consumePendingAppendRequests();
|
|
1776
|
+
if (stepCursor >= steps.length)
|
|
1777
|
+
break;
|
|
1778
|
+
const stepIndex = stepCursor++;
|
|
1779
|
+
const step = steps[stepIndex];
|
|
1780
|
+
if (isDynamicRunnerGroup(step)) {
|
|
1781
|
+
const groupStartFlatIndex = flatIndex;
|
|
1782
|
+
let materialized;
|
|
1783
|
+
try {
|
|
1784
|
+
materialized = materializeDynamicParallelStep(step, outputs, stepIndex, { maxItems: config.dynamicFanoutMaxItems, allowRunnerFields: true });
|
|
1785
|
+
if (materialized.collectedOnEmpty)
|
|
1786
|
+
validateDynamicCollection(step.collect.outputSchema, materialized.collectedOnEmpty);
|
|
1787
|
+
} catch (error) {
|
|
1788
|
+
const now = Date.now();
|
|
1789
|
+
const message = error instanceof DynamicFanoutError ? error.message : error instanceof Error ? error.message : String(error);
|
|
1790
|
+
statusPayload.state = "failed";
|
|
1791
|
+
statusPayload.error = message;
|
|
1792
|
+
statusPayload.currentStep = flatIndex;
|
|
1793
|
+
const placeholder = statusPayload.steps[groupStartFlatIndex];
|
|
1794
|
+
if (placeholder) {
|
|
1795
|
+
placeholder.status = "failed";
|
|
1796
|
+
placeholder.error = message;
|
|
1797
|
+
placeholder.startedAt = now;
|
|
1798
|
+
placeholder.endedAt = now;
|
|
1799
|
+
placeholder.durationMs = 0;
|
|
1800
|
+
placeholder.exitCode = 1;
|
|
1801
|
+
}
|
|
1802
|
+
statusPayload.lastUpdate = now;
|
|
1803
|
+
markDynamicGraphGroup(stepIndex, "failed", message);
|
|
1804
|
+
writeStatusPayload();
|
|
1805
|
+
results.push({ agent: step.parallel.agent, output: message, error: message, success: false, exitCode: 1 });
|
|
1806
|
+
break;
|
|
1807
|
+
}
|
|
1808
|
+
if (materialized.parallel.length === 0) {
|
|
1809
|
+
const now = Date.now();
|
|
1810
|
+
const collection = materialized.collectedOnEmpty ?? [];
|
|
1811
|
+
outputs[step.collect.as] = {
|
|
1812
|
+
text: JSON.stringify(collection),
|
|
1813
|
+
structured: collection,
|
|
1814
|
+
agent: step.parallel.agent,
|
|
1815
|
+
stepIndex
|
|
1816
|
+
};
|
|
1817
|
+
statusPayload.outputs = outputs;
|
|
1818
|
+
const placeholder = statusPayload.steps[groupStartFlatIndex];
|
|
1819
|
+
if (placeholder) {
|
|
1820
|
+
placeholder.status = "complete";
|
|
1821
|
+
placeholder.startedAt = now;
|
|
1822
|
+
placeholder.endedAt = now;
|
|
1823
|
+
placeholder.durationMs = 0;
|
|
1824
|
+
}
|
|
1825
|
+
previousOutput = "Dynamic fanout produced 0 results.";
|
|
1826
|
+
const groupAcceptance = step.effectiveAcceptance?.explicit && !timedOut ? await evaluateAcceptance({
|
|
1827
|
+
acceptance: step.effectiveAcceptance,
|
|
1828
|
+
output: "",
|
|
1829
|
+
report: aggregateAcceptanceReport({
|
|
1830
|
+
results: [],
|
|
1831
|
+
notes: "Dynamic fanout produced 0 results."
|
|
1832
|
+
}),
|
|
1833
|
+
cwd,
|
|
1834
|
+
signal: timeoutAbortController.signal,
|
|
1835
|
+
abortMessage: timeoutMessage ?? "Subagent timed out."
|
|
1836
|
+
}) : undefined;
|
|
1837
|
+
const groupTimedOut = timedOut || timeoutAbortController.signal.aborted;
|
|
1838
|
+
const effectiveGroupAcceptance = groupTimedOut ? undefined : groupAcceptance;
|
|
1839
|
+
if (placeholder && effectiveGroupAcceptance)
|
|
1840
|
+
placeholder.acceptance = effectiveGroupAcceptance;
|
|
1841
|
+
const groupAcceptanceFailure = effectiveGroupAcceptance ? acceptanceFailureMessage(effectiveGroupAcceptance) : undefined;
|
|
1842
|
+
if (groupTimedOut || groupAcceptanceFailure) {
|
|
1843
|
+
const errorMessage = groupTimedOut ? timeoutMessage ?? "Subagent timed out." : groupAcceptanceFailure;
|
|
1844
|
+
statusPayload.state = "failed";
|
|
1845
|
+
statusPayload.error = errorMessage;
|
|
1846
|
+
if (placeholder) {
|
|
1847
|
+
placeholder.status = "failed";
|
|
1848
|
+
placeholder.error = errorMessage;
|
|
1849
|
+
placeholder.exitCode = 1;
|
|
1850
|
+
placeholder.timedOut = groupTimedOut ? true : undefined;
|
|
1851
|
+
}
|
|
1852
|
+
markDynamicGraphGroup(stepIndex, "failed", errorMessage, effectiveGroupAcceptance);
|
|
1853
|
+
statusPayload.lastUpdate = Date.now();
|
|
1854
|
+
writeStatusPayload();
|
|
1855
|
+
results.push({ agent: step.parallel.agent, output: errorMessage, error: errorMessage, success: false, exitCode: 1, timedOut: groupTimedOut ? true : undefined, acceptance: effectiveGroupAcceptance });
|
|
1856
|
+
break;
|
|
1857
|
+
}
|
|
1858
|
+
flatIndex++;
|
|
1859
|
+
statusPayload.lastUpdate = now;
|
|
1860
|
+
markDynamicGraphGroup(stepIndex, "completed", undefined, effectiveGroupAcceptance);
|
|
1861
|
+
writeStatusPayload();
|
|
1862
|
+
continue;
|
|
1863
|
+
}
|
|
1864
|
+
const dynamicSteps = materialized.parallel.map((task, itemIndex) => {
|
|
1865
|
+
const thinkingOverride = step.thinkingOverrides?.[itemIndex];
|
|
1866
|
+
const model = thinkingOverride ? applyThinkingSuffix(step.parallel.model, thinkingOverride, true) : step.parallel.model;
|
|
1867
|
+
const thinking = thinkingOverride ? resolveEffectiveThinking(model, thinkingOverride) : undefined;
|
|
1868
|
+
return {
|
|
1869
|
+
...step.parallel,
|
|
1870
|
+
task: task.task ?? step.parallel.task,
|
|
1871
|
+
label: task.label ?? step.parallel.label,
|
|
1872
|
+
...step.sessionFiles?.[itemIndex] ? { sessionFile: step.sessionFiles[itemIndex] } : {},
|
|
1873
|
+
...thinkingOverride ? {
|
|
1874
|
+
...model ? { model } : {},
|
|
1875
|
+
...thinking ? { thinking } : {},
|
|
1876
|
+
...step.parallel.modelCandidates ? { modelCandidates: step.parallel.modelCandidates.map((candidate) => applyThinkingSuffix(candidate, thinkingOverride, true)) } : {}
|
|
1877
|
+
} : {},
|
|
1878
|
+
structuredOutput: undefined,
|
|
1879
|
+
structuredOutputSchema: step.parallel.structuredOutputSchema ?? step.parallel.structuredOutput?.schema
|
|
1880
|
+
};
|
|
1881
|
+
});
|
|
1882
|
+
const dynamicFlatStepCount = Math.max(statusPayload.steps.length - 1 + dynamicSteps.length, 1);
|
|
1883
|
+
const dynamicStatusSteps = dynamicSteps.map((task, itemIndex) => {
|
|
1884
|
+
const transcriptPath = resolveAsyncStepTranscriptPath({ artifactsDir, artifactConfig, runId: id, agent: task.agent, flatIndex: groupStartFlatIndex + itemIndex, flatStepCount: dynamicFlatStepCount });
|
|
1885
|
+
return {
|
|
1886
|
+
agent: task.agent,
|
|
1887
|
+
phase: task.phase ?? step.phase,
|
|
1888
|
+
label: task.label,
|
|
1889
|
+
outputName: undefined,
|
|
1890
|
+
structured: Boolean(task.structuredOutputSchema),
|
|
1891
|
+
status: "pending",
|
|
1892
|
+
...task.sessionFile ? { sessionFile: task.sessionFile } : {},
|
|
1893
|
+
...transcriptPath ? { transcriptPath } : {},
|
|
1894
|
+
skills: task.skills,
|
|
1895
|
+
model: task.model,
|
|
1896
|
+
thinking: task.thinking,
|
|
1897
|
+
attemptedModels: task.modelCandidates && task.modelCandidates.length > 0 ? task.modelCandidates : task.model ? [task.model] : undefined,
|
|
1898
|
+
recentTools: [],
|
|
1899
|
+
recentOutput: []
|
|
1900
|
+
};
|
|
1901
|
+
});
|
|
1902
|
+
statusPayload.steps.splice(groupStartFlatIndex, 1, ...dynamicStatusSteps);
|
|
1903
|
+
if (config.childIntercomTargets) {
|
|
1904
|
+
config.childIntercomTargets = statusPayload.steps.map((statusStep, index) => resolveSubagentIntercomTarget(id, statusStep.agent, index));
|
|
1905
|
+
}
|
|
1906
|
+
mutatingFailureStates.splice(groupStartFlatIndex, 1, ...dynamicStatusSteps.map(() => createMutatingFailureState()));
|
|
1907
|
+
pendingToolResults.splice(groupStartFlatIndex, 1, ...dynamicStatusSteps.map(() => {
|
|
1908
|
+
return;
|
|
1909
|
+
}));
|
|
1910
|
+
const materializedDelta = dynamicStatusSteps.length - 1;
|
|
1911
|
+
for (const group of statusPayload.parallelGroups) {
|
|
1912
|
+
if (group.stepIndex === stepIndex) {
|
|
1913
|
+
group.start = groupStartFlatIndex;
|
|
1914
|
+
group.count = dynamicStatusSteps.length;
|
|
1915
|
+
} else if (group.start > groupStartFlatIndex) {
|
|
1916
|
+
group.start += materializedDelta;
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
if (statusPayload.workflowGraph) {
|
|
1920
|
+
const shiftFlatIndexes = (nodes) => {
|
|
1921
|
+
for (const node of nodes) {
|
|
1922
|
+
if (node.stepIndex !== undefined && node.stepIndex > stepIndex && node.flatIndex !== undefined && node.flatIndex >= groupStartFlatIndex) {
|
|
1923
|
+
node.flatIndex += dynamicStatusSteps.length;
|
|
1924
|
+
}
|
|
1925
|
+
if (node.children)
|
|
1926
|
+
shiftFlatIndexes(node.children);
|
|
1927
|
+
}
|
|
1928
|
+
};
|
|
1929
|
+
shiftFlatIndexes(statusPayload.workflowGraph.nodes);
|
|
1930
|
+
const groupNode = statusPayload.workflowGraph.nodes.find((node) => node.id === `step-${stepIndex}`);
|
|
1931
|
+
if (groupNode) {
|
|
1932
|
+
groupNode.children = materialized.items.map((item, itemIndex) => ({
|
|
1933
|
+
id: `step-${stepIndex}-item-${item.idKey}`,
|
|
1934
|
+
kind: "agent",
|
|
1935
|
+
agent: step.parallel.agent,
|
|
1936
|
+
phase: dynamicSteps[itemIndex]?.phase ?? step.phase,
|
|
1937
|
+
label: dynamicSteps[itemIndex]?.label?.trim() || `${step.parallel.agent} ${item.key}`,
|
|
1938
|
+
status: "pending",
|
|
1939
|
+
flatIndex: groupStartFlatIndex + itemIndex,
|
|
1940
|
+
stepIndex,
|
|
1941
|
+
itemKey: item.key,
|
|
1942
|
+
structured: Boolean(dynamicSteps[itemIndex]?.structuredOutputSchema)
|
|
1943
|
+
}));
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
writeStatusPayload();
|
|
1947
|
+
const concurrency = step.concurrency ?? MAX_PARALLEL_CONCURRENCY;
|
|
1948
|
+
const failFast = step.failFast ?? false;
|
|
1949
|
+
let aborted = false;
|
|
1950
|
+
const parallelResults = await mapConcurrent(dynamicSteps, concurrency, async (task, taskIdx) => {
|
|
1951
|
+
const fi = groupStartFlatIndex + taskIdx;
|
|
1952
|
+
if (timedOut)
|
|
1953
|
+
return timedOutStepResult(task.agent);
|
|
1954
|
+
if (interrupted)
|
|
1955
|
+
return pausedStepResult(task.agent);
|
|
1956
|
+
if (aborted && failFast) {
|
|
1957
|
+
const skippedAt = Date.now();
|
|
1958
|
+
statusPayload.steps[fi].status = "failed";
|
|
1959
|
+
statusPayload.steps[fi].error = "Skipped due to fail-fast";
|
|
1960
|
+
statusPayload.steps[fi].startedAt = skippedAt;
|
|
1961
|
+
statusPayload.steps[fi].endedAt = skippedAt;
|
|
1962
|
+
statusPayload.steps[fi].durationMs = 0;
|
|
1963
|
+
statusPayload.steps[fi].exitCode = -1;
|
|
1964
|
+
statusPayload.lastUpdate = skippedAt;
|
|
1965
|
+
writeStatusPayload();
|
|
1966
|
+
return { agent: task.agent, output: "(skipped — fail-fast)", exitCode: -1, skipped: true };
|
|
1967
|
+
}
|
|
1968
|
+
const taskStartTime = Date.now();
|
|
1969
|
+
statusPayload.currentStep = fi;
|
|
1970
|
+
statusPayload.steps[fi].status = "running";
|
|
1971
|
+
statusPayload.steps[fi].error = undefined;
|
|
1972
|
+
statusPayload.steps[fi].activityState = undefined;
|
|
1973
|
+
resetStepLiveDetail(statusPayload.steps[fi]);
|
|
1974
|
+
statusPayload.steps[fi].startedAt = taskStartTime;
|
|
1975
|
+
statusPayload.steps[fi].lastActivityAt = taskStartTime;
|
|
1976
|
+
statusPayload.outputFile = path.join(asyncDir, `output-${fi}.log`);
|
|
1977
|
+
statusPayload.lastActivityAt = taskStartTime;
|
|
1978
|
+
statusPayload.lastUpdate = taskStartTime;
|
|
1979
|
+
writeStatusPayload();
|
|
1980
|
+
appendJsonl(eventsPath, JSON.stringify({ type: "subagent.step.started", ts: taskStartTime, runId: id, stepIndex: fi, agent: task.agent }));
|
|
1981
|
+
flushPendingStepSteers(fi);
|
|
1982
|
+
const singleResult = await runSingleStep(task, {
|
|
1983
|
+
previousOutput,
|
|
1984
|
+
placeholder,
|
|
1985
|
+
cwd,
|
|
1986
|
+
sessionEnabled,
|
|
1987
|
+
outputs,
|
|
1988
|
+
sessionDir: config.sessionDir ? path.join(config.sessionDir, `dynamic-${stepIndex}-${taskIdx}`) : undefined,
|
|
1989
|
+
artifactsDir,
|
|
1990
|
+
artifactConfig,
|
|
1991
|
+
id,
|
|
1992
|
+
flatIndex: fi,
|
|
1993
|
+
flatStepCount: Math.max(statusPayload.steps.length, 1),
|
|
1994
|
+
outputFile: path.join(asyncDir, `output-${fi}.log`),
|
|
1995
|
+
steerInboxDir: stepSteerInboxDir(asyncDir, fi),
|
|
1996
|
+
piPackageRoot: config.piPackageRoot,
|
|
1997
|
+
piArgv1: config.piArgv1,
|
|
1998
|
+
childIntercomTarget: config.childIntercomTargets?.[fi],
|
|
1999
|
+
orchestratorIntercomTarget: config.controlIntercomTarget,
|
|
2000
|
+
nestedRoute: config.nestedRoute,
|
|
2001
|
+
registerInterrupt: (interrupt) => registerStepInterrupt(fi, interrupt),
|
|
2002
|
+
registerTimeout: (interrupt) => registerStepTimeout(fi, interrupt),
|
|
2003
|
+
registerTurnBudgetAbort: (abort) => registerStepTurnBudgetAbort(fi, abort),
|
|
2004
|
+
timeoutSignal: timeoutAbortController.signal,
|
|
2005
|
+
timeoutMessage,
|
|
2006
|
+
turnBudget: config.turnBudget,
|
|
2007
|
+
onAttemptStart: (attempt) => updateStepModel(fi, attempt.model, attempt.thinking),
|
|
2008
|
+
onChildEvent: (event) => updateStepFromChildEvent(fi, event),
|
|
2009
|
+
skipAcceptance: () => timedOut
|
|
2010
|
+
});
|
|
2011
|
+
const taskEndTime = Date.now();
|
|
2012
|
+
const childInterrupted = singleResult.interrupted === true;
|
|
2013
|
+
statusPayload.steps[fi].status = timedOut ? "failed" : childInterrupted ? "paused" : singleResult.exitCode === 0 ? "complete" : "failed";
|
|
2014
|
+
statusPayload.steps[fi].endedAt = taskEndTime;
|
|
2015
|
+
statusPayload.steps[fi].durationMs = taskEndTime - taskStartTime;
|
|
2016
|
+
statusPayload.steps[fi].exitCode = timedOut ? 1 : childInterrupted ? 0 : singleResult.exitCode;
|
|
2017
|
+
statusPayload.steps[fi].timedOut = timedOut || singleResult.timedOut ? true : undefined;
|
|
2018
|
+
statusPayload.steps[fi].turnBudget = singleResult.turnBudget;
|
|
2019
|
+
statusPayload.steps[fi].turnBudgetExceeded = singleResult.turnBudgetExceeded;
|
|
2020
|
+
statusPayload.steps[fi].wrapUpRequested = singleResult.wrapUpRequested;
|
|
2021
|
+
statusPayload.steps[fi].toolBudget = singleResult.toolBudget;
|
|
2022
|
+
statusPayload.steps[fi].toolBudgetBlocked = singleResult.toolBudgetBlocked;
|
|
2023
|
+
if (singleResult.toolBudget)
|
|
2024
|
+
statusPayload.toolBudget = singleResult.toolBudget;
|
|
2025
|
+
if (singleResult.toolBudgetBlocked)
|
|
2026
|
+
statusPayload.toolBudgetBlocked = true;
|
|
2027
|
+
if (singleResult.turnBudget)
|
|
2028
|
+
statusPayload.turnBudget = singleResult.turnBudget;
|
|
2029
|
+
if (singleResult.turnBudgetExceeded)
|
|
2030
|
+
statusPayload.turnBudgetExceeded = true;
|
|
2031
|
+
if (singleResult.wrapUpRequested)
|
|
2032
|
+
statusPayload.wrapUpRequested = true;
|
|
2033
|
+
statusPayload.steps[fi].model = singleResult.model;
|
|
2034
|
+
statusPayload.steps[fi].thinking = resolveEffectiveThinking(singleResult.model, statusPayload.steps[fi].thinking);
|
|
2035
|
+
statusPayload.steps[fi].attemptedModels = singleResult.attemptedModels;
|
|
2036
|
+
statusPayload.steps[fi].modelAttempts = singleResult.modelAttempts;
|
|
2037
|
+
statusPayload.steps[fi].totalCost = singleResult.totalCost;
|
|
2038
|
+
statusPayload.steps[fi].error = timedOut ? timeoutMessage ?? "Subagent timed out." : singleResult.error;
|
|
2039
|
+
statusPayload.steps[fi].transcriptPath = singleResult.transcriptPath ?? statusPayload.steps[fi].transcriptPath;
|
|
2040
|
+
statusPayload.steps[fi].transcriptError = singleResult.transcriptError;
|
|
2041
|
+
statusPayload.steps[fi].structuredOutput = singleResult.structuredOutput;
|
|
2042
|
+
statusPayload.steps[fi].structuredOutputPath = singleResult.structuredOutputPath;
|
|
2043
|
+
statusPayload.steps[fi].structuredOutputSchemaPath = singleResult.structuredOutputSchemaPath;
|
|
2044
|
+
statusPayload.steps[fi].acceptance = singleResult.acceptance;
|
|
2045
|
+
statusPayload.lastUpdate = taskEndTime;
|
|
2046
|
+
writeStatusPayload();
|
|
2047
|
+
appendJsonl(eventsPath, JSON.stringify({
|
|
2048
|
+
type: timedOut ? "subagent.step.failed" : childInterrupted ? "subagent.step.paused" : singleResult.exitCode === 0 ? "subagent.step.completed" : "subagent.step.failed",
|
|
2049
|
+
ts: taskEndTime,
|
|
2050
|
+
runId: id,
|
|
2051
|
+
stepIndex: fi,
|
|
2052
|
+
agent: task.agent,
|
|
2053
|
+
exitCode: timedOut ? 1 : childInterrupted ? 0 : singleResult.exitCode,
|
|
2054
|
+
durationMs: taskEndTime - taskStartTime
|
|
2055
|
+
}));
|
|
2056
|
+
if (singleResult.exitCode !== 0 && failFast)
|
|
2057
|
+
aborted = true;
|
|
2058
|
+
return timedOut ? { ...singleResult, output: timeoutMessage ?? "Subagent timed out.", error: timeoutMessage ?? "Subagent timed out.", exitCode: 1, interrupted: false, timedOut: true, skipped: false } : { ...singleResult, skipped: false };
|
|
2059
|
+
}, globalSemaphore);
|
|
2060
|
+
flatIndex += dynamicSteps.length;
|
|
2061
|
+
for (const pr of parallelResults) {
|
|
2062
|
+
results.push({
|
|
2063
|
+
agent: pr.agent,
|
|
2064
|
+
output: pr.output,
|
|
2065
|
+
error: pr.error,
|
|
2066
|
+
success: pr.interrupted !== true && pr.exitCode === 0,
|
|
2067
|
+
exitCode: pr.interrupted === true ? 0 : pr.exitCode,
|
|
2068
|
+
skipped: pr.skipped,
|
|
2069
|
+
interrupted: pr.interrupted,
|
|
2070
|
+
timedOut: pr.timedOut,
|
|
2071
|
+
turnBudget: pr.turnBudget,
|
|
2072
|
+
turnBudgetExceeded: pr.turnBudgetExceeded,
|
|
2073
|
+
wrapUpRequested: pr.wrapUpRequested,
|
|
2074
|
+
toolBudget: pr.toolBudget,
|
|
2075
|
+
toolBudgetBlocked: pr.toolBudgetBlocked,
|
|
2076
|
+
sessionFile: pr.sessionFile,
|
|
2077
|
+
intercomTarget: pr.intercomTarget,
|
|
2078
|
+
model: pr.model,
|
|
2079
|
+
attemptedModels: pr.attemptedModels,
|
|
2080
|
+
modelAttempts: pr.modelAttempts,
|
|
2081
|
+
totalCost: pr.totalCost,
|
|
2082
|
+
artifactPaths: pr.artifactPaths,
|
|
2083
|
+
transcriptPath: pr.transcriptPath,
|
|
2084
|
+
transcriptError: pr.transcriptError,
|
|
2085
|
+
structuredOutput: pr.structuredOutput,
|
|
2086
|
+
structuredOutputPath: pr.structuredOutputPath,
|
|
2087
|
+
structuredOutputSchemaPath: pr.structuredOutputSchemaPath,
|
|
2088
|
+
acceptance: pr.acceptance
|
|
2089
|
+
});
|
|
2090
|
+
}
|
|
2091
|
+
const collection = collectDynamicResults(step, materialized.items, parallelResults);
|
|
2092
|
+
const failures = parallelResults.filter((result) => result.exitCode !== 0 && result.exitCode !== -1);
|
|
2093
|
+
if (failures.length === 0) {
|
|
2094
|
+
try {
|
|
2095
|
+
validateDynamicCollection(step.collect.outputSchema, collection);
|
|
2096
|
+
outputs[step.collect.as] = {
|
|
2097
|
+
text: JSON.stringify(collection),
|
|
2098
|
+
structured: collection,
|
|
2099
|
+
agent: step.parallel.agent,
|
|
2100
|
+
stepIndex
|
|
2101
|
+
};
|
|
2102
|
+
statusPayload.outputs = outputs;
|
|
2103
|
+
const groupAcceptance = step.effectiveAcceptance && !timedOut ? await evaluateAcceptance({
|
|
2104
|
+
acceptance: step.effectiveAcceptance,
|
|
2105
|
+
output: "",
|
|
2106
|
+
report: aggregateAcceptanceReport({
|
|
2107
|
+
results: parallelResults,
|
|
2108
|
+
notes: `Dynamic fanout collected ${collection.length} result(s) into ${step.collect.as}.`
|
|
2109
|
+
}),
|
|
2110
|
+
cwd,
|
|
2111
|
+
signal: timeoutAbortController.signal,
|
|
2112
|
+
abortMessage: timeoutMessage ?? "Subagent timed out."
|
|
2113
|
+
}) : undefined;
|
|
2114
|
+
const groupTimedOut = timedOut || timeoutAbortController.signal.aborted;
|
|
2115
|
+
const effectiveGroupAcceptance = groupTimedOut ? undefined : groupAcceptance;
|
|
2116
|
+
const groupAcceptanceFailure = effectiveGroupAcceptance ? acceptanceFailureMessage(effectiveGroupAcceptance) : undefined;
|
|
2117
|
+
const groupError = groupTimedOut ? timeoutMessage ?? "Subagent timed out." : groupAcceptanceFailure;
|
|
2118
|
+
markDynamicGraphGroup(stepIndex, groupError ? "failed" : "completed", groupError, effectiveGroupAcceptance);
|
|
2119
|
+
if (groupError) {
|
|
2120
|
+
results.push({
|
|
2121
|
+
agent: step.parallel.agent,
|
|
2122
|
+
output: groupError,
|
|
2123
|
+
error: groupError,
|
|
2124
|
+
success: false,
|
|
2125
|
+
exitCode: 1,
|
|
2126
|
+
timedOut: groupTimedOut ? true : undefined,
|
|
2127
|
+
structuredOutput: collection,
|
|
2128
|
+
acceptance: effectiveGroupAcceptance
|
|
2129
|
+
});
|
|
2130
|
+
statusPayload.error = groupError;
|
|
2131
|
+
}
|
|
2132
|
+
} catch (error) {
|
|
2133
|
+
const message = error instanceof DynamicFanoutError ? error.message : error instanceof Error ? error.message : String(error);
|
|
2134
|
+
results.push({ agent: step.parallel.agent, output: message, error: message, success: false, exitCode: 1, structuredOutput: collection });
|
|
2135
|
+
statusPayload.error = message;
|
|
2136
|
+
markDynamicGraphGroup(stepIndex, "failed", message);
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
previousOutput = aggregateParallelOutputs(parallelResults.map((r, i) => ({
|
|
2140
|
+
agent: r.agent,
|
|
2141
|
+
taskIndex: i,
|
|
2142
|
+
output: r.output,
|
|
2143
|
+
exitCode: r.exitCode,
|
|
2144
|
+
error: r.error
|
|
2145
|
+
})), (i, agent) => `=== Dynamic Item ${i + 1} (${agent}, key ${materialized.items[i]?.key ?? i}) ===`);
|
|
2146
|
+
appendJsonl(eventsPath, JSON.stringify({
|
|
2147
|
+
type: "subagent.dynamic.completed",
|
|
2148
|
+
ts: Date.now(),
|
|
2149
|
+
runId: id,
|
|
2150
|
+
stepIndex,
|
|
2151
|
+
success: failures.length === 0
|
|
2152
|
+
}));
|
|
2153
|
+
if (failures.length > 0)
|
|
2154
|
+
markDynamicGraphGroup(stepIndex, "failed", failures[0]?.error ?? "Dynamic fanout child failed.");
|
|
2155
|
+
statusPayload.lastUpdate = Date.now();
|
|
2156
|
+
writeStatusPayload();
|
|
2157
|
+
if (failures.length > 0 || statusPayload.error)
|
|
2158
|
+
break;
|
|
2159
|
+
continue;
|
|
2160
|
+
}
|
|
2161
|
+
if (isParallelGroup(step)) {
|
|
2162
|
+
const group = step;
|
|
2163
|
+
const concurrency = group.concurrency ?? MAX_PARALLEL_CONCURRENCY;
|
|
2164
|
+
const failFast = group.failFast ?? false;
|
|
2165
|
+
const groupStartFlatIndex = flatIndex;
|
|
2166
|
+
let aborted = false;
|
|
2167
|
+
let worktreeSetup;
|
|
2168
|
+
if (group.worktree) {
|
|
2169
|
+
const worktreeTaskCwdConflict = findWorktreeTaskCwdConflict(group.parallel, cwd);
|
|
2170
|
+
if (worktreeTaskCwdConflict) {
|
|
2171
|
+
const failedAt = Date.now();
|
|
2172
|
+
markParallelGroupSetupFailure({
|
|
2173
|
+
statusPayload,
|
|
2174
|
+
results,
|
|
2175
|
+
group,
|
|
2176
|
+
groupStartFlatIndex,
|
|
2177
|
+
setupError: formatWorktreeTaskCwdConflict(worktreeTaskCwdConflict, cwd),
|
|
2178
|
+
failedAt,
|
|
2179
|
+
statusPath,
|
|
2180
|
+
eventsPath,
|
|
2181
|
+
asyncDir,
|
|
2182
|
+
runId: id,
|
|
2183
|
+
stepIndex
|
|
2184
|
+
});
|
|
2185
|
+
flatIndex += group.parallel.length;
|
|
2186
|
+
break;
|
|
2187
|
+
}
|
|
2188
|
+
try {
|
|
2189
|
+
worktreeSetup = createWorktrees(cwd, `${id}-s${stepIndex}`, group.parallel.length, {
|
|
2190
|
+
agents: group.parallel.map((task) => task.agent),
|
|
2191
|
+
setupHook: config.worktreeSetupHook ? { hookPath: config.worktreeSetupHook, timeoutMs: config.worktreeSetupHookTimeoutMs } : undefined,
|
|
2192
|
+
baseDir: config.worktreeBaseDir
|
|
2193
|
+
});
|
|
2194
|
+
} catch (error) {
|
|
2195
|
+
const setupError = error instanceof Error ? error.message : String(error);
|
|
2196
|
+
const failedAt = Date.now();
|
|
2197
|
+
markParallelGroupSetupFailure({
|
|
2198
|
+
statusPayload,
|
|
2199
|
+
results,
|
|
2200
|
+
group,
|
|
2201
|
+
groupStartFlatIndex,
|
|
2202
|
+
setupError,
|
|
2203
|
+
failedAt,
|
|
2204
|
+
statusPath,
|
|
2205
|
+
eventsPath,
|
|
2206
|
+
asyncDir,
|
|
2207
|
+
runId: id,
|
|
2208
|
+
stepIndex
|
|
2209
|
+
});
|
|
2210
|
+
flatIndex += group.parallel.length;
|
|
2211
|
+
break;
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2214
|
+
try {
|
|
2215
|
+
if (group.worktree)
|
|
2216
|
+
ensureParallelProgressFile(cwd, group);
|
|
2217
|
+
const groupStartTime = Date.now();
|
|
2218
|
+
markParallelGroupRunning({
|
|
2219
|
+
statusPayload,
|
|
2220
|
+
group,
|
|
2221
|
+
groupStartFlatIndex,
|
|
2222
|
+
groupStartTime,
|
|
2223
|
+
statusPath,
|
|
2224
|
+
eventsPath,
|
|
2225
|
+
asyncDir,
|
|
2226
|
+
runId: id,
|
|
2227
|
+
stepIndex
|
|
2228
|
+
});
|
|
2229
|
+
const parallelResults = await mapConcurrent(group.parallel, concurrency, async (task, taskIdx) => {
|
|
2230
|
+
const fi = groupStartFlatIndex + taskIdx;
|
|
2231
|
+
if (timedOut)
|
|
2232
|
+
return timedOutStepResult(task.agent);
|
|
2233
|
+
if (interrupted)
|
|
2234
|
+
return pausedStepResult(task.agent);
|
|
2235
|
+
if (aborted && failFast) {
|
|
2236
|
+
const skippedAt = Date.now();
|
|
2237
|
+
statusPayload.steps[fi].status = "failed";
|
|
2238
|
+
statusPayload.steps[fi].error = "Skipped due to fail-fast";
|
|
2239
|
+
statusPayload.steps[fi].startedAt = skippedAt;
|
|
2240
|
+
statusPayload.steps[fi].endedAt = skippedAt;
|
|
2241
|
+
statusPayload.steps[fi].durationMs = 0;
|
|
2242
|
+
statusPayload.steps[fi].exitCode = -1;
|
|
2243
|
+
statusPayload.steps[fi].activityState = undefined;
|
|
2244
|
+
statusPayload.lastUpdate = skippedAt;
|
|
2245
|
+
writeStatusPayload();
|
|
2246
|
+
appendJsonl(eventsPath, JSON.stringify({
|
|
2247
|
+
type: "subagent.step.failed",
|
|
2248
|
+
ts: skippedAt,
|
|
2249
|
+
runId: id,
|
|
2250
|
+
stepIndex: fi,
|
|
2251
|
+
agent: task.agent,
|
|
2252
|
+
exitCode: -1,
|
|
2253
|
+
durationMs: 0
|
|
2254
|
+
}));
|
|
2255
|
+
return { agent: task.agent, output: "(skipped — fail-fast)", exitCode: -1, skipped: true };
|
|
2256
|
+
}
|
|
2257
|
+
const taskStartTime = Date.now();
|
|
2258
|
+
statusPayload.currentStep = fi;
|
|
2259
|
+
statusPayload.steps[fi].status = "running";
|
|
2260
|
+
statusPayload.steps[fi].error = undefined;
|
|
2261
|
+
statusPayload.steps[fi].activityState = undefined;
|
|
2262
|
+
resetStepLiveDetail(statusPayload.steps[fi]);
|
|
2263
|
+
statusPayload.steps[fi].startedAt = taskStartTime;
|
|
2264
|
+
statusPayload.steps[fi].endedAt = undefined;
|
|
2265
|
+
statusPayload.steps[fi].durationMs = undefined;
|
|
2266
|
+
statusPayload.steps[fi].lastActivityAt = taskStartTime;
|
|
2267
|
+
statusPayload.outputFile = path.join(asyncDir, `output-${fi}.log`);
|
|
2268
|
+
statusPayload.lastActivityAt = taskStartTime;
|
|
2269
|
+
statusPayload.lastUpdate = taskStartTime;
|
|
2270
|
+
writeStatusPayload();
|
|
2271
|
+
appendJsonl(eventsPath, JSON.stringify({
|
|
2272
|
+
type: "subagent.step.started",
|
|
2273
|
+
ts: taskStartTime,
|
|
2274
|
+
runId: id,
|
|
2275
|
+
stepIndex: fi,
|
|
2276
|
+
agent: task.agent
|
|
2277
|
+
}));
|
|
2278
|
+
const taskSessionDir = config.sessionDir ? path.join(config.sessionDir, `parallel-${taskIdx}`) : undefined;
|
|
2279
|
+
const { taskForRun, taskCwd } = prepareParallelTaskRun(task, cwd, worktreeSetup, taskIdx);
|
|
2280
|
+
flushPendingStepSteers(fi);
|
|
2281
|
+
const singleResult = await runSingleStep(taskForRun, {
|
|
2282
|
+
previousOutput,
|
|
2283
|
+
placeholder,
|
|
2284
|
+
cwd: taskCwd,
|
|
2285
|
+
sessionEnabled,
|
|
2286
|
+
outputs,
|
|
2287
|
+
sessionDir: taskSessionDir,
|
|
2288
|
+
artifactsDir,
|
|
2289
|
+
artifactConfig,
|
|
2290
|
+
id,
|
|
2291
|
+
flatIndex: fi,
|
|
2292
|
+
flatStepCount: Math.max(statusPayload.steps.length, 1),
|
|
2293
|
+
outputFile: path.join(asyncDir, `output-${fi}.log`),
|
|
2294
|
+
steerInboxDir: stepSteerInboxDir(asyncDir, fi),
|
|
2295
|
+
piPackageRoot: config.piPackageRoot,
|
|
2296
|
+
piArgv1: config.piArgv1,
|
|
2297
|
+
childIntercomTarget: config.childIntercomTargets?.[fi],
|
|
2298
|
+
orchestratorIntercomTarget: config.controlIntercomTarget,
|
|
2299
|
+
nestedRoute: config.nestedRoute,
|
|
2300
|
+
registerInterrupt: (interrupt) => registerStepInterrupt(fi, interrupt),
|
|
2301
|
+
registerTimeout: (interrupt) => registerStepTimeout(fi, interrupt),
|
|
2302
|
+
registerTurnBudgetAbort: (abort) => registerStepTurnBudgetAbort(fi, abort),
|
|
2303
|
+
timeoutSignal: timeoutAbortController.signal,
|
|
2304
|
+
timeoutMessage,
|
|
2305
|
+
turnBudget: config.turnBudget,
|
|
2306
|
+
onAttemptStart: (attempt) => updateStepModel(fi, attempt.model, attempt.thinking),
|
|
2307
|
+
onChildEvent: (event) => updateStepFromChildEvent(fi, event),
|
|
2308
|
+
skipAcceptance: () => timedOut
|
|
2309
|
+
});
|
|
2310
|
+
if (task.sessionFile) {
|
|
2311
|
+
latestSessionFile = task.sessionFile;
|
|
2312
|
+
}
|
|
2313
|
+
const taskEndTime = Date.now();
|
|
2314
|
+
const taskDuration = taskEndTime - taskStartTime;
|
|
2315
|
+
const childInterrupted = singleResult.interrupted === true;
|
|
2316
|
+
statusPayload.steps[fi].status = timedOut ? "failed" : childInterrupted ? "paused" : singleResult.exitCode === 0 ? "complete" : "failed";
|
|
2317
|
+
statusPayload.steps[fi].endedAt = taskEndTime;
|
|
2318
|
+
statusPayload.steps[fi].durationMs = taskDuration;
|
|
2319
|
+
statusPayload.steps[fi].exitCode = timedOut ? 1 : childInterrupted ? 0 : singleResult.exitCode;
|
|
2320
|
+
statusPayload.steps[fi].timedOut = timedOut || singleResult.timedOut ? true : undefined;
|
|
2321
|
+
statusPayload.steps[fi].turnBudget = singleResult.turnBudget;
|
|
2322
|
+
statusPayload.steps[fi].turnBudgetExceeded = singleResult.turnBudgetExceeded;
|
|
2323
|
+
statusPayload.steps[fi].wrapUpRequested = singleResult.wrapUpRequested;
|
|
2324
|
+
statusPayload.steps[fi].toolBudget = singleResult.toolBudget;
|
|
2325
|
+
statusPayload.steps[fi].toolBudgetBlocked = singleResult.toolBudgetBlocked;
|
|
2326
|
+
if (singleResult.toolBudget)
|
|
2327
|
+
statusPayload.toolBudget = singleResult.toolBudget;
|
|
2328
|
+
if (singleResult.toolBudgetBlocked)
|
|
2329
|
+
statusPayload.toolBudgetBlocked = true;
|
|
2330
|
+
if (singleResult.turnBudget)
|
|
2331
|
+
statusPayload.turnBudget = singleResult.turnBudget;
|
|
2332
|
+
if (singleResult.turnBudgetExceeded)
|
|
2333
|
+
statusPayload.turnBudgetExceeded = true;
|
|
2334
|
+
if (singleResult.wrapUpRequested)
|
|
2335
|
+
statusPayload.wrapUpRequested = true;
|
|
2336
|
+
statusPayload.steps[fi].model = singleResult.model;
|
|
2337
|
+
statusPayload.steps[fi].thinking = resolveEffectiveThinking(singleResult.model, statusPayload.steps[fi].thinking);
|
|
2338
|
+
statusPayload.steps[fi].attemptedModels = singleResult.attemptedModels;
|
|
2339
|
+
statusPayload.steps[fi].modelAttempts = singleResult.modelAttempts;
|
|
2340
|
+
statusPayload.steps[fi].totalCost = singleResult.totalCost;
|
|
2341
|
+
statusPayload.steps[fi].error = timedOut ? timeoutMessage ?? "Subagent timed out." : singleResult.error;
|
|
2342
|
+
statusPayload.steps[fi].transcriptPath = singleResult.transcriptPath ?? statusPayload.steps[fi].transcriptPath;
|
|
2343
|
+
statusPayload.steps[fi].transcriptError = singleResult.transcriptError;
|
|
2344
|
+
statusPayload.steps[fi].structuredOutput = singleResult.structuredOutput;
|
|
2345
|
+
statusPayload.steps[fi].structuredOutputPath = singleResult.structuredOutputPath;
|
|
2346
|
+
statusPayload.steps[fi].structuredOutputSchemaPath = singleResult.structuredOutputSchemaPath;
|
|
2347
|
+
statusPayload.steps[fi].acceptance = singleResult.acceptance;
|
|
2348
|
+
statusPayload.lastUpdate = taskEndTime;
|
|
2349
|
+
writeStatusPayload();
|
|
2350
|
+
appendJsonl(eventsPath, JSON.stringify({
|
|
2351
|
+
type: timedOut ? "subagent.step.failed" : childInterrupted ? "subagent.step.paused" : singleResult.exitCode === 0 ? "subagent.step.completed" : "subagent.step.failed",
|
|
2352
|
+
ts: taskEndTime,
|
|
2353
|
+
runId: id,
|
|
2354
|
+
stepIndex: fi,
|
|
2355
|
+
agent: task.agent,
|
|
2356
|
+
exitCode: timedOut ? 1 : childInterrupted ? 0 : singleResult.exitCode,
|
|
2357
|
+
durationMs: taskDuration
|
|
2358
|
+
}));
|
|
2359
|
+
if (singleResult.completionGuardTriggered) {
|
|
2360
|
+
const event = buildControlEvent({
|
|
2361
|
+
from: statusPayload.steps[fi].activityState,
|
|
2362
|
+
to: "needs_attention",
|
|
2363
|
+
runId: id,
|
|
2364
|
+
agent: task.agent,
|
|
2365
|
+
index: fi,
|
|
2366
|
+
ts: taskEndTime,
|
|
2367
|
+
message: `${task.agent} completed without making edits for an implementation task`,
|
|
2368
|
+
reason: "completion_guard"
|
|
2369
|
+
});
|
|
2370
|
+
appendControlEvent(event);
|
|
2371
|
+
}
|
|
2372
|
+
if (singleResult.exitCode !== 0 && failFast)
|
|
2373
|
+
aborted = true;
|
|
2374
|
+
return timedOut ? { ...singleResult, output: timeoutMessage ?? "Subagent timed out.", error: timeoutMessage ?? "Subagent timed out.", exitCode: 1, interrupted: false, timedOut: true, skipped: false } : { ...singleResult, skipped: false };
|
|
2375
|
+
}, globalSemaphore);
|
|
2376
|
+
flatIndex += group.parallel.length;
|
|
2377
|
+
for (let t = 0;t < group.parallel.length; t++) {
|
|
2378
|
+
const fi = groupStartFlatIndex + t;
|
|
2379
|
+
const sessionTokens = config.sessionDir ? parseSessionTokens(path.join(config.sessionDir, `parallel-${t}`)) : null;
|
|
2380
|
+
const taskTokens = sessionTokens ?? tokenUsageFromAttempts(parallelResults[t]?.modelAttempts);
|
|
2381
|
+
if (!taskTokens)
|
|
2382
|
+
continue;
|
|
2383
|
+
statusPayload.steps[fi].tokens = taskTokens;
|
|
2384
|
+
previousCumulativeTokens = {
|
|
2385
|
+
input: previousCumulativeTokens.input + taskTokens.input,
|
|
2386
|
+
output: previousCumulativeTokens.output + taskTokens.output,
|
|
2387
|
+
total: previousCumulativeTokens.total + taskTokens.total
|
|
2388
|
+
};
|
|
2389
|
+
}
|
|
2390
|
+
statusPayload.totalTokens = { ...previousCumulativeTokens };
|
|
2391
|
+
statusPayload.lastUpdate = Date.now();
|
|
2392
|
+
writeStatusPayload();
|
|
2393
|
+
for (const pr of parallelResults) {
|
|
2394
|
+
results.push({
|
|
2395
|
+
agent: pr.agent,
|
|
2396
|
+
output: pr.output,
|
|
2397
|
+
error: pr.error,
|
|
2398
|
+
success: pr.interrupted !== true && pr.exitCode === 0,
|
|
2399
|
+
exitCode: pr.interrupted === true ? 0 : pr.exitCode,
|
|
2400
|
+
skipped: pr.skipped,
|
|
2401
|
+
interrupted: pr.interrupted,
|
|
2402
|
+
timedOut: pr.timedOut,
|
|
2403
|
+
turnBudget: pr.turnBudget,
|
|
2404
|
+
turnBudgetExceeded: pr.turnBudgetExceeded,
|
|
2405
|
+
wrapUpRequested: pr.wrapUpRequested,
|
|
2406
|
+
toolBudget: pr.toolBudget,
|
|
2407
|
+
toolBudgetBlocked: pr.toolBudgetBlocked,
|
|
2408
|
+
sessionFile: pr.sessionFile,
|
|
2409
|
+
intercomTarget: pr.intercomTarget,
|
|
2410
|
+
model: pr.model,
|
|
2411
|
+
attemptedModels: pr.attemptedModels,
|
|
2412
|
+
modelAttempts: pr.modelAttempts,
|
|
2413
|
+
totalCost: pr.totalCost,
|
|
2414
|
+
artifactPaths: pr.artifactPaths,
|
|
2415
|
+
transcriptPath: pr.transcriptPath,
|
|
2416
|
+
transcriptError: pr.transcriptError,
|
|
2417
|
+
structuredOutput: pr.structuredOutput,
|
|
2418
|
+
structuredOutputPath: pr.structuredOutputPath,
|
|
2419
|
+
structuredOutputSchemaPath: pr.structuredOutputSchemaPath,
|
|
2420
|
+
acceptance: pr.acceptance
|
|
2421
|
+
});
|
|
2422
|
+
}
|
|
2423
|
+
for (let t = 0;t < group.parallel.length; t++) {
|
|
2424
|
+
const outputName = group.parallel[t]?.outputName;
|
|
2425
|
+
if (outputName)
|
|
2426
|
+
outputs[outputName] = outputEntryFromAsyncResult({
|
|
2427
|
+
agent: parallelResults[t].agent,
|
|
2428
|
+
output: parallelResults[t].output,
|
|
2429
|
+
structuredOutput: parallelResults[t].structuredOutput
|
|
2430
|
+
}, stepIndex);
|
|
2431
|
+
}
|
|
2432
|
+
statusPayload.outputs = outputs;
|
|
2433
|
+
previousOutput = aggregateParallelOutputs(parallelResults.map((r) => ({
|
|
2434
|
+
agent: r.agent,
|
|
2435
|
+
output: r.output,
|
|
2436
|
+
exitCode: r.exitCode,
|
|
2437
|
+
error: r.error,
|
|
2438
|
+
model: r.model,
|
|
2439
|
+
attemptedModels: r.attemptedModels
|
|
2440
|
+
})));
|
|
2441
|
+
previousOutput = appendParallelWorktreeSummary(previousOutput, worktreeSetup, asyncDir, stepIndex, group);
|
|
2442
|
+
appendJsonl(eventsPath, JSON.stringify({
|
|
2443
|
+
type: "subagent.parallel.completed",
|
|
2444
|
+
ts: Date.now(),
|
|
2445
|
+
runId: id,
|
|
2446
|
+
stepIndex,
|
|
2447
|
+
success: parallelResults.every((r) => r.exitCode === 0 || r.exitCode === -1)
|
|
2448
|
+
}));
|
|
2449
|
+
if (parallelResults.some((r) => r.exitCode !== 0 && r.exitCode !== -1)) {
|
|
2450
|
+
break;
|
|
2451
|
+
}
|
|
2452
|
+
} finally {
|
|
2453
|
+
if (worktreeSetup)
|
|
2454
|
+
cleanupWorktrees(worktreeSetup);
|
|
2455
|
+
}
|
|
2456
|
+
} else {
|
|
2457
|
+
const seqStep = step;
|
|
2458
|
+
const stepStartTime = Date.now();
|
|
2459
|
+
statusPayload.currentStep = flatIndex;
|
|
2460
|
+
statusPayload.steps[flatIndex].status = "running";
|
|
2461
|
+
statusPayload.steps[flatIndex].activityState = undefined;
|
|
2462
|
+
statusPayload.activityState = undefined;
|
|
2463
|
+
resetStepLiveDetail(statusPayload.steps[flatIndex]);
|
|
2464
|
+
statusPayload.steps[flatIndex].skills = seqStep.skills;
|
|
2465
|
+
statusPayload.steps[flatIndex].startedAt = stepStartTime;
|
|
2466
|
+
statusPayload.steps[flatIndex].lastActivityAt = stepStartTime;
|
|
2467
|
+
statusPayload.lastActivityAt = stepStartTime;
|
|
2468
|
+
statusPayload.lastUpdate = stepStartTime;
|
|
2469
|
+
statusPayload.outputFile = path.join(asyncDir, `output-${flatIndex}.log`);
|
|
2470
|
+
writeStatusPayload();
|
|
2471
|
+
appendJsonl(eventsPath, JSON.stringify({
|
|
2472
|
+
type: "subagent.step.started",
|
|
2473
|
+
ts: stepStartTime,
|
|
2474
|
+
runId: id,
|
|
2475
|
+
stepIndex: flatIndex,
|
|
2476
|
+
agent: seqStep.agent
|
|
2477
|
+
}));
|
|
2478
|
+
flushPendingStepSteers(flatIndex);
|
|
2479
|
+
const singleResult = await runSingleStep(seqStep, {
|
|
2480
|
+
previousOutput,
|
|
2481
|
+
placeholder,
|
|
2482
|
+
cwd,
|
|
2483
|
+
sessionEnabled,
|
|
2484
|
+
outputs,
|
|
2485
|
+
sessionDir: config.sessionDir,
|
|
2486
|
+
artifactsDir,
|
|
2487
|
+
artifactConfig,
|
|
2488
|
+
id,
|
|
2489
|
+
flatIndex,
|
|
2490
|
+
flatStepCount: Math.max(statusPayload.steps.length, 1),
|
|
2491
|
+
outputFile: path.join(asyncDir, `output-${flatIndex}.log`),
|
|
2492
|
+
steerInboxDir: stepSteerInboxDir(asyncDir, flatIndex),
|
|
2493
|
+
piPackageRoot: config.piPackageRoot,
|
|
2494
|
+
piArgv1: config.piArgv1,
|
|
2495
|
+
childIntercomTarget: config.childIntercomTargets?.[flatIndex],
|
|
2496
|
+
orchestratorIntercomTarget: config.controlIntercomTarget,
|
|
2497
|
+
nestedRoute: config.nestedRoute,
|
|
2498
|
+
registerInterrupt: (interrupt) => registerStepInterrupt(flatIndex, interrupt),
|
|
2499
|
+
registerTimeout: (interrupt) => registerStepTimeout(flatIndex, interrupt),
|
|
2500
|
+
registerTurnBudgetAbort: (abort) => registerStepTurnBudgetAbort(flatIndex, abort),
|
|
2501
|
+
timeoutSignal: timeoutAbortController.signal,
|
|
2502
|
+
timeoutMessage,
|
|
2503
|
+
turnBudget: config.turnBudget,
|
|
2504
|
+
onAttemptStart: (attempt) => updateStepModel(flatIndex, attempt.model, attempt.thinking),
|
|
2505
|
+
onChildEvent: (event) => updateStepFromChildEvent(flatIndex, event),
|
|
2506
|
+
skipAcceptance: () => timedOut
|
|
2507
|
+
});
|
|
2508
|
+
if (seqStep.sessionFile) {
|
|
2509
|
+
latestSessionFile = seqStep.sessionFile;
|
|
2510
|
+
}
|
|
2511
|
+
previousOutput = singleResult.output;
|
|
2512
|
+
results.push({
|
|
2513
|
+
agent: singleResult.agent,
|
|
2514
|
+
output: timedOut ? timeoutMessage ?? "Subagent timed out." : singleResult.output,
|
|
2515
|
+
error: timedOut ? timeoutMessage ?? "Subagent timed out." : singleResult.error,
|
|
2516
|
+
success: !timedOut && singleResult.interrupted !== true && singleResult.exitCode === 0,
|
|
2517
|
+
exitCode: timedOut ? 1 : singleResult.interrupted === true ? 0 : singleResult.exitCode,
|
|
2518
|
+
sessionFile: singleResult.sessionFile,
|
|
2519
|
+
intercomTarget: singleResult.intercomTarget,
|
|
2520
|
+
model: singleResult.model,
|
|
2521
|
+
attemptedModels: singleResult.attemptedModels,
|
|
2522
|
+
modelAttempts: singleResult.modelAttempts,
|
|
2523
|
+
totalCost: singleResult.totalCost,
|
|
2524
|
+
artifactPaths: singleResult.artifactPaths,
|
|
2525
|
+
transcriptPath: singleResult.transcriptPath,
|
|
2526
|
+
transcriptError: singleResult.transcriptError,
|
|
2527
|
+
structuredOutput: singleResult.structuredOutput,
|
|
2528
|
+
structuredOutputPath: singleResult.structuredOutputPath,
|
|
2529
|
+
structuredOutputSchemaPath: singleResult.structuredOutputSchemaPath,
|
|
2530
|
+
acceptance: singleResult.acceptance,
|
|
2531
|
+
interrupted: singleResult.interrupted,
|
|
2532
|
+
timedOut: timedOut || singleResult.timedOut ? true : undefined,
|
|
2533
|
+
turnBudget: singleResult.turnBudget,
|
|
2534
|
+
turnBudgetExceeded: singleResult.turnBudgetExceeded,
|
|
2535
|
+
wrapUpRequested: singleResult.wrapUpRequested,
|
|
2536
|
+
toolBudget: singleResult.toolBudget,
|
|
2537
|
+
toolBudgetBlocked: singleResult.toolBudgetBlocked
|
|
2538
|
+
});
|
|
2539
|
+
if (seqStep.outputName) {
|
|
2540
|
+
outputs[seqStep.outputName] = outputEntryFromAsyncResult({
|
|
2541
|
+
agent: singleResult.agent,
|
|
2542
|
+
output: singleResult.output,
|
|
2543
|
+
structuredOutput: singleResult.structuredOutput
|
|
2544
|
+
}, stepIndex);
|
|
2545
|
+
}
|
|
2546
|
+
statusPayload.outputs = outputs;
|
|
2547
|
+
const cumulativeTokens = config.sessionDir ? parseSessionTokens(config.sessionDir) : null;
|
|
2548
|
+
let stepTokens = cumulativeTokens ? {
|
|
2549
|
+
input: cumulativeTokens.input - previousCumulativeTokens.input,
|
|
2550
|
+
output: cumulativeTokens.output - previousCumulativeTokens.output,
|
|
2551
|
+
total: cumulativeTokens.total - previousCumulativeTokens.total
|
|
2552
|
+
} : null;
|
|
2553
|
+
if (cumulativeTokens) {
|
|
2554
|
+
previousCumulativeTokens = cumulativeTokens;
|
|
2555
|
+
} else {
|
|
2556
|
+
stepTokens = tokenUsageFromAttempts(singleResult.modelAttempts);
|
|
2557
|
+
if (stepTokens) {
|
|
2558
|
+
previousCumulativeTokens = {
|
|
2559
|
+
input: previousCumulativeTokens.input + stepTokens.input,
|
|
2560
|
+
output: previousCumulativeTokens.output + stepTokens.output,
|
|
2561
|
+
total: previousCumulativeTokens.total + stepTokens.total
|
|
2562
|
+
};
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
2565
|
+
const stepEndTime = Date.now();
|
|
2566
|
+
const childInterrupted = singleResult.interrupted === true;
|
|
2567
|
+
statusPayload.steps[flatIndex].status = timedOut ? "failed" : childInterrupted ? "paused" : singleResult.exitCode === 0 ? "complete" : "failed";
|
|
2568
|
+
statusPayload.steps[flatIndex].endedAt = stepEndTime;
|
|
2569
|
+
statusPayload.steps[flatIndex].durationMs = stepEndTime - stepStartTime;
|
|
2570
|
+
statusPayload.steps[flatIndex].exitCode = timedOut ? 1 : childInterrupted ? 0 : singleResult.exitCode;
|
|
2571
|
+
statusPayload.steps[flatIndex].timedOut = timedOut || singleResult.timedOut ? true : undefined;
|
|
2572
|
+
statusPayload.steps[flatIndex].turnBudget = singleResult.turnBudget;
|
|
2573
|
+
statusPayload.steps[flatIndex].turnBudgetExceeded = singleResult.turnBudgetExceeded;
|
|
2574
|
+
statusPayload.steps[flatIndex].wrapUpRequested = singleResult.wrapUpRequested;
|
|
2575
|
+
statusPayload.steps[flatIndex].toolBudget = singleResult.toolBudget;
|
|
2576
|
+
statusPayload.steps[flatIndex].toolBudgetBlocked = singleResult.toolBudgetBlocked;
|
|
2577
|
+
if (singleResult.toolBudget)
|
|
2578
|
+
statusPayload.toolBudget = singleResult.toolBudget;
|
|
2579
|
+
if (singleResult.toolBudgetBlocked)
|
|
2580
|
+
statusPayload.toolBudgetBlocked = true;
|
|
2581
|
+
if (singleResult.turnBudget)
|
|
2582
|
+
statusPayload.turnBudget = singleResult.turnBudget;
|
|
2583
|
+
if (singleResult.turnBudgetExceeded)
|
|
2584
|
+
statusPayload.turnBudgetExceeded = true;
|
|
2585
|
+
if (singleResult.wrapUpRequested)
|
|
2586
|
+
statusPayload.wrapUpRequested = true;
|
|
2587
|
+
statusPayload.steps[flatIndex].model = singleResult.model;
|
|
2588
|
+
statusPayload.steps[flatIndex].thinking = resolveEffectiveThinking(singleResult.model, statusPayload.steps[flatIndex].thinking);
|
|
2589
|
+
statusPayload.steps[flatIndex].attemptedModels = singleResult.attemptedModels;
|
|
2590
|
+
statusPayload.steps[flatIndex].modelAttempts = singleResult.modelAttempts;
|
|
2591
|
+
statusPayload.steps[flatIndex].totalCost = singleResult.totalCost;
|
|
2592
|
+
statusPayload.steps[flatIndex].error = timedOut ? timeoutMessage ?? "Subagent timed out." : singleResult.error;
|
|
2593
|
+
statusPayload.steps[flatIndex].transcriptPath = singleResult.transcriptPath ?? statusPayload.steps[flatIndex].transcriptPath;
|
|
2594
|
+
statusPayload.steps[flatIndex].transcriptError = singleResult.transcriptError;
|
|
2595
|
+
statusPayload.steps[flatIndex].structuredOutput = singleResult.structuredOutput;
|
|
2596
|
+
statusPayload.steps[flatIndex].structuredOutputPath = singleResult.structuredOutputPath;
|
|
2597
|
+
statusPayload.steps[flatIndex].structuredOutputSchemaPath = singleResult.structuredOutputSchemaPath;
|
|
2598
|
+
statusPayload.steps[flatIndex].acceptance = singleResult.acceptance;
|
|
2599
|
+
if (stepTokens) {
|
|
2600
|
+
statusPayload.steps[flatIndex].tokens = stepTokens;
|
|
2601
|
+
statusPayload.totalTokens = { ...previousCumulativeTokens };
|
|
2602
|
+
}
|
|
2603
|
+
statusPayload.lastUpdate = stepEndTime;
|
|
2604
|
+
writeStatusPayload();
|
|
2605
|
+
appendJsonl(eventsPath, JSON.stringify({
|
|
2606
|
+
type: timedOut ? "subagent.step.failed" : childInterrupted ? "subagent.step.paused" : singleResult.exitCode === 0 ? "subagent.step.completed" : "subagent.step.failed",
|
|
2607
|
+
ts: stepEndTime,
|
|
2608
|
+
runId: id,
|
|
2609
|
+
stepIndex: flatIndex,
|
|
2610
|
+
agent: seqStep.agent,
|
|
2611
|
+
exitCode: timedOut ? 1 : childInterrupted ? 0 : singleResult.exitCode,
|
|
2612
|
+
durationMs: stepEndTime - stepStartTime,
|
|
2613
|
+
tokens: stepTokens
|
|
2614
|
+
}));
|
|
2615
|
+
if (singleResult.completionGuardTriggered) {
|
|
2616
|
+
const event = buildControlEvent({
|
|
2617
|
+
from: statusPayload.steps[flatIndex].activityState,
|
|
2618
|
+
to: "needs_attention",
|
|
2619
|
+
runId: id,
|
|
2620
|
+
agent: seqStep.agent,
|
|
2621
|
+
index: flatIndex,
|
|
2622
|
+
ts: stepEndTime,
|
|
2623
|
+
message: `${seqStep.agent} completed without making edits for an implementation task`,
|
|
2624
|
+
reason: "completion_guard"
|
|
2625
|
+
});
|
|
2626
|
+
appendControlEvent(event);
|
|
2627
|
+
}
|
|
2628
|
+
flatIndex++;
|
|
2629
|
+
if (singleResult.exitCode !== 0) {
|
|
2630
|
+
break;
|
|
2631
|
+
}
|
|
2632
|
+
}
|
|
2633
|
+
}
|
|
2634
|
+
let summary = results.map((r) => {
|
|
2635
|
+
const output = r.output.trim();
|
|
2636
|
+
const detail = output ? r.error ? `${output}
|
|
76
2637
|
|
|
77
|
-
Error: ${
|
|
78
|
-
|
|
2638
|
+
Error: ${r.error}` : output : r.error ?? "(no output)";
|
|
2639
|
+
return `${r.agent}:
|
|
2640
|
+
${detail}`;
|
|
2641
|
+
}).join(`
|
|
79
2642
|
|
|
80
|
-
`)
|
|
2643
|
+
`);
|
|
2644
|
+
let truncated = false;
|
|
2645
|
+
if (maxOutput) {
|
|
2646
|
+
const config = { ...DEFAULT_MAX_OUTPUT, ...maxOutput };
|
|
2647
|
+
const lastArtifactPath = results[results.length - 1]?.artifactPaths?.outputPath;
|
|
2648
|
+
const truncResult = truncateOutput(summary, config, lastArtifactPath);
|
|
2649
|
+
if (truncResult.truncated) {
|
|
2650
|
+
summary = truncResult.text;
|
|
2651
|
+
truncated = true;
|
|
2652
|
+
}
|
|
2653
|
+
}
|
|
2654
|
+
const resultMode = config.resultMode ?? statusPayload.mode;
|
|
2655
|
+
const totalCost = results.reduce((sum, result) => ({
|
|
2656
|
+
inputTokens: sum.inputTokens + (result.totalCost?.inputTokens ?? 0),
|
|
2657
|
+
outputTokens: sum.outputTokens + (result.totalCost?.outputTokens ?? 0),
|
|
2658
|
+
costUsd: sum.costUsd + (result.totalCost?.costUsd ?? 0)
|
|
2659
|
+
}), { inputTokens: 0, outputTokens: 0, costUsd: 0 });
|
|
2660
|
+
const finalTotalCost = totalCost.inputTokens > 0 || totalCost.outputTokens > 0 || totalCost.costUsd > 0 ? totalCost : undefined;
|
|
2661
|
+
const finalFlatAgents = statusPayload.steps.map((step) => step.agent);
|
|
2662
|
+
const agentName = finalFlatAgents.length === 1 ? finalFlatAgents[0] : resultMode === "parallel" ? `parallel:${finalFlatAgents.join("+")}` : `chain:${finalFlatAgents.join("->")}`;
|
|
2663
|
+
let sessionFile;
|
|
2664
|
+
let shareUrl;
|
|
2665
|
+
let gistUrl;
|
|
2666
|
+
let shareError;
|
|
2667
|
+
if (shareEnabled) {
|
|
2668
|
+
sessionFile = config.sessionDir ? findLatestSessionFile(config.sessionDir) ?? undefined : undefined;
|
|
2669
|
+
if (!sessionFile && latestSessionFile) {
|
|
2670
|
+
sessionFile = latestSessionFile;
|
|
2671
|
+
}
|
|
2672
|
+
if (sessionFile) {
|
|
2673
|
+
try {
|
|
2674
|
+
const exportDir = config.sessionDir ?? path.dirname(sessionFile);
|
|
2675
|
+
const htmlPath = await exportSessionHtml(sessionFile, exportDir, config.piPackageRoot);
|
|
2676
|
+
const share = createShareLink(htmlPath);
|
|
2677
|
+
if ("error" in share)
|
|
2678
|
+
shareError = share.error;
|
|
2679
|
+
else {
|
|
2680
|
+
shareUrl = share.shareUrl;
|
|
2681
|
+
gistUrl = share.gistUrl;
|
|
2682
|
+
}
|
|
2683
|
+
} catch (err) {
|
|
2684
|
+
shareError = String(err);
|
|
2685
|
+
}
|
|
2686
|
+
} else {
|
|
2687
|
+
shareError = "Session file not found.";
|
|
2688
|
+
}
|
|
2689
|
+
}
|
|
2690
|
+
if (activityTimer) {
|
|
2691
|
+
clearInterval(activityTimer);
|
|
2692
|
+
activityTimer = undefined;
|
|
2693
|
+
}
|
|
2694
|
+
if (timeoutTimer) {
|
|
2695
|
+
clearTimeout(timeoutTimer);
|
|
2696
|
+
timeoutTimer = undefined;
|
|
2697
|
+
}
|
|
2698
|
+
disposeControlInbox();
|
|
2699
|
+
const effectiveSessionFile = sessionFile ?? latestSessionFile;
|
|
2700
|
+
const runEndedAt = Date.now();
|
|
2701
|
+
statusPayload.state = timedOut || turnBudgetExceeded ? "failed" : interrupted ? "paused" : results.every((r) => r.success) ? "complete" : "failed";
|
|
2702
|
+
statusPayload.activityState = undefined;
|
|
2703
|
+
if (timedOut) {
|
|
2704
|
+
statusPayload.timedOut = true;
|
|
2705
|
+
statusPayload.error = timeoutMessage ?? "Subagent timed out.";
|
|
2706
|
+
}
|
|
2707
|
+
if (turnBudgetExceeded && !statusPayload.error) {
|
|
2708
|
+
const budget = statusPayload.turnBudget;
|
|
2709
|
+
statusPayload.error = budget ? turnBudgetExceededMessage(budget, budget.turnCount) : "Subagent exceeded turn budget.";
|
|
2710
|
+
}
|
|
2711
|
+
statusPayload.endedAt = runEndedAt;
|
|
2712
|
+
statusPayload.lastUpdate = runEndedAt;
|
|
2713
|
+
statusPayload.sessionFile = effectiveSessionFile;
|
|
2714
|
+
statusPayload.totalCost = finalTotalCost;
|
|
2715
|
+
statusPayload.shareUrl = shareUrl;
|
|
2716
|
+
statusPayload.gistUrl = gistUrl;
|
|
2717
|
+
statusPayload.shareError = shareError;
|
|
2718
|
+
if (statusPayload.state === "failed" && !statusPayload.error) {
|
|
2719
|
+
const failedStep = statusPayload.steps.find((s) => s.status === "failed");
|
|
2720
|
+
if (failedStep?.agent) {
|
|
2721
|
+
statusPayload.error = failedStep.error ? `Step failed: ${failedStep.agent}: ${failedStep.error}` : `Step failed: ${failedStep.agent}`;
|
|
2722
|
+
}
|
|
2723
|
+
}
|
|
2724
|
+
writeStatusPayload();
|
|
2725
|
+
appendJsonl(eventsPath, JSON.stringify({
|
|
2726
|
+
type: "subagent.run.completed",
|
|
2727
|
+
lifecycleArtifactVersion: SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
|
|
2728
|
+
ts: runEndedAt,
|
|
2729
|
+
runId: id,
|
|
2730
|
+
status: statusPayload.state,
|
|
2731
|
+
durationMs: runEndedAt - overallStartTime,
|
|
2732
|
+
totalTokens: statusPayload.totalTokens,
|
|
2733
|
+
totalCost: finalTotalCost
|
|
2734
|
+
}));
|
|
2735
|
+
writeRunLog(logPath, {
|
|
2736
|
+
id,
|
|
2737
|
+
mode: statusPayload.mode,
|
|
2738
|
+
cwd,
|
|
2739
|
+
startedAt: overallStartTime,
|
|
2740
|
+
endedAt: runEndedAt,
|
|
2741
|
+
steps: statusPayload.steps.map((step) => ({
|
|
2742
|
+
agent: step.agent,
|
|
2743
|
+
status: step.status,
|
|
2744
|
+
durationMs: step.durationMs
|
|
2745
|
+
})),
|
|
2746
|
+
summary,
|
|
2747
|
+
truncated,
|
|
2748
|
+
artifactsDir,
|
|
2749
|
+
sessionFile: effectiveSessionFile,
|
|
2750
|
+
shareUrl,
|
|
2751
|
+
shareError
|
|
2752
|
+
});
|
|
2753
|
+
try {
|
|
2754
|
+
writeAtomicJson(resultPath, {
|
|
2755
|
+
lifecycleArtifactVersion: SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
|
|
2756
|
+
id,
|
|
2757
|
+
agent: agentName,
|
|
2758
|
+
mode: resultMode,
|
|
2759
|
+
success: !timedOut && !turnBudgetExceeded && !interrupted && results.every((r) => r.success),
|
|
2760
|
+
state: timedOut || turnBudgetExceeded ? "failed" : interrupted ? "paused" : results.every((r) => r.success) ? "complete" : "failed",
|
|
2761
|
+
summary: timedOut ? timeoutMessage ?? "Subagent timed out." : turnBudgetExceeded ? statusPayload.error ?? "Subagent exceeded turn budget." : interrupted ? "Paused after interrupt. Waiting for explicit next action." : summary,
|
|
2762
|
+
...config.timeoutMs !== undefined ? { timeoutMs: config.timeoutMs } : {},
|
|
2763
|
+
...config.deadlineAt !== undefined ? { deadlineAt: config.deadlineAt } : {},
|
|
2764
|
+
...statusPayload.turnBudget ? { turnBudget: statusPayload.turnBudget } : {},
|
|
2765
|
+
...statusPayload.turnBudgetExceeded ? { turnBudgetExceeded: true } : {},
|
|
2766
|
+
...statusPayload.wrapUpRequested ? { wrapUpRequested: true } : {},
|
|
2767
|
+
...statusPayload.toolBudget ? { toolBudget: statusPayload.toolBudget } : {},
|
|
2768
|
+
...statusPayload.toolBudgetBlocked ? { toolBudgetBlocked: true } : {},
|
|
2769
|
+
...timedOut ? { timedOut: true, error: timeoutMessage ?? "Subagent timed out." } : turnBudgetExceeded ? { error: statusPayload.error ?? "Subagent exceeded turn budget." } : {},
|
|
2770
|
+
results: results.map((r) => ({
|
|
2771
|
+
agent: r.agent,
|
|
2772
|
+
output: r.output,
|
|
2773
|
+
error: r.error,
|
|
2774
|
+
success: r.success,
|
|
2775
|
+
skipped: r.skipped || undefined,
|
|
2776
|
+
interrupted: r.interrupted || undefined,
|
|
2777
|
+
timedOut: r.timedOut || undefined,
|
|
2778
|
+
turnBudget: r.turnBudget,
|
|
2779
|
+
turnBudgetExceeded: r.turnBudgetExceeded || undefined,
|
|
2780
|
+
wrapUpRequested: r.wrapUpRequested || undefined,
|
|
2781
|
+
toolBudget: r.toolBudget,
|
|
2782
|
+
toolBudgetBlocked: r.toolBudgetBlocked || undefined,
|
|
2783
|
+
sessionFile: r.sessionFile,
|
|
2784
|
+
intercomTarget: r.intercomTarget,
|
|
2785
|
+
model: r.model,
|
|
2786
|
+
attemptedModels: r.attemptedModels,
|
|
2787
|
+
modelAttempts: r.modelAttempts,
|
|
2788
|
+
totalCost: r.totalCost,
|
|
2789
|
+
artifactPaths: r.artifactPaths,
|
|
2790
|
+
truncated: r.truncated,
|
|
2791
|
+
transcriptPath: r.transcriptPath,
|
|
2792
|
+
transcriptError: r.transcriptError,
|
|
2793
|
+
structuredOutput: r.structuredOutput,
|
|
2794
|
+
structuredOutputPath: r.structuredOutputPath,
|
|
2795
|
+
structuredOutputSchemaPath: r.structuredOutputSchemaPath,
|
|
2796
|
+
acceptance: r.acceptance
|
|
2797
|
+
})),
|
|
2798
|
+
outputs,
|
|
2799
|
+
workflowGraph: statusPayload.workflowGraph,
|
|
2800
|
+
exitCode: timedOut || turnBudgetExceeded ? 1 : interrupted || results.every((r) => r.success) ? 0 : 1,
|
|
2801
|
+
timestamp: runEndedAt,
|
|
2802
|
+
durationMs: runEndedAt - overallStartTime,
|
|
2803
|
+
totalTokens: statusPayload.totalTokens,
|
|
2804
|
+
totalCost: finalTotalCost,
|
|
2805
|
+
truncated,
|
|
2806
|
+
artifactsDir,
|
|
2807
|
+
cwd,
|
|
2808
|
+
asyncDir,
|
|
2809
|
+
sessionId: config.sessionId,
|
|
2810
|
+
sessionFile: effectiveSessionFile,
|
|
2811
|
+
intercomTarget: config.controlIntercomTarget,
|
|
2812
|
+
shareUrl,
|
|
2813
|
+
gistUrl,
|
|
2814
|
+
shareError,
|
|
2815
|
+
...taskIndex !== undefined && { taskIndex },
|
|
2816
|
+
...totalTasks !== undefined && { totalTasks }
|
|
2817
|
+
});
|
|
2818
|
+
} catch (err) {
|
|
2819
|
+
console.error(`Failed to write result file ${resultPath}:`, err);
|
|
2820
|
+
}
|
|
2821
|
+
}
|
|
2822
|
+
const configArg = process.argv[2];
|
|
2823
|
+
if (configArg) {
|
|
2824
|
+
try {
|
|
2825
|
+
const configJson = fs.readFileSync(configArg, "utf-8");
|
|
2826
|
+
const config = JSON.parse(configJson);
|
|
2827
|
+
try {
|
|
2828
|
+
fs.unlinkSync(configArg);
|
|
2829
|
+
} catch {}
|
|
2830
|
+
runSubagent(config).catch((runErr) => {
|
|
2831
|
+
console.error("Subagent runner error:", runErr);
|
|
2832
|
+
process.exit(1);
|
|
2833
|
+
});
|
|
2834
|
+
} catch (err) {
|
|
2835
|
+
console.error("Subagent runner error:", err);
|
|
2836
|
+
process.exit(1);
|
|
2837
|
+
}
|
|
2838
|
+
} else {
|
|
2839
|
+
let input = "";
|
|
2840
|
+
process.stdin.setEncoding("utf-8");
|
|
2841
|
+
process.stdin.on("data", (chunk) => {
|
|
2842
|
+
input += chunk;
|
|
2843
|
+
});
|
|
2844
|
+
process.stdin.on("end", () => {
|
|
2845
|
+
try {
|
|
2846
|
+
const config = JSON.parse(input);
|
|
2847
|
+
runSubagent(config).catch((runErr) => {
|
|
2848
|
+
console.error("Subagent runner error:", runErr);
|
|
2849
|
+
process.exit(1);
|
|
2850
|
+
});
|
|
2851
|
+
} catch (err) {
|
|
2852
|
+
console.error("Subagent runner error:", err);
|
|
2853
|
+
process.exit(1);
|
|
2854
|
+
}
|
|
2855
|
+
});
|
|
2856
|
+
}
|