@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
@@ -9,13 +9,14 @@
9
9
 
10
10
  import { isAbsolute, resolve, sep } from "node:path";
11
11
 
12
- import { addAppConversationId, getApp } from "../apps/app-store.js";
12
+ import type { AssistantEvent } from "../api/index.js";
13
+ import { getApp, linkAppToConversationLineage } from "../apps/app-store.js";
13
14
  import { findActiveSession } from "../channels/gateway-verification-sessions.js";
14
15
  import { getConfig } from "../config/loader.js";
15
16
  import { generateAppIcon } from "../media/app-icon-generator.js";
16
- import { invalidateEdgeIndex } from "../plugins/defaults/memory/v3/substrate/edge-index.js";
17
- import { invalidatePageIndex } from "../plugins/defaults/memory/v3/substrate/page-index.js";
18
- import { getConceptsDir } from "../plugins/defaults/memory/v3/substrate/page-store.js";
17
+ import { invalidateEdgeIndex } from "../plugins/defaults/memory/substrate/edge-index.js";
18
+ import { invalidatePageIndex } from "../plugins/defaults/memory/substrate/page-index.js";
19
+ import { getConceptsDir } from "../plugins/defaults/memory/substrate/page-store.js";
19
20
  import { broadcastMessage } from "../runtime/assistant-event-hub.js";
20
21
  import { publishAppsChanged } from "../runtime/sync/resource-sync-events.js";
21
22
  import { deliverVerificationSlack } from "../runtime/verification-outbound-actions.js";
@@ -26,7 +27,6 @@ import { getWorkspaceDir } from "../util/platform.js";
26
27
  import { ensureAppSourceWatcher } from "./app-source-watcher.js";
27
28
  import { refreshSurfacesForApp } from "./conversation-surfaces.js";
28
29
  import { isDoordashCommand, updateDoordashProgress } from "./doordash-steps.js";
29
- import type { ServerMessage } from "./message-protocol.js";
30
30
  import type { ToolSetupContext } from "./tool-setup-types.js";
31
31
 
32
32
  const log = getLogger("tool-side-effects");
