@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
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
export const BACKGROUND_WORK_PROTOCOL_VERSION = 1;
|
|
2
|
+
export const BACKGROUND_WORK_REGISTRY_KEY = "dm-subagents.background-work.v1";
|
|
3
|
+
const MAX_PROVIDER_NAME_LENGTH = 128;
|
|
4
|
+
const MAX_PROVIDERS = 100;
|
|
5
|
+
const MAX_ITEM_ID_LENGTH = 256;
|
|
6
|
+
const MAX_SESSION_ID_LENGTH = 4096;
|
|
7
|
+
const MAX_WAKE_CHANNEL_LENGTH = 256;
|
|
8
|
+
const MAX_ITEMS_PER_PROVIDER = 1e4;
|
|
9
|
+
function registry() {
|
|
10
|
+
const key = Symbol.for(BACKGROUND_WORK_REGISTRY_KEY);
|
|
11
|
+
const globalObject = globalThis;
|
|
12
|
+
const existing = globalObject[key];
|
|
13
|
+
if (existing === undefined) {
|
|
14
|
+
const created = {
|
|
15
|
+
version: BACKGROUND_WORK_PROTOCOL_VERSION,
|
|
16
|
+
providers: new Map
|
|
17
|
+
};
|
|
18
|
+
globalObject[key] = created;
|
|
19
|
+
return created;
|
|
20
|
+
}
|
|
21
|
+
if (!existing || typeof existing !== "object" || Array.isArray(existing)) {
|
|
22
|
+
throw new Error(`Malformed background-work registry at Symbol.for("${BACKGROUND_WORK_REGISTRY_KEY}").`);
|
|
23
|
+
}
|
|
24
|
+
const candidate = existing;
|
|
25
|
+
if (candidate.version !== BACKGROUND_WORK_PROTOCOL_VERSION || !(candidate.providers instanceof Map)) {
|
|
26
|
+
throw new Error(`Unsupported background-work registry at Symbol.for("${BACKGROUND_WORK_REGISTRY_KEY}").`);
|
|
27
|
+
}
|
|
28
|
+
return candidate;
|
|
29
|
+
}
|
|
30
|
+
function validateString(value, field, maxLength) {
|
|
31
|
+
if (typeof value !== "string" || value.length === 0 || value.trim() !== value) {
|
|
32
|
+
throw new Error(`${field} must be a non-empty string without leading or trailing whitespace.`);
|
|
33
|
+
}
|
|
34
|
+
if (value.length > maxLength)
|
|
35
|
+
throw new Error(`${field} must be at most ${maxLength} characters.`);
|
|
36
|
+
if (value.includes("\x00"))
|
|
37
|
+
throw new Error(`${field} must not contain NUL characters.`);
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
function validateProvider(value) {
|
|
41
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
42
|
+
throw new Error("Background-work provider must be an object.");
|
|
43
|
+
}
|
|
44
|
+
const provider = value;
|
|
45
|
+
const unknownFields = Object.keys(provider).filter((key) => !["name", "listActiveWork", "wakeChannels", "reconcile"].includes(key));
|
|
46
|
+
if (unknownFields.length > 0)
|
|
47
|
+
throw new Error(`Background-work provider has unknown fields: ${unknownFields.join(", ")}.`);
|
|
48
|
+
const name = validateString(provider.name, "Background-work provider name", MAX_PROVIDER_NAME_LENGTH);
|
|
49
|
+
if (typeof provider.listActiveWork !== "function") {
|
|
50
|
+
throw new Error(`Background-work provider '${name}' must expose listActiveWork().`);
|
|
51
|
+
}
|
|
52
|
+
if (provider.reconcile !== undefined && typeof provider.reconcile !== "function") {
|
|
53
|
+
throw new Error(`Background-work provider '${name}' reconcile must be a function when provided.`);
|
|
54
|
+
}
|
|
55
|
+
if (provider.wakeChannels !== undefined) {
|
|
56
|
+
if (!Array.isArray(provider.wakeChannels)) {
|
|
57
|
+
throw new Error(`Background-work provider '${name}' wakeChannels must be an array when provided.`);
|
|
58
|
+
}
|
|
59
|
+
const channels = provider.wakeChannels.map((channel, index) => validateString(channel, `Background-work provider '${name}' wakeChannels[${index}]`, MAX_WAKE_CHANNEL_LENGTH));
|
|
60
|
+
if (new Set(channels).size !== channels.length) {
|
|
61
|
+
throw new Error(`Background-work provider '${name}' wakeChannels must not contain duplicates.`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
function validateItem(provider, value, index) {
|
|
67
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
68
|
+
throw new Error(`Background-work provider '${provider}' item ${index} must be an object.`);
|
|
69
|
+
}
|
|
70
|
+
const item = value;
|
|
71
|
+
const unknownFields = Object.keys(item).filter((key) => key !== "id" && key !== "sessionId");
|
|
72
|
+
if (unknownFields.length > 0) {
|
|
73
|
+
throw new Error(`Background-work provider '${provider}' item ${index} has unknown fields: ${unknownFields.join(", ")}.`);
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
id: validateString(item.id, `Background-work provider '${provider}' item ${index} id`, MAX_ITEM_ID_LENGTH),
|
|
77
|
+
sessionId: validateString(item.sessionId, `Background-work provider '${provider}' item ${index} sessionId`, MAX_SESSION_ID_LENGTH)
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
export function registerBackgroundWorkProvider(provider) {
|
|
81
|
+
const validated = validateProvider(provider);
|
|
82
|
+
const current = registry();
|
|
83
|
+
if (!current.providers.has(validated.name) && current.providers.size >= MAX_PROVIDERS) {
|
|
84
|
+
throw new Error(`Background-work registry supports at most ${MAX_PROVIDERS} providers.`);
|
|
85
|
+
}
|
|
86
|
+
current.providers.set(validated.name, validated);
|
|
87
|
+
return () => {
|
|
88
|
+
if (current.providers.get(validated.name) === validated)
|
|
89
|
+
current.providers.delete(validated.name);
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
export function listBackgroundWorkProviders() {
|
|
93
|
+
const current = registry();
|
|
94
|
+
if (current.providers.size > MAX_PROVIDERS)
|
|
95
|
+
throw new Error(`Background-work registry contains more than ${MAX_PROVIDERS} providers.`);
|
|
96
|
+
const providers = [];
|
|
97
|
+
for (const [key, value] of current.providers) {
|
|
98
|
+
const provider = validateProvider(value);
|
|
99
|
+
if (key !== provider.name)
|
|
100
|
+
throw new Error(`Background-work registry key '${key}' does not match provider name '${provider.name}'.`);
|
|
101
|
+
providers.push(provider);
|
|
102
|
+
}
|
|
103
|
+
return providers;
|
|
104
|
+
}
|
|
105
|
+
export function listBackgroundWorkWakeChannels() {
|
|
106
|
+
const channels = new Set;
|
|
107
|
+
for (const provider of listBackgroundWorkProviders()) {
|
|
108
|
+
for (const channel of provider.wakeChannels ?? [])
|
|
109
|
+
channels.add(channel);
|
|
110
|
+
}
|
|
111
|
+
return [...channels];
|
|
112
|
+
}
|
|
113
|
+
export function snapshotBackgroundWork(sessionId, nowMs = Date.now()) {
|
|
114
|
+
validateString(sessionId, "Background-work snapshot sessionId", MAX_SESSION_ID_LENGTH);
|
|
115
|
+
const providers = listBackgroundWorkProviders();
|
|
116
|
+
const items = [];
|
|
117
|
+
const identities = new Set;
|
|
118
|
+
for (const provider of providers) {
|
|
119
|
+
try {
|
|
120
|
+
provider.reconcile?.({ sessionId, nowMs });
|
|
121
|
+
} catch (error) {
|
|
122
|
+
throw new Error(`Background-work provider '${provider.name}' reconcile failed: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
123
|
+
}
|
|
124
|
+
let active;
|
|
125
|
+
try {
|
|
126
|
+
active = provider.listActiveWork({ sessionId, nowMs });
|
|
127
|
+
} catch (error) {
|
|
128
|
+
throw new Error(`Background-work provider '${provider.name}' listActiveWork failed: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
129
|
+
}
|
|
130
|
+
if (!Array.isArray(active)) {
|
|
131
|
+
throw new Error(`Background-work provider '${provider.name}' listActiveWork() must return an array.`);
|
|
132
|
+
}
|
|
133
|
+
if (active.length > MAX_ITEMS_PER_PROVIDER) {
|
|
134
|
+
throw new Error(`Background-work provider '${provider.name}' returned ${active.length} items; maximum is ${MAX_ITEMS_PER_PROVIDER}.`);
|
|
135
|
+
}
|
|
136
|
+
active.forEach((value, index) => {
|
|
137
|
+
const item = validateItem(provider.name, value, index);
|
|
138
|
+
const identity = `${provider.name}\x00${item.sessionId}\x00${item.id}`;
|
|
139
|
+
if (identities.has(identity)) {
|
|
140
|
+
throw new Error(`Background-work provider '${provider.name}' returned duplicate item '${item.id}' for session '${item.sessionId}'.`);
|
|
141
|
+
}
|
|
142
|
+
identities.add(identity);
|
|
143
|
+
if (item.sessionId === sessionId)
|
|
144
|
+
items.push({ provider: provider.name, ...item });
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
providers: providers.map((provider) => provider.name),
|
|
149
|
+
items
|
|
150
|
+
};
|
|
151
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export {
|
|
2
|
+
SUBAGENT_CAPABILITY_CEILING_ENV,
|
|
3
|
+
SUBAGENT_CAPABILITY_CEILING_REGISTRY_KEY,
|
|
4
|
+
SUBAGENT_CAPABILITY_CEILING_VERSION,
|
|
5
|
+
decodeSubagentCapabilityCeiling,
|
|
6
|
+
encodeSubagentCapabilityCeiling,
|
|
7
|
+
intersectSubagentCapabilityCeilings,
|
|
8
|
+
parseSubagentCapabilityCeiling,
|
|
9
|
+
registerSubagentCapabilityCeiling,
|
|
10
|
+
resolveCurrentSubagentCapabilityCeiling,
|
|
11
|
+
resolveSubagentCapabilityCeiling
|
|
12
|
+
} from "../runs/shared/capability-ceiling.js";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export const SUBAGENT_DELEGATION_REQUEST_EVENT = "prompt-template:subagent:request";
|
|
2
|
+
export const SUBAGENT_DELEGATION_STARTED_EVENT = "prompt-template:subagent:started";
|
|
3
|
+
export const SUBAGENT_DELEGATION_UPDATE_EVENT = "prompt-template:subagent:update";
|
|
4
|
+
export const SUBAGENT_DELEGATION_RESPONSE_EVENT = "prompt-template:subagent:response";
|
|
5
|
+
export const SUBAGENT_DELEGATION_CANCEL_EVENT = "prompt-template:subagent:cancel";
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
export const EXTERNAL_JOB_PROVIDER_PROTOCOL_VERSION = 1;
|
|
2
|
+
export const EXTERNAL_JOB_PROVIDER_REGISTRY_KEY = "dm-subagents.external-job-providers.v1";
|
|
3
|
+
const MAX_PROVIDER_NAME_LENGTH = 128;
|
|
4
|
+
const MAX_PROVIDERS = 100;
|
|
5
|
+
const MAX_JOB_ID_LENGTH = 256;
|
|
6
|
+
const MAX_FAILURE_CODE_LENGTH = 128;
|
|
7
|
+
const MAX_FAILURE_MESSAGE_LENGTH = 4096;
|
|
8
|
+
const MAX_URL_LENGTH = 4096;
|
|
9
|
+
|
|
10
|
+
export class ExternalJobProviderError extends Error {
|
|
11
|
+
code;
|
|
12
|
+
blockingJobId;
|
|
13
|
+
constructor(message, options) {
|
|
14
|
+
super(message, options.cause === undefined ? undefined : { cause: options.cause });
|
|
15
|
+
this.name = "ExternalJobProviderError";
|
|
16
|
+
this.code = options.code;
|
|
17
|
+
this.blockingJobId = options.blockingJobId;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function registry() {
|
|
21
|
+
const key = Symbol.for(EXTERNAL_JOB_PROVIDER_REGISTRY_KEY);
|
|
22
|
+
const globalObject = globalThis;
|
|
23
|
+
const existing = globalObject[key];
|
|
24
|
+
if (existing === undefined) {
|
|
25
|
+
const created = {
|
|
26
|
+
version: EXTERNAL_JOB_PROVIDER_PROTOCOL_VERSION,
|
|
27
|
+
providers: new Map
|
|
28
|
+
};
|
|
29
|
+
globalObject[key] = created;
|
|
30
|
+
return created;
|
|
31
|
+
}
|
|
32
|
+
if (!existing || typeof existing !== "object" || Array.isArray(existing)) {
|
|
33
|
+
throw new Error(`Malformed external-job provider registry at Symbol.for("${EXTERNAL_JOB_PROVIDER_REGISTRY_KEY}").`);
|
|
34
|
+
}
|
|
35
|
+
const candidate = existing;
|
|
36
|
+
if (candidate.version !== EXTERNAL_JOB_PROVIDER_PROTOCOL_VERSION || !(candidate.providers instanceof Map)) {
|
|
37
|
+
throw new Error(`Unsupported external-job provider registry at Symbol.for("${EXTERNAL_JOB_PROVIDER_REGISTRY_KEY}").`);
|
|
38
|
+
}
|
|
39
|
+
return candidate;
|
|
40
|
+
}
|
|
41
|
+
function validateString(value, field, maxLength) {
|
|
42
|
+
if (typeof value !== "string" || value.length === 0 || value.trim() !== value) {
|
|
43
|
+
throw new Error(`${field} must be a non-empty string without leading or trailing whitespace.`);
|
|
44
|
+
}
|
|
45
|
+
if (value.length > maxLength)
|
|
46
|
+
throw new Error(`${field} must be at most ${maxLength} characters.`);
|
|
47
|
+
if (value.includes("\x00"))
|
|
48
|
+
throw new Error(`${field} must not contain NUL characters.`);
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
function validateOptionalString(value, field, maxLength) {
|
|
52
|
+
if (value === undefined)
|
|
53
|
+
return;
|
|
54
|
+
return validateString(value, field, maxLength);
|
|
55
|
+
}
|
|
56
|
+
function validateState(value, field) {
|
|
57
|
+
if (value === "queued" || value === "running" || value === "completed" || value === "failed" || value === "stopped" || value === "blocked")
|
|
58
|
+
return value;
|
|
59
|
+
throw new Error(`${field} must be queued, running, completed, failed, stopped, or blocked.`);
|
|
60
|
+
}
|
|
61
|
+
function validateHandle(provider, value, field, extraFields = []) {
|
|
62
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
63
|
+
throw new Error(`${field} from external-job provider '${provider}' must be an object.`);
|
|
64
|
+
const handle = value;
|
|
65
|
+
const supported = new Set(["providerJobId", "state", "handleUrl", "conversationUrl", "failureCode", "failureMessage", "blockingJobId", ...extraFields]);
|
|
66
|
+
const unknown = Object.keys(handle).filter((key) => !supported.has(key));
|
|
67
|
+
if (unknown.length > 0)
|
|
68
|
+
throw new Error(`${field} from external-job provider '${provider}' has unknown fields: ${unknown.join(", ")}.`);
|
|
69
|
+
const handleUrl = validateOptionalString(handle.handleUrl, `${field}.handleUrl`, MAX_URL_LENGTH);
|
|
70
|
+
const conversationUrl = validateOptionalString(handle.conversationUrl, `${field}.conversationUrl`, MAX_URL_LENGTH);
|
|
71
|
+
const failureCode = validateOptionalString(handle.failureCode, `${field}.failureCode`, MAX_FAILURE_CODE_LENGTH);
|
|
72
|
+
const failureMessage = validateOptionalString(handle.failureMessage, `${field}.failureMessage`, MAX_FAILURE_MESSAGE_LENGTH);
|
|
73
|
+
const blockingJobId = validateOptionalString(handle.blockingJobId, `${field}.blockingJobId`, MAX_JOB_ID_LENGTH);
|
|
74
|
+
return {
|
|
75
|
+
providerJobId: validateString(handle.providerJobId, `${field}.providerJobId`, MAX_JOB_ID_LENGTH),
|
|
76
|
+
state: validateState(handle.state, `${field}.state`),
|
|
77
|
+
...handleUrl ? { handleUrl } : {},
|
|
78
|
+
...conversationUrl ? { conversationUrl } : {},
|
|
79
|
+
...failureCode ? { failureCode } : {},
|
|
80
|
+
...failureMessage ? { failureMessage } : {},
|
|
81
|
+
...blockingJobId ? { blockingJobId } : {}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
export function validateExternalJobHandle(provider, value, field = "External-job handle") {
|
|
85
|
+
return validateHandle(provider, value, field);
|
|
86
|
+
}
|
|
87
|
+
export function validateExternalJobResult(provider, value, field = "External-job result") {
|
|
88
|
+
const result = validateHandle(provider, value, field, ["output", "artifactPath"]);
|
|
89
|
+
const record = value;
|
|
90
|
+
const output = validateOptionalString(record.output, `${field}.output`, 1024 * 1024);
|
|
91
|
+
const artifactPath = validateOptionalString(record.artifactPath, `${field}.artifactPath`, MAX_URL_LENGTH);
|
|
92
|
+
return {
|
|
93
|
+
...result,
|
|
94
|
+
...output !== undefined ? { output } : {},
|
|
95
|
+
...artifactPath !== undefined ? { artifactPath } : {}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function validateProvider(value) {
|
|
99
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
100
|
+
throw new Error("External-job provider must be an object.");
|
|
101
|
+
const provider = value;
|
|
102
|
+
const name = validateString(provider.name, "External-job provider name", MAX_PROVIDER_NAME_LENGTH);
|
|
103
|
+
for (const op of ["start", "status", "result", "reattach"]) {
|
|
104
|
+
if (typeof provider[op] !== "function")
|
|
105
|
+
throw new Error(`External-job provider '${name}' must expose ${op}().`);
|
|
106
|
+
}
|
|
107
|
+
return value;
|
|
108
|
+
}
|
|
109
|
+
export function registerExternalJobProvider(provider) {
|
|
110
|
+
const validated = validateProvider(provider);
|
|
111
|
+
const current = registry();
|
|
112
|
+
if (!current.providers.has(validated.name) && current.providers.size >= MAX_PROVIDERS) {
|
|
113
|
+
throw new Error(`External-job provider registry supports at most ${MAX_PROVIDERS} providers.`);
|
|
114
|
+
}
|
|
115
|
+
current.providers.set(validated.name, validated);
|
|
116
|
+
return () => {
|
|
117
|
+
if (current.providers.get(validated.name) === validated)
|
|
118
|
+
current.providers.delete(validated.name);
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
export function listExternalJobProviders() {
|
|
122
|
+
const current = registry();
|
|
123
|
+
if (current.providers.size > MAX_PROVIDERS)
|
|
124
|
+
throw new Error(`External-job provider registry contains more than ${MAX_PROVIDERS} providers.`);
|
|
125
|
+
const providers = [];
|
|
126
|
+
for (const [key, value] of current.providers) {
|
|
127
|
+
const provider = validateProvider(value);
|
|
128
|
+
if (key !== provider.name)
|
|
129
|
+
throw new Error(`External-job provider registry key '${key}' does not match provider name '${provider.name}'.`);
|
|
130
|
+
providers.push(provider);
|
|
131
|
+
}
|
|
132
|
+
return providers;
|
|
133
|
+
}
|
|
134
|
+
export function getExternalJobProvider(name) {
|
|
135
|
+
const safeName = validateString(name, "External-job provider name", MAX_PROVIDER_NAME_LENGTH);
|
|
136
|
+
return listExternalJobProviders().find((provider) => provider.name === safeName);
|
|
137
|
+
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { sanitizeDisplayText, truncateDisplayText } from "../shared/display-text.js";
|
|
2
|
+
export const EXTERNAL_RUN_REGISTRY_VERSION = 2;
|
|
3
|
+
export const EXTERNAL_RUN_REGISTRY_KEY = "dm-subagents.external-runs.v2";
|
|
4
|
+
export const EXTERNAL_RUN_LIMITS = {
|
|
5
|
+
maxCachedRuns: 100,
|
|
6
|
+
maxSnapshotRuns: 20,
|
|
7
|
+
maxIdentityLength: 160,
|
|
8
|
+
maxSessionIdLength: 4096,
|
|
9
|
+
maxTextLength: 160,
|
|
10
|
+
maxPreviewLength: 4096,
|
|
11
|
+
maxPathLength: 4096,
|
|
12
|
+
maxSerializedBytes: 32 * 1024
|
|
13
|
+
};
|
|
14
|
+
const trustedRecordsByRegistry = new WeakMap;
|
|
15
|
+
const RUN_FIELD_NAMES = [
|
|
16
|
+
"id",
|
|
17
|
+
"sessionId",
|
|
18
|
+
"source",
|
|
19
|
+
"label",
|
|
20
|
+
"state",
|
|
21
|
+
"startedAt",
|
|
22
|
+
"updatedAt",
|
|
23
|
+
"endedAt",
|
|
24
|
+
"currentAction",
|
|
25
|
+
"preview",
|
|
26
|
+
"reportPath",
|
|
27
|
+
"transcriptPath"
|
|
28
|
+
];
|
|
29
|
+
const RUN_FIELDS = new Set(RUN_FIELD_NAMES);
|
|
30
|
+
const UPDATE_FIELDS = new Set([...RUN_FIELDS].filter((field) => field !== "id" && field !== "sessionId" && field !== "source"));
|
|
31
|
+
function registry() {
|
|
32
|
+
const key = Symbol.for(EXTERNAL_RUN_REGISTRY_KEY);
|
|
33
|
+
const target = globalThis;
|
|
34
|
+
const existing = target[key];
|
|
35
|
+
if (existing === undefined) {
|
|
36
|
+
const created = { version: EXTERNAL_RUN_REGISTRY_VERSION, runs: new Map };
|
|
37
|
+
target[key] = created;
|
|
38
|
+
return created;
|
|
39
|
+
}
|
|
40
|
+
if (!existing || typeof existing !== "object" || Array.isArray(existing))
|
|
41
|
+
throw new Error(`Malformed external-run registry at Symbol.for("${EXTERNAL_RUN_REGISTRY_KEY}").`);
|
|
42
|
+
const candidate = existing;
|
|
43
|
+
if (candidate.version !== EXTERNAL_RUN_REGISTRY_VERSION || !(candidate.runs instanceof Map))
|
|
44
|
+
throw new Error(`Unsupported external-run registry at Symbol.for("${EXTERNAL_RUN_REGISTRY_KEY}").`);
|
|
45
|
+
return candidate;
|
|
46
|
+
}
|
|
47
|
+
function trustedRecords(current) {
|
|
48
|
+
const cached = trustedRecordsByRegistry.get(current);
|
|
49
|
+
if (cached)
|
|
50
|
+
return cached;
|
|
51
|
+
const records = new Map;
|
|
52
|
+
trustedRecordsByRegistry.set(current, records);
|
|
53
|
+
return records;
|
|
54
|
+
}
|
|
55
|
+
function inputObject(value, field, allowed) {
|
|
56
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
57
|
+
throw new Error(`${field} must be an object.`);
|
|
58
|
+
const input = value;
|
|
59
|
+
const unknown = Object.keys(input).filter((key) => !allowed.has(key));
|
|
60
|
+
if (unknown.length)
|
|
61
|
+
throw new Error(`${field} has unknown fields: ${unknown.join(", ")}.`);
|
|
62
|
+
return input;
|
|
63
|
+
}
|
|
64
|
+
function identity(value, field, maxLength = EXTERNAL_RUN_LIMITS.maxIdentityLength) {
|
|
65
|
+
if (typeof value !== "string" || value.length === 0 || value.trim() !== value || value.includes("\x00"))
|
|
66
|
+
throw new Error(`${field} must be a non-empty trimmed string without NUL characters.`);
|
|
67
|
+
if (value.length > maxLength)
|
|
68
|
+
throw new Error(`${field} must be at most ${maxLength} characters.`);
|
|
69
|
+
const safe = sanitizeDisplayText(value);
|
|
70
|
+
if (!safe || safe !== value)
|
|
71
|
+
throw new Error(`${field} must contain only display-safe text.`);
|
|
72
|
+
return value;
|
|
73
|
+
}
|
|
74
|
+
function displayText(value, field, maxLength, required = false) {
|
|
75
|
+
if (value === undefined && !required)
|
|
76
|
+
return;
|
|
77
|
+
if (typeof value !== "string" || value.length === 0)
|
|
78
|
+
throw new Error(`${field} must be a non-empty string.`);
|
|
79
|
+
const safe = sanitizeDisplayText(value.slice(0, Math.max(maxLength * 4, maxLength)));
|
|
80
|
+
if (!safe)
|
|
81
|
+
throw new Error(`${field} must contain displayable text.`);
|
|
82
|
+
return truncateDisplayText(safe, maxLength);
|
|
83
|
+
}
|
|
84
|
+
function timestamp(value, field, required = false) {
|
|
85
|
+
if (value === undefined && !required)
|
|
86
|
+
return;
|
|
87
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0 || value > 8640000000000000)
|
|
88
|
+
throw new Error(`${field} must be a non-negative safe timestamp.`);
|
|
89
|
+
return value;
|
|
90
|
+
}
|
|
91
|
+
function state(value, field) {
|
|
92
|
+
if (value === "queued" || value === "running" || value === "completed" || value === "failed" || value === "stopped")
|
|
93
|
+
return value;
|
|
94
|
+
throw new Error(`${field} is invalid.`);
|
|
95
|
+
}
|
|
96
|
+
function validateRun(value) {
|
|
97
|
+
const run = inputObject(value, "External run", RUN_FIELDS);
|
|
98
|
+
const updatedAt = timestamp(run.updatedAt, "External run updatedAt");
|
|
99
|
+
const endedAt = timestamp(run.endedAt, "External run endedAt");
|
|
100
|
+
const currentAction = displayText(run.currentAction, "External run currentAction", EXTERNAL_RUN_LIMITS.maxTextLength);
|
|
101
|
+
const preview = displayText(run.preview, "External run preview", EXTERNAL_RUN_LIMITS.maxPreviewLength);
|
|
102
|
+
const reportPath = displayText(run.reportPath, "External run reportPath", EXTERNAL_RUN_LIMITS.maxPathLength);
|
|
103
|
+
const transcriptPath = displayText(run.transcriptPath, "External run transcriptPath", EXTERNAL_RUN_LIMITS.maxPathLength);
|
|
104
|
+
return {
|
|
105
|
+
id: identity(run.id, "External run id"),
|
|
106
|
+
sessionId: identity(run.sessionId, "External run sessionId", EXTERNAL_RUN_LIMITS.maxSessionIdLength),
|
|
107
|
+
source: displayText(run.source, "External run source", EXTERNAL_RUN_LIMITS.maxTextLength, true),
|
|
108
|
+
label: displayText(run.label, "External run label", EXTERNAL_RUN_LIMITS.maxTextLength, true),
|
|
109
|
+
state: state(run.state, "External run state"),
|
|
110
|
+
startedAt: timestamp(run.startedAt, "External run startedAt", true),
|
|
111
|
+
...updatedAt !== undefined ? { updatedAt } : {},
|
|
112
|
+
...endedAt !== undefined ? { endedAt } : {},
|
|
113
|
+
...currentAction ? { currentAction } : {},
|
|
114
|
+
...preview ? { preview } : {},
|
|
115
|
+
...reportPath ? { reportPath } : {},
|
|
116
|
+
...transcriptPath ? { transcriptPath } : {}
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function key(sessionId, id) {
|
|
120
|
+
return `${sessionId}\x00${id}`;
|
|
121
|
+
}
|
|
122
|
+
function clone(run) {
|
|
123
|
+
return { ...run };
|
|
124
|
+
}
|
|
125
|
+
function trustedRecord(value, normalized) {
|
|
126
|
+
const candidate = value;
|
|
127
|
+
return {
|
|
128
|
+
value,
|
|
129
|
+
normalized,
|
|
130
|
+
keys: Object.keys(candidate),
|
|
131
|
+
values: RUN_FIELD_NAMES.map((field) => candidate[field])
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function trustedRecordValue(value, record) {
|
|
135
|
+
if (!record || record.value !== value || !value || typeof value !== "object")
|
|
136
|
+
return;
|
|
137
|
+
try {
|
|
138
|
+
const candidate = value;
|
|
139
|
+
const keys = Object.keys(candidate);
|
|
140
|
+
if (keys.length !== record.keys.length || keys.some((key, index) => key !== record.keys[index]))
|
|
141
|
+
return;
|
|
142
|
+
for (const [index, field] of RUN_FIELD_NAMES.entries()) {
|
|
143
|
+
if (candidate[field] !== record.values[index])
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
return record.normalized;
|
|
147
|
+
} catch {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function rememberTrustedRecord(current, cacheKey, value, normalized) {
|
|
152
|
+
trustedRecords(current).set(cacheKey, trustedRecord(value, normalized));
|
|
153
|
+
}
|
|
154
|
+
function normalizeCachedRecord(current, cacheKey, value) {
|
|
155
|
+
const run = validateRun(value);
|
|
156
|
+
rememberTrustedRecord(current, cacheKey, value, run);
|
|
157
|
+
return run;
|
|
158
|
+
}
|
|
159
|
+
export function registerExternalRun(input) {
|
|
160
|
+
const run = validateRun(input);
|
|
161
|
+
const current = registry();
|
|
162
|
+
const runKey = key(run.sessionId, run.id);
|
|
163
|
+
if (current.runs.has(runKey))
|
|
164
|
+
throw new Error(`External run '${run.id}' is already registered for session '${run.sessionId}'.`);
|
|
165
|
+
if (current.runs.size >= EXTERNAL_RUN_LIMITS.maxCachedRuns)
|
|
166
|
+
throw new Error(`External-run registry supports at most ${EXTERNAL_RUN_LIMITS.maxCachedRuns} cached runs.`);
|
|
167
|
+
current.runs.set(runKey, run);
|
|
168
|
+
rememberTrustedRecord(current, runKey, run, run);
|
|
169
|
+
return clone(run);
|
|
170
|
+
}
|
|
171
|
+
export function updateExternalRun(sessionId, id, update) {
|
|
172
|
+
const safeSessionId = identity(sessionId, "External run sessionId", EXTERNAL_RUN_LIMITS.maxSessionIdLength);
|
|
173
|
+
const safeId = identity(id, "External run id");
|
|
174
|
+
const patch = inputObject(update, "External run update", UPDATE_FIELDS);
|
|
175
|
+
const current = registry();
|
|
176
|
+
const runKey = key(safeSessionId, safeId);
|
|
177
|
+
const previous = current.runs.get(runKey);
|
|
178
|
+
if (!previous)
|
|
179
|
+
throw new Error(`External run '${safeId}' is not registered for session '${safeSessionId}'.`);
|
|
180
|
+
const next = validateRun({ ...previous, ...patch });
|
|
181
|
+
current.runs.set(runKey, next);
|
|
182
|
+
rememberTrustedRecord(current, runKey, next, next);
|
|
183
|
+
return clone(next);
|
|
184
|
+
}
|
|
185
|
+
export function unregisterExternalRun(sessionId, id) {
|
|
186
|
+
const current = registry();
|
|
187
|
+
const runKey = key(identity(sessionId, "External run sessionId", EXTERNAL_RUN_LIMITS.maxSessionIdLength), identity(id, "External run id"));
|
|
188
|
+
const deleted = current.runs.delete(runKey);
|
|
189
|
+
if (deleted)
|
|
190
|
+
trustedRecords(current).delete(runKey);
|
|
191
|
+
return deleted;
|
|
192
|
+
}
|
|
193
|
+
function snapshotBytes(runs) {
|
|
194
|
+
return Buffer.byteLength(JSON.stringify(runs), "utf8");
|
|
195
|
+
}
|
|
196
|
+
function getErrorMessage(error) {
|
|
197
|
+
return error instanceof Error ? error.message : String(error);
|
|
198
|
+
}
|
|
199
|
+
export function snapshotExternalRuns(sessionId, options = {}) {
|
|
200
|
+
const safeSessionId = identity(sessionId, "External-run snapshot sessionId", EXTERNAL_RUN_LIMITS.maxSessionIdLength);
|
|
201
|
+
const current = registry();
|
|
202
|
+
const trusted = trustedRecords(current);
|
|
203
|
+
const sessionPrefix = `${safeSessionId}\x00`;
|
|
204
|
+
const runs = [];
|
|
205
|
+
for (const [cacheKey, value] of current.runs.entries()) {
|
|
206
|
+
if (typeof cacheKey !== "string" || !cacheKey.startsWith(sessionPrefix))
|
|
207
|
+
continue;
|
|
208
|
+
try {
|
|
209
|
+
const run = trustedRecordValue(value, trusted.get(cacheKey)) ?? normalizeCachedRecord(current, cacheKey, value);
|
|
210
|
+
if (run.sessionId === safeSessionId)
|
|
211
|
+
runs.push(run);
|
|
212
|
+
} catch (error) {
|
|
213
|
+
const message = `Malformed cached external run '${cacheKey}': ${getErrorMessage(error)}`;
|
|
214
|
+
if (!options.ignoreMalformed)
|
|
215
|
+
throw new Error(message, { cause: error instanceof Error ? error : undefined });
|
|
216
|
+
current.runs.delete(cacheKey);
|
|
217
|
+
trusted.delete(cacheKey);
|
|
218
|
+
options.onMalformedRecord?.(message);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
runs.sort((left, right) => {
|
|
222
|
+
const leftActive = left.state === "queued" || left.state === "running";
|
|
223
|
+
const rightActive = right.state === "queued" || right.state === "running";
|
|
224
|
+
if (leftActive !== rightActive)
|
|
225
|
+
return leftActive ? -1 : 1;
|
|
226
|
+
return (right.updatedAt ?? right.endedAt ?? right.startedAt) - (left.updatedAt ?? left.endedAt ?? left.startedAt) || left.id.localeCompare(right.id);
|
|
227
|
+
});
|
|
228
|
+
const snapshot = runs.slice(0, EXTERNAL_RUN_LIMITS.maxSnapshotRuns).map(clone);
|
|
229
|
+
while (snapshot.length > 0 && snapshotBytes(snapshot) > EXTERNAL_RUN_LIMITS.maxSerializedBytes)
|
|
230
|
+
snapshot.pop();
|
|
231
|
+
return snapshot;
|
|
232
|
+
}
|
|
233
|
+
export const listExternalRuns = snapshotExternalRuns;
|