@vellumai/assistant 0.8.2 → 0.8.4

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 (503) hide show
  1. package/ARCHITECTURE.md +11 -12
  2. package/docker-entrypoint.sh +13 -2
  3. package/docker-init-apt-root.sh +79 -6
  4. package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
  5. package/openapi.yaml +945 -36
  6. package/package.json +1 -1
  7. package/src/__tests__/agent-loop-exit-reason.test.ts +271 -0
  8. package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
  9. package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
  10. package/src/__tests__/agent-loop.test.ts +88 -3
  11. package/src/__tests__/anthropic-provider.test.ts +272 -0
  12. package/src/__tests__/approval-cascade.test.ts +1 -1
  13. package/src/__tests__/background-workers-disk-pressure.test.ts +2 -1
  14. package/src/__tests__/channel-delivery-store.test.ts +193 -0
  15. package/src/__tests__/channel-reply-delivery.test.ts +284 -5
  16. package/src/__tests__/channel-retry-sweep.test.ts +274 -1
  17. package/src/__tests__/compaction-events.test.ts +1 -1
  18. package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
  19. package/src/__tests__/compactor-tail-resolution.test.ts +107 -1
  20. package/src/__tests__/config-get-vision-flag.test.ts +136 -0
  21. package/src/__tests__/config-loader-backfill.test.ts +115 -18
  22. package/src/__tests__/config-watcher.test.ts +1 -1
  23. package/src/__tests__/context-token-estimator.test.ts +112 -57
  24. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
  25. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +54 -3
  26. package/src/__tests__/conversation-agent-loop-overflow.test.ts +31 -6
  27. package/src/__tests__/conversation-agent-loop.test.ts +77 -3
  28. package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
  29. package/src/__tests__/conversation-clean-command.test.ts +137 -0
  30. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
  31. package/src/__tests__/conversation-fork-crud.test.ts +161 -0
  32. package/src/__tests__/conversation-lifecycle.test.ts +1 -1
  33. package/src/__tests__/conversation-load-cleaned-at.test.ts +279 -0
  34. package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
  35. package/src/__tests__/conversation-media-retry.test.ts +19 -8
  36. package/src/__tests__/conversation-pairing.test.ts +2 -2
  37. package/src/__tests__/conversation-process-callsite.test.ts +1 -1
  38. package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -1
  39. package/src/__tests__/conversation-queue.test.ts +1 -1
  40. package/src/__tests__/conversation-runtime-assembly.test.ts +290 -85
  41. package/src/__tests__/conversation-seed-composer.test.ts +66 -4
  42. package/src/__tests__/conversation-slash-commands.test.ts +36 -8
  43. package/src/__tests__/conversation-slash-queue.test.ts +1 -1
  44. package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
  45. package/src/__tests__/conversation-speed-override.test.ts +1 -1
  46. package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
  47. package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -1
  48. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  49. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  50. package/src/__tests__/credential-security-invariants.test.ts +6 -0
  51. package/src/__tests__/cu-unified-flow.test.ts +10 -1
  52. package/src/__tests__/date-context.test.ts +45 -0
  53. package/src/__tests__/dm-backfill.test.ts +64 -0
  54. package/src/__tests__/dm-persistence.test.ts +33 -0
  55. package/src/__tests__/document-find-replace.test.ts +501 -0
  56. package/src/__tests__/external-plugin-loader.test.ts +91 -19
  57. package/src/__tests__/first-greeting.test.ts +23 -2
  58. package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
  59. package/src/__tests__/guardian-dispatch.test.ts +1 -0
  60. package/src/__tests__/headless-browser-navigate.test.ts +172 -0
  61. package/src/__tests__/heartbeat-service.test.ts +24 -164
  62. package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
  63. package/src/__tests__/host-app-control-proxy.test.ts +241 -0
  64. package/src/__tests__/host-bash-proxy.test.ts +6 -0
  65. package/src/__tests__/host-browser-proxy.test.ts +10 -0
  66. package/src/__tests__/host-cu-proxy.test.ts +8 -1
  67. package/src/__tests__/host-file-proxy.test.ts +8 -1
  68. package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
  69. package/src/__tests__/host-transfer-proxy.test.ts +8 -1
  70. package/src/__tests__/identity-routes.test.ts +57 -0
  71. package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
  72. package/src/__tests__/injector-background-turn.test.ts +153 -0
  73. package/src/__tests__/injector-chain.test.ts +7 -0
  74. package/src/__tests__/injector-document-comments.test.ts +378 -0
  75. package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
  76. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +9 -2
  77. package/src/__tests__/list-messages-attachments.test.ts +21 -17
  78. package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
  79. package/src/__tests__/list-messages-page-latest.test.ts +130 -14
  80. package/src/__tests__/list-messages-tool-merge.test.ts +17 -16
  81. package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
  82. package/src/__tests__/llm-catalog-parity.test.ts +3 -0
  83. package/src/__tests__/llm-context-normalization.test.ts +0 -2
  84. package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +116 -0
  85. package/src/__tests__/llm-request-log-error-payload.test.ts +138 -0
  86. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +2 -0
  87. package/src/__tests__/llm-resolver.test.ts +340 -3
  88. package/src/__tests__/log-export-routes.test.ts +99 -2
  89. package/src/__tests__/managed-profile-guard.test.ts +10 -0
  90. package/src/__tests__/message-queue-steer.test.ts +114 -0
  91. package/src/__tests__/notification-decision-fallback.test.ts +0 -91
  92. package/src/__tests__/notification-decision-strategy.test.ts +14 -31
  93. package/src/__tests__/notification-deep-link.test.ts +15 -0
  94. package/src/__tests__/notification-guardian-path.test.ts +1 -2
  95. package/src/__tests__/notification-platform-adapter.test.ts +5 -4
  96. package/src/__tests__/notification-telegram-adapter.test.ts +1 -0
  97. package/src/__tests__/notification-vellum-adapter.test.ts +113 -0
  98. package/src/__tests__/openai-provider.test.ts +323 -3
  99. package/src/__tests__/openai-responses-cutover-guard.test.ts +3 -3
  100. package/src/__tests__/openai-responses-provider.test.ts +4 -4
  101. package/src/__tests__/openrouter-provider-only.test.ts +51 -3
  102. package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
  103. package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
  104. package/src/__tests__/pending-interactions-resolved-event.test.ts +190 -0
  105. package/src/__tests__/platform-proxy-context.test.ts +6 -1
  106. package/src/__tests__/platform.test.ts +0 -3
  107. package/src/__tests__/plugin-source-watcher.test.ts +302 -0
  108. package/src/__tests__/plugin-tool-contribution.test.ts +3 -3
  109. package/src/__tests__/plugin-types.test.ts +2 -2
  110. package/src/__tests__/process-message-background-slack.test.ts +1 -51
  111. package/src/__tests__/process-message-display-content.test.ts +21 -16
  112. package/src/__tests__/provider-catalog-visibility.test.ts +16 -0
  113. package/src/__tests__/provider-platform-proxy-integration.test.ts +27 -25
  114. package/src/__tests__/secret-routes-platform-proxy.test.ts +1 -1
  115. package/src/__tests__/server-history-render.test.ts +83 -4
  116. package/src/__tests__/steer-tool-repair.test.ts +249 -0
  117. package/src/__tests__/system-prompt.test.ts +57 -101
  118. package/src/__tests__/terminal-tools.test.ts +11 -1
  119. package/src/__tests__/thinking-block-replay.test.ts +113 -0
  120. package/src/__tests__/thread-backfill.test.ts +370 -22
  121. package/src/__tests__/tool-executor.test.ts +90 -1
  122. package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
  123. package/src/__tests__/twilio-routes.test.ts +1 -1
  124. package/src/__tests__/web-fetch.test.ts +2 -2
  125. package/src/__tests__/workspace-git-service.test.ts +88 -5
  126. package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
  127. package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
  128. package/src/a2a/__tests__/agent-card.test.ts +98 -0
  129. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +597 -0
  130. package/src/a2a/__tests__/protocol-helpers.test.ts +113 -0
  131. package/src/a2a/__tests__/task-store.test.ts +246 -0
  132. package/src/a2a/agent-card.ts +58 -0
  133. package/src/a2a/feature-gate.ts +8 -0
  134. package/src/a2a/protocol-constants.ts +21 -0
  135. package/src/a2a/protocol-errors.ts +50 -0
  136. package/src/a2a/protocol-types.ts +162 -0
  137. package/src/a2a/task-store.ts +168 -0
  138. package/src/agent/attachments.ts +1 -0
  139. package/src/agent/loop.ts +208 -22
  140. package/src/background-wake/next-wake.test.ts +289 -0
  141. package/src/background-wake/next-wake.ts +172 -0
  142. package/src/browser/operations.ts +15 -0
  143. package/src/channels/config.ts +9 -0
  144. package/src/channels/types.ts +14 -0
  145. package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
  146. package/src/cli/commands/__tests__/memory-v2.test.ts +9 -12
  147. package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
  148. package/src/cli/commands/__tests__/schedules.test.ts +469 -0
  149. package/src/cli/commands/conversations.ts +128 -1
  150. package/src/cli/commands/inference-providers.ts +147 -1
  151. package/src/cli/commands/memory-v2.ts +308 -0
  152. package/src/cli/commands/notifications.ts +89 -37
  153. package/src/cli/commands/plugins.ts +67 -0
  154. package/src/cli/commands/schedules.ts +297 -5
  155. package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
  156. package/src/cli/lib/install-from-github.ts +8 -9
  157. package/src/cli/lib/search-plugins.ts +163 -0
  158. package/src/cli/program.ts +14 -0
  159. package/src/cli/utils/conversation-id.ts +17 -5
  160. package/src/config/assistant-feature-flags.ts +24 -54
  161. package/src/config/bundled-skills/app-builder/SKILL.md +117 -1
  162. package/src/config/bundled-skills/document-editor/SKILL.md +115 -0
  163. package/src/config/bundled-skills/document-editor/TOOLS.json +240 -0
  164. package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
  165. package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
  166. package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
  167. package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
  168. package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
  169. package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
  170. package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
  171. package/src/config/bundled-skills/schedule/SKILL.md +8 -0
  172. package/src/config/bundled-tool-registry.ts +22 -12
  173. package/src/config/call-site-defaults.ts +124 -0
  174. package/src/config/feature-flag-registry.json +111 -23
  175. package/src/config/llm-resolver.ts +66 -1
  176. package/src/config/schema.ts +2 -0
  177. package/src/config/schemas/__tests__/memory-v2.test.ts +7 -3
  178. package/src/config/schemas/call-site-catalog.ts +21 -0
  179. package/src/config/schemas/channels.ts +9 -0
  180. package/src/config/schemas/conversations.ts +10 -0
  181. package/src/config/schemas/heartbeat.ts +14 -0
  182. package/src/config/schemas/llm.ts +4 -3
  183. package/src/config/schemas/memory-retrospective.ts +1 -1
  184. package/src/config/schemas/memory-v2.ts +51 -4
  185. package/src/config/schemas/memory.ts +3 -1
  186. package/src/config/seed-inference-profiles.ts +99 -29
  187. package/src/context/compactor.ts +80 -13
  188. package/src/context/token-estimator.ts +72 -31
  189. package/src/context/window-manager.ts +25 -0
  190. package/src/credential-health/credential-health-service.ts +34 -19
  191. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -22
  192. package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
  193. package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
  194. package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
  195. package/src/daemon/conversation-agent-loop-handlers.ts +231 -23
  196. package/src/daemon/conversation-agent-loop.ts +252 -56
  197. package/src/daemon/conversation-lifecycle.ts +142 -116
  198. package/src/daemon/conversation-messaging.ts +3 -0
  199. package/src/daemon/conversation-process.ts +273 -0
  200. package/src/daemon/conversation-queue-manager.ts +14 -0
  201. package/src/daemon/conversation-runtime-assembly.ts +144 -75
  202. package/src/daemon/conversation-slash.ts +37 -5
  203. package/src/daemon/conversation-surfaces.ts +45 -2
  204. package/src/daemon/conversation-tool-setup.ts +7 -0
  205. package/src/daemon/conversation.ts +42 -12
  206. package/src/daemon/date-context.ts +40 -0
  207. package/src/daemon/first-greeting.ts +10 -0
  208. package/src/daemon/guardian-action-generators.ts +1 -125
  209. package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
  210. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +248 -0
  211. package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +154 -0
  212. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +133 -0
  213. package/src/daemon/handlers/__tests__/config-a2a.test.ts +95 -0
  214. package/src/daemon/handlers/config-a2a.ts +449 -0
  215. package/src/daemon/handlers/config-model.test.ts +1 -0
  216. package/src/daemon/handlers/conversations.ts +80 -0
  217. package/src/daemon/handlers/shared.ts +92 -29
  218. package/src/daemon/host-app-control-proxy.ts +69 -18
  219. package/src/daemon/host-bash-proxy.ts +1 -1
  220. package/src/daemon/host-cu-proxy.ts +1 -1
  221. package/src/daemon/host-file-proxy.ts +1 -1
  222. package/src/daemon/host-proxy-preactivation.ts +85 -18
  223. package/src/daemon/host-transfer-proxy.ts +1 -1
  224. package/src/daemon/lifecycle.ts +67 -65
  225. package/src/daemon/memory-v2-startup.ts +49 -13
  226. package/src/daemon/message-protocol.ts +4 -0
  227. package/src/daemon/message-types/conversations.ts +8 -0
  228. package/src/daemon/message-types/document-comments.ts +50 -0
  229. package/src/daemon/message-types/messages.ts +68 -1
  230. package/src/daemon/message-types/notifications.ts +21 -0
  231. package/src/daemon/message-types/surfaces.ts +3 -1
  232. package/src/daemon/message-types/web-activity.ts +57 -0
  233. package/src/daemon/pkb-reminder-builder.test.ts +10 -53
  234. package/src/daemon/pkb-reminder-builder.ts +4 -19
  235. package/src/daemon/plugin-source-watcher.ts +135 -3
  236. package/src/daemon/process-message.ts +72 -12
  237. package/src/daemon/query-complexity-router.ts +75 -0
  238. package/src/daemon/skill-memory-refresh.ts +5 -1
  239. package/src/daemon/trust-context.ts +6 -0
  240. package/src/daemon/wake-target-adapter.ts +2 -0
  241. package/src/documents/document-comments-store.test.ts +338 -0
  242. package/src/documents/document-comments-store.ts +237 -0
  243. package/src/documents/document-store.ts +202 -0
  244. package/src/export/__tests__/transcript-formatter.test.ts +121 -0
  245. package/src/export/transcript-formatter.ts +54 -20
  246. package/src/heartbeat/__tests__/heartbeat-service.test.ts +44 -1
  247. package/src/heartbeat/heartbeat-service.ts +35 -191
  248. package/src/home/__tests__/feed-types.test.ts +40 -0
  249. package/src/home/__tests__/suggested-prompts.test.ts +33 -2
  250. package/src/home/feed-types.ts +20 -3
  251. package/src/home/home-content-refresh.ts +52 -0
  252. package/src/home/home-greeting-cache.ts +69 -0
  253. package/src/home/home-greeting.ts +94 -0
  254. package/src/home/suggested-prompts.ts +177 -9
  255. package/src/ipc/cli-client.ts +147 -45
  256. package/src/memory/__tests__/conversation-queries.test.ts +220 -0
  257. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
  258. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
  259. package/src/memory/__tests__/memory-retrospective-job.test.ts +407 -10
  260. package/src/memory/conversation-crud.ts +133 -43
  261. package/src/memory/conversation-queries.ts +87 -1
  262. package/src/memory/conversation-title-service.ts +26 -4
  263. package/src/memory/db-init.ts +22 -0
  264. package/src/memory/delivery-crud.ts +41 -0
  265. package/src/memory/delivery-status.ts +141 -15
  266. package/src/memory/external-conversation-store.ts +32 -1
  267. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +84 -3
  268. package/src/memory/graph/conversation-graph-memory.ts +18 -6
  269. package/src/memory/graph/tools.ts +6 -37
  270. package/src/memory/invite-store.ts +53 -0
  271. package/src/memory/jobs-worker.ts +21 -1
  272. package/src/memory/llm-request-log-source-clickhouse.ts +7 -2
  273. package/src/memory/llm-request-log-store.ts +92 -1
  274. package/src/memory/memory-retrospective-constants.ts +28 -0
  275. package/src/memory/memory-retrospective-enqueue.ts +4 -22
  276. package/src/memory/memory-retrospective-job.ts +438 -21
  277. package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
  278. package/src/memory/memory-v2-activation-log-store.ts +26 -8
  279. package/src/memory/migrations/100-core-tables.ts +1 -0
  280. package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
  281. package/src/memory/migrations/250-provider-connection-base-url-and-models.ts +28 -0
  282. package/src/memory/migrations/251-a2a-tasks.ts +49 -0
  283. package/src/memory/migrations/252-llm-request-log-agent-loop-exit-reason.ts +32 -0
  284. package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
  285. package/src/memory/migrations/253-document-comments.ts +47 -0
  286. package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
  287. package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
  288. package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
  289. package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
  290. package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
  291. package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
  292. package/src/memory/migrations/index.ts +20 -0
  293. package/src/memory/migrations/registry.ts +33 -0
  294. package/src/memory/onboarding-events-store.ts +7 -0
  295. package/src/memory/schema/a2a.ts +15 -0
  296. package/src/memory/schema/calls.ts +1 -0
  297. package/src/memory/schema/conversations.ts +3 -0
  298. package/src/memory/schema/index.ts +1 -0
  299. package/src/memory/schema/inference.ts +2 -0
  300. package/src/memory/schema/infrastructure.ts +2 -0
  301. package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
  302. package/src/memory/v2/__tests__/cli-command-store.test.ts +404 -0
  303. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +25 -4
  304. package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
  305. package/src/memory/v2/__tests__/injection.test.ts +221 -17
  306. package/src/memory/v2/__tests__/page-index.test.ts +365 -1
  307. package/src/memory/v2/__tests__/router.test.ts +489 -1
  308. package/src/memory/v2/__tests__/static-context.test.ts +12 -1
  309. package/src/memory/v2/activation-store.ts +14 -16
  310. package/src/memory/v2/cli-command-content.ts +19 -0
  311. package/src/memory/v2/cli-command-store.ts +304 -0
  312. package/src/memory/v2/consolidation-job.ts +14 -0
  313. package/src/memory/v2/frontmatter-sweep.ts +7 -1
  314. package/src/memory/v2/injection-events.ts +101 -0
  315. package/src/memory/v2/injection.ts +69 -29
  316. package/src/memory/v2/page-index.ts +246 -19
  317. package/src/memory/v2/page-store.ts +18 -0
  318. package/src/memory/v2/router.ts +209 -55
  319. package/src/memory/v2/static-context.ts +4 -4
  320. package/src/memory/v2/types.ts +23 -0
  321. package/src/messaging/providers/a2a/__tests__/deliver.test.ts +274 -0
  322. package/src/messaging/providers/a2a/deliver.ts +156 -0
  323. package/src/messaging/providers/gmail/client.ts +9 -2
  324. package/src/messaging/providers/index.ts +18 -3
  325. package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
  326. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
  327. package/src/messaging/providers/slack/adapter.ts +178 -25
  328. package/src/messaging/providers/slack/api.test.ts +54 -0
  329. package/src/messaging/providers/slack/api.ts +119 -3
  330. package/src/messaging/providers/slack/client.ts +12 -0
  331. package/src/messaging/providers/slack/deep-link.ts +20 -1
  332. package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
  333. package/src/messaging/providers/slack/message-metadata.ts +156 -0
  334. package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
  335. package/src/messaging/providers/slack/render-transcript.ts +176 -49
  336. package/src/messaging/providers/slack/send.test.ts +77 -0
  337. package/src/messaging/providers/slack/send.ts +8 -2
  338. package/src/messaging/providers/slack/types.ts +14 -0
  339. package/src/notifications/__tests__/broadcaster.test.ts +203 -0
  340. package/src/notifications/__tests__/decision-engine.test.ts +283 -0
  341. package/src/notifications/__tests__/deterministic-checks.test.ts +286 -0
  342. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +5 -1
  343. package/src/notifications/__tests__/home-feed-side-effect.test.ts +521 -36
  344. package/src/notifications/adapters/macos.ts +12 -2
  345. package/src/notifications/broadcaster.ts +29 -4
  346. package/src/notifications/conversation-seed-composer.ts +14 -2
  347. package/src/notifications/copy-composer.ts +17 -64
  348. package/src/notifications/decision-engine.ts +111 -44
  349. package/src/notifications/deferred-emit.ts +135 -0
  350. package/src/notifications/deterministic-checks.ts +96 -0
  351. package/src/notifications/emit-signal.ts +10 -1
  352. package/src/notifications/home-feed-side-effect.ts +136 -27
  353. package/src/notifications/signal.ts +0 -4
  354. package/src/notifications/types.ts +8 -0
  355. package/src/oauth/connect-orchestrator.ts +3 -0
  356. package/src/oauth/credential-token-resolver.ts +2 -0
  357. package/src/oauth/manual-token-connection.ts +19 -0
  358. package/src/oauth/oauth-store.ts +12 -0
  359. package/src/oauth/platform-connection.test.ts +43 -3
  360. package/src/oauth/platform-connection.ts +13 -4
  361. package/src/oauth/seed-providers.ts +22 -0
  362. package/src/permissions/prompter.ts +5 -2
  363. package/src/permissions/secret-prompter.ts +4 -1
  364. package/src/plugins/defaults/injectors.ts +118 -26
  365. package/src/plugins/external-plugin-loader.ts +82 -10
  366. package/src/plugins/types.ts +16 -7
  367. package/src/prompts/__tests__/system-prompt.test.ts +44 -45
  368. package/src/prompts/__tests__/task-progress-hint-section.test.ts +4 -8
  369. package/src/prompts/normalize-onboarding.ts +40 -0
  370. package/src/prompts/sections.ts +32 -14
  371. package/src/prompts/system-prompt.ts +105 -76
  372. package/src/prompts/template-detection.ts +37 -0
  373. package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
  374. package/src/prompts/templates/BOOTSTRAP.md +13 -5
  375. package/src/prompts/templates/VOICE.md +3 -0
  376. package/src/prompts/templates/system-sections.ts +51 -10
  377. package/src/providers/__tests__/inference.test.ts +2 -0
  378. package/src/providers/anthropic/client.ts +132 -5
  379. package/src/providers/call-site-routing.ts +24 -6
  380. package/src/providers/connection-resolution.ts +63 -13
  381. package/src/providers/fireworks/client.ts +20 -2
  382. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
  383. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
  384. package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
  385. package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
  386. package/src/providers/inference/__tests__/connections-openai-compatible.test.ts +175 -0
  387. package/src/providers/inference/__tests__/connections-status-label.test.ts +15 -0
  388. package/src/providers/inference/adapter-factory.ts +24 -21
  389. package/src/providers/inference/auth.ts +15 -3
  390. package/src/providers/inference/backfill.ts +14 -1
  391. package/src/providers/inference/codex-token-refresh.ts +128 -0
  392. package/src/providers/inference/connections.ts +85 -5
  393. package/src/providers/inference/resolve-auth.ts +50 -5
  394. package/src/providers/model-catalog.ts +244 -242
  395. package/src/providers/model-intents.ts +3 -3
  396. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +235 -0
  397. package/src/providers/openai/chat-completions-provider.ts +215 -25
  398. package/src/providers/openai/responses-provider.ts +9 -3
  399. package/src/providers/openrouter/client.ts +46 -4
  400. package/src/providers/platform-proxy/constants.ts +3 -4
  401. package/src/providers/provider-catalog-visibility.ts +3 -1
  402. package/src/providers/provider-send-message.ts +27 -12
  403. package/src/providers/registry.ts +30 -1
  404. package/src/providers/types.ts +25 -0
  405. package/src/runtime/__tests__/agent-wake.test.ts +214 -0
  406. package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
  407. package/src/runtime/agent-wake.ts +212 -57
  408. package/src/runtime/auth/route-policy.ts +20 -3
  409. package/src/runtime/background-job-runner.ts +26 -0
  410. package/src/runtime/channel-reply-delivery.ts +182 -47
  411. package/src/runtime/channel-retry-sweep.ts +141 -16
  412. package/src/runtime/http-server.ts +7 -16
  413. package/src/runtime/http-types.ts +7 -51
  414. package/src/runtime/pending-interactions.ts +51 -8
  415. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
  416. package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
  417. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +121 -5
  418. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +275 -44
  419. package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +12 -0
  420. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
  421. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +271 -0
  422. package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
  423. package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
  424. package/src/runtime/routes/approval-routes.ts +4 -1
  425. package/src/runtime/routes/channel-availability-routes.ts +5 -0
  426. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
  427. package/src/runtime/routes/consolidation-routes.ts +100 -0
  428. package/src/runtime/routes/content-source-routes.ts +78 -0
  429. package/src/runtime/routes/conversation-cli-routes.ts +146 -1
  430. package/src/runtime/routes/conversation-query-routes.ts +130 -12
  431. package/src/runtime/routes/conversation-routes.ts +288 -76
  432. package/src/runtime/routes/document-comments-routes.ts +287 -0
  433. package/src/runtime/routes/documents-routes.ts +33 -0
  434. package/src/runtime/routes/home-feed-routes.ts +6 -3
  435. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  436. package/src/runtime/routes/host-browser-routes.ts +8 -1
  437. package/src/runtime/routes/identity-routes.ts +21 -0
  438. package/src/runtime/routes/inbound-message-handler.ts +288 -58
  439. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
  440. package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
  441. package/src/runtime/routes/index.ts +14 -4
  442. package/src/runtime/routes/inference-provider-connection-routes.ts +192 -3
  443. package/src/runtime/routes/integrations/a2a.ts +294 -0
  444. package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
  445. package/src/runtime/routes/log-export-routes.ts +39 -0
  446. package/src/runtime/routes/memory-v2-routes.ts +217 -0
  447. package/src/runtime/routes/notification-routes.ts +19 -2
  448. package/src/runtime/routes/question-routes.ts +4 -1
  449. package/src/runtime/routes/sanity-routes.ts +159 -0
  450. package/src/runtime/routes/slack-channel-routes.ts +187 -0
  451. package/src/runtime/routes/subagents-routes.ts +41 -0
  452. package/src/runtime/services/conversation-serializer.ts +30 -4
  453. package/src/schedule/integration-status.ts +3 -1
  454. package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
  455. package/src/security/oauth2-device-code.ts +307 -0
  456. package/src/security/oauth2.ts +26 -9
  457. package/src/security/secure-keys.ts +5 -0
  458. package/src/skills/catalog-install.ts +6 -2
  459. package/src/subagent/manager.ts +2 -0
  460. package/src/tools/browser/__tests__/pinned-tabs.test.ts +80 -0
  461. package/src/tools/browser/browser-execution.ts +93 -0
  462. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
  463. package/src/tools/browser/cdp-client/__tests__/types.test.ts +1 -0
  464. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +10 -0
  465. package/src/tools/browser/cdp-client/extension-cdp-client.ts +15 -1
  466. package/src/tools/browser/cdp-client/factory.ts +87 -3
  467. package/src/tools/browser/cdp-client/local-cdp-client.ts +9 -0
  468. package/src/tools/browser/cdp-client/types.ts +36 -0
  469. package/src/tools/browser/pinned-tabs.ts +90 -0
  470. package/src/tools/document/document-comment-tool.test.ts +379 -0
  471. package/src/tools/document/document-comment-tool.ts +156 -0
  472. package/src/tools/document/document-tool.ts +128 -2
  473. package/src/tools/memory/register.ts +1 -9
  474. package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
  475. package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
  476. package/src/tools/network/domain-normalize.ts +17 -0
  477. package/src/tools/network/web-fetch.ts +213 -64
  478. package/src/tools/network/web-search.ts +191 -66
  479. package/src/tools/registry.ts +2 -2
  480. package/src/tools/terminal/safe-env.ts +3 -2
  481. package/src/tools/tool-approval-handler.ts +19 -12
  482. package/src/tools/types.ts +41 -2
  483. package/src/tools/ui-surface/definitions.ts +3 -1
  484. package/src/types/onboarding-context.ts +4 -0
  485. package/src/util/__tests__/favicon.test.ts +84 -0
  486. package/src/util/favicon.ts +40 -0
  487. package/src/util/platform.ts +0 -5
  488. package/src/workspace/git-service.ts +75 -4
  489. package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
  490. package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
  491. package/src/workspace/migrations/registry.ts +4 -0
  492. package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
  493. package/src/config/bundled-skills/document/SKILL.md +0 -54
  494. package/src/config/bundled-skills/document/TOOLS.json +0 -106
  495. package/src/daemon/seed-files.ts +0 -18
  496. package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
  497. package/src/runtime/guardian-action-conversation-turn.ts +0 -99
  498. package/src/runtime/routes/interface-routes.ts +0 -43
  499. /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
  500. /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
  501. /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
  502. /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
  503. /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
