@vellumai/assistant 0.8.1 → 0.8.2

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 (506) hide show
  1. package/ARCHITECTURE.md +2 -7
  2. package/Dockerfile +75 -1
  3. package/bun.lock +11 -1
  4. package/docker-entrypoint.sh +5 -0
  5. package/docker-init-apt-root.sh +94 -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 +325 -3
  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-wake-disk-pressure-callsite.test.ts +131 -0
  21. package/src/__tests__/anthropic-provider.test.ts +45 -0
  22. package/src/__tests__/app-builder-tool-scripts.test.ts +9 -3
  23. package/src/__tests__/app-executors.test.ts +220 -4
  24. package/src/__tests__/auto-analysis-end-to-end.test.ts +35 -0
  25. package/src/__tests__/bundled-asset.test.ts +6 -6
  26. package/src/__tests__/channel-availability-routes.test.ts +206 -0
  27. package/src/__tests__/channel-delivery-store.test.ts +289 -1
  28. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -1
  29. package/src/__tests__/clawhub.test.ts +75 -16
  30. package/src/__tests__/compactor-tail-resolution.test.ts +41 -0
  31. package/src/__tests__/config-schema.test.ts +21 -0
  32. package/src/__tests__/config-set-route.test.ts +80 -0
  33. package/src/__tests__/config-sounds-sync.test.ts +97 -0
  34. package/src/__tests__/config-watcher-skill-reseed.test.ts +453 -0
  35. package/src/__tests__/context-search-conversations-source.test.ts +117 -2
  36. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
  37. package/src/__tests__/context-search-workspace-source.test.ts +7 -0
  38. package/src/__tests__/context-token-estimator.test.ts +1 -0
  39. package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
  40. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
  41. package/src/__tests__/conversation-agent-loop-overflow.test.ts +92 -92
  42. package/src/__tests__/conversation-agent-loop.test.ts +2 -0
  43. package/src/__tests__/conversation-error.test.ts +42 -3
  44. package/src/__tests__/conversation-fork-crud.test.ts +82 -0
  45. package/src/__tests__/conversation-inference-profile-route.test.ts +40 -4
  46. package/src/__tests__/conversation-lifecycle.test.ts +173 -0
  47. package/src/__tests__/conversation-message-sync-tags.test.ts +97 -0
  48. package/src/__tests__/conversation-pairing.test.ts +54 -0
  49. package/src/__tests__/conversation-process-callsite.test.ts +4 -1
  50. package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -1
  51. package/src/__tests__/conversation-queue.test.ts +4 -1
  52. package/src/__tests__/conversation-runtime-assembly.test.ts +76 -9
  53. package/src/__tests__/conversation-slash-queue.test.ts +59 -1
  54. package/src/__tests__/conversation-slash-unknown.test.ts +4 -1
  55. package/src/__tests__/conversation-surfaces-table-action.test.ts +360 -0
  56. package/src/__tests__/conversation-sync-tags.test.ts +235 -0
  57. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  58. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  59. package/src/__tests__/credential-security-invariants.test.ts +3 -2
  60. package/src/__tests__/db-slack-external-content-normalization.test.ts +301 -0
  61. package/src/__tests__/delete-managed-skill-tool.test.ts +55 -13
  62. package/src/__tests__/disk-pressure-tools.test.ts +1 -0
  63. package/src/__tests__/dm-backfill.test.ts +121 -10
  64. package/src/__tests__/document-tool-security.test.ts +258 -0
  65. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  66. package/src/__tests__/edit-propagation.test.ts +33 -0
  67. package/src/__tests__/empty-response-pipeline.test.ts +0 -4
  68. package/src/__tests__/external-plugin-loader.test.ts +60 -36
  69. package/src/__tests__/filing-service.test.ts +140 -0
  70. package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
  71. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
  72. package/src/__tests__/helpers/tar-fixtures.ts +39 -0
  73. package/src/__tests__/helpers/wait-for.ts +21 -0
  74. package/src/__tests__/history-repair-pipeline.test.ts +0 -3
  75. package/src/__tests__/history-repair.test.ts +73 -0
  76. package/src/__tests__/host-app-control-proxy.test.ts +266 -10
  77. package/src/__tests__/image-credentials.test.ts +1 -1
  78. package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
  79. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +1 -1
  80. package/src/__tests__/inference-profile-reaper.test.ts +4 -2
  81. package/src/__tests__/inference-profile-session-handler.test.ts +18 -6
  82. package/src/__tests__/inference-profile-session-ipc.test.ts +17 -5
  83. package/src/__tests__/injector-chain.test.ts +10 -8
  84. package/src/__tests__/install-skill-routing.test.ts +155 -37
  85. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +92 -3
  86. package/src/__tests__/list-messages-page-latest.test.ts +55 -0
  87. package/src/__tests__/llm-call-pipeline.test.ts +0 -3
  88. package/src/__tests__/llm-catalog-parity.test.ts +55 -13
  89. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +34 -0
  90. package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
  91. package/src/__tests__/llm-usage-store.test.ts +114 -0
  92. package/src/__tests__/managed-profile-guard.test.ts +31 -29
  93. package/src/__tests__/managed-skill-lifecycle.test.ts +109 -18
  94. package/src/__tests__/managed-store.test.ts +84 -192
  95. package/src/__tests__/media-generate-image.test.ts +1 -1
  96. package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -2
  97. package/src/__tests__/messages-after-tiebreaker.test.ts +122 -0
  98. package/src/__tests__/oauth-commands-routes.test.ts +168 -16
  99. package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
  100. package/src/__tests__/openai-provider.test.ts +24 -0
  101. package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
  102. package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -2
  103. package/src/__tests__/persistence-pipeline.test.ts +0 -2
  104. package/src/__tests__/{managed-proxy-context.test.ts → platform-proxy-context.test.ts} +1 -1
  105. package/src/__tests__/platform.test.ts +2 -0
  106. package/src/__tests__/plugin-api-shim.test.ts +125 -0
  107. package/src/__tests__/plugin-bootstrap.test.ts +10 -36
  108. package/src/__tests__/plugin-external-api.test.ts +68 -0
  109. package/src/__tests__/plugin-registry.test.ts +0 -77
  110. package/src/__tests__/plugin-route-contribution.test.ts +0 -1
  111. package/src/__tests__/plugin-skill-contribution.test.ts +0 -2
  112. package/src/__tests__/plugin-tool-contribution.test.ts +16 -15
  113. package/src/__tests__/plugin-types.test.ts +3 -13
  114. package/src/__tests__/process-message-background-slack.test.ts +8 -1
  115. package/src/__tests__/process-message-display-content.test.ts +421 -0
  116. package/src/__tests__/provider-catalog-visibility.test.ts +142 -0
  117. package/src/__tests__/provider-error-scenarios.test.ts +111 -0
  118. package/src/__tests__/{provider-managed-proxy-integration.test.ts → provider-platform-proxy-integration.test.ts} +8 -8
  119. package/src/__tests__/scaffold-managed-skill-tool.test.ts +65 -13
  120. package/src/__tests__/schedule-routes.test.ts +50 -3
  121. package/src/__tests__/schedule-store.test.ts +94 -0
  122. package/src/__tests__/scheduler-reuse-conversation.test.ts +54 -7
  123. package/src/__tests__/schema-transforms.test.ts +20 -0
  124. package/src/__tests__/search-skills-unified.test.ts +0 -5
  125. package/src/__tests__/server-history-render.test.ts +43 -0
  126. package/src/__tests__/skill-load-feature-flag.test.ts +0 -12
  127. package/src/__tests__/skill-load-tool.test.ts +27 -89
  128. package/src/__tests__/skill-memory.test.ts +23 -3
  129. package/src/__tests__/skills-file-content-endpoint.test.ts +9 -38
  130. package/src/__tests__/skills-files-catalog-fallback.test.ts +0 -3
  131. package/src/__tests__/skills-install-extract.test.ts +49 -38
  132. package/src/__tests__/skills-install-staging.test.ts +159 -0
  133. package/src/__tests__/skills-uninstall.test.ts +9 -41
  134. package/src/__tests__/skills.test.ts +51 -58
  135. package/src/__tests__/slack-channel-config.test.ts +9 -0
  136. package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
  137. package/src/__tests__/system-prompt.test.ts +737 -63
  138. package/src/__tests__/terminal-tools.test.ts +28 -1
  139. package/src/__tests__/thread-backfill.test.ts +557 -27
  140. package/src/__tests__/title-generate-pipeline.test.ts +0 -13
  141. package/src/__tests__/token-estimate-pipeline.test.ts +0 -3
  142. package/src/__tests__/tool-error-pipeline.test.ts +0 -3
  143. package/src/__tests__/tool-execute-pipeline.test.ts +0 -5
  144. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  145. package/src/__tests__/tool-executor.test.ts +16 -4
  146. package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -12
  147. package/src/__tests__/turn-events-store.test.ts +256 -0
  148. package/src/__tests__/twilio-routes.test.ts +4 -0
  149. package/src/__tests__/user-plugin-loader.test.ts +0 -7
  150. package/src/__tests__/voice-session-bridge.test.ts +198 -0
  151. package/src/__tests__/web-search-catalog-parity.test.ts +32 -10
  152. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +115 -3
  153. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +50 -0
  154. package/src/__tests__/workspace-migration-073-repair-recall-callsite-empty-profile.test.ts +153 -0
  155. package/src/__tests__/workspace-migration-085-memory-v2-bm25-b-reembed-disabled-v2-pages.test.ts +220 -0
  156. package/src/__tests__/workspace-migration-086-revert-stale-gemini-mis-rewrites.test.ts +269 -0
  157. package/src/__tests__/workspace-migration-remove-legacy-skills-index.test.ts +309 -0
  158. package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
  159. package/src/acp/resolve-agent.ts +1 -1
  160. package/src/agent/image-optimize.ts +13 -5
  161. package/src/calls/voice-session-bridge.ts +61 -42
  162. package/src/channels/types.ts +108 -0
  163. package/src/cli/__tests__/unknown-command.test.ts +24 -0
  164. package/src/cli/commands/__tests__/changelog.test.ts +304 -319
  165. package/src/cli/commands/__tests__/schedules.test.ts +491 -0
  166. package/src/cli/commands/changelog.ts +106 -42
  167. package/src/cli/commands/conversations.ts +102 -17
  168. package/src/cli/commands/default-action.ts +10 -53
  169. package/src/cli/commands/notifications.ts +329 -317
  170. package/src/cli/commands/plugins.ts +185 -0
  171. package/src/cli/commands/schedules.ts +391 -0
  172. package/src/cli/commands/telemetry.ts +40 -0
  173. package/src/cli/lib/__tests__/cli-colors.test.ts +48 -0
  174. package/src/cli/lib/__tests__/confirm-prompt.test.ts +159 -0
  175. package/src/cli/lib/__tests__/install-from-github.test.ts +355 -0
  176. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +154 -0
  177. package/src/cli/lib/__tests__/uninstall-plugin.test.ts +124 -0
  178. package/src/cli/lib/__tests__/unknown-command.test.ts +106 -0
  179. package/src/cli/lib/cli-colors.ts +12 -0
  180. package/src/cli/lib/confirm-prompt.ts +79 -0
  181. package/src/cli/lib/install-from-github.ts +304 -0
  182. package/src/cli/lib/list-installed-plugins.ts +137 -0
  183. package/src/cli/lib/uninstall-plugin.ts +82 -0
  184. package/src/cli/lib/unknown-command.ts +111 -0
  185. package/src/cli/program.ts +38 -2
  186. package/src/config/bundled-skills/app-builder/SKILL.md +23 -21
  187. package/src/config/bundled-skills/app-builder/TOOLS.json +7 -0
  188. package/src/config/bundled-skills/computer-use/TOOLS.json +15 -52
  189. package/src/config/bundled-skills/document/SKILL.md +23 -3
  190. package/src/config/bundled-skills/document/TOOLS.json +53 -0
  191. package/src/config/bundled-skills/document/tools/document-delete.ts +12 -0
  192. package/src/config/bundled-skills/document/tools/document-list.ts +12 -0
  193. package/src/config/bundled-skills/document/tools/document-read.ts +12 -0
  194. package/src/config/bundled-skills/skill-management/SKILL.md +2 -2
  195. package/src/config/bundled-skills/skill-management/TOOLS.json +7 -7
  196. package/src/config/bundled-tool-registry.ts +6 -0
  197. package/src/config/feature-flag-registry.json +41 -1
  198. package/src/config/loader.ts +64 -38
  199. package/src/config/schema.ts +7 -10
  200. package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
  201. package/src/config/schemas/channels.ts +8 -0
  202. package/src/config/schemas/compaction.ts +28 -0
  203. package/src/config/schemas/heartbeat.ts +9 -0
  204. package/src/config/schemas/llm-request-logs.ts +31 -7
  205. package/src/config/schemas/llm.ts +3 -0
  206. package/src/config/schemas/memory-retrieval.ts +18 -0
  207. package/src/config/schemas/tools.ts +14 -0
  208. package/src/config/skills.ts +3 -96
  209. package/src/context/compactor.ts +1047 -0
  210. package/src/context/token-estimator.ts +2 -2
  211. package/src/context/window-manager.ts +197 -1520
  212. package/src/credential-execution/managed-catalog.ts +37 -0
  213. package/src/credential-health/credential-health-service.ts +280 -19
  214. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +34 -0
  215. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +138 -0
  216. package/src/daemon/__tests__/conversation-tool-setup.test.ts +74 -0
  217. package/src/daemon/approval-generators.ts +8 -6
  218. package/src/daemon/config-watcher.ts +94 -31
  219. package/src/daemon/conversation-agent-loop.ts +169 -9
  220. package/src/daemon/conversation-error.ts +171 -37
  221. package/src/daemon/conversation-lifecycle.ts +53 -40
  222. package/src/daemon/conversation-messaging.ts +25 -6
  223. package/src/daemon/conversation-process.ts +49 -12
  224. package/src/daemon/conversation-runtime-assembly.ts +16 -1
  225. package/src/daemon/conversation-slash.ts +12 -5
  226. package/src/daemon/conversation-store.ts +11 -4
  227. package/src/daemon/conversation-tool-setup.ts +39 -7
  228. package/src/daemon/conversation.ts +33 -1
  229. package/src/daemon/external-plugins-bootstrap.ts +217 -181
  230. package/src/daemon/first-greeting.ts +22 -2
  231. package/src/daemon/handlers/config-model.ts +6 -5
  232. package/src/daemon/handlers/config-slack-channel.ts +15 -3
  233. package/src/daemon/handlers/shared.ts +14 -5
  234. package/src/daemon/handlers/skills.ts +111 -108
  235. package/src/daemon/history-repair.ts +28 -1
  236. package/src/daemon/host-app-control-proxy.ts +98 -23
  237. package/src/daemon/lifecycle.ts +45 -35
  238. package/src/daemon/meet-host-supervisor.ts +5 -4
  239. package/src/daemon/memory-v2-startup.ts +49 -0
  240. package/src/daemon/message-protocol.ts +1 -0
  241. package/src/daemon/message-types/conversations.ts +25 -0
  242. package/src/daemon/message-types/messages.ts +61 -0
  243. package/src/daemon/message-types/subagents.ts +1 -0
  244. package/src/daemon/message-types/sync.ts +1 -0
  245. package/src/daemon/pkb-reminder-builder.test.ts +1 -1
  246. package/src/daemon/pkb-reminder-builder.ts +1 -1
  247. package/src/daemon/plugin-source-watcher.ts +146 -0
  248. package/src/daemon/process-message.ts +21 -3
  249. package/src/daemon/server.ts +11 -2
  250. package/src/daemon/skill-memory-refresh.ts +29 -0
  251. package/src/documents/document-store.ts +221 -3
  252. package/src/embedded/plugin-api.ts +40 -0
  253. package/src/filing/filing-service.ts +39 -0
  254. package/src/heartbeat/__tests__/heartbeat-service.test.ts +91 -6
  255. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  256. package/src/heartbeat/heartbeat-service.ts +41 -0
  257. package/src/home/__tests__/feed-types.test.ts +40 -0
  258. package/src/home/feed-types.ts +22 -0
  259. package/src/home/post-connect-feed.ts +1 -0
  260. package/src/index.ts +18 -1
  261. package/src/live-voice/__tests__/live-voice-stt.test.ts +57 -0
  262. package/src/mcp/client.ts +20 -4
  263. package/src/media/image-credentials.ts +3 -3
  264. package/src/memory/__tests__/bookmark-crud.test.ts +33 -27
  265. package/src/memory/__tests__/conversation-queries.test.ts +263 -0
  266. package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
  267. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +119 -14
  268. package/src/memory/__tests__/message-content.test.ts +35 -0
  269. package/src/memory/bookmark-crud.ts +42 -10
  270. package/src/memory/context-search/sources/conversations.ts +62 -2
  271. package/src/memory/context-search/sources/workspace.ts +4 -0
  272. package/src/memory/conversation-crud.ts +63 -19
  273. package/src/memory/conversation-queries.ts +110 -10
  274. package/src/memory/db-init.ts +6 -0
  275. package/src/memory/delivery-crud.ts +152 -5
  276. package/src/memory/embedding-backend.ts +4 -4
  277. package/src/memory/external-conversation-store.ts +66 -5
  278. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +66 -9
  279. package/src/memory/graph/conversation-graph-memory.ts +31 -15
  280. package/src/memory/graph/tools.ts +3 -3
  281. package/src/memory/indexer.ts +34 -29
  282. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +73 -0
  283. package/src/memory/jobs/embed-concept-page.ts +20 -11
  284. package/src/memory/jobs-worker.ts +6 -1
  285. package/src/memory/llm-request-log-source-clickhouse.ts +17 -10
  286. package/src/memory/llm-request-log-source.ts +19 -52
  287. package/src/memory/llm-usage-store.ts +125 -5
  288. package/src/memory/memory-retrospective-startup-cleanup.ts +72 -5
  289. package/src/memory/message-content.ts +1 -1
  290. package/src/memory/migrations/109-external-conversation-bindings.ts +15 -4
  291. package/src/memory/migrations/229-delete-private-conversations.test.ts +38 -1
  292. package/src/memory/migrations/229-delete-private-conversations.ts +7 -0
  293. package/src/memory/migrations/247-external-conversation-binding-thread-id.ts +78 -0
  294. package/src/memory/migrations/248-create-onboarding-events.ts +21 -0
  295. package/src/memory/migrations/249-normalize-slack-external-content.ts +240 -0
  296. package/src/memory/migrations/index.ts +6 -0
  297. package/src/memory/migrations/registry.ts +8 -0
  298. package/src/memory/onboarding-events-store.ts +106 -0
  299. package/src/memory/schema/bookmarks.ts +0 -2
  300. package/src/memory/schema/calls.ts +1 -0
  301. package/src/memory/schema/inference.ts +1 -3
  302. package/src/memory/schema/infrastructure.ts +12 -0
  303. package/src/memory/turn-events-store.ts +127 -2
  304. package/src/memory/v2/__tests__/activation.test.ts +0 -8
  305. package/src/memory/v2/__tests__/injection.test.ts +98 -8
  306. package/src/memory/v2/__tests__/migration.test.ts +87 -0
  307. package/src/memory/v2/__tests__/page-index.test.ts +83 -0
  308. package/src/memory/v2/__tests__/prompts-router.test.ts +58 -6
  309. package/src/memory/v2/__tests__/qdrant.test.ts +66 -3
  310. package/src/memory/v2/__tests__/router.test.ts +15 -0
  311. package/src/memory/v2/__tests__/skill-store.test.ts +387 -8
  312. package/src/memory/v2/injection.ts +32 -6
  313. package/src/memory/v2/migration.ts +49 -19
  314. package/src/memory/v2/page-index.ts +35 -5
  315. package/src/memory/v2/prompts/router.ts +11 -8
  316. package/src/memory/v2/prompts/sweep.ts +2 -2
  317. package/src/memory/v2/qdrant.ts +135 -7
  318. package/src/memory/v2/router.ts +9 -8
  319. package/src/memory/v2/skill-store.ts +120 -35
  320. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +45 -5
  321. package/src/messaging/providers/slack/__tests__/download.test.ts +231 -0
  322. package/src/messaging/providers/slack/adapter.ts +43 -5
  323. package/src/messaging/providers/slack/client.ts +27 -0
  324. package/src/messaging/providers/slack/deep-link.ts +65 -0
  325. package/src/messaging/providers/slack/download.ts +104 -0
  326. package/src/messaging/providers/slack/message-metadata.test.ts +32 -0
  327. package/src/messaging/providers/slack/message-metadata.ts +27 -0
  328. package/src/messaging/providers/slack/render-transcript.test.ts +134 -0
  329. package/src/messaging/providers/slack/render-transcript.ts +69 -5
  330. package/src/messaging/providers/slack/types.ts +20 -1
  331. package/src/notifications/conversation-pairing.ts +2 -1
  332. package/src/notifications/decision-engine.ts +2 -1
  333. package/src/notifications/emit-signal.ts +20 -1
  334. package/src/notifications/home-feed-side-effect.ts +54 -0
  335. package/src/notifications/signal.ts +3 -1
  336. package/src/oauth/connection-resolver.ts +8 -4
  337. package/src/oauth/platform-connection.ts +6 -2
  338. package/src/oauth/seed-providers.ts +10 -1
  339. package/src/permissions/checker.ts +2 -0
  340. package/src/permissions/ipc-risk-types.ts +1 -0
  341. package/src/permissions/question-prompter.test.ts +416 -0
  342. package/src/permissions/question-prompter.ts +294 -0
  343. package/src/platform/client.test.ts +1 -1
  344. package/src/platform/client.ts +1 -1
  345. package/src/plugin-api/constants.ts +26 -0
  346. package/src/plugin-api/index.ts +34 -1
  347. package/src/plugin-api/types.ts +104 -22
  348. package/src/plugins/defaults/circuit-breaker.ts +0 -5
  349. package/src/plugins/defaults/compaction.ts +0 -4
  350. package/src/plugins/defaults/empty-response.ts +0 -2
  351. package/src/plugins/defaults/history-repair.ts +0 -2
  352. package/src/plugins/defaults/injectors.ts +36 -3
  353. package/src/plugins/defaults/llm-call.ts +0 -2
  354. package/src/plugins/defaults/memory-retrieval.ts +0 -1
  355. package/src/plugins/defaults/overflow-reduce.ts +0 -1
  356. package/src/plugins/defaults/persistence.ts +0 -2
  357. package/src/plugins/defaults/title-generate.ts +0 -5
  358. package/src/plugins/defaults/token-estimate.ts +0 -2
  359. package/src/plugins/defaults/tool-error.ts +0 -7
  360. package/src/plugins/defaults/tool-execute.ts +0 -2
  361. package/src/plugins/defaults/tool-result-truncate.ts +0 -4
  362. package/src/plugins/ensure-plugin-api-shim.ts +96 -0
  363. package/src/plugins/external-api.ts +104 -0
  364. package/src/plugins/external-plugin-loader.ts +105 -32
  365. package/src/plugins/feature-gate.ts +22 -0
  366. package/src/plugins/pipeline.ts +37 -0
  367. package/src/plugins/registry.ts +48 -80
  368. package/src/plugins/types.ts +31 -26
  369. package/src/plugins/user-loader.ts +21 -2
  370. package/src/proactive-artifact/aux-message-injector.ts +11 -0
  371. package/src/proactive-artifact/job.test.ts +37 -5
  372. package/src/prompts/__tests__/system-prompt.test.ts +12 -0
  373. package/src/prompts/__tests__/task-progress-hint-section.test.ts +99 -0
  374. package/src/prompts/normalize-onboarding.ts +27 -0
  375. package/src/prompts/sections.ts +302 -0
  376. package/src/prompts/system-prompt.ts +63 -166
  377. package/src/prompts/templates/BOOTSTRAP.md +17 -1
  378. package/src/prompts/templates/system-sections.ts +173 -0
  379. package/src/providers/__tests__/inference.test.ts +22 -7
  380. package/src/providers/anthropic/client.ts +28 -28
  381. package/src/providers/connection-resolution.ts +7 -0
  382. package/src/providers/inference/adapter-factory.ts +41 -4
  383. package/src/providers/inference/connections.ts +74 -29
  384. package/src/providers/inference/resolve-auth.ts +12 -4
  385. package/src/providers/model-catalog.ts +294 -12
  386. package/src/providers/openai/chat-completions-provider.ts +10 -2
  387. package/src/providers/openrouter/client.ts +7 -0
  388. package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -1
  389. package/src/providers/{managed-proxy → platform-proxy}/context.ts +3 -3
  390. package/src/providers/provider-availability.ts +17 -2
  391. package/src/providers/provider-catalog-visibility.ts +36 -0
  392. package/src/providers/registry.ts +22 -14
  393. package/src/providers/retry.ts +47 -1
  394. package/src/runtime/__tests__/agent-wake.test.ts +152 -0
  395. package/src/runtime/agent-wake.ts +42 -14
  396. package/src/runtime/auth/route-policy.ts +8 -1
  397. package/src/runtime/btw-sidechain.ts +2 -0
  398. package/src/runtime/http-types.ts +19 -0
  399. package/src/runtime/migrations/origin-mode.ts +1 -1
  400. package/src/runtime/pending-interactions.ts +1 -0
  401. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +17 -0
  402. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +5 -1
  403. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +107 -20
  404. package/src/runtime/routes/__tests__/question-routes.test.ts +395 -0
  405. package/src/runtime/routes/__tests__/tts-routes.test.ts +64 -1
  406. package/src/runtime/routes/acp-routes-list.test.ts +143 -0
  407. package/src/runtime/routes/acp-routes.ts +5 -3
  408. package/src/runtime/routes/auth-routes.ts +1 -1
  409. package/src/runtime/routes/bookmark-routes.ts +5 -3
  410. package/src/runtime/routes/btw-routes.ts +5 -1
  411. package/src/runtime/routes/channel-availability-routes.ts +121 -0
  412. package/src/runtime/routes/conversation-cli-routes.ts +44 -3
  413. package/src/runtime/routes/conversation-list-routes.ts +3 -20
  414. package/src/runtime/routes/conversation-management-routes.ts +17 -42
  415. package/src/runtime/routes/conversation-query-routes.ts +40 -35
  416. package/src/runtime/routes/conversation-routes.ts +90 -11
  417. package/src/runtime/routes/documents-routes.ts +25 -86
  418. package/src/runtime/routes/group-routes.ts +5 -0
  419. package/src/runtime/routes/inbound-conversation.ts +28 -8
  420. package/src/runtime/routes/inbound-message-handler.ts +236 -41
  421. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +111 -0
  422. package/src/runtime/routes/inbound-stages/background-dispatch.ts +32 -1
  423. package/src/runtime/routes/inbound-stages/edit-intercept.ts +17 -4
  424. package/src/runtime/routes/index.ts +6 -0
  425. package/src/runtime/routes/inference-profile-session-handler.ts +17 -44
  426. package/src/runtime/routes/inference-profile-session-reaper.ts +7 -21
  427. package/src/runtime/routes/inference-provider-connection-routes.ts +65 -21
  428. package/src/runtime/routes/integrations/slack/share.ts +4 -52
  429. package/src/runtime/routes/integrations/slack/token.ts +43 -0
  430. package/src/runtime/routes/integrations/twilio.ts +6 -13
  431. package/src/runtime/routes/notification-routes.ts +1 -1
  432. package/src/runtime/routes/oauth-commands-routes.ts +105 -15
  433. package/src/runtime/routes/oauth-lifecycle-routes.ts +43 -0
  434. package/src/runtime/routes/question-routes.ts +259 -0
  435. package/src/runtime/routes/rename-conversation-routes.ts +2 -33
  436. package/src/runtime/routes/schedule-routes.ts +4 -7
  437. package/src/runtime/routes/subagents-routes.ts +57 -18
  438. package/src/runtime/routes/telemetry-routes.ts +27 -0
  439. package/src/runtime/routes/tts-routes.ts +27 -2
  440. package/src/runtime/routes/workspace-routes.test.ts +43 -0
  441. package/src/runtime/routes/workspace-routes.ts +28 -0
  442. package/src/runtime/services/conversation-serializer.ts +39 -7
  443. package/src/runtime/sync/resource-sync-events.ts +93 -1
  444. package/src/schedule/schedule-store.ts +27 -2
  445. package/src/schedule/scheduler.ts +9 -1
  446. package/src/security/__tests__/untrusted-content.test.ts +86 -0
  447. package/src/security/untrusted-content.ts +93 -8
  448. package/src/skills/catalog-files.ts +1 -1
  449. package/src/skills/catalog-install.ts +233 -116
  450. package/src/skills/clawhub.ts +70 -13
  451. package/src/skills/managed-store.ts +4 -119
  452. package/src/skills/skillssh-registry.ts +27 -48
  453. package/src/subagent/manager.ts +15 -7
  454. package/src/telemetry/types.ts +113 -1
  455. package/src/telemetry/usage-telemetry-reporter.test.ts +312 -5
  456. package/src/telemetry/usage-telemetry-reporter.ts +113 -7
  457. package/src/tools/apps/executors.ts +58 -7
  458. package/src/tools/ask-question/ask-question-tool.test.ts +509 -0
  459. package/src/tools/ask-question/ask-question-tool.ts +304 -0
  460. package/src/tools/browser/browser-execution.ts +15 -11
  461. package/src/tools/computer-use/definitions.ts +3 -3
  462. package/src/tools/credentials/vault.ts +1 -1
  463. package/src/tools/document/document-tool.ts +124 -1
  464. package/src/tools/filesystem/edit.ts +1 -1
  465. package/src/tools/filesystem/list.ts +1 -1
  466. package/src/tools/filesystem/read.ts +1 -1
  467. package/src/tools/filesystem/write.ts +5 -2
  468. package/src/tools/host-filesystem/transfer.ts +1 -1
  469. package/src/tools/host-terminal/host-shell.ts +1 -1
  470. package/src/tools/permission-checker.ts +1 -1
  471. package/src/tools/registry.ts +17 -7
  472. package/src/tools/schedule/create.ts +2 -2
  473. package/src/tools/schema-transforms.ts +7 -2
  474. package/src/tools/side-effects.ts +1 -0
  475. package/src/tools/skills/delete-managed.ts +4 -4
  476. package/src/tools/skills/execute.ts +1 -1
  477. package/src/tools/skills/scaffold-managed.ts +3 -2
  478. package/src/tools/subagent/notify-parent.ts +1 -1
  479. package/src/tools/system/request-permission.ts +2 -2
  480. package/src/tools/terminal/safe-env.ts +60 -1
  481. package/src/tools/tool-manifest.ts +2 -0
  482. package/src/tools/types.ts +72 -21
  483. package/src/tools/ui-surface/definitions.ts +6 -5
  484. package/src/tts/__tests__/provider-adapters.test.ts +76 -2
  485. package/src/tts/providers/elevenlabs-provider.ts +75 -1
  486. package/src/types/onboarding-context.ts +2 -0
  487. package/src/util/errors.ts +17 -0
  488. package/src/util/platform.ts +10 -0
  489. package/src/watcher/__tests__/engine.test.ts +22 -0
  490. package/src/watcher/engine.ts +6 -2
  491. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +80 -15
  492. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +35 -22
  493. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +3 -1
  494. package/src/workspace/migrations/083-system-prompt-prefix-to-file.ts +191 -0
  495. package/src/workspace/migrations/084-remove-legacy-skills-index.ts +276 -0
  496. package/src/workspace/migrations/085-memory-v2-bm25-b-reembed-disabled-v2-pages.ts +137 -0
  497. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +198 -0
  498. package/src/workspace/migrations/registry.ts +8 -0
  499. package/src/workspace/migrations/runner.ts +39 -9
  500. package/src/workspace/migrations/types.ts +4 -0
  501. package/examples/plugins/echo/bun.lock +0 -25
  502. package/src/__tests__/context-window-manager.test.ts +0 -2481
  503. package/src/context/__tests__/compact-prompt.test.ts +0 -63
  504. package/src/context/prompts/compact.md +0 -26
  505. package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
  506. /package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +0 -0
