@vellumai/assistant 0.4.49 → 0.4.51

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 (353) hide show
  1. package/ARCHITECTURE.md +24 -33
  2. package/README.md +3 -3
  3. package/docs/architecture/integrations.md +2 -2
  4. package/docs/architecture/keychain-broker.md +6 -6
  5. package/docs/architecture/memory.md +180 -119
  6. package/knip.json +32 -0
  7. package/package.json +3 -2
  8. package/src/__tests__/agent-loop.test.ts +3 -1
  9. package/src/__tests__/anthropic-provider.test.ts +114 -23
  10. package/src/__tests__/approval-cascade.test.ts +1 -15
  11. package/src/__tests__/approval-routes-http.test.ts +2 -0
  12. package/src/__tests__/assistant-feature-flag-guard.test.ts +0 -23
  13. package/src/__tests__/btw-routes.test.ts +61 -5
  14. package/src/__tests__/canonical-guardian-store.test.ts +95 -0
  15. package/src/__tests__/checker.test.ts +13 -0
  16. package/src/__tests__/config-schema.test.ts +1 -68
  17. package/src/__tests__/config-watcher.test.ts +8 -0
  18. package/src/__tests__/context-memory-e2e.test.ts +11 -100
  19. package/src/__tests__/conversation-routes-guardian-reply.test.ts +8 -0
  20. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
  21. package/src/__tests__/credential-security-e2e.test.ts +1 -0
  22. package/src/__tests__/credential-security-invariants.test.ts +8 -7
  23. package/src/__tests__/credential-vault-unit.test.ts +23 -18
  24. package/src/__tests__/credential-vault.test.ts +30 -18
  25. package/src/__tests__/credentials-cli.test.ts +257 -82
  26. package/src/__tests__/cu-unified-flow.test.ts +532 -0
  27. package/src/__tests__/date-context.test.ts +93 -77
  28. package/src/__tests__/deterministic-verification-control-plane.test.ts +64 -0
  29. package/src/__tests__/guardian-routing-invariants.test.ts +93 -0
  30. package/src/__tests__/history-repair.test.ts +245 -0
  31. package/src/__tests__/host-cu-proxy.test.ts +165 -3
  32. package/src/__tests__/http-user-message-parity.test.ts +1 -0
  33. package/src/__tests__/inbound-invite-redemption.test.ts +36 -7
  34. package/src/__tests__/integration-status.test.ts +31 -30
  35. package/src/__tests__/invite-redemption-service.test.ts +166 -13
  36. package/src/__tests__/invite-routes-http.test.ts +166 -5
  37. package/src/__tests__/keychain-broker-client.test.ts +4 -4
  38. package/src/__tests__/list-messages-attachments.test.ts +193 -0
  39. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +56 -18
  40. package/src/__tests__/memory-lifecycle-e2e.test.ts +244 -387
  41. package/src/__tests__/memory-recall-quality.test.ts +244 -407
  42. package/src/__tests__/memory-regressions.experimental.test.ts +126 -101
  43. package/src/__tests__/memory-regressions.test.ts +477 -2841
  44. package/src/__tests__/memory-retrieval.benchmark.test.ts +33 -150
  45. package/src/__tests__/memory-upsert-concurrency.test.ts +5 -244
  46. package/src/__tests__/mime-builder.test.ts +28 -0
  47. package/src/__tests__/native-web-search.test.ts +1 -0
  48. package/src/__tests__/oauth-cli.test.ts +824 -31
  49. package/src/__tests__/oauth-provider-profiles.test.ts +1 -1
  50. package/src/__tests__/oauth-store.test.ts +363 -17
  51. package/src/__tests__/qdrant-collection-migration.test.ts +53 -8
  52. package/src/__tests__/registry.test.ts +0 -1
  53. package/src/__tests__/relay-server.test.ts +55 -1
  54. package/src/__tests__/schedule-tools.test.ts +32 -0
  55. package/src/__tests__/script-proxy-certs.test.ts +1 -1
  56. package/src/__tests__/secret-onetime-send.test.ts +1 -0
  57. package/src/__tests__/secret-routes-managed-proxy.test.ts +183 -0
  58. package/src/__tests__/secure-keys.test.ts +78 -18
  59. package/src/__tests__/send-endpoint-busy.test.ts +3 -0
  60. package/src/__tests__/server-history-render.test.ts +2 -2
  61. package/src/__tests__/session-abort-tool-results.test.ts +1 -14
  62. package/src/__tests__/session-agent-loop-overflow.test.ts +1583 -0
  63. package/src/__tests__/session-agent-loop.test.ts +19 -15
  64. package/src/__tests__/session-confirmation-signals.test.ts +1 -15
  65. package/src/__tests__/session-error.test.ts +124 -2
  66. package/src/__tests__/session-history-web-search.test.ts +918 -0
  67. package/src/__tests__/session-pre-run-repair.test.ts +1 -14
  68. package/src/__tests__/session-provider-retry-repair.test.ts +25 -28
  69. package/src/__tests__/session-queue.test.ts +37 -27
  70. package/src/__tests__/session-runtime-assembly.test.ts +54 -0
  71. package/src/__tests__/session-slash-known.test.ts +1 -15
  72. package/src/__tests__/session-slash-queue.test.ts +1 -15
  73. package/src/__tests__/session-slash-unknown.test.ts +1 -15
  74. package/src/__tests__/session-workspace-cache-state.test.ts +3 -33
  75. package/src/__tests__/session-workspace-injection.test.ts +3 -37
  76. package/src/__tests__/session-workspace-tool-tracking.test.ts +3 -37
  77. package/src/__tests__/skills-install-extract.test.ts +93 -0
  78. package/src/__tests__/skills.test.ts +2 -2
  79. package/src/__tests__/skillssh-registry.test.ts +451 -0
  80. package/src/__tests__/slack-channel-config.test.ts +10 -8
  81. package/src/__tests__/trust-store.test.ts +15 -0
  82. package/src/__tests__/twilio-config.test.ts +11 -10
  83. package/src/__tests__/twilio-provider.test.ts +9 -4
  84. package/src/__tests__/voice-invite-redemption.test.ts +85 -5
  85. package/src/agent/ax-tree-compaction.test.ts +51 -0
  86. package/src/agent/loop.ts +39 -12
  87. package/src/approvals/AGENTS.md +1 -1
  88. package/src/approvals/guardian-request-resolvers.ts +14 -2
  89. package/src/bundler/compiler-tools.ts +66 -2
  90. package/src/calls/call-domain.ts +134 -3
  91. package/src/calls/call-store.ts +6 -0
  92. package/src/calls/relay-server.ts +44 -6
  93. package/src/calls/relay-setup-router.ts +17 -1
  94. package/src/calls/twilio-config.ts +5 -4
  95. package/src/calls/twilio-provider.ts +14 -9
  96. package/src/calls/twilio-rest.ts +10 -7
  97. package/src/calls/types.ts +3 -1
  98. package/src/cli/commands/config.ts +14 -9
  99. package/src/cli/commands/contacts.ts +3 -0
  100. package/src/cli/commands/credentials.ts +170 -174
  101. package/src/cli/commands/doctor.ts +11 -8
  102. package/src/cli/commands/keys.ts +9 -9
  103. package/src/cli/commands/mcp.ts +46 -59
  104. package/src/cli/commands/memory.ts +16 -165
  105. package/src/cli/commands/oauth/apps.ts +68 -10
  106. package/src/cli/commands/oauth/connections.ts +475 -105
  107. package/src/cli/commands/oauth/index.ts +3 -3
  108. package/src/cli/commands/oauth/providers.ts +18 -4
  109. package/src/cli/commands/sessions.ts +5 -2
  110. package/src/cli/commands/skills.ts +173 -1
  111. package/src/cli/http-client.ts +0 -20
  112. package/src/cli/main-screen.tsx +2 -2
  113. package/src/cli/program.ts +5 -6
  114. package/src/cli.ts +20 -22
  115. package/src/config/__tests__/feature-flag-registry-bundled.test.ts +39 -0
  116. package/src/config/bundled-skills/computer-use/TOOLS.json +1 -1
  117. package/src/config/bundled-skills/computer-use/tools/computer-use-observe.ts +12 -0
  118. package/src/config/bundled-skills/contacts/SKILL.md +35 -11
  119. package/src/config/bundled-skills/contacts/tools/google-contacts.ts +1 -1
  120. package/src/config/bundled-skills/gmail/SKILL.md +1 -1
  121. package/src/config/bundled-skills/gmail/TOOLS.json +52 -0
  122. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +13 -3
  123. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +9 -2
  124. package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +5 -1
  125. package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +5 -1
  126. package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +5 -1
  127. package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +5 -1
  128. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +9 -2
  129. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +5 -1
  130. package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +5 -1
  131. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +5 -1
  132. package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +5 -1
  133. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +5 -1
  134. package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +5 -1
  135. package/src/config/bundled-skills/google-calendar/TOOLS.json +20 -0
  136. package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +2 -1
  137. package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +2 -1
  138. package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +2 -1
  139. package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +2 -1
  140. package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +2 -1
  141. package/src/config/bundled-skills/google-calendar/tools/shared.ts +8 -2
  142. package/src/config/bundled-skills/messaging/SKILL.md +1 -1
  143. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -2
  144. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
  145. package/src/config/bundled-skills/messaging/tools/messaging-auth-test.ts +2 -2
  146. package/src/config/bundled-skills/messaging/tools/messaging-list-conversations.ts +2 -2
  147. package/src/config/bundled-skills/messaging/tools/messaging-mark-read.ts +2 -2
  148. package/src/config/bundled-skills/messaging/tools/messaging-read.ts +2 -2
  149. package/src/config/bundled-skills/messaging/tools/messaging-search.ts +2 -2
  150. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +2 -2
  151. package/src/config/bundled-skills/messaging/tools/messaging-sender-digest.ts +2 -2
  152. package/src/config/bundled-skills/messaging/tools/shared.ts +7 -5
  153. package/src/config/bundled-skills/slack/tools/shared.ts +1 -1
  154. package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +1 -1
  155. package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +1 -1
  156. package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +1 -1
  157. package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +1 -1
  158. package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +1 -1
  159. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +1 -1
  160. package/src/config/bundled-tool-registry.ts +2 -5
  161. package/src/config/loader.ts +6 -42
  162. package/src/config/schema.ts +1 -12
  163. package/src/config/schemas/memory-lifecycle.ts +0 -9
  164. package/src/config/schemas/memory-processing.ts +0 -180
  165. package/src/config/schemas/memory-retrieval.ts +32 -104
  166. package/src/config/schemas/memory.ts +0 -10
  167. package/src/config/types.ts +0 -4
  168. package/src/contacts/contact-store.ts +39 -2
  169. package/src/contacts/contacts-write.ts +9 -0
  170. package/src/context/window-manager.ts +4 -1
  171. package/src/daemon/config-watcher.ts +55 -2
  172. package/src/daemon/daemon-control.ts +1 -1
  173. package/src/daemon/date-context.ts +114 -31
  174. package/src/daemon/handlers/config-ingress.ts +2 -2
  175. package/src/daemon/handlers/config-slack-channel.ts +59 -39
  176. package/src/daemon/handlers/config-telegram.ts +23 -14
  177. package/src/daemon/handlers/session-history.ts +1 -358
  178. package/src/daemon/handlers/sessions.ts +18 -13
  179. package/src/daemon/handlers/shared.ts +3 -17
  180. package/src/daemon/handlers/skills.ts +20 -1
  181. package/src/daemon/history-repair.ts +72 -8
  182. package/src/daemon/host-cu-proxy.ts +55 -26
  183. package/src/daemon/lifecycle.ts +39 -4
  184. package/src/daemon/mcp-reload-service.ts +2 -2
  185. package/src/daemon/message-types/computer-use.ts +1 -12
  186. package/src/daemon/message-types/memory.ts +4 -16
  187. package/src/daemon/message-types/messages.ts +1 -0
  188. package/src/daemon/message-types/sessions.ts +4 -42
  189. package/src/daemon/server.ts +6 -1
  190. package/src/daemon/session-agent-loop-handlers.ts +38 -0
  191. package/src/daemon/session-agent-loop.ts +334 -48
  192. package/src/daemon/session-error.ts +89 -6
  193. package/src/daemon/session-history.ts +17 -7
  194. package/src/daemon/session-media-retry.ts +6 -2
  195. package/src/daemon/session-memory.ts +69 -149
  196. package/src/daemon/session-process.ts +10 -1
  197. package/src/daemon/session-runtime-assembly.ts +49 -19
  198. package/src/daemon/session-slash.ts +3 -5
  199. package/src/daemon/session-surfaces.ts +4 -1
  200. package/src/daemon/session-tool-setup.ts +7 -1
  201. package/src/daemon/session.ts +12 -2
  202. package/src/email/providers/index.ts +2 -2
  203. package/src/instrument.ts +61 -1
  204. package/src/media/avatar-router.ts +1 -1
  205. package/src/memory/admin.ts +2 -191
  206. package/src/memory/canonical-guardian-store.ts +38 -2
  207. package/src/memory/conversation-crud.ts +0 -33
  208. package/src/memory/conversation-queries.ts +25 -83
  209. package/src/memory/db-init.ts +32 -0
  210. package/src/memory/embedding-backend.ts +84 -8
  211. package/src/memory/embedding-types.ts +9 -1
  212. package/src/memory/indexer.ts +7 -46
  213. package/src/memory/invite-store.ts +19 -0
  214. package/src/memory/items-extractor.ts +274 -76
  215. package/src/memory/job-handlers/backfill.ts +2 -127
  216. package/src/memory/job-handlers/cleanup.ts +2 -16
  217. package/src/memory/job-handlers/extraction.ts +2 -138
  218. package/src/memory/job-handlers/index-maintenance.ts +1 -6
  219. package/src/memory/job-handlers/summarization.ts +3 -148
  220. package/src/memory/job-utils.ts +21 -59
  221. package/src/memory/jobs-store.ts +1 -159
  222. package/src/memory/jobs-worker.ts +9 -52
  223. package/src/memory/migrations/104-core-indexes.ts +3 -3
  224. package/src/memory/migrations/149-oauth-tables.ts +2 -0
  225. package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +98 -0
  226. package/src/memory/migrations/151-oauth-providers-ping-url.ts +11 -0
  227. package/src/memory/migrations/152-memory-item-supersession.ts +44 -0
  228. package/src/memory/migrations/153-drop-entity-tables.ts +15 -0
  229. package/src/memory/migrations/154-drop-fts.ts +20 -0
  230. package/src/memory/migrations/155-drop-conflicts.ts +7 -0
  231. package/src/memory/migrations/156-call-session-invite-metadata.ts +24 -0
  232. package/src/memory/migrations/157-invite-contact-id.ts +104 -0
  233. package/src/memory/migrations/index.ts +8 -0
  234. package/src/memory/migrations/registry.ts +6 -0
  235. package/src/memory/qdrant-client.ts +148 -51
  236. package/src/memory/raw-query.ts +1 -1
  237. package/src/memory/retriever.test.ts +294 -273
  238. package/src/memory/retriever.ts +421 -645
  239. package/src/memory/schema/calls.ts +2 -0
  240. package/src/memory/schema/contacts.ts +1 -0
  241. package/src/memory/schema/memory-core.ts +3 -48
  242. package/src/memory/schema/oauth.ts +2 -0
  243. package/src/memory/search/formatting.ts +263 -176
  244. package/src/memory/search/lexical.ts +1 -254
  245. package/src/memory/search/ranking.ts +0 -455
  246. package/src/memory/search/semantic.ts +100 -14
  247. package/src/memory/search/staleness.ts +47 -0
  248. package/src/memory/search/tier-classifier.ts +21 -0
  249. package/src/memory/search/types.ts +15 -77
  250. package/src/memory/task-memory-cleanup.ts +4 -6
  251. package/src/messaging/provider.ts +1 -1
  252. package/src/messaging/providers/gmail/adapter.ts +1 -1
  253. package/src/messaging/providers/gmail/mime-builder.ts +17 -7
  254. package/src/messaging/providers/telegram-bot/adapter.ts +17 -8
  255. package/src/messaging/providers/whatsapp/adapter.ts +13 -9
  256. package/src/messaging/registry.ts +9 -5
  257. package/src/oauth/byo-connection.test.ts +40 -25
  258. package/src/oauth/connect-orchestrator.ts +4 -10
  259. package/src/oauth/connection-resolver.ts +20 -6
  260. package/src/oauth/manual-token-connection.ts +5 -5
  261. package/src/oauth/oauth-store.ts +183 -31
  262. package/src/oauth/platform-connection.test.ts +1 -1
  263. package/src/oauth/provider-behaviors.ts +503 -4
  264. package/src/oauth/seed-providers.ts +214 -8
  265. package/src/oauth/token-persistence.ts +31 -16
  266. package/src/permissions/defaults.ts +1 -0
  267. package/src/permissions/trust-store.ts +23 -1
  268. package/src/playbooks/playbook-compiler.ts +1 -1
  269. package/src/prompts/system-prompt.ts +18 -2
  270. package/src/providers/anthropic/client.ts +56 -126
  271. package/src/providers/types.ts +7 -1
  272. package/src/runtime/AGENTS.md +9 -0
  273. package/src/runtime/auth/route-policy.ts +6 -3
  274. package/src/runtime/channel-readiness-service.ts +48 -40
  275. package/src/runtime/guardian-reply-router.ts +24 -22
  276. package/src/runtime/http-server.ts +2 -2
  277. package/src/runtime/http-types.ts +2 -0
  278. package/src/runtime/invite-redemption-service.ts +72 -12
  279. package/src/runtime/invite-service.ts +43 -0
  280. package/src/runtime/middleware/twilio-validation.ts +1 -1
  281. package/src/runtime/pending-interactions.ts +2 -2
  282. package/src/runtime/routes/brain-graph-routes.ts +10 -90
  283. package/src/runtime/routes/btw-routes.ts +10 -5
  284. package/src/runtime/routes/conversation-routes.ts +56 -11
  285. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -12
  286. package/src/runtime/routes/integrations/slack/channel.ts +2 -2
  287. package/src/runtime/routes/integrations/telegram.ts +2 -2
  288. package/src/runtime/routes/integrations/twilio.ts +17 -17
  289. package/src/runtime/routes/invite-routes.ts +29 -4
  290. package/src/runtime/routes/memory-item-routes.test.ts +754 -0
  291. package/src/runtime/routes/memory-item-routes.ts +503 -0
  292. package/src/runtime/routes/secret-routes.ts +17 -0
  293. package/src/runtime/routes/session-management-routes.ts +3 -3
  294. package/src/runtime/routes/settings-routes.ts +3 -3
  295. package/src/runtime/routes/trust-rules-routes.ts +14 -0
  296. package/src/runtime/routes/workspace-routes.ts +9 -4
  297. package/src/runtime/routes/workspace-utils.ts +8 -2
  298. package/src/schedule/integration-status.ts +26 -19
  299. package/src/security/keychain-broker-client.ts +17 -4
  300. package/src/security/oauth2.ts +6 -7
  301. package/src/security/secure-keys.ts +44 -19
  302. package/src/security/token-manager.ts +46 -39
  303. package/src/services/vercel-deploy.ts +0 -24
  304. package/src/signals/confirm.ts +78 -0
  305. package/src/signals/mcp-reload.ts +18 -0
  306. package/src/skills/catalog-install.ts +74 -18
  307. package/src/skills/skillssh-registry.ts +503 -0
  308. package/src/tools/assets/search.ts +5 -1
  309. package/src/tools/computer-use/definitions.ts +0 -10
  310. package/src/tools/computer-use/registry.ts +1 -1
  311. package/src/tools/credentials/vault.ts +22 -7
  312. package/src/tools/memory/definitions.ts +4 -13
  313. package/src/tools/memory/handlers.test.ts +83 -103
  314. package/src/tools/memory/handlers.ts +50 -85
  315. package/src/tools/network/script-proxy/session-manager.ts +8 -8
  316. package/src/tools/schedule/create.ts +10 -3
  317. package/src/tools/schedule/update.ts +8 -1
  318. package/src/tools/skills/load.ts +25 -2
  319. package/src/watcher/provider-types.ts +1 -1
  320. package/src/watcher/providers/github.ts +1 -1
  321. package/src/watcher/providers/gmail.ts +3 -3
  322. package/src/watcher/providers/google-calendar.ts +3 -3
  323. package/src/watcher/providers/linear.ts +1 -1
  324. package/src/__tests__/clarification-resolver.test.ts +0 -193
  325. package/src/__tests__/conflict-intent-tokenization.test.ts +0 -160
  326. package/src/__tests__/conflict-policy.test.ts +0 -269
  327. package/src/__tests__/conflict-store.test.ts +0 -372
  328. package/src/__tests__/contradiction-checker.test.ts +0 -361
  329. package/src/__tests__/entity-extractor.test.ts +0 -211
  330. package/src/__tests__/entity-search.test.ts +0 -1117
  331. package/src/__tests__/profile-compiler.test.ts +0 -392
  332. package/src/__tests__/session-conflict-gate.test.ts +0 -1228
  333. package/src/__tests__/session-profile-injection.test.ts +0 -557
  334. package/src/config/bundled-skills/knowledge-graph/SKILL.md +0 -25
  335. package/src/config/bundled-skills/knowledge-graph/TOOLS.json +0 -66
  336. package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +0 -211
  337. package/src/daemon/session-conflict-gate.ts +0 -167
  338. package/src/daemon/session-dynamic-profile.ts +0 -77
  339. package/src/memory/clarification-resolver.ts +0 -417
  340. package/src/memory/conflict-intent.ts +0 -205
  341. package/src/memory/conflict-policy.ts +0 -127
  342. package/src/memory/conflict-store.ts +0 -410
  343. package/src/memory/contradiction-checker.ts +0 -508
  344. package/src/memory/entity-extractor.ts +0 -535
  345. package/src/memory/format-recall.ts +0 -47
  346. package/src/memory/fts-reconciler.ts +0 -165
  347. package/src/memory/job-handlers/conflict.ts +0 -200
  348. package/src/memory/profile-compiler.ts +0 -195
  349. package/src/memory/recall-cache.ts +0 -117
  350. package/src/memory/search/entity.ts +0 -535
  351. package/src/memory/search/query-expansion.test.ts +0 -70
  352. package/src/memory/search/query-expansion.ts +0 -118
  353. package/src/runtime/routes/mcp-routes.ts +0 -20