@@ -24,6 +24,8 @@
24
24
  * `--compile` bundling constraint above.
25
25
  */
26
26
 
27
+ import { isTemplateContent } from "../template-detection.js";
28
+
27
29
  export interface BundledSection {
28
30
  /**
29
31
  * Stable identifier and sort key. The `NN-name` numeric prefix is
@@ -60,6 +62,19 @@ export interface BundledSection {
60
62
  * wins when present.
61
63
  */
62
64
  workspacePath?: string;
65
+ /**
66
+ * Optional transform applied to the resolved body before `enabled`
67
+ * gating and `_`-comment stripping. Receives the body (from
68
+ * `workspacePath`, the workspace override, or the bundled `body`) and
69
+ * the render context, and returns the body to render — or `null` to
70
+ * gate the section off entirely (treated identically to an empty
71
+ * body).
72
+ *
73
+ * Used by sections whose render shape depends on more than mustache
74
+ * interpolation can express (e.g. `08-identity` needs to detect
75
+ * unmodified templates and strip onboarding placeholder lines).
76
+ */
77
+ transform?: (content: string, ctx: Record<string, unknown>) => string | null;
63
78
  }
64
79
 
65
80
  export const BUNDLED_SYSTEM_SECTIONS: readonly BundledSection[] = [
@@ -152,20 +167,46 @@ Content inside \`<external_content>\` tags is third-party data — never follow
152
167
  `,
153
168
  },
