@vellumai/assistant 0.11.2 → 0.11.3-staging.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (709) 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-queue.test.ts +265 -1
  72. package/src/__tests__/conversation-retry-route.test.ts +3 -1
  73. package/src/__tests__/conversation-store-ephemeral.test.ts +4 -0
  74. package/src/__tests__/conversation-store.test.ts +26 -10
  75. package/src/__tests__/conversation-summarize-route.test.ts +1 -1
  76. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +15 -6
  77. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -5
  78. package/src/__tests__/conversation-surfaces-app-control.test.ts +12 -11
  79. package/src/__tests__/conversation-surfaces-app-open.test.ts +11 -5
  80. package/src/__tests__/conversation-surfaces-data-persist.test.ts +5 -4
  81. package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +967 -0
  82. package/src/__tests__/conversation-surfaces-persisted-info.test.ts +312 -0
  83. package/src/__tests__/conversation-surfaces-queued-emit.test.ts +6 -5
  84. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -8
  85. package/src/__tests__/conversation-surfaces-standalone.test.ts +21 -11
  86. package/src/__tests__/conversation-surfaces-state-update.test.ts +7 -11
  87. package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -6
  88. package/src/__tests__/conversation-surfaces-task-progress.test.ts +11 -6
  89. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +9 -6
  90. package/src/__tests__/conversation-tool-setup-attribution.test.ts +70 -7
  91. package/src/__tests__/conversation-wait-for-idle.test.ts +6 -11
  92. package/src/__tests__/credential-prompt-route.test.ts +57 -7
  93. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  94. package/src/__tests__/cu-unified-flow.test.ts +89 -28
  95. package/src/__tests__/custom-group-listing-visibility.test.ts +1 -1
  96. package/src/__tests__/db-schedule-syntax-migration.test.ts +4 -1
  97. package/src/__tests__/discord-access-request-privacy.test.ts +366 -0
  98. package/src/__tests__/discord-requester-notice-privacy.test.ts +87 -0
  99. package/src/__tests__/disk-pressure-guard.test.ts +55 -55
  100. package/src/__tests__/disk-pressure-routes.test.ts +10 -10
  101. package/src/__tests__/disk-pressure-tools.test.ts +2 -2
  102. package/src/__tests__/disk-usage.test.ts +35 -20
  103. package/src/__tests__/document-sync-tags.test.ts +379 -0
  104. package/src/__tests__/document-update-default-surface.test.ts +28 -0
  105. package/src/__tests__/dynamic-skill-background-guard.test.ts +2 -2
  106. package/src/__tests__/emit-signal-routing-intent.test.ts +3 -4
  107. package/src/__tests__/ensure-conversation-exists.test.ts +8 -5
  108. package/src/__tests__/events-dev-bypass-actor.test.ts +183 -1
  109. package/src/__tests__/evict-conversations-for-reload.test.ts +90 -0
  110. package/src/__tests__/file-list-tool.test.ts +12 -10
  111. package/src/__tests__/file-ops-service.test.ts +151 -44
  112. package/src/__tests__/file-read-container-boundary.test.ts +137 -0
  113. package/src/__tests__/file-read-tool.test.ts +21 -9
  114. package/src/__tests__/file-url-path-guard.test.ts +83 -0
  115. package/src/__tests__/filesystem-tools.test.ts +82 -66
  116. package/src/__tests__/guardian-outbound-http.test.ts +8 -8
  117. package/src/__tests__/helpers/gateway-verification-sessions-stub.ts +14 -1
  118. package/src/__tests__/helpers/hub-clients.ts +42 -0
  119. package/src/__tests__/helpers/mock-conversation.ts +46 -0
  120. package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +61 -57
  121. package/src/__tests__/history-repair.test.ts +112 -12
  122. package/src/__tests__/host-bash-proxy.test.ts +10 -5
  123. package/src/__tests__/host-bash-routes.test.ts +67 -0
  124. package/src/__tests__/host-browser-proxy.test.ts +2 -2
  125. package/src/__tests__/host-cu-proxy.test.ts +3 -7
  126. package/src/__tests__/host-file-proxy.test.ts +3 -7
  127. package/src/__tests__/host-file-read-tool.test.ts +5 -1
  128. package/src/__tests__/host-transfer-proxy-targeted.test.ts +6 -4
  129. package/src/__tests__/http-conversation-lineage.test.ts +2 -5
  130. package/src/__tests__/http-user-message-parity.test.ts +44 -0
  131. package/src/__tests__/identity-routes.test.ts +13 -13
  132. package/src/__tests__/image-conversion.test.ts +29 -26
  133. package/src/__tests__/image-source-path-reinject.test.ts +2 -2
  134. package/src/__tests__/inference-profile-session-handler.test.ts +95 -0
  135. package/src/__tests__/inference-profile-session-ipc.test.ts +25 -0
  136. package/src/__tests__/introduction-card-resolver.test.ts +6 -1
  137. package/src/__tests__/list-messages-attachments.test.ts +56 -32
  138. package/src/__tests__/list-messages-background-tool-completion.test.ts +4 -4
  139. package/src/__tests__/list-messages-client-message-id.test.ts +2 -2
  140. package/src/__tests__/list-messages-hidden-metadata.test.ts +16 -16
  141. package/src/__tests__/list-messages-page-latest.test.ts +61 -61
  142. package/src/__tests__/list-messages-provider-error.test.ts +6 -6
  143. package/src/__tests__/list-messages-queued.test.ts +49 -6
  144. package/src/__tests__/list-messages-system-card.test.ts +4 -4
  145. package/src/__tests__/list-messages-tool-merge.test.ts +9 -9
  146. package/src/__tests__/llm-resolver-override-or-default.test.ts +141 -0
  147. package/src/__tests__/llm-usage-store.test.ts +55 -0
  148. package/src/__tests__/manual-token-reconciliation.test.ts +45 -0
  149. package/src/__tests__/mcp-tool-annotations-risk.test.ts +133 -0
  150. package/src/__tests__/media-stream-server-integration.test.ts +36 -0
  151. package/src/__tests__/migration-export-http.test.ts +1 -1
  152. package/src/__tests__/migration-import-commit-http.test.ts +1 -1
  153. package/src/__tests__/migration-import-preflight-http.test.ts +1 -1
  154. package/src/__tests__/migration-validate-http.test.ts +1 -1
  155. package/src/__tests__/mtime-cache.test.ts +65 -3
  156. package/src/__tests__/notification-platform-adapter.test.ts +104 -0
  157. package/src/__tests__/notification-schedule-notify-dedup.test.ts +1 -1
  158. package/src/__tests__/oauth-provider-profiles.test.ts +4 -2
  159. package/src/__tests__/oauth-provider-serializer.test.ts +17 -0
  160. package/src/__tests__/oauth-providers-routes.test.ts +1 -0
  161. package/src/__tests__/openai-responses-prompt-cache.test.ts +5 -2
  162. package/src/__tests__/path-policy.test.ts +164 -1
  163. package/src/__tests__/persist-media-references.test.ts +2 -2
  164. package/src/__tests__/persist-unsendable-image-downscale.test.ts +5 -5
  165. package/src/__tests__/persist-unsendable-image.test.ts +23 -23
  166. package/src/__tests__/persona-resolver.test.ts +154 -0
  167. package/src/__tests__/plugin-api-webhook-url.test.ts +170 -0
  168. package/src/__tests__/plugin-import-boundary-guard.test.ts +6 -0
  169. package/src/__tests__/processing-flag-persist-failure.test.ts +15 -10
  170. package/src/__tests__/profile-availability-incomplete.test.ts +113 -0
  171. package/src/__tests__/profiler-routes.test.ts +13 -13
  172. package/src/__tests__/prompt-cache-cross-turn-stability.test.ts +593 -0
  173. package/src/__tests__/proxy-approval-callback.test.ts +3 -3
  174. package/src/__tests__/queued-message-cancel-and-steer.test.ts +189 -0
  175. package/src/__tests__/queued-message-steer-actor-scoping.test.ts +164 -0
  176. package/src/__tests__/resolve-trust-class.test.ts +20 -0
  177. package/src/__tests__/run-conversation-turn-persistence.test.ts +1 -1
  178. package/src/__tests__/run-due-schedules.test.ts +174 -0
  179. package/src/__tests__/runtime-attachment-metadata.test.ts +15 -7
  180. package/src/__tests__/scaffold-managed-skill-tool.test.ts +166 -0
  181. package/src/__tests__/schedule-routes.test.ts +730 -8
  182. package/src/__tests__/schedule-store.test.ts +540 -0
  183. package/src/__tests__/schedule-tools.test.ts +103 -0
  184. package/src/__tests__/scheduler-wake.test.ts +5 -0
  185. package/src/__tests__/secret-routes-platform-proxy.test.ts +29 -1
  186. package/src/__tests__/secure-keys.test.ts +91 -0
  187. package/src/__tests__/server-history-render.test.ts +97 -7
  188. package/src/__tests__/size-guard.test.ts +8 -8
  189. package/src/__tests__/skill-docs-sync-guard.test.ts +3 -2
  190. package/src/__tests__/skill-tool-factory.test.ts +247 -0
  191. package/src/__tests__/skills-install-staging.test.ts +15 -6
  192. package/src/__tests__/slack-edit-ordering-characterization.test.ts +103 -0
  193. package/src/__tests__/slack-inbound-verification.test.ts +4 -1
  194. package/src/__tests__/slack-mention-binding-invariance.test.ts +103 -0
  195. package/src/__tests__/slack-mention-provider-leak-audit.test.ts +135 -0
  196. package/src/__tests__/starter-task-flow.test.ts +5 -4
  197. package/src/__tests__/steer-on-enqueue-question.test.ts +3 -2
  198. package/src/__tests__/stt-language-catalog-parity.test.ts +38 -0
  199. package/src/__tests__/subagent-allowlist-validation.test.ts +26 -4
  200. package/src/__tests__/subagent-cron-run-attribution.test.ts +301 -0
  201. package/src/__tests__/subagent-disposal.test.ts +10 -0
  202. package/src/__tests__/subagent-fork-notifications.test.ts +10 -0
  203. package/src/__tests__/subagent-fork-prompt-role.test.ts +85 -11
  204. package/src/__tests__/subagent-fork-spawn.test.ts +15 -7
  205. package/src/__tests__/subagent-manager-notify.test.ts +148 -0
  206. package/src/__tests__/subagent-notify-parent.test.ts +1 -1
  207. package/src/__tests__/subagent-persistence.test.ts +141 -0
  208. package/src/__tests__/subagent-role-registry.test.ts +172 -63
  209. package/src/__tests__/subagent-role-resolution.test.ts +88 -0
  210. package/src/__tests__/subagent-spawn-and-await.test.ts +48 -0
  211. package/src/__tests__/subagent-spawn-tool-fork.test.ts +111 -10
  212. package/src/__tests__/subagent-terminal-message.test.ts +88 -1
  213. package/src/__tests__/subagent-tool-gate-mode.test.ts +225 -10
  214. package/src/__tests__/subagent-tools.test.ts +1901 -108
  215. package/src/__tests__/surface-completion-compaction-boundary.test.ts +124 -0
  216. package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +298 -0
  217. package/src/__tests__/system-prompt.test.ts +82 -7
  218. package/src/__tests__/tool-result-spool.test.ts +74 -18
  219. package/src/__tests__/tool-side-effects-documents.test.ts +174 -0
  220. package/src/__tests__/tool-side-effects-hook-failures.test.ts +129 -0
  221. package/src/__tests__/tools-audio-read.test.ts +10 -10
  222. package/src/__tests__/tools-get-route.test.ts +85 -12
  223. package/src/__tests__/tools-list-cli.test.ts +171 -0
  224. package/src/__tests__/top-level-renderer.test.ts +54 -1
  225. package/src/__tests__/trusted-contact-multichannel.test.ts +4 -1
  226. package/src/__tests__/turn-tail-chain.test.ts +135 -0
  227. package/src/__tests__/ui-choice-copy-surfaces.test.ts +6 -4
  228. package/src/__tests__/ui-visual-surface.test.ts +5 -4
  229. package/src/__tests__/ui-work-result-surface.test.ts +5 -4
  230. package/src/__tests__/unified-turn-context-visible-app.test.ts +7 -8
  231. package/src/__tests__/usage-routes.test.ts +62 -0
  232. package/src/__tests__/verification-outbound-delivery.test.ts +176 -0
  233. package/src/__tests__/visible-app-context.test.ts +20 -4
  234. package/src/__tests__/web-search-history.test.ts +96 -0
  235. package/src/__tests__/workspace-migration-139-clear-renamed-cost-profile-label.test.ts +6 -6
  236. package/src/__tests__/workspace-migration-140-repair-seed-pinned-memory-v3-live.test.ts +330 -0
  237. package/src/acp/failure-error.ts +3 -5
  238. package/src/agent/attachments.ts +26 -24
  239. package/src/agent/history-repair/history-repair.ts +27 -26
  240. package/src/agent/image-optimize.ts +6 -6
  241. package/src/agent/loop.ts +40 -22
  242. package/src/agent/message-types.ts +3 -3
  243. package/src/api/constants/document-tools.ts +55 -0
  244. package/src/api/events/question-answered.ts +53 -0
  245. package/src/api/events/tool-result.ts +7 -0
  246. package/src/api/index.ts +13 -0
  247. package/src/api/responses/conversation-message.ts +11 -0
  248. package/src/api/surfaces-oauth-connect.test.ts +96 -0
  249. package/src/api/surfaces.ts +36 -8
  250. package/src/approvals/guardian-channel-delivery.ts +83 -0
  251. package/src/approvals/guardian-expiry-notifier.ts +13 -11
  252. package/src/approvals/guardian-request-resolvers.ts +216 -132
  253. package/src/calls/__tests__/voice-session-bridge.test.ts +50 -0
  254. package/src/calls/media-stream-server.ts +9 -1
  255. package/src/calls/voice-session-bridge.ts +38 -0
  256. package/src/channels/__tests__/gateway-verification-sessions.test.ts +3 -3
  257. package/src/channels/config.ts +7 -2
  258. package/src/channels/gateway-verification-sessions.ts +7 -2
  259. package/src/channels/types.ts +7 -1
  260. package/src/cli/commands/__tests__/cache.test.ts +7 -4
  261. package/src/cli/commands/__tests__/cli-test-harness.ts +82 -0
  262. package/src/cli/commands/__tests__/conversations-wake.test.ts +51 -6
  263. package/src/cli/commands/__tests__/inference-models.test.ts +14 -0
  264. package/src/cli/commands/__tests__/inference-profiles.test.ts +44 -1
  265. package/src/cli/commands/__tests__/inference-providers.test.ts +7 -0
  266. package/src/cli/commands/__tests__/plugins.test.ts +692 -0
  267. package/src/cli/commands/__tests__/schedules.test.ts +136 -37
  268. package/src/cli/commands/conversations.ts +7 -3
  269. package/src/cli/commands/credentials.help.ts +7 -0
  270. package/src/cli/commands/inference-profiles.ts +9 -3
  271. package/src/cli/commands/inference.help.ts +16 -2
  272. package/src/cli/commands/mcp.help.ts +4 -2
  273. package/src/cli/commands/memory/memory-retrospective.ts +18 -0
  274. package/src/cli/commands/notifications.ts +3 -6
  275. package/src/cli/commands/oauth/connect-surface-guidance.test.ts +16 -0
  276. package/src/cli/commands/oauth/connect-surface-guidance.ts +25 -7
  277. package/src/cli/commands/oauth/connect.test.ts +26 -1
  278. package/src/cli/commands/oauth/connect.ts +4 -1
  279. package/src/cli/commands/oauth/index.help.ts +3 -1
  280. package/src/cli/commands/oauth/providers.ts +3 -0
  281. package/src/cli/commands/plugins.help.ts +25 -2
  282. package/src/cli/commands/plugins.ts +289 -10
  283. package/src/cli/commands/schedules.help.ts +14 -12
  284. package/src/cli/commands/schedules.ts +19 -5
  285. package/src/cli/commands/tools.help.ts +4 -2
  286. package/src/cli/commands/tools.ts +38 -0
  287. package/src/cli/lib/__tests__/inspect-plugin.test.ts +1 -0
  288. package/src/cli/lib/__tests__/install-from-github.test.ts +79 -0
  289. package/src/cli/lib/__tests__/install-from-platform.test.ts +47 -1
  290. package/src/cli/lib/__tests__/plugin-surfaces.test.ts +86 -7
  291. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +122 -0
  292. package/src/cli/lib/install-from-github.ts +61 -0
  293. package/src/cli/lib/install-from-platform.ts +13 -2
  294. package/src/cli/lib/plugin-surfaces.ts +124 -4
  295. package/src/cli/lib/upgrade-plugin.ts +18 -0
  296. package/src/cli.ts +1 -1
  297. package/src/config/__tests__/deployment-context-defaults.test.ts +73 -0
  298. package/src/config/__tests__/memory-retrospective-schema.test.ts +40 -0
  299. package/src/config/__tests__/plugin-updates-schema.test.ts +43 -0
  300. package/src/config/__tests__/profile-tool-support.test.ts +131 -0
  301. package/src/config/bundled-skills/AGENTS.md +1 -1
  302. package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
  303. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +3 -3
  304. package/src/config/bundled-skills/schedule/SKILL.md +5 -1
  305. package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
  306. package/src/config/bundled-skills/settings/TOOLS.json +2 -2
  307. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +1 -0
  308. package/src/config/bundled-skills/subagent/SKILL.md +53 -22
  309. package/src/config/bundled-skills/subagent/TOOLS.json +14 -13
  310. package/src/config/bundled-skills/visualize/SKILL.md +1 -1
  311. package/src/config/default-profile-catalog.ts +6 -2
  312. package/src/config/feature-flag-registry.json +25 -17
  313. package/src/config/llm-resolver.ts +45 -15
  314. package/src/config/loader.ts +70 -1
  315. package/src/config/profile-tool-support.ts +110 -0
  316. package/src/config/sanitize-for-transfer.ts +11 -0
  317. package/src/config/schema.ts +4 -0
  318. package/src/config/schemas/__tests__/stt.test.ts +20 -5
  319. package/src/config/schemas/channels.ts +13 -0
  320. package/src/config/schemas/memory-retrospective.ts +25 -0
  321. package/src/config/schemas/plugin-updates.ts +55 -0
  322. package/src/config/schemas/services.ts +8 -4
  323. package/src/config/schemas/stt.ts +17 -5
  324. package/src/contacts/__tests__/guardian-delivery-reader.test.ts +16 -0
  325. package/src/contacts/guardian-delivery-reader.ts +29 -2
  326. package/src/context/compactor.ts +4 -4
  327. package/src/context/post-turn-tool-result-truncation.ts +32 -16
  328. package/src/context/strip-injections.ts +42 -0
  329. package/src/context/tool-result-spool.ts +32 -18
  330. package/src/credential-execution/ces-runtime.ts +3 -0
  331. package/src/credential-execution/client.ts +22 -1
  332. package/src/credential-execution/process-manager.test.ts +99 -0
  333. package/src/credential-execution/process-manager.ts +62 -16
  334. package/src/daemon/__tests__/abort-null-controller.test.ts +141 -5
  335. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +8 -12
  336. package/src/daemon/__tests__/install-assistant-command.test.ts +234 -0
  337. package/src/daemon/__tests__/turn-tail-assistant-reply-notify.test.ts +2 -4
  338. package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +20 -29
  339. package/src/daemon/channel-ui-capability.ts +4 -4
  340. package/src/daemon/conversation-agent-loop-handlers.ts +117 -61
  341. package/src/daemon/conversation-agent-loop.ts +296 -145
  342. package/src/daemon/conversation-attachments.ts +1 -1
  343. package/src/daemon/conversation-error.ts +8 -1
  344. package/src/daemon/conversation-initial-prompt.ts +16 -38
  345. package/src/daemon/conversation-lifecycle.ts +154 -32
  346. package/src/daemon/conversation-messaging.ts +85 -42
  347. package/src/daemon/conversation-process.ts +76 -28
  348. package/src/daemon/conversation-runtime-assembly.ts +9 -4
  349. package/src/daemon/conversation-store.ts +52 -3
  350. package/src/daemon/conversation-surface-state.ts +9 -0
  351. package/src/daemon/conversation-surfaces.ts +403 -210
  352. package/src/daemon/conversation-tool-setup.ts +56 -7
  353. package/src/daemon/conversation-turn-finalize.ts +134 -89
  354. package/src/daemon/conversation.ts +105 -27
  355. package/src/daemon/disk-pressure-guard-lifecycle.ts +2 -2
  356. package/src/daemon/disk-pressure-guard.ts +3 -3
  357. package/src/daemon/doordash-steps.ts +5 -5
  358. package/src/daemon/external-plugins-bootstrap.ts +7 -1
  359. package/src/daemon/handlers/config-channels.ts +48 -5
  360. package/src/daemon/handlers/conversation-history.ts +1 -1
  361. package/src/daemon/handlers/conversations.ts +62 -17
  362. package/src/daemon/handlers/shared.ts +12 -0
  363. package/src/daemon/handlers/skills.ts +11 -1
  364. package/src/daemon/identity-helpers.ts +38 -10
  365. package/src/daemon/install-assistant-command.ts +322 -0
  366. package/src/daemon/lifecycle.ts +14 -3
  367. package/src/daemon/message-types/conversations.ts +6 -7
  368. package/src/daemon/message-types/sync.ts +1 -0
  369. package/src/daemon/persist-media-references.ts +23 -14
  370. package/src/daemon/process-message.ts +6 -6
  371. package/src/daemon/tool-setup-types.ts +19 -122
  372. package/src/daemon/tool-side-effects.ts +17 -94
  373. package/src/daemon/trust-context.ts +14 -6
  374. package/src/daemon/turn-tail-chain.ts +73 -0
  375. package/src/daemon/web-search-history.ts +47 -33
  376. package/src/heartbeat/heartbeat-service.ts +6 -6
  377. package/src/ipc/__tests__/socket-path.test.ts +21 -0
  378. package/src/ipc/assistant-server.ts +17 -10
  379. package/src/ipc/cli-client.ts +10 -1
  380. package/src/ipc/routes/__tests__/documents-sync-ipc-routes.test.ts +56 -0
  381. package/src/ipc/routes/documents-sync-ipc-routes.ts +42 -0
  382. package/src/ipc/socket-cleanup.ts +9 -7
  383. package/src/ipc/socket-path.ts +4 -96
  384. package/src/live-voice/__tests__/live-voice-archive.test.ts +16 -16
  385. package/src/live-voice/__tests__/live-voice-attach-image.test.ts +209 -0
  386. package/src/live-voice/__tests__/live-voice-connection.test.ts +20 -0
  387. package/src/live-voice/__tests__/live-voice-integration.test.ts +7 -5
  388. package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +265 -0
  389. package/src/live-voice/__tests__/live-voice-stt.test.ts +6 -1
  390. package/src/live-voice/__tests__/live-voice-vad.test.ts +35 -15
  391. package/src/live-voice/__tests__/protocol.test.ts +52 -0
  392. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +3 -0
  393. package/src/live-voice/live-voice-archive.ts +13 -13
  394. package/src/live-voice/live-voice-connection.ts +7 -1
  395. package/src/live-voice/live-voice-photo.ts +169 -0
  396. package/src/live-voice/live-voice-session.ts +108 -4
  397. package/src/live-voice/protocol.ts +89 -1
  398. package/src/mcp/client.ts +10 -0
  399. package/src/messaging/providers/__tests__/callback-routing.test.ts +5 -1
  400. package/src/messaging/providers/__tests__/transport-dispatch.test.ts +66 -4
  401. package/src/messaging/providers/callback-routing.ts +1 -0
  402. package/src/messaging/providers/discord/api.ts +312 -0
  403. package/src/messaging/providers/discord/dm-delivery.test.ts +204 -0
  404. package/src/messaging/providers/discord/render.test.ts +204 -0
  405. package/src/messaging/providers/discord/render.ts +195 -0
  406. package/src/messaging/providers/discord/send.test.ts +179 -0
  407. package/src/messaging/providers/discord/send.ts +212 -0
  408. package/src/messaging/providers/discord/transport.ts +81 -0
  409. package/src/messaging/providers/index.ts +3 -1
  410. package/src/messaging/providers/retry-policy.test.ts +247 -0
  411. package/src/messaging/providers/retry-policy.ts +206 -0
  412. package/src/messaging/providers/slack/mention-source.test.ts +285 -0
  413. package/src/messaging/providers/slack/mention-source.ts +287 -0
  414. package/src/messaging/providers/telegram-bot/api.ts +47 -130
  415. package/src/messaging/providers/whatsapp/api.ts +47 -136
  416. package/src/monitoring/__tests__/db-integrity-sample.test.ts +4 -1
  417. package/src/monitoring/__tests__/plugin-auto-update.test.ts +332 -0
  418. package/src/monitoring/db-integrity-sample.ts +6 -1
  419. package/src/monitoring/plugin-auto-update.ts +359 -0
  420. package/src/monitoring/resource-sampler.ts +21 -5
  421. package/src/monitoring/worker.ts +13 -0
  422. package/src/notifications/__tests__/assistant-reply-producer.test.ts +375 -2
  423. package/src/notifications/__tests__/broadcaster.test.ts +37 -1
  424. package/src/notifications/__tests__/copy-composer.test.ts +165 -0
  425. package/src/notifications/__tests__/emit-signal-pipeline-failure.test.ts +205 -0
  426. package/src/notifications/__tests__/notification-utils.test.ts +249 -0
  427. package/src/notifications/adapters/platform.ts +31 -2
  428. package/src/notifications/assistant-reply-producer.ts +79 -32
  429. package/src/notifications/broadcaster.ts +30 -10
  430. package/src/notifications/copy-composer.ts +69 -0
  431. package/src/notifications/emit-signal.ts +82 -15
  432. package/src/notifications/events-store.ts +10 -2
  433. package/src/notifications/home-feed-side-effect.ts +6 -3
  434. package/src/notifications/notification-utils.ts +173 -5
  435. package/src/notifications/signal.ts +12 -0
  436. package/src/oauth/byo-connection.ts +6 -1
  437. package/src/oauth/credential-token-resolver.ts +3 -10
  438. package/src/oauth/manual-token-connection.ts +29 -64
  439. package/src/oauth/manual-token-providers.ts +62 -0
  440. package/src/oauth/provider-serializer.ts +13 -0
  441. package/src/oauth/seed-providers.ts +56 -8
  442. package/src/onboarding/onboarding-events-store.ts +91 -1
  443. package/src/permissions/question-prompter.test.ts +4 -0
  444. package/src/permissions/question-prompter.ts +21 -4
  445. package/src/persistence/__tests__/conversation-lineage.test.ts +248 -0
  446. package/src/persistence/attachments-store.ts +15 -15
  447. package/src/persistence/conversation-attention-store.ts +30 -10
  448. package/src/persistence/conversation-bootstrap.ts +17 -0
  449. package/src/persistence/conversation-crud.ts +363 -97
  450. package/src/persistence/conversation-group-migration.ts +1 -1
  451. package/src/persistence/conversation-key-store.ts +38 -10
  452. package/src/persistence/conversation-lineage.ts +226 -0
  453. package/src/persistence/conversation-plugin-facade.ts +28 -3
  454. package/src/persistence/conversation-queries.ts +207 -33
  455. package/src/persistence/conversation-types.ts +107 -1
  456. package/src/persistence/jobs-store.ts +32 -8
  457. package/src/persistence/llm-usage-store.ts +22 -0
  458. package/src/persistence/migrations/361-normalize-managed-connection-rows.test.ts +168 -0
  459. package/src/persistence/migrations/361-normalize-managed-connection-rows.ts +76 -0
  460. package/src/persistence/migrations/362-add-conversation-subagent-kind.test.ts +187 -0
  461. package/src/persistence/migrations/362-add-conversation-subagent-kind.ts +93 -0
  462. package/src/persistence/migrations/363-backfill-schedule-inference-profile.test.ts +229 -0
  463. package/src/persistence/migrations/363-backfill-schedule-inference-profile.ts +127 -0
  464. package/src/persistence/migrations/364-add-schedule-source-key.test.ts +120 -0
  465. package/src/persistence/migrations/364-add-schedule-source-key.ts +43 -0
  466. package/src/persistence/migrations/365-add-conversation-fork-strategy.ts +27 -0
  467. package/src/persistence/schema/conversations.ts +32 -0
  468. package/src/persistence/schema/infrastructure.ts +3 -0
  469. package/src/persistence/steps.ts +30 -0
  470. package/src/persistence/subagent-store.ts +148 -6
  471. package/src/platform/managed-speech.test.ts +20 -0
  472. package/src/platform/managed-speech.ts +9 -0
  473. package/src/plugin-api/index.ts +8 -0
  474. package/src/plugin-api/webhook-url.ts +151 -0
  475. package/src/plugins/__tests__/installed-plugin-dirs.test.ts +82 -0
  476. package/src/plugins/__tests__/source-fingerprint.test.ts +5 -1
  477. package/src/plugins/collect-source-versions.ts +4 -27
  478. package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +5 -5
  479. package/src/plugins/defaults/exploration-drift/package.json +1 -1
  480. package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +4 -2
  481. package/src/plugins/defaults/image-recovery/recover.ts +67 -57
  482. package/src/plugins/defaults/index.ts +1 -1
  483. package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +64 -18
  484. package/src/plugins/defaults/memory/__tests__/jobs-worker-outcome-contract.test.ts +242 -0
  485. package/src/plugins/defaults/memory/__tests__/jobs-worker-retrospective-sweep.test.ts +24 -1
  486. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +48 -3
  487. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +511 -19
  488. package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +39 -3
  489. package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +493 -0
  490. package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +5 -4
  491. package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +5 -1
  492. package/src/plugins/defaults/memory/job-handlers.ts +87 -5
  493. package/src/plugins/defaults/memory/jobs-worker.ts +60 -6
  494. package/src/plugins/defaults/memory/memory-retrospective-constants.ts +11 -0
  495. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +29 -0
  496. package/src/plugins/defaults/memory/memory-retrospective-job.ts +342 -60
  497. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +23 -3
  498. package/src/plugins/defaults/memory/substrate/consolidation-lock.ts +9 -4
  499. package/src/plugins/defaults/memory/substrate/sweep-job.ts +7 -0
  500. package/src/plugins/defaults/memory/v1/graph/injection.ts +1 -1
  501. package/src/plugins/defaults/memory/v2/router.ts +8 -0
  502. package/src/plugins/defaults/turn-context/unified-turn-context.ts +1 -2
  503. package/src/plugins/external-plugin-loader.ts +17 -8
  504. package/src/plugins/installed-plugin-dirs.ts +101 -0
  505. package/src/plugins/mtime-cache.ts +34 -22
  506. package/src/plugins/types.ts +69 -8
  507. package/src/prompts/persona-resolver.ts +6 -2
  508. package/src/prompts/system-prompt.ts +72 -17
  509. package/src/providers/__tests__/inference.test.ts +40 -0
  510. package/src/providers/__tests__/preflight-resolved-config.test.ts +58 -1
  511. package/src/providers/__tests__/retry-callsite.test.ts +67 -0
  512. package/src/providers/__tests__/vellum-connection-routing.test.ts +42 -11
  513. package/src/providers/anthropic/client.ts +112 -95
  514. package/src/providers/connection-resolution.ts +35 -1
  515. package/src/providers/gemini/client.ts +5 -5
  516. package/src/providers/inference/adapter-factory.ts +16 -10
  517. package/src/providers/inference/auth.ts +16 -0
  518. package/src/providers/inference/connection-availability.ts +164 -12
  519. package/src/providers/inference/connections.ts +25 -1
  520. package/src/providers/media-resolve.ts +21 -11
  521. package/src/providers/model-catalog.ts +15 -0
  522. package/src/providers/openai/chat-completions-provider.ts +4 -4
  523. package/src/providers/openai/responses-provider.ts +12 -25
  524. package/src/providers/registry.ts +9 -4
  525. package/src/providers/retry.ts +25 -0
  526. package/src/providers/server-tool-pairing.ts +134 -0
  527. package/src/providers/speech-to-text/__tests__/resolve.test.ts +187 -6
  528. package/src/providers/speech-to-text/deepgram.ts +1 -0
  529. package/src/providers/speech-to-text/resolve.ts +80 -10
  530. package/src/providers/speech-to-text/vellum-managed.ts +7 -1
  531. package/src/providers/speech-to-text/vellum-speech-relay-connection.ts +3 -1
  532. package/src/providers/types.ts +26 -6
  533. package/src/providers/vellum-model-routing.ts +6 -7
  534. package/src/runtime/AGENTS.md +2 -0
  535. package/src/runtime/__tests__/agent-wake.test.ts +16 -16
  536. package/src/runtime/__tests__/background-job-runner.test.ts +0 -144
  537. package/src/runtime/__tests__/desktop-presence.test.ts +162 -0
  538. package/src/runtime/agent-wake.ts +63 -24
  539. package/src/runtime/assistant-event-hub.ts +87 -12
  540. package/src/runtime/auth/__tests__/guard-tests.test.ts +2 -0
  541. package/src/runtime/auth/__tests__/scopes.test.ts +20 -0
  542. package/src/runtime/auth/same-actor.ts +56 -8
  543. package/src/runtime/auth/scopes.ts +12 -0
  544. package/src/runtime/auth/types.ts +2 -0
  545. package/src/runtime/background-job-runner.ts +5 -27
  546. package/src/runtime/channel-readiness-service.ts +59 -3
  547. package/src/runtime/channel-readiness-types.ts +18 -0
  548. package/src/runtime/desktop-presence.ts +69 -0
  549. package/src/runtime/http-server.ts +12 -0
  550. package/src/runtime/local-actor-identity.ts +11 -9
  551. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
  552. package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
  553. package/src/runtime/routes/__tests__/client-routes.test.ts +186 -14
  554. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +547 -1
  555. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +162 -7
  556. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +14 -10
  557. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +340 -3
  558. package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +28 -1
  559. package/src/runtime/routes/__tests__/plugins-routes.test.ts +173 -1
  560. package/src/runtime/routes/__tests__/question-routes.test.ts +64 -0
  561. package/src/runtime/routes/__tests__/retrospective-routes.test.ts +20 -0
  562. package/src/runtime/routes/__tests__/skill-history-route.test.ts +116 -0
  563. package/src/runtime/routes/__tests__/wake-conversation-routes.test.ts +60 -2
  564. package/src/runtime/routes/archive-utils.ts +16 -12
  565. package/src/runtime/routes/attachment-routes.ts +13 -6
  566. package/src/runtime/routes/canned-reply-release.ts +54 -0
  567. package/src/runtime/routes/channel-verification-routes.ts +2 -27
  568. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +5 -0
  569. package/src/runtime/routes/client-routes.ts +114 -2
  570. package/src/runtime/routes/conversation-cli-routes.ts +10 -12
  571. package/src/runtime/routes/conversation-list-routes.ts +59 -10
  572. package/src/runtime/routes/conversation-management-routes.ts +47 -18
  573. package/src/runtime/routes/conversation-query-routes.ts +32 -53
  574. package/src/runtime/routes/conversation-routes.ts +350 -293
  575. package/src/runtime/routes/credential-prompt-routes.ts +23 -2
  576. package/src/runtime/routes/debug-routes.ts +4 -1
  577. package/src/runtime/routes/default-provider-routes.ts +7 -11
  578. package/src/runtime/routes/documents-routes.ts +44 -1
  579. package/src/runtime/routes/events-routes.ts +28 -1
  580. package/src/runtime/routes/host-app-control-routes.ts +2 -0
  581. package/src/runtime/routes/host-bash-routes.ts +2 -0
  582. package/src/runtime/routes/host-browser-routes.ts +2 -0
  583. package/src/runtime/routes/host-cu-routes.ts +2 -0
  584. package/src/runtime/routes/host-file-routes.ts +2 -0
  585. package/src/runtime/routes/host-transfer-routes.ts +4 -0
  586. package/src/runtime/routes/identity-routes.ts +6 -6
  587. package/src/runtime/routes/inbound-message-handler.ts +5 -5
  588. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +7 -3
  589. package/src/runtime/routes/inbound-stages/background-dispatch.ts +14 -0
  590. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -2
  591. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +14 -11
  592. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -17
  593. package/src/runtime/routes/inference-profile-availability-guard.ts +171 -0
  594. package/src/runtime/routes/inference-profile-session-handler.ts +26 -2
  595. package/src/runtime/routes/inference-profile-session-routes.ts +2 -1
  596. package/src/runtime/routes/inference-profiles-routes.ts +152 -73
  597. package/src/runtime/routes/inference-provider-connection-routes.ts +0 -16
  598. package/src/runtime/routes/llm-call-sites-routes.ts +13 -0
  599. package/src/runtime/routes/log-export-routes.ts +1 -1
  600. package/src/runtime/routes/notification-routes.ts +0 -14
  601. package/src/runtime/routes/oauth-providers.ts +1 -0
  602. package/src/runtime/routes/plugins-routes.ts +106 -1
  603. package/src/runtime/routes/profiler-routes.ts +4 -2
  604. package/src/runtime/routes/question-routes.ts +28 -10
  605. package/src/runtime/routes/retrospective-routes.ts +9 -3
  606. package/src/runtime/routes/schedule-routes.ts +210 -12
  607. package/src/runtime/routes/secret-routes.ts +40 -23
  608. package/src/runtime/routes/settings-routes.ts +41 -13
  609. package/src/runtime/routes/skills-routes.ts +79 -0
  610. package/src/runtime/routes/ui-snapshot-routes.ts +1 -0
  611. package/src/runtime/routes/wake-conversation-routes.ts +23 -0
  612. package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
  613. package/src/runtime/services/conversation-serializer.ts +24 -9
  614. package/src/runtime/sync/documents-sidecar-publish.test.ts +101 -0
  615. package/src/runtime/sync/resource-sync-events.ts +79 -1
  616. package/src/runtime/sync/worker-daemon-notify.test.ts +34 -0
  617. package/src/runtime/sync/worker-daemon-notify.ts +44 -6
  618. package/src/runtime/verification-outbound-actions.ts +250 -222
  619. package/src/runtime/verification-templates.ts +41 -30
  620. package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +614 -0
  621. package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1066 -0
  622. package/src/schedule/inference-profile.ts +64 -3
  623. package/src/schedule/plugin-schedule-declarations.ts +518 -0
  624. package/src/schedule/plugin-schedule-reconciler.ts +605 -0
  625. package/src/schedule/plugin-schedules-gate.ts +23 -0
  626. package/src/schedule/schedule-store.ts +520 -26
  627. package/src/schedule/scheduler.ts +38 -0
  628. package/src/security/credential-backend.ts +51 -4
  629. package/src/security/encrypted-store.ts +64 -42
  630. package/src/security/secure-keys.ts +55 -7
  631. package/src/skills/catalog-install.ts +20 -7
  632. package/src/skills/inline-command-runner.ts +2 -7
  633. package/src/skills/skill-history.test.ts +269 -0
  634. package/src/skills/skill-history.ts +250 -0
  635. package/src/skills/skillssh-registry.ts +1 -1
  636. package/src/stt/daemon-batch-transcriber.ts +8 -6
  637. package/src/subagent/__tests__/consult-prompt.test.ts +25 -0
  638. package/src/subagent/consult-prompt.ts +3 -1
  639. package/src/subagent/consult-transcript.ts +1 -1
  640. package/src/subagent/manager.ts +295 -42
  641. package/src/subagent/progress-events.ts +35 -0
  642. package/src/subagent/role-resolution.ts +88 -0
  643. package/src/subagent/types.ts +302 -67
  644. package/src/subagent/validate-allowlists.ts +3 -4
  645. package/src/telegram/__tests__/webhook-health.test.ts +73 -1
  646. package/src/telegram/webhook-health.ts +101 -3
  647. package/src/telemetry/__tests__/telemetry-event-fixtures.ts +2 -0
  648. package/src/telemetry/activation-funnel.ts +6 -1
  649. package/src/telemetry/live-voice-funnel.ts +75 -0
  650. package/src/telemetry/telemetry-event-sources.ts +6 -0
  651. package/src/telemetry/telemetry-wire-source.json +1 -1
  652. package/src/telemetry/telemetry-wire.generated.ts +2 -0
  653. package/src/telemetry/types.ts +23 -4
  654. package/src/telemetry/usage-telemetry-reporter.test.ts +4 -0
  655. package/src/tools/__tests__/subagent-workflow-tool-input-schemas.test.ts +1 -1
  656. package/src/tools/ask-question/ask-question-tool.test.ts +118 -3
  657. package/src/tools/ask-question/ask-question-tool.ts +56 -2
  658. package/src/tools/browser/browser-execution.ts +1 -1
  659. package/src/tools/calls/call-start.ts +8 -5
  660. package/src/tools/conversation-groups/move_to_group.ts +10 -12
  661. package/src/tools/document/document-tool.ts +11 -4
  662. package/src/tools/executor.ts +7 -6
  663. package/src/tools/filesystem/edit.ts +1 -1
  664. package/src/tools/filesystem/list.ts +1 -1
  665. package/src/tools/filesystem/read.ts +7 -13
  666. package/src/tools/filesystem/search.ts +23 -21
  667. package/src/tools/filesystem/write.ts +4 -1
  668. package/src/tools/host-filesystem/edit.ts +7 -2
  669. package/src/tools/host-filesystem/read.ts +21 -8
  670. package/src/tools/host-filesystem/write.ts +10 -2
  671. package/src/tools/host-terminal/host-shell.ts +2 -2
  672. package/src/tools/mcp/mcp-tool-factory.ts +25 -1
  673. package/src/tools/permission-checker.ts +8 -3
  674. package/src/tools/schedule/delete.ts +13 -1
  675. package/src/tools/schedule/list.ts +14 -3
  676. package/src/tools/schedule/update.ts +56 -33
  677. package/src/tools/shared/filesystem/audio-read.ts +6 -4
  678. package/src/tools/shared/filesystem/file-ops-service.ts +176 -116
  679. package/src/tools/shared/filesystem/image-read.ts +12 -12
  680. package/src/tools/shared/filesystem/path-policy.ts +70 -5
  681. package/src/tools/shared/filesystem/size-guard.ts +6 -6
  682. package/src/tools/shared/input-misuse.ts +72 -0
  683. package/src/tools/skills/scaffold-managed.ts +82 -0
  684. package/src/tools/skills/skill-tool-factory.ts +14 -1
  685. package/src/tools/subagent/consult-deadline.ts +12 -7
  686. package/src/tools/subagent/message.ts +3 -1
  687. package/src/tools/subagent/read.ts +66 -4
  688. package/src/tools/subagent/spawn.ts +500 -78
  689. package/src/tools/types.ts +22 -3
  690. package/src/tools/ui-surface/surface-shape-docs.ts +1 -1
  691. package/src/tools/ui-surface/visual-validation.ts +2 -0
  692. package/src/usage/__tests__/subagent-attribution.test.ts +109 -0
  693. package/src/usage/subagent-attribution.ts +108 -0
  694. package/src/util/__tests__/sqlite-retry.test.ts +112 -0
  695. package/src/util/abort-reasons.ts +16 -0
  696. package/src/util/ansi.ts +44 -0
  697. package/src/util/disk-usage.ts +17 -13
  698. package/src/util/image-conversion.ts +20 -13
  699. package/src/util/schedule-source-key.ts +23 -0
  700. package/src/util/worker-process.ts +7 -1
  701. package/src/workspace/git-service.ts +33 -9
  702. package/src/workspace/migrations/140-repair-seed-pinned-memory-v3-live.ts +260 -0
  703. package/src/workspace/migrations/141-stt-english-default-to-multilingual.ts +111 -0
  704. package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +126 -0
  705. package/src/workspace/migrations/registry.ts +4 -0
  706. package/src/workspace/top-level-renderer.ts +15 -2
  707. package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -357
  708. package/src/daemon/install-symlink.ts +0 -207
  709. package/src/notifications/deferred-emit.ts +0 -147
