@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
@@ -10,14 +10,43 @@ import { join, resolve } from "node:path";
10
10
 
11
11
  import { type ChannelId, parseInterfaceId } from "../channels/types.js";
12
12
  import { getAppDirPath, listAppFiles } from "../memory/app-store.js";
13
+ import {
14
+ getMessages as defaultGetMessages,
15
+ type MessageRow,
16
+ } from "../memory/conversation-crud.js";
17
+ import {
18
+ countMemoryPrefixBlocks,
19
+ extractMemoryPrefixBlocks,
20
+ } from "../memory/graph/conversation-graph-memory.js";
21
+ import { searchPkbFiles } from "../memory/pkb/pkb-search.js";
22
+ import type { QdrantSparseVector } from "../memory/qdrant-client.js";
23
+ import { readSlackMetadata } from "../messaging/providers/slack/message-metadata.js";
24
+ import {
25
+ extractTagLineTexts,
26
+ type RenderableSlackMessage,
27
+ renderSlackTranscript,
28
+ } from "../messaging/providers/slack/render-transcript.js";
13
29
  import { isPermissionControlsV2Enabled } from "../permissions/v2-consent-policy.js";
14
- import type { Message } from "../providers/types.js";
15
- import type { ActorTrustContext } from "../runtime/actor-trust-resolver.js";
30
+ import type { ContentBlock, Message } from "../providers/types.js";
31
+ import {
32
+ type ActorTrustContext,
33
+ isUntrustedTrustClass,
34
+ type TrustClass,
35
+ } from "../runtime/actor-trust-resolver.js";
16
36
  import { channelStatusToMemberStatus } from "../runtime/routes/inbound-stages/acl-enforcement.js";
17
37
  import type { SubagentState } from "../subagent/types.js";
18
38
  import { TERMINAL_STATUSES } from "../subagent/types.js";
39
+ import { getLogger } from "../util/logger.js";
19
40
  import { getWorkspaceDir, getWorkspacePromptPath } from "../util/platform.js";
20
41
  import { stripCommentLines } from "../util/strip-comment-lines.js";
42
+ import { filterMessagesForUntrustedActor } from "./conversation-lifecycle.js";
43
+ import {
44
+ getInContextPkbPaths,
45
+ type PkbContextConversation,
46
+ } from "./pkb-context-tracker.js";
47
+ import { buildPkbReminder } from "./pkb-reminder-builder.js";
48
+
49
+ const pkbReminderLog = getLogger("pkb-reminder");
21
50
 
22
51
  /**
23
52
  * Describes the capabilities of the channel through which the user is
@@ -557,7 +586,7 @@ export function injectNowScratchpad(
557
586
  ): Message {
558
587
  const scratchpadBlock = {
559
588
  type: "text" as const,
560
- text: `<NOW.md Always keep this up to date>\n${content}\n</NOW.md>`,
589
+ text: `<NOW.md Always keep this up to date; keep under 10 lines>\n${content}\n</NOW.md>`,
561
590
  };
562
591
 
563
592
  // Find insertion point: skip any leading injected-context text blocks
@@ -586,7 +615,9 @@ export function injectNowScratchpad(
586
615
  /** Strip `<NOW.md>` blocks injected by `injectNowScratchpad`. */
587
616
  export function stripNowScratchpad(messages: Message[]): Message[] {
588
617
  return stripUserTextBlocksByPrefix(messages, [
589
- "<NOW.md Always keep this up to date>",
618
+ // Shared prefix catches both the current tag and any pre-line-limit
619
+ // variant that may linger in in-flight histories during a rolling deploy.
620
+ "<NOW.md Always keep this up to date",
590
621
  "<now_scratchpad>", // backward-compat: strip legacy blocks from pre-rename history
591
622
  ]);
592
623
  }
@@ -607,13 +638,15 @@ const AUTOINJECT_FILENAME = "_autoinject.md";
607
638
  /** Max buffer.md lines injected into prompts — keeps context bounded even when filing is off. */
608
639
  const MAX_BUFFER_LINES = 50;
609
640
 
610
- const PKB_SYSTEM_REMINDER =
611
- "<system_reminder>" +
612
- "\n**CRITICAL:** you MUST read any PKB files that might be relevant to this conversation — " +
613
- "INDEX.md is your table of contents. Don't wait to be asked. " +
614
- "Use `remember` OFTEN for EVERY new fact you learn IMMEDIATELY, don't wait for the next turn. " +
615
- "Corrections to things you had wrong are the highest-priority remembers never skip them." +
616
- "\n</system_reminder>";
641
+ /** Minimum hybrid-search score for a PKB path to surface as an injection hint. */
642
+ const PKB_HINT_THRESHOLD = 0.5;
643
+
644
+ /**
645
+ * Stricter hint threshold for PKB entries under `archive/`. Archive files are
646
+ * date-indexed dumps of older notes they match loosely and are rarely the
647
+ * most relevant read, so require a higher bar before recommending them.
648
+ */
649
+ const PKB_HINT_ARCHIVE_THRESHOLD = 0.7;
617
650
 
618
651
  /**
619
652
  * Read `_autoinject.md` from the PKB directory and return the list of
@@ -639,6 +672,21 @@ export function readAutoinjectList(pkbDir: string): string[] | null {
639
672
  }
640
673
  }
641
674
 
675
+ /**
676
+ * Resolve the effective list of auto-inject filenames for a PKB directory.
677
+ *
678
+ * This is the single source of truth used both by `readPkbContext` (which
679
+ * actually injects the files) and by the PKB reminder-hint tracker in
680
+ * `conversation-agent-loop.ts` (which needs to know what's already in
681
+ * context so it doesn't redundantly recommend those files).
682
+ *
683
+ * Returns `PKB_DEFAULT_FILES` when `_autoinject.md` is missing/unreadable,
684
+ * or the parsed list (possibly empty) when it is present.
685
+ */
686
+ export function getPkbAutoInjectList(pkbRoot: string): string[] {
687
+ return readAutoinjectList(pkbRoot) ?? PKB_DEFAULT_FILES;
688
+ }
689
+
642
690
  /**
643
691
  * Read the always-loaded PKB files and append a nudge encouraging the
644
692
  * assistant to proactively read topic files and use `remember` aggressively.
@@ -653,7 +701,7 @@ export function readPkbContext(): string | null {
653
701
  const pkbDir = join(getWorkspaceDir(), "pkb");
654
702
  if (!existsSync(pkbDir)) return null;
655
703
 
656
- const filesToInject = readAutoinjectList(pkbDir) ?? PKB_DEFAULT_FILES;
704
+ const filesToInject = getPkbAutoInjectList(pkbDir);
657
705
 
658
706
  const parts: string[] = [];
659
707
  for (const file of filesToInject) {
@@ -686,10 +734,13 @@ export function readPkbContext(): string | null {
686
734
  */
