@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
@@ -9,7 +9,9 @@
9
9
  * or an access-request notification (LUM-2489),
10
10
  * - a stranger's reaction creates no conversation, binding, or transcript
11
11
  * row — it is dropped as channel noise,
12
- * - a known contact's reaction is recorded as an inline transcript signal,
12
+ * - a known contact's reaction is recorded as an inline transcript signal in
13
+ * the conversation of the message it was attached to, whether that message
14
+ * arrived from the channel or the assistant posted it,
13
15
  * - a guardian's reaction on an approval card is routed through the guardian
14
16
  * decision pipeline (the same path as buttons and text replies).
15
17
  *
@@ -28,14 +30,14 @@ import {
28
30
  } from "../../../messaging/providers/slack/message-metadata.js";
29
31
  import { addMessage } from "../../../persistence/conversation-crud.js";
30
32
  import {
31
- clearPayload,
33
+ findInboundEvent,
34
+ findMessageBySourceId,
35
+ findSlackConversationByMessageTs,
32
36
  linkMessage,
33
37
  recordInbound,
34
38
  } from "../../../persistence/delivery-crud.js";
35
39
  import { markProcessed } from "../../../persistence/delivery-status.js";
36
- import { upsertBinding } from "../../../persistence/external-conversation-store.js";
37
40
  import { getLogger } from "../../../util/logger.js";
38
- import { DAEMON_INTERNAL_ASSISTANT_ID } from "../../assistant-scope.js";
39
41
  import type { ApprovalConversationGenerator } from "../../http-types.js";
