@vellumai/assistant 0.8.3 → 0.8.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 (665) hide show
  1. package/ARCHITECTURE.md +2 -2
  2. package/docker-entrypoint.sh +0 -1
  3. package/docs/browser-use-architecture-phase2.md +1 -1
  4. package/knip.json +2 -1
  5. package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
  6. package/openapi.yaml +1492 -100
  7. package/package.json +1 -1
  8. package/src/__tests__/agent-loop-exit-reason.test.ts +4 -5
  9. package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
  10. package/src/__tests__/agent-loop.test.ts +88 -3
  11. package/src/__tests__/anthropic-provider.test.ts +302 -33
  12. package/src/__tests__/approval-cascade.test.ts +1 -1
  13. package/src/__tests__/assistant-event-hub-self-exclusion.test.ts +293 -0
  14. package/src/__tests__/assistant-feature-flags-integration.test.ts +3 -3
  15. package/src/__tests__/audit-log-rotation.test.ts +70 -16
  16. package/src/__tests__/background-workers-disk-pressure.test.ts +4 -3
  17. package/src/__tests__/btw-routes.test.ts +2 -3
  18. package/src/__tests__/call-controller.test.ts +0 -1
  19. package/src/__tests__/cancel-resolves-conversation-key.test.ts +1 -1
  20. package/src/__tests__/channel-delivery-store.test.ts +193 -0
  21. package/src/__tests__/channel-guardian.test.ts +3 -3
  22. package/src/__tests__/channel-reply-delivery.test.ts +284 -5
  23. package/src/__tests__/channel-retry-sweep.test.ts +274 -1
  24. package/src/__tests__/checker.test.ts +6 -15
  25. package/src/__tests__/compaction-events.test.ts +2 -1
  26. package/src/__tests__/compactor-call-site-logging.test.ts +214 -0
  27. package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
  28. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +5 -11
  29. package/src/__tests__/computer-use-tools.test.ts +2 -4
  30. package/src/__tests__/config-watcher.test.ts +1 -1
  31. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
  32. package/src/__tests__/context-token-estimator.test.ts +91 -1
  33. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
  34. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -1
  35. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +55 -4
  36. package/src/__tests__/conversation-agent-loop-overflow.test.ts +228 -8
  37. package/src/__tests__/conversation-agent-loop.test.ts +188 -129
  38. package/src/__tests__/conversation-app-control-instantiation.test.ts +2 -5
  39. package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
  40. package/src/__tests__/conversation-clean-command.test.ts +137 -0
  41. package/src/__tests__/conversation-clear-safety.test.ts +25 -25
  42. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
  43. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +1 -1
  44. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
  45. package/src/__tests__/conversation-error.test.ts +31 -0
  46. package/src/__tests__/conversation-fork-crud.test.ts +324 -0
  47. package/src/__tests__/conversation-lifecycle.test.ts +53 -12
  48. package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
  49. package/src/__tests__/conversation-load-history-stripped.test.ts +279 -0
  50. package/src/__tests__/conversation-pairing.test.ts +2 -2
  51. package/src/__tests__/conversation-process-callsite.test.ts +1 -1
  52. package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -1
  53. package/src/__tests__/conversation-queue.test.ts +1 -1
  54. package/src/__tests__/conversation-routes-disk-view.test.ts +109 -0
  55. package/src/__tests__/conversation-routes-slash-commands.test.ts +35 -0
  56. package/src/__tests__/conversation-runtime-assembly.test.ts +264 -81
  57. package/src/__tests__/conversation-seed-composer.test.ts +66 -4
  58. package/src/__tests__/conversation-skill-tools.test.ts +2 -5
  59. package/src/__tests__/conversation-slash-commands.test.ts +36 -8
  60. package/src/__tests__/conversation-slash-queue.test.ts +1 -1
  61. package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
  62. package/src/__tests__/conversation-speed-override.test.ts +1 -1
  63. package/src/__tests__/conversation-store.test.ts +1 -1
  64. package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
  65. package/src/__tests__/conversation-sync-tags.test.ts +99 -32
  66. package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -1
  67. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  68. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  69. package/src/__tests__/credential-execution-feature-gates.test.ts +9 -7
  70. package/src/__tests__/credential-execution-tools.test.ts +6 -6
  71. package/src/__tests__/credential-security-invariants.test.ts +7 -0
  72. package/src/__tests__/credential-vault-unit.test.ts +2 -2
  73. package/src/__tests__/cu-unified-flow.test.ts +10 -1
  74. package/src/__tests__/dm-backfill.test.ts +64 -0
  75. package/src/__tests__/dm-persistence.test.ts +33 -0
  76. package/src/__tests__/document-find-replace.test.ts +501 -0
  77. package/src/__tests__/dynamic-page-surface.test.ts +2 -2
  78. package/src/__tests__/email-html-renderer.test.ts +12 -0
  79. package/src/__tests__/first-greeting.test.ts +23 -2
  80. package/src/__tests__/gateway-flag-listener.test.ts +237 -0
  81. package/src/__tests__/gemini-provider.test.ts +78 -0
  82. package/src/__tests__/guardian-dispatch.test.ts +0 -1
  83. package/src/__tests__/guardian-outbound-http.test.ts +7 -5
  84. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  85. package/src/__tests__/headless-browser-navigate.test.ts +172 -0
  86. package/src/__tests__/heartbeat-disk-pressure.test.ts +4 -0
  87. package/src/__tests__/heartbeat-service.test.ts +4 -0
  88. package/src/__tests__/host-bash-proxy.test.ts +6 -0
  89. package/src/__tests__/host-browser-proxy.test.ts +10 -0
  90. package/src/__tests__/host-cu-proxy.test.ts +8 -1
  91. package/src/__tests__/host-file-proxy.test.ts +8 -1
  92. package/src/__tests__/host-shell-tool.test.ts +1 -1
  93. package/src/__tests__/host-transfer-proxy.test.ts +8 -1
  94. package/src/__tests__/identity-routes.test.ts +57 -0
  95. package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
  96. package/src/__tests__/init-feature-flag-overrides.test.ts +5 -6
  97. package/src/__tests__/injector-chain.test.ts +2 -0
  98. package/src/__tests__/injector-document-comments.test.ts +378 -0
  99. package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
  100. package/src/__tests__/list-messages-attachments.test.ts +21 -17
  101. package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
  102. package/src/__tests__/list-messages-page-latest.test.ts +130 -14
  103. package/src/__tests__/list-messages-tool-merge.test.ts +77 -17
  104. package/src/__tests__/llm-context-normalization.test.ts +0 -2
  105. package/src/__tests__/llm-request-log-call-site.test.ts +136 -0
  106. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +26 -0
  107. package/src/__tests__/llm-resolver.test.ts +161 -9
  108. package/src/__tests__/llm-usage-store.test.ts +66 -0
  109. package/src/__tests__/log-export-routes.test.ts +99 -2
  110. package/src/__tests__/logger.test.ts +89 -0
  111. package/src/__tests__/mcp-abort-signal.test.ts +2 -2
  112. package/src/__tests__/media-generate-image.test.ts +31 -0
  113. package/src/__tests__/memory-v2-static-injector.test.ts +7 -7
  114. package/src/__tests__/message-queue-steer.test.ts +114 -0
  115. package/src/__tests__/model-intents.test.ts +2 -4
  116. package/src/__tests__/notification-guardian-path.test.ts +0 -1
  117. package/src/__tests__/onboarding-template-contract.test.ts +1 -1
  118. package/src/__tests__/openai-provider.test.ts +151 -0
  119. package/src/__tests__/openai-responses-provider.test.ts +118 -16
  120. package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
  121. package/src/__tests__/pending-interactions-resolved-event.test.ts +189 -0
  122. package/src/__tests__/platform-bash-auto-approve.test.ts +2 -2
  123. package/src/__tests__/platform.test.ts +2 -5
  124. package/src/__tests__/plugin-api-tool-definition.test.ts +92 -0
  125. package/src/__tests__/plugin-bootstrap.test.ts +2 -2
  126. package/src/__tests__/plugin-source-watcher.test.ts +302 -0
  127. package/src/__tests__/plugin-tool-contribution.test.ts +13 -6
  128. package/src/__tests__/plugin-types.test.ts +3 -2
  129. package/src/__tests__/prechat-onboarding-contract.test.ts +131 -98
  130. package/src/__tests__/pricing.test.ts +12 -0
  131. package/src/__tests__/process-message-background-slack.test.ts +1 -51
  132. package/src/__tests__/process-message-display-content.test.ts +21 -16
  133. package/src/__tests__/prune-jobs-changes-parser.test.ts +61 -0
  134. package/src/__tests__/registry.test.ts +2 -8
  135. package/src/__tests__/require-fresh-approval.test.ts +2 -2
  136. package/src/__tests__/runtime-events-sse-bilingual.test.ts +154 -0
  137. package/src/__tests__/server-history-render.test.ts +83 -4
  138. package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -1
  139. package/src/__tests__/skill-feature-flags.test.ts +2 -2
  140. package/src/__tests__/skill-projection-feature-flag.test.ts +4 -7
  141. package/src/__tests__/skill-projection.benchmark.test.ts +2 -6
  142. package/src/__tests__/skill-tool-factory.test.ts +1 -1
  143. package/src/__tests__/steer-tool-repair.test.ts +249 -0
  144. package/src/__tests__/subagent-notify-parent.test.ts +1 -1
  145. package/src/__tests__/suggestion-routes.test.ts +1 -0
  146. package/src/__tests__/sync-message-contract.test.ts +59 -0
  147. package/src/__tests__/system-prompt.test.ts +161 -124
  148. package/src/__tests__/terminal-tools.test.ts +12 -2
  149. package/src/__tests__/thinking-block-replay.test.ts +113 -0
  150. package/src/__tests__/thread-backfill.test.ts +370 -22
  151. package/src/__tests__/tool-approval-handler.test.ts +1 -5
  152. package/src/__tests__/tool-execute-pipeline.test.ts +2 -2
  153. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -5
  154. package/src/__tests__/tool-executor-lifecycle-events.test.ts +15 -5
  155. package/src/__tests__/tool-executor.test.ts +89 -53
  156. package/src/__tests__/tool-grant-request-escalation.test.ts +1 -6
  157. package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
  158. package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
  159. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -6
  160. package/src/__tests__/trusted-contact-multichannel.test.ts +0 -1
  161. package/src/__tests__/twilio-routes.test.ts +1 -1
  162. package/src/__tests__/ui-file-upload-surface.test.ts +2 -2
  163. package/src/__tests__/usage-routes.test.ts +3 -0
  164. package/src/__tests__/verification-control-plane-policy.test.ts +2 -2
  165. package/src/__tests__/web-fetch.test.ts +2 -2
  166. package/src/__tests__/workspace-git-service.test.ts +94 -10
  167. package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
  168. package/src/__tests__/workspace-migration-089-move-memory-tree-out-of-v3.test.ts +86 -0
  169. package/src/acp/__tests__/prepare-agent-env.test.ts +146 -0
  170. package/src/acp/prepare-agent-env.ts +78 -0
  171. package/src/acp/session-manager.ts +1 -1
  172. package/src/agent/attachments.ts +1 -0
  173. package/src/agent/loop.ts +65 -20
  174. package/src/api/README.md +5 -0
  175. package/src/api/index.ts +4 -0
  176. package/src/api/package.json +10 -0
  177. package/src/background-wake/background-wake-routes.test.ts +233 -0
  178. package/src/background-wake/next-wake.test.ts +289 -0
  179. package/src/background-wake/next-wake.ts +172 -0
  180. package/src/background-wake/runtime-registry.ts +24 -0
  181. package/src/browser/operations.ts +15 -0
  182. package/src/cli/commands/__tests__/browser.test.ts +23 -5
  183. package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
  184. package/src/cli/commands/__tests__/domain-register.test.ts +110 -0
  185. package/src/cli/commands/__tests__/domain-status.test.ts +33 -33
  186. package/src/cli/commands/__tests__/inference-send.test.ts +108 -5
  187. package/src/cli/commands/__tests__/memory-v2-compare-render.test.ts +98 -0
  188. package/src/cli/commands/__tests__/memory-v2.test.ts +10 -12
  189. package/src/cli/commands/__tests__/memory-v3-render.test.ts +340 -0
  190. package/src/cli/commands/browser.ts +247 -0
  191. package/src/cli/commands/conversations.ts +128 -1
  192. package/src/cli/commands/domain.ts +91 -41
  193. package/src/cli/commands/inference-providers.ts +147 -1
  194. package/src/cli/commands/inference.ts +93 -40
  195. package/src/cli/commands/memory-v2-compare-render.ts +115 -0
  196. package/src/cli/commands/memory-v2.ts +483 -0
  197. package/src/cli/commands/memory-v3-render.ts +344 -0
  198. package/src/cli/commands/memory-v3.ts +316 -0
  199. package/src/cli/commands/notifications.ts +24 -2
  200. package/src/cli/program.ts +2 -0
  201. package/src/cli/utils/conversation-id.ts +17 -5
  202. package/src/config/assistant-feature-flags.ts +21 -9
  203. package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
  204. package/src/config/bundled-skills/document-editor/SKILL.md +124 -0
  205. package/src/config/bundled-skills/document-editor/TOOLS.json +258 -0
  206. package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
  207. package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
  208. package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
  209. package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
  210. package/src/config/bundled-skills/document-editor/tools/document-open.ts +12 -0
  211. package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
  212. package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
  213. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +2 -2
  214. package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
  215. package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +13 -8
  216. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +10 -3
  217. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +16 -14
  218. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +7 -2
  219. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +7 -2
  220. package/src/config/bundled-skills/schedule/SKILL.md +8 -0
  221. package/src/config/bundled-tool-registry.ts +24 -12
  222. package/src/config/call-site-defaults.ts +20 -0
  223. package/src/config/feature-flag-registry.json +115 -3
  224. package/src/config/llm-resolver.ts +16 -2
  225. package/src/config/schemas/__tests__/memory-v2.test.ts +217 -1
  226. package/src/config/schemas/call-site-catalog.ts +35 -0
  227. package/src/config/schemas/llm.ts +14 -0
  228. package/src/config/schemas/memory-v2.ts +294 -1
  229. package/src/config/schemas/memory.ts +2 -1
  230. package/src/context/compactor.ts +60 -1
  231. package/src/context/token-estimator.ts +47 -4
  232. package/src/context/window-manager.ts +25 -0
  233. package/src/conversations/__tests__/message-consolidation.test.ts +350 -0
  234. package/src/conversations/message-consolidation.ts +404 -0
  235. package/src/credential-health/credential-health-service.ts +34 -19
  236. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -1
  237. package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
  238. package/src/daemon/__tests__/meet-manifest-loader.test.ts +1 -1
  239. package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
  240. package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
  241. package/src/daemon/conversation-agent-loop-handlers.ts +155 -36
  242. package/src/daemon/conversation-agent-loop.ts +307 -88
  243. package/src/daemon/conversation-error.ts +31 -1
  244. package/src/daemon/conversation-lifecycle.ts +149 -118
  245. package/src/daemon/conversation-messaging.ts +3 -0
  246. package/src/daemon/conversation-process.ts +273 -0
  247. package/src/daemon/conversation-queue-manager.ts +14 -0
  248. package/src/daemon/conversation-runtime-assembly.ts +145 -84
  249. package/src/daemon/conversation-slash.ts +37 -5
  250. package/src/daemon/conversation-surfaces.ts +45 -2
  251. package/src/daemon/conversation-tool-setup.ts +70 -3
  252. package/src/daemon/conversation-usage.ts +2 -0
  253. package/src/daemon/conversation.ts +54 -32
  254. package/src/daemon/disk-pressure-guard.ts +14 -2
  255. package/src/daemon/first-greeting.ts +10 -0
  256. package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
  257. package/src/daemon/handlers/config-a2a.ts +160 -0
  258. package/src/daemon/handlers/config-model.test.ts +2 -0
  259. package/src/daemon/handlers/conversations.ts +90 -3
  260. package/src/daemon/handlers/shared.ts +92 -29
  261. package/src/daemon/host-bash-proxy.ts +1 -1
  262. package/src/daemon/host-browser-proxy.ts +5 -5
  263. package/src/daemon/host-cu-proxy.ts +5 -5
  264. package/src/daemon/host-file-proxy.ts +5 -5
  265. package/src/daemon/host-proxy-base.ts +4 -4
  266. package/src/daemon/host-transfer-proxy.ts +11 -11
  267. package/src/daemon/lifecycle.ts +40 -23
  268. package/src/daemon/meet-manifest-loader.ts +1 -7
  269. package/src/daemon/message-protocol.ts +4 -0
  270. package/src/daemon/message-types/conversations.ts +14 -9
  271. package/src/daemon/message-types/document-comments.ts +50 -0
  272. package/src/daemon/message-types/home.ts +1 -13
  273. package/src/daemon/message-types/messages.ts +66 -7
  274. package/src/daemon/message-types/surfaces.ts +3 -1
  275. package/src/daemon/message-types/sync.ts +14 -0
  276. package/src/daemon/message-types/web-activity.ts +57 -0
  277. package/src/daemon/plugin-source-watcher.ts +135 -3
  278. package/src/daemon/process-message.ts +69 -12
  279. package/src/daemon/shutdown-handlers.ts +24 -5
  280. package/src/daemon/switch-inference-profile-tool.ts +52 -0
  281. package/src/daemon/tool-setup-types.ts +13 -0
  282. package/src/daemon/trust-context.ts +6 -0
  283. package/src/documents/document-comments-store.test.ts +338 -0
  284. package/src/documents/document-comments-store.ts +237 -0
  285. package/src/documents/document-store.ts +202 -0
  286. package/src/events/relationship-state-updated.ts +25 -0
  287. package/src/heartbeat/__tests__/heartbeat-service.test.ts +1 -2
  288. package/src/heartbeat/heartbeat-service.ts +1 -0
  289. package/src/home/__tests__/suggested-prompts.test.ts +33 -2
  290. package/src/home/feed-types.ts +6 -1
  291. package/src/home/home-content-refresh.ts +52 -0
  292. package/src/home/home-greeting-cache.ts +69 -0
  293. package/src/home/home-greeting.ts +85 -0
  294. package/src/home/suggested-prompts.ts +168 -9
  295. package/src/ipc/gateway-flag-listener.ts +123 -0
  296. package/src/ipc/skill-routes/registries.ts +8 -12
  297. package/src/memory/__tests__/db-async-query.test.ts +165 -0
  298. package/src/memory/__tests__/db-maintenance.test.ts +115 -0
  299. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +241 -0
  300. package/src/memory/__tests__/jobs-store-job-classes.test.ts +28 -1
  301. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
  302. package/src/memory/__tests__/memory-retrospective-job.test.ts +327 -6
  303. package/src/memory/auto-analysis-enqueue.ts +5 -1
  304. package/src/memory/conversation-crud.ts +191 -100
  305. package/src/memory/conversation-starters-cadence.ts +3 -1
  306. package/src/memory/conversation-title-service.ts +19 -3
  307. package/src/memory/db-async-query.ts +214 -0
  308. package/src/memory/db-init.ts +26 -0
  309. package/src/memory/db-maintenance.ts +30 -21
  310. package/src/memory/delivery-crud.ts +41 -0
  311. package/src/memory/delivery-status.ts +141 -15
  312. package/src/memory/external-conversation-store.ts +32 -1
  313. package/src/memory/graph/bootstrap.ts +8 -1
  314. package/src/memory/graph/capability-seed.ts +7 -3
  315. package/src/memory/graph/conversation-graph-memory.ts +100 -17
  316. package/src/memory/graph/extraction.ts +1 -5
  317. package/src/memory/graph/graph-search.ts +7 -1
  318. package/src/memory/indexer.ts +28 -18
  319. package/src/memory/job-handlers/cleanup.ts +76 -18
  320. package/src/memory/job-handlers/conversation-starters.ts +1 -4
  321. package/src/memory/jobs/embed-pkb-file.ts +6 -1
  322. package/src/memory/jobs-store.ts +14 -0
  323. package/src/memory/jobs-worker.ts +68 -15
  324. package/src/memory/llm-request-log-source-clickhouse.ts +42 -2
  325. package/src/memory/llm-request-log-source-local.ts +7 -0
  326. package/src/memory/llm-request-log-source.ts +9 -2
  327. package/src/memory/llm-request-log-store.ts +43 -1
  328. package/src/memory/llm-usage-store.ts +24 -0
  329. package/src/memory/memory-retrospective-constants.ts +28 -0
  330. package/src/memory/memory-retrospective-enqueue.ts +11 -3
  331. package/src/memory/memory-retrospective-job.ts +413 -18
  332. package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
  333. package/src/memory/memory-v2-activation-log-store.ts +41 -14
  334. package/src/memory/migrations/100-core-tables.ts +1 -0
  335. package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
  336. package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
  337. package/src/memory/migrations/253-document-comments.ts +47 -0
  338. package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
  339. package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
  340. package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
  341. package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
  342. package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
  343. package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
  344. package/src/memory/migrations/260-rename-cleaned-at.ts +44 -0
  345. package/src/memory/migrations/261-llm-usage-add-raw-usage.ts +36 -0
  346. package/src/memory/migrations/262-memory-v3-coactivation.ts +57 -0
  347. package/src/memory/migrations/263-memory-v3-auto-edges.ts +50 -0
  348. package/src/memory/migrations/264-llm-request-log-call-site.ts +29 -0
  349. package/src/memory/migrations/index.ts +34 -0
  350. package/src/memory/migrations/registry.ts +58 -0
  351. package/src/memory/onboarding-events-store.ts +7 -0
  352. package/src/memory/schema/calls.ts +1 -0
  353. package/src/memory/schema/conversations.ts +3 -0
  354. package/src/memory/schema/infrastructure.ts +22 -0
  355. package/src/memory/tool-usage-store.ts +36 -8
  356. package/src/memory/v2/__tests__/consolidation-job.test.ts +1 -0
  357. package/src/memory/v2/__tests__/harness-compare.test.ts +186 -0
  358. package/src/memory/v2/__tests__/harness-metrics.test.ts +74 -0
  359. package/src/memory/v2/__tests__/harness-oracle.test.ts +257 -0
  360. package/src/memory/v2/__tests__/harness-replay-input.test.ts +225 -0
  361. package/src/memory/v2/__tests__/harness-runner.test.ts +109 -0
  362. package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
  363. package/src/memory/v2/__tests__/injection.test.ts +158 -112
  364. package/src/memory/v2/__tests__/page-index.test.ts +365 -1
  365. package/src/memory/v2/__tests__/qdrant.test.ts +36 -0
  366. package/src/memory/v2/__tests__/router.test.ts +660 -4
  367. package/src/memory/v2/consolidation-job.ts +14 -0
  368. package/src/memory/v2/harness/compare.ts +57 -0
  369. package/src/memory/v2/harness/metrics.ts +124 -0
  370. package/src/memory/v2/harness/oracle.ts +145 -0
  371. package/src/memory/v2/harness/replay-input.ts +224 -0
  372. package/src/memory/v2/harness/retriever.ts +74 -0
  373. package/src/memory/v2/harness/router-retriever.ts +43 -0
  374. package/src/memory/v2/harness/runner.ts +106 -0
  375. package/src/memory/v2/harness/trace.ts +58 -0
  376. package/src/memory/v2/injection-events.ts +101 -0
  377. package/src/memory/v2/injection.ts +42 -25
  378. package/src/memory/v2/page-index.ts +209 -7
  379. package/src/memory/v2/page-store.ts +18 -0
  380. package/src/memory/v2/prompts/router.ts +26 -1
  381. package/src/memory/v2/qdrant.ts +14 -2
  382. package/src/memory/v2/router.ts +369 -62
  383. package/src/memory/v3/__tests__/coactivation-store.test.ts +422 -0
  384. package/src/memory/v3/__tests__/consolidation-job.test.ts +468 -0
  385. package/src/memory/v3/__tests__/edge-learning-job.test.ts +324 -0
  386. package/src/memory/v3/__tests__/edges.test.ts +563 -0
  387. package/src/memory/v3/__tests__/filter.test.ts +512 -0
  388. package/src/memory/v3/__tests__/gate.test.ts +574 -0
  389. package/src/memory/v3/__tests__/index-composition.test.ts +233 -0
  390. package/src/memory/v3/__tests__/loop.test.ts +530 -0
  391. package/src/memory/v3/__tests__/retriever.test.ts +226 -0
  392. package/src/memory/v3/__tests__/scouts.test.ts +440 -0
  393. package/src/memory/v3/__tests__/shadow-middleware.test.ts +312 -0
  394. package/src/memory/v3/__tests__/system-prompts.test.ts +154 -0
  395. package/src/memory/v3/__tests__/traversal.test.ts +469 -0
  396. package/src/memory/v3/__tests__/tree-index.test.ts +280 -0
  397. package/src/memory/v3/__tests__/tree-store.test.ts +529 -0
  398. package/src/memory/v3/__tests__/tree-walk.test.ts +707 -0
  399. package/src/memory/v3/__tests__/validate.test.ts +245 -0
  400. package/src/memory/v3/auto-edges.ts +223 -0
  401. package/src/memory/v3/coactivation-store.ts +124 -0
  402. package/src/memory/v3/consolidation-job.ts +323 -0
  403. package/src/memory/v3/edge-learning-job.ts +160 -0
  404. package/src/memory/v3/edges.ts +249 -0
  405. package/src/memory/v3/filter.ts +281 -0
  406. package/src/memory/v3/gate.ts +334 -0
  407. package/src/memory/v3/index-composition.ts +113 -0
  408. package/src/memory/v3/llm-capture.ts +46 -0
  409. package/src/memory/v3/loop.ts +382 -0
  410. package/src/memory/v3/maintenance.ts +144 -0
  411. package/src/memory/v3/prompt-context.ts +33 -0
  412. package/src/memory/v3/prompts/consolidation.ts +458 -0
  413. package/src/memory/v3/prompts/system-prompts.ts +196 -0
  414. package/src/memory/v3/retriever.ts +33 -0
  415. package/src/memory/v3/scouts.ts +420 -0
  416. package/src/memory/v3/shadow-middleware.ts +305 -0
  417. package/src/memory/v3/traversal.ts +206 -0
  418. package/src/memory/v3/tree-index.ts +237 -0
  419. package/src/memory/v3/tree-store.ts +394 -0
  420. package/src/memory/v3/tree-walk.ts +351 -0
  421. package/src/memory/v3/types.ts +65 -0
  422. package/src/memory/v3/validate.ts +300 -0
  423. package/src/messaging/providers/index.ts +7 -1
  424. package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
  425. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
  426. package/src/messaging/providers/slack/adapter.ts +178 -25
  427. package/src/messaging/providers/slack/api.test.ts +54 -0
  428. package/src/messaging/providers/slack/api.ts +119 -3
  429. package/src/messaging/providers/slack/client.ts +12 -0
  430. package/src/messaging/providers/slack/deep-link.ts +20 -1
  431. package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
  432. package/src/messaging/providers/slack/message-metadata.ts +156 -0
  433. package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
  434. package/src/messaging/providers/slack/render-transcript.ts +176 -49
  435. package/src/messaging/providers/slack/send.test.ts +77 -0
  436. package/src/messaging/providers/slack/send.ts +8 -2
  437. package/src/messaging/providers/slack/types.ts +14 -0
  438. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +4 -1
  439. package/src/notifications/__tests__/home-feed-side-effect.test.ts +116 -54
  440. package/src/notifications/adapters/macos.ts +18 -1
  441. package/src/notifications/adapters/platform.ts +1 -1
  442. package/src/notifications/conversation-seed-composer.ts +14 -2
  443. package/src/notifications/decision-engine.ts +1 -4
  444. package/src/notifications/deferred-emit.ts +135 -0
  445. package/src/notifications/emit-signal.ts +38 -50
  446. package/src/notifications/home-feed-side-effect.ts +60 -30
  447. package/src/oauth/connect-orchestrator.ts +3 -0
  448. package/src/oauth/credential-token-resolver.ts +2 -0
  449. package/src/oauth/manual-token-connection.ts +19 -0
  450. package/src/oauth/oauth-store.ts +12 -0
  451. package/src/oauth/seed-providers.ts +22 -0
  452. package/src/permissions/prompter.ts +8 -5
  453. package/src/permissions/question-prompter.ts +5 -2
  454. package/src/permissions/secret-prompter.ts +6 -3
  455. package/src/plugin-api/index.ts +4 -0
  456. package/src/plugin-api/types.ts +7 -33
  457. package/src/plugins/defaults/index.ts +6 -0
  458. package/src/plugins/defaults/injectors.ts +100 -20
  459. package/src/plugins/external-plugin-loader.ts +5 -68
  460. package/src/plugins/types.ts +11 -16
  461. package/src/proactive-artifact/aux-message-injector.ts +17 -4
  462. package/src/prompts/__tests__/system-prompt.test.ts +46 -2
  463. package/src/prompts/__tests__/task-progress-hint-section.test.ts +3 -9
  464. package/src/prompts/normalize-onboarding.ts +40 -0
  465. package/src/prompts/persona-resolver.ts +36 -21
  466. package/src/prompts/sections.ts +69 -19
  467. package/src/prompts/system-prompt.ts +118 -216
  468. package/src/prompts/template-detection.ts +37 -0
  469. package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
  470. package/src/prompts/templates/BOOTSTRAP.md +10 -2
  471. package/src/prompts/templates/VOICE.md +3 -0
  472. package/src/prompts/templates/system-sections.ts +281 -9
  473. package/src/providers/__tests__/connection-model-compat.test.ts +234 -0
  474. package/src/providers/__tests__/retry-callsite.test.ts +85 -5
  475. package/src/providers/anthropic/client.ts +159 -66
  476. package/src/providers/call-site-routing.ts +14 -2
  477. package/src/providers/connection-model-compat.ts +38 -0
  478. package/src/providers/connection-resolution.ts +16 -2
  479. package/src/providers/fireworks/client.ts +20 -2
  480. package/src/providers/gemini/client.ts +49 -6
  481. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
  482. package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
  483. package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
  484. package/src/providers/inference/adapter-factory.ts +18 -1
  485. package/src/providers/inference/auth.ts +3 -3
  486. package/src/providers/inference/codex-token-refresh.ts +128 -0
  487. package/src/providers/inference/resolve-auth.ts +49 -6
  488. package/src/providers/minimax/client.ts +106 -0
  489. package/src/providers/model-catalog.ts +91 -1
  490. package/src/providers/model-intents.ts +1 -1
  491. package/src/providers/openai/chat-completions-provider.ts +63 -23
  492. package/src/providers/openai/codex-models.ts +18 -0
  493. package/src/providers/openai/responses-provider.ts +86 -23
  494. package/src/providers/openrouter/client.ts +5 -1
  495. package/src/providers/provider-send-message.ts +7 -1
  496. package/src/providers/retry.ts +34 -3
  497. package/src/providers/thinking-config.ts +26 -1
  498. package/src/providers/types.ts +25 -0
  499. package/src/providers/usage-tracking.ts +2 -0
  500. package/src/runtime/AGENTS.md +2 -2
  501. package/src/runtime/__tests__/agent-wake.test.ts +214 -0
  502. package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
  503. package/src/runtime/agent-wake.ts +152 -56
  504. package/src/runtime/assistant-event-hub.ts +76 -6
  505. package/src/runtime/auth/route-policy.ts +43 -3
  506. package/src/runtime/background-job-runner.ts +26 -0
  507. package/src/runtime/btw-sidechain.ts +0 -6
  508. package/src/runtime/channel-reply-delivery.ts +182 -47
  509. package/src/runtime/channel-retry-sweep.ts +141 -16
  510. package/src/runtime/http-types.ts +7 -6
  511. package/src/runtime/migrations/vbundle-builder.ts +10 -3
  512. package/src/runtime/pending-interactions.ts +50 -8
  513. package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
  514. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +161 -1
  515. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
  516. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +290 -0
  517. package/src/runtime/routes/__tests__/plugins-routes.test.ts +512 -0
  518. package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
  519. package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
  520. package/src/runtime/routes/acp-routes.test.ts +255 -6
  521. package/src/runtime/routes/acp-routes.ts +8 -1
  522. package/src/runtime/routes/approval-routes.ts +4 -1
  523. package/src/runtime/routes/avatar-routes.ts +10 -10
  524. package/src/runtime/routes/background-wake-routes.ts +188 -0
  525. package/src/runtime/routes/browser-tabs-routes.ts +200 -0
  526. package/src/runtime/routes/btw-routes.ts +0 -6
  527. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
  528. package/src/runtime/routes/content-source-routes.ts +78 -0
  529. package/src/runtime/routes/conversation-cli-routes.ts +147 -2
  530. package/src/runtime/routes/conversation-list-routes.ts +12 -4
  531. package/src/runtime/routes/conversation-management-routes.ts +77 -20
  532. package/src/runtime/routes/conversation-query-routes.ts +196 -31
  533. package/src/runtime/routes/conversation-routes.ts +472 -425
  534. package/src/runtime/routes/conversation-starter-routes.ts +6 -3
  535. package/src/runtime/routes/disk-pressure-routes.ts +1 -1
  536. package/src/runtime/routes/document-comments-routes.ts +287 -0
  537. package/src/runtime/routes/documents-routes.ts +33 -0
  538. package/src/runtime/routes/domain-routes.ts +60 -10
  539. package/src/runtime/routes/email-routes.ts +5 -2
  540. package/src/runtime/routes/events-routes.ts +54 -10
  541. package/src/runtime/routes/group-routes.ts +24 -8
  542. package/src/runtime/routes/home-feed-routes.ts +6 -3
  543. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  544. package/src/runtime/routes/host-browser-routes.ts +17 -2
  545. package/src/runtime/routes/host-cu-routes.ts +2 -2
  546. package/src/runtime/routes/identity-routes.ts +21 -0
  547. package/src/runtime/routes/inbound-message-handler.ts +288 -58
  548. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +96 -3
  549. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
  550. package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
  551. package/src/runtime/routes/index.ts +20 -4
  552. package/src/runtime/routes/inference-profile-session-handler.ts +22 -12
  553. package/src/runtime/routes/inference-profile-session-routes.ts +7 -1
  554. package/src/runtime/routes/inference-provider-connection-routes.ts +63 -7
  555. package/src/runtime/routes/integrations/a2a.ts +60 -1
  556. package/src/runtime/routes/llm-call-sites-routes.ts +32 -5
  557. package/src/runtime/routes/log-export-routes.ts +39 -0
  558. package/src/runtime/routes/memory-item-routes.ts +8 -3
  559. package/src/runtime/routes/memory-v2-routes.ts +427 -0
  560. package/src/runtime/routes/memory-v3-routes.ts +316 -0
  561. package/src/runtime/routes/migration-routes.ts +21 -24
  562. package/src/runtime/routes/notification-routes.ts +19 -2
  563. package/src/runtime/routes/plugins-routes.ts +337 -0
  564. package/src/runtime/routes/question-routes.ts +4 -1
  565. package/src/runtime/routes/rename-conversation-routes.ts +6 -2
  566. package/src/runtime/routes/sanity-routes.ts +159 -0
  567. package/src/runtime/routes/secret-routes.ts +25 -5
  568. package/src/runtime/routes/settings-routes.ts +12 -11
  569. package/src/runtime/routes/slack-channel-routes.ts +188 -0
  570. package/src/runtime/routes/workspace-routes.ts +25 -10
  571. package/src/runtime/services/conversation-serializer.ts +30 -4
  572. package/src/runtime/sync/resource-sync-events.ts +106 -38
  573. package/src/runtime/sync/sync-publisher.test.ts +49 -0
  574. package/src/runtime/sync/sync-publisher.ts +2 -1
  575. package/src/runtime/verification-outbound-actions.ts +73 -1
  576. package/src/schedule/integration-status.ts +3 -1
  577. package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
  578. package/src/security/oauth2-device-code.ts +307 -0
  579. package/src/security/oauth2.ts +26 -9
  580. package/src/security/secure-keys.ts +5 -0
  581. package/src/skills/catalog-install.ts +6 -2
  582. package/src/telemetry/types.ts +12 -0
  583. package/src/telemetry/usage-telemetry-reporter.test.ts +48 -0
  584. package/src/telemetry/usage-telemetry-reporter.ts +1 -0
  585. package/src/tools/acp/spawn.test.ts +119 -0
  586. package/src/tools/acp/spawn.ts +15 -2
  587. package/src/tools/apps/definitions.ts +2 -8
  588. package/src/tools/ask-question/ask-question-tool.test.ts +3 -3
  589. package/src/tools/ask-question/ask-question-tool.ts +38 -45
  590. package/src/tools/browser/__tests__/pinned-tabs.test.ts +150 -0
  591. package/src/tools/browser/browser-execution.ts +106 -0
  592. package/src/tools/browser/cdp-client/__tests__/browser-tabs-factory.test.ts +402 -0
  593. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
  594. package/src/tools/browser/cdp-client/__tests__/types.test.ts +4 -0
  595. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +22 -0
  596. package/src/tools/browser/cdp-client/extension-cdp-client.ts +42 -2
  597. package/src/tools/browser/cdp-client/factory.ts +171 -4
  598. package/src/tools/browser/cdp-client/local-cdp-client.ts +21 -0
  599. package/src/tools/browser/cdp-client/types.ts +101 -0
  600. package/src/tools/browser/pinned-tabs.ts +146 -0
  601. package/src/tools/computer-use/definitions.ts +22 -78
  602. package/src/tools/credential-execution/make-authenticated-request.ts +3 -9
  603. package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -9
  604. package/src/tools/credential-execution/run-authenticated-command.ts +3 -9
  605. package/src/tools/credentials/vault.ts +3 -9
  606. package/src/tools/document/document-comment-tool.test.ts +379 -0
  607. package/src/tools/document/document-comment-tool.ts +156 -0
  608. package/src/tools/document/document-tool.ts +187 -2
  609. package/src/tools/execution-target.ts +21 -23
  610. package/src/tools/executor.ts +6 -1
  611. package/src/tools/filesystem/edit.ts +3 -9
  612. package/src/tools/filesystem/list.ts +3 -9
  613. package/src/tools/filesystem/read.ts +3 -9
  614. package/src/tools/filesystem/write.ts +3 -9
  615. package/src/tools/host-filesystem/edit.ts +3 -9
  616. package/src/tools/host-filesystem/read.ts +3 -9
  617. package/src/tools/host-filesystem/transfer.ts +3 -9
  618. package/src/tools/host-filesystem/write.ts +3 -9
  619. package/src/tools/host-terminal/host-shell.ts +3 -9
  620. package/src/tools/mcp/mcp-tool-factory.ts +1 -8
  621. package/src/tools/memory/register.test.ts +1 -1
  622. package/src/tools/memory/register.ts +4 -9
  623. package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
  624. package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
  625. package/src/tools/network/domain-normalize.ts +17 -0
  626. package/src/tools/network/web-fetch.ts +216 -73
  627. package/src/tools/network/web-search.ts +216 -98
  628. package/src/tools/registry.ts +7 -23
  629. package/src/tools/schema-transforms.ts +1 -1
  630. package/src/tools/skills/execute.ts +3 -9
  631. package/src/tools/skills/load.ts +3 -9
  632. package/src/tools/skills/skill-tool-factory.ts +1 -8
  633. package/src/tools/subagent/notify-parent.ts +3 -9
  634. package/src/tools/system/request-permission.ts +3 -9
  635. package/src/tools/terminal/safe-env.ts +3 -2
  636. package/src/tools/terminal/shell.ts +3 -9
  637. package/src/tools/tool-approval-handler.ts +19 -12
  638. package/src/tools/tool-defaults.ts +94 -0
  639. package/src/tools/types.ts +31 -98
  640. package/src/tools/ui-surface/definitions.ts +9 -23
  641. package/src/types/onboarding-context.ts +4 -0
  642. package/src/usage/pricing.ts +23 -0
  643. package/src/usage/types.ts +12 -0
  644. package/src/util/__tests__/favicon.test.ts +84 -0
  645. package/src/util/favicon.ts +40 -0
  646. package/src/util/logger.ts +16 -7
  647. package/src/util/platform.ts +7 -7
  648. package/src/util/sqlite3-runtime.ts +65 -0
  649. package/src/workspace/git-service.ts +75 -4
  650. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +1 -0
  651. package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
  652. package/src/workspace/migrations/089-move-memory-tree-out-of-v3.ts +86 -0
  653. package/src/workspace/migrations/registry.ts +4 -0
  654. package/src/__tests__/compaction-strip-metadata-clear.test.ts +0 -206
  655. package/src/__tests__/message-complete-display-id.test.ts +0 -175
  656. package/src/config/bundled-skills/document/SKILL.md +0 -54
  657. package/src/config/bundled-skills/document/TOOLS.json +0 -106
  658. package/src/daemon/seed-files.ts +0 -18
  659. package/src/prompts/cache-boundary.ts +0 -8
  660. package/src/runtime/routes/interface-routes.ts +0 -43
  661. /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
  662. /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
  663. /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
  664. /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
  665. /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
