@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
@@ -75,9 +75,9 @@ export const UNSENDABLE_IMAGE_NOTE =
75
75
  * in context, so without it the original rejected block rehydrates and
76
76
  * re-rejects on every later turn.
77
77
  */
78
- export function unsendableImageReplacement(
78
+ export async function unsendableImageReplacement(
79
79
  block: Extract<ContentBlock, { type: "image" }>,
80
- ): ContentBlock | null {
80
+ ): Promise<ContentBlock | null> {
81
81
  // Resolve reference sources to their bytes so a reloaded (referenced) image
82
82
  // is gated on the same payload/dimension caps as an inline one. When the
83
83
  // attachment can no longer be read, leave the block untouched.
@@ -118,8 +118,8 @@ export function unsendableImageReplacement(
118
118
  // pre-send — the upscale runs only here, in response to an actual
119
119
  // provider rejection. Oversized images reuse the transport downscale.
120
120
  const optimized = belowMinDimension
121
- ? upscaleImageToMinimum(resolved.data, effectiveMediaType)
122
- : optimizeImageForTransport(resolved.data, effectiveMediaType);
121
+ ? await upscaleImageToMinimum(resolved.data, effectiveMediaType)
122
+ : await optimizeImageForTransport(resolved.data, effectiveMediaType);
123
123
  if (optimized && optimized.data !== resolved.data) {
124
124
  return {
125
125
  type: "image",
@@ -145,9 +145,9 @@ export function unsendableImageReplacement(
145
145
  * image, so neither matches on a second run. Returns the number of rewritten
146
146
  * messages.
147
147
  */
148
- export function persistUnsendableImageDowngrades(
148
+ export async function persistUnsendableImageDowngrades(
149
149
  conversationId: string,
150
- ): number {
150
+ ): Promise<number> {
151
151
  let rewritten = 0;
152
152
  for (const row of getMessages(conversationId)) {
153
153
  const hasImageBlock = row.content.some(
@@ -163,39 +163,47 @@ export function persistUnsendableImageDowngrades(
163
163
  const parsed = row.content;
164
164
 
165
165
  let changed = false;
166
- const next = (parsed as ContentBlock[]).map((block): ContentBlock => {
166
+ const next: ContentBlock[] = [];
167
+ for (const block of parsed as ContentBlock[]) {
167
168
  if (block.type === "image") {
168
- const replacement = unsendableImageReplacement(block);
169
+ const replacement = await unsendableImageReplacement(block);
169
170
  if (!replacement) {
170
- return block;
171
+ next.push(block);
172
+ continue;
171
173
  }
172
174
  changed = true;
173
- return replacement;
175
+ next.push(replacement);
176
+ continue;
174
177
  }
175
178
  // Images returned by a tool (e.g. browser_screenshot) live inside the
176
179
  // tool_result's contentBlocks, not as top-level blocks. Downgrade them
177
180
  // in place so the tool_use/tool_result pairing stays intact.
178
181
  if (block.type === "tool_result" && block.contentBlocks?.length) {
179
182
  let nestedChanged = false;
180
- const contentBlocks = block.contentBlocks.map((cb): ContentBlock => {
183
+ const contentBlocks: ContentBlock[] = [];
184
+ for (const cb of block.contentBlocks) {
181
185
  if (cb.type !== "image") {
182
- return cb;
186
+ contentBlocks.push(cb);
187
+ continue;
183
188
  }
184
- const replacement = unsendableImageReplacement(cb);
189
+ const replacement = await unsendableImageReplacement(cb);
185
190
  if (!replacement) {
186
- return cb;
191
+ contentBlocks.push(cb);
192
+ continue;
187
193
  }
188
194
  nestedChanged = true;
189
- return replacement;
190
- });
195
+ contentBlocks.push(replacement);
196
+ }
191
197
  if (!nestedChanged) {
192
- return block;
198
+ next.push(block);
199
+ continue;
193
200
  }
194
201
  changed = true;
195
- return { ...block, contentBlocks };
202
+ next.push({ ...block, contentBlocks });
203
+ continue;
196
204
  }
197
- return block;
198
- });
205
+ next.push(block);
206
+ }
199
207
  if (!changed) {
200
208
  continue;
201
209
  }
@@ -240,50 +248,52 @@ function messageHasImageBlock(content: ReadonlyArray<ContentBlock>): boolean {
240
248
  * caller must not retry an unchanged history: it would resend the identical
241
249
  * rejected image and falsely report a correction.
242
250
  */
243
- export function recoverUnsendableImages(messages: ReadonlyArray<Message>): {
251
+ export async function recoverUnsendableImages(
252
+ messages: ReadonlyArray<Message>,
253
+ ): Promise<{
244
254
  messages: Message[];
245
255
  changed: boolean;
246
- } {
256
+ }> {
247
257
  let changed = false;
248
- const recovered = messages.map((msg) => {
249
- if (!Array.isArray(msg.content)) {
250
- return msg;
251
- }
252
- if (!messageHasImageBlock(msg.content)) {
253
- return msg;
258
+ const recovered: Message[] = [];
259
+ for (const msg of messages) {
260
+ if (!Array.isArray(msg.content) || !messageHasImageBlock(msg.content)) {
261
+ recovered.push(msg);
262
+ continue;
254
263
  }
255
- return {
256
- ...msg,
257
- content: msg.content.flatMap((b): ContentBlock[] => {
258
- if (b.type === "image") {
259
- const replacement = unsendableImageReplacement(b);
264
+ const content: ContentBlock[] = [];
265
+ for (const b of msg.content) {
266
+ if (b.type === "image") {
267
+ const replacement = await unsendableImageReplacement(b);
268
+ if (replacement) {
269
+ changed = true;
270
+ content.push(replacement);
271
+ } else {
272
+ content.push(b);
273
+ }
274
+ continue;
275
+ }
276
+ if (b.type === "tool_result" && b.contentBlocks?.length) {
277
+ const contentBlocks: ContentBlock[] = [];
278
+ for (const cb of b.contentBlocks) {
279
+ if (cb.type !== "image") {
280
+ contentBlocks.push(cb);
281
+ continue;
282
+ }
283
+ const replacement = await unsendableImageReplacement(cb);
260
284
  if (replacement) {
261
285
  changed = true;
262
- return [replacement];
286
+ contentBlocks.push(replacement);
287
+ } else {
288
+ contentBlocks.push(cb);
263
289
  }
264
- return [b];
265
290
  }
266
- if (b.type === "tool_result" && b.contentBlocks?.length) {
267
- return [
268
- {
269
- ...b,
270
- contentBlocks: b.contentBlocks.map((cb) => {
271
- if (cb.type !== "image") {
272
- return cb;
273
- }
274
- const replacement = unsendableImageReplacement(cb);
275
- if (replacement) {
276
- changed = true;
277
- return replacement;
278
- }
279
- return cb;
280
- }),
281
- },
282
- ];
283
- }
284
- return [b];
285
- }),
286
- };
287
- });
291
+ content.push({ ...b, contentBlocks });
292
+ continue;
293
+ }
294
+ content.push(b);
295
+ }
296
+ recovered.push({ ...msg, content });
297
+ }
288
298
  return { messages: recovered, changed };
289
299
  }
@@ -356,7 +356,7 @@ export const defaultToolErrorPlugin: Plugin = {
356
356
  * file_list) with no user-facing text, or (on loop-prone models such as Kimi
357
357
  * K2.6 and MiniMax M3) the model re-issuing a byte-identical exploration call — and nudges
358
358
  * the model via `additionalContext` to summarize progress for the user and
359
- * delegate the remaining investigation to an `investigator` subagent rather
359
+ * delegate the remaining investigation to a `researcher` subagent rather
360
360
  * than continuing inline.
361
361
  */
362
362
  export const defaultExplorationDriftPlugin: Plugin = {
@@ -396,7 +396,7 @@ describe("countConversations", () => {
396
396
  const priv = createConversation("private-1");
397
397
  setConversationType(priv.id, "private");
398
398
 
399
- expect(countConversations("background")).toBe(2);
399
+ expect(countConversations({ conversationType: "background" })).toBe(2);
400
400
  });
401
401
 
402
402
  test("includes standard conversations with group_id system:background in background count", () => {
@@ -411,13 +411,13 @@ describe("countConversations", () => {
411
411
  createConversation("foreground-1");
412
412
 
413
413
  // WHEN counting background conversations
414
- const bgCount = countConversations("background");
414
+ const bgCount = countConversations({ conversationType: "background" });
415
415
 
416
416
  // THEN the heartbeat conversation is included
417
417
  expect(bgCount).toBe(1);
418
418
 
419
419
  // AND excluded from the foreground count
420
- expect(countConversations("standard")).toBe(1);
420
+ expect(countConversations({ conversationType: "standard" })).toBe(1);
421
421
  });
422
422
 
423
423
  test("excludes standard conversations with group_id system:background from foreground count", () => {
@@ -432,7 +432,7 @@ describe("countConversations", () => {
432
432
 
433
433
  // WHEN counting foreground conversations
434
434
  // THEN the heartbeat is excluded
435
- expect(countConversations("standard")).toBe(2);
435
+ expect(countConversations({ conversationType: "standard" })).toBe(2);
436
436
  });
437
437
 
438
438
  test('"scheduled" count returns only scheduled rows', () => {
@@ -443,7 +443,7 @@ describe("countConversations", () => {
443
443
 
444
444
  // WHEN counting scheduled conversations
445
445
  // THEN only the scheduled row is counted (background is excluded)
446
- expect(countConversations("scheduled")).toBe(1);
446
+ expect(countConversations({ conversationType: "scheduled" })).toBe(1);
447
447
  });
448
448
 
449
449
  describe("archiveStatus", () => {
@@ -473,7 +473,12 @@ describe("countConversations", () => {
473
473
  a2.id,
474
474
  );
475
475
 
476
- expect(countConversations("standard", "archived")).toBe(2);
476
+ expect(
477
+ countConversations({
478
+ conversationType: "standard",
479
+ archiveStatus: "archived",
480
+ }),
481
+ ).toBe(2);
477
482
  });
478
483
 
479
484
  test('archiveStatus "all" returns both', () => {
@@ -486,7 +491,12 @@ describe("countConversations", () => {
486
491
  archived.id,
487
492
  );
488
493
 
489
- expect(countConversations("standard", "all")).toBe(2);
494
+ expect(
495
+ countConversations({
496
+ conversationType: "standard",
497
+ archiveStatus: "all",
498
+ }),
499
+ ).toBe(2);
490
500
  });
491
501
  });
492
502
  });
@@ -511,7 +521,10 @@ describe("listConversations", () => {
511
521
  createConversation("foreground-1");
512
522
 
513
523
  // WHEN listing background conversations
514
- const bgList = listConversations(100, "background");
524
+ const bgList = listConversations({
525
+ limit: 100,
526
+ conversationType: "background",
527
+ });
515
528
 
516
529
  // THEN both background and heartbeat conversations are returned
517
530
  expect(bgList).toHaveLength(2);
@@ -532,7 +545,10 @@ describe("listConversations", () => {
532
545
  createConversation("foreground-1");
533
546
 
534
547
  // WHEN listing foreground conversations
535
- const fgList = listConversations(100, "standard");
548
+ const fgList = listConversations({
549
+ limit: 100,
550
+ conversationType: "standard",
551
+ });
536
552
 
537
553
  // THEN only the foreground conversation is returned
538
554
  expect(fgList).toHaveLength(1);
@@ -549,14 +565,20 @@ describe("listConversations", () => {
549
565
  );
550
566
 
551
567
  // WHEN listing background conversations
552
- const bgList = listConversations(100, "background");
568
+ const bgList = listConversations({
569
+ limit: 100,
570
+ conversationType: "background",
571
+ });
553
572
 
554
573
  // THEN it appears in the background list
555
574
  expect(bgList).toHaveLength(1);
556
575
  expect(bgList[0]!.title).toBe("schedule-routed");
557
576
 
558
577
  // AND not in the foreground list
559
- const fgList = listConversations(100, "standard");
578
+ const fgList = listConversations({
579
+ limit: 100,
580
+ conversationType: "standard",
581
+ });
560
582
  expect(fgList).toHaveLength(0);
561
583
  });
562
584
 
@@ -567,7 +589,10 @@ describe("listConversations", () => {
567
589
  createConversation("foreground-1");
568
590
 
569
591
  // WHEN listing scheduled conversations
570
- const scheduledList = listConversations(100, "scheduled");
592
+ const scheduledList = listConversations({
593
+ limit: 100,
594
+ conversationType: "scheduled",
595
+ });
571
596
 
572
597
  // THEN only the scheduled conversation is returned
573
598
  expect(scheduledList).toHaveLength(1);
@@ -592,7 +617,10 @@ describe("listConversations", () => {
592
617
  );
593
618
 
594
619
  // WHEN listing scheduled conversations
595
- const scheduledList = listConversations(100, "scheduled");
620
+ const scheduledList = listConversations({
621
+ limit: 100,
622
+ conversationType: "scheduled",
623
+ });
596
624
 
597
625
  // THEN only the system:scheduled row appears
598
626
  expect(scheduledList).toHaveLength(1);
@@ -608,7 +636,10 @@ describe("listConversations", () => {
608
636
  });
609
637
 
610
638
  // WHEN listing scheduled conversations
611
- const scheduledList = listConversations(100, "scheduled");
639
+ const scheduledList = listConversations({
640
+ limit: 100,
641
+ conversationType: "scheduled",
642
+ });
612
643
 
613
644
  // THEN the subagent run is excluded
614
645
  expect(scheduledList).toHaveLength(0);
@@ -627,7 +658,10 @@ describe("listConversations", () => {
627
658
  );
628
659
 
629
660
  // WHEN listing without an explicit archiveStatus
630
- const rows = listConversations(100, "standard");
661
+ const rows = listConversations({
662
+ limit: 100,
663
+ conversationType: "standard",
664
+ });
631
665
 
632
666
  // THEN only the live conversation appears
633
667
  expect(rows).toHaveLength(1);
@@ -646,7 +680,11 @@ describe("listConversations", () => {
646
680
  );
647
681
 
648
682
  // WHEN listing with archiveStatus "archived"
649
- const rows = listConversations(100, "standard", 0, "archived");
683
+ const rows = listConversations({
684
+ limit: 100,
685
+ conversationType: "standard",
686
+ archiveStatus: "archived",
687
+ });
650
688
 
651
689
  // THEN only the archived conversation appears
652
690
  expect(rows).toHaveLength(1);
@@ -665,7 +703,11 @@ describe("listConversations", () => {
665
703
  );
666
704
 
667
705
  // WHEN listing with archiveStatus "all"
668
- const rows = listConversations(100, "standard", 0, "all");
706
+ const rows = listConversations({
707
+ limit: 100,
708
+ conversationType: "standard",
709
+ archiveStatus: "all",
710
+ });
669
711
 
670
712
  // THEN both conversations appear
671
713
  expect(rows).toHaveLength(2);
@@ -690,7 +732,11 @@ describe("listConversations", () => {
690
732
  );
691
733
 
692
734
  // WHEN listing archived background conversations
693
- const rows = listConversations(100, "background", 0, "archived");
735
+ const rows = listConversations({
736
+ limit: 100,
737
+ conversationType: "background",
738
+ archiveStatus: "archived",
739
+ });
694
740
 
695
741
  // THEN only the archived background row appears
696
742
  expect(rows).toHaveLength(1);
@@ -0,0 +1,242 @@
1
+ /**
2
+ * Outcome-truthfulness contract between job handlers and the jobs worker.
3
+ *
4
+ * Handlers that report failure through a RETURNED domain outcome (the
5
+ * retrospective's `wake_failed` / `no_usable_output` / `source_processing`,
6
+ * the consolidation's `run_failed`) must not leave their job rows reading
7
+ * `completed` with a null `last_error`. The registration-site adapters in
8
+ * `job-handlers.ts` translate domain outcomes into `JobQueueResolution`s and
9
+ * the worker applies them, so the persisted `memory_jobs.status` reflects the
10
+ * handler's actual outcome.
11
+ *
12
+ * Also covers the store-level late-transition guards: once the stalled-job
13
+ * watchdog fails a row, a late `completeMemoryJob` from the abandoned
14
+ * execution must not flip it back to `completed`.
15
+ */
16
+ import { mkdtempSync, rmSync } from "node:fs";
17
+ import { tmpdir } from "node:os";
18
+ import { join } from "node:path";
19
+ import {
20
+ afterAll,
21
+ beforeAll,
22
+ beforeEach,
23
+ describe,
24
+ expect,
25
+ mock,
26
+ test,
27
+ } from "bun:test";
28
+
29
+ import { eq } from "drizzle-orm";
30
+
31
+ import type { MemoryRetrospectiveOutcome } from "../memory-retrospective-job.js";
32
+ import type { ConsolidationOutcome } from "../substrate/consolidation-job.js";
33
+
34
+ // Scripted handler outcomes, set per-test.
35
+ let retrospectiveOutcome: MemoryRetrospectiveOutcome = {
36
+ kind: "no_new_messages",
37
+ };
38
+ let consolidationOutcome: ConsolidationOutcome = { kind: "empty_buffer" };
39
+
40
+ mock.module("../memory-retrospective-job.js", () => ({
41
+ memoryRetrospectiveJob: async () => retrospectiveOutcome,
42
+ STALE_SOURCE_PROCESSING_OVERRIDE_MS: 6 * 60 * 60 * 1000,
43
+ }));
44
+
45
+ mock.module("../substrate/consolidation-job.js", () => ({
46
+ memoryV2ConsolidateJob: async () => consolidationOutcome,
47
+ // Scheduler helpers the worker reads on every pass.
48
+ countBufferLines: () => 0,
49
+ readConsolidationFailureState: () => null,
50
+ }));
51
+
52
+ mock.module("../graph/graph-search.js", () => ({
53
+ embedGraphNodeDirect: async () => {},
54
+ embedGraphNodeJob: async (): Promise<void> => {},
55
+ enqueueGraphNodeEmbed: () => {},
56
+ embedGraphTriggerJob: async (): Promise<void> => {},
57
+ enqueueGraphTriggerEmbed: () => {},
58
+ }));
59
+
60
+ mock.module("../v1/graph/graph-search.js", () => ({
61
+ searchGraphNodes: async () => [],
62
+ }));
63
+
64
+ mock.module("../../../../persistence/db-maintenance.js", () => ({
65
+ maybeRunDbMaintenance: async () => {},
66
+ maybeRunPassiveWalCheckpoint: async () => {},
67
+ }));
68
+
69
+ const tmpWorkspace = mkdtempSync(
70
+ join(tmpdir(), "jobs-worker-outcome-contract-"),
71
+ );
72
+ const previousWorkspaceEnv = process.env.VELLUM_WORKSPACE_DIR;
73
+ process.env.VELLUM_WORKSPACE_DIR = tmpWorkspace;
74
+
75
+ import { getMemoryDb } from "../../../../persistence/db-connection.js";
76
+ import { initializeDb } from "../../../../persistence/db-init.js";
77
+ import { _resetQdrantBreaker } from "../../../../persistence/embeddings/qdrant-circuit-breaker.js";
78
+ import {
79
+ completeMemoryJob,
80
+ enqueueMemoryJob,
81
+ failStalledJobs,
82
+ } from "../../../../persistence/jobs-store.js";
83
+ import { memoryJobs } from "../../../../persistence/schema/index.js";
84
+ import { registerMemoryPluginJobHandlers } from "../job-handler-registration.js";
85
+ import { runMemoryJobsOnce } from "../jobs-worker.js";
86
+
87
+ function jobRow(jobId: string) {
88
+ return getMemoryDb()!
89
+ .select()
90
+ .from(memoryJobs)
91
+ .where(eq(memoryJobs.id, jobId))
92
+ .get();
93
+ }
94
+
95
+ describe("job outcome truthfulness", () => {
96
+ beforeAll(async () => {
97
+ registerMemoryPluginJobHandlers();
98
+ await initializeDb();
99
+ });
100
+
101
+ afterAll(() => {
102
+ if (previousWorkspaceEnv === undefined) {
103
+ delete process.env.VELLUM_WORKSPACE_DIR;
104
+ } else {
105
+ process.env.VELLUM_WORKSPACE_DIR = previousWorkspaceEnv;
106
+ }
107
+ rmSync(tmpWorkspace, { recursive: true, force: true });
108
+ });
109
+
110
+ beforeEach(() => {
111
+ getMemoryDb()!.run("DELETE FROM memory_jobs");
112
+ _resetQdrantBreaker();
113
+ retrospectiveOutcome = { kind: "no_new_messages" };
114
+ consolidationOutcome = { kind: "empty_buffer" };
115
+ });
116
+
117
+ test("retrospective wake_failed dead-letters the row with an honest last_error", async () => {
118
+ retrospectiveOutcome = { kind: "wake_failed", reason: "run_error" };
119
+ const jobId = enqueueMemoryJob("memory_retrospective", {
120
+ conversationId: "conv-1",
121
+ });
122
+
123
+ await runMemoryJobsOnce();
124
+
125
+ const row = jobRow(jobId);
126
+ expect(row?.status).toBe("failed");
127
+ expect(row?.lastError).toContain("retrospective wake failed");
128
+ expect(row?.lastError).toContain("run_error");
129
+ });
130
+
131
+ test("retrospective no_usable_output dead-letters the row with the failure detail", async () => {
132
+ retrospectiveOutcome = {
133
+ kind: "no_usable_output",
134
+ reason: "run persisted no memory-writing tool call",
135
+ };
136
+ const jobId = enqueueMemoryJob("memory_retrospective", {
137
+ conversationId: "conv-1",
138
+ });
139
+
140
+ await runMemoryJobsOnce();
141
+
142
+ const row = jobRow(jobId);
143
+ expect(row?.status).toBe("failed");
144
+ expect(row?.lastError).toContain("no usable output");
145
+ });
146
+
147
+ test("retrospective source_processing defers the SAME row on the deferral counter", async () => {
148
+ retrospectiveOutcome = { kind: "source_processing" };
149
+ const jobId = enqueueMemoryJob("memory_retrospective", {
150
+ conversationId: "conv-1",
151
+ });
152
+
153
+ const before = Date.now();
154
+ await runMemoryJobsOnce();
155
+
156
+ const row = jobRow(jobId);
157
+ expect(row?.status).toBe("pending");
158
+ expect(row?.deferrals).toBe(1);
159
+ expect(row?.attempts).toBe(0);
160
+ expect(row!.runAfter).toBeGreaterThan(before);
161
+ // Exactly one row for the conversation: no fresh row minted per attempt.
162
+ const rows = getMemoryDb()!
163
+ .select()
164
+ .from(memoryJobs)
165
+ .where(eq(memoryJobs.type, "memory_retrospective"))
166
+ .all();
167
+ expect(rows).toHaveLength(1);
168
+ });
169
+
170
+ test("source_processing deferral budget exhausts into an honest terminal failure", async () => {
171
+ retrospectiveOutcome = { kind: "source_processing" };
172
+ const jobId = enqueueMemoryJob("memory_retrospective", {
173
+ conversationId: "conv-1",
174
+ });
175
+ // Spend 49 of the 50 deferrals up front; the next pass is the last one.
176
+ getMemoryDb()!
177
+ .update(memoryJobs)
178
+ .set({ deferrals: 49 })
179
+ .where(eq(memoryJobs.id, jobId))
180
+ .run();
181
+
182
+ await runMemoryJobsOnce();
183
+
184
+ const row = jobRow(jobId);
185
+ expect(row?.status).toBe("failed");
186
+ expect(row?.lastError).toContain("mid-turn");
187
+ });
188
+
189
+ test("retrospective success and benign no-ops still complete", async () => {
190
+ retrospectiveOutcome = { kind: "no_new_messages" };
191
+ const jobId = enqueueMemoryJob("memory_retrospective", {
192
+ conversationId: "conv-1",
193
+ });
194
+
195
+ await runMemoryJobsOnce();
196
+
197
+ expect(jobRow(jobId)?.status).toBe("completed");
198
+ });
199
+
200
+ test("consolidation run_failed dead-letters the row with the failure reason", async () => {
201
+ consolidationOutcome = { kind: "run_failed", reason: "timeout" };
202
+ const jobId = enqueueMemoryJob("memory_v2_consolidate", {});
203
+
204
+ await runMemoryJobsOnce();
205
+
206
+ const row = jobRow(jobId);
207
+ expect(row?.status).toBe("failed");
208
+ expect(row?.lastError).toContain("consolidation run failed");
209
+ expect(row?.lastError).toContain("timeout");
210
+ });
211
+
212
+ test("consolidation skips (locked/empty) still complete", async () => {
213
+ consolidationOutcome = { kind: "locked", holder: "1234 0 consolidation" };
214
+ const jobId = enqueueMemoryJob("memory_v2_consolidate", {});
215
+
216
+ await runMemoryJobsOnce();
217
+
218
+ expect(jobRow(jobId)?.status).toBe("completed");
219
+ });
220
+
221
+ test("late completion cannot overwrite a watchdog-failed row", async () => {
222
+ const jobId = enqueueMemoryJob("memory_retrospective", {
223
+ conversationId: "conv-1",
224
+ });
225
+ // Simulate a claimed row whose execution went silent long enough for the
226
+ // stalled-job watchdog to fail it.
227
+ getMemoryDb()!
228
+ .update(memoryJobs)
229
+ .set({ status: "running", startedAt: Date.now() - 60 * 60 * 1000 })
230
+ .where(eq(memoryJobs.id, jobId))
231
+ .run();
232
+ expect(failStalledJobs(30 * 60 * 1000)).toBe(1);
233
+ expect(jobRow(jobId)?.status).toBe("failed");
234
+
235
+ // The abandoned execution finally returns and reports success.
236
+ completeMemoryJob(jobId);
237
+
238
+ const row = jobRow(jobId);
239
+ expect(row?.status).toBe("failed");
240
+ expect(row?.lastError).toContain("timed out");
241
+ });
242
+ });
@@ -39,12 +39,15 @@ getMemoryDb();
39
39
  const SWEEP_INTERVAL_MS = 8 * 60 * 60 * 1000; // 8h default
40
40
 
41
41
  function buildConfig(
42
- overrides: { memoryEnabled?: boolean } = {},
42
+ overrides: { memoryEnabled?: boolean; retrospectiveEnabled?: boolean } = {},
43
43
  ): AssistantConfig {
44
44
  const config = applyNestedDefaults({}) as unknown as AssistantConfig;
45
45
  if (overrides.memoryEnabled !== undefined) {
46
46
  config.memory.enabled = overrides.memoryEnabled;
47
47
  }
48
+ if (overrides.retrospectiveEnabled !== undefined) {
49
+ config.memory.retrospective.enabled = overrides.retrospectiveEnabled;
50
+ }
48
51
  return config;
49
52
  }
50
53
 
@@ -118,6 +121,26 @@ describe("maybeEnqueueRetrospectiveSweepJob", () => {
118
121
  expect(countSweepJobs()).toBe(0);
119
122
  });
120
123
 
124
+ test("does not enqueue when retrospectives are disabled, and leaves the checkpoint alone", () => {
125
+ const now = Date.now();
126
+ const stale = staleCheckpoint(now);
127
+ setMemoryCheckpoint(RETROSPECTIVE_SWEEP_CHECKPOINT, stale);
128
+
129
+ const enqueued = maybeEnqueueRetrospectiveSweepJob(
130
+ buildConfig({ retrospectiveEnabled: false }),
131
+ now,
132
+ );
133
+
134
+ expect(enqueued).toBe(false);
135
+ expect(countSweepJobs()).toBe(0);
136
+ // Checkpoint untouched, so re-enabling sweeps on the next tick rather
137
+ // than waiting out another full interval.
138
+ expect(getMemoryCheckpoint(RETROSPECTIVE_SWEEP_CHECKPOINT)).toBe(stale);
139
+
140
+ expect(maybeEnqueueRetrospectiveSweepJob(buildConfig(), now)).toBe(true);
141
+ expect(countSweepJobs()).toBe(1);
142
+ });
143
+
121
144
  test("does not stack a second sweep while one is still in flight, but advances the checkpoint", () => {
122
145
  const now = Date.now();
123
146
  // A prior sweep is already pending.