@vellumai/assistant 0.10.12 → 0.11.0-staging.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (809) 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 +264 -14
  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__/app-conversation-ids-backfill.test.ts +33 -2
  39. package/src/__tests__/app-conversation-ids.test.ts +204 -0
  40. package/src/__tests__/approval-cascade.test.ts +5 -5
  41. package/src/__tests__/approval-routes-http.test.ts +3 -3
  42. package/src/__tests__/ask-question-resolver.test.ts +207 -0
  43. package/src/__tests__/assistant-event-hub-self-exclusion.test.ts +16 -16
  44. package/src/__tests__/assistant-event-hub-targeted.test.ts +13 -11
  45. package/src/__tests__/assistant-event-hub.test.ts +13 -11
  46. package/src/__tests__/assistant-event.test.ts +12 -9
  47. package/src/__tests__/assistant-feature-flag-guard.test.ts +3 -3
  48. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +1 -1
  49. package/src/__tests__/assistant-id-boundary-guard.test.ts +0 -58
  50. package/src/__tests__/assistant-stream-state.test.ts +17 -7
  51. package/src/__tests__/avatar-identity-sync.test.ts +3 -3
  52. package/src/__tests__/background-workers-disk-pressure.test.ts +1 -0
  53. package/src/__tests__/call-conversation-messages.test.ts +134 -18
  54. package/src/__tests__/call-start-guardian-guard.test.ts +27 -0
  55. package/src/__tests__/channel-approvals.test.ts +0 -27
  56. package/src/__tests__/channel-guardian.test.ts +85 -512
  57. package/src/__tests__/channel-readiness-slack-remote.test.ts +151 -3
  58. package/src/__tests__/channel-retry-sweep.test.ts +88 -1
  59. package/src/__tests__/channel-setup-panel-ack.test.ts +12 -9
  60. package/src/__tests__/chat-reveal-guard-priming.test.ts +31 -34
  61. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +2 -2
  62. package/src/__tests__/compaction-events.test.ts +8 -8
  63. package/src/__tests__/config-loader-platform-defaults.test.ts +54 -28
  64. package/src/__tests__/config-model-image-provider.test.ts +20 -0
  65. package/src/__tests__/config-schema-memory-substrate.test.ts +306 -0
  66. package/src/__tests__/config-schema.test.ts +14 -2
  67. package/src/__tests__/config-set-platform-guard.test.ts +11 -12
  68. package/src/__tests__/config-sounds-sync.test.ts +4 -4
  69. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +5 -0
  70. package/src/__tests__/context-search-memory-source.test.ts +1 -1
  71. package/src/__tests__/context-search-memory-v2-source.test.ts +6 -6
  72. package/src/__tests__/context-token-estimator.test.ts +54 -1
  73. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +12 -3
  74. package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +13 -4
  75. package/src/__tests__/conversation-agent-loop-handlers-max-tokens.test.ts +4 -4
  76. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +10 -1
  77. package/src/__tests__/conversation-agent-loop-overflow.test.ts +22 -13
  78. package/src/__tests__/conversation-agent-loop.test.ts +39 -30
  79. package/src/__tests__/conversation-confirmation-signals.test.ts +14 -14
  80. package/src/__tests__/conversation-evictor.test.ts +49 -0
  81. package/src/__tests__/conversation-fork-crud.test.ts +1 -1
  82. package/src/__tests__/conversation-group-tools.test.ts +279 -0
  83. package/src/__tests__/conversation-history-web-search.test.ts +1 -1
  84. package/src/__tests__/conversation-key-store-disk-view.test.ts +5 -5
  85. package/src/__tests__/conversation-lineage.test.ts +235 -0
  86. package/src/__tests__/conversation-loop-db-lock-resilience.test.ts +5 -5
  87. package/src/__tests__/conversation-message-sync-tags.test.ts +4 -4
  88. package/src/__tests__/conversation-queue.test.ts +81 -81
  89. package/src/__tests__/conversation-routes-enabled-plugins.test.ts +8 -1
  90. package/src/__tests__/conversation-routes-hidden-queue.test.ts +8 -1
  91. package/src/__tests__/conversation-routes-slash-commands.test.ts +10 -1
  92. package/src/__tests__/conversation-runtime-assembly.test.ts +29 -2
  93. package/src/__tests__/conversation-slash-queue.test.ts +11 -11
  94. package/src/__tests__/conversation-slash-unknown.test.ts +2 -2
  95. package/src/__tests__/conversation-speed-override.test.ts +2 -2
  96. package/src/__tests__/conversation-summarize-route.test.ts +9 -0
  97. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +18 -16
  98. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +17 -17
  99. package/src/__tests__/conversation-surfaces-app-open.test.ts +7 -4
  100. package/src/__tests__/conversation-surfaces-data-persist.test.ts +7 -7
  101. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +9 -9
  102. package/src/__tests__/conversation-surfaces-standalone.test.ts +12 -12
  103. package/src/__tests__/conversation-surfaces-state-update.test.ts +8 -5
  104. package/src/__tests__/conversation-surfaces-table-action.test.ts +6 -3
  105. package/src/__tests__/conversation-surfaces-task-progress.test.ts +46 -46
  106. package/src/__tests__/conversation-sync-tags.test.ts +5 -5
  107. package/src/__tests__/conversation-wait-for-idle.test.ts +2 -2
  108. package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -4
  109. package/src/__tests__/conversation-workspace-injection.test.ts +0 -3
  110. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -3
  111. package/src/__tests__/copy-composer-tc-templates.test.ts +3 -131
  112. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  113. package/src/__tests__/daemon-assistant-events.test.ts +14 -15
  114. package/src/__tests__/db-test-helpers.ts +13 -27
  115. package/src/__tests__/disk-pressure-routes.test.ts +12 -3
  116. package/src/__tests__/document-tool-security.test.ts +95 -0
  117. package/src/__tests__/drain-kick-guard.test.ts +232 -0
  118. package/src/__tests__/dynamic-skill-background-guard.test.ts +19 -6
  119. package/src/__tests__/emit-event-signal.test.ts +5 -6
  120. package/src/__tests__/encrypted-store-test-helpers.ts +10 -20
  121. package/src/__tests__/events-tail-route.test.ts +6 -4
  122. package/src/__tests__/feature-flag-test-helpers.ts +10 -20
  123. package/src/__tests__/file-edit-tool.test.ts +24 -4
  124. package/src/__tests__/file-list-tool.test.ts +2 -1
  125. package/src/__tests__/file-read-tool.test.ts +62 -10
  126. package/src/__tests__/file-write-tool.test.ts +24 -5
  127. package/src/__tests__/followup-tools.test.ts +58 -0
  128. package/src/__tests__/gateway-threshold-reader-mock.ts +99 -0
  129. package/src/__tests__/graph-extraction-event-date.test.ts +1 -1
  130. package/src/__tests__/guardian-question-mode.test.ts +42 -3
  131. package/src/__tests__/guardian-reply-router-answer-mode.test.ts +283 -0
  132. package/src/__tests__/guardian-vellum-destination-pinning.test.ts +221 -0
  133. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  134. package/src/__tests__/heartbeat-service.test.ts +1 -0
  135. package/src/__tests__/helpers/native-web-search-harness.ts +10 -10
  136. package/src/__tests__/host-browser-proxy.test.ts +1 -1
  137. package/src/__tests__/host-file-edit-tool.test.ts +3 -3
  138. package/src/__tests__/host-file-read-tool.test.ts +2 -2
  139. package/src/__tests__/host-file-write-tool.test.ts +7 -3
  140. package/src/__tests__/host-shell-tool.test.ts +17 -5
  141. package/src/__tests__/host-transfer-pending-interactions.test.ts +8 -8
  142. package/src/__tests__/image-credentials.test.ts +65 -8
  143. package/src/__tests__/injection-block.test.ts +4 -4
  144. package/src/__tests__/injector-chain.test.ts +1 -1
  145. package/src/__tests__/injector-pkb-v2-silenced.test.ts +2 -2
  146. package/src/__tests__/injector-registry-order-guard.test.ts +4 -9
  147. package/src/__tests__/injector-v3-suppression.test.ts +6 -4
  148. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +27 -28
  149. package/src/__tests__/list-messages-page-latest.test.ts +2 -2
  150. package/src/__tests__/llm-usage-store.test.ts +67 -4
  151. package/src/__tests__/log-redact.test.ts +18 -0
  152. package/src/__tests__/managed-profile-guard.test.ts +27 -0
  153. package/src/__tests__/managed-skill-lifecycle.test.ts +2 -2
  154. package/src/__tests__/media-generate-image.test.ts +62 -11
  155. package/src/__tests__/memory-identity-context-parity.test.ts +1 -1
  156. package/src/__tests__/memory-jobs-worker-lanes.test.ts +9 -1
  157. package/src/__tests__/memory-retrieval-hook.test.ts +4 -4
  158. package/src/__tests__/notification-decision-strategy.test.ts +115 -11
  159. package/src/__tests__/notification-deep-link.test.ts +12 -12
  160. package/src/__tests__/notification-vellum-adapter.test.ts +8 -8
  161. package/src/__tests__/outbound-slack-persistence.test.ts +8 -8
  162. package/src/__tests__/path-policy.test.ts +315 -0
  163. package/src/__tests__/pending-interactions-resolved-event.test.ts +6 -6
  164. package/src/__tests__/persistence-layering-guard.test.ts +2 -2
  165. package/src/__tests__/pkb-autoinject.test.ts +1 -1
  166. package/src/__tests__/platform-bash-auto-approve.test.ts +1 -0
  167. package/src/__tests__/platform-callback-registration.test.ts +10 -10
  168. package/src/__tests__/plugin-event-hub-facade.test.ts +22 -15
  169. package/src/__tests__/plugin-import-boundary-guard.test.ts +15 -15
  170. package/src/__tests__/processing-flag-persist-failure.test.ts +0 -4
  171. package/src/__tests__/provider-platform-proxy-integration.test.ts +0 -1
  172. package/src/__tests__/rebuild-index-graph-nodes.test.ts +1 -1
  173. package/src/__tests__/request-secret-standalone-channel-gate.test.ts +5 -5
  174. package/src/__tests__/require-fresh-approval.test.ts +38 -38
  175. package/src/__tests__/resolve-app-id.test.ts +32 -0
  176. package/src/__tests__/run-due-schedules.test.ts +72 -1
  177. package/src/__tests__/runtime-events-sse-parity.test.ts +11 -10
  178. package/src/__tests__/runtime-events-sse.test.ts +1 -1
  179. package/src/__tests__/schedule-routes.test.ts +44 -5
  180. package/src/__tests__/schedule-store.test.ts +3 -3
  181. package/src/__tests__/schedule-tools.test.ts +176 -0
  182. package/src/__tests__/secret-prompt-log-hygiene.test.ts +3 -3
  183. package/src/__tests__/secret-prompter-channel-fallback.test.ts +3 -3
  184. package/src/__tests__/secret-response-routing.test.ts +3 -3
  185. package/src/__tests__/secret-routes-platform-proxy.test.ts +4 -7
  186. package/src/__tests__/secret-scanner.test.ts +36 -0
  187. package/src/__tests__/send-endpoint-busy.test.ts +7 -7
  188. package/src/__tests__/slack-required-scopes-mirror.test.ts +81 -0
  189. package/src/__tests__/starter-task-flow.test.ts +26 -3
  190. package/src/__tests__/subagent-call-site-routing.test.ts +2 -2
  191. package/src/__tests__/subagent-disposal.test.ts +40 -11
  192. package/src/__tests__/subagent-fork-notifications.test.ts +4 -4
  193. package/src/__tests__/subagent-fork-prompt-role.test.ts +3 -3
  194. package/src/__tests__/subagent-fork-spawn.test.ts +4 -4
  195. package/src/__tests__/subagent-manager-notify.test.ts +73 -14
  196. package/src/__tests__/subagent-notify-parent.test.ts +48 -4
  197. package/src/__tests__/subagent-spawn-and-await.test.ts +62 -14
  198. package/src/__tests__/subagent-tools.test.ts +64 -0
  199. package/src/__tests__/sync-message-contract.test.ts +3 -3
  200. package/src/__tests__/terminal-tools.test.ts +12 -3
  201. package/src/__tests__/test-preload.ts +9 -0
  202. package/src/__tests__/tool-approval-handler.test.ts +587 -43
  203. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +7 -4
  204. package/src/__tests__/tool-executor.test.ts +116 -0
  205. package/src/__tests__/tool-grant-request-escalation.test.ts +5 -0
  206. package/src/__tests__/tool-preview-lifecycle.test.ts +32 -29
  207. package/src/__tests__/tool-result-metadata-plumbing.test.ts +5 -5
  208. package/src/__tests__/tool-side-effects-slack-dm.test.ts +1 -0
  209. package/src/__tests__/tool-start-timestamp.test.ts +4 -4
  210. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +9 -22
  211. package/src/__tests__/ui-choice-copy-surfaces.test.ts +13 -13
  212. package/src/__tests__/ui-work-result-surface.test.ts +3 -3
  213. package/src/__tests__/voice-scoped-grant-consumer.test.ts +6 -6
  214. package/src/__tests__/voice-session-bridge.test.ts +44 -44
  215. package/src/__tests__/workspace-migration-134-image-generation-mode-to-provider.test.ts +284 -0
  216. package/src/__tests__/workspace-policy.test.ts +482 -2
  217. package/src/acp/__tests__/client-handler.test.ts +3 -3
  218. package/src/acp/__tests__/session-manager-persistence.test.ts +10 -10
  219. package/src/acp/__tests__/session-manager-resume.test.ts +7 -7
  220. package/src/acp/client-handler.ts +3 -3
  221. package/src/acp/session-manager.ts +8 -8
  222. package/src/api/events/subagent-event.ts +2 -2
  223. package/src/api/responses/memory-v2-activation-log.ts +2 -2
  224. package/src/api/responses/subagent-detail.ts +13 -0
  225. package/src/approvals/AGENTS.md +2 -0
  226. package/src/approvals/guardian-request-resolvers.ts +118 -24
  227. package/src/apps/app-store.ts +95 -4
  228. package/src/calls/__tests__/voice-control-protocol.test.ts +57 -0
  229. package/src/calls/__tests__/voice-session-bridge.test.ts +229 -7
  230. package/src/calls/__tests__/voice-triage-escalate.test.ts +3 -4
  231. package/src/calls/call-controller.ts +27 -27
  232. package/src/calls/call-conversation-messages.ts +58 -18
  233. package/src/calls/voice-control-protocol.ts +56 -0
  234. package/src/calls/voice-session-bridge.ts +178 -65
  235. package/src/calls/voice-triage-escalate.ts +2 -3
  236. package/src/channels/config.ts +8 -0
  237. package/src/channels/types.ts +1 -0
  238. package/src/cli/commands/config.ts +49 -13
  239. package/src/cli/commands/memory/index.help.ts +4 -2
  240. package/src/cli/commands/memory/memory-retrospective.ts +5 -0
  241. package/src/cli/commands/webhooks.help.ts +9 -4
  242. package/src/cli/lib/bundled-marketplace.json +12 -0
  243. package/src/cli.ts +2 -109
  244. package/src/config/__tests__/deployment-context-defaults.test.ts +9 -0
  245. package/src/config/__tests__/substrate-twin-shadowing.test.ts +191 -0
  246. package/src/config/assistant-feature-flags.ts +1 -1
  247. package/src/config/bundled-skills/conversation-groups/SKILL.md +29 -0
  248. package/src/config/bundled-skills/conversation-groups/TOOLS.json +58 -0
  249. package/src/config/bundled-skills/conversation-groups/tools/group-create.ts +12 -0
  250. package/src/config/bundled-skills/conversation-groups/tools/group-list.ts +12 -0
  251. package/src/config/bundled-skills/conversation-groups/tools/move-to-group.ts +12 -0
  252. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +13 -7
  253. package/src/config/bundled-skills/schedule/SKILL.md +2 -0
  254. package/src/config/feature-flag-cache.ts +6 -16
  255. package/src/config/feature-flag-registry.json +14 -38
  256. package/src/config/loader.ts +7 -1
  257. package/src/config/memory-tier.ts +15 -10
  258. package/src/config/memory-v3-gate.test.ts +278 -1
  259. package/src/config/memory-v3-gate.ts +88 -26
  260. package/src/config/raw-config-utils.ts +1 -1
  261. package/src/config/schemas/__tests__/live-voice.test.ts +27 -16
  262. package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
  263. package/src/config/schemas/live-voice.ts +33 -7
  264. package/src/config/schemas/llm.ts +1 -0
  265. package/src/config/schemas/memory-retrospective.ts +13 -0
  266. package/src/config/schemas/memory-substrate.ts +183 -0
  267. package/src/config/schemas/memory-v2.ts +3 -2
  268. package/src/config/schemas/memory-v3.ts +11 -4
  269. package/src/config/schemas/memory.ts +35 -1
  270. package/src/config/schemas/services.ts +10 -2
  271. package/src/config/substrate-twin-shadowing.ts +195 -0
  272. package/src/config/webhook-routing.ts +67 -0
  273. package/src/context/compactor.ts +10 -3
  274. package/src/context/token-estimator.ts +21 -0
  275. package/src/daemon/__tests__/channel-ui-capability.test.ts +85 -0
  276. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +3 -3
  277. package/src/daemon/__tests__/embedding-reconcile.test.ts +3 -3
  278. package/src/daemon/__tests__/inference-profile-notification.test.ts +3 -6
  279. package/src/daemon/__tests__/native-web-search-metadata.test.ts +5 -5
  280. package/src/daemon/__tests__/web-search-status-text.test.ts +2 -2
  281. package/src/daemon/channel-ui-capability.ts +70 -1
  282. package/src/daemon/conversation-agent-loop-handlers.ts +4 -4
  283. package/src/daemon/conversation-agent-loop.ts +12 -5
  284. package/src/daemon/conversation-evictor.ts +26 -4
  285. package/src/daemon/conversation-launch.ts +11 -14
  286. package/src/daemon/conversation-lineage.ts +76 -0
  287. package/src/daemon/conversation-messaging.ts +14 -14
  288. package/src/daemon/conversation-notifiers.ts +2 -2
  289. package/src/daemon/conversation-process.ts +133 -21
  290. package/src/daemon/conversation-queue-manager.ts +7 -7
  291. package/src/daemon/conversation-runtime-assembly.ts +16 -0
  292. package/src/daemon/conversation-store.ts +11 -3
  293. package/src/daemon/conversation-surfaces.ts +8 -14
  294. package/src/daemon/conversation-tool-setup.ts +13 -7
  295. package/src/daemon/conversation-usage.ts +12 -11
  296. package/src/daemon/conversation.ts +38 -15
  297. package/src/daemon/disk-pressure-guard.ts +5 -12
  298. package/src/daemon/embedding-reconcile.ts +14 -9
  299. package/src/daemon/handlers/config-channels.ts +40 -134
  300. package/src/daemon/handlers/config-model.ts +15 -7
  301. package/src/daemon/handlers/conversations.ts +4 -2
  302. package/src/daemon/handlers/shared.ts +29 -0
  303. package/src/daemon/host-proxy-base.ts +4 -4
  304. package/src/daemon/lifecycle.ts +13 -0
  305. package/src/daemon/message-protocol.ts +8 -144
  306. package/src/daemon/message-types/computer-use.ts +0 -12
  307. package/src/daemon/message-types/conversations.ts +9 -345
  308. package/src/daemon/message-types/host-app-control.ts +0 -9
  309. package/src/daemon/message-types/host-browser.ts +0 -7
  310. package/src/daemon/message-types/host-ui-snapshot.ts +0 -7
  311. package/src/daemon/message-types/messages.ts +0 -48
  312. package/src/daemon/message-types/notifications.ts +0 -7
  313. package/src/daemon/message-types/skills.ts +0 -5
  314. package/src/daemon/message-types/surfaces.ts +0 -9
  315. package/src/daemon/message-types/sync.ts +0 -2
  316. package/src/daemon/process-message.ts +5 -5
  317. package/src/daemon/skill-memory-refresh.ts +5 -5
  318. package/src/daemon/tool-side-effects.ts +32 -16
  319. package/src/daemon/trust-context.ts +1 -1
  320. package/src/daemon/wake-conversation-ops.ts +4 -4
  321. package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +100 -7
  322. package/src/heartbeat/__tests__/heartbeat-service.test.ts +67 -0
  323. package/src/heartbeat/heartbeat-run-store.ts +81 -4
  324. package/src/heartbeat/heartbeat-service.ts +32 -1
  325. package/src/home/__tests__/feed-writer.test.ts +7 -7
  326. package/src/home/__tests__/home-content-refresh.test.ts +3 -0
  327. package/src/home/__tests__/suggested-prompts.test.ts +1 -0
  328. package/src/home/feed-writer.ts +6 -13
  329. package/src/home/home-content-refresh.ts +6 -9
  330. package/src/home/relationship-state-writer.ts +5 -12
  331. package/src/home/suggested-prompts-cache.ts +6 -19
  332. package/src/ipc/__tests__/events-publish-client.test.ts +67 -0
  333. package/src/ipc/events-publish-client.ts +44 -0
  334. package/src/ipc/routes/__tests__/conversation-sync-ipc-routes.test.ts +2 -2
  335. package/src/ipc/routes/__tests__/events-ipc-routes.test.ts +38 -9
  336. package/src/ipc/routes/events-ipc-routes.ts +11 -31
  337. package/src/live-voice/__tests__/front-decision.test.ts +4 -176
  338. package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +276 -68
  339. package/src/live-voice/__tests__/live-voice-continuation-fork-hydration.test.ts +286 -0
  340. package/src/live-voice/__tests__/live-voice-events.test.ts +6 -3
  341. package/src/live-voice/__tests__/live-voice-progress.test.ts +185 -46
  342. package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +24 -59
  343. package/src/live-voice/__tests__/live-voice-tts-session.test.ts +38 -54
  344. package/src/live-voice/__tests__/live-voice-vad.test.ts +1154 -636
  345. package/src/live-voice/front-decision.ts +5 -155
  346. package/src/live-voice/live-voice-metrics.ts +4 -1
  347. package/src/live-voice/live-voice-session.ts +1288 -439
  348. package/src/live-voice/progress-phrases.ts +17 -0
  349. package/src/live-voice/protocol.ts +15 -0
  350. package/src/media/app-icon-generator.ts +15 -8
  351. package/src/media/avatar-router.ts +9 -5
  352. package/src/media/image-credentials.ts +42 -10
  353. package/src/media/image-service.ts +1 -13
  354. package/src/media/types.ts +26 -3
  355. package/src/notifications/AGENTS.md +2 -0
  356. package/src/notifications/__tests__/broadcaster.test.ts +95 -0
  357. package/src/notifications/adapters/macos.ts +3 -3
  358. package/src/notifications/broadcaster.ts +57 -0
  359. package/src/notifications/copy-composer.ts +0 -15
  360. package/src/notifications/decision-engine.ts +21 -22
  361. package/src/notifications/guardian-question-mode.ts +57 -2
  362. package/src/notifications/home-feed-side-effect.ts +4 -0
  363. package/src/notifications/signal.ts +6 -4
  364. package/src/notifications/trusted-contact-payloads.ts +3 -4
  365. package/src/notifications/vellum-card-affinity.ts +39 -0
  366. package/src/oauth/AGENTS.md +1 -1
  367. package/src/permissions/channel-permission-query.test.ts +69 -0
  368. package/src/permissions/channel-permission-query.ts +73 -1
  369. package/src/permissions/checker.test.ts +298 -15
  370. package/src/permissions/checker.ts +102 -36
  371. package/src/permissions/confirmation-guardian-request.test.ts +2 -2
  372. package/src/permissions/confirmation-guardian-request.ts +2 -2
  373. package/src/permissions/gateway-threshold-reader.test.ts +224 -17
  374. package/src/permissions/gateway-threshold-reader.ts +82 -10
  375. package/src/permissions/ipc-risk-types.ts +8 -0
  376. package/src/permissions/prompter.ts +5 -5
  377. package/src/permissions/question-guardian-request.test.ts +222 -0
  378. package/src/permissions/question-guardian-request.ts +179 -0
  379. package/src/permissions/question-prompter.test.ts +8 -8
  380. package/src/permissions/question-prompter.ts +18 -0
  381. package/src/permissions/secret-prompter.ts +2 -4
  382. package/src/permissions/workspace-policy.ts +269 -34
  383. package/src/persistence/__tests__/lifecycle-quiesce.test.ts +82 -0
  384. package/src/persistence/__tests__/message-content-file.test.ts +58 -3
  385. package/src/persistence/conversation-crud.ts +93 -0
  386. package/src/persistence/conversation-key-store.ts +63 -0
  387. package/src/persistence/conversation-parent.ts +27 -0
  388. package/src/persistence/db-async-query.ts +1 -1
  389. package/src/persistence/db-singleton.ts +14 -27
  390. package/src/persistence/delivery-crud.ts +45 -0
  391. package/src/persistence/embeddings/embedding-backend.ts +2 -1
  392. package/src/persistence/embeddings/embedding-identity.ts +7 -3
  393. package/src/persistence/job-utils.ts +32 -27
  394. package/src/persistence/jobs-store.ts +92 -3
  395. package/src/persistence/lifecycle-quiesce.ts +80 -0
  396. package/src/persistence/llm-usage-store.ts +45 -8
  397. package/src/persistence/message-content-file.ts +4 -4
  398. package/src/persistence/migrations/336-move-memory-v2-activation-logs-to-memory-db.ts +1 -1
  399. package/src/persistence/migrations/351-schedule-skill-script-handoff.ts +39 -0
  400. package/src/persistence/migrations/352-drop-schedule-skill-script-handoff.test.ts +83 -0
  401. package/src/persistence/migrations/352-drop-schedule-skill-script-handoff.ts +36 -0
  402. package/src/persistence/migrations/353-add-llm-usage-conversation-type.test.ts +110 -0
  403. package/src/persistence/migrations/353-add-llm-usage-conversation-type.ts +49 -0
  404. package/src/persistence/migrations/354-backfill-app-conversation-lineage.ts +41 -0
  405. package/src/persistence/schema/infrastructure.ts +11 -0
  406. package/src/persistence/steps.ts +9 -1
  407. package/src/plugin-api/__tests__/publish-event-forwarding.test.ts +113 -0
  408. package/src/plugin-api/__tests__/publish-event.test.ts +57 -0
  409. package/src/plugin-api/conversation-turn.ts +4 -4
  410. package/src/plugin-api/event-hub-facade.ts +15 -6
  411. package/src/plugin-api/index.ts +10 -4
  412. package/src/plugin-api/publish-event.ts +36 -0
  413. package/src/plugins/AGENTS.md +2 -0
  414. package/src/plugins/defaults/compaction/window-manager.ts +9 -0
  415. package/src/plugins/defaults/index.ts +9 -13
  416. package/src/plugins/defaults/memory/AGENTS.md +775 -0
  417. package/src/plugins/defaults/memory/__tests__/capability-seed-embed-gate.test.ts +186 -0
  418. package/src/plugins/defaults/memory/__tests__/fixtures/memory-v2-activation-fixtures.ts +1 -1
  419. package/src/plugins/defaults/memory/__tests__/indexer-tier-dispatch.test.ts +129 -0
  420. package/src/plugins/defaults/memory/__tests__/jobs-worker-quiesce-gate.test.ts +91 -0
  421. package/src/plugins/defaults/memory/__tests__/jobs-worker-v1-graph-substrate-noop.test.ts +342 -0
  422. package/src/plugins/defaults/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +9 -1
  423. package/src/plugins/defaults/memory/__tests__/jobs-worker-v2-schedule.test.ts +36 -1
  424. package/src/plugins/defaults/memory/__tests__/jobs-worker-v2-sweep-defer.test.ts +9 -1
  425. package/src/plugins/defaults/memory/__tests__/memory-log-stores-degraded.test.ts +2 -2
  426. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +44 -10
  427. package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +151 -10
  428. package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +1155 -0
  429. package/src/plugins/defaults/memory/__tests__/memory-v2-activation-log-store.test.ts +1 -1
  430. package/src/plugins/defaults/memory/__tests__/memory-v2-concept-frequency.test.ts +3 -3
  431. package/src/plugins/defaults/memory/__tests__/tools-delete-memory-page.test.ts +1 -1
  432. package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +71 -35
  433. package/src/plugins/defaults/memory/context-search/sources/memory.ts +5 -1
  434. package/src/plugins/defaults/memory/fork-conversation-memory.ts +4 -4
  435. package/src/plugins/defaults/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +80 -10
  436. package/src/plugins/defaults/memory/graph/__tests__/handle-remember-v2.test.ts +1 -1
  437. package/src/plugins/defaults/memory/graph/capability-seed.ts +24 -10
  438. package/src/plugins/defaults/memory/graph/conversation-graph-memory.ts +123 -49
  439. package/src/plugins/defaults/memory/graph/graph-search.ts +6 -118
  440. package/src/plugins/defaults/memory/graph/in-context-tracker.test.ts +173 -0
  441. package/src/plugins/defaults/memory/graph/in-context-tracker.ts +109 -0
  442. package/src/plugins/defaults/memory/graph/store.ts +1 -0
  443. package/src/plugins/defaults/memory/graph/tool-handlers.test.ts +1 -1
  444. package/src/plugins/defaults/memory/graph/tool-handlers.ts +5 -2
  445. package/src/plugins/defaults/memory/graph-topology/__tests__/build-memory-graph.test.ts +1 -1
  446. package/src/plugins/defaults/memory/graph-topology/build-memory-graph.ts +6 -9
  447. package/src/plugins/defaults/memory/hooks/__tests__/user-prompt-submit.test.ts +30 -16
  448. package/src/plugins/defaults/memory/hooks/user-prompt-submit.ts +18 -14
  449. package/src/plugins/defaults/memory/indexer.ts +146 -89
  450. package/src/plugins/defaults/memory/injectors.ts +86 -41
  451. package/src/plugins/defaults/memory/job-handlers.ts +130 -39
  452. package/src/plugins/defaults/memory/jobs/__tests__/embed-concept-page.test.ts +2 -2
  453. package/src/plugins/defaults/memory/jobs/embed-concept-page.ts +22 -10
  454. package/src/plugins/defaults/memory/jobs-worker.ts +279 -208
  455. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +4 -2
  456. package/src/plugins/defaults/memory/memory-retrospective-job.ts +40 -3
  457. package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +99 -11
  458. package/src/plugins/defaults/memory/src/__tests__/memory-v2-routes.test.ts +5 -3
  459. package/src/plugins/defaults/memory/src/__tests__/memory-v2-simulate-route.test.ts +3 -4
  460. package/src/plugins/defaults/memory/src/memory-graph-routes.ts +2 -1
  461. package/src/plugins/defaults/memory/src/memory-item-routes.test.ts +7 -5
  462. package/src/plugins/defaults/memory/src/memory-item-routes.ts +51 -19
  463. package/src/plugins/defaults/memory/src/memory-v2-routes.ts +31 -20
  464. package/src/plugins/defaults/memory/startup.ts +58 -21
  465. package/src/plugins/defaults/memory/substrate/__tests__/boot-maintenance.test.ts +335 -0
  466. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/cli-command-store.test.ts +14 -8
  467. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/consolidation-job.test.ts +51 -21
  468. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/edge-index.test.ts +1 -1
  469. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/frontmatter-sweep.test.ts +3 -3
  470. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/page-index.test.ts +1 -1
  471. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/page-store.test.ts +1 -1
  472. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/prompts-consolidation.test.ts +2 -2
  473. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/qdrant.test.ts +23 -14
  474. package/src/plugins/defaults/memory/substrate/__tests__/reembed-job.test.ts +262 -0
  475. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/sim.test.ts +9 -12
  476. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/skill-content.test.ts +2 -2
  477. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/skill-store.test.ts +9 -9
  478. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/sparse-bm25.test.ts +12 -5
  479. package/src/plugins/defaults/memory/substrate/__tests__/spread.test.ts +276 -0
  480. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/static-context.test.ts +17 -8
  481. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/sweep-job.test.ts +9 -9
  482. package/src/plugins/defaults/memory/{v3/substrate/memory-v2-startup.ts → substrate/boot-maintenance.ts} +43 -24
  483. package/src/plugins/defaults/memory/{v3/substrate → substrate}/cli-command-store.ts +13 -8
  484. package/src/plugins/defaults/memory/{v3/substrate → substrate}/consolidation-job.ts +51 -27
  485. package/src/plugins/defaults/memory/{v3/substrate → substrate}/constants.ts +1 -0
  486. package/src/plugins/defaults/memory/{v3/substrate → substrate}/edge-index.ts +28 -9
  487. package/src/plugins/defaults/memory/{v3/substrate → substrate}/frontmatter-sweep.ts +14 -7
  488. package/src/plugins/defaults/memory/{v3/substrate → substrate}/injected-block-slugs.ts +9 -3
  489. package/src/plugins/defaults/memory/{v3/substrate → substrate}/page-index.ts +29 -10
  490. package/src/plugins/defaults/memory/{v3/substrate → substrate}/page-store.ts +35 -12
  491. package/src/plugins/defaults/memory/{v3/substrate → substrate}/prompts/consolidation.ts +14 -8
  492. package/src/plugins/defaults/memory/{v3/substrate → substrate}/prompts/sweep.ts +4 -1
  493. package/src/plugins/defaults/memory/{v3/substrate → substrate}/qdrant.ts +96 -36
  494. package/src/plugins/defaults/memory/substrate/reembed-job.ts +54 -0
  495. package/src/plugins/defaults/memory/{v3/substrate → substrate}/sim.ts +26 -15
  496. package/src/plugins/defaults/memory/{v3/substrate → substrate}/skill-content.ts +1 -1
  497. package/src/plugins/defaults/memory/{v3/substrate → substrate}/skill-store.ts +13 -8
  498. package/src/plugins/defaults/memory/{v3/substrate → substrate}/sparse-bm25.ts +27 -10
  499. package/src/plugins/defaults/memory/substrate/spread.ts +165 -0
  500. package/src/plugins/defaults/memory/{v3/substrate → substrate}/static-context.ts +12 -7
  501. package/src/plugins/defaults/memory/{v3/substrate → substrate}/sweep-job.ts +20 -15
  502. package/src/plugins/defaults/memory/substrate/tuning.ts +93 -0
  503. package/src/plugins/defaults/memory/{v3/substrate → substrate}/types.ts +7 -4
  504. package/src/plugins/defaults/memory/tools.test.ts +1 -1
  505. package/src/plugins/defaults/memory/tools.ts +1 -1
  506. package/src/plugins/defaults/memory/v1/README.md +5 -0
  507. package/src/plugins/defaults/memory/{__tests__ → v1/__tests__}/filing-jobs.test.ts +4 -4
  508. package/src/plugins/defaults/memory/{filing-jobs.ts → v1/filing-jobs.ts} +3 -3
  509. package/src/plugins/defaults/memory/{graph → v1/graph}/bootstrap.test.ts +68 -6
  510. package/src/plugins/defaults/memory/{graph → v1/graph}/bootstrap.ts +40 -21
  511. package/src/plugins/defaults/memory/{graph → v1/graph}/consolidation.ts +14 -14
  512. package/src/plugins/defaults/memory/{graph → v1/graph}/decay.test.ts +1 -1
  513. package/src/plugins/defaults/memory/{graph → v1/graph}/decay.ts +3 -3
  514. package/src/plugins/defaults/memory/{graph → v1/graph}/extraction-job.ts +5 -5
  515. package/src/plugins/defaults/memory/{graph → v1/graph}/extraction.test.ts +1 -1
  516. package/src/plugins/defaults/memory/{graph → v1/graph}/extraction.ts +20 -14
  517. package/src/plugins/defaults/memory/{graph → v1/graph}/graph-search.test.ts +3 -3
  518. package/src/plugins/defaults/memory/v1/graph/graph-search.ts +123 -0
  519. package/src/plugins/defaults/memory/{graph → v1/graph}/injection.test.ts +1 -118
  520. package/src/plugins/defaults/memory/{graph → v1/graph}/injection.ts +9 -111
  521. package/src/plugins/defaults/memory/{graph → v1/graph}/narrative.ts +6 -6
  522. package/src/plugins/defaults/memory/{graph → v1/graph}/pattern-scan.ts +5 -5
  523. package/src/plugins/defaults/memory/{graph → v1/graph}/retriever.test.ts +135 -42
  524. package/src/plugins/defaults/memory/{graph → v1/graph}/retriever.ts +40 -22
  525. package/src/plugins/defaults/memory/{graph → v1/graph}/scoring.test.ts +1 -1
  526. package/src/plugins/defaults/memory/{graph → v1/graph}/scoring.ts +1 -1
  527. package/src/plugins/defaults/memory/{graph → v1/graph}/serendipity.ts +1 -1
  528. package/src/plugins/defaults/memory/{graph → v1/graph}/triggers.test.ts +1 -1
  529. package/src/plugins/defaults/memory/{graph → v1/graph}/triggers.ts +1 -1
  530. package/src/plugins/defaults/memory/{identity-context.ts → v1/identity-context.ts} +4 -4
  531. package/src/plugins/defaults/memory/{job-handlers → v1/job-handlers}/backfill.ts +6 -6
  532. package/src/plugins/defaults/memory/{job-handlers → v1/job-handlers}/embedding.test.ts +6 -6
  533. package/src/plugins/defaults/memory/{job-handlers → v1/job-handlers}/embedding.ts +6 -6
  534. package/src/plugins/defaults/memory/{job-handlers → v1/job-handlers}/index-maintenance.ts +8 -8
  535. package/src/plugins/defaults/memory/{jobs → v1/jobs}/embed-pkb-file.test.ts +7 -7
  536. package/src/plugins/defaults/memory/{jobs → v1/jobs}/embed-pkb-file.ts +5 -5
  537. package/src/plugins/defaults/memory/{pkb → v1/pkb}/autoinject.ts +1 -1
  538. package/src/plugins/defaults/memory/{pkb → v1/pkb}/context.ts +2 -2
  539. package/src/plugins/defaults/memory/{pkb → v1/pkb}/pkb-index.test.ts +3 -3
  540. package/src/plugins/defaults/memory/{pkb → v1/pkb}/pkb-index.ts +2 -2
  541. package/src/plugins/defaults/memory/{pkb → v1/pkb}/pkb-reconcile.test.ts +3 -3
  542. package/src/plugins/defaults/memory/{pkb → v1/pkb}/pkb-reconcile.ts +2 -2
  543. package/src/plugins/defaults/memory/{pkb → v1/pkb}/pkb-search.test.ts +3 -3
  544. package/src/plugins/defaults/memory/{pkb → v1/pkb}/pkb-search.ts +6 -6
  545. package/src/plugins/defaults/memory/{pkb → v1/pkb}/types.ts +3 -5
  546. package/src/plugins/defaults/memory/{pkb-schedule.ts → v1/pkb-schedule.ts} +2 -2
  547. package/src/plugins/defaults/memory/{search/semantic.ts → v1/semantic-search.ts} +1 -1
  548. package/src/plugins/defaults/memory/v2/__tests__/activation-store.test.ts +1 -1
  549. package/src/plugins/defaults/memory/v2/__tests__/activation.test.ts +11 -274
  550. package/src/plugins/defaults/memory/v2/__tests__/backfill-jobs.test.ts +9 -131
  551. package/src/plugins/defaults/memory/v2/__tests__/harness-compare.test.ts +1 -1
  552. package/src/plugins/defaults/memory/v2/__tests__/harness-oracle.test.ts +1 -1
  553. package/src/plugins/defaults/memory/v2/__tests__/harness-replay-input.test.ts +1 -1
  554. package/src/plugins/defaults/memory/v2/__tests__/injection.test.ts +14 -10
  555. package/src/plugins/defaults/memory/v2/__tests__/reranker.test.ts +7 -5
  556. package/src/plugins/defaults/memory/v2/__tests__/router.test.ts +12 -6
  557. package/src/plugins/defaults/memory/{memory-v2-activation-log-store.ts → v2/activation-log-store.ts} +2 -2
  558. package/src/plugins/defaults/memory/v2/activation-store.ts +22 -8
  559. package/src/plugins/defaults/memory/v2/activation.ts +30 -163
  560. package/src/plugins/defaults/memory/v2/backfill-jobs.ts +7 -54
  561. package/src/plugins/defaults/memory/{memory-v2-concept-frequency.ts → v2/concept-frequency.ts} +3 -3
  562. package/src/plugins/defaults/memory/v2/harness/oracle.ts +1 -1
  563. package/src/plugins/defaults/memory/v2/harness/replay-input.ts +2 -2
  564. package/src/plugins/defaults/memory/v2/harness/retriever.ts +1 -1
  565. package/src/plugins/defaults/memory/v2/injection.ts +36 -26
  566. package/src/plugins/defaults/memory/v2/migration.ts +2 -2
  567. package/src/plugins/defaults/memory/{rerank-local.ts → v2/rerank-local.ts} +44 -18
  568. package/src/plugins/defaults/memory/v2/reranker.ts +29 -11
  569. package/src/plugins/defaults/memory/v2/router.ts +38 -15
  570. package/src/plugins/defaults/memory/v3/__tests__/candidate-match.test.ts +5 -3
  571. package/src/plugins/defaults/memory/v3/__tests__/carry-integration.test.ts +21 -8
  572. package/src/plugins/defaults/memory/v3/__tests__/edge.test.ts +1 -1
  573. package/src/plugins/defaults/memory/v3/__tests__/live-integration.test.ts +1 -1
  574. package/src/plugins/defaults/memory/v3/__tests__/maintain-job.test.ts +4 -1
  575. package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +181 -3
  576. package/src/plugins/defaults/memory/v3/__tests__/selection-log-store.test.ts +3 -0
  577. package/src/plugins/defaults/memory/v3/__tests__/shadow-integration.test.ts +14 -5
  578. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +40 -50
  579. package/src/plugins/defaults/memory/v3/__tests__/tuning-profile.test.ts +1 -0
  580. package/src/plugins/defaults/memory/v3/candidate-match.ts +2 -2
  581. package/src/plugins/defaults/memory/v3/capabilities.ts +6 -4
  582. package/src/plugins/defaults/memory/v3/card.ts +25 -9
  583. package/src/plugins/defaults/memory/v3/edge.ts +52 -18
  584. package/src/plugins/defaults/memory/v3/gate.ts +4 -4
  585. package/src/plugins/defaults/memory/v3/injector.ts +10 -3
  586. package/src/plugins/defaults/memory/v3/maintain-job.ts +3 -3
  587. package/src/plugins/defaults/memory/v3/orchestrate.ts +100 -18
  588. package/src/plugins/defaults/memory/v3/page-content.ts +26 -10
  589. package/src/plugins/defaults/memory/v3/prune.ts +40 -16
  590. package/src/plugins/defaults/memory/v3/selection-log-store.ts +34 -12
  591. package/src/plugins/defaults/memory/v3/shadow-plugin.ts +9 -13
  592. package/src/plugins/defaults/memory/v3/span-query.test.ts +86 -0
  593. package/src/plugins/defaults/memory/v3/span-query.ts +71 -0
  594. package/src/plugins/defaults/memory/v3/tuning-profile.ts +3 -0
  595. package/src/plugins/defaults/memory/v3/types.ts +4 -1
  596. package/src/plugins/defaults/memory/v3-eval/eval-packets.ts +2 -2
  597. package/src/plugins/defaults/memory/validation.ts +1 -1
  598. package/src/plugins/defaults/memory/worker.ts +2 -1
  599. package/src/plugins/defaults/platform-hosted/__tests__/reengage.test.ts +104 -12
  600. package/src/plugins/defaults/platform-hosted/routes/reengage.ts +104 -12
  601. package/src/plugins/types.ts +2 -2
  602. package/src/prompts/sections.ts +4 -16
  603. package/src/providers/__tests__/registry-native-web-search.test.ts +47 -2
  604. package/src/providers/anthropic/client.ts +15 -6
  605. package/src/providers/content-block-schema.ts +26 -0
  606. package/src/providers/inference/__tests__/connections-credential-normalize.test.ts +87 -0
  607. package/src/providers/inference/adapter-factory.ts +6 -0
  608. package/src/providers/inference/connections.ts +30 -14
  609. package/src/providers/model-catalog.ts +77 -0
  610. package/src/providers/openai/__tests__/chat-completions-input-audio.test.ts +260 -0
  611. package/src/providers/openai/chat-completions-provider.ts +65 -17
  612. package/src/providers/openai/input-audio.ts +76 -0
  613. package/src/providers/openrouter/client.ts +0 -2
  614. package/src/providers/platform-proxy/constants.ts +4 -0
  615. package/src/providers/poolside/client.ts +42 -0
  616. package/src/providers/registry.ts +40 -8
  617. package/src/providers/retry.ts +2 -0
  618. package/src/providers/types.ts +39 -1
  619. package/src/providers/vercel-ai-gateway/client.ts +0 -2
  620. package/src/runtime/AGENTS.md +7 -5
  621. package/src/runtime/__tests__/question-request-guardian-bridge.test.ts +173 -0
  622. package/src/runtime/__tests__/question-resolution.test.ts +111 -0
  623. package/src/runtime/access-request-helper.ts +7 -15
  624. package/src/runtime/assistant-event-hub.ts +43 -31
  625. package/src/runtime/assistant-event-publish-options.ts +31 -0
  626. package/src/runtime/assistant-event.ts +19 -30
  627. package/src/runtime/assistant-stream-state.ts +4 -4
  628. package/src/runtime/channel-approvals.ts +0 -25
  629. package/src/runtime/channel-readiness-service.ts +99 -40
  630. package/src/runtime/channel-retry-sweep.ts +38 -15
  631. package/src/runtime/confirmation-request-guardian-bridge.ts +4 -0
  632. package/src/runtime/guardian-reply-router.ts +108 -0
  633. package/src/runtime/http-server.ts +8 -0
  634. package/src/runtime/http-types.ts +2 -2
  635. package/src/runtime/process-role.ts +42 -0
  636. package/src/runtime/question-request-guardian-bridge.ts +184 -0
  637. package/src/runtime/question-resolution.ts +153 -0
  638. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +6 -2
  639. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +1 -1
  640. package/src/runtime/routes/__tests__/subagents-routes.test.ts +250 -0
  641. package/src/runtime/routes/__tests__/webhook-routes.test.ts +201 -0
  642. package/src/runtime/routes/acp-routes.ts +2 -2
  643. package/src/runtime/routes/bookmark-routes.ts +3 -15
  644. package/src/runtime/routes/canned-message-complete.ts +2 -2
  645. package/src/runtime/routes/consolidation-routes.ts +8 -2
  646. package/src/runtime/routes/conversation-list-routes.ts +3 -19
  647. package/src/runtime/routes/conversation-management-routes.ts +1 -5
  648. package/src/runtime/routes/conversation-query-routes.ts +52 -8
  649. package/src/runtime/routes/conversation-routes.ts +36 -22
  650. package/src/runtime/routes/credential-request-routes.ts +0 -1
  651. package/src/runtime/routes/filing-routes.ts +7 -7
  652. package/src/runtime/routes/global-search-routes.ts +1 -1
  653. package/src/runtime/routes/guardian-approval-prompt.ts +2 -2
  654. package/src/runtime/routes/image-generation-routes.ts +12 -9
  655. package/src/runtime/routes/inbound-message-handler.ts +17 -0
  656. package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +50 -0
  657. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +19 -0
  658. package/src/runtime/routes/inbound-stages/background-dispatch.ts +4 -4
  659. package/src/runtime/routes/inbound-stages/inbound-content-prep.test.ts +129 -0
  660. package/src/runtime/routes/inbound-stages/inbound-content-prep.ts +141 -9
  661. package/src/runtime/routes/index.ts +2 -0
  662. package/src/runtime/routes/lifecycle-routes.ts +199 -0
  663. package/src/runtime/routes/platform-routes.ts +3 -8
  664. package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +7 -7
  665. package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +5 -5
  666. package/src/runtime/routes/question-routes.ts +34 -87
  667. package/src/runtime/routes/secret-routes.ts +11 -1
  668. package/src/runtime/routes/settings-routes.ts +8 -18
  669. package/src/runtime/routes/subagents-routes.ts +35 -8
  670. package/src/runtime/routes/upgrade-broadcast-routes.ts +4 -5
  671. package/src/runtime/routes/webhook-routes.ts +89 -30
  672. package/src/runtime/routes/workspace-routes.test.ts +7 -4
  673. package/src/runtime/slack-reply-session.test.ts +12 -12
  674. package/src/runtime/slack-reply-session.ts +17 -17
  675. package/src/runtime/sync/sync-publisher.test.ts +7 -5
  676. package/src/runtime/tool-grant-request-helper.ts +4 -0
  677. package/src/schedule/schedule-store.ts +70 -0
  678. package/src/schedule/scheduler.ts +27 -0
  679. package/src/security/AGENTS.md +1 -1
  680. package/src/security/credential-key.ts +17 -0
  681. package/src/security/secret-ingress.ts +14 -105
  682. package/src/security/secret-patterns.ts +8 -139
  683. package/src/security/secret-scanner.ts +8 -3
  684. package/src/security/store-path-override.ts +6 -16
  685. package/src/sequence/store.ts +8 -0
  686. package/src/signals/emit-event.ts +5 -10
  687. package/src/signals/event-stream.ts +20 -10
  688. package/src/subagent/manager.ts +76 -25
  689. package/src/subagent/notify.ts +8 -0
  690. package/src/subagent/types.ts +13 -3
  691. package/src/telegram/__tests__/webhook-health.test.ts +442 -0
  692. package/src/telegram/webhook-health.ts +478 -0
  693. package/src/telemetry/AGENTS.md +6 -0
  694. package/src/telemetry/memory-corpus-size.test.ts +129 -0
  695. package/src/telemetry/memory-corpus-size.ts +184 -0
  696. package/src/telemetry/memory-tier-reporter.test.ts +67 -2
  697. package/src/telemetry/memory-tier-reporter.ts +30 -3
  698. package/src/tools/__tests__/conversation-group-tool-input-schemas.test.ts +61 -0
  699. package/src/tools/__tests__/document-tool-input-schemas.test.ts +103 -0
  700. package/src/tools/__tests__/followup-call-acp-tool-input-schemas.test.ts +125 -0
  701. package/src/tools/__tests__/schedule-tool-input-schemas.test.ts +83 -0
  702. package/src/tools/__tests__/subagent-workflow-tool-input-schemas.test.ts +116 -0
  703. package/src/tools/__tests__/tool-input-schemas.test.ts +192 -0
  704. package/src/tools/__tests__/tool-schema-drift-harness.ts +142 -0
  705. package/src/tools/acp/abort.ts +21 -1
  706. package/src/tools/acp/context.ts +5 -5
  707. package/src/tools/acp/spawn.test.ts +15 -5
  708. package/src/tools/acp/spawn.ts +26 -3
  709. package/src/tools/acp/status.ts +20 -1
  710. package/src/tools/acp/steer.test.ts +20 -0
  711. package/src/tools/acp/steer.ts +23 -2
  712. package/src/tools/apps/resolve-app-id.ts +15 -3
  713. package/src/tools/ask-question/ask-question-tool.test.ts +65 -0
  714. package/src/tools/ask-question/ask-question-tool.ts +68 -92
  715. package/src/tools/browser/browser-screencast.ts +6 -6
  716. package/src/tools/calls/call-end.ts +24 -3
  717. package/src/tools/calls/call-start.ts +35 -11
  718. package/src/tools/calls/call-status.ts +20 -1
  719. package/src/tools/conversation-groups/group_create.ts +63 -0
  720. package/src/tools/conversation-groups/group_list.ts +20 -0
  721. package/src/tools/conversation-groups/group_shared.ts +52 -0
  722. package/src/tools/conversation-groups/move_to_group.ts +106 -0
  723. package/src/tools/document/document-comment-tool.test.ts +49 -0
  724. package/src/tools/document/document-comment-tool.ts +45 -6
  725. package/src/tools/document/document-tool.ts +142 -24
  726. package/src/tools/executor.ts +27 -13
  727. package/src/tools/filesystem/edit.ts +62 -60
  728. package/src/tools/filesystem/list.ts +36 -29
  729. package/src/tools/filesystem/read.ts +56 -39
  730. package/src/tools/filesystem/write.ts +44 -38
  731. package/src/tools/followups/followup_create.ts +41 -12
  732. package/src/tools/followups/followup_list.ts +34 -6
  733. package/src/tools/followups/followup_resolve.ts +26 -4
  734. package/src/tools/host-filesystem/edit.ts +50 -60
  735. package/src/tools/host-filesystem/read.ts +45 -39
  736. package/src/tools/host-filesystem/write.ts +31 -36
  737. package/src/tools/host-terminal/host-shell.ts +76 -62
  738. package/src/tools/policy-context.ts +27 -10
  739. package/src/tools/schedule/create.ts +76 -33
  740. package/src/tools/schedule/delete.ts +21 -2
  741. package/src/tools/schedule/list.ts +25 -3
  742. package/src/tools/schedule/update.ts +105 -48
  743. package/src/tools/shared/filesystem/path-policy.ts +246 -40
  744. package/src/tools/shared/zod-tool-schema.ts +117 -0
  745. package/src/tools/subagent/abort.ts +12 -4
  746. package/src/tools/subagent/consult-deadline.ts +9 -3
  747. package/src/tools/subagent/message.ts +21 -5
  748. package/src/tools/subagent/notify-parent.ts +44 -23
  749. package/src/tools/subagent/read.ts +15 -4
  750. package/src/tools/subagent/resolve.ts +26 -4
  751. package/src/tools/subagent/spawn.ts +37 -13
  752. package/src/tools/subagent/status.ts +12 -4
  753. package/src/tools/system/request-permission.ts +35 -26
  754. package/src/tools/terminal/shell.ts +68 -55
  755. package/src/tools/tool-approval-handler.ts +327 -53
  756. package/src/tools/tool-input-schemas.ts +85 -0
  757. package/src/tools/tool-types.ts +7 -0
  758. package/src/tools/types.ts +8 -0
  759. package/src/tools/workflows/manage-workflows.ts +30 -2
  760. package/src/tools/workflows/run-workflow.test.ts +54 -2
  761. package/src/tools/workflows/run-workflow.ts +32 -4
  762. package/src/util/fs-symlinks.ts +30 -0
  763. package/src/util/log-redact.ts +7 -3
  764. package/src/util/platform.ts +15 -0
  765. package/src/vellum-assistant-namespace.d.ts +80 -0
  766. package/src/watcher/watcher-store.ts +8 -0
  767. package/src/workflows/leaf-runner.test.ts +104 -2
  768. package/src/workflows/leaf-runner.ts +31 -6
  769. package/src/workspace/migrations/070-memory-v2-summary-schema-rebuild.ts +2 -2
  770. package/src/workspace/migrations/134-image-generation-mode-to-provider.ts +128 -0
  771. package/src/workspace/migrations/135-copy-substrate-tunables.ts +248 -0
  772. package/src/workspace/migrations/__tests__/135-copy-substrate-tunables.test.ts +446 -0
  773. package/src/workspace/migrations/__tests__/backfill-v3-leaves.test.ts +2 -2
  774. package/src/workspace/migrations/registry.ts +4 -0
  775. package/tsconfig.plugin-api.json +1 -1
  776. package/src/__tests__/memory-retrieval-budget.test.ts +0 -49
  777. package/src/__tests__/secret-patterns.test.ts +0 -27
  778. package/src/config/__tests__/proc-to-skills.test.ts +0 -21
  779. package/src/config/memory-tier.test.ts +0 -44
  780. package/src/daemon/message-types/acp.ts +0 -20
  781. package/src/daemon/message-types/apps.ts +0 -11
  782. package/src/daemon/message-types/background-tools.ts +0 -14
  783. package/src/daemon/message-types/bookmarks.ts +0 -13
  784. package/src/daemon/message-types/contacts.ts +0 -13
  785. package/src/daemon/message-types/document-comments.ts +0 -14
  786. package/src/daemon/message-types/documents.ts +0 -13
  787. package/src/daemon/message-types/home.ts +0 -15
  788. package/src/daemon/message-types/host-bash.ts +0 -12
  789. package/src/daemon/message-types/host-cu.ts +0 -10
  790. package/src/daemon/message-types/host-file.ts +0 -12
  791. package/src/daemon/message-types/host-transfer.ts +0 -12
  792. package/src/daemon/message-types/integrations.ts +0 -90
  793. package/src/daemon/message-types/memory.ts +0 -10
  794. package/src/daemon/message-types/schedules.ts +0 -14
  795. package/src/daemon/message-types/settings.ts +0 -17
  796. package/src/daemon/message-types/subagents.ts +0 -63
  797. package/src/daemon/message-types/upgrades.ts +0 -14
  798. package/src/daemon/message-types/workflows.ts +0 -25
  799. package/src/daemon/message-types/workspace.ts +0 -137
  800. package/src/live-voice/ack-phrases.ts +0 -58
  801. package/src/plugins/defaults/memory/retrieval-budget.ts +0 -29
  802. package/src/plugins/defaults/memory/v3/__tests__/gate-flag.test.ts +0 -69
  803. package/src/plugins/defaults/memory/v3/gate-flag.ts +0 -20
  804. package/src/plugins/defaults/memory/v3/substrate/__tests__/memory-v2-startup.test.ts +0 -86
  805. package/src/plugins/defaults/memory/v3/substrate/memory-v2-startup.test.ts +0 -205
  806. /package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/concept-page-frontmatter-schema.test.ts +0 -0
  807. /package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/consolidation-prompt-flag-gating-guard.test.ts +0 -0
  808. /package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/injected-block-slugs.test.ts +0 -0
  809. /package/src/plugins/defaults/memory/{v3/substrate → substrate}/cli-command-content.ts +0 -0
