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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (596) hide show
  1. package/AGENTS.md +9 -3
  2. package/ARCHITECTURE.md +32 -10
  3. package/docs/architecture/memory.md +43 -5
  4. package/docs/architecture/security.md +96 -152
  5. package/docs/architecture/turn-actor.md +70 -0
  6. package/docs/browser-use-architecture-phase2.md +128 -56
  7. package/docs/flux-turn-detection-spike.md +248 -0
  8. package/docs/guardian-request-flow.md +35 -0
  9. package/docs/stt-provider-onboarding.md +3 -1
  10. package/knip.json +3 -0
  11. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  12. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
  13. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
  14. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  15. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
  16. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
  17. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  18. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
  19. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
  20. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  21. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
  22. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
  23. package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +34 -0
  24. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +166 -1
  25. package/node_modules/@vellumai/gateway-client/src/index.ts +2 -0
  26. package/node_modules/@vellumai/service-contracts/package.json +1 -0
  27. package/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
  28. package/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
  29. package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  30. package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
  31. package/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
  32. package/openapi.yaml +289 -113
  33. package/package.json +1 -1
  34. package/scripts/voice-ttft-spike.ts +3 -3
  35. package/scripts/write-plugin-api-shim.ts +10 -0
  36. package/src/__tests__/app-compiler.test.ts +38 -3
  37. package/src/__tests__/app-control-flow.test.ts +1 -0
  38. package/src/__tests__/approval-routes-http.test.ts +2 -2
  39. package/src/__tests__/assistant-feature-flag-guard.test.ts +25 -3
  40. package/src/__tests__/attachments-store.test.ts +21 -12
  41. package/src/__tests__/byok-default-profile-ensure.test.ts +17 -0
  42. package/src/__tests__/call-setup-flow-name-capture.test.ts +0 -1
  43. package/src/__tests__/call-site-routing-provider.test.ts +1 -1
  44. package/src/__tests__/channel-availability-routes.test.ts +14 -1
  45. package/src/__tests__/channel-capabilities-dedupe.test.ts +214 -0
  46. package/src/__tests__/channel-delivery-store.test.ts +14 -14
  47. package/src/__tests__/channel-inbound-disk-pressure.test.ts +4 -4
  48. package/src/__tests__/channel-setup-panel-ack.test.ts +1 -1
  49. package/src/__tests__/checker.test.ts +20 -314
  50. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +6 -2
  51. package/src/__tests__/compaction-events.test.ts +8 -10
  52. package/src/__tests__/config-loader-backfill.test.ts +3 -3
  53. package/src/__tests__/config-schema.test.ts +25 -10
  54. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
  55. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +8 -11
  56. package/src/__tests__/conversation-agent-loop-overflow.test.ts +8 -11
  57. package/src/__tests__/conversation-agent-loop.test.ts +32 -21
  58. package/src/__tests__/conversation-attachments.test.ts +0 -1
  59. package/src/__tests__/conversation-attention-store.test.ts +63 -0
  60. package/src/__tests__/conversation-confirmation-signals.test.ts +112 -0
  61. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -4
  62. package/src/__tests__/conversation-fork-crud.test.ts +69 -0
  63. package/src/__tests__/conversation-fork-referential.test.ts +67 -0
  64. package/src/__tests__/conversation-fork-retrospective.test.ts +24 -0
  65. package/src/__tests__/conversation-load-history-repair.test.ts +209 -0
  66. package/src/__tests__/conversation-notifiers-provenance.test.ts +59 -0
  67. package/src/__tests__/conversation-queue.test.ts +154 -4
  68. package/src/__tests__/conversation-routes-disk-view.test.ts +1 -1
  69. package/src/__tests__/conversation-routes-enabled-plugins.test.ts +1 -1
  70. package/src/__tests__/conversation-routes-guardian-reply.test.ts +9 -9
  71. package/src/__tests__/conversation-routes-hidden-queue.test.ts +1 -1
  72. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -1
  73. package/src/__tests__/conversation-runtime-assembly.test.ts +187 -102
  74. package/src/__tests__/conversation-runtime-workspace.test.ts +14 -10
  75. package/src/__tests__/conversation-slash-queue.test.ts +3 -0
  76. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +1 -0
  77. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +1 -0
  78. package/src/__tests__/conversation-surfaces-app-control.test.ts +1 -0
  79. package/src/__tests__/conversation-surfaces-app-open.test.ts +1 -1
  80. package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -1
  81. package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +21 -14
  82. package/src/__tests__/conversation-surfaces-queued-emit.test.ts +1 -0
  83. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +1 -0
  84. package/src/__tests__/conversation-surfaces-standalone.test.ts +1 -0
  85. package/src/__tests__/conversation-surfaces-state-update.test.ts +1 -1
  86. package/src/__tests__/conversation-surfaces-table-action.test.ts +1 -1
  87. package/src/__tests__/conversation-surfaces-task-progress.test.ts +1 -1
  88. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +1 -1
  89. package/src/__tests__/conversation-tool-setup-attribution.test.ts +1 -1
  90. package/src/__tests__/credential-prompt-route.test.ts +7 -10
  91. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  92. package/src/__tests__/cu-unified-flow.test.ts +1 -0
  93. package/src/__tests__/custom-profile-ensure.test.ts +5 -1
  94. package/src/__tests__/default-plugin-names-guard.test.ts +12 -1
  95. package/src/__tests__/discord-access-request-privacy.test.ts +5 -1
  96. package/src/__tests__/discord-requester-notice-privacy.test.ts +3 -3
  97. package/src/__tests__/document-append-idempotency.test.ts +233 -0
  98. package/src/__tests__/document-sync-tags.test.ts +0 -75
  99. package/src/__tests__/dynamic-skill-background-guard.test.ts +0 -2
  100. package/src/__tests__/edit-propagation.test.ts +107 -11
  101. package/src/__tests__/file-ops-service.test.ts +163 -30
  102. package/src/__tests__/filesystem-tools.test.ts +23 -24
  103. package/src/__tests__/gateway-only-guard.test.ts +2 -5
  104. package/src/__tests__/helpers/gateway-classify-mock.ts +27 -6
  105. package/src/__tests__/helpers/mock-actor-context.ts +49 -0
  106. package/src/__tests__/helpers/mock-conversation.ts +13 -1
  107. package/src/__tests__/host-file-read-tool.test.ts +16 -19
  108. package/src/__tests__/http-user-message-parity.test.ts +1 -1
  109. package/src/__tests__/init-feature-flag-overrides.test.ts +49 -0
  110. package/src/__tests__/injector-chain.test.ts +63 -41
  111. package/src/__tests__/injector-disk-pressure.test.ts +11 -23
  112. package/src/__tests__/inline-skill-load-permissions.test.ts +47 -36
  113. package/src/__tests__/llm-context-resolution.test.ts +73 -1
  114. package/src/__tests__/llm-schema.test.ts +5 -2
  115. package/src/__tests__/managed-skill-lifecycle.test.ts +7 -0
  116. package/src/__tests__/mcp-list-plugin-servers.test.ts +250 -0
  117. package/src/__tests__/media-generate-image.test.ts +131 -21
  118. package/src/__tests__/memory-retrieval-hook.test.ts +100 -7
  119. package/src/__tests__/messages-read-boundary-guard.test.ts +134 -0
  120. package/src/__tests__/mtime-cache.test.ts +3 -1
  121. package/src/__tests__/non-member-access-request.test.ts +0 -20
  122. package/src/__tests__/outbound-slack-persistence.test.ts +40 -1
  123. package/src/__tests__/platform-bash-auto-approve.test.ts +0 -4
  124. package/src/__tests__/plugin-api-store-credential.test.ts +261 -0
  125. package/src/__tests__/plugin-api-webhook-url.test.ts +10 -7
  126. package/src/__tests__/plugin-disabled-state.test.ts +2 -0
  127. package/src/__tests__/plugin-effective-enabled-set.test.ts +55 -3
  128. package/src/__tests__/plugin-execution-context.test.ts +73 -0
  129. package/src/__tests__/plugin-import-boundary-guard.test.ts +5 -1
  130. package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +7 -4
  131. package/src/__tests__/plugin-secret-pattern-contribution.test.ts +1 -1
  132. package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +14 -7
  133. package/src/__tests__/provider-commit-message-generator.test.ts +20 -0
  134. package/src/__tests__/proxy-approval-callback.test.ts +1 -0
  135. package/src/__tests__/qdrant-manager.test.ts +14 -1
  136. package/src/__tests__/reaction-persistence.test.ts +200 -7
  137. package/src/__tests__/require-fresh-approval.test.ts +0 -4
  138. package/src/__tests__/risk-classification-boundary-guard.test.ts +115 -0
  139. package/src/__tests__/run-conversation-turn-persistence.test.ts +460 -105
  140. package/src/__tests__/run-due-schedules.test.ts +21 -0
  141. package/src/__tests__/scaffold-managed-skill-tool.test.ts +187 -18
  142. package/src/__tests__/schedule-routes.test.ts +23 -0
  143. package/src/__tests__/schedule-store.test.ts +17 -0
  144. package/src/__tests__/scoped-approval-grants.test.ts +11 -6
  145. package/src/__tests__/secret-ingress-channel.test.ts +0 -1
  146. package/src/__tests__/secret-ingress-http.test.ts +1 -1
  147. package/src/__tests__/send-endpoint-busy.test.ts +3 -3
  148. package/src/__tests__/skills.test.ts +32 -0
  149. package/src/__tests__/slack-edit-ordering-characterization.test.ts +0 -1
  150. package/src/__tests__/starter-task-flow.test.ts +5 -4
  151. package/src/__tests__/subagent-call-site-routing.test.ts +31 -19
  152. package/src/__tests__/subagent-fork-prompt-role.test.ts +1 -1
  153. package/src/__tests__/subagent-spawn-and-await.test.ts +18 -17
  154. package/src/__tests__/subagent-tool-gate-mode.test.ts +1 -1
  155. package/src/__tests__/subagent-tools.test.ts +81 -101
  156. package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +1 -0
  157. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
  158. package/src/__tests__/tool-executor-lifecycle-events.test.ts +132 -5
  159. package/src/__tests__/tool-executor.test.ts +68 -39
  160. package/src/__tests__/turn-events-store.test.ts +43 -0
  161. package/src/__tests__/ui-choice-copy-surfaces.test.ts +1 -1
  162. package/src/__tests__/ui-shape-teaching.test.ts +33 -0
  163. package/src/__tests__/ui-visual-surface.test.ts +1 -1
  164. package/src/__tests__/ui-voice-picker-surface.test.ts +128 -0
  165. package/src/__tests__/ui-work-result-surface.test.ts +1 -1
  166. package/src/__tests__/user-plugin-loader.test.ts +3 -1
  167. package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
  168. package/src/__tests__/visible-app-context.test.ts +16 -9
  169. package/src/__tests__/voice-config-update.test.ts +40 -0
  170. package/src/__tests__/voice-scoped-grant-consumer.test.ts +5 -3
  171. package/src/__tests__/voice-session-bridge.test.ts +85 -29
  172. package/src/__tests__/worker-entrypoint-guards.test.ts +54 -0
  173. package/src/__tests__/worker-plugin-surface.test.ts +77 -0
  174. package/src/__tests__/workspace-migration-142-consolidate-voice-front-door.test.ts +158 -0
  175. package/src/__tests__/workspace-migration-143-repair-deprecated-codex-model-id.test.ts +133 -0
  176. package/src/__tests__/workspace-migration-144-convert-stranded-subscription-openai-profiles.test.ts +316 -0
  177. package/src/__tests__/workspace-migration-145-collapse-profile-bindings-to-entries.test.ts +325 -0
  178. package/src/__tests__/workspace-migration-146-repair-retired-fireworks-deepseek-flash-model-id.test.ts +235 -0
  179. package/src/acp/__tests__/acp-claude-oauth.test.ts +10 -2
  180. package/src/acp/__tests__/auth-required.test.ts +161 -0
  181. package/src/acp/acp-claude-oauth.ts +19 -2
  182. package/src/acp/agent-process.test.ts +100 -0
  183. package/src/acp/agent-process.ts +29 -26
  184. package/src/acp/auth-required.ts +102 -0
  185. package/src/acp/session-manager.test.ts +119 -0
  186. package/src/acp/session-manager.ts +76 -3
  187. package/src/api/events/acp-auth-required.ts +55 -0
  188. package/src/api/events/host-file.ts +2 -2
  189. package/src/api/index.ts +7 -0
  190. package/src/api/surfaces.ts +12 -3
  191. package/src/apps/app-store.ts +3 -0
  192. package/src/bundler/package-resolver.ts +2 -30
  193. package/src/calls/__tests__/voice-session-bridge.test.ts +194 -11
  194. package/src/calls/__tests__/voice-triage-escalate.test.ts +102 -0
  195. package/src/calls/call-controller.ts +19 -3
  196. package/src/calls/call-setup-flow.ts +0 -1
  197. package/src/calls/media-stream-stt-session.ts +15 -0
  198. package/src/calls/voice-session-bridge.ts +115 -41
  199. package/src/calls/voice-triage-escalate.ts +105 -2
  200. package/src/channels/__tests__/plugin-channel-declarations.test.ts +161 -0
  201. package/src/channels/config.ts +13 -0
  202. package/src/channels/plugin-channel-declarations.ts +108 -0
  203. package/src/channels/types.ts +30 -0
  204. package/src/cli/AGENTS.md +5 -2
  205. package/src/cli/bundled-modules.ts +29 -0
  206. package/src/cli/commands/credentials.help.ts +2 -2
  207. package/src/cli/commands/db/repair.ts +4 -8
  208. package/src/cli/commands/domain.ts +6 -3
  209. package/src/cli/commands/email.ts +6 -3
  210. package/src/cli/commands/inference-providers.ts +1 -1
  211. package/src/cli/commands/keys.ts +8 -3
  212. package/src/cli/commands/mcp.help.ts +13 -4
  213. package/src/cli/commands/mcp.ts +9 -0
  214. package/src/cli/commands/memory/__tests__/memory-v2.test.ts +57 -7
  215. package/src/cli/commands/memory/__tests__/memory-v3.test.ts +128 -5
  216. package/src/cli/commands/memory/index.help.ts +84 -28
  217. package/src/cli/commands/memory/index.ts +2 -0
  218. package/src/cli/commands/memory/memory-v2.ts +58 -54
  219. package/src/cli/commands/memory/memory-v3.ts +64 -0
  220. package/src/cli/commands/memory/memory-validate.ts +18 -0
  221. package/src/cli/commands/plugins.ts +85 -36
  222. package/src/cli/commands/schedules.ts +35 -1
  223. package/src/cli/commands/stt.help.ts +27 -2
  224. package/src/cli/commands/trust.ts +3 -14
  225. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +39 -0
  226. package/src/cli/lib/bundled-marketplace.json +14 -1
  227. package/src/cli/lib/upgrade-plugin.ts +42 -0
  228. package/src/config/__tests__/balanced-model-experiment.test.ts +278 -0
  229. package/src/config/__tests__/default-profile-catalog.test.ts +34 -2
  230. package/src/config/__tests__/default-provider.test.ts +6 -1
  231. package/src/config/__tests__/profile-materialization.test.ts +75 -19
  232. package/src/config/assistant-feature-flags.ts +36 -15
  233. package/src/config/balanced-model-experiment.ts +35 -0
  234. package/src/config/bundled-skills/acp/SKILL.md +6 -7
  235. package/src/config/bundled-skills/document-editor/SKILL.md +2 -2
  236. package/src/config/bundled-skills/document-editor/TOOLS.json +2 -2
  237. package/src/config/bundled-skills/image-studio/SKILL.md +5 -4
  238. package/src/config/bundled-skills/image-studio/TOOLS.json +1 -1
  239. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +101 -0
  240. package/src/config/bundled-skills/media-processing/services/preprocess.ts +14 -4
  241. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  242. package/src/config/bundled-skills/settings/tools/navigate-settings-tab.test.ts +65 -0
  243. package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +7 -1
  244. package/src/config/bundled-skills/settings/tools/shared.ts +16 -0
  245. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +19 -1
  246. package/src/config/bundled-skills/skill-management/TOOLS.json +9 -3
  247. package/src/config/bundled-skills/subagent/SKILL.md +17 -12
  248. package/src/config/bundled-skills/subagent/TOOLS.json +4 -4
  249. package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +22 -1
  250. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +9 -2
  251. package/src/config/call-site-defaults.ts +11 -5
  252. package/src/config/default-profile-catalog.ts +179 -16
  253. package/src/config/default-profile-names.ts +4 -1
  254. package/src/config/default-provider-resolution.ts +4 -0
  255. package/src/config/feature-flag-registry.json +12 -12
  256. package/src/config/llm-context-resolution.ts +11 -3
  257. package/src/config/llm-resolver.ts +28 -1
  258. package/src/config/profile-materialization.ts +70 -22
  259. package/src/config/schemas/__tests__/live-voice.test.ts +107 -4
  260. package/src/config/schemas/call-site-catalog.ts +4 -4
  261. package/src/config/schemas/live-voice.ts +57 -23
  262. package/src/config/schemas/llm.ts +59 -32
  263. package/src/config/schemas/mcp.ts +23 -0
  264. package/src/config/schemas/plugin-updates.ts +6 -2
  265. package/src/config/schemas/stt.ts +1 -0
  266. package/src/config/skills.ts +9 -2
  267. package/src/context/outbound-sanitize.ts +96 -1
  268. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +1 -1
  269. package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +82 -0
  270. package/src/daemon/conversation-agent-loop-handlers.ts +15 -10
  271. package/src/daemon/conversation-agent-loop.ts +31 -19
  272. package/src/daemon/conversation-messaging.ts +5 -1
  273. package/src/daemon/conversation-notifiers.ts +20 -10
  274. package/src/daemon/conversation-process.ts +20 -6
  275. package/src/daemon/conversation-runtime-assembly.ts +12 -6
  276. package/src/daemon/conversation-store.ts +14 -4
  277. package/src/daemon/conversation-surfaces.ts +75 -26
  278. package/src/daemon/conversation-tool-setup.ts +3 -6
  279. package/src/daemon/conversation.ts +154 -51
  280. package/src/daemon/doordash-steps.ts +2 -2
  281. package/src/daemon/lifecycle.ts +14 -1
  282. package/src/daemon/mcp-reload-service.ts +36 -6
  283. package/src/daemon/process-message.ts +13 -26
  284. package/src/daemon/providers-setup.ts +6 -3
  285. package/src/daemon/trust-context-types.ts +29 -0
  286. package/src/daemon/trust-context.ts +12 -7
  287. package/src/daemon/wake-conversation-ops.ts +3 -2
  288. package/src/daemon/windows-compiled-entry.ts +4 -0
  289. package/src/documents/document-store.ts +143 -240
  290. package/src/hooks/hook-loader.ts +3 -3
  291. package/src/hooks/registry.ts +103 -45
  292. package/src/hooks/types.ts +5 -0
  293. package/src/inbound/__tests__/oauth-callback-url.test.ts +83 -0
  294. package/src/inbound/oauth-callback-url.ts +61 -0
  295. package/src/ipc/gateway-client.test.ts +1 -1
  296. package/src/ipc/gateway-client.ts +14 -21
  297. package/src/ipc/gateway-flag-listener.ts +17 -3
  298. package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +1 -104
  299. package/src/live-voice/__tests__/live-voice-events.test.ts +7 -8
  300. package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +1050 -0
  301. package/src/live-voice/__tests__/live-voice-metrics.test.ts +115 -8
  302. package/src/live-voice/__tests__/live-voice-photo.test.ts +100 -0
  303. package/src/live-voice/__tests__/live-voice-progress.test.ts +60 -194
  304. package/src/live-voice/__tests__/live-voice-stt.test.ts +14 -0
  305. package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +29 -0
  306. package/src/live-voice/__tests__/live-voice-tts-session.test.ts +0 -483
  307. package/src/live-voice/__tests__/live-voice-vad.test.ts +0 -16
  308. package/src/live-voice/__tests__/progress-narration.test.ts +214 -0
  309. package/src/live-voice/live-voice-archive.ts +2 -0
  310. package/src/live-voice/live-voice-manager.ts +16 -3
  311. package/src/live-voice/live-voice-metrics.ts +57 -32
  312. package/src/live-voice/live-voice-photo.ts +1 -2
  313. package/src/live-voice/live-voice-session.ts +598 -323
  314. package/src/live-voice/progress-narration.ts +277 -0
  315. package/src/live-voice/protocol.ts +21 -1
  316. package/src/live-voice/windows-compiled-live-voice.ts +4 -0
  317. package/src/mcp/__tests__/effective-config.test.ts +238 -0
  318. package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +0 -1
  319. package/src/mcp/__tests__/mcp-oauth-client-registration.test.ts +200 -0
  320. package/src/mcp/__tests__/mcp-oauth-provider.test.ts +9 -9
  321. package/src/mcp/__tests__/plugin-server-credential-isolation.test.ts +95 -0
  322. package/src/mcp/client.ts +16 -11
  323. package/src/mcp/effective-config.ts +113 -0
  324. package/src/mcp/manager.ts +11 -6
  325. package/src/mcp/mcp-auth-orchestrator.ts +13 -22
  326. package/src/mcp/mcp-oauth-provider.ts +205 -240
  327. package/src/monitoring/__tests__/plugin-auto-update.test.ts +166 -3
  328. package/src/monitoring/control.ts +1 -0
  329. package/src/monitoring/db-integrity-sample.ts +4 -5
  330. package/src/monitoring/plugin-auto-update.ts +128 -24
  331. package/src/notifications/AGENTS.md +2 -0
  332. package/src/notifications/approval-card-data.ts +33 -0
  333. package/src/notifications/signal.ts +1 -0
  334. package/src/permissions/AGENTS.md +16 -0
  335. package/src/permissions/checker.test.ts +75 -122
  336. package/src/permissions/checker.ts +44 -560
  337. package/src/permissions/confirmation-guardian-request.test.ts +15 -11
  338. package/src/permissions/confirmation-guardian-request.ts +2 -2
  339. package/src/permissions/prompter.ts +1 -5
  340. package/src/permissions/question-guardian-request.test.ts +14 -6
  341. package/src/permissions/question-guardian-request.ts +1 -2
  342. package/src/persistence/attachments-store.ts +8 -1
  343. package/src/persistence/bookmark-crud.ts +3 -7
  344. package/src/persistence/conversation-attention-store.ts +16 -45
  345. package/src/persistence/conversation-crud.ts +33 -4
  346. package/src/persistence/conversation-lineage.ts +9 -0
  347. package/src/persistence/conversation-queries.ts +182 -52
  348. package/src/persistence/delivery-crud.ts +150 -36
  349. package/src/persistence/embeddings/qdrant-manager.ts +84 -49
  350. package/src/persistence/external-conversation-store.ts +32 -4
  351. package/src/persistence/llm-request-log-store.ts +4 -10
  352. package/src/persistence/llm-usage-store.ts +8 -3
  353. package/src/persistence/message-reads.test.ts +197 -0
  354. package/src/persistence/message-reads.ts +211 -0
  355. package/src/persistence/migrations/360-add-document-workspace-path.ts +5 -14
  356. package/src/persistence/migrations/366-chatgpt-subscription-row-identity.test.ts +120 -0
  357. package/src/persistence/migrations/366-chatgpt-subscription-row-identity.ts +62 -0
  358. package/src/persistence/real-user-turn-filter.ts +27 -3
  359. package/src/persistence/schema/documents.ts +4 -4
  360. package/src/persistence/steps.ts +9 -0
  361. package/src/plugin-api/__tests__/oauth-callback-url-export.test.ts +29 -0
  362. package/src/plugin-api/constants.ts +8 -0
  363. package/src/plugin-api/conversation-turn.ts +195 -5
  364. package/src/plugin-api/index.ts +38 -6
  365. package/src/plugin-api/resolve-credential.ts +3 -2
  366. package/src/plugin-api/store-credential.ts +146 -0
  367. package/src/plugin-api/vision-support.test.ts +1 -1
  368. package/src/plugin-api/webhook-url.ts +13 -11
  369. package/src/plugins/__tests__/mcp-servers.test.ts +371 -0
  370. package/src/plugins/defaults/main.ts +21 -7
  371. package/src/plugins/defaults/memory/AGENTS.md +15 -3
  372. package/src/plugins/defaults/memory/__tests__/buffer-format.test.ts +204 -0
  373. package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +72 -0
  374. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +4 -1
  375. package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +4 -1
  376. package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +0 -1
  377. package/src/plugins/defaults/memory/buffer-format.ts +165 -0
  378. package/src/plugins/defaults/memory/context-search/sources/conversations.ts +6 -0
  379. package/src/plugins/defaults/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +77 -0
  380. package/src/plugins/defaults/memory/graph/conversation-graph-memory.ts +24 -6
  381. package/src/plugins/defaults/memory/graph/image-ref-utils.ts +3 -0
  382. package/src/plugins/defaults/memory/graph/tool-handlers.ts +1 -30
  383. package/src/plugins/defaults/memory/graph-topology/pending-buffer.test.ts +34 -0
  384. package/src/plugins/defaults/memory/graph-topology/pending-buffer.ts +17 -24
  385. package/src/plugins/defaults/memory/hooks/post-compact.ts +1 -4
  386. package/src/plugins/defaults/memory/hooks/user-prompt-submit.ts +53 -5
  387. package/src/plugins/defaults/memory/indexer.ts +3 -1
  388. package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +19 -7
  389. package/src/plugins/defaults/memory/memory-retrospective-job.ts +4 -4
  390. package/src/plugins/defaults/memory/src/__tests__/memory-v3-gate-stats.test.ts +281 -0
  391. package/src/plugins/defaults/memory/src/memory-v2-routes.ts +12 -10
  392. package/src/plugins/defaults/memory/src/memory-v3-routes.ts +207 -0
  393. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +147 -0
  394. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-prompt-flag-gating-guard.test.ts +8 -0
  395. package/src/plugins/defaults/memory/substrate/__tests__/edge-index.test.ts +0 -30
  396. package/src/plugins/defaults/memory/substrate/__tests__/ingest.test.ts +73 -1
  397. package/src/plugins/defaults/memory/substrate/__tests__/page-index.test.ts +37 -0
  398. package/src/plugins/defaults/memory/substrate/__tests__/page-links.test.ts +208 -0
  399. package/src/plugins/defaults/memory/substrate/__tests__/prompts-consolidation.test.ts +158 -1
  400. package/src/plugins/defaults/memory/substrate/__tests__/static-context.test.ts +195 -58
  401. package/src/plugins/defaults/memory/substrate/consolidation-job.ts +93 -24
  402. package/src/plugins/defaults/memory/substrate/edge-index.ts +0 -31
  403. package/src/plugins/defaults/memory/substrate/ingest.ts +59 -0
  404. package/src/plugins/defaults/memory/substrate/page-index.ts +35 -8
  405. package/src/plugins/defaults/memory/substrate/page-links.ts +133 -0
  406. package/src/plugins/defaults/memory/substrate/page-store.ts +10 -0
  407. package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +124 -34
  408. package/src/plugins/defaults/memory/substrate/skill-content.ts +8 -1
  409. package/src/plugins/defaults/memory/substrate/static-context.ts +160 -33
  410. package/src/plugins/defaults/memory/substrate/sweep-job.ts +2 -4
  411. package/src/plugins/defaults/memory/v1/graph/extraction.ts +3 -1
  412. package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +18 -0
  413. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +66 -1
  414. package/src/plugins/defaults/memory/v3/card.ts +9 -9
  415. package/src/plugins/defaults/memory/v3/core-set.test.ts +7 -0
  416. package/src/plugins/defaults/memory/v3/core-set.ts +5 -1
  417. package/src/plugins/defaults/memory/v3/edge.ts +13 -57
  418. package/src/plugins/defaults/memory/v3/injector.ts +8 -0
  419. package/src/plugins/defaults/memory/v3/prune.ts +2 -0
  420. package/src/plugins/defaults/memory/v3/selection-log-store.ts +2 -0
  421. package/src/plugins/defaults/memory/v3/shadow-plugin.ts +23 -9
  422. package/src/plugins/defaults/memory/worker-control.ts +1 -0
  423. package/src/plugins/defaults/memory/worker.ts +6 -3
  424. package/src/plugins/defaults/worker-entrypoints.ts +3 -0
  425. package/src/plugins/external-plugin-loader.ts +47 -0
  426. package/src/plugins/mcp-servers.ts +361 -0
  427. package/src/plugins/mtime-cache.ts +37 -46
  428. package/src/plugins/pipeline.ts +14 -7
  429. package/src/plugins/plugin-execution-context.ts +35 -11
  430. package/src/plugins/worker-plugin-surface.ts +33 -0
  431. package/src/prompts/templates/system-sections.ts +0 -7
  432. package/src/providers/__tests__/connection-model-compat.test.ts +1 -1
  433. package/src/providers/__tests__/context-overflow-error.test.ts +24 -0
  434. package/src/providers/__tests__/dispatch-connection-routing.test.ts +214 -2
  435. package/src/providers/__tests__/preflight-resolved-config.test.ts +57 -0
  436. package/src/providers/__tests__/retry-callsite.test.ts +25 -2
  437. package/src/providers/anthropic/__tests__/pause-turn-continuation.test.ts +170 -0
  438. package/src/providers/anthropic/client.ts +332 -241
  439. package/src/providers/call-site-routing.ts +30 -3
  440. package/src/providers/connection-resolution.ts +194 -11
  441. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +4 -0
  442. package/src/providers/inference/adapter-factory.ts +3 -0
  443. package/src/providers/inference/auth.ts +6 -0
  444. package/src/providers/inference/connection-availability.ts +24 -2
  445. package/src/providers/inference/connections.ts +2 -0
  446. package/src/providers/model-catalog.ts +3 -3
  447. package/src/providers/model-intents.ts +28 -8
  448. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +163 -40
  449. package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +125 -2
  450. package/src/providers/openai/chat-completions-provider.ts +133 -36
  451. package/src/providers/openai/codex-models.ts +2 -1
  452. package/src/providers/provider-send-message.ts +32 -3
  453. package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +433 -0
  454. package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +625 -0
  455. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +34 -0
  456. package/src/providers/speech-to-text/__tests__/resolve.test.ts +285 -6
  457. package/src/providers/speech-to-text/deepgram-flux-frames.ts +395 -0
  458. package/src/providers/speech-to-text/deepgram-flux-realtime.ts +679 -0
  459. package/src/providers/speech-to-text/provider-catalog.ts +99 -8
  460. package/src/providers/speech-to-text/resolve.ts +25 -2
  461. package/src/routes/control.ts +1 -0
  462. package/src/routes/route-host-client.ts +1 -0
  463. package/src/routes/route-host-protocol.ts +7 -0
  464. package/src/routes/worker.ts +48 -15
  465. package/src/runtime/AGENTS.md +16 -17
  466. package/src/runtime/access-request-helper.ts +9 -12
  467. package/src/runtime/agent-wake.ts +18 -15
  468. package/src/runtime/effective-capabilities.test.ts +43 -0
  469. package/src/runtime/effective-capabilities.ts +27 -0
  470. package/src/runtime/guardian-reply-router.ts +6 -2
  471. package/src/runtime/pre-first-message-gate.ts +4 -0
  472. package/src/runtime/routes/__tests__/acp-claude-auth-routes.test.ts +12 -4
  473. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +522 -2
  474. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +52 -0
  475. package/src/runtime/routes/__tests__/default-provider-routes.test.ts +61 -0
  476. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +44 -0
  477. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +102 -1
  478. package/src/runtime/routes/__tests__/plugins-routes.test.ts +132 -35
  479. package/src/runtime/routes/__tests__/schedule-routes-disarm-reason.test.ts +215 -0
  480. package/src/runtime/routes/__tests__/stt-routes.test.ts +25 -0
  481. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +55 -6
  482. package/src/runtime/routes/__tests__/user-route-dispatcher-host.test.ts +52 -1
  483. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +133 -1
  484. package/src/runtime/routes/channel-availability-routes.ts +32 -14
  485. package/src/runtime/routes/channel-route-shared.ts +0 -6
  486. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +6 -6
  487. package/src/runtime/routes/conversation-list-routes.ts +181 -23
  488. package/src/runtime/routes/conversation-management-routes.ts +17 -12
  489. package/src/runtime/routes/conversation-query-routes.ts +40 -27
  490. package/src/runtime/routes/conversation-routes.ts +43 -29
  491. package/src/runtime/routes/credential-prompt-routes.ts +4 -7
  492. package/src/runtime/routes/credential-routes.ts +26 -82
  493. package/src/runtime/routes/default-provider-routes.ts +15 -0
  494. package/src/runtime/routes/documents-routes.ts +3 -222
  495. package/src/runtime/routes/inbound-message-handler.ts +21 -45
  496. package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +0 -1
  497. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -9
  498. package/src/runtime/routes/inbound-stages/admission-policy.ts +1 -17
  499. package/src/runtime/routes/inbound-stages/bootstrap-intercept.test.ts +0 -1
  500. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -3
  501. package/src/runtime/routes/inbound-stages/edit-intercept.ts +101 -79
  502. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +0 -1
  503. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +12 -7
  504. package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +91 -8
  505. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +72 -79
  506. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +2 -3
  507. package/src/runtime/routes/inference-profiles-routes.ts +20 -11
  508. package/src/runtime/routes/inference-provider-connection-routes.ts +77 -15
  509. package/src/runtime/routes/log-export-routes.ts +3 -0
  510. package/src/runtime/routes/mcp-auth-routes.ts +148 -57
  511. package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +2 -0
  512. package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +3 -0
  513. package/src/runtime/routes/playground/inject-failures.ts +2 -2
  514. package/src/runtime/routes/playground/reset-circuit.ts +1 -1
  515. package/src/runtime/routes/plugins-routes.ts +38 -8
  516. package/src/runtime/routes/schedule-routes.ts +94 -7
  517. package/src/runtime/routes/settings-routes.ts +6 -6
  518. package/src/runtime/routes/stt-routes.ts +31 -25
  519. package/src/runtime/routes/surface-action-routes.ts +20 -19
  520. package/src/runtime/routes/surface-conversation-resolver.ts +3 -0
  521. package/src/runtime/routes/user-route-dispatcher.ts +69 -15
  522. package/src/runtime/routes/user-route-import.ts +108 -0
  523. package/src/runtime/routes/user-route-resolution.ts +18 -1
  524. package/src/runtime/routes/workspace-routes.ts +0 -9
  525. package/src/runtime/routes/workspace-utils.ts +3 -13
  526. package/src/runtime/services/conversation-serializer.ts +7 -2
  527. package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +68 -5
  528. package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +81 -0
  529. package/src/schedule/plugin-schedule-availability.ts +58 -0
  530. package/src/schedule/plugin-schedule-declarations.ts +23 -27
  531. package/src/schedule/plugin-schedule-reconciler.ts +12 -3
  532. package/src/schedule/schedule-store.ts +5 -1
  533. package/src/schedule/scheduler.ts +9 -4
  534. package/src/schedule/worker-control.ts +1 -0
  535. package/src/schedule/worker.ts +6 -0
  536. package/src/security/oauth2.ts +6 -22
  537. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +22 -0
  538. package/src/stt/__tests__/types.test.ts +94 -0
  539. package/src/stt/daemon-batch-transcriber.ts +10 -0
  540. package/src/stt/stt-stream-session.ts +8 -4
  541. package/src/stt/types.ts +103 -0
  542. package/src/subagent/__tests__/consult-prompt.test.ts +26 -15
  543. package/src/subagent/consult-context.ts +11 -11
  544. package/src/subagent/consult-prompt.ts +26 -35
  545. package/src/subagent/manager.ts +21 -40
  546. package/src/subagent/notify.ts +7 -1
  547. package/src/subagent/types.ts +15 -12
  548. package/src/tools/__tests__/tool-input-schemas.test.ts +7 -7
  549. package/src/tools/acp/spawn.test.ts +97 -0
  550. package/src/tools/acp/spawn.ts +38 -4
  551. package/src/tools/credentials/ref-parse.ts +35 -0
  552. package/src/tools/credentials/resolve.ts +4 -10
  553. package/src/tools/credentials/store.ts +168 -0
  554. package/src/tools/document/document-tool.ts +12 -3
  555. package/src/tools/executor.ts +53 -15
  556. package/src/tools/filesystem/read.ts +27 -10
  557. package/src/tools/host-filesystem/read.ts +27 -15
  558. package/src/tools/network/url-safety.ts +9 -16
  559. package/src/tools/permission-checker.ts +43 -52
  560. package/src/tools/registry.ts +2 -1
  561. package/src/tools/shared/filesystem/file-ops-service.ts +63 -35
  562. package/src/tools/shared/filesystem/legacy-read-args.ts +22 -0
  563. package/src/tools/shared/filesystem/types.ts +5 -5
  564. package/src/tools/skills/scaffold-managed.ts +25 -7
  565. package/src/tools/subagent/spawn.ts +28 -88
  566. package/src/tools/tool-types.ts +26 -13
  567. package/src/tools/types.ts +6 -6
  568. package/src/tools/ui-surface/surface-shape-docs.ts +12 -1
  569. package/src/tools/workflows/run-workflow.ts +1 -2
  570. package/src/tts/__tests__/reasoning-tag-filter.test.ts +78 -0
  571. package/src/tts/reasoning-tag-filter.ts +89 -0
  572. package/src/util/__tests__/worker-process-command.test.ts +37 -0
  573. package/src/util/logger.ts +16 -0
  574. package/src/util/think-tag-stream.ts +95 -0
  575. package/src/util/worker-process.ts +37 -4
  576. package/src/windows-compiled-cli.ts +32 -0
  577. package/src/windows-compiled-entry.ts +4 -0
  578. package/src/windows-compiled-logger.ts +6 -0
  579. package/src/windows-compiled-worker-entry.ts +29 -0
  580. package/src/workspace/byok-default-profile-ensure.ts +76 -24
  581. package/src/workspace/custom-profile-ensure.ts +4 -24
  582. package/src/workspace/migrations/142-consolidate-voice-front-door.ts +70 -0
  583. package/src/workspace/migrations/143-repair-deprecated-codex-model-id.ts +134 -0
  584. package/src/workspace/migrations/144-convert-stranded-subscription-openai-profiles.ts +265 -0
  585. package/src/workspace/migrations/145-collapse-profile-bindings-to-entries.ts +328 -0
  586. package/src/workspace/migrations/146-repair-retired-fireworks-deepseek-flash-model-id.ts +195 -0
  587. package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +0 -10
  588. package/src/workspace/migrations/registry.ts +10 -0
  589. package/src/workspace/provider-commit-message-generator.ts +7 -5
  590. package/src/__tests__/document-workspace-file.test.ts +0 -467
  591. package/src/live-voice/__tests__/front-decision.test.ts +0 -645
  592. package/src/live-voice/front-decision.ts +0 -476
  593. package/src/permissions/ipc-risk-types.ts +0 -143
  594. package/src/permissions/risk-types.ts +0 -76
  595. package/src/subagent/__tests__/consult-transcript.test.ts +0 -184
  596. package/src/subagent/consult-transcript.ts +0 -90
