@vellumai/assistant 0.8.1 → 0.8.3

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 (630) hide show
  1. package/ARCHITECTURE.md +13 -19
  2. package/Dockerfile +75 -1
  3. package/bun.lock +11 -1
  4. package/docker-entrypoint.sh +17 -0
  5. package/docker-init-apt-root.sh +167 -0
  6. package/docker-kata-apt-env.sh +39 -0
  7. package/docs/plugins.md +88 -47
  8. package/docs/skills.md +9 -7
  9. package/examples/plugins/echo/README.md +27 -27
  10. package/examples/plugins/echo/package.json +3 -0
  11. package/examples/plugins/echo/register.ts +31 -31
  12. package/node_modules/@vellumai/slack-text/src/index.test.ts +114 -14
  13. package/node_modules/@vellumai/slack-text/src/index.ts +82 -18
  14. package/openapi.yaml +642 -5
  15. package/package.json +3 -1
  16. package/scripts/generate-openapi.ts +83 -10
  17. package/scripts/sync-llm-catalog.ts +2 -2
  18. package/scripts/sync-web-search-catalog.ts +47 -25
  19. package/src/__tests__/agent-image-optimize.test.ts +11 -3
  20. package/src/__tests__/agent-loop-exit-reason.test.ts +272 -0
  21. package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
  22. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +131 -0
  23. package/src/__tests__/anthropic-provider.test.ts +45 -0
  24. package/src/__tests__/app-builder-tool-scripts.test.ts +9 -3
  25. package/src/__tests__/app-executors.test.ts +220 -4
  26. package/src/__tests__/auto-analysis-end-to-end.test.ts +35 -0
  27. package/src/__tests__/bundled-asset.test.ts +6 -6
  28. package/src/__tests__/channel-availability-routes.test.ts +206 -0
  29. package/src/__tests__/channel-delivery-store.test.ts +289 -1
  30. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -1
  31. package/src/__tests__/clawhub.test.ts +75 -16
  32. package/src/__tests__/compactor-tail-resolution.test.ts +147 -0
  33. package/src/__tests__/config-get-vision-flag.test.ts +136 -0
  34. package/src/__tests__/config-loader-backfill.test.ts +115 -18
  35. package/src/__tests__/config-schema.test.ts +21 -0
  36. package/src/__tests__/config-set-route.test.ts +80 -0
  37. package/src/__tests__/config-sounds-sync.test.ts +97 -0
  38. package/src/__tests__/config-watcher-skill-reseed.test.ts +453 -0
  39. package/src/__tests__/context-search-conversations-source.test.ts +117 -2
  40. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
  41. package/src/__tests__/context-search-workspace-source.test.ts +7 -0
  42. package/src/__tests__/context-token-estimator.test.ts +31 -65
  43. package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
  44. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
  45. package/src/__tests__/conversation-agent-loop-overflow.test.ts +92 -92
  46. package/src/__tests__/conversation-agent-loop.test.ts +59 -1
  47. package/src/__tests__/conversation-error.test.ts +42 -3
  48. package/src/__tests__/conversation-fork-crud.test.ts +82 -0
  49. package/src/__tests__/conversation-inference-profile-route.test.ts +40 -4
  50. package/src/__tests__/conversation-lifecycle.test.ts +173 -0
  51. package/src/__tests__/conversation-media-retry.test.ts +19 -8
  52. package/src/__tests__/conversation-message-sync-tags.test.ts +97 -0
  53. package/src/__tests__/conversation-pairing.test.ts +54 -0
  54. package/src/__tests__/conversation-process-callsite.test.ts +4 -1
  55. package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -1
  56. package/src/__tests__/conversation-queue.test.ts +4 -1
  57. package/src/__tests__/conversation-runtime-assembly.test.ts +102 -13
  58. package/src/__tests__/conversation-slash-queue.test.ts +59 -1
  59. package/src/__tests__/conversation-slash-unknown.test.ts +4 -1
  60. package/src/__tests__/conversation-surfaces-table-action.test.ts +360 -0
  61. package/src/__tests__/conversation-sync-tags.test.ts +235 -0
  62. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  63. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  64. package/src/__tests__/credential-security-invariants.test.ts +3 -2
  65. package/src/__tests__/date-context.test.ts +45 -0
  66. package/src/__tests__/db-slack-external-content-normalization.test.ts +301 -0
  67. package/src/__tests__/delete-managed-skill-tool.test.ts +55 -13
  68. package/src/__tests__/disk-pressure-tools.test.ts +1 -0
  69. package/src/__tests__/dm-backfill.test.ts +121 -10
  70. package/src/__tests__/document-tool-security.test.ts +258 -0
  71. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  72. package/src/__tests__/edit-propagation.test.ts +33 -0
  73. package/src/__tests__/empty-response-pipeline.test.ts +0 -4
  74. package/src/__tests__/external-plugin-loader.test.ts +151 -55
  75. package/src/__tests__/filing-service.test.ts +140 -0
  76. package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
  77. package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
  78. package/src/__tests__/guardian-dispatch.test.ts +1 -0
  79. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
  80. package/src/__tests__/heartbeat-service.test.ts +24 -164
  81. package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
  82. package/src/__tests__/helpers/tar-fixtures.ts +39 -0
  83. package/src/__tests__/helpers/wait-for.ts +21 -0
  84. package/src/__tests__/history-repair-pipeline.test.ts +0 -3
  85. package/src/__tests__/history-repair.test.ts +73 -0
  86. package/src/__tests__/host-app-control-proxy.test.ts +507 -10
  87. package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
  88. package/src/__tests__/image-credentials.test.ts +1 -1
  89. package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
  90. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +1 -1
  91. package/src/__tests__/inference-profile-reaper.test.ts +4 -2
  92. package/src/__tests__/inference-profile-session-handler.test.ts +18 -6
  93. package/src/__tests__/inference-profile-session-ipc.test.ts +17 -5
  94. package/src/__tests__/injector-background-turn.test.ts +153 -0
  95. package/src/__tests__/injector-chain.test.ts +15 -8
  96. package/src/__tests__/install-skill-routing.test.ts +155 -37
  97. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +99 -3
  98. package/src/__tests__/list-messages-page-latest.test.ts +55 -0
  99. package/src/__tests__/llm-call-pipeline.test.ts +0 -3
  100. package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
  101. package/src/__tests__/llm-catalog-parity.test.ts +58 -13
  102. package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +116 -0
  103. package/src/__tests__/llm-request-log-error-payload.test.ts +138 -0
  104. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +36 -0
  105. package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
  106. package/src/__tests__/llm-resolver.test.ts +255 -2
  107. package/src/__tests__/llm-usage-store.test.ts +114 -0
  108. package/src/__tests__/managed-profile-guard.test.ts +41 -29
  109. package/src/__tests__/managed-skill-lifecycle.test.ts +109 -18
  110. package/src/__tests__/managed-store.test.ts +84 -192
  111. package/src/__tests__/media-generate-image.test.ts +1 -1
  112. package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -2
  113. package/src/__tests__/messages-after-tiebreaker.test.ts +122 -0
  114. package/src/__tests__/notification-decision-fallback.test.ts +0 -91
  115. package/src/__tests__/notification-decision-strategy.test.ts +14 -31
  116. package/src/__tests__/notification-deep-link.test.ts +15 -0
  117. package/src/__tests__/notification-guardian-path.test.ts +1 -2
  118. package/src/__tests__/notification-platform-adapter.test.ts +5 -4
  119. package/src/__tests__/notification-telegram-adapter.test.ts +1 -0
  120. package/src/__tests__/notification-vellum-adapter.test.ts +113 -0
  121. package/src/__tests__/oauth-commands-routes.test.ts +168 -16
  122. package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
  123. package/src/__tests__/openai-provider.test.ts +242 -3
  124. package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
  125. package/src/__tests__/openrouter-provider-only.test.ts +51 -3
  126. package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
  127. package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -2
  128. package/src/__tests__/persistence-pipeline.test.ts +0 -2
  129. package/src/__tests__/{managed-proxy-context.test.ts → platform-proxy-context.test.ts} +7 -2
  130. package/src/__tests__/platform.test.ts +2 -0
  131. package/src/__tests__/plugin-api-shim.test.ts +125 -0
  132. package/src/__tests__/plugin-bootstrap.test.ts +10 -36
  133. package/src/__tests__/plugin-external-api.test.ts +68 -0
  134. package/src/__tests__/plugin-registry.test.ts +0 -77
  135. package/src/__tests__/plugin-route-contribution.test.ts +0 -1
  136. package/src/__tests__/plugin-skill-contribution.test.ts +0 -2
  137. package/src/__tests__/plugin-tool-contribution.test.ts +16 -15
  138. package/src/__tests__/plugin-types.test.ts +3 -13
  139. package/src/__tests__/process-message-background-slack.test.ts +8 -1
  140. package/src/__tests__/process-message-display-content.test.ts +421 -0
  141. package/src/__tests__/provider-catalog-visibility.test.ts +158 -0
  142. package/src/__tests__/provider-error-scenarios.test.ts +111 -0
  143. package/src/__tests__/{provider-managed-proxy-integration.test.ts → provider-platform-proxy-integration.test.ts} +33 -31
  144. package/src/__tests__/scaffold-managed-skill-tool.test.ts +65 -13
  145. package/src/__tests__/schedule-routes.test.ts +50 -3
  146. package/src/__tests__/schedule-store.test.ts +94 -0
  147. package/src/__tests__/scheduler-reuse-conversation.test.ts +54 -7
  148. package/src/__tests__/schema-transforms.test.ts +20 -0
  149. package/src/__tests__/search-skills-unified.test.ts +0 -5
  150. package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +1 -1
  151. package/src/__tests__/server-history-render.test.ts +43 -0
  152. package/src/__tests__/skill-load-feature-flag.test.ts +0 -12
  153. package/src/__tests__/skill-load-tool.test.ts +27 -89
  154. package/src/__tests__/skill-memory.test.ts +23 -3
  155. package/src/__tests__/skills-file-content-endpoint.test.ts +9 -38
  156. package/src/__tests__/skills-files-catalog-fallback.test.ts +0 -3
  157. package/src/__tests__/skills-install-extract.test.ts +49 -38
  158. package/src/__tests__/skills-install-staging.test.ts +159 -0
  159. package/src/__tests__/skills-uninstall.test.ts +9 -41
  160. package/src/__tests__/skills.test.ts +51 -58
  161. package/src/__tests__/slack-channel-config.test.ts +9 -0
  162. package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
  163. package/src/__tests__/system-prompt.test.ts +670 -63
  164. package/src/__tests__/terminal-tools.test.ts +28 -1
  165. package/src/__tests__/thread-backfill.test.ts +557 -27
  166. package/src/__tests__/title-generate-pipeline.test.ts +0 -13
  167. package/src/__tests__/token-estimate-pipeline.test.ts +0 -3
  168. package/src/__tests__/tool-error-pipeline.test.ts +0 -3
  169. package/src/__tests__/tool-execute-pipeline.test.ts +0 -5
  170. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  171. package/src/__tests__/tool-executor.test.ts +16 -4
  172. package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -12
  173. package/src/__tests__/turn-events-store.test.ts +256 -0
  174. package/src/__tests__/twilio-routes.test.ts +4 -0
  175. package/src/__tests__/user-plugin-loader.test.ts +0 -7
  176. package/src/__tests__/voice-session-bridge.test.ts +198 -0
  177. package/src/__tests__/web-search-catalog-parity.test.ts +32 -10
  178. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +115 -3
  179. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +50 -0
  180. package/src/__tests__/workspace-migration-073-repair-recall-callsite-empty-profile.test.ts +153 -0
  181. package/src/__tests__/workspace-migration-085-memory-v2-bm25-b-reembed-disabled-v2-pages.test.ts +220 -0
  182. package/src/__tests__/workspace-migration-086-revert-stale-gemini-mis-rewrites.test.ts +269 -0
  183. package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
  184. package/src/__tests__/workspace-migration-remove-legacy-skills-index.test.ts +309 -0
  185. package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
  186. package/src/a2a/__tests__/agent-card.test.ts +98 -0
  187. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +597 -0
  188. package/src/a2a/__tests__/protocol-helpers.test.ts +113 -0
  189. package/src/a2a/__tests__/task-store.test.ts +246 -0
  190. package/src/a2a/agent-card.ts +58 -0
  191. package/src/a2a/feature-gate.ts +8 -0
  192. package/src/a2a/protocol-constants.ts +21 -0
  193. package/src/a2a/protocol-errors.ts +50 -0
  194. package/src/a2a/protocol-types.ts +162 -0
  195. package/src/a2a/task-store.ts +168 -0
  196. package/src/acp/resolve-agent.ts +1 -1
  197. package/src/agent/image-optimize.ts +13 -5
  198. package/src/agent/loop.ts +167 -18
  199. package/src/calls/voice-session-bridge.ts +61 -42
  200. package/src/channels/config.ts +9 -0
  201. package/src/channels/types.ts +122 -0
  202. package/src/cli/__tests__/unknown-command.test.ts +24 -0
  203. package/src/cli/commands/__tests__/changelog.test.ts +304 -319
  204. package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
  205. package/src/cli/commands/__tests__/schedules.test.ts +960 -0
  206. package/src/cli/commands/changelog.ts +106 -42
  207. package/src/cli/commands/conversations.ts +102 -17
  208. package/src/cli/commands/default-action.ts +10 -53
  209. package/src/cli/commands/notifications.ts +388 -346
  210. package/src/cli/commands/plugins.ts +252 -0
  211. package/src/cli/commands/schedules.ts +683 -0
  212. package/src/cli/commands/telemetry.ts +40 -0
  213. package/src/cli/lib/__tests__/cli-colors.test.ts +48 -0
  214. package/src/cli/lib/__tests__/confirm-prompt.test.ts +159 -0
  215. package/src/cli/lib/__tests__/install-from-github.test.ts +355 -0
  216. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +154 -0
  217. package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
  218. package/src/cli/lib/__tests__/uninstall-plugin.test.ts +124 -0
  219. package/src/cli/lib/__tests__/unknown-command.test.ts +106 -0
  220. package/src/cli/lib/cli-colors.ts +12 -0
  221. package/src/cli/lib/confirm-prompt.ts +79 -0
  222. package/src/cli/lib/install-from-github.ts +303 -0
  223. package/src/cli/lib/list-installed-plugins.ts +137 -0
  224. package/src/cli/lib/search-plugins.ts +163 -0
  225. package/src/cli/lib/uninstall-plugin.ts +82 -0
  226. package/src/cli/lib/unknown-command.ts +111 -0
  227. package/src/cli/program.ts +52 -2
  228. package/src/config/assistant-feature-flags.ts +24 -54
  229. package/src/config/bundled-skills/app-builder/SKILL.md +140 -22
  230. package/src/config/bundled-skills/app-builder/TOOLS.json +7 -0
  231. package/src/config/bundled-skills/computer-use/TOOLS.json +15 -52
  232. package/src/config/bundled-skills/document/SKILL.md +23 -3
  233. package/src/config/bundled-skills/document/TOOLS.json +53 -0
  234. package/src/config/bundled-skills/document/tools/document-delete.ts +12 -0
  235. package/src/config/bundled-skills/document/tools/document-list.ts +12 -0
  236. package/src/config/bundled-skills/document/tools/document-read.ts +12 -0
  237. package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
  238. package/src/config/bundled-skills/skill-management/SKILL.md +2 -2
  239. package/src/config/bundled-skills/skill-management/TOOLS.json +7 -7
  240. package/src/config/bundled-tool-registry.ts +6 -0
  241. package/src/config/call-site-defaults.ts +105 -0
  242. package/src/config/feature-flag-registry.json +41 -9
  243. package/src/config/llm-resolver.ts +52 -1
  244. package/src/config/loader.ts +64 -38
  245. package/src/config/schema.ts +9 -10
  246. package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
  247. package/src/config/schemas/__tests__/memory-v2.test.ts +3 -3
  248. package/src/config/schemas/channels.ts +17 -0
  249. package/src/config/schemas/compaction.ts +28 -0
  250. package/src/config/schemas/conversations.ts +10 -0
  251. package/src/config/schemas/heartbeat.ts +23 -0
  252. package/src/config/schemas/llm-request-logs.ts +31 -7
  253. package/src/config/schemas/llm.ts +1 -0
  254. package/src/config/schemas/memory-retrieval.ts +18 -0
  255. package/src/config/schemas/memory-retrospective.ts +1 -1
  256. package/src/config/schemas/memory-v2.ts +4 -4
  257. package/src/config/schemas/memory.ts +3 -1
  258. package/src/config/schemas/tools.ts +14 -0
  259. package/src/config/seed-inference-profiles.ts +99 -29
  260. package/src/config/skills.ts +3 -96
  261. package/src/context/compactor.ts +1107 -0
  262. package/src/context/token-estimator.ts +34 -36
  263. package/src/context/window-manager.ts +197 -1520
  264. package/src/credential-execution/managed-catalog.ts +37 -0
  265. package/src/credential-health/credential-health-service.ts +280 -19
  266. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +33 -18
  267. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +138 -0
  268. package/src/daemon/__tests__/conversation-tool-setup.test.ts +74 -0
  269. package/src/daemon/approval-generators.ts +8 -6
  270. package/src/daemon/config-watcher.ts +94 -31
  271. package/src/daemon/conversation-agent-loop-handlers.ts +78 -0
  272. package/src/daemon/conversation-agent-loop.ts +198 -11
  273. package/src/daemon/conversation-error.ts +171 -37
  274. package/src/daemon/conversation-lifecycle.ts +53 -40
  275. package/src/daemon/conversation-messaging.ts +25 -6
  276. package/src/daemon/conversation-process.ts +49 -12
  277. package/src/daemon/conversation-runtime-assembly.ts +25 -1
  278. package/src/daemon/conversation-slash.ts +12 -5
  279. package/src/daemon/conversation-store.ts +11 -4
  280. package/src/daemon/conversation-tool-setup.ts +39 -7
  281. package/src/daemon/conversation.ts +33 -8
  282. package/src/daemon/date-context.ts +40 -0
  283. package/src/daemon/external-plugins-bootstrap.ts +217 -181
  284. package/src/daemon/first-greeting.ts +22 -2
  285. package/src/daemon/guardian-action-generators.ts +1 -125
  286. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +248 -0
  287. package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +154 -0
  288. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +133 -0
  289. package/src/daemon/handlers/__tests__/config-a2a.test.ts +95 -0
  290. package/src/daemon/handlers/config-a2a.ts +289 -0
  291. package/src/daemon/handlers/config-model.ts +6 -5
  292. package/src/daemon/handlers/config-slack-channel.ts +15 -3
  293. package/src/daemon/handlers/conversations.ts +1 -0
  294. package/src/daemon/handlers/shared.ts +14 -5
  295. package/src/daemon/handlers/skills.ts +111 -108
  296. package/src/daemon/history-repair.ts +28 -1
  297. package/src/daemon/host-app-control-proxy.ts +153 -27
  298. package/src/daemon/host-proxy-preactivation.ts +85 -18
  299. package/src/daemon/lifecycle.ts +89 -91
  300. package/src/daemon/meet-host-supervisor.ts +5 -4
  301. package/src/daemon/memory-v2-startup.ts +85 -0
  302. package/src/daemon/message-protocol.ts +1 -0
  303. package/src/daemon/message-types/conversations.ts +25 -0
  304. package/src/daemon/message-types/messages.ts +61 -0
  305. package/src/daemon/message-types/notifications.ts +21 -0
  306. package/src/daemon/message-types/subagents.ts +1 -0
  307. package/src/daemon/message-types/sync.ts +1 -0
  308. package/src/daemon/pkb-reminder-builder.test.ts +11 -54
  309. package/src/daemon/pkb-reminder-builder.ts +5 -20
  310. package/src/daemon/plugin-source-watcher.ts +146 -0
  311. package/src/daemon/process-message.ts +24 -3
  312. package/src/daemon/server.ts +11 -2
  313. package/src/daemon/skill-memory-refresh.ts +33 -0
  314. package/src/daemon/wake-target-adapter.ts +2 -0
  315. package/src/documents/document-store.ts +221 -3
  316. package/src/embedded/plugin-api.ts +40 -0
  317. package/src/export/__tests__/transcript-formatter.test.ts +121 -0
  318. package/src/export/transcript-formatter.ts +54 -20
  319. package/src/filing/filing-service.ts +39 -0
  320. package/src/heartbeat/__tests__/heartbeat-service.test.ts +135 -6
  321. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  322. package/src/heartbeat/heartbeat-service.ts +73 -189
  323. package/src/home/__tests__/feed-types.test.ts +80 -0
  324. package/src/home/feed-types.ts +36 -2
  325. package/src/home/post-connect-feed.ts +1 -0
  326. package/src/index.ts +18 -1
  327. package/src/ipc/cli-client.ts +147 -45
  328. package/src/live-voice/__tests__/live-voice-stt.test.ts +57 -0
  329. package/src/mcp/client.ts +20 -4
  330. package/src/media/image-credentials.ts +3 -3
  331. package/src/memory/__tests__/bookmark-crud.test.ts +33 -27
  332. package/src/memory/__tests__/conversation-queries.test.ts +483 -0
  333. package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
  334. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
  335. package/src/memory/__tests__/memory-retrospective-job.test.ts +87 -4
  336. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +119 -14
  337. package/src/memory/__tests__/message-content.test.ts +35 -0
  338. package/src/memory/bookmark-crud.ts +42 -10
  339. package/src/memory/context-search/sources/conversations.ts +62 -2
  340. package/src/memory/context-search/sources/workspace.ts +4 -0
  341. package/src/memory/conversation-crud.ts +63 -19
  342. package/src/memory/conversation-queries.ts +197 -11
  343. package/src/memory/conversation-title-service.ts +26 -4
  344. package/src/memory/db-init.ts +12 -0
  345. package/src/memory/delivery-crud.ts +152 -5
  346. package/src/memory/embedding-backend.ts +4 -4
  347. package/src/memory/external-conversation-store.ts +66 -5
  348. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +150 -12
  349. package/src/memory/graph/conversation-graph-memory.ts +49 -21
  350. package/src/memory/graph/tools.ts +9 -40
  351. package/src/memory/indexer.ts +34 -29
  352. package/src/memory/invite-store.ts +53 -0
  353. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +73 -0
  354. package/src/memory/jobs/embed-concept-page.ts +20 -11
  355. package/src/memory/jobs-worker.ts +6 -1
  356. package/src/memory/llm-request-log-source-clickhouse.ts +24 -12
  357. package/src/memory/llm-request-log-source.ts +19 -52
  358. package/src/memory/llm-request-log-store.ts +92 -1
  359. package/src/memory/llm-usage-store.ts +125 -5
  360. package/src/memory/memory-retrospective-enqueue.ts +1 -20
  361. package/src/memory/memory-retrospective-job.ts +33 -6
  362. package/src/memory/memory-retrospective-startup-cleanup.ts +72 -5
  363. package/src/memory/message-content.ts +1 -1
  364. package/src/memory/migrations/109-external-conversation-bindings.ts +15 -4
  365. package/src/memory/migrations/229-delete-private-conversations.test.ts +38 -1
  366. package/src/memory/migrations/229-delete-private-conversations.ts +7 -0
  367. package/src/memory/migrations/247-external-conversation-binding-thread-id.ts +78 -0
  368. package/src/memory/migrations/248-create-onboarding-events.ts +21 -0
  369. package/src/memory/migrations/249-normalize-slack-external-content.ts +240 -0
  370. package/src/memory/migrations/250-provider-connection-base-url-and-models.ts +28 -0
  371. package/src/memory/migrations/251-a2a-tasks.ts +49 -0
  372. package/src/memory/migrations/252-llm-request-log-agent-loop-exit-reason.ts +32 -0
  373. package/src/memory/migrations/index.ts +9 -0
  374. package/src/memory/migrations/registry.ts +16 -0
  375. package/src/memory/onboarding-events-store.ts +106 -0
  376. package/src/memory/schema/a2a.ts +15 -0
  377. package/src/memory/schema/bookmarks.ts +0 -2
  378. package/src/memory/schema/calls.ts +1 -0
  379. package/src/memory/schema/index.ts +1 -0
  380. package/src/memory/schema/inference.ts +3 -3
  381. package/src/memory/schema/infrastructure.ts +13 -0
  382. package/src/memory/turn-events-store.ts +127 -2
  383. package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
  384. package/src/memory/v2/__tests__/activation.test.ts +0 -8
  385. package/src/memory/v2/__tests__/cli-command-store.test.ts +404 -0
  386. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +25 -4
  387. package/src/memory/v2/__tests__/injection.test.ts +288 -11
  388. package/src/memory/v2/__tests__/migration.test.ts +87 -0
  389. package/src/memory/v2/__tests__/page-index.test.ts +83 -0
  390. package/src/memory/v2/__tests__/prompts-router.test.ts +58 -6
  391. package/src/memory/v2/__tests__/qdrant.test.ts +66 -3
  392. package/src/memory/v2/__tests__/router.test.ts +15 -0
  393. package/src/memory/v2/__tests__/skill-store.test.ts +387 -8
  394. package/src/memory/v2/__tests__/static-context.test.ts +12 -1
  395. package/src/memory/v2/activation-store.ts +14 -16
  396. package/src/memory/v2/cli-command-content.ts +19 -0
  397. package/src/memory/v2/cli-command-store.ts +304 -0
  398. package/src/memory/v2/frontmatter-sweep.ts +7 -1
  399. package/src/memory/v2/injection.ts +81 -26
  400. package/src/memory/v2/migration.ts +49 -19
  401. package/src/memory/v2/page-index.ts +63 -8
  402. package/src/memory/v2/prompts/router.ts +11 -8
  403. package/src/memory/v2/prompts/sweep.ts +2 -2
  404. package/src/memory/v2/qdrant.ts +135 -7
  405. package/src/memory/v2/router.ts +9 -8
  406. package/src/memory/v2/skill-store.ts +120 -35
  407. package/src/memory/v2/static-context.ts +4 -4
  408. package/src/memory/v2/types.ts +23 -0
  409. package/src/messaging/providers/a2a/__tests__/deliver.test.ts +274 -0
  410. package/src/messaging/providers/a2a/deliver.ts +156 -0
  411. package/src/messaging/providers/gmail/client.ts +9 -2
  412. package/src/messaging/providers/index.ts +11 -2
  413. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +45 -5
  414. package/src/messaging/providers/slack/__tests__/download.test.ts +231 -0
  415. package/src/messaging/providers/slack/adapter.ts +43 -5
  416. package/src/messaging/providers/slack/client.ts +27 -0
  417. package/src/messaging/providers/slack/deep-link.ts +65 -0
  418. package/src/messaging/providers/slack/download.ts +104 -0
  419. package/src/messaging/providers/slack/message-metadata.test.ts +32 -0
  420. package/src/messaging/providers/slack/message-metadata.ts +27 -0
  421. package/src/messaging/providers/slack/render-transcript.test.ts +134 -0
  422. package/src/messaging/providers/slack/render-transcript.ts +69 -5
  423. package/src/messaging/providers/slack/types.ts +20 -1
  424. package/src/notifications/__tests__/broadcaster.test.ts +203 -0
  425. package/src/notifications/__tests__/decision-engine.test.ts +283 -0
  426. package/src/notifications/__tests__/deterministic-checks.test.ts +286 -0
  427. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
  428. package/src/notifications/__tests__/home-feed-side-effect.test.ts +430 -7
  429. package/src/notifications/adapters/macos.ts +12 -2
  430. package/src/notifications/broadcaster.ts +29 -4
  431. package/src/notifications/conversation-pairing.ts +2 -1
  432. package/src/notifications/copy-composer.ts +17 -64
  433. package/src/notifications/decision-engine.ts +113 -45
  434. package/src/notifications/deterministic-checks.ts +96 -0
  435. package/src/notifications/emit-signal.ts +21 -1
  436. package/src/notifications/home-feed-side-effect.ts +138 -5
  437. package/src/notifications/signal.ts +3 -5
  438. package/src/notifications/types.ts +8 -0
  439. package/src/oauth/connection-resolver.ts +8 -4
  440. package/src/oauth/platform-connection.test.ts +43 -3
  441. package/src/oauth/platform-connection.ts +19 -6
  442. package/src/oauth/seed-providers.ts +10 -1
  443. package/src/permissions/checker.ts +2 -0
  444. package/src/permissions/ipc-risk-types.ts +1 -0
  445. package/src/permissions/question-prompter.test.ts +416 -0
  446. package/src/permissions/question-prompter.ts +294 -0
  447. package/src/platform/client.test.ts +1 -1
  448. package/src/platform/client.ts +1 -1
  449. package/src/plugin-api/constants.ts +26 -0
  450. package/src/plugin-api/index.ts +34 -1
  451. package/src/plugin-api/types.ts +104 -22
  452. package/src/plugins/defaults/circuit-breaker.ts +0 -5
  453. package/src/plugins/defaults/compaction.ts +0 -4
  454. package/src/plugins/defaults/empty-response.ts +0 -2
  455. package/src/plugins/defaults/history-repair.ts +0 -2
  456. package/src/plugins/defaults/injectors.ts +74 -22
  457. package/src/plugins/defaults/llm-call.ts +0 -2
  458. package/src/plugins/defaults/memory-retrieval.ts +0 -1
  459. package/src/plugins/defaults/overflow-reduce.ts +0 -1
  460. package/src/plugins/defaults/persistence.ts +0 -2
  461. package/src/plugins/defaults/title-generate.ts +0 -5
  462. package/src/plugins/defaults/token-estimate.ts +0 -2
  463. package/src/plugins/defaults/tool-error.ts +0 -7
  464. package/src/plugins/defaults/tool-execute.ts +0 -2
  465. package/src/plugins/defaults/tool-result-truncate.ts +0 -4
  466. package/src/plugins/ensure-plugin-api-shim.ts +96 -0
  467. package/src/plugins/external-api.ts +104 -0
  468. package/src/plugins/external-plugin-loader.ts +187 -42
  469. package/src/plugins/feature-gate.ts +22 -0
  470. package/src/plugins/pipeline.ts +37 -0
  471. package/src/plugins/registry.ts +48 -80
  472. package/src/plugins/types.ts +40 -26
  473. package/src/plugins/user-loader.ts +21 -2
  474. package/src/proactive-artifact/aux-message-injector.ts +11 -0
  475. package/src/proactive-artifact/job.test.ts +37 -5
  476. package/src/prompts/__tests__/system-prompt.test.ts +10 -43
  477. package/src/prompts/__tests__/task-progress-hint-section.test.ts +95 -0
  478. package/src/prompts/normalize-onboarding.ts +27 -0
  479. package/src/prompts/sections.ts +302 -0
  480. package/src/prompts/system-prompt.ts +63 -174
  481. package/src/prompts/templates/BOOTSTRAP.md +17 -1
  482. package/src/prompts/templates/system-sections.ts +164 -0
  483. package/src/providers/__tests__/inference.test.ts +24 -7
  484. package/src/providers/anthropic/client.ts +28 -28
  485. package/src/providers/call-site-routing.ts +24 -6
  486. package/src/providers/connection-resolution.ts +68 -11
  487. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
  488. package/src/providers/inference/__tests__/connections-openai-compatible.test.ts +175 -0
  489. package/src/providers/inference/__tests__/connections-status-label.test.ts +15 -0
  490. package/src/providers/inference/adapter-factory.ts +32 -6
  491. package/src/providers/inference/auth.ts +12 -0
  492. package/src/providers/inference/backfill.ts +14 -1
  493. package/src/providers/inference/connections.ts +159 -34
  494. package/src/providers/inference/resolve-auth.ts +14 -4
  495. package/src/providers/model-catalog.ts +249 -12
  496. package/src/providers/model-intents.ts +3 -3
  497. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +235 -0
  498. package/src/providers/openai/chat-completions-provider.ts +169 -8
  499. package/src/providers/openrouter/client.ts +49 -4
  500. package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -2
  501. package/src/providers/{managed-proxy → platform-proxy}/context.ts +3 -3
  502. package/src/providers/provider-availability.ts +17 -2
  503. package/src/providers/provider-catalog-visibility.ts +38 -0
  504. package/src/providers/provider-send-message.ts +27 -12
  505. package/src/providers/registry.ts +52 -15
  506. package/src/providers/retry.ts +47 -1
  507. package/src/runtime/__tests__/agent-wake.test.ts +152 -0
  508. package/src/runtime/agent-wake.ts +103 -15
  509. package/src/runtime/auth/route-policy.ts +21 -1
  510. package/src/runtime/btw-sidechain.ts +2 -0
  511. package/src/runtime/http-server.ts +7 -16
  512. package/src/runtime/http-types.ts +19 -47
  513. package/src/runtime/migrations/origin-mode.ts +1 -1
  514. package/src/runtime/pending-interactions.ts +1 -0
  515. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +17 -0
  516. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
  517. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +5 -1
  518. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +172 -23
  519. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +275 -44
  520. package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +12 -0
  521. package/src/runtime/routes/__tests__/question-routes.test.ts +395 -0
  522. package/src/runtime/routes/__tests__/tts-routes.test.ts +64 -1
  523. package/src/runtime/routes/acp-routes-list.test.ts +143 -0
  524. package/src/runtime/routes/acp-routes.ts +5 -3
  525. package/src/runtime/routes/auth-routes.ts +1 -1
  526. package/src/runtime/routes/bookmark-routes.ts +5 -3
  527. package/src/runtime/routes/btw-routes.ts +5 -1
  528. package/src/runtime/routes/channel-availability-routes.ts +126 -0
  529. package/src/runtime/routes/consolidation-routes.ts +100 -0
  530. package/src/runtime/routes/conversation-cli-routes.ts +44 -3
  531. package/src/runtime/routes/conversation-list-routes.ts +3 -20
  532. package/src/runtime/routes/conversation-management-routes.ts +17 -42
  533. package/src/runtime/routes/conversation-query-routes.ts +99 -35
  534. package/src/runtime/routes/conversation-routes.ts +97 -11
  535. package/src/runtime/routes/documents-routes.ts +25 -86
  536. package/src/runtime/routes/group-routes.ts +5 -0
  537. package/src/runtime/routes/inbound-conversation.ts +28 -8
  538. package/src/runtime/routes/inbound-message-handler.ts +236 -41
  539. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +111 -0
  540. package/src/runtime/routes/inbound-stages/background-dispatch.ts +32 -1
  541. package/src/runtime/routes/inbound-stages/edit-intercept.ts +17 -4
  542. package/src/runtime/routes/index.ts +8 -0
  543. package/src/runtime/routes/inference-profile-session-handler.ts +17 -44
  544. package/src/runtime/routes/inference-profile-session-reaper.ts +7 -21
  545. package/src/runtime/routes/inference-provider-connection-routes.ts +199 -22
  546. package/src/runtime/routes/integrations/a2a.ts +235 -0
  547. package/src/runtime/routes/integrations/slack/share.ts +4 -52
  548. package/src/runtime/routes/integrations/slack/token.ts +43 -0
  549. package/src/runtime/routes/integrations/twilio.ts +6 -13
  550. package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
  551. package/src/runtime/routes/notification-routes.ts +1 -1
  552. package/src/runtime/routes/oauth-commands-routes.ts +105 -15
  553. package/src/runtime/routes/oauth-lifecycle-routes.ts +43 -0
  554. package/src/runtime/routes/question-routes.ts +259 -0
  555. package/src/runtime/routes/rename-conversation-routes.ts +2 -33
  556. package/src/runtime/routes/schedule-routes.ts +4 -7
  557. package/src/runtime/routes/subagents-routes.ts +98 -18
  558. package/src/runtime/routes/telemetry-routes.ts +27 -0
  559. package/src/runtime/routes/tts-routes.ts +27 -2
  560. package/src/runtime/routes/workspace-routes.test.ts +43 -0
  561. package/src/runtime/routes/workspace-routes.ts +28 -0
  562. package/src/runtime/services/conversation-serializer.ts +39 -7
  563. package/src/runtime/sync/resource-sync-events.ts +93 -1
  564. package/src/schedule/schedule-store.ts +27 -2
  565. package/src/schedule/scheduler.ts +9 -1
  566. package/src/security/__tests__/untrusted-content.test.ts +86 -0
  567. package/src/security/untrusted-content.ts +93 -8
  568. package/src/skills/catalog-files.ts +1 -1
  569. package/src/skills/catalog-install.ts +233 -116
  570. package/src/skills/clawhub.ts +70 -13
  571. package/src/skills/managed-store.ts +4 -119
  572. package/src/skills/skillssh-registry.ts +27 -48
  573. package/src/subagent/manager.ts +17 -7
  574. package/src/telemetry/types.ts +113 -1
  575. package/src/telemetry/usage-telemetry-reporter.test.ts +312 -5
  576. package/src/telemetry/usage-telemetry-reporter.ts +113 -7
  577. package/src/tools/apps/executors.ts +58 -7
  578. package/src/tools/ask-question/ask-question-tool.test.ts +509 -0
  579. package/src/tools/ask-question/ask-question-tool.ts +304 -0
  580. package/src/tools/browser/browser-execution.ts +15 -11
  581. package/src/tools/computer-use/definitions.ts +3 -3
  582. package/src/tools/credentials/vault.ts +1 -1
  583. package/src/tools/document/document-tool.ts +124 -1
  584. package/src/tools/filesystem/edit.ts +1 -1
  585. package/src/tools/filesystem/list.ts +1 -1
  586. package/src/tools/filesystem/read.ts +1 -1
  587. package/src/tools/filesystem/write.ts +5 -2
  588. package/src/tools/host-filesystem/transfer.ts +1 -1
  589. package/src/tools/host-terminal/host-shell.ts +1 -1
  590. package/src/tools/memory/register.ts +1 -9
  591. package/src/tools/permission-checker.ts +1 -1
  592. package/src/tools/registry.ts +17 -7
  593. package/src/tools/schedule/create.ts +2 -2
  594. package/src/tools/schema-transforms.ts +7 -2
  595. package/src/tools/side-effects.ts +1 -0
  596. package/src/tools/skills/delete-managed.ts +4 -4
  597. package/src/tools/skills/execute.ts +1 -1
  598. package/src/tools/skills/scaffold-managed.ts +3 -2
  599. package/src/tools/subagent/notify-parent.ts +1 -1
  600. package/src/tools/system/request-permission.ts +2 -2
  601. package/src/tools/terminal/safe-env.ts +60 -1
  602. package/src/tools/tool-manifest.ts +2 -0
  603. package/src/tools/types.ts +107 -21
  604. package/src/tools/ui-surface/definitions.ts +6 -5
  605. package/src/tts/__tests__/provider-adapters.test.ts +76 -2
  606. package/src/tts/providers/elevenlabs-provider.ts +75 -1
  607. package/src/types/onboarding-context.ts +2 -0
  608. package/src/util/errors.ts +17 -0
  609. package/src/util/platform.ts +10 -0
  610. package/src/watcher/__tests__/engine.test.ts +22 -0
  611. package/src/watcher/engine.ts +6 -2
  612. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +80 -15
  613. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +35 -22
  614. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +3 -1
  615. package/src/workspace/migrations/083-system-prompt-prefix-to-file.ts +191 -0
  616. package/src/workspace/migrations/084-remove-legacy-skills-index.ts +276 -0
  617. package/src/workspace/migrations/085-memory-v2-bm25-b-reembed-disabled-v2-pages.ts +137 -0
  618. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +198 -0
  619. package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
  620. package/src/workspace/migrations/registry.ts +10 -0
  621. package/src/workspace/migrations/runner.ts +39 -9
  622. package/src/workspace/migrations/types.ts +4 -0
  623. package/examples/plugins/echo/bun.lock +0 -25
  624. package/src/__tests__/context-window-manager.test.ts +0 -2481
  625. package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
  626. package/src/context/__tests__/compact-prompt.test.ts +0 -63
  627. package/src/context/prompts/compact.md +0 -26
  628. package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
  629. package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
  630. package/src/runtime/guardian-action-conversation-turn.ts +0 -99
