@vellumai/assistant 0.11.3 → 0.11.4-dev.202608190019.b94dbf2

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 (596) hide show
  1. package/AGENTS.md +9 -3
  2. package/ARCHITECTURE.md +32 -10
  3. package/docs/architecture/memory.md +43 -5
  4. package/docs/architecture/security.md +96 -152
  5. package/docs/architecture/turn-actor.md +70 -0
  6. package/docs/browser-use-architecture-phase2.md +128 -56
  7. package/docs/flux-turn-detection-spike.md +248 -0
  8. package/docs/guardian-request-flow.md +35 -0
  9. package/docs/stt-provider-onboarding.md +3 -1
  10. package/knip.json +3 -0
  11. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  12. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
  13. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
  14. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  15. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
  16. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
  17. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  18. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
  19. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
  20. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  21. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
  22. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
  23. package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +34 -0
  24. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +166 -1
  25. package/node_modules/@vellumai/gateway-client/src/index.ts +2 -0
  26. package/node_modules/@vellumai/service-contracts/package.json +1 -0
  27. package/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
  28. package/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
  29. package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  30. package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
  31. package/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
  32. package/openapi.yaml +289 -113
  33. package/package.json +1 -1
  34. package/scripts/voice-ttft-spike.ts +3 -3
  35. package/scripts/write-plugin-api-shim.ts +10 -0
  36. package/src/__tests__/app-compiler.test.ts +38 -3
  37. package/src/__tests__/app-control-flow.test.ts +1 -0
  38. package/src/__tests__/approval-routes-http.test.ts +2 -2
  39. package/src/__tests__/assistant-feature-flag-guard.test.ts +25 -3
  40. package/src/__tests__/attachments-store.test.ts +21 -12
  41. package/src/__tests__/byok-default-profile-ensure.test.ts +17 -0
  42. package/src/__tests__/call-setup-flow-name-capture.test.ts +0 -1
  43. package/src/__tests__/call-site-routing-provider.test.ts +1 -1
  44. package/src/__tests__/channel-availability-routes.test.ts +14 -1
  45. package/src/__tests__/channel-capabilities-dedupe.test.ts +214 -0
  46. package/src/__tests__/channel-delivery-store.test.ts +14 -14
  47. package/src/__tests__/channel-inbound-disk-pressure.test.ts +4 -4
  48. package/src/__tests__/channel-setup-panel-ack.test.ts +1 -1
  49. package/src/__tests__/checker.test.ts +20 -314
  50. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +6 -2
  51. package/src/__tests__/compaction-events.test.ts +8 -10
  52. package/src/__tests__/config-loader-backfill.test.ts +3 -3
  53. package/src/__tests__/config-schema.test.ts +25 -10
  54. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
  55. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +8 -11
  56. package/src/__tests__/conversation-agent-loop-overflow.test.ts +8 -11
  57. package/src/__tests__/conversation-agent-loop.test.ts +32 -21
  58. package/src/__tests__/conversation-attachments.test.ts +0 -1
  59. package/src/__tests__/conversation-attention-store.test.ts +63 -0
  60. package/src/__tests__/conversation-confirmation-signals.test.ts +112 -0
  61. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -4
  62. package/src/__tests__/conversation-fork-crud.test.ts +69 -0
  63. package/src/__tests__/conversation-fork-referential.test.ts +67 -0
  64. package/src/__tests__/conversation-fork-retrospective.test.ts +24 -0
  65. package/src/__tests__/conversation-load-history-repair.test.ts +209 -0
  66. package/src/__tests__/conversation-notifiers-provenance.test.ts +59 -0
  67. package/src/__tests__/conversation-queue.test.ts +154 -4
  68. package/src/__tests__/conversation-routes-disk-view.test.ts +1 -1
  69. package/src/__tests__/conversation-routes-enabled-plugins.test.ts +1 -1
  70. package/src/__tests__/conversation-routes-guardian-reply.test.ts +9 -9
  71. package/src/__tests__/conversation-routes-hidden-queue.test.ts +1 -1
  72. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -1
  73. package/src/__tests__/conversation-runtime-assembly.test.ts +187 -102
  74. package/src/__tests__/conversation-runtime-workspace.test.ts +14 -10
  75. package/src/__tests__/conversation-slash-queue.test.ts +3 -0
  76. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +1 -0
  77. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +1 -0
  78. package/src/__tests__/conversation-surfaces-app-control.test.ts +1 -0
  79. package/src/__tests__/conversation-surfaces-app-open.test.ts +1 -1
  80. package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -1
  81. package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +21 -14
  82. package/src/__tests__/conversation-surfaces-queued-emit.test.ts +1 -0
  83. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +1 -0
  84. package/src/__tests__/conversation-surfaces-standalone.test.ts +1 -0
  85. package/src/__tests__/conversation-surfaces-state-update.test.ts +1 -1
  86. package/src/__tests__/conversation-surfaces-table-action.test.ts +1 -1
  87. package/src/__tests__/conversation-surfaces-task-progress.test.ts +1 -1
  88. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +1 -1
  89. package/src/__tests__/conversation-tool-setup-attribution.test.ts +1 -1
  90. package/src/__tests__/credential-prompt-route.test.ts +7 -10
  91. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  92. package/src/__tests__/cu-unified-flow.test.ts +1 -0
  93. package/src/__tests__/custom-profile-ensure.test.ts +5 -1
  94. package/src/__tests__/default-plugin-names-guard.test.ts +12 -1
  95. package/src/__tests__/discord-access-request-privacy.test.ts +5 -1
  96. package/src/__tests__/discord-requester-notice-privacy.test.ts +3 -3
  97. package/src/__tests__/document-append-idempotency.test.ts +233 -0
  98. package/src/__tests__/document-sync-tags.test.ts +0 -75
  99. package/src/__tests__/dynamic-skill-background-guard.test.ts +0 -2
  100. package/src/__tests__/edit-propagation.test.ts +107 -11
  101. package/src/__tests__/file-ops-service.test.ts +163 -30
  102. package/src/__tests__/filesystem-tools.test.ts +23 -24
  103. package/src/__tests__/gateway-only-guard.test.ts +2 -5
  104. package/src/__tests__/helpers/gateway-classify-mock.ts +27 -6
  105. package/src/__tests__/helpers/mock-actor-context.ts +49 -0
  106. package/src/__tests__/helpers/mock-conversation.ts +13 -1
  107. package/src/__tests__/host-file-read-tool.test.ts +16 -19
  108. package/src/__tests__/http-user-message-parity.test.ts +1 -1
  109. package/src/__tests__/init-feature-flag-overrides.test.ts +49 -0
  110. package/src/__tests__/injector-chain.test.ts +63 -41
  111. package/src/__tests__/injector-disk-pressure.test.ts +11 -23
  112. package/src/__tests__/inline-skill-load-permissions.test.ts +47 -36
  113. package/src/__tests__/llm-context-resolution.test.ts +73 -1
  114. package/src/__tests__/llm-schema.test.ts +5 -2
  115. package/src/__tests__/managed-skill-lifecycle.test.ts +7 -0
  116. package/src/__tests__/mcp-list-plugin-servers.test.ts +250 -0
  117. package/src/__tests__/media-generate-image.test.ts +131 -21
  118. package/src/__tests__/memory-retrieval-hook.test.ts +100 -7
  119. package/src/__tests__/messages-read-boundary-guard.test.ts +134 -0
  120. package/src/__tests__/mtime-cache.test.ts +3 -1
  121. package/src/__tests__/non-member-access-request.test.ts +0 -20
  122. package/src/__tests__/outbound-slack-persistence.test.ts +40 -1
  123. package/src/__tests__/platform-bash-auto-approve.test.ts +0 -4
  124. package/src/__tests__/plugin-api-store-credential.test.ts +261 -0
  125. package/src/__tests__/plugin-api-webhook-url.test.ts +10 -7
  126. package/src/__tests__/plugin-disabled-state.test.ts +2 -0
  127. package/src/__tests__/plugin-effective-enabled-set.test.ts +55 -3
  128. package/src/__tests__/plugin-execution-context.test.ts +73 -0
  129. package/src/__tests__/plugin-import-boundary-guard.test.ts +5 -1
  130. package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +7 -4
  131. package/src/__tests__/plugin-secret-pattern-contribution.test.ts +1 -1
  132. package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +14 -7
  133. package/src/__tests__/provider-commit-message-generator.test.ts +20 -0
  134. package/src/__tests__/proxy-approval-callback.test.ts +1 -0
  135. package/src/__tests__/qdrant-manager.test.ts +14 -1
  136. package/src/__tests__/reaction-persistence.test.ts +200 -7
  137. package/src/__tests__/require-fresh-approval.test.ts +0 -4
  138. package/src/__tests__/risk-classification-boundary-guard.test.ts +115 -0
  139. package/src/__tests__/run-conversation-turn-persistence.test.ts +460 -105
  140. package/src/__tests__/run-due-schedules.test.ts +21 -0
  141. package/src/__tests__/scaffold-managed-skill-tool.test.ts +187 -18
  142. package/src/__tests__/schedule-routes.test.ts +23 -0
  143. package/src/__tests__/schedule-store.test.ts +17 -0
  144. package/src/__tests__/scoped-approval-grants.test.ts +11 -6
  145. package/src/__tests__/secret-ingress-channel.test.ts +0 -1
  146. package/src/__tests__/secret-ingress-http.test.ts +1 -1
  147. package/src/__tests__/send-endpoint-busy.test.ts +3 -3
  148. package/src/__tests__/skills.test.ts +32 -0
  149. package/src/__tests__/slack-edit-ordering-characterization.test.ts +0 -1
  150. package/src/__tests__/starter-task-flow.test.ts +5 -4
  151. package/src/__tests__/subagent-call-site-routing.test.ts +31 -19
  152. package/src/__tests__/subagent-fork-prompt-role.test.ts +1 -1
  153. package/src/__tests__/subagent-spawn-and-await.test.ts +18 -17
  154. package/src/__tests__/subagent-tool-gate-mode.test.ts +1 -1
  155. package/src/__tests__/subagent-tools.test.ts +81 -101
  156. package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +1 -0
  157. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
  158. package/src/__tests__/tool-executor-lifecycle-events.test.ts +132 -5
  159. package/src/__tests__/tool-executor.test.ts +68 -39
  160. package/src/__tests__/turn-events-store.test.ts +43 -0
  161. package/src/__tests__/ui-choice-copy-surfaces.test.ts +1 -1
  162. package/src/__tests__/ui-shape-teaching.test.ts +33 -0
  163. package/src/__tests__/ui-visual-surface.test.ts +1 -1
  164. package/src/__tests__/ui-voice-picker-surface.test.ts +128 -0
  165. package/src/__tests__/ui-work-result-surface.test.ts +1 -1
  166. package/src/__tests__/user-plugin-loader.test.ts +3 -1
  167. package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
  168. package/src/__tests__/visible-app-context.test.ts +16 -9
  169. package/src/__tests__/voice-config-update.test.ts +40 -0
  170. package/src/__tests__/voice-scoped-grant-consumer.test.ts +5 -3
  171. package/src/__tests__/voice-session-bridge.test.ts +85 -29
  172. package/src/__tests__/worker-entrypoint-guards.test.ts +54 -0
  173. package/src/__tests__/worker-plugin-surface.test.ts +77 -0
  174. package/src/__tests__/workspace-migration-142-consolidate-voice-front-door.test.ts +158 -0
  175. package/src/__tests__/workspace-migration-143-repair-deprecated-codex-model-id.test.ts +133 -0
  176. package/src/__tests__/workspace-migration-144-convert-stranded-subscription-openai-profiles.test.ts +316 -0
  177. package/src/__tests__/workspace-migration-145-collapse-profile-bindings-to-entries.test.ts +325 -0
  178. package/src/__tests__/workspace-migration-146-repair-retired-fireworks-deepseek-flash-model-id.test.ts +235 -0
  179. package/src/acp/__tests__/acp-claude-oauth.test.ts +10 -2
  180. package/src/acp/__tests__/auth-required.test.ts +161 -0
  181. package/src/acp/acp-claude-oauth.ts +19 -2
  182. package/src/acp/agent-process.test.ts +100 -0
  183. package/src/acp/agent-process.ts +29 -26
  184. package/src/acp/auth-required.ts +102 -0
  185. package/src/acp/session-manager.test.ts +119 -0
  186. package/src/acp/session-manager.ts +76 -3
  187. package/src/api/events/acp-auth-required.ts +55 -0
  188. package/src/api/events/host-file.ts +2 -2
  189. package/src/api/index.ts +7 -0
  190. package/src/api/surfaces.ts +12 -3
  191. package/src/apps/app-store.ts +3 -0
  192. package/src/bundler/package-resolver.ts +2 -30
  193. package/src/calls/__tests__/voice-session-bridge.test.ts +194 -11
  194. package/src/calls/__tests__/voice-triage-escalate.test.ts +102 -0
  195. package/src/calls/call-controller.ts +19 -3
  196. package/src/calls/call-setup-flow.ts +0 -1
  197. package/src/calls/media-stream-stt-session.ts +15 -0
  198. package/src/calls/voice-session-bridge.ts +115 -41
  199. package/src/calls/voice-triage-escalate.ts +105 -2
  200. package/src/channels/__tests__/plugin-channel-declarations.test.ts +161 -0
  201. package/src/channels/config.ts +13 -0
  202. package/src/channels/plugin-channel-declarations.ts +108 -0
  203. package/src/channels/types.ts +30 -0
  204. package/src/cli/AGENTS.md +5 -2
  205. package/src/cli/bundled-modules.ts +29 -0
  206. package/src/cli/commands/credentials.help.ts +2 -2
  207. package/src/cli/commands/db/repair.ts +4 -8
  208. package/src/cli/commands/domain.ts +6 -3
  209. package/src/cli/commands/email.ts +6 -3
  210. package/src/cli/commands/inference-providers.ts +1 -1
  211. package/src/cli/commands/keys.ts +8 -3
  212. package/src/cli/commands/mcp.help.ts +13 -4
  213. package/src/cli/commands/mcp.ts +9 -0
  214. package/src/cli/commands/memory/__tests__/memory-v2.test.ts +57 -7
  215. package/src/cli/commands/memory/__tests__/memory-v3.test.ts +128 -5
  216. package/src/cli/commands/memory/index.help.ts +84 -28
  217. package/src/cli/commands/memory/index.ts +2 -0
  218. package/src/cli/commands/memory/memory-v2.ts +58 -54
  219. package/src/cli/commands/memory/memory-v3.ts +64 -0
  220. package/src/cli/commands/memory/memory-validate.ts +18 -0
  221. package/src/cli/commands/plugins.ts +85 -36
  222. package/src/cli/commands/schedules.ts +35 -1
  223. package/src/cli/commands/stt.help.ts +27 -2
  224. package/src/cli/commands/trust.ts +3 -14
  225. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +39 -0
  226. package/src/cli/lib/bundled-marketplace.json +14 -1
  227. package/src/cli/lib/upgrade-plugin.ts +42 -0
  228. package/src/config/__tests__/balanced-model-experiment.test.ts +278 -0
  229. package/src/config/__tests__/default-profile-catalog.test.ts +34 -2
  230. package/src/config/__tests__/default-provider.test.ts +6 -1
  231. package/src/config/__tests__/profile-materialization.test.ts +75 -19
  232. package/src/config/assistant-feature-flags.ts +36 -15
  233. package/src/config/balanced-model-experiment.ts +35 -0
  234. package/src/config/bundled-skills/acp/SKILL.md +6 -7
  235. package/src/config/bundled-skills/document-editor/SKILL.md +2 -2
  236. package/src/config/bundled-skills/document-editor/TOOLS.json +2 -2
  237. package/src/config/bundled-skills/image-studio/SKILL.md +5 -4
  238. package/src/config/bundled-skills/image-studio/TOOLS.json +1 -1
  239. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +101 -0
  240. package/src/config/bundled-skills/media-processing/services/preprocess.ts +14 -4
  241. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  242. package/src/config/bundled-skills/settings/tools/navigate-settings-tab.test.ts +65 -0
  243. package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +7 -1
  244. package/src/config/bundled-skills/settings/tools/shared.ts +16 -0
  245. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +19 -1
  246. package/src/config/bundled-skills/skill-management/TOOLS.json +9 -3
  247. package/src/config/bundled-skills/subagent/SKILL.md +17 -12
  248. package/src/config/bundled-skills/subagent/TOOLS.json +4 -4
  249. package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +22 -1
  250. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +9 -2
  251. package/src/config/call-site-defaults.ts +11 -5
  252. package/src/config/default-profile-catalog.ts +179 -16
  253. package/src/config/default-profile-names.ts +4 -1
  254. package/src/config/default-provider-resolution.ts +4 -0
  255. package/src/config/feature-flag-registry.json +12 -12
  256. package/src/config/llm-context-resolution.ts +11 -3
  257. package/src/config/llm-resolver.ts +28 -1
  258. package/src/config/profile-materialization.ts +70 -22
  259. package/src/config/schemas/__tests__/live-voice.test.ts +107 -4
  260. package/src/config/schemas/call-site-catalog.ts +4 -4
  261. package/src/config/schemas/live-voice.ts +57 -23
  262. package/src/config/schemas/llm.ts +59 -32
  263. package/src/config/schemas/mcp.ts +23 -0
  264. package/src/config/schemas/plugin-updates.ts +6 -2
  265. package/src/config/schemas/stt.ts +1 -0
  266. package/src/config/skills.ts +9 -2
  267. package/src/context/outbound-sanitize.ts +96 -1
  268. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +1 -1
  269. package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +82 -0
  270. package/src/daemon/conversation-agent-loop-handlers.ts +15 -10
  271. package/src/daemon/conversation-agent-loop.ts +31 -19
  272. package/src/daemon/conversation-messaging.ts +5 -1
  273. package/src/daemon/conversation-notifiers.ts +20 -10
  274. package/src/daemon/conversation-process.ts +20 -6
  275. package/src/daemon/conversation-runtime-assembly.ts +12 -6
  276. package/src/daemon/conversation-store.ts +14 -4
  277. package/src/daemon/conversation-surfaces.ts +75 -26
  278. package/src/daemon/conversation-tool-setup.ts +3 -6
  279. package/src/daemon/conversation.ts +154 -51
  280. package/src/daemon/doordash-steps.ts +2 -2
  281. package/src/daemon/lifecycle.ts +14 -1
  282. package/src/daemon/mcp-reload-service.ts +36 -6
  283. package/src/daemon/process-message.ts +13 -26
  284. package/src/daemon/providers-setup.ts +6 -3
  285. package/src/daemon/trust-context-types.ts +29 -0
  286. package/src/daemon/trust-context.ts +12 -7
  287. package/src/daemon/wake-conversation-ops.ts +3 -2
  288. package/src/daemon/windows-compiled-entry.ts +4 -0
  289. package/src/documents/document-store.ts +143 -240
  290. package/src/hooks/hook-loader.ts +3 -3
  291. package/src/hooks/registry.ts +103 -45
  292. package/src/hooks/types.ts +5 -0
  293. package/src/inbound/__tests__/oauth-callback-url.test.ts +83 -0
  294. package/src/inbound/oauth-callback-url.ts +61 -0
  295. package/src/ipc/gateway-client.test.ts +1 -1
  296. package/src/ipc/gateway-client.ts +14 -21
  297. package/src/ipc/gateway-flag-listener.ts +17 -3
  298. package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +1 -104
  299. package/src/live-voice/__tests__/live-voice-events.test.ts +7 -8
  300. package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +1050 -0
  301. package/src/live-voice/__tests__/live-voice-metrics.test.ts +115 -8
  302. package/src/live-voice/__tests__/live-voice-photo.test.ts +100 -0
  303. package/src/live-voice/__tests__/live-voice-progress.test.ts +60 -194
  304. package/src/live-voice/__tests__/live-voice-stt.test.ts +14 -0
  305. package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +29 -0
  306. package/src/live-voice/__tests__/live-voice-tts-session.test.ts +0 -483
  307. package/src/live-voice/__tests__/live-voice-vad.test.ts +0 -16
  308. package/src/live-voice/__tests__/progress-narration.test.ts +214 -0
  309. package/src/live-voice/live-voice-archive.ts +2 -0
  310. package/src/live-voice/live-voice-manager.ts +16 -3
  311. package/src/live-voice/live-voice-metrics.ts +57 -32
  312. package/src/live-voice/live-voice-photo.ts +1 -2
  313. package/src/live-voice/live-voice-session.ts +598 -323
  314. package/src/live-voice/progress-narration.ts +277 -0
  315. package/src/live-voice/protocol.ts +21 -1
  316. package/src/live-voice/windows-compiled-live-voice.ts +4 -0
  317. package/src/mcp/__tests__/effective-config.test.ts +238 -0
  318. package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +0 -1
  319. package/src/mcp/__tests__/mcp-oauth-client-registration.test.ts +200 -0
  320. package/src/mcp/__tests__/mcp-oauth-provider.test.ts +9 -9
  321. package/src/mcp/__tests__/plugin-server-credential-isolation.test.ts +95 -0
  322. package/src/mcp/client.ts +16 -11
  323. package/src/mcp/effective-config.ts +113 -0
  324. package/src/mcp/manager.ts +11 -6
  325. package/src/mcp/mcp-auth-orchestrator.ts +13 -22
  326. package/src/mcp/mcp-oauth-provider.ts +205 -240
  327. package/src/monitoring/__tests__/plugin-auto-update.test.ts +166 -3
  328. package/src/monitoring/control.ts +1 -0
  329. package/src/monitoring/db-integrity-sample.ts +4 -5
  330. package/src/monitoring/plugin-auto-update.ts +128 -24
  331. package/src/notifications/AGENTS.md +2 -0
  332. package/src/notifications/approval-card-data.ts +33 -0
  333. package/src/notifications/signal.ts +1 -0
  334. package/src/permissions/AGENTS.md +16 -0
  335. package/src/permissions/checker.test.ts +75 -122
  336. package/src/permissions/checker.ts +44 -560
  337. package/src/permissions/confirmation-guardian-request.test.ts +15 -11
  338. package/src/permissions/confirmation-guardian-request.ts +2 -2
  339. package/src/permissions/prompter.ts +1 -5
  340. package/src/permissions/question-guardian-request.test.ts +14 -6
  341. package/src/permissions/question-guardian-request.ts +1 -2
  342. package/src/persistence/attachments-store.ts +8 -1
  343. package/src/persistence/bookmark-crud.ts +3 -7
  344. package/src/persistence/conversation-attention-store.ts +16 -45
  345. package/src/persistence/conversation-crud.ts +33 -4
  346. package/src/persistence/conversation-lineage.ts +9 -0
  347. package/src/persistence/conversation-queries.ts +182 -52
  348. package/src/persistence/delivery-crud.ts +150 -36
  349. package/src/persistence/embeddings/qdrant-manager.ts +84 -49
  350. package/src/persistence/external-conversation-store.ts +32 -4
  351. package/src/persistence/llm-request-log-store.ts +4 -10
  352. package/src/persistence/llm-usage-store.ts +8 -3
  353. package/src/persistence/message-reads.test.ts +197 -0
  354. package/src/persistence/message-reads.ts +211 -0
  355. package/src/persistence/migrations/360-add-document-workspace-path.ts +5 -14
  356. package/src/persistence/migrations/366-chatgpt-subscription-row-identity.test.ts +120 -0
  357. package/src/persistence/migrations/366-chatgpt-subscription-row-identity.ts +62 -0
  358. package/src/persistence/real-user-turn-filter.ts +27 -3
  359. package/src/persistence/schema/documents.ts +4 -4
  360. package/src/persistence/steps.ts +9 -0
  361. package/src/plugin-api/__tests__/oauth-callback-url-export.test.ts +29 -0
  362. package/src/plugin-api/constants.ts +8 -0
  363. package/src/plugin-api/conversation-turn.ts +195 -5
  364. package/src/plugin-api/index.ts +38 -6
  365. package/src/plugin-api/resolve-credential.ts +3 -2
  366. package/src/plugin-api/store-credential.ts +146 -0
  367. package/src/plugin-api/vision-support.test.ts +1 -1
  368. package/src/plugin-api/webhook-url.ts +13 -11
  369. package/src/plugins/__tests__/mcp-servers.test.ts +371 -0
  370. package/src/plugins/defaults/main.ts +21 -7
  371. package/src/plugins/defaults/memory/AGENTS.md +15 -3
  372. package/src/plugins/defaults/memory/__tests__/buffer-format.test.ts +204 -0
  373. package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +72 -0
  374. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +4 -1
  375. package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +4 -1
  376. package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +0 -1
  377. package/src/plugins/defaults/memory/buffer-format.ts +165 -0
  378. package/src/plugins/defaults/memory/context-search/sources/conversations.ts +6 -0
  379. package/src/plugins/defaults/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +77 -0
  380. package/src/plugins/defaults/memory/graph/conversation-graph-memory.ts +24 -6
  381. package/src/plugins/defaults/memory/graph/image-ref-utils.ts +3 -0
  382. package/src/plugins/defaults/memory/graph/tool-handlers.ts +1 -30
  383. package/src/plugins/defaults/memory/graph-topology/pending-buffer.test.ts +34 -0
  384. package/src/plugins/defaults/memory/graph-topology/pending-buffer.ts +17 -24
  385. package/src/plugins/defaults/memory/hooks/post-compact.ts +1 -4
  386. package/src/plugins/defaults/memory/hooks/user-prompt-submit.ts +53 -5
  387. package/src/plugins/defaults/memory/indexer.ts +3 -1
  388. package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +19 -7
  389. package/src/plugins/defaults/memory/memory-retrospective-job.ts +4 -4
  390. package/src/plugins/defaults/memory/src/__tests__/memory-v3-gate-stats.test.ts +281 -0
  391. package/src/plugins/defaults/memory/src/memory-v2-routes.ts +12 -10
  392. package/src/plugins/defaults/memory/src/memory-v3-routes.ts +207 -0
  393. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +147 -0
  394. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-prompt-flag-gating-guard.test.ts +8 -0
  395. package/src/plugins/defaults/memory/substrate/__tests__/edge-index.test.ts +0 -30
  396. package/src/plugins/defaults/memory/substrate/__tests__/ingest.test.ts +73 -1
  397. package/src/plugins/defaults/memory/substrate/__tests__/page-index.test.ts +37 -0
  398. package/src/plugins/defaults/memory/substrate/__tests__/page-links.test.ts +208 -0
  399. package/src/plugins/defaults/memory/substrate/__tests__/prompts-consolidation.test.ts +158 -1
  400. package/src/plugins/defaults/memory/substrate/__tests__/static-context.test.ts +195 -58
  401. package/src/plugins/defaults/memory/substrate/consolidation-job.ts +93 -24
  402. package/src/plugins/defaults/memory/substrate/edge-index.ts +0 -31
  403. package/src/plugins/defaults/memory/substrate/ingest.ts +59 -0
  404. package/src/plugins/defaults/memory/substrate/page-index.ts +35 -8
  405. package/src/plugins/defaults/memory/substrate/page-links.ts +133 -0
  406. package/src/plugins/defaults/memory/substrate/page-store.ts +10 -0
  407. package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +124 -34
  408. package/src/plugins/defaults/memory/substrate/skill-content.ts +8 -1
  409. package/src/plugins/defaults/memory/substrate/static-context.ts +160 -33
  410. package/src/plugins/defaults/memory/substrate/sweep-job.ts +2 -4
  411. package/src/plugins/defaults/memory/v1/graph/extraction.ts +3 -1
  412. package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +18 -0
  413. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +66 -1
  414. package/src/plugins/defaults/memory/v3/card.ts +9 -9
  415. package/src/plugins/defaults/memory/v3/core-set.test.ts +7 -0
  416. package/src/plugins/defaults/memory/v3/core-set.ts +5 -1
  417. package/src/plugins/defaults/memory/v3/edge.ts +13 -57
  418. package/src/plugins/defaults/memory/v3/injector.ts +8 -0
  419. package/src/plugins/defaults/memory/v3/prune.ts +2 -0
  420. package/src/plugins/defaults/memory/v3/selection-log-store.ts +2 -0
  421. package/src/plugins/defaults/memory/v3/shadow-plugin.ts +23 -9
  422. package/src/plugins/defaults/memory/worker-control.ts +1 -0
  423. package/src/plugins/defaults/memory/worker.ts +6 -3
  424. package/src/plugins/defaults/worker-entrypoints.ts +3 -0
  425. package/src/plugins/external-plugin-loader.ts +47 -0
  426. package/src/plugins/mcp-servers.ts +361 -0
  427. package/src/plugins/mtime-cache.ts +37 -46
  428. package/src/plugins/pipeline.ts +14 -7
  429. package/src/plugins/plugin-execution-context.ts +35 -11
  430. package/src/plugins/worker-plugin-surface.ts +33 -0
  431. package/src/prompts/templates/system-sections.ts +0 -7
  432. package/src/providers/__tests__/connection-model-compat.test.ts +1 -1
  433. package/src/providers/__tests__/context-overflow-error.test.ts +24 -0
  434. package/src/providers/__tests__/dispatch-connection-routing.test.ts +214 -2
  435. package/src/providers/__tests__/preflight-resolved-config.test.ts +57 -0
  436. package/src/providers/__tests__/retry-callsite.test.ts +25 -2
  437. package/src/providers/anthropic/__tests__/pause-turn-continuation.test.ts +170 -0
  438. package/src/providers/anthropic/client.ts +332 -241
  439. package/src/providers/call-site-routing.ts +30 -3
  440. package/src/providers/connection-resolution.ts +194 -11
  441. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +4 -0
  442. package/src/providers/inference/adapter-factory.ts +3 -0
  443. package/src/providers/inference/auth.ts +6 -0
  444. package/src/providers/inference/connection-availability.ts +24 -2
  445. package/src/providers/inference/connections.ts +2 -0
  446. package/src/providers/model-catalog.ts +3 -3
  447. package/src/providers/model-intents.ts +28 -8
  448. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +163 -40
  449. package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +125 -2
  450. package/src/providers/openai/chat-completions-provider.ts +133 -36
  451. package/src/providers/openai/codex-models.ts +2 -1
  452. package/src/providers/provider-send-message.ts +32 -3
  453. package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +433 -0
  454. package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +625 -0
  455. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +34 -0
  456. package/src/providers/speech-to-text/__tests__/resolve.test.ts +285 -6
  457. package/src/providers/speech-to-text/deepgram-flux-frames.ts +395 -0
  458. package/src/providers/speech-to-text/deepgram-flux-realtime.ts +679 -0
  459. package/src/providers/speech-to-text/provider-catalog.ts +99 -8
  460. package/src/providers/speech-to-text/resolve.ts +25 -2
  461. package/src/routes/control.ts +1 -0
  462. package/src/routes/route-host-client.ts +1 -0
  463. package/src/routes/route-host-protocol.ts +7 -0
  464. package/src/routes/worker.ts +48 -15
  465. package/src/runtime/AGENTS.md +16 -17
  466. package/src/runtime/access-request-helper.ts +9 -12
  467. package/src/runtime/agent-wake.ts +18 -15
  468. package/src/runtime/effective-capabilities.test.ts +43 -0
  469. package/src/runtime/effective-capabilities.ts +27 -0
  470. package/src/runtime/guardian-reply-router.ts +6 -2
  471. package/src/runtime/pre-first-message-gate.ts +4 -0
  472. package/src/runtime/routes/__tests__/acp-claude-auth-routes.test.ts +12 -4
  473. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +522 -2
  474. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +52 -0
  475. package/src/runtime/routes/__tests__/default-provider-routes.test.ts +61 -0
  476. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +44 -0
  477. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +102 -1
  478. package/src/runtime/routes/__tests__/plugins-routes.test.ts +132 -35
  479. package/src/runtime/routes/__tests__/schedule-routes-disarm-reason.test.ts +215 -0
  480. package/src/runtime/routes/__tests__/stt-routes.test.ts +25 -0
  481. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +55 -6
  482. package/src/runtime/routes/__tests__/user-route-dispatcher-host.test.ts +52 -1
  483. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +133 -1
  484. package/src/runtime/routes/channel-availability-routes.ts +32 -14
  485. package/src/runtime/routes/channel-route-shared.ts +0 -6
  486. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +6 -6
  487. package/src/runtime/routes/conversation-list-routes.ts +181 -23
  488. package/src/runtime/routes/conversation-management-routes.ts +17 -12
  489. package/src/runtime/routes/conversation-query-routes.ts +40 -27
  490. package/src/runtime/routes/conversation-routes.ts +43 -29
  491. package/src/runtime/routes/credential-prompt-routes.ts +4 -7
  492. package/src/runtime/routes/credential-routes.ts +26 -82
  493. package/src/runtime/routes/default-provider-routes.ts +15 -0
  494. package/src/runtime/routes/documents-routes.ts +3 -222
  495. package/src/runtime/routes/inbound-message-handler.ts +21 -45
  496. package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +0 -1
  497. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -9
  498. package/src/runtime/routes/inbound-stages/admission-policy.ts +1 -17
  499. package/src/runtime/routes/inbound-stages/bootstrap-intercept.test.ts +0 -1
  500. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -3
  501. package/src/runtime/routes/inbound-stages/edit-intercept.ts +101 -79
  502. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +0 -1
  503. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +12 -7
  504. package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +91 -8
  505. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +72 -79
  506. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +2 -3
  507. package/src/runtime/routes/inference-profiles-routes.ts +20 -11
  508. package/src/runtime/routes/inference-provider-connection-routes.ts +77 -15
  509. package/src/runtime/routes/log-export-routes.ts +3 -0
  510. package/src/runtime/routes/mcp-auth-routes.ts +148 -57
  511. package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +2 -0
  512. package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +3 -0
  513. package/src/runtime/routes/playground/inject-failures.ts +2 -2
  514. package/src/runtime/routes/playground/reset-circuit.ts +1 -1
  515. package/src/runtime/routes/plugins-routes.ts +38 -8
  516. package/src/runtime/routes/schedule-routes.ts +94 -7
  517. package/src/runtime/routes/settings-routes.ts +6 -6
  518. package/src/runtime/routes/stt-routes.ts +31 -25
  519. package/src/runtime/routes/surface-action-routes.ts +20 -19
  520. package/src/runtime/routes/surface-conversation-resolver.ts +3 -0
  521. package/src/runtime/routes/user-route-dispatcher.ts +69 -15
  522. package/src/runtime/routes/user-route-import.ts +108 -0
  523. package/src/runtime/routes/user-route-resolution.ts +18 -1
  524. package/src/runtime/routes/workspace-routes.ts +0 -9
  525. package/src/runtime/routes/workspace-utils.ts +3 -13
  526. package/src/runtime/services/conversation-serializer.ts +7 -2
  527. package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +68 -5
  528. package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +81 -0
  529. package/src/schedule/plugin-schedule-availability.ts +58 -0
  530. package/src/schedule/plugin-schedule-declarations.ts +23 -27
  531. package/src/schedule/plugin-schedule-reconciler.ts +12 -3
  532. package/src/schedule/schedule-store.ts +5 -1
  533. package/src/schedule/scheduler.ts +9 -4
  534. package/src/schedule/worker-control.ts +1 -0
  535. package/src/schedule/worker.ts +6 -0
  536. package/src/security/oauth2.ts +6 -22
  537. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +22 -0
  538. package/src/stt/__tests__/types.test.ts +94 -0
  539. package/src/stt/daemon-batch-transcriber.ts +10 -0
  540. package/src/stt/stt-stream-session.ts +8 -4
  541. package/src/stt/types.ts +103 -0
  542. package/src/subagent/__tests__/consult-prompt.test.ts +26 -15
  543. package/src/subagent/consult-context.ts +11 -11
  544. package/src/subagent/consult-prompt.ts +26 -35
  545. package/src/subagent/manager.ts +21 -40
  546. package/src/subagent/notify.ts +7 -1
  547. package/src/subagent/types.ts +15 -12
  548. package/src/tools/__tests__/tool-input-schemas.test.ts +7 -7
  549. package/src/tools/acp/spawn.test.ts +97 -0
  550. package/src/tools/acp/spawn.ts +38 -4
  551. package/src/tools/credentials/ref-parse.ts +35 -0
  552. package/src/tools/credentials/resolve.ts +4 -10
  553. package/src/tools/credentials/store.ts +168 -0
  554. package/src/tools/document/document-tool.ts +12 -3
  555. package/src/tools/executor.ts +53 -15
  556. package/src/tools/filesystem/read.ts +27 -10
  557. package/src/tools/host-filesystem/read.ts +27 -15
  558. package/src/tools/network/url-safety.ts +9 -16
  559. package/src/tools/permission-checker.ts +43 -52
  560. package/src/tools/registry.ts +2 -1
  561. package/src/tools/shared/filesystem/file-ops-service.ts +63 -35
  562. package/src/tools/shared/filesystem/legacy-read-args.ts +22 -0
  563. package/src/tools/shared/filesystem/types.ts +5 -5
  564. package/src/tools/skills/scaffold-managed.ts +25 -7
  565. package/src/tools/subagent/spawn.ts +28 -88
  566. package/src/tools/tool-types.ts +26 -13
  567. package/src/tools/types.ts +6 -6
  568. package/src/tools/ui-surface/surface-shape-docs.ts +12 -1
  569. package/src/tools/workflows/run-workflow.ts +1 -2
  570. package/src/tts/__tests__/reasoning-tag-filter.test.ts +78 -0
  571. package/src/tts/reasoning-tag-filter.ts +89 -0
  572. package/src/util/__tests__/worker-process-command.test.ts +37 -0
  573. package/src/util/logger.ts +16 -0
  574. package/src/util/think-tag-stream.ts +95 -0
  575. package/src/util/worker-process.ts +37 -4
  576. package/src/windows-compiled-cli.ts +32 -0
  577. package/src/windows-compiled-entry.ts +4 -0
  578. package/src/windows-compiled-logger.ts +6 -0
  579. package/src/windows-compiled-worker-entry.ts +29 -0
  580. package/src/workspace/byok-default-profile-ensure.ts +76 -24
  581. package/src/workspace/custom-profile-ensure.ts +4 -24
  582. package/src/workspace/migrations/142-consolidate-voice-front-door.ts +70 -0
  583. package/src/workspace/migrations/143-repair-deprecated-codex-model-id.ts +134 -0
  584. package/src/workspace/migrations/144-convert-stranded-subscription-openai-profiles.ts +265 -0
  585. package/src/workspace/migrations/145-collapse-profile-bindings-to-entries.ts +328 -0
  586. package/src/workspace/migrations/146-repair-retired-fireworks-deepseek-flash-model-id.ts +195 -0
  587. package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +0 -10
  588. package/src/workspace/migrations/registry.ts +10 -0
  589. package/src/workspace/provider-commit-message-generator.ts +7 -5
  590. package/src/__tests__/document-workspace-file.test.ts +0 -467
  591. package/src/live-voice/__tests__/front-decision.test.ts +0 -645
  592. package/src/live-voice/front-decision.ts +0 -476
  593. package/src/permissions/ipc-risk-types.ts +0 -143
  594. package/src/permissions/risk-types.ts +0 -76
  595. package/src/subagent/__tests__/consult-transcript.test.ts +0 -184
  596. package/src/subagent/consult-transcript.ts +0 -90
