@vellumai/assistant 0.11.4 → 0.11.5-staging.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (367) 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__/config-loader-backfill.test.ts +19 -10
  36. package/src/__tests__/container-cpu-sampler.test.ts +153 -0
  37. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
  38. package/src/__tests__/conversation-attachments.test.ts +0 -1
  39. package/src/__tests__/conversation-error.test.ts +17 -0
  40. package/src/__tests__/conversation-lifecycle.test.ts +20 -26
  41. package/src/__tests__/conversation-pairing.test.ts +186 -0
  42. package/src/__tests__/conversation-runtime-assembly.test.ts +21 -2
  43. package/src/__tests__/credential-broker-server-use.test.ts +24 -5
  44. package/src/__tests__/credential-routes.test.ts +22 -3
  45. package/src/__tests__/credential-security-invariants.test.ts +2 -1
  46. package/src/__tests__/daemon-credential-client.test.ts +88 -0
  47. package/src/__tests__/default-plugin-names-guard.test.ts +12 -1
  48. package/src/__tests__/dm-backfill.test.ts +63 -0
  49. package/src/__tests__/dynamic-skill-background-guard.test.ts +0 -2
  50. package/src/__tests__/edit-propagation.test.ts +107 -4
  51. package/src/__tests__/events-client-registration.test.ts +28 -0
  52. package/src/__tests__/file-write-tool.test.ts +4 -2
  53. package/src/__tests__/guardian-prompt-notice-privacy.test.ts +133 -0
  54. package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +143 -97
  55. package/src/__tests__/helpers/gateway-classify-mock.ts +27 -6
  56. package/src/__tests__/host-proxy-interface.test.ts +11 -1
  57. package/src/__tests__/host-shell-tool.test.ts +23 -4
  58. package/src/__tests__/image-conversion.test.ts +143 -1
  59. package/src/__tests__/inline-skill-load-permissions.test.ts +47 -36
  60. package/src/__tests__/input-repairs.test.ts +207 -0
  61. package/src/__tests__/live-workspace-guard.test.ts +75 -0
  62. package/src/__tests__/managed-profile-guard.test.ts +4 -2
  63. package/src/__tests__/mcp-abort-signal.test.ts +1 -1
  64. package/src/__tests__/mcp-client-auth.test.ts +1 -1
  65. package/src/__tests__/mcp-tool-annotations-risk.test.ts +1 -1
  66. package/src/__tests__/media-resolve-image-validation.test.ts +310 -0
  67. package/src/__tests__/mtime-cache.test.ts +2 -0
  68. package/src/__tests__/notification-vellum-adapter.test.ts +124 -1
  69. package/src/__tests__/openai-provider.test.ts +22 -0
  70. package/src/__tests__/personal-memory-auth-bypass.test.ts +22 -6
  71. package/src/__tests__/platform-bash-auto-approve.test.ts +0 -4
  72. package/src/__tests__/platform.test.ts +16 -1
  73. package/src/__tests__/plugin-api-resolve-credential.test.ts +22 -13
  74. package/src/__tests__/plugin-api-shim.test.ts +5 -0
  75. package/src/__tests__/plugin-api-store-credential.test.ts +268 -0
  76. package/src/__tests__/plugin-config-data-migration.test.ts +8 -1
  77. package/src/__tests__/plugin-disabled-state.test.ts +2 -0
  78. package/src/__tests__/plugin-effective-enabled-set.test.ts +55 -3
  79. package/src/__tests__/plugin-execution-context.test.ts +73 -0
  80. package/src/__tests__/plugin-import-boundary-guard.test.ts +0 -1
  81. package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +0 -1
  82. package/src/__tests__/reaction-persistence.test.ts +200 -7
  83. package/src/__tests__/require-fresh-approval.test.ts +0 -4
  84. package/src/__tests__/resource-pressure-guard.test.ts +428 -0
  85. package/src/__tests__/resource-pressure-routes.test.ts +113 -0
  86. package/src/__tests__/risk-classification-boundary-guard.test.ts +115 -0
  87. package/src/__tests__/run-conversation-turn-persistence.test.ts +27 -1
  88. package/src/__tests__/secret-routes-platform-proxy.test.ts +7 -0
  89. package/src/__tests__/skill-tool-factory.test.ts +161 -0
  90. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
  91. package/src/__tests__/tool-executor-lifecycle-events.test.ts +132 -5
  92. package/src/__tests__/tool-executor.test.ts +63 -38
  93. package/src/__tests__/tool-policy.test.ts +97 -1
  94. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +7 -4
  95. package/src/__tests__/user-plugin-loader.test.ts +2 -0
  96. package/src/__tests__/validate-input.test.ts +243 -7
  97. package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
  98. package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -0
  99. package/src/__tests__/voice-session-bridge.test.ts +42 -0
  100. package/src/acp/__tests__/acp-claude-oauth.test.ts +97 -29
  101. package/src/acp/__tests__/prepare-agent-env.test.ts +376 -153
  102. package/src/acp/acp-claude-oauth.ts +38 -21
  103. package/src/acp/prepare-agent-env.ts +146 -55
  104. package/src/agent/loop-tool-dedup.test.ts +161 -0
  105. package/src/agent/loop.ts +68 -22
  106. package/src/api/constants/app-tools.ts +34 -0
  107. package/src/api/events/resource-pressure-status-changed.ts +53 -0
  108. package/src/api/index.ts +19 -0
  109. package/src/api/responses/resource-pressure-status.ts +25 -0
  110. package/src/approvals/guardian-channel-delivery.ts +70 -6
  111. package/src/approvals/guardian-request-resolvers.ts +32 -66
  112. package/src/calls/__tests__/voice-session-bridge.test.ts +114 -0
  113. package/src/calls/voice-session-bridge.ts +60 -6
  114. package/src/channels/__tests__/message-audience.test.ts +49 -0
  115. package/src/channels/__tests__/types.test.ts +17 -3
  116. package/src/channels/message-audience.ts +40 -0
  117. package/src/channels/types.ts +16 -12
  118. package/src/cli/__tests__/catalog-search-help.test.ts +50 -0
  119. package/src/cli/commands/__tests__/channel-verification-sessions.test.ts +31 -0
  120. package/src/cli/commands/__tests__/conversations-search.test.ts +289 -0
  121. package/src/cli/commands/__tests__/conversations-slack.test.ts +1 -0
  122. package/src/cli/commands/__tests__/inference-providers.test.ts +68 -0
  123. package/src/cli/commands/__tests__/keys.test.ts +89 -8
  124. package/src/cli/commands/__tests__/plugins.test.ts +57 -1
  125. package/src/cli/commands/channel-verification-sessions.help.ts +11 -9
  126. package/src/cli/commands/channel-verification-sessions.ts +11 -21
  127. package/src/cli/commands/conversations.help.ts +34 -0
  128. package/src/cli/commands/conversations.ts +125 -0
  129. package/src/cli/commands/inference-providers.ts +9 -4
  130. package/src/cli/commands/inference.help.ts +9 -4
  131. package/src/cli/commands/keys.help.ts +13 -1
  132. package/src/cli/commands/keys.ts +26 -15
  133. package/src/cli/commands/memory/__tests__/memory-v2.test.ts +57 -7
  134. package/src/cli/commands/memory/index.help.ts +41 -27
  135. package/src/cli/commands/memory/index.ts +2 -0
  136. package/src/cli/commands/memory/memory-v2.ts +58 -54
  137. package/src/cli/commands/memory/memory-validate.ts +18 -0
  138. package/src/cli/commands/monitoring.ts +1 -0
  139. package/src/cli/commands/plugins.help.ts +28 -31
  140. package/src/cli/commands/plugins.ts +10 -0
  141. package/src/cli/commands/skills.help.ts +13 -16
  142. package/src/cli/commands/trust.ts +3 -14
  143. package/src/cli/lib/__tests__/install-from-github.test.ts +38 -0
  144. package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +34 -0
  145. package/src/cli/lib/__tests__/plugin-surfaces.test.ts +32 -1
  146. package/src/cli/lib/__tests__/toggle-plugin.test.ts +2 -1
  147. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +64 -0
  148. package/src/cli/lib/bundled-marketplace.json +13 -0
  149. package/src/cli/lib/daemon-credential-client.ts +25 -3
  150. package/src/cli/lib/install-from-github.ts +35 -24
  151. package/src/cli/lib/merge-plugin-tree.ts +22 -4
  152. package/src/cli/lib/plugin-surfaces.ts +27 -0
  153. package/src/config/__tests__/balanced-model-experiment.test.ts +7 -7
  154. package/src/config/__tests__/default-profile-catalog.test.ts +3 -3
  155. package/src/config/default-profile-catalog.ts +5 -6
  156. package/src/config/feature-flag-registry.json +32 -32
  157. package/src/config/webhook-routing.ts +8 -0
  158. package/src/context/compactor.ts +31 -2
  159. package/src/daemon/__tests__/provider-rejection-log-fields.test.ts +272 -0
  160. package/src/daemon/conversation-agent-loop-handlers.ts +4 -1
  161. package/src/daemon/conversation-error.ts +13 -0
  162. package/src/daemon/conversation-messaging.ts +22 -2
  163. package/src/daemon/conversation-process.ts +11 -0
  164. package/src/daemon/conversation-store.ts +10 -0
  165. package/src/daemon/conversation-surfaces.ts +21 -8
  166. package/src/daemon/conversation.ts +12 -10
  167. package/src/daemon/lifecycle.ts +6 -2
  168. package/src/daemon/message-types/conversations.ts +5 -7
  169. package/src/daemon/provider-rejection-log-fields.ts +124 -0
  170. package/src/daemon/resource-pressure-guard-lifecycle.ts +66 -0
  171. package/src/daemon/resource-pressure-guard.ts +387 -0
  172. package/src/daemon/shutdown-handlers.ts +2 -0
  173. package/src/daemon/startup-error.ts +16 -0
  174. package/src/daemon/trust-context.ts +14 -10
  175. package/src/daemon/unsendable-image-notice.ts +76 -0
  176. package/src/hooks/registry.ts +65 -51
  177. package/src/ipc/__tests__/socket-path.test.ts +15 -7
  178. package/src/ipc/gateway-client.test.ts +1 -1
  179. package/src/ipc/gateway-client.ts +14 -21
  180. package/src/ipc/socket-cleanup.ts +2 -11
  181. package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +1 -1
  182. package/src/messaging/provider-message-metadata.ts +128 -0
  183. package/src/messaging/providers/__tests__/transport-dispatch.test.ts +116 -68
  184. package/src/messaging/providers/channel-transport.ts +90 -13
  185. package/src/messaging/providers/discord/send.ts +16 -0
  186. package/src/messaging/providers/discord/transport.ts +11 -1
  187. package/src/messaging/providers/index.ts +109 -16
  188. package/src/messaging/providers/slack/message-metadata.ts +45 -0
  189. package/src/messaging/providers/slack/render-transcript.ts +1 -4
  190. package/src/messaging/providers/slack/send.test.ts +6 -9
  191. package/src/messaging/providers/slack/send.ts +40 -28
  192. package/src/messaging/providers/slack/transport.ts +22 -26
  193. package/src/messaging/providers/telegram-bot/send.test.ts +2 -8
  194. package/src/messaging/providers/telegram-bot/transport.ts +3 -6
  195. package/src/messaging/read-provider-metadata.test.ts +157 -0
  196. package/src/messaging/read-provider-metadata.ts +51 -0
  197. package/src/notifications/__tests__/assistant-reply-producer.test.ts +94 -0
  198. package/src/notifications/__tests__/edit-notification.test.ts +174 -2
  199. package/src/notifications/__tests__/home-feed-side-effect.test.ts +536 -4
  200. package/src/notifications/adapters/macos.ts +55 -0
  201. package/src/notifications/adapters/slack.ts +10 -5
  202. package/src/notifications/assistant-reply-producer.ts +37 -4
  203. package/src/notifications/conversation-pairing.ts +111 -21
  204. package/src/notifications/edit-notification.ts +38 -8
  205. package/src/notifications/emit-signal.ts +12 -14
  206. package/src/notifications/home-feed-side-effect.ts +218 -15
  207. package/src/notifications/types.ts +5 -0
  208. package/src/permissions/AGENTS.md +16 -0
  209. package/src/permissions/checker.test.ts +75 -122
  210. package/src/permissions/checker.ts +44 -560
  211. package/src/permissions/confirmation-guardian-request.test.ts +28 -3
  212. package/src/permissions/confirmation-guardian-request.ts +5 -1
  213. package/src/persistence/conversation-crud.ts +23 -20
  214. package/src/persistence/conversation-queries.ts +14 -1
  215. package/src/persistence/conversation-types.ts +22 -0
  216. package/src/persistence/delivery-crud.ts +122 -15
  217. package/src/plugin-api/__tests__/import-graph-partial-mock.test.ts +67 -0
  218. package/src/plugin-api/conversation-turn.ts +27 -0
  219. package/src/plugin-api/credential-scope.test.ts +15 -0
  220. package/src/plugin-api/credential-scope.ts +14 -0
  221. package/src/plugin-api/index.ts +19 -1
  222. package/src/plugin-api/resolve-credential.ts +15 -10
  223. package/src/plugin-api/store-credential.ts +148 -0
  224. package/src/plugin-api/system-card.ts +36 -0
  225. package/src/plugin-api/vision-support.test.ts +82 -0
  226. package/src/plugin-api/vision-support.ts +14 -2
  227. package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +361 -110
  228. package/src/plugins/defaults/image-fallback/__tests__/vision-recovery.test.ts +4 -0
  229. package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +58 -0
  230. package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +64 -23
  231. package/src/plugins/defaults/image-recovery/detect.ts +24 -1
  232. package/src/plugins/defaults/main.ts +15 -0
  233. package/src/plugins/defaults/memory/AGENTS.md +11 -3
  234. package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +0 -1
  235. package/src/plugins/defaults/memory/graph-topology/pending-buffer.ts +9 -12
  236. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +1 -1
  237. package/src/plugins/defaults/memory/src/memory-v2-routes.ts +12 -10
  238. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +114 -0
  239. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-prompt-flag-gating-guard.test.ts +8 -0
  240. package/src/plugins/defaults/memory/substrate/__tests__/edge-index.test.ts +0 -30
  241. package/src/plugins/defaults/memory/substrate/__tests__/ingest.test.ts +73 -1
  242. package/src/plugins/defaults/memory/substrate/__tests__/page-index.test.ts +37 -0
  243. package/src/plugins/defaults/memory/substrate/__tests__/page-links.test.ts +208 -0
  244. package/src/plugins/defaults/memory/substrate/__tests__/prompts-consolidation.test.ts +158 -1
  245. package/src/plugins/defaults/memory/substrate/__tests__/static-context.test.ts +1 -61
  246. package/src/plugins/defaults/memory/substrate/consolidation-job.ts +68 -6
  247. package/src/plugins/defaults/memory/substrate/edge-index.ts +0 -31
  248. package/src/plugins/defaults/memory/substrate/ingest.ts +59 -0
  249. package/src/plugins/defaults/memory/substrate/page-index.ts +35 -8
  250. package/src/plugins/defaults/memory/substrate/page-links.ts +133 -0
  251. package/src/plugins/defaults/memory/substrate/page-store.ts +10 -0
  252. package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +124 -34
  253. package/src/plugins/defaults/memory/substrate/static-context.ts +0 -29
  254. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +3 -1
  255. package/src/plugins/defaults/memory/v3/card.ts +9 -9
  256. package/src/plugins/defaults/memory/v3/core-set.test.ts +7 -0
  257. package/src/plugins/defaults/memory/v3/core-set.ts +5 -1
  258. package/src/plugins/defaults/memory/v3/edge.ts +13 -57
  259. package/src/plugins/mtime-cache.ts +1 -1
  260. package/src/plugins/pipeline.ts +14 -7
  261. package/src/plugins/plugin-execution-context.ts +35 -11
  262. package/src/plugins/plugin-tree-walk.ts +63 -4
  263. package/src/providers/anthropic/__tests__/pause-turn-continuation.test.ts +170 -0
  264. package/src/providers/anthropic/client.ts +332 -241
  265. package/src/providers/connection-resolution.ts +2 -1
  266. package/src/providers/content-blocks.ts +22 -0
  267. package/src/providers/gemini/client.ts +5 -2
  268. package/src/providers/inference/__tests__/adapter-factory-litellm.test.ts +47 -1
  269. package/src/providers/inference/__tests__/adapter-factory-ollama.test.ts +83 -0
  270. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +98 -1
  271. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +38 -0
  272. package/src/providers/inference/__tests__/base-url-security.test.ts +10 -0
  273. package/src/providers/inference/__tests__/connections-ollama.test.ts +90 -0
  274. package/src/providers/inference/__tests__/missing-credential-guard.test.ts +117 -0
  275. package/src/providers/inference/adapter-factory.ts +33 -2
  276. package/src/providers/inference/auth.ts +13 -2
  277. package/src/providers/inference/credential-usage.ts +37 -0
  278. package/src/providers/inference/missing-credential-guard.ts +110 -0
  279. package/src/providers/inference/resolve-auth.ts +7 -7
  280. package/src/providers/media-resolve.ts +176 -15
  281. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +576 -46
  282. package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +125 -2
  283. package/src/providers/openai/chat-completions-provider.ts +322 -42
  284. package/src/routes/route-host-protocol.ts +7 -0
  285. package/src/routes/worker.ts +31 -10
  286. package/src/runtime/AGENTS.md +35 -0
  287. package/src/runtime/__tests__/runtime-http-port-collision.test.ts +131 -0
  288. package/src/runtime/__tests__/web-presence.test.ts +234 -0
  289. package/src/runtime/assistant-event-hub.ts +38 -0
  290. package/src/runtime/channel-reply-delivery.ts +54 -31
  291. package/src/runtime/channel-retry-sweep.ts +6 -4
  292. package/src/runtime/effective-capabilities.test.ts +19 -0
  293. package/src/runtime/effective-capabilities.ts +25 -0
  294. package/src/runtime/guardian-reply-router.ts +6 -2
  295. package/src/runtime/http-errors.ts +1 -0
  296. package/src/runtime/http-server.ts +20 -5
  297. package/src/runtime/routes/__tests__/client-routes.test.ts +150 -0
  298. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +133 -0
  299. package/src/runtime/routes/__tests__/credential-delete-in-use.test.ts +155 -0
  300. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +52 -1
  301. package/src/runtime/routes/__tests__/monitoring-routes.test.ts +1 -0
  302. package/src/runtime/routes/__tests__/pending-interactions-route.test.ts +175 -0
  303. package/src/runtime/routes/__tests__/plugins-routes.test.ts +88 -35
  304. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +55 -6
  305. package/src/runtime/routes/__tests__/system-card-turn-termination.test.ts +107 -0
  306. package/src/runtime/routes/__tests__/user-route-dispatcher-host.test.ts +52 -1
  307. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +75 -4
  308. package/src/runtime/routes/approval-routes.ts +37 -4
  309. package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +16 -22
  310. package/src/runtime/routes/canned-message-complete.ts +68 -29
  311. package/src/runtime/routes/client-routes.ts +113 -0
  312. package/src/runtime/routes/conversation-list-routes.ts +22 -7
  313. package/src/runtime/routes/conversation-management-routes.ts +15 -9
  314. package/src/runtime/routes/conversation-routes.ts +36 -22
  315. package/src/runtime/routes/credential-in-use.ts +85 -0
  316. package/src/runtime/routes/credential-routes.ts +55 -85
  317. package/src/runtime/routes/guardian-approval-interception.ts +22 -22
  318. package/src/runtime/routes/guardian-approval-reply-helpers.ts +14 -12
  319. package/src/runtime/routes/identity-routes.ts +5 -121
  320. package/src/runtime/routes/inbound-message-handler.ts +71 -27
  321. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +16 -17
  322. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +77 -50
  323. package/src/runtime/routes/inbound-stages/background-dispatch.ts +61 -97
  324. package/src/runtime/routes/inbound-stages/edit-intercept.ts +101 -77
  325. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +17 -8
  326. package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +91 -7
  327. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +72 -70
  328. package/src/runtime/routes/index.ts +2 -0
  329. package/src/runtime/routes/inference-provider-connection-routes.ts +9 -7
  330. package/src/runtime/routes/monitoring-routes.ts +9 -1
  331. package/src/runtime/routes/plugins-routes.ts +17 -5
  332. package/src/runtime/routes/resource-pressure-routes.ts +22 -0
  333. package/src/runtime/routes/secret-routes.ts +39 -8
  334. package/src/runtime/routes/settings-routes.ts +6 -6
  335. package/src/runtime/routes/surface-action-routes.ts +20 -19
  336. package/src/runtime/routes/user-route-dispatcher.ts +31 -2
  337. package/src/runtime/routes/user-route-resolution.ts +18 -1
  338. package/src/runtime/slack-reply-session.test.ts +23 -13
  339. package/src/runtime/slack-reply-session.ts +38 -55
  340. package/src/runtime/web-presence.ts +90 -0
  341. package/src/skills/validate-input.ts +177 -40
  342. package/src/subagent/__tests__/consult-context-gating.test.ts +6 -10
  343. package/src/subagent/consult-context.ts +7 -23
  344. package/src/tools/credentials/broker.ts +24 -79
  345. package/src/tools/credentials/ref-parse.ts +35 -0
  346. package/src/tools/credentials/resolve.ts +4 -10
  347. package/src/tools/credentials/store.ts +168 -0
  348. package/src/tools/credentials/tool-policy.ts +67 -0
  349. package/src/tools/executor.ts +53 -15
  350. package/src/tools/network/__tests__/web-search.test.ts +41 -1
  351. package/src/tools/network/url-safety.ts +9 -16
  352. package/src/tools/network/web-search.ts +21 -3
  353. package/src/tools/permission-checker.ts +43 -52
  354. package/src/tools/schema-transforms.ts +40 -5
  355. package/src/tools/shared/input-repairs.ts +160 -0
  356. package/src/tools/skills/skill-tool-factory.ts +18 -4
  357. package/src/tools/subagent/spawn.ts +0 -1
  358. package/src/tools/tool-approval-handler.ts +16 -10
  359. package/src/tools/tool-types.ts +26 -13
  360. package/src/tools/types.ts +6 -6
  361. package/src/util/__tests__/cgroup-memory.test.ts +3 -0
  362. package/src/util/cgroup-memory.ts +3 -0
  363. package/src/util/container-cpu-sampler.ts +250 -0
  364. package/src/util/image-conversion.ts +178 -14
  365. package/src/util/platform.ts +93 -10
  366. package/src/permissions/ipc-risk-types.ts +0 -143
  367. package/src/permissions/risk-types.ts +0 -76
