@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
@@ -2,15 +2,32 @@ import { realpathSync } from "node:fs";
2
2
  import { basename, dirname, normalize, resolve } from "node:path";
3
3
 
4
4
  import { getIsContainerized } from "../config/env-registry.js";
5
+ import { resolveTrailingLinkTarget } from "../util/fs-symlinks.js";
6
+ import {
7
+ getMonitoringDataDir,
8
+ getWorkspaceHooksDir,
9
+ getWorkspacePluginsDir,
10
+ getWorkspaceRoutesDir,
11
+ getWorkspaceSkillsDir,
12
+ getWorkspaceToolsDir,
13
+ getWorkspaceWorkflowsDir,
14
+ } from "../util/platform.js";
5
15
 
6
16
  /**
7
- * Resolve a path to its canonical form. When the target itself doesn't
8
- * exist (e.g. a new file being written), walk up to the nearest existing
9
- * ancestor and append the remaining segments so that symlinks in parent
10
- * directories (like macOS `/var` -> `/private/var`) are still resolved.
17
+ * Resolve a path to its canonical form. A trailing (possibly dangling)
18
+ * symlink chain is followed first so a link whose destination does not
19
+ * exist yet still canonicalizes to that destination — a write through the
20
+ * link creates it. When the target itself doesn't exist (e.g. a new file
21
+ * being written), walk up to the nearest existing ancestor and append the
22
+ * remaining segments so that symlinks in parent directories (like macOS
23
+ * `/var` -> `/private/var`) are still resolved.
11
24
  */
12
25
  function canonicalize(p: string): string {
13
- const abs = resolve(p);
26
+ const abs = resolveTrailingLinkTarget(resolve(p));
27
+ return canonicalizeExisting(abs);
28
+ }
29
+
30
+ function canonicalizeExisting(abs: string): string {
14
31
  try {
15
32
  return realpathSync(abs);
16
33
  } catch {
@@ -21,10 +38,24 @@ function canonicalize(p: string): string {
21
38
  // Reached filesystem root — nothing left to resolve.
22
39
  return normalize(abs);
23
40
  }
24
- return `${canonicalize(parent)}/${name}`;
41
+ return `${canonicalizeExisting(parent)}/${name}`;
25
42
  }
26
43
  }
27
44
 
45
+ /**
46
+ * Whether a canonical path is a canonical directory or falls under it. The
47
+ * trailing separator keeps `/workspace-extra` from matching `/workspace`.
48
+ * Both arguments must already be canonicalized — comparing a canonical path
49
+ * against a lexical directory would re-open the symlink dodge.
50
+ */
51
+ function isAtOrUnderCanonicalDir(
52
+ canonicalPath: string,
53
+ canonicalDir: string,
54
+ ): boolean {
55
+ const prefix = canonicalDir.endsWith("/") ? canonicalDir : `${canonicalDir}/`;
56
+ return canonicalPath === canonicalDir || canonicalPath.startsWith(prefix);
57
+ }
58
+
28
59
  /**
29
60
  * Resolve a file path to its canonical form (resolving symlinks and
30
61
  * normalizing segments like `.` and `..`), then check whether it falls
@@ -34,19 +65,12 @@ export function isPathWithinWorkspaceRoot(
34
65
  filePath: string,
35
66
  workspaceRoot: string,
36
67
  ): boolean {
37
- if (!filePath || !workspaceRoot) return false;
38
-
39
- const canonicalPath = canonicalize(filePath);
40
- const canonicalRoot = canonicalize(workspaceRoot);
41
-
42
- // Ensure the root ends with a separator so `/workspace-extra` doesn't
43
- // match `/workspace`.
44
- const rootPrefix = canonicalRoot.endsWith("/")
45
- ? canonicalRoot
46
- : `${canonicalRoot}/`;
47
-
48
- return (
49
- canonicalPath === canonicalRoot || canonicalPath.startsWith(rootPrefix)
68
+ if (!filePath || !workspaceRoot) {
69
+ return false;
70
+ }
71
+ return isAtOrUnderCanonicalDir(
72
+ canonicalize(filePath),
73
+ canonicalize(workspaceRoot),
50
74
  );
51
75
  }
52
76
 
@@ -55,6 +79,14 @@ export function isPathWithinWorkspaceRoot(
55
79
  /** File-path tools whose workspace-scoped-ness depends on the file_path input. */
