@vellumai/assistant 0.11.2 → 0.11.3-staging.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (720) hide show
  1. package/ARCHITECTURE.md +58 -3
  2. package/docs/trusted-contact-access.md +9 -5
  3. package/docs/vellum-doctor.md +195 -0
  4. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
  5. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  6. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  7. package/node_modules/@vellumai/ces-client/src/index.ts +1 -0
  8. package/node_modules/@vellumai/ces-client/src/rpc-client.ts +19 -2
  9. package/node_modules/@vellumai/environments/package.json +2 -1
  10. package/node_modules/@vellumai/environments/src/__tests__/cloud-assistant-hub-url.test.ts +38 -0
  11. package/node_modules/@vellumai/environments/src/__tests__/install-layout.test.ts +98 -0
  12. package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +5 -5
  13. package/node_modules/@vellumai/environments/src/index.ts +17 -6
  14. package/node_modules/@vellumai/environments/src/install-layout.ts +49 -0
  15. package/node_modules/@vellumai/environments/src/seeds.ts +29 -0
  16. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
  17. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  18. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  19. package/node_modules/@vellumai/gateway-client/src/index.ts +4 -0
  20. package/node_modules/@vellumai/gateway-client/src/verification-session-contract.ts +91 -6
  21. package/node_modules/@vellumai/ipc-server-utils/src/endpoint.test.ts +36 -0
  22. package/node_modules/@vellumai/ipc-server-utils/src/endpoint.ts +142 -0
  23. package/node_modules/@vellumai/ipc-server-utils/src/index.ts +2 -0
  24. package/node_modules/@vellumai/ipc-server-utils/src/listen-options.ts +3 -0
  25. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +15 -1
  26. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +17 -2
  27. package/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
  28. package/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  29. package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  30. package/node_modules/@vellumai/slack-text/src/index.ts +16 -2
  31. package/openapi.yaml +589 -30
  32. package/package.json +1 -1
  33. package/src/__tests__/agent-image-optimize.test.ts +4 -4
  34. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +113 -66
  35. package/src/__tests__/agent-loop-override-profile.test.ts +38 -48
  36. package/src/__tests__/agent-loop-subagent-billing-headers.test.ts +166 -0
  37. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
  38. package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
  39. package/src/__tests__/answered-question-persistence.test.ts +240 -0
  40. package/src/__tests__/anthropic-provider.test.ts +165 -30
  41. package/src/__tests__/app-control-flow.test.ts +9 -8
  42. package/src/__tests__/assistant-event-hub.test.ts +48 -0
  43. package/src/__tests__/attachments-store-heic-normalize.test.ts +16 -12
  44. package/src/__tests__/attachments-store.test.ts +130 -68
  45. package/src/__tests__/attachments.test.ts +36 -36
  46. package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
  47. package/src/__tests__/call-start-guardian-guard.test.ts +18 -4
  48. package/src/__tests__/canned-reply-release.test.ts +86 -0
  49. package/src/__tests__/channel-guardian.test.ts +59 -29
  50. package/src/__tests__/channel-readiness-telegram-remote.test.ts +154 -0
  51. package/src/__tests__/channel-setup-panel-ack.test.ts +6 -5
  52. package/src/__tests__/client-os-metadata-persistence.test.ts +103 -4
  53. package/src/__tests__/compactor-image-manifest-trust.test.ts +1 -1
  54. package/src/__tests__/consult-deadline.test.ts +65 -0
  55. package/src/__tests__/conversation-agent-loop.test.ts +438 -2
  56. package/src/__tests__/conversation-disk-view-integration.test.ts +5 -1
  57. package/src/__tests__/conversation-disk-view.test.ts +4 -4
  58. package/src/__tests__/conversation-error.test.ts +22 -3
  59. package/src/__tests__/conversation-fork-crud.test.ts +5 -1
  60. package/src/__tests__/conversation-fork-referential.test.ts +352 -0
  61. package/src/__tests__/conversation-fork-retrospective.test.ts +5 -1
  62. package/src/__tests__/conversation-group-tools.test.ts +32 -2
  63. package/src/__tests__/conversation-inference-profile-route.test.ts +27 -1
  64. package/src/__tests__/conversation-initial-prompt.test.ts +51 -22
  65. package/src/__tests__/conversation-key-store-bootstrap-cleanup.test.ts +119 -0
  66. package/src/__tests__/conversation-key-store-origin.test.ts +105 -0
  67. package/src/__tests__/conversation-list-recency-ordering.test.ts +104 -0
  68. package/src/__tests__/conversation-origin-at-creation.test.ts +153 -0
  69. package/src/__tests__/conversation-origin-channel-filter.test.ts +95 -0
  70. package/src/__tests__/conversation-placement-promotion.test.ts +172 -0
  71. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +123 -0
  72. package/src/__tests__/conversation-queue.test.ts +433 -1
  73. package/src/__tests__/conversation-retry-route.test.ts +3 -1
  74. package/src/__tests__/conversation-routes-hidden-queue.test.ts +64 -1
  75. package/src/__tests__/conversation-store-ephemeral.test.ts +4 -0
  76. package/src/__tests__/conversation-store.test.ts +26 -10
  77. package/src/__tests__/conversation-summarize-route.test.ts +1 -1
  78. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +15 -6
  79. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -5
  80. package/src/__tests__/conversation-surfaces-app-control.test.ts +12 -11
  81. package/src/__tests__/conversation-surfaces-app-open.test.ts +11 -5
  82. package/src/__tests__/conversation-surfaces-data-persist.test.ts +5 -4
  83. package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +967 -0
  84. package/src/__tests__/conversation-surfaces-persisted-info.test.ts +312 -0
  85. package/src/__tests__/conversation-surfaces-queued-emit.test.ts +6 -5
  86. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -8
  87. package/src/__tests__/conversation-surfaces-standalone.test.ts +21 -11
  88. package/src/__tests__/conversation-surfaces-state-update.test.ts +7 -11
  89. package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -6
  90. package/src/__tests__/conversation-surfaces-task-progress.test.ts +11 -6
  91. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +9 -6
  92. package/src/__tests__/conversation-tool-setup-attribution.test.ts +70 -7
  93. package/src/__tests__/conversation-wait-for-idle.test.ts +6 -11
  94. package/src/__tests__/credential-prompt-route.test.ts +57 -7
  95. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  96. package/src/__tests__/cu-unified-flow.test.ts +89 -28
  97. package/src/__tests__/custom-group-listing-visibility.test.ts +1 -1
  98. package/src/__tests__/db-schedule-syntax-migration.test.ts +4 -1
  99. package/src/__tests__/discord-access-request-privacy.test.ts +366 -0
  100. package/src/__tests__/discord-requester-notice-privacy.test.ts +87 -0
  101. package/src/__tests__/disk-pressure-guard.test.ts +55 -55
  102. package/src/__tests__/disk-pressure-routes.test.ts +10 -10
  103. package/src/__tests__/disk-pressure-tools.test.ts +2 -2
  104. package/src/__tests__/disk-usage.test.ts +35 -20
  105. package/src/__tests__/document-sync-tags.test.ts +379 -0
  106. package/src/__tests__/document-update-default-surface.test.ts +28 -0
  107. package/src/__tests__/dynamic-skill-background-guard.test.ts +2 -2
  108. package/src/__tests__/emit-signal-routing-intent.test.ts +3 -4
  109. package/src/__tests__/ensure-conversation-exists.test.ts +8 -5
  110. package/src/__tests__/events-dev-bypass-actor.test.ts +183 -1
  111. package/src/__tests__/evict-conversations-for-reload.test.ts +90 -0
  112. package/src/__tests__/file-list-tool.test.ts +12 -10
  113. package/src/__tests__/file-ops-service.test.ts +151 -44
  114. package/src/__tests__/file-read-container-boundary.test.ts +137 -0
  115. package/src/__tests__/file-read-tool.test.ts +21 -9
  116. package/src/__tests__/file-url-path-guard.test.ts +83 -0
  117. package/src/__tests__/filesystem-tools.test.ts +82 -66
  118. package/src/__tests__/guardian-outbound-http.test.ts +8 -8
  119. package/src/__tests__/helpers/gateway-verification-sessions-stub.ts +14 -1
  120. package/src/__tests__/helpers/hub-clients.ts +42 -0
  121. package/src/__tests__/helpers/mock-conversation.ts +46 -0
  122. package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +61 -57
  123. package/src/__tests__/history-repair.test.ts +112 -12
  124. package/src/__tests__/host-bash-proxy.test.ts +10 -5
  125. package/src/__tests__/host-bash-routes.test.ts +67 -0
  126. package/src/__tests__/host-browser-proxy.test.ts +2 -2
  127. package/src/__tests__/host-cu-proxy.test.ts +3 -7
  128. package/src/__tests__/host-file-proxy.test.ts +3 -7
  129. package/src/__tests__/host-file-read-tool.test.ts +5 -1
  130. package/src/__tests__/host-transfer-proxy-targeted.test.ts +6 -4
  131. package/src/__tests__/http-conversation-lineage.test.ts +2 -5
  132. package/src/__tests__/http-user-message-parity.test.ts +44 -0
  133. package/src/__tests__/identity-routes.test.ts +13 -13
  134. package/src/__tests__/image-conversion.test.ts +29 -26
  135. package/src/__tests__/image-source-path-reinject.test.ts +2 -2
  136. package/src/__tests__/inference-profile-session-handler.test.ts +95 -0
  137. package/src/__tests__/inference-profile-session-ipc.test.ts +25 -0
  138. package/src/__tests__/introduction-card-resolver.test.ts +6 -1
  139. package/src/__tests__/list-messages-attachments.test.ts +56 -32
  140. package/src/__tests__/list-messages-background-tool-completion.test.ts +4 -4
  141. package/src/__tests__/list-messages-client-message-id.test.ts +2 -2
  142. package/src/__tests__/list-messages-hidden-metadata.test.ts +16 -16
  143. package/src/__tests__/list-messages-page-latest.test.ts +61 -61
  144. package/src/__tests__/list-messages-provider-error.test.ts +6 -6
  145. package/src/__tests__/list-messages-queued.test.ts +49 -6
  146. package/src/__tests__/list-messages-system-card.test.ts +4 -4
  147. package/src/__tests__/list-messages-tool-merge.test.ts +9 -9
  148. package/src/__tests__/llm-resolver-override-or-default.test.ts +141 -0
  149. package/src/__tests__/llm-usage-store.test.ts +55 -0
  150. package/src/__tests__/manual-token-reconciliation.test.ts +45 -0
  151. package/src/__tests__/mcp-tool-annotations-risk.test.ts +133 -0
  152. package/src/__tests__/media-stream-server-integration.test.ts +36 -0
  153. package/src/__tests__/migration-export-http.test.ts +1 -1
  154. package/src/__tests__/migration-import-commit-http.test.ts +1 -1
  155. package/src/__tests__/migration-import-preflight-http.test.ts +1 -1
  156. package/src/__tests__/migration-validate-http.test.ts +1 -1
  157. package/src/__tests__/mtime-cache.test.ts +65 -3
  158. package/src/__tests__/notification-platform-adapter.test.ts +104 -0
  159. package/src/__tests__/notification-schedule-notify-dedup.test.ts +1 -1
  160. package/src/__tests__/oauth-provider-profiles.test.ts +4 -2
  161. package/src/__tests__/oauth-provider-seed-logos.test.ts +65 -0
  162. package/src/__tests__/oauth-provider-serializer.test.ts +17 -0
  163. package/src/__tests__/oauth-providers-routes.test.ts +1 -0
  164. package/src/__tests__/openai-responses-prompt-cache.test.ts +5 -2
  165. package/src/__tests__/path-policy.test.ts +164 -1
  166. package/src/__tests__/persist-media-references.test.ts +2 -2
  167. package/src/__tests__/persist-unsendable-image-downscale.test.ts +5 -5
  168. package/src/__tests__/persist-unsendable-image.test.ts +23 -23
  169. package/src/__tests__/persona-resolver.test.ts +154 -0
  170. package/src/__tests__/plugin-api-webhook-url.test.ts +170 -0
  171. package/src/__tests__/plugin-import-boundary-guard.test.ts +6 -0
  172. package/src/__tests__/processing-flag-persist-failure.test.ts +15 -10
  173. package/src/__tests__/profile-availability-incomplete.test.ts +113 -0
  174. package/src/__tests__/profiler-routes.test.ts +13 -13
  175. package/src/__tests__/prompt-cache-cross-turn-stability.test.ts +593 -0
  176. package/src/__tests__/proxy-approval-callback.test.ts +3 -3
  177. package/src/__tests__/queued-message-cancel-and-steer.test.ts +189 -0
  178. package/src/__tests__/queued-message-steer-actor-scoping.test.ts +164 -0
  179. package/src/__tests__/resolve-trust-class.test.ts +20 -0
  180. package/src/__tests__/run-conversation-turn-persistence.test.ts +1 -1
  181. package/src/__tests__/run-due-schedules.test.ts +174 -0
  182. package/src/__tests__/runtime-attachment-metadata.test.ts +15 -7
  183. package/src/__tests__/scaffold-managed-skill-tool.test.ts +166 -0
  184. package/src/__tests__/schedule-routes.test.ts +730 -8
  185. package/src/__tests__/schedule-store.test.ts +540 -0
  186. package/src/__tests__/schedule-tools.test.ts +103 -0
  187. package/src/__tests__/scheduler-wake.test.ts +5 -0
  188. package/src/__tests__/secret-routes-platform-proxy.test.ts +29 -1
  189. package/src/__tests__/secure-keys.test.ts +91 -0
  190. package/src/__tests__/server-history-render.test.ts +97 -7
  191. package/src/__tests__/size-guard.test.ts +8 -8
  192. package/src/__tests__/skill-docs-sync-guard.test.ts +3 -2
  193. package/src/__tests__/skill-tool-factory.test.ts +247 -0
  194. package/src/__tests__/skills-install-staging.test.ts +15 -6
  195. package/src/__tests__/slack-edit-ordering-characterization.test.ts +103 -0
  196. package/src/__tests__/slack-inbound-verification.test.ts +4 -1
  197. package/src/__tests__/slack-mention-binding-invariance.test.ts +103 -0
  198. package/src/__tests__/slack-mention-provider-leak-audit.test.ts +135 -0
  199. package/src/__tests__/starter-task-flow.test.ts +5 -4
  200. package/src/__tests__/steer-on-enqueue-question.test.ts +3 -2
  201. package/src/__tests__/stt-language-catalog-parity.test.ts +38 -0
  202. package/src/__tests__/subagent-allowlist-validation.test.ts +26 -4
  203. package/src/__tests__/subagent-cron-run-attribution.test.ts +301 -0
  204. package/src/__tests__/subagent-disposal.test.ts +10 -0
  205. package/src/__tests__/subagent-fork-notifications.test.ts +10 -0
  206. package/src/__tests__/subagent-fork-prompt-role.test.ts +85 -11
  207. package/src/__tests__/subagent-fork-spawn.test.ts +15 -7
  208. package/src/__tests__/subagent-manager-notify.test.ts +148 -0
  209. package/src/__tests__/subagent-notify-parent.test.ts +1 -1
  210. package/src/__tests__/subagent-persistence.test.ts +141 -0
  211. package/src/__tests__/subagent-role-registry.test.ts +172 -63
  212. package/src/__tests__/subagent-role-resolution.test.ts +88 -0
  213. package/src/__tests__/subagent-spawn-and-await.test.ts +48 -0
  214. package/src/__tests__/subagent-spawn-tool-fork.test.ts +111 -10
  215. package/src/__tests__/subagent-terminal-message.test.ts +88 -1
  216. package/src/__tests__/subagent-tool-gate-mode.test.ts +225 -10
  217. package/src/__tests__/subagent-tools.test.ts +1901 -108
  218. package/src/__tests__/surface-completion-compaction-boundary.test.ts +124 -0
  219. package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +298 -0
  220. package/src/__tests__/system-prompt.test.ts +82 -7
  221. package/src/__tests__/tool-grant-request-escalation.test.ts +40 -1
  222. package/src/__tests__/tool-result-spool.test.ts +74 -18
  223. package/src/__tests__/tool-side-effects-documents.test.ts +174 -0
  224. package/src/__tests__/tool-side-effects-hook-failures.test.ts +129 -0
  225. package/src/__tests__/tools-audio-read.test.ts +10 -10
  226. package/src/__tests__/tools-get-route.test.ts +85 -12
  227. package/src/__tests__/tools-list-cli.test.ts +171 -0
  228. package/src/__tests__/top-level-renderer.test.ts +54 -1
  229. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +68 -1
  230. package/src/__tests__/trusted-contact-multichannel.test.ts +4 -1
  231. package/src/__tests__/turn-tail-chain.test.ts +135 -0
  232. package/src/__tests__/ui-choice-copy-surfaces.test.ts +6 -4
  233. package/src/__tests__/ui-visual-surface.test.ts +5 -4
  234. package/src/__tests__/ui-work-result-surface.test.ts +5 -4
  235. package/src/__tests__/unified-turn-context-visible-app.test.ts +7 -8
  236. package/src/__tests__/usage-routes.test.ts +62 -0
  237. package/src/__tests__/verification-outbound-delivery.test.ts +176 -0
  238. package/src/__tests__/visible-app-context.test.ts +20 -4
  239. package/src/__tests__/web-search-history.test.ts +96 -0
  240. package/src/__tests__/workspace-migration-139-clear-renamed-cost-profile-label.test.ts +6 -6
  241. package/src/__tests__/workspace-migration-140-repair-seed-pinned-memory-v3-live.test.ts +330 -0
  242. package/src/acp/failure-error.ts +3 -5
  243. package/src/agent/attachments.ts +26 -24
  244. package/src/agent/history-repair/history-repair.ts +27 -26
  245. package/src/agent/image-optimize.ts +6 -6
  246. package/src/agent/loop.ts +40 -22
  247. package/src/agent/message-types.ts +3 -3
  248. package/src/api/constants/document-tools.ts +55 -0
  249. package/src/api/events/question-answered.ts +53 -0
  250. package/src/api/events/tool-result.ts +7 -0
  251. package/src/api/index.ts +13 -0
  252. package/src/api/responses/conversation-message.ts +11 -0
  253. package/src/api/surfaces-oauth-connect.test.ts +96 -0
  254. package/src/api/surfaces.ts +36 -8
  255. package/src/approvals/guardian-channel-delivery.ts +83 -0
  256. package/src/approvals/guardian-expiry-notifier.ts +13 -11
  257. package/src/approvals/guardian-request-resolvers.ts +222 -135
  258. package/src/calls/__tests__/voice-session-bridge.test.ts +50 -0
  259. package/src/calls/media-stream-server.ts +9 -1
  260. package/src/calls/voice-session-bridge.ts +38 -0
  261. package/src/channels/__tests__/gateway-verification-sessions.test.ts +3 -3
  262. package/src/channels/config.ts +7 -2
  263. package/src/channels/gateway-verification-sessions.ts +7 -2
  264. package/src/channels/types.ts +7 -1
  265. package/src/cli/commands/__tests__/cache.test.ts +7 -4
  266. package/src/cli/commands/__tests__/cli-test-harness.ts +82 -0
  267. package/src/cli/commands/__tests__/conversations-wake.test.ts +51 -6
  268. package/src/cli/commands/__tests__/inference-models.test.ts +14 -0
  269. package/src/cli/commands/__tests__/inference-profiles.test.ts +44 -1
  270. package/src/cli/commands/__tests__/inference-providers.test.ts +7 -0
  271. package/src/cli/commands/__tests__/plugins.test.ts +692 -0
  272. package/src/cli/commands/__tests__/schedules.test.ts +136 -37
  273. package/src/cli/commands/conversations.ts +7 -3
  274. package/src/cli/commands/credentials.help.ts +7 -0
  275. package/src/cli/commands/inference-profiles.ts +9 -3
  276. package/src/cli/commands/inference.help.ts +16 -2
  277. package/src/cli/commands/mcp.help.ts +4 -2
  278. package/src/cli/commands/memory/memory-retrospective.ts +18 -0
  279. package/src/cli/commands/notifications.ts +3 -6
  280. package/src/cli/commands/oauth/connect-surface-guidance.test.ts +16 -0
  281. package/src/cli/commands/oauth/connect-surface-guidance.ts +25 -7
  282. package/src/cli/commands/oauth/connect.test.ts +26 -1
  283. package/src/cli/commands/oauth/connect.ts +4 -1
  284. package/src/cli/commands/oauth/index.help.ts +3 -1
  285. package/src/cli/commands/oauth/providers.ts +3 -0
  286. package/src/cli/commands/plugins.help.ts +25 -2
  287. package/src/cli/commands/plugins.ts +289 -10
  288. package/src/cli/commands/schedules.help.ts +14 -12
  289. package/src/cli/commands/schedules.ts +19 -5
  290. package/src/cli/commands/tools.help.ts +4 -2
  291. package/src/cli/commands/tools.ts +38 -0
  292. package/src/cli/lib/__tests__/inspect-plugin.test.ts +1 -0
  293. package/src/cli/lib/__tests__/install-from-github.test.ts +79 -0
  294. package/src/cli/lib/__tests__/install-from-platform.test.ts +47 -1
  295. package/src/cli/lib/__tests__/plugin-surfaces.test.ts +86 -7
  296. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +122 -0
  297. package/src/cli/lib/install-from-github.ts +61 -0
  298. package/src/cli/lib/install-from-platform.ts +13 -2
  299. package/src/cli/lib/plugin-surfaces.ts +124 -4
  300. package/src/cli/lib/upgrade-plugin.ts +18 -0
  301. package/src/cli.ts +1 -1
  302. package/src/config/__tests__/deployment-context-defaults.test.ts +73 -0
  303. package/src/config/__tests__/memory-retrospective-schema.test.ts +40 -0
  304. package/src/config/__tests__/plugin-updates-schema.test.ts +43 -0
  305. package/src/config/__tests__/profile-tool-support.test.ts +131 -0
  306. package/src/config/bundled-skills/AGENTS.md +1 -1
  307. package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
  308. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +3 -3
  309. package/src/config/bundled-skills/schedule/SKILL.md +5 -1
  310. package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
  311. package/src/config/bundled-skills/settings/TOOLS.json +2 -2
  312. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +1 -0
  313. package/src/config/bundled-skills/subagent/SKILL.md +53 -22
  314. package/src/config/bundled-skills/subagent/TOOLS.json +14 -13
  315. package/src/config/bundled-skills/visualize/SKILL.md +1 -1
  316. package/src/config/default-profile-catalog.ts +6 -2
  317. package/src/config/feature-flag-registry.json +25 -17
  318. package/src/config/llm-resolver.ts +45 -15
  319. package/src/config/loader.ts +70 -1
  320. package/src/config/profile-tool-support.ts +110 -0
  321. package/src/config/sanitize-for-transfer.ts +11 -0
  322. package/src/config/schema.ts +4 -0
  323. package/src/config/schemas/__tests__/stt.test.ts +20 -5
  324. package/src/config/schemas/channels.ts +13 -0
  325. package/src/config/schemas/memory-retrospective.ts +25 -0
  326. package/src/config/schemas/plugin-updates.ts +55 -0
  327. package/src/config/schemas/services.ts +8 -4
  328. package/src/config/schemas/stt.ts +17 -5
  329. package/src/config/schemas/timeouts.ts +1 -1
  330. package/src/contacts/__tests__/guardian-delivery-reader.test.ts +16 -0
  331. package/src/contacts/guardian-delivery-reader.ts +29 -2
  332. package/src/context/compactor.ts +4 -4
  333. package/src/context/post-turn-tool-result-truncation.ts +32 -16
  334. package/src/context/strip-injections.ts +42 -0
  335. package/src/context/tool-result-spool.ts +32 -18
  336. package/src/credential-execution/ces-runtime.ts +3 -0
  337. package/src/credential-execution/client.ts +22 -1
  338. package/src/credential-execution/process-manager.test.ts +99 -0
  339. package/src/credential-execution/process-manager.ts +62 -16
  340. package/src/daemon/__tests__/abort-null-controller.test.ts +141 -5
  341. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +8 -12
  342. package/src/daemon/__tests__/install-assistant-command.test.ts +234 -0
  343. package/src/daemon/__tests__/turn-tail-assistant-reply-notify.test.ts +2 -4
  344. package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +20 -29
  345. package/src/daemon/channel-ui-capability.ts +4 -4
  346. package/src/daemon/conversation-agent-loop-handlers.ts +117 -61
  347. package/src/daemon/conversation-agent-loop.ts +318 -148
  348. package/src/daemon/conversation-attachments.ts +1 -1
  349. package/src/daemon/conversation-error.ts +8 -1
  350. package/src/daemon/conversation-initial-prompt.ts +16 -38
  351. package/src/daemon/conversation-lifecycle.ts +154 -32
  352. package/src/daemon/conversation-messaging.ts +106 -43
  353. package/src/daemon/conversation-process.ts +131 -33
  354. package/src/daemon/conversation-queue-manager.ts +10 -0
  355. package/src/daemon/conversation-runtime-assembly.ts +9 -4
  356. package/src/daemon/conversation-store.ts +52 -3
  357. package/src/daemon/conversation-surface-state.ts +9 -0
  358. package/src/daemon/conversation-surfaces.ts +403 -210
  359. package/src/daemon/conversation-tool-setup.ts +56 -7
  360. package/src/daemon/conversation-turn-finalize.ts +134 -89
  361. package/src/daemon/conversation.ts +111 -27
  362. package/src/daemon/disk-pressure-guard-lifecycle.ts +2 -2
  363. package/src/daemon/disk-pressure-guard.ts +3 -3
  364. package/src/daemon/doordash-steps.ts +5 -5
  365. package/src/daemon/external-plugins-bootstrap.ts +7 -1
  366. package/src/daemon/handlers/config-channels.ts +48 -5
  367. package/src/daemon/handlers/conversation-history.ts +1 -1
  368. package/src/daemon/handlers/conversations.ts +62 -17
  369. package/src/daemon/handlers/shared.ts +12 -0
  370. package/src/daemon/handlers/skills.ts +11 -1
  371. package/src/daemon/identity-helpers.ts +38 -10
  372. package/src/daemon/install-assistant-command.ts +322 -0
  373. package/src/daemon/lifecycle.ts +14 -3
  374. package/src/daemon/message-types/conversations.ts +6 -7
  375. package/src/daemon/message-types/sync.ts +1 -0
  376. package/src/daemon/persist-media-references.ts +23 -14
  377. package/src/daemon/process-message.ts +6 -6
  378. package/src/daemon/tool-setup-types.ts +19 -122
  379. package/src/daemon/tool-side-effects.ts +17 -94
  380. package/src/daemon/trust-context-types.ts +39 -0
  381. package/src/daemon/trust-context.ts +14 -6
  382. package/src/daemon/turn-tail-chain.ts +73 -0
  383. package/src/daemon/web-search-history.ts +47 -33
  384. package/src/heartbeat/heartbeat-service.ts +6 -6
  385. package/src/ipc/__tests__/socket-path.test.ts +21 -0
  386. package/src/ipc/assistant-server.ts +17 -10
  387. package/src/ipc/cli-client.ts +10 -1
  388. package/src/ipc/routes/__tests__/documents-sync-ipc-routes.test.ts +56 -0
  389. package/src/ipc/routes/documents-sync-ipc-routes.ts +42 -0
  390. package/src/ipc/socket-cleanup.ts +9 -7
  391. package/src/ipc/socket-path.ts +4 -96
  392. package/src/live-voice/__tests__/live-voice-archive.test.ts +16 -16
  393. package/src/live-voice/__tests__/live-voice-attach-image.test.ts +209 -0
  394. package/src/live-voice/__tests__/live-voice-connection.test.ts +20 -0
  395. package/src/live-voice/__tests__/live-voice-integration.test.ts +7 -5
  396. package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +265 -0
  397. package/src/live-voice/__tests__/live-voice-stt.test.ts +6 -1
  398. package/src/live-voice/__tests__/live-voice-vad.test.ts +35 -15
  399. package/src/live-voice/__tests__/protocol.test.ts +52 -0
  400. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +3 -0
  401. package/src/live-voice/live-voice-archive.ts +13 -13
  402. package/src/live-voice/live-voice-connection.ts +7 -1
  403. package/src/live-voice/live-voice-photo.ts +169 -0
  404. package/src/live-voice/live-voice-session.ts +108 -4
  405. package/src/live-voice/protocol.ts +89 -1
  406. package/src/mcp/client.ts +10 -0
  407. package/src/messaging/providers/__tests__/callback-routing.test.ts +5 -1
  408. package/src/messaging/providers/__tests__/transport-dispatch.test.ts +66 -4
  409. package/src/messaging/providers/callback-routing.ts +1 -0
  410. package/src/messaging/providers/discord/api.ts +312 -0
  411. package/src/messaging/providers/discord/dm-delivery.test.ts +204 -0
  412. package/src/messaging/providers/discord/render.test.ts +204 -0
  413. package/src/messaging/providers/discord/render.ts +195 -0
  414. package/src/messaging/providers/discord/send.test.ts +179 -0
  415. package/src/messaging/providers/discord/send.ts +212 -0
  416. package/src/messaging/providers/discord/transport.ts +81 -0
  417. package/src/messaging/providers/index.ts +3 -1
  418. package/src/messaging/providers/retry-policy.test.ts +247 -0
  419. package/src/messaging/providers/retry-policy.ts +206 -0
  420. package/src/messaging/providers/slack/mention-source.test.ts +285 -0
  421. package/src/messaging/providers/slack/mention-source.ts +287 -0
  422. package/src/messaging/providers/telegram-bot/api.ts +47 -130
  423. package/src/messaging/providers/whatsapp/api.ts +47 -136
  424. package/src/monitoring/__tests__/db-integrity-sample.test.ts +4 -1
  425. package/src/monitoring/__tests__/plugin-auto-update.test.ts +332 -0
  426. package/src/monitoring/db-integrity-sample.ts +6 -1
  427. package/src/monitoring/plugin-auto-update.ts +359 -0
  428. package/src/monitoring/resource-sampler.ts +21 -5
  429. package/src/monitoring/worker.ts +13 -0
  430. package/src/notifications/__tests__/assistant-reply-producer.test.ts +375 -2
  431. package/src/notifications/__tests__/broadcaster.test.ts +37 -1
  432. package/src/notifications/__tests__/copy-composer.test.ts +165 -0
  433. package/src/notifications/__tests__/emit-signal-pipeline-failure.test.ts +205 -0
  434. package/src/notifications/__tests__/notification-utils.test.ts +249 -0
  435. package/src/notifications/adapters/platform.ts +31 -2
  436. package/src/notifications/assistant-reply-producer.ts +79 -32
  437. package/src/notifications/broadcaster.ts +30 -10
  438. package/src/notifications/copy-composer.ts +69 -0
  439. package/src/notifications/emit-signal.ts +82 -15
  440. package/src/notifications/events-store.ts +10 -2
  441. package/src/notifications/home-feed-side-effect.ts +6 -3
  442. package/src/notifications/notification-utils.ts +173 -5
  443. package/src/notifications/signal.ts +12 -0
  444. package/src/oauth/AGENTS.md +4 -2
  445. package/src/oauth/byo-connection.ts +6 -1
  446. package/src/oauth/credential-token-resolver.ts +3 -10
  447. package/src/oauth/manual-token-connection.ts +29 -64
  448. package/src/oauth/manual-token-providers.ts +62 -0
  449. package/src/oauth/provider-serializer.ts +13 -0
  450. package/src/oauth/seed-providers.ts +62 -11
  451. package/src/onboarding/onboarding-events-store.ts +91 -1
  452. package/src/permissions/question-prompter.test.ts +4 -0
  453. package/src/permissions/question-prompter.ts +21 -4
  454. package/src/persistence/__tests__/conversation-lineage.test.ts +248 -0
  455. package/src/persistence/attachments-store.ts +15 -15
  456. package/src/persistence/conversation-attention-store.ts +30 -10
  457. package/src/persistence/conversation-bootstrap.ts +17 -0
  458. package/src/persistence/conversation-crud.ts +363 -97
  459. package/src/persistence/conversation-group-migration.ts +1 -1
  460. package/src/persistence/conversation-key-store.ts +38 -10
  461. package/src/persistence/conversation-lineage.ts +226 -0
  462. package/src/persistence/conversation-plugin-facade.ts +28 -3
  463. package/src/persistence/conversation-queries.ts +207 -33
  464. package/src/persistence/conversation-types.ts +107 -1
  465. package/src/persistence/jobs-store.ts +32 -8
  466. package/src/persistence/llm-usage-store.ts +22 -0
  467. package/src/persistence/migrations/361-normalize-managed-connection-rows.test.ts +168 -0
  468. package/src/persistence/migrations/361-normalize-managed-connection-rows.ts +76 -0
  469. package/src/persistence/migrations/362-add-conversation-subagent-kind.test.ts +187 -0
  470. package/src/persistence/migrations/362-add-conversation-subagent-kind.ts +93 -0
  471. package/src/persistence/migrations/363-backfill-schedule-inference-profile.test.ts +229 -0
  472. package/src/persistence/migrations/363-backfill-schedule-inference-profile.ts +127 -0
  473. package/src/persistence/migrations/364-add-schedule-source-key.test.ts +120 -0
  474. package/src/persistence/migrations/364-add-schedule-source-key.ts +43 -0
  475. package/src/persistence/migrations/365-add-conversation-fork-strategy.ts +27 -0
  476. package/src/persistence/schema/conversations.ts +32 -0
  477. package/src/persistence/schema/infrastructure.ts +3 -0
  478. package/src/persistence/steps.ts +30 -0
  479. package/src/persistence/subagent-store.ts +148 -6
  480. package/src/platform/managed-speech.test.ts +20 -0
  481. package/src/platform/managed-speech.ts +9 -0
  482. package/src/plugin-api/index.ts +8 -0
  483. package/src/plugin-api/webhook-url.ts +151 -0
  484. package/src/plugins/__tests__/installed-plugin-dirs.test.ts +82 -0
  485. package/src/plugins/__tests__/source-fingerprint.test.ts +5 -1
  486. package/src/plugins/collect-source-versions.ts +4 -27
  487. package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +5 -5
  488. package/src/plugins/defaults/exploration-drift/package.json +1 -1
  489. package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +4 -2
  490. package/src/plugins/defaults/image-recovery/recover.ts +67 -57
  491. package/src/plugins/defaults/index.ts +1 -1
  492. package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +64 -18
  493. package/src/plugins/defaults/memory/__tests__/jobs-worker-outcome-contract.test.ts +242 -0
  494. package/src/plugins/defaults/memory/__tests__/jobs-worker-retrospective-sweep.test.ts +24 -1
  495. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +48 -3
  496. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +511 -19
  497. package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +39 -3
  498. package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +493 -0
  499. package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +5 -4
  500. package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +5 -1
  501. package/src/plugins/defaults/memory/job-handlers.ts +87 -5
  502. package/src/plugins/defaults/memory/jobs-worker.ts +60 -6
  503. package/src/plugins/defaults/memory/memory-retrospective-constants.ts +11 -0
  504. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +29 -0
  505. package/src/plugins/defaults/memory/memory-retrospective-job.ts +342 -60
  506. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +23 -3
  507. package/src/plugins/defaults/memory/substrate/consolidation-lock.ts +9 -4
  508. package/src/plugins/defaults/memory/substrate/sweep-job.ts +7 -0
  509. package/src/plugins/defaults/memory/v1/graph/injection.ts +1 -1
  510. package/src/plugins/defaults/memory/v2/router.ts +8 -0
  511. package/src/plugins/defaults/turn-context/unified-turn-context.ts +1 -2
  512. package/src/plugins/external-plugin-loader.ts +17 -8
  513. package/src/plugins/installed-plugin-dirs.ts +101 -0
  514. package/src/plugins/mtime-cache.ts +34 -22
  515. package/src/plugins/types.ts +69 -8
  516. package/src/prompts/persona-resolver.ts +6 -2
  517. package/src/prompts/system-prompt.ts +72 -17
  518. package/src/providers/__tests__/inference.test.ts +40 -0
  519. package/src/providers/__tests__/preflight-resolved-config.test.ts +58 -1
  520. package/src/providers/__tests__/retry-callsite.test.ts +67 -0
  521. package/src/providers/__tests__/vellum-connection-routing.test.ts +42 -11
  522. package/src/providers/anthropic/client.ts +112 -95
  523. package/src/providers/connection-resolution.ts +35 -1
  524. package/src/providers/gemini/client.ts +5 -5
  525. package/src/providers/inference/adapter-factory.ts +16 -10
  526. package/src/providers/inference/auth.ts +16 -0
  527. package/src/providers/inference/connection-availability.ts +164 -12
  528. package/src/providers/inference/connections.ts +25 -1
  529. package/src/providers/media-resolve.ts +21 -11
  530. package/src/providers/model-catalog.ts +15 -0
  531. package/src/providers/openai/chat-completions-provider.ts +4 -4
  532. package/src/providers/openai/responses-provider.ts +12 -25
  533. package/src/providers/registry.ts +9 -4
  534. package/src/providers/retry.ts +25 -0
  535. package/src/providers/server-tool-pairing.ts +134 -0
  536. package/src/providers/speech-to-text/__tests__/resolve.test.ts +187 -6
  537. package/src/providers/speech-to-text/deepgram.ts +1 -0
  538. package/src/providers/speech-to-text/resolve.ts +80 -10
  539. package/src/providers/speech-to-text/vellum-managed.ts +7 -1
  540. package/src/providers/speech-to-text/vellum-speech-relay-connection.ts +3 -1
  541. package/src/providers/types.ts +26 -6
  542. package/src/providers/vellum-model-routing.ts +6 -7
  543. package/src/runtime/AGENTS.md +2 -0
  544. package/src/runtime/__tests__/agent-wake.test.ts +16 -16
  545. package/src/runtime/__tests__/background-job-runner.test.ts +0 -144
  546. package/src/runtime/__tests__/desktop-presence.test.ts +162 -0
  547. package/src/runtime/agent-wake.ts +63 -24
  548. package/src/runtime/assistant-event-hub.ts +87 -12
  549. package/src/runtime/auth/__tests__/guard-tests.test.ts +2 -0
  550. package/src/runtime/auth/__tests__/scopes.test.ts +20 -0
  551. package/src/runtime/auth/same-actor.ts +56 -8
  552. package/src/runtime/auth/scopes.ts +12 -0
  553. package/src/runtime/auth/types.ts +2 -0
  554. package/src/runtime/background-job-runner.ts +5 -27
  555. package/src/runtime/channel-readiness-service.ts +59 -3
  556. package/src/runtime/channel-readiness-types.ts +18 -0
  557. package/src/runtime/desktop-presence.ts +69 -0
  558. package/src/runtime/http-server.ts +12 -0
  559. package/src/runtime/local-actor-identity.ts +11 -9
  560. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
  561. package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
  562. package/src/runtime/routes/__tests__/client-routes.test.ts +186 -14
  563. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +547 -1
  564. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +162 -7
  565. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +14 -10
  566. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +340 -3
  567. package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +28 -1
  568. package/src/runtime/routes/__tests__/plugins-routes.test.ts +173 -1
  569. package/src/runtime/routes/__tests__/question-routes.test.ts +64 -0
  570. package/src/runtime/routes/__tests__/retrospective-routes.test.ts +20 -0
  571. package/src/runtime/routes/__tests__/skill-history-route.test.ts +116 -0
  572. package/src/runtime/routes/__tests__/wake-conversation-routes.test.ts +60 -2
  573. package/src/runtime/routes/archive-utils.ts +16 -12
  574. package/src/runtime/routes/attachment-routes.ts +13 -6
  575. package/src/runtime/routes/canned-reply-release.ts +54 -0
  576. package/src/runtime/routes/channel-verification-routes.ts +2 -27
  577. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +5 -0
  578. package/src/runtime/routes/client-routes.ts +114 -2
  579. package/src/runtime/routes/conversation-cli-routes.ts +10 -12
  580. package/src/runtime/routes/conversation-list-routes.ts +59 -10
  581. package/src/runtime/routes/conversation-management-routes.ts +47 -18
  582. package/src/runtime/routes/conversation-query-routes.ts +32 -53
  583. package/src/runtime/routes/conversation-routes.ts +360 -293
  584. package/src/runtime/routes/credential-prompt-routes.ts +23 -2
  585. package/src/runtime/routes/debug-routes.ts +4 -1
  586. package/src/runtime/routes/default-provider-routes.ts +7 -11
  587. package/src/runtime/routes/documents-routes.ts +44 -1
  588. package/src/runtime/routes/events-routes.ts +28 -1
  589. package/src/runtime/routes/host-app-control-routes.ts +2 -0
  590. package/src/runtime/routes/host-bash-routes.ts +2 -0
  591. package/src/runtime/routes/host-browser-routes.ts +2 -0
  592. package/src/runtime/routes/host-cu-routes.ts +2 -0
  593. package/src/runtime/routes/host-file-routes.ts +2 -0
  594. package/src/runtime/routes/host-transfer-routes.ts +4 -0
  595. package/src/runtime/routes/identity-routes.ts +6 -6
  596. package/src/runtime/routes/inbound-message-handler.ts +5 -5
  597. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +7 -3
  598. package/src/runtime/routes/inbound-stages/background-dispatch.ts +14 -0
  599. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -2
  600. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +14 -11
  601. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -17
  602. package/src/runtime/routes/inference-profile-availability-guard.ts +171 -0
  603. package/src/runtime/routes/inference-profile-session-handler.ts +26 -2
  604. package/src/runtime/routes/inference-profile-session-routes.ts +2 -1
  605. package/src/runtime/routes/inference-profiles-routes.ts +152 -73
  606. package/src/runtime/routes/inference-provider-connection-routes.ts +0 -16
  607. package/src/runtime/routes/llm-call-sites-routes.ts +13 -0
  608. package/src/runtime/routes/log-export-routes.ts +1 -1
  609. package/src/runtime/routes/notification-routes.ts +0 -14
  610. package/src/runtime/routes/oauth-providers.ts +1 -0
  611. package/src/runtime/routes/plugins-routes.ts +106 -1
  612. package/src/runtime/routes/profiler-routes.ts +4 -2
  613. package/src/runtime/routes/question-routes.ts +28 -10
  614. package/src/runtime/routes/retrospective-routes.ts +9 -3
  615. package/src/runtime/routes/schedule-routes.ts +210 -12
  616. package/src/runtime/routes/secret-routes.ts +40 -23
  617. package/src/runtime/routes/settings-routes.ts +41 -13
  618. package/src/runtime/routes/skills-routes.ts +79 -0
  619. package/src/runtime/routes/ui-snapshot-routes.ts +1 -0
  620. package/src/runtime/routes/wake-conversation-routes.ts +23 -0
  621. package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
  622. package/src/runtime/services/conversation-serializer.ts +24 -9
  623. package/src/runtime/sync/documents-sidecar-publish.test.ts +101 -0
  624. package/src/runtime/sync/resource-sync-events.ts +79 -1
  625. package/src/runtime/sync/worker-daemon-notify.test.ts +34 -0
  626. package/src/runtime/sync/worker-daemon-notify.ts +44 -6
  627. package/src/runtime/verification-outbound-actions.ts +250 -222
  628. package/src/runtime/verification-templates.ts +41 -30
  629. package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +614 -0
  630. package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1066 -0
  631. package/src/schedule/inference-profile.ts +64 -3
  632. package/src/schedule/plugin-schedule-declarations.ts +518 -0
  633. package/src/schedule/plugin-schedule-reconciler.ts +605 -0
  634. package/src/schedule/plugin-schedules-gate.ts +23 -0
  635. package/src/schedule/schedule-store.ts +520 -26
  636. package/src/schedule/scheduler.ts +38 -0
  637. package/src/security/credential-backend.ts +51 -4
  638. package/src/security/encrypted-store.ts +64 -42
  639. package/src/security/secure-keys.ts +55 -7
  640. package/src/skills/catalog-install.ts +20 -7
  641. package/src/skills/inline-command-runner.ts +2 -7
  642. package/src/skills/skill-history.test.ts +269 -0
  643. package/src/skills/skill-history.ts +250 -0
  644. package/src/skills/skillssh-registry.ts +1 -1
  645. package/src/stt/daemon-batch-transcriber.ts +8 -6
  646. package/src/subagent/__tests__/consult-prompt.test.ts +25 -0
  647. package/src/subagent/consult-prompt.ts +3 -1
  648. package/src/subagent/consult-transcript.ts +1 -1
  649. package/src/subagent/manager.ts +304 -44
  650. package/src/subagent/progress-events.ts +35 -0
  651. package/src/subagent/role-resolution.ts +88 -0
  652. package/src/subagent/types.ts +302 -67
  653. package/src/subagent/validate-allowlists.ts +3 -4
  654. package/src/telegram/__tests__/webhook-health.test.ts +73 -1
  655. package/src/telegram/webhook-health.ts +101 -3
  656. package/src/telemetry/__tests__/telemetry-event-fixtures.ts +2 -0
  657. package/src/telemetry/activation-funnel.ts +6 -1
  658. package/src/telemetry/live-voice-funnel.ts +75 -0
  659. package/src/telemetry/telemetry-event-sources.ts +6 -0
  660. package/src/telemetry/telemetry-wire-source.json +1 -1
  661. package/src/telemetry/telemetry-wire.generated.ts +2 -0
  662. package/src/telemetry/types.ts +23 -4
  663. package/src/telemetry/usage-telemetry-reporter.test.ts +4 -0
  664. package/src/tools/__tests__/subagent-workflow-tool-input-schemas.test.ts +1 -1
  665. package/src/tools/ask-question/ask-question-tool.test.ts +118 -3
  666. package/src/tools/ask-question/ask-question-tool.ts +56 -2
  667. package/src/tools/browser/browser-execution.ts +1 -1
  668. package/src/tools/calls/call-start.ts +8 -5
  669. package/src/tools/conversation-groups/move_to_group.ts +10 -12
  670. package/src/tools/document/document-tool.ts +11 -4
  671. package/src/tools/execution-timeout.ts +9 -2
  672. package/src/tools/executor.ts +7 -6
  673. package/src/tools/filesystem/edit.ts +1 -1
  674. package/src/tools/filesystem/list.ts +1 -1
  675. package/src/tools/filesystem/read.ts +7 -13
  676. package/src/tools/filesystem/search.ts +23 -21
  677. package/src/tools/filesystem/write.ts +4 -1
  678. package/src/tools/host-filesystem/edit.ts +7 -2
  679. package/src/tools/host-filesystem/read.ts +21 -8
  680. package/src/tools/host-filesystem/write.ts +10 -2
  681. package/src/tools/host-terminal/host-shell.ts +2 -2
  682. package/src/tools/mcp/mcp-tool-factory.ts +25 -1
  683. package/src/tools/permission-checker.ts +8 -3
  684. package/src/tools/schedule/delete.ts +13 -1
  685. package/src/tools/schedule/list.ts +14 -3
  686. package/src/tools/schedule/update.ts +56 -33
  687. package/src/tools/shared/filesystem/audio-read.ts +6 -4
  688. package/src/tools/shared/filesystem/file-ops-service.ts +176 -116
  689. package/src/tools/shared/filesystem/image-read.ts +12 -12
  690. package/src/tools/shared/filesystem/path-policy.ts +70 -5
  691. package/src/tools/shared/filesystem/size-guard.ts +6 -6
  692. package/src/tools/shared/input-misuse.ts +72 -0
  693. package/src/tools/skills/scaffold-managed.ts +82 -0
  694. package/src/tools/skills/skill-tool-factory.ts +14 -1
  695. package/src/tools/subagent/consult-deadline.ts +12 -7
  696. package/src/tools/subagent/message.ts +3 -1
  697. package/src/tools/subagent/read.ts +66 -4
  698. package/src/tools/subagent/spawn.ts +500 -78
  699. package/src/tools/tool-approval-handler.ts +45 -4
  700. package/src/tools/types.ts +22 -3
  701. package/src/tools/ui-surface/surface-shape-docs.ts +1 -1
  702. package/src/tools/ui-surface/visual-validation.ts +2 -0
  703. package/src/usage/__tests__/subagent-attribution.test.ts +109 -0
  704. package/src/usage/subagent-attribution.ts +108 -0
  705. package/src/util/__tests__/sqlite-retry.test.ts +112 -0
  706. package/src/util/abort-reasons.ts +16 -0
  707. package/src/util/ansi.ts +44 -0
  708. package/src/util/disk-usage.ts +17 -13
  709. package/src/util/image-conversion.ts +20 -13
  710. package/src/util/schedule-source-key.ts +23 -0
  711. package/src/util/worker-process.ts +7 -1
  712. package/src/workspace/git-service.ts +33 -9
  713. package/src/workspace/migrations/140-repair-seed-pinned-memory-v3-live.ts +260 -0
  714. package/src/workspace/migrations/141-stt-english-default-to-multilingual.ts +111 -0
  715. package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +126 -0
  716. package/src/workspace/migrations/registry.ts +4 -0
  717. package/src/workspace/top-level-renderer.ts +15 -2
  718. package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -357
  719. package/src/daemon/install-symlink.ts +0 -207
  720. package/src/notifications/deferred-emit.ts +0 -147
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Register and tear down client subscribers on an assistant event hub.
3
+ *
4
+ * The caller supplies the hub, so this helper holds no runtime import into
5
+ * `src/` and stays safe for the test preload's import graph.
6
+ */
7
+ import type { HostProxyCapability, InterfaceId } from "../../channels/types.js";
8
+ import type {
9
+ AssistantEventHub,
10
+ DesktopPresenceState,
11
+ } from "../../runtime/assistant-event-hub.js";
12
+
13
+ export interface RegisterHubClientArgs {
14
+ hub: AssistantEventHub;
15
+ clientId: string;
16
+ interfaceId?: InterfaceId;
17
+ capabilities?: HostProxyCapability[];
18
+ actorPrincipalId?: string;
19
+ /** Reported immediately after subscribing, when supplied. */
20
+ presence?: DesktopPresenceState;
21
+ }
22
+
23
+ export function registerHubClient(args: RegisterHubClientArgs): void {
24
+ const { hub } = args;
25
+ hub.subscribe({
26
+ type: "client",
27
+ clientId: args.clientId,
28
+ interfaceId: args.interfaceId ?? "macos",
29
+ capabilities: args.capabilities ?? [],
30
+ actorPrincipalId: args.actorPrincipalId,
31
+ callback: () => {},
32
+ });
33
+ if (args.presence) {
34
+ hub.setClientPresence(args.clientId, args.presence);
35
+ }
36
+ }
37
+
38
+ export function clearHubClients(hub: AssistantEventHub): void {
39
+ for (const client of hub.listClients()) {
40
+ hub.disposeClient(client.clientId);
41
+ }
42
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Build a stand-in `Conversation` for tests that exercise functions taking the
3
+ * live conversation (surface helpers, the tool executor, post-execution
4
+ * hooks).
5
+ *
6
+ * Those functions read a handful of fields off a `Conversation` that has ~150
7
+ * members, so a test double supplies only what the function under test
8
+ * touches. `Partial<Conversation>` keeps the literal contextually typed —
9
+ * field types and callback parameter types are still checked against the real
10
+ * class, and a renamed or retyped member still breaks the build — while the
11
+ * single assertion here confines the unavoidable widening to one place
12
+ * instead of one `as unknown as` per test file.
13
+ *
14
+ * The `Conversation` import is type-only, so this helper contributes nothing
15
+ * to the runtime import graph.
16
+ */
17
+
18
+ import type { Conversation } from "../../daemon/conversation.js";
19
+ import type { ChannelCapabilities } from "../../daemon/conversation-runtime-assembly.js";
20
+
21
+ /**
22
+ * `TExtra` carries test-only fields a double hangs off the same object
23
+ * (recorded calls, captured messages) through to the return type, so a caller
24
+ * can still read `ctx.processMessageCalls` off the result.
25
+ */
26
+ export function asConversation<TExtra extends object = object>(
27
+ mock: Partial<Conversation> & TExtra,
28
+ ): Conversation & TExtra {
29
+ return mock as unknown as Conversation & TExtra;
30
+ }
31
+
32
+ /**
33
+ * Fill a partial capability set out to a full `ChannelCapabilities`.
34
+ *
35
+ * Surface tests care about `channel` and `supportsDynamicUi`; the remaining
36
+ * required fields are noise at the call site but must be present for the
37
+ * value to be a real `ChannelCapabilities`. Defaults are the conservative
38
+ * ones (no dashboard, no voice), so a test that forgets to opt in gets the
39
+ * restrictive branch rather than a silently permissive one.
40
+ */
41
+ export function mockChannelCapabilities(
42
+ caps: Pick<ChannelCapabilities, "channel" | "supportsDynamicUi"> &
43
+ Partial<ChannelCapabilities>,
44
+ ): ChannelCapabilities {
45
+ return { dashboardCapable: false, supportsVoiceInput: false, ...caps };
46
+ }
@@ -22,6 +22,8 @@
22
22
  import { randomBytes, randomUUID } from "node:crypto";
23
23
 
24
24
  import {
25
+ bindsSameIdentity,
26
+ boundIdentity,
25
27
  hashVerificationSecret,
26
28
  VERIFICATION_SESSIONS_IPC_METHODS,
27
29
  type VerificationSessionWire,
@@ -62,10 +64,29 @@ function isInterceptable(s: VerificationSessionWire): boolean {
62
64
  return (INTERCEPTABLE_STATUSES as readonly string[]).includes(s.status);
63
65
  }
64
66
 
65
- function revokeInterceptable(channel: string): void {
67
+ const OUTBOUND_LIVE_STATUSES = ["pending_bootstrap", "awaiting_response"];
68
+
69
+ /**
70
+ * Mirrors the gateway store: an outbound mint supersedes only sessions bound
71
+ * to the same identity, leaving other actors and inbound challenges alone.
72
+ * The identity rule itself comes from the shared contract, so this cannot
73
+ * drift from what the gateway does.
74
+ */
75
+ function revokeSameActorOutbound(
76
+ channel: string,
77
+ params: SimCreateOutboundParams,
78
+ ): void {
79
+ const identity = boundIdentity(params);
80
+ if (identity === null) {
81
+ return;
82
+ }
66
83
  const now = Date.now();
67
84
  for (const s of sessions.values()) {
68
- if (s.channel === channel && isInterceptable(s)) {
85
+ if (
86
+ s.channel === channel &&
87
+ OUTBOUND_LIVE_STATUSES.includes(s.status) &&
88
+ bindsSameIdentity(boundIdentity(s), identity)
89
+ ) {
69
90
  s.status = "revoked";
70
91
  s.updatedAt = now;
71
92
  }
@@ -164,7 +185,7 @@ export interface SimCreateOutboundResult {
164
185
  export function createOutboundSession(
165
186
  params: SimCreateOutboundParams,
166
187
  ): SimCreateOutboundResult {
167
- revokeInterceptable(params.channel);
188
+ revokeSameActorOutbound(params.channel, params);
168
189
 
169
190
  const isUnbound = params.identityBindingStatus === "pending_bootstrap";
170
191
  const secret = isUnbound
@@ -220,17 +241,30 @@ export type SimGuardedCreateOutboundResult =
220
241
  export function createOutboundSessionGuarded(
221
242
  params: SimCreateOutboundParams & {
222
243
  requireSourceSessionPending?: string;
223
- ifNoneActive?: boolean;
224
244
  ifNoneActiveForExternalUserId?: string;
225
245
  },
226
246
  ): SimGuardedCreateOutboundResult {
227
247
  const {
228
248
  requireSourceSessionPending,
229
- ifNoneActive,
230
249
  ifNoneActiveForExternalUserId,
231
250
  ...createParams
232
251
  } = params;
233
252
 
253
+ if (ifNoneActiveForExternalUserId !== undefined) {
254
+ // Scoped, like the gateway. Reading the channel's latest and comparing
255
+ // misses this sender's session whenever somebody else started later.
256
+ const active = findActiveSession(params.channel, {
257
+ expectedExternalUserId: ifNoneActiveForExternalUserId,
258
+ });
259
+ if (active !== null) {
260
+ return { conflict: true, reason: "active_session_exists" };
261
+ }
262
+ }
263
+
264
+ // Claim the named source row. Winning the claim IS the guard, so a second
265
+ // claim of the same deep-link token conflicts. The identity-scoped revoke
266
+ // cannot do this: a bootstrap row carries whichever identity was bound onto
267
+ // it last.
234
268
  if (requireSourceSessionPending !== undefined) {
235
269
  const source = sessions.get(requireSourceSessionPending);
236
270
  if (
@@ -240,20 +274,8 @@ export function createOutboundSessionGuarded(
240
274
  ) {
241
275
  return { conflict: true, reason: "source_session_not_pending" };
242
276
  }
243
- }
244
-
245
- if (ifNoneActive && findActiveSession(params.channel) !== null) {
246
- return { conflict: true, reason: "active_session_exists" };
247
- }
248
-
249
- if (ifNoneActiveForExternalUserId !== undefined) {
250
- const active = findActiveSession(params.channel);
251
- if (
252
- active !== null &&
253
- active.expectedExternalUserId === ifNoneActiveForExternalUserId
254
- ) {
255
- return { conflict: true, reason: "active_session_exists" };
256
- }
277
+ source.status = "revoked";
278
+ source.updatedAt = Date.now();
257
279
  }
258
280
 
259
281
  return createOutboundSession(createParams);
@@ -309,6 +331,10 @@ export function getPendingSession(
309
331
 
310
332
  export function findActiveSession(
311
333
  channel: string,
334
+ filter: {
335
+ expectedExternalUserId?: string;
336
+ verificationPurpose?: "guardian" | "trusted_contact";
337
+ } = {},
312
338
  ): VerificationSessionWire | null {
313
339
  const now = Date.now();
314
340
  return (
@@ -318,7 +344,11 @@ export function findActiveSession(
318
344
  s.channel === channel &&
319
345
  (s.status === "pending_bootstrap" ||
320
346
  s.status === "awaiting_response") &&
321
- s.expiresAt > now,
347
+ s.expiresAt > now &&
348
+ (!filter.expectedExternalUserId ||
349
+ s.expectedExternalUserId === filter.expectedExternalUserId) &&
350
+ (!filter.verificationPurpose ||
351
+ s.verificationPurpose === filter.verificationPurpose),
322
352
  )
323
353
  .sort(newestFirst)[0] ?? null
324
354
  );
@@ -475,50 +505,24 @@ function recordInvalidAttempt(key: string): void {
475
505
  rateLimits.set(key, entry);
476
506
  }
477
507
 
508
+ /**
509
+ * Mirrors the gateway consume path. Both sides read the shared
510
+ * `boundIdentity`, so the sim cannot accept a code the real service would
511
+ * refuse. A session bound to no identity, or not yet finally bound
512
+ * (`pending_bootstrap`), bypasses the check as it does at the gateway.
513
+ */
478
514
  function checkIdentityMatch(
479
515
  session: VerificationSessionWire,
480
516
  actorExternalUserId: string,
481
517
  actorChatId: string,
482
518
  ): boolean {
483
- const hasExpectedIdentity =
484
- session.expectedExternalUserId != null ||
485
- session.expectedChatId != null ||
486
- session.expectedPhoneE164 != null;
487
- // pending_bootstrap (and unbound inbound) sessions bypass the check.
488
- if (!hasExpectedIdentity || session.identityBindingStatus !== "bound") {
519
+ const identity = boundIdentity(session);
520
+ if (identity === null || session.identityBindingStatus !== "bound") {
489
521
  return true;
490
522
  }
491
-
492
- if (
493
- session.expectedPhoneE164 != null &&
494
- (actorExternalUserId === session.expectedPhoneE164 ||
495
- actorExternalUserId === session.expectedExternalUserId)
496
- ) {
497
- return true;
498
- }
499
-
500
- // Shared-chatId caveat: when both expected fields are set, require the
501
- // externalUserId match — chat IDs can be shared.
502
- if (session.expectedChatId != null) {
503
- if (session.expectedExternalUserId != null) {
504
- if (actorExternalUserId === session.expectedExternalUserId) {
505
- return true;
506
- }
507
- } else if (actorChatId === session.expectedChatId) {
508
- return true;
509
- }
510
- }
511
-
512
- if (
513
- session.expectedPhoneE164 == null &&
514
- session.expectedChatId == null &&
515
- session.expectedExternalUserId != null &&
516
- actorExternalUserId === session.expectedExternalUserId
517
- ) {
518
- return true;
519
- }
520
-
521
- return false;
523
+ return identity.field === "chatId"
524
+ ? actorChatId === identity.value
525
+ : actorExternalUserId === identity.value;
522
526
  }
523
527
 
524
528
  export function validateAndConsumeVerification(
@@ -572,8 +572,10 @@ describe("repairHistory", () => {
572
572
  ).toBe(true);
573
573
  });
574
574
 
575
- test("trailing server_tool_use gets synthetic result in same assistant message", () => {
576
- // No trailing user message needed result goes in the assistant message
575
+ test("trailing server_tool_use is preserved as a deferred execution", () => {
576
+ // An unanswered server_tool_use at the tail is a deferred search the
577
+ // provider executes on the next request; repairing it would cancel the
578
+ // search.
577
579
  const messages: Message[] = [
578
580
  { role: "user", content: [{ type: "text", text: "Go" }] },
579
581
  {
@@ -591,21 +593,107 @@ describe("repairHistory", () => {
591
593
 
592
594
  const { messages: repaired, stats } = repairHistory(messages);
593
595
 
594
- expect(stats.missingToolResultsInserted).toBe(1);
595
- // Result is in the assistant message
596
+ expect(stats.missingToolResultsInserted).toBe(0);
596
597
  expect(repaired).toHaveLength(2);
597
598
  expect(repaired[1].role).toBe("assistant");
598
- expect(repaired[1].content).toHaveLength(2);
599
- expect(repaired[1].content[1]).toMatchObject({
600
- type: "web_search_tool_result",
601
- tool_use_id: "stu_1",
602
- content: {
603
- type: "web_search_tool_result_error",
604
- error_code: "unavailable",
605
- },
599
+ expect(repaired[1].content).toHaveLength(1);
600
+ expect(repaired[1].content[0]).toMatchObject({
601
+ type: "server_tool_use",
602
+ id: "stu_1",
606
603
  });
607
604
  });
608
605
 
606
+ test("deferred mixed tail (tool_use + server_tool_use, then tool_result) survives a reload untouched", () => {
607
+ // Persisted mid-deferral shape: the model called a client tool and a
608
+ // search in one parallel group, the client result came back, and the
609
+ // daemon reloaded before the next model call ran the search.
610
+ const messages: Message[] = [
611
+ { role: "user", content: [{ type: "text", text: "Check both" }] },
612
+ {
613
+ role: "assistant",
614
+ content: [
615
+ { type: "tool_use", id: "tu_a", name: "file_read", input: {} },
616
+ {
617
+ type: "server_tool_use",
618
+ id: "stu_deferred",
619
+ name: "web_search",
620
+ input: { query: "news" },
621
+ },
622
+ ],
623
+ },
624
+ {
625
+ role: "user",
626
+ content: [
627
+ { type: "tool_result", tool_use_id: "tu_a", content: "file" },
628
+ ],
629
+ },
630
+ ];
631
+
632
+ const { messages: repaired, stats } = repairHistory(messages);
633
+
634
+ expect(stats.missingToolResultsInserted).toBe(0);
635
+ expect(stats.orphanToolResultsDowngraded).toBe(0);
636
+ expect(repaired).toHaveLength(3);
637
+ expect(repaired[1].content.map((b) => b.type)).toEqual([
638
+ "tool_use",
639
+ "server_tool_use",
640
+ ]);
641
+ });
642
+
643
+ test("cross-message server tool pair from a completed deferred execution survives a reload untouched", () => {
644
+ const messages: Message[] = [
645
+ { role: "user", content: [{ type: "text", text: "Check both" }] },
646
+ {
647
+ role: "assistant",
648
+ content: [
649
+ { type: "tool_use", id: "tu_a", name: "file_read", input: {} },
650
+ {
651
+ type: "server_tool_use",
652
+ id: "stu_split",
653
+ name: "web_search",
654
+ input: { query: "news" },
655
+ },
656
+ ],
657
+ },
658
+ {
659
+ role: "user",
660
+ content: [
661
+ { type: "tool_result", tool_use_id: "tu_a", content: "file" },
662
+ ],
663
+ },
664
+ {
665
+ role: "assistant",
666
+ content: [
667
+ {
668
+ type: "web_search_tool_result",
669
+ tool_use_id: "stu_split",
670
+ content: [
671
+ {
672
+ type: "web_search_result",
673
+ url: "https://example.com",
674
+ title: "Example",
675
+ },
676
+ ],
677
+ },
678
+ { type: "text", text: "Found it." },
679
+ ],
680
+ },
681
+ ];
682
+
683
+ const { messages: repaired, stats } = repairHistory(messages);
684
+
685
+ expect(stats.missingToolResultsInserted).toBe(0);
686
+ expect(stats.orphanToolResultsDowngraded).toBe(0);
687
+ expect(repaired[1].content.map((b) => b.type)).toEqual([
688
+ "tool_use",
689
+ "server_tool_use",
690
+ ]);
691
+ expect(repaired[3].content.map((b) => b.type)).toEqual([
692
+ "web_search_tool_result",
693
+ "text",
694
+ ]);
695
+ });
696
+
609
697
  test("synthetic web_search_tool_result is placed immediately after its server_tool_use, not at end", () => {
610
698
  // Regression: synthetic results appended to the end of the content array
611
699
  // get separated from their server_tool_use by ensureToolPairing's split
@@ -648,6 +736,12 @@ describe("repairHistory", () => {
648
736
  },
649
737
  ],
650
738
  },
739
+ // A later assistant message closes the deferral window: the searches
740
+ // never executed, so they are genuine orphans.
741
+ {
742
+ role: "assistant",
743
+ content: [{ type: "text", text: "Skill ready." }],
744
+ },
651
745
  ];
652
746
 
653
747
  const { messages: repaired, stats } = repairHistory(messages);
@@ -866,6 +960,12 @@ describe("repairHistory", () => {
866
960
  },
867
961
  ],
868
962
  },
963
+ // A later exchange closes the deferral window for stu_missing_result.
964
+ { role: "user", content: [{ type: "text", text: "and?" }] },
965
+ {
966
+ role: "assistant",
967
+ content: [{ type: "text", text: "done" }],
968
+ },
869
969
  ];
870
970
 
871
971
  const { messages: repaired, stats } = repairHistory(messages);
@@ -742,8 +742,11 @@ describe("HostBashProxy", () => {
742
742
  });
743
743
 
744
744
  describe("same-user binding (sourceActorPrincipalId)", () => {
745
- const SAME_USER_REJECTION =
746
- "Submitting actor does not match the target client's actor for this request. The targeted client's authenticated user must submit the result.";
745
+ // The proxy path names which comparison failed, so each rejection asserts
746
+ // its own reason a turn with no actor, a client registered without one,
747
+ // and two genuinely different users are three different problems.
748
+ const CROSS_USER_REJECTION =
749
+ "Submitting actor does not match the target client's actor for this request. The target client is signed in as a different user — run `assistant clients list` to see which connected clients you own.";
747
750
 
748
751
  test("same-user targeted request succeeds", async () => {
749
752
  setup();
@@ -784,7 +787,7 @@ describe("HostBashProxy", () => {
784
787
  );
785
788
 
786
789
  expect(result.isError).toBe(true);
787
- expect(result.content).toBe(SAME_USER_REJECTION);
790
+ expect(result.content).toBe(CROSS_USER_REJECTION);
788
791
  // No broadcast and no pending registration
789
792
  expect(sentMessages).toHaveLength(0);
790
793
  });
@@ -807,7 +810,9 @@ describe("HostBashProxy", () => {
807
810
  );
808
811
 
809
812
  expect(result.isError).toBe(true);
810
- expect(result.content).toBe(SAME_USER_REJECTION);
813
+ expect(result.content).toContain(
814
+ "registered without an authenticated user",
815
+ );
811
816
  expect(sentMessages).toHaveLength(0);
812
817
  });
813
818
 
@@ -823,7 +828,7 @@ describe("HostBashProxy", () => {
823
828
  );
824
829
 
825
830
  expect(result.isError).toBe(true);
826
- expect(result.content).toBe(SAME_USER_REJECTION);
831
+ expect(result.content).toContain("no authenticated actor");
827
832
  expect(sentMessages).toHaveLength(0);
828
833
  });
829
834
 
@@ -312,6 +312,73 @@ describe("handleHostBashResult", () => {
312
312
  });
313
313
  });
314
314
 
315
+ // ── Fill-if-missing: persisted target healed after registration ────
316
+ //
317
+ // A request registered while the target's SSE record had no principal
318
+ // persists `targetActorPrincipalId: undefined`. Once the SSE self-heal
319
+ // fills the hub record, the result route re-reads the live record,
320
+ // but ONLY when the persisted value is missing.
321
+
322
+ describe("targeted request - missing persisted target principal fills from the live hub record", () => {
323
+ test("accepts a matching submitter when the hub record was healed after registration", async () => {
324
+ const requestId = "req-healed-target";
325
+ // Registered pre-heal: hub had no principal, so nothing was persisted.
326
+ registerPending(requestId, { targetClientId: "client-abc" });
327
+ // SSE self-heal fills the hub record afterwards.
328
+ clientActorPrincipals.set("client-abc", "principal-shared");
329
+
330
+ const result = await handleHostBashResult({
331
+ body: bashBody(requestId),
332
+ headers: {
333
+ "x-vellum-client-id": "client-abc",
334
+ "x-vellum-actor-principal-id": "principal-shared",
335
+ },
336
+ });
337
+
338
+ expect(result).toEqual({ accepted: true });
339
+ expect(resolvedIds).toContain(requestId);
340
+ });
341
+
342
+ test("still throws ForbiddenError (403) for a submitter with a different principal than the healed target", () => {
343
+ const requestId = "req-healed-target-mismatch";
344
+ registerPending(requestId, { targetClientId: "client-abc" });
345
+ clientActorPrincipals.set("client-abc", "principal-victim");
346
+
347
+ expect(() =>
348
+ handleHostBashResult({
349
+ body: bashBody(requestId),
350
+ headers: {
351
+ "x-vellum-client-id": "client-abc",
352
+ "x-vellum-actor-principal-id": "principal-attacker",
353
+ },
354
+ }),
355
+ ).toThrow(ForbiddenError);
356
+ expect(pendingStore.has(requestId)).toBe(true);
357
+ });
358
+
359
+ test("a present persisted principal always wins over the live hub record", () => {
360
+ // The fill is missing-only: a persisted value captured at registration
361
+ // is never overridden, so a submitter matching only the (different)
362
+ // live record is still rejected.
363
+ const requestId = "req-persisted-wins";
364
+ registerPending(requestId, {
365
+ targetClientId: "client-abc",
366
+ targetActorPrincipalId: "principal-registration",
367
+ });
368
+ clientActorPrincipals.set("client-abc", "principal-live");
369
+
370
+ expect(() =>
371
+ handleHostBashResult({
372
+ body: bashBody(requestId),
373
+ headers: {
374
+ "x-vellum-client-id": "client-abc",
375
+ "x-vellum-actor-principal-id": "principal-live",
376
+ },
377
+ }),
378
+ ).toThrow(ForbiddenError);
379
+ });
380
+ });
381
+
315
382
  // ── Untargeted-request behavior (regression for new check) ─────────
316
383
 
317
384
  describe("untargeted request — actor principal check is skipped", () => {
@@ -867,7 +867,7 @@ describe("HostBrowserProxy", () => {
867
867
  );
868
868
 
869
869
  expect(result.isError).toBe(true);
870
- expect(result.content).toContain("Submitting actor does not match");
870
+ expect(result.content).toContain("signed in as a different user");
871
871
  expect(getPublishedMessages()).toHaveLength(0);
872
872
  });
873
873
 
@@ -1054,7 +1054,7 @@ describe("HostBrowserProxy", () => {
1054
1054
  );
1055
1055
 
1056
1056
  expect(result.isError).toBe(true);
1057
- expect(result.content).toContain("Submitting actor does not match");
1057
+ expect(result.content).toContain("signed in as a different user");
1058
1058
  expect(result.content).not.toContain("extension_required");
1059
1059
  expect(getPublishedMessages()).toHaveLength(0);
1060
1060
  });
@@ -1416,9 +1416,7 @@ describe("HostCuProxy", () => {
1416
1416
  );
1417
1417
 
1418
1418
  expect(result.isError).toBe(true);
1419
- expect(result.content).toContain(
1420
- "Submitting actor does not match the target client's actor",
1421
- );
1419
+ expect(result.content).toContain("signed in as a different user");
1422
1420
  expect(sentMessages).toHaveLength(0);
1423
1421
  });
1424
1422
 
@@ -1444,9 +1442,7 @@ describe("HostCuProxy", () => {
1444
1442
  );
1445
1443
 
1446
1444
  expect(result.isError).toBe(true);
1447
- expect(result.content).toContain(
1448
- "Submitting actor does not match the target client's actor",
1449
- );
1445
+ expect(result.content).toContain("no authenticated actor");
1450
1446
  expect(sentMessages).toHaveLength(0);
1451
1447
  });
1452
1448
 
@@ -1473,7 +1469,7 @@ describe("HostCuProxy", () => {
1473
1469
 
1474
1470
  expect(result.isError).toBe(true);
1475
1471
  expect(result.content).toContain(
1476
- "Submitting actor does not match the target client's actor",
1472
+ "registered without an authenticated user",
1477
1473
  );
1478
1474
  expect(sentMessages).toHaveLength(0);
1479
1475
  });
@@ -693,9 +693,7 @@ describe("HostFileProxy", () => {
693
693
  );
694
694
 
695
695
  expect(result.isError).toBe(true);
696
- expect(result.content).toContain(
697
- "Submitting actor does not match the target client's actor",
698
- );
696
+ expect(result.content).toContain("signed in as a different user");
699
697
  // No host_file_request was broadcast.
700
698
  expect(sentMessages).toHaveLength(0);
701
699
  });
@@ -720,7 +718,7 @@ describe("HostFileProxy", () => {
720
718
 
721
719
  expect(result.isError).toBe(true);
722
720
  expect(result.content).toContain(
723
- "Submitting actor does not match the target client's actor",
721
+ "registered without an authenticated user",
724
722
  );
725
723
  expect(sentMessages).toHaveLength(0);
726
724
  });
@@ -744,9 +742,7 @@ describe("HostFileProxy", () => {
744
742
  );
745
743
 
746
744
  expect(result.isError).toBe(true);
747
- expect(result.content).toContain(
748
- "Submitting actor does not match the target client's actor",
749
- );
745
+ expect(result.content).toContain("no authenticated actor");
750
746
  expect(sentMessages).toHaveLength(0);
751
747
  });
752
748
 
@@ -27,6 +27,7 @@ mock.module("../daemon/host-file-proxy.js", () => ({
27
27
  }));
28
28
 
29
29
  import { hostFileReadTool } from "../tools/host-filesystem/read.js";
30
+ import { DEFAULT_READ_LINE_LIMIT } from "../tools/shared/filesystem/file-ops-service.js";
30
31
  import type { ToolContext } from "../tools/types.js";
31
32
 
32
33
  const testDirs: string[] = [];
@@ -226,13 +227,16 @@ describe("host_file_read image support", () => {
226
227
 
227
228
  expect(result.isError).toBe(false);
228
229
  expect(result.contentBlocks).toHaveLength(1);
230
+ // The proxied request carries the resolved default window, so a host read
231
+ // is bounded by the same limit as a local one rather than streaming a
232
+ // whole file across the bridge.
229
233
  expect(requests).toEqual([
230
234
  {
231
235
  input: {
232
236
  operation: "read",
233
237
  path: "/host/screenshot.png",
234
238
  offset: undefined,
235
- limit: undefined,
239
+ limit: DEFAULT_READ_LINE_LIMIT,
236
240
  },
237
241
  conversationId: "test-conversation",
238
242
  signal: undefined,