687
735
  export function injectPkbContext(message: Message, content: string): Message {
688
736
  // Escape closing tags that could break out of the XML wrapper
689
- const escaped = content.replace(/<\/pkb\s*>/gi, "&lt;/pkb&gt;");
737
+ const escaped = content.replace(
738
+ /<\/knowledge_base\s*>/gi,
739
+ "&lt;/knowledge_base&gt;",
740
+ );
690
741
  const pkbBlock = {
691
742
  type: "text" as const,
692
- text: `<pkb>\n${escaped}\n</pkb>`,
743
+ text: `<knowledge_base>\n${escaped}\n</knowledge_base>`,
693
744
  };
694
745
 
695
746
  // Find insertion point: skip any leading memory/image blocks
@@ -721,9 +772,12 @@ export function injectPkbContext(message: Message, content: string): Message {
721
772
  };
722
773
  }
723
774
 
724
- /** Strip `<pkb>` blocks injected by `injectPkbContext`. */
775
+ /** Strip `<knowledge_base>` blocks injected by `injectPkbContext`. */
725
776
  export function stripPkbContext(messages: Message[]): Message[] {
726
- return stripUserTextBlocksByPrefix(messages, ["<pkb>"]);
777
+ return stripUserTextBlocksByPrefix(messages, [
778
+ "<knowledge_base>",
779
+ "<pkb>", // backward-compat: strip legacy blocks from pre-rename history
780
+ ]);
727
781
  }
728
782
 