@@ -10,9 +10,18 @@
10
10
 
11
11
  import { z } from "zod";
12
12
 
13
+ import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
13
14
  import { getConfigReadOnly } from "../../config/loader.js";
14
15
  import { getDb } from "../../memory/db-connection.js";
15
- import { AuthSchema, ConnectionProviderSchema, ConnectionStatusSchema, ProviderConnectionSchema, VALID_CONNECTION_PROVIDERS } from "../../providers/inference/auth.js";
16
+ import {
17
+ AuthSchema,
18
+ type ConnectionModel,
19
+ ConnectionModelSchema,
20
+ ConnectionProviderSchema,
21
+ ConnectionStatusSchema,
22
+ ProviderConnectionSchema,
23
+ VALID_CONNECTION_PROVIDERS,
24
+ } from "../../providers/inference/auth.js";
16
25
  import {
17
26
  createConnection,
18
27
  deleteConnection,
@@ -21,11 +30,7 @@ import {
21
30
  MANAGED_CONNECTION_NAMES,
22
31
  updateConnection,
23
32
  } from "../../providers/inference/connections.js";
24
- import {
25
- BadRequestError,
26
- ConflictError,
27
- NotFoundError,
28
- } from "./errors.js";
33
+ import { BadRequestError, ConflictError, NotFoundError } from "./errors.js";
29
34
  import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
30
35
 
31
36
  // ---------------------------------------------------------------------------
@@ -33,6 +38,75 @@ import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
33
38
  // ---------------------------------------------------------------------------
34
39
 
35
40
  const providerConnectionResponseSchema = ProviderConnectionSchema;
41
+ const OPENAI_COMPATIBLE_ENDPOINTS_FLAG = "openai-compatible-endpoints";
42
+
43
+ function openAICompatibleEndpointsEnabled(): boolean {
44
+ return isAssistantFeatureFlagEnabled(
45
+ OPENAI_COMPATIBLE_ENDPOINTS_FLAG,
46
+ getConfigReadOnly(),
47
+ );
48
+ }
49
+
50
+ function rejectDisabledOpenAICompatibleProvider(provider: string): void {
51
+ if (provider !== "openai-compatible") return;
52
+ if (openAICompatibleEndpointsEnabled()) return;
53
+ throw new BadRequestError(
54
+ "OpenAI-compatible endpoints are disabled. Enable the openai-compatible-endpoints feature flag to configure this provider.",
55
+ );
56
+ }
57
+
58
+ // ---------------------------------------------------------------------------
59
+ // Custom provider field parsing (openai-compatible base_url + models)
60
+ // ---------------------------------------------------------------------------
61
+
62
+ function parseCustomProviderFields(body: Record<string, unknown>): {
63
+ baseUrl?: string | null;
64
+ models?: ConnectionModel[] | null;
65
+ } {
66
+ const out: {
67
+ baseUrl?: string | null;
68
+ models?: ConnectionModel[] | null;
69
+ } = {};
70
+
71
+ if ("base_url" in body) {
72
+ const raw = body.base_url;
73
+ if (raw === null) {
74
+ out.baseUrl = null;
75
+ } else if (typeof raw === "string" && raw.length > 0) {
76
+ try {
77
+ const parsed = new URL(raw);
78
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
79
+ throw new BadRequestError(`Invalid base_url: must be an http(s) URL`);
80
+ }
81
+ } catch (err) {
82
+ if (err instanceof BadRequestError) throw err;
83
+ throw new BadRequestError(
84
+ `Invalid base_url: must be a valid http(s) URL`,
85
+ );
86
+ }
87
+ out.baseUrl = raw;
88
+ } else {
89
+ throw new BadRequestError(
90
+ `Invalid base_url: must be a non-empty string or null`,
91
+ );
92
+ }
93
+ }
94
+
95
+ if ("models" in body) {
96
+ const raw = body.models;
97
+ if (raw === null) {
98
+ out.models = null;
99
+ } else {
100
+ const parsed = z.array(ConnectionModelSchema).safeParse(raw);
101
+ if (!parsed.success) {
102
+ throw new BadRequestError(`Invalid models: ${parsed.error.message}`);
103
+ }
104
+ out.models = parsed.data;
105
+ }
106
+ }
107
+
108
+ return out;
109
+ }
36
110
 