@@ -0,0 +1,250 @@
1
+ /**
2
+ * Route-level coverage for plugin-declared MCP servers appearing in
3
+ * `internal_mcp_list` (what `assistant mcp list` renders).
4
+ *
5
+ * The unit tests in `plugins/__tests__/mcp-servers.test.ts` cover reading
6
+ * `mcp.json`. This file covers the merge, and two properties that a
7
+ * refactor could quietly break:
8
+ *
9
+ * 1. A workspace entry of the same id wins. Getting precedence backwards
10
+ * would let a plugin redirect a server the user configured by hand.
11
+ * 2. A plugin server is never health-checked. `McpClient.connect` resolves
12
+ * `mcp:<serverId>:headers` and `mcp:<serverId>:tokens` from the
13
+ * credential store, and a plugin controls both its server key and its
14
+ * URL, so probing one would send a workspace credential to an endpoint
15
+ * the plugin chose whenever an id happens to match a stored key.
16
+ */
17
+
18
+ import { mkdirSync, rmSync, writeFileSync } from "node:fs";
19
+ import { join } from "node:path";
20
+ import { beforeEach, describe, expect, jest, mock, test } from "bun:test";
21
+
22
+ const mockConnect = jest.fn();
23
+ const mockDisconnect = jest.fn();
24
+ /** Server ids the route actually constructed an MCP client for. */
25
+ const connectedServerIds: string[] = [];
26
+
27
+ mock.module("../mcp/client.js", () => ({
28
+ McpClient: class {
29
+ constructor(serverId: string) {
30
+ connectedServerIds.push(serverId);
31
+ }
32
+ get isConnected() {
33
+ return true;
34
+ }
35
+ get lastError() {
36
+ return null;
37
+ }
38
+ connect = mockConnect;
39
+ disconnect = mockDisconnect;
40
+ },
41
+ }));
42
+
43
+ mock.module("../mcp/mcp-auth-orchestrator.js", () => ({
44
+ orchestrateMcpOAuthConnect: async () => ({
45
+ auth_url: "",
46
+ already_authenticated: false,
47
+ }),
48
+ }));
49
+
50
+ mock.module("../mcp/mcp-auth-state.js", () => ({
51
+ getMcpAuthState: () => null,
52
+ }));
53
+
54
+ mock.module("../mcp/mcp-oauth-provider.js", () => ({
55
+ // Stand in for a credential store that holds tokens for every id, which
56
+ // is the condition under which a leak would be observable.
57
+ hasMcpOAuthTokens: async () => true,
58
+ deleteMcpOAuthCredentials: async () => ({ ok: true, failedKeys: [] }),
59
+ }));
60
+
61
+ mock.module("../mcp/mcp-header-store.js", () => ({
62
+ getMcpHeaders: async () => ({ Authorization: "Bearer workspace-secret" }),
63
+ setMcpHeaders: async () => {},
64
+ deleteMcpHeaders: async () => true,
65
+ }));
66
+
67
+ mock.module("../daemon/mcp-reload-service.js", () => ({
68
+ reloadMcpServers: async () => {},
69
+ }));
70
+
71
+ import { setConfig } from "./helpers/set-config.js";
72
+
73
+ setConfig("mcp", {
74
+ servers: {
75
+ "from-workspace": {
76
+ transport: { type: "streamable-http", url: "https://config.example/mcp" },
77
+ enabled: true,
78
+ defaultRiskLevel: "high",
79
+ maxTools: 20,
80
+ },
81
+ // Deliberately shares an id with the `shadowed` plugin below.
82
+ shadowed: {
83
+ transport: { type: "streamable-http", url: "https://wins.example/mcp" },
84
+ enabled: true,
85
+ defaultRiskLevel: "low",
86
+ maxTools: 20,
87
+ },
88
+ },
89
+ });
90
+
91
+ const { getWorkspacePluginsDir } = await import("../util/platform.js");
92
+ const { ROUTES } = await import("../runtime/routes/mcp-auth-routes.js");
93
+
94
+ const listHandler = ROUTES.find(
95
+ (r: { operationId: string }) => r.operationId === "internal_mcp_list",
96
+ )!.handler;
97
+
98
+ interface ListedServer {
99
+ id: string;
100
+ status: string;
101
+ source?: "workspace" | "plugin";
102
+ pluginName?: string;
103
+ defaultRiskLevel: string;
104
+ hasOAuth: boolean;
105
+ hasStaticAuth: boolean;
106
+ authType: string;
107
+ transport: { type: string; url?: string };
108
+ }
109
+
110
+ function writePlugin(name: string, mcpJson: unknown): void {
111
+ const dir = join(getWorkspacePluginsDir(), name);
112
+ mkdirSync(dir, { recursive: true });
113
+ writeFileSync(
114
+ join(dir, "package.json"),
115
+ JSON.stringify({ name, version: "1.0.0" }),
116
+ );
117
+ writeFileSync(join(dir, "mcp.json"), JSON.stringify(mcpJson));
118
+ }
119
+
120
+ function unabyssManifest(): unknown {
121
+ return {
122
+ mcpServers: {
123
+ unabyss: { type: "streamable-http", url: "https://mcp.unabyss.com" },
124
+ },
125
+ };
126
+ }
127
+
128
+ async function listServers(): Promise<ListedServer[]> {
129
+ const result = (await listHandler({})) as { servers: ListedServer[] };
130
+ return result.servers;
131
+ }
132
+
133
+ describe("internal_mcp_list, plugin-declared servers", () => {
134
+ beforeEach(() => {
135
+ mockConnect.mockReset();
136
+ mockDisconnect.mockReset();
137
+ connectedServerIds.length = 0;
138
+ rmSync(getWorkspacePluginsDir(), { recursive: true, force: true });
139
+ mkdirSync(getWorkspacePluginsDir(), { recursive: true });
140
+ });
141
+
142
+ test("a plugin's mcp.json server appears alongside workspace servers", async () => {
143
+ writePlugin("unabyss", unabyssManifest());
144
+
145
+ const ids = (await listServers()).map((s) => s.id);
146
+ expect(ids).toContain("from-workspace");
147
+ expect(ids).toContain("unabyss");
148
+ });
149
+
150
+ test("plugin servers are labelled with their origin, workspace servers are not", async () => {
151
+ writePlugin("unabyss", unabyssManifest());
152
+
153
+ const servers = await listServers();
154
+ const plugin = servers.find((s) => s.id === "unabyss")!;
155
+ const workspace = servers.find((s) => s.id === "from-workspace")!;
156
+
157
+ expect(plugin.source).toEqual("plugin");
158
+ expect(plugin.pluginName).toEqual("unabyss");
159
+ expect(workspace.source).toEqual("workspace");
160
+ expect(workspace.pluginName).toBeUndefined();
161
+ });
162
+
163
+ test("a workspace server of the same id wins over the plugin's", async () => {
164
+ writePlugin("shadowed", {
165
+ mcpServers: {
166
+ shadowed: { type: "streamable-http", url: "https://loses.example/mcp" },
167
+ },
168
+ });
169
+
170
+ const matches = (await listServers()).filter((s) => s.id === "shadowed");
171
+
172
+ expect(matches).toHaveLength(1);
173
+ expect(matches[0].source).toEqual("workspace");
174
+ expect(matches[0].transport.url).toEqual("https://wins.example/mcp");
175
+ });
176
+
177
+ test("plugin servers are not health-checked, so no credential can reach them", async () => {
178
+ writePlugin("unabyss", unabyssManifest());
179
+
180
+ const servers = await listServers();
181
+
182
+ expect(servers.find((s) => s.id === "unabyss")!.status).toEqual("declared");
183
+ // The credential mocks above return a token and an Authorization header
184
+ // for every id. Constructing a client for the plugin server is what
185
+ // would ship them to the plugin-declared URL.
186
+ expect(connectedServerIds).toContain("from-workspace");
187
+ expect(connectedServerIds).not.toContain("unabyss");
188
+ });
189
+
190
+ test("plugin servers report no assistant-owned auth even when the store has some", async () => {
191
+ writePlugin("unabyss", unabyssManifest());
192
+
193
+ const plugin = (await listServers()).find((s) => s.id === "unabyss")!;
194
+ expect(plugin.hasOAuth).toBe(false);
195
+ expect(plugin.hasStaticAuth).toBe(false);
196
+ expect(plugin.authType).toEqual("none");
197
+ });
198
+
199
+ test("workspace servers keep reporting their auth state", async () => {
200
+ const workspace = (await listServers()).find(
201
+ (s) => s.id === "from-workspace",
202
+ )!;
203
+ expect(workspace.hasOAuth).toBe(true);
204
+ expect(workspace.hasStaticAuth).toBe(true);
205
+ });
206
+
207
+ test("plugin servers default to low risk", async () => {
208
+ writePlugin("unabyss", unabyssManifest());
209
+
210
+ const servers = await listServers();
211
+ expect(servers.find((s) => s.id === "unabyss")!.defaultRiskLevel).toEqual(
212
+ "low",
213
+ );
214
+ });
215
+
216
+ test("a directory with no valid package.json is not advertised", async () => {
217
+ // The runtime loader would never load it, and its stdio command must
218
+ // not be reachable through the listing.
219
+ const dir = join(getWorkspacePluginsDir(), "no-manifest");
220
+ mkdirSync(dir, { recursive: true });
221
+ writeFileSync(
222
+ join(dir, "mcp.json"),
223
+ JSON.stringify({
224
+ mcpServers: { evil: { type: "stdio", command: "touch" } },
225
+ }),
226
+ );
227
+
228
+ const ids = (await listServers()).map((s) => s.id);
229
+ expect(ids).not.toContain("no-manifest__evil");
230
+ expect(ids).toContain("from-workspace");
231
+ });
232
+
233
+ test("a malformed plugin manifest does not remove workspace servers", async () => {
234
+ const dir = join(getWorkspacePluginsDir(), "broken");
235
+ mkdirSync(dir, { recursive: true });
236
+ writeFileSync(
237
+ join(dir, "package.json"),
238
+ JSON.stringify({ name: "broken", version: "1.0.0" }),
239
+ );
240
+ writeFileSync(join(dir, "mcp.json"), "{ not json");
241
+
242
+ const ids = (await listServers()).map((s) => s.id);
243
+ expect(ids).toContain("from-workspace");
244
+ });
245
+
246
+ test("no plugins installed leaves the listing unchanged", async () => {
247
+ const servers = await listServers();
248
+ expect(servers.every((s) => s.source === "workspace")).toBe(true);
249
+ });
250
+ });
@@ -1,6 +1,14 @@
1
1
  import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
