@vellumai/assistant 0.11.4 → 0.11.5-staging.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 (358) hide show
  1. package/AGENTS.md +2 -2
  2. package/ARCHITECTURE.md +29 -4
  3. package/README.md +1 -1
  4. package/docs/architecture/memory.md +17 -5
  5. package/docs/architecture/security.md +96 -152
  6. package/docs/guardian-request-flow.md +13 -2
  7. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  8. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
  9. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  10. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
  11. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  12. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
  13. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  14. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
  15. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +166 -1
  16. package/node_modules/@vellumai/gateway-client/src/http-delivery.ts +7 -14
  17. package/node_modules/@vellumai/gateway-client/src/index.ts +2 -0
  18. package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +29 -31
  19. package/node_modules/@vellumai/service-contracts/package.json +1 -0
  20. package/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
  21. package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  22. package/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
  23. package/openapi.yaml +193 -3
  24. package/package.json +1 -1
  25. package/src/__tests__/anthropic-provider.test.ts +10 -3
  26. package/src/__tests__/approval-interception-trust-gates.test.ts +40 -0
  27. package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
  28. package/src/__tests__/channel-approval-routes.test.ts +4 -0
  29. package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -6
  30. package/src/__tests__/channel-reply-delivery.test.ts +26 -13
  31. package/src/__tests__/checker.test.ts +20 -314
  32. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +6 -2
  33. package/src/__tests__/client-os-metadata-persistence.test.ts +16 -0
  34. package/src/__tests__/compactor-retained-image-validation.test.ts +142 -0
  35. package/src/__tests__/container-cpu-sampler.test.ts +153 -0
  36. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
  37. package/src/__tests__/conversation-attachments.test.ts +0 -1
  38. package/src/__tests__/conversation-error.test.ts +17 -0
  39. package/src/__tests__/conversation-lifecycle.test.ts +20 -26
  40. package/src/__tests__/conversation-pairing.test.ts +186 -0
  41. package/src/__tests__/conversation-runtime-assembly.test.ts +21 -2
  42. package/src/__tests__/credential-broker-server-use.test.ts +24 -5
  43. package/src/__tests__/credential-routes.test.ts +22 -3
  44. package/src/__tests__/credential-security-invariants.test.ts +2 -1
  45. package/src/__tests__/daemon-credential-client.test.ts +88 -0
  46. package/src/__tests__/default-plugin-names-guard.test.ts +12 -1
  47. package/src/__tests__/dm-backfill.test.ts +63 -0
  48. package/src/__tests__/dynamic-skill-background-guard.test.ts +0 -2
  49. package/src/__tests__/edit-propagation.test.ts +107 -4
  50. package/src/__tests__/events-client-registration.test.ts +28 -0
  51. package/src/__tests__/file-write-tool.test.ts +4 -2
  52. package/src/__tests__/guardian-prompt-notice-privacy.test.ts +133 -0
  53. package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +143 -97
  54. package/src/__tests__/helpers/gateway-classify-mock.ts +27 -6
  55. package/src/__tests__/host-proxy-interface.test.ts +11 -1
  56. package/src/__tests__/host-shell-tool.test.ts +23 -4
  57. package/src/__tests__/image-conversion.test.ts +143 -1
  58. package/src/__tests__/inline-skill-load-permissions.test.ts +47 -36
  59. package/src/__tests__/input-repairs.test.ts +207 -0
  60. package/src/__tests__/live-workspace-guard.test.ts +75 -0
  61. package/src/__tests__/mcp-abort-signal.test.ts +1 -1
  62. package/src/__tests__/mcp-client-auth.test.ts +1 -1
  63. package/src/__tests__/mcp-tool-annotations-risk.test.ts +1 -1
  64. package/src/__tests__/media-resolve-image-validation.test.ts +310 -0
  65. package/src/__tests__/mtime-cache.test.ts +2 -0
  66. package/src/__tests__/notification-vellum-adapter.test.ts +124 -1
  67. package/src/__tests__/openai-provider.test.ts +22 -0
  68. package/src/__tests__/personal-memory-auth-bypass.test.ts +22 -6
  69. package/src/__tests__/platform-bash-auto-approve.test.ts +0 -4
  70. package/src/__tests__/platform.test.ts +16 -1
  71. package/src/__tests__/plugin-api-resolve-credential.test.ts +22 -13
  72. package/src/__tests__/plugin-api-shim.test.ts +5 -0
  73. package/src/__tests__/plugin-api-store-credential.test.ts +268 -0
  74. package/src/__tests__/plugin-config-data-migration.test.ts +8 -1
  75. package/src/__tests__/plugin-disabled-state.test.ts +2 -0
  76. package/src/__tests__/plugin-effective-enabled-set.test.ts +55 -3
  77. package/src/__tests__/plugin-execution-context.test.ts +73 -0
  78. package/src/__tests__/plugin-import-boundary-guard.test.ts +0 -1
  79. package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +0 -1
  80. package/src/__tests__/reaction-persistence.test.ts +200 -7
  81. package/src/__tests__/require-fresh-approval.test.ts +0 -4
  82. package/src/__tests__/resource-pressure-guard.test.ts +428 -0
  83. package/src/__tests__/resource-pressure-routes.test.ts +113 -0
  84. package/src/__tests__/risk-classification-boundary-guard.test.ts +115 -0
  85. package/src/__tests__/run-conversation-turn-persistence.test.ts +27 -1
  86. package/src/__tests__/secret-routes-platform-proxy.test.ts +7 -0
  87. package/src/__tests__/skill-tool-factory.test.ts +161 -0
  88. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
  89. package/src/__tests__/tool-executor-lifecycle-events.test.ts +132 -5
  90. package/src/__tests__/tool-executor.test.ts +63 -38
  91. package/src/__tests__/tool-policy.test.ts +97 -1
  92. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +7 -4
  93. package/src/__tests__/user-plugin-loader.test.ts +2 -0
  94. package/src/__tests__/validate-input.test.ts +243 -7
  95. package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
  96. package/src/acp/__tests__/acp-claude-oauth.test.ts +97 -29
  97. package/src/acp/__tests__/prepare-agent-env.test.ts +376 -153
  98. package/src/acp/acp-claude-oauth.ts +38 -21
  99. package/src/acp/prepare-agent-env.ts +146 -55
  100. package/src/agent/loop-tool-dedup.test.ts +161 -0
  101. package/src/agent/loop.ts +68 -22
  102. package/src/api/constants/app-tools.ts +34 -0
  103. package/src/api/events/resource-pressure-status-changed.ts +53 -0
  104. package/src/api/index.ts +19 -0
  105. package/src/api/responses/resource-pressure-status.ts +25 -0
  106. package/src/approvals/guardian-channel-delivery.ts +70 -6
  107. package/src/approvals/guardian-request-resolvers.ts +32 -66
  108. package/src/channels/__tests__/message-audience.test.ts +49 -0
  109. package/src/channels/__tests__/types.test.ts +17 -3
  110. package/src/channels/message-audience.ts +40 -0
  111. package/src/channels/types.ts +16 -12
  112. package/src/cli/__tests__/catalog-search-help.test.ts +50 -0
  113. package/src/cli/commands/__tests__/channel-verification-sessions.test.ts +31 -0
  114. package/src/cli/commands/__tests__/conversations-search.test.ts +289 -0
  115. package/src/cli/commands/__tests__/conversations-slack.test.ts +1 -0
  116. package/src/cli/commands/__tests__/inference-providers.test.ts +68 -0
  117. package/src/cli/commands/__tests__/keys.test.ts +89 -8
  118. package/src/cli/commands/__tests__/plugins.test.ts +57 -1
  119. package/src/cli/commands/channel-verification-sessions.help.ts +11 -9
  120. package/src/cli/commands/channel-verification-sessions.ts +11 -21
  121. package/src/cli/commands/conversations.help.ts +34 -0
  122. package/src/cli/commands/conversations.ts +125 -0
  123. package/src/cli/commands/inference-providers.ts +9 -4
  124. package/src/cli/commands/inference.help.ts +9 -4
  125. package/src/cli/commands/keys.help.ts +13 -1
  126. package/src/cli/commands/keys.ts +26 -15
  127. package/src/cli/commands/memory/__tests__/memory-v2.test.ts +57 -7
  128. package/src/cli/commands/memory/index.help.ts +41 -27
  129. package/src/cli/commands/memory/index.ts +2 -0
  130. package/src/cli/commands/memory/memory-v2.ts +58 -54
  131. package/src/cli/commands/memory/memory-validate.ts +18 -0
  132. package/src/cli/commands/monitoring.ts +1 -0
  133. package/src/cli/commands/plugins.help.ts +28 -31
  134. package/src/cli/commands/plugins.ts +10 -0
  135. package/src/cli/commands/skills.help.ts +13 -16
  136. package/src/cli/commands/trust.ts +3 -14
  137. package/src/cli/lib/__tests__/install-from-github.test.ts +38 -0
  138. package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +34 -0
  139. package/src/cli/lib/__tests__/plugin-surfaces.test.ts +32 -1
  140. package/src/cli/lib/__tests__/toggle-plugin.test.ts +2 -1
  141. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +64 -0
  142. package/src/cli/lib/bundled-marketplace.json +13 -0
  143. package/src/cli/lib/daemon-credential-client.ts +25 -3
  144. package/src/cli/lib/install-from-github.ts +35 -24
  145. package/src/cli/lib/merge-plugin-tree.ts +22 -4
  146. package/src/cli/lib/plugin-surfaces.ts +27 -0
  147. package/src/config/feature-flag-registry.json +35 -19
  148. package/src/config/webhook-routing.ts +8 -0
  149. package/src/context/compactor.ts +31 -2
  150. package/src/daemon/__tests__/provider-rejection-log-fields.test.ts +272 -0
  151. package/src/daemon/conversation-agent-loop-handlers.ts +4 -1
  152. package/src/daemon/conversation-error.ts +13 -0
  153. package/src/daemon/conversation-messaging.ts +22 -2
  154. package/src/daemon/conversation-process.ts +11 -0
  155. package/src/daemon/conversation-store.ts +10 -0
  156. package/src/daemon/conversation-surfaces.ts +21 -8
  157. package/src/daemon/conversation.ts +12 -10
  158. package/src/daemon/lifecycle.ts +6 -2
  159. package/src/daemon/message-types/conversations.ts +5 -7
  160. package/src/daemon/provider-rejection-log-fields.ts +124 -0
  161. package/src/daemon/resource-pressure-guard-lifecycle.ts +66 -0
  162. package/src/daemon/resource-pressure-guard.ts +387 -0
  163. package/src/daemon/shutdown-handlers.ts +2 -0
  164. package/src/daemon/startup-error.ts +16 -0
  165. package/src/daemon/trust-context.ts +14 -10
  166. package/src/daemon/unsendable-image-notice.ts +76 -0
  167. package/src/hooks/registry.ts +65 -51
  168. package/src/ipc/__tests__/socket-path.test.ts +15 -7
  169. package/src/ipc/gateway-client.test.ts +1 -1
  170. package/src/ipc/gateway-client.ts +14 -21
  171. package/src/ipc/socket-cleanup.ts +2 -11
  172. package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +1 -1
  173. package/src/messaging/provider-message-metadata.ts +128 -0
  174. package/src/messaging/providers/__tests__/transport-dispatch.test.ts +116 -68
  175. package/src/messaging/providers/channel-transport.ts +90 -13
  176. package/src/messaging/providers/discord/send.ts +16 -0
  177. package/src/messaging/providers/discord/transport.ts +11 -1
  178. package/src/messaging/providers/index.ts +109 -16
  179. package/src/messaging/providers/slack/message-metadata.ts +45 -0
  180. package/src/messaging/providers/slack/render-transcript.ts +1 -4
  181. package/src/messaging/providers/slack/send.test.ts +6 -9
  182. package/src/messaging/providers/slack/send.ts +40 -28
  183. package/src/messaging/providers/slack/transport.ts +22 -26
  184. package/src/messaging/providers/telegram-bot/send.test.ts +2 -8
  185. package/src/messaging/providers/telegram-bot/transport.ts +3 -6
  186. package/src/messaging/read-provider-metadata.test.ts +157 -0
  187. package/src/messaging/read-provider-metadata.ts +51 -0
  188. package/src/notifications/__tests__/assistant-reply-producer.test.ts +94 -0
  189. package/src/notifications/__tests__/edit-notification.test.ts +174 -2
  190. package/src/notifications/__tests__/home-feed-side-effect.test.ts +536 -4
  191. package/src/notifications/adapters/macos.ts +55 -0
  192. package/src/notifications/adapters/slack.ts +10 -5
  193. package/src/notifications/assistant-reply-producer.ts +37 -4
  194. package/src/notifications/conversation-pairing.ts +111 -21
  195. package/src/notifications/edit-notification.ts +38 -8
  196. package/src/notifications/emit-signal.ts +12 -14
  197. package/src/notifications/home-feed-side-effect.ts +218 -15
  198. package/src/notifications/types.ts +5 -0
  199. package/src/permissions/AGENTS.md +16 -0
  200. package/src/permissions/checker.test.ts +75 -122
  201. package/src/permissions/checker.ts +44 -560
  202. package/src/permissions/confirmation-guardian-request.test.ts +28 -3
  203. package/src/permissions/confirmation-guardian-request.ts +5 -1
  204. package/src/persistence/conversation-crud.ts +23 -20
  205. package/src/persistence/conversation-queries.ts +14 -1
  206. package/src/persistence/conversation-types.ts +22 -0
  207. package/src/persistence/delivery-crud.ts +122 -15
  208. package/src/plugin-api/__tests__/import-graph-partial-mock.test.ts +67 -0
  209. package/src/plugin-api/conversation-turn.ts +27 -0
  210. package/src/plugin-api/credential-scope.test.ts +15 -0
  211. package/src/plugin-api/credential-scope.ts +14 -0
  212. package/src/plugin-api/index.ts +19 -1
  213. package/src/plugin-api/resolve-credential.ts +15 -10
  214. package/src/plugin-api/store-credential.ts +148 -0
  215. package/src/plugin-api/system-card.ts +36 -0
  216. package/src/plugin-api/vision-support.test.ts +82 -0
  217. package/src/plugin-api/vision-support.ts +14 -2
  218. package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +361 -110
  219. package/src/plugins/defaults/image-fallback/__tests__/vision-recovery.test.ts +4 -0
  220. package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +58 -0
  221. package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +64 -23
  222. package/src/plugins/defaults/image-recovery/detect.ts +24 -1
  223. package/src/plugins/defaults/main.ts +15 -0
  224. package/src/plugins/defaults/memory/AGENTS.md +11 -3
  225. package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +0 -1
  226. package/src/plugins/defaults/memory/graph-topology/pending-buffer.ts +9 -12
  227. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +1 -1
  228. package/src/plugins/defaults/memory/src/memory-v2-routes.ts +12 -10
  229. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +114 -0
  230. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-prompt-flag-gating-guard.test.ts +8 -0
  231. package/src/plugins/defaults/memory/substrate/__tests__/edge-index.test.ts +0 -30
  232. package/src/plugins/defaults/memory/substrate/__tests__/ingest.test.ts +73 -1
  233. package/src/plugins/defaults/memory/substrate/__tests__/page-index.test.ts +37 -0
  234. package/src/plugins/defaults/memory/substrate/__tests__/page-links.test.ts +208 -0
  235. package/src/plugins/defaults/memory/substrate/__tests__/prompts-consolidation.test.ts +158 -1
  236. package/src/plugins/defaults/memory/substrate/__tests__/static-context.test.ts +1 -61
  237. package/src/plugins/defaults/memory/substrate/consolidation-job.ts +68 -6
  238. package/src/plugins/defaults/memory/substrate/edge-index.ts +0 -31
  239. package/src/plugins/defaults/memory/substrate/ingest.ts +59 -0
  240. package/src/plugins/defaults/memory/substrate/page-index.ts +35 -8
  241. package/src/plugins/defaults/memory/substrate/page-links.ts +133 -0
  242. package/src/plugins/defaults/memory/substrate/page-store.ts +10 -0
  243. package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +124 -34
  244. package/src/plugins/defaults/memory/substrate/static-context.ts +0 -29
  245. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +3 -1
  246. package/src/plugins/defaults/memory/v3/card.ts +9 -9
  247. package/src/plugins/defaults/memory/v3/core-set.test.ts +7 -0
  248. package/src/plugins/defaults/memory/v3/core-set.ts +5 -1
  249. package/src/plugins/defaults/memory/v3/edge.ts +13 -57
  250. package/src/plugins/mtime-cache.ts +1 -1
  251. package/src/plugins/pipeline.ts +14 -7
  252. package/src/plugins/plugin-execution-context.ts +35 -11
  253. package/src/plugins/plugin-tree-walk.ts +63 -4
  254. package/src/providers/anthropic/__tests__/pause-turn-continuation.test.ts +170 -0
  255. package/src/providers/anthropic/client.ts +332 -241
  256. package/src/providers/connection-resolution.ts +2 -1
  257. package/src/providers/content-blocks.ts +22 -0
  258. package/src/providers/gemini/client.ts +5 -2
  259. package/src/providers/inference/__tests__/adapter-factory-litellm.test.ts +47 -1
  260. package/src/providers/inference/__tests__/adapter-factory-ollama.test.ts +83 -0
  261. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +98 -1
  262. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +38 -0
  263. package/src/providers/inference/__tests__/base-url-security.test.ts +10 -0
  264. package/src/providers/inference/__tests__/connections-ollama.test.ts +90 -0
  265. package/src/providers/inference/__tests__/missing-credential-guard.test.ts +117 -0
  266. package/src/providers/inference/adapter-factory.ts +33 -2
  267. package/src/providers/inference/auth.ts +13 -2
  268. package/src/providers/inference/credential-usage.ts +37 -0
  269. package/src/providers/inference/missing-credential-guard.ts +110 -0
  270. package/src/providers/inference/resolve-auth.ts +7 -7
  271. package/src/providers/media-resolve.ts +176 -15
  272. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +576 -46
  273. package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +125 -2
  274. package/src/providers/openai/chat-completions-provider.ts +322 -42
  275. package/src/routes/route-host-protocol.ts +7 -0
  276. package/src/routes/worker.ts +31 -10
  277. package/src/runtime/AGENTS.md +35 -0
  278. package/src/runtime/__tests__/runtime-http-port-collision.test.ts +131 -0
  279. package/src/runtime/__tests__/web-presence.test.ts +234 -0
  280. package/src/runtime/assistant-event-hub.ts +38 -0
  281. package/src/runtime/channel-reply-delivery.ts +54 -31
  282. package/src/runtime/channel-retry-sweep.ts +6 -4
  283. package/src/runtime/effective-capabilities.test.ts +19 -0
  284. package/src/runtime/effective-capabilities.ts +25 -0
  285. package/src/runtime/guardian-reply-router.ts +6 -2
  286. package/src/runtime/http-errors.ts +1 -0
  287. package/src/runtime/http-server.ts +20 -5
  288. package/src/runtime/routes/__tests__/client-routes.test.ts +150 -0
  289. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +133 -0
  290. package/src/runtime/routes/__tests__/credential-delete-in-use.test.ts +155 -0
  291. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +52 -1
  292. package/src/runtime/routes/__tests__/monitoring-routes.test.ts +1 -0
  293. package/src/runtime/routes/__tests__/pending-interactions-route.test.ts +175 -0
  294. package/src/runtime/routes/__tests__/plugins-routes.test.ts +88 -35
  295. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +55 -6
  296. package/src/runtime/routes/__tests__/system-card-turn-termination.test.ts +107 -0
  297. package/src/runtime/routes/__tests__/user-route-dispatcher-host.test.ts +52 -1
  298. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +75 -4
  299. package/src/runtime/routes/approval-routes.ts +37 -4
  300. package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +16 -22
  301. package/src/runtime/routes/canned-message-complete.ts +68 -29
  302. package/src/runtime/routes/client-routes.ts +113 -0
  303. package/src/runtime/routes/conversation-list-routes.ts +22 -7
  304. package/src/runtime/routes/conversation-management-routes.ts +15 -9
  305. package/src/runtime/routes/conversation-routes.ts +36 -22
  306. package/src/runtime/routes/credential-in-use.ts +85 -0
  307. package/src/runtime/routes/credential-routes.ts +55 -85
  308. package/src/runtime/routes/guardian-approval-interception.ts +22 -22
  309. package/src/runtime/routes/guardian-approval-reply-helpers.ts +14 -12
  310. package/src/runtime/routes/identity-routes.ts +5 -121
  311. package/src/runtime/routes/inbound-message-handler.ts +71 -27
  312. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +16 -17
  313. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +77 -50
  314. package/src/runtime/routes/inbound-stages/background-dispatch.ts +61 -97
  315. package/src/runtime/routes/inbound-stages/edit-intercept.ts +101 -77
  316. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +17 -8
  317. package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +91 -7
  318. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +72 -70
  319. package/src/runtime/routes/index.ts +2 -0
  320. package/src/runtime/routes/inference-provider-connection-routes.ts +9 -7
  321. package/src/runtime/routes/monitoring-routes.ts +9 -1
  322. package/src/runtime/routes/plugins-routes.ts +17 -5
  323. package/src/runtime/routes/resource-pressure-routes.ts +22 -0
  324. package/src/runtime/routes/secret-routes.ts +39 -8
  325. package/src/runtime/routes/settings-routes.ts +6 -6
  326. package/src/runtime/routes/surface-action-routes.ts +20 -19
  327. package/src/runtime/routes/user-route-dispatcher.ts +31 -2
  328. package/src/runtime/routes/user-route-resolution.ts +18 -1
  329. package/src/runtime/slack-reply-session.test.ts +23 -13
  330. package/src/runtime/slack-reply-session.ts +38 -55
  331. package/src/runtime/web-presence.ts +90 -0
  332. package/src/skills/validate-input.ts +177 -40
  333. package/src/subagent/__tests__/consult-context-gating.test.ts +6 -10
  334. package/src/subagent/consult-context.ts +7 -23
  335. package/src/tools/credentials/broker.ts +24 -79
  336. package/src/tools/credentials/ref-parse.ts +35 -0
  337. package/src/tools/credentials/resolve.ts +4 -10
  338. package/src/tools/credentials/store.ts +168 -0
  339. package/src/tools/credentials/tool-policy.ts +67 -0
  340. package/src/tools/executor.ts +53 -15
  341. package/src/tools/network/__tests__/web-search.test.ts +41 -1
  342. package/src/tools/network/url-safety.ts +9 -16
  343. package/src/tools/network/web-search.ts +21 -3
  344. package/src/tools/permission-checker.ts +43 -52
  345. package/src/tools/schema-transforms.ts +40 -5
  346. package/src/tools/shared/input-repairs.ts +160 -0
  347. package/src/tools/skills/skill-tool-factory.ts +18 -4
  348. package/src/tools/subagent/spawn.ts +0 -1
  349. package/src/tools/tool-approval-handler.ts +16 -10
  350. package/src/tools/tool-types.ts +26 -13
  351. package/src/tools/types.ts +6 -6
  352. package/src/util/__tests__/cgroup-memory.test.ts +3 -0
  353. package/src/util/cgroup-memory.ts +3 -0
  354. package/src/util/container-cpu-sampler.ts +250 -0
  355. package/src/util/image-conversion.ts +178 -14
  356. package/src/util/platform.ts +93 -10
  357. package/src/permissions/ipc-risk-types.ts +0 -143
  358. package/src/permissions/risk-types.ts +0 -76
