@bastani/atomic 0.9.18-alpha.5 → 0.9.18-alpha.7
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/CHANGELOG.md +88 -0
- package/README.md +3 -3
- package/dist/builtin/intercom/CHANGELOG.md +39 -0
- package/dist/builtin/intercom/README.md +22 -2
- package/dist/builtin/intercom/broker/broker.ts +676 -128
- package/dist/builtin/intercom/broker/client.ts +151 -35
- package/dist/builtin/intercom/broker/delivered-message-cache.ts +497 -37
- package/dist/builtin/intercom/broker/framing.ts +4 -2
- package/dist/builtin/intercom/broker/paths.ts +10 -0
- package/dist/builtin/intercom/broker/pending-send-registry.ts +1 -2
- package/dist/builtin/intercom/broker/send-handler.ts +311 -52
- package/dist/builtin/intercom/broker/send-signature.ts +4 -2
- package/dist/builtin/intercom/broker/socket-writes.ts +103 -0
- package/dist/builtin/intercom/broker/spawn.ts +9 -4
- package/dist/builtin/intercom/index.bundle.mjs +1410 -212
- package/dist/builtin/intercom/package.json +2 -2
- package/dist/builtin/intercom/recoverable-disconnect.ts +52 -0
- package/dist/builtin/intercom/retry-policy.ts +8 -0
- package/dist/builtin/intercom/skills/intercom/SKILL.md +17 -9
- package/dist/builtin/intercom/types.ts +27 -8
- package/dist/builtin/intercom/workflow-stage-path-matching.ts +90 -0
- package/dist/builtin/intercom/workflow-stage-target.ts +57 -0
- package/dist/builtin/mcp/index.bundle.mjs +427 -126
- package/dist/builtin/mcp/package.json +2 -2
- package/dist/builtin/subagents/CHANGELOG.md +19 -0
- package/dist/builtin/subagents/README.md +3 -3
- package/dist/builtin/subagents/agents/code-simplifier.md +2 -2
- package/dist/builtin/subagents/agents/codebase-analyzer.md +2 -2
- package/dist/builtin/subagents/agents/codebase-locator.md +2 -2
- package/dist/builtin/subagents/agents/codebase-online-researcher.md +11 -11
- package/dist/builtin/subagents/agents/codebase-pattern-finder.md +2 -2
- package/dist/builtin/subagents/agents/codebase-research-analyzer.md +2 -2
- package/dist/builtin/subagents/agents/codebase-research-locator.md +2 -2
- package/dist/builtin/subagents/agents/debugger.md +3 -3
- package/dist/builtin/subagents/agents/worker.md +2 -2
- package/dist/builtin/subagents/package.json +3 -3
- package/dist/builtin/subagents/skills/qlty/SKILL.md +29 -101
- package/dist/builtin/subagents/skills/qlty/references/manual-configuration.md +52 -0
- package/dist/builtin/subagents/skills/subagent/SKILL.md +11 -11
- package/dist/builtin/subagents/src/extension/index.bundle.mjs +49 -132
- package/dist/builtin/web-access/package.json +2 -2
- package/dist/builtin/workflows/CHANGELOG.md +46 -0
- package/dist/builtin/workflows/README.md +7 -7
- package/dist/builtin/workflows/builtin/adversarial-verification.js +2 -2
- package/dist/builtin/workflows/builtin/{chunk-szz1ajz9.js → chunk-42s7sw0b.js} +9 -2
- package/dist/builtin/workflows/builtin/{chunk-2dqb5s2q.js → chunk-79fjkcvh.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-n58a7v26.js → chunk-b0v2xab1.js} +181 -10
- package/dist/builtin/workflows/builtin/{chunk-fghhy2a5.js → chunk-ed4jfn5t.js} +23 -5
- package/dist/builtin/workflows/builtin/{chunk-h3r2vkzc.js → chunk-hqpe2cyx.js} +60 -21
- package/dist/builtin/workflows/builtin/{chunk-hzzn6adg.js → chunk-k3w87x8y.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-82ha8p41.js → chunk-n8h4yyew.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-qwzvgxnq.js → chunk-reqr3sr3.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-c53y8bdh.js → chunk-wgccch41.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-0x6e303p.js → chunk-zbekjqew.js} +20 -7
- package/dist/builtin/workflows/builtin/{chunk-29wrp38a.js → chunk-ze5x3d1r.js} +1 -1
- package/dist/builtin/workflows/builtin/classify-and-act.js +2 -2
- package/dist/builtin/workflows/builtin/fan-out-and-synthesize.js +2 -2
- package/dist/builtin/workflows/builtin/generate-and-filter.js +2 -2
- package/dist/builtin/workflows/builtin/goal.js +3 -3
- package/dist/builtin/workflows/builtin/index.js +11 -11
- package/dist/builtin/workflows/builtin/loop-until-done.js +2 -2
- package/dist/builtin/workflows/builtin/open-claude-design.js +2 -2
- package/dist/builtin/workflows/builtin/ralph.js +3 -3
- package/dist/builtin/workflows/builtin/steering-context.js +1 -1
- package/dist/builtin/workflows/builtin/tournament.js +2 -2
- package/dist/builtin/workflows/package.json +2 -2
- package/dist/builtin/workflows/skills/prompt-engineer/SKILL.md +39 -52
- package/dist/builtin/workflows/skills/prompt-engineer/references/advanced_patterns.md +9 -37
- package/dist/builtin/workflows/skills/prompt-engineer/references/claude_fable_5.md +49 -0
- package/dist/builtin/workflows/skills/prompt-engineer/references/claude_fable_5_1.md +57 -0
- package/dist/builtin/workflows/skills/prompt-engineer/references/claude_opus_4_8.md +57 -0
- package/dist/builtin/workflows/skills/prompt-engineer/references/claude_opus_5.md +47 -0
- package/dist/builtin/workflows/skills/prompt-engineer/references/claude_sonnet_5.md +57 -0
- package/dist/builtin/workflows/skills/prompt-engineer/references/gpt_5_5.md +60 -0
- package/dist/builtin/workflows/skills/prompt-engineer/references/gpt_5_6.md +58 -0
- package/dist/builtin/workflows/skills/prompt-engineer/references/gpt_6_astra.md +58 -0
- package/dist/builtin/workflows/skills/prompt-engineer/references/quality_improvement.md +18 -4
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +3194 -737
- package/dist/builtin/workflows/src/index.js +899 -436
- package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +0 -10
- package/dist/cli/list-models.d.ts.map +1 -1
- package/dist/cli/list-models.js +4 -0
- package/dist/cli/list-models.js.map +1 -1
- package/dist/client/index.d.ts +1 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -2
- package/dist/client/index.js.map +1 -1
- package/dist/config.d.ts +0 -9
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +0 -45
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-compaction.d.ts.map +1 -1
- package/dist/core/agent-session-compaction.js +8 -6
- package/dist/core/agent-session-compaction.js.map +1 -1
- package/dist/core/agent-session-methods.d.ts +1 -0
- package/dist/core/agent-session-methods.d.ts.map +1 -1
- package/dist/core/agent-session-methods.js.map +1 -1
- package/dist/core/agent-session-models.d.ts.map +1 -1
- package/dist/core/agent-session-models.js +3 -4
- package/dist/core/agent-session-models.js.map +1 -1
- package/dist/core/agent-session-queue-pause.d.ts.map +1 -1
- package/dist/core/agent-session-queue-pause.js +6 -0
- package/dist/core/agent-session-queue-pause.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +13 -5
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-tool-hooks.js +1 -1
- package/dist/core/agent-session-tool-hooks.js.map +1 -1
- package/dist/core/agent-session-tree.d.ts.map +1 -1
- package/dist/core/agent-session-tree.js +8 -0
- package/dist/core/agent-session-tree.js.map +1 -1
- package/dist/core/agent-session.d.ts +2 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +2 -0
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts +2 -2
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +4 -4
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/extensions/api-types.d.ts +8 -2
- package/dist/core/extensions/api-types.d.ts.map +1 -1
- package/dist/core/extensions/api-types.js.map +1 -1
- package/dist/core/extensions/context-types.d.ts +12 -0
- package/dist/core/extensions/context-types.d.ts.map +1 -1
- package/dist/core/extensions/context-types.js.map +1 -1
- package/dist/core/extensions/runner-shortcuts.d.ts.map +1 -1
- package/dist/core/extensions/runner-shortcuts.js +0 -1
- package/dist/core/extensions/runner-shortcuts.js.map +1 -1
- package/dist/core/fast-model-routing-transport.d.ts +12 -0
- package/dist/core/fast-model-routing-transport.d.ts.map +1 -0
- package/dist/core/{codex-fast-mode-transport.js → fast-model-routing-transport.js} +29 -29
- package/dist/core/fast-model-routing-transport.js.map +1 -0
- package/dist/core/fast-model-routing.d.ts +57 -0
- package/dist/core/fast-model-routing.d.ts.map +1 -0
- package/dist/core/fast-model-routing.js +201 -0
- package/dist/core/fast-model-routing.js.map +1 -0
- package/dist/core/fast-model-variants.d.ts +98 -0
- package/dist/core/fast-model-variants.d.ts.map +1 -0
- package/dist/core/fast-model-variants.js +148 -0
- package/dist/core/fast-model-variants.js.map +1 -0
- package/dist/core/http-dispatcher.d.ts.map +1 -1
- package/dist/core/http-dispatcher.js +4 -2
- package/dist/core/http-dispatcher.js.map +1 -1
- package/dist/core/keybindings.d.ts +0 -10
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +0 -8
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/messages.d.ts +1 -1
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js.map +1 -1
- package/dist/core/model-config.d.ts +15 -0
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +3 -0
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/model-resolver-initial.js +1 -1
- package/dist/core/model-resolver-initial.js.map +1 -1
- package/dist/core/model-runtime-streaming.d.ts +14 -1
- package/dist/core/model-runtime-streaming.d.ts.map +1 -1
- package/dist/core/model-runtime-streaming.js +25 -3
- package/dist/core/model-runtime-streaming.js.map +1 -1
- package/dist/core/model-runtime.d.ts +39 -2
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +91 -7
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/provider-composer-internal.d.ts.map +1 -1
- package/dist/core/provider-composer-internal.js +8 -2
- package/dist/core/provider-composer-internal.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +18 -18
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager-archive.d.ts.map +1 -1
- package/dist/core/session-manager-archive.js +25 -2
- package/dist/core/session-manager-archive.js.map +1 -1
- package/dist/core/settings-manager-core.d.ts +0 -2
- package/dist/core/settings-manager-core.d.ts.map +1 -1
- package/dist/core/settings-manager-core.js +2 -9
- package/dist/core/settings-manager-core.js.map +1 -1
- package/dist/core/settings-manager-internals.d.ts +0 -1
- package/dist/core/settings-manager-internals.d.ts.map +1 -1
- package/dist/core/settings-manager-internals.js.map +1 -1
- package/dist/core/settings-manager-ui-accessors.d.ts +0 -8
- package/dist/core/settings-manager-ui-accessors.d.ts.map +1 -1
- package/dist/core/settings-manager-ui-accessors.js +0 -53
- package/dist/core/settings-manager-ui-accessors.js.map +1 -1
- package/dist/core/settings-manager.d.ts +1 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/settings-types.d.ts +0 -5
- package/dist/core/settings-types.d.ts.map +1 -1
- package/dist/core/settings-types.js.map +1 -1
- package/dist/core/skills.d.ts +1 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +4 -2
- package/dist/core/skills.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +0 -1
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.js +7 -7
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/edit.d.ts +1 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +128 -3
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/hashline.d.ts +2 -2
- package/dist/core/tools/hashline.d.ts.map +1 -1
- package/dist/core/tools/hashline.js +40 -10
- package/dist/core/tools/hashline.js.map +1 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +8 -8
- package/dist/core/tools/write.js.map +1 -1
- package/dist/core/workflow-stage-admission.d.ts +6 -0
- package/dist/core/workflow-stage-admission.d.ts.map +1 -1
- package/dist/core/workflow-stage-admission.js +14 -0
- package/dist/core/workflow-stage-admission.js.map +1 -1
- package/dist/index-extensions.d.ts +1 -1
- package/dist/index-extensions.d.ts.map +1 -1
- package/dist/index-extensions.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -5
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/components/atomic-working-status.d.ts +4 -0
- package/dist/modes/interactive/components/atomic-working-status.d.ts.map +1 -1
- package/dist/modes/interactive/components/atomic-working-status.js +26 -1
- package/dist/modes/interactive/components/atomic-working-status.js.map +1 -1
- package/dist/modes/interactive/components/custom-editor.d.ts +7 -0
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js +43 -0
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +0 -6
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +1 -2
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +0 -1
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +6 -11
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts +2 -5
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.js +3 -19
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector-items.js +1 -1
- package/dist/modes/interactive/components/settings-selector-items.js.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.d.ts +1 -2
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.js +4 -11
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/dist/modes/interactive/components/transcript-follow-indicator.js +3 -3
- package/dist/modes/interactive/components/transcript-follow-indicator.js.map +1 -1
- package/dist/modes/interactive/interactive-agent-events.js +8 -8
- package/dist/modes/interactive/interactive-agent-events.js.map +1 -1
- package/dist/modes/interactive/interactive-autocomplete.js +5 -16
- package/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
- package/dist/modes/interactive/interactive-bash-compact.js +2 -9
- package/dist/modes/interactive/interactive-bash-compact.js.map +1 -1
- package/dist/modes/interactive/interactive-deferred-startup.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-deferred-startup.js +3 -0
- package/dist/modes/interactive/interactive-deferred-startup.js.map +1 -1
- package/dist/modes/interactive/interactive-editor-actions.js +2 -2
- package/dist/modes/interactive/interactive-editor-actions.js.map +1 -1
- package/dist/modes/interactive/interactive-extension-custom-ui.js +1 -0
- package/dist/modes/interactive/interactive-extension-custom-ui.js.map +1 -1
- package/dist/modes/interactive/interactive-extension-dialogs.js +7 -0
- package/dist/modes/interactive/interactive-extension-dialogs.js.map +1 -1
- package/dist/modes/interactive/interactive-extension-runtime.js +20 -10
- package/dist/modes/interactive/interactive-extension-runtime.js.map +1 -1
- package/dist/modes/interactive/interactive-hotkeys-debug.js +1 -5
- package/dist/modes/interactive/interactive-hotkeys-debug.js.map +1 -1
- package/dist/modes/interactive/interactive-initial-session-binding.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-initial-session-binding.js +3 -0
- package/dist/modes/interactive/interactive-initial-session-binding.js.map +1 -1
- package/dist/modes/interactive/interactive-input-handling.js +0 -8
- package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.d.ts +5 -1
- package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.js +15 -9
- package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-deps.d.ts +1 -3
- package/dist/modes/interactive/interactive-mode-deps.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-deps.js +1 -3
- package/dist/modes/interactive/interactive-mode-deps.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.d.ts +9 -12
- package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
- package/dist/modes/interactive/interactive-model-routing.js +10 -13
- package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
- package/dist/modes/interactive/interactive-render-chat.js +29 -0
- package/dist/modes/interactive/interactive-render-chat.js.map +1 -1
- package/dist/modes/interactive/interactive-selectors.js +2 -32
- package/dist/modes/interactive/interactive-selectors.js.map +1 -1
- package/dist/modes/interactive/interactive-session-routing.js +1 -5
- package/dist/modes/interactive/interactive-session-routing.js.map +1 -1
- package/dist/modes/interactive/interactive-session-runtime.js +1 -5
- package/dist/modes/interactive/interactive-session-runtime.js.map +1 -1
- package/dist/modes/interactive/interactive-slash-commands.js +6 -13
- package/dist/modes/interactive/interactive-slash-commands.js.map +1 -1
- package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-startup.js +19 -13
- package/dist/modes/interactive/interactive-startup.js.map +1 -1
- package/dist/modes/interactive/interactive-tui.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-tui.js +27 -3
- package/dist/modes/interactive/interactive-tui.js.map +1 -1
- package/dist/modes/interactive-engine/engine-custom-ui.d.ts.map +1 -1
- package/dist/modes/interactive-engine/engine-custom-ui.js +1 -0
- package/dist/modes/interactive-engine/engine-custom-ui.js.map +1 -1
- package/dist/modes/interactive-engine/isolated-runtime.d.ts.map +1 -1
- package/dist/modes/interactive-engine/isolated-runtime.js +4 -1
- package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
- package/dist/utils/child-process.d.ts.map +1 -1
- package/dist/utils/child-process.js +13 -6
- package/dist/utils/child-process.js.map +1 -1
- package/dist/utils/exif-orientation.d.ts.map +1 -1
- package/dist/utils/exif-orientation.js +2 -3
- package/dist/utils/exif-orientation.js.map +1 -1
- package/dist/utils/tools-manager.d.ts +1 -0
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +33 -18
- package/dist/utils/tools-manager.js.map +1 -1
- package/docs/custom-provider.md +2 -0
- package/docs/development.md +17 -0
- package/docs/docs.json +14 -2
- package/docs/extensions.md +9 -4
- package/docs/intercom.md +41 -7
- package/docs/keybindings.md +8 -6
- package/docs/models/evals.md +394 -0
- package/docs/models/model-selection.md +54 -27
- package/docs/models/pareto-efficiency.md +33 -16
- package/docs/models.md +37 -1
- package/docs/packages.md +2 -2
- package/docs/providers.md +44 -20
- package/docs/quickstart.md +8 -6
- package/docs/rpc.md +3 -3
- package/docs/sdk.md +2 -16
- package/docs/settings.md +19 -22
- package/docs/skills.md +4 -2
- package/docs/subagents.md +9 -3
- package/docs/tools/edit.md +373 -0
- package/docs/tools.md +3 -7
- package/docs/tui.md +2 -1
- package/docs/usage.md +3 -5
- package/docs/workflows/api-reference.md +1395 -0
- package/docs/workflows/authoring.md +678 -0
- package/docs/workflows/builtins.md +245 -0
- package/docs/workflows/operations.md +811 -0
- package/docs/workflows/reliable-design.md +2099 -0
- package/docs/workflows/verification.md +62 -0
- package/docs/workflows.md +15 -5183
- package/npm-shrinkwrap.json +584 -69
- package/package.json +7 -7
- package/dist/client/remote-session.d.ts +0 -64
- package/dist/client/remote-session.d.ts.map +0 -1
- package/dist/client/remote-session.js +0 -351
- package/dist/client/remote-session.js.map +0 -1
- package/dist/client/transcript.d.ts +0 -12
- package/dist/client/transcript.d.ts.map +0 -1
- package/dist/client/transcript.js +0 -98
- package/dist/client/transcript.js.map +0 -1
- package/dist/core/codex-fast-mode-transport.d.ts +0 -12
- package/dist/core/codex-fast-mode-transport.d.ts.map +0 -1
- package/dist/core/codex-fast-mode-transport.js.map +0 -1
- package/dist/core/codex-fast-mode.d.ts +0 -53
- package/dist/core/codex-fast-mode.d.ts.map +0 -1
- package/dist/core/codex-fast-mode.js +0 -249
- package/dist/core/codex-fast-mode.js.map +0 -1
- package/dist/modes/interactive/components/fast-mode-selector.d.ts +0 -27
- package/dist/modes/interactive/components/fast-mode-selector.d.ts.map +0 -1
- package/dist/modes/interactive/components/fast-mode-selector.js +0 -107
- package/dist/modes/interactive/components/fast-mode-selector.js.map +0 -1
- package/dist/server/create-harness.d.ts +0 -53
- package/dist/server/create-harness.d.ts.map +0 -1
- package/dist/server/create-harness.js +0 -360
- package/dist/server/create-harness.js.map +0 -1
- package/docs/models/artificial-analysis-index.md +0 -83
|
@@ -1,44 +1,504 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { createHmac, randomBytes } from "node:crypto";
|
|
2
|
+
import {
|
|
3
|
+
chmodSync,
|
|
4
|
+
closeSync,
|
|
5
|
+
existsSync,
|
|
6
|
+
mkdirSync,
|
|
7
|
+
openSync,
|
|
8
|
+
readFileSync,
|
|
9
|
+
writeFileSync,
|
|
10
|
+
} from "node:fs";
|
|
11
|
+
import { dirname } from "node:path";
|
|
12
|
+
import { DatabaseSync } from "node:sqlite";
|
|
13
|
+
import { DELIVERED_MESSAGE_TTL_MS } from "../retry-policy.js";
|
|
3
14
|
|
|
4
|
-
export
|
|
15
|
+
export const DELIVERED_MESSAGE_MAX_ENTRIES = 10_000;
|
|
16
|
+
/** Upper bound for persisted authority bytes (signatures are fixed-size HMAC digests). */
|
|
17
|
+
export const DELIVERED_MESSAGE_MAX_BYTES = 64 * 1024 * 1024;
|
|
18
|
+
const AUTHORITY_KEY_BYTES = 32;
|
|
19
|
+
const AUTHORITY_DIGEST_PATTERN = /^[a-f0-9]{64}$/u;
|
|
20
|
+
|
|
21
|
+
export type DeliveredMessageMatch = "miss" | "match" | "conflict" | "uncertain" | "invalid";
|
|
22
|
+
export type DeliveredMessageRecordResult = "recorded" | "match" | "conflict" | "uncertain" | "capacity" | "invalid";
|
|
23
|
+
export type DeliveredMessageAcceptResult = "accepted" | "conflict" | "invalid";
|
|
24
|
+
|
|
25
|
+
export interface DeliveredQuestionRoute {
|
|
26
|
+
readonly targetSessionId: string;
|
|
27
|
+
readonly senderGroupIdentity: string;
|
|
28
|
+
}
|
|
5
29
|
|
|
6
30
|
interface DeliveredMessage {
|
|
7
|
-
|
|
8
|
-
|
|
31
|
+
state: "reserved" | "accepted";
|
|
32
|
+
deliveredAt: number;
|
|
33
|
+
signature: string;
|
|
34
|
+
targetIdentity?: string;
|
|
35
|
+
questionRoute?: DeliveredQuestionRoute;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface SqliteStatement {
|
|
39
|
+
get(...params: Array<string | number | null>): Record<string, unknown> | undefined;
|
|
40
|
+
run(...params: Array<string | number | null>): { changes: number | bigint };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface SqliteDatabase {
|
|
44
|
+
exec(sql: string): void;
|
|
45
|
+
prepare(sql: string): SqliteStatement;
|
|
46
|
+
close(): void;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function validStoredString(value: unknown): value is string {
|
|
50
|
+
return typeof value === "string" && value.length > 0;
|
|
9
51
|
}
|
|
10
52
|
|
|
11
|
-
|
|
53
|
+
function entryByteSize(messageId: string, delivered: DeliveredMessage): number {
|
|
54
|
+
return [
|
|
55
|
+
messageId,
|
|
56
|
+
delivered.signature,
|
|
57
|
+
delivered.targetIdentity,
|
|
58
|
+
delivered.questionRoute?.targetSessionId,
|
|
59
|
+
delivered.questionRoute?.senderGroupIdentity,
|
|
60
|
+
].reduce((total, value) => total + (value === undefined ? 0 : Buffer.byteLength(value)), 0);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function parseStoredMessage(row: Record<string, unknown> | undefined): DeliveredMessage | undefined | "invalid" {
|
|
64
|
+
if (row === undefined) return undefined;
|
|
65
|
+
if (
|
|
66
|
+
(row.state !== "reserved" && row.state !== "accepted") ||
|
|
67
|
+
!validStoredString(row.signature) ||
|
|
68
|
+
!AUTHORITY_DIGEST_PATTERN.test(row.signature) ||
|
|
69
|
+
typeof row.delivered_at !== "number" ||
|
|
70
|
+
!Number.isSafeInteger(row.delivered_at) ||
|
|
71
|
+
row.delivered_at < 0 ||
|
|
72
|
+
(row.target_identity !== null && !validStoredString(row.target_identity))
|
|
73
|
+
) {
|
|
74
|
+
return "invalid";
|
|
75
|
+
}
|
|
76
|
+
const questionValues = [row.question_target_session_id, row.question_sender_group_identity];
|
|
77
|
+
const hasQuestionRoute = questionValues.some((value) => value !== null);
|
|
78
|
+
if (hasQuestionRoute && !questionValues.every(validStoredString)) return "invalid";
|
|
79
|
+
return {
|
|
80
|
+
state: row.state,
|
|
81
|
+
deliveredAt: row.delivered_at,
|
|
82
|
+
signature: row.signature,
|
|
83
|
+
...(typeof row.target_identity === "string" ? { targetIdentity: row.target_identity } : {}),
|
|
84
|
+
...(hasQuestionRoute
|
|
85
|
+
? {
|
|
86
|
+
questionRoute: {
|
|
87
|
+
targetSessionId: row.question_target_session_id as string,
|
|
88
|
+
senderGroupIdentity: row.question_sender_group_identity as string,
|
|
89
|
+
},
|
|
90
|
+
}
|
|
91
|
+
: {}),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Authority-key path derived without introducing another user setting. */
|
|
96
|
+
export function getDeliveredMessageAuthorityKeyPath(storagePath: string): string {
|
|
97
|
+
return storagePath.endsWith(".sqlite") ? `${storagePath.slice(0, -".sqlite".length)}.key` : `${storagePath}.key`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function restrictMode(path: string, mode: number): void {
|
|
101
|
+
if (process.platform !== "win32") chmodSync(path, mode);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function restrictExistingAuthorityArtifacts(storagePath: string, keyPath: string): void {
|
|
105
|
+
for (const path of [storagePath, `${storagePath}-wal`, `${storagePath}-shm`, keyPath]) {
|
|
106
|
+
if (existsSync(path)) restrictMode(path, 0o600);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Prepare a paired database and key. A missing half of an existing pair is not
|
|
112
|
+
* recreated: accepted authority may have been lost, so startup must fail closed.
|
|
113
|
+
*/
|
|
114
|
+
function prepareDurableAuthority(storagePath: string): Buffer | undefined {
|
|
115
|
+
const directory = dirname(storagePath);
|
|
116
|
+
const keyPath = getDeliveredMessageAuthorityKeyPath(storagePath);
|
|
117
|
+
mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
118
|
+
restrictMode(directory, 0o700);
|
|
119
|
+
restrictExistingAuthorityArtifacts(storagePath, keyPath);
|
|
120
|
+
const databaseExists = existsSync(storagePath);
|
|
121
|
+
const keyExists = existsSync(keyPath);
|
|
122
|
+
if (databaseExists !== keyExists) return undefined;
|
|
123
|
+
if (!databaseExists) {
|
|
124
|
+
const key = randomBytes(AUTHORITY_KEY_BYTES);
|
|
125
|
+
writeFileSync(keyPath, key, { flag: "wx", mode: 0o600 });
|
|
126
|
+
const databaseDescriptor = openSync(storagePath, "wx", 0o600);
|
|
127
|
+
closeSync(databaseDescriptor);
|
|
128
|
+
restrictExistingAuthorityArtifacts(storagePath, keyPath);
|
|
129
|
+
return key;
|
|
130
|
+
}
|
|
131
|
+
const key = readFileSync(keyPath);
|
|
132
|
+
if (key.length !== AUTHORITY_KEY_BYTES) return undefined;
|
|
133
|
+
return key;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Bounded durable operation authority. The broker uses a SQLite-backed instance;
|
|
138
|
+
* tests may omit `storagePath` for an isolated in-memory authority. Durable
|
|
139
|
+
* signatures are keyed SHA-256 HMACs, so message and attachment content never
|
|
140
|
+
* reaches SQLite and low-entropy payloads cannot be tested without the paired
|
|
141
|
+
* owner-only key file. A reservation is written before forwarding and promoted
|
|
142
|
+
* to accepted after a confirmed write.
|
|
143
|
+
*/
|
|
12
144
|
export class DeliveredMessageCache {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
145
|
+
private readonly delivered = new Map<string, DeliveredMessage>();
|
|
146
|
+
private retainedBytes = 0;
|
|
147
|
+
private readonly database?: SqliteDatabase;
|
|
148
|
+
private readonly authorityKey?: Buffer;
|
|
149
|
+
private readonly storagePath?: string;
|
|
150
|
+
private storageInvalid = false;
|
|
151
|
+
|
|
152
|
+
constructor(
|
|
153
|
+
private readonly ttlMs = DELIVERED_MESSAGE_TTL_MS,
|
|
154
|
+
private readonly maxEntries = DELIVERED_MESSAGE_MAX_ENTRIES,
|
|
155
|
+
storagePath?: string,
|
|
156
|
+
private readonly maxBytes = DELIVERED_MESSAGE_MAX_BYTES,
|
|
157
|
+
) {
|
|
158
|
+
if (storagePath === undefined) return;
|
|
159
|
+
this.storagePath = storagePath;
|
|
160
|
+
let database: SqliteDatabase | undefined;
|
|
161
|
+
try {
|
|
162
|
+
const authorityKey = prepareDurableAuthority(storagePath);
|
|
163
|
+
if (authorityKey === undefined) {
|
|
164
|
+
this.storageInvalid = true;
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
this.authorityKey = authorityKey;
|
|
168
|
+
database = new DatabaseSync(storagePath) as unknown as SqliteDatabase;
|
|
169
|
+
database.exec("PRAGMA busy_timeout = 5000");
|
|
170
|
+
database.exec("PRAGMA journal_mode = WAL");
|
|
171
|
+
database.exec("PRAGMA synchronous = FULL");
|
|
172
|
+
database.exec("PRAGMA trusted_schema = OFF");
|
|
173
|
+
database.exec(`
|
|
174
|
+
CREATE TABLE IF NOT EXISTS delivered_messages (
|
|
175
|
+
state TEXT NOT NULL CHECK (state IN ('reserved', 'accepted')),
|
|
176
|
+
message_id TEXT PRIMARY KEY NOT NULL,
|
|
177
|
+
signature TEXT NOT NULL,
|
|
178
|
+
delivered_at INTEGER NOT NULL,
|
|
179
|
+
target_identity TEXT,
|
|
180
|
+
question_target_session_id TEXT,
|
|
181
|
+
question_sender_group_identity TEXT
|
|
182
|
+
) STRICT;
|
|
183
|
+
CREATE INDEX IF NOT EXISTS delivered_messages_age
|
|
184
|
+
ON delivered_messages(delivered_at);
|
|
185
|
+
`);
|
|
186
|
+
database.prepare("SELECT state, signature, delivered_at, target_identity FROM delivered_messages LIMIT 0").get();
|
|
187
|
+
restrictExistingAuthorityArtifacts(storagePath, getDeliveredMessageAuthorityKeyPath(storagePath));
|
|
188
|
+
} catch {
|
|
189
|
+
database?.close();
|
|
190
|
+
try {
|
|
191
|
+
restrictExistingAuthorityArtifacts(storagePath, getDeliveredMessageAuthorityKeyPath(storagePath));
|
|
192
|
+
} catch {
|
|
193
|
+
// Storage is already invalid; permission correction is best-effort here.
|
|
194
|
+
}
|
|
195
|
+
this.storageInvalid = true;
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
this.database = database;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
lookup(messageId: string, signature: string, now = Date.now()): DeliveredMessageMatch {
|
|
202
|
+
const authority = this.signatureAuthority(signature);
|
|
203
|
+
return authority === undefined ? "invalid" : this.lookupEntry(messageId, authority, undefined, false, now);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
lookupForTarget(
|
|
207
|
+
messageId: string,
|
|
208
|
+
signature: string,
|
|
209
|
+
targetIdentity: string,
|
|
210
|
+
now = Date.now(),
|
|
211
|
+
): DeliveredMessageMatch {
|
|
212
|
+
const authority = this.signatureAuthority(signature);
|
|
213
|
+
return authority === undefined ? "invalid" : this.lookupEntry(messageId, authority, targetIdentity, true, now);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
lookupQuestionTarget(
|
|
217
|
+
messageId: string,
|
|
218
|
+
signature: string,
|
|
219
|
+
senderGroupIdentity: string,
|
|
220
|
+
now = Date.now(),
|
|
221
|
+
): string | undefined {
|
|
222
|
+
const authority = this.signatureAuthority(signature);
|
|
223
|
+
if (authority === undefined) return undefined;
|
|
224
|
+
this.prune(now);
|
|
225
|
+
const delivered = this.read(messageId);
|
|
226
|
+
if (
|
|
227
|
+
delivered === undefined ||
|
|
228
|
+
delivered === "invalid" ||
|
|
229
|
+
delivered.state !== "accepted" ||
|
|
230
|
+
now - delivered.deliveredAt > this.ttlMs ||
|
|
231
|
+
delivered.signature !== authority ||
|
|
232
|
+
delivered.questionRoute?.senderGroupIdentity !== senderGroupIdentity
|
|
233
|
+
) {
|
|
234
|
+
return undefined;
|
|
235
|
+
}
|
|
236
|
+
return delivered.questionRoute.targetSessionId;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
record(
|
|
240
|
+
messageId: string,
|
|
241
|
+
signature: string,
|
|
242
|
+
now = Date.now(),
|
|
243
|
+
targetIdentity?: string,
|
|
244
|
+
): DeliveredMessageRecordResult {
|
|
245
|
+
return this.recordWithSignature(messageId, { state: "accepted", deliveredAt: now, signature, targetIdentity });
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
recordQuestion(
|
|
249
|
+
messageId: string,
|
|
250
|
+
signature: string,
|
|
251
|
+
questionRoute: DeliveredQuestionRoute,
|
|
252
|
+
now = Date.now(),
|
|
253
|
+
targetIdentity?: string,
|
|
254
|
+
): DeliveredMessageRecordResult {
|
|
255
|
+
return this.recordWithSignature(messageId, {
|
|
256
|
+
state: "accepted",
|
|
257
|
+
deliveredAt: now,
|
|
258
|
+
signature,
|
|
259
|
+
targetIdentity,
|
|
260
|
+
questionRoute,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
reserve(
|
|
265
|
+
messageId: string,
|
|
266
|
+
signature: string,
|
|
267
|
+
now = Date.now(),
|
|
268
|
+
targetIdentity?: string,
|
|
269
|
+
): DeliveredMessageRecordResult {
|
|
270
|
+
return this.recordWithSignature(messageId, { state: "reserved", deliveredAt: now, signature, targetIdentity });
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
reserveQuestion(
|
|
274
|
+
messageId: string,
|
|
275
|
+
signature: string,
|
|
276
|
+
questionRoute: DeliveredQuestionRoute,
|
|
277
|
+
now = Date.now(),
|
|
278
|
+
targetIdentity?: string,
|
|
279
|
+
): DeliveredMessageRecordResult {
|
|
280
|
+
return this.recordWithSignature(messageId, {
|
|
281
|
+
state: "reserved",
|
|
282
|
+
deliveredAt: now,
|
|
283
|
+
signature,
|
|
284
|
+
targetIdentity,
|
|
285
|
+
questionRoute,
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
accept(messageId: string, signature: string, targetIdentity?: string): DeliveredMessageAcceptResult {
|
|
290
|
+
const authority = this.signatureAuthority(signature);
|
|
291
|
+
if (authority === undefined) return "invalid";
|
|
292
|
+
const existing = this.read(messageId);
|
|
293
|
+
if (existing === undefined || existing === "invalid") return "invalid";
|
|
294
|
+
if (existing.signature !== authority || existing.targetIdentity !== targetIdentity) return "conflict";
|
|
295
|
+
if (existing.state === "accepted") return "accepted";
|
|
296
|
+
if (this.database === undefined) {
|
|
297
|
+
existing.state = "accepted";
|
|
298
|
+
return "accepted";
|
|
299
|
+
}
|
|
300
|
+
const updated = this.database
|
|
301
|
+
.prepare("UPDATE delivered_messages SET state = 'accepted' WHERE message_id = ? AND signature = ?")
|
|
302
|
+
.run(messageId, authority);
|
|
303
|
+
this.restrictArtifacts();
|
|
304
|
+
return Number(updated.changes) === 1 ? "accepted" : "invalid";
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/** Remove only the exact pre-forward reservation after a proven write failure. */
|
|
308
|
+
forget(messageId: string, signature: string): void {
|
|
309
|
+
const authority = this.signatureAuthority(signature);
|
|
310
|
+
if (authority === undefined) return;
|
|
311
|
+
if (this.database === undefined) {
|
|
312
|
+
const delivered = this.delivered.get(messageId);
|
|
313
|
+
if (delivered?.signature === authority) {
|
|
314
|
+
this.retainedBytes -= entryByteSize(messageId, delivered);
|
|
315
|
+
this.delivered.delete(messageId);
|
|
316
|
+
}
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
this.database
|
|
320
|
+
.prepare("DELETE FROM delivered_messages WHERE message_id = ? AND signature = ?")
|
|
321
|
+
.run(messageId, authority);
|
|
322
|
+
this.restrictArtifacts();
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
close(): void {
|
|
326
|
+
this.restrictArtifacts();
|
|
327
|
+
this.database?.close();
|
|
328
|
+
this.restrictArtifacts();
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
private signatureAuthority(signature: string): string | undefined {
|
|
332
|
+
if (this.storageInvalid || !validStoredString(signature)) return undefined;
|
|
333
|
+
if (this.storagePath === undefined) return signature;
|
|
334
|
+
if (this.authorityKey === undefined) return undefined;
|
|
335
|
+
return createHmac("sha256", this.authorityKey).update(signature).digest("hex");
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
private recordWithSignature(messageId: string, delivered: DeliveredMessage): DeliveredMessageRecordResult {
|
|
339
|
+
const authority = this.signatureAuthority(delivered.signature);
|
|
340
|
+
if (authority === undefined) return "invalid";
|
|
341
|
+
return this.recordEntry(messageId, { ...delivered, signature: authority });
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
private lookupEntry(
|
|
345
|
+
messageId: string,
|
|
346
|
+
signature: string,
|
|
347
|
+
targetIdentity: string | undefined,
|
|
348
|
+
requireTargetIdentity: boolean,
|
|
349
|
+
now: number,
|
|
350
|
+
): DeliveredMessageMatch {
|
|
351
|
+
if (this.storageInvalid) return "invalid";
|
|
352
|
+
this.prune(now);
|
|
353
|
+
const delivered = this.read(messageId);
|
|
354
|
+
if (delivered === "invalid") return "invalid";
|
|
355
|
+
if (delivered === undefined || now - delivered.deliveredAt > this.ttlMs) return "miss";
|
|
356
|
+
if (delivered.signature !== signature) return "conflict";
|
|
357
|
+
if (requireTargetIdentity && delivered.targetIdentity !== targetIdentity) return "conflict";
|
|
358
|
+
if (delivered.state === "reserved") return "uncertain";
|
|
359
|
+
return "match";
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
private recordEntry(messageId: string, delivered: DeliveredMessage): DeliveredMessageRecordResult {
|
|
363
|
+
if (this.storageInvalid) return "invalid";
|
|
364
|
+
if (!validStoredString(messageId) || !validStoredString(delivered.signature)) return "invalid";
|
|
365
|
+
if (
|
|
366
|
+
!Number.isSafeInteger(delivered.deliveredAt) ||
|
|
367
|
+
delivered.deliveredAt < 0 ||
|
|
368
|
+
(delivered.targetIdentity !== undefined && !validStoredString(delivered.targetIdentity)) ||
|
|
369
|
+
(delivered.questionRoute !== undefined &&
|
|
370
|
+
(!validStoredString(delivered.questionRoute.targetSessionId) ||
|
|
371
|
+
!validStoredString(delivered.questionRoute.senderGroupIdentity)))
|
|
372
|
+
) {
|
|
373
|
+
return "invalid";
|
|
374
|
+
}
|
|
375
|
+
if (entryByteSize(messageId, delivered) > this.maxBytes) return "capacity";
|
|
376
|
+
if (this.database === undefined) return this.recordMemory(messageId, delivered);
|
|
377
|
+
return this.recordDurable(messageId, delivered);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
private recordMemory(messageId: string, delivered: DeliveredMessage): DeliveredMessageRecordResult {
|
|
381
|
+
this.pruneMemory(delivered.deliveredAt);
|
|
382
|
+
const existing = this.delivered.get(messageId);
|
|
383
|
+
if (existing !== undefined) return this.compare(existing, delivered);
|
|
384
|
+
const size = entryByteSize(messageId, delivered);
|
|
385
|
+
if (this.delivered.size >= this.maxEntries || this.retainedBytes + size > this.maxBytes) return "capacity";
|
|
386
|
+
this.delivered.set(messageId, delivered);
|
|
387
|
+
this.retainedBytes += size;
|
|
388
|
+
return "recorded";
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
private recordDurable(messageId: string, delivered: DeliveredMessage): DeliveredMessageRecordResult {
|
|
392
|
+
const database = this.database;
|
|
393
|
+
if (database === undefined) return "invalid";
|
|
394
|
+
database.exec("BEGIN IMMEDIATE");
|
|
395
|
+
try {
|
|
396
|
+
this.pruneDurable(delivered.deliveredAt);
|
|
397
|
+
const existing = this.readDurable(messageId);
|
|
398
|
+
if (existing !== undefined) {
|
|
399
|
+
database.exec("COMMIT");
|
|
400
|
+
return existing === "invalid" ? "invalid" : this.compare(existing, delivered);
|
|
401
|
+
}
|
|
402
|
+
const totals = database.prepare(`
|
|
403
|
+
SELECT COUNT(*) AS count,
|
|
404
|
+
COALESCE(SUM(
|
|
405
|
+
length(CAST(message_id AS BLOB)) + length(CAST(signature AS BLOB)) +
|
|
406
|
+
COALESCE(length(CAST(target_identity AS BLOB)), 0) +
|
|
407
|
+
COALESCE(length(CAST(question_target_session_id AS BLOB)), 0) +
|
|
408
|
+
COALESCE(length(CAST(question_sender_group_identity AS BLOB)), 0)
|
|
409
|
+
), 0) AS bytes
|
|
410
|
+
FROM delivered_messages
|
|
411
|
+
`).get();
|
|
412
|
+
const count = totals?.count;
|
|
413
|
+
const bytes = totals?.bytes;
|
|
414
|
+
if (
|
|
415
|
+
typeof count !== "number" ||
|
|
416
|
+
!Number.isSafeInteger(count) ||
|
|
417
|
+
count < 0 ||
|
|
418
|
+
typeof bytes !== "number" ||
|
|
419
|
+
!Number.isSafeInteger(bytes) ||
|
|
420
|
+
bytes < 0
|
|
421
|
+
) {
|
|
422
|
+
database.exec("COMMIT");
|
|
423
|
+
return "invalid";
|
|
424
|
+
}
|
|
425
|
+
if (count >= this.maxEntries || bytes + entryByteSize(messageId, delivered) > this.maxBytes) {
|
|
426
|
+
database.exec("COMMIT");
|
|
427
|
+
return "capacity";
|
|
428
|
+
}
|
|
429
|
+
database
|
|
430
|
+
.prepare(`
|
|
431
|
+
INSERT INTO delivered_messages (
|
|
432
|
+
message_id, state, signature, delivered_at, target_identity,
|
|
433
|
+
question_target_session_id, question_sender_group_identity
|
|
434
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
435
|
+
`)
|
|
436
|
+
.run(
|
|
437
|
+
messageId,
|
|
438
|
+
delivered.state,
|
|
439
|
+
delivered.signature,
|
|
440
|
+
delivered.deliveredAt,
|
|
441
|
+
delivered.targetIdentity ?? null,
|
|
442
|
+
delivered.questionRoute?.targetSessionId ?? null,
|
|
443
|
+
delivered.questionRoute?.senderGroupIdentity ?? null,
|
|
444
|
+
);
|
|
445
|
+
database.exec("COMMIT");
|
|
446
|
+
this.restrictArtifacts();
|
|
447
|
+
return "recorded";
|
|
448
|
+
} catch (error) {
|
|
449
|
+
try {
|
|
450
|
+
database.exec("ROLLBACK");
|
|
451
|
+
} catch {
|
|
452
|
+
// Preserve the original storage failure.
|
|
453
|
+
}
|
|
454
|
+
throw error;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
private compare(existing: DeliveredMessage, incoming: DeliveredMessage): DeliveredMessageRecordResult {
|
|
459
|
+
if (existing.signature !== incoming.signature || existing.targetIdentity !== incoming.targetIdentity) {
|
|
460
|
+
return "conflict";
|
|
461
|
+
}
|
|
462
|
+
return existing.state === "accepted" ? "match" : "uncertain";
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
private read(messageId: string): DeliveredMessage | undefined | "invalid" {
|
|
466
|
+
return this.database === undefined ? this.delivered.get(messageId) : this.readDurable(messageId);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
private readDurable(messageId: string): DeliveredMessage | undefined | "invalid" {
|
|
470
|
+
const row = this.database
|
|
471
|
+
?.prepare(`
|
|
472
|
+
SELECT state, signature, delivered_at, target_identity,
|
|
473
|
+
question_target_session_id, question_sender_group_identity
|
|
474
|
+
FROM delivered_messages WHERE message_id = ?
|
|
475
|
+
`)
|
|
476
|
+
.get(messageId);
|
|
477
|
+
return parseStoredMessage(row);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
private prune(now: number): void {
|
|
481
|
+
if (this.database === undefined) this.pruneMemory(now);
|
|
482
|
+
else this.pruneDurable(now);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
private pruneMemory(now: number): void {
|
|
486
|
+
for (const [messageId, delivered] of this.delivered) {
|
|
487
|
+
if (now - delivered.deliveredAt <= this.ttlMs) continue;
|
|
488
|
+
this.delivered.delete(messageId);
|
|
489
|
+
this.retainedBytes -= entryByteSize(messageId, delivered);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
private pruneDurable(now: number): void {
|
|
494
|
+
this.database
|
|
495
|
+
?.prepare("DELETE FROM delivered_messages WHERE typeof(delivered_at) = 'integer' AND ? - delivered_at > ?")
|
|
496
|
+
.run(now, this.ttlMs);
|
|
497
|
+
this.restrictArtifacts();
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
private restrictArtifacts(): void {
|
|
501
|
+
if (this.storagePath === undefined || this.storageInvalid) return;
|
|
502
|
+
restrictExistingAuthorityArtifacts(this.storagePath, getDeliveredMessageAuthorityKeyPath(this.storagePath));
|
|
503
|
+
}
|
|
44
504
|
}
|
|
@@ -4,12 +4,14 @@ import type { Socket } from "net";
|
|
|
4
4
|
* Write a length-prefixed message to a socket.
|
|
5
5
|
* Format: 4-byte big-endian length + JSON payload
|
|
6
6
|
*/
|
|
7
|
-
export function writeMessage(socket: Socket, msg: unknown): void {
|
|
7
|
+
export function writeMessage(socket: Socket, msg: unknown, callback?: (error?: Error | null) => void): void {
|
|
8
8
|
const json = JSON.stringify(msg);
|
|
9
9
|
const payload = Buffer.from(json, "utf-8");
|
|
10
10
|
const header = Buffer.alloc(4);
|
|
11
11
|
header.writeUInt32BE(payload.length, 0);
|
|
12
|
-
|
|
12
|
+
const frame = Buffer.concat([header, payload]);
|
|
13
|
+
if (callback === undefined) socket.write(frame);
|
|
14
|
+
else socket.write(frame, callback);
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
/**
|
|
@@ -46,6 +46,16 @@ export function getBrokerSpawnLockPath(agentDir: string = getAgentDir()): string
|
|
|
46
46
|
return join(getIntercomDirPath(agentDir), "broker.spawn.lock");
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
/** Durable accepted-operation authority shared by replacement broker processes. */
|
|
50
|
+
export function getBrokerDeliveredMessagesPath(agentDir: string = getAgentDir()): string {
|
|
51
|
+
return join(getIntercomDirPath(agentDir), "delivered-messages.sqlite");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Owner-only HMAC key paired with durable accepted-operation authority. */
|
|
55
|
+
export function getBrokerDeliveredMessagesKeyPath(agentDir: string = getAgentDir()): string {
|
|
56
|
+
return join(getIntercomDirPath(agentDir), "delivered-messages.key");
|
|
57
|
+
}
|
|
58
|
+
|
|
49
59
|
/**
|
|
50
60
|
* Startup/diagnostic log for the detached broker process. The parent opens this file
|
|
51
61
|
* and hands the descriptor to the child as stderr, so a broker that dies before it can
|