@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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellumai/assistant",
3
- "version": "0.11.3",
3
+ "version": "0.11.4-dev.202608190019.b94dbf2",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -8,7 +8,7 @@
8
8
  * script's `full` variants and the live session's `llmFirstDeltaMs`).
9
9
  *
10
10
  * Variants:
11
- * slim-haiku voiceFrontDecision call site (Haiku pin), slim prompt
11
+ * slim-front voiceFrontDoor call site, slim prompt
12
12
  * slim-flash cost-optimized profile, same slim prompt
13
13
  * full-notools cost-optimized, real buildSystemPrompt(), no tools
14
14
  * full-flash cost-optimized, real buildSystemPrompt() + full tool defs
@@ -170,8 +170,8 @@ async function main(): Promise<void> {
170
170
 
171
171
  const variants: VariantSpec[] = [
172
172
  {
173
- name: "slim-haiku",
174
- callSite: "voiceFrontDecision",
173
+ name: "slim-front",
174
+ callSite: "voiceFrontDoor",
175
175
  systemPrompt: () => SLIM_SYSTEM_PROMPT,
176
176
  },
177
177
  {
@@ -0,0 +1,10 @@
1
+ import path from "node:path";
2
+
3
+ import { ensurePluginApiShim } from "../src/plugins/ensure-plugin-api-shim.js";
4
+
5
+ const outputDir = Bun.argv[2];
6
+ if (!outputDir || !path.isAbsolute(outputDir)) {
7
+ throw new Error("An absolute plugin API shim output directory is required.");
8
+ }
9
+
10
+ await ensurePluginApiShim({ workspaceDir: outputDir });
@@ -20,6 +20,7 @@ import {
20
20
  packageName,
21
21
  resolvePackage,
22
22
  } from "../bundler/package-resolver.js";
23
+ import { ensureBun } from "../util/bun-runtime.js";
23
24
 
24
25
  // ---------------------------------------------------------------------------
25
26
  // Shared temp directory
@@ -56,6 +57,29 @@ async function scaffold(
56
57
  return appDir;
57
58
  }
58
59
 
60
+ /**
61
+ * Download `pkg` into bun's global module cache via a throwaway install.
62
+ *
63
+ * `resolvePackage` bounds its own install at INSTALL_TIMEOUT_MS (10s), which a
64
+ * cold global cache can exceed for a large package on a slow network. Priming
65
+ * first is unbounded, so the install the test measures resolves from cache and
66
+ * stays well inside that bound.
67
+ */
68
+ async function primeBunCache(pkg: string): Promise<void> {
69
+ const primeDir = join(tempDir, `prime-${pkg.replaceAll(/[^a-z0-9]/gi, "-")}`);
70
+ await mkdir(primeDir, { recursive: true });
71
+ await writeFile(
72
+ join(primeDir, "package.json"),
73
+ JSON.stringify({ name: "prime-cache", private: true }),
74
+ );
75
+ const proc = Bun.spawn([await ensureBun(), "install", "--no-save", pkg], {
76
+ cwd: primeDir,
77
+ stdout: "ignore",
78
+ stderr: "ignore",
79
+ });
80
+ await proc.exited;
81
+ }
82
+
59
83
  const MINIMAL_HTML = `<!DOCTYPE html>
60
84
  <html>
61
85
  <head><title>Test</title></head>
@@ -466,10 +490,21 @@ describe("package-resolver", () => {
466
490
  expect(ALLOWED_PACKAGES).toContain("clsx");
467
491
  });
468
492
 
469
- // `lucide` (the vanilla package) unpacks to ~29 MB, far above the resolver's
470
- // size cap, so it is installed then deleted and never resolves. It also
471
- // exports icon data arrays, not Preact components. Apps use inline SVG.
493
+ // `lucide` (the vanilla package) exports icon data arrays, not Preact
494
+ // components, and unpacks to ~29 MB for no benefit. Apps use inline SVG.
472
495
  test("ALLOWED_PACKAGES excludes lucide", () => {
473
496
  expect(ALLOWED_PACKAGES).not.toContain("lucide");
474
497
  });
498
+
499
+ // `chart.js` is a large allowlisted package: it must install into the shared
500
+ // cache and resolve so apps importing it can compile.
501
+ test("resolvePackage installs a large allowlisted package", async () => {
502
+ await primeBunCache("chart.js");
503
+
504
+ const result = await resolvePackage("chart.js");
505
+ expect(result).not.toBeNull();
506
+ expect(existsSync(join(getCacheDir(), "node_modules", "chart.js"))).toBe(
507
+ true,
508
+ );
509
+ }, 60_000);
475
510
  });
@@ -140,6 +140,7 @@ function buildContext(
140
140
  guardianPrincipalId: "user-1",
141
141
  },
142
142
  sendToClient: () => {},
143
+ emit: () => {},
143
144
  pendingSurfaceActions: new Map(),
144
145
  lastSurfaceAction: new Map(),
145
146
  surfaceState: new Map(),
@@ -113,7 +113,7 @@ function makeIdleSession(opts?: {
113
113
  ensureActorScopedHistory: async () => {},
114
114
  getMessages: () => [],
115
115
  usageStats: { inputTokens: 0, outputTokens: 0, estimatedCost: 0 },
116
- updateClient: () => {},
116
+ replayActivityState: () => {},
117
117
  setHostBrowserProxy: () => {},
118
118
  setHostCuProxy: () => {},
119
119
  setHostAppControlProxy: () => {},
@@ -176,7 +176,7 @@ function makeConfirmationEmittingSession(opts?: {
176
176
  ensureActorScopedHistory: async () => {},
177
177
  getMessages: () => [],
178
178
  usageStats: { inputTokens: 0, outputTokens: 0, estimatedCost: 0 },
179
- updateClient: () => {},
179
+ replayActivityState: () => {},
180
180
  setHostBrowserProxy: () => {},
181
181
  setHostCuProxy: () => {},
182
182
  setHostAppControlProxy: () => {},
@@ -40,7 +40,10 @@ interface RegistryFlag {
40
40
  key: string;
41
41
  label: string;
42
42
  description: string;
43
- defaultEnabled: boolean;
43
+ /** Boolean for a gate; the winning variation's name for a multivariate flag. */
44
+ defaultEnabled: boolean | string;
45
+ /** Declared variations. Present only on multivariate flags. */
46
+ values?: string[];
44
47
  }
45
48
 
46
49
  interface Registry {
@@ -179,8 +182,27 @@ describe("assistant feature flag guard", () => {
179
182
  const violations: string[] = [];
180
183
 
181
184
  for (const flag of assistantFlags) {
182
- if (typeof flag.defaultEnabled !== "boolean") {
183
- violations.push(`${flag.key}: missing or non-boolean 'defaultEnabled'`);
185
+ // A gate declares a boolean default. A multivariate flag declares its
186
+ // variations in `values` and names the winning one in `defaultEnabled`,
187
+ // which the resolver serves verbatim to string consumers.
188
+ if (flag.values === undefined) {
189
+ if (typeof flag.defaultEnabled !== "boolean") {
190
+ violations.push(
191
+ `${flag.key}: missing or non-boolean 'defaultEnabled'`,
192
+ );
193
+ }
194
+ } else if (
195
+ !Array.isArray(flag.values) ||
196
+ flag.values.length < 2 ||
197
+ flag.values.some((value) => typeof value !== "string")
198
+ ) {
199
+ violations.push(
200
+ `${flag.key}: 'values' must list at least two string variations`,
201
+ );
202
+ } else if (!flag.values.includes(flag.defaultEnabled as string)) {
203
+ violations.push(
204
+ `${flag.key}: 'defaultEnabled' must name one of the declared 'values'`,
205
+ );
184
206
  }
185
207
  if (
186
208
  typeof flag.description !== "string" ||
@@ -722,12 +722,6 @@ describe("validateAttachmentUpload", () => {
722
722
  expect(exeResult.error).toContain(".exe");
723
723
  }
724
724
 
725
- const shResult = validateAttachmentUpload("script.sh", "text/plain");
726
- expect(shResult.ok).toBe(false);
727
- if (!shResult.ok) {
728
- expect(shResult.error).toContain(".sh");
729
- }
730
-
731
725
  const isoResult = validateAttachmentUpload(
732
726
  "disk.iso",
733
727
  "application/octet-stream",
@@ -738,6 +732,24 @@ describe("validateAttachmentUpload", () => {
738
732
  }
739
733
  });
740
734
 
735
+ test("accepts shell scripts as text attachments", () => {
736
+ expect(validateAttachmentUpload("script.sh", "text/plain").ok).toBe(true);
737
+ expect(validateAttachmentUpload("script.SH", "text/plain").ok).toBe(true);
738
+ expect(
739
+ validateAttachmentUpload("setup.sh", "application/x-sh").ok,
740
+ ).toBe(true);
741
+ expect(
742
+ validateAttachmentUpload("run.sh", "application/x-shellscript").ok,
743
+ ).toBe(true);
744
+ expect(validateAttachmentUpload("run.sh", "text/x-sh").ok).toBe(true);
745
+ expect(validateAttachmentUpload("run.sh", "text/x-shellscript").ok).toBe(
746
+ true,
747
+ );
748
+ expect(
749
+ validateAttachmentUpload("run.sh", "application/octet-stream").ok,
750
+ ).toBe(true);
751
+ });
752
+
741
753
  test("rejects dangerous extensions regardless of claimed MIME type", () => {
742
754
  // .exe disguised as image/png
743
755
  const result = validateAttachmentUpload("payload.exe", "image/png");
@@ -748,7 +760,9 @@ describe("validateAttachmentUpload", () => {
748
760
  expect(
749
761
  validateAttachmentUpload("PROGRAM.EXE", "application/octet-stream").ok,
750
762
  ).toBe(false);
751
- expect(validateAttachmentUpload("script.SH", "text/plain").ok).toBe(false);
763
+ expect(validateAttachmentUpload("INSTALL.DMG", "application/octet-stream").ok).toBe(
764
+ false,
765
+ );
752
766
  });
753
767
 
754
768
  test("rejects unsupported MIME types", () => {
@@ -815,11 +829,6 @@ describe("validateAttachmentUpload", () => {
815
829
  trustedSource: true,
816
830
  }).ok,
817
831
  ).toBe(true);
818
- expect(
819
- validateAttachmentUpload("build.sh", "text/plain", {
820
- trustedSource: true,
821
- }).ok,
822
- ).toBe(true);
823
832
  });
824
833
 
825
834
  test("trustedSource: false keeps strict validation", () => {
@@ -278,13 +278,30 @@ describe("ensureByokDefaultProfiles", () => {
278
278
  ["openrouter", "quality-optimized", "anthropic/claude-opus-4.8"],
279
279
  ["gemini", "cost-optimized", "gemini-3.1-flash-lite-preview"],
280
280
  ["openai", "cost-optimized", "gpt-5.4-nano"],
281
+ ["openai", "balanced", "gpt-5.4-mini"],
282
+ ["openai", "quality-optimized", "gpt-5.4"],
281
283
  ["fireworks", "balanced", "accounts/fireworks/models/kimi-k2p5"],
282
284
  ["fireworks", "balanced", "accounts/fireworks/models/kimi-k2p6"],
285
+ [
286
+ "fireworks",
287
+ "balanced",
288
+ "accounts/fireworks/models/deepseek-v4-flash-0731",
289
+ ],
283
290
  [
284
291
  "fireworks",
285
292
  "quality-optimized",
286
293
  "accounts/fireworks/models/deepseek-v4-flash",
287
294
  ],
295
+ [
296
+ "fireworks",
297
+ "quality-optimized",
298
+ "accounts/fireworks/models/deepseek-v4-flash-0731",
299
+ ],
300
+ [
301
+ "fireworks",
302
+ "cost-optimized",
303
+ "accounts/fireworks/models/deepseek-v4-flash",
304
+ ],
288
305
  ] as const)(
289
306
  "a historical intent-era copy converts (%s %s pinned to %s)",
290
307
  (provider, key, model) => {
@@ -360,7 +360,6 @@ describe("CallSetupFlow name capture", () => {
360
360
  ).toEqual({ from: PHONE_NUMBER, callerName: CALLER_NAME });
361
361
  expect(f.notifyCalls).toEqual([
362
362
  {
363
- canonicalAssistantId: "self",
364
363
  sourceChannel: "phone",
365
364
  conversationExternalId: PHONE_NUMBER,
366
365
  actorExternalId: PHONE_NUMBER,
@@ -67,7 +67,7 @@ function makeConnectionHook(
67
67
  byConnection: Record<string, Provider>,
68
68
  ): (
69
69
  connectionName: string,
70
- expectedProvider: string,
70
+ expectedProvider: string | undefined,
71
71
  ) => Promise<Provider | null> {
72
72
  return async (connectionName, _expectedProvider) =>
73
73
  byConnection[connectionName] ?? null;
@@ -57,6 +57,8 @@ const handler = ROUTES[0]!.handler;
57
57
 
58
58
  interface ChannelEntry {
59
59
  id: string;
60
+ /** `default` here, `plugin:<name>` for a channel a plugin brings. */
61
+ source: string;
60
62
  label: string;
61
63
  subtitle: string;
62
64
  icon: string;
@@ -173,10 +175,21 @@ describe("channels/available", () => {
173
175
  for (const channel of result.channels) {
174
176
  const expected = (CHANNEL_METADATA as any)[channel.id];
175
177
  expect(expected).toBeDefined();
176
- expect(channel).toEqual(expected);
178
+ // Plus `source`, which the metadata does not carry: it says who
179
+ // contributes the row, and every row here comes from the assistant.
180
+ expect(channel).toEqual({ ...expected, source: "default" });
177
181
  }
178
182
  });
179
183
 
184
+ test("marks every channel the assistant ships as its own", async () => {
185
+ // The base list is what a client compares a plugin-contributed row
186
+ // against, so the marker has to be present rather than implied by an
187
+ // absent field.
188
+ const result = (await handler({})) as HandlerResult;
189
+
190
+ expect(result.channels.every((c) => c.source === "default")).toBe(true);
191
+ });
192
+
180
193
  test("email metadata: not verification-capable, mail icon", async () => {
181
194
  mockEmailAddressesResponse = {
182
195
  ok: true,
@@ -0,0 +1,214 @@
1
+ /**
2
+ * `<channel_capabilities>` must not accumulate one copy per turn, and
3
+ * deduplicating it must not disturb the cross-turn byte-identity that prompt
4
+ * caching depends on (see `prompt-cache-cross-turn-stability.test.ts`, which
5
+ * owns that invariant end to end).
6
+ */
7
+ import { describe, expect, test } from "bun:test";
8
+
9
+ import {
10
+ dedupeChannelCapabilityBlocks,
11
+ preModelCallSanitize,
12
+ } from "../context/outbound-sanitize.js";
13
+ import type { Message } from "../providers/types.js";
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // Fixtures, shaped like what runtime injection actually produces: the block is
17
+ // its OWN text content block, prepended to the turn-starting user message by
18
+ // `injectChannelCapabilityContext`, and then frozen into history.
19
+ // ---------------------------------------------------------------------------
20
+
21
+ const slackCapabilities = (dynamicUi = false): string =>
22
+ [
23
+ "<channel_capabilities>",
24
+ "channel: slack",
25
+ "dashboard_capable: false",
26
+ `supports_dynamic_ui: ${dynamicUi}`,
27
+ "supports_voice_input: false",
28
+ "",
29
+ "CHANNEL CONSTRAINTS:",
30
+ "- Do NOT reference the dashboard UI, settings panels, or visual preference pickers.",
31
+ '- Do NOT use app_create. Only use ui_show/ui_update for card surfaces with template: "task_progress"; present all other information as text.',
32
+ "- Present information as well-formatted text instead of dynamic UI.",
33
+ "</channel_capabilities>",
34
+ ].join("\n");
35
+
36
+ const CAPS = slackCapabilities();
37
+
38
+ const turnContextBlock = (turn: number): string =>
39
+ [
40
+ "<turn_context>",
41
+ `current_time: 2026-05-21 (Thursday) 10:${String(turn).padStart(2, "0")}:00 -05:00 (America/Chicago)`,
42
+ "interface: slack",
43
+ "trust_class: guardian",
44
+ "</turn_context>",
45
+ ].join("\n");
46
+
47
+ const userTurn = (turn: number, caps: string = CAPS): Message => ({
48
+ role: "user",
49
+ content: [
50
+ { type: "text", text: turnContextBlock(turn) },
51
+ { type: "text", text: caps },
52
+ { type: "text", text: `User message ${turn}.` },
53
+ ],
54
+ });
55
+
56
+ const assistantTurn = (turn: number): Message => ({
57
+ role: "assistant",
58
+ content: [{ type: "text", text: `Reply ${turn}.` }],
59
+ });
60
+
61
+ /** An N-turn conversation, every user row carrying its own injected copy. */
62
+ const conversation = (turns: number): Message[] => {
63
+ const messages: Message[] = [];
64
+ for (let turn = 1; turn <= turns; turn++) {
65
+ messages.push(userTurn(turn));
66
+ if (turn < turns) {
67
+ messages.push(assistantTurn(turn));
68
+ }
69
+ }
70
+ return messages;
71
+ };
72
+
73
+ const allText = (messages: Message[]): string =>
74
+ messages
75
+ .flatMap((m) =>
76
+ m.content.filter((b) => b.type === "text").map((b) => b.text),
77
+ )
78
+ .join("\n");
79
+
80
+ const countOccurrences = (haystack: string, needle: string): number =>
81
+ haystack.split(needle).length - 1;
82
+
83
+ // ---------------------------------------------------------------------------
84
+
85
+ describe("<channel_capabilities> does not accumulate across a conversation", () => {
86
+ test("a 50-turn conversation carries exactly one copy", () => {
87
+ const raw = conversation(50);
88
+ expect(countOccurrences(allText(raw), "<channel_capabilities>")).toBe(50);
89
+
90
+ const text = allText(preModelCallSanitize(raw));
91
+ expect(countOccurrences(text, "<channel_capabilities>")).toBe(1);
92
+ expect(countOccurrences(text, "CHANNEL CONSTRAINTS:")).toBe(1);
93
+ });
94
+
95
+ test("the surviving copy is the first one, unmodified", () => {
96
+ const sanitized = preModelCallSanitize(conversation(6));
97
+ expect(sanitized[0].content).toEqual(conversation(6)[0].content);
98
+ expect(allText([sanitized[2]])).not.toContain("<channel_capabilities>");
99
+ });
100
+
101
+ test("outbound size stops growing with the repeated block", () => {
102
+ const rawGrowth =
103
+ allText(conversation(45)).length - allText(conversation(5)).length;
104
+ const sanitizedGrowth =
105
+ allText(preModelCallSanitize(conversation(45))).length -
106
+ allText(preModelCallSanitize(conversation(5))).length;
107
+
108
+ // Every extra raw turn pays for a full copy of the block; the deduplicated
109
+ // projection pays nothing for it.
110
+ expect((rawGrowth - sanitizedGrowth) / 40).toBeGreaterThanOrEqual(
111
+ CAPS.length,
112
+ );
113
+ });
114
+
115
+ test("everything else on the row is untouched", () => {
116
+ const sanitized = preModelCallSanitize(conversation(4));
117
+ // `<turn_context>` is deliberately left alone: it carries a fresh
118
+ // timestamp each turn and historical copies are read back by the
119
+ // compactor's tail resolution.
120
+ expect(countOccurrences(allText(sanitized), "<turn_context>")).toBe(4);
121
+ // The dropped block is the only thing that goes: the row keeps its
122
+ // `<turn_context>` and the user's own text, in order.
123
+ expect(sanitized[6].content).toEqual([
124
+ { type: "text", text: turnContextBlock(4) },
125
+ { type: "text", text: "User message 4." },
126
+ ]);
127
+ });
128
+ });
129
+
130
+ describe("cross-turn byte stability", () => {
131
+ test("a message renders identically no matter how many turns follow it", () => {
132
+ // The prompt cache is only readable when turn N's messages recur
133
+ // byte-identically at the same index in turn N+1. Deduplication must
134
+ // therefore depend only on what sits ABOVE a message, never on how far it
135
+ // is from the end.
136
+ const turn2 = preModelCallSanitize(conversation(2));
137
+ const turn20 = preModelCallSanitize(conversation(20));
138
+
139
+ turn2.forEach((message, index) => {
140
+ expect(JSON.stringify(turn20[index])).toBe(JSON.stringify(message));
141
+ });
142
+ });
143
+
144
+ test("the turn-starting message is not treated specially", () => {
145
+ const sanitized = preModelCallSanitize(conversation(3));
146
+ const turnStart = sanitized[sanitized.length - 1];
147
+ // Turn 3 opened this turn and still drops its duplicate copy, which is
148
+ // exactly what keeps it stable once turn 4 arrives.
149
+ expect(allText([turnStart])).not.toContain("<channel_capabilities>");
150
+ });
151
+
152
+ test("is idempotent", () => {
153
+ const once = preModelCallSanitize(conversation(6));
154
+ expect(preModelCallSanitize(once)).toEqual(once);
155
+ });
156
+ });
157
+
158
+ describe("changes and safety", () => {
159
+ test("capabilities that actually change mid-conversation are kept", () => {
160
+ const changed = slackCapabilities(true);
161
+ const history: Message[] = [
162
+ userTurn(1),
163
+ assistantTurn(1),
164
+ userTurn(2),
165
+ assistantTurn(2),
166
+ userTurn(3, changed),
167
+ assistantTurn(3),
168
+ userTurn(4, changed),
169
+ ];
170
+
171
+ const sanitized = dedupeChannelCapabilityBlocks(history);
172
+ const text = allText(sanitized);
173
+ expect(countOccurrences(text, "<channel_capabilities>")).toBe(2);
174
+ expect(text).toContain("supports_dynamic_ui: false");
175
+ expect(text).toContain("supports_dynamic_ui: true");
176
+ // Turn 2 and turn 4 are the repeats and are the ones that go.
177
+ expect(allText([sanitized[2]])).not.toContain("<channel_capabilities>");
178
+ expect(allText([sanitized[6]])).not.toContain("<channel_capabilities>");
179
+ });
180
+
181
+ test("a conversation with a single copy is returned untouched", () => {
182
+ const history = conversation(1);
183
+ expect(dedupeChannelCapabilityBlocks(history)).toBe(history);
184
+ });
185
+
186
+ test("user-authored text that merely mentions the tag is left alone", () => {
187
+ const authored =
188
+ "why does <channel_capabilities> say dashboard_capable: false?";
189
+ const history: Message[] = [
190
+ { role: "user", content: [{ type: "text", text: authored }] },
191
+ assistantTurn(1),
192
+ { role: "user", content: [{ type: "text", text: authored }] },
193
+ ];
194
+
195
+ expect(dedupeChannelCapabilityBlocks(history)).toBe(history);
196
+ });
197
+
198
+ test("a user row made only of the block is never emptied or dropped", () => {
199
+ const history: Message[] = [
200
+ userTurn(1),
201
+ assistantTurn(1),
202
+ { role: "user", content: [{ type: "text", text: CAPS }] },
203
+ assistantTurn(2),
204
+ userTurn(3),
205
+ ];
206
+
207
+ const sanitized = dedupeChannelCapabilityBlocks(history);
208
+ expect(sanitized).toHaveLength(5);
209
+ expect(sanitized[2].content).toEqual(history[2].content);
210
+ // The all-block row is retained, so it becomes the baseline the later
211
+ // duplicate is measured against and turn 3 still drops.
212
+ expect(allText([sanitized[4]])).not.toContain("<channel_capabilities>");
213
+ });
214
+ });
@@ -504,23 +504,23 @@ describe("channel-delivery-store", () => {
504
504
  expect(r1.conversationId).not.toBe(r2.conversationId);
505
505
  });
506
506
 
507
- test("same chat/channel but different assistantId uses different conversations", () => {
508
- const r1 = recordInbound("telegram", "chat-1", "msg-1", {
509
- assistantId: "asst-A",
510
- });
511
- const r2 = recordInbound("telegram", "chat-1", "msg-2", {
512
- assistantId: "asst-B",
513
- });
507
+ test("the same chat keeps the same conversation", () => {
508
+ const r1 = recordInbound("telegram", "chat-1", "msg-1");
509
+ const r2 = recordInbound("telegram", "chat-1", "msg-2");
514
510
 
515
- expect(r1.conversationId).not.toBe(r2.conversationId);
511
+ expect(r1.conversationId).toBe(r2.conversationId);
516
512
  });
517
513
 
518
- test("no assistantId defaults to self-scoped key", () => {
519
- const r1 = recordInbound("telegram", "chat-1", "msg-1");
520
- const r2 = recordInbound("telegram", "chat-1", "msg-2", {
521
- assistantId: "self",
522
- });
523
- expect(r1.conversationId).toBe(r2.conversationId);
514
+ test("binds under the self-scoped key", () => {
515
+ // The prefix is a stored format, not an internal detail. It reads like a
516
+ // leftover of the assistant id that used to be interpolated into it, so
517
+ // pin it: rows already written under `asst:self:` stop resolving if a
518
+ // later tidy-up drops it.
519
+ const { conversationId } = recordInbound("telegram", "chat-1", "msg-1");
520
+
521
+ expect(
522
+ getConversationByKey("asst:self:telegram:chat-1")?.conversationId,
523
+ ).toBe(conversationId);
524
524
  });
525
525
 
526
526
  test("external bindings allow multiple Slack thread anchors per channel", () => {
@@ -250,7 +250,7 @@ describe("channel inbound disk pressure gate", () => {
250
250
 
251
251
  expect(body).toMatchObject({
252
252
  accepted: true,
253
- duplicate: false,
253
+ reaction: "dropped_disk_pressure",
254
254
  diskPressure: "blocked",
255
255
  reason: "trusted-contact",
256
256
  });
@@ -268,9 +268,9 @@ describe("channel inbound disk pressure gate", () => {
268
268
  eq(channelInboundEvents.externalMessageId, "slack-reaction-blocked"),
269
269
  )
270
270
  .get();
271
- expect(event?.processingStatus).toBe("processed");
272
- expect(event?.messageId).toBeNull();
273
- expect(event?.rawPayload).toBeNull();
271
+ // A blocked reaction writes nothing at all: it is dropped before the
272
+ // reacted message is resolved, so no event row is opened either.
273
+ expect(event).toBeUndefined();
274
274
  expect(db.select().from(messages).all()).toHaveLength(0);
275
275
  });
276
276
 
@@ -28,7 +28,7 @@ function makeContext(
28
28
  ): Conversation {
29
29
  return asConversation({
30
30
  conversationId: "session-1",
31
- sendToClient: (msg) => sent.push(msg),
31
+ emit: (msg) => sent.push(msg),
32
32
  pendingSurfaceActions: new Map<string, { surfaceType: SurfaceType }>(),
33
33
  lastSurfaceAction: new Map<
34
34
  string,