@adhdev/daemon-core 0.9.82-rc.44 → 0.9.82-rc.440
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 +120 -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 +50679 -20122
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +47546 -17153
- 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 +29 -0
- package/dist/mesh/mesh-init.d.ts +128 -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 +300 -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 +252 -3
- package/dist/providers/contracts.d.ts +149 -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 +509 -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 +572 -27
- 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 +714 -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 +1242 -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 +2846 -3484
- package/src/commands/stream-commands.ts +8 -0
- package/src/commands/upgrade-helper.ts +310 -45
- package/src/config/chat-history.ts +493 -24
- package/src/config/config.ts +12 -0
- package/src/config/mesh-config.ts +593 -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 +196 -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 +415 -34
- 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 +1897 -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 +111 -0
- package/src/mesh/mesh-init.ts +350 -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 +1924 -0
- package/src/mesh/mesh-reconcile-loop.ts +1997 -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 +1254 -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 +2001 -106
- package/src/providers/contracts.d.ts +55 -0
- package/src/providers/contracts.ts +160 -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 +345 -0
- package/src/providers/native-history/hermes-cli-transcript.ts +260 -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 +509 -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 +1143 -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 +748 -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,656 @@
|
|
|
1
|
+
import { appendFileSync, existsSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { randomUUID } from 'crypto';
|
|
4
|
+
import { LOG } from '../logging/logger.js';
|
|
5
|
+
import { getLedgerDir, readLedgerEntries, appendLedgerEntry } from './mesh-ledger.js';
|
|
6
|
+
import { MeshRuntimeStore } from './mesh-runtime-store.js';
|
|
7
|
+
import { buildMeshSystemMessage, readNonEmptyString, readRecord, resolveEventSessionId, readMeshCompletionSummary, isWeakCompletionMetadata } from './mesh-events-utils.js';
|
|
8
|
+
import { expandDaemonIdForms } from '@adhdev/mesh-shared';
|
|
9
|
+
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// MCP coordinator pending-event queue — FILE-BASED PERSISTENCE
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// When a mesh event fires but no CLI coordinator session is registered (e.g.
|
|
14
|
+
// the coordinator is Claude Code running via MCP), we persist the event to a
|
|
15
|
+
// per-mesh JSONL file so it survives daemon restarts. The 50-entry hard cap
|
|
16
|
+
// is removed; the file is drained atomically on each get_pending_mesh_events
|
|
17
|
+
// call and limited to 100 KB to prevent runaway growth.
|
|
18
|
+
//
|
|
19
|
+
// File: <ledgerDir>/<meshId>.pending-events.jsonl
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
export interface PendingMeshCoordinatorEvent {
|
|
23
|
+
event: string;
|
|
24
|
+
meshId: string;
|
|
25
|
+
nodeLabel: string;
|
|
26
|
+
nodeId?: string;
|
|
27
|
+
workspace?: string;
|
|
28
|
+
metadataEvent: Record<string, unknown>;
|
|
29
|
+
coordinatorMessage?: string;
|
|
30
|
+
queuedAt: number;
|
|
31
|
+
/**
|
|
32
|
+
* When set, this event is intended for a specific coordinator daemon.
|
|
33
|
+
* Coordinators on other daemons should ignore it during drain.
|
|
34
|
+
* Absent on legacy events — treated as broadcast to any coordinator.
|
|
35
|
+
*/
|
|
36
|
+
targetCoordinatorDaemonId?: string;
|
|
37
|
+
/**
|
|
38
|
+
* When set, this event is intended for a specific coordinator SESSION on the
|
|
39
|
+
* target daemon (the session that originally dispatched the work). PHASE 2 inject
|
|
40
|
+
* strict-matches the live coordinator by this session id so a sibling coordinator
|
|
41
|
+
* session on the same daemon does not receive another coordinator's completion.
|
|
42
|
+
* Absent on legacy / version-skewed events → daemon-level broadcast (no regression).
|
|
43
|
+
* Rides inside the event payload, so it survives the SQLite payload round-trip and
|
|
44
|
+
* the JSONL file without a dedicated column; it is NOT a drain-scoping key.
|
|
45
|
+
*/
|
|
46
|
+
targetCoordinatorSessionId?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const REFINE_TERMINAL_EVENTS = new Set(['refine:completed', 'refine:failed']);
|
|
50
|
+
|
|
51
|
+
/** Normalise a coordinator-daemon-id argument (single id, list, or undefined) into a
|
|
52
|
+
* de-duplicated list of non-empty strings, EXPANDED to every equivalent daemon-id
|
|
53
|
+
* form (bare `mach_X` ≡ `daemon_mach_X` ≡ `standalone_mach_X`).
|
|
54
|
+
*
|
|
55
|
+
* A coordinator resolves its own id through one path (status instanceId, the config-
|
|
56
|
+
* form node daemonId, or the bare machineId) but a worker stamps a completion's
|
|
57
|
+
* `coordinator_daemon_id` through another, so the two are routinely in DIFFERENT
|
|
58
|
+
* forms of the SAME machine. The scope filter is an exact-string match, so without
|
|
59
|
+
* expansion a `daemon_mach_X`-scoped completion is silently skipped by a coordinator
|
|
60
|
+
* that only knows itself as bare `mach_X` (the base-node completion-surface bug).
|
|
61
|
+
* Expanding here fixes every drain/peek/surface caller uniformly. The first ORIGINAL
|
|
62
|
+
* id stays at [0] so per-daemon JSONL file naming keeps its primary; expansion stays
|
|
63
|
+
* within one machine core so a different coordinator's events are never claimed. */
|
|
64
|
+
function normalizeCoordinatorDaemonIds(
|
|
65
|
+
coordinatorDaemonId?: string | null | ReadonlyArray<string>,
|
|
66
|
+
): string[] {
|
|
67
|
+
return expandDaemonIdForms(coordinatorDaemonId);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function readRefineJobId(event: { metadataEvent?: Record<string, unknown> } | Record<string, unknown>): string {
|
|
71
|
+
const metadata = readRecord((event as any).metadataEvent) || event as Record<string, unknown>;
|
|
72
|
+
const result = readRecord(metadata.result);
|
|
73
|
+
const refineJob = readRecord(result?.refineJob);
|
|
74
|
+
return readNonEmptyString(metadata.jobId) || readNonEmptyString(refineJob?.jobId);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function buildRefineTerminalEventFingerprint(meshId: string, eventName: string, metadataEvent: Record<string, unknown>): string {
|
|
78
|
+
const jobId = readRefineJobId({ metadataEvent });
|
|
79
|
+
return jobId && REFINE_TERMINAL_EVENTS.has(eventName) ? `${meshId}::${eventName}::${jobId}` : '';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function hasPendingRefineTerminalEventDuplicate(event: PendingMeshCoordinatorEvent): boolean {
|
|
83
|
+
if (!REFINE_TERMINAL_EVENTS.has(event.event)) return false;
|
|
84
|
+
const jobId = readRefineJobId(event);
|
|
85
|
+
if (!jobId) return false;
|
|
86
|
+
return readPendingMeshCoordinatorEventsFromDisk(event.meshId).some((pending) =>
|
|
87
|
+
pending.event === event.event && readRefineJobId(pending) === jobId,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// CANON-B / DUPNOTIF: terminal completion events that the coordinator surfaces as a
|
|
92
|
+
// notification. The native completion path (handleMeshCoordinatorEvent) and the transcript
|
|
93
|
+
// reconciliation fallback (reconcileDirectDispatchCompletionFromTranscript) BOTH queue one of
|
|
94
|
+
// these for the same finished task — with DIFFERENT timestamps — so a timestamp-bearing
|
|
95
|
+
// fingerprint lets both surface and the coordinator notifies twice. When the event carries a
|
|
96
|
+
// taskId we anchor the fingerprint on the taskId (dropping the timestamp), collapsing the two
|
|
97
|
+
// paths into a single surface. A weakness marker keeps a tentative false-idle completion
|
|
98
|
+
// distinct from the genuine completion that supersedes it, so the genuine one is never
|
|
99
|
+
// swallowed by the earlier weak one.
|
|
100
|
+
const TERMINAL_COMPLETION_EVENTS = new Set(['agent:generating_completed', 'agent:stopped']);
|
|
101
|
+
|
|
102
|
+
export function buildPendingEventFingerprint(event: PendingMeshCoordinatorEvent): string {
|
|
103
|
+
const metadata = readRecord(event.metadataEvent) || {};
|
|
104
|
+
// Bootstrap events are node-scoped: dedup by meshId+event+nodeId only.
|
|
105
|
+
// They carry no sessionId/taskId/timestamp — using those fields would produce
|
|
106
|
+
// an empty fingerprint that defeats dedup entirely.
|
|
107
|
+
if (event.event === 'worktree_bootstrap_complete' || event.event === 'worktree_bootstrap_failed') {
|
|
108
|
+
return [event.meshId, event.event, event.nodeId || ''].join('::');
|
|
109
|
+
}
|
|
110
|
+
// DUPNOTIF: a terminal completion carrying a taskId is deduped by taskId (+ weakness),
|
|
111
|
+
// NOT by timestamp — the native and transcript-reconciliation paths timestamp the same
|
|
112
|
+
// completion differently, and only taskId is stable across both.
|
|
113
|
+
if (TERMINAL_COMPLETION_EVENTS.has(event.event)) {
|
|
114
|
+
const terminalTaskId = readNonEmptyString(metadata.taskId) || readNonEmptyString(readRecord(metadata.payload)?.taskId);
|
|
115
|
+
if (terminalTaskId) {
|
|
116
|
+
return [
|
|
117
|
+
event.meshId,
|
|
118
|
+
event.event,
|
|
119
|
+
terminalTaskId,
|
|
120
|
+
isWeakCompletionMetadata(metadata) ? 'weak' : 'genuine',
|
|
121
|
+
].join('::');
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// MAGI consensus-group exemption: a consensusGroupId marks an INTENTIONAL
|
|
125
|
+
// same-prompt fan-out across N replicas — the exact opposite of the accidental
|
|
126
|
+
// duplicates this dedup collapses. Anchor the fingerprint on the unique
|
|
127
|
+
// (taskId, consensusGroupId) so grouped replicas can NEVER be collapsed by any
|
|
128
|
+
// future prompt-content-based tightening of this builder. Mirrors the
|
|
129
|
+
// bootstrap-event exemption above and serves as the explicit fan-out marker.
|
|
130
|
+
// (Today this is belt-and-suspenders: each replica already gets a distinct
|
|
131
|
+
// taskId, so the generic key below would not collapse them either.)
|
|
132
|
+
const consensusGroupId = readNonEmptyString(metadata.consensusGroupId)
|
|
133
|
+
|| readNonEmptyString(readRecord(metadata.payload)?.consensusGroupId);
|
|
134
|
+
if (consensusGroupId) {
|
|
135
|
+
const groupTaskId = readNonEmptyString(metadata.taskId)
|
|
136
|
+
|| readNonEmptyString(readRecord(metadata.payload)?.taskId);
|
|
137
|
+
return [event.meshId, event.event, groupTaskId || '', consensusGroupId, 'group'].join('::');
|
|
138
|
+
}
|
|
139
|
+
const sessionId = resolveEventSessionId(metadata);
|
|
140
|
+
const providerSessionId = readNonEmptyString(metadata.providerSessionId);
|
|
141
|
+
const taskId = readNonEmptyString(metadata.taskId) || readNonEmptyString(readRecord(metadata.payload)?.taskId);
|
|
142
|
+
const jobId = readRefineJobId(event);
|
|
143
|
+
const timestamp = metadata.timestamp !== undefined && metadata.timestamp !== null ? String(metadata.timestamp) : '';
|
|
144
|
+
return [
|
|
145
|
+
event.meshId,
|
|
146
|
+
event.event,
|
|
147
|
+
event.nodeId || '',
|
|
148
|
+
sessionId || '',
|
|
149
|
+
providerSessionId || '',
|
|
150
|
+
taskId || '',
|
|
151
|
+
jobId || '',
|
|
152
|
+
timestamp || '',
|
|
153
|
+
].join('::');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// NOTE: the former R3 "direct-delivered" marker (markMeshCoordinatorEventDirectDelivered /
|
|
157
|
+
// wasDirectDeliveredToCoordinator) was removed when spontaneous PTY direct-inject was retired.
|
|
158
|
+
// Delivery is now queue-drain-only: an event is consumed by exactly one drainer via the atomic
|
|
159
|
+
// SQLite drained=1 marking, so there is no PTY-vs-poll double-delivery left to dedup against.
|
|
160
|
+
|
|
161
|
+
export function hasPendingCoordinatorEventDuplicate(event: PendingMeshCoordinatorEvent): boolean {
|
|
162
|
+
const fingerprint = buildPendingEventFingerprint(event);
|
|
163
|
+
if (!fingerprint.trim()) return false;
|
|
164
|
+
// Check SQLite inbox first (G3 primary path)
|
|
165
|
+
try {
|
|
166
|
+
if (MeshRuntimeStore.getInstance().hasPendingEventFingerprint(event.meshId, fingerprint)) return true;
|
|
167
|
+
} catch { /* fall through to JSONL check */ }
|
|
168
|
+
return readPendingMeshCoordinatorEventsFromDisk(event.meshId).some((pending) => buildPendingEventFingerprint(pending) === fingerprint);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function getPendingEventsPath(meshId: string, coordinatorDaemonId?: string): string {
|
|
172
|
+
const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
173
|
+
if (coordinatorDaemonId) {
|
|
174
|
+
const safeDaemon = coordinatorDaemonId.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
175
|
+
return join(getLedgerDir(), `${safe}-${safeDaemon}.pending-events.jsonl`);
|
|
176
|
+
}
|
|
177
|
+
return join(getLedgerDir(), `${safe}.pending-events.jsonl`);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function readPendingMeshCoordinatorEventsFromDisk(meshId?: string, coordinatorDaemonId?: string | ReadonlyArray<string>): PendingMeshCoordinatorEvent[] {
|
|
181
|
+
if (!meshId) return [];
|
|
182
|
+
const daemonIds = normalizeCoordinatorDaemonIds(coordinatorDaemonId);
|
|
183
|
+
const primaryDaemonId = daemonIds[0];
|
|
184
|
+
// Read coordinator-scoped file first; fall back to legacy shared file.
|
|
185
|
+
const paths = primaryDaemonId
|
|
186
|
+
? [getPendingEventsPath(meshId, primaryDaemonId), getPendingEventsPath(meshId)]
|
|
187
|
+
: [getPendingEventsPath(meshId)];
|
|
188
|
+
const events: PendingMeshCoordinatorEvent[] = [];
|
|
189
|
+
for (const path of paths) {
|
|
190
|
+
if (!existsSync(path)) continue;
|
|
191
|
+
try {
|
|
192
|
+
const raw = readFileSync(path, 'utf-8');
|
|
193
|
+
const parsed = raw.split('\n').filter(Boolean).flatMap(line => {
|
|
194
|
+
try { return [JSON.parse(line) as PendingMeshCoordinatorEvent]; } catch { return []; }
|
|
195
|
+
});
|
|
196
|
+
// If reading the shared file, filter to events that target this coordinator or are unscoped.
|
|
197
|
+
const filtered = (primaryDaemonId && path === getPendingEventsPath(meshId))
|
|
198
|
+
? parsed.filter(e => !e.targetCoordinatorDaemonId || daemonIds.includes(e.targetCoordinatorDaemonId))
|
|
199
|
+
: parsed;
|
|
200
|
+
events.push(...filtered);
|
|
201
|
+
} catch { /* skip unreadable files */ }
|
|
202
|
+
}
|
|
203
|
+
return events;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function refineTerminalEventFromLedger(meshId: string, pending: readonly PendingMeshCoordinatorEvent[]): PendingMeshCoordinatorEvent[] {
|
|
207
|
+
const acceptedJobIds = new Set(
|
|
208
|
+
pending
|
|
209
|
+
.filter(event => event.event === 'refine:accepted')
|
|
210
|
+
.map(event => readRefineJobId(event))
|
|
211
|
+
.filter(Boolean),
|
|
212
|
+
);
|
|
213
|
+
if (acceptedJobIds.size === 0) return [];
|
|
214
|
+
const existingTerminalJobIds = new Set(
|
|
215
|
+
pending
|
|
216
|
+
.filter(event => REFINE_TERMINAL_EVENTS.has(event.event))
|
|
217
|
+
.map(event => `${event.event}:${readRefineJobId(event)}`)
|
|
218
|
+
.filter(value => !value.endsWith(':')),
|
|
219
|
+
);
|
|
220
|
+
const backfilled: PendingMeshCoordinatorEvent[] = [];
|
|
221
|
+
const entries = readLedgerEntries(meshId, { tail: 200 });
|
|
222
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
223
|
+
const entry = entries[i];
|
|
224
|
+
if (entry.kind !== 'task_completed' && entry.kind !== 'task_failed') continue;
|
|
225
|
+
const payload = readRecord(entry.payload);
|
|
226
|
+
if (payload?.source !== 'refine_mesh_node_async_job') continue;
|
|
227
|
+
const refineJob = readRecord(payload.refineJob);
|
|
228
|
+
const jobId = readNonEmptyString(refineJob?.jobId);
|
|
229
|
+
if (!jobId || !acceptedJobIds.has(jobId)) continue;
|
|
230
|
+
const eventName = entry.kind === 'task_completed' ? 'refine:completed' : 'refine:failed';
|
|
231
|
+
if (existingTerminalJobIds.has(`${eventName}:${jobId}`)) continue;
|
|
232
|
+
existingTerminalJobIds.add(`${eventName}:${jobId}`);
|
|
233
|
+
const result = readRecord(payload.result);
|
|
234
|
+
const metadataEvent = {
|
|
235
|
+
source: 'refine_mesh_node_async_job',
|
|
236
|
+
jobId,
|
|
237
|
+
interactionId: readNonEmptyString(refineJob?.interactionId),
|
|
238
|
+
meshId,
|
|
239
|
+
nodeId: readNonEmptyString(refineJob?.nodeId) || entry.nodeId,
|
|
240
|
+
targetDaemonId: readNonEmptyString(refineJob?.targetDaemonId),
|
|
241
|
+
workspace: readNonEmptyString(refineJob?.workspace),
|
|
242
|
+
status: eventName === 'refine:completed' ? 'completed' : 'failed',
|
|
243
|
+
startedAt: readNonEmptyString(refineJob?.startedAt),
|
|
244
|
+
completedAt: readNonEmptyString(refineJob?.completedAt) || entry.timestamp,
|
|
245
|
+
retryOfJobId: readNonEmptyString(refineJob?.retryOfJobId) || readNonEmptyString(payload.retryOfJobId),
|
|
246
|
+
...(result ? { result } : {}),
|
|
247
|
+
};
|
|
248
|
+
const nodeLabel = readNonEmptyString(refineJob?.nodeId) || entry.nodeId || 'refine job';
|
|
249
|
+
backfilled.push({
|
|
250
|
+
event: eventName,
|
|
251
|
+
meshId,
|
|
252
|
+
nodeLabel,
|
|
253
|
+
nodeId: readNonEmptyString(refineJob?.nodeId) || entry.nodeId,
|
|
254
|
+
workspace: readNonEmptyString(refineJob?.workspace),
|
|
255
|
+
metadataEvent,
|
|
256
|
+
coordinatorMessage: buildMeshSystemMessage({ event: eventName, nodeLabel, metadataEvent }),
|
|
257
|
+
queuedAt: Date.now(),
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
return backfilled.reverse();
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function reconcilePendingMeshCoordinatorEvents(meshId: string, events: PendingMeshCoordinatorEvent[]): PendingMeshCoordinatorEvent[] {
|
|
264
|
+
const backfilled = refineTerminalEventFromLedger(meshId, events);
|
|
265
|
+
// A refine:accepted event is a provisional "job accepted, result to follow" signal.
|
|
266
|
+
// Once its terminal (completed/failed) counterpart for the same jobId exists — whether
|
|
267
|
+
// already direct-queued into the pending store OR backfilled from the ledger here — the
|
|
268
|
+
// accepted is superseded and is dropped so the coordinator isn't shown stale duplicate
|
|
269
|
+
// noise alongside the terminal outcome.
|
|
270
|
+
const terminalJobIds = new Set(
|
|
271
|
+
[...events.filter(event => REFINE_TERMINAL_EVENTS.has(event.event)), ...backfilled]
|
|
272
|
+
.map(event => readRefineJobId(event))
|
|
273
|
+
.filter(Boolean),
|
|
274
|
+
);
|
|
275
|
+
const reconciled = terminalJobIds.size === 0
|
|
276
|
+
? events
|
|
277
|
+
: events.filter(event => !(event.event === 'refine:accepted' && terminalJobIds.has(readRefineJobId(event))));
|
|
278
|
+
return backfilled.length === 0 ? reconciled : [...reconciled, ...backfilled];
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const MAX_PENDING_EVENTS_BYTES = 100 * 1024; // 100 KB — keep the pending file small
|
|
282
|
+
const MAX_PENDING_EVENTS_KEEP = 50; // keep the last 50 events when trimming
|
|
283
|
+
|
|
284
|
+
function trimPendingEventsIfNeeded(path: string): void {
|
|
285
|
+
try {
|
|
286
|
+
if (!existsSync(path)) return;
|
|
287
|
+
if (statSync(path).size <= MAX_PENDING_EVENTS_BYTES) return;
|
|
288
|
+
const lines = readFileSync(path, 'utf-8').split('\n').filter(Boolean);
|
|
289
|
+
if (lines.length <= MAX_PENDING_EVENTS_KEEP) return;
|
|
290
|
+
// C1 (data safety): this trim discards the OLDEST queued lines to keep the file
|
|
291
|
+
// bounded. An undelivered terminal completion among them would otherwise lose its
|
|
292
|
+
// worker summary silently (the JSONL is the only copy when the SQLite dual-write
|
|
293
|
+
// failed). Before dropping, mirror any meaningful (coordinator-facing / summary-
|
|
294
|
+
// bearing) dropped event into the ledger so it stays auditable and recoverable,
|
|
295
|
+
// and LOG.warn so the drop is observable instead of silent.
|
|
296
|
+
const dropped = lines.slice(0, lines.length - MAX_PENDING_EVENTS_KEEP);
|
|
297
|
+
for (const line of dropped) {
|
|
298
|
+
let event: PendingMeshCoordinatorEvent | undefined;
|
|
299
|
+
try { event = JSON.parse(line) as PendingMeshCoordinatorEvent; } catch { continue; }
|
|
300
|
+
if (!event || !event.meshId) continue;
|
|
301
|
+
const finalSummary = readMeshCompletionSummary(event.metadataEvent || {});
|
|
302
|
+
// "Meaningful" = would have been delivered to a coordinator (carries a message)
|
|
303
|
+
// or carries worker output worth preserving. Silent lifecycle events are not
|
|
304
|
+
// logged — losing them on trim is harmless (they re-drive nothing once stale).
|
|
305
|
+
if (!readNonEmptyString(event.coordinatorMessage) && !finalSummary) continue;
|
|
306
|
+
try {
|
|
307
|
+
appendLedgerEntry(event.meshId, {
|
|
308
|
+
kind: 'event_held',
|
|
309
|
+
...(event.nodeId ? { nodeId: event.nodeId } : {}),
|
|
310
|
+
payload: {
|
|
311
|
+
event: event.event,
|
|
312
|
+
reason: 'pending_trim_dropped',
|
|
313
|
+
recoverable: true,
|
|
314
|
+
nodeLabel: event.nodeLabel,
|
|
315
|
+
...(event.workspace ? { workspace: event.workspace } : {}),
|
|
316
|
+
targetCoordinatorDaemonId: event.targetCoordinatorDaemonId ?? null,
|
|
317
|
+
queuedAt: event.queuedAt,
|
|
318
|
+
...(finalSummary ? { finalSummary } : {}),
|
|
319
|
+
},
|
|
320
|
+
});
|
|
321
|
+
LOG.warn('MeshEvents', `Pending-events trim dropping undelivered ${event.event} for mesh ${event.meshId} — recorded to ledger (recoverable)`);
|
|
322
|
+
} catch (e: any) {
|
|
323
|
+
LOG.warn('MeshEvents', `Failed to ledger-record trim-dropped ${event.event} for mesh ${event.meshId}: ${e?.message || e}`);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
writeFileSync(path, lines.slice(-MAX_PENDING_EVENTS_KEEP).join('\n') + '\n', 'utf-8');
|
|
327
|
+
} catch { /* best-effort; if trim fails, append still proceeds */ }
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
export function queuePendingMeshCoordinatorEvent(event: PendingMeshCoordinatorEvent): boolean {
|
|
331
|
+
try {
|
|
332
|
+
if (hasPendingRefineTerminalEventDuplicate(event)) {
|
|
333
|
+
LOG.info('MeshEvents', `Suppressed duplicate pending ${event.event} for refine job ${readRefineJobId(event)}`);
|
|
334
|
+
return true;
|
|
335
|
+
}
|
|
336
|
+
if (hasPendingCoordinatorEventDuplicate(event)) {
|
|
337
|
+
LOG.info('MeshEvents', `Suppressed duplicate pending ${event.event} for mesh ${event.meshId}`);
|
|
338
|
+
return true;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const fingerprint = buildPendingEventFingerprint(event);
|
|
342
|
+
|
|
343
|
+
// G3: Write to SQLite inbox (primary path going forward)
|
|
344
|
+
let sqliteOk = false;
|
|
345
|
+
try {
|
|
346
|
+
MeshRuntimeStore.getInstance().insertPendingEvent({
|
|
347
|
+
id: randomUUID(),
|
|
348
|
+
meshId: event.meshId,
|
|
349
|
+
coordinatorDaemonId: event.targetCoordinatorDaemonId ?? null,
|
|
350
|
+
event: event.event,
|
|
351
|
+
payload: event,
|
|
352
|
+
fingerprint: fingerprint || null,
|
|
353
|
+
queuedAt: event.queuedAt,
|
|
354
|
+
});
|
|
355
|
+
sqliteOk = true;
|
|
356
|
+
} catch {
|
|
357
|
+
// SQLite write failure is non-fatal; JSONL fallback below still works.
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Also write to JSONL (retained as legacy/export artifact). Best-effort once
|
|
361
|
+
// SQLite (the primary store) has the event: a JSONL append failure (disk full,
|
|
362
|
+
// permissions) must NOT report the whole persist as failed when SQLite holds it.
|
|
363
|
+
try {
|
|
364
|
+
const path = getPendingEventsPath(event.meshId, event.targetCoordinatorDaemonId);
|
|
365
|
+
trimPendingEventsIfNeeded(path);
|
|
366
|
+
appendFileSync(path, JSON.stringify(event) + '\n', 'utf-8');
|
|
367
|
+
} catch (e: any) {
|
|
368
|
+
if (!sqliteOk) throw e; // neither store has it — surface as a real failure
|
|
369
|
+
LOG.warn('MeshEvents', `JSONL append failed for mesh ${event.meshId}; SQLite holds the event: ${e?.message || e}`);
|
|
370
|
+
}
|
|
371
|
+
return true;
|
|
372
|
+
} catch (e: any) {
|
|
373
|
+
LOG.warn('MeshEvents', `Failed to persist pending coordinator event: ${e?.message || e}`);
|
|
374
|
+
return false;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Atomically rename the file before reading so concurrent drains can't both consume
|
|
379
|
+
// the same events. renameSync is atomic on POSIX (same filesystem); only one caller
|
|
380
|
+
// wins the rename — the other gets ENOENT and returns null, preventing duplicate delivery.
|
|
381
|
+
function atomicDrainFile(path: string): string | null {
|
|
382
|
+
const tmpPath = `${path}.draining`;
|
|
383
|
+
try {
|
|
384
|
+
renameSync(path, tmpPath);
|
|
385
|
+
} catch {
|
|
386
|
+
return null; // another drain already renamed it, or file doesn't exist
|
|
387
|
+
}
|
|
388
|
+
try {
|
|
389
|
+
const content = readFileSync(tmpPath, 'utf-8');
|
|
390
|
+
try { unlinkSync(tmpPath); } catch { /* already cleaned up */ }
|
|
391
|
+
return content;
|
|
392
|
+
} catch {
|
|
393
|
+
try { unlinkSync(tmpPath); } catch { /* best-effort cleanup */ }
|
|
394
|
+
return null;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Selectively drain a JSONL pending-events file: atomically claim it (rename), then
|
|
399
|
+
// consume only the lines whose parsed event matches `predicate` and rewrite the
|
|
400
|
+
// remaining (kept) lines back to the original path. Unparseable lines are kept
|
|
401
|
+
// untouched. Returns the consumed events. The rename makes claiming exclusive —
|
|
402
|
+
// only one concurrent caller wins, so there is no double-consume of the same lines.
|
|
403
|
+
function selectiveDrainFile(
|
|
404
|
+
path: string,
|
|
405
|
+
predicate: (event: PendingMeshCoordinatorEvent) => boolean,
|
|
406
|
+
): PendingMeshCoordinatorEvent[] {
|
|
407
|
+
const tmpPath = `${path}.draining`;
|
|
408
|
+
try {
|
|
409
|
+
renameSync(path, tmpPath);
|
|
410
|
+
} catch {
|
|
411
|
+
return []; // another drain claimed it, or the file doesn't exist
|
|
412
|
+
}
|
|
413
|
+
let content: string;
|
|
414
|
+
try {
|
|
415
|
+
content = readFileSync(tmpPath, 'utf-8');
|
|
416
|
+
} catch {
|
|
417
|
+
try { unlinkSync(tmpPath); } catch { /* best-effort */ }
|
|
418
|
+
return [];
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const consumed: PendingMeshCoordinatorEvent[] = [];
|
|
422
|
+
const keptLines: string[] = [];
|
|
423
|
+
for (const line of content.split('\n')) {
|
|
424
|
+
if (!line) continue;
|
|
425
|
+
let parsed: PendingMeshCoordinatorEvent | undefined;
|
|
426
|
+
try { parsed = JSON.parse(line) as PendingMeshCoordinatorEvent; } catch { parsed = undefined; }
|
|
427
|
+
if (parsed && predicate(parsed)) {
|
|
428
|
+
consumed.push(parsed);
|
|
429
|
+
} else {
|
|
430
|
+
keptLines.push(line); // non-matching or unparseable → leave queued
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
try {
|
|
435
|
+
if (keptLines.length > 0) {
|
|
436
|
+
writeFileSync(path, keptLines.join('\n') + '\n', 'utf-8');
|
|
437
|
+
}
|
|
438
|
+
unlinkSync(tmpPath);
|
|
439
|
+
} catch {
|
|
440
|
+
// If the rewrite/cleanup fails, restore the claimed file so no events are
|
|
441
|
+
// lost — the next drain retries the whole file.
|
|
442
|
+
try { if (existsSync(tmpPath) && !existsSync(path)) renameSync(tmpPath, path); } catch { /* best-effort */ }
|
|
443
|
+
return [];
|
|
444
|
+
}
|
|
445
|
+
return consumed;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Drain and return pending coordinator events for meshId, removing the drained
|
|
450
|
+
* ones from both the SQLite inbox and the JSONL legacy file.
|
|
451
|
+
*
|
|
452
|
+
* When `opts.onlyEvents` is supplied, ONLY events whose name is in that set are
|
|
453
|
+
* drained; every other event stays queued (undrained in SQLite, rewritten back to
|
|
454
|
+
* the JSONL file). The reconcile loop uses this to force-drain terminal/force-inject
|
|
455
|
+
* events into a *generating* coordinator while leaving non-force progress events for
|
|
456
|
+
* the coordinator's next idle transition. The atomic SQLite drained=1 marking and the
|
|
457
|
+
* atomic JSONL rename keep force-drain and a concurrent full drain from double-consuming.
|
|
458
|
+
*/
|
|
459
|
+
export function drainPendingMeshCoordinatorEvents(
|
|
460
|
+
meshId?: string,
|
|
461
|
+
coordinatorDaemonId?: string | ReadonlyArray<string>,
|
|
462
|
+
opts?: { onlyEvents?: ReadonlySet<string> },
|
|
463
|
+
): PendingMeshCoordinatorEvent[] {
|
|
464
|
+
if (!meshId) return [];
|
|
465
|
+
|
|
466
|
+
// A daemon may answer to more than one coordinator-id form (its canonical
|
|
467
|
+
// status id like `standalone_<machineId>` AND the bare machineId). Normalise
|
|
468
|
+
// to a list so both the SQLite IN-filter and the JSONL targeting predicate
|
|
469
|
+
// accept any of them.
|
|
470
|
+
const daemonIds = normalizeCoordinatorDaemonIds(coordinatorDaemonId);
|
|
471
|
+
const primaryDaemonId = daemonIds[0];
|
|
472
|
+
|
|
473
|
+
const onlyEvents = opts?.onlyEvents;
|
|
474
|
+
const matchesFilter = (eventName: string): boolean => !onlyEvents || onlyEvents.has(eventName);
|
|
475
|
+
|
|
476
|
+
// Dual-write means SQLite and JSONL hold the same events. Both stores must be
|
|
477
|
+
// emptied in one drain call — draining only one leaves the other to re-deliver
|
|
478
|
+
// the same events on the next call. Merge with fingerprint dedup.
|
|
479
|
+
const merged: PendingMeshCoordinatorEvent[] = [];
|
|
480
|
+
const seenFingerprints = new Set<string>();
|
|
481
|
+
const pushUnique = (event: PendingMeshCoordinatorEvent) => {
|
|
482
|
+
const fingerprint = buildPendingEventFingerprint(event);
|
|
483
|
+
if (fingerprint.trim()) {
|
|
484
|
+
if (seenFingerprints.has(fingerprint)) return;
|
|
485
|
+
seenFingerprints.add(fingerprint);
|
|
486
|
+
}
|
|
487
|
+
merged.push(event);
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
// G3: SQLite inbox
|
|
491
|
+
try {
|
|
492
|
+
const store = MeshRuntimeStore.getInstance();
|
|
493
|
+
if (store.pendingEventCount(meshId) > 0) {
|
|
494
|
+
for (const row of store.drainPendingEvents(meshId, daemonIds.length > 0 ? daemonIds : undefined, onlyEvents ? { onlyEvents } : undefined)) {
|
|
495
|
+
const event = row.payload as PendingMeshCoordinatorEvent;
|
|
496
|
+
if (event) pushUnique(event);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
} catch (e: any) {
|
|
500
|
+
// SQLite drain failed — JSONL below still drains. Surface it: a silent
|
|
501
|
+
// failure here means the JSONL copy is emptied while the SQLite rows
|
|
502
|
+
// survive undrained, so the next drain re-delivers the same events to the
|
|
503
|
+
// coordinator (duplicate refine:completed etc.) with no diagnostic trail.
|
|
504
|
+
LOG.warn('MeshEvents', `SQLite pending-event drain failed for mesh ${meshId}; JSONL fallback only: ${e?.message || e}`);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// JSONL (legacy / migration path) — always drained alongside SQLite.
|
|
508
|
+
// The scoped per-daemon file is keyed by a single id; use the primary. The
|
|
509
|
+
// shared (unscoped) file's targeting predicate accepts ANY of this daemon's ids.
|
|
510
|
+
const paths = primaryDaemonId
|
|
511
|
+
? [getPendingEventsPath(meshId, primaryDaemonId), getPendingEventsPath(meshId)]
|
|
512
|
+
: [getPendingEventsPath(meshId)];
|
|
513
|
+
for (const path of paths) {
|
|
514
|
+
const isSharedFile = !!primaryDaemonId && path === getPendingEventsPath(meshId);
|
|
515
|
+
// Targeting predicate for the shared (unscoped) file: only this coordinator's
|
|
516
|
+
// events (or legacy untargeted ones) are eligible.
|
|
517
|
+
const targets = (e: PendingMeshCoordinatorEvent): boolean =>
|
|
518
|
+
!isSharedFile || !e.targetCoordinatorDaemonId || daemonIds.includes(e.targetCoordinatorDaemonId);
|
|
519
|
+
|
|
520
|
+
if (onlyEvents) {
|
|
521
|
+
// Selective JSONL drain: consume only matching events, rewrite the rest back.
|
|
522
|
+
for (const event of selectiveDrainFile(path, e => targets(e) && matchesFilter(e.event))) {
|
|
523
|
+
pushUnique(event);
|
|
524
|
+
}
|
|
525
|
+
continue;
|
|
526
|
+
}
|
|
527
|
+
const content = atomicDrainFile(path);
|
|
528
|
+
if (!content) continue;
|
|
529
|
+
const parsed = content.split('\n').filter(Boolean).flatMap(line => {
|
|
530
|
+
try { return [JSON.parse(line) as PendingMeshCoordinatorEvent]; } catch { return []; }
|
|
531
|
+
});
|
|
532
|
+
// If reading the shared file, filter to events that target this coordinator or are unscoped.
|
|
533
|
+
const filtered = isSharedFile ? parsed.filter(targets) : parsed;
|
|
534
|
+
for (const event of filtered) pushUnique(event);
|
|
535
|
+
}
|
|
536
|
+
if (merged.length === 0) return [];
|
|
537
|
+
// (Former R3 direct-delivered dedup removed.) Spontaneous PTY direct-inject no
|
|
538
|
+
// longer exists — delivery is now queue-drain-only (reconcile loop or MCP pull),
|
|
539
|
+
// so an event is consumed by exactly one drainer via the atomic SQLite drained=1
|
|
540
|
+
// marking. There is no PTY-vs-poll double path left to dedup against.
|
|
541
|
+
return reconcilePendingMeshCoordinatorEvents(meshId, merged);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* FALSE-BLOCKER-CLONE-QUEUE: retract any still-UNDELIVERED `mesh:dispatch_blocked`
|
|
546
|
+
* actionable-skip event for a task whose blocker has since resolved (the task was
|
|
547
|
+
* claimed, or its skip transitioned to a self-resolving transient reason). Without
|
|
548
|
+
* this, a `target_node_id_unmatched` blocker paged during the brief clone/bootstrap
|
|
549
|
+
* propagation window would linger in the coordinator's pending queue and surface as a
|
|
550
|
+
* false "actionable blocker — will NOT clear on its own" even after the task dispatched.
|
|
551
|
+
*
|
|
552
|
+
* Only removes events that have NOT yet been drained/delivered to the coordinator — an
|
|
553
|
+
* already-delivered message cannot be unsent, but de-dup re-arm (caller side) plus this
|
|
554
|
+
* retraction guarantee no NEW stale blocker accumulates. Best-effort across both the
|
|
555
|
+
* SQLite inbox and the JSONL legacy files (scoped + shared). Returns rows removed.
|
|
556
|
+
*/
|
|
557
|
+
export function retractPendingDispatchBlockedEvent(
|
|
558
|
+
meshId: string | undefined,
|
|
559
|
+
taskId: string | undefined,
|
|
560
|
+
coordinatorDaemonId?: string,
|
|
561
|
+
): number {
|
|
562
|
+
if (!meshId || !taskId) return 0;
|
|
563
|
+
let removed = 0;
|
|
564
|
+
const matchesTask = (event: PendingMeshCoordinatorEvent | undefined): boolean => {
|
|
565
|
+
if (!event || event.event !== 'mesh:dispatch_blocked') return false;
|
|
566
|
+
const rowTaskId = readNonEmptyString((event.metadataEvent as Record<string, unknown> | undefined)?.taskId);
|
|
567
|
+
return rowTaskId === taskId;
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
// SQLite inbox: peek undrained rows for the mesh, hard-delete the matching ones by id.
|
|
571
|
+
try {
|
|
572
|
+
const store = MeshRuntimeStore.getInstance();
|
|
573
|
+
const ids: string[] = [];
|
|
574
|
+
for (const row of store.peekPendingEvents(meshId)) {
|
|
575
|
+
if (row.event !== 'mesh:dispatch_blocked') continue;
|
|
576
|
+
if (matchesTask(row.payload as PendingMeshCoordinatorEvent)) ids.push(row.id);
|
|
577
|
+
}
|
|
578
|
+
if (ids.length) removed += store.deletePendingEventsById(ids);
|
|
579
|
+
} catch { /* best-effort — JSONL retraction below still runs */ }
|
|
580
|
+
|
|
581
|
+
// JSONL legacy files: selectively drop matching lines (rewrites the rest back).
|
|
582
|
+
const daemonIds = normalizeCoordinatorDaemonIds(coordinatorDaemonId);
|
|
583
|
+
const primaryDaemonId = daemonIds[0];
|
|
584
|
+
const paths = primaryDaemonId
|
|
585
|
+
? [getPendingEventsPath(meshId, primaryDaemonId), getPendingEventsPath(meshId)]
|
|
586
|
+
: [getPendingEventsPath(meshId)];
|
|
587
|
+
for (const path of paths) {
|
|
588
|
+
try {
|
|
589
|
+
removed += selectiveDrainFile(path, matchesTask).length;
|
|
590
|
+
} catch { /* best-effort */ }
|
|
591
|
+
}
|
|
592
|
+
return removed;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/** Peek at pending coordinator events without draining (non-destructive). */
|
|
596
|
+
export function getPendingMeshCoordinatorEvents(meshId?: string, coordinatorDaemonId?: string | ReadonlyArray<string>): readonly PendingMeshCoordinatorEvent[] {
|
|
597
|
+
if (!meshId) return [];
|
|
598
|
+
const daemonIds = normalizeCoordinatorDaemonIds(coordinatorDaemonId);
|
|
599
|
+
|
|
600
|
+
// Merge SQLite (primary) + JSONL (legacy) with fingerprint dedup.
|
|
601
|
+
const merged: PendingMeshCoordinatorEvent[] = [];
|
|
602
|
+
const seenFingerprints = new Set<string>();
|
|
603
|
+
const pushUnique = (event: PendingMeshCoordinatorEvent) => {
|
|
604
|
+
const fingerprint = buildPendingEventFingerprint(event);
|
|
605
|
+
if (fingerprint.trim()) {
|
|
606
|
+
if (seenFingerprints.has(fingerprint)) return;
|
|
607
|
+
seenFingerprints.add(fingerprint);
|
|
608
|
+
}
|
|
609
|
+
merged.push(event);
|
|
610
|
+
};
|
|
611
|
+
|
|
612
|
+
// G3: SQLite inbox (non-destructive peek at undrained rows)
|
|
613
|
+
try {
|
|
614
|
+
const store = MeshRuntimeStore.getInstance();
|
|
615
|
+
if (store.pendingEventCount(meshId) > 0) {
|
|
616
|
+
for (const row of store.peekPendingEvents(meshId, daemonIds.length > 0 ? daemonIds : undefined)) {
|
|
617
|
+
const event = row.payload as PendingMeshCoordinatorEvent;
|
|
618
|
+
if (event) pushUnique(event);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
} catch { /* SQLite unavailable — JSONL fallback below */ }
|
|
622
|
+
|
|
623
|
+
// JSONL (legacy)
|
|
624
|
+
for (const event of readPendingMeshCoordinatorEventsFromDisk(meshId, daemonIds)) {
|
|
625
|
+
pushUnique(event);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// (Former R3 direct-delivered filter removed — no PTY direct-inject path exists
|
|
629
|
+
// anymore, so a peeked pending event has genuinely not yet been consumed.)
|
|
630
|
+
return reconcilePendingMeshCoordinatorEvents(meshId, merged);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Test helper: purge all pending-event state for a mesh — SQLite rows
|
|
635
|
+
* (including drained fingerprint history) and JSONL files.
|
|
636
|
+
*/
|
|
637
|
+
export function __clearMeshPendingEventsForTests(meshId: string): void {
|
|
638
|
+
try {
|
|
639
|
+
MeshRuntimeStore.getInstance().clearPendingEventsForMesh(meshId);
|
|
640
|
+
} catch { /* store unavailable — nothing to clear */ }
|
|
641
|
+
clearPendingMeshCoordinatorEvents(meshId);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/** Explicitly clear all pending coordinator events for a mesh (and coordinator if scoped). */
|
|
645
|
+
export function clearPendingMeshCoordinatorEvents(meshId?: string, coordinatorDaemonId?: string): void {
|
|
646
|
+
if (!meshId) return;
|
|
647
|
+
// Clear SQLite rows
|
|
648
|
+
try { MeshRuntimeStore.getInstance().clearPendingEventsForMesh(meshId); } catch { /* store unavailable */ }
|
|
649
|
+
// Clear JSONL files
|
|
650
|
+
const paths = coordinatorDaemonId
|
|
651
|
+
? [getPendingEventsPath(meshId, coordinatorDaemonId), getPendingEventsPath(meshId)]
|
|
652
|
+
: [getPendingEventsPath(meshId)];
|
|
653
|
+
for (const path of paths) {
|
|
654
|
+
if (existsSync(path)) try { unlinkSync(path); } catch { /* already removed */ }
|
|
655
|
+
}
|
|
656
|
+
}
|