729
783
  /**
@@ -909,6 +963,10 @@ export function buildUnifiedTurnContextBlock(
909
963
  .replace(/[\r\n\u0085\u2028\u2029]+/g, " ")
910
964
  // Replace remaining ASCII C0/C1 control characters and DEL.
911
965
  .replace(/[\x00-\x1F\x7F-\x9F]/g, " ")
966
+ // Escape XML special characters to prevent turn_context breakout.
967
+ .replace(/&/g, "&amp;")
968
+ .replace(/</g, "&lt;")
969
+ .replace(/>/g, "&gt;")
912
970
  .trim();
913
971
  return singleLine.length > 0 ? singleLine : "unknown";
914
972
  };
@@ -1138,6 +1196,433 @@ export function stripTransportHints(messages: Message[]): Message[] {
1138
1196
  return stripUserTextBlocksByPrefix(messages, ["<transport_hints>"]);
1139
1197
  }
1140
1198
 
1199
+ // ---------------------------------------------------------------------------
1200
+ // Slack chronological transcript assembly
1201
+ // ---------------------------------------------------------------------------
1202
+
1203
+ /**
1204
+ * True when the channel capabilities describe a Slack non-DM conversation
1205
+ * (group/channel/mpim). Used to gate thread-only behavior such as the
1206
+ * `<active_thread>` focus block. DMs are excluded because they have no
1207
+ * threads.
1208
+ *
1209
+ * The gateway normalizer sets `chatType: "channel"` for every non-DM Slack
1210
+ * conversation (public, private, and mpim alike — see
1211
+ * `gateway/src/slack/normalize.ts`) and omits the field entirely for DMs.
1212
+ * We therefore accept only `chatType === "channel"` — when the gateway
1213
+ * omits `chatType` (as it does for DMs), the check correctly returns
1214
+ * `false`.
1215
+ *
1216
+ * The chronological-transcript override applies to ALL Slack
1217
+ * conversations (channels and DMs) — gate that on
1218
+ * `channelCapabilities.channel === "slack"` rather than this helper.
1219
+ */
1220
+ export function isSlackChannelConversation(
1221
+ channelCapabilities?: ChannelCapabilities | null,
1222
+ ): boolean {
1223
+ return (
1224
+ channelCapabilities?.channel === "slack" &&
1225
+ channelCapabilities.chatType === "channel"
1226
+ );
1227
+ }
1228
+
1229
+ /**
1230
+ * Minimal structural shape of a persisted message row used by the Slack
1231
+ * chronological-transcript assembly path. Decouples the assembly logic from
1232
+ * the DB-row type so it can be unit-tested with plain literals.
1233
+ */
1234
+ export interface SlackTranscriptInputRow {
1235
+ role: "user" | "assistant";
1236
+ /** Raw persisted content column. JSON-encoded `ContentBlock[]` in production. */
1237
+ content: string;
1238
+ /** Epoch ms when the row was created. */
1239
+ createdAt: number;
1240
+ /** Raw `metadata` column value (JSON string with optional `slackMeta` sub-key). */
1241
+ metadata: string | null;
1242
+ }
1243
+
1244
+ /**
1245
+ * Extract the user-facing plain text from an already-parsed `ContentBlock[]`.
1246
+ * Only `text` blocks contribute to the rendered transcript line. Tool-use /
1247
+ * tool-result / thinking blocks are intentionally elided — they would clutter
1248
+ * the Slack-style transcript and the model can already recall them from the
1249
+ * surrounding turn structure.
1250
+ *
1251
+ * Rows with no text blocks (e.g. images, file uploads, pure tool turns) would
1252
+ * otherwise render as an empty transcript line like `[14:25 @alice]: `;
1253
+ * surface the attachment/tool context instead so the model can tell something
1254
+ * was actually said on that turn.
1255
+ */
1256
+ function extractPlainTextFromBlocks(blocks: ContentBlock[]): string {
1257
+ const parts: string[] = [];
1258
+ const placeholderLabels: string[] = [];
1259
+ for (const block of blocks) {
1260
+ if (!block || typeof block !== "object") continue;
1261
+ if (block.type === "text") {
1262
+ parts.push(block.text);
1263
+ continue;
1264
+ }
1265
+ const label = placeholderForBlockType(block.type);
1266
+ if (label && !placeholderLabels.includes(label)) {
1267
+ placeholderLabels.push(label);
1268
+ }
1269
+ }
1270
+ if (parts.length > 0) {
1271
+ return parts.join("\n");
1272
+ }
1273
+ return placeholderLabels.join(" ");
1274
+ }
1275
+
1276
+ function placeholderForBlockType(type: ContentBlock["type"]): string | null {
1277
+ switch (type) {
1278
+ case "image":
1279
+ return "[image]";
1280
+ case "file":
1281
+ return "[file]";
1282
+ case "tool_use":
1283
+ case "server_tool_use":
1284
+ return "[tool call]";
1285
+ case "tool_result":
1286
+ case "web_search_tool_result":
1287
+ return "[tool result]";
1288
+ case "thinking":
1289
+ case "redacted_thinking":
1290
+ case "text":
1291
+ return null;
1292
+ }
1293
+ }
1294
+
1295
+ /**
1296
+ * Convert a persisted row into the {@link RenderableSlackMessage} shape
1297
+ * consumed by `renderSlackTranscript`.
1298
+ *
1299
+ * Legacy pre-upgrade rows (no `slackMeta` sub-key, malformed metadata, etc.)
1300
+ * yield `metadata: null`; the renderer then takes its flat-render fallback
1301
+ * path and the row stays in chronological order via `createdAt`.
1302
+ *
1303
+ * Sender labels are emitted only when they add information beyond the role
1304
+ * slot:
1305
+ * - Reaction rows: always labeled — `@assistant` for the assistant, the real
1306
+ * `slackMeta.displayName` for a known user, or `@user` as a last-resort
1307
+ * subject so the rendered `[time X reacted ...]` line still parses.
1308
+ * - Assistant message rows: `null` — the role slot already says "assistant".
1309
+ * - User message rows: real `slackMeta.displayName` when available (to
1310
+ * disambiguate speakers in multi-party channels); `null` otherwise so the
1311
+ * renderer drops the redundant `@user` placeholder.
1312
+ */
1313
+ function rowToRenderable(row: SlackTranscriptInputRow): RenderableSlackMessage {
1314
+ let slackMeta: ReturnType<typeof readSlackMetadata> = null;
1315
+ if (row.metadata) {
1316
+ try {
1317
+ const outer = JSON.parse(row.metadata) as { slackMeta?: unknown };
1318
+ if (typeof outer.slackMeta === "string") {
1319
+ slackMeta = readSlackMetadata(outer.slackMeta);
1320
+ }
1321
+ } catch {
1322
+ // Malformed metadata — fall through to legacy/null treatment.
1323
+ }
1324
+ }
1325
+
1326
+ const isReaction = slackMeta?.eventKind === "reaction";
1327
+ let senderLabel: string | null;
1328
+ if (isReaction) {
1329
+ senderLabel =
1330
+ row.role === "assistant"
1331
+ ? "@assistant"
1332
+ : (slackMeta?.displayName ?? "@user");
1333
+ } else if (row.role === "assistant") {
1334
+ senderLabel = null;
1335
+ } else {
1336
+ senderLabel = slackMeta?.displayName ?? null;
1337
+ }
1338
+
1339
+ // Parse `row.content` once and derive both the structured `contentBlocks`
1340
+ // view (for downstream tool-block preservation) and the flattened
1341
+ // `plainText` view (used for tag-line rendering) from the same parsed
1342
+ // result. Large Slack histories with many tool payloads would otherwise
1343
+ // pay a double JSON-parse cost per row.
1344
+ let contentBlocks: ContentBlock[] = [];
1345
+ let plainText: string;
1346
+ try {
1347
+ const parsed = JSON.parse(row.content);
1348
+ if (Array.isArray(parsed)) {
1349
+ contentBlocks = parsed as ContentBlock[];
1350
+ plainText = extractPlainTextFromBlocks(contentBlocks);
1351
+ } else if (typeof parsed === "string") {
1352
+ plainText = parsed;
1353
+ } else {
1354
+ plainText = row.content;
1355
+ }
1356
+ } catch {
1357
+ // Plain string row (legacy) — no structured blocks to preserve.
1358
+ plainText = row.content;
1359
+ }
1360
+
1361
+ // Attachment-only rows (images, files) carry no text block, so the
1362
+ // transcript renderer would normally emit them *without* a tag line —
1363
+ // the model sees the image but loses sender/timestamp attribution.
1364
+ // Synthesize a leading text block carrying the placeholder so the
1365
+ // renderer emits `[14:25 @alice]: [image]` and then the image itself.
1366
+ // Pure tool-only rows (tool_use / tool_result) are intentionally
1367
+ // excluded — those are synthetic turn continuations that should stay
1368
+ // tag-line-free, matching the documented behaviour in
1369
+ // `buildMessageContentBlocks`.
1370
+ const hasTextBlock = contentBlocks.some((b) => b?.type === "text");
1371
+ const hasAttachmentBlock = contentBlocks.some(
1372
+ (b) => b?.type === "image" || b?.type === "file",
1373
+ );
1374
+ if (!hasTextBlock && hasAttachmentBlock && plainText !== "") {
1375
+ contentBlocks = [{ type: "text", text: plainText }, ...contentBlocks];
1376
+ }
1377
+
1378
+ return {
1379
+ role: row.role,
1380
+ content: plainText,
1381
+ metadata: slackMeta,
1382
+ senderLabel,
1383
+ createdAt: row.createdAt,
1384
+ contentBlocks,
1385
+ };
1386
+ }
1387
+
1388
+ /**
1389
+ * Build a chronological Slack transcript for Slack conversations (both DMs
1390
+ * and group/channel/mpim) and project it onto the LLM-facing `Message[]`
1391
+ * shape.
1392
+ *
1393
+ * Returns `null` when the channel is not Slack (caller should fall through
1394
+ * to the default message history). Legacy pre-upgrade rows without
1395
+ * `slackMeta` are tolerated: the renderer's flat fallback orders them by
1396
+ * `createdAt` alongside post-upgrade rows.
1397
+ *
1398
+ * For ALL Slack conversations (channels and DMs), `<transport_hints>`
1399
+ * injection is suppressed by `applyRuntimeInjections` so the model sees
1400
+ * one consistent persisted view instead of a duplicated gateway hint.
1401
+ */
1402
+ export function assembleSlackChronologicalMessages(
1403
+ rows: SlackTranscriptInputRow[],
1404
+ capabilities: ChannelCapabilities,
1405
+ ): Message[] | null {
1406
+ if (capabilities.channel !== "slack") {
1407
+ return null;
1408
+ }
1409
+ const renderable = rows.map(rowToRenderable);
1410
+ return renderSlackTranscript(renderable);
1411
+ }
1412
+
1413
+ /**
1414
+ * Load DB rows for a Slack conversation and project them onto the
1415
+ * chronological transcript shape.
1416
+ *
1417
+ * Convenience wrapper over `getMessages` + `assembleSlackChronologicalMessages`.
1418
+ * The loader is exposed as a parameter so tests can substitute a stub. In
1419
+ * production it defaults to `getMessages` from `conversation-crud.ts`.
1420
+ *
1421
+ * When `trustClass` identifies an untrusted actor (guardian-scoped rows
1422
+ * must not leak into the model context), rows are passed through
1423
+ * `filterMessagesForUntrustedActor` before assembly — mirroring the
1424
+ * filtering applied in `loadFromDb` so the chronological transcript
1425
+ * respects the same per-actor scoping as the default history path.
1426
+ *
1427
+ * Returns `null` when the channel is not Slack — callers should fall
1428
+ * through to the default in-memory message history.
1429
+ */
1430
+ export function loadSlackChronologicalMessages(
1431
+ conversationId: string,
1432
+ capabilities: ChannelCapabilities,
1433
+ options: {
1434
+ loader?: (id: string) => MessageRow[];
1435
+ trustClass?: TrustClass;
1436
+ } = {},
1437
+ ): Message[] | null {
1438
+ if (capabilities.channel !== "slack") {
1439
+ return null;
1440
+ }
1441
+ const loader = options.loader ?? defaultGetMessages;
1442
+ const allRows = loader(conversationId);
1443
+ const scopedRows = isUntrustedTrustClass(options.trustClass)
1444
+ ? filterMessagesForUntrustedActor(allRows)
1445
+ : allRows;
1446
+ // Coerce MessageRow.role (string) to the structural row's stricter union.
1447
+ const rows: SlackTranscriptInputRow[] = scopedRows.map((row) => ({
1448
+ role: row.role === "assistant" ? "assistant" : "user",
1449
+ content: row.content,
1450
+ createdAt: row.createdAt,
1451
+ metadata: row.metadata,
1452
+ }));
1453
+ return assembleSlackChronologicalMessages(rows, capabilities);
1454
+ }
1455
+
1456
+ // ---------------------------------------------------------------------------
1457
+ // Active-thread focus block (non-persisted; appended to current user turn)
1458
+ // ---------------------------------------------------------------------------
1459
+
1460
+ /**
1461
+ * Detect the "active" Slack thread ts for the current turn.
1462
+ *
1463
+ * The active thread is the thread the current inbound user message belongs
1464
+ * to: scan from newest to oldest and return the `slackMeta.threadTs` of the
1465
+ * most recent user row that carries one. Returns `null` when no recent user
1466
+ * row sits inside a thread (e.g. the inbound was a top-level channel post,
1467
+ * or the conversation has no Slack-tagged user rows yet).
1468
+ *
1469
+ * Pure: takes pre-mapped renderable rows and returns the ts string only.
1470
+ */
1471
+ function detectActiveThreadTs(rows: RenderableSlackMessage[]): string | null {
1472
+ for (let i = rows.length - 1; i >= 0; i--) {
1473
+ const row = rows[i];
1474
+ if (row.role !== "user") continue;
1475
+ const meta = row.metadata;
1476
+ if (!meta) continue;
1477
+ if (meta.eventKind !== "message") continue;
1478
+ if (typeof meta.threadTs === "string" && meta.threadTs.length > 0) {
1479
+ return meta.threadTs;
1480
+ }
1481
+ // First non-thread user row wins: the inbound is top-level, no active
1482
+ // thread to focus on.
1483
+ return null;
1484
+ }
1485
+ return null;
1486
+ }
1487
+
1488
+ /**
1489
+ * Build a focus block listing every message belonging to the active thread:
1490
+ * the parent (whose `channelTs` equals `activeThreadTs`) plus every reply
1491
+ * (whose `threadTs` equals `activeThreadTs`). Reactions targeting any of
1492
+ * those messages are also pulled in via their `targetChannelTs`. Edits and
1493
+ * deletions surface through the existing renderer markers.
1494
+ *
1495
+ * Returns `null` when no rows match (e.g. parent backfill hasn't run yet
1496
+ * AND the thread has no replies in storage either) so the caller can skip
1497
+ * the empty block. Otherwise returns the rendered XML block ready to append
1498
+ * to the user's tail message.
1499
+ *
1500
+ * Pure: takes pre-mapped renderable rows + a thread ts, returns text only.
1501
+ */
1502
+ function buildActiveThreadBlockFromRenderable(
1503
+ rows: RenderableSlackMessage[],
1504
+ activeThreadTs: string,
1505
+ ): string | null {
1506
+ const members: RenderableSlackMessage[] = [];
1507
+ for (const row of rows) {
1508
+ const meta = row.metadata;
1509
+ if (!meta) continue;
1510
+ if (meta.eventKind === "message") {
1511
+ if (
1512
+ meta.channelTs === activeThreadTs ||
1513
+ meta.threadTs === activeThreadTs
1514
+ ) {
1515
+ members.push(row);
1516
+ }
1517
+ continue;
1518
+ }
1519
+ if (
1520
+ meta.eventKind === "reaction" &&
1521
+ meta.reaction &&
1522
+ meta.reaction.targetChannelTs === activeThreadTs
1523
+ ) {
1524
+ members.push(row);
1525
+ continue;
1526
+ }
1527
+ // Reactions targeting a reply within the thread also belong in the
1528
+ // focus block — collect them by checking the reaction target against
1529
+ // any thread reply's channelTs we've already accepted. We do this in a
1530
+ // second pass below to avoid an O(n^2) inner scan here.
1531
+ }
1532
+
1533
+ // Second pass: pull in reactions whose target is one of the already-
1534
+ // collected reply messages. Using a Set keeps this O(n).
1535
+ const memberChannelTs = new Set(
1536
+ members
1537
+ .map((m) => m.metadata?.channelTs)
1538
+ .filter((v): v is string => typeof v === "string"),
1539
+ );
1540
+ for (const row of rows) {
1541
+ const meta = row.metadata;
1542
+ if (!meta || meta.eventKind !== "reaction" || !meta.reaction) continue;
1543
+ if (meta.reaction.targetChannelTs === activeThreadTs) continue; // already added
1544
+ if (memberChannelTs.has(meta.reaction.targetChannelTs)) {
1545
+ members.push(row);
1546
+ }
1547
+ }
1548
+
1549
+ if (members.length === 0) return null;
1550
+
1551
+ // The active-thread block is flattened to plain text below, which discards
1552
+ // `Message.role`. Force a role-derived sender label on any member whose
1553
+ // `rowToRenderable` emitted `null` (assistant rows, user rows without a
1554
+ // real Slack displayName) so speaker attribution survives the flattening.
1555
+ const labeledMembers = members.map((m) =>
1556
+ m.senderLabel
1557
+ ? m
1558
+ : {
1559
+ ...m,
1560
+ senderLabel: m.role === "assistant" ? "@assistant" : "@user",
1561
+ },
1562
+ );
1563
+
1564
+ const rendered = renderSlackTranscript(labeledMembers);
1565
+ if (rendered.length === 0) return null;
1566
+ const lines = extractTagLineTexts(rendered).join("\n");
1567
+ return `<active_thread>\n${lines}\n</active_thread>`;
1568
+ }
1569
+
1570
+ /**
1571
+ * Build the Slack active-thread focus block from raw rows.
1572
+ *
1573
+ * Pure assembly entrypoint mirroring `assembleSlackChronologicalMessages`.
1574
+ * Returns the rendered `<active_thread>` block as a string, or `null` when:
1575
+ * - the channel is not Slack, OR
1576
+ * - the channel is a Slack DM (DMs do not have threads), OR
1577
+ * - the latest user row is top-level (not in a thread), OR
1578
+ * - no rows belong to the active thread.
1579
+ */
1580
+ export function assembleSlackActiveThreadFocusBlock(
1581
+ rows: SlackTranscriptInputRow[],
1582
+ capabilities: ChannelCapabilities,
1583
+ ): string | null {
1584
+ if (capabilities.channel !== "slack") return null;
1585
+ // DMs do not have threads, so the focus block is always a no-op.
1586
+ // The gateway sets `chatType: "channel"` for every non-DM Slack
1587
+ // conversation and omits the field for DMs, so gate the focus block
1588
+ // on the positive `"channel"` match.
1589
+ if (capabilities.chatType !== "channel") return null;
1590
+ const renderable = rows.map(rowToRenderable);
1591
+ const activeThreadTs = detectActiveThreadTs(renderable);
1592
+ if (!activeThreadTs) return null;
1593
+ return buildActiveThreadBlockFromRenderable(renderable, activeThreadTs);
1594
+ }
1595
+
1596
+ /**
1597
+ * Loader convenience over `assembleSlackActiveThreadFocusBlock` mirroring
1598
+ * `loadSlackChronologicalMessages`. Returns `null` when the channel is not
1599
+ * Slack, or when it is a Slack DM (DMs have no threads), so callers can
1600
+ * skip the injection entirely without paying for a DB read.
1601
+ */
1602
+ export function loadSlackActiveThreadFocusBlock(
1603
+ conversationId: string,
1604
+ capabilities: ChannelCapabilities,
1605
+ options: {
1606
+ loader?: (id: string) => MessageRow[];
1607
+ trustClass?: TrustClass;
1608
+ } = {},
1609
+ ): string | null {
1610
+ if (capabilities.channel !== "slack") return null;
1611
+ if (capabilities.chatType !== "channel") return null;
1612
+ const loader = options.loader ?? defaultGetMessages;
1613
+ const allRows = loader(conversationId);
1614
+ const scopedRows = isUntrustedTrustClass(options.trustClass)
1615
+ ? filterMessagesForUntrustedActor(allRows)
1616
+ : allRows;
1617
+ const rows: SlackTranscriptInputRow[] = scopedRows.map((row) => ({
1618
+ role: row.role === "assistant" ? "assistant" : "user",
1619
+ content: row.content,
1620
+ createdAt: row.createdAt,
1621
+ metadata: row.metadata,
1622
+ }));
1623
+ return assembleSlackActiveThreadFocusBlock(rows, capabilities);
1624
+ }
1625
+
1141
1626
  /** Prefixes stripped by the pipeline (order doesn't matter — single pass). */
