@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,352 @@
1
+ /**
2
+ * End-to-end guards over referential forking: `forkStrategy: "reference"`
3
+ * builds a fork that copies no rows and reads its inherited window back
4
+ * through `fork_parent_message_id`.
5
+ *
6
+ * The load-bearing claim is equivalence. A referential fork must present the
7
+ * same message list a copied fork presents, because the retrospective agent
8
+ * reads the fork natively and every downstream consumer (accounting, dedup,
9
+ * the fork-boundary scan) reads it through the same helpers. The parity test
10
+ * against the cloning path is what pins that.
11
+ */
12
+
13
+ import { rmSync, writeFileSync } from "node:fs";
14
+ import { afterAll, beforeEach, describe, expect, test } from "bun:test";
15
+
16
+ import { eq } from "drizzle-orm";
17
+
18
+ import { invalidateConfigCache } from "../config/loader.js";
19
+ import {
20
+ addMessage,
21
+ countMessagesAfter,
22
+ createConversation,
23
+ deleteConversation,
24
+ deleteConversationGently,
25
+ forkConversationForRetrospective,
26
+ getMessages,
27
+ getMessagesAfter,
28
+ getMessagesPaginated,
29
+ hasMessages,
30
+ isReferentialHistoryOrphaned,
31
+ } from "../persistence/conversation-crud.js";
32
+ import { getDb, getLogsDb, getMemoryDb } from "../persistence/db-connection.js";
33
+ import { initializeDb } from "../persistence/db-init.js";
34
+ import {
35
+ conversations,
36
+ llmRequestLogs,
37
+ memoryJobs,
38
+ memoryRetrospectiveState,
39
+ messages,
40
+ } from "../persistence/schema/index.js";
41
+ import { getWorkspaceConfigPath } from "../util/platform.js";
42
+
43
+ await initializeDb();
44
+
45
+ const configPath = getWorkspaceConfigPath();
46
+
47
+ /**
48
+ * Point `memory.retrospective.forkStrategy` at a strategy. The fork path reads
49
+ * it from config rather than taking a parameter, so exercising both branches
50
+ * means writing the workspace config the loader reads.
51
+ */
52
+ function setForkStrategy(strategy: "cloning" | "reference"): void {
53
+ writeFileSync(
54
+ configPath,
55
+ JSON.stringify(
56
+ { memory: { retrospective: { forkStrategy: strategy } } },
57
+ null,
58
+ 2,
59
+ ) + "\n",
60
+ );
61
+ invalidateConfigCache();
62
+ }
63
+
64
+ function clearConfig(): void {
65
+ rmSync(configPath, { force: true });
66
+ invalidateConfigCache();
67
+ }
68
+
69
+ afterAll(clearConfig);
70
+
71
+ function resetTables(): void {
72
+ const db = getDb();
73
+ getMemoryDb()!.delete(memoryRetrospectiveState).run();
74
+ getMemoryDb()!.delete(memoryJobs).run();
75
+ getLogsDb()!.delete(llmRequestLogs).run();
76
+ db.run("DELETE FROM message_attachments");
77
+ db.run("DELETE FROM attachments");
78
+ db.run("DELETE FROM conversation_compaction_events");
79
+ db.run("DELETE FROM messages");
80
+ db.run("DELETE FROM conversations");
81
+ }
82
+
83
+ async function seedSource(title: string): Promise<{ id: string }> {
84
+ const source = createConversation(title);
85
+ await addMessage(source.id, "user", "draft a launch plan", {
86
+ skipIndexing: true,
87
+ });
88
+ await addMessage(source.id, "assistant", "here is a first pass", {
89
+ skipIndexing: true,
90
+ });
91
+ await addMessage(source.id, "user", "tweak the timeline", {
92
+ skipIndexing: true,
93
+ });
94
+ await addMessage(source.id, "assistant", "updated", { skipIndexing: true });
95
+ return source;
96
+ }
97
+
98
+ /** Ids of every conversation currently in the database. */
99
+ function conversationIds(): string[] {
100
+ return getDb()
101
+ .select({ id: conversations.id })
102
+ .from(conversations)
103
+ .all()
104
+ .map((row) => row.id);
105
+ }
106
+
107
+ /** Rows physically stored on a conversation, ignoring anything inherited. */
108
+ function ownedRowCount(conversationId: string): number {
109
+ return getDb()
110
+ .select()
111
+ .from(messages)
112
+ .where(eq(messages.conversationId, conversationId))
113
+ .all().length;
114
+ }
115
+
116
+ /** Flatten each row's content blocks down to their text, for readable asserts. */
117
+ function textOf(rows: Array<{ content: unknown }>): string[] {
118
+ return rows.map((row) => {
119
+ if (typeof row.content === "string") {
120
+ return row.content;
121
+ }
122
+ const blocks = Array.isArray(row.content) ? row.content : [];
123
+ return blocks
124
+ .map((block: unknown) =>
125
+ typeof block === "object" && block !== null && "text" in block
126
+ ? String((block as { text: unknown }).text)
127
+ : "",
128
+ )
129
+ .join("");
130
+ });
131
+ }
132
+
133
+ describe("referential forking", () => {
134
+ beforeEach(() => {
135
+ resetTables();
136
+ clearConfig();
137
+ });
138
+
139
+ test("copies no message rows and stamps the strategy", async () => {
140
+ const source = await seedSource("Launch");
141
+
142
+ setForkStrategy("reference");
143
+ const fork = await forkConversationForRetrospective({
144
+ conversationId: source.id,
145
+ });
146
+
147
+ expect(ownedRowCount(fork.id)).toBe(0);
148
+ expect(fork.forkStrategy).toBe("reference");
149
+ expect(fork.forkParentConversationId).toBe(source.id);
150
+ expect(fork.forkParentMessageId).not.toBeNull();
151
+ });
152
+
153
+ test("reads the source's history through the fork pointer", async () => {
154
+ const source = await seedSource("Launch");
155
+
156
+ setForkStrategy("reference");
157
+ const fork = await forkConversationForRetrospective({
158
+ conversationId: source.id,
159
+ });
160
+
161
+ expect(textOf(getMessages(fork.id))).toEqual([
162
+ "draft a launch plan",
163
+ "here is a first pass",
164
+ "tweak the timeline",
165
+ "updated",
166
+ ]);
167
+ expect(hasMessages(fork.id)).toBe(true);
168
+ });
169
+
170
+ test("presents the same messages a cloning fork does", async () => {
171
+ const source = await seedSource("Launch");
172
+
173
+ setForkStrategy("cloning");
174
+ const cloned = await forkConversationForRetrospective({
175
+ conversationId: source.id,
176
+ });
177
+ setForkStrategy("reference");
178
+ const referenced = await forkConversationForRetrospective({
179
+ conversationId: source.id,
180
+ });
181
+
182
+ expect(textOf(getMessages(referenced.id))).toEqual(
183
+ textOf(getMessages(cloned.id)),
184
+ );
185
+ expect(countMessagesAfter(referenced.id, null)).toBe(
186
+ countMessagesAfter(cloned.id, null),
187
+ );
188
+ });
189
+
190
+ test("rows written on the fork interleave after the inherited ones", async () => {
191
+ const source = await seedSource("Launch");
192
+ setForkStrategy("reference");
193
+ const fork = await forkConversationForRetrospective({
194
+ conversationId: source.id,
195
+ });
196
+
197
+ await addMessage(fork.id, "user", "review this conversation", {
198
+ skipIndexing: true,
199
+ });
200
+
201
+ expect(textOf(getMessages(fork.id)).at(-1)).toBe(
202
+ "review this conversation",
203
+ );
204
+ expect(ownedRowCount(fork.id)).toBe(1);
205
+ });
206
+
207
+ test("messages written on the source after the fork point are excluded", async () => {
208
+ const source = await seedSource("Launch");
209
+ setForkStrategy("reference");
210
+ const fork = await forkConversationForRetrospective({
211
+ conversationId: source.id,
212
+ });
213
+
214
+ await addMessage(source.id, "user", "written after the fork", {
215
+ skipIndexing: true,
216
+ });
217
+
218
+ const forkText = textOf(getMessages(fork.id));
219
+ expect(forkText).not.toContain("written after the fork");
220
+ expect(textOf(getMessages(source.id))).toContain("written after the fork");
221
+ });
222
+
223
+ test("a cutoff bounds the inherited window", async () => {
224
+ const source = await seedSource("Launch");
225
+ const sourceRows = getMessages(source.id);
226
+
227
+ setForkStrategy("reference");
228
+ const fork = await forkConversationForRetrospective({
229
+ conversationId: source.id,
230
+ throughMessageId: sourceRows[1]!.id,
231
+ });
232
+
233
+ expect(textOf(getMessages(fork.id))).toEqual([
234
+ "draft a launch plan",
235
+ "here is a first pass",
236
+ ]);
237
+ });
238
+
239
+ test("getMessagesAfter and countMessagesAfter cross the lineage boundary", async () => {
240
+ const source = await seedSource("Launch");
241
+ const sourceRows = getMessages(source.id);
242
+ setForkStrategy("reference");
243
+ const fork = await forkConversationForRetrospective({
244
+ conversationId: source.id,
245
+ });
246
+ await addMessage(fork.id, "user", "own row", { skipIndexing: true });
247
+
248
+ // The cursor sits on an ancestor row while the rows it must return span
249
+ // both the ancestor's tail and the fork's own writes.
250
+ const after = getMessagesAfter(fork.id, sourceRows[1]!.id);
251
+
252
+ expect(textOf(after)).toEqual(["tweak the timeline", "updated", "own row"]);
253
+ expect(countMessagesAfter(fork.id, sourceRows[1]!.id)).toBe(3);
254
+ });
255
+
256
+ test("pagination returns the inherited rows", async () => {
257
+ const source = await seedSource("Launch");
258
+ setForkStrategy("reference");
259
+ const fork = await forkConversationForRetrospective({
260
+ conversationId: source.id,
261
+ });
262
+
263
+ const unlimited = getMessagesPaginated(fork.id, undefined);
264
+ expect(textOf(unlimited.messages)).toHaveLength(4);
265
+
266
+ const page = getMessagesPaginated(fork.id, 2);
267
+ expect(page.messages).toHaveLength(2);
268
+ expect(page.hasMore).toBe(true);
269
+ // Newest-first paging, so the page holds the tail of the inherited window.
270
+ expect(textOf(page.messages)).toContain("updated");
271
+ });
272
+
273
+ test("deleting the source orphans the fork instead of blocking or cascading", async () => {
274
+ const source = await seedSource("Launch");
275
+ setForkStrategy("reference");
276
+ const fork = await forkConversationForRetrospective({
277
+ conversationId: source.id,
278
+ });
279
+ await addMessage(fork.id, "user", "own row", { skipIndexing: true });
280
+
281
+ deleteConversation(source.id);
282
+
283
+ // The delete touches only what it names: the fork survives, keeps the rows
284
+ // it owns, and the lineage read truncates at the missing parent instead of
285
+ // throwing.
286
+ expect(conversationIds()).toContain(fork.id);
287
+ expect(textOf(getMessages(fork.id))).toEqual(["own row"]);
288
+ expect(hasMessages(fork.id)).toBe(true);
289
+ expect(countMessagesAfter(fork.id, null)).toBe(1);
290
+ });
291
+
292
+ test("the gentle delete path orphans the same way", async () => {
293
+ const source = await seedSource("Launch");
294
+ setForkStrategy("reference");
295
+ const fork = await forkConversationForRetrospective({
296
+ conversationId: source.id,
297
+ });
298
+
299
+ // `deleteConversationGently` is a separate implementation of the same
300
+ // operation and is what the plugin facade exposes, so it must not diverge.
301
+ await deleteConversationGently(source.id);
302
+
303
+ expect(conversationIds()).toContain(fork.id);
304
+ expect(getMessages(fork.id)).toHaveLength(0);
305
+ });
306
+
307
+ test("an orphaned fork reports the loss so clients can explain it", async () => {
308
+ const source = await seedSource("Launch");
309
+ setForkStrategy("reference");
310
+ const fork = await forkConversationForRetrospective({
311
+ conversationId: source.id,
312
+ });
313
+
314
+ expect(isReferentialHistoryOrphaned(fork)).toBe(false);
315
+ deleteConversation(source.id);
316
+
317
+ const orphan = getDb()
318
+ .select()
319
+ .from(conversations)
320
+ .where(eq(conversations.id, fork.id))
321
+ .get()!;
322
+ expect(isReferentialHistoryOrphaned(orphan)).toBe(true);
323
+ });
324
+
325
+ test("a cloning fork whose source is deleted is not reported as orphaned", async () => {
326
+ const source = await seedSource("Launch");
327
+ setForkStrategy("cloning");
328
+ const cloned = await forkConversationForRetrospective({
329
+ conversationId: source.id,
330
+ });
331
+
332
+ deleteConversation(source.id);
333
+
334
+ // It holds its own copy, so a missing parent costs it nothing and there is
335
+ // nothing to tell the user about.
336
+ expect(isReferentialHistoryOrphaned(cloned)).toBe(false);
337
+ expect(textOf(getMessages(cloned.id))).toHaveLength(4);
338
+ });
339
+
340
+ test("deleting a fork leaves its source alone", async () => {
341
+ const source = await seedSource("Launch");
342
+ setForkStrategy("reference");
343
+ const fork = await forkConversationForRetrospective({
344
+ conversationId: source.id,
345
+ });
346
+
347
+ deleteConversation(fork.id);
348
+
349
+ expect(conversationIds()).toContain(source.id);
350
+ expect(textOf(getMessages(source.id))).toHaveLength(4);
351
+ });
352
+ });
@@ -198,7 +198,11 @@ describe("forkConversationForRetrospective", () => {
198
198
  const assistant = await addMessage(source.id, "assistant", "see mockup", {
199
199
  skipIndexing: true,
200
200
  });
201
- const uploaded = uploadAttachment("wireframe.png", "image/png", "iVBORw0K");
201
+ const uploaded = await uploadAttachment(
202
+ "wireframe.png",
203
+ "image/png",
204
+ "iVBORw0K",
205
+ );
202
206
  linkAttachmentToMessage(assistant.id, uploaded.id, 0);
203
207
 
204
208
  const asyncFork = await forkConversationForRetrospective({
@@ -257,7 +257,7 @@ describe("conversation_move_to_group tool", () => {
257
257
  expect(result.content).toContain("no conversation found");
258
258
  });
259
259
 
260
- test("notes hidden conversation types", async () => {
260
+ test("tells the model a custom group surfaces a hidden conversation", async () => {
261
261
  createConversation({ id: "conv-bg", conversationType: "background" });
262
262
  await executeConversationGroupCreate({ name: "Visible" }, ctx);
263
263
 
@@ -267,7 +267,37 @@ describe("conversation_move_to_group tool", () => {
267
267
  );
268
268
 
269
269
  expect(result.isError).toBe(false);
270
- expect(result.content).toContain("hidden from the sidebar");
270
+ expect(result.content).toContain("surfaces it into the sidebar");
271
+ });
272
+
273
+ test("does not claim a move to Recents surfaces a hidden conversation", async () => {
274
+ // Recents is a removal target, not a promotion: the write path leaves an
275
+ // unpromoted background row hidden there, so saying otherwise would report
276
+ // an outcome to the model that did not happen.
277
+ createConversation({ id: "conv-bg-all", conversationType: "background" });
278
+
279
+ const result = await executeConversationMoveToGroup(
280
+ { group: "system:all", conversation_id: "conv-bg-all" },
281
+ ctx,
282
+ );
283
+
284
+ expect(result.isError).toBe(false);
285
+ expect(result.content).not.toContain("surfaces it into the sidebar");
286
+ });
287
+
288
+ test("does not claim a move to Background surfaces a hidden conversation", async () => {
289
+ createConversation({
290
+ id: "conv-bg-demote",
291
+ conversationType: "background",
292
+ });
293
+
294
+ const result = await executeConversationMoveToGroup(
295
+ { group: "system:background", conversation_id: "conv-bg-demote" },
296
+ ctx,
297
+ );
298
+
299
+ expect(result.isError).toBe(false);
300
+ expect(result.content).not.toContain("surfaces it into the sidebar");
271
301
  });
272
302
 
273
303
  test("rejects missing group", async () => {
@@ -1,4 +1,16 @@
1
- import { afterAll, beforeEach, describe, expect, test } from "bun:test";
1
+ import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
2
+
3
+ // The profiles pinned below are managed presets judged for real by the
4
+ // session handler's availability guard. Stand in for a signed-in platform
5
+ // session so the pin path under test is the healthy one — same pattern as
6
+ // inference-profile-session-handler.test.ts.
7
+ mock.module("../providers/platform-proxy/context.js", () => ({
8
+ resolveManagedProxyContext: async () => ({
9
+ enabled: true,
10
+ platformBaseUrl: "https://platform.example",
11
+ assistantApiKey: "key",
12
+ }),
13
+ }));
2
14
 
3
15
  import {
4
16
  conversationMetadataSyncTag,
@@ -10,6 +22,7 @@ import {
10
22
  } from "../persistence/conversation-crud.js";
11
23
  import { getDb } from "../persistence/db-connection.js";
12
24
  import { initializeDb } from "../persistence/db-init.js";
25
+ import { providerConnections } from "../persistence/schema/inference.js";
13
26
  import { assistantEventHub } from "../runtime/assistant-event-hub.js";
14
27
  import { ROUTES } from "../runtime/routes/conversation-management-routes.js";
15
28
  import { BadRequestError, NotFoundError } from "../runtime/routes/errors.js";
@@ -18,6 +31,19 @@ import { waitFor } from "./helpers/wait-for.js";
18
31
 
19
32
  await initializeDb();
20
33
 
34
+ // The Vellum-managed connection row the managed presets resolve to.
35
+ getDb()
36
+ .insert(providerConnections)
37
+ .values({
38
+ name: "vellum",
39
+ provider: "vellum",
40
+ auth: JSON.stringify({ type: "platform" }),
41
+ createdAt: Date.now(),
42
+ updatedAt: Date.now(),
43
+ })
44
+ .onConflictDoNothing()
45
+ .run();
46
+
21
47
  const profileRoute = ROUTES.find(
22
48
  (r) => r.operationId === "setConversationInferenceProfile",
23
49
  )!;
@@ -3,11 +3,12 @@
3
3
  *
4
4
  * A conversation's system prompt is built once at construction and frozen for
5
5
  * every turn (the agent loop never re-resolves it), so the persona slot must
6
- * resolve correctly here. With no construction-time identity the default-build
7
- * path must warm the gateway guardian binding BEFORE building (else a cold cache
8
- * pins `users/default.md`); a channel-routed conversation that already carries
9
- * the requester's trust context must build with it so the requester's profile
10
- * resolves instead of the guardian/default one.
6
+ * resolve correctly here. With no construction-time identity OR a
7
+ * guardian-class trust context (background/scheduled turns, memory
8
+ * consolidation, managed desktop) the guardian binding must be warmed BEFORE
9
+ * building (else a cold cache pins `users/default.md`); a channel-routed
10
+ * conversation carrying a non-guardian requester trust context must build with
11
+ * it directly so the requester's profile resolves via the DB contact lookup.
11
12
  *
12
13
  * Dependency seams are injected so this exercises the sequencing without mocking
13
14
  * the widely-imported guardian-delivery / system-prompt modules (those global
@@ -15,10 +16,7 @@
15
16
  */
16
17
  import { describe, expect, test } from "bun:test";
17
18
 
18
- import {
19
- resolveInitialSystemPrompt,
20
- warmGuardianBindings,
21
- } from "../daemon/conversation-initial-prompt.js";
19
+ import { resolveInitialSystemPrompt } from "../daemon/conversation-initial-prompt.js";
22
20
  import type { ConversationCreateOptions } from "../daemon/handlers/shared.js";
23
21
  import type { TrustContext } from "../daemon/trust-context-types.js";
24
22
 
@@ -47,18 +45,22 @@ const REQUESTER_TRUST = {
47
45
  requesterExternalUserId: "user-123",
48
46
  } as unknown as TrustContext;
49
47
 
50
- describe("warmGuardianBindings", () => {
51
- test("warms both the vellum-channel key and the unfiltered fallback key", async () => {
52
- const keys: string[] = [];
53
- await warmGuardianBindings(async (input) => {
54
- keys.push(input?.channelTypes?.join(",") ?? "ALL");
55
- return null;
56
- });
57
- // Both keys the persona resolver reads — peekGuardianForChannel("vellum")
58
- // and its peekAnyGuardian() fallback must be warmed.
59
- expect(keys.sort()).toEqual(["ALL", "vellum"]);
60
- });
61
- });
48
+ // The shape scheduler execute/reuse runs, memory consolidation, and other
49
+ // runBackgroundJob callers pass at construction: guardian class, no
50
+ // per-actor identity.
51
+ const GUARDIAN_BACKGROUND_TRUST = {
52
+ sourceChannel: "vellum",
53
+ trustClass: "guardian",
54
+ } as unknown as TrustContext;
55
+
56
+ // Managed desktop: guardian class WITH a requester principal id. The persona
57
+ // resolution may still need the guardian-delivery cache (contact lookup miss
58
+ // or null userFile falls through to the guardian file).
59
+ const GUARDIAN_REQUESTER_TRUST = {
60
+ sourceChannel: "vellum",
61
+ trustClass: "guardian",
62
+ requesterExternalUserId: "principal-123",
63
+ } as unknown as TrustContext;
62
64
 
63
65
  describe("resolveInitialSystemPrompt", () => {
64
66
  test("no construction-time identity: warms the guardian binding, then builds with no trust context", async () => {
@@ -72,7 +74,7 @@ describe("resolveInitialSystemPrompt", () => {
72
74
  expect(builtWith).toEqual([undefined]);
73
75
  });
74
76
 
75
- test("channel-routed: threads the requester trust context and skips the guardian warm", async () => {
77
+ test("non-guardian channel-routed: threads the requester trust context and skips the guardian warm", async () => {
76
78
  const { calls, builtWith, deps } = spyDeps();
77
79
  const result = await resolveInitialSystemPrompt(
78
80
  { trustContext: REQUESTER_TRUST } as ConversationCreateOptions,
@@ -86,6 +88,33 @@ describe("resolveInitialSystemPrompt", () => {
86
88
  expect(builtWith).toEqual([REQUESTER_TRUST]);
87
89
  });
88
90
 
91
+ test("guardian-class background shape: warms the guardian binding, then builds with the trust context", async () => {
92
+ const { calls, builtWith, deps } = spyDeps();
93
+ const result = await resolveInitialSystemPrompt(
94
+ { trustContext: GUARDIAN_BACKGROUND_TRUST } as ConversationCreateOptions,
95
+ deps,
96
+ );
97
+
98
+ expect(result).toBe("BUILD");
99
+ // Scheduled tasks and memory consolidation construct conversations in
100
+ // worker processes whose guardian-delivery cache starts cold; the warm
101
+ // MUST precede the build or the persona slot pins users/default.md.
102
+ expect(calls).toEqual(["warm", "build"]);
103
+ expect(builtWith).toEqual([GUARDIAN_BACKGROUND_TRUST]);
104
+ });
105
+
106
+ test("guardian-class with requester identity (managed desktop): still warms before building", async () => {
107
+ const { calls, builtWith, deps } = spyDeps();
108
+ const result = await resolveInitialSystemPrompt(
109
+ { trustContext: GUARDIAN_REQUESTER_TRUST } as ConversationCreateOptions,
110
+ deps,
111
+ );
112
+
113
+ expect(result).toBe("BUILD");
114
+ expect(calls).toEqual(["warm", "build"]);
115
+ expect(builtWith).toEqual([GUARDIAN_REQUESTER_TRUST]);
116
+ });
117
+
89
118
  test("an explicit override is used verbatim and skips the warm and build", async () => {
90
119
  const { calls, deps } = spyDeps();
91
120
  const result = await resolveInitialSystemPrompt(
@@ -0,0 +1,119 @@
1
+ /**
2
+ * `getOrCreateConversation` doubles as the first-conversation bootstrap
3
+ * bookkeeper: the first standard conversation is the onboarding one (keep
4
+ * BOOTSTRAP.md), and the second means onboarding is over (delete it).
5
+ *
6
+ * Onboarding also mints internal side threads (research, personality rewrite,
7
+ * identity rewrite) as `conversationType: "background"` rows that the user
8
+ * never opens. Those must be completely inert with respect to this bookkeeping:
9
+ * if a hidden side thread consumed the first-conversation slot, the user's
10
+ * FIRST visible chat would look like the second conversation and lose
11
+ * BOOTSTRAP.md before its opening turn.
12
+ */
13
+
14
+ import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
15
+ import { join } from "node:path";
16
+ import { beforeEach, describe, expect, test } from "bun:test";
17
+
18
+ import { assertNotLiveDb } from "./assert-not-live-db.js";
19
+
20
+ const testDir = process.env.VELLUM_WORKSPACE_DIR!;
21
+ const conversationsDir = join(testDir, "conversations");
22
+ mkdirSync(conversationsDir, { recursive: true });
23
+
24
+ import {
25
+ _resetFirstConversationSeenForTesting,
26
+ getOrCreateConversation,
27
+ } from "../persistence/conversation-key-store.js";
28
+ import { getDb } from "../persistence/db-connection.js";
29
+ import { initializeDb } from "../persistence/db-init.js";
30
+ import {
31
+ conversationKeys,
32
+ conversations,
33
+ } from "../persistence/schema/index.js";
34
+
35
+ await initializeDb();
36
+
37
+ const bootstrapPath = join(testDir, "BOOTSTRAP.md");
38
+ const bootstrapReferencePath = join(testDir, "BOOTSTRAP-REFERENCE.md");
39
+
40
+ function seedBootstrapFiles(): void {
41
+ writeFileSync(bootstrapPath, "# First run\n", "utf-8");
42
+ writeFileSync(bootstrapReferencePath, "# Reference\n", "utf-8");
43
+ }
44
+
45
+ beforeEach(() => {
46
+ const db = getDb();
47
+ db.delete(conversationKeys).run();
48
+ db.delete(conversations).run();
49
+
50
+ assertNotLiveDb(conversationsDir);
51
+ rmSync(conversationsDir, { recursive: true, force: true });
52
+ mkdirSync(conversationsDir, { recursive: true });
53
+
54
+ // `firstConversationSeen` is module-level (process-global) state, so each
55
+ // test has to replay the fresh-install sequence from a known-clean flag.
56
+ _resetFirstConversationSeenForTesting();
57
+ seedBootstrapFiles();
58
+ });
59
+
60
+ describe("getOrCreateConversation bootstrap bookkeeping", () => {
61
+ test("background side threads do not consume the first-conversation slot", () => {
62
+ // The exact onboarding sequence: three hidden side threads, then the
63
+ // user's first real chat.
64
+ for (const key of ["onboarding-research", "personality", "identity"]) {
65
+ const created = getOrCreateConversation(key, {
66
+ conversationType: "background",
67
+ });
68
+ expect(created.created).toBe(true);
69
+ expect(created.conversationType).toBe("background");
70
+ expect(existsSync(bootstrapPath)).toBe(true);
71
+ }
72
+
73
+ const firstVisible = getOrCreateConversation("user-first-chat");
74
+ expect(firstVisible.created).toBe(true);
75
+ expect(firstVisible.conversationType).toBe("standard");
76
+
77
+ // The user's first visible chat is still the FIRST conversation as far as
78
+ // onboarding is concerned, so its first-run context survives.
79
+ expect(existsSync(bootstrapPath)).toBe(true);
80
+ expect(existsSync(bootstrapReferencePath)).toBe(true);
81
+ });
82
+
83
+ test("a background create after the first standard one is still inert", () => {
84
+ getOrCreateConversation("user-first-chat");
85
+ expect(existsSync(bootstrapPath)).toBe(true);
86
+
87
+ getOrCreateConversation("side-thread", { conversationType: "background" });
88
+ expect(existsSync(bootstrapPath)).toBe(true);
89
+ expect(existsSync(bootstrapReferencePath)).toBe(true);
90
+ });
91
+
92
+ test("the second standard conversation still deletes the bootstrap files", () => {
93
+ getOrCreateConversation("first-chat");
94
+ expect(existsSync(bootstrapPath)).toBe(true);
95
+
96
+ getOrCreateConversation("second-chat");
97
+ expect(existsSync(bootstrapPath)).toBe(false);
98
+ expect(existsSync(bootstrapReferencePath)).toBe(false);
99
+ });
100
+
101
+ test("background creates interleaved with standard ones do not shift the slot", () => {
102
+ getOrCreateConversation("bg-a", { conversationType: "background" });
103
+ getOrCreateConversation("first-chat");
104
+ getOrCreateConversation("bg-b", { conversationType: "background" });
105
+ expect(existsSync(bootstrapPath)).toBe(true);
106
+
107
+ // Only the second STANDARD conversation ends onboarding.
108
+ getOrCreateConversation("second-chat");
109
+ expect(existsSync(bootstrapPath)).toBe(false);
110
+ });
111
+
112
+ test("resolving an existing key never re-runs the bookkeeping", () => {
113
+ const first = getOrCreateConversation("first-chat");
114
+ const again = getOrCreateConversation("first-chat");
115
+ expect(again.created).toBe(false);
116
+ expect(again.conversationId).toBe(first.conversationId);
117
+ expect(existsSync(bootstrapPath)).toBe(true);
118
+ });
119
+ });