@@ -2,15 +2,33 @@ 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
+ getWorkspaceSystemPromptDir,
13
+ getWorkspaceToolsDir,
14
+ getWorkspaceWorkflowsDir,
15
+ } from "../util/platform.js";
5
16
 
6
17
  /**
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.
18
+ * Resolve a path to its canonical form. A trailing (possibly dangling)
19
+ * symlink chain is followed first so a link whose destination does not
20
+ * exist yet still canonicalizes to that destination — a write through the
21
+ * link creates it. When the target itself doesn't exist (e.g. a new file
22
+ * being written), walk up to the nearest existing ancestor and append the
23
+ * remaining segments so that symlinks in parent directories (like macOS
24
+ * `/var` -> `/private/var`) are still resolved.
11
25
  */
12
26
  function canonicalize(p: string): string {
13
- const abs = resolve(p);
27
+ const abs = resolveTrailingLinkTarget(resolve(p));
28
+ return canonicalizeExisting(abs);
29
+ }
30
+
31
+ function canonicalizeExisting(abs: string): string {
14
32
  try {
15
33
  return realpathSync(abs);
16
34
  } catch {
@@ -21,10 +39,38 @@ function canonicalize(p: string): string {
21
39
  // Reached filesystem root — nothing left to resolve.
22
40
  return normalize(abs);
23
41
  }
24
- return `${canonicalize(parent)}/${name}`;
42
+ return `${canonicalizeExisting(parent)}/${name}`;
25
43
  }
26
44
  }