@@ -16,6 +16,8 @@
16
16
  * - under --json the consent listing, the prompt, and the cancellation line
17
17
  * all go to stderr, leaving stdout a pure JSON document
18
18
  * - inspect renders the schedules surface block
19
+ * - install prints a setup-skill hint when `skills/setup` or
20
+ * `skills/<name>-setup` is present on the installed tree
19
21
  *
20
22
  * The installers are replaced by fakes that stage a fixture tree and run the
21
23
  * real `confirmStagedOrAbort` gate, so the command's consent callback is
@@ -76,6 +78,10 @@ let ipcResults: Array<{
76
78
 
77
79
  let inspectResult: PluginInspection | null = null;
78
80
 
81
+ /** On-disk tree returned as the fake install `target` so post-install walks work. */
82
+ let installTarget: string | null = null;
83
+ const installTargetDirs: string[] = [];
84
+
79
85
  // ---------------------------------------------------------------------------
80
86
  // Mocks
81
87
  // ---------------------------------------------------------------------------
@@ -123,7 +129,7 @@ async function runFakeInstall(
123
129
  await stageAndConfirm(name, deps?.confirmStaged);
124
130
  return {
125
131
  name,
126
- target: `/plugins/${name}`,
132
+ target: installTarget ?? `/plugins/${name}`,
127
133
  fileCount: 3,
128
134
  ref: "main",
129
135
  commit: "a".repeat(40),
@@ -253,6 +259,7 @@ beforeEach(() => {
253
259
  upgradePluginCalls = [];
254
260
  ipcResults = [];
255
261
  inspectResult = null;
262
+ installTarget = null;
256
263
  // Platform features on by default, so a plain name install takes the
257
264
  // platform-tarball branch.
258
265
  delete process.env.VELLUM_DISABLE_PLATFORM;
@@ -260,6 +267,10 @@ beforeEach(() => {
260
267
  });
261
268
 
262
269
  afterEach(() => {
270
+ for (const dir of installTargetDirs) {
271
+ rmSync(dir, { recursive: true, force: true });
272
+ }
273
+ installTargetDirs.length = 0;
263
274
  if (savedDisablePlatform === undefined) {
264
275
  delete process.env.VELLUM_DISABLE_PLATFORM;
265
276
  } else {
@@ -346,6 +357,7 @@ describe("plugins install - declared-schedules consent", () => {
346
357
  expect(confirmCalls.length).toBe(0);
347
358
  expect(r.stdout).not.toContain("declares");
348
359
  expect(r.stdout).not.toContain("schedule");
360
+ expect(r.stdout).not.toContain("to help set up this plugin");
349
361
  expect(r.stdout).toContain('Installed plugin "example"');
350
362
  expect(r.exitCode).toBe(0);
351
363
  });
@@ -690,3 +702,47 @@ describe("plugins inspect - schedules surface", () => {
690
702
  expect(r.exitCode).toBe(0);
691
703
  });
692
704
  });
705
+
706
+ describe("plugins install - setup skill hint", () => {
707
+ function writeSetupSkill(skillId: string): void {
708
+ const dir = mkdtempSync(join(tmpdir(), "plugins-cmd-target-"));
709
+ installTargetDirs.push(dir);
710
+ mkdirSync(join(dir, "skills", skillId), { recursive: true });
711
+ writeFileSync(
712
+ join(dir, "skills", skillId, "SKILL.md"),
713
+ "---\nname: setup\ndescription: Set up the plugin.\n---\n",
714
+ );
715
+ installTarget = dir;
716
+ }
717
+
718
+ test("points at skills/setup after a successful install", async () => {
719
+ writeSetupSkill("setup");
720
+
721
+ const r = await runCommand(["plugins", "install", "example"]);
722
+
723
+ expect(r.stdout).toContain('Installed plugin "example"');
724
+ expect(r.stdout).toContain(
725
+ "Load the setup skill to help set up this plugin",
726
+ );
727
+ expect(r.exitCode).toBe(0);
728
+ });
729
+
730
+ test("points at skills/<name>-setup when that is the shipped id", async () => {
731
+ writeSetupSkill("imessage-setup");
732
+
733
+ const r = await runCommand(["plugins", "install", "imessage"]);
734
+
735
+ expect(r.stdout).toContain(
736
+ "Load the imessage-setup skill to help set up this plugin",
737
+ );
738
+ expect(r.exitCode).toBe(0);
739
+ });
740
+
741
+ test("prints no setup hint when the plugin ships no setup skill", async () => {
742
+ const r = await runCommand(["plugins", "install", "example"]);
743
+
744
+ expect(r.stdout).toContain('Installed plugin "example"');
745
+ expect(r.stdout).not.toContain("to help set up this plugin");
746
+ expect(r.exitCode).toBe(0);
747
+ });
748
+ });
@@ -10,15 +10,16 @@ export const channelVerificationSessionsHelp: CliCommandHelp = {
10
10
  ],
11
11
  helpText: `
12
12
  Verification sessions are used to verify guardian bindings and trusted
13
- contacts across channels (telegram, phone, slack, email). Three flows exist:
13
+ contacts across channels (telegram, phone, slack, discord, email). Three
14
+ flows exist:
14
15
 
15
16
  1. Inbound challenge — the assistant generates a secret code and waits
16
17
  for the guardian to send it back on the channel. Used when the
17
18
  guardian can already message the assistant.
18
19
 
19
20
  2. Outbound verification — the assistant sends a verification code to
20
- a destination (Telegram handle, phone number, Slack user ID) and
21
- waits for confirmation. Used when bootstrapping a new channel.
21
+ a destination (Telegram handle, phone number, Slack or Discord user
22
+ ID) and waits for confirmation. Used when bootstrapping a new channel.
22
23
 
23
24
  3. Trusted contact verification — verifies a contact channel that
24
25
  already exists in the contact graph, sending a code to the channel
@@ -36,7 +37,7 @@ Examples:
36
37
  options: [
37
38
  {
38
39
  flags: "--channel <channel>",
39
- description: "Channel type (telegram, phone, slack, email)",
40
+ description: "Channel type (telegram, phone, slack, discord, email)",
40
41
  },
41
42
  {
42
43
  flags: "--destination <destination>",
@@ -72,8 +73,8 @@ Routes between three creation modes based on the provided options:
72
73
 
73
74
  2. Outbound: --channel <ch> --destination <dest>
74
75
  Sends a verification code to the given destination. Supports telegram
75
- (handle or chat ID), phone (E.164 number), slack (user ID), and email.
76
- Use --rebind to replace an existing guardian binding.
76
+ (handle or chat ID), phone (E.164 number), slack and discord (user ID),
77
+ and email. Use --rebind to replace an existing guardian binding.
77
78
 
78
79
  3. Inbound: --channel <ch> (no --destination)
79
80
  Generates a challenge secret for the guardian to send back on the
@@ -91,7 +92,8 @@ Examples:
91
92
  options: [
92
93
  {
93
94
  flags: "--channel <channel>",
94
- description: "Channel type (telegram, phone). Defaults to telegram.",
95
+ description:
96
+ "Channel type (telegram, phone, slack, discord, email). Defaults to telegram.",
95
97
  },
96
98
  ],
97
99
  helpText: `
@@ -113,7 +115,7 @@ Examples:
113
115
  options: [
114
116
  {
115
117
  flags: "--channel <channel>",
116
- description: "Channel type (telegram, phone, slack, email)",
118
+ description: "Channel type (telegram, phone, slack, discord, email)",
117
119
  required: true,
118
120
  },
119
121
  {
@@ -137,7 +139,7 @@ Examples:
137
139
  options: [
138
140
  {
139
141
  flags: "--channel <channel>",
140
- description: "Channel type (telegram, phone, slack, email)",
142
+ description: "Channel type (telegram, phone, slack, discord, email)",
141
143
  required: true,
142
144
  },
143
145
  ],
@@ -1,3 +1,12 @@
1
+ // `cli/no-daemon-internals` forbids hoisting `channels/types.js`, so the CLI
2
+ // reads the vocabulary straight from the contract package that file re-exports.
3
+ // Both sides then validate `--channel` against the same set.
4
+
5
+ import {
6
+ CHANNEL_IDS,
7
+ type ChannelId,
8
+ isChannelId,
9
+ } from "@vellumai/service-contracts/channels";
1
10
  import type { Command } from "commander";
2
11
 
3
12
  import { cliIpcCall, exitFromIpcResult } from "../../ipc/cli-client.js";
@@ -6,25 +15,6 @@ import { registerCommand } from "../lib/register-command.js";
6
15
  import { writeOutput } from "../output.js";
7
16
  import { channelVerificationSessionsHelp } from "./channel-verification-sessions.help.js";
8
17
 
9
- // ---------------------------------------------------------------------------
10
- // Local channel validation (replaces daemon-internal channels/types.js import)
11
- // ---------------------------------------------------------------------------
12
-
13
- const VALID_CHANNEL_IDS = [
14
- "telegram",
15
- "phone",
16
- "vellum",
17
- "whatsapp",
18
- "slack",
19
- "email",
20
- "platform",
21
- ] as const;
22
- type ChannelId = (typeof VALID_CHANNEL_IDS)[number];
23
-
24
- function isChannelId(raw: string): raw is ChannelId {
25
- return (VALID_CHANNEL_IDS as readonly string[]).includes(raw);
26
- }
27
-
28
18
  /**
29
19
  * Validate the --channel option. Returns the validated ChannelId or writes an
30
20
  * error and returns `false`. When `required` is false an absent value is fine
@@ -49,7 +39,7 @@ function validateChannelOpt(
49
39
  if (required) {
50
40
  writeOutput(cmd, {
51
41
  ok: false,
52
- error: `The "channel" option is required. Valid values: ${VALID_CHANNEL_IDS.join(", ")}`,
42
+ error: `The "channel" option is required. Valid values: ${CHANNEL_IDS.join(", ")}`,
53
43
  });
54
44
  process.exitCode = 1;
55
45
  return false;
@@ -59,7 +49,7 @@ function validateChannelOpt(
59
49
  if (!isChannelId(raw)) {
60
50
  writeOutput(cmd, {
61
51
  ok: false,
62
- error: `Invalid channel "${raw}". Valid values: ${VALID_CHANNEL_IDS.join(", ")}`,
52
+ error: `Invalid channel "${raw}". Valid values: ${CHANNEL_IDS.join(", ")}`,
63
53
  });
64
54
  process.exitCode = 1;
65
55
  return false;
@@ -12,6 +12,7 @@ running.
12
12
 
13
13
  Examples:
14
14
  $ assistant conversations list
15
+ $ assistant conversations search "project planning"
15
16
  $ assistant conversations new "Project planning"
16
17
  $ assistant conversations export
17
18
  $ assistant conversations clear`,
@@ -168,6 +169,39 @@ include them.
168
169
  Examples:
169
170
  $ assistant conversations list
170
171
  $ assistant conversations list --include-archived`,
172
+ },
173
+ {
174
+ name: "search",
175
+ args: "<term>",
176
+ description: "Search conversations by title and message content",
177
+ options: [
178
+ {
179
+ flags: "--limit <n>",
180
+ description: "Maximum number of conversations to return (default 20)",
181
+ },
182
+ {
183
+ flags: "--json",
184
+ description: "Output result as machine-readable JSON",
185
+ },
186
+ ],
187
+ helpText: `
188
+ Arguments:
189
+ term Search query. Quote multi-word terms so the shell keeps them as one
190
+ argument. Matches conversation titles (substring) and message
191
+ content (full-text). Archived conversations are excluded, matching
192
+ 'assistant conversations list'.
193
+
194
+ Requires the assistant to be running. Communicates via IPC socket.
195
+
196
+ Results are grouped by conversation, ordered by most recently updated, and
197
+ capped at 20 conversations unless --limit is set. Each result includes the
198
+ conversation ID, title, relative timestamp, and up to three matching
199
+ message excerpts. Use the ID with rename, export, or wake.
200
+
201
+ Examples:
202
+ $ assistant conversations search "project planning"
203
+ $ assistant conversations search flux --limit 5
204
+ $ assistant conversations search "quarterly metrics" --json`,
171
205
  },
172
206
  {
173
207
  name: "new",
@@ -11,6 +11,7 @@ import { applyCommandHelp, subcommand } from "../lib/cli-command-help.js";
11
11
  import { registerCommand } from "../lib/register-command.js";
12
12
  import { timeAgo } from "../lib/time-ago.js";
13
13
  import { log } from "../logger.js";
14
+ import { shouldOutputJson, writeError, writeOutput } from "../output.js";
14
15
  import { tryResolveConversationId } from "../utils/conversation-id.js";
15
16
  import { conversationsHelp } from "./conversations.help.js";
16
17
  import { registerConversationsDeferCommand } from "./conversations-defer.js";
@@ -29,6 +30,66 @@ type SlackDetachCliResult = {
29
30
  conversationId?: string;
30
31
  };
31
32
 
33
+ type ConversationSearchMessage = {
34
+ messageId: string;
35
+ role: string;
36
+ excerpt: string;
37
+ createdAt: number;
38
+ };
39
+
40
+ type ConversationSearchResult = {
41
+ conversationId: string;
42
+ conversationTitle: string | null;
43
+ conversationUpdatedAt: number;
44
+ matchingMessages: ConversationSearchMessage[];
45
+ };
46
+
47
+ type ConversationSearchResponse = {
48
+ query: string;
49
+ results: ConversationSearchResult[];
50
+ };
51
+
52
+ function parsePositiveLimit(
53
+ raw: string | undefined,
54
+ ): { ok: true; value: number | undefined } | { ok: false; error: string } {
55
+ if (raw === undefined) {
56
+ return { ok: true, value: undefined };
57
+ }
58
+ const trimmed = raw.trim();
59
+ if (!/^\d+$/.test(trimmed)) {
60
+ return {
61
+ ok: false,
62
+ error:
63
+ "Invalid --limit value. Must be a positive integer, e.g. --limit 10.",
64
+ };
65
+ }
66
+ const value = Number(trimmed);
67
+ if (!Number.isSafeInteger(value) || value < 1) {
68
+ return {
69
+ ok: false,
70
+ error:
71
+ "Invalid --limit value. Must be a positive integer, e.g. --limit 10.",
72
+ };
73
+ }
74
+ return { ok: true, value };
75
+ }
76
+
77
+ function formatSearchResultLines(
78
+ results: ConversationSearchResult[],
79
+ ): string[] {
80
+ const lines: string[] = [];
81
+ for (const result of results) {
82
+ lines.push(
83
+ ` ${result.conversationId} ${result.conversationTitle ?? "Untitled"} ${timeAgo(result.conversationUpdatedAt)}`,
84
+ );
85
+ for (const message of result.matchingMessages) {
86
+ const excerpt = message.excerpt.replace(/\s+/g, " ").trim();
87
+ lines.push(` ${message.role} ${excerpt}`);
88
+ }
89
+ }
90
+ return lines;
91
+ }
92
+
32
93
  function outputSlackDetachError(message: string, json?: boolean): void {
33
94
  if (json) {
34
95
  process.stdout.write(JSON.stringify({ ok: false, error: message }) + "\n");
@@ -200,6 +261,70 @@ export function registerConversationsCommand(program: Command): void {
200
261
  },
201
262
  );
202
263
 
264
+ // -------------------------------------------------------------------
265
+ // search
266
+ // -------------------------------------------------------------------
267
+
268
+ subcommand(conversations, "search").action(
269
+ async (term: string, opts: { limit?: string }, cmd: Command) => {
270
+ const trimmed = term.trim();
271
+ if (!trimmed) {
272
+ writeError(
273
+ cmd,
274
+ 'Search term must be a non-empty string. Quote multi-word queries, e.g. assistant conversations search "project planning".',
275
+ );
276
+ process.exitCode = 1;
277
+ return;
278
+ }
279
+
280
+ const parsedLimit = parsePositiveLimit(opts.limit);
281
+ if (!parsedLimit.ok) {
282
+ writeError(cmd, parsedLimit.error);
283
+ process.exitCode = 1;
284
+ return;
285
+ }
286
+
287
+ const queryParams: Record<string, string> = { q: trimmed };
288
+ if (parsedLimit.value !== undefined) {
289
+ queryParams.limit = String(parsedLimit.value);
290
+ }
291
+
292
+ const result = await cliIpcCall<ConversationSearchResponse>(
293
+ "conversations_search",
294
+ { queryParams },
295
+ );
296
+
297
+ if (!result.ok) {
298
+ if (shouldOutputJson(cmd)) {
299
+ writeOutput(cmd, {
300
+ ok: false,
301
+ error: result.error ?? "Conversation search failed",
302
+ });
303
+ process.exitCode = exitCodeFromIpcResult(result);
304
+ return;
305
+ }
306
+ return exitFromIpcResult(result);
307
+ }
308
+
309
+ const payload = result.result!;
310
+ if (shouldOutputJson(cmd)) {
311
+ writeOutput(cmd, payload);
312
+ return;
313
+ }
314
+
315
+ if (payload.results.length === 0) {
316
+ process.stdout.write(
317
+ `No conversations matched "${payload.query}"\n`,
318
+ );
319
+ return;
320
+ }
321
+
322
+ process.stdout.write(
323
+ formatSearchResultLines(payload.results).join("\n") + "\n",
324
+ );
325
+ },
326
+ );
327
+
203
328
  // -------------------------------------------------------------------
204
329
  // new
205
330
  // -------------------------------------------------------------------
@@ -38,6 +38,7 @@ interface ProviderConnection {
38
38
  name: string;
39
39
  provider: string;
40
40
  auth: AuthInfo;
41
+ baseUrl?: string | null;
41
42
  createdAt: string;
42
43
  updatedAt: string;
43
44
  }
@@ -129,10 +130,11 @@ function collectRepeatable(value: string, previous: string[]): string[] {
129
130
  }
130
131
 
131
132
  /**
132
- * Build the openai-compatible custom-provider fields (`base_url`, `models`)
133
- * from CLI flags, forwarded to the connection route under its exact field
134
- * names. The daemon stays authoritative on whether they are required/allowed;
135
- * the CLI only shape-forwards what the user passed.
133
+ * Build the custom-provider fields (`base_url`, `models`) from CLI flags,
134
+ * forwarded to the connection route under its exact field names. The daemon
135
+ * stays authoritative on whether they are required or allowed (openai-compatible
136
+ * requires both; ollama accepts an optional base URL); the CLI only
137
+ * shape-forwards what the user passed.
136
138
  */
137
139
  function buildCustomProviderFields(opts: {
138
140
  baseUrl?: string;
@@ -219,6 +221,9 @@ function attachGetSubcommand(parent: Command): void {
219
221
  process.stdout.write(`name: ${conn.name}\n`);
220
222
  process.stdout.write(`provider: ${conn.provider}\n`);
221
223
  process.stdout.write(`auth: ${formatAuth(conn.auth)}\n`);
224
+ if (conn.baseUrl) {
225
+ process.stdout.write(`base_url: ${conn.baseUrl}\n`);
226
+ }
222
227
  process.stdout.write(
223
228
  `created: ${new Date(conn.createdAt).toISOString()}\n`,
224
229
  );
@@ -218,8 +218,12 @@ Examples:
218
218
  $ assistant inference providers create local-llm \\
219
219
  --provider openai-compatible \\
220
220
  --base-url http://localhost:1234/v1 --model my-model
221
+ $ assistant inference providers create ollama-home \\
222
+ --provider ollama --base-url http://192.168.1.50:11434/v1
221
223
  $ assistant inference providers update anthropic-personal \\
222
224
  --credential credential/anthropic/api_key
225
+ $ assistant inference providers update ollama \\
226
+ --base-url http://127.0.0.1:11434/v1
223
227
  $ assistant inference providers delete anthropic-personal
224
228
 
225
229
  After creating or updating a provider, validate it with a live call through
@@ -268,7 +272,7 @@ a profile that uses it:
268
272
  {
269
273
  flags: "--base-url <url>",
270
274
  description:
271
- "Endpoint base URL (required for --provider openai-compatible)",
275
+ "Endpoint base URL (required for openai-compatible; optional for ollama)",
272
276
  },
273
277
  ],
274
278
  },
@@ -290,7 +294,8 @@ a profile that uses it:
290
294
  },
291
295
  {
292
296
  flags: "--base-url <url>",
293
- description: "Endpoint base URL (openai-compatible providers)",
297
+ description:
298
+ "Endpoint base URL (openai-compatible or ollama)",
294
299
  },
295
300
  ],
296
301
  },
@@ -345,7 +350,7 @@ matching \`assistant inference providers <verb>\` command.`,
345
350
  {
346
351
  flags: "--base-url <url>",
347
352
  description:
348
- "Endpoint base URL (required for --provider openai-compatible)",
353
+ "Endpoint base URL (required for openai-compatible; optional for ollama)",
349
354
  },
350
355
  ],
351
356
  },
@@ -367,7 +372,7 @@ matching \`assistant inference providers <verb>\` command.`,
367
372
  {
368
373
  flags: "--base-url <url>",
369
374
  description:
370
- "Endpoint base URL (openai-compatible providers)",
375
+ "Endpoint base URL (openai-compatible or ollama)",
371
376
  },
372
377
  ],
373
378
  },
@@ -46,6 +46,13 @@ Examples:
46
46
  name: "delete",
47
47
  args: "<provider>",
48
48
  description: "Delete a stored API key",
49
+ options: [
50
+ {
51
+ flags: "--force",
52
+ description:
53
+ "Delete even when LLM provider connections resolve their auth through the key; those connections stop answering until a new key is stored",
54
+ },
55
+ ],
49
56
  helpText: `
50
57
  Arguments:
51
58
  provider Provider name whose key should be removed from secure storage
@@ -53,9 +60,14 @@ Arguments:
53
60
  Removes the API key for the given provider from secure local storage. If
54
61
  no key exists for the provider, exits with an error.
55
62
 
63
+ The delete is refused while an LLM provider connection resolves its auth
64
+ through the key; the error names the connections that depend on it. Run
65
+ 'assistant inference providers list' to inspect them, and pass --force to delete
66
+ anyway.
67
+
56
68
  Examples:
57
69
  $ assistant keys delete openai
58
- $ assistant keys delete anthropic`,
70
+ $ assistant keys delete anthropic --force`,
59
71
  },
60
72
  ],
61
73
  };
@@ -123,21 +123,32 @@ Examples:
123
123
  },
124
124
  );
125
125
 
126
- subcommand(keys, "delete").action(async (provider: string) => {
127
- const { deleteSecureKeyViaDaemon } =
128
- await import("../lib/daemon-credential-client.js");
129
- const delResult = await deleteSecureKeyViaDaemon("api_key", provider);
130
- if (delResult.result === "deleted") {
131
- log.info(`Deleted API key for "${provider}"`);
132
- } else if (delResult.result === "error") {
133
- const detail = delResult.error ? `: ${delResult.error}` : "";
134
- log.error(`Failed to delete API key for "${provider}"${detail}`);
135
- process.exit(1);
136
- } else {
137
- log.error(`No API key found for "${provider}"`);
138
- process.exit(1);
139
- }
140
- });
126
+ subcommand(keys, "delete").action(
127
+ async (provider: string, opts: { force?: boolean }) => {
128
+ const { deleteSecureKeyViaDaemon } =
129
+ await import("../lib/daemon-credential-client.js");
130
+ const delResult = await deleteSecureKeyViaDaemon(
131
+ "api_key",
132
+ provider,
133
+ opts.force === true,
134
+ );
135
+ if (delResult.result === "deleted") {
136
+ log.info(`Deleted API key for "${provider}"`);
137
+ } else if (delResult.result === "error") {
138
+ const detail = delResult.error ? `: ${delResult.error}` : "";
139
+ log.error(`Failed to delete API key for "${provider}"${detail}`);
140
+ if (delResult.code === "CREDENTIAL_IN_USE") {
141
+ log.error(
142
+ `Run 'assistant inference providers list' to inspect those connections, or 'assistant keys delete ${provider} --force' to delete the key anyway.`,
143
+ );
144
+ }
145
+ process.exit(1);
146
+ } else {
147
+ log.error(`No API key found for "${provider}"`);
148
+ process.exit(1);
149
+ }
150
+ },
151
+ );
141
152
  },
142
153
  });
143
154
  }