@vellumai/assistant 0.8.0 → 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 (692) hide show
  1. package/AGENTS.md +11 -0
  2. package/Dockerfile +5 -4
  3. package/README.md +2 -2
  4. package/docker-entrypoint.sh +16 -0
  5. package/eslint-rules/__tests__/cli-no-daemon-internals.test.ts +420 -0
  6. package/eslint-rules/cli-no-daemon-internals.js +283 -0
  7. package/eslint.config.mjs +12 -0
  8. package/knip.json +2 -1
  9. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +10 -1
  10. package/openapi.yaml +4847 -1698
  11. package/package.json +3 -1
  12. package/scripts/generate-openapi.ts +52 -4
  13. package/scripts/sync-llm-catalog.ts +165 -0
  14. package/scripts/sync-web-search-catalog.ts +107 -0
  15. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +169 -0
  16. package/src/__tests__/agent-loop-override-profile.test.ts +26 -1
  17. package/src/__tests__/anthropic-provider.test.ts +92 -2
  18. package/src/__tests__/app-control-flow.test.ts +7 -0
  19. package/src/__tests__/assistant-events-sse-shed.test.ts +232 -0
  20. package/src/__tests__/avatar-identity-sync.test.ts +87 -0
  21. package/src/__tests__/background-workers-disk-pressure.test.ts +11 -22
  22. package/src/__tests__/btw-routes.test.ts +1 -0
  23. package/src/__tests__/call-site-routing-provider.test.ts +172 -45
  24. package/src/__tests__/cancel-resolves-conversation-key.test.ts +44 -3
  25. package/src/__tests__/channel-policy.test.ts +12 -0
  26. package/src/__tests__/checker.test.ts +89 -0
  27. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +35 -7
  28. package/src/__tests__/compact-event-conversation-id-guard.test.ts +33 -5
  29. package/src/__tests__/compaction-strip-metadata-clear.test.ts +26 -1
  30. package/src/__tests__/config-loader-backfill.test.ts +526 -102
  31. package/src/__tests__/config-loader-corrupt.test.ts +68 -0
  32. package/src/__tests__/config-loader-platform-defaults.test.ts +77 -23
  33. package/src/__tests__/config-schema-cmd.test.ts +63 -29
  34. package/src/__tests__/config-schema.test.ts +14 -3
  35. package/src/__tests__/config-set-platform-guard.test.ts +75 -152
  36. package/src/__tests__/config-set-route.test.ts +198 -0
  37. package/src/__tests__/config-watcher.test.ts +6 -0
  38. package/src/__tests__/contacts-tools.test.ts +51 -199
  39. package/src/__tests__/context-search-agent-protocol.test.ts +21 -2
  40. package/src/__tests__/context-search-agent-runner.test.ts +22 -138
  41. package/src/__tests__/context-search-conversations-source.test.ts +42 -16
  42. package/src/__tests__/context-search-fanout.test.ts +20 -157
  43. package/src/__tests__/context-search-memory-v2-source.test.ts +3 -3
  44. package/src/__tests__/context-search-types.test.ts +7 -2
  45. package/src/__tests__/context-window-manager.test.ts +389 -1
  46. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  47. package/src/__tests__/conversation-crud-inference-profile.test.ts +100 -0
  48. package/src/__tests__/conversation-error.test.ts +38 -0
  49. package/src/__tests__/conversation-fork-crud.test.ts +241 -1
  50. package/src/__tests__/conversation-inference-profile-route.test.ts +14 -14
  51. package/src/__tests__/conversation-init.benchmark.test.ts +1 -0
  52. package/src/__tests__/conversation-lifecycle.test.ts +124 -0
  53. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +100 -1
  54. package/src/__tests__/conversation-process-callsite.test.ts +21 -1
  55. package/src/__tests__/conversation-runtime-assembly.test.ts +4 -4
  56. package/src/__tests__/conversation-slash-commands.test.ts +194 -2
  57. package/src/__tests__/conversation-surfaces-app-control.test.ts +323 -3
  58. package/src/__tests__/credential-security-invariants.test.ts +5 -6
  59. package/src/__tests__/daemon-credential-client.test.ts +56 -1
  60. package/src/__tests__/db-activation-state-fk-cascade.test.ts +132 -0
  61. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +37 -0
  62. package/src/__tests__/db-memory-graph-event-date-repair.test.ts +43 -20
  63. package/src/__tests__/db-proxy-transaction.test.ts +206 -0
  64. package/src/__tests__/external-plugin-loader.test.ts +458 -0
  65. package/src/__tests__/filing-service.test.ts +23 -3
  66. package/src/__tests__/fixtures/mock-chrome-extension.ts +5 -0
  67. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  68. package/src/__tests__/graph-extraction-event-date.test.ts +34 -0
  69. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +0 -8
  70. package/src/__tests__/heartbeat-disk-pressure.test.ts +21 -8
  71. package/src/__tests__/heartbeat-service.test.ts +50 -233
  72. package/src/__tests__/history-repair.test.ts +89 -0
  73. package/src/__tests__/host-app-control-proxy.test.ts +109 -1
  74. package/src/__tests__/host-app-control-routes.test.ts +247 -1
  75. package/src/__tests__/host-browser-proxy.test.ts +416 -20
  76. package/src/__tests__/host-browser-routes.test.ts +325 -33
  77. package/src/__tests__/host-proxy-preactivation.test.ts +211 -0
  78. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +246 -0
  79. package/src/__tests__/inference-profile-reaper.test.ts +154 -0
  80. package/src/__tests__/inference-profile-session-handler.test.ts +398 -0
  81. package/src/__tests__/inference-profile-session-ipc.test.ts +236 -0
  82. package/src/__tests__/inline-skill-load-permissions.test.ts +6 -1
  83. package/src/__tests__/install-skill-routing.test.ts +2 -2
  84. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +15 -0
  85. package/src/__tests__/llm-callsite-catalog.test.ts +20 -1
  86. package/src/__tests__/llm-catalog-parity.test.ts +146 -0
  87. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +188 -0
  88. package/src/__tests__/llm-request-log-source-factory.test.ts +124 -0
  89. package/src/__tests__/llm-resolver.test.ts +46 -0
  90. package/src/__tests__/managed-profile-guard.test.ts +131 -2
  91. package/src/__tests__/mcp-auth-routes.test.ts +1 -0
  92. package/src/__tests__/mcp-cli.test.ts +182 -220
  93. package/src/__tests__/mcp-health-check.test.ts +56 -27
  94. package/src/__tests__/memory-jobs-worker-lanes.test.ts +18 -11
  95. package/src/__tests__/message-complete-display-id.test.ts +175 -0
  96. package/src/__tests__/notification-platform-adapter.test.ts +229 -0
  97. package/src/__tests__/oauth-cli.test.ts +38 -2009
  98. package/src/__tests__/oauth-commands-routes.test.ts +711 -0
  99. package/src/__tests__/oauth-connect-routes.test.ts +174 -11
  100. package/src/__tests__/oauth-providers-routes.test.ts +14 -10
  101. package/src/__tests__/openai-responses-cutover-guard.test.ts +33 -12
  102. package/src/__tests__/openai-responses-provider.test.ts +17 -0
  103. package/src/__tests__/plugin-bootstrap.test.ts +31 -2
  104. package/src/__tests__/plugin-route-contribution.test.ts +31 -3
  105. package/src/__tests__/plugin-tool-contribution.test.ts +31 -3
  106. package/src/__tests__/plugin-types.test.ts +13 -11
  107. package/src/__tests__/process-message-background-slack.test.ts +46 -0
  108. package/src/__tests__/profile-entry-status.test.ts +43 -0
  109. package/src/__tests__/provider-managed-proxy-integration.test.ts +12 -4
  110. package/src/__tests__/provider-registry-ollama.test.ts +12 -4
  111. package/src/__tests__/provider-send-message-override-profile.test.ts +10 -4
  112. package/src/__tests__/relay-server.test.ts +118 -0
  113. package/src/__tests__/retry-thinking-tool-choice.test.ts +15 -0
  114. package/src/__tests__/schedule-retry.test.ts +56 -4
  115. package/src/__tests__/schedule-routes.test.ts +104 -0
  116. package/src/__tests__/scheduler-disk-pressure.test.ts +0 -4
  117. package/src/__tests__/scheduler-recurrence.test.ts +87 -34
  118. package/src/__tests__/scheduler-reuse-conversation.test.ts +161 -5
  119. package/src/__tests__/scheduler-wake.test.ts +0 -63
  120. package/src/__tests__/secret-allowlist.test.ts +1 -0
  121. package/src/__tests__/secret-routes-managed-proxy.test.ts +12 -4
  122. package/src/__tests__/shell-credential-ref.test.ts +95 -3
  123. package/src/__tests__/shell-tool-proxy-mode.test.ts +14 -0
  124. package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
  125. package/src/__tests__/skill-load-tool.test.ts +2 -4
  126. package/src/__tests__/subagent-call-site-routing.test.ts +78 -16
  127. package/src/__tests__/suggestion-routes.test.ts +3 -3
  128. package/src/__tests__/sync-message-contract.test.ts +63 -0
  129. package/src/__tests__/task-scheduler.test.ts +88 -23
  130. package/src/__tests__/update-bulletin-job.test.ts +96 -193
  131. package/src/__tests__/usage-cli.test.ts +11 -73
  132. package/src/__tests__/user-plugin-loader.test.ts +145 -0
  133. package/src/__tests__/vercel-config.test.ts +168 -0
  134. package/src/__tests__/web-search-catalog-parity.test.ts +86 -0
  135. package/src/__tests__/web-search.test.ts +303 -2
  136. package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +1 -21
  137. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +58 -0
  138. package/src/__tests__/workspace-migration-069-seed-onboarding-threads.test.ts +53 -20
  139. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +191 -0
  140. package/src/__tests__/workspace-migration-076-drop-services-inference-mode.test.ts +211 -0
  141. package/src/__tests__/workspace-migration-077-seed-memory-router-callsite.test.ts +174 -0
  142. package/src/__tests__/workspace-migration-079-home-feed-notification-only.test.ts +323 -0
  143. package/src/__tests__/workspace-migration-080-restrict-vercel-api-token-metadata.test.ts +299 -0
  144. package/src/__tests__/workspace-migration-081-backfill-bash-allowed-tools.test.ts +410 -0
  145. package/src/__tests__/workspace-migration-082-backfill-managed-profile-labels.test.ts +268 -0
  146. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +3 -3
  147. package/src/__tests__/workspace-release-notes-feature-flag-guard.test.ts +115 -0
  148. package/src/acp/__tests__/helpers/which-stub.ts +4 -2
  149. package/src/acp/resolve-agent.test.ts +25 -0
  150. package/src/acp/resolve-agent.ts +13 -2
  151. package/src/acp/session-manager.ts +14 -0
  152. package/src/approvals/guardian-request-resolvers.ts +32 -87
  153. package/src/calls/relay-server.ts +35 -0
  154. package/src/calls/relay-setup-router.ts +36 -0
  155. package/src/calls/types.ts +1 -0
  156. package/src/calls/voice-session-bridge.ts +23 -4
  157. package/src/channels/config.ts +14 -1
  158. package/src/channels/types.ts +1 -0
  159. package/src/cli/AGENTS.md +164 -4
  160. package/src/cli/__tests__/notifications.test.ts +54 -0
  161. package/src/cli/commands/__tests__/avatar.test.ts +540 -0
  162. package/src/cli/commands/__tests__/backup.test.ts +236 -776
  163. package/src/cli/commands/__tests__/cache.test.ts +1 -1
  164. package/src/cli/commands/__tests__/changelog.test.ts +593 -0
  165. package/src/cli/commands/__tests__/channel-verification-sessions.test.ts +503 -0
  166. package/src/cli/commands/__tests__/conversations-import.test.ts +515 -0
  167. package/src/cli/commands/__tests__/domain-register.test.ts +140 -167
  168. package/src/cli/commands/__tests__/domain-status.test.ts +137 -76
  169. package/src/cli/commands/__tests__/email-attachment.test.ts +314 -337
  170. package/src/cli/commands/__tests__/email-core.test.ts +579 -0
  171. package/src/cli/commands/__tests__/image-generation.test.ts +87 -824
  172. package/src/cli/commands/__tests__/inference-send.test.ts +30 -266
  173. package/src/cli/commands/__tests__/inference-session.test.ts +423 -0
  174. package/src/cli/commands/__tests__/memory-v2.test.ts +81 -110
  175. package/src/cli/commands/__tests__/skills.test.ts +563 -0
  176. package/src/cli/commands/__tests__/status.test.ts +249 -0
  177. package/src/cli/commands/__tests__/stt.test.ts +320 -0
  178. package/src/cli/commands/__tests__/tts-synthesize.test.ts +4 -603
  179. package/src/cli/commands/__tests__/tts.test.ts +321 -0
  180. package/src/cli/commands/__tests__/webhooks.test.ts +86 -511
  181. package/src/cli/commands/attachment.ts +8 -3
  182. package/src/cli/commands/audit.ts +95 -64
  183. package/src/cli/commands/auth.ts +61 -58
  184. package/src/cli/commands/avatar.ts +276 -390
  185. package/src/cli/commands/backup.ts +409 -505
  186. package/src/cli/commands/bash.ts +9 -5
  187. package/src/cli/commands/browser.ts +28 -9
  188. package/src/cli/commands/cache.ts +9 -4
  189. package/src/cli/commands/changelog.ts +414 -0
  190. package/src/cli/commands/channel-verification-sessions.ts +238 -317
  191. package/src/cli/commands/clients.ts +8 -3
  192. package/src/cli/commands/completions.ts +9 -9
  193. package/src/cli/commands/config.ts +102 -72
  194. package/src/cli/commands/contacts.ts +575 -696
  195. package/src/cli/commands/conversations-defer.ts +17 -69
  196. package/src/cli/commands/conversations-import.ts +90 -253
  197. package/src/cli/commands/conversations.ts +346 -436
  198. package/src/cli/commands/credential-execution.ts +9 -6
  199. package/src/cli/commands/credentials.ts +456 -736
  200. package/src/cli/commands/domain.ts +128 -206
  201. package/src/cli/commands/email.ts +606 -794
  202. package/src/cli/commands/gateway.ts +8 -1
  203. package/src/cli/commands/image-generation.ts +157 -205
  204. package/src/cli/commands/inference-providers.ts +352 -0
  205. package/src/cli/commands/inference-session.ts +415 -0
  206. package/src/cli/commands/inference.ts +87 -65
  207. package/src/cli/commands/keys.ts +8 -3
  208. package/src/cli/commands/mcp.ts +103 -287
  209. package/src/cli/commands/memory-v2.ts +162 -516
  210. package/src/cli/commands/notifications.ts +33 -7
  211. package/src/cli/commands/oauth/apps.ts +292 -261
  212. package/src/cli/commands/oauth/connect.ts +176 -297
  213. package/src/cli/commands/oauth/disconnect.ts +16 -215
  214. package/src/cli/commands/oauth/index.ts +49 -45
  215. package/src/cli/commands/oauth/mode.ts +43 -199
  216. package/src/cli/commands/oauth/ping.ts +17 -125
  217. package/src/cli/commands/oauth/providers.ts +732 -921
  218. package/src/cli/commands/oauth/request.ts +60 -350
  219. package/src/cli/commands/oauth/shared.ts +11 -121
  220. package/src/cli/commands/oauth/status.ts +31 -121
  221. package/src/cli/commands/oauth/token.ts +13 -55
  222. package/src/cli/commands/pending.ts +19 -10
  223. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +133 -183
  224. package/src/cli/commands/platform/__tests__/connect.test.ts +66 -181
  225. package/src/cli/commands/platform/__tests__/disconnect.test.ts +71 -227
  226. package/src/cli/commands/platform/__tests__/status.test.ts +169 -287
  227. package/src/cli/commands/platform/connect.ts +16 -80
  228. package/src/cli/commands/platform/disconnect.ts +14 -112
  229. package/src/cli/commands/platform/index.ts +177 -246
  230. package/src/cli/commands/routes.ts +153 -336
  231. package/src/cli/commands/sequence.ts +316 -360
  232. package/src/cli/commands/skills.ts +449 -671
  233. package/src/cli/commands/status.ts +58 -37
  234. package/src/cli/commands/stt.ts +94 -262
  235. package/src/cli/commands/task.ts +14 -40
  236. package/src/cli/commands/trust.ts +8 -3
  237. package/src/cli/commands/tts.ts +162 -167
  238. package/src/cli/commands/ui.ts +35 -42
  239. package/src/cli/commands/usage.ts +188 -126
  240. package/src/cli/commands/watchers.ts +8 -3
  241. package/src/cli/commands/webhooks.ts +99 -193
  242. package/src/cli/lib/__tests__/register-command.test.ts +85 -0
  243. package/src/cli/lib/daemon-credential-client.ts +4 -5
  244. package/src/cli/lib/nested-value.ts +44 -0
  245. package/src/cli/lib/open-browser.ts +36 -0
  246. package/src/cli/lib/register-command.ts +19 -0
  247. package/src/cli/lib/time-ago.ts +34 -0
  248. package/src/cli/program.ts +2 -4
  249. package/src/cli/utils/__tests__/conversation-id.test.ts +66 -0
  250. package/src/cli/utils/__tests__/parse-duration.test.ts +49 -0
  251. package/src/cli/utils/conversation-id.ts +30 -0
  252. package/src/cli/utils/parse-duration.ts +41 -0
  253. package/src/config/acp-defaults.test.ts +5 -1
  254. package/src/config/acp-defaults.ts +11 -4
  255. package/src/config/bundled-skills/acp/TOOLS.json +2 -2
  256. package/src/config/bundled-skills/app-control/TOOLS.json +32 -0
  257. package/src/config/bundled-skills/contacts/SKILL.md +12 -45
  258. package/src/config/bundled-skills/contacts/TOOLS.json +0 -57
  259. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +0 -12
  260. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +0 -58
  261. package/src/config/bundled-tool-registry.ts +0 -2
  262. package/src/config/feature-flag-registry.json +16 -0
  263. package/src/config/llm-resolver.ts +16 -1
  264. package/src/config/loader.ts +76 -14
  265. package/src/config/raw-config-utils.ts +2 -30
  266. package/src/config/schema.ts +4 -0
  267. package/src/config/schemas/__tests__/memory-v2.test.ts +49 -0
  268. package/src/config/schemas/call-site-catalog.ts +29 -7
  269. package/src/config/schemas/llm-request-logs.ts +57 -0
  270. package/src/config/schemas/llm.ts +52 -2
  271. package/src/config/schemas/memory-retrospective.ts +48 -0
  272. package/src/config/schemas/memory-v2.ts +32 -1
  273. package/src/config/schemas/memory.ts +4 -0
  274. package/src/config/schemas/services.ts +15 -12
  275. package/src/config/seed-inference-profiles.ts +195 -134
  276. package/src/contacts/contact-store.ts +0 -61
  277. package/src/context/window-manager.ts +191 -5
  278. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +79 -0
  279. package/src/daemon/__tests__/conversation-tool-setup.test.ts +109 -4
  280. package/src/daemon/__tests__/daemon-skill-host.test.ts +10 -4
  281. package/src/daemon/approval-generators.ts +23 -29
  282. package/src/daemon/config-watcher.ts +2 -0
  283. package/src/daemon/conversation-agent-loop-handlers.ts +24 -0
  284. package/src/daemon/conversation-agent-loop.ts +127 -97
  285. package/src/daemon/conversation-error.ts +21 -0
  286. package/src/daemon/conversation-lifecycle.ts +46 -5
  287. package/src/daemon/conversation-process.ts +36 -19
  288. package/src/daemon/conversation-runtime-assembly.ts +14 -5
  289. package/src/daemon/conversation-slash.ts +175 -23
  290. package/src/daemon/conversation-store.ts +17 -10
  291. package/src/daemon/conversation-surfaces.ts +76 -12
  292. package/src/daemon/conversation-tool-setup.ts +24 -14
  293. package/src/daemon/conversation.ts +48 -9
  294. package/src/daemon/external-plugins-bootstrap.ts +18 -8
  295. package/src/daemon/guardian-action-generators.ts +7 -22
  296. package/src/daemon/handlers/config-model.ts +8 -126
  297. package/src/daemon/handlers/config-slack-channel.ts +10 -7
  298. package/src/daemon/handlers/config-vercel.ts +3 -1
  299. package/src/daemon/handlers/skills.ts +84 -5
  300. package/src/daemon/history-repair.ts +33 -6
  301. package/src/daemon/host-app-control-proxy.ts +44 -19
  302. package/src/daemon/host-bash-proxy.ts +85 -158
  303. package/src/daemon/host-browser-proxy.ts +96 -35
  304. package/src/daemon/host-proxy-base.ts +13 -1
  305. package/src/daemon/host-proxy-preactivation.ts +25 -1
  306. package/src/daemon/identity-helpers.ts +19 -0
  307. package/src/daemon/lifecycle.ts +42 -43
  308. package/src/daemon/meet-host-supervisor.ts +15 -15
  309. package/src/daemon/memory-v2-startup.ts +9 -2
  310. package/src/daemon/message-protocol.ts +6 -0
  311. package/src/daemon/message-types/bookmarks.ts +18 -0
  312. package/src/daemon/message-types/conversations.ts +12 -9
  313. package/src/daemon/message-types/messages.ts +9 -1
  314. package/src/daemon/message-types/sync.ts +60 -0
  315. package/src/daemon/pkb-reminder-builder.test.ts +54 -13
  316. package/src/daemon/pkb-reminder-builder.ts +21 -7
  317. package/src/daemon/process-message.ts +56 -23
  318. package/src/daemon/server.ts +23 -18
  319. package/src/daemon/shutdown-handlers.ts +0 -2
  320. package/src/daemon/tool-setup-types.ts +9 -0
  321. package/src/daemon/tool-side-effects.ts +6 -4
  322. package/src/daemon/wake-target-adapter.ts +11 -0
  323. package/src/export/transcript-formatter.ts +61 -2
  324. package/src/filing/filing-service.ts +40 -53
  325. package/src/heartbeat/__tests__/heartbeat-service.test.ts +359 -0
  326. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  327. package/src/heartbeat/heartbeat-service.ts +148 -127
  328. package/src/home/__tests__/feed-types.test.ts +63 -131
  329. package/src/home/__tests__/feed-writer.test.ts +77 -278
  330. package/src/home/__tests__/post-connect-feed.test.ts +9 -12
  331. package/src/home/feed-types.ts +19 -73
  332. package/src/home/feed-writer.ts +25 -156
  333. package/src/home/post-connect-feed.ts +1 -3
  334. package/src/ipc/__tests__/cli-ipc.test.ts +2 -0
  335. package/src/ipc/__tests__/email-ipc.test.ts +506 -0
  336. package/src/ipc/__tests__/exit-helper.test.ts +104 -0
  337. package/src/ipc/__tests__/streaming-client.test.ts +237 -0
  338. package/src/ipc/__tests__/streaming-framing.test.ts +142 -0
  339. package/src/ipc/assistant-server.ts +55 -6
  340. package/src/ipc/cli-client.ts +370 -50
  341. package/src/ipc/routes/db-proxy-transaction.ts +151 -0
  342. package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +60 -0
  343. package/src/ipc/skill-routes/events.ts +30 -3
  344. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +46 -0
  345. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +1 -0
  346. package/src/live-voice/live-voice-session-manager.ts +11 -4
  347. package/src/live-voice/live-voice-session.ts +14 -6
  348. package/src/memory/__tests__/bookmark-crud.test.ts +258 -0
  349. package/src/memory/__tests__/bookmark-schema.test.ts +181 -0
  350. package/src/memory/__tests__/conversation-types.test.ts +36 -0
  351. package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +130 -0
  352. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +177 -0
  353. package/src/memory/__tests__/memory-retrospective-job.test.ts +328 -0
  354. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +213 -0
  355. package/src/memory/__tests__/memory-retrospective-trigger-check.test.ts +90 -0
  356. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +69 -0
  357. package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +3 -0
  358. package/src/memory/bookmark-crud.ts +179 -0
  359. package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +31 -9
  360. package/src/memory/context-search/agent-protocol.ts +5 -1
  361. package/src/memory/context-search/agent-runner.ts +60 -85
  362. package/src/memory/context-search/limits.ts +1 -4
  363. package/src/memory/context-search/search.ts +23 -113
  364. package/src/memory/context-search/sources/conversations.ts +18 -6
  365. package/src/memory/context-search/sources/memory-v2.ts +39 -14
  366. package/src/memory/context-search/sources/memory.ts +7 -0
  367. package/src/memory/context-search/sources/workspace.ts +13 -10
  368. package/src/memory/context-search/types.ts +1 -1
  369. package/src/memory/conversation-bootstrap.ts +11 -0
  370. package/src/memory/conversation-crud.ts +312 -10
  371. package/src/memory/conversation-queries.ts +9 -5
  372. package/src/memory/conversation-title-service.ts +1 -0
  373. package/src/memory/conversation-types.ts +16 -0
  374. package/src/memory/db-init.ts +14 -0
  375. package/src/memory/embedding-backend.ts +2 -1
  376. package/src/memory/embedding-runtime-manager.ts +1 -2
  377. package/src/memory/graph/__tests__/remember-description.test.ts +55 -0
  378. package/src/memory/graph/conversation-graph-memory.ts +76 -5
  379. package/src/memory/graph/extraction.ts +4 -0
  380. package/src/memory/graph/graph-memory-state-store.ts +16 -3
  381. package/src/memory/graph/tool-handlers.ts +17 -7
  382. package/src/memory/graph/tools.ts +44 -5
  383. package/src/memory/indexer.ts +17 -0
  384. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +13 -15
  385. package/src/memory/jobs/embed-concept-page.ts +45 -9
  386. package/src/memory/jobs-store.ts +51 -1
  387. package/src/memory/jobs-worker.ts +52 -3
  388. package/src/memory/llm-request-log-source-clickhouse.ts +317 -0
  389. package/src/memory/llm-request-log-source-local.ts +26 -0
  390. package/src/memory/llm-request-log-source.ts +97 -0
  391. package/src/memory/llm-request-log-store.ts +1 -1
  392. package/src/memory/memory-retrospective-constants.ts +13 -0
  393. package/src/memory/memory-retrospective-enqueue.ts +114 -0
  394. package/src/memory/memory-retrospective-job.ts +351 -0
  395. package/src/memory/memory-retrospective-startup-cleanup.ts +108 -0
  396. package/src/memory/memory-retrospective-state.ts +162 -0
  397. package/src/memory/memory-retrospective-trigger-check.ts +91 -0
  398. package/src/memory/memory-v2-activation-log-store.ts +49 -5
  399. package/src/memory/memory-v2-concept-frequency.ts +4 -0
  400. package/src/memory/message-content.ts +38 -1
  401. package/src/memory/migrations/227-add-conversation-inference-profile.ts +6 -1
  402. package/src/memory/migrations/228-rename-inference-profile-snake-case.ts +20 -7
  403. package/src/memory/migrations/229-delete-private-conversations.test.ts +70 -1
  404. package/src/memory/migrations/229-delete-private-conversations.ts +12 -0
  405. package/src/memory/migrations/231-repair-memory-graph-event-dates.ts +16 -2
  406. package/src/memory/migrations/240-conversation-inference-profile-session.ts +25 -0
  407. package/src/memory/migrations/241-activation-state-fk-cascade.ts +50 -0
  408. package/src/memory/migrations/242-message-bookmarks.ts +38 -0
  409. package/src/memory/migrations/243-provider-connections.ts +68 -0
  410. package/src/memory/migrations/244-provider-connection-status-label.ts +23 -0
  411. package/src/memory/migrations/245-memory-retrospective-state.ts +36 -0
  412. package/src/memory/migrations/246-backfill-provider-connection-label.ts +81 -0
  413. package/src/memory/migrations/__tests__/244-provider-connection-status-label.test.ts +84 -0
  414. package/src/memory/migrations/__tests__/245-memory-retrospective-state.test.ts +125 -0
  415. package/src/memory/migrations/__tests__/246-backfill-provider-connection-label.test.ts +192 -0
  416. package/src/memory/migrations/index.ts +7 -0
  417. package/src/memory/published-pages-store.ts +16 -0
  418. package/src/memory/schema/bookmarks.ts +38 -0
  419. package/src/memory/schema/conversations.ts +2 -0
  420. package/src/memory/schema/index.ts +2 -0
  421. package/src/memory/schema/inference.ts +29 -0
  422. package/src/memory/schema/memory-core.ts +9 -0
  423. package/src/memory/search/semantic.ts +1 -4
  424. package/src/memory/v2/__tests__/__snapshots__/prompts-router.test.ts.snap +27 -0
  425. package/src/memory/v2/__tests__/activation-store.test.ts +5 -5
  426. package/src/memory/v2/__tests__/activation.test.ts +11 -4
  427. package/src/memory/v2/__tests__/backfill-jobs.test.ts +38 -21
  428. package/src/memory/v2/__tests__/consolidation-job.test.ts +123 -135
  429. package/src/memory/v2/__tests__/edge-index.test.ts +1 -1
  430. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +111 -0
  431. package/src/memory/v2/__tests__/injection.test.ts +628 -10
  432. package/src/memory/v2/__tests__/migration.test.ts +7 -3
  433. package/src/memory/v2/__tests__/page-index.test.ts +277 -0
  434. package/src/memory/v2/__tests__/page-store.test.ts +14 -1
  435. package/src/memory/v2/__tests__/prompts-router.test.ts +257 -0
  436. package/src/memory/v2/__tests__/qdrant.test.ts +72 -0
  437. package/src/memory/v2/__tests__/reranker.test.ts +4 -4
  438. package/src/memory/v2/__tests__/router.test.ts +516 -0
  439. package/src/memory/v2/__tests__/sim.test.ts +45 -1
  440. package/src/memory/v2/__tests__/skill-store.test.ts +58 -3
  441. package/src/memory/v2/__tests__/static-context.test.ts +7 -22
  442. package/src/memory/v2/__tests__/sweep-job.test.ts +95 -0
  443. package/src/memory/v2/activation-store.ts +34 -5
  444. package/src/memory/v2/activation.ts +40 -27
  445. package/src/memory/v2/backfill-jobs.ts +17 -84
  446. package/src/memory/v2/consolidation-job.ts +85 -78
  447. package/src/memory/v2/frontmatter-sweep.ts +91 -0
  448. package/src/memory/v2/injection.ts +440 -109
  449. package/src/memory/v2/migration.ts +117 -20
  450. package/src/memory/v2/page-index.ts +191 -0
  451. package/src/memory/v2/page-store.ts +3 -0
  452. package/src/memory/v2/prompts/consolidation.ts +9 -7
  453. package/src/memory/v2/prompts/router.ts +192 -0
  454. package/src/memory/v2/qdrant.ts +100 -87
  455. package/src/memory/v2/reranker.ts +14 -7
  456. package/src/memory/v2/router.ts +322 -0
  457. package/src/memory/v2/sim.ts +25 -12
  458. package/src/memory/v2/skill-store.ts +118 -29
  459. package/src/memory/v2/static-context.ts +16 -9
  460. package/src/memory/v2/sweep-job.ts +122 -96
  461. package/src/memory/v2/types.ts +10 -6
  462. package/src/memory/validation.ts +13 -0
  463. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +182 -0
  464. package/src/notifications/__tests__/home-feed-side-effect.test.ts +199 -0
  465. package/src/notifications/__tests__/signal-registry.test.ts +17 -0
  466. package/src/notifications/adapters/platform.ts +171 -0
  467. package/src/notifications/conversation-pairing.ts +2 -2
  468. package/src/notifications/copy-composer.ts +15 -0
  469. package/src/notifications/destination-resolver.ts +21 -0
  470. package/src/notifications/emit-signal.ts +28 -1
  471. package/src/notifications/home-feed-side-effect.ts +111 -0
  472. package/src/notifications/signal.ts +5 -0
  473. package/src/permissions/checker.ts +12 -0
  474. package/src/permissions/ipc-risk-types.ts +2 -0
  475. package/src/plugin-api/index.ts +13 -0
  476. package/src/plugin-api/package.json +12 -0
  477. package/src/plugin-api/types.ts +62 -0
  478. package/src/plugins/defaults/injectors.ts +19 -3
  479. package/src/plugins/external-plugin-loader.ts +294 -0
  480. package/src/plugins/types.ts +46 -30
  481. package/src/plugins/user-loader.ts +64 -41
  482. package/src/proactive-artifact/job.test.ts +12 -4
  483. package/src/proactive-artifact/job.ts +4 -0
  484. package/src/proactive-artifact/trigger-state.test.ts +9 -0
  485. package/src/proactive-artifact/trigger-state.ts +4 -0
  486. package/src/prompts/__tests__/system-prompt.test.ts +105 -0
  487. package/src/prompts/system-prompt.ts +22 -1
  488. package/src/prompts/update-bulletin-job.ts +61 -73
  489. package/src/providers/__tests__/dispatch-connection-routing.test.ts +279 -0
  490. package/src/providers/__tests__/inference.test.ts +288 -0
  491. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  492. package/src/providers/__tests__/provider-secret-catalog.test.ts +6 -0
  493. package/src/providers/__tests__/retry-callsite.test.ts +14 -32
  494. package/src/providers/__tests__/satellite-connection-routing.test.ts +510 -0
  495. package/src/providers/__tests__/search-provider-catalog.test.ts +80 -0
  496. package/src/providers/anthropic/client.ts +95 -26
  497. package/src/providers/call-site-routing.ts +94 -16
  498. package/src/providers/connection-resolution.ts +163 -0
  499. package/src/providers/inference/__tests__/connections-status-label.test.ts +250 -0
  500. package/src/providers/inference/adapter-factory.ts +173 -0
  501. package/src/providers/inference/auth.ts +112 -0
  502. package/src/providers/inference/backfill.ts +196 -0
  503. package/src/providers/inference/connections.ts +356 -0
  504. package/src/providers/inference/resolve-auth.ts +65 -0
  505. package/src/providers/model-catalog.ts +104 -6
  506. package/src/providers/openai/responses-provider.ts +4 -2
  507. package/src/providers/provider-env-vars.ts +17 -7
  508. package/src/providers/provider-secret-catalog.ts +49 -30
  509. package/src/providers/provider-send-message.ts +41 -20
  510. package/src/providers/registry.ts +143 -159
  511. package/src/providers/retry.ts +18 -10
  512. package/src/providers/search-provider-catalog.ts +121 -0
  513. package/src/runtime/AGENTS.md +18 -5
  514. package/src/runtime/__tests__/background-job-runner.test.ts +357 -0
  515. package/src/runtime/__tests__/pre-first-message-gate.test.ts +82 -0
  516. package/src/runtime/actor-trust-resolver.ts +32 -10
  517. package/src/runtime/agent-wake.ts +35 -6
  518. package/src/runtime/assistant-event-hub.ts +3 -85
  519. package/src/runtime/auth/route-policy.ts +303 -8
  520. package/src/runtime/auth/same-actor.ts +2 -0
  521. package/src/runtime/background-job-runner.ts +339 -0
  522. package/src/runtime/btw-sidechain.ts +1 -0
  523. package/src/runtime/http-router.ts +36 -1
  524. package/src/runtime/http-server.ts +31 -5
  525. package/src/runtime/http-types.ts +2 -0
  526. package/src/runtime/middleware/__tests__/request-logger.test.ts +162 -0
  527. package/src/runtime/middleware/request-logger.ts +62 -1
  528. package/src/runtime/pre-first-message-gate.ts +83 -0
  529. package/src/runtime/routes/__tests__/backup-routes.test.ts +8 -1
  530. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +251 -0
  531. package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +142 -0
  532. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +315 -0
  533. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +189 -0
  534. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +15 -136
  535. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +736 -0
  536. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +4 -4
  537. package/src/runtime/routes/__tests__/stt-routes.test.ts +5 -1
  538. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +384 -0
  539. package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
  540. package/src/runtime/routes/acp-routes.ts +10 -8
  541. package/src/runtime/routes/app-management-routes.ts +228 -3
  542. package/src/runtime/routes/approval-routes.ts +0 -18
  543. package/src/runtime/routes/audit-routes.ts +43 -0
  544. package/src/runtime/routes/auth-routes.ts +72 -0
  545. package/src/runtime/routes/avatar-routes.ts +273 -20
  546. package/src/runtime/routes/backup-routes.ts +406 -2
  547. package/src/runtime/routes/bookmark-routes.ts +154 -0
  548. package/src/runtime/routes/channel-verification-routes.ts +2 -1
  549. package/src/runtime/routes/contact-routes.ts +0 -160
  550. package/src/runtime/routes/conversation-cli-routes.ts +192 -0
  551. package/src/runtime/routes/conversation-management-routes.ts +30 -43
  552. package/src/runtime/routes/conversation-query-routes.ts +334 -86
  553. package/src/runtime/routes/conversation-routes.ts +31 -10
  554. package/src/runtime/routes/conversations-import-routes.ts +229 -0
  555. package/src/runtime/routes/credential-routes.ts +540 -0
  556. package/src/runtime/routes/debug-routes.ts +2 -2
  557. package/src/runtime/routes/document-pdf-renderer.ts +5 -1
  558. package/src/runtime/routes/domain-routes.ts +167 -0
  559. package/src/runtime/routes/email-routes.ts +603 -0
  560. package/src/runtime/routes/errors.ts +2 -2
  561. package/src/runtime/routes/events-routes.ts +192 -0
  562. package/src/runtime/routes/home-feed-routes.ts +6 -78
  563. package/src/runtime/routes/host-app-control-routes.ts +44 -2
  564. package/src/runtime/routes/host-browser-routes.ts +103 -22
  565. package/src/runtime/routes/http-adapter.ts +2 -0
  566. package/src/runtime/routes/identity-routes.ts +5 -0
  567. package/src/runtime/routes/image-generation-routes.ts +99 -0
  568. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +137 -1
  569. package/src/runtime/routes/inbound-stages/background-dispatch.ts +87 -7
  570. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +156 -0
  571. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +22 -4
  572. package/src/runtime/routes/index.ts +36 -0
  573. package/src/runtime/routes/inference-profile-session-handler.ts +312 -0
  574. package/src/runtime/routes/inference-profile-session-reaper.ts +98 -0
  575. package/src/runtime/routes/inference-profile-session-routes.ts +146 -0
  576. package/src/runtime/routes/inference-provider-connection-routes.ts +317 -0
  577. package/src/runtime/routes/inference-send-routes.ts +115 -0
  578. package/src/runtime/routes/integrations/twilio.ts +1 -0
  579. package/src/runtime/routes/mcp-auth-routes.ts +283 -9
  580. package/src/runtime/routes/memory-v2-routes.ts +13 -398
  581. package/src/runtime/routes/notification-routes.ts +2 -0
  582. package/src/runtime/routes/oauth-apps.ts +112 -7
  583. package/src/runtime/routes/oauth-commands-routes.ts +1007 -0
  584. package/src/runtime/routes/oauth-connect-routes.ts +67 -5
  585. package/src/runtime/routes/oauth-providers.ts +298 -8
  586. package/src/runtime/routes/platform-routes.ts +336 -0
  587. package/src/runtime/routes/playground/inject-failures.ts +2 -1
  588. package/src/runtime/routes/playground/reset-circuit.ts +2 -1
  589. package/src/runtime/routes/playground/state.ts +2 -1
  590. package/src/runtime/routes/publish-routes.ts +221 -0
  591. package/src/runtime/routes/schedule-routes.ts +82 -0
  592. package/src/runtime/routes/sequence-routes.ts +291 -0
  593. package/src/runtime/routes/settings-routes.ts +2 -10
  594. package/src/runtime/routes/skills-routes.ts +31 -1
  595. package/src/runtime/routes/stt-routes.ts +240 -3
  596. package/src/runtime/routes/surface-action-routes.ts +43 -7
  597. package/src/runtime/routes/tts-routes.ts +67 -0
  598. package/src/runtime/routes/types.ts +32 -0
  599. package/src/runtime/routes/user-routes-cli.ts +243 -0
  600. package/src/runtime/routes/webhook-routes.ts +165 -0
  601. package/src/runtime/sync/resource-sync-events.ts +25 -0
  602. package/src/runtime/sync/sync-publisher.test.ts +105 -0
  603. package/src/runtime/sync/sync-publisher.ts +21 -0
  604. package/src/schedule/scheduler.ts +200 -123
  605. package/src/security/__tests__/provider-key-env-fallback.test.ts +12 -6
  606. package/src/security/secret-patterns.ts +3 -0
  607. package/src/sequence/engine.ts +38 -40
  608. package/src/subagent/manager.ts +20 -15
  609. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +206 -0
  610. package/src/tools/browser/browser-execution.ts +15 -4
  611. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +174 -0
  612. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +16 -13
  613. package/src/tools/browser/cdp-client/extension-cdp-client.ts +24 -1
  614. package/src/tools/browser/cdp-client/factory.ts +66 -5
  615. package/src/tools/browser/runtime-check.ts +77 -0
  616. package/src/tools/memory/register.test.ts +3 -3
  617. package/src/tools/memory/register.ts +9 -1
  618. package/src/tools/network/__tests__/web-search.test.ts +156 -0
  619. package/src/tools/network/web-search.ts +280 -37
  620. package/src/tools/permission-checker.ts +13 -5
  621. package/src/tools/subagent/spawn.ts +3 -3
  622. package/src/tools/terminal/shell.ts +44 -0
  623. package/src/usage/attribution.ts +3 -2
  624. package/src/util/pricing.ts +86 -160
  625. package/src/watcher/__tests__/engine.test.ts +301 -0
  626. package/src/watcher/constants.ts +7 -0
  627. package/src/watcher/engine.ts +90 -90
  628. package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +6 -9
  629. package/src/workspace/migrations/054-seed-recall-callsite.ts +10 -1
  630. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +28 -4
  631. package/src/workspace/migrations/069-seed-onboarding-threads.ts +8 -2
  632. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +104 -0
  633. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +93 -0
  634. package/src/workspace/migrations/074-drop-deprecated-secret-detection-keys.ts +117 -0
  635. package/src/workspace/migrations/075-memory-v2-bm25-b-default-reembed.ts +61 -0
  636. package/src/workspace/migrations/076-drop-services-inference-mode.ts +62 -0
  637. package/src/workspace/migrations/077-seed-memory-router-callsite.ts +89 -0
  638. package/src/workspace/migrations/078-release-notes-tavily-web-search.ts +66 -0
  639. package/src/workspace/migrations/079-home-feed-notification-only.ts +197 -0
  640. package/src/workspace/migrations/080-restrict-vercel-api-token-metadata.ts +182 -0
  641. package/src/workspace/migrations/081-backfill-bash-allowed-tools-for-injection-credentials.ts +160 -0
  642. package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +154 -0
  643. package/src/workspace/migrations/registry.ts +22 -0
  644. package/src/workspace/migrations/runner.ts +13 -2
  645. package/src/workspace/migrations/types.ts +13 -3
  646. package/src/workspace/provider-commit-message-generator.ts +3 -2
  647. package/src/__tests__/context-search-pkb-source.test.ts +0 -498
  648. package/src/__tests__/credentials-cli.test.ts +0 -1225
  649. package/src/__tests__/memory-admin-recall.test.ts +0 -213
  650. package/src/approvals/__tests__/guardian-feed-event.test.ts +0 -303
  651. package/src/cli/commands/__tests__/email-download.test.ts +0 -260
  652. package/src/cli/commands/__tests__/email-list.test.ts +0 -216
  653. package/src/cli/commands/__tests__/email-register.test.ts +0 -186
  654. package/src/cli/commands/__tests__/email-send.test.ts +0 -416
  655. package/src/cli/commands/__tests__/email-status.test.ts +0 -185
  656. package/src/cli/commands/__tests__/email-unregister.test.ts +0 -168
  657. package/src/cli/commands/__tests__/routes.test.ts +0 -562
  658. package/src/cli/commands/__tests__/stt-transcribe.test.ts +0 -454
  659. package/src/cli/commands/autonomy.ts +0 -365
  660. package/src/cli/commands/memory.ts +0 -424
  661. package/src/cli/commands/oauth/__tests__/connect.test.ts +0 -947
  662. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +0 -686
  663. package/src/cli/commands/oauth/__tests__/mode.test.ts +0 -632
  664. package/src/cli/commands/oauth/__tests__/ping.test.ts +0 -631
  665. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +0 -573
  666. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +0 -330
  667. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +0 -521
  668. package/src/cli/commands/oauth/__tests__/status.test.ts +0 -551
  669. package/src/cli/commands/oauth/__tests__/token.test.ts +0 -420
  670. package/src/cli/lib/daemon-avatar-client.ts +0 -37
  671. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -87
  672. package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +0 -207
  673. package/src/daemon/__tests__/conversation-feed-event.test.ts +0 -304
  674. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +0 -233
  675. package/src/home/__tests__/assistant-feed-authoring.test.ts +0 -156
  676. package/src/home/__tests__/emit-feed-event.test.ts +0 -169
  677. package/src/home/__tests__/feed-population-integration.test.ts +0 -312
  678. package/src/home/__tests__/feed-scheduler.test.ts +0 -222
  679. package/src/home/__tests__/phase5-exit-criteria.test.ts +0 -229
  680. package/src/home/__tests__/platform-gmail-digest.test.ts +0 -222
  681. package/src/home/__tests__/rollup-producer.test.ts +0 -507
  682. package/src/home/assistant-feed-authoring.ts +0 -135
  683. package/src/home/emit-feed-event.ts +0 -169
  684. package/src/home/feed-scheduler.ts +0 -281
  685. package/src/home/platform-gmail-digest.ts +0 -163
  686. package/src/home/rewrite-command-preview.ts +0 -66
  687. package/src/home/rewrite-feed-title.ts +0 -58
  688. package/src/home/rollup-producer.ts +0 -426
  689. package/src/memory/admin.ts +0 -326
  690. package/src/memory/context-search/sources/pkb.ts +0 -476
  691. package/src/memory/graph/compaction.ts +0 -299
  692. /package/src/cli/{commands → lib}/cache-fs.ts +0 -0
