@vellumai/assistant 0.5.10 → 0.5.12

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 (395) hide show
  1. package/AGENTS.md +8 -0
  2. package/ARCHITECTURE.md +43 -43
  3. package/Dockerfile +3 -0
  4. package/docs/architecture/integrations.md +37 -42
  5. package/docs/architecture/memory.md +7 -12
  6. package/docs/credential-execution-service.md +9 -9
  7. package/docs/skills.md +1 -1
  8. package/node_modules/@vellumai/ces-contracts/src/__tests__/grants.test.ts +7 -7
  9. package/node_modules/@vellumai/ces-contracts/src/handles.ts +5 -4
  10. package/node_modules/@vellumai/credential-storage/src/index.ts +3 -3
  11. package/node_modules/@vellumai/credential-storage/src/static-credentials.ts +1 -1
  12. package/openapi.yaml +7208 -0
  13. package/package.json +2 -1
  14. package/scripts/generate-openapi.ts +562 -0
  15. package/src/__tests__/acp-session.test.ts +239 -44
  16. package/src/__tests__/assistant-feature-flag-guard.test.ts +8 -8
  17. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +5 -86
  18. package/src/__tests__/assistant-feature-flags-integration.test.ts +7 -14
  19. package/src/__tests__/browser-skill-endstate.test.ts +1 -1
  20. package/src/__tests__/btw-routes.test.ts +8 -0
  21. package/src/__tests__/bundled-skill-retrieval-guard.test.ts +10 -10
  22. package/src/__tests__/catalog-cache.test.ts +164 -0
  23. package/src/__tests__/catalog-search.test.ts +61 -0
  24. package/src/__tests__/channel-approvals.test.ts +7 -7
  25. package/src/__tests__/channel-readiness-service.test.ts +41 -0
  26. package/src/__tests__/cli-command-risk-guard.test.ts +181 -6
  27. package/src/__tests__/config-schema.test.ts +10 -2
  28. package/src/__tests__/context-memory-e2e.test.ts +2 -6
  29. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +396 -0
  30. package/src/__tests__/conversation-error.test.ts +3 -2
  31. package/src/__tests__/conversation-skill-tools.test.ts +1 -3
  32. package/src/__tests__/conversation-title-service.test.ts +2 -15
  33. package/src/__tests__/credential-execution-feature-gates.test.ts +4 -8
  34. package/src/__tests__/credential-execution-managed-contract.test.ts +8 -8
  35. package/src/__tests__/credential-security-e2e.test.ts +4 -4
  36. package/src/__tests__/credential-security-invariants.test.ts +12 -18
  37. package/src/__tests__/credential-vault-unit.test.ts +32 -34
  38. package/src/__tests__/credential-vault.test.ts +25 -33
  39. package/src/__tests__/credentials-cli.test.ts +3 -3
  40. package/src/__tests__/daemon-credential-client.test.ts +2 -2
  41. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -1
  42. package/src/__tests__/gateway-only-guard.test.ts +3 -0
  43. package/src/__tests__/heartbeat-service.test.ts +35 -0
  44. package/src/__tests__/host-bash-proxy.test.ts +79 -0
  45. package/src/__tests__/host-cu-proxy.test.ts +90 -0
  46. package/src/__tests__/host-file-proxy.test.ts +89 -0
  47. package/src/__tests__/host-shell-tool.test.ts +1 -1
  48. package/src/__tests__/inline-skill-load-permissions.test.ts +3 -3
  49. package/src/__tests__/integration-status.test.ts +5 -5
  50. package/src/__tests__/list-messages-attachments.test.ts +171 -0
  51. package/src/__tests__/llm-request-log-turn-query.test.ts +64 -0
  52. package/src/__tests__/log-export-workspace.test.ts +1 -1
  53. package/src/__tests__/mcp-abort-signal.test.ts +205 -0
  54. package/src/__tests__/mcp-client-auth.test.ts +1 -1
  55. package/src/__tests__/memory-lifecycle-e2e.test.ts +2 -2
  56. package/src/__tests__/memory-recall-log-store.test.ts +182 -0
  57. package/src/__tests__/memory-recall-quality.test.ts +6 -8
  58. package/src/__tests__/memory-regressions.test.ts +53 -42
  59. package/src/__tests__/memory-retrieval.benchmark.test.ts +5 -9
  60. package/src/__tests__/messaging-send-tool.test.ts +5 -5
  61. package/src/__tests__/messaging-skill-split.test.ts +2 -17
  62. package/src/__tests__/notification-telegram-adapter.test.ts +125 -0
  63. package/src/__tests__/oauth-cli.test.ts +203 -649
  64. package/src/__tests__/oauth-provider-profiles.test.ts +55 -20
  65. package/src/__tests__/oauth-scope-policy.test.ts +4 -6
  66. package/src/__tests__/onboarding-template-contract.test.ts +2 -2
  67. package/src/__tests__/platform-callback-registration.test.ts +119 -0
  68. package/src/__tests__/secret-ingress-channel.test.ts +261 -0
  69. package/src/__tests__/secret-ingress-cli.test.ts +201 -0
  70. package/src/__tests__/secret-ingress-http.test.ts +312 -0
  71. package/src/__tests__/secret-ingress.test.ts +283 -0
  72. package/src/__tests__/secret-onetime-send.test.ts +4 -4
  73. package/src/__tests__/secret-routes-managed-proxy.test.ts +78 -0
  74. package/src/__tests__/secure-keys-managed-failover.test.ts +73 -0
  75. package/src/__tests__/skill-feature-flags-integration.test.ts +4 -4
  76. package/src/__tests__/skill-feature-flags.test.ts +11 -19
  77. package/src/__tests__/skill-load-feature-flag.test.ts +1 -1
  78. package/src/__tests__/skill-load-inline-command.test.ts +3 -3
  79. package/src/__tests__/skill-load-inline-includes.test.ts +2 -2
  80. package/src/__tests__/skill-memory.test.ts +2 -4
  81. package/src/__tests__/skill-projection-feature-flag.test.ts +2 -4
  82. package/src/__tests__/skill-projection.benchmark.test.ts +1 -3
  83. package/src/__tests__/skills-uninstall.test.ts +2 -2
  84. package/src/__tests__/skills.test.ts +16 -2
  85. package/src/__tests__/slack-channel-config.test.ts +1 -1
  86. package/src/__tests__/slack-messaging-token-resolution.test.ts +22 -24
  87. package/src/__tests__/slack-share-routes.test.ts +5 -5
  88. package/src/__tests__/slack-skill.test.ts +5 -69
  89. package/src/__tests__/system-prompt.test.ts +39 -0
  90. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -1
  91. package/src/__tests__/workspace-migration-018-rekey-compound-credential-keys.test.ts +181 -0
  92. package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +5 -4
  93. package/src/acp/client-handler.ts +113 -31
  94. package/src/acp/session-manager.ts +29 -27
  95. package/src/approvals/guardian-request-resolvers.ts +1 -1
  96. package/src/cli/AGENTS.md +113 -0
  97. package/src/cli/commands/autonomy.ts +3 -5
  98. package/src/cli/commands/browser-relay.ts +2 -17
  99. package/src/cli/commands/contacts.ts +6 -4
  100. package/src/cli/commands/conversations.ts +13 -1
  101. package/src/cli/commands/credential-execution.ts +17 -3
  102. package/src/cli/commands/credentials.ts +2 -8
  103. package/src/cli/commands/memory.ts +2 -3
  104. package/src/cli/commands/oauth/__tests__/connect.test.ts +706 -0
  105. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +686 -0
  106. package/src/cli/commands/oauth/__tests__/mode.test.ts +625 -0
  107. package/src/cli/commands/oauth/__tests__/ping.test.ts +631 -0
  108. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +574 -0
  109. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +416 -0
  110. package/src/cli/commands/oauth/__tests__/status.test.ts +551 -0
  111. package/src/cli/commands/oauth/__tests__/token.test.ts +420 -0
  112. package/src/cli/commands/oauth/apps.ts +87 -50
  113. package/src/cli/commands/oauth/connect.ts +405 -0
  114. package/src/cli/commands/oauth/disconnect.ts +285 -0
  115. package/src/cli/commands/oauth/index.ts +62 -20
  116. package/src/cli/commands/oauth/mode.ts +251 -0
  117. package/src/cli/commands/oauth/ping.ts +196 -0
  118. package/src/cli/commands/oauth/providers.ts +589 -55
  119. package/src/cli/commands/oauth/request.ts +564 -0
  120. package/src/cli/commands/oauth/shared.ts +114 -0
  121. package/src/cli/commands/oauth/status.ts +191 -0
  122. package/src/cli/commands/oauth/token.ts +150 -0
  123. package/src/cli/commands/platform/connect.ts +104 -0
  124. package/src/cli/commands/platform/disconnect.ts +118 -0
  125. package/src/cli/commands/platform/index.ts +252 -0
  126. package/src/cli/commands/sequence.ts +5 -4
  127. package/src/cli/commands/shotgun.ts +16 -0
  128. package/src/cli/commands/skills.ts +173 -41
  129. package/src/cli/commands/usage.ts +5 -11
  130. package/src/cli/lib/daemon-credential-client.ts +22 -38
  131. package/src/cli/program.ts +1 -1
  132. package/src/cli.ts +82 -17
  133. package/src/config/assistant-feature-flags.ts +77 -18
  134. package/src/config/bundled-skills/_shared/CLI_RETRIEVAL_PATTERN.md +1 -1
  135. package/src/config/bundled-skills/app-builder/tools/app-create.ts +1 -1
  136. package/src/config/bundled-skills/contacts/tools/google-contacts.ts +1 -1
  137. package/src/config/bundled-skills/conversations/SKILL.md +20 -0
  138. package/src/config/bundled-skills/conversations/TOOLS.json +23 -0
  139. package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +66 -0
  140. package/src/config/bundled-skills/gmail/SKILL.md +13 -13
  141. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +3 -3
  142. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +2 -2
  143. package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +1 -1
  144. package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +1 -1
  145. package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +1 -1
  146. package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +1 -1
  147. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +2 -2
  148. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +1 -1
  149. package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +1 -1
  150. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +1 -1
  151. package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +1 -1
  152. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +1 -1
  153. package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +1 -1
  154. package/src/config/bundled-skills/google-calendar/SKILL.md +10 -4
  155. package/src/config/bundled-skills/google-calendar/tools/shared.ts +1 -1
  156. package/src/config/bundled-skills/messaging/SKILL.md +19 -42
  157. package/src/config/bundled-skills/messaging/TOOLS.json +9 -9
  158. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +1 -1
  159. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +5 -2
  160. package/src/config/bundled-skills/messaging/tools/shared.ts +5 -6
  161. package/src/config/bundled-skills/notifications/SKILL.md +1 -1
  162. package/src/config/bundled-skills/schedule/SKILL.md +2 -2
  163. package/src/config/bundled-skills/settings/SKILL.md +5 -3
  164. package/src/config/bundled-skills/settings/TOOLS.json +17 -0
  165. package/src/config/bundled-skills/settings/tools/avatar-get.ts +50 -0
  166. package/src/config/bundled-skills/settings/tools/avatar-remove.ts +7 -0
  167. package/src/config/bundled-skills/settings/tools/avatar-update.ts +6 -1
  168. package/src/config/bundled-skills/settings/tools/identity-avatar.ts +55 -0
  169. package/src/config/bundled-skills/skills-catalog/SKILL.md +3 -3
  170. package/src/config/bundled-skills/slack/SKILL.md +58 -44
  171. package/src/config/bundled-tool-registry.ts +7 -19
  172. package/src/config/env.ts +5 -1
  173. package/src/config/feature-flag-registry.json +58 -42
  174. package/src/config/loader.ts +4 -0
  175. package/src/config/schemas/platform.ts +0 -8
  176. package/src/config/schemas/security.ts +9 -1
  177. package/src/config/schemas/services.ts +1 -1
  178. package/src/config/skill-state.ts +1 -3
  179. package/src/config/skills.ts +2 -4
  180. package/src/credential-execution/client.ts +1 -1
  181. package/src/credential-execution/feature-gates.ts +9 -16
  182. package/src/credential-execution/process-manager.ts +12 -0
  183. package/src/daemon/config-watcher.ts +4 -0
  184. package/src/daemon/conversation-agent-loop-handlers.ts +10 -0
  185. package/src/daemon/conversation-agent-loop.ts +51 -2
  186. package/src/daemon/conversation-error.ts +36 -6
  187. package/src/daemon/conversation-memory.ts +0 -1
  188. package/src/daemon/conversation-messaging.ts +9 -0
  189. package/src/daemon/conversation-runtime-assembly.ts +33 -0
  190. package/src/daemon/conversation-surfaces.ts +120 -14
  191. package/src/daemon/conversation.ts +5 -0
  192. package/src/daemon/handlers/config-slack-channel.ts +43 -1
  193. package/src/daemon/handlers/conversations.ts +41 -33
  194. package/src/daemon/handlers/skills.ts +148 -3
  195. package/src/daemon/host-bash-proxy.ts +16 -0
  196. package/src/daemon/host-cu-proxy.ts +16 -0
  197. package/src/daemon/host-file-proxy.ts +16 -0
  198. package/src/daemon/lifecycle.ts +73 -3
  199. package/src/daemon/message-types/acp.ts +0 -15
  200. package/src/daemon/message-types/conversations.ts +1 -0
  201. package/src/daemon/message-types/guardian-actions.ts +2 -0
  202. package/src/daemon/message-types/host-bash.ts +6 -1
  203. package/src/daemon/message-types/host-cu.ts +6 -1
  204. package/src/daemon/message-types/host-file.ts +6 -1
  205. package/src/daemon/message-types/integrations.ts +0 -1
  206. package/src/daemon/message-types/memory.ts +0 -1
  207. package/src/daemon/message-types/messages.ts +9 -1
  208. package/src/daemon/message-types/schedules.ts +9 -0
  209. package/src/daemon/server.ts +48 -9
  210. package/src/email/feature-gate.ts +3 -3
  211. package/src/heartbeat/heartbeat-service.ts +48 -0
  212. package/src/hooks/cli.ts +74 -0
  213. package/src/inbound/platform-callback-registration.ts +68 -19
  214. package/src/mcp/client.ts +6 -1
  215. package/src/mcp/manager.ts +2 -1
  216. package/src/mcp/mcp-oauth-provider.ts +3 -3
  217. package/src/memory/app-store.ts +3 -3
  218. package/src/memory/conversation-crud.ts +213 -0
  219. package/src/memory/conversation-key-store.ts +26 -0
  220. package/src/memory/conversation-title-service.ts +7 -17
  221. package/src/memory/db-init.ts +24 -0
  222. package/src/memory/embedding-local.ts +47 -2
  223. package/src/memory/indexer.ts +13 -10
  224. package/src/memory/items-extractor.ts +12 -4
  225. package/src/memory/job-utils.ts +5 -0
  226. package/src/memory/jobs-store.ts +10 -2
  227. package/src/memory/journal-memory.ts +6 -2
  228. package/src/memory/llm-request-log-store.ts +88 -21
  229. package/src/memory/memory-recall-log-store.ts +128 -0
  230. package/src/memory/migrations/194-memory-recall-logs.ts +50 -0
  231. package/src/memory/migrations/195-oauth-providers-ping-config.ts +23 -0
  232. package/src/memory/migrations/196-messages-conversation-created-at-index.ts +9 -0
  233. package/src/memory/migrations/196-strip-integration-prefix-from-provider-keys.ts +186 -0
  234. package/src/memory/migrations/197-oauth-providers-behavior-columns.ts +29 -0
  235. package/src/memory/migrations/198-drop-setup-skill-id-column.ts +11 -0
  236. package/src/memory/migrations/index.ts +6 -0
  237. package/src/memory/migrations/registry.ts +8 -0
  238. package/src/memory/retriever.test.ts +4 -5
  239. package/src/memory/schema/infrastructure.ts +31 -0
  240. package/src/memory/schema/oauth.ts +14 -0
  241. package/src/messaging/provider.ts +13 -12
  242. package/src/messaging/providers/gmail/adapter.ts +44 -35
  243. package/src/messaging/providers/slack/adapter.ts +63 -33
  244. package/src/messaging/providers/telegram-bot/adapter.ts +7 -9
  245. package/src/messaging/providers/whatsapp/adapter.ts +6 -8
  246. package/src/notifications/adapters/telegram.ts +78 -2
  247. package/src/oauth/__tests__/identity-verifier.test.ts +464 -0
  248. package/src/oauth/byo-connection.test.ts +22 -24
  249. package/src/oauth/connect-orchestrator.ts +79 -64
  250. package/src/oauth/connect-types.ts +7 -65
  251. package/src/oauth/connection-resolver.test.ts +13 -13
  252. package/src/oauth/connection-resolver.ts +3 -4
  253. package/src/oauth/identity-verifier.ts +177 -0
  254. package/src/oauth/manual-token-connection.ts +5 -5
  255. package/src/oauth/oauth-store.ts +251 -5
  256. package/src/oauth/platform-connection.test.ts +56 -6
  257. package/src/oauth/platform-connection.ts +8 -1
  258. package/src/oauth/seed-providers.ts +256 -34
  259. package/src/permissions/checker.ts +129 -3
  260. package/src/permissions/trust-client.ts +2 -2
  261. package/src/platform/client.ts +2 -2
  262. package/src/prompts/journal-context.ts +6 -1
  263. package/src/prompts/system-prompt.ts +43 -9
  264. package/src/prompts/templates/BOOTSTRAP.md +16 -5
  265. package/src/providers/anthropic/client.ts +139 -28
  266. package/src/runtime/auth/__tests__/middleware.test.ts +19 -0
  267. package/src/runtime/auth/route-policy.ts +0 -1
  268. package/src/runtime/btw-sidechain.ts +7 -1
  269. package/src/runtime/channel-approvals.ts +2 -2
  270. package/src/runtime/channel-readiness-service.ts +30 -7
  271. package/src/runtime/guardian-action-service.ts +7 -2
  272. package/src/runtime/http-router.ts +31 -0
  273. package/src/runtime/http-server.ts +26 -7
  274. package/src/runtime/http-types.ts +9 -0
  275. package/src/runtime/pending-interactions.ts +21 -3
  276. package/src/runtime/routes/acp-routes.ts +46 -28
  277. package/src/runtime/routes/app-management-routes.ts +123 -0
  278. package/src/runtime/routes/app-routes.ts +31 -0
  279. package/src/runtime/routes/approval-routes.ts +108 -3
  280. package/src/runtime/routes/attachment-routes.ts +45 -0
  281. package/src/runtime/routes/avatar-routes.ts +16 -0
  282. package/src/runtime/routes/brain-graph-routes.ts +18 -0
  283. package/src/runtime/routes/btw-routes.ts +20 -0
  284. package/src/runtime/routes/call-routes.ts +81 -0
  285. package/src/runtime/routes/channel-readiness-routes.ts +48 -7
  286. package/src/runtime/routes/channel-routes.ts +18 -0
  287. package/src/runtime/routes/channel-verification-routes.ts +49 -1
  288. package/src/runtime/routes/contact-routes.ts +77 -0
  289. package/src/runtime/routes/conversation-attention-routes.ts +37 -0
  290. package/src/runtime/routes/conversation-management-routes.ts +125 -0
  291. package/src/runtime/routes/conversation-query-routes.ts +78 -0
  292. package/src/runtime/routes/conversation-routes.ts +191 -39
  293. package/src/runtime/routes/conversation-starter-routes.ts +29 -0
  294. package/src/runtime/routes/debug-routes.ts +23 -0
  295. package/src/runtime/routes/diagnostics-routes.ts +30 -0
  296. package/src/runtime/routes/documents-routes.ts +42 -0
  297. package/src/runtime/routes/events-routes.ts +10 -0
  298. package/src/runtime/routes/global-search-routes.ts +35 -0
  299. package/src/runtime/routes/guardian-action-routes.ts +61 -3
  300. package/src/runtime/routes/guardian-approval-prompt.ts +77 -2
  301. package/src/runtime/routes/heartbeat-routes.ts +278 -0
  302. package/src/runtime/routes/host-bash-routes.ts +16 -1
  303. package/src/runtime/routes/host-cu-routes.ts +23 -1
  304. package/src/runtime/routes/host-file-routes.ts +18 -1
  305. package/src/runtime/routes/identity-routes.ts +35 -0
  306. package/src/runtime/routes/inbound-message-handler.ts +46 -25
  307. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -8
  308. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +30 -2
  309. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +1 -2
  310. package/src/runtime/routes/integrations/slack/share.ts +1 -1
  311. package/src/runtime/routes/integrations/twilio.ts +32 -22
  312. package/src/runtime/routes/invite-routes.ts +83 -0
  313. package/src/runtime/routes/log-export-routes.ts +14 -0
  314. package/src/runtime/routes/memory-item-routes.ts +99 -1
  315. package/src/runtime/routes/migration-rollback-routes.ts +25 -0
  316. package/src/runtime/routes/migration-routes.ts +40 -0
  317. package/src/runtime/routes/notification-routes.ts +20 -0
  318. package/src/runtime/routes/oauth-apps.ts +13 -4
  319. package/src/runtime/routes/pairing-routes.ts +15 -0
  320. package/src/runtime/routes/recording-routes.ts +72 -0
  321. package/src/runtime/routes/schedule-routes.ts +77 -5
  322. package/src/runtime/routes/secret-routes.ts +99 -14
  323. package/src/runtime/routes/settings-routes.ts +102 -19
  324. package/src/runtime/routes/skills-routes.ts +141 -18
  325. package/src/runtime/routes/subagents-routes.ts +38 -3
  326. package/src/runtime/routes/surface-action-routes.ts +66 -24
  327. package/src/runtime/routes/surface-content-routes.ts +20 -0
  328. package/src/runtime/routes/telemetry-routes.ts +12 -0
  329. package/src/runtime/routes/trace-event-routes.ts +25 -0
  330. package/src/runtime/routes/trust-rules-routes.ts +46 -0
  331. package/src/runtime/routes/tts-routes.ts +15 -4
  332. package/src/runtime/routes/upgrade-broadcast-routes.ts +38 -0
  333. package/src/runtime/routes/usage-routes.ts +59 -0
  334. package/src/runtime/routes/watch-routes.ts +28 -0
  335. package/src/runtime/routes/work-items-routes.ts +59 -0
  336. package/src/runtime/routes/workspace-commit-routes.ts +12 -0
  337. package/src/runtime/routes/workspace-routes.ts +102 -0
  338. package/src/schedule/integration-status.ts +2 -2
  339. package/src/schedule/scheduler.ts +7 -1
  340. package/src/security/AGENTS.md +7 -0
  341. package/src/security/ces-rpc-credential-backend.ts +19 -16
  342. package/src/security/credential-backend.ts +1 -1
  343. package/src/security/encrypted-store.ts +3 -3
  344. package/src/security/oauth-completion-page.ts +153 -0
  345. package/src/security/oauth2.ts +58 -17
  346. package/src/security/secret-ingress.ts +174 -0
  347. package/src/security/secret-patterns.ts +133 -0
  348. package/src/security/secret-scanner.ts +28 -117
  349. package/src/security/secure-keys.ts +207 -7
  350. package/src/security/token-manager.ts +3 -6
  351. package/src/signals/bash.ts +6 -1
  352. package/src/signals/confirm.ts +12 -8
  353. package/src/signals/user-message.ts +18 -3
  354. package/src/skills/catalog-cache.ts +44 -0
  355. package/src/skills/catalog-search.ts +18 -0
  356. package/src/skills/skill-memory.ts +1 -2
  357. package/src/tasks/task-runner.ts +7 -1
  358. package/src/tools/credentials/broker.ts +1 -1
  359. package/src/tools/credentials/metadata-store.ts +1 -1
  360. package/src/tools/credentials/post-connect-hooks.ts +1 -1
  361. package/src/tools/credentials/vault.ts +36 -48
  362. package/src/tools/host-terminal/host-shell.ts +16 -3
  363. package/src/tools/mcp/mcp-tool-factory.ts +2 -1
  364. package/src/tools/memory/definitions.ts +1 -1
  365. package/src/tools/memory/handlers.test.ts +2 -4
  366. package/src/tools/skills/load.ts +1 -1
  367. package/src/tools/skills/sandbox-runner.ts +16 -3
  368. package/src/tools/terminal/safe-env.ts +7 -0
  369. package/src/tools/terminal/shell.ts +16 -3
  370. package/src/tools/tool-manifest.ts +1 -1
  371. package/src/util/log-redact.ts +9 -34
  372. package/src/util/logger.ts +11 -1
  373. package/src/util/sentry-log-stream.ts +51 -0
  374. package/src/watcher/providers/github.ts +2 -2
  375. package/src/watcher/providers/gmail.ts +1 -1
  376. package/src/watcher/providers/google-calendar.ts +1 -1
  377. package/src/watcher/providers/linear.ts +2 -2
  378. package/src/workspace/migrations/011-backfill-installation-id.ts +5 -3
  379. package/src/workspace/migrations/020-rename-oauth-skill-dirs.ts +119 -0
  380. package/src/workspace/migrations/registry.ts +2 -0
  381. package/docs/architecture/keychain-broker.md +0 -68
  382. package/src/cli/commands/oauth/connections.ts +0 -734
  383. package/src/cli/commands/oauth/platform.ts +0 -525
  384. package/src/cli/commands/platform.ts +0 -176
  385. package/src/config/bundled-skills/slack/TOOLS.json +0 -272
  386. package/src/config/bundled-skills/slack/tools/shared.ts +0 -34
  387. package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +0 -27
  388. package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +0 -38
  389. package/src/config/bundled-skills/slack/tools/slack-channel-permissions.ts +0 -146
  390. package/src/config/bundled-skills/slack/tools/slack-configure-channels.ts +0 -105
  391. package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +0 -26
  392. package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +0 -27
  393. package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +0 -25
  394. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +0 -372
  395. package/src/oauth/provider-behaviors.ts +0 -634
