@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
@@ -340,6 +340,110 @@ describe("PlatformPushAdapter", () => {
340
340
  expect(result.remotePushAccepted).toBeUndefined();
341
341
  });
342
342
 
343
+ // A lock screen renders no markdown, so any marker that survives to the
344
+ // dispatch body reads as literal punctuation. See `stripMarkdownForPreview`.
345
+ describe("markdown flattening", () => {
346
+ test("flattens markdown out of the dispatched title and body", async () => {
347
+ const adapter = new PlatformPushAdapter();
348
+ const payload = makePayload({
349
+ copy: {
350
+ title: "**Build** finished",
351
+ body: [
352
+ "Here is the clip: ![vellum scene](vellum://workspace/a.mp4)",
353
+ "```ts",
354
+ "const a = 1;",
355
+ "```",
356
+ "| name | value |",
357
+ "| --- | --- |",
358
+ "| a | 1 |",
359
+ ].join("\n"),
360
+ },
361
+ });
362
+
363
+ const result = await adapter.send(payload, makeDestination());
364
+
365
+ expect(result.success).toBe(true);
366
+ const dispatched = fetchCalls[0]!.body;
367
+ expect(dispatched.title).toBe("Build finished");
368
+ const dispatchedBody = dispatched.body as string;
369
+ expect(dispatchedBody).not.toContain("![");
370
+ expect(dispatchedBody).not.toContain("```");
371
+ expect(dispatchedBody).not.toContain("|");
372
+ expect(dispatchedBody).toContain("Here is the clip:");
373
+ expect(dispatchedBody).toContain("const a = 1;");
374
+ expect(dispatchedBody).toContain("name value");
375
+ });
376
+
377
+ // Guardian question copy joins its question and instruction with a blank
378
+ // line, and iOS renders the break, so flattening must not collapse it.
379
+ test("preserves newlines in the dispatched body", async () => {
380
+ const adapter = new PlatformPushAdapter();
381
+ const payload = makePayload({
382
+ copy: {
383
+ title: "Question",
384
+ body: "Should I book the flight?\n\nReply here to answer.",
385
+ },
386
+ });
387
+
388
+ const result = await adapter.send(payload, makeDestination());
389
+
390
+ expect(result.success).toBe(true);
391
+ expect(fetchCalls[0]!.body.body).toBe(
392
+ "Should I book the flight?\n\nReply here to answer.",
393
+ );
394
+ });
395
+
396
+ test("leaves plain prose untouched", async () => {
397
+ const adapter = new PlatformPushAdapter();
398
+ const result = await adapter.send(makePayload(), makeDestination());
399
+
400
+ expect(result.success).toBe(true);
401
+ expect(fetchCalls[0]!.body.title).toBe("Reminder");
402
+ expect(fetchCalls[0]!.body.body).toBe("Check the oven!");
403
+ });
404
+
405
+ // The pass-through path copies a verbatim `requestedMessage` into both the
406
+ // title and the body, so media-only copy flattens both to nothing. Every
407
+ // empty-copy guard in the pipeline runs upstream of this adapter, and the
408
+ // platform's serializer rejects a blank title, so an unrecovered field
409
+ // costs the whole notification.
410
+ describe("copy that flattens to nothing", () => {
411
+ test("names the media instead of dispatching blank fields", async () => {
412
+ const adapter = new PlatformPushAdapter();
413
+ const embed = "![clip](vellum://workspace/a.mp4)";
414
+ const payload = makePayload({ copy: { title: embed, body: embed } });
415
+
416
+ const result = await adapter.send(payload, makeDestination());
417
+
418
+ expect(result.success).toBe(true);
419
+ expect(fetchCalls[0]!.body.title).toBe("Sent clip");
420
+ expect(fetchCalls[0]!.body.body).toBe("Sent clip");
421
+ });
422
+
423
+ test("counts several embeds", async () => {
424
+ const adapter = new PlatformPushAdapter();
425
+ const embeds =
426
+ "![one](vellum://workspace/1.mp4) ![two](https://e.com/2.png)";
427
+ const payload = makePayload({ copy: { title: embeds, body: embeds } });
428
+
429
+ await adapter.send(payload, makeDestination());
430
+
431
+ expect(fetchCalls[0]!.body.title).toBe("Sent 2 attachments");
432
+ });
433
+
434
+ // Nothing nameable is left, so the original beats a blank field: ugly
435
+ // copy still delivers, an empty title is a 400.
436
+ test("keeps the original when there is no media to name", async () => {
437
+ const adapter = new PlatformPushAdapter();
438
+ const payload = makePayload({ copy: { title: "###", body: "###" } });
439
+
440
+ await adapter.send(payload, makeDestination());
441
+
442
+ expect(fetchCalls[0]!.body.title).toBe("###");
443
+ });
444
+ });
445
+ });
446
+
343
447
  test("omits optional fields when absent from payload", async () => {
344
448
  const adapter = new PlatformPushAdapter();
345
449
  const payload: ChannelDeliveryPayload = {
@@ -3,7 +3,7 @@
3
3
  * deduplicated against prior firings of the same schedule.
4
4
  *
5
5
  * The scheduler supplies a unique per-firing dedupeKey
6
- * (`schedule:notify:<id>:<timestamp>`) so `updateEventDedupeKey` is never
6
+ * (`schedule:notify:<id>:<timestamp>`) so `setEventDedupeKey` is never
7
7
  * called for schedule signals and `checkDedupe` never finds a matching
8
8
  * row when the LLM decision engine generates a stable key like
9
9
  * `schedule:notify:<id>`.
@@ -23,7 +23,7 @@ describe("oauth provider profiles (DB-seeded)", () => {
23
23
  );
24
24
  });
25
25
 
26
- test("google provider row contains bearer injection templates for 4 Google API hosts", () => {
26
+ test("google provider row contains bearer injection templates for 6 Google API hosts", () => {
27
27
  const provider = getProvider("google");
28
28
 
29
29
  expect(provider).toBeDefined();
@@ -36,7 +36,7 @@ describe("oauth provider profiles (DB-seeded)", () => {
36
36
  valuePrefix: string;
37
37
  }>;
38
38
 
39
- expect(templates).toHaveLength(4);
39
+ expect(templates).toHaveLength(6);
40
40
 
41
41
  const byHost = new Map(templates.map((t) => [t.hostPattern, t]));
42
42
 
@@ -45,6 +45,8 @@ describe("oauth provider profiles (DB-seeded)", () => {
45
45
  "www.googleapis.com",
46
46
  "people.googleapis.com",
47
47
  "docs.googleapis.com",
48
+ "tasks.googleapis.com",
49
+ "calendar.googleapis.com",
48
50
  ]) {
49
51
  const tpl = byHost.get(host);
50
52
  expect(tpl).toBeDefined();
@@ -291,9 +291,26 @@ describe("serializeProviderSummary", () => {
291
291
  supports_managed_mode: true,
292
292
  managed_service_is_paid: false,
293
293
  feature_flag: null,
294
+ acts_as: "user",
294
295
  });
295
296
  });
296
297
 
298
+ test("names which sense of connected a provider represents", () => {
299
+ // The provider key does not say, and the naming actively misleads:
300
+ // `slack` is the user integration while its bot is `slack_channel`, but
301
+ // `telegram` is itself the bot. A caller asking "is X connected?" needs
302
+ // this to answer which X it found.
303
+ const sense = (provider: string) =>
304
+ serializeProviderSummary(makeRow({ provider }))!.acts_as;
305
+
306
+ expect(sense("slack")).toBe("user");
307
+ expect(sense("slack_channel")).toBe("assistant");
308
+ expect(sense("discord")).toBe("user");
309
+ expect(sense("discord_channel")).toBe("assistant");
310
+ expect(sense("telegram")).toBe("assistant");
311
+ expect(sense("google")).toBe("user");
312
+ });
313
+
297
314
  test("nullifies missing optional fields", () => {
298
315
  const row = makeRow({
299
316
  displayLabel: null,
@@ -178,6 +178,7 @@ describe("GET /v1/oauth/providers", () => {
178
178
  "supports_managed_mode",
179
179
  "managed_service_is_paid",
180
180
  "feature_flag",
181
+ "acts_as",
181
182
  ];
182
183
 
183
184
  for (const provider of providers) {
@@ -238,16 +238,19 @@ describe("OpenAIResponsesProvider explicit prompt caching (GPT-5.6+)", () => {
238
238
  expect(breakpointedItemIndexes()).toEqual([0, 2]);
239
239
  });
240
240
 
241
- test("volatile single-message first turn: no markers (nothing stable to anchor)", async () => {
241
+ test("volatile single-message first turn: the sole item is still marked", async () => {
242
242
  const provider = makeProvider("gpt-5.6-sol");
243
243
  await provider.sendMessage([userMsg("hi")], {
244
244
  config: { mutableLatestUserMessage: true, promptCacheKey: "conv-1" },
245
245
  });
246
246
 
247
+ // A volatile message is fixed within its own turn, so marking it prepays
248
+ // one write that the turn's tool-loop iterations read back, covering the
249
+ // instructions and tools that precede it in the cached prefix.
247
250
  expect(lastStreamParams?.prompt_cache_options).toEqual({
248
251
  mode: "explicit",
249
252
  });
250
- expect(breakpointedItemIndexes()).toEqual([]);
253
+ expect(breakpointedItemIndexes()).toEqual([0]);
251
254
  });
252
255
 
253
256
  test("tool loop: single fixed anchor on the turn-start user item, none on function_call_output", async () => {
@@ -15,6 +15,7 @@ import {
15
15
  resolveRealPath,
16
16
  sandboxPolicy,
17
17
  sandboxPolicyWithHostFallback,
18
+ sandboxReadPolicy,
18
19
  } from "../tools/shared/filesystem/path-policy.js";
19
20
  import { getDotEnvPath } from "../util/platform.js";
20
21
 
@@ -506,6 +507,162 @@ describe("sandboxPolicyWithHostFallback", () => {
506
507
  });
507
508
  });
508
509
 
510
+ // ---------------------------------------------------------------------------
511
+ // Sandbox read policy
512
+ // ---------------------------------------------------------------------------
513
+
514
+ describe("sandboxReadPolicy", () => {
515
+ afterEach(() => {
516
+ delete process.env.IS_CONTAINERIZED;
517
+ delete process.env.GATEWAY_SECURITY_DIR;
518
+ delete process.env.CREDENTIAL_SECURITY_DIR;
519
+ });
520
+
521
+ test("matches sandboxPolicy for in-bounds paths", () => {
522
+ const boundary = makeTempDir();
523
+ mkdirSync(join(boundary, "sub"));
524
+
525
+ const result = sandboxReadPolicy("sub/file.txt", boundary);
526
+ expect(result).toEqual(sandboxPolicy("sub/file.txt", boundary));
527
+ expect(result.ok).toBe(true);
528
+ });
529
+
530
+ test("reads outside the boundary when containerized", () => {
531
+ const boundary = makeTempDir();
532
+ const installTree = makeTempDir();
533
+ const target = join(installTree, "SKILL.md");
534
+ writeFileSync(target, "# skill");
535
+ process.env.IS_CONTAINERIZED = "true";
536
+
537
+ const result = sandboxReadPolicy(target, boundary);
538
+ expect(result.ok).toBe(true);
539
+ if (result.ok) {
540
+ expect(result.resolved).toBe(target);
541
+ }
542
+ });
543
+
544
+ test("follows an in-boundary symlink that points outside", () => {
545
+ const boundary = makeTempDir();
546
+ const outside = makeTempDir();
547
+ const target = join(outside, "reference.md");
548
+ writeFileSync(target, "reference body");
549
+ symlinkSync(target, join(boundary, "link.md"));
550
+ process.env.IS_CONTAINERIZED = "true";
551
+
552
+ const result = sandboxReadPolicy(join(boundary, "link.md"), boundary);
553
+ expect(result.ok).toBe(true);
554
+ });
555
+
556
+ test("denies the gateway security dir when containerized", () => {
557
+ const boundary = makeTempDir();
558
+ const securityDir = makeTempDir();
559
+ writeFileSync(join(securityDir, "trust.json"), "{}");
560
+ process.env.GATEWAY_SECURITY_DIR = securityDir;
561
+ process.env.IS_CONTAINERIZED = "true";
562
+
563
+ const result = sandboxReadPolicy(join(securityDir, "trust.json"), boundary);
564
+ expect(result.ok).toBe(false);
565
+ if (!result.ok) {
566
+ expect(result.reason).toBe("denied");
567
+ }
568
+ });
569
+
570
+ test("denies a symlink that resolves into the credential security dir", () => {
571
+ const boundary = makeTempDir();
572
+ const securityDir = makeTempDir();
573
+ writeFileSync(join(securityDir, "keys.enc"), "cipher");
574
+ symlinkSync(join(securityDir, "keys.enc"), join(boundary, "innocent.txt"));
575
+ process.env.CREDENTIAL_SECURITY_DIR = securityDir;
576
+ process.env.IS_CONTAINERIZED = "true";
577
+
578
+ const result = sandboxReadPolicy(join(boundary, "innocent.txt"), boundary);
579
+ expect(result.ok).toBe(false);
580
+ if (!result.ok) {
581
+ expect(result.reason).toBe("denied");
582
+ }
583
+ });
584
+
585
+ test("denies a denylisted basename outside the boundary", () => {
586
+ const boundary = makeTempDir();
587
+ const outside = makeTempDir();
588
+ writeFileSync(join(outside, "backup.key"), "key");
589
+ process.env.IS_CONTAINERIZED = "true";
590
+
591
+ const result = sandboxReadPolicy(join(outside, "backup.key"), boundary);
592
+ expect(result.ok).toBe(false);
593
+ if (!result.ok) {
594
+ expect(result.reason).toBe("denied");
595
+ }
596
+ });
597
+
598
+ test("fails closed to the hard boundary when a security-dir override is relative", () => {
599
+ const boundary = makeTempDir();
600
+ const outside = makeTempDir();
601
+ writeFileSync(join(outside, "file.txt"), "x");
602
+ // Unmirrorable deny set: the real security dir could be anywhere, so the
603
+ // read allowance must not widen past the boundary.
604
+ process.env.GATEWAY_SECURITY_DIR = "relative/security-dir";
605
+ process.env.IS_CONTAINERIZED = "true";
606
+
607
+ const result = sandboxReadPolicy(join(outside, "file.txt"), boundary);
608
+ expect(result.ok).toBe(false);
609
+ if (!result.ok) {
610
+ expect(result.reason).toBe("out_of_bounds");
611
+ }
612
+ });
613
+
614
+ // The daemon's own environment carries the actor token signing key, the CES
615
+ // service token, the guardian bootstrap secret, and forwarded provider API
616
+ // keys, so no policy may serve a process environment block.
617
+ test.each([
618
+ "/proc/self/environ",
619
+ "/proc/thread-self/environ",
620
+ "/proc/1/environ",
621
+ "/proc/4242/environ",
622
+ ])("denies %s", (path) => {
623
+ const boundary = makeTempDir();
624
+ process.env.IS_CONTAINERIZED = "true";
625
+
626
+ const result = sandboxReadPolicy(path, boundary, { mustExist: false });
627
+ expect(result.ok).toBe(false);
628
+ if (!result.ok) {
629
+ expect(result.reason).toBe("denied");
630
+ }
631
+ });
632
+
633
+ test("denies a process environ path reached through a symlink", () => {
634
+ const boundary = makeTempDir();
635
+ symlinkSync("/proc/self/environ", join(boundary, "innocent.txt"));
636
+ process.env.IS_CONTAINERIZED = "true";
637
+
638
+ const result = sandboxReadPolicy(join(boundary, "innocent.txt"), boundary, {
639
+ mustExist: false,
640
+ });
641
+ expect(result.ok).toBe(false);
642
+ if (!result.ok) {
643
+ expect(result.reason).toBe("denied");
644
+ }
645
+ });
646
+
647
+ test("still allows other /proc entries", () => {
648
+ const boundary = makeTempDir();
649
+ process.env.IS_CONTAINERIZED = "true";
650
+
651
+ const result = sandboxReadPolicy("/proc/self/status", boundary, {
652
+ mustExist: false,
653
+ });
654
+ expect(result.ok).toBe(true);
655
+ });
656
+
657
+ test("does not deny an unrelated file named environ", () => {
658
+ const boundary = makeTempDir();
659
+ writeFileSync(join(boundary, "environ"), "notes");
660
+
661
+ const result = sandboxReadPolicy("environ", boundary);
662
+ expect(result.ok).toBe(true);
663
+ });
664
+ });
665
+
509
666
  // ---------------------------------------------------------------------------
510
667
  // Host policy
511
668
  // ---------------------------------------------------------------------------
@@ -575,7 +732,13 @@ describe("resolveRealPath", () => {
575
732
  });
576
733
 
577
734
  test("falls back to the lexical path when nothing on it exists", () => {
578
- const phantom = join(tmpdir(), "definitely-not-here-12345", "nope.txt");
735
+ // realpath the temp root so a symlinked prefix (macOS /var -> /private/var)
736
+ // is not mistaken for the resolution under test.
737
+ const phantom = join(
738
+ realpathSync(tmpdir()),
739
+ "definitely-not-here-12345",
740
+ "nope.txt",
741
+ );
579
742
  expect(resolveRealPath(phantom)).toBe(phantom);
580
743
  });
581
744
  });
@@ -49,7 +49,7 @@ describe("referenceMediaBlocksForPersist", () => {
49
49
  },
50
50
  ];
51
51
 
52
- const referenced = referenceMediaBlocksForPersist(
52
+ const referenced = await referenceMediaBlocksForPersist(
53
53
  conv.id,
54
54
  conv.createdAt,
55
55
  msg.id,
@@ -101,7 +101,7 @@ describe("referenceMediaBlocksForPersist", () => {
101
101
  },
102
102
  ];
103
103
 
104
- const referenced = referenceMediaBlocksForPersist(
104
+ const referenced = await referenceMediaBlocksForPersist(
105
105
  conv.id,
106
106
  conv.createdAt,
107
107
  msg.id,
@@ -28,8 +28,8 @@ mock.module("../agent/image-optimize.js", () => ({
28
28
  // oversized (never undersized), so the min-dimension gate never matches
29
29
  // and the rejection-path upscale is never reached.
30
30
  isBelowMinDimension: () => false,
31
- upscaleImageToMinimum: () => null,
32
- optimizeImageForTransport: () => ({
31
+ upscaleImageToMinimum: async () => null,
32
+ optimizeImageForTransport: async () => ({
33
33
  data: SHRUNK_DATA,
34
34
  mediaType: "image/jpeg",
35
35
  }),
@@ -133,7 +133,7 @@ describe("persistUnsendableImageDowngrades (downscalable host)", () => {
133
133
  );
134
134
 
135
135
  // WHEN the downgrade is persisted
136
- const rewritten = persistUnsendableImageDowngrades(conv.id);
136
+ const rewritten = await persistUnsendableImageDowngrades(conv.id);
137
137
 
138
138
  // THEN the nested block stays an image, rewritten to the downscaled payload
139
139
  expect(rewritten).toBe(1);
@@ -159,10 +159,10 @@ describe("persistUnsendableImageDowngrades (downscalable host)", () => {
159
159
  JSON.stringify([toolResultWithImage(oversizedPngBase64())]),
160
160
  { skipIndexing: true },
161
161
  );
162
- expect(persistUnsendableImageDowngrades(conv.id)).toBe(1);
162
+ expect(await persistUnsendableImageDowngrades(conv.id)).toBe(1);
163
163
 
164
164
  // WHEN the downgrade runs again
165
- const secondRun = persistUnsendableImageDowngrades(conv.id);
165
+ const secondRun = await persistUnsendableImageDowngrades(conv.id);
166
166
 
167
167
  // THEN nothing further is rewritten
168
168
  expect(secondRun).toBe(0);
@@ -132,7 +132,7 @@ describe("persistUnsendableImageDowngrades", () => {
132
132
  );
133
133
 
134
134
  // WHEN the downgrade is persisted
135
- const rewritten = persistUnsendableImageDowngrades(conv.id);
135
+ const rewritten = await persistUnsendableImageDowngrades(conv.id);
136
136
 
137
137
  // THEN one message is rewritten with no image block left
138
138
  expect(rewritten).toBe(1);
@@ -162,7 +162,7 @@ describe("persistUnsendableImageDowngrades", () => {
162
162
  );
163
163
 
164
164
  // WHEN the downgrade is persisted
165
- const rewritten = persistUnsendableImageDowngrades(conv.id);
165
+ const rewritten = await persistUnsendableImageDowngrades(conv.id);
166
166
 
167
167
  // THEN only the rejected original is removed
168
168
  expect(rewritten).toBe(1);
@@ -184,7 +184,7 @@ describe("persistUnsendableImageDowngrades", () => {
184
184
  );
185
185
 
186
186
  // WHEN the downgrade is persisted
187
- const rewritten = persistUnsendableImageDowngrades(conv.id);
187
+ const rewritten = await persistUnsendableImageDowngrades(conv.id);
188
188
 
189
189
  // THEN nothing is rewritten and the image remains
190
190
  expect(rewritten).toBe(0);
@@ -202,7 +202,7 @@ describe("persistUnsendableImageDowngrades", () => {
202
202
  });
203
203
 
204
204
  // WHEN the downgrade is persisted
205
- const rewritten = persistUnsendableImageDowngrades(conv.id);
205
+ const rewritten = await persistUnsendableImageDowngrades(conv.id);
206
206
 
207
207
  // THEN the oversized-payload image is removed
208
208
  expect(rewritten).toBe(1);
@@ -224,7 +224,7 @@ describe("persistUnsendableImageDowngrades", () => {
224
224
  );
225
225
 
226
226
  // WHEN the downgrade is persisted
227
- const rewritten = persistUnsendableImageDowngrades(conv.id);
227
+ const rewritten = await persistUnsendableImageDowngrades(conv.id);
228
228
 
229
229
  // THEN the undersized image is removed (upscale is a no-op on this host)
230
230
  expect(rewritten).toBe(1);
@@ -248,7 +248,7 @@ describe("persistUnsendableImageDowngrades", () => {
248
248
  );
249
249
 
250
250
  // WHEN the downgrade is persisted
251
- const rewritten = persistUnsendableImageDowngrades(conv.id);
251
+ const rewritten = await persistUnsendableImageDowngrades(conv.id);
252
252
 
253
253
  // THEN the message is rewritten with the nested image swapped for a note
254
254
  expect(rewritten).toBe(1);
@@ -274,7 +274,7 @@ describe("persistUnsendableImageDowngrades", () => {
274
274
  );
275
275
 
276
276
  // WHEN the downgrade is persisted
277
- const rewritten = persistUnsendableImageDowngrades(conv.id);
277
+ const rewritten = await persistUnsendableImageDowngrades(conv.id);
278
278
 
279
279
  // THEN nothing is rewritten and the nested image remains
280
280
  expect(rewritten).toBe(0);
@@ -302,7 +302,7 @@ describe("persistUnsendableImageDowngrades", () => {
302
302
  );
303
303
 
304
304
  // WHEN the downgrade is persisted
305
- const rewritten = persistUnsendableImageDowngrades(conv.id);
305
+ const rewritten = await persistUnsendableImageDowngrades(conv.id);
306
306
 
307
307
  // THEN the image survives with its media type corrected to the sniffed one
308
308
  expect(rewritten).toBe(1);
@@ -319,7 +319,7 @@ describe("persistUnsendableImageDowngrades", () => {
319
319
  });
320
320
 
321
321
  // AND a second run is a no-op (the corrected block no longer mismatches)
322
- expect(persistUnsendableImageDowngrades(conv.id)).toBe(0);
322
+ expect(await persistUnsendableImageDowngrades(conv.id)).toBe(0);
323
323
  });
324
324
 
325
325
  /** Re-running after a rewrite is a safe no-op (no image blocks remain). */
@@ -332,10 +332,10 @@ describe("persistUnsendableImageDowngrades", () => {
332
332
  JSON.stringify([imageBlock(makePngBase64(10000, 10000))]),
333
333
  { skipIndexing: true },
334
334
  );
335
- expect(persistUnsendableImageDowngrades(conv.id)).toBe(1);
335
+ expect(await persistUnsendableImageDowngrades(conv.id)).toBe(1);
336
336
 
337
337
  // WHEN the downgrade runs a second time
338
- const secondRun = persistUnsendableImageDowngrades(conv.id);
338
+ const secondRun = await persistUnsendableImageDowngrades(conv.id);
339
339
 
340
340
  // THEN nothing further is rewritten
341
341
  expect(secondRun).toBe(0);
@@ -346,46 +346,46 @@ describe("unsendableImageReplacement", () => {
346
346
  /** A still-sendable image must be left alone — never replaced with a note.
347
347
  * This is the gate that keeps the in-memory recovery from discarding valid
348
348
  * screenshots when only one image in the turn was actually oversized. */
349
- test("returns null for an image within the provider caps", () => {
349
+ test("returns null for an image within the provider caps", async () => {
350
350
  const sendable = imageBlock(makePngBase64(1024, 768)) as Extract<
351
351
  ContentBlock,
352
352
  { type: "image" }
353
353
  >;
354
- expect(unsendableImageReplacement(sendable)).toBeNull();
354
+ expect(await unsendableImageReplacement(sendable)).toBeNull();
355
355
  });
356
356
 
357
357
  /** An image past the provider caps that cannot be shrunk on this host (fake
358
358
  * PNG that sips cannot decode) collapses to the unsendable note. */
359
- test("returns the unsendable note when an oversized image cannot be shrunk", () => {
359
+ test("returns the unsendable note when an oversized image cannot be shrunk", async () => {
360
360
  const oversized = imageBlock(makePngBase64(12000, 9000)) as Extract<
361
361
  ContentBlock,
362
362
  { type: "image" }
363
363
  >;
364
- const replacement = unsendableImageReplacement(oversized);
364
+ const replacement = await unsendableImageReplacement(oversized);
365
365
  expect(replacement?.type).toBe("text");
366
366
  });
367
367
 
368
368
  /** An image below the provider's minimum-size floor (rejected with "Could
369
369
  * not process image") that cannot be upscaled on this host collapses to
370
370
  * the unsendable note, same as the oversized direction. */
371
- test("returns the unsendable note when an undersized image cannot be upscaled", () => {
371
+ test("returns the unsendable note when an undersized image cannot be upscaled", async () => {
372
372
  const undersized = imageBlock(makePngBase64(16, 14)) as Extract<
373
373
  ContentBlock,
374
374
  { type: "image" }
375
375
  >;
376
- const replacement = unsendableImageReplacement(undersized);
376
+ const replacement = await unsendableImageReplacement(undersized);
377
377
  expect(replacement?.type).toBe("text");
378
378
  });
379
379
 
380
380
  /** A within-limits image whose declared media type disagrees with its bytes
381
381
  * is relabeled with the sniffed type instead of being noted out. */
382
- test("relabels a mislabeled image with its sniffed media type", () => {
382
+ test("relabels a mislabeled image with its sniffed media type", async () => {
383
383
  const pngData = makePngBase64(1024, 768);
384
384
  const mislabeled = imageBlock(pngData, "image/jpeg") as Extract<
385
385
  ContentBlock,
386
386
  { type: "image" }
387
387
  >;
388
- const replacement = unsendableImageReplacement(mislabeled);
388
+ const replacement = await unsendableImageReplacement(mislabeled);
389
389
  expect(replacement).toMatchObject({
390
390
  type: "image",
391
391
  source: { type: "base64", media_type: "image/png", data: pngData },
@@ -394,9 +394,9 @@ describe("unsendableImageReplacement", () => {
394
394
 
395
395
  /** Relabeling a persisted (workspace_ref) image keeps the reference shape —
396
396
  * inlining it would bake the full payload into the stored message row. */
397
- test("relabels a mislabeled workspace_ref without inlining the payload", () => {
397
+ test("relabels a mislabeled workspace_ref without inlining the payload", async () => {
398
398
  const pngData = makePngBase64(1024, 768);
399
- const stored = uploadAttachment("photo.png", "image/png", pngData);
399
+ const stored = await uploadAttachment("photo.png", "image/png", pngData);
400
400
  const mislabeledRef = {
401
401
  type: "image",
402
402
  source: {
@@ -407,7 +407,7 @@ describe("unsendableImageReplacement", () => {
407
407
  },
408
408
  } as Extract<ContentBlock, { type: "image" }>;
409
409
 
410
- const replacement = unsendableImageReplacement(mislabeledRef);
410
+ const replacement = await unsendableImageReplacement(mislabeledRef);
411
411
 
412
412
  expect(replacement).toMatchObject({
413
413
  type: "image",
@@ -419,7 +419,7 @@ describe("unsendableImageReplacement", () => {
419
419
  });
420
420
  // AND the corrected reference no longer matches on a second pass
421
421
  expect(
422
- unsendableImageReplacement(
422
+ await unsendableImageReplacement(
423
423
  replacement as Extract<ContentBlock, { type: "image" }>,
424
424
  ),
425
425
  ).toBeNull();