@@ -1,117 +1,26 @@
1
- /**
2
- * Tests for the `assistant image-generation` CLI command.
3
- *
4
- * Validates:
5
- * - Help text for `image-generation` and `image-generation generate`
6
- * - Required --prompt enforcement
7
- * - Managed vs your-own credential resolution
8
- * - Error when no credentials are available
9
- * - Generate mode success (file written, path on stdout)
10
- * - --json output format
11
- * - Edit mode with --source (source images passed through)
12
- * - --variants passed through
13
- * - --model override
14
- * - Provider dispatch (gemini vs openai)
15
- */
16
-
17
- import { existsSync, readFileSync } from "node:fs";
18
- import os from "node:os";
19
- import { join } from "node:path";
20
- import { beforeEach, describe, expect, mock, test } from "bun:test";
1
+ import { beforeEach, describe, expect, it, mock } from "bun:test";
21
2
 
22
3
  import { Command } from "commander";
23
4
 
24
- // ---------------------------------------------------------------------------
25
- // Mock state
26
- // ---------------------------------------------------------------------------
27
-
28
- /** Config returned by getConfig() */
29
- let mockConfig = {
30
- services: {
31
- "image-generation": {
32
- mode: "your-own" as "managed" | "your-own",
33
- provider: "gemini" as "gemini" | "openai",
34
- model: "gemini-3.1-flash-image-preview",
35
- },
36
- },
37
- };
38
-
39
- /** Context returned by resolveManagedProxyContext() */
40
- let mockManagedProxyContext = {
41
- enabled: false,
42
- platformBaseUrl: "",
43
- assistantApiKey: "",
44
- };
45
-
46
- /** Key returned by getProviderKeyAsync(). Keyed by provider. */
47
- let mockProviderKeys: Record<string, string | undefined> = {};
48
-
49
- /** Result returned by generateImage() */
50
- let mockGenerateResult: {
51
- images: Array<{ mimeType: string; dataBase64: string; title?: string }>;
52
- text?: string;
53
- resolvedModel: string;
54
- } = {
55
- images: [
56
- {
57
- mimeType: "image/png",
58
- dataBase64: Buffer.from("fake-png-data").toString("base64"),
59
- },
60
- ],
61
- resolvedModel: "gemini-3.1-flash-image-preview",
5
+ // State for mock
6
+ let mockCalls: Array<[string, Record<string, unknown>]> = [];
7
+ let mockResponse: unknown = {
8
+ ok: true,
9
+ result: { images: [], resolvedModel: "gemini-3.1-flash-image-preview" },
62
10
  };
63
11
 
64
- /** Error to throw from generateImage() (if set) */
65
- let mockGenerateError: Error | undefined = undefined;
66
-
67
- /** Captured generateImage call args */
68
- let lastGenerateCall: {
69
- provider: unknown;
70
- credentials: unknown;
71
- request: unknown;
72
- } | null = null;
73
-
74
- // ---------------------------------------------------------------------------
75
- // Mocks
76
- // ---------------------------------------------------------------------------
77
-
78
- mock.module("../../../config/loader.js", () => ({
79
- getConfig: () => mockConfig,
80
- getConfigReadOnly: () => mockConfig,
81
- }));
82
-
83
- mock.module("../../../providers/managed-proxy/context.js", () => ({
84
- resolveManagedProxyContext: async () => mockManagedProxyContext,
85
- }));
86
-
87
- mock.module("../../../security/secure-keys.js", () => ({
88
- getProviderKeyAsync: async (provider: string) => mockProviderKeys[provider],
89
- }));
90
-
91
- mock.module("../../../media/image-service.js", () => ({
92
- generateImage: async (
93
- provider: unknown,
94
- credentials: unknown,
95
- request: unknown,
96
- ) => {
97
- lastGenerateCall = { provider, credentials, request };
98
- if (mockGenerateError) throw mockGenerateError;
99
- return mockGenerateResult;
12
+ // Mock BEFORE importing the command module
13
+ mock.module("../../../ipc/cli-client.js", () => ({
14
+ cliIpcCall: async (method: string, params: Record<string, unknown>) => {
15
+ mockCalls.push([method, params]);
16
+ return mockResponse;
100
17
  },
101
- mapImageGenError: (provider: unknown, error: unknown) => {
102
- if (error instanceof Error)
103
- return `Mapped error (${String(provider)}): ${error.message}`;
104
- return "An unexpected error occurred during image generation.";
105
- },
106
- providerForModel: (model: string | undefined, fallback: string) => {
107
- if (!model) return fallback;
108
- if (model.startsWith("gpt-") || model.startsWith("dall-e-"))
109
- return "openai";
110
- if (model.startsWith("gemini-")) return "gemini";
111
- return fallback;
18
+ exitFromIpcResult: (_r: unknown, _cmd: unknown) => {
19
+ throw new Error(`exitFromIpcResult called`);
112
20
  },
113
21
  }));
114
22
 
23
+ // Also mock the logger to suppress output
115
24
  mock.module("../../../util/logger.js", () => ({
116
25
  getLogger: () => ({
117
26
  info: () => {},
@@ -127,760 +36,114 @@ mock.module("../../../util/logger.js", () => ({
127
36
  }),
128
37
  }));
129
38
 
130
- // ---------------------------------------------------------------------------
131
- // Import module under test (after mocks)
132
- // ---------------------------------------------------------------------------
133
-
134
- const { registerImageGenerationCommand } =
135
- await import("../image-generation.js");
136
-
137
- // ---------------------------------------------------------------------------
138
- // Test helper
139
- // ---------------------------------------------------------------------------
140
-
141
- async function runCommand(
142
- args: string[],
143
- ): Promise<{ stdout: string; stderr: string; exitCode: number }> {
144
- const originalStdoutWrite = process.stdout.write.bind(process.stdout);
145
- const originalStderrWrite = process.stderr.write.bind(process.stderr);
146
- const stdoutChunks: string[] = [];
147
- const stderrChunks: string[] = [];
148
-
149
- process.stdout.write = ((chunk: unknown) => {
150
- stdoutChunks.push(typeof chunk === "string" ? chunk : String(chunk));
151
- return true;
152
- }) as typeof process.stdout.write;
153
-
154
- process.stderr.write = ((chunk: unknown) => {
155
- stderrChunks.push(typeof chunk === "string" ? chunk : String(chunk));
156
- return true;
157
- }) as typeof process.stderr.write;
39
+ // We can set outputDir via --output-dir to avoid needing to mock os.tmpdir
40
+ const TEST_OUTPUT_DIR = "/tmp/test-image-gen";
158
41
 
159
- process.exitCode = 0;
42
+ const { registerImageGenerationCommand } = await import("../image-generation.js");
160
43
 
161
- try {
162
- const program = new Command();
163
- program.exitOverride();
164
- program.configureOutput({
165
- writeErr: (str: string) => stderrChunks.push(str),
166
- writeOut: (str: string) => stdoutChunks.push(str),
167
- });
168
- registerImageGenerationCommand(program);
169
- await program.parseAsync(["node", "assistant", ...args]);
170
- } catch {
171
- if (process.exitCode === 0) process.exitCode = 1;
172
- } finally {
173
- process.stdout.write = originalStdoutWrite;
174
- process.stderr.write = originalStderrWrite;
175
- }
176
-
177
- const exitCode = process.exitCode ?? 0;
178
- process.exitCode = 0;
179
-
180
- return {
181
- exitCode,
182
- stdout: stdoutChunks.join(""),
183
- stderr: stderrChunks.join(""),
184
- };
44
+ function buildProgram(): Command {
45
+ const program = new Command();
46
+ program.exitOverride(); // prevent process.exit
47
+ registerImageGenerationCommand(program);
48
+ return program;
185
49
  }
186
50
 
187
- // ---------------------------------------------------------------------------
188
- // Setup / Teardown
189
- // ---------------------------------------------------------------------------
190
-
191
- beforeEach(() => {
192
- mockConfig = {
193
- services: {
194
- "image-generation": {
195
- mode: "your-own",
196
- provider: "gemini",
197
- model: "gemini-3.1-flash-image-preview",
198
- },
199
- },
200
- };
201
- mockManagedProxyContext = {
202
- enabled: false,
203
- platformBaseUrl: "",
204
- assistantApiKey: "",
205
- };
206
- mockProviderKeys = {};
207
- mockGenerateResult = {
208
- images: [
209
- {
210
- mimeType: "image/png",
211
- dataBase64: Buffer.from("fake-png-data").toString("base64"),
212
- },
213
- ],
214
- resolvedModel: "gemini-3.1-flash-image-preview",
215
- };
216
- mockGenerateError = undefined;
217
- lastGenerateCall = null;
218
- process.exitCode = 0;
219
- });
220
-
221
- // ---------------------------------------------------------------------------
222
- // Help text
223
- // ---------------------------------------------------------------------------
224
-
225
- describe("help text", () => {
226
- test("image-generation --help renders mode explanation and examples", async () => {
227
- const { stdout } = await runCommand(["image-generation", "--help"]);
228
- expect(stdout).toContain("AI image generation and editing");
229
- expect(stdout).toContain("managed");
230
- expect(stdout).toContain("your-own");
231
- expect(stdout).toContain("Examples:");
232
- expect(stdout).toContain("generate");
233
- // Both providers mentioned and gpt-image-2 listed as a supported model.
234
- expect(stdout).toContain("Gemini");
235
- expect(stdout).toContain("OpenAI");
236
- expect(stdout).toContain("gpt-image-2");
237
- });
238
-
239
- test("image-generation generate --help renders argument docs and examples", async () => {
240
- const { stdout } = await runCommand([
241
- "image-generation",
242
- "generate",
243
- "--help",
244
- ]);
245
- expect(stdout).toContain("--prompt");
246
- expect(stdout).toContain("--mode");
247
- expect(stdout).toContain("--source");
248
- expect(stdout).toContain("--variants");
249
- expect(stdout).toContain("--output-dir");
250
- expect(stdout).toContain("--json");
251
- expect(stdout).toContain("Examples:");
252
- });
253
- });
254
-
255
- // ---------------------------------------------------------------------------
256
- // Required --prompt enforcement
257
- // ---------------------------------------------------------------------------
258
-
259
- describe("required arguments", () => {
260
- test("generate requires --prompt", async () => {
261
- mockProviderKeys.gemini = "test-key";
262
- const { exitCode } = await runCommand(["image-generation", "generate"]);
263
- expect(exitCode).not.toBe(0);
264
- });
265
- });
266
-
267
- // ---------------------------------------------------------------------------
268
- // No credentials error
269
- // ---------------------------------------------------------------------------
270
-
271
- describe("credential errors", () => {
272
- test("exits with code 1 when no credentials in your-own mode", async () => {
273
- mockConfig.services["image-generation"].mode = "your-own";
274
- mockProviderKeys.gemini = undefined;
275
-
276
- const { exitCode } = await runCommand([
277
- "image-generation",
278
- "generate",
279
- "--prompt",
280
- "A sunset",
281
- ]);
282
- expect(exitCode).toBe(1);
283
- });
284
-
285
- test("exits with code 1 when no credentials in managed mode", async () => {
286
- mockConfig.services["image-generation"].mode = "managed";
287
-
288
- const { exitCode } = await runCommand([
289
- "image-generation",
290
- "generate",
291
- "--prompt",
292
- "A sunset",
293
- ]);
294
- expect(exitCode).toBe(1);
295
- });
296
-
297
- test("--json outputs error when no credentials in your-own mode (gemini)", async () => {
298
- mockConfig.services["image-generation"].mode = "your-own";
299
- mockConfig.services["image-generation"].provider = "gemini";
300
- mockProviderKeys.gemini = undefined;
301
-
302
- const { exitCode, stdout } = await runCommand([
303
- "image-generation",
304
- "generate",
305
- "--prompt",
306
- "A sunset",
307
- "--json",
308
- ]);
309
- expect(exitCode).toBe(1);
310
- const parsed = JSON.parse(stdout.trim());
311
- expect(parsed.ok).toBe(false);
312
- expect(parsed.error).toContain("Gemini API key");
313
- // CLI prepends the actionable `assistant keys set` command with the
314
- // resolved provider — the shared hint is UI-neutral ("Settings") so
315
- // without this prefix CLI users get no copy-pasteable next step.
316
- expect(parsed.error).toContain("Run: assistant keys set gemini <key>");
317
- });
318
-
319
- test("--json outputs OpenAI-specific hint when provider=openai and no key set", async () => {
320
- mockConfig.services["image-generation"].mode = "your-own";
321
- mockConfig.services["image-generation"].provider = "openai";
322
- mockConfig.services["image-generation"].model = "gpt-image-2";
323
- mockProviderKeys.openai = undefined;
324
-
325
- const { exitCode, stdout } = await runCommand([
326
- "image-generation",
327
- "generate",
328
- "--prompt",
329
- "A sunset",
330
- "--json",
331
- ]);
332
- expect(exitCode).toBe(1);
333
- const parsed = JSON.parse(stdout.trim());
334
- expect(parsed.ok).toBe(false);
335
- expect(parsed.error).toContain("OpenAI API key");
336
- expect(parsed.error).toContain("Run: assistant keys set openai <key>");
337
- });
338
-
339
- test("--json outputs error when no credentials in managed mode", async () => {
340
- mockConfig.services["image-generation"].mode = "managed";
341
-
342
- const { exitCode, stdout } = await runCommand([
343
- "image-generation",
344
- "generate",
345
- "--prompt",
346
- "A sunset",
347
- "--json",
348
- ]);
349
- expect(exitCode).toBe(1);
350
- const parsed = JSON.parse(stdout.trim());
351
- expect(parsed.ok).toBe(false);
352
- // Base hint from image-credentials.ts is preserved.
353
- expect(parsed.error).toContain("Managed proxy");
354
- // CLI augments the hint with CLI-specific recovery guidance so users
355
- // know how to resolve the problem from the CLI (the shared hint is
356
- // tool-flavored and only mentions the Vellum app).
357
- expect(parsed.error).toContain("assistant auth login");
358
- expect(parsed.error).toContain(
359
- "services.image-generation.mode to 'your-own'",
360
- );
361
- });
362
-
363
- test("your-own mode hint is NOT augmented with CLI auth guidance", async () => {
364
- // The CLI-specific `assistant auth login` guidance only makes sense when
365
- // the user is trying to use managed mode. In your-own mode, the shared
366
- // hint (pointing at Settings > Models & Services for the API key) is
367
- // already actionable from the CLI perspective (the key is in secure
368
- // storage, the user just hasn't set it). Augmenting here would confuse
369
- // the user into thinking they need to authenticate to Vellum.
370
- mockConfig.services["image-generation"].mode = "your-own";
371
- mockConfig.services["image-generation"].provider = "gemini";
372
- mockProviderKeys.gemini = undefined;
373
-
374
- const { exitCode, stdout } = await runCommand([
375
- "image-generation",
376
- "generate",
377
- "--prompt",
378
- "A sunset",
379
- "--json",
380
- ]);
381
- expect(exitCode).toBe(1);
382
- const parsed = JSON.parse(stdout.trim());
383
- expect(parsed.ok).toBe(false);
384
- expect(parsed.error).not.toContain("assistant auth login");
385
- });
386
- });
387
-
388
- // ---------------------------------------------------------------------------
389
- // Credential resolution paths
390
- // ---------------------------------------------------------------------------
391
-
392
- describe("credential resolution", () => {
393
- test("your-own mode uses getProviderKeyAsync for direct credentials", async () => {
394
- mockConfig.services["image-generation"].mode = "your-own";
395
- mockProviderKeys.gemini = "test-gemini-key";
396
-
397
- await runCommand([
398
- "image-generation",
399
- "generate",
400
- "--prompt",
401
- "Test",
402
- "--output-dir",
403
- os.tmpdir(),
404
- ]);
405
-
406
- expect(lastGenerateCall).toBeDefined();
407
- const creds = lastGenerateCall!.credentials as {
408
- type: string;
409
- apiKey: string;
410
- };
411
- expect(creds.type).toBe("direct");
412
- expect(creds.apiKey).toBe("test-gemini-key");
413
- });
414
-
415
- test("managed mode uses resolveManagedProxyContext", async () => {
416
- mockConfig.services["image-generation"].mode = "managed";
417
- mockManagedProxyContext = {
418
- enabled: true,
419
- platformBaseUrl: "https://platform.example.com",
420
- assistantApiKey: "managed-api-key",
421
- };
422
-
423
- await runCommand([
424
- "image-generation",
425
- "generate",
426
- "--prompt",
427
- "Test",
428
- "--output-dir",
429
- os.tmpdir(),
430
- ]);
431
-
432
- expect(lastGenerateCall).toBeDefined();
433
- const creds = lastGenerateCall!.credentials as {
434
- type: string;
435
- assistantApiKey: string;
436
- baseUrl: string;
437
- };
438
- expect(creds.type).toBe("managed-proxy");
439
- expect(creds.assistantApiKey).toBe("managed-api-key");
440
- expect(creds.baseUrl).toBe(
441
- "https://platform.example.com/v1/runtime-proxy/gemini",
442
- );
443
- });
444
- });
445
-
446
- // ---------------------------------------------------------------------------
447
- // Provider dispatch
448
- // ---------------------------------------------------------------------------
449
-
450
- describe("provider dispatch", () => {
451
- test("provider=gemini is forwarded to the dispatcher", async () => {
452
- mockConfig.services["image-generation"].mode = "your-own";
453
- mockConfig.services["image-generation"].provider = "gemini";
454
- mockProviderKeys.gemini = "test-gemini-key";
455
- const outDir = join(os.tmpdir(), `img-dispatch-gemini-${Date.now()}`);
456
-
457
- const { exitCode } = await runCommand([
458
- "image-generation",
459
- "generate",
460
- "--prompt",
461
- "Test",
462
- "--output-dir",
463
- outDir,
464
- ]);
465
-
466
- expect(exitCode).toBe(0);
467
- expect(lastGenerateCall).toBeDefined();
468
- expect(lastGenerateCall!.provider).toBe("gemini");
469
- });
470
-
471
- test("provider=openai with --model gpt-image-2 is forwarded to the dispatcher", async () => {
472
- mockConfig.services["image-generation"].mode = "your-own";
473
- mockConfig.services["image-generation"].provider = "openai";
474
- mockConfig.services["image-generation"].model = "gpt-image-2";
475
- mockProviderKeys.openai = "test-openai-key";
476
- mockGenerateResult = {
477
- images: [
478
- {
479
- mimeType: "image/png",
480
- dataBase64: Buffer.from("fake-png-data").toString("base64"),
481
- },
482
- ],
483
- resolvedModel: "gpt-image-2",
51
+ describe("image-generation generate", () => {
52
+ beforeEach(() => {
53
+ mockCalls = [];
54
+ mockResponse = {
55
+ ok: true,
56
+ result: { images: [], resolvedModel: "gemini-3.1-flash-image-preview" },
484
57
  };
485
- const outDir = join(os.tmpdir(), `img-dispatch-openai-${Date.now()}`);
486
-
487
- const { exitCode } = await runCommand([
488
- "image-generation",
489
- "generate",
490
- "--prompt",
491
- "Test",
492
- "--model",
493
- "gpt-image-2",
494
- "--output-dir",
495
- outDir,
496
- ]);
497
-
498
- expect(exitCode).toBe(0);
499
- expect(lastGenerateCall).toBeDefined();
500
- expect(lastGenerateCall!.provider).toBe("openai");
501
- const req = lastGenerateCall!.request as { model: string };
502
- expect(req.model).toBe("gpt-image-2");
503
- const creds = lastGenerateCall!.credentials as {
504
- type: string;
505
- apiKey: string;
506
- };
507
- expect(creds.type).toBe("direct");
508
- expect(creds.apiKey).toBe("test-openai-key");
509
- });
510
-
511
- test("cross-provider override: config=gemini + --model gpt-image-2 dispatches to openai", async () => {
512
- // Config still points at gemini (the user's Settings default), but the
513
- // CLI caller explicitly picks gpt-image-2. The command must dispatch to
514
- // OpenAI and resolve OpenAI credentials, not fall back to Gemini's
515
- // default model.
516
- mockConfig.services["image-generation"].mode = "your-own";
517
- mockConfig.services["image-generation"].provider = "gemini";
518
- mockConfig.services["image-generation"].model =
519
- "gemini-3.1-flash-image-preview";
520
- mockProviderKeys.openai = "test-openai-key";
521
- mockGenerateResult = {
522
- images: [
523
- {
524
- mimeType: "image/png",
525
- dataBase64: Buffer.from("fake-png-data").toString("base64"),
526
- },
527
- ],
528
- resolvedModel: "gpt-image-2",
529
- };
530
- const outDir = join(os.tmpdir(), `img-cross-openai-${Date.now()}`);
531
-
532
- const { exitCode } = await runCommand([
533
- "image-generation",
534
- "generate",
535
- "--prompt",
536
- "Test",
537
- "--model",
538
- "gpt-image-2",
539
- "--output-dir",
540
- outDir,
541
- ]);
542
-
543
- expect(exitCode).toBe(0);
544
- expect(lastGenerateCall).toBeDefined();
545
- expect(lastGenerateCall!.provider).toBe("openai");
546
- const req = lastGenerateCall!.request as { model: string };
547
- expect(req.model).toBe("gpt-image-2");
548
- const creds = lastGenerateCall!.credentials as {
549
- type: string;
550
- apiKey: string;
551
- };
552
- expect(creds.type).toBe("direct");
553
- expect(creds.apiKey).toBe("test-openai-key");
58
+ process.exitCode = 0;
554
59
  });
555
60
 
556
- test("cross-provider override: config=openai + --model gemini-3-pro-image-preview dispatches to gemini", async () => {
557
- mockConfig.services["image-generation"].mode = "your-own";
558
- mockConfig.services["image-generation"].provider = "openai";
559
- mockConfig.services["image-generation"].model = "gpt-image-2";
560
- mockProviderKeys.gemini = "test-gemini-key";
561
- mockGenerateResult = {
562
- images: [
563
- {
564
- mimeType: "image/png",
565
- dataBase64: Buffer.from("fake-png-data").toString("base64"),
566
- },
567
- ],
568
- resolvedModel: "gemini-3-pro-image-preview",
569
- };
570
- const outDir = join(os.tmpdir(), `img-cross-gemini-${Date.now()}`);
571
-
572
- const { exitCode } = await runCommand([
61
+ it("calls image_generation_generate with prompt and mode defaults", async () => {
62
+ const program = buildProgram();
63
+ await program.parseAsync([
64
+ "node",
65
+ "assistant",
573
66
  "image-generation",
574
67
  "generate",
575
68
  "--prompt",
576
- "Test",
577
- "--model",
578
- "gemini-3-pro-image-preview",
69
+ "A cat",
579
70
  "--output-dir",
580
- outDir,
581
- ]);
582
-
583
- expect(exitCode).toBe(0);
584
- expect(lastGenerateCall).toBeDefined();
585
- expect(lastGenerateCall!.provider).toBe("gemini");
586
- const req = lastGenerateCall!.request as { model: string };
587
- expect(req.model).toBe("gemini-3-pro-image-preview");
588
- const creds = lastGenerateCall!.credentials as {
589
- type: string;
590
- apiKey: string;
591
- };
592
- expect(creds.type).toBe("direct");
593
- expect(creds.apiKey).toBe("test-gemini-key");
594
- });
595
- });
596
-
597
- // ---------------------------------------------------------------------------
598
- // Generate mode success
599
- // ---------------------------------------------------------------------------
600
-
601
- describe("generate mode", () => {
602
- test("generates image and prints file path to stdout", async () => {
603
- mockProviderKeys.gemini = "test-key";
604
- const outDir = join(os.tmpdir(), `img-gen-test-${Date.now()}`);
605
-
606
- const { exitCode, stdout } = await runCommand([
607
- "image-generation",
608
- "generate",
609
- "--prompt",
610
- "A sunset over the ocean",
611
- "--output-dir",
612
- outDir,
613
- ]);
614
-
615
- expect(exitCode).toBe(0);
616
- const outputPath = stdout.trim();
617
- expect(outputPath).toContain("image-1.png");
618
- expect(existsSync(outputPath)).toBe(true);
619
-
620
- // Verify file content matches decoded base64
621
- const written = readFileSync(outputPath);
622
- const expected = Buffer.from(
623
- mockGenerateResult.images[0].dataBase64,
624
- "base64",
625
- );
626
- expect(written).toEqual(expected);
627
- });
628
-
629
- test("--json produces structured output with paths, MIME types, and sizes", async () => {
630
- mockProviderKeys.gemini = "test-key";
631
- mockGenerateResult = {
632
- images: [
633
- {
634
- mimeType: "image/png",
635
- dataBase64: Buffer.from("test-data").toString("base64"),
636
- },
637
- ],
638
- text: "A beautiful sunset",
639
- resolvedModel: "gemini-3.1-flash-image-preview",
640
- };
641
- const outDir = join(os.tmpdir(), `img-gen-json-${Date.now()}`);
642
-
643
- const { exitCode, stdout } = await runCommand([
644
- "image-generation",
645
- "generate",
646
- "--prompt",
647
- "Test",
648
- "--output-dir",
649
- outDir,
650
- "--json",
651
- ]);
652
-
653
- expect(exitCode).toBe(0);
654
- const parsed = JSON.parse(stdout.trim());
655
- expect(parsed.ok).toBe(true);
656
- expect(parsed.model).toBe("gemini-3.1-flash-image-preview");
657
- expect(parsed.text).toBe("A beautiful sunset");
658
- expect(parsed.images).toHaveLength(1);
659
- expect(parsed.images[0].path).toContain("image-1.png");
660
- expect(parsed.images[0].mimeType).toBe("image/png");
661
- expect(typeof parsed.images[0].sizeBytes).toBe("number");
662
- expect(parsed.images[0].sizeBytes).toBeGreaterThan(0);
663
- });
664
- });
665
-
666
- // ---------------------------------------------------------------------------
667
- // Edit mode with --source
668
- // ---------------------------------------------------------------------------
669
-
670
- describe("edit mode", () => {
671
- test("exits with code 1 when --mode edit is used without --source", async () => {
672
- mockProviderKeys.gemini = "test-key";
673
-
674
- const { exitCode, stdout } = await runCommand([
675
- "image-generation",
676
- "generate",
677
- "--prompt",
678
- "Remove background",
679
- "--mode",
680
- "edit",
681
- "--json",
71
+ TEST_OUTPUT_DIR,
682
72
  ]);
683
-
684
- expect(exitCode).toBe(1);
685
- const parsed = JSON.parse(stdout.trim());
686
- expect(parsed.ok).toBe(false);
687
- expect(parsed.error).toBe(
688
- "Edit mode requires at least one --source image file.",
689
- );
73
+ expect(mockCalls.length).toBe(1);
74
+ expect(mockCalls[0][0]).toBe("image_generation_generate");
75
+ const body = mockCalls[0][1].body as Record<string, unknown>;
76
+ expect(body.prompt).toBe("A cat");
77
+ expect(body.mode).toBe("generate");
78
+ expect(body.variants).toBe(1);
690
79
  });
691
80
 
692
- test("passes source images to generateImage in edit mode", async () => {
693
- mockProviderKeys.gemini = "test-key";
694
-
695
- // Use a real temp file for the source image
696
- const sourceDir = join(os.tmpdir(), `img-src-test-${Date.now()}`);
697
- const { mkdirSync, writeFileSync } = await import("node:fs");
698
- mkdirSync(sourceDir, { recursive: true });
699
- const sourcePath = join(sourceDir, "input.png");
700
- writeFileSync(sourcePath, Buffer.from("fake-source-png"));
701
-
702
- const outDir = join(os.tmpdir(), `img-edit-test-${Date.now()}`);
703
-
704
- const { exitCode } = await runCommand([
81
+ it("does not call IPC when edit mode has no --source", async () => {
82
+ const program = buildProgram();
83
+ await program.parseAsync([
84
+ "node",
85
+ "assistant",
705
86
  "image-generation",
706
87
  "generate",
707
88
  "--prompt",
708
- "Remove background",
89
+ "P",
709
90
  "--mode",
710
91
  "edit",
711
- "--source",
712
- sourcePath,
713
92
  "--output-dir",
714
- outDir,
93
+ TEST_OUTPUT_DIR,
715
94
  ]);
716
-
717
- expect(exitCode).toBe(0);
718
- expect(lastGenerateCall).toBeDefined();
719
- const req = lastGenerateCall!.request as {
720
- mode: string;
721
- sourceImages: Array<{ mimeType: string; dataBase64: string }>;
722
- };
723
- expect(req.mode).toBe("edit");
724
- expect(req.sourceImages).toBeDefined();
725
- expect(req.sourceImages).toHaveLength(1);
726
- expect(req.sourceImages[0].dataBase64).toBe(
727
- Buffer.from("fake-source-png").toString("base64"),
728
- );
95
+ expect(mockCalls.length).toBe(0);
96
+ expect(process.exitCode).toBe(1);
729
97
  });
730
- });
731
-
732
- // ---------------------------------------------------------------------------
733
- // --variants
734
- // ---------------------------------------------------------------------------
735
98
 
736
- describe("variants", () => {
737
- test("non-numeric --variants defaults to 1", async () => {
738
- mockProviderKeys.gemini = "test-key";
739
- const outDir = join(os.tmpdir(), `img-nan-variants-${Date.now()}`);
740
-
741
- const { exitCode } = await runCommand([
99
+ it("passes model override", async () => {
100
+ const program = buildProgram();
101
+ await program.parseAsync([
102
+ "node",
103
+ "assistant",
742
104
  "image-generation",
743
105
  "generate",
744
106
  "--prompt",
745
- "Test",
746
- "--variants",
747
- "abc",
107
+ "P",
108
+ "--model",
109
+ "gpt-image-2",
748
110
  "--output-dir",
749
- outDir,
111
+ TEST_OUTPUT_DIR,
750
112
  ]);
751
-
752
- expect(exitCode).toBe(0);
753
- expect(lastGenerateCall).toBeDefined();
754
- const req = lastGenerateCall!.request as { variants: number };
755
- expect(req.variants).toBe(1);
113
+ expect((mockCalls[0][1].body as Record<string, unknown>).model).toBe("gpt-image-2");
756
114
  });
757
115
 
758
- test("--variants is passed through to generateImage", async () => {
759
- mockProviderKeys.gemini = "test-key";
760
- mockGenerateResult = {
761
- images: [
762
- {
763
- mimeType: "image/png",
764
- dataBase64: Buffer.from("img1").toString("base64"),
765
- },
766
- {
767
- mimeType: "image/png",
768
- dataBase64: Buffer.from("img2").toString("base64"),
769
- },
770
- {
771
- mimeType: "image/png",
772
- dataBase64: Buffer.from("img3").toString("base64"),
773
- },
774
- ],
775
- resolvedModel: "gemini-3.1-flash-image-preview",
776
- };
777
- const outDir = join(os.tmpdir(), `img-variants-test-${Date.now()}`);
778
-
779
- const { exitCode, stdout } = await runCommand([
116
+ it("passes variants", async () => {
117
+ const program = buildProgram();
118
+ await program.parseAsync([
119
+ "node",
120
+ "assistant",
780
121
  "image-generation",
781
122
  "generate",
782
123
  "--prompt",
783
- "Logo design",
124
+ "P",
784
125
  "--variants",
785
126
  "3",
786
127
  "--output-dir",
787
- outDir,
788
- ]);
789
-
790
- expect(exitCode).toBe(0);
791
- expect(lastGenerateCall).toBeDefined();
792
- const req = lastGenerateCall!.request as { variants: number };
793
- expect(req.variants).toBe(3);
794
-
795
- // Should output 3 file paths
796
- const lines = stdout.trim().split("\n");
797
- expect(lines).toHaveLength(3);
798
- expect(lines[0]).toContain("image-1.png");
799
- expect(lines[1]).toContain("image-2.png");
800
- expect(lines[2]).toContain("image-3.png");
801
- });
802
- });
803
-
804
- // ---------------------------------------------------------------------------
805
- // --model override
806
- // ---------------------------------------------------------------------------
807
-
808
- describe("model override", () => {
809
- test("--model overrides config model", async () => {
810
- mockProviderKeys.gemini = "test-key";
811
- const outDir = join(os.tmpdir(), `img-model-test-${Date.now()}`);
812
-
813
- await runCommand([
814
- "image-generation",
815
- "generate",
816
- "--prompt",
817
- "Test",
818
- "--model",
819
- "gemini-3-pro-image-preview",
820
- "--output-dir",
821
- outDir,
822
- ]);
823
-
824
- expect(lastGenerateCall).toBeDefined();
825
- const req = lastGenerateCall!.request as { model: string };
826
- expect(req.model).toBe("gemini-3-pro-image-preview");
827
- });
828
-
829
- test("falls back to config model when --model is not provided", async () => {
830
- mockProviderKeys.gemini = "test-key";
831
- mockConfig.services["image-generation"].model =
832
- "gemini-3.1-flash-image-preview";
833
- const outDir = join(os.tmpdir(), `img-model-fallback-${Date.now()}`);
834
-
835
- await runCommand([
836
- "image-generation",
837
- "generate",
838
- "--prompt",
839
- "Test",
840
- "--output-dir",
841
- outDir,
842
- ]);
843
-
844
- expect(lastGenerateCall).toBeDefined();
845
- const req = lastGenerateCall!.request as { model: string };
846
- expect(req.model).toBe("gemini-3.1-flash-image-preview");
847
- });
848
- });
849
-
850
- // ---------------------------------------------------------------------------
851
- // Error handling
852
- // ---------------------------------------------------------------------------
853
-
854
- describe("error handling", () => {
855
- test("maps generateImage error and exits with code 1", async () => {
856
- mockProviderKeys.gemini = "test-key";
857
- mockGenerateError = new Error("API rate limit exceeded");
858
-
859
- const { exitCode } = await runCommand([
860
- "image-generation",
861
- "generate",
862
- "--prompt",
863
- "Test",
864
- ]);
865
-
866
- expect(exitCode).toBe(1);
867
- });
868
-
869
- test("--json outputs mapped error on generateImage failure", async () => {
870
- mockProviderKeys.gemini = "test-key";
871
- mockGenerateError = new Error("Connection timeout");
872
-
873
- const { exitCode, stdout } = await runCommand([
874
- "image-generation",
875
- "generate",
876
- "--prompt",
877
- "Test",
878
- "--json",
879
- ]);
880
-
881
- expect(exitCode).toBe(1);
882
- const parsed = JSON.parse(stdout.trim());
883
- expect(parsed.ok).toBe(false);
884
- expect(parsed.error).toContain("Connection timeout");
128
+ TEST_OUTPUT_DIR,
129
+ ]);
130
+ expect((mockCalls[0][1].body as Record<string, unknown>).variants).toBe(3);
131
+ });
132
+
133
+ it("calls exitFromIpcResult on daemon error", async () => {
134
+ mockResponse = { ok: false, error: "no creds", statusCode: 422 };
135
+ const program = buildProgram();
136
+ await expect(
137
+ program.parseAsync([
138
+ "node",
139
+ "assistant",
140
+ "image-generation",
141
+ "generate",
142
+ "--prompt",
143
+ "P",
144
+ "--output-dir",
145
+ TEST_OUTPUT_DIR,
146
+ ]),
147
+ ).rejects.toThrow("exitFromIpcResult called");
885
148
  });
886
149
  });