@vellumai/assistant 0.6.4 → 0.6.5

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 (717) hide show
  1. package/.prettierignore +5 -0
  2. package/ARCHITECTURE.md +32 -36
  3. package/Dockerfile +12 -0
  4. package/README.md +3 -4
  5. package/bun.lock +8 -3
  6. package/docs/architecture/integrations.md +1 -20
  7. package/docs/architecture/security.md +16 -16
  8. package/docs/error-handling.md +111 -0
  9. package/docs/skills.md +10 -10
  10. package/docs/stt-provider-onboarding.md +2 -1
  11. package/knip.json +9 -2
  12. package/node_modules/@vellumai/ces-contracts/package.json +2 -1
  13. package/node_modules/@vellumai/ces-contracts/src/__tests__/trust-rules.test.ts +471 -0
  14. package/node_modules/@vellumai/ces-contracts/src/trust-rules.ts +398 -4
  15. package/node_modules/@vellumai/credential-storage/bun.lock +2 -2
  16. package/node_modules/@vellumai/credential-storage/package.json +2 -2
  17. package/node_modules/@vellumai/credential-storage/src/oauth-runtime.ts +20 -2
  18. package/node_modules/@vellumai/egress-proxy/bun.lock +2 -2
  19. package/node_modules/@vellumai/egress-proxy/package.json +2 -2
  20. package/openapi.yaml +123 -11
  21. package/package.json +6 -3
  22. package/scripts/generate-openapi.ts +50 -11
  23. package/src/__tests__/agent-loop-callsite-precedence.test.ts +318 -0
  24. package/src/__tests__/agent-loop-sentry-hygiene.test.ts +137 -0
  25. package/src/__tests__/agent-loop.test.ts +112 -1
  26. package/src/__tests__/anthropic-error-formatting.test.ts +98 -0
  27. package/src/__tests__/anthropic-provider.test.ts +171 -2
  28. package/src/__tests__/approval-cascade.test.ts +31 -10
  29. package/src/__tests__/approval-routes-http.test.ts +134 -10
  30. package/src/__tests__/assistant-attachments.test.ts +44 -0
  31. package/src/__tests__/assistant-feature-flags-integration.test.ts +29 -0
  32. package/src/__tests__/browser-fill-credential.test.ts +1 -1
  33. package/src/__tests__/browser-identifier-parity-guard.test.ts +53 -0
  34. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +23 -33
  35. package/src/__tests__/browser-skill-endstate.test.ts +51 -182
  36. package/src/__tests__/btw-routes.test.ts +47 -1
  37. package/src/__tests__/call-controller.test.ts +1 -2
  38. package/src/__tests__/call-site-routing-provider.test.ts +214 -0
  39. package/src/__tests__/catalog-cache.test.ts +27 -4
  40. package/src/__tests__/channel-approval-routes.test.ts +4 -4
  41. package/src/__tests__/channel-reply-delivery.test.ts +300 -2
  42. package/src/__tests__/checker.test.ts +428 -501
  43. package/src/__tests__/cli-command-risk-guard.test.ts +30 -33
  44. package/src/__tests__/compaction-circuit-breaker.test.ts +336 -0
  45. package/src/__tests__/compaction.benchmark.test.ts +1 -1
  46. package/src/__tests__/config-analysis.test.ts +11 -28
  47. package/src/__tests__/config-loader-backfill.test.ts +174 -0
  48. package/src/__tests__/config-loader-corrupt.test.ts +183 -0
  49. package/src/__tests__/config-loader-quarantine-bulletin.test.ts +202 -0
  50. package/src/__tests__/config-schema-cmd.test.ts +11 -5
  51. package/src/__tests__/config-schema.test.ts +427 -114
  52. package/src/__tests__/config-watcher.test.ts +2 -2
  53. package/src/__tests__/contact-store-user-file.test.ts +72 -73
  54. package/src/__tests__/contacts-write.test.ts +4 -4
  55. package/src/__tests__/context-token-estimator.test.ts +191 -1
  56. package/src/__tests__/context-window-manager.test.ts +530 -2
  57. package/src/__tests__/conversation-abort-tool-results.test.ts +30 -16
  58. package/src/__tests__/conversation-agent-loop-overflow.test.ts +61 -17
  59. package/src/__tests__/conversation-agent-loop.test.ts +412 -82
  60. package/src/__tests__/conversation-attachments.test.ts +1 -1
  61. package/src/__tests__/conversation-confirmation-signals.test.ts +30 -9
  62. package/src/__tests__/conversation-error.test.ts +37 -6
  63. package/src/__tests__/conversation-history-web-search.test.ts +6 -0
  64. package/src/__tests__/conversation-init.benchmark.test.ts +36 -0
  65. package/src/__tests__/conversation-lifecycle.test.ts +336 -0
  66. package/src/__tests__/conversation-load-history-repair.test.ts +27 -10
  67. package/src/__tests__/conversation-pre-run-repair.test.ts +30 -16
  68. package/src/__tests__/conversation-process-callsite.test.ts +306 -0
  69. package/src/__tests__/conversation-provider-retry-repair.test.ts +30 -16
  70. package/src/__tests__/conversation-queue.test.ts +41 -26
  71. package/src/__tests__/conversation-routes-disk-view.test.ts +29 -1
  72. package/src/__tests__/conversation-routes-slash-commands.test.ts +31 -3
  73. package/src/__tests__/conversation-runtime-assembly.test.ts +2735 -55
  74. package/src/__tests__/conversation-runtime-workspace.test.ts +12 -12
  75. package/src/__tests__/conversation-skill-tools.test.ts +12 -146
  76. package/src/__tests__/conversation-slash-queue.test.ts +34 -19
  77. package/src/__tests__/conversation-slash-unknown.test.ts +30 -16
  78. package/src/__tests__/conversation-speed-override.test.ts +30 -11
  79. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +1035 -0
  80. package/src/__tests__/conversation-surfaces-standalone.test.ts +630 -0
  81. package/src/__tests__/conversation-title-service.test.ts +2 -2
  82. package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +1 -1
  83. package/src/__tests__/conversation-unread-route.test.ts +2 -2
  84. package/src/__tests__/conversation-usage.test.ts +3 -1
  85. package/src/__tests__/conversation-workspace-cache-state.test.ts +31 -10
  86. package/src/__tests__/conversation-workspace-injection.test.ts +43 -15
  87. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +44 -16
  88. package/src/__tests__/credential-broker-browser-fill.test.ts +110 -0
  89. package/src/__tests__/credential-security-invariants.test.ts +3 -0
  90. package/src/__tests__/credential-storage-oauth-compat.test.ts +18 -0
  91. package/src/__tests__/credential-storage-static-compat.test.ts +28 -0
  92. package/src/__tests__/credential-vault-unit.test.ts +135 -19
  93. package/src/__tests__/credentials-cli.test.ts +1 -9
  94. package/src/__tests__/cross-provider-web-search.test.ts +84 -0
  95. package/src/__tests__/daemon-server-persist-and-process-callsite.test.ts +92 -0
  96. package/src/__tests__/delete-propagation.test.ts +437 -0
  97. package/src/__tests__/dm-backfill.test.ts +417 -0
  98. package/src/__tests__/dm-persistence.test.ts +227 -0
  99. package/src/__tests__/edit-propagation.test.ts +280 -0
  100. package/src/__tests__/ephemeral-permissions.test.ts +93 -3
  101. package/src/__tests__/estimator-calibration-integration.test.ts +208 -0
  102. package/src/__tests__/estimator-calibration.test.ts +213 -0
  103. package/src/__tests__/extension-id-sync-guard.test.ts +26 -7
  104. package/src/__tests__/file-write-tool.test.ts +151 -1
  105. package/src/__tests__/filing-service.test.ts +255 -0
  106. package/src/__tests__/gemini-provider.test.ts +0 -3
  107. package/src/__tests__/guardian-grant-minting.test.ts +8 -0
  108. package/src/__tests__/headless-browser-interactions.test.ts +1 -1
  109. package/src/__tests__/heartbeat-service.test.ts +96 -15
  110. package/src/__tests__/host-shell-tool.test.ts +124 -18
  111. package/src/__tests__/http-user-message-parity.test.ts +29 -1
  112. package/src/__tests__/inbound-slack-persistence.test.ts +340 -0
  113. package/src/__tests__/intent-routing.test.ts +1 -40
  114. package/src/__tests__/llm-catalog-parity.test.ts +174 -0
  115. package/src/__tests__/llm-context-normalization.test.ts +121 -0
  116. package/src/__tests__/llm-resolver.test.ts +214 -0
  117. package/src/__tests__/llm-schema.test.ts +223 -0
  118. package/src/__tests__/managed-proxy-context.test.ts +6 -2
  119. package/src/__tests__/messaging-skill-split.test.ts +3 -34
  120. package/src/__tests__/migration-import-from-url.test.ts +684 -0
  121. package/src/__tests__/model-intents.test.ts +9 -83
  122. package/src/__tests__/notification-decision-fallback.test.ts +0 -10
  123. package/src/__tests__/notification-decision-identity.test.ts +0 -9
  124. package/src/__tests__/notification-decision-recipient-context.test.ts +0 -9
  125. package/src/__tests__/oauth-store.test.ts +10 -7
  126. package/src/__tests__/oauth2-gateway-transport.test.ts +8 -3
  127. package/src/__tests__/oauth2-refresh-retry.test.ts +279 -0
  128. package/src/__tests__/openai-provider.test.ts +7 -0
  129. package/src/__tests__/openai-responses-provider.test.ts +396 -0
  130. package/src/__tests__/openrouter-provider-only.test.ts +135 -0
  131. package/src/__tests__/outbound-slack-persistence.test.ts +293 -0
  132. package/src/__tests__/permission-checker-host-gate.test.ts +1 -1
  133. package/src/__tests__/permission-mode.test.ts +16 -0
  134. package/src/__tests__/permission-types.test.ts +0 -1
  135. package/src/__tests__/persona-resolver.test.ts +13 -13
  136. package/src/__tests__/pkb-autoinject.test.ts +37 -1
  137. package/src/__tests__/platform-bash-auto-approve.test.ts +1 -1
  138. package/src/__tests__/pricing.test.ts +50 -3
  139. package/src/__tests__/profiler-routes.test.ts +1 -1
  140. package/src/__tests__/provider-commit-message-generator.test.ts +14 -84
  141. package/src/__tests__/provider-env-vars-scope.test.ts +52 -0
  142. package/src/__tests__/provider-error-scenarios.test.ts +135 -6
  143. package/src/__tests__/provider-managed-proxy-integration.test.ts +42 -11
  144. package/src/__tests__/provider-registry-ollama.test.ts +1 -2
  145. package/src/__tests__/proxy-approval-callback.test.ts +0 -1
  146. package/src/__tests__/reaction-persistence.test.ts +560 -0
  147. package/src/__tests__/relay-server.test.ts +1 -1
  148. package/src/__tests__/require-fresh-approval.test.ts +1 -1
  149. package/src/__tests__/retry-openrouter-only-normalization.test.ts +136 -0
  150. package/src/__tests__/retry-thinking-tool-choice.test.ts +226 -0
  151. package/src/__tests__/risk-classifier-parity.test.ts +230 -0
  152. package/src/__tests__/sanitize-config-for-transfer.test.ts +78 -1
  153. package/src/__tests__/secret-ingress-http.test.ts +28 -0
  154. package/src/__tests__/secret-prompter-channel-fallback.test.ts +125 -0
  155. package/src/__tests__/secret-routes-managed-proxy.test.ts +2 -3
  156. package/src/__tests__/secret-scanner-executor.test.ts +1 -1
  157. package/src/__tests__/send-endpoint-busy.test.ts +29 -1
  158. package/src/__tests__/server-history-render.test.ts +31 -0
  159. package/src/__tests__/shell-parser-property.test.ts +13 -13
  160. package/src/__tests__/skill-cache-store.test.ts +182 -0
  161. package/src/__tests__/skills.test.ts +19 -33
  162. package/src/__tests__/slack-app-setup-skill-regression.test.ts +3 -1
  163. package/src/__tests__/slack-skill.test.ts +3 -8
  164. package/src/__tests__/starter-bundle.test.ts +35 -0
  165. package/src/__tests__/subagent-call-site-routing.test.ts +280 -0
  166. package/src/__tests__/suggestion-routes.test.ts +160 -3
  167. package/src/__tests__/system-prompt.test.ts +22 -35
  168. package/src/__tests__/task-runner.test.ts +3 -1
  169. package/src/__tests__/tcc-sandbox-deny.test.ts +198 -0
  170. package/src/__tests__/terminal-tools.test.ts +8 -0
  171. package/src/__tests__/test-support/browser-skill-harness.ts +2 -52
  172. package/src/__tests__/thread-backfill.test.ts +941 -0
  173. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -2
  174. package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -2
  175. package/src/__tests__/tool-executor.test.ts +60 -94
  176. package/src/__tests__/trust-store.test.ts +442 -109
  177. package/src/__tests__/update-bulletin-job.test.ts +389 -0
  178. package/src/__tests__/usage-cache-backfill-migration.test.ts +3 -1
  179. package/src/__tests__/verification-control-plane-policy.test.ts +1 -22
  180. package/src/__tests__/voice-session-bridge.test.ts +39 -0
  181. package/src/__tests__/volume-security-guard.test.ts +3 -2
  182. package/src/__tests__/web-search-history.test.ts +337 -0
  183. package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +343 -0
  184. package/src/__tests__/workspace-migration-043-release-notes-latex-rendering.test.ts +202 -0
  185. package/src/__tests__/workspace-migration-045-release-notes-meet-avatar.test.ts +210 -0
  186. package/src/__tests__/workspace-migration-drop-user-md.test.ts +11 -11
  187. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +841 -0
  188. package/src/__tests__/workspace-policy.test.ts +1 -13
  189. package/src/acp/client-handler.ts +1 -2
  190. package/src/agent/loop.ts +209 -17
  191. package/src/avatar/resvg-lazy.test.ts +136 -0
  192. package/src/avatar/resvg-lazy.ts +82 -9
  193. package/src/avatar/traits-png-sync.ts +21 -1
  194. package/src/browser/__tests__/operations.test.ts +163 -0
  195. package/src/browser/identifiers.ts +51 -0
  196. package/src/browser/operations.ts +660 -0
  197. package/src/browser/types.ts +81 -0
  198. package/src/calls/guardian-question-copy.ts +2 -2
  199. package/src/calls/telephony-stt-routing.ts +1 -1
  200. package/src/calls/voice-session-bridge.ts +1 -0
  201. package/src/cli/AGENTS.md +1 -1
  202. package/src/cli/commands/__tests__/attachment.test.ts +438 -0
  203. package/src/cli/commands/__tests__/browser.test.ts +554 -0
  204. package/src/cli/commands/__tests__/cache.test.ts +623 -0
  205. package/src/cli/commands/__tests__/email-list.test.ts +6 -0
  206. package/src/cli/commands/__tests__/email-send.test.ts +93 -1
  207. package/src/cli/commands/__tests__/image-generation.test.ts +666 -0
  208. package/src/cli/commands/__tests__/inference-send.test.ts +451 -0
  209. package/src/cli/commands/__tests__/stt-transcribe.test.ts +454 -0
  210. package/src/cli/commands/__tests__/task.test.ts +913 -0
  211. package/src/cli/commands/__tests__/tts-synthesize.test.ts +594 -0
  212. package/src/cli/commands/__tests__/ui-confirm.test.ts +650 -0
  213. package/src/cli/commands/__tests__/ui.test.ts +1215 -0
  214. package/src/cli/commands/__tests__/watchers.test.ts +716 -0
  215. package/src/cli/commands/attachment.ts +182 -0
  216. package/src/cli/commands/browser.ts +350 -0
  217. package/src/cli/commands/cache.ts +341 -0
  218. package/src/cli/commands/completions.ts +0 -3
  219. package/src/cli/commands/config.ts +6 -6
  220. package/src/cli/commands/conversations-import.ts +347 -0
  221. package/src/cli/commands/conversations.ts +14 -1
  222. package/src/cli/commands/email.ts +234 -194
  223. package/src/cli/commands/image-generation.ts +300 -0
  224. package/src/cli/commands/inference.ts +200 -0
  225. package/src/cli/commands/memory.ts +127 -17
  226. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -1
  227. package/src/cli/commands/platform/__tests__/connect.test.ts +0 -1
  228. package/src/cli/commands/platform/__tests__/disconnect.test.ts +0 -1
  229. package/src/cli/commands/platform/__tests__/status.test.ts +0 -1
  230. package/src/cli/commands/stt.ts +339 -0
  231. package/src/cli/commands/task.ts +795 -0
  232. package/src/cli/commands/trust.ts +50 -19
  233. package/src/cli/commands/tts.ts +273 -0
  234. package/src/cli/commands/ui.ts +670 -0
  235. package/src/cli/commands/watchers.ts +509 -0
  236. package/src/cli/lib/daemon-credential-client.ts +0 -19
  237. package/src/cli/program.ts +23 -4
  238. package/src/cli.ts +0 -37
  239. package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +23 -1
  240. package/src/config/bundled-skills/media-processing/services/reduce.ts +1 -1
  241. package/src/config/bundled-skills/messaging/SKILL.md +2 -2
  242. package/src/config/bundled-skills/messaging/TOOLS.json +4 -0
  243. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +8 -1
  244. package/src/config/bundled-skills/messaging/tools/messaging-read.ts +15 -1
  245. package/src/config/bundled-skills/messaging/tools/messaging-search.ts +21 -1
  246. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +11 -12
  247. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +9 -8
  248. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  249. package/src/config/bundled-tool-registry.ts +0 -175
  250. package/src/config/env.ts +7 -2
  251. package/src/config/feature-flag-registry.json +25 -9
  252. package/src/config/llm-resolver.ts +128 -0
  253. package/src/config/loader.ts +194 -10
  254. package/src/config/raw-config-utils.ts +30 -2
  255. package/src/config/sanitize-for-transfer.ts +35 -0
  256. package/src/config/schema.ts +30 -41
  257. package/src/config/schemas/analysis.ts +3 -22
  258. package/src/config/schemas/calls.ts +0 -4
  259. package/src/config/schemas/filing.ts +2 -7
  260. package/src/config/schemas/heartbeat.ts +0 -5
  261. package/src/config/schemas/inference.ts +3 -23
  262. package/src/config/schemas/llm.ts +318 -0
  263. package/src/config/schemas/memory-processing.ts +1 -9
  264. package/src/config/schemas/notifications.ts +4 -11
  265. package/src/config/schemas/platform.ts +3 -9
  266. package/src/config/schemas/security.ts +33 -0
  267. package/src/config/schemas/services.ts +9 -4
  268. package/src/config/schemas/stt.ts +1 -0
  269. package/src/config/schemas/tts.ts +53 -0
  270. package/src/config/schemas/updates.ts +1 -1
  271. package/src/config/schemas/workspace-git.ts +3 -40
  272. package/src/config/skills.ts +2 -2
  273. package/src/context/__tests__/compact-prompt.test.ts +45 -0
  274. package/src/context/__tests__/microcompact.test.ts +805 -0
  275. package/src/context/estimator-calibration.ts +136 -0
  276. package/src/context/microcompact.ts +443 -0
  277. package/src/context/prompts/compact.md +12 -0
  278. package/src/context/token-estimator.ts +61 -3
  279. package/src/context/window-manager.ts +229 -25
  280. package/src/credential-execution/approval-bridge.ts +0 -1
  281. package/src/credential-execution/executable-discovery.ts +19 -8
  282. package/src/credential-execution/process-manager.test.ts +109 -0
  283. package/src/credential-execution/process-manager.ts +65 -2
  284. package/src/daemon/approval-generators.ts +29 -4
  285. package/src/daemon/assistant-attachments.ts +24 -13
  286. package/src/daemon/classifier.ts +2 -2
  287. package/src/daemon/config-watcher.ts +0 -1
  288. package/src/daemon/context-overflow-reducer.ts +4 -1
  289. package/src/daemon/conversation-agent-loop-handlers.ts +79 -12
  290. package/src/daemon/conversation-agent-loop.ts +462 -80
  291. package/src/daemon/conversation-attachments.ts +2 -6
  292. package/src/daemon/conversation-error.ts +36 -1
  293. package/src/daemon/conversation-lifecycle.ts +30 -6
  294. package/src/daemon/conversation-messaging.ts +73 -4
  295. package/src/daemon/conversation-process.ts +10 -4
  296. package/src/daemon/conversation-queue-manager.ts +3 -0
  297. package/src/daemon/conversation-runtime-assembly.ts +760 -29
  298. package/src/daemon/conversation-slash.ts +2 -2
  299. package/src/daemon/conversation-surfaces.ts +389 -1
  300. package/src/daemon/conversation-tool-setup.ts +10 -5
  301. package/src/daemon/conversation-usage.ts +1 -1
  302. package/src/daemon/conversation.ts +118 -30
  303. package/src/daemon/external-skills-bootstrap.ts +41 -0
  304. package/src/daemon/guardian-action-generators.ts +34 -14
  305. package/src/daemon/handlers/config-model.test.ts +86 -0
  306. package/src/daemon/handlers/config-model.ts +54 -12
  307. package/src/daemon/handlers/conversations.ts +9 -2
  308. package/src/daemon/handlers/shared.ts +39 -11
  309. package/src/daemon/handlers/skills.ts +2 -2
  310. package/src/daemon/handlers/slack-channel-oauth-install.ts +197 -0
  311. package/src/daemon/lifecycle.ts +76 -14
  312. package/src/daemon/message-types/conversations.ts +14 -0
  313. package/src/daemon/message-types/messages.ts +9 -1
  314. package/src/daemon/message-types/trust.ts +0 -2
  315. package/src/daemon/parse-actual-tokens-from-error.test.ts +57 -1
  316. package/src/daemon/parse-actual-tokens-from-error.ts +66 -0
  317. package/src/daemon/pkb-context-tracker.test.ts +169 -0
  318. package/src/daemon/pkb-context-tracker.ts +125 -0
  319. package/src/daemon/pkb-reminder-builder.test.ts +70 -0
  320. package/src/daemon/pkb-reminder-builder.ts +31 -0
  321. package/src/daemon/providers-setup.ts +6 -0
  322. package/src/daemon/server.ts +117 -9
  323. package/src/daemon/tool-side-effects.ts +0 -9
  324. package/src/daemon/watch-handler.ts +4 -4
  325. package/src/daemon/web-search-history.ts +126 -0
  326. package/src/events/domain-events.ts +0 -1
  327. package/src/filing/filing-service.ts +9 -10
  328. package/src/heartbeat/heartbeat-service.ts +76 -28
  329. package/src/home/__tests__/feed-scheduler.test.ts +39 -11
  330. package/src/home/__tests__/rollup-producer.test.ts +44 -0
  331. package/src/home/assistant-feed-authoring.ts +4 -0
  332. package/src/home/emit-feed-event.ts +4 -0
  333. package/src/home/feed-scheduler.ts +20 -4
  334. package/src/home/feed-types.ts +56 -2
  335. package/src/home/relationship-state-writer.ts +2 -2
  336. package/src/home/rollup-producer.ts +34 -5
  337. package/src/home/suggested-prompts.ts +101 -0
  338. package/src/ipc/__tests__/attachment-ipc.test.ts +213 -0
  339. package/src/ipc/__tests__/browser-ipc.test.ts +339 -0
  340. package/src/ipc/__tests__/cache-ipc.test.ts +266 -0
  341. package/src/ipc/__tests__/socket-path.test.ts +73 -0
  342. package/src/ipc/__tests__/task-ipc.test.ts +577 -0
  343. package/src/ipc/__tests__/ui-request-route.test.ts +495 -0
  344. package/src/ipc/__tests__/watcher-ipc.test.ts +295 -0
  345. package/src/ipc/cli-client.ts +2 -1
  346. package/src/ipc/cli-server.ts +26 -8
  347. package/src/ipc/gateway-client.ts +4 -4
  348. package/src/ipc/routes/attachment.ts +114 -0
  349. package/src/ipc/routes/browser-context.ts +61 -0
  350. package/src/ipc/routes/browser.ts +96 -0
  351. package/src/ipc/routes/cache.ts +96 -0
  352. package/src/ipc/routes/index.ts +17 -1
  353. package/src/ipc/routes/task-queue.ts +226 -0
  354. package/src/ipc/routes/task.ts +173 -0
  355. package/src/ipc/routes/ui-request.ts +50 -0
  356. package/src/ipc/routes/watcher.ts +203 -0
  357. package/src/ipc/socket-path.ts +100 -0
  358. package/src/memory/__tests__/conversation-analyze-job.test.ts +9 -8
  359. package/src/memory/__tests__/conversation-group-migration.test.ts +99 -0
  360. package/src/memory/admin.ts +18 -0
  361. package/src/memory/conversation-analyze-job.ts +14 -13
  362. package/src/memory/conversation-attention-store.ts +13 -6
  363. package/src/memory/conversation-crud.ts +103 -3
  364. package/src/memory/conversation-group-migration.ts +38 -6
  365. package/src/memory/conversation-title-service.ts +7 -4
  366. package/src/memory/db-init.ts +2 -0
  367. package/src/memory/embedding-backend.ts +1 -1
  368. package/src/memory/graph/compaction.ts +299 -0
  369. package/src/memory/graph/consolidation.ts +4 -4
  370. package/src/memory/graph/conversation-graph-memory.ts +89 -29
  371. package/src/memory/graph/extraction.test.ts +272 -2
  372. package/src/memory/graph/extraction.ts +173 -51
  373. package/src/memory/graph/graph-search.test.ts +92 -0
  374. package/src/memory/graph/graph-search.ts +4 -1
  375. package/src/memory/graph/narrative.ts +2 -2
  376. package/src/memory/graph/pattern-scan.ts +2 -2
  377. package/src/memory/graph/retriever.test.ts +459 -0
  378. package/src/memory/graph/retriever.ts +230 -48
  379. package/src/memory/graph/store.ts +41 -0
  380. package/src/memory/graph/tool-handlers.ts +27 -0
  381. package/src/memory/graph/tools.ts +6 -1
  382. package/src/memory/indexer.ts +5 -5
  383. package/src/memory/job-handlers/conversation-starters.ts +23 -20
  384. package/src/memory/job-handlers/summarization.ts +2 -2
  385. package/src/memory/job-utils.ts +7 -1
  386. package/src/memory/jobs/embed-pkb-file.test.ts +168 -0
  387. package/src/memory/jobs/embed-pkb-file.ts +54 -0
  388. package/src/memory/jobs-store.ts +44 -3
  389. package/src/memory/jobs-worker.ts +4 -0
  390. package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +1 -1
  391. package/src/memory/migrations/220-normalize-user-file-by-principal.ts +2 -2
  392. package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +82 -0
  393. package/src/memory/migrations/index.ts +1 -0
  394. package/src/memory/pkb/pkb-index.test.ts +368 -0
  395. package/src/memory/pkb/pkb-index.ts +255 -0
  396. package/src/memory/pkb/pkb-reconcile.test.ts +251 -0
  397. package/src/memory/pkb/pkb-reconcile.ts +148 -0
  398. package/src/memory/pkb/pkb-search.test.ts +438 -0
  399. package/src/memory/pkb/pkb-search.ts +137 -0
  400. package/src/memory/pkb/types.ts +53 -0
  401. package/src/memory/qdrant-client.ts +122 -1
  402. package/src/memory/slack-thread-store.ts +37 -0
  403. package/src/messaging/providers/gmail/adapter.ts +6 -16
  404. package/src/messaging/providers/gmail/client.ts +22 -0
  405. package/src/messaging/providers/gmail/types.ts +7 -0
  406. package/src/messaging/providers/slack/adapter.ts +14 -2
  407. package/src/messaging/providers/slack/backfill.test.ts +257 -0
  408. package/src/messaging/providers/slack/backfill.ts +101 -0
  409. package/src/messaging/providers/slack/message-metadata.test.ts +316 -0
  410. package/src/messaging/providers/slack/message-metadata.ts +123 -0
  411. package/src/messaging/providers/slack/render-transcript.test.ts +1373 -0
  412. package/src/messaging/providers/slack/render-transcript.ts +443 -0
  413. package/src/messaging/style-analyzer.ts +5 -2
  414. package/src/notifications/README.md +9 -5
  415. package/src/notifications/decision-engine.ts +3 -9
  416. package/src/notifications/preference-extractor.ts +2 -6
  417. package/src/oauth/oauth-store.ts +1 -0
  418. package/src/oauth/platform-connection.test.ts +47 -0
  419. package/src/oauth/platform-connection.ts +15 -5
  420. package/src/oauth/seed-providers.ts +4 -2
  421. package/src/permissions/approval-policy.test.ts +948 -0
  422. package/src/permissions/approval-policy.ts +257 -0
  423. package/src/permissions/bash-risk-classifier.test.ts +1208 -0
  424. package/src/permissions/bash-risk-classifier.ts +707 -0
  425. package/src/permissions/checker.ts +217 -708
  426. package/src/permissions/command-registry.test.ts +535 -0
  427. package/src/permissions/command-registry.ts +825 -0
  428. package/src/permissions/defaults.ts +26 -78
  429. package/src/permissions/file-risk-classifier.test.ts +535 -0
  430. package/src/permissions/file-risk-classifier.ts +274 -0
  431. package/src/permissions/risk-types.ts +205 -0
  432. package/src/permissions/secret-prompter.ts +53 -2
  433. package/src/permissions/skill-risk-classifier.test.ts +311 -0
  434. package/src/permissions/skill-risk-classifier.ts +214 -0
  435. package/src/permissions/trust-client.ts +52 -25
  436. package/src/permissions/trust-store-interface.ts +1 -6
  437. package/src/permissions/trust-store.ts +161 -62
  438. package/src/permissions/types.ts +23 -14
  439. package/src/permissions/web-risk-classifier.test.ts +170 -0
  440. package/src/permissions/web-risk-classifier.ts +89 -0
  441. package/src/permissions/workspace-policy.ts +1 -16
  442. package/src/platform/client.ts +19 -1
  443. package/src/prompts/persona-resolver.ts +3 -3
  444. package/src/prompts/system-prompt.ts +19 -20
  445. package/src/prompts/templates/SOUL.md +2 -2
  446. package/src/prompts/update-bulletin-job.ts +190 -0
  447. package/src/providers/__tests__/context-overflow-error.test.ts +328 -0
  448. package/src/providers/__tests__/provider-env-vars.test.ts +102 -0
  449. package/src/providers/__tests__/retry-callsite.test.ts +424 -0
  450. package/src/providers/anthropic/client.ts +183 -14
  451. package/src/providers/call-site-routing.ts +71 -0
  452. package/src/providers/gemini/client.ts +65 -2
  453. package/src/providers/managed-proxy/constants.ts +2 -1
  454. package/src/providers/model-catalog.ts +501 -33
  455. package/src/providers/model-intents.ts +4 -4
  456. package/src/providers/openai/chat-completions-provider.ts +57 -1
  457. package/src/providers/openai/responses-provider.ts +86 -9
  458. package/src/providers/openrouter/client.ts +76 -9
  459. package/src/providers/provider-env-vars.ts +56 -0
  460. package/src/providers/provider-send-message.ts +22 -5
  461. package/src/providers/ratelimit.ts +4 -0
  462. package/src/providers/registry.ts +19 -8
  463. package/src/providers/retry.ts +174 -39
  464. package/src/providers/speech-to-text/__tests__/resolve.test.ts +55 -0
  465. package/src/providers/speech-to-text/google-gemini-live-stream.ts +4 -4
  466. package/src/providers/speech-to-text/provider-catalog.ts +17 -0
  467. package/src/providers/speech-to-text/resolve.ts +7 -0
  468. package/src/providers/speech-to-text/xai-realtime.test.ts +578 -0
  469. package/src/providers/speech-to-text/xai-realtime.ts +796 -0
  470. package/src/providers/speech-to-text/xai.test.ts +155 -0
  471. package/src/providers/speech-to-text/xai.ts +97 -0
  472. package/src/providers/types.ts +93 -3
  473. package/src/runtime/AGENTS.md +2 -2
  474. package/src/runtime/__tests__/agent-wake.test.ts +43 -2
  475. package/src/runtime/__tests__/interactive-ui.test.ts +673 -0
  476. package/src/runtime/agent-wake.ts +63 -22
  477. package/src/runtime/auth/route-policy.ts +4 -0
  478. package/src/runtime/btw-sidechain.ts +13 -3
  479. package/src/runtime/channel-reply-delivery.ts +106 -2
  480. package/src/runtime/decision-token.ts +116 -0
  481. package/src/runtime/gateway-client.ts +2 -2
  482. package/src/runtime/http-router.ts +32 -0
  483. package/src/runtime/http-server.ts +52 -1
  484. package/src/runtime/http-types.ts +23 -1
  485. package/src/runtime/interactive-ui.ts +362 -0
  486. package/src/runtime/invite-instruction-generator.ts +2 -2
  487. package/src/runtime/migrations/__tests__/gcs-signed-url.test.ts +176 -0
  488. package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +390 -0
  489. package/src/runtime/migrations/__tests__/vbundle-metadata-merge.test.ts +221 -0
  490. package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +1540 -0
  491. package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +453 -0
  492. package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +222 -0
  493. package/src/runtime/migrations/gcs-signed-url.ts +162 -0
  494. package/src/runtime/migrations/vbundle-importer.ts +154 -9
  495. package/src/runtime/migrations/vbundle-metadata-merge.ts +124 -0
  496. package/src/runtime/migrations/vbundle-streaming-importer.ts +2522 -0
  497. package/src/runtime/migrations/vbundle-streaming-validator.ts +244 -0
  498. package/src/runtime/migrations/vbundle-tar-stream.ts +217 -0
  499. package/src/runtime/migrations/vbundle-validator.ts +15 -6
  500. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +111 -0
  501. package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +114 -75
  502. package/src/runtime/routes/__tests__/migration-vellum-metadata-reconcile.test.ts +246 -0
  503. package/src/runtime/routes/approval-prompt-ts-tracker.ts +58 -0
  504. package/src/runtime/routes/approval-routes.ts +12 -17
  505. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +9 -0
  506. package/src/runtime/routes/avatar-routes.ts +20 -4
  507. package/src/runtime/routes/btw-routes.ts +1 -4
  508. package/src/runtime/routes/conversation-management-routes.ts +20 -2
  509. package/src/runtime/routes/conversation-routes.ts +133 -27
  510. package/src/runtime/routes/debug-routes.ts +1 -1
  511. package/src/runtime/routes/diagnostics-routes.ts +6 -4
  512. package/src/runtime/routes/events-routes.ts +16 -0
  513. package/src/runtime/routes/guardian-approval-interception.ts +33 -3
  514. package/src/runtime/routes/guardian-approval-prompt.ts +13 -3
  515. package/src/runtime/routes/home-feed-routes.ts +120 -2
  516. package/src/runtime/routes/inbound-message-handler.ts +912 -2
  517. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +113 -2
  518. package/src/runtime/routes/inbound-stages/background-dispatch.ts +61 -3
  519. package/src/runtime/routes/inbound-stages/edit-intercept.ts +129 -6
  520. package/src/runtime/routes/integrations/slack/channel.ts +25 -3
  521. package/src/runtime/routes/llm-context-normalization.ts +23 -1
  522. package/src/runtime/routes/migration-routes.ts +720 -124
  523. package/src/runtime/routes/settings-routes.ts +4 -2
  524. package/src/runtime/routes/trust-rules-routes.ts +30 -14
  525. package/src/runtime/routes/work-items-routes.test.ts +1 -1
  526. package/src/runtime/routes/work-items-routes.ts +3 -2
  527. package/src/runtime/services/__tests__/analyze-conversation.test.ts +25 -43
  528. package/src/runtime/services/analyze-conversation.ts +12 -16
  529. package/src/runtime/skill-route-registry.ts +28 -6
  530. package/src/schedule/scheduler.ts +8 -0
  531. package/src/security/__tests__/provider-key-env-fallback.test.ts +119 -0
  532. package/src/security/__tests__/untrusted-content.test.ts +109 -0
  533. package/src/security/oauth2.ts +98 -35
  534. package/src/security/secure-keys.ts +7 -8
  535. package/src/security/token-manager.ts +27 -13
  536. package/src/security/untrusted-content.ts +102 -0
  537. package/src/skills/catalog-cache.ts +26 -7
  538. package/src/skills/catalog-install.ts +31 -3
  539. package/src/skills/skill-cache-store.ts +97 -0
  540. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +76 -0
  541. package/src/stt/daemon-batch-transcriber.ts +33 -0
  542. package/src/stt/stt-stream-session.ts +8 -1
  543. package/src/stt/types.ts +5 -1
  544. package/src/subagent/manager.ts +41 -13
  545. package/src/tasks/ephemeral-permissions.ts +9 -4
  546. package/src/telemetry/usage-telemetry-reporter.ts +27 -5
  547. package/src/tools/browser/__tests__/browser-status.test.ts +45 -2
  548. package/src/tools/browser/browser-execution.ts +65 -38
  549. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +22 -0
  550. package/src/tools/credentials/tool-policy.ts +39 -5
  551. package/src/tools/credentials/vault.ts +9 -4
  552. package/src/tools/executor.ts +4 -0
  553. package/src/tools/filesystem/write.ts +52 -0
  554. package/src/tools/host-terminal/host-shell.ts +45 -5
  555. package/src/tools/memory/register.test.ts +185 -0
  556. package/src/tools/memory/register.ts +3 -1
  557. package/src/tools/network/web-fetch.ts +20 -10
  558. package/src/tools/network/web-search.ts +19 -4
  559. package/src/tools/permission-checker.ts +36 -15
  560. package/src/tools/policy-context.ts +25 -8
  561. package/src/tools/registry.ts +55 -3
  562. package/src/tools/side-effects.ts +0 -11
  563. package/src/tools/skills/execute.ts +2 -2
  564. package/src/tools/skills/sandbox-runner.ts +5 -2
  565. package/src/tools/terminal/backends/native.ts +51 -2
  566. package/src/tools/terminal/safe-env.ts +3 -2
  567. package/src/tools/terminal/shell.ts +1 -0
  568. package/src/tools/tool-manifest.ts +6 -21
  569. package/src/tools/types.ts +12 -3
  570. package/src/tools/verification-control-plane-policy.ts +1 -1
  571. package/src/tts/__tests__/provider-adapters.test.ts +240 -13
  572. package/src/tts/provider-catalog.ts +18 -0
  573. package/src/tts/providers/index.ts +2 -0
  574. package/src/tts/providers/xai-provider.ts +224 -0
  575. package/src/tts/types.ts +46 -0
  576. package/src/types/tar-stream.d.ts +66 -0
  577. package/src/util/json.ts +17 -0
  578. package/src/util/platform.ts +2 -2
  579. package/src/util/pricing.ts +15 -5
  580. package/src/watcher/engine.ts +1 -1
  581. package/src/watcher/providers/google-calendar.ts +134 -8
  582. package/src/watcher/providers/outlook-calendar.ts +42 -2
  583. package/src/workspace/git-service.ts +23 -4
  584. package/src/workspace/migrations/038-unify-llm-callsite-configs.ts +516 -0
  585. package/src/workspace/migrations/039-drop-legacy-llm-keys.ts +171 -0
  586. package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +154 -0
  587. package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +57 -0
  588. package/src/workspace/migrations/042-fix-backfill-google-gmail-settings-scope.ts +70 -0
  589. package/src/workspace/migrations/043-release-notes-latex-rendering.ts +75 -0
  590. package/src/workspace/migrations/044-bump-stale-provider-stream-timeout.ts +51 -0
  591. package/src/workspace/migrations/045-release-notes-meet-avatar.ts +130 -0
  592. package/src/workspace/migrations/AGENTS.md +1 -1
  593. package/src/workspace/migrations/registry.ts +16 -0
  594. package/src/workspace/provider-commit-message-generator.ts +19 -38
  595. package/src/__tests__/gmail-archive-fallback.test.ts +0 -193
  596. package/src/__tests__/gmail-archive-gate.test.ts +0 -246
  597. package/src/__tests__/gmail-preferences.test.ts +0 -117
  598. package/src/__tests__/outlook-attachments.test.ts +0 -301
  599. package/src/__tests__/outlook-automation-tools.test.ts +0 -425
  600. package/src/__tests__/outlook-categories.test.ts +0 -212
  601. package/src/__tests__/outlook-compose-tools.test.ts +0 -325
  602. package/src/__tests__/outlook-declutter-tools.test.ts +0 -585
  603. package/src/__tests__/outlook-follow-up.test.ts +0 -196
  604. package/src/__tests__/outlook-trash.test.ts +0 -77
  605. package/src/__tests__/outlook-unsubscribe.test.ts +0 -279
  606. package/src/__tests__/update-bulletin-format.test.ts +0 -181
  607. package/src/__tests__/update-bulletin-state.test.ts +0 -135
  608. package/src/__tests__/update-bulletin.test.ts +0 -478
  609. package/src/__tests__/update-template-contract.test.ts +0 -29
  610. package/src/cli/commands/doctor.ts +0 -341
  611. package/src/config/bundled-skills/browser/SKILL.md +0 -88
  612. package/src/config/bundled-skills/browser/TOOLS.json +0 -516
  613. package/src/config/bundled-skills/browser/tools/browser-attach.ts +0 -12
  614. package/src/config/bundled-skills/browser/tools/browser-click.ts +0 -12
  615. package/src/config/bundled-skills/browser/tools/browser-close.ts +0 -12
  616. package/src/config/bundled-skills/browser/tools/browser-detach.ts +0 -12
  617. package/src/config/bundled-skills/browser/tools/browser-extract.ts +0 -12
  618. package/src/config/bundled-skills/browser/tools/browser-fill-credential.ts +0 -12
  619. package/src/config/bundled-skills/browser/tools/browser-hover.ts +0 -12
  620. package/src/config/bundled-skills/browser/tools/browser-navigate.ts +0 -12
  621. package/src/config/bundled-skills/browser/tools/browser-press-key.ts +0 -12
  622. package/src/config/bundled-skills/browser/tools/browser-screenshot.ts +0 -12
  623. package/src/config/bundled-skills/browser/tools/browser-scroll.ts +0 -12
  624. package/src/config/bundled-skills/browser/tools/browser-select-option.ts +0 -12
  625. package/src/config/bundled-skills/browser/tools/browser-snapshot.ts +0 -12
  626. package/src/config/bundled-skills/browser/tools/browser-status.ts +0 -12
  627. package/src/config/bundled-skills/browser/tools/browser-type.ts +0 -12
  628. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +0 -49
  629. package/src/config/bundled-skills/browser/tools/browser-wait-for.ts +0 -12
  630. package/src/config/bundled-skills/chatgpt-import/SKILL.md +0 -27
  631. package/src/config/bundled-skills/chatgpt-import/TOOLS.json +0 -27
  632. package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +0 -378
  633. package/src/config/bundled-skills/gmail/SKILL.md +0 -221
  634. package/src/config/bundled-skills/gmail/TOOLS.json +0 -588
  635. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +0 -256
  636. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +0 -112
  637. package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +0 -44
  638. package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +0 -81
  639. package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +0 -108
  640. package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +0 -146
  641. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +0 -53
  642. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +0 -347
  643. package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +0 -59
  644. package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +0 -82
  645. package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +0 -26
  646. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +0 -347
  647. package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +0 -29
  648. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +0 -122
  649. package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +0 -67
  650. package/src/config/bundled-skills/gmail/tools/scan-result-store.ts +0 -100
  651. package/src/config/bundled-skills/gmail/tools/shared.ts +0 -47
  652. package/src/config/bundled-skills/google-calendar/SKILL.md +0 -51
  653. package/src/config/bundled-skills/google-calendar/TOOLS.json +0 -226
  654. package/src/config/bundled-skills/google-calendar/calendar-client.ts +0 -223
  655. package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +0 -27
  656. package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +0 -48
  657. package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +0 -19
  658. package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +0 -36
  659. package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +0 -58
  660. package/src/config/bundled-skills/google-calendar/tools/shared.ts +0 -17
  661. package/src/config/bundled-skills/google-calendar/types.ts +0 -97
  662. package/src/config/bundled-skills/outlook/SKILL.md +0 -196
  663. package/src/config/bundled-skills/outlook/TOOLS.json +0 -530
  664. package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +0 -85
  665. package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +0 -77
  666. package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +0 -84
  667. package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +0 -94
  668. package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +0 -49
  669. package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +0 -237
  670. package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +0 -161
  671. package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +0 -32
  672. package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +0 -272
  673. package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +0 -29
  674. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +0 -129
  675. package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +0 -87
  676. package/src/config/bundled-skills/outlook/tools/shared.ts +0 -20
  677. package/src/config/bundled-skills/outlook-calendar/SKILL.md +0 -51
  678. package/src/config/bundled-skills/outlook-calendar/TOOLS.json +0 -221
  679. package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +0 -252
  680. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +0 -53
  681. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +0 -74
  682. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +0 -18
  683. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +0 -46
  684. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +0 -36
  685. package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +0 -17
  686. package/src/config/bundled-skills/outlook-calendar/types.ts +0 -120
  687. package/src/config/bundled-skills/slack/SKILL.md +0 -108
  688. package/src/config/bundled-skills/tasks/SKILL.md +0 -37
  689. package/src/config/bundled-skills/tasks/TOOLS.json +0 -353
  690. package/src/config/bundled-skills/tasks/icon.svg +0 -34
  691. package/src/config/bundled-skills/tasks/tools/task-delete.ts +0 -12
  692. package/src/config/bundled-skills/tasks/tools/task-list-add.ts +0 -12
  693. package/src/config/bundled-skills/tasks/tools/task-list-remove.ts +0 -12
  694. package/src/config/bundled-skills/tasks/tools/task-list-show.ts +0 -12
  695. package/src/config/bundled-skills/tasks/tools/task-list-update.ts +0 -12
  696. package/src/config/bundled-skills/tasks/tools/task-list.ts +0 -12
  697. package/src/config/bundled-skills/tasks/tools/task-queue-run.ts +0 -12
  698. package/src/config/bundled-skills/tasks/tools/task-run.ts +0 -12
  699. package/src/config/bundled-skills/tasks/tools/task-save.ts +0 -12
  700. package/src/config/bundled-skills/watcher/SKILL.md +0 -31
  701. package/src/config/bundled-skills/watcher/TOOLS.json +0 -167
  702. package/src/config/bundled-skills/watcher/tools/watcher-create.ts +0 -12
  703. package/src/config/bundled-skills/watcher/tools/watcher-delete.ts +0 -12
  704. package/src/config/bundled-skills/watcher/tools/watcher-digest.ts +0 -12
  705. package/src/config/bundled-skills/watcher/tools/watcher-list.ts +0 -12
  706. package/src/config/bundled-skills/watcher/tools/watcher-update.ts +0 -12
  707. package/src/prompts/templates/UPDATES.md +0 -50
  708. package/src/prompts/update-bulletin-format.ts +0 -85
  709. package/src/prompts/update-bulletin-state.ts +0 -58
  710. package/src/prompts/update-bulletin-template-path.ts +0 -13
  711. package/src/prompts/update-bulletin.ts +0 -139
  712. package/src/shared/provider-env-vars.ts +0 -19
  713. package/src/tools/watcher/create.ts +0 -86
  714. package/src/tools/watcher/delete.ts +0 -36
  715. package/src/tools/watcher/digest.ts +0 -54
  716. package/src/tools/watcher/list.ts +0 -83
  717. package/src/tools/watcher/update.ts +0 -71
