@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
@@ -0,0 +1,82 @@
1
+ /**
2
+ * The plugin source reconcile drives MCP reload.
3
+ *
4
+ * Without this, installing a plugin leaves its `mcp.json` servers
5
+ * unconnected and its tools unregistered until an explicit MCP reload, a
6
+ * config edit, or a restart — and, worse in the other direction, an
7
+ * uninstalled or disabled plugin's connected client and registered tools
8
+ * stay callable. The reconcile is the seam every install / uninstall /
9
+ * upgrade / enable / disable already funnels through.
10
+ *
11
+ * The guard is equally load-bearing: a reconcile also fires for plugin
12
+ * edits that touch no `mcp.json`, and reloading on those would tear down
13
+ * every healthy workspace connection to rebuild it for nothing.
14
+ */
15
+
16
+ import { beforeEach, describe, expect, jest, mock, test } from "bun:test";
17
+
18
+ let pluginServersChanged = true;
19
+
20
+ const managerStop = jest.fn(async () => {});
21
+ const managerStart = jest.fn(async () => []);
22
+
23
+ mock.module("../../mcp/effective-config.js", () => ({
24
+ buildEffectiveMcpConfig: () => ({
25
+ servers: {
26
+ unabyss: {
27
+ transport: { type: "streamable-http", url: "https://mcp.example/x" },
28
+ enabled: true,
29
+ defaultRiskLevel: "low",
30
+ maxTools: 20,
31
+ source: "plugin",
32
+ },
33
+ },
34
+ globalMaxTools: 50,
35
+ }),
36
+ pluginMcpServersChangedSinceLastBuild: () => pluginServersChanged,
37
+ }));
38
+
39
+ mock.module("../../mcp/manager.js", () => ({
40
+ getMcpServerManager: () => ({ stop: managerStop, start: managerStart }),
41
+ }));
42
+
43
+ mock.module("../../mcp/mcp-header-store.js", () => ({
44
+ migrateLegacyMcpHeaders: async () => {},
45
+ }));
46
+
47
+ mock.module("../../config/loader.js", () => ({
48
+ getConfig: () => ({ mcp: { servers: {}, globalMaxTools: 50 } }),
49
+ invalidateConfigCache: () => {},
50
+ }));
51
+
52
+ mock.module("../../tools/registry.js", () => ({
53
+ registerMcpTools: () => [],
54
+ unregisterAllMcpTools: () => {},
55
+ }));
56
+
57
+ const { reconcilePluginMcpServers } = await import("../mcp-reload-service.js");
58
+
59
+ describe("reconcilePluginMcpServers", () => {
60
+ beforeEach(() => {
61
+ managerStop.mockClear();
62
+ managerStart.mockClear();
63
+ });
64
+
65
+ test("reloads when the plugin-declared set changed", async () => {
66
+ pluginServersChanged = true;
67
+
68
+ await reconcilePluginMcpServers();
69
+
70
+ expect(managerStop).toHaveBeenCalledTimes(1);
71
+ expect(managerStart).toHaveBeenCalledTimes(1);
72
+ });
73
+
74
+ test("does nothing when no plugin changed the set", async () => {
75
+ pluginServersChanged = false;
76
+
77
+ await reconcilePluginMcpServers();
78
+
79
+ expect(managerStop).not.toHaveBeenCalled();
80
+ expect(managerStart).not.toHaveBeenCalled();
81
+ });
82
+ });
@@ -143,6 +143,7 @@ import type {
143
143
  WebSearchResultItem,
144
144
  } from "./message-types/web-activity.js";
145
145
  import { referenceMediaBlocksForPersist } from "./persist-media-references.js";
146
+ import { turnOrRestingTrust } from "./trust-context-types.js";
146
147
  import type { TurnLatencyTracker } from "./turn-latency-tracker.js";
147
148
 
148
149
  const log = getLogger("agent-loop-handlers");