2
2
 
3
- import { readFileSync } from "fs";
3
+ import {
4
+ mkdtempSync,
5
+ readdirSync,
6
+ readFileSync,
7
+ rmSync,
8
+ symlinkSync,
9
+ writeFileSync,
10
+ } from "fs";
11
+ import { tmpdir } from "os";
4
12
  import { dirname, join } from "path";
5
13
 
6
14
  import { __resetRegistryForTesting, getTool } from "../tools/registry.js";
@@ -97,8 +105,23 @@ import { run } from "../config/bundled-skills/image-studio/tools/media-generate-
97
105
  // Clean up after this file to prevent contamination of later test files.
98
106
  afterAll(() => {
99
107
  __resetRegistryForTesting();
108
+ for (const dir of tempWorkingDirs) {
109
+ rmSync(dir, { recursive: true, force: true });
110
+ }
100
111
  });
101
112
 
113
+ const tempWorkingDirs: string[] = [];
114
+
115
+ /** Build a ToolContext with a fresh temp working directory. */
116
+ function makeContext(): ToolContext {
117
+ const workingDir = mkdtempSync(join(tmpdir(), "media-gen-test-"));
118
+ tempWorkingDirs.push(workingDir);
119
+ return {
120
+ conversationId: "conv-123",
121
+ workingDir,
122
+ } as unknown as ToolContext;
123
+ }
124
+
102
125
  const CONFIG_DIR = join(
103
126
  dirname(import.meta.dirname!),
104
127
  "config",
@@ -128,12 +151,10 @@ beforeEach(() => {
128
151
  platformBaseUrl: "",
129
152
  assistantApiKey: "",
130
153
  };
154
+ fakeContext = makeContext();
131
155
  });