37
111
  // ---------------------------------------------------------------------------
38
112
  // Handlers
@@ -40,7 +114,18 @@ const providerConnectionResponseSchema = ProviderConnectionSchema;
40
114
 
41
115
  function handleListConnections({ queryParams = {} }: RouteHandlerArgs) {
42
116
  const provider = queryParams.provider;
43
- const connections = listConnections(getDb(), provider ? { provider } : undefined);
117
+ if (provider) rejectDisabledOpenAICompatibleProvider(provider);
118
+ const connections = listConnections(
119
+ getDb(),
120
+ provider ? { provider } : undefined,
121
+ );
122
+ if (!openAICompatibleEndpointsEnabled()) {
123
+ return {
124
+ connections: connections.filter(
125
+ (conn) => conn.provider !== "openai-compatible",
126
+ ),
127
+ };
128
+ }
44
129
  return { connections };
45
130
  }
46
131
 
@@ -50,6 +135,12 @@ function handleGetConnection({ pathParams = {} }: RouteHandlerArgs) {
50
135
 
51
136
  const conn = getConnection(getDb(), name);
52
137
  if (!conn) throw new NotFoundError(`Connection "${name}" not found.`);
138
+ if (
139
+ conn.provider === "openai-compatible" &&
140
+ !openAICompatibleEndpointsEnabled()
141
+ ) {
142
+ throw new NotFoundError(`Connection "${name}" not found.`);
143
+ }
53
144
 
54
145
  return conn;
55
146
  }