@@ -1330,7 +1331,7 @@ function buildAssistantChannelMetadata(
1330
1331
  deps: EventHandlerDeps,
1331
1332
  ): Record<string, unknown> {
1332
1333
  const metadata: Record<string, unknown> = {
1333
- ...provenanceFromTrustContext(deps.ctx.trustContext),
1334
+ ...provenanceFromTrustContext(turnOrRestingTrust(deps.ctx)),
1334
1335
  userMessageChannel: deps.turnChannelContext.userMessageChannel,
1335
1336
  assistantMessageChannel: deps.turnChannelContext.assistantMessageChannel,
1336
1337
  userMessageInterface: deps.turnInterfaceContext.userMessageInterface,
@@ -1340,15 +1341,19 @@ function buildAssistantChannelMetadata(
1340
1341
  };
1341
1342
 
1342
1343
  if (deps.turnChannelContext.assistantMessageChannel === "slack") {
1343
- const channelId = deps.ctx.trustContext?.requesterChatId;
1344
+ // The whole envelope resolves from one turn-local snapshot: the actor the
1345
+ // provenance above names is the actor whose channel and thread the row
1346
+ // routes to. Reading the live slot here instead would let a concurrent
1347
+ // inbound repoint the reply mid-turn, and could stamp one actor's class
1348
+ // beside another actor's routing identity.
1349
+ const rowTrust = turnOrRestingTrust(deps.ctx);
1350
+ const channelId = rowTrust?.requesterChatId;
1344
1351
  if (channelId) {
1345
- // Resolve the reply thread from this turn's own inbound thread id,
1346
- // captured turn-locally on the trust context at ingress (the same field
1347
- // guardian-approval cards read). This is deliberately not the shared
1348
- // conversation binding: on a legacy flat→thread aliased Slack
1349
- // conversation a concurrent inbound can rewrite the binding's
1350
- // externalThreadId mid-turn, whereas the trust context is per-turn.
1351
- const turnThreadTs = deps.ctx.trustContext?.sourceThreadId;
1352
+ // This turn's own inbound thread id (the same field guardian-approval
1353
+ // cards read). Deliberately not the shared conversation binding: on a
1354
+ // legacy flat→thread aliased Slack conversation a concurrent inbound
1355
+ // can rewrite the binding's externalThreadId mid-turn.
1356
+ const turnThreadTs = rowTrust?.sourceThreadId;
1352
1357
  const threadTs = isSlackTs(turnThreadTs) ? turnThreadTs : undefined;
1353
1358
  const timestampTimezone = resolveAssistantReplyTimestampTimezone(
1354
1359
  deps.ctx,
@@ -1896,7 +1901,7 @@ function buildToolResultMetadata(
1896
1901
  deps: EventHandlerDeps,
1897
1902
  ): Record<string, unknown> {
1898
1903
  return {
1899
- ...provenanceFromTrustContext(deps.ctx.trustContext),
1904
+ ...provenanceFromTrustContext(turnOrRestingTrust(deps.ctx)),
1900
1905
  userMessageChannel: deps.turnChannelContext.userMessageChannel,
1901
1906
  assistantMessageChannel: deps.turnChannelContext.assistantMessageChannel,
1902
1907
  userMessageInterface: deps.turnInterfaceContext.userMessageInterface,
@@ -61,7 +61,11 @@ import { HOOKS } from "../plugin-api/constants.js";
61
61
  import type { ConversationGraphMemory } from "../plugins/defaults/memory/graph/conversation-graph-memory.js";
62
62
  import { enqueueMemoryRetrospectiveOnCompaction } from "../plugins/defaults/memory/memory-retrospective-enqueue.js";
63
63
  import { runHook } from "../plugins/pipeline.js";
64
- import { isManagedConnectionRoute } from "../providers/connection-resolution.js";
64
+ import {
65
+ dispatchProviderResolvable,
66
+ isManagedConnectionRoute,
67
+ resolveEntryConnectionName,
68
+ } from "../providers/connection-resolution.js";
65
69
  import {
66
70
  ConnectionResolutionError,
67
71
  resolveRoutingIdentity,
@@ -130,6 +134,7 @@ import {
130
134
  } from "./inflight-turn-registry.js";
131
135
  import type { UsageStats } from "./message-protocol.js";
132
136
  import type { TrustContext } from "./trust-context-types.js";
137
+ import { turnOrRestingTrust } from "./trust-context-types.js";
133
138
  import { resolveTurnCallSite } from "./turn-call-site.js";
134
139
  import { runWithLatencySubSpans } from "./turn-latency-sub-spans.js";
135
140
  import {
@@ -315,6 +320,8 @@ export async function runAgentLoopImpl(
315
320
  * the turn.
316
321
  */
317
322
  isHiddenPrompt?: boolean;
323
+ /** Daemon-authored kind of the user row that triggered this turn. */
324
+ messageKind?: string;
318
325
  /**
319
326
  * Row the end-of-turn reply notification should treat as the prompt this
320
327
  * turn answers. Defaults to `userMessageId`; a coalesced batch overrides it
@@ -399,6 +406,17 @@ export async function runAgentLoopImpl(
399
406
  // LUM-3148 removes the ambiguity by making trust ride the turn.
400
407
  ctx.currentTurnTrustContext = options?.turnTrustContext ?? ctx.trustContext;
401
408
  ctx.currentTurnChannelCapabilities = ctx.channelCapabilities;
409
+ // Presence is the third per-turn snapshot: whether a human is present to see
410
+ // UI and answer prompts. Callers declare it via `isInteractive`; a caller
411
+ // that omits it gets a non-interactive turn. Set before the prompt build
412
+ // below, which reads it (through `hasNoClient`).
413
+ const isInteractiveResolved = options?.isInteractive ?? false;
414
+ // Resolved once and threaded into every re-injection (including the
415
+ // post-compaction hook) rather than re-read per assembly call, and exposed
416
+ // to tool execution so tools (e.g. ask_question) see whether a human is
417
+ // present rather than re-deriving it from live state.
418
+ const isNonInteractive = !isInteractiveResolved;
419
+ ctx.currentTurnIsNonInteractive = isNonInteractive;
402
420
 
403
421
  // Re-resolve the system prompt under the snapshots just set and push it into
404
422
  // the loop when the persona changed. The loop reuses the prompt frozen at
@@ -491,10 +509,13 @@ export async function runAgentLoopImpl(
491
509
  const turnErrorAttribution = (): ConversationErrorAttribution => {
492
510
  try {
493
511
  const overrideProfile = readCurrentOverrideProfile();
512
+ // Mirrors dispatch's selection (including the resolvable-provider
513
+ // healing) so attribution names the profile that actually ran.
494
514
  const resolveOpts = {
495
515
  overrideProfile,
496
516
  forceOverrideProfile,
497
517
  selectionSeed: ctx.conversationId,
518
+ isResolvableProvider: dispatchProviderResolvable,
498
519
  };
499
520
  const { config: resolved, profileName } =
500
521
  resolveCallSiteConfigWithProfile(turnCallSite, config.llm, resolveOpts);
@@ -509,6 +530,10 @@ export async function runAgentLoopImpl(
509
530
  }
510
531
  connectionName = error.connectionName;
511
532
  }
533
+ // An entry-name provider IS the connection name, matching the
534
+ // dispatch-side translation.
535
+ connectionName ??=
536
+ resolveEntryConnectionName(resolved.provider) ?? undefined;
512
537
  // Managed-ness comes from the connection row, matching what dispatch
513
538
  // decides. The profile's own provider can't stand in: a concrete
514
539
  // provider tweak over a managed winner keeps the managed connection
@@ -666,19 +691,6 @@ export async function runAgentLoopImpl(
666
691
  };
667
692
  })();
668
693
 
669
- const isInteractiveResolved =
670
- options?.isInteractive ?? (!ctx.hasNoClient && !ctx.headlessLock);
671
- // Whether the in-flight turn has no human present to answer clarification
672
- // questions. Derived from the loop's `isInteractive` option (which can fall
673
- // back to mutable client/headless state that flips mid-turn), so it is
674
- // resolved once here and threaded into every re-injection — including the
675
- // post-compaction hook — rather than re-read per assembly call.
676
- const isNonInteractive = !isInteractiveResolved;
677
- // Expose the resolved turn-level interactivity to tool execution so tools
678
- // (e.g. ask_question) see whether a human is present to answer, rather than
679
- // re-deriving it from live client state that misclassifies a scheduled turn
680
- // running on a client-attached conversation.
681
- ctx.currentTurnIsNonInteractive = isNonInteractive;
682
694
  const diskPressureDecision = classifyDiskPressureTurnPolicy(
683
695
  getDiskPressureStatus(),
684
696
  {
@@ -1192,6 +1204,7 @@ export async function runAgentLoopImpl(
1192
1204
  requestId: reqId,
1193
1205
  prompt: options?.titleText ?? content,
1194
1206
  isHiddenPrompt: options?.isHiddenPrompt === true,
1207
+ messageKind: options?.messageKind,
1195
1208
  originalMessages: Object.freeze([...ctx.messages]),
1196
1209
  latestMessages: ctx.messages,
1197
1210
  modelProfileKey,
@@ -1283,8 +1296,7 @@ export async function runAgentLoopImpl(
1283
1296
  // context, then the fallback — matching the trust the runtime injection
1284
1297
  // assembly resolves for the same turn. The loop's other turn-identity
1285
1298
  // fields self-resolve from its own conversation id.
1286
- const loopTrust =
1287
- ctx.currentTurnTrustContext ?? ctx.trustContext ?? FALLBACK_TURN_TRUST;
1299
+ const loopTrust = ctx.getTurnOrRestingTrust() ?? FALLBACK_TURN_TRUST;
1288
1300
 
1289
1301
  /**
1290
1302
  * Shared closure: runs the agent loop with the wrapper's turn context and
@@ -1404,7 +1416,7 @@ export async function runAgentLoopImpl(
1404
1416
  // row here rather than writing a duplicate.
1405
1417
  if (state.pendingToolResults.size > 0) {
1406
1418
  const toolResultMetadata = {
1407
- ...provenanceFromTrustContext(ctx.trustContext),
1419
+ ...provenanceFromTrustContext(turnOrRestingTrust(ctx)),
1408
1420
  userMessageChannel: capturedTurnChannelContext.userMessageChannel,
1409
1421
  assistantMessageChannel:
1410
1422
  capturedTurnChannelContext.assistantMessageChannel,
@@ -1431,7 +1443,7 @@ export async function runAgentLoopImpl(
1431
1443
  budgetYieldClassification.userMessage,
1432
1444
  );
1433
1445
  const yieldNoticeMetadata = {
1434
- ...provenanceFromTrustContext(ctx.trustContext),
1446
+ ...provenanceFromTrustContext(turnOrRestingTrust(ctx)),
1435
1447
  userMessageChannel: capturedTurnChannelContext.userMessageChannel,
1436
1448
  assistantMessageChannel:
1437
1449
  capturedTurnChannelContext.assistantMessageChannel,
@@ -1565,7 +1577,7 @@ export async function runAgentLoopImpl(
1565
1577
  state.lastAssistantMessageId = undefined;
1566
1578
  } else {
1567
1579
  const errChannelMeta = {
1568
- ...provenanceFromTrustContext(ctx.trustContext),
1580
+ ...provenanceFromTrustContext(turnOrRestingTrust(ctx)),
1569
1581
  userMessageChannel: capturedTurnChannelContext.userMessageChannel,
1570
1582
  assistantMessageChannel:
1571
1583
  capturedTurnChannelContext.assistantMessageChannel,
@@ -68,6 +68,7 @@ import type { SlackInboundMessageMetadata } from "./handlers/shared.js";
68
68
  import type { UserMessageAttachment } from "./message-protocol.js";
69
69
  import type { ConversationTransportMetadata } from "./message-types/conversations.js";
70
70
  import type { TrustContext } from "./trust-context-types.js";
71
+ import { restingTrust } from "./trust-context-types.js";
71
72
 
72
73
  const log = getLogger("conversation-messaging");
73
74
 
@@ -863,7 +864,10 @@ export async function persistQueuedMessageBody(
863
864
  const turnIfCtx =
864
865
  extractTurnInterfaceContext(metadata) ?? ctx.getTurnInterfaceContext();
865
866
  const provenance = provenanceFromTrustContext(
866
- options.trustContext ?? ctx.trustContext,
867
+ // Callers that own a turn pass the sender's trust; the fallback serves
868
+ // ingress paths that persist before any per-turn stamp exists, where
869
+ // the slot their own resolution just wrote is the right actor.
870
+ options.trustContext ?? restingTrust(ctx),
867
871
  );
868
872
  const imageSourcePaths = extractImageSourcePaths(attachments);
869
873
 
@@ -3,8 +3,10 @@
3
3
  * the Conversation constructor and dispose/abort methods.
4
4
  *
5
5
  * Notifier callbacks read from the provided context object at invocation
6
- * time (not registration time), so they always see the latest sendToClient
7
- * and messages references even after updateClient().
6
+ * time (not registration time), so they always see the latest messages
7
+ * reference. They emit through the conversation, whose sink is fixed for its
8
+ * life, so an out-of-turn notification reaches every subscribed client
9
+ * without any per-turn wiring.
8
10
  */
9
11
 
10
12
  import { createAssistantMessage } from "../agent/message-types.js";
@@ -25,13 +27,14 @@ import {
25
27
  } from "../persistence/conversation-crud.js";
26
28
  import type { Message } from "../providers/types.js";
27
29
  import type { TrustContext } from "./trust-context-types.js";
30
+ import { restingTrust } from "./trust-context-types.js";
28
31
 
29
32
  /**
30
33
  * Subset of Conversation state that notifier callbacks need to read at
31
34
  * invocation time. Properties are read lazily from this reference.
32
35
  */
33
36
  export interface NotifierConversationContext {
34
- sendToClient: (msg: AssistantEvent) => void;
37
+ emit: (msg: AssistantEvent) => void;
35
38
  messages: Message[];
36
39
  trustContext?: TrustContext;
37
40
  }
@@ -58,7 +61,14 @@ export function registerConversationNotifiers(
58
61
  JSON.stringify([{ type: "text", text: questionText }]),
59
62
  {
60
63
  metadata: {
61
- ...provenanceFromTrustContext(ctx.trustContext),
64
+ ...provenanceFromTrustContext(
65
+ // This callback fires after the voice turn has settled: the
66
+ // per-turn field may still hold that turn's actor (nothing
67
+ // clears it at release), while voice cleanup has restored the
68
+ // conversation's resting trust. The owner is the right actor
69
+ // for a row persisted outside any turn.
70
+ restingTrust(ctx),
71
+ ),
62
72
  userMessageChannel: "phone",
63
73
  assistantMessageChannel: "phone",
64
74
  userMessageInterface: "phone",
@@ -69,12 +79,12 @@ export function registerConversationNotifiers(
69
79
 
70
80
  ctx.messages.push(createAssistantMessage(questionText));
71
81
 
72
- ctx.sendToClient({
82
+ ctx.emit({
73
83
  type: "assistant_text_delta",
74
84
  text: questionText,
75
85
  conversationId: conversationId,
76
86
  });
77
- ctx.sendToClient({
87
+ ctx.emit({
78
88
  type: "message_complete",
79
89
  conversationId: conversationId,
80
90
  messageId: msg.id,
@@ -89,12 +99,12 @@ export function registerConversationNotifiers(
89
99
  const speakerLabel = speaker === "caller" ? "Caller" : "Assistant";
90
100
  const transcriptText = `**Live call transcript**\n${speakerLabel}: ${text}`;
91
101
 
92
- ctx.sendToClient({
102
+ ctx.emit({
93
103
  type: "assistant_text_delta",
94
104
  text: transcriptText,
95
105
  conversationId: conversationId,
96
106
  });
97
- ctx.sendToClient({
107
+ ctx.emit({
98
108
  type: "message_complete",
99
109
  conversationId: conversationId,
100
110
  source: "aux",
@@ -105,12 +115,12 @@ export function registerConversationNotifiers(
105
115
  registerCallCompletionNotifier(conversationId, (callSessionId: string) => {
106
116
  const summaryText = buildCallCompletionMessage(callSessionId);
107
117
 
108
- ctx.sendToClient({
118
+ ctx.emit({
109
119
  type: "assistant_text_delta",
110
120
  text: summaryText,
111
121
  conversationId: conversationId,
112
122
  });
113
- ctx.sendToClient({
123
+ ctx.emit({
114
124
  type: "message_complete",
115
125
  conversationId: conversationId,
116
126
  source: "aux",
@@ -63,6 +63,7 @@ import { preactivateHostProxySkills } from "./host-proxy-preactivation.js";
63
63
  import type { UserMessageAttachment } from "./message-protocol.js";
64
64
  import { buildTransportHints } from "./transport-hints.js";
65
65
  import { sameTrustIdentity, type TrustContext } from "./trust-context-types.js";
66
+ import { turnOrRestingTrust } from "./trust-context-types.js";
66
67
  import { resolveVerificationSessionIntent } from "./verification-session-intent.js";
67
68
 
68
69
  const log = getLogger("conversation-process");
@@ -823,7 +824,7 @@ async function drainSingleMessage(
823
824
  if (slashResult.kind === "unknown") {
824
825
  try {
825
826
  const drainProvenance = provenanceFromTrustContext(
826
- conversation.trustContext,
827
+ turnOrRestingTrust(conversation),
827
828
  );
828
829
  const drainImageSourcePaths: Record<string, string> = {};
829
830
  for (let i = 0; i < next.attachments.length; i++) {
@@ -938,7 +939,7 @@ async function drainSingleMessage(
938
939
  let persistedCompactMessage = false;
939
940
  try {
940
941
  const drainProvenance = provenanceFromTrustContext(
941
- conversation.trustContext,
942
+ turnOrRestingTrust(conversation),
942
943
  );
943
944
  const drainChannelMeta = {
944
945
  ...drainProvenance,
@@ -1031,7 +1032,7 @@ async function drainSingleMessage(
1031
1032
  let persistedCleanMessage = false;
1032
1033
  try {
1033
1034
  const drainProvenance = provenanceFromTrustContext(
1034
- conversation.trustContext,
1035
+ turnOrRestingTrust(conversation),
1035
1036
  );
1036
1037
  const drainChannelMeta = {
1037
1038
  ...drainProvenance,
@@ -1826,6 +1827,13 @@ export interface ProcessMessageOptions {
1826
1827
  displayContent?: string;
1827
1828
  /** JWT-verified committer principal for turn-scoped host-proxy authorization. */
1828
1829
  sourceActorPrincipalId?: string;
1830
+ /**
1831
+ * The actor this turn is being started for. Stamped onto the conversation
1832
+ * before history is scoped, so the run hydrates as its committer rather
1833
+ * than as whoever last left the resting slot set. Callers with no actor of
1834
+ * their own (internal dispatch) omit it and the resting actor stands.
1835
+ */
1836
+ trustContext?: TrustContext;
1829
1837
  /**
1830
1838
  * True when this turn was auto-sent on the user's behalf rather than typed
1831
1839
  * (see `PersistMessageOptions.scripted`). Forwarded to persistence so the
@@ -1872,7 +1880,11 @@ export async function processMessage(
1872
1880
  sourceActorPrincipalId,
1873
1881
  scripted,
1874
1882
  metadata: callerMetadata,
1883
+ trustContext: committingTrustContext,
1875
1884
  } = options;
1885
+ if (committingTrustContext) {
1886
+ conversation.setTrustContext(committingTrustContext);
1887
+ }
1876
1888
  await conversation.ensureActorScopedHistory();
1877
1889
  // Snapshot persona context at turn start so later tool turns can't pick up
1878
1890
  // a different actor's context if a concurrent request mutates the live fields.
@@ -2024,7 +2036,9 @@ export async function processMessage(
2024
2036
  if (slashResult.kind === "unknown") {
2025
2037
  const pmTurnCtx = conversation.getTurnChannelContext();
2026
2038
  const pmInterfaceCtx = conversation.getTurnInterfaceContext();
2027
- const pmProvenance = provenanceFromTrustContext(conversation.trustContext);
2039
+ const pmProvenance = provenanceFromTrustContext(
2040
+ turnOrRestingTrust(conversation),
2041
+ );
2028
2042
  const pmImageSourcePaths: Record<string, string> = {};
2029
2043
  for (let i = 0; i < attachments.length; i++) {
2030
2044
  const a = attachments[i];
@@ -2116,7 +2130,7 @@ export async function processMessage(
2116
2130
  const pmTurnCtx = conversation.getTurnChannelContext();
2117
2131
  const pmInterfaceCtx = conversation.getTurnInterfaceContext();
2118
2132
  const pmProvenance = provenanceFromTrustContext(
2119
- conversation.trustContext,
2133
+ turnOrRestingTrust(conversation),
2120
2134
  );
2121
2135
  const pmChannelMeta = {
2122
2136
  ...pmProvenance,
@@ -2197,7 +2211,7 @@ export async function processMessage(
2197
2211
  const pmTurnCtx = conversation.getTurnChannelContext();
2198
2212
  const pmInterfaceCtx = conversation.getTurnInterfaceContext();
2199
2213
  const pmProvenance = provenanceFromTrustContext(
2200
- conversation.trustContext,
2214
+ turnOrRestingTrust(conversation),
2201
2215
  );
2202
2216
  const pmChannelMeta = {
2203
2217
  ...pmProvenance,
@@ -44,6 +44,7 @@ import {
44
44
  type RenderedSlackTranscriptMessage,
45
45
  renderSlackTranscriptWithProvenance,
46
46
  } from "../messaging/providers/slack/render-transcript.js";
47
+ import { isGuardianCardRow } from "../notifications/approval-card-data.js";
47
48
  import {
48
49
  getMessages as defaultGetMessages,
49
50
  type MessageRow,
@@ -520,7 +521,7 @@ function injectActiveSurfaceContext(
520
521
  "RULES FOR WORKSPACE MODIFICATION:",
521
522
  `1. Use \`file_edit\` to make surgical changes to app files. The file path is \`${getAppDirPath(ctx.appId)}/<path>\`.`,
522
523
  "2. Use `file_write` to create new files or rewrite files.",
523
- "3. Use `file_read` to read any file with line numbers before editing.",
524
+ "3. Use `file_read` to read any file before editing, and `code_search` when you need a line reference.",
524
525
  "4. Use `bash ls` to see all files in the app directory.",
525
526
  `5. Call \`app_refresh\` with app_id "${ctx.appId}" ONCE after all changes are complete.`,
526
527
  "6. NEVER respond with only text — the user expects a visual update.",
@@ -1513,10 +1514,16 @@ export function loadSlackChronologicalContext(
1513
1514
  allRows,
1514
1515
  options.trustClass,
1515
1516
  );
1517
+ // Applied after the compaction boundary, never before it: that filter
1518
+ // indexes into the row list when no watermark is set, so dropping rows
1519
+ // earlier would shift what it cuts. Same rule and same ordering constraint
1520
+ // as `Conversation.loadFromDb` -- Slack builds the provider history from
1521
+ // rows rather than `this.messages`, so a rule applied only there would
1522
+ // exempt this channel entirely.
1516
1523
  const rows = filterRowsAfterSlackCompactionBoundary(
1517
1524
  messageRowsToSlackTranscriptRows(scopedRows),
1518
1525
  options,
1519
- );
1526
+ ).filter((row) => !isGuardianCardRow(row.content));
1520
1527
  return assembleSlackChronologicalContext(rows, capabilities, {
1521
1528
  contextSummary: resolveCapabilities(options.trustClass).canAccessMemory
1522
1529
  ? options.contextSummary
@@ -2319,7 +2326,7 @@ export async function applyRuntimeInjections(
2319
2326
  // folded into the summary are excluded.
2320
2327
  const slackActiveThreadFocusBlock = channelCapabilities
2321
2328
  ? loadSlackActiveThreadFocusBlock(conversationId, channelCapabilities, {
2322
- trustClass: liveConversation?.trustContext?.trustClass,
2329
+ trustClass: liveConversation?.getTrustContext()?.trustClass,
2323
2330
  contextCompactedMessageCount:
2324
2331
  liveConversation?.contextCompactedMessageCount,
2325
2332
  slackContextCompactionWatermarkTs:
@@ -2337,7 +2344,7 @@ export async function applyRuntimeInjections(
2337
2344
  // boundary without the orchestrator threading a snapshot in.
2338
2345
  const slackChronologicalMessages = channelCapabilities
2339
2346
  ? loadSlackChronologicalMessages(conversationId, channelCapabilities, {
2340
- trustClass: liveConversation?.trustContext?.trustClass,
2347
+ trustClass: liveConversation?.getTrustContext()?.trustClass,
2341
2348
  contextSummary: liveConversation?.contextSummary,
2342
2349
  contextCompactedMessageCount:
2343
2350
  liveConversation?.contextCompactedMessageCount,
@@ -2380,8 +2387,7 @@ export async function applyRuntimeInjections(
2380
2387
  turnIndex: options.turnIndex ?? liveConversation?.turnCount ?? 0,
2381
2388
  trust:
2382
2389
  options.trust ??
2383
- liveConversation?.currentTurnTrustContext ??
2384
- liveConversation?.trustContext ??
2390
+ liveConversation?.getTurnOrRestingTrust() ??
2385
2391
  fallbackTurnTrust(channelCapabilities),
2386
2392
  callSite: options.callSite ?? liveConversation?.currentCallSite,
2387
2393
  ...injectionInputs,
@@ -27,6 +27,7 @@ import {
27
27
  } from "../providers/connection-resolution.js";
28
28
  import { RateLimitProvider } from "../providers/ratelimit.js";
29
29
  import { listProviders } from "../providers/registry.js";
30
+ import { broadcastMessage } from "../runtime/assistant-event-hub.js";
30
31
  import { getSubagentManager } from "../subagent/index.js";
31
32
  import { getSandboxWorkingDir } from "../util/platform.js";
32
33
  import { Conversation } from "./conversation.js";
@@ -150,13 +151,19 @@ function applyTransportMetadata(
150
151
  *
151
152
  * Handles provider setup, rate limiting, system prompt, memory policy,
152
153
  * and conversation hydration.
154
+ *
155
+ * The in-memory Conversation snapshots the tool registry at construction.
156
+ * An empty snapshot is an empty tool list on the wire for the life of the
157
+ * instance, so the create path initializes the registry before that
158
+ * constructor runs. A hit in `findConversation` already has its snapshot.
159
+ * `initializeTools` is idempotent: a process that already initialized at
160
+ * boot awaits the settled promise.
153
161
  */
154
162
  export async function getOrCreateConversation(
155
163
  conversationId: string,
156
164
  options?: ConversationCreateOptions,
157
165
  ): Promise<Conversation> {
158
166
  let conversation = findConversation(conversationId);
159
- const sendToClient = () => {};
160
167
 
161
168
  // `taskRunId` and `ephemeral` are per-call scopes, not durable conversation
162
169
  // metadata, so they are stripped before the remaining options are merged
@@ -228,11 +235,16 @@ export async function getOrCreateConversation(
228
235
  const systemPrompt = await resolveInitialSystemPrompt(storedOptions);
229
236
  const maxTokens = storedOptions?.maxResponseTokens;
230
237
 
238
+ const { initializeTools } = await import("../tools/registry.js");
239
+ await initializeTools();
240
+
231
241
  const newConversation = new Conversation(
232
242
  conversationId,
233
243
  provider,
234
244
  systemPrompt,
235
- sendToClient,
245
+ // Top-level conversations deliver to the SSE hub for their whole life,
246
+ // so every subscribed client sees every event with no per-turn wiring.
247
+ broadcastMessage,
236
248
  workingDir,
237
249
  {
238
250
  maxTokens,
@@ -240,8 +252,6 @@ export async function getOrCreateConversation(
240
252
  modelOverride: storedOptions?.modelOverride,
241
253
  },
242
254
  );
243
- newConversation.updateClient(sendToClient, true);
244
-
245
255
  // Ensure the conversations row exists before hydrating from DB.
246
256
  // `getOrCreateConversation` builds the in-memory Conversation, but
247
257
  // the persisted row is what `loadFromDb` reads for conversationType,