@vellumai/assistant 0.4.17 → 0.4.18
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/eslint.config.mjs +2 -2
- package/package.json +1 -1
- package/src/__tests__/access-request-decision.test.ts +128 -120
- package/src/__tests__/account-registry.test.ts +121 -110
- package/src/__tests__/active-skill-tools.test.ts +200 -172
- package/src/__tests__/actor-token-service.test.ts +341 -274
- package/src/__tests__/agent-loop-thinking.test.ts +28 -19
- package/src/__tests__/agent-loop.test.ts +798 -378
- package/src/__tests__/anthropic-provider.test.ts +405 -247
- package/src/__tests__/app-builder-tool-scripts.test.ts +97 -97
- package/src/__tests__/app-bundler.test.ts +112 -79
- package/src/__tests__/app-executors.test.ts +205 -178
- package/src/__tests__/app-git-history.test.ts +90 -73
- package/src/__tests__/app-git-service.test.ts +67 -53
- package/src/__tests__/app-open-proxy.test.ts +29 -25
- package/src/__tests__/approval-conversation-turn.test.ts +100 -81
- package/src/__tests__/approval-hardcoded-copy-guard.test.ts +45 -17
- package/src/__tests__/approval-message-composer.test.ts +119 -119
- package/src/__tests__/approval-primitive.test.ts +264 -233
- package/src/__tests__/approval-routes-http.test.ts +4 -3
- package/src/__tests__/asset-materialize-tool.test.ts +250 -178
- package/src/__tests__/asset-search-tool.test.ts +251 -191
- package/src/__tests__/assistant-attachment-directive.test.ts +187 -142
- package/src/__tests__/assistant-attachments.test.ts +254 -186
- package/src/__tests__/assistant-event-hub.test.ts +105 -63
- package/src/__tests__/assistant-event.test.ts +66 -58
- package/src/__tests__/assistant-events-sse-hardening.test.ts +113 -73
- package/src/__tests__/assistant-feature-flag-guard.test.ts +78 -52
- package/src/__tests__/assistant-feature-flag-guardrails.test.ts +48 -45
- package/src/__tests__/assistant-feature-flags-integration.test.ts +118 -77
- package/src/__tests__/assistant-id-boundary-guard.test.ts +158 -104
- package/src/__tests__/attachments-store.test.ts +240 -183
- package/src/__tests__/attachments.test.ts +70 -62
- package/src/__tests__/audit-log-rotation.test.ts +50 -35
- package/src/__tests__/browser-fill-credential.test.ts +169 -101
- package/src/__tests__/browser-manager.test.ts +97 -75
- package/src/__tests__/browser-runtime-check.test.ts +16 -15
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +12 -10
- package/src/__tests__/browser-skill-endstate.test.ts +97 -72
- package/src/__tests__/bundle-scanner.test.ts +47 -22
- package/src/__tests__/bundled-asset.test.ts +74 -47
- package/src/__tests__/call-constants.test.ts +19 -19
- package/src/__tests__/call-controller.test.ts +0 -1
- package/src/__tests__/call-conversation-messages.test.ts +90 -65
- package/src/__tests__/call-domain.test.ts +149 -121
- package/src/__tests__/call-pointer-message-composer.test.ts +113 -83
- package/src/__tests__/call-pointer-messages.test.ts +213 -154
- package/src/__tests__/call-pointer-no-hardcoded-copy.guard.test.ts +9 -10
- package/src/__tests__/call-recovery.test.ts +232 -212
- package/src/__tests__/call-routes-http.test.ts +0 -1
- package/src/__tests__/call-start-guardian-guard.test.ts +32 -30
- package/src/__tests__/call-state-machine.test.ts +62 -51
- package/src/__tests__/call-state.test.ts +89 -75
- package/src/__tests__/call-store.test.ts +387 -316
- package/src/__tests__/callback-handoff-copy.test.ts +84 -82
- package/src/__tests__/canonical-guardian-store.test.ts +331 -280
- package/src/__tests__/channel-approval-routes.test.ts +1643 -1115
- package/src/__tests__/channel-approval.test.ts +139 -137
- package/src/__tests__/channel-approvals.test.ts +0 -1
- package/src/__tests__/channel-delivery-store.test.ts +232 -194
- package/src/__tests__/channel-guardian.test.ts +5 -3
- package/src/__tests__/channel-invite-transport.test.ts +107 -92
- package/src/__tests__/channel-policy.test.ts +42 -38
- package/src/__tests__/channel-readiness-service.test.ts +119 -102
- package/src/__tests__/channel-reply-delivery.test.ts +147 -118
- package/src/__tests__/channel-retry-sweep.test.ts +153 -110
- package/src/__tests__/checker.test.ts +3309 -1850
- package/src/__tests__/clarification-resolver.test.ts +91 -79
- package/src/__tests__/classifier.test.ts +64 -54
- package/src/__tests__/claude-code-skill-regression.test.ts +42 -37
- package/src/__tests__/claude-code-tool-profiles.test.ts +31 -29
- package/src/__tests__/clawhub.test.ts +92 -82
- package/src/__tests__/cli.test.ts +30 -30
- package/src/__tests__/clipboard.test.ts +53 -46
- package/src/__tests__/commit-guarantee.test.ts +59 -52
- package/src/__tests__/commit-message-enrichment-service.test.ts +203 -75
- package/src/__tests__/compaction.benchmark.test.ts +33 -31
- package/src/__tests__/computer-use-session-compaction.test.ts +60 -50
- package/src/__tests__/computer-use-session-lifecycle.test.ts +145 -117
- package/src/__tests__/computer-use-session-working-dir.test.ts +62 -48
- package/src/__tests__/computer-use-skill-baseline.test.ts +22 -19
- package/src/__tests__/computer-use-skill-endstate.test.ts +45 -31
- package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +121 -88
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +65 -42
- package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +33 -18
- package/src/__tests__/computer-use-tools.test.ts +121 -98
- package/src/__tests__/config-schema.test.ts +443 -347
- package/src/__tests__/config-watcher.test.ts +96 -81
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +148 -133
- package/src/__tests__/conflict-intent-tokenization.test.ts +96 -78
- package/src/__tests__/conflict-policy.test.ts +151 -80
- package/src/__tests__/conflict-store.test.ts +203 -157
- package/src/__tests__/connection-policy.test.ts +89 -59
- package/src/__tests__/contacts-tools.test.ts +247 -178
- package/src/__tests__/context-memory-e2e.test.ts +306 -214
- package/src/__tests__/context-token-estimator.test.ts +114 -74
- package/src/__tests__/context-window-manager.test.ts +269 -167
- package/src/__tests__/contradiction-checker.test.ts +161 -135
- package/src/__tests__/conversation-attention-store.test.ts +350 -290
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
- package/src/__tests__/conversation-pairing.test.ts +220 -113
- package/src/__tests__/conversation-store.test.ts +390 -235
- package/src/__tests__/credential-broker-browser-fill.test.ts +325 -250
- package/src/__tests__/credential-broker-server-use.test.ts +283 -243
- package/src/__tests__/credential-broker.test.ts +128 -74
- package/src/__tests__/credential-host-pattern-match.test.ts +64 -44
- package/src/__tests__/credential-metadata-store.test.ts +360 -311
- package/src/__tests__/credential-policy-validate.test.ts +81 -65
- package/src/__tests__/credential-resolve.test.ts +212 -145
- package/src/__tests__/credential-security-e2e.test.ts +144 -103
- package/src/__tests__/credential-security-invariants.test.ts +253 -208
- package/src/__tests__/credential-selection.test.ts +254 -146
- package/src/__tests__/credential-vault-unit.test.ts +531 -341
- package/src/__tests__/credential-vault.test.ts +761 -484
- package/src/__tests__/daemon-assistant-events.test.ts +91 -66
- package/src/__tests__/daemon-lifecycle.test.ts +258 -190
- package/src/__tests__/daemon-server-session-init.test.ts +0 -1
- package/src/__tests__/date-context.test.ts +314 -249
- package/src/__tests__/db-migration-rollback.test.ts +259 -130
- package/src/__tests__/db-schedule-syntax-migration.test.ts +78 -41
- package/src/__tests__/delete-managed-skill-tool.test.ts +77 -53
- package/src/__tests__/deterministic-verification-control-plane.test.ts +0 -1
- package/src/__tests__/dictation-mode-detection.test.ts +77 -55
- package/src/__tests__/dictation-profile-store.test.ts +70 -56
- package/src/__tests__/dictation-text-processing.test.ts +53 -35
- package/src/__tests__/diff.test.ts +102 -98
- package/src/__tests__/domain-normalize.test.ts +54 -54
- package/src/__tests__/domain-policy.test.ts +71 -55
- package/src/__tests__/dynamic-page-surface.test.ts +31 -33
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +69 -69
- package/src/__tests__/edit-engine.test.ts +56 -56
- package/src/__tests__/elevenlabs-client.test.ts +117 -91
- package/src/__tests__/elevenlabs-config.test.ts +32 -31
- package/src/__tests__/email-classifier.test.ts +15 -12
- package/src/__tests__/email-cli.test.ts +121 -108
- package/src/__tests__/emit-signal-routing-intent.test.ts +76 -69
- package/src/__tests__/encrypted-store.test.ts +180 -154
- package/src/__tests__/entity-extractor.test.ts +108 -87
- package/src/__tests__/entity-search.test.ts +664 -258
- package/src/__tests__/ephemeral-permissions.test.ts +224 -188
- package/src/__tests__/event-bus.test.ts +81 -77
- package/src/__tests__/extract-email.test.ts +29 -20
- package/src/__tests__/file-edit-tool.test.ts +62 -44
- package/src/__tests__/file-ops-service.test.ts +131 -114
- package/src/__tests__/file-read-tool.test.ts +48 -31
- package/src/__tests__/file-write-tool.test.ts +43 -37
- package/src/__tests__/filesystem-tools.test.ts +238 -209
- package/src/__tests__/followup-tools.test.ts +237 -162
- package/src/__tests__/forbidden-legacy-symbols.test.ts +19 -20
- package/src/__tests__/frontmatter.test.ts +96 -81
- package/src/__tests__/fuzzy-match-property.test.ts +75 -81
- package/src/__tests__/fuzzy-match.test.ts +71 -65
- package/src/__tests__/gateway-client-managed-outbound.test.ts +76 -57
- package/src/__tests__/gateway-only-enforcement.test.ts +0 -1
- package/src/__tests__/gateway-only-guard.test.ts +0 -1
- package/src/__tests__/gemini-image-service.test.ts +113 -100
- package/src/__tests__/gemini-provider.test.ts +297 -220
- package/src/__tests__/get-weather.test.ts +188 -114
- package/src/__tests__/gmail-integration.test.ts +0 -1
- package/src/__tests__/guardian-action-conversation-turn.test.ts +226 -171
- package/src/__tests__/guardian-action-copy-generator.test.ts +111 -93
- package/src/__tests__/guardian-action-followup-executor.test.ts +0 -1
- package/src/__tests__/guardian-action-followup-store.test.ts +199 -167
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +297 -250
- package/src/__tests__/guardian-action-late-reply.test.ts +462 -316
- package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +23 -18
- package/src/__tests__/guardian-action-store.test.ts +158 -109
- package/src/__tests__/guardian-action-sweep.test.ts +114 -100
- package/src/__tests__/guardian-actions-endpoint.test.ts +440 -256
- package/src/__tests__/guardian-control-plane-policy.test.ts +497 -331
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +217 -215
- package/src/__tests__/guardian-dispatch.test.ts +316 -256
- package/src/__tests__/guardian-grant-minting.test.ts +247 -178
- package/src/__tests__/guardian-outbound-http.test.ts +5 -3
- package/src/__tests__/guardian-principal-id-roundtrip.test.ts +99 -96
- package/src/__tests__/guardian-question-copy.test.ts +17 -17
- package/src/__tests__/guardian-question-mode.test.ts +134 -100
- package/src/__tests__/guardian-routing-invariants.test.ts +0 -1
- package/src/__tests__/guardian-routing-state.test.ts +0 -1
- package/src/__tests__/guardian-verification-intent-routing.test.ts +94 -88
- package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -1
- package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +0 -1
- package/src/__tests__/handle-user-message-secret-resume.test.ts +0 -1
- package/src/__tests__/handlers-add-trust-rule-metadata.test.ts +92 -76
- package/src/__tests__/handlers-cu-observation-blob.test.ts +103 -70
- package/src/__tests__/handlers-ipc-blob-probe.test.ts +77 -51
- package/src/__tests__/handlers-slack-config.test.ts +63 -54
- package/src/__tests__/handlers-task-submit-slash.test.ts +18 -18
- package/src/__tests__/handlers-telegram-config.test.ts +662 -329
- package/src/__tests__/handlers-twitter-config.test.ts +525 -298
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +3 -2
- package/src/__tests__/headless-browser-interactions.test.ts +444 -280
- package/src/__tests__/headless-browser-navigate.test.ts +116 -79
- package/src/__tests__/headless-browser-read-tools.test.ts +123 -86
- package/src/__tests__/headless-browser-snapshot.test.ts +71 -56
- package/src/__tests__/heartbeat-service.test.ts +76 -58
- package/src/__tests__/history-repair-observability.test.ts +14 -14
- package/src/__tests__/history-repair.test.ts +171 -167
- package/src/__tests__/home-base-bootstrap.test.ts +30 -27
- package/src/__tests__/hooks-blocking.test.ts +86 -37
- package/src/__tests__/hooks-cli.test.ts +104 -68
- package/src/__tests__/hooks-config.test.ts +81 -43
- package/src/__tests__/hooks-discovery.test.ts +106 -96
- package/src/__tests__/hooks-integration.test.ts +78 -72
- package/src/__tests__/hooks-manager.test.ts +99 -61
- package/src/__tests__/hooks-runner.test.ts +94 -71
- package/src/__tests__/hooks-settings.test.ts +69 -64
- package/src/__tests__/hooks-templates.test.ts +85 -54
- package/src/__tests__/hooks-ts-runner.test.ts +82 -45
- package/src/__tests__/hooks-watch.test.ts +32 -22
- package/src/__tests__/host-file-edit-tool.test.ts +190 -148
- package/src/__tests__/host-file-read-tool.test.ts +86 -63
- package/src/__tests__/host-file-write-tool.test.ts +98 -64
- package/src/__tests__/host-shell-tool.test.ts +342 -233
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -1
- package/src/__tests__/ingress-member-store.test.ts +163 -159
- package/src/__tests__/ingress-reconcile.test.ts +0 -1
- package/src/__tests__/ingress-routes-http.test.ts +441 -356
- package/src/__tests__/ingress-url-consistency.test.ts +125 -64
- package/src/__tests__/integration-status.test.ts +93 -73
- package/src/__tests__/intent-routing.test.ts +148 -118
- package/src/__tests__/invite-redemption-service.test.ts +163 -121
- package/src/__tests__/ipc-blob-store.test.ts +104 -91
- package/src/__tests__/ipc-contract-inventory.test.ts +27 -15
- package/src/__tests__/ipc-contract.test.ts +24 -23
- package/src/__tests__/ipc-protocol.test.ts +52 -46
- package/src/__tests__/ipc-roundtrip.benchmark.test.ts +61 -50
- package/src/__tests__/ipc-snapshot.test.ts +1135 -1056
- package/src/__tests__/ipc-validate.test.ts +240 -179
- package/src/__tests__/key-migration.test.ts +123 -90
- package/src/__tests__/keychain.test.ts +150 -123
- package/src/__tests__/lifecycle-docs-guard.test.ts +65 -64
- package/src/__tests__/llm-usage-store.test.ts +112 -87
- package/src/__tests__/managed-skill-lifecycle.test.ts +147 -108
- package/src/__tests__/managed-store.test.ts +411 -360
- package/src/__tests__/mcp-cli.test.ts +189 -123
- package/src/__tests__/mcp-health-check.test.ts +26 -21
- package/src/__tests__/media-generate-image.test.ts +122 -99
- package/src/__tests__/media-reuse-story.e2e.test.ts +282 -214
- package/src/__tests__/media-visibility-policy.test.ts +86 -38
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +146 -100
- package/src/__tests__/memory-lifecycle-e2e.test.ts +385 -297
- package/src/__tests__/memory-query-builder.test.ts +32 -33
- package/src/__tests__/memory-recall-quality.test.ts +761 -407
- package/src/__tests__/memory-regressions.experimental.test.ts +443 -380
- package/src/__tests__/memory-regressions.test.ts +3725 -2642
- package/src/__tests__/memory-retrieval-budget.test.ts +7 -8
- package/src/__tests__/memory-retrieval.benchmark.test.ts +144 -109
- package/src/__tests__/memory-upsert-concurrency.test.ts +292 -201
- package/src/__tests__/messaging-send-tool.test.ts +36 -29
- package/src/__tests__/migration-cli-flows.test.ts +69 -53
- package/src/__tests__/migration-ordering.test.ts +103 -86
- package/src/__tests__/mime-builder.test.ts +55 -32
- package/src/__tests__/mock-signup-server.test.ts +384 -246
- package/src/__tests__/model-intents.test.ts +61 -37
- package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +9 -12
- package/src/__tests__/no-is-trusted-guard.test.ts +24 -21
- package/src/__tests__/non-member-access-request.test.ts +3 -2
- package/src/__tests__/notification-broadcaster.test.ts +99 -81
- package/src/__tests__/notification-decision-fallback.test.ts +223 -178
- package/src/__tests__/notification-decision-strategy.test.ts +375 -337
- package/src/__tests__/notification-deep-link.test.ts +67 -61
- package/src/__tests__/notification-guardian-path.test.ts +248 -206
- package/src/__tests__/notification-routing-intent.test.ts +166 -93
- package/src/__tests__/notification-thread-candidate-validation.test.ts +78 -75
- package/src/__tests__/notification-thread-candidates.test.ts +64 -61
- package/src/__tests__/oauth-callback-registry.test.ts +40 -30
- package/src/__tests__/oauth-connect-handler.test.ts +109 -89
- package/src/__tests__/oauth-scope-policy.test.ts +63 -55
- package/src/__tests__/oauth2-gateway-transport.test.ts +252 -174
- package/src/__tests__/onboarding-starter-tasks.test.ts +93 -89
- package/src/__tests__/onboarding-template-contract.test.ts +93 -94
- package/src/__tests__/openai-provider.test.ts +366 -274
- package/src/__tests__/pairing-concurrent.test.ts +18 -12
- package/src/__tests__/pairing-routes.test.ts +45 -41
- package/src/__tests__/parallel-tool.benchmark.test.ts +108 -58
- package/src/__tests__/parser.test.ts +316 -226
- package/src/__tests__/path-classifier.test.ts +24 -25
- package/src/__tests__/path-policy.test.ts +187 -147
- package/src/__tests__/phone.test.ts +36 -36
- package/src/__tests__/platform-move-helper.test.ts +48 -40
- package/src/__tests__/platform-socket-path.test.ts +23 -24
- package/src/__tests__/platform-workspace-migration.test.ts +464 -414
- package/src/__tests__/platform.test.ts +61 -53
- package/src/__tests__/playbook-execution.test.ts +397 -265
- package/src/__tests__/playbook-tools.test.ts +267 -196
- package/src/__tests__/prebuilt-home-base-seed.test.ts +30 -27
- package/src/__tests__/pricing.test.ts +316 -136
- package/src/__tests__/profile-compiler.test.ts +206 -188
- package/src/__tests__/provider-commit-message-generator.test.ts +114 -106
- package/src/__tests__/provider-error-scenarios.test.ts +212 -158
- package/src/__tests__/provider-fail-open-selection.test.ts +51 -44
- package/src/__tests__/provider-registry-ollama.test.ts +13 -9
- package/src/__tests__/provider-streaming.benchmark.test.ts +232 -183
- package/src/__tests__/proxy-approval-callback.test.ts +180 -119
- package/src/__tests__/public-ingress-urls.test.ts +112 -94
- package/src/__tests__/qdrant-manager.test.ts +147 -98
- package/src/__tests__/ratelimit.test.ts +152 -82
- package/src/__tests__/recording-handler.test.ts +273 -151
- package/src/__tests__/recording-intent-fallback.test.ts +94 -75
- package/src/__tests__/recording-intent-handler.test.ts +0 -1
- package/src/__tests__/recording-intent.test.ts +578 -379
- package/src/__tests__/recording-state-machine.test.ts +530 -316
- package/src/__tests__/recurrence-engine-rruleset.test.ts +150 -92
- package/src/__tests__/recurrence-engine.test.ts +81 -41
- package/src/__tests__/recurrence-types.test.ts +63 -44
- package/src/__tests__/relay-server.test.ts +2131 -1602
- package/src/__tests__/reminder-store.test.ts +158 -80
- package/src/__tests__/reminder.test.ts +113 -109
- package/src/__tests__/remote-skill-policy.test.ts +96 -72
- package/src/__tests__/request-file-tool.test.ts +74 -67
- package/src/__tests__/response-tier.test.ts +131 -74
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/runtime-events-sse-parity.test.ts +167 -145
- package/src/__tests__/runtime-events-sse.test.ts +0 -1
- package/src/__tests__/sandbox-diagnostics.test.ts +66 -56
- package/src/__tests__/sandbox-host-parity.test.ts +377 -301
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +213 -161
- package/src/__tests__/schedule-store.test.ts +268 -205
- package/src/__tests__/schedule-tools.test.ts +702 -524
- package/src/__tests__/scheduler-recurrence.test.ts +240 -130
- package/src/__tests__/scoped-approval-grants.test.ts +258 -168
- package/src/__tests__/scoped-grant-security-matrix.test.ts +160 -146
- package/src/__tests__/script-proxy-certs.test.ts +38 -35
- package/src/__tests__/script-proxy-connect-tunnel.test.ts +71 -46
- package/src/__tests__/script-proxy-decision-trace.test.ts +161 -84
- package/src/__tests__/script-proxy-http-forwarder.test.ts +146 -129
- package/src/__tests__/script-proxy-injection-runtime.test.ts +139 -113
- package/src/__tests__/script-proxy-mitm-handler.test.ts +226 -142
- package/src/__tests__/script-proxy-policy-runtime.test.ts +126 -86
- package/src/__tests__/script-proxy-policy.test.ts +308 -153
- package/src/__tests__/script-proxy-rewrite-specificity.test.ts +74 -62
- package/src/__tests__/script-proxy-router.test.ts +111 -77
- package/src/__tests__/script-proxy-session-manager.test.ts +156 -113
- package/src/__tests__/script-proxy-session-runtime.test.ts +28 -24
- package/src/__tests__/secret-allowlist.test.ts +105 -90
- package/src/__tests__/secret-ingress-handler.test.ts +41 -30
- package/src/__tests__/secret-onetime-send.test.ts +67 -50
- package/src/__tests__/secret-prompt-log-hygiene.test.ts +35 -31
- package/src/__tests__/secret-response-routing.test.ts +50 -41
- package/src/__tests__/secret-scanner-executor.test.ts +152 -111
- package/src/__tests__/secret-scanner.test.ts +495 -413
- package/src/__tests__/secure-keys.test.ts +132 -121
- package/src/__tests__/send-endpoint-busy.test.ts +0 -1
- package/src/__tests__/send-notification-tool.test.ts +43 -42
- package/src/__tests__/sensitive-output-placeholders.test.ts +72 -64
- package/src/__tests__/sequence-store.test.ts +335 -167
- package/src/__tests__/server-history-render.test.ts +341 -202
- package/src/__tests__/session-abort-tool-results.test.ts +133 -70
- package/src/__tests__/session-confirmation-signals.test.ts +252 -160
- package/src/__tests__/session-conflict-gate.test.ts +775 -585
- package/src/__tests__/session-error.test.ts +222 -191
- package/src/__tests__/session-evictor.test.ts +79 -62
- package/src/__tests__/session-init.benchmark.test.ts +170 -108
- package/src/__tests__/session-load-history-repair.test.ts +273 -139
- package/src/__tests__/session-messaging-secret-redirect.test.ts +130 -90
- package/src/__tests__/session-pre-run-repair.test.ts +106 -59
- package/src/__tests__/session-profile-injection.test.ts +198 -130
- package/src/__tests__/session-provider-retry-repair.test.ts +223 -141
- package/src/__tests__/session-queue.test.ts +624 -321
- package/src/__tests__/session-runtime-assembly.test.ts +425 -329
- package/src/__tests__/session-runtime-workspace.test.ts +69 -61
- package/src/__tests__/session-skill-tools.test.ts +973 -678
- package/src/__tests__/session-slash-known.test.ts +185 -133
- package/src/__tests__/session-slash-queue.test.ts +147 -81
- package/src/__tests__/session-slash-unknown.test.ts +135 -90
- package/src/__tests__/session-surfaces-task-progress.test.ts +122 -87
- package/src/__tests__/session-tool-setup-app-refresh.test.ts +338 -177
- package/src/__tests__/session-tool-setup-memory-scope.test.ts +63 -40
- package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +60 -37
- package/src/__tests__/session-tool-setup-tools-disabled.test.ts +28 -26
- package/src/__tests__/session-undo.test.ts +43 -30
- package/src/__tests__/session-workspace-cache-state.test.ts +108 -67
- package/src/__tests__/session-workspace-injection.test.ts +245 -117
- package/src/__tests__/session-workspace-tool-tracking.test.ts +260 -93
- package/src/__tests__/shared-filesystem-errors.test.ts +47 -47
- package/src/__tests__/shell-credential-ref.test.ts +126 -90
- package/src/__tests__/shell-identity.test.ts +134 -111
- package/src/__tests__/shell-parser-fuzz.test.ts +263 -179
- package/src/__tests__/shell-parser-property.test.ts +435 -288
- package/src/__tests__/shell-tool-proxy-mode.test.ts +142 -70
- package/src/__tests__/size-guard.test.ts +42 -44
- package/src/__tests__/skill-feature-flags-integration.test.ts +79 -52
- package/src/__tests__/skill-feature-flags.test.ts +75 -47
- package/src/__tests__/skill-include-graph.test.ts +143 -148
- package/src/__tests__/skill-load-feature-flag.test.ts +94 -59
- package/src/__tests__/skill-load-tool.test.ts +371 -199
- package/src/__tests__/skill-projection-feature-flag.test.ts +131 -88
- package/src/__tests__/skill-projection.benchmark.test.ts +93 -65
- package/src/__tests__/skill-script-runner-host.test.ts +460 -250
- package/src/__tests__/skill-script-runner-sandbox.test.ts +168 -108
- package/src/__tests__/skill-script-runner.test.ts +115 -74
- package/src/__tests__/skill-tool-factory.test.ts +140 -96
- package/src/__tests__/skill-tool-manifest.test.ts +306 -210
- package/src/__tests__/skill-version-hash.test.ts +70 -56
- package/src/__tests__/skills.test.ts +0 -1
- package/src/__tests__/slack-channel-config.test.ts +127 -84
- package/src/__tests__/slack-skill.test.ts +60 -47
- package/src/__tests__/slash-commands-catalog.test.ts +37 -31
- package/src/__tests__/slash-commands-parser.test.ts +71 -64
- package/src/__tests__/slash-commands-resolver.test.ts +143 -107
- package/src/__tests__/slash-commands-rewrite.test.ts +22 -22
- package/src/__tests__/speaker-identification.test.ts +28 -25
- package/src/__tests__/starter-bundle.test.ts +27 -23
- package/src/__tests__/starter-task-flow.test.ts +67 -52
- package/src/__tests__/subagent-manager-notify.test.ts +154 -108
- package/src/__tests__/subagent-tools.test.ts +311 -270
- package/src/__tests__/subagent-types.test.ts +40 -40
- package/src/__tests__/surface-mutex-cleanup.test.ts +42 -30
- package/src/__tests__/swarm-dag-pathological.test.ts +122 -111
- package/src/__tests__/swarm-orchestrator.test.ts +135 -101
- package/src/__tests__/swarm-plan-validator.test.ts +125 -73
- package/src/__tests__/swarm-recursion.test.ts +58 -46
- package/src/__tests__/swarm-router-planner.test.ts +99 -74
- package/src/__tests__/swarm-session-integration.test.ts +148 -91
- package/src/__tests__/swarm-tool.test.ts +65 -45
- package/src/__tests__/swarm-worker-backend.test.ts +59 -45
- package/src/__tests__/swarm-worker-runner.test.ts +133 -118
- package/src/__tests__/system-prompt.test.ts +290 -256
- package/src/__tests__/task-compiler.test.ts +176 -120
- package/src/__tests__/task-management-tools.test.ts +561 -456
- package/src/__tests__/task-memory-cleanup.test.ts +627 -362
- package/src/__tests__/task-runner.test.ts +117 -94
- package/src/__tests__/task-scheduler.test.ts +113 -84
- package/src/__tests__/task-tools.test.ts +349 -264
- package/src/__tests__/terminal-sandbox.test.ts +138 -108
- package/src/__tests__/terminal-tools.test.ts +350 -305
- package/src/__tests__/thread-seed-composer.test.ts +307 -180
- package/src/__tests__/tool-approval-handler.test.ts +238 -137
- package/src/__tests__/tool-audit-listener.test.ts +69 -69
- package/src/__tests__/tool-domain-event-publisher.test.ts +142 -132
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +153 -146
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +136 -105
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +355 -239
- package/src/__tests__/tool-executor-redaction.test.ts +112 -109
- package/src/__tests__/tool-executor-shell-integration.test.ts +130 -79
- package/src/__tests__/tool-executor.test.ts +1274 -674
- package/src/__tests__/tool-grant-request-escalation.test.ts +401 -283
- package/src/__tests__/tool-metrics-listener.test.ts +97 -85
- package/src/__tests__/tool-notification-listener.test.ts +42 -25
- package/src/__tests__/tool-permission-simulate-handler.test.ts +137 -113
- package/src/__tests__/tool-policy.test.ts +44 -25
- package/src/__tests__/tool-profiling-listener.test.ts +99 -93
- package/src/__tests__/tool-result-truncation.test.ts +5 -4
- package/src/__tests__/tool-trace-listener.test.ts +131 -111
- package/src/__tests__/top-level-renderer.test.ts +62 -58
- package/src/__tests__/top-level-scanner.test.ts +68 -64
- package/src/__tests__/trace-emitter.test.ts +56 -56
- package/src/__tests__/trust-context-guards.test.ts +65 -65
- package/src/__tests__/trust-store.test.ts +1239 -806
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +0 -1
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +3 -2
- package/src/__tests__/trusted-contact-multichannel.test.ts +3 -2
- package/src/__tests__/trusted-contact-verification.test.ts +251 -231
- package/src/__tests__/turn-commit.test.ts +259 -200
- package/src/__tests__/twilio-provider.test.ts +140 -126
- package/src/__tests__/twilio-rest.test.ts +22 -18
- package/src/__tests__/twilio-routes-elevenlabs.test.ts +0 -1
- package/src/__tests__/twilio-routes-twiml.test.ts +55 -55
- package/src/__tests__/twilio-routes.test.ts +0 -1
- package/src/__tests__/twitter-auth-handler.test.ts +184 -139
- package/src/__tests__/twitter-cli-error-shaping.test.ts +88 -73
- package/src/__tests__/twitter-cli-routing.test.ts +146 -99
- package/src/__tests__/twitter-oauth-client.test.ts +82 -65
- package/src/__tests__/update-bulletin-format.test.ts +69 -66
- package/src/__tests__/update-bulletin-state.test.ts +66 -60
- package/src/__tests__/update-bulletin.test.ts +150 -114
- package/src/__tests__/update-template-contract.test.ts +15 -10
- package/src/__tests__/url-safety.test.ts +288 -265
- package/src/__tests__/user-reference.test.ts +32 -32
- package/src/__tests__/view-image-tool.test.ts +118 -96
- package/src/__tests__/voice-invite-redemption.test.ts +111 -106
- package/src/__tests__/voice-quality.test.ts +117 -102
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +204 -146
- package/src/__tests__/voice-session-bridge.test.ts +351 -216
- package/src/__tests__/weather-skill-regression.test.ts +170 -120
- package/src/__tests__/web-fetch.test.ts +664 -526
- package/src/__tests__/web-search.test.ts +379 -213
- package/src/__tests__/work-item-output.test.ts +90 -53
- package/src/__tests__/workspace-git-service.test.ts +437 -356
- package/src/__tests__/workspace-heartbeat-service.test.ts +125 -91
- package/src/__tests__/workspace-lifecycle.test.ts +98 -64
- package/src/__tests__/workspace-policy.test.ts +139 -71
- package/src/commands/__tests__/cc-command-registry.test.ts +142 -134
- package/src/config/__tests__/feature-flag-registry-guard.test.ts +48 -39
- package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +25 -10
- package/src/config/bundled-skills/doordash/__tests__/doordash-session.test.ts +0 -1
- package/src/config/bundled-skills/messaging/SKILL.md +4 -3
- package/src/config/bundled-skills/messaging/tools/gmail-outreach-scan.ts +15 -5
- package/src/config/bundled-skills/messaging/tools/gmail-sender-digest.ts +16 -5
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +34 -32
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/env.ts +3 -4
- package/src/memory/db-connection.ts +16 -10
- package/src/messaging/providers/gmail/adapter.ts +10 -3
- package/src/messaging/providers/gmail/client.ts +280 -72
- package/src/runtime/auth/__tests__/context.test.ts +75 -65
- package/src/runtime/auth/__tests__/credential-service.test.ts +137 -114
- package/src/runtime/auth/__tests__/guard-tests.test.ts +84 -90
- package/src/runtime/auth/__tests__/ipc-auth-context.test.ts +40 -40
- package/src/runtime/auth/__tests__/middleware.test.ts +80 -74
- package/src/runtime/auth/__tests__/policy.test.ts +9 -9
- package/src/runtime/auth/__tests__/route-policy.test.ts +76 -65
- package/src/runtime/auth/__tests__/scopes.test.ts +68 -60
- package/src/runtime/auth/__tests__/subject.test.ts +54 -54
- package/src/runtime/auth/__tests__/token-service.test.ts +115 -108
- package/src/runtime/auth/scopes.ts +3 -0
- package/src/runtime/auth/token-service.ts +4 -1
- package/src/runtime/auth/types.ts +2 -1
- package/src/runtime/http-server.ts +2 -1
- package/src/security/secure-keys.ts +103 -53
- package/src/tools/browser/__tests__/auth-cache.test.ts +69 -63
- package/src/tools/browser/__tests__/auth-detector.test.ts +218 -157
- package/src/tools/browser/__tests__/jit-auth.test.ts +83 -99
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
import { mkdtempSync, rmSync } from
|
|
2
|
-
import { tmpdir } from
|
|
3
|
-
import { join } from
|
|
1
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
const testDir = mkdtempSync(join(tmpdir(), "channel-delivery-store-test-"));
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
mock.module('../util/platform.js', () => ({
|
|
8
|
+
mock.module("../util/platform.js", () => ({
|
|
10
9
|
getDataDir: () => testDir,
|
|
11
|
-
isMacOS: () => process.platform ===
|
|
12
|
-
isLinux: () => process.platform ===
|
|
13
|
-
isWindows: () => process.platform ===
|
|
14
|
-
getSocketPath: () => join(testDir,
|
|
15
|
-
getPidPath: () => join(testDir,
|
|
16
|
-
getDbPath: () => join(testDir,
|
|
17
|
-
getLogPath: () => join(testDir,
|
|
10
|
+
isMacOS: () => process.platform === "darwin",
|
|
11
|
+
isLinux: () => process.platform === "linux",
|
|
12
|
+
isWindows: () => process.platform === "win32",
|
|
13
|
+
getSocketPath: () => join(testDir, "test.sock"),
|
|
14
|
+
getPidPath: () => join(testDir, "test.pid"),
|
|
15
|
+
getDbPath: () => join(testDir, "test.db"),
|
|
16
|
+
getLogPath: () => join(testDir, "test.log"),
|
|
18
17
|
ensureDataDir: () => {},
|
|
19
18
|
}));
|
|
20
19
|
|
|
21
|
-
mock.module(
|
|
22
|
-
getLogger: () =>
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
mock.module("../util/logger.js", () => ({
|
|
21
|
+
getLogger: () =>
|
|
22
|
+
new Proxy({} as Record<string, unknown>, {
|
|
23
|
+
get: () => () => {},
|
|
24
|
+
}),
|
|
25
25
|
}));
|
|
26
26
|
|
|
27
|
-
import { eq } from
|
|
27
|
+
import { eq } from "drizzle-orm";
|
|
28
28
|
|
|
29
29
|
import {
|
|
30
30
|
acknowledgeDelivery,
|
|
@@ -38,49 +38,63 @@ import {
|
|
|
38
38
|
recordProcessingFailure,
|
|
39
39
|
replayDeadLetters,
|
|
40
40
|
storePayload,
|
|
41
|
-
} from
|
|
42
|
-
import {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
import {
|
|
41
|
+
} from "../memory/channel-delivery-store.js";
|
|
42
|
+
import {
|
|
43
|
+
getConversationByKey,
|
|
44
|
+
setConversationKey,
|
|
45
|
+
} from "../memory/conversation-key-store.js";
|
|
46
|
+
import { getDb, initializeDb, resetDb } from "../memory/db.js";
|
|
47
|
+
import { RETRY_MAX_ATTEMPTS } from "../memory/job-utils.js";
|
|
48
|
+
import {
|
|
49
|
+
channelInboundEvents,
|
|
50
|
+
conversations,
|
|
51
|
+
externalConversationBindings,
|
|
52
|
+
messages,
|
|
53
|
+
} from "../memory/schema.js";
|
|
54
|
+
import { handleDeleteConversation } from "../runtime/routes/channel-routes.js";
|
|
47
55
|
|
|
48
56
|
initializeDb();
|
|
49
57
|
|
|
50
58
|
afterAll(() => {
|
|
51
59
|
resetDb();
|
|
52
|
-
try {
|
|
60
|
+
try {
|
|
61
|
+
rmSync(testDir, { recursive: true });
|
|
62
|
+
} catch {
|
|
63
|
+
/* best effort */
|
|
64
|
+
}
|
|
53
65
|
});
|
|
54
66
|
|
|
55
67
|
function resetTables() {
|
|
56
68
|
const db = getDb();
|
|
57
|
-
db.run(
|
|
58
|
-
db.run(
|
|
59
|
-
db.run(
|
|
60
|
-
db.run(
|
|
69
|
+
db.run("DELETE FROM channel_inbound_events");
|
|
70
|
+
db.run("DELETE FROM messages");
|
|
71
|
+
db.run("DELETE FROM conversation_keys");
|
|
72
|
+
db.run("DELETE FROM conversations");
|
|
61
73
|
}
|
|
62
74
|
|
|
63
75
|
/** Insert a message row so FK constraints on channel_inbound_events.message_id pass. */
|
|
64
76
|
function insertMessage(id: string, conversationId: string): void {
|
|
65
77
|
const db = getDb();
|
|
66
|
-
db.insert(messages)
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
78
|
+
db.insert(messages)
|
|
79
|
+
.values({
|
|
80
|
+
id,
|
|
81
|
+
conversationId,
|
|
82
|
+
role: "user",
|
|
83
|
+
content: "test message",
|
|
84
|
+
createdAt: Date.now(),
|
|
85
|
+
})
|
|
86
|
+
.run();
|
|
73
87
|
}
|
|
74
88
|
|
|
75
|
-
describe(
|
|
89
|
+
describe("channel-delivery-store", () => {
|
|
76
90
|
beforeEach(() => {
|
|
77
91
|
resetTables();
|
|
78
92
|
});
|
|
79
93
|
|
|
80
94
|
// ── Recording inbound events ──────────────────────────────────────
|
|
81
95
|
|
|
82
|
-
test(
|
|
83
|
-
const result = recordInbound(
|
|
96
|
+
test("records an inbound event and creates a conversation", () => {
|
|
97
|
+
const result = recordInbound("telegram", "chat-1", "msg-1");
|
|
84
98
|
|
|
85
99
|
expect(result.accepted).toBe(true);
|
|
86
100
|
expect(result.duplicate).toBe(false);
|
|
@@ -95,17 +109,17 @@ describe('channel-delivery-store', () => {
|
|
|
95
109
|
.get();
|
|
96
110
|
|
|
97
111
|
expect(row).toBeDefined();
|
|
98
|
-
expect(row!.sourceChannel).toBe(
|
|
99
|
-
expect(row!.externalChatId).toBe(
|
|
100
|
-
expect(row!.externalMessageId).toBe(
|
|
101
|
-
expect(row!.deliveryStatus).toBe(
|
|
102
|
-
expect(row!.processingStatus).toBe(
|
|
112
|
+
expect(row!.sourceChannel).toBe("telegram");
|
|
113
|
+
expect(row!.externalChatId).toBe("chat-1");
|
|
114
|
+
expect(row!.externalMessageId).toBe("msg-1");
|
|
115
|
+
expect(row!.deliveryStatus).toBe("pending");
|
|
116
|
+
expect(row!.processingStatus).toBe("pending");
|
|
103
117
|
expect(row!.processingAttempts).toBe(0);
|
|
104
118
|
});
|
|
105
119
|
|
|
106
|
-
test(
|
|
107
|
-
const result = recordInbound(
|
|
108
|
-
sourceMessageId:
|
|
120
|
+
test("records inbound with sourceMessageId option", () => {
|
|
121
|
+
const result = recordInbound("telegram", "chat-1", "msg-1", {
|
|
122
|
+
sourceMessageId: "src-42",
|
|
109
123
|
});
|
|
110
124
|
|
|
111
125
|
const db = getDb();
|
|
@@ -115,56 +129,64 @@ describe('channel-delivery-store', () => {
|
|
|
115
129
|
.where(eq(channelInboundEvents.id, result.eventId))
|
|
116
130
|
.get();
|
|
117
131
|
|
|
118
|
-
expect(row!.sourceMessageId).toBe(
|
|
132
|
+
expect(row!.sourceMessageId).toBe("src-42");
|
|
119
133
|
});
|
|
120
134
|
|
|
121
|
-
test(
|
|
122
|
-
const r1 = recordInbound(
|
|
123
|
-
const r2 = recordInbound(
|
|
135
|
+
test("same chat on same channel reuses the same conversation", () => {
|
|
136
|
+
const r1 = recordInbound("telegram", "chat-1", "msg-1");
|
|
137
|
+
const r2 = recordInbound("telegram", "chat-1", "msg-2");
|
|
124
138
|
|
|
125
139
|
expect(r1.conversationId).toBe(r2.conversationId);
|
|
126
140
|
});
|
|
127
141
|
|
|
128
|
-
test(
|
|
129
|
-
const r1 = recordInbound(
|
|
130
|
-
const r2 = recordInbound(
|
|
142
|
+
test("different chats get different conversations", () => {
|
|
143
|
+
const r1 = recordInbound("telegram", "chat-1", "msg-1");
|
|
144
|
+
const r2 = recordInbound("telegram", "chat-2", "msg-1");
|
|
131
145
|
|
|
132
146
|
expect(r1.conversationId).not.toBe(r2.conversationId);
|
|
133
147
|
});
|
|
134
148
|
|
|
135
|
-
test(
|
|
136
|
-
const r1 = recordInbound(
|
|
137
|
-
const r2 = recordInbound(
|
|
149
|
+
test("different channels get different conversations", () => {
|
|
150
|
+
const r1 = recordInbound("telegram", "chat-1", "msg-1");
|
|
151
|
+
const r2 = recordInbound("slack", "chat-1", "msg-1");
|
|
138
152
|
|
|
139
153
|
expect(r1.conversationId).not.toBe(r2.conversationId);
|
|
140
154
|
});
|
|
141
155
|
|
|
142
|
-
test(
|
|
143
|
-
const r1 = recordInbound(
|
|
144
|
-
|
|
156
|
+
test("same chat/channel but different assistantId uses different conversations", () => {
|
|
157
|
+
const r1 = recordInbound("telegram", "chat-1", "msg-1", {
|
|
158
|
+
assistantId: "asst-A",
|
|
159
|
+
});
|
|
160
|
+
const r2 = recordInbound("telegram", "chat-1", "msg-2", {
|
|
161
|
+
assistantId: "asst-B",
|
|
162
|
+
});
|
|
145
163
|
|
|
146
164
|
expect(r1.conversationId).not.toBe(r2.conversationId);
|
|
147
165
|
});
|
|
148
166
|
|
|
149
|
-
test(
|
|
167
|
+
test("self assistant reuses legacy key and creates scoped alias", () => {
|
|
150
168
|
// Create a conversation via the legacy (no assistantId) path
|
|
151
|
-
const legacy = recordInbound(
|
|
169
|
+
const legacy = recordInbound("telegram", "chat-1", "msg-1");
|
|
152
170
|
|
|
153
171
|
// Now use assistantId='self' — should reuse the legacy conversation
|
|
154
|
-
const scoped = recordInbound(
|
|
172
|
+
const scoped = recordInbound("telegram", "chat-1", "msg-2", {
|
|
173
|
+
assistantId: "self",
|
|
174
|
+
});
|
|
155
175
|
expect(scoped.conversationId).toBe(legacy.conversationId);
|
|
156
176
|
|
|
157
177
|
// The scoped alias key should exist, so subsequent calls with 'self'
|
|
158
178
|
// resolve directly without falling back to the legacy key
|
|
159
|
-
const again = recordInbound(
|
|
179
|
+
const again = recordInbound("telegram", "chat-1", "msg-3", {
|
|
180
|
+
assistantId: "self",
|
|
181
|
+
});
|
|
160
182
|
expect(again.conversationId).toBe(legacy.conversationId);
|
|
161
183
|
});
|
|
162
184
|
|
|
163
185
|
// ── Deduplication ─────────────────────────────────────────────────
|
|
164
186
|
|
|
165
|
-
test(
|
|
166
|
-
const first = recordInbound(
|
|
167
|
-
const second = recordInbound(
|
|
187
|
+
test("duplicate inbound returns duplicate: true with same eventId", () => {
|
|
188
|
+
const first = recordInbound("telegram", "chat-1", "msg-1");
|
|
189
|
+
const second = recordInbound("telegram", "chat-1", "msg-1");
|
|
168
190
|
|
|
169
191
|
expect(second.duplicate).toBe(true);
|
|
170
192
|
expect(second.accepted).toBe(true);
|
|
@@ -172,9 +194,9 @@ describe('channel-delivery-store', () => {
|
|
|
172
194
|
expect(second.conversationId).toBe(first.conversationId);
|
|
173
195
|
});
|
|
174
196
|
|
|
175
|
-
test(
|
|
176
|
-
const r1 = recordInbound(
|
|
177
|
-
const r2 = recordInbound(
|
|
197
|
+
test("same message ID on different chats is not a duplicate", () => {
|
|
198
|
+
const r1 = recordInbound("telegram", "chat-1", "msg-1");
|
|
199
|
+
const r2 = recordInbound("telegram", "chat-2", "msg-1");
|
|
178
200
|
|
|
179
201
|
expect(r1.duplicate).toBe(false);
|
|
180
202
|
expect(r2.duplicate).toBe(false);
|
|
@@ -183,61 +205,61 @@ describe('channel-delivery-store', () => {
|
|
|
183
205
|
|
|
184
206
|
// ── linkMessage + findMessageBySourceId ───────────────────────────
|
|
185
207
|
|
|
186
|
-
test(
|
|
187
|
-
const result = recordInbound(
|
|
188
|
-
sourceMessageId:
|
|
208
|
+
test("linkMessage sets messageId and findMessageBySourceId retrieves it", () => {
|
|
209
|
+
const result = recordInbound("telegram", "chat-1", "msg-1", {
|
|
210
|
+
sourceMessageId: "src-100",
|
|
189
211
|
});
|
|
190
212
|
|
|
191
|
-
const msgId =
|
|
213
|
+
const msgId = "internal-msg-abc";
|
|
192
214
|
insertMessage(msgId, result.conversationId);
|
|
193
215
|
linkMessage(result.eventId, msgId);
|
|
194
216
|
|
|
195
|
-
const found = findMessageBySourceId(
|
|
217
|
+
const found = findMessageBySourceId("telegram", "chat-1", "src-100");
|
|
196
218
|
expect(found).not.toBeNull();
|
|
197
219
|
expect(found!.messageId).toBe(msgId);
|
|
198
220
|
expect(found!.conversationId).toBe(result.conversationId);
|
|
199
221
|
});
|
|
200
222
|
|
|
201
|
-
test(
|
|
202
|
-
const found = findMessageBySourceId(
|
|
223
|
+
test("findMessageBySourceId returns null when no match", () => {
|
|
224
|
+
const found = findMessageBySourceId("telegram", "chat-1", "nonexistent");
|
|
203
225
|
expect(found).toBeNull();
|
|
204
226
|
});
|
|
205
227
|
|
|
206
|
-
test(
|
|
207
|
-
recordInbound(
|
|
208
|
-
sourceMessageId:
|
|
228
|
+
test("findMessageBySourceId returns null when messageId is not linked", () => {
|
|
229
|
+
recordInbound("telegram", "chat-1", "msg-1", {
|
|
230
|
+
sourceMessageId: "src-200",
|
|
209
231
|
});
|
|
210
232
|
// Not calling linkMessage — messageId stays null
|
|
211
|
-
const found = findMessageBySourceId(
|
|
233
|
+
const found = findMessageBySourceId("telegram", "chat-1", "src-200");
|
|
212
234
|
expect(found).toBeNull();
|
|
213
235
|
});
|
|
214
236
|
|
|
215
237
|
// ── Delivery status transitions ───────────────────────────────────
|
|
216
238
|
|
|
217
|
-
test(
|
|
218
|
-
recordInbound(
|
|
239
|
+
test("acknowledgeDelivery transitions from pending to delivered", () => {
|
|
240
|
+
recordInbound("telegram", "chat-1", "msg-1");
|
|
219
241
|
|
|
220
|
-
const ack = acknowledgeDelivery(
|
|
242
|
+
const ack = acknowledgeDelivery("telegram", "chat-1", "msg-1");
|
|
221
243
|
expect(ack).toBe(true);
|
|
222
244
|
|
|
223
245
|
const db = getDb();
|
|
224
246
|
const row = db
|
|
225
247
|
.select()
|
|
226
248
|
.from(channelInboundEvents)
|
|
227
|
-
.where(eq(channelInboundEvents.externalMessageId,
|
|
249
|
+
.where(eq(channelInboundEvents.externalMessageId, "msg-1"))
|
|
228
250
|
.get();
|
|
229
|
-
expect(row!.deliveryStatus).toBe(
|
|
251
|
+
expect(row!.deliveryStatus).toBe("delivered");
|
|
230
252
|
});
|
|
231
253
|
|
|
232
|
-
test(
|
|
233
|
-
const ack = acknowledgeDelivery(
|
|
254
|
+
test("acknowledgeDelivery returns false for unknown event", () => {
|
|
255
|
+
const ack = acknowledgeDelivery("telegram", "chat-1", "nonexistent");
|
|
234
256
|
expect(ack).toBe(false);
|
|
235
257
|
});
|
|
236
258
|
|
|
237
259
|
// ── Processing status transitions ─────────────────────────────────
|
|
238
260
|
|
|
239
|
-
test(
|
|
240
|
-
const result = recordInbound(
|
|
261
|
+
test("markProcessed sets processingStatus to processed", () => {
|
|
262
|
+
const result = recordInbound("telegram", "chat-1", "msg-1");
|
|
241
263
|
markProcessed(result.eventId);
|
|
242
264
|
|
|
243
265
|
const db = getDb();
|
|
@@ -246,14 +268,14 @@ describe('channel-delivery-store', () => {
|
|
|
246
268
|
.from(channelInboundEvents)
|
|
247
269
|
.where(eq(channelInboundEvents.id, result.eventId))
|
|
248
270
|
.get();
|
|
249
|
-
expect(row!.processingStatus).toBe(
|
|
271
|
+
expect(row!.processingStatus).toBe("processed");
|
|
250
272
|
});
|
|
251
273
|
|
|
252
|
-
test(
|
|
253
|
-
const result = recordInbound(
|
|
274
|
+
test("recordProcessingFailure with retryable error sets status to failed", () => {
|
|
275
|
+
const result = recordInbound("telegram", "chat-1", "msg-1");
|
|
254
276
|
|
|
255
277
|
// A timeout error is classified as retryable
|
|
256
|
-
const err = new Error(
|
|
278
|
+
const err = new Error("request timeout");
|
|
257
279
|
recordProcessingFailure(result.eventId, err);
|
|
258
280
|
|
|
259
281
|
const db = getDb();
|
|
@@ -263,17 +285,17 @@ describe('channel-delivery-store', () => {
|
|
|
263
285
|
.where(eq(channelInboundEvents.id, result.eventId))
|
|
264
286
|
.get();
|
|
265
287
|
|
|
266
|
-
expect(row!.processingStatus).toBe(
|
|
288
|
+
expect(row!.processingStatus).toBe("failed");
|
|
267
289
|
expect(row!.processingAttempts).toBe(1);
|
|
268
|
-
expect(row!.lastProcessingError).toBe(
|
|
290
|
+
expect(row!.lastProcessingError).toBe("request timeout");
|
|
269
291
|
expect(row!.retryAfter).toBeGreaterThan(0);
|
|
270
292
|
});
|
|
271
293
|
|
|
272
|
-
test(
|
|
273
|
-
const result = recordInbound(
|
|
294
|
+
test("recordProcessingFailure with fatal error sets status to dead_letter", () => {
|
|
295
|
+
const result = recordInbound("telegram", "chat-1", "msg-1");
|
|
274
296
|
|
|
275
297
|
// A 400-status error is classified as fatal
|
|
276
|
-
const err = { status: 400, message:
|
|
298
|
+
const err = { status: 400, message: "Bad Request" };
|
|
277
299
|
recordProcessingFailure(result.eventId, err);
|
|
278
300
|
|
|
279
301
|
const db = getDb();
|
|
@@ -283,16 +305,16 @@ describe('channel-delivery-store', () => {
|
|
|
283
305
|
.where(eq(channelInboundEvents.id, result.eventId))
|
|
284
306
|
.get();
|
|
285
307
|
|
|
286
|
-
expect(row!.processingStatus).toBe(
|
|
308
|
+
expect(row!.processingStatus).toBe("dead_letter");
|
|
287
309
|
expect(row!.processingAttempts).toBe(1);
|
|
288
310
|
expect(row!.retryAfter).toBeNull();
|
|
289
311
|
});
|
|
290
312
|
|
|
291
|
-
test(
|
|
292
|
-
const result = recordInbound(
|
|
313
|
+
test("recordProcessingFailure dead-letters after max attempts", () => {
|
|
314
|
+
const result = recordInbound("telegram", "chat-1", "msg-1");
|
|
293
315
|
|
|
294
316
|
// Exhaust all retry attempts with retryable errors
|
|
295
|
-
const err = new Error(
|
|
317
|
+
const err = new Error("request timeout");
|
|
296
318
|
for (let i = 0; i < RETRY_MAX_ATTEMPTS; i++) {
|
|
297
319
|
recordProcessingFailure(result.eventId, err);
|
|
298
320
|
}
|
|
@@ -304,15 +326,15 @@ describe('channel-delivery-store', () => {
|
|
|
304
326
|
.where(eq(channelInboundEvents.id, result.eventId))
|
|
305
327
|
.get();
|
|
306
328
|
|
|
307
|
-
expect(row!.processingStatus).toBe(
|
|
329
|
+
expect(row!.processingStatus).toBe("dead_letter");
|
|
308
330
|
expect(row!.processingAttempts).toBe(RETRY_MAX_ATTEMPTS);
|
|
309
331
|
});
|
|
310
332
|
|
|
311
333
|
// ── Payload storage ───────────────────────────────────────────────
|
|
312
334
|
|
|
313
|
-
test(
|
|
314
|
-
const result = recordInbound(
|
|
315
|
-
const payload = { update_id: 123, message: { text:
|
|
335
|
+
test("storePayload persists raw payload and clearPayload removes it", () => {
|
|
336
|
+
const result = recordInbound("telegram", "chat-1", "msg-1");
|
|
337
|
+
const payload = { update_id: 123, message: { text: "hello" } };
|
|
316
338
|
|
|
317
339
|
storePayload(result.eventId, payload);
|
|
318
340
|
|
|
@@ -336,13 +358,13 @@ describe('channel-delivery-store', () => {
|
|
|
336
358
|
|
|
337
359
|
// ── Retryable events query ────────────────────────────────────────
|
|
338
360
|
|
|
339
|
-
test(
|
|
340
|
-
const r1 = recordInbound(
|
|
341
|
-
const r2 = recordInbound(
|
|
342
|
-
const _r3 = recordInbound(
|
|
361
|
+
test("getRetryableEvents returns failed events past their backoff", () => {
|
|
362
|
+
const r1 = recordInbound("telegram", "chat-1", "msg-1");
|
|
363
|
+
const r2 = recordInbound("telegram", "chat-1", "msg-2");
|
|
364
|
+
const _r3 = recordInbound("telegram", "chat-1", "msg-3");
|
|
343
365
|
|
|
344
366
|
// r1: failed with past retry_after
|
|
345
|
-
const err = new Error(
|
|
367
|
+
const err = new Error("request timeout");
|
|
346
368
|
recordProcessingFailure(r1.eventId, err);
|
|
347
369
|
// Force retry_after to be in the past
|
|
348
370
|
const db = getDb();
|
|
@@ -365,13 +387,13 @@ describe('channel-delivery-store', () => {
|
|
|
365
387
|
expect(retryable[0].conversationId).toBe(r1.conversationId);
|
|
366
388
|
});
|
|
367
389
|
|
|
368
|
-
test(
|
|
390
|
+
test("getRetryableEvents respects limit parameter", () => {
|
|
369
391
|
const db = getDb();
|
|
370
|
-
const err = new Error(
|
|
392
|
+
const err = new Error("request timeout");
|
|
371
393
|
const ids: string[] = [];
|
|
372
394
|
|
|
373
395
|
for (let i = 0; i < 5; i++) {
|
|
374
|
-
const r = recordInbound(
|
|
396
|
+
const r = recordInbound("telegram", "chat-1", `msg-${i}`);
|
|
375
397
|
ids.push(r.eventId);
|
|
376
398
|
recordProcessingFailure(r.eventId, err);
|
|
377
399
|
db.update(channelInboundEvents)
|
|
@@ -386,73 +408,79 @@ describe('channel-delivery-store', () => {
|
|
|
386
408
|
|
|
387
409
|
// ── Dead-letter queue ─────────────────────────────────────────────
|
|
388
410
|
|
|
389
|
-
test(
|
|
390
|
-
const r1 = recordInbound(
|
|
391
|
-
const _r2 = recordInbound(
|
|
411
|
+
test("getDeadLetterEvents returns dead-lettered events", () => {
|
|
412
|
+
const r1 = recordInbound("telegram", "chat-1", "msg-1");
|
|
413
|
+
const _r2 = recordInbound("telegram", "chat-1", "msg-2");
|
|
392
414
|
|
|
393
415
|
// r1: dead-letter via fatal error
|
|
394
|
-
recordProcessingFailure(r1.eventId, { status: 400, message:
|
|
416
|
+
recordProcessingFailure(r1.eventId, { status: 400, message: "invalid" });
|
|
395
417
|
|
|
396
418
|
// r2: still pending
|
|
397
419
|
const deadLetters = getDeadLetterEvents();
|
|
398
420
|
expect(deadLetters).toHaveLength(1);
|
|
399
421
|
expect(deadLetters[0].id).toBe(r1.eventId);
|
|
400
|
-
expect(deadLetters[0].sourceChannel).toBe(
|
|
401
|
-
expect(deadLetters[0].externalChatId).toBe(
|
|
402
|
-
expect(deadLetters[0].externalMessageId).toBe(
|
|
422
|
+
expect(deadLetters[0].sourceChannel).toBe("telegram");
|
|
423
|
+
expect(deadLetters[0].externalChatId).toBe("chat-1");
|
|
424
|
+
expect(deadLetters[0].externalMessageId).toBe("msg-1");
|
|
403
425
|
});
|
|
404
426
|
|
|
405
|
-
test(
|
|
406
|
-
const r1 = recordInbound(
|
|
407
|
-
const r2 = recordInbound(
|
|
427
|
+
test("replayDeadLetters resets dead-lettered events to failed for retry", () => {
|
|
428
|
+
const r1 = recordInbound("telegram", "chat-1", "msg-1");
|
|
429
|
+
const r2 = recordInbound("telegram", "chat-1", "msg-2");
|
|
408
430
|
|
|
409
431
|
// Dead-letter both
|
|
410
|
-
recordProcessingFailure(r1.eventId, { status: 400, message:
|
|
411
|
-
recordProcessingFailure(r2.eventId, { status: 401, message:
|
|
432
|
+
recordProcessingFailure(r1.eventId, { status: 400, message: "bad" });
|
|
433
|
+
recordProcessingFailure(r2.eventId, { status: 401, message: "auth" });
|
|
412
434
|
|
|
413
435
|
const count = replayDeadLetters([r1.eventId, r2.eventId]);
|
|
414
436
|
expect(count).toBe(2);
|
|
415
437
|
|
|
416
438
|
const db = getDb();
|
|
417
|
-
const row1 = db
|
|
418
|
-
.
|
|
419
|
-
|
|
420
|
-
.where(eq(channelInboundEvents.id,
|
|
439
|
+
const row1 = db
|
|
440
|
+
.select()
|
|
441
|
+
.from(channelInboundEvents)
|
|
442
|
+
.where(eq(channelInboundEvents.id, r1.eventId))
|
|
443
|
+
.get();
|
|
444
|
+
const row2 = db
|
|
445
|
+
.select()
|
|
446
|
+
.from(channelInboundEvents)
|
|
447
|
+
.where(eq(channelInboundEvents.id, r2.eventId))
|
|
448
|
+
.get();
|
|
421
449
|
|
|
422
|
-
expect(row1!.processingStatus).toBe(
|
|
450
|
+
expect(row1!.processingStatus).toBe("failed");
|
|
423
451
|
expect(row1!.processingAttempts).toBe(0);
|
|
424
452
|
expect(row1!.lastProcessingError).toBeNull();
|
|
425
453
|
expect(row1!.retryAfter).toBeGreaterThan(0);
|
|
426
454
|
|
|
427
|
-
expect(row2!.processingStatus).toBe(
|
|
455
|
+
expect(row2!.processingStatus).toBe("failed");
|
|
428
456
|
expect(row2!.processingAttempts).toBe(0);
|
|
429
457
|
});
|
|
430
458
|
|
|
431
|
-
test(
|
|
432
|
-
const r1 = recordInbound(
|
|
459
|
+
test("replayDeadLetters skips non-dead-lettered events", () => {
|
|
460
|
+
const r1 = recordInbound("telegram", "chat-1", "msg-1");
|
|
433
461
|
|
|
434
462
|
// r1 is still pending, not dead-lettered
|
|
435
463
|
const count = replayDeadLetters([r1.eventId]);
|
|
436
464
|
expect(count).toBe(0);
|
|
437
465
|
});
|
|
438
466
|
|
|
439
|
-
test(
|
|
440
|
-
const count = replayDeadLetters([
|
|
467
|
+
test("replayDeadLetters skips nonexistent IDs", () => {
|
|
468
|
+
const count = replayDeadLetters(["nonexistent-id"]);
|
|
441
469
|
expect(count).toBe(0);
|
|
442
470
|
});
|
|
443
471
|
|
|
444
472
|
// ── Full lifecycle ────────────────────────────────────────────────
|
|
445
473
|
|
|
446
|
-
test(
|
|
447
|
-
const result = recordInbound(
|
|
448
|
-
sourceMessageId:
|
|
474
|
+
test("full lifecycle: inbound -> link -> acknowledge -> processed", () => {
|
|
475
|
+
const result = recordInbound("telegram", "chat-1", "msg-1", {
|
|
476
|
+
sourceMessageId: "src-1",
|
|
449
477
|
});
|
|
450
478
|
expect(result.duplicate).toBe(false);
|
|
451
479
|
|
|
452
|
-
const msgId =
|
|
480
|
+
const msgId = "internal-msg-1";
|
|
453
481
|
insertMessage(msgId, result.conversationId);
|
|
454
482
|
linkMessage(result.eventId, msgId);
|
|
455
|
-
acknowledgeDelivery(
|
|
483
|
+
acknowledgeDelivery("telegram", "chat-1", "msg-1");
|
|
456
484
|
markProcessed(result.eventId);
|
|
457
485
|
|
|
458
486
|
const db = getDb();
|
|
@@ -463,67 +491,72 @@ describe('channel-delivery-store', () => {
|
|
|
463
491
|
.get();
|
|
464
492
|
|
|
465
493
|
expect(row!.messageId).toBe(msgId);
|
|
466
|
-
expect(row!.deliveryStatus).toBe(
|
|
467
|
-
expect(row!.processingStatus).toBe(
|
|
494
|
+
expect(row!.deliveryStatus).toBe("delivered");
|
|
495
|
+
expect(row!.processingStatus).toBe("processed");
|
|
468
496
|
|
|
469
|
-
const found = findMessageBySourceId(
|
|
497
|
+
const found = findMessageBySourceId("telegram", "chat-1", "src-1");
|
|
470
498
|
expect(found!.messageId).toBe(msgId);
|
|
471
499
|
});
|
|
472
500
|
|
|
473
501
|
// ── handleDeleteConversation assistantId parameter ───────────────
|
|
474
502
|
|
|
475
|
-
test(
|
|
503
|
+
test("handleDeleteConversation with non-self assistant deletes only scoped key", async () => {
|
|
476
504
|
// Set up a scoped conversation key like the one created by recordInbound
|
|
477
505
|
// with a specific assistantId.
|
|
478
|
-
const convId =
|
|
479
|
-
const scopedKey =
|
|
480
|
-
const legacyKey =
|
|
506
|
+
const convId = "conv-delete-test";
|
|
507
|
+
const scopedKey = "asst:my-assistant:telegram:chat-del";
|
|
508
|
+
const legacyKey = "telegram:chat-del";
|
|
481
509
|
|
|
482
510
|
// Insert a conversation row so FK constraints are satisfied
|
|
483
511
|
const now = Date.now();
|
|
484
512
|
const db = getDb();
|
|
485
|
-
db.insert(conversations)
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
513
|
+
db.insert(conversations)
|
|
514
|
+
.values({
|
|
515
|
+
id: convId,
|
|
516
|
+
title: "test",
|
|
517
|
+
createdAt: now,
|
|
518
|
+
updatedAt: now,
|
|
519
|
+
})
|
|
520
|
+
.run();
|
|
491
521
|
setConversationKey(scopedKey, convId);
|
|
492
522
|
setConversationKey(legacyKey, convId);
|
|
493
|
-
db.insert(externalConversationBindings)
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
523
|
+
db.insert(externalConversationBindings)
|
|
524
|
+
.values({
|
|
525
|
+
conversationId: convId,
|
|
526
|
+
sourceChannel: "telegram",
|
|
527
|
+
externalChatId: "chat-del",
|
|
528
|
+
createdAt: now,
|
|
529
|
+
updatedAt: now,
|
|
530
|
+
})
|
|
531
|
+
.run();
|
|
500
532
|
|
|
501
533
|
// Verify both keys exist
|
|
502
534
|
expect(getConversationByKey(scopedKey)).not.toBeNull();
|
|
503
535
|
expect(getConversationByKey(legacyKey)).not.toBeNull();
|
|
504
536
|
|
|
505
537
|
// Call handleDeleteConversation with assistantId as a parameter (not in body)
|
|
506
|
-
const req = new Request(
|
|
507
|
-
method:
|
|
508
|
-
headers: {
|
|
538
|
+
const req = new Request("http://localhost/channels/conversation", {
|
|
539
|
+
method: "DELETE",
|
|
540
|
+
headers: { "Content-Type": "application/json" },
|
|
509
541
|
body: JSON.stringify({
|
|
510
|
-
sourceChannel:
|
|
511
|
-
conversationExternalId:
|
|
542
|
+
sourceChannel: "telegram",
|
|
543
|
+
conversationExternalId: "chat-del",
|
|
512
544
|
// Note: no assistantId in the body — it comes from the route param
|
|
513
545
|
}),
|
|
514
546
|
});
|
|
515
547
|
|
|
516
|
-
const res = await handleDeleteConversation(req,
|
|
548
|
+
const res = await handleDeleteConversation(req, "my-assistant");
|
|
517
549
|
expect(res.status).toBe(200);
|
|
518
550
|
|
|
519
|
-
const json = await res.json() as { ok: boolean };
|
|
551
|
+
const json = (await res.json()) as { ok: boolean };
|
|
520
552
|
expect(json.ok).toBe(true);
|
|
521
553
|
|
|
522
554
|
// Non-self delete should only remove the scoped key and preserve legacy.
|
|
523
555
|
expect(getConversationByKey(scopedKey)).toBeNull();
|
|
524
556
|
expect(getConversationByKey(legacyKey)).not.toBeNull();
|
|
525
557
|
// Non-self delete should not mutate assistant-agnostic external bindings.
|
|
526
|
-
const remainingBinding = db
|
|
558
|
+
const remainingBinding = db
|
|
559
|
+
.select()
|
|
527
560
|
.from(externalConversationBindings)
|
|
528
561
|
.where(eq(externalConversationBindings.conversationId, convId))
|
|
529
562
|
.get();
|
|
@@ -531,34 +564,38 @@ describe('channel-delivery-store', () => {
|
|
|
531
564
|
});
|
|
532
565
|
|
|
533
566
|
test('handleDeleteConversation defaults to "self" when no assistantId provided', async () => {
|
|
534
|
-
const convId =
|
|
535
|
-
const scopedKey =
|
|
536
|
-
const legacyKey =
|
|
567
|
+
const convId = "conv-delete-default";
|
|
568
|
+
const scopedKey = "asst:self:telegram:chat-def";
|
|
569
|
+
const legacyKey = "telegram:chat-def";
|
|
537
570
|
|
|
538
571
|
const now = Date.now();
|
|
539
572
|
const db = getDb();
|
|
540
|
-
db.insert(conversations)
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
573
|
+
db.insert(conversations)
|
|
574
|
+
.values({
|
|
575
|
+
id: convId,
|
|
576
|
+
title: "test",
|
|
577
|
+
createdAt: now,
|
|
578
|
+
updatedAt: now,
|
|
579
|
+
})
|
|
580
|
+
.run();
|
|
546
581
|
setConversationKey(scopedKey, convId);
|
|
547
582
|
setConversationKey(legacyKey, convId);
|
|
548
|
-
db.insert(externalConversationBindings)
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
583
|
+
db.insert(externalConversationBindings)
|
|
584
|
+
.values({
|
|
585
|
+
conversationId: convId,
|
|
586
|
+
sourceChannel: "telegram",
|
|
587
|
+
externalChatId: "chat-def",
|
|
588
|
+
createdAt: now,
|
|
589
|
+
updatedAt: now,
|
|
590
|
+
})
|
|
591
|
+
.run();
|
|
592
|
+
|
|
593
|
+
const req = new Request("http://localhost/channels/conversation", {
|
|
594
|
+
method: "DELETE",
|
|
595
|
+
headers: { "Content-Type": "application/json" },
|
|
559
596
|
body: JSON.stringify({
|
|
560
|
-
sourceChannel:
|
|
561
|
-
conversationExternalId:
|
|
597
|
+
sourceChannel: "telegram",
|
|
598
|
+
conversationExternalId: "chat-def",
|
|
562
599
|
}),
|
|
563
600
|
});
|
|
564
601
|
|
|
@@ -569,7 +606,8 @@ describe('channel-delivery-store', () => {
|
|
|
569
606
|
expect(getConversationByKey(scopedKey)).toBeNull();
|
|
570
607
|
expect(getConversationByKey(legacyKey)).toBeNull();
|
|
571
608
|
// Self delete should keep external bindings in sync for the canonical route.
|
|
572
|
-
const remainingBinding = db
|
|
609
|
+
const remainingBinding = db
|
|
610
|
+
.select()
|
|
573
611
|
.from(externalConversationBindings)
|
|
574
612
|
.where(eq(externalConversationBindings.conversationId, convId))
|
|
575
613
|
.get();
|