@vellumai/assistant 0.4.48 → 0.4.50

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 (423) hide show
  1. package/ARCHITECTURE.md +26 -35
  2. package/README.md +5 -26
  3. package/docs/architecture/integrations.md +45 -41
  4. package/docs/architecture/keychain-broker.md +3 -3
  5. package/docs/architecture/memory.md +180 -119
  6. package/docs/runbook-trusted-contacts.md +3 -8
  7. package/hook-templates/debug-prompt-logger/hook.json +1 -1
  8. package/hook-templates/debug-prompt-logger/run.sh +1 -3
  9. package/package.json +2 -2
  10. package/src/__tests__/actor-token-service.test.ts +0 -1
  11. package/src/__tests__/agent-loop.test.ts +3 -1
  12. package/src/__tests__/anthropic-provider.test.ts +249 -2
  13. package/src/__tests__/approval-cascade.test.ts +796 -0
  14. package/src/__tests__/approval-primitive.test.ts +0 -1
  15. package/src/__tests__/approval-routes-http.test.ts +4 -0
  16. package/src/__tests__/assistant-attachments.test.ts +12 -34
  17. package/src/__tests__/assistant-feature-flag-guard.test.ts +0 -23
  18. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +76 -0
  19. package/src/__tests__/assistant-feature-flags-integration.test.ts +0 -1
  20. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
  21. package/src/__tests__/canonical-guardian-store.test.ts +95 -0
  22. package/src/__tests__/channel-guardian.test.ts +0 -2
  23. package/src/__tests__/channel-readiness-routes.test.ts +15 -6
  24. package/src/__tests__/channel-readiness-service.test.ts +10 -9
  25. package/src/__tests__/checker.test.ts +13 -20
  26. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +1 -1
  27. package/src/__tests__/computer-use-tools.test.ts +2 -19
  28. package/src/__tests__/config-schema.test.ts +1 -68
  29. package/src/__tests__/config-watcher.test.ts +0 -1
  30. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
  31. package/src/__tests__/context-image-dimensions.test.ts +332 -0
  32. package/src/__tests__/context-memory-e2e.test.ts +11 -100
  33. package/src/__tests__/context-token-estimator.test.ts +196 -13
  34. package/src/__tests__/conversation-attention-store.test.ts +0 -1
  35. package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
  36. package/src/__tests__/conversation-routes-guardian-reply.test.ts +152 -0
  37. package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -0
  38. package/src/__tests__/credential-metadata-store.test.ts +64 -73
  39. package/src/__tests__/credential-security-e2e.test.ts +1 -0
  40. package/src/__tests__/credential-security-invariants.test.ts +13 -7
  41. package/src/__tests__/credential-vault-unit.test.ts +284 -49
  42. package/src/__tests__/credential-vault.test.ts +150 -16
  43. package/src/__tests__/credentials-cli.test.ts +71 -0
  44. package/src/__tests__/cu-unified-flow.test.ts +532 -0
  45. package/src/__tests__/date-context.test.ts +93 -77
  46. package/src/__tests__/deterministic-verification-control-plane.test.ts +64 -0
  47. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  48. package/src/__tests__/ephemeral-permissions.test.ts +3 -3
  49. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  50. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -1
  51. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +0 -1
  52. package/src/__tests__/guardian-routing-invariants.test.ts +93 -1
  53. package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -1
  54. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -39
  55. package/src/__tests__/heartbeat-service.test.ts +0 -1
  56. package/src/__tests__/history-repair.test.ts +245 -0
  57. package/src/__tests__/host-cu-proxy.test.ts +791 -0
  58. package/src/__tests__/host-shell-tool.test.ts +27 -15
  59. package/src/__tests__/http-user-message-parity.test.ts +2 -0
  60. package/src/__tests__/ingress-url-consistency.test.ts +14 -21
  61. package/src/__tests__/integration-status.test.ts +32 -51
  62. package/src/__tests__/intent-routing.test.ts +0 -1
  63. package/src/__tests__/invite-redemption-service.test.ts +65 -1
  64. package/src/__tests__/invite-routes-http.test.ts +10 -9
  65. package/src/__tests__/keychain-broker-client.test.ts +14 -46
  66. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +56 -18
  67. package/src/__tests__/memory-lifecycle-e2e.test.ts +244 -387
  68. package/src/__tests__/memory-recall-quality.test.ts +244 -407
  69. package/src/__tests__/memory-regressions.experimental.test.ts +126 -101
  70. package/src/__tests__/memory-regressions.test.ts +477 -2841
  71. package/src/__tests__/memory-retrieval.benchmark.test.ts +33 -150
  72. package/src/__tests__/memory-upsert-concurrency.test.ts +5 -244
  73. package/src/__tests__/mime-builder.test.ts +28 -0
  74. package/src/__tests__/native-web-search.test.ts +1 -0
  75. package/src/__tests__/notification-routing-intent.test.ts +0 -1
  76. package/src/__tests__/oauth-cli.test.ts +941 -15
  77. package/src/__tests__/oauth-provider-profiles.test.ts +9 -9
  78. package/src/__tests__/oauth-scope-policy.test.ts +4 -6
  79. package/src/__tests__/oauth-store.test.ts +870 -0
  80. package/src/__tests__/onboarding-starter-tasks.test.ts +0 -1
  81. package/src/__tests__/provider-error-scenarios.test.ts +0 -1
  82. package/src/__tests__/provider-streaming.benchmark.test.ts +0 -1
  83. package/src/__tests__/public-ingress-urls.test.ts +15 -21
  84. package/src/__tests__/qdrant-collection-migration.test.ts +53 -8
  85. package/src/__tests__/recording-handler.test.ts +3 -4
  86. package/src/__tests__/registry.test.ts +2 -3
  87. package/src/__tests__/relay-server.test.ts +46 -1
  88. package/src/__tests__/runtime-events-sse.test.ts +55 -7
  89. package/src/__tests__/schedule-store.test.ts +0 -1
  90. package/src/__tests__/schedule-tools.test.ts +32 -0
  91. package/src/__tests__/scheduler-recurrence.test.ts +0 -1
  92. package/src/__tests__/scoped-approval-grants.test.ts +0 -1
  93. package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -1
  94. package/src/__tests__/script-proxy-certs.test.ts +1 -1
  95. package/src/__tests__/secret-ingress-handler.test.ts +0 -1
  96. package/src/__tests__/secret-onetime-send.test.ts +1 -0
  97. package/src/__tests__/secure-keys.test.ts +7 -2
  98. package/src/__tests__/send-endpoint-busy.test.ts +24 -6
  99. package/src/__tests__/sequence-store.test.ts +0 -1
  100. package/src/__tests__/session-abort-tool-results.test.ts +1 -14
  101. package/src/__tests__/session-agent-loop-overflow.test.ts +1583 -0
  102. package/src/__tests__/session-agent-loop.test.ts +19 -15
  103. package/src/__tests__/session-confirmation-signals.test.ts +1 -15
  104. package/src/__tests__/session-error.test.ts +124 -2
  105. package/src/__tests__/session-history-web-search.test.ts +918 -0
  106. package/src/__tests__/session-init.benchmark.test.ts +4 -5
  107. package/src/__tests__/session-pre-run-repair.test.ts +1 -14
  108. package/src/__tests__/session-provider-retry-repair.test.ts +25 -28
  109. package/src/__tests__/session-queue.test.ts +37 -27
  110. package/src/__tests__/session-runtime-assembly.test.ts +54 -0
  111. package/src/__tests__/session-slash-known.test.ts +1 -15
  112. package/src/__tests__/session-slash-queue.test.ts +1 -15
  113. package/src/__tests__/session-slash-unknown.test.ts +1 -15
  114. package/src/__tests__/session-workspace-cache-state.test.ts +3 -33
  115. package/src/__tests__/session-workspace-injection.test.ts +3 -37
  116. package/src/__tests__/session-workspace-tool-tracking.test.ts +3 -37
  117. package/src/__tests__/skill-include-graph.test.ts +66 -0
  118. package/src/__tests__/skill-load-feature-flag.test.ts +0 -1
  119. package/src/__tests__/skill-load-tool.test.ts +149 -1
  120. package/src/__tests__/skill-projection-feature-flag.test.ts +0 -1
  121. package/src/__tests__/skills-install-extract.test.ts +93 -0
  122. package/src/__tests__/skills-uninstall.test.ts +1 -1
  123. package/src/__tests__/skills.test.ts +3 -3
  124. package/src/__tests__/skillssh-registry.test.ts +451 -0
  125. package/src/__tests__/slack-channel-config.test.ts +67 -3
  126. package/src/__tests__/slack-share-routes.test.ts +17 -19
  127. package/src/__tests__/system-prompt.test.ts +0 -1
  128. package/src/__tests__/telegram-invite-adapter.test.ts +18 -22
  129. package/src/__tests__/terminal-tools.test.ts +4 -3
  130. package/src/__tests__/test-support/computer-use-skill-harness.ts +3 -2
  131. package/src/__tests__/tool-approval-handler.test.ts +0 -1
  132. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -1
  133. package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
  134. package/src/__tests__/tool-executor-shell-integration.test.ts +0 -1
  135. package/src/__tests__/tool-executor.test.ts +0 -1
  136. package/src/__tests__/tool-grant-request-escalation.test.ts +0 -1
  137. package/src/__tests__/trust-store-pattern-matches.test.ts +29 -0
  138. package/src/__tests__/trust-store.test.ts +7 -13
  139. package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
  140. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +0 -1
  141. package/src/__tests__/twilio-routes.test.ts +0 -16
  142. package/src/__tests__/verification-control-plane-policy.test.ts +0 -1
  143. package/src/__tests__/voice-invite-redemption.test.ts +32 -1
  144. package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -1
  145. package/src/agent/ax-tree-compaction.test.ts +286 -0
  146. package/src/agent/loop.ts +104 -131
  147. package/src/approvals/AGENTS.md +1 -1
  148. package/src/approvals/guardian-request-resolvers.ts +14 -2
  149. package/src/bundler/compiler-tools.ts +66 -2
  150. package/src/calls/call-domain.ts +133 -6
  151. package/src/calls/call-store.ts +6 -0
  152. package/src/calls/relay-server.ts +52 -18
  153. package/src/calls/relay-setup-router.ts +17 -1
  154. package/src/calls/twilio-config.ts +3 -8
  155. package/src/calls/twilio-routes.ts +1 -2
  156. package/src/calls/types.ts +3 -1
  157. package/src/calls/voice-ingress-preflight.ts +1 -1
  158. package/src/cli/commands/browser-relay.ts +18 -12
  159. package/src/cli/commands/completions.ts +0 -3
  160. package/src/cli/commands/credentials.ts +101 -15
  161. package/src/cli/commands/doctor.ts +4 -3
  162. package/src/cli/commands/mcp.ts +46 -59
  163. package/src/cli/commands/memory.ts +16 -165
  164. package/src/cli/commands/oauth/apps.ts +284 -0
  165. package/src/cli/commands/oauth/connections.ts +633 -0
  166. package/src/cli/commands/oauth/index.ts +52 -0
  167. package/src/cli/commands/oauth/providers.ts +256 -0
  168. package/src/cli/commands/sessions.ts +5 -2
  169. package/src/cli/commands/skills.ts +177 -339
  170. package/src/cli/http-client.ts +0 -20
  171. package/src/cli/main-screen.tsx +2 -2
  172. package/src/cli/program.ts +6 -11
  173. package/src/cli/reference.ts +1 -3
  174. package/src/cli.ts +4 -10
  175. package/src/config/assistant-feature-flags.ts +0 -3
  176. package/src/config/bundled-skills/_shared/CLI_RETRIEVAL_PATTERN.md +1 -1
  177. package/src/config/bundled-skills/computer-use/SKILL.md +3 -6
  178. package/src/config/bundled-skills/computer-use/TOOLS.json +23 -5
  179. package/src/config/bundled-skills/computer-use/tools/{computer-use-request-control.ts → computer-use-observe.ts} +1 -5
  180. package/src/config/bundled-skills/google-calendar/calendar-client.ts +21 -16
  181. package/src/config/bundled-skills/messaging/tools/shared.ts +1 -4
  182. package/src/config/bundled-skills/settings/SKILL.md +1 -1
  183. package/src/config/bundled-skills/settings/TOOLS.json +2 -8
  184. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +5 -33
  185. package/src/config/bundled-tool-registry.ts +2 -5
  186. package/src/config/env-registry.ts +14 -83
  187. package/src/config/env.ts +11 -50
  188. package/src/config/feature-flag-registry.json +16 -16
  189. package/src/config/loader.ts +0 -6
  190. package/src/config/schema.ts +4 -13
  191. package/src/config/schemas/memory-lifecycle.ts +0 -9
  192. package/src/config/schemas/memory-processing.ts +0 -180
  193. package/src/config/schemas/memory-retrieval.ts +32 -104
  194. package/src/config/schemas/memory.ts +0 -10
  195. package/src/config/skills.ts +21 -2
  196. package/src/config/types.ts +0 -4
  197. package/src/context/image-dimensions.ts +229 -0
  198. package/src/context/token-estimator.ts +75 -12
  199. package/src/context/window-manager.ts +53 -11
  200. package/src/daemon/assistant-attachments.ts +1 -13
  201. package/src/daemon/config-watcher.ts +61 -3
  202. package/src/daemon/daemon-control.ts +1 -1
  203. package/src/daemon/date-context.ts +114 -31
  204. package/src/daemon/handlers/config-ingress.ts +8 -33
  205. package/src/daemon/handlers/config-slack-channel.ts +49 -46
  206. package/src/daemon/handlers/config-telegram.ts +32 -16
  207. package/src/daemon/handlers/sessions.ts +27 -36
  208. package/src/daemon/handlers/shared.ts +0 -130
  209. package/src/daemon/handlers/skills.ts +20 -1
  210. package/src/daemon/history-repair.ts +72 -8
  211. package/src/daemon/host-cu-proxy.ts +430 -0
  212. package/src/daemon/lifecycle.ts +67 -71
  213. package/src/daemon/mcp-reload-service.ts +2 -2
  214. package/src/daemon/message-protocol.ts +3 -0
  215. package/src/daemon/message-types/computer-use.ts +1 -129
  216. package/src/daemon/message-types/host-cu.ts +19 -0
  217. package/src/daemon/message-types/memory.ts +4 -16
  218. package/src/daemon/message-types/messages.ts +4 -0
  219. package/src/daemon/message-types/sessions.ts +4 -0
  220. package/src/daemon/server.ts +25 -21
  221. package/src/daemon/session-agent-loop-handlers.ts +40 -0
  222. package/src/daemon/session-agent-loop.ts +334 -48
  223. package/src/daemon/session-attachments.ts +1 -2
  224. package/src/daemon/session-error.ts +89 -6
  225. package/src/daemon/session-history.ts +17 -7
  226. package/src/daemon/session-media-retry.ts +6 -2
  227. package/src/daemon/session-memory.ts +69 -149
  228. package/src/daemon/session-process.ts +10 -1
  229. package/src/daemon/session-runtime-assembly.ts +49 -19
  230. package/src/daemon/session-slash.ts +1 -1
  231. package/src/daemon/session-surfaces.ts +43 -28
  232. package/src/daemon/session-tool-setup.ts +9 -10
  233. package/src/daemon/session.ts +150 -17
  234. package/src/daemon/tool-side-effects.ts +2 -8
  235. package/src/daemon/watch-handler.ts +2 -2
  236. package/src/events/tool-metrics-listener.ts +2 -2
  237. package/src/hooks/manager.ts +1 -4
  238. package/src/inbound/public-ingress-urls.ts +7 -7
  239. package/src/instrument.ts +61 -1
  240. package/src/logfire.ts +16 -5
  241. package/src/memory/admin.ts +2 -191
  242. package/src/memory/canonical-guardian-store.ts +38 -2
  243. package/src/memory/conversation-crud.ts +0 -33
  244. package/src/memory/conversation-key-store.ts +21 -0
  245. package/src/memory/conversation-queries.ts +22 -3
  246. package/src/memory/db-init.ts +32 -0
  247. package/src/memory/embedding-backend.ts +84 -8
  248. package/src/memory/embedding-types.ts +9 -1
  249. package/src/memory/indexer.ts +7 -46
  250. package/src/memory/items-extractor.ts +274 -76
  251. package/src/memory/job-handlers/backfill.ts +2 -127
  252. package/src/memory/job-handlers/cleanup.ts +2 -16
  253. package/src/memory/job-handlers/extraction.ts +2 -138
  254. package/src/memory/job-handlers/index-maintenance.ts +1 -6
  255. package/src/memory/job-handlers/summarization.ts +3 -148
  256. package/src/memory/job-utils.ts +21 -59
  257. package/src/memory/jobs-store.ts +1 -159
  258. package/src/memory/jobs-worker.ts +9 -52
  259. package/src/memory/migrations/104-core-indexes.ts +3 -3
  260. package/src/memory/migrations/149-oauth-tables.ts +62 -0
  261. package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +98 -0
  262. package/src/memory/migrations/151-oauth-providers-ping-url.ts +11 -0
  263. package/src/memory/migrations/152-memory-item-supersession.ts +44 -0
  264. package/src/memory/migrations/153-drop-entity-tables.ts +15 -0
  265. package/src/memory/migrations/154-drop-fts.ts +20 -0
  266. package/src/memory/migrations/155-drop-conflicts.ts +7 -0
  267. package/src/memory/migrations/156-call-session-invite-metadata.ts +24 -0
  268. package/src/memory/migrations/index.ts +8 -0
  269. package/src/memory/qdrant-client.ts +148 -51
  270. package/src/memory/raw-query.ts +1 -1
  271. package/src/memory/retriever.test.ts +294 -273
  272. package/src/memory/retriever.ts +421 -645
  273. package/src/memory/schema/calls.ts +2 -0
  274. package/src/memory/schema/index.ts +1 -0
  275. package/src/memory/schema/memory-core.ts +3 -48
  276. package/src/memory/schema/oauth.ts +67 -0
  277. package/src/memory/search/formatting.ts +263 -176
  278. package/src/memory/search/lexical.ts +1 -254
  279. package/src/memory/search/ranking.ts +0 -455
  280. package/src/memory/search/semantic.ts +100 -14
  281. package/src/memory/search/staleness.ts +47 -0
  282. package/src/memory/search/tier-classifier.ts +21 -0
  283. package/src/memory/search/types.ts +15 -77
  284. package/src/memory/task-memory-cleanup.ts +4 -6
  285. package/src/messaging/provider.ts +4 -4
  286. package/src/messaging/providers/gmail/client.ts +82 -2
  287. package/src/messaging/providers/gmail/mime-builder.ts +17 -7
  288. package/src/messaging/providers/gmail/people-client.ts +10 -10
  289. package/src/messaging/providers/telegram-bot/adapter.ts +17 -17
  290. package/src/messaging/providers/whatsapp/adapter.ts +11 -8
  291. package/src/messaging/registry.ts +2 -32
  292. package/src/notifications/copy-composer.ts +0 -5
  293. package/src/notifications/signal.ts +4 -5
  294. package/src/oauth/byo-connection.test.ts +133 -25
  295. package/src/oauth/byo-connection.ts +22 -6
  296. package/src/oauth/connect-orchestrator.ts +113 -57
  297. package/src/oauth/connect-types.ts +17 -23
  298. package/src/oauth/connection-resolver.ts +35 -11
  299. package/src/oauth/connection.ts +1 -1
  300. package/src/oauth/manual-token-connection.ts +104 -0
  301. package/src/oauth/oauth-store.ts +582 -0
  302. package/src/oauth/platform-connection.test.ts +29 -0
  303. package/src/oauth/platform-connection.ts +6 -5
  304. package/src/oauth/provider-behaviors.ts +124 -0
  305. package/src/oauth/scope-policy.ts +9 -2
  306. package/src/oauth/seed-providers.ts +167 -0
  307. package/src/oauth/token-persistence.ts +81 -77
  308. package/src/permissions/checker.ts +3 -3
  309. package/src/permissions/defaults.ts +1 -1
  310. package/src/permissions/prompter.ts +10 -1
  311. package/src/permissions/trust-store.ts +36 -1
  312. package/src/playbooks/playbook-compiler.ts +1 -1
  313. package/src/prompts/__tests__/build-cli-reference-section.test.ts +3 -1
  314. package/src/prompts/system-prompt.ts +46 -42
  315. package/src/providers/anthropic/client.ts +59 -20
  316. package/src/providers/retry.ts +1 -27
  317. package/src/providers/types.ts +7 -1
  318. package/src/runtime/AGENTS.md +9 -0
  319. package/src/runtime/auth/route-policy.ts +6 -6
  320. package/src/runtime/channel-reply-delivery.ts +0 -40
  321. package/src/runtime/gateway-client.ts +0 -7
  322. package/src/runtime/guardian-reply-router.ts +24 -22
  323. package/src/runtime/http-server.ts +10 -8
  324. package/src/runtime/http-types.ts +2 -2
  325. package/src/runtime/invite-redemption-service.ts +19 -1
  326. package/src/runtime/invite-service.ts +25 -0
  327. package/src/runtime/middleware/twilio-validation.ts +1 -11
  328. package/src/runtime/pending-interactions.ts +14 -12
  329. package/src/runtime/routes/brain-graph-routes.ts +10 -90
  330. package/src/runtime/routes/channel-delivery-routes.ts +0 -1
  331. package/src/runtime/routes/conversation-routes.ts +81 -19
  332. package/src/runtime/routes/events-routes.ts +21 -11
  333. package/src/runtime/routes/host-cu-routes.ts +97 -0
  334. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -12
  335. package/src/runtime/routes/inbound-stages/background-dispatch.ts +12 -111
  336. package/src/runtime/routes/integrations/slack/share.ts +6 -7
  337. package/src/runtime/routes/log-export-routes.ts +126 -8
  338. package/src/runtime/routes/memory-item-routes.test.ts +754 -0
  339. package/src/runtime/routes/memory-item-routes.ts +503 -0
  340. package/src/runtime/routes/session-management-routes.ts +3 -3
  341. package/src/runtime/routes/settings-routes.ts +55 -48
  342. package/src/runtime/routes/surface-action-routes.ts +1 -1
  343. package/src/runtime/routes/trust-rules-routes.ts +14 -0
  344. package/src/runtime/routes/watch-routes.ts +128 -0
  345. package/src/runtime/routes/workspace-routes.ts +2 -1
  346. package/src/schedule/integration-status.ts +10 -9
  347. package/src/security/credential-key.ts +0 -156
  348. package/src/security/keychain-broker-client.ts +22 -10
  349. package/src/security/oauth2.ts +1 -1
  350. package/src/security/secure-keys.ts +25 -3
  351. package/src/security/token-manager.ts +137 -64
  352. package/src/skills/catalog-install.ts +414 -0
  353. package/src/skills/include-graph.ts +32 -0
  354. package/src/skills/skillssh-registry.ts +503 -0
  355. package/src/telegram/bot-username.ts +2 -3
  356. package/src/tools/assets/search.ts +5 -1
  357. package/src/tools/browser/network-recorder.ts +1 -1
  358. package/src/tools/browser/network-recording-types.ts +1 -1
  359. package/src/tools/computer-use/definitions.ts +36 -11
  360. package/src/tools/computer-use/registry.ts +5 -6
  361. package/src/tools/credentials/broker.ts +1 -2
  362. package/src/tools/credentials/metadata-store.ts +17 -121
  363. package/src/tools/credentials/vault.ts +92 -167
  364. package/src/tools/memory/definitions.ts +4 -13
  365. package/src/tools/memory/handlers.test.ts +83 -103
  366. package/src/tools/memory/handlers.ts +50 -85
  367. package/src/tools/registry.ts +2 -7
  368. package/src/tools/schedule/create.ts +8 -1
  369. package/src/tools/schedule/update.ts +8 -1
  370. package/src/tools/skills/load.ts +85 -3
  371. package/src/tools/watch/watch-state.ts +0 -12
  372. package/src/util/logger.ts +7 -41
  373. package/src/util/platform.ts +9 -28
  374. package/src/watcher/providers/google-calendar.ts +2 -1
  375. package/src/__tests__/clarification-resolver.test.ts +0 -193
  376. package/src/__tests__/computer-use-session-compaction.test.ts +0 -143
  377. package/src/__tests__/computer-use-session-lifecycle.test.ts +0 -322
  378. package/src/__tests__/computer-use-session-working-dir.test.ts +0 -166
  379. package/src/__tests__/computer-use-skill-baseline.test.ts +0 -78
  380. package/src/__tests__/computer-use-skill-endstate.test.ts +0 -105
  381. package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +0 -249
  382. package/src/__tests__/conflict-intent-tokenization.test.ts +0 -160
  383. package/src/__tests__/conflict-policy.test.ts +0 -269
  384. package/src/__tests__/conflict-store.test.ts +0 -372
  385. package/src/__tests__/contradiction-checker.test.ts +0 -361
  386. package/src/__tests__/entity-extractor.test.ts +0 -211
  387. package/src/__tests__/entity-search.test.ts +0 -1117
  388. package/src/__tests__/profile-compiler.test.ts +0 -392
  389. package/src/__tests__/ride-shotgun-handler.test.ts +0 -452
  390. package/src/__tests__/session-conflict-gate.test.ts +0 -1228
  391. package/src/__tests__/session-profile-injection.test.ts +0 -557
  392. package/src/cli/commands/dev.ts +0 -129
  393. package/src/cli/commands/map.ts +0 -391
  394. package/src/cli/commands/oauth.ts +0 -77
  395. package/src/config/bundled-skills/knowledge-graph/SKILL.md +0 -25
  396. package/src/config/bundled-skills/knowledge-graph/TOOLS.json +0 -66
  397. package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +0 -211
  398. package/src/daemon/computer-use-session.ts +0 -1026
  399. package/src/daemon/ride-shotgun-handler.ts +0 -569
  400. package/src/daemon/session-conflict-gate.ts +0 -167
  401. package/src/daemon/session-dynamic-profile.ts +0 -77
  402. package/src/memory/clarification-resolver.ts +0 -417
  403. package/src/memory/conflict-intent.ts +0 -205
  404. package/src/memory/conflict-policy.ts +0 -127
  405. package/src/memory/conflict-store.ts +0 -410
  406. package/src/memory/contradiction-checker.ts +0 -508
  407. package/src/memory/entity-extractor.ts +0 -535
  408. package/src/memory/format-recall.ts +0 -47
  409. package/src/memory/fts-reconciler.ts +0 -165
  410. package/src/memory/job-handlers/conflict.ts +0 -200
  411. package/src/memory/profile-compiler.ts +0 -195
  412. package/src/memory/recall-cache.ts +0 -117
  413. package/src/memory/search/entity.ts +0 -535
  414. package/src/memory/search/query-expansion.test.ts +0 -70
  415. package/src/memory/search/query-expansion.ts +0 -118
  416. package/src/oauth/provider-base-urls.ts +0 -21
  417. package/src/oauth/provider-profiles.ts +0 -192
  418. package/src/prompts/computer-use-prompt.ts +0 -98
  419. package/src/runtime/routes/computer-use-routes.ts +0 -641
  420. package/src/runtime/routes/mcp-routes.ts +0 -20
  421. package/src/runtime/telegram-streaming-delivery.test.ts +0 -729
  422. package/src/runtime/telegram-streaming-delivery.ts +0 -393
  423. package/src/tools/computer-use/request-computer-control.ts +0 -56
