@vellumai/assistant 0.7.3 → 0.8.1

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 (778) hide show
  1. package/AGENTS.md +11 -0
  2. package/ARCHITECTURE.md +29 -28
  3. package/Dockerfile +6 -4
  4. package/README.md +2 -2
  5. package/__tests__/permissions/gateway-threshold-reader.test.ts +236 -9
  6. package/bun.lock +3 -0
  7. package/docker-entrypoint.sh +16 -0
  8. package/eslint-rules/__tests__/cli-no-daemon-internals.test.ts +420 -0
  9. package/eslint-rules/cli-no-daemon-internals.js +283 -0
  10. package/eslint.config.mjs +12 -0
  11. package/knip.json +3 -1
  12. package/node_modules/@vellumai/ipc-server-utils/bun.lock +24 -0
  13. package/node_modules/@vellumai/ipc-server-utils/package.json +18 -0
  14. package/node_modules/@vellumai/ipc-server-utils/src/index.ts +6 -0
  15. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +430 -0
  16. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +221 -0
  17. package/node_modules/@vellumai/ipc-server-utils/tsconfig.json +20 -0
  18. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +10 -1
  19. package/openapi.yaml +4126 -959
  20. package/package.json +5 -1
  21. package/scripts/generate-openapi.ts +52 -4
  22. package/scripts/sync-llm-catalog.ts +165 -0
  23. package/scripts/sync-web-search-catalog.ts +107 -0
  24. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +169 -0
  25. package/src/__tests__/agent-loop-override-profile.test.ts +26 -1
  26. package/src/__tests__/annotate-risk-options.test.ts +291 -0
  27. package/src/__tests__/anthropic-provider.test.ts +92 -2
  28. package/src/__tests__/app-control-flow.test.ts +7 -0
  29. package/src/__tests__/approval-cascade.test.ts +8 -16
  30. package/src/__tests__/approval-routes-http.test.ts +6 -0
  31. package/src/__tests__/assistant-events-sse-shed.test.ts +232 -0
  32. package/src/__tests__/auto-analysis-end-to-end.test.ts +12 -25
  33. package/src/__tests__/avatar-identity-sync.test.ts +87 -0
  34. package/src/__tests__/background-workers-disk-pressure.test.ts +11 -22
  35. package/src/__tests__/btw-routes.test.ts +1 -0
  36. package/src/__tests__/call-constants.test.ts +10 -1
  37. package/src/__tests__/call-controller.test.ts +127 -0
  38. package/src/__tests__/call-site-routing-provider.test.ts +172 -45
  39. package/src/__tests__/cancel-resolves-conversation-key.test.ts +44 -3
  40. package/src/__tests__/channel-policy.test.ts +12 -0
  41. package/src/__tests__/checker.test.ts +89 -0
  42. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +88 -30
  43. package/src/__tests__/compact-event-conversation-id-guard.test.ts +33 -5
  44. package/src/__tests__/compaction-strip-metadata-clear.test.ts +26 -1
  45. package/src/__tests__/config-loader-backfill.test.ts +526 -102
  46. package/src/__tests__/config-loader-corrupt.test.ts +68 -0
  47. package/src/__tests__/config-loader-platform-defaults.test.ts +345 -8
  48. package/src/__tests__/config-schema-cmd.test.ts +63 -29
  49. package/src/__tests__/config-schema.test.ts +14 -3
  50. package/src/__tests__/config-set-platform-guard.test.ts +75 -152
  51. package/src/__tests__/config-set-route.test.ts +198 -0
  52. package/src/__tests__/config-watcher.test.ts +6 -0
  53. package/src/__tests__/contacts-tools.test.ts +51 -199
  54. package/src/__tests__/context-search-agent-protocol.test.ts +21 -2
  55. package/src/__tests__/context-search-agent-runner.test.ts +22 -138
  56. package/src/__tests__/context-search-conversations-source.test.ts +42 -16
  57. package/src/__tests__/context-search-fanout.test.ts +20 -157
  58. package/src/__tests__/context-search-memory-source.test.ts +3 -26
  59. package/src/__tests__/context-search-memory-v2-source.test.ts +3 -3
  60. package/src/__tests__/context-search-types.test.ts +7 -2
  61. package/src/__tests__/context-window-manager.test.ts +389 -1
  62. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -6
  63. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -1
  64. package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -1
  65. package/src/__tests__/conversation-agent-loop.test.ts +3 -3
  66. package/src/__tests__/conversation-confirmation-signals.test.ts +5 -13
  67. package/src/__tests__/conversation-crud-inference-profile.test.ts +100 -0
  68. package/src/__tests__/conversation-error.test.ts +38 -0
  69. package/src/__tests__/conversation-fork-crud.test.ts +241 -1
  70. package/src/__tests__/conversation-inference-profile-route.test.ts +14 -14
  71. package/src/__tests__/conversation-init.benchmark.test.ts +2 -1
  72. package/src/__tests__/conversation-lifecycle.test.ts +124 -0
  73. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +100 -1
  74. package/src/__tests__/conversation-process-callsite.test.ts +22 -7
  75. package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -6
  76. package/src/__tests__/conversation-runtime-assembly.test.ts +19 -10
  77. package/src/__tests__/conversation-slash-commands.test.ts +194 -2
  78. package/src/__tests__/conversation-slash-unknown.test.ts +1 -6
  79. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +170 -9
  80. package/src/__tests__/conversation-surfaces-app-control.test.ts +323 -3
  81. package/src/__tests__/conversation-surfaces-data-persist.test.ts +73 -1
  82. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +59 -0
  83. package/src/__tests__/conversation-workspace-injection.test.ts +1 -7
  84. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -7
  85. package/src/__tests__/credential-security-invariants.test.ts +5 -6
  86. package/src/__tests__/daemon-credential-client.test.ts +56 -1
  87. package/src/__tests__/db-activation-state-fk-cascade.test.ts +132 -0
  88. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +37 -0
  89. package/src/__tests__/db-memory-graph-event-date-repair.test.ts +43 -20
  90. package/src/__tests__/db-proxy-transaction.test.ts +206 -0
  91. package/src/__tests__/external-plugin-loader.test.ts +458 -0
  92. package/src/__tests__/filing-service.test.ts +25 -22
  93. package/src/__tests__/fixtures/mock-chrome-extension.ts +5 -0
  94. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  95. package/src/__tests__/graph-extraction-event-date.test.ts +34 -0
  96. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +10 -34
  97. package/src/__tests__/heartbeat-disk-pressure.test.ts +21 -8
  98. package/src/__tests__/heartbeat-service.test.ts +50 -233
  99. package/src/__tests__/history-repair.test.ts +89 -0
  100. package/src/__tests__/host-app-control-proxy.test.ts +109 -1
  101. package/src/__tests__/host-app-control-routes.test.ts +247 -1
  102. package/src/__tests__/host-browser-proxy.test.ts +416 -20
  103. package/src/__tests__/host-browser-routes.test.ts +325 -33
  104. package/src/__tests__/host-proxy-preactivation.test.ts +211 -0
  105. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +246 -0
  106. package/src/__tests__/inference-profile-reaper.test.ts +154 -0
  107. package/src/__tests__/inference-profile-session-handler.test.ts +398 -0
  108. package/src/__tests__/inference-profile-session-ipc.test.ts +236 -0
  109. package/src/__tests__/injector-chain.test.ts +24 -16
  110. package/src/__tests__/injector-pkb-v2-silenced.test.ts +10 -7
  111. package/src/__tests__/inline-skill-load-permissions.test.ts +6 -1
  112. package/src/__tests__/install-skill-routing.test.ts +2 -2
  113. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +169 -67
  114. package/src/__tests__/llm-callsite-catalog.test.ts +20 -1
  115. package/src/__tests__/llm-catalog-parity.test.ts +146 -0
  116. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +188 -0
  117. package/src/__tests__/llm-request-log-source-factory.test.ts +124 -0
  118. package/src/__tests__/llm-resolver.test.ts +46 -0
  119. package/src/__tests__/managed-profile-guard.test.ts +131 -2
  120. package/src/__tests__/mcp-auth-routes.test.ts +1 -0
  121. package/src/__tests__/mcp-cli.test.ts +182 -220
  122. package/src/__tests__/mcp-health-check.test.ts +56 -27
  123. package/src/__tests__/memory-jobs-worker-lanes.test.ts +18 -11
  124. package/src/__tests__/message-complete-display-id.test.ts +175 -0
  125. package/src/__tests__/notification-decision-fallback.test.ts +91 -0
  126. package/src/__tests__/notification-decision-strategy.test.ts +22 -0
  127. package/src/__tests__/notification-platform-adapter.test.ts +229 -0
  128. package/src/__tests__/oauth-cli.test.ts +38 -1888
  129. package/src/__tests__/oauth-commands-routes.test.ts +711 -0
  130. package/src/__tests__/oauth-connect-routes.test.ts +174 -11
  131. package/src/__tests__/oauth-providers-routes.test.ts +14 -10
  132. package/src/__tests__/openai-responses-cutover-guard.test.ts +33 -12
  133. package/src/__tests__/openai-responses-provider.test.ts +17 -0
  134. package/src/__tests__/plugin-bootstrap.test.ts +31 -2
  135. package/src/__tests__/plugin-route-contribution.test.ts +31 -3
  136. package/src/__tests__/plugin-tool-contribution.test.ts +31 -3
  137. package/src/__tests__/plugin-types.test.ts +13 -11
  138. package/src/__tests__/process-message-background-slack.test.ts +46 -0
  139. package/src/__tests__/profile-entry-status.test.ts +43 -0
  140. package/src/__tests__/provider-managed-proxy-integration.test.ts +12 -4
  141. package/src/__tests__/provider-registry-ollama.test.ts +12 -4
  142. package/src/__tests__/provider-send-message-override-profile.test.ts +10 -4
  143. package/src/__tests__/relay-server.test.ts +164 -2
  144. package/src/__tests__/retry-thinking-tool-choice.test.ts +15 -0
  145. package/src/__tests__/schedule-retry.test.ts +56 -4
  146. package/src/__tests__/schedule-routes.test.ts +104 -0
  147. package/src/__tests__/scheduler-disk-pressure.test.ts +0 -4
  148. package/src/__tests__/scheduler-recurrence.test.ts +87 -34
  149. package/src/__tests__/scheduler-reuse-conversation.test.ts +161 -5
  150. package/src/__tests__/scheduler-wake.test.ts +0 -63
  151. package/src/__tests__/secret-allowlist.test.ts +1 -0
  152. package/src/__tests__/secret-prompt-log-hygiene.test.ts +7 -5
  153. package/src/__tests__/secret-prompter-channel-fallback.test.ts +7 -5
  154. package/src/__tests__/secret-response-routing.test.ts +7 -5
  155. package/src/__tests__/secret-routes-managed-proxy.test.ts +12 -4
  156. package/src/__tests__/server-history-render.test.ts +82 -0
  157. package/src/__tests__/shell-credential-ref.test.ts +95 -3
  158. package/src/__tests__/shell-tool-proxy-mode.test.ts +14 -0
  159. package/src/__tests__/skill-include-graph.test.ts +31 -0
  160. package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
  161. package/src/__tests__/skill-load-tool.test.ts +42 -16
  162. package/src/__tests__/skills.test.ts +39 -0
  163. package/src/__tests__/subagent-call-site-routing.test.ts +78 -16
  164. package/src/__tests__/suggestion-routes.test.ts +3 -3
  165. package/src/__tests__/sync-message-contract.test.ts +63 -0
  166. package/src/__tests__/task-scheduler.test.ts +88 -23
  167. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -42
  168. package/src/__tests__/tool-executor.test.ts +155 -0
  169. package/src/__tests__/update-bulletin-job.test.ts +96 -193
  170. package/src/__tests__/usage-cli.test.ts +11 -73
  171. package/src/__tests__/user-plugin-loader.test.ts +145 -0
  172. package/src/__tests__/vercel-config.test.ts +168 -0
  173. package/src/__tests__/voice-session-bridge.test.ts +3 -0
  174. package/src/__tests__/web-search-catalog-parity.test.ts +86 -0
  175. package/src/__tests__/web-search.test.ts +303 -2
  176. package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +1 -21
  177. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +58 -0
  178. package/src/__tests__/workspace-migration-069-seed-onboarding-threads.test.ts +153 -0
  179. package/src/__tests__/workspace-migration-071-remove-safe-storage-release-note.test.ts +206 -0
  180. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +191 -0
  181. package/src/__tests__/workspace-migration-076-drop-services-inference-mode.test.ts +211 -0
  182. package/src/__tests__/workspace-migration-077-seed-memory-router-callsite.test.ts +174 -0
  183. package/src/__tests__/workspace-migration-079-home-feed-notification-only.test.ts +323 -0
  184. package/src/__tests__/workspace-migration-080-restrict-vercel-api-token-metadata.test.ts +299 -0
  185. package/src/__tests__/workspace-migration-081-backfill-bash-allowed-tools.test.ts +410 -0
  186. package/src/__tests__/workspace-migration-082-backfill-managed-profile-labels.test.ts +268 -0
  187. package/src/__tests__/workspace-migration-safe-storage-limits-release.test.ts +15 -27
  188. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +3 -3
  189. package/src/__tests__/workspace-release-notes-feature-flag-guard.test.ts +115 -0
  190. package/src/acp/__tests__/helpers/which-stub.ts +4 -2
  191. package/src/acp/resolve-agent.test.ts +25 -0
  192. package/src/acp/resolve-agent.ts +13 -2
  193. package/src/acp/session-manager.ts +14 -0
  194. package/src/agent/loop.ts +11 -0
  195. package/src/approvals/guardian-decision-primitive.ts +0 -13
  196. package/src/approvals/guardian-request-resolvers.ts +19 -102
  197. package/src/calls/call-constants.ts +5 -8
  198. package/src/calls/call-controller.ts +130 -67
  199. package/src/calls/relay-server.ts +42 -1
  200. package/src/calls/relay-setup-router.ts +36 -0
  201. package/src/calls/types.ts +1 -0
  202. package/src/calls/voice-session-bridge.ts +24 -5
  203. package/src/channels/config.ts +14 -1
  204. package/src/channels/types.ts +1 -0
  205. package/src/cli/AGENTS.md +164 -4
  206. package/src/cli/__tests__/notifications.test.ts +54 -0
  207. package/src/cli/commands/__tests__/avatar.test.ts +540 -0
  208. package/src/cli/commands/__tests__/backup.test.ts +236 -776
  209. package/src/cli/commands/__tests__/cache.test.ts +1 -1
  210. package/src/cli/commands/__tests__/changelog.test.ts +593 -0
  211. package/src/cli/commands/__tests__/channel-verification-sessions.test.ts +503 -0
  212. package/src/cli/commands/__tests__/conversations-import.test.ts +515 -0
  213. package/src/cli/commands/__tests__/domain-register.test.ts +140 -167
  214. package/src/cli/commands/__tests__/domain-status.test.ts +137 -76
  215. package/src/cli/commands/__tests__/email-attachment.test.ts +314 -337
  216. package/src/cli/commands/__tests__/email-core.test.ts +579 -0
  217. package/src/cli/commands/__tests__/image-generation.test.ts +87 -824
  218. package/src/cli/commands/__tests__/inference-send.test.ts +30 -266
  219. package/src/cli/commands/__tests__/inference-session.test.ts +423 -0
  220. package/src/cli/commands/__tests__/memory-v2.test.ts +81 -110
  221. package/src/cli/commands/__tests__/skills.test.ts +563 -0
  222. package/src/cli/commands/__tests__/status.test.ts +249 -0
  223. package/src/cli/commands/__tests__/stt.test.ts +320 -0
  224. package/src/cli/commands/__tests__/tts-synthesize.test.ts +4 -603
  225. package/src/cli/commands/__tests__/tts.test.ts +321 -0
  226. package/src/cli/commands/__tests__/webhooks.test.ts +86 -511
  227. package/src/cli/commands/attachment.ts +8 -3
  228. package/src/cli/commands/audit.ts +95 -64
  229. package/src/cli/commands/auth.ts +61 -58
  230. package/src/cli/commands/avatar.ts +276 -390
  231. package/src/cli/commands/backup.ts +409 -505
  232. package/src/cli/commands/bash.ts +9 -5
  233. package/src/cli/commands/browser.ts +28 -9
  234. package/src/cli/commands/cache.ts +9 -4
  235. package/src/cli/commands/changelog.ts +414 -0
  236. package/src/cli/commands/channel-verification-sessions.ts +238 -317
  237. package/src/cli/commands/clients.ts +8 -3
  238. package/src/cli/commands/completions.ts +9 -9
  239. package/src/cli/commands/config.ts +102 -72
  240. package/src/cli/commands/contacts.ts +575 -696
  241. package/src/cli/commands/conversations-defer.ts +17 -69
  242. package/src/cli/commands/conversations-import.ts +90 -253
  243. package/src/cli/commands/conversations.ts +346 -436
  244. package/src/cli/commands/credential-execution.ts +9 -6
  245. package/src/cli/commands/credentials.ts +456 -736
  246. package/src/cli/commands/domain.ts +128 -206
  247. package/src/cli/commands/email.ts +606 -794
  248. package/src/cli/commands/gateway.ts +8 -1
  249. package/src/cli/commands/image-generation.ts +157 -205
  250. package/src/cli/commands/inference-providers.ts +352 -0
  251. package/src/cli/commands/inference-session.ts +415 -0
  252. package/src/cli/commands/inference.ts +87 -65
  253. package/src/cli/commands/keys.ts +8 -3
  254. package/src/cli/commands/mcp.ts +103 -287
  255. package/src/cli/commands/memory-v2.ts +163 -517
  256. package/src/cli/commands/notifications.ts +33 -7
  257. package/src/cli/commands/oauth/apps.ts +292 -261
  258. package/src/cli/commands/oauth/connect.ts +182 -345
  259. package/src/cli/commands/oauth/disconnect.ts +16 -215
  260. package/src/cli/commands/oauth/index.ts +49 -45
  261. package/src/cli/commands/oauth/mode.ts +43 -199
  262. package/src/cli/commands/oauth/ping.ts +17 -125
  263. package/src/cli/commands/oauth/providers.ts +732 -921
  264. package/src/cli/commands/oauth/request.ts +60 -350
  265. package/src/cli/commands/oauth/shared.ts +11 -121
  266. package/src/cli/commands/oauth/status.ts +31 -121
  267. package/src/cli/commands/oauth/token.ts +13 -55
  268. package/src/cli/commands/pending.ts +19 -10
  269. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +133 -183
  270. package/src/cli/commands/platform/__tests__/connect.test.ts +66 -181
  271. package/src/cli/commands/platform/__tests__/disconnect.test.ts +71 -227
  272. package/src/cli/commands/platform/__tests__/status.test.ts +169 -287
  273. package/src/cli/commands/platform/connect.ts +16 -80
  274. package/src/cli/commands/platform/disconnect.ts +14 -112
  275. package/src/cli/commands/platform/index.ts +177 -246
  276. package/src/cli/commands/routes.ts +153 -336
  277. package/src/cli/commands/sequence.ts +316 -360
  278. package/src/cli/commands/skills.ts +449 -671
  279. package/src/cli/commands/status.ts +58 -37
  280. package/src/cli/commands/stt.ts +94 -262
  281. package/src/cli/commands/task.ts +14 -40
  282. package/src/cli/commands/trust.ts +8 -3
  283. package/src/cli/commands/tts.ts +162 -167
  284. package/src/cli/commands/ui.ts +35 -42
  285. package/src/cli/commands/usage.ts +188 -126
  286. package/src/cli/commands/watchers.ts +8 -3
  287. package/src/cli/commands/webhooks.ts +99 -193
  288. package/src/cli/lib/__tests__/register-command.test.ts +85 -0
  289. package/src/cli/lib/daemon-credential-client.ts +4 -5
  290. package/src/cli/lib/nested-value.ts +44 -0
  291. package/src/cli/lib/open-browser.ts +36 -0
  292. package/src/cli/lib/register-command.ts +19 -0
  293. package/src/cli/lib/time-ago.ts +34 -0
  294. package/src/cli/program.ts +2 -4
  295. package/src/cli/utils/__tests__/conversation-id.test.ts +66 -0
  296. package/src/cli/utils/__tests__/parse-duration.test.ts +49 -0
  297. package/src/cli/utils/conversation-id.ts +30 -0
  298. package/src/cli/utils/parse-duration.ts +41 -0
  299. package/src/config/acp-defaults.test.ts +5 -1
  300. package/src/config/acp-defaults.ts +11 -4
  301. package/src/config/bundled-skills/acp/TOOLS.json +2 -2
  302. package/src/config/bundled-skills/app-builder/SKILL.md +1 -3
  303. package/src/config/bundled-skills/app-control/TOOLS.json +32 -0
  304. package/src/config/bundled-skills/contacts/SKILL.md +12 -45
  305. package/src/config/bundled-skills/contacts/TOOLS.json +0 -57
  306. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +0 -12
  307. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +0 -58
  308. package/src/config/bundled-tool-registry.ts +0 -2
  309. package/src/config/feature-flag-registry.json +17 -17
  310. package/src/config/llm-resolver.ts +16 -1
  311. package/src/config/loader.ts +148 -33
  312. package/src/config/raw-config-utils.ts +2 -30
  313. package/src/config/schema.ts +4 -0
  314. package/src/config/schemas/__tests__/memory-v2.test.ts +49 -0
  315. package/src/config/schemas/call-site-catalog.ts +29 -7
  316. package/src/config/schemas/llm-request-logs.ts +57 -0
  317. package/src/config/schemas/llm.ts +52 -2
  318. package/src/config/schemas/memory-retrospective.ts +48 -0
  319. package/src/config/schemas/memory-v2.ts +33 -2
  320. package/src/config/schemas/memory.ts +4 -0
  321. package/src/config/schemas/services.ts +15 -12
  322. package/src/config/seed-inference-profiles.ts +195 -134
  323. package/src/contacts/contact-store.ts +0 -61
  324. package/src/context/window-manager.ts +191 -5
  325. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +111 -0
  326. package/src/daemon/__tests__/conversation-tool-setup.test.ts +109 -4
  327. package/src/daemon/__tests__/daemon-skill-host.test.ts +10 -4
  328. package/src/daemon/approval-generators.ts +23 -29
  329. package/src/daemon/config-watcher.ts +2 -0
  330. package/src/daemon/conversation-agent-loop-handlers.ts +56 -0
  331. package/src/daemon/conversation-agent-loop.ts +140 -107
  332. package/src/daemon/conversation-error.ts +21 -0
  333. package/src/daemon/conversation-lifecycle.ts +68 -13
  334. package/src/daemon/conversation-process.ts +36 -19
  335. package/src/daemon/conversation-runtime-assembly.ts +14 -5
  336. package/src/daemon/conversation-slash.ts +175 -23
  337. package/src/daemon/conversation-store.ts +17 -10
  338. package/src/daemon/conversation-surfaces.ts +92 -26
  339. package/src/daemon/conversation-tool-setup.ts +33 -19
  340. package/src/daemon/conversation.ts +49 -10
  341. package/src/daemon/external-plugins-bootstrap.ts +18 -8
  342. package/src/daemon/guardian-action-generators.ts +7 -22
  343. package/src/daemon/handlers/config-model.ts +8 -126
  344. package/src/daemon/handlers/config-slack-channel.ts +10 -7
  345. package/src/daemon/handlers/config-vercel.ts +3 -1
  346. package/src/daemon/handlers/shared.ts +26 -0
  347. package/src/daemon/handlers/skills.ts +84 -5
  348. package/src/daemon/history-repair.ts +33 -6
  349. package/src/daemon/host-app-control-proxy.ts +44 -19
  350. package/src/daemon/host-bash-proxy.ts +85 -158
  351. package/src/daemon/host-browser-proxy.ts +97 -36
  352. package/src/daemon/host-cu-proxy.ts +1 -1
  353. package/src/daemon/host-file-proxy.ts +1 -1
  354. package/src/daemon/host-proxy-base.ts +13 -1
  355. package/src/daemon/host-proxy-preactivation.ts +25 -1
  356. package/src/daemon/host-transfer-proxy.ts +2 -2
  357. package/src/daemon/identity-helpers.ts +19 -0
  358. package/src/daemon/lifecycle.ts +128 -114
  359. package/src/daemon/meet-host-supervisor.ts +15 -15
  360. package/src/daemon/memory-v2-startup.ts +62 -14
  361. package/src/daemon/message-protocol.ts +6 -0
  362. package/src/daemon/message-types/bookmarks.ts +18 -0
  363. package/src/daemon/message-types/conversations.ts +12 -9
  364. package/src/daemon/message-types/messages.ts +28 -2
  365. package/src/daemon/message-types/sync.ts +60 -0
  366. package/src/daemon/pkb-reminder-builder.test.ts +54 -13
  367. package/src/daemon/pkb-reminder-builder.ts +21 -7
  368. package/src/daemon/process-message.ts +56 -23
  369. package/src/daemon/server.ts +23 -18
  370. package/src/daemon/shutdown-handlers.ts +0 -2
  371. package/src/daemon/tool-setup-types.ts +9 -0
  372. package/src/daemon/tool-side-effects.ts +6 -4
  373. package/src/daemon/wake-target-adapter.ts +11 -0
  374. package/src/documents/document-store.ts +35 -1
  375. package/src/export/transcript-formatter.ts +61 -2
  376. package/src/filing/filing-service.ts +42 -56
  377. package/src/heartbeat/__tests__/heartbeat-service.test.ts +359 -0
  378. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  379. package/src/heartbeat/heartbeat-service.ts +149 -128
  380. package/src/home/__tests__/feed-types.test.ts +63 -131
  381. package/src/home/__tests__/feed-writer.test.ts +77 -278
  382. package/src/home/__tests__/post-connect-feed.test.ts +9 -12
  383. package/src/home/feed-types.ts +19 -73
  384. package/src/home/feed-writer.ts +25 -156
  385. package/src/home/post-connect-feed.ts +1 -3
  386. package/src/ipc/__tests__/cli-ipc.test.ts +2 -0
  387. package/src/ipc/__tests__/email-ipc.test.ts +506 -0
  388. package/src/ipc/__tests__/exit-helper.test.ts +104 -0
  389. package/src/ipc/__tests__/streaming-client.test.ts +237 -0
  390. package/src/ipc/__tests__/streaming-framing.test.ts +142 -0
  391. package/src/ipc/assistant-server.ts +148 -42
  392. package/src/ipc/cli-client.ts +370 -50
  393. package/src/ipc/routes/db-proxy-transaction.ts +151 -0
  394. package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +60 -0
  395. package/src/ipc/skill-routes/events.ts +30 -3
  396. package/src/ipc/skill-server.ts +99 -42
  397. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +46 -0
  398. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +1 -0
  399. package/src/live-voice/live-voice-session-manager.ts +11 -4
  400. package/src/live-voice/live-voice-session.ts +14 -6
  401. package/src/memory/__tests__/bookmark-crud.test.ts +258 -0
  402. package/src/memory/__tests__/bookmark-schema.test.ts +181 -0
  403. package/src/memory/__tests__/conversation-types.test.ts +36 -0
  404. package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +130 -0
  405. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +10 -57
  406. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +177 -0
  407. package/src/memory/__tests__/memory-retrospective-job.test.ts +328 -0
  408. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +213 -0
  409. package/src/memory/__tests__/memory-retrospective-trigger-check.test.ts +90 -0
  410. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +69 -0
  411. package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +3 -0
  412. package/src/memory/bookmark-crud.ts +179 -0
  413. package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +31 -9
  414. package/src/memory/context-search/agent-protocol.ts +5 -1
  415. package/src/memory/context-search/agent-runner.ts +60 -85
  416. package/src/memory/context-search/limits.ts +1 -4
  417. package/src/memory/context-search/search.ts +23 -113
  418. package/src/memory/context-search/sources/conversations.ts +18 -6
  419. package/src/memory/context-search/sources/memory-v2.ts +40 -31
  420. package/src/memory/context-search/sources/memory.ts +9 -2
  421. package/src/memory/context-search/sources/workspace.ts +13 -10
  422. package/src/memory/context-search/types.ts +1 -1
  423. package/src/memory/conversation-bootstrap.ts +11 -0
  424. package/src/memory/conversation-crud.ts +312 -10
  425. package/src/memory/conversation-queries.ts +9 -5
  426. package/src/memory/conversation-title-service.ts +1 -0
  427. package/src/memory/conversation-types.ts +16 -0
  428. package/src/memory/db-init.ts +14 -0
  429. package/src/memory/embedding-backend.ts +2 -1
  430. package/src/memory/embedding-runtime-manager.ts +1 -2
  431. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +104 -61
  432. package/src/memory/graph/__tests__/handle-remember-v2.test.ts +11 -26
  433. package/src/memory/graph/__tests__/remember-description.test.ts +55 -0
  434. package/src/memory/graph/conversation-graph-memory.ts +108 -14
  435. package/src/memory/graph/extraction.ts +4 -0
  436. package/src/memory/graph/graph-memory-state-store.ts +16 -3
  437. package/src/memory/graph/graph-search.test.ts +6 -5
  438. package/src/memory/graph/graph-search.ts +3 -4
  439. package/src/memory/graph/retriever.test.ts +12 -7
  440. package/src/memory/graph/retriever.ts +4 -5
  441. package/src/memory/graph/tool-handlers.ts +20 -11
  442. package/src/memory/graph/tools.ts +48 -9
  443. package/src/memory/indexer.ts +18 -2
  444. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +120 -6
  445. package/src/memory/jobs/embed-concept-page.ts +261 -89
  446. package/src/memory/jobs-store.ts +51 -1
  447. package/src/memory/jobs-worker.ts +60 -7
  448. package/src/memory/llm-request-log-source-clickhouse.ts +317 -0
  449. package/src/memory/llm-request-log-source-local.ts +26 -0
  450. package/src/memory/llm-request-log-source.ts +97 -0
  451. package/src/memory/llm-request-log-store.ts +1 -1
  452. package/src/memory/memory-retrospective-constants.ts +13 -0
  453. package/src/memory/memory-retrospective-enqueue.ts +114 -0
  454. package/src/memory/memory-retrospective-job.ts +351 -0
  455. package/src/memory/memory-retrospective-startup-cleanup.ts +108 -0
  456. package/src/memory/memory-retrospective-state.ts +162 -0
  457. package/src/memory/memory-retrospective-trigger-check.ts +91 -0
  458. package/src/memory/memory-v2-activation-log-store.ts +49 -5
  459. package/src/memory/memory-v2-concept-frequency.ts +4 -0
  460. package/src/memory/message-content.ts +38 -1
  461. package/src/memory/migrations/227-add-conversation-inference-profile.ts +6 -1
  462. package/src/memory/migrations/228-rename-inference-profile-snake-case.ts +20 -7
  463. package/src/memory/migrations/229-delete-private-conversations.test.ts +70 -1
  464. package/src/memory/migrations/229-delete-private-conversations.ts +12 -0
  465. package/src/memory/migrations/231-repair-memory-graph-event-dates.ts +16 -2
  466. package/src/memory/migrations/240-conversation-inference-profile-session.ts +25 -0
  467. package/src/memory/migrations/241-activation-state-fk-cascade.ts +50 -0
  468. package/src/memory/migrations/242-message-bookmarks.ts +38 -0
  469. package/src/memory/migrations/243-provider-connections.ts +68 -0
  470. package/src/memory/migrations/244-provider-connection-status-label.ts +23 -0
  471. package/src/memory/migrations/245-memory-retrospective-state.ts +36 -0
  472. package/src/memory/migrations/246-backfill-provider-connection-label.ts +81 -0
  473. package/src/memory/migrations/__tests__/244-provider-connection-status-label.test.ts +84 -0
  474. package/src/memory/migrations/__tests__/245-memory-retrospective-state.test.ts +125 -0
  475. package/src/memory/migrations/__tests__/246-backfill-provider-connection-label.test.ts +192 -0
  476. package/src/memory/migrations/index.ts +7 -0
  477. package/src/memory/pkb/pkb-search.test.ts +6 -5
  478. package/src/memory/pkb/pkb-search.ts +4 -5
  479. package/src/memory/published-pages-store.ts +16 -0
  480. package/src/memory/qdrant-client.ts +3 -0
  481. package/src/memory/schema/bookmarks.ts +38 -0
  482. package/src/memory/schema/conversations.ts +2 -0
  483. package/src/memory/schema/index.ts +2 -0
  484. package/src/memory/schema/inference.ts +29 -0
  485. package/src/memory/schema/memory-core.ts +9 -0
  486. package/src/memory/search/semantic.ts +5 -9
  487. package/src/memory/v2/__tests__/__snapshots__/prompts-router.test.ts.snap +27 -0
  488. package/src/memory/v2/__tests__/activation-store.test.ts +5 -5
  489. package/src/memory/v2/__tests__/activation.test.ts +46 -9
  490. package/src/memory/v2/__tests__/backfill-jobs.test.ts +38 -21
  491. package/src/memory/v2/__tests__/consolidation-job.test.ts +140 -163
  492. package/src/memory/v2/__tests__/edge-index.test.ts +1 -1
  493. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +111 -0
  494. package/src/memory/v2/__tests__/injection.test.ts +768 -33
  495. package/src/memory/v2/__tests__/migration.test.ts +7 -3
  496. package/src/memory/v2/__tests__/page-index.test.ts +277 -0
  497. package/src/memory/v2/__tests__/page-store.test.ts +14 -1
  498. package/src/memory/v2/__tests__/prompts-router.test.ts +257 -0
  499. package/src/memory/v2/__tests__/qdrant.test.ts +382 -9
  500. package/src/memory/v2/__tests__/reranker.test.ts +4 -4
  501. package/src/memory/v2/__tests__/router.test.ts +516 -0
  502. package/src/memory/v2/__tests__/sim.test.ts +163 -8
  503. package/src/memory/v2/__tests__/skill-store.test.ts +58 -3
  504. package/src/memory/v2/__tests__/static-context.test.ts +8 -35
  505. package/src/memory/v2/__tests__/sweep-job.test.ts +114 -33
  506. package/src/memory/v2/activation-store.ts +34 -5
  507. package/src/memory/v2/activation.ts +40 -27
  508. package/src/memory/v2/backfill-jobs.ts +17 -84
  509. package/src/memory/v2/consolidation-job.ts +92 -86
  510. package/src/memory/v2/frontmatter-sweep.ts +91 -0
  511. package/src/memory/v2/injection.ts +466 -115
  512. package/src/memory/v2/migration.ts +117 -20
  513. package/src/memory/v2/page-index.ts +191 -0
  514. package/src/memory/v2/page-store.ts +42 -0
  515. package/src/memory/v2/prompts/consolidation.ts +14 -7
  516. package/src/memory/v2/prompts/router.ts +192 -0
  517. package/src/memory/v2/qdrant.ts +307 -133
  518. package/src/memory/v2/reranker.ts +14 -7
  519. package/src/memory/v2/router.ts +322 -0
  520. package/src/memory/v2/sim.ts +88 -34
  521. package/src/memory/v2/skill-store.ts +118 -29
  522. package/src/memory/v2/static-context.ts +20 -17
  523. package/src/memory/v2/sweep-job.ts +127 -102
  524. package/src/memory/v2/types.ts +16 -5
  525. package/src/memory/validation.ts +13 -0
  526. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +182 -0
  527. package/src/notifications/__tests__/home-feed-side-effect.test.ts +199 -0
  528. package/src/notifications/__tests__/signal-registry.test.ts +17 -0
  529. package/src/notifications/adapters/platform.ts +171 -0
  530. package/src/notifications/conversation-pairing.ts +2 -2
  531. package/src/notifications/copy-composer.ts +61 -12
  532. package/src/notifications/decision-engine.ts +46 -0
  533. package/src/notifications/destination-resolver.ts +21 -0
  534. package/src/notifications/emit-signal.ts +28 -1
  535. package/src/notifications/home-feed-side-effect.ts +111 -0
  536. package/src/notifications/signal.ts +5 -0
  537. package/src/permissions/checker.ts +12 -0
  538. package/src/permissions/gateway-threshold-reader.ts +116 -8
  539. package/src/permissions/ipc-risk-types.ts +2 -0
  540. package/src/permissions/prompter.ts +86 -96
  541. package/src/permissions/secret-prompter.ts +31 -31
  542. package/src/plugin-api/index.ts +13 -0
  543. package/src/plugin-api/package.json +12 -0
  544. package/src/plugin-api/types.ts +62 -0
  545. package/src/plugins/defaults/injectors.ts +20 -5
  546. package/src/plugins/external-plugin-loader.ts +294 -0
  547. package/src/plugins/types.ts +46 -30
  548. package/src/plugins/user-loader.ts +64 -41
  549. package/src/proactive-artifact/job.test.ts +63 -8
  550. package/src/proactive-artifact/job.ts +20 -2
  551. package/src/proactive-artifact/message-copy.ts +18 -1
  552. package/src/proactive-artifact/trigger-state.test.ts +9 -0
  553. package/src/proactive-artifact/trigger-state.ts +4 -0
  554. package/src/prompts/__tests__/system-prompt.test.ts +105 -0
  555. package/src/prompts/system-prompt.ts +22 -1
  556. package/src/prompts/templates/SOUL.md +13 -28
  557. package/src/prompts/update-bulletin-job.ts +61 -73
  558. package/src/providers/__tests__/dispatch-connection-routing.test.ts +279 -0
  559. package/src/providers/__tests__/inference.test.ts +288 -0
  560. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  561. package/src/providers/__tests__/provider-secret-catalog.test.ts +6 -0
  562. package/src/providers/__tests__/retry-callsite.test.ts +14 -32
  563. package/src/providers/__tests__/satellite-connection-routing.test.ts +510 -0
  564. package/src/providers/__tests__/search-provider-catalog.test.ts +80 -0
  565. package/src/providers/anthropic/client.ts +95 -26
  566. package/src/providers/call-site-routing.ts +94 -16
  567. package/src/providers/connection-resolution.ts +163 -0
  568. package/src/providers/inference/__tests__/connections-status-label.test.ts +250 -0
  569. package/src/providers/inference/adapter-factory.ts +173 -0
  570. package/src/providers/inference/auth.ts +112 -0
  571. package/src/providers/inference/backfill.ts +196 -0
  572. package/src/providers/inference/connections.ts +356 -0
  573. package/src/providers/inference/resolve-auth.ts +65 -0
  574. package/src/providers/model-catalog.ts +104 -6
  575. package/src/providers/openai/responses-provider.ts +4 -2
  576. package/src/providers/provider-env-vars.ts +17 -7
  577. package/src/providers/provider-secret-catalog.ts +49 -30
  578. package/src/providers/provider-send-message.ts +41 -20
  579. package/src/providers/registry.ts +143 -159
  580. package/src/providers/retry.ts +18 -10
  581. package/src/providers/search-provider-catalog.ts +121 -0
  582. package/src/runtime/AGENTS.md +18 -5
  583. package/src/runtime/__tests__/background-job-runner.test.ts +357 -0
  584. package/src/runtime/__tests__/pre-first-message-gate.test.ts +82 -0
  585. package/src/runtime/actor-trust-resolver.ts +32 -10
  586. package/src/runtime/agent-wake.ts +35 -6
  587. package/src/runtime/assistant-event-hub.ts +3 -85
  588. package/src/runtime/auth/route-policy.ts +304 -8
  589. package/src/runtime/auth/same-actor.ts +2 -0
  590. package/src/runtime/background-job-runner.ts +339 -0
  591. package/src/runtime/btw-sidechain.ts +1 -0
  592. package/src/runtime/channel-approvals.ts +3 -2
  593. package/src/runtime/guardian-reply-router.ts +0 -10
  594. package/src/runtime/http-router.ts +36 -1
  595. package/src/runtime/http-server.ts +31 -5
  596. package/src/runtime/http-types.ts +2 -0
  597. package/src/runtime/middleware/__tests__/request-logger.test.ts +162 -0
  598. package/src/runtime/middleware/request-logger.ts +62 -1
  599. package/src/runtime/pending-interactions.ts +19 -15
  600. package/src/runtime/pre-first-message-gate.ts +83 -0
  601. package/src/runtime/routes/__tests__/backup-routes.test.ts +8 -1
  602. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +251 -0
  603. package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +142 -0
  604. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +315 -0
  605. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +189 -0
  606. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +15 -136
  607. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +736 -0
  608. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +147 -0
  609. package/src/runtime/routes/__tests__/stt-routes.test.ts +5 -1
  610. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +384 -0
  611. package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
  612. package/src/runtime/routes/acp-routes.ts +10 -8
  613. package/src/runtime/routes/app-management-routes.ts +228 -3
  614. package/src/runtime/routes/approval-routes.ts +7 -21
  615. package/src/runtime/routes/audit-routes.ts +43 -0
  616. package/src/runtime/routes/auth-routes.ts +72 -0
  617. package/src/runtime/routes/avatar-routes.ts +273 -20
  618. package/src/runtime/routes/backup-routes.ts +406 -2
  619. package/src/runtime/routes/bookmark-routes.ts +154 -0
  620. package/src/runtime/routes/channel-verification-routes.ts +2 -1
  621. package/src/runtime/routes/consolidation-routes.ts +8 -9
  622. package/src/runtime/routes/contact-routes.ts +0 -160
  623. package/src/runtime/routes/conversation-cli-routes.ts +192 -0
  624. package/src/runtime/routes/conversation-management-routes.ts +30 -43
  625. package/src/runtime/routes/conversation-query-routes.ts +373 -82
  626. package/src/runtime/routes/conversation-routes.ts +31 -10
  627. package/src/runtime/routes/conversations-import-routes.ts +229 -0
  628. package/src/runtime/routes/credential-routes.ts +540 -0
  629. package/src/runtime/routes/debug-bash-routes.ts +2 -0
  630. package/src/runtime/routes/debug-routes.ts +2 -2
  631. package/src/runtime/routes/document-pdf-renderer.ts +5 -1
  632. package/src/runtime/routes/domain-routes.ts +167 -0
  633. package/src/runtime/routes/email-routes.ts +603 -0
  634. package/src/runtime/routes/errors.ts +2 -2
  635. package/src/runtime/routes/events-routes.ts +192 -0
  636. package/src/runtime/routes/filing-routes.ts +2 -3
  637. package/src/runtime/routes/home-feed-routes.ts +6 -78
  638. package/src/runtime/routes/host-app-control-routes.ts +44 -2
  639. package/src/runtime/routes/host-browser-routes.ts +103 -22
  640. package/src/runtime/routes/http-adapter.ts +2 -0
  641. package/src/runtime/routes/identity-routes.ts +5 -0
  642. package/src/runtime/routes/image-generation-routes.ts +99 -0
  643. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +137 -1
  644. package/src/runtime/routes/inbound-stages/background-dispatch.ts +87 -7
  645. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +156 -0
  646. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +22 -7
  647. package/src/runtime/routes/index.ts +36 -0
  648. package/src/runtime/routes/inference-profile-session-handler.ts +312 -0
  649. package/src/runtime/routes/inference-profile-session-reaper.ts +98 -0
  650. package/src/runtime/routes/inference-profile-session-routes.ts +146 -0
  651. package/src/runtime/routes/inference-provider-connection-routes.ts +317 -0
  652. package/src/runtime/routes/inference-send-routes.ts +115 -0
  653. package/src/runtime/routes/integrations/twilio.ts +1 -0
  654. package/src/runtime/routes/mcp-auth-routes.ts +283 -9
  655. package/src/runtime/routes/memory-item-routes.test.ts +3 -9
  656. package/src/runtime/routes/memory-item-routes.ts +5 -6
  657. package/src/runtime/routes/memory-v2-routes.ts +105 -404
  658. package/src/runtime/routes/notification-routes.ts +2 -0
  659. package/src/runtime/routes/oauth-apps.ts +112 -7
  660. package/src/runtime/routes/oauth-commands-routes.ts +1007 -0
  661. package/src/runtime/routes/oauth-connect-routes.ts +67 -5
  662. package/src/runtime/routes/oauth-providers.ts +298 -8
  663. package/src/runtime/routes/platform-routes.ts +336 -0
  664. package/src/runtime/routes/playground/inject-failures.ts +2 -1
  665. package/src/runtime/routes/playground/reset-circuit.ts +2 -1
  666. package/src/runtime/routes/playground/state.ts +2 -1
  667. package/src/runtime/routes/publish-routes.ts +221 -0
  668. package/src/runtime/routes/schedule-routes.ts +82 -0
  669. package/src/runtime/routes/sequence-routes.ts +291 -0
  670. package/src/runtime/routes/settings-routes.ts +2 -10
  671. package/src/runtime/routes/skills-routes.ts +31 -1
  672. package/src/runtime/routes/stt-routes.ts +240 -3
  673. package/src/runtime/routes/surface-action-routes.ts +43 -7
  674. package/src/runtime/routes/tts-routes.ts +67 -0
  675. package/src/runtime/routes/types.ts +32 -0
  676. package/src/runtime/routes/user-routes-cli.ts +243 -0
  677. package/src/runtime/routes/webhook-routes.ts +165 -0
  678. package/src/runtime/sync/resource-sync-events.ts +25 -0
  679. package/src/runtime/sync/sync-publisher.test.ts +105 -0
  680. package/src/runtime/sync/sync-publisher.ts +21 -0
  681. package/src/schedule/scheduler.ts +200 -123
  682. package/src/security/__tests__/provider-key-env-fallback.test.ts +12 -6
  683. package/src/security/secret-patterns.ts +3 -0
  684. package/src/sequence/engine.ts +38 -40
  685. package/src/skills/include-graph.ts +35 -13
  686. package/src/subagent/manager.ts +20 -15
  687. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +206 -0
  688. package/src/tools/browser/browser-execution.ts +15 -4
  689. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +174 -0
  690. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +16 -13
  691. package/src/tools/browser/cdp-client/extension-cdp-client.ts +24 -1
  692. package/src/tools/browser/cdp-client/factory.ts +66 -5
  693. package/src/tools/browser/runtime-check.ts +77 -0
  694. package/src/tools/document/document-tool.ts +20 -0
  695. package/src/tools/executor.ts +18 -2
  696. package/src/tools/memory/register.test.ts +10 -8
  697. package/src/tools/memory/register.ts +9 -1
  698. package/src/tools/network/__tests__/web-search.test.ts +156 -0
  699. package/src/tools/network/web-search.ts +280 -37
  700. package/src/tools/permission-checker.ts +28 -5
  701. package/src/tools/skills/load.ts +24 -20
  702. package/src/tools/subagent/spawn.ts +3 -3
  703. package/src/tools/terminal/shell.ts +44 -0
  704. package/src/tools/tool-name-aliases.ts +19 -0
  705. package/src/tools/types.ts +19 -1
  706. package/src/usage/attribution.ts +3 -2
  707. package/src/util/pricing.ts +86 -160
  708. package/src/watcher/__tests__/engine.test.ts +301 -0
  709. package/src/watcher/constants.ts +7 -0
  710. package/src/watcher/engine.ts +90 -90
  711. package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +6 -9
  712. package/src/workspace/migrations/054-seed-recall-callsite.ts +10 -1
  713. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +28 -4
  714. package/src/workspace/migrations/067-release-notes-safe-storage-limits.ts +4 -62
  715. package/src/workspace/migrations/069-seed-onboarding-threads.ts +34 -0
  716. package/src/workspace/migrations/070-memory-v2-summary-schema-rebuild.ts +31 -0
  717. package/src/workspace/migrations/071-remove-safe-storage-release-note.ts +111 -0
  718. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +104 -0
  719. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +93 -0
  720. package/src/workspace/migrations/074-drop-deprecated-secret-detection-keys.ts +117 -0
  721. package/src/workspace/migrations/075-memory-v2-bm25-b-default-reembed.ts +61 -0
  722. package/src/workspace/migrations/076-drop-services-inference-mode.ts +62 -0
  723. package/src/workspace/migrations/077-seed-memory-router-callsite.ts +89 -0
  724. package/src/workspace/migrations/078-release-notes-tavily-web-search.ts +66 -0
  725. package/src/workspace/migrations/079-home-feed-notification-only.ts +197 -0
  726. package/src/workspace/migrations/080-restrict-vercel-api-token-metadata.ts +182 -0
  727. package/src/workspace/migrations/081-backfill-bash-allowed-tools-for-injection-credentials.ts +160 -0
  728. package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +154 -0
  729. package/src/workspace/migrations/registry.ts +28 -0
  730. package/src/workspace/migrations/runner.ts +13 -2
  731. package/src/workspace/migrations/types.ts +13 -3
  732. package/src/workspace/provider-commit-message-generator.ts +3 -2
  733. package/src/__tests__/context-search-pkb-source.test.ts +0 -492
  734. package/src/__tests__/credentials-cli.test.ts +0 -1225
  735. package/src/__tests__/memory-admin-recall.test.ts +0 -213
  736. package/src/approvals/__tests__/guardian-feed-event.test.ts +0 -303
  737. package/src/cli/commands/__tests__/email-download.test.ts +0 -260
  738. package/src/cli/commands/__tests__/email-list.test.ts +0 -216
  739. package/src/cli/commands/__tests__/email-register.test.ts +0 -186
  740. package/src/cli/commands/__tests__/email-send.test.ts +0 -416
  741. package/src/cli/commands/__tests__/email-status.test.ts +0 -185
  742. package/src/cli/commands/__tests__/email-unregister.test.ts +0 -168
  743. package/src/cli/commands/__tests__/routes.test.ts +0 -562
  744. package/src/cli/commands/__tests__/stt-transcribe.test.ts +0 -454
  745. package/src/cli/commands/autonomy.ts +0 -365
  746. package/src/cli/commands/memory.ts +0 -424
  747. package/src/cli/commands/oauth/__tests__/connect.test.ts +0 -1201
  748. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +0 -686
  749. package/src/cli/commands/oauth/__tests__/mode.test.ts +0 -632
  750. package/src/cli/commands/oauth/__tests__/ping.test.ts +0 -631
  751. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +0 -573
  752. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +0 -330
  753. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +0 -521
  754. package/src/cli/commands/oauth/__tests__/status.test.ts +0 -551
  755. package/src/cli/commands/oauth/__tests__/token.test.ts +0 -420
  756. package/src/cli/lib/daemon-avatar-client.ts +0 -37
  757. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -87
  758. package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +0 -207
  759. package/src/daemon/__tests__/conversation-feed-event.test.ts +0 -304
  760. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +0 -233
  761. package/src/home/__tests__/assistant-feed-authoring.test.ts +0 -156
  762. package/src/home/__tests__/emit-feed-event.test.ts +0 -169
  763. package/src/home/__tests__/feed-population-integration.test.ts +0 -312
  764. package/src/home/__tests__/feed-scheduler.test.ts +0 -222
  765. package/src/home/__tests__/phase5-exit-criteria.test.ts +0 -229
  766. package/src/home/__tests__/platform-gmail-digest.test.ts +0 -222
  767. package/src/home/__tests__/rollup-producer.test.ts +0 -507
  768. package/src/home/assistant-feed-authoring.ts +0 -135
  769. package/src/home/emit-feed-event.ts +0 -169
  770. package/src/home/feed-scheduler.ts +0 -281
  771. package/src/home/platform-gmail-digest.ts +0 -163
  772. package/src/home/rewrite-command-preview.ts +0 -66
  773. package/src/home/rewrite-feed-title.ts +0 -58
  774. package/src/home/rollup-producer.ts +0 -426
  775. package/src/memory/admin.ts +0 -326
  776. package/src/memory/context-search/sources/pkb.ts +0 -477
  777. package/src/memory/graph/compaction.ts +0 -299
  778. /package/src/cli/{commands → lib}/cache-fs.ts +0 -0
