@selesai/code 0.5.27 → 0.5.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +9 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/config-selector.d.ts.map +1 -1
- package/dist/cli/config-selector.js +1 -1
- package/dist/cli/config-selector.js.map +1 -1
- package/dist/cli/credential-print.d.ts +23 -0
- package/dist/cli/credential-print.d.ts.map +1 -0
- package/dist/cli/credential-print.js +117 -0
- package/dist/cli/credential-print.js.map +1 -0
- package/dist/cli/startup-ui.d.ts.map +1 -1
- package/dist/cli/startup-ui.js +1 -1
- package/dist/cli/startup-ui.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +3 -0
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session.d.ts +12 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +20 -14
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +11 -3
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +1 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +11 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +14 -1
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +10 -0
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +1 -1
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/llama/provider.d.ts.map +1 -1
- package/dist/core/llama/provider.js +8 -3
- package/dist/core/llama/provider.js.map +1 -1
- package/dist/core/model-config.d.ts +30 -0
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +6 -0
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +2 -2
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts +1 -0
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +20 -3
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/model-runtime.d.ts +2 -0
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +5 -4
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +13 -6
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/remote-catalog-provider.d.ts +1 -1
- package/dist/core/remote-catalog-provider.d.ts.map +1 -1
- package/dist/core/remote-catalog-provider.js +24 -11
- package/dist/core/remote-catalog-provider.js.map +1 -1
- package/dist/core/resource-loader.d.ts +15 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +66 -9
- package/dist/core/resource-loader.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/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +1 -1
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/bash.d.ts +2 -0
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +34 -5
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
- package/dist/core/tools/tool-definition-wrapper.js +3 -1
- package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
- package/dist/extensions/pi-intercom/CHANGELOG.md +249 -0
- package/dist/extensions/pi-intercom/README.md +102 -39
- package/dist/extensions/pi-intercom/broker/broker.ts +1233 -36
- package/dist/extensions/pi-intercom/broker/client.test.ts +83 -0
- package/dist/extensions/pi-intercom/broker/client.ts +315 -12
- package/dist/extensions/pi-intercom/broker/extension-state.ts +186 -0
- package/dist/extensions/pi-intercom/broker/extension.test.ts +387 -0
- package/dist/extensions/pi-intercom/broker/framing.test.ts +114 -0
- package/dist/extensions/pi-intercom/broker/framing.ts +82 -24
- package/dist/extensions/pi-intercom/broker/paths.test.ts +153 -0
- package/dist/extensions/pi-intercom/broker/paths.ts +117 -8
- package/dist/extensions/pi-intercom/broker/runtime-claim.test.ts +34 -0
- package/dist/extensions/pi-intercom/broker/runtime-claim.ts +21 -0
- package/dist/extensions/pi-intercom/broker/spawn.test.ts +160 -23
- package/dist/extensions/pi-intercom/broker/spawn.ts +113 -27
- package/dist/extensions/pi-intercom/config.test.ts +93 -0
- package/dist/extensions/pi-intercom/config.ts +55 -6
- package/dist/extensions/pi-intercom/cwd.test.ts +40 -0
- package/dist/extensions/pi-intercom/cwd.ts +31 -0
- package/dist/extensions/pi-intercom/extension-api.ts +44 -0
- package/dist/extensions/pi-intercom/format-context.test.ts +31 -0
- package/dist/extensions/pi-intercom/format-context.ts +32 -0
- package/dist/extensions/pi-intercom/index.ts +742 -145
- package/dist/extensions/pi-intercom/intercom.integration.test.ts +2646 -0
- package/dist/extensions/pi-intercom/package.json +15 -5
- package/dist/extensions/pi-intercom/reply-tracker.test.ts +134 -0
- package/dist/extensions/pi-intercom/reply-tracker.ts +31 -13
- package/dist/extensions/pi-intercom/skills/pi-intercom/SKILL.md +13 -11
- package/dist/extensions/pi-intercom/test/inline-message.test.ts +184 -0
- package/dist/extensions/pi-intercom/test/overlay-width.test.ts +66 -0
- package/dist/extensions/pi-intercom/types.ts +94 -4
- package/dist/extensions/pi-intercom/ui/compose.ts +8 -4
- package/dist/extensions/pi-intercom/ui/inline-message.ts +61 -25
- package/dist/extensions/pi-intercom/ui/session-list.ts +7 -3
- package/dist/extensions/pi-subagents/CHANGELOG.md +85 -0
- package/dist/extensions/pi-subagents/LICENSE +21 -0
- package/dist/extensions/pi-subagents/README.md +146 -52
- package/dist/extensions/pi-subagents/agents/architect.md +1 -0
- package/dist/extensions/pi-subagents/agents/builder.md +1 -0
- package/dist/extensions/pi-subagents/package-lock.json +2 -2
- package/dist/extensions/pi-subagents/package.json +3 -3
- package/dist/extensions/pi-subagents/prompts/review-loop.md +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +21 -989
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +256 -0
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +430 -0
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +144 -0
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +282 -0
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +71 -30
- package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +4 -0
- package/dist/extensions/pi-subagents/src/agents/agents.ts +118 -9
- package/dist/extensions/pi-subagents/src/agents/skills.ts +14 -12
- package/dist/extensions/pi-subagents/src/api/delegation.ts +3 -0
- package/dist/extensions/pi-subagents/src/api/preflight.ts +16 -12
- package/dist/extensions/pi-subagents/src/extension/chain-validation.ts +17 -1
- package/dist/extensions/pi-subagents/src/extension/index.ts +17 -7
- package/dist/extensions/pi-subagents/src/extension/rpc.ts +248 -6
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +31 -6
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +8 -7
- package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +9 -4
- package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +33 -4
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +63 -16
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +24 -13
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +7 -5
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +18 -2
- package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +48 -5
- package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +68 -1
- package/dist/extensions/pi-subagents/src/runs/background/notify.ts +29 -4
- package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +29 -6
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +29 -3
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +372 -28
- package/dist/extensions/pi-subagents/src/runs/foreground/async-stop-action.ts +65 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-clarify.ts +3 -3
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +143 -43
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +527 -247
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +42 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +426 -123
- package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +15 -7
- package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +51 -19
- package/dist/extensions/pi-subagents/src/runs/shared/chain-outputs.ts +3 -1
- package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +44 -11
- package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +8 -0
- package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +97 -20
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +43 -12
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +52 -4
- package/dist/extensions/pi-subagents/src/runs/shared/process-signal.ts +19 -0
- package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +45 -9
- package/dist/extensions/pi-subagents/src/runs/shared/runtime-acknowledged-extensions.ts +71 -0
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +31 -1
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +101 -0
- package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/usage-budget.ts +65 -0
- package/dist/extensions/pi-subagents/src/runs/shared/workflow-graph.ts +26 -1
- package/dist/extensions/pi-subagents/src/shared/settings.ts +17 -1
- package/dist/extensions/pi-subagents/src/shared/types.ts +155 -10
- package/dist/extensions/pi-subagents/src/shared/utils.ts +73 -4
- package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +5 -0
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +46 -4
- package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +5 -3
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +87 -24
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +182 -9
- package/dist/extensions/pi-subagents/src/tui/render.ts +27 -28
- package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +14 -7
- package/dist/extensions/pi-subagents/src/watchdog/review.ts +5 -4
- package/dist/extensions/pi-subagents/src/watchdog/runtime.ts +170 -17
- package/dist/extensions/pi-subagents/src/watchdog/scope.ts +62 -0
- package/dist/extensions/pi-subagents/src/watchdog/settings.ts +41 -1
- package/dist/extensions/pi-subagents/src/watchdog/types.ts +10 -0
- package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +348 -7
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +84 -0
- package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +4 -2
- package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +51 -2
- package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +26 -1
- package/dist/extensions/pi-subagents/test/integration/detect-error.test.ts +48 -0
- package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +3 -3
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +213 -13
- package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +34 -0
- package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +44 -0
- package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +118 -12
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +850 -7
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +112 -23
- package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +17 -5
- package/dist/extensions/pi-subagents/test/support/mock-pi.ts +2 -0
- package/dist/extensions/pi-subagents/test/support/register-loader.mjs +3 -3
- package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +3 -1
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +128 -0
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +47 -2
- package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +60 -0
- package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +8 -0
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling-agent-allowlist.test.ts +102 -0
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/chain-append.test.ts +6 -0
- package/dist/extensions/pi-subagents/test/unit/chain-validation.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +48 -48
- package/dist/extensions/pi-subagents/test/unit/config-dir-runtime.test.ts +23 -0
- package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +27 -0
- package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +4 -2
- package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +176 -8
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +164 -5
- package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +41 -1
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +11 -11
- package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +18 -2
- package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +20 -1
- package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +30 -2
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +10 -2
- package/dist/extensions/pi-subagents/test/unit/parallel-utils.test.ts +22 -0
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +103 -0
- package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +18 -0
- package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +46 -0
- package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +13 -1
- package/dist/extensions/pi-subagents/test/unit/result-intercom.test.ts +29 -4
- package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +223 -4
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +30 -0
- package/dist/extensions/pi-subagents/test/unit/runtime-acknowledged-extensions.test.ts +52 -0
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +34 -2
- package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/streamed-progress-bounds.test.ts +78 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +41 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +77 -0
- package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +3 -0
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/total-cost.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +210 -1
- package/dist/extensions/pi-subagents/test/unit/watchdog-scope.test.ts +33 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +32 -0
- package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +1 -1
- package/dist/extensions/question/index.ts +14 -1
- package/dist/extensions/question/tests/wizard.test.ts +11 -8
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +43 -0
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/custom-message.d.ts +3 -1
- package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-message.js +10 -2
- package/dist/modes/interactive/components/custom-message.js.map +1 -1
- package/dist/modes/interactive/components/extension-editor.d.ts +1 -2
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js +16 -46
- package/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +4 -1
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.js +22 -13
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/dist/modes/interactive/external-editor.d.ts +12 -0
- package/dist/modes/interactive/external-editor.d.ts.map +1 -0
- package/dist/modes/interactive/external-editor.js +37 -0
- package/dist/modes/interactive/external-editor.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +72 -64
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +14 -0
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/skills/batch-grill-me/SKILL.md +3 -1
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +19 -8
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js +1 -1
- package/dist/utils/version-check.js.map +1 -1
- package/docs/compaction.md +1 -1
- package/docs/custom-provider.md +14 -5
- package/docs/environment-variables.md +86 -0
- package/docs/extensions.md +15 -5
- package/docs/index.md +1 -0
- package/docs/models.md +9 -2
- package/docs/providers.md +21 -2
- package/docs/rpc.md +23 -6
- package/docs/session-format.md +2 -0
- package/docs/settings.md +1 -1
- package/docs/usage.md +0 -13
- package/examples/extensions/custom-compaction.ts +5 -2
- package/examples/extensions/custom-provider-anthropic/index.ts +9 -3
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/handoff.ts +11 -3
- package/examples/extensions/message-renderer.ts +3 -3
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/summarize.ts +5 -2
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/sdk/12-full-control.ts +3 -1
- package/package.json +15 -6
- package/dist/extensions/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { IntercomClient } from "./client.ts";
|
|
4
|
+
|
|
5
|
+
test("validated session lifecycle messages reach broker-message subscribers", () => {
|
|
6
|
+
const client = new IntercomClient();
|
|
7
|
+
(client as any)._sessionId = "session-1";
|
|
8
|
+
const received: unknown[] = [];
|
|
9
|
+
client.onBrokerMessage((message) => received.push(message));
|
|
10
|
+
const session = {
|
|
11
|
+
id: "session-2",
|
|
12
|
+
cwd: "/test",
|
|
13
|
+
model: "test",
|
|
14
|
+
pid: 2,
|
|
15
|
+
startedAt: 1,
|
|
16
|
+
lastActivity: 1,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
(client as any).handleBrokerMessage({ type: "session_joined", session });
|
|
20
|
+
(client as any).handleBrokerMessage({ type: "presence_update", session });
|
|
21
|
+
(client as any).handleBrokerMessage({ type: "session_left", sessionId: "session-2" });
|
|
22
|
+
|
|
23
|
+
assert.deepEqual(received, [
|
|
24
|
+
{ type: "session_joined", session },
|
|
25
|
+
{ type: "presence_update", session },
|
|
26
|
+
{ type: "session_left", sessionId: "session-2" },
|
|
27
|
+
]);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("registered feature negotiation rejects non-string feature entries", () => {
|
|
31
|
+
const client = new IntercomClient();
|
|
32
|
+
assert.throws(
|
|
33
|
+
() => (client as any).handleBrokerMessage({ type: "registered", sessionId: "session-1", features: ["valid", 123] }),
|
|
34
|
+
/Invalid registered features/,
|
|
35
|
+
);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("malformed extension broker messages are rejected", () => {
|
|
39
|
+
const client = new IntercomClient();
|
|
40
|
+
(client as any)._sessionId = "session-1";
|
|
41
|
+
|
|
42
|
+
assert.throws(
|
|
43
|
+
() => (client as any).handleBrokerMessage({ type: "extension_owner", namespace: "test/v1", ownerId: "owner" }),
|
|
44
|
+
/Invalid extension_owner/,
|
|
45
|
+
);
|
|
46
|
+
assert.throws(
|
|
47
|
+
() => (client as any).handleBrokerMessage({ type: "extension_owner", namespace: "test/v1", ownerEpoch: "epoch" }),
|
|
48
|
+
/Invalid extension_owner/,
|
|
49
|
+
);
|
|
50
|
+
assert.throws(
|
|
51
|
+
() => (client as any).handleBrokerMessage({ type: "extension_message", namespace: "test/v1" }),
|
|
52
|
+
/Invalid extension_message/,
|
|
53
|
+
);
|
|
54
|
+
assert.throws(
|
|
55
|
+
() => (client as any).handleBrokerMessage({ type: "extension_state", namespace: "test/v1", revision: -1 }),
|
|
56
|
+
/Invalid extension_state/,
|
|
57
|
+
);
|
|
58
|
+
assert.throws(
|
|
59
|
+
() => (client as any).handleBrokerMessage({ type: "extension_state_result", namespace: "test/v1", committed: "yes", revision: 1 }),
|
|
60
|
+
/Invalid extension_state_result/,
|
|
61
|
+
);
|
|
62
|
+
assert.doesNotThrow(() => (client as any).handleBrokerMessage({
|
|
63
|
+
type: "extension_message",
|
|
64
|
+
namespace: "test/v1",
|
|
65
|
+
fromSessionId: "session-2",
|
|
66
|
+
payload: { peerOnly: true },
|
|
67
|
+
}));
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("cancelAsk ignores synchronous socket write failures", () => {
|
|
71
|
+
const client = new IntercomClient();
|
|
72
|
+
(client as any)._sessionId = "session-1";
|
|
73
|
+
(client as any).socket = {
|
|
74
|
+
destroyed: false,
|
|
75
|
+
writableEnded: false,
|
|
76
|
+
writable: true,
|
|
77
|
+
write() {
|
|
78
|
+
throw new Error("write failed");
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
assert.doesNotThrow(() => client.cancelAsk("ask-1"));
|
|
83
|
+
});
|
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
import { EventEmitter } from "events";
|
|
2
2
|
import net from "net";
|
|
3
3
|
import { randomUUID } from "crypto";
|
|
4
|
-
import { writeMessage, createMessageReader } from "./framing.
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
import { writeMessage, createMessageReader } from "./framing.ts";
|
|
5
|
+
import { getBrokerConnectTarget, type BrokerConnectTarget } from "./paths.ts";
|
|
6
|
+
import { EXTENSION_BUS_FEATURE } from "../types.ts";
|
|
7
|
+
import type {
|
|
8
|
+
Attachment,
|
|
9
|
+
BrokerMessage,
|
|
10
|
+
ClientMessage,
|
|
11
|
+
Message,
|
|
12
|
+
MessageControl,
|
|
13
|
+
MessageReceipt,
|
|
14
|
+
MessageReceiptStatus,
|
|
15
|
+
SessionInfo,
|
|
16
|
+
SessionRegistration,
|
|
17
|
+
} from "../types.ts";
|
|
9
18
|
|
|
10
19
|
interface SendOptions {
|
|
11
20
|
text: string;
|
|
@@ -13,6 +22,8 @@ interface SendOptions {
|
|
|
13
22
|
replyTo?: string;
|
|
14
23
|
expectsReply?: boolean;
|
|
15
24
|
messageId?: string;
|
|
25
|
+
supersedes?: string;
|
|
26
|
+
retryOf?: string;
|
|
16
27
|
}
|
|
17
28
|
|
|
18
29
|
interface SendResult {
|
|
@@ -25,6 +36,51 @@ function toError(error: unknown): Error {
|
|
|
25
36
|
return error instanceof Error ? error : new Error(String(error));
|
|
26
37
|
}
|
|
27
38
|
|
|
39
|
+
function connectToBrokerTarget(target: BrokerConnectTarget): net.Socket {
|
|
40
|
+
return typeof target === "string"
|
|
41
|
+
? net.connect(target)
|
|
42
|
+
: net.connect({ host: target.host, port: target.port });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function isMessageReceiptStatus(value: unknown): value is MessageReceiptStatus {
|
|
46
|
+
return value === "receiver_received"
|
|
47
|
+
|| value === "queued"
|
|
48
|
+
|| value === "injected"
|
|
49
|
+
|| value === "acknowledged"
|
|
50
|
+
|| value === "expired"
|
|
51
|
+
|| value === "cancelled"
|
|
52
|
+
|| value === "superseded"
|
|
53
|
+
|| value === "cancellation_requested";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function isMessageReceipt(value: unknown): value is MessageReceipt {
|
|
57
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
const receipt = value as Record<string, unknown>;
|
|
61
|
+
if (typeof receipt.messageId !== "string" || !isMessageReceiptStatus(receipt.status) || typeof receipt.timestamp !== "number") {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
return receipt.detail === undefined || typeof receipt.detail === "string";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function isMessageControl(value: unknown): value is MessageControl {
|
|
68
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
const control = value as Record<string, unknown>;
|
|
72
|
+
if (typeof control.messageId !== "string" || typeof control.timestamp !== "number") {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
if (control.action !== "cancel" && control.action !== "supersede") {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
if (control.supersededBy !== undefined && typeof control.supersededBy !== "string") {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
return control.detail === undefined || typeof control.detail === "string";
|
|
82
|
+
}
|
|
83
|
+
|
|
28
84
|
function isAttachment(value: unknown): value is Attachment {
|
|
29
85
|
if (typeof value !== "object" || value === null) {
|
|
30
86
|
return false;
|
|
@@ -58,6 +114,20 @@ function isMessage(value: unknown): value is Message {
|
|
|
58
114
|
return false;
|
|
59
115
|
}
|
|
60
116
|
|
|
117
|
+
for (const key of ["senderSequence", "brokerReceivedAt", "brokerDeliveredAt", "receiverReceivedAt", "injectedAt"] as const) {
|
|
118
|
+
if (message[key] !== undefined && typeof message[key] !== "number") {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (message.supersedes !== undefined && typeof message.supersedes !== "string") {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (message.retryOf !== undefined && typeof message.retryOf !== "string") {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
|
|
61
131
|
if (message.replyTo !== undefined && typeof message.replyTo !== "string") {
|
|
62
132
|
return false;
|
|
63
133
|
}
|
|
@@ -101,14 +171,30 @@ function isSessionInfo(value: unknown): value is SessionInfo {
|
|
|
101
171
|
return false;
|
|
102
172
|
}
|
|
103
173
|
|
|
104
|
-
|
|
174
|
+
if (session.status !== undefined && typeof session.status !== "string") {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (session.peerUid !== undefined && typeof session.peerUid !== "number") {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
for (const key of ["contextPct", "contextTokens", "contextWindow"] as const) {
|
|
183
|
+
if (session[key] !== undefined && typeof session[key] !== "number") {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return session.trustedLocal === undefined || typeof session.trustedLocal === "boolean";
|
|
105
189
|
}
|
|
106
190
|
|
|
107
191
|
export class IntercomClient extends EventEmitter {
|
|
108
192
|
private socket: net.Socket | null = null;
|
|
109
193
|
private _sessionId: string | null = null;
|
|
194
|
+
private _features = new Set<string>();
|
|
110
195
|
private pendingSends = new Map<string, { resolve: (r: SendResult) => void; reject: (e: Error) => void }>();
|
|
111
196
|
private pendingLists = new Map<string, { resolve: (sessions: SessionInfo[]) => void; reject: (e: Error) => void }>();
|
|
197
|
+
private nextSenderSequence = 1;
|
|
112
198
|
private disconnecting = false;
|
|
113
199
|
private disconnectError: Error | null = null;
|
|
114
200
|
|
|
@@ -127,6 +213,10 @@ export class IntercomClient extends EventEmitter {
|
|
|
127
213
|
return this._sessionId;
|
|
128
214
|
}
|
|
129
215
|
|
|
216
|
+
supportsFeature(feature: string): boolean {
|
|
217
|
+
return this._features.has(feature);
|
|
218
|
+
}
|
|
219
|
+
|
|
130
220
|
isConnected(): boolean {
|
|
131
221
|
const socket = this.socket;
|
|
132
222
|
return Boolean(socket && this._sessionId && !this.disconnecting && !socket.destroyed && !socket.writableEnded && socket.writable);
|
|
@@ -149,13 +239,21 @@ export class IntercomClient extends EventEmitter {
|
|
|
149
239
|
return socket;
|
|
150
240
|
}
|
|
151
241
|
|
|
152
|
-
connect(session:
|
|
242
|
+
connect(session: SessionRegistration, sessionId?: string): Promise<void> {
|
|
153
243
|
if (this.socket) {
|
|
154
244
|
return Promise.reject(new Error("Already connected"));
|
|
155
245
|
}
|
|
156
246
|
|
|
157
247
|
return new Promise((resolve, reject) => {
|
|
158
|
-
|
|
248
|
+
let socket: net.Socket;
|
|
249
|
+
let target: BrokerConnectTarget;
|
|
250
|
+
try {
|
|
251
|
+
target = getBrokerConnectTarget();
|
|
252
|
+
socket = connectToBrokerTarget(target);
|
|
253
|
+
} catch (error) {
|
|
254
|
+
reject(toError(error));
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
159
257
|
this.socket = socket;
|
|
160
258
|
this.disconnectError = null;
|
|
161
259
|
let settled = false;
|
|
@@ -203,6 +301,7 @@ export class IntercomClient extends EventEmitter {
|
|
|
203
301
|
this.socket = null;
|
|
204
302
|
}
|
|
205
303
|
this._sessionId = null;
|
|
304
|
+
this._features.clear();
|
|
206
305
|
this.disconnectError = null;
|
|
207
306
|
if (connectionEstablished && !wasDisconnecting) {
|
|
208
307
|
this.emit("disconnected", disconnectError);
|
|
@@ -254,7 +353,12 @@ export class IntercomClient extends EventEmitter {
|
|
|
254
353
|
this.once("_registered", onRegistered);
|
|
255
354
|
|
|
256
355
|
try {
|
|
257
|
-
writeMessage(socket, {
|
|
356
|
+
writeMessage(socket, {
|
|
357
|
+
type: "register",
|
|
358
|
+
session,
|
|
359
|
+
...(sessionId ? { sessionId } : {}),
|
|
360
|
+
...(typeof target === "string" ? {} : { stateId: target.stateId }),
|
|
361
|
+
});
|
|
258
362
|
} catch (error) {
|
|
259
363
|
cleanupConnectionAttempt();
|
|
260
364
|
cleanupSocketListeners();
|
|
@@ -274,7 +378,7 @@ export class IntercomClient extends EventEmitter {
|
|
|
274
378
|
|
|
275
379
|
const brokerMessage = msg as { type: string } & Record<string, unknown>;
|
|
276
380
|
|
|
277
|
-
if (this._sessionId === null && brokerMessage.type !== "registered") {
|
|
381
|
+
if (this._sessionId === null && brokerMessage.type !== "registered" && brokerMessage.type !== "error") {
|
|
278
382
|
throw new Error(`Received ${brokerMessage.type} before registered`);
|
|
279
383
|
}
|
|
280
384
|
|
|
@@ -288,8 +392,22 @@ export class IntercomClient extends EventEmitter {
|
|
|
288
392
|
throw new Error("Received duplicate registered message");
|
|
289
393
|
}
|
|
290
394
|
|
|
395
|
+
if (
|
|
396
|
+
brokerMessage.features !== undefined
|
|
397
|
+
&& (!Array.isArray(brokerMessage.features) || !brokerMessage.features.every((feature) => typeof feature === "string"))
|
|
398
|
+
) {
|
|
399
|
+
throw new Error("Invalid registered features");
|
|
400
|
+
}
|
|
401
|
+
|
|
291
402
|
this._sessionId = brokerMessage.sessionId;
|
|
292
|
-
this.
|
|
403
|
+
this._features = new Set((brokerMessage.features as string[] | undefined) ?? []);
|
|
404
|
+
const registered: BrokerMessage = {
|
|
405
|
+
type: "registered",
|
|
406
|
+
sessionId: brokerMessage.sessionId,
|
|
407
|
+
...(this._features.size > 0 ? { features: [...this._features] } : {}),
|
|
408
|
+
};
|
|
409
|
+
this.emit("broker_message", registered);
|
|
410
|
+
this.emit("_registered", registered);
|
|
293
411
|
break;
|
|
294
412
|
}
|
|
295
413
|
|
|
@@ -354,11 +472,31 @@ export class IntercomClient extends EventEmitter {
|
|
|
354
472
|
break;
|
|
355
473
|
}
|
|
356
474
|
|
|
475
|
+
case "message_receipt": {
|
|
476
|
+
if (!isSessionInfo(brokerMessage.from) || !isMessageReceipt(brokerMessage.receipt)) {
|
|
477
|
+
throw new Error("Invalid message_receipt event");
|
|
478
|
+
}
|
|
479
|
+
this.emit("broker_message", brokerMessage as BrokerMessage);
|
|
480
|
+
this.emit("message_receipt", brokerMessage.from, brokerMessage.receipt);
|
|
481
|
+
break;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
case "message_control": {
|
|
485
|
+
if (!isSessionInfo(brokerMessage.from) || !isMessageControl(brokerMessage.control)) {
|
|
486
|
+
throw new Error("Invalid message_control event");
|
|
487
|
+
}
|
|
488
|
+
this.emit("broker_message", brokerMessage as BrokerMessage);
|
|
489
|
+
this.emit("message_control", brokerMessage.from, brokerMessage.control);
|
|
490
|
+
break;
|
|
491
|
+
}
|
|
492
|
+
|
|
357
493
|
case "session_joined": {
|
|
358
494
|
if (!isSessionInfo(brokerMessage.session)) {
|
|
359
495
|
throw new Error("Invalid session_joined message");
|
|
360
496
|
}
|
|
361
497
|
|
|
498
|
+
const message: BrokerMessage = { type: "session_joined", session: brokerMessage.session };
|
|
499
|
+
this.emit("broker_message", message);
|
|
362
500
|
this.emit("session_joined", brokerMessage.session);
|
|
363
501
|
break;
|
|
364
502
|
}
|
|
@@ -368,6 +506,8 @@ export class IntercomClient extends EventEmitter {
|
|
|
368
506
|
throw new Error("Invalid session_left message");
|
|
369
507
|
}
|
|
370
508
|
|
|
509
|
+
const message: BrokerMessage = { type: "session_left", sessionId: brokerMessage.sessionId };
|
|
510
|
+
this.emit("broker_message", message);
|
|
371
511
|
this.emit("session_left", brokerMessage.sessionId);
|
|
372
512
|
break;
|
|
373
513
|
}
|
|
@@ -377,6 +517,8 @@ export class IntercomClient extends EventEmitter {
|
|
|
377
517
|
throw new Error("Invalid presence_update message");
|
|
378
518
|
}
|
|
379
519
|
|
|
520
|
+
const message: BrokerMessage = { type: "presence_update", session: brokerMessage.session };
|
|
521
|
+
this.emit("broker_message", message);
|
|
380
522
|
this.emit("presence_update", brokerMessage.session);
|
|
381
523
|
break;
|
|
382
524
|
}
|
|
@@ -386,10 +528,74 @@ export class IntercomClient extends EventEmitter {
|
|
|
386
528
|
throw new Error("Invalid error message");
|
|
387
529
|
}
|
|
388
530
|
|
|
531
|
+
if (this._sessionId === null) {
|
|
532
|
+
throw new Error(brokerMessage.error);
|
|
533
|
+
}
|
|
389
534
|
this.emit("error", new Error(brokerMessage.error));
|
|
390
535
|
break;
|
|
391
536
|
}
|
|
392
537
|
|
|
538
|
+
case "extension_owner": {
|
|
539
|
+
const hasOwnerId = typeof brokerMessage.ownerId === "string";
|
|
540
|
+
const hasOwnerEpoch = typeof brokerMessage.ownerEpoch === "string";
|
|
541
|
+
if (
|
|
542
|
+
typeof brokerMessage.namespace !== "string"
|
|
543
|
+
|| hasOwnerId !== hasOwnerEpoch
|
|
544
|
+
|| (brokerMessage.ownerId !== undefined && !hasOwnerId)
|
|
545
|
+
|| (brokerMessage.ownerEpoch !== undefined && !hasOwnerEpoch)
|
|
546
|
+
) {
|
|
547
|
+
throw new Error("Invalid extension_owner message");
|
|
548
|
+
}
|
|
549
|
+
this.emit("broker_message", brokerMessage as BrokerMessage);
|
|
550
|
+
this.emit("extension_owner", brokerMessage);
|
|
551
|
+
break;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
case "extension_message": {
|
|
555
|
+
const hasOwnerId = typeof brokerMessage.ownerId === "string";
|
|
556
|
+
const hasOwnerEpoch = typeof brokerMessage.ownerEpoch === "string";
|
|
557
|
+
if (
|
|
558
|
+
typeof brokerMessage.namespace !== "string"
|
|
559
|
+
|| typeof brokerMessage.fromSessionId !== "string"
|
|
560
|
+
|| hasOwnerId !== hasOwnerEpoch
|
|
561
|
+
|| (brokerMessage.ownerId !== undefined && !hasOwnerId)
|
|
562
|
+
|| (brokerMessage.ownerEpoch !== undefined && !hasOwnerEpoch)
|
|
563
|
+
) {
|
|
564
|
+
throw new Error("Invalid extension_message");
|
|
565
|
+
}
|
|
566
|
+
this.emit("broker_message", brokerMessage as BrokerMessage);
|
|
567
|
+
this.emit("extension_message", brokerMessage);
|
|
568
|
+
break;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
case "extension_state": {
|
|
572
|
+
if (
|
|
573
|
+
typeof brokerMessage.namespace !== "string"
|
|
574
|
+
|| !Number.isSafeInteger(brokerMessage.revision)
|
|
575
|
+
|| Number(brokerMessage.revision) < 0
|
|
576
|
+
) {
|
|
577
|
+
throw new Error("Invalid extension_state");
|
|
578
|
+
}
|
|
579
|
+
this.emit("broker_message", brokerMessage as BrokerMessage);
|
|
580
|
+
this.emit("extension_state", brokerMessage);
|
|
581
|
+
break;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
case "extension_state_result": {
|
|
585
|
+
if (
|
|
586
|
+
typeof brokerMessage.namespace !== "string"
|
|
587
|
+
|| typeof brokerMessage.committed !== "boolean"
|
|
588
|
+
|| !Number.isSafeInteger(brokerMessage.revision)
|
|
589
|
+
|| Number(brokerMessage.revision) < 0
|
|
590
|
+
|| (brokerMessage.reason !== undefined && typeof brokerMessage.reason !== "string")
|
|
591
|
+
) {
|
|
592
|
+
throw new Error("Invalid extension_state_result");
|
|
593
|
+
}
|
|
594
|
+
this.emit("broker_message", brokerMessage as BrokerMessage);
|
|
595
|
+
this.emit("extension_state_result", brokerMessage);
|
|
596
|
+
break;
|
|
597
|
+
}
|
|
598
|
+
|
|
393
599
|
default:
|
|
394
600
|
throw new Error(`Unknown broker message type: ${brokerMessage.type}`);
|
|
395
601
|
}
|
|
@@ -438,6 +644,12 @@ export class IntercomClient extends EventEmitter {
|
|
|
438
644
|
});
|
|
439
645
|
}
|
|
440
646
|
|
|
647
|
+
updateExtensionCapabilities(extensions: SessionRegistration["extensions"]): void {
|
|
648
|
+
if (!this.supportsFeature(EXTENSION_BUS_FEATURE)) return;
|
|
649
|
+
const socket = this.requireActiveSocket();
|
|
650
|
+
writeMessage(socket, { type: "extension_capabilities_update", extensions: extensions ?? [] });
|
|
651
|
+
}
|
|
652
|
+
|
|
441
653
|
listSessions(): Promise<SessionInfo[]> {
|
|
442
654
|
let socket: net.Socket;
|
|
443
655
|
try {
|
|
@@ -485,6 +697,9 @@ export class IntercomClient extends EventEmitter {
|
|
|
485
697
|
const message: Message = {
|
|
486
698
|
id: messageId,
|
|
487
699
|
timestamp: Date.now(),
|
|
700
|
+
senderSequence: this.nextSenderSequence++,
|
|
701
|
+
supersedes: options.supersedes,
|
|
702
|
+
retryOf: options.retryOf,
|
|
488
703
|
replyTo: options.replyTo,
|
|
489
704
|
expectsReply: options.expectsReply,
|
|
490
705
|
content: {
|
|
@@ -520,7 +735,72 @@ export class IntercomClient extends EventEmitter {
|
|
|
520
735
|
});
|
|
521
736
|
}
|
|
522
737
|
|
|
523
|
-
|
|
738
|
+
cancelMessage(messageId: string): Promise<SendResult> {
|
|
739
|
+
let socket: net.Socket;
|
|
740
|
+
try {
|
|
741
|
+
socket = this.requireActiveSocket();
|
|
742
|
+
} catch (error) {
|
|
743
|
+
return Promise.reject(toError(error));
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
return new Promise((resolve, reject) => {
|
|
747
|
+
const wrappedResolve = (result: SendResult) => {
|
|
748
|
+
clearTimeout(timeout);
|
|
749
|
+
resolve(result);
|
|
750
|
+
};
|
|
751
|
+
const wrappedReject = (error: Error) => {
|
|
752
|
+
clearTimeout(timeout);
|
|
753
|
+
reject(error);
|
|
754
|
+
};
|
|
755
|
+
const timeout = setTimeout(() => {
|
|
756
|
+
if (this.pendingSends.has(messageId)) {
|
|
757
|
+
this.pendingSends.delete(messageId);
|
|
758
|
+
wrappedReject(new Error("Cancel timeout"));
|
|
759
|
+
}
|
|
760
|
+
}, 10000);
|
|
761
|
+
this.pendingSends.set(messageId, { resolve: wrappedResolve, reject: wrappedReject });
|
|
762
|
+
|
|
763
|
+
try {
|
|
764
|
+
writeMessage(socket, { type: "cancel_message", messageId });
|
|
765
|
+
} catch (error) {
|
|
766
|
+
clearTimeout(timeout);
|
|
767
|
+
this.pendingSends.delete(messageId);
|
|
768
|
+
reject(toError(error));
|
|
769
|
+
}
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
sendMessageReceipt(receipt: MessageReceipt): void {
|
|
774
|
+
if (this.disconnecting) {
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
const socket = this.socket;
|
|
779
|
+
if (!socket || !this._sessionId || socket.destroyed || socket.writableEnded || !socket.writable) {
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
writeMessage(socket, { type: "message_receipt", receipt });
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
cancelAsk(messageId: string): void {
|
|
787
|
+
if (this.disconnecting) {
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
const socket = this.socket;
|
|
792
|
+
if (!socket || !this._sessionId || socket.destroyed || socket.writableEnded || !socket.writable) {
|
|
793
|
+
return;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
try {
|
|
797
|
+
writeMessage(socket, { type: "cancel_ask", messageId });
|
|
798
|
+
} catch {
|
|
799
|
+
// Cancellation is best-effort; local waiter cleanup must still proceed.
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
updatePresence(updates: { name?: string; status?: string; model?: string; contextPct?: number | null; contextTokens?: number | null; contextWindow?: number | null }): void {
|
|
524
804
|
if (this.disconnecting) {
|
|
525
805
|
return;
|
|
526
806
|
}
|
|
@@ -532,4 +812,27 @@ export class IntercomClient extends EventEmitter {
|
|
|
532
812
|
|
|
533
813
|
writeMessage(socket, { type: "presence", ...updates });
|
|
534
814
|
}
|
|
815
|
+
|
|
816
|
+
sendExtensionMessage(message: Extract<ClientMessage, { type: "extension_publish" | "extension_state_commit" }>): void {
|
|
817
|
+
if (!this.supportsFeature(EXTENSION_BUS_FEATURE)) {
|
|
818
|
+
throw new Error(`Connected broker does not support ${EXTENSION_BUS_FEATURE}`);
|
|
819
|
+
}
|
|
820
|
+
const socket = this.requireActiveSocket();
|
|
821
|
+
writeMessage(socket, message);
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
onBrokerMessage(handler: (message: BrokerMessage) => void): () => void {
|
|
825
|
+
this.on("broker_message", handler);
|
|
826
|
+
return () => this.off("broker_message", handler);
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
onMessageReceipt(handler: (from: SessionInfo, receipt: MessageReceipt) => void): () => void {
|
|
830
|
+
this.on("message_receipt", handler);
|
|
831
|
+
return () => this.off("message_receipt", handler);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
onMessageControl(handler: (from: SessionInfo, control: MessageControl) => void): () => void {
|
|
835
|
+
this.on("message_control", handler);
|
|
836
|
+
return () => this.off("message_control", handler);
|
|
837
|
+
}
|
|
535
838
|
}
|