@vellumai/assistant 0.10.12 → 0.11.0-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 (776) hide show
  1. package/AGENTS.md +1 -1
  2. package/ARCHITECTURE.md +4 -5
  3. package/README.md +1 -1
  4. package/docs/architecture/memory.md +60 -23
  5. package/docs/error-handling.md +3 -3
  6. package/docs/guardian-request-flow.md +112 -0
  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__/channels.test.ts +9 -1
  9. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +449 -0
  10. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +1 -0
  11. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/secret-detection.ts +468 -0
  12. package/node_modules/@vellumai/gateway-client/node_modules/@slack/types/dist/events/app.d.ts +28 -0
  13. package/node_modules/@vellumai/gateway-client/node_modules/@slack/types/dist/events/app.d.ts.map +1 -1
  14. package/node_modules/@vellumai/gateway-client/node_modules/@slack/types/dist/events/index.d.ts +2 -2
  15. package/node_modules/@vellumai/gateway-client/node_modules/@slack/types/dist/events/index.d.ts.map +1 -1
  16. package/node_modules/@vellumai/gateway-client/node_modules/@slack/types/dist/events/index.js.map +1 -1
  17. package/node_modules/@vellumai/gateway-client/node_modules/@slack/types/dist/events/message.d.ts +2 -0
  18. package/node_modules/@vellumai/gateway-client/node_modules/@slack/types/dist/events/message.d.ts.map +1 -1
  19. package/node_modules/@vellumai/gateway-client/node_modules/@slack/types/package.json +1 -1
  20. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  21. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +9 -1
  22. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +449 -0
  23. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +1 -0
  24. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/secret-detection.ts +468 -0
  25. package/node_modules/@vellumai/gateway-client/package.json +1 -1
  26. package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +6 -0
  27. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +34 -0
  28. package/node_modules/@vellumai/service-contracts/package.json +1 -0
  29. package/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +9 -1
  30. package/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +449 -0
  31. package/node_modules/@vellumai/service-contracts/src/channels.ts +1 -0
  32. package/node_modules/@vellumai/service-contracts/src/secret-detection.ts +468 -0
  33. package/openapi.yaml +246 -10
  34. package/package.json +2 -2
  35. package/src/__tests__/acp-session.test.ts +3 -3
  36. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +3 -0
  37. package/src/__tests__/anthropic-provider.test.ts +40 -0
  38. package/src/__tests__/approval-cascade.test.ts +5 -5
  39. package/src/__tests__/approval-routes-http.test.ts +3 -3
  40. package/src/__tests__/ask-question-resolver.test.ts +207 -0
  41. package/src/__tests__/assistant-event-hub-self-exclusion.test.ts +16 -16
  42. package/src/__tests__/assistant-event-hub-targeted.test.ts +13 -11
  43. package/src/__tests__/assistant-event-hub.test.ts +13 -11
  44. package/src/__tests__/assistant-event.test.ts +12 -9
  45. package/src/__tests__/assistant-feature-flag-guard.test.ts +3 -3
  46. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +1 -1
  47. package/src/__tests__/assistant-id-boundary-guard.test.ts +0 -58
  48. package/src/__tests__/assistant-stream-state.test.ts +17 -7
  49. package/src/__tests__/avatar-identity-sync.test.ts +3 -3
  50. package/src/__tests__/background-workers-disk-pressure.test.ts +1 -0
  51. package/src/__tests__/call-conversation-messages.test.ts +134 -18
  52. package/src/__tests__/call-start-guardian-guard.test.ts +27 -0
  53. package/src/__tests__/channel-approvals.test.ts +0 -27
  54. package/src/__tests__/channel-guardian.test.ts +85 -512
  55. package/src/__tests__/channel-readiness-slack-remote.test.ts +151 -3
  56. package/src/__tests__/channel-retry-sweep.test.ts +88 -1
  57. package/src/__tests__/channel-setup-panel-ack.test.ts +12 -9
  58. package/src/__tests__/chat-reveal-guard-priming.test.ts +31 -34
  59. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +2 -2
  60. package/src/__tests__/compaction-events.test.ts +8 -8
  61. package/src/__tests__/config-loader-platform-defaults.test.ts +54 -28
  62. package/src/__tests__/config-model-image-provider.test.ts +20 -0
  63. package/src/__tests__/config-schema-memory-substrate.test.ts +306 -0
  64. package/src/__tests__/config-schema.test.ts +14 -2
  65. package/src/__tests__/config-set-platform-guard.test.ts +11 -12
  66. package/src/__tests__/config-sounds-sync.test.ts +4 -4
  67. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +5 -0
  68. package/src/__tests__/context-search-memory-source.test.ts +1 -1
  69. package/src/__tests__/context-search-memory-v2-source.test.ts +6 -6
  70. package/src/__tests__/context-token-estimator.test.ts +54 -1
  71. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +12 -3
  72. package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +13 -4
  73. package/src/__tests__/conversation-agent-loop-handlers-max-tokens.test.ts +4 -4
  74. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +10 -1
  75. package/src/__tests__/conversation-agent-loop-overflow.test.ts +22 -13
  76. package/src/__tests__/conversation-agent-loop.test.ts +39 -30
  77. package/src/__tests__/conversation-confirmation-signals.test.ts +14 -14
  78. package/src/__tests__/conversation-evictor.test.ts +49 -0
  79. package/src/__tests__/conversation-fork-crud.test.ts +1 -1
  80. package/src/__tests__/conversation-history-web-search.test.ts +1 -1
  81. package/src/__tests__/conversation-key-store-disk-view.test.ts +5 -5
  82. package/src/__tests__/conversation-loop-db-lock-resilience.test.ts +5 -5
  83. package/src/__tests__/conversation-message-sync-tags.test.ts +4 -4
  84. package/src/__tests__/conversation-queue.test.ts +81 -81
  85. package/src/__tests__/conversation-routes-enabled-plugins.test.ts +8 -1
  86. package/src/__tests__/conversation-routes-hidden-queue.test.ts +8 -1
  87. package/src/__tests__/conversation-routes-slash-commands.test.ts +10 -1
  88. package/src/__tests__/conversation-runtime-assembly.test.ts +29 -2
  89. package/src/__tests__/conversation-slash-queue.test.ts +11 -11
  90. package/src/__tests__/conversation-slash-unknown.test.ts +2 -2
  91. package/src/__tests__/conversation-speed-override.test.ts +2 -2
  92. package/src/__tests__/conversation-summarize-route.test.ts +9 -0
  93. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +18 -16
  94. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +17 -17
  95. package/src/__tests__/conversation-surfaces-app-open.test.ts +7 -4
  96. package/src/__tests__/conversation-surfaces-data-persist.test.ts +7 -7
  97. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +9 -9
  98. package/src/__tests__/conversation-surfaces-standalone.test.ts +12 -12
  99. package/src/__tests__/conversation-surfaces-state-update.test.ts +8 -5
  100. package/src/__tests__/conversation-surfaces-table-action.test.ts +6 -3
  101. package/src/__tests__/conversation-surfaces-task-progress.test.ts +46 -46
  102. package/src/__tests__/conversation-sync-tags.test.ts +5 -5
  103. package/src/__tests__/conversation-wait-for-idle.test.ts +2 -2
  104. package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -4
  105. package/src/__tests__/conversation-workspace-injection.test.ts +0 -3
  106. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -3
  107. package/src/__tests__/copy-composer-tc-templates.test.ts +3 -131
  108. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  109. package/src/__tests__/daemon-assistant-events.test.ts +14 -15
  110. package/src/__tests__/db-test-helpers.ts +13 -27
  111. package/src/__tests__/disk-pressure-routes.test.ts +12 -3
  112. package/src/__tests__/document-tool-security.test.ts +95 -0
  113. package/src/__tests__/drain-kick-guard.test.ts +232 -0
  114. package/src/__tests__/dynamic-skill-background-guard.test.ts +19 -6
  115. package/src/__tests__/emit-event-signal.test.ts +5 -6
  116. package/src/__tests__/encrypted-store-test-helpers.ts +10 -20
  117. package/src/__tests__/events-tail-route.test.ts +6 -4
  118. package/src/__tests__/feature-flag-test-helpers.ts +10 -20
  119. package/src/__tests__/file-edit-tool.test.ts +24 -4
  120. package/src/__tests__/file-list-tool.test.ts +2 -1
  121. package/src/__tests__/file-read-tool.test.ts +62 -10
  122. package/src/__tests__/file-write-tool.test.ts +24 -5
  123. package/src/__tests__/followup-tools.test.ts +58 -0
  124. package/src/__tests__/gateway-threshold-reader-mock.ts +99 -0
  125. package/src/__tests__/graph-extraction-event-date.test.ts +1 -1
  126. package/src/__tests__/guardian-question-mode.test.ts +42 -3
  127. package/src/__tests__/guardian-reply-router-answer-mode.test.ts +283 -0
  128. package/src/__tests__/guardian-vellum-destination-pinning.test.ts +221 -0
  129. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  130. package/src/__tests__/heartbeat-service.test.ts +1 -0
  131. package/src/__tests__/helpers/native-web-search-harness.ts +10 -10
  132. package/src/__tests__/host-browser-proxy.test.ts +1 -1
  133. package/src/__tests__/host-file-edit-tool.test.ts +3 -3
  134. package/src/__tests__/host-file-read-tool.test.ts +2 -2
  135. package/src/__tests__/host-file-write-tool.test.ts +7 -3
  136. package/src/__tests__/host-shell-tool.test.ts +17 -5
  137. package/src/__tests__/host-transfer-pending-interactions.test.ts +8 -8
  138. package/src/__tests__/image-credentials.test.ts +65 -8
  139. package/src/__tests__/injection-block.test.ts +4 -4
  140. package/src/__tests__/injector-chain.test.ts +1 -1
  141. package/src/__tests__/injector-pkb-v2-silenced.test.ts +2 -2
  142. package/src/__tests__/injector-registry-order-guard.test.ts +4 -9
  143. package/src/__tests__/injector-v3-suppression.test.ts +6 -4
  144. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +27 -28
  145. package/src/__tests__/list-messages-page-latest.test.ts +2 -2
  146. package/src/__tests__/llm-usage-store.test.ts +67 -4
  147. package/src/__tests__/log-redact.test.ts +18 -0
  148. package/src/__tests__/managed-profile-guard.test.ts +27 -0
  149. package/src/__tests__/managed-skill-lifecycle.test.ts +2 -2
  150. package/src/__tests__/media-generate-image.test.ts +62 -11
  151. package/src/__tests__/memory-identity-context-parity.test.ts +1 -1
  152. package/src/__tests__/memory-jobs-worker-lanes.test.ts +9 -1
  153. package/src/__tests__/memory-retrieval-hook.test.ts +4 -4
  154. package/src/__tests__/notification-decision-strategy.test.ts +115 -11
  155. package/src/__tests__/notification-deep-link.test.ts +12 -12
  156. package/src/__tests__/notification-vellum-adapter.test.ts +8 -8
  157. package/src/__tests__/outbound-slack-persistence.test.ts +8 -8
  158. package/src/__tests__/path-policy.test.ts +315 -0
  159. package/src/__tests__/pending-interactions-resolved-event.test.ts +6 -6
  160. package/src/__tests__/persistence-layering-guard.test.ts +2 -2
  161. package/src/__tests__/pkb-autoinject.test.ts +1 -1
  162. package/src/__tests__/platform-bash-auto-approve.test.ts +1 -0
  163. package/src/__tests__/plugin-event-hub-facade.test.ts +22 -15
  164. package/src/__tests__/plugin-import-boundary-guard.test.ts +15 -15
  165. package/src/__tests__/processing-flag-persist-failure.test.ts +0 -4
  166. package/src/__tests__/provider-platform-proxy-integration.test.ts +0 -1
  167. package/src/__tests__/rebuild-index-graph-nodes.test.ts +1 -1
  168. package/src/__tests__/request-secret-standalone-channel-gate.test.ts +5 -5
  169. package/src/__tests__/require-fresh-approval.test.ts +38 -38
  170. package/src/__tests__/run-due-schedules.test.ts +72 -1
  171. package/src/__tests__/runtime-events-sse-parity.test.ts +11 -10
  172. package/src/__tests__/runtime-events-sse.test.ts +1 -1
  173. package/src/__tests__/schedule-routes.test.ts +44 -5
  174. package/src/__tests__/schedule-store.test.ts +3 -3
  175. package/src/__tests__/schedule-tools.test.ts +176 -0
  176. package/src/__tests__/secret-prompt-log-hygiene.test.ts +3 -3
  177. package/src/__tests__/secret-prompter-channel-fallback.test.ts +3 -3
  178. package/src/__tests__/secret-response-routing.test.ts +3 -3
  179. package/src/__tests__/secret-routes-platform-proxy.test.ts +4 -7
  180. package/src/__tests__/secret-scanner.test.ts +36 -0
  181. package/src/__tests__/send-endpoint-busy.test.ts +7 -7
  182. package/src/__tests__/slack-required-scopes-mirror.test.ts +81 -0
  183. package/src/__tests__/starter-task-flow.test.ts +6 -3
  184. package/src/__tests__/subagent-call-site-routing.test.ts +2 -2
  185. package/src/__tests__/subagent-disposal.test.ts +40 -11
  186. package/src/__tests__/subagent-fork-notifications.test.ts +4 -4
  187. package/src/__tests__/subagent-fork-prompt-role.test.ts +3 -3
  188. package/src/__tests__/subagent-fork-spawn.test.ts +4 -4
  189. package/src/__tests__/subagent-manager-notify.test.ts +73 -14
  190. package/src/__tests__/subagent-notify-parent.test.ts +48 -4
  191. package/src/__tests__/subagent-spawn-and-await.test.ts +62 -14
  192. package/src/__tests__/subagent-tools.test.ts +64 -0
  193. package/src/__tests__/sync-message-contract.test.ts +3 -3
  194. package/src/__tests__/terminal-tools.test.ts +12 -3
  195. package/src/__tests__/test-preload.ts +9 -0
  196. package/src/__tests__/tool-approval-handler.test.ts +583 -43
  197. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +7 -4
  198. package/src/__tests__/tool-executor.test.ts +116 -0
  199. package/src/__tests__/tool-grant-request-escalation.test.ts +5 -0
  200. package/src/__tests__/tool-preview-lifecycle.test.ts +32 -29
  201. package/src/__tests__/tool-result-metadata-plumbing.test.ts +5 -5
  202. package/src/__tests__/tool-start-timestamp.test.ts +4 -4
  203. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +9 -22
  204. package/src/__tests__/ui-choice-copy-surfaces.test.ts +13 -13
  205. package/src/__tests__/ui-work-result-surface.test.ts +3 -3
  206. package/src/__tests__/voice-scoped-grant-consumer.test.ts +6 -6
  207. package/src/__tests__/voice-session-bridge.test.ts +44 -44
  208. package/src/__tests__/workspace-migration-134-image-generation-mode-to-provider.test.ts +284 -0
  209. package/src/__tests__/workspace-policy.test.ts +399 -0
  210. package/src/acp/__tests__/client-handler.test.ts +3 -3
  211. package/src/acp/__tests__/session-manager-persistence.test.ts +10 -10
  212. package/src/acp/__tests__/session-manager-resume.test.ts +7 -7
  213. package/src/acp/client-handler.ts +3 -3
  214. package/src/acp/session-manager.ts +8 -8
  215. package/src/api/events/subagent-event.ts +2 -2
  216. package/src/api/responses/memory-v2-activation-log.ts +2 -2
  217. package/src/approvals/AGENTS.md +2 -0
  218. package/src/approvals/guardian-request-resolvers.ts +118 -24
  219. package/src/calls/__tests__/voice-control-protocol.test.ts +57 -0
  220. package/src/calls/__tests__/voice-session-bridge.test.ts +158 -4
  221. package/src/calls/__tests__/voice-triage-escalate.test.ts +3 -4
  222. package/src/calls/call-controller.ts +27 -27
  223. package/src/calls/call-conversation-messages.ts +58 -18
  224. package/src/calls/voice-control-protocol.ts +56 -0
  225. package/src/calls/voice-session-bridge.ts +156 -58
  226. package/src/calls/voice-triage-escalate.ts +2 -3
  227. package/src/channels/config.ts +8 -0
  228. package/src/channels/types.ts +1 -0
  229. package/src/cli/commands/config.ts +49 -13
  230. package/src/cli/commands/memory/index.help.ts +4 -2
  231. package/src/cli/commands/memory/memory-retrospective.ts +5 -0
  232. package/src/cli/lib/bundled-marketplace.json +12 -0
  233. package/src/cli.ts +2 -109
  234. package/src/config/__tests__/deployment-context-defaults.test.ts +9 -0
  235. package/src/config/__tests__/substrate-twin-shadowing.test.ts +191 -0
  236. package/src/config/assistant-feature-flags.ts +1 -1
  237. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +13 -7
  238. package/src/config/feature-flag-cache.ts +6 -16
  239. package/src/config/feature-flag-registry.json +14 -30
  240. package/src/config/loader.ts +7 -1
  241. package/src/config/memory-tier.ts +15 -10
  242. package/src/config/memory-v3-gate.test.ts +278 -1
  243. package/src/config/memory-v3-gate.ts +88 -26
  244. package/src/config/raw-config-utils.ts +1 -1
  245. package/src/config/schemas/__tests__/live-voice.test.ts +27 -16
  246. package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
  247. package/src/config/schemas/live-voice.ts +33 -7
  248. package/src/config/schemas/llm.ts +1 -0
  249. package/src/config/schemas/memory-retrospective.ts +13 -0
  250. package/src/config/schemas/memory-substrate.ts +183 -0
  251. package/src/config/schemas/memory-v2.ts +3 -2
  252. package/src/config/schemas/memory-v3.ts +11 -4
  253. package/src/config/schemas/memory.ts +35 -1
  254. package/src/config/schemas/services.ts +10 -2
  255. package/src/config/substrate-twin-shadowing.ts +195 -0
  256. package/src/config/webhook-routing.ts +67 -0
  257. package/src/context/compactor.ts +10 -3
  258. package/src/context/token-estimator.ts +21 -0
  259. package/src/daemon/__tests__/channel-ui-capability.test.ts +85 -0
  260. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +3 -3
  261. package/src/daemon/__tests__/embedding-reconcile.test.ts +3 -3
  262. package/src/daemon/__tests__/inference-profile-notification.test.ts +3 -6
  263. package/src/daemon/__tests__/native-web-search-metadata.test.ts +5 -5
  264. package/src/daemon/__tests__/web-search-status-text.test.ts +2 -2
  265. package/src/daemon/channel-ui-capability.ts +70 -1
  266. package/src/daemon/conversation-agent-loop-handlers.ts +4 -4
  267. package/src/daemon/conversation-agent-loop.ts +12 -5
  268. package/src/daemon/conversation-evictor.ts +26 -4
  269. package/src/daemon/conversation-launch.ts +11 -14
  270. package/src/daemon/conversation-messaging.ts +14 -14
  271. package/src/daemon/conversation-notifiers.ts +2 -2
  272. package/src/daemon/conversation-process.ts +133 -21
  273. package/src/daemon/conversation-queue-manager.ts +7 -7
  274. package/src/daemon/conversation-runtime-assembly.ts +16 -0
  275. package/src/daemon/conversation-store.ts +11 -3
  276. package/src/daemon/conversation-surfaces.ts +4 -4
  277. package/src/daemon/conversation-tool-setup.ts +13 -7
  278. package/src/daemon/conversation-usage.ts +12 -11
  279. package/src/daemon/conversation.ts +38 -15
  280. package/src/daemon/disk-pressure-guard.ts +5 -12
  281. package/src/daemon/embedding-reconcile.ts +14 -9
  282. package/src/daemon/handlers/config-channels.ts +40 -134
  283. package/src/daemon/handlers/config-model.ts +15 -7
  284. package/src/daemon/handlers/conversations.ts +4 -2
  285. package/src/daemon/handlers/shared.ts +29 -0
  286. package/src/daemon/host-proxy-base.ts +4 -4
  287. package/src/daemon/lifecycle.ts +13 -0
  288. package/src/daemon/message-protocol.ts +8 -144
  289. package/src/daemon/message-types/computer-use.ts +0 -12
  290. package/src/daemon/message-types/conversations.ts +9 -345
  291. package/src/daemon/message-types/host-app-control.ts +0 -9
  292. package/src/daemon/message-types/host-browser.ts +0 -7
  293. package/src/daemon/message-types/host-ui-snapshot.ts +0 -7
  294. package/src/daemon/message-types/messages.ts +0 -48
  295. package/src/daemon/message-types/notifications.ts +0 -7
  296. package/src/daemon/message-types/skills.ts +0 -5
  297. package/src/daemon/message-types/surfaces.ts +0 -9
  298. package/src/daemon/message-types/sync.ts +0 -2
  299. package/src/daemon/process-message.ts +5 -5
  300. package/src/daemon/skill-memory-refresh.ts +5 -5
  301. package/src/daemon/tool-side-effects.ts +13 -13
  302. package/src/daemon/trust-context.ts +1 -1
  303. package/src/daemon/wake-conversation-ops.ts +4 -4
  304. package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +100 -7
  305. package/src/heartbeat/__tests__/heartbeat-service.test.ts +67 -0
  306. package/src/heartbeat/heartbeat-run-store.ts +81 -4
  307. package/src/heartbeat/heartbeat-service.ts +32 -1
  308. package/src/home/__tests__/feed-writer.test.ts +7 -7
  309. package/src/home/__tests__/home-content-refresh.test.ts +3 -0
  310. package/src/home/__tests__/suggested-prompts.test.ts +1 -0
  311. package/src/home/feed-writer.ts +6 -13
  312. package/src/home/home-content-refresh.ts +6 -9
  313. package/src/home/relationship-state-writer.ts +5 -12
  314. package/src/home/suggested-prompts-cache.ts +6 -19
  315. package/src/ipc/__tests__/events-publish-client.test.ts +67 -0
  316. package/src/ipc/events-publish-client.ts +44 -0
  317. package/src/ipc/routes/__tests__/conversation-sync-ipc-routes.test.ts +2 -2
  318. package/src/ipc/routes/__tests__/events-ipc-routes.test.ts +38 -9
  319. package/src/ipc/routes/events-ipc-routes.ts +11 -31
  320. package/src/live-voice/__tests__/front-decision.test.ts +4 -176
  321. package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +276 -68
  322. package/src/live-voice/__tests__/live-voice-events.test.ts +6 -3
  323. package/src/live-voice/__tests__/live-voice-progress.test.ts +185 -46
  324. package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +24 -59
  325. package/src/live-voice/__tests__/live-voice-tts-session.test.ts +38 -54
  326. package/src/live-voice/__tests__/live-voice-vad.test.ts +218 -422
  327. package/src/live-voice/front-decision.ts +5 -155
  328. package/src/live-voice/live-voice-metrics.ts +4 -1
  329. package/src/live-voice/live-voice-session.ts +650 -333
  330. package/src/live-voice/progress-phrases.ts +17 -0
  331. package/src/live-voice/protocol.ts +15 -0
  332. package/src/media/app-icon-generator.ts +15 -8
  333. package/src/media/avatar-router.ts +9 -5
  334. package/src/media/image-credentials.ts +42 -10
  335. package/src/media/image-service.ts +1 -13
  336. package/src/media/types.ts +26 -3
  337. package/src/notifications/AGENTS.md +2 -0
  338. package/src/notifications/__tests__/broadcaster.test.ts +95 -0
  339. package/src/notifications/adapters/macos.ts +3 -3
  340. package/src/notifications/broadcaster.ts +57 -0
  341. package/src/notifications/copy-composer.ts +0 -15
  342. package/src/notifications/decision-engine.ts +21 -22
  343. package/src/notifications/guardian-question-mode.ts +57 -2
  344. package/src/notifications/home-feed-side-effect.ts +4 -0
  345. package/src/notifications/signal.ts +6 -4
  346. package/src/notifications/trusted-contact-payloads.ts +3 -4
  347. package/src/notifications/vellum-card-affinity.ts +39 -0
  348. package/src/oauth/AGENTS.md +1 -1
  349. package/src/permissions/channel-permission-query.test.ts +69 -0
  350. package/src/permissions/channel-permission-query.ts +73 -1
  351. package/src/permissions/checker.test.ts +85 -9
  352. package/src/permissions/checker.ts +17 -22
  353. package/src/permissions/confirmation-guardian-request.test.ts +2 -2
  354. package/src/permissions/confirmation-guardian-request.ts +2 -2
  355. package/src/permissions/gateway-threshold-reader.test.ts +224 -17
  356. package/src/permissions/gateway-threshold-reader.ts +82 -10
  357. package/src/permissions/ipc-risk-types.ts +8 -0
  358. package/src/permissions/prompter.ts +5 -5
  359. package/src/permissions/question-guardian-request.test.ts +222 -0
  360. package/src/permissions/question-guardian-request.ts +179 -0
  361. package/src/permissions/question-prompter.test.ts +8 -8
  362. package/src/permissions/question-prompter.ts +18 -0
  363. package/src/permissions/secret-prompter.ts +2 -4
  364. package/src/permissions/workspace-policy.ts +236 -34
  365. package/src/persistence/__tests__/lifecycle-quiesce.test.ts +82 -0
  366. package/src/persistence/__tests__/message-content-file.test.ts +58 -3
  367. package/src/persistence/conversation-crud.ts +93 -0
  368. package/src/persistence/conversation-key-store.ts +63 -0
  369. package/src/persistence/db-async-query.ts +1 -1
  370. package/src/persistence/db-singleton.ts +14 -27
  371. package/src/persistence/delivery-crud.ts +45 -0
  372. package/src/persistence/embeddings/embedding-backend.ts +2 -1
  373. package/src/persistence/embeddings/embedding-identity.ts +7 -3
  374. package/src/persistence/job-utils.ts +32 -27
  375. package/src/persistence/jobs-store.ts +92 -3
  376. package/src/persistence/lifecycle-quiesce.ts +80 -0
  377. package/src/persistence/llm-usage-store.ts +45 -8
  378. package/src/persistence/message-content-file.ts +4 -4
  379. package/src/persistence/migrations/336-move-memory-v2-activation-logs-to-memory-db.ts +1 -1
  380. package/src/persistence/migrations/351-schedule-skill-script-handoff.ts +39 -0
  381. package/src/persistence/migrations/352-drop-schedule-skill-script-handoff.test.ts +83 -0
  382. package/src/persistence/migrations/352-drop-schedule-skill-script-handoff.ts +36 -0
  383. package/src/persistence/migrations/353-add-llm-usage-conversation-type.test.ts +110 -0
  384. package/src/persistence/migrations/353-add-llm-usage-conversation-type.ts +49 -0
  385. package/src/persistence/schema/infrastructure.ts +11 -0
  386. package/src/persistence/steps.ts +7 -1
  387. package/src/plugin-api/__tests__/publish-event-forwarding.test.ts +113 -0
  388. package/src/plugin-api/__tests__/publish-event.test.ts +57 -0
  389. package/src/plugin-api/conversation-turn.ts +4 -4
  390. package/src/plugin-api/event-hub-facade.ts +15 -6
  391. package/src/plugin-api/index.ts +10 -4
  392. package/src/plugin-api/publish-event.ts +36 -0
  393. package/src/plugins/AGENTS.md +2 -0
  394. package/src/plugins/defaults/compaction/window-manager.ts +9 -0
  395. package/src/plugins/defaults/index.ts +9 -13
  396. package/src/plugins/defaults/memory/AGENTS.md +775 -0
  397. package/src/plugins/defaults/memory/__tests__/capability-seed-embed-gate.test.ts +186 -0
  398. package/src/plugins/defaults/memory/__tests__/fixtures/memory-v2-activation-fixtures.ts +1 -1
  399. package/src/plugins/defaults/memory/__tests__/indexer-tier-dispatch.test.ts +129 -0
  400. package/src/plugins/defaults/memory/__tests__/jobs-worker-quiesce-gate.test.ts +91 -0
  401. package/src/plugins/defaults/memory/__tests__/jobs-worker-v1-graph-substrate-noop.test.ts +342 -0
  402. package/src/plugins/defaults/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +9 -1
  403. package/src/plugins/defaults/memory/__tests__/jobs-worker-v2-schedule.test.ts +36 -1
  404. package/src/plugins/defaults/memory/__tests__/jobs-worker-v2-sweep-defer.test.ts +9 -1
  405. package/src/plugins/defaults/memory/__tests__/memory-log-stores-degraded.test.ts +2 -2
  406. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +44 -10
  407. package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +151 -10
  408. package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +1155 -0
  409. package/src/plugins/defaults/memory/__tests__/memory-v2-activation-log-store.test.ts +1 -1
  410. package/src/plugins/defaults/memory/__tests__/memory-v2-concept-frequency.test.ts +3 -3
  411. package/src/plugins/defaults/memory/__tests__/tools-delete-memory-page.test.ts +1 -1
  412. package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +71 -35
  413. package/src/plugins/defaults/memory/context-search/sources/memory.ts +5 -1
  414. package/src/plugins/defaults/memory/fork-conversation-memory.ts +4 -4
  415. package/src/plugins/defaults/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +80 -10
  416. package/src/plugins/defaults/memory/graph/__tests__/handle-remember-v2.test.ts +1 -1
  417. package/src/plugins/defaults/memory/graph/capability-seed.ts +24 -10
  418. package/src/plugins/defaults/memory/graph/conversation-graph-memory.ts +123 -49
  419. package/src/plugins/defaults/memory/graph/graph-search.ts +6 -118
  420. package/src/plugins/defaults/memory/graph/in-context-tracker.test.ts +173 -0
  421. package/src/plugins/defaults/memory/graph/in-context-tracker.ts +109 -0
  422. package/src/plugins/defaults/memory/graph/store.ts +1 -0
  423. package/src/plugins/defaults/memory/graph/tool-handlers.test.ts +1 -1
  424. package/src/plugins/defaults/memory/graph/tool-handlers.ts +5 -2
  425. package/src/plugins/defaults/memory/graph-topology/__tests__/build-memory-graph.test.ts +1 -1
  426. package/src/plugins/defaults/memory/graph-topology/build-memory-graph.ts +6 -9
  427. package/src/plugins/defaults/memory/hooks/__tests__/user-prompt-submit.test.ts +30 -16
  428. package/src/plugins/defaults/memory/hooks/user-prompt-submit.ts +18 -14
  429. package/src/plugins/defaults/memory/indexer.ts +146 -89
  430. package/src/plugins/defaults/memory/injectors.ts +86 -41
  431. package/src/plugins/defaults/memory/job-handlers.ts +130 -39
  432. package/src/plugins/defaults/memory/jobs/__tests__/embed-concept-page.test.ts +2 -2
  433. package/src/plugins/defaults/memory/jobs/embed-concept-page.ts +22 -10
  434. package/src/plugins/defaults/memory/jobs-worker.ts +279 -208
  435. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +4 -2
  436. package/src/plugins/defaults/memory/memory-retrospective-job.ts +40 -3
  437. package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +99 -11
  438. package/src/plugins/defaults/memory/src/__tests__/memory-v2-routes.test.ts +5 -3
  439. package/src/plugins/defaults/memory/src/__tests__/memory-v2-simulate-route.test.ts +3 -4
  440. package/src/plugins/defaults/memory/src/memory-graph-routes.ts +2 -1
  441. package/src/plugins/defaults/memory/src/memory-item-routes.test.ts +7 -5
  442. package/src/plugins/defaults/memory/src/memory-item-routes.ts +51 -19
  443. package/src/plugins/defaults/memory/src/memory-v2-routes.ts +31 -20
  444. package/src/plugins/defaults/memory/startup.ts +58 -21
  445. package/src/plugins/defaults/memory/substrate/__tests__/boot-maintenance.test.ts +335 -0
  446. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/cli-command-store.test.ts +14 -8
  447. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/consolidation-job.test.ts +51 -21
  448. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/edge-index.test.ts +1 -1
  449. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/frontmatter-sweep.test.ts +3 -3
  450. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/page-index.test.ts +1 -1
  451. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/page-store.test.ts +1 -1
  452. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/prompts-consolidation.test.ts +2 -2
  453. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/qdrant.test.ts +23 -14
  454. package/src/plugins/defaults/memory/substrate/__tests__/reembed-job.test.ts +262 -0
  455. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/sim.test.ts +9 -12
  456. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/skill-content.test.ts +2 -2
  457. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/skill-store.test.ts +9 -9
  458. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/sparse-bm25.test.ts +12 -5
  459. package/src/plugins/defaults/memory/substrate/__tests__/spread.test.ts +276 -0
  460. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/static-context.test.ts +17 -8
  461. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/sweep-job.test.ts +9 -9
  462. package/src/plugins/defaults/memory/{v3/substrate/memory-v2-startup.ts → substrate/boot-maintenance.ts} +43 -24
  463. package/src/plugins/defaults/memory/{v3/substrate → substrate}/cli-command-store.ts +13 -8
  464. package/src/plugins/defaults/memory/{v3/substrate → substrate}/consolidation-job.ts +51 -27
  465. package/src/plugins/defaults/memory/{v3/substrate → substrate}/constants.ts +1 -0
  466. package/src/plugins/defaults/memory/{v3/substrate → substrate}/edge-index.ts +28 -9
  467. package/src/plugins/defaults/memory/{v3/substrate → substrate}/frontmatter-sweep.ts +14 -7
  468. package/src/plugins/defaults/memory/{v3/substrate → substrate}/injected-block-slugs.ts +9 -3
  469. package/src/plugins/defaults/memory/{v3/substrate → substrate}/page-index.ts +29 -10
  470. package/src/plugins/defaults/memory/{v3/substrate → substrate}/page-store.ts +35 -12
  471. package/src/plugins/defaults/memory/{v3/substrate → substrate}/prompts/consolidation.ts +14 -8
  472. package/src/plugins/defaults/memory/{v3/substrate → substrate}/prompts/sweep.ts +4 -1
  473. package/src/plugins/defaults/memory/{v3/substrate → substrate}/qdrant.ts +96 -36
  474. package/src/plugins/defaults/memory/substrate/reembed-job.ts +54 -0
  475. package/src/plugins/defaults/memory/{v3/substrate → substrate}/sim.ts +26 -15
  476. package/src/plugins/defaults/memory/{v3/substrate → substrate}/skill-content.ts +1 -1
  477. package/src/plugins/defaults/memory/{v3/substrate → substrate}/skill-store.ts +13 -8
  478. package/src/plugins/defaults/memory/{v3/substrate → substrate}/sparse-bm25.ts +27 -10
  479. package/src/plugins/defaults/memory/substrate/spread.ts +165 -0
  480. package/src/plugins/defaults/memory/{v3/substrate → substrate}/static-context.ts +12 -7
  481. package/src/plugins/defaults/memory/{v3/substrate → substrate}/sweep-job.ts +20 -15
  482. package/src/plugins/defaults/memory/substrate/tuning.ts +93 -0
  483. package/src/plugins/defaults/memory/{v3/substrate → substrate}/types.ts +7 -4
  484. package/src/plugins/defaults/memory/tools.test.ts +1 -1
  485. package/src/plugins/defaults/memory/tools.ts +1 -1
  486. package/src/plugins/defaults/memory/v1/README.md +5 -0
  487. package/src/plugins/defaults/memory/{__tests__ → v1/__tests__}/filing-jobs.test.ts +4 -4
  488. package/src/plugins/defaults/memory/{filing-jobs.ts → v1/filing-jobs.ts} +3 -3
  489. package/src/plugins/defaults/memory/{graph → v1/graph}/bootstrap.test.ts +68 -6
  490. package/src/plugins/defaults/memory/{graph → v1/graph}/bootstrap.ts +40 -21
  491. package/src/plugins/defaults/memory/{graph → v1/graph}/consolidation.ts +14 -14
  492. package/src/plugins/defaults/memory/{graph → v1/graph}/decay.test.ts +1 -1
  493. package/src/plugins/defaults/memory/{graph → v1/graph}/decay.ts +3 -3
  494. package/src/plugins/defaults/memory/{graph → v1/graph}/extraction-job.ts +5 -5
  495. package/src/plugins/defaults/memory/{graph → v1/graph}/extraction.test.ts +1 -1
  496. package/src/plugins/defaults/memory/{graph → v1/graph}/extraction.ts +20 -14
  497. package/src/plugins/defaults/memory/{graph → v1/graph}/graph-search.test.ts +3 -3
  498. package/src/plugins/defaults/memory/v1/graph/graph-search.ts +123 -0
  499. package/src/plugins/defaults/memory/{graph → v1/graph}/injection.test.ts +1 -118
  500. package/src/plugins/defaults/memory/{graph → v1/graph}/injection.ts +9 -111
  501. package/src/plugins/defaults/memory/{graph → v1/graph}/narrative.ts +6 -6
  502. package/src/plugins/defaults/memory/{graph → v1/graph}/pattern-scan.ts +5 -5
  503. package/src/plugins/defaults/memory/{graph → v1/graph}/retriever.test.ts +135 -42
  504. package/src/plugins/defaults/memory/{graph → v1/graph}/retriever.ts +40 -22
  505. package/src/plugins/defaults/memory/{graph → v1/graph}/scoring.test.ts +1 -1
  506. package/src/plugins/defaults/memory/{graph → v1/graph}/scoring.ts +1 -1
  507. package/src/plugins/defaults/memory/{graph → v1/graph}/serendipity.ts +1 -1
  508. package/src/plugins/defaults/memory/{graph → v1/graph}/triggers.test.ts +1 -1
  509. package/src/plugins/defaults/memory/{graph → v1/graph}/triggers.ts +1 -1
  510. package/src/plugins/defaults/memory/{identity-context.ts → v1/identity-context.ts} +4 -4
  511. package/src/plugins/defaults/memory/{job-handlers → v1/job-handlers}/backfill.ts +6 -6
  512. package/src/plugins/defaults/memory/{job-handlers → v1/job-handlers}/embedding.test.ts +6 -6
  513. package/src/plugins/defaults/memory/{job-handlers → v1/job-handlers}/embedding.ts +6 -6
  514. package/src/plugins/defaults/memory/{job-handlers → v1/job-handlers}/index-maintenance.ts +8 -8
  515. package/src/plugins/defaults/memory/{jobs → v1/jobs}/embed-pkb-file.test.ts +7 -7
  516. package/src/plugins/defaults/memory/{jobs → v1/jobs}/embed-pkb-file.ts +5 -5
  517. package/src/plugins/defaults/memory/{pkb → v1/pkb}/autoinject.ts +1 -1
  518. package/src/plugins/defaults/memory/{pkb → v1/pkb}/context.ts +2 -2
  519. package/src/plugins/defaults/memory/{pkb → v1/pkb}/pkb-index.test.ts +3 -3
  520. package/src/plugins/defaults/memory/{pkb → v1/pkb}/pkb-index.ts +2 -2
  521. package/src/plugins/defaults/memory/{pkb → v1/pkb}/pkb-reconcile.test.ts +3 -3
  522. package/src/plugins/defaults/memory/{pkb → v1/pkb}/pkb-reconcile.ts +2 -2
  523. package/src/plugins/defaults/memory/{pkb → v1/pkb}/pkb-search.test.ts +3 -3
  524. package/src/plugins/defaults/memory/{pkb → v1/pkb}/pkb-search.ts +6 -6
  525. package/src/plugins/defaults/memory/{pkb → v1/pkb}/types.ts +3 -5
  526. package/src/plugins/defaults/memory/{pkb-schedule.ts → v1/pkb-schedule.ts} +2 -2
  527. package/src/plugins/defaults/memory/{search/semantic.ts → v1/semantic-search.ts} +1 -1
  528. package/src/plugins/defaults/memory/v2/__tests__/activation-store.test.ts +1 -1
  529. package/src/plugins/defaults/memory/v2/__tests__/activation.test.ts +11 -274
  530. package/src/plugins/defaults/memory/v2/__tests__/backfill-jobs.test.ts +9 -131
  531. package/src/plugins/defaults/memory/v2/__tests__/harness-compare.test.ts +1 -1
  532. package/src/plugins/defaults/memory/v2/__tests__/harness-oracle.test.ts +1 -1
  533. package/src/plugins/defaults/memory/v2/__tests__/harness-replay-input.test.ts +1 -1
  534. package/src/plugins/defaults/memory/v2/__tests__/injection.test.ts +14 -10
  535. package/src/plugins/defaults/memory/v2/__tests__/reranker.test.ts +7 -5
  536. package/src/plugins/defaults/memory/v2/__tests__/router.test.ts +12 -6
  537. package/src/plugins/defaults/memory/{memory-v2-activation-log-store.ts → v2/activation-log-store.ts} +2 -2
  538. package/src/plugins/defaults/memory/v2/activation-store.ts +22 -8
  539. package/src/plugins/defaults/memory/v2/activation.ts +30 -163
  540. package/src/plugins/defaults/memory/v2/backfill-jobs.ts +7 -54
  541. package/src/plugins/defaults/memory/{memory-v2-concept-frequency.ts → v2/concept-frequency.ts} +3 -3
  542. package/src/plugins/defaults/memory/v2/harness/oracle.ts +1 -1
  543. package/src/plugins/defaults/memory/v2/harness/replay-input.ts +2 -2
  544. package/src/plugins/defaults/memory/v2/harness/retriever.ts +1 -1
  545. package/src/plugins/defaults/memory/v2/injection.ts +36 -26
  546. package/src/plugins/defaults/memory/v2/migration.ts +2 -2
  547. package/src/plugins/defaults/memory/{rerank-local.ts → v2/rerank-local.ts} +44 -18
  548. package/src/plugins/defaults/memory/v2/reranker.ts +29 -11
  549. package/src/plugins/defaults/memory/v2/router.ts +38 -15
  550. package/src/plugins/defaults/memory/v3/__tests__/candidate-match.test.ts +5 -3
  551. package/src/plugins/defaults/memory/v3/__tests__/carry-integration.test.ts +21 -8
  552. package/src/plugins/defaults/memory/v3/__tests__/edge.test.ts +1 -1
  553. package/src/plugins/defaults/memory/v3/__tests__/live-integration.test.ts +1 -1
  554. package/src/plugins/defaults/memory/v3/__tests__/maintain-job.test.ts +4 -1
  555. package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +181 -3
  556. package/src/plugins/defaults/memory/v3/__tests__/selection-log-store.test.ts +3 -0
  557. package/src/plugins/defaults/memory/v3/__tests__/shadow-integration.test.ts +14 -5
  558. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +40 -50
  559. package/src/plugins/defaults/memory/v3/__tests__/tuning-profile.test.ts +1 -0
  560. package/src/plugins/defaults/memory/v3/candidate-match.ts +2 -2
  561. package/src/plugins/defaults/memory/v3/capabilities.ts +6 -4
  562. package/src/plugins/defaults/memory/v3/card.ts +25 -9
  563. package/src/plugins/defaults/memory/v3/edge.ts +52 -18
  564. package/src/plugins/defaults/memory/v3/gate.ts +4 -4
  565. package/src/plugins/defaults/memory/v3/injector.ts +10 -3
  566. package/src/plugins/defaults/memory/v3/maintain-job.ts +3 -3
  567. package/src/plugins/defaults/memory/v3/orchestrate.ts +100 -18
  568. package/src/plugins/defaults/memory/v3/page-content.ts +26 -10
  569. package/src/plugins/defaults/memory/v3/prune.ts +40 -16
  570. package/src/plugins/defaults/memory/v3/selection-log-store.ts +34 -12
  571. package/src/plugins/defaults/memory/v3/shadow-plugin.ts +9 -13
  572. package/src/plugins/defaults/memory/v3/span-query.test.ts +86 -0
  573. package/src/plugins/defaults/memory/v3/span-query.ts +71 -0
  574. package/src/plugins/defaults/memory/v3/tuning-profile.ts +3 -0
  575. package/src/plugins/defaults/memory/v3/types.ts +4 -1
  576. package/src/plugins/defaults/memory/v3-eval/eval-packets.ts +2 -2
  577. package/src/plugins/defaults/memory/validation.ts +1 -1
  578. package/src/plugins/defaults/memory/worker.ts +2 -1
  579. package/src/plugins/defaults/platform-hosted/__tests__/reengage.test.ts +104 -12
  580. package/src/plugins/defaults/platform-hosted/routes/reengage.ts +104 -12
  581. package/src/plugins/types.ts +2 -2
  582. package/src/providers/__tests__/registry-native-web-search.test.ts +47 -2
  583. package/src/providers/anthropic/client.ts +15 -6
  584. package/src/providers/content-block-schema.ts +26 -0
  585. package/src/providers/inference/__tests__/connections-credential-normalize.test.ts +87 -0
  586. package/src/providers/inference/adapter-factory.ts +6 -0
  587. package/src/providers/inference/connections.ts +30 -14
  588. package/src/providers/model-catalog.ts +77 -0
  589. package/src/providers/openai/__tests__/chat-completions-input-audio.test.ts +260 -0
  590. package/src/providers/openai/chat-completions-provider.ts +65 -17
  591. package/src/providers/openai/input-audio.ts +76 -0
  592. package/src/providers/openrouter/client.ts +0 -2
  593. package/src/providers/platform-proxy/constants.ts +4 -0
  594. package/src/providers/poolside/client.ts +42 -0
  595. package/src/providers/registry.ts +40 -8
  596. package/src/providers/retry.ts +2 -0
  597. package/src/providers/types.ts +39 -1
  598. package/src/providers/vercel-ai-gateway/client.ts +0 -2
  599. package/src/runtime/AGENTS.md +7 -5
  600. package/src/runtime/__tests__/question-request-guardian-bridge.test.ts +173 -0
  601. package/src/runtime/__tests__/question-resolution.test.ts +111 -0
  602. package/src/runtime/access-request-helper.ts +7 -15
  603. package/src/runtime/assistant-event-hub.ts +43 -31
  604. package/src/runtime/assistant-event-publish-options.ts +31 -0
  605. package/src/runtime/assistant-event.ts +19 -30
  606. package/src/runtime/assistant-stream-state.ts +4 -4
  607. package/src/runtime/channel-approvals.ts +0 -25
  608. package/src/runtime/channel-readiness-service.ts +99 -40
  609. package/src/runtime/channel-retry-sweep.ts +38 -15
  610. package/src/runtime/confirmation-request-guardian-bridge.ts +4 -0
  611. package/src/runtime/guardian-reply-router.ts +108 -0
  612. package/src/runtime/http-server.ts +8 -0
  613. package/src/runtime/http-types.ts +2 -2
  614. package/src/runtime/process-role.ts +42 -0
  615. package/src/runtime/question-request-guardian-bridge.ts +184 -0
  616. package/src/runtime/question-resolution.ts +153 -0
  617. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +6 -2
  618. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +1 -1
  619. package/src/runtime/routes/acp-routes.ts +2 -2
  620. package/src/runtime/routes/bookmark-routes.ts +3 -15
  621. package/src/runtime/routes/canned-message-complete.ts +2 -2
  622. package/src/runtime/routes/consolidation-routes.ts +8 -2
  623. package/src/runtime/routes/conversation-list-routes.ts +3 -19
  624. package/src/runtime/routes/conversation-management-routes.ts +1 -5
  625. package/src/runtime/routes/conversation-query-routes.ts +52 -8
  626. package/src/runtime/routes/conversation-routes.ts +36 -22
  627. package/src/runtime/routes/credential-request-routes.ts +0 -1
  628. package/src/runtime/routes/filing-routes.ts +7 -7
  629. package/src/runtime/routes/global-search-routes.ts +1 -1
  630. package/src/runtime/routes/guardian-approval-prompt.ts +2 -2
  631. package/src/runtime/routes/image-generation-routes.ts +12 -9
  632. package/src/runtime/routes/inbound-message-handler.ts +17 -0
  633. package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +50 -0
  634. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +19 -0
  635. package/src/runtime/routes/inbound-stages/background-dispatch.ts +4 -4
  636. package/src/runtime/routes/inbound-stages/inbound-content-prep.test.ts +129 -0
  637. package/src/runtime/routes/inbound-stages/inbound-content-prep.ts +141 -9
  638. package/src/runtime/routes/index.ts +2 -0
  639. package/src/runtime/routes/lifecycle-routes.ts +199 -0
  640. package/src/runtime/routes/platform-routes.ts +3 -8
  641. package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +7 -7
  642. package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +5 -5
  643. package/src/runtime/routes/question-routes.ts +34 -87
  644. package/src/runtime/routes/secret-routes.ts +11 -1
  645. package/src/runtime/routes/settings-routes.ts +8 -18
  646. package/src/runtime/routes/upgrade-broadcast-routes.ts +4 -5
  647. package/src/runtime/routes/workspace-routes.test.ts +7 -4
  648. package/src/runtime/slack-reply-session.test.ts +12 -12
  649. package/src/runtime/slack-reply-session.ts +17 -17
  650. package/src/runtime/sync/sync-publisher.test.ts +7 -5
  651. package/src/runtime/tool-grant-request-helper.ts +4 -0
  652. package/src/schedule/schedule-store.ts +70 -0
  653. package/src/schedule/scheduler.ts +27 -0
  654. package/src/security/AGENTS.md +1 -1
  655. package/src/security/credential-key.ts +17 -0
  656. package/src/security/secret-ingress.ts +14 -105
  657. package/src/security/secret-patterns.ts +8 -139
  658. package/src/security/secret-scanner.ts +8 -3
  659. package/src/security/store-path-override.ts +6 -16
  660. package/src/sequence/store.ts +8 -0
  661. package/src/signals/emit-event.ts +5 -10
  662. package/src/signals/event-stream.ts +20 -10
  663. package/src/subagent/manager.ts +76 -25
  664. package/src/subagent/notify.ts +8 -0
  665. package/src/subagent/types.ts +13 -3
  666. package/src/telegram/__tests__/webhook-health.test.ts +442 -0
  667. package/src/telegram/webhook-health.ts +478 -0
  668. package/src/telemetry/AGENTS.md +6 -0
  669. package/src/telemetry/memory-corpus-size.test.ts +129 -0
  670. package/src/telemetry/memory-corpus-size.ts +184 -0
  671. package/src/telemetry/memory-tier-reporter.test.ts +67 -2
  672. package/src/telemetry/memory-tier-reporter.ts +30 -3
  673. package/src/tools/__tests__/document-tool-input-schemas.test.ts +103 -0
  674. package/src/tools/__tests__/followup-call-acp-tool-input-schemas.test.ts +125 -0
  675. package/src/tools/__tests__/schedule-tool-input-schemas.test.ts +83 -0
  676. package/src/tools/__tests__/subagent-workflow-tool-input-schemas.test.ts +116 -0
  677. package/src/tools/__tests__/tool-input-schemas.test.ts +192 -0
  678. package/src/tools/__tests__/tool-schema-drift-harness.ts +142 -0
  679. package/src/tools/acp/abort.ts +21 -1
  680. package/src/tools/acp/context.ts +5 -5
  681. package/src/tools/acp/spawn.test.ts +15 -5
  682. package/src/tools/acp/spawn.ts +26 -3
  683. package/src/tools/acp/status.ts +20 -1
  684. package/src/tools/acp/steer.test.ts +20 -0
  685. package/src/tools/acp/steer.ts +23 -2
  686. package/src/tools/ask-question/ask-question-tool.test.ts +65 -0
  687. package/src/tools/ask-question/ask-question-tool.ts +68 -92
  688. package/src/tools/browser/browser-screencast.ts +6 -6
  689. package/src/tools/calls/call-end.ts +24 -3
  690. package/src/tools/calls/call-start.ts +35 -11
  691. package/src/tools/calls/call-status.ts +20 -1
  692. package/src/tools/document/document-comment-tool.test.ts +49 -0
  693. package/src/tools/document/document-comment-tool.ts +45 -6
  694. package/src/tools/document/document-tool.ts +142 -24
  695. package/src/tools/executor.ts +27 -13
  696. package/src/tools/filesystem/edit.ts +62 -60
  697. package/src/tools/filesystem/list.ts +36 -29
  698. package/src/tools/filesystem/read.ts +56 -39
  699. package/src/tools/filesystem/write.ts +44 -38
  700. package/src/tools/followups/followup_create.ts +41 -12
  701. package/src/tools/followups/followup_list.ts +34 -6
  702. package/src/tools/followups/followup_resolve.ts +26 -4
  703. package/src/tools/host-filesystem/edit.ts +50 -60
  704. package/src/tools/host-filesystem/read.ts +45 -39
  705. package/src/tools/host-filesystem/write.ts +31 -36
  706. package/src/tools/host-terminal/host-shell.ts +76 -62
  707. package/src/tools/policy-context.ts +27 -10
  708. package/src/tools/schedule/create.ts +76 -33
  709. package/src/tools/schedule/delete.ts +21 -2
  710. package/src/tools/schedule/list.ts +25 -3
  711. package/src/tools/schedule/update.ts +105 -48
  712. package/src/tools/shared/filesystem/path-policy.ts +246 -40
  713. package/src/tools/shared/zod-tool-schema.ts +117 -0
  714. package/src/tools/subagent/abort.ts +12 -4
  715. package/src/tools/subagent/consult-deadline.ts +9 -3
  716. package/src/tools/subagent/message.ts +21 -5
  717. package/src/tools/subagent/notify-parent.ts +44 -23
  718. package/src/tools/subagent/read.ts +15 -4
  719. package/src/tools/subagent/resolve.ts +26 -4
  720. package/src/tools/subagent/spawn.ts +37 -13
  721. package/src/tools/subagent/status.ts +12 -4
  722. package/src/tools/system/request-permission.ts +35 -26
  723. package/src/tools/terminal/shell.ts +68 -55
  724. package/src/tools/tool-approval-handler.ts +327 -53
  725. package/src/tools/tool-input-schemas.ts +85 -0
  726. package/src/tools/tool-types.ts +7 -0
  727. package/src/tools/types.ts +8 -0
  728. package/src/tools/workflows/manage-workflows.ts +30 -2
  729. package/src/tools/workflows/run-workflow.test.ts +54 -2
  730. package/src/tools/workflows/run-workflow.ts +32 -4
  731. package/src/util/fs-symlinks.ts +30 -0
  732. package/src/util/log-redact.ts +7 -3
  733. package/src/vellum-assistant-namespace.d.ts +80 -0
  734. package/src/watcher/watcher-store.ts +8 -0
  735. package/src/workflows/leaf-runner.test.ts +104 -2
  736. package/src/workflows/leaf-runner.ts +31 -6
  737. package/src/workspace/migrations/070-memory-v2-summary-schema-rebuild.ts +2 -2
  738. package/src/workspace/migrations/134-image-generation-mode-to-provider.ts +128 -0
  739. package/src/workspace/migrations/135-copy-substrate-tunables.ts +248 -0
  740. package/src/workspace/migrations/__tests__/135-copy-substrate-tunables.test.ts +446 -0
  741. package/src/workspace/migrations/__tests__/backfill-v3-leaves.test.ts +2 -2
  742. package/src/workspace/migrations/registry.ts +4 -0
  743. package/src/__tests__/memory-retrieval-budget.test.ts +0 -49
  744. package/src/__tests__/secret-patterns.test.ts +0 -27
  745. package/src/config/__tests__/proc-to-skills.test.ts +0 -21
  746. package/src/config/memory-tier.test.ts +0 -44
  747. package/src/daemon/message-types/acp.ts +0 -20
  748. package/src/daemon/message-types/apps.ts +0 -11
  749. package/src/daemon/message-types/background-tools.ts +0 -14
  750. package/src/daemon/message-types/bookmarks.ts +0 -13
  751. package/src/daemon/message-types/contacts.ts +0 -13
  752. package/src/daemon/message-types/document-comments.ts +0 -14
  753. package/src/daemon/message-types/documents.ts +0 -13
  754. package/src/daemon/message-types/home.ts +0 -15
  755. package/src/daemon/message-types/host-bash.ts +0 -12
  756. package/src/daemon/message-types/host-cu.ts +0 -10
  757. package/src/daemon/message-types/host-file.ts +0 -12
  758. package/src/daemon/message-types/host-transfer.ts +0 -12
  759. package/src/daemon/message-types/integrations.ts +0 -90
  760. package/src/daemon/message-types/memory.ts +0 -10
  761. package/src/daemon/message-types/schedules.ts +0 -14
  762. package/src/daemon/message-types/settings.ts +0 -17
  763. package/src/daemon/message-types/subagents.ts +0 -63
  764. package/src/daemon/message-types/upgrades.ts +0 -14
  765. package/src/daemon/message-types/workflows.ts +0 -25
  766. package/src/daemon/message-types/workspace.ts +0 -137
  767. package/src/live-voice/ack-phrases.ts +0 -58
  768. package/src/plugins/defaults/memory/retrieval-budget.ts +0 -29
  769. package/src/plugins/defaults/memory/v3/__tests__/gate-flag.test.ts +0 -69
  770. package/src/plugins/defaults/memory/v3/gate-flag.ts +0 -20
  771. package/src/plugins/defaults/memory/v3/substrate/__tests__/memory-v2-startup.test.ts +0 -86
  772. package/src/plugins/defaults/memory/v3/substrate/memory-v2-startup.test.ts +0 -205
  773. /package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/concept-page-frontmatter-schema.test.ts +0 -0
  774. /package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/consolidation-prompt-flag-gating-guard.test.ts +0 -0
  775. /package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/injected-block-slugs.test.ts +0 -0
  776. /package/src/plugins/defaults/memory/{v3/substrate → substrate}/cli-command-content.ts +0 -0