@@ -472,6 +472,8 @@ mock.module("../memory/archive-store.js", () => ({
472
472
  import { AgentLoop } from "../agent/loop.js";
473
473
  import type { Conversation } from "../daemon/conversation.js";
474
474
  import { runAgentLoopImpl } from "../daemon/conversation-agent-loop.js";
475
+ import type { QueueDrainReason } from "../daemon/conversation-queue-manager.js";
476
+ import { asConversation } from "./helpers/mock-conversation.js";
475
477
  import {
476
478
  createMockProvider,
477
479
  type ScriptedResponse,
@@ -519,7 +521,7 @@ function makeCtx(
519
521
  toolExecutor,
520
522
  });
521
523
 
522
- const ctx = {
524
+ const ctx = asConversation({
523
525
  conversationId: "test-conv",
524
526
  messages: [
525
527
  { role: "user", content: [{ type: "text", text: "Hello" }] },
@@ -572,12 +574,7 @@ function makeCtx(
572
574
  skillProjectionCache:
573
575
  new Map() as unknown as Conversation["skillProjectionCache"],
574
576
 
575
- usageStats: {
576
- totalInputTokens: 0,
577
- totalOutputTokens: 0,
578
- totalEstimatedCost: 0,
579
- model: "",
580
- },
577
+ usageStats: { inputTokens: 0, outputTokens: 0, estimatedCost: 0 },
581
578
  turnCount: 0,
582
579
 
583
580
  lastAssistantAttachments: [],
@@ -595,12 +592,12 @@ function makeCtx(
595
592
  getQueueDepth: () => 0,
596
593
  hasQueuedMessages: () => false,
597
594
  canHandoffAtCheckpoint: () => false,
598
- drainQueue: (_reason?: string) => {},
595
+ drainQueue: async (_reason?: QueueDrainReason) => {},
599
596
  // Forwards to drainQueue so tests that spy the drain observe the agent
600
597
  // loop's post-turn kick through the guarded entry point.
601
598
  kickDrainQueue(
602
- this: { drainQueue: (reason?: string) => unknown },
603
- reason: string = "loop_complete",
599
+ this: { drainQueue: (reason?: QueueDrainReason) => Promise<void> },
600
+ reason: QueueDrainReason = "loop_complete",
604
601
  _origin?: string,
605
602
  ) {
606
603
  return this.drainQueue(reason);
@@ -632,7 +629,7 @@ function makeCtx(
632
629
  } as unknown as Conversation["graphMemory"],
633
630
 
634
631
  ...ctxOverrides,
635
- } as unknown as Conversation;
632
+ });
636
633
  // The convergence driver resolves the turn-scoped overflow ladder off the
637
634
  // manager; give every fake manager the ladder methods unless a test supplied
638
635
  // its own.
@@ -268,7 +268,18 @@ const setConversationHistoryStrippedAtMock = mock(
268
268
  const updateConversationSlackContextWatermarkMock = mock(
269
269
  (_conversationId: string, _watermarkTs: string, _compactedAt?: number) => {},
270
270
  );
271
- let mockConversationRow: Record<string, unknown> = {
271
+ interface MockConversationRow {
272
+ conversationType?: string;
273
+ source?: string;
274
+ contextSummary?: string | null;
275
+ contextCompactedMessageCount?: number;
276
+ contextCompactedAt?: number | null;
277
+ slackContextCompactionWatermarkTs?: string | null;
278
+ lastNotifiedInferenceProfile?: string | null;
279
+ processingStartedAt?: string | null;
280
+ [key: string]: unknown;
281
+ }
282
+ let mockConversationRow: MockConversationRow = {
272
283
  id: "conv-1",
273
284
  createdAt: 1_700_000_000_000,
274
285
  contextSummary: null,
@@ -675,7 +686,9 @@ import {
675
686
  applyCompactionResult,
676
687
  runAgentLoopImpl,
677
688
  } from "../daemon/conversation-agent-loop.js";
689
+ import type { QueueDrainReason } from "../daemon/conversation-queue-manager.js";
678
690
  import { settleTurnTail } from "../daemon/turn-tail-chain.js";
691
+ import { asConversation } from "./helpers/mock-conversation.js";
679
692
  import {
680
693
  createMockProvider,
681
694
  type ScriptedResponse,
@@ -731,7 +744,7 @@ function makeCtx(
731
744
  toolExecutor,
732
745
  });
733
746
 
734
- const ctx = {
747
+ const ctx = asConversation({
735
748
  conversationId: "test-conv",
736
749
  messages: [
737
750
  { role: "user", content: [{ type: "text", text: "Hello" }] },
@@ -791,12 +804,7 @@ function makeCtx(
791
804
  skillProjectionCache:
792
805
  new Map() as unknown as Conversation["skillProjectionCache"],
793
806
 
794
- usageStats: {
795
- totalInputTokens: 0,
796
- totalOutputTokens: 0,
797
- totalEstimatedCost: 0,
798
- model: "",
799
- },
807
+ usageStats: { inputTokens: 0, outputTokens: 0, estimatedCost: 0 },
800
808
  turnCount: 0,
801
809
 
802
810
  lastAssistantAttachments: [],
@@ -814,12 +822,12 @@ function makeCtx(
814
822
  getQueueDepth: () => 0,
815
823
  hasQueuedMessages: () => false,
816
824
  canHandoffAtCheckpoint: () => false,
817
- drainQueue: (_reason?: string) => {},
825
+ drainQueue: async (_reason?: QueueDrainReason) => {},
818
826
  // Forwards to drainQueue so tests that spy the drain observe the agent
819
827
  // loop's post-turn kick through the guarded entry point.
820
828
  kickDrainQueue(
821
- this: { drainQueue: (reason?: string) => unknown },
822
- reason: string = "loop_complete",
829
+ this: { drainQueue: (reason?: QueueDrainReason) => Promise<void> },
830
+ reason: QueueDrainReason = "loop_complete",
823
831
  _origin?: string,
824
832
  ) {
825
833
  return this.drainQueue(reason);
@@ -851,7 +859,7 @@ function makeCtx(
851
859
  } as unknown as Conversation["graphMemory"],
852
860
 
853
861
  ...ctxOverrides,
854
- } as unknown as Conversation;
862
+ });
855
863
  // Reactive overflow recovery resolves the turn-scoped reduction ladder off
856
864
  // the manager; give every fake manager the ladder methods unless a test
857
865
  // supplied its own.
@@ -1337,7 +1345,10 @@ describe("session-agent-loop", () => {
1337
1345
  });
1338
1346
  const cleanupFlagDuringInjection = captureCleanupFlagDuringInjection(ctx);
1339
1347
 
1340
- await runAgentLoopImpl(ctx, "free up space", "msg-1", () => {});
1348
+ // Presence is declared per turn; the policy input must carry it.
1349
+ await runAgentLoopImpl(ctx, "free up space", "msg-1", () => {}, {
1350
+ isInteractive: true,
1351
+ });
1341
1352
 
1342
1353
  expect(classifyDiskPressureTurnPolicyMock).toHaveBeenCalledWith(
1343
1354
  mockDiskPressureStatus,
@@ -1795,7 +1806,7 @@ describe("session-agent-loop", () => {
1795
1806
  ],
1796
1807
  toolExecutor: async () => ({ content: "file content", isError: false }),
1797
1808
  canHandoffAtCheckpoint: () => true,
1798
- } as unknown as Partial<Conversation>);
1809
+ });
1799
1810
 
1800
1811
  await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
1801
1812
 
@@ -1826,7 +1837,7 @@ describe("session-agent-loop", () => {
1826
1837
  ],
1827
1838
  toolExecutor: async () => ({ content: "content", isError: false }),
1828
1839
  canHandoffAtCheckpoint: () => false,
1829
- } as unknown as Partial<Conversation>);
1840
+ });
1830
1841
 
1831
1842
  await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
1832
1843
 
@@ -1975,13 +1986,13 @@ describe("session-agent-loop", () => {
1975
1986
 
1976
1987
  test("drains queue after completion", async () => {
1977
1988
  // GIVEN a real loop that answers in a single text turn
1978
- let drainReason: string | undefined;
1989
+ let drainReason: QueueDrainReason | undefined;
1979
1990
  const ctx = makeCtx({
1980
1991
  providerResponses: [textResponse("ok")],
1981
- drainQueue: (reason: string) => {
1992
+ drainQueue: async (reason?: QueueDrainReason) => {
1982
1993
  drainReason = reason;
1983
1994
  },
1984
- } as unknown as Partial<Conversation>);
1995
+ });
1985
1996
 
1986
1997
  // WHEN the orchestrator runs the turn to completion
1987
1998
  await runAgentLoopImpl(ctx, "hi", "msg-1", () => {});
@@ -2018,10 +2029,10 @@ describe("session-agent-loop", () => {
2018
2029
  abortController,
2019
2030
  // Fire the watchdog quickly instead of the ~45s production default.
2020
2031
  abortWatchdogMs: 30,
2021
- drainQueue: (reason: string) => {
2032
+ drainQueue: async (reason?: QueueDrainReason) => {
2022
2033
  drainReason = reason;
2023
2034
  },
2024
- } as unknown as Partial<Conversation>);
2035
+ });
2025
2036
 
2026
2037
  try {
2027
2038
  // WHEN the orchestrator runs the turn
@@ -2520,7 +2531,7 @@ describe("session-agent-loop", () => {
2520
2531
  },
2521
2532
  ],
2522
2533
  toolExecutor: async () => ({ content: "file content", isError: false }),
2523
- } as unknown as Partial<Conversation>);
2534
+ });
2524
2535
 
2525
2536
  await runAgentLoopImpl(ctx, "hello", "msg-user-daily", () => {});
2526
2537
 
@@ -15,7 +15,6 @@ mock.module("../permissions/checker.js", () => ({
15
15
  isDynamicSkillLoadInvocation: () => false,
16
16
  check: checkSpy,
17
17
  classifyRisk: () => Promise.resolve({ level: "low" }),
18
- generateAllowlistOptions: () => Promise.resolve([]),
19
18
  generateScopeOptions: () => [],
20
19
  }));
21
20
 
@@ -832,3 +832,66 @@ describe("conversation-attention-store", () => {
832
832
  });
833
833
  });
834
834
  });
835
+
836
+ describe("attention watermarks and streaming rows", () => {
837
+ test("state init from an upgraded database anchors on the latest finalized reply", () => {
838
+ clearTables();
839
+ ensureConversation("conv-1");
840
+ insertAssistantMessage("conv-1", "msg-done", 1_000);
841
+ getDb()
842
+ .insert(messages)
843
+ .values({
844
+ id: "msg-streaming",
845
+ conversationId: "conv-1",
846
+ role: "assistant",
847
+ content: "still being written",
848
+ createdAt: 2_000,
849
+ metadata: null,
850
+ finalized: 0,
851
+ })
852
+ .run();
853
+
854
+ // No attention row exists, so the seen signal initializes state from the
855
+ // messages table. The streaming row is newest but is not a reply yet;
856
+ // anchoring the watermark on it would classify the finished reply as
857
+ // already seen the moment it completes.
858
+ recordConversationSeenSignal({
859
+ conversationId: "conv-1",
860
+ sourceChannel: "vellum",
861
+ signalType: "macos_conversation_opened",
862
+ confidence: "explicit",
863
+ source: "desktop-client",
864
+ });
865
+
866
+ const state = getAttentionStateByConversationIds(["conv-1"]).get("conv-1")!;
867
+ expect(state.latestAssistantMessageId).toBe("msg-done");
868
+ expect(state.lastSeenAssistantMessageId).toBe("msg-done");
869
+ });
870
+ });
871
+
872
+ describe("markConversationUnread with only a streaming reply", () => {
873
+ test("reports no assistant message rather than rewinding onto the streaming row", () => {
874
+ clearTables();
875
+ ensureConversation("conv-1");
876
+ getDb()
877
+ .insert(messages)
878
+ .values({
879
+ id: "msg-streaming-only",
880
+ conversationId: "conv-1",
881
+ role: "assistant",
882
+ content: "still being written",
883
+ createdAt: 1_000,
884
+ metadata: null,
885
+ finalized: 0,
886
+ })
887
+ .run();
888
+
889
+ // The write-path projection anchors at the finalize seam
890
+ // (`reserveMessage` deliberately skips it), so attention state for this
891
+ // conversation says "no reply yet". The rewind fallback must agree with
892
+ // the projection rather than anchoring on the streaming row.
893
+ expect(() => markConversationUnread("conv-1")).toThrow(
894
+ /no assistant message/i,
895
+ );
896
+ });
897
+ });
@@ -431,3 +431,115 @@ describe("sendToClient receives state signals", () => {
431
431
  });
432
432
  });
433
433
  });