27
45
 
46
+ /**
47
+ * Canonicalize a path's directory chain while keeping the final segment as
48
+ * addressed: symlinks among the ancestors (macOS `/var` → `/private/var`, a
49
+ * linked parent directory) resolve, a trailing symlink does not. This is
50
+ * the *name* the daemon acts on — the prompt renderer and the code loaders
51
+ * open control-plane paths by name and follow links while reading, so when
52
+ * the final segment is itself a symlink, a write addressed at it changes
53
+ * what they read even though the bytes land at the link's destination.
54
+ */
55
+ function canonicalizeAddressedName(p: string): string {
56
+ const abs = resolve(p);
57
+ return `${canonicalizeExisting(dirname(abs))}/${basename(abs)}`;
58
+ }
59
+
60
+ /**
61
+ * Whether a canonical path is a canonical directory or falls under it. The
62
+ * trailing separator keeps `/workspace-extra` from matching `/workspace`.
63
+ * Both arguments must already be canonicalized — comparing a canonical path
64
+ * against a lexical directory would re-open the symlink dodge.
65
+ */
66
+ function isAtOrUnderCanonicalDir(
67
+ canonicalPath: string,
68
+ canonicalDir: string,
69
+ ): boolean {
70
+ const prefix = canonicalDir.endsWith("/") ? canonicalDir : `${canonicalDir}/`;
71
+ return canonicalPath === canonicalDir || canonicalPath.startsWith(prefix);
72
+ }
73
+
28
74
  /**
29
75
  * Resolve a file path to its canonical form (resolving symlinks and
30
76
  * normalizing segments like `.` and `..`), then check whether it falls
@@ -34,19 +80,12 @@ export function isPathWithinWorkspaceRoot(
34
80
  filePath: string,
35
81
  workspaceRoot: string,
36
82
  ): 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)
83
+ if (!filePath || !workspaceRoot) {
84
+ return false;
85
+ }
86
+ return isAtOrUnderCanonicalDir(
87
+ canonicalize(filePath),
88
+ canonicalize(workspaceRoot),
50
89
  );
51
90
  }
52
91
 
@@ -55,6 +94,14 @@ export function isPathWithinWorkspaceRoot(
55
94
  /** File-path tools whose workspace-scoped-ness depends on the file_path input. */