@@ -20,7 +20,10 @@ import {
20
20
  updateMessageContent,
21
21
  updateMessageContentAndMetadata,
22
22
  } from "../../../memory/conversation-crud.js";
23
- import { findMessageBySourceId, recordInbound } from "../../../memory/delivery-crud.js";
23
+ import {
24
+ findMessageBySourceId,
25
+ recordInbound,
26
+ } from "../../../memory/delivery-crud.js";
24
27
  import {
25
28
  mergeSlackMetadata,
26
29
  readSlackMetadata,
@@ -39,6 +42,7 @@ export interface EditInterceptParams {
39
42
  conversationExternalId: string;
40
43
  externalMessageId: string;
41
44
  sourceMessageId: string;
45
+ sourceThreadId?: string;
42
46
  canonicalAssistantId: string;
43
47
  assistantId: string;
44
48
  content: string | undefined;
@@ -61,6 +65,7 @@ export async function handleEditIntercept(
61
65
  conversationExternalId,
62
66
  externalMessageId,
63
67
  sourceMessageId,
68
+ sourceThreadId,
64
69
  canonicalAssistantId,
65
70
  assistantId,
66
71
  content,
@@ -71,7 +76,7 @@ export async function handleEditIntercept(
71
76
  sourceChannel,
72
77
  conversationExternalId,
73
78
  externalMessageId,
74
- { sourceMessageId, assistantId: canonicalAssistantId },
79
+ { sourceMessageId, sourceThreadId, assistantId: canonicalAssistantId },
75
80
  );
76
81
 
77
82
  if (editResult.duplicate) {
@@ -145,6 +150,7 @@ export async function handleEditIntercept(
145
150
  messageId: original.messageId,
146
151
  conversationExternalId,
147
152
  sourceMessageId,
153
+ sourceThreadId,
148
154
  newContent,
149
155
  });
150
156
  } else {
@@ -203,10 +209,16 @@ function applySlackEditMetadata(params: {
203
209
  messageId: string;
204
210
  conversationExternalId: string;
205
211
  sourceMessageId: string;
212
+ sourceThreadId?: string;
206
213
  newContent: string;
207
214
  }): void {
208
- const { messageId, conversationExternalId, sourceMessageId, newContent } =
209
- params;
215
+ const {
216
+ messageId,
217
+ conversationExternalId,
218
+ sourceMessageId,
219
+ sourceThreadId,
220
+ newContent,
221
+ } = params;
210
222
 
211
223
  const row = getMessageById(messageId);
212
224
  const outerMetadata: Record<string, unknown> =
@@ -230,6 +242,7 @@ function applySlackEditMetadata(params: {
230
242
  source: "slack" as const,
231
243
  channelId: conversationExternalId,
232
244
  channelTs: sourceMessageId,
245
+ ...(sourceThreadId ? { threadTs: sourceThreadId } : {}),
233
246
  eventKind: "message" as const,
234
247
  }),
235
248
  editedAt,
@@ -25,6 +25,7 @@ import { ROUTES as BROWSER_ROUTES } from "./browser-routes.js";
25
25
  import { ROUTES as BTW_ROUTES } from "./btw-routes.js";
26
26
  import { ROUTES as CACHE_ROUTES } from "./cache-routes.js";
27
27
  import { ROUTES as CALL_ROUTES } from "./call-routes.js";
28
+ import { ROUTES as CHANNEL_AVAILABILITY_ROUTES } from "./channel-availability-routes.js";
28
29
  import { ROUTES as CHANNEL_READINESS_ROUTES } from "./channel-readiness-routes.js";
29
30
  import { CHANNEL_ROUTES } from "./channel-route-definitions.js";
30
31
  import { ROUTES as CHANNEL_VERIFICATION_ROUTES } from "./channel-verification-routes.js";
@@ -90,12 +91,14 @@ import { ROUTES as NOTIFICATION_ROUTES } from "./notification-routes.js";
90
91
  import { ROUTES as OAUTH_APPS_ROUTES } from "./oauth-apps.js";
91
92
  import { ROUTES as OAUTH_COMMANDS_ROUTES } from "./oauth-commands-routes.js";
92
93
  import { ROUTES as OAUTH_CONNECT_ROUTES } from "./oauth-connect-routes.js";
94
+ import { ROUTES as OAUTH_LIFECYCLE_ROUTES } from "./oauth-lifecycle-routes.js";
93
95
  import { ROUTES as OAUTH_PROVIDERS_ROUTES } from "./oauth-providers.js";
94
96
  import { ROUTES as PLATFORM_ROUTES } from "./platform-routes.js";
95
97
  import { ROUTES as PLAYGROUND_ROUTES } from "./playground/index.js";
96
98
  import { ROUTES as PROFILER_ROUTES } from "./profiler-routes.js";
97
99
  import { ROUTES as PS_ROUTES } from "./ps-routes.js";
98
100
  import { ROUTES as PUBLISH_ROUTES } from "./publish-routes.js";
101
+ import { ROUTES as QUESTION_ROUTES } from "./question-routes.js";
99
102
  import { ROUTES as RECORDING_ROUTES } from "./recording-routes.js";
100
103
  import { ROUTES as RENAME_CONVERSATION_ROUTES } from "./rename-conversation-routes.js";
101
104
  import { ROUTES as SCHEDULE_ROUTES } from "./schedule-routes.js";
@@ -144,6 +147,7 @@ export const ROUTES: RouteDefinition[] = [
144
147
  ...CALL_ROUTES,
145
148
  ...CHANNEL_ROUTES,
146
149
  ...CHANNEL_VERIFICATION_ROUTES,
150
+ ...CHANNEL_AVAILABILITY_ROUTES,
147
151
  ...CHANNEL_READINESS_ROUTES,
148
152
  ...BROWSER_ROUTES,
149
153
  ...BTW_ROUTES,
@@ -204,6 +208,7 @@ export const ROUTES: RouteDefinition[] = [
204
208
  ...MIGRATION_ROUTES,
205
209
  ...NOTIFICATION_ROUTES,
206
210
  ...OAUTH_APPS_ROUTES,
211
+ ...OAUTH_LIFECYCLE_ROUTES,
207
212
  ...OAUTH_COMMANDS_ROUTES,
208
213
  ...OAUTH_PROVIDERS_ROUTES,
209
214
  ...PLATFORM_ROUTES,
@@ -211,6 +216,7 @@ export const ROUTES: RouteDefinition[] = [
211
216
  ...PROFILER_ROUTES,
212
217
  ...PS_ROUTES,
213
218
  ...PUBLISH_ROUTES,
219
+ ...QUESTION_ROUTES,
214
220
  ...RECORDING_ROUTES,
215
221
  ...RENAME_CONVERSATION_ROUTES,
216
222
  ...SCHEDULE_ROUTES,
@@ -21,13 +21,9 @@ import {
21
21
  setConversationInferenceProfileSession,
22
22
  } from "../../memory/conversation-crud.js";
23
23
  import { resolveConversationId } from "../../memory/conversation-key-store.js";
24
- import { getLogger } from "../../util/logger.js";
25
- import { buildAssistantEvent } from "../assistant-event.js";
26
- import { assistantEventHub } from "../assistant-event-hub.js";
24
+ import { publishConversationInferenceProfileChanged } from "../sync/resource-sync-events.js";
27
25
  import { BadRequestError, NotFoundError } from "./errors.js";
28
26
 
29
- const log = getLogger("inference-profile-session-handler");
30
-
31
27
  // ---------------------------------------------------------------------------
32
28
  // Types
33
29
  // ---------------------------------------------------------------------------
@@ -120,25 +116,12 @@ export async function setInferenceProfileSession({
120
116
  };
121
117
  }
122
118
  setConversationInferenceProfileSession(resolvedId, null, null, null);
123
- await assistantEventHub
124
- .publish(
125
- buildAssistantEvent(
126
- {
127
- type: "conversation_inference_profile_updated",
128
- conversationId: resolvedId,
129
- profile: null,
130
- sessionId: null,
131
- expiresAt: null,
132
- },
133
- resolvedId,
134
- ),
135
- )
136
- .catch((err) => {
137
- log.warn(
138
- { err, conversationId: resolvedId },
139
- "Failed to publish conversation_inference_profile_updated event",
140
- );
141
- });
119
+ publishConversationInferenceProfileChanged({
120
+ conversationId: resolvedId,
121
+ profile: null,
122
+ sessionId: null,
123
+ expiresAt: null,
124
+ });
142
125
  return {
143
126
  conversationId: resolvedId,
144
127
  profile: null,
@@ -201,25 +184,12 @@ export async function setInferenceProfileSession({
201
184
  newExpiresAt ?? null,
202
185
  );
203
186
 
204
- await assistantEventHub
205
- .publish(
206
- buildAssistantEvent(
207
- {
208
- type: "conversation_inference_profile_updated",
209
- conversationId: resolvedId,
210
- profile,
211
- sessionId: newSessionId ?? null,
212
- expiresAt: newExpiresAt ?? null,
213
- },
214
- resolvedId,
215
- ),
216
- )
217
- .catch((err) => {
218
- log.warn(
219
- { err, conversationId: resolvedId },
220
- "Failed to publish conversation_inference_profile_updated event",
221
- );
222
- });
187
+ publishConversationInferenceProfileChanged({
188
+ conversationId: resolvedId,
189
+ profile,
190
+ sessionId: newSessionId ?? null,
191
+ expiresAt: newExpiresAt ?? null,
192
+ });
223
193
 
224
194
  return {
225
195
  conversationId: resolvedId,
@@ -307,6 +277,9 @@ export function listInferenceProfileSessionsWithRemaining(
307
277
  }> {
308
278
  return listActiveInferenceProfileSessions(conversationId).map((row) => ({
309
279
  ...row,
310
- remainingSeconds: Math.max(0, Math.floor((row.expiresAt - Date.now()) / 1000)),
280
+ remainingSeconds: Math.max(
281
+ 0,
282
+ Math.floor((row.expiresAt - Date.now()) / 1000),
283
+ ),
311
284
  }));
312
285
  }
@@ -13,8 +13,7 @@
13
13
 
14
14
  import { clearExpiredInferenceProfiles } from "../../memory/conversation-crud.js";
15
15
  import { getLogger } from "../../util/logger.js";
16
- import { buildAssistantEvent } from "../assistant-event.js";
17
- import { assistantEventHub } from "../assistant-event-hub.js";
16
+ import { publishConversationInferenceProfileChanged } from "../sync/resource-sync-events.js";
18
17
 
19
18
  const log = getLogger("inference-profile-session-reaper");
20
19
 
@@ -38,25 +37,12 @@ export function tickInferenceProfileReaper(): void {
38
37
  const now = Date.now();
39
38
  const cleared = clearExpiredInferenceProfiles(now);
40
39
  for (const { conversationId } of cleared) {
41
- assistantEventHub
42
- .publish(
43
- buildAssistantEvent(
44
- {
45
- type: "conversation_inference_profile_updated",
46
- conversationId,
47
- profile: null,
48
- sessionId: null,
49
- expiresAt: null,
50
- },
51
- conversationId,
52
- ),
53
- )
54
- .catch((err) => {
55
- log.warn(
56
- { err, conversationId },
57
- "Failed to publish inference profile expiry event",
58
- );
59
- });
40
+ publishConversationInferenceProfileChanged({
41
+ conversationId,
42
+ profile: null,
43
+ sessionId: null,
44
+ expiresAt: null,
45
+ });
60
46
  }
61
47
  if (cleared.length > 0) {
62
48
  log.info(
@@ -12,7 +12,13 @@ import { z } from "zod";
12
12
 
13
13
  import { getConfigReadOnly } from "../../config/loader.js";
14
14
  import { getDb } from "../../memory/db-connection.js";
15
- import { AuthSchema, ConnectionProviderSchema, ConnectionStatusSchema, ProviderConnectionSchema, VALID_CONNECTION_PROVIDERS } from "../../providers/inference/auth.js";
15
+ import {
16
+ AuthSchema,
17
+ ConnectionProviderSchema,
18
+ ConnectionStatusSchema,
19
+ ProviderConnectionSchema,
20
+ VALID_CONNECTION_PROVIDERS,
21
+ } from "../../providers/inference/auth.js";
16
22
  import {
17
23
  createConnection,
18
24
  deleteConnection,
@@ -21,11 +27,7 @@ import {
21
27
  MANAGED_CONNECTION_NAMES,
22
28
  updateConnection,
23
29
  } from "../../providers/inference/connections.js";
24
- import {
25
- BadRequestError,
26
- ConflictError,
27
- NotFoundError,
28
- } from "./errors.js";
30
+ import { BadRequestError, ConflictError, NotFoundError } from "./errors.js";
29
31
  import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
30
32
 
31
33
  // ---------------------------------------------------------------------------
@@ -40,7 +42,10 @@ const providerConnectionResponseSchema = ProviderConnectionSchema;
40
42
 
41
43
  function handleListConnections({ queryParams = {} }: RouteHandlerArgs) {
42
44
  const provider = queryParams.provider;
43
- const connections = listConnections(getDb(), provider ? { provider } : undefined);
45
+ const connections = listConnections(
46
+ getDb(),
47
+ provider ? { provider } : undefined,
48
+ );
44
49
  return { connections };
45
50
  }
46
51
 
@@ -75,14 +80,23 @@ function handleCreateConnection({ body = {} }: RouteHandlerArgs) {
75
80
  throw new BadRequestError(`Invalid auth: ${authResult.error.message}`);
76
81
  }
77
82
 
78
- const statusResult = body.status !== undefined ? ConnectionStatusSchema.safeParse(body.status) : null;
83
+ const statusResult =
84
+ body.status !== undefined
85
+ ? ConnectionStatusSchema.safeParse(body.status)
86
+ : null;
79
87
  if (statusResult && !statusResult.success) {
80
88
  throw new BadRequestError(`Invalid status: must be "active" or "disabled"`);
81
89
  }
82
90
 
83
91
  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`);
92
+ if (
93
+ labelRaw !== undefined &&
94
+ labelRaw !== null &&
95
+ (typeof labelRaw !== "string" || labelRaw.length === 0)
96
+ ) {
97
+ throw new BadRequestError(
98
+ `Invalid label: must be a non-empty string or null`,
99
+ );
86
100
  }
87
101
 
88
102
  const result = createConnection(getDb(), {
@@ -110,7 +124,10 @@ function handleCreateConnection({ body = {} }: RouteHandlerArgs) {
110
124
  return result.connection;
111
125
  }
112
126
 
113
- function handleUpdateConnection({ pathParams = {}, body = {} }: RouteHandlerArgs) {
127
+ function handleUpdateConnection({
128
+ pathParams = {},
129
+ body = {},
130
+ }: RouteHandlerArgs) {
114
131
  const { name } = pathParams;
115
132
  if (!name) throw new BadRequestError("name is required");
116
133
 
@@ -120,21 +137,33 @@ function handleUpdateConnection({ pathParams = {}, body = {} }: RouteHandlerArgs
120
137
  throw new BadRequestError(`Invalid auth: ${authResult.error.message}`);
121
138
  }
122
139
 
123
- const statusResult = body.status !== undefined ? ConnectionStatusSchema.safeParse(body.status) : null;
140
+ const statusResult =
141
+ body.status !== undefined
142
+ ? ConnectionStatusSchema.safeParse(body.status)
143
+ : null;
124
144
  if (statusResult && !statusResult.success) {
125
145
  throw new BadRequestError(`Invalid status: must be "active" or "disabled"`);
126
146
  }
127
147
 
128
148
  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`);
149
+ if (
150
+ labelRaw !== undefined &&
151
+ labelRaw !== null &&
152
+ (typeof labelRaw !== "string" || labelRaw.length === 0)
153
+ ) {
154
+ throw new BadRequestError(
155
+ `Invalid label: must be a non-empty string or null`,
156
+ );
131
157
  }
132
158
 
133
159
  // Managed connections: lock auth to `{type:"platform"}`. The boot upsert in
134
160
  // `seedCanonicalConnections` would revert any other value on next restart;
135
161
  // reject the write here so the surprise loop never happens. Label and status
136
162
  // remain user-editable (the boot upsert leaves those alone).
137
- if (MANAGED_CONNECTION_NAMES.has(name) && authResult.data.type !== "platform") {
163
+ if (
164
+ MANAGED_CONNECTION_NAMES.has(name) &&
165
+ authResult.data.type !== "platform"
166
+ ) {
138
167
  throw new BadRequestError(
139
168
  `Cannot change auth on managed connection "${name}". Auth is locked to platform.`,
140
169
  );
@@ -180,7 +209,10 @@ function handleDeleteConnection({ pathParams = {} }: RouteHandlerArgs) {
180
209
  const config = getConfigReadOnly();
181
210
 
182
211
  // llm.default carries provider_connection (LLMConfigBase).
183
- if ((config.llm?.default as Record<string, unknown> | undefined)?.provider_connection === name) {
212
+ if (
213
+ (config.llm?.default as Record<string, unknown> | undefined)
214
+ ?.provider_connection === name
215
+ ) {
184
216
  throw new ConflictError(
185
217
  `Connection "${name}" is referenced by llm.default. Update llm.default.provider_connection before deleting.`,
186
218
  { referencedBy: ["llm.default"] },
@@ -190,7 +222,9 @@ function handleDeleteConnection({ pathParams = {} }: RouteHandlerArgs) {
190
222
  // llm.profiles.*: only ProfileEntry has provider_connection.
191
223
  const profiles = config.llm?.profiles ?? {};
192
224
  const referencingProfiles = Object.entries(profiles)
193
- .filter(([, p]) => (p as Record<string, unknown>).provider_connection === name)
225
+ .filter(
226
+ ([, p]) => (p as Record<string, unknown>).provider_connection === name,
227
+ )
194
228
  .map(([profileName]) => profileName);
195
229
 
196
230
  const result = deleteConnection(getDb(), name, {
@@ -234,7 +268,9 @@ export const ROUTES: RouteDefinition[] = [
234
268
  description: `Filter by provider. One of: ${VALID_CONNECTION_PROVIDERS.join(", ")}`,
235
269
  },
236
270
  ],
237
- responseBody: z.object({ connections: z.array(providerConnectionResponseSchema) }),
271
+ responseBody: z.object({
272
+ connections: z.array(providerConnectionResponseSchema),
273
+ }),
238
274
  handler: handleListConnections,
239
275
  },
240
276
  {
@@ -291,7 +327,10 @@ export const ROUTES: RouteDefinition[] = [
291
327
  }),
292
328
  responseBody: providerConnectionResponseSchema,
293
329
  additionalResponses: {
294
- "400": { description: "Invalid auth schema, or attempt to change auth on a managed connection" },
330
+ "400": {
331
+ description:
332
+ "Invalid auth schema, or attempt to change auth on a managed connection",
333
+ },
295
334
  "404": { description: "Connection not found" },
296
335
  },
297
336
  handler: handleUpdateConnection,
@@ -308,9 +347,14 @@ export const ROUTES: RouteDefinition[] = [
308
347
  pathParams: [{ name: "name", description: "Connection name" }],
309
348
  responseBody: z.object({ ok: z.literal(true) }),
310
349
  additionalResponses: {
311
- "400": { description: "Connection is a Vellum-managed connection and cannot be deleted" },
350
+ "400": {
351
+ description:
352
+ "Connection is a Vellum-managed connection and cannot be deleted",
353
+ },
312
354
  "404": { description: "Connection not found" },
313
- "409": { description: "Connection is referenced by profile(s) or call site(s)" },
355
+ "409": {
356
+ description: "Connection is referenced by profile(s) or call site(s)",
357
+ },
314
358
  },
315
359
  handler: handleDeleteConnection,
316
360
  },
@@ -12,9 +12,6 @@ import {
12
12
  userInfo,
13
13
  } from "../../../../messaging/providers/slack/client.js";
14
14
  import type { SlackConversation } from "../../../../messaging/providers/slack/types.js";
15
- import { getConnectionByProvider } from "../../../../oauth/oauth-store.js";
16
- import { credentialKey } from "../../../../security/credential-key.js";
17
- import { getSecureKeyAsync } from "../../../../security/secure-keys.js";
18
15
  import { getLogger } from "../../../../util/logger.js";
19
16
  import {
20
17
  BadRequestError,
@@ -23,50 +20,10 @@ import {
23
20
  ServiceUnavailableError,
24
21
  } from "../../errors.js";
25
22
  import type { RouteDefinition, RouteHandlerArgs } from "../../types.js";
23
+ import { resolveSlackToken } from "./token.js";
26
24
 
27
25
  const log = getLogger("slack-share");
28
26
 
29
- // ---------------------------------------------------------------------------
30
- // Shared helpers
31
- // ---------------------------------------------------------------------------
32
-
33
- /**
34
- * Resolve a Slack token for the Share UI, mirroring the read/write auth split
35
- * in `messaging/providers/slack/adapter.ts`.
36
- *
37
- * For Socket Mode installs (tokens stored under `credential/slack_channel/*`),
38
- * prefer the user OAuth token (xoxp-) for reads when present — this lets the
39
- * channel picker surface channels the user belongs to but the bot doesn't.
40
- * Fall back to the bot token (xoxb-) otherwise.
41
- *
42
- * Writes MUST always use the bot token so posted messages come from the bot
43
- * identity, never the user. Passing `user_token` to chat.postMessage would
44
- * post as the user — unambiguously wrong for Share UI behavior.
45
- *
46
- * For legacy OAuth installs (no Socket Mode tokens), fall back to the OAuth
47
- * connection's access_token, which is the bot token in Slack's OAuth v2 flow.
48
- */
49
- async function resolveSlackToken(
50
- mode: "read" | "write",
51
- ): Promise<string | undefined> {
52
- const botToken = await getSecureKeyAsync(
53
- credentialKey("slack_channel", "bot_token"),
54
- );
55
- if (botToken) {
56
- if (mode === "read") {
57
- const userToken = await getSecureKeyAsync(
58
- credentialKey("slack_channel", "user_token"),
59
- );
60
- return userToken ?? botToken;
61
- }
62
- return botToken;
63
- }
64
-
65
- const conn = getConnectionByProvider("slack");
66
- if (!conn) return undefined;
67
- return await getSecureKeyAsync(`oauth_connection/${conn.id}/access_token`);
68
- }
69
-
70
27
  // ---------------------------------------------------------------------------
71
28
  // GET /v1/slack/channels
72
29
  // ---------------------------------------------------------------------------
@@ -179,15 +136,11 @@ export async function handleShareToSlackChannel({
179
136
  };
180
137
 
181
138
  if (!appId || !channelId) {
182
- throw new BadRequestError(
183
- "Missing required fields: appId, channelId",
184
- );
139
+ throw new BadRequestError("Missing required fields: appId, channelId");
185
140
  }
186
141
 
187
142
  if (typeof appId !== "string" || typeof channelId !== "string") {
188
- throw new BadRequestError(
189
- "Fields appId and channelId must be strings",
190
- );
143
+ throw new BadRequestError("Fields appId and channelId must be strings");
191
144
  }
192
145
 
193
146
  if (message !== undefined && typeof message !== "string") {
@@ -245,8 +198,7 @@ export const ROUTES: RouteDefinition[] = [
245
198
  endpoint: "slack/channels",
246
199
  method: "GET",
247
200
  summary: "List Slack channels",
248
- description:
249
- "List Slack channels, groups, and DMs for the channel picker.",
201
+ description: "List Slack channels, groups, and DMs for the channel picker.",
250
202
  tags: ["integrations"],
251
203
  requirePolicyEnforcement: true,
252
204
  handler: () => handleListSlackChannels(),
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Shared Slack token resolver.
3
+ *
4
+ * Resolve a Slack token for the Share UI, mirroring the read/write auth split
5
+ * in `messaging/providers/slack/adapter.ts`.
6
+ *
7
+ * For Socket Mode installs (tokens stored under `credential/slack_channel/*`),
8
+ * prefer the user OAuth token (xoxp-) for reads when present — this lets the
9
+ * channel picker surface channels the user belongs to but the bot doesn't.
10
+ * Fall back to the bot token (xoxb-) otherwise.
11
+ *
12
+ * Writes MUST always use the bot token so posted messages come from the bot
13
+ * identity, never the user. Passing `user_token` to chat.postMessage would
14
+ * post as the user — unambiguously wrong for Share UI behavior.
15
+ *
16
+ * For legacy OAuth installs (no Socket Mode tokens), fall back to the OAuth
17
+ * connection's access_token, which is the bot token in Slack's OAuth v2 flow.
18
+ */
19
+
20
+ import { getConnectionByProvider } from "../../../../oauth/oauth-store.js";
21
+ import { credentialKey } from "../../../../security/credential-key.js";
22
+ import { getSecureKeyAsync } from "../../../../security/secure-keys.js";
23
+
24
+ export async function resolveSlackToken(
25
+ mode: "read" | "write",
26
+ ): Promise<string | undefined> {
27
+ const botToken = await getSecureKeyAsync(
28
+ credentialKey("slack_channel", "bot_token"),
29
+ );
30
+ if (botToken) {
31
+ if (mode === "read") {
32
+ const userToken = await getSecureKeyAsync(
33
+ credentialKey("slack_channel", "user_token"),
34
+ );
35
+ return userToken ?? botToken;
36
+ }
37
+ return botToken;
38
+ }
39
+
40
+ const conn = getConnectionByProvider("slack");
41
+ if (!conn) return undefined;
42
+ return await getSecureKeyAsync(`oauth_connection/${conn.id}/access_token`);
43
+ }
@@ -95,8 +95,7 @@ export async function handleSetTwilioCredentials({
95
95
 
96
96
  // Validate credentials against Twilio API
97
97
  const authHeader =
98
- "Basic " +
99
- Buffer.from(`${accountSid}:${authToken}`).toString("base64");
98
+ "Basic " + Buffer.from(`${accountSid}:${authToken}`).toString("base64");
100
99
  try {
101
100
  const res = await fetch(
102
101
  `https://api.twilio.com/2010-04-01/Accounts/${accountSid}.json`,
@@ -121,9 +120,7 @@ export async function handleSetTwilioCredentials({
121
120
  accountSid,
122
121
  );
123
122
  if (!sidStored) {
124
- throw new InternalError(
125
- "Failed to store Account SID in secure storage",
126
- );
123
+ throw new InternalError("Failed to store Account SID in secure storage");
127
124
  }
128
125
 
129
126
  const tokenStored = await setSecureKeyAsync(
@@ -132,14 +129,13 @@ export async function handleSetTwilioCredentials({
132
129
  );
133
130
  if (!tokenStored) {
134
131
  await deleteSecureKeyAsync(credentialKey("twilio", "account_sid"));
135
- throw new InternalError(
136
- "Failed to store Auth Token in secure storage",
137
- );
132
+ throw new InternalError("Failed to store Auth Token in secure storage");
138
133
  }
139
134
 
140
135
  const raw = loadRawConfig();
141
136
  const twilio = (raw?.twilio ?? {}) as Record<string, unknown>;
142
137
  twilio.accountSid = accountSid;
138
+ twilio.setupStarted = true;
143
139
  saveRawConfig({ ...raw, twilio });
144
140
 
145
141
  upsertCredentialMetadata("twilio", "account_sid", {
@@ -210,9 +206,7 @@ async function handleListTwilioNumbers() {
210
206
  return { success: true, hasCredentials: true, numbers };
211
207
  }
212
208
 
213
- export async function handleProvisionTwilioNumber({
214
- body,
215
- }: RouteHandlerArgs) {
209
+ export async function handleProvisionTwilioNumber({ body }: RouteHandlerArgs) {
216
210
  if (!(await hasTwilioCredentials())) {
217
211
  throw new BadRequestError(
218
212
  "Twilio credentials not configured. Set credentials first.",
@@ -323,8 +317,7 @@ async function handleReleaseTwilioNumber({ body }: RouteHandlerArgs) {
323
317
  };
324
318
  const raw = loadRawConfig();
325
319
  const twilio = (raw?.twilio ?? {}) as Record<string, unknown>;
326
- const phoneNumber =
327
- requestedNumber || (twilio.phoneNumber as string) || "";
320
+ const phoneNumber = requestedNumber || (twilio.phoneNumber as string) || "";
328
321
 
329
322
  if (!phoneNumber) {
330
323
  throw new BadRequestError(
@@ -52,7 +52,7 @@ function handleNotificationIntentResult({ body = {} }: RouteHandlerArgs) {
52
52
 
53
53
  const AttentionHintsSchema = z.object({
54
54
  requiresAction: z.boolean(),
55
- urgency: z.enum(["low", "medium", "high"]),
55
+ urgency: z.enum(["low", "medium", "high", "critical"]),
56
56
  deadlineAt: z.number().optional(),
57
57
  isAsyncBackground: z.boolean(),
58
58
  visibleInSourceNow: z.boolean(),