434
+
435
+ describe("the sink is fixed for the conversation's life", () => {
436
+ test("a cold instance delivers activity state with no turn ever having run", () => {
437
+ // The summarize-up-to route resolves its conversation outside the send
438
+ // path and emits a thinking indicator before any turn: it must reach the
439
+ // sink the conversation was built with, not depend on a send having wired
440
+ // one (LUM-2987).
441
+ const clientMsgs: AssistantEvent[] = [];
442
+ const conversation = makeConversation((msg) => clientMsgs.push(msg));
443
+
444
+ conversation.emitActivityState("thinking", "context_compacting", {
445
+ statusText: "Summarizing conversation",
446
+ });
447
+
448
+ const activity = clientMsgs.filter(
449
+ (m) => m.type === "assistant_activity_state",
450
+ );
451
+ expect(activity).toHaveLength(1);
452
+ expect((activity[0] as { statusText?: string }).statusText).toBe(
453
+ "Summarizing conversation",
454
+ );
455
+ });
456
+
457
+ test("presence is a property of the turn, not of delivery", () => {
458
+ // Nothing is in flight, so no human is asserted present, even though the
459
+ // sink is live and delivering.
460
+ const clientMsgs: AssistantEvent[] = [];
461
+ const conversation = makeConversation((msg) => clientMsgs.push(msg));
462
+ expect(conversation.hasNoClient).toBe(true);
463
+ conversation.emitActivityState("thinking", "message_dequeued");
464
+ expect(clientMsgs).toHaveLength(1);
465
+ expect(conversation.hasNoClient).toBe(true);
466
+ });
467
+ });
468
+
469
+ describe("event observers", () => {
470
+ test("an observer sees each event after the sink delivered it, and detaches", () => {
471
+ const order: string[] = [];
472
+ const conversation = makeConversation((msg) => {
473
+ order.push(`sink:${msg.type}`);
474
+ });
475
+ const detach = conversation.addEventObserver((msg) => {
476
+ order.push(`observer:${msg.type}`);
477
+ });
478
+
479
+ conversation.emitActivityState("thinking", "message_dequeued");
480
+ expect(order).toEqual([
481
+ "sink:assistant_activity_state",
482
+ "observer:assistant_activity_state",
483
+ ]);
484
+
485
+ detach();
486
+ conversation.emitActivityState("idle", "message_complete");
487
+ expect(order).toEqual([
488
+ "sink:assistant_activity_state",
489
+ "observer:assistant_activity_state",
490
+ "sink:assistant_activity_state",
491
+ ]);
492
+ });
493
+
494
+ test("a throwing observer neither blocks the sink nor its siblings", () => {
495
+ const seen: string[] = [];
496
+ const conversation = makeConversation((msg) => {
497
+ seen.push(`sink:${msg.type}`);
498
+ });
499
+ conversation.addEventObserver(() => {
500
+ throw new Error("observer boom");
501
+ });
502
+ conversation.addEventObserver((msg) => {
503
+ seen.push(`second:${msg.type}`);
504
+ });
505
+
506
+ conversation.emitActivityState("thinking", "message_dequeued");
507
+ expect(seen).toEqual([
508
+ "sink:assistant_activity_state",
509
+ "second:assistant_activity_state",
510
+ ]);
511
+ });
512
+
513
+ test("the prompter's confirmation_request reaches observers after the sink", () => {
514
+ // Voice auto-resolves approval prompts it has no UI for; it must observe
515
+ // the request only once the sink has put it on the wire, so its resolution
516
+ // lands after the request it answers.
517
+ const order: string[] = [];
518
+ const conversation = makeConversation((msg) => {
519
+ order.push(`sink:${msg.type}`);
520
+ });
521
+ conversation.addEventObserver((msg) => {
522
+ if (msg.type === "confirmation_request") {
523
+ order.push("observer:confirmation_request");
524
+ conversation.handleConfirmationResponse(msg.requestId, "deny", {
525
+ emissionContext: { source: "system" },
526
+ });
527
+ }
528
+ });
529
+
530
+ void conversation.prompter.prompt(
531
+ "bash",
532
+ { command: "rm -rf /tmp/x" },
533
+ "high",
534
+ [],
535
+ [],
536
+ undefined,
537
+ conversation.conversationId,
538
+ "sandbox",
539
+ false,
540
+ );
541
+
542
+ expect(order[0]).toBe("sink:confirmation_request");
543
+ expect(order[1]).toBe("observer:confirmation_request");
544
+ });
545
+ });
@@ -10,10 +10,6 @@ mock.module("../config/env.js", () => ({
10
10
  hasUngatedHttpAuthDisabled: () => false,
11
11
  }));
