@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
|
@@ -68,13 +68,49 @@ var renderContactSupervisorResult = (result, { isPartial }, theme, context) => {
|
|
|
68
68
|
};
|
|
69
69
|
var init_result_renderers = () => {};
|
|
70
70
|
|
|
71
|
+
// dist/builtin/intercom/recoverable-disconnect.ts
|
|
72
|
+
function isRecoverableIntercomDisconnect(error) {
|
|
73
|
+
let current = error;
|
|
74
|
+
for (let depth = 0;depth <= MAX_CAUSE_DEPTH; depth += 1) {
|
|
75
|
+
if (!(current instanceof Error))
|
|
76
|
+
return false;
|
|
77
|
+
if (current instanceof IntercomClientDisconnectedError)
|
|
78
|
+
return true;
|
|
79
|
+
current = current.cause;
|
|
80
|
+
}
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
var RECOVERABLE_DISCONNECT_MESSAGE = "Client disconnected. Retry the same Intercom call up to three times to reconnect.", MAX_CAUSE_DEPTH = 8, IntercomClientDisconnectedError;
|
|
84
|
+
var init_recoverable_disconnect = __esm(() => {
|
|
85
|
+
IntercomClientDisconnectedError = class IntercomClientDisconnectedError extends Error {
|
|
86
|
+
intercomRecoverableDisconnect = true;
|
|
87
|
+
constructor(options) {
|
|
88
|
+
super(RECOVERABLE_DISCONNECT_MESSAGE, options);
|
|
89
|
+
this.name = "IntercomClientDisconnectedError";
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// dist/builtin/intercom/reconnect-backoff.ts
|
|
95
|
+
function reconnectDelayMs(attempt) {
|
|
96
|
+
return BACKOFF_MS[Math.min(attempt, BACKOFF_MS.length - 1)];
|
|
97
|
+
}
|
|
98
|
+
var BACKOFF_MS;
|
|
99
|
+
var init_reconnect_backoff = __esm(() => {
|
|
100
|
+
BACKOFF_MS = [1000, 2000, 5000, 1e4, 30000];
|
|
101
|
+
});
|
|
102
|
+
|
|
71
103
|
// dist/builtin/intercom/broker/framing.ts
|
|
72
|
-
function writeMessage(socket, msg) {
|
|
104
|
+
function writeMessage(socket, msg, callback) {
|
|
73
105
|
const json = JSON.stringify(msg);
|
|
74
106
|
const payload = Buffer.from(json, "utf-8");
|
|
75
107
|
const header = Buffer.alloc(4);
|
|
76
108
|
header.writeUInt32BE(payload.length, 0);
|
|
77
|
-
|
|
109
|
+
const frame = Buffer.concat([header, payload]);
|
|
110
|
+
if (callback === undefined)
|
|
111
|
+
socket.write(frame);
|
|
112
|
+
else
|
|
113
|
+
socket.write(frame, callback);
|
|
78
114
|
}
|
|
79
115
|
function createMessageReader(onMessage, onError) {
|
|
80
116
|
let buffer = Buffer.alloc(0);
|
|
@@ -162,7 +198,7 @@ function getBrokerSocketPath(platform = process.platform, agentDir = getAgentDir
|
|
|
162
198
|
var init_paths = () => {};
|
|
163
199
|
|
|
164
200
|
// dist/builtin/intercom/broker/send-signature.ts
|
|
165
|
-
function
|
|
201
|
+
function canonicalizeAttachmentsForSendSignature(attachments) {
|
|
166
202
|
return attachments?.map((attachment) => ({
|
|
167
203
|
type: attachment.type,
|
|
168
204
|
name: attachment.name,
|
|
@@ -174,7 +210,7 @@ function buildSendSignature(to, options) {
|
|
|
174
210
|
return JSON.stringify({
|
|
175
211
|
to,
|
|
176
212
|
text: options.text,
|
|
177
|
-
attachments:
|
|
213
|
+
attachments: canonicalizeAttachmentsForSendSignature(options.attachments) ?? [],
|
|
178
214
|
replyTo: options.replyTo ?? null,
|
|
179
215
|
expectsReply: options.expectsReply ?? false,
|
|
180
216
|
replyError: options.replyError ?? null
|
|
@@ -416,6 +452,9 @@ import { randomUUID as randomUUID2 } from "crypto";
|
|
|
416
452
|
function toError(error) {
|
|
417
453
|
return error instanceof Error ? error : new Error(String(error));
|
|
418
454
|
}
|
|
455
|
+
function isWorkflowFutureStageRosterEntries(value) {
|
|
456
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "object" && entry !== null && entry.kind === "workflow-future-stage" && typeof entry.runId === "string" && typeof entry.target === "string" && typeof entry.queuedCount === "number" && Number.isInteger(entry.queuedCount) && entry.queuedCount >= 0 && typeof entry.group === "string");
|
|
457
|
+
}
|
|
419
458
|
function isWorkflowStageRosterEntries(value) {
|
|
420
459
|
return Array.isArray(value) && value.every((entry) => typeof entry === "object" && entry !== null && entry.kind === "workflow-stage" && typeof entry.runId === "string" && typeof entry.stageId === "string" && typeof entry.stageName === "string" && typeof entry.target === "string" && (entry.lifecycle === "pending" || entry.lifecycle === "running") && typeof entry.group === "string" && (entry.sessionId === undefined || typeof entry.sessionId === "string"));
|
|
421
460
|
}
|
|
@@ -424,6 +463,7 @@ var init_client = __esm(() => {
|
|
|
424
463
|
init_paths();
|
|
425
464
|
init_pending_send_registry();
|
|
426
465
|
init_group();
|
|
466
|
+
init_recoverable_disconnect();
|
|
427
467
|
BROKER_SOCKET = getBrokerSocketPath();
|
|
428
468
|
IntercomClient = class IntercomClient extends EventEmitter {
|
|
429
469
|
returnAddress;
|
|
@@ -490,11 +530,11 @@ var init_client = __esm(() => {
|
|
|
490
530
|
throw new Error("Not connected");
|
|
491
531
|
}
|
|
492
532
|
if (socket.destroyed || socket.writableEnded || !socket.writable) {
|
|
493
|
-
throw new
|
|
533
|
+
throw new IntercomClientDisconnectedError;
|
|
494
534
|
}
|
|
495
535
|
return socket;
|
|
496
536
|
}
|
|
497
|
-
connect(session, supervisor, supervisorOwnerToken, messageSource) {
|
|
537
|
+
connect(session, supervisor, supervisorOwnerToken, messageSource, registrationGroup) {
|
|
498
538
|
if (this.socket) {
|
|
499
539
|
return Promise.reject(new Error("Already connected"));
|
|
500
540
|
}
|
|
@@ -537,7 +577,7 @@ var init_client = __esm(() => {
|
|
|
537
577
|
const onClose = () => {
|
|
538
578
|
const wasConnecting = !settled && !this._sessionId;
|
|
539
579
|
const wasDisconnecting = this.disconnecting;
|
|
540
|
-
const disconnectError = this.disconnectError ?? new
|
|
580
|
+
const disconnectError = this.disconnectError ?? new IntercomClientDisconnectedError;
|
|
541
581
|
this.disconnecting = false;
|
|
542
582
|
cleanupConnectionAttempt();
|
|
543
583
|
cleanupSocketListeners();
|
|
@@ -558,7 +598,7 @@ var init_client = __esm(() => {
|
|
|
558
598
|
};
|
|
559
599
|
const onSocketError = (err) => {
|
|
560
600
|
if (connectionEstablished) {
|
|
561
|
-
this.disconnectError
|
|
601
|
+
this.disconnectError ??= new IntercomClientDisconnectedError({ cause: err });
|
|
562
602
|
this.emit("error", err);
|
|
563
603
|
}
|
|
564
604
|
};
|
|
@@ -596,6 +636,7 @@ var init_client = __esm(() => {
|
|
|
596
636
|
writeMessage(socket, {
|
|
597
637
|
type: "register",
|
|
598
638
|
session,
|
|
639
|
+
...registrationGroup !== undefined ? { registrationGroup } : {},
|
|
599
640
|
returnAddress: this.returnAddress,
|
|
600
641
|
...supervisor ? { supervisor } : {},
|
|
601
642
|
...supervisorOwnerToken ? { supervisorOwnerToken } : {}
|
|
@@ -639,15 +680,19 @@ var init_client = __esm(() => {
|
|
|
639
680
|
break;
|
|
640
681
|
}
|
|
641
682
|
case "sessions": {
|
|
642
|
-
const { requestId, sessions, workflowStages } = brokerMessage;
|
|
643
|
-
if (typeof requestId !== "string" || !Array.isArray(sessions) || !sessions.every(isSessionInfo) || workflowStages !== undefined && !isWorkflowStageRosterEntries(workflowStages)) {
|
|
683
|
+
const { requestId, sessions, workflowStages, workflowFutureStages } = brokerMessage;
|
|
684
|
+
if (typeof requestId !== "string" || !Array.isArray(sessions) || !sessions.every(isSessionInfo) || workflowStages !== undefined && !isWorkflowStageRosterEntries(workflowStages) || workflowFutureStages !== undefined && !isWorkflowFutureStageRosterEntries(workflowFutureStages)) {
|
|
644
685
|
throw new Error("Invalid sessions message");
|
|
645
686
|
}
|
|
646
687
|
const pending = this.pendingLists.get(requestId);
|
|
647
688
|
if (!pending)
|
|
648
689
|
return;
|
|
649
690
|
this.pendingLists.delete(requestId);
|
|
650
|
-
pending.resolve({
|
|
691
|
+
pending.resolve({
|
|
692
|
+
sessions,
|
|
693
|
+
workflowStages: workflowStages ?? [],
|
|
694
|
+
workflowFutureStages: workflowFutureStages ?? []
|
|
695
|
+
});
|
|
651
696
|
break;
|
|
652
697
|
}
|
|
653
698
|
case "groups": {
|
|
@@ -683,8 +728,8 @@ var init_client = __esm(() => {
|
|
|
683
728
|
break;
|
|
684
729
|
}
|
|
685
730
|
case "pending_stage_message": {
|
|
686
|
-
const { requestId, from, senderRegistrationName, senderReturnAddress, runId,
|
|
687
|
-
if (typeof requestId !== "string" || !isSessionInfo(from) || senderRegistrationName !== undefined && typeof senderRegistrationName !== "string" || senderReturnAddress !== undefined && typeof senderReturnAddress !== "string" || typeof runId !== "string" || typeof
|
|
731
|
+
const { requestId, from, senderRegistrationName, senderReturnAddress, runId, target, message, live } = brokerMessage;
|
|
732
|
+
if (typeof requestId !== "string" || !isSessionInfo(from) || senderRegistrationName !== undefined && typeof senderRegistrationName !== "string" || senderReturnAddress !== undefined && typeof senderReturnAddress !== "string" || typeof runId !== "string" || typeof target !== "string" || !isMessage(message) || live !== undefined && typeof live !== "boolean") {
|
|
688
733
|
throw new Error("Invalid pending-stage message event");
|
|
689
734
|
}
|
|
690
735
|
this.emit("pending_stage_message", {
|
|
@@ -693,7 +738,7 @@ var init_client = __esm(() => {
|
|
|
693
738
|
...senderRegistrationName === undefined ? {} : { senderRegistrationName },
|
|
694
739
|
...senderReturnAddress === undefined ? {} : { senderReturnAddress },
|
|
695
740
|
runId,
|
|
696
|
-
|
|
741
|
+
target,
|
|
697
742
|
message,
|
|
698
743
|
...live === true ? { live: true } : {}
|
|
699
744
|
});
|
|
@@ -711,6 +756,19 @@ var init_client = __esm(() => {
|
|
|
711
756
|
});
|
|
712
757
|
break;
|
|
713
758
|
}
|
|
759
|
+
case "sticky_live_delivered": {
|
|
760
|
+
const { runId, messageId, target, deliveredTargets } = brokerMessage;
|
|
761
|
+
if (typeof runId !== "string" || typeof messageId !== "string" || typeof target !== "string" || !Array.isArray(deliveredTargets) || !deliveredTargets.every((entry) => typeof entry === "string")) {
|
|
762
|
+
throw new Error("Invalid sticky live-delivery event");
|
|
763
|
+
}
|
|
764
|
+
this.emit("sticky_live_delivered", {
|
|
765
|
+
runId,
|
|
766
|
+
messageId,
|
|
767
|
+
target,
|
|
768
|
+
deliveredTargets
|
|
769
|
+
});
|
|
770
|
+
break;
|
|
771
|
+
}
|
|
714
772
|
case "live_workflow_stage_route_registered": {
|
|
715
773
|
const { requestId } = brokerMessage;
|
|
716
774
|
if (typeof requestId !== "string")
|
|
@@ -723,11 +781,18 @@ var init_client = __esm(() => {
|
|
|
723
781
|
break;
|
|
724
782
|
}
|
|
725
783
|
case "queued": {
|
|
726
|
-
const { messageId, attemptId,
|
|
727
|
-
if (typeof messageId !== "string" || attemptId !== undefined && typeof attemptId !== "string" || typeof
|
|
784
|
+
const { messageId, attemptId, target, position, notInKnownSet } = brokerMessage;
|
|
785
|
+
if (typeof messageId !== "string" || attemptId !== undefined && typeof attemptId !== "string" || typeof target !== "string" || typeof position !== "number" || position < 1 || notInKnownSet !== undefined && notInKnownSet !== true) {
|
|
728
786
|
throw new Error("Invalid queued message");
|
|
729
787
|
}
|
|
730
|
-
const result = {
|
|
788
|
+
const result = {
|
|
789
|
+
id: messageId,
|
|
790
|
+
delivered: false,
|
|
791
|
+
queued: true,
|
|
792
|
+
target,
|
|
793
|
+
position,
|
|
794
|
+
...notInKnownSet === true ? { notInKnownSet: true } : {}
|
|
795
|
+
};
|
|
731
796
|
if (attemptId === undefined)
|
|
732
797
|
this.pendingSends.resolveLegacy(messageId, result);
|
|
733
798
|
else
|
|
@@ -748,7 +813,7 @@ var init_client = __esm(() => {
|
|
|
748
813
|
}
|
|
749
814
|
case "delivery_failed": {
|
|
750
815
|
const { messageId, attemptId, reason, reasonCode } = brokerMessage;
|
|
751
|
-
if (typeof messageId !== "string" || attemptId !== undefined && typeof attemptId !== "string" || typeof reason !== "string" || reasonCode !== undefined && reasonCode !== "message_id_conflict") {
|
|
816
|
+
if (typeof messageId !== "string" || attemptId !== undefined && typeof attemptId !== "string" || typeof reason !== "string" || reasonCode !== undefined && reasonCode !== "message_id_conflict" && reasonCode !== "session_not_found") {
|
|
752
817
|
throw new Error("Invalid delivery_failed message");
|
|
753
818
|
}
|
|
754
819
|
const result = {
|
|
@@ -857,7 +922,7 @@ var init_client = __esm(() => {
|
|
|
857
922
|
}
|
|
858
923
|
this.disconnecting = true;
|
|
859
924
|
this.disconnectError = null;
|
|
860
|
-
this.failPending(new
|
|
925
|
+
this.failPending(new IntercomClientDisconnectedError);
|
|
861
926
|
await new Promise((resolve) => {
|
|
862
927
|
let settled = false;
|
|
863
928
|
const finish = () => {
|
|
@@ -1031,8 +1096,15 @@ var init_client = __esm(() => {
|
|
|
1031
1096
|
}
|
|
1032
1097
|
});
|
|
1033
1098
|
}
|
|
1034
|
-
registerPendingStageRoute(runId, group, capability, stages) {
|
|
1035
|
-
writeMessage(this.requireActiveSocket(), {
|
|
1099
|
+
registerPendingStageRoute(runId, group, capability, stages, possibleStages) {
|
|
1100
|
+
writeMessage(this.requireActiveSocket(), {
|
|
1101
|
+
type: "register_pending_stage_route",
|
|
1102
|
+
runId,
|
|
1103
|
+
group,
|
|
1104
|
+
capability,
|
|
1105
|
+
stages,
|
|
1106
|
+
possibleStages
|
|
1107
|
+
});
|
|
1036
1108
|
}
|
|
1037
1109
|
registerLiveWorkflowStageRoute(runId, stageKeys, capability) {
|
|
1038
1110
|
let socket;
|
|
@@ -1060,7 +1132,14 @@ var init_client = __esm(() => {
|
|
|
1060
1132
|
}
|
|
1061
1133
|
respondPendingStageMessage(requestId, result) {
|
|
1062
1134
|
const socket = this.requireActiveSocket();
|
|
1063
|
-
writeMessage(socket, result.outcome === "queued" ? {
|
|
1135
|
+
writeMessage(socket, result.outcome === "queued" ? {
|
|
1136
|
+
type: "pending_stage_message_result",
|
|
1137
|
+
requestId,
|
|
1138
|
+
outcome: "queued",
|
|
1139
|
+
position: result.position,
|
|
1140
|
+
...result.notInKnownSet === true ? { notInKnownSet: true } : {},
|
|
1141
|
+
...result.forwardTargets === undefined ? {} : { forwardTargets: [...result.forwardTargets] }
|
|
1142
|
+
} : result.outcome === "forward" ? { type: "pending_stage_message_result", requestId, outcome: "forward", target: result.target } : result.outcome === "refused" ? {
|
|
1064
1143
|
type: "pending_stage_message_result",
|
|
1065
1144
|
requestId,
|
|
1066
1145
|
outcome: "refused",
|
|
@@ -1095,7 +1174,12 @@ var init_client = __esm(() => {
|
|
|
1095
1174
|
const messageId = options.messageId ?? randomUUID2();
|
|
1096
1175
|
let acquired;
|
|
1097
1176
|
try {
|
|
1098
|
-
|
|
1177
|
+
const pendingSignature = JSON.stringify({
|
|
1178
|
+
transportTarget: to,
|
|
1179
|
+
logicalSignature: buildSendSignature(options.logicalTarget ?? to, options),
|
|
1180
|
+
requirePendingReply: options.requirePendingReply ?? false
|
|
1181
|
+
});
|
|
1182
|
+
acquired = this.pendingSends.acquire(messageId, pendingSignature, 1e4);
|
|
1099
1183
|
} catch (error) {
|
|
1100
1184
|
return Promise.reject(toError(error));
|
|
1101
1185
|
}
|
|
@@ -1111,7 +1195,15 @@ var init_client = __esm(() => {
|
|
|
1111
1195
|
content: { text: options.text, attachments: options.attachments }
|
|
1112
1196
|
};
|
|
1113
1197
|
try {
|
|
1114
|
-
writeMessage(socket, {
|
|
1198
|
+
writeMessage(socket, {
|
|
1199
|
+
type,
|
|
1200
|
+
to,
|
|
1201
|
+
...options.logicalTarget === undefined ? {} : { logicalTarget: options.logicalTarget },
|
|
1202
|
+
...options.requirePendingReply === undefined ? {} : { requirePendingReply: options.requirePendingReply },
|
|
1203
|
+
message,
|
|
1204
|
+
attemptId: acquired.attempt.attemptId,
|
|
1205
|
+
...extra
|
|
1206
|
+
});
|
|
1115
1207
|
} catch (error) {
|
|
1116
1208
|
this.pendingSends.reject(acquired.attempt, toError(error));
|
|
1117
1209
|
}
|
|
@@ -1173,7 +1265,7 @@ var init_client = __esm(() => {
|
|
|
1173
1265
|
|
|
1174
1266
|
// dist/builtin/intercom/broker/spawn.ts
|
|
1175
1267
|
import { spawn } from "child_process";
|
|
1176
|
-
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, statSync, unlinkSync, writeFileSync } from "fs";
|
|
1268
|
+
import { chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, statSync, unlinkSync, writeFileSync } from "fs";
|
|
1177
1269
|
import { join as join2, dirname } from "path";
|
|
1178
1270
|
import { fileURLToPath } from "url";
|
|
1179
1271
|
import { createRequire } from "module";
|
|
@@ -1192,6 +1284,11 @@ function resolveBrokerEntrypoint(packageRoot) {
|
|
|
1192
1284
|
const bundled = join2(packageRoot, "broker", "broker.bundle.mjs");
|
|
1193
1285
|
return existsSync(bundled) ? bundled : join2(packageRoot, "broker", "broker.ts");
|
|
1194
1286
|
}
|
|
1287
|
+
function ensurePrivateDirectory(path) {
|
|
1288
|
+
mkdirSync(path, { recursive: true, mode: 448 });
|
|
1289
|
+
if (process.platform !== "win32")
|
|
1290
|
+
chmodSync(path, 448);
|
|
1291
|
+
}
|
|
1195
1292
|
function sleep(ms) {
|
|
1196
1293
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1197
1294
|
}
|
|
@@ -1257,7 +1354,7 @@ function getWindowsHiddenLauncherScript(commandLine) {
|
|
|
1257
1354
|
`);
|
|
1258
1355
|
}
|
|
1259
1356
|
function writeWindowsHiddenLauncher(commandLine, launcherPath = getWindowsHiddenLauncherPath()) {
|
|
1260
|
-
|
|
1357
|
+
ensurePrivateDirectory(dirname(launcherPath));
|
|
1261
1358
|
writeFileSync(launcherPath, getWindowsHiddenLauncherScript(commandLine), "utf-8");
|
|
1262
1359
|
return launcherPath;
|
|
1263
1360
|
}
|
|
@@ -1296,7 +1393,7 @@ function getBrokerSpawnOptions(extensionDir = EXTENSION_DIR, stderr = "ignore")
|
|
|
1296
1393
|
};
|
|
1297
1394
|
}
|
|
1298
1395
|
function resetBrokerLog(logPath = BROKER_LOG) {
|
|
1299
|
-
|
|
1396
|
+
ensurePrivateDirectory(dirname(logPath));
|
|
1300
1397
|
closeSync(openSync(logPath, "w"));
|
|
1301
1398
|
}
|
|
1302
1399
|
function readBrokerLogTail(logPath = BROKER_LOG, maxBytes = BROKER_LOG_TAIL_BYTES) {
|
|
@@ -1332,7 +1429,7 @@ function toError2(error) {
|
|
|
1332
1429
|
return error instanceof Error ? error : new Error(String(error));
|
|
1333
1430
|
}
|
|
1334
1431
|
async function spawnBrokerIfNeeded(brokerCommand, brokerArgs) {
|
|
1335
|
-
|
|
1432
|
+
ensurePrivateDirectory(INTERCOM_DIR);
|
|
1336
1433
|
if (await isBrokerRunning()) {
|
|
1337
1434
|
return;
|
|
1338
1435
|
}
|
|
@@ -1579,6 +1676,15 @@ class InlineMessageComponent {
|
|
|
1579
1676
|
}
|
|
1580
1677
|
var init_inline_message = () => {};
|
|
1581
1678
|
|
|
1679
|
+
// dist/builtin/intercom/retry-policy.ts
|
|
1680
|
+
var ASK_REPLY_TIMEOUT_MS, RETRY_IDENTITY_RETRY_OPPORTUNITY_MS, RETRY_IDENTITY_TTL_MS, DELIVERED_MESSAGE_TTL_MS;
|
|
1681
|
+
var init_retry_policy = __esm(() => {
|
|
1682
|
+
ASK_REPLY_TIMEOUT_MS = 10 * 60 * 1000;
|
|
1683
|
+
RETRY_IDENTITY_RETRY_OPPORTUNITY_MS = 60 * 1000;
|
|
1684
|
+
RETRY_IDENTITY_TTL_MS = ASK_REPLY_TIMEOUT_MS + RETRY_IDENTITY_RETRY_OPPORTUNITY_MS;
|
|
1685
|
+
DELIVERED_MESSAGE_TTL_MS = RETRY_IDENTITY_TTL_MS + RETRY_IDENTITY_RETRY_OPPORTUNITY_MS;
|
|
1686
|
+
});
|
|
1687
|
+
|
|
1582
1688
|
// dist/builtin/intercom/reply-waiter.ts
|
|
1583
1689
|
class ReplyWaiterRegistry {
|
|
1584
1690
|
timeoutMs;
|
|
@@ -1657,7 +1763,8 @@ class ReplyWaiterRegistry {
|
|
|
1657
1763
|
}
|
|
1658
1764
|
var DEFAULT_REPLY_TIMEOUT_MS, DEFAULT_MAX_PENDING_REPLY_WAITS = 6;
|
|
1659
1765
|
var init_reply_waiter = __esm(() => {
|
|
1660
|
-
|
|
1766
|
+
init_retry_policy();
|
|
1767
|
+
DEFAULT_REPLY_TIMEOUT_MS = ASK_REPLY_TIMEOUT_MS;
|
|
1661
1768
|
});
|
|
1662
1769
|
|
|
1663
1770
|
// dist/builtin/intercom/config.ts
|
|
@@ -1786,7 +1893,7 @@ class ReplyTracker {
|
|
|
1786
1893
|
pendingAsks = new Map;
|
|
1787
1894
|
pendingTurnContexts = [];
|
|
1788
1895
|
currentTurnContext = null;
|
|
1789
|
-
constructor(askTimeoutMs =
|
|
1896
|
+
constructor(askTimeoutMs = ASK_REPLY_TIMEOUT_MS) {
|
|
1790
1897
|
this.askTimeoutMs = askTimeoutMs;
|
|
1791
1898
|
}
|
|
1792
1899
|
recordIncomingMessage(from, message, receivedAt = Date.now()) {
|
|
@@ -1885,7 +1992,9 @@ class ReplyTracker {
|
|
|
1885
1992
|
}
|
|
1886
1993
|
}
|
|
1887
1994
|
}
|
|
1888
|
-
var init_reply_tracker = () => {
|
|
1995
|
+
var init_reply_tracker = __esm(() => {
|
|
1996
|
+
init_retry_policy();
|
|
1997
|
+
});
|
|
1889
1998
|
|
|
1890
1999
|
// dist/builtin/intercom/parent-ask-handoff.ts
|
|
1891
2000
|
import { isStaleExtensionContextError as isStaleExtensionContextError2 } from "@bastani/atomic";
|
|
@@ -2579,59 +2688,381 @@ var init_contact_supervisor_tool = __esm(() => {
|
|
|
2579
2688
|
init_intercom_utils();
|
|
2580
2689
|
});
|
|
2581
2690
|
|
|
2582
|
-
// dist/builtin/intercom/
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2691
|
+
// dist/builtin/intercom/workflow-stage-target.ts
|
|
2692
|
+
function parseWorkflowStageTarget(target) {
|
|
2693
|
+
if (!target.startsWith(WORKFLOW_TARGET_PREFIX))
|
|
2694
|
+
return;
|
|
2695
|
+
const slash = target.indexOf("/", WORKFLOW_TARGET_PREFIX.length);
|
|
2696
|
+
if (slash < 0)
|
|
2697
|
+
return;
|
|
2698
|
+
const rootRunId = target.slice(WORKFLOW_TARGET_PREFIX.length, slash);
|
|
2699
|
+
if (!WORKFLOW_RUN_ID_PATTERN.test(rootRunId))
|
|
2700
|
+
return;
|
|
2701
|
+
const segments = target.slice(slash + 1).split("/");
|
|
2702
|
+
if (segments.length === 0 || segments.some((segment) => segment.length === 0))
|
|
2703
|
+
return;
|
|
2704
|
+
const kind = segments.includes("**") ? "deep-pattern" : segments.some((segment) => segment.includes("*")) ? "pattern" : "path";
|
|
2705
|
+
return { rootRunId, segments, kind };
|
|
2706
|
+
}
|
|
2707
|
+
function formatWorkflowStageTarget(rootRunId, ...segments) {
|
|
2708
|
+
return `${WORKFLOW_TARGET_PREFIX}${rootRunId}/${segments.join("/")}`;
|
|
2709
|
+
}
|
|
2710
|
+
function withWorkflowStageTargetFinalSegment(target, segment) {
|
|
2711
|
+
const parsed = parseWorkflowStageTarget(target);
|
|
2712
|
+
if (parsed === undefined || segment.length === 0 || segment.includes("/"))
|
|
2713
|
+
return;
|
|
2714
|
+
return formatWorkflowStageTarget(parsed.rootRunId, ...parsed.segments.slice(0, -1), segment);
|
|
2715
|
+
}
|
|
2716
|
+
var WORKFLOW_RUN_ID_PATTERN, WORKFLOW_TARGET_PREFIX = "workflow:";
|
|
2717
|
+
var init_workflow_stage_target = __esm(() => {
|
|
2718
|
+
WORKFLOW_RUN_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
2586
2719
|
});
|
|
2587
2720
|
|
|
2588
|
-
// dist/builtin/intercom/
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2721
|
+
// dist/builtin/intercom/retry-identity.ts
|
|
2722
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
2723
|
+
function operationKey(input) {
|
|
2724
|
+
return JSON.stringify([
|
|
2725
|
+
input.sessionId,
|
|
2726
|
+
input.action,
|
|
2727
|
+
input.target,
|
|
2728
|
+
input.text,
|
|
2729
|
+
canonicalizeAttachmentsForSendSignature(input.attachments),
|
|
2730
|
+
input.replyTo,
|
|
2731
|
+
input.expectsReply
|
|
2732
|
+
]);
|
|
2733
|
+
}
|
|
2592
2734
|
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2735
|
+
class RetryIdentityReservations {
|
|
2736
|
+
tokenReservations = new Map;
|
|
2737
|
+
attemptStates = new WeakMap;
|
|
2738
|
+
ttlMs;
|
|
2739
|
+
maxEntries;
|
|
2740
|
+
maxReuses;
|
|
2741
|
+
now;
|
|
2742
|
+
createId;
|
|
2743
|
+
createToken;
|
|
2744
|
+
freshInFlightCount = 0;
|
|
2745
|
+
constructor(options = {}) {
|
|
2746
|
+
this.ttlMs = options.ttlMs ?? RETRY_IDENTITY_TTL_MS;
|
|
2747
|
+
this.maxEntries = options.maxEntries ?? RETRY_IDENTITY_MAX_ENTRIES;
|
|
2748
|
+
this.maxReuses = options.maxReuses ?? RETRY_IDENTITY_MAX_REUSES;
|
|
2749
|
+
this.now = options.now ?? Date.now;
|
|
2750
|
+
this.createId = options.createId ?? randomUUID4;
|
|
2751
|
+
this.createToken = options.createToken ?? randomUUID4;
|
|
2752
|
+
}
|
|
2753
|
+
begin(input, retryToken) {
|
|
2754
|
+
const now = this.now();
|
|
2755
|
+
const key = operationKey(input);
|
|
2756
|
+
if (retryToken !== undefined)
|
|
2757
|
+
return this.claimRetry(key, retryToken, now);
|
|
2758
|
+
this.prune(now);
|
|
2759
|
+
this.discardSettledTombstonesForCapacity();
|
|
2760
|
+
if (this.tokenReservations.size + this.freshInFlightCount >= this.maxEntries) {
|
|
2761
|
+
throw new RetryIdentityCapacityError;
|
|
2762
|
+
}
|
|
2763
|
+
const reservation = {
|
|
2764
|
+
key,
|
|
2765
|
+
messageId: this.createId(),
|
|
2766
|
+
expiresAt: now + this.ttlMs,
|
|
2767
|
+
sessionId: input.sessionId,
|
|
2768
|
+
action: input.action,
|
|
2769
|
+
reuseCount: 0,
|
|
2770
|
+
state: "fresh-in-flight"
|
|
2771
|
+
};
|
|
2772
|
+
this.freshInFlightCount += 1;
|
|
2773
|
+
return this.attempt(reservation);
|
|
2774
|
+
}
|
|
2775
|
+
validateRetryToken(retryToken, sessionId, action) {
|
|
2776
|
+
const reservation = this.tokenReservation(retryToken, this.now());
|
|
2777
|
+
if (reservation.sessionId !== sessionId || reservation.action !== action) {
|
|
2778
|
+
throw new RetryTokenError("mismatch");
|
|
2779
|
+
}
|
|
2780
|
+
this.requireClaimable(reservation);
|
|
2781
|
+
return Math.max(0, this.maxReuses - reservation.reuseCount);
|
|
2782
|
+
}
|
|
2783
|
+
bindReplyRoute(attempt, route) {
|
|
2784
|
+
const reservation = this.currentReservation(attempt);
|
|
2785
|
+
if (reservation === undefined || reservation.action !== "reply" || reservation.state !== "fresh-in-flight" || reservation.replyRoute !== undefined || route.senderId.length === 0 || route.messageId.length === 0) {
|
|
2786
|
+
throw new RetryTokenError("invalid");
|
|
2787
|
+
}
|
|
2788
|
+
reservation.replyRoute = { ...route };
|
|
2789
|
+
}
|
|
2790
|
+
replyRoute(attempt) {
|
|
2791
|
+
const route = this.currentReservation(attempt)?.replyRoute;
|
|
2792
|
+
return route === undefined ? undefined : { ...route };
|
|
2793
|
+
}
|
|
2794
|
+
retainAfterRecoverableDisconnect(attempt) {
|
|
2795
|
+
return this.retain(attempt, true);
|
|
2796
|
+
}
|
|
2797
|
+
retainAfterInconclusiveRetry(attempt) {
|
|
2798
|
+
return this.retain(attempt, false);
|
|
2799
|
+
}
|
|
2800
|
+
release(attempt) {
|
|
2801
|
+
const reservation = this.currentReservation(attempt);
|
|
2802
|
+
if (reservation === undefined || reservation.state === "released" || reservation.state === "settled")
|
|
2803
|
+
return;
|
|
2804
|
+
if (reservation.retryToken === undefined) {
|
|
2805
|
+
this.releaseFreshCapacity(reservation);
|
|
2806
|
+
reservation.state = "released";
|
|
2807
|
+
return;
|
|
2808
|
+
}
|
|
2809
|
+
this.finishRetained(reservation, "settled");
|
|
2810
|
+
}
|
|
2811
|
+
remainingRetries(attempt) {
|
|
2812
|
+
const reservation = this.currentReservation(attempt);
|
|
2813
|
+
return reservation === undefined ? 0 : Math.max(0, this.maxReuses - reservation.reuseCount);
|
|
2814
|
+
}
|
|
2815
|
+
claimRetry(key, retryToken, now) {
|
|
2816
|
+
const reservation = this.tokenReservation(retryToken, now);
|
|
2817
|
+
if (reservation.key !== key)
|
|
2818
|
+
throw new RetryTokenError("mismatch");
|
|
2819
|
+
this.requireClaimable(reservation);
|
|
2820
|
+
reservation.state = "retry-in-flight";
|
|
2821
|
+
reservation.reuseCount += 1;
|
|
2822
|
+
return this.attempt(reservation);
|
|
2823
|
+
}
|
|
2824
|
+
tokenReservation(retryToken, now) {
|
|
2825
|
+
const reservation = this.tokenReservations.get(retryToken);
|
|
2826
|
+
if (reservation === undefined)
|
|
2827
|
+
throw new RetryTokenError("invalid");
|
|
2828
|
+
if (reservation.expiresAt <= now) {
|
|
2829
|
+
this.expire(reservation);
|
|
2830
|
+
throw new RetryTokenError("expired");
|
|
2831
|
+
}
|
|
2832
|
+
return reservation;
|
|
2833
|
+
}
|
|
2834
|
+
requireClaimable(reservation) {
|
|
2835
|
+
if (reservation.state === "settled")
|
|
2836
|
+
throw new RetryTokenError("settled");
|
|
2837
|
+
if (reservation.state === "exhausted" || reservation.reuseCount >= this.maxReuses) {
|
|
2838
|
+
reservation.state = "exhausted";
|
|
2839
|
+
throw new RetryTokenError("exhausted");
|
|
2840
|
+
}
|
|
2841
|
+
if (reservation.state === "retry-in-flight")
|
|
2842
|
+
throw new RetryTokenError("in_flight");
|
|
2843
|
+
if (reservation.state !== "retained")
|
|
2844
|
+
throw new RetryTokenError("invalid");
|
|
2845
|
+
}
|
|
2846
|
+
retain(attempt, allowFresh) {
|
|
2847
|
+
const now = this.now();
|
|
2848
|
+
this.prune(now);
|
|
2849
|
+
const reservation = this.currentReservation(attempt);
|
|
2850
|
+
if (reservation === undefined)
|
|
2851
|
+
return;
|
|
2852
|
+
const fresh = reservation.state === "fresh-in-flight";
|
|
2853
|
+
if (!fresh && reservation.state !== "retry-in-flight")
|
|
2854
|
+
return;
|
|
2855
|
+
if (fresh && !allowFresh) {
|
|
2856
|
+
this.releaseFreshCapacity(reservation);
|
|
2857
|
+
reservation.state = "released";
|
|
2858
|
+
return;
|
|
2859
|
+
}
|
|
2860
|
+
if (reservation.expiresAt <= now) {
|
|
2861
|
+
if (reservation.retryToken === undefined) {
|
|
2862
|
+
this.releaseFreshCapacity(reservation);
|
|
2863
|
+
reservation.state = "released";
|
|
2864
|
+
} else
|
|
2865
|
+
this.expire(reservation);
|
|
2866
|
+
return;
|
|
2867
|
+
}
|
|
2868
|
+
if (reservation.reuseCount >= this.maxReuses) {
|
|
2869
|
+
if (reservation.retryToken === undefined) {
|
|
2870
|
+
this.releaseFreshCapacity(reservation);
|
|
2871
|
+
reservation.state = "released";
|
|
2872
|
+
} else
|
|
2873
|
+
reservation.state = "exhausted";
|
|
2874
|
+
return;
|
|
2875
|
+
}
|
|
2876
|
+
if (fresh) {
|
|
2877
|
+
reservation.retryToken = this.uniqueToken();
|
|
2878
|
+
this.tokenReservations.set(reservation.retryToken, reservation);
|
|
2879
|
+
this.releaseFreshCapacity(reservation);
|
|
2880
|
+
}
|
|
2881
|
+
reservation.state = "retained";
|
|
2882
|
+
return reservation.retryToken;
|
|
2883
|
+
}
|
|
2884
|
+
discardSettledTombstonesForCapacity() {
|
|
2885
|
+
if (this.tokenReservations.size + this.freshInFlightCount < this.maxEntries)
|
|
2886
|
+
return;
|
|
2887
|
+
for (const [token, reservation] of this.tokenReservations) {
|
|
2888
|
+
if (reservation.state !== "settled")
|
|
2889
|
+
continue;
|
|
2890
|
+
this.tokenReservations.delete(token);
|
|
2891
|
+
if (this.tokenReservations.size + this.freshInFlightCount < this.maxEntries)
|
|
2892
|
+
return;
|
|
2893
|
+
}
|
|
2894
|
+
}
|
|
2895
|
+
uniqueToken() {
|
|
2896
|
+
for (;; ) {
|
|
2897
|
+
const token = this.createToken();
|
|
2898
|
+
if (token.length > 0 && !this.tokenReservations.has(token))
|
|
2899
|
+
return token;
|
|
2900
|
+
}
|
|
2901
|
+
}
|
|
2902
|
+
attempt(reservation) {
|
|
2903
|
+
const attempt = {
|
|
2904
|
+
key: reservation.key,
|
|
2905
|
+
messageId: reservation.messageId,
|
|
2906
|
+
reuseCount: reservation.reuseCount,
|
|
2907
|
+
expiresAt: reservation.expiresAt,
|
|
2908
|
+
...reservation.state === "retry-in-flight" && reservation.retryToken !== undefined ? { retryToken: reservation.retryToken } : {}
|
|
2909
|
+
};
|
|
2910
|
+
this.attemptStates.set(attempt, reservation);
|
|
2911
|
+
return attempt;
|
|
2912
|
+
}
|
|
2913
|
+
currentReservation(attempt) {
|
|
2914
|
+
const reservation = this.attemptStates.get(attempt);
|
|
2915
|
+
return reservation?.reuseCount === attempt.reuseCount ? reservation : undefined;
|
|
2916
|
+
}
|
|
2917
|
+
releaseFreshCapacity(reservation) {
|
|
2918
|
+
if (reservation.state !== "fresh-in-flight")
|
|
2919
|
+
return;
|
|
2920
|
+
this.freshInFlightCount = Math.max(0, this.freshInFlightCount - 1);
|
|
2921
|
+
}
|
|
2922
|
+
finishRetained(reservation, state) {
|
|
2923
|
+
reservation.state = state;
|
|
2924
|
+
}
|
|
2925
|
+
expire(reservation) {
|
|
2926
|
+
if (reservation.retryToken !== undefined)
|
|
2927
|
+
this.tokenReservations.delete(reservation.retryToken);
|
|
2928
|
+
this.finishRetained(reservation, "released");
|
|
2929
|
+
}
|
|
2930
|
+
prune(now) {
|
|
2931
|
+
for (const reservation of this.tokenReservations.values()) {
|
|
2932
|
+
if (reservation.expiresAt <= now)
|
|
2933
|
+
this.expire(reservation);
|
|
2934
|
+
}
|
|
2935
|
+
}
|
|
2936
|
+
}
|
|
2937
|
+
var RETRY_IDENTITY_MAX_ENTRIES = 1000, RETRY_IDENTITY_MAX_REUSES = 3, RetryIdentityCapacityError, RetryTokenError;
|
|
2938
|
+
var init_retry_identity = __esm(() => {
|
|
2939
|
+
init_retry_policy();
|
|
2940
|
+
RetryIdentityCapacityError = class RetryIdentityCapacityError extends Error {
|
|
2941
|
+
constructor() {
|
|
2942
|
+
super("Intercom retry identity capacity is exhausted; refusing delivery until an in-flight or retained identity is released or expires");
|
|
2943
|
+
this.name = "RetryIdentityCapacityError";
|
|
2944
|
+
}
|
|
2945
|
+
};
|
|
2946
|
+
RetryTokenError = class RetryTokenError extends Error {
|
|
2947
|
+
code;
|
|
2948
|
+
constructor(code) {
|
|
2949
|
+
const reason = {
|
|
2950
|
+
invalid: "is invalid or belongs to another Intercom tool session",
|
|
2951
|
+
expired: "has expired",
|
|
2952
|
+
mismatch: "does not match this Intercom operation",
|
|
2953
|
+
in_flight: "is already claimed by an in-flight retry",
|
|
2954
|
+
settled: "belongs to an operation that is already settled",
|
|
2955
|
+
exhausted: `has exhausted its ${RETRY_IDENTITY_MAX_REUSES} retry attempts`
|
|
2956
|
+
}[code];
|
|
2957
|
+
super(`Intercom retry token ${reason}; refusing delivery`);
|
|
2958
|
+
this.code = code;
|
|
2959
|
+
this.name = "RetryTokenError";
|
|
2960
|
+
}
|
|
2961
|
+
};
|
|
2612
2962
|
});
|
|
2613
2963
|
|
|
2614
2964
|
// dist/builtin/intercom/intercom-tool.ts
|
|
2615
|
-
import { randomUUID as randomUUID4 } from "crypto";
|
|
2616
2965
|
import { Type as Type2 } from "typebox";
|
|
2617
2966
|
import { Text as Text3 } from "@earendil-works/pi-tui";
|
|
2967
|
+
function retryTokenGuidance(retryToken, remainingRetries) {
|
|
2968
|
+
if (retryToken === undefined)
|
|
2969
|
+
return "";
|
|
2970
|
+
return ` Retry this exact operation with retryToken \`${retryToken}\` (${remainingRetries} claimed ${remainingRetries === 1 ? "attempt" : "attempts"} remain; the original deadline is unchanged).`;
|
|
2971
|
+
}
|
|
2972
|
+
function retryErrorDetails(retryToken) {
|
|
2973
|
+
return retryToken === undefined ? { error: true } : { error: true, retryToken };
|
|
2974
|
+
}
|
|
2975
|
+
function retainRecoverableRetry(reservations, attempt) {
|
|
2976
|
+
const retryToken = reservations.retainAfterRecoverableDisconnect(attempt);
|
|
2977
|
+
return {
|
|
2978
|
+
...retryToken === undefined ? {} : { retryToken },
|
|
2979
|
+
remainingRetries: reservations.remainingRetries(attempt)
|
|
2980
|
+
};
|
|
2981
|
+
}
|
|
2982
|
+
function retainInconclusiveRetry(reservations, attempt) {
|
|
2983
|
+
const retryToken = reservations.retainAfterInconclusiveRetry(attempt);
|
|
2984
|
+
return {
|
|
2985
|
+
...retryToken === undefined ? {} : { retryToken },
|
|
2986
|
+
remainingRetries: reservations.remainingRetries(attempt)
|
|
2987
|
+
};
|
|
2988
|
+
}
|
|
2618
2989
|
async function listDirectory(client, group) {
|
|
2619
2990
|
if (typeof client.listDirectory === "function")
|
|
2620
2991
|
return client.listDirectory(group);
|
|
2621
|
-
return { sessions: await client.listSessions(group), workflowStages: [] };
|
|
2992
|
+
return { sessions: await client.listSessions(group), workflowStages: [], workflowFutureStages: [] };
|
|
2622
2993
|
}
|
|
2623
2994
|
async function resolveReplySender(client, logicalTarget, sendTarget) {
|
|
2624
2995
|
if (logicalTarget !== sendTarget)
|
|
2625
|
-
return
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2996
|
+
return { kind: "unresolved" };
|
|
2997
|
+
const parsedTarget = parseWorkflowStageTarget(logicalTarget);
|
|
2998
|
+
if (parsedTarget?.kind !== "path")
|
|
2999
|
+
return { kind: "unresolved" };
|
|
3000
|
+
const stages = (await listDirectory(client)).workflowStages;
|
|
3001
|
+
const aliasMatches = stages.filter((candidate) => candidate.sessionId !== undefined && (candidate.target === logicalTarget || withWorkflowStageTargetFinalSegment(candidate.target, candidate.stageName) === logicalTarget));
|
|
3002
|
+
const aliasSessionIds = new Set(aliasMatches.map((candidate) => candidate.sessionId));
|
|
3003
|
+
if (aliasSessionIds.size > 1) {
|
|
3004
|
+
return {
|
|
3005
|
+
kind: "ambiguous",
|
|
3006
|
+
reason: `multiple live workflow stages share the target "${logicalTarget}"`
|
|
3007
|
+
};
|
|
3008
|
+
}
|
|
3009
|
+
if (aliasSessionIds.size === 1) {
|
|
3010
|
+
const [onlySessionId] = aliasSessionIds;
|
|
3011
|
+
if (onlySessionId !== undefined)
|
|
3012
|
+
return { kind: "resolved", sessionId: onlySessionId };
|
|
3013
|
+
}
|
|
3014
|
+
const finalSegment = parsedTarget.segments.at(-1);
|
|
3015
|
+
const middleSegments = parsedTarget.segments.slice(0, -1);
|
|
3016
|
+
const boundaryAgrees = (candidate) => {
|
|
3017
|
+
const parsedCandidate = parseWorkflowStageTarget(candidate.target);
|
|
3018
|
+
if (parsedCandidate === undefined || parsedCandidate.rootRunId !== parsedTarget.rootRunId || parsedCandidate.segments.length < 2) {
|
|
3019
|
+
return false;
|
|
3020
|
+
}
|
|
3021
|
+
if (middleSegments.length === 0)
|
|
3022
|
+
return true;
|
|
3023
|
+
const candidateMiddles = parsedCandidate.segments.slice(0, -1);
|
|
3024
|
+
return middleSegments.every((segment, index) => {
|
|
3025
|
+
const owningRunIdentity = index === candidateMiddles.length - 1 ? candidate.runId : undefined;
|
|
3026
|
+
return segment === candidateMiddles[index] || segment === owningRunIdentity;
|
|
3027
|
+
});
|
|
3028
|
+
};
|
|
3029
|
+
const looseCandidates = stages.filter((candidate) => {
|
|
3030
|
+
if (candidate.sessionId === undefined)
|
|
3031
|
+
return false;
|
|
3032
|
+
if (candidate.stageId !== finalSegment && candidate.stageName !== finalSegment)
|
|
3033
|
+
return false;
|
|
3034
|
+
const parsedCandidate = parseWorkflowStageTarget(candidate.target);
|
|
3035
|
+
return parsedCandidate !== undefined && parsedCandidate.rootRunId === parsedTarget.rootRunId && parsedCandidate.segments.length >= 2;
|
|
3036
|
+
});
|
|
3037
|
+
const strictMatches = looseCandidates.filter(boundaryAgrees);
|
|
3038
|
+
const strictSessionIds = new Set(strictMatches.map((candidate) => candidate.sessionId));
|
|
3039
|
+
if (strictSessionIds.size === 1) {
|
|
3040
|
+
const [onlySessionId] = strictSessionIds;
|
|
3041
|
+
if (onlySessionId !== undefined)
|
|
3042
|
+
return { kind: "resolved", sessionId: onlySessionId };
|
|
3043
|
+
}
|
|
3044
|
+
const looseSessionIds = new Set(looseCandidates.map((candidate) => candidate.sessionId));
|
|
3045
|
+
if (looseSessionIds.size > 1 || strictSessionIds.size > 1) {
|
|
3046
|
+
return {
|
|
3047
|
+
kind: "ambiguous",
|
|
3048
|
+
reason: `multiple live workflow stages share the final segment "${finalSegment}" under different boundaries`
|
|
3049
|
+
};
|
|
3050
|
+
}
|
|
3051
|
+
if (looseSessionIds.size === 1) {
|
|
3052
|
+
const [onlySessionId] = looseSessionIds;
|
|
3053
|
+
if (onlySessionId !== undefined)
|
|
3054
|
+
return { kind: "resolved", sessionId: onlySessionId };
|
|
3055
|
+
}
|
|
3056
|
+
return { kind: "unresolved" };
|
|
2630
3057
|
}
|
|
2631
3058
|
function formatWorkflowStageRow(stage) {
|
|
2632
3059
|
return `- **${stage.stageName}** — workflow stage [${stage.lifecycle.toUpperCase()}] — target: \`${stage.target}\`${stage.sessionId === undefined ? "" : ` — intercom session: ${stage.sessionId}`}`;
|
|
2633
3060
|
}
|
|
3061
|
+
function formatWorkflowFutureStageRow(stage) {
|
|
3062
|
+
return `- future workflow stage \`${stage.target}\` — ${stage.queuedCount} queued message${stage.queuedCount === 1 ? "" : "s"}`;
|
|
3063
|
+
}
|
|
2634
3064
|
function registerIntercomTool(pi, deps) {
|
|
3065
|
+
const retryIdentities = new RetryIdentityReservations({ maxEntries: deps.retryIdentityMaxEntries });
|
|
2635
3066
|
const { childOrchestratorMetadata, ensureConnected, syncPresenceIdentity, beginReplyWait } = deps;
|
|
2636
3067
|
const resolveTarget = deps.resolveSessionTarget ?? resolveSessionTargetId;
|
|
2637
3068
|
const getMetadata = typeof childOrchestratorMetadata === "function" ? childOrchestratorMetadata : () => childOrchestratorMetadata ?? null;
|
|
@@ -2645,9 +3076,17 @@ Use this to communicate findings, request help, or coordinate work with other se
|
|
|
2645
3076
|
Sessions belong to an intercom group and can ONLY message sessions in the same group;
|
|
2646
3077
|
cross-group sends are rejected by the broker. Ungrouped sessions share the "default" group.
|
|
2647
3078
|
|
|
2648
|
-
For send, live session names and exact full session IDs remain supported.
|
|
2649
|
-
workflow
|
|
2650
|
-
|
|
3079
|
+
For send, live session names and exact full session IDs remain supported. Workflow-stage targets use
|
|
3080
|
+
\`workflow:<rootRunId>/<segment>[/<segment>...]\`; a segment may be a stage name, run id, or glob
|
|
3081
|
+
(\`*\` matches one segment and may be embedded, while \`**\` matches any depth). Use \`intercom list\`
|
|
3082
|
+
inside the invocation group to see live, pending, and possible future targets with queued counts.
|
|
3083
|
+
\`workflow:<rootRunId>/**\` reaches every live stage immediately and remains sticky for every future
|
|
3084
|
+
stage until the root run terminates; narrower name or pattern sends likewise reach every future match.
|
|
3085
|
+
A valid target outside the known set queues speculatively with a \`notInKnownSet\` warning and settles
|
|
3086
|
+
undeliverable at terminal only if never delivered. Use \`ask\` only for a live, reply-capable target.
|
|
3087
|
+
Stage paths win over same-named owned subgroups for send/ask; \`list\` with \`group\` continues to
|
|
3088
|
+
select the group.
|
|
3089
|
+
If a \`send\`, \`ask\`, or \`reply\` fails with \`Client disconnected\`, retry that exact operation with the returned \`retryToken\`, up to three claimed attempts; a call without the token is always a distinct operation.
|
|
2651
3090
|
|
|
2652
3091
|
Usage:
|
|
2653
3092
|
intercom({ action: "list" }) → List sessions visible through your groups
|
|
@@ -2659,19 +3098,20 @@ Usage:
|
|
|
2659
3098
|
intercom({ action: "send", to: "session-name", message: "..." }) → Send message (shared group only)
|
|
2660
3099
|
intercom({ action: "ask", to: "session-name", message: "..." }) → Ask and wait for reply
|
|
2661
3100
|
intercom({ action: "reply", message: "..." }) → Reply to the active or exact pending ask
|
|
3101
|
+
intercom({ action: "send", to: "session-name", message: "...", retryToken: "..." }) → Claim the exact retry returned by a retryable failure
|
|
2662
3102
|
intercom({ action: "pending" }) → List unresolved inbound asks
|
|
2663
3103
|
intercom({ action: "status" }) → Show connection status and all your groups
|
|
2664
3104
|
|
|
2665
3105
|
The "join" action is additive. "default" is the shared default group; "true" and
|
|
2666
3106
|
"auto" are reserved for subagent auto-groups. Ordinary delivery requires at least
|
|
2667
3107
|
one shared membership; contact_supervisor remains the only cross-group path.`,
|
|
2668
|
-
promptSnippet: "Use to coordinate with other local agent sessions that share an intercom group
|
|
3108
|
+
promptSnippet: "Use to coordinate with other local agent sessions that share an intercom group. For a retryable send/ask/reply failure, repeat the exact operation with its returned retryToken; omit retryToken for fresh operations.",
|
|
2669
3109
|
parameters: Type2.Object({
|
|
2670
3110
|
action: Type2.String({
|
|
2671
3111
|
description: "Action: 'list', 'groups', 'join', 'leave', 'send', 'ask', 'reply', 'pending', or 'status'"
|
|
2672
3112
|
}),
|
|
2673
3113
|
to: Type2.Optional(Type2.String({
|
|
2674
|
-
description: "Live session name, exact full session ID, or
|
|
3114
|
+
description: "Live session name, exact full session ID, or `workflow:<rootRunId>/<segment>[/<segment>...]` path; `*` matches one segment and `**` any depth. Send queues sticky delivery for pending/future matches; `workflow:<rootRunId>/**` broadcasts to live and future stages. Use `ask` only on live targets (for 'send', 'ask', or targeted 'reply')"
|
|
2675
3115
|
})),
|
|
2676
3116
|
message: Type2.Optional(Type2.String({
|
|
2677
3117
|
description: "Message to send (for 'send', 'ask', or 'reply' action)"
|
|
@@ -2685,23 +3125,53 @@ one shared membership; contact_supervisor remains the only cross-group path.`,
|
|
|
2685
3125
|
replyTo: Type2.Optional(Type2.String({
|
|
2686
3126
|
description: "Exact pending-ask message ID; disambiguates concurrent asks, including asks from one sender"
|
|
2687
3127
|
})),
|
|
3128
|
+
retryToken: Type2.Optional(Type2.String({
|
|
3129
|
+
minLength: 1,
|
|
3130
|
+
maxLength: 128,
|
|
3131
|
+
description: "Opaque token returned after a retryable send/ask/reply failure. Repeat the exact same action and arguments with this token; omit it for every fresh operation."
|
|
3132
|
+
})),
|
|
2688
3133
|
group: Type2.Optional(Type2.String({
|
|
2689
3134
|
description: "Group name for 'join' or optional targeted 'leave'; read-only group filter for 'list'/'status'. 'send'/'ask' use shared memberships."
|
|
2690
3135
|
}))
|
|
2691
3136
|
}),
|
|
2692
3137
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
3138
|
+
const toolSessionId = ctx.sessionManager.getSessionId();
|
|
3139
|
+
const { action, to, message, attachments, replyTo, retryToken, group } = params;
|
|
3140
|
+
let retryPreflightRemaining;
|
|
3141
|
+
if (retryToken !== undefined) {
|
|
3142
|
+
if (action !== "send" && action !== "ask" && action !== "reply") {
|
|
3143
|
+
const error = new RetryTokenError("mismatch");
|
|
3144
|
+
return {
|
|
3145
|
+
content: [{ type: "text", text: getErrorMessage(error) }],
|
|
3146
|
+
isError: true,
|
|
3147
|
+
details: { error: true }
|
|
3148
|
+
};
|
|
3149
|
+
}
|
|
3150
|
+
try {
|
|
3151
|
+
retryPreflightRemaining = retryIdentities.validateRetryToken(retryToken, toolSessionId, action);
|
|
3152
|
+
} catch (error) {
|
|
3153
|
+
return {
|
|
3154
|
+
content: [{ type: "text", text: getErrorMessage(error) }],
|
|
3155
|
+
isError: true,
|
|
3156
|
+
details: { error: true }
|
|
3157
|
+
};
|
|
3158
|
+
}
|
|
3159
|
+
}
|
|
2693
3160
|
let connectedClient;
|
|
2694
3161
|
try {
|
|
2695
3162
|
connectedClient = await ensureConnected("tool");
|
|
2696
3163
|
} catch (error) {
|
|
3164
|
+
const retainedToken = retryToken !== undefined && isRecoverableIntercomDisconnect(error) ? retryToken : undefined;
|
|
2697
3165
|
return {
|
|
2698
|
-
content: [{
|
|
3166
|
+
content: [{
|
|
3167
|
+
type: "text",
|
|
3168
|
+
text: `Intercom not connected: ${getErrorMessage(error)}${retryTokenGuidance(retainedToken, retryPreflightRemaining ?? 0)}`
|
|
3169
|
+
}],
|
|
2699
3170
|
isError: true,
|
|
2700
|
-
details:
|
|
3171
|
+
details: retryErrorDetails(retainedToken)
|
|
2701
3172
|
};
|
|
2702
3173
|
}
|
|
2703
|
-
syncPresenceIdentity(
|
|
2704
|
-
const { action, to, message, attachments, replyTo, group } = params;
|
|
3174
|
+
syncPresenceIdentity(toolSessionId);
|
|
2705
3175
|
const resolveOwnGroups = (sessions) => {
|
|
2706
3176
|
if (Array.isArray(connectedClient.groups)) {
|
|
2707
3177
|
return connectedClient.groups.map((membership) => normalizeGroup(membership));
|
|
@@ -2826,7 +3296,8 @@ ${lines.join(`
|
|
|
2826
3296
|
const peeked = await listDirectory(connectedClient, requestedGroup);
|
|
2827
3297
|
const rows = [
|
|
2828
3298
|
...peeked.sessions.map((session) => formatSessionListRow(session, currentSession.cwd, session.id === mySessionId)),
|
|
2829
|
-
...peeked.workflowStages.map(formatWorkflowStageRow)
|
|
3299
|
+
...peeked.workflowStages.map(formatWorkflowStageRow),
|
|
3300
|
+
...(peeked.workflowFutureStages ?? []).map(formatWorkflowFutureStageRow)
|
|
2830
3301
|
];
|
|
2831
3302
|
const section = rows.length === 0 ? `**Group [${requestedGroup}] (read-only peek):**
|
|
2832
3303
|
No sessions or workflow stages in this group.` : `**Group [${requestedGroup}] (read-only peek):**
|
|
@@ -2841,7 +3312,8 @@ ${section}` }],
|
|
|
2841
3312
|
group: ownGroups.at(-1),
|
|
2842
3313
|
groups: ownGroups,
|
|
2843
3314
|
peekGroup: requestedGroup,
|
|
2844
|
-
workflowStages: peeked.workflowStages
|
|
3315
|
+
workflowStages: peeked.workflowStages,
|
|
3316
|
+
...(peeked.workflowFutureStages?.length ?? 0) === 0 ? {} : { workflowFutureStages: peeked.workflowFutureStages }
|
|
2845
3317
|
}
|
|
2846
3318
|
};
|
|
2847
3319
|
}
|
|
@@ -2850,7 +3322,8 @@ ${section}` }],
|
|
|
2850
3322
|
${formatSessionListRow(currentSession, currentSession.cwd, true)}`;
|
|
2851
3323
|
const visibleRows = [
|
|
2852
3324
|
...otherSessions.map((session) => formatSessionListRow(session, currentSession.cwd, false)),
|
|
2853
|
-
...ownDirectory.workflowStages.map(formatWorkflowStageRow)
|
|
3325
|
+
...ownDirectory.workflowStages.map(formatWorkflowStageRow),
|
|
3326
|
+
...(ownDirectory.workflowFutureStages ?? []).map(formatWorkflowFutureStageRow)
|
|
2854
3327
|
];
|
|
2855
3328
|
const otherSection = visibleRows.length === 0 ? `**Other sessions and workflow stages:**
|
|
2856
3329
|
No other sessions or workflow stages share any of your groups.` : `**Other visible sessions and workflow stages:**
|
|
@@ -2864,7 +3337,8 @@ ${otherSection}` }],
|
|
|
2864
3337
|
details: {
|
|
2865
3338
|
group: ownGroups.at(-1),
|
|
2866
3339
|
groups: ownGroups,
|
|
2867
|
-
...ownDirectory.workflowStages.length === 0 ? {} : { workflowStages: ownDirectory.workflowStages }
|
|
3340
|
+
...ownDirectory.workflowStages.length === 0 ? {} : { workflowStages: ownDirectory.workflowStages },
|
|
3341
|
+
...(ownDirectory.workflowFutureStages?.length ?? 0) === 0 ? {} : { workflowFutureStages: ownDirectory.workflowFutureStages }
|
|
2868
3342
|
}
|
|
2869
3343
|
};
|
|
2870
3344
|
} catch (error) {
|
|
@@ -2877,20 +3351,35 @@ ${otherSection}` }],
|
|
|
2877
3351
|
}
|
|
2878
3352
|
case "send": {
|
|
2879
3353
|
if (!to || !message) {
|
|
3354
|
+
const errorText = retryToken === undefined ? "Missing 'to' or 'message' parameter" : getErrorMessage(new RetryTokenError("mismatch"));
|
|
2880
3355
|
return {
|
|
2881
|
-
content: [{ type: "text", text:
|
|
3356
|
+
content: [{ type: "text", text: errorText }],
|
|
2882
3357
|
isError: true,
|
|
2883
3358
|
details: { error: true }
|
|
2884
3359
|
};
|
|
2885
3360
|
}
|
|
3361
|
+
if (_signal?.aborted) {
|
|
3362
|
+
return {
|
|
3363
|
+
content: [{
|
|
3364
|
+
type: "text",
|
|
3365
|
+
text: `Cancelled${retryTokenGuidance(retryToken, retryPreflightRemaining ?? 0)}`
|
|
3366
|
+
}],
|
|
3367
|
+
isError: true,
|
|
3368
|
+
details: retryErrorDetails(retryToken)
|
|
3369
|
+
};
|
|
3370
|
+
}
|
|
3371
|
+
let retryIdentity;
|
|
2886
3372
|
try {
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
3373
|
+
if (retryToken === undefined) {
|
|
3374
|
+
retryIdentity = retryIdentities.begin({
|
|
3375
|
+
sessionId: toolSessionId,
|
|
3376
|
+
action: "send",
|
|
3377
|
+
target: to,
|
|
3378
|
+
text: message,
|
|
3379
|
+
attachments,
|
|
3380
|
+
replyTo,
|
|
3381
|
+
expectsReply: false
|
|
3382
|
+
});
|
|
2894
3383
|
}
|
|
2895
3384
|
if (!replyTo && deps.confirmSend && ctx.hasUI) {
|
|
2896
3385
|
const attachmentText = attachments?.length ? formatAttachments(attachments) : "";
|
|
@@ -2898,6 +3387,9 @@ ${otherSection}` }],
|
|
|
2898
3387
|
|
|
2899
3388
|
${message}${attachmentText}`);
|
|
2900
3389
|
if (!confirmed) {
|
|
3390
|
+
if (retryIdentity !== undefined)
|
|
3391
|
+
retryIdentities.release(retryIdentity);
|
|
3392
|
+
retryIdentity = undefined;
|
|
2901
3393
|
return {
|
|
2902
3394
|
content: [{ type: "text", text: "Message cancelled by user" }],
|
|
2903
3395
|
isError: false,
|
|
@@ -2905,78 +3397,159 @@ ${message}${attachmentText}`);
|
|
|
2905
3397
|
};
|
|
2906
3398
|
}
|
|
2907
3399
|
}
|
|
3400
|
+
retryIdentity ??= retryIdentities.begin({
|
|
3401
|
+
sessionId: toolSessionId,
|
|
3402
|
+
action: "send",
|
|
3403
|
+
target: to,
|
|
3404
|
+
text: message,
|
|
3405
|
+
attachments,
|
|
3406
|
+
replyTo,
|
|
3407
|
+
expectsReply: false
|
|
3408
|
+
}, retryToken);
|
|
3409
|
+
const sendTo = await resolveTarget(connectedClient, to) ?? to;
|
|
3410
|
+
if (_signal?.aborted) {
|
|
3411
|
+
const retained = retainInconclusiveRetry(retryIdentities, retryIdentity);
|
|
3412
|
+
retryIdentity = undefined;
|
|
3413
|
+
return {
|
|
3414
|
+
content: [{ type: "text", text: `Cancelled${retryTokenGuidance(retained.retryToken, retained.remainingRetries)}` }],
|
|
3415
|
+
isError: true,
|
|
3416
|
+
details: retryErrorDetails(retained.retryToken)
|
|
3417
|
+
};
|
|
3418
|
+
}
|
|
3419
|
+
if (sendTo === connectedClient.sessionId) {
|
|
3420
|
+
const retained = retainInconclusiveRetry(retryIdentities, retryIdentity);
|
|
3421
|
+
retryIdentity = undefined;
|
|
3422
|
+
return {
|
|
3423
|
+
content: [{
|
|
3424
|
+
type: "text",
|
|
3425
|
+
text: `Cannot message the current session${retryTokenGuidance(retained.retryToken, retained.remainingRetries)}`
|
|
3426
|
+
}],
|
|
3427
|
+
isError: true,
|
|
3428
|
+
details: retryErrorDetails(retained.retryToken)
|
|
3429
|
+
};
|
|
3430
|
+
}
|
|
2908
3431
|
const result = await connectedClient.send(sendTo, {
|
|
3432
|
+
messageId: retryIdentity.messageId,
|
|
3433
|
+
logicalTarget: to,
|
|
2909
3434
|
text: message,
|
|
2910
3435
|
attachments,
|
|
2911
3436
|
replyTo
|
|
2912
3437
|
});
|
|
2913
3438
|
if (result.queued === true) {
|
|
3439
|
+
retryIdentities.release(retryIdentity);
|
|
3440
|
+
retryIdentity = undefined;
|
|
2914
3441
|
pi.appendEntry("intercom_sent", {
|
|
2915
3442
|
to,
|
|
2916
3443
|
message: { text: message, attachments, replyTo },
|
|
2917
3444
|
messageId: result.id,
|
|
2918
3445
|
timestamp: Date.now()
|
|
2919
3446
|
});
|
|
3447
|
+
const notInKnownSet = result.notInKnownSet === true;
|
|
2920
3448
|
return {
|
|
2921
|
-
content: [{
|
|
3449
|
+
content: [{
|
|
3450
|
+
type: "text",
|
|
3451
|
+
text: notInKnownSet ? `Message queued for ${to} (not in the workflow's known stage set; it will be delivered to every matching stage that starts before the run terminates)` : `Message queued for ${to}`
|
|
3452
|
+
}],
|
|
2922
3453
|
isError: false,
|
|
2923
3454
|
details: {
|
|
2924
3455
|
messageId: result.id,
|
|
2925
3456
|
delivered: false,
|
|
2926
3457
|
queued: true,
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
3458
|
+
target: result.target,
|
|
3459
|
+
position: result.position,
|
|
3460
|
+
...notInKnownSet ? { notInKnownSet: true } : {}
|
|
2930
3461
|
}
|
|
2931
3462
|
};
|
|
2932
3463
|
}
|
|
2933
3464
|
if (!result.delivered) {
|
|
3465
|
+
const retained = retainInconclusiveRetry(retryIdentities, retryIdentity);
|
|
3466
|
+
retryIdentity = undefined;
|
|
2934
3467
|
const errorText = result.reason ?? "Session may not exist or has disconnected.";
|
|
2935
3468
|
return {
|
|
2936
|
-
content: [{
|
|
3469
|
+
content: [{
|
|
3470
|
+
type: "text",
|
|
3471
|
+
text: `Message to "${to}" was not delivered: ${errorText}${retryTokenGuidance(retained.retryToken, retained.remainingRetries)}`
|
|
3472
|
+
}],
|
|
2937
3473
|
isError: true,
|
|
2938
|
-
details: { messageId: result.id, delivered: false, reason: result.reason }
|
|
3474
|
+
details: retryToken === undefined ? { messageId: result.id, delivered: false, reason: result.reason } : {
|
|
3475
|
+
delivered: false,
|
|
3476
|
+
reason: result.reason,
|
|
3477
|
+
...retained.retryToken === undefined ? {} : { retryToken: retained.retryToken }
|
|
3478
|
+
}
|
|
2939
3479
|
};
|
|
2940
3480
|
}
|
|
3481
|
+
retryIdentities.release(retryIdentity);
|
|
3482
|
+
retryIdentity = undefined;
|
|
2941
3483
|
pi.appendEntry("intercom_sent", {
|
|
2942
3484
|
to,
|
|
2943
3485
|
message: { text: message, attachments, replyTo },
|
|
2944
3486
|
messageId: result.id,
|
|
2945
3487
|
timestamp: Date.now()
|
|
2946
3488
|
});
|
|
2947
|
-
if (replyTo)
|
|
3489
|
+
if (replyTo)
|
|
2948
3490
|
activeReplyTracker().markReplied(replyTo);
|
|
2949
|
-
}
|
|
2950
3491
|
return {
|
|
2951
3492
|
content: [{ type: "text", text: `Message sent to ${to}` }],
|
|
2952
3493
|
isError: false,
|
|
2953
3494
|
details: { messageId: result.id, delivered: true }
|
|
2954
3495
|
};
|
|
2955
3496
|
} catch (error) {
|
|
3497
|
+
let failure = error;
|
|
3498
|
+
let retained;
|
|
3499
|
+
if (retryIdentity !== undefined) {
|
|
3500
|
+
if (isRecoverableIntercomDisconnect(error)) {
|
|
3501
|
+
try {
|
|
3502
|
+
retained = retainRecoverableRetry(retryIdentities, retryIdentity);
|
|
3503
|
+
} catch (retentionError) {
|
|
3504
|
+
failure = retentionError;
|
|
3505
|
+
}
|
|
3506
|
+
} else {
|
|
3507
|
+
retryIdentities.release(retryIdentity);
|
|
3508
|
+
}
|
|
3509
|
+
}
|
|
2956
3510
|
return {
|
|
2957
|
-
content: [{
|
|
3511
|
+
content: [{
|
|
3512
|
+
type: "text",
|
|
3513
|
+
text: `Failed to send: ${getErrorMessage(failure)}${retryTokenGuidance(retained?.retryToken, retained?.remainingRetries ?? 0)}`
|
|
3514
|
+
}],
|
|
2958
3515
|
isError: true,
|
|
2959
|
-
details:
|
|
3516
|
+
details: retryErrorDetails(retained?.retryToken)
|
|
2960
3517
|
};
|
|
2961
3518
|
}
|
|
2962
3519
|
}
|
|
2963
3520
|
case "ask": {
|
|
2964
3521
|
if (!to) {
|
|
3522
|
+
const errorText = retryToken === undefined ? "Missing 'to' or 'message' parameter" : getErrorMessage(new RetryTokenError("mismatch"));
|
|
2965
3523
|
return {
|
|
2966
|
-
content: [{ type: "text", text:
|
|
3524
|
+
content: [{ type: "text", text: errorText }],
|
|
2967
3525
|
isError: true,
|
|
2968
3526
|
details: { error: true }
|
|
2969
3527
|
};
|
|
2970
3528
|
}
|
|
2971
3529
|
if (_signal?.aborted) {
|
|
2972
3530
|
return {
|
|
2973
|
-
content: [{
|
|
3531
|
+
content: [{
|
|
3532
|
+
type: "text",
|
|
3533
|
+
text: `Cancelled${retryTokenGuidance(retryToken, retryPreflightRemaining ?? 0)}`
|
|
3534
|
+
}],
|
|
2974
3535
|
isError: true,
|
|
2975
|
-
details:
|
|
3536
|
+
details: retryErrorDetails(retryToken)
|
|
2976
3537
|
};
|
|
2977
3538
|
}
|
|
2978
3539
|
let wait = null;
|
|
3540
|
+
let retryIdentity;
|
|
2979
3541
|
try {
|
|
3542
|
+
if (retryToken !== undefined && !message)
|
|
3543
|
+
throw new RetryTokenError("mismatch");
|
|
3544
|
+
retryIdentity = retryIdentities.begin({
|
|
3545
|
+
sessionId: toolSessionId,
|
|
3546
|
+
action: "ask",
|
|
3547
|
+
target: to,
|
|
3548
|
+
text: message ?? "",
|
|
3549
|
+
attachments,
|
|
3550
|
+
replyTo,
|
|
3551
|
+
expectsReply: true
|
|
3552
|
+
}, retryToken);
|
|
2980
3553
|
const metadata = getMetadata();
|
|
2981
3554
|
const currentSupervisorId = connectedClient.supervisorSessionId ?? undefined;
|
|
2982
3555
|
const metadataSupervisorId = metadata?.supervisor?.supervisorSessionId;
|
|
@@ -2987,7 +3560,9 @@ ${message}${attachmentText}`);
|
|
|
2987
3560
|
attachments,
|
|
2988
3561
|
resolvedTargetId
|
|
2989
3562
|
}));
|
|
2990
|
-
if (directParentTarget && claimParentAsk(currentSupervisorId ?? metadataSupervisorId ?? to)) {
|
|
3563
|
+
if (retryToken === undefined && directParentTarget && claimParentAsk(currentSupervisorId ?? metadataSupervisorId ?? to)) {
|
|
3564
|
+
retryIdentities.release(retryIdentity);
|
|
3565
|
+
retryIdentity = undefined;
|
|
2991
3566
|
return {
|
|
2992
3567
|
content: [{ type: "text", text: "Parent ask claimed; this child is ending for a fresh subagent start." }],
|
|
2993
3568
|
isError: false,
|
|
@@ -2996,23 +3571,35 @@ ${message}${attachmentText}`);
|
|
|
2996
3571
|
}
|
|
2997
3572
|
const sendTo = await resolveTarget(connectedClient, to) ?? to;
|
|
2998
3573
|
if (_signal?.aborted) {
|
|
3574
|
+
const retained = retryIdentity === undefined ? undefined : retainInconclusiveRetry(retryIdentities, retryIdentity);
|
|
3575
|
+
retryIdentity = undefined;
|
|
2999
3576
|
return {
|
|
3000
|
-
content: [{
|
|
3577
|
+
content: [{
|
|
3578
|
+
type: "text",
|
|
3579
|
+
text: `Cancelled${retryTokenGuidance(retained?.retryToken, retained?.remainingRetries ?? 0)}`
|
|
3580
|
+
}],
|
|
3001
3581
|
isError: true,
|
|
3002
|
-
details:
|
|
3582
|
+
details: retryErrorDetails(retained?.retryToken)
|
|
3003
3583
|
};
|
|
3004
3584
|
}
|
|
3005
3585
|
if (sendTo === connectedClient.sessionId) {
|
|
3586
|
+
const retained = retryIdentity === undefined ? undefined : retainInconclusiveRetry(retryIdentities, retryIdentity);
|
|
3587
|
+
retryIdentity = undefined;
|
|
3006
3588
|
return {
|
|
3007
|
-
content: [{
|
|
3589
|
+
content: [{
|
|
3590
|
+
type: "text",
|
|
3591
|
+
text: `Cannot message the current session${retryTokenGuidance(retained?.retryToken, retained?.remainingRetries ?? 0)}`
|
|
3592
|
+
}],
|
|
3008
3593
|
isError: true,
|
|
3009
|
-
details:
|
|
3594
|
+
details: retryErrorDetails(retained?.retryToken)
|
|
3010
3595
|
};
|
|
3011
3596
|
}
|
|
3012
|
-
if (metadata && !directParentTarget) {
|
|
3597
|
+
if (retryToken === undefined && metadata && !directParentTarget) {
|
|
3013
3598
|
const authoritativeParent = [currentSupervisorId, metadataSupervisorId].find((candidate) => candidate === sendTo);
|
|
3014
3599
|
const resolvedParent = authoritativeParent ?? await resolveTarget(connectedClient, metadata.orchestratorTarget);
|
|
3015
3600
|
if (resolvedParent !== null && resolvedParent === sendTo && claimParentAsk(sendTo)) {
|
|
3601
|
+
retryIdentities.release(retryIdentity);
|
|
3602
|
+
retryIdentity = undefined;
|
|
3016
3603
|
return {
|
|
3017
3604
|
content: [{ type: "text", text: "Parent ask claimed; this child is ending for a fresh subagent start." }],
|
|
3018
3605
|
isError: false,
|
|
@@ -3021,44 +3608,94 @@ ${message}${attachmentText}`);
|
|
|
3021
3608
|
}
|
|
3022
3609
|
}
|
|
3023
3610
|
if (!message) {
|
|
3611
|
+
retryIdentities.release(retryIdentity);
|
|
3612
|
+
retryIdentity = undefined;
|
|
3024
3613
|
return {
|
|
3025
3614
|
content: [{ type: "text", text: "Missing 'to' or 'message' parameter" }],
|
|
3026
3615
|
isError: true,
|
|
3027
3616
|
details: { error: true }
|
|
3028
3617
|
};
|
|
3029
3618
|
}
|
|
3030
|
-
const
|
|
3031
|
-
|
|
3619
|
+
const replySender = await resolveReplySender(connectedClient, to, sendTo);
|
|
3620
|
+
if (replySender.kind === "ambiguous") {
|
|
3621
|
+
const retained = retryIdentity === undefined ? undefined : retainInconclusiveRetry(retryIdentities, retryIdentity);
|
|
3622
|
+
retryIdentity = undefined;
|
|
3623
|
+
return {
|
|
3624
|
+
content: [{
|
|
3625
|
+
type: "text",
|
|
3626
|
+
text: `Message to "${to}" is ambiguous: ${replySender.reason}. Use the exact target shown by intercom list.${retryTokenGuidance(retained?.retryToken, retained?.remainingRetries ?? 0)}`
|
|
3627
|
+
}],
|
|
3628
|
+
isError: true,
|
|
3629
|
+
details: retryErrorDetails(retained?.retryToken)
|
|
3630
|
+
};
|
|
3631
|
+
}
|
|
3632
|
+
const questionId = retryIdentity.messageId;
|
|
3633
|
+
const replyFrom = replySender.kind === "resolved" ? replySender.sessionId : sendTo;
|
|
3032
3634
|
const admission = beginReplyWait(replyFrom, questionId, _signal);
|
|
3033
3635
|
if (!admission.ok) {
|
|
3636
|
+
const retained = retainInconclusiveRetry(retryIdentities, retryIdentity);
|
|
3637
|
+
retryIdentity = undefined;
|
|
3034
3638
|
const text = admission.reason === "busy" ? `Too many pending asks (${admission.limit}); reply-wait slots are full` : "Cancelled";
|
|
3035
3639
|
return {
|
|
3036
|
-
content: [{
|
|
3640
|
+
content: [{
|
|
3641
|
+
type: "text",
|
|
3642
|
+
text: `${text}${retryTokenGuidance(retained.retryToken, retained.remainingRetries)}`
|
|
3643
|
+
}],
|
|
3037
3644
|
isError: true,
|
|
3038
|
-
details:
|
|
3645
|
+
details: retryErrorDetails(retained.retryToken)
|
|
3039
3646
|
};
|
|
3040
3647
|
}
|
|
3041
3648
|
wait = admission.wait;
|
|
3042
3649
|
const sendResult = await connectedClient.send(sendTo, {
|
|
3043
3650
|
messageId: questionId,
|
|
3651
|
+
logicalTarget: to,
|
|
3044
3652
|
text: message,
|
|
3045
3653
|
attachments,
|
|
3046
3654
|
replyTo,
|
|
3047
3655
|
expectsReply: true
|
|
3048
3656
|
});
|
|
3657
|
+
if (sendResult.queued === true) {
|
|
3658
|
+
retryIdentities.release(retryIdentity);
|
|
3659
|
+
retryIdentity = undefined;
|
|
3660
|
+
wait.cancel(new Error(`Message queued for ${to}`));
|
|
3661
|
+
pi.appendEntry("intercom_sent", {
|
|
3662
|
+
to,
|
|
3663
|
+
message: { text: message, attachments, replyTo },
|
|
3664
|
+
messageId: sendResult.id,
|
|
3665
|
+
timestamp: Date.now()
|
|
3666
|
+
});
|
|
3667
|
+
return {
|
|
3668
|
+
content: [{ type: "text", text: `Message queued for ${to}` }],
|
|
3669
|
+
isError: false,
|
|
3670
|
+
details: {
|
|
3671
|
+
messageId: sendResult.id,
|
|
3672
|
+
delivered: false,
|
|
3673
|
+
queued: true,
|
|
3674
|
+
target: sendResult.target,
|
|
3675
|
+
position: sendResult.position
|
|
3676
|
+
}
|
|
3677
|
+
};
|
|
3678
|
+
}
|
|
3049
3679
|
if (!sendResult.delivered) {
|
|
3680
|
+
const retained = retainInconclusiveRetry(retryIdentities, retryIdentity);
|
|
3681
|
+
retryIdentity = undefined;
|
|
3050
3682
|
const errorText = sendResult.reason ?? "Session may not exist or has disconnected.";
|
|
3051
3683
|
wait.cancel(new Error(`Message to "${to}" was not delivered: ${errorText}`));
|
|
3052
3684
|
const pendingStageAskRefusal = errorText.startsWith("Cannot ask a workflow stage whose session has not initialized.");
|
|
3053
3685
|
return {
|
|
3054
|
-
content: [{
|
|
3686
|
+
content: [{
|
|
3687
|
+
type: "text",
|
|
3688
|
+
text: `Message to "${to}" was not delivered: ${errorText}${retryTokenGuidance(retained.retryToken, retained.remainingRetries)}`
|
|
3689
|
+
}],
|
|
3055
3690
|
isError: true,
|
|
3056
|
-
details:
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3691
|
+
details: {
|
|
3692
|
+
...pendingStageAskRefusal ? {
|
|
3693
|
+
refusal: "pending_stage_ask_unsupported",
|
|
3694
|
+
recommendedAction: "send",
|
|
3695
|
+
reason: errorText
|
|
3696
|
+
} : {},
|
|
3697
|
+
...retryErrorDetails(retained.retryToken)
|
|
3698
|
+
}
|
|
3062
3699
|
};
|
|
3063
3700
|
}
|
|
3064
3701
|
pi.appendEntry("intercom_sent", {
|
|
@@ -3068,6 +3705,8 @@ ${message}${attachmentText}`);
|
|
|
3068
3705
|
timestamp: Date.now()
|
|
3069
3706
|
});
|
|
3070
3707
|
const replyMessage = await wait.promise;
|
|
3708
|
+
retryIdentities.release(retryIdentity);
|
|
3709
|
+
retryIdentity = undefined;
|
|
3071
3710
|
const replyText = replyMessage.content.text;
|
|
3072
3711
|
const replyAttachments = replyMessage.content.attachments?.length ? formatAttachments(replyMessage.content.attachments) : "";
|
|
3073
3712
|
pi.appendEntry("intercom_received", {
|
|
@@ -3091,59 +3730,164 @@ ${replyText}${replyAttachments}` }],
|
|
|
3091
3730
|
};
|
|
3092
3731
|
} catch (error) {
|
|
3093
3732
|
wait?.cancel(toError3(error));
|
|
3733
|
+
let failure = error;
|
|
3734
|
+
let retained;
|
|
3735
|
+
if (retryIdentity !== undefined) {
|
|
3736
|
+
if (isRecoverableIntercomDisconnect(error)) {
|
|
3737
|
+
try {
|
|
3738
|
+
retained = retainRecoverableRetry(retryIdentities, retryIdentity);
|
|
3739
|
+
} catch (retentionError) {
|
|
3740
|
+
failure = retentionError;
|
|
3741
|
+
}
|
|
3742
|
+
} else {
|
|
3743
|
+
retryIdentities.release(retryIdentity);
|
|
3744
|
+
}
|
|
3745
|
+
}
|
|
3094
3746
|
return {
|
|
3095
|
-
content: [{
|
|
3747
|
+
content: [{
|
|
3748
|
+
type: "text",
|
|
3749
|
+
text: `Failed: ${getErrorMessage(failure)}${retryTokenGuidance(retained?.retryToken, retained?.remainingRetries ?? 0)}`
|
|
3750
|
+
}],
|
|
3096
3751
|
isError: true,
|
|
3097
|
-
details:
|
|
3752
|
+
details: retryErrorDetails(retained?.retryToken)
|
|
3098
3753
|
};
|
|
3099
3754
|
}
|
|
3100
3755
|
}
|
|
3101
3756
|
case "reply": {
|
|
3102
3757
|
if (!message) {
|
|
3758
|
+
const errorText = retryToken === undefined ? "Missing 'message' parameter" : getErrorMessage(new RetryTokenError("mismatch"));
|
|
3103
3759
|
return {
|
|
3104
|
-
content: [{ type: "text", text:
|
|
3760
|
+
content: [{ type: "text", text: errorText }],
|
|
3105
3761
|
isError: true,
|
|
3106
3762
|
details: { error: true }
|
|
3107
3763
|
};
|
|
3108
3764
|
}
|
|
3765
|
+
let retryIdentity;
|
|
3109
3766
|
try {
|
|
3110
|
-
|
|
3111
|
-
|
|
3767
|
+
retryIdentity = retryIdentities.begin({
|
|
3768
|
+
sessionId: toolSessionId,
|
|
3769
|
+
action: "reply",
|
|
3770
|
+
target: to,
|
|
3771
|
+
text: message,
|
|
3772
|
+
attachments,
|
|
3773
|
+
replyTo,
|
|
3774
|
+
expectsReply: false
|
|
3775
|
+
}, retryToken);
|
|
3776
|
+
if (retryToken === undefined) {
|
|
3777
|
+
const target = activeReplyTracker().resolveReplyTarget({ to, replyTo });
|
|
3778
|
+
retryIdentities.bindReplyRoute(retryIdentity, {
|
|
3779
|
+
senderId: target.from.id,
|
|
3780
|
+
...target.from.name === undefined ? {} : { senderName: target.from.name },
|
|
3781
|
+
messageId: target.message.id,
|
|
3782
|
+
expectsReply: target.message.expectsReply === true
|
|
3783
|
+
});
|
|
3784
|
+
}
|
|
3785
|
+
const route = retryIdentities.replyRoute(retryIdentity);
|
|
3786
|
+
if (route === undefined)
|
|
3787
|
+
throw new RetryTokenError("invalid");
|
|
3788
|
+
const replySendTo = to ?? route.senderId;
|
|
3789
|
+
const replyLogicalTarget = to ?? route.senderId;
|
|
3790
|
+
const displayTarget = route.senderName || route.senderId;
|
|
3791
|
+
if (route.senderId === connectedClient.sessionId) {
|
|
3792
|
+
const retained = retainInconclusiveRetry(retryIdentities, retryIdentity);
|
|
3793
|
+
retryIdentity = undefined;
|
|
3112
3794
|
return {
|
|
3113
|
-
content: [{
|
|
3795
|
+
content: [{
|
|
3796
|
+
type: "text",
|
|
3797
|
+
text: `Cannot message the current session${retryTokenGuidance(retained.retryToken, retained.remainingRetries)}`
|
|
3798
|
+
}],
|
|
3114
3799
|
isError: true,
|
|
3115
|
-
details:
|
|
3800
|
+
details: retryErrorDetails(retained.retryToken)
|
|
3116
3801
|
};
|
|
3117
3802
|
}
|
|
3118
|
-
const
|
|
3803
|
+
const replyMessageId = retryIdentity.messageId;
|
|
3804
|
+
const sendReply = (targetId) => connectedClient.send(targetId, {
|
|
3805
|
+
messageId: replyMessageId,
|
|
3806
|
+
logicalTarget: replyLogicalTarget,
|
|
3807
|
+
...route.expectsReply ? { requirePendingReply: true } : {},
|
|
3119
3808
|
text: message,
|
|
3120
|
-
|
|
3809
|
+
attachments,
|
|
3810
|
+
replyTo: route.messageId
|
|
3121
3811
|
});
|
|
3812
|
+
let result = await sendReply(replySendTo);
|
|
3813
|
+
if (to === undefined && result.delivered === false && result.reasonCode === "session_not_found" && route.senderName !== undefined) {
|
|
3814
|
+
result = await sendReply(route.senderName);
|
|
3815
|
+
}
|
|
3816
|
+
if (result.queued === true) {
|
|
3817
|
+
retryIdentities.release(retryIdentity);
|
|
3818
|
+
retryIdentity = undefined;
|
|
3819
|
+
activeReplyTracker().markReplied(route.messageId);
|
|
3820
|
+
pi.appendEntry("intercom_sent", {
|
|
3821
|
+
to: displayTarget,
|
|
3822
|
+
message: { text: message, attachments, replyTo: route.messageId },
|
|
3823
|
+
messageId: result.id,
|
|
3824
|
+
timestamp: Date.now()
|
|
3825
|
+
});
|
|
3826
|
+
return {
|
|
3827
|
+
content: [{ type: "text", text: `Reply queued for ${displayTarget}` }],
|
|
3828
|
+
isError: false,
|
|
3829
|
+
details: {
|
|
3830
|
+
messageId: result.id,
|
|
3831
|
+
delivered: false,
|
|
3832
|
+
queued: true,
|
|
3833
|
+
replyTo: route.messageId,
|
|
3834
|
+
target: result.target,
|
|
3835
|
+
position: result.position
|
|
3836
|
+
}
|
|
3837
|
+
};
|
|
3838
|
+
}
|
|
3122
3839
|
if (!result.delivered) {
|
|
3840
|
+
const retained = retainInconclusiveRetry(retryIdentities, retryIdentity);
|
|
3841
|
+
retryIdentity = undefined;
|
|
3123
3842
|
const errorText = result.reason ?? "Session may not exist or has disconnected.";
|
|
3124
3843
|
return {
|
|
3125
|
-
content: [{
|
|
3844
|
+
content: [{
|
|
3845
|
+
type: "text",
|
|
3846
|
+
text: `Reply to "${displayTarget}" was not delivered: ${errorText}${retryTokenGuidance(retained.retryToken, retained.remainingRetries)}`
|
|
3847
|
+
}],
|
|
3126
3848
|
isError: true,
|
|
3127
|
-
details: { messageId: result.id, delivered: false, reason: result.reason }
|
|
3849
|
+
details: retryToken === undefined ? { messageId: result.id, delivered: false, reason: result.reason } : {
|
|
3850
|
+
delivered: false,
|
|
3851
|
+
reason: result.reason,
|
|
3852
|
+
...retained.retryToken === undefined ? {} : { retryToken: retained.retryToken }
|
|
3853
|
+
}
|
|
3128
3854
|
};
|
|
3129
3855
|
}
|
|
3130
|
-
|
|
3856
|
+
retryIdentities.release(retryIdentity);
|
|
3857
|
+
retryIdentity = undefined;
|
|
3858
|
+
activeReplyTracker().markReplied(route.messageId);
|
|
3131
3859
|
pi.appendEntry("intercom_sent", {
|
|
3132
|
-
to:
|
|
3133
|
-
message: { text: message, replyTo:
|
|
3860
|
+
to: displayTarget,
|
|
3861
|
+
message: { text: message, attachments, replyTo: route.messageId },
|
|
3134
3862
|
messageId: result.id,
|
|
3135
3863
|
timestamp: Date.now()
|
|
3136
3864
|
});
|
|
3137
3865
|
return {
|
|
3138
|
-
content: [{ type: "text", text: `Reply sent to ${
|
|
3866
|
+
content: [{ type: "text", text: `Reply sent to ${displayTarget}` }],
|
|
3139
3867
|
isError: false,
|
|
3140
|
-
details: { messageId: result.id, delivered: true, replyTo:
|
|
3868
|
+
details: { messageId: result.id, delivered: true, replyTo: route.messageId }
|
|
3141
3869
|
};
|
|
3142
3870
|
} catch (error) {
|
|
3871
|
+
let failure = error;
|
|
3872
|
+
let retained;
|
|
3873
|
+
if (retryIdentity !== undefined) {
|
|
3874
|
+
if (isRecoverableIntercomDisconnect(error)) {
|
|
3875
|
+
try {
|
|
3876
|
+
retained = retainRecoverableRetry(retryIdentities, retryIdentity);
|
|
3877
|
+
} catch (retentionError) {
|
|
3878
|
+
failure = retentionError;
|
|
3879
|
+
}
|
|
3880
|
+
} else {
|
|
3881
|
+
retryIdentities.release(retryIdentity);
|
|
3882
|
+
}
|
|
3883
|
+
}
|
|
3143
3884
|
return {
|
|
3144
|
-
content: [{
|
|
3885
|
+
content: [{
|
|
3886
|
+
type: "text",
|
|
3887
|
+
text: `Failed to reply: ${getErrorMessage(failure)}${retryTokenGuidance(retained?.retryToken, retained?.remainingRetries ?? 0)}`
|
|
3888
|
+
}],
|
|
3145
3889
|
isError: true,
|
|
3146
|
-
details:
|
|
3890
|
+
details: retryErrorDetails(retained?.retryToken)
|
|
3147
3891
|
};
|
|
3148
3892
|
}
|
|
3149
3893
|
}
|
|
@@ -3238,7 +3982,9 @@ var init_intercom_tool = __esm(() => {
|
|
|
3238
3982
|
init_result_renderers();
|
|
3239
3983
|
init_intercom_utils();
|
|
3240
3984
|
init_group();
|
|
3241
|
-
|
|
3985
|
+
init_workflow_stage_target();
|
|
3986
|
+
init_recoverable_disconnect();
|
|
3987
|
+
init_retry_identity();
|
|
3242
3988
|
});
|
|
3243
3989
|
|
|
3244
3990
|
// dist/builtin/intercom/ui/compose.ts
|
|
@@ -3747,43 +4493,379 @@ var init_lifecycle = __esm(() => {
|
|
|
3747
4493
|
});
|
|
3748
4494
|
|
|
3749
4495
|
// dist/builtin/intercom/broker/delivered-message-cache.ts
|
|
4496
|
+
import { createHmac, randomBytes } from "node:crypto";
|
|
4497
|
+
import {
|
|
4498
|
+
chmodSync as chmodSync2,
|
|
4499
|
+
closeSync as closeSync2,
|
|
4500
|
+
existsSync as existsSync3,
|
|
4501
|
+
mkdirSync as mkdirSync2,
|
|
4502
|
+
openSync as openSync2,
|
|
4503
|
+
readFileSync as readFileSync3,
|
|
4504
|
+
writeFileSync as writeFileSync2
|
|
4505
|
+
} from "node:fs";
|
|
4506
|
+
import { dirname as dirname2 } from "node:path";
|
|
4507
|
+
import { DatabaseSync } from "node:sqlite";
|
|
4508
|
+
function validStoredString(value) {
|
|
4509
|
+
return typeof value === "string" && value.length > 0;
|
|
4510
|
+
}
|
|
4511
|
+
function entryByteSize(messageId, delivered) {
|
|
4512
|
+
return [
|
|
4513
|
+
messageId,
|
|
4514
|
+
delivered.signature,
|
|
4515
|
+
delivered.targetIdentity,
|
|
4516
|
+
delivered.questionRoute?.targetSessionId,
|
|
4517
|
+
delivered.questionRoute?.senderGroupIdentity
|
|
4518
|
+
].reduce((total, value) => total + (value === undefined ? 0 : Buffer.byteLength(value)), 0);
|
|
4519
|
+
}
|
|
4520
|
+
function parseStoredMessage(row) {
|
|
4521
|
+
if (row === undefined)
|
|
4522
|
+
return;
|
|
4523
|
+
if (row.state !== "reserved" && row.state !== "accepted" || !validStoredString(row.signature) || !AUTHORITY_DIGEST_PATTERN.test(row.signature) || typeof row.delivered_at !== "number" || !Number.isSafeInteger(row.delivered_at) || row.delivered_at < 0 || row.target_identity !== null && !validStoredString(row.target_identity)) {
|
|
4524
|
+
return "invalid";
|
|
4525
|
+
}
|
|
4526
|
+
const questionValues = [row.question_target_session_id, row.question_sender_group_identity];
|
|
4527
|
+
const hasQuestionRoute = questionValues.some((value) => value !== null);
|
|
4528
|
+
if (hasQuestionRoute && !questionValues.every(validStoredString))
|
|
4529
|
+
return "invalid";
|
|
4530
|
+
return {
|
|
4531
|
+
state: row.state,
|
|
4532
|
+
deliveredAt: row.delivered_at,
|
|
4533
|
+
signature: row.signature,
|
|
4534
|
+
...typeof row.target_identity === "string" ? { targetIdentity: row.target_identity } : {},
|
|
4535
|
+
...hasQuestionRoute ? {
|
|
4536
|
+
questionRoute: {
|
|
4537
|
+
targetSessionId: row.question_target_session_id,
|
|
4538
|
+
senderGroupIdentity: row.question_sender_group_identity
|
|
4539
|
+
}
|
|
4540
|
+
} : {}
|
|
4541
|
+
};
|
|
4542
|
+
}
|
|
4543
|
+
function getDeliveredMessageAuthorityKeyPath(storagePath) {
|
|
4544
|
+
return storagePath.endsWith(".sqlite") ? `${storagePath.slice(0, -".sqlite".length)}.key` : `${storagePath}.key`;
|
|
4545
|
+
}
|
|
4546
|
+
function restrictMode(path, mode) {
|
|
4547
|
+
if (process.platform !== "win32")
|
|
4548
|
+
chmodSync2(path, mode);
|
|
4549
|
+
}
|
|
4550
|
+
function restrictExistingAuthorityArtifacts(storagePath, keyPath) {
|
|
4551
|
+
for (const path of [storagePath, `${storagePath}-wal`, `${storagePath}-shm`, keyPath]) {
|
|
4552
|
+
if (existsSync3(path))
|
|
4553
|
+
restrictMode(path, 384);
|
|
4554
|
+
}
|
|
4555
|
+
}
|
|
4556
|
+
function prepareDurableAuthority(storagePath) {
|
|
4557
|
+
const directory = dirname2(storagePath);
|
|
4558
|
+
const keyPath = getDeliveredMessageAuthorityKeyPath(storagePath);
|
|
4559
|
+
mkdirSync2(directory, { recursive: true, mode: 448 });
|
|
4560
|
+
restrictMode(directory, 448);
|
|
4561
|
+
restrictExistingAuthorityArtifacts(storagePath, keyPath);
|
|
4562
|
+
const databaseExists = existsSync3(storagePath);
|
|
4563
|
+
const keyExists = existsSync3(keyPath);
|
|
4564
|
+
if (databaseExists !== keyExists)
|
|
4565
|
+
return;
|
|
4566
|
+
if (!databaseExists) {
|
|
4567
|
+
const key2 = randomBytes(AUTHORITY_KEY_BYTES);
|
|
4568
|
+
writeFileSync2(keyPath, key2, { flag: "wx", mode: 384 });
|
|
4569
|
+
const databaseDescriptor = openSync2(storagePath, "wx", 384);
|
|
4570
|
+
closeSync2(databaseDescriptor);
|
|
4571
|
+
restrictExistingAuthorityArtifacts(storagePath, keyPath);
|
|
4572
|
+
return key2;
|
|
4573
|
+
}
|
|
4574
|
+
const key = readFileSync3(keyPath);
|
|
4575
|
+
if (key.length !== AUTHORITY_KEY_BYTES)
|
|
4576
|
+
return;
|
|
4577
|
+
return key;
|
|
4578
|
+
}
|
|
4579
|
+
|
|
3750
4580
|
class DeliveredMessageCache {
|
|
3751
4581
|
ttlMs;
|
|
3752
4582
|
maxEntries;
|
|
4583
|
+
maxBytes;
|
|
3753
4584
|
delivered = new Map;
|
|
3754
|
-
|
|
4585
|
+
retainedBytes = 0;
|
|
4586
|
+
database;
|
|
4587
|
+
authorityKey;
|
|
4588
|
+
storagePath;
|
|
4589
|
+
storageInvalid = false;
|
|
4590
|
+
constructor(ttlMs = DELIVERED_MESSAGE_TTL_MS, maxEntries = DELIVERED_MESSAGE_MAX_ENTRIES, storagePath, maxBytes = DELIVERED_MESSAGE_MAX_BYTES) {
|
|
3755
4591
|
this.ttlMs = ttlMs;
|
|
3756
4592
|
this.maxEntries = maxEntries;
|
|
4593
|
+
this.maxBytes = maxBytes;
|
|
4594
|
+
if (storagePath === undefined)
|
|
4595
|
+
return;
|
|
4596
|
+
this.storagePath = storagePath;
|
|
4597
|
+
let database;
|
|
4598
|
+
try {
|
|
4599
|
+
const authorityKey = prepareDurableAuthority(storagePath);
|
|
4600
|
+
if (authorityKey === undefined) {
|
|
4601
|
+
this.storageInvalid = true;
|
|
4602
|
+
return;
|
|
4603
|
+
}
|
|
4604
|
+
this.authorityKey = authorityKey;
|
|
4605
|
+
database = new DatabaseSync(storagePath);
|
|
4606
|
+
database.exec("PRAGMA busy_timeout = 5000");
|
|
4607
|
+
database.exec("PRAGMA journal_mode = WAL");
|
|
4608
|
+
database.exec("PRAGMA synchronous = FULL");
|
|
4609
|
+
database.exec("PRAGMA trusted_schema = OFF");
|
|
4610
|
+
database.exec(`
|
|
4611
|
+
CREATE TABLE IF NOT EXISTS delivered_messages (
|
|
4612
|
+
state TEXT NOT NULL CHECK (state IN ('reserved', 'accepted')),
|
|
4613
|
+
message_id TEXT PRIMARY KEY NOT NULL,
|
|
4614
|
+
signature TEXT NOT NULL,
|
|
4615
|
+
delivered_at INTEGER NOT NULL,
|
|
4616
|
+
target_identity TEXT,
|
|
4617
|
+
question_target_session_id TEXT,
|
|
4618
|
+
question_sender_group_identity TEXT
|
|
4619
|
+
) STRICT;
|
|
4620
|
+
CREATE INDEX IF NOT EXISTS delivered_messages_age
|
|
4621
|
+
ON delivered_messages(delivered_at);
|
|
4622
|
+
`);
|
|
4623
|
+
database.prepare("SELECT state, signature, delivered_at, target_identity FROM delivered_messages LIMIT 0").get();
|
|
4624
|
+
restrictExistingAuthorityArtifacts(storagePath, getDeliveredMessageAuthorityKeyPath(storagePath));
|
|
4625
|
+
} catch {
|
|
4626
|
+
database?.close();
|
|
4627
|
+
try {
|
|
4628
|
+
restrictExistingAuthorityArtifacts(storagePath, getDeliveredMessageAuthorityKeyPath(storagePath));
|
|
4629
|
+
} catch {}
|
|
4630
|
+
this.storageInvalid = true;
|
|
4631
|
+
return;
|
|
4632
|
+
}
|
|
4633
|
+
this.database = database;
|
|
3757
4634
|
}
|
|
3758
4635
|
lookup(messageId, signature, now = Date.now()) {
|
|
4636
|
+
const authority = this.signatureAuthority(signature);
|
|
4637
|
+
return authority === undefined ? "invalid" : this.lookupEntry(messageId, authority, undefined, false, now);
|
|
4638
|
+
}
|
|
4639
|
+
lookupForTarget(messageId, signature, targetIdentity, now = Date.now()) {
|
|
4640
|
+
const authority = this.signatureAuthority(signature);
|
|
4641
|
+
return authority === undefined ? "invalid" : this.lookupEntry(messageId, authority, targetIdentity, true, now);
|
|
4642
|
+
}
|
|
4643
|
+
lookupQuestionTarget(messageId, signature, senderGroupIdentity, now = Date.now()) {
|
|
4644
|
+
const authority = this.signatureAuthority(signature);
|
|
4645
|
+
if (authority === undefined)
|
|
4646
|
+
return;
|
|
3759
4647
|
this.prune(now);
|
|
3760
|
-
const delivered = this.
|
|
3761
|
-
if (
|
|
3762
|
-
return
|
|
3763
|
-
|
|
4648
|
+
const delivered = this.read(messageId);
|
|
4649
|
+
if (delivered === undefined || delivered === "invalid" || delivered.state !== "accepted" || now - delivered.deliveredAt > this.ttlMs || delivered.signature !== authority || delivered.questionRoute?.senderGroupIdentity !== senderGroupIdentity) {
|
|
4650
|
+
return;
|
|
4651
|
+
}
|
|
4652
|
+
return delivered.questionRoute.targetSessionId;
|
|
4653
|
+
}
|
|
4654
|
+
record(messageId, signature, now = Date.now(), targetIdentity) {
|
|
4655
|
+
return this.recordWithSignature(messageId, { state: "accepted", deliveredAt: now, signature, targetIdentity });
|
|
4656
|
+
}
|
|
4657
|
+
recordQuestion(messageId, signature, questionRoute, now = Date.now(), targetIdentity) {
|
|
4658
|
+
return this.recordWithSignature(messageId, {
|
|
4659
|
+
state: "accepted",
|
|
4660
|
+
deliveredAt: now,
|
|
4661
|
+
signature,
|
|
4662
|
+
targetIdentity,
|
|
4663
|
+
questionRoute
|
|
4664
|
+
});
|
|
4665
|
+
}
|
|
4666
|
+
reserve(messageId, signature, now = Date.now(), targetIdentity) {
|
|
4667
|
+
return this.recordWithSignature(messageId, { state: "reserved", deliveredAt: now, signature, targetIdentity });
|
|
4668
|
+
}
|
|
4669
|
+
reserveQuestion(messageId, signature, questionRoute, now = Date.now(), targetIdentity) {
|
|
4670
|
+
return this.recordWithSignature(messageId, {
|
|
4671
|
+
state: "reserved",
|
|
4672
|
+
deliveredAt: now,
|
|
4673
|
+
signature,
|
|
4674
|
+
targetIdentity,
|
|
4675
|
+
questionRoute
|
|
4676
|
+
});
|
|
4677
|
+
}
|
|
4678
|
+
accept(messageId, signature, targetIdentity) {
|
|
4679
|
+
const authority = this.signatureAuthority(signature);
|
|
4680
|
+
if (authority === undefined)
|
|
4681
|
+
return "invalid";
|
|
4682
|
+
const existing = this.read(messageId);
|
|
4683
|
+
if (existing === undefined || existing === "invalid")
|
|
4684
|
+
return "invalid";
|
|
4685
|
+
if (existing.signature !== authority || existing.targetIdentity !== targetIdentity)
|
|
4686
|
+
return "conflict";
|
|
4687
|
+
if (existing.state === "accepted")
|
|
4688
|
+
return "accepted";
|
|
4689
|
+
if (this.database === undefined) {
|
|
4690
|
+
existing.state = "accepted";
|
|
4691
|
+
return "accepted";
|
|
4692
|
+
}
|
|
4693
|
+
const updated = this.database.prepare("UPDATE delivered_messages SET state = 'accepted' WHERE message_id = ? AND signature = ?").run(messageId, authority);
|
|
4694
|
+
this.restrictArtifacts();
|
|
4695
|
+
return Number(updated.changes) === 1 ? "accepted" : "invalid";
|
|
4696
|
+
}
|
|
4697
|
+
forget(messageId, signature) {
|
|
4698
|
+
const authority = this.signatureAuthority(signature);
|
|
4699
|
+
if (authority === undefined)
|
|
4700
|
+
return;
|
|
4701
|
+
if (this.database === undefined) {
|
|
4702
|
+
const delivered = this.delivered.get(messageId);
|
|
4703
|
+
if (delivered?.signature === authority) {
|
|
4704
|
+
this.retainedBytes -= entryByteSize(messageId, delivered);
|
|
4705
|
+
this.delivered.delete(messageId);
|
|
4706
|
+
}
|
|
4707
|
+
return;
|
|
4708
|
+
}
|
|
4709
|
+
this.database.prepare("DELETE FROM delivered_messages WHERE message_id = ? AND signature = ?").run(messageId, authority);
|
|
4710
|
+
this.restrictArtifacts();
|
|
3764
4711
|
}
|
|
3765
|
-
|
|
4712
|
+
close() {
|
|
4713
|
+
this.restrictArtifacts();
|
|
4714
|
+
this.database?.close();
|
|
4715
|
+
this.restrictArtifacts();
|
|
4716
|
+
}
|
|
4717
|
+
signatureAuthority(signature) {
|
|
4718
|
+
if (this.storageInvalid || !validStoredString(signature))
|
|
4719
|
+
return;
|
|
4720
|
+
if (this.storagePath === undefined)
|
|
4721
|
+
return signature;
|
|
4722
|
+
if (this.authorityKey === undefined)
|
|
4723
|
+
return;
|
|
4724
|
+
return createHmac("sha256", this.authorityKey).update(signature).digest("hex");
|
|
4725
|
+
}
|
|
4726
|
+
recordWithSignature(messageId, delivered) {
|
|
4727
|
+
const authority = this.signatureAuthority(delivered.signature);
|
|
4728
|
+
if (authority === undefined)
|
|
4729
|
+
return "invalid";
|
|
4730
|
+
return this.recordEntry(messageId, { ...delivered, signature: authority });
|
|
4731
|
+
}
|
|
4732
|
+
lookupEntry(messageId, signature, targetIdentity, requireTargetIdentity, now) {
|
|
4733
|
+
if (this.storageInvalid)
|
|
4734
|
+
return "invalid";
|
|
3766
4735
|
this.prune(now);
|
|
3767
|
-
this.
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
4736
|
+
const delivered = this.read(messageId);
|
|
4737
|
+
if (delivered === "invalid")
|
|
4738
|
+
return "invalid";
|
|
4739
|
+
if (delivered === undefined || now - delivered.deliveredAt > this.ttlMs)
|
|
4740
|
+
return "miss";
|
|
4741
|
+
if (delivered.signature !== signature)
|
|
4742
|
+
return "conflict";
|
|
4743
|
+
if (requireTargetIdentity && delivered.targetIdentity !== targetIdentity)
|
|
4744
|
+
return "conflict";
|
|
4745
|
+
if (delivered.state === "reserved")
|
|
4746
|
+
return "uncertain";
|
|
4747
|
+
return "match";
|
|
4748
|
+
}
|
|
4749
|
+
recordEntry(messageId, delivered) {
|
|
4750
|
+
if (this.storageInvalid)
|
|
4751
|
+
return "invalid";
|
|
4752
|
+
if (!validStoredString(messageId) || !validStoredString(delivered.signature))
|
|
4753
|
+
return "invalid";
|
|
4754
|
+
if (!Number.isSafeInteger(delivered.deliveredAt) || delivered.deliveredAt < 0 || delivered.targetIdentity !== undefined && !validStoredString(delivered.targetIdentity) || delivered.questionRoute !== undefined && (!validStoredString(delivered.questionRoute.targetSessionId) || !validStoredString(delivered.questionRoute.senderGroupIdentity))) {
|
|
4755
|
+
return "invalid";
|
|
4756
|
+
}
|
|
4757
|
+
if (entryByteSize(messageId, delivered) > this.maxBytes)
|
|
4758
|
+
return "capacity";
|
|
4759
|
+
if (this.database === undefined)
|
|
4760
|
+
return this.recordMemory(messageId, delivered);
|
|
4761
|
+
return this.recordDurable(messageId, delivered);
|
|
4762
|
+
}
|
|
4763
|
+
recordMemory(messageId, delivered) {
|
|
4764
|
+
this.pruneMemory(delivered.deliveredAt);
|
|
4765
|
+
const existing = this.delivered.get(messageId);
|
|
4766
|
+
if (existing !== undefined)
|
|
4767
|
+
return this.compare(existing, delivered);
|
|
4768
|
+
const size = entryByteSize(messageId, delivered);
|
|
4769
|
+
if (this.delivered.size >= this.maxEntries || this.retainedBytes + size > this.maxBytes)
|
|
4770
|
+
return "capacity";
|
|
4771
|
+
this.delivered.set(messageId, delivered);
|
|
4772
|
+
this.retainedBytes += size;
|
|
4773
|
+
return "recorded";
|
|
4774
|
+
}
|
|
4775
|
+
recordDurable(messageId, delivered) {
|
|
4776
|
+
const database = this.database;
|
|
4777
|
+
if (database === undefined)
|
|
4778
|
+
return "invalid";
|
|
4779
|
+
database.exec("BEGIN IMMEDIATE");
|
|
4780
|
+
try {
|
|
4781
|
+
this.pruneDurable(delivered.deliveredAt);
|
|
4782
|
+
const existing = this.readDurable(messageId);
|
|
4783
|
+
if (existing !== undefined) {
|
|
4784
|
+
database.exec("COMMIT");
|
|
4785
|
+
return existing === "invalid" ? "invalid" : this.compare(existing, delivered);
|
|
4786
|
+
}
|
|
4787
|
+
const totals = database.prepare(`
|
|
4788
|
+
SELECT COUNT(*) AS count,
|
|
4789
|
+
COALESCE(SUM(
|
|
4790
|
+
length(CAST(message_id AS BLOB)) + length(CAST(signature AS BLOB)) +
|
|
4791
|
+
COALESCE(length(CAST(target_identity AS BLOB)), 0) +
|
|
4792
|
+
COALESCE(length(CAST(question_target_session_id AS BLOB)), 0) +
|
|
4793
|
+
COALESCE(length(CAST(question_sender_group_identity AS BLOB)), 0)
|
|
4794
|
+
), 0) AS bytes
|
|
4795
|
+
FROM delivered_messages
|
|
4796
|
+
`).get();
|
|
4797
|
+
const count = totals?.count;
|
|
4798
|
+
const bytes = totals?.bytes;
|
|
4799
|
+
if (typeof count !== "number" || !Number.isSafeInteger(count) || count < 0 || typeof bytes !== "number" || !Number.isSafeInteger(bytes) || bytes < 0) {
|
|
4800
|
+
database.exec("COMMIT");
|
|
4801
|
+
return "invalid";
|
|
4802
|
+
}
|
|
4803
|
+
if (count >= this.maxEntries || bytes + entryByteSize(messageId, delivered) > this.maxBytes) {
|
|
4804
|
+
database.exec("COMMIT");
|
|
4805
|
+
return "capacity";
|
|
4806
|
+
}
|
|
4807
|
+
database.prepare(`
|
|
4808
|
+
INSERT INTO delivered_messages (
|
|
4809
|
+
message_id, state, signature, delivered_at, target_identity,
|
|
4810
|
+
question_target_session_id, question_sender_group_identity
|
|
4811
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
4812
|
+
`).run(messageId, delivered.state, delivered.signature, delivered.deliveredAt, delivered.targetIdentity ?? null, delivered.questionRoute?.targetSessionId ?? null, delivered.questionRoute?.senderGroupIdentity ?? null);
|
|
4813
|
+
database.exec("COMMIT");
|
|
4814
|
+
this.restrictArtifacts();
|
|
4815
|
+
return "recorded";
|
|
4816
|
+
} catch (error) {
|
|
4817
|
+
try {
|
|
4818
|
+
database.exec("ROLLBACK");
|
|
4819
|
+
} catch {}
|
|
4820
|
+
throw error;
|
|
4821
|
+
}
|
|
4822
|
+
}
|
|
4823
|
+
compare(existing, incoming) {
|
|
4824
|
+
if (existing.signature !== incoming.signature || existing.targetIdentity !== incoming.targetIdentity) {
|
|
4825
|
+
return "conflict";
|
|
3774
4826
|
}
|
|
4827
|
+
return existing.state === "accepted" ? "match" : "uncertain";
|
|
4828
|
+
}
|
|
4829
|
+
read(messageId) {
|
|
4830
|
+
return this.database === undefined ? this.delivered.get(messageId) : this.readDurable(messageId);
|
|
4831
|
+
}
|
|
4832
|
+
readDurable(messageId) {
|
|
4833
|
+
const row = this.database?.prepare(`
|
|
4834
|
+
SELECT state, signature, delivered_at, target_identity,
|
|
4835
|
+
question_target_session_id, question_sender_group_identity
|
|
4836
|
+
FROM delivered_messages WHERE message_id = ?
|
|
4837
|
+
`).get(messageId);
|
|
4838
|
+
return parseStoredMessage(row);
|
|
3775
4839
|
}
|
|
3776
4840
|
prune(now) {
|
|
4841
|
+
if (this.database === undefined)
|
|
4842
|
+
this.pruneMemory(now);
|
|
4843
|
+
else
|
|
4844
|
+
this.pruneDurable(now);
|
|
4845
|
+
}
|
|
4846
|
+
pruneMemory(now) {
|
|
3777
4847
|
for (const [messageId, delivered] of this.delivered) {
|
|
3778
4848
|
if (now - delivered.deliveredAt <= this.ttlMs)
|
|
3779
|
-
|
|
4849
|
+
continue;
|
|
3780
4850
|
this.delivered.delete(messageId);
|
|
4851
|
+
this.retainedBytes -= entryByteSize(messageId, delivered);
|
|
3781
4852
|
}
|
|
3782
4853
|
}
|
|
4854
|
+
pruneDurable(now) {
|
|
4855
|
+
this.database?.prepare("DELETE FROM delivered_messages WHERE typeof(delivered_at) = 'integer' AND ? - delivered_at > ?").run(now, this.ttlMs);
|
|
4856
|
+
this.restrictArtifacts();
|
|
4857
|
+
}
|
|
4858
|
+
restrictArtifacts() {
|
|
4859
|
+
if (this.storagePath === undefined || this.storageInvalid)
|
|
4860
|
+
return;
|
|
4861
|
+
restrictExistingAuthorityArtifacts(this.storagePath, getDeliveredMessageAuthorityKeyPath(this.storagePath));
|
|
4862
|
+
}
|
|
3783
4863
|
}
|
|
3784
|
-
var
|
|
4864
|
+
var DELIVERED_MESSAGE_MAX_ENTRIES = 1e4, DELIVERED_MESSAGE_MAX_BYTES, AUTHORITY_KEY_BYTES = 32, AUTHORITY_DIGEST_PATTERN;
|
|
3785
4865
|
var init_delivered_message_cache = __esm(() => {
|
|
3786
|
-
|
|
4866
|
+
init_retry_policy();
|
|
4867
|
+
DELIVERED_MESSAGE_MAX_BYTES = 64 * 1024 * 1024;
|
|
4868
|
+
AUTHORITY_DIGEST_PATTERN = /^[a-f0-9]{64}$/u;
|
|
3787
4869
|
});
|
|
3788
4870
|
|
|
3789
4871
|
// dist/builtin/intercom/terminal-ordering-barrier.ts
|
|
@@ -4030,8 +5112,7 @@ async function dispatchRelayFallback(pi, prefix, terminalMessage, stageAdmission
|
|
|
4030
5112
|
}
|
|
4031
5113
|
function registerSubagentRelay(pi, deps) {
|
|
4032
5114
|
const { getLiveContext, currentSessionTargetMatches, sendIncomingMessage, ensureConnected, resolveSessionTarget: resolveSessionTarget2 } = deps;
|
|
4033
|
-
const localDeliveries = new DeliveredMessageCache;
|
|
4034
|
-
const localDeliveriesInFlight = new Map;
|
|
5115
|
+
const localDeliveries = deps.localDeliveryAuthority ?? new DeliveredMessageCache;
|
|
4035
5116
|
function deliverLocalSubagentRelayMessage(sender, status, messageText, terminalBarrier, requestId) {
|
|
4036
5117
|
const now = Date.now();
|
|
4037
5118
|
const entry = {
|
|
@@ -4124,37 +5205,33 @@ ${messageText}`,
|
|
|
4124
5205
|
}
|
|
4125
5206
|
async function deliverLocal(parsed, options) {
|
|
4126
5207
|
try {
|
|
4127
|
-
const signature =
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
5208
|
+
const signature = JSON.stringify({
|
|
5209
|
+
sender: options.sender,
|
|
5210
|
+
send: buildSendSignature(parsed.to, { text: parsed.message })
|
|
5211
|
+
});
|
|
5212
|
+
if (!parsed.requestId) {
|
|
5213
|
+
await deliverLocalSubagentRelayMessage(options.sender, options.status, parsed.message, options.terminalBarrier);
|
|
5214
|
+
acknowledgeResult(options, parsed.requestId, true);
|
|
5215
|
+
return;
|
|
4131
5216
|
}
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
localDeliveriesInFlight.delete(parsed.requestId);
|
|
4151
|
-
}).catch(() => {});
|
|
4152
|
-
} else if (!delivery) {
|
|
4153
|
-
delivery = deliverLocalSubagentRelayMessage(options.sender, options.status, parsed.message, options.terminalBarrier, parsed.requestId);
|
|
4154
|
-
}
|
|
4155
|
-
await delivery;
|
|
4156
|
-
if (parsed.requestId)
|
|
4157
|
-
localDeliveries.record(parsed.requestId, signature);
|
|
5217
|
+
const reservation = localDeliveries.reserve(parsed.requestId, signature);
|
|
5218
|
+
if (reservation === "match") {
|
|
5219
|
+
acknowledgeResult(options, parsed.requestId, true);
|
|
5220
|
+
return;
|
|
5221
|
+
}
|
|
5222
|
+
if (reservation !== "recorded") {
|
|
5223
|
+
const reason = reservation === "conflict" ? `Intercom message ID '${parsed.requestId}' was already delivered with a different sender, target, or payload` : reservation === "capacity" ? "Local Intercom delivery authority is at capacity; refusing delivery" : reservation === "uncertain" ? `Intercom message ID '${parsed.requestId}' has uncertain local delivery state; refusing replay` : "Local Intercom delivery authority is invalid; refusing delivery";
|
|
5224
|
+
throw new Error(reason);
|
|
5225
|
+
}
|
|
5226
|
+
try {
|
|
5227
|
+
await deliverLocalSubagentRelayMessage(options.sender, options.status, parsed.message, options.terminalBarrier, parsed.requestId);
|
|
5228
|
+
} catch (error) {
|
|
5229
|
+
localDeliveries.forget(parsed.requestId, signature);
|
|
5230
|
+
throw error;
|
|
5231
|
+
}
|
|
5232
|
+
const accepted = localDeliveries.accept(parsed.requestId, signature);
|
|
5233
|
+
if (accepted !== "accepted") {
|
|
5234
|
+
throw new Error(`Local Intercom delivery authority could not accept message '${parsed.requestId}'; refusing acknowledgement`);
|
|
4158
5235
|
}
|
|
4159
5236
|
acknowledgeResult(options, parsed.requestId, true);
|
|
4160
5237
|
} catch (error) {
|
|
@@ -4873,7 +5950,10 @@ async function settleOpenStageDeliveryFailure(input, error) {
|
|
|
4873
5950
|
}
|
|
4874
5951
|
|
|
4875
5952
|
// dist/builtin/intercom/closed-workflow-stage-message.ts
|
|
4876
|
-
function routeClosedWorkflowStageMessage(entry, admission, tracker, waiter, deliver, currentClient, isCurrent) {
|
|
5953
|
+
function routeClosedWorkflowStageMessage(entry, admission, tracker, waiter, deliver, currentClient, isCurrent, ownsSubagentRun) {
|
|
5954
|
+
const sourceRunId = entry.message.source?.subagentRunId;
|
|
5955
|
+
if (sourceRunId !== undefined && ownsSubagentRun(sourceRunId))
|
|
5956
|
+
return;
|
|
4877
5957
|
if (entry.message.expectsReply !== true) {
|
|
4878
5958
|
retryStableDelivery({ deliver, isCurrent }).catch(() => {});
|
|
4879
5959
|
return;
|
|
@@ -4920,15 +6000,6 @@ function clearRuntimeIntercomGroup(sessionId) {
|
|
|
4920
6000
|
}
|
|
4921
6001
|
var init_runtime_group = () => {};
|
|
4922
6002
|
|
|
4923
|
-
// dist/builtin/intercom/reconnect-backoff.ts
|
|
4924
|
-
function reconnectDelayMs(attempt) {
|
|
4925
|
-
return BACKOFF_MS[Math.min(attempt, BACKOFF_MS.length - 1)];
|
|
4926
|
-
}
|
|
4927
|
-
var BACKOFF_MS;
|
|
4928
|
-
var init_reconnect_backoff = __esm(() => {
|
|
4929
|
-
BACKOFF_MS = [1000, 2000, 5000, 1e4, 30000];
|
|
4930
|
-
});
|
|
4931
|
-
|
|
4932
6003
|
// dist/builtin/intercom/supervisor-authorization-registry.ts
|
|
4933
6004
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
4934
6005
|
|
|
@@ -5190,9 +6261,10 @@ function piIntercomExtension(pi, testOverrides = {}) {
|
|
|
5190
6261
|
if (receivedBeforeStageStart) {
|
|
5191
6262
|
return sendIncomingMessage(entry, "prelude", messageGeneration, false);
|
|
5192
6263
|
}
|
|
5193
|
-
const
|
|
6264
|
+
const stageAdmission = liveContext.orchestrationContext?.kind === "workflow-stage" ? liveContext.orchestrationContext.messageAdmission : undefined;
|
|
6265
|
+
const stageClosed = stageAdmission?.isOpen() === false;
|
|
5194
6266
|
if (stageClosed) {
|
|
5195
|
-
routeClosedWorkflowStageMessage(entry, inboundDeliveries, replyTracker, replyWaiters.pending(), () => sendIncomingMessage(entry, "trigger", messageGeneration, false), () => client, () => Boolean(getLiveContext(liveContext, messageGeneration)));
|
|
6267
|
+
routeClosedWorkflowStageMessage(entry, inboundDeliveries, replyTracker, replyWaiters.pending(), () => sendIncomingMessage(entry, "trigger", messageGeneration, false), () => client, () => Boolean(getLiveContext(liveContext, messageGeneration)), (runId) => stageAdmission.boundary.ownsSubagentRun(runId));
|
|
5196
6268
|
return;
|
|
5197
6269
|
}
|
|
5198
6270
|
const admission = inboundDeliveries.admit(from, message);
|
|
@@ -5378,6 +6450,9 @@ function piIntercomExtension(pi, testOverrides = {}) {
|
|
|
5378
6450
|
return reject(error);
|
|
5379
6451
|
}
|
|
5380
6452
|
}
|
|
6453
|
+
function relayStickyLiveDelivered(notice) {
|
|
6454
|
+
pi.events.emit(STICKY_LIVE_DELIVERED_EVENT, { handled: false, ...notice });
|
|
6455
|
+
}
|
|
5381
6456
|
function handlePendingStageNotification(nextClient, request) {
|
|
5382
6457
|
admitPendingStageNotification(nextClient, request).catch(() => false).then((delivered) => {
|
|
5383
6458
|
if (client !== nextClient || !nextClient.isConnected())
|
|
@@ -5401,6 +6476,10 @@ function piIntercomExtension(pi, testOverrides = {}) {
|
|
|
5401
6476
|
nextClient.on("pending_stage_notification", (request) => {
|
|
5402
6477
|
handlePendingStageNotification(nextClient, request);
|
|
5403
6478
|
});
|
|
6479
|
+
nextClient.on("sticky_live_delivered", (notice) => {
|
|
6480
|
+
if (client === nextClient)
|
|
6481
|
+
relayStickyLiveDelivered(notice);
|
|
6482
|
+
});
|
|
5404
6483
|
nextClient.on("peer_disconnected", (notice) => {
|
|
5405
6484
|
if (client !== nextClient) {
|
|
5406
6485
|
return;
|
|
@@ -5466,6 +6545,10 @@ function piIntercomExtension(pi, testOverrides = {}) {
|
|
|
5466
6545
|
schedulePendingStageRouteReconnect(runId, state);
|
|
5467
6546
|
});
|
|
5468
6547
|
nextClient.on("error", () => {});
|
|
6548
|
+
nextClient.on("sticky_live_delivered", (notice) => {
|
|
6549
|
+
if (state.client === nextClient)
|
|
6550
|
+
relayStickyLiveDelivered(notice);
|
|
6551
|
+
});
|
|
5469
6552
|
}
|
|
5470
6553
|
async function ensurePendingStageRouteClient(runId, route) {
|
|
5471
6554
|
const contextAtStart = getLiveContext();
|
|
@@ -5498,7 +6581,7 @@ function piIntercomExtension(pi, testOverrides = {}) {
|
|
|
5498
6581
|
pendingStageRouteClients.set(runId, state);
|
|
5499
6582
|
if (state.client?.isConnected()) {
|
|
5500
6583
|
state.route = route;
|
|
5501
|
-
state.client.registerPendingStageRoute(runId, normalizeGroup(route.group), route.capability, route.stages);
|
|
6584
|
+
state.client.registerPendingStageRoute(runId, normalizeGroup(route.group), route.capability, route.stages, route.possibleStages);
|
|
5502
6585
|
return;
|
|
5503
6586
|
}
|
|
5504
6587
|
if (state.promise) {
|
|
@@ -5515,7 +6598,7 @@ function piIntercomExtension(pi, testOverrides = {}) {
|
|
|
5515
6598
|
await nextClient.disconnect();
|
|
5516
6599
|
throw new Error("Intercom runtime no longer active");
|
|
5517
6600
|
}
|
|
5518
|
-
nextClient.registerPendingStageRoute(runId, normalizeGroup(route.group), route.capability, route.stages);
|
|
6601
|
+
nextClient.registerPendingStageRoute(runId, normalizeGroup(route.group), route.capability, route.stages, route.possibleStages);
|
|
5519
6602
|
await nextClient.listSessions();
|
|
5520
6603
|
if (!pendingStageRouteClientIsCurrent(runId, state, contextAtStart, generationAtStart)) {
|
|
5521
6604
|
await nextClient.disconnect();
|
|
@@ -5563,7 +6646,7 @@ function piIntercomExtension(pi, testOverrides = {}) {
|
|
|
5563
6646
|
async function registerPendingStageRoute(activeClient, runId, route) {
|
|
5564
6647
|
const routeGroup = normalizeGroup(route.group);
|
|
5565
6648
|
if (clientRegistrationGroup === routeGroup) {
|
|
5566
|
-
activeClient.registerPendingStageRoute(runId, routeGroup, route.capability, route.stages);
|
|
6649
|
+
activeClient.registerPendingStageRoute(runId, routeGroup, route.capability, route.stages, route.possibleStages);
|
|
5567
6650
|
return;
|
|
5568
6651
|
}
|
|
5569
6652
|
await ensurePendingStageRouteClient(runId, { ...route, group: routeGroup });
|
|
@@ -5596,15 +6679,18 @@ function piIntercomExtension(pi, testOverrides = {}) {
|
|
|
5596
6679
|
await testOverrides.beforeConnectAttempt?.(reason);
|
|
5597
6680
|
await spawnBrokerIfNeeded(config.brokerCommand, config.brokerArgs);
|
|
5598
6681
|
const childMetadata = currentChildOrchestratorMetadata();
|
|
5599
|
-
await nextClient.connect(registration, childMetadata?.supervisor, supervisorAuthorizations.ownerToken, readSubagentMessageSource(runtimeContext?.subagentPolicy));
|
|
5600
|
-
clientRegistrationGroup =
|
|
6682
|
+
await nextClient.connect(registration, childMetadata?.supervisor, supervisorAuthorizations.ownerToken, readSubagentMessageSource(runtimeContext?.subagentPolicy), resolveSessionHomeGroup());
|
|
6683
|
+
clientRegistrationGroup = resolveSessionHomeGroup();
|
|
5601
6684
|
await supervisorAuthorizations.restore(nextClient);
|
|
5602
6685
|
for (const [runId, route] of pendingStageRoutes) {
|
|
5603
6686
|
await registerPendingStageRoute(nextClient, runId, route);
|
|
5604
6687
|
}
|
|
5605
6688
|
const orchestration = contextAtStart.orchestrationContext;
|
|
5606
6689
|
if (orchestration?.kind === "workflow-stage" && orchestration.pendingStageDelivery !== undefined) {
|
|
5607
|
-
|
|
6690
|
+
const liveStageKeys = [orchestration.workflowStageId, orchestration.workflowStageName].filter((stageKey) => !stageKey.includes("/") && !stageKey.includes("*"));
|
|
6691
|
+
if (liveStageKeys.length > 0) {
|
|
6692
|
+
await nextClient.registerLiveWorkflowStageRoute(orchestration.workflowRunId, liveStageKeys, orchestration.pendingStageDelivery.routeCapability);
|
|
6693
|
+
}
|
|
5608
6694
|
}
|
|
5609
6695
|
if (!getLiveContext(contextAtStart, generationAtStart)) {
|
|
5610
6696
|
await nextClient.disconnect();
|
|
@@ -5647,7 +6733,8 @@ function piIntercomExtension(pi, testOverrides = {}) {
|
|
|
5647
6733
|
pendingStageRoutes.set(payload.runId, {
|
|
5648
6734
|
group: payload.group,
|
|
5649
6735
|
capability: payload.capability,
|
|
5650
|
-
...payload.stages === undefined ? {} : { stages: payload.stages }
|
|
6736
|
+
...payload.stages === undefined ? {} : { stages: payload.stages },
|
|
6737
|
+
...payload.possibleStages === undefined ? {} : { possibleStages: payload.possibleStages }
|
|
5651
6738
|
});
|
|
5652
6739
|
const completion = ensureConnected("background").then((activeClient) => registerPendingStageRoute(activeClient, payload.runId, payload));
|
|
5653
6740
|
payload.completion = completion;
|
|
@@ -5670,6 +6757,22 @@ function piIntercomExtension(pi, testOverrides = {}) {
|
|
|
5670
6757
|
return;
|
|
5671
6758
|
payload.handled = true;
|
|
5672
6759
|
const actionable = `Pending workflow stage could not receive intercom message: ${payload.reason}`;
|
|
6760
|
+
const selfClient = client;
|
|
6761
|
+
const notifiesThisSession = selfClient !== null && selfClient.isConnected() && (payload.senderId === selfClient.sessionId || payload.senderReturnAddress !== undefined && payload.senderReturnAddress === currentSessionId);
|
|
6762
|
+
if (notifiesThisSession && selfClient.sessionId !== null) {
|
|
6763
|
+
payload.completion = admitPendingStageNotification(selfClient, {
|
|
6764
|
+
requestId: `local:${payload.notificationId}`,
|
|
6765
|
+
from: { id: selfClient.sessionId, ...buildRegistration() },
|
|
6766
|
+
message: {
|
|
6767
|
+
id: payload.notificationId,
|
|
6768
|
+
timestamp: Date.now(),
|
|
6769
|
+
replyTo: payload.messageId,
|
|
6770
|
+
replyError: actionable,
|
|
6771
|
+
content: { text: actionable }
|
|
6772
|
+
}
|
|
6773
|
+
}).catch(() => false);
|
|
6774
|
+
return;
|
|
6775
|
+
}
|
|
5673
6776
|
payload.completion = pendingStageNotificationClient(payload.runId).then((activeClient) => {
|
|
5674
6777
|
const route = pendingStageRoutes.get(payload.runId);
|
|
5675
6778
|
if (route === undefined)
|
|
@@ -5798,7 +6901,7 @@ function piIntercomExtension(pi, testOverrides = {}) {
|
|
|
5798
6901
|
syncPresenceIdentity
|
|
5799
6902
|
});
|
|
5800
6903
|
}
|
|
5801
|
-
var INTERCOM_SESSION_ID_ENV, PENDING_STAGE_ROUTE_EVENT = "atomic:workflow-pending-stage-route", PENDING_STAGE_MESSAGE_EVENT = "atomic:workflow-pending-stage-message", PENDING_STAGE_UNDELIVERABLE_EVENT = "atomic:workflow-pending-stage-undeliverable";
|
|
6904
|
+
var INTERCOM_SESSION_ID_ENV, PENDING_STAGE_ROUTE_EVENT = "atomic:workflow-pending-stage-route", PENDING_STAGE_MESSAGE_EVENT = "atomic:workflow-pending-stage-message", PENDING_STAGE_UNDELIVERABLE_EVENT = "atomic:workflow-pending-stage-undeliverable", STICKY_LIVE_DELIVERED_EVENT = "atomic:workflow-sticky-live-delivered";
|
|
5802
6905
|
var init_index_heavy = __esm(() => {
|
|
5803
6906
|
init_client();
|
|
5804
6907
|
init_spawn();
|
|
@@ -5920,6 +7023,22 @@ function rejectLazyResultRelay(pi, eventName, payload, error) {
|
|
|
5920
7023
|
}
|
|
5921
7024
|
}
|
|
5922
7025
|
|
|
7026
|
+
// dist/builtin/intercom/index.ts
|
|
7027
|
+
init_recoverable_disconnect();
|
|
7028
|
+
|
|
7029
|
+
// dist/builtin/intercom/warm-up-exhaustion.ts
|
|
7030
|
+
class IntercomWarmUpExhaustedError extends Error {
|
|
7031
|
+
attempts;
|
|
7032
|
+
constructor(attempts, options) {
|
|
7033
|
+
super(`Intercom could not reach the broker after ${attempts} warm-up attempts.`, options);
|
|
7034
|
+
this.name = "IntercomWarmUpExhaustedError";
|
|
7035
|
+
this.attempts = attempts;
|
|
7036
|
+
}
|
|
7037
|
+
}
|
|
7038
|
+
|
|
7039
|
+
// dist/builtin/intercom/index.ts
|
|
7040
|
+
init_reconnect_backoff();
|
|
7041
|
+
|
|
5923
7042
|
// dist/builtin/intercom/lazy-heavy-proxy.ts
|
|
5924
7043
|
var LIVE_EVENT_SUBSCRIPTIONS = new Set(["pi-intercom:detach-response"]);
|
|
5925
7044
|
function createForwardedHandlerMap() {
|
|
@@ -6050,9 +7169,12 @@ function renderHeavyToolResult(loadedHeavy, name, args) {
|
|
|
6050
7169
|
return renderer(...args);
|
|
6051
7170
|
return renderIntercomToolResult(name, args);
|
|
6052
7171
|
}
|
|
6053
|
-
function
|
|
6054
|
-
|
|
7172
|
+
function reportRelayFailure(eventName, error) {
|
|
7173
|
+
if (isRecoverableIntercomDisconnect(error))
|
|
7174
|
+
return;
|
|
7175
|
+
console.error(`Intercom event relay failed (${eventName}):`, error);
|
|
6055
7176
|
}
|
|
7177
|
+
var WARM_UP_RETRY_ATTEMPTS = 5;
|
|
6056
7178
|
function intercom(pi, options = {}) {
|
|
6057
7179
|
const inheritedDelegatedSessionName = readSubagentEnv("INTERCOM_SESSION_NAME");
|
|
6058
7180
|
let heavyAttempt = null;
|
|
@@ -6206,13 +7328,73 @@ function intercom(pi, options = {}) {
|
|
|
6206
7328
|
}, (error) => {
|
|
6207
7329
|
if (heavyAttempt?.promise === promise)
|
|
6208
7330
|
heavyAttempt = null;
|
|
6209
|
-
if (!
|
|
7331
|
+
if (!isRecoverableIntercomDisconnect(error)) {
|
|
6210
7332
|
const message = error instanceof Error ? error.message : String(error);
|
|
6211
7333
|
console.error(`Intercom heavy initialization failed; a later call will retry: ${message}`, error);
|
|
6212
7334
|
}
|
|
6213
7335
|
});
|
|
6214
7336
|
return promise;
|
|
6215
7337
|
}
|
|
7338
|
+
let warmUpRetry = null;
|
|
7339
|
+
function cancelWarmUpRetry() {
|
|
7340
|
+
if (!warmUpRetry)
|
|
7341
|
+
return;
|
|
7342
|
+
warmUpRetry.cancelled = true;
|
|
7343
|
+
if (warmUpRetry.timer)
|
|
7344
|
+
clearTimeout(warmUpRetry.timer);
|
|
7345
|
+
warmUpRetry = null;
|
|
7346
|
+
}
|
|
7347
|
+
function warmUpRetryDelay(attempt) {
|
|
7348
|
+
const schedule = options.warmUpRetryDelaysMs;
|
|
7349
|
+
if (schedule)
|
|
7350
|
+
return schedule[attempt];
|
|
7351
|
+
return attempt < WARM_UP_RETRY_ATTEMPTS ? reconnectDelayMs(attempt) : undefined;
|
|
7352
|
+
}
|
|
7353
|
+
function scheduleWarmUpRetry(ctx, lease, generation, pendingStageDelivery, initialError) {
|
|
7354
|
+
if (warmUpRetry)
|
|
7355
|
+
return;
|
|
7356
|
+
const state = { lease, generation, timer: null, cancelled: false };
|
|
7357
|
+
warmUpRetry = state;
|
|
7358
|
+
let lastError = initialError;
|
|
7359
|
+
const clearOwner = () => {
|
|
7360
|
+
if (warmUpRetry === state)
|
|
7361
|
+
warmUpRetry = null;
|
|
7362
|
+
};
|
|
7363
|
+
const stale = () => state.cancelled || activeLease !== lease || lease.retired || sessionSnapshot?.generation !== generation;
|
|
7364
|
+
const attemptAt = (attempt) => {
|
|
7365
|
+
const delay = warmUpRetryDelay(attempt);
|
|
7366
|
+
if (delay === undefined) {
|
|
7367
|
+
clearOwner();
|
|
7368
|
+
const exhausted = new IntercomWarmUpExhaustedError(attempt, lastError instanceof Error ? { cause: lastError } : undefined);
|
|
7369
|
+
try {
|
|
7370
|
+
pendingStageDelivery.fail(exhausted);
|
|
7371
|
+
} catch {}
|
|
7372
|
+
return;
|
|
7373
|
+
}
|
|
7374
|
+
const timer = setTimeout(() => {
|
|
7375
|
+
state.timer = null;
|
|
7376
|
+
if (stale()) {
|
|
7377
|
+
clearOwner();
|
|
7378
|
+
return;
|
|
7379
|
+
}
|
|
7380
|
+
loadHeavy(ctx).then(clearOwner, (error) => {
|
|
7381
|
+
lastError = error;
|
|
7382
|
+
if (stale()) {
|
|
7383
|
+
clearOwner();
|
|
7384
|
+
return;
|
|
7385
|
+
}
|
|
7386
|
+
if (!isRecoverableIntercomDisconnect(error)) {
|
|
7387
|
+
clearOwner();
|
|
7388
|
+
return;
|
|
7389
|
+
}
|
|
7390
|
+
attemptAt(attempt + 1);
|
|
7391
|
+
});
|
|
7392
|
+
}, delay);
|
|
7393
|
+
timer.unref?.();
|
|
7394
|
+
state.timer = timer;
|
|
7395
|
+
};
|
|
7396
|
+
attemptAt(0);
|
|
7397
|
+
}
|
|
6216
7398
|
let typedContactSupervisorRegistered = hasSubagentIntercomEnv();
|
|
6217
7399
|
const activateTypedContactSupervisor = () => {
|
|
6218
7400
|
const activeTools = pi.getActiveTools();
|
|
@@ -6259,8 +7441,16 @@ function intercom(pi, options = {}) {
|
|
|
6259
7441
|
}
|
|
6260
7442
|
const generation = ++lifecycleGeneration;
|
|
6261
7443
|
sessionSnapshot = { event, ctx, generation, lease };
|
|
7444
|
+
cancelWarmUpRetry();
|
|
6262
7445
|
if (ctx.orchestrationContext?.kind === "workflow-stage" && ctx.orchestrationContext.pendingStageDelivery !== undefined) {
|
|
6263
|
-
|
|
7446
|
+
const pendingStageDelivery = ctx.orchestrationContext.pendingStageDelivery;
|
|
7447
|
+
try {
|
|
7448
|
+
await loadHeavy(ctx);
|
|
7449
|
+
} catch (error) {
|
|
7450
|
+
if (!isRecoverableIntercomDisconnect(error))
|
|
7451
|
+
throw error;
|
|
7452
|
+
scheduleWarmUpRetry(ctx, lease, generation, pendingStageDelivery, error);
|
|
7453
|
+
}
|
|
6264
7454
|
} else if (loadedHeavy) {
|
|
6265
7455
|
await ensureSessionStartReplayed(loadedHeavy.heavy, lease);
|
|
6266
7456
|
}
|
|
@@ -6269,6 +7459,7 @@ function intercom(pi, options = {}) {
|
|
|
6269
7459
|
const lease = activeLease;
|
|
6270
7460
|
const generation = ++lifecycleGeneration;
|
|
6271
7461
|
retireLifecycleLease(lease, { event, ctx, generation });
|
|
7462
|
+
cancelWarmUpRetry();
|
|
6272
7463
|
const retiredHeavy = loadedHeavy?.heavy ?? null;
|
|
6273
7464
|
const retiredAttempt = heavyAttempt?.lease === lease ? heavyAttempt.promise : null;
|
|
6274
7465
|
const retiredReplay = replayAttempt?.lease === lease ? replayAttempt.promise : null;
|
|
@@ -6377,6 +7568,10 @@ function intercom(pi, options = {}) {
|
|
|
6377
7568
|
if (!forwarded.completion)
|
|
6378
7569
|
throw new Error("Intercom supervisor authorization provider is unavailable");
|
|
6379
7570
|
return await forwarded.completion;
|
|
7571
|
+
}).catch((error) => {
|
|
7572
|
+
if (isRecoverableIntercomDisconnect(error))
|
|
7573
|
+
return;
|
|
7574
|
+
throw error;
|
|
6380
7575
|
});
|
|
6381
7576
|
});
|
|
6382
7577
|
pi.events.on(PENDING_STAGE_UNDELIVERABLE_EVENT2, (payload) => {
|
|
@@ -6390,7 +7585,7 @@ function intercom(pi, options = {}) {
|
|
|
6390
7585
|
handle.assertCurrent();
|
|
6391
7586
|
return forwarded.handled === true && forwarded.completion !== undefined ? await forwarded.completion : false;
|
|
6392
7587
|
}).catch((error) => {
|
|
6393
|
-
|
|
7588
|
+
reportRelayFailure(PENDING_STAGE_UNDELIVERABLE_EVENT2, error);
|
|
6394
7589
|
return false;
|
|
6395
7590
|
});
|
|
6396
7591
|
});
|
|
@@ -6415,7 +7610,7 @@ function intercom(pi, options = {}) {
|
|
|
6415
7610
|
}
|
|
6416
7611
|
completion.catch((error) => {
|
|
6417
7612
|
rejectLazyResultRelay(pi, eventName, payload, error);
|
|
6418
|
-
|
|
7613
|
+
reportRelayFailure(eventName, error);
|
|
6419
7614
|
});
|
|
6420
7615
|
});
|
|
6421
7616
|
}
|
|
@@ -6438,7 +7633,8 @@ function intercom(pi, options = {}) {
|
|
|
6438
7633
|
description: `Send a message to another local agent session running on this machine.
|
|
6439
7634
|
Use this to communicate findings, request help, or coordinate work with other sessions.
|
|
6440
7635
|
Sessions belong to an intercom group and can ONLY message sessions in the same group; cross-group sends are rejected by the broker. Ungrouped sessions share the "default" group.
|
|
6441
|
-
For send, live session names and exact full session IDs remain supported.
|
|
7636
|
+
For send, live session names and exact full session IDs remain supported. Workflow-stage targets use \`workflow:<rootRunId>/<segment>[/<segment>...]\`; \`*\` matches one segment and \`**\` any depth. Use \`intercom list\` inside the invocation group to see live, pending, and possible future targets with queued counts. \`workflow:<rootRunId>/**\` reaches live stages now and remains sticky for every future stage until root termination; valid targets outside the known set queue with a \`notInKnownSet\` warning and settle undeliverable at terminal only if never delivered. Use \`ask\` only on live targets.
|
|
7637
|
+
If a \`send\`, \`ask\`, or \`reply\` fails with \`Client disconnected\`, retry that exact operation with the returned \`retryToken\`, up to three claimed attempts; omit the token for every fresh operation.
|
|
6442
7638
|
Usage:
|
|
6443
7639
|
intercom({ action: "list" }) → List sessions in your group
|
|
6444
7640
|
intercom({ action: "list", group: "name" }) → Read-only peek at another group's sessions
|
|
@@ -6447,14 +7643,15 @@ Usage:
|
|
|
6447
7643
|
intercom({ action: "send", to: "session-name", message: "..." }) → Send message (own group only)
|
|
6448
7644
|
intercom({ action: "ask", to: "session-name", message: "..." }) → Ask and wait for reply
|
|
6449
7645
|
intercom({ action: "reply", message: "..." }) → Reply to the active or exact pending ask
|
|
7646
|
+
intercom({ action: "send", to: "session-name", message: "...", retryToken: "..." }) → Claim the exact retry returned by a retryable failure
|
|
6450
7647
|
intercom({ action: "pending" }) → List unresolved inbound asks
|
|
6451
7648
|
intercom({ action: "status" }) → Show connection status and your group
|
|
6452
7649
|
|
|
6453
7650
|
"default" is the shared group; "true" and "auto" are reserved for subagent auto-groups. Joining does not grant cross-group access; contact_supervisor remains the only cross-group path.`,
|
|
6454
|
-
promptSnippet: "Use to coordinate with other local agent sessions in your intercom group
|
|
7651
|
+
promptSnippet: "Use to coordinate with other local agent sessions in your intercom group. For a retryable send/ask/reply failure, repeat the exact operation with its returned retryToken; omit retryToken for fresh operations.",
|
|
6455
7652
|
parameters: Type3.Object({
|
|
6456
7653
|
action: Type3.String({ description: "Action: 'list', 'join', 'leave', 'send', 'ask', 'reply', 'pending', or 'status'" }),
|
|
6457
|
-
to: Type3.Optional(Type3.String({ description: "Live session name, exact full session ID, or
|
|
7654
|
+
to: Type3.Optional(Type3.String({ description: "Live session name, exact full session ID, or `workflow:<rootRunId>/<segment>[/<segment>...]` path; `*` matches one segment and `**` any depth. Send queues sticky pending/future delivery and `workflow:<rootRunId>/**` broadcasts to live and future stages; use `ask` only on live targets (for 'send', 'ask', or targeted 'reply')" })),
|
|
6458
7655
|
message: Type3.Optional(Type3.String({ description: "Message to send (for 'send', 'ask', or 'reply' action)" })),
|
|
6459
7656
|
attachments: Type3.Optional(Type3.Array(Type3.Object({
|
|
6460
7657
|
type: Type3.Union([Type3.Literal("file"), Type3.Literal("snippet"), Type3.Literal("context")]),
|
|
@@ -6463,6 +7660,7 @@ Usage:
|
|
|
6463
7660
|
language: Type3.Optional(Type3.String())
|
|
6464
7661
|
}))),
|
|
6465
7662
|
replyTo: Type3.Optional(Type3.String({ description: "Exact pending-ask message ID; disambiguates concurrent asks, including asks from one sender" })),
|
|
7663
|
+
retryToken: Type3.Optional(Type3.String({ minLength: 1, maxLength: 128, description: "Opaque token returned after a retryable send/ask/reply failure. Repeat the exact same action and arguments with it; omit it for fresh operations." })),
|
|
6466
7664
|
group: Type3.Optional(Type3.String({ description: "Group name for 'join'; read-only group filter for 'list'/'status'. 'send'/'ask' are locked to your own group." }))
|
|
6467
7665
|
}),
|
|
6468
7666
|
execute: (...args) => executeHeavyTool(loadHeavy, "intercom", args),
|