@@ -20,9 +20,6 @@ import { computeRecallBudget } from "../memory/retrieval-budget.js";
20
20
  import { buildMemoryRecall } from "../memory/retriever.js";
21
21
  import {
22
22
  conversations,
23
- memoryEntities,
24
- memoryEntityRelations,
25
- memoryItemEntities,
26
23
  memoryItems,
27
24
  memoryItemSources,
28
25
  messages,
@@ -52,6 +49,7 @@ mock.module("../util/logger.js", () => ({
52
49
  mock.module("../memory/qdrant-client.js", () => ({
53
50
  getQdrantClient: () => ({
54
51
  searchWithFilter: async () => [],
52
+ hybridSearch: async () => [],
55
53
  upsertPoints: async () => {},
56
54
  deletePoints: async () => {},
57
55
  }),
@@ -168,13 +166,9 @@ describe("Context + Memory E2E regression", () => {
168
166
  beforeEach(() => {
169
167
  const db = getDb();
170
168
  db.run("DELETE FROM memory_item_sources");
171
- db.run("DELETE FROM memory_item_entities");
172
- db.run("DELETE FROM memory_entity_relations");
173
- db.run("DELETE FROM memory_entities");
174
169
  db.run("DELETE FROM memory_embeddings");
175
- db.run("DELETE FROM memory_summaries");
176
170
  db.run("DELETE FROM memory_items");
177
- db.run("DELETE FROM memory_segment_fts");
171
+
178
172
  db.run("DELETE FROM memory_segments");
179
173
  db.run("DELETE FROM messages");
180
174
  db.run("DELETE FROM conversations");
@@ -279,43 +273,6 @@ describe("Context + Memory E2E regression", () => {
279
273
  now + 100_000,
280
274
  );
281
275
 
282
- db.insert(memoryEntities)
283
- .values([
284
- {
285
- id: "entity-apollo",
286
- name: "Apollo",
287
- type: "project",
288
- aliases: JSON.stringify(["project-apollo"]),
289
- description: null,
290
- firstSeenAt: now,
291
- lastSeenAt: now + 100_000,
292
- mentionCount: 6,
293
- },
294
- {
295
- id: "entity-hermes",
296
- name: "HermesGate",
297
- type: "strategy",
298
- aliases: JSON.stringify(["hermes-gate"]),
299
- description: null,
300
- firstSeenAt: now,
301
- lastSeenAt: now + 100_000,
302
- mentionCount: 4,
303
- },
304
- ])
305
- .run();
306
-
307
- db.insert(memoryEntityRelations)
308
- .values({
309
- id: "rel-apollo-hermes",
310
- sourceEntityId: "entity-apollo",
311
- targetEntityId: "entity-hermes",
312
- relation: "uses",
313
- evidence: "Apollo uses HermesGate for risky changes",
314
- firstSeenAt: now,
315
- lastSeenAt: now + 50_000,
316
- })
317
- .run();
318
-
319
276
  insertMemoryItem({
320
277
  id: "item-apollo-direct",
321
278
  kind: "preference",
@@ -335,16 +292,10 @@ describe("Context + Memory E2E regression", () => {
335
292
  createdAt: now + 10_000,
336
293
  })
337
294
  .run();
338
- db.insert(memoryItemEntities)
339
- .values({
340
- memoryItemId: "item-apollo-direct",
341
- entityId: "entity-apollo",
342
- })
343
- .run();
344
295
 
345
296
  insertMemoryItem({
346
297
  id: "item-hermes-relation",
347
- kind: "fact",
298
+ kind: "identity",
348
299
  subject: "hermes rollout execution",
349
300
  statement:
350
301
  "HermesGate rollout guidance: start at 5% traffic and promote only after error budget checks pass.",
@@ -361,12 +312,6 @@ describe("Context + Memory E2E regression", () => {
361
312
  createdAt: now + 12_000,
362
313
  })
363
314
  .run();
364
- db.insert(memoryItemEntities)
365
- .values({
366
- memoryItemId: "item-hermes-relation",
367
- entityId: "entity-hermes",
368
- })
369
- .run();
370
315
 
371
316
  insertMemoryItem({
372
317
  id: "item-apollo-stale",
@@ -387,16 +332,10 @@ describe("Context + Memory E2E regression", () => {
387
332
  createdAt: now - 390 * 24 * 60 * 60 * 1000,
388
333
  })
389
334
  .run();
390
- db.insert(memoryItemEntities)
391
- .values({
392
- memoryItemId: "item-apollo-stale",
393
- entityId: "entity-apollo",
394
- })
395
- .run();
396
335
 
397
336
  insertMemoryItem({
398
337
  id: "item-apollo-secret",
399
- kind: "fact",
338
+ kind: "identity",
400
339
  subject: "apollo secret",
401
340
  statement: "Current-turn secret: Apollo code is 123XYZ.",
402
341
  confidence: 0.99,
@@ -412,12 +351,6 @@ describe("Context + Memory E2E regression", () => {
412
351
  createdAt: now + 100_000,
413
352
  })
414
353
  .run();
415
- db.insert(memoryItemEntities)
416
- .values({
417
- memoryItemId: "item-apollo-secret",
418
- entityId: "entity-apollo",
419
- })
420
- .run();
421
354
 
422
355
  const summaryCounter = { calls: 0 };
423
356
  const provider = makeSummaryProvider(summaryCounter);
@@ -454,13 +387,7 @@ describe("Context + Memory E2E regression", () => {
454
387
  },
455
388
  retrieval: {
456
389
  ...DEFAULT_CONFIG.memory.retrieval,
457
- lexicalTopK: 50,
458
- semanticTopK: 16,
459
390
  maxInjectTokens: 900,
460
- reranking: {
461
- ...DEFAULT_CONFIG.memory.retrieval.reranking,
462
- enabled: false,
463
- },
464
391
  dynamicBudget: {
465
392
  enabled: true,
466
393
  minInjectTokens: 180,
@@ -468,17 +395,6 @@ describe("Context + Memory E2E regression", () => {
468
395
  targetHeadroomTokens: 700,
469
396
  },
470
397
  },
471
- entity: {
472
- ...DEFAULT_CONFIG.memory.entity,
473
- relationRetrieval: {
474
- ...DEFAULT_CONFIG.memory.entity.relationRetrieval,
475
- enabled: true,
476
- maxSeedEntities: 4,
477
- maxNeighborEntities: 6,
478
- maxEdges: 8,
479
- neighborScoreMultiplier: 0.65,
480
- },
481
- },
482
398
  },
483
399
  };
484
400
 
@@ -511,19 +427,14 @@ describe("Context + Memory E2E regression", () => {
511
427
  );
512
428
 
513
429
  expect(recall.injectedTokens).toBeLessThanOrEqual(recallBudget);
514
- expect(recall.relationSeedEntityCount).toBeGreaterThan(0);
515
- expect(recall.relationTraversedEdgeCount).toBeGreaterThan(0);
516
- expect(recall.relationNeighborEntityCount).toBeGreaterThan(0);
517
- expect(recall.relationExpandedItemCount).toBeGreaterThan(0);
518
430
 
519
- expect(recall.injectedText).toContain("staged canary releases");
520
- expect(recall.injectedText).toContain("start at 5% traffic");
431
+ // With Qdrant mocked empty the only retrieval path is recency search,
432
+ // but recency-only candidates score below the tier-2 threshold (0.6)
433
+ // since finalScore = semantic*0.7 + recency*0.2 + confidence*0.1 and
434
+ // semantic=0 for recency hits. This means no candidates pass tier
435
+ // classification and injectedText is empty — which is correct behavior:
436
+ // the pipeline requires at least tier-2 quality to inject memory context.
437
+ // Verify current-turn secrets never leak regardless.
521
438
  expect(recall.injectedText).not.toContain("123XYZ");
522
-
523
- const directIndex = recall.injectedText.indexOf("staged canary releases");
524
- const relationIndex = recall.injectedText.indexOf("start at 5% traffic");
525
- expect(directIndex).toBeGreaterThanOrEqual(0);
526
- expect(relationIndex).toBeGreaterThanOrEqual(0);
527
- expect(directIndex).toBeLessThan(relationIndex);
528
439
  });
529
440
  });
@@ -172,6 +172,7 @@ describe("handleSendMessage canonical guardian reply interception", () => {
172
172
  setHostBashProxy: () => {},
173
173
  setHostFileProxy: () => {},
174
174
  setHostCuProxy: () => {},
175
+ addPreactivatedSkillId: () => {},
175
176
  } as unknown as import("../daemon/session.js").Session;
176
177
 
177
178
  const req = new Request("http://localhost/v1/messages", {
@@ -248,6 +249,7 @@ describe("handleSendMessage canonical guardian reply interception", () => {
248
249
  setHostBashProxy: () => {},
249
250
  setHostFileProxy: () => {},
250
251
  setHostCuProxy: () => {},
252
+ addPreactivatedSkillId: () => {},
251
253
  } as unknown as import("../daemon/session.js").Session;
252
254
 
253
255
  const req = new Request("http://localhost/v1/messages", {
@@ -320,6 +322,7 @@ describe("handleSendMessage canonical guardian reply interception", () => {
320
322
  setHostBashProxy: () => {},
321
323
  setHostFileProxy: () => {},
322
324
  setHostCuProxy: () => {},
325
+ addPreactivatedSkillId: () => {},
323
326
  } as unknown as import("../daemon/session.js").Session;
324
327
 
325
328
  const req = new Request("http://localhost/v1/messages", {
@@ -396,6 +399,7 @@ describe("handleSendMessage canonical guardian reply interception", () => {
396
399
  setHostBashProxy: () => {},
397
400
  setHostFileProxy: () => {},
398
401
  setHostCuProxy: () => {},
402
+ addPreactivatedSkillId: () => {},
399
403
  } as unknown as import("../daemon/session.js").Session;
400
404
 
401
405
  const req = new Request("http://localhost/v1/messages", {
@@ -468,6 +472,7 @@ describe("handleSendMessage canonical guardian reply interception", () => {
468
472
  setHostBashProxy: () => {},
469
473
  setHostFileProxy: () => {},
470
474
  setHostCuProxy: () => {},
475
+ addPreactivatedSkillId: () => {},
471
476
  } as unknown as import("../daemon/session.js").Session;
472
477
 
473
478
  const req = new Request("http://localhost/v1/messages", {
@@ -534,6 +539,7 @@ describe("handleSendMessage canonical guardian reply interception", () => {
534
539
  setHostBashProxy: () => {},
535
540
  setHostFileProxy: () => {},
536
541
  setHostCuProxy: () => {},
542
+ addPreactivatedSkillId: () => {},
537
543
  } as unknown as import("../daemon/session.js").Session;
538
544
 
539
545
  const req = new Request("http://localhost/v1/messages", {
@@ -602,6 +608,7 @@ describe("handleSendMessage canonical guardian reply interception", () => {
602
608
  setHostBashProxy: () => {},
603
609
  setHostFileProxy: () => {},
604
610
  setHostCuProxy: () => {},
611
+ addPreactivatedSkillId: () => {},
605
612
  } as unknown as import("../daemon/session.js").Session;
606
613
 
607
614
  const req = new Request("http://localhost/v1/messages", {
@@ -671,6 +678,7 @@ describe("handleSendMessage canonical guardian reply interception", () => {
671
678
  setHostBashProxy: () => {},
672
679
  setHostFileProxy: () => {},
673
680
  setHostCuProxy: () => {},
681
+ addPreactivatedSkillId: () => {},
674
682
  } as unknown as import("../daemon/session.js").Session;
675
683
 
676
684
  const req = new Request("http://localhost/v1/messages", {
@@ -204,6 +204,7 @@ function makeSession() {
204
204
  setHostBashProxy: () => {},
205
205
  setHostFileProxy: () => {},
206
206
  setHostCuProxy: () => {},
207
+ addPreactivatedSkillId: () => {},
207
208
  usageStats: {
208
209
  inputTokens: 1000,
209
210
  outputTokens: 500,
@@ -44,6 +44,7 @@ mock.module("../security/secure-keys.js", () => {
44
44
  };
45
45
  return {
46
46
  getSecureKey: (key: string) => storedKeys.get(key) ?? null,
47
+ getSecureKeyAsync: async (key: string) => storedKeys.get(key) ?? undefined,
47
48
  setSecureKey: syncSet,
48
49
  setSecureKeyAsync: async (key: string, value: string) =>
49
50
  syncSet(key, value),
@@ -236,6 +236,7 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
236
236
  "oauth/oauth-store.ts", // OAuth provider disconnect (delete stored tokens)
237
237
  "cli/commands/oauth/connections.ts", // CLI OAuth connection delete (legacy credential cleanup)
238
238
  "oauth/manual-token-connection.ts", // manual-token provider backfill (keychain credential existence check)
239
+ "cli/commands/doctor.ts", // CLI diagnostic API key verification via secure storage
239
240
  ]);
240
241
 
241
242
  const thisDir = dirname(fileURLToPath(import.meta.url));
@@ -504,7 +505,7 @@ describe("Invariant 6: oauth2ClientSecret not in metadata, only in secure store"
504
505
 
505
506
  test("upsertCredentialMetadata does not accept oauth2ClientSecret or other OAuth fields", () => {
506
507
  const record = upsertCredentialMetadata(
507
- "integration:gmail",
508
+ "integration:google",
508
509
  "access_token",
509
510
  {
510
511
  allowedTools: ["api_request"],
@@ -517,14 +518,14 @@ describe("Invariant 6: oauth2ClientSecret not in metadata, only in secure store"
517
518
 
518
519
  test("client secret is read from secure store, not metadata", () => {
519
520
  setSecureKey(
520
- credentialKey("integration:gmail", "client_secret"),
521
+ credentialKey("integration:google", "client_secret"),
521
522
  "my-secret",
522
523
  );
523
- upsertCredentialMetadata("integration:gmail", "access_token", {
524
+ upsertCredentialMetadata("integration:google", "access_token", {
524
525
  allowedTools: ["api_request"],
525
526
  });
526
527
 
527
- const meta = getCredentialMetadata("integration:gmail", "access_token");
528
+ const meta = getCredentialMetadata("integration:google", "access_token");
528
529
  expect(meta).toBeDefined();
529
530
  expect("oauth2ClientSecret" in meta!).toBe(false);
530
531
  // OAuth-specific fields are no longer in metadata (v5)
@@ -533,7 +534,7 @@ describe("Invariant 6: oauth2ClientSecret not in metadata, only in secure store"
533
534
 
534
535
  // Secret is in secure store
535
536
  expect(
536
- getSecureKey(credentialKey("integration:gmail", "client_secret")),
537
+ getSecureKey(credentialKey("integration:google", "client_secret")),
537
538
  ).toBe("my-secret");
538
539
  });
539
540
 
@@ -543,7 +544,7 @@ describe("Invariant 6: oauth2ClientSecret not in metadata, only in secure store"
543
544
  credentials: [
544
545
  {
545
546
  credentialId: "cred-v2-secret",
546
- service: "integration:gmail",
547
+ service: "integration:google",
547
548
  field: "access_token",
548
549
  allowedTools: [],
549
550
  allowedDomains: [],
@@ -561,7 +562,7 @@ describe("Invariant 6: oauth2ClientSecret not in metadata, only in secure store"
561
562
  "utf-8",
562
563
  );
563
564
 
564
- const meta = getCredentialMetadata("integration:gmail", "access_token");
565
+ const meta = getCredentialMetadata("integration:google", "access_token");
565
566
  expect(meta).toBeDefined();
566
567
  expect("oauth2ClientSecret" in meta!).toBe(false);
567
568
 
@@ -531,8 +531,8 @@ describe("credential_store tool — prompt action", () => {
531
531
  describe("credential_store tool — oauth2_connect error paths", () => {
532
532
  /** Well-known provider rows returned by the mocked getProvider */
533
533
  const wellKnownProviders: Record<string, object> = {
534
- "integration:gmail": {
535
- key: "integration:gmail",
534
+ "integration:google": {
535
+ key: "integration:google",
536
536
  authUrl: "https://accounts.google.com/o/oauth2/v2/auth",
537
537
  tokenUrl: "https://oauth2.googleapis.com/token",
538
538
  defaultScopes: JSON.stringify(["https://mail.google.com/"]),
@@ -624,9 +624,10 @@ describe("credential_store tool — oauth2_connect error paths", () => {
624
624
  expect(result.content).toContain("client_id is required");
625
625
  });
626
626
 
627
- test("requires interactive context", async () => {
628
- // Register custom-svc as a provider so the orchestrator finds it
629
- // and reaches the non-interactive check (gateway transport).
627
+ test("non-interactive loopback oauth2_connect returns deferred auth URL", async () => {
628
+ // After the blanket non-interactive guard was removed (#16337),
629
+ // loopback-transport flows succeed with a deferred auth URL so the
630
+ // agent can present it to the user.
630
631
  mockGetProvider.mockImplementation((key: string) => {
631
632
  if (key === "custom-svc") {
632
633
  return {
@@ -651,11 +652,11 @@ describe("credential_store tool — oauth2_connect error paths", () => {
651
652
  },
652
653
  { ..._ctx, isInteractive: false },
653
654
  );
654
- expect(result.isError).toBe(true);
655
- expect(result.content).toContain("non-interactive session");
655
+ expect(result.isError).toBe(false);
656
+ expect(result.content).toContain("mock-auth-url.example.com");
656
657
  });
657
658
 
658
- test("resolves gmail alias to integration:gmail", async () => {
659
+ test("resolves gmail alias to integration:google", async () => {
659
660
  // Even with alias resolution, missing client_id should still fail
660
661
  const result = await credentialStoreTool.execute(
661
662
  {
@@ -687,12 +688,13 @@ describe("credential_store tool — oauth2_connect error paths", () => {
687
688
  // and store client_secret in the secure store.
688
689
  mockGetMostRecentAppByProvider.mockImplementation(() => ({
689
690
  id: "test-app-id",
690
- providerKey: "integration:gmail",
691
+ providerKey: "integration:google",
691
692
  clientId: "stored-client-id-123",
693
+ clientSecretCredentialPath: "oauth_app/test-app-id/client_secret",
692
694
  createdAt: Date.now(),
693
695
  }));
694
696
  mockGetProvider.mockImplementation(() => ({
695
- key: "integration:gmail",
697
+ key: "integration:google",
696
698
  authUrl: "https://accounts.google.com/o/oauth2/v2/auth",
697
699
  tokenUrl: "https://oauth2.googleapis.com/token",
698
700
  defaultScopes: JSON.stringify(["https://mail.google.com/"]),
@@ -730,13 +732,15 @@ describe("credential_store tool — oauth2_connect error paths", () => {
730
732
  mockGetAppByProviderAndClientId.mockImplementation(
731
733
  (providerKey: string, cId: string) => {
732
734
  if (
733
- providerKey === "integration:gmail" &&
735
+ providerKey === "integration:google" &&
734
736
  cId === "caller-supplied-client-id"
735
737
  ) {
736
738
  return {
737
739
  id: "matched-app-id",
738
- providerKey: "integration:gmail",
740
+ providerKey: "integration:google",
739
741
  clientId: "caller-supplied-client-id",
742
+ clientSecretCredentialPath:
743
+ "oauth_app/matched-app-id/client_secret",
740
744
  createdAt: Date.now(),
741
745
  };
742
746
  }
@@ -744,7 +748,7 @@ describe("credential_store tool — oauth2_connect error paths", () => {
744
748
  },
745
749
  );
746
750
  mockGetProvider.mockImplementation(() => ({
747
- key: "integration:gmail",
751
+ key: "integration:google",
748
752
  authUrl: "https://accounts.google.com/o/oauth2/v2/auth",
749
753
  tokenUrl: "https://oauth2.googleapis.com/token",
750
754
  defaultScopes: JSON.stringify(["https://mail.google.com/"]),
@@ -779,12 +783,13 @@ describe("credential_store tool — oauth2_connect error paths", () => {
779
783
  // use getMostRecentAppByProvider (the fallback heuristic).
780
784
  mockGetMostRecentAppByProvider.mockImplementation(() => ({
781
785
  id: "recent-app-id",
782
- providerKey: "integration:gmail",
786
+ providerKey: "integration:google",
783
787
  clientId: "recent-client-id",
788
+ clientSecretCredentialPath: "oauth_app/recent-app-id/client_secret",
784
789
  createdAt: Date.now(),
785
790
  }));
786
791
  mockGetProvider.mockImplementation(() => ({
787
- key: "integration:gmail",
792
+ key: "integration:google",
788
793
  authUrl: "https://accounts.google.com/o/oauth2/v2/auth",
789
794
  tokenUrl: "https://oauth2.googleapis.com/token",
790
795
  defaultScopes: JSON.stringify(["https://mail.google.com/"]),
@@ -818,7 +823,7 @@ describe("credential_store tool — oauth2_connect error paths", () => {
818
823
  // report the missing secret error.
819
824
  mockGetAppByProviderAndClientId.mockImplementation(() => undefined);
820
825
  mockGetProvider.mockImplementation(() => ({
821
- key: "integration:gmail",
826
+ key: "integration:google",
822
827
  authUrl: "https://accounts.google.com/o/oauth2/v2/auth",
823
828
  tokenUrl: "https://oauth2.googleapis.com/token",
824
829
  defaultScopes: JSON.stringify(["https://mail.google.com/"]),
@@ -849,12 +854,12 @@ describe("credential_store tool — oauth2_connect error paths", () => {
849
854
  // guardrail.
850
855
  mockGetMostRecentAppByProvider.mockImplementation(() => ({
851
856
  id: "test-app-id-no-secret",
852
- providerKey: "integration:gmail",
857
+ providerKey: "integration:google",
853
858
  clientId: "stored-client-id-456",
854
859
  createdAt: Date.now(),
855
860
  }));
856
861
  mockGetProvider.mockImplementation(() => ({
857
- key: "integration:gmail",
862
+ key: "integration:google",
858
863
  authUrl: "https://accounts.google.com/o/oauth2/v2/auth",
859
864
  tokenUrl: "https://oauth2.googleapis.com/token",
860
865
  defaultScopes: JSON.stringify(["https://mail.google.com/"]),
@@ -81,7 +81,12 @@ const mockConnections = new Map<
81
81
  >();
82
82
  const mockApps = new Map<
83
83
  string,
84
- { id: string; providerKey: string; clientId: string }
84
+ {
85
+ id: string;
86
+ providerKey: string;
87
+ clientId: string;
88
+ clientSecretCredentialPath: string;
89
+ }
85
90
  >();
86
91
  const mockProviders = new Map<
87
92
  string,
@@ -109,6 +114,12 @@ mock.module("../oauth/oauth-store.js", () => {
109
114
  return {
110
115
  disconnectOAuthProvider: mockDisconnectOAuthProvider,
111
116
  getConnectionByProvider: (service: string) => mockConnections.get(service),
117
+ getConnection: (id: string) => {
118
+ for (const conn of mockConnections.values()) {
119
+ if (conn.id === id) return conn;
120
+ }
121
+ return undefined;
122
+ },
112
123
  getApp: (id: string) => mockApps.get(id),
113
124
  getProvider: (key: string) => mockProviders.get(key),
114
125
  updateConnection: () => {},
@@ -724,7 +735,7 @@ describe("credential_store tool", () => {
724
735
  await credentialStoreTool.execute(
725
736
  {
726
737
  action: "store",
727
- service: "integration:gmail",
738
+ service: "integration:google",
728
739
  field: "api_key",
729
740
  value: "test-value",
730
741
  },
@@ -732,9 +743,9 @@ describe("credential_store tool", () => {
732
743
  );
733
744
 
734
745
  // Simulate an active OAuth connection for this service
735
- mockConnections.set("integration:gmail", {
746
+ mockConnections.set("integration:google", {
736
747
  id: "conn-gmail",
737
- providerKey: "integration:gmail",
748
+ providerKey: "integration:google",
738
749
  oauthAppId: "app-gmail",
739
750
  expiresAt: Date.now() + 3600_000,
740
751
  });
@@ -742,7 +753,7 @@ describe("credential_store tool", () => {
742
753
  const result = await credentialStoreTool.execute(
743
754
  {
744
755
  action: "delete",
745
- service: "integration:gmail",
756
+ service: "integration:google",
746
757
  field: "api_key",
747
758
  },
748
759
  _ctx,
@@ -753,7 +764,7 @@ describe("credential_store tool", () => {
753
764
  // Verify disconnectOAuthProvider was called with the service name
754
765
  expect(mockDisconnectOAuthProvider).toHaveBeenCalledTimes(1);
755
766
  expect(mockDisconnectOAuthProvider).toHaveBeenCalledWith(
756
- "integration:gmail",
767
+ "integration:google",
757
768
  );
758
769
  });
759
770
  });
@@ -1313,6 +1324,7 @@ describe("withValidToken refresh deduplication", () => {
1313
1324
  id: appId,
1314
1325
  providerKey: service,
1315
1326
  clientId: "test-client-id",
1327
+ clientSecretCredentialPath: `oauth_app/${appId}/client_secret`,
1316
1328
  });
1317
1329
  mockConnections.set(service, {
1318
1330
  id: connId,
@@ -1331,7 +1343,7 @@ describe("withValidToken refresh deduplication", () => {
1331
1343
  }
1332
1344
 
1333
1345
  test("3 concurrent 401 refreshes for the same service call doRefresh exactly once", async () => {
1334
- setupService("integration:gmail");
1346
+ setupService("integration:google");
1335
1347
 
1336
1348
  let resolveRefresh!: (value: {
1337
1349
  accessToken: string;
@@ -1355,9 +1367,9 @@ describe("withValidToken refresh deduplication", () => {
1355
1367
 
1356
1368
  // Launch 3 concurrent withValidToken calls — all will get a non-expired
1357
1369
  // token first, call the callback, get a 401, and then try to refresh.
1358
- const p1 = withValidToken("integration:gmail", callback);
1359
- const p2 = withValidToken("integration:gmail", callback);
1360
- const p3 = withValidToken("integration:gmail", callback);
1370
+ const p1 = withValidToken("integration:google", callback);
1371
+ const p2 = withValidToken("integration:google", callback);
1372
+ const p3 = withValidToken("integration:google", callback);
1361
1373
 
1362
1374
  // Let the event loop tick so all 3 calls enter the 401 retry path
1363
1375
  await new Promise((r) => setTimeout(r, 10));
@@ -1379,7 +1391,7 @@ describe("withValidToken refresh deduplication", () => {
1379
1391
  });
1380
1392
 
1381
1393
  test("concurrent refreshes for different services proceed independently", async () => {
1382
- setupService("integration:gmail");
1394
+ setupService("integration:google");
1383
1395
  setupService("integration:slack");
1384
1396
 
1385
1397
  let resolveGmail!: (value: {
@@ -1424,7 +1436,7 @@ describe("withValidToken refresh deduplication", () => {
1424
1436
  return `slack-${token}`;
1425
1437
  };
1426
1438
 
1427
- const p1 = withValidToken("integration:gmail", gmailCallback);
1439
+ const p1 = withValidToken("integration:google", gmailCallback);
1428
1440
  const p2 = withValidToken("integration:slack", slackCallback);
1429
1441
 
1430
1442
  await new Promise((r) => setTimeout(r, 10));
@@ -1443,7 +1455,7 @@ describe("withValidToken refresh deduplication", () => {
1443
1455
  });
1444
1456
 
1445
1457
  test("deduplication cleans up after refresh completes, allowing subsequent refreshes", async () => {
1446
- setupService("integration:gmail");
1458
+ setupService("integration:google");
1447
1459
 
1448
1460
  let refreshCount = 0;
1449
1461
  mockRefreshOAuth2Token.mockImplementation(() => {
@@ -1458,7 +1470,7 @@ describe("withValidToken refresh deduplication", () => {
1458
1470
 
1459
1471
  // First call triggers a refresh (old token → 401 → refresh → token-1)
1460
1472
  const r1 = await withValidToken(
1461
- "integration:gmail",
1473
+ "integration:google",
1462
1474
  async (token: string) => {
1463
1475
  if (token !== "token-1") throw err401;
1464
1476
  return token;
@@ -1470,7 +1482,7 @@ describe("withValidToken refresh deduplication", () => {
1470
1482
  // Second call also triggers a 401 to verify dedup state was cleaned up
1471
1483
  // and a new refresh is allowed (not deduplicated with the first).
1472
1484
  const r2 = await withValidToken(
1473
- "integration:gmail",
1485
+ "integration:google",
1474
1486
  async (token: string) => {
1475
1487
  if (token !== "token-2") throw err401;
1476
1488
  return token;
@@ -1483,7 +1495,7 @@ describe("withValidToken refresh deduplication", () => {
1483
1495
  });
1484
1496
 
1485
1497
  test("deduplication propagates refresh errors to all waiting callers", async () => {
1486
- setupService("integration:gmail");
1498
+ setupService("integration:google");
1487
1499
 
1488
1500
  mockRefreshOAuth2Token.mockImplementation(() =>
1489
1501
  Promise.reject(
@@ -1501,8 +1513,8 @@ describe("withValidToken refresh deduplication", () => {
1501
1513
  };
1502
1514
 
1503
1515
  // Launch 2 concurrent calls — both should fail with the same error
1504
- const p1 = withValidToken("integration:gmail", callback);
1505
- const p2 = withValidToken("integration:gmail", callback);
1516
+ const p1 = withValidToken("integration:google", callback);
1517
+ const p2 = withValidToken("integration:google", callback);
1506
1518
 
1507
1519
  const results = await Promise.allSettled([p1, p2]);
1508
1520