@vellumai/assistant 0.6.3 → 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 (1114) hide show
  1. package/.prettierignore +5 -0
  2. package/ARCHITECTURE.md +298 -39
  3. package/Dockerfile +14 -3
  4. package/README.md +3 -4
  5. package/bun.lock +13 -16
  6. package/docs/architecture/integrations.md +1 -20
  7. package/docs/architecture/security.md +16 -16
  8. package/docs/backup-troubleshooting.md +52 -0
  9. package/docs/browser-use-architecture-phase2.md +174 -0
  10. package/docs/error-handling.md +111 -0
  11. package/docs/skills.md +10 -10
  12. package/docs/stt-provider-onboarding.md +121 -0
  13. package/knip.json +20 -3
  14. package/node_modules/@vellumai/ces-contracts/bun.lock +8 -6
  15. package/node_modules/@vellumai/ces-contracts/package.json +5 -4
  16. package/node_modules/@vellumai/ces-contracts/src/__tests__/trust-rules.test.ts +471 -0
  17. package/node_modules/@vellumai/ces-contracts/src/trust-rules.ts +398 -4
  18. package/node_modules/@vellumai/credential-storage/bun.lock +2 -2
  19. package/node_modules/@vellumai/credential-storage/package.json +2 -2
  20. package/node_modules/@vellumai/credential-storage/src/oauth-runtime.ts +20 -2
  21. package/node_modules/@vellumai/egress-proxy/bun.lock +2 -2
  22. package/node_modules/@vellumai/egress-proxy/package.json +2 -2
  23. package/openapi.yaml +1094 -72
  24. package/package.json +9 -8
  25. package/scripts/generate-openapi.ts +50 -12
  26. package/scripts/test.sh +73 -18
  27. package/src/__tests__/agent-image-optimize.test.ts +28 -0
  28. package/src/__tests__/agent-loop-callsite-precedence.test.ts +318 -0
  29. package/src/__tests__/agent-loop-sentry-hygiene.test.ts +137 -0
  30. package/src/__tests__/agent-loop.test.ts +235 -1
  31. package/src/__tests__/anthropic-error-formatting.test.ts +98 -0
  32. package/src/__tests__/anthropic-provider.test.ts +434 -12
  33. package/src/__tests__/approval-cascade.test.ts +31 -10
  34. package/src/__tests__/approval-routes-http.test.ts +134 -10
  35. package/src/__tests__/assistant-attachments.test.ts +44 -0
  36. package/src/__tests__/assistant-feature-flags-integration.test.ts +29 -0
  37. package/src/__tests__/auto-analysis-end-to-end.test.ts +550 -0
  38. package/src/__tests__/auto-analysis-prompt.test.ts +50 -0
  39. package/src/__tests__/browser-fill-credential.test.ts +12 -1
  40. package/src/__tests__/browser-identifier-parity-guard.test.ts +53 -0
  41. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +23 -33
  42. package/src/__tests__/browser-skill-endstate.test.ts +52 -159
  43. package/src/__tests__/btw-routes.test.ts +54 -1
  44. package/src/__tests__/call-controller.test.ts +582 -22
  45. package/src/__tests__/call-site-routing-provider.test.ts +214 -0
  46. package/src/__tests__/catalog-cache.test.ts +27 -4
  47. package/src/__tests__/catalog-files.test.ts +138 -0
  48. package/src/__tests__/channel-approval-routes.test.ts +4 -4
  49. package/src/__tests__/channel-invite-transport.test.ts +2 -2
  50. package/src/__tests__/channel-readiness-routes.test.ts +16 -20
  51. package/src/__tests__/channel-readiness-service.test.ts +12 -7
  52. package/src/__tests__/channel-reply-delivery.test.ts +300 -2
  53. package/src/__tests__/checker.test.ts +576 -502
  54. package/src/__tests__/clawhub-files.test.ts +347 -0
  55. package/src/__tests__/cli-command-risk-guard.test.ts +30 -33
  56. package/src/__tests__/commit-message-enrichment-service.test.ts +36 -19
  57. package/src/__tests__/compaction-circuit-breaker.test.ts +336 -0
  58. package/src/__tests__/compaction.benchmark.test.ts +1 -1
  59. package/src/__tests__/config-analysis.test.ts +83 -0
  60. package/src/__tests__/config-loader-backfill.test.ts +174 -0
  61. package/src/__tests__/config-loader-corrupt.test.ts +183 -0
  62. package/src/__tests__/config-loader-quarantine-bulletin.test.ts +202 -0
  63. package/src/__tests__/config-schema-cmd.test.ts +11 -5
  64. package/src/__tests__/config-schema.test.ts +1458 -198
  65. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +339 -0
  66. package/src/__tests__/config-watcher.test.ts +45 -10
  67. package/src/__tests__/contact-store-user-file.test.ts +511 -0
  68. package/src/__tests__/contacts-write.test.ts +197 -0
  69. package/src/__tests__/context-token-estimator.test.ts +191 -1
  70. package/src/__tests__/context-window-manager.test.ts +618 -2
  71. package/src/__tests__/conversation-abort-tool-results.test.ts +32 -16
  72. package/src/__tests__/conversation-agent-loop-overflow.test.ts +62 -17
  73. package/src/__tests__/conversation-agent-loop.test.ts +510 -84
  74. package/src/__tests__/conversation-attachments.test.ts +1 -1
  75. package/src/__tests__/conversation-confirmation-signals.test.ts +165 -9
  76. package/src/__tests__/conversation-error.test.ts +102 -1
  77. package/src/__tests__/conversation-history-web-search.test.ts +17 -4
  78. package/src/__tests__/conversation-init.benchmark.test.ts +42 -1
  79. package/src/__tests__/conversation-launcher-skill-regression.test.ts +51 -0
  80. package/src/__tests__/conversation-lifecycle.test.ts +336 -0
  81. package/src/__tests__/conversation-list-source.test.ts +145 -0
  82. package/src/__tests__/conversation-load-history-repair.test.ts +27 -10
  83. package/src/__tests__/conversation-pre-run-repair.test.ts +32 -16
  84. package/src/__tests__/conversation-process-callsite.test.ts +306 -0
  85. package/src/__tests__/conversation-provider-retry-repair.test.ts +32 -16
  86. package/src/__tests__/conversation-queue.test.ts +932 -76
  87. package/src/__tests__/conversation-routes-disk-view.test.ts +299 -1
  88. package/src/__tests__/conversation-routes-slash-commands.test.ts +31 -3
  89. package/src/__tests__/conversation-runtime-assembly.test.ts +2790 -55
  90. package/src/__tests__/conversation-runtime-workspace.test.ts +12 -12
  91. package/src/__tests__/conversation-skill-tools.test.ts +12 -143
  92. package/src/__tests__/conversation-slash-commands.test.ts +33 -0
  93. package/src/__tests__/conversation-slash-queue.test.ts +120 -34
  94. package/src/__tests__/conversation-slash-unknown.test.ts +32 -16
  95. package/src/__tests__/conversation-speed-override.test.ts +30 -11
  96. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +1035 -0
  97. package/src/__tests__/conversation-surfaces-standalone.test.ts +630 -0
  98. package/src/__tests__/conversation-title-service.test.ts +2 -2
  99. package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +226 -0
  100. package/src/__tests__/conversation-unread-route.test.ts +2 -2
  101. package/src/__tests__/conversation-usage.test.ts +3 -1
  102. package/src/__tests__/conversation-workspace-cache-state.test.ts +31 -10
  103. package/src/__tests__/conversation-workspace-injection.test.ts +45 -15
  104. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +46 -16
  105. package/src/__tests__/credential-broker-browser-fill.test.ts +110 -0
  106. package/src/__tests__/credential-health-service.test.ts +352 -0
  107. package/src/__tests__/credential-security-invariants.test.ts +8 -3
  108. package/src/__tests__/credential-storage-oauth-compat.test.ts +18 -0
  109. package/src/__tests__/credential-storage-static-compat.test.ts +28 -0
  110. package/src/__tests__/credential-vault-unit.test.ts +495 -3
  111. package/src/__tests__/credentials-cli.test.ts +32 -16
  112. package/src/__tests__/cross-provider-web-search.test.ts +230 -35
  113. package/src/__tests__/daemon-server-persist-and-process-callsite.test.ts +92 -0
  114. package/src/__tests__/delete-propagation.test.ts +437 -0
  115. package/src/__tests__/deterministic-verification-control-plane.test.ts +10 -1
  116. package/src/__tests__/device-id.test.ts +112 -0
  117. package/src/__tests__/dm-backfill.test.ts +417 -0
  118. package/src/__tests__/dm-persistence.test.ts +227 -0
  119. package/src/__tests__/docker-signing-key-bootstrap.test.ts +167 -4
  120. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -3
  121. package/src/__tests__/edit-propagation.test.ts +280 -0
  122. package/src/__tests__/email-html-renderer.test.ts +71 -0
  123. package/src/__tests__/email-invite-adapter.test.ts +36 -32
  124. package/src/__tests__/emit-event-signal.test.ts +71 -0
  125. package/src/__tests__/ephemeral-permissions.test.ts +93 -3
  126. package/src/__tests__/estimator-calibration-integration.test.ts +208 -0
  127. package/src/__tests__/estimator-calibration.test.ts +213 -0
  128. package/src/__tests__/extension-id-sync-guard.test.ts +101 -15
  129. package/src/__tests__/file-write-tool.test.ts +151 -1
  130. package/src/__tests__/filing-service.test.ts +255 -0
  131. package/src/__tests__/fixtures/mock-chrome-extension.ts +11 -0
  132. package/src/__tests__/gateway-only-enforcement.test.ts +206 -1
  133. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  134. package/src/__tests__/gemini-provider.test.ts +64 -3
  135. package/src/__tests__/get-skill-detail-audit.test.ts +325 -0
  136. package/src/__tests__/guardian-grant-minting.test.ts +8 -0
  137. package/src/__tests__/headless-browser-interactions.test.ts +44 -1
  138. package/src/__tests__/headless-browser-mode.test.ts +614 -0
  139. package/src/__tests__/headless-browser-navigate.test.ts +142 -5
  140. package/src/__tests__/headless-browser-read-tools.test.ts +11 -0
  141. package/src/__tests__/headless-browser-snapshot.test.ts +10 -0
  142. package/src/__tests__/heartbeat-service.test.ts +166 -32
  143. package/src/__tests__/home-state-routes.test.ts +162 -0
  144. package/src/__tests__/host-bash-proxy.test.ts +0 -5
  145. package/src/__tests__/host-browser-e2e-cloud.test.ts +138 -4
  146. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +4 -4
  147. package/src/__tests__/host-browser-ws-events-e2e.test.ts +103 -0
  148. package/src/__tests__/host-cu-proxy.test.ts +0 -5
  149. package/src/__tests__/host-shell-tool.test.ts +124 -18
  150. package/src/__tests__/http-user-message-parity.test.ts +29 -1
  151. package/src/__tests__/identity-intro-cache.test.ts +40 -10
  152. package/src/__tests__/inbound-slack-persistence.test.ts +340 -0
  153. package/src/__tests__/init-feature-flag-overrides.test.ts +38 -112
  154. package/src/__tests__/intent-routing.test.ts +1 -40
  155. package/src/__tests__/jobs-store-upsert-debounced.test.ts +141 -0
  156. package/src/__tests__/llm-catalog-parity.test.ts +174 -0
  157. package/src/__tests__/llm-context-normalization.test.ts +609 -0
  158. package/src/__tests__/llm-context-route-provider.test.ts +86 -5
  159. package/src/__tests__/llm-resolver.test.ts +214 -0
  160. package/src/__tests__/llm-schema.test.ts +223 -0
  161. package/src/__tests__/llm-usage-store.test.ts +363 -0
  162. package/src/__tests__/managed-proxy-context.test.ts +6 -2
  163. package/src/__tests__/media-stream-output.test.ts +555 -0
  164. package/src/__tests__/media-stream-parser.test.ts +374 -0
  165. package/src/__tests__/media-stream-server-integration.test.ts +1234 -0
  166. package/src/__tests__/media-stream-stt-session.test.ts +588 -0
  167. package/src/__tests__/media-turn-detector.test.ts +440 -0
  168. package/src/__tests__/message-queue.test.ts +125 -0
  169. package/src/__tests__/messaging-skill-split.test.ts +3 -34
  170. package/src/__tests__/migration-export-http.test.ts +6 -6
  171. package/src/__tests__/migration-import-commit-http.test.ts +8 -6
  172. package/src/__tests__/migration-import-from-url.test.ts +684 -0
  173. package/src/__tests__/migration-import-preflight-http.test.ts +6 -5
  174. package/src/__tests__/migration-validate-http.test.ts +3 -3
  175. package/src/__tests__/mock-gateway-ipc.ts +151 -0
  176. package/src/__tests__/model-intents.test.ts +10 -84
  177. package/src/__tests__/notification-decision-fallback.test.ts +0 -10
  178. package/src/__tests__/notification-decision-identity.test.ts +0 -9
  179. package/src/__tests__/notification-decision-recipient-context.test.ts +0 -9
  180. package/src/__tests__/oauth-apps-routes.test.ts +1 -0
  181. package/src/__tests__/oauth-cli.test.ts +2 -0
  182. package/src/__tests__/oauth-connect-orchestrator.test.ts +2 -0
  183. package/src/__tests__/oauth-provider-serializer.test.ts +1 -0
  184. package/src/__tests__/oauth-providers-routes.test.ts +2 -0
  185. package/src/__tests__/oauth-store.test.ts +95 -7
  186. package/src/__tests__/oauth2-gateway-transport.test.ts +257 -9
  187. package/src/__tests__/oauth2-refresh-retry.test.ts +279 -0
  188. package/src/__tests__/onboarding-template-contract.test.ts +6 -13
  189. package/src/__tests__/openai-provider.test.ts +183 -0
  190. package/src/__tests__/openai-responses-cutover-guard.test.ts +184 -0
  191. package/src/__tests__/openai-responses-provider.test.ts +1501 -0
  192. package/src/__tests__/openrouter-provider-only.test.ts +135 -0
  193. package/src/__tests__/openrouter-token-estimation.test.ts +100 -0
  194. package/src/__tests__/outbound-slack-persistence.test.ts +293 -0
  195. package/src/__tests__/permission-checker-host-gate.test.ts +1 -1
  196. package/src/__tests__/permission-mode.test.ts +16 -0
  197. package/src/__tests__/permission-types.test.ts +0 -1
  198. package/src/__tests__/persona-resolver.test.ts +251 -0
  199. package/src/__tests__/pkb-autoinject.test.ts +37 -1
  200. package/src/__tests__/platform-bash-auto-approve.test.ts +5 -1
  201. package/src/__tests__/platform.test.ts +92 -1
  202. package/src/__tests__/post-turn-tool-result-truncation.test.ts +47 -0
  203. package/src/__tests__/prechat-onboarding-contract.test.ts +267 -0
  204. package/src/__tests__/pricing.test.ts +224 -3
  205. package/src/__tests__/profiler-routes.test.ts +1 -1
  206. package/src/__tests__/provider-commit-message-generator.test.ts +14 -84
  207. package/src/__tests__/provider-env-vars-scope.test.ts +52 -0
  208. package/src/__tests__/provider-error-scenarios.test.ts +135 -6
  209. package/src/__tests__/provider-managed-proxy-integration.test.ts +42 -11
  210. package/src/__tests__/provider-registry-ollama.test.ts +1 -2
  211. package/src/__tests__/proxy-approval-callback.test.ts +0 -1
  212. package/src/__tests__/qdrant-manager.test.ts +29 -8
  213. package/src/__tests__/reaction-persistence.test.ts +560 -0
  214. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +194 -0
  215. package/src/__tests__/relationship-state-contract.test.ts +175 -0
  216. package/src/__tests__/relay-server.test.ts +424 -6
  217. package/src/__tests__/require-fresh-approval.test.ts +1 -1
  218. package/src/__tests__/retry-openrouter-only-normalization.test.ts +136 -0
  219. package/src/__tests__/retry-thinking-tool-choice.test.ts +226 -0
  220. package/src/__tests__/risk-classifier-parity.test.ts +230 -0
  221. package/src/__tests__/sanitize-config-for-transfer.test.ts +78 -1
  222. package/src/__tests__/search-skills-unified.test.ts +118 -0
  223. package/src/__tests__/secret-ingress-http.test.ts +28 -0
  224. package/src/__tests__/secret-prompter-channel-fallback.test.ts +125 -0
  225. package/src/__tests__/secret-routes-managed-proxy.test.ts +2 -3
  226. package/src/__tests__/secret-scanner-executor.test.ts +5 -1
  227. package/src/__tests__/secure-keys.test.ts +107 -0
  228. package/src/__tests__/send-endpoint-busy.test.ts +34 -2
  229. package/src/__tests__/sequence-store.test.ts +1 -1
  230. package/src/__tests__/server-history-render.test.ts +80 -0
  231. package/src/__tests__/settings-routes.test.ts +201 -0
  232. package/src/__tests__/shell-parser-property.test.ts +13 -13
  233. package/src/__tests__/skill-cache-store.test.ts +182 -0
  234. package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
  235. package/src/__tests__/skills-file-content-endpoint.test.ts +276 -145
  236. package/src/__tests__/skills-files-catalog-fallback.test.ts +381 -93
  237. package/src/__tests__/skills.test.ts +19 -30
  238. package/src/__tests__/skillssh-files.test.ts +446 -0
  239. package/src/__tests__/slack-app-setup-skill-regression.test.ts +3 -1
  240. package/src/__tests__/slack-block-formatting.test.ts +110 -0
  241. package/src/__tests__/slack-channel-config.test.ts +564 -1
  242. package/src/__tests__/slack-skill.test.ts +3 -8
  243. package/src/__tests__/starter-bundle.test.ts +35 -0
  244. package/src/__tests__/stt-catalog-parity.test.ts +282 -0
  245. package/src/__tests__/stt-stream-session.test.ts +535 -0
  246. package/src/__tests__/subagent-call-site-routing.test.ts +280 -0
  247. package/src/__tests__/suggestion-routes.test.ts +160 -3
  248. package/src/__tests__/system-prompt.test.ts +126 -53
  249. package/src/__tests__/task-runner.test.ts +3 -1
  250. package/src/__tests__/tcc-sandbox-deny.test.ts +198 -0
  251. package/src/__tests__/telephony-stt-routing.test.ts +329 -0
  252. package/src/__tests__/terminal-tools.test.ts +26 -7
  253. package/src/__tests__/test-preload.ts +18 -0
  254. package/src/__tests__/test-support/browser-skill-harness.ts +2 -49
  255. package/src/__tests__/thread-backfill.test.ts +941 -0
  256. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -2
  257. package/src/__tests__/tool-executor-lifecycle-events.test.ts +10 -6
  258. package/src/__tests__/tool-executor-shell-integration.test.ts +4 -0
  259. package/src/__tests__/tool-executor.test.ts +88 -113
  260. package/src/__tests__/tool-result-truncation.test.ts +36 -0
  261. package/src/__tests__/trust-store.test.ts +442 -103
  262. package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -1
  263. package/src/__tests__/tts-catalog-parity.test.ts +345 -0
  264. package/src/__tests__/twilio-routes-twiml.test.ts +512 -114
  265. package/src/__tests__/twilio-routes.test.ts +376 -0
  266. package/src/__tests__/unicode.test.ts +293 -0
  267. package/src/__tests__/update-bulletin-job.test.ts +389 -0
  268. package/src/__tests__/usage-cache-backfill-migration.test.ts +3 -1
  269. package/src/__tests__/usage-routes.test.ts +25 -4
  270. package/src/__tests__/user-reference.test.ts +46 -61
  271. package/src/__tests__/verification-control-plane-policy.test.ts +5 -22
  272. package/src/__tests__/voice-config-update.test.ts +403 -0
  273. package/src/__tests__/voice-quality.test.ts +434 -19
  274. package/src/__tests__/voice-session-bridge.test.ts +39 -0
  275. package/src/__tests__/volume-security-guard.test.ts +3 -2
  276. package/src/__tests__/web-search-history.test.ts +337 -0
  277. package/src/__tests__/workspace-heartbeat-service.test.ts +7 -0
  278. package/src/__tests__/workspace-migration-033-stt-service-explicit-config.test.ts +547 -0
  279. package/src/__tests__/workspace-migration-034-remove-calls-voice-transcription-provider.test.ts +596 -0
  280. package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +343 -0
  281. package/src/__tests__/workspace-migration-043-release-notes-latex-rendering.test.ts +202 -0
  282. package/src/__tests__/workspace-migration-045-release-notes-meet-avatar.test.ts +210 -0
  283. package/src/__tests__/workspace-migration-drop-user-md.test.ts +368 -0
  284. package/src/__tests__/workspace-migration-meets.test.ts +244 -0
  285. package/src/__tests__/workspace-migration-seed-device-id.test.ts +14 -20
  286. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +841 -0
  287. package/src/__tests__/workspace-policy.test.ts +1 -11
  288. package/src/acp/client-handler.ts +1 -2
  289. package/src/agent/image-optimize.ts +24 -12
  290. package/src/agent/loop.ts +251 -19
  291. package/src/avatar/resvg-lazy.test.ts +136 -0
  292. package/src/avatar/resvg-lazy.ts +82 -9
  293. package/src/avatar/traits-png-sync.ts +21 -1
  294. package/src/backup/__tests__/backup-key.test.ts +152 -0
  295. package/src/backup/__tests__/backup-worker.test.ts +767 -0
  296. package/src/backup/__tests__/list-snapshots.test.ts +87 -0
  297. package/src/backup/__tests__/local-writer.test.ts +218 -0
  298. package/src/backup/__tests__/offsite-writer.test.ts +641 -0
  299. package/src/backup/__tests__/paths.test.ts +300 -0
  300. package/src/backup/__tests__/restore.test.ts +498 -0
  301. package/src/backup/__tests__/snapshot-lock.test.ts +352 -0
  302. package/src/backup/__tests__/stream-crypt.test.ts +228 -0
  303. package/src/backup/backup-key.ts +137 -0
  304. package/src/backup/backup-worker.ts +459 -0
  305. package/src/backup/list-snapshots.ts +147 -0
  306. package/src/backup/local-writer.ts +133 -0
  307. package/src/backup/offsite-writer.ts +222 -0
  308. package/src/backup/paths.ts +226 -0
  309. package/src/backup/restore.ts +322 -0
  310. package/src/backup/snapshot-lock.ts +431 -0
  311. package/src/backup/stream-crypt.ts +263 -0
  312. package/src/browser/__tests__/operations.test.ts +163 -0
  313. package/src/browser/identifiers.ts +51 -0
  314. package/src/browser/operations.ts +660 -0
  315. package/src/browser/types.ts +81 -0
  316. package/src/bundler/package-resolver.ts +4 -0
  317. package/src/calls/audio-store.ts +11 -5
  318. package/src/calls/call-controller.ts +226 -71
  319. package/src/calls/call-domain.ts +9 -0
  320. package/src/calls/call-speech-output.ts +190 -0
  321. package/src/calls/call-transport.ts +77 -0
  322. package/src/calls/guardian-question-copy.ts +2 -2
  323. package/src/calls/media-stream-audio-transcode.ts +173 -0
  324. package/src/calls/media-stream-output.ts +660 -0
  325. package/src/calls/media-stream-parser.ts +300 -0
  326. package/src/calls/media-stream-protocol.ts +166 -0
  327. package/src/calls/media-stream-server.ts +592 -0
  328. package/src/calls/media-stream-stt-session.ts +460 -0
  329. package/src/calls/media-turn-detector.ts +230 -0
  330. package/src/calls/relay-server.ts +90 -75
  331. package/src/calls/resolve-call-tts-provider.ts +136 -0
  332. package/src/calls/telephony-stt-routing.ts +145 -0
  333. package/src/calls/tts-call-strategy.ts +161 -0
  334. package/src/calls/tts-text-sanitizer.ts +32 -16
  335. package/src/calls/twilio-routes.ts +281 -17
  336. package/src/calls/voice-quality.ts +78 -35
  337. package/src/calls/voice-session-bridge.ts +9 -1
  338. package/src/channels/types.ts +16 -0
  339. package/src/cli/AGENTS.md +1 -1
  340. package/src/cli/__tests__/run-assistant-command.ts +11 -1
  341. package/src/cli/commands/__tests__/attachment.test.ts +438 -0
  342. package/src/cli/commands/__tests__/backup.test.ts +1165 -0
  343. package/src/cli/commands/__tests__/browser.test.ts +554 -0
  344. package/src/cli/commands/__tests__/cache.test.ts +623 -0
  345. package/src/cli/commands/__tests__/domain-register.test.ts +234 -0
  346. package/src/cli/commands/__tests__/domain-status.test.ts +132 -0
  347. package/src/cli/commands/__tests__/email-attachment.test.ts +422 -0
  348. package/src/cli/commands/__tests__/email-download.test.ts +16 -1
  349. package/src/cli/commands/__tests__/email-list.test.ts +28 -4
  350. package/src/cli/commands/__tests__/email-register.test.ts +4 -4
  351. package/src/cli/commands/__tests__/email-send.test.ts +130 -5
  352. package/src/cli/commands/__tests__/email-status.test.ts +5 -1
  353. package/src/cli/commands/__tests__/email-unregister.test.ts +34 -5
  354. package/src/cli/commands/__tests__/image-generation.test.ts +666 -0
  355. package/src/cli/commands/__tests__/inference-send.test.ts +451 -0
  356. package/src/cli/commands/__tests__/stt-transcribe.test.ts +454 -0
  357. package/src/cli/commands/__tests__/task.test.ts +913 -0
  358. package/src/cli/commands/__tests__/tts-synthesize.test.ts +594 -0
  359. package/src/cli/commands/__tests__/ui-confirm.test.ts +650 -0
  360. package/src/cli/commands/__tests__/ui.test.ts +1215 -0
  361. package/src/cli/commands/__tests__/watchers.test.ts +716 -0
  362. package/src/cli/commands/attachment.ts +182 -0
  363. package/src/cli/commands/backup.ts +993 -0
  364. package/src/cli/commands/browser.ts +350 -0
  365. package/src/cli/commands/cache.ts +341 -0
  366. package/src/cli/commands/completions.ts +0 -3
  367. package/src/cli/commands/config.ts +6 -6
  368. package/src/cli/commands/conversations-import.ts +347 -0
  369. package/src/cli/commands/conversations.ts +90 -0
  370. package/src/cli/commands/credentials.ts +0 -1
  371. package/src/cli/commands/domain.ts +210 -0
  372. package/src/cli/commands/email.ts +308 -16
  373. package/src/cli/commands/image-generation.ts +300 -0
  374. package/src/cli/commands/inference.ts +200 -0
  375. package/src/cli/commands/memory.ts +127 -17
  376. package/src/cli/commands/oauth/__tests__/connect.test.ts +12 -0
  377. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +1 -0
  378. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -0
  379. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -0
  380. package/src/cli/commands/oauth/mode.ts +12 -3
  381. package/src/cli/commands/oauth/providers.ts +15 -0
  382. package/src/cli/commands/oauth/shared.ts +2 -1
  383. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +4 -10
  384. package/src/cli/commands/platform/__tests__/connect.test.ts +6 -1
  385. package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -2
  386. package/src/cli/commands/platform/__tests__/status.test.ts +6 -1
  387. package/src/cli/commands/stt.ts +339 -0
  388. package/src/cli/commands/task.ts +795 -0
  389. package/src/cli/commands/trust.ts +50 -19
  390. package/src/cli/commands/tts.ts +273 -0
  391. package/src/cli/commands/ui.ts +670 -0
  392. package/src/cli/commands/watchers.ts +509 -0
  393. package/src/cli/lib/daemon-credential-client.ts +0 -19
  394. package/src/cli/program.ts +53 -8
  395. package/src/cli.ts +0 -37
  396. package/src/config/__tests__/backup-schema.test.ts +134 -0
  397. package/src/config/assistant-feature-flags.ts +61 -62
  398. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +37 -1
  399. package/src/config/bundled-skills/contacts/SKILL.md +2 -2
  400. package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +23 -1
  401. package/src/config/bundled-skills/media-processing/SKILL.md +3 -9
  402. package/src/config/bundled-skills/media-processing/TOOLS.json +1 -6
  403. package/src/config/bundled-skills/media-processing/__tests__/audio-transcribe.test.ts +125 -0
  404. package/src/config/bundled-skills/media-processing/__tests__/extract-keyframes.test.ts +181 -0
  405. package/src/config/bundled-skills/media-processing/__tests__/preprocess-audio.test.ts +141 -0
  406. package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +32 -87
  407. package/src/config/bundled-skills/media-processing/services/preprocess.ts +8 -4
  408. package/src/config/bundled-skills/media-processing/services/reduce.ts +1 -1
  409. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +0 -10
  410. package/src/config/bundled-skills/messaging/SKILL.md +5 -5
  411. package/src/config/bundled-skills/messaging/TOOLS.json +4 -0
  412. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -2
  413. package/src/config/bundled-skills/messaging/tools/messaging-read.ts +15 -1
  414. package/src/config/bundled-skills/messaging/tools/messaging-search.ts +21 -1
  415. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +11 -12
  416. package/src/config/bundled-skills/phone-calls/SKILL.md +2 -2
  417. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +28 -18
  418. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +3 -3
  419. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  420. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +26 -22
  421. package/src/config/bundled-skills/transcribe/SKILL.md +9 -14
  422. package/src/config/bundled-skills/transcribe/TOOLS.json +2 -7
  423. package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +256 -0
  424. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +38 -188
  425. package/src/config/bundled-tool-registry.ts +0 -167
  426. package/src/config/env-registry.ts +24 -0
  427. package/src/config/env.ts +39 -10
  428. package/src/config/feature-flag-registry.json +63 -15
  429. package/src/config/llm-resolver.ts +128 -0
  430. package/src/config/loader.ts +220 -22
  431. package/src/config/raw-config-utils.ts +30 -2
  432. package/src/config/sanitize-for-transfer.ts +35 -0
  433. package/src/config/schema.ts +65 -51
  434. package/src/config/schemas/__tests__/stt.test.ts +43 -0
  435. package/src/config/schemas/analysis.ts +32 -0
  436. package/src/config/schemas/backup.ts +72 -0
  437. package/src/config/schemas/calls.ts +1 -30
  438. package/src/config/schemas/elevenlabs.ts +0 -59
  439. package/src/config/schemas/filing.ts +49 -14
  440. package/src/config/schemas/heartbeat.ts +27 -10
  441. package/src/config/schemas/host-browser.ts +47 -1
  442. package/src/config/schemas/inference.ts +3 -23
  443. package/src/config/schemas/llm.ts +318 -0
  444. package/src/config/schemas/memory-lifecycle.ts +14 -2
  445. package/src/config/schemas/memory-processing.ts +1 -9
  446. package/src/config/schemas/notifications.ts +4 -11
  447. package/src/config/schemas/platform.ts +3 -9
  448. package/src/config/schemas/security.ts +33 -0
  449. package/src/config/schemas/services.ts +53 -4
  450. package/src/config/schemas/stt.ts +60 -0
  451. package/src/config/schemas/tts.ts +283 -0
  452. package/src/config/schemas/updates.ts +14 -0
  453. package/src/config/schemas/workspace-git.ts +3 -40
  454. package/src/config/skills.ts +6 -2
  455. package/src/config/types.ts +4 -0
  456. package/src/contacts/contact-store.ts +56 -11
  457. package/src/contacts/contacts-write.ts +38 -1
  458. package/src/context/__tests__/compact-prompt.test.ts +45 -0
  459. package/src/context/__tests__/microcompact.test.ts +805 -0
  460. package/src/context/estimator-calibration.ts +136 -0
  461. package/src/context/microcompact.ts +443 -0
  462. package/src/context/post-turn-tool-result-truncation.ts +3 -2
  463. package/src/context/prompts/compact.md +12 -0
  464. package/src/context/token-estimator.ts +61 -3
  465. package/src/context/tool-result-truncation.ts +2 -1
  466. package/src/context/window-manager.ts +272 -35
  467. package/src/credential-execution/approval-bridge.ts +0 -1
  468. package/src/credential-execution/executable-discovery.ts +23 -2
  469. package/src/credential-execution/process-manager.test.ts +109 -0
  470. package/src/credential-execution/process-manager.ts +96 -2
  471. package/src/credential-health/credential-health-service.ts +366 -0
  472. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +324 -0
  473. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +497 -0
  474. package/src/daemon/__tests__/conversation-tool-setup.test.ts +17 -8
  475. package/src/daemon/__tests__/lifecycle-startup-ordering.test.ts +127 -0
  476. package/src/daemon/approval-generators.ts +29 -4
  477. package/src/daemon/assistant-attachments.ts +24 -13
  478. package/src/daemon/classifier.ts +2 -2
  479. package/src/daemon/config-watcher.ts +99 -6
  480. package/src/daemon/context-overflow-reducer.ts +4 -1
  481. package/src/daemon/conversation-agent-loop-handlers.ts +85 -12
  482. package/src/daemon/conversation-agent-loop.ts +563 -104
  483. package/src/daemon/conversation-attachments.ts +2 -6
  484. package/src/daemon/conversation-error.ts +46 -0
  485. package/src/daemon/conversation-history.ts +40 -6
  486. package/src/daemon/conversation-launch.ts +220 -0
  487. package/src/daemon/conversation-lifecycle.ts +85 -11
  488. package/src/daemon/conversation-messaging.ts +110 -7
  489. package/src/daemon/conversation-notifiers.ts +5 -0
  490. package/src/daemon/conversation-process.ts +591 -23
  491. package/src/daemon/conversation-queue-manager.ts +27 -0
  492. package/src/daemon/conversation-runtime-assembly.ts +769 -28
  493. package/src/daemon/conversation-slash.ts +38 -2
  494. package/src/daemon/conversation-surfaces.ts +483 -5
  495. package/src/daemon/conversation-tool-setup.ts +35 -5
  496. package/src/daemon/conversation-usage.ts +8 -5
  497. package/src/daemon/conversation.ts +193 -47
  498. package/src/daemon/external-skills-bootstrap.ts +41 -0
  499. package/src/daemon/guardian-action-generators.ts +34 -14
  500. package/src/daemon/handlers/config-model.test.ts +86 -0
  501. package/src/daemon/handlers/config-model.ts +54 -12
  502. package/src/daemon/handlers/config-slack-channel.ts +269 -94
  503. package/src/daemon/handlers/conversations.ts +13 -3
  504. package/src/daemon/handlers/shared.ts +51 -1
  505. package/src/daemon/handlers/skills.ts +323 -79
  506. package/src/daemon/handlers/slack-channel-oauth-install.ts +197 -0
  507. package/src/daemon/host-browser-proxy.ts +2 -1
  508. package/src/daemon/lifecycle.ts +185 -26
  509. package/src/daemon/message-protocol.ts +6 -0
  510. package/src/daemon/message-types/conversations.ts +48 -1
  511. package/src/daemon/message-types/home.ts +40 -0
  512. package/src/daemon/message-types/meet.ts +143 -0
  513. package/src/daemon/message-types/messages.ts +23 -1
  514. package/src/daemon/message-types/schedules.ts +34 -2
  515. package/src/daemon/message-types/skills.ts +16 -0
  516. package/src/daemon/message-types/surfaces.ts +2 -0
  517. package/src/daemon/message-types/trust.ts +0 -2
  518. package/src/daemon/parse-actual-tokens-from-error.test.ts +57 -1
  519. package/src/daemon/parse-actual-tokens-from-error.ts +66 -0
  520. package/src/daemon/pkb-context-tracker.test.ts +169 -0
  521. package/src/daemon/pkb-context-tracker.ts +125 -0
  522. package/src/daemon/pkb-reminder-builder.test.ts +70 -0
  523. package/src/daemon/pkb-reminder-builder.ts +31 -0
  524. package/src/daemon/providers-setup.ts +6 -0
  525. package/src/daemon/server.ts +463 -10
  526. package/src/daemon/shutdown-handlers.ts +32 -4
  527. package/src/daemon/shutdown-registry.ts +40 -0
  528. package/src/daemon/tool-side-effects.ts +9 -9
  529. package/src/daemon/watch-handler.ts +4 -4
  530. package/src/daemon/web-search-history.ts +126 -0
  531. package/src/email/html-renderer.ts +76 -0
  532. package/src/events/domain-events.ts +0 -1
  533. package/src/filing/filing-service.ts +9 -10
  534. package/src/heartbeat/heartbeat-service.ts +156 -22
  535. package/src/home/__tests__/assistant-feed-authoring.test.ts +156 -0
  536. package/src/home/__tests__/emit-feed-event.test.ts +169 -0
  537. package/src/home/__tests__/feed-scheduler.test.ts +222 -0
  538. package/src/home/__tests__/feed-types.test.ts +275 -0
  539. package/src/home/__tests__/feed-writer.test.ts +688 -0
  540. package/src/home/__tests__/phase5-exit-criteria.test.ts +212 -0
  541. package/src/home/__tests__/platform-gmail-digest.test.ts +222 -0
  542. package/src/home/__tests__/progress-formula.test.ts +213 -0
  543. package/src/home/__tests__/relationship-state-writer.test.ts +740 -0
  544. package/src/home/__tests__/rollup-producer.test.ts +442 -0
  545. package/src/home/assistant-feed-authoring.ts +128 -0
  546. package/src/home/emit-feed-event.ts +162 -0
  547. package/src/home/feed-scheduler.ts +263 -0
  548. package/src/home/feed-types.ts +235 -0
  549. package/src/home/feed-writer.ts +469 -0
  550. package/src/home/platform-gmail-digest.ts +163 -0
  551. package/src/home/progress-formula.ts +86 -0
  552. package/src/home/relationship-state-writer.ts +824 -0
  553. package/src/home/relationship-state.ts +143 -0
  554. package/src/home/rollup-producer.ts +413 -0
  555. package/src/home/suggested-prompts.ts +101 -0
  556. package/src/hooks/runner.ts +7 -0
  557. package/src/inbound/platform-callback-registration.ts +12 -3
  558. package/src/inbound/public-ingress-urls.ts +12 -0
  559. package/src/instrument.ts +1 -1
  560. package/src/ipc/__tests__/attachment-ipc.test.ts +213 -0
  561. package/src/ipc/__tests__/browser-ipc.test.ts +339 -0
  562. package/src/ipc/__tests__/cache-ipc.test.ts +266 -0
  563. package/src/ipc/__tests__/cli-ipc.test.ts +200 -0
  564. package/src/ipc/__tests__/socket-path.test.ts +73 -0
  565. package/src/ipc/__tests__/task-ipc.test.ts +577 -0
  566. package/src/ipc/__tests__/ui-request-route.test.ts +495 -0
  567. package/src/ipc/__tests__/watcher-ipc.test.ts +295 -0
  568. package/src/ipc/cli-client.ts +152 -0
  569. package/src/ipc/cli-server.ts +252 -0
  570. package/src/ipc/gateway-client.ts +180 -0
  571. package/src/ipc/routes/attachment.ts +114 -0
  572. package/src/ipc/routes/browser-context.ts +61 -0
  573. package/src/ipc/routes/browser.ts +96 -0
  574. package/src/ipc/routes/cache.ts +96 -0
  575. package/src/ipc/routes/index.ts +21 -0
  576. package/src/ipc/routes/task-queue.ts +226 -0
  577. package/src/ipc/routes/task.ts +173 -0
  578. package/src/ipc/routes/ui-request.ts +50 -0
  579. package/src/ipc/routes/wake-conversation.ts +19 -0
  580. package/src/ipc/routes/watcher.ts +203 -0
  581. package/src/ipc/socket-path.ts +100 -0
  582. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +356 -0
  583. package/src/memory/__tests__/auto-analysis-guard.test.ts +57 -0
  584. package/src/memory/__tests__/conversation-analyze-job.test.ts +233 -0
  585. package/src/memory/__tests__/conversation-group-migration.test.ts +99 -0
  586. package/src/memory/__tests__/find-analysis-conversation.test.ts +196 -0
  587. package/src/memory/admin.ts +18 -0
  588. package/src/memory/app-store.ts +1 -1
  589. package/src/memory/attachments-store.ts +70 -0
  590. package/src/memory/auto-analysis-enqueue.ts +127 -0
  591. package/src/memory/auto-analysis-guard.ts +27 -0
  592. package/src/memory/cleanup-schedule-state.ts +37 -0
  593. package/src/memory/conversation-analyze-job.ts +74 -0
  594. package/src/memory/conversation-attention-store.ts +13 -6
  595. package/src/memory/conversation-crud.ts +199 -0
  596. package/src/memory/conversation-disk-view.ts +7 -0
  597. package/src/memory/conversation-group-migration.ts +65 -1
  598. package/src/memory/conversation-queries.ts +6 -5
  599. package/src/memory/conversation-title-service.ts +7 -4
  600. package/src/memory/db-init.ts +8 -0
  601. package/src/memory/db-maintenance.ts +108 -0
  602. package/src/memory/db.ts +1 -0
  603. package/src/memory/embedding-backend.ts +1 -1
  604. package/src/memory/graph/compaction.ts +299 -0
  605. package/src/memory/graph/consolidation.ts +4 -4
  606. package/src/memory/graph/conversation-graph-memory.ts +104 -29
  607. package/src/memory/graph/extraction.test.ts +295 -2
  608. package/src/memory/graph/extraction.ts +181 -51
  609. package/src/memory/graph/graph-search.test.ts +92 -0
  610. package/src/memory/graph/graph-search.ts +4 -1
  611. package/src/memory/graph/narrative.ts +2 -2
  612. package/src/memory/graph/pattern-scan.ts +2 -2
  613. package/src/memory/graph/retriever.test.ts +459 -0
  614. package/src/memory/graph/retriever.ts +257 -66
  615. package/src/memory/graph/scoring.test.ts +186 -0
  616. package/src/memory/graph/scoring.ts +31 -1
  617. package/src/memory/graph/store.ts +41 -0
  618. package/src/memory/graph/tool-handlers.ts +27 -0
  619. package/src/memory/graph/tools.ts +6 -1
  620. package/src/memory/group-crud.ts +6 -1
  621. package/src/memory/indexer.ts +95 -16
  622. package/src/memory/job-handlers/cleanup.ts +11 -8
  623. package/src/memory/job-handlers/conversation-starters.ts +39 -30
  624. package/src/memory/job-handlers/summarization.ts +2 -2
  625. package/src/memory/job-utils.ts +7 -1
  626. package/src/memory/jobs/embed-pkb-file.test.ts +168 -0
  627. package/src/memory/jobs/embed-pkb-file.ts +54 -0
  628. package/src/memory/jobs-store.ts +106 -5
  629. package/src/memory/jobs-worker.ts +26 -9
  630. package/src/memory/llm-usage-store.ts +92 -56
  631. package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +1 -1
  632. package/src/memory/migrations/219-oauth-providers-token-exchange-body-format.ts +15 -0
  633. package/src/memory/migrations/220-normalize-user-file-by-principal.ts +190 -0
  634. package/src/memory/migrations/221-conversations-archived-at.ts +16 -0
  635. package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +82 -0
  636. package/src/memory/migrations/index.ts +7 -0
  637. package/src/memory/migrations/registry.ts +8 -0
  638. package/src/memory/pkb/pkb-index.test.ts +368 -0
  639. package/src/memory/pkb/pkb-index.ts +255 -0
  640. package/src/memory/pkb/pkb-reconcile.test.ts +251 -0
  641. package/src/memory/pkb/pkb-reconcile.ts +148 -0
  642. package/src/memory/pkb/pkb-search.test.ts +438 -0
  643. package/src/memory/pkb/pkb-search.ts +137 -0
  644. package/src/memory/pkb/types.ts +53 -0
  645. package/src/memory/qdrant-client.ts +122 -1
  646. package/src/memory/qdrant-manager.ts +43 -16
  647. package/src/memory/schema/conversations.ts +2 -0
  648. package/src/memory/schema/oauth.ts +3 -0
  649. package/src/memory/slack-thread-store.ts +37 -0
  650. package/src/memory/usage-buckets.ts +396 -0
  651. package/src/messaging/providers/gmail/adapter.ts +6 -16
  652. package/src/messaging/providers/gmail/client.ts +79 -6
  653. package/src/messaging/providers/gmail/types.ts +7 -0
  654. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +282 -0
  655. package/src/messaging/providers/slack/adapter.ts +155 -38
  656. package/src/messaging/providers/slack/backfill.test.ts +257 -0
  657. package/src/messaging/providers/slack/backfill.ts +101 -0
  658. package/src/messaging/providers/slack/client.ts +16 -0
  659. package/src/messaging/providers/slack/message-metadata.test.ts +316 -0
  660. package/src/messaging/providers/slack/message-metadata.ts +123 -0
  661. package/src/messaging/providers/slack/render-transcript.test.ts +1373 -0
  662. package/src/messaging/providers/slack/render-transcript.ts +443 -0
  663. package/src/messaging/providers/slack/types.ts +4 -0
  664. package/src/messaging/style-analyzer.ts +5 -2
  665. package/src/notifications/README.md +9 -5
  666. package/src/notifications/decision-engine.ts +6 -12
  667. package/src/notifications/preference-extractor.ts +2 -6
  668. package/src/notifications/signal.ts +5 -0
  669. package/src/oauth/__tests__/identity-verifier.test.ts +1 -0
  670. package/src/oauth/byo-connection.test.ts +18 -1
  671. package/src/oauth/byo-connection.ts +3 -1
  672. package/src/oauth/connect-orchestrator.ts +2 -0
  673. package/src/oauth/connection-resolver.ts +6 -2
  674. package/src/oauth/connection.ts +2 -0
  675. package/src/oauth/oauth-store.ts +10 -0
  676. package/src/oauth/platform-connection.test.ts +145 -0
  677. package/src/oauth/platform-connection.ts +62 -31
  678. package/src/oauth/seed-providers.ts +10 -1
  679. package/src/permissions/approval-policy.test.ts +948 -0
  680. package/src/permissions/approval-policy.ts +257 -0
  681. package/src/permissions/bash-risk-classifier.test.ts +1208 -0
  682. package/src/permissions/bash-risk-classifier.ts +707 -0
  683. package/src/permissions/checker.ts +218 -699
  684. package/src/permissions/command-registry.test.ts +535 -0
  685. package/src/permissions/command-registry.ts +825 -0
  686. package/src/permissions/defaults.ts +71 -75
  687. package/src/permissions/file-risk-classifier.test.ts +535 -0
  688. package/src/permissions/file-risk-classifier.ts +274 -0
  689. package/src/permissions/risk-types.ts +205 -0
  690. package/src/permissions/secret-prompter.ts +53 -2
  691. package/src/permissions/skill-risk-classifier.test.ts +311 -0
  692. package/src/permissions/skill-risk-classifier.ts +214 -0
  693. package/src/permissions/trust-client.ts +52 -25
  694. package/src/permissions/trust-store-interface.ts +1 -6
  695. package/src/permissions/trust-store.ts +164 -65
  696. package/src/permissions/types.ts +23 -14
  697. package/src/permissions/web-risk-classifier.test.ts +170 -0
  698. package/src/permissions/web-risk-classifier.ts +89 -0
  699. package/src/permissions/workspace-policy.ts +1 -13
  700. package/src/platform/client.test.ts +10 -0
  701. package/src/platform/client.ts +19 -1
  702. package/src/platform/sync-identity.ts +129 -0
  703. package/src/prompts/persona-resolver.ts +127 -3
  704. package/src/prompts/system-prompt.ts +78 -38
  705. package/src/prompts/templates/BOOTSTRAP.md +5 -5
  706. package/src/prompts/templates/SOUL.md +5 -3
  707. package/src/prompts/templates/channels/slack.md +20 -0
  708. package/src/prompts/update-bulletin-job.ts +190 -0
  709. package/src/prompts/user-reference.ts +20 -17
  710. package/src/providers/__tests__/context-overflow-error.test.ts +328 -0
  711. package/src/providers/__tests__/provider-env-vars.test.ts +102 -0
  712. package/src/providers/__tests__/provider-secret-catalog.test.ts +42 -0
  713. package/src/providers/__tests__/retry-callsite.test.ts +424 -0
  714. package/src/providers/anthropic/client.ts +335 -70
  715. package/src/providers/call-site-routing.ts +71 -0
  716. package/src/providers/fireworks/client.ts +2 -2
  717. package/src/providers/gemini/client.ts +74 -3
  718. package/src/providers/managed-proxy/constants.ts +2 -1
  719. package/src/providers/model-catalog.ts +502 -28
  720. package/src/providers/model-intents.ts +8 -8
  721. package/src/providers/ollama/client.ts +2 -2
  722. package/src/providers/openai/chat-completions-provider.ts +530 -0
  723. package/src/providers/openai/client.ts +25 -440
  724. package/src/providers/openai/responses-provider.ts +579 -0
  725. package/src/providers/openrouter/client.ts +168 -4
  726. package/src/providers/provider-env-vars.ts +56 -0
  727. package/src/providers/provider-secret-catalog.ts +139 -0
  728. package/src/providers/provider-send-message.ts +22 -5
  729. package/src/providers/ratelimit.ts +4 -0
  730. package/src/providers/registry.ts +21 -10
  731. package/src/providers/retry.ts +185 -39
  732. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +251 -0
  733. package/src/providers/speech-to-text/__tests__/resolve.test.ts +883 -0
  734. package/src/providers/speech-to-text/deepgram-realtime.test.ts +980 -0
  735. package/src/providers/speech-to-text/deepgram-realtime.ts +767 -0
  736. package/src/providers/speech-to-text/deepgram.test.ts +332 -0
  737. package/src/providers/speech-to-text/deepgram.ts +115 -0
  738. package/src/providers/speech-to-text/google-gemini-live-stream.test.ts +743 -0
  739. package/src/providers/speech-to-text/google-gemini-live-stream.ts +625 -0
  740. package/src/providers/speech-to-text/google-gemini.test.ts +226 -0
  741. package/src/providers/speech-to-text/google-gemini.ts +101 -0
  742. package/src/providers/speech-to-text/openai-whisper-stream.test.ts +564 -0
  743. package/src/providers/speech-to-text/openai-whisper-stream.ts +381 -0
  744. package/src/providers/speech-to-text/openai-whisper.test.ts +1 -37
  745. package/src/providers/speech-to-text/openai-whisper.ts +63 -33
  746. package/src/providers/speech-to-text/provider-catalog.ts +323 -0
  747. package/src/providers/speech-to-text/resolve.ts +393 -6
  748. package/src/providers/speech-to-text/xai-realtime.test.ts +578 -0
  749. package/src/providers/speech-to-text/xai-realtime.ts +796 -0
  750. package/src/providers/speech-to-text/xai.test.ts +155 -0
  751. package/src/providers/speech-to-text/xai.ts +97 -0
  752. package/src/providers/types.ts +102 -3
  753. package/src/runtime/AGENTS.md +45 -3
  754. package/src/runtime/__tests__/agent-wake.test.ts +872 -0
  755. package/src/runtime/__tests__/interactive-ui.test.ts +673 -0
  756. package/src/runtime/__tests__/runtime-mode.test.ts +62 -0
  757. package/src/runtime/__tests__/slack-block-formatting.test.ts +481 -0
  758. package/src/runtime/agent-wake.ts +553 -0
  759. package/src/runtime/auth/__tests__/route-policy.test.ts +40 -0
  760. package/src/runtime/auth/route-policy.ts +34 -5
  761. package/src/runtime/auth/token-service.ts +56 -1
  762. package/src/runtime/btw-sidechain.ts +15 -3
  763. package/src/runtime/capability-tokens.ts +10 -10
  764. package/src/runtime/channel-invite-transport.ts +1 -1
  765. package/src/runtime/channel-invite-transports/email.ts +14 -6
  766. package/src/runtime/channel-readiness-service.ts +12 -22
  767. package/src/runtime/channel-reply-delivery.ts +106 -2
  768. package/src/runtime/chrome-extension-registry.ts +38 -2
  769. package/src/runtime/decision-token.ts +116 -0
  770. package/src/runtime/gateway-client.ts +2 -2
  771. package/src/runtime/http-router.ts +32 -0
  772. package/src/runtime/http-server.ts +447 -11
  773. package/src/runtime/http-types.ts +29 -3
  774. package/src/runtime/interactive-ui.ts +362 -0
  775. package/src/runtime/invite-instruction-generator.ts +2 -2
  776. package/src/runtime/migrations/__tests__/gcs-signed-url.test.ts +176 -0
  777. package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +36 -0
  778. package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +360 -0
  779. package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +390 -0
  780. package/src/runtime/migrations/__tests__/vbundle-metadata-merge.test.ts +221 -0
  781. package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +1540 -0
  782. package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +453 -0
  783. package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +222 -0
  784. package/src/runtime/migrations/gcs-signed-url.ts +162 -0
  785. package/src/runtime/migrations/migration-transport.ts +1 -0
  786. package/src/runtime/migrations/migration-wizard.ts +1 -0
  787. package/src/runtime/migrations/vbundle-import-analyzer.ts +77 -1
  788. package/src/runtime/migrations/vbundle-importer.ts +187 -8
  789. package/src/runtime/migrations/vbundle-metadata-merge.ts +124 -0
  790. package/src/runtime/migrations/vbundle-streaming-importer.ts +2522 -0
  791. package/src/runtime/migrations/vbundle-streaming-validator.ts +244 -0
  792. package/src/runtime/migrations/vbundle-tar-stream.ts +217 -0
  793. package/src/runtime/migrations/vbundle-validator.ts +15 -6
  794. package/src/runtime/pending-interactions.ts +0 -11
  795. package/src/runtime/routes/__tests__/backup-routes.test.ts +967 -0
  796. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +618 -0
  797. package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +247 -0
  798. package/src/runtime/routes/__tests__/migration-vellum-metadata-reconcile.test.ts +246 -0
  799. package/src/runtime/routes/__tests__/stt-routes.test.ts +406 -0
  800. package/src/runtime/routes/__tests__/tts-routes.test.ts +474 -0
  801. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +148 -17
  802. package/src/runtime/routes/app-management-routes.ts +12 -18
  803. package/src/runtime/routes/approval-prompt-ts-tracker.ts +58 -0
  804. package/src/runtime/routes/approval-routes.ts +12 -17
  805. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +9 -0
  806. package/src/runtime/routes/attachment-routes.test.ts +9 -3
  807. package/src/runtime/routes/attachment-routes.ts +216 -17
  808. package/src/runtime/routes/avatar-routes.ts +20 -4
  809. package/src/runtime/routes/backup-routes.ts +519 -0
  810. package/src/runtime/routes/browser-extension-pair-routes.ts +82 -23
  811. package/src/runtime/routes/btw-routes.ts +9 -10
  812. package/src/runtime/routes/contact-routes.test.ts +298 -0
  813. package/src/runtime/routes/contact-routes.ts +132 -5
  814. package/src/runtime/routes/conversation-analysis-routes.ts +22 -142
  815. package/src/runtime/routes/conversation-management-routes.ts +133 -0
  816. package/src/runtime/routes/conversation-routes.ts +487 -160
  817. package/src/runtime/routes/debug-routes.ts +1 -1
  818. package/src/runtime/routes/diagnostics-routes.ts +6 -4
  819. package/src/runtime/routes/events-routes.ts +16 -0
  820. package/src/runtime/routes/filing-routes.ts +93 -0
  821. package/src/runtime/routes/guardian-approval-interception.ts +33 -3
  822. package/src/runtime/routes/guardian-approval-prompt.ts +13 -3
  823. package/src/runtime/routes/home-feed-routes.ts +452 -0
  824. package/src/runtime/routes/home-state-routes.ts +138 -0
  825. package/src/runtime/routes/host-browser-routes.ts +3 -14
  826. package/src/runtime/routes/identity-intro-cache.ts +7 -3
  827. package/src/runtime/routes/identity-routes.ts +3 -17
  828. package/src/runtime/routes/inbound-message-handler.ts +912 -2
  829. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +113 -2
  830. package/src/runtime/routes/inbound-stages/background-dispatch.ts +61 -3
  831. package/src/runtime/routes/inbound-stages/edit-intercept.ts +129 -6
  832. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +46 -39
  833. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +15 -15
  834. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +137 -0
  835. package/src/runtime/routes/integrations/slack/__tests__/share.test.ts +179 -0
  836. package/src/runtime/routes/integrations/slack/channel.ts +36 -6
  837. package/src/runtime/routes/integrations/slack/share.ts +45 -7
  838. package/src/runtime/routes/llm-context-normalization.ts +325 -0
  839. package/src/runtime/routes/memory-item-routes.test.ts +3 -2
  840. package/src/runtime/routes/migration-routes.ts +722 -91
  841. package/src/runtime/routes/settings-routes.ts +26 -7
  842. package/src/runtime/routes/skills-routes.ts +76 -7
  843. package/src/runtime/routes/stt-routes.ts +233 -0
  844. package/src/runtime/routes/surface-action-routes.ts +41 -2
  845. package/src/runtime/routes/trust-rules-routes.ts +30 -14
  846. package/src/runtime/routes/tts-routes.ts +108 -24
  847. package/src/runtime/routes/usage-routes.ts +30 -2
  848. package/src/runtime/routes/user-route-dispatcher.ts +50 -5
  849. package/src/runtime/routes/user-routes.ts +13 -1
  850. package/src/runtime/routes/work-items-routes.test.ts +1 -1
  851. package/src/runtime/routes/work-items-routes.ts +11 -3
  852. package/src/runtime/runtime-mode.ts +33 -0
  853. package/src/runtime/services/__tests__/analyze-conversation.test.ts +426 -0
  854. package/src/runtime/services/__tests__/analyze-deps-singleton.test.ts +67 -0
  855. package/src/runtime/services/__tests__/auto-analysis-prompt.test.ts +53 -0
  856. package/src/runtime/services/__tests__/manual-analysis-prompt.test.ts +41 -0
  857. package/src/runtime/services/analyze-conversation.ts +340 -0
  858. package/src/runtime/services/analyze-deps-singleton.ts +32 -0
  859. package/src/runtime/services/auto-analysis-prompt.ts +55 -0
  860. package/src/runtime/skill-route-registry.ts +71 -0
  861. package/src/runtime/slack-block-formatting.ts +437 -10
  862. package/src/schedule/scheduler.ts +58 -0
  863. package/src/security/__tests__/provider-key-env-fallback.test.ts +119 -0
  864. package/src/security/__tests__/untrusted-content.test.ts +109 -0
  865. package/src/security/oauth2.ts +122 -37
  866. package/src/security/secure-keys.ts +32 -10
  867. package/src/security/token-manager.ts +35 -13
  868. package/src/security/untrusted-content.ts +102 -0
  869. package/src/sequence/engine.ts +23 -0
  870. package/src/sequence/types.ts +1 -1
  871. package/src/skills/catalog-cache.ts +26 -7
  872. package/src/skills/catalog-files.ts +64 -2
  873. package/src/skills/catalog-install.ts +31 -3
  874. package/src/skills/category-inference.ts +122 -0
  875. package/src/skills/clawhub-files.ts +213 -0
  876. package/src/skills/clawhub.ts +84 -23
  877. package/src/skills/skill-cache-store.ts +97 -0
  878. package/src/skills/skill-file-provider.ts +40 -0
  879. package/src/skills/skillssh-files.ts +395 -0
  880. package/src/skills/skillssh-registry.ts +4 -4
  881. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +468 -0
  882. package/src/stt/__tests__/types.test.ts +89 -0
  883. package/src/stt/daemon-batch-transcriber.ts +228 -0
  884. package/src/stt/stt-stream-session.ts +506 -0
  885. package/src/stt/types.ts +334 -0
  886. package/src/stt/wav-encoder.test.ts +373 -0
  887. package/src/stt/wav-encoder.ts +175 -0
  888. package/src/subagent/manager.ts +79 -27
  889. package/src/tasks/ephemeral-permissions.ts +9 -4
  890. package/src/telemetry/usage-telemetry-reporter.ts +27 -5
  891. package/src/tools/browser/__tests__/browser-mode.test.ts +119 -0
  892. package/src/tools/browser/__tests__/browser-status.test.ts +166 -0
  893. package/src/tools/browser/browser-execution.ts +1208 -41
  894. package/src/tools/browser/browser-manager.ts +45 -0
  895. package/src/tools/browser/browser-mode-constants.ts +12 -0
  896. package/src/tools/browser/browser-mode.ts +92 -0
  897. package/src/tools/browser/browser-status-constants.ts +33 -0
  898. package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +393 -0
  899. package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +29 -0
  900. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +1648 -32
  901. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +264 -0
  902. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +205 -17
  903. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +254 -21
  904. package/src/tools/browser/cdp-client/errors.ts +15 -0
  905. package/src/tools/browser/cdp-client/extension-cdp-client.ts +39 -16
  906. package/src/tools/browser/cdp-client/factory.ts +797 -87
  907. package/src/tools/browser/cdp-client/index.ts +16 -2
  908. package/src/tools/browser/cdp-client/types.ts +68 -0
  909. package/src/tools/credentials/tool-policy.ts +39 -5
  910. package/src/tools/credentials/vault.ts +41 -7
  911. package/src/tools/executor.ts +4 -0
  912. package/src/tools/filesystem/write.ts +52 -0
  913. package/src/tools/host-terminal/host-shell.ts +45 -5
  914. package/src/tools/memory/register.test.ts +185 -0
  915. package/src/tools/memory/register.ts +3 -1
  916. package/src/tools/network/web-fetch.ts +25 -12
  917. package/src/tools/network/web-search.ts +20 -2
  918. package/src/tools/permission-checker.ts +36 -15
  919. package/src/tools/policy-context.ts +25 -8
  920. package/src/tools/registry.ts +55 -3
  921. package/src/tools/shared/shell-output.ts +3 -1
  922. package/src/tools/side-effects.ts +0 -9
  923. package/src/tools/skills/execute.ts +2 -2
  924. package/src/tools/skills/sandbox-runner.ts +6 -2
  925. package/src/tools/terminal/backends/native.ts +51 -2
  926. package/src/tools/terminal/safe-env.ts +11 -2
  927. package/src/tools/terminal/shell.ts +16 -4
  928. package/src/tools/tool-manifest.ts +6 -0
  929. package/src/tools/types.ts +29 -3
  930. package/src/tools/ui-surface/definitions.ts +6 -1
  931. package/src/tools/verification-control-plane-policy.ts +1 -1
  932. package/src/tts/__tests__/provider-adapters.test.ts +1061 -0
  933. package/src/tts/__tests__/provider-catalog-consistency.test.ts +196 -0
  934. package/src/tts/__tests__/provider-catalog.test.ts +183 -0
  935. package/src/tts/__tests__/provider-registry.test.ts +90 -0
  936. package/src/tts/provider-catalog.ts +219 -0
  937. package/src/tts/provider-registry.ts +73 -0
  938. package/src/tts/providers/deepgram-provider.ts +219 -0
  939. package/src/tts/providers/elevenlabs-provider.ts +211 -0
  940. package/src/tts/providers/fish-audio-provider.ts +183 -0
  941. package/src/tts/providers/index.ts +44 -0
  942. package/src/tts/providers/register-builtins.ts +130 -0
  943. package/src/tts/providers/xai-provider.ts +224 -0
  944. package/src/tts/synthesize-text.ts +110 -0
  945. package/src/tts/tts-config-resolver.ts +78 -0
  946. package/src/tts/types.ts +199 -0
  947. package/src/types/onboarding-context.ts +7 -0
  948. package/src/types/tar-stream.d.ts +66 -0
  949. package/src/util/abort-reasons.ts +58 -0
  950. package/src/util/device-id.ts +32 -16
  951. package/src/util/errors.ts +9 -1
  952. package/src/util/json.ts +17 -0
  953. package/src/util/platform.ts +56 -12
  954. package/src/util/pricing.ts +78 -5
  955. package/src/util/spawn.ts +1 -1
  956. package/src/util/truncate.ts +4 -2
  957. package/src/util/unicode.ts +201 -0
  958. package/src/version.ts +19 -24
  959. package/src/watcher/engine.ts +24 -1
  960. package/src/watcher/providers/google-calendar.ts +134 -8
  961. package/src/watcher/providers/outlook-calendar.ts +42 -2
  962. package/src/watcher/watcher-store.ts +31 -0
  963. package/src/workspace/git-service.ts +23 -4
  964. package/src/workspace/migrations/003-seed-device-id.ts +9 -3
  965. package/src/workspace/migrations/017-seed-persona-dirs.ts +68 -4
  966. package/src/workspace/migrations/029-seed-pkb.ts +1 -1
  967. package/src/workspace/migrations/031-drop-user-md.ts +317 -0
  968. package/src/workspace/migrations/031-llm-log-retention-zero-to-null.ts +73 -0
  969. package/src/workspace/migrations/032-tts-provider-unification.ts +227 -0
  970. package/src/workspace/migrations/033-stt-service-explicit-config.ts +122 -0
  971. package/src/workspace/migrations/034-remove-calls-voice-transcription-provider.ts +215 -0
  972. package/src/workspace/migrations/035-seed-slack-channel-persona.ts +50 -0
  973. package/src/workspace/migrations/036-update-pkb-index-bar.ts +37 -0
  974. package/src/workspace/migrations/037-create-meets-dir.ts +61 -0
  975. package/src/workspace/migrations/038-unify-llm-callsite-configs.ts +516 -0
  976. package/src/workspace/migrations/039-drop-legacy-llm-keys.ts +171 -0
  977. package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +154 -0
  978. package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +57 -0
  979. package/src/workspace/migrations/042-fix-backfill-google-gmail-settings-scope.ts +70 -0
  980. package/src/workspace/migrations/043-release-notes-latex-rendering.ts +75 -0
  981. package/src/workspace/migrations/044-bump-stale-provider-stream-timeout.ts +51 -0
  982. package/src/workspace/migrations/045-release-notes-meet-avatar.ts +130 -0
  983. package/src/workspace/migrations/AGENTS.md +1 -1
  984. package/src/workspace/migrations/registry.ts +32 -0
  985. package/src/workspace/provider-commit-message-generator.ts +19 -38
  986. package/src/workspace/top-level-renderer.ts +13 -1
  987. package/src/workspace/turn-commit.ts +31 -0
  988. package/src/__tests__/email-cli.test.ts +0 -297
  989. package/src/__tests__/email-service-config-fallback.test.ts +0 -102
  990. package/src/__tests__/outlook-attachments.test.ts +0 -301
  991. package/src/__tests__/outlook-automation-tools.test.ts +0 -425
  992. package/src/__tests__/outlook-categories.test.ts +0 -212
  993. package/src/__tests__/outlook-compose-tools.test.ts +0 -325
  994. package/src/__tests__/outlook-declutter-tools.test.ts +0 -585
  995. package/src/__tests__/outlook-follow-up.test.ts +0 -196
  996. package/src/__tests__/outlook-trash.test.ts +0 -77
  997. package/src/__tests__/outlook-unsubscribe.test.ts +0 -250
  998. package/src/__tests__/update-bulletin-format.test.ts +0 -122
  999. package/src/__tests__/update-bulletin-state.test.ts +0 -135
  1000. package/src/__tests__/update-bulletin.test.ts +0 -277
  1001. package/src/__tests__/update-template-contract.test.ts +0 -29
  1002. package/src/cli/commands/browser-relay.ts +0 -466
  1003. package/src/cli/commands/doctor.ts +0 -341
  1004. package/src/config/bundled-skills/browser/SKILL.md +0 -63
  1005. package/src/config/bundled-skills/browser/TOOLS.json +0 -393
  1006. package/src/config/bundled-skills/browser/tools/browser-click.ts +0 -12
  1007. package/src/config/bundled-skills/browser/tools/browser-close.ts +0 -12
  1008. package/src/config/bundled-skills/browser/tools/browser-extract.ts +0 -12
  1009. package/src/config/bundled-skills/browser/tools/browser-fill-credential.ts +0 -12
  1010. package/src/config/bundled-skills/browser/tools/browser-hover.ts +0 -12
  1011. package/src/config/bundled-skills/browser/tools/browser-navigate.ts +0 -12
  1012. package/src/config/bundled-skills/browser/tools/browser-press-key.ts +0 -12
  1013. package/src/config/bundled-skills/browser/tools/browser-screenshot.ts +0 -12
  1014. package/src/config/bundled-skills/browser/tools/browser-scroll.ts +0 -12
  1015. package/src/config/bundled-skills/browser/tools/browser-select-option.ts +0 -12
  1016. package/src/config/bundled-skills/browser/tools/browser-snapshot.ts +0 -12
  1017. package/src/config/bundled-skills/browser/tools/browser-type.ts +0 -12
  1018. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +0 -32
  1019. package/src/config/bundled-skills/browser/tools/browser-wait-for.ts +0 -12
  1020. package/src/config/bundled-skills/chatgpt-import/SKILL.md +0 -27
  1021. package/src/config/bundled-skills/chatgpt-import/TOOLS.json +0 -27
  1022. package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +0 -378
  1023. package/src/config/bundled-skills/gmail/SKILL.md +0 -175
  1024. package/src/config/bundled-skills/gmail/TOOLS.json +0 -558
  1025. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +0 -149
  1026. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +0 -112
  1027. package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +0 -44
  1028. package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +0 -81
  1029. package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +0 -108
  1030. package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +0 -146
  1031. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +0 -53
  1032. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +0 -220
  1033. package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +0 -26
  1034. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +0 -251
  1035. package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +0 -29
  1036. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +0 -122
  1037. package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +0 -67
  1038. package/src/config/bundled-skills/gmail/tools/scan-result-store.ts +0 -100
  1039. package/src/config/bundled-skills/gmail/tools/shared.ts +0 -47
  1040. package/src/config/bundled-skills/google-calendar/SKILL.md +0 -51
  1041. package/src/config/bundled-skills/google-calendar/TOOLS.json +0 -226
  1042. package/src/config/bundled-skills/google-calendar/calendar-client.ts +0 -223
  1043. package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +0 -27
  1044. package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +0 -48
  1045. package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +0 -19
  1046. package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +0 -36
  1047. package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +0 -58
  1048. package/src/config/bundled-skills/google-calendar/tools/shared.ts +0 -17
  1049. package/src/config/bundled-skills/google-calendar/types.ts +0 -97
  1050. package/src/config/bundled-skills/outlook/SKILL.md +0 -196
  1051. package/src/config/bundled-skills/outlook/TOOLS.json +0 -530
  1052. package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +0 -85
  1053. package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +0 -77
  1054. package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +0 -84
  1055. package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +0 -94
  1056. package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +0 -49
  1057. package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +0 -237
  1058. package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +0 -161
  1059. package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +0 -32
  1060. package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +0 -272
  1061. package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +0 -29
  1062. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +0 -129
  1063. package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +0 -87
  1064. package/src/config/bundled-skills/outlook/tools/shared.ts +0 -20
  1065. package/src/config/bundled-skills/outlook-calendar/SKILL.md +0 -51
  1066. package/src/config/bundled-skills/outlook-calendar/TOOLS.json +0 -221
  1067. package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +0 -252
  1068. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +0 -53
  1069. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +0 -74
  1070. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +0 -18
  1071. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +0 -46
  1072. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +0 -36
  1073. package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +0 -17
  1074. package/src/config/bundled-skills/outlook-calendar/types.ts +0 -120
  1075. package/src/config/bundled-skills/slack/SKILL.md +0 -107
  1076. package/src/config/bundled-skills/tasks/SKILL.md +0 -37
  1077. package/src/config/bundled-skills/tasks/TOOLS.json +0 -353
  1078. package/src/config/bundled-skills/tasks/icon.svg +0 -34
  1079. package/src/config/bundled-skills/tasks/tools/task-delete.ts +0 -12
  1080. package/src/config/bundled-skills/tasks/tools/task-list-add.ts +0 -12
  1081. package/src/config/bundled-skills/tasks/tools/task-list-remove.ts +0 -12
  1082. package/src/config/bundled-skills/tasks/tools/task-list-show.ts +0 -12
  1083. package/src/config/bundled-skills/tasks/tools/task-list-update.ts +0 -12
  1084. package/src/config/bundled-skills/tasks/tools/task-list.ts +0 -12
  1085. package/src/config/bundled-skills/tasks/tools/task-queue-run.ts +0 -12
  1086. package/src/config/bundled-skills/tasks/tools/task-run.ts +0 -12
  1087. package/src/config/bundled-skills/tasks/tools/task-save.ts +0 -12
  1088. package/src/config/bundled-skills/watcher/SKILL.md +0 -31
  1089. package/src/config/bundled-skills/watcher/TOOLS.json +0 -167
  1090. package/src/config/bundled-skills/watcher/tools/watcher-create.ts +0 -12
  1091. package/src/config/bundled-skills/watcher/tools/watcher-delete.ts +0 -12
  1092. package/src/config/bundled-skills/watcher/tools/watcher-digest.ts +0 -12
  1093. package/src/config/bundled-skills/watcher/tools/watcher-list.ts +0 -12
  1094. package/src/config/bundled-skills/watcher/tools/watcher-update.ts +0 -12
  1095. package/src/email/guardrails.ts +0 -221
  1096. package/src/email/provider.ts +0 -117
  1097. package/src/email/providers/agentmail.ts +0 -361
  1098. package/src/email/providers/index.ts +0 -65
  1099. package/src/email/service.ts +0 -384
  1100. package/src/email/types.ts +0 -126
  1101. package/src/prompts/templates/UPDATES.md +0 -38
  1102. package/src/prompts/templates/USER.md +0 -13
  1103. package/src/prompts/update-bulletin-format.ts +0 -68
  1104. package/src/prompts/update-bulletin-state.ts +0 -58
  1105. package/src/prompts/update-bulletin-template-path.ts +0 -13
  1106. package/src/prompts/update-bulletin.ts +0 -128
  1107. package/src/providers/speech-to-text/types.ts +0 -17
  1108. package/src/runtime/routes/browser-cdp-routes.ts +0 -229
  1109. package/src/shared/provider-env-vars.ts +0 -19
  1110. package/src/tools/watcher/create.ts +0 -86
  1111. package/src/tools/watcher/delete.ts +0 -36
  1112. package/src/tools/watcher/digest.ts +0 -54
  1113. package/src/tools/watcher/list.ts +0 -83
  1114. 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,12 +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