@@ -121,7 +121,7 @@ registerHook("app_create", (_name, _input, result, { ctx }) => {
121
121
  return;
122
122
  }
123
123
  try {
124
- addAppConversationId(appId, ctx.conversationId);
124
+ linkAppToConversationLineage(appId, ctx.conversationId);
125
125
  } catch (err) {
126
126
  log.warn({ err, appId }, "Failed to track conversation ID on app_create");
127
127
  }
@@ -169,7 +169,7 @@ function registerAppSurfaceRefreshHook(toolName: string): void {
169
169
  return;
170
170
  }
171
171
  try {
172
- addAppConversationId(appId, ctx.conversationId);
172
+ linkAppToConversationLineage(appId, ctx.conversationId);
173
173
  } catch (err) {
174
174
  log.warn({ err, appId }, `Failed to track conversation ID on ${name}`);
175
175
  }
@@ -181,7 +181,9 @@ registerAppSurfaceRefreshHook("app_update");
181
181
 
182
182
  registerHook("voice_config_update", (_name, input) => {
183
183
  const setting = input.setting as string | undefined;
184
- if (!setting) return;
184
+ if (!setting) {
185
+ return;
186
+ }
185
187
 
186
188
  const SETTING_TO_KEY: Record<string, string> = {
187
189
  activation_key: "pttActivationKey",
@@ -191,7 +193,9 @@ registerHook("voice_config_update", (_name, input) => {
191
193
  fish_audio_reference_id: "fishAudioReferenceId",
192
194
  };
193
195
  const key = SETTING_TO_KEY[setting];
194
- if (!key) return;
196
+ if (!key) {
197
+ return;
198
+ }
195
199
 
196
200
  // `ttsVoiceId` is an ElevenLabs concept on the desktop client. When the
197
201
  // active provider is managed (vellum) or anything else, the voice lives only
@@ -222,7 +226,7 @@ registerHook("voice_config_update", (_name, input) => {
222
226
  type: "client_settings_update",
223
227
  key,
224
228
  value: coerced,
225
- } as unknown as ServerMessage);
229
+ } as unknown as AssistantEvent);
226
230
  });
227
231
 
228
232
  // Dispatch pending Slack DM delivery when a CLI verification command
@@ -231,8 +235,12 @@ registerHook("voice_config_update", (_name, input) => {
231
235
  // This hook runs in the unsandboxed daemon process and delivers the DM.
232
236
  registerHook("bash", async (_name, input, result) => {
233
237
  const command = (input.command ?? "") as string;
234
- if (!command.includes("channel-verification-sessions")) return;
235
- if (!result.content.includes("_pendingSlackDm")) return;
238
+ if (!command.includes("channel-verification-sessions")) {
239
+ return;
240
+ }
241
+ if (!result.content.includes("_pendingSlackDm")) {
242
+ return;
243
+ }
236
244
 
237
245
  type PendingDm = { userId: string; text: string; assistantId: string };
238
246
  type Parsed = { _pendingSlackDm?: PendingDm };
@@ -288,18 +296,24 @@ registerHook("bash", async (_name, input, result) => {
288
296
  // multi-object output (e.g. cancel + create chained with &&).
289
297
  }
290
298
  if (singleObject !== undefined) {
291
- if ((await dispatch(singleObject)) !== null) return;
299
+ if ((await dispatch(singleObject)) !== null) {
300
+ return;
301
+ }
292
302
  }
293
303
  for (const line of result.content.split("\n")) {
294
304
  const trimmed = line.trim();
295
- if (!trimmed.startsWith("{")) continue;
305
+ if (!trimmed.startsWith("{")) {
306
+ continue;
307
+ }
296
308
  let parsed: Parsed;
297
309
  try {
298
310
  parsed = JSON.parse(trimmed) as Parsed;
299
311
  } catch {
300
312
  continue;
301
313
  }
302
- if ((await dispatch(parsed)) === "delivered") return;
314
+ if ((await dispatch(parsed)) === "delivered") {
315
+ return;
316
+ }
303
317
  }
304
318
  });
305
319
 
@@ -311,7 +325,9 @@ function invalidateEdgeIndexIfConceptPage(
311
325
  input: Record<string, unknown>,
312
326
  ): void {
313
327
  const rawPath = input.path;
314
- if (typeof rawPath !== "string" || rawPath.length === 0) return;
328
+ if (typeof rawPath !== "string" || rawPath.length === 0) {
329
+ return;
330
+ }
315
331
  const workspaceDir = getWorkspaceDir();
316
332
  const conceptsRoot = getConceptsDir(workspaceDir);
317
333
  const absPath = isAbsolute(rawPath)
@@ -7,7 +7,7 @@
7
7
  import type { ChannelConversationType } from "@vellumai/gateway-client";
8
8
 
9
9
  import { isHttpAuthDisabled } from "../config/env.js";
10
- import { shouldExposePersonalMemory } from "../plugins/defaults/memory/v3/substrate/static-context.js";
10
+ import { shouldExposePersonalMemory } from "../plugins/defaults/memory/substrate/static-context.js";
11
11
  import type { TrustClass } from "../runtime/trust-class.js";
12
12
  import type { TrustContext } from "./trust-context-types.js";
13
13
 
@@ -13,6 +13,7 @@
13
13
  */
14
14
 
15
15
  import type { AgentEvent } from "../agent/loop.js";
16
+ import type { AssistantEvent } from "../api/index.js";
16
17
  import {
17
18
  addMessage,
18
19
  getConversation,
@@ -27,7 +28,6 @@ import { publishConversationMessagesChanged } from "../runtime/sync/resource-syn
27
28
  import type { CompletedBackgroundTool } from "../tools/background-tool-registry.js";
28
29
  import { getLogger } from "../util/logger.js";
29
30
  import type { Conversation } from "./conversation.js";
30
- import type { ServerMessage } from "./message-protocol.js";
31
31
  import type {
32
32
  SubagentToolGateMode,
33
33
  WakeToolContextPin,
@@ -37,7 +37,7 @@ const log = getLogger("wake-conversation-ops");
37
37
 
38
38
  /**
39
39
  * Translate a raw {@link AgentEvent} from the agent loop into the
40
- * corresponding {@link ServerMessage} wire frame. The normal user-turn
40
+ * corresponding {@link AssistantEvent} wire frame. The normal user-turn
41
41
  * path does this via the full state-aware handler in
42
42
  * `conversation-agent-loop-handlers.ts`; the wake path has no tool
43
43
  * accounting, title generation, or activity-state tracking to worry
@@ -48,7 +48,7 @@ const log = getLogger("wake-conversation-ops");
48
48
  function translateAgentEventToServerMessage(
49
49
  event: AgentEvent,
50
50
  conversationId: string,
51
- ): ServerMessage | null {
51
+ ): AssistantEvent | null {
52
52
  switch (event.type) {
53
53
  case "text_delta":
54
54
  return {
@@ -176,7 +176,7 @@ export function emitWakeAgentEvent(
176
176
  event,
177
177
  conversation.conversationId,
178
178
  );
179
- if (!frame) return;
179
+ if (!frame) {return;}
180
180
  broadcastMessage(frame);
181
181
  }
182
182
 
@@ -4,9 +4,11 @@ import { sql } from "drizzle-orm";
4
4
 
5
5
  import { getDb } from "../../persistence/db-connection.js";
6
6
  import { initializeDb } from "../../persistence/db-init.js";
7
+ import { heartbeatRuns } from "../../persistence/schema/index.js";
7
8
  import {
8
9
  completeHeartbeatRun,
9
10
  countCompletedHeartbeatRuns,
11
+ getLastHeartbeatRunAt,
10
12
  insertPendingHeartbeatRun,
11
13
  listHeartbeatRuns,
12
14
  markStaleRunningAsError,
@@ -18,6 +20,19 @@ import {
18
20
 
19
21
  await initializeDb();
20
22
 
23
+ /**
24
+ * Raw read-back of every row (any status). `listHeartbeatRuns` serves run
25
+ * history and hides bookkeeping rows (pending/superseded), so tests that
26
+ * assert on those statuses read the table directly.
27
+ */
28
+ function allRuns() {
29
+ return getDb()
30
+ .select()
31
+ .from(heartbeatRuns)
32
+ .orderBy(sql`${heartbeatRuns.scheduledFor} DESC`)
33
+ .all();
34
+ }
35
+
21
36
  describe("heartbeat-run-store", () => {
22
37
  beforeEach(() => {
23
38
  const db = getDb();
@@ -29,7 +44,7 @@ describe("heartbeat-run-store", () => {
29
44
  const id = insertPendingHeartbeatRun(scheduledFor);
30
45
  expect(id).toBeTruthy();
31
46
 
32
- const rows = listHeartbeatRuns();
47
+ const rows = allRuns();
33
48
  expect(rows).toHaveLength(1);
34
49
  expect(rows[0].id).toBe(id);
35
50
  expect(rows[0].status).toBe("pending");
@@ -134,7 +149,7 @@ describe("heartbeat-run-store", () => {
134
149
  const ok = supersedePendingRun(id);
135
150
  expect(ok).toBe(true);
136
151
 
137
- const rows = listHeartbeatRuns();
152
+ const rows = allRuns();
138
153
  expect(rows[0].status).toBe("superseded");
139
154
  });
140
155
 
@@ -156,7 +171,7 @@ describe("heartbeat-run-store", () => {
156
171
  const count = markStaleRunsAsMissed(5 * 60 * 1000);
157
172
  expect(count).toBe(2);
158
173
 
159
- const rows = listHeartbeatRuns();
174
+ const rows = allRuns();
160
175
  const byId = Object.fromEntries(rows.map((r) => [r.id, r]));
161
176
  expect(byId[id1].status).toBe("missed");
162
177
  expect(byId[id2].status).toBe("missed");
@@ -185,9 +200,9 @@ describe("heartbeat-run-store", () => {
185
200
 
186
201
  test("listHeartbeatRuns returns rows ordered by scheduledFor desc", () => {
187
202
  const now = Date.now();
188
- insertPendingHeartbeatRun(now - 2000);
189
- insertPendingHeartbeatRun(now);
190
- insertPendingHeartbeatRun(now - 1000);
203
+ skipHeartbeatRun(insertPendingHeartbeatRun(now - 2000), "overlap");
204
+ skipHeartbeatRun(insertPendingHeartbeatRun(now), "overlap");
205
+ skipHeartbeatRun(insertPendingHeartbeatRun(now - 1000), "overlap");
191
206
 
192
207
  const rows = listHeartbeatRuns();
193
208
  expect(rows).toHaveLength(3);
@@ -199,13 +214,48 @@ describe("heartbeat-run-store", () => {
199
214
  test("listHeartbeatRuns respects limit", () => {
200
215
  const now = Date.now();
201
216
  for (let i = 0; i < 5; i++) {
202
- insertPendingHeartbeatRun(now + i);
217
+ skipHeartbeatRun(insertPendingHeartbeatRun(now + i), "overlap");
203
218
  }
204
219
 
205
220
  const rows = listHeartbeatRuns(3);
206
221
  expect(rows).toHaveLength(3);
207
222
  });
208
223
 
224
+ test("listHeartbeatRuns hides bookkeeping rows (pending, superseded)", () => {
225
+ const now = Date.now();
226
+
227
+ // Superseded rows: pending runs replaced by timer resets, scheduled in
228
+ // the future at insert time — never ran.
229
+ for (let i = 0; i < 3; i++) {
230
+ supersedePendingRun(insertPendingHeartbeatRun(now + 60 * 60 * 1000 + i));
231
+ }
232
+ // The not-yet-fired next scheduled run.
233
+ insertPendingHeartbeatRun(now + 60 * 60 * 1000 + 10);
234
+
235
+ // Real history: a completed run, a skipped run, a missed run, and one
236
+ // in flight.
237
+ const okId = insertPendingHeartbeatRun(now - 3000);
238
+ startHeartbeatRun(okId);
239
+ completeHeartbeatRun(okId, { status: "ok", conversationId: "conv-1" });
240
+ const skippedId = insertPendingHeartbeatRun(now - 2000);
241
+ skipHeartbeatRun(skippedId, "outside_active_hours");
242
+ const missedId = insertPendingHeartbeatRun(now - 10 * 60 * 1000);
243
+ markStaleRunsAsMissed(5 * 60 * 1000);
244
+ const runningId = insertPendingHeartbeatRun(now - 1000);
245
+ startHeartbeatRun(runningId);
246
+
247
+ const rows = listHeartbeatRuns();
248
+ expect(rows.map((r) => r.id).sort()).toEqual(
249
+ [okId, skippedId, missedId, runningId].sort(),
250
+ );
251
+
252
+ // The pagination cursor path applies the same filter.
253
+ const paged = listHeartbeatRuns(10, now + 2 * 60 * 60 * 1000);
254
+ expect(paged.map((r) => r.id).sort()).toEqual(
255
+ [okId, skippedId, missedId, runningId].sort(),
256
+ );
257
+ });
258
+
209
259
  test("countCompletedHeartbeatRuns counts only ok rows", () => {
210
260
  const now = Date.now();
211
261
 
@@ -240,4 +290,47 @@ describe("heartbeat-run-store", () => {
240
290
 
241
291
  expect(countCompletedHeartbeatRuns()).toBe(0);
242
292
  });
293
+
294
+ test("getLastHeartbeatRunAt returns the newest executed-run timestamp", () => {
295
+ const now = Date.now();
296
+
297
+ const oldId = insertPendingHeartbeatRun(now - 5000);
298
+ startHeartbeatRun(oldId);
299
+ completeHeartbeatRun(oldId, { status: "ok", conversationId: "conv-1" });
300
+
301
+ const newId = insertPendingHeartbeatRun(now - 1000);
302
+ startHeartbeatRun(newId);
303
+ completeHeartbeatRun(newId, { status: "error", error: "fail" });
304
+
305
+ // Non-executed rows never contribute, even when newer.
306
+ skipHeartbeatRun(insertPendingHeartbeatRun(now), "outside_active_hours");
307
+ insertPendingHeartbeatRun(now + 60 * 60 * 1000);
308
+
309
+ const newRow = allRuns().find((r) => r.id === newId);
310
+ expect(getLastHeartbeatRunAt()).toBe(newRow!.finishedAt!);
311
+ });
312
+
313
+ test("getLastHeartbeatRunAt falls back to startedAt when finishedAt is null", () => {
314
+ const now = Date.now();
315
+ const id = insertPendingHeartbeatRun(now - 60 * 60 * 1000);
316
+ startHeartbeatRun(id);
317
+ const db = getDb();
318
+ const backdatedStartedAt = now - 60 * 60 * 1000;
319
+ db.run(
320
+ sql`UPDATE heartbeat_runs SET started_at = ${backdatedStartedAt} WHERE id = ${id}`,
321
+ );
322
+ // Crash sweep finalizes the row as error without a finishedAt.
323
+ markStaleRunningAsError(45 * 60 * 1000);
324
+
325
+ expect(getLastHeartbeatRunAt()).toBe(backdatedStartedAt);
326
+ });
327
+
328
+ test("getLastHeartbeatRunAt returns null when no run ever executed", () => {
329
+ expect(getLastHeartbeatRunAt()).toBeNull();
330
+
331
+ skipHeartbeatRun(insertPendingHeartbeatRun(Date.now()), "disabled");
332
+ supersedePendingRun(insertPendingHeartbeatRun(Date.now() + 1));
333
+
334
+ expect(getLastHeartbeatRunAt()).toBeNull();
335
+ });
243
336
  });
@@ -178,6 +178,7 @@ mock.module("../../credential-health/credential-health-service.js", () => ({
178
178
  // Stub the heartbeat-run-store so the tests don't need a populated SQLite
179
179
  // schema. Each function is a no-op that returns sensible defaults.
180
180
  let heartbeatRunIdCounter = 0;
181
+ let lastRunAtFromHistory: number | null = null;
181
182
  const skipHeartbeatRunCalls: Array<{ runId: string; reason: string }> = [];
182
183
  mock.module("../heartbeat-run-store.js", () => ({
183
184
  insertPendingHeartbeatRun: () => `heartbeat-run-${++heartbeatRunIdCounter}`,
@@ -192,6 +193,7 @@ mock.module("../heartbeat-run-store.js", () => ({
192
193
  countCompletedHeartbeatRuns: () => 10,
193
194
  countCompletedRunsToday: () => 0,
194
195
  countRecentConsecutiveRuns: () => 0,
196
+ getLastHeartbeatRunAt: () => lastRunAtFromHistory,
195
197
  }));
196
198
 
197
199
  // Stub the pre-first-message gate so tests can flip it on/off without
@@ -202,6 +204,16 @@ mock.module("../../runtime/pre-first-message-gate.js", () => ({
202
204
  hasReceivedUserMessage: () => preFirstMessageGateOpen,
203
205
  }));
204
206
 
207
+ // Stub the drain quiesce lease so tests can flip it without a database.
208
+ // Defaults to INACTIVE so existing tests keep passing.
209
+ const realLifecycleQuiesce =
210
+ await import("../../persistence/lifecycle-quiesce.js");
211
+ let quiesceLeaseActive = false;
212
+ mock.module("../../persistence/lifecycle-quiesce.js", () => ({
213
+ ...realLifecycleQuiesce,
214
+ isLifecycleQuiesced: () => quiesceLeaseActive,
215
+ }));
216
+
205
217
  const { HeartbeatService } = await import("../heartbeat-service.js");
206
218
 
207
219
  let origWorkspaceDir: string | undefined;
@@ -215,6 +227,7 @@ beforeEach(() => {
215
227
  onBroadcast = null;
216
228
  runBackgroundJobCalls.length = 0;
217
229
  skipHeartbeatRunCalls.length = 0;
230
+ lastRunAtFromHistory = null;
218
231
  preFirstMessageGateOpen = true;
219
232
  seedHeartbeat();
220
233
  runBackgroundJobImpl = async () => ({
@@ -260,6 +273,24 @@ describe("HeartbeatService", () => {
260
273
  expect(call.prompt).toContain("<heartbeat-disposition>");
261
274
  });
262
275
 
276
+ test("lastRunAt rehydrates from run history when unset in memory", async () => {
277
+ const historicalRunAt = Date.now() - 60 * 60 * 1000;
278
+ lastRunAtFromHistory = historicalRunAt;
279
+
280
+ const service = new HeartbeatService();
281
+ expect(service.lastRunAt).toBe(historicalRunAt);
282
+
283
+ // An in-process run takes over from the rehydrated value.
284
+ await service.runOnce({ force: true });
285
+ expect(service.lastRunAt).toBeGreaterThan(historicalRunAt);
286
+ });
287
+
288
+ test("lastRunAt is null when no run has ever executed", () => {
289
+ lastRunAtFromHistory = null;
290
+ const service = new HeartbeatService();
291
+ expect(service.lastRunAt).toBeNull();
292
+ });
293
+
263
294
  test("fires onConversationCreated synchronously via the runner BEFORE the runner returns", async () => {
264
295
  const created: Array<{ conversationId: string; title: string }> = [];
265
296
  let runnerHasResolved = false;
@@ -389,6 +420,42 @@ describe("HeartbeatService", () => {
389
420
  ).toBe(false);
390
421
  });
391
422
 
423
+ test("scheduled run skips with reason 'quiesced' while the drain lease is active", async () => {
424
+ quiesceLeaseActive = true;
425
+ const service = new HeartbeatService();
426
+ service.start();
427
+ skipHeartbeatRunCalls.length = 0;
428
+
429
+ try {
430
+ const result = await service.runOnce({ force: false });
431
+
432
+ expect(result).toBe(false);
433
+ expect(runBackgroundJobCalls).toHaveLength(0);
434
+ expect(skipHeartbeatRunCalls.some((c) => c.reason === "quiesced")).toBe(
435
+ true,
436
+ );
437
+ } finally {
438
+ await service.stop();
439
+ quiesceLeaseActive = false;
440
+ }
441
+ });
442
+
443
+ test("forced run bypasses the quiesce gate (manual operator action)", async () => {
444
+ quiesceLeaseActive = true;
445
+ const service = new HeartbeatService();
446
+
447
+ try {
448
+ await service.runOnce({ force: true });
449
+
450
+ expect(runBackgroundJobCalls).toHaveLength(1);
451
+ expect(skipHeartbeatRunCalls.some((c) => c.reason === "quiesced")).toBe(
452
+ false,
453
+ );
454
+ } finally {
455
+ quiesceLeaseActive = false;
456
+ }
457
+ });
458
+
392
459
  describe("max consecutive runs cap", () => {
393
460
  test("skips with reason 'max_consecutive_runs' after the cap is hit", async () => {
394
461
  seedHeartbeat({ maxConsecutiveRuns: 2 });
@@ -1,4 +1,14 @@
1
- import { desc, eq, lt, sql } from "drizzle-orm";
1
+ import {
2
+ and,
3
+ desc,
4
+ eq,
5
+ gt,
6
+ inArray,
7
+ isNotNull,
8
+ lt,
9
+ notInArray,
10
+ sql,
11
+ } from "drizzle-orm";
2
12
  import { v4 as uuid } from "uuid";
3
13
 
4
14
  import { getDb } from "../persistence/db-connection.js";
@@ -25,7 +35,8 @@ export type HeartbeatSkipReason =
25
35
  | "overlap"
26
36
  | "pre_first_user_message"
27
37
  | "max_consecutive_runs"
28
- | "max_daily_runs";
38
+ | "max_daily_runs"
39
+ | "quiesced";
29
40
 
30
41
  export interface HeartbeatRunRecord {
31
42
  id: string;
@@ -271,7 +282,19 @@ export function countRecentConsecutiveRuns(maxToCheck: number): number {
271
282
  }
272
283
 
273
284
  /**
274
- * List heartbeat runs ordered by `scheduledFor` descending.
285
+ * Statuses excluded from run history: scheduler bookkeeping rows, not runs.
286
+ * `pending` is the not-yet-fired next scheduled run (surfaced separately as
287
+ * "next run"), and `superseded` rows are pending rows replaced by a timer
288
+ * reset — a guardian message resets the heartbeat timer, so an active chat
289
+ * day leaves dozens of them behind without a single heartbeat firing.
290
+ */
291
+ const BOOKKEEPING_STATUSES: HeartbeatRunStatus[] = ["pending", "superseded"];
292
+
293
+ /**
294
+ * List heartbeat run history ordered by `scheduledFor` descending.
295
+ * Bookkeeping rows ({@link BOOKKEEPING_STATUSES}) are excluded — every
296
+ * returned row is a run that executed, was skipped by a guard (with its
297
+ * `skipReason`), or was missed while the daemon was down.
275
298
  * When `before` is set, only runs with `scheduledFor` strictly older than it
276
299
  * are returned (cursor for paginating into history).
277
300
  */
@@ -280,16 +303,70 @@ export function listHeartbeatRuns(
280
303
  before?: number,
281
304
  ): HeartbeatRunRecord[] {
282
305
  const db = getDb();
306
+ const historyOnly = notInArray(heartbeatRuns.status, BOOKKEEPING_STATUSES);
283
307
  const rows = db
284
308
  .select()
285
309
  .from(heartbeatRuns)
286
- .where(before != null ? lt(heartbeatRuns.scheduledFor, before) : undefined)
310
+ .where(
311
+ before != null
312
+ ? and(lt(heartbeatRuns.scheduledFor, before), historyOnly)
313
+ : historyOnly,
314
+ )
287
315
  .orderBy(desc(heartbeatRuns.scheduledFor))
288
316
  .limit(limit)
289
317
  .all();
290
318
  return rows.map(parseRow);
291
319
  }
292
320
 
321
+ /** Terminal statuses of runs that actually executed. */
322
+ const EXECUTED_STATUSES: HeartbeatRunStatus[] = ["ok", "error", "timeout"];
323
+
324
+ /**
325
+ * Epoch-ms timestamp of the most recent heartbeat run that actually executed
326
+ * (reached `ok`, `error`, or `timeout`), or null when none exists. Uses
327
+ * `finishedAt`, falling back to `startedAt` for rows finalized by a crash
328
+ * sweep without one.
329
+ */
330
+ export function getLastHeartbeatRunAt(): number | null {
331
+ const db = getDb();
332
+ const row = db
333
+ .select({
334
+ lastRunAt: sql<
335
+ number | null
336
+ >`max(coalesce(${heartbeatRuns.finishedAt}, ${heartbeatRuns.startedAt}))`,
337
+ })
338
+ .from(heartbeatRuns)
339
+ .where(inArray(heartbeatRuns.status, EXECUTED_STATUSES))
340
+ .get();
341
+ return row?.lastRunAt ?? null;
342
+ }
343
+
344
+ /**
345
+ * Heartbeat runs currently `running`, capped to those started within
346
+ * `maxAgeMs`. The age bound matters: a run orphaned by a crash keeps its
347
+ * `running` status until a later boot's stale sweep relabels it, and an
348
+ * unbounded read would let that phantom row hold a drain open indefinitely.
349
+ */
350
+ export function listRunningHeartbeatRuns(
351
+ maxAgeMs: number,
352
+ ): Array<{ runId: string; startedAt: number }> {
353
+ const db = getDb();
354
+ const cutoff = Date.now() - maxAgeMs;
355
+ return db
356
+ .select({ runId: heartbeatRuns.id, startedAt: heartbeatRuns.startedAt })
357
+ .from(heartbeatRuns)
358
+ .where(
359
+ and(
360
+ eq(heartbeatRuns.status, "running"),
361
+ isNotNull(heartbeatRuns.startedAt),
362
+ gt(heartbeatRuns.startedAt, cutoff),
363
+ ),
364
+ )
365
+ .orderBy(desc(heartbeatRuns.startedAt))
366
+ .limit(20)
367
+ .all() as Array<{ runId: string; startedAt: number }>;
368
+ }
369
+
293
370
  // ---------------------------------------------------------------------------
294
371
  // Internal helpers
295
372
  // ---------------------------------------------------------------------------
@@ -11,6 +11,7 @@ import {
11
11
  shouldLogDiskPressureBackgroundSkip,
12
12
  } from "../daemon/disk-pressure-background-gate.js";
13
13
  import { emitNotificationSignal } from "../notifications/emit-signal.js";
14
+ import { isLifecycleQuiesced } from "../persistence/lifecycle-quiesce.js";
14
15
  import {
15
16
  GUARDIAN_PERSONA_TEMPLATE,
16
17
  resolveGuardianPersona,
@@ -29,6 +30,7 @@ import {
29
30
  countCompletedHeartbeatRuns,
30
31
  countCompletedRunsToday,
31
32
  countRecentConsecutiveRuns,
33
+ getLastHeartbeatRunAt,
32
34
  insertPendingHeartbeatRun,
33
35
  markStaleRunningAsError,
34
36
  markStaleRunsAsMissed,
@@ -168,8 +170,23 @@ export class HeartbeatService {
168
170
  HeartbeatService.instance = this;
169
171
  }
170
172
 
171
- /** Epoch-ms timestamp of the last completed heartbeat run. */
173
+ /**
174
+ * Epoch-ms timestamp of the last completed heartbeat run. The in-memory
175
+ * field only covers runs completed in this process's lifetime, so when it
176
+ * is unset the value is rehydrated from run history — a daemon restart
177
+ * must not blank "last run" while completed runs exist in the database.
178
+ */
172
179
  get lastRunAt(): number | null {
180
+ if (this._lastRunAt == null) {
181
+ try {
182
+ this._lastRunAt = getLastHeartbeatRunAt();
183
+ } catch (err) {
184
+ // DB unavailable (e.g. migrations still settling) — report unknown
185
+ // and retry on the next read.
186
+ log.debug({ err }, "Failed to read last heartbeat run from history");
187
+ return null;
188
+ }
189
+ }
173
190
  return this._lastRunAt;
174
191
  }
175
192
 
@@ -486,6 +503,20 @@ export class HeartbeatService {
486
503
  return false;
487
504
  }
488
505
 
506
+ // Drain guard: while a quiesce lease is active (a client is waiting for
507
+ // background work to finish before stopping the assistant), do not start
508
+ // new beats. Recorded as a skipped run so run history explains the gap.
509
+ if (!force && isLifecycleQuiesced()) {
510
+ log.info("Heartbeat skipped — quiesce lease active");
511
+ if (runId) {
512
+ skipHeartbeatRun(runId, "quiesced");
513
+ }
514
+ if (!this.cronMode) {
515
+ this.scheduleNextRun(config.intervalMs);
516
+ }
517
+ return false;
518
+ }
519
+
489
520
  // Warm-pool guard: skip heartbeats until the user has actually
490
521
  // interacted with the assistant. Heartbeats run the LLM against the
491
522
  // guardian persona, which doesn't exist in a fresh warm-pool image —
@@ -35,7 +35,9 @@ mock.module("../../runtime/assistant-event-hub.js", () => ({
35
35
  // hub's real shape has more fields. Tests never call them.
36
36
  subscribe: () => () => {},
37
37
  },
38
- broadcastMessage: async () => {},
38
+ broadcastMessage: (message: unknown, conversationId?: string) => {
39
+ void publishSpy({ message, conversationId });
40
+ },
39
41
  }));
40
42
 
41
43
  // Dynamic import so the module resolves after the mock above is in
@@ -717,11 +719,7 @@ describe("feed-writer", () => {
717
719
  }),
718
720
  );
719
721
 
720
- const count = await bulkSetFeedItemStatus(
721
- ["new"],
722
- "seen",
723
- ["n1", "n3"],
724
- );
722
+ const count = await bulkSetFeedItemStatus(["new"], "seen", ["n1", "n3"]);
725
723
  expect(count).toBe(2);
726
724
 
727
725
  const decoded = readFileJson();
@@ -734,7 +732,9 @@ describe("feed-writer", () => {
734
732
  test("ids scope with no matching ids returns 0", async () => {
735
733
  await appendFeedItem(makeItem({ id: "n1", status: "new" }));
736
734
 
737
- const count = await bulkSetFeedItemStatus(["new"], "seen", ["nonexistent"]);
735
+ const count = await bulkSetFeedItemStatus(["new"], "seen", [
736
+ "nonexistent",
737
+ ]);
738
738
  expect(count).toBe(0);
739
739
 
740
740
  const decoded = readFileJson();
@@ -26,6 +26,9 @@ mock.module("../suggested-prompts.js", () => ({
26
26
  const publishSpy = mock<(event: unknown) => Promise<void>>(async () => {});
27
27
  mock.module("../../runtime/assistant-event-hub.js", () => ({
28
28
  assistantEventHub: { publish: publishSpy },
29
+ broadcastMessage: (message: unknown) => {
30
+ void publishSpy(message);
31
+ },
29
32
  }));
30
33
 
31
34
  mock.module("../../runtime/assistant-event.js", () => ({