@vellumai/assistant 0.11.2 → 0.11.3-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 (720) hide show
  1. package/ARCHITECTURE.md +58 -3
  2. package/docs/trusted-contact-access.md +9 -5
  3. package/docs/vellum-doctor.md +195 -0
  4. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
  5. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  6. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  7. package/node_modules/@vellumai/ces-client/src/index.ts +1 -0
  8. package/node_modules/@vellumai/ces-client/src/rpc-client.ts +19 -2
  9. package/node_modules/@vellumai/environments/package.json +2 -1
  10. package/node_modules/@vellumai/environments/src/__tests__/cloud-assistant-hub-url.test.ts +38 -0
  11. package/node_modules/@vellumai/environments/src/__tests__/install-layout.test.ts +98 -0
  12. package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +5 -5
  13. package/node_modules/@vellumai/environments/src/index.ts +17 -6
  14. package/node_modules/@vellumai/environments/src/install-layout.ts +49 -0
  15. package/node_modules/@vellumai/environments/src/seeds.ts +29 -0
  16. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
  17. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  18. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  19. package/node_modules/@vellumai/gateway-client/src/index.ts +4 -0
  20. package/node_modules/@vellumai/gateway-client/src/verification-session-contract.ts +91 -6
  21. package/node_modules/@vellumai/ipc-server-utils/src/endpoint.test.ts +36 -0
  22. package/node_modules/@vellumai/ipc-server-utils/src/endpoint.ts +142 -0
  23. package/node_modules/@vellumai/ipc-server-utils/src/index.ts +2 -0
  24. package/node_modules/@vellumai/ipc-server-utils/src/listen-options.ts +3 -0
  25. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +15 -1
  26. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +17 -2
  27. package/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
  28. package/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  29. package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  30. package/node_modules/@vellumai/slack-text/src/index.ts +16 -2
  31. package/openapi.yaml +589 -30
  32. package/package.json +1 -1
  33. package/src/__tests__/agent-image-optimize.test.ts +4 -4
  34. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +113 -66
  35. package/src/__tests__/agent-loop-override-profile.test.ts +38 -48
  36. package/src/__tests__/agent-loop-subagent-billing-headers.test.ts +166 -0
  37. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
  38. package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
  39. package/src/__tests__/answered-question-persistence.test.ts +240 -0
  40. package/src/__tests__/anthropic-provider.test.ts +165 -30
  41. package/src/__tests__/app-control-flow.test.ts +9 -8
  42. package/src/__tests__/assistant-event-hub.test.ts +48 -0
  43. package/src/__tests__/attachments-store-heic-normalize.test.ts +16 -12
  44. package/src/__tests__/attachments-store.test.ts +130 -68
  45. package/src/__tests__/attachments.test.ts +36 -36
  46. package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
  47. package/src/__tests__/call-start-guardian-guard.test.ts +18 -4
  48. package/src/__tests__/canned-reply-release.test.ts +86 -0
  49. package/src/__tests__/channel-guardian.test.ts +59 -29
  50. package/src/__tests__/channel-readiness-telegram-remote.test.ts +154 -0
  51. package/src/__tests__/channel-setup-panel-ack.test.ts +6 -5
  52. package/src/__tests__/client-os-metadata-persistence.test.ts +103 -4
  53. package/src/__tests__/compactor-image-manifest-trust.test.ts +1 -1
  54. package/src/__tests__/consult-deadline.test.ts +65 -0
  55. package/src/__tests__/conversation-agent-loop.test.ts +438 -2
  56. package/src/__tests__/conversation-disk-view-integration.test.ts +5 -1
  57. package/src/__tests__/conversation-disk-view.test.ts +4 -4
  58. package/src/__tests__/conversation-error.test.ts +22 -3
  59. package/src/__tests__/conversation-fork-crud.test.ts +5 -1
  60. package/src/__tests__/conversation-fork-referential.test.ts +352 -0
  61. package/src/__tests__/conversation-fork-retrospective.test.ts +5 -1
  62. package/src/__tests__/conversation-group-tools.test.ts +32 -2
  63. package/src/__tests__/conversation-inference-profile-route.test.ts +27 -1
  64. package/src/__tests__/conversation-initial-prompt.test.ts +51 -22
  65. package/src/__tests__/conversation-key-store-bootstrap-cleanup.test.ts +119 -0
  66. package/src/__tests__/conversation-key-store-origin.test.ts +105 -0
  67. package/src/__tests__/conversation-list-recency-ordering.test.ts +104 -0
  68. package/src/__tests__/conversation-origin-at-creation.test.ts +153 -0
  69. package/src/__tests__/conversation-origin-channel-filter.test.ts +95 -0
  70. package/src/__tests__/conversation-placement-promotion.test.ts +172 -0
  71. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +123 -0
  72. package/src/__tests__/conversation-queue.test.ts +433 -1
  73. package/src/__tests__/conversation-retry-route.test.ts +3 -1
  74. package/src/__tests__/conversation-routes-hidden-queue.test.ts +64 -1
  75. package/src/__tests__/conversation-store-ephemeral.test.ts +4 -0
  76. package/src/__tests__/conversation-store.test.ts +26 -10
  77. package/src/__tests__/conversation-summarize-route.test.ts +1 -1
  78. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +15 -6
  79. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -5
  80. package/src/__tests__/conversation-surfaces-app-control.test.ts +12 -11
  81. package/src/__tests__/conversation-surfaces-app-open.test.ts +11 -5
  82. package/src/__tests__/conversation-surfaces-data-persist.test.ts +5 -4
  83. package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +967 -0
  84. package/src/__tests__/conversation-surfaces-persisted-info.test.ts +312 -0
  85. package/src/__tests__/conversation-surfaces-queued-emit.test.ts +6 -5
  86. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -8
  87. package/src/__tests__/conversation-surfaces-standalone.test.ts +21 -11
  88. package/src/__tests__/conversation-surfaces-state-update.test.ts +7 -11
  89. package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -6
  90. package/src/__tests__/conversation-surfaces-task-progress.test.ts +11 -6
  91. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +9 -6
  92. package/src/__tests__/conversation-tool-setup-attribution.test.ts +70 -7
  93. package/src/__tests__/conversation-wait-for-idle.test.ts +6 -11
  94. package/src/__tests__/credential-prompt-route.test.ts +57 -7
  95. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  96. package/src/__tests__/cu-unified-flow.test.ts +89 -28
  97. package/src/__tests__/custom-group-listing-visibility.test.ts +1 -1
  98. package/src/__tests__/db-schedule-syntax-migration.test.ts +4 -1
  99. package/src/__tests__/discord-access-request-privacy.test.ts +366 -0
  100. package/src/__tests__/discord-requester-notice-privacy.test.ts +87 -0
  101. package/src/__tests__/disk-pressure-guard.test.ts +55 -55
  102. package/src/__tests__/disk-pressure-routes.test.ts +10 -10
  103. package/src/__tests__/disk-pressure-tools.test.ts +2 -2
  104. package/src/__tests__/disk-usage.test.ts +35 -20
  105. package/src/__tests__/document-sync-tags.test.ts +379 -0
  106. package/src/__tests__/document-update-default-surface.test.ts +28 -0
  107. package/src/__tests__/dynamic-skill-background-guard.test.ts +2 -2
  108. package/src/__tests__/emit-signal-routing-intent.test.ts +3 -4
  109. package/src/__tests__/ensure-conversation-exists.test.ts +8 -5
  110. package/src/__tests__/events-dev-bypass-actor.test.ts +183 -1
  111. package/src/__tests__/evict-conversations-for-reload.test.ts +90 -0
  112. package/src/__tests__/file-list-tool.test.ts +12 -10
  113. package/src/__tests__/file-ops-service.test.ts +151 -44
  114. package/src/__tests__/file-read-container-boundary.test.ts +137 -0
  115. package/src/__tests__/file-read-tool.test.ts +21 -9
  116. package/src/__tests__/file-url-path-guard.test.ts +83 -0
  117. package/src/__tests__/filesystem-tools.test.ts +82 -66
  118. package/src/__tests__/guardian-outbound-http.test.ts +8 -8
  119. package/src/__tests__/helpers/gateway-verification-sessions-stub.ts +14 -1
  120. package/src/__tests__/helpers/hub-clients.ts +42 -0
  121. package/src/__tests__/helpers/mock-conversation.ts +46 -0
  122. package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +61 -57
  123. package/src/__tests__/history-repair.test.ts +112 -12
  124. package/src/__tests__/host-bash-proxy.test.ts +10 -5
  125. package/src/__tests__/host-bash-routes.test.ts +67 -0
  126. package/src/__tests__/host-browser-proxy.test.ts +2 -2
  127. package/src/__tests__/host-cu-proxy.test.ts +3 -7
  128. package/src/__tests__/host-file-proxy.test.ts +3 -7
  129. package/src/__tests__/host-file-read-tool.test.ts +5 -1
  130. package/src/__tests__/host-transfer-proxy-targeted.test.ts +6 -4
  131. package/src/__tests__/http-conversation-lineage.test.ts +2 -5
  132. package/src/__tests__/http-user-message-parity.test.ts +44 -0
  133. package/src/__tests__/identity-routes.test.ts +13 -13
  134. package/src/__tests__/image-conversion.test.ts +29 -26
  135. package/src/__tests__/image-source-path-reinject.test.ts +2 -2
  136. package/src/__tests__/inference-profile-session-handler.test.ts +95 -0
  137. package/src/__tests__/inference-profile-session-ipc.test.ts +25 -0
  138. package/src/__tests__/introduction-card-resolver.test.ts +6 -1
  139. package/src/__tests__/list-messages-attachments.test.ts +56 -32
  140. package/src/__tests__/list-messages-background-tool-completion.test.ts +4 -4
  141. package/src/__tests__/list-messages-client-message-id.test.ts +2 -2
  142. package/src/__tests__/list-messages-hidden-metadata.test.ts +16 -16
  143. package/src/__tests__/list-messages-page-latest.test.ts +61 -61
  144. package/src/__tests__/list-messages-provider-error.test.ts +6 -6
  145. package/src/__tests__/list-messages-queued.test.ts +49 -6
  146. package/src/__tests__/list-messages-system-card.test.ts +4 -4
  147. package/src/__tests__/list-messages-tool-merge.test.ts +9 -9
  148. package/src/__tests__/llm-resolver-override-or-default.test.ts +141 -0
  149. package/src/__tests__/llm-usage-store.test.ts +55 -0
  150. package/src/__tests__/manual-token-reconciliation.test.ts +45 -0
  151. package/src/__tests__/mcp-tool-annotations-risk.test.ts +133 -0
  152. package/src/__tests__/media-stream-server-integration.test.ts +36 -0
  153. package/src/__tests__/migration-export-http.test.ts +1 -1
  154. package/src/__tests__/migration-import-commit-http.test.ts +1 -1
  155. package/src/__tests__/migration-import-preflight-http.test.ts +1 -1
  156. package/src/__tests__/migration-validate-http.test.ts +1 -1
  157. package/src/__tests__/mtime-cache.test.ts +65 -3
  158. package/src/__tests__/notification-platform-adapter.test.ts +104 -0
  159. package/src/__tests__/notification-schedule-notify-dedup.test.ts +1 -1
  160. package/src/__tests__/oauth-provider-profiles.test.ts +4 -2
  161. package/src/__tests__/oauth-provider-seed-logos.test.ts +65 -0
  162. package/src/__tests__/oauth-provider-serializer.test.ts +17 -0
  163. package/src/__tests__/oauth-providers-routes.test.ts +1 -0
  164. package/src/__tests__/openai-responses-prompt-cache.test.ts +5 -2
  165. package/src/__tests__/path-policy.test.ts +164 -1
  166. package/src/__tests__/persist-media-references.test.ts +2 -2
  167. package/src/__tests__/persist-unsendable-image-downscale.test.ts +5 -5
  168. package/src/__tests__/persist-unsendable-image.test.ts +23 -23
  169. package/src/__tests__/persona-resolver.test.ts +154 -0
  170. package/src/__tests__/plugin-api-webhook-url.test.ts +170 -0
  171. package/src/__tests__/plugin-import-boundary-guard.test.ts +6 -0
  172. package/src/__tests__/processing-flag-persist-failure.test.ts +15 -10
  173. package/src/__tests__/profile-availability-incomplete.test.ts +113 -0
  174. package/src/__tests__/profiler-routes.test.ts +13 -13
  175. package/src/__tests__/prompt-cache-cross-turn-stability.test.ts +593 -0
  176. package/src/__tests__/proxy-approval-callback.test.ts +3 -3
  177. package/src/__tests__/queued-message-cancel-and-steer.test.ts +189 -0
  178. package/src/__tests__/queued-message-steer-actor-scoping.test.ts +164 -0
  179. package/src/__tests__/resolve-trust-class.test.ts +20 -0
  180. package/src/__tests__/run-conversation-turn-persistence.test.ts +1 -1
  181. package/src/__tests__/run-due-schedules.test.ts +174 -0
  182. package/src/__tests__/runtime-attachment-metadata.test.ts +15 -7
  183. package/src/__tests__/scaffold-managed-skill-tool.test.ts +166 -0
  184. package/src/__tests__/schedule-routes.test.ts +730 -8
  185. package/src/__tests__/schedule-store.test.ts +540 -0
  186. package/src/__tests__/schedule-tools.test.ts +103 -0
  187. package/src/__tests__/scheduler-wake.test.ts +5 -0
  188. package/src/__tests__/secret-routes-platform-proxy.test.ts +29 -1
  189. package/src/__tests__/secure-keys.test.ts +91 -0
  190. package/src/__tests__/server-history-render.test.ts +97 -7
  191. package/src/__tests__/size-guard.test.ts +8 -8
  192. package/src/__tests__/skill-docs-sync-guard.test.ts +3 -2
  193. package/src/__tests__/skill-tool-factory.test.ts +247 -0
  194. package/src/__tests__/skills-install-staging.test.ts +15 -6
  195. package/src/__tests__/slack-edit-ordering-characterization.test.ts +103 -0
  196. package/src/__tests__/slack-inbound-verification.test.ts +4 -1
  197. package/src/__tests__/slack-mention-binding-invariance.test.ts +103 -0
  198. package/src/__tests__/slack-mention-provider-leak-audit.test.ts +135 -0
  199. package/src/__tests__/starter-task-flow.test.ts +5 -4
  200. package/src/__tests__/steer-on-enqueue-question.test.ts +3 -2
  201. package/src/__tests__/stt-language-catalog-parity.test.ts +38 -0
  202. package/src/__tests__/subagent-allowlist-validation.test.ts +26 -4
  203. package/src/__tests__/subagent-cron-run-attribution.test.ts +301 -0
  204. package/src/__tests__/subagent-disposal.test.ts +10 -0
  205. package/src/__tests__/subagent-fork-notifications.test.ts +10 -0
  206. package/src/__tests__/subagent-fork-prompt-role.test.ts +85 -11
  207. package/src/__tests__/subagent-fork-spawn.test.ts +15 -7
  208. package/src/__tests__/subagent-manager-notify.test.ts +148 -0
  209. package/src/__tests__/subagent-notify-parent.test.ts +1 -1
  210. package/src/__tests__/subagent-persistence.test.ts +141 -0
  211. package/src/__tests__/subagent-role-registry.test.ts +172 -63
  212. package/src/__tests__/subagent-role-resolution.test.ts +88 -0
  213. package/src/__tests__/subagent-spawn-and-await.test.ts +48 -0
  214. package/src/__tests__/subagent-spawn-tool-fork.test.ts +111 -10
  215. package/src/__tests__/subagent-terminal-message.test.ts +88 -1
  216. package/src/__tests__/subagent-tool-gate-mode.test.ts +225 -10
  217. package/src/__tests__/subagent-tools.test.ts +1901 -108
  218. package/src/__tests__/surface-completion-compaction-boundary.test.ts +124 -0
  219. package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +298 -0
  220. package/src/__tests__/system-prompt.test.ts +82 -7
  221. package/src/__tests__/tool-grant-request-escalation.test.ts +40 -1
  222. package/src/__tests__/tool-result-spool.test.ts +74 -18
  223. package/src/__tests__/tool-side-effects-documents.test.ts +174 -0
  224. package/src/__tests__/tool-side-effects-hook-failures.test.ts +129 -0
  225. package/src/__tests__/tools-audio-read.test.ts +10 -10
  226. package/src/__tests__/tools-get-route.test.ts +85 -12
  227. package/src/__tests__/tools-list-cli.test.ts +171 -0
  228. package/src/__tests__/top-level-renderer.test.ts +54 -1
  229. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +68 -1
  230. package/src/__tests__/trusted-contact-multichannel.test.ts +4 -1
  231. package/src/__tests__/turn-tail-chain.test.ts +135 -0
  232. package/src/__tests__/ui-choice-copy-surfaces.test.ts +6 -4
  233. package/src/__tests__/ui-visual-surface.test.ts +5 -4
  234. package/src/__tests__/ui-work-result-surface.test.ts +5 -4
  235. package/src/__tests__/unified-turn-context-visible-app.test.ts +7 -8
  236. package/src/__tests__/usage-routes.test.ts +62 -0
  237. package/src/__tests__/verification-outbound-delivery.test.ts +176 -0
  238. package/src/__tests__/visible-app-context.test.ts +20 -4
  239. package/src/__tests__/web-search-history.test.ts +96 -0
  240. package/src/__tests__/workspace-migration-139-clear-renamed-cost-profile-label.test.ts +6 -6
  241. package/src/__tests__/workspace-migration-140-repair-seed-pinned-memory-v3-live.test.ts +330 -0
  242. package/src/acp/failure-error.ts +3 -5
  243. package/src/agent/attachments.ts +26 -24
  244. package/src/agent/history-repair/history-repair.ts +27 -26
  245. package/src/agent/image-optimize.ts +6 -6
  246. package/src/agent/loop.ts +40 -22
  247. package/src/agent/message-types.ts +3 -3
  248. package/src/api/constants/document-tools.ts +55 -0
  249. package/src/api/events/question-answered.ts +53 -0
  250. package/src/api/events/tool-result.ts +7 -0
  251. package/src/api/index.ts +13 -0
  252. package/src/api/responses/conversation-message.ts +11 -0
  253. package/src/api/surfaces-oauth-connect.test.ts +96 -0
  254. package/src/api/surfaces.ts +36 -8
  255. package/src/approvals/guardian-channel-delivery.ts +83 -0
  256. package/src/approvals/guardian-expiry-notifier.ts +13 -11
  257. package/src/approvals/guardian-request-resolvers.ts +222 -135
  258. package/src/calls/__tests__/voice-session-bridge.test.ts +50 -0
  259. package/src/calls/media-stream-server.ts +9 -1
  260. package/src/calls/voice-session-bridge.ts +38 -0
  261. package/src/channels/__tests__/gateway-verification-sessions.test.ts +3 -3
  262. package/src/channels/config.ts +7 -2
  263. package/src/channels/gateway-verification-sessions.ts +7 -2
  264. package/src/channels/types.ts +7 -1
  265. package/src/cli/commands/__tests__/cache.test.ts +7 -4
  266. package/src/cli/commands/__tests__/cli-test-harness.ts +82 -0
  267. package/src/cli/commands/__tests__/conversations-wake.test.ts +51 -6
  268. package/src/cli/commands/__tests__/inference-models.test.ts +14 -0
  269. package/src/cli/commands/__tests__/inference-profiles.test.ts +44 -1
  270. package/src/cli/commands/__tests__/inference-providers.test.ts +7 -0
  271. package/src/cli/commands/__tests__/plugins.test.ts +692 -0
  272. package/src/cli/commands/__tests__/schedules.test.ts +136 -37
  273. package/src/cli/commands/conversations.ts +7 -3
  274. package/src/cli/commands/credentials.help.ts +7 -0
  275. package/src/cli/commands/inference-profiles.ts +9 -3
  276. package/src/cli/commands/inference.help.ts +16 -2
  277. package/src/cli/commands/mcp.help.ts +4 -2
  278. package/src/cli/commands/memory/memory-retrospective.ts +18 -0
  279. package/src/cli/commands/notifications.ts +3 -6
  280. package/src/cli/commands/oauth/connect-surface-guidance.test.ts +16 -0
  281. package/src/cli/commands/oauth/connect-surface-guidance.ts +25 -7
  282. package/src/cli/commands/oauth/connect.test.ts +26 -1
  283. package/src/cli/commands/oauth/connect.ts +4 -1
  284. package/src/cli/commands/oauth/index.help.ts +3 -1
  285. package/src/cli/commands/oauth/providers.ts +3 -0
  286. package/src/cli/commands/plugins.help.ts +25 -2
  287. package/src/cli/commands/plugins.ts +289 -10
  288. package/src/cli/commands/schedules.help.ts +14 -12
  289. package/src/cli/commands/schedules.ts +19 -5
  290. package/src/cli/commands/tools.help.ts +4 -2
  291. package/src/cli/commands/tools.ts +38 -0
  292. package/src/cli/lib/__tests__/inspect-plugin.test.ts +1 -0
  293. package/src/cli/lib/__tests__/install-from-github.test.ts +79 -0
  294. package/src/cli/lib/__tests__/install-from-platform.test.ts +47 -1
  295. package/src/cli/lib/__tests__/plugin-surfaces.test.ts +86 -7
  296. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +122 -0
  297. package/src/cli/lib/install-from-github.ts +61 -0
  298. package/src/cli/lib/install-from-platform.ts +13 -2
  299. package/src/cli/lib/plugin-surfaces.ts +124 -4
  300. package/src/cli/lib/upgrade-plugin.ts +18 -0
  301. package/src/cli.ts +1 -1
  302. package/src/config/__tests__/deployment-context-defaults.test.ts +73 -0
  303. package/src/config/__tests__/memory-retrospective-schema.test.ts +40 -0
  304. package/src/config/__tests__/plugin-updates-schema.test.ts +43 -0
  305. package/src/config/__tests__/profile-tool-support.test.ts +131 -0
  306. package/src/config/bundled-skills/AGENTS.md +1 -1
  307. package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
  308. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +3 -3
  309. package/src/config/bundled-skills/schedule/SKILL.md +5 -1
  310. package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
  311. package/src/config/bundled-skills/settings/TOOLS.json +2 -2
  312. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +1 -0
  313. package/src/config/bundled-skills/subagent/SKILL.md +53 -22
  314. package/src/config/bundled-skills/subagent/TOOLS.json +14 -13
  315. package/src/config/bundled-skills/visualize/SKILL.md +1 -1
  316. package/src/config/default-profile-catalog.ts +6 -2
  317. package/src/config/feature-flag-registry.json +25 -17
  318. package/src/config/llm-resolver.ts +45 -15
  319. package/src/config/loader.ts +70 -1
  320. package/src/config/profile-tool-support.ts +110 -0
  321. package/src/config/sanitize-for-transfer.ts +11 -0
  322. package/src/config/schema.ts +4 -0
  323. package/src/config/schemas/__tests__/stt.test.ts +20 -5
  324. package/src/config/schemas/channels.ts +13 -0
  325. package/src/config/schemas/memory-retrospective.ts +25 -0
  326. package/src/config/schemas/plugin-updates.ts +55 -0
  327. package/src/config/schemas/services.ts +8 -4
  328. package/src/config/schemas/stt.ts +17 -5
  329. package/src/config/schemas/timeouts.ts +1 -1
  330. package/src/contacts/__tests__/guardian-delivery-reader.test.ts +16 -0
  331. package/src/contacts/guardian-delivery-reader.ts +29 -2
  332. package/src/context/compactor.ts +4 -4
  333. package/src/context/post-turn-tool-result-truncation.ts +32 -16
  334. package/src/context/strip-injections.ts +42 -0
  335. package/src/context/tool-result-spool.ts +32 -18
  336. package/src/credential-execution/ces-runtime.ts +3 -0
  337. package/src/credential-execution/client.ts +22 -1
  338. package/src/credential-execution/process-manager.test.ts +99 -0
  339. package/src/credential-execution/process-manager.ts +62 -16
  340. package/src/daemon/__tests__/abort-null-controller.test.ts +141 -5
  341. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +8 -12
  342. package/src/daemon/__tests__/install-assistant-command.test.ts +234 -0
  343. package/src/daemon/__tests__/turn-tail-assistant-reply-notify.test.ts +2 -4
  344. package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +20 -29
  345. package/src/daemon/channel-ui-capability.ts +4 -4
  346. package/src/daemon/conversation-agent-loop-handlers.ts +117 -61
  347. package/src/daemon/conversation-agent-loop.ts +318 -148
  348. package/src/daemon/conversation-attachments.ts +1 -1
  349. package/src/daemon/conversation-error.ts +8 -1
  350. package/src/daemon/conversation-initial-prompt.ts +16 -38
  351. package/src/daemon/conversation-lifecycle.ts +154 -32
  352. package/src/daemon/conversation-messaging.ts +106 -43
  353. package/src/daemon/conversation-process.ts +131 -33
  354. package/src/daemon/conversation-queue-manager.ts +10 -0
  355. package/src/daemon/conversation-runtime-assembly.ts +9 -4
  356. package/src/daemon/conversation-store.ts +52 -3
  357. package/src/daemon/conversation-surface-state.ts +9 -0
  358. package/src/daemon/conversation-surfaces.ts +403 -210
  359. package/src/daemon/conversation-tool-setup.ts +56 -7
  360. package/src/daemon/conversation-turn-finalize.ts +134 -89
  361. package/src/daemon/conversation.ts +111 -27
  362. package/src/daemon/disk-pressure-guard-lifecycle.ts +2 -2
  363. package/src/daemon/disk-pressure-guard.ts +3 -3
  364. package/src/daemon/doordash-steps.ts +5 -5
  365. package/src/daemon/external-plugins-bootstrap.ts +7 -1
  366. package/src/daemon/handlers/config-channels.ts +48 -5
  367. package/src/daemon/handlers/conversation-history.ts +1 -1
  368. package/src/daemon/handlers/conversations.ts +62 -17
  369. package/src/daemon/handlers/shared.ts +12 -0
  370. package/src/daemon/handlers/skills.ts +11 -1
  371. package/src/daemon/identity-helpers.ts +38 -10
  372. package/src/daemon/install-assistant-command.ts +322 -0
  373. package/src/daemon/lifecycle.ts +14 -3
  374. package/src/daemon/message-types/conversations.ts +6 -7
  375. package/src/daemon/message-types/sync.ts +1 -0
  376. package/src/daemon/persist-media-references.ts +23 -14
  377. package/src/daemon/process-message.ts +6 -6
  378. package/src/daemon/tool-setup-types.ts +19 -122
  379. package/src/daemon/tool-side-effects.ts +17 -94
  380. package/src/daemon/trust-context-types.ts +39 -0
  381. package/src/daemon/trust-context.ts +14 -6
  382. package/src/daemon/turn-tail-chain.ts +73 -0
  383. package/src/daemon/web-search-history.ts +47 -33
  384. package/src/heartbeat/heartbeat-service.ts +6 -6
  385. package/src/ipc/__tests__/socket-path.test.ts +21 -0
  386. package/src/ipc/assistant-server.ts +17 -10
  387. package/src/ipc/cli-client.ts +10 -1
  388. package/src/ipc/routes/__tests__/documents-sync-ipc-routes.test.ts +56 -0
  389. package/src/ipc/routes/documents-sync-ipc-routes.ts +42 -0
  390. package/src/ipc/socket-cleanup.ts +9 -7
  391. package/src/ipc/socket-path.ts +4 -96
  392. package/src/live-voice/__tests__/live-voice-archive.test.ts +16 -16
  393. package/src/live-voice/__tests__/live-voice-attach-image.test.ts +209 -0
  394. package/src/live-voice/__tests__/live-voice-connection.test.ts +20 -0
  395. package/src/live-voice/__tests__/live-voice-integration.test.ts +7 -5
  396. package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +265 -0
  397. package/src/live-voice/__tests__/live-voice-stt.test.ts +6 -1
  398. package/src/live-voice/__tests__/live-voice-vad.test.ts +35 -15
  399. package/src/live-voice/__tests__/protocol.test.ts +52 -0
  400. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +3 -0
  401. package/src/live-voice/live-voice-archive.ts +13 -13
  402. package/src/live-voice/live-voice-connection.ts +7 -1
  403. package/src/live-voice/live-voice-photo.ts +169 -0
  404. package/src/live-voice/live-voice-session.ts +108 -4
  405. package/src/live-voice/protocol.ts +89 -1
  406. package/src/mcp/client.ts +10 -0
  407. package/src/messaging/providers/__tests__/callback-routing.test.ts +5 -1
  408. package/src/messaging/providers/__tests__/transport-dispatch.test.ts +66 -4
  409. package/src/messaging/providers/callback-routing.ts +1 -0
  410. package/src/messaging/providers/discord/api.ts +312 -0
  411. package/src/messaging/providers/discord/dm-delivery.test.ts +204 -0
  412. package/src/messaging/providers/discord/render.test.ts +204 -0
  413. package/src/messaging/providers/discord/render.ts +195 -0
  414. package/src/messaging/providers/discord/send.test.ts +179 -0
  415. package/src/messaging/providers/discord/send.ts +212 -0
  416. package/src/messaging/providers/discord/transport.ts +81 -0
  417. package/src/messaging/providers/index.ts +3 -1
  418. package/src/messaging/providers/retry-policy.test.ts +247 -0
  419. package/src/messaging/providers/retry-policy.ts +206 -0
  420. package/src/messaging/providers/slack/mention-source.test.ts +285 -0
  421. package/src/messaging/providers/slack/mention-source.ts +287 -0
  422. package/src/messaging/providers/telegram-bot/api.ts +47 -130
  423. package/src/messaging/providers/whatsapp/api.ts +47 -136
  424. package/src/monitoring/__tests__/db-integrity-sample.test.ts +4 -1
  425. package/src/monitoring/__tests__/plugin-auto-update.test.ts +332 -0
  426. package/src/monitoring/db-integrity-sample.ts +6 -1
  427. package/src/monitoring/plugin-auto-update.ts +359 -0
  428. package/src/monitoring/resource-sampler.ts +21 -5
  429. package/src/monitoring/worker.ts +13 -0
  430. package/src/notifications/__tests__/assistant-reply-producer.test.ts +375 -2
  431. package/src/notifications/__tests__/broadcaster.test.ts +37 -1
  432. package/src/notifications/__tests__/copy-composer.test.ts +165 -0
  433. package/src/notifications/__tests__/emit-signal-pipeline-failure.test.ts +205 -0
  434. package/src/notifications/__tests__/notification-utils.test.ts +249 -0
  435. package/src/notifications/adapters/platform.ts +31 -2
  436. package/src/notifications/assistant-reply-producer.ts +79 -32
  437. package/src/notifications/broadcaster.ts +30 -10
  438. package/src/notifications/copy-composer.ts +69 -0
  439. package/src/notifications/emit-signal.ts +82 -15
  440. package/src/notifications/events-store.ts +10 -2
  441. package/src/notifications/home-feed-side-effect.ts +6 -3
  442. package/src/notifications/notification-utils.ts +173 -5
  443. package/src/notifications/signal.ts +12 -0
  444. package/src/oauth/AGENTS.md +4 -2
  445. package/src/oauth/byo-connection.ts +6 -1
  446. package/src/oauth/credential-token-resolver.ts +3 -10
  447. package/src/oauth/manual-token-connection.ts +29 -64
  448. package/src/oauth/manual-token-providers.ts +62 -0
  449. package/src/oauth/provider-serializer.ts +13 -0
  450. package/src/oauth/seed-providers.ts +62 -11
  451. package/src/onboarding/onboarding-events-store.ts +91 -1
  452. package/src/permissions/question-prompter.test.ts +4 -0
  453. package/src/permissions/question-prompter.ts +21 -4
  454. package/src/persistence/__tests__/conversation-lineage.test.ts +248 -0
  455. package/src/persistence/attachments-store.ts +15 -15
  456. package/src/persistence/conversation-attention-store.ts +30 -10
  457. package/src/persistence/conversation-bootstrap.ts +17 -0
  458. package/src/persistence/conversation-crud.ts +363 -97
  459. package/src/persistence/conversation-group-migration.ts +1 -1
  460. package/src/persistence/conversation-key-store.ts +38 -10
  461. package/src/persistence/conversation-lineage.ts +226 -0
  462. package/src/persistence/conversation-plugin-facade.ts +28 -3
  463. package/src/persistence/conversation-queries.ts +207 -33
  464. package/src/persistence/conversation-types.ts +107 -1
  465. package/src/persistence/jobs-store.ts +32 -8
  466. package/src/persistence/llm-usage-store.ts +22 -0
  467. package/src/persistence/migrations/361-normalize-managed-connection-rows.test.ts +168 -0
  468. package/src/persistence/migrations/361-normalize-managed-connection-rows.ts +76 -0
  469. package/src/persistence/migrations/362-add-conversation-subagent-kind.test.ts +187 -0
  470. package/src/persistence/migrations/362-add-conversation-subagent-kind.ts +93 -0
  471. package/src/persistence/migrations/363-backfill-schedule-inference-profile.test.ts +229 -0
  472. package/src/persistence/migrations/363-backfill-schedule-inference-profile.ts +127 -0
  473. package/src/persistence/migrations/364-add-schedule-source-key.test.ts +120 -0
  474. package/src/persistence/migrations/364-add-schedule-source-key.ts +43 -0
  475. package/src/persistence/migrations/365-add-conversation-fork-strategy.ts +27 -0
  476. package/src/persistence/schema/conversations.ts +32 -0
  477. package/src/persistence/schema/infrastructure.ts +3 -0
  478. package/src/persistence/steps.ts +30 -0
  479. package/src/persistence/subagent-store.ts +148 -6
  480. package/src/platform/managed-speech.test.ts +20 -0
  481. package/src/platform/managed-speech.ts +9 -0
  482. package/src/plugin-api/index.ts +8 -0
  483. package/src/plugin-api/webhook-url.ts +151 -0
  484. package/src/plugins/__tests__/installed-plugin-dirs.test.ts +82 -0
  485. package/src/plugins/__tests__/source-fingerprint.test.ts +5 -1
  486. package/src/plugins/collect-source-versions.ts +4 -27
  487. package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +5 -5
  488. package/src/plugins/defaults/exploration-drift/package.json +1 -1
  489. package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +4 -2
  490. package/src/plugins/defaults/image-recovery/recover.ts +67 -57
  491. package/src/plugins/defaults/index.ts +1 -1
  492. package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +64 -18
  493. package/src/plugins/defaults/memory/__tests__/jobs-worker-outcome-contract.test.ts +242 -0
  494. package/src/plugins/defaults/memory/__tests__/jobs-worker-retrospective-sweep.test.ts +24 -1
  495. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +48 -3
  496. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +511 -19
  497. package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +39 -3
  498. package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +493 -0
  499. package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +5 -4
  500. package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +5 -1
  501. package/src/plugins/defaults/memory/job-handlers.ts +87 -5
  502. package/src/plugins/defaults/memory/jobs-worker.ts +60 -6
  503. package/src/plugins/defaults/memory/memory-retrospective-constants.ts +11 -0
  504. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +29 -0
  505. package/src/plugins/defaults/memory/memory-retrospective-job.ts +342 -60
  506. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +23 -3
  507. package/src/plugins/defaults/memory/substrate/consolidation-lock.ts +9 -4
  508. package/src/plugins/defaults/memory/substrate/sweep-job.ts +7 -0
  509. package/src/plugins/defaults/memory/v1/graph/injection.ts +1 -1
  510. package/src/plugins/defaults/memory/v2/router.ts +8 -0
  511. package/src/plugins/defaults/turn-context/unified-turn-context.ts +1 -2
  512. package/src/plugins/external-plugin-loader.ts +17 -8
  513. package/src/plugins/installed-plugin-dirs.ts +101 -0
  514. package/src/plugins/mtime-cache.ts +34 -22
  515. package/src/plugins/types.ts +69 -8
  516. package/src/prompts/persona-resolver.ts +6 -2
  517. package/src/prompts/system-prompt.ts +72 -17
  518. package/src/providers/__tests__/inference.test.ts +40 -0
  519. package/src/providers/__tests__/preflight-resolved-config.test.ts +58 -1
  520. package/src/providers/__tests__/retry-callsite.test.ts +67 -0
  521. package/src/providers/__tests__/vellum-connection-routing.test.ts +42 -11
  522. package/src/providers/anthropic/client.ts +112 -95
  523. package/src/providers/connection-resolution.ts +35 -1
  524. package/src/providers/gemini/client.ts +5 -5
  525. package/src/providers/inference/adapter-factory.ts +16 -10
  526. package/src/providers/inference/auth.ts +16 -0
  527. package/src/providers/inference/connection-availability.ts +164 -12
  528. package/src/providers/inference/connections.ts +25 -1
  529. package/src/providers/media-resolve.ts +21 -11
  530. package/src/providers/model-catalog.ts +15 -0
  531. package/src/providers/openai/chat-completions-provider.ts +4 -4
  532. package/src/providers/openai/responses-provider.ts +12 -25
  533. package/src/providers/registry.ts +9 -4
  534. package/src/providers/retry.ts +25 -0
  535. package/src/providers/server-tool-pairing.ts +134 -0
  536. package/src/providers/speech-to-text/__tests__/resolve.test.ts +187 -6
  537. package/src/providers/speech-to-text/deepgram.ts +1 -0
  538. package/src/providers/speech-to-text/resolve.ts +80 -10
  539. package/src/providers/speech-to-text/vellum-managed.ts +7 -1
  540. package/src/providers/speech-to-text/vellum-speech-relay-connection.ts +3 -1
  541. package/src/providers/types.ts +26 -6
  542. package/src/providers/vellum-model-routing.ts +6 -7
  543. package/src/runtime/AGENTS.md +2 -0
  544. package/src/runtime/__tests__/agent-wake.test.ts +16 -16
  545. package/src/runtime/__tests__/background-job-runner.test.ts +0 -144
  546. package/src/runtime/__tests__/desktop-presence.test.ts +162 -0
  547. package/src/runtime/agent-wake.ts +63 -24
  548. package/src/runtime/assistant-event-hub.ts +87 -12
  549. package/src/runtime/auth/__tests__/guard-tests.test.ts +2 -0
  550. package/src/runtime/auth/__tests__/scopes.test.ts +20 -0
  551. package/src/runtime/auth/same-actor.ts +56 -8
  552. package/src/runtime/auth/scopes.ts +12 -0
  553. package/src/runtime/auth/types.ts +2 -0
  554. package/src/runtime/background-job-runner.ts +5 -27
  555. package/src/runtime/channel-readiness-service.ts +59 -3
  556. package/src/runtime/channel-readiness-types.ts +18 -0
  557. package/src/runtime/desktop-presence.ts +69 -0
  558. package/src/runtime/http-server.ts +12 -0
  559. package/src/runtime/local-actor-identity.ts +11 -9
  560. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
  561. package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
  562. package/src/runtime/routes/__tests__/client-routes.test.ts +186 -14
  563. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +547 -1
  564. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +162 -7
  565. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +14 -10
  566. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +340 -3
  567. package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +28 -1
  568. package/src/runtime/routes/__tests__/plugins-routes.test.ts +173 -1
  569. package/src/runtime/routes/__tests__/question-routes.test.ts +64 -0
  570. package/src/runtime/routes/__tests__/retrospective-routes.test.ts +20 -0
  571. package/src/runtime/routes/__tests__/skill-history-route.test.ts +116 -0
  572. package/src/runtime/routes/__tests__/wake-conversation-routes.test.ts +60 -2
  573. package/src/runtime/routes/archive-utils.ts +16 -12
  574. package/src/runtime/routes/attachment-routes.ts +13 -6
  575. package/src/runtime/routes/canned-reply-release.ts +54 -0
  576. package/src/runtime/routes/channel-verification-routes.ts +2 -27
  577. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +5 -0
  578. package/src/runtime/routes/client-routes.ts +114 -2
  579. package/src/runtime/routes/conversation-cli-routes.ts +10 -12
  580. package/src/runtime/routes/conversation-list-routes.ts +59 -10
  581. package/src/runtime/routes/conversation-management-routes.ts +47 -18
  582. package/src/runtime/routes/conversation-query-routes.ts +32 -53
  583. package/src/runtime/routes/conversation-routes.ts +360 -293
  584. package/src/runtime/routes/credential-prompt-routes.ts +23 -2
  585. package/src/runtime/routes/debug-routes.ts +4 -1
  586. package/src/runtime/routes/default-provider-routes.ts +7 -11
  587. package/src/runtime/routes/documents-routes.ts +44 -1
  588. package/src/runtime/routes/events-routes.ts +28 -1
  589. package/src/runtime/routes/host-app-control-routes.ts +2 -0
  590. package/src/runtime/routes/host-bash-routes.ts +2 -0
  591. package/src/runtime/routes/host-browser-routes.ts +2 -0
  592. package/src/runtime/routes/host-cu-routes.ts +2 -0
  593. package/src/runtime/routes/host-file-routes.ts +2 -0
  594. package/src/runtime/routes/host-transfer-routes.ts +4 -0
  595. package/src/runtime/routes/identity-routes.ts +6 -6
  596. package/src/runtime/routes/inbound-message-handler.ts +5 -5
  597. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +7 -3
  598. package/src/runtime/routes/inbound-stages/background-dispatch.ts +14 -0
  599. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -2
  600. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +14 -11
  601. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -17
  602. package/src/runtime/routes/inference-profile-availability-guard.ts +171 -0
  603. package/src/runtime/routes/inference-profile-session-handler.ts +26 -2
  604. package/src/runtime/routes/inference-profile-session-routes.ts +2 -1
  605. package/src/runtime/routes/inference-profiles-routes.ts +152 -73
  606. package/src/runtime/routes/inference-provider-connection-routes.ts +0 -16
  607. package/src/runtime/routes/llm-call-sites-routes.ts +13 -0
  608. package/src/runtime/routes/log-export-routes.ts +1 -1
  609. package/src/runtime/routes/notification-routes.ts +0 -14
  610. package/src/runtime/routes/oauth-providers.ts +1 -0
  611. package/src/runtime/routes/plugins-routes.ts +106 -1
  612. package/src/runtime/routes/profiler-routes.ts +4 -2
  613. package/src/runtime/routes/question-routes.ts +28 -10
  614. package/src/runtime/routes/retrospective-routes.ts +9 -3
  615. package/src/runtime/routes/schedule-routes.ts +210 -12
  616. package/src/runtime/routes/secret-routes.ts +40 -23
  617. package/src/runtime/routes/settings-routes.ts +41 -13
  618. package/src/runtime/routes/skills-routes.ts +79 -0
  619. package/src/runtime/routes/ui-snapshot-routes.ts +1 -0
  620. package/src/runtime/routes/wake-conversation-routes.ts +23 -0
  621. package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
  622. package/src/runtime/services/conversation-serializer.ts +24 -9
  623. package/src/runtime/sync/documents-sidecar-publish.test.ts +101 -0
  624. package/src/runtime/sync/resource-sync-events.ts +79 -1
  625. package/src/runtime/sync/worker-daemon-notify.test.ts +34 -0
  626. package/src/runtime/sync/worker-daemon-notify.ts +44 -6
  627. package/src/runtime/verification-outbound-actions.ts +250 -222
  628. package/src/runtime/verification-templates.ts +41 -30
  629. package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +614 -0
  630. package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1066 -0
  631. package/src/schedule/inference-profile.ts +64 -3
  632. package/src/schedule/plugin-schedule-declarations.ts +518 -0
  633. package/src/schedule/plugin-schedule-reconciler.ts +605 -0
  634. package/src/schedule/plugin-schedules-gate.ts +23 -0
  635. package/src/schedule/schedule-store.ts +520 -26
  636. package/src/schedule/scheduler.ts +38 -0
  637. package/src/security/credential-backend.ts +51 -4
  638. package/src/security/encrypted-store.ts +64 -42
  639. package/src/security/secure-keys.ts +55 -7
  640. package/src/skills/catalog-install.ts +20 -7
  641. package/src/skills/inline-command-runner.ts +2 -7
  642. package/src/skills/skill-history.test.ts +269 -0
  643. package/src/skills/skill-history.ts +250 -0
  644. package/src/skills/skillssh-registry.ts +1 -1
  645. package/src/stt/daemon-batch-transcriber.ts +8 -6
  646. package/src/subagent/__tests__/consult-prompt.test.ts +25 -0
  647. package/src/subagent/consult-prompt.ts +3 -1
  648. package/src/subagent/consult-transcript.ts +1 -1
  649. package/src/subagent/manager.ts +304 -44
  650. package/src/subagent/progress-events.ts +35 -0
  651. package/src/subagent/role-resolution.ts +88 -0
  652. package/src/subagent/types.ts +302 -67
  653. package/src/subagent/validate-allowlists.ts +3 -4
  654. package/src/telegram/__tests__/webhook-health.test.ts +73 -1
  655. package/src/telegram/webhook-health.ts +101 -3
  656. package/src/telemetry/__tests__/telemetry-event-fixtures.ts +2 -0
  657. package/src/telemetry/activation-funnel.ts +6 -1
  658. package/src/telemetry/live-voice-funnel.ts +75 -0
  659. package/src/telemetry/telemetry-event-sources.ts +6 -0
  660. package/src/telemetry/telemetry-wire-source.json +1 -1
  661. package/src/telemetry/telemetry-wire.generated.ts +2 -0
  662. package/src/telemetry/types.ts +23 -4
  663. package/src/telemetry/usage-telemetry-reporter.test.ts +4 -0
  664. package/src/tools/__tests__/subagent-workflow-tool-input-schemas.test.ts +1 -1
  665. package/src/tools/ask-question/ask-question-tool.test.ts +118 -3
  666. package/src/tools/ask-question/ask-question-tool.ts +56 -2
  667. package/src/tools/browser/browser-execution.ts +1 -1
  668. package/src/tools/calls/call-start.ts +8 -5
  669. package/src/tools/conversation-groups/move_to_group.ts +10 -12
  670. package/src/tools/document/document-tool.ts +11 -4
  671. package/src/tools/execution-timeout.ts +9 -2
  672. package/src/tools/executor.ts +7 -6
  673. package/src/tools/filesystem/edit.ts +1 -1
  674. package/src/tools/filesystem/list.ts +1 -1
  675. package/src/tools/filesystem/read.ts +7 -13
  676. package/src/tools/filesystem/search.ts +23 -21
  677. package/src/tools/filesystem/write.ts +4 -1
  678. package/src/tools/host-filesystem/edit.ts +7 -2
  679. package/src/tools/host-filesystem/read.ts +21 -8
  680. package/src/tools/host-filesystem/write.ts +10 -2
  681. package/src/tools/host-terminal/host-shell.ts +2 -2
  682. package/src/tools/mcp/mcp-tool-factory.ts +25 -1
  683. package/src/tools/permission-checker.ts +8 -3
  684. package/src/tools/schedule/delete.ts +13 -1
  685. package/src/tools/schedule/list.ts +14 -3
  686. package/src/tools/schedule/update.ts +56 -33
  687. package/src/tools/shared/filesystem/audio-read.ts +6 -4
  688. package/src/tools/shared/filesystem/file-ops-service.ts +176 -116
  689. package/src/tools/shared/filesystem/image-read.ts +12 -12
  690. package/src/tools/shared/filesystem/path-policy.ts +70 -5
  691. package/src/tools/shared/filesystem/size-guard.ts +6 -6
  692. package/src/tools/shared/input-misuse.ts +72 -0
  693. package/src/tools/skills/scaffold-managed.ts +82 -0
  694. package/src/tools/skills/skill-tool-factory.ts +14 -1
  695. package/src/tools/subagent/consult-deadline.ts +12 -7
  696. package/src/tools/subagent/message.ts +3 -1
  697. package/src/tools/subagent/read.ts +66 -4
  698. package/src/tools/subagent/spawn.ts +500 -78
  699. package/src/tools/tool-approval-handler.ts +45 -4
  700. package/src/tools/types.ts +22 -3
  701. package/src/tools/ui-surface/surface-shape-docs.ts +1 -1
  702. package/src/tools/ui-surface/visual-validation.ts +2 -0
  703. package/src/usage/__tests__/subagent-attribution.test.ts +109 -0
  704. package/src/usage/subagent-attribution.ts +108 -0
  705. package/src/util/__tests__/sqlite-retry.test.ts +112 -0
  706. package/src/util/abort-reasons.ts +16 -0
  707. package/src/util/ansi.ts +44 -0
  708. package/src/util/disk-usage.ts +17 -13
  709. package/src/util/image-conversion.ts +20 -13
  710. package/src/util/schedule-source-key.ts +23 -0
  711. package/src/util/worker-process.ts +7 -1
  712. package/src/workspace/git-service.ts +33 -9
  713. package/src/workspace/migrations/140-repair-seed-pinned-memory-v3-live.ts +260 -0
  714. package/src/workspace/migrations/141-stt-english-default-to-multilingual.ts +111 -0
  715. package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +126 -0
  716. package/src/workspace/migrations/registry.ts +4 -0
  717. package/src/workspace/top-level-renderer.ts +15 -2
  718. package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -357
  719. package/src/daemon/install-symlink.ts +0 -207
  720. package/src/notifications/deferred-emit.ts +0 -147
