@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,244 @@
1
+ /**
2
+ * Streaming validation primitives for `.vbundle` archives.
3
+ *
4
+ * The non-streaming `validateVBundle` decompresses the entire archive into
5
+ * memory and walks the tar buffer to compute per-file SHA-256s. That is fine
6
+ * for small bundles but peaks at 2x the decompressed size in RAM — an 8 GB
7
+ * bundle OOMs a 3 GB pod.
8
+ *
9
+ * This module lets a caller validate a bundle while streaming:
10
+ * - `readAndValidateManifest` consumes the first tar entry (which must be
11
+ * `manifest.json`), validates the schema, and verifies the self-referencing
12
+ * `manifest_sha256` against the canonicalized JSON.
13
+ * - `createHashVerifier` returns a passthrough `Transform` that hashes bytes
14
+ * flowing through it and errors the pipeline if the final digest or byte
15
+ * count does not match the expected values from the manifest.
16
+ *
17
+ * Together, these let a consumer pipe every subsequent tar entry through a
18
+ * hash verifier before writing it to disk, without ever buffering the full
19
+ * bundle.
20
+ */
21
+
22
+ import { createHash } from "node:crypto";
23
+ import { Transform, type TransformCallback } from "node:stream";
24
+
25
+ import type { StreamedTarEntry } from "./vbundle-tar-stream.js";
26
+ import {
27
+ computeManifestSha256,
28
+ ManifestSchema,
29
+ type ManifestType,
30
+ } from "./vbundle-validator.js";
31
+
32
+ // ---------------------------------------------------------------------------
33
+ // Public types
34
+ // ---------------------------------------------------------------------------
35
+
36
+ export interface ManifestReadResult {
37
+ manifest: ManifestType;
38
+ /** Fast lookup from archive path -> expected sha256 + size (from manifest.files). */
39
+ expected: Map<string, { sha256: string; size: number }>;
40
+ }
41
+
42
+ /**
43
+ * All failure modes produced by this module. Every throw/error includes a
44
+ * stable `code` string so callers can branch on the failure kind without
45
+ * string-matching the message.
46
+ */
47
+ export class StreamingValidationError extends Error {
48
+ public readonly code: string;
49
+ public readonly archivePath?: string;
50
+
51
+ constructor(code: string, message: string, archivePath?: string) {
52
+ super(message);
53
+ this.name = "StreamingValidationError";
54
+ this.code = code;
55
+ if (archivePath !== undefined) {
56
+ this.archivePath = archivePath;
57
+ }
58
+ }
59
+ }
60
+
61
+ // ---------------------------------------------------------------------------
62
+ // Manifest validation
63
+ // ---------------------------------------------------------------------------
64
+
65
+ // Manifests are metadata only — typically tens to hundreds of KB even for
66
+ // huge bundles. A 1 MiB cap is comfortably above realistic sizes and
67
+ // protects against a malicious archive whose "manifest" is actually a
68
+ // multi-GB stream intended to OOM the validator.
69
+ const MANIFEST_MAX_BYTES = 1 * 1024 * 1024;
70
+
71
+ /**
72
+ * Drain the first tar entry — which MUST be `manifest.json` — and run the
73
+ * full manifest-level validation pipeline:
74
+ * 1. Entry name check.
75
+ * 2. Size cap (1 MiB).
76
+ * 3. JSON parse.
77
+ * 4. Zod schema validation.
78
+ * 5. Self-referencing `manifest_sha256` verification against the
79
+ * canonicalized JSON (minus that field).
80
+ *
81
+ * On success, returns the parsed manifest plus a `Map` keyed by archive
82
+ * path that callers consult as each subsequent entry streams past.
83
+ *
84
+ * On failure, throws a `StreamingValidationError` with a distinct `code`
85
+ * for every failure mode.
86
+ */
87
+ export async function readAndValidateManifest(
88
+ first: StreamedTarEntry,
89
+ ): Promise<ManifestReadResult> {
90
+ if (first.header.name !== "manifest.json") {
91
+ // Drain the body so the underlying tar extractor isn't left dangling
92
+ // on backpressure before the caller reports the error.
93
+ first.body.resume();
94
+ throw new StreamingValidationError(
95
+ "manifest_not_first",
96
+ `Expected manifest.json as the first tar entry, got "${first.header.name}"`,
97
+ );
98
+ }
99
+
100
+ // Drain the entry body into a Buffer, enforcing the size cap as we go.
101
+ // The moment we cross the cap we destroy the entry stream — this signals
102
+ // the tar extractor (and therefore gunzip + upstream source) to abort,
103
+ // so a malicious archive whose "manifest" is a multi-GB decompressed
104
+ // stream can't force us to read through all of it before rejecting.
105
+ const chunks: Buffer[] = [];
106
+ let total = 0;
107
+ for await (const chunk of first.body) {
108
+ const buf = chunk instanceof Buffer ? chunk : Buffer.from(chunk);
109
+ total += buf.length;
110
+ if (total > MANIFEST_MAX_BYTES) {
111
+ first.body.destroy();
112
+ throw new StreamingValidationError(
113
+ "manifest_too_large",
114
+ `manifest.json exceeds ${MANIFEST_MAX_BYTES} byte limit (read ${total} bytes before aborting)`,
115
+ );
116
+ }
117
+ chunks.push(buf);
118
+ }
119
+
120
+ const bodyBuf = Buffer.concat(chunks, total);
121
+
122
+ let manifestRaw: unknown;
123
+ try {
124
+ manifestRaw = JSON.parse(bodyBuf.toString("utf8"));
125
+ } catch (err) {
126
+ throw new StreamingValidationError(
127
+ "manifest_malformed",
128
+ `manifest.json is not valid JSON: ${
129
+ err instanceof Error ? err.message : String(err)
130
+ }`,
131
+ );
132
+ }
133
+
134
+ const parseResult = ManifestSchema.safeParse(manifestRaw);
135
+ if (!parseResult.success) {
136
+ const issues = parseResult.error.issues
137
+ .map((i) => `${i.path.join(".") || "<root>"}: ${i.message}`)
138
+ .join("; ");
139
+ throw new StreamingValidationError(
140
+ "manifest_schema",
141
+ `manifest.json failed schema validation: ${issues}`,
142
+ );
143
+ }
144
+
145
+ const manifest = parseResult.data;
146
+
147
+ // Recompute the self-referencing checksum using the exact canonicalization
148
+ // that vbundle-validator.ts uses. Any drift here would silently reject
149
+ // valid bundles produced by buildVBundle.
150
+ const computed = computeManifestSha256(manifestRaw);
151
+ if (computed !== manifest.manifest_sha256) {
152
+ throw new StreamingValidationError(
153
+ "manifest_sha256",
154
+ `Manifest checksum mismatch: expected ${manifest.manifest_sha256}, computed ${computed}`,
155
+ );
156
+ }
157
+
158
+ const expected = new Map<string, { sha256: string; size: number }>();
159
+ for (const file of manifest.files) {
160
+ if (expected.has(file.path)) {
161
+ throw new StreamingValidationError(
162
+ "manifest_duplicate_path",
163
+ `Manifest contains duplicate entry for path: ${file.path}`,
164
+ );
165
+ }
166
+ expected.set(file.path, { sha256: file.sha256, size: file.size });
167
+ }
168
+
169
+ return { manifest, expected };
170
+ }
171
+
172
+ // ---------------------------------------------------------------------------
173
+ // Per-entry hash + size verifier
174
+ // ---------------------------------------------------------------------------
175
+
176
+ /**
177
+ * Create a passthrough `Transform` that:
178
+ * - forwards every chunk unchanged (identity transform for correct input),
179
+ * - incrementally SHA-256s the byte stream,
180
+ * - on `_flush`, errors the pipeline if the final digest or total byte
181
+ * count differs from `expected`.
182
+ *
183
+ * Errors are emitted as `StreamingValidationError` with `code` set to
184
+ * `"entry_hash"` or `"entry_size"` and `archivePath` populated so callers
185
+ * can surface which file failed.
186
+ *
187
+ * Consumers should pipe the entry body through this transform before
188
+ * writing to disk — that way a bad payload is caught before the byte
189
+ * reaches storage rather than after a whole 8 GB write completes.
190
+ */
191
+ export function createHashVerifier(expected: {
192
+ sha256: string;
193
+ size: number;
194
+ archivePath: string;
195
+ }): Transform {
196
+ const hash = createHash("sha256");
197
+ let bytes = 0;
198
+
199
+ return new Transform({
200
+ transform(
201
+ chunk: Buffer | string,
202
+ encoding: BufferEncoding,
203
+ callback: TransformCallback,
204
+ ) {
205
+ try {
206
+ const buf =
207
+ typeof chunk === "string" ? Buffer.from(chunk, encoding) : chunk;
208
+ hash.update(buf);
209
+ bytes += buf.length;
210
+ callback(null, buf);
211
+ } catch (err) {
212
+ callback(err instanceof Error ? err : new Error(String(err)));
213
+ }
214
+ },
215
+ flush(callback: TransformCallback) {
216
+ // Size check first — a wrong size is a sharper signal than a hash
217
+ // collision, and a truncated payload frequently triggers both.
218
+ if (bytes !== expected.size) {
219
+ callback(
220
+ new StreamingValidationError(
221
+ "entry_size",
222
+ `Size mismatch for ${expected.archivePath}: expected ${expected.size} bytes, got ${bytes}`,
223
+ expected.archivePath,
224
+ ),
225
+ );
226
+ return;
227
+ }
228
+
229
+ const digest = hash.digest("hex");
230
+ if (digest !== expected.sha256) {
231
+ callback(
232
+ new StreamingValidationError(
233
+ "entry_hash",
234
+ `Checksum mismatch for ${expected.archivePath}: expected ${expected.sha256}, computed ${digest}`,
235
+ expected.archivePath,
236
+ ),
237
+ );
238
+ return;
239
+ }
240
+
241
+ callback();
242
+ },
243
+ });
244
+ }
@@ -0,0 +1,217 @@
1
+ /**
2
+ * Streaming tar reader for `.vbundle` archives.
3
+ *
4
+ * A `.vbundle` is a gzip-compressed tar. This module gunzips the incoming
5
+ * `Readable`, pipes it through `tar-stream`'s push-style extractor, and
6
+ * adapts the `(header, stream, next)` event into a consumer-friendly async
7
+ * generator that yields one entry at a time.
8
+ *
9
+ * Memory invariant: each entry's body is surfaced as a `Readable`, never a
10
+ * pre-buffered blob. Callers MUST fully consume (or explicitly `resume()`)
11
+ * each body stream before advancing the outer generator — otherwise the
12
+ * underlying tar extractor will stall waiting on backpressure.
13
+ */
14
+
15
+ import type { Readable } from "node:stream";
16
+ import { createGunzip } from "node:zlib";
17
+
18
+ import { extract as tarExtract } from "tar-stream";
19
+
20
+ // ---------------------------------------------------------------------------
21
+ // Public types
22
+ // ---------------------------------------------------------------------------
23
+
24
+ /** Subset of tar-stream's header surface that vbundle consumers care about. */
25
+ export interface StreamedTarHeader {
26
+ name: string;
27
+ size: number;
28
+ type: "file" | "directory" | "pax-header" | "other";
29
+ }
30
+
31
+ export interface StreamedTarEntry {
32
+ header: StreamedTarHeader;
33
+ /**
34
+ * The entry body. Must be fully consumed (drained via for-await, piped,
35
+ * or explicitly `.resume()`'d) before the generator is advanced again.
36
+ */
37
+ body: Readable;
38
+ }
39
+
40
+ // ---------------------------------------------------------------------------
41
+ // Implementation
42
+ // ---------------------------------------------------------------------------
43
+
44
+ interface PendingEntry {
45
+ header: StreamedTarHeader;
46
+ body: Readable;
47
+ next: (err?: Error | null) => void;
48
+ }
49
+
50
+ function normalizeHeaderType(
51
+ type: string | undefined,
52
+ ): StreamedTarHeader["type"] {
53
+ switch (type) {
54
+ case "file":
55
+ return "file";
56
+ case "directory":
57
+ return "directory";
58
+ case "pax-header":
59
+ return "pax-header";
60
+ default:
61
+ return "other";
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Stream a `.vbundle` archive as an async sequence of tar entries.
67
+ *
68
+ * Errors from the upstream source, gunzip, or tar-stream extractor are
69
+ * surfaced by throwing from the generator.
70
+ *
71
+ * Early termination (caller `break`s or the for-await loop throws) destroys
72
+ * the upstream source and the tar extractor so that sockets and file
73
+ * descriptors are released promptly.
74
+ */
75
+ export async function* parseVBundleStream(
76
+ source: Readable,
77
+ ): AsyncGenerator<StreamedTarEntry, void, void> {
78
+ const gunzip = createGunzip();
79
+ const extractor = tarExtract();
80
+
81
+ // Terminal error captured from any stage in the pipeline. Consumed by the
82
+ // generator's internal pump; re-thrown on the next `yield`.
83
+ let pipelineError: Error | null = null;
84
+ let finished = false;
85
+
86
+ // Single-slot mailbox: the extractor pushes one entry here, and the
87
+ // generator consumes it. We gate the push-style API by holding the tar
88
+ // extractor's `next` callback until the caller is done with the body.
89
+ let waiter: {
90
+ resolve: (entry: PendingEntry | null) => void;
91
+ reject: (err: Error) => void;
92
+ } | null = null;
93
+ let pending: PendingEntry | null = null;
94
+
95
+ function pushEntry(entry: PendingEntry): void {
96
+ if (waiter) {
97
+ const w = waiter;
98
+ waiter = null;
99
+ w.resolve(entry);
100
+ } else {
101
+ pending = entry;
102
+ }
103
+ }
104
+
105
+ function pushError(err: Error): void {
106
+ if (pipelineError) return;
107
+ pipelineError = err;
108
+ if (waiter) {
109
+ const w = waiter;
110
+ waiter = null;
111
+ w.reject(err);
112
+ }
113
+ }
114
+
115
+ function pushFinish(): void {
116
+ finished = true;
117
+ if (waiter) {
118
+ const w = waiter;
119
+ waiter = null;
120
+ w.resolve(null);
121
+ }
122
+ }
123
+
124
+ extractor.on("entry", (header, body, next) => {
125
+ // Avoid unhandled "error" on body streams destroyed mid-flight; the
126
+ // extractor itself propagates the real error to its "error" listener.
127
+ body.on("error", () => {});
128
+ pushEntry({
129
+ header: {
130
+ name: header.name,
131
+ size: header.size,
132
+ type: normalizeHeaderType(header.type),
133
+ },
134
+ body,
135
+ next,
136
+ });
137
+ });
138
+
139
+ extractor.on("error", (err: Error) => {
140
+ pushError(err);
141
+ });
142
+
143
+ extractor.on("finish", () => {
144
+ pushFinish();
145
+ });
146
+
147
+ gunzip.on("error", (err: Error) => {
148
+ pushError(err);
149
+ extractor.destroy(err);
150
+ });
151
+
152
+ source.on("error", (err: Error) => {
153
+ pushError(err);
154
+ gunzip.destroy(err);
155
+ extractor.destroy(err);
156
+ });
157
+
158
+ // Kick off the pipeline. We wire stages with `.pipe(..., { end: true })`
159
+ // so that clean EOF from the source triggers `finish` on the extractor.
160
+ source.pipe(gunzip).pipe(extractor);
161
+
162
+ function nextEntry(): Promise<PendingEntry | null> {
163
+ if (pipelineError) return Promise.reject(pipelineError);
164
+ if (pending) {
165
+ const p = pending;
166
+ pending = null;
167
+ return Promise.resolve(p);
168
+ }
169
+ if (finished) return Promise.resolve(null);
170
+ return new Promise<PendingEntry | null>((resolve, reject) => {
171
+ waiter = { resolve, reject };
172
+ });
173
+ }
174
+
175
+ try {
176
+ while (true) {
177
+ const entry = await nextEntry();
178
+ if (entry === null) return;
179
+
180
+ let advanced = false;
181
+ const advance = (err?: Error | null): void => {
182
+ if (advanced) return;
183
+ advanced = true;
184
+ entry.next(err ?? null);
185
+ };
186
+
187
+ // When the consumer finishes (or abandons) the body, release the tar
188
+ // extractor so the next entry can flow.
189
+ entry.body.once("end", () => advance());
190
+ entry.body.once("close", () => advance());
191
+ entry.body.once("error", (err: Error) => advance(err));
192
+
193
+ try {
194
+ yield { header: entry.header, body: entry.body };
195
+ } catch (err) {
196
+ // Caller threw (or re-threw) inside the for-await loop. Propagate
197
+ // after cleanup in the finally below.
198
+ advance(err instanceof Error ? err : new Error(String(err)));
199
+ throw err;
200
+ }
201
+
202
+ // If the consumer neither consumed nor destroyed the body, drain it
203
+ // for them so the extractor can advance.
204
+ if (!advanced) {
205
+ entry.body.resume();
206
+ }
207
+ }
208
+ } finally {
209
+ // Early termination (break, throw, or natural completion): tear down the
210
+ // pipeline so we don't leak the socket/file descriptor underneath.
211
+ if (!finished || pipelineError) {
212
+ source.destroy();
213
+ gunzip.destroy();
214
+ extractor.destroy();
215
+ }
216
+ }
217
+ }
@@ -29,7 +29,7 @@ const ManifestFileEntry = z.object({
29
29
  size: z.number().int().nonnegative(),
30
30
  });
31
31
 
32
- const ManifestSchema = z.object({
32
+ export const ManifestSchema = z.object({
33
33
  schema_version: z.string(),
34
34
  created_at: z.string(),
35
35
  source: z.string().optional(),
@@ -176,7 +176,7 @@ function sha256Hex(data: Uint8Array | string): string {
176
176
  * Canonicalize a JSON object by sorting keys recursively, then SHA-256 hash it.
177
177
  * This matches the platform's canonicalization approach.
178
178
  */
179
- function canonicalizeJson(obj: unknown): string {
179
+ export function canonicalizeJson(obj: unknown): string {
180
180
  return JSON.stringify(obj, (_key, value) => {
181
181
  if (value && typeof value === "object" && !Array.isArray(value)) {
182
182
  const sorted: Record<string, unknown> = {};
@@ -189,6 +189,18 @@ function canonicalizeJson(obj: unknown): string {
189
189
  });
190
190
  }
191
191
 
192
+ /**
193
+ * Recompute the `manifest_sha256` field for a manifest object. Strips the
194
+ * `manifest_sha256` property, canonicalizes the remaining JSON, and returns
195
+ * the SHA-256 hex digest. Centralized here so the streaming validator and
196
+ * the in-memory validator agree on the exact canonicalization.
197
+ */
198
+ export function computeManifestSha256(manifest: unknown): string {
199
+ const copy = { ...(manifest as Record<string, unknown>) };
200
+ delete copy.manifest_sha256;
201
+ return sha256Hex(canonicalizeJson(copy));
202
+ }
203
+
192
204
  // ---------------------------------------------------------------------------
193
205
  // Core validation
194
206
  // ---------------------------------------------------------------------------
@@ -300,10 +312,7 @@ export function validateVBundle(data: Uint8Array): VBundleValidationResult {
300
312
  // Step 5: Verify manifest checksum
301
313
  // The manifest_sha256 field is the SHA-256 of the canonicalized JSON
302
314
  // with the manifest_sha256 field itself excluded.
303
- const manifestForChecksum = { ...(manifestRaw as Record<string, unknown>) };
304
- delete manifestForChecksum.manifest_sha256;
305
- const canonicalized = canonicalizeJson(manifestForChecksum);
306
- const computedManifestSha256 = sha256Hex(canonicalized);
315
+ const computedManifestSha256 = computeManifestSha256(manifestRaw);
307
316
 
308
317
  if (computedManifestSha256 !== manifest.manifest_sha256) {
309
318
  errors.push({
@@ -33,7 +33,10 @@ const addedMessages: Array<{
33
33
  }> = [];
34
34
  let createConversationShouldThrow = false;
35
35
 
36
+ const realConversationCrud =
37
+ await import("../../../memory/conversation-crud.js");
36
38
  mock.module("../../../memory/conversation-crud.js", () => ({
39
+ ...realConversationCrud,
37
40
  createConversation: (opts: unknown) => {
38
41
  if (createConversationShouldThrow) {
39
42
  throw new Error("synthetic createConversation failure");
@@ -51,10 +54,37 @@ mock.module("../../../memory/conversation-crud.js", () => ({
51
54
  addedMessages.push({ conversationId, role, content });
52
55
  return { id: `msg-${addedMessages.length}` };
53
56
  },
57
+ // Stub the message reader surface transitively required by other
58
+ // modules that route through conversation-crud. The home-feed route
59
+ // paths don't consult it directly, but Bun's ESM mock needs the named
60
+ // export to exist so transitive `import { getMessages }` resolves.
61
+ getMessages: () => [],
62
+ getMessagesPaginated: () => ({ messages: [], hasMore: false }),
63
+ getMessageById: () => null,
64
+ }));
65
+
66
+ // Stub the rollup producer so the on-visit refresh trigger inside
67
+ // handleGetHomeFeed doesn't try to resolve a real provider or touch
68
+ // relationship state. The route calls the producer fire-and-forget,
69
+ // so tests observe the trigger via call-count on this spy rather
70
+ // than awaiting a return value.
71
+ //
72
+ // Default skip reason is `empty_items` — a real LLM attempt that
73
+ // returned nothing to consolidate. Using a real-run skip means the
74
+ // debounce gate holds firm in the default case (matching production
75
+ // semantics); individual tests override with `no_provider` etc. to
76
+ // exercise the rollback path.
77
+ const rollupProducerSpy = mock<() => Promise<unknown>>(async () => ({
78
+ wroteCount: 0,
79
+ skippedReason: "empty_items",
80
+ }));
81
+ mock.module("../../../home/rollup-producer.js", () => ({
82
+ runRollupProducer: rollupProducerSpy,
54
83
  }));
55
84
 
56
85
  // Dynamic imports so module mocks are wired before evaluation.
57
86
  const {
87
+ __resetOnVisitRefreshStateForTests,
58
88
  computeGreeting,
59
89
  formatRelativeTime,
60
90
  handleGetHomeFeed,
@@ -125,6 +155,8 @@ beforeEach(() => {
125
155
  origWorkspaceDir = process.env.VELLUM_WORKSPACE_DIR;
126
156
  process.env.VELLUM_WORKSPACE_DIR = workspaceDir;
127
157
  publishSpy.mockClear();
158
+ rollupProducerSpy.mockClear();
159
+ __resetOnVisitRefreshStateForTests();
128
160
  createdConversations.length = 0;
129
161
  addedMessages.length = 0;
130
162
  createConversationShouldThrow = false;
@@ -322,6 +354,85 @@ describe("handleGetHomeFeed", () => {
322
354
  expect(body.contextBanner.timeAwayLabel).toBe("2 hours ago");
323
355
  });
324
356
 
357
+ test("fires the rollup producer fire-and-forget on the first GET", async () => {
358
+ const res = await handleGetHomeFeed(
359
+ new Request("http://localhost/v1/home/feed?timeAwaySeconds=0"),
360
+ );
361
+ expect(res.status).toBe(200);
362
+ // Yield a microtask so the fire-and-forget call reaches its
363
+ // first await point.
364
+ await Promise.resolve();
365
+ expect(rollupProducerSpy).toHaveBeenCalledTimes(1);
366
+ });
367
+
368
+ test("does NOT refire the rollup when the debounce window has not elapsed", async () => {
369
+ await handleGetHomeFeed(
370
+ new Request("http://localhost/v1/home/feed?timeAwaySeconds=0"),
371
+ );
372
+ await Promise.resolve();
373
+ expect(rollupProducerSpy).toHaveBeenCalledTimes(1);
374
+
375
+ // A second GET milliseconds later should NOT re-trigger the
376
+ // rollup — the 10-minute debounce prevents aggressive pollers or
377
+ // multiple panels from firing repeat refreshes.
378
+ await handleGetHomeFeed(
379
+ new Request("http://localhost/v1/home/feed?timeAwaySeconds=0"),
380
+ );
381
+ await handleGetHomeFeed(
382
+ new Request("http://localhost/v1/home/feed?timeAwaySeconds=0"),
383
+ );
384
+ await Promise.resolve();
385
+ expect(rollupProducerSpy).toHaveBeenCalledTimes(1);
386
+ });
387
+
388
+ test("debounce is rolled back when the producer skips before the LLM call", async () => {
389
+ // Simulate the daemon-boot race: the first GET fires the
390
+ // producer but the provider registry isn't ready yet, so the
391
+ // producer short-circuits with `no_provider`. A second GET a
392
+ // moment later must still be allowed to fire — otherwise Home
393
+ // stays stale for the full 10-minute debounce window while the
394
+ // user is actively trying to refresh.
395
+ rollupProducerSpy.mockImplementationOnce(async () => ({
396
+ wroteCount: 0,
397
+ skippedReason: "no_provider",
398
+ }));
399
+
400
+ await handleGetHomeFeed(
401
+ new Request("http://localhost/v1/home/feed?timeAwaySeconds=0"),
402
+ );
403
+ // Let the fire-and-forget `.then()` that performs the rollback
404
+ // run before we issue the second GET. Two microtask ticks
405
+ // because the chain is runRollupProducer -> .then handler.
406
+ await Promise.resolve();
407
+ await Promise.resolve();
408
+ expect(rollupProducerSpy).toHaveBeenCalledTimes(1);
409
+
410
+ // Second GET — producer is now ready. Gate must have been
411
+ // rolled back so this GET re-fires the producer.
412
+ await handleGetHomeFeed(
413
+ new Request("http://localhost/v1/home/feed?timeAwaySeconds=0"),
414
+ );
415
+ await Promise.resolve();
416
+ expect(rollupProducerSpy).toHaveBeenCalledTimes(2);
417
+ });
418
+
419
+ test("rollup producer failure does not turn the GET into an error", async () => {
420
+ // Even if the rollup producer rejects, the GET must still return
421
+ // 200 with the cached feed — the refresh is fire-and-forget.
422
+ rollupProducerSpy.mockImplementationOnce(async () => {
423
+ throw new Error("synthetic rollup failure");
424
+ });
425
+
426
+ const res = await handleGetHomeFeed(
427
+ new Request("http://localhost/v1/home/feed?timeAwaySeconds=0"),
428
+ );
429
+ expect(res.status).toBe(200);
430
+ // Drain the rejected promise so it doesn't leak into the next
431
+ // test as an unhandled rejection.
432
+ await Promise.resolve();
433
+ await Promise.resolve();
434
+ });
435
+
325
436
  test("newCount counts only status=new after filtering", async () => {
326
437
  writeFeedFile([
327
438
  makeItem({ id: "a", status: "new" }),