@duckmind/dm-windows-x64 0.61.5 → 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
|
@@ -1,39 +1,18 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { DIRS } from "../../shared/types.js";
|
|
4
|
+
import { normalizeExtensionBindings } from "../shared/extension-bindings.js";
|
|
5
|
+
import { normalizeWorkflowLaneMetadata } from "../shared/lane-metadata.js";
|
|
6
|
+
import { validateAcceptanceInput } from "../shared/acceptance.js";
|
|
7
|
+
import { validateToolBudgetConfig } from "../shared/tool-budget.js";
|
|
8
|
+
import { intersectSubagentCapabilityCeilings, parseSubagentCapabilityCeiling } from "../shared/capability-ceiling.js";
|
|
9
|
+
import { validateRunFanoutBudgetDescriptor } from "../shared/run-fanout-budget.js";
|
|
6
10
|
import { reconcileAsyncRun } from "./stale-run-reconciler.js";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
const status = reconcileAsyncRun(input.target.asyncDir, { resultsDir: input.resultsDir, kill: input.kill, now: input.now }).status;
|
|
14
|
-
if (!status || status.state !== "running" || typeof status.pid !== "number") {
|
|
15
|
-
return { ok: false, message: `Async run ${asyncId} is live but no interrupt-capable runner pid was found.` };
|
|
16
|
-
}
|
|
17
|
-
try {
|
|
18
|
-
deliverInterruptRequest({
|
|
19
|
-
asyncDir: input.target.asyncDir,
|
|
20
|
-
pid: status.pid,
|
|
21
|
-
kill: input.kill,
|
|
22
|
-
signal: ASYNC_RESUME_INTERRUPT_SIGNAL,
|
|
23
|
-
now: input.now,
|
|
24
|
-
source: "async-resume"
|
|
25
|
-
});
|
|
26
|
-
const tracked = input.state?.asyncJobs.get(asyncId);
|
|
27
|
-
if (tracked) {
|
|
28
|
-
tracked.activityState = undefined;
|
|
29
|
-
tracked.updatedAt = input.now?.() ?? Date.now();
|
|
30
|
-
}
|
|
31
|
-
return { ok: true, asyncId };
|
|
32
|
-
} catch (error) {
|
|
33
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
34
|
-
return { ok: false, message: `Failed to interrupt async run ${asyncId}: ${message}` };
|
|
35
|
-
}
|
|
36
|
-
}
|
|
11
|
+
import { resultFilePath, resultPayloadPathForIndexedRun } from "./result-files.js";
|
|
12
|
+
import { canScanAsyncRunPrefix, MIN_SAFE_ASYNC_RUN_PREFIX_LENGTH } from "./run-id-query.js";
|
|
13
|
+
import { parallelHandoffPath, resolveRetainedWorktreeCwd } from "../shared/parallel-handoff.js";
|
|
14
|
+
import { intersectThinkingCeilings, parseThinkingLevel } from "../../shared/thinking-ceiling.js";
|
|
15
|
+
import { assertWorkflowGraphHostSteps } from "../shared/host-step-status.js";
|
|
37
16
|
function getErrorMessage(error) {
|
|
38
17
|
return error instanceof Error ? error.message : String(error);
|
|
39
18
|
}
|
|
@@ -62,11 +41,16 @@ function validateResultFile(value, resultPath) {
|
|
|
62
41
|
const child = ensureObject(entry, `${resultPath} results[${index}]`);
|
|
63
42
|
const agent = validateOptionalString(child, "agent", resultPath, `results[${index}].agent`);
|
|
64
43
|
const sessionFile = validateOptionalString(child, "sessionFile", resultPath, `results[${index}].sessionFile`);
|
|
44
|
+
const sessionName = validateOptionalString(child, "sessionName", resultPath, `results[${index}].sessionName`);
|
|
65
45
|
const intercomTarget = validateOptionalString(child, "intercomTarget", resultPath, `results[${index}].intercomTarget`);
|
|
46
|
+
const model = validateOptionalString(child, "model", resultPath, `results[${index}].model`);
|
|
47
|
+
const thinking = validateOptionalString(child, "thinking", resultPath, `results[${index}].thinking`);
|
|
48
|
+
const launchContractDigest = validateOptionalString(child, "launchContractDigest", resultPath, `results[${index}].launchContractDigest`);
|
|
49
|
+
const capabilityCeiling = child.capabilityCeiling === undefined ? undefined : parseSubagentCapabilityCeiling(child.capabilityCeiling, `async result file '${resultPath}' results[${index}].capabilityCeiling`);
|
|
66
50
|
const success = child.success;
|
|
67
51
|
if (success !== undefined && typeof success !== "boolean")
|
|
68
52
|
throw new Error(`Invalid async result file '${resultPath}': results[${index}].success must be a boolean.`);
|
|
69
|
-
return { agent, sessionFile, intercomTarget, ...typeof success === "boolean" ? { success } : {} };
|
|
53
|
+
return { agent, sessionName, sessionFile, intercomTarget, model, thinking, launchContractDigest, ...capabilityCeiling ? { capabilityCeiling } : {}, ...typeof success === "boolean" ? { success } : {} };
|
|
70
54
|
});
|
|
71
55
|
}
|
|
72
56
|
const success = data.success;
|
|
@@ -79,7 +63,12 @@ function validateResultFile(value, resultPath) {
|
|
|
79
63
|
mode: validateOptionalString(data, "mode", resultPath),
|
|
80
64
|
state: validateOptionalString(data, "state", resultPath),
|
|
81
65
|
cwd: validateOptionalString(data, "cwd", resultPath),
|
|
66
|
+
sessionId: validateOptionalString(data, "sessionId", resultPath),
|
|
82
67
|
sessionFile: validateOptionalString(data, "sessionFile", resultPath),
|
|
68
|
+
model: validateOptionalString(data, "model", resultPath),
|
|
69
|
+
thinking: validateOptionalString(data, "thinking", resultPath),
|
|
70
|
+
launchContractDigest: validateOptionalString(data, "launchContractDigest", resultPath),
|
|
71
|
+
...data.capabilityCeiling === undefined ? {} : { capabilityCeiling: parseSubagentCapabilityCeiling(data.capabilityCeiling, `async result file '${resultPath}' capabilityCeiling`) },
|
|
83
72
|
...typeof success === "boolean" ? { success } : {},
|
|
84
73
|
...results ? { results } : {}
|
|
85
74
|
};
|
|
@@ -128,7 +117,10 @@ function prefixedRunIds(dir, prefix, suffix = "") {
|
|
|
128
117
|
return fs.readdirSync(dir).filter((entry) => entry.startsWith(prefix) && (!suffix || entry.endsWith(suffix))).map((entry) => suffix ? entry.slice(0, -suffix.length) : entry).sort();
|
|
129
118
|
}
|
|
130
119
|
function exactResultPath(resultsDir, runId) {
|
|
131
|
-
const
|
|
120
|
+
const indexed = resultPayloadPathForIndexedRun(resultsDir, runId);
|
|
121
|
+
if (indexed)
|
|
122
|
+
return indexed;
|
|
123
|
+
const resultPath = resultFilePath(resultsDir, runId);
|
|
132
124
|
assertInsideRoot(resultsDir, resultPath, "Async result file");
|
|
133
125
|
return fs.existsSync(resultPath) ? resultPath : null;
|
|
134
126
|
}
|
|
@@ -136,12 +128,11 @@ export function findAsyncRunPrefixMatches(prefix, asyncDirRoot, resultsDir) {
|
|
|
136
128
|
const requestedId = assertRunId(prefix, "id");
|
|
137
129
|
if (!requestedId)
|
|
138
130
|
return [];
|
|
131
|
+
if (!canScanAsyncRunPrefix(requestedId))
|
|
132
|
+
return [];
|
|
139
133
|
const asyncRoot = path.resolve(asyncDirRoot);
|
|
140
134
|
const resultRoot = path.resolve(resultsDir);
|
|
141
|
-
const matchingIds =
|
|
142
|
-
...prefixedRunIds(asyncRoot, requestedId),
|
|
143
|
-
...prefixedRunIds(resultRoot, requestedId, ".json")
|
|
144
|
-
])].sort();
|
|
135
|
+
const matchingIds = prefixedRunIds(asyncRoot, requestedId).sort();
|
|
145
136
|
return matchingIds.map((id) => {
|
|
146
137
|
const asyncDir = path.join(asyncRoot, id);
|
|
147
138
|
assertInsideRoot(asyncRoot, asyncDir, "Async run directory");
|
|
@@ -180,6 +171,11 @@ export function resolveAsyncRunLocation(params, asyncDirRoot, resultsDir) {
|
|
|
180
171
|
resolvedId: requestedId
|
|
181
172
|
};
|
|
182
173
|
}
|
|
174
|
+
if (requestedId.length < MIN_SAFE_ASYNC_RUN_PREFIX_LENGTH) {
|
|
175
|
+
throw new Error(`Async run id prefix '${requestedId}' is too short. Provide at least ${MIN_SAFE_ASYNC_RUN_PREFIX_LENGTH} characters.`);
|
|
176
|
+
}
|
|
177
|
+
if (!canScanAsyncRunPrefix(requestedId))
|
|
178
|
+
return { asyncDir: null, resultPath: null, resolvedId: requestedId };
|
|
183
179
|
const matching = findAsyncRunPrefixMatches(requestedId, asyncRoot, resultRoot);
|
|
184
180
|
if (matching.length === 0)
|
|
185
181
|
return { asyncDir: null, resultPath: null, resolvedId: requestedId };
|
|
@@ -189,7 +185,7 @@ export function resolveAsyncRunLocation(params, asyncDirRoot, resultsDir) {
|
|
|
189
185
|
return matching[0].location;
|
|
190
186
|
}
|
|
191
187
|
function resultState(result) {
|
|
192
|
-
if (result.state === "complete" || result.state === "failed" || result.state === "paused" || result.state === "running" || result.state === "queued") {
|
|
188
|
+
if (result.state === "complete" || result.state === "failed" || result.state === "partial" || result.state === "paused" || result.state === "stopped" || result.state === "running" || result.state === "queued") {
|
|
193
189
|
return result.state;
|
|
194
190
|
}
|
|
195
191
|
return result.success ? "complete" : "failed";
|
|
@@ -199,25 +195,269 @@ function validateStatusForResume(status, source) {
|
|
|
199
195
|
return;
|
|
200
196
|
if (typeof status.runId !== "string")
|
|
201
197
|
throw new Error(`Invalid async status '${source}': runId must be a string.`);
|
|
198
|
+
assertWorkflowGraphHostSteps(status.workflowGraph, source, status.runId);
|
|
202
199
|
if (status.sessionId !== undefined && typeof status.sessionId !== "string")
|
|
203
200
|
throw new Error(`Invalid async status '${source}': sessionId must be a string.`);
|
|
204
201
|
if (status.cwd !== undefined && typeof status.cwd !== "string")
|
|
205
202
|
throw new Error(`Invalid async status '${source}': cwd must be a string.`);
|
|
206
203
|
if (status.sessionFile !== undefined && typeof status.sessionFile !== "string")
|
|
207
204
|
throw new Error(`Invalid async status '${source}': sessionFile must be a string.`);
|
|
205
|
+
if (status.capabilityCeiling !== undefined)
|
|
206
|
+
status.capabilityCeiling = parseSubagentCapabilityCeiling(status.capabilityCeiling, `async status '${source}' capabilityCeiling`);
|
|
208
207
|
if (status.steps !== undefined) {
|
|
209
208
|
if (!Array.isArray(status.steps))
|
|
210
209
|
throw new Error(`Invalid async status '${source}': steps must be an array.`);
|
|
211
210
|
status.steps.forEach((step, index) => {
|
|
212
211
|
if (!step || typeof step !== "object" || Array.isArray(step))
|
|
213
212
|
throw new Error(`Invalid async status '${source}': steps[${index}] must be an object.`);
|
|
214
|
-
|
|
213
|
+
const stepRecord = step;
|
|
214
|
+
if (typeof stepRecord.agent !== "string")
|
|
215
215
|
throw new Error(`Invalid async status '${source}': steps[${index}].agent must be a string.`);
|
|
216
|
-
if (
|
|
216
|
+
if (stepRecord.sessionFile !== undefined && typeof stepRecord.sessionFile !== "string")
|
|
217
217
|
throw new Error(`Invalid async status '${source}': steps[${index}].sessionFile must be a string.`);
|
|
218
|
+
if (stepRecord.sessionName !== undefined && typeof stepRecord.sessionName !== "string")
|
|
219
|
+
throw new Error(`Invalid async status '${source}': steps[${index}].sessionName must be a string.`);
|
|
220
|
+
if (stepRecord.model !== undefined && typeof stepRecord.model !== "string")
|
|
221
|
+
throw new Error(`Invalid async status '${source}': steps[${index}].model must be a string.`);
|
|
222
|
+
if (stepRecord.thinking !== undefined && typeof stepRecord.thinking !== "string")
|
|
223
|
+
throw new Error(`Invalid async status '${source}': steps[${index}].thinking must be a string.`);
|
|
224
|
+
if (stepRecord.thinkingCeiling !== undefined)
|
|
225
|
+
stepRecord.thinkingCeiling = parseThinkingLevel(stepRecord.thinkingCeiling, `async status '${source}' steps[${index}].thinkingCeiling`);
|
|
226
|
+
if (stepRecord.launchContractDigest !== undefined && typeof stepRecord.launchContractDigest !== "string")
|
|
227
|
+
throw new Error(`Invalid async status '${source}': steps[${index}].launchContractDigest must be a string.`);
|
|
228
|
+
if (stepRecord.capabilityCeiling !== undefined)
|
|
229
|
+
stepRecord.capabilityCeiling = parseSubagentCapabilityCeiling(stepRecord.capabilityCeiling, `async status '${source}' steps[${index}].capabilityCeiling`);
|
|
218
230
|
});
|
|
219
231
|
}
|
|
220
232
|
}
|
|
233
|
+
function normalizeRecoveryAcceptance(value, descriptorPath) {
|
|
234
|
+
const errors = validateAcceptanceInput(value, "recoveryDescriptor.acceptance");
|
|
235
|
+
if (errors.length)
|
|
236
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': ${errors.join(" ")}`);
|
|
237
|
+
return value;
|
|
238
|
+
}
|
|
239
|
+
export function asyncReviveRequiresRecoveryDescriptor(target) {
|
|
240
|
+
if (target.recoveryDescriptor)
|
|
241
|
+
return false;
|
|
242
|
+
return !(target.mode === "workflow" && Boolean(target.sessionFile));
|
|
243
|
+
}
|
|
244
|
+
function resumeTargetMode(status, result) {
|
|
245
|
+
if (status?.mode)
|
|
246
|
+
return status.mode;
|
|
247
|
+
if (result?.mode === "single" || result?.mode === "parallel" || result?.mode === "chain" || result?.mode === "workflow")
|
|
248
|
+
return result.mode;
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
export function readAsyncRecoveryDescriptor(asyncDir) {
|
|
252
|
+
if (!asyncDir)
|
|
253
|
+
return;
|
|
254
|
+
const descriptorPath = path.join(asyncDir, "recovery-descriptor.json");
|
|
255
|
+
if (!fs.existsSync(descriptorPath))
|
|
256
|
+
return;
|
|
257
|
+
let value;
|
|
258
|
+
try {
|
|
259
|
+
value = JSON.parse(fs.readFileSync(descriptorPath, "utf-8"));
|
|
260
|
+
} catch (error) {
|
|
261
|
+
throw new Error(`Failed to parse async recovery descriptor '${descriptorPath}': ${getErrorMessage(error)}`, { cause: error instanceof Error ? error : undefined });
|
|
262
|
+
}
|
|
263
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
264
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': expected an object.`);
|
|
265
|
+
const parsed = value;
|
|
266
|
+
const allowedFields = new Set([
|
|
267
|
+
"version",
|
|
268
|
+
"launchContractDigest",
|
|
269
|
+
"sourceRunId",
|
|
270
|
+
"agentContract",
|
|
271
|
+
"agent",
|
|
272
|
+
"sessionFile",
|
|
273
|
+
"cwd",
|
|
274
|
+
"model",
|
|
275
|
+
"modelProvider",
|
|
276
|
+
"modelOverrideFromParent",
|
|
277
|
+
"fallbackModels",
|
|
278
|
+
"thinking",
|
|
279
|
+
"thinkingCeiling",
|
|
280
|
+
"tools",
|
|
281
|
+
"allowNestedSubagents",
|
|
282
|
+
"extensions",
|
|
283
|
+
"subagentOnlyExtensions",
|
|
284
|
+
"mcpDirectTools",
|
|
285
|
+
"mutationTools",
|
|
286
|
+
"systemPrompt",
|
|
287
|
+
"systemPromptMode",
|
|
288
|
+
"inheritProjectContext",
|
|
289
|
+
"inheritGlobalContext",
|
|
290
|
+
"inheritSkills",
|
|
291
|
+
"skills",
|
|
292
|
+
"skillPath",
|
|
293
|
+
"agentFilePath",
|
|
294
|
+
"completionGuard",
|
|
295
|
+
"memory",
|
|
296
|
+
"outputPath",
|
|
297
|
+
"outputMode",
|
|
298
|
+
"structuredOutputSchema",
|
|
299
|
+
"acceptance",
|
|
300
|
+
"sessionDir",
|
|
301
|
+
"artifactConfig",
|
|
302
|
+
"artifactsDir",
|
|
303
|
+
"maxOutput",
|
|
304
|
+
"controlConfig",
|
|
305
|
+
"context",
|
|
306
|
+
"intercomBridge",
|
|
307
|
+
"absoluteDeadlineAt",
|
|
308
|
+
"initialTurnBudget",
|
|
309
|
+
"initialToolBudget",
|
|
310
|
+
"maxSubagentDepth",
|
|
311
|
+
"share",
|
|
312
|
+
"capabilityCeiling",
|
|
313
|
+
"launchResolvedExtensions",
|
|
314
|
+
"runFanoutBudget",
|
|
315
|
+
"lane",
|
|
316
|
+
"extensionBindings"
|
|
317
|
+
]);
|
|
318
|
+
for (const field of Object.keys(parsed)) {
|
|
319
|
+
if (!allowedFields.has(field))
|
|
320
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': unknown field '${field}'.`);
|
|
321
|
+
}
|
|
322
|
+
const requiredStrings = ["sourceRunId", "agent", "cwd", "systemPromptMode", "outputMode"];
|
|
323
|
+
for (const field of requiredStrings) {
|
|
324
|
+
if (typeof parsed[field] !== "string" || !parsed[field].trim())
|
|
325
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': ${field} must be a non-empty string.`);
|
|
326
|
+
}
|
|
327
|
+
if (parsed.version !== 1)
|
|
328
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': version must be 1.`);
|
|
329
|
+
try {
|
|
330
|
+
parsed.runFanoutBudget = validateRunFanoutBudgetDescriptor(parsed.runFanoutBudget);
|
|
331
|
+
} catch (error) {
|
|
332
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': ${error instanceof Error ? error.message : String(error)}`);
|
|
333
|
+
}
|
|
334
|
+
if (parsed.capabilityCeiling !== undefined)
|
|
335
|
+
parsed.capabilityCeiling = parseSubagentCapabilityCeiling(parsed.capabilityCeiling, `async recovery descriptor '${descriptorPath}' capabilityCeiling`);
|
|
336
|
+
if (parsed.thinkingCeiling !== undefined)
|
|
337
|
+
parsed.thinkingCeiling = parseThinkingLevel(parsed.thinkingCeiling, `async recovery descriptor '${descriptorPath}' thinkingCeiling`);
|
|
338
|
+
if (parsed.extensionBindings !== undefined)
|
|
339
|
+
parsed.extensionBindings = normalizeExtensionBindings(parsed.extensionBindings).value;
|
|
340
|
+
if (parsed.lane !== undefined)
|
|
341
|
+
parsed.lane = normalizeWorkflowLaneMetadata(parsed.lane, `Invalid async recovery descriptor '${descriptorPath}': lane`);
|
|
342
|
+
if (parsed.agentContract !== undefined) {
|
|
343
|
+
if (!parsed.agentContract || typeof parsed.agentContract !== "object" || Array.isArray(parsed.agentContract))
|
|
344
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': agentContract must be an object.`);
|
|
345
|
+
const contract = parsed.agentContract;
|
|
346
|
+
if (contract.version !== 1 || Object.keys(contract).some((key) => key !== "version"))
|
|
347
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': agentContract must be { version: 1 }.`);
|
|
348
|
+
}
|
|
349
|
+
if (parsed.systemPromptMode !== "append" && parsed.systemPromptMode !== "replace")
|
|
350
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': systemPromptMode is invalid.`);
|
|
351
|
+
if (parsed.outputMode !== "inline" && parsed.outputMode !== "file-only")
|
|
352
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': outputMode is invalid.`);
|
|
353
|
+
if (parsed.context !== undefined && parsed.context !== "fresh" && parsed.context !== "fork")
|
|
354
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': context is invalid.`);
|
|
355
|
+
if (parsed.modelOverrideFromParent !== undefined && typeof parsed.modelOverrideFromParent !== "boolean")
|
|
356
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': modelOverrideFromParent must be a boolean.`);
|
|
357
|
+
for (const field of ["inheritProjectContext", "inheritSkills", "share"]) {
|
|
358
|
+
if (typeof parsed[field] !== "boolean")
|
|
359
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': ${field} must be a boolean.`);
|
|
360
|
+
}
|
|
361
|
+
if (parsed.inheritGlobalContext === undefined)
|
|
362
|
+
parsed.inheritGlobalContext = parsed.inheritProjectContext;
|
|
363
|
+
else if (typeof parsed.inheritGlobalContext !== "boolean")
|
|
364
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': inheritGlobalContext must be a boolean.`);
|
|
365
|
+
if (parsed.allowNestedSubagents !== undefined && typeof parsed.allowNestedSubagents !== "boolean")
|
|
366
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': allowNestedSubagents must be a boolean.`);
|
|
367
|
+
if (!Number.isInteger(parsed.maxSubagentDepth) || parsed.maxSubagentDepth < 0)
|
|
368
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': maxSubagentDepth must be a non-negative integer.`);
|
|
369
|
+
for (const field of ["fallbackModels", "tools", "extensions", "subagentOnlyExtensions", "mcpDirectTools", "mutationTools", "skills", "skillPath"]) {
|
|
370
|
+
const item = parsed[field];
|
|
371
|
+
if (item !== undefined && (!Array.isArray(item) || item.some((entry) => typeof entry !== "string" || !entry.trim())))
|
|
372
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': ${field} must contain non-empty strings.`);
|
|
373
|
+
}
|
|
374
|
+
if (parsed.systemPrompt !== undefined && typeof parsed.systemPrompt !== "string")
|
|
375
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': systemPrompt must be a string.`);
|
|
376
|
+
for (const field of ["launchContractDigest", "sessionFile", "model", "modelProvider", "thinking", "agentFilePath", "outputPath", "sessionDir", "artifactsDir"]) {
|
|
377
|
+
if (parsed[field] !== undefined && (typeof parsed[field] !== "string" || !parsed[field].trim()))
|
|
378
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': ${field} must be a non-empty string.`);
|
|
379
|
+
}
|
|
380
|
+
if (parsed.completionGuard !== undefined && typeof parsed.completionGuard !== "boolean")
|
|
381
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': completionGuard must be a boolean.`);
|
|
382
|
+
if (parsed.structuredOutputSchema !== undefined && (!parsed.structuredOutputSchema || typeof parsed.structuredOutputSchema !== "object" || Array.isArray(parsed.structuredOutputSchema)))
|
|
383
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': structuredOutputSchema must be an object.`);
|
|
384
|
+
if (parsed.memory !== undefined) {
|
|
385
|
+
if (!parsed.memory || typeof parsed.memory !== "object" || Array.isArray(parsed.memory))
|
|
386
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': memory must be an object.`);
|
|
387
|
+
const memory = parsed.memory;
|
|
388
|
+
if (memory.scope !== "project" && memory.scope !== "user" || typeof memory.path !== "string" || !memory.path.trim())
|
|
389
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': memory is invalid.`);
|
|
390
|
+
}
|
|
391
|
+
if (parsed.absoluteDeadlineAt !== undefined && (!Number.isFinite(parsed.absoluteDeadlineAt) || parsed.absoluteDeadlineAt <= 0))
|
|
392
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': absoluteDeadlineAt must be a positive timestamp.`);
|
|
393
|
+
if (parsed.initialTurnBudget !== undefined) {
|
|
394
|
+
delete parsed.initialTurnBudget;
|
|
395
|
+
}
|
|
396
|
+
if (parsed.initialToolBudget !== undefined) {
|
|
397
|
+
const result = validateToolBudgetConfig(parsed.initialToolBudget, "recoveryDescriptor.initialToolBudget");
|
|
398
|
+
if (result.error)
|
|
399
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': ${result.error}`);
|
|
400
|
+
}
|
|
401
|
+
if (parsed.maxOutput !== undefined) {
|
|
402
|
+
if (!parsed.maxOutput || typeof parsed.maxOutput !== "object" || Array.isArray(parsed.maxOutput))
|
|
403
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': maxOutput must be an object.`);
|
|
404
|
+
for (const field of ["bytes", "lines"]) {
|
|
405
|
+
const item = parsed.maxOutput[field];
|
|
406
|
+
if (item !== undefined && (!Number.isInteger(item) || item < 1))
|
|
407
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': maxOutput.${field} must be a positive integer.`);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
if (parsed.artifactConfig !== undefined) {
|
|
411
|
+
if (!parsed.artifactConfig || typeof parsed.artifactConfig !== "object" || Array.isArray(parsed.artifactConfig))
|
|
412
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': artifactConfig must be an object.`);
|
|
413
|
+
const artifact = parsed.artifactConfig;
|
|
414
|
+
for (const field of ["enabled", "includeInput", "includeOutput", "includeJsonl", "includeMetadata"]) {
|
|
415
|
+
if (typeof artifact[field] !== "boolean")
|
|
416
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': artifactConfig.${field} must be a boolean.`);
|
|
417
|
+
}
|
|
418
|
+
if (artifact.includeTranscript !== undefined && typeof artifact.includeTranscript !== "boolean")
|
|
419
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': artifactConfig.includeTranscript must be a boolean.`);
|
|
420
|
+
if (!Number.isInteger(artifact.cleanupDays) || artifact.cleanupDays < 0)
|
|
421
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': artifactConfig.cleanupDays must be a non-negative integer.`);
|
|
422
|
+
}
|
|
423
|
+
if (parsed.intercomBridge !== undefined) {
|
|
424
|
+
if (!parsed.intercomBridge || typeof parsed.intercomBridge !== "object" || Array.isArray(parsed.intercomBridge))
|
|
425
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': intercomBridge must be an object.`);
|
|
426
|
+
const bridge = parsed.intercomBridge;
|
|
427
|
+
for (const field of Object.keys(bridge)) {
|
|
428
|
+
if (field !== "mode" && field !== "instructionFile" && field !== "resultDelivery")
|
|
429
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': intercomBridge.${field} is not supported.`);
|
|
430
|
+
}
|
|
431
|
+
if (bridge.mode !== undefined && bridge.mode !== "off" && bridge.mode !== "fork-only" && bridge.mode !== "always")
|
|
432
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': intercomBridge.mode is invalid.`);
|
|
433
|
+
if (bridge.instructionFile !== undefined && typeof bridge.instructionFile !== "string")
|
|
434
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': intercomBridge.instructionFile must be a string.`);
|
|
435
|
+
if (bridge.resultDelivery !== undefined && typeof bridge.resultDelivery !== "boolean")
|
|
436
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': intercomBridge.resultDelivery must be a boolean.`);
|
|
437
|
+
}
|
|
438
|
+
if (parsed.controlConfig !== undefined) {
|
|
439
|
+
if (!parsed.controlConfig || typeof parsed.controlConfig !== "object" || Array.isArray(parsed.controlConfig))
|
|
440
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': controlConfig must be an object.`);
|
|
441
|
+
const control = parsed.controlConfig;
|
|
442
|
+
if (typeof control.enabled !== "boolean")
|
|
443
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': controlConfig.enabled must be a boolean.`);
|
|
444
|
+
for (const field of ["needsAttentionAfterMs", "activeNoticeAfterMs", "failedToolAttemptsBeforeAttention"]) {
|
|
445
|
+
if (!Number.isInteger(control[field]) || control[field] < 1)
|
|
446
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': controlConfig.${field} must be a positive integer.`);
|
|
447
|
+
}
|
|
448
|
+
for (const field of ["activeNoticeAfterTurns", "activeNoticeAfterTokens"]) {
|
|
449
|
+
if (control[field] !== undefined && (!Number.isInteger(control[field]) || control[field] < 1))
|
|
450
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': controlConfig.${field} must be a positive integer.`);
|
|
451
|
+
}
|
|
452
|
+
if (!Array.isArray(control.notifyOn) || control.notifyOn.some((item) => item !== "active_long_running" && item !== "needs_attention"))
|
|
453
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': controlConfig.notifyOn is invalid.`);
|
|
454
|
+
if (!Array.isArray(control.notifyChannels) || control.notifyChannels.some((item) => item !== "event" && item !== "async" && item !== "intercom"))
|
|
455
|
+
throw new Error(`Invalid async recovery descriptor '${descriptorPath}': controlConfig.notifyChannels is invalid.`);
|
|
456
|
+
}
|
|
457
|
+
if (parsed.acceptance !== undefined)
|
|
458
|
+
parsed.acceptance = normalizeRecoveryAcceptance(parsed.acceptance, descriptorPath);
|
|
459
|
+
return parsed;
|
|
460
|
+
}
|
|
221
461
|
function validateResumeSessionFile(runId, sessionFile) {
|
|
222
462
|
if (path.extname(sessionFile) !== ".jsonl")
|
|
223
463
|
throw new Error(`Async run '${runId}' session file must be a .jsonl file: ${sessionFile}`);
|
|
@@ -226,9 +466,21 @@ function validateResumeSessionFile(runId, sessionFile) {
|
|
|
226
466
|
throw new Error(`Async run '${runId}' session file does not exist: ${sessionFile}`);
|
|
227
467
|
return resolved;
|
|
228
468
|
}
|
|
469
|
+
function validateResumeCwd(runId, cwd) {
|
|
470
|
+
if (!cwd)
|
|
471
|
+
return;
|
|
472
|
+
const resolved = path.resolve(cwd);
|
|
473
|
+
try {
|
|
474
|
+
if (!fs.statSync(resolved).isDirectory())
|
|
475
|
+
throw new Error("path is not a directory");
|
|
476
|
+
} catch (error) {
|
|
477
|
+
throw new Error(`Async run '${runId}' required cwd does not exist: ${cwd}`, { cause: error instanceof Error ? error : undefined });
|
|
478
|
+
}
|
|
479
|
+
return resolved;
|
|
480
|
+
}
|
|
229
481
|
export function resolveAsyncResumeTarget(params, deps = {}, options = {}) {
|
|
230
|
-
const asyncDirRoot = deps.asyncDirRoot ??
|
|
231
|
-
const resultsDir = deps.resultsDir ??
|
|
482
|
+
const asyncDirRoot = deps.asyncDirRoot ?? DIRS.async;
|
|
483
|
+
const resultsDir = deps.resultsDir ?? DIRS.results;
|
|
232
484
|
const requireSessionFile = options.requireSessionFile ?? true;
|
|
233
485
|
const location = resolveAsyncRunLocation(params, asyncDirRoot, resultsDir);
|
|
234
486
|
if (!location.asyncDir && !location.resultPath) {
|
|
@@ -237,11 +489,20 @@ export function resolveAsyncResumeTarget(params, deps = {}, options = {}) {
|
|
|
237
489
|
const reconciliation = location.asyncDir ? reconcileAsyncRun(location.asyncDir, { resultsDir, kill: deps.kill, now: deps.now }) : undefined;
|
|
238
490
|
const status = reconciliation?.status ?? null;
|
|
239
491
|
validateStatusForResume(status, location.asyncDir ? path.join(location.asyncDir, "status.json") : "status.json");
|
|
492
|
+
const recoveryDescriptor = readAsyncRecoveryDescriptor(location.asyncDir ?? undefined);
|
|
240
493
|
const result = location.resultPath ? readResultFile(location.resultPath) : undefined;
|
|
241
494
|
const runId = status?.runId ?? result?.runId ?? result?.id ?? location.resolvedId ?? (location.asyncDir ? path.basename(location.asyncDir) : "unknown");
|
|
495
|
+
const mode = resumeTargetMode(status, result);
|
|
496
|
+
if (options.sessionId && (status && status.sessionId !== options.sessionId || result && result.sessionId !== options.sessionId)) {
|
|
497
|
+
throw new Error(`Async run '${runId}' was not found in the active session.`);
|
|
498
|
+
}
|
|
499
|
+
if (recoveryDescriptor && recoveryDescriptor.sourceRunId !== runId)
|
|
500
|
+
throw new Error(`Async run '${runId}' has a recovery descriptor for a different source run.`);
|
|
242
501
|
const state = status?.state ?? (result ? resultState(result) : undefined);
|
|
243
502
|
if (!state)
|
|
244
503
|
throw new Error(`Status file not found for async run '${runId}'.`);
|
|
504
|
+
if (state === "stopped")
|
|
505
|
+
throw new Error(`Async run '${runId}' was stopped and cannot be resumed. Start a new run instead.`);
|
|
245
506
|
const statusSteps = status?.steps ?? [];
|
|
246
507
|
const resultSteps = result?.results ?? [];
|
|
247
508
|
const stepCount = statusSteps.length || resultSteps.length || (result?.agent ? 1 : 0);
|
|
@@ -255,16 +516,26 @@ export function resolveAsyncResumeTarget(params, deps = {}, options = {}) {
|
|
|
255
516
|
throw new Error(`Async run '${runId}' has ${stepCount} children. Index ${requestedIndex} is out of range.`);
|
|
256
517
|
const selectedStep = statusSteps[requestedIndex];
|
|
257
518
|
if (selectedStep?.status === "running") {
|
|
519
|
+
const capabilityCeiling = intersectSubagentCapabilityCeilings(status?.capabilityCeiling, selectedStep.capabilityCeiling);
|
|
258
520
|
return {
|
|
259
521
|
kind: "live",
|
|
260
522
|
runId,
|
|
261
523
|
asyncDir: location.asyncDir ?? undefined,
|
|
262
524
|
state,
|
|
525
|
+
...mode ? { mode } : {},
|
|
263
526
|
agent: selectedStep.agent,
|
|
527
|
+
...selectedStep.sessionName ?? result?.results?.[requestedIndex]?.sessionName ? { sessionName: selectedStep.sessionName ?? result?.results?.[requestedIndex]?.sessionName } : {},
|
|
264
528
|
index: requestedIndex,
|
|
265
|
-
intercomTarget: resolveSubagentIntercomTarget(runId, selectedStep.agent, requestedIndex),
|
|
266
529
|
cwd: status?.cwd ?? result?.cwd,
|
|
267
|
-
sessionFile: selectedStep.sessionFile ?? status?.sessionFile ?? result?.sessionFile
|
|
530
|
+
sessionFile: selectedStep.sessionFile ?? status?.sessionFile ?? result?.sessionFile,
|
|
531
|
+
model: selectedStep.model,
|
|
532
|
+
thinking: selectedStep.thinking,
|
|
533
|
+
launchContractDigest: selectedStep.launchContractDigest ?? result?.results?.[requestedIndex]?.launchContractDigest ?? result?.launchContractDigest ?? recoveryDescriptor?.launchContractDigest,
|
|
534
|
+
...selectedStep.runner ? { runner: selectedStep.runner } : {},
|
|
535
|
+
...selectedStep.externalJob ? { externalJob: selectedStep.externalJob } : {},
|
|
536
|
+
...capabilityCeiling ? { capabilityCeiling } : {},
|
|
537
|
+
...selectedStep.thinkingCeiling ? { thinkingCeiling: selectedStep.thinkingCeiling } : {},
|
|
538
|
+
...recoveryDescriptor ? { recoveryDescriptor } : {}
|
|
268
539
|
};
|
|
269
540
|
}
|
|
270
541
|
if (selectedStep?.status === "pending")
|
|
@@ -277,16 +548,26 @@ export function resolveAsyncResumeTarget(params, deps = {}, options = {}) {
|
|
|
277
548
|
if (!selected) {
|
|
278
549
|
throw new Error(`Async run '${runId}' has ${running.length} running children. Provide index to choose one.`);
|
|
279
550
|
}
|
|
551
|
+
const capabilityCeiling = intersectSubagentCapabilityCeilings(status?.capabilityCeiling, selected.step.capabilityCeiling);
|
|
280
552
|
return {
|
|
281
553
|
kind: "live",
|
|
282
554
|
runId,
|
|
283
555
|
asyncDir: location.asyncDir ?? undefined,
|
|
284
556
|
state,
|
|
557
|
+
...mode ? { mode } : {},
|
|
285
558
|
agent: selected.step.agent,
|
|
559
|
+
...selected.step.sessionName ?? result?.results?.[selected.index]?.sessionName ? { sessionName: selected.step.sessionName ?? result?.results?.[selected.index]?.sessionName } : {},
|
|
286
560
|
index: selected.index,
|
|
287
|
-
intercomTarget: resolveSubagentIntercomTarget(runId, selected.step.agent, selected.index),
|
|
288
561
|
cwd: status?.cwd ?? result?.cwd,
|
|
289
|
-
sessionFile: selected.step.sessionFile ?? status?.sessionFile ?? result?.sessionFile
|
|
562
|
+
sessionFile: selected.step.sessionFile ?? status?.sessionFile ?? result?.sessionFile,
|
|
563
|
+
model: selected.step.model,
|
|
564
|
+
thinking: selected.step.thinking,
|
|
565
|
+
launchContractDigest: selected.step.launchContractDigest ?? result?.results?.[selected.index]?.launchContractDigest ?? result?.launchContractDigest ?? recoveryDescriptor?.launchContractDigest,
|
|
566
|
+
...selected.step.runner ? { runner: selected.step.runner } : {},
|
|
567
|
+
...selected.step.externalJob ? { externalJob: selected.step.externalJob } : {},
|
|
568
|
+
...capabilityCeiling ? { capabilityCeiling } : {},
|
|
569
|
+
...selected.step.thinkingCeiling ? { thinkingCeiling: selected.step.thinkingCeiling } : {},
|
|
570
|
+
...recoveryDescriptor ? { recoveryDescriptor } : {}
|
|
290
571
|
};
|
|
291
572
|
}
|
|
292
573
|
}
|
|
@@ -301,20 +582,66 @@ export function resolveAsyncResumeTarget(params, deps = {}, options = {}) {
|
|
|
301
582
|
const agent = statusSteps[index]?.agent ?? resultSteps[index]?.agent ?? result?.agent;
|
|
302
583
|
if (!agent)
|
|
303
584
|
throw new Error(`Could not determine child agent for async run '${runId}'.`);
|
|
585
|
+
if (recoveryDescriptor && recoveryDescriptor.agent !== agent)
|
|
586
|
+
throw new Error(`Async run '${runId}' has a recovery descriptor for '${recoveryDescriptor.agent}', not '${agent}'.`);
|
|
304
587
|
const sessionFile = statusSteps[index]?.sessionFile ?? resultSteps[index]?.sessionFile ?? (stepCount === 1 ? status?.sessionFile ?? result?.sessionFile : undefined);
|
|
305
588
|
if (!sessionFile && requireSessionFile)
|
|
306
589
|
throw new Error(`Async run '${runId}' child ${index} does not have a persisted session file to resume from.`);
|
|
307
590
|
const resolvedSessionFile = sessionFile ? validateResumeSessionFile(runId, sessionFile) : undefined;
|
|
591
|
+
const stepModel = statusSteps[index]?.model ?? resultSteps[index]?.model ?? (stepCount === 1 ? result?.model : undefined);
|
|
592
|
+
const stepThinking = statusSteps[index]?.thinking ?? resultSteps[index]?.thinking ?? (stepCount === 1 ? result?.thinking : undefined);
|
|
593
|
+
const thinkingCeiling = statusSteps[index]?.thinkingCeiling ?? (stepCount === 1 ? recoveryDescriptor?.thinkingCeiling : undefined);
|
|
594
|
+
const capabilityCeiling = intersectSubagentCapabilityCeilings(status?.capabilityCeiling, statusSteps[index]?.capabilityCeiling, result?.capabilityCeiling, resultSteps[index]?.capabilityCeiling);
|
|
595
|
+
const managedWorktreeCwd = location.asyncDir ? resolveRetainedWorktreeCwd(parallelHandoffPath(location.asyncDir), runId, index) : undefined;
|
|
596
|
+
const resumeCwd = validateResumeCwd(runId, managedWorktreeCwd ?? status?.cwd ?? result?.cwd ?? recoveryDescriptor?.cwd);
|
|
308
597
|
return {
|
|
309
598
|
kind: "revive",
|
|
310
599
|
runId,
|
|
311
600
|
asyncDir: location.asyncDir ?? undefined,
|
|
312
601
|
state,
|
|
602
|
+
...mode ? { mode } : {},
|
|
313
603
|
agent,
|
|
604
|
+
...statusSteps[index]?.sessionName ?? resultSteps[index]?.sessionName ? { sessionName: statusSteps[index]?.sessionName ?? resultSteps[index]?.sessionName } : {},
|
|
314
605
|
index,
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
...
|
|
606
|
+
...resumeCwd ? { cwd: resumeCwd } : {},
|
|
607
|
+
...resolvedSessionFile ? { sessionFile: resolvedSessionFile } : {},
|
|
608
|
+
...stepModel ? { model: stepModel } : {},
|
|
609
|
+
...stepThinking ? { thinking: stepThinking } : {},
|
|
610
|
+
launchContractDigest: statusSteps[index]?.launchContractDigest ?? resultSteps[index]?.launchContractDigest ?? result?.launchContractDigest ?? recoveryDescriptor?.launchContractDigest,
|
|
611
|
+
...statusSteps[index]?.runner ? { runner: statusSteps[index].runner } : {},
|
|
612
|
+
...statusSteps[index]?.externalJob ? { externalJob: statusSteps[index].externalJob } : {},
|
|
613
|
+
...capabilityCeiling ? { capabilityCeiling } : {},
|
|
614
|
+
...thinkingCeiling ? { thinkingCeiling } : {},
|
|
615
|
+
...recoveryDescriptor ? { recoveryDescriptor } : {}
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
export function applySteeringRecoveryAgentConfig(agentConfig, descriptor) {
|
|
619
|
+
return {
|
|
620
|
+
...agentConfig,
|
|
621
|
+
model: descriptor.model,
|
|
622
|
+
modelProvider: descriptor.modelProvider,
|
|
623
|
+
fallbackModels: descriptor.fallbackModels ? [...descriptor.fallbackModels] : undefined,
|
|
624
|
+
thinking: descriptor.thinking,
|
|
625
|
+
maxThinking: intersectThinkingCeilings(descriptor.thinkingCeiling, agentConfig.maxThinking),
|
|
626
|
+
tools: descriptor.tools ? [...descriptor.tools] : undefined,
|
|
627
|
+
allowNestedSubagents: descriptor.allowNestedSubagents,
|
|
628
|
+
extensions: descriptor.extensions ? [...descriptor.extensions] : undefined,
|
|
629
|
+
subagentOnlyExtensions: descriptor.subagentOnlyExtensions ? [...descriptor.subagentOnlyExtensions] : undefined,
|
|
630
|
+
mcpDirectTools: descriptor.mcpDirectTools ? [...descriptor.mcpDirectTools] : undefined,
|
|
631
|
+
mutationTools: descriptor.mutationTools ? [...descriptor.mutationTools] : undefined,
|
|
632
|
+
systemPrompt: descriptor.systemPrompt ?? agentConfig.systemPrompt,
|
|
633
|
+
systemPromptMode: descriptor.systemPromptMode,
|
|
634
|
+
inheritProjectContext: descriptor.inheritProjectContext,
|
|
635
|
+
inheritGlobalContext: descriptor.inheritGlobalContext,
|
|
636
|
+
inheritSkills: descriptor.inheritSkills,
|
|
637
|
+
skills: descriptor.skills ? [...descriptor.skills] : undefined,
|
|
638
|
+
skillPath: descriptor.skillPath ? [...descriptor.skillPath] : undefined,
|
|
639
|
+
filePath: descriptor.agentFilePath,
|
|
640
|
+
completionGuard: descriptor.completionGuard,
|
|
641
|
+
memory: descriptor.memory ? { ...descriptor.memory } : undefined,
|
|
642
|
+
output: descriptor.outputPath,
|
|
643
|
+
toolBudget: descriptor.initialToolBudget,
|
|
644
|
+
maxSubagentDepth: descriptor.maxSubagentDepth
|
|
318
645
|
};
|
|
319
646
|
}
|
|
320
647
|
export function buildRevivedAsyncTask(target, message) {
|