56
80
  const PATH_SCOPED_TOOLS = new Set(["file_read", "file_write", "file_edit"]);
57
81
 
82
+ /** Sandbox file tools that write. Reads never plant code. */
83
+ const WORKSPACE_WRITE_TOOLS = new Set(["file_write", "file_edit"]);
84
+
85
+ /** Whether a tool is a sandbox file tool that writes. */
86
+ export function isWorkspaceWriteTool(toolName: string): boolean {
87
+ return WORKSPACE_WRITE_TOOLS.has(toolName);
88
+ }
89
+
58
90
  /** Network-accessing tools — never workspace-scoped. */
59
91
  const NETWORK_TOOLS = new Set(["web_search", "web_fetch", "network_request"]);
60
92
 
@@ -76,6 +108,178 @@ const ALWAYS_SCOPED_TOOLS = new Set([
76
108
  "ui_dismiss",
77
109
  ]);
78
110
 
111
+ // The Docker sandbox mounts the workspace at /workspace, and the model emits
112
+ // container-scoped paths (e.g. "/workspace/tools/evil.ts") even on local
113
+ // turns. The execution-time path policy and the gateway classifier both
114
+ // apply this remap, so every workspace-containment decision here must too.
115
+ const CONTAINER_WORKSPACE_PREFIX = "/workspace/";
116
+ const CONTAINER_WORKSPACE_EXACT = "/workspace";
117
+
118
+ /**
119
+ * Resolve a sandbox file path to its lexical base the same way the
120
+ * execution-time `sandboxPolicy` and the gateway classifier do: remap a
121
+ * container-scoped `/workspace/...` path onto `workingDir`, then resolve
122
+ * (relative paths resolve against `workingDir`, not process.cwd()).
123
+ */
124
+ export function resolveSandboxBase(
125
+ rawPath: string,
126
+ workingDir: string,
127
+ ): string {
128
+ let effectivePath = rawPath;
129
+ if (!rawPath.startsWith(workingDir + "/") && rawPath !== workingDir) {
130
+ if (rawPath.startsWith(CONTAINER_WORKSPACE_PREFIX)) {
131
+ effectivePath = rawPath.slice(CONTAINER_WORKSPACE_PREFIX.length);
132
+ } else if (rawPath === CONTAINER_WORKSPACE_EXACT) {
133
+ effectivePath = ".";
134
+ }
135
+ }
136
+ return resolve(workingDir, effectivePath);
137
+ }
138
+
139
+ /**
140
+ * Extract a path-scoped tool's target path from its input. `path` takes
141
+ * priority over `file_path` — the file tools execute `input.path` and the
142
+ * risk classifier reads the fields in the same order, so containment
143
+ * decisions must be derived from the field that actually executes (an input
144
+ * carrying both fields is not runtime-stripped). Returns `""` when the
145
+ * input carries no usable path.
146
+ */
147
+ function resolvePathScopedTarget(
148
+ toolInput: Record<string, unknown>,
149
+ workspaceRoot: string,
150
+ ): string {
151
+ const rawPath =
152
+ typeof toolInput.path === "string"
153
+ ? toolInput.path
154
+ : typeof toolInput.file_path === "string"
155
+ ? toolInput.file_path
156
+ : "";
157
+ if (rawPath === "") {
158
+ return "";
159
+ }
160
+ return resolveSandboxBase(rawPath, workspaceRoot);
161
+ }
162
+
163
+ /**
164
+ * Whether a sandbox file-tool invocation targets a path outside the
165
+ * workspace root. Always false in containerized mode — the execution-time
166
+ * boundary is hard there, so an escaping path never executes. Non-path
167
+ * tools are never classified by this predicate.
168
+ */
169
+ export function isOutOfWorkspaceFileInvocation(
170
+ toolName: string,
171
+ toolInput: Record<string, unknown>,
172
+ workspaceRoot: string,
173
+ ): boolean {
174
+ if (getIsContainerized()) {
175
+ return false;
176
+ }
177
+ if (!PATH_SCOPED_TOOLS.has(toolName)) {
178
+ return false;
179
+ }
180
+ const filePath = resolvePathScopedTarget(toolInput, workspaceRoot);
181
+ return filePath !== "" && !isPathWithinWorkspaceRoot(filePath, workspaceRoot);
182
+ }
183
+
184
+ /**
185
+ * The workspace directories the daemon imports and executes — hooks,
186
+ * plugins, skills, tools, routes, workflows — plus the monitoring data
187
+ * directory, whose source-versions sentinel steers which plugin code the
188
+ * daemon imports. A write to any of them is code that runs later with the
189
+ * daemon's own reach. The list mirrors the file risk classifier's
190
+ * code-injection sinks.
191
+ */
192
+ function executableSinkDirs(): string[] {
193
+ return [
194
+ getWorkspaceHooksDir(),
195
+ getWorkspacePluginsDir(),
196
+ getWorkspaceSkillsDir(),
197
+ getWorkspaceToolsDir(),
198
+ getWorkspaceRoutesDir(),
199
+ getWorkspaceWorkflowsDir(),
200
+ getMonitoringDataDir(),
201
+ ];
202
+ }
203
+
204
+ /**
205
+ * Workspace-root files and directories the prompt renderer reads into the
206
+ * system prompt at render time (`prompts/templates/system-sections.ts`
207
+ * `workspacePath` entries — a drift-guard test walks the real section list
208
+ * against this predicate). A write to any of them rewrites the assistant's
209
+ * standing instructions, per-user context, or per-channel context.
210
+ */
211
+ const PROMPT_SURFACE_FILES = [
212
+ "IDENTITY.md",
213
+ "SOUL.md",
214
+ "VOICE.md",
215
+ "BOOTSTRAP.md",
216
+ // Read by the heartbeat service as its checklist — instructions executed
217
+ // unattended (`runtime/routes/heartbeat-routes.ts`).
218
+ "HEARTBEAT.md",
219
+ // The scratchpad the heartbeat checklist reads its to-dos from, injected
220
+ // into every full-mode guardian turn by the `now-md` injector
221
+ // (`plugins/defaults/workspace/injectors.ts`). Injector-fed rather than a
222
+ // system section, so the drift guard cannot see it — this entry is the
223
+ // coverage.
224
+ "NOW.md",
225
+ ];
226
+ const PROMPT_SURFACE_DIRS = ["users", "channels"];
227
+
228
+ // `memory/**` is deliberately data-plane, not control-plane: memory pages
229
+ // inject into guardian sessions as past-record rather than standing
230
+ // instructions, consolidation owns and rewrites those files, and the gated
231
+ // write path is `remember()` (provenance-checked in the indexer). If direct
232
+ // file writes to memory pages ever become a delegation surface, they join
233
+ // this list.
234
+
235
+ /**
236
+ * Whether a sandbox file-tool invocation writes a workspace control-plane
237
+ * target: an executable sink directory (code the daemon executes) or a
238
+ * prompt surface (instructions it obeys). The two categories are one
239
+ * delegation a layer apart — approving the write approves everything the
240
+ * planted code or rewritten instructions cause later.
241
+ *
242
+ * Unlike {@link isOutOfWorkspaceFileInvocation} this holds in containerized
243
+ * mode too: the workspace boundary is what contains an escaping *path*, and
244
+ * these paths do not escape — the daemon acts on them from inside.
245
+ *
246
+ * Both sides are canonicalized before comparing, for the same reason
247
+ * {@link isPathWithinWorkspaceRoot} canonicalizes: a symlink whose name
248
+ * looks benign but whose target is a control-plane path is a write to that
249
+ * path, and a lexical prefix check does not see it.
250
+ */
251
+ export function isControlPlaneWorkspaceWrite(
252
+ toolName: string,
253
+ toolInput: Record<string, unknown>,
254
+ workspaceRoot: string,
255
+ ): boolean {
256
+ if (!isWorkspaceWriteTool(toolName)) {
257
+ return false;
258
+ }
259
+ const filePath = resolvePathScopedTarget(toolInput, workspaceRoot);
260
+ if (filePath === "") {
261
+ return false;
262
+ }
263
+ const target = canonicalize(filePath);
264
+ const root = canonicalize(workspaceRoot);
265
+ // The baselines are canonicalized too, not just the target: a prompt
266
+ // surface may itself be a symlink (SOUL.md → personas/current.md), and the
267
+ // renderer reads through it — so a write to either name must match. A
268
+ // lexical baseline would miss both the through-link write (target
269
+ // canonicalizes past it) and the direct write to the link's destination.
270
+ return (
271
+ executableSinkDirs().some((dir) =>
272
+ isAtOrUnderCanonicalDir(target, canonicalize(dir)),
273
+ ) ||
274
+ PROMPT_SURFACE_FILES.some(
275
+ (file) => target === canonicalize(`${root}/${file}`),
276
+ ) ||
277
+ PROMPT_SURFACE_DIRS.some((dir) =>
278
+ isAtOrUnderCanonicalDir(target, canonicalize(`${root}/${dir}`)),
279
+ )
280
+ );
281
+ }
282
+
79
283
  /**
80
284
  * Determine whether a tool invocation only affects resources within the
81
285
  * workspace root. This is a conservative classification — unknown tools
@@ -86,22 +290,18 @@ export function isWorkspaceScopedInvocation(
86
290
  toolInput: Record<string, unknown>,
87
291
  workspaceRoot: string,
88
292
  ): boolean {
89
- if (ALWAYS_SCOPED_TOOLS.has(toolName)) return true;
90
- if (NETWORK_TOOLS.has(toolName)) return false;
91
- if (HOST_TOOLS.has(toolName)) return false;
293
+ if (ALWAYS_SCOPED_TOOLS.has(toolName)) {
294
+ return true;
295
+ }
296
+ if (NETWORK_TOOLS.has(toolName)) {
297
+ return false;
298
+ }
299
+ if (HOST_TOOLS.has(toolName)) {
300
+ return false;
301
+ }
92
302
 
93
303
  if (PATH_SCOPED_TOOLS.has(toolName)) {
94
- const rawPath =
95
- typeof toolInput.file_path === "string"
96
- ? toolInput.file_path
97
- : typeof toolInput.path === "string"
98
- ? toolInput.path
99
- : "";
100
- // Resolve relative paths against workspaceRoot (not process.cwd())
101
- const filePath =
102
- rawPath !== "" && !rawPath.startsWith("/")
103
- ? resolve(workspaceRoot, rawPath)
104
- : rawPath;
304
+ const filePath = resolvePathScopedTarget(toolInput, workspaceRoot);
105
305
  return (
106
306
  filePath !== "" && isPathWithinWorkspaceRoot(filePath, workspaceRoot)
107
307
  );
@@ -112,7 +312,9 @@ export function isWorkspaceScopedInvocation(
112
312
  // bash is NOT workspace-scoped here — path resolution for allowlisted commands is
113
313
  // handled upstream in the checker's hasSandboxAutoApprove computation, which validates
114
314
  // all path arguments against the workspace root for non-containerized environments.
115
- if (toolName === "bash") return getIsContainerized();
315
+ if (toolName === "bash") {
316
+ return getIsContainerized();
317
+ }
116
318
 
117
319
  // Unknown tool — conservative default.
118
320
  return false;
@@ -0,0 +1,82 @@
1
+ /**
2
+ * The quiesce lease is the safety-critical piece of `vellum sleep --wait`:
3
+ * a stuck or misread lease would silently pause heartbeats, schedules, and
4
+ * memory jobs. These tests pin the fail-open contract (missing, malformed,
5
+ * or expired lease reads as "not quiesced") and the TTL clamps.
6
+ */
7
+ import { beforeEach, describe, expect, test } from "bun:test";
8
+
9
+ import { setMemoryCheckpoint } from "../checkpoints.js";
10
+ import { initializeDb } from "../db-init.js";
11
+ import {
12
+ clearLifecycleQuiesce,
13
+ DEFAULT_QUIESCE_TTL_MS,
14
+ getLifecycleQuiesceUntil,
15
+ isLifecycleQuiesced,
16
+ MAX_QUIESCE_TTL_MS,
17
+ MIN_QUIESCE_TTL_MS,
18
+ setLifecycleQuiesce,
19
+ } from "../lifecycle-quiesce.js";
20
+ import { resetTestTables } from "../raw-query.js";
21
+
22
+ const QUIESCE_KEY = "lifecycle:quiesce_until";
23
+
24
+ await initializeDb();
25
+
26
+ beforeEach(() => {
27
+ resetTestTables("memory_checkpoints");
28
+ });
29
+
30
+ describe("lifecycle-quiesce", () => {
31
+ test("no lease reads as not quiesced (fail-open default)", () => {
32
+ expect(isLifecycleQuiesced()).toBe(false);
33
+ expect(getLifecycleQuiesceUntil()).toBeNull();
34
+ });
35
+
36
+ test("setLifecycleQuiesce arms the lease for the requested TTL", () => {
37
+ const before = Date.now();
38
+ const until = setLifecycleQuiesce(DEFAULT_QUIESCE_TTL_MS);
39
+ expect(until).toBeGreaterThanOrEqual(before + DEFAULT_QUIESCE_TTL_MS);
40
+ expect(until).toBeLessThanOrEqual(Date.now() + DEFAULT_QUIESCE_TTL_MS);
41
+ expect(isLifecycleQuiesced()).toBe(true);
42
+ expect(getLifecycleQuiesceUntil()).toBe(until);
43
+ });
44
+
45
+ test("TTL is clamped to the sane range", () => {
46
+ const beforeMin = Date.now();
47
+ const untilMin = setLifecycleQuiesce(1);
48
+ expect(untilMin).toBeGreaterThanOrEqual(beforeMin + MIN_QUIESCE_TTL_MS);
49
+
50
+ const beforeMax = Date.now();
51
+ const untilMax = setLifecycleQuiesce(Number.MAX_SAFE_INTEGER);
52
+ expect(untilMax).toBeLessThanOrEqual(
53
+ Date.now() + MAX_QUIESCE_TTL_MS + 1_000,
54
+ );
55
+ expect(untilMax).toBeGreaterThanOrEqual(beforeMax + MAX_QUIESCE_TTL_MS);
56
+ });
57
+
58
+ test("re-arming extends the lease (refresh semantics)", () => {
59
+ const first = setLifecycleQuiesce(MIN_QUIESCE_TTL_MS);
60
+ const second = setLifecycleQuiesce(MAX_QUIESCE_TTL_MS);
61
+ expect(second).toBeGreaterThan(first);
62
+ expect(getLifecycleQuiesceUntil()).toBe(second);
63
+ });
64
+
65
+ test("an expired lease reads as not quiesced", () => {
66
+ setMemoryCheckpoint(QUIESCE_KEY, String(Date.now() - 1));
67
+ expect(isLifecycleQuiesced()).toBe(false);
68
+ expect(getLifecycleQuiesceUntil()).toBeNull();
69
+ });
70
+
71
+ test("a malformed lease value reads as not quiesced (fail-open)", () => {
72
+ setMemoryCheckpoint(QUIESCE_KEY, "not-a-number");
73
+ expect(isLifecycleQuiesced()).toBe(false);
74
+ });
75
+
76
+ test("clearLifecycleQuiesce releases an active lease", () => {
77
+ setLifecycleQuiesce(DEFAULT_QUIESCE_TTL_MS);
78
+ expect(isLifecycleQuiesced()).toBe(true);
79
+ clearLifecycleQuiesce();
80
+ expect(isLifecycleQuiesced()).toBe(false);
81
+ });
82
+ });
@@ -9,6 +9,7 @@ import { mkdirSync, writeFileSync } from "node:fs";
9
9
  import { join } from "node:path";