@@ -69,28 +160,41 @@ function handleCreateConnection({ body = {} }: RouteHandlerArgs) {
69
160
  `Invalid provider "${String(provider)}". Valid: ${VALID_CONNECTION_PROVIDERS.join(", ")}`,
70
161
  );
71
162
  }
163
+ rejectDisabledOpenAICompatibleProvider(providerResult.data);
72
164
 
73
165
  const authResult = AuthSchema.safeParse(auth);
74
166
  if (!authResult.success) {
75
167
  throw new BadRequestError(`Invalid auth: ${authResult.error.message}`);
76
168
  }
77
169
 
78
- const statusResult = body.status !== undefined ? ConnectionStatusSchema.safeParse(body.status) : null;
170
+ const statusResult =
171
+ body.status !== undefined
172
+ ? ConnectionStatusSchema.safeParse(body.status)
173
+ : null;
79
174
  if (statusResult && !statusResult.success) {
80
175
  throw new BadRequestError(`Invalid status: must be "active" or "disabled"`);
81
176
  }
82
177
 
83
178
  const labelRaw = body.label;
84
- if (labelRaw !== undefined && labelRaw !== null && (typeof labelRaw !== "string" || labelRaw.length === 0)) {
85
- throw new BadRequestError(`Invalid label: must be a non-empty string or null`);
179
+ if (
180
+ labelRaw !== undefined &&
181
+ labelRaw !== null &&
182
+ (typeof labelRaw !== "string" || labelRaw.length === 0)
183
+ ) {
184
+ throw new BadRequestError(
185
+ `Invalid label: must be a non-empty string or null`,
186
+ );
86
187
  }
87
188
 
189
+ const customFields = parseCustomProviderFields(body);
190
+
88
191
  const result = createConnection(getDb(), {
89
192
  name,
90
193
  provider: providerResult.data,
91
194
  auth: authResult.data,
92
195
  ...(statusResult ? { status: statusResult.data } : {}),
93
196
  ...(labelRaw !== undefined ? { label: labelRaw as string | null } : {}),
197
+ ...customFields,
94
198
  });
95
199
 
96
200
  if (!result.ok) {
@@ -104,52 +208,99 @@ function handleCreateConnection({ body = {} }: RouteHandlerArgs) {
104
208
  `Invalid provider "${result.error.provider}". Valid: ${VALID_CONNECTION_PROVIDERS.join(", ")}`,
105
209
  );
106
210
  }
211
+ if (result.error.code === "base_url_required") {
212
+ throw new BadRequestError(
213
+ "base_url is required for openai-compatible connections.",
214
+ );
215
+ }
216
+ if (result.error.code === "models_required") {
217
+ throw new BadRequestError(
218
+ "At least one model is required for openai-compatible connections.",
219
+ );
220
+ }
107
221
  throw new BadRequestError("Invalid auth configuration.");
108
222
  }