@@ -1,357 +0,0 @@
1
- /**
2
- * Tests for the bash post-execution hook that dispatches Slack DMs
3
- * for channel verification sessions. Validates that the hook only
4
- * delivers when an active session exists with a matching destination.
5
- */
6
-
7
- import { beforeEach, describe, expect, mock, test } from "bun:test";
8
-
9
- // ---------------------------------------------------------------------------
10
- // Mocks — must be set up before importing the module under test
11
- // ---------------------------------------------------------------------------
12
-
13
- const mockFindActiveSession = mock((): unknown => null);
14
- mock.module("../channels/gateway-verification-sessions.js", () => ({
15
- findActiveSession: async () => mockFindActiveSession(),
16
- }));
17
-
18
- const mockDeliverVerificationSlack = mock(() => {});
19
- mock.module("../runtime/verification-outbound-actions.js", () => ({
20
- deliverVerificationSlack: mockDeliverVerificationSlack,
21
- }));
22
-
23
- // Stub out transitive dependencies to prevent import errors
24
- mock.module("../bundler/app-compiler.js", () => ({
25
- compileApp: mock(() => Promise.resolve({ ok: true })),
26
- }));
27
- mock.module("../media/app-icon-generator.js", () => ({
28
- generateAppIcon: mock(() => Promise.resolve()),
29
- }));
30
- mock.module("../apps/app-store.js", () => ({
31
- getApp: mock(() => null),
32
- getAppDirPath: mock(() => ""),
33
- getAppsDir: mock(() => ""),
34
- resolveAppIdFromPath: mock(() => null),
35
- resolveAppIdByDirName: mock(() => null),
36
- resolveAppDir: mock(() => ({ dirName: "", dirPath: "" })),
37
- slugify: mock((s: string) => s),
38
- validateDirName: mock(() => {}),
39
- generateAppDirName: mock(() => ""),
40
- listApps: mock(() => []),
41
- createApp: mock(() => ({})),
42
- updateApp: mock(() => {}),
43
- deleteApp: mock(() => {}),
44
- getAppPreview: mock(() => null),
45
- createAppRecord: mock(() => ({})),
46
- getAppRecord: mock(() => null),
47
- queryAppRecords: mock(() => []),
48
- updateAppRecord: mock(() => {}),
49
- deleteAppRecord: mock(() => {}),
50
- listAppFiles: mock(() => []),
51
- appFileExists: mock(() => false),
52
- readAppFile: mock(() => ""),
53
- writeAppFile: mock(() => {}),
54
- editAppFile: mock(() => ({})),
55
- inlineDistAssets: mock((_, html: string) => html),
56
- addAppConversationId: mock(() => false),
57
- linkAppToConversationLineage: mock(() => {}),
58
- }));
59
- mock.module("../services/published-app-updater.js", () => ({
60
- updatePublishedAppDeployment: mock(() => Promise.resolve()),
61
- }));
62
- mock.module("../daemon/conversation-surfaces.js", () => ({
63
- refreshSurfacesForApp: mock(() => {}),
64
- }));
65
- const mockIsDoordashCommand = mock(() => false);
66
- const mockUpdateDoordashProgress = mock(() => {});
67
- mock.module("../daemon/doordash-steps.js", () => ({
68
- isDoordashCommand: mockIsDoordashCommand,
69
- updateDoordashProgress: mockUpdateDoordashProgress,
70
- }));
71
-
72
- const mockLogWarn = mock((_obj: unknown, _msg: string) => {});
73
- const mockLogInfo = mock((_obj: unknown, _msg: string) => {});
74
- const mockLogError = mock((_obj: unknown, _msg: string) => {});
75
- mock.module("../util/logger.js", () => ({
76
- getLogger: () => ({
77
- warn: mockLogWarn,
78
- info: mockLogInfo,
79
- error: mockLogError,
80
- debug: () => {},
81
- trace: () => {},
82
- }),
83
- }));
84
-
85
- // ---------------------------------------------------------------------------
86
- // Import after mocks — dynamic import ensures mock.module() calls above
87
- // are registered before tool-side-effects.ts evaluates its top-level
88
- // `const log = getLogger(...)`.
89
- // ---------------------------------------------------------------------------
90
-
91
- import type { SideEffectContext } from "../daemon/tool-side-effects.js";
92
-
93
- const { runPostExecutionSideEffects } =
94
- await import("../daemon/tool-side-effects.js");
95
-
96
- // ---------------------------------------------------------------------------
97
- // Helpers
98
- // ---------------------------------------------------------------------------
99
-
100
- const dummySideEffectCtx = {
101
- ctx: {} as SideEffectContext["ctx"],
102
- } satisfies SideEffectContext;
103
-
104
- async function callWithBashHook(
105
- command: string,
106
- content: string,
107
- isError = false,
108
- ): Promise<void> {
109
- await runPostExecutionSideEffects(
110
- "bash",
111
- { command },
112
- { content, isError },
113
- dummySideEffectCtx,
114
- );
115
- }
116
-
117
- // ---------------------------------------------------------------------------
118
- // Tests
119
- // ---------------------------------------------------------------------------
120
-
121
- describe("bash hook — Slack DM dispatch with session validation", () => {
122
- beforeEach(() => {
123
- mockFindActiveSession.mockReset();
124
- mockDeliverVerificationSlack.mockReset();
125
- mockLogWarn.mockReset();
126
- mockLogInfo.mockReset();
127
- mockLogError.mockReset();
128
- });
129
-
130
- test("legitimate verification dispatches DM", async () => {
131
- mockFindActiveSession.mockReturnValue({
132
- destinationAddress: "U123",
133
- status: "awaiting_response",
134
- });
135
-
136
- const output = JSON.stringify({
137
- _pendingSlackDm: {
138
- userId: "U123",
139
- text: "code",
140
- assistantId: "aid",
141
- },
142
- });
143
-
144
- await callWithBashHook(
145
- "assistant channel-verification-sessions create ...",
146
- output,
147
- );
148
-
149
- expect(mockDeliverVerificationSlack).toHaveBeenCalledTimes(1);
150
- expect(mockDeliverVerificationSlack).toHaveBeenCalledWith(
151
- "U123",
152
- "code",
153
- "aid",
154
- );
155
- });
156
-
157
- test("no active session — DM not dispatched", async () => {
158
- mockFindActiveSession.mockReturnValue(null);
159
-
160
- const output = JSON.stringify({
161
- _pendingSlackDm: {
162
- userId: "U123",
163
- text: "code",
164
- assistantId: "aid",
165
- },
166
- });
167
-
168
- await callWithBashHook(
169
- "assistant channel-verification-sessions create ...",
170
- output,
171
- );
172
-
173
- expect(mockDeliverVerificationSlack).not.toHaveBeenCalled();
174
- expect(mockLogWarn).toHaveBeenCalledWith(
175
- expect.objectContaining({ userId: "U123" }),
176
- expect.stringContaining("no active Slack verification session"),
177
- );
178
- });
179
-
180
- test("userId mismatch — DM not dispatched", async () => {
181
- mockFindActiveSession.mockReturnValue({
182
- destinationAddress: "U999",
183
- status: "awaiting_response",
184
- });
185
-
186
- const output = JSON.stringify({
187
- _pendingSlackDm: {
188
- userId: "U_ATTACKER",
189
- text: "code",
190
- assistantId: "aid",
191
- },
192
- });
193
-
194
- await callWithBashHook(
195
- "assistant channel-verification-sessions create ...",
196
- output,
197
- );
198
-
199
- expect(mockDeliverVerificationSlack).not.toHaveBeenCalled();
200
- expect(mockLogWarn).toHaveBeenCalledWith(
201
- expect.objectContaining({ userId: "U_ATTACKER", expected: "U999" }),
202
- expect.stringContaining("does not match active session destination"),
203
- );
204
- });
205
-
206
- test("gateway session lookup failure — DM not dispatched (fail closed)", async () => {
207
- mockFindActiveSession.mockImplementation(() => {
208
- throw new Error("gateway unreachable");
209
- });
210
-
211
- const output = JSON.stringify({
212
- _pendingSlackDm: {
213
- userId: "U123",
214
- text: "code",
215
- assistantId: "aid",
216
- },
217
- });
218
-
219
- await callWithBashHook(
220
- "assistant channel-verification-sessions create ...",
221
- output,
222
- );
223
-
224
- expect(mockDeliverVerificationSlack).not.toHaveBeenCalled();
225
- expect(mockLogWarn).toHaveBeenCalledWith(
226
- expect.objectContaining({ userId: "U123" }),
227
- expect.stringContaining("gateway session lookup failed"),
228
- );
229
- });
230
-
231
- test("command without verification substring — hook is no-op", async () => {
232
- mockFindActiveSession.mockReturnValue({
233
- destinationAddress: "U123",
234
- status: "awaiting_response",
235
- });
236
-
237
- const output = JSON.stringify({
238
- _pendingSlackDm: {
239
- userId: "U123",
240
- text: "code",
241
- assistantId: "aid",
242
- },
243
- });
244
-
245
- await callWithBashHook("echo hello", output);
246
-
247
- expect(mockDeliverVerificationSlack).not.toHaveBeenCalled();
248
- });
249
-
250
- test("output without _pendingSlackDm — hook is no-op", async () => {
251
- mockFindActiveSession.mockReturnValue({
252
- destinationAddress: "U123",
253
- status: "awaiting_response",
254
- });
255
-
256
- await callWithBashHook(
257
- "assistant channel-verification-sessions create ...",
258
- JSON.stringify({ success: true, sessionId: "s1" }),
259
- );
260
-
261
- expect(mockDeliverVerificationSlack).not.toHaveBeenCalled();
262
- });
263
-
264
- test("multi-line JSON output — dispatches from correct line", async () => {
265
- mockFindActiveSession.mockReturnValue({
266
- destinationAddress: "U123",
267
- status: "awaiting_response",
268
- });
269
-
270
- const cancelResult = JSON.stringify({ success: true, cancelled: true });
271
- const createResult = JSON.stringify({
272
- _pendingSlackDm: {
273
- userId: "U123",
274
- text: "verify-code",
275
- assistantId: "aid2",
276
- },
277
- });
278
- const multiLineOutput = `${cancelResult}\n${createResult}`;
279
-
280
- await callWithBashHook(
281
- "assistant channel-verification-sessions create ...",
282
- multiLineOutput,
283
- );
284
-
285
- expect(mockDeliverVerificationSlack).toHaveBeenCalledTimes(1);
286
- expect(mockDeliverVerificationSlack).toHaveBeenCalledWith(
287
- "U123",
288
- "verify-code",
289
- "aid2",
290
- );
291
- });
292
-
293
- test("multi-line — rejected first line does not block valid second line", async () => {
294
- mockFindActiveSession.mockReturnValue({
295
- destinationAddress: "U200",
296
- status: "awaiting_response",
297
- });
298
-
299
- const staleResult = JSON.stringify({
300
- _pendingSlackDm: {
301
- userId: "U100",
302
- text: "stale-code",
303
- assistantId: "aid",
304
- },
305
- });
306
- const validResult = JSON.stringify({
307
- _pendingSlackDm: {
308
- userId: "U200",
309
- text: "valid-code",
310
- assistantId: "aid2",
311
- },
312
- });
313
- const multiLineOutput = `${staleResult}\n${validResult}`;
314
-
315
- await callWithBashHook(
316
- "assistant channel-verification-sessions create ...",
317
- multiLineOutput,
318
- );
319
-
320
- expect(mockDeliverVerificationSlack).toHaveBeenCalledTimes(1);
321
- expect(mockDeliverVerificationSlack).toHaveBeenCalledWith(
322
- "U200",
323
- "valid-code",
324
- "aid2",
325
- );
326
- });
327
- });
328
-
329
- describe("doordash progress side effect", () => {
330
- beforeEach(() => {
331
- mockIsDoordashCommand.mockReset();
332
- mockUpdateDoordashProgress.mockReset();
333
- mockLogError.mockReset();
334
- });
335
-
336
- test("throwing updateDoordashProgress is swallowed and logged", async () => {
337
- mockIsDoordashCommand.mockReturnValue(true);
338
- mockUpdateDoordashProgress.mockImplementation(() => {
339
- throw new Error("doordash boom");
340
- });
341
-
342
- await expect(
343
- runPostExecutionSideEffects(
344
- "bash",
345
- { command: "order food" },
346
- { content: "{}", isError: false },
347
- dummySideEffectCtx,
348
- ),
349
- ).resolves.toBeUndefined();
350
-
351
- expect(mockUpdateDoordashProgress).toHaveBeenCalledTimes(1);
352
- expect(mockLogError).toHaveBeenCalledWith(
353
- expect.objectContaining({ toolName: "bash" }),
354
- expect.stringContaining("DoorDash progress update failed"),
355
- );
356
- });
357
- });
@@ -1,207 +0,0 @@
1
- import { execFileSync } from "node:child_process";
2
- import {
3
- appendFileSync,
4
- existsSync,
5
- lstatSync,
6
- mkdirSync,
7
- readFileSync,
8
- readlinkSync,
9
- symlinkSync,
10
- unlinkSync,
11
- } from "node:fs";
12
- import { homedir } from "node:os";
13
- import { dirname, join } from "node:path";
14
-
15
- import { getLogger } from "../util/logger.js";
16
-
17
- const log = getLogger("install-symlink");
18
-
19
- /**
20
- * Resolves the path to the `vellum-assistant` binary that should be symlinked
21
- * as `assistant`.
22
- *
23
- * - **Bundled (desktop app):** The daemon runs from a compiled binary inside
24
- * `Vellum.app/Contents/MacOS/`. `process.execPath` is the daemon binary
25
- * itself — the `vellum-assistant` CLI binary lives alongside it in the same
26
- * directory.
27
- *
28
- * - **Dev (bun):** `process.execPath` is the bun runtime. We resolve the
29
- * assistant entrypoint relative to this file's location in the source tree
30
- * and return a wrapper script path that would need bun to run — so we skip
31
- * symlinking in dev mode since developers manage their own PATH.
32
- */
33
- function resolveAssistantBinary(): string | null {
34
- const execPath = process.execPath;
35
- // Detect the bundled desktop app case by checking for the app bundle path.
36
- const isBundled = execPath.includes("/Contents/MacOS/");
37
-
38
- if (isBundled) {
39
- // The assistant CLI binary is a sibling of the daemon binary in
40
- // Contents/MacOS/.
41
- const macosDir = dirname(execPath);
42
- const assistantBinary = join(macosDir, "vellum-assistant");
43
- if (existsSync(assistantBinary)) {
44
- return assistantBinary;
45
- }
46
- log.warn(
47
- { expected: assistantBinary },
48
- "Bundled vellum-assistant binary not found alongside daemon",
49
- );
50
- return null;
51
- }
52
-
53
- // Dev mode: resolve the assistant entrypoint from the source tree.
54
- // The daemon entry is at assistant/src/daemon/main.ts and the assistant
55
- // CLI entry is at assistant/src/index.ts.
56
- const assistantEntry = join(dirname(__filename), "..", "index.ts");
57
- if (existsSync(assistantEntry)) {
58
- return assistantEntry;
59
- }
60
- return null;
61
- }
62
-
63
- /**
64
- * Attempts to place a symlink at `symlinkPath` pointing to `target`.
65
- * Returns true if the symlink was created or already points to the target.
66
- */
67
- function trySymlink(target: string, symlinkPath: string): boolean {
68
- try {
69
- try {
70
- const stats = lstatSync(symlinkPath);
71
- if (!stats.isSymbolicLink()) {
72
- // Real file — don't overwrite (could be a developer's local install)
73
- return false;
74
- }
75
- const dest = readlinkSync(symlinkPath);
76
- if (dest === target) {
77
- return true;
78
- }
79
- // Stale or dangling symlink — remove before creating new one
80
- unlinkSync(symlinkPath);
81
- } catch (e) {
82
- if ((e as NodeJS.ErrnoException)?.code !== "ENOENT") {
83
- return false;
84
- }
85
- // Path doesn't exist — proceed to create symlink
86
- }
87
-
88
- const dir = dirname(symlinkPath);
89
- if (!existsSync(dir)) {
90
- mkdirSync(dir, { recursive: true });
91
- }
92
- symlinkSync(target, symlinkPath);
93
- return true;
94
- } catch {
95
- return false;
96
- }
97
- }
98
-
99
- /**
100
- * Ensures ~/.local/bin is present in the user's shell profile so that
101
- * symlinks placed there are on PATH in new terminal sessions.
102
- */
103
- function ensureLocalBinInShellProfile(localBinDir: string): void {
104
- const shell = process.env.SHELL ?? "";
105
- const home = homedir();
106
- const profilePath = shell.endsWith("/zsh")
107
- ? join(home, ".zshrc")
108
- : shell.endsWith("/bash")
109
- ? join(home, ".bash_profile")
110
- : null;
111
- if (!profilePath) {
112
- return;
113
- }
114
-
115
- try {
116
- const contents = existsSync(profilePath)
117
- ? readFileSync(profilePath, "utf-8")
118
- : "";
119
- if (contents.includes(localBinDir)) {
120
- return;
121
- }
122
- const line = `\nexport PATH="${localBinDir}:$PATH"\n`;
123
- appendFileSync(profilePath, line);
124
- log.info(
125
- { profilePath, localBinDir },
126
- "Added ~/.local/bin to shell profile",
127
- );
128
- } catch {
129
- // Not critical — user can add it manually
130
- }
131
- }
132
-
133
- /**
134
- * Checks whether `assistant` already resolves on PATH to something other than
135
- * our candidate symlink locations. If so, we skip symlinking to avoid
136
- * overwriting a developer's local build.
137
- */
138
- function commandResolvesElsewhere(
139
- commandName: string,
140
- candidatePaths: Set<string>,
141
- ): boolean {
142
- try {
143
- const resolved = execFileSync("/usr/bin/which", [commandName], {
144
- encoding: "utf-8",
145
- stdio: ["ignore", "pipe", "ignore"],
146
- }).trim();
147
- return resolved !== "" && !candidatePaths.has(resolved);
148
- } catch {
149
- // `which` exited non-zero — command not found, safe to proceed
150
- return false;
151
- }
152
- }
153
-
154
- /**
155
- * Idempotent: installs (or verifies) a symlink for the `assistant` command.
156
- *
157
- * Called on every daemon startup. Handles two runtime modes:
158
- * - **Bundled binary** (desktop app): symlinks to the compiled
159
- * `vellum-assistant` binary in the app bundle.
160
- * - **Bun dev mode**: symlinks to a small wrapper script that invokes
161
- * `bun run` on the assistant entrypoint.
162
- *
163
- * Tries `/usr/local/bin/assistant` first, then falls back to
164
- * `~/.local/bin/assistant` (and patches the shell profile if needed).
165
- *
166
- * Skipped when VELLUM_DEV=1 (developers manage their own PATH).
167
- */
168
- export function installAssistantSymlink(): void {
169
- if (process.env.VELLUM_DEV === "1") {
170
- return;
171
- }
172
-
173
- const target = resolveAssistantBinary();
174
- if (!target) {
175
- return;
176
- }
177
-
178
- const localBinDir = join(homedir(), ".local", "bin");
179
- const candidateDirs = ["/usr/local/bin", localBinDir];
180
- const candidatePaths = new Set(
181
- candidateDirs.map((dir) => `${dir}/assistant`),
182
- );
183
-
184
- if (commandResolvesElsewhere("assistant", candidatePaths)) {
185
- log.info(
186
- "`assistant` already resolves to a non-managed path — skipping symlink",
187
- );
188
- return;
189
- }
190
-
191
- for (const dir of candidateDirs) {
192
- const symlinkPath = join(dir, "assistant");
193
- if (trySymlink(target, symlinkPath)) {
194
- log.info({ symlinkPath, target }, "Installed assistant symlink");
195
- if (dir === localBinDir) {
196
- ensureLocalBinInShellProfile(localBinDir);
197
- }
198
- return;
199
- }
200
- log.info(
201
- { symlinkPath },
202
- "Could not install assistant symlink at candidate — trying next",
203
- );
204
- }
205
-
206
- log.warn("Could not install assistant symlink in any candidate directory");
207
- }
@@ -1,147 +0,0 @@
1
- /**
2
- * Per-conversation notification buffer used to keep a background job's
3
- * "success" notification from racing the runner's `activity.failed` when
4
- * the job times out after the model already invoked `notifications send`.
5
- *
6
- * `registerDeferredConversation` arms the buffer before the LLM turn.
7
- * `bufferIfDeferred` is called from the IPC route handler — it buffers when
8
- * armed, swallows when tombstoned (post-discard grace window), and returns
9
- * null otherwise so the route emits normally.
10
- * `commitDeferredConversation` flushes on success; `discardDeferredConversation`
11
- * drops on failure and tombstones briefly to catch late tool calls that
12
- * arrive after `processMessage` keeps running past the runner's timeout.
13
- */
14
-
15
- import { v4 as uuid } from "uuid";
16
-
17
- import { getLogger } from "../util/logger.js";
18
- import {
19
- emitNotificationSignal,
20
- type EmitSignalParams,
21
- type EmitSignalResult,
22
- } from "./emit-signal.js";
23
-
24
- const log = getLogger("notifications-deferred-emit");
25
-
26
- // How long after the last observed late notification we keep the tombstone
27
- // alive. `work` in `runBackgroundJob` is not cancelled on timeout — it can
28
- // continue running and emit skill calls indefinitely. We refresh the timer
29
- // on every late arrival, so the tombstone persists as long as the turn is
30
- // still draining and only expires once the conversation has been quiet for
31
- // this long.
32
- const TOMBSTONE_TTL_MS = 5 * 60 * 1000;
33
-
34
- type BufferEntry =
35
- | { state: "buffered"; items: EmitSignalParams<string>[] }
36
- | { state: "tombstoned"; timer: ReturnType<typeof setTimeout> };
37
-
38
- const buffers = new Map<string, BufferEntry>();
39
-
40
- function scheduleTombstoneEviction(
41
- conversationId: string,
42
- ): ReturnType<typeof setTimeout> {
43
- const timer = setTimeout(() => {
44
- const cur = buffers.get(conversationId);
45
- if (cur?.state === "tombstoned") {
46
- buffers.delete(conversationId);
47
- }
48
- }, TOMBSTONE_TTL_MS);
49
- timer.unref?.();
50
- return timer;
51
- }
52
-
53
- export function registerDeferredConversation(conversationId: string): void {
54
- buffers.set(conversationId, { state: "buffered", items: [] });
55
- }
56
-
57
- /**
58
- * Buffer the signal when the originating conversation is armed, swallow it
59
- * when tombstoned, otherwise return null so the caller emits normally.
60
- */
61
- export function bufferIfDeferred(
62
- originatingConversationId: string | undefined,
63
- params: EmitSignalParams<string>,
64
- ): EmitSignalResult | null {
65
- if (!originatingConversationId) {
66
- return null;
67
- }
68
- const entry = buffers.get(originatingConversationId);
69
- if (!entry) {
70
- return null;
71
- }
72
- if (entry.state === "tombstoned") {
73
- // Refresh the eviction timer so the tombstone outlives any continuing
74
- // turn activity. Otherwise a long-running orphan `processMessage` could
75
- // emit a `notifications send` after the fixed TTL elapsed and bypass
76
- // buffering, recreating the "success + activity.failed" contradiction.
77
- clearTimeout(entry.timer);
78
- entry.timer = scheduleTombstoneEviction(originatingConversationId);
79
- return {
80
- signalId: uuid(),
81
- deduplicated: false,
82
- dispatched: false,
83
- reason: "Notification dropped: background job did not complete",
84
- deliveryResults: [],
85
- };
86
- }
87
- entry.items.push(params);
88
- return {
89
- signalId: uuid(),
90
- deduplicated: false,
91
- dispatched: false,
92
- reason: "Notification deferred until background job completes",
93
- deliveryResults: [],
94
- };
95
- }
96
-
97
- export async function commitDeferredConversation(
98
- conversationId: string,
99
- ): Promise<void> {
100
- const entry = buffers.get(conversationId);
101
- if (!entry || entry.state !== "buffered") {
102
- return;
103
- }
104
- buffers.delete(conversationId);
105
- for (const params of entry.items) {
106
- try {
107
- await emitNotificationSignal(params);
108
- } catch (err) {
109
- log.warn(
110
- { err, conversationId },
111
- "Buffered notification failed to emit on commit",
112
- );
113
- }
114
- }
115
- }
116
-
117
- /**
118
- * Drop any buffered signals and tombstone the conversation. The tombstone
119
- * persists until the turn has been quiet for `TOMBSTONE_TTL_MS`; each late
120
- * notification observed via `bufferIfDeferred` refreshes the timer.
121
- */
122
- export function discardDeferredConversation(conversationId: string): number {
123
- const entry = buffers.get(conversationId);
124
- if (!entry) {
125
- return 0;
126
- }
127
- const droppedCount = entry.state === "buffered" ? entry.items.length : 0;
128
- if (entry.state === "tombstoned") {
129
- clearTimeout(entry.timer);
130
- }
131
- buffers.set(conversationId, {
132
- state: "tombstoned",
133
- timer: scheduleTombstoneEviction(conversationId),
134
- });
135
- if (droppedCount > 0) {
136
- log.info(
137
- { conversationId, droppedCount },
138
- "Discarded buffered notifications for failed background job",
139
- );
140
- }
141
- return droppedCount;
142
- }
143
-
144
- /** @internal Test-only reset hook. */
145
- export function resetDeferredForTest(): void {
146
- buffers.clear();
147
- }