@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
@@ -102,6 +102,15 @@ export async function handleGuardianCallbackDecision(
102
102
  approvalMessageTs,
103
103
  } = params;
104
104
 
105
+ // Reactions have their own deterministic emoji-to-action mapping in
106
+ // `handleApprovalInterception`. Return null immediately so reaction
107
+ // callbackData never enters the conversational engine below, which would
108
+ // misclassify `reaction:white_check_mark` etc. as plain text and only
109
+ // ever produce `approve_once`/`reject`.
110
+ if (callbackData?.startsWith("reaction:")) {
111
+ return null;
112
+ }
113
+
105
114
  // Callback/button path: deterministic and takes priority.
106
115
  let callbackDecision: ApprovalDecisionResult | null = null;
107
116
  if (callbackData) {
@@ -10,7 +10,7 @@ import { getAvatarImagePath } from "../../util/platform.js";
10
10
  import { buildAssistantEvent } from "../assistant-event.js";
11
11
  import { assistantEventHub } from "../assistant-event-hub.js";
12
12
  import { DAEMON_INTERNAL_ASSISTANT_ID } from "../assistant-scope.js";
13
- import { httpError } from "../http-errors.js";
13
+ import { httpError, type HttpErrorCode } from "../http-errors.js";
14
14
  import type { RouteDefinition } from "../http-router.js";
15
15
 
16
16
  const log = getLogger("avatar-routes");
@@ -78,9 +78,25 @@ export function avatarRouteDefinitions(): RouteDefinition[] {
78
78
  const result = writeTraitsAndRenderAvatar(body);
79
79
 
80
80
  if (!result.ok) {
81
- const status = result.reason === "render_error" ? 500 : 400;
82
- const code =
83
- result.reason === "render_error" ? "INTERNAL_ERROR" : "BAD_REQUEST";
81
+ // Map each failure reason to an HTTP status that reflects its
82
+ // cause: invalid inputs → 400, missing native dependency → 503,
83
+ // everything else 500.
84
+ let status: number;
85
+ let code: HttpErrorCode;
86
+ switch (result.reason) {
87
+ case "invalid_traits":
88
+ status = 400;
89
+ code = "BAD_REQUEST";
90
+ break;
91
+ case "native_unavailable":
92
+ status = 503;
93
+ code = "SERVICE_UNAVAILABLE";
94
+ break;
95
+ case "render_error":
96
+ status = 500;
97
+ code = "INTERNAL_ERROR";
98
+ break;
99
+ }
84
100
  return httpError(code, result.message, status);
85
101
  }
86
102
 
@@ -16,7 +16,6 @@ import { existsSync, readFileSync } from "node:fs";
16
16
 
17
17
  import { z } from "zod";
18
18
 
19
- import { getConfig } from "../../config/loader.js";
20
19
  import { readNowScratchpad } from "../../daemon/conversation-runtime-assembly.js";
21
20
  import { getConversationByKey } from "../../memory/conversation-key-store.js";
22
21
  import { resolvePersonaContext } from "../../prompts/persona-resolver.js";
@@ -178,9 +177,7 @@ async function handleBtw(
178
177
  userPersona,
179
178
  channelPersona,
180
179
  userSlug,
181
- ...(isGreeting
182
- ? { modelIntent: getConfig().ui.greetingModelIntent }
183
- : {}),
180
+ ...(isGreeting ? { callSite: "emptyStateGreeting" as const } : {}),
184
181
  onEvent: (event) => {
185
182
  if (event.type === "text_delta") {
186
183
  controller.enqueue(
@@ -386,8 +386,13 @@ export function conversationManagementRouteDefinitions(
386
386
  );
387
387
  }
388
388
 
389
- // Broadcast conversation_title_updated so all connected clients
390
- // (including the one that initiated the rename) update immediately.
389
+ // Broadcast conversation_title_updated so the client currently
390
+ // viewing this conversation can update the header in-place.
391
+ // Scoped to the conversation id so foreign conversationId values
392
+ // don't leak to other subscribers' speculative ID-resolution
393
+ // paths. Other clients learn about the rename via the unscoped
394
+ // `conversation_list_invalidated` published below, which triggers
395
+ // their sidebars to refetch and pick up the new title.
391
396
  assistantEventHub
392
397
  .publish(
393
398
  buildAssistantEvent(
@@ -752,6 +757,19 @@ export function conversationManagementRouteDefinitions(
752
757
  groupId: u.groupId,
753
758
  })),
754
759
  );
760
+ assistantEventHub
761
+ .publish(
762
+ buildAssistantEvent(DAEMON_INTERNAL_ASSISTANT_ID, {
763
+ type: "conversation_list_invalidated",
764
+ reason: "reordered",
765
+ }),
766
+ )
767
+ .catch((err) => {
768
+ log.warn(
769
+ { err },
770
+ "Failed to publish conversation_list_invalidated (reordered)",
771
+ );
772
+ });
755
773
  return Response.json({ ok: true });
756
774
  },
757
775
  },
@@ -65,6 +65,7 @@ import {
65
65
  getLastAssistantTimestampBefore,
66
66
  getMessages,
67
67
  getMessagesPaginated,
68
+ hasMessages,
68
69
  type MessageRow,
69
70
  provenanceFromTrustContext,
70
71
  setConversationOriginChannelIfUnset,
@@ -1066,7 +1067,20 @@ function makeHubPublisher(
1066
1067
  typeof (msg as { conversationId?: unknown }).conversationId === "string"
1067
1068
  ? (msg as { conversationId: string }).conversationId
1068
1069
  : undefined;
1069
- const resolvedConversationId = msgConversationId ?? conversationId;
1070
+ // `conversation_list_invalidated` is a list-level system event: it
1071
+ // describes no particular conversation and every connected client
1072
+ // should refresh its sidebar. Publish it unscoped so the SSE hub does
1073
+ // not filter it out by the subscriber's `filter.conversationId`.
1074
+ // Other events (including `conversation_title_updated`) stay scoped to
1075
+ // their conversation — unscoped scoped-events would leak foreign
1076
+ // `conversationId` values to native clients' speculative ID-resolution
1077
+ // path. For `conversation_title_updated` we instead enqueue a matching
1078
+ // unscoped `conversation_list_invalidated` below so other clients'
1079
+ // sidebars can refresh and pick up the new title.
1080
+ const resolvedConversationId =
1081
+ msg.type === "conversation_list_invalidated"
1082
+ ? undefined
1083
+ : (msgConversationId ?? conversationId);
1070
1084
  const event = buildAssistantEvent(
1071
1085
  DAEMON_INTERNAL_ASSISTANT_ID,
1072
1086
  msg,
@@ -1082,6 +1096,29 @@ function makeHubPublisher(
1082
1096
  "assistant-events hub subscriber threw during POST /messages",
1083
1097
  );
1084
1098
  }
1099
+
1100
+ // When the agent loop auto-generates a conversation title, also
1101
+ // broadcast an unscoped `conversation_list_invalidated` so every
1102
+ // connected client's sidebar can refresh and pick up the new title.
1103
+ // Without this, clients viewing other conversations (or a draft)
1104
+ // would never learn that the title for this conversation changed.
1105
+ // The scoped `conversation_title_updated` above still handles the
1106
+ // in-place update for the client currently viewing this conversation.
1107
+ if (msg.type === "conversation_title_updated") {
1108
+ try {
1109
+ await deps.assistantEventHub.publish(
1110
+ buildAssistantEvent(DAEMON_INTERNAL_ASSISTANT_ID, {
1111
+ type: "conversation_list_invalidated",
1112
+ reason: "renamed",
1113
+ }),
1114
+ );
1115
+ } catch (err) {
1116
+ log.warn(
1117
+ { err },
1118
+ "Failed to publish conversation_list_invalidated after title update",
1119
+ );
1120
+ }
1121
+ }
1085
1122
  })();
1086
1123
  };
1087
1124
  }
