@adhdev/daemon-core 0.9.82-rc.39 → 0.9.82-rc.391
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 +67 -2
- package/dist/cli-adapters/cli-script-runner.d.ts +117 -0
- package/dist/cli-adapters/cli-state-engine.d.ts +198 -0
- package/dist/cli-adapters/provider-cli-adapter.d.ts +121 -64
- package/dist/cli-adapters/provider-cli-parse.d.ts +4 -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 +14 -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 +2 -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 +116 -0
- package/dist/commands/mesh-coordinator.d.ts +85 -1
- package/dist/commands/router.d.ts +237 -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 +77 -1
- 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 +23 -0
- package/dist/git/git-types.d.ts +2 -50
- package/dist/git/git-worktree.d.ts +22 -0
- package/dist/git/index.d.ts +2 -0
- package/dist/index.d.ts +57 -13
- package/dist/index.js +46318 -18260
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +46450 -18533
- 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-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 +19 -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 +55 -0
- package/dist/mesh/mesh-events-stale.d.ts +52 -0
- package/dist/mesh/mesh-events-utils.d.ts +64 -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-missions.d.ts +164 -0
- package/dist/mesh/mesh-node-identity.d.ts +289 -0
- package/dist/mesh/mesh-queue-assignment.d.ts +86 -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 +447 -0
- package/dist/mesh/mesh-scheduling-runtime.d.ts +78 -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 +247 -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 +135 -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 +13 -1
- package/dist/providers/cli-provider-instance.d.ts +157 -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 +14 -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 +407 -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 +376 -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 +1 -0
- 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 +1221 -0
- package/src/cli-adapters/provider-cli-adapter.d.ts +1 -1
- package/src/cli-adapters/provider-cli-adapter.ts +836 -1141
- package/src/cli-adapters/provider-cli-parse.d.ts +1 -0
- package/src/cli-adapters/provider-cli-parse.ts +13 -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 +90 -0
- package/src/cli-adapters/session-host-transport.ts +2 -1
- package/src/cli-adapters/terminal-backends/ghostty-vt-backend.ts +53 -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 +880 -0
- package/src/commands/chat-commands.ts +19 -1841
- package/src/commands/cli-manager.ts +513 -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 +657 -0
- package/src/commands/high-family/mesh-events.ts +80 -0
- package/src/commands/high-family/mesh-status.ts +678 -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 +230 -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 +836 -0
- package/src/commands/med-family/mesh-host-pairing.ts +234 -0
- package/src/commands/med-family/mesh-queue.ts +131 -0
- package/src/commands/med-family/mesh-restart.ts +92 -0
- package/src/commands/med-family/types.ts +120 -0
- package/src/commands/mesh-coordinator.ts +334 -124
- package/src/commands/router.ts +2476 -3207
- 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 +353 -22
- package/src/config/recent-activity.ts +8 -2
- 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 +55 -14
- package/src/git/git-diff.ts +81 -11
- package/src/git/git-executor.ts +12 -0
- package/src/git/git-status.ts +574 -43
- package/src/git/git-types.ts +14 -62
- package/src/git/git-worktree.ts +76 -1
- package/src/git/index.ts +16 -0
- package/src/index.ts +171 -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-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 +1693 -0
- package/src/mesh/mesh-event-trace.ts +67 -0
- package/src/mesh/mesh-events-coordinator.ts +30 -0
- package/src/mesh/mesh-events-pending.ts +590 -0
- package/src/mesh/mesh-events-stale.ts +363 -0
- package/src/mesh/mesh-events-utils.ts +425 -0
- package/src/mesh/mesh-events.ts +32 -1035
- package/src/mesh/mesh-fast-forward.ts +839 -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 +596 -102
- package/src/mesh/mesh-missions.ts +322 -0
- package/src/mesh/mesh-node-identity.ts +1887 -0
- package/src/mesh/mesh-queue-assignment.ts +1491 -0
- package/src/mesh/mesh-reconcile-loop.ts +1527 -0
- package/src/mesh/mesh-refine-batch.ts +197 -0
- package/src/mesh/mesh-refine-gates.ts +1652 -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 +1795 -0
- package/src/mesh/mesh-scheduling-runtime.ts +199 -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 +1141 -141
- package/src/mesh/preview-freshness.ts +118 -0
- package/src/mesh/refine-config.ts +423 -0
- package/src/mesh/worktree-bootstrap-config.ts +331 -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 +46 -6
- package/src/providers/cli-provider-instance.ts +1552 -102
- 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 +30 -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 +1304 -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 +542 -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
package/src/mesh/mesh-ledger.ts
CHANGED
|
@@ -13,11 +13,12 @@
|
|
|
13
13
|
* Safety: mode 0o600, atomic append via appendFileSync
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import { existsSync, mkdirSync, readFileSync,
|
|
16
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync, statSync, renameSync, writeFileSync } from 'fs';
|
|
17
17
|
import { join } from 'path';
|
|
18
18
|
import { randomUUID } from 'crypto';
|
|
19
19
|
import { getConfigDir } from '../config/config.js';
|
|
20
20
|
import { EventEmitter } from 'events';
|
|
21
|
+
import { MeshRuntimeStore } from './mesh-runtime-store.js';
|
|
21
22
|
// ─── Types ──────────────────────────────────────
|
|
22
23
|
|
|
23
24
|
export type MeshLedgerKind =
|
|
@@ -26,16 +27,28 @@ export type MeshLedgerKind =
|
|
|
26
27
|
| 'task_failed'
|
|
27
28
|
| 'task_stalled'
|
|
28
29
|
| 'task_approval_needed'
|
|
30
|
+
| 'p2p_dispatch_failed'
|
|
29
31
|
| 'session_launched'
|
|
30
32
|
| 'session_auto_launch'
|
|
31
33
|
| 'session_stopped'
|
|
32
34
|
| 'checkpoint_created'
|
|
33
35
|
| 'node_cloned'
|
|
36
|
+
| 'node_joined'
|
|
34
37
|
| 'node_removed'
|
|
35
38
|
| 'coordinator_started'
|
|
36
39
|
| 'recovery_attempted'
|
|
37
40
|
| 'ledger_replicated'
|
|
38
41
|
| 'ledger_reconciled'
|
|
42
|
+
| 'direct_fast_forward'
|
|
43
|
+
| 'delivery_unroutable'
|
|
44
|
+
| 'direct_dispatch_pruned'
|
|
45
|
+
| 'event_held'
|
|
46
|
+
| 'task_reclaimed'
|
|
47
|
+
// Gap2-A: a coordinator-recorded operating note — a runtime-accumulated
|
|
48
|
+
// lesson (provider quirk, pattern to avoid, recovery lesson) persisted in
|
|
49
|
+
// the ledger so it survives coordinator restarts and is provider-neutral.
|
|
50
|
+
// payload: { text, category?, createdAt?, sourceCoordinator? }
|
|
51
|
+
| 'coordinator_operating_note'
|
|
39
52
|
;
|
|
40
53
|
|
|
41
54
|
export interface MeshLedgerEntry {
|
|
@@ -61,6 +74,44 @@ export function isIntentionalCleanupStopEntry(entry: Pick<MeshLedgerEntry, 'kind
|
|
|
61
74
|
|| payload.source === 'mesh_remove_node');
|
|
62
75
|
}
|
|
63
76
|
|
|
77
|
+
export type MeshWorkerResultStatus = 'completed' | 'failed' | 'blocked' | 'partial' | 'unknown';
|
|
78
|
+
export type MeshProcessArtifactKind = 'process' | 'log' | 'port' | 'window' | 'session' | 'file' | 'url' | 'other';
|
|
79
|
+
|
|
80
|
+
export interface MeshValidationResultArtifact {
|
|
81
|
+
command?: string;
|
|
82
|
+
status: 'passed' | 'failed' | 'skipped' | 'unknown';
|
|
83
|
+
durationMs?: number;
|
|
84
|
+
outputPath?: string;
|
|
85
|
+
summary?: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface MeshProcessArtifact {
|
|
89
|
+
kind: MeshProcessArtifactKind;
|
|
90
|
+
id?: string;
|
|
91
|
+
label?: string;
|
|
92
|
+
locator?: string;
|
|
93
|
+
pid?: number;
|
|
94
|
+
port?: number;
|
|
95
|
+
url?: string;
|
|
96
|
+
path?: string;
|
|
97
|
+
sessionId?: string;
|
|
98
|
+
keepRunning?: boolean;
|
|
99
|
+
metadata?: Record<string, unknown>;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface MeshWorkerResultArtifact {
|
|
103
|
+
status: MeshWorkerResultStatus;
|
|
104
|
+
classification?: string;
|
|
105
|
+
changedFiles: string[];
|
|
106
|
+
validationResults: MeshValidationResultArtifact[];
|
|
107
|
+
gitStatus?: Record<string, unknown>;
|
|
108
|
+
processArtifacts: MeshProcessArtifact[];
|
|
109
|
+
errors: string[];
|
|
110
|
+
nextAction?: string;
|
|
111
|
+
requiresUserAction: boolean;
|
|
112
|
+
source: 'explicit_metadata' | 'final_summary_json' | 'default';
|
|
113
|
+
}
|
|
114
|
+
|
|
64
115
|
export interface MeshTaskCompletionEvidence {
|
|
65
116
|
source: 'agent_status_event';
|
|
66
117
|
event: 'agent:generating_completed' | 'agent:ready';
|
|
@@ -74,6 +125,7 @@ export interface MeshTaskCompletionEvidence {
|
|
|
74
125
|
providerSessionId?: string;
|
|
75
126
|
finalSummaryAvailable: boolean;
|
|
76
127
|
};
|
|
128
|
+
workerResult: MeshWorkerResultArtifact;
|
|
77
129
|
git: {
|
|
78
130
|
status: 'deferred';
|
|
79
131
|
reason: string;
|
|
@@ -96,6 +148,7 @@ export interface BuildTaskCompletionEvidenceOptions {
|
|
|
96
148
|
providerType?: string;
|
|
97
149
|
providerSessionId?: string;
|
|
98
150
|
finalSummary?: string;
|
|
151
|
+
workerResult?: Record<string, unknown>;
|
|
99
152
|
completedAt?: string;
|
|
100
153
|
}
|
|
101
154
|
|
|
@@ -160,8 +213,19 @@ export interface AppendRemoteLedgerResult {
|
|
|
160
213
|
// ─── Constants ──────────────────────────────────
|
|
161
214
|
|
|
162
215
|
const LEDGER_DIR_NAME = 'mesh-ledger';
|
|
163
|
-
const MAX_FILE_SIZE_BYTES = 10 * 1024 * 1024; // 10 MB
|
|
216
|
+
const MAX_FILE_SIZE_BYTES = 10 * 1024 * 1024; // 10 MB — full rotation threshold
|
|
217
|
+
const COMPACT_THRESHOLD_BYTES = 2 * 1024 * 1024; // 2 MB — compaction threshold
|
|
218
|
+
const ARCHIVE_TERMINAL_OLDER_THAN_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
164
219
|
const RECENT_FAILURE_WINDOW_MS = 30 * 60 * 1000; // 30 minutes
|
|
220
|
+
|
|
221
|
+
// Kinds that accumulate indefinitely and are safe to archive after ARCHIVE_TERMINAL_OLDER_THAN_MS.
|
|
222
|
+
// Non-terminal kinds (dispatched, sessions, nodes, checkpoints) are always kept in the active file.
|
|
223
|
+
const ARCHIVABLE_KINDS: ReadonlySet<MeshLedgerKind> = new Set([
|
|
224
|
+
'task_completed',
|
|
225
|
+
'task_failed',
|
|
226
|
+
'task_stalled',
|
|
227
|
+
'recovery_attempted',
|
|
228
|
+
] as MeshLedgerKind[]);
|
|
165
229
|
const DEFAULT_LEDGER_SLICE_LIMIT = 100;
|
|
166
230
|
export const MAX_LEDGER_SLICE_LIMIT = 500;
|
|
167
231
|
|
|
@@ -186,8 +250,258 @@ function getRotatedPath(meshId: string, index: number): string {
|
|
|
186
250
|
return join(getLedgerDir(), `${safe}.${index}.jsonl`);
|
|
187
251
|
}
|
|
188
252
|
|
|
253
|
+
function getArchivePath(meshId: string): string {
|
|
254
|
+
const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
255
|
+
return join(getLedgerDir(), `${safe}.archive.jsonl`);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function getRotatedArchivePath(meshId: string, index: number): string {
|
|
259
|
+
const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
260
|
+
return join(getLedgerDir(), `${safe}.archive.${index}.jsonl`);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function getArchivedCountsPath(meshId: string): string {
|
|
264
|
+
const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
265
|
+
return join(getLedgerDir(), `${safe}.archived-counts.json`);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function rotateArchiveFile(meshId: string, archivePath: string): void {
|
|
269
|
+
let index = 1;
|
|
270
|
+
while (existsSync(getRotatedArchivePath(meshId, index))) {
|
|
271
|
+
index++;
|
|
272
|
+
if (index > 5) break;
|
|
273
|
+
}
|
|
274
|
+
if (index > 5) index = 5;
|
|
275
|
+
try {
|
|
276
|
+
renameSync(archivePath, getRotatedArchivePath(meshId, index));
|
|
277
|
+
} catch (e: any) {
|
|
278
|
+
process.stderr.write(`[adhdev-mesh] Archive rotation failed for mesh ${meshId}: ${e?.message || e}\n`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
interface LedgerArchivedCounts {
|
|
283
|
+
taskCompleted: number;
|
|
284
|
+
taskFailed: number;
|
|
285
|
+
taskStalled: number;
|
|
286
|
+
recoveryAttempted: number;
|
|
287
|
+
totalArchived: number;
|
|
288
|
+
lastArchivedAt: string;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function readArchivedCounts(meshId: string): LedgerArchivedCounts {
|
|
292
|
+
const path = getArchivedCountsPath(meshId);
|
|
293
|
+
if (!existsSync(path)) return { taskCompleted: 0, taskFailed: 0, taskStalled: 0, recoveryAttempted: 0, totalArchived: 0, lastArchivedAt: '' };
|
|
294
|
+
try { return JSON.parse(readFileSync(path, 'utf-8')) as LedgerArchivedCounts; } catch { return { taskCompleted: 0, taskFailed: 0, taskStalled: 0, recoveryAttempted: 0, totalArchived: 0, lastArchivedAt: '' }; }
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function updateArchivedCounts(meshId: string, archived: MeshLedgerEntry[]): void {
|
|
298
|
+
const counts = readArchivedCounts(meshId);
|
|
299
|
+
for (const e of archived) {
|
|
300
|
+
if (e.kind === 'task_completed') counts.taskCompleted++;
|
|
301
|
+
else if (e.kind === 'task_failed') counts.taskFailed++;
|
|
302
|
+
else if (e.kind === 'task_stalled') counts.taskStalled++;
|
|
303
|
+
else if (e.kind === 'recovery_attempted') counts.recoveryAttempted++;
|
|
304
|
+
}
|
|
305
|
+
counts.totalArchived += archived.length;
|
|
306
|
+
counts.lastArchivedAt = new Date().toISOString();
|
|
307
|
+
try { writeFileSync(getArchivedCountsPath(meshId), JSON.stringify(counts), { encoding: 'utf-8', mode: 0o600 }); } catch { /* best-effort */ }
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// ─── Worker Result Footer ───────────────────────
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Footer to append to worker task messages so workers output structured results
|
|
314
|
+
* that the daemon parses via extractJsonObjectFromSummary / normalizeMeshWorkerResult.
|
|
315
|
+
*
|
|
316
|
+
* Usage: append buildWorkerTaskFooter() to the task message in mesh_send_task /
|
|
317
|
+
* mesh_enqueue_task. The coordinator prompt rules instruct coordinators to do this.
|
|
318
|
+
*/
|
|
319
|
+
export function buildWorkerTaskFooter(): string {
|
|
320
|
+
return `
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
When your task is done, end your final response with a JSON code block in this exact format (omit fields that don't apply):
|
|
324
|
+
\`\`\`json
|
|
325
|
+
{
|
|
326
|
+
"status": "completed",
|
|
327
|
+
"changedFiles": ["src/foo.ts", "tests/foo.test.ts"],
|
|
328
|
+
"gitStatus": { "branch": "feat/your-branch", "committed": true, "pushed": false },
|
|
329
|
+
"validationResults": [{ "command": "npm test", "status": "passed" }],
|
|
330
|
+
"errors": [],
|
|
331
|
+
"nextAction": "optional guidance for the coordinator"
|
|
332
|
+
}
|
|
333
|
+
\`\`\`
|
|
334
|
+
Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// ─── Ledger Compaction ──────────────────────────
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Compact the active ledger file for a mesh by moving old terminal entries
|
|
341
|
+
* (task_completed, task_failed, task_stalled, recovery_attempted older than 7 days)
|
|
342
|
+
* to <meshId>.archive.jsonl, keeping the active file lean.
|
|
343
|
+
*
|
|
344
|
+
* Non-terminal entries (dispatch, sessions, node lifecycle) are always retained.
|
|
345
|
+
* Called automatically from appendLedgerEntry when the file exceeds COMPACT_THRESHOLD_BYTES.
|
|
346
|
+
*/
|
|
347
|
+
export function compactLedger(meshId: string): { archivedCount: number; retainedCount: number } {
|
|
348
|
+
const filePath = getLedgerPath(meshId);
|
|
349
|
+
if (!existsSync(filePath)) return { archivedCount: 0, retainedCount: 0 };
|
|
350
|
+
|
|
351
|
+
const cutoff = Date.now() - ARCHIVE_TERMINAL_OLDER_THAN_MS;
|
|
352
|
+
const entries = readLedgerEntries(meshId);
|
|
353
|
+
|
|
354
|
+
const keep: MeshLedgerEntry[] = [];
|
|
355
|
+
const archive: MeshLedgerEntry[] = [];
|
|
356
|
+
for (const entry of entries) {
|
|
357
|
+
if (ARCHIVABLE_KINDS.has(entry.kind) && new Date(entry.timestamp).getTime() < cutoff) {
|
|
358
|
+
archive.push(entry);
|
|
359
|
+
} else {
|
|
360
|
+
keep.push(entry);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (archive.length === 0) return { archivedCount: 0, retainedCount: keep.length };
|
|
365
|
+
|
|
366
|
+
// Append archived entries to the archive file, rotate if it exceeds 50MB
|
|
367
|
+
const archivePath = getArchivePath(meshId);
|
|
368
|
+
try {
|
|
369
|
+
if (existsSync(archivePath) && statSync(archivePath).size > 50 * 1024 * 1024) {
|
|
370
|
+
rotateArchiveFile(meshId, archivePath);
|
|
371
|
+
}
|
|
372
|
+
const archiveLines = archive.map(e => JSON.stringify(e)).join('\n') + '\n';
|
|
373
|
+
appendFileSync(archivePath, archiveLines, { encoding: 'utf-8', mode: 0o600 });
|
|
374
|
+
updateArchivedCounts(meshId, archive);
|
|
375
|
+
} catch (e: any) {
|
|
376
|
+
process.stderr.write(`[adhdev-mesh] Ledger archive write failed for mesh ${meshId}: ${e?.message || e}\n`);
|
|
377
|
+
return { archivedCount: 0, retainedCount: entries.length };
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Rewrite active file with retained entries only
|
|
381
|
+
try {
|
|
382
|
+
const keepLines = keep.length ? keep.map(e => JSON.stringify(e)).join('\n') + '\n' : '';
|
|
383
|
+
writeFileSync(filePath, keepLines, { encoding: 'utf-8', mode: 0o600 });
|
|
384
|
+
invalidateLedgerCache(meshId);
|
|
385
|
+
} catch (e: any) {
|
|
386
|
+
process.stderr.write(`[adhdev-mesh] Ledger compaction rewrite failed for mesh ${meshId}: ${e?.message || e}\n`);
|
|
387
|
+
return { archivedCount: archive.length, retainedCount: keep.length };
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// G2: mirror the compaction in SQLite so the runtime store matches the active
|
|
391
|
+
// ledger set. Archived entries live on in the JSONL archive files (export/debug).
|
|
392
|
+
try {
|
|
393
|
+
MeshRuntimeStore.getInstance().deleteLedgerEntries(meshId, archive.map(e => e.id));
|
|
394
|
+
invalidateLedgerCache(meshId);
|
|
395
|
+
} catch { /* best-effort; summary counts absorb the difference via archived counts */ }
|
|
396
|
+
|
|
397
|
+
return { archivedCount: archive.length, retainedCount: keep.length };
|
|
398
|
+
}
|
|
399
|
+
|
|
189
400
|
// ─── Core API ───────────────────────────────────
|
|
190
401
|
|
|
402
|
+
function readNonEmptyString(value: unknown): string | undefined {
|
|
403
|
+
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function readStringArray(value: unknown): string[] {
|
|
407
|
+
if (!Array.isArray(value)) return [];
|
|
408
|
+
return value.map(item => readNonEmptyString(item)).filter(Boolean) as string[];
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function extractJsonObjectFromSummary(summary?: string): Record<string, unknown> | undefined {
|
|
412
|
+
const text = readNonEmptyString(summary);
|
|
413
|
+
if (!text) return undefined;
|
|
414
|
+
const fenced = text.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
415
|
+
const candidates = [fenced?.[1], text].filter(Boolean) as string[];
|
|
416
|
+
for (const candidate of candidates) {
|
|
417
|
+
const trimmed = candidate.trim();
|
|
418
|
+
if (!trimmed.startsWith('{') || !trimmed.endsWith('}')) continue;
|
|
419
|
+
try {
|
|
420
|
+
const parsed = JSON.parse(trimmed);
|
|
421
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
422
|
+
// Require at least one mesh worker result field to avoid false positives
|
|
423
|
+
// (e.g. JSON from tool call outputs or log lines in the final summary).
|
|
424
|
+
const hasWorkerShape = 'status' in parsed && (
|
|
425
|
+
'changedFiles' in parsed || 'errors' in parsed
|
|
426
|
+
|| 'gitStatus' in parsed || 'nextAction' in parsed
|
|
427
|
+
|| 'validationResults' in parsed
|
|
428
|
+
);
|
|
429
|
+
if (hasWorkerShape) return parsed;
|
|
430
|
+
}
|
|
431
|
+
} catch { /* try next candidate */ }
|
|
432
|
+
}
|
|
433
|
+
return undefined;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function normalizeValidationResults(value: unknown): MeshValidationResultArtifact[] {
|
|
437
|
+
if (!Array.isArray(value)) return [];
|
|
438
|
+
return value
|
|
439
|
+
.filter(item => item && typeof item === 'object' && !Array.isArray(item))
|
|
440
|
+
.map((item: any) => {
|
|
441
|
+
const status = ['passed', 'failed', 'skipped', 'unknown'].includes(item.status) ? item.status : 'unknown';
|
|
442
|
+
return {
|
|
443
|
+
...(readNonEmptyString(item.command) ? { command: readNonEmptyString(item.command) } : {}),
|
|
444
|
+
status,
|
|
445
|
+
...(Number.isFinite(Number(item.durationMs)) ? { durationMs: Number(item.durationMs) } : {}),
|
|
446
|
+
...(readNonEmptyString(item.outputPath) ? { outputPath: readNonEmptyString(item.outputPath) } : {}),
|
|
447
|
+
...(readNonEmptyString(item.summary) ? { summary: readNonEmptyString(item.summary) } : {}),
|
|
448
|
+
};
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
function normalizeProcessArtifacts(value: unknown): MeshProcessArtifact[] {
|
|
453
|
+
if (!Array.isArray(value)) return [];
|
|
454
|
+
const kinds = new Set(['process', 'log', 'port', 'window', 'session', 'file', 'url', 'other']);
|
|
455
|
+
return value
|
|
456
|
+
.filter(item => item && typeof item === 'object' && !Array.isArray(item))
|
|
457
|
+
.map((item: any) => ({
|
|
458
|
+
kind: kinds.has(item.kind) ? item.kind : 'other',
|
|
459
|
+
...(readNonEmptyString(item.id) ? { id: readNonEmptyString(item.id) } : {}),
|
|
460
|
+
...(readNonEmptyString(item.label) ? { label: readNonEmptyString(item.label) } : {}),
|
|
461
|
+
...(readNonEmptyString(item.locator) ? { locator: readNonEmptyString(item.locator) } : {}),
|
|
462
|
+
...(Number.isFinite(Number(item.pid)) ? { pid: Number(item.pid) } : {}),
|
|
463
|
+
...(Number.isFinite(Number(item.port)) ? { port: Number(item.port) } : {}),
|
|
464
|
+
...(readNonEmptyString(item.url) ? { url: readNonEmptyString(item.url) } : {}),
|
|
465
|
+
...(readNonEmptyString(item.path) ? { path: readNonEmptyString(item.path) } : {}),
|
|
466
|
+
...(readNonEmptyString(item.sessionId) ? { sessionId: readNonEmptyString(item.sessionId) } : {}),
|
|
467
|
+
...(typeof item.keepRunning === 'boolean' ? { keepRunning: item.keepRunning } : {}),
|
|
468
|
+
...(item.metadata && typeof item.metadata === 'object' && !Array.isArray(item.metadata) ? { metadata: item.metadata as Record<string, unknown> } : {}),
|
|
469
|
+
}));
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
export function normalizeMeshWorkerResult(input?: Record<string, unknown>, source: MeshWorkerResultArtifact['source'] = 'explicit_metadata'): MeshWorkerResultArtifact {
|
|
473
|
+
const raw = input && typeof input === 'object' ? input : {};
|
|
474
|
+
const status = ['completed', 'failed', 'blocked', 'partial', 'unknown'].includes(String(raw.status))
|
|
475
|
+
? raw.status as MeshWorkerResultStatus
|
|
476
|
+
: 'unknown';
|
|
477
|
+
const gitStatus = raw.gitStatus && typeof raw.gitStatus === 'object' && !Array.isArray(raw.gitStatus)
|
|
478
|
+
? raw.gitStatus as Record<string, unknown>
|
|
479
|
+
: undefined;
|
|
480
|
+
return {
|
|
481
|
+
status,
|
|
482
|
+
...(readNonEmptyString(raw.classification) ? { classification: readNonEmptyString(raw.classification) } : {}),
|
|
483
|
+
changedFiles: readStringArray(raw.changedFiles),
|
|
484
|
+
validationResults: normalizeValidationResults(raw.validationResults),
|
|
485
|
+
...(gitStatus ? { gitStatus } : {}),
|
|
486
|
+
processArtifacts: normalizeProcessArtifacts(raw.processArtifacts),
|
|
487
|
+
errors: readStringArray(raw.errors),
|
|
488
|
+
...(readNonEmptyString(raw.nextAction) ? { nextAction: readNonEmptyString(raw.nextAction) } : {}),
|
|
489
|
+
requiresUserAction: raw.requiresUserAction === true,
|
|
490
|
+
source,
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
function resolveWorkerResult(opts: BuildTaskCompletionEvidenceOptions): MeshWorkerResultArtifact {
|
|
495
|
+
if (opts.workerResult && typeof opts.workerResult === 'object') {
|
|
496
|
+
return normalizeMeshWorkerResult(opts.workerResult, 'explicit_metadata');
|
|
497
|
+
}
|
|
498
|
+
const parsed = extractJsonObjectFromSummary(opts.finalSummary);
|
|
499
|
+
if (parsed) {
|
|
500
|
+
return normalizeMeshWorkerResult(parsed, 'final_summary_json');
|
|
501
|
+
}
|
|
502
|
+
return normalizeMeshWorkerResult(undefined, 'default');
|
|
503
|
+
}
|
|
504
|
+
|
|
191
505
|
export function buildTaskCompletionEvidence(opts: BuildTaskCompletionEvidenceOptions): MeshTaskCompletionEvidence {
|
|
192
506
|
const providerSessionId = opts.providerSessionId?.trim() || undefined;
|
|
193
507
|
const providerType = opts.providerType?.trim() || undefined;
|
|
@@ -204,6 +518,7 @@ export function buildTaskCompletionEvidence(opts: BuildTaskCompletionEvidenceOpt
|
|
|
204
518
|
providerSessionId,
|
|
205
519
|
finalSummaryAvailable: typeof opts.finalSummary === 'string' && opts.finalSummary.trim().length > 0,
|
|
206
520
|
},
|
|
521
|
+
workerResult: resolveWorkerResult(opts),
|
|
207
522
|
git: {
|
|
208
523
|
status: 'deferred',
|
|
209
524
|
reason: 'ordinary_completion_git_status_not_checked',
|
|
@@ -239,21 +554,44 @@ export function appendLedgerEntry(
|
|
|
239
554
|
|
|
240
555
|
const filePath = getLedgerPath(meshId);
|
|
241
556
|
|
|
242
|
-
//
|
|
557
|
+
// Compact or rotate based on file size
|
|
243
558
|
if (existsSync(filePath)) {
|
|
244
559
|
try {
|
|
245
560
|
const stat = statSync(filePath);
|
|
246
561
|
if (stat.size >= MAX_FILE_SIZE_BYTES) {
|
|
247
562
|
rotateLedgerFile(meshId, filePath);
|
|
563
|
+
} else if (stat.size >= COMPACT_THRESHOLD_BYTES) {
|
|
564
|
+
compactLedger(meshId);
|
|
248
565
|
}
|
|
249
566
|
} catch {
|
|
250
567
|
// stat failed — proceed with append anyway
|
|
251
568
|
}
|
|
252
569
|
}
|
|
253
570
|
|
|
571
|
+
// Write to SQLite (G2: primary runtime read/write path)
|
|
572
|
+
try {
|
|
573
|
+
MeshRuntimeStore.getInstance().appendLedgerEntry({
|
|
574
|
+
id: entry.id,
|
|
575
|
+
meshId: entry.meshId,
|
|
576
|
+
timestamp: entry.timestamp,
|
|
577
|
+
kind: entry.kind,
|
|
578
|
+
nodeId: entry.nodeId ?? null,
|
|
579
|
+
sessionId: entry.sessionId ?? null,
|
|
580
|
+
providerType: entry.providerType ?? null,
|
|
581
|
+
payload: entry.payload,
|
|
582
|
+
});
|
|
583
|
+
} catch {
|
|
584
|
+
// SQLite write failed but the JSONL append below still records the entry.
|
|
585
|
+
// Reset the one-time import flag so the next read re-imports from JSONL
|
|
586
|
+
// and the store self-heals instead of silently missing this entry.
|
|
587
|
+
ledgerImportDone.delete(meshId);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// Also write to JSONL (retained as export/import/debug/legacy artifact)
|
|
254
591
|
try {
|
|
255
592
|
const line = JSON.stringify(entry) + '\n';
|
|
256
593
|
appendFileSync(filePath, line, { encoding: 'utf-8', mode: 0o600 });
|
|
594
|
+
invalidateLedgerCache(meshId);
|
|
257
595
|
meshLedgerEvents.emit('append', meshId, entry);
|
|
258
596
|
return entry;
|
|
259
597
|
} catch (e: any) {
|
|
@@ -285,8 +623,9 @@ export function appendRemoteLedgerEntries(meshId: string, entries: MeshLedgerEnt
|
|
|
285
623
|
if (entries.length === 0) return { accepted: 0, skippedDuplicate: 0, rejectedInvalid: 0, entries: [] };
|
|
286
624
|
const ledgerPath = getLedgerPath(meshId);
|
|
287
625
|
|
|
288
|
-
//
|
|
289
|
-
|
|
626
|
+
// Dedup against recent entries only — P2P replication is incremental (cursor-based),
|
|
627
|
+
// so duplicates appear in the recent tail, not deep history.
|
|
628
|
+
const existing = new Set(readLedgerEntries(meshId, { tail: 1000 }).map(e => e.id));
|
|
290
629
|
const validEntries: MeshLedgerEntry[] = [];
|
|
291
630
|
let rejectedInvalid = 0;
|
|
292
631
|
let skippedDuplicate = 0;
|
|
@@ -307,9 +646,24 @@ export function appendRemoteLedgerEntries(meshId: string, entries: MeshLedgerEnt
|
|
|
307
646
|
return { accepted: 0, skippedDuplicate, rejectedInvalid, entries: [] };
|
|
308
647
|
}
|
|
309
648
|
|
|
649
|
+
// G2: write to SQLite (primary runtime store); INSERT OR IGNORE dedups by id.
|
|
650
|
+
try {
|
|
651
|
+
MeshRuntimeStore.getInstance().importLedgerEntries(validEntries.map(e => ({
|
|
652
|
+
id: e.id,
|
|
653
|
+
meshId: e.meshId,
|
|
654
|
+
timestamp: e.timestamp,
|
|
655
|
+
kind: e.kind,
|
|
656
|
+
nodeId: e.nodeId ?? null,
|
|
657
|
+
sessionId: e.sessionId ?? null,
|
|
658
|
+
providerType: e.providerType ?? null,
|
|
659
|
+
payload: e.payload ?? {},
|
|
660
|
+
})));
|
|
661
|
+
} catch { /* best-effort; JSONL append below still records the entries */ }
|
|
662
|
+
|
|
310
663
|
try {
|
|
311
664
|
const lines = validEntries.map(e => JSON.stringify(e)).join('\n') + '\n';
|
|
312
665
|
appendFileSync(ledgerPath, lines, { encoding: 'utf-8', mode: 0o600 });
|
|
666
|
+
invalidateLedgerCache(meshId);
|
|
313
667
|
for (const entry of validEntries) {
|
|
314
668
|
meshLedgerEvents.emit('append', meshId, entry);
|
|
315
669
|
}
|
|
@@ -319,103 +673,151 @@ export function appendRemoteLedgerEntries(meshId: string, entries: MeshLedgerEnt
|
|
|
319
673
|
}
|
|
320
674
|
}
|
|
321
675
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
676
|
+
// ─── Ledger Read Cache ─────────────────────────
|
|
677
|
+
// Absorbs repeated reads within a single event-processing burst (e.g. agent:stopped
|
|
678
|
+
// triggers shouldSuppressIntentionalCleanupStop, findRecentTerminalLedgerEvidence,
|
|
679
|
+
// hasDispatchAfterTerminal, and getSessionRecoveryContext — all reading the same store).
|
|
680
|
+
// TTL is 100ms: short enough to stay current, long enough to cover one event cycle.
|
|
681
|
+
// Cache is invalidated on every write (append, remote import, compaction).
|
|
682
|
+
|
|
683
|
+
const ledgerReadCache = new Map<string, { entries: MeshLedgerEntry[]; cachedAt: number }>();
|
|
684
|
+
const LEDGER_CACHE_TTL_MS = 100;
|
|
685
|
+
|
|
686
|
+
function readLedgerFile(meshId: string): MeshLedgerEntry[] {
|
|
326
687
|
const filePath = getLedgerPath(meshId);
|
|
327
688
|
if (!existsSync(filePath)) return [];
|
|
328
|
-
|
|
329
689
|
let content: string;
|
|
690
|
+
try { content = readFileSync(filePath, 'utf-8'); } catch { return []; }
|
|
691
|
+
const entries: MeshLedgerEntry[] = [];
|
|
692
|
+
for (const line of content.split('\n')) {
|
|
693
|
+
if (!line.trim()) continue;
|
|
694
|
+
try {
|
|
695
|
+
const entry = JSON.parse(line) as MeshLedgerEntry;
|
|
696
|
+
if (entry.id && entry.kind) entries.push(entry);
|
|
697
|
+
} catch { /* skip malformed lines */ }
|
|
698
|
+
}
|
|
699
|
+
return entries;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// ─── G2: One-Time JSONL → SQLite Import ─────────
|
|
703
|
+
// On the first SQLite read for a mesh (per store instance), import any legacy
|
|
704
|
+
// JSONL entries into mesh_event_ledger. INSERT OR IGNORE makes this idempotent:
|
|
705
|
+
// dual-written entries are skipped, only pre-cutover legacy entries are added.
|
|
706
|
+
// Keyed by the store instance so MeshRuntimeStore.resetForTests() (fresh DB)
|
|
707
|
+
// naturally re-imports.
|
|
708
|
+
|
|
709
|
+
let ledgerImportStoreRef: MeshRuntimeStore | undefined;
|
|
710
|
+
const ledgerImportDone = new Set<string>();
|
|
711
|
+
|
|
712
|
+
function ensureLedgerImported(store: MeshRuntimeStore, meshId: string): void {
|
|
713
|
+
if (ledgerImportStoreRef !== store) {
|
|
714
|
+
ledgerImportDone.clear();
|
|
715
|
+
ledgerImportStoreRef = store;
|
|
716
|
+
}
|
|
717
|
+
if (ledgerImportDone.has(meshId)) return;
|
|
718
|
+
ledgerImportDone.add(meshId);
|
|
719
|
+
const fileEntries = readLedgerFile(meshId);
|
|
720
|
+
if (fileEntries.length === 0) return;
|
|
330
721
|
try {
|
|
331
|
-
|
|
722
|
+
store.importLedgerEntries(fileEntries.map(e => ({
|
|
723
|
+
id: e.id,
|
|
724
|
+
meshId: e.meshId,
|
|
725
|
+
timestamp: e.timestamp,
|
|
726
|
+
kind: e.kind,
|
|
727
|
+
nodeId: e.nodeId ?? null,
|
|
728
|
+
sessionId: e.sessionId ?? null,
|
|
729
|
+
providerType: e.providerType ?? null,
|
|
730
|
+
payload: e.payload ?? {},
|
|
731
|
+
})));
|
|
732
|
+
} catch { /* import is best-effort; reads fall back to JSONL on store failure */ }
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
function readLedgerFromStore(meshId: string): MeshLedgerEntry[] {
|
|
736
|
+
const store = MeshRuntimeStore.getInstance();
|
|
737
|
+
ensureLedgerImported(store, meshId);
|
|
738
|
+
return store.readLedgerEntriesOrdered(meshId).map(r => ({
|
|
739
|
+
id: r.id,
|
|
740
|
+
meshId: r.meshId,
|
|
741
|
+
timestamp: r.timestamp,
|
|
742
|
+
kind: r.kind as MeshLedgerKind,
|
|
743
|
+
...(r.nodeId ? { nodeId: r.nodeId } : {}),
|
|
744
|
+
...(r.sessionId ? { sessionId: r.sessionId } : {}),
|
|
745
|
+
...(r.providerType ? { providerType: r.providerType } : {}),
|
|
746
|
+
payload: (r.payload && typeof r.payload === 'object' ? r.payload : {}) as Record<string, unknown>,
|
|
747
|
+
}));
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
function getCachedRawEntries(meshId: string): MeshLedgerEntry[] {
|
|
751
|
+
const now = Date.now();
|
|
752
|
+
const cached = ledgerReadCache.get(meshId);
|
|
753
|
+
if (cached && now - cached.cachedAt < LEDGER_CACHE_TTL_MS) return cached.entries;
|
|
754
|
+
let entries: MeshLedgerEntry[];
|
|
755
|
+
try {
|
|
756
|
+
// G2: SQLite mesh_event_ledger is the primary runtime read path.
|
|
757
|
+
entries = readLedgerFromStore(meshId);
|
|
332
758
|
} catch {
|
|
333
|
-
|
|
759
|
+
// Store unavailable — fall back to the JSONL export artifact.
|
|
760
|
+
entries = readLedgerFile(meshId);
|
|
334
761
|
}
|
|
762
|
+
ledgerReadCache.set(meshId, { entries, cachedAt: now });
|
|
763
|
+
return entries;
|
|
764
|
+
}
|
|
335
765
|
|
|
336
|
-
|
|
337
|
-
|
|
766
|
+
function invalidateLedgerCache(meshId: string): void {
|
|
767
|
+
ledgerReadCache.delete(meshId);
|
|
768
|
+
}
|
|
338
769
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
770
|
+
/**
|
|
771
|
+
* Test helper: clear all runtime ledger state for a mesh — SQLite rows, read
|
|
772
|
+
* cache, and the one-time import flag. JSONL files are the caller's concern.
|
|
773
|
+
*/
|
|
774
|
+
export function __clearMeshLedgerForTests(meshId: string): void {
|
|
775
|
+
try {
|
|
776
|
+
MeshRuntimeStore.getInstance().clearLedgerForMesh(meshId);
|
|
777
|
+
} catch { /* store unavailable — nothing to clear */ }
|
|
778
|
+
ledgerReadCache.delete(meshId);
|
|
779
|
+
ledgerImportDone.delete(meshId);
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
/**
|
|
783
|
+
* Read ledger entries with optional filtering.
|
|
784
|
+
* G2: SQLite (mesh_event_ledger) is the primary read path; legacy JSONL is
|
|
785
|
+
* imported once per store instance and otherwise retained as an
|
|
786
|
+
* export/import/debug artifact only.
|
|
787
|
+
*/
|
|
788
|
+
export function readLedgerEntries(meshId: string, opts?: ReadLedgerOptions): MeshLedgerEntry[] {
|
|
789
|
+
let entries = getCachedRawEntries(meshId);
|
|
348
790
|
|
|
349
|
-
// Apply filters
|
|
350
791
|
if (opts?.since) {
|
|
351
792
|
const sinceDate = new Date(opts.since).getTime();
|
|
352
|
-
if (!isNaN(sinceDate))
|
|
353
|
-
entries = entries.filter(e => new Date(e.timestamp).getTime() >= sinceDate);
|
|
354
|
-
}
|
|
793
|
+
if (!isNaN(sinceDate)) entries = entries.filter(e => new Date(e.timestamp).getTime() >= sinceDate);
|
|
355
794
|
}
|
|
356
|
-
|
|
357
795
|
if (opts?.kind?.length) {
|
|
358
796
|
const kindSet = new Set(opts.kind);
|
|
359
797
|
entries = entries.filter(e => kindSet.has(e.kind));
|
|
360
798
|
}
|
|
361
|
-
|
|
362
|
-
// Apply tail (return last N entries)
|
|
363
799
|
if (opts?.tail && opts.tail > 0 && entries.length > opts.tail) {
|
|
364
800
|
entries = entries.slice(-opts.tail);
|
|
365
801
|
}
|
|
366
|
-
|
|
367
802
|
return entries;
|
|
368
803
|
}
|
|
369
804
|
|
|
370
805
|
/**
|
|
371
|
-
*
|
|
372
|
-
*
|
|
373
|
-
* remote daemons on demand without Cloud/D1 becoming a ledger data-plane.
|
|
374
|
-
*/
|
|
375
|
-
export function readLedgerSlice(meshId: string, opts?: ReadLedgerSliceOptions): MeshLedgerSlice {
|
|
376
|
-
const limit = clampLedgerSliceLimit(opts?.limit);
|
|
377
|
-
let entries = readLedgerEntries(meshId, { since: opts?.since, kind: opts?.kind });
|
|
378
|
-
const afterId = typeof opts?.afterId === 'string' && opts.afterId.trim() ? opts.afterId.trim() : null;
|
|
379
|
-
if (afterId) {
|
|
380
|
-
const index = entries.findIndex(entry => entry.id === afterId);
|
|
381
|
-
entries = index >= 0 ? entries.slice(index + 1) : entries;
|
|
382
|
-
}
|
|
383
|
-
const bounded = entries.slice(0, limit);
|
|
384
|
-
return {
|
|
385
|
-
protocol: 'adhdev.mesh.ledger.slice.v1',
|
|
386
|
-
meshId,
|
|
387
|
-
entries: bounded,
|
|
388
|
-
cursor: {
|
|
389
|
-
afterId,
|
|
390
|
-
nextAfterId: bounded.length ? bounded[bounded.length - 1].id : afterId,
|
|
391
|
-
limit,
|
|
392
|
-
hasMore: entries.length > bounded.length,
|
|
393
|
-
},
|
|
394
|
-
summary: getLedgerSummary(meshId),
|
|
395
|
-
sourceOfTruth: {
|
|
396
|
-
kind: 'local_jsonl',
|
|
397
|
-
path: getLedgerPath(meshId),
|
|
398
|
-
bounded: true,
|
|
399
|
-
maxLimit: MAX_LEDGER_SLICE_LIMIT,
|
|
400
|
-
},
|
|
401
|
-
};
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
/**
|
|
405
|
-
* Get a summary of mesh activity from the ledger.
|
|
806
|
+
* Build a ledger summary from pre-loaded entries. Used by both getLedgerSummary
|
|
807
|
+
* and readLedgerSlice so they share a single getCachedRawEntries() call.
|
|
406
808
|
*/
|
|
407
|
-
|
|
408
|
-
const
|
|
809
|
+
function buildLedgerSummary(meshId: string, entries: MeshLedgerEntry[]): MeshLedgerSummary {
|
|
810
|
+
const archived = readArchivedCounts(meshId);
|
|
409
811
|
const now = Date.now();
|
|
410
812
|
const recentFailureCutoff = now - RECENT_FAILURE_WINDOW_MS;
|
|
411
813
|
|
|
412
814
|
const summary: MeshLedgerSummary = {
|
|
413
815
|
meshId,
|
|
414
|
-
totalEntries: entries.length,
|
|
816
|
+
totalEntries: entries.length + archived.totalArchived,
|
|
415
817
|
taskDispatched: 0,
|
|
416
|
-
taskCompleted:
|
|
417
|
-
taskFailed:
|
|
418
|
-
taskStalled:
|
|
818
|
+
taskCompleted: archived.taskCompleted,
|
|
819
|
+
taskFailed: archived.taskFailed,
|
|
820
|
+
taskStalled: archived.taskStalled,
|
|
419
821
|
sessionLaunched: 0,
|
|
420
822
|
checkpointCreated: 0,
|
|
421
823
|
lastActivityAt: null,
|
|
@@ -450,6 +852,79 @@ export function getLedgerSummary(meshId: string): MeshLedgerSummary {
|
|
|
450
852
|
return summary;
|
|
451
853
|
}
|
|
452
854
|
|
|
855
|
+
/**
|
|
856
|
+
* Read a bounded, cursor-addressable ledger slice for local-first/P2P replication.
|
|
857
|
+
* The result is intentionally small and self-describing so coordinators can query
|
|
858
|
+
* remote daemons on demand without Cloud/D1 becoming a ledger data-plane.
|
|
859
|
+
*/
|
|
860
|
+
export function readLedgerSlice(meshId: string, opts?: ReadLedgerSliceOptions): MeshLedgerSlice {
|
|
861
|
+
const limit = clampLedgerSliceLimit(opts?.limit);
|
|
862
|
+
// Load raw entries once and share between filtering, pagination, and summary.
|
|
863
|
+
const rawEntries = getCachedRawEntries(meshId);
|
|
864
|
+
|
|
865
|
+
let entries: MeshLedgerEntry[] = rawEntries;
|
|
866
|
+
if (opts?.since) {
|
|
867
|
+
const sinceDate = new Date(opts.since).getTime();
|
|
868
|
+
if (!isNaN(sinceDate)) entries = entries.filter(e => new Date(e.timestamp).getTime() >= sinceDate);
|
|
869
|
+
}
|
|
870
|
+
if (opts?.kind?.length) {
|
|
871
|
+
const kindSet = new Set(opts.kind);
|
|
872
|
+
entries = entries.filter(e => kindSet.has(e.kind));
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
const afterId = typeof opts?.afterId === 'string' && opts.afterId.trim() ? opts.afterId.trim() : null;
|
|
876
|
+
if (afterId) {
|
|
877
|
+
const index = entries.findIndex(entry => entry.id === afterId);
|
|
878
|
+
entries = index >= 0 ? entries.slice(index + 1) : entries;
|
|
879
|
+
}
|
|
880
|
+
const bounded = entries.slice(0, limit);
|
|
881
|
+
return {
|
|
882
|
+
protocol: 'adhdev.mesh.ledger.slice.v1',
|
|
883
|
+
meshId,
|
|
884
|
+
entries: bounded,
|
|
885
|
+
cursor: {
|
|
886
|
+
afterId,
|
|
887
|
+
nextAfterId: bounded.length ? bounded[bounded.length - 1].id : afterId,
|
|
888
|
+
limit,
|
|
889
|
+
hasMore: entries.length > bounded.length,
|
|
890
|
+
},
|
|
891
|
+
summary: buildLedgerSummary(meshId, rawEntries),
|
|
892
|
+
sourceOfTruth: {
|
|
893
|
+
kind: 'local_jsonl',
|
|
894
|
+
path: getLedgerPath(meshId),
|
|
895
|
+
bounded: true,
|
|
896
|
+
maxLimit: MAX_LEDGER_SLICE_LIMIT,
|
|
897
|
+
},
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
/**
|
|
902
|
+
* G4: Read a bounded ledger slice from the SQLite mesh_event_ledger table.
|
|
903
|
+
* This is the preferred P2P reconcile read path; JSONL files are retained as
|
|
904
|
+
* export/import/debug/legacy artifacts only.
|
|
905
|
+
*
|
|
906
|
+
* Returns a shape structurally compatible with MeshLedgerSlice (minus the
|
|
907
|
+
* JSONL-specific `summary` and `sourceOfTruth.path` fields) so callers can
|
|
908
|
+
* pass it to buildMeshLedgerReplicaEvidence without modification.
|
|
909
|
+
*/
|
|
910
|
+
export function readLedgerSliceFromStore(meshId: string, opts?: ReadLedgerSliceOptions): ReturnType<typeof MeshRuntimeStore.prototype.readLedgerSlice> {
|
|
911
|
+
return MeshRuntimeStore.getInstance().readLedgerSlice(meshId, {
|
|
912
|
+
afterId: opts?.afterId,
|
|
913
|
+
since: opts?.since,
|
|
914
|
+
// ReadLedgerSliceOptions allows kind as array; SQLite path takes a single kind string.
|
|
915
|
+
// Pass first kind value if provided; callers needing multi-kind filtering should use readLedgerSlice (JSONL).
|
|
916
|
+
kind: opts?.kind?.length ? opts.kind[0] : undefined,
|
|
917
|
+
limit: opts?.limit,
|
|
918
|
+
});
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
/**
|
|
922
|
+
* Get a summary of mesh activity from the ledger.
|
|
923
|
+
*/
|
|
924
|
+
export function getLedgerSummary(meshId: string): MeshLedgerSummary {
|
|
925
|
+
return buildLedgerSummary(meshId, getCachedRawEntries(meshId));
|
|
926
|
+
}
|
|
927
|
+
|
|
453
928
|
// ─── Recovery Context ───────────────────────────
|
|
454
929
|
|
|
455
930
|
export interface SessionRecoveryContext {
|
|
@@ -484,46 +959,64 @@ export function getSessionRecoveryContext(
|
|
|
484
959
|
},
|
|
485
960
|
): SessionRecoveryContext {
|
|
486
961
|
const maxRetries = opts.maxRetries ?? 1;
|
|
487
|
-
|
|
962
|
+
// tail:500 is sufficient — task_dispatched is never archived (only terminal kinds are),
|
|
963
|
+
// so dispatch history is always present. The 30-min failure window means we never need
|
|
964
|
+
// more than a few dozen recent entries for consecutiveNodeFailures. Bounding to 500
|
|
965
|
+
// avoids a full O(n) scan for meshes with many historical entries.
|
|
966
|
+
const entries = readLedgerEntries(meshId, { tail: 500 });
|
|
488
967
|
|
|
489
|
-
//
|
|
968
|
+
// Single backward pass: find last task_dispatched AND count consecutive recent failures.
|
|
969
|
+
const now = Date.now();
|
|
970
|
+
const recentWindow = now - RECENT_FAILURE_WINDOW_MS;
|
|
490
971
|
let lastDispatch: MeshLedgerEntry | null = null;
|
|
972
|
+
let consecutiveNodeFailures = 0;
|
|
973
|
+
let failureCountDone = false;
|
|
491
974
|
for (let i = entries.length - 1; i >= 0; i--) {
|
|
492
975
|
const e = entries[i];
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
976
|
+
const ts = new Date(e.timestamp).getTime();
|
|
977
|
+
|
|
978
|
+
// Failure counting: scan until we exit the recent window or hit a chain-breaker
|
|
979
|
+
if (!failureCountDone) {
|
|
980
|
+
if (ts < recentWindow) {
|
|
981
|
+
failureCountDone = true;
|
|
982
|
+
} else if (opts.nodeId && e.nodeId !== opts.nodeId) {
|
|
983
|
+
// Entry for a different node — skip for failure counting but continue scanning for dispatch
|
|
984
|
+
} else if (e.kind === 'task_failed') {
|
|
985
|
+
if (!isIntentionalCleanupStopEntry(e)) consecutiveNodeFailures++;
|
|
986
|
+
} else if (e.kind === 'task_completed' || e.kind === 'task_dispatched') {
|
|
987
|
+
// A completion or new dispatch breaks the consecutive failure chain
|
|
988
|
+
failureCountDone = true;
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
// Dispatch search: find the last dispatch matching this session or node
|
|
993
|
+
if (lastDispatch === null && e.kind === 'task_dispatched') {
|
|
994
|
+
if (opts.sessionId && e.sessionId === opts.sessionId) { lastDispatch = e; }
|
|
995
|
+
else if (!opts.sessionId && opts.nodeId && e.nodeId === opts.nodeId) { lastDispatch = e; }
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
// Stop once both tasks are done
|
|
999
|
+
if (lastDispatch !== null && failureCountDone) break;
|
|
496
1000
|
}
|
|
497
1001
|
|
|
498
1002
|
const lastTaskMessage = typeof lastDispatch?.payload?.message === 'string'
|
|
499
1003
|
? lastDispatch.payload.message
|
|
500
1004
|
: null;
|
|
501
1005
|
|
|
502
|
-
// Count
|
|
503
|
-
|
|
504
|
-
const recentWindow = now - RECENT_FAILURE_WINDOW_MS;
|
|
505
|
-
let consecutiveNodeFailures = 0;
|
|
506
|
-
for (let i = entries.length - 1; i >= 0; i--) {
|
|
507
|
-
const e = entries[i];
|
|
508
|
-
if (new Date(e.timestamp).getTime() < recentWindow) break;
|
|
509
|
-
if (opts.nodeId && e.nodeId !== opts.nodeId) continue;
|
|
510
|
-
if (e.kind === 'task_failed') {
|
|
511
|
-
if (isIntentionalCleanupStopEntry(e)) continue;
|
|
512
|
-
consecutiveNodeFailures++;
|
|
513
|
-
} else if (e.kind === 'task_completed' || e.kind === 'task_dispatched') {
|
|
514
|
-
// A completion or new dispatch breaks the consecutive failure chain
|
|
515
|
-
break;
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
// Count how many times the same task was attempted (match by message prefix)
|
|
1006
|
+
// Count how many times the same task was attempted.
|
|
1007
|
+
// Prefer exact taskId match (payload.taskId) to avoid 200-char prefix collisions.
|
|
520
1008
|
let taskAttemptCount = 0;
|
|
521
|
-
if (
|
|
522
|
-
const
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
if (e.payload
|
|
526
|
-
|
|
1009
|
+
if (lastDispatch) {
|
|
1010
|
+
const taskId = typeof lastDispatch.payload?.taskId === 'string' ? lastDispatch.payload.taskId : null;
|
|
1011
|
+
if (taskId) {
|
|
1012
|
+
for (const e of entries) {
|
|
1013
|
+
if (e.kind === 'task_dispatched' && e.payload?.taskId === taskId) taskAttemptCount++;
|
|
1014
|
+
}
|
|
1015
|
+
} else if (lastTaskMessage) {
|
|
1016
|
+
const prefix = lastTaskMessage.slice(0, 200);
|
|
1017
|
+
for (const e of entries) {
|
|
1018
|
+
if (e.kind === 'task_dispatched' && typeof e.payload?.message === 'string') {
|
|
1019
|
+
if (e.payload.message.startsWith(prefix)) taskAttemptCount++;
|
|
527
1020
|
}
|
|
528
1021
|
}
|
|
529
1022
|
}
|
|
@@ -573,7 +1066,8 @@ function rotateLedgerFile(meshId: string, currentPath: string): void {
|
|
|
573
1066
|
|
|
574
1067
|
try {
|
|
575
1068
|
renameSync(currentPath, getRotatedPath(meshId, index));
|
|
576
|
-
} catch {
|
|
1069
|
+
} catch (e: any) {
|
|
577
1070
|
// Rotation failed — the next append will just grow the file
|
|
1071
|
+
process.stderr.write(`[adhdev-mesh] Ledger rotation failed for mesh ${meshId}: ${e?.message || e}. File will continue to grow.\n`);
|
|
578
1072
|
}
|
|
579
1073
|
}
|