- "\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;
616
650
 
617
651
  /**
618
652
  * Read `_autoinject.md` from the PKB directory and return the list of
@@ -638,6 +672,21 @@ export function readAutoinjectList(pkbDir: string): string[] | null {
638
672
  }
639
673
  }
640
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
+
641
690
  /**
642
691
  * Read the always-loaded PKB files and append a nudge encouraging the
643
692
  * assistant to proactively read topic files and use `remember` aggressively.
@@ -652,7 +701,7 @@ export function readPkbContext(): string | null {
652
701
  const pkbDir = join(getWorkspaceDir(), "pkb");
653
702
  if (!existsSync(pkbDir)) return null;
654
703
 
655
- const filesToInject = readAutoinjectList(pkbDir) ?? PKB_DEFAULT_FILES;
704
+ const filesToInject = getPkbAutoInjectList(pkbDir);
656
705
 
657
706
  const parts: string[] = [];
658
707
  for (const file of filesToInject) {
@@ -685,10 +734,13 @@ export function readPkbContext(): string | null {
685
734
  */
686
735
  export function injectPkbContext(message: Message, content: string): Message {
687
736
  // Escape closing tags that could break out of the XML wrapper
688
- 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
+ );
689
741
  const pkbBlock = {
690
742
  type: "text" as const,
691
- text: `<pkb>\n${escaped}\n</pkb>`,
743
+ text: `<knowledge_base>\n${escaped}\n</knowledge_base>`,
692
744
  };
