@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
@@ -1,40 +1,64 @@
1
1
  import { type Command } from "commander";
2
2
 
3
- import { loadConfig } from "../../../config/loader.js";
4
- import {
5
- deleteApp,
6
- deleteConnection,
7
- deleteProvider,
8
- disconnectOAuthProvider,
9
- getProvider,
10
- listApps,
11
- listConnections,
12
- listProviders,
13
- registerProvider,
14
- updateProvider,
15
- } from "../../../oauth/oauth-store.js";
16
- import {
17
- type SerializedProvider,
18
- serializeProvider,
19
- } from "../../../oauth/provider-serializer.js";
20
- import { isProviderVisible } from "../../../oauth/provider-visibility.js";
21
- import { SEEDED_PROVIDER_KEYS } from "../../../oauth/seed-providers.js";
3
+ import { cliIpcCall, exitFromIpcResult } from "../../../ipc/cli-client.js";
4
+ import { registerCommand } from "../../lib/register-command.js";
22
5
  import { getCliLogger } from "../../logger.js";
23
6
  import { shouldOutputJson, writeOutput } from "../../output.js";
24
7
 
25
8
  const log = getCliLogger("cli");
26
9
 
27
- const LOOPBACK_CALLBACK_PATH = "/oauth/callback";
10
+ // ---------------------------------------------------------------------------
11
+ // Types
12
+ // ---------------------------------------------------------------------------
13
+
14
+ interface SerializedProvider {
15
+ providerKey: string;
16
+ displayName?: string | null;
17
+ description?: string | null;
18
+ supportsManagedMode?: boolean;
19
+ managedServiceIsPaid?: boolean;
20
+ defaultScopes?: string[];
21
+ authUrl?: string;
22
+ tokenUrl?: string;
23
+ refreshUrl?: string | null;
24
+ dashboardUrl?: string | null;
25
+ appType?: string | null;
26
+ requiresClientSecret?: boolean;
27
+ clientIdPlaceholder?: string | null;
28
+ scopeSeparator?: string;
29
+ tokenEndpointAuthMethod?: string | null;
30
+ tokenExchangeBodyFormat?: string | null;
31
+ extraParams?: unknown;
32
+ redirectUri?: string | null;
33
+ baseUrl?: string | null;
34
+ userinfoUrl?: string | null;
35
+ pingUrl?: string | null;
36
+ pingMethod?: string | null;
37
+ pingHeaders?: unknown;
38
+ pingBody?: unknown;
39
+ revokeUrl?: string | null;
40
+ revokeBodyTemplate?: unknown;
41
+ loopbackPort?: number | null;
42
+ injectionTemplates?: unknown;
43
+ identityUrl?: string | null;
44
+ identityMethod?: string | null;
45
+ identityHeaders?: unknown;
46
+ identityBody?: unknown;
47
+ identityResponsePaths?: string[] | null;
48
+ identityFormat?: string | null;
49
+ identityOkField?: string | null;
50
+ availableScopes?: unknown;
51
+ setupNotes?: string[] | unknown;
52
+ featureFlag?: string | null;
53
+ logoUrl?: string | null;
54
+ createdAt?: string;
55
+ updatedAt?: string;
56
+ }
28
57
 
29
58
  // ---------------------------------------------------------------------------
30
59
  // Text formatting helpers (non-JSON output)
31
60
  // ---------------------------------------------------------------------------
32
61
 