@@ -63,14 +63,54 @@ mock.module("../security/oauth2.js", () => {
63
63
  };
64
64
  });
65
65
 
66
+ // ---------------------------------------------------------------------------
67
+ // Mock oauth-store — token-manager reads refresh config from SQLite
68
+ // ---------------------------------------------------------------------------
69
+
70
+ /** Mutable per-test map of provider connections for getConnectionByProvider */
71
+ const mockConnections = new Map<
72
+ string,
73
+ {
74
+ id: string;
75
+ providerKey: string;
76
+ oauthAppId: string;
77
+ expiresAt: number | null;
78
+ grantedScopes?: string;
79
+ accountInfo?: string | null;
80
+ }
81
+ >();
82
+ const mockApps = new Map<
83
+ string,
84
+ {
85
+ id: string;
86
+ providerKey: string;
87
+ clientId: string;
88
+ clientSecretCredentialPath: string;
89
+ }
90
+ >();
91
+ const mockProviders = new Map<
92
+ string,
93
+ {
94
+ key: string;
95
+ tokenUrl: string;
96
+ tokenEndpointAuthMethod?: string;
97
+ baseUrl?: string;
98
+ }
99
+ >();
100
+
101
+ mock.module("./oauth-store.js", () => ({
102
+ getConnectionByProvider: (service: string) => mockConnections.get(service),
103
+ getApp: (id: string) => mockApps.get(id),
104
+ getProvider: (key: string) => mockProviders.get(key),
105
+ updateConnection: () => {},
106
+ getMostRecentAppByProvider: () => undefined,
107
+ listConnections: () => [],
108
+ }));
109
+
66
110
  // ---------------------------------------------------------------------------