109
223
 
110
224
  return result.connection;
111
225
  }
112
226
 
113
- function handleUpdateConnection({ pathParams = {}, body = {} }: RouteHandlerArgs) {
227
+ function handleUpdateConnection({
228
+ pathParams = {},
229
+ body = {},
230
+ }: RouteHandlerArgs) {
114
231
  const { name } = pathParams;
115
232
  if (!name) throw new BadRequestError("name is required");
116
233
 
234
+ const existing = getConnection(getDb(), name);
235
+ if (!existing) throw new NotFoundError(`Connection "${name}" not found.`);
236
+ if (
237
+ existing.provider === "openai-compatible" &&
238
+ !openAICompatibleEndpointsEnabled()
239
+ ) {
240
+ throw new NotFoundError(`Connection "${name}" not found.`);
241
+ }
242
+
117
243
  const auth = body.auth;
118
244
  const authResult = AuthSchema.safeParse(auth);
119
245
  if (!authResult.success) {
120
246
  throw new BadRequestError(`Invalid auth: ${authResult.error.message}`);
121
247
  }
122
248
 
123
- const statusResult = body.status !== undefined ? ConnectionStatusSchema.safeParse(body.status) : null;
249
+ const statusResult =
250
+ body.status !== undefined
251
+ ? ConnectionStatusSchema.safeParse(body.status)
252
+ : null;
124
253
  if (statusResult && !statusResult.success) {
125
254
  throw new BadRequestError(`Invalid status: must be "active" or "disabled"`);
126
255
  }
127
256
 
128
257
  const labelRaw = body.label;
129
- if (labelRaw !== undefined && labelRaw !== null && (typeof labelRaw !== "string" || labelRaw.length === 0)) {
130
- throw new BadRequestError(`Invalid label: must be a non-empty string or null`);
258
+ if (
259
+ labelRaw !== undefined &&
260
+ labelRaw !== null &&
261
+ (typeof labelRaw !== "string" || labelRaw.length === 0)
262
+ ) {
263
+ throw new BadRequestError(
264
+ `Invalid label: must be a non-empty string or null`,
265
+ );
131
266
  }
132
267
 
133
268
  // Managed connections: lock auth to `{type:"platform"}`. The boot upsert in
134
269
  // `seedCanonicalConnections` would revert any other value on next restart;
135
270
  // reject the write here so the surprise loop never happens. Label and status
136
271
  // remain user-editable (the boot upsert leaves those alone).
137
- if (MANAGED_CONNECTION_NAMES.has(name) && authResult.data.type !== "platform") {
272
+ if (
273
+ MANAGED_CONNECTION_NAMES.has(name) &&
274
+ authResult.data.type !== "platform"
275
+ ) {
138
276
  throw new BadRequestError(
139
277
  `Cannot change auth on managed connection "${name}". Auth is locked to platform.`,
140
278
  );
141
279
  }
142
280
 
281
+ const customFields = parseCustomProviderFields(body);
282
+
143
283
  const result = updateConnection(getDb(), name, {
144
284
  auth: authResult.data,
145
285
  ...(statusResult ? { status: statusResult.data } : {}),
146
286
  ...(labelRaw !== undefined ? { label: labelRaw as string | null } : {}),
287
+ ...customFields,
147
288
  });
148
289
 
149
290
  if (!result.ok) {
150
291
  if (result.error.code === "not_found") {
151
292
  throw new NotFoundError(`Connection "${name}" not found.`);
152
293
  }
294
+ if (result.error.code === "base_url_required") {
295
+ throw new BadRequestError(
296
+ "base_url is required for openai-compatible connections.",
297
+ );
298
+ }
299
+ if (result.error.code === "models_required") {
300
+ throw new BadRequestError(
301
+ "At least one model is required for openai-compatible connections.",
302
+ );
303
+ }
153
304
  throw new BadRequestError("Invalid auth configuration.");
154
305
  }
155
306
 
@@ -162,7 +313,14 @@ function handleDeleteConnection({ pathParams = {} }: RouteHandlerArgs) {
162
313
 
163
314
  // Existence check first so a stale `llm.default.provider_connection`
164
315
  // reference to a missing connection returns 404 (not 409).
165
- if (!getConnection(getDb(), name)) {
316
+ const existing = getConnection(getDb(), name);
317
+ if (!existing) {
318
+ throw new NotFoundError(`Connection "${name}" not found.`);
319
+ }
320
+ if (
321
+ existing.provider === "openai-compatible" &&
322
+ !openAICompatibleEndpointsEnabled()
323
+ ) {
166
324
  throw new NotFoundError(`Connection "${name}" not found.`);
167
325
  }
168
326
 
@@ -180,7 +338,10 @@ function handleDeleteConnection({ pathParams = {} }: RouteHandlerArgs) {
180
338
  const config = getConfigReadOnly();
181
339
 
182
340
  // llm.default carries provider_connection (LLMConfigBase).
183
- if ((config.llm?.default as Record<string, unknown> | undefined)?.provider_connection === name) {
341
+ if (
342
+ (config.llm?.default as Record<string, unknown> | undefined)
343
+ ?.provider_connection === name
344
+ ) {
184
345
  throw new ConflictError(
185
346
  `Connection "${name}" is referenced by llm.default. Update llm.default.provider_connection before deleting.`,
186
347
  { referencedBy: ["llm.default"] },
@@ -190,7 +351,9 @@ function handleDeleteConnection({ pathParams = {} }: RouteHandlerArgs) {
190
351
  // llm.profiles.*: only ProfileEntry has provider_connection.
191
352
  const profiles = config.llm?.profiles ?? {};
192
353
  const referencingProfiles = Object.entries(profiles)
193
- .filter(([, p]) => (p as Record<string, unknown>).provider_connection === name)
354
+ .filter(
355
+ ([, p]) => (p as Record<string, unknown>).provider_connection === name,
356
+ )
194
357
  .map(([profileName]) => profileName);
195
358
 
196
359
  const result = deleteConnection(getDb(), name, {
@@ -234,7 +397,9 @@ export const ROUTES: RouteDefinition[] = [
234
397
  description: `Filter by provider. One of: ${VALID_CONNECTION_PROVIDERS.join(", ")}`,
235
398
  },
236
399
  ],
237
- responseBody: z.object({ connections: z.array(providerConnectionResponseSchema) }),
400
+ responseBody: z.object({
401
+ connections: z.array(providerConnectionResponseSchema),
402
+ }),
238
403
  handler: handleListConnections,
239
404
  },
240
405
  {
@@ -265,6 +430,8 @@ export const ROUTES: RouteDefinition[] = [
265
430
  auth: AuthSchema,
266
431
  label: z.string().min(1).optional(),
267
432
  status: ConnectionStatusSchema.optional(),
433
+ base_url: z.string().url().nullable().optional(),
434
+ models: z.array(ConnectionModelSchema).nullable().optional(),
268
435
  }),
269
436
  responseBody: providerConnectionResponseSchema,
270
437
  responseStatus: "201",
@@ -288,10 +455,15 @@ export const ROUTES: RouteDefinition[] = [
288
455
  auth: AuthSchema,
289
456
  status: ConnectionStatusSchema.optional(),
290
457
  label: z.string().min(1).nullable().optional(),
458
+ base_url: z.string().url().nullable().optional(),
459
+ models: z.array(ConnectionModelSchema).nullable().optional(),
291
460
  }),
292
461
  responseBody: providerConnectionResponseSchema,
293
462
  additionalResponses: {
294
- "400": { description: "Invalid auth schema, or attempt to change auth on a managed connection" },
463
+ "400": {
464
+ description:
465
+ "Invalid auth schema, or attempt to change auth on a managed connection",
466
+ },
295
467
  "404": { description: "Connection not found" },
296
468
  },
297
469
  handler: handleUpdateConnection,
@@ -308,9 +480,14 @@ export const ROUTES: RouteDefinition[] = [
308
480
  pathParams: [{ name: "name", description: "Connection name" }],
309
481
  responseBody: z.object({ ok: z.literal(true) }),
310
482
  additionalResponses: {
311
- "400": { description: "Connection is a Vellum-managed connection and cannot be deleted" },
483
+ "400": {
484
+ description:
485
+ "Connection is a Vellum-managed connection and cannot be deleted",
486
+ },
312
487
  "404": { description: "Connection not found" },
313
- "409": { description: "Connection is referenced by profile(s) or call site(s)" },
488
+ "409": {
489
+ description: "Connection is referenced by profile(s) or call site(s)",
490
+ },
314
491
  },
315
492
  handler: handleDeleteConnection,
316
493
  },
@@ -0,0 +1,235 @@
1
+ /**
2
+ * Route handlers for A2A integration config endpoints.
3
+ *
4
+ * GET /v1/integrations/a2a/config — get current A2A config status
5
+ * POST /v1/integrations/a2a/config — enable A2A channel
6
+ * DELETE /v1/integrations/a2a/config — disable A2A channel
7
+ * POST /v1/integrations/a2a/invite — create a shareable A2A invite token
8
+ * POST /v1/integrations/a2a/invite/complete — sender-side invite completion
9
+ * POST /v1/integrations/a2a/invite/redeem — receiver-side invite redemption
10
+ */
11
+
12
+ import { isA2AEnabled } from "../../../a2a/feature-gate.js";
13
+ import { getConfig } from "../../../config/loader.js";
14
+ import {
15
+ clearA2AConfig,
16
+ completeA2AInvite,
17
+ createA2AInvite,
18
+ getA2AConfig,
19
+ redeemA2AInvite,
20
+ setA2AConfig,
21
+ } from "../../../daemon/handlers/config-a2a.js";
22
+ import { BadRequestError } from "../errors.js";
23
+ import type { RouteDefinition, RouteHandlerArgs } from "../types.js";
24
+
25
+ // ---------------------------------------------------------------------------
26
+ // Helpers
27
+ // ---------------------------------------------------------------------------
28
+
29
+ function assertA2AFlag(): void {
30
+ if (!isA2AEnabled(getConfig())) {
31
+ throw new BadRequestError("A2A channel is not available");
32
+ }
33
+ }
34
+
35
+ // ---------------------------------------------------------------------------
36
+ // Handlers
37
+ // ---------------------------------------------------------------------------
38
+
39
+ function handleGetA2AConfig() {
40
+ assertA2AFlag();
41
+ return getA2AConfig();
42
+ }
43
+
44
+ function handleSetA2AConfig() {
45
+ assertA2AFlag();
46
+ const result = setA2AConfig();
47
+ if (!result.success) {
48
+ throw new BadRequestError(result.error ?? "Failed to enable A2A");
49
+ }
50
+ return result;
51
+ }
52
+
53
+ function handleClearA2AConfig() {
54
+ assertA2AFlag();
55
+ return clearA2AConfig();
56
+ }
57
+
58
+ function handleCreateA2AInvite({ body = {} }: RouteHandlerArgs) {
59
+ assertA2AFlag();
60
+ const { expiresInHours } = body as { expiresInHours?: unknown };
61
+ if (expiresInHours !== undefined) {
62
+ if (
63
+ typeof expiresInHours !== "number" ||
64
+ !Number.isFinite(expiresInHours) ||
65
+ expiresInHours <= 0
66
+ ) {
67
+ throw new BadRequestError(
68
+ "expiresInHours must be a positive finite number",
69
+ );
70
+ }
71
+ }
72
+ const result = createA2AInvite({
73
+ expiresInHours: expiresInHours as number | undefined,
74
+ });
75
+ if (!result.success) {
76
+ throw new BadRequestError(result.error ?? "Failed to create A2A invite");
77
+ }
78
+ return result;
79
+ }
80
+
81
+ function handleCompleteA2AInvite({ body = {} }: RouteHandlerArgs) {
82
+ const { token, senderAssistantId, acceptor } = body as {
83
+ token?: unknown;
84
+ senderAssistantId?: unknown;
85
+ acceptor?: {
86
+ assistantId?: unknown;
87
+ displayName?: unknown;
88
+ gatewayUrl?: unknown;
89
+ };
90
+ };
91
+
92
+ if (typeof token !== "string" || !token) {
93
+ throw new BadRequestError(
94
+ "token is required and must be a non-empty string",
95
+ );
96
+ }
97
+ if (typeof senderAssistantId !== "string" || !senderAssistantId) {
98
+ throw new BadRequestError(
99
+ "senderAssistantId is required and must be a non-empty string",
100
+ );
101
+ }
102
+ if (
103
+ !acceptor ||
104
+ typeof acceptor.assistantId !== "string" ||
105
+ !acceptor.assistantId ||
106
+ typeof acceptor.displayName !== "string" ||
107
+ !acceptor.displayName ||
108
+ typeof acceptor.gatewayUrl !== "string" ||
109
+ !acceptor.gatewayUrl
110
+ ) {
111
+ throw new BadRequestError(
112
+ "acceptor must include non-empty assistantId, displayName, and gatewayUrl",
113
+ );
114
+ }
115
+
116
+ const result = completeA2AInvite({
117
+ token,
118
+ senderAssistantId,
119
+ acceptor: {
120
+ assistantId: acceptor.assistantId,
121
+ displayName: acceptor.displayName,
122
+ gatewayUrl: acceptor.gatewayUrl,
123
+ },
124
+ });
125
+ if (!result.success) {
126
+ throw new BadRequestError(result.error ?? "Failed to complete A2A invite");
127
+ }
128
+ return result;
129
+ }
130
+
131
+ function handleRedeemA2AInvite({ body = {} }: RouteHandlerArgs) {
132
+ const { sender } = body as {
133
+ sender?: {
134
+ assistantId?: unknown;
135
+ displayName?: unknown;
136
+ gatewayUrl?: unknown;
137
+ };
138
+ };
139
+
140
+ if (
141
+ !sender ||
142
+ typeof sender.assistantId !== "string" ||
143
+ !sender.assistantId ||
144
+ typeof sender.displayName !== "string" ||
145
+ !sender.displayName ||
146
+ typeof sender.gatewayUrl !== "string" ||
147
+ !sender.gatewayUrl
148
+ ) {
149
+ throw new BadRequestError(
150
+ "sender must include non-empty assistantId, displayName, and gatewayUrl",
151
+ );
152
+ }
153
+
154
+ const result = redeemA2AInvite({
155
+ sender: {
156
+ assistantId: sender.assistantId,
157
+ displayName: sender.displayName,
158
+ gatewayUrl: sender.gatewayUrl,
159
+ },
160
+ });
161
+ if (!result.success) {
162
+ throw new BadRequestError(result.error ?? "Failed to redeem A2A invite");
163
+ }
164
+ return result;
165
+ }
166
+
167
+ // ---------------------------------------------------------------------------
168
+ // Route definitions
169
+ // ---------------------------------------------------------------------------
170
+
171
+ export const ROUTES: RouteDefinition[] = [
172
+ {
173
+ operationId: "integrations_a2a_config_get",
174
+ endpoint: "integrations/a2a/config",
175
+ method: "GET",
176
+ summary: "Get A2A config",
177
+ description: "Check current A2A channel configuration status.",
178
+ tags: ["integrations"],
179
+ requirePolicyEnforcement: true,
180
+ handler: () => handleGetA2AConfig(),
181
+ },
182
+ {
183
+ operationId: "integrations_a2a_config_post",
184
+ endpoint: "integrations/a2a/config",
185
+ method: "POST",
186
+ summary: "Enable A2A channel",
187
+ description: "Enable the A2A channel for inter-assistant communication.",
188
+ tags: ["integrations"],
189
+ requirePolicyEnforcement: true,
190
+ handler: () => handleSetA2AConfig(),
191
+ },
192
+ {
193
+ operationId: "integrations_a2a_config_delete",
194
+ endpoint: "integrations/a2a/config",
195
+ method: "DELETE",
196
+ summary: "Disable A2A channel",
197
+ description: "Disable the A2A channel.",
198
+ tags: ["integrations"],
199
+ requirePolicyEnforcement: true,
200
+ handler: () => handleClearA2AConfig(),
201
+ },
202
+ {
203
+ operationId: "integrations_a2a_invite_post",
204
+ endpoint: "integrations/a2a/invite",
205
+ method: "POST",
206
+ summary: "Create A2A invite",
207
+ description:
208
+ "Create a shareable A2A invite token for link-based contact creation.",
209
+ tags: ["integrations"],
210
+ requirePolicyEnforcement: true,
211
+ handler: handleCreateA2AInvite,
212
+ },
213
+ {
214
+ operationId: "integrations_a2a_invite_complete_post",
215
+ endpoint: "integrations/a2a/invite/complete",
216
+ method: "POST",
217
+ summary: "Complete A2A invite (sender side)",
218
+ description:
219
+ "Called by the platform to finalize the sender side of a link-based A2A connection.",
220
+ tags: ["integrations"],
221
+ requirePolicyEnforcement: true,
222
+ handler: handleCompleteA2AInvite,
223
+ },
224
+ {
225
+ operationId: "integrations_a2a_invite_redeem_post",
226
+ endpoint: "integrations/a2a/invite/redeem",
227
+ method: "POST",
228
+ summary: "Redeem A2A invite (receiver side)",
229
+ description:
230
+ "Called by the platform to create a trusted contact on the receiver side of a link-based A2A connection.",
231
+ tags: ["integrations"],
232
+ requirePolicyEnforcement: true,
233
+ handler: handleRedeemA2AInvite,
234
+ },
235
+ ];