@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
@@ -14,6 +14,7 @@
14
14
  */
15
15
 
16
16
  import type { AssistantEvent } from "../api/index.js";
17
+ import type { ChannelId } from "../channels/types.js";
17
18
  import type { LLMCallSite } from "../config/schemas/llm.js";
18
19
  import type { UserMessageAttachment } from "../daemon/message-types/shared.js";
19
20
  import type { ContentBlock, MediaSource } from "../providers/types.js";
@@ -22,12 +23,62 @@ import type { ContentBlock, MediaSource } from "../providers/types.js";
22
23
  // Public types
23
24
  // ---------------------------------------------------------------------------
24
25
 
26
+ /**
27
+ * The chat a turn belongs to, in the channel's own terms.
28
+ *
29
+ * Supplied instead of a `conversationId` when the caller knows where the
30
+ * message came from but not which conversation that is. The pair resolves to
31
+ * the same conversation an inbound message on those coordinates would land
32
+ * in, because it resolves through the same binding.
33
+ *
34
+ * That sameness is the reason this exists at all. A caller could keep its own
35
+ * `externalChatId -> conversationId` map and pass the id, and turns would run
36
+ * in the right place. What it could not do is make the rest of the assistant
37
+ * agree: conversation reset is addressed by channel coordinates
38
+ * (`handleDeleteConversation`), the deny lanes attach an access-request card
39
+ * by them (`findInboundConversationId`), and the conversation and session
40
+ * APIs read the channel, chat name and sender off the external binding. A
41
+ * private map is a second name for the same conversation, and everything
42
+ * keyed on the public one quietly misses.
43
+ */
44
+ export interface ConversationChannelAddress {
45
+ /** Channel the message arrived on. */
46
+ sourceChannel: ChannelId;
47
+ /** The chat, in the channel's own id space. */
48
+ externalChatId: string;
49
+ /**
50
+ * Thread within the chat, where the channel has threads. Only Slack and
51
+ * Telegram scope a conversation by thread; elsewhere this is carried as
52
+ * binding metadata and does not split the conversation.
53
+ */
54
+ externalThreadId?: string | null;
55
+ /** Human-readable chat name, for the conversation list. */
56
+ externalChatName?: string | null;
57
+ /** Who sent it, in the channel's id space. */
58
+ externalUserId?: string | null;
59
+ displayName?: string | null;
60
+ username?: string | null;
61
+ }
62
+
25
63
  export interface RunConversationTurnOptions {
26
64
  /**
27
65
  * Conversation to run the turn in. If omitted, a new conversation is
28
- * created (its ID is generated with `uuidv7` and returned in the result).
66
+ * created (its ID is generated with `uuidv7` and returned in the result),
67
+ * unless {@link RunConversationTurnOptions.channel} says which chat this
68
+ * belongs to, in which case that chat's conversation is used.
29
69
  */
30
70
  conversationId?: string;
71
+ /**
72
+ * The chat this turn belongs to, resolved to a conversation and bound to
73
+ * the channel. See {@link ConversationChannelAddress}.
74
+ *
75
+ * Ignored when `conversationId` is given: an explicit conversation is the
76
+ * caller saying which one, and re-resolving would overrule it. A caller
77
+ * that wants both the binding and a conversation of its own choosing is
78
+ * describing two different conversations, which is a bug worth surfacing
79
+ * as one rather than silently picking a winner.
80
+ */
81
+ channel?: ConversationChannelAddress;
31
82
  /**
32
83
  * User message content blocks for this turn. Text blocks become the
33
84
  * user message body; image/file blocks are resolved to inline
@@ -121,6 +172,77 @@ function extractContentAndAttachments(
121
172
  return { text: textParts.join("\n"), attachments };
122
173
  }
123
174
 
175
+ /**
176
+ * Resolve a chat to its conversation, binding the two if they are not already.
177
+ *
178
+ * Two writes, the same two `handleChannelInbound` makes for an arriving
179
+ * message, and for the same reasons. The conversation key is what makes the
180
+ * chat resolve to this conversation next time and what conversation reset
181
+ * addresses; the external binding is what the conversation and session APIs
182
+ * read to show which channel a conversation came from and who is on the other
183
+ * end. One without the other is a conversation that is half-registered, and
184
+ * which half is missing decides which feature quietly stops working.
185
+ *
186
+ * Deliberately not `recordInbound`, which is the same resolution plus an
187
+ * inbound event row. That row exists to dedup a vendor's redelivery and to
188
+ * correlate later edits, and a caller driving a turn on its own schedule has
189
+ * neither a vendor nor a message id to correlate. Plugin deliveries are
190
+ * deduped upstream in the gateway before the message ever gets here.
191
+ */
192
+ async function resolveChannelConversation(
193
+ channel: ConversationChannelAddress,
194
+ conversationType?: "standard" | "background",
195
+ ): Promise<{ conversationId: string; created: boolean }> {
196
+ const { findInboundConversationId, resolveInboundConversation } =
197
+ await import("../persistence/delivery-crud.js");
198
+ const { upsertBinding } =
199
+ await import("../persistence/external-conversation-store.js");
200
+
201
+ // Asked before resolving, because resolving is what creates it: once the id
202
+ // is in hand a new conversation is indistinguishable from an existing one,
203
+ // and the caller announces only the new. `findInboundConversationId` is the
204
+ // read-only mirror of the resolution below, so the two agree on what
205
+ // "already bound" means, including the Slack flat-key alias.
206
+ const created =
207
+ findInboundConversationId(
208
+ channel.sourceChannel,
209
+ channel.externalChatId,
210
+ channel.externalThreadId,
211
+ ) === null;
212
+
213
+ const { conversationId } = resolveInboundConversation(
214
+ channel.sourceChannel,
215
+ channel.externalChatId,
216
+ channel.externalThreadId,
217
+ // Both apply only if this call is what mints the conversation. `origin` is
218
+ // first-message attribution, which a chat's first turn is the only chance
219
+ // to record.
220
+ {
221
+ origin: channel.sourceChannel,
222
+ ...(conversationType ? { conversationType } : {}),
223
+ },
224
+ );
225
+
226
+ // Refreshed on every turn rather than only on creation, because the sender's
227
+ // display name and the chat's name are the vendor's to change and the
228
+ // conversation list shows whichever we last heard. The optional fields pass
229
+ // through as given: a caller that omits one on a later turn knows only the
230
+ // chat coordinates this time, which `upsertBinding` reads as silence rather
231
+ // than as the sender having no name.
232
+ upsertBinding({
233
+ conversationId,
234
+ sourceChannel: channel.sourceChannel,
235
+ externalChatId: channel.externalChatId,
236
+ externalChatName: channel.externalChatName,
237
+ externalThreadId: channel.externalThreadId ?? null,
238
+ externalUserId: channel.externalUserId,
239
+ displayName: channel.displayName,
240
+ username: channel.username,
241
+ });
242
+
243
+ return { conversationId, created };
244
+ }
245
+
124
246
  // ---------------------------------------------------------------------------
125
247
  // Public API
126
248
  // ---------------------------------------------------------------------------
@@ -158,6 +280,9 @@ export async function runConversationTurn(
158
280
  await import("../persistence/conversation-crud.js");
159
281
  const { publishConversationListAndMetadataChanged } =
160
282
  await import("../runtime/sync/resource-sync-events.js");
283
+ const { parseInterfaceId } = await import("../channels/types.js");
284
+ const { resolveChannelCapabilities } =
285
+ await import("../daemon/conversation-runtime-assembly.js");
161
286
 
162
287
  // Plugin-driven turns run as the guardian: plugins are installed by the
163
288
  // guardian, so their conversations inherit guardian trust. This lets the
@@ -166,8 +291,25 @@ export async function runConversationTurn(
166
291
  // below) without requiring a client to approve prompts.
167
292
  const { INTERNAL_GUARDIAN_TRUST_CONTEXT } =
168
293
  await import("../daemon/trust-context.js");
169
- const conversationId = options.conversationId ?? uuidv7();
170
- const rowExisted = getConversation(conversationId) != null;
294
+
295
+ // A channel address resolves through the same binding an inbound message
296
+ // uses, so a turn addressed by chat lands in that chat's conversation
297
+ // rather than a private one only this caller can find.
298
+ const channelConversation =
299
+ !options.conversationId && options.channel
300
+ ? await resolveChannelConversation(
301
+ options.channel,
302
+ options.conversationType,
303
+ )
304
+ : undefined;
305
+ const conversationId =
306
+ options.conversationId ?? channelConversation?.conversationId ?? uuidv7();
307
+ // A channel address reports its own novelty, since resolving it creates the
308
+ // row: asking the DB here answers yes for a conversation minted a line ago
309
+ // and swallows the announcement it is owed.
310
+ const rowExisted = channelConversation
311
+ ? !channelConversation.created
312
+ : getConversation(conversationId) != null;
171
313
  const conversation = await getOrCreateConversation(conversationId, {
172
314
  trustContext: INTERNAL_GUARDIAN_TRUST_CONTEXT,
173
315
  ...(options.conversationType
@@ -199,6 +341,51 @@ export async function runConversationTurn(
199
341
  resolveMediaSourceData,
200
342
  );
201
343
 
344
+ // The channel this turn speaks on. Runtime assembly reads the per-turn
345
+ // context first and falls back to the conversation's `originChannel`, then
346
+ // to `vellum`, so a turn into a conversation carrying no origin runs as
347
+ // `vellum` without this: the wrong channel for the message row and for the
348
+ // channel-permission cascade the tools are approved against. Null when the
349
+ // caller names no channel, which both restores that fallback and clears any
350
+ // context left by a previous turn.
351
+ const turnChannelContext = options.channel
352
+ ? {
353
+ userMessageChannel: options.channel.sourceChannel,
354
+ assistantMessageChannel: options.channel.sourceChannel,
355
+ }
356
+ : null;
357
+ // The interface the message arrived on. `plugin` is in both the channel
358
+ // and interface unions, matching gateway inbound. Tool gating reads this
359
+ // as `transportInterface`; unset, the loop falls back to `web`.
360
+ const turnInterfaceContext = options.channel
361
+ ? (() => {
362
+ const iface = parseInterfaceId(options.channel.sourceChannel);
363
+ return iface
364
+ ? {
365
+ userMessageInterface: iface,
366
+ assistantMessageInterface: iface,
367
+ }
368
+ : null;
369
+ })()
370
+ : null;
371
+ if (options.channel) {
372
+ conversation.setChannelCapabilities(
373
+ resolveChannelCapabilities(
374
+ options.channel.sourceChannel,
375
+ turnInterfaceContext?.userMessageInterface,
376
+ ),
377
+ );
378
+ }
379
+ // Carried on the metadata as well, because a queued turn is drained after
380
+ // this call returns and reads its channel from there. Without it the drain
381
+ // inherits whichever turn was in flight, which on a shared conversation is
382
+ // some other channel entirely.
383
+ const metadata = {
384
+ ...PLUGIN_TURN_MESSAGE_METADATA,
385
+ ...(turnChannelContext ?? {}),
386
+ ...(turnInterfaceContext ?? {}),
387
+ };
388
+
202
389
  // Build the event emitter: fan out to SSE/event-hub subscribers via
203
390
  // broadcastMessage, then invoke the collector callback. This mirrors the
204
391
  // buildEventEmitter pattern from process-message.ts so plugin-driven
@@ -221,7 +408,7 @@ export async function runConversationTurn(
221
408
  onEvent,
222
409
  requestId,
223
410
  isInteractive: false,
224
- metadata: { ...PLUGIN_TURN_MESSAGE_METADATA },
411
+ metadata,
225
412
  });
226
413
  if (enqueueResult.rejected) {
227
414
  throw new Error(
@@ -236,12 +423,15 @@ export async function runConversationTurn(
236
423
  };
237
424
  }
238
425
 
426
+ conversation.setTurnChannelContext(turnChannelContext);
427
+ conversation.setTurnInterfaceContext(turnInterfaceContext);
428
+
239
429
  const userMessageId = await conversation.processMessage({
240
430
  content: text,
241
431
  attachments,
242
432
  onEvent,
243
433
  isInteractive: false,
244
- metadata: { ...PLUGIN_TURN_MESSAGE_METADATA },
434
+ metadata,
245
435
  ...(options.callSite ? { callSite: options.callSite } : {}),
246
436
  });
247
437
 
@@ -67,7 +67,11 @@
67
67
  */
68
68
 
69
69
  export type { HookName } from "./constants.js";
70
- export { HOOKS, INTERNAL_NUDGE_OUTPUT_SUPPRESSION } from "./constants.js";
70
+ export {
71
+ HOOKS,
72
+ INTERNAL_NUDGE_OUTPUT_SUPPRESSION,
73
+ VOICE_ESCALATION_CONTINUATION_MESSAGE_KIND,
74
+ } from "./constants.js";
71
75
  // Conversation message/content shapes. A hook receives the live message
72
76
  // history (e.g. `PostToolUseContext.latestMessages: Message[]`), so plugins
73
77
  // that inspect or narrow content blocks — reading a `tool_use` block's input,
@@ -173,6 +177,18 @@ export {
173
177
  CredentialResolutionError,
174
178
  resolveCredential,
175
179
  } from "./resolve-credential.js";
180
+ // Store a credential's plaintext value (the same write `assistant credentials
181
+ // set` performs), creating it or replacing an existing one, named by UUID or a
182
+ // "service/field" reference. A plugin may only write credentials whose `field`
183
+ // matches its manifest name, and the write fails closed with no plugin in
184
+ // context. Throws CredentialStoreError when there is no calling plugin, the ref
185
+ // is malformed, the value is invalid, the store rejects the write, or the
186
+ // credential is out of the plugin's scope.
187
+ export type {
188
+ StoreCredentialOptions,
189
+ StoredCredentialRef,
190
+ } from "./store-credential.js";
191
+ export { CredentialStoreError, storeCredential } from "./store-credential.js";
176
192
  // Resolve the public URL a third party should deliver to for one of the
177
193
  // plugin's own ingress routes. Which URL is correct depends on how the
178
194
  // assistant is reachable (a managed platform callback route, or a configured
@@ -180,6 +196,16 @@ export {
180
196
  // the same resolution as `webhooks register`, and registers the callback route
181
197
  // on the managed branches. The plugin defaults to the one in context.
182
198
  export { resolveWebhookUrl, type WebhookUrlOptions } from "./webhook-url.js";
199
+ // Resolve the redirect URI an authorization server should send a user back
200
+ // to after consent. One shared route serves every OAuth flow in the
201
+ // assistant, demultiplexed by OAuth `state`, so this takes no arguments and
202
+ // returns the same URL for every caller and every attempt. A plugin needs it
203
+ // when it publishes something that has to name the redirect URI ahead of the
204
+ // flow, such as a Client ID Metadata Document, whose `redirect_uris` an
205
+ // authorization server matches exactly. Throws when no public ingress is
206
+ // configured and the assistant is not connected to the platform, which is the
207
+ // case where no URL would work.
208
+ export { resolveOauthCallbackUrl } from "../inbound/oauth-callback-url.js";
183
209
  // Resolve a provider for a call site (optionally overriding the profile) so a
184
210
  // plugin can run inference through the workspace's configured profiles and
185
211
  // credentials — managed-proxy or BYOK — without supplying its own API key.
@@ -345,21 +371,26 @@ export { synthesizeText, TtsSynthesisError } from "../tts/synthesize-text.js";
345
371
  export type { TtsSynthesisResult } from "../tts/types.js";
346
372
  // Streaming speech-to-text — open a live transcription session against the
347
373
  // assistant's globally configured STT provider stack. The plugin feeds audio
348
- // chunks via `sendAudio` and receives partial/final transcript events through
349
- // the `start(onEvent)` callback, closing with `stop`. `SttStreamServerEvent`
350
- // and its variants type the events handed to `onEvent`; `SttErrorCategory`
351
- // classifies `error` events; `SttProviderId` names the resolved session's
352
- // provider.
374
+ // chunks via `sendAudio` and receives transcript and turn-boundary events
375
+ // through the `start(onEvent)` callback, closing with `stop`.
376
+ // `SttStreamServerEvent` types the events handed to `onEvent`; every member of
377
+ // that union is exported alongside it so a plugin can name each variant in an
378
+ // exhaustive switch. `SttErrorCategory` classifies `error` events;
379
+ // `SttProviderId` names the resolved session's provider.
353
380
  export type {
354
381
  StreamingTranscriber,
355
382
  SttErrorCategory,
356
383
  SttProviderId,
357
384
  SttStreamServerClosedEvent,
385
+ SttStreamServerEagerTurnEndEvent,
358
386
  SttStreamServerErrorEvent,
359
387
  SttStreamServerEvent,
360
388
  SttStreamServerFinalEvent,
361
389
  SttStreamServerFinalizedEvent,
362
390
  SttStreamServerPartialEvent,
391
+ SttStreamServerTurnEndEvent,
392
+ SttStreamServerTurnResumedEvent,
393
+ SttStreamServerTurnStartEvent,
363
394
  } from "../stt/types.js";
364
395
  export { openTranscriptionSession } from "./transcription-session.js";
365
396
  // Conversation agent-loop turn — run a full conversation turn (persist user
@@ -370,6 +401,7 @@ export { openTranscriptionSession } from "./transcription-session.js";
370
401
  // (e.g. meeting-bot flushing a transcript excerpt) should prefer this over the
371
402
  // stateless `provider.sendMessage()` call.
372
403
  export type {
404
+ ConversationChannelAddress,
373
405
  RunConversationTurnOptions,
374
406
  RunConversationTurnResult,
375
407
  } from "./conversation-turn.js";
@@ -9,8 +9,9 @@
9
9
  *
10
10
  * ## Plugin scoping
11
11
  *
12
- * When a plugin is in context — its hook or tool is executing, tracked by
13
- * {@link ../plugins/plugin-execution-context.getCurrentPluginName} — resolution
12
+ * When a plugin is in context (its hook, tool, or one of its own
13
+ * `/x/plugins/<name>/` routes is executing, tracked by
14
+ * {@link ../plugins/plugin-execution-context.getCurrentPluginName}), resolution
14
15
  * is restricted: the plugin may only resolve credentials whose `field` equals
15
16
  * the plugin's own manifest name. A plugin named `acme` can therefore read
16
17
  * `openai/acme` or `stripe/acme` but not `openai/api_key`. Outside any plugin
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Plugin-facing credential storage.
3
+ *
4
+ * {@link storeCredential} writes a credential's plaintext to the secure store,
5
+ * the same write `assistant credentials set` performs, naming it either by an
6
+ * existing credential's UUID or by a `"service/field"` string, the same
7
+ * vocabulary {@link ./resolve-credential.resolveCredential} reads back. A
8
+ * plugin that obtains a token for itself (an OAuth exchange it drives, a key a
9
+ * user hands it through its own route) persists it here instead of inventing
10
+ * its own on-disk secret file.
11
+ *
12
+ * ## Plugin scoping
13
+ *
14
+ * A plugin may only write credentials whose `field` equals its manifest name,
15
+ * so `acme` writes `openai/acme` or `stripe/acme` but never `openai/api_key`.
16
+ * The name comes from the execution context
17
+ * ({@link ../plugins/plugin-execution-context.getCurrentPluginName}), which the
18
+ * host establishes around a plugin's hook, tool, and route invocations.
19
+ *
20
+ * Unlike the read path, this **fails closed**: with no plugin in context there
21
+ * is nobody to scope the write to, so it is refused rather than treated as an
22
+ * unscoped host write. A plugin's module body is evaluated by the loader
23
+ * outside any context, and an unscoped branch there would let top-level code
24
+ * overwrite the user's own credentials. Host-internal callers that legitimately
25
+ * write unscoped (the `credentials/set` route, the CLI behind it) compose
26
+ * {@link ../tools/credentials/store.storeCredentialValue} directly. This
27
+ * matches the plugin-owned index (`indexDocument`), which likewise requires a
28
+ * context because a write with no owner has nothing to attribute.
29
+ */
30
+
31
+ import { getCurrentPluginName } from "../plugins/plugin-execution-context.js";
32
+ import { parseServiceFieldRef } from "../tools/credentials/ref-parse.js";
33
+ import { resolveCredentialRef } from "../tools/credentials/resolve.js";
34
+ import { storeCredentialValue } from "../tools/credentials/store.js";
35
+
36
+ /**
37
+ * Raised when a credential cannot be stored: the reference is malformed, the
38
+ * value is empty or invalid for its service, the secure backend rejected the
39
+ * write, or the calling plugin is not permitted to write the credential.
40
+ */
41
+ export class CredentialStoreError extends Error {
42
+ constructor(message: string) {
43
+ super(message);
44
+ this.name = "CredentialStoreError";
45
+ }
46
+ }
47
+
48
+ export interface StoreCredentialOptions {
49
+ /** Human-friendly alias shown in credential listings. */
50
+ label?: string;
51
+ /** What the credential is for, surfaced to the agent. */
52
+ description?: string;
53
+ /**
54
+ * Skip the retroactive sweep that removes the value from recent transcripts.
55
+ * The sweep exists because a secret is often pasted into chat before it is
56
+ * stored; pass `true` only for a value that provably never transited a
57
+ * conversation, such as a token minted by an OAuth refresh.
58
+ */
59
+ skipTranscriptScrub?: boolean;
60
+ }
61
+
62
+ /** Identity of the credential that was written. */
63
+ export interface StoredCredentialRef {
64
+ credentialId: string;
65
+ service: string;
66
+ field: string;
67
+ }
68
+
69
+ /**
70
+ * Store a credential's plaintext value, creating it or replacing the value of
71
+ * an existing one.
72
+ *
73
+ * @param ref A credential UUID (an existing credential) or a `"service/field"`
74
+ * string (created when no such credential exists).
75
+ * @param value The plaintext value. Edge whitespace is trimmed.
76
+ * @returns The identity of the stored credential, which
77
+ * {@link ./resolve-credential.resolveCredential} accepts as a ref.
78
+ * @throws {CredentialStoreError} when no plugin is in context, the ref is
79
+ * malformed or names no credential, the value is empty or invalid for its
80
+ * service, the store rejects the write, or the calling plugin is not scoped
81
+ * to the credential.
82
+ */
83
+ export async function storeCredential(
84
+ ref: string,
85
+ value: string,
86
+ options: StoreCredentialOptions = {},
87
+ ): Promise<StoredCredentialRef> {
88
+ // Both gates run before anything is written, so a refused write never
89
+ // touches the secure backend. Fail closed when there is no plugin to scope
90
+ // to: a plugin's module body is evaluated outside any context, and an
91
+ // unscoped branch there would be a way to overwrite the user's credentials.
92
+ const pluginName = getCurrentPluginName();
93
+ if (pluginName === undefined) {
94
+ throw new CredentialStoreError(
95
+ "storeCredential requires an active plugin execution context (no calling plugin found). " +
96
+ "Call it from a hook, tool, or route handler rather than at module scope.",
97
+ );
98
+ }
99
+
100
+ const target = resolveStoreTarget(ref);
101
+ if (target.field !== pluginName) {
102
+ throw new CredentialStoreError(
103
+ `Plugin "${pluginName}" may only store credentials whose field matches its name; ` +
104
+ `"${target.service}/${target.field}" is out of scope.`,
105
+ );
106
+ }
107
+
108
+ try {
109
+ return await storeCredentialValue({
110
+ service: target.service,
111
+ field: target.field,
112
+ value,
113
+ alias: options.label,
114
+ usageDescription: options.description,
115
+ skipTranscriptScrub: options.skipTranscriptScrub,
116
+ });
117
+ } catch (err) {
118
+ throw new CredentialStoreError(
119
+ err instanceof Error ? err.message : String(err),
120
+ );
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Resolve the `ref` a write targets. An existing credential (by UUID or by
126
+ * `"service/field"`) keeps its identity; otherwise the ref must name the
127
+ * service and field of the credential to create, which a bare UUID cannot.
128
+ */
129
+ function resolveStoreTarget(ref: string): { service: string; field: string } {
130
+ if (!ref || ref.trim().length === 0) {
131
+ throw new CredentialStoreError("Credential reference is required");
132
+ }
133
+
134
+ const existing = resolveCredentialRef(ref);
135
+ if (existing) {
136
+ return { service: existing.service, field: existing.field };
137
+ }
138
+
139
+ const parsed = parseServiceFieldRef(ref);
140
+ if (!parsed) {
141
+ throw new CredentialStoreError(
142
+ `Cannot store credential: "${ref}" matches no stored credential and is not a "service/field" reference`,
143
+ );
144
+ }
145
+ return parsed;
146
+ }
@@ -107,7 +107,7 @@ describe("doesSupportVision", () => {
107
107
  managed: { provider: "vellum", model: "claude-fable-5" },
108
108
  "managed-text": {
109
109
  provider: "vellum",
110
- model: "accounts/fireworks/models/deepseek-v4-flash",
110
+ model: "accounts/fireworks/models/deepseek-v4-flash-0731",
111
111
  },
112
112
  });
113
113
  expect(doesSupportVision(profile("managed"))).toBe(true);
@@ -90,22 +90,24 @@ function registrationType(plugin: string, route: string): string {
90
90
  }
91
91
 
92
92
  /**
93
- * Drop a trailing slash from a resolved callback URL.
93
+ * Keep a trailing slash on a resolved callback URL.
94
94
  *
95
- * The platform appends one to the callback URLs it hands back, and a provider
96
- * given `.../events-comms/` delivers to that spelling verbatim. The gateway
97
- * matches a plugin route against its declared path, which may never end in a
98
- * slash (see `IngressRouteSchema`), so the slash comes off once here rather
99
- * than at each plugin that registers a URL.
95
+ * Django in front of managed callbacks canonicalizes onto `/`. A vendor given
96
+ * the slashless spelling is 301'd, and clients that follow a 301 on POST
97
+ * typically retry as GET and drop the body. The gateway serves both spellings
98
+ * of a plugin webhook, so the slashed URL is the one to hand out.
100
99
  *
101
- * Only the path is ever trimmed: this resolver passes no query parameters, and
102
- * a URL carrying one is left alone rather than truncated.
100
+ * Query-bearing URLs are left alone: this resolver passes no query
101
+ * parameters, and appending there would cut into the query rather than the
102
+ * path. The callback path registered with the platform stays slashless. The
103
+ * platform strips slashes on store, and Django's path converter typically
104
+ * does not include the final `/` in the lookup key.
103
105
  */
104
- function withoutTrailingSlash(url: string): string {
106
+ function withTrailingSlash(url: string): string {
105
107
  if (url.includes("?") || url.includes("#")) {
106
108
  return url;
107
109
  }
108
- return url.replace(/\/+$/, "");
110
+ return url.endsWith("/") ? url : `${url}/`;
109
111
  }
110
112
 
111
113
  /**
@@ -147,5 +149,5 @@ export async function resolveWebhookUrl(
147
149
  undefined,
148
150
  sourceIdentifier,
149
151
  );
150
- return withoutTrailingSlash(resolved);
152
+ return withTrailingSlash(resolved);
151
153
  }