67
111
  // Imports (after mocks)
68
112
  // ---------------------------------------------------------------------------
69
113
 
70
- import {
71
- _resetMigrationFlag,
72
- credentialKey,
73
- } from "../security/credential-key.js";
74
114
  import { setSecureKey } from "../security/secure-keys.js";
75
115
  import {
76
116
  _resetInflightRefreshes,
@@ -88,7 +128,6 @@ import { resolveOAuthConnection } from "./connection-resolver.js";
88
128
  // ---------------------------------------------------------------------------
89
129
 
90
130
  const originalFetch = globalThis.fetch;
91
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
92
131
  let mockFetch: ReturnType<typeof mock<any>>;
93
132
 
94
133
  // ---------------------------------------------------------------------------
@@ -110,7 +149,10 @@ beforeEach(() => {
110
149
  _resetBackend();
111
150
  _resetRefreshBreakers();
112
151
  _resetInflightRefreshes();
113
- _resetMigrationFlag();
152
+ // Clear mock oauth-store maps
153
+ mockConnections.clear();
154
+ mockApps.clear();
155
+ mockProviders.clear();
114
156
 
115
157
  // Default mock fetch returning 200 JSON
116
158
  mockFetch = mock(() =>
@@ -139,16 +181,41 @@ function setupCredential(
139
181
  service: string,
140
182
  opts?: { expiresAt?: number; grantedScopes?: string[] },
141
183
  ) {
142
- setSecureKey(credentialKey(service, "access_token"), "test-access-token");
143
- setSecureKey(credentialKey(service, "refresh_token"), "test-refresh-token");
144
- setSecureKey(credentialKey(service, "client_secret"), "test-client-secret");
145
- upsertCredentialMetadata(service, "access_token", {
184
+ // Seed mock oauth-store maps so token-manager can resolve refresh config
185
+ const appId = `app-${service}`;
186
+ const connId = `conn-${service}`;
187
+ mockProviders.set(service, {
188
+ key: service,
189
+ tokenUrl: "https://oauth2.googleapis.com/token",
190
+ // Only well-known providers (gmail) have a baseUrl; custom services don't
191
+ baseUrl:
192
+ service === "integration:gmail"
193
+ ? "https://gmail.googleapis.com/gmail/v1/users/me"
194
+ : undefined,
195
+ });
196
+ mockApps.set(appId, {
197
+ id: appId,
198
+ providerKey: service,
199
+ clientId: "test-client-id",
200
+ clientSecretCredentialPath: `oauth_app/${appId}/client_secret`,
201
+ });
202
+ mockConnections.set(service, {
203
+ id: connId,
204
+ providerKey: service,
205
+ oauthAppId: appId,
146
206
  expiresAt: opts?.expiresAt ?? Date.now() + 3600 * 1000,
147
- grantedScopes: opts?.grantedScopes ?? ["read", "write"],
148
- oauth2TokenUrl: "https://oauth2.googleapis.com/token",
149
- oauth2ClientId: "test-client-id",
150
- hasRefreshToken: true,
207
+ grantedScopes: JSON.stringify(opts?.grantedScopes ?? ["read", "write"]),
208
+ accountInfo: null,
151
209
  });
210
+ // Store access token in oauth-store key format
211
+ setSecureKey(`oauth_connection/${connId}/access_token`, "test-access-token");
212
+ // Store refresh token and client_secret in secure keys (token-manager reads them)
213
+ setSecureKey(
214
+ `oauth_connection/${connId}/refresh_token`,
215
+ "test-refresh-token",
216
+ );
217
+ setSecureKey(`oauth_app/${appId}/client_secret`, "test-client-secret");
218
+ upsertCredentialMetadata(service, "access_token", {});
152
219
  }
153
220
 
154
221
  function createConnection(service = "integration:gmail"): BYOOAuthConnection {
@@ -185,10 +252,10 @@ describe("BYOOAuthConnection", () => {
185
252
  expect(url).toBe(
186
253
  "https://gmail.googleapis.com/gmail/v1/users/me/messages",
187
254
  );
188
- expect((init as RequestInit).headers).toMatchObject({
189
- Authorization: "Bearer test-access-token",
190
- "Content-Type": "application/json",
191
- });
255
+ const headers = (init as RequestInit).headers as Headers;
256
+ expect(headers.get("Authorization")).toBe("Bearer test-access-token");
257
+ // GET requests have no body, so Content-Type should not be set
258
+ expect(headers.has("Content-Type")).toBe(false);
192
259
  expect((init as RequestInit).method).toBe("GET");
193
260
  });
194
261
 
@@ -237,6 +304,9 @@ describe("BYOOAuthConnection", () => {
237
304
  JSON.stringify({ raw: "base64-encoded-email" }),
238
305
  );
239
306
  expect((init as RequestInit).method).toBe("POST");
307
+ // POST requests with a body should include Content-Type
308
+ const headers = (init as RequestInit).headers as Headers;
309
+ expect(headers.get("Content-Type")).toBe("application/json");
240
310
  });
241
311
 
242
312
  test("retries once on 401 response", async () => {
@@ -336,10 +406,9 @@ describe("BYOOAuthConnection", () => {
336
406
  });
337
407
 
338
408
  const [, init] = mockFetch.mock.calls[0];
339
- expect((init as RequestInit).headers).toMatchObject({
340
- "X-Custom-Header": "custom-value",
341
- Authorization: "Bearer test-access-token",
342
- });
409
+ const headers = (init as RequestInit).headers as Headers;
410
+ expect(headers.get("X-Custom-Header")).toBe("custom-value");
411
+ expect(headers.get("Authorization")).toBe("Bearer test-access-token");
343
412
  });
344
413
  });
345
414
 
@@ -361,9 +430,10 @@ describe("BYOOAuthConnection", () => {
361
430
 
362
431
  // The request should use the refreshed token
363
432
  const [, init] = mockFetch.mock.calls[0];
364
- expect((init as RequestInit).headers).toMatchObject({
365
- Authorization: "Bearer refreshed-access-token",
366
- });
433
+ const headers = (init as RequestInit).headers as Headers;
434
+ expect(headers.get("Authorization")).toBe(
435
+ "Bearer refreshed-access-token",
436
+ );
367
437
  });
368
438
  });
369
439
 
@@ -433,4 +503,42 @@ describe("resolveOAuthConnection", () => {
433
503
  /No base URL configured for "integration:custom-service"/,
434
504
  );
435
505
  });
506
+
507
+ test("resolves base URL via app's canonical providerKey for custom credential_service", () => {
508
+ // Set up a well-known provider with a baseUrl
509
+ mockProviders.set("github", {
510
+ key: "github",
511
+ tokenUrl: "https://github.com/login/oauth/access_token",
512
+ baseUrl: "https://api.github.com",
513
+ });
514
+ // The custom credential service has no provider entry of its own
515
+ // (getProvider("integration:github-work") returns undefined)
516
+
517
+ // App points to the canonical "github" provider
518
+ const appId = "app-github-work";
519
+ mockApps.set(appId, {
520
+ id: appId,
521
+ providerKey: "github",
522
+ clientId: "test-client-id",
523
+ clientSecretCredentialPath: `oauth_app/${appId}/client_secret`,
524
+ });
525
+
526
+ // Connection uses the custom credential service as its providerKey
527
+ const connId = "conn-github-work";
528
+ mockConnections.set("integration:github-work", {
529
+ id: connId,
530
+ providerKey: "integration:github-work",
531
+ oauthAppId: appId,
532
+ expiresAt: Date.now() + 3600 * 1000,
533
+ grantedScopes: JSON.stringify(["repo"]),
534
+ accountInfo: null,
535
+ });
536
+ setSecureKey(`oauth_connection/${connId}/access_token`, "ghp-test-token");
537
+
538
+ const conn = resolveOAuthConnection("integration:github-work");
539
+
540
+ expect(conn).toBeInstanceOf(BYOOAuthConnection);
541
+ expect(conn.providerKey).toBe("integration:github-work");
542
+ expect(conn.grantedScopes).toEqual(["repo"]);
543
+ });
436
544
  });