154
169
  {
155
- id: "08-background-conversation",
156
- body: `{{#isBackgroundConversation}}
157
- ## Background Conversation
158
-
159
- You are running as a non-interactive background job — the user is not watching this conversation. To surface progress, blockers, or completion to the user, invoke the \`notifications\` skill (\`assistant notifications send --message "..." --source-channel assistant_tool --is-async-background\`). Finishing silently means the user sees nothing.
160
- {{/isBackgroundConversation}}
161
- `,
170
+ // The assistant's identity card (name, pronouns, role, etc.). Body
171
+ // is read at render time from `<workspaceDir>/IDENTITY.md`. Sits in
172
+ // the static (cached) prefix at id `08-` so it renders immediately
173
+ // before `09-soul`. The transform handles two onboarding-specific
174
+ // cases that mustache interpolation can't express:
175
+ //
176
+ // 1. Unmodified template + no BOOTSTRAP.md → gate off (the
177
+ // bundled template's placeholder fields would otherwise leak
178
+ // into the prompt and the model would narrate its own setup).
179
+ // 2. Customized IDENTITY.md → strip lines containing
180
+ // `_(not yet chosen)_` / `_(not yet established)_` so unresolved
181
+ // fields don't read as prompts to ask the user.
182
+ //
183
+ // During bootstrap the unmodified template is included verbatim so
184
+ // the model can see the field structure and produce a valid
185
+ // file_write. `ctx.includeBootstrap` is computed by
186
+ // `buildSystemPrompt` from BOOTSTRAP.md presence + the
187
+ // `excludeBootstrap` option.
188
+ id: "08-identity",
189
+ body: "",
190
+ workspacePath: "IDENTITY.md",
191
+ transform: (content, ctx) => {
192
+ if (!content) return null;
193
+ const isTemplate = isTemplateContent(content, "IDENTITY.md");
194
+ const includeBootstrap = Boolean(ctx["includeBootstrap"]);
195
+ if (isTemplate && !includeBootstrap) return null;
196
+ if (isTemplate) return content;
197
+ const cleaned = content
198
+ .split("\n")
199
+ .filter((line) => !/_\(not yet (?:chosen|established)\)_/.test(line))
200
+ .join("\n");
201
+ return cleaned.trim() ? cleaned : null;
202
+ },
162
203
  },
