@vellumai/assistant 0.10.9 → 0.10.10-dev.202607162206.d08e98e

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 (456) hide show
  1. package/ARCHITECTURE.md +1 -1
  2. package/Dockerfile +8 -0
  3. package/docs/activation-funnel-telemetry.md +13 -7
  4. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  5. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
  6. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
  7. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  8. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
  9. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
  10. package/node_modules/@vellumai/service-contracts/package.json +1 -0
  11. package/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
  12. package/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
  13. package/openapi.yaml +530 -107
  14. package/package.json +1 -1
  15. package/scripts/generate-openapi.ts +8 -0
  16. package/src/__tests__/activation-early-marking.test.ts +6 -5
  17. package/src/__tests__/agent-loop-override-profile.test.ts +22 -25
  18. package/src/__tests__/agent-wake-override-profile.test.ts +21 -48
  19. package/src/__tests__/app-builder-tool-scripts.test.ts +0 -1
  20. package/src/__tests__/app-bundler.test.ts +4 -12
  21. package/src/__tests__/app-executors.test.ts +2 -49
  22. package/src/__tests__/app-routes-csp.test.ts +114 -146
  23. package/src/__tests__/auth-fallback-events-store.test.ts +8 -1
  24. package/src/__tests__/build-persisted-content.test.ts +96 -0
  25. package/src/__tests__/bundle-scanner.test.ts +27 -1
  26. package/src/__tests__/call-controller.test.ts +291 -0
  27. package/src/__tests__/call-site-routing-connection-auto-resolve.test.ts +165 -0
  28. package/src/__tests__/chat-credential-redaction.test.ts +1395 -0
  29. package/src/__tests__/chat-reveal-guard-priming.test.ts +791 -0
  30. package/src/__tests__/compaction.benchmark.test.ts +2 -1
  31. package/src/__tests__/compactor-image-manifest-trust.test.ts +50 -0
  32. package/src/__tests__/config-loader-backfill.test.ts +9 -4
  33. package/src/__tests__/config-schema-cmd.test.ts +10 -11
  34. package/src/__tests__/config-schema.test.ts +190 -257
  35. package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +208 -0
  36. package/src/__tests__/conversation-agent-loop-overflow.test.ts +5 -19
  37. package/src/__tests__/conversation-agent-loop.test.ts +22 -9
  38. package/src/__tests__/conversation-error.test.ts +31 -0
  39. package/src/__tests__/conversation-load-history-repair.test.ts +110 -0
  40. package/src/__tests__/conversation-process-callsite.test.ts +12 -19
  41. package/src/__tests__/conversation-routes-slash-commands.test.ts +7 -21
  42. package/src/__tests__/conversation-summarize-route.test.ts +36 -44
  43. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +1 -0
  44. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +7 -7
  45. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +0 -1
  46. package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -1
  47. package/src/__tests__/conversation-usage.test.ts +4 -12
  48. package/src/__tests__/credential-routes.test.ts +250 -0
  49. package/src/__tests__/credential-security-invariants.test.ts +3 -0
  50. package/src/__tests__/db-migration-rollback.test.ts +22 -0
  51. package/src/__tests__/empty-response-hook.test.ts +186 -1
  52. package/src/__tests__/external-plugin-loader.test.ts +11 -5
  53. package/src/__tests__/heartbeat-service.test.ts +0 -28
  54. package/src/__tests__/host-shell-tool.test.ts +2 -0
  55. package/src/__tests__/inactive-tool-error-messages.test.ts +2 -2
  56. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +52 -8
  57. package/src/__tests__/internal-telemetry-routes.test.ts +23 -4
  58. package/src/__tests__/invite-routes-http.test.ts +12 -16
  59. package/src/__tests__/list-all-apps.test.ts +0 -4
  60. package/src/__tests__/llm-context-resolution.test.ts +32 -56
  61. package/src/__tests__/llm-request-log-turn-query.test.ts +109 -0
  62. package/src/__tests__/llm-resolver-override-or-default.test.ts +3 -52
  63. package/src/__tests__/llm-resolver.test.ts +342 -602
  64. package/src/__tests__/llm-schema.test.ts +79 -37
  65. package/src/__tests__/max-tokens-continue-hook.test.ts +19 -0
  66. package/src/__tests__/media-stream-output.test.ts +259 -3
  67. package/src/__tests__/media-stream-server-integration.test.ts +22 -1
  68. package/src/__tests__/media-stream-stt-session.test.ts +47 -0
  69. package/src/__tests__/memory-jobs-worker-cleanup-cadence.test.ts +33 -0
  70. package/src/__tests__/memory-recall-log-store.test.ts +47 -13
  71. package/src/__tests__/mock-gateway-ipc.ts +46 -1
  72. package/src/__tests__/mtime-cache.test.ts +61 -0
  73. package/src/__tests__/navigate-settings-tab.test.ts +2 -0
  74. package/src/__tests__/normalize-onboarding.test.ts +33 -0
  75. package/src/__tests__/onboarding-persona-write.test.ts +26 -0
  76. package/src/__tests__/plugin-api-resolve-credential.test.ts +140 -0
  77. package/src/__tests__/plugin-app-serve-routes.test.ts +166 -14
  78. package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +2 -0
  79. package/src/__tests__/post-turn-tool-result-truncation.test.ts +38 -0
  80. package/src/__tests__/provider-commit-message-generator.test.ts +27 -20
  81. package/src/__tests__/provider-connections-backfill.test.ts +138 -0
  82. package/src/__tests__/provider-platform-proxy-integration.test.ts +10 -31
  83. package/src/__tests__/provider-registry-ollama.test.ts +8 -18
  84. package/src/__tests__/provider-send-message-override-profile.test.ts +23 -23
  85. package/src/__tests__/provider-usage-tracking.test.ts +9 -19
  86. package/src/__tests__/prune-old-conversations-job.test.ts +12 -0
  87. package/src/__tests__/published-app-updater.test.ts +22 -16
  88. package/src/__tests__/registry.test.ts +5 -16
  89. package/src/__tests__/retry-openrouter-only-normalization.test.ts +22 -23
  90. package/src/__tests__/retry-thinking-adaptive-only.test.ts +43 -48
  91. package/src/__tests__/retry-thinking-tool-choice.test.ts +57 -66
  92. package/src/__tests__/retry-verbosity-normalization.test.ts +24 -23
  93. package/src/__tests__/reveal-success-registry.test.ts +123 -0
  94. package/src/__tests__/run-conversation-turn-persistence.test.ts +130 -0
  95. package/src/__tests__/secret-fixtures.ts +9 -0
  96. package/src/__tests__/server-history-render.test.ts +28 -0
  97. package/src/__tests__/skills.test.ts +9 -4
  98. package/src/__tests__/slack-share-routes.test.ts +0 -1
  99. package/src/__tests__/stt-stream-session.test.ts +6 -5
  100. package/src/__tests__/subagent-call-site-routing.test.ts +69 -95
  101. package/src/__tests__/subagent-disposal.test.ts +2 -0
  102. package/src/__tests__/subagent-fork-notifications.test.ts +2 -0
  103. package/src/__tests__/subagent-fork-spawn.test.ts +2 -0
  104. package/src/__tests__/subagent-manager-notify.test.ts +2 -0
  105. package/src/__tests__/subagent-role-registry.test.ts +37 -0
  106. package/src/__tests__/subagent-spawn-and-await.test.ts +1 -0
  107. package/src/__tests__/subagent-terminal-message.test.ts +50 -0
  108. package/src/__tests__/subagent-tool-gate-mode.test.ts +78 -5
  109. package/src/__tests__/surface-completion-nudge-hook.test.ts +19 -0
  110. package/src/__tests__/telemetry-routes.test.ts +99 -19
  111. package/src/__tests__/tool-audit.test.ts +34 -4
  112. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  113. package/src/__tests__/tool-profiler.test.ts +72 -1
  114. package/src/__tests__/tool-result-spool.test.ts +49 -4
  115. package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -1
  116. package/src/__tests__/ui-channel-variants.test.ts +108 -0
  117. package/src/__tests__/ui-shape-teaching.test.ts +255 -0
  118. package/src/__tests__/usage-attribution.test.ts +18 -41
  119. package/src/__tests__/user-plugin-loader.test.ts +4 -4
  120. package/src/__tests__/voice-config-update.test.ts +46 -0
  121. package/src/__tests__/voice-session-bridge.test.ts +216 -84
  122. package/src/__tests__/workspace-migration-131-drop-web-fetch-mode.test.ts +120 -0
  123. package/src/agent/loop.ts +4 -3
  124. package/src/api/events/open-conversation.test.ts +64 -0
  125. package/src/api/events/open-conversation.ts +33 -0
  126. package/src/api/index.ts +6 -0
  127. package/src/api/responses/conversation-message.ts +5 -0
  128. package/src/apps/app-store.ts +25 -35
  129. package/src/bundler/app-bundler.ts +34 -48
  130. package/src/bundler/app-compiler.ts +39 -4
  131. package/src/bundler/bundle-scanner.ts +13 -0
  132. package/src/bundler/manifest.ts +1 -1
  133. package/src/calls/__tests__/voice-session-bridge.test.ts +47 -0
  134. package/src/calls/call-constants.ts +5 -0
  135. package/src/calls/call-controller.ts +100 -32
  136. package/src/calls/call-transport.ts +9 -0
  137. package/src/calls/media-stream-output.ts +107 -4
  138. package/src/calls/media-stream-server.ts +29 -9
  139. package/src/calls/media-stream-stt-session.ts +7 -1
  140. package/src/calls/media-turn-detector.ts +11 -1
  141. package/src/calls/voice-session-bridge.ts +84 -63
  142. package/src/cli/commands/__tests__/inference-providers.test.ts +270 -33
  143. package/src/cli/commands/__tests__/notifications.test.ts +24 -3
  144. package/src/cli/commands/config.help.ts +6 -6
  145. package/src/cli/commands/credentials.help.ts +13 -0
  146. package/src/cli/commands/credentials.ts +6 -1
  147. package/src/cli/commands/email.help.ts +7 -0
  148. package/src/cli/commands/email.ts +35 -1
  149. package/src/cli/commands/inference-providers.ts +168 -107
  150. package/src/cli/commands/inference.help.ts +118 -46
  151. package/src/cli/commands/memory/index.help.ts +23 -0
  152. package/src/cli/commands/memory/nodes.ts +146 -0
  153. package/src/cli/commands/notifications.help.ts +10 -10
  154. package/src/cli/commands/oauth/connect-surface-guidance.test.ts +40 -0
  155. package/src/cli/commands/oauth/connect-surface-guidance.ts +54 -0
  156. package/src/cli/commands/oauth/connect.test.ts +126 -0
  157. package/src/cli/commands/oauth/connect.ts +29 -6
  158. package/src/cli/commands/oauth/index.help.ts +7 -1
  159. package/src/cli/commands/oauth/status.test.ts +69 -3
  160. package/src/cli/commands/oauth/status.ts +50 -12
  161. package/src/cli/commands/plugins.help.ts +13 -2
  162. package/src/cli/commands/plugins.ts +61 -7
  163. package/src/cli/commands/telemetry.help.ts +13 -0
  164. package/src/cli/commands/telemetry.ts +45 -5
  165. package/src/cli/lib/__tests__/plugin-catalog-local.test.ts +8 -2
  166. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +213 -9
  167. package/src/cli/lib/bundled-marketplace.json +93 -36
  168. package/src/cli/lib/inspect-plugin.ts +5 -1
  169. package/src/cli/lib/install-from-github.ts +68 -4
  170. package/src/cli/lib/plugin-catalog-local.ts +6 -2
  171. package/src/cli/lib/plugin-fingerprint.ts +3 -3
  172. package/src/cli/lib/upgrade-plugin.ts +236 -35
  173. package/src/config/__tests__/default-profile-catalog.test.ts +8 -12
  174. package/src/config/__tests__/plugin-resident-skill-discovery.test.ts +137 -0
  175. package/src/config/__tests__/profile-materialization.test.ts +1 -88
  176. package/src/config/bundled-skills/AGENTS.md +3 -30
  177. package/src/config/bundled-skills/app-builder/SKILL.md +5 -3
  178. package/src/config/bundled-skills/app-builder/TOOLS.json +23 -0
  179. package/src/config/bundled-skills/app-builder/tools/app-open.ts +32 -0
  180. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +1 -1
  181. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +7 -7
  182. package/src/config/bundled-skills/schedule/SKILL.md +1 -1
  183. package/src/config/bundled-skills/schedule/TOOLS.json +1 -1
  184. package/src/config/bundled-skills/settings/TOOLS.json +3 -1
  185. package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +2 -0
  186. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +18 -10
  187. package/src/config/bundled-skills/subagent/SKILL.md +2 -0
  188. package/src/config/bundled-skills/subagent/TOOLS.json +1 -1
  189. package/src/config/call-site-defaults.ts +3 -3
  190. package/src/config/feature-flag-registry.json +24 -39
  191. package/src/config/llm-resolver.ts +88 -475
  192. package/src/config/profile-materialization.ts +11 -11
  193. package/src/config/schema.ts +93 -127
  194. package/src/config/schemas/__tests__/live-voice.test.ts +24 -6
  195. package/src/config/schemas/__tests__/stt.test.ts +31 -3
  196. package/src/config/schemas/live-voice.ts +10 -4
  197. package/src/config/schemas/llm.ts +52 -15
  198. package/src/config/schemas/memory-lifecycle.ts +1 -1
  199. package/src/config/schemas/memory-retrospective.ts +8 -0
  200. package/src/config/schemas/memory-v2.ts +2 -2
  201. package/src/config/schemas/memory-v3.ts +1 -1
  202. package/src/config/schemas/services.ts +6 -3
  203. package/src/config/schemas/stt.ts +38 -21
  204. package/src/config/schemas/tts.ts +13 -18
  205. package/src/config/skills.ts +31 -21
  206. package/src/context/compactor.ts +44 -7
  207. package/src/context/post-turn-tool-result-truncation.ts +4 -2
  208. package/src/context/tool-result-spool.ts +26 -5
  209. package/src/conversations/__tests__/message-consolidation.test.ts +48 -0
  210. package/src/conversations/message-consolidation.ts +22 -2
  211. package/src/daemon/__tests__/conversation-tool-setup.test.ts +5 -12
  212. package/src/daemon/app-source-watcher.ts +17 -23
  213. package/src/daemon/chat-credential-redaction.ts +1365 -0
  214. package/src/daemon/conversation-agent-loop-handlers.ts +627 -34
  215. package/src/daemon/conversation-agent-loop.ts +71 -31
  216. package/src/daemon/conversation-error.ts +36 -14
  217. package/src/daemon/conversation-process.ts +22 -0
  218. package/src/daemon/conversation-store.ts +35 -0
  219. package/src/daemon/conversation-surfaces.ts +25 -25
  220. package/src/daemon/conversation-tool-setup.ts +33 -7
  221. package/src/daemon/conversation.ts +55 -5
  222. package/src/daemon/handlers/shared.ts +33 -2
  223. package/src/daemon/lifecycle.ts +4 -4
  224. package/src/daemon/message-types/conversations.ts +6 -17
  225. package/src/daemon/providers-setup.ts +8 -0
  226. package/src/daemon/tool-setup-types.ts +7 -1
  227. package/src/daemon/wake-conversation-ops.ts +10 -1
  228. package/src/hooks/types.ts +9 -0
  229. package/src/ipc/__tests__/email-ipc.test.ts +90 -0
  230. package/src/ipc/gateway-client.test.ts +59 -0
  231. package/src/ipc/gateway-client.ts +70 -27
  232. package/src/live-voice/__tests__/live-voice-events.test.ts +14 -2
  233. package/src/live-voice/__tests__/live-voice-integration.test.ts +116 -2
  234. package/src/live-voice/__tests__/live-voice-vad.test.ts +804 -13
  235. package/src/live-voice/__tests__/protocol.test.ts +122 -0
  236. package/src/live-voice/live-voice-session.ts +443 -40
  237. package/src/live-voice/protocol.ts +143 -1
  238. package/src/monitoring/__tests__/plugin-source-watch.test.ts +3 -1
  239. package/src/monitoring/plugin-source-watch.ts +3 -62
  240. package/src/notifications/README.md +1 -1
  241. package/src/permissions/checker.ts +8 -4
  242. package/src/persistence/__tests__/db-init-migrations-ok.test.ts +26 -0
  243. package/src/persistence/conversation-crud.ts +104 -2
  244. package/src/persistence/db-init.ts +14 -3
  245. package/src/persistence/job-handlers/cleanup.ts +15 -7
  246. package/src/persistence/llm-request-log-store.ts +88 -52
  247. package/src/persistence/migrations/298-move-memory-jobs-to-memory-db.ts +7 -31
  248. package/src/persistence/migrations/305-drop-contact-acl-columns.ts +3 -2
  249. package/src/persistence/migrations/326-move-injection-events-to-memory-db.ts +8 -34
  250. package/src/persistence/migrations/336-move-memory-v2-activation-logs-to-memory-db.ts +90 -0
  251. package/src/persistence/migrations/337-move-memory-recall-logs-to-memory-db.ts +114 -0
  252. package/src/persistence/migrations/338-move-memory-v3-selections-to-memory-db.ts +84 -0
  253. package/src/persistence/migrations/339-move-activation-sessions-to-memory-db.ts +52 -0
  254. package/src/persistence/migrations/__tests__/run-migrations.test.ts +155 -0
  255. package/src/persistence/migrations/helpers/relocation.ts +44 -1
  256. package/src/persistence/migrations/run-migrations.ts +25 -1
  257. package/src/persistence/schema/infrastructure.ts +9 -0
  258. package/src/persistence/schema/memory-core.ts +3 -0
  259. package/src/persistence/schema/memory-injection.ts +2 -0
  260. package/src/persistence/steps.ts +36 -0
  261. package/src/platform/client.test.ts +1 -44
  262. package/src/platform/client.ts +10 -20
  263. package/src/platform/consent-cache.test.ts +89 -23
  264. package/src/platform/consent-cache.ts +71 -33
  265. package/src/plugin-api/constants.ts +12 -0
  266. package/src/plugin-api/conversation-turn.ts +37 -14
  267. package/src/plugin-api/index.ts +11 -1
  268. package/src/plugin-api/resolve-credential.ts +75 -0
  269. package/src/plugin-api/vision-support.test.ts +8 -19
  270. package/src/plugin-api/vision-support.ts +26 -17
  271. package/src/plugins/collect-source-versions.ts +77 -0
  272. package/src/plugins/defaults/compaction/compact.ts +6 -0
  273. package/src/plugins/defaults/compaction/window-manager.ts +9 -0
  274. package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +18 -24
  275. package/src/plugins/defaults/empty-response/hooks/user-prompt-submit.ts +38 -0
  276. package/src/plugins/defaults/empty-response/refusal-quarantine.ts +99 -0
  277. package/src/plugins/defaults/image-fallback/__tests__/caption-cache-persistence.test.ts +7 -3
  278. package/src/plugins/defaults/index.ts +8 -1
  279. package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +4 -1
  280. package/src/plugins/defaults/memory/__tests__/activation-session-store.test.ts +48 -6
  281. package/src/plugins/defaults/memory/__tests__/db-memory-attach.test.ts +28 -22
  282. package/src/plugins/defaults/memory/__tests__/memory-log-stores-degraded.test.ts +148 -0
  283. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +80 -8
  284. package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +202 -0
  285. package/src/plugins/defaults/memory/__tests__/memory-v2-activation-log-store.test.ts +64 -25
  286. package/src/plugins/defaults/memory/__tests__/memory-v2-concept-frequency.test.ts +24 -11
  287. package/src/plugins/defaults/memory/__tests__/prompt-override.test.ts +70 -6
  288. package/src/plugins/defaults/memory/__tests__/table-relocation.test.ts +278 -0
  289. package/src/plugins/defaults/memory/activation-session-store.ts +27 -20
  290. package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +2 -9
  291. package/src/plugins/defaults/memory/context-search/sources/workspace.ts +1 -8
  292. package/src/plugins/defaults/memory/graph/retriever.test.ts +6 -6
  293. package/src/plugins/defaults/memory/graph/store.ts +114 -0
  294. package/src/plugins/defaults/memory/graph/tool-handlers.ts +36 -1
  295. package/src/plugins/defaults/memory/graph/tools.ts +47 -13
  296. package/src/plugins/defaults/memory/graph-topology/build-memory-graph.ts +16 -35
  297. package/src/plugins/defaults/memory/jobs-worker.ts +10 -1
  298. package/src/plugins/defaults/memory/memory-db.ts +3 -2
  299. package/src/plugins/defaults/memory/memory-recall-log-store.ts +129 -66
  300. package/src/plugins/defaults/memory/memory-retrospective-constants.ts +8 -0
  301. package/src/plugins/defaults/memory/memory-retrospective-job.ts +11 -122
  302. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +216 -0
  303. package/src/plugins/defaults/memory/memory-v2-activation-log-store.ts +84 -46
  304. package/src/plugins/defaults/memory/memory-v2-concept-frequency.ts +42 -32
  305. package/src/plugins/defaults/memory/path-containment.ts +21 -0
  306. package/src/plugins/defaults/memory/prompt-override.ts +51 -13
  307. package/src/plugins/defaults/memory/tools.test.ts +34 -0
  308. package/src/plugins/defaults/memory/tools.ts +12 -2
  309. package/src/plugins/defaults/memory/v2/__tests__/harness-compare.test.ts +19 -15
  310. package/src/plugins/defaults/memory/v2/__tests__/harness-oracle.test.ts +24 -19
  311. package/src/plugins/defaults/memory/v2/__tests__/harness-replay-input.test.ts +19 -15
  312. package/src/plugins/defaults/memory/v2/__tests__/injection.test.ts +22 -2
  313. package/src/plugins/defaults/memory/v2/__tests__/prompts-consolidation.test.ts +5 -3
  314. package/src/plugins/defaults/memory/v2/harness/oracle.ts +59 -41
  315. package/src/plugins/defaults/memory/v2/harness/replay-input.ts +29 -25
  316. package/src/plugins/defaults/memory/v2/migration.ts +46 -16
  317. package/src/plugins/defaults/memory/v2/prompts/consolidation.ts +4 -0
  318. package/src/plugins/defaults/memory/v3/__tests__/carry-integration.test.ts +17 -15
  319. package/src/plugins/defaults/memory/v3/__tests__/gate.test.ts +24 -22
  320. package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +9 -5
  321. package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +277 -10
  322. package/src/plugins/defaults/memory/v3/__tests__/selection-log-store.test.ts +57 -5
  323. package/src/plugins/defaults/memory/v3/__tests__/shadow-integration.test.ts +9 -7
  324. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +38 -7
  325. package/src/plugins/defaults/memory/v3/hot-set.test.ts +36 -18
  326. package/src/plugins/defaults/memory/v3/hot-set.ts +12 -14
  327. package/src/plugins/defaults/memory/v3/learned-edges.test.ts +47 -27
  328. package/src/plugins/defaults/memory/v3/learned-edges.ts +12 -14
  329. package/src/plugins/defaults/memory/v3/orchestrate.ts +139 -22
  330. package/src/plugins/defaults/memory/v3/prune.test.ts +10 -3
  331. package/src/plugins/defaults/memory/v3/prune.ts +17 -11
  332. package/src/plugins/defaults/memory/v3/selection-log-store.ts +23 -11
  333. package/src/plugins/defaults/memory/v3/shadow-plugin.ts +70 -53
  334. package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +9 -6
  335. package/src/plugins/external-plugin-loader.ts +15 -15
  336. package/src/plugins/mtime-cache.ts +47 -0
  337. package/src/plugins/pipeline.ts +9 -1
  338. package/src/plugins/plugin-execution-context.ts +44 -0
  339. package/src/plugins/plugin-tree-walk.ts +31 -24
  340. package/src/plugins/source-fingerprint.ts +3 -4
  341. package/src/prompts/normalize-onboarding.ts +12 -0
  342. package/src/prompts/persona-resolver.ts +8 -0
  343. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +1 -1
  344. package/src/providers/__tests__/dispatch-connection-routing.test.ts +6 -9
  345. package/src/providers/__tests__/registry-native-web-search.test.ts +4 -10
  346. package/src/providers/__tests__/retry-callsite.test.ts +235 -215
  347. package/src/providers/__tests__/satellite-connection-routing.test.ts +8 -16
  348. package/src/providers/atlascloud/client.ts +10 -49
  349. package/src/providers/baseten/client.ts +43 -0
  350. package/src/providers/call-site-routing.ts +26 -13
  351. package/src/providers/connection-resolution.ts +9 -11
  352. package/src/providers/fetch-provider-catalog.ts +4 -2
  353. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +29 -4
  354. package/src/providers/inference/__tests__/connection-availability-keyless.test.ts +78 -0
  355. package/src/providers/inference/adapter-factory.ts +27 -6
  356. package/src/providers/inference/auth.ts +29 -1
  357. package/src/providers/inference/backfill.ts +61 -10
  358. package/src/providers/inference/connection-availability.ts +3 -2
  359. package/src/providers/inference/resolve-auth.ts +9 -1
  360. package/src/providers/model-catalog.ts +37 -0
  361. package/src/providers/openai/__tests__/api-error-normalization.test.ts +24 -2
  362. package/src/providers/openai/api-key-validation.ts +70 -0
  363. package/src/providers/retry.ts +2 -0
  364. package/src/providers/types.ts +11 -4
  365. package/src/providers/vellum-model-routing.test.ts +26 -0
  366. package/src/providers/vellum-model-routing.ts +30 -0
  367. package/src/providers/voice-error-copy.ts +47 -0
  368. package/src/runtime/agent-wake.ts +40 -24
  369. package/src/runtime/for-chat-mint-registry.ts +118 -0
  370. package/src/runtime/reveal-nonce.ts +49 -0
  371. package/src/runtime/reveal-success-registry.ts +306 -0
  372. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +88 -3
  373. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +161 -30
  374. package/src/runtime/routes/__tests__/migration-vellum-metadata-reconcile.test.ts +7 -0
  375. package/src/runtime/routes/__tests__/schedule-worker-routes.test.ts +15 -0
  376. package/src/runtime/routes/app-management-routes.ts +152 -45
  377. package/src/runtime/routes/app-routes.ts +16 -77
  378. package/src/runtime/routes/canned-message-complete.ts +16 -16
  379. package/src/runtime/routes/conversation-management-routes.ts +17 -14
  380. package/src/runtime/routes/conversation-query-routes.ts +54 -6
  381. package/src/runtime/routes/conversation-routes.ts +48 -2
  382. package/src/runtime/routes/credential-routes.ts +116 -3
  383. package/src/runtime/routes/email-routes.ts +17 -1
  384. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +33 -5
  385. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +6 -5
  386. package/src/runtime/routes/inference-provider-connection-routes.ts +77 -28
  387. package/src/runtime/routes/inference-send-routes.ts +1 -1
  388. package/src/runtime/routes/internal-telemetry-routes.ts +7 -6
  389. package/src/runtime/routes/plugins-routes.ts +37 -6
  390. package/src/runtime/routes/publish-routes.ts +15 -18
  391. package/src/runtime/routes/schedule-worker-routes.ts +9 -0
  392. package/src/runtime/routes/secret-routes.ts +10 -0
  393. package/src/runtime/routes/telemetry-routes.ts +149 -45
  394. package/src/schedule/__tests__/schedule-timezone.test.ts +101 -0
  395. package/src/schedule/__tests__/worker-watchdog.test.ts +209 -0
  396. package/src/schedule/schedule-store.ts +12 -1
  397. package/src/schedule/schedule-timezone.ts +63 -0
  398. package/src/schedule/scheduler.ts +100 -6
  399. package/src/schedule/worker-control.ts +19 -0
  400. package/src/security/auth-fallback-events-store.ts +7 -6
  401. package/src/security/secret-scanner.ts +26 -1
  402. package/src/services/published-app-updater.ts +6 -11
  403. package/src/stt/stt-stream-session.ts +36 -16
  404. package/src/subagent/manager.ts +34 -8
  405. package/src/telemetry/AGENTS.md +30 -1
  406. package/src/telemetry/__tests__/config-setting-snapshot.test.ts +18 -0
  407. package/src/telemetry/__tests__/outbox-test-harness.ts +5 -3
  408. package/src/telemetry/config-setting-snapshot.ts +44 -10
  409. package/src/telemetry/telemetry-event-sources.test.ts +124 -30
  410. package/src/telemetry/telemetry-event-sources.ts +137 -83
  411. package/src/telemetry/telemetry-events-outbox.test.ts +46 -1
  412. package/src/telemetry/telemetry-events-outbox.ts +60 -9
  413. package/src/telemetry/telemetry-wire-source.json +1 -1
  414. package/src/telemetry/telemetry-wire-validation.ts +39 -2
  415. package/src/telemetry/telemetry-wire.generated.ts +8 -0
  416. package/src/telemetry/tool-audit.ts +15 -9
  417. package/src/telemetry/tool-executed-events-store.test.ts +1 -1
  418. package/src/telemetry/turn-events-store.ts +20 -0
  419. package/src/telemetry/types.ts +45 -12
  420. package/src/telemetry/usage-telemetry-reporter.test.ts +294 -25
  421. package/src/telemetry/usage-telemetry-reporter.ts +117 -18
  422. package/src/telemetry/watchdog-direct-emit.test.ts +11 -3
  423. package/src/telemetry/watchdog-direct-emit.ts +11 -6
  424. package/src/tools/apps/executors.ts +11 -36
  425. package/src/tools/executor.ts +14 -1
  426. package/src/tools/host-terminal/host-shell.ts +6 -2
  427. package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +1 -1
  428. package/src/tools/network/__tests__/web-fetch-metadata.test.ts +25 -0
  429. package/src/tools/network/web-fetch.ts +6 -2
  430. package/src/tools/skills/sandbox-runner.ts +5 -2
  431. package/src/tools/subagent/spawn.ts +7 -11
  432. package/src/tools/terminal/shell.ts +5 -2
  433. package/src/tools/tool-manifest.ts +0 -2
  434. package/src/tools/tool-profiler.ts +37 -6
  435. package/src/tools/ui-surface/channel-variants.ts +101 -0
  436. package/src/tools/ui-surface/definitions.ts +23 -67
  437. package/src/tools/ui-surface/surface-shape-docs.ts +229 -0
  438. package/src/tts/__tests__/provider-adapters.test.ts +18 -0
  439. package/src/tts/provider-catalog.ts +2 -4
  440. package/src/tts/providers/deepgram-provider.ts +10 -1
  441. package/src/types/onboarding-context.ts +8 -0
  442. package/src/usage/attribution.ts +18 -112
  443. package/src/{config/bundled-skills/messaging/tools/gmail-mime-helpers.ts → util/mime-type.ts} +4 -1
  444. package/src/util/provider-error-patterns.ts +7 -1
  445. package/src/util/worker-process.ts +105 -0
  446. package/src/watcher/__tests__/telemetry.test.ts +17 -4
  447. package/src/watcher/telemetry.ts +5 -5
  448. package/src/workspace/migrations/131-drop-web-fetch-mode.ts +61 -0
  449. package/src/workspace/migrations/registry.ts +2 -0
  450. package/src/workspace/provider-commit-message-generator.ts +6 -4
  451. package/src/__tests__/app-open-proxy.test.ts +0 -67
  452. package/src/onboarding/onboarding-research-events-store.test.ts +0 -230
  453. package/src/onboarding/onboarding-research-events-store.ts +0 -104
  454. package/src/runtime/routes/assets/vellum-design-system.css +0 -2236
  455. package/src/tools/apps/definitions.ts +0 -73
  456. package/src/tools/apps/open-proxy.ts +0 -43