12
12
 
13
- mock.module("../daemon/conversation-store.js", () => ({
14
- destroyActiveConversation: () => {},
15
- }));
16
-
17
13
  mock.module("../daemon/handlers/conversations.js", () => ({
18
14
  cancelGeneration: () => true,
19
15
  clearAllConversations: async () => 0,
@@ -1794,3 +1794,72 @@ describe("forkConversation + memory_retrospective_state", () => {
1794
1794
  expect(fork.slackContextCompactionWatermarkAt).toBe(compactedAt);
1795
1795
  });
1796
1796
  });
1797
+
1798
+ describe("forkConversation with unfinalized rows", () => {
1799
+ beforeEach(() => {
1800
+ resetTables();
1801
+ });
1802
+
1803
+ test("skips unfinalized rows and anchors the fork at the last finalized message", async () => {
1804
+ const source = createConversation("Mid-turn fork");
1805
+ await addMessage(source.id, "user", "question", { skipIndexing: true });
1806
+ const lastFinalized = await addMessage(
1807
+ source.id,
1808
+ "assistant",
1809
+ "completed reply",
1810
+ { skipIndexing: true },
1811
+ );
1812
+ const streaming = await addMessage(
1813
+ source.id,
1814
+ "assistant",
1815
+ "partial reply still being written",
1816
+ { skipIndexing: true },
1817
+ );
1818
+ rawRun(
1819
+ "test:markUnfinalized",
1820
+ "UPDATE messages SET finalized = 0 WHERE id = ?",
1821
+ streaming.id,
1822
+ );
1823
+
1824
+ const fork = forkConversation({ conversationId: source.id });
1825
+ const forkMessages = getMessages(fork.id);
1826
+
1827
+ // The invariant, not mere presence: exactly the finalized rows were
1828
+ // copied, every copy reads as finalized, no copy descends from the
1829
+ // unfinalized source row, and the lineage anchor is a row the fork
1830
+ // actually holds.
1831
+ expect(forkMessages).toHaveLength(2);
1832
+ expect(forkMessages.every((row) => row.finalized === 1)).toBe(true);
1833
+ const forkedFromIds = forkMessages.map(
1834
+ (row) =>
1835
+ (parseMetadata(row.metadata) as { forkSourceMessageId?: string })
1836
+ ?.forkSourceMessageId,
1837
+ );
1838
+ expect(forkedFromIds).not.toContain(streaming.id);
1839
+ expect(fork.forkParentMessageId).toBe(lastFinalized.id);
1840
+ // The source still owns its in-flight row untouched.
1841
+ expect(getMessages(source.id)).toHaveLength(3);
1842
+ });
1843
+
1844
+ test("a stale mid-history unfinalized row is skipped without shifting the anchor", async () => {
1845
+ const source = createConversation("Stale row fork");
1846
+ await addMessage(source.id, "user", "first", { skipIndexing: true });
1847
+ const stale = await addMessage(source.id, "assistant", "orphaned partial", {
1848
+ skipIndexing: true,
1849
+ });
1850
+ const tail = await addMessage(source.id, "user", "latest", {
1851
+ skipIndexing: true,
1852
+ });
1853
+ rawRun(
1854
+ "test:markUnfinalized",
1855
+ "UPDATE messages SET finalized = 0 WHERE id = ?",
1856
+ stale.id,
1857
+ );
1858
+
1859
+ const fork = forkConversation({ conversationId: source.id });
1860
+ const forkMessages = getMessages(fork.id);
1861
+
1862
+ expect(forkMessages).toHaveLength(2);
1863
+ expect(fork.forkParentMessageId).toBe(tail.id);
1864
+ });
1865
+ });
@@ -350,3 +350,70 @@ describe("referential forking", () => {
350
350
  expect(textOf(getMessages(source.id))).toHaveLength(4);
351
351
  });
352
352
  });