132
156
 
133
- const fakeContext = {
134
- conversationId: "conv-123",
135
- workingDir: "/tmp",
136
- } as unknown as ToolContext;
157
+ let fakeContext: ToolContext;
137
158
 
138
159
  describe("image-studio skill script wrapper", () => {
139
160
  test("exports a run function without registering media_generate_image in the tool registry", async () => {
@@ -431,28 +452,117 @@ describe("image-studio skill script wrapper", () => {
431
452
  });
432
453
 
433
454
  test("reads source images from file paths on disk", async () => {
434
- // Write a temp image file inside the workspace (fakeContext.workingDir = /tmp)
435
- const tmpPath = join("/tmp", "test-source-image.png");
455
+ // Write a temp image file inside the workspace
456
+ const tmpPath = join(fakeContext.workingDir, "test-source-image.png");
436
457
  const pngBytes = Buffer.from(
437
458
  "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==",
438
459
  "base64",
439
460
  );
440
461
  await Bun.write(tmpPath, pngBytes);
441
462
 
442
- try {
443
- const result = await run(
444
- { prompt: "edit this", mode: "edit", source_paths: [tmpPath] },
445
- fakeContext,
446
- );
447
-
448
- expect(result.isError).toBe(false);
449
- expect(result.content).toContain("Generated 1 image");
450
- } finally {
451
- const { unlink } = await import("fs/promises");
452
- if (await Bun.file(tmpPath).exists()) {
453
- await unlink(tmpPath);
454
- }
455
- }
463
+ const result = await run(
464
+ { prompt: "edit this", mode: "edit", source_paths: [tmpPath] },
465
+ fakeContext,
466
+ );
467
+
468
+ expect(result.isError).toBe(false);
469
+ expect(result.content).toContain("Generated 1 image");
470
+ });
471
+
472
+ test("saves the image into media/generated and returns the embed instruction", async () => {
473
+ const result = await run({ prompt: "a sunset" }, fakeContext);
474
+
475
+ expect(result.isError).toBe(false);
476
+ expect(result.content).toContain("Saved to media/generated/a-sunset.png");
477
+ expect(result.content).toContain(
478
+ "![description](vellum://workspace/media/generated/a-sunset.png)",
479
+ );
480
+ const saved = Bun.file(
481
+ join(fakeContext.workingDir, "media/generated/a-sunset.png"),
482
+ );
483
+ expect(await saved.exists()).toBe(true);
484
+ const bytes = Buffer.from(await saved.arrayBuffer());
485
+ expect(bytes.equals(Buffer.from("generated-data", "base64"))).toBe(true);
486
+ });
487
+
488
+ test("uses the image title for the filename when present", async () => {
489
+ mockGenerateResult = {
490
+ images: [
491
+ {
492
+ mimeType: "image/png",
493
+ dataBase64: "img1",
494
+ title: "Twilight Gremlin!",
495
+ } as never,
496
+ ],
497
+ text: "",
498
+ resolvedModel: "gemini-3.1-flash-image-preview",
499
+ };
500
+
501
+ const result = await run({ prompt: "a pond creature" }, fakeContext);
502
+
503
+ expect(result.isError).toBe(false);
504
+ expect(result.content).toContain(
505
+ "Saved to media/generated/twilight-gremlin.png",
506
+ );
507
+ });
508
+
509
+ test("gives each variant a distinct filename", async () => {
510
+ mockGenerateResult = {
511
+ images: [
512
+ { mimeType: "image/png", dataBase64: "img1" },
513
+ { mimeType: "image/png", dataBase64: "img2" },
514
+ ],
515
+ text: undefined as unknown as string,
516
+ resolvedModel: "gemini-3.1-flash-image-preview",
517
+ };
518
+
519
+ const result = await run({ prompt: "a robot", variants: 2 }, fakeContext);
520
+
521
+ expect(result.isError).toBe(false);
522
+ expect(result.content).toContain("- media/generated/a-robot.png");
523
+ expect(result.content).toContain("- media/generated/a-robot-2.png");
524
+ expect(
525
+ await Bun.file(
526
+ join(fakeContext.workingDir, "media/generated/a-robot-2.png"),
527
+ ).exists(),
528
+ ).toBe(true);
529
+ });
530
+
531
+ test("suffixes the filename when a previous generation already used it", async () => {
532
+ const first = await run({ prompt: "a fox" }, fakeContext);
533
+ expect(first.content).toContain("Saved to media/generated/a-fox.png");
534
+
535
+ const second = await run({ prompt: "a fox" }, fakeContext);
536
+ expect(second.content).toContain("Saved to media/generated/a-fox-2.png");
537
+ });
538
+
539
+ test("refuses to write through a symlink that escapes the workspace", async () => {
540
+ const outside = mkdtempSync(join(tmpdir(), "media-gen-outside-"));
541
+ tempWorkingDirs.push(outside);
542
+ symlinkSync(outside, join(fakeContext.workingDir, "media"));
543
+
544
+ const result = await run({ prompt: "a cat" }, fakeContext);
545
+
546
+ expect(result.isError).toBe(false);
547
+ expect(result.content).toContain("Could not save to the workspace");
548
+ expect(result.content).not.toContain("vellum://workspace/");
549
+ expect(readdirSync(outside)).toEqual([]);
550
+ });
551
+
552
+ test("falls back to inline-only when the workspace write fails", async () => {
553
+ // Occupy the media path with a regular file so the directory creation
554
+ // under it fails.
555
+ writeFileSync(join(fakeContext.workingDir, "media"), "not a directory");
556
+
557
+ const result = await run({ prompt: "a cat" }, fakeContext);
558
+
559
+ expect(result.isError).toBe(false);
560
+ expect(result.content).toContain("Could not save to the workspace");
561
+ expect(result.content).toContain(
562
+ "will be attached to your reply automatically",
563
+ );
564
+ expect(result.content).not.toContain("vellum://workspace/");
565
+ expect(result.contentBlocks).toHaveLength(1);
456
566
  });
457
567
 
458
568
  test("returns error when all source_paths are invalid", async () => {
@@ -64,15 +64,21 @@ mock.module("../runtime/assistant-event-hub.js", () => ({
64
64
  broadcastMessage: broadcastMessageMock,
65
65
  }));
66
66
 
67
- import type { UserPromptSubmitContext } from "@vellumai/plugin-api";
67
+ import {
68
+ type UserPromptSubmitContext,
69
+ VOICE_ESCALATION_CONTINUATION_MESSAGE_KIND,
70
+ } from "@vellumai/plugin-api";
68
71
 
69
72
  import type { AssistantEvent } from "../api/index.js";
73
+ import { ESCALATION_CONTINUATION_CONTENT } from "../calls/voice-triage-escalate.js";
70
74
  import type { AssistantConfig } from "../config/schema.js";
71
75
  import type { Conversation } from "../daemon/conversation.js";
72
76
  import type { QdrantSparseVector } from "../persistence/embeddings/qdrant-client.js";
73
77
  import type { ConversationGraphMemory } from "../plugins/defaults/memory/graph/conversation-graph-memory.js";
74
78
  import userPromptSubmitMemoryRetrieval from "../plugins/defaults/memory/hooks/user-prompt-submit.js";
75
79
  import type { Message } from "../providers/types.js";
80
+ import { asConversation } from "./helpers/mock-conversation.js";
81
+ import { setConfig } from "./helpers/set-config.js";
76
82
 
77
83
  /** Canonical metrics payload the graph retriever attaches to a real hit. */
78
84
  function makeMetrics() {
@@ -172,15 +178,16 @@ function installConversation(
172
178
  graphMemory: ConversationGraphMemory,
173
179
  opts?: {
174
180
  trusted?: boolean;
175
- signal?: AbortSignal;
181
+ abortController?: AbortController;
176
182
  },
177
- ): void {
183
+ ): Conversation {
178
184
  currentTrustClass = opts?.trusted === false ? "unknown" : "guardian";
179
- currentConversation = {
185
+ currentConversation = asConversation({
180
186
  graphMemory,
181
187
  trustContext: undefined,
182
- abortController: { signal: opts?.signal ?? new AbortController().signal },
183
- } as unknown as Conversation;
188
+ abortController: opts?.abortController ?? new AbortController(),
189
+ });
190
+ return currentConversation;
184
191
  }
185
192
 
186
193
  beforeEach(() => {
@@ -191,9 +198,95 @@ beforeEach(() => {
191
198
  broadcastMessageMock.mockReset();
192
199
  currentConversation = undefined;
193
200
  currentTrustClass = "guardian";
201
+ setConfig("memory", { enabled: true, v3: { live: false } });
194
202
  });
195
203
 
196
204
  describe("user-prompt-submit hook (memory retrieval)", () => {
205
+ test("voice front door skips serial legacy retrieval", async () => {
206
+ const { memory, prepareMemoryMock, recordPkbQueryVectorsMock } =
207
+ makeFakeGraphMemory();
208
+ const conversation = installConversation(memory, { trusted: true });
209
+ conversation.currentCallSite = "voiceFrontDoor";
210
+ const ctx = makeHookCtx({ conversationId: "conv-voice" });
211
+
212
+ await userPromptSubmitMemoryRetrieval(ctx);
213
+
214
+ expect(prepareMemoryMock).not.toHaveBeenCalled();
215
+ expect(recordPkbQueryVectorsMock).toHaveBeenCalledWith(
216
+ undefined,
217
+ undefined,
218
+ );
219
+ expect(applyRuntimeInjectionsMock).toHaveBeenCalledTimes(1);
220
+ });
221
+
222
+ test("voice escalation continuation routes legacy retrieval on the preceding user message", async () => {
223
+ const { memory, prepareMemoryMock } = makeFakeGraphMemory();
224
+ installConversation(memory, { trusted: true });
225
+ const latestMessages: Message[] = [
226
+ {
227
+ role: "user",
228
+ content: [
229
+ { type: "text", text: "<info>\ncarried context\n</info>" },
230
+ {
231
+ type: "text",
232
+ text: "<turn_context>\nturn context\n</turn_context>",
233
+ },
234
+ { type: "text", text: "What is my preferred editor?" },
235
+ ],
236
+ },
237
+ {
238
+ role: "assistant",
239
+ content: [{ type: "text", text: "Let me check that." }],
240
+ },
241
+ {
242
+ role: "user",
243
+ content: [{ type: "text", text: "Continue the answer." }],
244
+ },
245
+ ];
246
+ const ctx = makeHookCtx({
247
+ latestMessages,
248
+ isHiddenPrompt: true,
249
+ prompt: ESCALATION_CONTINUATION_CONTENT,
250
+ messageKind: VOICE_ESCALATION_CONTINUATION_MESSAGE_KIND,
251
+ });
252
+
253
+ await userPromptSubmitMemoryRetrieval(ctx);
254
+
255
+ expect(prepareMemoryMock).toHaveBeenCalledTimes(1);
256
+ expect(prepareMemoryMock.mock.calls[0]?.[0]).toBe(latestMessages);
257
+ expect(prepareMemoryMock.mock.calls[0]?.[4]).toEqual([
258
+ {
259
+ role: "user",
260
+ content: [{ type: "text", text: "What is my preferred editor?" }],
261
+ },
262
+ ]);
263
+ });
264
+
265
+ test("generic hidden prompts route legacy retrieval on their own content", async () => {
266
+ const { memory, prepareMemoryMock } = makeFakeGraphMemory();
267
+ installConversation(memory, { trusted: true });
268
+ const latestMessages: Message[] = [
269
+ {
270
+ role: "user",
271
+ content: [{ type: "text", text: "Visible caller message" }],
272
+ },
273
+ {
274
+ role: "user",
275
+ content: [{ type: "text", text: "Hidden prompt content" }],
276
+ },
277
+ ];
278
+ const ctx = makeHookCtx({
279
+ latestMessages,
280
+ isHiddenPrompt: true,
281
+ prompt: "Hidden prompt content",
282
+ });
283
+
284
+ await userPromptSubmitMemoryRetrieval(ctx);
285
+
286
+ expect(prepareMemoryMock).toHaveBeenCalledTimes(1);
287
+ expect(prepareMemoryMock.mock.calls[0]?.[4]).toBe(latestMessages);
288
+ });
289
+
197
290
  test("adopts the injected run messages when the actor is trusted", async () => {
198
291
  const injected: Message[] = [
199
292
  { role: "user", content: [{ type: "text", text: "injected" }] },
@@ -492,7 +585,7 @@ describe("user-prompt-submit hook (memory retrieval)", () => {
492
585
  const controller = new AbortController();
493
586
  installConversation(graphMemory, {
494
587
  trusted: true,
495
- signal: controller.signal,
588
+ abortController: controller,
496
589
  });
497
590
  const ctx = makeHookCtx();
498
591