@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
@@ -12,6 +12,7 @@ import type {
12
12
  import type { AssistantEvent } from "../api/index.js";
13
13
  import type { Message, ProviderResponse } from "../providers/types.js";
14
14
  import { stampAndBuffer } from "../runtime/assistant-stream-state.js";
15
+ import { createAbortReason } from "../util/abort-reasons.js";
15
16
  import { setConfig } from "./helpers/set-config.js";
16
17
 
17
18
  // ---------------------------------------------------------------------------
@@ -675,6 +676,62 @@ describe("Conversation message queue", () => {
675
676
  expect(pendingRuns.length).toBe(3);
676
677
  });
677
678
 
679
+ // `Conversation.clientOs` is a live field that only a transport-carrying
680
+ // message refreshes, so a transport-less drain (a surface action, a signal)
681
+ // persists the OS of an earlier send. Both rows keep that OS for telemetry;
682
+ // only the row that reported it claims the surface, which is what stops an
683
+ // attended Mac from suppressing the push for a button tapped on the phone.
684
+ test("[experimental] only a queued send that reported its own OS claims that surface", async () => {
685
+ capturedAddMessages.length = 0;
686
+ const conversation = makeConversation();
687
+ await conversation.loadFromDb();
688
+
689
+ const p1 = conversation.processMessage({
690
+ content: "msg-1",
691
+ attachments: [],
692
+ onEvent: () => {},
693
+ requestId: "req-1",
694
+ });
695
+ await waitForPendingRun(1);
696
+
697
+ conversation.enqueueMessage({
698
+ content: "from-the-mac",
699
+ onEvent: () => {},
700
+ requestId: "req-mac",
701
+ transport: { channelId: "vellum", interfaceId: "web", clientOs: "macos" },
702
+ });
703
+ // A surface action: no transport, so the drain leaves the conversation's
704
+ // `clientOs` on the macOS value the send above applied.
705
+ conversation.enqueueMessage({
706
+ content: "[User action on card surface: submit]",
707
+ onEvent: () => {},
708
+ requestId: "req-surface-action",
709
+ });
710
+
711
+ await resolveRun(0);
712
+ await p1;
713
+ await waitForPendingRun(2);
714
+ await resolveRun(1);
715
+ await waitForPendingRun(3);
716
+
717
+ const macRow = capturedAddMessages.find(
718
+ (m) => m.role === "user" && m.content.includes("from-the-mac"),
719
+ );
720
+ expect(macRow).toBeDefined();
721
+ expect(macRow!.metadata?.client).toEqual({ os: "macos" });
722
+ expect(macRow!.metadata?.clientOsFromRequest).toBe(true);
723
+
724
+ const surfaceActionRow = capturedAddMessages.find(
725
+ (m) => m.role === "user" && m.content.includes("User action on card"),
726
+ );
727
+ expect(surfaceActionRow).toBeDefined();
728
+ expect(surfaceActionRow!.metadata?.client).toEqual({ os: "macos" });
729
+ expect(surfaceActionRow!.metadata?.clientOsFromRequest).toBeUndefined();
730
+
731
+ await resolveRun(2);
732
+ await new Promise((r) => setTimeout(r, 10));
733
+ });
734
+
678
735
  test("[experimental] queued passthrough siblings viewing different apps do NOT batch", async () => {
679
736
  // A batched turn applies only the head's `visibleAppId`, which drives the
680
737
  // `visible_app:` context line. Coalescing messages sent while different
@@ -826,7 +883,87 @@ describe("Conversation message queue", () => {
826
883
  await new Promise((r) => setTimeout(r, 10));
827
884
  });
828
885
 
829
- test("abort() clears the queue and sends generation_cancelled for each queued message", async () => {
886
+ test("subagent notifications are never acked with message_queued and don't shift visible positions", async () => {
887
+ const conversation = makeConversation();
888
+ await conversation.loadFromDb();
889
+
890
+ const notificationEvents: AssistantEvent[] = [];
891
+ const visibleEvents: AssistantEvent[] = [];
892
+
893
+ const p1 = conversation.processMessage({
894
+ content: "msg-1",
895
+ attachments: [],
896
+ requestId: "req-1",
897
+ });
898
+ await waitForPendingRun(1);
899
+
900
+ // A subagent's completion summary injected into a busy parent is daemon
901
+ // scaffolding: the user follows that run through its inline card, so the
902
+ // row gets no queued ack and no queue-drawer bubble.
903
+ const notification = conversation.enqueueMessage({
904
+ content: '[Subagent "hermes-local-review" — important] review finished',
905
+ onEvent: (e) => notificationEvents.push(e),
906
+ requestId: "req-subagent",
907
+ metadata: {
908
+ subagentNotification: {
909
+ subagentId: "sub-1",
910
+ label: "hermes-local-review",
911
+ status: "completed",
912
+ conversationId: "conv-sub-1",
913
+ objective: "review the diff",
914
+ },
915
+ },
916
+ });
917
+ expect(notification.queued).toBe(true);
918
+ expect(
919
+ notificationEvents.filter((e) => e.type === "message_queued"),
920
+ ).toEqual([]);
921
+
922
+ // The user's own send queued behind it is position 1: the notification
923
+ // does not consume a visible slot, matching the cold-load snapshot.
924
+ const visible = conversation.enqueueMessage({
925
+ content: "visible-msg",
926
+ onEvent: (e) => visibleEvents.push(e),
927
+ requestId: "req-visible",
928
+ });
929
+ expect(visible.queued).toBe(true);
930
+ expect(conversation.getQueueDepth()).toBe(2);
931
+ expect(
932
+ visibleEvents.find((e) => e.type === "message_queued"),
933
+ ).toMatchObject({
934
+ requestId: "req-visible",
935
+ position: 1,
936
+ });
937
+
938
+ // Suppressing the ack changes only what clients are told: the row is
939
+ // still queued, still drains, and still wakes the parent on the notification
940
+ // text (delivery is asserted end-to-end in "drained subagent-notification
941
+ // message persists and wakes the agent but emits no user_message_echo").
942
+ await resolveRun(0);
943
+ await p1;
944
+ await waitForPendingRun(2);
945
+ await waitForCondition(() => conversation.getQueueDepth() === 0);
946
+ expect(
947
+ capturedAddMessages.some((m) =>
948
+ m.content.includes("hermes-local-review"),
949
+ ),
950
+ ).toBe(true);
951
+
952
+ for (let i = 1; i < pendingRuns.length; i++) {
953
+ await resolveRun(i);
954
+ }
955
+ await new Promise((r) => setTimeout(r, 10));
956
+
957
+ // Queue events stay paired: an enqueue that was never acked is never
958
+ // dequeued either, so the client counter can't retire an entry it never
959
+ // took. The visible send keeps both halves.
960
+ expect(
961
+ notificationEvents.filter((e) => e.type === "message_dequeued"),
962
+ ).toEqual([]);
963
+ expect(visibleEvents.some((e) => e.type === "message_dequeued")).toBe(true);
964
+ });
965
+
966
+ test("abort() clears the queue and closes out each queued message", async () => {
830
967
  const conversation = makeConversation();
831
968
  await conversation.loadFromDb();
832
969
 
@@ -873,6 +1010,17 @@ describe("Conversation message queue", () => {
873
1010
  conversationId: "conv-1",
874
1011
  });
875
1012
 
1013
+ // Each discarded row also gets its own terminal event. `generation_cancelled`
1014
+ // closes out the turn they were waiting on; only `message_queued_deleted`
1015
+ // closes out the queued rows themselves, and without it clients keep the
1016
+ // pending indicator up forever, since no `message_dequeued` is coming.
1017
+ expect(
1018
+ events2.find((e) => e.type === "message_queued_deleted"),
1019
+ ).toMatchObject({ conversationId: "conv-1", requestId: "req-2" });
1020
+ expect(
1021
+ events3.find((e) => e.type === "message_queued_deleted"),
1022
+ ).toMatchObject({ conversationId: "conv-1", requestId: "req-3" });
1023
+
876
1024
  // abort() must NOT emit conversation_error or generic error for queued discards.
877
1025
  const err2 = events2.find((e) => e.type === "error");
878
1026
  expect(err2).toBeUndefined();
@@ -897,6 +1045,122 @@ describe("Conversation message queue", () => {
897
1045
  await new Promise((r) => setTimeout(r, 10));
898
1046
  });
899
1047
 
1048
+ test("a user interrupt keeps the queue and the stopped turn's drain sends it", async () => {
1049
+ // GIVEN a turn in flight with a message queued behind it
1050
+ const conversation = makeConversation();
1051
+ await conversation.loadFromDb();
1052
+
1053
+ const events2: AssistantEvent[] = [];
1054
+ conversation.processMessage({
1055
+ content: "msg-1",
1056
+ attachments: [],
1057
+ requestId: "req-1",
1058
+ });
1059
+ await waitForPendingRun(1);
1060
+
1061
+ conversation.enqueueMessage({
1062
+ content: "msg-2",
1063
+ onEvent: (e) => events2.push(e),
1064
+ requestId: "req-2",
1065
+ });
1066
+ expect(conversation.getQueueDepth()).toBe(1);
1067
+
1068
+ // WHEN the user hits Stop
1069
+ conversation.abort(
1070
+ createAbortReason("user_cancel", "conversation-queue.test", "conv-1"),
1071
+ );
1072
+
1073
+ // THEN the queued message survives the abort. Stop ends the turn the user
1074
+ // is watching, not the message they queued behind it, and a discard here
1075
+ // reaches no client: it emits no per-row terminal event.
1076
+ expect(conversation.getQueueDepth()).toBe(1);
1077
+ expect(
1078
+ events2.find((e) => e.type === "generation_cancelled"),
1079
+ ).toBeUndefined();
1080
+ expect(
1081
+ events2.find((e) => e.type === "message_queued_deleted"),
1082
+ ).toBeUndefined();
1083
+
1084
+ // AND once the stopped turn unwinds, its `finally` drains the queue, so the
1085
+ // message is sent instead of needing to be retyped.
1086
+ await resolveRun(0);
1087
+ await waitForPendingRun(2);
1088
+ expect(events2.find((e) => e.type === "message_dequeued")).toMatchObject({
1089
+ conversationId: "conv-1",
1090
+ requestId: "req-2",
1091
+ });
1092
+ expect(conversation.getQueueDepth()).toBe(0);
1093
+ expect(
1094
+ JSON.stringify(
1095
+ pendingRuns[1].messages[pendingRuns[1].messages.length - 1],
1096
+ ),
1097
+ ).toContain("msg-2");
1098
+
1099
+ await resolveRun(1);
1100
+ await new Promise((r) => setTimeout(r, 10));
1101
+ });
1102
+
1103
+ test("a user interrupt mid-tool repairs the abandoned tool_use before the queued message runs", async () => {
1104
+ // GIVEN a turn whose history ends with an unanswered tool_use, the shape a
1105
+ // Stop mid-tool-call leaves behind and which providers reject outright
1106
+ const conversation = makeConversation();
1107
+ await conversation.loadFromDb();
1108
+
1109
+ const events2: AssistantEvent[] = [];
1110
+ conversation.processMessage({
1111
+ content: "msg-1",
1112
+ attachments: [],
1113
+ requestId: "req-1",
1114
+ });
1115
+ await waitForPendingRun(1);
1116
+
1117
+ conversation.enqueueMessage({
1118
+ content: "msg-2",
1119
+ onEvent: (e) => events2.push(e),
1120
+ requestId: "req-2",
1121
+ });
1122
+
1123
+ // WHEN the user interrupts and the stopped turn unwinds with the tool call
1124
+ // still unanswered, then drains the queue
1125
+ conversation.abort(
1126
+ createAbortReason("user_cancel", "conversation-queue.test", "conv-1"),
1127
+ );
1128
+ const interrupted = pendingRuns[0];
1129
+ await interrupted.onEvent({ type: "llm_call_started" });
1130
+ interrupted.resolve([
1131
+ ...interrupted.messages,
1132
+ {
1133
+ role: "assistant",
1134
+ content: [
1135
+ {
1136
+ type: "tool_use",
1137
+ id: "toolu-interrupted",
1138
+ name: "bash",
1139
+ input: {},
1140
+ },
1141
+ ],
1142
+ },
1143
+ ]);
1144
+ await waitForPendingRun(2);
1145
+
1146
+ // THEN the drained turn opens with a synthetic result for the abandoned
1147
+ // call rather than a dangling tool_use.
1148
+ const repaired = pendingRuns[1].messages.find(
1149
+ (m) =>
1150
+ m.role === "user" &&
1151
+ m.content.some(
1152
+ (block) =>
1153
+ block.type === "tool_result" &&
1154
+ block.tool_use_id === "toolu-interrupted",
1155
+ ),
1156
+ );
1157
+ expect(repaired).toBeDefined();
1158
+ expect(conversation.pendingInterruptRepair).toBe(false);
1159
+
1160
+ await resolveRun(1);
1161
+ await new Promise((r) => setTimeout(r, 10));
1162
+ });
1163
+
900
1164
  test("conversation-scoped errors emit both conversation_error and generic error", async () => {
901
1165
  const conversation = makeConversation();
902
1166
  await conversation.loadFromDb();
@@ -24,6 +24,7 @@ import { z } from "zod";
24
24
  import {
25
25
  isBackgroundEventMetadata,
26
26
  isEchoSuppressedUserMessage,
27
+ isHiddenMessageMetadata,
27
28
  } from "../persistence/conversation-types.js";
28
29
 
29
30
  mock.module("../config/env.js", () => ({
@@ -73,7 +74,7 @@ const getConversationMock = mock((id: string) =>
73
74
  mock.module("../persistence/conversation-crud.js", () => ({
74
75
  addMessage: async () => ({ id: "unused", deduplicated: false }),
75
76
  archiveConversation: () => true,
76
- batchSetDisplayOrders: () => {},
77
+ batchSetConversationPlacement: () => {},
77
78
  countConversationsByScheduleJobId: () => 0,
78
79
  deleteConversation: () => ({ segmentIds: [], deletedSummaryIds: [] }),
79
80
  extractImageSourcePaths: () => undefined,
@@ -81,6 +82,7 @@ mock.module("../persistence/conversation-crud.js", () => ({
81
82
  getConversation: getConversationMock,
82
83
  isBackgroundEventMetadata,
83
84
  isEchoSuppressedUserMessage,
85
+ isHiddenMessageMetadata,
84
86
  provenanceFromTrustContext: () => ({ provenanceTrustClass: "unknown" }),
85
87
  setConversationSurfaced: () => null,
86
88
  unarchiveConversation: () => true,
@@ -175,8 +175,12 @@ describe("getOrCreateConversation ephemeral flag", () => {
175
175
 
176
176
  await getOrCreateConversation("real-conversation-id");
177
177
 
178
+ // No trust context on this call, so no origin is asserted: the row is
179
+ // left unattributed for the first inbound message to claim, rather than
180
+ // being guessed as native.
178
181
  expect(mockEnsureConversationExists).toHaveBeenCalledWith(
179
182
  "real-conversation-id",
183
+ undefined,
180
184
  );
181
185
  });
182
186
  });
@@ -304,7 +304,7 @@ describe("attachment orphan cleanup", () => {
304
304
  "Here is a file",
305
305
  );
306
306
 
307
- const stored = uploadAttachment("chart.png", "image/png", "iVBOR");
307
+ const stored = await uploadAttachment("chart.png", "image/png", "iVBOR");
308
308
  linkAttachmentToMessage(assistantMsg.id, stored.id, 0);
309
309
 
310
310
  // Verify attachment is linked
@@ -331,7 +331,7 @@ describe("attachment orphan cleanup", () => {
331
331
  await addMessage(conv.id, "user", "question");
332
332
  const msg2 = await addMessage(conv.id, "assistant", "second");
333
333
 
334
- const shared = uploadAttachment("shared.png", "image/png", "AAAA");
334
+ const shared = await uploadAttachment("shared.png", "image/png", "AAAA");
335
335
  linkAttachmentToMessage(msg1.id, shared.id, 0);
336
336
  linkAttachmentToMessage(msg2.id, shared.id, 0);
337
337
 
@@ -353,7 +353,11 @@ describe("attachment orphan cleanup", () => {
353
353
  test("clearAll removes all attachments", async () => {
354
354
  const conv = createConversation("test");
355
355
  const msg = await addMessage(conv.id, "assistant", "file");
356
- const stored = uploadAttachment("doc.pdf", "application/pdf", "JVBER");
356
+ const stored = await uploadAttachment(
357
+ "doc.pdf",
358
+ "application/pdf",
359
+ "JVBER",
360
+ );
357
361
  linkAttachmentToMessage(msg.id, stored.id, 0);
358
362
 
359
363
  await clearAll();
@@ -441,11 +445,11 @@ describe("attachment orphan cleanup", () => {
441
445
  );
442
446
 
443
447
  // An attachment linked to the assistant message (should be cleaned up)
444
- const linked = uploadAttachment("chart.png", "image/png", "iVBOR");
448
+ const linked = await uploadAttachment("chart.png", "image/png", "iVBOR");
445
449
  linkAttachmentToMessage(assistantMsg.id, linked.id, 0);
446
450
 
447
451
  // A freshly uploaded attachment not linked to any message (should survive)
448
- uploadAttachment("pending.png", "image/png", "AAAA");
452
+ await uploadAttachment("pending.png", "image/png", "AAAA");
449
453
 
450
454
  deleteLastExchange(conv.id);
451
455
 
@@ -543,7 +547,7 @@ describe("attachment reuse across conversation lifecycles", () => {
543
547
  test("attachment uploaded in conversation A is retrievable by ID without any conversation reference", async () => {
544
548
  const convA = createConversation("Conversation A");
545
549
  const msgA = await addMessage(convA.id, "assistant", "Here is a file");
546
- const stored = uploadAttachment(
550
+ const stored = await uploadAttachment(
547
551
  "report.pdf",
548
552
  "application/pdf",
549
553
  "JVBERA==",
@@ -570,7 +574,11 @@ describe("attachment reuse across conversation lifecycles", () => {
570
574
  const msgB = await addMessage(convB.id, "assistant", "Reused file");
571
575
 
572
576
  // Upload once, link to both conversations
573
- const stored = uploadAttachment("shared.png", "image/png", "iVBORw0K");
577
+ const stored = await uploadAttachment(
578
+ "shared.png",
579
+ "image/png",
580
+ "iVBORw0K",
581
+ );
574
582
  linkAttachmentToMessage(msgA.id, stored.id, 0);
575
583
  linkAttachmentToMessage(msgB.id, stored.id, 0);
576
584
 
@@ -595,7 +603,7 @@ describe("attachment reuse across conversation lifecycles", () => {
595
603
  await addMessage(convB.id, "user", "Show me the chart");
596
604
  const msgB = await addMessage(convB.id, "assistant", "Reused");
597
605
 
598
- const stored = uploadAttachment("chart.png", "image/png", "AAAA");
606
+ const stored = await uploadAttachment("chart.png", "image/png", "AAAA");
599
607
  linkAttachmentToMessage(msgA.id, stored.id, 0);
600
608
  linkAttachmentToMessage(msgB.id, stored.id, 0);
601
609
  const linkedB = getAttachmentsForMessage(msgB.id);
@@ -621,8 +629,16 @@ describe("attachment reuse across conversation lifecycles", () => {
621
629
  await addMessage(convA.id, "user", "upload in A");
622
630
  await addMessage(convB.id, "user", "upload in B");
623
631
 
624
- const first = uploadAttachment("photo.png", "image/png", "DEDUPCROSS");
625
- const second = uploadAttachment("photo.png", "image/png", "DEDUPCROSS");
632
+ const first = await uploadAttachment(
633
+ "photo.png",
634
+ "image/png",
635
+ "DEDUPCROSS",
636
+ );
637
+ const second = await uploadAttachment(
638
+ "photo.png",
639
+ "image/png",
640
+ "DEDUPCROSS",
641
+ );
626
642
 
627
643
  expect(second.id).not.toBe(first.id);
628
644
  });
@@ -53,7 +53,7 @@ const getConversationMock = mock((id: string) =>
53
53
  mock.module("../persistence/conversation-crud.js", () => ({
54
54
  addMessage: addMessageMock,
55
55
  archiveConversation: () => true,
56
- batchSetDisplayOrders: () => {},
56
+ batchSetConversationPlacement: () => {},
57
57
  countConversationsByScheduleJobId: () => 0,
58
58
  deleteConversation: () => ({ segmentIds: [], deletedSummaryIds: [] }),
59
59
  extractImageSourcePaths: () => undefined,
@@ -9,12 +9,23 @@ mock.module("../runtime/assistant-event-hub.js", () => ({
9
9
  broadcastMessage: (msg: AssistantEvent) => broadcastedMessages.push(msg),
10
10
  }));
11
11
 
12
+ // These surfaces are shown live and asserted through their client messages, so
13
+ // history holds nothing. Stand in an empty store: the completion path reads
14
+ // persisted `ui_surface` blocks, and this file's DB carries no schema, so the
15
+ // real read would throw and be indistinguishable from a persistence failure.
16
+ const realCrud = await import("../persistence/conversation-crud.js");
17
+ mock.module("../persistence/conversation-crud.js", () => ({
18
+ ...realCrud,
19
+ getMessages: () => [],
20
+ }));
21
+
12
22
  const { createSurfaceMutex, handleSurfaceAction, surfaceProxyResolver } =
13
23
  await import("../daemon/conversation-surfaces.js");
14
24
 
15
- import type { SurfaceConversationContext } from "../daemon/conversation-surfaces.js";
25
+ import type { Conversation } from "../daemon/conversation.js";
16
26
  import type { SurfaceType, UiSurfaceShow } from "../daemon/message-protocol.js";
17
27
  import type { UserMessageAttachment } from "../daemon/message-types/shared.js";
28
+ import { asConversation } from "./helpers/mock-conversation.js";
18
29
 
19
30
  interface ProcessMessageCall {
20
31
  content: string;
@@ -25,13 +36,11 @@ interface ProcessMessageCall {
25
36
  sourceActorPrincipalId?: string;
26
37
  }
27
38
 
28
- function makeContext(
29
- sent: AssistantEvent[] = [],
30
- ): SurfaceConversationContext & {
39
+ function makeContext(sent: AssistantEvent[] = []): Conversation & {
31
40
  processMessageCalls: ProcessMessageCall[];
32
41
  } {
33
42
  const processMessageCalls: ProcessMessageCall[] = [];
34
- return {
43
+ return asConversation({
35
44
  conversationId: "conv-1",
36
45
  sendToClient: (msg: AssistantEvent) => sent.push(msg),
37
46
  pendingSurfaceActions: new Map<string, { surfaceType: SurfaceType }>(),
@@ -60,7 +69,7 @@ function makeContext(
60
69
  },
61
70
  withSurface: createSurfaceMutex(),
62
71
  processMessageCalls,
63
- };
72
+ });
64
73
  }
65
74
 
66
75
  describe("surface action delivery to assistant", () => {
@@ -29,7 +29,8 @@ const {
29
29
  surfaceProxyResolver,
30
30
  } = await import("../daemon/conversation-surfaces.js");
31
31
 
32
- import type { SurfaceConversationContext } from "../daemon/conversation-surfaces.js";
32
+ import type { Conversation } from "../daemon/conversation.js";
33
+ import type { ProcessMessageOptions } from "../daemon/conversation-process.js";
33
34
  import type { SurfaceType, UiSurfaceShow } from "../daemon/message-protocol.js";
34
35
  import {
35
36
  getMemorySqlite,
@@ -61,7 +62,7 @@ interface ProcessMessageCall {
61
62
  function makeContext(
62
63
  conversationId: string,
63
64
  sent: AssistantEvent[] = [],
64
- ): SurfaceConversationContext & {
65
+ ): Conversation & {
65
66
  processMessageCalls: ProcessMessageCall[];
66
67
  } {
67
68
  const processMessageCalls: ProcessMessageCall[] = [];
@@ -81,7 +82,7 @@ function makeContext(
81
82
  isProcessing: () => false,
82
83
  enqueueMessage: () => ({ queued: false, requestId: "req-1" }),
83
84
  getQueueDepth: () => 0,
84
- processMessage: async (options) => {
85
+ processMessage: async (options: ProcessMessageOptions) => {
85
86
  processMessageCalls.push({
86
87
  content: options.content,
87
88
  activeSurfaceId: options.activeSurfaceId,
@@ -90,7 +91,7 @@ function makeContext(
90
91
  },
91
92
  withSurface: createSurfaceMutex(),
92
93
  processMessageCalls,
93
- } as SurfaceConversationContext & {
94
+ } as unknown as Conversation & {
94
95
  processMessageCalls: ProcessMessageCall[];
95
96
  };
96
97
  }
@@ -102,7 +103,7 @@ function resetTables(): void {
102
103
 
103
104
  /** Render a choice surface tagged (or not) with an activation_moment. */
104
105
  async function showTaggedChoice(
105
- ctx: SurfaceConversationContext,
106
+ ctx: Conversation,
106
107
  sent: AssistantEvent[],
107
108
  activationMoment?: string,
108
109
  ): Promise<string> {
@@ -9,11 +9,13 @@
9
9
 
10
10
  import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
11
11
 
12
+ import { asConversation } from "./helpers/mock-conversation.js";
13
+
12
14
  const sentMessages: unknown[] = [];
13
15
  let mockHasClient = true;
14
16
  // Default principal id used for both ctx.trustContext and clients in the
15
17
  // existing single-user tests. Tests that exercise cross-user behaviour
16
- // override this on individual clients and on the SurfaceConversationContext.
18
+ // override this on individual clients and on the Conversation.
17
19
  const DEFAULT_PRINCIPAL = "user-1";
18
20
  type MockClient = {
19
21
  clientId: string;
@@ -47,22 +49,21 @@ mock.module("../runtime/pending-interactions.js", () => ({
47
49
  removeByConversation: () => {},
48
50
  }));
49
51
 
50
- const { surfaceProxyResolver } =
52
+ const { createSurfaceMutex, surfaceProxyResolver } =
51
53
  await import("../daemon/conversation-surfaces.js");
52
54
  const {
53
55
  HostAppControlProxy,
54
56
  _resetActiveAppControlSession,
55
57
  _setActiveAppControlSession,
56
58
  } = await import("../daemon/host-app-control-proxy.js");
57
- type SurfaceConversationContext =
58
- import("../daemon/conversation-surfaces.js").SurfaceConversationContext;
59
+ type Conversation = import("../daemon/conversation.js").Conversation;
59
60
 
60
61
  // ---------------------------------------------------------------------------
61
62
  // Test helpers
62
63
  // ---------------------------------------------------------------------------
63
64
 
64
65
  /**
65
- * Build a minimal SurfaceConversationContext with an optional
66
+ * Build a minimal Conversation with an optional
66
67
  * hostAppControlProxy. Only the fields required by the app-control routing
67
68
  * path are populated.
68
69
  */
@@ -74,8 +75,8 @@ function buildMockContext(
74
75
  ) => void,
75
76
  trustGuardianPrincipalId: string | null = DEFAULT_PRINCIPAL,
76
77
  actorPrincipalId: string | null = trustGuardianPrincipalId,
77
- ): SurfaceConversationContext {
78
- return {
78
+ ): Conversation {
79
+ return asConversation({
79
80
  conversationId,
80
81
  trustContext:
81
82
  trustGuardianPrincipalId != null
@@ -87,13 +88,13 @@ function buildMockContext(
87
88
  : undefined,
88
89
  authContext:
89
90
  actorPrincipalId != null
90
- ? ({ actorPrincipalId } as SurfaceConversationContext["authContext"])
91
+ ? ({ actorPrincipalId } as Conversation["authContext"])
91
92
  : undefined,
92
93
  currentTurnAuthContext:
93
94
  actorPrincipalId != null
94
95
  ? ({
95
96
  actorPrincipalId,
96
- } as SurfaceConversationContext["currentTurnAuthContext"])
97
+ } as Conversation["currentTurnAuthContext"])
97
98
  : undefined,
98
99
  sendToClient: () => {},
99
100
  pendingSurfaceActions: new Map(),
@@ -109,8 +110,8 @@ function buildMockContext(
109
110
  enqueueMessage: () => ({ queued: false, requestId: "r1" }),
110
111
  getQueueDepth: () => 0,
111
112
  processMessage: async () => "",
112
- withSurface: async (_id, fn) => fn(),
113
- };
113
+ withSurface: createSurfaceMutex(),
114
+ });
114
115
  }
115
116
 
116
117
  // ---------------------------------------------------------------------------
@@ -2,13 +2,17 @@ import { describe, expect, test } from "bun:test";
2
2
 
3
3
  import type { AssistantEvent } from "../api/index.js";
4
4
  import { DynamicPagePreviewSchema } from "../api/surfaces.js";
5
+ import type { Conversation } from "../daemon/conversation.js";
5
6
  import {
6
7
  buildAppOpenPreview,
7
8
  createSurfaceMutex,
8
- type SurfaceConversationContext,
9
9
  surfaceProxyResolver,
10
10
  } from "../daemon/conversation-surfaces.js";
11
11
  import type { SurfaceType } from "../daemon/message-protocol.js";
12
+ import {
13
+ asConversation,
14
+ mockChannelCapabilities,
15
+ } from "./helpers/mock-conversation.js";
12
16
 
13
17
  interface ContextOptions {
14
18
  hasNoClient?: boolean;
@@ -18,11 +22,13 @@ interface ContextOptions {
18
22
  function makeContext(
19
23
  sent: AssistantEvent[] = [],
20
24
  options: ContextOptions = {},
21
- ): SurfaceConversationContext {
22
- return {
25
+ ): Conversation {
26
+ return asConversation({
23
27
  conversationId: "session-1",
24
28
  hasNoClient: options.hasNoClient,
25
- channelCapabilities: options.channelCapabilities,
29
+ channelCapabilities:
30
+ options.channelCapabilities &&
31
+ mockChannelCapabilities(options.channelCapabilities),
26
32
  sendToClient: (msg) => sent.push(msg),
27
33
  pendingSurfaceActions: new Map<string, { surfaceType: SurfaceType }>(),
28
34
  lastSurfaceAction: new Map<
@@ -39,7 +45,7 @@ function makeContext(
39
45
  getQueueDepth: () => 0,
40
46
  processMessage: async () => "ok",
41
47
  withSurface: createSurfaceMutex(),
42
- };
48
+ });
43
49
  }
44
50
 
45
51
  describe("app_open render-capability gate", () => {