@@ -26,6 +26,7 @@ model (section-grain lanes cached as live shadow state). Each subgroup exposes
26
26
  operator-facing maintenance verbs — reindexing, backfills, validation, and evals.
27
27
 
28
28
  Examples:
29
+ $ assistant memory nodes stats
29
30
  $ assistant memory nodes list --search "coffee"
30
31
  $ assistant memory nodes delete "User prefers TypeScript"
31
32
  $ assistant memory nodes update "User prefers TypeScript" "User prefers TypeScript and Bun"
@@ -48,11 +49,33 @@ a remembered fact without first looking up its ID.
48
49
  All subcommands require memory v2 to be enabled and the assistant to be running.
49
50
 
50
51
  Examples:
52
+ $ assistant memory nodes stats
51
53
  $ assistant memory nodes list
52
54
  $ assistant memory nodes list --search "TypeScript" --limit 20
53
55
  $ assistant memory nodes delete "User prefers TypeScript"
54
56
  $ assistant memory nodes update "User prefers TypeScript" "User prefers TypeScript and Bun"`,
55
57
  subcommands: [
58
+ {
59
+ name: "stats",
60
+ description: "Show a health overview of the memory v2 graph",
61
+ options: [
62
+ {
63
+ flags: "--json",
64
+ description: "Machine-readable compact JSON output",
65
+ },
66
+ ],
67
+ helpText: `
68
+ Behavior:
69
+ Scans all active memory graph nodes in a single pass and reports:
70
+ total count, breakdown by type and fidelity (with an ASCII bar chart),
71
+ nodes at risk of decay (significance < 15%), average significance,
72
+ oldest/newest node timestamps, last reinforcement, and the top 5 nodes
73
+ by significance. Does not require the daemon to be running.
74
+
75
+ Examples:
76
+ $ assistant memory nodes stats
77
+ $ assistant memory nodes stats --json`,
78
+ },
56
79
  {
57
80
  name: "list",
58
81
  description: "List active memory graph nodes",
@@ -203,4 +203,150 @@ export function registerMemoryNodesCommand(memory: Command): void {
203
203
  log.info(result.message);
204
204
  },
205
205
  );
206
+
207
+ // ── stats ─────────────────────────────────────────────────────────────
208
+
209
+ subcommand(nodes, "stats").action(
210
+ async (opts: { json?: boolean }, cmd: Command) => {
211
+ // Deferred: loads config and the graph stats handler in-process.
212
+ // No daemon needed — reads directly from the workspace SQLite DB.
213
+ const [{ handleStatsMemory }, { getConfig }] = await Promise.all([
214
+ import("../../../plugins/defaults/memory/graph/tool-handlers.js") as Promise<
215
+ typeof import("../../../plugins/defaults/memory/graph/tool-handlers.js")
216
+ >,
217
+ import("../../../config/loader.js") as Promise<
218
+ typeof import("../../../config/loader.js")
219
+ >,
220
+ ]);
221
+
222
+ const result = handleStatsMemory(getConfig());
223
+
224
+ if (!result.success) {
225
+ log.error(result.message);
226
+ process.exitCode = 1;
227
+ return;
228
+ }
229
+
230
+ if (opts.json) {
231
+ writeOutput(cmd, result.stats);
232
+ return;
233
+ }
234
+
235
+ printStats(result.stats!);
236
+ },
237
+ );
238
+ }
239
+
240
+ const MEMORY_TYPES = [
241
+ "episodic",
242
+ "semantic",
243
+ "procedural",
244
+ "emotional",
245
+ "prospective",
246
+ "behavioral",
247
+ "narrative",
248
+ "shared",
249
+ ] as const;
250
+
251
+ interface StatsData {
252
+ total: number;
253
+ byType: { [key: string]: number | undefined };
254
+ byFidelity: { vivid: number; clear: number; faded: number; gist: number };
255
+ atRisk: number;
256
+ edgeCount: number;
257
+ oldestCreated: number | null;
258
+ newestCreated: number | null;
259
+ lastReinforced: number | null;
260
+ avgSignificance: number;
261
+ topNodes: ReadonlyArray<{ content: string; significance: number }>;
262
+ }
263
+
264
+ function printStats(s: StatsData): void {
265
+ const n = s.total;
266
+ const e = s.edgeCount;
267
+
268
+ console.log(
269
+ `\nMemory graph ${n} node${n === 1 ? "" : "s"} · ${e} edge${e === 1 ? "" : "s"}\n`,
270
+ );
271
+
272
+ if (n === 0) {
273
+ console.log(" The memory graph is empty.");
274
+ console.log("");
275
+ return;
276
+ }
277
+
278
+ // ── by type ─────────────────────────────────────────────────────────────
279
+
280
+ console.log("BY TYPE");
281
+ for (const t of MEMORY_TYPES) {
282
+ const count = s.byType[t] ?? 0;
283
+ if (count > 0) {
284
+ console.log(` ${t.padEnd(12)} ${count}`);
285
+ }
286
+ }
287
+
288
+ // ── by fidelity (with ASCII bar) ─────────────────────────────────────────
289
+
290
+ console.log("\nBY FIDELITY");
291
+ const fidelities = ["vivid", "clear", "faded", "gist"] as const;
292
+ for (const f of fidelities) {
293
+ const count = s.byFidelity[f];
294
+ const barLen = n > 0 ? Math.round((count / n) * 20) : 0;
295
+ const bar = "█".repeat(barLen);
296
+ console.log(` ${f.padEnd(6)} ${String(count).padStart(4)} ${bar}`);
297
+ }
298
+
299
+ // ── significance ─────────────────────────────────────────────────────────
300
+
301
+ console.log("\nSIGNIFICANCE");
302
+ console.log(` average ${(s.avgSignificance * 100).toFixed(1)}%`);
303
+ if (s.atRisk > 0) {
304
+ console.log(
305
+ ` at risk ${s.atRisk} node${s.atRisk === 1 ? "" : "s"} fading (significance < 15%)`,
306
+ );
307
+ }
308
+
309
+ // ── timeline ─────────────────────────────────────────────────────────────
310
+
311
+ console.log("\nTIMELINE");
312
+ const dateOpts: Intl.DateTimeFormatOptions = {
313
+ month: "short",
314
+ day: "numeric",
315
+ year: "numeric",
316
+ };
317
+ if (s.oldestCreated) {
318
+ console.log(
319
+ ` oldest ${new Date(s.oldestCreated).toLocaleString("en-US", dateOpts)}`,
320
+ );
321
+ }
322
+ if (s.newestCreated) {
323
+ console.log(
324
+ ` newest ${new Date(s.newestCreated).toLocaleString("en-US", dateOpts)}`,
325
+ );
326
+ }
327
+ if (s.lastReinforced) {
328
+ console.log(
329
+ ` reinforced ${new Date(s.lastReinforced).toLocaleString("en-US", {
330
+ ...dateOpts,
331
+ hour: "numeric",
332
+ minute: "2-digit",
333
+ })}`,
334
+ );
335
+ }
336
+
337
+ // ── top nodes ────────────────────────────────────────────────────────────
338
+
339
+ if (s.topNodes.length > 0) {
340
+ console.log("\nTOP NODES BY SIGNIFICANCE");
341
+ const WIDTH = 60;
342
+ const trunc = (str: string) =>
343
+ str.length > WIDTH ? str.slice(0, WIDTH - 1) + "…" : str;
344
+ for (const node of s.topNodes) {
345
+ console.log(
346
+ ` ${(node.significance * 100).toFixed(0).padStart(3)}% ${trunc(node.content)}`,
347
+ );
348
+ }
349
+ }
350
+
351
+ console.log("");
206
352
  }
@@ -48,9 +48,9 @@ source channel, event name, and attention hints. The decision engine evaluates
48
48
  whether and where to deliver the notification based on connected channels,
49
49
  urgency, and user preferences.
50
50
 
51
- Minimal usage: only --message is required. Add --urgent for a push + visual
52
- flag in the inbox. Source channel/event name fall back to assistant_tool /
53
- assistant.share when omitted.
51
+ Minimal usage: only --message is required. Add --urgent for a push +
52
+ visual flag in the Vellum Home feed. Source channel/event name fall back
53
+ to assistant_tool / assistant.share when omitted.
54
54
 
55
55
  Examples:
56
56
  $ assistant notifications send --message "Build finished"
@@ -71,7 +71,7 @@ Examples:
71
71
  {
72
72
  flags: "--urgent",
73
73
  description:
74
- "Mark this notification as urgent (fires push + visual flag in inbox)",
74
+ "Mark this notification as urgent (fires push + visual flag in Vellum Home)",
75
75
  defaultValue: false,
76
76
  },
77
77
  {
@@ -176,7 +176,7 @@ Examples:
176
176
  {
177
177
  name: "list",
178
178
  description:
179
- "List notifications surfaced to the user via the home feed. Excludes dismissed items by default.",
179
+ "List Vellum Home feed notifications surfaced by the assistant. Excludes dismissed items by default.",
180
180
  options: [
181
181
  {
182
182
  flags: "--all",
@@ -231,11 +231,11 @@ Examples:
231
231
  },
232
232
  ],
233
233
  helpText: `
