@vellumai/assistant 0.7.0 → 0.7.1

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 (666) hide show
  1. package/ARCHITECTURE.md +6 -7
  2. package/Dockerfile +1 -0
  3. package/README.md +2 -2
  4. package/__tests__/permissions/gateway-threshold-reader.test.ts +79 -139
  5. package/bun.lock +3 -0
  6. package/docs/architecture/security.md +18 -16
  7. package/knip.json +1 -0
  8. package/node_modules/@vellumai/skill-host-contracts/__tests__/client.test.ts +1 -5
  9. package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +0 -5
  10. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +10 -16
  11. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +1 -9
  12. package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +12 -12
  13. package/node_modules/@vellumai/slack-text/bun.lock +24 -0
  14. package/node_modules/@vellumai/slack-text/package.json +18 -0
  15. package/node_modules/@vellumai/slack-text/src/index.test.ts +153 -0
  16. package/node_modules/@vellumai/slack-text/src/index.ts +235 -0
  17. package/node_modules/@vellumai/slack-text/tsconfig.json +20 -0
  18. package/openapi.yaml +294 -107
  19. package/package.json +4 -2
  20. package/scripts/generate-openapi.ts +16 -111
  21. package/src/__tests__/agent-wake-override-profile.test.ts +23 -1
  22. package/src/__tests__/anthropic-provider.test.ts +56 -13
  23. package/src/__tests__/app-conversation-ids-backfill.test.ts +278 -0
  24. package/src/__tests__/app-conversation-ids.test.ts +151 -0
  25. package/src/__tests__/approval-cascade.test.ts +0 -15
  26. package/src/__tests__/approval-routes-http.test.ts +6 -17
  27. package/src/__tests__/assistant-event-hub.test.ts +126 -77
  28. package/src/__tests__/assistant-event.test.ts +0 -5
  29. package/src/__tests__/assistant-events-sse-hardening.test.ts +37 -15
  30. package/src/__tests__/assistant-feature-flags-integration.test.ts +0 -29
  31. package/src/__tests__/background-shell-host-bash.test.ts +34 -43
  32. package/src/__tests__/call-controller.test.ts +1 -1
  33. package/src/__tests__/call-site-routing-provider.test.ts +193 -0
  34. package/src/__tests__/channel-approval-routes.test.ts +10 -296
  35. package/src/__tests__/channel-approvals.test.ts +25 -17
  36. package/src/__tests__/channel-guardian.test.ts +100 -146
  37. package/src/__tests__/checker.test.ts +20 -34
  38. package/src/__tests__/compact-event-conversation-id-guard.test.ts +50 -0
  39. package/src/__tests__/compaction-events.test.ts +2 -0
  40. package/src/__tests__/config-schema.test.ts +6 -48
  41. package/src/__tests__/config-watcher.test.ts +12 -0
  42. package/src/__tests__/connection-policy.test.ts +1 -52
  43. package/src/__tests__/contacts-write.test.ts +2 -64
  44. package/src/__tests__/context-image-dimensions.test.ts +1 -1
  45. package/src/__tests__/context-search-memory-source.test.ts +120 -1
  46. package/src/__tests__/context-search-memory-v2-source.test.ts +383 -0
  47. package/src/__tests__/context-search-pkb-source.test.ts +49 -0
  48. package/src/__tests__/context-search-workspace-source.test.ts +9 -22
  49. package/src/__tests__/context-window-manager.test.ts +46 -0
  50. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +2 -0
  51. package/src/__tests__/conversation-agent-loop-overflow.test.ts +102 -29
  52. package/src/__tests__/conversation-agent-loop.test.ts +980 -13
  53. package/src/__tests__/conversation-analysis-routes.test.ts +12 -10
  54. package/src/__tests__/conversation-attention-telegram.test.ts +11 -3
  55. package/src/__tests__/conversation-confirmation-signals.test.ts +0 -291
  56. package/src/__tests__/conversation-history-web-search.test.ts +4 -3
  57. package/src/__tests__/conversation-inference-profile-route.test.ts +12 -23
  58. package/src/__tests__/conversation-lifecycle.test.ts +4 -4
  59. package/src/__tests__/conversation-process-callsite.test.ts +79 -2
  60. package/src/__tests__/conversation-queue.test.ts +3 -8
  61. package/src/__tests__/conversation-routes-disk-view.test.ts +1 -161
  62. package/src/__tests__/conversation-routes-guardian-reply.test.ts +0 -32
  63. package/src/__tests__/conversation-routes-slash-commands.test.ts +75 -66
  64. package/src/__tests__/conversation-runtime-assembly.test.ts +257 -3
  65. package/src/__tests__/conversation-slash-commands.test.ts +24 -4
  66. package/src/__tests__/conversation-slash-queue.test.ts +2 -0
  67. package/src/__tests__/conversation-speed-override.test.ts +0 -3
  68. package/src/__tests__/conversation-starter-routes.test.ts +79 -2
  69. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -5
  70. package/src/__tests__/conversation-surfaces-standalone.test.ts +18 -14
  71. package/src/__tests__/conversation-surfaces-state-update.test.ts +3 -2
  72. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +8 -46
  73. package/src/__tests__/conversation-usage.test.ts +253 -3
  74. package/src/__tests__/credential-execution-shell-lockdown.test.ts +0 -39
  75. package/src/__tests__/credential-health-service.test.ts +68 -0
  76. package/src/__tests__/credential-security-e2e.test.ts +4 -3
  77. package/src/__tests__/credential-security-invariants.test.ts +1 -5
  78. package/src/__tests__/credential-token-resolver.test.ts +180 -0
  79. package/src/__tests__/cu-unified-flow.test.ts +33 -16
  80. package/src/__tests__/daemon-assistant-events.test.ts +34 -21
  81. package/src/__tests__/daemon-credential-client.test.ts +4 -1
  82. package/src/__tests__/db-connection-isolation.test.ts +125 -0
  83. package/src/__tests__/db-migration-rollback.test.ts +101 -0
  84. package/src/__tests__/db-slack-compaction-watermark-migration.test.ts +169 -0
  85. package/src/__tests__/deterministic-verification-control-plane.test.ts +7 -80
  86. package/src/__tests__/document-conversations.test.ts +332 -0
  87. package/src/__tests__/embedding-managed-proxy-selection.test.ts +2 -2
  88. package/src/__tests__/emit-event-signal.test.ts +4 -6
  89. package/src/__tests__/events-client-registration.test.ts +193 -49
  90. package/src/__tests__/filing-service.test.ts +58 -7
  91. package/src/__tests__/first-greeting.test.ts +156 -150
  92. package/src/__tests__/fixtures/mock-chrome-extension.ts +108 -66
  93. package/src/__tests__/get-skill-detail-audit.test.ts +3 -8
  94. package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
  95. package/src/__tests__/guardian-dispatch.test.ts +1 -1
  96. package/src/__tests__/guardian-grant-minting.test.ts +7 -2
  97. package/src/__tests__/guardian-routing-invariants.test.ts +7 -2
  98. package/src/__tests__/guardian-routing-state.test.ts +1 -1
  99. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +32 -11
  100. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +2 -83
  101. package/src/__tests__/headless-browser-mode.test.ts +4 -9
  102. package/src/__tests__/headless-browser-navigate.test.ts +21 -20
  103. package/src/__tests__/heartbeat-service.test.ts +289 -7
  104. package/src/__tests__/helpers/channel-test-adapter.ts +2 -2
  105. package/src/__tests__/helpers/create-guardian-binding.ts +91 -0
  106. package/src/__tests__/host-bash-proxy.test.ts +46 -122
  107. package/src/__tests__/host-browser-e2e-cloud.test.ts +36 -497
  108. package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +26 -96
  109. package/src/__tests__/host-browser-proxy.test.ts +111 -185
  110. package/src/__tests__/host-browser-routes.test.ts +45 -75
  111. package/src/__tests__/host-browser-ws-events-e2e.test.ts +26 -30
  112. package/src/__tests__/host-cu-proxy.test.ts +56 -111
  113. package/src/__tests__/host-file-proxy.test.ts +44 -98
  114. package/src/__tests__/host-file-read-tool.test.ts +42 -21
  115. package/src/__tests__/host-shell-tool.test.ts +33 -68
  116. package/src/__tests__/host-transfer-pending-interactions.test.ts +2 -18
  117. package/src/__tests__/host-transfer-proxy.test.ts +43 -53
  118. package/src/__tests__/http-user-message-parity.test.ts +0 -6
  119. package/src/__tests__/inbound-slack-persistence.test.ts +31 -0
  120. package/src/__tests__/injector-chain.test.ts +10 -5
  121. package/src/__tests__/injector-pkb-v2-silenced.test.ts +124 -0
  122. package/src/__tests__/inline-command-runner.test.ts +0 -66
  123. package/src/__tests__/inline-skill-load-permissions.test.ts +0 -2
  124. package/src/__tests__/install-skill-routing.test.ts +1 -13
  125. package/src/__tests__/llm-callsite-catalog.test.ts +34 -0
  126. package/src/__tests__/llm-catalog-parity.test.ts +90 -0
  127. package/src/__tests__/llm-context-resolution.test.ts +180 -0
  128. package/src/__tests__/llm-resolver.test.ts +80 -12
  129. package/src/__tests__/llm-usage-store.test.ts +269 -4
  130. package/src/__tests__/log-export-routes.test.ts +89 -0
  131. package/src/__tests__/managed-profile-guard.test.ts +225 -0
  132. package/src/__tests__/managed-skill-lifecycle.test.ts +0 -10
  133. package/src/__tests__/manual-token-reconciliation.test.ts +334 -0
  134. package/src/__tests__/memory-v2-static-injector.test.ts +95 -0
  135. package/src/__tests__/migration-cross-version-compatibility.test.ts +197 -291
  136. package/src/__tests__/migration-export-http.test.ts +33 -26
  137. package/src/__tests__/migration-export-streaming.test.ts +18 -10
  138. package/src/__tests__/migration-export-to-gcs.test.ts +49 -9
  139. package/src/__tests__/migration-import-commit-http.test.ts +66 -21
  140. package/src/__tests__/migration-import-from-gcs.test.ts +50 -9
  141. package/src/__tests__/migration-import-from-url.test.ts +20 -6
  142. package/src/__tests__/migration-import-preflight-http.test.ts +95 -95
  143. package/src/__tests__/migration-parity-persistence.test.ts +62 -25
  144. package/src/__tests__/migration-transport.test.ts +115 -23
  145. package/src/__tests__/migration-validate-http.test.ts +105 -80
  146. package/src/__tests__/migration-wizard.test.ts +133 -27
  147. package/src/__tests__/non-member-access-request.test.ts +1 -1
  148. package/src/__tests__/notification-guardian-path.test.ts +1 -1
  149. package/src/__tests__/oauth-store.test.ts +19 -0
  150. package/src/__tests__/platform-bash-auto-approve.test.ts +21 -12
  151. package/src/__tests__/prechat-onboarding-contract.test.ts +31 -7
  152. package/src/__tests__/pricing.test.ts +68 -4
  153. package/src/__tests__/process-message-background-slack.test.ts +331 -0
  154. package/src/__tests__/provider-managed-proxy-integration.test.ts +153 -17
  155. package/src/__tests__/provider-send-message-override-profile.test.ts +50 -0
  156. package/src/__tests__/provider-usage-tracking.test.ts +208 -0
  157. package/src/__tests__/reaction-persistence.test.ts +9 -6
  158. package/src/__tests__/rebind-secrets-screen.test.ts +53 -16
  159. package/src/__tests__/recording-handler.test.ts +64 -81
  160. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +4 -3
  161. package/src/__tests__/relay-server.test.ts +18 -13
  162. package/src/__tests__/require-fresh-approval.test.ts +13 -22
  163. package/src/__tests__/runtime-attachment-metadata.test.ts +1 -1
  164. package/src/__tests__/runtime-events-sse-parity.test.ts +3 -4
  165. package/src/__tests__/runtime-events-sse.test.ts +3 -12
  166. package/src/__tests__/search-skills-unified.test.ts +9 -15
  167. package/src/__tests__/secret-ingress-cli.test.ts +2 -5
  168. package/src/__tests__/secret-ingress-http.test.ts +0 -4
  169. package/src/__tests__/secret-onetime-send.test.ts +4 -2
  170. package/src/__tests__/secret-prompt-log-hygiene.test.ts +24 -7
  171. package/src/__tests__/secret-prompter-channel-fallback.test.ts +42 -47
  172. package/src/__tests__/secret-response-routing.test.ts +29 -15
  173. package/src/__tests__/secret-routes-managed-proxy.test.ts +5 -1
  174. package/src/__tests__/secret-scanner.test.ts +2 -545
  175. package/src/__tests__/send-endpoint-busy.test.ts +9 -24
  176. package/src/__tests__/settings-routes.test.ts +1 -1
  177. package/src/__tests__/shell-credential-ref.test.ts +0 -8
  178. package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -56
  179. package/src/__tests__/skill-script-runner-sandbox.test.ts +0 -11
  180. package/src/__tests__/skill-tool-factory.test.ts +97 -0
  181. package/src/__tests__/skills-file-content-endpoint.test.ts +9 -30
  182. package/src/__tests__/skills-files-catalog-fallback.test.ts +11 -17
  183. package/src/__tests__/slack-inbound-verification.test.ts +1 -62
  184. package/src/__tests__/subagent-fork-notifications.test.ts +57 -47
  185. package/src/__tests__/subagent-manager-notify.test.ts +70 -70
  186. package/src/__tests__/subagent-notify-parent.test.ts +80 -83
  187. package/src/__tests__/system-prompt.test.ts +115 -13
  188. package/src/__tests__/terminal-tools.test.ts +0 -89
  189. package/src/__tests__/thread-backfill.test.ts +945 -31
  190. package/src/__tests__/tool-domain-event-publisher.test.ts +0 -36
  191. package/src/__tests__/tool-execute-pipeline.test.ts +0 -6
  192. package/src/__tests__/tool-execution-abort-cleanup.test.ts +0 -16
  193. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +9 -19
  194. package/src/__tests__/tool-executor-lifecycle-events.test.ts +4 -7
  195. package/src/__tests__/tool-executor.test.ts +12 -19
  196. package/src/__tests__/tool-metrics-listener.test.ts +0 -35
  197. package/src/__tests__/tool-side-effects-slack-dm.test.ts +1 -0
  198. package/src/__tests__/tool-trace-listener.test.ts +0 -17
  199. package/src/__tests__/transfer-progress-screen.test.ts +63 -26
  200. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +2 -149
  201. package/src/__tests__/trusted-contact-multichannel.test.ts +2 -4
  202. package/src/__tests__/trusted-contact-verification.test.ts +1 -1
  203. package/src/__tests__/tts-catalog-parity.test.ts +16 -5
  204. package/src/__tests__/usage-attribution.test.ts +247 -0
  205. package/src/__tests__/usage-cli.test.ts +143 -0
  206. package/src/__tests__/usage-grouped-buckets.test.ts +155 -0
  207. package/src/__tests__/usage-routes.test.ts +150 -0
  208. package/src/__tests__/validation-results-screen.test.ts +39 -16
  209. package/src/__tests__/vbundle-pax-and-symlink.test.ts +12 -3
  210. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +49 -137
  211. package/src/__tests__/verification-control-plane-policy.test.ts +4 -7
  212. package/src/__tests__/voice-session-bridge.test.ts +5 -5
  213. package/src/__tests__/workspace-migration-062-drop-memory-v2-edges-json.test.ts +103 -0
  214. package/src/__tests__/workspace-migration-063-release-notes-dynamic-model-context.test.ts +77 -0
  215. package/src/__tests__/workspace-migration-064-unwind-main-agent-opus-seed.test.ts +225 -0
  216. package/src/__tests__/workspace-migration-memory-v2-init.test.ts +8 -30
  217. package/src/acp/index.ts +0 -15
  218. package/src/acp/session-manager.ts +37 -34
  219. package/src/agent/loop.ts +16 -1
  220. package/src/approvals/AGENTS.md +4 -0
  221. package/src/approvals/__tests__/guardian-feed-event.test.ts +10 -3
  222. package/src/approvals/guardian-request-resolvers.ts +10 -2
  223. package/src/backup/__tests__/backup-worker.test.ts +36 -8
  224. package/src/backup/__tests__/paths.test.ts +2 -2
  225. package/src/backup/__tests__/restore.test.ts +45 -28
  226. package/src/backup/backup-worker.ts +36 -2
  227. package/src/backup/paths.ts +9 -6
  228. package/src/browser-session/events.ts +0 -9
  229. package/src/calls/call-store.ts +1 -34
  230. package/src/calls/guardian-question-copy.ts +0 -108
  231. package/src/calls/relay-server.ts +0 -24
  232. package/src/calls/twilio-rest.ts +0 -38
  233. package/src/calls/twilio-routes.ts +1 -1
  234. package/src/calls/voice-session-bridge.ts +7 -38
  235. package/src/channels/types.ts +1 -36
  236. package/src/cli/commands/__tests__/cache.test.ts +152 -5
  237. package/src/cli/commands/__tests__/memory-v2.test.ts +14 -28
  238. package/src/cli/commands/__tests__/trust.test.ts +21 -387
  239. package/src/cli/commands/backup.ts +4 -4
  240. package/src/cli/commands/cache-fs.ts +8 -0
  241. package/src/cli/commands/cache.ts +153 -82
  242. package/src/cli/commands/clients.ts +63 -5
  243. package/src/cli/commands/completions.ts +3 -3
  244. package/src/cli/commands/contacts.ts +231 -76
  245. package/src/cli/commands/keys.ts +4 -1
  246. package/src/cli/commands/memory-v2.ts +24 -52
  247. package/src/cli/commands/oauth/shared.ts +2 -29
  248. package/src/cli/commands/pending.ts +102 -0
  249. package/src/cli/commands/skills.ts +77 -35
  250. package/src/cli/commands/trust.ts +70 -430
  251. package/src/cli/commands/usage.ts +25 -16
  252. package/src/cli/lib/daemon-credential-client.ts +14 -0
  253. package/src/cli/program.ts +2 -0
  254. package/src/cli.ts +0 -21
  255. package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
  256. package/src/config/bundled-skills/messaging/TOOLS.json +14 -4
  257. package/src/config/env-registry.ts +12 -2
  258. package/src/config/env.ts +3 -14
  259. package/src/config/feature-flag-registry.json +30 -30
  260. package/src/config/llm-callsite-catalog.ts +12 -0
  261. package/src/config/llm-context-resolution.ts +80 -0
  262. package/src/config/llm-resolver.ts +58 -22
  263. package/src/config/loader.ts +3 -3
  264. package/src/config/schema.ts +2 -158
  265. package/src/config/schemas/__tests__/memory-v2.test.ts +1 -0
  266. package/src/config/schemas/call-site-catalog.ts +271 -0
  267. package/src/config/schemas/calls.ts +5 -5
  268. package/src/config/schemas/inference.ts +1 -1
  269. package/src/config/schemas/ingress.ts +1 -1
  270. package/src/config/schemas/llm.ts +31 -3
  271. package/src/config/schemas/memory-retrieval.ts +2 -2
  272. package/src/config/schemas/memory-v2.ts +9 -0
  273. package/src/config/schemas/security.ts +1 -42
  274. package/src/config/schemas/services.ts +6 -6
  275. package/src/config/schemas/skills.ts +5 -5
  276. package/src/config/schemas/tts.ts +1 -1
  277. package/src/config/seed-inference-profiles.ts +117 -0
  278. package/src/config/skills.ts +0 -90
  279. package/src/config/types.ts +3 -6
  280. package/src/contacts/contact-store.ts +0 -17
  281. package/src/contacts/contacts-write.ts +1 -105
  282. package/src/context/window-manager.ts +44 -5
  283. package/src/credential-execution/process-manager.ts +34 -10
  284. package/src/credential-health/credential-health-service.ts +21 -16
  285. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +75 -82
  286. package/src/daemon/__tests__/daemon-skill-host.test.ts +2 -9
  287. package/src/daemon/connection-policy.ts +1 -26
  288. package/src/daemon/conversation-agent-loop-handlers.ts +53 -4
  289. package/src/daemon/conversation-agent-loop.ts +277 -36
  290. package/src/daemon/conversation-history.ts +8 -8
  291. package/src/daemon/conversation-launch.ts +20 -135
  292. package/src/daemon/conversation-lifecycle.ts +1 -1
  293. package/src/daemon/conversation-messaging.ts +1 -0
  294. package/src/daemon/conversation-process.ts +83 -163
  295. package/src/daemon/conversation-runtime-assembly.ts +219 -76
  296. package/src/daemon/conversation-slash.ts +47 -5
  297. package/src/daemon/conversation-store.ts +7 -31
  298. package/src/daemon/conversation-surfaces.ts +22 -28
  299. package/src/daemon/conversation-tool-setup.ts +3 -33
  300. package/src/daemon/conversation-usage.ts +36 -0
  301. package/src/daemon/conversation.ts +117 -233
  302. package/src/daemon/daemon-control.ts +3 -71
  303. package/src/daemon/daemon-skill-host.ts +8 -11
  304. package/src/daemon/dictation-profile-store.ts +2 -26
  305. package/src/daemon/first-greeting.ts +44 -156
  306. package/src/daemon/handlers/config-channels.ts +12 -12
  307. package/src/daemon/handlers/config-ingress.ts +4 -165
  308. package/src/daemon/handlers/config-model.ts +1 -1
  309. package/src/daemon/handlers/config-voice.ts +0 -42
  310. package/src/daemon/handlers/conversations.ts +11 -190
  311. package/src/daemon/handlers/recording.ts +26 -158
  312. package/src/daemon/handlers/shared.ts +23 -71
  313. package/src/daemon/handlers/skills.ts +42 -93
  314. package/src/daemon/host-bash-proxy.ts +67 -45
  315. package/src/daemon/host-browser-proxy.ts +65 -27
  316. package/src/daemon/host-cu-proxy.ts +40 -39
  317. package/src/daemon/host-file-proxy.ts +58 -37
  318. package/src/daemon/host-transfer-proxy.ts +84 -46
  319. package/src/daemon/lifecycle.ts +49 -15
  320. package/src/daemon/message-types/conversations.ts +7 -0
  321. package/src/daemon/message-types/host-bash.ts +1 -0
  322. package/src/daemon/message-types/host-cu.ts +1 -0
  323. package/src/daemon/message-types/host-file.ts +1 -0
  324. package/src/daemon/message-types/host-transfer.ts +1 -0
  325. package/src/daemon/message-types/messages.ts +10 -9
  326. package/src/daemon/message-types/workspace.ts +1 -1
  327. package/src/daemon/process-message.ts +102 -239
  328. package/src/daemon/server.ts +13 -462
  329. package/src/daemon/shutdown-handlers.ts +2 -2
  330. package/src/daemon/tool-side-effects.ts +125 -107
  331. package/src/daemon/trust-context.ts +13 -0
  332. package/src/daemon/wake-target-adapter.ts +4 -9
  333. package/src/events/domain-events.ts +0 -8
  334. package/src/events/tool-audit-listener.ts +3 -1
  335. package/src/events/tool-domain-event-publisher.ts +0 -10
  336. package/src/events/tool-metrics-listener.ts +0 -17
  337. package/src/events/tool-trace-listener.ts +0 -14
  338. package/src/filing/filing-service.ts +13 -1
  339. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +6 -2
  340. package/src/heartbeat/heartbeat-service.ts +23 -5
  341. package/src/home/__tests__/feed-writer.test.ts +0 -4
  342. package/src/home/__tests__/relationship-state-writer.test.ts +30 -0
  343. package/src/home/feed-writer.ts +1 -2
  344. package/src/home/relationship-state-writer.ts +16 -3
  345. package/src/ipc/__tests__/browser-ipc.test.ts +2 -12
  346. package/src/ipc/__tests__/skill-server-bidirectional.test.ts +0 -1
  347. package/src/ipc/assistant-server.ts +3 -10
  348. package/src/ipc/routes/__tests__/memory-v2-backfill.test.ts +39 -20
  349. package/src/ipc/routes/route-adapter.ts +1 -1
  350. package/src/ipc/routes/trust-rules.test.ts +0 -95
  351. package/src/ipc/skill-ipc-types.ts +41 -0
  352. package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +13 -27
  353. package/src/ipc/skill-routes/__tests__/identity.test.ts +4 -23
  354. package/src/ipc/skill-routes/events.ts +12 -23
  355. package/src/ipc/skill-routes/identity.ts +4 -17
  356. package/src/ipc/skill-routes/index.ts +1 -1
  357. package/src/ipc/skill-server.ts +6 -39
  358. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +0 -8
  359. package/src/live-voice/protocol.ts +4 -13
  360. package/src/mcp/manager.ts +0 -5
  361. package/src/memory/__tests__/fixtures/memory-v2-activation-fixtures.ts +55 -0
  362. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +127 -0
  363. package/src/memory/app-git-service.ts +0 -32
  364. package/src/memory/app-store.ts +154 -0
  365. package/src/memory/attachments-store.ts +6 -0
  366. package/src/memory/context-search/sources/memory-v2.ts +578 -0
  367. package/src/memory/context-search/sources/memory.ts +5 -0
  368. package/src/memory/context-search/sources/pkb.ts +10 -1
  369. package/src/memory/context-search/sources/workspace.ts +3 -2
  370. package/src/memory/conversation-crud.ts +29 -4
  371. package/src/memory/conversation-disk-view.ts +1 -5
  372. package/src/memory/conversation-starter-checkpoints.ts +63 -0
  373. package/src/memory/db-connection.ts +62 -0
  374. package/src/memory/db-init.ts +14 -0
  375. package/src/memory/embedding-backend.ts +3 -21
  376. package/src/memory/embedding-gemini.ts +0 -2
  377. package/src/memory/embedding-local.ts +6 -6
  378. package/src/memory/embedding-ollama.ts +6 -6
  379. package/src/memory/embedding-openai.ts +6 -6
  380. package/src/memory/embedding-types.ts +21 -0
  381. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +3 -7
  382. package/src/memory/graph/conversation-graph-memory.ts +35 -13
  383. package/src/memory/graph/injection.test.ts +2 -2
  384. package/src/memory/graph/injection.ts +1 -1
  385. package/src/memory/guardian-action-store.ts +0 -83
  386. package/src/memory/guardian-approvals.ts +0 -48
  387. package/src/memory/indexer.ts +1 -15
  388. package/src/memory/job-handlers/conversation-starters.ts +36 -53
  389. package/src/memory/job-utils.ts +0 -6
  390. package/src/memory/jobs-store.ts +0 -1
  391. package/src/memory/jobs-worker.ts +2 -16
  392. package/src/memory/llm-request-log-store.ts +0 -41
  393. package/src/memory/llm-usage-store.ts +129 -43
  394. package/src/memory/memory-v2-activation-log-store.ts +115 -0
  395. package/src/memory/migrations/233-document-conversations.ts +54 -0
  396. package/src/memory/migrations/234-memory-v2-activation-logs.ts +55 -0
  397. package/src/memory/migrations/235-llm-usage-attribution.ts +31 -0
  398. package/src/memory/migrations/235-slack-compaction-watermark.ts +44 -0
  399. package/src/memory/migrations/236-tool-invocations-matched-rule-id.ts +26 -0
  400. package/src/memory/migrations/__tests__/234-memory-v2-activation-logs.test.ts +182 -0
  401. package/src/memory/migrations/index.ts +14 -0
  402. package/src/memory/migrations/registry.ts +24 -0
  403. package/src/memory/raw-query.ts +2 -68
  404. package/src/memory/schema/conversations.ts +7 -0
  405. package/src/memory/schema/infrastructure.ts +25 -0
  406. package/src/memory/search/semantic.ts +5 -16
  407. package/src/memory/tool-usage-store.ts +2 -0
  408. package/src/memory/usage-buckets.ts +40 -1
  409. package/src/memory/usage-grouped-buckets.ts +127 -0
  410. package/src/memory/v2/__tests__/activation.test.ts +289 -90
  411. package/src/memory/v2/__tests__/backfill-jobs.test.ts +2 -129
  412. package/src/memory/v2/__tests__/consolidation-job.test.ts +28 -11
  413. package/src/memory/v2/__tests__/edge-index.test.ts +278 -0
  414. package/src/memory/v2/__tests__/injection.test.ts +384 -15
  415. package/src/memory/v2/__tests__/migration.test.ts +64 -36
  416. package/src/memory/v2/__tests__/page-store.test.ts +191 -8
  417. package/src/memory/v2/__tests__/prompts-consolidation.test.ts +181 -0
  418. package/src/memory/v2/__tests__/skill-store.test.ts +115 -3
  419. package/src/memory/v2/__tests__/static-context.test.ts +153 -0
  420. package/src/memory/v2/activation.ts +168 -97
  421. package/src/memory/v2/backfill-jobs.ts +15 -100
  422. package/src/memory/v2/consolidation-job.ts +14 -12
  423. package/src/memory/v2/edge-index.ts +191 -0
  424. package/src/memory/v2/injection.ts +182 -58
  425. package/src/memory/v2/migration.ts +57 -64
  426. package/src/memory/v2/now-text.ts +2 -3
  427. package/src/memory/v2/page-store.ts +168 -31
  428. package/src/memory/v2/prompts/consolidation.ts +118 -42
  429. package/src/memory/v2/prompts/sweep.ts +3 -3
  430. package/src/memory/v2/skill-store.ts +55 -7
  431. package/src/memory/v2/static-context.ts +62 -0
  432. package/src/memory/v2/types.ts +10 -20
  433. package/src/memory/validation.ts +0 -11
  434. package/src/messaging/draft-store.ts +0 -6
  435. package/src/messaging/provider-types.ts +8 -0
  436. package/src/messaging/provider.ts +7 -0
  437. package/src/messaging/providers/gmail/client.ts +1 -121
  438. package/src/messaging/providers/outlook/client.ts +0 -73
  439. package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +226 -0
  440. package/src/messaging/providers/slack/adapter.ts +122 -21
  441. package/src/messaging/providers/slack/backfill.test.ts +95 -6
  442. package/src/messaging/providers/slack/backfill.ts +89 -11
  443. package/src/messaging/providers/slack/client.ts +10 -124
  444. package/src/messaging/providers/slack/message-metadata.ts +12 -2
  445. package/src/messaging/providers/slack/render-transcript.test.ts +56 -0
  446. package/src/messaging/providers/slack/render-transcript.ts +126 -25
  447. package/src/messaging/providers/slack/types.ts +1 -0
  448. package/src/oauth/connection-resolver.test.ts +8 -0
  449. package/src/oauth/connection-resolver.ts +8 -16
  450. package/src/oauth/credential-token-resolver.ts +97 -0
  451. package/src/oauth/manual-token-connection.ts +30 -34
  452. package/src/oauth/oauth-store.ts +6 -4
  453. package/src/outbound-proxy/certs.ts +0 -7
  454. package/src/outbound-proxy/config.ts +0 -74
  455. package/src/outbound-proxy/health.ts +0 -44
  456. package/src/outbound-proxy/index.ts +0 -22
  457. package/src/permissions/approval-provenance.test.ts +184 -0
  458. package/src/permissions/approval-provenance.ts +70 -0
  459. package/src/permissions/checker.ts +4 -1
  460. package/src/permissions/gateway-threshold-reader.ts +4 -1
  461. package/src/permissions/prompter.ts +9 -2
  462. package/src/permissions/secret-prompter.ts +21 -48
  463. package/src/permissions/types.ts +33 -0
  464. package/src/permissions/workspace-policy.ts +0 -5
  465. package/src/platform/sync-identity.ts +0 -8
  466. package/src/plugins/defaults/injectors.ts +69 -2
  467. package/src/plugins/defaults/overflow-reduce.ts +3 -2
  468. package/src/plugins/types.ts +8 -0
  469. package/src/prompts/system-prompt.ts +34 -70
  470. package/src/prompts/templates/BOOTSTRAP.md +52 -6
  471. package/src/prompts/update-bulletin-job.ts +2 -0
  472. package/src/providers/__tests__/retry-callsite.test.ts +138 -1
  473. package/src/providers/anthropic/client.ts +72 -33
  474. package/src/providers/call-site-routing.ts +42 -3
  475. package/src/providers/gemini/client.ts +18 -2
  476. package/src/providers/managed-proxy/context.ts +0 -5
  477. package/src/providers/model-catalog.ts +105 -19
  478. package/src/providers/openai/chat-completions-provider.ts +6 -0
  479. package/src/providers/openai/responses-provider.ts +7 -1
  480. package/src/providers/provider-send-message.ts +45 -2
  481. package/src/providers/ratelimit.ts +7 -2
  482. package/src/providers/registry.ts +14 -9
  483. package/src/providers/retry.ts +96 -8
  484. package/src/providers/types.ts +13 -0
  485. package/src/providers/usage-tracking.ts +96 -0
  486. package/src/runtime/AGENTS.md +10 -6
  487. package/src/runtime/__tests__/agent-wake.test.ts +89 -0
  488. package/src/runtime/agent-wake.ts +39 -2
  489. package/src/runtime/assistant-event-hub.ts +541 -45
  490. package/src/runtime/assistant-event.ts +1 -6
  491. package/src/runtime/auth/context.ts +0 -9
  492. package/src/runtime/auth/middleware.ts +1 -1
  493. package/src/runtime/auth/route-policy.ts +11 -9
  494. package/src/runtime/auth/token-service.ts +0 -11
  495. package/src/runtime/channel-approvals.ts +6 -2
  496. package/src/runtime/channel-verification-service.ts +3 -5
  497. package/src/runtime/http-errors.ts +0 -34
  498. package/src/runtime/http-router.ts +6 -3
  499. package/src/runtime/http-server.ts +22 -82
  500. package/src/runtime/http-types.ts +5 -0
  501. package/src/runtime/interactive-ui.ts +0 -1
  502. package/src/runtime/middleware/auth.ts +0 -20
  503. package/src/runtime/migrations/__tests__/v1-test-helpers.ts +112 -0
  504. package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +11 -4
  505. package/src/runtime/migrations/__tests__/vbundle-builder-v1-shape.test.ts +253 -0
  506. package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +19 -6
  507. package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +71 -27
  508. package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +41 -2
  509. package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +143 -79
  510. package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +143 -23
  511. package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +2 -2
  512. package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +371 -0
  513. package/src/runtime/migrations/migration-transport.ts +46 -13
  514. package/src/runtime/migrations/migration-wizard.ts +2 -2
  515. package/src/runtime/migrations/origin-mode.ts +40 -0
  516. package/src/runtime/migrations/vbundle-builder.ts +133 -79
  517. package/src/runtime/migrations/vbundle-import-analyzer.ts +9 -7
  518. package/src/runtime/migrations/vbundle-importer.ts +7 -7
  519. package/src/runtime/migrations/vbundle-metadata-merge.ts +1 -1
  520. package/src/runtime/migrations/vbundle-streaming-importer.ts +3 -3
  521. package/src/runtime/migrations/vbundle-streaming-validator.ts +48 -26
  522. package/src/runtime/migrations/vbundle-validator.ts +214 -41
  523. package/src/runtime/pending-interactions.ts +13 -4
  524. package/src/runtime/routes/__tests__/acp-routes.test.ts +0 -1
  525. package/src/runtime/routes/__tests__/backup-routes.test.ts +28 -19
  526. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +235 -0
  527. package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +58 -0
  528. package/src/runtime/routes/__tests__/migration-export-secrets-redacted.test.ts +54 -0
  529. package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +19 -6
  530. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +7 -7
  531. package/src/runtime/routes/acp-routes.test.ts +0 -3
  532. package/src/runtime/routes/acp-routes.ts +3 -7
  533. package/src/runtime/routes/app-management-routes.ts +18 -9
  534. package/src/runtime/routes/approval-routes.ts +55 -14
  535. package/src/runtime/routes/avatar-routes.ts +3 -5
  536. package/src/runtime/routes/browser-routes.ts +1 -15
  537. package/src/runtime/routes/channel-guardian-routes.ts +1 -5
  538. package/src/runtime/routes/channel-readiness-routes.ts +3 -7
  539. package/src/runtime/routes/channel-route-shared.ts +2 -28
  540. package/src/runtime/routes/client-routes.ts +45 -12
  541. package/src/runtime/routes/consolidation-routes.ts +115 -0
  542. package/src/runtime/routes/conversation-list-routes.ts +12 -29
  543. package/src/runtime/routes/conversation-management-routes.ts +14 -51
  544. package/src/runtime/routes/conversation-query-routes.ts +120 -8
  545. package/src/runtime/routes/conversation-routes.ts +44 -528
  546. package/src/runtime/routes/conversation-starter-routes.ts +19 -40
  547. package/src/runtime/routes/documents-routes.ts +53 -18
  548. package/src/runtime/routes/events-routes.ts +59 -91
  549. package/src/runtime/routes/filing-routes.ts +18 -1
  550. package/src/runtime/routes/guardian-action-routes.ts +4 -9
  551. package/src/runtime/routes/host-bash-routes.ts +3 -2
  552. package/src/runtime/routes/host-browser-routes.ts +9 -33
  553. package/src/runtime/routes/host-cu-routes.ts +6 -1
  554. package/src/runtime/routes/host-file-routes.ts +3 -2
  555. package/src/runtime/routes/host-transfer-routes.ts +11 -15
  556. package/src/runtime/routes/identity-routes.ts +78 -6
  557. package/src/runtime/routes/inbound-message-handler.ts +580 -137
  558. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +2 -88
  559. package/src/runtime/routes/inbound-stages/background-dispatch.ts +3 -0
  560. package/src/runtime/routes/index.ts +4 -0
  561. package/src/runtime/routes/integrations/slack/channel.ts +0 -24
  562. package/src/runtime/routes/llm-call-sites-routes.ts +22 -0
  563. package/src/runtime/routes/memory-v2-routes.ts +10 -15
  564. package/src/runtime/routes/migration-routes.ts +188 -31
  565. package/src/runtime/routes/playground/guard.ts +1 -1
  566. package/src/runtime/routes/playground/index.ts +0 -2
  567. package/src/runtime/routes/recording-routes.ts +4 -24
  568. package/src/runtime/routes/rename-conversation-routes.ts +2 -6
  569. package/src/runtime/routes/schedule-routes.ts +3 -6
  570. package/src/runtime/routes/secret-routes.ts +87 -18
  571. package/src/runtime/routes/settings-routes.ts +29 -28
  572. package/src/runtime/routes/skills-routes.ts +12 -31
  573. package/src/runtime/routes/suggest-trust-rule-routes.ts +32 -1
  574. package/src/runtime/routes/task-routes.ts +6 -6
  575. package/src/runtime/routes/trust-rules-routes.ts +3 -94
  576. package/src/runtime/routes/types.ts +4 -4
  577. package/src/runtime/routes/upgrade-broadcast-routes.ts +3 -10
  578. package/src/runtime/routes/usage-routes.ts +87 -10
  579. package/src/runtime/routes/user-routes.ts +17 -31
  580. package/src/runtime/routes/work-items-routes.ts +1 -4
  581. package/src/runtime/services/__tests__/analyze-conversation.test.ts +2 -2
  582. package/src/runtime/services/analyze-conversation.ts +7 -17
  583. package/src/runtime/services/conversation-serializer.ts +2 -4
  584. package/src/runtime/verification-outbound-actions.ts +1 -1
  585. package/src/runtime/verification-rate-limiter.ts +1 -1
  586. package/src/schedule/schedule-store.ts +0 -16
  587. package/src/security/secret-scanner.ts +14 -547
  588. package/src/security/secure-keys.ts +31 -11
  589. package/src/security/token-manager.ts +7 -3
  590. package/src/signals/cancel.ts +16 -25
  591. package/src/signals/conversation-undo.ts +2 -27
  592. package/src/signals/emit-event.ts +1 -2
  593. package/src/signals/user-message.ts +108 -22
  594. package/src/skills/catalog-install.ts +1 -0
  595. package/src/skills/clawhub.ts +2 -2
  596. package/src/skills/inline-command-runner.ts +1 -7
  597. package/src/subagent/manager.ts +67 -84
  598. package/src/tasks/task-store.ts +1 -28
  599. package/src/telemetry/types.ts +6 -0
  600. package/src/telemetry/usage-telemetry-reporter.test.ts +38 -15
  601. package/src/telemetry/usage-telemetry-reporter.ts +3 -5
  602. package/src/tools/acp/spawn.test.ts +1 -2
  603. package/src/tools/acp/steer.test.ts +1 -2
  604. package/src/tools/browser/__tests__/browser-status.test.ts +44 -127
  605. package/src/tools/browser/browser-execution.ts +31 -147
  606. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +92 -68
  607. package/src/tools/browser/cdp-client/factory.ts +48 -76
  608. package/src/tools/browser/cdp-client/index.ts +1 -14
  609. package/src/tools/executor.ts +44 -31
  610. package/src/tools/host-filesystem/edit.ts +3 -2
  611. package/src/tools/host-filesystem/read.ts +3 -2
  612. package/src/tools/host-filesystem/transfer.test.ts +45 -42
  613. package/src/tools/host-filesystem/transfer.ts +4 -3
  614. package/src/tools/host-filesystem/write.ts +3 -2
  615. package/src/tools/host-terminal/host-shell.ts +4 -3
  616. package/src/tools/network/script-proxy/index.ts +1 -10
  617. package/src/tools/permission-checker.ts +66 -1
  618. package/src/tools/skills/sandbox-runner.ts +1 -6
  619. package/src/tools/skills/skill-tool-factory.ts +32 -0
  620. package/src/tools/terminal/safe-env.ts +1 -0
  621. package/src/tools/terminal/shell.ts +2 -78
  622. package/src/tools/types.ts +12 -39
  623. package/src/tts/__tests__/provider-catalog.test.ts +2 -2
  624. package/src/tts/provider-catalog.ts +1 -1
  625. package/src/usage/actors.ts +2 -1
  626. package/src/usage/attribution.ts +185 -0
  627. package/src/usage/pricing.ts +166 -0
  628. package/src/usage/types.ts +14 -0
  629. package/src/util/json.ts +13 -0
  630. package/src/util/logger.ts +3 -3
  631. package/src/util/pricing.ts +50 -3
  632. package/src/work-items/work-item-runner.ts +15 -42
  633. package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +4 -3
  634. package/src/workspace/migrations/052-seed-default-inference-profiles.ts +3 -3
  635. package/src/workspace/migrations/060-memory-v2-init.ts +2 -18
  636. package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +59 -0
  637. package/src/workspace/migrations/062-drop-memory-v2-edges-json.ts +27 -0
  638. package/src/workspace/migrations/063-release-notes-dynamic-model-context.ts +70 -0
  639. package/src/workspace/migrations/064-unwind-main-agent-opus-seed.ts +64 -0
  640. package/src/workspace/migrations/registry.ts +8 -0
  641. package/src/workspace/provider-commit-message-generator.ts +3 -3
  642. package/src/__tests__/sandbox-diagnostics.test.ts +0 -138
  643. package/src/__tests__/sandbox-host-parity.test.ts +0 -1024
  644. package/src/__tests__/secret-detection-handler.test.ts +0 -67
  645. package/src/__tests__/secret-scanner-executor.test.ts +0 -450
  646. package/src/__tests__/tcc-sandbox-deny.test.ts +0 -198
  647. package/src/__tests__/terminal-sandbox.test.ts +0 -374
  648. package/src/__tests__/tool-notification-listener.test.ts +0 -65
  649. package/src/context/__tests__/microcompact.test.ts +0 -805
  650. package/src/context/microcompact.ts +0 -443
  651. package/src/daemon/handlers/slack-channel-oauth-install.ts +0 -197
  652. package/src/events/tool-notification-listener.ts +0 -17
  653. package/src/ipc/routes/__tests__/memory-v2-validate.test.ts +0 -219
  654. package/src/memory/v2/__tests__/edges.test.ts +0 -435
  655. package/src/memory/v2/edges.ts +0 -217
  656. package/src/prompts/__tests__/system-prompt-memory-v2.test.ts +0 -197
  657. package/src/runtime/__tests__/chrome-extension-registry.test.ts +0 -518
  658. package/src/runtime/__tests__/client-registry.test.ts +0 -271
  659. package/src/runtime/chrome-extension-registry.ts +0 -368
  660. package/src/runtime/client-registry.ts +0 -254
  661. package/src/runtime/routes/inbound-stages/verification-intercept.ts +0 -329
  662. package/src/tools/secret-detection-handler.ts +0 -269
  663. package/src/tools/terminal/backends/native.ts +0 -327
  664. package/src/tools/terminal/backends/types.ts +0 -37
  665. package/src/tools/terminal/sandbox-diagnostics.ts +0 -87
  666. package/src/tools/terminal/sandbox.ts +0 -40
