@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
@@ -0,0 +1,268 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { existsSync, mkdirSync, rmSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import {
6
+ afterAll,
7
+ afterEach,
8
+ beforeEach,
9
+ describe,
10
+ expect,
11
+ spyOn,
12
+ test,
13
+ } from "bun:test";
14
+
15
+ import { credentialKey } from "@vellumai/credential-storage";
16
+
17
+ import * as scrub from "../daemon/credential-transcript-scrub.js";
18
+ import * as manualToken from "../oauth/manual-token-connection.js";
19
+ import { resolveCredential } from "../plugin-api/resolve-credential.js";
20
+ import {
21
+ CredentialStoreError,
22
+ storeCredential,
23
+ } from "../plugin-api/store-credential.js";
24
+ import { runInPluginContext } from "../plugins/plugin-execution-context.js";
25
+ import * as secureKeys from "../security/secure-keys.js";
26
+ import {
27
+ _setMetadataPath,
28
+ getCredentialMetadata,
29
+ } from "../tools/credentials/metadata-store.js";
30
+ import { storeCredentialValue } from "../tools/credentials/store.js";
31
+
32
+ // Real metadata store backed by a temp file. A mock.module on metadata-store
33
+ // or secure-keys would replace the whole module namespace, so only the secure
34
+ // backend access, the transcript scrub, and the manual-token connection sync
35
+ // are intercepted, via restorable spies.
36
+
37
+ const TEST_DIR = join(
38
+ tmpdir(),
39
+ `vellum-plugin-storecred-${randomBytes(4).toString("hex")}`,
40
+ );
41
+ const META_PATH = join(TEST_DIR, "metadata.json");
42
+
43
+ /** The plugin every scoped case runs as: it owns the `acme` service. */
44
+ const PLUGIN = "acme";
45
+
46
+ /** Run `fn` as the plugin would: inside its execution context. */
47
+ function asPlugin<T>(fn: () => T): T {
48
+ return runInPluginContext(PLUGIN, fn);
49
+ }
50
+
51
+ let secureStore: Map<string, string>;
52
+ let writeSucceeds: boolean;
53
+ let setSpy: ReturnType<typeof spyOn>;
54
+ let getSpy: ReturnType<typeof spyOn>;
55
+ let scrubSpy: ReturnType<typeof spyOn>;
56
+ let syncSpy: ReturnType<typeof spyOn>;
57
+
58
+ beforeEach(() => {
59
+ if (existsSync(TEST_DIR)) {
60
+ rmSync(TEST_DIR, { recursive: true });
61
+ }
62
+ mkdirSync(TEST_DIR, { recursive: true });
63
+ _setMetadataPath(META_PATH);
64
+
65
+ secureStore = new Map();
66
+ writeSucceeds = true;
67
+ setSpy = spyOn(secureKeys, "setSecureKeyAsync").mockImplementation(
68
+ async (key: string, value: string) => {
69
+ if (!writeSucceeds) {
70
+ return false;
71
+ }
72
+ secureStore.set(key, value);
73
+ return true;
74
+ },
75
+ );
76
+ getSpy = spyOn(secureKeys, "getSecureKeyResultAsync").mockImplementation(
77
+ async (key: string) => ({
78
+ value: secureStore.get(key),
79
+ unreachable: false,
80
+ }),
81
+ );
82
+ scrubSpy = spyOn(
83
+ scrub,
84
+ "scrubStoredCredentialFromTranscripts",
85
+ ).mockImplementation(async () => ({
86
+ dbMessagesScrubbed: 0,
87
+ residentMessagesScrubbed: 0,
88
+ }));
89
+ syncSpy = spyOn(manualToken, "syncManualTokenConnection").mockImplementation(
90
+ async () => {},
91
+ );
92
+ });
93
+
94
+ afterEach(() => {
95
+ setSpy.mockRestore();
96
+ getSpy.mockRestore();
97
+ scrubSpy.mockRestore();
98
+ syncSpy.mockRestore();
99
+ });
100
+
101
+ afterAll(() => {
102
+ _setMetadataPath(null);
103
+ if (existsSync(TEST_DIR)) {
104
+ rmSync(TEST_DIR, { recursive: true });
105
+ }
106
+ });
107
+
108
+ describe("storeCredential", () => {
109
+ test("creates a credential from a service/field ref", async () => {
110
+ const stored = await asPlugin(() =>
111
+ storeCredential("acme/api_key", "sk-secret"),
112
+ );
113
+
114
+ expect(stored.service).toBe("acme");
115
+ expect(stored.field).toBe("api_key");
116
+ expect(stored.credentialId).toBeTruthy();
117
+ expect(secureStore.get(credentialKey("acme", "api_key"))).toBe("sk-secret");
118
+ expect(getCredentialMetadata("acme", "api_key")?.credentialId).toBe(
119
+ stored.credentialId,
120
+ );
121
+ });
122
+
123
+ test("stores a value that resolveCredential reads back", async () => {
124
+ await asPlugin(() => storeCredential("acme/api_key", "sk-secret"));
125
+ await expect(
126
+ asPlugin(() => resolveCredential("acme/api_key")),
127
+ ).resolves.toBe("sk-secret");
128
+ });
129
+
130
+ test("trims edge whitespace from the value", async () => {
131
+ await asPlugin(() => storeCredential("acme/api_key", " sk-secret\n"));
132
+ expect(secureStore.get(credentialKey("acme", "api_key"))).toBe("sk-secret");
133
+ });
134
+
135
+ test("records label and description on the credential metadata", async () => {
136
+ await asPlugin(() =>
137
+ storeCredential("acme/api_key", "sk-secret", {
138
+ label: "Acme key",
139
+ description: "Used by the acme plugin",
140
+ }),
141
+ );
142
+
143
+ const metadata = getCredentialMetadata("acme", "api_key");
144
+ expect(metadata?.alias).toBe("Acme key");
145
+ expect(metadata?.usageDescription).toBe("Used by the acme plugin");
146
+ });
147
+
148
+ test("replaces the value of an existing credential named by UUID", async () => {
149
+ const first = await asPlugin(() =>
150
+ storeCredential("acme/api_key", "sk-old"),
151
+ );
152
+ const second = await asPlugin(() =>
153
+ storeCredential(first.credentialId, "sk-new"),
154
+ );
155
+
156
+ expect(second).toEqual(first);
157
+ expect(secureStore.get(credentialKey("acme", "api_key"))).toBe("sk-new");
158
+ });
159
+
160
+ test("rejects a ref that names no credential and is not service/field", async () => {
161
+ await expect(
162
+ asPlugin(() => storeCredential("openai", "sk-secret")),
163
+ ).rejects.toBeInstanceOf(CredentialStoreError);
164
+ expect(setSpy).not.toHaveBeenCalled();
165
+ });
166
+
167
+ test("rejects an empty value", async () => {
168
+ await expect(
169
+ asPlugin(() => storeCredential("acme/api_key", " ")),
170
+ ).rejects.toThrow(/value is required/);
171
+ expect(setSpy).not.toHaveBeenCalled();
172
+ });
173
+
174
+ test("surfaces a failed secure-backend write", async () => {
175
+ writeSucceeds = false;
176
+ await expect(
177
+ asPlugin(() => storeCredential("acme/api_key", "sk-secret")),
178
+ ).rejects.toThrow(CredentialStoreError);
179
+ expect(getCredentialMetadata("acme", "api_key")).toBeUndefined();
180
+ });
181
+
182
+ test("scrubs the stored value from recent transcripts", async () => {
183
+ await asPlugin(() => storeCredential("acme/api_key", "sk-secret"));
184
+ expect(scrubSpy).toHaveBeenCalledWith("sk-secret");
185
+ });
186
+
187
+ test("skips the transcript scrub when asked", async () => {
188
+ await asPlugin(() =>
189
+ storeCredential("acme/api_key", "sk-secret", {
190
+ skipTranscriptScrub: true,
191
+ }),
192
+ );
193
+ expect(scrubSpy).not.toHaveBeenCalled();
194
+ });
195
+
196
+ test("reconciles the manual-token connection for the service", async () => {
197
+ await asPlugin(() => storeCredential("acme/api_key", "sk-secret"));
198
+ expect(syncSpy).toHaveBeenCalledWith("acme");
199
+ });
200
+
201
+ describe("plugin scoping", () => {
202
+ test("fails closed with no plugin in context", async () => {
203
+ // A plugin's module body is evaluated outside any context. An unscoped
204
+ // branch there would let top-level code overwrite the user's own
205
+ // credentials, so the write is refused instead.
206
+ await expect(storeCredential("acme/api_key", "sk-secret")).rejects.toThrow(
207
+ /requires an active plugin execution context/,
208
+ );
209
+ expect(setSpy).not.toHaveBeenCalled();
210
+ expect(getCredentialMetadata("acme", "api_key")).toBeUndefined();
211
+ });
212
+
213
+ test("blocks a plugin from storing a credential it does not own", async () => {
214
+ await expect(
215
+ asPlugin(() => storeCredential("openai/api_key", "sk-secret")),
216
+ ).rejects.toThrow(/out of scope/);
217
+ });
218
+
219
+ test("does not write the secure backend when out of scope", async () => {
220
+ await expect(
221
+ asPlugin(() => storeCredential("openai/api_key", "sk-secret")),
222
+ ).rejects.toThrow(CredentialStoreError);
223
+ expect(setSpy).not.toHaveBeenCalled();
224
+ expect(getCredentialMetadata("openai", "api_key")).toBeUndefined();
225
+ });
226
+
227
+ test("blocks a plugin from overwriting another credential by UUID", async () => {
228
+ // Seeded through the host write path, the way the user's own credential
229
+ // is created (`credentials set`), not through the plugin API.
230
+ const existing = await storeCredentialValue({
231
+ service: "openai",
232
+ field: "api_key",
233
+ value: "sk-user",
234
+ skipTranscriptScrub: true,
235
+ });
236
+ setSpy.mockClear();
237
+
238
+ await expect(
239
+ asPlugin(() => storeCredential(existing.credentialId, "sk-hijacked")),
240
+ ).rejects.toThrow(/out of scope/);
241
+ expect(setSpy).not.toHaveBeenCalled();
242
+ expect(secureStore.get(credentialKey("openai", "api_key"))).toBe(
243
+ "sk-user",
244
+ );
245
+ });
246
+
247
+ test("blocks a plugin from storing another service even when the field matches its name", async () => {
248
+ await expect(
249
+ asPlugin(() => storeCredential("openai/acme", "sk-secret")),
250
+ ).rejects.toThrow(/out of scope/);
251
+ expect(setSpy).not.toHaveBeenCalled();
252
+ });
253
+
254
+ test("allows a plugin to store multiple fields under its own service", async () => {
255
+ await asPlugin(async () => {
256
+ await storeCredential("acme/api_key", "key-secret");
257
+ await storeCredential("acme/token", "token-secret");
258
+ });
259
+
260
+ expect(secureStore.get(credentialKey("acme", "api_key"))).toBe(
261
+ "key-secret",
262
+ );
263
+ expect(secureStore.get(credentialKey("acme", "token"))).toBe(
264
+ "token-secret",
265
+ );
266
+ });
267
+ });
268
+ });
@@ -32,7 +32,10 @@ import {
32
32
  populateCacheAtBoot,
33
33
  resetPluginCacheForTests,
34
34
  } from "../plugins/mtime-cache.js";
35
- import { PRESERVED_ENTRIES } from "../plugins/plugin-tree-walk.js";
35
+ import {
36
+ PRESERVED_ENTRIES,
37
+ USER_STATE_ENTRIES,
38
+ } from "../plugins/plugin-tree-walk.js";
36
39
 
37
40
  const ROOT = join(
38
41
  tmpdir(),
@@ -87,6 +90,10 @@ describe("PRESERVED_ENTRIES", () => {
87
90
  expect(PRESERVED_ENTRIES).toContain("data");
88
91
  expect(PRESERVED_ENTRIES).toContain(".disabled");
89
92
  });
93
+
94
+ test("user-state entries are the preserved set minus the provenance sidecar", () => {
95
+ expect([...USER_STATE_ENTRIES]).toEqual(["config.json", "data", ".disabled"]);
96
+ });
90
97
  });
