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