@vellumai/assistant 0.6.0 → 0.6.2

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 (358) hide show
  1. package/AGENTS.md +4 -0
  2. package/ARCHITECTURE.md +68 -15
  3. package/Dockerfile +2 -2
  4. package/bun.lock +6 -2
  5. package/docker-entrypoint.sh +42 -1
  6. package/docs/architecture/integrations.md +1 -1
  7. package/docs/architecture/memory.md +21 -24
  8. package/node_modules/@vellumai/ces-contracts/src/handles.ts +7 -9
  9. package/openapi.yaml +539 -4
  10. package/package.json +5 -1
  11. package/src/__tests__/anthropic-provider.test.ts +160 -95
  12. package/src/__tests__/app-dir-path-guard.test.ts +1 -0
  13. package/src/__tests__/app-executors.test.ts +47 -1
  14. package/src/__tests__/app-source-watcher.test.ts +159 -0
  15. package/src/__tests__/assistant-event-hub.test.ts +30 -0
  16. package/src/__tests__/checker.test.ts +138 -172
  17. package/src/__tests__/cli-command-risk-guard.test.ts +1 -1
  18. package/src/__tests__/config-schema.test.ts +5 -0
  19. package/src/__tests__/context-overflow-approval.test.ts +5 -5
  20. package/src/__tests__/conversation-agent-loop-overflow.test.ts +4 -6
  21. package/src/__tests__/conversation-agent-loop.test.ts +4 -51
  22. package/src/__tests__/conversation-analysis-routes.test.ts +169 -0
  23. package/src/__tests__/conversation-directories-parse.test.ts +105 -0
  24. package/src/__tests__/conversation-history-web-search.test.ts +1 -1
  25. package/src/__tests__/conversation-runtime-assembly.test.ts +653 -832
  26. package/src/__tests__/conversation-runtime-workspace.test.ts +1 -93
  27. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +17 -4
  28. package/src/__tests__/conversation-wipe.test.ts +2 -6
  29. package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -12
  30. package/src/__tests__/conversation-workspace-injection.test.ts +25 -26
  31. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
  32. package/src/__tests__/copy-composer-tc-templates.test.ts +335 -0
  33. package/src/__tests__/credential-execution-approval-bridge.test.ts +0 -2
  34. package/src/__tests__/date-context.test.ts +76 -210
  35. package/src/__tests__/db-schedule-syntax-migration.test.ts +16 -1
  36. package/src/__tests__/file-list-tool.test.ts +219 -0
  37. package/src/__tests__/first-greeting.test.ts +1 -1
  38. package/src/__tests__/heartbeat-service.test.ts +180 -3
  39. package/src/__tests__/identity-routes.test.ts +328 -0
  40. package/src/__tests__/init-feature-flag-overrides.test.ts +167 -0
  41. package/src/__tests__/injection-block.test.ts +24 -0
  42. package/src/__tests__/inline-command-runner.test.ts +7 -5
  43. package/src/__tests__/install-skill-routing.test.ts +7 -6
  44. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +15 -14
  45. package/src/__tests__/list-messages-tool-merge.test.ts +300 -0
  46. package/src/__tests__/llm-context-normalization.test.ts +18 -18
  47. package/src/__tests__/llm-context-route-provider.test.ts +101 -0
  48. package/src/__tests__/llm-request-log-turn-query.test.ts +162 -0
  49. package/src/__tests__/log-export-workspace.test.ts +257 -100
  50. package/src/__tests__/managed-credential-catalog-cli.test.ts +12 -14
  51. package/src/__tests__/mcp-abort-signal.test.ts +5 -0
  52. package/src/__tests__/mcp-client-auth.test.ts +5 -0
  53. package/src/__tests__/memory-recall-log-store.test.ts +132 -0
  54. package/src/__tests__/migration-export-streaming.test.ts +304 -0
  55. package/src/__tests__/migration-import-commit-http.test.ts +11 -10
  56. package/src/__tests__/mock-fetch.ts +87 -0
  57. package/src/__tests__/navigate-settings-tab.test.ts +14 -1
  58. package/src/__tests__/notification-broadcaster.test.ts +65 -0
  59. package/src/__tests__/notification-decision-recipient-context.test.ts +282 -0
  60. package/src/__tests__/onboarding-template-contract.test.ts +63 -14
  61. package/src/__tests__/parser.test.ts +32 -0
  62. package/src/__tests__/permission-checker-host-gate.test.ts +452 -0
  63. package/src/__tests__/permission-controls-v2-flag.test.ts +55 -0
  64. package/src/__tests__/permission-mode-sse.test.ts +418 -0
  65. package/src/__tests__/permission-mode-store.test.ts +277 -0
  66. package/src/__tests__/permission-mode.test.ts +101 -0
  67. package/src/__tests__/pkb-autoinject.test.ts +96 -0
  68. package/src/__tests__/platform-bash-auto-approve.test.ts +359 -0
  69. package/src/__tests__/profiler-routes.test.ts +502 -0
  70. package/src/__tests__/profiler-run-store.test.ts +441 -0
  71. package/src/__tests__/proxy-approval-callback.test.ts +4 -75
  72. package/src/__tests__/registry.test.ts +1 -1
  73. package/src/__tests__/require-fresh-approval.test.ts +0 -2
  74. package/src/__tests__/sandbox-diagnostics.test.ts +1 -32
  75. package/src/__tests__/sandbox-host-parity.test.ts +5 -4
  76. package/src/__tests__/scheduler-reuse-conversation.test.ts +368 -0
  77. package/src/__tests__/scrub-corrupted-image-attachments.test.ts +278 -0
  78. package/src/__tests__/search-skills-unified.test.ts +4 -3
  79. package/src/__tests__/send-endpoint-busy.test.ts +42 -3
  80. package/src/__tests__/set-permission-mode.test.ts +274 -0
  81. package/src/__tests__/skill-load-feature-flag.test.ts +12 -0
  82. package/src/__tests__/skill-memory.test.ts +2 -783
  83. package/src/__tests__/strip-memory-injections.test.ts +187 -0
  84. package/src/__tests__/subagent-detail.test.ts +84 -0
  85. package/src/__tests__/subagent-disposal.test.ts +308 -0
  86. package/src/__tests__/subagent-manager-notify.test.ts +19 -10
  87. package/src/__tests__/subagent-notify-parent.test.ts +390 -0
  88. package/src/__tests__/subagent-role-registry.test.ts +108 -0
  89. package/src/__tests__/subagent-tool-filtering.test.ts +71 -0
  90. package/src/__tests__/subagent-tools.test.ts +464 -4
  91. package/src/__tests__/system-prompt-ask-mode.test.ts +139 -0
  92. package/src/__tests__/task-memory-cleanup.test.ts +12 -12
  93. package/src/__tests__/terminal-sandbox.test.ts +1 -1
  94. package/src/__tests__/terminal-tools.test.ts +16 -29
  95. package/src/__tests__/test-preload.ts +18 -0
  96. package/src/__tests__/tool-domain-event-publisher.test.ts +0 -1
  97. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -8
  98. package/src/__tests__/tool-executor.test.ts +4 -27
  99. package/src/__tests__/tool-side-effects-slack-dm.test.ts +1 -0
  100. package/src/__tests__/top-level-renderer.test.ts +10 -13
  101. package/src/__tests__/transport-hints-queue.test.ts +77 -0
  102. package/src/__tests__/trust-store.test.ts +4 -4
  103. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +116 -2
  104. package/src/__tests__/workspace-migration-028-recover-conversations-from-disk-view.test.ts +387 -0
  105. package/src/__tests__/workspace-migration-030-seed-pkb-autoinject.test.ts +168 -0
  106. package/src/__tests__/workspace-policy.test.ts +2 -7
  107. package/src/agent/loop.ts +6 -29
  108. package/src/approvals/guardian-request-resolvers.ts +24 -0
  109. package/src/avatar/traits-png-sync.ts +3 -3
  110. package/src/channels/types.ts +5 -0
  111. package/src/cli/__tests__/run-assistant-command.ts +56 -0
  112. package/src/cli/__tests__/unknown-command.test.ts +33 -0
  113. package/src/cli/commands/__tests__/email-download.test.ts +245 -0
  114. package/src/cli/commands/__tests__/email-list.test.ts +192 -0
  115. package/src/cli/commands/__tests__/email-register.test.ts +186 -0
  116. package/src/cli/commands/__tests__/email-send.test.ts +291 -0
  117. package/src/cli/commands/__tests__/email-status.test.ts +181 -0
  118. package/src/cli/commands/__tests__/email-unregister.test.ts +139 -0
  119. package/src/cli/commands/__tests__/routes.test.ts +562 -0
  120. package/src/cli/commands/conversations.ts +1 -8
  121. package/src/cli/commands/default-action.ts +68 -1
  122. package/src/cli/commands/email.ts +584 -835
  123. package/src/cli/commands/memory.ts +1 -34
  124. package/src/cli/commands/notifications.ts +7 -2
  125. package/src/cli/commands/oauth/__tests__/connect.test.ts +27 -0
  126. package/src/cli/commands/oauth/connect.ts +25 -5
  127. package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
  128. package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
  129. package/src/cli/commands/platform/__tests__/status.test.ts +1 -1
  130. package/src/cli/commands/routes.ts +396 -0
  131. package/src/cli/commands/skills.ts +130 -20
  132. package/src/cli/program.ts +11 -2
  133. package/src/cli.ts +1 -120
  134. package/src/config/assistant-feature-flags.ts +59 -55
  135. package/src/config/bundled-skills/app-builder/SKILL.md +91 -5
  136. package/src/config/bundled-skills/gmail/SKILL.md +13 -8
  137. package/src/config/bundled-skills/gmail/TOOLS.json +1 -1
  138. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -1
  139. package/src/config/bundled-skills/messaging/SKILL.md +7 -0
  140. package/src/config/bundled-skills/schedule/SKILL.md +22 -2
  141. package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
  142. package/src/config/bundled-skills/settings/TOOLS.json +1 -1
  143. package/src/config/bundled-skills/settings/tools/avatar-get.ts +3 -13
  144. package/src/config/bundled-skills/settings/tools/avatar-remove.ts +2 -4
  145. package/src/config/bundled-skills/settings/tools/avatar-update.ts +5 -2
  146. package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +8 -3
  147. package/src/config/bundled-skills/slack/SKILL.md +2 -0
  148. package/src/config/bundled-skills/subagent/SKILL.md +43 -3
  149. package/src/config/bundled-skills/subagent/TOOLS.json +29 -4
  150. package/src/config/env-registry.ts +63 -0
  151. package/src/config/feature-flag-registry.json +17 -1
  152. package/src/config/schema.ts +8 -0
  153. package/src/config/schemas/filing.ts +51 -0
  154. package/src/config/schemas/heartbeat.ts +15 -12
  155. package/src/config/schemas/memory-lifecycle.ts +12 -0
  156. package/src/config/schemas/security.ts +14 -0
  157. package/src/config/schemas/services.ts +8 -0
  158. package/src/credential-execution/approval-bridge.ts +0 -1
  159. package/src/credential-execution/managed-catalog.ts +3 -7
  160. package/src/daemon/app-source-watcher.ts +93 -0
  161. package/src/daemon/config-watcher.ts +85 -3
  162. package/src/daemon/context-overflow-approval.ts +0 -1
  163. package/src/daemon/conversation-agent-loop-handlers.ts +20 -0
  164. package/src/daemon/conversation-agent-loop.ts +179 -65
  165. package/src/daemon/conversation-attachments.ts +0 -1
  166. package/src/daemon/conversation-history.ts +4 -19
  167. package/src/daemon/conversation-lifecycle.ts +8 -14
  168. package/src/daemon/conversation-messaging.ts +3 -0
  169. package/src/daemon/conversation-process.ts +30 -8
  170. package/src/daemon/conversation-queue-manager.ts +8 -0
  171. package/src/daemon/conversation-runtime-assembly.ts +359 -308
  172. package/src/daemon/conversation-surfaces.ts +65 -0
  173. package/src/daemon/conversation-tool-setup.ts +44 -17
  174. package/src/daemon/conversation-workspace.ts +1 -2
  175. package/src/daemon/conversation.ts +19 -3
  176. package/src/daemon/date-context.ts +26 -53
  177. package/src/daemon/first-greeting.ts +1 -1
  178. package/src/daemon/handlers/conversations.ts +5 -7
  179. package/src/daemon/handlers/shared.test.ts +143 -0
  180. package/src/daemon/handlers/shared.ts +70 -5
  181. package/src/daemon/handlers/skills.ts +11 -18
  182. package/src/daemon/lifecycle.ts +220 -158
  183. package/src/daemon/message-types/conversations.ts +29 -6
  184. package/src/daemon/message-types/messages.ts +9 -2
  185. package/src/daemon/message-types/notifications.ts +12 -0
  186. package/src/daemon/message-types/schedules.ts +1 -0
  187. package/src/daemon/message-types/settings.ts +18 -0
  188. package/src/daemon/profiler-run-store.ts +557 -0
  189. package/src/daemon/server.ts +87 -10
  190. package/src/daemon/shutdown-handlers.ts +5 -0
  191. package/src/daemon/tool-side-effects.ts +23 -3
  192. package/src/daemon/transport-hints.ts +33 -0
  193. package/src/export/transcript-formatter.ts +148 -0
  194. package/src/filing/filing-service.ts +228 -0
  195. package/src/heartbeat/heartbeat-service.ts +96 -7
  196. package/src/index.ts +1 -1
  197. package/src/mcp/client.ts +6 -0
  198. package/src/mcp/mcp-oauth-provider.ts +149 -27
  199. package/src/memory/admin.ts +33 -32
  200. package/src/memory/app-store.ts +69 -0
  201. package/src/memory/conversation-bootstrap.ts +1 -1
  202. package/src/memory/conversation-crud.ts +151 -117
  203. package/src/memory/conversation-directories.ts +39 -0
  204. package/src/memory/conversation-group-migration.ts +66 -6
  205. package/src/memory/conversation-queries.ts +58 -12
  206. package/src/memory/conversation-title-service.ts +1 -0
  207. package/src/memory/db-init.ts +182 -376
  208. package/src/memory/embedding-local.ts +1 -1
  209. package/src/memory/graph/bootstrap.ts +75 -66
  210. package/src/memory/graph/capability-seed.ts +167 -17
  211. package/src/memory/graph/consolidation.ts +38 -4
  212. package/src/memory/graph/conversation-graph-memory.ts +133 -104
  213. package/src/memory/graph/extraction-job.ts +9 -4
  214. package/src/memory/graph/extraction.ts +66 -23
  215. package/src/memory/graph/graph-memory-state-store.ts +37 -0
  216. package/src/memory/graph/graph-search.ts +29 -15
  217. package/src/memory/graph/injection.ts +38 -8
  218. package/src/memory/graph/inspect.ts +12 -3
  219. package/src/memory/graph/retriever.ts +365 -262
  220. package/src/memory/graph/store.test.ts +48 -0
  221. package/src/memory/graph/store.ts +150 -11
  222. package/src/memory/graph/tool-handlers.ts +84 -209
  223. package/src/memory/graph/tools.ts +8 -52
  224. package/src/memory/graph/types.ts +24 -0
  225. package/src/memory/group-crud.ts +25 -9
  226. package/src/memory/job-handlers/cleanup.ts +44 -1
  227. package/src/memory/jobs-store.ts +70 -60
  228. package/src/memory/jobs-worker.ts +44 -28
  229. package/src/memory/llm-request-log-store.ts +96 -12
  230. package/src/memory/memory-recall-log-store.ts +49 -5
  231. package/src/memory/migrations/203-drop-memory-items-tables.ts +33 -1
  232. package/src/memory/migrations/206-memory-graph-node-edits.ts +19 -0
  233. package/src/memory/migrations/206-scrub-corrupted-image-attachments.ts +131 -0
  234. package/src/memory/migrations/207-conversation-graph-memory-state.ts +20 -0
  235. package/src/memory/migrations/208-conversations-last-message-at.ts +35 -0
  236. package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +85 -0
  237. package/src/memory/migrations/210-schedule-reuse-conversation.ts +13 -0
  238. package/src/memory/migrations/211-memory-recall-logs-query-context.ts +21 -0
  239. package/src/memory/migrations/212-llm-request-logs-created-at-index.ts +19 -0
  240. package/src/memory/migrations/index.ts +8 -0
  241. package/src/memory/migrations/registry.ts +8 -0
  242. package/src/memory/schema/conversations.ts +14 -0
  243. package/src/memory/schema/infrastructure.ts +8 -1
  244. package/src/memory/schema/memory-core.ts +0 -51
  245. package/src/memory/schema/memory-graph.ts +15 -0
  246. package/src/memory/task-memory-cleanup.ts +30 -11
  247. package/src/messaging/provider.ts +1 -1
  248. package/src/notifications/broadcaster.ts +6 -0
  249. package/src/notifications/conversation-pairing.ts +12 -4
  250. package/src/notifications/copy-composer.ts +86 -0
  251. package/src/notifications/decision-engine.ts +35 -0
  252. package/src/notifications/emit-signal.ts +14 -0
  253. package/src/notifications/signal.ts +11 -0
  254. package/src/oauth/platform-connection.test.ts +2 -2
  255. package/src/oauth/seed-providers.ts +1 -0
  256. package/src/permissions/checker.ts +15 -4
  257. package/src/permissions/defaults.ts +7 -8
  258. package/src/permissions/permission-mode-store.ts +180 -0
  259. package/src/permissions/permission-mode.ts +31 -0
  260. package/src/permissions/prompter.ts +0 -2
  261. package/src/permissions/workspace-policy.ts +9 -0
  262. package/src/platform/client.ts +1 -1
  263. package/src/prompts/system-prompt.ts +59 -7
  264. package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +100 -0
  265. package/src/prompts/templates/BOOTSTRAP.md +76 -162
  266. package/src/prompts/templates/HEARTBEAT.md +3 -1
  267. package/src/prompts/templates/SOUL.md +30 -9
  268. package/src/prompts/templates/UPDATES.md +8 -0
  269. package/src/providers/anthropic/client.ts +107 -219
  270. package/src/runtime/assistant-event-hub.ts +22 -0
  271. package/src/runtime/auth/route-policy.ts +23 -0
  272. package/src/runtime/auth/token-service.ts +8 -0
  273. package/src/runtime/http-server.ts +32 -2
  274. package/src/runtime/http-types.ts +12 -1
  275. package/src/runtime/migrations/vbundle-builder.ts +389 -3
  276. package/src/runtime/migrations/vbundle-importer.ts +8 -6
  277. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +378 -0
  278. package/src/runtime/routes/app-management-routes.ts +1 -11
  279. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +26 -0
  280. package/src/runtime/routes/archive-utils.ts +29 -0
  281. package/src/runtime/routes/avatar-routes.ts +2 -9
  282. package/src/runtime/routes/btw-routes.ts +14 -1
  283. package/src/runtime/routes/conversation-analysis-routes.ts +185 -0
  284. package/src/runtime/routes/conversation-management-routes.ts +1 -14
  285. package/src/runtime/routes/conversation-query-routes.ts +49 -3
  286. package/src/runtime/routes/conversation-routes.ts +270 -44
  287. package/src/runtime/routes/group-routes.ts +22 -8
  288. package/src/runtime/routes/heartbeat-routes.ts +4 -10
  289. package/src/runtime/routes/identity-routes.ts +53 -18
  290. package/src/runtime/routes/llm-context-normalization.ts +14 -10
  291. package/src/runtime/routes/log-export/AGENTS.md +104 -0
  292. package/src/runtime/routes/log-export/__tests__/workspace-allowlist-error-contract.test.ts +103 -0
  293. package/src/runtime/routes/log-export/__tests__/workspace-allowlist.test.ts +716 -0
  294. package/src/runtime/routes/log-export/workspace-allowlist.ts +458 -0
  295. package/src/runtime/routes/log-export-routes.ts +41 -278
  296. package/src/runtime/routes/memory-item-routes.test.ts +168 -233
  297. package/src/runtime/routes/migration-routes.ts +18 -7
  298. package/src/runtime/routes/profiler-routes.ts +350 -0
  299. package/src/runtime/routes/schedule-routes.ts +27 -12
  300. package/src/runtime/routes/settings-routes.ts +95 -8
  301. package/src/runtime/routes/subagents-routes.ts +28 -7
  302. package/src/runtime/routes/user-route-dispatcher.ts +223 -0
  303. package/src/runtime/routes/user-routes.ts +41 -0
  304. package/src/runtime/routes/workspace-routes.ts +0 -1
  305. package/src/schedule/schedule-store.ts +30 -0
  306. package/src/schedule/scheduler.ts +45 -18
  307. package/src/skills/catalog-install.ts +10 -2
  308. package/src/skills/inline-command-runner.ts +12 -14
  309. package/src/skills/managed-store.ts +2 -2
  310. package/src/skills/skill-memory.ts +1 -293
  311. package/src/subagent/index.ts +13 -3
  312. package/src/subagent/manager.ts +308 -29
  313. package/src/subagent/types.ts +68 -0
  314. package/src/tasks/task-runner.ts +4 -4
  315. package/src/tools/apps/executors.ts +29 -4
  316. package/src/tools/filesystem/list.ts +93 -0
  317. package/src/tools/permission-checker.ts +78 -18
  318. package/src/tools/registry.ts +4 -0
  319. package/src/tools/schedule/create.ts +3 -0
  320. package/src/tools/schedule/list.ts +1 -0
  321. package/src/tools/schedule/update.ts +6 -0
  322. package/src/tools/secret-detection-handler.ts +0 -1
  323. package/src/tools/shared/filesystem/errors.ts +5 -0
  324. package/src/tools/shared/filesystem/file-ops-service.ts +90 -2
  325. package/src/tools/shared/filesystem/types.ts +17 -0
  326. package/src/tools/shared/shell-output.ts +31 -2
  327. package/src/tools/skills/sandbox-runner.ts +3 -6
  328. package/src/tools/subagent/abort.ts +12 -2
  329. package/src/tools/subagent/message.ts +9 -2
  330. package/src/tools/subagent/notify-parent.ts +79 -0
  331. package/src/tools/subagent/read.ts +29 -8
  332. package/src/tools/subagent/resolve.ts +21 -0
  333. package/src/tools/subagent/spawn.ts +2 -0
  334. package/src/tools/subagent/status.ts +11 -1
  335. package/src/tools/system/avatar-generator.ts +3 -3
  336. package/src/tools/system/register.ts +23 -0
  337. package/src/tools/system/set-permission-mode.ts +103 -0
  338. package/src/tools/terminal/parser.ts +30 -5
  339. package/src/tools/terminal/safe-env.ts +16 -1
  340. package/src/tools/terminal/sandbox-diagnostics.ts +4 -4
  341. package/src/tools/terminal/sandbox.ts +4 -1
  342. package/src/tools/terminal/shell.ts +3 -5
  343. package/src/tools/tool-manifest.ts +6 -0
  344. package/src/tools/types.ts +2 -3
  345. package/src/util/logger.ts +1 -1
  346. package/src/util/platform.ts +50 -17
  347. package/src/watcher/provider-types.ts +1 -1
  348. package/src/workspace/migrations/023-move-config-files-to-workspace.ts +2 -2
  349. package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +2 -2
  350. package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +270 -0
  351. package/src/workspace/migrations/029-seed-pkb.ts +85 -0
  352. package/src/workspace/migrations/030-seed-pkb-autoinject.ts +73 -0
  353. package/src/workspace/migrations/registry.ts +6 -0
  354. package/src/workspace/top-level-renderer.ts +5 -9
  355. package/src/__tests__/cli-memory.test.ts +0 -377
  356. package/src/__tests__/clipboard.test.ts +0 -88
  357. package/src/cli/cli-memory.ts +0 -179
  358. package/src/util/clipboard.ts +0 -34