234
- Reads the home feed at $VELLUM_WORKSPACE_DIR/data/home-feed.json — the
235
- user's notification inbox. Items are ordered by priority then recency,
236
- matching what the user sees in the macOS Home page. The home feed covers
237
- background/async notifications mirrored from the unified pipeline;
238
- real-time chat pushes that did not mirror to the feed will not appear.
234
+ Reads Vellum's Home feed at $VELLUM_WORKSPACE_DIR/data/home-feed.json.
235
+ Items are ordered by priority then recency, matching what the user sees
236
+ in the macOS Home page. This is only the assistant's Vellum notification
237
+ feed; it does not read email inboxes, Gmail, Outlook, or other external
238
+ message providers. Use OAuth/provider-specific commands for external mail.
239
239
 
240
240
  Examples:
241
241
  $ assistant notifications list
@@ -0,0 +1,40 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import {
4
+ buildOAuthConnectSurfaceRedirect,
5
+ isModelSpawnedConversationShell,
6
+ oauthConnectSurfaceHint,
7
+ } from "./connect-surface-guidance.js";
8
+
9
+ describe("OAuth connect surface guidance", () => {
10
+ test("builds the oauth_connect next action payload", () => {
11
+ expect(buildOAuthConnectSurfaceRedirect("google")).toEqual({
12
+ ok: false,
13
+ code: "use_oauth_connect_surface",
14
+ provider: "google",
15
+ hint: oauthConnectSurfaceHint("google"),
16
+ nextAction: {
17
+ type: "ui_show",
18
+ surfaceType: "oauth_connect",
19
+ data: { providerKey: "google" },
20
+ },
21
+ });
22
+ });
23
+
24
+ test("detects model-spawned conversation shells", () => {
25
+ expect(
26
+ isModelSpawnedConversationShell({ __CONVERSATION_ID: "conv-123" }),
27
+ ).toBe(true);
28
+ expect(isModelSpawnedConversationShell({ __CONVERSATION_ID: " " })).toBe(
29
+ false,
30
+ );
31
+ expect(isModelSpawnedConversationShell({})).toBe(false);
32
+ });
33
+
34
+ test("hint tells the model not to paste OAuth URLs", () => {
35
+ const hint = oauthConnectSurfaceHint("google");
36
+ expect(hint).toContain('surface_type "oauth_connect"');
37
+ expect(hint).toContain('data.providerKey "google"');
38
+ expect(hint).toContain("paste an OAuth URL");
39
+ });
40
+ });
@@ -0,0 +1,54 @@
1
+ export interface OAuthConnectSurfaceNextAction {
2
+ type: "ui_show";
3
+ surfaceType: "oauth_connect";
4
+ data: { providerKey: string };
5
+ }
6
+
7
+ export interface OAuthConnectSurfaceRedirect {
8
+ ok: false;
9
+ code: "use_oauth_connect_surface";
10
+ provider: string;
11
+ hint: string;
12
+ nextAction: OAuthConnectSurfaceNextAction;
13
+ }
14
+
15
+ export function oauthConnectSurfaceHint(provider: string): string {
16
+ return (
17
+ `To let the user connect, render the connect button: call ` +
18
+ `\`ui_show\` with surface_type "oauth_connect" and ` +
19
+ `data.providerKey "${provider}". That surface is always available — do ` +
20
+ `not run further \`oauth\`/\`channels\` commands, paste an OAuth URL, or ` +
21
+ `load a setup skill just to display it.`
22
+ );
23
+ }
24
+
25
+ export function buildOAuthConnectSurfaceNextAction(
26
+ provider: string,
27
+ ): OAuthConnectSurfaceNextAction {
28
+ return {
29
+ type: "ui_show",
30
+ surfaceType: "oauth_connect",
31
+ data: { providerKey: provider },
32
+ };
33
+ }
34
+
35
+ export function buildOAuthConnectSurfaceRedirect(
36
+ provider: string,
37
+ ): OAuthConnectSurfaceRedirect {
38
+ return {
39
+ ok: false,
40
+ code: "use_oauth_connect_surface",
41
+ provider,
42
+ hint: oauthConnectSurfaceHint(provider),
43
+ nextAction: buildOAuthConnectSurfaceNextAction(provider),
44
+ };
45
+ }
46
+
47
+ export function isModelSpawnedConversationShell(
48
+ env: NodeJS.ProcessEnv = process.env,
49
+ ): boolean {
50
+ return (
51
+ typeof env.__CONVERSATION_ID === "string" &&
52
+ env.__CONVERSATION_ID.trim().length > 0
53
+ );
54
+ }
@@ -0,0 +1,126 @@
1
+ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
2
+
3
+ const ipcCalls: Array<{ method: string; params?: Record<string, unknown> }> =
4
+ [];
5
+ const openedUrls: string[] = [];
6
+
7
+ mock.module("../../../ipc/cli-client.js", () => ({
8
+ cliIpcCall: async (method: string, params?: Record<string, unknown>) => {
9
+ ipcCalls.push({ method, params });
10
+ if (method === "oauth_providers_by_providerKey_get") {
11
+ return {
12
+ ok: true,
13
+ result: {
14
+ provider: { authUrl: "https://accounts.example.com/oauth" },
15
+ },
16
+ };
17
+ }
18
+ if (method === "oauth_mode_get") {
19
+ return { ok: true, result: { ok: true, mode: "managed" } };
20
+ }
21
+ if (method === "oauth_managed_connect_start") {
22
+ return {
23
+ ok: true,
24
+ result: { ok: true, connect_url: "https://connect.example.com" },
25
+ };
26
+ }
27
+ return { ok: false, error: `Unexpected IPC method ${method}` };
28
+ },
29
+ exitFromIpcResult: (r: { error?: string }) => {
30
+ throw new Error(r.error ?? "IPC error");
31
+ },
32
+ }));
33
+
34
+ mock.module("../../lib/open-browser.js", () => ({
35
+ openInHostBrowser: (url: string) => {
36
+ openedUrls.push(url);
37
+ },
38
+ }));
39
+
40
+ mock.module("../../logger.js", () => ({
41
+ getCliLogger: () => ({
42
+ info: () => {},
43
+ warn: () => {},
44
+ error: () => {},
45
+ debug: () => {},
46
+ }),
47
+ }));
48
+
49
+ import { Command } from "commander";
50
+
51
+ import { applyCommandHelp } from "../../lib/cli-command-help.js";
52
+ import { registerConnectCommand } from "./connect.js";
53
+ import { oauthHelp } from "./index.help.js";
54
+
55
+ const originalConversationId = process.env.__CONVERSATION_ID;
56
+
57
+ beforeEach(() => {
58
+ ipcCalls.length = 0;
59
+ openedUrls.length = 0;
60
+ process.exitCode = 0;
61
+ process.env.__CONVERSATION_ID = "conv-123";
62
+ });
63
+
64
+ afterEach(() => {
65
+ if (originalConversationId === undefined) {
66
+ delete process.env.__CONVERSATION_ID;
67
+ } else {
68
+ process.env.__CONVERSATION_ID = originalConversationId;
69
+ }
70
+ process.exitCode = 0;
71
+ });
72
+
73
+ async function runConnectCommand(): Promise<{
74
+ stdout: string;
75
+ exitCode: number;
76
+ }> {
77
+ const chunks: string[] = [];
78
+ const originalWrite = process.stdout.write;
79
+ process.stdout.write = ((chunk: string | Buffer) => {
80
+ chunks.push(typeof chunk === "string" ? chunk : chunk.toString());
81
+ return true;
82
+ }) as typeof process.stdout.write;
83
+
84
+ try {
85
+ const program = new Command();
86
+ program.exitOverride();
87
+ const oauth = program.command("oauth").description(oauthHelp.description);
88
+ applyCommandHelp(oauth, oauthHelp);
89
+ registerConnectCommand(oauth);
90
+ await program.parseAsync([
91
+ "node",
92
+ "test",
93
+ "oauth",
94
+ "--json",
95
+ "connect",
96
+ "google",
97
+ ]);
98
+ } catch {
99
+ // Commander may throw under exitOverride for parse errors; the assertions
100
+ // below verify the command behavior we care about.
101
+ } finally {
102
+ process.stdout.write = originalWrite;
103
+ }
104
+
105
+ return { stdout: chunks.join(""), exitCode: Number(process.exitCode ?? 0) };
106
+ }
107
+
108
+ describe("oauth connect", () => {
109
+ test("managed provider in a conversation shell returns oauth_connect guidance without opening a browser", async () => {
110
+ const { stdout, exitCode } = await runConnectCommand();
111
+
112
+ expect(exitCode).toBe(1);
113
+ const parsed = JSON.parse(stdout) as Record<string, unknown>;
114
+ expect(parsed.code).toBe("use_oauth_connect_surface");
115
+ expect(parsed.nextAction).toEqual({
116
+ type: "ui_show",
117
+ surfaceType: "oauth_connect",
118
+ data: { providerKey: "google" },
119
+ });
120
+ expect(openedUrls).toEqual([]);
121
+ expect(ipcCalls.map((call) => call.method)).toEqual([
122
+ "oauth_providers_by_providerKey_get",
123
+ "oauth_mode_get",
124
+ ]);
125
+ });
126
+ });
@@ -5,6 +5,10 @@ import { subcommand } from "../../lib/cli-command-help.js";
5
5
  import { openInHostBrowser } from "../../lib/open-browser.js";