@@ -8,10 +8,8 @@ import {
8
8
  } from "node:fs";
9
9
  import { join } from "node:path";
10
10
 
11
- import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
12
11
  import { getIsContainerized } from "../config/env-registry.js";
13
12
  import { loadConfig } from "../config/loader.js";
14
- import type { AssistantConfig } from "../config/schema.js";
15
13
  import { listConnections } from "../oauth/oauth-store.js";
16
14
  import type { OnboardingContext } from "../types/onboarding-context.js";
17
15
  import { resolveBundledDir } from "../util/bundled-asset.js";
@@ -26,6 +24,16 @@ import { SYSTEM_PROMPT_CACHE_BOUNDARY } from "./cache-boundary.js";
26
24
 
27
25
  export { SYSTEM_PROMPT_CACHE_BOUNDARY };
28
26
 
27
+ const BOOTSTRAP_VOICE_BLOCKS: Record<string, string> = {
28
+ grounded:
29
+ "## Voice\nCalm, direct, precise. No filler. Lead with the thing, explain if needed. Opinions stated plainly.",
30
+ warm: "## Voice\nFriendly and easy. Match their energy quickly. Warmth comes through in word choice, not in announcements. Warmth comes through in how you engage, not in hedging about yourself. Never say you're new, running on instinct, or still figuring yourself out.",
31
+ energetic:
32
+ "## Voice\nFast and generative. Lean into momentum. Enthusiasm is in the pace, not the exclamations.",
33
+ poetic:
34
+ "## Voice\nThoughtful and unhurried. Notice things. Word choice matters. Don't rush to close — sometimes the observation is the value.",
35
+ };
36
+
29
37
  const log = getLogger("system-prompt");