@@ -69,50 +69,37 @@ mock.module("../prompts/user-reference.js", () => ({
69
69
  resolveUserPronouns: () => null,
70
70
  }));
71
71
 
72
+ // Stub persona-resolver so tests can dictate the slug `buildSystemPrompt`
73
+ // sees without needing to write contact rows to the test DB. The user
74
+ // and channel persona files themselves now flow through bundled sections
75
+ // (`10-user-persona` / `11-channel-persona`) that read from disk, so
76
+ // persona *content* is exercised by writing the file under TEST_DIR
77
+ // rather than mocking it here. Tests mutate `mockPersona` in place;
78
+ // the default (all-null) matches a fresh workspace with no contacts
79
+ // and no `users/default.md`.
80
+ const mockPersona: {
81
+ userSlug: string | null;
82
+ guardianPersona: string | null;
83
+ } = { userSlug: null, guardianPersona: null };
84
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
85
+ const realPersonaResolver = require("../prompts/persona-resolver.js");
86
+ mock.module("../prompts/persona-resolver.js", () => ({
87
+ ...realPersonaResolver,
88
+ resolveUserSlug: () => mockPersona.userSlug,
89
+ resolveGuardianPersona: () => mockPersona.guardianPersona,
90
+ }));
91
+
72
92
  // Import after mock