693
745
 
694
746
  // Find insertion point: skip any leading memory/image blocks
@@ -720,9 +772,12 @@ export function injectPkbContext(message: Message, content: string): Message {
720
772
  };
721
773
  }
722
774
 
723
- /** Strip `<pkb>` blocks injected by `injectPkbContext`. */
775
+ /** Strip `<knowledge_base>` blocks injected by `injectPkbContext`. */
724
776
  export function stripPkbContext(messages: Message[]): Message[] {
725
- return stripUserTextBlocksByPrefix(messages, ["<pkb>"]);
777
+ return stripUserTextBlocksByPrefix(messages, [
778
+ "<knowledge_base>",
779
+ "<pkb>", // backward-compat: strip legacy blocks from pre-rename history
780
+ ]);
726
781
  }
727
782
 
728
783
  /**
@@ -876,6 +931,12 @@ export interface UnifiedTurnContextOptions {
876
931
  interfaceName?: string;
877
932
  channelName?: string;
878
933
  actorContext?: InboundActorContext | null;
934
+ /**
935
+ * Human-readable duration since the previous user message (e.g. "14h ago",
936
+ * "yesterday", "3d ago"). Only populated when the gap exceeds 12 hours so
937
+ * the model can acknowledge long absences; otherwise omitted.
938
+ */
939
+ timeSinceLastMessage?: string | null;
879
940
  }