1142
1627
  const RUNTIME_INJECTION_PREFIXES = [
1143
1628
  "<channel_capabilities>",
@@ -1164,11 +1649,18 @@ const RUNTIME_INJECTION_PREFIXES = [
1164
1649
  "<active_workspace>",
1165
1650
  "<active_dynamic_page>",
1166
1651
  "<non_interactive_context>",
1167
- "<NOW.md Always keep this up to date>",
1652
+ // Shared prefix catches both the current NOW.md tag and any pre-line-limit
1653
+ // variant that may linger in in-flight histories during a rolling deploy.
1654
+ "<NOW.md Always keep this up to date",
1168
1655
  "<now_scratchpad>", // backward-compat: strip legacy blocks from pre-rename history
1169
- "<pkb>",
1656
+ "<knowledge_base>",
1657
+ "<pkb>", // backward-compat: strip legacy tag from pre-rename history
1170
1658
  "<system_reminder>",
1171
1659
  "<transport_hints>",
1660
+ // The Slack active-thread focus block is non-persisted and injected on
1661
+ // the FINAL user turn only. Strip it here so re-assembly during compaction
1662
+ // and overflow recovery does not duplicate it across turns.
1663
+ "<active_thread>",
1172
1664
  "<system_notice>One or more tool calls returned an error.",
1173
1665
  ];
1174
1666
 
@@ -1189,16 +1681,23 @@ export function stripInjectionsForCompaction(messages: Message[]): Message[] {
1189
1681
  * Returns null if no NOW.md injection is found.
1190
1682
  */
1191
1683
  export function findLastInjectedNowContent(messages: Message[]): string | null {
1192
- const prefix = "<NOW.md Always keep this up to date>\n";
1684
+ // Matches every NOW.md opening tag we emit (the tag text may evolve over
1685
+ // time, e.g. adding a line-limit hint), so in-flight histories with older
1686
+ // tag variants remain discoverable during a rolling deploy.
1687
+ const openTagPrefix = "<NOW.md Always keep this up to date";
1193
1688
  const suffix = "\n</NOW.md>";
1194
1689
  for (let i = messages.length - 1; i >= 0; i--) {
1195
1690
  const msg = messages[i];
1196
1691
  if (msg.role !== "user") continue;
1197
1692
  for (const block of msg.content) {
1198
- if (block.type === "text" && block.text.startsWith(prefix)) {
1199
- const end = block.text.lastIndexOf(suffix);
1200
- if (end > prefix.length) return block.text.slice(prefix.length, end);
1693
+ if (block.type !== "text" || !block.text.startsWith(openTagPrefix)) {
1694
+ continue;
1201
1695
  }
1696
+ const tagEnd = block.text.indexOf(">\n");
1697
+ if (tagEnd < 0) continue;
1698
+ const contentStart = tagEnd + ">\n".length;
1699
+ const end = block.text.lastIndexOf(suffix);
1700
+ if (end > contentStart) return block.text.slice(contentStart, end);
1202
1701
  }
1203
1702
  }
1204
1703
  return null;
@@ -1215,13 +1714,29 @@ export function findLastInjectedNowContent(messages: Message[]): string | null {
1215
1714
  */
1216
1715
  export type InjectionMode = "full" | "minimal";
1217
1716
 
1717
+ /**
1718
+ * Per-turn injection bytes captured for later persistence to message
1719
+ * metadata. Empty in this PR — later PRs capture `<turn_context>` and
1720
+ * `<system_reminder>` bodies so they survive daemon restarts.
1721
+ */
1722
+ export interface RuntimeInjectionBlocks {
1723
+ unifiedTurnContext?: string;
1724
+ pkbSystemReminder?: string;
1725
+ }
1726
+
1727
+ export interface RuntimeInjectionResult {
1728
+ messages: Message[];
1729
+ blocks: RuntimeInjectionBlocks;
1730
+ }
1731
+
1218
1732
  /**
1219
1733
  * Apply a chain of user-message injections to `runMessages`.
1220
1734
  *
1221
1735
  * Each injection is optional — pass `null`/`undefined` to skip it.
1222
- * Returns the final message array ready for the provider.
1736
+ * Returns the final message array ready for the provider, along with a
1737
+ * `blocks` object reserved for captured injection bytes (currently empty).
1223
1738
  */
1224
- export function applyRuntimeInjections(
1739
+ export async function applyRuntimeInjections(
1225
1740
  runMessages: Message[],
1226
1741
  options: {
1227
1742
  activeSurface?: ActiveSurfaceContext | null;
@@ -1232,15 +1747,114 @@ export function applyRuntimeInjections(
1232
1747
  voiceCallControlPrompt?: string | null;
1233
1748
  pkbContext?: string | null;
1234
1749
  pkbActive?: boolean;
1750
+ /**
1751
+ * Dense query vector surfaced from the graph memory retriever (PR 3).
1752
+ * When present together with `pkbActive`, used to run `searchPkbFiles`
1753
+ * to surface relevance hints in the PKB system reminder. When missing,
1754
+ * the reminder falls back to the flat static text.
1755
+ */
1756
+ pkbQueryVector?: number[];
1757
+ /** Optional sparse vector accompanying `pkbQueryVector`. */
1758
+ pkbSparseVector?: QdrantSparseVector;
1759
+ /** Memory scope id used to filter PKB search results. */
1760
+ pkbScopeId?: string;
1761
+ /**
1762
+ * The live conversation (or a minimal shape containing `messages`) used
1763
+ * to compute which PKB paths are already "in context" and therefore
1764
+ * suppressed from hint suggestions.
1765
+ */
1766
+ pkbConversation?: PkbContextConversation;
1767
+ /** Auto-injected PKB filenames (resolved relative to `pkbRoot`). */
1768
+ pkbAutoInjectList?: string[];
1769
+ /** Absolute path to the PKB directory (e.g. `<workspace>/pkb`). */
1770
+ pkbRoot?: string;
1771
+ /**
1772
+ * Working directory against which relative `file_read` tool paths
1773
+ * resolve, used to detect workspace-relative reads like
1774
+ * `pkb/threads.md`. Falls back to `pkbRoot` when omitted.
1775
+ */
1776
+ pkbWorkingDir?: string;
1235
1777
  nowScratchpad?: string | null;
1236
1778
  subagentStatusBlock?: string | null;
1237
1779
  isNonInteractive?: boolean;
1238
1780
  transportHints?: string[] | null;
1781
+ /**
1782
+ * Pre-rendered Slack chronological transcript that replaces the
1783
+ * default `runMessages` history for any Slack conversation (channels
1784
+ * and DMs alike).
1785
+ *
1786
+ * When `channelCapabilities` describes a Slack conversation and this
1787
+ * array is non-empty, it overrides `runMessages` so the model sees one
1788
+ * chronologically-ordered transcript built from the stored Slack
1789
+ * metadata. Channel renders include sibling-thread tags; DM renders
1790
+ * are flat (DMs have no threads). The `transportHints` pipeline is
1791
+ * skipped for any Slack conversation so the persisted view isn't
1792
+ * duplicated by gateway-side hints.
1793
+ *
1794
+ * Callers build this via `loadSlackChronologicalMessages` (or the
1795
+ * underlying `assembleSlackChronologicalMessages`) before invoking
1796
+ * this function so the assembly path stays free of direct DB calls
1797
+ * and remains easy to test.
1798
+ */
1799
+ slackChronologicalMessages?: Message[] | null;
1800
+ /**
1801
+ * Pre-rendered `<active_thread>` focus block listing the messages of
1802
+ * the thread the current inbound user message belongs to.
1803
+ *
1804
+ * Appended (tail-block) to the FINAL user message ONLY when
1805
+ * `channelCapabilities` describes a Slack non-DM channel. The block is
1806
+ * non-persisted: history rebuilds re-derive it from storage on each
1807
+ * turn, and `RUNTIME_INJECTION_PREFIXES` strips any `<active_thread>`
1808
+ * blocks from prior turns so they do not accumulate.
1809
+ *
1810
+ * Callers build this via `loadSlackActiveThreadFocusBlock` (or the
1811
+ * underlying `assembleSlackActiveThreadFocusBlock`). Pass `null` /
1812
+ * `undefined` when the inbound is a top-level (non-thread) post.
1813
+ */
1814
+ slackActiveThreadFocusBlock?: string | null;
1239
1815
  mode?: InjectionMode;
1240
1816
  },
1241
- ): Message[] {
1817
+ ): Promise<RuntimeInjectionResult> {
1242
1818
  const mode = options.mode ?? "full";
1819
+ let pkbSystemReminderCaptured: string | undefined;
1820
+ const slackChannel = isSlackChannelConversation(options.channelCapabilities);
1821
+ // Slack DMs and channels both assemble context from persisted message
1822
+ // rows, so suppress hint injection for any Slack conversation. Other
1823
+ // channels (telegram, email, etc.) keep the generic hint pipeline.
1824
+ const slackConversation = options.channelCapabilities?.channel === "slack";
1825
+ let turnContextCaptured: string | undefined;
1243
1826
  let result = runMessages;
1827
+ // Slack channels AND DMs both override `runMessages` with a pre-rendered
1828
+ // chronological transcript built from persisted message rows. The shared
1829
+ // assembler (`assembleSlackChronologicalMessages`) renders thread tags
1830
+ // for channels and a flat sequence for DMs, so the same branch handles
1831
+ // both. The active-thread focus block below stays gated on `slackChannel`
1832
+ // since DMs do not have threads.
1833
+ if (
1834
+ slackConversation &&
1835
+ options.slackChronologicalMessages &&
1836
+ options.slackChronologicalMessages.length > 0
1837
+ ) {
1838
+ // `graphMemory.prepareMemory` prepends a `<memory __injected>` block
1839
+ // (and any memory-image groups) to the last user message before
1840
+ // runtime assembly runs. The Slack transcript is freshly rendered
1841
+ // from persisted rows and has no such prefix, so swap it in and then
1842
+ // re-prepend the captured prefix onto the new tail user message.
1843
+ const carriedMemoryBlocks = extractMemoryPrefixBlocks(runMessages);
1844
+ result = options.slackChronologicalMessages;
1845
+ if (carriedMemoryBlocks.length > 0) {
1846
+ const slackTail = result[result.length - 1];
1847
+ if (slackTail && slackTail.role === "user") {
1848
+ result = [
1849
+ ...result.slice(0, -1),
1850
+ {
1851
+ ...slackTail,
1852
+ content: [...carriedMemoryBlocks, ...slackTail.content],
1853
+ },
1854
+ ];
1855
+ }
1856
+ }
1857
+ }
1244
1858
 
1245
1859
  // For non-interactive conversations (scheduled jobs, work items), instruct the
1246
1860
  // model to never ask for clarification — there is no human present to answer.
@@ -1284,17 +1898,90 @@ export function applyRuntimeInjections(
1284
1898
  }
1285
1899
 
1286
1900
  // PKB behavioral nudge — injected on every turn when PKB is active so
1287
- // the model keeps reading topic files and calling `remember`.
1901
+ // the model keeps reading topic files and calling `remember`. When a
1902
+ // query vector is available from the graph memory retriever, run a
1903
+ // hybrid PKB search to surface up to three relevance hints; fall back
1904
+ // to the flat static reminder on empty results or any error.
1288
1905
  if (mode === "full" && options.pkbActive) {
1289
1906
  const userTail = result[result.length - 1];
1290
1907
  if (userTail && userTail.role === "user") {
1908
+ let hints: string[] = [];
1909
+ const queryVector = options.pkbQueryVector;
1910
+ if (
1911
+ queryVector &&
1912
+ queryVector.length > 0 &&
1913
+ options.pkbScopeId &&
1914
+ options.pkbConversation &&
1915
+ options.pkbRoot
1916
+ ) {
1917
+ try {
1918
+ const results = await searchPkbFiles(
1919
+ queryVector,
1920
+ options.pkbSparseVector,
1921
+ 8,
1922
+ [options.pkbScopeId],
1923
+ );
1924
+ const workingDir = options.pkbWorkingDir ?? options.pkbRoot;
1925
+ const inContext = getInContextPkbPaths(
1926
+ options.pkbConversation,
1927
+ options.pkbAutoInjectList ?? [],
1928
+ options.pkbRoot,
1929
+ workingDir,
1930
+ );
1931
+ const pkbRoot = options.pkbRoot;
1932
+ // Gate on `denseScore` (cosine, [0, 1]) so the quality bar is stable
1933
+ // regardless of whether sparse was provided. Rank by `hybridScore`
1934
+ // (RRF) when available — that captures the sparse signal for
1935
+ // re-ordering eligible hits. hybridScore and denseScore live on
1936
+ // different scales, so items with hybridScore are ordered together
1937
+ // and placed ahead of items that only have denseScore.
1938
+ hints = results
1939
+ .filter((r) => {
1940
+ const abs = resolve(pkbRoot, r.path);
1941
+ if (inContext.has(abs)) return false;
1942
+ const threshold = r.path
1943
+ .replace(/\\/g, "/")
1944
+ .startsWith("archive/")
1945
+ ? PKB_HINT_ARCHIVE_THRESHOLD
1946
+ : PKB_HINT_THRESHOLD;
1947
+ return r.denseScore >= threshold;
1948
+ })
1949
+ .sort((a, b) => {
1950
+ const aHasHybrid = a.hybridScore !== undefined;
1951
+ const bHasHybrid = b.hybridScore !== undefined;
1952
+ if (aHasHybrid && !bHasHybrid) return -1;
1953
+ if (!aHasHybrid && bHasHybrid) return 1;
1954
+ if (aHasHybrid && bHasHybrid) {
1955
+ return b.hybridScore! - a.hybridScore!;
1956
+ }
1957
+ return b.denseScore - a.denseScore;
1958
+ })
1959
+ .slice(0, 3)
1960
+ .map((r) => r.path);
1961
+ } catch (err) {
1962
+ pkbReminderLog.warn(
1963
+ { err: err instanceof Error ? err.message : String(err) },
1964
+ "PKB hint search failed — falling back to flat reminder",
1965
+ );
1966
+ hints = [];
1967
+ }
1968
+ }
1969
+
1970
+ const reminder = buildPkbReminder(hints);
1971
+ pkbSystemReminderCaptured = reminder;
1972
+ // Splice the reminder in right after the memory prefix blocks so it
1973
+ // lands above the user's typed text, producing the tail shape
1974
+ // `[<turn_context>, <memory __injected>, <system_reminder>, ...your_text, ...later_appends]`
1975
+ // after `unifiedTurnContext` later prepends `<turn_context>` at index 0.
1976
+ const memoryPrefixCount = countMemoryPrefixBlocks(userTail.content);
1291
1977
  result = [
1292
1978
  ...result.slice(0, -1),
1293
1979
  {
1294
1980
  ...userTail,
1295
1981
  content: [
1296
- ...userTail.content,
1297
- { type: "text" as const, text: PKB_SYSTEM_REMINDER },
1982
+ ...userTail.content.slice(0, memoryPrefixCount),
1983
+ { type: "text" as const, text: reminder },
1984
+ ...userTail.content.slice(memoryPrefixCount),
1298
1985
  ],
1299
1986
  },
1300
1987
  ];
@@ -1354,6 +2041,7 @@ export function applyRuntimeInjections(
1354
2041
  if (options.unifiedTurnContext) {
1355
2042
  const userTail = result[result.length - 1];
1356
2043
  if (userTail && userTail.role === "user") {
2044
+ turnContextCaptured = options.unifiedTurnContext;
1357
2045
  result = [
1358
2046
  ...result.slice(0, -1),
1359
2047
  {
@@ -1367,8 +2055,15 @@ export function applyRuntimeInjections(
1367
2055
  }
1368
2056
  }
1369
2057
 
2058
+ // Slack conversations (both channels and DMs) build their own
2059
+ // chronological transcript from persisted messages and intentionally do
2060
+ // not receive the per-turn `<transport_hints>` block — the rendered
2061
+ // history already covers the active thread / DM, so duplicating it
2062
+ // would confuse the model. Other channels (telegram, email, etc.) keep
2063
+ // the existing injection.
1370
2064
  if (
1371
2065
  mode === "full" &&
2066
+ !slackConversation &&
1372
2067
  options.transportHints &&
1373
2068
  options.transportHints.length > 0
1374
2069
  ) {
@@ -1381,6 +2076,36 @@ export function applyRuntimeInjections(
1381
2076
  }
1382
2077
  }
1383
2078
 
2079
+ // Slack active-thread focus block: when the inbound user message lives
2080
+ // inside a thread, append a non-persisted `<active_thread>` tail block
2081
+ // listing that thread's parent + replies so the model can orient even
2082
+ // when the channel-wide chronological transcript is long and
2083
+ // interleaved. Stripped on subsequent rebuilds via the
2084
+ // `RUNTIME_INJECTION_PREFIXES` list so focus blocks never accumulate.
2085
+ if (
2086
+ mode === "full" &&
2087
+ slackChannel &&
2088
+ typeof options.slackActiveThreadFocusBlock === "string" &&
2089
+ options.slackActiveThreadFocusBlock.length > 0
2090
+ ) {
2091
+ const userTail = result[result.length - 1];
2092
+ if (userTail && userTail.role === "user") {
2093
+ result = [
2094
+ ...result.slice(0, -1),
2095
+ {
2096
+ ...userTail,
2097
+ content: [
2098
+ ...userTail.content,
2099
+ {
2100
+ type: "text" as const,
2101
+ text: options.slackActiveThreadFocusBlock,
2102
+ },
2103
+ ],
2104
+ },
2105
+ ];
2106
+ }
2107
+ }
2108
+
1384
2109
  // Workspace top-level context is injected last so it appears first
1385
2110
  // (prepended) in the user message content, keeping cache breakpoints
1386
2111
  // anchored to the trailing blocks.
@@ -1397,5 +2122,11 @@ export function applyRuntimeInjections(
1397
2122
  }
1398
2123
  }
1399
2124
 
1400
- return result;
2125
+ return {
2126
+ messages: result,
2127
+ blocks: {
2128
+ unifiedTurnContext: turnContextCaptured,
2129
+ pkbSystemReminder: pkbSystemReminderCaptured,
2130
+ },
2131
+ };
1401
2132
  }