@@ -0,0 +1,186 @@
1
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
+
3
+ import {
4
+ getMockFetchCalls,
5
+ mockFetch,
6
+ resetMockFetch,
7
+ } from "../../../__tests__/mock-fetch.js";
8
+ import { _setOverridesForTesting } from "../../../config/assistant-feature-flags.js";
9
+ import { setPlatformAssistantId } from "../../../config/env.js";
10
+ import { credentialKey } from "../../../security/credential-key.js";
11
+ import {
12
+ _resetBackend,
13
+ setSecureKeyAsync,
14
+ } from "../../../security/secure-keys.js";
15
+ import { runAssistantCommand } from "../../__tests__/run-assistant-command.js";
16
+
17
+ const ASSISTANT_ID = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee";
18
+ const API_KEY_CREDENTIAL = credentialKey("vellum", "assistant_api_key");
19
+
20
+ beforeEach(async () => {
21
+ process.exitCode = 0;
22
+ _resetBackend();
23
+ resetMockFetch();
24
+ _setOverridesForTesting({ "email-channel": true });
25
+ setPlatformAssistantId(ASSISTANT_ID);
26
+ await setSecureKeyAsync(API_KEY_CREDENTIAL, "test-api-key");
27
+ });
28
+
29
+ afterEach(() => {
30
+ resetMockFetch();
31
+ _setOverridesForTesting({});
32
+ setPlatformAssistantId(undefined);
33
+ _resetBackend();
34
+ });
35
+
36
+ describe("assistant email register", () => {
37
+ test("successful registration calls correct URL and body", async () => {
38
+ mockFetch(
39
+ "/email-addresses/",
40
+ { method: "POST" },
41
+ {
42
+ body: {
43
+ id: "550e8400-e29b-41d4-a716-446655440000",
44
+ address: "mybot@vellum.me",
45
+ created_at: "2026-04-04T21:00:00Z",
46
+ },
47
+ status: 201,
48
+ },
49
+ );
50
+
51
+ await runAssistantCommand("email", "register", "mybot");
52
+
53
+ const calls = getMockFetchCalls();
54
+ expect(calls).toHaveLength(1);
55
+ expect(calls[0].path).toBe(
56
+ `/v1/assistants/${ASSISTANT_ID}/email-addresses/`,
57
+ );
58
+ expect(calls[0].init.method).toBe("POST");
59
+ expect(JSON.parse(calls[0].init.body as string)).toEqual({
60
+ username: "mybot",
61
+ });
62
+ expect(process.exitCode).toBe(0);
63
+ });
64
+
65
+ test("--json outputs structured response", async () => {
66
+ mockFetch(
67
+ "/email-addresses/",
68
+ { method: "POST" },
69
+ {
70
+ body: {
71
+ id: "550e8400-e29b-41d4-a716-446655440000",
72
+ address: "support@vellum.me",
73
+ created_at: "2026-04-04T21:00:00Z",
74
+ },
75
+ status: 201,
76
+ },
77
+ );
78
+
79
+ const output = await runAssistantCommand(
80
+ "email",
81
+ "--json",
82
+ "register",
83
+ "support",
84
+ );
85
+
86
+ const parsed = JSON.parse(output.trim());
87
+ expect(parsed.address).toBe("support@vellum.me");
88
+ expect(parsed.id).toBe("550e8400-e29b-41d4-a716-446655440000");
89
+ expect(parsed.created_at).toBe("2026-04-04T21:00:00Z");
90
+ expect(process.exitCode).toBe(0);
91
+ });
92
+
93
+ test("duplicate address returns error", async () => {
94
+ mockFetch(
95
+ "/email-addresses/",
96
+ { method: "POST" },
97
+ {
98
+ body: {
99
+ assistant_id: ["This assistant already has an email address."],
100
+ },
101
+ status: 400,
102
+ },
103
+ );
104
+
105
+ const output = await runAssistantCommand(
106
+ "email",
107
+ "--json",
108
+ "register",
109
+ "mybot",
110
+ );
111
+
112
+ expect(process.exitCode).toBe(1);
113
+ const parsed = JSON.parse(output.trim());
114
+ expect(parsed.error).toContain("already has an email address");
115
+ });
116
+
117
+ test("missing platform credentials returns error", async () => {
118
+ // Remove the API key so create() returns null
119
+ _resetBackend();
120
+ setPlatformAssistantId(undefined);
121
+
122
+ const output = await runAssistantCommand(
123
+ "email",
124
+ "--json",
125
+ "register",
126
+ "mybot",
127
+ );
128
+
129
+ expect(process.exitCode).toBe(1);
130
+ const parsed = JSON.parse(output.trim());
131
+ expect(parsed.error).toContain("Platform credentials not configured");
132
+ });
133
+
134
+ test("missing assistant ID returns error", async () => {
135
+ setPlatformAssistantId("");
136
+
137
+ const output = await runAssistantCommand(
138
+ "email",
139
+ "--json",
140
+ "register",
141
+ "mybot",
142
+ );
143
+
144
+ expect(process.exitCode).toBe(1);
145
+ const parsed = JSON.parse(output.trim());
146
+ expect(parsed.error).toContain("Assistant ID");
147
+ });
148
+
149
+ test("platform 5xx returns error", async () => {
150
+ mockFetch(
151
+ "/email-addresses/",
152
+ { method: "POST" },
153
+ { body: { detail: "Internal server error" }, status: 500 },
154
+ );
155
+
156
+ const output = await runAssistantCommand(
157
+ "email",
158
+ "--json",
159
+ "register",
160
+ "mybot",
161
+ );
162
+
163
+ expect(process.exitCode).toBe(1);
164
+ const parsed = JSON.parse(output.trim());
165
+ expect(parsed.error).toContain("Internal server error");
166
+ });
167
+
168
+ test("username validation error from platform is surfaced", async () => {
169
+ mockFetch(
170
+ "/email-addresses/",
171
+ { method: "POST" },
172
+ { body: { username: ["Enter a valid value."] }, status: 400 },
173
+ );
174
+
175
+ const output = await runAssistantCommand(
176
+ "email",
177
+ "--json",
178
+ "register",
179
+ "invalid username!",
180
+ );
181
+
182
+ expect(process.exitCode).toBe(1);
183
+ const parsed = JSON.parse(output.trim());
184
+ expect(parsed.error).toContain("valid value");
185
+ });
186
+ });
@@ -0,0 +1,291 @@
1
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
+
3
+ import {
4
+ getMockFetchCalls,
5
+ mockFetch,
6
+ resetMockFetch,
7
+ } from "../../../__tests__/mock-fetch.js";
8
+ import { _setOverridesForTesting } from "../../../config/assistant-feature-flags.js";
9
+ import { setPlatformAssistantId } from "../../../config/env.js";
10
+ import { credentialKey } from "../../../security/credential-key.js";
11
+ import {
12
+ _resetBackend,
13
+ setSecureKeyAsync,
14
+ } from "../../../security/secure-keys.js";
15
+ import { runAssistantCommand } from "../../__tests__/run-assistant-command.js";
16
+
17
+ const ASSISTANT_ID = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee";
18
+ const ADDRESS_ID = "550e8400-e29b-41d4-a716-446655440000";
19
+ const ADDRESS = "mybot@vellum.me";
20
+ const API_KEY_CREDENTIAL = credentialKey("vellum", "assistant_api_key");
21
+
22
+ function mockListAddresses(
23
+ addresses: { id: string; address: string }[] = [
24
+ { id: ADDRESS_ID, address: ADDRESS },
25
+ ],
26
+ status = 200,
27
+ ): void {
28
+ mockFetch("/email-addresses/", {}, { body: { results: addresses }, status });
29
+ }
30
+
31
+ function mockSendSuccess(deliveryId = "del_abc123", status = 202): void {
32
+ mockFetch(
33
+ "/runtime-proxy/email/send/",
34
+ { method: "POST" },
35
+ { body: { delivery_id: deliveryId, status: "accepted" }, status },
36
+ );
37
+ }
38
+
39
+ beforeEach(async () => {
40
+ process.exitCode = 0;
41
+ _resetBackend();
42
+ resetMockFetch();
43
+ _setOverridesForTesting({ "email-channel": true });
44
+ setPlatformAssistantId(ASSISTANT_ID);
45
+ await setSecureKeyAsync(API_KEY_CREDENTIAL, "test-api-key");
46
+ });
47
+
48
+ afterEach(() => {
49
+ resetMockFetch();
50
+ _setOverridesForTesting({});
51
+ setPlatformAssistantId(undefined);
52
+ _resetBackend();
53
+ });
54
+
55
+ describe("assistant email send", () => {
56
+ test("successful send with --body and --subject", async () => {
57
+ mockListAddresses();
58
+ mockSendSuccess();
59
+
60
+ await runAssistantCommand(
61
+ "email",
62
+ "send",
63
+ "user@example.com",
64
+ "-s",
65
+ "Hello",
66
+ "-b",
67
+ "Hi there",
68
+ );
69
+
70
+ const calls = getMockFetchCalls();
71
+ expect(calls).toHaveLength(2);
72
+
73
+ // First call: list addresses to resolve "from"
74
+ expect(calls[0].path).toBe(
75
+ `/v1/assistants/${ASSISTANT_ID}/email-addresses/`,
76
+ );
77
+
78
+ // Second call: send via runtime proxy
79
+ expect(calls[1].path).toBe("/v1/runtime-proxy/email/send/");
80
+ expect(calls[1].init.method).toBe("POST");
81
+
82
+ const payload = JSON.parse(calls[1].init.body as string);
83
+ expect(payload.to).toBe("user@example.com");
84
+ expect(payload.from_address).toBe(ADDRESS);
85
+ expect(payload.subject).toBe("Hello");
86
+ expect(payload.text).toBe("Hi there");
87
+
88
+ expect(process.exitCode).toBe(0);
89
+ });
90
+
91
+ test("--json outputs structured response", async () => {
92
+ mockListAddresses();
93
+ mockSendSuccess("del_xyz789");
94
+
95
+ const output = await runAssistantCommand(
96
+ "email",
97
+ "--json",
98
+ "send",
99
+ "user@example.com",
100
+ "-s",
101
+ "Test",
102
+ "-b",
103
+ "Body",
104
+ );
105
+
106
+ const parsed = JSON.parse(output.trim());
107
+ expect(parsed.delivery_id).toBe("del_xyz789");
108
+ expect(parsed.status).toBe("accepted");
109
+ expect(process.exitCode).toBe(0);
110
+ });
111
+
112
+ test("no registered address returns error", async () => {
113
+ mockListAddresses([]);
114
+
115
+ const output = await runAssistantCommand(
116
+ "email",
117
+ "--json",
118
+ "send",
119
+ "user@example.com",
120
+ "-s",
121
+ "Hello",
122
+ "-b",
123
+ "Body",
124
+ );
125
+
126
+ expect(process.exitCode).toBe(1);
127
+ const parsed = JSON.parse(output.trim());
128
+ expect(parsed.error).toContain("No email address registered");
129
+ });
130
+
131
+ test("missing body returns error", async () => {
132
+ // Force isTTY to true so stdin fallback is skipped
133
+ const origIsTTY = process.stdin.isTTY;
134
+ process.stdin.isTTY = true as unknown as boolean;
135
+
136
+ mockListAddresses();
137
+
138
+ const output = await runAssistantCommand(
139
+ "email",
140
+ "--json",
141
+ "send",
142
+ "user@example.com",
143
+ "-s",
144
+ "Hello",
145
+ );
146
+
147
+ process.stdin.isTTY = origIsTTY;
148
+
149
+ expect(process.exitCode).toBe(1);
150
+ const parsed = JSON.parse(output.trim());
151
+ expect(parsed.error).toContain("Email body is required");
152
+ });
153
+
154
+ test("send endpoint failure surfaces error detail", async () => {
155
+ mockListAddresses();
156
+ mockFetch(
157
+ "/runtime-proxy/email/send/",
158
+ { method: "POST" },
159
+ {
160
+ body: { detail: "From address not owned by this assistant." },
161
+ status: 403,
162
+ },
163
+ );
164
+
165
+ const output = await runAssistantCommand(
166
+ "email",
167
+ "--json",
168
+ "send",
169
+ "user@example.com",
170
+ "-s",
171
+ "Hello",
172
+ "-b",
173
+ "Body",
174
+ );
175
+
176
+ expect(process.exitCode).toBe(1);
177
+ const parsed = JSON.parse(output.trim());
178
+ expect(parsed.error).toContain("not owned by this assistant");
179
+ });
180
+
181
+ test("list addresses failure returns error", async () => {
182
+ mockFetch(
183
+ "/email-addresses/",
184
+ {},
185
+ { body: { detail: "Internal server error" }, status: 500 },
186
+ );
187
+
188
+ const output = await runAssistantCommand(
189
+ "email",
190
+ "--json",
191
+ "send",
192
+ "user@example.com",
193
+ "-s",
194
+ "Hello",
195
+ "-b",
196
+ "Body",
197
+ );
198
+
199
+ expect(process.exitCode).toBe(1);
200
+ const parsed = JSON.parse(output.trim());
201
+ expect(parsed.error).toContain("Failed to list email addresses");
202
+ });
203
+
204
+ test("missing platform credentials returns error", async () => {
205
+ _resetBackend();
206
+ setPlatformAssistantId(undefined);
207
+
208
+ const output = await runAssistantCommand(
209
+ "email",
210
+ "--json",
211
+ "send",
212
+ "user@example.com",
213
+ "-s",
214
+ "Hello",
215
+ "-b",
216
+ "Body",
217
+ );
218
+
219
+ expect(process.exitCode).toBe(1);
220
+ const parsed = JSON.parse(output.trim());
221
+ expect(parsed.error).toContain("Platform credentials not configured");
222
+ });
223
+
224
+ test("missing assistant ID returns error", async () => {
225
+ setPlatformAssistantId("");
226
+
227
+ const output = await runAssistantCommand(
228
+ "email",
229
+ "--json",
230
+ "send",
231
+ "user@example.com",
232
+ "-s",
233
+ "Hello",
234
+ "-b",
235
+ "Body",
236
+ );
237
+
238
+ expect(process.exitCode).toBe(1);
239
+ const parsed = JSON.parse(output.trim());
240
+ expect(parsed.error).toContain("Assistant ID");
241
+ });
242
+
243
+ test("send without subject omits subject from payload", async () => {
244
+ mockListAddresses();
245
+ mockSendSuccess();
246
+
247
+ await runAssistantCommand(
248
+ "email",
249
+ "send",
250
+ "user@example.com",
251
+ "-b",
252
+ "Body only, no subject",
253
+ );
254
+
255
+ const calls = getMockFetchCalls();
256
+ const payload = JSON.parse(calls[1].init.body as string);
257
+ expect(payload.subject).toBeUndefined();
258
+ expect(payload.text).toBe("Body only, no subject");
259
+ expect(process.exitCode).toBe(0);
260
+ });
261
+
262
+ test("suppressed recipient error is surfaced", async () => {
263
+ mockListAddresses();
264
+ mockFetch(
265
+ "/runtime-proxy/email/send/",
266
+ { method: "POST" },
267
+ {
268
+ body: {
269
+ detail:
270
+ "Recipient user@example.com is suppressed due to prior bounce or spam complaint.",
271
+ },
272
+ status: 422,
273
+ },
274
+ );
275
+
276
+ const output = await runAssistantCommand(
277
+ "email",
278
+ "--json",
279
+ "send",
280
+ "user@example.com",
281
+ "-s",
282
+ "Hello",
283
+ "-b",
284
+ "Body",
285
+ );
286
+
287
+ expect(process.exitCode).toBe(1);
288
+ const parsed = JSON.parse(output.trim());
289
+ expect(parsed.error).toContain("suppressed");
290
+ });
291
+ });
@@ -0,0 +1,181 @@
1
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
+
3
+ import {
4
+ getMockFetchCalls,
5
+ mockFetch,
6
+ resetMockFetch,
7
+ } from "../../../__tests__/mock-fetch.js";
8
+ import { _setOverridesForTesting } from "../../../config/assistant-feature-flags.js";
9
+ import { setPlatformAssistantId } from "../../../config/env.js";
10
+ import { credentialKey } from "../../../security/credential-key.js";
11
+ import {
12
+ _resetBackend,
13
+ deleteSecureKeyAsync,
14
+ setSecureKeyAsync,
15
+ } from "../../../security/secure-keys.js";
16
+ import { runAssistantCommand } from "../../__tests__/run-assistant-command.js";
17
+
18
+ const ASSISTANT_ID = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee";
19
+ const ADDRESS_ID = "550e8400-e29b-41d4-a716-446655440000";
20
+ const ADDRESS = "mybot@vellum.me";
21
+ const API_KEY_CREDENTIAL = credentialKey("vellum", "assistant_api_key");
22
+
23
+ function mockListAddresses(
24
+ addresses: { id: string; address: string }[] = [
25
+ { id: ADDRESS_ID, address: ADDRESS },
26
+ ],
27
+ status = 200,
28
+ ): void {
29
+ mockFetch("/email-addresses/", {}, { body: { results: addresses }, status });
30
+ }
31
+
32
+ function mockStatusSuccess(
33
+ overrides?: Partial<{
34
+ address: string;
35
+ status: string;
36
+ usage: { sent_today: number; daily_limit: number; received_today: number };
37
+ }>,
38
+ ): void {
39
+ mockFetch(
40
+ `/email-addresses/${ADDRESS_ID}/status/`,
41
+ {},
42
+ {
43
+ body: {
44
+ address: overrides?.address ?? ADDRESS,
45
+ status: overrides?.status ?? "active",
46
+ usage: overrides?.usage ?? {
47
+ sent_today: 12,
48
+ daily_limit: 100,
49
+ received_today: 5,
50
+ },
51
+ },
52
+ status: 200,
53
+ },
54
+ );
55
+ }
56
+
57
+ let savedCesUrl: string | undefined;
58
+ let savedContainerized: string | undefined;
59
+
60
+ beforeEach(async () => {
61
+ process.exitCode = 0;
62
+
63
+ // Force encrypted-store backend so setSecureKeyAsync works in sandbox
64
+ savedCesUrl = process.env.CES_CREDENTIAL_URL;
65
+ savedContainerized = process.env.IS_CONTAINERIZED;
66
+ delete process.env.CES_CREDENTIAL_URL;
67
+ delete process.env.IS_CONTAINERIZED;
68
+
69
+ _resetBackend();
70
+ resetMockFetch();
71
+ _setOverridesForTesting({ "email-channel": true });
72
+ setPlatformAssistantId(ASSISTANT_ID);
73
+ await setSecureKeyAsync(API_KEY_CREDENTIAL, "test-api-key");
74
+ });
75
+
76
+ afterEach(() => {
77
+ resetMockFetch();
78
+ _setOverridesForTesting({});
79
+ setPlatformAssistantId(undefined);
80
+ _resetBackend();
81
+
82
+ // Restore env
83
+ if (savedCesUrl !== undefined) process.env.CES_CREDENTIAL_URL = savedCesUrl;
84
+ else delete process.env.CES_CREDENTIAL_URL;
85
+ if (savedContainerized !== undefined)
86
+ process.env.IS_CONTAINERIZED = savedContainerized;
87
+ else delete process.env.IS_CONTAINERIZED;
88
+ });
89
+
90
+ describe("assistant email status", () => {
91
+ test("successful status shows address and usage", async () => {
92
+ mockListAddresses();
93
+ mockStatusSuccess();
94
+
95
+ const output = await runAssistantCommand("email", "--json", "status");
96
+
97
+ const parsed = JSON.parse(output.trim());
98
+ expect(parsed.address).toBe(ADDRESS);
99
+ expect(parsed.status).toBe("active");
100
+ expect(parsed.usage.sent_today).toBe(12);
101
+ expect(parsed.usage.daily_limit).toBe(100);
102
+ expect(parsed.usage.received_today).toBe(5);
103
+ expect(process.exitCode).toBe(0);
104
+ });
105
+
106
+ test("calls correct URLs in order", async () => {
107
+ mockListAddresses();
108
+ mockStatusSuccess();
109
+
110
+ await runAssistantCommand("email", "--json", "status");
111
+
112
+ const calls = getMockFetchCalls();
113
+ expect(calls).toHaveLength(2);
114
+ expect(calls[0].path).toContain(
115
+ `/v1/assistants/${ASSISTANT_ID}/email-addresses/`,
116
+ );
117
+ expect(calls[1].path).toContain(
118
+ `/v1/assistants/${ASSISTANT_ID}/email-addresses/${ADDRESS_ID}/status/`,
119
+ );
120
+ });
121
+
122
+ test("no registered address returns error", async () => {
123
+ mockListAddresses([]);
124
+
125
+ const output = await runAssistantCommand("email", "--json", "status");
126
+
127
+ expect(process.exitCode).toBe(1);
128
+ const parsed = JSON.parse(output.trim());
129
+ expect(parsed.error).toContain("No email address registered");
130
+ });
131
+
132
+ test("list endpoint failure returns error", async () => {
133
+ mockFetch(
134
+ "/email-addresses/",
135
+ {},
136
+ { body: { detail: "Internal server error" }, status: 500 },
137
+ );
138
+
139
+ const output = await runAssistantCommand("email", "--json", "status");
140
+
141
+ expect(process.exitCode).toBe(1);
142
+ const parsed = JSON.parse(output.trim());
143
+ expect(parsed.error).toContain("Failed to list email addresses");
144
+ });
145
+
146
+ test("status endpoint failure returns error", async () => {
147
+ mockListAddresses();
148
+ mockFetch(
149
+ `/email-addresses/${ADDRESS_ID}/status/`,
150
+ {},
151
+ { body: { detail: "Service unavailable" }, status: 503 },
152
+ );
153
+
154
+ const output = await runAssistantCommand("email", "--json", "status");
155
+
156
+ expect(process.exitCode).toBe(1);
157
+ const parsed = JSON.parse(output.trim());
158
+ expect(parsed.error).toContain("Service unavailable");
159
+ });
160
+
161
+ test("missing platform credentials returns error", async () => {
162
+ // Delete the API key so create() returns null
163
+ await deleteSecureKeyAsync(API_KEY_CREDENTIAL);
164
+
165
+ const output = await runAssistantCommand("email", "--json", "status");
166
+
167
+ expect(process.exitCode).toBe(1);
168
+ const parsed = JSON.parse(output.trim());
169
+ expect(parsed.error).toContain("Platform credentials not configured");
170
+ });
171
+
172
+ test("missing assistant ID returns error", async () => {
173
+ setPlatformAssistantId("");
174
+
175
+ const output = await runAssistantCommand("email", "--json", "status");
176
+
177
+ expect(process.exitCode).toBe(1);
178
+ const parsed = JSON.parse(output.trim());
179
+ expect(parsed.error).toContain("Assistant ID");
180
+ });
181
+ });