@@ -53,7 +53,14 @@ export class BYOOAuthConnection implements OAuthConnection {
53
53
  let fullUrl = `${effectiveBaseUrl}${req.path}`;
54
54
 
55
55
  if (req.query && Object.keys(req.query).length > 0) {
56
- const params = new URLSearchParams(req.query);
56
+ const params = new URLSearchParams();
57
+ for (const [key, value] of Object.entries(req.query)) {
58
+ if (Array.isArray(value)) {
59
+ for (const v of value) params.append(key, v);
60
+ } else {
61
+ params.append(key, value);
62
+ }
63
+ }
57
64
  fullUrl += `?${params.toString()}`;
58
65
  }
59
66
 
@@ -62,13 +69,22 @@ export class BYOOAuthConnection implements OAuthConnection {
62
69
  "Making authenticated request",
63
70
  );
64
71
 
72
+ // Use the Headers API for case-insensitive merging. Set defaults
73
+ // first so caller-supplied headers (in any casing) override them.
74
+ const headers = new Headers();
75
+ if (req.body) {
76
+ headers.set("Content-Type", "application/json");
77
+ }
78
+ if (req.headers) {
79
+ for (const [key, value] of Object.entries(req.headers)) {
80
+ headers.set(key, value);
81
+ }
82
+ }
83
+ headers.set("Authorization", `Bearer ${token}`);
84
+
65
85
  const resp = await fetch(fullUrl, {
66
86
  method: req.method,
67
- headers: {
68
- ...(req.body ? { "Content-Type": "application/json" } : {}),
69
- ...req.headers,
70
- Authorization: `Bearer ${token}`,
71
- },
87
+ headers,
72
88
  body: req.body ? JSON.stringify(req.body) : undefined,
73
89
  signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
74
90
  });
@@ -12,7 +12,7 @@
12
12
  * - Ensuring metadata is writable (assertMetadataWritable)
13
13
  *
14
14
  * The orchestrator handles:
15
- * - Provider profile resolution
15
+ * - Provider config resolution (from DB)
16
16
  * - Scope policy enforcement
17
17
  * - Building the OAuth2Config
18
18
  * - Running the interactive or deferred flow
@@ -23,13 +23,54 @@
23
23
  import type { TokenEndpointAuthMethod } from "../security/oauth2.js";
24
24
  import { prepareOAuth2Flow, startOAuth2Flow } from "../security/oauth2.js";
25
25
  import { getLogger } from "../util/logger.js";
26
- import type { OAuthConnectResult } from "./connect-types.js";
27
- import { getProviderProfile, resolveService } from "./provider-profiles.js";
26
+ import type {
27
+ OAuthConnectResult,
28
+ OAuthProviderBehavior,
29
+ OAuthScopePolicy,
30
+ } from "./connect-types.js";
31
+ import { getProvider } from "./oauth-store.js";
32
+ import { getProviderBehavior, resolveService } from "./provider-behaviors.js";
28
33
  import { resolveScopes } from "./scope-policy.js";
29
34
  import { storeOAuth2Tokens } from "./token-persistence.js";
30
35
 
31
36
  const log = getLogger("oauth-connect-orchestrator");
32
37
 
38
+ // ---------------------------------------------------------------------------
39
+ // Helpers
40
+ // ---------------------------------------------------------------------------
41
+
42
+ /**
43
+ * Look up the code-side behavioral fields for a provider.
44
+ * Returns an empty object when no behavior is registered.
45
+ */
46
+ function resolveBehavior(providerKey: string): {
47
+ identityVerifier?: OAuthProviderBehavior["identityVerifier"];
48
+ setup?: OAuthProviderBehavior["setup"];
49
+ setupSkillId?: string;
50
+ postConnectHookId?: string;
51
+ injectionTemplates?: OAuthProviderBehavior["injectionTemplates"];
52
+ } {
53
+ const behavior = getProviderBehavior(providerKey);
54
+ if (!behavior) return {};
55
+ return {
56
+ identityVerifier: behavior.identityVerifier,
57
+ setup: behavior.setup,
58
+ setupSkillId: behavior.setupSkillId,
59
+ postConnectHookId: behavior.postConnectHookId,
60
+ injectionTemplates: behavior.injectionTemplates,
61
+ };
62
+ }
63
+
64
+ /** Safely parse a JSON string, returning a fallback on failure or null/undefined input. */
65
+ function safeJsonParse<T>(value: string | null | undefined, fallback: T): T {
66
+ if (value == null) return fallback;
67
+ try {
68
+ return JSON.parse(value) as T;
69
+ } catch {
70
+ return fallback;
71
+ }
72
+ }
73
+
33
74
  // ---------------------------------------------------------------------------
34
75
  // Options
35
76
  // ---------------------------------------------------------------------------
@@ -63,15 +104,6 @@ export interface OAuthConnectOptions {
63
104
  accountInfo?: string;
64
105
  error?: string;
65
106
  }) => void;