@@ -1,13 +1,24 @@
1
- import { and, eq, gte, inArray, isNull, lte } from "drizzle-orm";
1
+ import { and, eq, gte, inArray, isNull, lte, sql } from "drizzle-orm";
2
2
  import { v4 as uuid } from "uuid";
3
3
 
4
4
  import {
5
5
  getAssistantMessageIdsInTurn,
6
6
  getMessageById,
7
+ getTurnTimeBounds,
7
8
  messageMetadataSchema,
8
9
  } from "./conversation-crud.js";
9
10
  import { getDb } from "./db.js";
10
- import { llmRequestLogs } from "./schema.js";
11
+ import { llmRequestLogs, messages } from "./schema.js";
12
+
13
+ type LogRow = {
14
+ id: string;
15
+ conversationId: string;
16
+ messageId: string | null;
17
+ provider: string | null;
18
+ requestPayload: string;
19
+ responsePayload: string;
20
+ createdAt: number;
21
+ };
11
22
 
12
23
  export function recordRequestLog(
13
24
  conversationId: string,
@@ -77,15 +88,7 @@ export function backfillMessageIdOnLogs(
77
88
  * given message IDs, ordered by `createdAt ASC`. Uses the existing
78
89
  * `idx_llm_request_logs_message_id` index via `inArray`.
79
90
  */
80
- function selectLogsByMessageIds(messageIds: string[]): Array<{
81
- id: string;
82
- conversationId: string;
83
- messageId: string | null;
84
- provider: string | null;
85
- requestPayload: string;
86
- responsePayload: string;
87
- createdAt: number;
88
- }> {
91
+ function selectLogsByMessageIds(messageIds: string[]): LogRow[] {
89
92
  if (messageIds.length === 0) return [];
90
93
  const db = getDb();
91
94
  return db
@@ -104,26 +107,90 @@ function selectLogsByMessageIds(messageIds: string[]): Array<{
104
107
  .all();
105
108
  }
106
109
 
107
- export function getRequestLogsByMessageId(messageId: string): Array<{
108
- id: string;
109
- conversationId: string;
110
- messageId: string | null;
111
- provider: string | null;
112
- requestPayload: string;
113
- responsePayload: string;
114
- createdAt: number;
115
- }> {
110
+ /**
111
+ * Find orphaned logs — logs whose `message_id` references a message that no
112
+ * longer exists in the DB. These are left behind when intermediate assistant
113
+ * messages are deleted (e.g. by retry/deleteLastExchange).
114
+ *
115
+ * Scoped to a single conversation and a time range to avoid cross-turn bleed.
116
+ */
117
+ function selectOrphanedLogsInRange(
118
+ conversationId: string,
119
+ startTime: number,
120
+ endTime: number,
121
+ ): LogRow[] {
122
+ if (endTime <= startTime) return [];
123
+ const db = getDb();
124
+ // LEFT JOIN messages → filter where message row IS NULL (orphaned).
125
+ return db
126
+ .select({
127
+ id: llmRequestLogs.id,
128
+ conversationId: llmRequestLogs.conversationId,
129
+ messageId: llmRequestLogs.messageId,
130
+ provider: llmRequestLogs.provider,
131
+ requestPayload: llmRequestLogs.requestPayload,
132
+ responsePayload: llmRequestLogs.responsePayload,
133
+ createdAt: llmRequestLogs.createdAt,
134
+ })
135
+ .from(llmRequestLogs)
136
+ .leftJoin(messages, eq(llmRequestLogs.messageId, messages.id))
137
+ .where(
138
+ and(
139
+ eq(llmRequestLogs.conversationId, conversationId),
140
+ gte(llmRequestLogs.createdAt, startTime),
141
+ lte(llmRequestLogs.createdAt, endTime),
142
+ sql`${messages.id} IS NULL`,
143
+ sql`${llmRequestLogs.messageId} IS NOT NULL`,
144
+ ),
145
+ )
146
+ .orderBy(llmRequestLogs.createdAt)
147
+ .all();
148
+ }
149
+
150
+ export function getRequestLogsByMessageId(messageId: string): LogRow[] {
116
151
  // Resolve all assistant message IDs in the same turn so the inspector
117
152
  // shows every LLM call from the entire agent turn, not just the queried message.
118
153
  const turnMessageIds = getAssistantMessageIdsInTurn(messageId);
119
154
  const turnLogs = selectLogsByMessageIds(turnMessageIds);
155
+
156
+ // Orphaned-log recovery: intermediate assistant messages within a turn can
157
+ // be deleted (e.g. by retry / deleteLastExchange) while their
158
+ // llm_request_logs rows survive. When that happens the message-ID-based
159
+ // query misses those logs. We detect orphaned logs (logs whose message_id
160
+ // references a deleted message) within the turn's time window and merge
161
+ // them with the message-ID-based results.
162
+ const message = getMessageById(messageId);
163
+ if (message) {
164
+ const bounds = getTurnTimeBounds(message.conversationId, message.createdAt);
165
+ if (bounds) {
166
+ const orphanedLogs = selectOrphanedLogsInRange(
167
+ message.conversationId,
168
+ bounds.startTime,
169
+ bounds.endTime,
170
+ );
171
+ if (orphanedLogs.length > 0) {
172
+ const seen = new Set(turnLogs.map((l) => l.id));
173
+ const merged = [...turnLogs];
174
+ for (const log of orphanedLogs) {
175
+ if (!seen.has(log.id)) {
176
+ merged.push(log);
177
+ seen.add(log.id);
178
+ }
179
+ }
180
+ merged.sort(
181
+ (a, b) => a.createdAt - b.createdAt || a.id.localeCompare(b.id),
182
+ );
183
+ return merged;
184
+ }
185
+ }
186
+ }
187
+
120
188
  if (turnLogs.length > 0) {
121
189
  return turnLogs;
122
190
  }
123
191
 
124
192
  // Fork-source fallback: if no logs found for the turn, check whether
125
193
  // the queried message was forked from a source and resolve that source's turn.
126
- const message = getMessageById(messageId);
127
194
  if (!message?.metadata) {
128
195
  return [];
129
196
  }
@@ -0,0 +1,128 @@
1
+ import { and, eq, inArray, isNull } from "drizzle-orm";
2
+ import { v4 as uuid } from "uuid";
3
+
4
+ import { getDb } from "./db.js";
5
+ import { memoryRecallLogs } from "./schema.js";
6
+
7
+ export interface RecordMemoryRecallLogParams {
8
+ conversationId: string;
9
+ enabled: boolean;
10
+ degraded: boolean;
11
+ provider?: string;
12
+ model?: string;
13
+ degradationJson?: unknown;
14
+ semanticHits: number;
15
+ mergedCount: number;
16
+ selectedCount: number;
17
+ tier1Count: number;
18
+ tier2Count: number;
19
+ hybridSearchLatencyMs: number;
20
+ sparseVectorUsed: boolean;
21
+ injectedTokens: number;
22
+ latencyMs: number;
23
+ topCandidatesJson: unknown;
24
+ injectedText?: string;
25
+ reason?: string;
26
+ }
27
+
28
+ export function recordMemoryRecallLog(params: RecordMemoryRecallLogParams): void {
29
+ const db = getDb();
30
+ db.insert(memoryRecallLogs)
31
+ .values({
32
+ id: uuid(),
33
+ conversationId: params.conversationId,
34
+ messageId: null,
35
+ enabled: params.enabled ? 1 : 0,
36
+ degraded: params.degraded ? 1 : 0,
37
+ provider: params.provider ?? null,
38
+ model: params.model ?? null,
39
+ degradationJson: params.degradationJson
40
+ ? JSON.stringify(params.degradationJson)
41
+ : null,
42
+ semanticHits: params.semanticHits,
43
+ mergedCount: params.mergedCount,
44
+ selectedCount: params.selectedCount,
45
+ tier1Count: params.tier1Count,
46
+ tier2Count: params.tier2Count,
47
+ hybridSearchLatencyMs: params.hybridSearchLatencyMs,
48
+ sparseVectorUsed: params.sparseVectorUsed ? 1 : 0,
49
+ injectedTokens: params.injectedTokens,
50
+ latencyMs: params.latencyMs,
51
+ topCandidatesJson: JSON.stringify(params.topCandidatesJson),
52
+ injectedText: params.injectedText ?? null,
53
+ reason: params.reason ?? null,
54
+ createdAt: Date.now(),
55
+ })
56
+ .run();
57
+ }
58
+
59
+ export function backfillMemoryRecallLogMessageId(
60
+ conversationId: string,
61
+ messageId: string,
62
+ ): void {
63
+ const db = getDb();
64
+ db.update(memoryRecallLogs)
65
+ .set({ messageId })
66
+ .where(
67
+ and(
68
+ eq(memoryRecallLogs.conversationId, conversationId),
69
+ isNull(memoryRecallLogs.messageId),
70
+ ),
71
+ )
72
+ .run();
73
+ }
74
+
75
+ export interface MemoryRecallLog {
76
+ enabled: boolean;
77
+ degraded: boolean;
78
+ provider: string | null;
79
+ model: string | null;
80
+ degradation: unknown | null;
81
+ semanticHits: number;
82
+ mergedCount: number;
83
+ selectedCount: number;
84
+ tier1Count: number;
85
+ tier2Count: number;
86
+ hybridSearchLatencyMs: number;
87
+ sparseVectorUsed: boolean;
88
+ injectedTokens: number;
89
+ latencyMs: number;
90
+ topCandidates: unknown;
91
+ injectedText: string | null;
92
+ reason: string | null;
93
+ }
94
+
95
+ export function getMemoryRecallLogByMessageIds(
96
+ messageIds: string[],
97
+ ): MemoryRecallLog | null {
98
+ if (messageIds.length === 0) return null;
99
+ const db = getDb();
100
+ const rows = db
101
+ .select()
102
+ .from(memoryRecallLogs)
103
+ .where(inArray(memoryRecallLogs.messageId, messageIds))
104
+ .all();
105
+ if (rows.length === 0) return null;
106
+ const row = rows[0]!;
107
+ return {
108
+ enabled: !!row.enabled,
109
+ degraded: !!row.degraded,
110
+ provider: row.provider,
111
+ model: row.model,
112
+ degradation: row.degradationJson
113
+ ? JSON.parse(row.degradationJson)
114
+ : null,
115
+ semanticHits: row.semanticHits,
116
+ mergedCount: row.mergedCount,
117
+ selectedCount: row.selectedCount,
118
+ tier1Count: row.tier1Count,
119
+ tier2Count: row.tier2Count,
120
+ hybridSearchLatencyMs: row.hybridSearchLatencyMs,
121
+ sparseVectorUsed: !!row.sparseVectorUsed,
122
+ injectedTokens: row.injectedTokens,
123
+ latencyMs: row.latencyMs,
124
+ topCandidates: JSON.parse(row.topCandidatesJson),
125
+ injectedText: row.injectedText,
126
+ reason: row.reason,
127
+ };
128
+ }
@@ -0,0 +1,50 @@
1
+ import type { DrizzleDb } from "../db-connection.js";
2
+ import { getSqliteFrom } from "../db-connection.js";
3
+ import { withCrashRecovery } from "./validate-migration-state.js";
4
+
5
+ const CHECKPOINT_KEY = "migration_create_memory_recall_logs_v1";
6
+
7
+ /**
8
+ * Create the memory_recall_logs table for the inspector memory tab.
9
+ */
10
+ export function migrateCreateMemoryRecallLogs(database: DrizzleDb): void {
11
+ withCrashRecovery(database, CHECKPOINT_KEY, () => {
12
+ const raw = getSqliteFrom(database);
13
+
14
+ raw.exec(/*sql*/ `
15
+ CREATE TABLE IF NOT EXISTS memory_recall_logs (
16
+ id TEXT PRIMARY KEY,
17
+ conversation_id TEXT NOT NULL,
18
+ message_id TEXT,
19
+ enabled INTEGER NOT NULL,
20
+ degraded INTEGER NOT NULL,
21
+ provider TEXT,
22
+ model TEXT,
23
+ degradation_json TEXT,
24
+ semantic_hits INTEGER NOT NULL,
25
+ merged_count INTEGER NOT NULL,
26
+ selected_count INTEGER NOT NULL,
27
+ tier1_count INTEGER NOT NULL,
28
+ tier2_count INTEGER NOT NULL,
29
+ hybrid_search_latency_ms INTEGER NOT NULL,
30
+ sparse_vector_used INTEGER NOT NULL,
31
+ injected_tokens INTEGER NOT NULL,
32
+ latency_ms INTEGER NOT NULL,
33
+ top_candidates_json TEXT NOT NULL,
34
+ injected_text TEXT,
35
+ reason TEXT,
36
+ created_at INTEGER NOT NULL
37
+ )
38
+ `);
39
+
40
+ raw.exec(/*sql*/ `
41
+ CREATE INDEX IF NOT EXISTS idx_memory_recall_logs_message_id
42
+ ON memory_recall_logs (message_id)
43
+ `);
44
+
45
+ raw.exec(/*sql*/ `
46
+ CREATE INDEX IF NOT EXISTS idx_memory_recall_logs_conversation_id
47
+ ON memory_recall_logs (conversation_id)
48
+ `);
49
+ });
50
+ }
@@ -0,0 +1,23 @@
1
+ import type { DrizzleDb } from "../db-connection.js";
2
+ import { getSqliteFrom } from "../db-connection.js";
3
+
4
+ export function migrateOAuthProvidersPingConfig(database: DrizzleDb): void {
5
+ const raw = getSqliteFrom(database);
6
+ try {
7
+ raw.exec(/*sql*/ `ALTER TABLE oauth_providers ADD COLUMN ping_method TEXT`);
8
+ } catch {
9
+ // Column already exists — nothing to do.
10
+ }
11
+ try {
12
+ raw.exec(
13
+ /*sql*/ `ALTER TABLE oauth_providers ADD COLUMN ping_headers TEXT`,
14
+ );
15
+ } catch {
16
+ // Column already exists — nothing to do.
17
+ }
18
+ try {
19
+ raw.exec(/*sql*/ `ALTER TABLE oauth_providers ADD COLUMN ping_body TEXT`);
20
+ } catch {
21
+ // Column already exists — nothing to do.
22
+ }
23
+ }
@@ -0,0 +1,9 @@
1
+ import type { DrizzleDb } from "../db-connection.js";
2
+
3
+ export function migrateMessagesConversationCreatedAtIndex(
4
+ database: DrizzleDb,
5
+ ): void {
6
+ database.run(
7
+ /*sql*/ `CREATE INDEX IF NOT EXISTS idx_messages_conversation_created_at ON messages(conversation_id, created_at)`,
8
+ );
9
+ }
@@ -0,0 +1,186 @@
1
+ import type { DrizzleDb } from "../db-connection.js";
2
+ import { getSqliteFrom } from "../db-connection.js";
3
+ import { withCrashRecovery } from "./validate-migration-state.js";
4
+
5
+ /**
6
+ * One-shot migration: strip the `integration:` prefix from provider_key
7
+ * values across all three OAuth tables (oauth_providers, oauth_apps,
8
+ * oauth_connections).
9
+ *
10
+ * Historically provider keys were stored as `integration:google`,
11
+ * `integration:slack`, etc. The codebase is moving to bare-name keys
12
+ * (`google`, `slack`) for simplicity. Providers that were already stored
13
+ * with bare names (e.g. `slack_channel`, `telegram`) are unaffected.
14
+ *
15
+ * If a bare-name key already exists (runtime seed data created it), the
16
+ * old `integration:` rows are orphaned — we delete them instead of
17
+ * renaming to avoid UNIQUE constraint violations.
18
+ *
19
+ * FK constraints require us to update child tables (oauth_apps,
20
+ * oauth_connections) before the parent (oauth_providers), or disable FKs.
21
+ * We disable FKs for safety and update all three tables atomically.
22
+ */
23
+ export function migrateStripIntegrationPrefixFromProviderKeys(
24
+ database: DrizzleDb,
25
+ ): void {
26
+ withCrashRecovery(
27
+ database,
28
+ "migration_strip_integration_prefix_from_provider_keys_v1",
29
+ () => {
30
+ const raw = getSqliteFrom(database);
31
+
32
+ raw.exec("PRAGMA foreign_keys = OFF");
33
+ try {
34
+ // Find all provider keys with the integration: prefix.
35
+ const rows = raw
36
+ .prepare(
37
+ /*sql*/ `SELECT provider_key FROM oauth_providers WHERE provider_key LIKE 'integration:%'`,
38
+ )
39
+ .all() as Array<{ provider_key: string }>;
40
+
41
+ for (const { provider_key: oldKey } of rows) {
42
+ const newKey = oldKey.replace(/^integration:/, "");
43
+
44
+ // Check if the bare-name key already exists (seed data may have created it).
45
+ const bareExists = raw
46
+ .prepare(
47
+ /*sql*/ `SELECT 1 FROM oauth_providers WHERE provider_key = ?`,
48
+ )
49
+ .get(newKey);
50
+
51
+ if (bareExists) {
52
+ // Bare-name provider already exists — delete the old prefixed rows
53
+ // to avoid UNIQUE constraint violations.
54
+ raw
55
+ .prepare(
56
+ /*sql*/ `DELETE FROM oauth_connections WHERE provider_key = ?`,
57
+ )
58
+ .run(oldKey);
59
+ raw
60
+ .prepare(/*sql*/ `DELETE FROM oauth_apps WHERE provider_key = ?`)
61
+ .run(oldKey);
62
+ raw
63
+ .prepare(
64
+ /*sql*/ `DELETE FROM oauth_providers WHERE provider_key = ?`,
65
+ )
66
+ .run(oldKey);
67
+ } else {
68
+ // Rename: update child tables first, then parent.
69
+ raw
70
+ .prepare(
71
+ /*sql*/ `UPDATE oauth_connections SET provider_key = ? WHERE provider_key = ?`,
72
+ )
73
+ .run(newKey, oldKey);
74
+ raw
75
+ .prepare(
76
+ /*sql*/ `UPDATE oauth_apps SET provider_key = ? WHERE provider_key = ?`,
77
+ )
78
+ .run(newKey, oldKey);
79
+ raw
80
+ .prepare(
81
+ /*sql*/ `UPDATE oauth_providers SET provider_key = ? WHERE provider_key = ?`,
82
+ )
83
+ .run(newKey, oldKey);
84
+ }
85
+ }
86
+
87
+ // Also update the watchers table — credential_service stores provider
88
+ // keys like "integration:google" that feed into resolveOAuthConnection().
89
+ raw
90
+ .prepare(
91
+ /*sql*/ `UPDATE watchers SET credential_service = REPLACE(credential_service, 'integration:', '') WHERE credential_service LIKE 'integration:%'`,
92
+ )
93
+ .run();
94
+ } finally {
95
+ raw.exec("PRAGMA foreign_keys = ON");
96
+ }
97
+ },
98
+ );
99
+ }
100
+
101
+ /**
102
+ * Reverse: re-add the `integration:` prefix to provider keys that don't
103
+ * already have one and aren't known bare-name providers.
104
+ *
105
+ * This is a best-effort rollback — we prefix all keys that look like they
106
+ * were originally `integration:` prefixed. Known bare-name keys
107
+ * (`slack_channel`, `telegram`) are left as-is because they never had the
108
+ * prefix.
109
+ */
110
+ export function migrateStripIntegrationPrefixFromProviderKeysDown(
111
+ database: DrizzleDb,
112
+ ): void {
113
+ const raw = getSqliteFrom(database);
114
+
115
+ // Keys that were always bare — never had an integration: prefix.
116
+ const ALWAYS_BARE = new Set(["slack_channel", "telegram"]);
117
+
118
+ raw.exec("PRAGMA foreign_keys = OFF");
119
+ try {
120
+ const rows = raw
121
+ .prepare(
122
+ /*sql*/ `SELECT provider_key FROM oauth_providers WHERE provider_key NOT LIKE 'integration:%'`,
123
+ )
124
+ .all() as Array<{ provider_key: string }>;
125
+
126
+ for (const { provider_key: bareKey } of rows) {
127
+ if (ALWAYS_BARE.has(bareKey)) continue;
128
+
129
+ const prefixedKey = `integration:${bareKey}`;
130
+
131
+ // If the prefixed key already exists, delete the bare rows.
132
+ const prefixedExists = raw
133
+ .prepare(/*sql*/ `SELECT 1 FROM oauth_providers WHERE provider_key = ?`)
134
+ .get(prefixedKey);
135
+
136
+ if (prefixedExists) {
137
+ raw
138
+ .prepare(
139
+ /*sql*/ `DELETE FROM oauth_connections WHERE provider_key = ?`,
140
+ )
141
+ .run(bareKey);
142
+ raw
143
+ .prepare(/*sql*/ `DELETE FROM oauth_apps WHERE provider_key = ?`)
144
+ .run(bareKey);
145
+ raw
146
+ .prepare(/*sql*/ `DELETE FROM oauth_providers WHERE provider_key = ?`)
147
+ .run(bareKey);
148
+ } else {
149
+ raw
150
+ .prepare(
151
+ /*sql*/ `UPDATE oauth_connections SET provider_key = ? WHERE provider_key = ?`,
152
+ )
153
+ .run(prefixedKey, bareKey);
154
+ raw
155
+ .prepare(
156
+ /*sql*/ `UPDATE oauth_apps SET provider_key = ? WHERE provider_key = ?`,
157
+ )
158
+ .run(prefixedKey, bareKey);
159
+ raw
160
+ .prepare(
161
+ /*sql*/ `UPDATE oauth_providers SET provider_key = ? WHERE provider_key = ?`,
162
+ )
163
+ .run(prefixedKey, bareKey);
164
+ }
165
+ }
166
+
167
+ // Reverse the watchers table update — re-add the prefix for keys that
168
+ // aren't known bare-name providers.
169
+ const watcherRows = raw
170
+ .prepare(
171
+ /*sql*/ `SELECT DISTINCT credential_service FROM watchers WHERE credential_service NOT LIKE 'integration:%'`,
172
+ )
173
+ .all() as Array<{ credential_service: string }>;
174
+
175
+ for (const { credential_service: bareKey } of watcherRows) {
176
+ if (ALWAYS_BARE.has(bareKey)) continue;
177
+ raw
178
+ .prepare(
179
+ /*sql*/ `UPDATE watchers SET credential_service = ? WHERE credential_service = ?`,
180
+ )
181
+ .run(`integration:${bareKey}`, bareKey);
182
+ }
183
+ } finally {
184
+ raw.exec("PRAGMA foreign_keys = ON");
185
+ }
186
+ }
@@ -0,0 +1,29 @@
1
+ import type { DrizzleDb } from "../db-connection.js";
2
+ import { getSqliteFrom } from "../db-connection.js";
3
+
4
+ export function migrateOAuthProvidersBehaviorColumns(
5
+ database: DrizzleDb,
6
+ ): void {
7
+ const raw = getSqliteFrom(database);
8
+ const columns = [
9
+ "loopback_port INTEGER",
10
+ "injection_templates TEXT",
11
+ "setup_skill_id TEXT",
12
+ "app_type TEXT",
13
+ "setup_notes TEXT",
14
+ "identity_url TEXT",
15
+ "identity_method TEXT",
16
+ "identity_headers TEXT",
17
+ "identity_body TEXT",
18
+ "identity_response_paths TEXT",
19
+ "identity_format TEXT",
20
+ "identity_ok_field TEXT",
21
+ ];
22
+ for (const col of columns) {
23
+ try {
24
+ raw.exec(`ALTER TABLE oauth_providers ADD COLUMN ${col}`);
25
+ } catch {
26
+ // Column already exists — nothing to do.
27
+ }
28
+ }
29
+ }
@@ -0,0 +1,11 @@
1
+ import type { DrizzleDb } from "../db-connection.js";
2
+ import { getSqliteFrom } from "../db-connection.js";
3
+
4
+ export function migrateDropSetupSkillIdColumn(database: DrizzleDb): void {
5
+ const raw = getSqliteFrom(database);
6
+ try {
7
+ raw.exec(/*sql*/ `ALTER TABLE oauth_providers DROP COLUMN setup_skill_id`);
8
+ } catch {
9
+ // Column already dropped or doesn't exist — nothing to do.
10
+ }
11
+ }
@@ -132,6 +132,12 @@ export { migrateCallSessionSkipDisclosure } from "./190-call-session-skip-disclo
132
132
  export { migrateBackfillAudioAttachmentMimeTypes } from "./191-backfill-audio-attachment-mime-types.js";
133
133
  export { migrateContactsUserFileColumn } from "./192-contacts-user-file-column.js";
134
134
  export { migrateAddSourceTypeColumns } from "./193-add-source-type-columns.js";
135
+ export { migrateCreateMemoryRecallLogs } from "./194-memory-recall-logs.js";
136
+ export { migrateOAuthProvidersPingConfig } from "./195-oauth-providers-ping-config.js";
137
+ export { migrateMessagesConversationCreatedAtIndex } from "./196-messages-conversation-created-at-index.js";
138
+ export { migrateStripIntegrationPrefixFromProviderKeys } from "./196-strip-integration-prefix-from-provider-keys.js";
139
+ export { migrateOAuthProvidersBehaviorColumns } from "./197-oauth-providers-behavior-columns.js";
140
+ export { migrateDropSetupSkillIdColumn } from "./198-drop-setup-skill-id-column.js";
135
141
  export {
136
142
  MIGRATION_REGISTRY,
137
143
  type MigrationRegistryEntry,
@@ -38,6 +38,7 @@ import { migrateBackfillInlineAttachmentsToDiskDown } from "./180-backfill-inlin
38
38
  import { migrateRenameThreadStartersCheckpointsDown } from "./181-rename-thread-starters-checkpoints.js";
39
39
  import { migrateBackfillAudioAttachmentMimeTypesDown } from "./191-backfill-audio-attachment-mime-types.js";
40
40
  import { migrateAddSourceTypeColumnsDown } from "./193-add-source-type-columns.js";
41
+ import { migrateStripIntegrationPrefixFromProviderKeysDown } from "./196-strip-integration-prefix-from-provider-keys.js";
41
42
 
42
43
  export interface MigrationRegistryEntry {
43
44
  /** The checkpoint key written to memory_checkpoints on completion. */
@@ -333,6 +334,13 @@ export const MIGRATION_REGISTRY: MigrationRegistryEntry[] = [
333
334
  "Add source_type and source_message_role columns to memory_items with backfill from verification_state and source messages",
334
335
  down: migrateAddSourceTypeColumnsDown,
335
336
  },
337
+ {
338
+ key: "migration_strip_integration_prefix_from_provider_keys_v1",
339
+ version: 38,
340
+ description:
341
+ "Strip integration: prefix from provider_key across oauth_providers, oauth_apps, and oauth_connections",
342
+ down: migrateStripIntegrationPrefixFromProviderKeysDown,
343
+ },
336
344
  ];
337
345
 
338
346
  export function getMaxMigrationVersion(): number {
@@ -331,8 +331,7 @@ describe("Memory Retriever Pipeline", () => {
331
331
  expect(result.enabled).toBe(true);
332
332
  expect(result.degraded).toBe(false);
333
333
  expect(result.degradation).toBeUndefined();
334
- // With mock Qdrant returning empty results and recency-only candidates
335
- // scoring below tier thresholds, no candidates are selected.
334
+ // With Qdrant mocked empty, no candidates are found.
336
335
  // The pipeline still completes successfully with tier metadata.
337
336
  expect(result.tier1Count).toBeDefined();
338
337
  expect(result.tier2Count).toBeDefined();
@@ -345,7 +344,7 @@ describe("Memory Retriever Pipeline", () => {
345
344
  // Current-conversation segment filtering
346
345
  // -----------------------------------------------------------------------
347
346
 
348
- test("current-conversation segments are filtered from recency results", async () => {
347
+ test("current-conversation segments are filtered from search results", async () => {
349
348
  const db = getDb();
350
349
  const now = Date.now();
351
350
  const activeConv = "conv-active";
@@ -613,7 +612,7 @@ describe("Memory Retriever Pipeline", () => {
613
612
 
614
613
  insertConversation(db, convId, now - MS_PER_DAY * 200);
615
614
 
616
- // Create a message from 200 days ago to serve as recency source
615
+ // Create a message from 200 days ago (staleness test anchor)
617
616
  insertMessage(
618
617
  db,
619
618
  "msg-old",
@@ -679,7 +678,7 @@ describe("Memory Retriever Pipeline", () => {
679
678
  // Degradation: Qdrant circuit breaker open
680
679
  // -----------------------------------------------------------------------
681
680
 
682
- test("Qdrant unavailable: pipeline completes with recency fallback", async () => {
681
+ test("Qdrant unavailable: pipeline completes with empty results", async () => {
683
682
  seedMemory();
684
683
 
685
684
  // Force the Qdrant circuit breaker open