@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,26 +1,25 @@
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(), "conv-attn-store-test-"));
6
7
 
7
- const testDir = mkdtempSync(join(tmpdir(), 'conv-attn-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
  migrateToDataLayout: () => {},
20
19
  migrateToWorkspaceLayout: () => {},
21
20
  }));
22
21
 
23
- mock.module('../util/logger.js', () => ({
22
+ mock.module("../util/logger.js", () => ({
24
23
  getLogger: () =>
25
24
  new Proxy({} as Record<string, unknown>, {
26
25
  get: () => () => {},
@@ -29,20 +28,20 @@ mock.module('../util/logger.js', () => ({
29
28
  truncateForLog: (value: string) => value,
30
29
  }));
31
30
 
32
- import { eq } from 'drizzle-orm';
31
+ import { eq } from "drizzle-orm";
33
32
 
34
33
  import {
35
34
  getAttentionStateByConversationIds,
36
35
  listConversationAttention,
37
36
  projectAssistantMessage,
38
37
  recordConversationSeenSignal,
39
- } from '../memory/conversation-attention-store.js';
40
- import { getDb, initializeDb, resetDb } from '../memory/db.js';
38
+ } from "../memory/conversation-attention-store.js";
39
+ import { getDb, initializeDb, resetDb } from "../memory/db.js";
41
40
  import {
42
41
  conversationAssistantAttentionState,
43
42
  conversationAttentionEvents,
44
43
  conversations,
45
- } from '../memory/schema.js';
44
+ } from "../memory/schema.js";
46
45
 
47
46
  initializeDb();
48
47
 
@@ -75,516 +74,577 @@ afterAll(() => {
75
74
  }
76
75
  });
77
76
 
78
- describe('conversation-attention-store', () => {
77
+ describe("conversation-attention-store", () => {
79
78
  beforeEach(() => {
80
79
  clearTables();
81
80
  });
82
81
 
83
82
  // ── projectAssistantMessage ─────────────────────────────────────
84
83
 
85
- describe('projectAssistantMessage', () => {
86
- test('creates a new state row when none exists', () => {
87
- ensureConversation('conv-1');
84
+ describe("projectAssistantMessage", () => {
85
+ test("creates a new state row when none exists", () => {
86
+ ensureConversation("conv-1");
88
87
  projectAssistantMessage({
89
- conversationId: 'conv-1',
90
- assistantId: 'self',
91
- messageId: 'msg-1',
88
+ conversationId: "conv-1",
89
+ assistantId: "self",
90
+ messageId: "msg-1",
92
91
  messageAt: 1000,
93
92
  });
94
93
 
95
- const states = getAttentionStateByConversationIds(['conv-1']);
94
+ const states = getAttentionStateByConversationIds(["conv-1"]);
96
95
  expect(states.size).toBe(1);
97
- const state = states.get('conv-1')!;
98
- expect(state.latestAssistantMessageId).toBe('msg-1');
96
+ const state = states.get("conv-1")!;
97
+ expect(state.latestAssistantMessageId).toBe("msg-1");
99
98
  expect(state.latestAssistantMessageAt).toBe(1000);
100
99
  expect(state.lastSeenAssistantMessageId).toBeNull();
101
100
  expect(state.lastSeenAssistantMessageAt).toBeNull();
102
101
  });
103
102
 
104
- test('advances cursor when new message is later', () => {
105
- ensureConversation('conv-1');
103
+ test("advances cursor when new message is later", () => {
104
+ ensureConversation("conv-1");
106
105
  projectAssistantMessage({
107
- conversationId: 'conv-1',
108
- assistantId: 'self',
109
- messageId: 'msg-1',
106
+ conversationId: "conv-1",
107
+ assistantId: "self",
108
+ messageId: "msg-1",
110
109
  messageAt: 1000,
111
110
  });
112
111
  projectAssistantMessage({
113
- conversationId: 'conv-1',
114
- assistantId: 'self',
115
- messageId: 'msg-2',
112
+ conversationId: "conv-1",
113
+ assistantId: "self",
114
+ messageId: "msg-2",
116
115
  messageAt: 2000,
117
116
  });
118
117
 
119
- const states = getAttentionStateByConversationIds(['conv-1']);
120
- const state = states.get('conv-1')!;
121
- expect(state.latestAssistantMessageId).toBe('msg-2');
118
+ const states = getAttentionStateByConversationIds(["conv-1"]);
119
+ const state = states.get("conv-1")!;
120
+ expect(state.latestAssistantMessageId).toBe("msg-2");
122
121
  expect(state.latestAssistantMessageAt).toBe(2000);
123
122
  });
124
123
 
125
- test('does not move cursor backward (monotonic invariant)', () => {
126
- ensureConversation('conv-1');
124
+ test("does not move cursor backward (monotonic invariant)", () => {
125
+ ensureConversation("conv-1");
127
126
  projectAssistantMessage({
128
- conversationId: 'conv-1',
129
- assistantId: 'self',
130
- messageId: 'msg-2',
127
+ conversationId: "conv-1",
128
+ assistantId: "self",
129
+ messageId: "msg-2",
131
130
  messageAt: 2000,
132
131
  });
133
132
  projectAssistantMessage({
134
- conversationId: 'conv-1',
135
- assistantId: 'self',
136
- messageId: 'msg-1',
133
+ conversationId: "conv-1",
134
+ assistantId: "self",
135
+ messageId: "msg-1",
137
136
  messageAt: 1000,
138
137
  });
139
138
 
140
- const states = getAttentionStateByConversationIds(['conv-1']);
141
- const state = states.get('conv-1')!;
142
- expect(state.latestAssistantMessageId).toBe('msg-2');
139
+ const states = getAttentionStateByConversationIds(["conv-1"]);
140
+ const state = states.get("conv-1")!;
141
+ expect(state.latestAssistantMessageId).toBe("msg-2");
143
142
  expect(state.latestAssistantMessageAt).toBe(2000);
144
143
  });
145
144
 
146
- test('does not advance cursor when timestamp is equal', () => {
147
- ensureConversation('conv-1');
145
+ test("does not advance cursor when timestamp is equal", () => {
146
+ ensureConversation("conv-1");
148
147
  projectAssistantMessage({
149
- conversationId: 'conv-1',
150
- assistantId: 'self',
151
- messageId: 'msg-1',
148
+ conversationId: "conv-1",
149
+ assistantId: "self",
150
+ messageId: "msg-1",
152
151
  messageAt: 1000,
153
152
  });
154
153
  projectAssistantMessage({
155
- conversationId: 'conv-1',
156
- assistantId: 'self',
157
- messageId: 'msg-1-dup',
154
+ conversationId: "conv-1",
155
+ assistantId: "self",
156
+ messageId: "msg-1-dup",
158
157
  messageAt: 1000,
159
158
  });
160
159
 
161
- const states = getAttentionStateByConversationIds(['conv-1']);
162
- const state = states.get('conv-1')!;
163
- expect(state.latestAssistantMessageId).toBe('msg-1');
160
+ const states = getAttentionStateByConversationIds(["conv-1"]);
161
+ const state = states.get("conv-1")!;
162
+ expect(state.latestAssistantMessageId).toBe("msg-1");
164
163
  });
165
164
  });
166
165
 
167
166
  // ── recordConversationSeenSignal ────────────────────────────────
168
167
 
169
- describe('recordConversationSeenSignal', () => {
170
- test('appends an immutable event row', () => {
171
- ensureConversation('conv-1');
168
+ describe("recordConversationSeenSignal", () => {
169
+ test("appends an immutable event row", () => {
170
+ ensureConversation("conv-1");
172
171
  const event = recordConversationSeenSignal({
173
- conversationId: 'conv-1',
174
- assistantId: 'self',
175
- sourceChannel: 'vellum',
176
- signalType: 'macos_conversation_opened',
177
- confidence: 'explicit',
178
- source: 'desktop-client',
172
+ conversationId: "conv-1",
173
+ assistantId: "self",
174
+ sourceChannel: "vellum",
175
+ signalType: "macos_conversation_opened",
176
+ confidence: "explicit",
177
+ source: "desktop-client",
179
178
  });
180
179
 
181
180
  expect(event.id).toBeTruthy();
182
- expect(event.conversationId).toBe('conv-1');
183
- expect(event.signalType).toBe('macos_conversation_opened');
184
- expect(event.confidence).toBe('explicit');
181
+ expect(event.conversationId).toBe("conv-1");
182
+ expect(event.signalType).toBe("macos_conversation_opened");
183
+ expect(event.confidence).toBe("explicit");
185
184
  });
186
185
 
187
- test('advances seen cursor to current latest assistant message', () => {
188
- ensureConversation('conv-1');
186
+ test("advances seen cursor to current latest assistant message", () => {
187
+ ensureConversation("conv-1");
189
188
 
190
189
  // Project an assistant message first
191
190
  projectAssistantMessage({
192
- conversationId: 'conv-1',
193
- assistantId: 'self',
194
- messageId: 'msg-1',
191
+ conversationId: "conv-1",
192
+ assistantId: "self",
193
+ messageId: "msg-1",
195
194
  messageAt: 1000,
196
195
  });
197
196
 
198
197
  // Now record a seen signal
199
198
  recordConversationSeenSignal({
200
- conversationId: 'conv-1',
201
- assistantId: 'self',
202
- sourceChannel: 'vellum',
203
- signalType: 'macos_conversation_opened',
204
- confidence: 'explicit',
205
- source: 'desktop-client',
199
+ conversationId: "conv-1",
200
+ assistantId: "self",
201
+ sourceChannel: "vellum",
202
+ signalType: "macos_conversation_opened",
203
+ confidence: "explicit",
204
+ source: "desktop-client",
206
205
  });
207
206
 
208
- const states = getAttentionStateByConversationIds(['conv-1']);
209
- const state = states.get('conv-1')!;
210
- expect(state.lastSeenAssistantMessageId).toBe('msg-1');
207
+ const states = getAttentionStateByConversationIds(["conv-1"]);
208
+ const state = states.get("conv-1")!;
209
+ expect(state.lastSeenAssistantMessageId).toBe("msg-1");
211
210
  expect(state.lastSeenAssistantMessageAt).toBe(1000);
212
211
  });
213
212
 
214
- test('creates state row if none exists when recording seen signal', () => {
215
- ensureConversation('conv-1');
213
+ test("creates state row if none exists when recording seen signal", () => {
214
+ ensureConversation("conv-1");
216
215
 
217
216
  recordConversationSeenSignal({
218
- conversationId: 'conv-1',
219
- assistantId: 'self',
220
- sourceChannel: 'telegram',
221
- signalType: 'telegram_inbound_message',
222
- confidence: 'inferred',
223
- source: 'telegram-gateway',
217
+ conversationId: "conv-1",
218
+ assistantId: "self",
219
+ sourceChannel: "telegram",
220
+ signalType: "telegram_inbound_message",
221
+ confidence: "inferred",
222
+ source: "telegram-gateway",
224
223
  });
225
224
 
226
- const states = getAttentionStateByConversationIds(['conv-1']);
225
+ const states = getAttentionStateByConversationIds(["conv-1"]);
227
226
  expect(states.size).toBe(1);
228
- const state = states.get('conv-1')!;
227
+ const state = states.get("conv-1")!;
229
228
  // No latest assistant message to mark as seen
230
229
  expect(state.lastSeenAssistantMessageId).toBeNull();
231
- expect(state.lastSeenSignalType).toBe('telegram_inbound_message');
230
+ expect(state.lastSeenSignalType).toBe("telegram_inbound_message");
232
231
  });
233
232
 
234
- test('does not regress seen cursor (monotonic invariant)', () => {
235
- ensureConversation('conv-1');
233
+ test("does not regress seen cursor (monotonic invariant)", () => {
234
+ ensureConversation("conv-1");
236
235
 
237
236
  // Project two assistant messages
238
237
  projectAssistantMessage({
239
- conversationId: 'conv-1',
240
- assistantId: 'self',
241
- messageId: 'msg-1',
238
+ conversationId: "conv-1",
239
+ assistantId: "self",
240
+ messageId: "msg-1",
242
241
  messageAt: 1000,
243
242
  });
244
243
 
245
244
  // Mark as seen at msg-1
246
245
  recordConversationSeenSignal({
247
- conversationId: 'conv-1',
248
- assistantId: 'self',
249
- sourceChannel: 'vellum',
250
- signalType: 'macos_conversation_opened',
251
- confidence: 'explicit',
252
- source: 'desktop-client',
246
+ conversationId: "conv-1",
247
+ assistantId: "self",
248
+ sourceChannel: "vellum",
249
+ signalType: "macos_conversation_opened",
250
+ confidence: "explicit",
251
+ source: "desktop-client",
253
252
  });
254
253
 
255
254
  // Project a second message
256
255
  projectAssistantMessage({
257
- conversationId: 'conv-1',
258
- assistantId: 'self',
259
- messageId: 'msg-2',
256
+ conversationId: "conv-1",
257
+ assistantId: "self",
258
+ messageId: "msg-2",
260
259
  messageAt: 2000,
261
260
  });
262
261
 
263
262
  // Mark as seen at msg-2
264
263
  recordConversationSeenSignal({
265
- conversationId: 'conv-1',
266
- assistantId: 'self',
267
- sourceChannel: 'vellum',
268
- signalType: 'macos_conversation_opened',
269
- confidence: 'explicit',
270
- source: 'desktop-client',
264
+ conversationId: "conv-1",
265
+ assistantId: "self",
266
+ sourceChannel: "vellum",
267
+ signalType: "macos_conversation_opened",
268
+ confidence: "explicit",
269
+ source: "desktop-client",
271
270
  });
272
271
 
273
- const states = getAttentionStateByConversationIds(['conv-1']);
274
- const state = states.get('conv-1')!;
275
- expect(state.lastSeenAssistantMessageId).toBe('msg-2');
272
+ const states = getAttentionStateByConversationIds(["conv-1"]);
273
+ const state = states.get("conv-1")!;
274
+ expect(state.lastSeenAssistantMessageId).toBe("msg-2");
276
275
  expect(state.lastSeenAssistantMessageAt).toBe(2000);
277
276
  });
278
277
 
279
- test('records evidence text and metadata in event', () => {
280
- ensureConversation('conv-1');
278
+ test("records evidence text and metadata in event", () => {
279
+ ensureConversation("conv-1");
281
280
 
282
281
  const event = recordConversationSeenSignal({
283
- conversationId: 'conv-1',
284
- assistantId: 'self',
285
- sourceChannel: 'telegram',
286
- signalType: 'telegram_callback',
287
- confidence: 'explicit',
288
- source: 'telegram-gateway',
289
- evidenceText: 'User pressed inline button',
290
- metadata: { callbackData: 'ack:123' },
291
- });
292
-
293
- expect(event.evidenceText).toBe('User pressed inline button');
294
- expect(JSON.parse(event.metadataJson)).toEqual({ callbackData: 'ack:123' });
282
+ conversationId: "conv-1",
283
+ assistantId: "self",
284
+ sourceChannel: "telegram",
285
+ signalType: "telegram_callback",
286
+ confidence: "explicit",
287
+ source: "telegram-gateway",
288
+ evidenceText: "User pressed inline button",
289
+ metadata: { callbackData: "ack:123" },
290
+ });
291
+
292
+ expect(event.evidenceText).toBe("User pressed inline button");
293
+ expect(JSON.parse(event.metadataJson)).toEqual({
294
+ callbackData: "ack:123",
295
+ });
295
296
  });
296
297
 
297
- test('seen signal with no latest assistant message does not set seen cursor', () => {
298
- ensureConversation('conv-1');
298
+ test("seen signal with no latest assistant message does not set seen cursor", () => {
299
+ ensureConversation("conv-1");
299
300
 
300
301
  // Record seen signal without any assistant message
301
302
  recordConversationSeenSignal({
302
- conversationId: 'conv-1',
303
- assistantId: 'self',
304
- sourceChannel: 'vellum',
305
- signalType: 'macos_notification_view',
306
- confidence: 'inferred',
307
- source: 'desktop-client',
303
+ conversationId: "conv-1",
304
+ assistantId: "self",
305
+ sourceChannel: "vellum",
306
+ signalType: "macos_notification_view",
307
+ confidence: "inferred",
308
+ source: "desktop-client",
308
309
  });
309
310
 
310
- const states = getAttentionStateByConversationIds(['conv-1']);
311
- const state = states.get('conv-1')!;
311
+ const states = getAttentionStateByConversationIds(["conv-1"]);
312
+ const state = states.get("conv-1")!;
312
313
  expect(state.lastSeenAssistantMessageId).toBeNull();
313
314
  expect(state.lastSeenAssistantMessageAt).toBeNull();
314
- expect(state.lastSeenSignalType).toBe('macos_notification_view');
315
+ expect(state.lastSeenSignalType).toBe("macos_notification_view");
315
316
  });
316
317
 
317
- test('already-seen conversation does not regress on additional seen signal', () => {
318
- ensureConversation('conv-1');
318
+ test("already-seen conversation does not regress on additional seen signal", () => {
319
+ ensureConversation("conv-1");
319
320
 
320
321
  projectAssistantMessage({
321
- conversationId: 'conv-1',
322
- assistantId: 'self',
323
- messageId: 'msg-1',
322
+ conversationId: "conv-1",
323
+ assistantId: "self",
324
+ messageId: "msg-1",
324
325
  messageAt: 1000,
325
326
  });
326
327
 
327
328
  // Mark as seen
328
329
  recordConversationSeenSignal({
329
- conversationId: 'conv-1',
330
- assistantId: 'self',
331
- sourceChannel: 'vellum',
332
- signalType: 'macos_conversation_opened',
333
- confidence: 'explicit',
334
- source: 'desktop-client',
330
+ conversationId: "conv-1",
331
+ assistantId: "self",
332
+ sourceChannel: "vellum",
333
+ signalType: "macos_conversation_opened",
334
+ confidence: "explicit",
335
+ source: "desktop-client",
335
336
  });
336
337
 
337
338
  // Record another seen signal (should not regress)
338
339
  recordConversationSeenSignal({
339
- conversationId: 'conv-1',
340
- assistantId: 'self',
341
- sourceChannel: 'telegram',
342
- signalType: 'telegram_inbound_message',
343
- confidence: 'inferred',
344
- source: 'telegram-gateway',
340
+ conversationId: "conv-1",
341
+ assistantId: "self",
342
+ sourceChannel: "telegram",
343
+ signalType: "telegram_inbound_message",
344
+ confidence: "inferred",
345
+ source: "telegram-gateway",
345
346
  });
346
347
 
347
- const states = getAttentionStateByConversationIds(['conv-1']);
348
- const state = states.get('conv-1')!;
348
+ const states = getAttentionStateByConversationIds(["conv-1"]);
349
+ const state = states.get("conv-1")!;
349
350
  // Seen cursor should still point to msg-1
350
- expect(state.lastSeenAssistantMessageId).toBe('msg-1');
351
+ expect(state.lastSeenAssistantMessageId).toBe("msg-1");
351
352
  expect(state.lastSeenAssistantMessageAt).toBe(1000);
352
353
  // Signal metadata should reflect the latest signal
353
- expect(state.lastSeenSignalType).toBe('telegram_inbound_message');
354
+ expect(state.lastSeenSignalType).toBe("telegram_inbound_message");
354
355
  });
355
356
  });
356
357
 
357
358
  // ── getAttentionStateByConversationIds ──────────────────────────
358
359
 
359
- describe('getAttentionStateByConversationIds', () => {
360
- test('returns empty map for empty input', () => {
360
+ describe("getAttentionStateByConversationIds", () => {
361
+ test("returns empty map for empty input", () => {
361
362
  const result = getAttentionStateByConversationIds([]);
362
363
  expect(result.size).toBe(0);
363
364
  });
364
365
 
365
- test('returns states for multiple conversations', () => {
366
- ensureConversation('conv-1');
367
- ensureConversation('conv-2');
366
+ test("returns states for multiple conversations", () => {
367
+ ensureConversation("conv-1");
368
+ ensureConversation("conv-2");
368
369
 
369
370
  projectAssistantMessage({
370
- conversationId: 'conv-1',
371
- assistantId: 'self',
372
- messageId: 'msg-1',
371
+ conversationId: "conv-1",
372
+ assistantId: "self",
373
+ messageId: "msg-1",
373
374
  messageAt: 1000,
374
375
  });
375
376
  projectAssistantMessage({
376
- conversationId: 'conv-2',
377
- assistantId: 'self',
378
- messageId: 'msg-2',
377
+ conversationId: "conv-2",
378
+ assistantId: "self",
379
+ messageId: "msg-2",
379
380
  messageAt: 2000,
380
381
  });
381
382
 
382
- const result = getAttentionStateByConversationIds(['conv-1', 'conv-2']);
383
+ const result = getAttentionStateByConversationIds(["conv-1", "conv-2"]);
383
384
  expect(result.size).toBe(2);
384
- expect(result.get('conv-1')!.latestAssistantMessageId).toBe('msg-1');
385
- expect(result.get('conv-2')!.latestAssistantMessageId).toBe('msg-2');
385
+ expect(result.get("conv-1")!.latestAssistantMessageId).toBe("msg-1");
386
+ expect(result.get("conv-2")!.latestAssistantMessageId).toBe("msg-2");
386
387
  });
387
388
 
388
- test('omits conversations without state', () => {
389
- ensureConversation('conv-1');
390
- ensureConversation('conv-2');
389
+ test("omits conversations without state", () => {
390
+ ensureConversation("conv-1");
391
+ ensureConversation("conv-2");
391
392
 
392
393
  projectAssistantMessage({
393
- conversationId: 'conv-1',
394
- assistantId: 'self',
395
- messageId: 'msg-1',
394
+ conversationId: "conv-1",
395
+ assistantId: "self",
396
+ messageId: "msg-1",
396
397
  messageAt: 1000,
397
398
  });
398
399
 
399
- const result = getAttentionStateByConversationIds(['conv-1', 'conv-2']);
400
+ const result = getAttentionStateByConversationIds(["conv-1", "conv-2"]);
400
401
  expect(result.size).toBe(1);
401
- expect(result.has('conv-1')).toBe(true);
402
- expect(result.has('conv-2')).toBe(false);
402
+ expect(result.has("conv-1")).toBe(true);
403
+ expect(result.has("conv-2")).toBe(false);
403
404
  });
404
405
  });
405
406
 
406
407
  // ── listConversationAttention ───────────────────────────────────
407
408
 
408
- describe('listConversationAttention', () => {
409
- test('returns all states for an assistant', () => {
410
- ensureConversation('conv-1');
411
- ensureConversation('conv-2');
409
+ describe("listConversationAttention", () => {
410
+ test("returns all states for an assistant", () => {
411
+ ensureConversation("conv-1");
412
+ ensureConversation("conv-2");
412
413
 
413
414
  projectAssistantMessage({
414
- conversationId: 'conv-1',
415
- assistantId: 'self',
416
- messageId: 'msg-1',
415
+ conversationId: "conv-1",
416
+ assistantId: "self",
417
+ messageId: "msg-1",
417
418
  messageAt: 1000,
418
419
  });
419
420
  projectAssistantMessage({
420
- conversationId: 'conv-2',
421
- assistantId: 'self',
422
- messageId: 'msg-2',
421
+ conversationId: "conv-2",
422
+ assistantId: "self",
423
+ messageId: "msg-2",
423
424
  messageAt: 2000,
424
425
  });
425
426
 
426
- const result = listConversationAttention({ assistantId: 'self' });
427
+ const result = listConversationAttention({ assistantId: "self" });
427
428
  expect(result).toHaveLength(2);
428
429
  });
429
430
 
430
- test('filters by unseen state', () => {
431
- ensureConversation('conv-1');
432
- ensureConversation('conv-2');
431
+ test("filters by unseen state", () => {
432
+ ensureConversation("conv-1");
433
+ ensureConversation("conv-2");
433
434
 
434
435
  // conv-1: has assistant message, not seen
435
436
  projectAssistantMessage({
436
- conversationId: 'conv-1',
437
- assistantId: 'self',
438
- messageId: 'msg-1',
437
+ conversationId: "conv-1",
438
+ assistantId: "self",
439
+ messageId: "msg-1",
439
440
  messageAt: 1000,
440
441
  });
441
442
 
442
443
  // conv-2: has assistant message, seen
443
444
  projectAssistantMessage({
444
- conversationId: 'conv-2',
445
- assistantId: 'self',
446
- messageId: 'msg-2',
445
+ conversationId: "conv-2",
446
+ assistantId: "self",
447
+ messageId: "msg-2",
447
448
  messageAt: 2000,
448
449
  });
449
450
  recordConversationSeenSignal({
450
- conversationId: 'conv-2',
451
- assistantId: 'self',
452
- sourceChannel: 'vellum',
453
- signalType: 'macos_conversation_opened',
454
- confidence: 'explicit',
455
- source: 'desktop-client',
451
+ conversationId: "conv-2",
452
+ assistantId: "self",
453
+ sourceChannel: "vellum",
454
+ signalType: "macos_conversation_opened",
455
+ confidence: "explicit",
456
+ source: "desktop-client",
456
457
  });
457
458
 
458
- const unseen = listConversationAttention({ assistantId: 'self', state: 'unseen' });
459
+ const unseen = listConversationAttention({
460
+ assistantId: "self",
461
+ state: "unseen",
462
+ });
459
463
  expect(unseen).toHaveLength(1);
460
- expect(unseen[0].conversationId).toBe('conv-1');
464
+ expect(unseen[0].conversationId).toBe("conv-1");
461
465
  });
462
466
 
463
- test('filters by seen state', () => {
464
- ensureConversation('conv-1');
465
- ensureConversation('conv-2');
467
+ test("filters by seen state", () => {
468
+ ensureConversation("conv-1");
469
+ ensureConversation("conv-2");
466
470
 
467
471
  projectAssistantMessage({
468
- conversationId: 'conv-1',
469
- assistantId: 'self',
470
- messageId: 'msg-1',
472
+ conversationId: "conv-1",
473
+ assistantId: "self",
474
+ messageId: "msg-1",
471
475
  messageAt: 1000,
472
476
  });
473
477
 
474
478
  projectAssistantMessage({
475
- conversationId: 'conv-2',
476
- assistantId: 'self',
477
- messageId: 'msg-2',
479
+ conversationId: "conv-2",
480
+ assistantId: "self",
481
+ messageId: "msg-2",
478
482
  messageAt: 2000,
479
483
  });
480
484
  recordConversationSeenSignal({
481
- conversationId: 'conv-2',
482
- assistantId: 'self',
483
- sourceChannel: 'vellum',
484
- signalType: 'macos_conversation_opened',
485
- confidence: 'explicit',
486
- source: 'desktop-client',
485
+ conversationId: "conv-2",
486
+ assistantId: "self",
487
+ sourceChannel: "vellum",
488
+ signalType: "macos_conversation_opened",
489
+ confidence: "explicit",
490
+ source: "desktop-client",
487
491
  });
488
492
 
489
- const seen = listConversationAttention({ assistantId: 'self', state: 'seen' });
493
+ const seen = listConversationAttention({
494
+ assistantId: "self",
495
+ state: "seen",
496
+ });
490
497
  expect(seen).toHaveLength(1);
491
- expect(seen[0].conversationId).toBe('conv-2');
498
+ expect(seen[0].conversationId).toBe("conv-2");
492
499
  });
493
500
 
494
- test('respects limit parameter', () => {
495
- ensureConversation('conv-1');
496
- ensureConversation('conv-2');
497
- ensureConversation('conv-3');
501
+ test("respects limit parameter", () => {
502
+ ensureConversation("conv-1");
503
+ ensureConversation("conv-2");
504
+ ensureConversation("conv-3");
498
505
 
499
- projectAssistantMessage({ conversationId: 'conv-1', assistantId: 'self', messageId: 'msg-1', messageAt: 1000 });
500
- projectAssistantMessage({ conversationId: 'conv-2', assistantId: 'self', messageId: 'msg-2', messageAt: 2000 });
501
- projectAssistantMessage({ conversationId: 'conv-3', assistantId: 'self', messageId: 'msg-3', messageAt: 3000 });
506
+ projectAssistantMessage({
507
+ conversationId: "conv-1",
508
+ assistantId: "self",
509
+ messageId: "msg-1",
510
+ messageAt: 1000,
511
+ });
512
+ projectAssistantMessage({
513
+ conversationId: "conv-2",
514
+ assistantId: "self",
515
+ messageId: "msg-2",
516
+ messageAt: 2000,
517
+ });
518
+ projectAssistantMessage({
519
+ conversationId: "conv-3",
520
+ assistantId: "self",
521
+ messageId: "msg-3",
522
+ messageAt: 3000,
523
+ });
502
524
 
503
- const result = listConversationAttention({ assistantId: 'self', limit: 2 });
525
+ const result = listConversationAttention({
526
+ assistantId: "self",
527
+ limit: 2,
528
+ });
504
529
  expect(result).toHaveLength(2);
505
530
  });
506
531
 
507
- test('orders by latest assistant message descending', () => {
508
- ensureConversation('conv-1');
509
- ensureConversation('conv-2');
510
- ensureConversation('conv-3');
532
+ test("orders by latest assistant message descending", () => {
533
+ ensureConversation("conv-1");
534
+ ensureConversation("conv-2");
535
+ ensureConversation("conv-3");
511
536
 
512
- projectAssistantMessage({ conversationId: 'conv-1', assistantId: 'self', messageId: 'msg-1', messageAt: 1000 });
513
- projectAssistantMessage({ conversationId: 'conv-2', assistantId: 'self', messageId: 'msg-2', messageAt: 3000 });
514
- projectAssistantMessage({ conversationId: 'conv-3', assistantId: 'self', messageId: 'msg-3', messageAt: 2000 });
537
+ projectAssistantMessage({
538
+ conversationId: "conv-1",
539
+ assistantId: "self",
540
+ messageId: "msg-1",
541
+ messageAt: 1000,
542
+ });
543
+ projectAssistantMessage({
544
+ conversationId: "conv-2",
545
+ assistantId: "self",
546
+ messageId: "msg-2",
547
+ messageAt: 3000,
548
+ });
549
+ projectAssistantMessage({
550
+ conversationId: "conv-3",
551
+ assistantId: "self",
552
+ messageId: "msg-3",
553
+ messageAt: 2000,
554
+ });
515
555
 
516
- const result = listConversationAttention({ assistantId: 'self' });
517
- expect(result[0].conversationId).toBe('conv-2');
518
- expect(result[1].conversationId).toBe('conv-3');
519
- expect(result[2].conversationId).toBe('conv-1');
556
+ const result = listConversationAttention({ assistantId: "self" });
557
+ expect(result[0].conversationId).toBe("conv-2");
558
+ expect(result[1].conversationId).toBe("conv-3");
559
+ expect(result[2].conversationId).toBe("conv-1");
520
560
  });
521
561
 
522
- test('before cursor filters out newer conversations', () => {
523
- ensureConversation('conv-1');
524
- ensureConversation('conv-2');
525
- ensureConversation('conv-3');
562
+ test("before cursor filters out newer conversations", () => {
563
+ ensureConversation("conv-1");
564
+ ensureConversation("conv-2");
565
+ ensureConversation("conv-3");
526
566
 
527
- projectAssistantMessage({ conversationId: 'conv-1', assistantId: 'self', messageId: 'msg-1', messageAt: 1000 });
528
- projectAssistantMessage({ conversationId: 'conv-2', assistantId: 'self', messageId: 'msg-2', messageAt: 2000 });
529
- projectAssistantMessage({ conversationId: 'conv-3', assistantId: 'self', messageId: 'msg-3', messageAt: 3000 });
567
+ projectAssistantMessage({
568
+ conversationId: "conv-1",
569
+ assistantId: "self",
570
+ messageId: "msg-1",
571
+ messageAt: 1000,
572
+ });
573
+ projectAssistantMessage({
574
+ conversationId: "conv-2",
575
+ assistantId: "self",
576
+ messageId: "msg-2",
577
+ messageAt: 2000,
578
+ });
579
+ projectAssistantMessage({
580
+ conversationId: "conv-3",
581
+ assistantId: "self",
582
+ messageId: "msg-3",
583
+ messageAt: 3000,
584
+ });
530
585
 
531
- const result = listConversationAttention({ assistantId: 'self', before: 2500 });
586
+ const result = listConversationAttention({
587
+ assistantId: "self",
588
+ before: 2500,
589
+ });
532
590
  expect(result).toHaveLength(2);
533
- expect(result[0].conversationId).toBe('conv-2');
534
- expect(result[1].conversationId).toBe('conv-1');
591
+ expect(result[0].conversationId).toBe("conv-2");
592
+ expect(result[1].conversationId).toBe("conv-1");
535
593
  });
536
594
 
537
- test('filters by different assistant ID', () => {
538
- ensureConversation('conv-1');
595
+ test("filters by different assistant ID", () => {
596
+ ensureConversation("conv-1");
539
597
 
540
598
  projectAssistantMessage({
541
- conversationId: 'conv-1',
542
- assistantId: 'self',
543
- messageId: 'msg-1',
599
+ conversationId: "conv-1",
600
+ assistantId: "self",
601
+ messageId: "msg-1",
544
602
  messageAt: 1000,
545
603
  });
546
604
 
547
- const result = listConversationAttention({ assistantId: 'other-assistant' });
605
+ const result = listConversationAttention({
606
+ assistantId: "other-assistant",
607
+ });
548
608
  expect(result).toHaveLength(0);
549
609
  });
550
610
  });
551
611
 
552
612
  // ── Evidence immutability ───────────────────────────────────────
553
613
 
554
- describe('evidence immutability', () => {
555
- test('multiple seen signals append separate event rows', () => {
556
- ensureConversation('conv-1');
614
+ describe("evidence immutability", () => {
615
+ test("multiple seen signals append separate event rows", () => {
616
+ ensureConversation("conv-1");
557
617
 
558
618
  projectAssistantMessage({
559
- conversationId: 'conv-1',
560
- assistantId: 'self',
561
- messageId: 'msg-1',
619
+ conversationId: "conv-1",
620
+ assistantId: "self",
621
+ messageId: "msg-1",
562
622
  messageAt: 1000,
563
623
  });
564
624
 
565
625
  recordConversationSeenSignal({
566
- conversationId: 'conv-1',
567
- assistantId: 'self',
568
- sourceChannel: 'vellum',
569
- signalType: 'macos_notification_view',
570
- confidence: 'inferred',
571
- source: 'desktop-client',
626
+ conversationId: "conv-1",
627
+ assistantId: "self",
628
+ sourceChannel: "vellum",
629
+ signalType: "macos_notification_view",
630
+ confidence: "inferred",
631
+ source: "desktop-client",
572
632
  });
573
633
 
574
634
  recordConversationSeenSignal({
575
- conversationId: 'conv-1',
576
- assistantId: 'self',
577
- sourceChannel: 'vellum',
578
- signalType: 'macos_conversation_opened',
579
- confidence: 'explicit',
580
- source: 'desktop-client',
635
+ conversationId: "conv-1",
636
+ assistantId: "self",
637
+ sourceChannel: "vellum",
638
+ signalType: "macos_conversation_opened",
639
+ confidence: "explicit",
640
+ source: "desktop-client",
581
641
  });
582
642
 
583
643
  const db = getDb();
584
644
  const events = db
585
645
  .select()
586
646
  .from(conversationAttentionEvents)
587
- .where(eq(conversationAttentionEvents.conversationId, 'conv-1'))
647
+ .where(eq(conversationAttentionEvents.conversationId, "conv-1"))
588
648
  .all();
589
649
 
590
650
  expect(events).toHaveLength(2);