@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
@@ -223,6 +223,35 @@ const STT_ERROR_MAP: Record<SttErrorCategory, () => RouteError> = {
223
223
  "provider-error": () => new BadGatewayError("STT provider error"),
224
224
  };
225
225
 
226
+ /**
227
+ * Resolve the configured batch transcriber, or fail the request with a 503
228
+ * naming which kind of unavailability this is.
229
+ *
230
+ * A typed {@link SttError} already names the situation and its fix (a
231
+ * configured provider that cannot batch transcribe), so its message is
232
+ * surfaced verbatim. `null` means nothing is configured at all, and any other
233
+ * throw is an unexpected resolver failure.
234
+ */
235
+ async function resolveBatchTranscriberOrUnavailable(): Promise<BatchTranscriber> {
236
+ let transcriber: BatchTranscriber | null;
237
+ try {
238
+ transcriber = await resolveBatchTranscriber();
239
+ } catch (err) {
240
+ if (err instanceof SttError) {
241
+ throw new ServiceUnavailableError(err.message);
242
+ }
243
+ log.error({ err }, "Failed to resolve STT transcriber");
244
+ throw new ServiceUnavailableError("STT provider is not available");
245
+ }
246
+
247
+ if (!transcriber) {
248
+ throw new ServiceUnavailableError(
249
+ "No speech-to-text provider is configured",
250
+ );
251
+ }
252
+ return transcriber;
253
+ }
254
+
226
255
  // ---------------------------------------------------------------------------
227
256
  // Handlers
228
257
  // ---------------------------------------------------------------------------
@@ -288,19 +317,7 @@ async function handleTranscribe({ body }: RouteHandlerArgs) {
288
317
  }
289
318
 
290
319
  // -- Resolve transcriber --------------------------------------------------
291
- let transcriber;
292
- try {
293
- transcriber = await resolveBatchTranscriber();
294
- } catch (err) {
295
- log.error({ err }, "Failed to resolve STT transcriber");
296
- throw new ServiceUnavailableError("STT provider is not available");
297
- }
298
-
299
- if (!transcriber) {
300
- throw new ServiceUnavailableError(
301
- "No speech-to-text provider is configured",
302
- );
303
- }
320
+ const transcriber = await resolveBatchTranscriberOrUnavailable();
304
321
 
305
322
  // -- Transcribe with timeout ----------------------------------------------
306
323
  const abortController = new AbortController();
@@ -358,18 +375,7 @@ async function handleTranscribeFile({ body }: RouteHandlerArgs) {
358
375
  );
359
376
  }
360
377
 
361
- let transcriber;
362
- try {
363
- transcriber = await resolveBatchTranscriber();
364
- } catch (err) {
365
- log.error({ err }, "Failed to resolve STT transcriber");
366
- throw new ServiceUnavailableError("STT provider is not available");
367
- }
368
- if (!transcriber) {
369
- throw new ServiceUnavailableError(
370
- "No speech-to-text provider is configured",
371
- );
372
- }
378
+ const transcriber = await resolveBatchTranscriberOrUnavailable();
373
379
 
374
380
  const startTime = Date.now();
375
381
  let wavPath: string | null = null;
@@ -37,26 +37,25 @@ const log = getLogger("surface-action-routes");
37
37
 
38
38
  /**
39
39
  * Resolve trust context for the actor principal from the gateway guardian
40
- * binding and set it on the conversation. A vellum principal is the guardian or
41
- * nobody, so the mapper yields guardian or unknown. Resolution (including the
42
- * dev-bypass principal translation and the mutating reset-drift repair) lives
43
- * in the shared {@link resolveVellumActorTrustContext} so the surface content
44
- * route's read-only requester scoping cannot drift from it.
40
+ * binding, and return it for the caller to attribute this action with. A vellum
41
+ * principal is the guardian or nobody, so the mapper yields guardian or
42
+ * unknown. Resolution (including the dev-bypass principal translation and the
43
+ * mutating reset-drift repair) lives in the shared
44
+ * {@link resolveVellumActorTrustContext} so the surface content route's
45
+ * read-only requester scoping cannot drift from it.
46
+ *
47
+ * Resolution only: the requester travels to `handleSurfaceAction` as an
48
+ * argument, and the conversation's slot is stamped there, at the point the
49
+ * click is committed to starting a turn. Stamping here instead would repoint
50
+ * the actor of a turn already in flight, or of a turn that starts between this
51
+ * call and the dispatch.
45
52
  */