@@ -0,0 +1,443 @@
1
+ /**
2
+ * Pure chronological thread-tag rendering for Slack transcripts.
3
+ *
4
+ * Given a list of stored messages (post-upgrade rows with structured metadata
5
+ * AND legacy pre-upgrade rows with `metadata === null`), produces a flat
6
+ * `{role, content}[]` chronologically ordered with compact thread tags so
7
+ * the model can reason across sibling threads in one channel.
8
+ *
9
+ * The function is pure: no I/O, no implicit clock reads. Time is taken from
10
+ * `opts.now` only when needed for relative formatting. Sort and tag rendering
11
+ * are deterministic.
12
+ *
13
+ * Consumers wire this into inbound history rendering and the compaction
14
+ * boundary.
15
+ */
16
+
17
+ import { createHash } from "node:crypto";
18
+
19
+ import type { ContentBlock, Message } from "../../../providers/types.js";
20
+ import type { SlackMessageMetadata } from "./message-metadata.js";
21
+
22
+ export interface RenderableSlackMessage {
23
+ role: "user" | "assistant";
24
+ content: string;
25
+ /** `null` indicates a legacy pre-upgrade row stored without Slack metadata. */
26
+ metadata: SlackMessageMetadata | null;
27
+ /**
28
+ * Sender display name to prepend to the tag line (e.g. `"@alice"`), or
29
+ * `null` to omit the label entirely. Callers should pass `null` when the
30
+ * label would be redundant with the `role` slot — i.e. assistant rows and
31
+ * user rows with no real Slack displayName. Reaction rows always need a
32
+ * subject, so they receive a role-derived fallback if `null` is passed.
33
+ */
34
+ senderLabel: string | null;
35
+ /** Fallback sort key for legacy rows; ignored when metadata.channelTs is set. */
36
+ createdAt: number;
37
+ /**
38
+ * Full structured content blocks parsed from the persisted row, when
39
+ * available. Optional so existing fixtures and callers that only need the
40
+ * flattened `content` string continue to compile. `renderSlackTranscript`
41
+ * consumes this field to preserve replayable Anthropic blocks
42
+ * (`tool_use`, `tool_result`, `thinking`, `redacted_thinking`, `image`,
43
+ * `file`) in their original order, emitting the tag line inline at the
44
+ * position of the first `text` block. Non-replayable blocks
45
+ * (`ui_surface`, `server_tool_use`, `web_search_tool_result`, unknown
46
+ * types) are stripped; when stripping empties the row entirely, a
47
+ * fallback tag-line text block is emitted so chronology is preserved.
48
+ */
49
+ readonly contentBlocks?: readonly ContentBlock[];
50
+ }
51
+
52
+ export interface RenderOptions {
53
+ /** Reserved for future relative-time rendering; currently unused. */
54
+ now?: Date;
55
+ /** Cap rendered reactions per parent message; default 5. */
56
+ maxReactionsPerMessage?: number;
57
+ }
58
+
59
+ const DEFAULT_MAX_REACTIONS = 5;
60
+
61
+ /**
62
+ * Replayable Anthropic content-block types that we preserve verbatim from a
63
+ * persisted row when rendering the Slack chronological transcript.
64
+ *
65
+ * `text` is intentionally omitted — text content is subsumed into the tag
66
+ * line (e.g. `[11/14/23 14:25 @alice]: ...`) so callers reading the
67
+ * rendered output see one human-readable line per row rather than a raw
68
+ * text block stripped of thread context.
69
+ *
70
+ * Non-replayable types (`ui_surface`, `server_tool_use`,
71
+ * `web_search_tool_result`, unknown types) are dropped: `ui_surface` blocks
72
+ * are the assistant's ephemeral local UI scaffolding and not meaningful to
73
+ * replay; `server_tool_use` / `web_search_tool_result` carry provider-
74
+ * specific `encrypted_content` that becomes stale and is rejected by the
75
+ * provider on re-send.
76
+ */
77
+ const REPLAYABLE_BLOCK_TYPES = new Set<ContentBlock["type"]>([
78
+ "tool_use",
79
+ "tool_result",
80
+ "thinking",
81
+ "redacted_thinking",
82
+ "image",
83
+ "file",
84
+ ]);
85
+
86
+ /**
87
+ * Compute a short, stable, deterministic alias for a Slack message ts.
88
+ *
89
+ * Used as the "parent label" inside thread-reply tags so the model can
90
+ * cross-reference children with their parent without leaking raw ts values.
91
+ * First 6 hex chars of sha256(channelTs) prefixed with `M`.
92
+ */
93
+ export function parentAlias(channelTs: string): string {
94
+ const hash = createHash("sha256").update(channelTs).digest("hex");
95
+ return `M${hash.slice(0, 6)}`;
96
+ }
97
+
98
+ /**
99
+ * Format a Slack ts (`"1700000000.000100"`) as `MM/DD/YY HH:MM` (UTC).
100
+ *
101
+ * Slack ts is `<unix-seconds>.<microseconds>`; we treat it as a unix epoch
102
+ * second value for display purposes. Pure — derives only from the ts string.
103
+ */
104
+ function formatSlackTs(channelTs: string): string {
105
+ const seconds = Number.parseFloat(channelTs);
106
+ if (!Number.isFinite(seconds)) return "??/??/?? ??:??";
107
+ return formatEpochMs(seconds * 1000);
108
+ }
109
+
110
+ /**
111
+ * Format an epoch millisecond timestamp as `MM/DD/YY HH:MM` (UTC).
112
+ */
113
+ function formatEpochMs(ms: number): string {
114
+ if (!Number.isFinite(ms)) return "??/??/?? ??:??";
115
+ const d = new Date(ms);
116
+ const mo = String(d.getUTCMonth() + 1).padStart(2, "0");
117
+ const da = String(d.getUTCDate()).padStart(2, "0");
118
+ const yy = String(d.getUTCFullYear() % 100).padStart(2, "0");
119
+ const hh = String(d.getUTCHours()).padStart(2, "0");
120
+ const mm = String(d.getUTCMinutes()).padStart(2, "0");
121
+ return `${mo}/${da}/${yy} ${hh}:${mm}`;
122
+ }
123
+
124
+ /**
125
+ * Sort key for chronological ordering.
126
+ *
127
+ * Post-upgrade rows are ordered by their Slack `channelTs`; legacy rows
128
+ * (metadata === null) fall back to `createdAt`. Both produce a numeric
129
+ * seconds-since-epoch value so they intermix correctly.
130
+ */
131
+ function sortKey(msg: RenderableSlackMessage): number {
132
+ if (msg.metadata) {
133
+ const n = Number.parseFloat(msg.metadata.channelTs);
134
+ if (Number.isFinite(n)) return n;
135
+ }
136
+ // createdAt is epoch ms; convert to seconds for like-with-like comparison.
137
+ return msg.createdAt / 1000;
138
+ }
139
+
140
+ /**
141
+ * Render a single non-reaction message (post-upgrade or legacy) as one
142
+ * tagged line.
143
+ */
144
+ function renderMessage(msg: RenderableSlackMessage): string {
145
+ const meta = msg.metadata;
146
+ const senderPart = msg.senderLabel ? ` ${msg.senderLabel}` : "";
147
+ if (!meta) {
148
+ // Legacy pre-upgrade row: flat render, no thread tag.
149
+ const time = formatEpochMs(msg.createdAt);
150
+ return `[${time}${senderPart}]: ${msg.content}`;
151
+ }
152
+
153
+ const time = formatSlackTs(meta.channelTs);
154
+
155
+ if (meta.deletedAt !== undefined) {
156
+ const dtime = formatEpochMs(meta.deletedAt);
157
+ return `[${time}${senderPart} — deleted ${dtime}]`;
158
+ }
159
+
160
+ let head = `[${time}${senderPart}`;
161
+ if (meta.threadTs && meta.threadTs !== meta.channelTs) {
162
+ head += ` → ${parentAlias(meta.threadTs)}`;
163
+ }
164
+ if (meta.editedAt !== undefined) {
165
+ head += `, edited ${formatEpochMs(meta.editedAt)}`;
166
+ }
167
+ head += `]: ${msg.content}`;
168
+ return head;
169
+ }
170
+
171
+ /**
172
+ * Render a single reaction event as one tagged line.
173
+ *
174
+ * `[11/14/23 14:28 @bob reacted 👍 to M1a2b3c]` or
175
+ * `[11/14/23 14:28 @bob removed 👍 from M1a2b3c]`.
176
+ */
177
+ function renderReaction(msg: RenderableSlackMessage): string | null {
178
+ const meta = msg.metadata;
179
+ if (!meta || meta.eventKind !== "reaction" || !meta.reaction) return null;
180
+ const time = formatSlackTs(meta.channelTs);
181
+ const actor =
182
+ msg.senderLabel ?? (msg.role === "assistant" ? "@assistant" : "@user");
183
+ const verb = meta.reaction.op === "added" ? "reacted" : "removed";
184
+ const prep = meta.reaction.op === "added" ? "to" : "from";
185
+ const target = parentAlias(meta.reaction.targetChannelTs);
186
+ return `[${time} ${actor} ${verb} ${meta.reaction.emoji} ${prep} ${target}]`;
187
+ }
188
+
189
+ /**
190
+ * Build the content blocks for a single non-reaction message.
191
+ *
192
+ * Emits the tag line (`[MM/DD/YY HH:MM @sender ...]: body`) inline at the position of
193
+ * the first `text` block in `contentBlocks`, and preserves any replayable
194
+ * blocks (`tool_use`, `tool_result`, `thinking`, `redacted_thinking`,
195
+ * `image`, `file`) in their original order. Non-replayable blocks
196
+ * (`ui_surface`, `server_tool_use`, `web_search_tool_result`, unknown types)
197
+ * are dropped.
198
+ *
199
+ * Special cases:
200
+ * - **Deleted rows**: always emit a single tag-line block; replayable blocks
201
+ * (if any) are discarded because the delete is a logical erasure.
202
+ * - **Legacy rows** (no structured `contentBlocks`, or empty array): fall
203
+ * back to a single tag-line block to preserve pre-plumbing behaviour.
204
+ * - **Pure tool-only rows** (`contentBlocks` present but no `text` block):
205
+ * emit only the replayable blocks — no tag line. Anthropic accepts role-
206
+ * correct messages with only tool blocks.
207
+ * - **All-non-replayable rows** (`contentBlocks` present but every block is
208
+ * filtered out — e.g. a row whose only blocks are `server_tool_use` or
209
+ * `ui_surface`): emit a single fallback tag-line text block annotated
210
+ * with the stripped block types/names. Dropping the row entirely would
211
+ * silently alter chronology and can orphan adjacent tool_result context
212
+ * in later repair/conversion steps.
213
+ */
214
+ function buildMessageContentBlocks(
215
+ msg: RenderableSlackMessage,
216
+ tagLine: string,
217
+ ): ContentBlock[] {
218
+ const isDeleted = msg.metadata?.deletedAt !== undefined;
219
+ const blocks = msg.contentBlocks;
220
+
221
+ // Deleted rows: single tag line, drop any replayable content.
222
+ if (isDeleted) {
223
+ return [{ type: "text", text: tagLine }];
224
+ }
225
+
226
+ // Legacy / unplumbed rows: fall back to single tag line.
227
+ if (!blocks || blocks.length === 0) {
228
+ return [{ type: "text", text: tagLine }];
229
+ }
230
+
231
+ const out: ContentBlock[] = [];
232
+ let tagEmitted = false;
233
+ const strippedLabels: string[] = [];
234
+ for (const block of blocks) {
235
+ if (block.type === "text") {
236
+ if (!tagEmitted) {
237
+ out.push({ type: "text", text: tagLine });
238
+ tagEmitted = true;
239
+ }
240
+ // Subsequent text blocks are already subsumed into the tag line.
241
+ continue;
242
+ }
243
+ if (REPLAYABLE_BLOCK_TYPES.has(block.type)) {
244
+ out.push(block);
245
+ continue;
246
+ }
247
+ // Non-replayable (ui_surface, server_tool_use, web_search_tool_result,
248
+ // unknown) — drop, but remember what we saw in case we need a fallback.
249
+ if (block.type === "server_tool_use") {
250
+ strippedLabels.push(`server_tool_use(${block.name})`);
251
+ } else {
252
+ strippedLabels.push(block.type);
253
+ }
254
+ }
255
+
256
+ // Non-empty source fully filtered to nothing: emit a fallback tag line so
257
+ // the turn still appears in chronology. Annotate with the stripped block
258
+ // types/names so the model has a hint about what was there.
259
+ if (out.length === 0) {
260
+ const suffix =
261
+ strippedLabels.length > 0
262
+ ? ` [stripped non-replayable: ${strippedLabels.join(", ")}]`
263
+ : "";
264
+ return [{ type: "text", text: `${tagLine}${suffix}` }];
265
+ }
266
+ return out;
267
+ }
268
+
269
+ /**
270
+ * Render a chronological transcript with compact thread tags.
271
+ *
272
+ * Sort is stable: messages with identical sort keys preserve their input
273
+ * order so callers controlling input ordering can break ties deterministically.
274
+ *
275
+ * Reactions are rendered as their own lines (`[time @actor reacted ... to Mxxx]`),
276
+ * but capped per-target at `opts.maxReactionsPerMessage` (default 5). Excess
277
+ * reactions on the same target are collapsed into a single trailer line
278
+ * (`[…and N more reactions to Mxxx]`, singular `reaction` when N===1), emitted
279
+ * at the point the overflow window closes — i.e. immediately before the next
280
+ * event that is not an overflowing reaction for the same target — so trailers
281
+ * stay in chronological position rather than clustered at the end.
282
+ */
283
+ export function renderSlackTranscript(
284
+ messages: RenderableSlackMessage[],
285
+ opts?: RenderOptions,
286
+ ): Message[] {
287
+ if (messages.length === 0) return [];
288
+
289
+ const maxReactions = Math.max(
290
+ 1,
291
+ Math.floor(opts?.maxReactionsPerMessage ?? DEFAULT_MAX_REACTIONS),
292
+ );
293
+
294
+ // Stable sort: decorate-sort-undecorate so equal keys preserve input order.
295
+ const indexed = messages.map((m, i) => ({ m, i, k: sortKey(m) }));
296
+ indexed.sort((a, b) => {
297
+ if (a.k !== b.k) return a.k - b.k;
298
+ return a.i - b.i;
299
+ });
300
+ const sorted = indexed.map((x) => x.m);
301
+
302
+ // Per-target reaction counters used to enforce the cap.
303
+ const reactionCount = new Map<string, number>();
304
+ // Open per-target overflow windows. Excess reactions accumulate here; the
305
+ // window is closed (trailer emitted) as soon as the chronological walk
306
+ // reaches an event that is not an overflowing reaction for that target.
307
+ const overflowAccumulator = new Map<
308
+ string,
309
+ { excess: number; role: "user" | "assistant" }
310
+ >();
311
+
312
+ const trailerMessage = (
313
+ target: string,
314
+ acc: { excess: number; role: "user" | "assistant" },
315
+ ): Message => ({
316
+ role: acc.role,
317
+ content: [
318
+ {
319
+ type: "text" as const,
320
+ text: `[…and ${acc.excess} more ${acc.excess === 1 ? "reaction" : "reactions"} to ${parentAlias(target)}]`,
321
+ },
322
+ ],
323
+ });
324
+
325
+ const flushOverflowExcept = (out: Message[], keepTarget: string | null) => {
326
+ for (const target of Array.from(overflowAccumulator.keys())) {
327
+ if (target === keepTarget) continue;
328
+ const acc = overflowAccumulator.get(target)!;
329
+ out.push(trailerMessage(target, acc));
330
+ overflowAccumulator.delete(target);
331
+ }
332
+ };
333
+
334
+ const out: Message[] = [];
335
+ for (const m of sorted) {
336
+ const meta = m.metadata;
337
+ if (meta?.eventKind === "reaction" && meta.reaction) {
338
+ const target = meta.reaction.targetChannelTs;
339
+ const seen = reactionCount.get(target) ?? 0;
340
+ if (seen < maxReactions) {
341
+ // Reaction fits under the cap for `target`. Any open overflow windows
342
+ // for other targets are now behind us chronologically — close them.
343
+ flushOverflowExcept(out, null);
344
+ reactionCount.set(target, seen + 1);
345
+ const line = renderReaction(m);
346
+ if (line !== null) {
347
+ out.push({
348
+ role: m.role,
349
+ content: [{ type: "text" as const, text: line }],
350
+ });
351
+ }
352
+ } else {
353
+ // Reaction overflows for `target`. Close any other open windows, then
354
+ // extend this target's open window.
355
+ flushOverflowExcept(out, target);
356
+ const acc = overflowAccumulator.get(target) ?? {
357
+ excess: 0,
358
+ role: m.role,
359
+ };
360
+ acc.excess += 1;
361
+ overflowAccumulator.set(target, acc);
362
+ }
363
+ continue;
364
+ }
365
+ // Non-reaction event: every open overflow window closes here.
366
+ flushOverflowExcept(out, null);
367
+ const tagLine = renderMessage(m);
368
+ const blocks = buildMessageContentBlocks(m, tagLine);
369
+ if (blocks.length === 0) continue;
370
+ out.push({
371
+ role: m.role,
372
+ content: blocks,
373
+ });
374
+ }
375
+
376
+ // End of the walk: flush any still-open overflow windows.
377
+ flushOverflowExcept(out, null);
378
+
379
+ return filterOrphanToolPairs(out);
380
+ }
381
+
382
+ /**
383
+ * Final safety pass that drops unpaired `tool_use` ↔ `tool_result` blocks.
384
+ *
385
+ * Anthropic's API requires every `tool_use` in an assistant turn to be
386
+ * matched by a `tool_result` in the following user turn (and vice versa).
387
+ * In normal operation `renderSlackTranscript` emits fully-paired turns
388
+ * because the persisted transcript reflects completed tool exchanges, but
389
+ * edge cases (mid-turn compaction, partial failures, a race between
390
+ * producer persistence and consumer persistence) can leave an orphan in
391
+ * the rendered output. Sending an orphan to the provider hard-fails the
392
+ * entire request, so we defensively prune any unpaired block here.
393
+ *
394
+ * Server-side block types (`server_tool_use`, `web_search_tool_result`) are
395
+ * stripped earlier by `buildMessageContentBlocks` — they carry stale
396
+ * provider-specific `encrypted_content` and are never replayed — so they
397
+ * cannot reach this filter.
398
+ *
399
+ * A message that becomes empty after filtering (e.g. an assistant row that
400
+ * carried only an orphaned `tool_use`) is dropped entirely rather than
401
+ * emitted as `{role, content: []}` — empty-content messages are also
402
+ * rejected by the provider.
403
+ */
404
+ function filterOrphanToolPairs(messages: Message[]): Message[] {
405
+ const produced = new Set<string>();
406
+ const consumed = new Set<string>();
407
+ for (const msg of messages) {
408
+ for (const b of msg.content) {
409
+ if (b.type === "tool_use") produced.add(b.id);
410
+ else if (b.type === "tool_result" || b.type === "web_search_tool_result")
411
+ consumed.add(b.tool_use_id);
412
+ }
413
+ }
414
+ const out: Message[] = [];
415
+ for (const msg of messages) {
416
+ const kept: ContentBlock[] = [];
417
+ for (const b of msg.content) {
418
+ if (b.type === "tool_use" && !consumed.has(b.id)) continue;
419
+ if (
420
+ (b.type === "tool_result" || b.type === "web_search_tool_result") &&
421
+ !produced.has(b.tool_use_id)
422
+ )
423
+ continue;
424
+ kept.push(b);
425
+ }
426
+ if (kept.length > 0) out.push({ role: msg.role, content: kept });
427
+ }
428
+ return out;
429
+ }
430
+
431
+ /**
432
+ * Extract the first text-block text from each rendered message.
433
+ *
434
+ * Used by callers (e.g. the active-thread focus block) that need a flat
435
+ * `string[]` of rendered tag lines rather than the structured `Message[]`
436
+ * output. Messages with no text block yield an empty string.
437
+ */
438
+ export function extractTagLineTexts(rendered: Message[]): string[] {
439
+ return rendered.map((msg) => {
440
+ const first = msg.content.find((b) => b.type === "text");
441
+ return first && first.type === "text" ? first.text : "";
442
+ });
443
+ }
@@ -127,7 +127,7 @@ export async function extractStylePatterns(
127
127
  .map((e, i) => `--- Message ${i + 1} ---\n${e}`)
128
128
  .join("\n\n");
129
129
 
130
- const provider = await getConfiguredProvider();
130
+ const provider = await getConfiguredProvider("styleAnalyzer");
131
131
  if (!provider) {
132
132
  return { stylePatterns: [], contactObservations: [] };
133
133
  }
@@ -147,7 +147,10 @@ export async function extractStylePatterns(
147
147
  promptMessages,
148
148
  [storeStyleAnalysisTool],
149
149
  STYLE_EXTRACTION_SYSTEM_PROMPT,
150
- { signal: AbortSignal.timeout(30_000) },
150
+ {
151
+ signal: AbortSignal.timeout(30_000),
152
+ config: { callSite: "styleAnalyzer" },
153
+ },
151
154
  );
152
155
 
153
156
  const toolBlock = response.content.find((b) => b.type === "tool_use");
@@ -31,7 +31,7 @@ The candidate set is serialized into a compact `<conversation-candidates>` block
31
31
 
32
32
  ### 3. Decision
33
33
 
34
- The decision engine (`decision-engine.ts`) sends the signal to an LLM (configured via `notifications.decisionModelIntent`) along with available channels, the user's preference summary, and the conversation candidate set. The LLM responds with a structured decision: whether to notify, which channels, rendered copy per channel, a deduplication key, and **per-channel conversation actions**.
34
+ The decision engine (`decision-engine.ts`) sends the signal to an LLM (configured via `llm.callSites.notificationDecision`) along with available channels, the user's preference summary, and the conversation candidate set. The LLM responds with a structured decision: whether to notify, which channels, rendered copy per channel, a deduplication key, and **per-channel conversation actions**.
35
35
 
36
36
  **Conversation actions:** For each selected channel, the LLM decides:
37
37
 
@@ -538,10 +538,14 @@ Preferences are sanitized against prompt injection (angle brackets replaced with
538
538
 
539
539
  ## Configuration
540
540
 
541
- All settings live under the `notifications` key in `config.json`:
541
+ The decision engine and preference extractor pick their per-call LLM config
542
+ from the unified `llm` block. Override defaults by setting either of:
542
543
 
543
- | Key | Type | Default | Description |
544
- | ----------------------------------- | ------ | --------------------- | ------------------------------------------------------------------------ |
545
- | `notifications.decisionModelIntent` | string | `"latency-optimized"` | Model intent used for both the decision engine and preference extraction |
544
+ | Key | Type | Default | Description |
545
+ | ------------------------------------ | ------- | -------------- | --------------------------------------------------------------------------- |
546
+ | `llm.callSites.notificationDecision` | object | _(unset)_ | Provider/model/effort/etc. override for the decision engine call site |
547
+ | `llm.callSites.preferenceExtraction` | object | _(unset)_ | Provider/model/effort/etc. override for the preference extractor call site |
548
+
549
+ When a call site override is unset, the resolver falls back to `llm.default`.
546
550
 
547
551
  The notification pipeline is always active -- signals are processed and dispatched as soon as the daemon is running. The audit trail (events, decisions, deliveries) is written for every signal.
@@ -12,7 +12,6 @@
12
12
  import { v4 as uuid } from "uuid";
13
13
 
14
14
  import { getDeliverableChannels } from "../channels/config.js";
15
- import { getConfig } from "../config/loader.js";
16
15
  import { listGuardianChannels } from "../contacts/contact-store.js";
17
16
  import { resolveGuardianPersona } from "../prompts/persona-resolver.js";
18
17
  import { buildCoreIdentityContext } from "../prompts/system-prompt.js";
@@ -22,7 +21,7 @@ import {
22
21
  getConfiguredProvider,
23
22
  userMessage,
24
23
  } from "../providers/provider-send-message.js";
25
- import type { ModelIntent, Provider } from "../providers/types.js";
24
+ import type { Provider } from "../providers/types.js";
26
25
  import { getLogger } from "../util/logger.js";
27
26
  import { truncate } from "../util/truncate.js";
28
27
  import {
@@ -717,9 +716,6 @@ export async function evaluateSignal(
717
716
  availableChannels: NotificationChannel[],
718
717
  preferenceContext?: string,
719
718
  ): Promise<NotificationDecision> {
720
- const config = getConfig();
721
- const decisionModelIntent = config.notifications.decisionModelIntent;
722
-
723
719
  // When no explicit preference context is provided, load the user's
724
720
  // stored notification preferences from the memory-backed store.
725
721
  // Wrapped in try/catch so a DB failure doesn't break the decision path.
@@ -750,7 +746,7 @@ export async function evaluateSignal(
750
746
  );
751
747
  }
752
748
 
753
- const provider = await getConfiguredProvider();
749
+ const provider = await getConfiguredProvider("notificationDecision");
754
750
  if (!provider) {
755
751
  log.warn(
756
752
  "Configured provider unavailable for notification decision, using fallback",
@@ -774,7 +770,6 @@ export async function evaluateSignal(
774
770
  signal,
775
771
  availableChannels,
776
772
  resolvedPreferenceContext,
777
- decisionModelIntent,
778
773
  candidateSet,
779
774
  );
780
775
  } catch (err) {
@@ -805,7 +800,6 @@ async function classifyWithLLM(
805
800
  signal: NotificationSignal,
806
801
  availableChannels: NotificationChannel[],
807
802
  preferenceContext: string | undefined,
808
- modelIntent: ModelIntent,
809
803
  candidateSet?: ConversationCandidateSet,
810
804
  ): Promise<NotificationDecision> {
811
805
  const { signal: abortSignal, cleanup } = createTimeout(DECISION_TIMEOUT_MS);
@@ -858,7 +852,7 @@ async function classifyWithLLM(
858
852
  systemPrompt,
859
853
  {
860
854
  config: {
861
- modelIntent,
855
+ callSite: "notificationDecision",
862
856
  max_tokens: 2048,
863
857
  tool_choice: {
864
858
  type: "tool" as const,
@@ -11,7 +11,6 @@
11
11
  * - "Weeknights after 10pm: only critical notifications"
12
12
  */
13
13
 
14
- import { getConfig } from "../config/loader.js";
15
14
  import {
16
15
  createTimeout,
17
16
  extractToolUse,
@@ -140,15 +139,12 @@ const EXTRACTION_TOOL = {
140
139
  export async function extractPreferences(
141
140
  message: string,
142
141
  ): Promise<ExtractionResult> {
143
- const provider = await getConfiguredProvider();
142
+ const provider = await getConfiguredProvider("preferenceExtraction");
144
143
  if (!provider) {
145
144
  log.debug("No provider available for preference extraction");
146
145
  return { detected: false, preferences: [] };
147
146
  }
148
147
 
149
- const config = getConfig();
150
- const modelIntent = config.notifications.decisionModelIntent;
151
-
152
148
  const { signal, cleanup } = createTimeout(EXTRACTION_TIMEOUT_MS);
153
149
 
154
150
  try {
@@ -158,7 +154,7 @@ export async function extractPreferences(
158
154
  SYSTEM_PROMPT,
159
155
  {
160
156
  config: {
161
- modelIntent,
157
+ callSite: "preferenceExtraction",
162
158
  max_tokens: 1024,
163
159
  tool_choice: {
164
160
  type: "tool" as const,
@@ -223,6 +223,7 @@ export function seedProviders(
223
223
  tokenExchangeBodyFormat,
224
224
  userinfoUrl,
225
225
  baseUrl: sql`COALESCE(${oauthProviders.baseUrl}, ${baseUrl})`,
226
+ defaultScopes,
226
227
  scopeSeparator,
227
228
  authorizeParams,
228
229
  pingUrl,
@@ -12,6 +12,7 @@ import type { VellumPlatformClient } from "../platform/client.js";
12
12
  import { BackendError, VellumError } from "../util/errors.js";
13
13
  import {
14
14
  CredentialRequiredError,
15
+ InsufficientBalanceError,
15
16
  PlatformOAuthConnection,
16
17
  ProviderUnreachableError,
17
18
  } from "./platform-connection.js";
@@ -199,6 +200,52 @@ describe("PlatformOAuthConnection", () => {
199
200
  const provErr = new ProviderUnreachableError();
200
201
  expect(provErr).toBeInstanceOf(BackendError);
201
202
  expect(provErr).toBeInstanceOf(VellumError);
203
+
204
+ const balErr = new InsufficientBalanceError();
205
+ expect(balErr).toBeInstanceOf(BackendError);
206
+ expect(balErr).toBeInstanceOf(VellumError);
207
+ });
208
+
209
+ test("402 response throws InsufficientBalanceError", async () => {
210
+ const client = makeMockClient(
211
+ mock(
212
+ async () => new Response("", { status: 402 }),
213
+ ) as unknown as typeof globalThis.fetch,
214
+ );
215
+
216
+ const conn = new PlatformOAuthConnection({ ...DEFAULT_OPTIONS, client });
217
+ await expect(
218
+ conn.request({ method: "GET", path: "/test" }),
219
+ ).rejects.toThrow(InsufficientBalanceError);
220
+ });
221
+
222
+ test("402 response includes actionable billing message", async () => {
223
+ const client = makeMockClient(
224
+ mock(
225
+ async () => new Response("", { status: 402 }),
226
+ ) as unknown as typeof globalThis.fetch,
227
+ );
228
+
229
+ const conn = new PlatformOAuthConnection({ ...DEFAULT_OPTIONS, client });
230
+ await expect(
231
+ conn.request({ method: "GET", path: "/test" }),
232
+ ).rejects.toThrow(/add funds/i);
233
+ });
234
+
235
+ test("does not retry on 402", async () => {
236
+ let callCount = 0;
237
+ const client = makeMockClient(
238
+ mock(async () => {
239
+ callCount++;
240
+ return new Response("", { status: 402 });
241
+ }) as unknown as typeof globalThis.fetch,
242
+ );
243
+
244
+ const conn = new PlatformOAuthConnection({ ...DEFAULT_OPTIONS, client });
245
+ await expect(
246
+ conn.request({ method: "GET", path: "/test" }),
247
+ ).rejects.toThrow(InsufficientBalanceError);
248
+ expect(callCount).toBe(1);
202
249
  });
203
250
 
204
251
  test("424 response throws CredentialRequiredError", async () => {