@@ -1302,6 +1339,7 @@ export async function handleSendMessage(
1302
1339
  bypassSecretCheck?: boolean;
1303
1340
  hostHomeDir?: string;
1304
1341
  hostUsername?: string;
1342
+ clientMessageId?: string;
1305
1343
  onboarding?: {
1306
1344
  tools: string[];
1307
1345
  tasks: string[];
@@ -1312,6 +1350,8 @@ export async function handleSendMessage(
1312
1350
  };
1313
1351
 
1314
1352
  const { conversationKey, content, attachmentIds } = body;
1353
+ const clientMessageId =
1354
+ typeof body.clientMessageId === "string" ? body.clientMessageId : undefined;
1315
1355
  if (!body.sourceChannel || typeof body.sourceChannel !== "string") {
1316
1356
  return httpError("BAD_REQUEST", "sourceChannel is required", 400);
1317
1357
  }
@@ -1414,19 +1454,27 @@ export async function handleSendMessage(
1414
1454
 
1415
1455
  const smDeps = deps.sendMessageDeps;
1416
1456
 
1417
- // Notify all connected clients that the conversation list changed when a
1418
- // new standard conversation is created so sidebars can refresh.
1419
- if (mapping.created && mapping.conversationType === "standard") {
1420
- smDeps.assistantEventHub
1421
- .publish(
1422
- buildAssistantEvent(DAEMON_INTERNAL_ASSISTANT_ID, {
1423
- type: "conversation_list_invalidated",
1424
- reason: "created",
1425
- }),
1426
- )
1427
- .catch((err) => {
1428
- log.warn({ err }, "Failed to publish conversation_list_invalidated");
1429
- });
1457
+ // Notify all connected clients that the conversation list changed when
1458
+ // this is the first message in a standard conversation, so sidebars on
1459
+ // other devices can refresh. We check for first-message rather than
1460
+ // first-create because the SSE subscribe handler (events-routes.ts) may
1461
+ // have already materialised the conversation from a draft key before any
1462
+ // message was sent — in that case `mapping.created` is `false` even
1463
+ // though, from the user's perspective, this is a brand-new conversation
1464
+ // that other clients don't yet know about.
1465
+ if (mapping.conversationType === "standard") {
1466
+ if (!hasMessages(mapping.conversationId)) {
1467
+ smDeps.assistantEventHub
1468
+ .publish(
1469
+ buildAssistantEvent(DAEMON_INTERNAL_ASSISTANT_ID, {
1470
+ type: "conversation_list_invalidated",
1471
+ reason: "created",
1472
+ }),
1473
+ )
1474
+ .catch((err) => {
1475
+ log.warn({ err }, "Failed to publish conversation_list_invalidated");
1476
+ });
1477
+ }
1430
1478
  }
1431
1479
 
1432
1480
  // Build transport metadata from the request so the daemon can inject
@@ -1720,6 +1768,7 @@ export async function handleSendMessage(
1720
1768
  text: rawContent,
1721
1769
  conversationId,
1722
1770
  messageId: persisted.id,
1771
+ clientMessageId,
1723
1772
  });
1724
1773
  onEvent({ type: "assistant_text_delta", text: cannedGreeting });
1725
1774
  onEvent({ type: "message_complete", conversationId });
@@ -1817,6 +1866,7 @@ export async function handleSendMessage(
1817
1866
  { isInteractive },
1818
1867
  undefined, // displayContent
1819
1868
  transport,
1869
+ clientMessageId,
1820
1870
  );
1821
1871
  if (enqueueResult.rejected) {
1822
1872
  return Response.json(
@@ -1929,9 +1979,9 @@ export async function handleSendMessage(
1929
1979
  messageCount: conversation.getMessages().length,
1930
1980
  inputTokens: conversation.usageStats.inputTokens,
1931
1981
  outputTokens: conversation.usageStats.outputTokens,
1932
- maxInputTokens: config.contextWindow.maxInputTokens,
1933
- model: config.services.inference.model,
1934
- provider: config.services.inference.provider,
1982
+ maxInputTokens: config.llm.default.contextWindow.maxInputTokens,
1983
+ model: config.llm.default.model,
1984
+ provider: config.llm.default.provider,
1935
1985
  estimatedCost: conversation.usageStats.estimatedCost,
1936
1986
  userMessageInterface: sourceInterface,
1937
1987
  };
@@ -2019,6 +2069,7 @@ export async function handleSendMessage(
2019
2069
  text: rawContent,
2020
2070
  conversationId,
2021
2071
  messageId: persisted.id,
2072
+ clientMessageId,
2022
2073
  });
2023
2074
  if (modelInfoEvent) {
2024
2075
  onEvent(modelInfoEvent);
@@ -2075,6 +2126,7 @@ export async function handleSendMessage(
2075
2126
  text: rawContent,
2076
2127
  conversationId,
2077
2128
  messageId: persisted.id,
2129
+ clientMessageId,
2078
2130
  });
2079
2131
  conversation.emitActivityState(
2080
2132
  "thinking",
@@ -2144,6 +2196,7 @@ export async function handleSendMessage(
2144
2196
  conversationId: mapping.conversationId,
2145
2197
  messageId,
2146
2198
  requestId,
2199
+ clientMessageId,
2147
2200
  });
2148
2201
 
2149
2202
  // Fire-and-forget the agent loop; events flow to the hub via onEvent.
@@ -2165,28 +2218,62 @@ export async function handleSendMessage(
2165
2218
  );
2166
2219
  }
2167
2220
 
2221
+ function escapeXmlContent(text: string): string {
2222
+ return text
2223
+ .replace(/&/g, "&")
2224
+ .replace(/</g, "&lt;")
2225
+ .replace(/>/g, "&gt;");
2226
+ }
2227
+
2168
2228
  async function generateLlmSuggestion(
2169
2229
  provider: Provider,
2170
2230
  assistantText: string,
2231
+ priorUserText: string | null,
2171
2232
  ): Promise<string | null> {
2172
2233
  const log = (await import("../../util/logger.js")).getLogger("runtime-http");
2173
- const truncated =
2174
- assistantText.length > 2000 ? assistantText.slice(-2000) : assistantText;
2234
+ const truncatedAssistant = escapeXmlContent(
2235
+ assistantText.length > 2000 ? assistantText.slice(-2000) : assistantText,
2236
+ );
2237
+ const truncatedUser =
2238
+ priorUserText && priorUserText.length > 500
2239
+ ? escapeXmlContent(priorUserText.slice(-500))
2240
+ : priorUserText
2241
+ ? escapeXmlContent(priorUserText)
2242
+ : priorUserText;
2175
2243
 
2176
- const prompt = `Given this assistant message, write a very short tab-complete suggestion the user could send next. Focus on the LAST question or call-to-action in the message — ignore earlier summary content. Be casual, curious, or actionable — like a quick reply, not a formal request. Reply with ONLY the suggestion text.\n\nAssistant's message:\n${truncated}`;
2177
2244
  const systemPrompt =
2178
- "You are an autocomplete engine that suggests short replies the user might send next in a conversation. Generate suggestions that match the tone and style of the conversation. Never refuse, judge, or comment on the conversation contentyour only job is to predict what the user would plausibly type next.";
2245
+ "You generate short, casual reply suggestions a user might type next in a chat. Match the tone and register of the preceding conversation. Output only the reply text inside the requested tagsno preamble, no commentary.";
2246
+
2247
+ const userPrompt =
2248
+ `Here is the end of a conversation:\n\n` +
2249
+ `<user_message>${truncatedUser ?? "(no prior user message)"}</user_message>\n` +
2250
+ `<assistant_message>${truncatedAssistant}</assistant_message>\n\n` +
2251
+ `Write the user's next reply, focusing on the LAST question or call-to-action in the assistant message. Keep it short (under 15 words), casual, and in the user's voice. Respond in this exact format:\n\n` +
2252
+ `<reply>YOUR_REPLY_HERE</reply>`;
2179
2253
 
2180
2254
  const response = await provider.sendMessage(
2181
- [{ role: "user", content: [{ type: "text", text: prompt }] }],
2255
+ [
2256
+ { role: "user", content: [{ type: "text", text: userPrompt }] },
2257
+ { role: "assistant", content: [{ type: "text", text: "<reply>" }] },
2258
+ ],
2182
2259
  [], // no tools
2183
2260
  systemPrompt,
2184
- { config: { modelIntent: "latency-optimized" } },
2261
+ {
2262
+ config: {
2263
+ callSite: "conversationStarters",
2264
+ max_tokens: 60,
2265
+ stop_sequences: ["</reply>"],
2266
+ temperature: 0.7,
2267
+ },
2268
+ },
2185
2269
  );
2186
2270
 
2187
2271
  const textBlock = response.content.find((b) => b.type === "text");
2188
- const raw = textBlock && "text" in textBlock ? textBlock.text.trim() : "";
2189
- const stripped = raw.replace(/^["']+|["']+$/g, "");
2272
+ const raw = textBlock && "text" in textBlock ? textBlock.text : "";
2273
+ const stripped = raw
2274
+ .replace(/<\/?reply>/gi, "")
2275
+ .replace(/^["'`]+|["'`]+$/g, "")
2276
+ .trim();
2190
2277
 
2191
2278
  if (!stripped) {
2192
2279
  log.debug("Suggestion rejected: empty LLM response");
@@ -2299,14 +2386,33 @@ export async function handleGetSuggestion(
2299
2386
  });
2300
2387
  }
2301
2388
 
2389
+ // Find the most recent user message preceding this assistant turn so the
2390
+ // suggestion model can see both sides of the conversation and doesn't have
2391
+ // to guess which role it's generating for.
2392
+ let priorUserText: string | null = null;
2393
+ for (let j = i - 1; j >= 0; j--) {
2394
+ if (rawMessages[j].role !== "user") continue;
2395
+ let userContent: unknown;
2396
+ try {
2397
+ userContent = JSON.parse(rawMessages[j].content);
2398
+ } catch {
2399
+ userContent = rawMessages[j].content;
2400
+ }
2401
+ const userText = renderHistoryContent(userContent).text.trim();
2402
+ if (userText) {
2403
+ priorUserText = userText;
2404
+ break;
2405
+ }
2406
+ }
2407
+
2302
2408
  // Try LLM suggestion using the configured provider
2303
- const provider = await getConfiguredProvider();
2409
+ const provider = await getConfiguredProvider("conversationStarters");
2304
2410
  if (provider) {
2305
2411
  try {
2306
2412
  // Deduplicate concurrent requests
2307
2413
  let promise = suggestionInFlight.get(msg.id);
2308
2414
  if (!promise) {
2309
- promise = generateLlmSuggestion(provider, text);
2415
+ promise = generateLlmSuggestion(provider, text, priorUserText);
2310
2416
  suggestionInFlight.set(msg.id, promise);
2311
2417
  }
2312
2418
 
@@ -65,7 +65,7 @@ function handleDebug(): Response {
65
65
  startedAt: new Date(startedAt).toISOString(),
66
66
  },
67
67
  provider: {
68
- configuredProvider: config.services.inference.provider,
68
+ configuredProvider: config.llm.default.provider,
69
69
  registeredProviders,
70
70
  routingSources,
71
71
  inferenceMode: config.services.inference.mode,
@@ -222,7 +222,7 @@ async function handleDictation(body: DictationBody): Promise<Response> {
222
222
  const transcription = expandSnippets(body.transcription, profile.snippets);
223
223
 
224
224
  try {
225
- const provider = await getConfiguredProvider();
225
+ const provider = await getConfiguredProvider("interactionClassifier");
226
226
  if (!provider) {
227
227
  log.warn(
228
228
  "Dictation: no provider available, using heuristic + raw transcription",
@@ -288,7 +288,7 @@ async function handleDictation(body: DictationBody): Promise<Response> {
288
288
  systemPrompt,
289
289
  {
290
290
  config: {
291
- modelIntent: "latency-optimized",
291
+ callSite: "interactionClassifier",
292
292
  max_tokens: maxTokens,
293
293
  tool_choice: {
294
294
  type: "tool" as const,
@@ -381,7 +381,7 @@ async function handleCommandMode(
381
381
  const maxTokens = Math.max(1024, computeMaxTokens(inputLength));
382
382
 
383
383
  try {
384
- const provider = await getConfiguredProvider();
384
+ const provider = await getConfiguredProvider("interactionClassifier");
385
385
  if (!provider) {
386
386
  log.warn("Command mode: no provider available, returning selected text");
387
387
  const normalizedText = applyDictionary(
@@ -399,7 +399,9 @@ async function handleCommandMode(
399
399
  [userMessage(body.transcription)],
400
400
  [],
401
401
  systemPrompt,
402
- { config: { modelIntent: "latency-optimized", max_tokens: maxTokens } },
402
+ {
403
+ config: { callSite: "interactionClassifier", max_tokens: maxTokens },
404
+ },
403
405
  );
404
406
 
405
407
  const textBlock = response.content.find((b) => b.type === "text");
@@ -10,6 +10,16 @@
10
10
  * When `conversationKey` is provided, subscribers receive events scoped to
11
11
  * that conversation. When omitted, subscribers receive events from ALL
12
12
  * conversations for this assistant (unfiltered).
13
+ *
14
+ * If the conversationKey has no server-side mapping yet (e.g. a client-
15
+ * generated draft UUID that has not been sent a first message), this
16
+ * handler eagerly materialises the conversation so the subscriber's
17
+ * `filter.conversationId` matches the id under which the first turn's
18
+ * scoped events (text deltas, tool calls, message_complete) will be
19
+ * published by `handleSendMessage`. The `conversation_list_invalidated`
20
+ * notification for other clients is driven by `handleSendMessage`'s
21
+ * first-message check, so eager materialisation here is safe and does
22
+ * not hide the first-message notification from other clients.
13
23
  */
14
24
 
15
25
  import { getOrCreateConversation } from "../../memory/conversation-key-store.js";
@@ -75,6 +85,12 @@ export function handleSubscribeAssistantEvents(
75
85
  assistantId: DAEMON_INTERNAL_ASSISTANT_ID,
76
86
  };
77
87
  if (conversationKey) {
88
+ // Eagerly resolve (and if necessary create) the conversation so the
89
+ // subscriber's filter matches the id under which first-turn scoped
90
+ // events will be published. The `conversation_list_invalidated`
91
+ // publish is driven by `handleSendMessage`'s first-message check,
92
+ // so eager materialisation here is safe and does not suppress the
93
+ // cross-client notification.
78
94
  const mapping = getOrCreateConversation(conversationKey);
79
95
  filter.conversationId = mapping.conversationId;
80
96
  }
@@ -31,6 +31,7 @@ import type {
31
31
  ApprovalCopyGenerator,
32
32
  } from "../http-types.js";
33
33
  import { parseApprovalIntent } from "../nl-approval-parser.js";
34
+ import { isTrackedApprovalPromptTs } from "./approval-prompt-ts-tracker.js";
34
35
  import { handleGuardianCallbackDecision } from "./approval-strategies/guardian-callback-strategy.js";
35
36
  import { handleGuardianTextEngineDecision } from "./approval-strategies/guardian-text-engine-strategy.js";
36
37
  import {
@@ -139,7 +140,15 @@ export async function handleApprovalInterception(
139
140
  // so getChannelApprovalPrompt(conversationId) would return null.
140
141
  // Only guardians can approve via reaction — non-guardian reactions are
141
142
  // silently ignored to prevent self-approval.
142
- if (callbackData?.startsWith("reaction:")) {
143
+ //
144
+ // `reaction_removed:` callbackData never expresses an approval intent, and
145
+ // `isSlackReactionEvent` short-circuits before reaching here for removals,
146
+ // but guard explicitly so a future refactor can't turn an un-react into an
147
+ // unintended approval.
148
+ if (
149
+ callbackData?.startsWith("reaction:") &&
150
+ !callbackData.startsWith("reaction_removed:")
151
+ ) {
143
152
  if (trustCtx.trustClass !== "guardian" || !actorExternalId) {
144
153
  return { handled: true, type: "stale_ignored" };
145
154
  }
@@ -149,6 +158,23 @@ export async function handleApprovalInterception(
149
158
  return { handled: true, type: "stale_ignored" };
150
159
  }
151
160
 
161
+ // Require the reacted-to message to be a tracked approval prompt. Without
162
+ // this check, any unrelated 👍 reaction from the guardian in a subscribed
163
+ // channel would approve the outstanding pending request (now that
164
+ // reactions are admitted from any subscribed channel, not just tracked
165
+ // bot threads). `approvalMessageTs` is `item.ts` of the reacted-to
166
+ // Slack message, propagated from `sourceMetadata.messageId`.
167
+ if (
168
+ !approvalMessageTs ||
169
+ !isTrackedApprovalPromptTs(
170
+ sourceChannel,
171
+ conversationExternalId,
172
+ approvalMessageTs,
173
+ )
174
+ ) {
175
+ return { handled: true, type: "stale_ignored" };
176
+ }
177
+
152
178
  const allPending = getAllPendingApprovalsByGuardianChat(
153
179
  sourceChannel,
154
180
  conversationExternalId,
@@ -493,8 +519,12 @@ export async function handleApprovalInterception(
493
519
  // ── Slack reaction path ──
494
520
  // Reactions produce `callbackData` of the form `reaction:<emoji_name>`.
495
521
  // Only guardians can approve via reaction — non-guardian reactions are
496
- // silently ignored to prevent self-approval.
497
- if (callbackData?.startsWith("reaction:")) {
522
+ // silently ignored to prevent self-approval. `reaction_removed:` never
523
+ // expresses an approval intent.
524
+ if (
525
+ callbackData?.startsWith("reaction:") &&
526
+ !callbackData.startsWith("reaction_removed:")
527
+ ) {
498
528
  if (trustCtx.trustClass !== "guardian") {
499
529
  return { handled: true, type: "stale_ignored" };
500
530
  }
@@ -17,6 +17,7 @@ import {
17
17
  } from "../gateway-client.js";
18
18
  import { buildActionLegend } from "../guardian-decision-types.js";
19
19
  import type { ApprovalCopyGenerator } from "../http-types.js";
20
+ import { trackApprovalPromptTs } from "./approval-prompt-ts-tracker.js";
20
21
  import { requiredDecisionKeywords } from "./channel-route-shared.js";
21
22
 
22
23
  const log = getLogger("runtime-http");
@@ -141,7 +142,7 @@ export async function deliverGeneratedApprovalPrompt(
141
142
  }
142
143
 
143
144
  try {
144
- await deliverApprovalPrompt(
145
+ const deliveryResult = await deliverApprovalPrompt(
145
146
  replyCallbackUrl,
146
147
  chatId,
147
148
  enrichedText,
@@ -149,6 +150,9 @@ export async function deliverGeneratedApprovalPrompt(
149
150
  assistantId,
150
151
  bearerToken,
151
152
  );
153
+ if (deliveryResult.ts) {
154
+ trackApprovalPromptTs(sourceChannel, chatId, deliveryResult.ts);
155
+ }
152
156
  return true;
153
157
  } catch (err) {
154
158
  log.error(
@@ -168,7 +172,7 @@ export async function deliverGeneratedApprovalPrompt(
168
172
  const taggedFallback = `${plainTextFallback}\n[ref:${uiMetadata.requestId}]`;
169
173
 
170
174
  try {
171
- await deliverChannelReply(
175
+ const fallbackResult = await deliverChannelReply(
172
176
  replyCallbackUrl,
173
177
  {
174
178
  chatId,
@@ -177,6 +181,9 @@ export async function deliverGeneratedApprovalPrompt(
177
181
  },
178
182
  bearerToken,
179
183
  );
184
+ if (fallbackResult.ts) {
185
+ trackApprovalPromptTs(sourceChannel, chatId, fallbackResult.ts);
186
+ }
180
187
  return true;
181
188
  } catch (err) {
182
189
  log.error(
@@ -197,7 +204,7 @@ export async function deliverGeneratedApprovalPrompt(
197
204
  const taggedPlainText = `${plainText}\n[ref:${uiMetadata.requestId}]`;
198
205
 
199
206
  try {
200
- await deliverChannelReply(
207
+ const plainResult = await deliverChannelReply(
201
208
  replyCallbackUrl,
202
209
  {
203
210
  chatId,
@@ -206,6 +213,9 @@ export async function deliverGeneratedApprovalPrompt(
206
213
  },
207
214
  bearerToken,
208
215
  );
216
+ if (plainResult.ts) {
217
+ trackApprovalPromptTs(sourceChannel, chatId, plainResult.ts);
218
+ }
209
219
  return true;
210
220
  } catch (err) {
211
221
  log.error(