66
-
67
- // Optional overrides — when provided, these take precedence over the
68
- // provider profile. This lets callers connect custom / unknown providers.
69
- authUrl?: string;
70
- tokenUrl?: string;
71
- scopes?: string[];
72
- extraParams?: Record<string, string>;
73
- userinfoUrl?: string;
74
- tokenEndpointAuthMethod?: TokenEndpointAuthMethod;
75
107
  }
76
108
 
77
109
  // ---------------------------------------------------------------------------
@@ -90,42 +122,69 @@ export async function orchestrateOAuthConnect(
90
122
  options: OAuthConnectOptions,
91
123
  ): Promise<OAuthConnectResult> {
92
124
  const resolvedService = resolveService(options.service);
93
- const profile = getProviderProfile(resolvedService);
94
125
 
95
- // Merge explicit overrides with profile defaults
96
- const authUrl = options.authUrl ?? profile?.authUrl;
97
- const tokenUrl = options.tokenUrl ?? profile?.tokenUrl;
98
- const extraParams = options.extraParams ?? profile?.extraParams;
99
- const userinfoUrl = options.userinfoUrl ?? profile?.userinfoUrl;
100
- const tokenEndpointAuthMethod =
101
- options.tokenEndpointAuthMethod ?? profile?.tokenEndpointAuthMethod;
126
+ // Read provider config from the DB
127
+ const providerRow = getProvider(resolvedService);
128
+ if (!providerRow) {
129
+ return {
130
+ success: false,
131
+ error: `No OAuth provider registered for "${resolvedService}". Ensure the provider is seeded in the database.`,
132
+ safeError: true,
133
+ };
134
+ }
135
+
136
+ // Behavioral/code-side fields come from the behavior registry
137
+ const behavior = resolveBehavior(resolvedService);
102
138
 
103
- // Scopes: use explicit override, then try scope policy resolution, then profile defaults
104
- let finalScopes: string[];
105
- if (options.scopes) {
106
- // Explicit scopes override — bypass policy (caller takes responsibility)
107
- finalScopes = options.scopes;
108
- } else if (profile) {
109
- const scopeResult = resolveScopes(profile, options.requestedScopes);
110
- if (!scopeResult.ok) {
111
- const guidance = scopeResult.allowedScopes
112
- ? ` Allowed scopes: ${scopeResult.allowedScopes.join(", ")}`
113
- : "";
114
- return {
115
- success: false,
116
- error: `${scopeResult.error}${guidance}`,
117
- safeError: true,
118
- };
119
- }
120
- finalScopes = scopeResult.scopes;
121
- } else {
122
- // No profile and no explicit scopes — cannot proceed
139
+ // Deserialize JSON fields from the DB row
140
+ const dbDefaultScopes = safeJsonParse<string[]>(
141
+ providerRow.defaultScopes,
142
+ [],
143
+ );
144
+ const dbScopePolicy = safeJsonParse<OAuthScopePolicy>(
145
+ providerRow.scopePolicy,
146
+ {
147
+ allowAdditionalScopes: false,
148
+ allowedOptionalScopes: [],
149
+ forbiddenScopes: [],
150
+ },
151
+ );
152
+ const dbExtraParams = safeJsonParse<Record<string, string> | undefined>(
153
+ providerRow.extraParams,
154
+ undefined,
155
+ );
156
+
157
+ // Resolve all protocol-level config from the DB
158
+ const authUrl = providerRow.authUrl;
159
+ const tokenUrl = providerRow.tokenUrl;
160
+ const extraParams = dbExtraParams;
161
+ const userinfoUrl = providerRow.userinfoUrl ?? undefined;
162
+ const tokenEndpointAuthMethod = providerRow.tokenEndpointAuthMethod as
163
+ | TokenEndpointAuthMethod
164
+ | undefined;
165
+ const callbackTransport =
166
+ (providerRow.callbackTransport as "loopback" | "gateway" | null) ??
167
+ "gateway";
168
+ const loopbackPort = providerRow.loopbackPort;
169
+
170
+ // Resolve scopes via the scope policy engine
171
+ const scopeProfile = {
172
+ service: resolvedService,
173
+ defaultScopes: dbDefaultScopes,
174
+ scopePolicy: dbScopePolicy,
175
+ };
176
+ const scopeResult = resolveScopes(scopeProfile, options.requestedScopes);
177
+ if (!scopeResult.ok) {
178
+ const guidance = scopeResult.allowedScopes
179
+ ? ` Allowed scopes: ${scopeResult.allowedScopes.join(", ")}`
180
+ : "";
123
181
  return {
124
182
  success: false,
125
- error: `No well-known OAuth config found for "${options.service}" and no scopes were provided`,
183
+ error: `${scopeResult.error}${guidance}`,
126
184
  safeError: true,
127
185
  };
128
186
  }
187
+ const finalScopes = scopeResult.scopes;
129
188
 
130
189
  if (!authUrl) {
131
190
  return {
@@ -161,7 +220,7 @@ export async function orchestrateOAuthConnect(
161
220
  tokenEndpointAuthMethod,
162
221
  userinfoUrl,
163
222
  allowedTools: options.allowedTools,
164
- wellKnownInjectionTemplates: profile?.injectionTemplates,
223
+ wellKnownInjectionTemplates: behavior.injectionTemplates,
165
224
  };
166
225
 
167
226
  // -----------------------------------------------------------------------
@@ -169,8 +228,6 @@ export async function orchestrateOAuthConnect(
169
228
  // -----------------------------------------------------------------------
170
229
  if (!options.isInteractive) {
171
230
  try {
172
- const callbackTransport = profile?.callbackTransport ?? "gateway";
173
-
174
231
  // Gateway transport needs a public ingress URL
175
232
  if (callbackTransport !== "loopback") {
176
233
  const { loadConfig } = await import("../config/loader.js");
@@ -191,7 +248,7 @@ export async function orchestrateOAuthConnect(
191
248
  const prepared = await prepareOAuth2Flow(
192
249
  oauthConfig,
193
250
  callbackTransport === "loopback"
194
- ? { callbackTransport, loopbackPort: profile?.loopbackPort }
251
+ ? { callbackTransport, loopbackPort: loopbackPort ?? undefined }
195
252
  : undefined,
196
253
  );
197
254
 
@@ -201,10 +258,10 @@ export async function orchestrateOAuthConnect(
201
258
  try {
202
259
  let accountInfo: string | undefined;
203
260
 
204
- // Run identity verifier if available
205
- if (profile?.identityVerifier) {
261
+ // Run identity verifier if available (code-side behavior)
262
+ if (behavior.identityVerifier) {
206
263
  try {
207
- accountInfo = await profile.identityVerifier(
264
+ accountInfo = await behavior.identityVerifier(
208
265
  result.tokens.accessToken,
209
266
  );
210
267
  } catch {
@@ -293,19 +350,18 @@ export async function orchestrateOAuthConnect(
293
350
  }
294
351
  },
295
352
  },
296
- profile?.callbackTransport
297
- ? {
298
- callbackTransport: profile.callbackTransport,
299
- loopbackPort: profile.loopbackPort,
300
- }
301
- : undefined,
353
+ callbackTransport === "loopback"
354
+ ? { callbackTransport, loopbackPort: loopbackPort ?? undefined }
355
+ : callbackTransport === "gateway"
356
+ ? { callbackTransport }
357
+ : undefined,
302
358
  );
303
359
 
304
- // Run identity verifier if available
360
+ // Run identity verifier if available (code-side behavior)
305
361
  let verifiedIdentity: string | undefined;
306
- if (profile?.identityVerifier) {
362
+ if (behavior.identityVerifier) {
307
363
  try {
308
- verifiedIdentity = await profile.identityVerifier(tokens.accessToken);
364
+ verifiedIdentity = await behavior.identityVerifier(tokens.accessToken);
309
365
  } catch {
310
366
  // Non-fatal
311
367
  }
@@ -1,11 +1,15 @@
1
1
  /**
2
2
  * Shared types for the OAuth provider extensibility layer.
3
3
  *
4
- * These types are consumed by the provider profile registry, the token
4
+ * These types are consumed by the provider behavior registry, the token
5
5
  * persistence module, and the credential vault orchestrator.
6
+ *
7
+ * Protocol-level OAuth config (authUrl, tokenUrl, scopes, etc.) is now
8
+ * stored exclusively in the `oauth_providers` SQLite table. This file
9
+ * only defines code-side behavioral types that cannot be serialised to
10
+ * a DB row (functions, templates, UI metadata).
6
11
  */
7
12
 
8
- import type { TokenEndpointAuthMethod } from "../security/oauth2.js";
9
13
  import type { CredentialInjectionTemplate } from "../tools/credentials/policy-types.js";
10
14
 
11
15
  // ---------------------------------------------------------------------------
@@ -23,31 +27,21 @@ export interface OAuthScopePolicy {
23
27
  }
24
28
 
25
29
  // ---------------------------------------------------------------------------
26
- // Provider profile
30
+ // Provider behavior
27
31
  // ---------------------------------------------------------------------------
28
32
 
29
- /** Full configuration profile for a well-known OAuth provider. */
30
- export interface OAuthProviderProfile {
33
+ /**
34
+ * Code-side behavioral configuration for a well-known OAuth provider.
35
+ *
36
+ * Protocol-level fields (authUrl, tokenUrl, defaultScopes, scopePolicy,
37
+ * tokenEndpointAuthMethod, callbackTransport, loopbackPort, userinfoUrl,
38
+ * extraParams) are stored in the `oauth_providers` DB table. This
39
+ * interface contains only fields that require code references (functions,
40
+ * templates, skill IDs) and cannot be serialised to a DB row.
41
+ */
42
+ export interface OAuthProviderBehavior {
31
43
  /** Canonical service key (e.g. "integration:twitter"). */
32
44
  service: string;
33
- /** OAuth2 authorization endpoint. */
34
- authUrl: string;
35
- /** OAuth2 token endpoint. */
36
- tokenUrl: string;
37
- /** Default scopes requested during authorization. */
38
- defaultScopes: string[];
39
- /** Policy governing additional/forbidden scopes. */
40
- scopePolicy: OAuthScopePolicy;
41
- /** How to send client credentials at the token endpoint. */
42
- tokenEndpointAuthMethod?: TokenEndpointAuthMethod;
43
- /** Force a specific callback transport. */
44
- callbackTransport?: "loopback" | "gateway";
45
- /** Fixed port for loopback transport (e.g. Slack). */
46
- loopbackPort?: number;
47
- /** Endpoint to fetch user identity info after authorization. */
48
- userinfoUrl?: string;
49
- /** Extra query parameters appended to the authorization URL. */
50
- extraParams?: Record<string, string>;
51
45
  /**
52
46
  * Async function that verifies the user's identity after a successful
53
47
  * token exchange. Returns a human-readable account identifier (e.g.
@@ -1,34 +1,58 @@
1
- import { getCredentialMetadata } from "../tools/credentials/metadata-store.js";
1
+ import { getSecureKey } from "../security/secure-keys.js";
2
2
  import { BYOOAuthConnection } from "./byo-connection.js";
3
3
  import type { OAuthConnection } from "./connection.js";
4
- import { getProviderBaseUrl } from "./provider-base-urls.js";
4
+ import { getApp, getConnectionByProvider, getProvider } from "./oauth-store.js";
5
5
 
6
6
  /**
7
7
  * Resolve an OAuthConnection for a given credential service.
8
- * Currently always creates a BYO connection from existing credential store.
9
- * Will be extended to create Platform connections when storage model supports it.
8
+ *
9
+ * Reads exclusively from the SQLite oauth-store. Throws if no connection
10
+ * exists (authorization required).
10
11
  */
11
12
  export function resolveOAuthConnection(
12
13
  credentialService: string,
13
14
  ): OAuthConnection {
14
- const meta = getCredentialMetadata(credentialService, "access_token");
15
- if (!meta) {
15
+ const conn = getConnectionByProvider(credentialService);
16
+ if (!conn) {
16
17
  throw new Error(
17
18
  `No credential found for "${credentialService}". Authorization required.`,
18
19
  );
19
20
  }
20
21
 
21
- const baseUrl = getProviderBaseUrl(credentialService);
22
+ const accessToken = getSecureKey(`oauth_connection/${conn.id}/access_token`);
23
+
24
+ if (!accessToken) {
25
+ throw new Error(
26
+ `No access token found for "${credentialService}". Authorization required.`,
27
+ );
28
+ }
29
+
30
+ // Look up the provider by credentialService first; fall back to the
31
+ // connection's app's canonical providerKey so custom credential_service
32
+ // overrides (e.g. "integration:github-work") still resolve to the well-known
33
+ // provider's base URL. We traverse conn -> oauthApp -> providerKey because
34
+ // conn.providerKey equals credentialService (getConnectionByProvider queries
35
+ // WHERE providerKey = credentialService), whereas the app's providerKey is a
36
+ // foreign key to the oauthProviders table.
37
+ const provider =
38
+ getProvider(credentialService) ??
39
+ getProvider(getApp(conn.oauthAppId)?.providerKey ?? "");
40
+ const baseUrl = provider?.baseUrl;
41
+
22
42
  if (!baseUrl) {
23
43
  throw new Error(`No base URL configured for "${credentialService}".`);
24
44
  }
25
45
 
46
+ const grantedScopes: string[] = conn.grantedScopes
47
+ ? JSON.parse(conn.grantedScopes)
48
+ : [];
49
+
26
50
  return new BYOOAuthConnection({
27
- id: meta.credentialId,
28
- providerKey: credentialService,
51
+ id: conn.id,
52
+ providerKey: conn.providerKey,
29
53
  baseUrl,
30
- accountInfo: null,
31
- grantedScopes: meta.grantedScopes ?? [],
54
+ accountInfo: conn.accountInfo,
55
+ grantedScopes,
32
56
  credentialService,
33
57
  });
34
58
  }
@@ -1,7 +1,7 @@
1
1
  export interface OAuthConnectionRequest {
2
2
  method: string;
3
3
  path: string; // relative, e.g. "/2/tweets"
4
- query?: Record<string, string>;
4
+ query?: Record<string, string | string[]>;
5
5
  headers?: Record<string, string>;
6
6
  body?: unknown; // JSON-serializable
7
7
  /**