880
941
 
881
942
  /**
@@ -902,12 +963,19 @@ export function buildUnifiedTurnContextBlock(
902
963
  .replace(/[\r\n\u0085\u2028\u2029]+/g, " ")
903
964
  // Replace remaining ASCII C0/C1 control characters and DEL.
904
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;")
905
970
  .trim();
906
971
  return singleLine.length > 0 ? singleLine : "unknown";
907
972
  };
908
973
 
909
974
  const lines: string[] = ["<turn_context>"];
910
975
  lines.push(`current_time: ${options.timestamp}`);
976
+ if (options.timeSinceLastMessage) {
977
+ lines.push(`time_since_last_message: ${options.timeSinceLastMessage}`);
978
+ }
911
979
  if (options.interfaceName) {
912
980
  lines.push(`interface: ${options.interfaceName}`);
913
981
  }
@@ -1128,6 +1196,433 @@ export function stripTransportHints(messages: Message[]): Message[] {
1128
1196
  return stripUserTextBlocksByPrefix(messages, ["<transport_hints>"]);
1129
1197
  }
1130
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
+
1131
1626
  /** Prefixes stripped by the pipeline (order doesn't matter — single pass). */
1132
1627
  const RUNTIME_INJECTION_PREFIXES = [
1133
1628
  "<channel_capabilities>",
@@ -1154,11 +1649,18 @@ const RUNTIME_INJECTION_PREFIXES = [
1154
1649
  "<active_workspace>",
1155
1650
  "<active_dynamic_page>",
1156
1651
  "<non_interactive_context>",
1157
- "<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",
1158
1655
  "<now_scratchpad>", // backward-compat: strip legacy blocks from pre-rename history
1159
- "<pkb>",
1656
+ "<knowledge_base>",
1657
+ "<pkb>", // backward-compat: strip legacy tag from pre-rename history
1160
1658
  "<system_reminder>",
1161
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>",
1162
1664
  "<system_notice>One or more tool calls returned an error.",
1163
1665
  ];
1164
1666
 
@@ -1179,16 +1681,23 @@ export function stripInjectionsForCompaction(messages: Message[]): Message[] {
1179
1681
  * Returns null if no NOW.md injection is found.
1180
1682
  */
1181
1683
  export function findLastInjectedNowContent(messages: Message[]): string | null {
1182
- 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";
1183
1688
  const suffix = "\n</NOW.md>";
1184
1689
  for (let i = messages.length - 1; i >= 0; i--) {
1185
1690
  const msg = messages[i];
1186
1691
  if (msg.role !== "user") continue;
1187
1692
  for (const block of msg.content) {
1188
- if (block.type === "text" && block.text.startsWith(prefix)) {
1189
- const end = block.text.lastIndexOf(suffix);
1190
- if (end > prefix.length) return block.text.slice(prefix.length, end);
1693
+ if (block.type !== "text" || !block.text.startsWith(openTagPrefix)) {
1694
+ continue;
1191
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);
1192
1701
  }
1193
1702
  }
1194
1703
  return null;
@@ -1205,13 +1714,29 @@ export function findLastInjectedNowContent(messages: Message[]): string | null {
1205
1714
  */
1206
1715
  export type InjectionMode = "full" | "minimal";
1207
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
+
1208
1732
  /**
1209
1733
  * Apply a chain of user-message injections to `runMessages`.
1210
1734
  *
1211
1735
  * Each injection is optional — pass `null`/`undefined` to skip it.
1212
- * 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).
1213
1738
  */
1214
- export function applyRuntimeInjections(
1739
+ export async function applyRuntimeInjections(
1215
1740
  runMessages: Message[],
1216
1741
  options: {
1217
1742
  activeSurface?: ActiveSurfaceContext | null;
@@ -1222,15 +1747,114 @@ export function applyRuntimeInjections(
1222
1747
  voiceCallControlPrompt?: string | null;
1223
1748
  pkbContext?: string | null;
1224
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;
1225
1777
  nowScratchpad?: string | null;
1226
1778
  subagentStatusBlock?: string | null;
1227
1779
  isNonInteractive?: boolean;
1228
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;
1229
1815
  mode?: InjectionMode;
1230
1816
  },
1231
- ): Message[] {
1817
+ ): Promise<RuntimeInjectionResult> {
1232
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;
1233
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
+ }
1234
1858
 
1235
1859
  // For non-interactive conversations (scheduled jobs, work items), instruct the
1236
1860
  // model to never ask for clarification — there is no human present to answer.
@@ -1274,17 +1898,90 @@ export function applyRuntimeInjections(
1274
1898
  }
1275
1899
 
1276
1900
  // PKB behavioral nudge — injected on every turn when PKB is active so
1277
- // 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.
1278
1905
  if (mode === "full" && options.pkbActive) {
1279
1906
  const userTail = result[result.length - 1];
1280
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);
1281
1977
  result = [
1282
1978
  ...result.slice(0, -1),
1283
1979
  {
1284
1980
  ...userTail,
1285
1981
  content: [
1286
- ...userTail.content,
1287
- { 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),
1288
1985
  ],
1289
1986
  },
1290
1987
  ];
@@ -1344,6 +2041,7 @@ export function applyRuntimeInjections(
1344
2041
  if (options.unifiedTurnContext) {
1345
2042
  const userTail = result[result.length - 1];
1346
2043
  if (userTail && userTail.role === "user") {
2044
+ turnContextCaptured = options.unifiedTurnContext;
1347
2045
  result = [
1348
2046
  ...result.slice(0, -1),
1349
2047
  {
@@ -1357,8 +2055,15 @@ export function applyRuntimeInjections(
1357
2055
  }
1358
2056
  }
1359
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.
1360
2064
  if (
1361
2065
  mode === "full" &&
2066
+ !slackConversation &&
1362
2067
  options.transportHints &&
1363
2068
  options.transportHints.length > 0
1364
2069
  ) {
@@ -1371,6 +2076,36 @@ export function applyRuntimeInjections(
1371
2076
  }
1372
2077
  }
1373
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
+
1374
2109
  // Workspace top-level context is injected last so it appears first
1375
2110
  // (prepended) in the user message content, keeping cache breakpoints
1376
2111
  // anchored to the trailing blocks.
@@ -1387,5 +2122,11 @@ export function applyRuntimeInjections(
1387
2122
  }
1388
2123
  }
1389
2124
 
1390
- return result;
2125
+ return {
2126
+ messages: result,
2127
+ blocks: {
2128
+ unifiedTurnContext: turnContextCaptured,
2129
+ pkbSystemReminder: pkbSystemReminderCaptured,
2130
+ },
2131
+ };
1391
2132
  }