91
98
 
92
99
  describe("fingerprint exclusion of preserved entries", () => {
@@ -22,6 +22,7 @@ import {
22
22
  getRegisteredInjectors,
23
23
  registerPluginInjectors,
24
24
  } from "../plugins/injector-registry.js";
25
+ import { resetPluginCacheForTests } from "../plugins/mtime-cache.js";
25
26
  import {
26
27
  registerPlugin,
27
28
  resetPluginRegistryForTests,
@@ -85,6 +86,7 @@ function makeFakeTool(name: string): Tool {
85
86
 
86
87
  beforeEach(() => {
87
88
  resetPluginRegistryForTests();
89
+ resetPluginCacheForTests();
88
90
  clearInjectorRegistry();
89
91
  });
90
92
 
@@ -8,14 +8,18 @@
8
8
  * non-null Set is an allowlist, `null` means no restriction. This suite locks
9
9
  * the contract that the hook gather sites honor it:
10
10
  * - `getHooksFor(name, { conversationId })` resolves the conversation's scope
11
- * and excludes in-process default-plugin hooks outside it; omitting the
11
+ * and excludes user-plugin hooks outside it. First-party default plugins
12
+ * always remain (they are core runtime, not per-chat toggles). Omitting the
12
13
  * conversationId imposes no restriction.
14
+ * - `getUserHooksFor` returns those same in-process default-plugin hooks
15
+ * (plus user-land hooks), not only filesystem plugins.
13
16
  * - `collectUserHookEntries(name, dirs, set)` excludes user-land plugin hooks
14
17
  * outside the set, while standalone workspace hooks (not owned by a plugin)
15
18
  * still run.
16
19
  *
17
20
  * The per-chat scope layers on top of (does not replace) the global
18
- * `.disabled` sentinel check — a plugin excluded by either is excluded.
21
+ * `.disabled` sentinel check. A user plugin excluded by either is excluded.
22
+ * A first-party default plugin is excluded only by `.disabled`.
19
23
  * Injectors are intentionally NOT per-chat scoped (see
20
24
  * `getRegisteredInjectors`): they run inside already-scoped plugin hooks, and
21
25
  * every injector-contributing plugin is a first-party default.
@@ -39,7 +43,8 @@ import {
39
43
  collectUserHookEntries,
40
44
  resetHookCacheForTests,
41
45
  } from "../hooks/hook-loader.js";
42
- import { getHooksFor } from "../hooks/registry.js";
46
+ import { getHooksFor, getUserHooksFor } from "../hooks/registry.js";
47
+ import { resetPluginCacheForTests } from "../plugins/mtime-cache.js";
43
48
  import {
44
49
  registerPlugin,
45
50
  resetPluginRegistryForTests,
@@ -66,6 +71,7 @@ function buildPlugin(
66
71
  describe("getHooksFor per-chat plugin scope (in-process default hooks)", () => {
67
72
  beforeEach(() => {
68
73
  resetPluginRegistryForTests();
74
+ resetPluginCacheForTests();
69
75
  resolveScopeMock.mockReset();
70
76
  resolveScopeMock.mockImplementation(() => null);
71
77
  });
@@ -136,6 +142,52 @@ describe("getHooksFor per-chat plugin scope (in-process default hooks)", () => {
136
142
  await getHooksFor("user-prompt-submit", { conversationId: "c1" }),
137
143
  ).toHaveLength(0);
138
144
  });
145
+
146
+ test("getUserHooksFor returns in-process default plugin hooks", async () => {
147
+ registerPlugin(
148
+ buildPlugin("default-memory", {
149
+ "user-prompt-submit": () => Promise.resolve(),
150
+ }),
151
+ );
152
+ expect(await getUserHooksFor("user-prompt-submit")).toHaveLength(1);
153
+ });
154
+
155
+ test("a first-party default plugin's hooks run even when the chat scope omits it", async () => {
156
+ let ran = 0;
157
+ registerPlugin(
158
+ buildPlugin("default-memory", {
159
+ "user-prompt-submit": () => {
160
+ ran++;
161
+ return Promise.resolve();
162
+ },
163
+ }),
164
+ );
165
+ resolveScopeMock.mockImplementation(() => new Set(["user-a"]));
166
+
167
+ const hooks = await getHooksFor("user-prompt-submit", {
168
+ conversationId: "c1",
169
+ });
170
+ expect(hooks).toHaveLength(1);
171
+ await hooks[0]!({});
172
+ expect(ran).toBe(1);
173
+ });
174
+
175
+ test("a workspace-disabled first-party default is still excluded", async () => {
176
+ registerPlugin(
177
+ buildPlugin("default-memory", {
178
+ "user-prompt-submit": () => Promise.resolve(),
179
+ }),
180
+ );
181
+ const dir = join(getWorkspacePluginsDir(), "default-memory");
182
+ mkdirSync(dir, { recursive: true });
183
+ writeFileSync(join(dir, ".disabled"), "");
184
+ try {
185
+ expect(await getHooksFor("user-prompt-submit")).toHaveLength(0);
186
+ expect(await getUserHooksFor("user-prompt-submit")).toHaveLength(0);
187
+ } finally {
188
+ rmSync(dir, { recursive: true, force: true });
189
+ }
190
+ });
139
191
  });
140
192
 
141
193
  describe("collectUserHookEntries per-chat plugin scope (user-land hooks)", () => {
@@ -0,0 +1,73 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import {
4
+ getCurrentPluginName,
5
+ runInPluginContext,
6
+ runOutsidePluginContext,
7
+ } from "../plugins/plugin-execution-context.js";
8
+
9
+ describe("plugin execution context", () => {
10
+ test("reports no plugin outside any context", () => {
11
+ expect(getCurrentPluginName()).toBeUndefined();
12
+ });
13
+
14
+ test("marks the plugin for the duration of the call", () => {
15
+ const seen = runInPluginContext("acme", () => getCurrentPluginName());
16
+ expect(seen).toBe("acme");
17
+ expect(getCurrentPluginName()).toBeUndefined();
18
+ });
19
+
20
+ test("carries across await boundaries", async () => {
21
+ const seen = await runInPluginContext("acme", async () => {
22
+ await Promise.resolve();
23
+ return getCurrentPluginName();
24
+ });
25
+ expect(seen).toBe("acme");
26
+ });
27
+
28
+ test("the innermost plugin wins when contexts nest", () => {
29
+ const seen = runInPluginContext("outer", () =>
30
+ runInPluginContext("inner", () => getCurrentPluginName()),
31
+ );
32
+ expect(seen).toBe("inner");
33
+ });
34
+
35
+ describe("runOutsidePluginContext", () => {
36
+ // Host code reached from a plugin (a default tool run during a turn a
37
+ // plugin route started) is not the plugin's code and must not borrow its
38
+ // identity, or a scoped API would attribute that work to the plugin.
39
+ test("clears a surrounding plugin context", () => {
40
+ const seen = runInPluginContext("acme", () =>
41
+ runOutsidePluginContext(() => getCurrentPluginName()),
42
+ );
43
+ expect(seen).toBeUndefined();
44
+ });
45
+
46
+ test("keeps the context cleared across await boundaries", async () => {
47
+ const seen = await runInPluginContext("acme", () =>
48
+ runOutsidePluginContext(async () => {
49
+ await Promise.resolve();
50
+ return getCurrentPluginName();
51
+ }),
52
+ );
53
+ expect(seen).toBeUndefined();
54
+ });
55
+
56
+ test("restores the plugin context for work after it", () => {
57
+ const seen = runInPluginContext("acme", () => {
58
+ runOutsidePluginContext(() => getCurrentPluginName());
59
+ return getCurrentPluginName();
60
+ });
61
+ expect(seen).toBe("acme");
62
+ });
63
+
64
+ test("a plugin context inside it scopes to the inner plugin", () => {
65
+ const seen = runInPluginContext("acme", () =>
66
+ runOutsidePluginContext(() =>
67
+ runInPluginContext("other", () => getCurrentPluginName()),
68
+ ),
69
+ );
70
+ expect(seen).toBe("other");
71
+ });
72
+ });
73
+ });
@@ -99,7 +99,6 @@ const BASELINE: Record<string, readonly string[]> = {
99
99
  "../../../../../security/untrusted-content.js",
100
100
  "../../../../api/events/memory-recalled.js",
101
101
  "../../../../api/responses/memory-v3-selection-log.js",
102
- "../../../../channels/types.js",
103
102
  "../../../../config/assistant-feature-flags.js",
104
103
  "../../../../config/default-profile-catalog.js",
105
104
  "../../../../config/loader.js",
@@ -99,7 +99,6 @@ const BASELINE: Record<string, readonly string[]> = {
99
99
  "src/daemon/handlers/skills.ts",
100
100
  "src/daemon/skill-memory-refresh.ts",
101
101
  "src/daemon/tool-side-effects.ts",
102
- "src/daemon/trust-context.ts",
103
102
  "src/home/feed-source-enrichment.ts",
104
103
  "src/permissions/checker.ts",
105
104
  "src/persistence/conversation-crud.ts",