@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
@@ -157,12 +157,12 @@ describe("ConfigWatcher workspace file handlers", () => {
157
157
  expect(evictCallCount).toBe(1);
158
158
  });
159
159
 
160
- test("UPDATES.md change triggers onConversationEvict", async () => {
160
+ test("UPDATES.md change does NOT trigger onConversationEvict", async () => {
161
161
  watcher.start(onConversationEvict);
162
162
  simulateFileChange(WORKSPACE_DIR, "UPDATES.md");
163
163
 
164
164
  await new Promise((r) => setTimeout(r, 300));
165
- expect(evictCallCount).toBe(1);
165
+ expect(evictCallCount).toBe(0);
166
166
  });
167
167
 
168
168
  test("config.json change calls refreshConfigFromSources", async () => {
@@ -67,7 +67,7 @@ describe("upsertContact user_file selection", () => {
67
67
 
68
68
  test("reuses an existing sibling's userFile when principalId matches", () => {
69
69
  const primary = upsertContact({
70
- displayName: "Sidd",
70
+ displayName: "Chris",
71
71
  role: "guardian",
72
72
  principalId: "principal-abc",
73
73
  channels: [
@@ -78,12 +78,12 @@ describe("upsertContact user_file selection", () => {
78
78
  },
79
79
  ],
80
80
  });
81
- expect(primary.userFile).toBe("sidd.md");
81
+ expect(primary.userFile).toBe("chris.md");
82
82
 
83
83
  // Second contact for the same principal on Slack — must inherit the
84
- // first contact's userFile, NOT auto-increment to sidd-2.md.
84
+ // first contact's userFile, NOT auto-increment to chris-2.md.
85
85
  const slack = upsertContact({
86
- displayName: "sidd",
86
+ displayName: "chris",
87
87
  role: "guardian",
88
88
  principalId: "principal-abc",
89
89
  channels: [
@@ -95,7 +95,7 @@ describe("upsertContact user_file selection", () => {
95
95
  },
96
96
  ],
97
97
  });
98
- expect(slack.userFile).toBe("sidd.md");
98
+ expect(slack.userFile).toBe("chris.md");
99
99
  expect(slack.id).not.toBe(primary.id);
100
100
  });
101
101
 
@@ -118,31 +118,31 @@ describe("upsertContact user_file selection", () => {
118
118
 
119
119
  test("still auto-increments when principalId is not set and displayName collides", () => {
120
120
  const first = upsertContact({
121
- displayName: "Akash",
121
+ displayName: "Bob",
122
122
  role: "contact",
123
123
  channels: [
124
124
  {
125
125
  type: "slack",
126
- address: "uakash1",
127
- externalUserId: "UAKASH1",
128
- externalChatId: "DAKASH1",
126
+ address: "ubob1",
127
+ externalUserId: "UBOB1",
128
+ externalChatId: "DBOB1",
129
129
  },
130
130
  ],
131
131
  });
132
132
  const second = upsertContact({
133
- displayName: "Akash",
133
+ displayName: "Bob",
134
134
  role: "contact",
135
135
  channels: [
136
136
  {
137
137
  type: "slack",
138
- address: "uakash2",
139
- externalUserId: "UAKASH2",
140
- externalChatId: "DAKASH2",
138
+ address: "ubob2",
139
+ externalUserId: "UBOB2",
140
+ externalChatId: "DBOB2",
141
141
  },
142
142
  ],
143
143
  });
144
- expect(first.userFile).toBe("akash.md");
145
- expect(second.userFile).toBe("akash-2.md");
144
+ expect(first.userFile).toBe("bob.md");
145
+ expect(second.userFile).toBe("bob-2.md");
146
146
  });
147
147
 
148
148
  test("ignores a sibling whose userFile is null and generates a new slug", () => {
@@ -203,18 +203,18 @@ describe("migrateNormalizeUserFileByPrincipal", () => {
203
203
  const now = Date.now();
204
204
  insertContact({
205
205
  id: "c1",
206
- displayName: "sidd",
206
+ displayName: "chris",
207
207
  role: "guardian",
208
208
  principalId: "principal-x",
209
- userFile: "sidd.md",
209
+ userFile: "chris.md",
210
210
  createdAt: now - 1000,
211
211
  });
212
212
  insertContact({
213
213
  id: "c2",
214
- displayName: "sidd",
214
+ displayName: "chris",
215
215
  role: "guardian",
216
216
  principalId: "principal-x",
217
- userFile: "sidd-2.md",
217
+ userFile: "chris-2.md",
218
218
  createdAt: now,
219
219
  });
220
220
 
@@ -222,23 +222,23 @@ describe("migrateNormalizeUserFileByPrincipal", () => {
222
222
 
223
223
  const rows = fetchUserFilesByPrincipal("principal-x");
224
224
  expect(rows).toHaveLength(2);
225
- expect(rows[0]?.user_file).toBe("sidd.md");
226
- expect(rows[1]?.user_file).toBe("sidd.md");
225
+ expect(rows[0]?.user_file).toBe("chris.md");
226
+ expect(rows[1]?.user_file).toBe("chris.md");
227
227
  });
228
228
 
229
229
  test("propagates a sibling's user_file to NULL rows", () => {
230
230
  const now = Date.now();
231
231
  insertContact({
232
232
  id: "c1",
233
- displayName: "sidd",
233
+ displayName: "chris",
234
234
  role: "guardian",
235
235
  principalId: "principal-y",
236
- userFile: "sidd.md",
236
+ userFile: "chris.md",
237
237
  createdAt: now - 1000,
238
238
  });
239
239
  insertContact({
240
240
  id: "c2",
241
- displayName: "sidd",
241
+ displayName: "chris",
242
242
  role: "guardian",
243
243
  principalId: "principal-y",
244
244
  userFile: null,
@@ -248,8 +248,8 @@ describe("migrateNormalizeUserFileByPrincipal", () => {
248
248
  migrateNormalizeUserFileByPrincipal(getDb());
249
249
 
250
250
  const rows = fetchUserFilesByPrincipal("principal-y");
251
- expect(rows[0]?.user_file).toBe("sidd.md");
252
- expect(rows[1]?.user_file).toBe("sidd.md");
251
+ expect(rows[0]?.user_file).toBe("chris.md");
252
+ expect(rows[1]?.user_file).toBe("chris.md");
253
253
  });
254
254
 
255
255
  test("prefers non-auto-incremented candidate over auto-incremented older row", () => {
@@ -258,26 +258,26 @@ describe("migrateNormalizeUserFileByPrincipal", () => {
258
258
  const now = Date.now();
259
259
  insertContact({
260
260
  id: "c1",
261
- displayName: "sidd",
261
+ displayName: "chris",
262
262
  role: "guardian",
263
263
  principalId: "principal-z",
264
- userFile: "sidd-3.md",
264
+ userFile: "chris-3.md",
265
265
  createdAt: now - 2000,
266
266
  });
267
267
  insertContact({
268
268
  id: "c2",
269
- displayName: "sidd",
269
+ displayName: "chris",
270
270
  role: "guardian",
271
271
  principalId: "principal-z",
272
- userFile: "sidd.md",
272
+ userFile: "chris.md",
273
273
  createdAt: now,
274
274
  });
275
275
 
276
276
  migrateNormalizeUserFileByPrincipal(getDb());
277
277
 
278
278
  const rows = fetchUserFilesByPrincipal("principal-z");
279
- expect(rows[0]?.user_file).toBe("sidd.md");
280
- expect(rows[1]?.user_file).toBe("sidd.md");
279
+ expect(rows[0]?.user_file).toBe("chris.md");
280
+ expect(rows[1]?.user_file).toBe("chris.md");
281
281
  });
282
282
 
283
283
  test("leaves untouched when only one contact exists for a principal", () => {
@@ -298,25 +298,25 @@ describe("migrateNormalizeUserFileByPrincipal", () => {
298
298
  });
299
299
 
300
300
  test("does not classify dated-slug filenames as auto-incremented", () => {
301
- // A display name containing a 4-digit year (e.g. "Alex 2024") produces
302
- // `alex-2024.md`. The auto-increment suffix only ever appends 1–3 digits
303
- // (starting at 2), so `alex-2024.md` must be treated as a normal filename
301
+ // A display name containing a 4-digit year (e.g. "Dana 2024") produces
302
+ // `dana-2024.md`. The auto-increment suffix only ever appends 1–3 digits
303
+ // (starting at 2), so `dana-2024.md` must be treated as a normal filename
304
304
  // and not deprioritized in favor of an older sibling.
305
305
  const now = Date.now();
306
306
  insertContact({
307
307
  id: "c1",
308
- displayName: "Alex 2024",
308
+ displayName: "Dana 2024",
309
309
  role: "guardian",
310
310
  principalId: "principal-dated",
311
- userFile: "alex-2024.md",
311
+ userFile: "dana-2024.md",
312
312
  createdAt: now,
313
313
  });
314
314
  insertContact({
315
315
  id: "c2",
316
- displayName: "Alex",
316
+ displayName: "Dana",
317
317
  role: "guardian",
318
318
  principalId: "principal-dated",
319
- userFile: "alex.md",
319
+ userFile: "dana.md",
320
320
  createdAt: now - 1000,
321
321
  });
322
322
 
@@ -324,9 +324,9 @@ describe("migrateNormalizeUserFileByPrincipal", () => {
324
324
 
325
325
  const rows = fetchUserFilesByPrincipal("principal-dated");
326
326
  // Neither candidate looks auto-incremented, so tiebreaker is oldest
327
- // created_at — c2 (`alex.md`) wins. Crucially, `alex-2024.md` was NOT
327
+ // created_at — c2 (`dana.md`) wins. Crucially, `dana-2024.md` was NOT
328
328
  // classified as auto-incremented and penalized.
329
- expect(rows.map((r) => r.user_file).sort()).toEqual(["alex.md", "alex.md"]);
329
+ expect(rows.map((r) => r.user_file).sort()).toEqual(["dana.md", "dana.md"]);
330
330
  });
331
331
 
332
332
  test("excludes year-like 4-digit tails from the auto-increment class", () => {
@@ -389,117 +389,116 @@ describe("migrateNormalizeUserFileByPrincipal", () => {
389
389
  });
390
390
 
391
391
  test("excludes full date-shaped tails from the auto-increment class", () => {
392
- // `alex-2025-04-13.md` ends with `-13.md` (which otherwise looks like a
392
+ // `dana-2025-04-13.md` ends with `-13.md` (which otherwise looks like a
393
393
  // small counter), but the preceding `-2025-04` marks the whole tail as a
394
394
  // date. Must NOT be classified as auto-incremented.
395
395
  const now = Date.now();
396
396
  insertContact({
397
397
  id: "c1",
398
- displayName: "Alex 2025 04 13",
398
+ displayName: "Dana 2025 04 13",
399
399
  role: "guardian",
400
400
  principalId: "principal-datefull",
401
- userFile: "alex-2025-04-13.md",
401
+ userFile: "dana-2025-04-13.md",
402
402
  createdAt: now - 2000,
403
403
  });
404
404
  insertContact({
405
405
  id: "c2",
406
- displayName: "Alex",
406
+ displayName: "Dana",
407
407
  role: "guardian",
408
408
  principalId: "principal-datefull",
409
- userFile: "alex-2.md",
409
+ userFile: "dana-2.md",
410
410
  createdAt: now - 1000,
411
411
  });
412
412
 
413
413
  migrateNormalizeUserFileByPrincipal(getDb());
414
414
 
415
415
  const rows = fetchUserFilesByPrincipal("principal-datefull");
416
- // `alex-2.md` is auto-incremented; `alex-2025-04-13.md` is a date-shaped
416
+ // `dana-2.md` is auto-incremented; `dana-2025-04-13.md` is a date-shaped
417
417
  // slug and wins as canonical.
418
- for (const row of rows)
419
- expect(row.user_file).toBe("alex-2025-04-13.md");
418
+ for (const row of rows) expect(row.user_file).toBe("dana-2025-04-13.md");
420
419
  });
421
420
 
422
421
  test("treats year-prefixed single-digit counter slug as auto-incremented", () => {
423
- // `alex-2025-2.md` is `generateUserFileSlug` output when the base
424
- // `alex-2025.md` was already taken — it is a collision counter, NOT a
422
+ // `dana-2025-2.md` is `generateUserFileSlug` output when the base
423
+ // `dana-2025.md` was already taken — it is a collision counter, NOT a
425
424
  // date. Counters are emitted without leading zeros (`-2`, `-3`, ...)
426
425
  // while ISO date components are always 2 digits (`-02`, `-04`), so
427
426
  // single-digit trailing segments mark the tail as a counter.
428
427
  const now = Date.now();
429
428
  insertContact({
430
429
  id: "c1",
431
- displayName: "Alex 2025",
430
+ displayName: "Dana 2025",
432
431
  role: "guardian",
433
432
  principalId: "principal-yc",
434
- userFile: "alex-2025-2.md",
433
+ userFile: "dana-2025-2.md",
435
434
  createdAt: now - 2000,
436
435
  });
437
436
  insertContact({
438
437
  id: "c2",
439
- displayName: "Alex 2025",
438
+ displayName: "Dana 2025",
440
439
  role: "guardian",
441
440
  principalId: "principal-yc",
442
- userFile: "alex-2025.md",
441
+ userFile: "dana-2025.md",
443
442
  createdAt: now,
444
443
  });
445
444
 
446
445
  migrateNormalizeUserFileByPrincipal(getDb());
447
446
 
448
447
  const rows = fetchUserFilesByPrincipal("principal-yc");
449
- // Despite being older, `alex-2025-2.md` is auto-incremented, so
450
- // `alex-2025.md` (the clean base) wins as canonical.
451
- for (const row of rows) expect(row.user_file).toBe("alex-2025.md");
448
+ // Despite being older, `dana-2025-2.md` is auto-incremented, so
449
+ // `dana-2025.md` (the clean base) wins as canonical.
450
+ for (const row of rows) expect(row.user_file).toBe("dana-2025.md");
452
451
  });
453
452
 
454
453
  test("treats year-prefixed single-digit tail as base slug when display name generates it", () => {
455
- // Ambiguous filename: `alex-2025-4.md` could be either a collision counter
456
- // on base `alex-2025.md` OR the direct base slug from display name
457
- // "Alex 2025 4". The classifier must cross-reference the row's display
454
+ // Ambiguous filename: `dana-2025-4.md` could be either a collision counter
455
+ // on base `dana-2025.md` OR the direct base slug from display name
456
+ // "Dana 2025 4". The classifier must cross-reference the row's display
458
457
  // name — if `generateUserFileSlug`'s pure slug transform maps the name to
459
458
  // the filename, treat it as a base slug, not an auto-incremented counter.
460
459
  const now = Date.now();
461
460
  insertContact({
462
461
  id: "c1",
463
- displayName: "Alex 2025 4",
462
+ displayName: "Dana 2025 4",
464
463
  role: "guardian",
465
464
  principalId: "principal-yb",
466
- userFile: "alex-2025-4.md",
465
+ userFile: "dana-2025-4.md",
467
466
  createdAt: now,
468
467
  });
469
468
  insertContact({
470
469
  id: "c2",
471
- displayName: "Alex",
470
+ displayName: "Dana",
472
471
  role: "guardian",
473
472
  principalId: "principal-yb",
474
- userFile: "alex-2.md",
473
+ userFile: "dana-2.md",
475
474
  createdAt: now - 1000,
476
475
  });
477
476
 
478
477
  migrateNormalizeUserFileByPrincipal(getDb());
479
478
 
480
479
  const rows = fetchUserFilesByPrincipal("principal-yb");
481
- // `alex-2025-4.md` is a legitimate base slug (disambiguated by display
482
- // name), while `alex-2.md` is auto-incremented. The base slug must win
480
+ // `dana-2025-4.md` is a legitimate base slug (disambiguated by display
481
+ // name), while `dana-2.md` is auto-incremented. The base slug must win
483
482
  // as canonical, otherwise canonical would point at a collision counter.
484
- for (const row of rows) expect(row.user_file).toBe("alex-2025-4.md");
483
+ for (const row of rows) expect(row.user_file).toBe("dana-2025-4.md");
485
484
  });
486
485
 
487
486
  test("is idempotent", () => {
488
487
  const now = Date.now();
489
488
  insertContact({
490
489
  id: "c1",
491
- displayName: "sidd",
490
+ displayName: "chris",
492
491
  role: "guardian",
493
492
  principalId: "principal-i",
494
- userFile: "sidd.md",
493
+ userFile: "chris.md",
495
494
  createdAt: now - 1000,
496
495
  });
497
496
  insertContact({
498
497
  id: "c2",
499
- displayName: "sidd",
498
+ displayName: "chris",
500
499
  role: "guardian",
501
500
  principalId: "principal-i",
502
- userFile: "sidd-2.md",
501
+ userFile: "chris-2.md",
503
502
  createdAt: now,
504
503
  });
505
504
 
@@ -507,6 +506,6 @@ describe("migrateNormalizeUserFileByPrincipal", () => {
507
506
  migrateNormalizeUserFileByPrincipal(getDb());
508
507
 
509
508
  const rows = fetchUserFilesByPrincipal("principal-i");
510
- for (const row of rows) expect(row.user_file).toBe("sidd.md");
509
+ for (const row of rows) expect(row.user_file).toBe("chris.md");
511
510
  });
512
511
  });
@@ -70,13 +70,13 @@ describe("createGuardianBinding seeds users/<slug>.md", () => {
70
70
  test("writes the persona template scaffold on first creation", () => {
71
71
  createGuardianBinding({
72
72
  channel: "telegram",
73
- guardianExternalUserId: "Sidd",
74
- guardianDeliveryChatId: "chat-sidd",
75
- guardianPrincipalId: "principal-sidd",
73
+ guardianExternalUserId: "Chris",
74
+ guardianDeliveryChatId: "chat-chris",
75
+ guardianPrincipalId: "principal-chris",
76
76
  verifiedVia: "challenge",
77
77
  });
78
78
 
79
- const expectedPath = userFilePath("sidd.md");
79
+ const expectedPath = userFilePath("chris.md");
80
80
  expect(existsSync(expectedPath)).toBe(true);
81
81
 
82
82
  const content = readFileSync(expectedPath, "utf-8");
@@ -472,7 +472,9 @@ describe("token estimator", () => {
472
472
  },
473
473
  ];
474
474
 
475
- const total = estimateMessagesTokens(messages, { providerName: "anthropic" });
475
+ const total = estimateMessagesTokens(messages, {
476
+ providerName: "anthropic",
477
+ });
476
478
 
477
479
  // Each image: ~73 tokens. 100 images + message overhead ≈ 7,304
478
480
  // Old behavior: 100 * ~1,043 = ~104,300 (14x overestimate)
@@ -522,3 +524,191 @@ describe("token estimator", () => {
522
524
  expect(tallTokens).toBeLessThan(1_800);
523
525
  });
524
526
  });
527
+
528
+ describe("tool_result estimation mirrors Anthropic wire format", () => {
529
+ test("plain text tool_result counts overhead + id + content", () => {
530
+ const tokens = estimateContentBlockTokens({
531
+ type: "tool_result",
532
+ tool_use_id: "call_1",
533
+ content: "operation complete",
534
+ });
535
+ // Sanity bounds — small string, small overhead.
536
+ expect(tokens).toBeGreaterThan(estimateTextTokens("operation complete"));
537
+ expect(tokens).toBeLessThan(estimateTextTokens("operation complete") + 50);
538
+ });
539
+
540
+ test("image sub-block is counted when is_error is false", () => {
541
+ const pngBase64 = makePngBase64(512, 512);
542
+ const withImage = estimateContentBlockTokens(
543
+ {
544
+ type: "tool_result",
545
+ tool_use_id: "call_img",
546
+ content: "screenshot captured",
547
+ contentBlocks: [
548
+ {
549
+ type: "image",
550
+ source: {
551
+ type: "base64",
552
+ media_type: "image/png",
553
+ data: pngBase64,
554
+ },
555
+ },
556
+ ],
557
+ },
558
+ { providerName: "anthropic" },
559
+ );
560
+ const withoutImage = estimateContentBlockTokens(
561
+ {
562
+ type: "tool_result",
563
+ tool_use_id: "call_img",
564
+ content: "screenshot captured",
565
+ },
566
+ { providerName: "anthropic" },
567
+ );
568
+ // 512x512 = 262144 pixels, tokens ≈ ceil(262144/750) ≈ 350.
569
+ expect(withImage - withoutImage).toBeGreaterThan(300);
570
+ expect(withImage - withoutImage).toBeLessThan(500);
571
+ });
572
+
573
+ test("image sub-block is NOT counted when is_error is true", () => {
574
+ // The Anthropic serializer filters image parts out of error tool results
575
+ // (client.ts:1398), so the estimator must match.
576
+ const pngBase64 = makePngBase64(512, 512);
577
+ const errorWithImage = estimateContentBlockTokens(
578
+ {
579
+ type: "tool_result",
580
+ tool_use_id: "call_err",
581
+ content: "operation failed",
582
+ is_error: true,
583
+ contentBlocks: [
584
+ {
585
+ type: "image",
586
+ source: {
587
+ type: "base64",
588
+ media_type: "image/png",
589
+ data: pngBase64,
590
+ },
591
+ },
592
+ ],
593
+ },
594
+ { providerName: "anthropic" },
595
+ );
596
+ const errorNoImage = estimateContentBlockTokens(
597
+ {
598
+ type: "tool_result",
599
+ tool_use_id: "call_err",
600
+ content: "operation failed",
601
+ is_error: true,
602
+ },
603
+ { providerName: "anthropic" },
604
+ );
605
+ expect(errorWithImage).toBe(errorNoImage);
606
+ });
607
+
608
+ test("image sub-block IS counted on error for non-Anthropic providers", () => {
609
+ // OpenAI and Gemini serializers forward error-result images (as a
610
+ // follow-up user message / parts entry), so the estimator must count
611
+ // them regardless of is_error under those providers.
612
+ const pngBase64 = makePngBase64(512, 512);
613
+ const build = (providerName: string) =>
614
+ estimateContentBlockTokens(
615
+ {
616
+ type: "tool_result",
617
+ tool_use_id: "call_err_img",
618
+ content: "operation failed",
619
+ is_error: true,
620
+ contentBlocks: [
621
+ {
622
+ type: "image",
623
+ source: {
624
+ type: "base64",
625
+ media_type: "image/png",
626
+ data: pngBase64,
627
+ },
628
+ },
629
+ ],
630
+ },
631
+ { providerName },
632
+ );
633
+ const buildPlain = (providerName: string) =>
634
+ estimateContentBlockTokens(
635
+ {
636
+ type: "tool_result",
637
+ tool_use_id: "call_err_img",
638
+ content: "operation failed",
639
+ is_error: true,
640
+ },
641
+ { providerName },
642
+ );
643
+ for (const providerName of ["openai", "gemini"]) {
644
+ const withImage = build(providerName);
645
+ const withoutImage = buildPlain(providerName);
646
+ expect(withImage - withoutImage).toBeGreaterThan(0);
647
+ }
648
+ });
649
+
650
+ test("unknown sub-block types are NOT counted", () => {
651
+ // The Anthropic serializer only forwards image/text sub-blocks. Anything
652
+ // else (thinking, tool_use, etc.) is dropped — the estimator must not
653
+ // add tokens for content that never reaches the wire.
654
+ const withThinking = estimateContentBlockTokens({
655
+ type: "tool_result",
656
+ tool_use_id: "call_think",
657
+ content: "done",
658
+ contentBlocks: [
659
+ {
660
+ type: "thinking",
661
+ thinking: "a".repeat(4000),
662
+ signature: "sig_stub",
663
+ },
664
+ ],
665
+ });
666
+ const plain = estimateContentBlockTokens({
667
+ type: "tool_result",
668
+ tool_use_id: "call_think",
669
+ content: "done",
670
+ });
671
+ expect(withThinking).toBe(plain);
672
+ });
673
+
674
+ test("text sub-block beyond block.content is counted once", () => {
675
+ // Handlers (e.g. secret-detection) may populate contentBlocks with an
676
+ // additional text entry distinct from block.content. The serializer
677
+ // forwards both, so the estimator counts block.content once and each
678
+ // text sub-block once — never doubling the content string against an
679
+ // echoing text sub-block.
680
+ const extraText = "x".repeat(4000);
681
+ const tokens = estimateContentBlockTokens({
682
+ type: "tool_result",
683
+ tool_use_id: "call_dual_text",
684
+ content: "short summary",
685
+ contentBlocks: [{ type: "text", text: extraText }],
686
+ });
687
+ // Estimate should be roughly:
688
+ // overhead + id + "short summary" + (text overhead + 1000 tokens for extraText)
689
+ // The extra text is ~1000 tokens on its own; overhead is small.
690
+ expect(tokens).toBeGreaterThan(1000);
691
+ expect(tokens).toBeLessThan(1100);
692
+ });
693
+
694
+ test("regression: tool_result with thinking sub-block does not inflate estimate by 3x+", () => {
695
+ // A modest tool_result whose contentBlocks carry a large sub-block the
696
+ // serializer discards must not inflate the estimate: the estimator
697
+ // skips the thinking payload, matching the plain wire shape.
698
+ const content = "y".repeat(2000);
699
+ const inflated = estimateContentBlockTokens({
700
+ type: "tool_result",
701
+ tool_use_id: "call_regress",
702
+ content,
703
+ contentBlocks: [
704
+ { type: "thinking", thinking: "z".repeat(8000), signature: "s" },
705
+ ],
706
+ });
707
+ const wireShape = estimateContentBlockTokens({
708
+ type: "tool_result",
709
+ tool_use_id: "call_regress",
710
+ content,
711
+ });
712
+ expect(inflated).toBe(wireShape);
713
+ });
714
+ });