@duckmind/dm-windows-x64 0.61.4 → 0.61.9
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 +211 -67
- package/extensions/dm-subagents/agents/claude-code-writer.md +15 -0
- package/extensions/dm-subagents/agents/claude-code.md +15 -0
- package/extensions/dm-subagents/agents/codex-exec-writer.md +15 -0
- package/extensions/dm-subagents/agents/codex-exec.md +15 -0
- package/extensions/dm-subagents/agents/cursor-agent-writer.md +14 -0
- package/extensions/dm-subagents/agents/cursor-agent.md +14 -0
- package/extensions/dm-subagents/agents/delegate.md +3 -2
- package/extensions/dm-subagents/agents/oracle.md +10 -5
- package/extensions/dm-subagents/agents/researcher.md +2 -2
- package/extensions/dm-subagents/agents/reviewer.md +17 -7
- package/extensions/dm-subagents/agents/scout.md +5 -5
- package/extensions/dm-subagents/agents/worker.md +6 -2
- package/extensions/dm-subagents/async-retention-discovery-worker.mjs +167 -0
- package/extensions/dm-subagents/index.js +4 -0
- package/extensions/dm-subagents/inspector-runner.mjs +10 -0
- package/extensions/dm-subagents/install.mjs +3 -2
- package/extensions/dm-subagents/package.json +2 -2
- package/extensions/dm-subagents/prompts/council.md +60 -0
- package/extensions/dm-subagents/prompts/parallel-review.md +5 -1
- package/extensions/dm-subagents/prompts/review-loop.md +13 -7
- package/extensions/dm-subagents/skills/council-mode/SKILL.md +59 -0
- package/extensions/dm-subagents/skills/council-mode/references/pass-contracts.md +150 -0
- package/extensions/dm-subagents/skills/dm-subagents/SKILL.md +96 -911
- package/extensions/dm-subagents/skills/dm-subagents/references/constraints-and-recipes.md +70 -0
- package/extensions/dm-subagents/skills/dm-subagents/references/execution-controls.md +539 -0
- package/extensions/dm-subagents/skills/dm-subagents/references/management-authoring-rpc.md +161 -0
- package/extensions/dm-subagents/skills/dm-subagents/references/multi-lane-orchestration.md +51 -0
- package/extensions/dm-subagents/skills/dm-subagents/references/prompting-and-roles.md +295 -0
- package/extensions/dm-subagents/skills/dm-subagents/references/review-and-validation.md +73 -0
- package/extensions/dm-subagents/src/agents/agent-management.js +716 -484
- package/extensions/dm-subagents/src/agents/agent-refinements.js +563 -0
- package/extensions/dm-subagents/src/agents/agent-serializer.js +70 -5
- package/extensions/dm-subagents/src/agents/agents.js +1447 -299
- package/extensions/dm-subagents/src/agents/builtin-names.js +15 -0
- package/extensions/dm-subagents/src/agents/chain-serializer.js +12 -7
- package/extensions/dm-subagents/src/agents/frontmatter.js +64 -14
- package/extensions/dm-subagents/src/agents/identity.js +1 -1
- package/extensions/dm-subagents/src/agents/proactive-skills.js +14 -11
- package/extensions/dm-subagents/src/agents/runtime-agent-events.js +49 -0
- package/extensions/dm-subagents/src/agents/runtime-agent-registry.js +412 -0
- package/extensions/dm-subagents/src/agents/skills.js +52 -35
- package/extensions/dm-subagents/src/api/agents.js +6 -0
- package/extensions/dm-subagents/src/api/background-work.js +151 -0
- package/extensions/dm-subagents/src/api/capability-ceiling.js +12 -0
- package/extensions/dm-subagents/src/api/control-channel.js +3 -0
- package/extensions/dm-subagents/src/api/delegation.js +5 -0
- package/extensions/dm-subagents/src/api/dm-args.js +3 -0
- package/extensions/dm-subagents/src/api/external-job-provider.js +137 -0
- package/extensions/dm-subagents/src/api/external-runs.js +233 -0
- package/extensions/dm-subagents/src/api/intercom-bridge.js +3 -0
- package/extensions/dm-subagents/src/api/preflight.js +322 -0
- package/extensions/dm-subagents/src/api/project-panes.js +11 -0
- package/extensions/dm-subagents/src/api/shared-types.js +4 -0
- package/extensions/dm-subagents/src/extension/config.js +175 -0
- package/extensions/dm-subagents/src/extension/control-notices.js +4 -43
- package/extensions/dm-subagents/src/extension/doctor.js +71 -17
- package/extensions/dm-subagents/src/extension/fanout-child.js +49 -32
- package/extensions/dm-subagents/src/extension/index.js +711 -265
- package/extensions/dm-subagents/src/extension/public-execution.js +114 -0
- package/extensions/dm-subagents/src/extension/rpc.js +427 -22
- package/extensions/dm-subagents/src/extension/schemas.js +158 -65
- package/extensions/dm-subagents/src/extension/steering-notices.js +23 -0
- package/extensions/dm-subagents/src/extension/subagent-guide.js +31 -0
- package/extensions/dm-subagents/src/extension/tool-description.js +92 -74
- package/extensions/dm-subagents/src/extension/tool-result.js +7 -0
- package/extensions/dm-subagents/src/inspectors/herdr/actions.js +218 -0
- package/extensions/dm-subagents/src/inspectors/herdr/client.js +123 -0
- package/extensions/dm-subagents/src/inspectors/herdr/focus.js +47 -0
- package/extensions/dm-subagents/src/inspectors/herdr/inspector-runner.js +160 -0
- package/extensions/dm-subagents/src/inspectors/herdr/project-panes.js +618 -0
- package/extensions/dm-subagents/src/inspectors/herdr/session-roots-codec.js +21 -0
- package/extensions/dm-subagents/src/inspectors/herdr/shell-command.js +15 -0
- package/extensions/dm-subagents/src/integrations/herdr-status.js +377 -0
- package/extensions/dm-subagents/src/intercom/intercom-bridge.js +17 -13
- package/extensions/dm-subagents/src/intercom/native-supervisor-channel.js +371 -79
- package/extensions/dm-subagents/src/intercom/result-intercom.js +47 -7
- package/extensions/dm-subagents/src/missions/actions.js +394 -0
- package/extensions/dm-subagents/src/missions/goal-driver.js +149 -0
- package/extensions/dm-subagents/src/missions/lifecycle.js +331 -0
- package/extensions/dm-subagents/src/missions/store.js +548 -0
- package/extensions/dm-subagents/src/missions/types.js +9 -0
- package/extensions/dm-subagents/src/missions/workflow-state.js +245 -0
- package/extensions/dm-subagents/src/policy/authority.js +37 -0
- package/extensions/dm-subagents/src/profiles/profiles.js +36 -18
- package/extensions/dm-subagents/src/runs/background/active-async-capacity.js +427 -0
- package/extensions/dm-subagents/src/runs/background/active-run-index.js +122 -0
- package/extensions/dm-subagents/src/runs/background/async-execution.js +925 -137
- package/extensions/dm-subagents/src/runs/background/async-job-tracker.js +515 -148
- package/extensions/dm-subagents/src/runs/background/async-resume.js +378 -51
- package/extensions/dm-subagents/src/runs/background/async-retention.js +828 -0
- package/extensions/dm-subagents/src/runs/background/async-status-snapshot.js +31 -0
- package/extensions/dm-subagents/src/runs/background/async-status.js +259 -22
- package/extensions/dm-subagents/src/runs/background/auto-drain.js +46 -0
- package/extensions/dm-subagents/src/runs/background/chain-append.js +50 -15
- package/extensions/dm-subagents/src/runs/background/chain-root-attachment.js +67 -12
- package/extensions/dm-subagents/src/runs/background/completion-batcher.js +5 -1
- package/extensions/dm-subagents/src/runs/background/completion-dedupe.js +3 -11
- package/extensions/dm-subagents/src/runs/background/completion-replay.js +245 -0
- package/extensions/dm-subagents/src/runs/background/control-channel.js +423 -36
- package/extensions/dm-subagents/src/runs/background/fleet-view.js +132 -59
- package/extensions/dm-subagents/src/runs/background/index-segment.js +38 -0
- package/extensions/dm-subagents/src/runs/background/inspect-rpc.js +373 -0
- package/extensions/dm-subagents/src/runs/background/notify.js +357 -57
- package/extensions/dm-subagents/src/runs/background/owned-process-tree.js +86 -0
- package/extensions/dm-subagents/src/runs/background/process-terminal.js +269 -0
- package/extensions/dm-subagents/src/runs/background/result-delivery-ownership.js +34 -0
- package/extensions/dm-subagents/src/runs/background/result-files.js +469 -0
- package/extensions/dm-subagents/src/runs/background/result-watcher.js +540 -75
- package/extensions/dm-subagents/src/runs/background/resume-guidance.js +44 -0
- package/extensions/dm-subagents/src/runs/background/retained-children.js +119 -0
- package/extensions/dm-subagents/src/runs/background/run-id-query.js +5 -0
- package/extensions/dm-subagents/src/runs/background/run-id-resolver.js +93 -9
- package/extensions/dm-subagents/src/runs/background/run-status.js +303 -32
- package/extensions/dm-subagents/src/runs/background/scheduled-runs.js +784 -376
- package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.js +75 -34
- package/extensions/dm-subagents/src/runs/background/steering.js +221 -0
- package/extensions/dm-subagents/src/runs/background/subagent-runner.js +3106 -844
- package/extensions/dm-subagents/src/runs/background/subagent-wait.js +529 -0
- package/extensions/dm-subagents/src/runs/background/terminal-run-index.js +106 -0
- package/extensions/dm-subagents/src/runs/background/top-level-async.js +1 -1
- package/extensions/dm-subagents/src/runs/background/wait-completions.js +155 -0
- package/extensions/dm-subagents/src/runs/background/wait-config.js +46 -0
- package/extensions/dm-subagents/src/runs/background/wait-subscriptions.js +278 -0
- package/extensions/dm-subagents/src/runs/background/wait-tool.js +47 -0
- package/extensions/dm-subagents/src/runs/foreground/async-dismiss-action.js +81 -0
- package/extensions/dm-subagents/src/runs/foreground/async-steering-action.js +245 -0
- package/extensions/dm-subagents/src/runs/foreground/async-stop-action.js +74 -0
- package/extensions/dm-subagents/src/runs/foreground/execution.js +1401 -342
- package/extensions/dm-subagents/src/runs/foreground/foreground-control.js +133 -0
- package/extensions/dm-subagents/src/runs/foreground/foreground-history.js +148 -0
- package/extensions/dm-subagents/src/runs/foreground/prompt-audit.js +139 -0
- package/extensions/dm-subagents/src/runs/foreground/subagent-executor.js +4278 -1441
- package/extensions/dm-subagents/src/runs/foreground/workflow-detach-reconcile.js +278 -0
- package/extensions/dm-subagents/src/runs/foreground/workflow-foreground-steering.js +155 -0
- package/extensions/dm-subagents/src/runs/shared/abort-recovery.js +97 -0
- package/extensions/dm-subagents/src/runs/shared/acceptance.js +597 -148
- package/extensions/dm-subagents/src/runs/shared/agent-contract.js +35 -0
- package/extensions/dm-subagents/src/runs/shared/async-status-projection.js +472 -0
- package/extensions/dm-subagents/src/runs/shared/background-process-options.js +6 -0
- package/extensions/dm-subagents/src/runs/shared/capability-ceiling.js +175 -0
- package/extensions/dm-subagents/src/runs/shared/child-identity.js +32 -0
- package/extensions/dm-subagents/src/runs/shared/child-launch-plan.js +65 -0
- package/extensions/dm-subagents/src/runs/shared/child-protocol.js +447 -0
- package/extensions/dm-subagents/src/runs/shared/claude-code-adapter.js +120 -0
- package/extensions/dm-subagents/src/runs/shared/codex-exec-adapter.js +129 -0
- package/extensions/dm-subagents/src/runs/shared/completion-evidence.js +40 -0
- package/extensions/dm-subagents/src/runs/shared/completion-guard.js +140 -83
- package/extensions/dm-subagents/src/runs/shared/context-mode.js +38 -0
- package/extensions/dm-subagents/src/runs/shared/cursor-agent-adapter.js +101 -0
- package/extensions/dm-subagents/src/runs/shared/dm-args.js +445 -72
- package/extensions/dm-subagents/src/runs/shared/dm-spawn.js +27 -16
- package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.js +19 -6
- package/extensions/dm-subagents/src/runs/shared/extension-bindings.js +81 -0
- package/extensions/dm-subagents/src/runs/shared/external-cli-contract.js +134 -0
- package/extensions/dm-subagents/src/runs/shared/external-cli-preflight.js +98 -0
- package/extensions/dm-subagents/src/runs/shared/external-cli-runner.js +419 -0
- package/extensions/dm-subagents/src/runs/shared/external-job-bridge.js +404 -0
- package/extensions/dm-subagents/src/runs/shared/external-job-runner.js +334 -0
- package/extensions/dm-subagents/src/runs/shared/fast-mode-extension.js +8 -0
- package/extensions/dm-subagents/src/runs/shared/host-step-status.js +228 -0
- package/extensions/dm-subagents/src/runs/shared/lane-metadata.js +104 -0
- package/extensions/dm-subagents/src/runs/shared/launch-cwd.js +17 -0
- package/extensions/dm-subagents/src/runs/shared/llm-intent-arbiter.js +190 -0
- package/extensions/dm-subagents/src/runs/shared/long-running-guard.js +48 -3
- package/extensions/dm-subagents/src/runs/shared/mcp-config-sources.js +387 -0
- package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.js +212 -137
- package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-grant.js +131 -0
- package/extensions/dm-subagents/src/runs/shared/model-exclusions.js +207 -0
- package/extensions/dm-subagents/src/runs/shared/model-fallback.js +225 -55
- package/extensions/dm-subagents/src/runs/shared/model-scope.js +85 -28
- package/extensions/dm-subagents/src/runs/shared/mutation-evidence.js +182 -0
- package/extensions/dm-subagents/src/runs/shared/nested-events.js +264 -102
- package/extensions/dm-subagents/src/runs/shared/nested-render.js +15 -5
- package/extensions/dm-subagents/src/runs/shared/orca-progress-tabs.js +505 -0
- package/extensions/dm-subagents/src/runs/shared/parallel-handoff.js +653 -0
- package/extensions/dm-subagents/src/runs/shared/parallel-utils.js +29 -12
- package/extensions/dm-subagents/src/runs/shared/permissions.js +108 -0
- package/extensions/dm-subagents/src/runs/shared/process-signal.js +13 -0
- package/extensions/dm-subagents/src/runs/shared/run-fanout-budget.js +257 -0
- package/extensions/dm-subagents/src/runs/shared/run-history.js +133 -13
- package/extensions/dm-subagents/src/runs/shared/runtime-acknowledged-extensions.js +62 -0
- package/extensions/dm-subagents/src/runs/shared/session-lease.js +225 -0
- package/extensions/dm-subagents/src/runs/shared/single-output.js +129 -27
- package/extensions/dm-subagents/src/runs/shared/spawn-budget.js +95 -0
- package/extensions/dm-subagents/src/runs/shared/structured-output.js +129 -10
- package/extensions/dm-subagents/src/runs/shared/subagent-control.js +66 -11
- package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.js +548 -70
- package/extensions/dm-subagents/src/runs/shared/subagent-startup-retry.js +50 -0
- package/extensions/dm-subagents/src/runs/shared/task-intent.js +130 -0
- package/extensions/dm-subagents/src/runs/shared/tool-availability.js +59 -0
- package/extensions/dm-subagents/src/runs/shared/tool-budget.js +7 -5
- package/extensions/dm-subagents/src/runs/shared/tool-timeout.js +64 -0
- package/extensions/dm-subagents/src/runs/shared/usage-budget.js +74 -0
- package/extensions/dm-subagents/src/runs/shared/workflow-graph.js +22 -0
- package/extensions/dm-subagents/src/runs/shared/worktree-cleanup-plan.js +721 -0
- package/extensions/dm-subagents/src/runs/shared/worktree.js +168 -19
- package/extensions/dm-subagents/src/shared/accessible-dir.js +35 -0
- package/extensions/dm-subagents/src/shared/agent-stream-options.js +3 -0
- package/extensions/dm-subagents/src/shared/artifacts.js +170 -11
- package/extensions/dm-subagents/src/shared/atomic-json.js +36 -38
- package/extensions/dm-subagents/src/shared/capacity-resilient-json.js +77 -0
- package/extensions/dm-subagents/src/shared/child-session-name.js +15 -0
- package/extensions/dm-subagents/src/shared/child-transcript.js +57 -2
- package/extensions/dm-subagents/src/shared/completion-owner.js +7 -0
- package/extensions/dm-subagents/src/shared/display-text.js +142 -0
- package/extensions/dm-subagents/src/shared/extension-context.js +17 -0
- package/extensions/dm-subagents/src/shared/file-coalescer.js +9 -0
- package/extensions/dm-subagents/src/shared/file-system-retry.js +56 -0
- package/extensions/dm-subagents/src/shared/fork-context.js +96 -33
- package/extensions/dm-subagents/src/shared/formatters.js +21 -7
- package/extensions/dm-subagents/src/shared/launch-contract.js +94 -0
- package/extensions/dm-subagents/src/shared/model-info.js +10 -5
- package/extensions/dm-subagents/src/shared/node-executable.js +19 -0
- package/extensions/dm-subagents/src/shared/prompt-resources.js +10 -0
- package/extensions/dm-subagents/src/shared/pruned-fork.js +427 -0
- package/extensions/dm-subagents/src/shared/session-file-trust.js +19 -0
- package/extensions/dm-subagents/src/shared/session-tokens.js +14 -3
- package/extensions/dm-subagents/src/shared/settings.js +39 -47
- package/extensions/dm-subagents/src/shared/shortcuts.js +16 -0
- package/extensions/dm-subagents/src/shared/status-format.js +9 -2
- package/extensions/dm-subagents/src/shared/thinking-ceiling.js +41 -0
- package/extensions/dm-subagents/src/shared/types.js +47 -7
- package/extensions/dm-subagents/src/shared/utf8.js +12 -0
- package/extensions/dm-subagents/src/shared/utils.js +151 -131
- package/extensions/dm-subagents/src/shared/watch-strategy.js +3 -0
- package/extensions/dm-subagents/src/shared/workflow-child-permit.js +84 -0
- package/extensions/dm-subagents/src/slash/delegation-adapters.js +274 -0
- package/extensions/dm-subagents/src/slash/delegation-json.js +113 -0
- package/extensions/dm-subagents/src/slash/delegation-request.js +152 -0
- package/extensions/dm-subagents/src/slash/prompt-template-bridge.js +294 -243
- package/extensions/dm-subagents/src/slash/prompt-workflows.js +35 -73
- package/extensions/dm-subagents/src/slash/selector.js +101 -0
- package/extensions/dm-subagents/src/slash/slash-bridge.js +17 -1
- package/extensions/dm-subagents/src/slash/slash-commands.js +722 -732
- package/extensions/dm-subagents/src/slash/slash-live-state.js +37 -19
- package/extensions/dm-subagents/src/slash/subagents-admin.js +410 -0
- package/extensions/dm-subagents/src/tui/fleet-status.js +824 -0
- package/extensions/dm-subagents/src/tui/fleet-transcript.js +479 -0
- package/extensions/dm-subagents/src/tui/fleet.js +1326 -0
- package/extensions/dm-subagents/src/tui/render-helpers.js +22 -0
- package/extensions/dm-subagents/src/tui/render.js +1511 -261
- package/extensions/dm-subagents/src/watchdog/change-signature.js +220 -0
- package/extensions/dm-subagents/src/watchdog/child-status.js +151 -0
- package/extensions/dm-subagents/src/watchdog/emission-guard.js +90 -0
- package/extensions/dm-subagents/src/watchdog/lsp-diagnostics.js +484 -0
- package/extensions/dm-subagents/src/watchdog/model-selection.js +154 -0
- package/extensions/dm-subagents/src/watchdog/permission-arbiter.js +138 -0
- package/extensions/dm-subagents/src/watchdog/register-child.js +112 -0
- package/extensions/dm-subagents/src/watchdog/register-main.js +419 -0
- package/extensions/dm-subagents/src/watchdog/render.js +54 -0
- package/extensions/dm-subagents/src/watchdog/review.js +251 -0
- package/extensions/dm-subagents/src/watchdog/runtime.js +803 -0
- package/extensions/dm-subagents/src/watchdog/scope.js +56 -0
- package/extensions/dm-subagents/src/watchdog/settings.js +515 -0
- package/extensions/dm-subagents/src/watchdog/tool-actions.js +151 -0
- package/extensions/dm-subagents/src/watchdog/turn-delta.js +169 -0
- package/extensions/dm-subagents/src/watchdog/types.js +29 -0
- package/extensions/dm-subagents/src/watchdog/warning-format.js +58 -0
- package/extensions/dm-subagents/src/workflows/chat-progress.js +116 -0
- package/extensions/dm-subagents/src/workflows/host-command.js +227 -0
- package/extensions/dm-subagents/src/workflows/scripted-workflow.js +2011 -0
- package/extensions/dm-subagents/src/workflows/workflow-child-summary.js +116 -0
- package/extensions/dm-subagents/src/workflows/workflow-preflight.js +243 -0
- package/extensions/dm-subagents/src/workflows/workflow-receipt.js +387 -0
- package/extensions/dm-subagents/src/workflows/workflow-settlement.js +189 -0
- package/package.json +4 -3
- package/extensions/dm-fff/package.json +0 -21
- package/extensions/dm-fff/src/index.js +0 -691
- package/extensions/dm-fff/src/query.js +0 -60
- package/extensions/dm-subagents/agents/context-builder.md +0 -46
- package/extensions/dm-subagents/agents/planner.md +0 -55
- package/extensions/dm-subagents/prompts/parallel-context-build.md +0 -55
- package/extensions/dm-subagents/prompts/parallel-handoff-plan.md +0 -61
- package/extensions/dm-subagents/src/runs/background/wait.js +0 -206
- package/extensions/dm-subagents/src/runs/foreground/chain-clarify.js +0 -1013
- package/extensions/dm-subagents/src/runs/foreground/chain-execution.js +0 -981
- package/extensions/dm-subagents/src/runs/shared/turn-budget.js +0 -50
|
@@ -3,7 +3,8 @@ import * as fs from "node:fs";
|
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import { writeAtomicJson } from "../../shared/atomic-json.js";
|
|
5
5
|
import { appendJsonl } from "../../shared/artifacts.js";
|
|
6
|
-
import { readStatus } from "../../shared/utils.js";
|
|
6
|
+
import { PROMPT_REDACTED, readStatus } from "../../shared/utils.js";
|
|
7
|
+
import { deriveChildSessionName } from "../../shared/child-session-name.js";
|
|
7
8
|
import { isDynamicRunnerGroup, isParallelGroup } from "../shared/parallel-utils.js";
|
|
8
9
|
const APPEND_REQUESTS_DIR = "append-requests";
|
|
9
10
|
function appendDir(asyncDir) {
|
|
@@ -60,30 +61,49 @@ export function enqueueChainAppendRequest(input) {
|
|
|
60
61
|
steps: input.steps
|
|
61
62
|
};
|
|
62
63
|
fs.mkdirSync(appendDir(input.asyncDir), { recursive: true });
|
|
63
|
-
writeAtomicJson(appendRequestPath(input.asyncDir, request), request);
|
|
64
|
-
|
|
64
|
+
const persist = () => writeAtomicJson(appendRequestPath(input.asyncDir, request), request);
|
|
65
|
+
if (input.admit)
|
|
66
|
+
input.admit(persist);
|
|
67
|
+
else
|
|
68
|
+
persist();
|
|
69
|
+
let pendingCount = 1;
|
|
70
|
+
const bookkeepingErrors = [];
|
|
71
|
+
try {
|
|
72
|
+
pendingCount = countPendingChainAppendRequests(input.asyncDir);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
bookkeepingErrors.push(`pending append count failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
75
|
+
}
|
|
65
76
|
const statusPath = path.join(input.asyncDir, "status.json");
|
|
66
77
|
const updatedStatus = { ...status, pendingAppends: pendingCount, lastUpdate: request.createdAt };
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
78
|
+
try {
|
|
79
|
+
writeAtomicJson(statusPath, updatedStatus);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
bookkeepingErrors.push(`status update failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
appendJsonl(path.join(input.asyncDir, "events.jsonl"), JSON.stringify({
|
|
85
|
+
type: "subagent.chain.append.requested",
|
|
86
|
+
ts: request.createdAt,
|
|
87
|
+
runId: input.runId,
|
|
88
|
+
requestId: request.id,
|
|
89
|
+
stepCount: input.steps.length,
|
|
90
|
+
pendingAppends: pendingCount
|
|
91
|
+
}));
|
|
92
|
+
} catch (error) {
|
|
93
|
+
bookkeepingErrors.push(`event append failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
94
|
+
}
|
|
95
|
+
return { request, pendingCount, ...bookkeepingErrors.length > 0 ? { bookkeepingError: bookkeepingErrors.join("; ") } : {} };
|
|
77
96
|
}
|
|
78
97
|
function readAppendRequest(filePath) {
|
|
79
98
|
const raw = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
80
99
|
if (!raw.id || typeof raw.id !== "string")
|
|
81
100
|
return;
|
|
82
|
-
|
|
101
|
+
const createdAt = raw.createdAt;
|
|
102
|
+
if (typeof createdAt !== "number" || !Number.isFinite(createdAt))
|
|
83
103
|
return;
|
|
84
104
|
if (!Array.isArray(raw.steps) || raw.steps.length === 0)
|
|
85
105
|
return;
|
|
86
|
-
return { id: raw.id, createdAt
|
|
106
|
+
return { id: raw.id, createdAt, steps: raw.steps };
|
|
87
107
|
}
|
|
88
108
|
export function readPendingChainAppendRequests(asyncDir) {
|
|
89
109
|
return listAppendRequestFiles(asyncDir).map((filePath) => readAppendRequest(filePath)).filter((request) => Boolean(request)).sort((left, right) => left.createdAt - right.createdAt || left.id.localeCompare(right.id));
|
|
@@ -100,9 +120,21 @@ export function consumeChainAppendRequests(asyncDir) {
|
|
|
100
120
|
}
|
|
101
121
|
return requests.sort((left, right) => left.createdAt - right.createdAt || left.id.localeCompare(right.id));
|
|
102
122
|
}
|
|
123
|
+
const MAX_STATUS_STEP_DESCRIPTION_CHARS = 160;
|
|
124
|
+
export function statusStepDescription(task) {
|
|
125
|
+
if (!task?.trim())
|
|
126
|
+
return;
|
|
127
|
+
const description = PROMPT_REDACTED;
|
|
128
|
+
return description.length > MAX_STATUS_STEP_DESCRIPTION_CHARS ? `${description.slice(0, MAX_STATUS_STEP_DESCRIPTION_CHARS - 1)}…` : description;
|
|
129
|
+
}
|
|
103
130
|
function statusStepForTask(task) {
|
|
131
|
+
const description = statusStepDescription(task.task);
|
|
132
|
+
const sessionName = task.sessionName ?? deriveChildSessionName({ agent: task.agent, task: task.task, label: task.label });
|
|
104
133
|
return {
|
|
105
134
|
agent: task.agent,
|
|
135
|
+
...sessionName ? { sessionName } : {},
|
|
136
|
+
...description ? { description } : {},
|
|
137
|
+
...task.context ? { context: task.context } : {},
|
|
106
138
|
phase: task.phase,
|
|
107
139
|
label: task.label,
|
|
108
140
|
outputName: task.outputName,
|
|
@@ -111,6 +143,7 @@ function statusStepForTask(task) {
|
|
|
111
143
|
...task.sessionFile ? { sessionFile: task.sessionFile } : {},
|
|
112
144
|
skills: task.skills,
|
|
113
145
|
model: task.model,
|
|
146
|
+
...task.contextLimit !== undefined ? { contextLimit: task.contextLimit } : {},
|
|
114
147
|
thinking: task.thinking,
|
|
115
148
|
attemptedModels: task.modelCandidates && task.modelCandidates.length > 0 ? task.modelCandidates : task.model ? [task.model] : undefined,
|
|
116
149
|
recentTools: [],
|
|
@@ -123,10 +156,12 @@ function statusStepsForRunnerStep(step) {
|
|
|
123
156
|
if (isDynamicRunnerGroup(step)) {
|
|
124
157
|
return [{
|
|
125
158
|
agent: `expand:${step.parallel.agent}`,
|
|
159
|
+
...step.parallel.context ? { context: step.parallel.context } : {},
|
|
126
160
|
phase: step.phase ?? step.parallel.phase,
|
|
127
161
|
label: step.label ?? step.parallel.label ?? `Dynamic fanout (${step.collect.as})`,
|
|
128
162
|
outputName: step.collect.as,
|
|
129
163
|
structured: Boolean(step.collect.outputSchema),
|
|
164
|
+
...step.parallel.contextLimit !== undefined ? { contextLimit: step.parallel.contextLimit } : {},
|
|
130
165
|
status: "pending",
|
|
131
166
|
recentTools: [],
|
|
132
167
|
recentOutput: []
|
|
@@ -1,8 +1,25 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
+
import { resultFilePath, resultPayloadPathForSessionRun } from "./result-files.js";
|
|
3
4
|
import { readStatus } from "../../shared/utils.js";
|
|
4
|
-
const TERMINAL_STATES = new Set(["complete", "failed", "paused"]);
|
|
5
|
-
const TERMINAL_STEP_STATUSES = new Set(["complete", "completed", "failed", "paused"]);
|
|
5
|
+
const TERMINAL_STATES = new Set(["complete", "failed", "partial", "paused", "stopped"]);
|
|
6
|
+
const TERMINAL_STEP_STATUSES = new Set(["complete", "completed", "failed", "partial", "paused", "stopped"]);
|
|
7
|
+
function usageFromAttempts(attempts) {
|
|
8
|
+
if (!attempts?.length)
|
|
9
|
+
return;
|
|
10
|
+
const usage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 };
|
|
11
|
+
for (const attempt of attempts) {
|
|
12
|
+
if (!attempt.usage)
|
|
13
|
+
continue;
|
|
14
|
+
usage.input += attempt.usage.input;
|
|
15
|
+
usage.output += attempt.usage.output;
|
|
16
|
+
usage.cacheRead += attempt.usage.cacheRead;
|
|
17
|
+
usage.cacheWrite += attempt.usage.cacheWrite;
|
|
18
|
+
usage.cost += attempt.usage.cost;
|
|
19
|
+
usage.turns += attempt.usage.turns;
|
|
20
|
+
}
|
|
21
|
+
return usage.input !== 0 || usage.output !== 0 || usage.cacheRead !== 0 || usage.cacheWrite !== 0 || usage.cost !== 0 || usage.turns !== 0 ? usage : undefined;
|
|
22
|
+
}
|
|
6
23
|
function readResultFile(resultPath) {
|
|
7
24
|
try {
|
|
8
25
|
return JSON.parse(fs.readFileSync(resultPath, "utf-8"));
|
|
@@ -13,6 +30,13 @@ function readResultFile(resultPath) {
|
|
|
13
30
|
throw error;
|
|
14
31
|
}
|
|
15
32
|
}
|
|
33
|
+
function readImportedResultFile(root, status) {
|
|
34
|
+
const direct = readResultFile(root.resultPath);
|
|
35
|
+
if (direct || !status?.sessionId)
|
|
36
|
+
return direct;
|
|
37
|
+
const indexedPath = resultPayloadPathForSessionRun(path.dirname(root.resultPath), status.sessionId, root.runId);
|
|
38
|
+
return indexedPath ? readResultFile(indexedPath) : undefined;
|
|
39
|
+
}
|
|
16
40
|
function selectedStatusStep(status, index) {
|
|
17
41
|
return status?.steps?.[index];
|
|
18
42
|
}
|
|
@@ -27,11 +51,13 @@ function isTerminalStatus(status, index) {
|
|
|
27
51
|
function resultState(result, child) {
|
|
28
52
|
if (!result)
|
|
29
53
|
return;
|
|
54
|
+
if (child?.stopped === true)
|
|
55
|
+
return "stopped";
|
|
30
56
|
if (child?.success === true)
|
|
31
57
|
return "complete";
|
|
32
58
|
if (child?.success === false)
|
|
33
|
-
return result.state === "paused" ? "paused" : "failed";
|
|
34
|
-
if (result.state === "complete" || result.state === "failed" || result.state === "paused")
|
|
59
|
+
return result.state === "stopped" ? "stopped" : result.state === "paused" ? "paused" : result.state === "partial" ? "partial" : "failed";
|
|
60
|
+
if (result.state === "complete" || result.state === "failed" || result.state === "partial" || result.state === "paused" || result.state === "stopped")
|
|
35
61
|
return result.state;
|
|
36
62
|
if (result.success === true)
|
|
37
63
|
return "complete";
|
|
@@ -42,7 +68,11 @@ function resultState(result, child) {
|
|
|
42
68
|
function outputFromTerminalStatus(root, status, step) {
|
|
43
69
|
const agent = step?.agent ?? status.steps?.[root.index]?.agent ?? "subagent";
|
|
44
70
|
const timedOut = step?.timedOut === true || status.timedOut === true;
|
|
45
|
-
const
|
|
71
|
+
const stopped = step?.stopped === true || status.stopped === true || status.state === "stopped";
|
|
72
|
+
const message = step?.error ?? status.error ?? (stopped ? "Subagent stopped by user." : `Attached async root ${root.runId} ended without a result file at ${root.resultPath}.`);
|
|
73
|
+
const partialStatus = status.state === "partial" || step?.status === "partial";
|
|
74
|
+
const execution = step?.execution ?? (partialStatus ? { status: "partial", success: false, exitCode: 1, error: message } : undefined);
|
|
75
|
+
const usage = usageFromAttempts(step?.modelAttempts);
|
|
46
76
|
return {
|
|
47
77
|
agent,
|
|
48
78
|
output: message,
|
|
@@ -50,19 +80,27 @@ function outputFromTerminalStatus(root, status, step) {
|
|
|
50
80
|
exitCode: 1,
|
|
51
81
|
error: message,
|
|
52
82
|
...timedOut ? { timedOut: true } : {},
|
|
83
|
+
...stopped ? { stopped: true } : {},
|
|
84
|
+
...step?.sessionName ? { sessionName: step.sessionName } : {},
|
|
53
85
|
...step?.sessionFile ?? status.sessionFile ? { sessionFile: step?.sessionFile ?? status.sessionFile } : {},
|
|
54
86
|
...step?.model ? { model: step.model } : {},
|
|
55
87
|
...step?.attemptedModels ? { attemptedModels: step.attemptedModels } : {},
|
|
56
88
|
...step?.modelAttempts ? { modelAttempts: step.modelAttempts } : {},
|
|
89
|
+
...step?.contextOverflow ? { contextOverflow: true } : {},
|
|
57
90
|
...step?.totalCost ? { totalCost: step.totalCost } : {},
|
|
91
|
+
...usage ? { usage } : {},
|
|
58
92
|
...step?.structuredOutput !== undefined ? { structuredOutput: step.structuredOutput } : {},
|
|
59
93
|
...step?.structuredOutputPath ? { structuredOutputPath: step.structuredOutputPath } : {},
|
|
60
94
|
...step?.structuredOutputSchemaPath ? { structuredOutputSchemaPath: step.structuredOutputSchemaPath } : {},
|
|
61
|
-
...step?.acceptance ? { acceptance: step.acceptance } : {}
|
|
95
|
+
...step?.acceptance ? { acceptance: step.acceptance } : {},
|
|
96
|
+
...execution ? { execution } : {},
|
|
97
|
+
...step?.effects ? { effects: step.effects } : {},
|
|
98
|
+
...step?.transcriptPath ? { transcriptPath: step.transcriptPath } : {}
|
|
62
99
|
};
|
|
63
100
|
}
|
|
64
101
|
function outputFromTimeout(root, status, message) {
|
|
65
102
|
const step = selectedStatusStep(status, root.index);
|
|
103
|
+
const usage = usageFromAttempts(step?.modelAttempts);
|
|
66
104
|
return {
|
|
67
105
|
agent: step?.agent ?? status?.steps?.[root.index]?.agent ?? "subagent",
|
|
68
106
|
output: message,
|
|
@@ -70,11 +108,15 @@ function outputFromTimeout(root, status, message) {
|
|
|
70
108
|
exitCode: 1,
|
|
71
109
|
error: message,
|
|
72
110
|
timedOut: true,
|
|
111
|
+
...step?.sessionName ? { sessionName: step.sessionName } : {},
|
|
73
112
|
...step?.sessionFile ?? status?.sessionFile ? { sessionFile: step?.sessionFile ?? status?.sessionFile } : {},
|
|
74
113
|
...step?.model ? { model: step.model } : {},
|
|
75
114
|
...step?.attemptedModels ? { attemptedModels: step.attemptedModels } : {},
|
|
76
115
|
...step?.modelAttempts ? { modelAttempts: step.modelAttempts } : {},
|
|
77
|
-
...step?.
|
|
116
|
+
...step?.contextOverflow ? { contextOverflow: true } : {},
|
|
117
|
+
...step?.totalCost ? { totalCost: step.totalCost } : {},
|
|
118
|
+
...usage ? { usage } : {},
|
|
119
|
+
...step?.transcriptPath ? { transcriptPath: step.transcriptPath } : {}
|
|
78
120
|
};
|
|
79
121
|
}
|
|
80
122
|
function buildImportedResult(root, status, result) {
|
|
@@ -84,8 +126,11 @@ function buildImportedResult(root, status, result) {
|
|
|
84
126
|
const agent = child?.agent ?? step?.agent ?? status?.steps?.[root.index]?.agent ?? "subagent";
|
|
85
127
|
const output = child?.output ?? result.summary ?? "";
|
|
86
128
|
const timedOut = child?.timedOut === true || step?.timedOut === true || result.timedOut === true || status?.timedOut === true;
|
|
87
|
-
const
|
|
88
|
-
const
|
|
129
|
+
const stopped = child?.stopped === true || step?.stopped === true || result.stopped === true || status?.stopped === true || state === "stopped";
|
|
130
|
+
const success = state === "complete" && !timedOut && !stopped;
|
|
131
|
+
const error = child?.error ?? (success ? undefined : stopped ? "Subagent stopped by user." : result.error ?? result.summary ?? status?.error ?? `Attached async root ${root.runId} did not complete successfully.`);
|
|
132
|
+
const execution = state ? { status: state === "complete" ? "completed" : state, success, exitCode: success ? 0 : 1, ...error ? { error } : {} } : undefined;
|
|
133
|
+
const usage = child?.usage ?? usageFromAttempts(step?.modelAttempts);
|
|
89
134
|
return {
|
|
90
135
|
agent,
|
|
91
136
|
output: success ? output : output || error || "",
|
|
@@ -93,16 +138,26 @@ function buildImportedResult(root, status, result) {
|
|
|
93
138
|
exitCode: success ? 0 : 1,
|
|
94
139
|
...error ? { error } : {},
|
|
95
140
|
...timedOut ? { timedOut: true } : {},
|
|
141
|
+
...stopped ? { stopped: true } : {},
|
|
142
|
+
...child?.sessionName ?? step?.sessionName ? { sessionName: child?.sessionName ?? step?.sessionName } : {},
|
|
96
143
|
...child?.sessionFile ?? step?.sessionFile ?? status?.sessionFile ? { sessionFile: child?.sessionFile ?? step?.sessionFile ?? status?.sessionFile } : {},
|
|
97
144
|
...child?.intercomTarget ? { intercomTarget: child.intercomTarget } : {},
|
|
98
145
|
...child?.model ?? step?.model ? { model: child?.model ?? step?.model } : {},
|
|
99
146
|
...child?.attemptedModels ?? step?.attemptedModels ? { attemptedModels: child?.attemptedModels ?? step?.attemptedModels } : {},
|
|
100
147
|
...child?.modelAttempts ?? step?.modelAttempts ? { modelAttempts: child?.modelAttempts ?? step?.modelAttempts } : {},
|
|
148
|
+
...child?.contextOverflow || step?.contextOverflow ? { contextOverflow: true } : {},
|
|
101
149
|
...child?.totalCost ?? step?.totalCost ? { totalCost: child?.totalCost ?? step?.totalCost } : {},
|
|
150
|
+
...usage ? { usage } : {},
|
|
102
151
|
...child?.structuredOutput !== undefined ? { structuredOutput: child.structuredOutput } : step?.structuredOutput !== undefined ? { structuredOutput: step.structuredOutput } : {},
|
|
103
152
|
...child?.structuredOutputPath ?? step?.structuredOutputPath ? { structuredOutputPath: child?.structuredOutputPath ?? step?.structuredOutputPath } : {},
|
|
104
153
|
...child?.structuredOutputSchemaPath ?? step?.structuredOutputSchemaPath ? { structuredOutputSchemaPath: child?.structuredOutputSchemaPath ?? step?.structuredOutputSchemaPath } : {},
|
|
105
|
-
...child?.acceptance ?? step?.acceptance ? { acceptance: child?.acceptance ?? step?.acceptance } : {}
|
|
154
|
+
...child?.acceptance ?? step?.acceptance ? { acceptance: child?.acceptance ?? step?.acceptance } : {},
|
|
155
|
+
...child?.artifactPaths ? { artifactPaths: child.artifactPaths } : {},
|
|
156
|
+
...child?.outputSaveError ? { outputSaveError: child.outputSaveError } : {},
|
|
157
|
+
...child?.transcriptPath ?? step?.transcriptPath ? { transcriptPath: child?.transcriptPath ?? step?.transcriptPath } : {},
|
|
158
|
+
...child?.transcriptError ? { transcriptError: child.transcriptError } : {},
|
|
159
|
+
...execution ? { execution } : {},
|
|
160
|
+
...child?.effects ?? step?.effects ? { effects: child?.effects ?? step?.effects } : {}
|
|
106
161
|
};
|
|
107
162
|
}
|
|
108
163
|
export async function waitForImportedAsyncRoot(root, options = {}) {
|
|
@@ -114,7 +169,7 @@ export async function waitForImportedAsyncRoot(root, options = {}) {
|
|
|
114
169
|
const status = readStatus(root.asyncDir);
|
|
115
170
|
if (options.shouldAbort?.())
|
|
116
171
|
return outputFromTimeout(root, status, options.timeoutMessage ?? "Subagent timed out.");
|
|
117
|
-
const result =
|
|
172
|
+
const result = readImportedResultFile(root, status);
|
|
118
173
|
if (result)
|
|
119
174
|
return buildImportedResult(root, status, result);
|
|
120
175
|
if (isTerminalStatus(status, root.index)) {
|
|
@@ -132,5 +187,5 @@ export async function waitForImportedAsyncRoot(root, options = {}) {
|
|
|
132
187
|
}
|
|
133
188
|
}
|
|
134
189
|
export function resolveAsyncRootResultPath(resultsDir, runId) {
|
|
135
|
-
return
|
|
190
|
+
return resultFilePath(resultsDir, runId);
|
|
136
191
|
}
|
|
@@ -41,7 +41,9 @@ export function createCompletionBatcher(options) {
|
|
|
41
41
|
options.emit([item]);
|
|
42
42
|
},
|
|
43
43
|
flush() {},
|
|
44
|
-
dispose() {
|
|
44
|
+
dispose() {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
45
47
|
};
|
|
46
48
|
}
|
|
47
49
|
let pending = [];
|
|
@@ -88,7 +90,9 @@ export function createCompletionBatcher(options) {
|
|
|
88
90
|
flush: emitGroup,
|
|
89
91
|
dispose() {
|
|
90
92
|
clearTimers();
|
|
93
|
+
const abandoned = pending;
|
|
91
94
|
pending = [];
|
|
95
|
+
return abandoned;
|
|
92
96
|
}
|
|
93
97
|
};
|
|
94
98
|
}
|
|
@@ -10,10 +10,11 @@ function asFiniteNumber(value) {
|
|
|
10
10
|
return Number.isFinite(value) ? value : undefined;
|
|
11
11
|
}
|
|
12
12
|
export function buildCompletionKey(data, fallback) {
|
|
13
|
+
const sessionId = asNonEmptyString(data.sessionId) ?? "no-session";
|
|
13
14
|
const id = asNonEmptyString(data.id);
|
|
15
|
+
const state = asNonEmptyString(data.state);
|
|
14
16
|
if (id)
|
|
15
|
-
return `id:${id}`;
|
|
16
|
-
const sessionId = asNonEmptyString(data.sessionId) ?? "no-session";
|
|
17
|
+
return state ? `session:${sessionId}:id:${id}:state:${state}` : `session:${sessionId}:id:${id}`;
|
|
17
18
|
const agent = asNonEmptyString(data.agent) ?? "unknown";
|
|
18
19
|
const timestamp = asFiniteNumber(data.timestamp);
|
|
19
20
|
const taskIndex = asFiniteNumber(data.taskIndex);
|
|
@@ -43,12 +44,3 @@ export function markSeenWithTtl(seen, key, now, ttlMs) {
|
|
|
43
44
|
seen.set(key, now);
|
|
44
45
|
return false;
|
|
45
46
|
}
|
|
46
|
-
export function getGlobalSeenMap(storeKey) {
|
|
47
|
-
const globalStore = globalThis;
|
|
48
|
-
const existing = globalStore[storeKey];
|
|
49
|
-
if (existing instanceof Map)
|
|
50
|
-
return existing;
|
|
51
|
-
const map = new Map;
|
|
52
|
-
globalStore[storeKey] = map;
|
|
53
|
-
return map;
|
|
54
|
-
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { writePrivateAtomicJson } from "../../shared/atomic-json.js";
|
|
4
|
+
import { utf8Tail } from "../../shared/utf8.js";
|
|
5
|
+
const REPLAY_VERSION = 1;
|
|
6
|
+
const ARCHIVE_VERSION = 1;
|
|
7
|
+
const ARCHIVE_TEXT_LIMIT_BYTES = 64 * 1024;
|
|
8
|
+
const REPLAY_DIR_NAME = "completion-replay";
|
|
9
|
+
const ARCHIVE_DIR_NAME = "output-archives";
|
|
10
|
+
const CLEANUP_INTERVAL_MS = 60000;
|
|
11
|
+
const lastCleanupByResultsDir = new Map;
|
|
12
|
+
function safeRunFile(runId) {
|
|
13
|
+
return `${encodeURIComponent(runId)}.json`;
|
|
14
|
+
}
|
|
15
|
+
export function completionReplayPath(resultsDir, runId) {
|
|
16
|
+
return path.join(resultsDir, REPLAY_DIR_NAME, safeRunFile(runId));
|
|
17
|
+
}
|
|
18
|
+
export function completionArchivePath(resultsDir, runId) {
|
|
19
|
+
return path.join(resultsDir, ARCHIVE_DIR_NAME, safeRunFile(runId));
|
|
20
|
+
}
|
|
21
|
+
function nonEmptyString(value) {
|
|
22
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
23
|
+
}
|
|
24
|
+
function existingFile(value) {
|
|
25
|
+
const filePath = nonEmptyString(value);
|
|
26
|
+
if (!filePath)
|
|
27
|
+
return;
|
|
28
|
+
try {
|
|
29
|
+
return fs.statSync(filePath).isFile() ? filePath : undefined;
|
|
30
|
+
} catch {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function outputArtifactPath(child) {
|
|
35
|
+
if (!child.artifactPaths || typeof child.artifactPaths !== "object" || Array.isArray(child.artifactPaths))
|
|
36
|
+
return;
|
|
37
|
+
return existingFile(child.artifactPaths.outputPath);
|
|
38
|
+
}
|
|
39
|
+
export function writeCompletionArchive(resultsDir, runId, data, createdAt) {
|
|
40
|
+
const entries = [];
|
|
41
|
+
const results = Array.isArray(data.results) ? data.results : [];
|
|
42
|
+
for (let resultIndex = 0;resultIndex < results.length; resultIndex++) {
|
|
43
|
+
const value = results[resultIndex];
|
|
44
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
45
|
+
continue;
|
|
46
|
+
const child = value;
|
|
47
|
+
const agent = nonEmptyString(child.agent);
|
|
48
|
+
const artifactPath = outputArtifactPath(child);
|
|
49
|
+
if (artifactPath) {
|
|
50
|
+
entries.push({ ...agent ? { agent } : {}, resultIndex, source: "output-artifact", path: artifactPath });
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const sessionPath = existingFile(child.sessionFile);
|
|
54
|
+
if (sessionPath) {
|
|
55
|
+
entries.push({ ...agent ? { agent } : {}, resultIndex, source: "session", path: sessionPath });
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const output = nonEmptyString(child.output);
|
|
59
|
+
const error = nonEmptyString(child.error);
|
|
60
|
+
if (output || error) {
|
|
61
|
+
const bounded = utf8Tail([error ? `Error: ${error}` : undefined, output].filter(Boolean).join(`
|
|
62
|
+
`), ARCHIVE_TEXT_LIMIT_BYTES);
|
|
63
|
+
entries.push({
|
|
64
|
+
...agent ? { agent } : {},
|
|
65
|
+
resultIndex,
|
|
66
|
+
source: "result-tail",
|
|
67
|
+
text: bounded.text,
|
|
68
|
+
...bounded.truncated ? { truncated: true } : {}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (results.length === 0) {
|
|
73
|
+
const sessionPath = existingFile(data.sessionFile);
|
|
74
|
+
if (sessionPath)
|
|
75
|
+
entries.push({ source: "session", path: sessionPath });
|
|
76
|
+
}
|
|
77
|
+
if (entries.length === 0) {
|
|
78
|
+
const summary = nonEmptyString(data.summary);
|
|
79
|
+
if (summary) {
|
|
80
|
+
const bounded = utf8Tail(summary, ARCHIVE_TEXT_LIMIT_BYTES);
|
|
81
|
+
entries.push({ source: "result-tail", text: bounded.text, ...bounded.truncated ? { truncated: true } : {} });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const archive = { version: ARCHIVE_VERSION, runId, createdAt, entries };
|
|
85
|
+
const archivePath = completionArchivePath(resultsDir, runId);
|
|
86
|
+
writePrivateAtomicJson(archivePath, archive);
|
|
87
|
+
return archivePath;
|
|
88
|
+
}
|
|
89
|
+
function parseCompletion(value, runId) {
|
|
90
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
91
|
+
return;
|
|
92
|
+
const completion = value;
|
|
93
|
+
if (completion.runId !== runId)
|
|
94
|
+
return;
|
|
95
|
+
return completion;
|
|
96
|
+
}
|
|
97
|
+
function parseReplay(value) {
|
|
98
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
99
|
+
return;
|
|
100
|
+
const record = value;
|
|
101
|
+
if (record.version !== REPLAY_VERSION || typeof record.runId !== "string" || typeof record.sessionId !== "string" || typeof record.completedAt !== "number" || typeof record.expiresAt !== "number" || typeof record.archivePath !== "string")
|
|
102
|
+
return;
|
|
103
|
+
const completion = parseCompletion(record.completion, record.runId);
|
|
104
|
+
return completion ? { ...record, completion } : undefined;
|
|
105
|
+
}
|
|
106
|
+
function validateReplayRecord(resultsDir, runId, record) {
|
|
107
|
+
if (record.runId !== runId)
|
|
108
|
+
return;
|
|
109
|
+
const archivePath = completionArchivePath(resultsDir, runId);
|
|
110
|
+
return path.resolve(record.archivePath) === path.resolve(archivePath) ? { ...record, archivePath, completion: { ...record.completion, archivePath } } : undefined;
|
|
111
|
+
}
|
|
112
|
+
function runIdFromReplayFile(file) {
|
|
113
|
+
if (!file.endsWith(".json"))
|
|
114
|
+
return;
|
|
115
|
+
try {
|
|
116
|
+
const runId = decodeURIComponent(file.slice(0, -".json".length));
|
|
117
|
+
return safeRunFile(runId) === file ? runId : undefined;
|
|
118
|
+
} catch {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function removeBestEffort(filePath) {
|
|
123
|
+
try {
|
|
124
|
+
fs.rmSync(filePath, { force: true });
|
|
125
|
+
} catch {}
|
|
126
|
+
}
|
|
127
|
+
function parseArchive(value) {
|
|
128
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
129
|
+
return;
|
|
130
|
+
const archive = value;
|
|
131
|
+
if (archive.version !== ARCHIVE_VERSION || typeof archive.runId !== "string" || typeof archive.createdAt !== "number" || !Array.isArray(archive.entries))
|
|
132
|
+
return;
|
|
133
|
+
const entries = archive.entries.flatMap((value) => {
|
|
134
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
135
|
+
return [];
|
|
136
|
+
const entry = value;
|
|
137
|
+
if (entry.source !== "output-artifact" && entry.source !== "session" && entry.source !== "result-tail")
|
|
138
|
+
return [];
|
|
139
|
+
return [{
|
|
140
|
+
...typeof entry.agent === "string" ? { agent: entry.agent } : {},
|
|
141
|
+
...typeof entry.resultIndex === "number" && Number.isSafeInteger(entry.resultIndex) && entry.resultIndex >= 0 ? { resultIndex: entry.resultIndex } : {},
|
|
142
|
+
source: entry.source,
|
|
143
|
+
...typeof entry.path === "string" ? { path: entry.path } : {},
|
|
144
|
+
...typeof entry.text === "string" ? { text: entry.text } : {},
|
|
145
|
+
...entry.truncated === true ? { truncated: true } : {}
|
|
146
|
+
}];
|
|
147
|
+
});
|
|
148
|
+
return { version: ARCHIVE_VERSION, runId: archive.runId, createdAt: archive.createdAt, entries };
|
|
149
|
+
}
|
|
150
|
+
export function writeCompletionReplay(input) {
|
|
151
|
+
const archivePath = writeCompletionArchive(input.resultsDir, input.runId, input.data, input.now);
|
|
152
|
+
const completion = { ...input.completion, archivePath };
|
|
153
|
+
const record = {
|
|
154
|
+
version: REPLAY_VERSION,
|
|
155
|
+
runId: input.runId,
|
|
156
|
+
sessionId: input.sessionId,
|
|
157
|
+
completedAt: input.now,
|
|
158
|
+
expiresAt: input.now + input.ttlMs,
|
|
159
|
+
completion,
|
|
160
|
+
archivePath
|
|
161
|
+
};
|
|
162
|
+
writePrivateAtomicJson(completionReplayPath(input.resultsDir, input.runId), record);
|
|
163
|
+
cleanupCompletionReplayIfDue(input.resultsDir, input.now, input.ttlMs);
|
|
164
|
+
return record;
|
|
165
|
+
}
|
|
166
|
+
export function readCompletionReplay(resultsDir, runId, options = {}) {
|
|
167
|
+
const replayPath = completionReplayPath(resultsDir, runId);
|
|
168
|
+
let parsed;
|
|
169
|
+
try {
|
|
170
|
+
parsed = parseReplay(JSON.parse(fs.readFileSync(replayPath, "utf-8")));
|
|
171
|
+
} catch (error) {
|
|
172
|
+
if (error.code === "ENOENT")
|
|
173
|
+
return;
|
|
174
|
+
throw error;
|
|
175
|
+
}
|
|
176
|
+
if (!parsed)
|
|
177
|
+
return;
|
|
178
|
+
const safeRecord = validateReplayRecord(resultsDir, runId, parsed);
|
|
179
|
+
if (!safeRecord) {
|
|
180
|
+
removeBestEffort(replayPath);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
parsed = safeRecord;
|
|
184
|
+
if (options.sessionId !== undefined && parsed.sessionId !== options.sessionId)
|
|
185
|
+
return;
|
|
186
|
+
if (parsed.expiresAt <= (options.now ?? Date.now())) {
|
|
187
|
+
removeBestEffort(replayPath);
|
|
188
|
+
removeBestEffort(parsed.archivePath);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
return parsed;
|
|
192
|
+
}
|
|
193
|
+
export function readCompletionArchive(archivePath) {
|
|
194
|
+
try {
|
|
195
|
+
const archive = parseArchive(JSON.parse(fs.readFileSync(archivePath, "utf-8")));
|
|
196
|
+
if (!archive)
|
|
197
|
+
throw new Error("Completion archive is malformed.");
|
|
198
|
+
return archive;
|
|
199
|
+
} catch (error) {
|
|
200
|
+
if (error.code === "ENOENT")
|
|
201
|
+
return;
|
|
202
|
+
throw error;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
export function cleanupCompletionReplayIfDue(resultsDir, now, maxAgeMs, intervalMs = CLEANUP_INTERVAL_MS) {
|
|
206
|
+
const last = lastCleanupByResultsDir.get(resultsDir);
|
|
207
|
+
if (last !== undefined && now - last < intervalMs)
|
|
208
|
+
return false;
|
|
209
|
+
lastCleanupByResultsDir.set(resultsDir, now);
|
|
210
|
+
cleanupCompletionReplay(resultsDir, now, maxAgeMs);
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
export function cleanupCompletionReplay(resultsDir, now, maxAgeMs) {
|
|
214
|
+
const replayDir = path.join(resultsDir, REPLAY_DIR_NAME);
|
|
215
|
+
try {
|
|
216
|
+
for (const file of fs.readdirSync(replayDir)) {
|
|
217
|
+
const runId = runIdFromReplayFile(file);
|
|
218
|
+
if (!runId)
|
|
219
|
+
continue;
|
|
220
|
+
const filePath = path.join(replayDir, file);
|
|
221
|
+
try {
|
|
222
|
+
const record = parseReplay(JSON.parse(fs.readFileSync(filePath, "utf-8")));
|
|
223
|
+
const safeRecord = record ? validateReplayRecord(resultsDir, runId, record) : undefined;
|
|
224
|
+
if (record && !safeRecord) {
|
|
225
|
+
fs.rmSync(filePath, { force: true });
|
|
226
|
+
} else if (safeRecord && safeRecord.expiresAt <= now) {
|
|
227
|
+
fs.rmSync(filePath, { force: true });
|
|
228
|
+
fs.rmSync(safeRecord.archivePath, { force: true });
|
|
229
|
+
} else if (!record && now - fs.statSync(filePath).mtimeMs > maxAgeMs) {
|
|
230
|
+
fs.rmSync(filePath, { force: true });
|
|
231
|
+
}
|
|
232
|
+
} catch {}
|
|
233
|
+
}
|
|
234
|
+
} catch {}
|
|
235
|
+
const archiveDir = path.join(resultsDir, ARCHIVE_DIR_NAME);
|
|
236
|
+
try {
|
|
237
|
+
for (const file of fs.readdirSync(archiveDir)) {
|
|
238
|
+
const filePath = path.join(archiveDir, file);
|
|
239
|
+
try {
|
|
240
|
+
if (now - fs.statSync(filePath).mtimeMs > maxAgeMs)
|
|
241
|
+
fs.rmSync(filePath, { force: true });
|
|
242
|
+
} catch {}
|
|
243
|
+
}
|
|
244
|
+
} catch {}
|
|
245
|
+
}
|