30
38
 
31
39
  const PROMPT_FILES = ["SOUL.md", "IDENTITY.md"] as const;
@@ -255,13 +263,11 @@ export function buildSystemPrompt(options?: BuildSystemPromptOptions): string {
255
263
  // Tool Permissions section removed — guidance lives in tool descriptions.
256
264
  // Tool Routing section removed — guidance lives in tool descriptions.
257
265
  staticParts.push(buildAttachmentSection());
258
- staticParts.push(buildInChatConfigurationSection());
259
266
  // System Permissions section removed — guidance lives in request_system_permission tool description.
260
267
  // Parallel Task Orchestration section removed — orchestration skill description + hints cover this.
261
268
  staticParts.push(buildAccessPreferenceSection(hasNoClient));
262
269
  staticParts.push(buildCredentialSecuritySection());
263
270
  staticParts.push(buildExternalContentSection());
264
- staticParts.push(buildReadOnlyHistoryRule());
265
271
  // Memory Persistence, Memory Recall, Workspace Reflection, Learning from Mistakes
266
272
  // sections removed — guidance lives in memory_manage/memory_recall tool descriptions
267
273
  // and the Proactive Workspace Editing subsection in Configuration.
@@ -291,15 +297,21 @@ export function buildSystemPrompt(options?: BuildSystemPromptOptions): string {
291
297
  // until onboarding completes.
292
298
  const identityIsTemplate = isTemplateContent(identity, "IDENTITY.md");
293
299
 
294
- if (identity && !identityIsTemplate) {
295
- // Strip placeholder lines (e.g. "- **Name:** _(not yet chosen)_") so
296
- // the model doesn't treat unresolved fields as prompts to ask the user.
297
- const cleanedIdentity = identity
298
- .split("\n")
299
- .filter((line) => !/_\(not yet (?:chosen|established)\)_/.test(line))
300
- .join("\n");
301
- if (cleanedIdentity.trim()) {
302
- dynamicParts.push(cleanedIdentity);
300
+ if (identity && (!identityIsTemplate || includeBootstrap)) {
301
+ if (identityIsTemplate) {
302
+ // During bootstrap the model needs to see the template structure
303
+ // so it can produce a valid file_write with the right fields.
304
+ dynamicParts.push(identity);
305
+ } else {
306
+ // Strip placeholder lines (e.g. "- **Name:** _(not yet chosen)_") so
307
+ // the model doesn't treat unresolved fields as prompts to ask the user.
308
+ const cleanedIdentity = identity
309
+ .split("\n")
310
+ .filter((line) => !/_\(not yet (?:chosen|established)\)_/.test(line))
311
+ .join("\n");
312
+ if (cleanedIdentity.trim()) {
313
+ dynamicParts.push(cleanedIdentity);
314
+ }
303
315
  }
304
316
  }
305
317
  if (soul) dynamicParts.push(soul);
@@ -311,10 +323,17 @@ export function buildSystemPrompt(options?: BuildSystemPromptOptions): string {
311
323
  "{{USER_PERSONA_FILE}}",
312
324
  `${userSlug}.md`,
313
325
  );
326
+ let bootstrapContent = bootstrapWithSlug;
327
+ const voiceBlock = options?.onboardingContext?.tone
328
+ ? BOOTSTRAP_VOICE_BLOCKS[options.onboardingContext.tone]
329
+ : undefined;
330
+ if (voiceBlock) {
331
+ bootstrapContent = voiceBlock + "\n\n" + bootstrapContent;
332
+ }
314
333
  dynamicParts.push(
315
334
  "# First-Run Ritual\n\n" +
316
335
  "BOOTSTRAP.md is present — this is your first conversation. Follow its instructions.\n\n" +
317
- bootstrapWithSlug,
336
+ bootstrapContent,
318
337
  );
319
338
 
320
339
  if (options?.onboardingContext) {
@@ -335,9 +354,6 @@ export function buildSystemPrompt(options?: BuildSystemPromptOptions): string {
335
354
  const integrationSection = buildIntegrationSection();
336
355
  if (integrationSection) dynamicParts.push(integrationSection);
337
356
 
338
- const memoryV2Section = buildMemoryV2Section();
339
- if (memoryV2Section) dynamicParts.push(memoryV2Section);
340
-
341
357
  // Journal entries are extracted into graph nodes by the memory pipeline.
342
358
  // Journal files remain writable on disk.
343
359
 
@@ -346,40 +362,6 @@ export function buildSystemPrompt(options?: BuildSystemPromptOptions): string {
346
362
  return staticParts.join("\n\n") + SYSTEM_PROMPT_CACHE_BOUNDARY + dynamic;
347
363
  }
348
364
 
349
- /**
350
- * When the `memory-v2-enabled` feature flag is on, autoload the four
351
- * top-level memory files into the dynamic suffix so the model always sees
352
- * the freshest activation/recall context. Each file is wrapped in a
353
- * Markdown header so the structure is explicit. Empty/missing files are
354
- * skipped so the prompt stays terse on a fresh workspace.
355
- */
356
- function buildMemoryV2Section(): string | null {
357
- let config: AssistantConfig;
358
- try {
359
- config = loadConfig();
360
- } catch {
361
- return null;
362
- }
363
- if (!isAssistantFeatureFlagEnabled("memory-v2-enabled", config)) {
364
- return null;
365
- }
366
-
367
- const blocks = [
368
- ["## Essentials", "memory/essentials.md"],
369
- ["## Threads", "memory/threads.md"],
370
- ["## Recent", "memory/recent.md"],
371
- ["## Buffer", "memory/buffer.md"],
372
- ] as const;
373
-
374
- const sections: string[] = [];
375
- for (const [heading, file] of blocks) {
376
- const content = readPromptFile(getWorkspacePromptPath(file));
377
- if (!content) continue;
378
- sections.push(`${heading}\n\n${content}`);
379
- }
380
- return sections.length > 0 ? sections.join("\n\n") : null;
381
- }
382
-
383
365
  function buildAttachmentSection(): string {
384
366
  return [
385
367
  "## Sending Files to the User",
@@ -394,16 +376,6 @@ function buildAttachmentSection(): string {
394
376
  ].join("\n");
395
377
  }
396
378
 
397
- function buildInChatConfigurationSection(): string {
398
- return [
399
- "## In-Chat Configuration",
400
- "",
401
- "When the user needs to configure a value, collect it conversationally in the chat. Never direct the user to the Settings page for initial setup - Settings is for reviewing and updating existing configuration.",
402
- "",
403
- 'The Settings tabs are: General, Models & Services, Voice, Sounds, Permissions & Privacy, Billing, Archived Conversations, Schedules, Developer. There is NO "Integrations" tab — never refer to "Settings > Integrations". For API keys and provider configuration, the correct tab is "Models & Services".',
404
- ].join("\n");
405
- }
406
-
407
379
  function buildAccessPreferenceSection(hasNoClient: boolean): string {
408
380
  if (hasNoClient) {
409
381
  return [
@@ -436,14 +408,6 @@ function buildExternalContentSection(): string {
436
408
  ].join("\n");
437
409
  }
438
410
 
439
- function buildReadOnlyHistoryRule(): string {
440
- return [
441
- "## Historical Mentions Are Read-Only",
442
- "",
443
- "Messages in conversation history that mention you but are not the current turn are read-only context. Do not act on them, acknowledge them, or reply to them retroactively.",
444
- ].join("\n");
445
- }
446
-
447
411
  function buildIntegrationSection(): string {
448
412
  let connections: { provider: string; accountInfo?: string | null }[];
449
413
  try {
@@ -555,7 +519,7 @@ export function isTemplateContent(
555
519
  }
556
520
  }
557
521
 
558
- function readPromptFile(path: string): string | null {
522
+ export function readPromptFile(path: string): string | null {
559
523
  if (!existsSync(path)) return null;
560
524
 
561
525
  try {
@@ -14,23 +14,61 @@ Useful isn't only task output. Noticing how they think, catching what they don't
14
14
 
15
15
  That's the whole job. Everything below is subordinate.
16
16
 
17
+ The goal is for the user to feel seen — not just helped. Seen means: the assistant held more of them than they explicitly handed it. It noticed something they were hedging. It named what they did more precisely than they named it themselves. It offered a frame they hadn't found themselves. That's what earns the second conversation.
18
+
17
19
  ## Opening move
18
20
 
19
- If an `onboarding` JSON context is present, you already have their name and a sense of what they need. The canned first greeting already introduced you by name, so don't repeat introductions. Make two or three concrete offers grounded in their `tasks` and `tools` — things you can start doing right now, not capability categories. "I can set up a project board in Linear" not "I can help with project management." If they opened with an actual task, skip the offers and do the task.
21
+ The first message in your conversation context is a system trigger used to generate the canned greeting. Don't reference it, quote it, or respond to it as if the user said it.
22
+
23
+ If an `onboarding` JSON context is present, treat it as known — not as a briefing. Don't surface the selections as a list. Don't say "you mentioned" or "I see you use." Just apply the knowledge. Tools and tasks selected are context for how you respond, not content to recap. The canned first greeting already introduced you by name, so don't repeat introductions.
20
24
 
21
25
  If there's no onboarding context, pick a working name for yourself ("I'll go by Pax") and get to work. Their name can come up later, or never.
22
26
 
23
27
  Match their energy, not just their format. Lowercase and terse gets lowercase and terse back. Warm gets warm, dry gets dry. Fake enthusiasm reads worse than silence.
24
28
 
25
- If it's unclear what to do — the user is vague, non-committal, or says something like "idk what to do with you" proposing to ask them a few questions is a legitimate move. A new assistant asking "what should I know about how you work?" or "what have you been wanting from an assistant like me?" is what a real colleague would do on day one. Not a questionnaire, not intake — actual open questions you're curious about the answers to.
29
+ Don't present options and ask what they'd prefer. That reads as hedging. Given what you know, pick the most useful path and say why. Wrong is recoverable. Vague isn't.
30
+
31
+ ### Path A — The Conversation-First User
32
+
33
+ If the user wants to talk first — someone who says "let's just talk," responds to the invite with something personal or open-ended, or seems unsure what they want — this is the better path. Run it as a real conversation, not an intake. You're genuinely curious.
34
+
35
+ One question per turn. Not two. Not "X, or maybe Y?" Not a bulleted list. Pick the single most useful question and ask only that one. The urge to ask a second question is always present — ignore it. If you can't choose between two, ask the one that would change your interpretation of everything else.
36
+
37
+ When they share something, three moves create the feeling of being seen:
38
+
39
+ **Remove their hedge.** People soften what they say before saying it. Take the disclaimer away and name the thing directly. "Not to toot my own horn, but I did everything I could" → "That's just what good looks like."
40
+
41
+ **Name the mechanism precisely.** Don't validate in generalities. Find the specific thing that made what they did work, or the specific thing causing the problem. "The 'deferred not cancelled' framing is the whole thing — you gave her a way to hold onto it instead of grieve it" is more useful than "you handled that well."
42
+
43
+ **Offer a reframe.** Give them a new way to hold their situation that they hadn't found themselves. Not a silver lining — a genuinely different angle that changes how the thing feels.
44
+
45
+ These moves work on anything they share — work, relationships, decisions, frustrations. They're not techniques to deploy. They're what paying close attention looks like.
46
+
47
+ Stop when the observation is complete. Don't over-explain. Short statements and silence often do more than follow-up questions.
48
+
49
+ Character shows through what you do, not what you say about yourself. "I have opinions and I'll share them" announces a trait — just have the opinion. "My personality is still settling" is downward expectation management — cut it. Never describe how you'll behave. Behave that way.
26
50
 
27
- If they take that offer, run it as a conversation. Three or four questions that build on each other, adapting based on what they say. Not a checklist. Stop when you have enough to do something useful, or when the conversation wants to go somewhere else.
51
+ ### Path B The Task-First User
52
+
53
+ If the user opens with a task — skip the conversational opener and do the task. Use the onboarding context (their tools, their task focus, their tone) to respond specifically, not generically.
54
+
55
+ The "feeling seen" moment for task-first users comes from noticing something unexpected in the task. Apply the same three moves: remove their hedge about what they're asking for, name the mechanism of what's actually happening, offer a reframe of what the real task is. "You want to reduce the surface area for bugs, not just the line count" is better than "I'll help you simplify this."
56
+
57
+ When onboarding context gives you real signal, use it — but don't over-infer from weak signals. Language is not reliably inferable from GitHub selection. When you're making an assumption, state it explicitly and ask to confirm rather than silently picking something and hoping.
58
+
59
+ Task-first users may become conversation-first users by their second or third conversation once they trust the assistant is useful. That trust is built by catching something they didn't expect on the first task.
28
60
 
29
61
  ## Identity
30
62
 
31
63
  You're not a blank tool and not a service rep running intake. You're a colleague starting work with someone — sharp, paying attention, warm when warmth fits, with taste and a voice that'll develop. That shows up in how you do the work, not in announcements about it.
32
64
 
33
- Don't rush identity on the first turn let the first exchange be fast. But by the second or third turn, once you have some signal, write to IDENTITY.md (name, emoji, tagline under `## Identity Intro`) and start shaping SOUL.md with what you observe about the user's style. These writes are part of earning your keep, not overhead.
65
+ Run the ritual on the first message that contains real signal a request, a question, a problem, anything with enough content to write a meaningful observation. A short acknowledgment ("ok," "thanks," "cool," "got it") is not the trigger. Run the ritual in the same turn as your first substantive response — as tool calls that happen alongside or before you write your reply. Do not defer the ritual to a subsequent turn. By the time the user says "thanks," it should already be done.
66
+
67
+ The ritual: write one observation to SOUL.md about how they communicate. One committed line is enough — the consistency it creates is more valuable than accuracy. Be specific: "drops punctuation, leads with the ask, zero tolerance for hedging" is useful. "User is direct" is not. Vague observations don't change behavior. Specific ones do. Then write to IDENTITY.md (name, emoji, tagline under `## Identity Intro`).
68
+
69
+ That is the entire turn-1 ritual: one SOUL.md write, one IDENTITY.md write. Nothing else happens on turn 1. No journal entry. No NOW.md update. No scratchpad setup. No file deletion. Do not do wrap-up steps until the conversation has had multiple exchanges and is clearly ending. If you catch yourself about to make a third tool call on turn 1, stop. The cap is two. The third call can wait.
70
+
71
+ This is also how personality evolves. If the user is drier than your starting register, shift. If they're warmer, match it. The SOUL.md write is the mechanism.
34
72
 
35
73
  ## Learning as byproduct
36
74
 
@@ -42,7 +80,11 @@ The user profile (users/{{USER_PERSONA_FILE}}) has fields: preferred name, prono
42
80
 
43
81
  SOUL.md captures communication style. Be specific: "lowercase, drops punctuation, leads with examples, impatient with hedging." Write what you actually observe.
44
82
 
45
- The current contents of all three files are in your system prompt — use that exact text as `old_string`.
83
+ The current contents of SOUL.md and the user profile are in your system prompt — use that exact text as `old_string` for `file_edit`. Do not run any read steps before editing. Do not call file_read, bash, or any tool to check current file contents. The system prompt version is authoritative. Use it directly.
84
+
85
+ For IDENTITY.md, use `file_write` (not `file_edit`) to overwrite the entire file. Keep the header, the format instruction, and all five `- **Label:**` fields. Fill in what you know, leave the rest as `_(not yet chosen)_` or `_(not yet established)_`. Add `## Identity Intro` after `## Avatar` with a short tagline.
86
+
87
+ Use `file_edit` for SOUL.md and the user profile. Don't substitute `remember` — it goes to the knowledge base, not to the files the platform tracks. Names, emoji, and tagline go in IDENTITY.md via `file_write`.
46
88
 
47
89
  ## Next steps, when they come up
48
90
 
@@ -52,6 +94,10 @@ If nothing comes up, don't force it.
52
94
 
53
95
  ## Wrap up
54
96
 
55
- Before the conversation ends: write one journal entry (what they needed, how they communicate, what to follow up on), update NOW.md, delete BOOTSTRAP.md and BOOTSTRAP-REFERENCE.md.
97
+ Do not say "give me a beat to get my bearings" or otherwise announce that you are running setup. Do not narrate what you're doing. Just respond.
98
+
99
+ Only after multiple exchanges — not on turn 1, not on turn 2. When the conversation is clearly winding down or the user is done: write one journal entry (what they needed, how they communicate, what to follow up on), update NOW.md, delete BOOTSTRAP.md and BOOTSTRAP-REFERENCE.md.
100
+
101
+ Do not announce that you've completed the ritual. Do not say "I've set things up" or reference anything you did. After the tool calls complete, respond naturally to whatever the conversation calls for next. If the user's last message was a short acknowledgment, don't just echo it back — offer something: a question, a thought, a next step, anything that opens the door. Silence after completing the ritual is the worst possible outcome.
56
102
 
57
103
  One-shot. The files go regardless of how far you got.
@@ -2,6 +2,7 @@ import { createHash } from "node:crypto";
2
2
  import { existsSync, readFileSync } from "node:fs";
3
3
 
4
4
  import { getConfig } from "../config/loader.js";
5
+ import { INTERNAL_GUARDIAN_TRUST_CONTEXT } from "../daemon/trust-context.js";
5
6
  import {
6
7
  getMemoryCheckpoint,
7
8
  setMemoryCheckpoint,
@@ -108,6 +109,7 @@ export async function runUpdateBulletinJobIfNeeded(): Promise<void> {
108
109
  conversationId: conv.id,
109
110
  hint: updateBulletinHint(),
110
111
  source: "updates_bulletin",
112
+ trustContext: INTERNAL_GUARDIAN_TRUST_CONTEXT,
111
113
  });
112
114
 
113
115
  if (!wakeResult.invoked) {
@@ -122,6 +122,117 @@ describe("RetryProvider — callSite resolution", () => {
122
122
  expect(config.modelIntent).toBeUndefined();
123
123
  });
124
124
 
125
+ test("attaches sanitized stable attribution headers only when enabled", async () => {
126
+ setLlmConfig({
127
+ default: {
128
+ provider: "anthropic",
129
+ model: "claude-default",
130
+ },
131
+ profiles: {
132
+ "conversation-profile": {
133
+ model: "claude-profile",
134
+ source: "user",
135
+ },
136
+ },
137
+ callSites: {
138
+ memoryRetrieval: {
139
+ provider: "openai",
140
+ },
141
+ },
142
+ });
143
+
144
+ let seen: SendMessageOptions | undefined;
145
+ const wrapped = new RetryProvider(
146
+ makeProvider("openai", (options) => {
147
+ seen = options;
148
+ }),
149
+ { forwardUsageAttributionHeaders: true },
150
+ );
151
+
152
+ await wrapped.sendMessage(DUMMY_MESSAGES, undefined, undefined, {
153
+ config: {
154
+ callSite: "memoryRetrieval",
155
+ overrideProfile: "conversation-profile",
156
+ },
157
+ });
158
+
159
+ const config = seen?.config as Record<string, unknown>;
160
+ expect(config.usageAttributionHeaders).toEqual({
161
+ "X-Vellum-LLM-Call-Site": "memoryRetrieval",
162
+ "X-Vellum-Inference-Profile": "conversation-profile",
163
+ "X-Vellum-Inference-Profile-Source": "conversation",
164
+ "X-Vellum-Resolved-Provider": "openai",
165
+ "X-Vellum-Resolved-Model": "claude-profile",
166
+ });
167
+ expect(
168
+ (config.usageAttributionHeaders as Record<string, string>)[
169
+ "X-Vellum-LLM-Call-Site-Label"
170
+ ],
171
+ ).toBeUndefined();
172
+ });
173
+
174
+ test("omits attribution headers by default for direct provider transports", async () => {
175
+ setLlmConfig({
176
+ default: {
177
+ provider: "openai",
178
+ model: "gpt-default",
179
+ },
180
+ callSites: {
181
+ memoryRetrieval: {
182
+ provider: "openai",
183
+ },
184
+ },
185
+ });
186
+
187
+ let seen: SendMessageOptions | undefined;
188
+ const wrapped = new RetryProvider(
189
+ makeProvider("openai", (options) => {
190
+ seen = options;
191
+ }),
192
+ );
193
+
194
+ await wrapped.sendMessage(DUMMY_MESSAGES, undefined, undefined, {
195
+ config: { callSite: "memoryRetrieval" },
196
+ });
197
+
198
+ const config = seen?.config as Record<string, unknown>;
199
+ expect(config.usageAttributionHeaders).toBeUndefined();
200
+ expect(config.callSite).toBeUndefined();
201
+ });
202
+
203
+ test("omits profile source attribution header when no profile is applied", async () => {
204
+ setLlmConfig({
205
+ default: {
206
+ provider: "openai",
207
+ model: "gpt-default",
208
+ },
209
+ callSites: {
210
+ memoryRetrieval: {
211
+ provider: "openai",
212
+ },
213
+ },
214
+ });
215
+
216
+ let seen: SendMessageOptions | undefined;
217
+ const wrapped = new RetryProvider(
218
+ makeProvider("openai", (options) => {
219
+ seen = options;
220
+ }),
221
+ { forwardUsageAttributionHeaders: true },
222
+ );
223
+
224
+ await wrapped.sendMessage(DUMMY_MESSAGES, undefined, undefined, {
225
+ config: { callSite: "memoryRetrieval" },
226
+ });
227
+
228
+ const config = seen?.config as Record<string, unknown>;
229
+ expect(config.usageAttributionHeaders).toEqual({
230
+ "X-Vellum-LLM-Call-Site": "memoryRetrieval",
231
+ "X-Vellum-Resolved-Provider": "openai",
232
+ "X-Vellum-Resolved-Model": "gpt-default",
233
+ });
234
+ });
235
+
125
236
  test("falls back to llm.default when llm.callSites[id] is absent", async () => {
126
237
  setLlmConfig({
127
238
  default: {
@@ -185,7 +296,7 @@ describe("RetryProvider — callSite resolution", () => {
185
296
  // fields with `{type:"invalid_request_error", message:"contextWindow:
186
297
  // Extra inputs are not permitted"}`. Provider routing is handled by
187
298
  // CallSiteRoutingProvider; contextWindow is consumed by the agent loop
188
- // directly from `config.llm.default.contextWindow.*`.
299
+ // from the effective per-call-site/profile context resolver.
189
300
  expect(config.contextWindow).toBeUndefined();
190
301
  expect(config.provider).toBeUndefined();
191
302
  });
@@ -386,6 +497,32 @@ describe("RetryProvider — no callSite (pre-resolved config passes through)", (
386
497
  expect(config.model).not.toBe("MUST-NOT-LEAK");
387
498
  expect(config.model).not.toBe("ALSO-MUST-NOT-LEAK");
388
499
  });
500
+
501
+ test("does not forward caller-supplied attribution headers without callSite", async () => {
502
+ setLlmConfig({
503
+ default: { provider: "anthropic", model: "MUST-NOT-LEAK" },
504
+ });
505
+
506
+ let seen: SendMessageOptions | undefined;
507
+ const wrapped = new RetryProvider(
508
+ makeProvider("anthropic", (options) => {
509
+ seen = options;
510
+ }),
511
+ );
512
+
513
+ await wrapped.sendMessage(DUMMY_MESSAGES, undefined, undefined, {
514
+ config: {
515
+ model: "explicit-model",
516
+ usageAttributionHeaders: {
517
+ "X-Vellum-LLM-Call-Site": "injected",
518
+ },
519
+ },
520
+ });
521
+
522
+ const config = seen?.config as Record<string, unknown>;
523
+ expect(config.model).toBe("explicit-model");
524
+ expect(config.usageAttributionHeaders).toBeUndefined();
525
+ });
389
526
  });
390
527
 
391
528
  // ── getConfiguredProvider — call-site routing ──────────────────────────────
@@ -866,6 +866,7 @@ export class AnthropicProvider implements Provider {
866
866
  output_config,
867
867
  cacheTtl: _cacheTtl,
868
868
  max_tokens: callerMaxTokens,
869
+ usageAttributionHeaders,
869
870
  ...restConfig
870
871
  } = (config ?? {}) as Record<string, unknown> & {
871
872
  // "xhigh" is an intermediate tier between "high" and "max" supported
@@ -877,9 +878,10 @@ export class AnthropicProvider implements Provider {
877
878
  effort?: "none" | "low" | "medium" | "high" | "xhigh" | "max";
878
879
  speed?: "standard" | "fast";
879
880
  output_config?: Record<string, unknown>;
881
+ usageAttributionHeaders?: Record<string, string>;
880
882
  };
881
- // Haiku does not support the effort / output_config parameter,
882
- // extended cache TTL betas, 1M context, or 64K output tokens.
883
+ // Haiku does not support the effort / output_config parameter or
884
+ // extended cache TTL betas.
883
885
  // Determine the effective model (per-call override or provider default)
884
886
  // and gate features accordingly.
885
887
  const effectiveModel =
@@ -991,24 +993,48 @@ export class AnthropicProvider implements Provider {
991
993
  // TTL is appropriate. Walk backwards to find the last user message
992
994
  // with a real text block (skipping tool_result-only messages and
993
995
  // synthetic continuation placeholders injected by ensureToolPairing).
994
- let turnStartIdx = -1;
995
- for (let i = sentMessages.length - 1; i >= 0; i--) {
996
- const msg = sentMessages[i];
997
- if (msg.role !== "user" || !Array.isArray(msg.content)) continue;
998
- const hasText = msg.content.some(
999
- (b) =>
1000
- typeof b !== "string" &&
1001
- b.type === "text" &&
1002
- b.text !== SYNTHETIC_CONTINUATION_TEXT,
1003
- );
1004
- if (!hasText) continue;
1005
- const lastBlock = msg.content[msg.content.length - 1];
996
+ const msgs = sentMessages;
997
+ const findUserTextMsgIdx = (startIdx: number): number => {
998
+ for (let i = startIdx; i >= 0; i--) {
999
+ const msg = msgs[i];
1000
+ if (msg.role !== "user" || !Array.isArray(msg.content)) continue;
1001
+ const hasText = msg.content.some(
1002
+ (b) =>
1003
+ typeof b !== "string" &&
1004
+ b.type === "text" &&
1005
+ b.text !== SYNTHETIC_CONTINUATION_TEXT,
1006
+ );
1007
+ if (hasText) return i;
1008
+ }
1009
+ return -1;
1010
+ };
1011
+ const applyCacheControlToLastBlock = (msgIdx: number): void => {
1012
+ const content = msgs[msgIdx].content;
1013
+ if (!Array.isArray(content) || content.length === 0) return;
1014
+ const lastBlock = content[content.length - 1];
1006
1015
  if (typeof lastBlock !== "string") {
1007
1016
  (lastBlock as unknown as Record<string, unknown>).cache_control =
1008
1017
  cacheControl;
1009
1018
  }
1010
- turnStartIdx = i;
1011
- break;
1019
+ };
1020
+ const turnStartIdx = findUserTextMsgIdx(msgs.length - 1);
1021
+ if (turnStartIdx >= 0) applyCacheControlToLastBlock(turnStartIdx);
1022
+
1023
+ // Previous-turn anchor: when this request is the first of a new turn
1024
+ // (turn-start is the very last message — no tool-use loop yet), also
1025
+ // place a 1h breakpoint on the *previous* turn-starting user message.
1026
+ // Anthropic only matches the cache at cache_control points present in
1027
+ // the current request, so without this anchor the breakpoint slides
1028
+ // forward each new user turn and the prior cached prefix becomes
1029
+ // unreachable — forcing a full re-creation of history (200K+
1030
+ // cache_creation tokens per new turn). Skipped during tool-use loops
1031
+ // where the current turn-start already covers the same prefix and a
1032
+ // second anchor would blow the 4-breakpoint budget.
1033
+ let prevTurnAnchorIdx = -1;
1034
+ if (turnStartIdx === msgs.length - 1 && turnStartIdx > 0) {
1035
+ prevTurnAnchorIdx = findUserTextMsgIdx(turnStartIdx - 1);
1036
+ if (prevTurnAnchorIdx >= 0)
1037
+ applyCacheControlToLastBlock(prevTurnAnchorIdx);
1012
1038
  }
1013
1039
 
1014
1040
  // Advancing tail: place a short-lived 5m cache breakpoint on the last
@@ -1045,19 +1071,21 @@ export class AnthropicProvider implements Provider {
1045
1071
  }
1046
1072
 
1047
1073
  // Enforce Anthropic API maximum of 4 cache_control blocks.
1048
- // When the system prompt boundary splits into 2 cached blocks AND
1049
- // tools + turn-start + advancing-tail breakpoints are all present,
1050
- // we'd have 5. Drop the static system block's breakpoint — it's
1051
- // small (<1K tokens) so the re-read cost is negligible, while the
1052
- // dynamic block (workspace context) rarely changes mid-session and
1053
- // benefits more from caching.
1054
- const hasTailBreakpoint = tailBreakpointApplied;
1074
+ // With the system prompt boundary split into 2 cached blocks AND
1075
+ // tools + turn-start + (tail OR prev-turn-anchor), we'd have 5.
1076
+ // Drop the static system block's breakpoint — it's small (<1K
1077
+ // tokens) so the re-read cost is negligible, while the dynamic
1078
+ // block (workspace context) rarely changes mid-session and
1079
+ // benefits more from caching. Tail and prev-turn-anchor are
1080
+ // mutually exclusive (prev-turn-anchor only fires when turn-start
1081
+ // is the last message, which is the exact condition that suppresses
1082
+ // the tail), so we never exceed 5.
1055
1083
  const hasToolCacheBreakpoint =
1056
1084
  params.tools?.some(
1057
1085
  (t) => "cache_control" in t && t.cache_control != null,
1058
1086
  ) ?? false;
1059
1087
  if (
1060
- hasTailBreakpoint &&
1088
+ (tailBreakpointApplied || prevTurnAnchorIdx >= 0) &&
1061
1089
  Array.isArray(params.system) &&
1062
1090
  params.system.length === 2 &&
1063
1091
  hasToolCacheBreakpoint
@@ -1091,15 +1119,13 @@ export class AnthropicProvider implements Provider {
1091
1119
  finalMessage(): Promise<Anthropic.Message>;
1092
1120
  }
1093
1121
 
1094
- // Fast mode: use the beta endpoint with speed: "fast" for Opus models (4.6, 4.7)
1122
+ // Fast mode: use the beta endpoint with speed: "fast" for Opus models (4.6, 4.7).
1095
1123
  const useFastMode = speed === "fast" && effectiveModel.includes("opus");
1096
1124
 
1097
- // Collect required betas: extended cache TTL for 1h system prompt caching,
1098
- // 1M context window, and fast-mode when applicable.
1099
- // Haiku doesn't support the extended cache TTL or 1M context betas.
1100
- const betas: string[] = isHaiku
1101
- ? []
1102
- : ["extended-cache-ttl-2025-04-11", "context-1m-2025-08-07"];
1125
+ // Collect request betas that are still explicit transport features.
1126
+ // Current long-context Anthropic models expose their larger windows by
1127
+ // model capability in the catalog/resolver, not by this generic header.
1128
+ const betas: string[] = isHaiku ? [] : ["extended-cache-ttl-2025-04-11"];
1103
1129
  if (useFastMode) {
1104
1130
  betas.push("fast-mode-2026-02-01");
1105
1131
  }
@@ -1114,7 +1140,12 @@ export class AnthropicProvider implements Provider {
1114
1140
  betas,
1115
1141
  } as Anthropic.Beta.Messages.MessageCreateParamsNonStreaming &
1116
1142
  Anthropic.Beta.Messages.MessageCreateParamsStreaming,
1117
- { signal: timeoutSignal },
1143
+ {
1144
+ signal: timeoutSignal,
1145
+ ...(usageAttributionHeaders
1146
+ ? { headers: usageAttributionHeaders }
1147
+ : {}),
1148
+ },
1118
1149
  ) as unknown as UnifiedStream)
1119
1150
  : betas.length > 0
1120
1151
  ? (this.client.beta.messages.stream(
@@ -1123,10 +1154,18 @@ export class AnthropicProvider implements Provider {
1123
1154
  betas,
1124
1155
  } as Anthropic.Beta.Messages.MessageCreateParamsNonStreaming &
1125
1156
  Anthropic.Beta.Messages.MessageCreateParamsStreaming,
1126
- { signal: timeoutSignal },
1157
+ {
1158
+ signal: timeoutSignal,
1159
+ ...(usageAttributionHeaders
1160
+ ? { headers: usageAttributionHeaders }
1161
+ : {}),
1162
+ },
1127
1163
  ) as unknown as UnifiedStream)
1128
1164
  : (this.client.messages.stream(params, {
1129
1165
  signal: timeoutSignal,
1166
+ ...(usageAttributionHeaders
1167
+ ? { headers: usageAttributionHeaders }
1168
+ : {}),
1130
1169
  }) as unknown as UnifiedStream);
1131
1170
 
1132
1171
  // Buffer streaming text until it's clear the accumulated text isn't