@@ -7,7 +7,7 @@
7
7
  * double typed as `Conversation` (`makeWakeConversation`) that stubs only the
8
8
  * handful of members the wake touches — `getMessages`, `messages.push`,
9
9
  * `isProcessing`/`setProcessing`/`waitForIdle`, `currentTurnTrustContext`,
10
- * `setSubagentAllowedTools`, `drainQueue`, `maybeCompact`,
10
+ * `setSubagentAllowedTools`, `kickDrainQueue`, `maybeCompact`,
11
11
  * `contextWindowManager.estimateInputTokens`, and a scripted `agentLoop.run()`.
12
12
  *
13
13
  * The wake's side effects flow through the daemon boundary, so the
@@ -62,7 +62,7 @@ interface WakeConversationProbe {
62
62
  processingToggles: boolean[];
63
63
  /** Tail messages persisted via `addMessage`, in call order. */
64
64
  persistedTailCalls: Message[];
65
- /** Number of times `drainQueue` was invoked. */
65
+ /** Number of times `kickDrainQueue` was invoked. */
66
66
  drainQueueCalls: number;
67
67
  /**
68
68
  * Cross-hook call sequence tag. Each push/persist/drain (and the
@@ -71,7 +71,7 @@ interface WakeConversationProbe {
71
71
  */
72
72
  callSequence: string[];
73
73
  /**
74
- * Snapshot of the processing flag at the moment `drainQueue` was
74
+ * Snapshot of the processing flag at the moment `kickDrainQueue` was
75
75
  * invoked. Lets tests prove drain ran AFTER setProcessing(false),
76
76
  * rather than just inferring it from the order of recorded toggles.
77
77
  */
@@ -380,7 +380,7 @@ function makeWakeConversation(options: {
380
380
  scriptedTail?: Message[];
381
381
  scriptedEvents?: AgentEvent[];
382
382
  isProcessing?: boolean;
383
- /** When true, omit `drainQueue` so we can verify the wake handles its absence. */
383
+ /** When true, omit `kickDrainQueue` so we can verify the wake handles its absence. */
384
384
  omitDrainQueue?: boolean;
385
385
  initialAllowedTools?: Set<string>;
386
386
  /** Replaces the default scripted `agentLoop.run` body entirely. */
@@ -471,7 +471,7 @@ function makeWakeConversation(options: {
471
471
 
472
472
  const runBody = options.runImpl ?? defaultRun;
473
473
 
474
- const drainQueue = options.omitDrainQueue
474
+ const kickDrainQueue = options.omitDrainQueue
475
475
  ? undefined
476
476
  : async () => {
477
477
  probe.drainQueueCalls += 1;
@@ -597,7 +597,7 @@ function makeWakeConversation(options: {
597
597
  },
598
598
  buildCurrentSystemPrompt: () => "mock-system-prompt",
599
599
  modelOverride: undefined,
600
- ...(drainQueue ? { drainQueue } : {}),
600
+ ...(kickDrainQueue ? { kickDrainQueue } : {}),
601
601
  };
602
602
 
603
603
  return conversation as unknown as WakeConversation;
@@ -2297,11 +2297,11 @@ describe("wakeAgentForOpportunity", () => {
2297
2297
  expect(conversation.persistedTailCalls).toHaveLength(0);
2298
2298
  });
2299
2299
 
2300
- test("drainQueue is called in finally after a successful run", async () => {
2300
+ test("kickDrainQueue is called in finally after a successful run", async () => {
2301
2301
  // Verifies Gap 1 fix: messages queued during a wake (because the
2302
2302
  // wake set `processing = true`) must be picked up after the wake
2303
2303
  // completes. Mirrors the canonical user-turn `finally` path which
2304
- // sets `processing = false` then calls `drainQueue`.
2304
+ // sets `processing = false` then calls `kickDrainQueue`.
2305
2305
  const conversation = makeWakeConversation({
2306
2306
  baseline: [{ role: "user", content: [{ type: "text", text: "hi" }] }],
2307
2307
  scriptedAssistant: {
@@ -2333,7 +2333,7 @@ describe("wakeAgentForOpportunity", () => {
2333
2333
  expect(conversation.isProcessing()).toBe(false);
2334
2334
  });
2335
2335
 
2336
- test("drainQueue is called in finally even when the agent loop throws", async () => {
2336
+ test("kickDrainQueue is called in finally even when the agent loop throws", async () => {
2337
2337
  // Verifies the drain is in the finally block, not just on success.
2338
2338
  // A wake that crashes mid-run must still flush queued messages —
2339
2339
  // otherwise a transient LLM error strands every concurrent send.
@@ -2361,7 +2361,7 @@ describe("wakeAgentForOpportunity", () => {
2361
2361
  expect(conversation.processingToggles).toEqual([true, false]);
2362
2362
  });
2363
2363
 
2364
- test("missing drainQueue hook is tolerated (no-op fallback)", async () => {
2364
+ test("missing kickDrainQueue hook is tolerated (no-op fallback)", async () => {
2365
2365
  // The hook is intentionally optional so test stubs without a queue
2366
2366
  // can omit it. Production daemon always wires it.
2367
2367
  const conversation = makeWakeConversation({
@@ -2387,7 +2387,7 @@ describe("wakeAgentForOpportunity", () => {
2387
2387
  expect(conversation.drainQueueCalls).toBe(0);
2388
2388
  });
2389
2389
 
2390
- test("drainQueue rejection does not propagate from the wake", async () => {
2390
+ test("kickDrainQueue rejection does not propagate from the wake", async () => {
2391
2391
  // Defense in depth: if the queue drain throws (e.g. a poisoned
2392
2392
  // message), the wake itself must still resolve normally — the
2393
2393
  // drain failure is logged but never surfaced.
@@ -2398,7 +2398,7 @@ describe("wakeAgentForOpportunity", () => {
2398
2398
  content: [{ type: "text", text: "reply" }],
2399
2399
  },
2400
2400
  });
2401
- conversation.drainQueue = async () => {
2401
+ conversation.kickDrainQueue = async () => {
2402
2402
  throw new Error("drain blew up");
2403
2403
  };
2404
2404
 
@@ -2461,7 +2461,7 @@ describe("wakeAgentForOpportunity", () => {
2461
2461
  });
2462
2462
 
2463
2463
  test(
2464
- "tail messages are pushed and persisted BEFORE drainQueue runs " +
2464
+ "tail messages are pushed and persisted BEFORE the queue drain runs " +
2465
2465
  "(so dequeued turns see updated history)",
2466
2466
  async () => {
2467
2467
  // Locks in the round-3 fix: a user message queued during the wake
@@ -2474,7 +2474,7 @@ describe("wakeAgentForOpportunity", () => {
2474
2474
  //
2475
2475
  // Mirrors the canonical user-turn pattern in
2476
2476
  // conversation-agent-loop.ts: messages updated →
2477
- // processing=false → drainQueue.
2477
+ // processing=false → kickDrainQueue.
2478
2478
  const firstAssistant: Message = {
2479
2479
  role: "assistant",
2480
2480
  content: [
@@ -2536,13 +2536,13 @@ describe("wakeAgentForOpportunity", () => {
2536
2536
  );
2537
2537
 
2538
2538
  test(
2539
- "silent no-op: drainQueue still runs (in finally) but nothing is " +
2539
+ "silent no-op: the queue drain still runs (in finally) but nothing is " +
2540
2540
  "pushed, persisted, or emitted",
2541
2541
  async () => {
2542
2542
  // The wake's silent-no-op semantics must be preserved by the
2543
2543
  // round-3 reordering: an empty assistant reply produces no
2544
2544
  // visible text and no tool calls, so no push/persist/emit should
2545
- // happen. drainQueue must still run in the finally block so a
2545
+ // happen. The queue drain must still run in the finally block so a
2546
2546
  // racy queued message is not stranded.
2547
2547
  const conversation = makeWakeConversation({
2548
2548
  baseline: [{ role: "user", content: [{ type: "text", text: "hi" }] }],
@@ -96,8 +96,6 @@ mock.module("../pre-first-message-gate.js", () => ({
96
96
 
97
97
  // Import after mocks are in place.
98
98
  const { runBackgroundJob } = await import("../background-job-runner.js");
99
- const { bufferIfDeferred, resetDeferredForTest } =
100
- await import("../../notifications/deferred-emit.js");
101
99
 
102
100
  // ── Shared fixtures ──────────────────────────────────────────────────
103
101
 
@@ -125,7 +123,6 @@ beforeEach(() => {
125
123
  processMessageCalls.length = 0;
126
124
  emitCalls.length = 0;
127
125
  addMessageCalls.length = 0;
128
- resetDeferredForTest();
129
126
  preFirstMessageGateOpen = true;
130
127
  processMessageImpl = async () => ({ messageId: "msg-1" });
131
128
  emitImpl = async () => ({
@@ -290,22 +287,6 @@ describe("runBackgroundJob", () => {
290
287
  ).toBe("model_provider");
291
288
  });
292
289
 
293
- test("non-throwing turn failure with deferNotifications drops buffered notifications", async () => {
294
- processMessageImpl = async () => ({
295
- messageId: "msg-failed-turn",
296
- turnFailure: { failureCode: "provider_error" },
297
- });
298
-
299
- const result = await runBackgroundJob(
300
- baseOpts({ deferNotifications: true }),
301
- );
302
-
303
- expect(result.ok).toBe(false);
304
- // Only the runner's failure signal emits — no committed "success" signal.
305
- expect(emitCalls).toHaveLength(1);
306
- expect(emitCalls[0].sourceEventName).toBe("activity.failed");
307
- });
308
-
309
290
  test("timeout: returns ok=false with errorKind=timeout and emits activity.failed", async () => {
310
291
  // Never resolve — force timeout to win the race.
311
292
  processMessageImpl = () => new Promise(() => {});
@@ -464,129 +445,4 @@ describe("runBackgroundJob", () => {
464
445
  expect(processMessageCalls).toHaveLength(1);
465
446
  });
466
447
  });
467
-
468
- describe("deferNotifications", () => {
469
- function buildSkillNotificationParams(message: string) {
470
- return {
471
- sourceEventName: "assistant.share",
472
- sourceChannel: "assistant_tool" as const,
473
- sourceContextId: "skill-ctx",
474
- contextPayload: { requestedMessage: message },
475
- attentionHints: {
476
- requiresAction: false,
477
- urgency: "low" as const,
478
- isAsyncBackground: false,
479
- visibleInSourceNow: false,
480
- },
481
- };
482
- }
483
-
484
- test("success path commits buffered in-band notifications", async () => {
485
- processMessageImpl = async () => {
486
- // Stand in for the IPC route's bufferIfDeferred call when the model
487
- // invokes `notifications send` mid-turn.
488
- const buffered = bufferIfDeferred(
489
- STUB_CONVERSATION_ID,
490
- buildSkillNotificationParams("all green"),
491
- );
492
- expect(buffered).not.toBeNull();
493
- expect(buffered!.dispatched).toBe(false);
494
- return { messageId: "msg-success" };
495
- };
496
-
497
- const result = await runBackgroundJob(
498
- baseOpts({ deferNotifications: true }),
499
- );
500
-
501
- expect(result.ok).toBe(true);
502
- // Commit flushed the buffered notification through emitNotificationSignal.
503
- const successEmits = emitCalls.filter(
504
- (e) => e.sourceEventName !== "activity.failed",
505
- );
506
- expect(successEmits).toHaveLength(1);
507
- expect(successEmits[0].sourceEventName).toBe("assistant.share");
508
- });
509
-
510
- // Regression for the PR #31216 Codex P1 finding: a heartbeat that calls
511
- // the notifications skill and then times out must not leave a "success"
512
- // notification standing alongside the runner's `activity.failed` emit.
513
- test("timeout drops buffered in-band notifications; only activity.failed emits", async () => {
514
- processMessageImpl = () => {
515
- bufferIfDeferred(
516
- STUB_CONVERSATION_ID,
517
- buildSkillNotificationParams("premature success"),
518
- );
519
- return new Promise(() => {});
520
- };
521
-
522
- const result = await runBackgroundJob(
523
- baseOpts({ deferNotifications: true, timeoutMs: 30 }),
524
- );
525
-
526
- expect(result.ok).toBe(false);
527
- expect(result.errorKind).toBe("timeout");
528
- // Only the runner's failure signal makes it out — the buffered
529
- // "success" notification is discarded.
530
- expect(emitCalls).toHaveLength(1);
531
- expect(emitCalls[0].sourceEventName).toBe("activity.failed");
532
- });
533
-
534
- test("thrown exception also drops buffered notifications", async () => {
535
- processMessageImpl = async () => {
536
- bufferIfDeferred(
537
- STUB_CONVERSATION_ID,
538
- buildSkillNotificationParams("doomed"),
539
- );
540
- throw new Error("kaboom");
541
- };
542
-
543
- const result = await runBackgroundJob(
544
- baseOpts({ deferNotifications: true }),
545
- );
546
-
547
- expect(result.ok).toBe(false);
548
- expect(emitCalls).toHaveLength(1);
549
- expect(emitCalls[0].sourceEventName).toBe("activity.failed");
550
- });
551
-
552
- // Regression: after timeout, `processMessage` keeps running and may
553
- // emit a late skill call. The tombstone must swallow it instead of
554
- // letting it bypass the buffer and reach the dispatch pipeline.
555
- test("late skill call after timeout is swallowed by the tombstone", async () => {
556
- processMessageImpl = () => new Promise(() => {});
557
-
558
- const result = await runBackgroundJob(
559
- baseOpts({ deferNotifications: true, timeoutMs: 20 }),
560
- );
561
- expect(result.ok).toBe(false);
562
-
563
- const late = bufferIfDeferred(
564
- STUB_CONVERSATION_ID,
565
- buildSkillNotificationParams("post-timeout"),
566
- );
567
- expect(late).not.toBeNull();
568
- expect(late!.dispatched).toBe(false);
569
- expect(late!.reason).toMatch(/did not complete/);
570
-
571
- // Only the runner's failure signal made it out.
572
- expect(emitCalls).toHaveLength(1);
573
- expect(emitCalls[0].sourceEventName).toBe("activity.failed");
574
- });
575
-
576
- test("without deferNotifications, bufferIfDeferred is a no-op", async () => {
577
- processMessageImpl = async () => {
578
- const buffered = bufferIfDeferred(
579
- STUB_CONVERSATION_ID,
580
- buildSkillNotificationParams("immediate"),
581
- );
582
- // Buffer was never armed, so the call returns null and the IPC
583
- // handler would emit directly.
584
- expect(buffered).toBeNull();
585
- return { messageId: "msg-success" };
586
- };
587
-
588
- const result = await runBackgroundJob(baseOpts());
589
- expect(result.ok).toBe(true);
590
- });
591
- });
592
448
  });
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Tests for the desktop presence policy.
3
+ *
4
+ * The invariant under test is fail-open: only a fresh `active` report from a
5
+ * `macos` client answers `true`. Every other shape (stale, idle, away, no
6
+ * presence, wrong interface, no clients, hub throw) answers `false` so the
7
+ * push is sent. Scoping the read to an actor principal narrows what counts as
8
+ * attendance, so it can only ever turn a `true` into a `false`.
9
+ */
10
+
11
+ import { afterEach, describe, expect, spyOn, test } from "bun:test";
12
+
13
+ import {
14
+ clearHubClients,
15
+ registerHubClient,
16
+ type RegisterHubClientArgs,
17
+ } from "../../__tests__/helpers/hub-clients.js";
18
+ import { assistantEventHub } from "../assistant-event-hub.js";
19
+ import {
20
+ isDesktopAttended,
21
+ PRESENCE_STALE_AFTER_MS,
22
+ } from "../desktop-presence.js";
23
+
24
+ // ── Test helpers ──────────────────────────────────────────────────────────
25
+
26
+ /** A `now` far enough past every report to push all of them out of the window. */
27
+ function afterStaleness(): Date {
28
+ return new Date(Date.now() + PRESENCE_STALE_AFTER_MS + 1_000);
29
+ }
30
+
31
+ /** The policy reads the process singleton, so every client registers there. */
32
+ function registerClient(args: Omit<RegisterHubClientArgs, "hub">): void {
33
+ registerHubClient({ ...args, hub: assistantEventHub });
34
+ }
35
+
36
+ afterEach(() => {
37
+ clearHubClients(assistantEventHub);
38
+ });
39
+
40
+ // ── Tests ─────────────────────────────────────────────────────────────────
41
+
42
+ describe("isDesktopAttended", () => {
43
+ test("fresh active macos client is attended", () => {
44
+ registerClient({ clientId: "mac-1", presence: "active" });
45
+ expect(isDesktopAttended()).toBe(true);
46
+ });
47
+
48
+ test("active report older than the staleness bound is not attended", () => {
49
+ registerClient({ clientId: "mac-1", presence: "active" });
50
+ expect(isDesktopAttended({ now: afterStaleness() })).toBe(false);
51
+ });
52
+
53
+ test("idle is not attended", () => {
54
+ registerClient({ clientId: "mac-1", presence: "idle" });
55
+ expect(isDesktopAttended()).toBe(false);
56
+ });
57
+
58
+ test("away is not attended", () => {
59
+ registerClient({ clientId: "mac-1", presence: "away" });
60
+ expect(isDesktopAttended()).toBe(false);
61
+ });
62
+
63
+ test("client that never reported presence is not attended", () => {
64
+ registerClient({ clientId: "mac-1" });
65
+ expect(isDesktopAttended()).toBe(false);
66
+ });
67
+
68
+ test("active web client does not count as desktop presence", () => {
69
+ registerClient({
70
+ clientId: "web-1",
71
+ interfaceId: "web",
72
+ presence: "active",
73
+ });
74
+ expect(isDesktopAttended()).toBe(false);
75
+ });
76
+
77
+ test("no connected clients is not attended", () => {
78
+ expect(isDesktopAttended()).toBe(false);
79
+ });
80
+
81
+ test("a hub read that throws is not attended", () => {
82
+ const spy = spyOn(
83
+ assistantEventHub,
84
+ "listClientsByInterface",
85
+ ).mockImplementation(() => {
86
+ throw new Error("hub exploded");
87
+ });
88
+ try {
89
+ expect(isDesktopAttended()).toBe(false);
90
+ } finally {
91
+ spy.mockRestore();
92
+ }
93
+ });
94
+
95
+ test("one active macos client among several is enough", () => {
96
+ registerClient({ clientId: "mac-1", presence: "away" });
97
+ registerClient({ clientId: "mac-2", presence: "active" });
98
+ expect(isDesktopAttended()).toBe(true);
99
+ });
100
+ });
101
+
102
+ describe("isDesktopAttended scoped to an actor principal", () => {
103
+ test("matching actor principal is attended", () => {
104
+ registerClient({
105
+ clientId: "mac-1",
106
+ presence: "active",
107
+ actorPrincipalId: "actor-a",
108
+ });
109
+ expect(isDesktopAttended({ actorPrincipalId: "actor-a" })).toBe(true);
110
+ });
111
+
112
+ test("another actor's attended mac does not count", () => {
113
+ registerClient({
114
+ clientId: "mac-1",
115
+ presence: "active",
116
+ actorPrincipalId: "actor-a",
117
+ });
118
+ expect(isDesktopAttended({ actorPrincipalId: "actor-b" })).toBe(false);
119
+ });
120
+
121
+ test("client with no principal does not match a supplied principal", () => {
122
+ registerClient({ clientId: "mac-1", presence: "active" });
123
+ expect(isDesktopAttended({ actorPrincipalId: "actor-a" })).toBe(false);
124
+ });
125
+
126
+ test("omitting the principal counts any attended macos client", () => {
127
+ registerClient({
128
+ clientId: "mac-1",
129
+ presence: "active",
130
+ actorPrincipalId: "actor-a",
131
+ });
132
+ expect(isDesktopAttended()).toBe(true);
133
+ });
134
+
135
+ test("target actor's stale report is not attended", () => {
136
+ registerClient({
137
+ clientId: "mac-1",
138
+ presence: "active",
139
+ actorPrincipalId: "actor-a",
140
+ });
141
+ expect(
142
+ isDesktopAttended({
143
+ actorPrincipalId: "actor-a",
144
+ now: afterStaleness(),
145
+ }),
146
+ ).toBe(false);
147
+ });
148
+
149
+ test("only the non-target actor is active among several clients", () => {
150
+ registerClient({
151
+ clientId: "mac-1",
152
+ presence: "active",
153
+ actorPrincipalId: "actor-a",
154
+ });
155
+ registerClient({
156
+ clientId: "mac-2",
157
+ presence: "away",
158
+ actorPrincipalId: "actor-b",
159
+ });
160
+ expect(isDesktopAttended({ actorPrincipalId: "actor-b" })).toBe(false);
161
+ });
162
+ });
@@ -86,6 +86,7 @@ import { getConfig } from "../config/loader.js";
86
86
  import type { LLMCallSite } from "../config/schemas/llm.js";
87
87
  import { conversationSupportsDynamicUi } from "../daemon/channel-ui-capability.js";
88
88
  import type { Conversation } from "../daemon/conversation.js";
89
+ import type { QueueDrainReason } from "../daemon/conversation-queue-manager.js";
89
90
  import { recordUsage } from "../daemon/conversation-usage.js";
90
91
  import { getDiskPressureStatus } from "../daemon/disk-pressure-guard.js";
91
92
  import {
@@ -534,6 +535,47 @@ async function defaultResolveTarget(
534
535
  }
535
536
  }
536
537
 
538
+ // ── Queue drain kick ──────────────────────────────────────────────────
539
+
540
+ /**
541
+ * The queue-drain surface the wake kicks on its way out.
542
+ *
543
+ * The hook is optional because the wake runs against whatever
544
+ * {@link WakeDeps.resolveTarget} hands it, which in tests is a partial
545
+ * conversation double. Production `Conversation` always provides it.
546
+ */
547
+ interface WakeDrainTarget {
548
+ kickDrainQueue?: (
549
+ reason?: QueueDrainReason,
550
+ origin?: string,
551
+ ) => Promise<void>;
552
+ }
553
+
554
+ /**
555
+ * Drain the target's message queue once the wake has released the
556
+ * conversation.
557
+ *
558
+ * `Conversation.kickDrainQueue` retries a failed drain once and notifies the
559
+ * queued senders when the retry also fails, so a stalled wake-tail drain is
560
+ * visible instead of silently stranding the queue, and it never rejects. An
561
+ * injected target can still omit the hook or reject from it, and neither may
562
+ * fail the wake, whose own work is already complete by this point.
563
+ */
564
+ async function kickWakeDrainQueue(
565
+ conversation: WakeDrainTarget,
566
+ origin: string,
567
+ logContext: { conversationId: string; source: string },
568
+ ): Promise<void> {
569
+ try {
570
+ await conversation.kickDrainQueue?.("loop_complete", origin);
571
+ } catch (err) {
572
+ log.warn(
573
+ { ...logContext, err },
574
+ "agent-wake: kickDrainQueue threw; continuing",
575
+ );
576
+ }
577
+ }
578
+
537
579
  // ── Per-conversation single-flight lock ───────────────────────────────
538
580
  //
539
581
  // When a wake arrives and another run is in flight for the same
@@ -813,7 +855,7 @@ export async function wakeAgentForOpportunity(
813
855
  // Apply the caller's persona override for the duration of the run. The
814
856
  // prompt is built once before `agentLoop.run()` (via
815
857
  // `conversation.buildCurrentSystemPrompt()`), which reads this field;
816
- // cleared (below, before drainQueue) so a queued user turn never builds
858
+ // cleared (below, before the queue drain) so a queued user turn never builds
817
859
  // its prompt under the wake's override. Assigned only AFTER the
818
860
  // profile/config reads above — those can throw, and they run before the
819
861
  // try/finally that clears the override, so an earlier assignment would
@@ -1411,10 +1453,14 @@ export async function wakeAgentForOpportunity(
1411
1453
  // user turn or a later background read never inherits the wake's stamps.
1412
1454
  const priorCallSite = conversation.currentCallSite;
1413
1455
  const priorTurnOverrideProfile = conversation.currentTurnOverrideProfile;
1456
+ const priorTurnCronRunId = conversation.currentTurnCronRunId;
1414
1457
  const priorHasNoClient = conversation.hasNoClient;
1415
1458
  const priorTurnTrust = conversation.currentTurnTrustContext;
1416
1459
  conversation.currentCallSite = callSite;
1417
1460
  conversation.currentTurnOverrideProfile = overrideProfile;
1461
+ // Same reason as the stamps above: a wake triggered by a schedule firing
1462
+ // delegates work to subagents whose usage must attribute to that firing.
1463
+ conversation.currentTurnCronRunId = opts.cronRunId ?? null;
1418
1464
  if (opts.clientless) {
1419
1465
  conversation.hasNoClient = true;
1420
1466
  }
@@ -1503,6 +1549,7 @@ export async function wakeAgentForOpportunity(
1503
1549
  // at the start of the next normal turn regardless.)
1504
1550
  conversation.currentCallSite = priorCallSite;
1505
1551
  conversation.currentTurnOverrideProfile = priorTurnOverrideProfile;
1552
+ conversation.currentTurnCronRunId = priorTurnCronRunId;
1506
1553
  conversation.hasNoClient = priorHasNoClient;
1507
1554
  conversation.currentTurnTrustContext = priorTurnTrust;
1508
1555
  }
@@ -1519,7 +1566,7 @@ export async function wakeAgentForOpportunity(
1519
1566
  // Run completed cleanly. The canonical user-turn pattern
1520
1567
  // (conversation-agent-loop.ts:1860, 2106-2126) updates
1521
1568
  // `ctx.messages` first, then clears the flag via `ctx.setProcessing(false)`, then
1522
- // calls `ctx.drainQueue(...)`. We mirror that order so a message
1569
+ // calls `ctx.kickDrainQueue(...)`. We mirror that order so a message
1523
1570
  // queued during the wake dequeues against an already-updated
1524
1571
  // history — otherwise `drainSingleMessage` reads `ctx.messages`
1525
1572
  // mid-tail and writes a DB row that lands out of chronological
@@ -1590,7 +1637,7 @@ export async function wakeAgentForOpportunity(
1590
1637
  // Silent no-op: drop buffered events, push nothing, persist
1591
1638
  // nothing, emit nothing. (No checkpoint fired during the run
1592
1639
  // since checkpoints only fire after tool turns and there were
1593
- // none.) The finally still runs drainQueue so a racy queued
1640
+ // none.) The finally still kicks the queue drain so a racy queued
1594
1641
  // message isn't stranded.
1595
1642
  return { invoked: true, producedToolCalls: false };
1596
1643
  }
@@ -1621,25 +1668,21 @@ export async function wakeAgentForOpportunity(
1621
1668
  "agent-wake: setProcessing(false) threw; continuing",
1622
1669
  );
1623
1670
  }
1624
- try {
1625
- await conversation.drainQueue();
1626
- } catch (err) {
1627
- log.warn(
1628
- { conversationId, source, err },
1629
- "agent-wake: drainQueue threw; continuing",
1630
- );
1631
- }
1671
+ await kickWakeDrainQueue(conversation, "agent_wake_tail", {
1672
+ conversationId,
1673
+ source,
1674
+ });
1632
1675
  drainedInTry = true;
1633
1676
 
1634
1677
  return { invoked: true, producedToolCalls };
1635
1678
  } finally {
1636
1679
  // Put the conversation's resting trust back on every exit path.
1637
1680
  restorePersistentWakeTrust();
1638
- // The success path (above) already called setProcessing(false)
1639
- // + drainQueue after tail persist. This catch-all handles the
1640
- // error and early-return paths where no tail was produced those
1641
- // exit the try body before reaching the drain block, so
1642
- // `drainedInTry` is still false.
1681
+ // The success path (above) already called setProcessing(false) and
1682
+ // kicked the queue drain after tail persist. This catch-all handles the
1683
+ // error and early-return paths where no tail was produced: those exit
1684
+ // the try body before reaching the drain block, so `drainedInTry` is
1685
+ // still false.
1643
1686
  if (!drainedInTry) {
1644
1687
  restoreWakeAllowedTools();
1645
1688
  clearWakePersonaOverride();
@@ -1651,14 +1694,10 @@ export async function wakeAgentForOpportunity(
1651
1694
  "agent-wake: setProcessing(false) threw; continuing",
1652
1695
  );
1653
1696
  }
1654
- try {
1655
- await conversation.drainQueue();
1656
- } catch (err) {
1657
- log.warn(
1658
- { conversationId, source, err },
1659
- "agent-wake: drainQueue threw; continuing",
1660
- );
1661
- }
1697
+ await kickWakeDrainQueue(conversation, "agent_wake_cleanup", {
1698
+ conversationId,
1699
+ source,
1700
+ });
1662
1701
  }
1663
1702
 
1664
1703
  const durationMs = nowFn() - startedAt;