6
6
  import { getCliLogger } from "../../logger.js";
7
7
  import { shouldOutputJson, writeOutput } from "../../output.js";
8
+ import {
9
+ buildOAuthConnectSurfaceRedirect,
10
+ isModelSpawnedConversationShell,
11
+ } from "./connect-surface-guidance.js";
8
12
 
9
13
  const log = getCliLogger("cli");
10
14
 
@@ -125,7 +129,9 @@ export function registerConnectCommand(oauth: Command): void {
125
129
  queryParams: { provider },
126
130
  });
127
131
 
128
- if (!modeResult.ok) return exitFromIpcResult(modeResult);
132
+ if (!modeResult.ok) {
133
+ return exitFromIpcResult(modeResult);
134
+ }
129
135
 
130
136
  const managed = modeResult.result?.mode === "managed";
131
137
 
@@ -134,6 +140,13 @@ export function registerConnectCommand(oauth: Command): void {
134
140
  // MANAGED PATH
135
141
  // =============================================================
136
142
 
143
+ if (isModelSpawnedConversationShell()) {
144
+ const redirect = buildOAuthConnectSurfaceRedirect(provider);
145
+ writeOutput(cmd, redirect);
146
+ process.exitCode = 1;
147
+ return;
148
+ }
149
+
137
150
  if (opts.clientId) {
138
151
  log.info(
139
152
  `Warning: --client-id is ignored for platform-managed providers. The platform manages OAuth apps for "${provider}".`,
@@ -152,7 +165,9 @@ export function registerConnectCommand(oauth: Command): void {
152
165
  body: startBody,
153
166
  });
154
167
 
155
- if (!startResult.ok) return exitFromIpcResult(startResult);
168
+ if (!startResult.ok) {
169
+ return exitFromIpcResult(startResult);
170
+ }
156
171
 
157
172
  const connectUrl = startResult.result!.connect_url;
158
173
 
@@ -169,7 +184,9 @@ export function registerConnectCommand(oauth: Command): void {
169
184
  queryParams: { provider },
170
185
  });
171
186
 
172
- if (!snapshotResult.ok) return exitFromIpcResult(snapshotResult);
187
+ if (!snapshotResult.ok) {
188
+ return exitFromIpcResult(snapshotResult);
189
+ }
173
190
 
174
191
  const snapshotIds = new Set(
175
192
  (snapshotResult.result?.connections ?? []).map((e) => e.id),
@@ -207,7 +224,9 @@ export function registerConnectCommand(oauth: Command): void {
207
224
  queryParams: { provider },
208
225
  });
209
226
 
210
- if (!currentResult.ok || !currentResult.result) continue;
227
+ if (!currentResult.ok || !currentResult.result) {
228
+ continue;
229
+ }
211
230
 
212
231
  const found = currentResult.result.connections.find(
213
232
  (e) => !snapshotIds.has(e.id),
@@ -282,8 +301,12 @@ export function registerConnectCommand(oauth: Command): void {
282
301
  service: provider,
283
302
  callbackTransport: opts.callbackTransport,
284
303
  };
285
- if (opts.clientId) startBody.clientId = opts.clientId;
286
- if (opts.scopes) startBody.requestedScopes = opts.scopes;
304
+ if (opts.clientId) {
305
+ startBody.clientId = opts.clientId;
306
+ }
307
+ if (opts.scopes) {
308
+ startBody.requestedScopes = opts.scopes;
309
+ }
287
310
 
288
311
  const startResult = await cliIpcCall<{
289
312
  auth_url: string;
@@ -655,7 +655,7 @@ Examples:
655
655
  name: "connect",
656
656
  args: "<provider>",
657
657
  description:
658
- "Initiate an OAuth authorization flow for a specified provider",
658
+ "Initiate a terminal/headless OAuth authorization flow for a specified provider",
659
659
  options: [
660
660
  {
661
661
  flags: "--scopes <scopes...>",
@@ -687,6 +687,12 @@ By default, the browser opens automatically and the command waits for
687
687
  completion. Use --no-browser to print the URL instead (useful for headless
688
688
  or SSH sessions).
689
689
 
690
+ Important for chat/UI turns: do not run this command just to let the user
691
+ connect a managed provider. Render the first-class connect surface instead:
692
+ call \`ui_show\` with surface_type "oauth_connect" and data.providerKey set to
693
+ the provider (for example, "google"). That surface starts the managed OAuth UI
694
+ and avoids pasting raw authorization links into the conversation.
695
+
690
696
  Examples:
691
697
  $ assistant oauth connect google
692
698
  $ assistant oauth connect google --no-browser
@@ -1,12 +1,16 @@
1
1
  import { afterEach, describe, expect, test } from "bun:test";
2
2
 
3
- import { noConnectionsMessage } from "./status.js";
3
+ import { oauthHelp } from "./index.help.js";
4
+ import { decorateStatusJsonForModel, noConnectionsMessage } from "./status.js";
4
5
 
5
6
  const original = process.env.__RESOLVED_MODEL;
6
7
 
7
8
  afterEach(() => {
8
- if (original === undefined) delete process.env.__RESOLVED_MODEL;
9
- else process.env.__RESOLVED_MODEL = original;
9
+ if (original === undefined) {
10
+ delete process.env.__RESOLVED_MODEL;
11
+ } else {
12
+ process.env.__RESOLVED_MODEL = original;
13
+ }
10
14
  });
11
15
 
12
16
  describe("noConnectionsMessage", () => {
@@ -31,6 +35,68 @@ describe("noConnectionsMessage", () => {
31
35
  const msg = await noConnectionsMessage("google");
32
36
  expect(msg).toContain('surface_type "oauth_connect"');
33
37
  expect(msg).toContain('data.providerKey "google"');
38
+ expect(msg).toContain("paste an OAuth URL");
34
39
  expect(msg).not.toContain("assistant oauth connect google");
35
40
  });
41
+
42
+ test("weak open models are not steered to oauth_connect for BYO mode", async () => {
43
+ process.env.__RESOLVED_MODEL = "accounts/fireworks/models/minimax-m3";
44
+ const msg = await noConnectionsMessage("google", "byo");
45
+ expect(msg).not.toContain('surface_type "oauth_connect"');
46
+ expect(msg).toContain("assistant oauth connect google");
47
+ });
48
+
49
+ test("weak open model JSON status includes oauth_connect next action", async () => {
50
+ process.env.__RESOLVED_MODEL = "accounts/fireworks/models/glm-5p2";
51
+ const result = await decorateStatusJsonForModel({
52
+ ok: true,
53
+ provider: "google",
54
+ mode: "managed",
55
+ connections: [],
56
+ });
57
+ expect(result.hint).toContain('surface_type "oauth_connect"');
58
+ expect(result.hint).toContain("paste an OAuth URL");
59
+ expect(result.nextAction).toEqual({
60
+ type: "ui_show",
61
+ surfaceType: "oauth_connect",
62
+ data: { providerKey: "google" },
63
+ });
64
+ });
65
+
66
+ test("weak open model JSON status stays compact for BYO mode", async () => {
67
+ process.env.__RESOLVED_MODEL = "accounts/fireworks/models/glm-5p2";
68
+ const result = await decorateStatusJsonForModel({
69
+ ok: true,
70
+ provider: "google",
71
+ mode: "byo",
72
+ connections: [],
73
+ });
74
+ expect(result.hint).toBeUndefined();
75
+ expect(result.nextAction).toBeUndefined();
76
+ });
77
+
78
+ test("capable model JSON status stays compact", async () => {
79
+ process.env.__RESOLVED_MODEL = "claude-opus-4-8";
80
+ const result = await decorateStatusJsonForModel({
81
+ ok: true,
82
+ provider: "google",
83
+ mode: "managed",
84
+ connections: [],
85
+ });
86
+ expect(result.hint).toBeUndefined();
87
+ expect(result.nextAction).toBeUndefined();
88
+ });
89
+ });
90
+
91
+ describe("oauth connect help", () => {
92
+ test("steers chat turns to the oauth_connect surface", () => {
93
+ const connectHelp = oauthHelp.subcommands?.find(
94
+ (command) => command.name === "connect",
95
+ );
96
+ expect(connectHelp?.description).toContain("terminal/headless");
97
+ expect(connectHelp?.helpText).toContain('surface_type "oauth_connect"');
98
+ expect(connectHelp?.helpText).toContain(
99
+ "avoids pasting raw authorization links",
100
+ );
101
+ });
36
102
  });