10
10
  import { describe, expect, test } from "bun:test";
11
11
 
12
+ import { contentBlockArraySchema } from "../../providers/content-block-schema.js";
12
13
  import type { ContentBlock } from "../../providers/types.js";
13
14
  import { getWorkspaceDir } from "../../util/platform.js";
14
15
  import {
@@ -170,11 +171,10 @@ describe("resolveMessageContentBlocks", () => {
170
171
  });
171
172
 
172
173
  test("passes typed blocks outside the union through untouched", () => {
173
- // Persisted kinds like ui_surface live outside the provider union;
174
- // their renderers own their shape, so repair must not rewrite them.
174
+ // A persisted kind the union does not model still passes through: its
175
+ // renderer owns the shape, so repair must not rewrite it.
175
176
  const historical = [
176
177
  textBlock("kept as-is"),
177
- { type: "ui_surface", surfaceType: "call_summary", data: { x: 1 } },
178
178
  { type: "some_retired_block_kind", payload: 1 },
179
179
  ];
180
180
  expect(resolveMessageContentBlocks(JSON.stringify(historical))).toEqual(
@@ -182,6 +182,61 @@ describe("resolveMessageContentBlocks", () => {
182
182
  );
183
183
  });
184
184
 
185
+ test("validates ui_surface blocks without entering the repair path", () => {
186
+ // ui_surface is a first-class union member (LUM-2869). Before it was
187
+ // modelled, every read of a row holding a card failed schema validation
188
+ // and paid a per-block repair that changed nothing — the read-path noise
189
+ // that spiked with voice GA. Asserting schema success (not just a clean
190
+ // round trip) is what proves the repair path can no longer fire, since
191
+ // repair passes these blocks through unchanged either way.
192
+ const surfaceRow = [
193
+ {
194
+ type: "ui_surface",
195
+ surfaceId: "call-1",
196
+ surfaceType: "call_summary",
197
+ completed: true,
198
+ display: "inline",
199
+ data: { summaryText: "**Call completed** (42s).", events: [] },
200
+ },
201
+ {
202
+ type: "text",
203
+ text: "**Call completed** (42s).",
204
+ _surfaceFallback: true,
205
+ },
206
+ ];
207
+ expect(contentBlockArraySchema.safeParse(surfaceRow).success).toBe(true);
208
+ // `data` stays opaque and internal riders survive the round trip.
209
+ expect(resolveMessageContentBlocks(JSON.stringify(surfaceRow))).toEqual(
210
+ surfaceRow as unknown as ContentBlock[],
211
+ );
212
+ });
213
+
214
+ test("validates an LLM-emitted surface with an arbitrary display value", () => {
215
+ // Surfaces written by the `ui_show` tool carry whatever the model chose:
216
+ // `CurrentTurnSurface.display` is a bare string, NOT the inline/panel enum
217
+ // of the `ui_surface_show` wire event. Narrowing it here would send every
218
+ // LLM surface back through the per-block repair — the read-path noise this
219
+ // variant exists to stop. Daemon-only riders must survive too.
220
+ const llmSurfaceRow = [
221
+ {
222
+ type: "ui_surface",
223
+ surfaceId: "surface-1",
224
+ surfaceType: "card",
225
+ title: "Pick one",
226
+ data: { title: "Pick one", options: ["a", "b"] },
227
+ actions: [{ id: "a", label: "A" }],
228
+ display: "modal",
229
+ persistent: true,
230
+ toolCallId: "tu-1",
231
+ activationMoment: "commit",
232
+ },
233
+ ];
234
+ expect(contentBlockArraySchema.safeParse(llmSurfaceRow).success).toBe(true);
235
+ expect(resolveMessageContentBlocks(JSON.stringify(llmSurfaceRow))).toEqual(
236
+ llmSurfaceRow as unknown as ContentBlock[],
237
+ );
238
+ });
239
+
185
240
  test("wraps type-less values in a text block carrying the payload", () => {
186
241
  const historical = [{ payload: 1 }, "bare string"];
187
242
  expect(resolveMessageContentBlocks(JSON.stringify(historical))).toEqual([
@@ -677,6 +677,58 @@ interface InsertMessageCoreParams {
677
677
  * WAL contention. The timestamp is recomputed each attempt so a late
678
678
  * retry doesn't persist a stale `updatedAt`.
679
679
  */
680
+ /**
681
+ * Guard: a message whose blocks are ALL `ui_surface` cards is invisible to
682
+ * the model — every provider drops cards when serializing history, and the
683
+ * contract (see `notifications/approval-card-builder.ts`) is that the
684
+ * producer pairs a card with a model-readable sibling block (for cards whose
685
+ * meaning the model needs, a `_surfaceFallback` text block; surfaces appended
686
+ * to a normal turn ride alongside its text/tool blocks and need nothing).
687
+ *
688
+ * Voice call summaries shipped without the sibling and the model silently
689
+ * lost those turns (LUM-2869) — nothing enforced the contract. In tests this
690
+ * throws, so a producer that forgets the pairing fails its own suite before
691
+ * merge; in production it only warns, per the daemon's never-block posture —
692
+ * a degraded card row beats a dropped guardian notification.
693
+ */
694
+ function warnOnModelInvisibleContent(
695
+ content: string,
696
+ conversationId: string,
697
+ ): void {
698
+ // Fast path: the overwhelmingly common non-surface message never parses.
699
+ if (!content.includes('"ui_surface"')) {
700
+ return;
701
+ }
702
+ let parsed: unknown;
703
+ try {
704
+ parsed = JSON.parse(content);
705
+ } catch {
706
+ return; // legacy plain-string content — not this guard's concern
707
+ }
708
+ if (!Array.isArray(parsed) || parsed.length === 0) {
709
+ return;
710
+ }
711
+ const allCards = parsed.every(
712
+ (block) =>
713
+ typeof block === "object" &&
714
+ block !== null &&
715
+ (block as { type?: unknown }).type === "ui_surface",
716
+ );
717
+ if (!allCards) {
718
+ return;
719
+ }
720
+ const surfaceTypes = parsed
721
+ .map((block) => (block as { surfaceType?: unknown }).surfaceType)
722
+ .filter((t): t is string => typeof t === "string");
723
+ const message =
724
+ "Persisting a message whose only content is ui_surface blocks — the model cannot see it. " +
725
+ "Pair the card with a model-readable sibling (e.g. a _surfaceFallback text block).";
726
+ if (process.env.NODE_ENV === "test") {
727
+ throw new Error(`${message} (surfaceTypes: ${surfaceTypes.join(", ")})`);
728
+ }
729
+ log.warn({ conversationId, surfaceTypes }, message);
730
+ }
731
+
680
732
  async function insertMessageCore(
681
733
  params: InsertMessageCoreParams,
682
734
  ): Promise<InsertedMessage> {
@@ -689,6 +741,7 @@ async function insertMessageCore(
689
741
  clientMessageId,
690
742
  id,
691
743
  } = params;
744
+ warnOnModelInvisibleContent(content, conversationId);
692
745
  const db = getDb();
693
746
  // Time-ordered UUIDv7 so server-generated message ids append to the tail of
694
747
  // the WITHOUT ROWID `messages` primary key instead of scattering (v4).
@@ -2721,6 +2774,42 @@ export function isConversationProcessing(id: string): boolean {
2721
2774
  return row?.processing_started_at != null;
2722
2775
  }
2723
2776
 
2777
+ /**
2778
+ * Conversations currently mid-turn, longest-running first. Throws on a read
2779
+ * failure — drain callers must distinguish "nothing processing" from "could
2780
+ * not read", so this does not degrade to an empty list.
2781
+ */
2782
+ export function listProcessingConversations(limit = 20): Array<{
2783
+ conversationId: string;
2784
+ title: string | null;
2785
+ originChannel: string | null;
2786
+ originInterface: string | null;
2787
+ processingStartedAt: number;
2788
+ }> {
2789
+ const rows = rawAll<{
2790
+ id: string;
2791
+ title: string | null;
2792
+ origin_channel: string | null;
2793
+ origin_interface: string | null;
2794
+ processing_started_at: number;
2795
+ }>(
2796
+ "conversation:listProcessing",
2797
+ `SELECT id, title, origin_channel, origin_interface, processing_started_at
2798
+ FROM conversations
2799
+ WHERE processing_started_at IS NOT NULL
2800
+ ORDER BY processing_started_at ASC
2801
+ LIMIT ?`,
2802
+ limit,
2803
+ );
2804
+ return rows.map((row) => ({
2805
+ conversationId: row.id,
2806
+ title: row.title,
2807
+ originChannel: row.origin_channel,
2808
+ originInterface: row.origin_interface,
2809
+ processingStartedAt: row.processing_started_at,
2810
+ }));
2811
+ }
2812
+
2724
2813
  /**
2725
2814
  * Highest stream `seq` whose content is durably persisted to this
2726
2815
  * conversation's message rows, read from the `conversations.seq` column. This
@@ -3161,6 +3250,10 @@ export async function clearAll(): Promise<{
3161
3250
  await runOrThrow("DELETE FROM tool_invocations");
3162
3251
  await runOrThrow("DELETE FROM messages");
3163
3252
  await runOrThrow("DELETE FROM conversations");
3253
+ // Subagent lifecycle records reference conversations by id without an FK
3254
+ // cascade; wipe them explicitly so labels/objectives don't survive (or
3255
+ // rehydrate after) a clear-all.
3256
+ await runOrThrow("DELETE FROM subagents");
3164
3257
 
3165
3258
  // The memory feature's relocated conversation-keyed tables lost their main-DB
3166
3259
  // cascade. Signal the wipe through the hook rather than reaching into the