353
+
354
+ describe("referential forking with unfinalized rows", () => {
355
+ beforeEach(() => {
356
+ resetTables();
357
+ clearConfig();
358
+ });
359
+
360
+ test("ancestor rows are hidden while unfinalized and appear once finalized", async () => {
361
+ const source = await seedSource("Launch");
362
+ const midRow = getMessages(source.id)[1]!;
363
+ getDb()
364
+ .update(messages)
365
+ .set({ finalized: 0 })
366
+ .where(eq(messages.id, midRow.id))
367
+ .run();
368
+
369
+ setForkStrategy("reference");
370
+ const fork = await forkConversationForRetrospective({
371
+ conversationId: source.id,
372
+ });
373
+
374
+ // The fork's lineage read excludes the ancestor's in-flight row; the
375
+ // source's own read keeps it.
376
+ expect(textOf(getMessages(fork.id))).toEqual([
377
+ "draft a launch plan",
378
+ "tweak the timeline",
379
+ "updated",
380
+ ]);
381
+ expect(getMessages(source.id)).toHaveLength(4);
382
+
383
+ getDb()
384
+ .update(messages)
385
+ .set({ finalized: 1 })
386
+ .where(eq(messages.id, midRow.id))
387
+ .run();
388
+
389
+ // Once the row finalizes it enters the inherited window on its own.
390
+ expect(textOf(getMessages(fork.id))).toEqual([
391
+ "draft a launch plan",
392
+ "here is a first pass",
393
+ "tweak the timeline",
394
+ "updated",
395
+ ]);
396
+ });
397
+
398
+ test("an unfinalized tail is not the fork anchor", async () => {
399
+ const source = await seedSource("Launch");
400
+ const tail = getMessages(source.id).at(-1)!;
401
+ getDb()
402
+ .update(messages)
403
+ .set({ finalized: 0 })
404
+ .where(eq(messages.id, tail.id))
405
+ .run();
406
+
407
+ setForkStrategy("reference");
408
+ const fork = await forkConversationForRetrospective({
409
+ conversationId: source.id,
410
+ });
411
+
412
+ expect(fork.forkParentMessageId).not.toBe(tail.id);
413
+ expect(textOf(getMessages(fork.id))).toEqual([
414
+ "draft a launch plan",
415
+ "here is a first pass",
416
+ "tweak the timeline",
417
+ ]);
418
+ });
419
+ });
@@ -723,3 +723,27 @@ describe("forkConversationForRetrospective — compacted source", () => {
723
723
  });
724
724
  });
725
725
  });
726
+
727
+ describe("forkConversationForRetrospective with unfinalized rows", () => {
728
+ beforeEach(() => {
729
+ resetTables();
730
+ });
731
+
732
+ test("skips the unfinalized tail and anchors at the last finalized message", async () => {
733
+ const source = await seedSource("Mid-turn retro");
734
+ const tail = getMessages(source.id).at(-1)!;
735
+ const db = getDb();
736
+ db.run(`UPDATE messages SET finalized = 0 WHERE id = '${tail.id}'`);
737
+
738
+ const fork = await forkConversationForRetrospective({
739
+ conversationId: source.id,
740
+ });
741
+ const forkMessages = getMessages(fork.id);
742
+
743
+ // Invariant: only finalized rows are copied, and the lineage anchor is a
744
+ // row the fork actually holds.
745
+ expect(forkMessages).toHaveLength(3);
746
+ expect(forkMessages.every((row) => row.finalized === 1)).toBe(true);
747
+ expect(fork.forkParentMessageId).not.toBe(tail.id);
748
+ });
749
+ });