33
- /**
34
- * Format available scopes for text output.
35
- * Returns a single-line string for URLs, or a multi-line bullet list for
36
- * structured scope arrays.
37
- */
38
62
  function formatAvailableScopes(
39
63
  availableScopes: unknown,
40
64
  indent: string = " ",
@@ -55,7 +79,6 @@ function formatAvailableScopes(
55
79
  return null;
56
80
  }
57
81
 
58
- /** Render a single provider as a concise summary line for `list`. */
59
82
  function formatProviderSummary(p: SerializedProvider): string {
60
83
  const name = p.displayName ?? p.providerKey;
61
84
  const desc = p.description ? ` — ${p.description}` : "";
@@ -71,7 +94,6 @@ function formatProviderSummary(p: SerializedProvider): string {
71
94
  );
72
95
  }
73
96
 
74
- /** Format a JSON value as indented text for `get` detail output. */
75
97
  function formatJsonValue(value: unknown, indent: string = " "): string {
76
98
  const json = JSON.stringify(value, null, 2);
77
99
  return json
@@ -80,7 +102,6 @@ function formatJsonValue(value: unknown, indent: string = " "): string {
80
102
  .join("\n");
81
103
  }
82
104
 
83
- /** Render a single provider as structured text for `get` with all fields. */
84
105
  function formatProviderDetail(p: SerializedProvider): string {
85
106
  const lines: string[] = [];
86
107
  const name = p.displayName ?? p.providerKey;
@@ -159,11 +180,7 @@ function formatProviderDetail(p: SerializedProvider): string {
159
180
 
160
181
  /**
161
182
  * Resolve a logo URL from CLI flags, enforcing mutual exclusion between
162
- * --logo-url and --logo-simpleicons-slug. Returns:
163
- * - `undefined` when neither flag is set (caller should leave the field unchanged)
164
- * - `null` when `--logo-url ""` is passed (clear the stored value)
165
- * - a non-empty string URL otherwise
166
- * Throws when both flags are set simultaneously.
183
+ * --logo-url and --logo-simpleicons-slug.
167
184
  */
168
185
  function resolveLogoUrlFromFlags(opts: {
169
186
  logoUrl?: string;
@@ -182,51 +199,22 @@ function resolveLogoUrlFromFlags(opts: {
182
199
  return `https://cdn.simpleicons.org/${encodeURIComponent(slug)}`;
183
200
  }
184
201
  if (opts.logoUrl !== undefined) {
185
- // Trim whitespace so copy-paste-padded URLs don't fail to parse on the
186
- // client. Empty string (after trimming) clears the stored value
187
- // (matches --revoke-url semantics documented in the `update` command
188
- // help text).
189
202
  const trimmed = opts.logoUrl.trim();
190
203
  return trimmed === "" ? null : trimmed;
191
204
  }
192
205
  return undefined;
193
206
  }
194
207
 
195
- /**
196
- * Resolve the redirect URI for a provider based on its loopback port.
197
- *
198
- * Resolves the loopback redirect URI for display purposes. Gateway
199
- * redirect URIs are resolved dynamically at connect time.
200
- */
201
- function resolveRedirectUri(loopbackPort: number | null): string | null {
202
- if (!loopbackPort) {
203
- // No fixed port — loopback still works at runtime with an OS-assigned
204
- // port, but we can't predict the redirect URI ahead of time. Return
205
- // a sentinel so callers know the transport is loopback-dynamic rather
206
- // than unsupported.
207
- return "http://localhost:<dynamic>/oauth/callback";
208
- }
209
- return `http://localhost:${loopbackPort}${LOOPBACK_CALLBACK_PATH}`;
210
- }
211
-
212
- /** Serialize a provider row with the CLI-resolved redirect URI. */
213
- function parseProviderRow(row: ReturnType<typeof getProvider>) {
214
- if (!row) return row;
215
- return serializeProvider(row, {
216
- redirectUri: resolveRedirectUri(row.loopbackPort),
217
- });
218
- }
219
-
220
208
  export function registerProviderCommands(oauth: Command): void {
221
- const providers = oauth
222
- .command("providers")
223
- .description(
209
+ registerCommand(oauth, {
210
+ name: "providers",
211
+ transport: "ipc",
212
+ description:
224
213
  "Fetch configured OAuth providers and register custom providers of your own",
225
- );
226
-
227
- providers.addHelpText(
228
- "after",
229
- `
214
+ build: (providers) => {
215
+ providers.addHelpText(
216
+ "after",
217
+ `
230
218
  Providers define the protocol-level configuration for an OAuth integration:
231
219
  authorization URL, token URL, default scopes, and other endpoint details.
232
220
 
@@ -234,26 +222,26 @@ They are seeded on startup for built-in integrations (e.g. Google, Slack,
234
222
  GitHub) but can also be registered dynamically via the "register" subcommand.
235
223
 
236
224
  Each provider is identified by a provider key (e.g. "google").`,
237
- );
225
+ );
238
226
 
239
- // ---------------------------------------------------------------------------
240
- // providers list
241
- // ---------------------------------------------------------------------------
227
+ // -----------------------------------------------------------------------
228
+ // providers list
229
+ // -----------------------------------------------------------------------
242
230
 
243
- providers
244
- .command("list")
245
- .description("List all registered OAuth providers")
246
- .option(
247
- "--provider-key <key>",
248
- 'Filter by provider key substring (case-insensitive). Comma-separated values are OR\'d (e.g. "google,slack")',
249
- )
250
- .option(
251
- "--supports-managed",
252
- "Only show providers that support managed mode",
253
- )
254
- .addHelpText(
255
- "after",
256
- `
231
+ providers
232
+ .command("list")
233
+ .description("List all registered OAuth providers")
234
+ .option(
235
+ "--provider-key <key>",
236
+ 'Filter by provider key substring (case-insensitive). Comma-separated values are OR\'d (e.g. "google,slack")',
237
+ )
238
+ .option(
239
+ "--supports-managed",
240
+ "Only show providers that support managed mode",
241
+ )
242
+ .addHelpText(
243
+ "after",
244
+ `
257
245
  Returns registered OAuth providers, including both built-in providers
258
246
  seeded at startup and any dynamically registered via "providers register".
259
247
 
@@ -273,67 +261,76 @@ Examples:
273
261
  $ assistant oauth providers list --provider-key notion --json
274
262
  $ assistant oauth providers list --supports-managed
275
263
  $ assistant oauth providers list --supports-managed --json`,
276
- )
277
- .action(
278
- (
279
- opts: { providerKey?: string; supportsManaged?: boolean },
280
- cmd: Command,
281
- ) => {
282
- try {
283
- const config = loadConfig();
284
- let allProviders = listProviders();
285
- allProviders = allProviders.filter((r) =>
286
- isProviderVisible(r, config),
287
- );
288
- let rows = allProviders.map(parseProviderRow);
289
-
290
- if (opts.providerKey) {
291
- const needles = opts.providerKey
292
- .split(",")
293
- .map((n) => n.trim().toLowerCase())
294
- .filter(Boolean);
295
- rows = rows.filter(
296
- (r) =>
297
- r &&
298
- needles.some((needle) =>
299
- r.providerKey.toLowerCase().includes(needle),
300
- ),
301
- );
302
- }
303
-
304
- if (opts.supportsManaged) {
305
- rows = rows.filter((r) => r && r.supportsManagedMode);
306
- }
264
+ )
265
+ .action(
266
+ async (
267
+ opts: { providerKey?: string; supportsManaged?: boolean },
268
+ cmd: Command,
269
+ ) => {
270
+ const queryParams: Record<string, string> = {};
271
+ if (opts.supportsManaged) {
272
+ queryParams.supports_managed_mode = "true";
273
+ }
274
+ const r = await cliIpcCall<{
275
+ providers: Array<Record<string, unknown>>;
276
+ }>("oauth_providers_get", {
277
+ queryParams,
278
+ });
307
279
 
308
- if (shouldOutputJson(cmd)) {
309
- writeOutput(cmd, rows);
310
- } else {
311
- const validRows = rows.filter(
312
- (r): r is NonNullable<typeof r> => r != null,
313
- );
314
- const lines = validRows.map(formatProviderSummary);
315
- process.stdout.write(
316
- `${validRows.length} provider(s):\n\n${lines.join("\n\n")}\n`,
280
+ if (!r.ok) return exitFromIpcResult(r);
281
+
282
+ // The route returns snake_case summaries; map to camelCase for
283
+ // display consistency with the existing CLI contract.
284
+ let rows: SerializedProvider[] = (r.result?.providers ?? []).map(
285
+ (p) => ({
286
+ providerKey: p.provider_key as string,
287
+ displayName: p.display_name as string | null,
288
+ description: p.description as string | null,
289
+ supportsManagedMode: p.supports_managed_mode as boolean,
290
+ managedServiceIsPaid: p.managed_service_is_paid as boolean,
291
+ requiresClientSecret: p.requires_client_secret as boolean,
292
+ logoUrl: p.logo_url as string | null,
293
+ dashboardUrl: p.dashboard_url as string | null,
294
+ clientIdPlaceholder: p.client_id_placeholder as string | null,
295
+ featureFlag: p.feature_flag as string | null,
296
+ }),
317
297
  );
318
- }
319
- } catch (err) {
320
- const message = err instanceof Error ? err.message : String(err);
321
- writeOutput(cmd, { ok: false, error: message });
322
- process.exitCode = 1;
323
- }
324
- },
325
- );
326
298
 
327
- // ---------------------------------------------------------------------------
328
- // providers get <provider-key>
329
- // ---------------------------------------------------------------------------
299
+ if (opts.providerKey) {
300
+ const needles = opts.providerKey
301
+ .split(",")
302
+ .map((n) => n.trim().toLowerCase())
303
+ .filter(Boolean);
304
+ rows = rows.filter(
305
+ (r) =>
306
+ r &&
307
+ needles.some((needle) =>
308
+ r.providerKey.toLowerCase().includes(needle),
309
+ ),
310
+ );
311
+ }
330
312
 
331
- providers
332
- .command("get <provider-key>")
333
- .description("Show details of a specific OAuth provider")
334
- .addHelpText(
335
- "after",
336
- `
313
+ if (shouldOutputJson(cmd)) {
314
+ writeOutput(cmd, rows);
315
+ } else {
316
+ const lines = rows.map(formatProviderSummary);
317
+ process.stdout.write(
318
+ `${rows.length} provider(s):\n\n${lines.join("\n\n")}\n`,
319
+ );
320
+ }
321
+ },
322
+ );
323
+
324
+ // -----------------------------------------------------------------------
325
+ // providers get <provider-key>
326
+ // -----------------------------------------------------------------------
327
+
328
+ providers
329
+ .command("get <provider-key>")
330
+ .description("Show details of a specific OAuth provider")
331
+ .addHelpText(
332
+ "after",
333
+ `
337
334
  Arguments:
338
335
  provider-key Provider key (e.g. "google").
339
336
  Must match the key used during registration or seeding.
@@ -345,184 +342,175 @@ if the provider key is not found.
345
342
  Examples:
346
343
  $ assistant oauth providers get google
347
344
  $ assistant oauth providers get twitter --json`,
348
- )
349
- .action((provider: string, _opts: unknown, cmd: Command) => {
350
- try {
351
- const row = getProvider(provider);
352
-
353
- if (!row) {
354
- writeOutput(cmd, {
355
- ok: false,
356
- error: `Provider not found: "${provider}". Run 'assistant oauth providers list' to see all registered providers. To register a custom provider, run 'assistant oauth providers register --help'.`,
357
- });
358
- process.exitCode = 1;
359
- return;
360
- }
361
-
362
- if (!isProviderVisible(row, loadConfig())) {
363
- writeOutput(cmd, {
364
- ok: false,
365
- error: `Provider not found: "${provider}". Run 'assistant oauth providers list' to see all registered providers. To register a custom provider, run 'assistant oauth providers register --help'.`,
366
- });
367
- process.exitCode = 1;
368
- return;
369
- }
370
-
371
- const parsed = parseProviderRow(row);
372
- if (shouldOutputJson(cmd)) {
373
- writeOutput(cmd, parsed);
374
- } else if (parsed) {
375
- process.stdout.write(formatProviderDetail(parsed) + "\n");
376
- }
377
- } catch (err) {
378
- const message = err instanceof Error ? err.message : String(err);
379
- writeOutput(cmd, { ok: false, error: message });
380
- process.exitCode = 1;
381
- }
382
- });
345
+ )
346
+ .action(async (provider: string, _opts: unknown, cmd: Command) => {
347
+ const r = await cliIpcCall<{ provider: SerializedProvider }>(
348
+ "oauth_providers_by_providerKey_get",
349
+ { pathParams: { providerKey: provider } },
350
+ );
383
351
 
384
- // ---------------------------------------------------------------------------
385
- // providers register
386
- // ---------------------------------------------------------------------------
352
+ if (!r.ok) {
353
+ if (r.statusCode === 404) {
354
+ writeOutput(cmd, {
355
+ ok: false,
356
+ error: `Provider not found: "${provider}". Run 'assistant oauth providers list' to see all registered providers. To register a custom provider, run 'assistant oauth providers register --help'.`,
357
+ });
358
+ process.exitCode = 1;
359
+ return;
360
+ }
361
+ return exitFromIpcResult(r);
362
+ }
387
363
 
388
- providers
389
- .command("register")
390
- .description("Register a new OAuth provider configuration")
391
- .requiredOption(
392
- "--provider-key <key>",
393
- "Unique provider key (e.g. \"custom-service\"). Must not collide with an existing key from 'assistant oauth providers list'.",
394
- )
395
- .requiredOption(
396
- "--auth-url <url>",
397
- "OAuth authorization endpoint URL (e.g. https://accounts.example.com/o/oauth2/auth)",
398
- )
399
- .requiredOption(
400
- "--token-url <url>",
401
- "OAuth token endpoint URL (e.g. https://oauth2.example.com/token)",
402
- )
403
- .option(
404
- "--refresh-url <url>",
405
- "OAuth token refresh endpoint URL. Defaults to --token-url when omitted. Set this when the provider uses a different endpoint for the refresh_token grant than for the authorization_code grant.",
406
- )
407
- .option("--base-url <url>", "API base URL for the service")
408
- .option("--userinfo-url <url>", "OpenID Connect userinfo endpoint URL")
409
- .option(
410
- "--scopes <scopes>",
411
- 'Comma-separated default scopes (e.g. "read,write,profile")',
412
- )
413
- .option(
414
- "--scope-separator <sep>",
415
- 'Separator used to join scopes in the authorize URL (default: " "). Use "," for providers like Linear that expect comma-separated scopes.',
416
- )
417
- .option(
418
- "--token-auth-method <method>",
419
- 'How the client authenticates at the token endpoint: "client_secret_post" or "client_secret_basic"',
420
- )
421
- .option(
422
- "--token-exchange-body-format <format>",
423
- 'Body encoding for the token exchange request: "form" (application/x-www-form-urlencoded, default) or "json" (application/json)',
424
- "form",
425
- )
426
- .option(
427
- "--ping-url <url>",
428
- 'Health-check endpoint URL for token validation (e.g. "https://api.example.com/user"). Used by "assistant oauth ping" to verify a stored token.',
429
- )
430
- .option(
431
- "--ping-method <method>",
432
- "HTTP method for the ping endpoint: GET (default) or POST",
433
- )
434
- .option(
435
- "--ping-headers <json>",
436
- 'JSON object of extra headers for the ping request (e.g. \'{"Notion-Version":"2022-06-28"}\')',
437
- )
438
- .option(
439
- "--ping-body <json>",
440
- 'JSON body to send with the ping request (e.g. \'{"query":"{ viewer { id } }"}\')',
441
- )
442
- .option(
443
- "--revoke-url <url>",
444
- 'OAuth token revocation endpoint URL. Called best-effort during disconnect to invalidate the access token upstream (e.g. "https://oauth2.googleapis.com/revoke"). When omitted, disconnect is local-only — the upstream token is left valid until it naturally expires.',
445
- )
446
- .option(
447
- "--revoke-body-template <json>",
448
- 'JSON object body template for the revoke request, supporting {access_token} and {client_id} substitution (e.g. \'{"token":"{access_token}","client_id":"{client_id}"}\'). The body is form-encoded and POSTed to --revoke-url.',
449
- )
450
- .option(
451
- "--display-name <name>",
452
- "Human-readable display name for the provider",
453
- )
454
- .option("--description <text>", "Short description of the provider")
455
- .option(
456
- "--dashboard-url <url>",
457
- "URL to the provider's developer console / dashboard",
458
- )
459
- .option(
460
- "--logo-url <url>",
461
- "URL to the provider's logo image (SVG or PNG). Mutually exclusive with --logo-simpleicons-slug.",
462
- )
463
- .option(
464
- "--logo-simpleicons-slug <slug>",
465
- 'Simple Icons slug (e.g. "notion", "linear"). Resolves to https://cdn.simpleicons.org/<slug>. Mutually exclusive with --logo-url.',
466
- )
467
- .option(
468
- "--client-id-placeholder <text>",
469
- "Placeholder text shown in the client ID input field",
470
- )
471
- .option(
472
- "--no-client-secret",
473
- "Mark this provider as not requiring a client secret (default: required)",
474
- )
475
- .option(
476
- "--loopback-port <port>",
477
- "Fixed port for the local OAuth callback server (e.g. 17322). When set, the redirect URI is http://localhost:<port>/oauth/callback",
478
- )
479
- .option(
480
- "--injection-templates <json>",
481
- 'JSON array of token injection templates each with hostPattern, injectionType, headerName, valuePrefix (e.g. \'[{"hostPattern":"api.example.com","injectionType":"header","headerName":"Authorization","valuePrefix":"Bearer "}]\')',
482
- )
483
- .option(
484
- "--app-type <type>",
485
- 'What the provider calls its OAuth apps (e.g. "OAuth App", "Desktop app", "Public integration")',
486
- )
487
- .option(
488
- "--identity-url <url>",
489
- "Identity verification endpoint URL called after OAuth to identify the connected account",
490
- )
491
- .option(
492
- "--identity-method <method>",
493
- "HTTP method for the identity endpoint: GET (default) or POST",
494
- )
495
- .option(
496
- "--identity-headers <json>",
497
- 'JSON object of extra headers for the identity request (e.g. \'{"Notion-Version":"2022-06-28"}\')',
498
- )
499
- .option(
500
- "--identity-body <body>",
501
- 'JSON body to send with the identity request (e.g. \'{"query":"{ viewer { email } }"}\')',
502
- )
503
- .option(
504
- "--identity-response-paths <paths>",
505
- 'Comma-separated dot-notation paths to extract identity from the response (e.g. "email,name,person.email")',
506
- )
507
- .option(
508
- "--identity-format <template>",
509
- 'Format template for the extracted identity using ${pathName} tokens from --identity-response-paths (e.g. "@${login}" or "@${user} (${team})")',
510
- )
511
- .option(
512
- "--identity-ok-field <field>",
513
- 'Dot-notation path to a boolean field that must be truthy for the response to be considered valid (e.g. "ok")',
514
- )
515
- .option(
516
- "--setup-notes <json>",
517
- 'JSON array of setup instruction notes shown during guided setup (e.g. \'["Enable the Gmail API","Add test users"]\')',
518
- )
519
- .option(
520
- "--available-scopes <value>",
521
- "Available scopes: either a JSON array of {scope, description?} objects or a URL to the provider scope docs",
522
- )
523
- .addHelpText(
524
- "after",
525
- `
364
+ const parsed = r.result?.provider;
365
+ if (shouldOutputJson(cmd)) {
366
+ writeOutput(cmd, parsed);
367
+ } else if (parsed) {
368
+ process.stdout.write(formatProviderDetail(parsed) + "\n");
369
+ }
370
+ });
371
+
372
+ // -----------------------------------------------------------------------
373
+ // providers register
374
+ // -----------------------------------------------------------------------
375
+
376
+ providers
377
+ .command("register")
378
+ .description("Register a new OAuth provider configuration")
379
+ .requiredOption(
380
+ "--provider-key <key>",
381
+ "Unique provider key (e.g. \"custom-service\"). Must not collide with an existing key from 'assistant oauth providers list'.",
382
+ )
383
+ .requiredOption(
384
+ "--auth-url <url>",
385
+ "OAuth authorization endpoint URL (e.g. https://accounts.example.com/o/oauth2/auth)",
386
+ )
387
+ .requiredOption(
388
+ "--token-url <url>",
389
+ "OAuth token endpoint URL (e.g. https://oauth2.example.com/token)",
390
+ )
391
+ .option(
392
+ "--refresh-url <url>",
393
+ "OAuth token refresh endpoint URL. Defaults to --token-url when omitted.",
394
+ )
395
+ .option("--base-url <url>", "API base URL for the service")
396
+ .option("--userinfo-url <url>", "OpenID Connect userinfo endpoint URL")
397
+ .option(
398
+ "--scopes <scopes>",
399
+ 'Comma-separated default scopes (e.g. "read,write,profile")',
400
+ )
401
+ .option(
402
+ "--scope-separator <sep>",
403
+ 'Separator used to join scopes in the authorize URL (default: " ").',
404
+ )
405
+ .option(
406
+ "--token-auth-method <method>",
407
+ 'How the client authenticates at the token endpoint: "client_secret_post" or "client_secret_basic"',
408
+ )
409
+ .option(
410
+ "--token-exchange-body-format <format>",
411
+ 'Body encoding for the token exchange request: "form" (default) or "json"',
412
+ "form",
413
+ )
414
+ .option(
415
+ "--ping-url <url>",
416
+ "Health-check endpoint URL for token validation",
417
+ )
418
+ .option(
419
+ "--ping-method <method>",
420
+ "HTTP method for the ping endpoint: GET (default) or POST",
421
+ )
422
+ .option(
423
+ "--ping-headers <json>",
424
+ "JSON object of extra headers for the ping request",
425
+ )
426
+ .option(
427
+ "--ping-body <json>",
428
+ "JSON body to send with the ping request",
429
+ )
430
+ .option(
431
+ "--revoke-url <url>",
432
+ "OAuth token revocation endpoint URL",
433
+ )
434
+ .option(
435
+ "--revoke-body-template <json>",
436
+ "JSON object body template for the revoke request",
437
+ )
438
+ .option(
439
+ "--display-name <name>",
440
+ "Human-readable display name for the provider",
441
+ )
442
+ .option("--description <text>", "Short description of the provider")
443
+ .option(
444
+ "--dashboard-url <url>",
445
+ "URL to the provider's developer console / dashboard",
446
+ )
447
+ .option(
448
+ "--logo-url <url>",
449
+ "URL to the provider's logo image. Mutually exclusive with --logo-simpleicons-slug.",
450
+ )
451
+ .option(
452
+ "--logo-simpleicons-slug <slug>",
453
+ 'Simple Icons slug (e.g. "notion"). Mutually exclusive with --logo-url.',
454
+ )
455
+ .option(
456
+ "--client-id-placeholder <text>",
457
+ "Placeholder text shown in the client ID input field",
458
+ )
459
+ .option(
460
+ "--no-client-secret",
461
+ "Mark this provider as not requiring a client secret",
462
+ )
463
+ .option(
464
+ "--loopback-port <port>",
465
+ "Fixed port for the local OAuth callback server",
466
+ )
467
+ .option(
468
+ "--injection-templates <json>",
469
+ "JSON array of token injection templates",
470
+ )
471
+ .option(
472
+ "--app-type <type>",
473
+ 'What the provider calls its OAuth apps (e.g. "OAuth App")',
474
+ )
475
+ .option(
476
+ "--identity-url <url>",
477
+ "Identity verification endpoint URL",
478
+ )
479
+ .option(
480
+ "--identity-method <method>",
481
+ "HTTP method for the identity endpoint: GET (default) or POST",
482
+ )
483
+ .option(
484
+ "--identity-headers <json>",
485
+ "JSON object of extra headers for the identity request",
486
+ )
487
+ .option(
488
+ "--identity-body <body>",
489
+ "JSON body to send with the identity request",
490
+ )
491
+ .option(
492
+ "--identity-response-paths <paths>",
493
+ "Comma-separated dot-notation paths to extract identity from the response",
494
+ )
495
+ .option(
496
+ "--identity-format <template>",
497
+ "Format template for the extracted identity",
498
+ )
499
+ .option(
500
+ "--identity-ok-field <field>",
501
+ "Dot-notation path to a boolean field that must be truthy for the response to be valid",
502
+ )
503
+ .option(
504
+ "--setup-notes <json>",
505
+ "JSON array of setup instruction notes shown during guided setup",
506
+ )
507
+ .option(
508
+ "--available-scopes <value>",
509
+ "Available scopes: either a JSON array of {scope, description?} objects or a URL",
510
+ )
511
+ .addHelpText(
512
+ "after",
513
+ `
526
514
  Registers a new OAuth provider configuration in the local store for custom
527
515
  integrations not covered by the built-in provider seeds. The provider key
528
516
  must be unique — if it collides with an existing key, the command fails.
@@ -532,10 +520,6 @@ On success, returns the full provider row including generated timestamps.
532
520
  After registering, create an OAuth app with 'assistant oauth apps create'
533
521
  and then connect with 'assistant oauth connect <provider-key>'.
534
522
 
535
- Token injection templates control how the OAuth access token is injected
536
- into outgoing HTTP requests matched by host pattern. Identity config
537
- defines how the assistant verifies the connected account after OAuth.
538
-
539
523
  Examples:
540
524
  $ assistant oauth providers register \\
541
525
  --provider-key custom-api \\
@@ -545,536 +529,418 @@ Examples:
545
529
  --provider-key my-service \\
546
530
  --auth-url https://my-service.com/auth \\
547
531
  --token-url https://my-service.com/token \\
548
- --scopes read,write --json
549
- $ assistant oauth providers register \\
550
- --provider-key my-graphql-api \\
551
- --auth-url https://example.com/auth \\
552
- --token-url https://example.com/token \\
553
- --ping-url https://example.com/graphql \\
554
- --ping-method POST \\
555
- --ping-body '{"query":"{ viewer { id } }"}'
556
- $ assistant oauth providers register \\
557
- --provider-key linear-custom \\
558
- --auth-url https://linear.app/oauth/authorize \\
559
- --token-url https://api.linear.app/oauth/token \\
560
- --scopes read,write \\
561
- --scope-separator ","
562
- $ assistant oauth providers register \\
563
- --provider-key split-grants \\
564
- --auth-url https://example.com/oauth/authorize \\
565
- --token-url https://example.com/oauth/token \\
566
- --refresh-url https://example.com/oauth/refresh
567
- $ assistant oauth providers register \\
568
- --provider-key my-api \\
569
- --auth-url https://example.com/auth \\
570
- --token-url https://example.com/token \\
571
- --loopback-port 17400 \\
572
- --injection-templates '[{"hostPattern":"api.example.com","injectionType":"header","headerName":"Authorization","valuePrefix":"Bearer "}]' \\
573
- --identity-url https://api.example.com/me \\
574
- --identity-response-paths email,name
575
- $ assistant oauth providers register \\
576
- --provider-key custom-revokable \\
577
- --auth-url https://example.com/oauth/authorize \\
578
- --token-url https://example.com/oauth/token \\
579
- --revoke-url https://example.com/oauth/revoke \\
580
- --revoke-body-template '{"token":"{access_token}","client_id":"{client_id}"}'
581
- $ assistant oauth providers register \\
582
- --provider-key notion-custom \\
583
- --auth-url https://api.notion.com/v1/oauth/authorize \\
584
- --token-url https://api.notion.com/v1/oauth/token \\
585
- --token-exchange-body-format json \\
586
- --logo-simpleicons-slug notion`,
587
- )
588
- .action(
589
- (
590
- opts: {
591
- providerKey: string;
592
- authUrl: string;
593
- tokenUrl: string;
594
- refreshUrl?: string;
595
- baseUrl?: string;
596
- userinfoUrl?: string;
597
- scopes?: string;
598
- scopeSeparator?: string;
599
- tokenAuthMethod?: string;
600
- tokenExchangeBodyFormat?: string;
601
- pingUrl?: string;
602
- pingMethod?: string;
603
- pingHeaders?: string;
604
- pingBody?: string;
605
- revokeUrl?: string;
606
- revokeBodyTemplate?: string;
607
- displayName?: string;
608
- description?: string;
609
- dashboardUrl?: string;
610
- logoUrl?: string;
611
- logoSimpleiconsSlug?: string;
612
- clientIdPlaceholder?: string;
613
- clientSecret: boolean;
614
- loopbackPort?: string;
615
- injectionTemplates?: string;
616
- appType?: string;
617
- identityUrl?: string;
618
- identityMethod?: string;
619
- identityHeaders?: string;
620
- identityBody?: string;
621
- identityResponsePaths?: string;
622
- identityFormat?: string;
623
- identityOkField?: string;
624
- setupNotes?: string;
625
- availableScopes?: string;
626
- },
627
- cmd: Command,
628
- ) => {
629
- try {
630
- const resolvedLogoUrl = resolveLogoUrlFromFlags(opts);
631
- if (resolvedLogoUrl === null) {
632
- throw new Error(
633
- "Cannot clear logo_url with empty --logo-url during registration. Omit the flag instead.",
634
- );
635
- }
636
-
637
- const row = registerProvider({
638
- provider: opts.providerKey,
639
- authorizeUrl: opts.authUrl,
640
- tokenExchangeUrl: opts.tokenUrl,
641
- refreshUrl: opts.refreshUrl,
642
- baseUrl: opts.baseUrl,
643
- userinfoUrl: opts.userinfoUrl,
644
- defaultScopes: opts.scopes ? opts.scopes.split(",") : [],
645
- availableScopes: opts.availableScopes
646
- ? opts.availableScopes.startsWith("http")
647
- ? opts.availableScopes
648
- : JSON.parse(opts.availableScopes)
649
- : undefined,
650
- scopeSeparator: opts.scopeSeparator,
651
- tokenEndpointAuthMethod: opts.tokenAuthMethod,
652
- tokenExchangeBodyFormat: opts.tokenExchangeBodyFormat,
653
- pingUrl: opts.pingUrl,
654
- pingMethod: opts.pingMethod,
655
- pingHeaders: opts.pingHeaders
656
- ? JSON.parse(opts.pingHeaders)
657
- : undefined,
658
- pingBody: opts.pingBody ? JSON.parse(opts.pingBody) : undefined,
659
- revokeUrl: opts.revokeUrl,
660
- revokeBodyTemplate: opts.revokeBodyTemplate
661
- ? JSON.parse(opts.revokeBodyTemplate)
662
- : undefined,
663
- displayLabel: opts.displayName,
664
- description: opts.description,
665
- dashboardUrl: opts.dashboardUrl,
666
- logoUrl: resolvedLogoUrl,
667
- clientIdPlaceholder: opts.clientIdPlaceholder,
668
- requiresClientSecret: opts.clientSecret ? 1 : 0,
669
- loopbackPort: opts.loopbackPort
670
- ? parseInt(opts.loopbackPort, 10)
671
- : undefined,
672
- injectionTemplates: opts.injectionTemplates
673
- ? JSON.parse(opts.injectionTemplates)
674
- : undefined,
675
- appType: opts.appType,
676
- identityUrl: opts.identityUrl,
677
- identityMethod: opts.identityMethod,
678
- identityHeaders: opts.identityHeaders
679
- ? JSON.parse(opts.identityHeaders)
680
- : undefined,
681
- identityBody: opts.identityBody
682
- ? JSON.parse(opts.identityBody)
683
- : undefined,
684
- identityResponsePaths: opts.identityResponsePaths
685
- ? opts.identityResponsePaths.split(",")
686
- : undefined,
687
- identityFormat: opts.identityFormat,
688
- identityOkField: opts.identityOkField,
689
- setupNotes: opts.setupNotes
690
- ? JSON.parse(opts.setupNotes)
691
- : undefined,
692
- });
693
-
694
- writeOutput(cmd, parseProviderRow(row));
695
- } catch (err) {
696
- let message = err instanceof Error ? err.message : String(err);
697
- if (message.includes("already exists")) {
698
- message += ` Run 'assistant oauth providers list' to see existing providers, or choose a different --provider-key.`;
699
- }
700
- writeOutput(cmd, { ok: false, error: message });
701
- process.exitCode = 1;
702
- }
703
- },
704
- );
705
-
706
- // ---------------------------------------------------------------------------
707
- // providers update <provider-key>
708
- // ---------------------------------------------------------------------------
532
+ --scopes read,write --json`,
533
+ )
534
+ .action(
535
+ async (
536
+ opts: {
537
+ providerKey: string;
538
+ authUrl: string;
539
+ tokenUrl: string;
540
+ refreshUrl?: string;
541
+ baseUrl?: string;
542
+ userinfoUrl?: string;
543
+ scopes?: string;
544
+ scopeSeparator?: string;
545
+ tokenAuthMethod?: string;
546
+ tokenExchangeBodyFormat?: string;
547
+ pingUrl?: string;
548
+ pingMethod?: string;
549
+ pingHeaders?: string;
550
+ pingBody?: string;
551
+ revokeUrl?: string;
552
+ revokeBodyTemplate?: string;
553
+ displayName?: string;
554
+ description?: string;
555
+ dashboardUrl?: string;
556
+ logoUrl?: string;
557
+ logoSimpleiconsSlug?: string;
558
+ clientIdPlaceholder?: string;
559
+ clientSecret: boolean;
560
+ loopbackPort?: string;
561
+ injectionTemplates?: string;
562
+ appType?: string;
563
+ identityUrl?: string;
564
+ identityMethod?: string;
565
+ identityHeaders?: string;
566
+ identityBody?: string;
567
+ identityResponsePaths?: string;
568
+ identityFormat?: string;
569
+ identityOkField?: string;
570
+ setupNotes?: string;
571
+ availableScopes?: string;
572
+ },
573
+ cmd: Command,
574
+ ) => {
575
+ try {
576
+ const resolvedLogoUrl = resolveLogoUrlFromFlags(opts);
577
+ if (resolvedLogoUrl === null) {
578
+ throw new Error(
579
+ "Cannot clear logo_url with empty --logo-url during registration. Omit the flag instead.",
580
+ );
581
+ }
582
+
583
+ const body: Record<string, unknown> = {
584
+ provider_key: opts.providerKey,
585
+ auth_url: opts.authUrl,
586
+ token_url: opts.tokenUrl,
587
+ };
588
+
589
+ if (opts.refreshUrl !== undefined)
590
+ body.refresh_url = opts.refreshUrl;
591
+ if (opts.baseUrl !== undefined) body.base_url = opts.baseUrl;
592
+ if (opts.userinfoUrl !== undefined)
593
+ body.userinfo_url = opts.userinfoUrl;
594
+ body.default_scopes = opts.scopes ? opts.scopes.split(",") : [];
595
+ if (opts.scopeSeparator !== undefined)
596
+ body.scope_separator = opts.scopeSeparator;
597
+ if (opts.tokenAuthMethod !== undefined)
598
+ body.token_endpoint_auth_method = opts.tokenAuthMethod;
599
+ if (opts.tokenExchangeBodyFormat !== undefined)
600
+ body.token_exchange_body_format = opts.tokenExchangeBodyFormat;
601
+ if (opts.pingUrl !== undefined) body.ping_url = opts.pingUrl;
602
+ if (opts.pingMethod !== undefined)
603
+ body.ping_method = opts.pingMethod;
604
+ if (opts.pingHeaders !== undefined)
605
+ body.ping_headers = JSON.parse(opts.pingHeaders);
606
+ if (opts.pingBody !== undefined)
607
+ body.ping_body = JSON.parse(opts.pingBody);
608
+ if (opts.revokeUrl !== undefined) body.revoke_url = opts.revokeUrl;
609
+ if (opts.revokeBodyTemplate !== undefined)
610
+ body.revoke_body_template = JSON.parse(opts.revokeBodyTemplate);
611
+ if (opts.displayName !== undefined)
612
+ body.display_name = opts.displayName;
613
+ if (opts.description !== undefined)
614
+ body.description = opts.description;
615
+ if (opts.dashboardUrl !== undefined)
616
+ body.dashboard_url = opts.dashboardUrl;
617
+ if (resolvedLogoUrl !== undefined)
618
+ body.logo_url = resolvedLogoUrl;
619
+ if (opts.clientIdPlaceholder !== undefined)
620
+ body.client_id_placeholder = opts.clientIdPlaceholder;
621
+ body.requires_client_secret = opts.clientSecret;
622
+ if (opts.loopbackPort !== undefined)
623
+ body.loopback_port = parseInt(opts.loopbackPort, 10);
624
+ if (opts.injectionTemplates !== undefined)
625
+ body.injection_templates = JSON.parse(opts.injectionTemplates);
626
+ if (opts.appType !== undefined) body.app_type = opts.appType;
627
+ if (opts.identityUrl !== undefined)
628
+ body.identity_url = opts.identityUrl;
629
+ if (opts.identityMethod !== undefined)
630
+ body.identity_method = opts.identityMethod;
631
+ if (opts.identityHeaders !== undefined)
632
+ body.identity_headers = JSON.parse(opts.identityHeaders);
633
+ if (opts.identityBody !== undefined)
634
+ body.identity_body = JSON.parse(opts.identityBody);
635
+ if (opts.identityResponsePaths !== undefined)
636
+ body.identity_response_paths =
637
+ opts.identityResponsePaths.split(",");
638
+ if (opts.identityFormat !== undefined)
639
+ body.identity_format = opts.identityFormat;
640
+ if (opts.identityOkField !== undefined)
641
+ body.identity_ok_field = opts.identityOkField;
642
+ if (opts.setupNotes !== undefined)
643
+ body.setup_notes = JSON.parse(opts.setupNotes);
644
+ if (opts.availableScopes !== undefined) {
645
+ body.available_scopes = opts.availableScopes.startsWith("http")
646
+ ? opts.availableScopes
647
+ : JSON.parse(opts.availableScopes);
648
+ }
649
+
650
+ const r = await cliIpcCall<{ provider: SerializedProvider }>(
651
+ "oauth_providers_post",
652
+ { body },
653
+ );
709
654
 
710
- providers
711
- .command("update <provider-key>")
712
- .description("Update an existing custom OAuth provider configuration")
713
- .option(
714
- "--auth-url <url>",
715
- "OAuth authorization endpoint URL (e.g. https://accounts.example.com/o/oauth2/auth)",
716
- )
717
- .option(
718
- "--token-url <url>",
719
- "OAuth token endpoint URL (e.g. https://oauth2.example.com/token)",
720
- )
721
- .option(
722
- "--refresh-url <url>",
723
- "OAuth token refresh endpoint URL. Defaults to --token-url when omitted. Set this when the provider uses a different endpoint for the refresh_token grant than for the authorization_code grant.",
724
- )
725
- .option("--base-url <url>", "API base URL for the service")
726
- .option("--userinfo-url <url>", "OpenID Connect userinfo endpoint URL")
727
- .option(
728
- "--scopes <scopes>",
729
- 'Comma-separated default scopes (e.g. "read,write,profile")',
730
- )
731
- .option(
732
- "--scope-separator <sep>",
733
- 'Separator used to join scopes in the authorize URL (default: " "). Use "," for providers like Linear that expect comma-separated scopes.',
734
- )
735
- .option(
736
- "--token-auth-method <method>",
737
- 'How the client authenticates at the token endpoint: "client_secret_post" or "client_secret_basic"',
738
- )
739
- .option(
740
- "--token-exchange-body-format <format>",
741
- 'Body encoding for the token exchange request: "form" (application/x-www-form-urlencoded, default) or "json" (application/json)',
742
- )
743
- .option(
744
- "--ping-url <url>",
745
- 'Health-check endpoint URL for token validation (e.g. "https://api.example.com/user"). Used by "assistant oauth ping" to verify a stored token.',
746
- )
747
- .option(
748
- "--ping-method <method>",
749
- "HTTP method for the ping endpoint: GET (default) or POST",
750
- )
751
- .option(
752
- "--ping-headers <json>",
753
- 'JSON object of extra headers for the ping request (e.g. \'{"Notion-Version":"2022-06-28"}\')',
754
- )
755
- .option(
756
- "--ping-body <json>",
757
- 'JSON body to send with the ping request (e.g. \'{"query":"{ viewer { id } }"}\')',
758
- )
759
- .option(
760
- "--revoke-url <url>",
761
- "OAuth token revocation endpoint URL. Called best-effort during disconnect to invalidate the access token upstream. Pass an empty string to clear.",
762
- )
763
- .option(
764
- "--revoke-body-template <json>",
765
- "JSON object body template for the revoke request, supporting {access_token} and {client_id} substitution. Pass an empty string to clear.",
766
- )
767
- .option(
768
- "--display-name <name>",
769
- "Human-readable display name for the provider",
770
- )
771
- .option("--description <text>", "Short description of the provider")
772
- .option(
773
- "--dashboard-url <url>",
774
- "URL to the provider's developer console / dashboard",
775
- )
776
- .option(
777
- "--logo-url <url>",
778
- "URL to the provider's logo image (SVG or PNG). Mutually exclusive with --logo-simpleicons-slug.",
779
- )
780
- .option(
781
- "--logo-simpleicons-slug <slug>",
782
- 'Simple Icons slug (e.g. "notion", "linear"). Resolves to https://cdn.simpleicons.org/<slug>. Mutually exclusive with --logo-url.',
783
- )
784
- .option(
785
- "--client-id-placeholder <text>",
786
- "Placeholder text shown in the client ID input field",
787
- )
788
- .option(
789
- "--no-client-secret",
790
- "Mark this provider as not requiring a client secret (default: required)",
791
- )
792
- .option(
793
- "--loopback-port <port>",
794
- "Fixed port for the local OAuth callback server (e.g. 17322). When set, the redirect URI is http://localhost:<port>/oauth/callback",
795
- )
796
- .option(
797
- "--injection-templates <json>",
798
- 'JSON array of token injection templates — each with hostPattern, injectionType, headerName, valuePrefix (e.g. \'[{"hostPattern":"api.example.com","injectionType":"header","headerName":"Authorization","valuePrefix":"Bearer "}]\')',
799
- )
800
- .option(
801
- "--app-type <type>",
802
- 'What the provider calls its OAuth apps (e.g. "OAuth App", "Desktop app", "Public integration")',
803
- )
804
- .option(
805
- "--identity-url <url>",
806
- "Identity verification endpoint URL — called after OAuth to identify the connected account",
807
- )
808
- .option(
809
- "--identity-method <method>",
810
- "HTTP method for the identity endpoint: GET (default) or POST",
811
- )
812
- .option(
813
- "--identity-headers <json>",
814
- 'JSON object of extra headers for the identity request (e.g. \'{"Notion-Version":"2022-06-28"}\')',
815
- )
816
- .option(
817
- "--identity-body <body>",
818
- 'JSON body to send with the identity request (e.g. \'{"query":"{ viewer { email } }"}\')',
819
- )
820
- .option(
821
- "--identity-response-paths <paths>",
822
- 'Comma-separated dot-notation paths to extract identity from the response (e.g. "email,name,person.email")',
823
- )
824
- .option(
825
- "--identity-format <template>",
826
- 'Format template for the extracted identity using ${pathName} tokens from --identity-response-paths (e.g. "@${login}" or "@${user} (${team})")',
827
- )
828
- .option(
829
- "--identity-ok-field <field>",
830
- 'Dot-notation path to a boolean field that must be truthy for the response to be considered valid (e.g. "ok")',
831
- )
832
- .option(
833
- "--setup-notes <json>",
834
- 'JSON array of setup instruction notes shown during guided setup (e.g. \'["Enable the Gmail API","Add test users"]\')',
835
- )
836
- .option(
837
- "--available-scopes <value>",
838
- "Available scopes: either a JSON array of {scope, description?} objects or a URL to the provider scope docs",
839
- )
840
- .addHelpText(
841
- "after",
842
- `
655
+ if (!r.ok) {
656
+ let message = r.error ?? "Unknown error";
657
+ if (message.includes("already exists")) {
658
+ message += ` Run 'assistant oauth providers list' to see existing providers, or choose a different --provider-key.`;
659
+ }
660
+ writeOutput(cmd, { ok: false, error: message });
661
+ process.exitCode = 1;
662
+ return;
663
+ }
664
+
665
+ writeOutput(cmd, r.result?.provider);
666
+ } catch (err) {
667
+ const message = err instanceof Error ? err.message : String(err);
668
+ writeOutput(cmd, { ok: false, error: message });
669
+ process.exitCode = 1;
670
+ }
671
+ },
672
+ );
673
+
674
+ // -----------------------------------------------------------------------
675
+ // providers update <provider-key>
676
+ // -----------------------------------------------------------------------
677
+
678
+ providers
679
+ .command("update <provider-key>")
680
+ .description("Update an existing custom OAuth provider configuration")
681
+ .option(
682
+ "--auth-url <url>",
683
+ "OAuth authorization endpoint URL",
684
+ )
685
+ .option(
686
+ "--token-url <url>",
687
+ "OAuth token endpoint URL",
688
+ )
689
+ .option(
690
+ "--refresh-url <url>",
691
+ "OAuth token refresh endpoint URL",
692
+ )
693
+ .option("--base-url <url>", "API base URL for the service")
694
+ .option("--userinfo-url <url>", "OpenID Connect userinfo endpoint URL")
695
+ .option(
696
+ "--scopes <scopes>",
697
+ 'Comma-separated default scopes (e.g. "read,write,profile")',
698
+ )
699
+ .option(
700
+ "--scope-separator <sep>",
701
+ 'Separator used to join scopes in the authorize URL',
702
+ )
703
+ .option(
704
+ "--token-auth-method <method>",
705
+ 'How the client authenticates at the token endpoint',
706
+ )
707
+ .option(
708
+ "--token-exchange-body-format <format>",
709
+ 'Body encoding for the token exchange request: "form" or "json"',
710
+ )
711
+ .option("--ping-url <url>", "Health-check endpoint URL")
712
+ .option(
713
+ "--ping-method <method>",
714
+ "HTTP method for the ping endpoint: GET (default) or POST",
715
+ )
716
+ .option("--ping-headers <json>", "JSON object of extra headers for the ping request")
717
+ .option("--ping-body <json>", "JSON body for the ping request")
718
+ .option(
719
+ "--revoke-url <url>",
720
+ "OAuth token revocation endpoint URL. Pass empty string to clear.",
721
+ )
722
+ .option(
723
+ "--revoke-body-template <json>",
724
+ "JSON object body template for the revoke request. Pass empty string to clear.",
725
+ )
726
+ .option("--display-name <name>", "Human-readable display name")
727
+ .option("--description <text>", "Short description")
728
+ .option("--dashboard-url <url>", "Developer console / dashboard URL")
729
+ .option(
730
+ "--logo-url <url>",
731
+ "URL to the provider's logo image. Mutually exclusive with --logo-simpleicons-slug.",
732
+ )
733
+ .option(
734
+ "--logo-simpleicons-slug <slug>",
735
+ 'Simple Icons slug. Mutually exclusive with --logo-url.',
736
+ )
737
+ .option("--client-id-placeholder <text>", "Placeholder for client ID input")
738
+ .option("--no-client-secret", "Mark as not requiring a client secret")
739
+ .option("--loopback-port <port>", "Fixed port for the local OAuth callback server")
740
+ .option("--injection-templates <json>", "JSON array of token injection templates")
741
+ .option("--app-type <type>", "What the provider calls its OAuth apps")
742
+ .option("--identity-url <url>", "Identity verification endpoint URL")
743
+ .option("--identity-method <method>", "HTTP method for identity endpoint")
744
+ .option("--identity-headers <json>", "JSON object of extra headers for identity request")
745
+ .option("--identity-body <body>", "JSON body for identity request")
746
+ .option("--identity-response-paths <paths>", "Comma-separated dot-notation paths")
747
+ .option("--identity-format <template>", "Format template for extracted identity")
748
+ .option("--identity-ok-field <field>", "Dot-notation path to a boolean ok field")
749
+ .option("--setup-notes <json>", "JSON array of setup instruction notes")
750
+ .option("--available-scopes <value>", "Available scopes: JSON array or URL")
751
+ .addHelpText(
752
+ "after",
753
+ `
843
754
  Arguments:
844
755
  provider-key Provider key to update (e.g. "custom-api").
845
756
  Run 'assistant oauth providers list' to see all registered providers.
846
757
 
847
758
  Only the fields you specify are updated — all other fields remain unchanged.
848
759
  Built-in providers (e.g. "google", "slack") cannot be updated; they are
849
- managed by the system and reset on startup. To create a custom provider with
850
- different settings, use 'assistant oauth providers register'.
851
-
852
- Token injection templates control how the OAuth access token is injected
853
- into outgoing HTTP requests matched by host pattern. Identity config
854
- defines how the assistant verifies the connected account after OAuth.
760
+ managed by the system and reset on startup.
855
761
 
856
762
  Examples:
857
763
  $ assistant oauth providers update custom-api --display-name "My Custom API"
858
764
  $ assistant oauth providers update custom-api --scopes read,write --auth-url https://new.example.com/auth
859
765
  $ assistant oauth providers update custom-api --ping-url https://api.example.com/me --json
860
- $ assistant oauth providers update custom-api --scope-separator ","
861
- $ assistant oauth providers update custom-api --refresh-url https://example.com/oauth/refresh
862
- $ assistant oauth providers update custom-api \\
863
- --identity-url https://api.example.com/me \\
864
- --identity-response-paths email,name
865
- $ assistant oauth providers update custom-api \\
866
- --injection-templates '[{"hostPattern":"api.example.com","injectionType":"header","headerName":"Authorization","valuePrefix":"Bearer "}]'
867
- $ assistant oauth providers update custom-api \\
868
- --revoke-url https://api.example.com/oauth/revoke \\
869
- --revoke-body-template '{"token":"{access_token}"}'
870
- $ assistant oauth providers update custom-api --logo-simpleicons-slug notion
871
766
  $ assistant oauth providers update custom-api --logo-url ""`,
872
- )
873
- .action(
874
- (
875
- provider: string,
876
- opts: {
877
- authUrl?: string;
878
- tokenUrl?: string;
879
- refreshUrl?: string;
880
- baseUrl?: string;
881
- userinfoUrl?: string;
882
- scopes?: string;
883
- scopeSeparator?: string;
884
- tokenAuthMethod?: string;
885
- tokenExchangeBodyFormat?: string;
886
- pingUrl?: string;
887
- pingMethod?: string;
888
- pingHeaders?: string;
889
- pingBody?: string;
890
- revokeUrl?: string;
891
- revokeBodyTemplate?: string;
892
- displayName?: string;
893
- description?: string;
894
- dashboardUrl?: string;
895
- logoUrl?: string;
896
- logoSimpleiconsSlug?: string;
897
- clientIdPlaceholder?: string;
898
- clientSecret: boolean;
899
- loopbackPort?: string;
900
- injectionTemplates?: string;
901
- appType?: string;
902
- identityUrl?: string;
903
- identityMethod?: string;
904
- identityHeaders?: string;
905
- identityBody?: string;
906
- identityResponsePaths?: string;
907
- identityFormat?: string;
908
- identityOkField?: string;
909
- setupNotes?: string;
910
- availableScopes?: string;
911
- },
912
- cmd: Command,
913
- ) => {
914
- try {
915
- // Verify provider exists
916
- const existing = getProvider(provider);
917
- if (!existing) {
918
- writeOutput(cmd, {
919
- ok: false,
920
- error: `Provider "${provider}" not found. Run 'assistant oauth providers list' to see all registered providers.`,
921
- });
922
- process.exitCode = 1;
923
- return;
924
- }
925
-
926
- if (!isProviderVisible(existing, loadConfig())) {
927
- writeOutput(cmd, {
928
- ok: false,
929
- error: `Provider "${provider}" not found. Run 'assistant oauth providers list' to see all registered providers.`,
930
- });
931
- process.exitCode = 1;
932
- return;
933
- }
934
-
935
- // Block updates to built-in providers
936
- if (SEEDED_PROVIDER_KEYS.has(provider)) {
937
- writeOutput(cmd, {
938
- ok: false,
939
- error: `Cannot update built-in provider "${provider}". Built-in providers are managed by the system and reset on startup. To create a custom provider with different settings, use 'assistant oauth providers register --provider-key <your-custom-key> ...'`,
940
- });
941
- process.exitCode = 1;
942
- return;
943
- }
944
-
945
- // Build params object from provided options, omitting undefined values
946
- const params: Record<string, unknown> = {};
947
-
948
- if (opts.authUrl !== undefined) params.authorizeUrl = opts.authUrl;
949
- if (opts.tokenUrl !== undefined)
950
- params.tokenExchangeUrl = opts.tokenUrl;
951
- if (opts.refreshUrl !== undefined)
952
- params.refreshUrl = opts.refreshUrl;
953
- if (opts.baseUrl !== undefined) params.baseUrl = opts.baseUrl;
954
- if (opts.userinfoUrl !== undefined)
955
- params.userinfoUrl = opts.userinfoUrl;
956
- if (opts.scopes !== undefined)
957
- params.defaultScopes = opts.scopes.split(",");
958
- if (opts.scopeSeparator !== undefined)
959
- params.scopeSeparator = opts.scopeSeparator;
960
- if (opts.tokenAuthMethod !== undefined)
961
- params.tokenEndpointAuthMethod = opts.tokenAuthMethod;
962
- if (opts.tokenExchangeBodyFormat !== undefined)
963
- params.tokenExchangeBodyFormat = opts.tokenExchangeBodyFormat;
964
- if (opts.pingUrl !== undefined) params.pingUrl = opts.pingUrl;
965
- if (opts.pingMethod !== undefined)
966
- params.pingMethod = opts.pingMethod;
967
- if (opts.pingHeaders !== undefined)
968
- params.pingHeaders = JSON.parse(opts.pingHeaders);
969
- if (opts.pingBody !== undefined)
970
- params.pingBody = JSON.parse(opts.pingBody);
971
- if (opts.revokeUrl !== undefined) {
972
- // Empty string means "clear" — normalize to null so the stored
973
- // value matches the "disabled" semantics documented in the help
974
- // text. `updateProvider`'s Partial type accepts `string | null`
975
- // for this field so drizzle writes `null` to clear the column.
976
- params.revokeUrl = opts.revokeUrl === "" ? null : opts.revokeUrl;
977
- }
978
- if (opts.revokeBodyTemplate !== undefined) {
979
- // Empty string means "clear" — normalize to null to match --revoke-url's
980
- // empty-string-clear semantics documented in the help text. The
981
- // updateProvider type accepts `Record<string, string> | null` for this.
982
- params.revokeBodyTemplate =
983
- opts.revokeBodyTemplate === ""
984
- ? null
985
- : JSON.parse(opts.revokeBodyTemplate);
986
- }
987
- if (opts.displayName !== undefined)
988
- params.displayLabel = opts.displayName;
989
- if (opts.description !== undefined)
990
- params.description = opts.description;
991
- if (opts.dashboardUrl !== undefined)
992
- params.dashboardUrl = opts.dashboardUrl;
993
- if (opts.clientIdPlaceholder !== undefined)
994
- params.clientIdPlaceholder = opts.clientIdPlaceholder;
995
-
996
- const resolvedLogoUrl = resolveLogoUrlFromFlags(opts);
997
- if (resolvedLogoUrl !== undefined) {
998
- params.logoUrl = resolvedLogoUrl;
999
- }
1000
-
1001
- // Handle the negated --no-client-* flag: Commander defaults
1002
- // opts.clientSecret to true; the negated form sets it to false.
1003
- // Use getOptionValueSource to detect explicit user intent.
1004
- if (cmd.getOptionValueSource("clientSecret") === "cli") {
1005
- params.requiresClientSecret = opts.clientSecret ? 1 : 0;
1006
- }
767
+ )
768
+ .action(
769
+ async (
770
+ provider: string,
771
+ opts: {
772
+ authUrl?: string;
773
+ tokenUrl?: string;
774
+ refreshUrl?: string;
775
+ baseUrl?: string;
776
+ userinfoUrl?: string;
777
+ scopes?: string;
778
+ scopeSeparator?: string;
779
+ tokenAuthMethod?: string;
780
+ tokenExchangeBodyFormat?: string;
781
+ pingUrl?: string;
782
+ pingMethod?: string;
783
+ pingHeaders?: string;
784
+ pingBody?: string;
785
+ revokeUrl?: string;
786
+ revokeBodyTemplate?: string;
787
+ displayName?: string;
788
+ description?: string;
789
+ dashboardUrl?: string;
790
+ logoUrl?: string;
791
+ logoSimpleiconsSlug?: string;
792
+ clientIdPlaceholder?: string;
793
+ clientSecret: boolean;
794
+ loopbackPort?: string;
795
+ injectionTemplates?: string;
796
+ appType?: string;
797
+ identityUrl?: string;
798
+ identityMethod?: string;
799
+ identityHeaders?: string;
800
+ identityBody?: string;
801
+ identityResponsePaths?: string;
802
+ identityFormat?: string;
803
+ identityOkField?: string;
804
+ setupNotes?: string;
805
+ availableScopes?: string;
806
+ },
807
+ cmd: Command,
808
+ ) => {
809
+ try {
810
+ const body: Record<string, unknown> = {};
811
+
812
+ if (opts.authUrl !== undefined) body.auth_url = opts.authUrl;
813
+ if (opts.tokenUrl !== undefined) body.token_url = opts.tokenUrl;
814
+ if (opts.refreshUrl !== undefined)
815
+ body.refresh_url = opts.refreshUrl;
816
+ if (opts.baseUrl !== undefined) body.base_url = opts.baseUrl;
817
+ if (opts.userinfoUrl !== undefined)
818
+ body.userinfo_url = opts.userinfoUrl;
819
+ if (opts.scopes !== undefined)
820
+ body.default_scopes = opts.scopes.split(",");
821
+ if (opts.scopeSeparator !== undefined)
822
+ body.scope_separator = opts.scopeSeparator;
823
+ if (opts.tokenAuthMethod !== undefined)
824
+ body.token_endpoint_auth_method = opts.tokenAuthMethod;
825
+ if (opts.tokenExchangeBodyFormat !== undefined)
826
+ body.token_exchange_body_format = opts.tokenExchangeBodyFormat;
827
+ if (opts.pingUrl !== undefined) body.ping_url = opts.pingUrl;
828
+ if (opts.pingMethod !== undefined)
829
+ body.ping_method = opts.pingMethod;
830
+ if (opts.pingHeaders !== undefined)
831
+ body.ping_headers = JSON.parse(opts.pingHeaders);
832
+ if (opts.pingBody !== undefined)
833
+ body.ping_body = JSON.parse(opts.pingBody);
834
+ if (opts.revokeUrl !== undefined) {
835
+ body.revoke_url =
836
+ opts.revokeUrl === "" ? null : opts.revokeUrl;
837
+ }
838
+ if (opts.revokeBodyTemplate !== undefined) {
839
+ body.revoke_body_template =
840
+ opts.revokeBodyTemplate === ""
841
+ ? null
842
+ : JSON.parse(opts.revokeBodyTemplate);
843
+ }
844
+ if (opts.displayName !== undefined)
845
+ body.display_name = opts.displayName;
846
+ if (opts.description !== undefined)
847
+ body.description = opts.description;
848
+ if (opts.dashboardUrl !== undefined)
849
+ body.dashboard_url = opts.dashboardUrl;
850
+ if (opts.clientIdPlaceholder !== undefined)
851
+ body.client_id_placeholder = opts.clientIdPlaceholder;
852
+
853
+ const resolvedLogoUrl = resolveLogoUrlFromFlags(opts);
854
+ if (resolvedLogoUrl !== undefined) {
855
+ body.logo_url = resolvedLogoUrl;
856
+ }
857
+
858
+ if (cmd.getOptionValueSource("clientSecret") === "cli") {
859
+ body.requires_client_secret = opts.clientSecret;
860
+ }
861
+
862
+ if (opts.loopbackPort !== undefined)
863
+ body.loopback_port = parseInt(opts.loopbackPort, 10);
864
+ if (opts.injectionTemplates !== undefined)
865
+ body.injection_templates = JSON.parse(opts.injectionTemplates);
866
+ if (opts.appType !== undefined) body.app_type = opts.appType;
867
+ if (opts.identityUrl !== undefined)
868
+ body.identity_url = opts.identityUrl;
869
+ if (opts.identityMethod !== undefined)
870
+ body.identity_method = opts.identityMethod;
871
+ if (opts.identityHeaders !== undefined)
872
+ body.identity_headers = JSON.parse(opts.identityHeaders);
873
+ if (opts.identityBody !== undefined)
874
+ body.identity_body = JSON.parse(opts.identityBody);
875
+ if (opts.identityResponsePaths !== undefined)
876
+ body.identity_response_paths =
877
+ opts.identityResponsePaths.split(",");
878
+ if (opts.identityFormat !== undefined)
879
+ body.identity_format = opts.identityFormat;
880
+ if (opts.identityOkField !== undefined)
881
+ body.identity_ok_field = opts.identityOkField;
882
+ if (opts.setupNotes !== undefined)
883
+ body.setup_notes = JSON.parse(opts.setupNotes);
884
+ if (opts.availableScopes !== undefined) {
885
+ if (opts.availableScopes === "") {
886
+ body.available_scopes = null;
887
+ } else {
888
+ body.available_scopes =
889
+ opts.availableScopes.startsWith("http")
890
+ ? opts.availableScopes
891
+ : JSON.parse(opts.availableScopes);
892
+ }
893
+ }
894
+
895
+ if (Object.keys(body).length === 0) {
896
+ writeOutput(cmd, {
897
+ ok: false,
898
+ error:
899
+ "Nothing to update. Provide at least one option to change (e.g. --auth-url, --scopes, --display-name). Run 'assistant oauth providers update --help' for all options.",
900
+ });
901
+ process.exitCode = 1;
902
+ return;
903
+ }
904
+
905
+ const r = await cliIpcCall<{ provider: SerializedProvider }>(
906
+ "oauth_providers_by_providerKey_patch",
907
+ { pathParams: { providerKey: provider }, body },
908
+ );
1007
909
 
1008
- if (opts.loopbackPort !== undefined)
1009
- params.loopbackPort = parseInt(opts.loopbackPort, 10);
1010
- if (opts.injectionTemplates !== undefined)
1011
- params.injectionTemplates = JSON.parse(opts.injectionTemplates);
1012
- if (opts.appType !== undefined) params.appType = opts.appType;
1013
- if (opts.identityUrl !== undefined)
1014
- params.identityUrl = opts.identityUrl;
1015
- if (opts.identityMethod !== undefined)
1016
- params.identityMethod = opts.identityMethod;
1017
- if (opts.identityHeaders !== undefined)
1018
- params.identityHeaders = JSON.parse(opts.identityHeaders);
1019
- if (opts.identityBody !== undefined)
1020
- params.identityBody = JSON.parse(opts.identityBody);
1021
- if (opts.identityResponsePaths !== undefined)
1022
- params.identityResponsePaths =
1023
- opts.identityResponsePaths.split(",");
1024
- if (opts.identityFormat !== undefined)
1025
- params.identityFormat = opts.identityFormat;
1026
- if (opts.identityOkField !== undefined)
1027
- params.identityOkField = opts.identityOkField;
1028
- if (opts.setupNotes !== undefined)
1029
- params.setupNotes = JSON.parse(opts.setupNotes);
1030
- if (opts.availableScopes !== undefined) {
1031
- if (opts.availableScopes === "") {
1032
- params.availableScopes = null;
1033
- } else {
1034
- params.availableScopes = opts.availableScopes.startsWith("http")
1035
- ? opts.availableScopes
1036
- : JSON.parse(opts.availableScopes);
910
+ if (!r.ok) {
911
+ writeOutput(cmd, {
912
+ ok: false,
913
+ error: r.error ?? "Unknown error",
914
+ });
915
+ process.exitCode = 1;
916
+ return;
917
+ }
918
+
919
+ writeOutput(cmd, r.result?.provider);
920
+ } catch (err) {
921
+ const message = err instanceof Error ? err.message : String(err);
922
+ writeOutput(cmd, { ok: false, error: message });
923
+ process.exitCode = 1;
1037
924
  }
1038
- }
1039
-
1040
- // Check if any fields were actually provided
1041
- if (Object.keys(params).length === 0) {
1042
- writeOutput(cmd, {
1043
- ok: false,
1044
- error:
1045
- "Nothing to update. Provide at least one option to change (e.g. --auth-url, --scopes, --display-name). Run 'assistant oauth providers update --help' for all options.",
1046
- });
1047
- process.exitCode = 1;
1048
- return;
1049
- }
1050
-
1051
- const row = updateProvider(provider, params);
925
+ },
926
+ );
1052
927
 
1053
- writeOutput(cmd, parseProviderRow(row));
1054
- } catch (err) {
1055
- const message = err instanceof Error ? err.message : String(err);
1056
- writeOutput(cmd, { ok: false, error: message });
1057
- process.exitCode = 1;
1058
- }
1059
- },
1060
- );
928
+ // -----------------------------------------------------------------------
929
+ // providers delete <provider-key>
930
+ // -----------------------------------------------------------------------
1061
931
 
1062
- // ---------------------------------------------------------------------------
1063
- // providers delete <provider-key>
1064
- // ---------------------------------------------------------------------------
1065
-
1066
- providers
1067
- .command("delete <provider-key>")
1068
- .description(
1069
- "Delete a custom OAuth provider and optionally its associated apps and connections",
1070
- )
1071
- .option(
1072
- "--force",
1073
- "Cascade-delete all associated apps and connections before removing the provider",
1074
- )
1075
- .addHelpText(
1076
- "after",
1077
- `
932
+ providers
933
+ .command("delete <provider-key>")
934
+ .description(
935
+ "Delete a custom OAuth provider and optionally its associated apps and connections",
936
+ )
937
+ .option(
938
+ "--force",
939
+ "Cascade-delete all associated apps and connections before removing the provider",
940
+ )
941
+ .addHelpText(
942
+ "after",
943
+ `
1078
944
  Arguments:
1079
945
  provider-key Provider key to delete (e.g. "custom-api").
1080
946
  Run 'assistant oauth providers list' to see registered providers.
@@ -1092,92 +958,37 @@ Examples:
1092
958
  $ assistant oauth providers delete custom-api
1093
959
  $ assistant oauth providers delete custom-api --force
1094
960
  $ assistant oauth providers delete custom-api --force --json`,
1095
- )
1096
- .action(
1097
- async (provider: string, opts: { force?: boolean }, cmd: Command) => {
1098
- try {
1099
- const providerRow = getProvider(provider);
1100
- if (!providerRow) {
1101
- writeOutput(cmd, {
1102
- ok: false,
1103
- error: `Provider not found: "${provider}". Run 'assistant oauth providers list' to see all registered providers.`,
1104
- });
1105
- process.exitCode = 1;
1106
- return;
1107
- }
1108
-
1109
- if (!isProviderVisible(providerRow, loadConfig())) {
1110
- writeOutput(cmd, {
1111
- ok: false,
1112
- error: `Provider not found: "${provider}". Run 'assistant oauth providers list' to see all registered providers.`,
1113
- });
1114
- process.exitCode = 1;
1115
- return;
1116
- }
1117
-
1118
- if (SEEDED_PROVIDER_KEYS.has(provider) && !opts.force) {
1119
- log.info(
1120
- `Note: "${provider}" is a built-in provider and will be re-created on next startup.`,
1121
- );
1122
- }
1123
-
1124
- const dependentApps = listApps().filter(
1125
- (a) => a.provider === provider,
1126
- );
1127
- const dependentConnections = listConnections(provider);
1128
- const appCount = dependentApps.length;
1129
- const connCount = dependentConnections.length;
1130
-
1131
- if ((appCount > 0 || connCount > 0) && !opts.force) {
1132
- writeOutput(cmd, {
1133
- ok: false,
1134
- error: `Cannot delete provider "${provider}": ${appCount} app(s) and ${connCount} connection(s) depend on it. Use --force to cascade-delete all dependent apps and connections, or remove them manually first with 'assistant oauth apps delete' and 'assistant oauth disconnect'.`,
961
+ )
962
+ .action(
963
+ async (provider: string, opts: { force?: boolean }, cmd: Command) => {
964
+ const r = await cliIpcCall<{
965
+ ok: boolean;
966
+ deleted: {
967
+ provider: number;
968
+ apps: number;
969
+ connections: number;
970
+ };
971
+ }>("oauth_providers_by_providerKey_delete", {
972
+ pathParams: { providerKey: provider },
973
+ body: { force: opts.force ?? false },
1135
974
  });
1136
- process.exitCode = 1;
1137
- return;
1138
- }
1139
-
1140
- // Warn about built-in providers when --force is used
1141
- if (SEEDED_PROVIDER_KEYS.has(provider) && opts.force) {
1142
- log.info(
1143
- `Note: "${provider}" is a built-in provider and will be re-created on next startup.`,
1144
- );
1145
- }
1146
975
 
1147
- // Cascade-delete connections first, then apps, then the provider.
1148
- // Use disconnectOAuthProvider to clean up OAuth tokens from secure
1149
- // storage in addition to deleting the connection DB row.
1150
- for (const conn of dependentConnections) {
1151
- const result = await disconnectOAuthProvider(
1152
- provider,
1153
- undefined,
1154
- conn.id as string,
1155
- );
1156
- if (result === "error") {
1157
- log.info(
1158
- `Warning: failed to clean up tokens for connection ${conn.id} — deleting connection row to continue cascade.`,
1159
- );
1160
- deleteConnection(conn.id);
976
+ if (!r.ok) {
977
+ writeOutput(cmd, {
978
+ ok: false,
979
+ error: r.error ?? "Unknown error",
980
+ });
981
+ process.exitCode = 1;
982
+ return;
1161
983
  }
1162
- }
1163
- for (const app of dependentApps) {
1164
- await deleteApp(app.id);
1165
- }
1166
- deleteProvider(provider);
1167
984
 
1168
- if (!shouldOutputJson(cmd)) {
1169
- log.info(`Deleted provider: ${provider}`);
1170
- }
985
+ if (!shouldOutputJson(cmd)) {
986
+ log.info(`Deleted provider: ${provider}`);
987
+ }
1171
988
 
1172
- writeOutput(cmd, {
1173
- ok: true,
1174
- deleted: { provider: 1, apps: appCount, connections: connCount },
1175
- });
1176
- } catch (err) {
1177
- const message = err instanceof Error ? err.message : String(err);
1178
- writeOutput(cmd, { ok: false, error: message });
1179
- process.exitCode = 1;
1180
- }
1181
- },
1182
- );
989
+ writeOutput(cmd, r.result);
990
+ },
991
+ );
992
+ },
993
+ });
1183
994
  }