@bastani/atomic 0.9.18-alpha.5 → 0.9.18-alpha.6
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 +86 -0
- package/README.md +3 -3
- package/dist/builtin/intercom/CHANGELOG.md +33 -0
- package/dist/builtin/intercom/README.md +17 -2
- package/dist/builtin/intercom/broker/broker.ts +670 -127
- package/dist/builtin/intercom/broker/client.ts +149 -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 +1406 -209
- 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 +25 -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 +44 -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/docs.json +14 -2
- package/docs/extensions.md +9 -4
- package/docs/intercom.md +32 -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,12 +1,18 @@
|
|
|
1
1
|
import type net from "node:net";
|
|
2
2
|
import type { BrokerMessage, Message, SessionInfo } from "../types.js";
|
|
3
|
+
import {
|
|
4
|
+
legacyWorkflowStageTargetMigrationHint,
|
|
5
|
+
parseLegacyWorkflowStageTarget,
|
|
6
|
+
parseWorkflowStageTarget,
|
|
7
|
+
} from "../workflow-stage-target.js";
|
|
8
|
+
import { normalizeGroup } from "../group.js";
|
|
3
9
|
import { isMessage } from "./client-message-validation.js";
|
|
4
10
|
import { resolveSessionTarget, sessionTargetFailureReason } from "../session-target.js";
|
|
5
11
|
import { DeliveredMessageCache } from "./delivered-message-cache.js";
|
|
6
12
|
import { buildMessageSendSignature } from "./send-signature.js";
|
|
7
13
|
import { SupervisorChannelCache } from "./supervisor-channel.js";
|
|
8
14
|
import { isVerticalBypass, sameGroup } from "./group-isolation.js";
|
|
9
|
-
import { sessionsShareGroup } from "./group-membership.js";
|
|
15
|
+
import { sessionGroups, sessionsShareGroup } from "./group-membership.js";
|
|
10
16
|
import { PendingQuestionIndex } from "./pending-question-index.js";
|
|
11
17
|
|
|
12
18
|
export interface BrokerConnectedSession {
|
|
@@ -28,11 +34,25 @@ export interface BrokerConnectedSession {
|
|
|
28
34
|
supervisorOwnerToken?: string;
|
|
29
35
|
}
|
|
30
36
|
|
|
37
|
+
/** Immutable registration authority plus normalized memberships used when an accepted ask is rebound. */
|
|
38
|
+
export function senderGroupIdentity(session: BrokerConnectedSession): string {
|
|
39
|
+
return JSON.stringify([
|
|
40
|
+
normalizeGroup(session.registrationGroup),
|
|
41
|
+
[...sessionGroups(session.info)].sort((left, right) => left.localeCompare(right)),
|
|
42
|
+
]);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
/** Stable recipient authority used independently from the freshly resolved transport ID. */
|
|
47
|
+
export function deliveryTargetIdentity(session: BrokerConnectedSession): string {
|
|
48
|
+
return session.registrationReturnAddress === undefined
|
|
49
|
+
? JSON.stringify(["session", session.info.id])
|
|
50
|
+
: JSON.stringify(["return", session.registrationReturnAddress, normalizeGroup(session.registrationGroup)]);
|
|
51
|
+
}
|
|
31
52
|
export interface PendingStageRoute {
|
|
32
53
|
readonly socket: net.Socket;
|
|
33
54
|
readonly from: BrokerConnectedSession;
|
|
34
|
-
|
|
35
|
-
readonly stageKey: string;
|
|
55
|
+
readonly target: string;
|
|
36
56
|
readonly message: Message;
|
|
37
57
|
readonly attemptId?: string;
|
|
38
58
|
readonly liveTargetId?: string;
|
|
@@ -40,6 +60,11 @@ export interface PendingStageRoute {
|
|
|
40
60
|
}
|
|
41
61
|
|
|
42
62
|
export type PendingStageRouter = (route: PendingStageRoute) => boolean;
|
|
63
|
+
export type ConfirmedMessageWriter = (
|
|
64
|
+
target: net.Socket,
|
|
65
|
+
message: BrokerMessage,
|
|
66
|
+
onSettled: (written: boolean) => void,
|
|
67
|
+
) => void;
|
|
43
68
|
|
|
44
69
|
export type LiveWorkflowStageResolver = (target: string) => BrokerConnectedSession | undefined;
|
|
45
70
|
export type LiveWorkflowStageController = (
|
|
@@ -48,15 +73,7 @@ export type LiveWorkflowStageController = (
|
|
|
48
73
|
logicalTarget: string,
|
|
49
74
|
) => boolean;
|
|
50
75
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
export function parsePendingStageTarget(target: string): { runId: string; stageKey: string } | undefined {
|
|
54
|
-
const separator = target.indexOf(":");
|
|
55
|
-
if (separator < 0) return undefined;
|
|
56
|
-
const runId = target.slice(0, separator);
|
|
57
|
-
const stageKey = target.slice(separator + 1);
|
|
58
|
-
return WORKFLOW_RUN_ID_PATTERN.test(runId) && stageKey.length > 0 ? { runId, stageKey } : undefined;
|
|
59
|
-
}
|
|
76
|
+
export type LegacyWorkflowStageTargetResolver = (runId: string, stageKey: string) => string | undefined;
|
|
60
77
|
export const PENDING_STAGE_ASK_REFUSAL =
|
|
61
78
|
"Cannot ask a workflow stage whose session has not initialized. Use send; Atomic will queue the message until the stage session initializes.";
|
|
62
79
|
|
|
@@ -80,12 +97,20 @@ export function handleBrokerSend(
|
|
|
80
97
|
currentId: string | null,
|
|
81
98
|
sessions: Map<string, BrokerConnectedSession>,
|
|
82
99
|
deliveredMessages: DeliveredMessageCache,
|
|
83
|
-
|
|
100
|
+
/**
|
|
101
|
+
* Hand one frame to a socket. Returns whether the frame was actually written.
|
|
102
|
+
* Durable acceptance is reserved before this call to close the restart window;
|
|
103
|
+
* a `false` answer removes that exact reservation, while only `true` is
|
|
104
|
+
* acknowledged to the sender.
|
|
105
|
+
*/
|
|
106
|
+
write: (target: net.Socket, message: BrokerMessage) => boolean,
|
|
84
107
|
supervisorCache: SupervisorChannelCache = new SupervisorChannelCache(),
|
|
85
108
|
pendingQuestions: PendingQuestionIndex = new PendingQuestionIndex(),
|
|
86
109
|
routePendingStage?: PendingStageRouter,
|
|
87
110
|
resolveLiveWorkflowStage?: LiveWorkflowStageResolver,
|
|
88
111
|
canControlLiveWorkflowStage?: LiveWorkflowStageController,
|
|
112
|
+
resolveLegacyWorkflowStageTarget?: LegacyWorkflowStageTargetResolver,
|
|
113
|
+
writeConfirmed?: ConfirmedMessageWriter,
|
|
89
114
|
): void {
|
|
90
115
|
const message = clientMessage.message;
|
|
91
116
|
const messageId = wireMessageId(message);
|
|
@@ -99,15 +124,30 @@ export function handleBrokerSend(
|
|
|
99
124
|
return;
|
|
100
125
|
}
|
|
101
126
|
const attemptId = typeof clientMessage.attemptId === "string" ? clientMessage.attemptId : undefined;
|
|
127
|
+
const hasLogicalTarget = Object.prototype.hasOwnProperty.call(clientMessage, "logicalTarget");
|
|
128
|
+
if (hasLogicalTarget && typeof clientMessage.logicalTarget !== "string") {
|
|
129
|
+
write(socket, { type: "delivery_failed", messageId, attemptId, reason: "Invalid logicalTarget format" });
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
102
132
|
if (typeof clientMessage.to !== "string" || !isMessage(message)) {
|
|
103
133
|
write(socket, { type: "delivery_failed", messageId, attemptId, reason: "Invalid message format" });
|
|
104
134
|
return;
|
|
105
135
|
}
|
|
136
|
+
const hasRequirePendingReply = Object.prototype.hasOwnProperty.call(clientMessage, "requirePendingReply");
|
|
137
|
+
if (hasRequirePendingReply && clientMessage.requirePendingReply !== true) {
|
|
138
|
+
write(socket, { type: "delivery_failed", messageId, attemptId, reason: "Invalid requirePendingReply format" });
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
const requirePendingReply = clientMessage.requirePendingReply === true;
|
|
106
142
|
if (Object.prototype.hasOwnProperty.call(clientMessage, "channel")) {
|
|
107
143
|
write(socket, { type: "delivery_failed", messageId: message.id, attemptId, reason: "Invalid channel" });
|
|
108
144
|
return;
|
|
109
145
|
}
|
|
110
146
|
const supervisorSend = clientMessage.type === "supervisor_send";
|
|
147
|
+
if (requirePendingReply && (supervisorSend || message.replyTo === undefined || message.expectsReply === true)) {
|
|
148
|
+
write(socket, { type: "delivery_failed", messageId, attemptId, reason: "Invalid requirePendingReply message" });
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
111
151
|
|
|
112
152
|
|
|
113
153
|
const fromSession = currentId ? sessions.get(currentId) : undefined;
|
|
@@ -115,22 +155,16 @@ export function handleBrokerSend(
|
|
|
115
155
|
write(socket, { type: "delivery_failed", messageId: message.id, attemptId, reason: "Sender session not found" });
|
|
116
156
|
return;
|
|
117
157
|
}
|
|
118
|
-
const
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
attemptId,
|
|
129
|
-
reason: `Intercom message ID '${message.id}' was already delivered with a different target or payload`,
|
|
130
|
-
reasonCode: "message_id_conflict",
|
|
131
|
-
});
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
158
|
+
const senderIdentity = fromSession.registrationReturnAddress ?? fromSession.info.id;
|
|
159
|
+
const logicalTarget = typeof clientMessage.logicalTarget === "string" ? clientMessage.logicalTarget : clientMessage.to;
|
|
160
|
+
const baseSignature = buildMessageSendSignature(logicalTarget, message, senderIdentity);
|
|
161
|
+
const signature = requirePendingReply
|
|
162
|
+
? JSON.stringify({
|
|
163
|
+
requirePendingReply: true,
|
|
164
|
+
senderGroupIdentity: senderGroupIdentity(fromSession),
|
|
165
|
+
baseSignature,
|
|
166
|
+
})
|
|
167
|
+
: baseSignature;
|
|
134
168
|
const trimmedTo = clientMessage.to.trim();
|
|
135
169
|
if (supervisorSend && !fromSession.supervisorId) {
|
|
136
170
|
write(socket, { type: "delivery_failed", messageId: message.id, attemptId, reason: "Supervisor channel is not authorized" });
|
|
@@ -140,6 +174,30 @@ export function handleBrokerSend(
|
|
|
140
174
|
write(socket, { type: "delivery_failed", messageId: message.id, attemptId, reason: "Supervisor target does not match the authorized relationship" });
|
|
141
175
|
return;
|
|
142
176
|
}
|
|
177
|
+
const workflowTarget = parseWorkflowStageTarget(trimmedTo);
|
|
178
|
+
// Slice 3 (D3): asks to pattern/future stage targets stay refused; `send` is queued
|
|
179
|
+
// sticky by the workflow host, so the refusal must happen before any live delivery.
|
|
180
|
+
if (workflowTarget !== undefined && workflowTarget.kind !== "path" && message.expectsReply === true) {
|
|
181
|
+
write(socket, {
|
|
182
|
+
type: "delivery_failed",
|
|
183
|
+
messageId: message.id,
|
|
184
|
+
...(attemptId ? { attemptId } : {}),
|
|
185
|
+
reason: PENDING_STAGE_ASK_REFUSAL,
|
|
186
|
+
});
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
const legacyTarget = parseLegacyWorkflowStageTarget(trimmedTo);
|
|
190
|
+
if (legacyTarget !== undefined) {
|
|
191
|
+
write(socket, {
|
|
192
|
+
type: "delivery_failed",
|
|
193
|
+
messageId: message.id,
|
|
194
|
+
...(attemptId ? { attemptId } : {}),
|
|
195
|
+
reason: legacyWorkflowStageTargetMigrationHint(
|
|
196
|
+
resolveLegacyWorkflowStageTarget?.(legacyTarget.runId, legacyTarget.stageKey),
|
|
197
|
+
),
|
|
198
|
+
});
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
143
201
|
|
|
144
202
|
|
|
145
203
|
// Exact-id targeting always resolves against the full pool so a cross-group id
|
|
@@ -159,16 +217,65 @@ export function handleBrokerSend(
|
|
|
159
217
|
if (resolution.kind === "resolved") {
|
|
160
218
|
const target = sessions.get(resolution.session.id);
|
|
161
219
|
if (!target) {
|
|
162
|
-
write(socket, {
|
|
220
|
+
write(socket, {
|
|
221
|
+
type: "delivery_failed",
|
|
222
|
+
messageId: message.id,
|
|
223
|
+
attemptId,
|
|
224
|
+
reason: "Session not found",
|
|
225
|
+
...(requirePendingReply ? { reasonCode: "session_not_found" as const } : {}),
|
|
226
|
+
});
|
|
163
227
|
return;
|
|
164
228
|
}
|
|
165
229
|
if (target.info.id === fromSession.info.id) {
|
|
166
230
|
write(socket, { type: "delivery_failed", messageId: message.id, attemptId, reason: "Cannot message the current session" });
|
|
167
231
|
return;
|
|
168
232
|
}
|
|
233
|
+
const targetIdentity = deliveryTargetIdentity(target);
|
|
234
|
+
const deliveredMatch = deliveredMessages.lookupForTarget(message.id, signature, targetIdentity);
|
|
235
|
+
if (requirePendingReply && deliveredMatch === "match") {
|
|
236
|
+
write(socket, { type: "delivered", messageId: message.id, attemptId });
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
if (requirePendingReply && deliveredMatch === "conflict") {
|
|
240
|
+
write(socket, {
|
|
241
|
+
type: "delivery_failed",
|
|
242
|
+
messageId: message.id,
|
|
243
|
+
attemptId,
|
|
244
|
+
reason: `Intercom message ID '${message.id}' was already delivered with a different target or payload`,
|
|
245
|
+
reasonCode: "message_id_conflict",
|
|
246
|
+
});
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
if (requirePendingReply && deliveredMatch === "invalid") {
|
|
250
|
+
write(socket, {
|
|
251
|
+
type: "delivery_failed",
|
|
252
|
+
messageId: message.id,
|
|
253
|
+
attemptId,
|
|
254
|
+
reason: "Intercom accepted-delivery authority is invalid; refusing possible duplicate delivery",
|
|
255
|
+
});
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
if (requirePendingReply && deliveredMatch === "uncertain") {
|
|
259
|
+
write(socket, {
|
|
260
|
+
type: "delivery_failed",
|
|
261
|
+
messageId: message.id,
|
|
262
|
+
attemptId,
|
|
263
|
+
reason: "Intercom cannot prove whether this reserved operation reached its recipient; refusing redelivery",
|
|
264
|
+
});
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
169
267
|
const correlatedReply =
|
|
170
268
|
message.replyTo !== undefined &&
|
|
171
269
|
pendingQuestions.matchesReply(fromSession.info.id, target.info.id, message.replyTo);
|
|
270
|
+
if (requirePendingReply && !correlatedReply) {
|
|
271
|
+
write(socket, {
|
|
272
|
+
type: "delivery_failed",
|
|
273
|
+
messageId: message.id,
|
|
274
|
+
attemptId,
|
|
275
|
+
reason: "Pending question route does not authorize this public reply",
|
|
276
|
+
});
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
172
279
|
const bypass =
|
|
173
280
|
supervisorSend ||
|
|
174
281
|
isVerticalBypass({
|
|
@@ -188,14 +295,62 @@ export function handleBrokerSend(
|
|
|
188
295
|
});
|
|
189
296
|
return;
|
|
190
297
|
}
|
|
191
|
-
|
|
298
|
+
if (deliveredMatch === "match") {
|
|
299
|
+
if (message.expectsReply === true) {
|
|
300
|
+
const acceptedQuestionTarget = deliveredMessages.lookupQuestionTarget(
|
|
301
|
+
message.id,
|
|
302
|
+
signature,
|
|
303
|
+
senderGroupIdentity(fromSession),
|
|
304
|
+
);
|
|
305
|
+
if (acceptedQuestionTarget === undefined) {
|
|
306
|
+
write(socket, {
|
|
307
|
+
type: "delivery_failed",
|
|
308
|
+
messageId: message.id,
|
|
309
|
+
attemptId,
|
|
310
|
+
reason: "Accepted Intercom question route could not be securely rebound",
|
|
311
|
+
});
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
pendingQuestions.record(fromSession.info.id, target.info.id, message.id);
|
|
315
|
+
}
|
|
316
|
+
write(socket, { type: "delivered", messageId: message.id, attemptId });
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
if (deliveredMatch === "conflict") {
|
|
320
|
+
write(socket, {
|
|
321
|
+
type: "delivery_failed",
|
|
322
|
+
messageId: message.id,
|
|
323
|
+
attemptId,
|
|
324
|
+
reason: `Intercom message ID '${message.id}' was already delivered with a different target or payload`,
|
|
325
|
+
reasonCode: "message_id_conflict",
|
|
326
|
+
});
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
if (deliveredMatch === "invalid") {
|
|
330
|
+
write(socket, {
|
|
331
|
+
type: "delivery_failed",
|
|
332
|
+
messageId: message.id,
|
|
333
|
+
attemptId,
|
|
334
|
+
reason: "Intercom accepted-delivery authority is invalid; refusing possible duplicate delivery",
|
|
335
|
+
});
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
if (deliveredMatch === "uncertain") {
|
|
339
|
+
write(socket, {
|
|
340
|
+
type: "delivery_failed",
|
|
341
|
+
messageId: message.id,
|
|
342
|
+
attemptId,
|
|
343
|
+
reason: "Intercom cannot prove whether this reserved operation reached its recipient; refusing redelivery",
|
|
344
|
+
});
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
192
347
|
if (
|
|
193
348
|
liveWorkflowTarget !== undefined &&
|
|
194
|
-
|
|
349
|
+
workflowTarget !== undefined &&
|
|
195
350
|
routePendingStage?.({
|
|
196
351
|
socket,
|
|
197
352
|
from: fromSession,
|
|
198
|
-
|
|
353
|
+
target: trimmedTo,
|
|
199
354
|
message,
|
|
200
355
|
...(attemptId ? { attemptId } : {}),
|
|
201
356
|
liveTargetId: target.info.id,
|
|
@@ -204,33 +359,137 @@ export function handleBrokerSend(
|
|
|
204
359
|
) {
|
|
205
360
|
return;
|
|
206
361
|
}
|
|
207
|
-
|
|
208
|
-
? { type: "message", from: fromSession.info, message, channel: "supervisor" }
|
|
209
|
-
: { type: "message", from: fromSession.info, message });
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
362
|
+
const outbound = supervisorSend
|
|
363
|
+
? ({ type: "message", from: fromSession.info, message, channel: "supervisor" } as const)
|
|
364
|
+
: ({ type: "message", from: fromSession.info, message } as const);
|
|
365
|
+
const failDelivery = (): void => {
|
|
366
|
+
if (reservation === "recorded") deliveredMessages.forget(message.id, signature);
|
|
367
|
+
write(socket, {
|
|
368
|
+
type: "delivery_failed",
|
|
369
|
+
messageId: message.id,
|
|
370
|
+
attemptId,
|
|
371
|
+
reason: "Session not found",
|
|
372
|
+
...(requirePendingReply ? { reasonCode: "session_not_found" as const } : {}),
|
|
373
|
+
});
|
|
374
|
+
};
|
|
375
|
+
const reservation = message.expectsReply === true
|
|
376
|
+
? deliveredMessages.reserveQuestion(
|
|
377
|
+
message.id,
|
|
378
|
+
signature,
|
|
379
|
+
{
|
|
380
|
+
targetSessionId: target.info.id,
|
|
381
|
+
senderGroupIdentity: senderGroupIdentity(fromSession),
|
|
382
|
+
},
|
|
383
|
+
Date.now(),
|
|
384
|
+
targetIdentity,
|
|
385
|
+
)
|
|
386
|
+
: deliveredMessages.reserve(message.id, signature, Date.now(), targetIdentity);
|
|
387
|
+
if (reservation === "match") {
|
|
388
|
+
if (
|
|
389
|
+
message.expectsReply === true &&
|
|
390
|
+
deliveredMessages.lookupQuestionTarget(
|
|
391
|
+
message.id,
|
|
392
|
+
signature,
|
|
393
|
+
senderGroupIdentity(fromSession),
|
|
394
|
+
) === undefined
|
|
395
|
+
) {
|
|
396
|
+
write(socket, {
|
|
397
|
+
type: "delivery_failed",
|
|
398
|
+
messageId: message.id,
|
|
399
|
+
attemptId,
|
|
400
|
+
reason: "Accepted Intercom question route could not be securely rebound",
|
|
401
|
+
});
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
if (message.expectsReply === true) pendingQuestions.record(fromSession.info.id, target.info.id, message.id);
|
|
405
|
+
write(socket, { type: "delivered", messageId: message.id, attemptId });
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
if (reservation !== "recorded") {
|
|
409
|
+
write(socket, {
|
|
410
|
+
type: "delivery_failed",
|
|
411
|
+
messageId: message.id,
|
|
412
|
+
attemptId,
|
|
413
|
+
reason: reservation === "capacity"
|
|
414
|
+
? "Intercom accepted-delivery capacity is full; refusing delivery without evicting live retry authority"
|
|
415
|
+
: reservation === "uncertain"
|
|
416
|
+
? "Intercom cannot prove whether this reserved operation reached its recipient; refusing redelivery"
|
|
417
|
+
: "Intercom accepted-delivery authority changed before forwarding; refusing possible duplicate delivery",
|
|
418
|
+
...(reservation === "conflict" ? { reasonCode: "message_id_conflict" as const } : {}),
|
|
419
|
+
});
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
const finishDelivery = (): void => {
|
|
423
|
+
let accepted: ReturnType<DeliveredMessageCache["accept"]>;
|
|
424
|
+
try {
|
|
425
|
+
accepted = deliveredMessages.accept(message.id, signature, targetIdentity);
|
|
426
|
+
} catch {
|
|
427
|
+
write(socket, {
|
|
428
|
+
type: "delivery_failed",
|
|
429
|
+
messageId: message.id,
|
|
430
|
+
attemptId,
|
|
431
|
+
reason: "Intercom could not durably confirm the forwarded operation; refusing a possibly unsafe retry",
|
|
432
|
+
});
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
if (accepted !== "accepted") {
|
|
436
|
+
write(socket, {
|
|
437
|
+
type: "delivery_failed",
|
|
438
|
+
messageId: message.id,
|
|
439
|
+
attemptId,
|
|
440
|
+
reason: "Intercom could not durably confirm the forwarded operation; refusing a possibly unsafe retry",
|
|
441
|
+
...(accepted === "conflict" ? { reasonCode: "message_id_conflict" as const } : {}),
|
|
442
|
+
});
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
if (message.expectsReply === true) {
|
|
446
|
+
pendingQuestions.record(fromSession.info.id, target.info.id, message.id);
|
|
447
|
+
}
|
|
448
|
+
if (message.replyTo !== undefined) {
|
|
449
|
+
pendingQuestions.clearReply(fromSession.info.id, target.info.id, message.replyTo);
|
|
450
|
+
}
|
|
451
|
+
if (supervisorSend) supervisorCache.record(message.id, fromSession.info.id, target.info.id);
|
|
452
|
+
write(socket, { type: "delivered", messageId: message.id, attemptId });
|
|
453
|
+
};
|
|
454
|
+
if (writeConfirmed !== undefined) {
|
|
455
|
+
writeConfirmed(target.socket, outbound, (written) => {
|
|
456
|
+
if (written) finishDelivery();
|
|
457
|
+
else failDelivery();
|
|
458
|
+
});
|
|
459
|
+
return;
|
|
216
460
|
}
|
|
217
|
-
if (
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
if (resolution.kind === "not_found" && !supervisorSend && routePendingStage !== undefined) {
|
|
222
|
-
const pendingTarget = parsePendingStageTarget(trimmedTo);
|
|
223
|
-
if (
|
|
224
|
-
pendingTarget !== undefined &&
|
|
225
|
-
routePendingStage({ socket, from: fromSession, ...pendingTarget, message, ...(attemptId ? { attemptId } : {}) })
|
|
226
|
-
) {
|
|
461
|
+
if (!write(target.socket, outbound)) {
|
|
462
|
+
// Nothing was written, so the message id and reply authorization remain
|
|
463
|
+
// available for an honest retry.
|
|
464
|
+
failDelivery();
|
|
227
465
|
return;
|
|
228
466
|
}
|
|
467
|
+
finishDelivery();
|
|
468
|
+
return;
|
|
229
469
|
}
|
|
470
|
+
if (
|
|
471
|
+
resolution.kind === "not_found" &&
|
|
472
|
+
!supervisorSend &&
|
|
473
|
+
routePendingStage !== undefined &&
|
|
474
|
+
workflowTarget !== undefined &&
|
|
475
|
+
routePendingStage({
|
|
476
|
+
socket,
|
|
477
|
+
from: fromSession,
|
|
478
|
+
target: trimmedTo,
|
|
479
|
+
message,
|
|
480
|
+
...(attemptId ? { attemptId } : {}),
|
|
481
|
+
signature,
|
|
482
|
+
})
|
|
483
|
+
) {
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
230
486
|
write(socket, {
|
|
231
487
|
type: "delivery_failed",
|
|
232
488
|
messageId: message.id,
|
|
233
489
|
attemptId,
|
|
234
490
|
reason: sessionTargetFailureReason(clientMessage.to, resolution),
|
|
491
|
+
...(requirePendingReply && resolution.kind === "not_found"
|
|
492
|
+
? { reasonCode: "session_not_found" as const }
|
|
493
|
+
: {}),
|
|
235
494
|
});
|
|
236
495
|
}
|
|
@@ -8,7 +8,9 @@ export interface LogicalSendOptions {
|
|
|
8
8
|
replyError?: string;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
function
|
|
11
|
+
export function canonicalizeAttachmentsForSendSignature(
|
|
12
|
+
attachments: readonly Attachment[] | undefined,
|
|
13
|
+
): Array<Record<string, string>> | undefined {
|
|
12
14
|
return attachments?.map((attachment) => ({
|
|
13
15
|
type: attachment.type,
|
|
14
16
|
name: attachment.name,
|
|
@@ -22,7 +24,7 @@ export function buildSendSignature(to: string, options: LogicalSendOptions): str
|
|
|
22
24
|
return JSON.stringify({
|
|
23
25
|
to,
|
|
24
26
|
text: options.text,
|
|
25
|
-
attachments:
|
|
27
|
+
attachments: canonicalizeAttachmentsForSendSignature(options.attachments) ?? [],
|
|
26
28
|
replyTo: options.replyTo ?? null,
|
|
27
29
|
expectsReply: options.expectsReply ?? false,
|
|
28
30
|
replyError: options.replyError ?? null,
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type net from "node:net";
|
|
2
|
+
import type { BrokerMessage } from "../types.js";
|
|
3
|
+
import { writeMessage } from "./framing.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Broker-side socket writes.
|
|
7
|
+
*
|
|
8
|
+
* `socket.write()` on a socket whose writable side has already ended does not
|
|
9
|
+
* throw: it returns `false`, hands `ERR_STREAM_WRITE_AFTER_END` to the write
|
|
10
|
+
* callback, **destroys the socket synchronously**, and only then emits
|
|
11
|
+
* `'error'`. A caller-side `try`/`catch` is therefore dead code, and a
|
|
12
|
+
* writability snapshot taken anywhere other than immediately before the write
|
|
13
|
+
* can go stale — the broker stores sender sockets in its pending-acknowledgment
|
|
14
|
+
* maps and writes to them up to ten seconds later.
|
|
15
|
+
*
|
|
16
|
+
* So the check lives *inside* the write. Every broker write goes through
|
|
17
|
+
* `writeMessageIfOpen`, which reports whether the frame was actually handed to
|
|
18
|
+
* the socket. Callers whose next step claims a delivery — recording it in the
|
|
19
|
+
* delivered-message cache, opening a reply authorization, answering
|
|
20
|
+
* `delivered` — must branch on that answer instead of assuming it.
|
|
21
|
+
*
|
|
22
|
+
* `destroyed || writableEnded` is the whole test, matching the pre-existing
|
|
23
|
+
* guards in `broker.ts`. `!socket.writable` is deliberately not part of it: it
|
|
24
|
+
* is `undefined` on the socket doubles the broker unit suites inject, so adding
|
|
25
|
+
* it would classify every fake as dead. Failing open on an unknown shape is the
|
|
26
|
+
* safe direction here — a real ended socket always sets one of the two flags.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/** True while `socket` can still accept a frame. */
|
|
30
|
+
export function isSocketOpenForWrite(socket: net.Socket): boolean {
|
|
31
|
+
return !socket.destroyed && !socket.writableEnded;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Frames the broker writes to a client socket.
|
|
36
|
+
*
|
|
37
|
+
* `BrokerMessage` is the client-validated union. Two frames the broker already
|
|
38
|
+
* emits are absent from it — `sticky_live_delivered`, and `queued` carrying
|
|
39
|
+
* `notInKnownSet` — because `writeMessage` accepts `unknown` and never checked
|
|
40
|
+
* them. Widening the shared union is a separate change, so they are named here
|
|
41
|
+
* rather than typed away with a cast.
|
|
42
|
+
*/
|
|
43
|
+
export type BrokerOutboundFrame =
|
|
44
|
+
| BrokerMessage
|
|
45
|
+
| {
|
|
46
|
+
type: "sticky_live_delivered";
|
|
47
|
+
runId: string;
|
|
48
|
+
messageId: string;
|
|
49
|
+
target: string;
|
|
50
|
+
deliveredTargets: string[];
|
|
51
|
+
}
|
|
52
|
+
| {
|
|
53
|
+
type: "queued";
|
|
54
|
+
messageId: string;
|
|
55
|
+
attemptId?: string;
|
|
56
|
+
target: string;
|
|
57
|
+
position: number;
|
|
58
|
+
notInKnownSet?: true;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Write one broker frame unless the socket's writable side is already gone.
|
|
63
|
+
*
|
|
64
|
+
* Returns `true` only when the frame was handed to the socket. A `false` answer
|
|
65
|
+
* means nothing was written and nothing may be recorded as delivered; it never
|
|
66
|
+
* means the peer acknowledged anything. The converse under-approximates on
|
|
67
|
+
* purpose: a `true` answer is not proof of receipt either, which is why the
|
|
68
|
+
* sender-side send timeout remains the terminal authority.
|
|
69
|
+
*/
|
|
70
|
+
export function writeMessageIfOpen(socket: net.Socket, message: BrokerOutboundFrame): boolean {
|
|
71
|
+
if (!isSocketOpenForWrite(socket)) return false;
|
|
72
|
+
writeMessage(socket, message);
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Write one broker frame and report the socket write callback's outcome.
|
|
78
|
+
*
|
|
79
|
+
* Delivery-producing paths use this instead of treating `socket.write()`'s
|
|
80
|
+
* synchronous return as success. That return only reports backpressure; an
|
|
81
|
+
* immediate peer reset is reported asynchronously to the callback.
|
|
82
|
+
*/
|
|
83
|
+
export function writeMessageWithOutcome(
|
|
84
|
+
socket: net.Socket,
|
|
85
|
+
message: BrokerOutboundFrame,
|
|
86
|
+
onSettled: (written: boolean) => void,
|
|
87
|
+
): void {
|
|
88
|
+
if (!isSocketOpenForWrite(socket)) {
|
|
89
|
+
onSettled(false);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
let settled = false;
|
|
93
|
+
const finish = (written: boolean): void => {
|
|
94
|
+
if (settled) return;
|
|
95
|
+
settled = true;
|
|
96
|
+
onSettled(written);
|
|
97
|
+
};
|
|
98
|
+
try {
|
|
99
|
+
writeMessage(socket, message, (error) => finish(error == null));
|
|
100
|
+
} catch {
|
|
101
|
+
finish(false);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawn } from "child_process";
|
|
2
|
-
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, statSync, unlinkSync, writeFileSync } from "fs";
|
|
2
|
+
import { chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, statSync, unlinkSync, writeFileSync } from "fs";
|
|
3
3
|
import { join, dirname } from "path";
|
|
4
4
|
import { fileURLToPath } from "url";
|
|
5
5
|
import { createRequire } from "module";
|
|
@@ -37,6 +37,11 @@ const BROKER_PID = getBrokerPidPath();
|
|
|
37
37
|
const BROKER_SPAWN_LOCK = getBrokerSpawnLockPath();
|
|
38
38
|
const BROKER_LOG = getBrokerLogPath();
|
|
39
39
|
|
|
40
|
+
function ensurePrivateDirectory(path: string): void {
|
|
41
|
+
mkdirSync(path, { recursive: true, mode: 0o700 });
|
|
42
|
+
if (process.platform !== "win32") chmodSync(path, 0o700);
|
|
43
|
+
}
|
|
44
|
+
|
|
40
45
|
/**
|
|
41
46
|
* How much of the broker startup log is read back into an error message.
|
|
42
47
|
*
|
|
@@ -182,7 +187,7 @@ function writeWindowsHiddenLauncher(
|
|
|
182
187
|
commandLine: string,
|
|
183
188
|
launcherPath: string = getWindowsHiddenLauncherPath(),
|
|
184
189
|
): string {
|
|
185
|
-
|
|
190
|
+
ensurePrivateDirectory(dirname(launcherPath));
|
|
186
191
|
writeFileSync(launcherPath, getWindowsHiddenLauncherScript(commandLine), "utf-8");
|
|
187
192
|
return launcherPath;
|
|
188
193
|
}
|
|
@@ -254,7 +259,7 @@ export function getBrokerSpawnOptions(
|
|
|
254
259
|
|
|
255
260
|
/** Truncate (or create) the broker log so each spawn starts from a bounded, current file. */
|
|
256
261
|
function resetBrokerLog(logPath: string = BROKER_LOG): void {
|
|
257
|
-
|
|
262
|
+
ensurePrivateDirectory(dirname(logPath));
|
|
258
263
|
closeSync(openSync(logPath, "w"));
|
|
259
264
|
}
|
|
260
265
|
|
|
@@ -295,7 +300,7 @@ function toError(error: unknown): Error {
|
|
|
295
300
|
}
|
|
296
301
|
|
|
297
302
|
export async function spawnBrokerIfNeeded(brokerCommand: string, brokerArgs: string[]): Promise<void> {
|
|
298
|
-
|
|
303
|
+
ensurePrivateDirectory(INTERCOM_DIR);
|
|
299
304
|
|
|
300
305
|
if (await isBrokerRunning()) {
|
|
301
306
|
return;
|