@adhdev/daemon-core 0.9.82-rc.43 → 0.9.82-rc.431
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/dist/boot/daemon-lifecycle.d.ts +9 -0
- package/dist/boot/process-hardening.d.ts +50 -0
- package/dist/build-info.d.ts +37 -0
- package/dist/chat/source-machine.d.ts +166 -0
- package/dist/chat/source-resolver.d.ts +104 -0
- package/dist/chat/subscription-updates.d.ts +1 -0
- package/dist/cli-adapter-types.d.ts +68 -2
- package/dist/cli-adapters/cli-script-runner.d.ts +117 -0
- package/dist/cli-adapters/cli-state-engine.d.ts +200 -0
- package/dist/cli-adapters/provider-cli-adapter.d.ts +126 -64
- package/dist/cli-adapters/provider-cli-parse.d.ts +5 -0
- package/dist/cli-adapters/provider-cli-runtime.d.ts +34 -0
- package/dist/cli-adapters/provider-cli-shared.d.ts +74 -0
- package/dist/cli-adapters/pty-write-chunking.d.ts +34 -0
- package/dist/cli-adapters/raw-terminal-io.d.ts +37 -0
- package/dist/cli-adapters/resolve-executable.d.ts +69 -0
- package/dist/cli-adapters/terminal-backends/ghostty-vt-backend.d.ts +6 -3
- package/dist/cli-adapters/terminal-backends/types.d.ts +10 -2
- package/dist/cli-adapters/terminal-screen.d.ts +4 -11
- package/dist/commands/chat-commands-debug-bundle.d.ts +14 -0
- package/dist/commands/chat-commands-read.d.ts +7 -0
- package/dist/commands/chat-commands-scope.d.ts +39 -0
- package/dist/commands/chat-commands-shared.d.ts +33 -0
- package/dist/commands/chat-commands-write.d.ts +14 -0
- package/dist/commands/chat-commands.d.ts +10 -23
- package/dist/commands/cli-manager.d.ts +15 -1
- package/dist/commands/handler.d.ts +125 -0
- package/dist/commands/high-family/index.d.ts +3 -0
- package/dist/commands/high-family/mesh-coordinator-launch.d.ts +2 -0
- package/dist/commands/high-family/mesh-events.d.ts +2 -0
- package/dist/commands/high-family/mesh-status.d.ts +2 -0
- package/dist/commands/high-family/types.d.ts +60 -0
- package/dist/commands/low-family/coordinator-prompt.d.ts +9 -0
- package/dist/commands/low-family/daemon-lifecycle.d.ts +2 -0
- package/dist/commands/low-family/diagnostics.d.ts +2 -0
- package/dist/commands/low-family/index.d.ts +3 -0
- package/dist/commands/low-family/mesh-ledger.d.ts +10 -0
- package/dist/commands/low-family/mesh-node-logs.d.ts +2 -0
- package/dist/commands/low-family/notification.d.ts +2 -0
- package/dist/commands/low-family/refine-config.d.ts +2 -0
- package/dist/commands/low-family/session-host.d.ts +2 -0
- package/dist/commands/low-family/spec-providerdev.d.ts +11 -0
- package/dist/commands/low-family/status-meta.d.ts +2 -0
- package/dist/commands/low-family/types.d.ts +33 -0
- package/dist/commands/med-family/cli-agent.d.ts +2 -0
- package/dist/commands/med-family/fast-forward.d.ts +2 -0
- package/dist/commands/med-family/ide.d.ts +10 -0
- package/dist/commands/med-family/index.d.ts +3 -0
- package/dist/commands/med-family/mesh-crud.d.ts +29 -0
- package/dist/commands/med-family/mesh-host-pairing.d.ts +2 -0
- package/dist/commands/med-family/mesh-queue.d.ts +2 -0
- package/dist/commands/med-family/mesh-restart.d.ts +2 -0
- package/dist/commands/med-family/types.d.ts +142 -0
- package/dist/commands/mesh-coordinator.d.ts +85 -1
- package/dist/commands/router.d.ts +276 -2
- package/dist/commands/upgrade-helper.d.ts +41 -1
- package/dist/config/chat-history.d.ts +9 -0
- package/dist/config/config.d.ts +5 -0
- package/dist/config/mesh-config.d.ts +100 -1
- package/dist/config/mesh-json-config.d.ts +199 -0
- package/dist/config/repo-settings.d.ts +77 -0
- package/dist/daemon/dev-server.d.ts +0 -2
- package/dist/detection/ide-detector.d.ts +13 -0
- package/dist/detection/win32-ide-version.d.ts +37 -0
- package/dist/git/change-impact-config.d.ts +159 -0
- package/dist/git/git-commands.d.ts +11 -1
- package/dist/git/git-diff.d.ts +6 -0
- package/dist/git/git-executor.d.ts +11 -0
- package/dist/git/git-status.d.ts +57 -0
- package/dist/git/git-types.d.ts +2 -50
- package/dist/git/git-worktree.d.ts +71 -1
- package/dist/git/index.d.ts +3 -1
- package/dist/index.d.ts +64 -14
- package/dist/index.js +49391 -19228
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +48574 -18570
- package/dist/index.mjs.map +1 -1
- package/dist/installer.d.ts +1 -4
- package/dist/ipc/local-ipc-server.d.ts +91 -0
- package/dist/launch.d.ts +1 -1
- package/dist/logging/async-batch-writer.d.ts +10 -0
- package/dist/logging/log-redactor.d.ts +24 -0
- package/dist/logging/log-tail-reader.d.ts +81 -0
- package/dist/logging/logger.d.ts +1 -1
- package/dist/mesh/contracts.d.ts +164 -0
- package/dist/mesh/coordinator-prompt.d.ts +89 -0
- package/dist/mesh/coordinator-registry.d.ts +59 -0
- package/dist/mesh/mesh-active-work.d.ts +174 -0
- package/dist/mesh/mesh-clone-grace.d.ts +8 -0
- package/dist/mesh/mesh-coordinator-config.d.ts +21 -0
- package/dist/mesh/mesh-delivery-policy.d.ts +131 -0
- package/dist/mesh/mesh-event-classify.d.ts +5 -0
- package/dist/mesh/mesh-event-forwarding.d.ts +41 -0
- package/dist/mesh/mesh-event-trace.d.ts +21 -0
- package/dist/mesh/mesh-events-coordinator.d.ts +4 -0
- package/dist/mesh/mesh-events-pending.d.ts +69 -0
- package/dist/mesh/mesh-events-stale.d.ts +61 -0
- package/dist/mesh/mesh-events-utils.d.ts +75 -0
- package/dist/mesh/mesh-events.d.ts +6 -49
- package/dist/mesh/mesh-fast-forward.d.ts +81 -0
- package/dist/mesh/mesh-host-ownership.d.ts +9 -0
- package/dist/mesh/mesh-init.d.ts +86 -0
- package/dist/mesh/mesh-ledger-reconciliation.d.ts +23 -4
- package/dist/mesh/mesh-ledger.d.ts +77 -1
- package/dist/mesh/mesh-magi-status.d.ts +63 -0
- package/dist/mesh/mesh-missions.d.ts +189 -0
- package/dist/mesh/mesh-node-identity.d.ts +289 -0
- package/dist/mesh/mesh-queue-assignment.d.ts +113 -0
- package/dist/mesh/mesh-reconcile-loop.d.ts +65 -0
- package/dist/mesh/mesh-refine-batch.d.ts +68 -0
- package/dist/mesh/mesh-refine-gates.d.ts +428 -0
- package/dist/mesh/mesh-refine-status.d.ts +62 -0
- package/dist/mesh/mesh-review-inbox.d.ts +90 -0
- package/dist/mesh/mesh-routing.d.ts +70 -0
- package/dist/mesh/mesh-runtime-store.d.ts +450 -0
- package/dist/mesh/mesh-scheduling-runtime.d.ts +78 -0
- package/dist/mesh/mesh-task-inflight.d.ts +46 -0
- package/dist/mesh/mesh-task-stats.d.ts +49 -0
- package/dist/mesh/mesh-unresolved-forward-outbox.d.ts +30 -0
- package/dist/mesh/mesh-warmup-deadline.d.ts +68 -0
- package/dist/mesh/mesh-work-queue.d.ts +290 -5
- package/dist/mesh/preview-freshness.d.ts +18 -0
- package/dist/mesh/refine-config.d.ts +216 -0
- package/dist/mesh/worktree-bootstrap-config.d.ts +145 -0
- package/dist/providers/acp-provider-instance.d.ts +5 -0
- package/dist/providers/approval-utils.d.ts +20 -0
- package/dist/providers/chat-message-normalization.d.ts +31 -1
- package/dist/providers/cli-provider-instance.d.ts +221 -3
- package/dist/providers/contracts.d.ts +139 -6
- package/dist/providers/external-sources.d.ts +71 -0
- package/dist/providers/manual-attendance.d.ts +63 -0
- package/dist/providers/native-history/antigravity-cli-transcript.d.ts +100 -0
- package/dist/providers/native-history/claude-cli-transcript.d.ts +70 -0
- package/dist/providers/native-history/codex-cli-transcript.d.ts +73 -0
- package/dist/providers/native-history/constants.d.ts +12 -0
- package/dist/providers/native-history/dispatcher.d.ts +27 -0
- package/dist/providers/native-history/hermes-cli-transcript.d.ts +30 -0
- package/dist/providers/native-history/index.d.ts +13 -0
- package/dist/providers/provider-instance-manager.d.ts +29 -0
- package/dist/providers/provider-instance.d.ts +23 -1
- package/dist/providers/provider-loader.d.ts +26 -4
- package/dist/providers/provider-trust.d.ts +31 -0
- package/dist/providers/read-chat-contract.d.ts +29 -0
- package/dist/providers/sdk/v1/builders/acp/detect-status.d.ts +68 -0
- package/dist/providers/sdk/v1/builders/cli/detect-status.d.ts +86 -0
- package/dist/providers/sdk/v1/builders/cli/parse-approval-squash.d.ts +59 -0
- package/dist/providers/sdk/v1/builders/cli/parse-approval.d.ts +65 -0
- package/dist/providers/sdk/v1/builders/cli/parse-session.d.ts +91 -0
- package/dist/providers/sdk/v1/builders/cli/visible-region.d.ts +42 -0
- package/dist/providers/sdk/v1/fixture-tooling/format.d.ts +126 -0
- package/dist/providers/sdk/v1/fixture-tooling/index.d.ts +8 -0
- package/dist/providers/sdk/v1/fixture-tooling/replay.d.ts +38 -0
- package/dist/providers/sdk/v1/index.d.ts +30 -0
- package/dist/providers/sdk/v1/sandbox/README-design.d.ts +193 -0
- package/dist/providers/sdk/v1/sandbox/require-whitelist.d.ts +74 -0
- package/dist/providers/sdk/v1/sandbox/script-runner.d.ts +98 -0
- package/dist/providers/sdk/v1/types/cli/index.d.ts +277 -0
- package/dist/providers/sdk/v1/types/common/index.d.ts +203 -0
- package/dist/providers/sdk/v1/validators/index.d.ts +5 -0
- package/dist/providers/sdk/v1/validators/manifest.d.ts +40 -0
- package/dist/providers/sdk/v1/validators/taint.d.ts +52 -0
- package/dist/providers/spec/adapter.d.ts +91 -0
- package/dist/providers/spec/cli-adapter.d.ts +223 -0
- package/dist/providers/spec/evaluator.d.ts +45 -0
- package/dist/providers/spec/fsm-driver.d.ts +422 -0
- package/dist/providers/spec/fsm-evaluator.d.ts +76 -0
- package/dist/providers/spec/fsm-loader.d.ts +14 -0
- package/dist/providers/spec/fsm-types.d.ts +174 -0
- package/dist/providers/spec/native-history-executor.d.ts +41 -0
- package/dist/providers/spec/pre-launch-trust.d.ts +16 -0
- package/dist/providers/spec/route.d.ts +4 -0
- package/dist/providers/spec/types.d.ts +222 -0
- package/dist/providers/status-monitor.d.ts +7 -7
- package/dist/providers/transcript-v2.d.ts +176 -0
- package/dist/providers/types/interactive-prompt.d.ts +84 -0
- package/dist/providers/working-dir.d.ts +17 -0
- package/dist/repo-mesh-types.d.ts +501 -13
- package/dist/runtime-defaults.d.ts +2 -0
- package/dist/sessions/registry.d.ts +6 -0
- package/dist/shared-types-extra.d.ts +2 -4
- package/dist/shared-types.d.ts +59 -2
- package/dist/status/normalize.d.ts +1 -1
- package/dist/status/normalize.js +1 -0
- package/dist/status/normalize.js.map +1 -1
- package/dist/status/normalize.mjs +1 -0
- package/dist/status/normalize.mjs.map +1 -1
- package/dist/status/reporter.d.ts +2 -0
- package/dist/status/snapshot.d.ts +26 -0
- package/dist/system/hash.d.ts +8 -0
- package/dist/system/load-better-sqlite3.d.ts +21 -0
- package/dist/types.d.ts +5 -0
- package/package.json +7 -3
- package/src/agent-stream/poller.ts +2 -3
- package/src/agent-stream/provider-adapter.ts +1 -1
- package/src/boot/daemon-lifecycle.ts +63 -12
- package/src/boot/process-hardening.ts +89 -0
- package/src/build-info.ts +73 -0
- package/src/chat/source-machine.ts +534 -0
- package/src/chat/source-resolver.ts +0 -0
- package/src/chat/subscription-updates.ts +20 -1
- package/src/cli-adapter-types.d.ts +3 -1
- package/src/cli-adapter-types.ts +68 -2
- package/src/cli-adapters/cli-script-runner.ts +421 -0
- package/src/cli-adapters/cli-state-engine.ts +1246 -0
- package/src/cli-adapters/provider-cli-adapter.d.ts +1 -1
- package/src/cli-adapters/provider-cli-adapter.ts +914 -1144
- package/src/cli-adapters/provider-cli-parse.d.ts +2 -0
- package/src/cli-adapters/provider-cli-parse.ts +19 -0
- package/src/cli-adapters/provider-cli-runtime.ts +56 -7
- package/src/cli-adapters/provider-cli-shared.d.ts +4 -0
- package/src/cli-adapters/provider-cli-shared.ts +121 -11
- package/src/cli-adapters/pty-transport.ts +2 -1
- package/src/cli-adapters/pty-write-chunking.ts +106 -0
- package/src/cli-adapters/raw-terminal-io.ts +252 -0
- package/src/cli-adapters/resolve-executable.ts +204 -0
- package/src/cli-adapters/session-host-transport.ts +2 -1
- package/src/cli-adapters/terminal-backends/ghostty-vt-backend.ts +63 -29
- package/src/cli-adapters/terminal-backends/types.ts +10 -3
- package/src/cli-adapters/terminal-screen.ts +16 -81
- package/src/commands/WINDOWS-UPGRADE-LOCK-FAILURE.md +198 -0
- package/src/commands/chat-commands-debug-bundle.ts +398 -0
- package/src/commands/chat-commands-read.ts +2327 -0
- package/src/commands/chat-commands-scope.ts +54 -0
- package/src/commands/chat-commands-shared.ts +114 -0
- package/src/commands/chat-commands-write.ts +891 -0
- package/src/commands/chat-commands.ts +19 -1841
- package/src/commands/cli-manager.ts +544 -26
- package/src/commands/handler.ts +841 -2
- package/src/commands/high-family/index.ts +28 -0
- package/src/commands/high-family/mesh-coordinator-launch.ts +678 -0
- package/src/commands/high-family/mesh-events.ts +80 -0
- package/src/commands/high-family/mesh-status.ts +710 -0
- package/src/commands/high-family/types.ts +76 -0
- package/src/commands/low-family/coordinator-prompt.ts +72 -0
- package/src/commands/low-family/daemon-lifecycle.ts +107 -0
- package/src/commands/low-family/diagnostics.ts +57 -0
- package/src/commands/low-family/index.ts +37 -0
- package/src/commands/low-family/mesh-ledger.ts +62 -0
- package/src/commands/low-family/mesh-node-logs.ts +87 -0
- package/src/commands/low-family/notification.ts +116 -0
- package/src/commands/low-family/refine-config.ts +106 -0
- package/src/commands/low-family/session-host.ts +274 -0
- package/src/commands/low-family/spec-providerdev.ts +217 -0
- package/src/commands/low-family/status-meta.ts +112 -0
- package/src/commands/low-family/types.ts +39 -0
- package/src/commands/med-family/cli-agent.ts +238 -0
- package/src/commands/med-family/fast-forward.ts +230 -0
- package/src/commands/med-family/ide.ts +163 -0
- package/src/commands/med-family/index.ts +37 -0
- package/src/commands/med-family/mesh-crud.ts +1066 -0
- package/src/commands/med-family/mesh-host-pairing.ts +234 -0
- package/src/commands/med-family/mesh-queue.ts +167 -0
- package/src/commands/med-family/mesh-restart.ts +92 -0
- package/src/commands/med-family/types.ts +143 -0
- package/src/commands/mesh-coordinator.ts +334 -124
- package/src/commands/router.ts +2711 -3254
- package/src/commands/stream-commands.ts +8 -0
- package/src/commands/upgrade-helper.ts +310 -45
- package/src/config/chat-history.ts +483 -24
- package/src/config/config.ts +12 -0
- package/src/config/mesh-config.ts +495 -22
- package/src/config/mesh-json-config.ts +376 -0
- package/src/config/recent-activity.ts +8 -2
- package/src/config/repo-settings.ts +111 -0
- package/src/daemon/dev-auto-implement.ts +3 -2
- package/src/daemon/dev-cli-debug.ts +10 -1
- package/src/daemon/dev-server.ts +0 -541
- package/src/detection/cli-detector.ts +28 -9
- package/src/detection/ide-detector.ts +55 -16
- package/src/detection/win32-ide-version.ts +106 -0
- package/src/git/change-impact-config.ts +354 -0
- package/src/git/git-commands.ts +59 -15
- package/src/git/git-diff.ts +81 -11
- package/src/git/git-executor.ts +12 -0
- package/src/git/git-status.ts +767 -48
- package/src/git/git-types.ts +14 -62
- package/src/git/git-worktree.ts +261 -4
- package/src/git/index.ts +17 -0
- package/src/index.ts +194 -12
- package/src/installer.d.ts +1 -1
- package/src/installer.ts +8 -6
- package/src/ipc/local-ipc-server.ts +278 -0
- package/src/launch.d.ts +1 -1
- package/src/launch.ts +37 -28
- package/src/logging/async-batch-writer.ts +55 -0
- package/src/logging/command-log.ts +7 -5
- package/src/logging/log-redactor.ts +100 -0
- package/src/logging/log-tail-reader.ts +341 -0
- package/src/logging/logger.ts +14 -7
- package/src/mesh/contracts.ts +338 -0
- package/src/mesh/coordinator-prompt.ts +381 -33
- package/src/mesh/coordinator-registry.ts +121 -0
- package/src/mesh/mesh-active-work.ts +645 -0
- package/src/mesh/mesh-clone-grace.ts +68 -0
- package/src/mesh/mesh-coordinator-config.ts +97 -0
- package/src/mesh/mesh-delivery-policy.ts +315 -0
- package/src/mesh/mesh-event-classify.ts +51 -0
- package/src/mesh/mesh-event-forwarding.ts +1888 -0
- package/src/mesh/mesh-event-trace.ts +67 -0
- package/src/mesh/mesh-events-coordinator.ts +32 -0
- package/src/mesh/mesh-events-pending.ts +656 -0
- package/src/mesh/mesh-events-stale.ts +389 -0
- package/src/mesh/mesh-events-utils.ts +443 -0
- package/src/mesh/mesh-events.ts +33 -1035
- package/src/mesh/mesh-fast-forward.ts +843 -0
- package/src/mesh/mesh-host-ownership.ts +73 -0
- package/src/mesh/mesh-init.ts +260 -0
- package/src/mesh/mesh-ledger-reconciliation.ts +12 -7
- package/src/mesh/mesh-ledger.ts +656 -102
- package/src/mesh/mesh-magi-status.ts +223 -0
- package/src/mesh/mesh-missions.ts +449 -0
- package/src/mesh/mesh-node-identity.ts +1887 -0
- package/src/mesh/mesh-queue-assignment.ts +1918 -0
- package/src/mesh/mesh-reconcile-loop.ts +1780 -0
- package/src/mesh/mesh-refine-batch.ts +205 -0
- package/src/mesh/mesh-refine-gates.ts +1673 -0
- package/src/mesh/mesh-refine-status.ts +231 -0
- package/src/mesh/mesh-review-inbox.ts +307 -0
- package/src/mesh/mesh-routing.ts +291 -0
- package/src/mesh/mesh-runtime-store.ts +1853 -0
- package/src/mesh/mesh-scheduling-runtime.ts +198 -0
- package/src/mesh/mesh-task-inflight.ts +70 -0
- package/src/mesh/mesh-task-stats.ts +161 -0
- package/src/mesh/mesh-unresolved-forward-outbox.ts +185 -0
- package/src/mesh/mesh-warmup-deadline.ts +152 -0
- package/src/mesh/mesh-work-queue.ts +1243 -141
- package/src/mesh/preview-freshness.ts +118 -0
- package/src/mesh/refine-config.ts +423 -0
- package/src/mesh/worktree-bootstrap-config.ts +450 -0
- package/src/providers/acp-provider-instance.ts +43 -10
- package/src/providers/approval-utils.d.ts +5 -0
- package/src/providers/approval-utils.ts +57 -5
- package/src/providers/chat-message-normalization.ts +92 -4
- package/src/providers/cli-provider-instance.ts +1933 -104
- package/src/providers/contracts.d.ts +55 -0
- package/src/providers/contracts.ts +150 -6
- package/src/providers/extension-provider-instance.ts +12 -7
- package/src/providers/external-sources.ts +218 -0
- package/src/providers/ide-provider-instance.ts +35 -12
- package/src/providers/manual-attendance.ts +85 -0
- package/src/providers/native-history/antigravity-cli-transcript.ts +643 -0
- package/src/providers/native-history/claude-cli-transcript.ts +396 -0
- package/src/providers/native-history/codex-cli-transcript.ts +479 -0
- package/src/providers/native-history/constants.ts +19 -0
- package/src/providers/native-history/dispatcher.ts +339 -0
- package/src/providers/native-history/hermes-cli-transcript.ts +230 -0
- package/src/providers/native-history/index.ts +30 -0
- package/src/providers/provider-instance-manager.ts +71 -0
- package/src/providers/provider-instance.ts +19 -1
- package/src/providers/provider-loader.ts +668 -50
- package/src/providers/provider-schema.ts +87 -14
- package/src/providers/provider-trust.ts +114 -0
- package/src/providers/read-chat-contract.ts +76 -16
- package/src/providers/sdk/README.md +49 -0
- package/src/providers/sdk/v1/builders/acp/detect-status.ts +144 -0
- package/src/providers/sdk/v1/builders/cli/detect-status.ts +313 -0
- package/src/providers/sdk/v1/builders/cli/parse-approval-squash.ts +158 -0
- package/src/providers/sdk/v1/builders/cli/parse-approval.ts +250 -0
- package/src/providers/sdk/v1/builders/cli/parse-session.ts +276 -0
- package/src/providers/sdk/v1/builders/cli/visible-region.ts +143 -0
- package/src/providers/sdk/v1/fixture-tooling/format.ts +130 -0
- package/src/providers/sdk/v1/fixture-tooling/index.ts +22 -0
- package/src/providers/sdk/v1/fixture-tooling/replay.ts +352 -0
- package/src/providers/sdk/v1/index.ts +152 -0
- package/src/providers/sdk/v1/sandbox/README-design.ts +195 -0
- package/src/providers/sdk/v1/sandbox/require-whitelist.ts +472 -0
- package/src/providers/sdk/v1/sandbox/script-runner.ts +150 -0
- package/src/providers/sdk/v1/schemas/cli/provider.schema.json +504 -0
- package/src/providers/sdk/v1/schemas/primitives/acp-session-protocol-v1.json +131 -0
- package/src/providers/sdk/v1/schemas/primitives/native-history-codex-rollout-v1.json +66 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-approval-squash-v1.json +91 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-assistant-block-v1.json +91 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-cue-ordering-v1.json +47 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-dispatch-order-v1.json +32 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-footer-chrome-v1.json +42 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-index-finder-v1.json +27 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-modal-v1.json +125 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-prompt-marker-v1.json +45 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-session-id-extraction-v1.json +46 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-settled-prompt-v1.json +71 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-spinner-v1.json +83 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-transcript-pty-v1.json +83 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-visible-region-v1.json +57 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-welcome-screen-v1.json +35 -0
- package/src/providers/sdk/v1/types/cli/index.ts +365 -0
- package/src/providers/sdk/v1/types/common/index.ts +229 -0
- package/src/providers/sdk/v1/validators/index.ts +19 -0
- package/src/providers/sdk/v1/validators/manifest.ts +110 -0
- package/src/providers/sdk/v1/validators/taint.ts +309 -0
- package/src/providers/spec/adapter.ts +235 -0
- package/src/providers/spec/cli-adapter.ts +1056 -0
- package/src/providers/spec/evaluator.ts +407 -0
- package/src/providers/spec/fsm-driver.ts +1410 -0
- package/src/providers/spec/fsm-evaluator.ts +272 -0
- package/src/providers/spec/fsm-loader.ts +120 -0
- package/src/providers/spec/fsm-types.ts +273 -0
- package/src/providers/spec/native-history-executor.ts +1128 -0
- package/src/providers/spec/pre-launch-trust.ts +104 -0
- package/src/providers/spec/route.ts +51 -0
- package/src/providers/spec/types.ts +262 -0
- package/src/providers/status-monitor.d.ts +7 -7
- package/src/providers/status-monitor.ts +37 -22
- package/src/providers/transcript-v2.ts +567 -0
- package/src/providers/types/interactive-prompt.ts +536 -0
- package/src/providers/version-archive.ts +64 -24
- package/src/providers/working-dir.ts +23 -0
- package/src/repo-mesh-types.ts +740 -14
- package/src/runtime-defaults.ts +39 -0
- package/src/sessions/registry.ts +6 -0
- package/src/shared-types-extra.ts +2 -4
- package/src/shared-types.d.ts +8 -0
- package/src/shared-types.ts +64 -1
- package/src/status/builders.ts +26 -6
- package/src/status/normalize.ts +2 -0
- package/src/status/reporter.ts +19 -1
- package/src/status/snapshot.ts +95 -26
- package/src/system/hash.ts +23 -0
- package/src/system/host-memory.ts +29 -12
- package/src/system/load-better-sqlite3.ts +68 -0
- package/src/types.ts +5 -0
- package/dist/cli-adapters/terminal-backends/xterm-backend.d.ts +0 -17
- package/dist/mesh/mesh-sync.d.ts +0 -53
- package/src/cli-adapters/terminal-backends/ghostty-vt-backend.d.ts +0 -16
- package/src/cli-adapters/terminal-backends/xterm-backend.ts +0 -97
- package/src/mesh/mesh-sync.ts +0 -111
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
// Mesh scheduling-runtime view — a read-only, derived projection of how the queue
|
|
2
|
+
// scheduler currently sees the mesh: which tie-break strategy is live, the global
|
|
3
|
+
// parallel caps and how much of them is consumed, and per-node load / priority /
|
|
4
|
+
// per-(node, provider) caps plus a structured "why this node can't take more write
|
|
5
|
+
// work right now" reason set.
|
|
6
|
+
//
|
|
7
|
+
// This is OBSERVABILITY ONLY. It re-derives the same numbers the live claim path
|
|
8
|
+
// (maybeAutoLaunchOneQueueSession → orderEligibleNodes → claimNextQueueTask) acts
|
|
9
|
+
// on, but never mutates anything and never drives a scheduling decision. It is built
|
|
10
|
+
// from the in-memory mesh config + the queue snapshot, so callers (mesh_status) get a
|
|
11
|
+
// consistent picture without touching the SQLite claim transaction. Keeping the
|
|
12
|
+
// derivation here — next to the constants it mirrors — means the exposed numbers and
|
|
13
|
+
// the enforced numbers stay defined in one module family.
|
|
14
|
+
|
|
15
|
+
import type { RepoMeshNodePolicy, RepoMeshSchedulingStrategy } from '../repo-mesh-types.js';
|
|
16
|
+
import {
|
|
17
|
+
normalizeMeshSchedulingStrategy,
|
|
18
|
+
resolveMaxParallelTasks,
|
|
19
|
+
resolveMaxReadonlyParallelTasks,
|
|
20
|
+
resolveNodeSchedulingPriority,
|
|
21
|
+
resolveProviderMaxParallel,
|
|
22
|
+
} from '../repo-mesh-types.js';
|
|
23
|
+
import { normalizeMeshNodeId } from '@adhdev/mesh-shared';
|
|
24
|
+
import type { MeshWorkQueueEntry } from './mesh-work-queue.js';
|
|
25
|
+
import { isTaskReadonly } from './mesh-work-queue.js';
|
|
26
|
+
|
|
27
|
+
/** Per-(node, provider) cap and its current consumption. */
|
|
28
|
+
export interface MeshNodeProviderSchedulingRuntime {
|
|
29
|
+
providerType: string;
|
|
30
|
+
/** Declared maxParallel cap for this (node, provider). Omitted when uncapped. */
|
|
31
|
+
maxParallel?: number;
|
|
32
|
+
/** Active (status='assigned') tasks on this node claimed by this provider. */
|
|
33
|
+
activeAssigned: number;
|
|
34
|
+
/** True when activeAssigned has reached maxParallel (a further claim is refused). */
|
|
35
|
+
capReached: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Per-node scheduling-runtime projection. */
|
|
39
|
+
export interface MeshNodeSchedulingRuntime {
|
|
40
|
+
nodeId: string;
|
|
41
|
+
/** Active (status='assigned') task count on this node — the least-loaded rank key. */
|
|
42
|
+
load: number;
|
|
43
|
+
/** Soft scheduling priority (PRIORITY rank key; higher = preferred). */
|
|
44
|
+
schedulingPriority: number;
|
|
45
|
+
/** Per-node concurrent-session cap, when configured. */
|
|
46
|
+
maxConcurrentSessions?: number;
|
|
47
|
+
/** Per-(node, provider) caps + consumption, when providerRoles declares any. */
|
|
48
|
+
providerRoles?: MeshNodeProviderSchedulingRuntime[];
|
|
49
|
+
/**
|
|
50
|
+
* True when the node currently cannot claim a NEW write (non-readonly) task —
|
|
51
|
+
* either the global write cap is exhausted or a node-local gate (active write
|
|
52
|
+
* assignment / a fully-consumed provider cap / session cap) blocks it.
|
|
53
|
+
*/
|
|
54
|
+
capReached: boolean;
|
|
55
|
+
/** Structured reasons backing capReached (empty when the node can take work). */
|
|
56
|
+
capReasons: string[];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Mesh-level scheduling-runtime projection. */
|
|
60
|
+
export interface MeshSchedulingRuntime {
|
|
61
|
+
/** Resolved tie-break strategy (defaults to 'first_eligible'). */
|
|
62
|
+
strategy: RepoMeshSchedulingStrategy;
|
|
63
|
+
/** Effective global write-task parallel cap (clamped). */
|
|
64
|
+
maxParallelTasks: number;
|
|
65
|
+
/** Derived read-only diagnosis cap (max(2, 2× write cap)) — mirrors the claim path. */
|
|
66
|
+
maxReadonlyParallelTasks: number;
|
|
67
|
+
/** Current global write (non-readonly) assigned-task load. */
|
|
68
|
+
activeWriteAssigned: number;
|
|
69
|
+
/** Current global read-only assigned-task load. */
|
|
70
|
+
activeReadonlyAssigned: number;
|
|
71
|
+
/** True when the global write cap is exhausted (no new write task can launch). */
|
|
72
|
+
globalWriteCapReached: boolean;
|
|
73
|
+
/** True when the read-only diagnosis cap is exhausted. */
|
|
74
|
+
globalReadonlyCapReached: boolean;
|
|
75
|
+
/** Per-node projections, in mesh config order. */
|
|
76
|
+
nodes: MeshNodeSchedulingRuntime[];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
interface MeshLike {
|
|
80
|
+
policy?: { schedulingStrategy?: unknown; maxParallelTasks?: unknown } | null;
|
|
81
|
+
nodes?: Array<{ id?: string; nodeId?: string; node_id?: string; policy?: RepoMeshNodePolicy | null; isLocalWorktree?: boolean }> | null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function isAssigned(task: MeshWorkQueueEntry): boolean {
|
|
85
|
+
return task.status === 'assigned';
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Build the scheduling-runtime projection for a mesh from its config + a snapshot of
|
|
90
|
+
* its work queue. Pure and side-effect free — safe to call on any read path.
|
|
91
|
+
*
|
|
92
|
+
* The derivation deliberately mirrors maybeAutoLaunchOneQueueSession's gates so the
|
|
93
|
+
* exposed "capReached/capReasons" match why a real claim would be refused:
|
|
94
|
+
* • global write cap → activeWriteAssigned >= maxParallelTasks
|
|
95
|
+
* • node write conflict→ a node already holding an assigned write task (worktree isolation)
|
|
96
|
+
* • provider cap → a (node, provider) at its declared maxParallel
|
|
97
|
+
* It does NOT re-evaluate eligibility/required-tags (those are task-specific); it only
|
|
98
|
+
* reports the capacity picture, which is what an operator needs to read load balance.
|
|
99
|
+
*/
|
|
100
|
+
export function buildMeshSchedulingRuntime(
|
|
101
|
+
mesh: MeshLike | null | undefined,
|
|
102
|
+
queue: MeshWorkQueueEntry[],
|
|
103
|
+
): MeshSchedulingRuntime {
|
|
104
|
+
const strategy = normalizeMeshSchedulingStrategy(mesh?.policy?.schedulingStrategy);
|
|
105
|
+
const maxParallelTasks = resolveMaxParallelTasks(mesh?.policy?.maxParallelTasks);
|
|
106
|
+
// Read-only diagnoses are exempt from the write cap and get their own higher
|
|
107
|
+
// safety cap (multiplier × the write cap, floor 2) — identical to the claim
|
|
108
|
+
// path, via the shared helper so the two can never drift.
|
|
109
|
+
const maxReadonlyParallelTasks = resolveMaxReadonlyParallelTasks(maxParallelTasks);
|
|
110
|
+
|
|
111
|
+
const assignedTasks = (Array.isArray(queue) ? queue : []).filter(isAssigned);
|
|
112
|
+
const activeWriteAssigned = assignedTasks.filter(t => !isTaskReadonly(t)).length;
|
|
113
|
+
const activeReadonlyAssigned = assignedTasks.filter(isTaskReadonly).length;
|
|
114
|
+
const globalWriteCapReached = activeWriteAssigned >= maxParallelTasks;
|
|
115
|
+
const globalReadonlyCapReached = activeReadonlyAssigned >= maxReadonlyParallelTasks;
|
|
116
|
+
|
|
117
|
+
// Pre-bucket assigned tasks by node so per-node load and per-provider counts are
|
|
118
|
+
// a single pass rather than O(nodes × queue).
|
|
119
|
+
const writeAssignedByNode = new Map<string, number>();
|
|
120
|
+
const assignedByNode = new Map<string, number>();
|
|
121
|
+
const providerCountByNode = new Map<string, Map<string, number>>();
|
|
122
|
+
for (const task of assignedTasks) {
|
|
123
|
+
const nodeId = typeof task.assignedNodeId === 'string' ? task.assignedNodeId.trim() : '';
|
|
124
|
+
if (!nodeId) continue;
|
|
125
|
+
assignedByNode.set(nodeId, (assignedByNode.get(nodeId) ?? 0) + 1);
|
|
126
|
+
if (!isTaskReadonly(task)) {
|
|
127
|
+
writeAssignedByNode.set(nodeId, (writeAssignedByNode.get(nodeId) ?? 0) + 1);
|
|
128
|
+
}
|
|
129
|
+
const provider = typeof task.assignedProviderType === 'string' ? task.assignedProviderType : '';
|
|
130
|
+
if (provider) {
|
|
131
|
+
let byProvider = providerCountByNode.get(nodeId);
|
|
132
|
+
if (!byProvider) { byProvider = new Map(); providerCountByNode.set(nodeId, byProvider); }
|
|
133
|
+
byProvider.set(provider, (byProvider.get(provider) ?? 0) + 1);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const nodes: MeshNodeSchedulingRuntime[] = [];
|
|
138
|
+
for (const rawNode of (Array.isArray(mesh?.nodes) ? mesh!.nodes! : [])) {
|
|
139
|
+
const nodeId = normalizeMeshNodeId(rawNode);
|
|
140
|
+
if (!nodeId) continue;
|
|
141
|
+
const policy = (rawNode?.policy || undefined) as RepoMeshNodePolicy | undefined;
|
|
142
|
+
const load = assignedByNode.get(nodeId) ?? 0;
|
|
143
|
+
const schedulingPriority = resolveNodeSchedulingPriority(policy);
|
|
144
|
+
|
|
145
|
+
const capReasons: string[] = [];
|
|
146
|
+
if (globalWriteCapReached) capReasons.push('global_max_parallel_tasks_reached');
|
|
147
|
+
// Write isolation: a node already holding an assigned write task can't take another.
|
|
148
|
+
if ((writeAssignedByNode.get(nodeId) ?? 0) > 0) capReasons.push('node_has_active_assignment');
|
|
149
|
+
|
|
150
|
+
// Per-(node, provider) caps, with live consumption.
|
|
151
|
+
let providerRoles: MeshNodeProviderSchedulingRuntime[] | undefined;
|
|
152
|
+
const declaredRoles = Array.isArray(policy?.providerRoles) ? policy!.providerRoles! : [];
|
|
153
|
+
if (declaredRoles.length) {
|
|
154
|
+
const byProvider = providerCountByNode.get(nodeId);
|
|
155
|
+
providerRoles = [];
|
|
156
|
+
for (const role of declaredRoles) {
|
|
157
|
+
if (!role || typeof role !== 'object') continue;
|
|
158
|
+
const providerType = typeof role.providerType === 'string' ? role.providerType.trim() : '';
|
|
159
|
+
if (!providerType) continue;
|
|
160
|
+
const maxParallel = resolveProviderMaxParallel(policy, providerType);
|
|
161
|
+
const activeAssigned = byProvider?.get(providerType) ?? 0;
|
|
162
|
+
const capReached = maxParallel !== undefined && activeAssigned >= maxParallel;
|
|
163
|
+
providerRoles.push({
|
|
164
|
+
providerType,
|
|
165
|
+
...(maxParallel !== undefined ? { maxParallel } : {}),
|
|
166
|
+
activeAssigned,
|
|
167
|
+
capReached,
|
|
168
|
+
});
|
|
169
|
+
if (capReached) capReasons.push(`max_provider_parallel_reached:${providerType}`);
|
|
170
|
+
}
|
|
171
|
+
if (!providerRoles.length) providerRoles = undefined;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const maxConcurrentSessions = Number(policy?.maxConcurrentSessions);
|
|
175
|
+
const hasSessionCap = Number.isFinite(maxConcurrentSessions) && maxConcurrentSessions >= 0;
|
|
176
|
+
|
|
177
|
+
nodes.push({
|
|
178
|
+
nodeId,
|
|
179
|
+
load,
|
|
180
|
+
schedulingPriority,
|
|
181
|
+
...(hasSessionCap ? { maxConcurrentSessions: Math.floor(maxConcurrentSessions) } : {}),
|
|
182
|
+
...(providerRoles ? { providerRoles } : {}),
|
|
183
|
+
capReached: capReasons.length > 0,
|
|
184
|
+
capReasons,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
strategy,
|
|
190
|
+
maxParallelTasks,
|
|
191
|
+
maxReadonlyParallelTasks,
|
|
192
|
+
activeWriteAssigned,
|
|
193
|
+
activeReadonlyAssigned,
|
|
194
|
+
globalWriteCapReached,
|
|
195
|
+
globalReadonlyCapReached,
|
|
196
|
+
nodes,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CANON-IDENTITY single-flight dispatch guard.
|
|
3
|
+
*
|
|
4
|
+
* A queue task is double-dispatched into two worker sessions when a SECOND dispatch
|
|
5
|
+
* path opens after the first has already claimed + dispatched the task. The two
|
|
6
|
+
* observed paths:
|
|
7
|
+
* - the idle-claim drain / auto-launch both funnel through tryAssignQueueTask,
|
|
8
|
+
* which is serialized by the atomic claim transaction (claimNextQueueTask) — so
|
|
9
|
+
* two concurrent claims can never both win; and
|
|
10
|
+
* - the operator requeue tool (mesh_queue_requeue → requeueTask), which flips an
|
|
11
|
+
* already-`assigned` row back to `pending` REGARDLESS of whether the worker
|
|
12
|
+
* holding it is still generating. A requeue issued while the worker is mid-turn
|
|
13
|
+
* re-opens the task for a SECOND session to claim — the live `ade8586d` race.
|
|
14
|
+
*
|
|
15
|
+
* The atomic claim already discriminates pending-vs-assigned, but it cannot tell a
|
|
16
|
+
* GENUINELY-in-flight assigned row (dispatched, worker generating) from a STALE
|
|
17
|
+
* assigned row (dead session, dispatch never confirmed) — and the requeue contract
|
|
18
|
+
* must still reopen the stale case. This module is that discriminator: a task id is
|
|
19
|
+
* registered here ONLY by the dispatch path at the moment it hands the claimed task
|
|
20
|
+
* to a transport, and cleared the moment the task leaves the `assigned` state
|
|
21
|
+
* (terminal completion/failure, dispatch-failure requeue, cancel, reclaim, or a
|
|
22
|
+
* forced requeue). requeueTask consults it and refuses (no-op) to reopen a task that
|
|
23
|
+
* is still in-flight unless the caller passes `force`.
|
|
24
|
+
*
|
|
25
|
+
* Dependency-free leaf (no imports) so both mesh-queue-assignment (begin) and
|
|
26
|
+
* mesh-work-queue (clear + the requeue guard) can import it without a cycle. The key
|
|
27
|
+
* is `${meshId}::${taskId}`; a task id is a single-form UUID, so no daemon-id
|
|
28
|
+
* normalization is needed on the key itself.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
const inFlight = new Set<string>();
|
|
32
|
+
|
|
33
|
+
function key(meshId: string, taskId: string): string {
|
|
34
|
+
return `${meshId}::${taskId}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Mark a task as actively dispatched/generating (in-flight). Called by the dispatch
|
|
39
|
+
* path right after a successful claim, before/as the task is handed to a transport.
|
|
40
|
+
* Returns true when this call transitioned the task into the in-flight set, false
|
|
41
|
+
* when it was already in-flight (a redundant begin — the caller may treat that as a
|
|
42
|
+
* signal that a dispatch is already live for this task).
|
|
43
|
+
*/
|
|
44
|
+
export function beginTaskDispatchInFlight(meshId: string, taskId: string): boolean {
|
|
45
|
+
if (!meshId || !taskId) return false;
|
|
46
|
+
const k = key(meshId, taskId);
|
|
47
|
+
if (inFlight.has(k)) return false;
|
|
48
|
+
inFlight.add(k);
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** True while a task is actively dispatched/generating (registered by the dispatch
|
|
53
|
+
* path and not yet cleared by a terminal/requeue/cancel transition). */
|
|
54
|
+
export function isTaskDispatchInFlight(meshId: string, taskId: string): boolean {
|
|
55
|
+
if (!meshId || !taskId) return false;
|
|
56
|
+
return inFlight.has(key(meshId, taskId));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Clear a task's in-flight mark. Called whenever the task leaves the `assigned`
|
|
60
|
+
* state (terminal completion/failure, dispatch-failure requeue, cancel, reclaim, or
|
|
61
|
+
* a forced requeue). Idempotent / safe to call when the task was never in-flight. */
|
|
62
|
+
export function endTaskDispatchInFlight(meshId: string, taskId: string): void {
|
|
63
|
+
if (!meshId || !taskId) return;
|
|
64
|
+
inFlight.delete(key(meshId, taskId));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Test-only: drop all in-flight marks so a fresh test starts from a clean guard. */
|
|
68
|
+
export function __resetTaskDispatchInFlightForTests(): void {
|
|
69
|
+
inFlight.clear();
|
|
70
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M7: Operational stats (time/attempts) derived from existing truth.
|
|
3
|
+
*
|
|
4
|
+
* No cost/token accounting — ADHDev observes PTY/CDP and cannot see API
|
|
5
|
+
* tokens (explicit non-goal). Everything here is derived at query time from
|
|
6
|
+
* the SQLite ledger and queue rows; there is no separate aggregate table.
|
|
7
|
+
* Tasks with missing ledger evidence report incompleteEvidence instead of
|
|
8
|
+
* estimated numbers.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { readLedgerEntries } from './mesh-ledger.js';
|
|
12
|
+
import type { MeshLedgerEntry } from './mesh-ledger.js';
|
|
13
|
+
import { getQueue } from './mesh-work-queue.js';
|
|
14
|
+
|
|
15
|
+
export interface MeshTaskStats {
|
|
16
|
+
taskId: string;
|
|
17
|
+
status: string;
|
|
18
|
+
dispatchedAt: string | null;
|
|
19
|
+
terminalAt: string | null;
|
|
20
|
+
terminalKind: 'task_completed' | 'task_failed' | null;
|
|
21
|
+
/** dispatched → terminal wall clock; null when evidence is incomplete. */
|
|
22
|
+
durationMs: number | null;
|
|
23
|
+
/** Number of task_dispatched ledger entries observed for this task. */
|
|
24
|
+
dispatchCount: number;
|
|
25
|
+
/** Queue requeueCount (0 when the row is gone or never requeued). */
|
|
26
|
+
requeueCount: number;
|
|
27
|
+
/** True when dispatch or terminal ledger evidence is missing — numbers are withheld, never estimated. */
|
|
28
|
+
incompleteEvidence?: true;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface MeshMissionStats {
|
|
32
|
+
missionId: string;
|
|
33
|
+
taskCount: number;
|
|
34
|
+
completed: number;
|
|
35
|
+
failed: number;
|
|
36
|
+
/** Sum of per-task durations with complete evidence. */
|
|
37
|
+
totalDurationMs: number;
|
|
38
|
+
/** First dispatch → last terminal across the mission's tasks; null without complete endpoints. */
|
|
39
|
+
wallClockMs: number | null;
|
|
40
|
+
/** Total requeue attempts across the mission's tasks. */
|
|
41
|
+
retries: number;
|
|
42
|
+
/** Task ids whose ledger evidence was incomplete (excluded from sums). */
|
|
43
|
+
incompleteTaskIds: string[];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function readPayloadTaskId(entry: MeshLedgerEntry): string {
|
|
47
|
+
const value = entry.payload?.taskId;
|
|
48
|
+
return typeof value === 'string' && value.trim() ? value.trim() : '';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function parseTime(value: string | null | undefined): number | null {
|
|
52
|
+
if (!value) return null;
|
|
53
|
+
const parsed = new Date(value).getTime();
|
|
54
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Compute per-task stats from ledger entries. Scans a bounded tail window —
|
|
59
|
+
* stats are an operational view of recent work, not a full historical report.
|
|
60
|
+
*/
|
|
61
|
+
export function computeMeshTaskStats(meshId: string, opts?: { taskIds?: string[]; missionId?: string; tail?: number }): MeshTaskStats[] {
|
|
62
|
+
const queue = getQueue(meshId);
|
|
63
|
+
const queueById = new Map(queue.map(task => [task.id, task]));
|
|
64
|
+
|
|
65
|
+
let targetIds: string[];
|
|
66
|
+
if (opts?.taskIds?.length) {
|
|
67
|
+
targetIds = [...new Set(opts.taskIds)];
|
|
68
|
+
} else if (opts?.missionId) {
|
|
69
|
+
targetIds = queue.filter(task => task.missionId === opts.missionId).map(task => task.id);
|
|
70
|
+
} else {
|
|
71
|
+
targetIds = queue.map(task => task.id);
|
|
72
|
+
}
|
|
73
|
+
if (targetIds.length === 0) return [];
|
|
74
|
+
const targetSet = new Set(targetIds);
|
|
75
|
+
|
|
76
|
+
const entries = readLedgerEntries(meshId, { tail: opts?.tail ?? 1000 });
|
|
77
|
+
const dispatches = new Map<string, { first: string; count: number }>();
|
|
78
|
+
const terminals = new Map<string, { at: string; kind: 'task_completed' | 'task_failed' }>();
|
|
79
|
+
for (const entry of entries) {
|
|
80
|
+
const taskId = readPayloadTaskId(entry);
|
|
81
|
+
if (!taskId || !targetSet.has(taskId)) continue;
|
|
82
|
+
if (entry.kind === 'task_dispatched') {
|
|
83
|
+
const existing = dispatches.get(taskId);
|
|
84
|
+
if (existing) existing.count += 1;
|
|
85
|
+
else dispatches.set(taskId, { first: entry.timestamp, count: 1 });
|
|
86
|
+
} else if (entry.kind === 'task_completed' || entry.kind === 'task_failed') {
|
|
87
|
+
// Last terminal wins (requeued tasks can have multiple terminals).
|
|
88
|
+
terminals.set(taskId, { at: entry.timestamp, kind: entry.kind });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return targetIds.map(taskId => {
|
|
93
|
+
const queueEntry = queueById.get(taskId);
|
|
94
|
+
const dispatch = dispatches.get(taskId);
|
|
95
|
+
const terminal = terminals.get(taskId);
|
|
96
|
+
// Direct dispatches (mesh_send_task) have no work-queue row, so queueEntry is undefined.
|
|
97
|
+
// Derive a terminal status from the attributed terminal ledger entry instead of reporting
|
|
98
|
+
// status='unknown' — without this a completed direct task showed unknown + terminalKind=null
|
|
99
|
+
// even though its task_completed event fired (see mesh-events-coordinator Fix B attribution).
|
|
100
|
+
const status = queueEntry?.status
|
|
101
|
+
?? (terminal
|
|
102
|
+
? (terminal.kind === 'task_completed' ? 'completed' : 'failed')
|
|
103
|
+
: 'unknown');
|
|
104
|
+
const isTerminalStatus = status === 'completed' || status === 'failed' || status === 'cancelled';
|
|
105
|
+
const dispatchTime = parseTime(dispatch?.first ?? queueEntry?.dispatchTimestamp);
|
|
106
|
+
const terminalTime = parseTime(terminal?.at);
|
|
107
|
+
const stats: MeshTaskStats = {
|
|
108
|
+
taskId,
|
|
109
|
+
status,
|
|
110
|
+
dispatchedAt: dispatch?.first ?? queueEntry?.dispatchTimestamp ?? null,
|
|
111
|
+
terminalAt: terminal?.at ?? null,
|
|
112
|
+
terminalKind: terminal?.kind ?? null,
|
|
113
|
+
durationMs: null,
|
|
114
|
+
dispatchCount: dispatch?.count ?? 0,
|
|
115
|
+
requeueCount: queueEntry?.requeueCount ?? 0,
|
|
116
|
+
};
|
|
117
|
+
if (dispatchTime !== null && terminalTime !== null && terminalTime >= dispatchTime) {
|
|
118
|
+
stats.durationMs = terminalTime - dispatchTime;
|
|
119
|
+
} else if (isTerminalStatus) {
|
|
120
|
+
// Terminal task without complete dispatch+terminal ledger evidence:
|
|
121
|
+
// withhold numbers rather than estimate (M7 rule).
|
|
122
|
+
stats.incompleteEvidence = true;
|
|
123
|
+
}
|
|
124
|
+
return stats;
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Mission rollup — derived from per-task stats, no stored aggregates. */
|
|
129
|
+
export function computeMeshMissionStats(meshId: string, missionId: string): MeshMissionStats {
|
|
130
|
+
const tasks = computeMeshTaskStats(meshId, { missionId });
|
|
131
|
+
const stats: MeshMissionStats = {
|
|
132
|
+
missionId,
|
|
133
|
+
taskCount: tasks.length,
|
|
134
|
+
completed: 0,
|
|
135
|
+
failed: 0,
|
|
136
|
+
totalDurationMs: 0,
|
|
137
|
+
wallClockMs: null,
|
|
138
|
+
retries: 0,
|
|
139
|
+
incompleteTaskIds: [],
|
|
140
|
+
};
|
|
141
|
+
let firstDispatch: number | null = null;
|
|
142
|
+
let lastTerminal: number | null = null;
|
|
143
|
+
for (const task of tasks) {
|
|
144
|
+
if (task.status === 'completed') stats.completed += 1;
|
|
145
|
+
else if (task.status === 'failed') stats.failed += 1;
|
|
146
|
+
stats.retries += task.requeueCount;
|
|
147
|
+
if (task.incompleteEvidence) {
|
|
148
|
+
stats.incompleteTaskIds.push(task.taskId);
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (task.durationMs !== null) stats.totalDurationMs += task.durationMs;
|
|
152
|
+
const dispatchTime = parseTime(task.dispatchedAt);
|
|
153
|
+
const terminalTime = parseTime(task.terminalAt);
|
|
154
|
+
if (dispatchTime !== null && (firstDispatch === null || dispatchTime < firstDispatch)) firstDispatch = dispatchTime;
|
|
155
|
+
if (terminalTime !== null && (lastTerminal === null || terminalTime > lastTerminal)) lastTerminal = terminalTime;
|
|
156
|
+
}
|
|
157
|
+
if (firstDispatch !== null && lastTerminal !== null && lastTerminal >= firstDispatch) {
|
|
158
|
+
stats.wallClockMs = lastTerminal - firstDispatch;
|
|
159
|
+
}
|
|
160
|
+
return stats;
|
|
161
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// mesh-unresolved-forward-outbox — durable retry for unresolved-delegate forwards
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// A REMOTE worker daemon that is P2P-remote-controlled by a coordinator is NOT a
|
|
5
|
+
// member of the coordinator's mesh — it has no local mesh record. So when its
|
|
6
|
+
// completion event reaches setupMeshEventForwarding, resolveWorkerDelegateRouting
|
|
7
|
+
// returns mesh_unresolved: the worker carries the coordinator daemon anchor but no
|
|
8
|
+
// resolvable mesh id, so the normal queue-then-coordinator-pull path can't be used
|
|
9
|
+
// (the coordinator's reconcile PHASE 1 only pulls mesh.nodes, and this worker is in
|
|
10
|
+
// none of them — see docs/refactoring/2026-06-16-mesh-completion-polling-single-model.md).
|
|
11
|
+
//
|
|
12
|
+
// The ONLY delivery route for this case is a directed push to the coordinator
|
|
13
|
+
// daemon (mesh_forward_event over P2P). Previously that push was fire-and-forget:
|
|
14
|
+
// a single dispatchMeshCommand whose rejection was only logged, so one transient
|
|
15
|
+
// P2P failure dropped the completion forever (delivery_unroutable).
|
|
16
|
+
//
|
|
17
|
+
// This outbox makes that push DURABLE without inventing a new persistence layer:
|
|
18
|
+
// it reuses the existing mesh_pending_events SQLite table (the same store every
|
|
19
|
+
// other mesh coordinator event is persisted to), under a synthetic, reserved
|
|
20
|
+
// "mesh id" namespace so it never collides with a real mesh's queue. The worker's
|
|
21
|
+
// reconcile tick (PHASE 0) peeks the outbox, pushes each entry to its coordinator,
|
|
22
|
+
// and marks it drained (acked) ONLY on a successful push — a failed push leaves the
|
|
23
|
+
// row undrained for the next tick. Entries that exceed a max age are expired so the
|
|
24
|
+
// outbox can't grow unbounded when a coordinator stays permanently unreachable.
|
|
25
|
+
//
|
|
26
|
+
// Idempotency:
|
|
27
|
+
// - Enqueue is idempotent on the event fingerprint (mesh_pending_events has a
|
|
28
|
+
// UNIQUE (mesh_id, fingerprint) index + INSERT OR IGNORE), so the same
|
|
29
|
+
// completion fired twice on the worker queues once.
|
|
30
|
+
// - The receiver dedups independently: handleMeshForwardEvent → injectMeshSystemMessage
|
|
31
|
+
// → queuePendingMeshCoordinatorEvent recomputes the fingerprint and suppresses a
|
|
32
|
+
// duplicate, so an at-least-once retry that double-delivers is harmless.
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
import { randomUUID } from 'crypto';
|
|
36
|
+
import { LOG } from '../logging/logger.js';
|
|
37
|
+
import { MeshRuntimeStore } from './mesh-runtime-store.js';
|
|
38
|
+
import { buildPendingEventFingerprint, type PendingMeshCoordinatorEvent } from './mesh-events-pending.js';
|
|
39
|
+
import { readNonEmptyString } from './mesh-events-utils.js';
|
|
40
|
+
|
|
41
|
+
// Reserved synthetic mesh id for the worker-side unresolved-forward outbox. The `::`
|
|
42
|
+
// and leading `__` make it impossible to collide with a real mesh id (which are
|
|
43
|
+
// `mesh_*` / `mreg_*` slugs). Scoping rows by coordinator_daemon_id inside this one
|
|
44
|
+
// namespace lets a single worker forward to multiple coordinators.
|
|
45
|
+
export const UNRESOLVED_FORWARD_OUTBOX_MESH_ID = '__unresolved_forward_outbox__';
|
|
46
|
+
|
|
47
|
+
// Entries older than this are expired (dropped) rather than retried forever — a
|
|
48
|
+
// coordinator that has been unreachable this long is treated as gone. Generous
|
|
49
|
+
// enough to ride out a long coordinator outage / restart, bounded enough that a
|
|
50
|
+
// permanently-dead coordinator can't accumulate outbox rows indefinitely.
|
|
51
|
+
const UNRESOLVED_FORWARD_MAX_AGE_MS = 30 * 60 * 1000; // 30 minutes
|
|
52
|
+
|
|
53
|
+
export interface UnresolvedForwardEntry {
|
|
54
|
+
/** Row id in mesh_pending_events; pass back to ack/expire after a push attempt. */
|
|
55
|
+
id: string;
|
|
56
|
+
/** The coordinator daemon to push this event to (mesh_forward_event target). */
|
|
57
|
+
coordinatorDaemonId: string;
|
|
58
|
+
/** The flat payload to forward (already shaped for handleMeshForwardEvent). */
|
|
59
|
+
payload: Record<string, unknown>;
|
|
60
|
+
/** When the entry was first enqueued (epoch ms) — used for age-based expiry. */
|
|
61
|
+
queuedAt: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function getStore(): MeshRuntimeStore | undefined {
|
|
65
|
+
try { return MeshRuntimeStore.getInstance(); } catch { return undefined; }
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Durably enqueue an unresolved-delegate forward for a coordinator daemon. The
|
|
70
|
+
* `forwardPayload` is the flat shape handleMeshForwardEvent reads on the coordinator.
|
|
71
|
+
* Idempotent on the event fingerprint. Returns true when a new row was written (or a
|
|
72
|
+
* duplicate was harmlessly ignored), false on a hard persistence failure.
|
|
73
|
+
*/
|
|
74
|
+
export function enqueueUnresolvedDelegateForward(
|
|
75
|
+
coordinatorDaemonId: string,
|
|
76
|
+
eventName: string,
|
|
77
|
+
forwardPayload: Record<string, unknown>,
|
|
78
|
+
): boolean {
|
|
79
|
+
const target = readNonEmptyString(coordinatorDaemonId);
|
|
80
|
+
const event = readNonEmptyString(eventName);
|
|
81
|
+
if (!target || !event) return false;
|
|
82
|
+
const store = getStore();
|
|
83
|
+
if (!store) return false;
|
|
84
|
+
|
|
85
|
+
const queuedAt = Date.now();
|
|
86
|
+
// Reuse the standard pending-event fingerprint so enqueue is idempotent AND the
|
|
87
|
+
// receiver's own dedup keys on a comparable identity. We synthesise the minimal
|
|
88
|
+
// PendingMeshCoordinatorEvent shape buildPendingEventFingerprint needs.
|
|
89
|
+
const fingerprintSource: PendingMeshCoordinatorEvent = {
|
|
90
|
+
event,
|
|
91
|
+
meshId: UNRESOLVED_FORWARD_OUTBOX_MESH_ID,
|
|
92
|
+
nodeLabel: readNonEmptyString(forwardPayload.nodeId) || readNonEmptyString(forwardPayload.workspace) || 'unresolved-delegate',
|
|
93
|
+
nodeId: readNonEmptyString(forwardPayload.nodeId) || undefined,
|
|
94
|
+
workspace: readNonEmptyString(forwardPayload.workspace) || undefined,
|
|
95
|
+
metadataEvent: forwardPayload,
|
|
96
|
+
queuedAt,
|
|
97
|
+
targetCoordinatorDaemonId: target,
|
|
98
|
+
};
|
|
99
|
+
// Scope the fingerprint to the coordinator so two coordinators awaiting the same
|
|
100
|
+
// worker session don't collapse into one outbox row.
|
|
101
|
+
const fingerprint = `${target}::${buildPendingEventFingerprint(fingerprintSource)}`;
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
const inserted = store.insertPendingEvent({
|
|
105
|
+
id: randomUUID(),
|
|
106
|
+
meshId: UNRESOLVED_FORWARD_OUTBOX_MESH_ID,
|
|
107
|
+
coordinatorDaemonId: target,
|
|
108
|
+
event,
|
|
109
|
+
// Store the flat forward payload + the queue timestamp so the retry tick can
|
|
110
|
+
// rebuild the push args and apply age-based expiry without a schema change.
|
|
111
|
+
payload: { forwardPayload, coordinatorDaemonId: target, queuedAt },
|
|
112
|
+
fingerprint,
|
|
113
|
+
queuedAt,
|
|
114
|
+
});
|
|
115
|
+
if (inserted) {
|
|
116
|
+
LOG.info('MeshEvents', `Durably queued unresolved-delegate ${event} for coordinator ${target} (outbox)`);
|
|
117
|
+
}
|
|
118
|
+
return true;
|
|
119
|
+
} catch (e: any) {
|
|
120
|
+
LOG.warn('MeshEvents', `Failed to persist unresolved-delegate forward to outbox: ${e?.message || e}`);
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Peek (non-destructive) every undrained outbox entry across all coordinators. */
|
|
126
|
+
export function peekUnresolvedDelegateForwards(): UnresolvedForwardEntry[] {
|
|
127
|
+
const store = getStore();
|
|
128
|
+
if (!store) return [];
|
|
129
|
+
let rows: Array<{ id: string; event: string; payload: unknown }>;
|
|
130
|
+
try {
|
|
131
|
+
rows = store.peekPendingEvents(UNRESOLVED_FORWARD_OUTBOX_MESH_ID);
|
|
132
|
+
} catch {
|
|
133
|
+
return [];
|
|
134
|
+
}
|
|
135
|
+
const out: UnresolvedForwardEntry[] = [];
|
|
136
|
+
for (const row of rows) {
|
|
137
|
+
const stored = row.payload && typeof row.payload === 'object' ? row.payload as Record<string, unknown> : {};
|
|
138
|
+
const coordinatorDaemonId = readNonEmptyString(stored.coordinatorDaemonId);
|
|
139
|
+
const forwardPayload = stored.forwardPayload && typeof stored.forwardPayload === 'object'
|
|
140
|
+
? stored.forwardPayload as Record<string, unknown>
|
|
141
|
+
: undefined;
|
|
142
|
+
if (!coordinatorDaemonId || !forwardPayload) continue;
|
|
143
|
+
const queuedAt = typeof stored.queuedAt === 'number' ? stored.queuedAt : 0;
|
|
144
|
+
out.push({ id: row.id, coordinatorDaemonId, payload: forwardPayload, queuedAt });
|
|
145
|
+
}
|
|
146
|
+
return out;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Mark an outbox entry delivered (acked) after a successful push. */
|
|
150
|
+
export function ackUnresolvedDelegateForward(id: string): void {
|
|
151
|
+
const store = getStore();
|
|
152
|
+
if (!store) return;
|
|
153
|
+
try { store.markPendingEventsDrainedById([id]); } catch { /* best-effort */ }
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Drop outbox entries that have exceeded the max retry age. Returns the count
|
|
158
|
+
* expired so the caller can log a fail-loud trace (a dropped completion is a real
|
|
159
|
+
* loss; it must be visible, not silent).
|
|
160
|
+
*/
|
|
161
|
+
export function expireStaleUnresolvedDelegateForwards(nowMs: number = Date.now()): number {
|
|
162
|
+
const entries = peekUnresolvedDelegateForwards();
|
|
163
|
+
const staleIds = entries
|
|
164
|
+
.filter(e => e.queuedAt > 0 && nowMs - e.queuedAt >= UNRESOLVED_FORWARD_MAX_AGE_MS)
|
|
165
|
+
.map(e => e.id);
|
|
166
|
+
if (staleIds.length === 0) return 0;
|
|
167
|
+
const store = getStore();
|
|
168
|
+
if (!store) return 0;
|
|
169
|
+
try {
|
|
170
|
+
const removed = store.deletePendingEventsById(staleIds);
|
|
171
|
+
if (removed > 0) {
|
|
172
|
+
LOG.warn('MeshEvents', `Expired ${removed} unresolved-delegate forward(s) after ${Math.round(UNRESOLVED_FORWARD_MAX_AGE_MS / 60000)}m of failed retries — coordinator unreachable, completion dropped`);
|
|
173
|
+
}
|
|
174
|
+
return removed;
|
|
175
|
+
} catch {
|
|
176
|
+
return 0;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** Test helper: purge the entire outbox. */
|
|
181
|
+
export function __clearUnresolvedDelegateForwardOutboxForTests(): void {
|
|
182
|
+
const store = getStore();
|
|
183
|
+
if (!store) return;
|
|
184
|
+
try { store.clearPendingEventsForMesh(UNRESOLVED_FORWARD_OUTBOX_MESH_ID); } catch { /* nothing to clear */ }
|
|
185
|
+
}
|