40
42
  import {
41
43
  actorTrustContextFromVerdict,
@@ -100,15 +102,12 @@ export interface ReactionInterceptParams {
100
102
  sourceInterface: InterfaceId | undefined;
101
103
  conversationExternalId: string;
102
104
  externalMessageId: string;
103
- canonicalAssistantId: string;
104
105
  rawSenderId: string | undefined;
105
106
  canonicalSenderId: string | null;
106
107
  actorDisplayName: string | undefined;
107
108
  actorUsername: string | undefined;
108
109
  replyCallbackUrl: string | undefined;
109
110
  sourceMetadata: SourceMetadata | undefined;
110
- /** Slack channel display name, for the conversation binding. */
111
- slackChannelName: string | null;
112
111
  approvalConversationGenerator: ApprovalConversationGenerator | undefined;
113
112
  }
114
113
 
@@ -126,14 +125,12 @@ export async function handleSlackReactionIntercept(
126
125
  sourceInterface,
127
126
  conversationExternalId,
128
127
  externalMessageId,
129
- canonicalAssistantId,
130
128
  rawSenderId,
131
129
  canonicalSenderId,
132
130
  actorDisplayName,
133
131
  actorUsername,
134
132
  replyCallbackUrl,
135
133
  sourceMetadata,
136
- slackChannelName,
137
134
  approvalConversationGenerator,
138
135
  } = params;
139
136
 
@@ -168,28 +165,11 @@ export async function handleSlackReactionIntercept(
168
165
  typeof sourceMetadata?.messageId === "string"
169
166
  ? sourceMetadata.messageId
170
167
  : undefined;
171
- const threadTs =
172
- typeof sourceMetadata?.threadId === "string" &&
173
- sourceMetadata.threadId.trim().length > 0
174
- ? sourceMetadata.threadId.trim()
175
- : undefined;
176
-
177
- // Record for dedup + conversation resolution (known contacts only — strangers
178
- // were dropped above).
179
- const result = recordInbound(
180
- sourceChannel,
181
- conversationExternalId,
182
- externalMessageId,
183
- {
184
- sourceMessageId: reactedMessageTs,
185
- assistantId: canonicalAssistantId,
186
- sourceThreadId: threadTs,
187
- },
188
- );
189
-
190
168
  // Respect disk-pressure cleanup so reactions don't bypass storage
191
169
  // protection. Guardians resolve to `allow-cleanup-mode` (not `block`), so a
192
- // guardian's approval-by-reaction still flows.
170
+ // guardian's approval-by-reaction still flows. Blocked silently and before
171
+ // any write: a reaction is a passive signal, so the message pipeline's
172
+ // "storage is low, try again" notice is meaningless for an emoji.
193
173
  const diskPressure = classifyDiskPressureTurnPolicy(getDiskPressureStatus(), {
194
174
  sourceChannel,
195
175
  sourceInterface,
@@ -199,86 +179,97 @@ export async function handleSlackReactionIntercept(
199
179
  },
200
180
  });
201
181
  if (diskPressure.action === "block") {
202
- // Block silently: a reaction is a passive signal, so the message
203
- // pipeline's "storage is low, try again" notice is meaningless for an
204
- // emoji — there is nothing to retry. Mark the event processed and stop
205
- // before binding/persistence.
206
- if (!result.duplicate) {
207
- clearPayload(result.eventId);
208
- markProcessed(result.eventId);
209
- }
210
182
  return {
211
183
  accepted: true,
212
- duplicate: result.duplicate,
213
- eventId: result.eventId,
184
+ reaction: "dropped_disk_pressure",
214
185
  diskPressure: "blocked",
215
186
  reason: diskPressure.reason,
216
187
  };
217
188
  }
218
189
 
219
- // Maintain the conversation binding, matching the message pipeline. Scoped to
220
- // the daemon's own assistant so assistant-scoped legacy routes don't clobber
221
- // each other's binding metadata.
222
- if (canonicalAssistantId === DAEMON_INTERNAL_ASSISTANT_ID) {
223
- upsertBinding({
224
- conversationId: result.conversationId,
225
- sourceChannel,
226
- externalChatId: conversationExternalId,
227
- externalChatName: slackChannelName,
228
- externalThreadId: threadTs ?? null,
229
- externalUserId: canonicalSenderId ?? rawSenderId ?? null,
230
- displayName: actorDisplayName ?? null,
231
- username: actorUsername ?? null,
232
- });
190
+ // A redelivery is answered before anything acts on it. Recording the event
191
+ // is what dedups a reaction, and that happens after the guardian rail, so
192
+ // without this probe the same emoji could drive a decision twice.
193
+ const alreadyRecorded = findInboundEvent(
194
+ sourceChannel,
195
+ conversationExternalId,
196
+ externalMessageId,
197
+ );
198
+ if (alreadyRecorded) {
199
+ return {
200
+ accepted: true,
201
+ duplicate: true,
202
+ eventId: alreadyRecorded.eventId,
203
+ };
233
204
  }
234
205
 
235
- // Guardian approval-by-reaction → guardian decision pipeline, exactly like
236
- // buttons and text replies. Only `reaction:` (added) expresses intent;
206
+ // Guardian approval-by-reaction runs before the reacted message is resolved:
207
+ // an approval card is addressed by its own message id and is not always a
208
+ // stored message. Only `reaction:` (added) expresses intent;
237
209
  // `reaction_removed:` never does. `handleGuardianReplyIntercept` self-gates
238
- // on `trustClass === "guardian"`, so a contact's reaction returns no response
239
- // and falls through to persistence.
240
- const isReactionAdded = callbackData.startsWith("reaction:");
241
- if (isReactionAdded && replyCallbackUrl && !result.duplicate) {
210
+ // on `trustClass === "guardian"`, so a contact's reaction returns no
211
+ // response and falls through to persistence.
212
+ if (callbackData.startsWith("reaction:") && replyCallbackUrl) {
242
213
  const reactionIntercept = await handleGuardianReplyIntercept({
243
- isDuplicate: result.duplicate,
214
+ isDuplicate: false,
244
215
  trimmedContent: "",
245
216
  hasCallbackData: true,
246
217
  callbackData,
247
218
  reactedMessageTs,
248
219
  rawSenderId,
249
220
  canonicalSenderId,
250
- canonicalAssistantId,
251
221
  sourceChannel,
252
222
  conversationExternalId,
253
- conversationId: result.conversationId,
254
- eventId: result.eventId,
255
223
  replyCallbackUrl,
256
224
  trustClass: trustCtx.trustClass,
257
225
  guardianPrincipalId: trustCtx.guardianPrincipalId,
258
226
  approvalConversationGenerator,
259
227
  });
260
- // Consumed as a guardian decision (applied, or a surfaced failure delivered
261
- // as an ephemeral reply). Short-circuit so we do not also persist a
262
- // transcript row.
263
228
  if (reactionIntercept.response) {
264
229
  return reactionIntercept.response;
265
230
  }
266
231
  }
267
232
 
268
- // Record the reaction as an inline transcript signal. Requires the reacted
269
- // message ts to anchor the rendering.
270
- if (!reactedMessageTs) {
233
+ // The reaction belongs to the conversation of the message it was attached
234
+ // to. Slack sends no `thread_ts` on a reaction, so resolving a conversation
235
+ // from the reaction's own address keys one on the reacted message instead of
236
+ // finding the one that message lives in, minting an orphan per reaction.
237
+ // A message the assistant never stored has nothing to annotate, so the
238
+ // reaction is dropped rather than given a conversation of its own.
239
+ // Inbound messages carry their provider id on the event that delivered
240
+ // them. The assistant's own posts open no inbound event, so a reaction on
241
+ // one is resolved through the `slackMeta` those rows carry.
242
+ const targetConversationId = reactedMessageTs
243
+ ? (findMessageBySourceId(
244
+ sourceChannel,
245
+ conversationExternalId,
246
+ reactedMessageTs,
247
+ )?.conversationId ??
248
+ findSlackConversationByMessageTs(
249
+ conversationExternalId,
250
+ reactedMessageTs,
251
+ ))
252
+ : null;
253
+ if (!targetConversationId || !reactedMessageTs) {
271
254
  log.debug(
272
- { conversationId: result.conversationId, eventId: result.eventId },
273
- "Skipping reaction persistence: missing sourceMetadata.messageId",
255
+ { sourceChannel, conversationExternalId, reactedMessageTs },
256
+ "Dropping reaction: reacted message is not stored",
274
257
  );
275
- return {
276
- accepted: result.accepted,
277
- duplicate: result.duplicate,
278
- eventId: result.eventId,
279
- };
258
+ return { accepted: true, reaction: "dropped_unknown_target" };
280
259
  }
281
260
 
261
+ const result = recordInbound(
262
+ sourceChannel,
263
+ conversationExternalId,
264
+ externalMessageId,
265
+ // No `sourceMessageId`: that column names the provider id of the event's
266
+ // own message, and a reaction is not one. Claiming the reacted message's
267
+ // id here would put two linked rows on that id, leaving a later edit or
268
+ // delete of it free to resolve to the reaction instead.
269
+ { conversationId: targetConversationId },
270
+ );
271
+
272
+ // Record the reaction as an inline transcript signal.
282
273
  try {
283
274
  await persistSlackReactionAsMessage({
284
275
  conversationId: result.conversationId,
@@ -286,7 +277,6 @@ export async function handleSlackReactionIntercept(
286
277
  eventId: result.eventId,
287
278
  callbackData,
288
279
  actorDisplayName,
289
- threadTs,
290
280
  reactedMessageTs,
291
281
  duplicate: result.duplicate,
292
282
  });
@@ -320,7 +310,6 @@ async function persistSlackReactionAsMessage(params: {
320
310
  eventId: string;
321
311
  callbackData: string;
322
312
  actorDisplayName?: string;
323
- threadTs?: string;
324
313
  reactedMessageTs: string;
325
314
  duplicate: boolean;
326
315
  }): Promise<void> {
@@ -345,7 +334,11 @@ async function persistSlackReactionAsMessage(params: {
345
334
  channelId: params.conversationExternalId,
346
335
  channelTs: params.reactedMessageTs,
347
336
  eventKind: "reaction",
348
- ...(params.threadTs ? { threadTs: params.threadTs } : {}),
337
+ // No `threadTs`: Slack sends none on a reaction, so the gateway's thread
338
+ // id here is the reacted message's own ts. It equals `channelTs`, which
339
+ // every reader treats as "not in a thread" anyway, and storing it makes
340
+ // the row false evidence that a thread belongs to this conversation
341
+ // (`legacySlackConversationHasThreadEvidence`).
349
342
  ...(params.actorDisplayName
350
343
  ? { displayName: params.actorDisplayName }
351
344
  : {}),
@@ -14,6 +14,7 @@ import {
14
14
  } from "../../../persistence/delivery-crud.js";
15
15
  import { checkIngressForSecrets } from "../../../security/secret-ingress.js";
16
16
  import { getLogger } from "../../../util/logger.js";
17
+ import { DAEMON_INTERNAL_ASSISTANT_ID } from "../../assistant-scope.js";
17
18
 
18
19
  const log = getLogger("runtime-http");
19
20
 
@@ -36,7 +37,6 @@ export interface SecretIngressCheckParams {
36
37
  actorUsername: string | undefined;
37
38
  trustCtx: TrustContext;
38
39
  replyCallbackUrl: string | undefined;
39
- canonicalAssistantId: string;
40
40
  }
41
41
 
42
42
  export interface SecretIngressCheckResult {
@@ -69,7 +69,6 @@ export function runSecretIngressCheck(
69
69
  actorUsername,
70
70
  trustCtx,
71
71
  replyCallbackUrl,
72
- canonicalAssistantId,
73
72
  } = params;
74
73
 
75
74
  // Persist the raw payload so dead-lettered events can always be replayed.
@@ -85,7 +84,7 @@ export function runSecretIngressCheck(
85
84
  senderUsername: actorUsername,
86
85
  trustCtx,
87
86
  replyCallbackUrl,
88
- assistantId: canonicalAssistantId,
87
+ assistantId: DAEMON_INTERNAL_ASSISTANT_ID,
89
88
  });
90
89
 
91
90
  // ── Secret ingress scan ──
@@ -29,11 +29,14 @@ import {
29
29
  loadRawConfig,
30
30
  } from "../../config/loader.js";
31
31
  import {
32
- LLMProvider,
33
32
  ProfileEntry,
34
33
  routingIdentityModelIssue,
35
34
  } from "../../config/schemas/llm.js";
36
35
  import { getDb } from "../../persistence/db-connection.js";
36
+ import {
37
+ resolveEntryProviderKind,
38
+ writableProfileProviderIssue,
39
+ } from "../../providers/connection-resolution.js";
37
40
  import { ROUTING_IDENTITY_PROVIDERS } from "../../providers/inference/auth.js";
38
41
  import type { ConnectionAvailability } from "../../providers/inference/connection-availability.js";
39
42
  import {
@@ -150,10 +153,9 @@ const updateRequestSchema = z.object({
150
153
  // ---------------------------------------------------------------------------
151
154
 
152
155
  function assertValidProvider(provider: string): void {
153
- if (!LLMProvider.safeParse(provider).success) {
154
- throw new BadRequestError(
155
- `Invalid provider "${provider}". Valid providers: ${LLMProvider.options.join(", ")}.`,
156
- );
156
+ const issue = writableProfileProviderIssue(provider);
157
+ if (issue) {
158
+ throw new BadRequestError(issue);
157
159
  }
158
160
  }
159
161
 
@@ -182,31 +184,38 @@ function validateModel(
182
184
  }
183
185
  return [];
184
186
  }
185
- if (isModelInCatalog(provider, model)) {
187
+ // An entry-name provider validates against its row's dispatchable kind,
188
+ // the same translation dispatch uses; the entry itself also serves as the
189
+ // model-list connection for custom endpoints below.
190
+ const entryKind = resolveEntryProviderKind(provider, model);
191
+ const catalogProvider = entryKind ?? provider;
192
+ if (isModelInCatalog(catalogProvider, model)) {
186
193
  return [];
187
194
  }
188
195
  // The named connection's advertised model list is authoritative for models
189
196
  // the code-owned catalog doesn't know — a custom (openai-compatible)
190
197
  // endpoint declares its own models at connection-create time.
191
- if (connectionName) {
192
- const connection = getConnection(getDb(), connectionName);
198
+ const modelListConnection =
199
+ connectionName ?? (entryKind !== null ? provider : undefined);
200
+ if (modelListConnection) {
201
+ const connection = getConnection(getDb(), modelListConnection);
193
202
  if (connection?.models?.some((m) => m.id === model)) {
194
203
  return [];
195
204
  }
196
205
  }
197
206
  if (!allowUnlisted) {
198
207
  const remedy =
199
- provider === "openai-compatible"
208
+ catalogProvider === "openai-compatible"
200
209
  ? `Pass allowUnlisted to create it anyway, or declare the model on the connection ` +
201
210
  `("assistant inference providers update <name> --model ${model}").`
202
211
  : `Pass allowUnlisted to create it anyway, or run ` +
203
212
  `"assistant inference models list --provider ${provider}" to see valid ids.`;
204
213
  throw new BadRequestError(
205
- `Model "${model}" is not in the catalog for provider "${provider}". ${remedy}`,
214
+ `Model "${model}" is not in the catalog for provider "${catalogProvider}". ${remedy}`,
206
215
  );
207
216
  }
208
217
  return [
209
- `Model "${model}" is not in the catalog for provider "${provider}" — created anyway (allowUnlisted).`,
218
+ `Model "${model}" is not in the catalog for provider "${catalogProvider}"; created anyway (allowUnlisted).`,
210
219
  ];
211
220
  }
212
221
 
@@ -21,6 +21,7 @@ import { getDb } from "../../persistence/db-connection.js";
21
21
  import {
22
22
  type Auth,
23
23
  AuthSchema,
24
+ CHATGPT_SUBSCRIPTION_CONNECTION_NAME,
24
25
  type ConnectionModel,
25
26
  ConnectionModelSchema,
26
27
  ConnectionProviderSchema,
@@ -205,14 +206,26 @@ function deriveConnectionAuth(provider: string, credential: unknown): Auth {
205
206
  function assertAuthMatchesProvider(provider: string, auth: Auth): void {
206
207
  const managedAuth = auth.type === "platform";
207
208
  const managedProvider = provider === VELLUM_MANAGED_PROVIDER;
208
- if (managedAuth === managedProvider) {
209
- return;
209
+ if (managedAuth !== managedProvider) {
210
+ throw new BadRequestError(
211
+ managedAuth
212
+ ? `Auth type "platform" is only valid for provider "${VELLUM_MANAGED_PROVIDER}", not "${provider}". Vellum-managed routing is selected by the provider, so omit "auth" to derive it.`
213
+ : `Provider "${VELLUM_MANAGED_PROVIDER}" is always platform-authenticated; "${auth.type}" auth is not valid for it. Omit "auth" to derive it, or name a real provider for key-based auth.`,
214
+ );
215
+ }
216
+ // Same rule for the subscription identity: provider "chatgpt" and
217
+ // oauth_subscription auth record one fact and must pair, or a key-auth
218
+ // row under the identity would dispatch against the API while the user
219
+ // believes they are on subscription billing.
220
+ const subscriptionAuth = auth.type === "oauth_subscription";
221
+ const subscriptionProvider = provider === "chatgpt";
222
+ if (subscriptionAuth !== subscriptionProvider) {
223
+ throw new BadRequestError(
224
+ subscriptionAuth
225
+ ? `Auth type "oauth_subscription" is only valid for provider "chatgpt", not "${provider}". Run the ChatGPT sign-in flow to connect a subscription.`
226
+ : `Provider "chatgpt" is always subscription-authenticated; "${auth.type}" auth is not valid for it. Run the ChatGPT sign-in flow, or name a real provider for key-based auth.`,
227
+ );
210
228
  }
211
- throw new BadRequestError(
212
- managedAuth
213
- ? `Auth type "platform" is only valid for provider "${VELLUM_MANAGED_PROVIDER}", not "${provider}". Vellum-managed routing is selected by the provider, so omit "auth" to derive it.`
214
- : `Provider "${VELLUM_MANAGED_PROVIDER}" is always platform-authenticated; "${auth.type}" auth is not valid for it. Omit "auth" to derive it, or name a real provider for key-based auth.`,
215
- );
216
229
  }
217
230
 
218
231
  /**
@@ -323,6 +336,15 @@ async function handleCreateConnection({ body = {} }: RouteHandlerArgs) {
323
336
  `Connection name "${name}" is reserved for the Vellum-managed connection. Pick another name.`,
324
337
  );
325
338
  }
339
+ // Provider ids and routing identities are labels in profile config, so an
340
+ // entry under one of those names could never be referenced by name (the
341
+ // label reads as the vendor), and a future catalog addition must never
342
+ // capture an existing user name. Reserve the whole vocabulary.
343
+ if (VALID_CONNECTION_PROVIDERS.includes(name)) {
344
+ throw new BadRequestError(
345
+ `Connection name "${name}" is reserved as a provider id. Pick another name.`,
346
+ );
347
+ }
326
348
 
327
349
  const providerResult = ConnectionProviderSchema.safeParse(provider);
328
350
  if (!providerResult.success) {
@@ -330,15 +352,27 @@ async function handleCreateConnection({ body = {} }: RouteHandlerArgs) {
330
352
  `Invalid provider "${String(provider)}". Valid: ${VALID_CONNECTION_PROVIDERS.join(", ")}`,
331
353
  );
332
354
  }
355
+ // The chatgpt identity lives on its canonical row: routing resolves the
356
+ // subscription by that name, so an identity row under any other name can
357
+ // never dispatch.
358
+ if (
359
+ providerResult.data === "chatgpt" &&
360
+ name !== CHATGPT_SUBSCRIPTION_CONNECTION_NAME
361
+ ) {
362
+ throw new BadRequestError(
363
+ `Provider "chatgpt" is reserved for the "${CHATGPT_SUBSCRIPTION_CONNECTION_NAME}" connection. Run the ChatGPT sign-in flow to connect a subscription.`,
364
+ );
365
+ }
333
366
  const authResult = AuthSchema.safeParse(
334
367
  auth ?? deriveConnectionAuth(providerResult.data, body.credential),
335
368
  );
336
369
  if (!authResult.success) {
337
370
  throw new BadRequestError(`Invalid auth: ${authResult.error.message}`);
338
371
  }
339
- if (auth !== undefined) {
340
- assertAuthMatchesProvider(providerResult.data, authResult.data);
341
- }
372
+ // Asserted on derived auth as well: derivation pairs every provider
373
+ // correctly except the chatgpt identity, whose fallthrough would mint
374
+ // api_key auth from a bare credential.
375
+ assertAuthMatchesProvider(providerResult.data, authResult.data);
342
376
 
343
377
  const labelRaw = body.label;
344
378
  if (
@@ -434,15 +468,31 @@ async function handleUpdateConnection({
434
468
  if (!authResult.success) {
435
469
  throw new BadRequestError(`Invalid auth: ${authResult.error.message}`);
436
470
  }
471
+ // The canonical subscription row owns the "chatgpt" identity: writing
472
+ // subscription auth to it stamps the provider with the auth, mirroring
473
+ // the daemon's own sign-in exchange route. The CLI's login-chatgpt PATCHes
474
+ // auth through this route, and without the stamp a row the identity
475
+ // migration deliberately skipped (a claiming row with key auth) would end
476
+ // up as provider "openai" with subscription auth. Provider stays
477
+ // immutable for every other row.
478
+ const chatgptIdentityStamp =
479
+ name === CHATGPT_SUBSCRIPTION_CONNECTION_NAME &&
480
+ authResult.data.type === "oauth_subscription" &&
481
+ existing.provider !== "chatgpt";
482
+
437
483
  // The pairing is enforced on an actual auth change, not on the field being
438
484
  // present: the web editor and the CLI both resend the stored auth on every
439
485
  // edit, so a row whose columns already disagree stays relabelable and
440
- // re-pointable.
486
+ // re-pointable. Judged against the stamped provider when the stamp
487
+ // applies, since that is the pair being written.
441
488
  if (
442
489
  body.auth !== undefined &&
443
490
  authFingerprint(authResult.data) !== authFingerprint(existing.auth)
444
491
  ) {
445
- assertAuthMatchesProvider(existing.provider, authResult.data);
492
+ assertAuthMatchesProvider(
493
+ chatgptIdentityStamp ? "chatgpt" : existing.provider,
494
+ authResult.data,
495
+ );
446
496
  }
447
497
 
448
498
  const labelRaw = body.label;
@@ -473,6 +523,7 @@ async function handleUpdateConnection({
473
523
 
474
524
  const result = updateConnection(getDb(), name, {
475
525
  auth: authResult.data,
526
+ ...(chatgptIdentityStamp ? { provider: "chatgpt" } : {}),
476
527
  ...(labelRaw !== undefined ? { label: labelRaw as string | null } : {}),
477
528
  ...customFields,
478
529
  });
@@ -574,10 +625,21 @@ async function handleDeleteConnection({ pathParams = {} }: RouteHandlerArgs) {
574
625
  // above; this keeps the scan a faithful backstop rather than one that
575
626
  // silently skips the defaults.
576
627
  const profiles = getEffectiveProfilesForProvider(config.llm?.profiles, dp);
628
+ // A binding lives in `provider` (the entry name) under the entries model,
629
+ // or in the legacy `provider_connection` field; both count, or deleting a
630
+ // bound entry would dangle the profile behind selection's silent healing.
631
+ // Provider values count only for non-vendor names: a profile saying
632
+ // "vellum" references the routing identity, never a row that happens to
633
+ // claim that name, and the claiming-row delete is the recovery path.
634
+ const nameIsEntryName = !VALID_CONNECTION_PROVIDERS.includes(name);
577
635
  const referencingProfiles = Object.entries(profiles)
578
- .filter(
579
- ([, p]) => (p as Record<string, unknown>).provider_connection === name,
580
- )
636
+ .filter(([, p]) => {
637
+ const entry = p as Record<string, unknown>;
638
+ return (
639
+ entry.provider_connection === name ||
640
+ (nameIsEntryName && entry.provider === name)
641
+ );
642
+ })
581
643
  .map(([profileName]) => profileName);
582
644
 
583
645
  const result = deleteConnection(getDb(), name, {
@@ -143,6 +143,9 @@ async function handleExport({
143
143
  ? [eq(messages.conversationId, conversationId)]
144
144
  : [];
145
145
 
146
+ // Any-state export, deliberately: this is a diagnostic surface, and an
147
+ // in-flight row (finalized = 0, content a { ref } into the turn's delta
148
+ // file) is often exactly what a bug report needs to show.
146
149
  const messageRows = capRows(
147
150
  db
148
151
  .select()