@@ -0,0 +1,1007 @@
1
+ /**
2
+ * Route handlers for OAuth CLI command operations: disconnect, mode, status,
3
+ * ping, token, and request.
4
+ *
5
+ * These routes back the thin IPC wrappers in assistant/src/cli/commands/oauth/.
6
+ */
7
+
8
+ import { readFileSync } from "node:fs";
9
+
10
+ import { getConfig, loadRawConfig, saveRawConfig, setNestedValue } from "../../config/loader.js";
11
+ import {
12
+ getServiceMode,
13
+ type Services,
14
+ ServicesSchema,
15
+ } from "../../config/schemas/services.js";
16
+ import type { OAuthConnectionRequest } from "../../oauth/connection.js";
17
+ import {
18
+ resolveOAuthConnection,
19
+ type ResolveOAuthConnectionOptions,
20
+ } from "../../oauth/connection-resolver.js";
21
+ import {
22
+ disconnectOAuthProvider,
23
+ getActiveConnection,
24
+ getAppByProviderAndClientId,
25
+ getConnection,
26
+ getProvider,
27
+ listActiveConnectionsByProvider,
28
+ listConnections,
29
+ } from "../../oauth/oauth-store.js";
30
+ import { VellumPlatformClient } from "../../platform/client.js";
31
+ import { withValidToken } from "../../security/token-manager.js";
32
+ import { getLogger } from "../../util/logger.js";
33
+ import { BadRequestError, InternalError, NotFoundError } from "./errors.js";
34
+ import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
35
+
36
+ const log = getLogger("oauth-commands-routes");
37
+
38
+ // ---------------------------------------------------------------------------
39
+ // Shared helpers
40
+ // ---------------------------------------------------------------------------
41
+
42
+ interface PlatformConnectionEntry {
43
+ id: string;
44
+ account_label?: string;
45
+ scopes_granted?: string[];
46
+ status?: string;
47
+ }
48
+
49
+ function getManagedServiceConfigKey(provider: string): string | null {
50
+ const providerRow = getProvider(provider);
51
+ const managedKey = providerRow?.managedServiceConfigKey;
52
+ if (!managedKey || !(managedKey in ServicesSchema.shape)) return null;
53
+ return managedKey;
54
+ }
55
+
56
+ function isManagedMode(provider: string): boolean {
57
+ const managedKey = getManagedServiceConfigKey(provider);
58
+ if (!managedKey) return false;
59
+ try {
60
+ const services: Services = getConfig().services;
61
+ return getServiceMode(services, managedKey as keyof Services) === "managed";
62
+ } catch {
63
+ return false;
64
+ }
65
+ }
66
+
67
+ async function requirePlatformClient(): Promise<VellumPlatformClient> {
68
+ const client = await VellumPlatformClient.create();
69
+ if (!client) {
70
+ throw new BadRequestError(
71
+ "Not connected to Vellum platform. Run `vellum platform connect` to connect first.",
72
+ );
73
+ }
74
+ if (!client.platformAssistantId) {
75
+ throw new BadRequestError(
76
+ "Connected to Vellum platform but no assistant ID is configured. Ensure the assistant is registered on the platform.",
77
+ );
78
+ }
79
+ return client;
80
+ }
81
+
82
+ async function fetchActiveConnections(
83
+ client: VellumPlatformClient,
84
+ provider: string,
85
+ ): Promise<PlatformConnectionEntry[]> {
86
+ const params = new URLSearchParams();
87
+ params.set("provider", provider);
88
+ params.set("status", "ACTIVE");
89
+
90
+ const path = `/v1/assistants/${encodeURIComponent(client.platformAssistantId)}/oauth/connections/?${params.toString()}`;
91
+ const response = await client.fetch(path);
92
+
93
+ if (!response.ok) {
94
+ const hint =
95
+ response.status === 401 || response.status === 403
96
+ ? `. Your platform session may have expired. Run \`vellum platform connect\` to reconnect.`
97
+ : "";
98
+ throw new InternalError(`Platform returned HTTP ${response.status}${hint}`);
99
+ }
100
+
101
+ const body = (await response.json()) as unknown;
102
+ return (
103
+ Array.isArray(body)
104
+ ? body
105
+ : ((body as Record<string, unknown>).results ?? [])
106
+ ) as PlatformConnectionEntry[];
107
+ }
108
+
109
+ /**
110
+ * Best-effort helper to count active platform connections for a provider.
111
+ * Returns 0 if the platform client cannot be created or the fetch fails.
112
+ */
113
+ async function countManagedConnections(provider: string): Promise<number> {
114
+ try {
115
+ const client = await VellumPlatformClient.create();
116
+ if (!client || !client.platformAssistantId) return 0;
117
+ const entries = await fetchActiveConnections(client, provider);
118
+ return entries.length;
119
+ } catch {
120
+ return 0;
121
+ }
122
+ }
123
+
124
+ // ---------------------------------------------------------------------------
125
+ // Disconnect handler
126
+ // ---------------------------------------------------------------------------
127
+
128
+ async function handleDisconnect({ body = {} }: RouteHandlerArgs) {
129
+ const b = body as {
130
+ provider: string;
131
+ account?: string;
132
+ connection_id?: string;
133
+ };
134
+
135
+ if (!b.provider) throw new BadRequestError("provider is required");
136
+
137
+ const providerRow = getProvider(b.provider);
138
+ if (!providerRow) {
139
+ throw new NotFoundError(
140
+ `Unknown provider "${b.provider}". Run 'assistant oauth providers list' to see available providers.`,
141
+ );
142
+ }
143
+
144
+ if (b.account && b.connection_id) {
145
+ throw new BadRequestError(
146
+ `Cannot specify both account and connection_id. Use one or the other.`,
147
+ );
148
+ }
149
+
150
+ const managed = isManagedMode(b.provider);
151
+
152
+ if (managed) {
153
+ const client = await requirePlatformClient();
154
+ const entries = await fetchActiveConnections(client, b.provider);
155
+
156
+ let connectionId: string | undefined;
157
+ let accountLabel: string | undefined;
158
+
159
+ if (b.account) {
160
+ const matching = entries.filter((c) => c.account_label === b.account);
161
+ if (matching.length === 0) {
162
+ throw new NotFoundError(
163
+ `No active connection found for "${b.provider}" with account "${b.account}".`,
164
+ );
165
+ }
166
+ connectionId = matching[0].id;
167
+ accountLabel = matching[0].account_label;
168
+ } else if (b.connection_id) {
169
+ const match = entries.find((c) => c.id === b.connection_id);
170
+ if (!match) {
171
+ throw new NotFoundError(
172
+ `Connection "${b.connection_id}" is not an active ${b.provider} connection.`,
173
+ );
174
+ }
175
+ connectionId = match.id;
176
+ accountLabel = match.account_label;
177
+ } else {
178
+ if (entries.length === 0) {
179
+ throw new NotFoundError(`No active connections found for "${b.provider}".`);
180
+ }
181
+ if (entries.length > 1) {
182
+ throw new BadRequestError(
183
+ `Multiple active connections for "${b.provider}". Specify which one to disconnect with account or connection_id. ` +
184
+ `Run 'assistant oauth status ${b.provider}' to see connected accounts and IDs.`,
185
+ );
186
+ }
187
+ connectionId = entries[0].id;
188
+ accountLabel = entries[0].account_label;
189
+ }
190
+
191
+ const disconnectPath = `/v1/assistants/${encodeURIComponent(client.platformAssistantId)}/oauth/connections/${encodeURIComponent(connectionId!)}/disconnect/`;
192
+ const disconnectResponse = await client.fetch(disconnectPath, {
193
+ method: "POST",
194
+ headers: { "Content-Type": "application/json" },
195
+ });
196
+
197
+ if (!disconnectResponse.ok) {
198
+ const errorText = await disconnectResponse.text().catch(() => "");
199
+ throw new InternalError(
200
+ `Platform returned HTTP ${disconnectResponse.status}${errorText ? `: ${errorText}` : ""}`,
201
+ );
202
+ }
203
+
204
+ const result: Record<string, unknown> = {
205
+ ok: true,
206
+ provider: b.provider,
207
+ connectionId,
208
+ };
209
+ if (accountLabel) result.account = accountLabel;
210
+ return result;
211
+ }
212
+
213
+ // BYO path
214
+ let connectionId: string | undefined;
215
+ let accountLabel: string | undefined;
216
+
217
+ if (b.account) {
218
+ const conn = getActiveConnection(b.provider, { account: b.account });
219
+ if (!conn) {
220
+ throw new NotFoundError(
221
+ `No active connection found for "${b.provider}" with account "${b.account}".`,
222
+ );
223
+ }
224
+ connectionId = conn.id;
225
+ accountLabel = conn.accountInfo ?? undefined;
226
+ } else if (b.connection_id) {
227
+ const conn = getConnection(b.connection_id);
228
+ if (!conn || conn.provider !== b.provider) {
229
+ throw new NotFoundError(
230
+ `Connection "${b.connection_id}" is not an active ${b.provider} connection.`,
231
+ );
232
+ }
233
+ connectionId = conn.id;
234
+ accountLabel = conn.accountInfo ?? undefined;
235
+ } else {
236
+ const active = listActiveConnectionsByProvider(b.provider);
237
+ if (active.length === 0) {
238
+ throw new NotFoundError(`No active connections found for "${b.provider}".`);
239
+ }
240
+ if (active.length > 1) {
241
+ throw new BadRequestError(
242
+ `Multiple active connections for "${b.provider}". Specify which one to disconnect with account or connection_id. ` +
243
+ `Run 'assistant oauth status ${b.provider}' to see connected accounts and IDs.`,
244
+ );
245
+ }
246
+ connectionId = active[0].id;
247
+ accountLabel = active[0].accountInfo ?? undefined;
248
+ }
249
+
250
+ const oauthResult = await disconnectOAuthProvider(
251
+ b.provider,
252
+ undefined,
253
+ connectionId,
254
+ );
255
+ if (oauthResult === "error") {
256
+ throw new InternalError(
257
+ `Failed to disconnect OAuth provider "${b.provider}" — please try again.`,
258
+ );
259
+ }
260
+
261
+ const result: Record<string, unknown> = {
262
+ ok: true,
263
+ provider: b.provider,
264
+ connectionId,
265
+ };
266
+ if (accountLabel) result.account = accountLabel;
267
+ return result;
268
+ }
269
+
270
+ // ---------------------------------------------------------------------------
271
+ // Mode handlers
272
+ // ---------------------------------------------------------------------------
273
+
274
+ function handleModeGet({ queryParams = {} }: RouteHandlerArgs) {
275
+ const provider = queryParams.provider;
276
+ if (!provider) throw new BadRequestError("provider query param is required");
277
+
278
+ const providerRow = getProvider(provider);
279
+ if (!providerRow) {
280
+ throw new NotFoundError(
281
+ `Unknown provider "${provider}". Run 'assistant oauth providers list' to see available providers.`,
282
+ );
283
+ }
284
+
285
+ const managedKey = getManagedServiceConfigKey(provider);
286
+ if (managedKey === null) {
287
+ return {
288
+ ok: true,
289
+ provider,
290
+ mode: "your-own",
291
+ managedModeSupported: false,
292
+ };
293
+ }
294
+
295
+ const services: Services = getConfig().services;
296
+ const currentMode = getServiceMode(services, managedKey as keyof Services);
297
+
298
+ return {
299
+ ok: true,
300
+ provider,
301
+ mode: currentMode,
302
+ managedModeSupported: true,
303
+ };
304
+ }
305
+
306
+ async function handleModeSet({ body = {} }: RouteHandlerArgs) {
307
+ const b = body as { provider: string; mode: string };
308
+ if (!b.provider) throw new BadRequestError("provider is required");
309
+ if (!b.mode) throw new BadRequestError("mode is required");
310
+
311
+ const providerRow = getProvider(b.provider);
312
+ if (!providerRow) {
313
+ throw new NotFoundError(
314
+ `Unknown provider "${b.provider}". Run 'assistant oauth providers list' to see available providers.`,
315
+ );
316
+ }
317
+
318
+ if (b.mode !== "managed" && b.mode !== "your-own") {
319
+ throw new BadRequestError(
320
+ `Invalid mode "${b.mode}". Valid values are "managed" or "your-own".`,
321
+ );
322
+ }
323
+
324
+ const managedKey = getManagedServiceConfigKey(b.provider);
325
+
326
+ if (managedKey === null) {
327
+ if (b.mode === "your-own") {
328
+ return {
329
+ ok: true,
330
+ provider: b.provider,
331
+ mode: "your-own",
332
+ changed: false,
333
+ managedModeSupported: false,
334
+ };
335
+ }
336
+ throw new BadRequestError(
337
+ `Managed mode is not available for ${b.provider}. Only providers with platform-managed OAuth support can be switched to managed mode.`,
338
+ );
339
+ }
340
+
341
+ // Require platform connection when switching to managed mode
342
+ if (b.mode === "managed") {
343
+ const client = await VellumPlatformClient.create();
344
+ if (!client) {
345
+ throw new BadRequestError(
346
+ "Not connected to Vellum platform. Run `vellum platform connect` to connect first.",
347
+ );
348
+ }
349
+ }
350
+
351
+ const services: Services = getConfig().services;
352
+ const currentMode = getServiceMode(services, managedKey as keyof Services);
353
+
354
+ if (currentMode === b.mode) {
355
+ return {
356
+ ok: true,
357
+ provider: b.provider,
358
+ mode: b.mode,
359
+ changed: false,
360
+ managedModeSupported: true,
361
+ };
362
+ }
363
+
364
+ const raw = loadRawConfig();
365
+ setNestedValue(raw, `services.${managedKey}.mode`, b.mode);
366
+ saveRawConfig(raw);
367
+
368
+ // Best-effort check for active connections on old and new modes
369
+ let oldModeConnections = 0;
370
+ let newModeConnections = 0;
371
+ if (currentMode === "managed") {
372
+ oldModeConnections = await countManagedConnections(b.provider);
373
+ newModeConnections = listActiveConnectionsByProvider(b.provider).length;
374
+ } else {
375
+ oldModeConnections = listActiveConnectionsByProvider(b.provider).length;
376
+ newModeConnections = await countManagedConnections(b.provider);
377
+ }
378
+
379
+ let hint: string | undefined;
380
+ if (oldModeConnections > 0 && newModeConnections === 0) {
381
+ hint = `No active connections in ${b.mode} mode. Run 'assistant oauth connect ${b.provider}' to connect.`;
382
+ }
383
+
384
+ const result: Record<string, unknown> = {
385
+ ok: true,
386
+ provider: b.provider,
387
+ mode: b.mode,
388
+ changed: true,
389
+ managedModeSupported: true,
390
+ };
391
+ if (hint) result.hint = hint;
392
+ return result;
393
+ }
394
+
395
+ // ---------------------------------------------------------------------------
396
+ // Status handler
397
+ // ---------------------------------------------------------------------------
398
+
399
+ async function handleStatus({ queryParams = {} }: RouteHandlerArgs) {
400
+ const provider = queryParams.provider;
401
+ if (!provider) throw new BadRequestError("provider query param is required");
402
+
403
+ const providerRow = getProvider(provider);
404
+ if (!providerRow) {
405
+ throw new NotFoundError(
406
+ `Unknown provider "${provider}". Run 'assistant oauth providers list' to see available providers.`,
407
+ );
408
+ }
409
+
410
+ const managed = isManagedMode(provider);
411
+
412
+ if (managed) {
413
+ const client = await requirePlatformClient();
414
+ const rawEntries = await fetchActiveConnections(client, provider);
415
+
416
+ const connections = rawEntries.map((c) => ({
417
+ id: c.id,
418
+ account: c.account_label ?? null,
419
+ grantedScopes: c.scopes_granted ?? [],
420
+ status: c.status ?? "ACTIVE",
421
+ }));
422
+
423
+ return {
424
+ ok: true,
425
+ provider,
426
+ mode: "managed",
427
+ connections,
428
+ };
429
+ }
430
+
431
+ // BYO path
432
+ const allConnections = listConnections(provider);
433
+ const activeRows = allConnections.filter((r) => r.status === "active");
434
+
435
+ const connections = activeRows.map((r) => {
436
+ let grantedScopes: string[] = [];
437
+ try {
438
+ grantedScopes = r.grantedScopes ? JSON.parse(r.grantedScopes) : [];
439
+ } catch {
440
+ // Malformed JSON — default to empty
441
+ }
442
+
443
+ return {
444
+ id: r.id,
445
+ account: r.accountInfo ?? null,
446
+ grantedScopes,
447
+ expiresAt: r.expiresAt ? new Date(r.expiresAt).toISOString() : null,
448
+ hasRefreshToken: r.hasRefreshToken === 1,
449
+ status: r.status,
450
+ };
451
+ });
452
+
453
+ return {
454
+ ok: true,
455
+ provider,
456
+ mode: "byo",
457
+ connections,
458
+ };
459
+ }
460
+
461
+ // ---------------------------------------------------------------------------
462
+ // Ping handler
463
+ // ---------------------------------------------------------------------------
464
+
465
+ async function handlePing({ body = {} }: RouteHandlerArgs) {
466
+ const b = body as {
467
+ provider: string;
468
+ account?: string;
469
+ client_id?: string;
470
+ };
471
+
472
+ if (!b.provider) throw new BadRequestError("provider is required");
473
+
474
+ const providerRow = getProvider(b.provider);
475
+ if (!providerRow) {
476
+ throw new NotFoundError(
477
+ `Unknown provider "${b.provider}". Run 'assistant oauth providers list' to see available providers.`,
478
+ );
479
+ }
480
+
481
+ if (!providerRow.pingUrl) {
482
+ throw new BadRequestError(
483
+ `No ping URL configured for "${b.provider}". Register one with 'assistant oauth providers register --ping-url <url>'.`,
484
+ );
485
+ }
486
+
487
+ const pingUrl = providerRow.pingUrl as string;
488
+ const parsed = new URL(pingUrl);
489
+ const baseUrl = `${parsed.protocol}//${parsed.host}`;
490
+ const path = parsed.pathname;
491
+
492
+ const query: Record<string, string> = {};
493
+ for (const [key, value] of parsed.searchParams) {
494
+ query[key] = value;
495
+ }
496
+
497
+ const resolveOptions: ResolveOAuthConnectionOptions = {};
498
+ if (b.account) resolveOptions.account = b.account;
499
+ if (b.client_id) resolveOptions.clientId = b.client_id;
500
+
501
+ const connection = await resolveOAuthConnection(b.provider, resolveOptions);
502
+
503
+ const method = (providerRow.pingMethod as string | null) ?? "GET";
504
+
505
+ const pingHeaders: Record<string, string> = providerRow.pingHeaders
506
+ ? JSON.parse(providerRow.pingHeaders as string)
507
+ : {};
508
+
509
+ const pingBody: unknown = providerRow.pingBody
510
+ ? JSON.parse(providerRow.pingBody as string)
511
+ : undefined;
512
+
513
+ const response = await connection.request({
514
+ method,
515
+ path,
516
+ baseUrl,
517
+ ...(Object.keys(query).length > 0 ? { query } : {}),
518
+ ...(Object.keys(pingHeaders).length > 0 ? { headers: pingHeaders } : {}),
519
+ ...(pingBody !== undefined ? { body: pingBody } : {}),
520
+ });
521
+
522
+ if (response.status >= 200 && response.status < 300) {
523
+ return { ok: true, provider: b.provider, status: response.status };
524
+ }
525
+
526
+ const payload: Record<string, unknown> = {
527
+ ok: false,
528
+ provider: b.provider,
529
+ status: response.status,
530
+ error: `Ping failed with HTTP ${response.status}`,
531
+ };
532
+
533
+ if (response.status === 401 || response.status === 403) {
534
+ payload.hint =
535
+ `Run 'assistant oauth status ${b.provider}' to check connection health. ` +
536
+ `To reconnect, run 'assistant oauth connect --help'.`;
537
+ }
538
+
539
+ return payload;
540
+ }
541
+
542
+ // ---------------------------------------------------------------------------
543
+ // Token handler
544
+ // ---------------------------------------------------------------------------
545
+
546
+ async function handleToken({ body = {} }: RouteHandlerArgs) {
547
+ const b = body as {
548
+ provider: string;
549
+ account?: string;
550
+ client_id?: string;
551
+ };
552
+
553
+ if (!b.provider) throw new BadRequestError("provider is required");
554
+
555
+ if (isManagedMode(b.provider)) {
556
+ throw new BadRequestError(
557
+ "Token retrieval is not supported for platform-managed providers. " +
558
+ "When a provider is in managed mode, Vellum handles OAuth tokens on your behalf — " +
559
+ "they are not exposed directly.\n\n" +
560
+ `To verify your connection is working, run 'assistant oauth ping ${b.provider}'.\n` +
561
+ `To make authenticated requests, use 'assistant oauth request --provider ${b.provider} <url>'.`,
562
+ );
563
+ }
564
+
565
+ let tokenOpts: string | { connectionId: string } | undefined;
566
+
567
+ if (b.account || b.client_id) {
568
+ const conn = getActiveConnection(b.provider, {
569
+ clientId: b.client_id,
570
+ account: b.account,
571
+ });
572
+ if (!conn) {
573
+ const hint = b.account
574
+ ? ` for account "${b.account}"`
575
+ : b.client_id
576
+ ? ` with client ID "${b.client_id}"`
577
+ : "";
578
+ throw new NotFoundError(
579
+ `No active connection found for "${b.provider}"${hint}. Connect first with 'assistant oauth connect ${b.provider}'.`,
580
+ );
581
+ }
582
+ tokenOpts = { connectionId: conn.id };
583
+ }
584
+
585
+ const token = await withValidToken(
586
+ b.provider,
587
+ async (t) => t,
588
+ tokenOpts,
589
+ );
590
+
591
+ return { ok: true, token };
592
+ }
593
+
594
+ // ---------------------------------------------------------------------------
595
+ // Request handler
596
+ // ---------------------------------------------------------------------------
597
+
598
+ function tryJsonParse(raw: string): unknown {
599
+ try {
600
+ return JSON.parse(raw);
601
+ } catch {
602
+ return raw;
603
+ }
604
+ }
605
+
606
+ function readBodyData(data: string): unknown {
607
+ if (data === "@-") {
608
+ const raw = readFileSync("/dev/stdin", "utf-8");
609
+ return tryJsonParse(raw);
610
+ }
611
+
612
+ if (data.startsWith("@")) {
613
+ const filePath = data.slice(1);
614
+ const raw = readFileSync(filePath, "utf-8");
615
+ return tryJsonParse(raw);
616
+ }
617
+
618
+ return tryJsonParse(data);
619
+ }
620
+
621
+ async function handleRequest({ body = {} }: RouteHandlerArgs) {
622
+ const b = body as {
623
+ provider: string;
624
+ url: string;
625
+ method?: string;
626
+ headers?: Record<string, string>;
627
+ /** Pre-parsed body data (file/stdin reading happens CLI-side). */
628
+ parsed_data?: unknown;
629
+ /** Raw data string (for direct API callers, not the CLI). */
630
+ data?: string;
631
+ force_get?: boolean;
632
+ head?: boolean;
633
+ account?: string;
634
+ client_id?: string;
635
+ };
636
+
637
+ if (!b.provider) throw new BadRequestError("provider is required");
638
+ if (!b.url) throw new BadRequestError("url is required");
639
+
640
+ const providerRow = getProvider(b.provider);
641
+ if (!providerRow) {
642
+ throw new NotFoundError(
643
+ `Unknown provider "${b.provider}". Run 'assistant oauth providers list' to see available providers.`,
644
+ );
645
+ }
646
+
647
+ const managed = isManagedMode(b.provider);
648
+
649
+ if (b.client_id) {
650
+ if (managed) {
651
+ log.info("--client-id is ignored for platform-managed providers");
652
+ } else {
653
+ const app = getAppByProviderAndClientId(b.provider, b.client_id);
654
+ if (!app) {
655
+ throw new NotFoundError(
656
+ `No registered OAuth app found for "${b.provider}" with client ID "${b.client_id}".`,
657
+ );
658
+ }
659
+ }
660
+ }
661
+
662
+ // Parse URL
663
+ let baseUrl: string | undefined;
664
+ let requestPath: string;
665
+ const queryFromUrl: Record<string, string | string[]> = {};
666
+
667
+ if (b.url.startsWith("http://") || b.url.startsWith("https://")) {
668
+ const parsed = new URL(b.url);
669
+ baseUrl = `${parsed.protocol}//${parsed.host}`;
670
+ requestPath = parsed.pathname;
671
+ for (const [key, value] of parsed.searchParams.entries()) {
672
+ const existing = queryFromUrl[key];
673
+ if (existing !== undefined) {
674
+ queryFromUrl[key] = Array.isArray(existing)
675
+ ? [...existing, value]
676
+ : [existing, value];
677
+ } else {
678
+ queryFromUrl[key] = value;
679
+ }
680
+ }
681
+ } else {
682
+ const qIdx = b.url.indexOf("?");
683
+ if (qIdx !== -1) {
684
+ requestPath = b.url.slice(0, qIdx);
685
+ const embeddedParams = new URLSearchParams(b.url.slice(qIdx + 1));
686
+ for (const [key, value] of embeddedParams.entries()) {
687
+ const existing = queryFromUrl[key];
688
+ if (existing !== undefined) {
689
+ queryFromUrl[key] = Array.isArray(existing)
690
+ ? [...existing, value]
691
+ : [existing, value];
692
+ } else {
693
+ queryFromUrl[key] = value;
694
+ }
695
+ }
696
+ } else {
697
+ requestPath = b.url;
698
+ }
699
+ }
700
+
701
+ // Resolve method
702
+ let method: string;
703
+ if (b.head) {
704
+ method = "HEAD";
705
+ } else if (b.method) {
706
+ method = b.method.toUpperCase();
707
+ } else if (b.force_get) {
708
+ method = "GET";
709
+ } else if (b.data !== undefined || b.parsed_data !== undefined) {
710
+ method = "POST";
711
+ } else {
712
+ method = "GET";
713
+ }
714
+
715
+ // Handle body / query params
716
+ let reqBody: unknown = undefined;
717
+ const query: Record<string, string | string[]> = { ...queryFromUrl };
718
+
719
+ // Use pre-parsed data from CLI, or fall back to raw data string for direct API callers
720
+ const resolvedData = b.parsed_data !== undefined ? b.parsed_data : b.data !== undefined ? readBodyData(b.data) : undefined;
721
+
722
+ if (resolvedData !== undefined) {
723
+ const rawBody = resolvedData;
724
+
725
+ if (b.force_get) {
726
+ if (typeof rawBody === "string") {
727
+ const bodyParams = new URLSearchParams(rawBody);
728
+ for (const [key, value] of bodyParams.entries()) {
729
+ const existing = query[key];
730
+ if (existing !== undefined) {
731
+ query[key] = Array.isArray(existing)
732
+ ? [...existing, value]
733
+ : [existing, value];
734
+ } else {
735
+ query[key] = value;
736
+ }
737
+ }
738
+ } else if (
739
+ rawBody !== null &&
740
+ typeof rawBody === "object" &&
741
+ !Array.isArray(rawBody)
742
+ ) {
743
+ for (const [key, value] of Object.entries(
744
+ rawBody as Record<string, unknown>,
745
+ )) {
746
+ const existing = query[key];
747
+ const strValue = String(value);
748
+ if (existing !== undefined) {
749
+ query[key] = Array.isArray(existing)
750
+ ? [...existing, strValue]
751
+ : [existing, strValue];
752
+ } else {
753
+ query[key] = strValue;
754
+ }
755
+ }
756
+ }
757
+ } else {
758
+ reqBody = rawBody;
759
+ }
760
+ }
761
+
762
+ // Resolve connection and make request
763
+ const resolveOptions: ResolveOAuthConnectionOptions = {};
764
+ if (b.client_id && !managed) {
765
+ resolveOptions.clientId = b.client_id;
766
+ }
767
+ if (b.account) {
768
+ resolveOptions.account = b.account;
769
+ }
770
+
771
+ const connection = await resolveOAuthConnection(b.provider, resolveOptions);
772
+
773
+ const headers = b.headers ?? {};
774
+
775
+ const req: OAuthConnectionRequest = {
776
+ method,
777
+ path: requestPath,
778
+ ...(Object.keys(query).length > 0 ? { query } : {}),
779
+ ...(Object.keys(headers).length > 0 ? { headers } : {}),
780
+ ...(reqBody !== undefined ? { body: reqBody } : {}),
781
+ ...(baseUrl ? { baseUrl } : {}),
782
+ };
783
+
784
+ const response = await connection.request(req);
785
+
786
+ const result: Record<string, unknown> = {
787
+ ok: response.status >= 200 && response.status < 300,
788
+ status: response.status,
789
+ headers: response.headers,
790
+ body: response.body,
791
+ };
792
+
793
+ if (response.status === 401 || response.status === 403) {
794
+ result.hint = managed
795
+ ? `Request returned HTTP ${response.status}. The OAuth token may be expired or revoked.\n\n` +
796
+ `Run 'assistant oauth status ${b.provider}' to check connection health.\n` +
797
+ `To reconnect, run 'assistant oauth connect --help'.`
798
+ : `Request returned HTTP ${response.status}. The OAuth token may be expired or revoked.\n\n` +
799
+ `Run 'assistant oauth status ${b.provider}' to check connection status.\n` +
800
+ `To reconnect, run 'assistant oauth connect --help'.`;
801
+ }
802
+
803
+ return result;
804
+ }
805
+
806
+ // ---------------------------------------------------------------------------
807
+ // Connect handler (managed path for platform OAuth)
808
+ // ---------------------------------------------------------------------------
809
+
810
+ async function handleManagedConnect({ body = {} }: RouteHandlerArgs) {
811
+ const b = body as {
812
+ provider: string;
813
+ scopes?: string[];
814
+ redirect_after_connect?: string;
815
+ };
816
+
817
+ if (!b.provider) throw new BadRequestError("provider is required");
818
+
819
+ const client = await requirePlatformClient();
820
+
821
+ const startPath = `/v1/assistants/${encodeURIComponent(client.platformAssistantId)}/oauth/${encodeURIComponent(b.provider)}/start/`;
822
+
823
+ const reqBody: Record<string, unknown> = {};
824
+ if (b.scopes && b.scopes.length > 0) {
825
+ reqBody.requested_scopes = b.scopes;
826
+ }
827
+ reqBody.redirect_after_connect =
828
+ b.redirect_after_connect ?? "/account/oauth/desktop-complete";
829
+
830
+ const response = await client.fetch(startPath, {
831
+ method: "POST",
832
+ headers: { "Content-Type": "application/json" },
833
+ body: JSON.stringify(reqBody),
834
+ });
835
+
836
+ if (!response.ok) {
837
+ const errorText = await response.text().catch(() => "");
838
+ const baseMsg = `Platform returned HTTP ${response.status}${errorText ? `: ${errorText}` : ""}`;
839
+ if (response.status === 401 || response.status === 403) {
840
+ throw new InternalError(
841
+ `${baseMsg}. Your platform session may have expired. Run \`vellum platform connect\` to reconnect.`,
842
+ );
843
+ }
844
+ throw new InternalError(baseMsg);
845
+ }
846
+
847
+ const result = (await response.json()) as { connect_url?: string };
848
+
849
+ if (!result.connect_url) {
850
+ throw new InternalError(
851
+ "Platform did not return a connect URL — the OAuth flow could not be started",
852
+ );
853
+ }
854
+
855
+ return { ok: true, connect_url: result.connect_url };
856
+ }
857
+
858
+ async function handleManagedConnectPoll({ queryParams = {} }: RouteHandlerArgs) {
859
+ const provider = queryParams.provider;
860
+ if (!provider) throw new BadRequestError("provider query param is required");
861
+
862
+ const client = await requirePlatformClient();
863
+ const entries = await fetchActiveConnections(client, provider);
864
+
865
+ return {
866
+ ok: true,
867
+ connections: entries.map((e) => ({
868
+ id: e.id,
869
+ account_label: e.account_label ?? null,
870
+ scopes_granted: e.scopes_granted ?? [],
871
+ })),
872
+ };
873
+ }
874
+
875
+ // ---------------------------------------------------------------------------
876
+ // Route definitions
877
+ // ---------------------------------------------------------------------------
878
+
879
+ export const ROUTES: RouteDefinition[] = [
880
+ {
881
+ operationId: "oauth_disconnect",
882
+ endpoint: "oauth/disconnect",
883
+ method: "POST",
884
+ policyKey: "oauth/disconnect",
885
+ summary: "Disconnect OAuth provider",
886
+ description:
887
+ "Disconnect an OAuth provider and remove associated credentials (BYO or managed).",
888
+ tags: ["oauth"],
889
+ requirePolicyEnforcement: true,
890
+ handler: handleDisconnect,
891
+ },
892
+ {
893
+ operationId: "oauth_mode_get",
894
+ endpoint: "oauth/mode",
895
+ method: "GET",
896
+ summary: "Get OAuth mode",
897
+ description: "Get the current OAuth mode (managed or your-own) for a provider.",
898
+ tags: ["oauth"],
899
+ requirePolicyEnforcement: true,
900
+ queryParams: [
901
+ {
902
+ name: "provider",
903
+ type: "string",
904
+ required: true,
905
+ description: "Provider key",
906
+ },
907
+ ],
908
+ handler: handleModeGet,
909
+ },
910
+ {
911
+ operationId: "oauth_mode_set",
912
+ endpoint: "oauth/mode",
913
+ method: "POST",
914
+ policyKey: "oauth/mode.set",
915
+ summary: "Set OAuth mode",
916
+ description:
917
+ "Set the OAuth mode (managed or your-own) for a provider.",
918
+ tags: ["oauth"],
919
+ requirePolicyEnforcement: true,
920
+ handler: handleModeSet,
921
+ },
922
+ {
923
+ operationId: "oauth_status",
924
+ endpoint: "oauth/status",
925
+ method: "GET",
926
+ summary: "Get OAuth status",
927
+ description:
928
+ "Show OAuth connection status for a specified provider (BYO or managed).",
929
+ tags: ["oauth"],
930
+ requirePolicyEnforcement: true,
931
+ queryParams: [
932
+ {
933
+ name: "provider",
934
+ type: "string",
935
+ required: true,
936
+ description: "Provider key",
937
+ },
938
+ ],
939
+ handler: handleStatus,
940
+ },
941
+ {
942
+ operationId: "oauth_ping",
943
+ endpoint: "oauth/ping",
944
+ method: "POST",
945
+ summary: "Ping OAuth provider",
946
+ description:
947
+ "Verify an OAuth token is valid by hitting the provider's configured health-check endpoint.",
948
+ tags: ["oauth"],
949
+ requirePolicyEnforcement: true,
950
+ handler: handlePing,
951
+ },
952
+ {
953
+ operationId: "oauth_token",
954
+ endpoint: "oauth/token",
955
+ method: "POST",
956
+ policyKey: "oauth/token",
957
+ summary: "Get OAuth token",
958
+ description:
959
+ "Retrieve a valid OAuth access token for a BYO-mode provider.",
960
+ tags: ["oauth"],
961
+ requirePolicyEnforcement: true,
962
+ handler: handleToken,
963
+ },
964
+ {
965
+ operationId: "oauth_request",
966
+ endpoint: "oauth/request",
967
+ method: "POST",
968
+ policyKey: "oauth/request",
969
+ summary: "Make authenticated OAuth request",
970
+ description:
971
+ "Make an authenticated HTTP request through an OAuth connection (supports curl-like interface).",
972
+ tags: ["oauth"],
973
+ requirePolicyEnforcement: true,
974
+ handler: handleRequest,
975
+ },
976
+ {
977
+ operationId: "oauth_managed_connect_start",
978
+ endpoint: "oauth/managed-connect/start",
979
+ method: "POST",
980
+ policyKey: "oauth/managed-connect.start",
981
+ summary: "Start managed OAuth connect",
982
+ description:
983
+ "Start a managed (platform) OAuth connect flow and return the connect URL.",
984
+ tags: ["oauth"],
985
+ requirePolicyEnforcement: true,
986
+ handler: handleManagedConnect,
987
+ },
988
+ {
989
+ operationId: "oauth_managed_connect_poll",
990
+ endpoint: "oauth/managed-connect/poll",
991
+ method: "GET",
992
+ summary: "Poll managed OAuth connections",
993
+ description:
994
+ "Fetch active platform connections for a provider (used to detect new connections after managed connect).",
995
+ tags: ["oauth"],
996
+ requirePolicyEnforcement: true,
997
+ queryParams: [
998
+ {
999
+ name: "provider",
1000
+ type: "string",
1001
+ required: true,
1002
+ description: "Provider key",
1003
+ },
1004
+ ],
1005
+ handler: handleManagedConnectPoll,
1006
+ },
1007
+ ];