@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
@@ -32,6 +32,13 @@ let newMessages: Array<{
32
32
  metadata?: string | null;
33
33
  }> = [];
34
34
 
35
+ // Recorded `getMessagesAfter` calls, so overrideCursor tests can assert which
36
+ // cursor value actually reached the slice read.
37
+ let messagesAfterCalls: Array<{
38
+ conversationId: string;
39
+ afterId: string | null;
40
+ }> = [];
41
+
35
42
  // Prior retrospective conversation + messages. `priorRetroOwnerId` is the
36
43
  // fork-chain conversation the prior is rooted at — `findMostRecentRetrospectiveFor`
37
44
  // returns it so the GC ownership check can tell "this source's own prior"
@@ -97,6 +104,10 @@ let messagesByConversationId: Record<string, StubMessage[]> = {};
97
104
  // the real registry's semantics for conversations not in memory.
98
105
  let loadedConversations: Record<string, { processing: boolean }> = {};
99
106
 
107
+ // Persisted `processing_started_at` stamps, keyed by conversation id, backing
108
+ // the stale-flag override's age check. Absent ids read as null (stampless).
109
+ let processingStartedAtById: Record<string, number | null> = {};
110
+
100
111
  const watchdogEvents: Array<{
101
112
  checkName: string;
102
113
  value?: number | null;
@@ -155,7 +166,10 @@ mock.module("../find-most-recent-retrospective-for.js", () => ({
155
166
  }));
156
167
 
157
168
  mock.module("../../../../persistence/conversation-crud.js", () => ({
158
- getMessagesAfter: (_id: string, _afterId: string | null) => newMessages,
169
+ getMessagesAfter: (id: string, afterId: string | null) => {
170
+ messagesAfterCalls.push({ conversationId: id, afterId });
171
+ return newMessages;
172
+ },
159
173
  getMessages: (id: string) => {
160
174
  if (messagesByConversationId[id]) {
161
175
  return messagesByConversationId[id];
@@ -195,6 +209,8 @@ mock.module("../../../../persistence/conversation-crud.js", () => ({
195
209
  const entry = loadedConversations[id];
196
210
  return entry?.processing ?? false;
197
211
  },
212
+ getConversationProcessingStartedAt: (id: string) =>
213
+ processingStartedAtById[id] ?? null,
198
214
  forkConversationForRetrospective: async (params: {
199
215
  conversationId: string;
200
216
  throughMessageId?: string;
@@ -328,7 +344,7 @@ import type { MemoryJob } from "../../../../persistence/jobs-store.js";
328
344
  import {
329
345
  memoryRetrospectiveJob,
330
346
  runForkBasedRetrospective,
331
- SOURCE_PROCESSING_REQUEUE_DELAY_MS,
347
+ STALE_SOURCE_PROCESSING_OVERRIDE_MS,
332
348
  } from "../memory-retrospective-job.js";
333
349
 
334
350
  function makeConfig(
@@ -339,12 +355,14 @@ function makeConfig(
339
355
  matchConversationProfile?: boolean;
340
356
  promptPath?: string;
341
357
  requireUserActivity?: boolean;
358
+ enabled?: boolean;
342
359
  } = {},
343
360
  ): Parameters<typeof memoryRetrospectiveJob>[1] {
344
361
  return {
345
362
  memory: {
346
363
  v2: { enabled: true },
347
364
  retrospective: {
365
+ enabled: overrides.enabled ?? true,
348
366
  keepSupersededRuns: overrides.keepSupersededRuns ?? false,
349
367
  matchConversationProfile: overrides.matchConversationProfile ?? false,
350
368
  promptPath: overrides.promptPath ?? null,
@@ -397,6 +415,41 @@ function persistedInstructionText(): string {
397
415
  return blocks[0]!.text;
398
416
  }
399
417
 
418
+ /**
419
+ * Default persisted run output for the wake's fork conversation: a single
420
+ * assistant `remember` tool_use with an empty batch payload plus its
421
+ * successful tool_result. Satisfies the fail-closed advancement gate (a
422
+ * durable memory-writing call exists AND its execution succeeded) while
423
+ * appending nothing to the remembered log, so pre-gate assertions about log
424
+ * contents hold unchanged. Tests that assert the gate itself override
425
+ * `messagesByConversationId` for the fork id.
426
+ */
427
+ function defaultForkRunOutput() {
428
+ return [
429
+ {
430
+ role: "assistant",
431
+ content: JSON.stringify([
432
+ {
433
+ type: "tool_use",
434
+ id: "tu-run-1",
435
+ name: "remember",
436
+ input: { content: [] },
437
+ },
438
+ ]),
439
+ createdAt: Date.parse("2026-05-11T10:20:00Z"),
440
+ metadata: null,
441
+ },
442
+ {
443
+ role: "user",
444
+ content: JSON.stringify([
445
+ { type: "tool_result", tool_use_id: "tu-run-1", content: "Saved." },
446
+ ]),
447
+ createdAt: Date.parse("2026-05-11T10:20:01Z"),
448
+ metadata: null,
449
+ },
450
+ ];
451
+ }
452
+
400
453
  function priorRetroMessage(rememberContents: string[]) {
401
454
  return {
402
455
  role: "assistant",
@@ -421,6 +474,7 @@ describe("memoryRetrospectiveJob", () => {
421
474
  { id: "m2", createdAt: Date.parse("2026-05-11T10:05:00Z") },
422
475
  { id: "m3", createdAt: Date.parse("2026-05-11T10:10:00Z") },
423
476
  ];
477
+ messagesAfterCalls = [];
424
478
  priorRetroId = null;
425
479
  priorRetroOwnerId = "src-conv-1";
426
480
  priorRetroMessages = [];
@@ -434,8 +488,9 @@ describe("memoryRetrospectiveJob", () => {
434
488
  addMessageCalls = [];
435
489
  retrospectiveJobUpserts = [];
436
490
  conversationOverrides = {};
437
- messagesByConversationId = {};
491
+ messagesByConversationId = { "fork-conv-1": defaultForkRunOutput() };
438
492
  loadedConversations = {};
493
+ processingStartedAtById = {};
439
494
  mockResolvedUserSlug = "alice";
440
495
  resolveUserSlugCalls = [];
441
496
  mockV3TierActive = false;
@@ -460,6 +515,21 @@ describe("memoryRetrospectiveJob", () => {
460
515
  expect(forkCalls[0]!.conversationId).toBe("src-conv-1");
461
516
  });
462
517
 
518
+ test("retrospectives disabled: queued row drains as a no-op without forking", async () => {
519
+ const outcome = await memoryRetrospectiveJob(
520
+ makeJob(),
521
+ makeConfig({ enabled: false }),
522
+ );
523
+
524
+ expect(outcome.kind).toBe("disabled");
525
+ // The whole point of the switch: no fork, no wake, no pointer movement,
526
+ // so re-enabling reviews the same window the row was queued for.
527
+ expect(forkCalls).toHaveLength(0);
528
+ expect(wakeCalls).toHaveLength(0);
529
+ expect(stateUpserts).toHaveLength(0);
530
+ expect(lastRunAtBumps).toHaveLength(0);
531
+ });
532
+
463
533
  test("dormant source beyond the sweep lookback: completed as a no-op, both pointers untouched", async () => {
464
534
  conversationOverrides["src-conv-1"] = {
465
535
  source: "user",
@@ -740,6 +810,375 @@ describe("memoryRetrospectiveJob", () => {
740
810
  expect(deletedConversationIds).toEqual(["fork-conv-1"]);
741
811
  });
742
812
 
813
+ // -------------------------------------------------------------------------
814
+ // Fail-closed usable-output gate: `invoked: true` alone must not advance
815
+ // the cursor. The agent loop swallows provider rejections into a normal
816
+ // no-output return, so a wake can report success for a run that persisted
817
+ // nothing; advancement requires a durable memory-writing tool call on the
818
+ // run's own persisted tail.
819
+ // -------------------------------------------------------------------------
820
+
821
+ test("invoked wake with NO persisted run output: no_usable_output, cursor and log untouched, window retryable", async () => {
822
+ mockWakeResult = { invoked: true };
823
+ // The fork persisted nothing (e.g. a swallowed provider rejection or a
824
+ // zero-output max-tokens stop).
825
+ messagesByConversationId["fork-conv-1"] = [];
826
+
827
+ const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
828
+
829
+ expect(outcome.kind).toBe("no_usable_output");
830
+ // Cursor NOT advanced, remembered log NOT appended.
831
+ expect(stateUpserts).toHaveLength(0);
832
+ // Retryable: cooldown bump only, orphan fork cleaned up.
833
+ expect(lastRunAtBumps).toHaveLength(1);
834
+ expect(deletedConversationIds).toEqual(["fork-conv-1"]);
835
+ expect(
836
+ watchdogEvents.filter((e) => e.checkName === "memory_retrospective_run"),
837
+ ).toEqual([
838
+ {
839
+ checkName: "memory_retrospective_run",
840
+ value: 1,
841
+ detail: {
842
+ outcome: "no_usable_output",
843
+ reason: "run persisted no memory-writing tool call",
844
+ },
845
+ },
846
+ ]);
847
+ });
848
+
849
+ test("invoked wake whose run persisted only analysis text (no tool calls): no_usable_output, prior retrospective preserved", async () => {
850
+ mockWakeResult = { invoked: true };
851
+ priorRetroId = "prior-retro-1";
852
+ messagesByConversationId["fork-conv-1"] = [
853
+ {
854
+ role: "assistant",
855
+ content: JSON.stringify([
856
+ { type: "text", text: "Nothing new worth remembering here." },
857
+ ]),
858
+ createdAt: Date.parse("2026-05-11T10:20:00Z"),
859
+ metadata: null,
860
+ },
861
+ ];
862
+
863
+ const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
864
+
865
+ expect(outcome.kind).toBe("no_usable_output");
866
+ expect(stateUpserts).toHaveLength(0);
867
+ // Only this run's own fork is deleted; the prior retrospective (the
868
+ // dedup baseline for the retry) is preserved.
869
+ expect(deletedConversationIds).toEqual(["fork-conv-1"]);
870
+ });
871
+
872
+ test("run-message load failure classifies as no_usable_output (fail-closed), not success", async () => {
873
+ mockWakeResult = { invoked: true };
874
+ // Fork-kind conversation with no stamps and no leading instruction row:
875
+ // `loadRetrospectiveRunMessages` returns null (indeterminate).
876
+ conversationOverrides["fork-conv-1"] = {
877
+ source: "memory-retrospective-fork",
878
+ forkParentMessageId: null,
879
+ };
880
+ messagesByConversationId["fork-conv-1"] = [
881
+ {
882
+ role: "assistant",
883
+ content: JSON.stringify([{ type: "text", text: "copied row" }]),
884
+ createdAt: 1,
885
+ metadata: null,
886
+ },
887
+ ];
888
+
889
+ const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
890
+
891
+ expect(outcome.kind).toBe("no_usable_output");
892
+ expect(stateUpserts).toHaveLength(0);
893
+ });
894
+
895
+ test("checkpoint-persisted saves on a fork-kind tail count as usable output (rebase-after-live cannot fake no-output)", async () => {
896
+ mockWakeResult = { invoked: true };
897
+ conversationOverrides["fork-conv-1"] = {
898
+ source: "memory-retrospective-fork",
899
+ forkParentMessageId: null,
900
+ };
901
+ // Copied prefix rows carry forkSourceMessageId stamps; the run's own
902
+ // persisted tail (from checkpoint persistence, regardless of what the
903
+ // in-memory history looked like after any rebase) sits after the
904
+ // boundary and carries the durable remember call.
905
+ messagesByConversationId["fork-conv-1"] = [
906
+ {
907
+ role: "user",
908
+ content: JSON.stringify([{ type: "text", text: "copied source row" }]),
909
+ createdAt: 100,
910
+ metadata: JSON.stringify({ forkSourceMessageId: "m2" }),
911
+ },
912
+ {
913
+ role: "assistant",
914
+ content: JSON.stringify([
915
+ {
916
+ type: "tool_use",
917
+ id: "tu-ckpt-1",
918
+ name: "remember",
919
+ input: { content: ["fact persisted via checkpoint"] },
920
+ },
921
+ ]),
922
+ createdAt: 200,
923
+ metadata: null,
924
+ },
925
+ {
926
+ role: "user",
927
+ content: JSON.stringify([
928
+ { type: "tool_result", tool_use_id: "tu-ckpt-1", content: "Saved." },
929
+ ]),
930
+ createdAt: 201,
931
+ metadata: null,
932
+ },
933
+ ];
934
+
935
+ const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
936
+
937
+ expect(outcome.kind).toBe("invoked");
938
+ expect(stateUpserts).toHaveLength(1);
939
+ expect(stateUpserts[0]!.rememberedLog).toEqual([
940
+ "fact persisted via checkpoint",
941
+ ]);
942
+ });
943
+
944
+ test("scaffold_managed_skill counts as durable work even with zero remembers", async () => {
945
+ mockWakeResult = { invoked: true };
946
+ messagesByConversationId["fork-conv-1"] = [
947
+ {
948
+ role: "assistant",
949
+ content: JSON.stringify([
950
+ {
951
+ type: "tool_use",
952
+ id: "tu-skill-1",
953
+ name: "scaffold_managed_skill",
954
+ input: { name: "deploy-checklist" },
955
+ },
956
+ ]),
957
+ createdAt: Date.parse("2026-05-11T10:20:00Z"),
958
+ metadata: null,
959
+ },
960
+ {
961
+ role: "user",
962
+ content: JSON.stringify([
963
+ {
964
+ type: "tool_result",
965
+ tool_use_id: "tu-skill-1",
966
+ content: "Skill scaffolded.",
967
+ },
968
+ ]),
969
+ createdAt: Date.parse("2026-05-11T10:20:01Z"),
970
+ metadata: null,
971
+ },
972
+ ];
973
+
974
+ const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
975
+
976
+ expect(outcome.kind).toBe("invoked");
977
+ expect(stateUpserts).toHaveLength(1);
978
+ expect(stateUpserts[0]!.rememberedLog).toEqual([]);
979
+ });
980
+
981
+ test("a remember whose execution FAILED (is_error tool_result) is not durable evidence and its facts stay out of the log", async () => {
982
+ mockWakeResult = { invoked: true };
983
+ messagesByConversationId["fork-conv-1"] = [
984
+ {
985
+ role: "assistant",
986
+ content: JSON.stringify([
987
+ {
988
+ type: "tool_use",
989
+ id: "tu-fail-1",
990
+ name: "remember",
991
+ input: { content: "fact that never landed" },
992
+ },
993
+ ]),
994
+ createdAt: Date.parse("2026-05-11T10:20:00Z"),
995
+ metadata: null,
996
+ },
997
+ {
998
+ role: "user",
999
+ content: JSON.stringify([
1000
+ {
1001
+ type: "tool_result",
1002
+ tool_use_id: "tu-fail-1",
1003
+ content: "memory buffer write failed",
1004
+ is_error: true,
1005
+ },
1006
+ ]),
1007
+ createdAt: Date.parse("2026-05-11T10:20:01Z"),
1008
+ metadata: null,
1009
+ },
1010
+ ];
1011
+
1012
+ const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
1013
+
1014
+ // The tool_use alone proves the model asked; the durable write happens
1015
+ // in the executor, and it failed. Window stays retryable, and the failed
1016
+ // fact must not enter <already_remembered> where it would suppress the
1017
+ // retry's re-save.
1018
+ expect(outcome.kind).toBe("no_usable_output");
1019
+ expect(stateUpserts).toHaveLength(0);
1020
+ expect(lastRunAtBumps).toHaveLength(1);
1021
+ });
1022
+
1023
+ test("explicit no-findings reply advances the cursor without fabricating a memory write", async () => {
1024
+ mockWakeResult = { invoked: true };
1025
+ messagesByConversationId["fork-conv-1"] = [
1026
+ {
1027
+ role: "assistant",
1028
+ content: JSON.stringify([
1029
+ { type: "text", text: "Nothing new to save." },
1030
+ ]),
1031
+ createdAt: Date.parse("2026-05-11T10:20:00Z"),
1032
+ metadata: null,
1033
+ },
1034
+ ];
1035
+
1036
+ const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
1037
+
1038
+ // A reviewed-and-no-findings pass is a legitimate success: the mandated
1039
+ // exact reply is the positive persisted artifact, the cursor advances,
1040
+ // and the remembered log gains nothing.
1041
+ expect(outcome.kind).toBe("invoked");
1042
+ expect(stateUpserts).toHaveLength(1);
1043
+ expect(stateUpserts[0]!.lastProcessedMessageId).toBe("m3");
1044
+ expect(stateUpserts[0]!.rememberedLog).toEqual([]);
1045
+ });
1046
+
1047
+ test("analysis prose that merely mentions the no-findings phrase does not advance", async () => {
1048
+ mockWakeResult = { invoked: true };
1049
+ messagesByConversationId["fork-conv-1"] = [
1050
+ {
1051
+ role: "assistant",
1052
+ content: JSON.stringify([
1053
+ {
1054
+ type: "text",
1055
+ text: "I reviewed the window. Nothing new to save. Here is my reasoning about why each item was already covered by prior passes.",
1056
+ },
1057
+ ]),
1058
+ createdAt: Date.parse("2026-05-11T10:20:00Z"),
1059
+ metadata: null,
1060
+ },
1061
+ ];
1062
+
1063
+ const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
1064
+
1065
+ expect(outcome.kind).toBe("no_usable_output");
1066
+ expect(stateUpserts).toHaveLength(0);
1067
+ });
1068
+
1069
+ test("no-findings reply cannot advance a run that attempted a save and failed", async () => {
1070
+ mockWakeResult = { invoked: true };
1071
+ messagesByConversationId["fork-conv-1"] = [
1072
+ {
1073
+ role: "assistant",
1074
+ content: JSON.stringify([
1075
+ {
1076
+ type: "tool_use",
1077
+ id: "tu-mixed-1",
1078
+ name: "remember",
1079
+ input: { content: "fact that failed to land" },
1080
+ },
1081
+ ]),
1082
+ createdAt: Date.parse("2026-05-11T10:20:00Z"),
1083
+ metadata: null,
1084
+ },
1085
+ {
1086
+ role: "user",
1087
+ content: JSON.stringify([
1088
+ {
1089
+ type: "tool_result",
1090
+ tool_use_id: "tu-mixed-1",
1091
+ content: "write failed",
1092
+ is_error: true,
1093
+ },
1094
+ ]),
1095
+ createdAt: Date.parse("2026-05-11T10:20:01Z"),
1096
+ metadata: null,
1097
+ },
1098
+ {
1099
+ role: "assistant",
1100
+ content: JSON.stringify([
1101
+ { type: "text", text: "Nothing new to save." },
1102
+ ]),
1103
+ createdAt: Date.parse("2026-05-11T10:20:02Z"),
1104
+ metadata: null,
1105
+ },
1106
+ ];
1107
+
1108
+ const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
1109
+
1110
+ // The run demonstrably had findings (it attempted a save); the failed
1111
+ // write must stay retryable rather than being papered over by a
1112
+ // no-findings claim.
1113
+ expect(outcome.kind).toBe("no_usable_output");
1114
+ expect(stateUpserts).toHaveLength(0);
1115
+ });
1116
+
1117
+ test("a remember tool_use with NO persisted tool_result is not durable evidence", async () => {
1118
+ mockWakeResult = { invoked: true };
1119
+ messagesByConversationId["fork-conv-1"] = [
1120
+ {
1121
+ role: "assistant",
1122
+ content: JSON.stringify([
1123
+ {
1124
+ type: "tool_use",
1125
+ id: "tu-noresult-1",
1126
+ name: "remember",
1127
+ input: { content: "fact with unproven execution" },
1128
+ },
1129
+ ]),
1130
+ createdAt: Date.parse("2026-05-11T10:20:00Z"),
1131
+ metadata: null,
1132
+ },
1133
+ ];
1134
+
1135
+ const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
1136
+
1137
+ expect(outcome.kind).toBe("no_usable_output");
1138
+ expect(stateUpserts).toHaveLength(0);
1139
+ });
1140
+
1141
+ test("retry after a no_usable_output run advances only once durable output exists", async () => {
1142
+ mockWakeResult = { invoked: true };
1143
+ messagesByConversationId["fork-conv-1"] = [];
1144
+
1145
+ const first = await memoryRetrospectiveJob(makeJob(), stubConfig);
1146
+ expect(first.kind).toBe("no_usable_output");
1147
+ expect(stateUpserts).toHaveLength(0);
1148
+
1149
+ // Retry: same window, this time the run persists a real save and its
1150
+ // successful execution.
1151
+ messagesByConversationId["fork-conv-1"] = [
1152
+ {
1153
+ role: "assistant",
1154
+ content: JSON.stringify([
1155
+ {
1156
+ type: "tool_use",
1157
+ id: "tu-retry-1",
1158
+ name: "remember",
1159
+ input: { content: "fact captured on retry" },
1160
+ },
1161
+ ]),
1162
+ createdAt: Date.parse("2026-05-11T10:25:00Z"),
1163
+ metadata: null,
1164
+ },
1165
+ {
1166
+ role: "user",
1167
+ content: JSON.stringify([
1168
+ { type: "tool_result", tool_use_id: "tu-retry-1", content: "Saved." },
1169
+ ]),
1170
+ createdAt: Date.parse("2026-05-11T10:25:01Z"),
1171
+ metadata: null,
1172
+ },
1173
+ ];
1174
+ const second = await memoryRetrospectiveJob(makeJob(), stubConfig);
1175
+
1176
+ expect(second.kind).toBe("invoked");
1177
+ expect(stateUpserts).toHaveLength(1);
1178
+ expect(stateUpserts[0]!.lastProcessedMessageId).toBe("m3");
1179
+ expect(stateUpserts[0]!.rememberedLog).toEqual(["fact captured on retry"]);
1180
+ });
1181
+
743
1182
  test("wake throws: lastRunAt bumped before rethrow, orphan fork deleted, error rethrown", async () => {
744
1183
  mockWakeThrows = new Error("LLM provider 503");
745
1184
  await expect(memoryRetrospectiveJob(makeJob(), stubConfig)).rejects.toThrow(
@@ -957,7 +1396,7 @@ describe("memoryRetrospectiveJob", () => {
957
1396
  await memoryRetrospectiveJob(makeJob(), stubConfig);
958
1397
 
959
1398
  const instructionText = persistedInstructionText();
960
- expect(instructionText).toContain("<​/already_remembered>");
1399
+ expect(instructionText).toContain("<\u200B/already_remembered>");
961
1400
  });
962
1401
 
963
1402
  test("honors memory.retrospective.promptPath override when set", async () => {
@@ -1012,12 +1451,10 @@ describe("memoryRetrospectiveJob", () => {
1012
1451
  // Mid-turn requeue gate
1013
1452
  // -------------------------------------------------------------------------
1014
1453
 
1015
- test("source mid-turn → skipped outcome, state fully untouched, job re-upserted with the fallback delay, no fork", async () => {
1454
+ test("source mid-turn → source_processing outcome, state fully untouched, no self-upserted row, no fork", async () => {
1016
1455
  loadedConversations["src-conv-1"] = { processing: true };
1017
1456
 
1018
- const before = Date.now();
1019
1457
  const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
1020
- const after = Date.now();
1021
1458
 
1022
1459
  expect(outcome.kind).toBe("source_processing");
1023
1460
  // BOTH pointers untouched. `lastRunAt` must stay unbumped so the
@@ -1027,18 +1464,11 @@ describe("memoryRetrospectiveJob", () => {
1027
1464
  // forever.
1028
1465
  expect(stateUpserts).toHaveLength(0);
1029
1466
  expect(lastRunAtBumps).toHaveLength(0);
1030
- // Timed fallback row for a turn that aborts without indexing another
1031
- // message: coalescing re-upsert at the named delay.
1032
- expect(retrospectiveJobUpserts).toHaveLength(1);
1033
- expect(retrospectiveJobUpserts[0]!.payload).toEqual({
1034
- conversationId: "src-conv-1",
1035
- });
1036
- expect(retrospectiveJobUpserts[0]!.runAfter).toBeGreaterThanOrEqual(
1037
- before + SOURCE_PROCESSING_REQUEUE_DELAY_MS,
1038
- );
1039
- expect(retrospectiveJobUpserts[0]!.runAfter).toBeLessThanOrEqual(
1040
- after + SOURCE_PROCESSING_REQUEUE_DELAY_MS,
1041
- );
1467
+ // The handler no longer mints a fresh fallback row per attempt: the
1468
+ // worker defers the SAME job row on the bounded deferral counter (see
1469
+ // `resolveRetrospectiveOutcome`), so a stranded processing flag cannot
1470
+ // produce an unbounded stream of job rows.
1471
+ expect(retrospectiveJobUpserts).toHaveLength(0);
1042
1472
  // No fork, no instruction, no wake, no cleanup side effects.
1043
1473
  expect(forkCalls).toHaveLength(0);
1044
1474
  expect(addMessageCalls).toHaveLength(0);
@@ -1046,6 +1476,41 @@ describe("memoryRetrospectiveJob", () => {
1046
1476
  expect(deletedConversationIds).toEqual([]);
1047
1477
  });
1048
1478
 
1479
+ test("source mid-turn with a fresh processing stamp → still deferred", async () => {
1480
+ loadedConversations["src-conv-1"] = { processing: true };
1481
+ processingStartedAtById["src-conv-1"] = Date.now() - 60_000;
1482
+
1483
+ const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
1484
+
1485
+ expect(outcome.kind).toBe("source_processing");
1486
+ expect(forkCalls).toHaveLength(0);
1487
+ });
1488
+
1489
+ test("source mid-turn with a stamp older than the stale override → proceeds and runs normally", async () => {
1490
+ loadedConversations["src-conv-1"] = { processing: true };
1491
+ processingStartedAtById["src-conv-1"] =
1492
+ Date.now() - STALE_SOURCE_PROCESSING_OVERRIDE_MS - 60_000;
1493
+
1494
+ const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
1495
+
1496
+ // A stranded flag (swallowed turn-end clear while the daemon stayed up)
1497
+ // must not block memory formation indefinitely: past the override window
1498
+ // the run proceeds as if idle.
1499
+ expect(outcome.kind).toBe("invoked");
1500
+ expect(forkCalls).toHaveLength(1);
1501
+ expect(stateUpserts).toHaveLength(1);
1502
+ });
1503
+
1504
+ test("source mid-turn with a set flag but no persisted stamp → deferred (reads as live)", async () => {
1505
+ loadedConversations["src-conv-1"] = { processing: true };
1506
+ // No processingStartedAtById entry: the stamp write may have failed, so
1507
+ // age cannot be established. The bounded deferral budget still caps it.
1508
+ const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
1509
+
1510
+ expect(outcome.kind).toBe("source_processing");
1511
+ expect(forkCalls).toHaveLength(0);
1512
+ });
1513
+
1049
1514
  test("source loaded but idle → normal run, no requeue upsert", async () => {
1050
1515
  loadedConversations["src-conv-1"] = { processing: false };
1051
1516
 
@@ -1750,6 +2215,7 @@ describe("memoryRetrospectiveJob", () => {
1750
2215
  content: JSON.stringify([
1751
2216
  {
1752
2217
  type: "tool_use",
2218
+ id: "tu-tail-1",
1753
2219
  name: "remember",
1754
2220
  input: { content: "post-fork tail save — included" },
1755
2221
  },
@@ -1757,6 +2223,14 @@ describe("memoryRetrospectiveJob", () => {
1757
2223
  createdAt: 2000,
1758
2224
  metadata: null,
1759
2225
  },
2226
+ {
2227
+ role: "user",
2228
+ content: JSON.stringify([
2229
+ { type: "tool_result", tool_use_id: "tu-tail-1", content: "Saved." },
2230
+ ]),
2231
+ createdAt: 2001,
2232
+ metadata: null,
2233
+ },
1760
2234
  ];
1761
2235
 
1762
2236
  const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
@@ -1814,6 +2288,7 @@ describe("memoryRetrospectiveJob", () => {
1814
2288
  content: JSON.stringify([
1815
2289
  {
1816
2290
  type: "tool_use",
2291
+ id: "tu-seed-1",
1817
2292
  name: "remember",
1818
2293
  input: { content: "this run's save" },
1819
2294
  },
@@ -1821,6 +2296,14 @@ describe("memoryRetrospectiveJob", () => {
1821
2296
  createdAt: 2000,
1822
2297
  metadata: null,
1823
2298
  },
2299
+ {
2300
+ role: "user",
2301
+ content: JSON.stringify([
2302
+ { type: "tool_result", tool_use_id: "tu-seed-1", content: "Saved." },
2303
+ ]),
2304
+ createdAt: 2001,
2305
+ metadata: null,
2306
+ },
1824
2307
  ];
1825
2308
 
1826
2309
  const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
@@ -1859,6 +2342,7 @@ describe("memoryRetrospectiveJob", () => {
1859
2342
  content: JSON.stringify([
1860
2343
  {
1861
2344
  type: "tool_use",
2345
+ id: "tu-carry-1",
1862
2346
  name: "remember",
1863
2347
  input: { content: "fork tail save" },
1864
2348
  },
@@ -1866,6 +2350,14 @@ describe("memoryRetrospectiveJob", () => {
1866
2350
  createdAt: 2000,
1867
2351
  metadata: null,
1868
2352
  },
2353
+ {
2354
+ role: "user",
2355
+ content: JSON.stringify([
2356
+ { type: "tool_result", tool_use_id: "tu-carry-1", content: "Saved." },
2357
+ ]),
2358
+ createdAt: 2001,
2359
+ metadata: null,
2360
+ },
1869
2361
  ];
1870
2362
 
1871
2363
  await memoryRetrospectiveJob(makeJob(), stubConfig);