@@ -1,4 +1,5 @@
1
1
  import { realpathSync } from "node:fs";
2
+ import { homedir, userInfo } from "node:os";
2
3
  import {
3
4
  basename,
4
5
  dirname,
@@ -8,6 +9,10 @@ import {
8
9
  resolve,
9
10
  } from "node:path";
10
11
 
12
+ import { getIsContainerized } from "../../../config/env-registry.js";
13
+ import { resolveTrailingLinkTarget } from "../../../util/fs-symlinks.js";
14
+ import { getDotEnvPath } from "../../../util/platform.js";
15
+
11
16
  /**
12
17
  * Result type shared by both sandbox and host path policies.
13
18
  */
@@ -48,12 +53,15 @@ const CONTAINER_WORKSPACE_EXACT = "/workspace";
48
53
  /**
49
54
  * Resolve symlinks in an absolute path.
50
55
  *
51
- * For an existing path, returns its `realpathSync`. For a path that does not
52
- * exist yet (e.g. a `file_write` target), walks up to the nearest existing
53
- * ancestor, resolves that ancestor via `realpathSync`, then re-appends the
54
- * trailing (non-existent) components — so a symlink anywhere in the existing
55
- * prefix is still followed. Falls back to the lexical input when nothing on
56
- * the path resolves (e.g. the path lives on a filesystem this process cannot
56
+ * A trailing (possibly dangling) symlink chain is followed first — see
57
+ * {@link resolveTrailingLinkTarget} — so a link whose destination does not
58
+ * exist yet still reports that destination. For an existing path, returns
59
+ * its `realpathSync`. For a path that does not exist yet (e.g. a
60
+ * `file_write` target), walks up to the nearest existing ancestor, resolves
61
+ * that ancestor via `realpathSync`, then re-appends the trailing
62
+ * (non-existent) components — so a symlink anywhere in the existing prefix
63
+ * is still followed. Falls back to the lexical input when nothing on the
64
+ * path resolves (e.g. the path lives on a filesystem this process cannot
57
65
  * see, as with host_file paths proxied to a remote client).
58
66
  *
59
67
  * Used both for sandbox-boundary enforcement and to canonicalize paths before
@@ -61,7 +69,7 @@ const CONTAINER_WORKSPACE_EXACT = "/workspace";
61
69
  * file operation.
62
70
  */
63
71
  export function resolveRealPath(absolutePath: string): string {
64
- let current = absolutePath;
72
+ let current = resolveTrailingLinkTarget(absolutePath);
65
73
  const trailing: string[] = [];
66
74
  while (current !== dirname(current)) {
67
75
  try {
@@ -79,26 +87,25 @@ export function resolveRealPath(absolutePath: string): string {
79
87
  // Sandbox policy
80
88
  // ---------------------------------------------------------------------------
81
89
 
90
+ interface SandboxTarget {
91
+ /** Lexically resolved absolute path (boundary-relative input resolved). */
92
+ resolved: string;
93
+ /** Symlink-canonicalized form of `resolved`. */
94
+ realResolved: string;
95
+ /** Symlink-canonicalized boundary directory. */
96
+ realBoundary: string;
97
+ }
98
+
82
99
  /**
83
- * Resolve a user-supplied path against a boundary directory and verify
84
- * that the result stays within it.
85
- *
86
- * For existing paths, symlinks are resolved via realpathSync so a symlink
87
- * pointing outside the boundary is caught. For new paths (e.g. file_write),
88
- * pass `mustExist: false` - the nearest existing ancestor directory is
89
- * resolved via realpathSync to catch symlinks in parent dirs.
90
- *
91
- * Paths starting with `/workspace/` are treated as container-scoped and
92
- * remapped relative to the boundary directory (the Docker sandbox mounts
93
- * the host workspace at /workspace).
100
+ * Resolve a user-supplied path against the boundary directory: apply the
101
+ * container /workspace remap, resolve to an absolute path, and canonicalize
102
+ * symlinks on both the target and the boundary.
94
103
  */
95
- export function sandboxPolicy(
104
+ function resolveSandboxTarget(
96
105
  rawPath: string,
97
106
  boundaryDir: string,
98
- options?: { mustExist?: boolean },
99
- ): PathResult {
100
- const mustExist = options?.mustExist ?? true;
101
-
107
+ mustExist: boolean,
108
+ ): SandboxTarget {
102
109
  // Remap container-scoped /workspace paths to the host boundary dir.
103
110
  // Skip remapping if the path already starts with boundaryDir to avoid
104
111
  // double-nesting (e.g. /workspace/project/file.ts → /workspace/project/project/file.ts
@@ -114,19 +121,24 @@ export function sandboxPolicy(
114
121
 
115
122
  const resolved = resolve(boundaryDir, effectivePath);
116
123
 
124
+ // Follow a trailing (possibly dangling) symlink chain first — a write
125
+ // through a dangling link creates the link's destination, so the
126
+ // containment and denial checks below must run against it.
127
+ const linkTarget = resolveTrailingLinkTarget(resolved);
128
+
117
129
  // Resolve symlinks to catch symlink-based escapes.
118
130
  // For mustExist=false, walk up to the nearest existing ancestor and
119
131
  // resolve it, then re-append the trailing components.
120
- let realResolved = resolved;
132
+ let realResolved = linkTarget;
121
133
  if (mustExist) {
122
134
  try {
123
- realResolved = realpathSync(resolved);
135
+ realResolved = realpathSync(linkTarget);
124
136
  } catch {
125
137
  // File doesn't exist - will be caught by the tool's own existence check
126
- realResolved = resolved;
138
+ realResolved = linkTarget;
127
139
  }
128
140
  } else {
129
- realResolved = resolveRealPath(resolved);
141
+ realResolved = resolveRealPath(linkTarget);
130
142
  }
131
143
 
132
144
  // Resolve the boundary directory's real path too (in case it's a symlink)
@@ -137,26 +149,220 @@ export function sandboxPolicy(
137
149
  realBoundary = boundaryDir;
138
150
  }
139
151
 
140
- const rel = relative(realBoundary, realResolved);
141
- if (rel.startsWith("..") || resolve(realBoundary, rel) !== realResolved) {
142
- return {
143
- ok: false,
144
- reason: "out_of_bounds",
145
- error: `Path "${rawPath}" resolves to "${realResolved}" which is outside the working directory "${realBoundary}"`,
146
- };
147
- }
152
+ return { resolved, realResolved, realBoundary };
153
+ }
148
154
 
149
- // Check both the logical path and the symlink-resolved path so a symlink
150
- // with a non-denied name pointing at a denied file is still caught.
151
- if (isDeniedBasename(resolved) || isDeniedBasename(realResolved)) {
155
+ /** Whether the canonicalized target escapes the canonicalized boundary. */
156
+ function isOutOfBounds(target: SandboxTarget): boolean {
157
+ const rel = relative(target.realBoundary, target.realResolved);
158
+ return (
159
+ rel.startsWith("..") ||
160
+ resolve(target.realBoundary, rel) !== target.realResolved
161
+ );
162
+ }
163
+
164
+ function outOfBoundsFailure(
165
+ rawPath: string,
166
+ target: SandboxTarget,
167
+ ): PathResult {
168
+ return {
169
+ ok: false,
170
+ reason: "out_of_bounds",
171
+ error: `Path "${rawPath}" resolves to "${target.realResolved}" which is outside the working directory "${target.realBoundary}"`,
172
+ };
173
+ }
174
+
175
+ // The denied check covers both the logical path and the symlink-resolved
176
+ // path so a symlink with a non-denied name pointing at a denied file is
177
+ // still caught.
178
+ function deniedFailure(target: SandboxTarget): PathResult | null {
179
+ if (
180
+ isDeniedBasename(target.resolved) ||
181
+ isDeniedBasename(target.realResolved)
182
+ ) {
152
183
  return {
153
184
  ok: false,
154
185
  reason: "denied",
155
- error: `Access to "${basename(resolved)}" is denied`,
186
+ error: `Access to "${basename(target.resolved)}" is denied`,
156
187
  };
157
188
  }
189
+ return null;
190
+ }
191
+
192
+ function safeUserInfoHomedir(): string {
193
+ try {
194
+ return userInfo().homedir;
195
+ } catch {
196
+ return "";
197
+ }
198
+ }
199
+
200
+ const SECURITY_DIR_ENV_VARS = [
201
+ "GATEWAY_SECURITY_DIR",
202
+ "CREDENTIAL_SECURITY_DIR",
203
+ ] as const;
204
+
205
+ /**
206
+ * Secret-bearing paths the file tools must never touch, in any policy:
207
+ *
208
+ * - Gateway trust material and token signing keys (GATEWAY_SECURITY_DIR)
209
+ * and CES credential keys (CREDENTIAL_SECURITY_DIR — `keys.enc`,
210
+ * `store.key`). The daemon must never read from or write to them — that
211
+ * data flows through the gateway and CES APIs (root AGENTS.md). The
212
+ * env-based resolution mirrors the owning services', since the
213
+ * cross-package import boundary bars importing it; the shared bare-metal
214
+ * default `<home>/.vellum/protected` is always denied. Only absolute
215
+ * overrides join the set — a relative override is unmirrorable and
216
+ * disables the host fallback outright (see
217
+ * {@link securityDirConfigMirrorable}).
218
+ * - The daemon dotenv (`<vellumRoot>/.env`), which carries provider
219
+ * secrets.
220
+ */
221
+ function getProtectedServicePaths(): string[] {
222
+ const paths = new Set<string>();
223
+ paths.add(
224
+ join(
225
+ process.env.HOME || safeUserInfoHomedir() || homedir(),
226
+ ".vellum",
227
+ "protected",
228
+ ),
229
+ );
230
+ for (const name of SECURITY_DIR_ENV_VARS) {
231
+ const override = process.env[name]?.trim();
232
+ if (override && isAbsolute(override)) {
233
+ paths.add(resolve(override));
234
+ }
235
+ }
236
+ paths.add(getDotEnvPath());
237
+ return [...paths];
238
+ }
239
+
240
+ /**
241
+ * Whether the security-dir deny set can be mirrored faithfully. A relative
242
+ * override resolves against the owning service's cwd, which this process
243
+ * cannot know — the true directory would be absent from the deny set, so
244
+ * the host fallback fails closed to the hard boundary instead.
245
+ */
246
+ function securityDirConfigMirrorable(): boolean {
247
+ return SECURITY_DIR_ENV_VARS.every((name) => {
248
+ const override = process.env[name]?.trim();
249
+ return !override || isAbsolute(override);
250
+ });
251
+ }
158
252
 
159
- return { ok: true, resolved };
253
+ function isWithinDir(path: string, dir: string): boolean {
254
+ const normalized = dir !== "/" && dir.endsWith("/") ? dir.slice(0, -1) : dir;
255
+ if (normalized === "/") {
256
+ return isAbsolute(path);
257
+ }
258
+ return path === normalized || path.startsWith(normalized + "/");
259
+ }
260
+
261
+ /**
262
+ * Deny a target that lands in (or on) a protected service path. Both the
263
+ * lexical and symlink-resolved target are checked against both the lexical
264
+ * and symlink-resolved protected path, so neither a symlinked target nor a
265
+ * symlinked directory prefix can mask the hit.
266
+ */
267
+ function securityDirDenial(target: SandboxTarget): PathResult | null {
268
+ for (const protectedPath of getProtectedServicePaths()) {
269
+ const realProtectedPath = resolveRealPath(protectedPath);
270
+ const hit = [target.resolved, target.realResolved].some(
271
+ (path) =>
272
+ isWithinDir(path, protectedPath) ||
273
+ isWithinDir(path, realProtectedPath),
274
+ );
275
+ if (hit) {
276
+ return {
277
+ ok: false,
278
+ reason: "denied",
279
+ error: "Access to the service security directory is denied",
280
+ };
281
+ }
282
+ }
283
+ return null;
284
+ }
285
+
286
+ function evaluateSandboxPolicy(
287
+ rawPath: string,
288
+ boundaryDir: string,
289
+ options: { mustExist?: boolean } | undefined,
290
+ allowOutOfBounds: boolean,
291
+ ): PathResult {
292
+ const target = resolveSandboxTarget(
293
+ rawPath,
294
+ boundaryDir,
295
+ options?.mustExist ?? true,
296
+ );
297
+
298
+ if (!allowOutOfBounds && isOutOfBounds(target)) {
299
+ return outOfBoundsFailure(rawPath, target);
300
+ }
301
+
302
+ // Applied unconditionally — even a boundary configured to contain a
303
+ // service security dir must not expose it through the file tools.
304
+ const securityDenied = securityDirDenial(target);
305
+ if (securityDenied) {
306
+ return securityDenied;
307
+ }
308
+
309
+ const denied = deniedFailure(target);
310
+ if (denied) {
311
+ return denied;
312
+ }
313
+
314
+ return { ok: true, resolved: target.resolved };
315
+ }
316
+
317
+ /**
318
+ * Resolve a user-supplied path against a boundary directory and verify
319
+ * that the result stays within it.
320
+ *
321
+ * For existing paths, symlinks are resolved via realpathSync so a symlink
322
+ * pointing outside the boundary is caught. For new paths (e.g. file_write),
323
+ * pass `mustExist: false` - the nearest existing ancestor directory is
324
+ * resolved via realpathSync to catch symlinks in parent dirs.
325
+ *
326
+ * Paths starting with `/workspace/` are treated as container-scoped and
327
+ * remapped relative to the boundary directory (the Docker sandbox mounts
328
+ * the host workspace at /workspace).
329
+ */
330
+ export function sandboxPolicy(
331
+ rawPath: string,
332
+ boundaryDir: string,
333
+ options?: { mustExist?: boolean },
334
+ ): PathResult {
335
+ return evaluateSandboxPolicy(rawPath, boundaryDir, options, false);
336
+ }
337
+
338
+ /**
339
+ * Sandbox policy that permits out-of-workspace targets on non-containerized
340
+ * installs.
341
+ *
342
+ * Identical to {@link sandboxPolicy} for in-bounds targets. A target that
343
+ * escapes the boundary is allowed with host-style validation: the basename
344
+ * denylist still applies to both the logical and symlink-resolved paths, and
345
+ * the service security directories stay denied outright.
346
+ * The permission lane runs before tool execution and classifies
347
+ * out-of-workspace file operations as elevated risk (see the gateway
348
+ * FileRiskClassifier), so an escape reaching this policy has already been
349
+ * threshold-approved or user-approved.
350
+ *
351
+ * In containerized mode the boundary stays hard: the container filesystem is
352
+ * not the host, and the host_file_* proxy tools are the escape hatch for the
353
+ * guardian's device.
354
+ */
355
+ export function sandboxPolicyWithHostFallback(
356
+ rawPath: string,
357
+ boundaryDir: string,
358
+ options?: { mustExist?: boolean },
359
+ ): PathResult {
360
+ return evaluateSandboxPolicy(
361
+ rawPath,
362
+ boundaryDir,
363
+ options,
364
+ !getIsContainerized() && securityDirConfigMirrorable(),
365
+ );
160
366
  }
161
367
 
162
368
  // ---------------------------------------------------------------------------
@@ -0,0 +1,117 @@
1
+ import { z } from "zod";
2
+
3
+ import type { ToolExecutionResult } from "../types.js";
4
+
5
+ /**
6
+ * Derive a tool's LLM-facing `input_schema` from its Zod input schema, so the
7
+ * schema the model sees and the validation the executor runs share one source
8
+ * and cannot drift (`TOOL_INPUT_SCHEMAS` in `../tool-input-schemas.ts` holds
9
+ * the executor-side half).
10
+ *
11
+ * Conversion uses Zod 4's native `z.toJSONSchema` (mirroring
12
+ * `workflows/leaf-runner.ts`), then adjusts the output for the tool-definition
13
+ * context:
14
+ *
15
+ * - The `$schema` marker is dropped — tool definitions don't carry one.
16
+ * - `additionalProperties` values of `false` / `{}` are dropped. They are
17
+ * authoring artifacts of `z.object` / `z.looseObject`, not intentional
18
+ * contract: runtime validation never rejects unknown keys (registry schemas
19
+ * must tolerate injected fields like `activity`, see
20
+ * `schema-transforms.ts`), so advertising `additionalProperties: false`
21
+ * would claim a strictness the runtime doesn't enforce.
22
+ *
23
+ * `advertiseRequired` names fields to mark required in the advertised schema
24
+ * even though the Zod schema treats them as optional. This is the one
25
+ * sanctioned divergence, and only in the tolerant direction: the model is told
26
+ * to always send the field (e.g. `activity`, which drives status UX), but a
27
+ * call that omits it still executes rather than failing on a field the tool
28
+ * doesn't need.
29
+ */
30
+ export function toToolInputSchema(
31
+ schema: z.ZodType,
32
+ opts?: { advertiseRequired?: readonly string[] },
33
+ ): Record<string, unknown> {
34
+ const json = z.toJSONSchema(schema, { unrepresentable: "any" }) as Record<
35
+ string,
36
+ unknown
37
+ >;
38
+ delete json.$schema;
39
+ stripPermissiveAdditionalProperties(json);
40
+
41
+ for (const field of opts?.advertiseRequired ?? []) {
42
+ const required = Array.isArray(json.required) ? json.required : [];
43
+ if (!required.includes(field)) {
44
+ json.required = [...required, field];
45
+ }
46
+ }
47
+ return json;
48
+ }
49
+
50
+ /**
51
+ * Recursively remove `additionalProperties` keys whose value is `false` or the
52
+ * empty schema `{}`. A real subschema (e.g. `additionalProperties:
53
+ * { type: "string" }` from `z.record`) is left intact.
54
+ */
55
+ function stripPermissiveAdditionalProperties(node: unknown): void {
56
+ if (Array.isArray(node)) {
57
+ for (const entry of node) {
58
+ stripPermissiveAdditionalProperties(entry);
59
+ }
60
+ return;
61
+ }
62
+ if (node === null || typeof node !== "object") {
63
+ return;
64
+ }
65
+ const record = node as Record<string, unknown>;
66
+ const ap = record.additionalProperties;
67
+ if (
68
+ ap === false ||
69
+ (typeof ap === "object" &&
70
+ ap !== null &&
71
+ !Array.isArray(ap) &&
72
+ Object.keys(ap).length === 0)
73
+ ) {
74
+ delete record.additionalProperties;
75
+ }
76
+ for (const value of Object.values(record)) {
77
+ stripPermissiveAdditionalProperties(value);
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Wrap an optional field's schema so an explicit `null` parses as "omitted"
83
+ * (`undefined`). Models routinely send `null` for optional fields they mean
84
+ * to skip, and executors read such fields via `input.x ?? fallback`, where
85
+ * null and absent are equivalent — validation must not reject null there.
86
+ * The derived JSON Schema is the inner schema's (the null-tolerance is
87
+ * runtime slack, not advertised contract).
88
+ */
89
+ export function nullAsOmitted<T extends z.ZodType>(schema: T) {
90
+ return z.preprocess((value) => value ?? undefined, schema.optional());
91
+ }
92
+
93
+ /**
94
+ * Render a failed tool-input parse as a compact `field: message` summary the
95
+ * model can correct from. Mirrors `runtime/routes/parse-body.ts`.
96
+ */
97
+ export function formatToolInputError(
98
+ toolName: string,
99
+ error: z.ZodError,
100
+ ): string {
101
+ const detail = error.issues
102
+ .map((issue) => `${issue.path.join(".") || "(input)"}: ${issue.message}`)
103
+ .join("; ");
104
+ return `Invalid input for tool "${toolName}": ${detail}. Fix the listed field(s) and retry the call.`;
105
+ }
106
+
107
+ /**
108
+ * Failed-parse tool result for executors that validate their own input (the
109
+ * pre-execution gate in `ToolApprovalHandler` produces the same message, so
110
+ * the error reads identically whichever layer catches it first).
111
+ */
112
+ export function invalidToolInputResult(
113
+ toolName: string,
114
+ error: z.ZodError,
115
+ ): ToolExecutionResult {
116
+ return { content: formatToolInputError(toolName, error), isError: true };
117
+ }
@@ -1,15 +1,23 @@
1
1
  import { getSubagentManager } from "../../subagent/index.js";
2
+ import { invalidToolInputResult } from "../shared/zod-tool-schema.js";
2
3
  import type { ToolContext, ToolExecutionResult } from "../types.js";
3
- import { resolveSubagentId } from "./resolve.js";
4
+ import { resolveSubagentId, subagentRefInputSchema } from "./resolve.js";
5
+
6
+ export const subagentAbortInputSchema = subagentRefInputSchema;
4
7
 
5
8
  export async function executeSubagentAbort(
6
9
  input: Record<string, unknown>,
7
10
  context: ToolContext,
8
11
  ): Promise<ToolExecutionResult> {
9
- const subagentId = resolveSubagentId(input, context);
10
- if (!subagentId && input.label) {
12
+ const parsedInput = subagentAbortInputSchema.safeParse(input);
13
+ if (!parsedInput.success) {
14
+ return invalidToolInputResult("subagent_abort", parsedInput.error);
15
+ }
16
+ const parsed = parsedInput.data;
17
+ const subagentId = resolveSubagentId(parsed, context);
18
+ if (!subagentId && parsed.label) {
11
19
  return {
12
- content: `No subagent found with label "${input.label as string}".`,
20
+ content: `No subagent found with label "${parsed.label}".`,
13
21
  isError: true,
14
22
  };
15
23
  }
@@ -29,8 +29,12 @@ export function createConsultDeadline(opts: {
29
29
 
30
30
  const recordProgress = (): void => {
31
31
  // Once aborted, don't re-arm — the consult is already being torn down.
32
- if (controller.signal.aborted) return;
33
- if (idleTimer) clearTimeout(idleTimer);
32
+ if (controller.signal.aborted) {
33
+ return;
34
+ }
35
+ if (idleTimer) {
36
+ clearTimeout(idleTimer);
37
+ }
34
38
  idleTimer = setTimeout(() => controller.abort(), opts.idleMs);
35
39
  };
36
40
 
@@ -41,7 +45,9 @@ export function createConsultDeadline(opts: {
41
45
  recordProgress();
42
46
 
43
47
  const dispose = (): void => {
44
- if (idleTimer) clearTimeout(idleTimer);
48
+ if (idleTimer) {
49
+ clearTimeout(idleTimer);
50
+ }
45
51
  clearTimeout(maxTimer);
46
52
  };
47
53
 
@@ -1,17 +1,33 @@
1
+ import { z } from "zod";
2
+
1
3
  import { getSubagentManager } from "../../subagent/index.js";
4
+ import {
5
+ invalidToolInputResult,
6
+ nullAsOmitted,
7
+ } from "../shared/zod-tool-schema.js";
2
8
  import type { ToolContext, ToolExecutionResult } from "../types.js";
3
- import { resolveSubagentId } from "./resolve.js";
9
+ import { resolveSubagentId, subagentRefInputSchema } from "./resolve.js";
10
+
11
+ export const subagentMessageInputSchema = z.looseObject({
12
+ ...subagentRefInputSchema.shape,
13
+ content: nullAsOmitted(z.string()),
14
+ });
4
15
 
5
16
  export async function executeSubagentMessage(
6
17
  input: Record<string, unknown>,
7
18
  context: ToolContext,
8
19
  ): Promise<ToolExecutionResult> {
9
- const subagentId = resolveSubagentId(input, context);
10
- const content = input.content as string;
20
+ const parsedInput = subagentMessageInputSchema.safeParse(input);
21
+ if (!parsedInput.success) {
22
+ return invalidToolInputResult("subagent_message", parsedInput.error);
23
+ }
24
+ const parsed = parsedInput.data;
25
+ const subagentId = resolveSubagentId(parsed, context);
26
+ const content = parsed.content;
11
27
 
12
- if (!subagentId && input.label) {
28
+ if (!subagentId && parsed.label) {
13
29
  return {
14
- content: `No subagent found with label "${input.label as string}".`,
30
+ content: `No subagent found with label "${parsed.label}".`,
15
31
  isError: true,
16
32
  };
17
33
  }
@@ -1,17 +1,56 @@
1
+ import { z } from "zod";
2
+
1
3
  import { RiskLevel } from "../../permissions/types.js";
2
4
  import { notifyParentFromChild } from "../../subagent/notify.js";
5
+ import {
6
+ invalidToolInputResult,
7
+ nullAsOmitted,
8
+ toToolInputSchema,
9
+ } from "../shared/zod-tool-schema.js";
3
10
  import type {
4
11
  ToolContext,
5
12
  ToolDefinition,
6
13
  ToolExecutionResult,
7
14
  } from "../types.js";
8
15
 
16
+ /**
17
+ * Model-input schema, the single source for both runtime validation (via
18
+ * `TOOL_INPUT_SCHEMAS`) and the advertised `input_schema` below. `message`
19
+ * stays runtime-optional (`advertiseRequired`) so the bespoke
20
+ * '"message" is required.' check keeps its test-asserted error; `urgency`
21
+ * catches to `undefined` so a malformed value degrades to "info" instead of
22
+ * forwarding garbage to the parent's notification.
23
+ */
24
+ export const notifyParentInputSchema = z.looseObject({
25
+ message: nullAsOmitted(
26
+ z.string().describe("The notification content for the parent."),
27
+ ),
28
+ urgency: z
29
+ .enum(["info", "important", "blocked"])
30
+ .describe(
31
+ "'info' for progress updates, 'important' for key findings, 'blocked' when you need guidance.",
32
+ )
33
+ .optional()
34
+ .catch(undefined),
35
+ activity: z
36
+ .string()
37
+ .describe(
38
+ "Brief non-technical explanation of what you are doing and why, shown as a status update.",
39
+ )
40
+ .optional()
41
+ .catch(undefined),
42
+ });
43
+
9
44
  export async function executeSubagentNotifyParent(
10
45
  input: Record<string, unknown>,
11
46
  context: ToolContext,
12
47
  ): Promise<ToolExecutionResult> {
13
- const message = input.message as string;
14
- const urgency = (input.urgency as string) || "info";
48
+ const parsed = notifyParentInputSchema.safeParse(input);
49
+ if (!parsed.success) {
50
+ return invalidToolInputResult("notify_parent", parsed.error);
51
+ }
52
+ const message = parsed.data.message;
53
+ const urgency = parsed.data.urgency ?? "info";
15
54
 
16
55
  if (!message) {
17
56
  return { content: '"message" is required.', isError: true };
@@ -41,27 +80,9 @@ export const notifyParentTool = {
41
80
  executionTarget: "sandbox",
42
81
  defaultRiskLevel: RiskLevel.Low,
43
82
 
44
- input_schema: {
45
- type: "object",
46
- properties: {
47
- message: {
48
- type: "string",
49
- description: "The notification content for the parent.",
50
- },
51
- urgency: {
52
- type: "string",
53
- enum: ["info", "important", "blocked"],
54
- description:
55
- "'info' for progress updates, 'important' for key findings, 'blocked' when you need guidance.",
56
- },
57
- activity: {
58
- type: "string",
59
- description:
60
- "Brief non-technical explanation of what you are doing and why, shown as a status update.",
61
- },
62
- },
63
- required: ["message", "activity"],
64
- },
83
+ input_schema: toToolInputSchema(notifyParentInputSchema, {
84
+ advertiseRequired: ["message", "activity"],
85
+ }),
65
86
 
66
87
  async execute(
67
88
  input: Record<string, unknown>,
@@ -1,17 +1,28 @@
1
1
  import { getMessages } from "../../persistence/conversation-crud.js";
2
2
  import { extractTextFromStoredMessageContent } from "../../persistence/message-content.js";
3
3
  import { getSubagentManager, TERMINAL_STATUSES } from "../../subagent/index.js";
4
+ import { invalidToolInputResult } from "../shared/zod-tool-schema.js";
4
5
  import type { ToolContext, ToolExecutionResult } from "../types.js";
5
- import { resolveSubagentId } from "./resolve.js";
6
+ import { resolveSubagentId, subagentRefInputSchema } from "./resolve.js";
7
+
8
+ // `last_n` is deliberately UNDECLARED (loose passthrough): the executor's
9
+ // typeof-guarded read below ignores it when malformed — including non-integer
10
+ // numbers the advertised `integer` type wouldn't admit.
11
+ export const subagentReadInputSchema = subagentRefInputSchema;
6
12
 
7
13
  export async function executeSubagentRead(
8
14
  input: Record<string, unknown>,
9
15
  context: ToolContext,
10
16
  ): Promise<ToolExecutionResult> {
11
- const subagentId = resolveSubagentId(input, context);
12
- if (!subagentId && input.label) {
17
+ const parsedInput = subagentReadInputSchema.safeParse(input);
18
+ if (!parsedInput.success) {
19
+ return invalidToolInputResult("subagent_read", parsedInput.error);
20
+ }
21
+ const parsed = parsedInput.data;
22
+ const subagentId = resolveSubagentId(parsed, context);
23
+ if (!subagentId && parsed.label) {
13
24
  return {
14
- content: `No subagent found with label "${input.label as string}".`,
25
+ content: `No subagent found with label "${parsed.label}".`,
15
26
  isError: true,
16
27
  };
17
28
  }