@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,1225 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
2
-
3
- import { Command } from "commander";
4
-
5
- import { credentialKey } from "../security/credential-key.js";
6
- import type { CredentialMetadata } from "../tools/credentials/metadata-store.js";
7
-
8
- // ---------------------------------------------------------------------------
9
- // In-memory mock state
10
- // ---------------------------------------------------------------------------
11
-
12
- let secureKeyStore = new Map<string, string>();
13
- let metadataStore: CredentialMetadata[] = [];
14
- let idCounter = 0;
15
- let mockBrokerUnreachable = false;
16
-
17
- function nextUUID(): string {
18
- idCounter += 1;
19
- return `00000000-0000-0000-0000-${String(idCounter).padStart(12, "0")}`;
20
- }
21
-
22
- // ---------------------------------------------------------------------------
23
- // Mock secure-keys (reads) and daemon-credential-client (writes/deletes)
24
- // ---------------------------------------------------------------------------
25
-
26
- function normalizeCredentialAccount(type: string, name: string): string {
27
- if (type !== "credential") return name;
28
- if (name.startsWith("credential/")) return name;
29
- const colonIdx = name.lastIndexOf(":");
30
- if (colonIdx > 0 && colonIdx < name.length - 1) {
31
- return `credential/${name.slice(0, colonIdx)}/${name.slice(colonIdx + 1)}`;
32
- }
33
- return name;
34
- }
35
-
36
- mock.module("../security/secure-keys.js", () => ({
37
- getSecureKeyAsync: async (account: string): Promise<string | undefined> => {
38
- return secureKeyStore.get(account);
39
- },
40
- getSecureKeyResultAsync: async (
41
- account: string,
42
- ): Promise<{ value: string | undefined; unreachable: boolean }> => ({
43
- value: secureKeyStore.get(account),
44
- unreachable: mockBrokerUnreachable,
45
- }),
46
- setSecureKeyAsync: async () => true,
47
- deleteSecureKeyAsync: async () => "deleted" as const,
48
- getProviderKeyAsync: async () => undefined,
49
- getMaskedProviderKey: async () => undefined,
50
- bulkSetSecureKeysAsync: async () => {},
51
- listSecureKeysAsync: async () => ({ credentials: [] }),
52
- setCesClient: () => {},
53
- onCesClientChanged: () => ({ unsubscribe: () => {} }),
54
- setCesReconnect: () => {},
55
- getActiveBackendName: () => "file",
56
- getActiveBackendInfoAsync: async () => ({
57
- backend: "encrypted-store",
58
- storePath: "/tmp/keys.enc",
59
- storeKeyPath: "/tmp/store.key",
60
- storeExists: false,
61
- storeKeyExists: false,
62
- }),
63
- _resetBackend: () => {},
64
- }));
65
-
66
- mock.module("../cli/lib/daemon-credential-client.js", () => ({
67
- setSecureKeyViaDaemon: async (type: string, name: string, value: string) => {
68
- secureKeyStore.set(normalizeCredentialAccount(type, name), value);
69
- return { ok: true };
70
- },
71
- deleteSecureKeyViaDaemon: async (type: string, name: string) => {
72
- const key = normalizeCredentialAccount(type, name);
73
- if (secureKeyStore.has(key)) {
74
- secureKeyStore.delete(key);
75
- return { result: "deleted" as const };
76
- }
77
- return { result: "not-found" as const };
78
- },
79
- }));
80
-
81
- // ---------------------------------------------------------------------------
82
- // Mock metadata-store
83
- // ---------------------------------------------------------------------------
84
-
85
- mock.module("../tools/credentials/metadata-store.js", () => ({
86
- assertMetadataWritable: (): void => {},
87
- upsertCredentialMetadata: (
88
- service: string,
89
- field: string,
90
- policy?: {
91
- allowedTools?: string[];
92
- allowedDomains?: string[];
93
- usageDescription?: string;
94
- expiresAt?: number | null;
95
- grantedScopes?: string[];
96
- alias?: string | null;
97
- injectionTemplates?: unknown[] | null;
98
- },
99
- ): CredentialMetadata => {
100
- const now = Date.now();
101
- const existing = metadataStore.find(
102
- (c) => c.service === service && c.field === field,
103
- );
104
-
105
- if (existing) {
106
- if (policy?.allowedTools !== undefined)
107
- existing.allowedTools = policy.allowedTools;
108
- if (policy?.allowedDomains !== undefined)
109
- existing.allowedDomains = policy.allowedDomains;
110
- if (policy?.usageDescription !== undefined)
111
- existing.usageDescription = policy.usageDescription;
112
- if (policy?.alias !== undefined) {
113
- if (policy.alias == null) {
114
- delete existing.alias;
115
- } else {
116
- existing.alias = policy.alias;
117
- }
118
- }
119
- existing.updatedAt = now;
120
- return existing;
121
- }
122
-
123
- const record: CredentialMetadata = {
124
- credentialId: nextUUID(),
125
- service,
126
- field,
127
- allowedTools: policy?.allowedTools ?? [],
128
- allowedDomains: policy?.allowedDomains ?? [],
129
- usageDescription: policy?.usageDescription,
130
- alias: policy?.alias ?? undefined,
131
- createdAt: now,
132
- updatedAt: now,
133
- };
134
- metadataStore.push(record);
135
- return record;
136
- },
137
- getCredentialMetadata: (
138
- service: string,
139
- field: string,
140
- ): CredentialMetadata | undefined => {
141
- return metadataStore.find(
142
- (c) => c.service === service && c.field === field,
143
- );
144
- },
145
- getCredentialMetadataById: (
146
- credentialId: string,
147
- ): CredentialMetadata | undefined => {
148
- return metadataStore.find((c) => c.credentialId === credentialId);
149
- },
150
- deleteCredentialMetadata: (service: string, field: string): boolean => {
151
- const idx = metadataStore.findIndex(
152
- (c) => c.service === service && c.field === field,
153
- );
154
- if (idx === -1) return false;
155
- metadataStore.splice(idx, 1);
156
- return true;
157
- },
158
- listCredentialMetadata: (): CredentialMetadata[] => {
159
- return [...metadataStore];
160
- },
161
- }));
162
-
163
- // ---------------------------------------------------------------------------
164
- // Mock oauth-store
165
- // ---------------------------------------------------------------------------
166
-
167
- let disconnectOAuthProviderCalls: string[] = [];
168
- let disconnectOAuthProviderResult: "disconnected" | "not-found" | "error" =
169
- "not-found";
170
-
171
- mock.module("../oauth/oauth-store.js", () => ({
172
- disconnectOAuthProvider: async (
173
- provider: string,
174
- ): Promise<"disconnected" | "not-found" | "error"> => {
175
- disconnectOAuthProviderCalls.push(provider);
176
- return disconnectOAuthProviderResult;
177
- },
178
- // Provide stubs for all named exports so transitive importers don't crash
179
- seedProviders: () => {},
180
- getProvider: (): undefined => undefined,
181
- listProviders: (): never[] => [],
182
- registerProvider: () => {},
183
- updateProvider: () => {},
184
- deleteProvider: (): boolean => false,
185
- upsertApp: async () => ({ id: "mock-app-id" }),
186
- getApp: (): undefined => undefined,
187
- getAppClientSecret: async (): Promise<undefined> => undefined,
188
- getAppByProviderAndClientId: (): undefined => undefined,
189
- getMostRecentAppByProvider: (): undefined => undefined,
190
- listApps: (): never[] => [],
191
- deleteApp: async (): Promise<boolean> => false,
192
- createConnection: () => ({ id: "mock-conn-id" }),
193
- getConnection: (): undefined => undefined,
194
- getActiveConnection: (): undefined => undefined,
195
- getConnectionByProvider: (): undefined => undefined,
196
- getConnectionByProviderAndAccount: (): undefined => undefined,
197
- listActiveConnectionsByProvider: (): never[] => [],
198
- isProviderConnected: async (): Promise<boolean> => false,
199
- updateConnection: (): boolean => true,
200
- listConnections: (): never[] => [],
201
- deleteConnection: (): boolean => false,
202
- }));
203
-
204
- // ---------------------------------------------------------------------------
205
- // Import the module under test (after mocks are registered)
206
- // ---------------------------------------------------------------------------
207
-
208
- const { registerCredentialsCommand } =
209
- await import("../cli/commands/credentials.js");
210
-
211
- // ---------------------------------------------------------------------------
212
- // Test helper
213
- // ---------------------------------------------------------------------------
214
-
215
- async function runCli(
216
- args: string[],
217
- ): Promise<{ exitCode: number; stdout: string }> {
218
- const originalStdoutWrite = process.stdout.write.bind(process.stdout);
219
- const originalStderrWrite = process.stderr.write.bind(process.stderr);
220
- const stdoutChunks: string[] = [];
221
-
222
- process.stdout.write = ((chunk: unknown) => {
223
- stdoutChunks.push(typeof chunk === "string" ? chunk : String(chunk));
224
- return true;
225
- }) as typeof process.stdout.write;
226
-
227
- // Suppress stderr so Commander error messages don't leak to test runner
228
- process.stderr.write = (() => true) as typeof process.stderr.write;
229
-
230
- process.exitCode = 0;
231
-
232
- try {
233
- const program = new Command();
234
- program.exitOverride();
235
- program.configureOutput({
236
- writeErr: () => {},
237
- writeOut: (str: string) => stdoutChunks.push(str),
238
- });
239
- registerCredentialsCommand(program);
240
- await program.parseAsync(["node", "vellum", "credentials", ...args]);
241
- } catch {
242
- // Commander throws on --help and on missing required args; treat as non-zero exit
243
- if (process.exitCode === 0) process.exitCode = 1;
244
- } finally {
245
- process.stdout.write = originalStdoutWrite;
246
- process.stderr.write = originalStderrWrite;
247
- }
248
-
249
- const exitCode = process.exitCode ?? 0;
250
- process.exitCode = 0;
251
-
252
- return {
253
- exitCode,
254
- stdout: stdoutChunks.join(""),
255
- };
256
- }
257
-
258
- /**
259
- * Pre-populate mock stores with a credential.
260
- */
261
- function seedCredential(
262
- service: string,
263
- field: string,
264
- secret: string,
265
- extra?: Partial<CredentialMetadata>,
266
- ): CredentialMetadata {
267
- const now = Date.now();
268
- const record: CredentialMetadata = {
269
- credentialId: nextUUID(),
270
- service,
271
- field,
272
- allowedTools: [],
273
- allowedDomains: [],
274
- createdAt: now,
275
- updatedAt: now,
276
- ...extra,
277
- };
278
- metadataStore.push(record);
279
- secureKeyStore.set(credentialKey(service, field), secret);
280
- return record;
281
- }
282
-
283
- /**
284
- * Pre-populate mock stores with metadata only (no secret).
285
- */
286
- function seedMetadataOnly(
287
- service: string,
288
- field: string,
289
- extra?: Partial<CredentialMetadata>,
290
- ): CredentialMetadata {
291
- const now = Date.now();
292
- const record: CredentialMetadata = {
293
- credentialId: nextUUID(),
294
- service,
295
- field,
296
- allowedTools: [],
297
- allowedDomains: [],
298
- createdAt: now,
299
- updatedAt: now,
300
- ...extra,
301
- };
302
- metadataStore.push(record);
303
- return record;
304
- }
305
-
306
- // ---------------------------------------------------------------------------
307
- // Tests
308
- // ---------------------------------------------------------------------------
309
-
310
- describe("assistant credentials CLI", () => {
311
- beforeEach(() => {
312
- secureKeyStore = new Map();
313
- metadataStore = [];
314
- idCounter = 0;
315
- mockBrokerUnreachable = false;
316
- disconnectOAuthProviderCalls = [];
317
- disconnectOAuthProviderResult = "not-found";
318
- process.exitCode = 0;
319
- });
320
-
321
- // =========================================================================
322
- // list
323
- // =========================================================================
324
-
325
- describe("list", () => {
326
- test("returns empty array when no credentials exist", async () => {
327
- const result = await runCli(["list", "--json"]);
328
- expect(result.exitCode).toBe(0);
329
- const parsed = JSON.parse(result.stdout);
330
- expect(parsed).toEqual({
331
- ok: true,
332
- credentials: [],
333
- managedCredentials: [],
334
- });
335
- });
336
-
337
- test("returns all credentials with correct shapes", async () => {
338
- seedCredential("twilio", "account_sid", "AC12345678abcdefgh");
339
- seedCredential("twilio", "auth_token", "auth_secret_val");
340
- seedCredential("github", "token", "ghp_abcdefghij1234");
341
-
342
- const result = await runCli(["list", "--json"]);
343
- expect(result.exitCode).toBe(0);
344
- const parsed = JSON.parse(result.stdout);
345
- expect(parsed.ok).toBe(true);
346
- expect(parsed.credentials).toHaveLength(3);
347
-
348
- for (const cred of parsed.credentials) {
349
- expect(cred).toHaveProperty("ok", true);
350
- expect(cred).toHaveProperty("service");
351
- expect(cred).toHaveProperty("field");
352
- expect(cred).toHaveProperty("credentialId");
353
- expect(cred).toHaveProperty("scrubbedValue");
354
- expect(cred).toHaveProperty("hasSecret");
355
- expect(cred).toHaveProperty("alias");
356
- expect(cred).toHaveProperty("usageDescription");
357
- expect(cred).toHaveProperty("allowedTools");
358
- expect(cred).toHaveProperty("allowedDomains");
359
- expect(cred).toHaveProperty("grantedScopes");
360
- expect(cred).toHaveProperty("expiresAt");
361
- expect(cred).toHaveProperty("createdAt");
362
- expect(cred).toHaveProperty("updatedAt");
363
- expect(cred).toHaveProperty("injectionTemplateCount");
364
- }
365
- });
366
-
367
- test("filters by --search matching service name", async () => {
368
- seedCredential("twilio", "account_sid", "AC123456789012");
369
- seedCredential("twilio", "auth_token", "auth_secret_1234");
370
- seedCredential("github", "token", "ghp_abcdefghij");
371
-
372
- const result = await runCli(["list", "--search", "twilio", "--json"]);
373
- expect(result.exitCode).toBe(0);
374
- const parsed = JSON.parse(result.stdout);
375
- expect(parsed.ok).toBe(true);
376
- expect(parsed.credentials).toHaveLength(2);
377
- expect(parsed.credentials[0].service).toBe("twilio");
378
- expect(parsed.credentials[1].service).toBe("twilio");
379
- });
380
-
381
- test("filters by --search matching alias/label", async () => {
382
- seedCredential("twilio", "account_sid", "AC123456789012", {
383
- alias: "prod",
384
- });
385
- seedCredential("github", "token", "ghp_abcdefghij");
386
-
387
- const result = await runCli(["list", "--search", "prod", "--json"]);
388
- expect(result.exitCode).toBe(0);
389
- const parsed = JSON.parse(result.stdout);
390
- expect(parsed.ok).toBe(true);
391
- expect(parsed.credentials).toHaveLength(1);
392
- expect(parsed.credentials[0].service).toBe("twilio");
393
- expect(parsed.credentials[0].alias).toBe("prod");
394
- });
395
-
396
- test("filters by --search matching field name", async () => {
397
- seedCredential("twilio", "account_sid", "AC123456789012");
398
- seedCredential("slack", "bot_token", "xoxb-1234567890");
399
- seedCredential("github", "token", "ghp_abcdefghij");
400
-
401
- const result = await runCli(["list", "--search", "bot_token", "--json"]);
402
- expect(result.exitCode).toBe(0);
403
- const parsed = JSON.parse(result.stdout);
404
- expect(parsed.ok).toBe(true);
405
- expect(parsed.credentials).toHaveLength(1);
406
- expect(parsed.credentials[0].field).toBe("bot_token");
407
- });
408
-
409
- test("filters by --search matching description", async () => {
410
- seedCredential("fal", "api_key", "key_live_abc123456", {
411
- usageDescription: "Image generation",
412
- });
413
- seedCredential("github", "token", "ghp_abcdefghij");
414
-
415
- const result = await runCli(["list", "--search", "image", "--json"]);
416
- expect(result.exitCode).toBe(0);
417
- const parsed = JSON.parse(result.stdout);
418
- expect(parsed.ok).toBe(true);
419
- expect(parsed.credentials).toHaveLength(1);
420
- expect(parsed.credentials[0].service).toBe("fal");
421
- expect(parsed.credentials[0].usageDescription).toBe("Image generation");
422
- });
423
-
424
- test("returns empty array when --search has no matches", async () => {
425
- seedCredential("twilio", "account_sid", "AC123456789012");
426
- seedCredential("github", "token", "ghp_abcdefghij");
427
-
428
- const result = await runCli([
429
- "list",
430
- "--search",
431
- "nonexistent",
432
- "--json",
433
- ]);
434
- expect(result.exitCode).toBe(0);
435
- const parsed = JSON.parse(result.stdout);
436
- expect(parsed).toEqual({
437
- ok: true,
438
- credentials: [],
439
- managedCredentials: [],
440
- });
441
- });
442
-
443
- test("list items have the same shape as inspect output", async () => {
444
- seedCredential("twilio", "account_sid", "AC123456789012");
445
-
446
- const listResult = await runCli(["list", "--json"]);
447
- const listParsed = JSON.parse(listResult.stdout);
448
- const listItem = listParsed.credentials[0];
449
-
450
- const inspectResult = await runCli([
451
- "inspect",
452
- "--service",
453
- "twilio",
454
- "--field",
455
- "account_sid",
456
- "--json",
457
- ]);
458
- const inspectParsed = JSON.parse(inspectResult.stdout);
459
-
460
- const listKeys = Object.keys(listItem).sort();
461
- const inspectKeys = Object.keys(inspectParsed).sort();
462
- expect(listKeys).toEqual(inspectKeys);
463
- });
464
- });
465
-
466
- // =========================================================================
467
- // set
468
- // =========================================================================
469
-
470
- describe("set", () => {
471
- test("stores secret and creates metadata", async () => {
472
- const result = await runCli([
473
- "set",
474
- "--service",
475
- "twilio",
476
- "--field",
477
- "account_sid",
478
- "AC1234567890",
479
- "--json",
480
- ]);
481
- expect(result.exitCode).toBe(0);
482
- const parsed = JSON.parse(result.stdout);
483
- expect(parsed.ok).toBe(true);
484
- expect(parsed.service).toBe("twilio");
485
- expect(parsed.field).toBe("account_sid");
486
- expect(parsed.credentialId).toBeTruthy();
487
-
488
- // Verify secret stored in mock map
489
- expect(secureKeyStore.get(credentialKey("twilio", "account_sid"))).toBe(
490
- "AC1234567890",
491
- );
492
-
493
- // Verify metadata created
494
- const meta = metadataStore.find(
495
- (m) => m.service === "twilio" && m.field === "account_sid",
496
- );
497
- expect(meta).toBeTruthy();
498
- expect(meta!.service).toBe("twilio");
499
- expect(meta!.field).toBe("account_sid");
500
- });
501
-
502
- test("stores metadata with --label and --description", async () => {
503
- const result = await runCli([
504
- "set",
505
- "--service",
506
- "fal",
507
- "--field",
508
- "api_key",
509
- "key_live_abc",
510
- "--label",
511
- "fal-prod",
512
- "--description",
513
- "Image generation",
514
- "--json",
515
- ]);
516
- expect(result.exitCode).toBe(0);
517
- const parsed = JSON.parse(result.stdout);
518
- expect(parsed.ok).toBe(true);
519
-
520
- const meta = metadataStore.find(
521
- (m) => m.service === "fal" && m.field === "api_key",
522
- );
523
- expect(meta).toBeTruthy();
524
- expect(meta!.alias).toBe("fal-prod");
525
- expect(meta!.usageDescription).toBe("Image generation");
526
- });
527
-
528
- test("errors when --service flag is missing", async () => {
529
- const result = await runCli([
530
- "set",
531
- "--field",
532
- "account_sid",
533
- "some_value",
534
- "--json",
535
- ]);
536
- // Commander should error on missing required option
537
- expect(result.exitCode).not.toBe(0);
538
- });
539
-
540
- test("errors when --field flag is missing", async () => {
541
- const result = await runCli([
542
- "set",
543
- "--service",
544
- "twilio",
545
- "some_value",
546
- "--json",
547
- ]);
548
- // Commander should error on missing required option
549
- expect(result.exitCode).not.toBe(0);
550
- });
551
-
552
- test("errors when value argument is missing", async () => {
553
- const result = await runCli([
554
- "set",
555
- "--service",
556
- "twilio",
557
- "--field",
558
- "account_sid",
559
- "--json",
560
- ]);
561
- // Commander should error on missing required arg
562
- expect(result.exitCode).not.toBe(0);
563
- });
564
-
565
- test("stores metadata with --allowed-tools", async () => {
566
- const result = await runCli([
567
- "set",
568
- "--service",
569
- "twilio",
570
- "--field",
571
- "auth_token",
572
- "sometoken",
573
- "--allowed-tools",
574
- "bash,host_bash",
575
- "--json",
576
- ]);
577
- expect(result.exitCode).toBe(0);
578
- const parsed = JSON.parse(result.stdout);
579
- expect(parsed.ok).toBe(true);
580
-
581
- const meta = metadataStore.find(
582
- (m) => m.service === "twilio" && m.field === "auth_token",
583
- );
584
- expect(meta).toBeTruthy();
585
- expect(meta!.allowedTools).toEqual(["bash", "host_bash"]);
586
- });
587
-
588
- test("updates existing credential on second set", async () => {
589
- // First set
590
- await runCli([
591
- "set",
592
- "--service",
593
- "twilio",
594
- "--field",
595
- "account_sid",
596
- "original_value",
597
- "--json",
598
- ]);
599
- const meta1 = metadataStore.find(
600
- (m) => m.service === "twilio" && m.field === "account_sid",
601
- );
602
- expect(meta1).toBeTruthy();
603
- const firstUpdatedAt = meta1!.updatedAt;
604
-
605
- // Small delay to ensure timestamp differs
606
- await new Promise((resolve) => setTimeout(resolve, 5));
607
-
608
- // Second set
609
- await runCli([
610
- "set",
611
- "--service",
612
- "twilio",
613
- "--field",
614
- "account_sid",
615
- "new_value",
616
- "--json",
617
- ]);
618
- const meta2 = metadataStore.find(
619
- (m) => m.service === "twilio" && m.field === "account_sid",
620
- );
621
- expect(meta2).toBeTruthy();
622
- expect(meta2!.updatedAt).toBeGreaterThan(firstUpdatedAt);
623
-
624
- // Verify secret is overwritten
625
- expect(secureKeyStore.get(credentialKey("twilio", "account_sid"))).toBe(
626
- "new_value",
627
- );
628
- });
629
- });
630
-
631
- // =========================================================================
632
- // delete
633
- // =========================================================================
634
-
635
- describe("delete", () => {
636
- test("removes both secret and metadata", async () => {
637
- seedCredential("twilio", "auth_token", "secret_value_here");
638
-
639
- const result = await runCli([
640
- "delete",
641
- "--service",
642
- "twilio",
643
- "--field",
644
- "auth_token",
645
- "--json",
646
- ]);
647
- expect(result.exitCode).toBe(0);
648
- const parsed = JSON.parse(result.stdout);
649
- expect(parsed.ok).toBe(true);
650
- expect(parsed.service).toBe("twilio");
651
- expect(parsed.field).toBe("auth_token");
652
-
653
- // Verify both removed
654
- expect(secureKeyStore.has(credentialKey("twilio", "auth_token"))).toBe(
655
- false,
656
- );
657
- expect(
658
- metadataStore.find(
659
- (m) => m.service === "twilio" && m.field === "auth_token",
660
- ),
661
- ).toBeUndefined();
662
- });
663
-
664
- test("errors on nonexistent credential", async () => {
665
- const result = await runCli([
666
- "delete",
667
- "--service",
668
- "twilio",
669
- "--field",
670
- "nonexistent",
671
- "--json",
672
- ]);
673
- expect(result.exitCode).toBe(1);
674
- const parsed = JSON.parse(result.stdout);
675
- expect(parsed.ok).toBe(false);
676
- expect(parsed.error).toContain("not found");
677
- });
678
-
679
- test("errors when --service flag is missing", async () => {
680
- const result = await runCli([
681
- "delete",
682
- "--field",
683
- "auth_token",
684
- "--json",
685
- ]);
686
- // Commander should error on missing required option
687
- expect(result.exitCode).not.toBe(0);
688
- });
689
-
690
- test("errors when --field flag is missing", async () => {
691
- const result = await runCli(["delete", "--service", "twilio", "--json"]);
692
- // Commander should error on missing required option
693
- expect(result.exitCode).not.toBe(0);
694
- });
695
-
696
- test("succeeds when only metadata exists (no secret)", async () => {
697
- seedMetadataOnly("twilio", "auth_token");
698
-
699
- const result = await runCli([
700
- "delete",
701
- "--service",
702
- "twilio",
703
- "--field",
704
- "auth_token",
705
- "--json",
706
- ]);
707
- expect(result.exitCode).toBe(0);
708
- const parsed = JSON.parse(result.stdout);
709
- expect(parsed.ok).toBe(true);
710
-
711
- // Verify metadata removed
712
- expect(
713
- metadataStore.find(
714
- (m) => m.service === "twilio" && m.field === "auth_token",
715
- ),
716
- ).toBeUndefined();
717
- });
718
-
719
- test("calls disconnectOAuthProvider for OAuth cleanup", async () => {
720
- seedCredential("gmail", "access_token", "ya29.token_value");
721
-
722
- const result = await runCli([
723
- "delete",
724
- "--service",
725
- "gmail",
726
- "--field",
727
- "access_token",
728
- "--json",
729
- ]);
730
- expect(result.exitCode).toBe(0);
731
- const parsed = JSON.parse(result.stdout);
732
- expect(parsed.ok).toBe(true);
733
-
734
- // disconnectOAuthProvider should have been called with the service name
735
- expect(disconnectOAuthProviderCalls).toEqual(["gmail"]);
736
- });
737
-
738
- test("succeeds when only OAuth connection exists (no legacy credential)", async () => {
739
- // No legacy credential seeded — only the OAuth disconnect finds something
740
- disconnectOAuthProviderResult = "disconnected";
741
-
742
- const result = await runCli([
743
- "delete",
744
- "--service",
745
- "gmail",
746
- "--field",
747
- "access_token",
748
- "--json",
749
- ]);
750
- expect(result.exitCode).toBe(0);
751
- const parsed = JSON.parse(result.stdout);
752
- expect(parsed.ok).toBe(true);
753
- expect(parsed.service).toBe("gmail");
754
- expect(parsed.field).toBe("access_token");
755
-
756
- expect(disconnectOAuthProviderCalls).toEqual(["gmail"]);
757
- });
758
- });
759
-
760
- // =========================================================================
761
- // inspect
762
- // =========================================================================
763
-
764
- describe("inspect", () => {
765
- test("shows metadata and scrubbed value by --service/--field", async () => {
766
- const meta = seedCredential("twilio", "account_sid", "AC123456789012");
767
-
768
- const result = await runCli([
769
- "inspect",
770
- "--service",
771
- "twilio",
772
- "--field",
773
- "account_sid",
774
- "--json",
775
- ]);
776
- expect(result.exitCode).toBe(0);
777
- const parsed = JSON.parse(result.stdout);
778
- expect(parsed.ok).toBe(true);
779
- expect(parsed.service).toBe("twilio");
780
- expect(parsed.field).toBe("account_sid");
781
- expect(parsed.credentialId).toBe(meta.credentialId);
782
- expect(parsed.scrubbedValue).toBe("****9012");
783
- expect(parsed.hasSecret).toBe(true);
784
- expect(parsed.createdAt).toBe(new Date(meta.createdAt).toISOString());
785
- expect(parsed.updatedAt).toBe(new Date(meta.updatedAt).toISOString());
786
- expect(parsed).toHaveProperty("alias");
787
- expect(parsed).toHaveProperty("usageDescription");
788
- expect(parsed).toHaveProperty("allowedTools");
789
- expect(parsed).toHaveProperty("allowedDomains");
790
- expect(parsed).toHaveProperty("grantedScopes");
791
- expect(parsed).toHaveProperty("expiresAt");
792
- expect(parsed).toHaveProperty("injectionTemplateCount");
793
- });
794
-
795
- test("looks up credential by UUID", async () => {
796
- const meta = seedCredential("github", "token", "ghp_abcdefghij1234");
797
-
798
- const result = await runCli(["inspect", meta.credentialId, "--json"]);
799
- expect(result.exitCode).toBe(0);
800
- const parsed = JSON.parse(result.stdout);
801
- expect(parsed.ok).toBe(true);
802
- expect(parsed.service).toBe("github");
803
- expect(parsed.field).toBe("token");
804
- expect(parsed.credentialId).toBe(meta.credentialId);
805
- });
806
-
807
- test("scrubs normal-length secret (>4 chars): shows last 4", async () => {
808
- seedCredential("test", "normal", "abcdefgh");
809
-
810
- const result = await runCli([
811
- "inspect",
812
- "--service",
813
- "test",
814
- "--field",
815
- "normal",
816
- "--json",
817
- ]);
818
- const parsed = JSON.parse(result.stdout);
819
- expect(parsed.scrubbedValue).toBe("****efgh");
820
- });
821
-
822
- test("scrubs short secret (<=4 chars): shows only ****", async () => {
823
- seedCredential("test", "short", "ab");
824
-
825
- const result = await runCli([
826
- "inspect",
827
- "--service",
828
- "test",
829
- "--field",
830
- "short",
831
- "--json",
832
- ]);
833
- const parsed = JSON.parse(result.stdout);
834
- expect(parsed.scrubbedValue).toBe("****");
835
- });
836
-
837
- test("shows (not set) when no secret exists", async () => {
838
- seedMetadataOnly("test", "nosecret");
839
-
840
- const result = await runCli([
841
- "inspect",
842
- "--service",
843
- "test",
844
- "--field",
845
- "nosecret",
846
- "--json",
847
- ]);
848
- const parsed = JSON.parse(result.stdout);
849
- expect(parsed.ok).toBe(true);
850
- expect(parsed.scrubbedValue).toBe("(not set)");
851
- expect(parsed.hasSecret).toBe(false);
852
- });
853
-
854
- test("errors when neither flags nor UUID provided", async () => {
855
- const result = await runCli(["inspect", "--json"]);
856
- expect(result.exitCode).toBe(1);
857
- const parsed = JSON.parse(result.stdout);
858
- expect(parsed.ok).toBe(false);
859
- expect(parsed.error).toContain("--service");
860
- });
861
-
862
- test("errors on nonexistent credential by --service/--field", async () => {
863
- const result = await runCli([
864
- "inspect",
865
- "--service",
866
- "nonexistent",
867
- "--field",
868
- "field",
869
- "--json",
870
- ]);
871
- expect(result.exitCode).toBe(1);
872
- const parsed = JSON.parse(result.stdout);
873
- expect(parsed.ok).toBe(false);
874
- expect(parsed.error).toContain("not found");
875
- });
876
-
877
- test("errors on nonexistent UUID", async () => {
878
- const result = await runCli([
879
- "inspect",
880
- "00000000-0000-0000-0000-000000000099",
881
- "--json",
882
- ]);
883
- expect(result.exitCode).toBe(1);
884
- const parsed = JSON.parse(result.stdout);
885
- expect(parsed.ok).toBe(false);
886
- expect(parsed.error).toContain("not found");
887
- });
888
-
889
- test("--json flag produces compact JSON (single line)", async () => {
890
- seedCredential("twilio", "account_sid", "AC123456789012");
891
-
892
- const result = await runCli([
893
- "inspect",
894
- "--service",
895
- "twilio",
896
- "--field",
897
- "account_sid",
898
- "--json",
899
- ]);
900
- const lines = result.stdout.trim().split("\n");
901
- expect(lines).toHaveLength(1);
902
- // Verify it parses as valid JSON
903
- expect(() => JSON.parse(lines[0])).not.toThrow();
904
- });
905
-
906
- test("shows hasSecret: false when metadata exists but no secret", async () => {
907
- seedMetadataOnly("test", "metaonly");
908
-
909
- const result = await runCli([
910
- "inspect",
911
- "--service",
912
- "test",
913
- "--field",
914
- "metaonly",
915
- "--json",
916
- ]);
917
- const parsed = JSON.parse(result.stdout);
918
- expect(parsed.ok).toBe(true);
919
- expect(parsed.hasSecret).toBe(false);
920
- expect(parsed.scrubbedValue).toBe("(not set)");
921
- });
922
-
923
- test("shows broker unreachable when metadata exists but broker is down", async () => {
924
- seedMetadataOnly("twilio", "account_sid");
925
- mockBrokerUnreachable = true;
926
-
927
- const result = await runCli([
928
- "inspect",
929
- "--service",
930
- "twilio",
931
- "--field",
932
- "account_sid",
933
- "--json",
934
- ]);
935
- expect(result.exitCode).toBe(0);
936
- const parsed = JSON.parse(result.stdout);
937
- expect(parsed.ok).toBe(true);
938
- expect(parsed.scrubbedValue).toBe("(credential store unreachable)");
939
- expect(parsed.brokerUnreachable).toBe(true);
940
- });
941
-
942
- test("shows unreachable error when no metadata and broker is down", async () => {
943
- mockBrokerUnreachable = true;
944
-
945
- const result = await runCli([
946
- "inspect",
947
- "--service",
948
- "nonexistent",
949
- "--field",
950
- "field",
951
- "--json",
952
- ]);
953
- expect(result.exitCode).toBe(1);
954
- const parsed = JSON.parse(result.stdout);
955
- expect(parsed.ok).toBe(false);
956
- expect(parsed.error).toContain("Credential store is unreachable");
957
- });
958
- });
959
-
960
- // =========================================================================
961
- // reveal
962
- // =========================================================================
963
-
964
- describe("reveal", () => {
965
- test("returns plaintext value by --service/--field", async () => {
966
- seedCredential("twilio", "account_sid", "AC123456789012");
967
-
968
- const result = await runCli([
969
- "reveal",
970
- "--service",
971
- "twilio",
972
- "--field",
973
- "account_sid",
974
- "--json",
975
- ]);
976
- expect(result.exitCode).toBe(0);
977
- const parsed = JSON.parse(result.stdout);
978
- expect(parsed.ok).toBe(true);
979
- expect(parsed.value).toBe("AC123456789012");
980
- });
981
-
982
- test("returns plaintext value by UUID", async () => {
983
- const meta = seedCredential("github", "token", "ghp_abcdefghij1234");
984
-
985
- const result = await runCli(["reveal", meta.credentialId, "--json"]);
986
- expect(result.exitCode).toBe(0);
987
- const parsed = JSON.parse(result.stdout);
988
- expect(parsed.ok).toBe(true);
989
- expect(parsed.value).toBe("ghp_abcdefghij1234");
990
- });
991
-
992
- test("errors on nonexistent credential by --service/--field", async () => {
993
- const result = await runCli([
994
- "reveal",
995
- "--service",
996
- "nonexistent",
997
- "--field",
998
- "field",
999
- "--json",
1000
- ]);
1001
- expect(result.exitCode).toBe(1);
1002
- const parsed = JSON.parse(result.stdout);
1003
- expect(parsed.ok).toBe(false);
1004
- expect(parsed.error).toContain("not found");
1005
- });
1006
-
1007
- test("errors on nonexistent UUID", async () => {
1008
- const result = await runCli([
1009
- "reveal",
1010
- "00000000-0000-0000-0000-000000000099",
1011
- "--json",
1012
- ]);
1013
- expect(result.exitCode).toBe(1);
1014
- const parsed = JSON.parse(result.stdout);
1015
- expect(parsed.ok).toBe(false);
1016
- expect(parsed.error).toContain("not found");
1017
- });
1018
-
1019
- test("errors when neither flags nor UUID provided", async () => {
1020
- const result = await runCli(["reveal", "--json"]);
1021
- expect(result.exitCode).toBe(1);
1022
- const parsed = JSON.parse(result.stdout);
1023
- expect(parsed.ok).toBe(false);
1024
- expect(parsed.error).toContain("--service");
1025
- });
1026
-
1027
- test("reveal in human mode emits bare secret with trailing newline", async () => {
1028
- seedCredential("twilio", "auth_token", "secret_xyz_789");
1029
-
1030
- const result = await runCli([
1031
- "reveal",
1032
- "--service",
1033
- "twilio",
1034
- "--field",
1035
- "auth_token",
1036
- ]);
1037
- expect(result.exitCode).toBe(0);
1038
- expect(result.stdout).toBe("secret_xyz_789\n");
1039
- });
1040
-
1041
- test("errors when metadata exists but no secret stored", async () => {
1042
- seedMetadataOnly("test", "nosecret");
1043
-
1044
- const result = await runCli([
1045
- "reveal",
1046
- "--service",
1047
- "test",
1048
- "--field",
1049
- "nosecret",
1050
- "--json",
1051
- ]);
1052
- expect(result.exitCode).toBe(1);
1053
- const parsed = JSON.parse(result.stdout);
1054
- expect(parsed.ok).toBe(false);
1055
- expect(parsed.error).toContain("not found");
1056
- });
1057
-
1058
- test("returns unreachable error when broker is down", async () => {
1059
- mockBrokerUnreachable = true;
1060
-
1061
- const result = await runCli([
1062
- "reveal",
1063
- "--service",
1064
- "twilio",
1065
- "--field",
1066
- "auth_token",
1067
- "--json",
1068
- ]);
1069
- expect(result.exitCode).toBe(1);
1070
- const parsed = JSON.parse(result.stdout);
1071
- expect(parsed.ok).toBe(false);
1072
- expect(parsed.error).toContain("Credential store is unreachable");
1073
- });
1074
-
1075
- test("returns credential-not-found when broker is up", async () => {
1076
- mockBrokerUnreachable = false;
1077
-
1078
- const result = await runCli([
1079
- "reveal",
1080
- "--service",
1081
- "twilio",
1082
- "--field",
1083
- "nonexistent",
1084
- "--json",
1085
- ]);
1086
- expect(result.exitCode).toBe(1);
1087
- const parsed = JSON.parse(result.stdout);
1088
- expect(parsed.ok).toBe(false);
1089
- expect(parsed.error).toBe("Credential not found");
1090
- });
1091
- });
1092
-
1093
- // =========================================================================
1094
- // compound service names (colons in service)
1095
- // =========================================================================
1096
-
1097
- describe("compound service names", () => {
1098
- test("set and reveal with colon in service name works correctly", async () => {
1099
- const setResult = await runCli([
1100
- "set",
1101
- "--service",
1102
- "google",
1103
- "--field",
1104
- "client_secret",
1105
- "secret123",
1106
- "--json",
1107
- ]);
1108
- expect(setResult.exitCode).toBe(0);
1109
- const setParsed = JSON.parse(setResult.stdout);
1110
- expect(setParsed.ok).toBe(true);
1111
- expect(setParsed.service).toBe("google");
1112
- expect(setParsed.field).toBe("client_secret");
1113
-
1114
- const revealResult = await runCli([
1115
- "reveal",
1116
- "--service",
1117
- "google",
1118
- "--field",
1119
- "client_secret",
1120
- "--json",
1121
- ]);
1122
- expect(revealResult.exitCode).toBe(0);
1123
- const revealParsed = JSON.parse(revealResult.stdout);
1124
- expect(revealParsed.ok).toBe(true);
1125
- expect(revealParsed.value).toBe("secret123");
1126
- });
1127
- });
1128
-
1129
- // =========================================================================
1130
- // instance-scoped workspace
1131
- // =========================================================================
1132
-
1133
- describe("instance-scoped workspace", () => {
1134
- let savedWorkspaceDir: string | undefined;
1135
-
1136
- beforeEach(() => {
1137
- savedWorkspaceDir = process.env.VELLUM_WORKSPACE_DIR;
1138
- });
1139
-
1140
- afterEach(() => {
1141
- if (savedWorkspaceDir === undefined) {
1142
- delete process.env.VELLUM_WORKSPACE_DIR;
1143
- } else {
1144
- process.env.VELLUM_WORKSPACE_DIR = savedWorkspaceDir;
1145
- }
1146
- });
1147
-
1148
- test("credential reveal reads from instance-scoped store when VELLUM_WORKSPACE_DIR is set", async () => {
1149
- // Point VELLUM_WORKSPACE_DIR to a temp directory (simulating instance-scoped dir)
1150
- const tmpDir = (await import("node:os")).tmpdir();
1151
- const instanceDir = (await import("node:path")).join(
1152
- tmpDir,
1153
- `vellum-test-instance-${Date.now()}`,
1154
- );
1155
- process.env.VELLUM_WORKSPACE_DIR = instanceDir;
1156
-
1157
- // Seed a credential in the mock store
1158
- seedCredential("twilio", "auth_token", "instance_secret_abc123");
1159
-
1160
- // Run `credentials reveal --service twilio --field auth_token`
1161
- const result = await runCli([
1162
- "reveal",
1163
- "--service",
1164
- "twilio",
1165
- "--field",
1166
- "auth_token",
1167
- "--json",
1168
- ]);
1169
- expect(result.exitCode).toBe(0);
1170
- const parsed = JSON.parse(result.stdout);
1171
- expect(parsed.ok).toBe(true);
1172
- expect(parsed.value).toBe("instance_secret_abc123");
1173
-
1174
- // Verify the correct key was looked up in the secure store
1175
- expect(secureKeyStore.has(credentialKey("twilio", "auth_token"))).toBe(
1176
- true,
1177
- );
1178
- expect(secureKeyStore.get(credentialKey("twilio", "auth_token"))).toBe(
1179
- "instance_secret_abc123",
1180
- );
1181
- });
1182
- });
1183
-
1184
- // =========================================================================
1185
- // help text quality
1186
- // =========================================================================
1187
-
1188
- describe("help text", () => {
1189
- test("credentials --help contains naming convention table and storage description", async () => {
1190
- const result = await runCli(["--help"]);
1191
- const out = result.stdout;
1192
- expect(out).toContain("--service twilio --field account_sid");
1193
- expect(out).toContain("AES-256-GCM");
1194
- expect(out).toContain("Examples:");
1195
- });
1196
-
1197
- test("credentials list --help contains --search description and examples", async () => {
1198
- const result = await runCli(["list", "--help"]);
1199
- const out = result.stdout;
1200
- expect(out).toContain("--search");
1201
- expect(out).toContain("Examples:");
1202
- expect(out).toContain("credentials list --search twilio");
1203
- });
1204
-
1205
- test("credentials set --help contains Arguments: and Examples: sections", async () => {
1206
- const result = await runCli(["set", "--help"]);
1207
- const out = result.stdout;
1208
- expect(out).toContain("Arguments:");
1209
- expect(out).toContain("Examples:");
1210
- });
1211
-
1212
- test("credentials inspect --help mentions UUID support", async () => {
1213
- const result = await runCli(["inspect", "--help"]);
1214
- const out = result.stdout;
1215
- expect(out).toContain("UUID");
1216
- });
1217
-
1218
- test("credentials reveal --help mentions piping and examples", async () => {
1219
- const result = await runCli(["reveal", "--help"]);
1220
- const out = result.stdout;
1221
- expect(out).toContain("stdout");
1222
- expect(out).toContain("Examples:");
1223
- });
1224
- });
1225
- });