@@ -9,18 +9,20 @@
9
9
  *
10
10
  * ## Plugin scoping
11
11
  *
12
- * When a plugin is in context — its hook or tool is executing, tracked by
13
- * {@link ../plugins/plugin-execution-context.getCurrentPluginName} — resolution
14
- * is restricted: the plugin may only resolve credentials whose `field` equals
15
- * the plugin's own manifest name. A plugin named `acme` can therefore read
16
- * `openai/acme` or `stripe/acme` but not `openai/api_key`. Outside any plugin
17
- * context (host-internal callers, CLI, tests) the resolver is unscoped and
18
- * behaves like a direct reveal.
12
+ * When a plugin is in context (its hook, tool, or one of its own
13
+ * `/x/plugins/<name>/` routes is executing, tracked by
14
+ * {@link ../plugins/plugin-execution-context.getCurrentPluginName}), resolution
15
+ * is restricted: the plugin may only resolve credentials whose `service`
16
+ * equals its manifest name (`imessage/api_key` for plugin `imessage`). It
17
+ * cannot read another service (`openai/api_key`, `openai/imessage`). Outside
18
+ * any plugin context (host-internal callers, CLI, tests) the resolver is
19
+ * unscoped and behaves like a direct reveal.
19
20
  */
20
21
 