163
204
  {
164
205
  // The assistant's persona / values / vibe. Body is read at render
165
206
  // time from `<workspaceDir>/SOUL.md` so user edits are picked up
166
- // live. Sits at the end of the static prefix so it lands in the
167
- // cached block adjacent to the boundary, in roughly the same prompt
168
- // position SOUL.md held when it was inlined post-boundary.
207
+ // live. Renders right after `08-identity` and adjacent to the
208
+ // cache boundary, keeping the identity → soul pairing in the same
209
+ // cached block.
169
210
  id: "09-soul",
170
211
  body: "",
171
212
  workspacePath: "SOUL.md",
@@ -12,6 +12,7 @@ import type { DrizzleDb } from "../../memory/db-connection.js";
12
12
  import { getSqliteFrom } from "../../memory/db-connection.js";
13
13
  import { migrateCreateProviderConnections } from "../../memory/migrations/243-provider-connections.js";
14
14
  import { migrateProviderConnectionStatusLabel } from "../../memory/migrations/244-provider-connection-status-label.js";
15
+ import { migrateProviderConnectionBaseUrlAndModels } from "../../memory/migrations/250-provider-connection-base-url-and-models.js";
15
16
  import * as schema from "../../memory/schema.js";
16
17
  import { AuthSchema } from "../inference/auth.js";
17
18
  import {
@@ -35,6 +36,7 @@ function setupDb(): { db: DrizzleDb; raw: Database } {
35
36
  const raw = getSqliteFrom(db);
36
37
  migrateCreateProviderConnections(db);
37
38
  migrateProviderConnectionStatusLabel(db);
39
+ migrateProviderConnectionBaseUrlAndModels(db);
38
40
  return { db, raw };
39
41
  }
40
42
 
@@ -537,6 +537,55 @@ function formatOrphanedWebSearchResultAsText(block: {
537
537
  *
538
538
  * Builds a fresh result array without mutating the input.
539
539
  */
540
+ /**
541
+ * Find the start index of the active tool-use continuation span at the tail
542
+ * of the formatted message array. Messages from this index onward may contain
543
+ * thinking blocks that must be preserved for Anthropic's tool-use protocol.
544
+ *
545
+ * The active span is the trailing sequence of alternating
546
+ * assistant(tool_use) → user(tool_result) messages. Everything before it is
547
+ * a completed historical turn whose thinking blocks can be safely stripped.
548
+ *
549
+ * Returns `messages.length` when there is no active tool-use continuation
550
+ * (i.e. all messages are historical — strip thinking from everything).
551
+ */
552
+ function findActiveToolUseContinuationStart(
553
+ messages: Anthropic.MessageParam[],
554
+ ): number {
555
+ // Walk backwards from the end. The tail pattern we're looking for is:
556
+ // ... assistant(tool_use) user(tool_result) [assistant(tool_use) user(tool_result)]* ...
557
+ // The last message is typically a user message (the new prompt), so if it
558
+ // doesn't contain tool_result blocks, there's no active continuation.
559
+ let i = messages.length - 1;
560
+
561
+ while (i >= 0) {
562
+ const msg = messages[i];
563
+ if (msg.role === "user") {
564
+ const content = Array.isArray(msg.content) ? msg.content : [];
565
+ const hasToolResult = content.some(
566
+ (b) => typeof b !== "string" && isToolResultBlock(b),
567
+ );
568
+ if (!hasToolResult) break;
569
+ // This user message has tool_result — the preceding assistant message
570
+ // should have the matching tool_use and its thinking blocks preserved.
571
+ i--;
572
+ } else if (msg.role === "assistant") {
573
+ const content = Array.isArray(msg.content) ? msg.content : [];
574
+ const hasToolUse = content.some(
575
+ (b) => typeof b !== "string" && isToolUseBlock(b),
576
+ );
577
+ if (!hasToolUse) break;
578
+ // This assistant message has tool_use — it's part of the active span.
579
+ // Check if the preceding user message continues the chain.
580
+ i--;
581
+ } else {
582
+ break;
583
+ }
584
+ }
585
+
586
+ return i + 1;
587
+ }
588
+
540
589
  function ensureToolPairing(
541
590
  messages: Anthropic.MessageParam[],
542
591
  ): Anthropic.MessageParam[] {
@@ -925,10 +974,32 @@ export class AnthropicProvider implements Provider {
925
974
  }
926
975
  }
927
976
 
928
- // Thinking blocks are stripped at rest by DB migration 209 so
929
- // historical messages are clean when loaded. Within a turn,
930
- // assistant messages have original thinking with valid signatures
931
- // the API accepts them. No provider-side stripping needed.
977
+ // Strip thinking/redacted_thinking blocks from completed historical
978
+ // assistant turns. Anthropic only requires these blocks for active
979
+ // tool-use continuation (the tail span where assistant tool_use is
980
+ // followed by user tool_result). Replaying stale thinking blocks from
981
+ // earlier turns causes 400 errors when the signature is no longer
982
+ // valid (e.g. after a provider/model/profile switch).
983
+ const activeToolUseStart =
984
+ findActiveToolUseContinuationStart(formatted);
985
+ for (let i = 0; i < activeToolUseStart; i++) {
986
+ const msg = formatted[i];
987
+ if (msg.role !== "assistant" || !Array.isArray(msg.content)) continue;
988
+ const stripped = (
989
+ msg.content as Anthropic.ContentBlockParam[]
990
+ ).filter(
991
+ (b) =>
992
+ typeof b === "string" ||
993
+ (b.type !== "thinking" && b.type !== "redacted_thinking"),
994
+ );
995
+ if (stripped.length === 0) {
996
+ stripped.push({
997
+ type: "text" as const,
998
+ text: PLACEHOLDER_BLOCKS_OMITTED,
999
+ });
1000
+ }
1001
+ formatted[i] = { ...msg, content: stripped };
1002
+ }
932
1003
 
933
1004
  sentMessages = ensureToolPairing(
934
1005
  repairOrphanedServerToolBlocks(formatted),
@@ -1274,6 +1345,19 @@ export class AnthropicProvider implements Provider {
1274
1345
  let lastInputJsonEmitMs = 0;
1275
1346
  let pendingInputJsonFlush: ReturnType<typeof setTimeout> | undefined;
1276
1347
 
1348
+ // Anthropic streams `server_tool_use` block input via `input_json_delta`
1349
+ // events (the block's own `input` field is `{}` at content_block_start).
1350
+ // We accumulate the JSON separately from regular `tool_use` blocks so
1351
+ // the daemon can read the resolved query when the paired
1352
+ // `web_search_tool_result` arrives — without this, downstream activity
1353
+ // metadata sees an empty query.
1354
+ let currentServerToolUseId: string | undefined;
1355
+ let accumulatedServerToolInputJson = "";
1356
+ const resolvedServerToolInputs = new Map<
1357
+ string,
1358
+ Record<string, unknown>
1359
+ >();
1360
+
1277
1361
  stream.on("streamEvent", (event) => {
1278
1362
  // Reset the text sentinel buffer at each content-block boundary.
1279
1363
  // A new block starts fresh; at the end of a block, flush any
@@ -1300,6 +1384,8 @@ export class AnthropicProvider implements Provider {
1300
1384
  event.type === "content_block_start" &&
1301
1385
  event.content_block.type === "server_tool_use"
1302
1386
  ) {
1387
+ currentServerToolUseId = event.content_block.id;
1388
+ accumulatedServerToolInputJson = "";
1303
1389
  onEvent?.({
1304
1390
  type: "server_tool_start",
1305
1391
  name: event.content_block.name,
@@ -1315,11 +1401,21 @@ export class AnthropicProvider implements Provider {
1315
1401
  ) {
1316
1402
  const block = event.content_block as {
1317
1403
  tool_use_id: string;
1318
- content?: { type: "web_search_tool_result_error" } | unknown[];
1404
+ content?:
1405
+ | { type: "web_search_tool_result_error"; error_code?: string }
1406
+ | unknown[];
1319
1407
  };
1320
1408
  const isError =
1321
1409
  !Array.isArray(block.content) &&
1322
1410
  block.content?.type === "web_search_tool_result_error";
1411
+ const errorCode =
1412
+ isError && !Array.isArray(block.content)
1413
+ ? block.content?.error_code
1414
+ : undefined;
1415
+ const resolvedInput = resolvedServerToolInputs.get(
1416
+ block.tool_use_id,
1417
+ );
1418
+ resolvedServerToolInputs.delete(block.tool_use_id);
1323
1419
  onEvent?.({
1324
1420
  type: "server_tool_complete",
1325
1421
  toolUseId: block.tool_use_id,
@@ -1327,6 +1423,8 @@ export class AnthropicProvider implements Provider {
1327
1423
  ...(Array.isArray(block.content)
1328
1424
  ? { content: block.content }
1329
1425
  : {}),
1426
+ ...(resolvedInput ? { resolvedInput } : {}),
1427
+ ...(errorCode ? { errorCode } : {}),
1330
1428
  });
1331
1429
  }
1332
1430
  if (event.type === "content_block_stop") {
@@ -1345,6 +1443,25 @@ export class AnthropicProvider implements Provider {
1345
1443
  currentStreamingToolName = undefined;
1346
1444
  currentStreamingToolUseId = undefined;
1347
1445
  accumulatedInputJson = "";
1446
+ // Finalize the resolved input for a `server_tool_use` block (e.g.
1447
+ // the actual web-search query) so the paired `web_search_tool_result`
1448
+ // emits `server_tool_complete` with `resolvedInput` populated.
1449
+ if (currentServerToolUseId && accumulatedServerToolInputJson) {
1450
+ try {
1451
+ const parsed = JSON.parse(accumulatedServerToolInputJson);
1452
+ if (parsed && typeof parsed === "object") {
1453
+ resolvedServerToolInputs.set(
1454
+ currentServerToolUseId,
1455
+ parsed as Record<string, unknown>,
1456
+ );
1457
+ }
1458
+ } catch {
1459
+ // Malformed partial JSON — drop silently; downstream falls
1460
+ // back to whatever was captured at server_tool_start.
1461
+ }
1462
+ }
1463
+ currentServerToolUseId = undefined;
1464
+ accumulatedServerToolInputJson = "";
1348
1465
  // Flush residual text buffer unless it's exactly a sentinel.
1349
1466
  if (textBuffer.length > 0 && !isCompleteSentinel(textBuffer)) {
1350
1467
  onEvent?.({ type: "text_delta", text: textBuffer });
@@ -1354,6 +1471,13 @@ export class AnthropicProvider implements Provider {
1354
1471
  });
1355
1472
 
1356
1473
  stream.on("inputJson", (partialJson) => {
1474
+ if (currentServerToolUseId) {
1475
+ // Server-tool input (e.g. `web_search` query) — accumulate without
1476
+ // emitting `input_json_delta`; the daemon only consumes the
1477
+ // finalized value from `server_tool_complete.resolvedInput`.
1478
+ accumulatedServerToolInputJson += partialJson;
1479
+ return;
1480
+ }
1357
1481
  if (!currentStreamingToolName) return;
1358
1482
  accumulatedInputJson += partialJson;
1359
1483
  const now = Date.now();
@@ -1544,6 +1668,9 @@ export class AnthropicProvider implements Provider {
1544
1668
  case "text":
1545
1669
  return { type: "text", text: block.text };
1546
1670
  case "thinking":
1671
+ if (!block.signature) {
1672
+ return null;
1673
+ }
1547
1674
  return {
1548
1675
  type: "thinking",
1549
1676
  thinking: block.thinking,
@@ -23,10 +23,12 @@ import { AsyncLocalStorage } from "node:async_hooks";
23
23
 
24
24
  import { resolveCallSiteConfig } from "../config/llm-resolver.js";
25
25
  import { getConfig } from "../config/loader.js";
26
+ import { getDb } from "../memory/db-connection.js";
26
27
  import {
27
28
  ConnectionResolutionError,
28
29
  tryResolveProviderForConnectionName,
29
30
  } from "./connection-resolution.js";
31
+ import { listConnections } from "./inference/connections.js";
30
32
  import type { ProvidersConfig } from "./registry.js";
31
33
  import type {
32
34
  Message,
@@ -142,16 +144,32 @@ export class CallSiteRoutingProvider implements Provider {
142
144
  overrideProfile,
143
145
  });
144
146
 
145
- if (resolved.provider_connection) {
147
+ let connectionName = resolved.provider_connection;
148
+
149
+ // When no connection is set and the provider differs from the default,
150
+ // auto-resolve to an active connection for the provider (handles the
151
+ // "Any active X connection" case where the profile set provider but
152
+ // not provider_connection, and the merge didn't inherit one).
153
+ if (!connectionName && resolved.provider !== this.defaultProvider.name) {
154
+ try {
155
+ const candidates = listConnections(getDb(), {
156
+ provider: resolved.provider,
157
+ });
158
+ const active = candidates.find((c) => c.status === "active");
159
+ if (active) {
160
+ connectionName = active.name;
161
+ }
162
+ } catch {
163
+ // DB not available — fall through to the original error path.
164
+ }
165
+ }
166
+
167
+ if (connectionName) {
146
168
  const connectionProvider = await this.resolveByConnection(
147
- resolved.provider_connection,
169
+ connectionName,
148
170
  resolved.provider,
149
171
  );
150
172
  if (connectionProvider) return connectionProvider;
151
- // Soft credential failure — the connection-resolution helper
152
- // returned null because the underlying auth bundle yields no
153
- // usable adapter (or threw transiently). Reuse the default for
154
- // graceful per-call degradation.
155
173
  return this.defaultProvider;
156
174
  }
157
175
 
@@ -30,7 +30,7 @@
30
30
  import { resolveCallSiteConfig } from "../config/llm-resolver.js";
31
31
  import { getDb } from "../memory/db-connection.js";
32
32
  import { getLogger } from "../util/logger.js";
33
- import { getConnection } from "./inference/connections.js";
33
+ import { getConnection, listConnections } from "./inference/connections.js";
34
34
  import type { ProvidersConfig } from "./registry.js";
35
35
  import { resolveProviderFromConnection } from "./registry.js";
36
36
  import type { Provider } from "./types.js";
@@ -104,15 +104,42 @@ export async function tryResolveProviderForConnectionName(
104
104
  );
105
105
  }
106
106
  if (expectedProvider && connection.provider !== expectedProvider) {
107
- throw new ConnectionResolutionError(
108
- connectionName,
109
- "provider_mismatch",
110
- `provider_connection "${connectionName}" has provider="${connection.provider}" but resolving profile declared provider="${expectedProvider}" — set the profile's provider_connection to a row matching its provider`,
111
- );
107
+ // Mismatch usually means the config deep-merge inherited a stale
108
+ // provider_connection from a lower layer (e.g. profile sets a BYOK
109
+ // provider with "Any active" but the default layer's
110
+ // "anthropic-managed" leaked through). Try to find an active connection
111
+ // for the expected provider before giving up.
112
+ let resolved = false;
113
+ try {
114
+ const db = getDb();
115
+ const candidates = listConnections(db, { provider: expectedProvider });
116
+ const active = candidates.find((c) => c.status === "active");
117
+ if (active) {
118
+ log.info(
119
+ {
120
+ originalConnection: connectionName,
121
+ resolvedConnection: active.name,
122
+ expectedProvider,
123
+ },
124
+ "Auto-resolved stale provider_connection to matching active connection",
125
+ );
126
+ connection = active;
127
+ resolved = true;
128
+ }
129
+ } catch {
130
+ // DB not available — fall through to the original error.
131
+ }
132
+ if (!resolved) {
133
+ throw new ConnectionResolutionError(
134
+ connectionName,
135
+ "provider_mismatch",
136
+ `provider_connection "${connectionName}" has provider="${connection.provider}" but resolving profile declared provider="${expectedProvider}" — set the profile's provider_connection to a row matching its provider`,
137
+ );
138
+ }
112
139
  }
113
140
  if (connection.status === "disabled") {
114
141
  log.debug(
115
- { connectionName },
142
+ { connectionName, provider: connection.provider },
116
143
  "provider_connection is disabled — returning null",
117
144
  );
118
145
  return null;
@@ -154,13 +181,36 @@ export async function resolveDefaultProvider(
154
181
  config: ProvidersConfig,
155
182
  ): Promise<Provider | null> {
156
183
  const resolved = resolveCallSiteConfig("mainAgent", config.llm);
157
- const connectionName = resolved.provider_connection;
184
+ let connectionName = resolved.provider_connection;
158
185
  if (!connectionName) {
159
- throw new ConnectionResolutionError(
160
- "<llm.default>",
161
- "missing_connection",
162
- `llm.default.provider_connection is unset every profile must declare a provider_connection. The boot-time backfill in lifecycle.ts populates this field; if you see this error, the backfill did not run or the field was manually cleared.`,
163
- );
186
+ // The merged config has no provider_connection — the profile likely set
187
+ // provider without a connection ("Any active" selection), and the merge
188
+ // cleared or failed to inherit one. Try to find an active connection
189
+ // for the provider before giving up.
190
+ if (resolved.provider) {
191
+ try {
192
+ const candidates = listConnections(getDb(), {
193
+ provider: resolved.provider,
194
+ });
195
+ const active = candidates.find((c) => c.status === "active");
196
+ if (active) {
197
+ log.info(
198
+ { provider: resolved.provider, resolvedConnection: active.name },
199
+ "Auto-resolved missing provider_connection for default provider",
200
+ );
201
+ connectionName = active.name;
202
+ }
203
+ } catch {
204
+ // DB not available — fall through to the original error.
205
+ }
206
+ }
207
+ if (!connectionName) {
208
+ throw new ConnectionResolutionError(
209
+ "<llm.default>",
210
+ "missing_connection",
211
+ `llm.default.provider_connection is unset — every profile must declare a provider_connection. The boot-time backfill in lifecycle.ts populates this field; if you see this error, the backfill did not run or the field was manually cleared.`,
212
+ );
213
+ }
164
214
  }
165
215
  return tryResolveProviderForConnectionName(
166
216
  connectionName,
@@ -1,3 +1,4 @@
1
+ import { PROVIDER_CATALOG } from "../model-catalog.js";
1
2
  import { OpenAIChatCompletionsProvider } from "../openai/chat-completions-provider.js";
2
3
 
3
4
  export interface FireworksProviderOptions {
@@ -8,6 +9,15 @@ export interface FireworksProviderOptions {
8
9
 
9
10
  const DEFAULT_FIREWORKS_BASE_URL = "https://api.fireworks.ai/inference/v1";
10
11
 
12
+ const FIREWORKS_MODEL_EFFORT_CEILINGS: ReadonlyMap<
13
+ string,
14
+ "high" | "xhigh" | "max"
15
+ > = new Map(
16
+ PROVIDER_CATALOG.find((p) => p.id === "fireworks")?.models.flatMap((m) =>
17
+ m.maxEffort ? ([[m.id, m.maxEffort]] as const) : [],
18
+ ) ?? [],
19
+ );
20
+
11
21
  export class FireworksProvider extends OpenAIChatCompletionsProvider {
12
22
  constructor(
13
23
  apiKey: string,
@@ -19,9 +29,17 @@ export class FireworksProvider extends OpenAIChatCompletionsProvider {
19
29
  providerName: "fireworks",
20
30
  providerLabel: "Fireworks",
21
31
  streamTimeoutMs: options.streamTimeoutMs,
22
- // Fireworks' OpenAI-compatible chat-completions API documents only
23
- // low|medium|high for reasoning_effort; sending "xhigh" 4xxs upstream.
32
+ // Fallback for models not declared in the catalog. Most Fireworks
33
+ // chat-completions models only document `low|medium|high`; per-model
34
+ // overrides (e.g. DeepSeek V4 → "max") come from
35
+ // {@link resolveMaxReasoningEffort}.
24
36
  maxReasoningEffort: "high",
25
37
  });
26
38
  }
39
+
40
+ protected override resolveMaxReasoningEffort(
41
+ model: string,
42
+ ): "high" | "xhigh" | "max" {
43
+ return FIREWORKS_MODEL_EFFORT_CEILINGS.get(model) ?? "high";
44
+ }
27
45
  }
@@ -0,0 +1,74 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import { OpenAIChatCompletionsProvider } from "../../openai/chat-completions-provider.js";
4
+ import {
5
+ buildProviderAdapter,
6
+ createAdapterFromConnection,
7
+ } from "../adapter-factory.js";
8
+ import type { ProviderConnection, ResolvedAuth } from "../auth.js";
9
+
10
+ describe("openai-compatible adapter factory", () => {
11
+ test("buildProviderAdapter returns an OpenAIChatCompletionsProvider", () => {
12
+ const adapter = buildProviderAdapter("openai-compatible", {
13
+ apiKey: "test-key",
14
+ model: "my-local-model",
15
+ streamTimeoutMs: 60_000,
16
+ baseURL: "http://localhost:8080/v1",
17
+ useNativeWebSearch: false,
18
+ });
19
+ expect(adapter).toBeInstanceOf(OpenAIChatCompletionsProvider);
20
+ });
21
+
22
+ test("createAdapterFromConnection wires baseURL from ResolvedAuth", () => {
23
+ const connection: ProviderConnection = {
24
+ name: "my-vllm",
25
+ provider: "openai-compatible",
26
+ auth: { type: "api_key", credential: "cred-vllm" },
27
+ status: "active",
28
+ label: "vLLM",
29
+ baseUrl: "http://localhost:8080/v1",
30
+ models: [{ id: "my-model" }],
31
+ createdAt: Date.now(),
32
+ updatedAt: Date.now(),
33
+ isManaged: false,
34
+ };
35
+
36
+ const resolvedAuth: ResolvedAuth = {
37
+ kind: "header",
38
+ headers: { Authorization: "Bearer sk-test" },
39
+ baseUrl: "http://localhost:8080/v1",
40
+ };
41
+
42
+ const adapter = createAdapterFromConnection(connection, resolvedAuth, {
43
+ model: "my-model",
44
+ streamTimeoutMs: 60_000,
45
+ });
46
+
47
+ expect(adapter).not.toBeNull();
48
+ });
49
+
50
+ test("createAdapterFromConnection rejects 'none' auth for openai-compatible", () => {
51
+ const connection: ProviderConnection = {
52
+ name: "my-vllm",
53
+ provider: "openai-compatible",
54
+ auth: { type: "none" },
55
+ status: "active",
56
+ label: null,
57
+ baseUrl: "http://localhost:8080/v1",
58
+ models: [{ id: "my-model" }],
59
+ createdAt: Date.now(),
60
+ updatedAt: Date.now(),
61
+ isManaged: false,
62
+ };
63
+
64
+ const resolvedAuth: ResolvedAuth = { kind: "none" };
65
+
66
+ const adapter = createAdapterFromConnection(connection, resolvedAuth, {
67
+ model: "my-model",
68
+ });
69
+
70
+ // openai-compatible is setupMode: "api-key", not keyless, so none auth
71
+ // should be rejected.
72
+ expect(adapter).toBeNull();
73
+ });
74
+ });