46
- async function applyTrustContext(
47
- conversation: {
48
- setTrustContext?(ctx: TrustContext): void;
49
- },
53
+ async function resolveRequesterTrustContext(
50
54
  actorPrincipalId: string | undefined,
51
- ): Promise<void> {
52
- if (!conversation.setTrustContext) {
53
- return;
54
- }
55
- conversation.setTrustContext(
56
- await resolveVellumActorTrustContext(actorPrincipalId, {
57
- healResetDrift: true,
58
- }),
59
- );
55
+ ): Promise<TrustContext> {
56
+ return resolveVellumActorTrustContext(actorPrincipalId, {
57
+ healResetDrift: true,
58
+ });
60
59
  }
61
60
 
62
61
  // ---------------------------------------------------------------------------
@@ -154,7 +153,8 @@ async function handleSurfaceAction({
154
153
  }
155
154
 
156
155
  const actorPrincipalId = headers?.["x-vellum-actor-principal-id"];
157
- await applyTrustContext(conversation, actorPrincipalId);
156
+ const requesterTrustContext =
157
+ await resolveRequesterTrustContext(actorPrincipalId);
158
158
 
159
159
  // Translate dev-bypass → real guardian so the surface turn's principal matches
160
160
  // the SSE host-proxy client's registered principal; otherwise CU/app-control
@@ -170,6 +170,7 @@ async function handleSurfaceAction({
170
170
  actionId,
171
171
  data,
172
172
  resolvedActorPrincipalId,
173
+ requesterTrustContext,
173
174
  );
174
175
  const result =
175
176
  raw && typeof raw === "object" && "accepted" in raw
@@ -64,6 +64,9 @@ export async function resolveSurfaceConversation(
64
64
  const escaped = surfaceId.replace(/[\\%_]/g, "\\$&");
65
65
  const row = rawGet<{ conversation_id: string }>(
66
66
  "surfaceResolver:resolveConversation",
67
+ // Unfinalized rows cannot match: their content is a { ref } pointer,
68
+ // never inline surface JSON, so the LIKE probes below see finalized
69
+ // content only by shape. No completeness predicate needed.
67
70
  `SELECT conversation_id FROM messages
68
71
  WHERE content LIKE ? ESCAPE '\\'
69
72
  ORDER BY created_at DESC
@@ -27,13 +27,17 @@
27
27
  * shows no route depends on it.
28
28
  *
29
29
  * Modules are lazily loaded on first request and cached by file path +
30
- * mtime. When a file changes on disk, the next request reloads it via
31
- * Bun's dynamic `import()` with a cache-busting query parameter. A request
32
- * whose file does not exist 404s — nothing is registered ahead of time.
30
+ * mtime. When a file changes on disk, the next request evicts the whole
31
+ * source tree that file belongs to (plugin root, or the workspace
32
+ * `routes/` directory) and re-imports the entry. Cache-busting only the
33
+ * entry file would re-bind it to stale helpers still in Bun's registry.
34
+ * A request whose file does not exist 404s. Nothing is registered ahead
35
+ * of time.
33
36
  */
34
37
 
35
38
  import { statSync } from "node:fs";
36
39
 
40
+ import { runInPluginContext } from "../../plugins/plugin-execution-context.js";
37
41
  import { isRouteHostEnabled } from "../../routes/control.js";
38
42
  import {
39
43
  RouteHostClient,
@@ -47,6 +51,12 @@ import {
47
51
  type UserRouteContext,
48
52
  } from "./deprecated-route-context.js";
49
53
  import {
54
+ evictRouteSourceTree,
55
+ importRouteModule,
56
+ routeSourceRoot,
57
+ } from "./user-route-import.js";
58
+ import {
59
+ pluginNameForRoutePath,
50
60
  resolveHandlerFile,
51
61
  resolveRouteLocation,
52
62
  } from "./user-route-resolution.js";
@@ -134,7 +144,7 @@ export class UserRouteDispatcher {
134
144
  ? resolveHandlerFile(location.routesDir, location.subPath)
135
145
  : null;
136
146
 
137
- if (!filePath) {
147
+ if (!location || !filePath) {
138
148
  return httpError(
139
149
  "NOT_FOUND",
140
150
  `No route handler found for /x/${routePath}`,
@@ -142,11 +152,36 @@ export class UserRouteDispatcher {
142
152
  );
143
153
  }
144
154
 
155
+ // A plugin's own routes execute as that plugin, so the host APIs a handler
156
+ // reaches scope to it exactly as they do inside its hooks and tools. Both
157
+ // execution paths carry it: in-thread here, and the route host through the
158
+ // invoke params it marks its own execution with.
159
+ const pluginName = pluginNameForRoutePath(routePath);
160
+
145
161
  if (isRouteHostEnabled()) {
146
- return this.dispatchViaHost(filePath, routePath, request);
162
+ return this.dispatchViaHost(filePath, routePath, request, pluginName);
147
163
  }
148
164
 
149
- const mod = await this.loadModule(filePath);
165
+ // The context covers module evaluation, not just the handler call. A route
166
+ // module can reach a scoped host API at import time (a top-level `await`),
167
+ // and that import happens once per mtime, so a load left outside the
168
+ // context would take the unscoped branch and never be re-run scoped.
169
+ const serve = () =>
170
+ this.serveInThread(filePath, location.routesDir, routePath, request);
171
+ return pluginName ? runInPluginContext(pluginName, serve) : serve();
172
+ }
173
+
174
+ /**
175
+ * Load the handler module and run the method's handler in this thread.
176
+ * Called inside the owning plugin's execution context, when the route has one.
177
+ */
178
+ private async serveInThread(
179
+ filePath: string,
180
+ routesDir: string,
181
+ routePath: string,
182
+ request: Request,
183
+ ): Promise<Response> {
184
+ const mod = await this.loadModule(filePath, routesDir);
150
185
  const method = request.method as HttpMethod;
151
186
  const handler = mod.handlers[method];
152
187
 
@@ -172,6 +207,7 @@ export class UserRouteDispatcher {
172
207
  filePath: string,
173
208
  routePath: string,
174
209
  request: Request,
210
+ pluginName: string | undefined,
175
211
  ): Promise<Response> {
176
212
  const mtimeMs = statSync(filePath).mtimeMs;
177
213
 
@@ -193,6 +229,7 @@ export class UserRouteDispatcher {
193
229
  method: request.method,
194
230
  url: request.url,
195
231
  headers,
232
+ pluginName,
196
233
  },
197
234
  body,
198
235
  );
@@ -231,11 +268,15 @@ export class UserRouteDispatcher {
231
268
  /**
232
269
  * Load a handler module, using the mtime-based cache when possible.
233
270
  *
234
- * On cache miss or stale mtime, the module is re-imported via Bun's
235
- * dynamic `import()` with a cache-busting query parameter derived
236
- * from the file's current mtime.
271
+ * On cache miss or stale mtime, every source file in the handler's tree
272
+ * is evicted first so a new entry cannot import a helper that is still
273
+ * the pre-upgrade module. Then the entry is re-imported with an mtime
274
+ * query parameter.
237
275
  */
238
- private async loadModule(filePath: string): Promise<CachedModule> {
276
+ private async loadModule(
277
+ filePath: string,
278
+ routesDir: string,
279
+ ): Promise<CachedModule> {
239
280
  const stat = statSync(filePath);
240
281
  const mtimeMs = stat.mtimeMs;
241
282
 
@@ -244,11 +285,11 @@ export class UserRouteDispatcher {
244
285
  return cached;
245
286
  }
246
287
 
247
- // Cache-bust Bun's module cache by appending mtime as a query param.
248
- const mod = (await import(`${filePath}?t=${mtimeMs}`)) as Record<
249
- string,
250
- unknown
251
- >;
288
+ const sourceRoot = routeSourceRoot(routesDir);
289
+ evictRouteSourceTree(sourceRoot);
290
+ this.dropCachedModulesUnder(sourceRoot);
291
+
292
+ const mod = await importRouteModule(filePath);
252
293
 
253
294
  const handlers: Partial<Record<HttpMethod, RouteHandler>> = {};
254
295
  for (const method of HTTP_METHODS) {
@@ -271,6 +312,19 @@ export class UserRouteDispatcher {
271
312
  return entry;
272
313
  }
273
314
 
315
+ /**
316
+ * Drop every cached handler under `sourceRoot`. Sibling routes must not
317
+ * keep closures over the helpers we just evicted.
318
+ */
319
+ private dropCachedModulesUnder(sourceRoot: string): void {
320
+ const prefix = sourceRoot.endsWith("/") ? sourceRoot : `${sourceRoot}/`;
321
+ for (const path of this.moduleCache.keys()) {
322
+ if (path === sourceRoot || path.startsWith(prefix)) {
323
+ this.moduleCache.delete(path);
324
+ }
325
+ }
326
+ }
327
+
274
328
  /**
275
329
  * Execute a handler function with a per-request timeout and error boundary.
276
330
  */
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Import a user/plugin route module so its helpers match the files on disk.
3
+ *
4
+ * Bun caches every imported helper independently. Cache-busting only the
5
+ * entry file (`import(file?t=mtime)`) re-binds it to whatever helpers are
6
+ * already in the registry. That is how a new route that imports a new export
7
+ * from `src/http.ts` 500s with "Export named 'requireNumber' not found" while
8
+ * `/frame` (still on the old http.ts surface) keeps answering 200.
9
+ *
10
+ * Hooks and tools already treat the whole plugin directory as the reload
11
+ * unit (`plugins/source-fingerprint.ts`). Routes must do the same: when the
12
+ * entry file's mtime moves, evict every source file in that tree before the
13
+ * next import, so the new entry cannot pair with a stale helper.
14
+ */
15
+
16
+ import { statSync } from "node:fs";
17
+ import { dirname, sep } from "node:path";
18
+ import { fileURLToPath, pathToFileURL } from "node:url";
19
+
20
+ import { snapshotPluginSource } from "../../plugins/source-fingerprint.js";
21
+ import { evictModule } from "../../plugins/surface-import.js";
22
+ import { getWorkspaceRoutesDir } from "../../util/platform.js";
23
+
24
+ /**
25
+ * Directory whose source files must stay mutually consistent with this
26
+ * handler. An installed or default plugin uses the plugin root (so `src/`
27
+ * helpers evict with `routes/`). Workspace routes use the workspace
28
+ * `routes/` directory.
29
+ */
30
+ export function routeSourceRoot(routesDir: string): string {
31
+ if (routesDir === getWorkspaceRoutesDir()) {
32
+ return routesDir;
33
+ }
34
+ return dirname(routesDir);
35
+ }
36
+
37
+ /**
38
+ * Infer the source root from a handler path when the caller has no
39
+ * {@link routeSourceRoot} (the route-host worker only sees `filePath`).
40
+ */
41
+ export function sourceRootForHandler(filePath: string): string {
42
+ const marker = `${sep}routes${sep}`;
43
+ const idx = filePath.lastIndexOf(marker);
44
+ if (idx !== -1) {
45
+ return filePath.slice(0, idx);
46
+ }
47
+ return dirname(filePath);
48
+ }
49
+
50
+ /**
51
+ * Drop every source module under `sourceRoot` from the runtime registry,
52
+ * including query-string aliases (`file.ts?t=mtime`) the dispatcher uses to
53
+ * cache-bust the entry file.
54
+ */
55
+ export function evictRouteSourceTree(sourceRoot: string): void {
56
+ const { evictionPaths } = snapshotPluginSource(sourceRoot);
57
+ for (const path of evictionPaths) {
58
+ evictModule(path);
59
+ }
60
+ evictRegistryAliases(sourceRoot);
61
+ }
62
+
63
+ function evictRegistryAliases(sourceRoot: string): void {
64
+ const prefix = sourceRoot.endsWith("/") ? sourceRoot : `${sourceRoot}/`;
65
+ const fileUrlPrefix = pathToFileURL(prefix).href;
66
+ for (const key of Object.keys(require.cache)) {
67
+ if (!keyBelongsToTree(key, sourceRoot, prefix, fileUrlPrefix)) {
68
+ continue;
69
+ }
70
+ delete require.cache[key];
71
+ }
72
+ }
73
+
74
+ function keyBelongsToTree(
75
+ key: string,
76
+ sourceRoot: string,
77
+ prefix: string,
78
+ fileUrlPrefix: string,
79
+ ): boolean {
80
+ const bare = key.split("?")[0] ?? key;
81
+ if (bare.includes("/node_modules/") || bare.includes("/node_modules?")) {
82
+ return false;
83
+ }
84
+ if (
85
+ bare === sourceRoot ||
86
+ bare.startsWith(prefix) ||
87
+ bare.startsWith(fileUrlPrefix)
88
+ ) {
89
+ return true;
90
+ }
91
+ if (bare.startsWith("file:")) {
92
+ try {
93
+ const asPath = fileURLToPath(bare);
94
+ return asPath === sourceRoot || asPath.startsWith(prefix);
95
+ } catch {
96
+ return false;
97
+ }
98
+ }
99
+ return false;
100
+ }
101
+
102
+ /** Dynamic-import a handler, cache-busted by the file's current mtime. */
103
+ export async function importRouteModule(
104
+ filePath: string,
105
+ ): Promise<Record<string, unknown>> {
106
+ const mtimeMs = statSync(filePath).mtimeMs;
107
+ return (await import(`${filePath}?t=${mtimeMs}`)) as Record<string, unknown>;
108
+ }
@@ -66,6 +66,23 @@ export interface RouteLocation {
66
66
  subPath: string;
67
67
  }
68
68
 
69
+ /**
70
+ * The manifest name of the plugin whose namespace an `/x/` route path falls in,
71
+ * or undefined for a workspace route.
72
+ *
73
+ * The dispatcher marks that plugin as in context for the handler's execution,
74
+ * so host APIs a route handler reaches (`resolveCredential`,
75
+ * `indexDocument`) scope to the owning plugin exactly as they do inside its
76
+ * hooks and tools.
77
+ */
78
+ export function pluginNameForRoutePath(routePath: string): string | undefined {
79
+ const segments = routePath.split("/");
80
+ if (segments[0] !== PLUGIN_ROUTE_SEGMENT) {
81
+ return undefined;
82
+ }
83
+ return segments[1] || undefined;
84
+ }
85
+
69
86
  /**
70
87
  * Resolve an `/x/` route path to the base directory + sub-path the handler file
71
88
  * is looked up under.
@@ -87,7 +104,7 @@ export interface RouteLocation {
87
104
  export function resolveRouteLocation(routePath: string): RouteLocation | null {
88
105
  const segments = routePath.split("/");
89
106
  if (segments[0] === PLUGIN_ROUTE_SEGMENT) {
90
- const pluginName = segments[1];
107
+ const pluginName = pluginNameForRoutePath(routePath);
91
108
  if (!pluginName) {
92
109
  return null;
93
110
  }
@@ -19,7 +19,6 @@ import { basename, dirname, join } from "node:path";
19
19
 
20
20
  import { z } from "zod";
21
21
 
22
- import { rebindDocumentsToRenamedPath } from "../../documents/document-store.js";
23
22
  import { getWorkspaceDir } from "../../util/platform.js";
24
23
  import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
25
24
  import { publishSoundsConfigUpdated } from "../sync/resource-sync-events.js";
@@ -35,7 +34,6 @@ import {
35
34
  isTextMimeType,
36
35
  MAX_INLINE_TEXT_SIZE,
37
36
  resolveWorkspacePath,
38
- toWorkspaceRelativePath,
39
37
  writeWorkspaceFile,
40
38
  } from "./workspace-utils.js";
41
39
 
@@ -505,13 +503,6 @@ function handleWorkspaceRename({ body, headers }: RouteHandlerArgs) {
505
503
 
506
504
  mkdirSync(dirname(resolvedNew), { recursive: true });
507
505
  renameSync(resolvedOld, resolvedNew);
508
- // File-backed documents key off the canonical relative path, so they follow
509
- // the file to its new name. The request strings are not canonical, hence the
510
- // resolved paths.
511
- rebindDocumentsToRenamedPath({
512
- oldPath: toWorkspaceRelativePath(resolvedOld),
513
- newPath: toWorkspaceRelativePath(resolvedNew),
514
- });
515
506
  publishSoundsConfigUpdatedForPaths(
516
507
  [oldPath, newPath],
517
508
  headers?.["x-vellum-client-id"]?.trim() || undefined,
@@ -6,7 +6,7 @@ import {
6
6
  statSync,
7
7
  writeFileSync,
8
8
  } from "node:fs";
9
- import { dirname, relative, resolve, sep } from "node:path";
9
+ import { dirname, resolve, sep } from "node:path";
10
10
 
11
11
  import { getWorkspaceDir } from "../../util/platform.js";
12
12
  import { BadRequestError, ConflictError } from "./errors.js";
@@ -85,22 +85,12 @@ export function resolveWorkspacePath(
85
85
  return resolved;
86
86
  }
87
87
 
88
- /**
89
- * Normalise a workspace path to the canonical relative form used as a stable
90
- * key for the file — forward slashes, no `.`/`..` segments, no leading slash.
91
- * `absolutePath` must already have passed {@link resolveWorkspacePath}.
92
- */
93
- export function toWorkspaceRelativePath(absolutePath: string): string {
94
- return relative(getWorkspaceDir(), absolutePath).split(sep).join("/");
95
- }
96
-
97
88
  /**
98
89
  * Write bytes to a workspace-relative path, enforcing the workspace boundary.
99
90
  *
100
91
  * This is the one write primitive behind every daemon-side workspace file
101
- * write: the workspace write route and the document store's write-through for
102
- * file-backed documents both go through it, so the traversal and
103
- * directory-conflict rules cannot drift apart.
92
+ * write, so the traversal and directory-conflict rules cannot drift apart
93
+ * between callers.
104
94
  *
105
95
  * Returns the number of bytes written. Throws {@link BadRequestError} when the
106
96
  * path escapes the workspace and {@link ConflictError} when it names a
@@ -244,13 +244,18 @@ export function serializeConversationSummary(params: {
244
244
 
245
245
  /**
246
246
  * Build a full conversation detail response from a conversation ID.
247
- * Returns null if the conversation doesn't exist.
247
+ * Returns null if the conversation doesn't exist, or if it is a legacy
248
+ * `private` row: every listing hides those by type (see
249
+ * `standardListingVisibilitySql`) and migration cleanup deletes them, and the
250
+ * wire type collapses the value to `standard`, so serving one by id would let
251
+ * a client that only holds a stale id (a persisted last-viewed conversation)
252
+ * resurrect a row it can never see listed.
248
253
  */
249
254
  export function buildConversationDetailResponse(
250
255
  conversationId: string,
251
256
  ): { conversation: ReturnType<typeof serializeConversationSummary> } | null {
252
257
  const conversation = getConversation(conversationId);
253
- if (!conversation) {
258
+ if (!conversation || conversation.conversationType === "private") {
254
259
  return null;
255
260
  }
256
261
 
@@ -487,11 +487,39 @@ describe("parsePluginScheduleDeclarations", () => {
487
487
  expect(after).not.toBe(before);
488
488
  });
489
489
 
490
- test("changes when a helper file inside a directory declaration is edited or renamed", () => {
490
+ test("changes when config.json is edited", () => {
491
+ const pluginDir = makePlugin(FULL_DIGEST);
492
+ const before = parsePluginScheduleDeclarations(pluginDir, "p")
493
+ .declarations[0]!.definitionHash;
494
+ writeFileSync(
495
+ join(pluginDir, "schedules", "digest", "config.json"),
496
+ JSON.stringify({ expression: "0 10 * * *" }),
497
+ );
498
+ const after = parsePluginScheduleDeclarations(pluginDir, "p")
499
+ .declarations[0]!.definitionHash;
500
+ expect(after).not.toBe(before);
501
+ });
502
+
503
+ test("changes when the index.sh entrypoint is edited", () => {
504
+ const pluginDir = makePlugin({
505
+ "backup/config.json": JSON.stringify({ expression: "0 3 * * *" }),
506
+ "backup/index.sh": "echo v1\n",
507
+ });
508
+ const parse = () =>
509
+ parsePluginScheduleDeclarations(pluginDir, "p").declarations[0]!
510
+ .definitionHash;
511
+ const before = parse();
512
+ writeFileSync(
513
+ join(pluginDir, "schedules", "backup", "index.sh"),
514
+ "echo v2\n",
515
+ );
516
+ expect(parse()).not.toBe(before);
517
+ });
518
+
519
+ test("ignores every other file under the declaration directory", () => {
491
520
  const pluginDir = makePlugin({
492
521
  "backup/config.json": JSON.stringify({ expression: "0 3 * * *" }),
493
522
  "backup/index.sh": "sh lib/helper.sh\n",
494
- "backup/lib/helper.sh": "echo v1\n",
495
523
  });
496
524
  const parse = () =>
497
525
  parsePluginScheduleDeclarations(pluginDir, "p").declarations[0]!
@@ -505,15 +533,50 @@ describe("parsePluginScheduleDeclarations", () => {
505
533
  "lib",
506
534
  "helper.sh",
507
535
  );
536
+ mkdirSync(dirname(helperPath), { recursive: true });
537
+ writeFileSync(helperPath, "echo v1\n");
538
+ const statePath = join(
539
+ pluginDir,
540
+ "schedules",
541
+ "backup",
542
+ "data",
543
+ "state.json",
544
+ );
545
+ mkdirSync(dirname(statePath), { recursive: true });
546
+ writeFileSync(statePath, JSON.stringify({ lastRun: 1 }));
547
+ expect(parse()).toBe(original);
548
+
508
549
  writeFileSync(helperPath, "echo v2\n");
509
- const afterEdit = parse();
510
- expect(afterEdit).not.toBe(original);
550
+ writeFileSync(statePath, JSON.stringify({ lastRun: 2 }));
551
+ expect(parse()).toBe(original);
511
552
 
512
553
  renameSync(
513
554
  helperPath,
514
555
  join(pluginDir, "schedules", "backup", "lib", "helper2.sh"),
515
556
  );
516
- expect(parse()).not.toBe(afterEdit);
557
+ expect(parse()).toBe(original);
558
+
559
+ rmSync(join(pluginDir, "schedules", "backup", "data"), {
560
+ recursive: true,
561
+ force: true,
562
+ });
563
+ expect(parse()).toBe(original);
564
+ });
565
+
566
+ test("hashes a two-file declaration to its known value", () => {
567
+ const pluginDir = makePlugin({
568
+ "backup/config.json": JSON.stringify({ expression: "0 3 * * *" }),
569
+ "backup/index.sh": "echo backup\n",
570
+ });
571
+ // Pinned so stored rows keep their hash across upgrades. It holds only
572
+ // while the hashed paths stay `<name>/config.json` and
573
+ // `<name>/<entrypoint>`, relative to schedules/.
574
+ expect(
575
+ parsePluginScheduleDeclarations(pluginDir, "p").declarations[0]!
576
+ .definitionHash,
577
+ ).toBe(
578
+ "0b5722fc45abdd2c46672f275e3bcf548016ee5c0722da5a0588ef45fa7bbcab",
579
+ );
517
580
  });
518
581
 
519
582
  test("differs between two schedules with different names but same content", () => {