21
22
  import { getCurrentPluginName } from "../plugins/plugin-execution-context.js";
22
23
  import { getSecureKeyResultAsync } from "../security/secure-keys.js";
23
24
  import { resolveCredentialRef } from "../tools/credentials/resolve.js";
25
+ import { credentialInPluginScope } from "./credential-scope.js";
24
26
 
25
27
  /**
26
28
  * Raised when a credential cannot be resolved: the reference does not match a
@@ -48,13 +50,16 @@ export async function resolveCredential(ref: string): Promise<string> {
48
50
  throw new CredentialResolutionError(`Credential not found: ${ref}`);
49
51
  }
50
52
 
51
- // Scope the resolution to the plugin in context, if any. The field-name gate
53
+ // Scope the resolution to the plugin in context, if any. The ownership gate
52
54
  // is enforced before the plaintext is read so an out-of-scope plugin never
53
55
  // touches the secure backend.
54
56
  const pluginName = getCurrentPluginName();
55
- if (pluginName !== undefined && resolved.field !== pluginName) {
57
+ if (
58
+ pluginName !== undefined &&
59
+ !credentialInPluginScope(pluginName, resolved.service)
60
+ ) {
56
61
  throw new CredentialResolutionError(
57
- `Plugin "${pluginName}" may only resolve credentials whose field matches its name; ` +
62
+ `Plugin "${pluginName}" may only resolve credentials under its own service; ` +
58
63
  `"${resolved.service}/${resolved.field}" is out of scope.`,
59
64
  );
60
65
  }
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Plugin-facing credential storage.
3
+ *
4
+ * {@link storeCredential} writes a credential's plaintext to the secure store,
5
+ * the same write `assistant credentials set` performs, naming it either by an
6
+ * existing credential's UUID or by a `"service/field"` string, the same
7
+ * vocabulary {@link ./resolve-credential.resolveCredential} reads back. A
8
+ * plugin that obtains a token for itself (an OAuth exchange it drives, a key a
9
+ * user hands it through its own route) persists it here instead of inventing
10
+ * its own on-disk secret file.
11
+ *
12
+ * ## Plugin scoping
13
+ *
14
+ * A plugin may only write credentials whose `service` equals its manifest
15
+ * name (`imessage/api_key` for plugin `imessage`). It cannot write another
16
+ * service (`openai/api_key`, `openai/imessage`). The name comes from the
17
+ * execution context
18
+ * ({@link ../plugins/plugin-execution-context.getCurrentPluginName}), which the
19
+ * host establishes around a plugin's hook, tool, and route invocations.
20
+ *
21
+ * Unlike the read path, this **fails closed**: with no plugin in context there
22
+ * is nobody to scope the write to, so it is refused rather than treated as an
23
+ * unscoped host write. A plugin's module body is evaluated by the loader
24
+ * outside any context, and an unscoped branch there would let top-level code
25
+ * overwrite the user's own credentials. Host-internal callers that legitimately
26
+ * write unscoped (the `credentials/set` route, the CLI behind it) compose
27
+ * {@link ../tools/credentials/store.storeCredentialValue} directly. This
28
+ * matches the plugin-owned index (`indexDocument`), which likewise requires a
29
+ * context because a write with no owner has nothing to attribute.
30
+ */
31
+
32
+ import { getCurrentPluginName } from "../plugins/plugin-execution-context.js";
33
+ import { parseServiceFieldRef } from "../tools/credentials/ref-parse.js";
34
+ import { resolveCredentialRef } from "../tools/credentials/resolve.js";
35
+ import { storeCredentialValue } from "../tools/credentials/store.js";
36
+ import { credentialInPluginScope } from "./credential-scope.js";
37
+
38
+ /**
39
+ * Raised when a credential cannot be stored: the reference is malformed, the
40
+ * value is empty or invalid for its service, the secure backend rejected the
41
+ * write, or the calling plugin is not permitted to write the credential.
42
+ */
43
+ export class CredentialStoreError extends Error {
44
+ constructor(message: string) {
45
+ super(message);
46
+ this.name = "CredentialStoreError";
47
+ }
48
+ }
49
+
50
+ export interface StoreCredentialOptions {
51
+ /** Human-friendly alias shown in credential listings. */
52
+ label?: string;
53
+ /** What the credential is for, surfaced to the agent. */
54
+ description?: string;
55
+ /**
56
+ * Skip the retroactive sweep that removes the value from recent transcripts.
57
+ * The sweep exists because a secret is often pasted into chat before it is
58
+ * stored; pass `true` only for a value that provably never transited a
59
+ * conversation, such as a token minted by an OAuth refresh.
60
+ */
61
+ skipTranscriptScrub?: boolean;
62
+ }
63
+
64
+ /** Identity of the credential that was written. */
65
+ export interface StoredCredentialRef {
66
+ credentialId: string;
67
+ service: string;
68
+ field: string;
69
+ }
70
+
71
+ /**
72
+ * Store a credential's plaintext value, creating it or replacing the value of
73
+ * an existing one.
74
+ *
75
+ * @param ref A credential UUID (an existing credential) or a `"service/field"`
76
+ * string (created when no such credential exists).
77
+ * @param value The plaintext value. Edge whitespace is trimmed.
78
+ * @returns The identity of the stored credential, which
79
+ * {@link ./resolve-credential.resolveCredential} accepts as a ref.
80
+ * @throws {CredentialStoreError} when no plugin is in context, the ref is
81
+ * malformed or names no credential, the value is empty or invalid for its
82
+ * service, the store rejects the write, or the calling plugin is not scoped
83
+ * to the credential.
84
+ */
85
+ export async function storeCredential(
86
+ ref: string,
87
+ value: string,
88
+ options: StoreCredentialOptions = {},
89
+ ): Promise<StoredCredentialRef> {
90
+ // Both gates run before anything is written, so a refused write never
91
+ // touches the secure backend. Fail closed when there is no plugin to scope
92
+ // to: a plugin's module body is evaluated outside any context, and an
93
+ // unscoped branch there would be a way to overwrite the user's credentials.
94
+ const pluginName = getCurrentPluginName();
95
+ if (pluginName === undefined) {
96
+ throw new CredentialStoreError(
97
+ "storeCredential requires an active plugin execution context (no calling plugin found). " +
98
+ "Call it from a hook, tool, or route handler rather than at module scope.",
99
+ );
100
+ }
101
+
102
+ const target = resolveStoreTarget(ref);
103
+ if (!credentialInPluginScope(pluginName, target.service)) {
104
+ throw new CredentialStoreError(
105
+ `Plugin "${pluginName}" may only store credentials under its own service; ` +
106
+ `"${target.service}/${target.field}" is out of scope.`,
107
+ );
108
+ }
109
+
110
+ try {
111
+ return await storeCredentialValue({
112
+ service: target.service,
113
+ field: target.field,
114
+ value,
115
+ alias: options.label,
116
+ usageDescription: options.description,
117
+ skipTranscriptScrub: options.skipTranscriptScrub,
118
+ });
119
+ } catch (err) {
120
+ throw new CredentialStoreError(
121
+ err instanceof Error ? err.message : String(err),
122
+ );
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Resolve the `ref` a write targets. An existing credential (by UUID or by
128
+ * `"service/field"`) keeps its identity; otherwise the ref must name the
129
+ * service and field of the credential to create, which a bare UUID cannot.
130
+ */
131
+ function resolveStoreTarget(ref: string): { service: string; field: string } {
132
+ if (!ref || ref.trim().length === 0) {
133
+ throw new CredentialStoreError("Credential reference is required");
134
+ }
135
+
136
+ const existing = resolveCredentialRef(ref);
137
+ if (existing) {
138
+ return { service: existing.service, field: existing.field };
139
+ }
140
+
141
+ const parsed = parseServiceFieldRef(ref);
142
+ if (!parsed) {
143
+ throw new CredentialStoreError(
144
+ `Cannot store credential: "${ref}" matches no stored credential and is not a "service/field" reference`,
145
+ );
146
+ }
147
+ return parsed;
148
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Plugin-facing facade over the host's system-card writer: a daemon-authored
3
+ * transcript notice, persisted as an assistant row stamped
4
+ * `messageKind: "system_card"` so clients render it as a standalone system
5
+ * notice rather than assistant-persona speech. Plugins use it to tell the user
6
+ * about something the turn did to their input that the model's own reply
7
+ * cannot explain (e.g. an attachment the turn could not send).
8
+ *
9
+ * The writer is loaded through a dynamic `import()` inside the wrapper, for the
10
+ * same reason `persistence/conversation-plugin-facade.ts` does: it carries the
11
+ * DB/drizzle import graph, whose named exports must not be forced to resolve
12
+ * merely because a plugin imported `@vellumai/plugin-api`. Suites that
13
+ * partial-mock a module in that graph would otherwise fail to instantiate.
14
+ */
15
+
16
+ /**
17
+ * Persist a system card in a conversation's transcript and announce it to
18
+ * connected clients through the sync invalidation that drives a refetch.
19
+ * Returns the persisted message id.
20
+ *
21
+ * A plugin card is non-terminal: it never emits `message_complete`, so a card
22
+ * written from a hook that runs inside a turn leaves that turn's streaming and
23
+ * processing state untouched. It is also not appended to the conversation's
24
+ * in-memory working history, so it cannot leave a trailing assistant message
25
+ * for the turn's next provider call to continue from.
26
+ */
27
+ export async function persistSystemCard(opts: {
28
+ conversationId: string;
29
+ text: string;
30
+ metadata: Record<string, unknown>;
31
+ }): Promise<string> {
32
+ const { persistSystemCard: persistHostSystemCard } =
33
+ await import("../runtime/routes/canned-message-complete.js");
34
+ const { id } = await persistHostSystemCard({ ...opts, endsTurn: false });
35
+ return id;
36
+ }
@@ -2,8 +2,14 @@ import { beforeEach, describe, expect, test } from "bun:test";
2
2
 
3
3
  import { setConfig } from "../__tests__/helpers/set-config.js";
4
4
  import { getConfig } from "../config/loader.js";
5
+ import { getDb } from "../persistence/db-connection.js";
6
+ import { initializeDb } from "../persistence/db-init.js";
7
+ import { providerConnections } from "../persistence/schema/inference.js";
5
8
  import type { ModelProfileInfo } from "./types.js";
6
9
 
10
+ // Entry-name translation reads provider_connections rows through the real DB.
11
+ await initializeDb();
12
+
7
13
  // ─── Fixture config ─────────────────────────────────────────────────────────
8
14
 
9
15
  interface MockProfileEntry {
@@ -211,6 +217,82 @@ describe("doesSupportVision with a BYO default provider", () => {
211
217
  });
212
218
  });
213
219
 
220
+ describe("doesSupportVision with connection entry-name providers", () => {
221
+ function seedConnection(opts: { name: string; provider: string }): void {
222
+ const now = Date.now();
223
+ getDb()
224
+ .insert(providerConnections)
225
+ .values({
226
+ name: opts.name,
227
+ provider: opts.provider,
228
+ auth: JSON.stringify({
229
+ type: "api_key",
230
+ credential: `${opts.provider}/api_key`,
231
+ }),
232
+ createdAt: now,
233
+ updatedAt: now,
234
+ })
235
+ .run();
236
+ }
237
+
238
+ beforeEach(() => {
239
+ getDb().delete(providerConnections).run();
240
+ });
241
+
242
+ test("judges an entry-name profile by its row's vendor (vision-capable)", () => {
243
+ seedConnection({ name: "openrouter-personal", provider: "openrouter" });
244
+ setMockConfig({
245
+ "personal-opus": {
246
+ provider: "openrouter-personal",
247
+ model: "anthropic/claude-opus-4.6",
248
+ },
249
+ });
250
+ expect(doesSupportVision(profile("personal-opus"))).toBe(true);
251
+ });
252
+
253
+ test("judges an entry-name profile by its row's vendor (text-only)", () => {
254
+ seedConnection({ name: "openrouter-personal", provider: "openrouter" });
255
+ setMockConfig({
256
+ "personal-deepseek": {
257
+ provider: "openrouter-personal",
258
+ model: "deepseek/deepseek-v4-pro",
259
+ },
260
+ });
261
+ expect(doesSupportVision(profile("personal-deepseek"))).toBe(false);
262
+ });
263
+
264
+ test("an entry name naming no row fails safe to false", () => {
265
+ setMockConfig({
266
+ dangling: {
267
+ provider: "ghost-connection",
268
+ model: "anthropic/claude-opus-4.6",
269
+ },
270
+ });
271
+ expect(doesSupportVision(profile("dangling"))).toBe(false);
272
+ });
273
+
274
+ test("mix arms with entry-name providers translate the same way", () => {
275
+ seedConnection({ name: "openrouter-personal", provider: "openrouter" });
276
+ setMockConfig({
277
+ "mix-profile": {
278
+ mix: [
279
+ { profile: "personal-deepseek", weight: 0.5 },
280
+ { profile: "personal-opus", weight: 0.5 },
281
+ ],
282
+ },
283
+ "personal-deepseek": {
284
+ provider: "openrouter-personal",
285
+ model: "deepseek/deepseek-v4-pro",
286
+ },
287
+ "personal-opus": {
288
+ provider: "openrouter-personal",
289
+ model: "anthropic/claude-opus-4.6",
290
+ },
291
+ });
292
+ expect(doesSupportVision(profile("mix-profile"))).toBe(true);
293
+ });
294
+ });
295
+
214
296
  describe("doesSupportVision with a bare string", () => {
215
297
  test("returns true for a known vision-capable model id", () => {
216
298
  expect(doesSupportVision("claude-opus-4-6")).toBe(true);
@@ -10,7 +10,9 @@
10
10
  * resolved through `llm.profiles` to an effective `(provider, model)`.
11
11
  * Default profile keys resolve through `llm.defaultProvider`'s column of the
12
12
  * intent × provider matrix, so the judged model is the one that actually
13
- * runs on a BYO install.
13
+ * runs on a BYO install. A profile whose provider names a connection entry
14
+ * row (e.g. a `<vendor>-personal` binding) is judged by the row's
15
+ * dispatchable vendor, sharing dispatch's entry-name translation.
14
16
  *
15
17
  * A bare string is tried as a model id first and then as a profile key, so the
16
18
  * two callers share one function. Resolution returns `false` when nothing
@@ -21,6 +23,7 @@
21
23
 
22
24
  import { resolveDefaultProfileForProvider } from "../config/default-profile-catalog.js";
23
25
  import { getConfig } from "../config/loader.js";
26
+ import { resolveEntryProviderKind } from "../providers/connection-resolution.js";
24
27
  import { ROUTING_IDENTITY_PROVIDERS } from "../providers/inference/auth.js";
25
28
  import {
26
29
  getCatalogProviderForModel,
@@ -117,12 +120,21 @@ function resolveEntryVision(entry: {
117
120
  }): boolean | undefined {
118
121
  // Routing identities ("vellum"/"chatgpt") are not catalog providers; the
119
122
  // model's catalog owner is the capability source for them.
120
- const provider =
123
+ const declared =
121
124
  entry.provider != null && ROUTING_IDENTITY_PROVIDERS.has(entry.provider)
122
125
  ? undefined
123
126
  : entry.provider;
124
127
  const model = entry.model;
125
128
 
129
+ // A provider naming a connection entry row (e.g. a "<vendor>-personal"
130
+ // binding) translates to the row's dispatchable kind, the same translation
131
+ // dispatch uses, so the catalog judges the vendor actually serving the
132
+ // profile. Vendor ids pass through untranslated.
133
+ const provider =
134
+ declared != null
135
+ ? (resolveEntryProviderKind(declared, model) ?? declared)
136
+ : undefined;
137
+
126
138
  // Infer provider from model when missing (mirrors the resolver's catalog
127
139
  // provider implication).
128
140
  const effectiveProvider =