@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
@@ -76,6 +76,7 @@ import type { Conversation } from "../daemon/conversation.js";
76
76
  import { readSlackMetadata } from "../messaging/providers/slack/message-metadata.js";
77
77
  import { getDb } from "../persistence/db-connection.js";
78
78
  import { initializeDb } from "../persistence/db-init.js";
79
+ import { linkMessage, recordInbound } from "../persistence/delivery-crud.js";
79
80
  import { messages } from "../persistence/schema/conversations.js";
80
81
  import * as pendingInteractions from "../runtime/pending-interactions.js";
81
82
  import {
@@ -158,6 +159,29 @@ function buildReactionRequest(
158
159
  });
159
160
  }
160
161
 
162
+ /**
163
+ * Store the message a reaction will be attached to. A reaction resolves its
164
+ * conversation through this row, so without one there is nothing to annotate.
165
+ */
166
+ function seedStoredMessage(reactedTs: string): string {
167
+ const event = recordInbound(
168
+ "slack",
169
+ SLACK_CHANNEL_ID,
170
+ `${SLACK_CHANNEL_ID}:${reactedTs}:seed`,
171
+ { sourceMessageId: reactedTs },
172
+ );
173
+ const db = getDb();
174
+ const messageId = `msg-${reactedTs}`;
175
+ db.$client
176
+ .prepare(
177
+ `INSERT INTO messages (id, conversation_id, role, content, created_at)
178
+ VALUES (?, ?, 'user', '"hello"', ?)`,
179
+ )
180
+ .run(messageId, event.conversationId, Date.now());
181
+ linkMessage(event.eventId, messageId);
182
+ return event.conversationId;
183
+ }
184
+
161
185
  function readPersistedMessages(): Array<{
162
186
  role: string;
163
187
  content: string;
@@ -257,6 +281,7 @@ describe("Slack reaction event persistence", () => {
257
281
  });
258
282
 
259
283
  test("reaction:thumbsup is persisted with slackMeta.eventKind=reaction", async () => {
284
+ seedStoredMessage("1700000000.111111");
260
285
  let agentDispatched = false;
261
286
  const processMessage = async (): Promise<{ messageId: string }> => {
262
287
  agentDispatched = true;
@@ -276,7 +301,9 @@ describe("Slack reaction event persistence", () => {
276
301
 
277
302
  expect(agentDispatched).toBe(false);
278
303
 
279
- const rows = readPersistedMessages();
304
+ const rows = readPersistedMessages().filter(
305
+ (r) => r.content === "[reaction]",
306
+ );
280
307
  expect(rows.length).toBe(1);
281
308
 
282
309
  const row = rows[0];
@@ -293,7 +320,8 @@ describe("Slack reaction event persistence", () => {
293
320
  expect(slackMeta!.eventKind).toBe("reaction");
294
321
  expect(slackMeta!.channelId).toBe(SLACK_CHANNEL_ID);
295
322
  expect(slackMeta!.channelTs).toBe("1700000000.111111");
296
- expect(slackMeta!.threadTs).toBe("1700000000.111111");
323
+ // Slack sends no thread on a reaction, so the row claims none.
324
+ expect(slackMeta!.threadTs).toBeUndefined();
297
325
  expect(slackMeta!.displayName).toBe(SLACK_DISPLAY_NAME);
298
326
  expect(slackMeta!.reaction).toEqual({
299
327
  emoji: "thumbsup",
@@ -304,11 +332,14 @@ describe("Slack reaction event persistence", () => {
304
332
  });
305
333
 
306
334
  test("reaction_removed:eyes records op === removed", async () => {
335
+ seedStoredMessage("1700000000.111111");
307
336
  const req = buildReactionRequest("reaction_removed:eyes");
308
337
  const resp = await handleChannelInbound(req, undefined, TEST_BEARER_TOKEN);
309
338
  expect(resp.status).toBe(200);
310
339
 
311
- const rows = readPersistedMessages();
340
+ const rows = readPersistedMessages().filter(
341
+ (r) => r.content === "[reaction]",
342
+ );
312
343
  expect(rows.length).toBe(1);
313
344
 
314
345
  const envelope = JSON.parse(rows[0].metadata!) as Record<string, unknown>;
@@ -325,12 +356,35 @@ describe("Slack reaction event persistence", () => {
325
356
  });
326
357
  const resp = await handleChannelInbound(req, undefined, TEST_BEARER_TOKEN);
327
358
  expect(resp.status).toBe(200);
359
+ expect(((await resp.json()) as Record<string, unknown>).reaction).toBe(
360
+ "dropped_unknown_target",
361
+ );
328
362
 
329
363
  const rows = readPersistedMessages();
330
364
  expect(rows.length).toBe(0);
331
365
  });
332
366
 
367
+ test("a reaction never claims a thread, so it is not thread evidence", async () => {
368
+ // Storing the gateway's fabricated thread id here made the row look like
369
+ // proof that a thread belongs to this conversation.
370
+ seedStoredMessage("1700000000.111111");
371
+ await handleChannelInbound(
372
+ buildReactionRequest("reaction:thumbsup"),
373
+ undefined,
374
+ TEST_BEARER_TOKEN,
375
+ );
376
+ const rows = readPersistedMessages().filter(
377
+ (r) => r.content === "[reaction]",
378
+ );
379
+ const meta = readSlackMetadata(
380
+ (JSON.parse(rows[0].metadata!) as Record<string, unknown>)
381
+ .slackMeta as string,
382
+ );
383
+ expect(meta?.threadTs).toBeUndefined();
384
+ });
385
+
333
386
  test("reaction without threadId omits threadTs in metadata", async () => {
387
+ seedStoredMessage("1700000000.222222");
334
388
  const req = buildReactionRequest("reaction:wave", {
335
389
  sourceMetadata: {
336
390
  messageId: "1700000000.222222",
@@ -340,7 +394,9 @@ describe("Slack reaction event persistence", () => {
340
394
  const resp = await handleChannelInbound(req, undefined, TEST_BEARER_TOKEN);
341
395
  expect(resp.status).toBe(200);
342
396
 
343
- const rows = readPersistedMessages();
397
+ const rows = readPersistedMessages().filter(
398
+ (r) => r.content === "[reaction]",
399
+ );
344
400
  expect(rows.length).toBe(1);
345
401
 
346
402
  const envelope = JSON.parse(rows[0].metadata!) as Record<string, unknown>;
@@ -352,6 +408,7 @@ describe("Slack reaction event persistence", () => {
352
408
  });
353
409
 
354
410
  test("agent loop is never dispatched for reaction events", async () => {
411
+ seedStoredMessage("1700000000.111111");
355
412
  let dispatchCount = 0;
356
413
  const processMessage = async (): Promise<{ messageId: string }> => {
357
414
  dispatchCount++;
@@ -373,6 +430,7 @@ describe("Slack reaction event persistence", () => {
373
430
  });
374
431
 
375
432
  test("duplicate reaction events do not double-persist", async () => {
433
+ seedStoredMessage("1700000000.111111");
376
434
  const sharedExternalMessageId = `${SLACK_CHANNEL_ID}:1700000000.555555:alice`;
377
435
  const makeReq = () =>
378
436
  buildReactionRequest("reaction:tada", {
@@ -395,18 +453,150 @@ describe("Slack reaction event persistence", () => {
395
453
  const j2 = (await r2.json()) as Record<string, unknown>;
396
454
  expect(j2.duplicate).toBe(true);
397
455
 
398
- const rows = readPersistedMessages();
456
+ const rows = readPersistedMessages().filter(
457
+ (r) => r.content === "[reaction]",
458
+ );
399
459
  expect(rows.length).toBe(1);
400
460
  });
401
461
 
462
+ test("reaction on the assistant's own post lands in that conversation", async () => {
463
+ // An outbound post opens no inbound event, so the only record of its ts
464
+ // is the `slackMeta` on the assistant row. Seeded here the way the
465
+ // outbound reconciler writes it.
466
+ const botTs = "1700000000.999999";
467
+ const conversationId = seedStoredMessage("1700000000.111111");
468
+ const db = getDb();
469
+ db.$client
470
+ .prepare(
471
+ `INSERT INTO messages (id, conversation_id, role, content, created_at, metadata)
472
+ VALUES (?, ?, 'assistant', '"posted"', ?, ?)`,
473
+ )
474
+ .run(
475
+ "msg-bot-post",
476
+ conversationId,
477
+ Date.now(),
478
+ JSON.stringify({
479
+ slackMeta: JSON.stringify({
480
+ source: "slack",
481
+ channelId: SLACK_CHANNEL_ID,
482
+ channelTs: botTs,
483
+ eventKind: "message",
484
+ }),
485
+ }),
486
+ );
487
+
488
+ const resp = await handleChannelInbound(
489
+ buildReactionRequest("reaction:tada", {
490
+ externalMessageId: `${SLACK_CHANNEL_ID}:${botTs}:reactor`,
491
+ sourceMetadata: { messageId: botTs, chatType: "channel" },
492
+ }),
493
+ undefined,
494
+ TEST_BEARER_TOKEN,
495
+ );
496
+ expect(resp.status).toBe(200);
497
+
498
+ const reactionRow = db.$client
499
+ .prepare(
500
+ "SELECT conversation_id AS conversationId FROM messages WHERE content = '[reaction]'",
501
+ )
502
+ .get() as { conversationId: string } | null;
503
+ expect(reactionRow?.conversationId).toBe(conversationId);
504
+ });
505
+
506
+ test("a reaction leaves the reacted message resolvable by its own id", async () => {
507
+ // Two linked rows on one provider id would let a later edit or delete of
508
+ // the message resolve to the reaction instead.
509
+ const reactedTs = "1700000000.111111";
510
+ seedStoredMessage(reactedTs);
511
+ await handleChannelInbound(
512
+ buildReactionRequest("reaction:thumbsup"),
513
+ undefined,
514
+ TEST_BEARER_TOKEN,
515
+ );
516
+
517
+ const db = getDb();
518
+ const claimants = (
519
+ db.$client
520
+ .prepare(
521
+ `SELECT COUNT(*) AS n FROM channel_inbound_events
522
+ WHERE source_message_id = ? AND message_id IS NOT NULL`,
523
+ )
524
+ .get(reactedTs) as { n: number }
525
+ ).n;
526
+ expect(claimants).toBe(1);
527
+ });
528
+
529
+ test("reaction on a message the assistant never stored creates no conversation", async () => {
530
+ // The reported bug: Slack sends no `thread_ts` on a reaction, so keying a
531
+ // conversation off the reaction's own address minted one per reacted
532
+ // message, each holding a single "[reaction]" row and a permanent
533
+ // "Generating title..." placeholder.
534
+ const db = getDb();
535
+ const countRows = (table: string): number =>
536
+ (
537
+ db.$client.prepare(`SELECT COUNT(*) AS n FROM ${table}`).get() as {
538
+ n: number;
539
+ }
540
+ ).n;
541
+
542
+ const resp = await handleChannelInbound(
543
+ buildReactionRequest("reaction:thumbsup"),
544
+ undefined,
545
+ TEST_BEARER_TOKEN,
546
+ );
547
+ const json = (await resp.json()) as Record<string, unknown>;
548
+
549
+ expect(json.accepted).toBe(true);
550
+ expect(json.reaction).toBe("dropped_unknown_target");
551
+ expect(countRows("conversations")).toBe(0);
552
+ expect(countRows("channel_inbound_events")).toBe(0);
553
+ expect(readPersistedMessages().length).toBe(0);
554
+ });
555
+
556
+ test("reaction lands in the conversation of the reacted message, not a new one", async () => {
557
+ const targetConversationId = seedStoredMessage("1700000000.111111");
558
+ const db = getDb();
559
+ const conversationsBefore = (
560
+ db.$client.prepare("SELECT COUNT(*) AS n FROM conversations").get() as {
561
+ n: number;
562
+ }
563
+ ).n;
564
+
565
+ const resp = await handleChannelInbound(
566
+ buildReactionRequest("reaction:thumbsup"),
567
+ undefined,
568
+ TEST_BEARER_TOKEN,
569
+ );
570
+ expect(resp.status).toBe(200);
571
+
572
+ const conversationsAfter = (
573
+ db.$client.prepare("SELECT COUNT(*) AS n FROM conversations").get() as {
574
+ n: number;
575
+ }
576
+ ).n;
577
+ expect(conversationsAfter).toBe(conversationsBefore);
578
+
579
+ const reactionRow = db.$client
580
+ .prepare(
581
+ "SELECT conversation_id AS conversationId FROM messages WHERE content = '[reaction]'",
582
+ )
583
+ .get() as { conversationId: string } | null;
584
+ expect(reactionRow?.conversationId).toBe(targetConversationId);
585
+ });
586
+
402
587
  test("link to channel_inbound_events is created", async () => {
588
+ seedStoredMessage("1700000000.111111");
403
589
  const req = buildReactionRequest("reaction:thumbsup");
404
590
  const resp = await handleChannelInbound(req, undefined, TEST_BEARER_TOKEN);
405
591
  const json = (await resp.json()) as Record<string, unknown>;
406
592
  const eventId = json.eventId as string;
407
593
 
408
594
  const db = getDb();
409
- const messageRows = db.select().from(messages).all();
595
+ const messageRows = db
596
+ .select()
597
+ .from(messages)
598
+ .all()
599
+ .filter((row) => row.content === "[reaction]");
410
600
  expect(messageRows.length).toBe(1);
411
601
 
412
602
  const { channelInboundEvents } =
@@ -707,6 +897,7 @@ describe("reaction access control (no verification handshake)", () => {
707
897
  displayName: "Pending Contact",
708
898
  });
709
899
 
900
+ seedStoredMessage("1700000000.111111");
710
901
  const req = buildReactionRequest("reaction:tada", {
711
902
  actorExternalId: CONTACT_USER_ID,
712
903
  actorDisplayName: "Pending Contact",
@@ -718,7 +909,9 @@ describe("reaction access control (no verification handshake)", () => {
718
909
  expect(json.denied).not.toBe(true);
719
910
  expect(json.reason).not.toBe("verification_challenge_sent");
720
911
 
721
- const rows = readPersistedMessages();
912
+ const rows = readPersistedMessages().filter(
913
+ (r) => r.content === "[reaction]",
914
+ );
722
915
  expect(rows.length).toBe(1);
723
916
  const envelope = JSON.parse(rows[0].metadata!) as Record<string, unknown>;
724
917
  const slackMeta = readSlackMetadata(envelope.slackMeta as string);
@@ -94,12 +94,8 @@ mock.module("../permissions/checker.js", () => ({
94
94
  }
95
95
  return { decision: "allow", reason: "allowed" };
96
96
  },
97
- generateAllowlistOptions: () => [
98
- { label: "exact", description: "exact", pattern: "exact" },
99
- ],
100
97
  generateScopeOptions: () =>
101
98
  scopeOptionsOverride ?? [{ label: "/tmp", scope: "/tmp" }],
102
- getCachedAssessment: () => undefined,
103
99
  }));
104
100
 
105
101
  mock.module("../telemetry/tool-usage-store.js", () => ({