73
- const {
74
- buildSystemPrompt,
75
- ensurePromptFiles,
76
- stripCommentLines,
77
- SYSTEM_PROMPT_CACHE_BOUNDARY,
78
- } = await import("../prompts/system-prompt.js");
79
-
80
- /**
81
- * Extract IDENTITY.md / BOOTSTRAP.md content + the user persona from the
82
- * dynamic block of the system prompt, stripping configuration, skills
83
- * catalog, and connected services.
84
- *
85
- * SOUL.md no longer flows through this helper — it renders as the
86
- * `09-soul` workspace-backed section in the static (cached) prefix.
87
- * Tests that assert on SOUL.md content slice the static block directly.
88
- */
89
- function basePrompt(result: string): string {
90
- // The workspace files are in the dynamic block after the cache boundary.
91
- const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
92
- let s =
93
- boundaryIdx >= 0
94
- ? result.slice(boundaryIdx + SYSTEM_PROMPT_CACHE_BOUNDARY.length)
95
- : result;
96
- for (const heading of [
97
- "## Configuration",
98
- "## Skills Catalog",
99
- "## External Communications Identity",
100
- "# Connected Services",
101
- "## Dynamic Skill Authoring Workflow",
102
- ]) {
103
- if (s.startsWith(heading)) {
104
- s = "";
105
- break;
106
- }
107
- const idx = s.indexOf(`\n\n${heading}`);
108
- if (idx !== -1) s = s.slice(0, idx);
109
- }
110
- return s;
111
- }
93
+ const { buildSystemPrompt, ensurePromptFiles, stripCommentLines } =
94
+ await import("../prompts/system-prompt.js");
112
95
 