56
95
  const PATH_SCOPED_TOOLS = new Set(["file_read", "file_write", "file_edit"]);
57
96
 
97
+ /** Sandbox file tools that write. Reads never plant code. */
98
+ const WORKSPACE_WRITE_TOOLS = new Set(["file_write", "file_edit"]);
99
+
100
+ /** Whether a tool is a sandbox file tool that writes. */
101
+ export function isWorkspaceWriteTool(toolName: string): boolean {
102
+ return WORKSPACE_WRITE_TOOLS.has(toolName);
103
+ }
104
+
58
105
  /** Network-accessing tools — never workspace-scoped. */
59
106
  const NETWORK_TOOLS = new Set(["web_search", "web_fetch", "network_request"]);
60
107
 
@@ -76,6 +123,196 @@ const ALWAYS_SCOPED_TOOLS = new Set([
76
123
  "ui_dismiss",
77
124
  ]);
78
125
 
126
+ // The Docker sandbox mounts the workspace at /workspace, and the model emits
127
+ // container-scoped paths (e.g. "/workspace/tools/evil.ts") even on local
128
+ // turns. The execution-time path policy and the gateway classifier both
129
+ // apply this remap, so every workspace-containment decision here must too.
130
+ const CONTAINER_WORKSPACE_PREFIX = "/workspace/";
131
+ const CONTAINER_WORKSPACE_EXACT = "/workspace";
132
+
133
+ /**
134
+ * Resolve a sandbox file path to its lexical base the same way the
135
+ * execution-time `sandboxPolicy` and the gateway classifier do: remap a
136
+ * container-scoped `/workspace/...` path onto `workingDir`, then resolve
137
+ * (relative paths resolve against `workingDir`, not process.cwd()).
138
+ */
139
+ export function resolveSandboxBase(
140
+ rawPath: string,
141
+ workingDir: string,
142
+ ): string {
143
+ let effectivePath = rawPath;
144
+ if (!rawPath.startsWith(workingDir + "/") && rawPath !== workingDir) {
145
+ if (rawPath.startsWith(CONTAINER_WORKSPACE_PREFIX)) {
146
+ effectivePath = rawPath.slice(CONTAINER_WORKSPACE_PREFIX.length);
147
+ } else if (rawPath === CONTAINER_WORKSPACE_EXACT) {
148
+ effectivePath = ".";
149
+ }
150
+ }
151
+ return resolve(workingDir, effectivePath);
152
+ }
153
+
154
+ /**
155
+ * Extract a path-scoped tool's target path from its input. `path` takes
156
+ * priority over `file_path` — the file tools execute `input.path` and the
157
+ * risk classifier reads the fields in the same order, so containment
158
+ * decisions must be derived from the field that actually executes (an input
159
+ * carrying both fields is not runtime-stripped). Returns `""` when the
160
+ * input carries no usable path.
161
+ */
162
+ function resolvePathScopedTarget(
163
+ toolInput: Record<string, unknown>,
164
+ workspaceRoot: string,
165
+ ): string {
166
+ const rawPath =
167
+ typeof toolInput.path === "string"
168
+ ? toolInput.path
169
+ : typeof toolInput.file_path === "string"
170
+ ? toolInput.file_path
171
+ : "";
172
+ if (rawPath === "") {
173
+ return "";
174
+ }
175
+ return resolveSandboxBase(rawPath, workspaceRoot);
176
+ }
177
+
178
+ /**
179
+ * Whether a sandbox file-tool invocation targets a path outside the
180
+ * workspace root. Always false in containerized mode — the execution-time
181
+ * boundary is hard there, so an escaping path never executes. Non-path
182
+ * tools are never classified by this predicate.
183
+ */
184
+ export function isOutOfWorkspaceFileInvocation(
185
+ toolName: string,
186
+ toolInput: Record<string, unknown>,
187
+ workspaceRoot: string,
188
+ ): boolean {
189
+ if (getIsContainerized()) {
190
+ return false;
191
+ }
192
+ if (!PATH_SCOPED_TOOLS.has(toolName)) {
193
+ return false;
194
+ }
195
+ const filePath = resolvePathScopedTarget(toolInput, workspaceRoot);
196
+ return filePath !== "" && !isPathWithinWorkspaceRoot(filePath, workspaceRoot);
197
+ }
198
+
199
+ /**
200
+ * The workspace directories the daemon imports and executes — hooks,
201
+ * plugins, skills, tools, routes, workflows — plus the monitoring data
202
+ * directory, whose source-versions sentinel steers which plugin code the
203
+ * daemon imports. A write to any of them is code that runs later with the
204
+ * daemon's own reach. The list mirrors the file risk classifier's
205
+ * code-injection sinks.
206
+ */
207
+ function executableSinkDirs(): string[] {
208
+ return [
209
+ getWorkspaceHooksDir(),
210
+ getWorkspacePluginsDir(),
211
+ getWorkspaceSkillsDir(),
212
+ getWorkspaceToolsDir(),
213
+ getWorkspaceRoutesDir(),
214
+ getWorkspaceWorkflowsDir(),
215
+ getMonitoringDataDir(),
216
+ ];
217
+ }
218
+
219
+ /**
220
+ * Workspace-root files and directories the prompt renderer reads into the
221
+ * system prompt at render time (`prompts/templates/system-sections.ts`
222
+ * `workspacePath` entries — a drift-guard test walks the real section list
223
+ * against this predicate). A write to any of them rewrites the assistant's
224
+ * standing instructions, per-user context, or per-channel context.
225
+ */
226
+ const PROMPT_SURFACE_FILES = [
227
+ "IDENTITY.md",
228
+ "SOUL.md",
229
+ "VOICE.md",
230
+ "BOOTSTRAP.md",
231
+ // Read by the heartbeat service as its checklist — instructions executed
232
+ // unattended (`runtime/routes/heartbeat-routes.ts`).
233
+ "HEARTBEAT.md",
234
+ // The scratchpad the heartbeat checklist reads its to-dos from, injected
235
+ // into every full-mode guardian turn by the `now-md` injector
236
+ // (`plugins/defaults/workspace/injectors.ts`). Injector-fed rather than a
237
+ // system section, so the drift guard cannot see it — this entry is the
238
+ // coverage.
239
+ "NOW.md",
240
+ ];
241
+ const PROMPT_SURFACE_DIRS = ["users", "channels"];
242
+
243
+ // `memory/**` is deliberately data-plane, not control-plane: memory pages
244
+ // inject into guardian sessions as past-record rather than standing
245
+ // instructions, consolidation owns and rewrites those files, and the gated
246
+ // write path is `remember()` (provenance-checked in the indexer). If direct
247
+ // file writes to memory pages ever become a delegation surface, they join
248
+ // this list.
249
+
250
+ /**
251
+ * Whether a sandbox file-tool invocation writes a workspace control-plane
252
+ * target: an executable sink directory (code the daemon executes) or a
253
+ * prompt surface (instructions it obeys). The two categories are one
254
+ * delegation a layer apart — approving the write approves everything the
255
+ * planted code or rewritten instructions cause later.
256
+ *
257
+ * Unlike {@link isOutOfWorkspaceFileInvocation} this holds in containerized
258
+ * mode too: the workspace boundary is what contains an escaping *path*, and
259
+ * these paths do not escape — the daemon acts on them from inside.
260
+ *
261
+ * Two views of the write are checked against every baseline, because a
262
+ * symlink at either end dodges a single view:
263
+ *
264
+ * - The canonical destination ({@link canonicalize}): a benign-looking name
265
+ * whose trailing link points at a control-plane path is a write to that
266
+ * path, and a lexical check does not see it.
267
+ * - The addressed name ({@link canonicalizeAddressedName}): a control-plane
268
+ * name that is itself a symlink to a benign path still changes what the
269
+ * daemon reads under that name — the renderer and the loaders follow the
270
+ * link — while the destination alone canonicalizes past the surface.
271
+ *
272
+ * The baselines are canonicalized too: a prompt surface may itself be a
273
+ * symlink (SOUL.md → personas/current.md), and the renderer reads through
274
+ * it — so a write to the link's destination must match as well.
275
+ */
276
+ export function isControlPlaneWorkspaceWrite(
277
+ toolName: string,
278
+ toolInput: Record<string, unknown>,
279
+ workspaceRoot: string,
280
+ ): boolean {
281
+ if (!isWorkspaceWriteTool(toolName)) {
282
+ return false;
283
+ }
284
+ const filePath = resolvePathScopedTarget(toolInput, workspaceRoot);
285
+ if (filePath === "") {
286
+ return false;
287
+ }
288
+ const target = canonicalize(filePath);
289
+ const addressed = canonicalizeAddressedName(filePath);
290
+ const root = canonicalize(workspaceRoot);
291
+ const writesUnder = (dir: string): boolean => {
292
+ const canonicalDir = canonicalize(dir);
293
+ return (
294
+ isAtOrUnderCanonicalDir(target, canonicalDir) ||
295
+ isAtOrUnderCanonicalDir(addressed, canonicalDir)
296
+ );
297
+ };
298
+ return (
299
+ executableSinkDirs().some(writesUnder) ||
300
+ PROMPT_SURFACE_FILES.some((file) => {
301
+ const canonicalFile = canonicalize(`${root}/${file}`);
302
+ return target === canonicalFile || addressed === canonicalFile;
303
+ }) ||
304
+ PROMPT_SURFACE_DIRS.some((dir) => writesUnder(`${root}/${dir}`)) ||
305
+ // The system-section override layer: a `<section-id>.md` under
306
+ // `prompts/system/` replaces the bundled section of the same id — or,
307
+ // stripped to nothing, silences it — including the security-policy
308
+ // sections (credential handling, external content, the non-guardian
309
+ // boundary). A brand-new id adds a workspace-only section, so the whole
310
+ // directory is a prompt surface. The baseline is the getter the renderer
311
+ // itself resolves, like {@link executableSinkDirs}.
312
+ writesUnder(getWorkspaceSystemPromptDir())
313
+ );
314
+ }
315
+
79
316
  /**
80
317
  * Determine whether a tool invocation only affects resources within the
81
318
  * workspace root. This is a conservative classification — unknown tools
@@ -86,22 +323,18 @@ export function isWorkspaceScopedInvocation(
86
323
  toolInput: Record<string, unknown>,
87
324
  workspaceRoot: string,
88
325
  ): 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;
326
+ if (ALWAYS_SCOPED_TOOLS.has(toolName)) {
327
+ return true;
328
+ }
329
+ if (NETWORK_TOOLS.has(toolName)) {
330
+ return false;
331
+ }
332
+ if (HOST_TOOLS.has(toolName)) {
333
+ return false;
334
+ }
92
335
 
93
336
  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;
337
+ const filePath = resolvePathScopedTarget(toolInput, workspaceRoot);
105
338
  return (
106
339
  filePath !== "" && isPathWithinWorkspaceRoot(filePath, workspaceRoot)
107
340
  );
@@ -112,7 +345,9 @@ export function isWorkspaceScopedInvocation(
112
345
  // bash is NOT workspace-scoped here — path resolution for allowlisted commands is
113
346
  // handled upstream in the checker's hasSandboxAutoApprove computation, which validates
114
347
  // all path arguments against the workspace root for non-containerized environments.
115
- if (toolName === "bash") return getIsContainerized();
348
+ if (toolName === "bash") {
349
+ return getIsContainerized();
350
+ }
116
351
 
117
352
  // Unknown tool — conservative default.
118
353
  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