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