113
96
  describe("buildSystemPrompt", () => {
114
97
  beforeEach(() => {
115
98
  mkdirSync(TEST_DIR, { recursive: true });
99
+ // Reset persona stub so each test starts from a fresh
100
+ // no-guardian baseline.
101
+ mockPersona.userSlug = null;
102
+ mockPersona.guardianPersona = null;
116
103
  });
117
104
 
118
105
  afterEach(() => {
@@ -122,8 +109,10 @@ describe("buildSystemPrompt", () => {
122
109
  "USER.md",
123
110
  "BOOTSTRAP.md",
124
111
  "UPDATES.md",
112
+ "VOICE.md",
125
113
  "skills",
126
114
  "users",
115
+ "channels",
127
116
  ]) {
128
117
  const p = join(TEST_DIR, name);
129
118
  if (existsSync(p)) rmSync(p, { recursive: true, force: true });
@@ -133,19 +122,11 @@ describe("buildSystemPrompt", () => {
133
122
  }
134
123
  });
135
124
 
136
- test("returns empty string when no files exist", () => {
137
- const result = buildSystemPrompt();
138
- expect(basePrompt(result)).toBe("");
139
- });
140
-
141
125
  test("uses SOUL.md when it exists", () => {
142
126
  writeFileSync(join(TEST_DIR, "SOUL.md"), "# My Soul\n\nBe awesome.");
143
127
  const result = buildSystemPrompt();
144
- // SOUL.md renders as the `09-soul` workspace-backed section in the
145
- // static (cached) prefix before SYSTEM_PROMPT_CACHE_BOUNDARY.
146
- const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
147
- expect(boundaryIdx).toBeGreaterThan(-1);
148
- expect(result.slice(0, boundaryIdx)).toContain("# My Soul\n\nBe awesome.");
128
+ // SOUL.md renders as the `09-soul` workspace-backed section.
129
+ expect(result).toContain("# My Soul\n\nBe awesome.");
149
130
  });
150
131
 
151
132
  test("uses IDENTITY.md when it exists", () => {
@@ -154,30 +135,40 @@ describe("buildSystemPrompt", () => {
154
135
  "# My Identity\n\nI am Vellum.",
155
136
  );
156
137
  const result = buildSystemPrompt();
157
- expect(basePrompt(result)).toBe("# My Identity\n\nI am Vellum.");
138
+ // IDENTITY.md renders as the `08-identity` workspace-backed section.
139
+ expect(result).toContain("# My Identity\n\nI am Vellum.");
158
140
  });
159
141
 
160
142
  test("composes IDENTITY.md + SOUL.md when both exist", () => {
161
143
  writeFileSync(join(TEST_DIR, "IDENTITY.md"), "# Identity\n\nI am Vellum.");
162
144
  writeFileSync(join(TEST_DIR, "SOUL.md"), "# Soul\n\nBe thoughtful.");
163
145
  const result = buildSystemPrompt();
164
- // SOUL renders as the workspace-backed section in the static prefix;
165
- // IDENTITY renders in the dynamic suffix.
166
- const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
167
- expect(result.slice(0, boundaryIdx)).toContain("# Soul\n\nBe thoughtful.");
168
- expect(basePrompt(result)).toBe("# Identity\n\nI am Vellum.");
146
+ // IDENTITY renders before SOUL (sections `08-identity` then
147
+ // `09-soul`).
148
+ expect(result).toContain("# Identity\n\nI am Vellum.");
149
+ expect(result).toContain("# Soul\n\nBe thoughtful.");
150
+ const identityIdx = result.indexOf("# Identity\n\nI am Vellum.");
151
+ const soulIdx = result.indexOf("# Soul\n\nBe thoughtful.");
152
+ expect(identityIdx).toBeLessThan(soulIdx);
169
153
  });
170
154
 
171
155
  test("ignores empty SOUL.md", () => {
172
156
  writeFileSync(join(TEST_DIR, "SOUL.md"), " \n \n ");
157
+ writeFileSync(join(TEST_DIR, "IDENTITY.md"), "# Identity\n\nI am Vellum.");
173
158
  const result = buildSystemPrompt();
174
- expect(basePrompt(result)).toBe("");
159
+ // IDENTITY renders but SOUL is gated off by the renderer's
160
+ // empty-body check; no SOUL content should appear.
161
+ expect(result).toContain("# Identity\n\nI am Vellum.");
162
+ expect(result).not.toContain(" \n \n ");
175
163
  });
176
164
 
177
165
  test("ignores empty IDENTITY.md", () => {
178
166
  writeFileSync(join(TEST_DIR, "IDENTITY.md"), "");
167
+ writeFileSync(join(TEST_DIR, "SOUL.md"), "# Soul\n\nBe thoughtful.");
179
168
  const result = buildSystemPrompt();
180
- expect(basePrompt(result)).toBe("");
169
+ // SOUL renders but IDENTITY's empty file is gated off by the
170
+ // renderer's empty-body check.
171
+ expect(result).toContain("# Soul\n\nBe thoughtful.");
181
172
  });
182
173
 
183
174
  test("trims whitespace from file content", () => {
@@ -185,10 +176,8 @@ describe("buildSystemPrompt", () => {
185
176
  const result = buildSystemPrompt();
186
177
  // SOUL.md renders via the `09-soul` workspace-backed section;
187
178
  // stripCommentLines + trim run inside the section renderer.
188
- const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
189
- const staticBlock = result.slice(0, boundaryIdx);
190
- expect(staticBlock).toContain("Be kind");
191
- expect(staticBlock).not.toContain("\n Be kind \n");
179
+ expect(result).toContain("Be kind");
180
+ expect(result).not.toContain("\n Be kind \n");
192
181
  });
193
182
 
194
183
  test("does not include skills catalog in system prompt", () => {
@@ -217,10 +206,9 @@ describe("buildSystemPrompt", () => {
217
206
  writeFileSync(join(TEST_DIR, "SOUL.md"), "Soul content");
218
207
 
219
208
  const result = buildSystemPrompt();
220
- // After SOUL.md became the `09-soul` workspace-backed section, it
221
- // renders in the static prefix while IDENTITY stays in the dynamic
222
- // suffix — the two are no longer adjacent. Verify both are present
223
- // and the skills catalog is still suppressed.
209
+ // Both files render in the static prefix via `08-identity` /
210
+ // `09-soul`. Verify both are present and the skills catalog is
211
+ // still suppressed.
224
212
  expect(result).toContain("Identity content");
225
213
  expect(result).toContain("Soul content");
226
214
  expect(result).not.toContain("## Available Skills");
@@ -262,11 +250,7 @@ describe("buildSystemPrompt", () => {
262
250
  writeFileSync(join(TEST_DIR, "IDENTITY.md"), "Identity");
263
251
  writeFileSync(join(TEST_DIR, "SOUL.md"), "Soul");
264
252
  const result = buildSystemPrompt();
265
- // SOUL.md now renders in the static (cached) prefix via the 09-soul
266
- // section, so it doesn't flow through basePrompt. Assert that
267
- // IDENTITY is the only dynamic content and that SOUL is still in the
268
- // full prompt.
269
- expect(basePrompt(result)).toBe("Identity");
253
+ expect(result).toContain("Identity");
270
254
  expect(result).toContain("Soul");
271
255
  });
272
256
 
@@ -280,55 +264,118 @@ describe("buildSystemPrompt", () => {
280
264
  );
281
265
  const result = buildSystemPrompt();
282
266
  expect(result).not.toContain("stale user content");
283
- expect(basePrompt(result)).toBe("Identity");
267
+ expect(result).toContain("Identity");
284
268
  });
285
269
 
286
- test("uses options.userPersona instead of USER.md", () => {
270
+ test("includes resolved user persona in the static prefix", () => {
271
+ // User persona flows through the `10-user-persona` bundled section,
272
+ // which reads from `users/<userSlug>.md` (or `users/default.md` as
273
+ // a fallback). Set the slug + write the file to exercise both.
274
+ mockPersona.userSlug = "alice";
275
+ mkdirSync(join(TEST_DIR, "users"), { recursive: true });
276
+ writeFileSync(
277
+ join(TEST_DIR, "users", "alice.md"),
278
+ "# User persona\n\nName: Alice",
279
+ );
287
280
  writeFileSync(join(TEST_DIR, "IDENTITY.md"), "Identity");
288
281
  writeFileSync(join(TEST_DIR, "SOUL.md"), "Soul");
289
- const result = buildSystemPrompt({
290
- userPersona: "# User persona\n\nName: Alice",
291
- });
292
- // SOUL.md renders in the static (cached) prefix via the 09-soul section
293
- // and is no longer part of the dynamic block sliced by basePrompt.
294
- expect(basePrompt(result)).toBe(
295
- "Identity\n\n# User persona\n\nName: Alice",
296
- );
282
+ const result = buildSystemPrompt();
283
+ // IDENTITY, SOUL, and the user persona all render as workspace-backed
284
+ // bundled sections in the assembled prompt.
285
+ expect(result).toContain("Identity");
297
286
  expect(result).toContain("Soul");
287
+ expect(result).toContain("# User persona");
288
+ expect(result).toContain("Name: Alice");
289
+ });
290
+
291
+ test("user persona falls back to users/default.md when the slug's file is missing", () => {
292
+ // The `10-user-persona` section's workspacePath is
293
+ // `["users/{{userSlug}}.md", "users/default.md"]` — when the
294
+ // primary file doesn't exist the renderer falls through to default.
295
+ mockPersona.userSlug = "alice";
296
+ mkdirSync(join(TEST_DIR, "users"), { recursive: true });
297
+ writeFileSync(
298
+ join(TEST_DIR, "users", "default.md"),
299
+ "# Default persona\n\nNo contact bound.",
300
+ );
301
+ const result = buildSystemPrompt();
302
+ expect(result).toContain("# Default persona");
303
+ expect(result).toContain("No contact bound.");
304
+ });
305
+
306
+ test("includes channel persona from channels/<channelSlug>.md", () => {
307
+ // Channel persona flows through the `11-channel-persona` section.
308
+ // Default channel is "vellum" when no channelCapabilities passed.
309
+ mkdirSync(join(TEST_DIR, "channels"), { recursive: true });
310
+ writeFileSync(
311
+ join(TEST_DIR, "channels", "vellum.md"),
312
+ "# Channel persona\n\nThis is the Vellum channel.",
313
+ );
314
+ const result = buildSystemPrompt();
315
+ expect(result).toContain("# Channel persona");
316
+ expect(result).toContain("This is the Vellum channel.");
317
+ });
318
+
319
+ test("includes VOICE.md as the 12-voice section with prepended heading", () => {
320
+ // VOICE.md flows through the `12-voice` bundled section. The
321
+ // section transform prepends `# Voice Profile` so the file itself
322
+ // stays heading-free; the model writes voice markers as plain
323
+ // bullets / lines.
324
+ writeFileSync(
325
+ join(TEST_DIR, "VOICE.md"),
326
+ "- Prefers lowercase. Replies tightly. Skips greetings.",
327
+ );
328
+ const result = buildSystemPrompt();
329
+ expect(result).toContain("# Voice Profile");
330
+ expect(result).toContain("Prefers lowercase");
331
+ });
332
+
333
+ test("omits the 12-voice section when VOICE.md is missing", () => {
334
+ const result = buildSystemPrompt();
335
+ expect(result).not.toContain("# Voice Profile");
336
+ });
337
+
338
+ test("omits the 12-voice section when VOICE.md is empty / whitespace-only", () => {
339
+ writeFileSync(join(TEST_DIR, "VOICE.md"), " \n\n \n");
340
+ const result = buildSystemPrompt();
341
+ expect(result).not.toContain("# Voice Profile");
298
342
  });
299
343
 
300
344
  describe("BOOTSTRAP.md user persona placeholder", () => {
301
- test("substitutes {{USER_PERSONA_FILE}} with users/<slug>.md when userSlug is provided", () => {
345
+ test("substitutes {{userSlug}} with the resolved slug when a guardian slug is resolvable", () => {
346
+ // Simulate a guardian contact whose userFile resolves to alice.md.
347
+ mockPersona.userSlug = "alice";
302
348
  writeFileSync(
303
349
  join(TEST_DIR, "BOOTSTRAP.md"),
304
- "# First run\n\nSave facts to users/{{USER_PERSONA_FILE}} immediately.",
350
+ "# First run\n\nSave facts to users/{{userSlug}}.md immediately.",
305
351
  );
306
- const result = buildSystemPrompt({ userSlug: "alice" });
352
+ const result = buildSystemPrompt();
307
353
  expect(result).toContain("users/alice.md");
308
- expect(result).not.toContain("{{USER_PERSONA_FILE}}");
354
+ expect(result).not.toContain("{{userSlug}}");
309
355
  });
310
356
 
311
- test("falls back to users/default.md when userSlug is omitted", () => {
357
+ test("falls back to users/default.md when no slug is resolvable", () => {
312
358
  writeFileSync(
313
359
  join(TEST_DIR, "BOOTSTRAP.md"),
314
- "# First run\n\nSave facts to users/{{USER_PERSONA_FILE}} immediately.",
360
+ "# First run\n\nSave facts to users/{{userSlug}}.md immediately.",
315
361
  );
316
362
  const result = buildSystemPrompt();
317
363
  expect(result).toContain("users/default.md");
318
- expect(result).not.toContain("{{USER_PERSONA_FILE}}");
364
+ expect(result).not.toContain("{{userSlug}}");
319
365
  });
320
366
 
321
367
  test("substitutes the unmodified bundled BOOTSTRAP.md template", () => {
322
368
  // Copy the real bundled BOOTSTRAP.md into the test workspace so we
323
369
  // verify substitution against the actual template the daemon ships.
370
+ mockPersona.userSlug = "alice";
324
371
  const bundled = readFileSync(
325
372
  join(import.meta.dirname, "..", "prompts", "templates", "BOOTSTRAP.md"),
326
373
  "utf-8",
327
374
  );
328
375
  writeFileSync(join(TEST_DIR, "BOOTSTRAP.md"), bundled);
329
- const result = buildSystemPrompt({ userSlug: "alice" });
376
+ const result = buildSystemPrompt();
330
377
  expect(result).toContain("users/alice.md");
331
- expect(result).not.toContain("{{USER_PERSONA_FILE}}");
378
+ expect(result).not.toContain("{{userSlug}}");
332
379
  });
333
380
  });
334
381
 
@@ -480,7 +527,11 @@ describe("buildSystemPrompt", () => {
480
527
  "# Identity\n_ This is a comment\nI am Vellum.\n_ Another comment",
481
528
  );
482
529
  const result = buildSystemPrompt();
483
- expect(basePrompt(result)).toBe("# Identity\nI am Vellum.");
530
+ // IDENTITY.md renders in the static prefix via the 08-identity section,
531
+ // so we assert against the full prompt rather than basePrompt.
532
+ expect(result).toContain("# Identity\nI am Vellum.");
533
+ expect(result).not.toContain("_ This is a comment");
534
+ expect(result).not.toContain("_ Another comment");
484
535
  });
485
536
 
486
537
  test("collapses whitespace around stripped comment lines", () => {
@@ -498,8 +549,14 @@ describe("buildSystemPrompt", () => {
498
549
 
499
550
  test("file with only comment lines is treated as empty", () => {
500
551
  writeFileSync(join(TEST_DIR, "SOUL.md"), "_ All comments\n_ Nothing else");
552
+ writeFileSync(join(TEST_DIR, "IDENTITY.md"), "# Identity\n\nI am Vellum.");
501
553
  const result = buildSystemPrompt();
502
- expect(basePrompt(result)).toBe("");
554
+ // Comment-only SOUL.md gets stripped down to "" by
555
+ // `stripCommentLines` and is then gated off by the renderer's
556
+ // empty-body check; only IDENTITY contributes content here.
557
+ expect(result).toContain("# Identity\n\nI am Vellum.");
558
+ expect(result).not.toContain("_ All comments");
559
+ expect(result).not.toContain("_ Nothing else");
503
560
  });
504
561
 
505
562
  describe("workspace system prompt sections", () => {
@@ -532,11 +589,7 @@ describe("buildSystemPrompt", () => {
532
589
  );
533
590
  const result = buildSystemPrompt();
534
591
  expect(result.startsWith("You are operating in demo mode.")).toBe(true);
535
- // Prefix lives in the static (cached) block.
536
- const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
537
- expect(boundaryIdx).toBeGreaterThan(-1);
538
- const staticBlock = result.slice(0, boundaryIdx);
539
- expect(staticBlock).toContain("You are operating in demo mode.");
592
+ expect(result).toContain("You are operating in demo mode.");
540
593
  });
541
594
 
542
595
  test("workspace file without frontmatter is rendered as-is (always-on)", () => {
@@ -610,7 +663,12 @@ describe("buildSystemPrompt", () => {
610
663
  writeFileSync(join(TEST_DIR, "IDENTITY.md"), "I am Vellum.");
611
664
  const result = buildSystemPrompt();
612
665
  expect(result.startsWith("Custom prefix")).toBe(true);
613
- expect(basePrompt(result)).toBe("I am Vellum.");
666
+ // IDENTITY.md renders via 08-identity in the static prefix after
667
+ // the 00-prefix slot.
668
+ const prefixIdx = result.indexOf("Custom prefix");
669
+ const identityIdx = result.indexOf("I am Vellum.");
670
+ expect(prefixIdx).toBeGreaterThan(-1);
671
+ expect(identityIdx).toBeGreaterThan(prefixIdx);
614
672
  });
615
673
 
616
674
  test("parallel tool calls section is sourced from workspace when present", () => {
@@ -786,11 +844,7 @@ describe("buildSystemPrompt", () => {
786
844
  expect(result).toContain(
787
845
  "Run `assistant --help` to discover commands.",
788
846
  );
789
- // Section lives in the static (cached) block.
790
- const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
791
- expect(boundaryIdx).toBeGreaterThan(-1);
792
- const staticBlock = result.slice(0, boundaryIdx);
793
- expect(staticBlock).toContain("## Assistant CLI");
847
+ expect(result).toContain("## Assistant CLI");
794
848
  });
795
849
 
796
850
  test("bundled cli-reference default renders when no workspace override", () => {
@@ -831,11 +885,7 @@ describe("buildSystemPrompt", () => {
831
885
  // The no-client body (em-dash separator after sandbox `bash`) must
832
886
  // not leak when the with-client variant is active.
833
887
  expect(result).not.toContain("install tools yourself; (2) browser");
834
- // Section lives in the static (cached) block.
835
- const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
836
- expect(boundaryIdx).toBeGreaterThan(-1);
837
- const staticBlock = result.slice(0, boundaryIdx);
838
- expect(staticBlock).toContain("## External Service Access");
888
+ expect(result).toContain("## External Service Access");
839
889
  });
840
890
 
841
891
  test("hasNoClient=true renders the two-tier (no host_bash) priority list", () => {
@@ -944,8 +994,7 @@ describe("buildSystemPrompt", () => {
944
994
 
945
995
  test("paired {{#flag}} + {{^flag}} acts as if/else", () => {
946
996
  // Use long unique markers — single letters collide with substrings
947
- // in the rest of the system prompt (e.g. "B" lives inside
948
- // SYSTEM_PROMPT_CACHE_BOUNDARY, "A" inside "API keys").
997
+ // in the rest of the system prompt (e.g. "A" inside "API keys").
949
998
  mkdirSync(SYSTEM_PROMPTS_DIR, { recursive: true });
950
999
  writeFileSync(
951
1000
  SECTION_FILE,
@@ -1010,11 +1059,7 @@ describe("buildSystemPrompt", () => {
1010
1059
  const result = buildSystemPrompt();
1011
1060
  expect(result).toContain("## Sending Files to the User");
1012
1061
  expect(result).toContain("Use the `<vellum-attachment />` tag.");
1013
- // Section lives in the static (cached) block.
1014
- const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
1015
- expect(boundaryIdx).toBeGreaterThan(-1);
1016
- const staticBlock = result.slice(0, boundaryIdx);
1017
- expect(staticBlock).toContain("## Sending Files to the User");
1062
+ expect(result).toContain("## Sending Files to the User");
1018
1063
  });
1019
1064
 
1020
1065
  test("renders after the cli-reference section to preserve original order", () => {
@@ -1060,11 +1105,7 @@ describe("buildSystemPrompt", () => {
1060
1105
  const result = buildSystemPrompt();
1061
1106
  expect(result).toContain("## Credential Security");
1062
1107
  expect(result).toContain("Workspace override marker BRAVO_TANGO_7.");
1063
- // Section lives in the static (cached) block.
1064
- const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
1065
- expect(boundaryIdx).toBeGreaterThan(-1);
1066
- const staticBlock = result.slice(0, boundaryIdx);
1067
- expect(staticBlock).toContain("## Credential Security");
1108
+ expect(result).toContain("## Credential Security");
1068
1109
  });
1069
1110
 
1070
1111
  test("bundled credential-security default renders when no workspace override", () => {
@@ -1104,11 +1145,7 @@ describe("buildSystemPrompt", () => {
1104
1145
  const result = buildSystemPrompt();
1105
1146
  expect(result).toContain("## External Content");
1106
1147
  expect(result).toContain("Workspace override marker NEBULA_9X.");
1107
- // Section lives in the static (cached) block.
1108
- const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
1109
- expect(boundaryIdx).toBeGreaterThan(-1);
1110
- const staticBlock = result.slice(0, boundaryIdx);
1111
- expect(staticBlock).toContain("## External Content");
1148
+ expect(result).toContain("## External Content");
1112
1149
  });
1113
1150
 
1114
1151
  test("bundled external-content default renders when no workspace override", () => {
@@ -67,6 +67,7 @@ mock.module("../tools/network/script-proxy/index.js", () => ({
67
67
  import {
68
68
  ALWAYS_INJECTED_ENV_VARS,
69
69
  buildSanitizedEnv,
70
+ KATA_INJECTED_ENV_VARS,
70
71
  KATA_SAFE_ENV_VARS,
71
72
  SAFE_ENV_VARS,
72
73
  } from "../tools/terminal/safe-env.js";
@@ -145,7 +146,7 @@ describe("buildSanitizedEnv", () => {
145
146
  process.env.VELLUM_SANDBOX_RUNTIME = "gvisor";
146
147
  process.env.PATH = "/usr/bin";
147
148
  process.env.VELLUM_APT_DATA_ROOT = "/data/system";
148
- process.env.LD_LIBRARY_PATH = "/data/system/usr/lib";
149
+ process.env.LD_LIBRARY_PATH = "/host/lib";
149
150
 
150
151
  let env = buildSanitizedEnv();
151
152
  expect(env.VELLUM_APT_DATA_ROOT).toBeUndefined();
@@ -161,6 +162,7 @@ describe("buildSanitizedEnv", () => {
161
162
  expect(env.LD_LIBRARY_PATH.split(":")).toContain(
162
163
  "/data/system/usr/local/lib",
163
164
  );
165
+ expect(env.LD_LIBRARY_PATH.split(":")).not.toContain("/host/lib");
164
166
  });
165
167
 
166
168
  test("defaults LANG and LC_ALL to UTF-8 when unset", () => {
@@ -181,12 +183,20 @@ describe("buildSanitizedEnv", () => {
181
183
  delete process.env.GATEWAY_PORT;
182
184
  });
183
185
 
186
+ test("Kata entrypoint initializes apt root without inheriting apt loader paths", () => {
187
+ const entrypoint = readFileSync("docker-entrypoint.sh", "utf8");
188
+
189
+ expect(entrypoint).toContain("/app/assistant/docker-init-apt-root.sh");
190
+ expect(entrypoint).not.toContain(". /app/assistant/docker-kata-apt-env.sh");
191
+ });
192
+
184
193
  test("result is a plain object with no prototype-inherited secrets", () => {
185
194
  const env = buildSanitizedEnv();
186
195
  const keys = Object.keys(env);
187
196
  const safeKeys: string[] = [
188
197
  ...SAFE_ENV_VARS,
189
198
  ...KATA_SAFE_ENV_VARS,
199
+ ...KATA_INJECTED_ENV_VARS,
190
200
  ...ALWAYS_INJECTED_ENV_VARS,
191
201
  ];
192
202
  for (const key of keys) {
@@ -278,7 +288,7 @@ describe("Shell tool input validation", () => {
278
288
  });
279
289
 
280
290
  test("tool definition includes required schema fields", () => {
281
- const def = shellTool.getDefinition();
291
+ const def = shellTool;
282
292
  const schema = def.input_schema as {
283
293
  required: string[];
284
294
  properties: Record<string, unknown>;
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Reproduction & regression tests for the thinking-block provider-switch bug.
3
+ *
4
+ * Phase 1 (real API): proves that replaying a thinking block with a tampered
5
+ * signature causes Anthropic to reject the request with a 400.
6
+ * Phase 2 (mocked): verifies the send-time filtering fix strips historical
7
+ * thinking blocks while preserving active tool-use continuation blocks.
8
+ */
9
+
10
+ import { describe, expect, test } from "bun:test";
11
+
12
+ import Anthropic from "@anthropic-ai/sdk";
13
+
14
+ // ---------------------------------------------------------------------------
15
+ // Phase 1 — Real API reproduction
16
+ // ---------------------------------------------------------------------------
17
+
18
+ const apiKey = process.env.ANTHROPIC_API_KEY;
19
+
20
+ describe.skipIf(!apiKey)(
21
+ "Thinking block replay — real API reproduction",
22
+ () => {
23
+ test("Anthropic rejects a tampered thinking signature with 400", async () => {
24
+ const client = new Anthropic({ apiKey });
25
+
26
+ // Step 1: Get a real thinking block from the API
27
+ const initialResponse = await client.messages.create({
28
+ model: "claude-sonnet-4-6",
29
+ max_tokens: 4096,
30
+ thinking: { type: "enabled", budget_tokens: 1024 },
31
+ messages: [{ role: "user", content: "What is 2 + 2?" }],
32
+ });
33
+
34
+ const thinkingBlock = initialResponse.content.find(
35
+ (b) => b.type === "thinking",
36
+ ) as Anthropic.ThinkingBlock | undefined;
37
+
38
+ expect(thinkingBlock).toBeDefined();
39
+ expect(thinkingBlock!.signature).toBeTruthy();
40
+
41
+ // Step 2: Tamper with the signature to simulate a stale/cross-provider block
42
+ const tamperedSignature =
43
+ thinkingBlock!.signature.slice(0, -4) + "XXXX";
44
+
45
+ // Step 3: Replay the tampered block as historical context
46
+ const historicalAssistantContent: Anthropic.ContentBlockParam[] = [
47
+ {
48
+ type: "thinking",
49
+ thinking: thinkingBlock!.thinking,
50
+ signature: tamperedSignature,
51
+ },
52
+ { type: "text", text: "4" },
53
+ ];
54
+
55
+ // Step 4: Confirm Anthropic rejects with 400
56
+ try {
57
+ await client.messages.create({
58
+ model: "claude-sonnet-4-6",
59
+ max_tokens: 4096,
60
+ thinking: { type: "enabled", budget_tokens: 1024 },
61
+ messages: [
62
+ { role: "user", content: "What is 2 + 2?" },
63
+ { role: "assistant", content: historicalAssistantContent },
64
+ { role: "user", content: "And what is 3 + 3?" },
65
+ ],
66
+ });
67
+ expect.unreachable("API should have rejected the tampered signature");
68
+ } catch (err: unknown) {
69
+ const apiErr = err as { status?: number; message?: string };
70
+ expect(apiErr.status).toBe(400);
71
+ expect(apiErr.message).toContain("signature");
72
+ }
73
+ }, 30_000);
74
+
75
+ test("Anthropic accepts the request when thinking blocks are stripped from historical turns", async () => {
76
+ const client = new Anthropic({ apiKey });
77
+
78
+ // Step 1: Get a real thinking block
79
+ const initialResponse = await client.messages.create({
80
+ model: "claude-sonnet-4-6",
81
+ max_tokens: 4096,
82
+ thinking: { type: "enabled", budget_tokens: 1024 },
83
+ messages: [{ role: "user", content: "What is 2 + 2?" }],
84
+ });
85
+
86
+ const thinkingBlock = initialResponse.content.find(
87
+ (b) => b.type === "thinking",
88
+ ) as Anthropic.ThinkingBlock | undefined;
89
+
90
+ expect(thinkingBlock).toBeDefined();
91
+
92
+ // Step 2: Build history WITHOUT thinking blocks (the fix)
93
+ const cleanAssistantContent: Anthropic.ContentBlockParam[] = [
94
+ { type: "text", text: "4" },
95
+ ];
96
+
97
+ // Step 3: Confirm request succeeds
98
+ const followUp = await client.messages.create({
99
+ model: "claude-sonnet-4-6",
100
+ max_tokens: 4096,
101
+ thinking: { type: "enabled", budget_tokens: 1024 },
102
+ messages: [
103
+ { role: "user", content: "What is 2 + 2?" },
104
+ { role: "assistant", content: cleanAssistantContent },
105
+ { role: "user", content: "And what is 3 + 3?" },
106
+ ],
107
+ });
108
+
109
+ expect(followUp.content).toBeDefined();
110
+ expect(followUp.stop_reason).toBe("end_turn");
111
+ }, 30_000);
112
+ },
113
+ );