@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
@@ -93,7 +93,10 @@ function collectSectionIds(workspaceDir: string): string[] {
93
93
  if (name.endsWith(".md")) ids.add(name.slice(0, -".md".length));
94
94
  }
95
95
  } catch (err) {
96
- log.warn({ err, workspaceDir }, "Failed to list workspace system prompt dir");
96
+ log.warn(
97
+ { err, workspaceDir },
98
+ "Failed to list workspace system prompt dir",
99
+ );
97
100
  }
98
101
  }
99
102
  return [...ids].sort();
@@ -102,10 +105,12 @@ function collectSectionIds(workspaceDir: string): string[] {
102
105
  interface ResolvedSection {
103
106
  enabled: string | boolean | undefined;
104
107
  body: string;
108
+ transform?: BundledSection["transform"];
105
109
  }
106
110
 
107
111
  function resolveSection(
108
112
  id: string,
113
+ ctx: SectionRenderContext,
109
114
  workspaceDir: string,
110
115
  ): ResolvedSection | null {
111
116
  const workspacePath = join(workspaceDir, `${id}.md`);
@@ -114,12 +119,20 @@ function resolveSection(
114
119
  try {
115
120
  raw = readFileSync(workspacePath, "utf-8");
116
121
  } catch (err) {
117
- log.warn({ err, workspacePath }, "Failed to read workspace section override");
122
+ log.warn(
123
+ { err, workspacePath },
124
+ "Failed to read workspace section override",
125
+ );
118
126
  return null;
119
127
  }
120
128
  const parsed = parseFrontmatterFields(raw);
121
129
  const fields = parsed?.fields ?? {};
122
130
  const body = parsed?.body ?? raw;
131
+ // Workspace override skips the bundled transform: when the user has
132
+ // written their own `prompts/system/<id>.md` they've taken full
133
+ // control of the body shape, and re-running the bundled transform
134
+ // (e.g. unmodified-template detection on IDENTITY.md) would
135
+ // misclassify their override.
123
136
  return { enabled: fields["enabled"] as string | boolean | undefined, body };
124
137
  }
125
138
  const bundled = BUNDLED_SYSTEM_SECTIONS.find((s) => s.id === id);
@@ -127,25 +140,58 @@ function resolveSection(
127
140
 
128
141
  // A bundled section may delegate its body to a workspace file outside
129
142
  // the section override directory (e.g. `SOUL.md` at the workspace
130
- // root). Read it now; missing/empty files yield "", which
131
- // `renderSection` then gates off via its empty-body check.
143
+ // root). `workspacePath` may be a single path or an array of paths
144
+ // tried in order the first one whose file exists and has non-empty
145
+ // content wins. Each entry may reference `{{ctx-key}}` variables
146
+ // (e.g. `users/{{userSlug}}.md`) that are interpolated against the
147
+ // render context before resolution. Missing/empty files yield "",
148
+ // which `renderSection` then gates off via its empty-body check (or
149
+ // via the section's `transform`, if set).
132
150
  if (bundled.workspacePath) {
133
- const filePath = getWorkspacePromptPath(bundled.workspacePath);
151
+ const paths = Array.isArray(bundled.workspacePath)
152
+ ? bundled.workspacePath
153
+ : [bundled.workspacePath];
134
154
  let body = "";
135
- if (existsSync(filePath)) {
155
+ for (const pathTemplate of paths) {
156
+ const interpolated = interpolateWorkspacePath(pathTemplate, ctx);
157
+ const filePath = getWorkspacePromptPath(interpolated);
158
+ if (!existsSync(filePath)) continue;
136
159
  try {
137
- body = readFileSync(filePath, "utf-8");
160
+ const content = readFileSync(filePath, "utf-8");
161
+ if (content.trim().length > 0) {
162
+ body = content;
163
+ break;
164
+ }
138
165
  } catch (err) {
139
- log.warn(
140
- { err, filePath, id },
141
- "Failed to read section workspacePath",
142
- );
166
+ log.warn({ err, filePath, id }, "Failed to read section workspacePath");
143
167
  }
144
168
  }
145
- return { enabled: bundled.enabled, body };
169
+ return { enabled: bundled.enabled, body, transform: bundled.transform };
146
170
  }
147
171
 
148
- return { enabled: bundled.enabled, body: bundled.body };
172
+ return {
173
+ enabled: bundled.enabled,
174
+ body: bundled.body,
175
+ transform: bundled.transform,
176
+ };
177
+ }
178
+
179
+ /**
180
+ * Interpolate `{{key}}` references in a workspace-path template against
181
+ * `ctx`. Section / inverted-section tags are not supported in paths —
182
+ * only flat variable substitution. Unresolved keys stay literal so a
183
+ * typo surfaces as a missing file rather than silently rendering an
184
+ * unrelated section.
185
+ */
186
+ function interpolateWorkspacePath(
187
+ template: string,
188
+ ctx: SectionRenderContext,
189
+ ): string {
190
+ return template.replace(VARIABLE, (match, key: string) => {
191
+ const value = ctx[key];
192
+ if (value === undefined || value === null) return match;
193
+ return String(value);
194
+ });
149
195
  }
150
196
 
151
197
  function renderSection(
@@ -153,12 +199,19 @@ function renderSection(
153
199
  ctx: SectionRenderContext,
154
200
  workspaceDir: string,
155
201
  ): string | null {
156
- const section = resolveSection(id, workspaceDir);
202
+ const section = resolveSection(id, ctx, workspaceDir);
157
203
  if (section === null) return null;
158
204
 
159
205
  if (!isEnabled(section.enabled, ctx)) return null;
160
206
 
161
- const stripped = stripCommentLines(section.body).trim();
207
+ let body = section.body;
208
+ if (section.transform) {
209
+ const transformed = section.transform(body, ctx);
210
+ if (transformed === null) return null;
211
+ body = transformed;
212
+ }
213
+
214
+ const stripped = stripCommentLines(body).trim();
162
215
  if (stripped.length === 0) return null;
163
216
  return interpolateVariables(stripped, ctx);
164
217
  }
@@ -190,10 +243,7 @@ const IDENT_REGEX = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
190
243
  * typo on a `{{key}}` substitution surfaces at the warn log rather than
191
244
  * inlining the string `"undefined"`).
192
245
  */
193
- function interpolateVariables(
194
- body: string,
195
- ctx: SectionRenderContext,
196
- ): string {
246
+ function interpolateVariables(body: string, ctx: SectionRenderContext): string {
197
247
  // Collapse standalone tag lines so multiline section templates render
198
248
  // without phantom blank lines from the layout markers.
199
249
  const collapsed = body.replace(STANDALONE_TAG_LINE, "$1");
@@ -4,12 +4,13 @@ import {
4
4
  mkdirSync,
5
5
  readdirSync,
6
6
  readFileSync,
7
+ writeFileSync,
7
8
  } from "node:fs";
8
9
  import { join } from "node:path";
9
10
 
10
11
  import { getIsContainerized } from "../config/env-registry.js";
11
- import { getCachedManagedConnections } from "../credential-execution/managed-catalog.js";
12
- import { listConnections } from "../oauth/oauth-store.js";
12
+ import type { ChannelCapabilities } from "../daemon/conversation-runtime-assembly.js";
13
+ import type { TrustContext } from "../daemon/trust-context.js";
13
14
  import type { OnboardingContext } from "../types/onboarding-context.js";
14
15
  import { resolveBundledDir } from "../util/bundled-asset.js";
15
16
  import { getLogger } from "../util/logger.js";
@@ -20,25 +21,28 @@ import {
20
21
  } from "../util/platform.js";
21
22
  import { stripCommentLines } from "../util/strip-comment-lines.js";
22
23
  import { cleanupBootstrapFiles } from "./bootstrap-cleanup.js";
23
- import { SYSTEM_PROMPT_CACHE_BOUNDARY } from "./cache-boundary.js";
24
- import { normalizeOnboardingContext } from "./normalize-onboarding.js";
24
+ import {
25
+ resolveGuardianPersona,
26
+ resolveUserSlug,
27
+ } from "./persona-resolver.js";
25
28
  import { renderWorkspaceSections } from "./sections.js";
29
+ import { isTemplateContent } from "./template-detection.js";
26
30
 
27
- export { SYSTEM_PROMPT_CACHE_BOUNDARY };
31
+ export { isTemplateContent };
28
32
 
29
- const BOOTSTRAP_VOICE_BLOCKS: Record<string, string> = {
30
- grounded:
31
- "## Voice\nCalm, direct, precise. No filler. Lead with the thing, explain if needed. Opinions stated plainly.",
32
- 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.",
33
- energetic:
34
- "## Voice\nFast and generative. Lean into momentum. Enthusiasm is in the pace, not the exclamations.",
35
- poetic:
36
- "## Voice\nThoughtful and unhurried. Notice things. Word choice matters. Don't rush to close — sometimes the observation is the value.",
33
+ /**
34
+ * Maps onboarding cohort identifiers to their cohort-specific bootstrap
35
+ * template filenames. When a cohort key is present in OnboardingContext,
36
+ * `maybeReseedBootstrapForCohort` swaps the generic BOOTSTRAP.md with the
37
+ * cohort-specific variant — but only if the workspace file is still pristine.
38
+ */
39
+ const COHORT_BOOTSTRAP_TEMPLATES: Record<string, string> = {
40
+ "content-automation": "BOOTSTRAP-CONTENT-AUTOMATION.md",
37
41
  };
38
42
 
39
43
  const log = getLogger("system-prompt");
40
44
 
41
- const PROMPT_FILES = ["SOUL.md", "IDENTITY.md"] as const;
45
+ const PROMPT_FILES = ["IDENTITY.md", "SOUL.md"] as const;
42
46
 
43
47
  function hasPopulatedUsersDir(): boolean {
44
48
  try {
@@ -210,224 +214,118 @@ export function ensurePromptFiles(): void {
210
214
  }
211
215
 
212
216
  /**
213
- * Build the system prompt from ~/.vellum prompt files.
214
- *
215
- * Composition:
216
- * 1. Base prompt: IDENTITY.md + SOUL.md (guaranteed to exist after ensurePromptFiles)
217
- * 2. Append the resolved user persona from users/<slug>.md (via options.userPersona)
218
- * 3. If BOOTSTRAP.md exists, append first-run ritual instructions
217
+ * One-shot swap: if the workspace BOOTSTRAP.md is still the unmodified generic
218
+ * template AND a cohort-specific template exists, overwrite the workspace file
219
+ * with the cohort variant. No-op when BOOTSTRAP.md has been deleted, modified,
220
+ * or the cohort has no mapped template.
219
221
  */
222
+ export function maybeReseedBootstrapForCohort(cohort: string): void {
223
+ const templateFileName = COHORT_BOOTSTRAP_TEMPLATES[cohort];
224
+ if (!templateFileName) return;
225
+
226
+ const bootstrapPath = getWorkspacePromptPath("BOOTSTRAP.md");
227
+ if (!existsSync(bootstrapPath)) return;
228
+
229
+ const currentContent = readPromptFile(bootstrapPath);
230
+ // Compare against the GENERIC "BOOTSTRAP.md" template, not the cohort-
231
+ // specific one. After the swap, the workspace content no longer matches
232
+ // the generic template, so this guard returns false on subsequent calls —
233
+ // making the swap idempotent. Do NOT change the comparison target to the
234
+ // cohort template filename; that would re-swap on every prompt build.
235
+ if (!isTemplateContent(currentContent, "BOOTSTRAP.md")) return;
236
+
237
+ const templatesDir = resolveBundledDir(
238
+ import.meta.dirname ?? __dirname,
239
+ "templates",
240
+ "templates",
241
+ );
242
+ const cohortTemplatePath = join(templatesDir, templateFileName);
243
+ if (!existsSync(cohortTemplatePath)) {
244
+ log.warn(
245
+ { cohort, templateFileName },
246
+ "Cohort bootstrap template not found, keeping generic BOOTSTRAP.md",
247
+ );
248
+ return;
249
+ }
250
+
251
+ try {
252
+ const cohortContent = readFileSync(cohortTemplatePath, "utf-8");
253
+ writeFileSync(bootstrapPath, cohortContent, "utf-8");
254
+ log.info(
255
+ { cohort, templateFileName },
256
+ "Replaced generic BOOTSTRAP.md with cohort-specific template",
257
+ );
258
+ } catch (err) {
259
+ log.warn(
260
+ { err, cohort, templateFileName },
261
+ "Failed to reseed BOOTSTRAP.md for cohort",
262
+ );
263
+ }
264
+ }
265
+
220
266
  export interface BuildSystemPromptOptions {
221
267
  hasNoClient?: boolean;
222
268
  excludeBootstrap?: boolean;
223
269
  excludeCustomPrefix?: boolean;
224
- userPersona?: string | null;
225
- channelPersona?: string | null;
226
- userSlug?: string | null;
270
+ trustContext?: TrustContext;
271
+ channelCapabilities?: ChannelCapabilities;
227
272
  onboardingContext?: OnboardingContext;
228
273
  }
229
274
 
230
275
  /**
231
- * Sentinel that separates the static instruction prefix (stable across turns)
232
- * from the dynamic workspace suffix (changes when workspace files are edited).
233
- *
234
- * The Anthropic provider splits on this marker to create two system-prompt
235
- * cache blocks so that static instructions stay cached even when workspace
236
- * files change between turns.
276
+ * Build the system prompt by rendering `BUNDLED_SYSTEM_SECTIONS` (with
277
+ * workspace overrides per section). Per-section behaviour lives in
278
+ * `system-sections.ts`; the renderer in `sections.ts` handles
279
+ * frontmatter `enabled:` predicates, `{{variable}}` interpolation,
280
+ * file-backed bodies, and runtime-computed transforms.
237
281
  */
238
282
  export function buildSystemPrompt(options?: BuildSystemPromptOptions): string {
283
+ // One-shot cohort swap: if the user has a cohort and BOOTSTRAP.md is still
284
+ // the generic template, replace it with the cohort-specific variant before
285
+ // the prompt reads the file.
286
+ if (options?.onboardingContext?.cohort) {
287
+ maybeReseedBootstrapForCohort(options.onboardingContext.cohort);
288
+ }
289
+
290
+ // Slugs used by the persona sections (`10-user-persona`,
291
+ // `11-channel-persona`) and the BOOTSTRAP block. `userSlug` is the
292
+ // raw slug derived from the caller's trust context (falling back to
293
+ // the guardian's contact, then to "default" when nothing resolves);
294
+ // `users/<slug>.md → users/default.md` fallback lives in the
295
+ // section's `workspacePath` array. `channelSlug` is the channel
296
+ // identifier from `channelCapabilities`, defaulting to "vellum".
297
+ const userSlug = resolveUserSlug(options?.trustContext) ?? "default";
298
+ const channelSlug = options?.channelCapabilities?.channel ?? "vellum";
299
+
239
300
  // Section render context. Workspace section frontmatter `enabled:`
240
- // predicates and `{{key}}` / `{{#flag}}...{{/flag}}` body interpolation
241
- // both resolve against this map, so anything the renderer needs to see
242
- // (runtime gates, paths) must be lifted onto `ctx` rather than branched
243
- // on at the call site. Mustache section tags `{{#flag}}` / `{{^flag}}`
244
- // coerce `ctx[flag]` to boolean via `Boolean(...)`, so options that are
245
- // undefined (caller didn't pass them) behave identically to false — no
246
- // explicit normalization needed; `...options` is enough.
301
+ // predicates, `{{key}}` / `{{#flag}}...{{/flag}}` body interpolation,
302
+ // and `{{key}}` paths inside `workspacePath` all resolve against this
303
+ // map, so anything the renderer needs to see (runtime gates, slugs,
304
+ // paths) must be lifted onto `ctx` rather than branched on at the
305
+ // call site. Mustache section tags `{{#flag}}` / `{{^flag}}` coerce
306
+ // `ctx[flag]` to boolean via `Boolean(...)`, so options that are
307
+ // undefined (caller didn't pass them) behave identically to false —
308
+ // no explicit normalization needed; `...options` is enough.
247
309
  const ctx = {
248
310
  ...options,
249
311
  isContainerized: getIsContainerized(),
250
312
  workspaceDir: getWorkspaceDir(),
313
+ userSlug,
314
+ channelSlug,
251
315
  };
252
316
 
253
- // Single array. Everything pushed before `dynamicStart` lands in the
254
- // static (cached) prefix; everything after lands in the dynamic suffix.
255
- // The two halves are joined around `SYSTEM_PROMPT_CACHE_BOUNDARY` so the
256
- // Anthropic provider can key its prompt cache on the prefix.
257
- const systemParts: string[] = [...renderWorkspaceSections(ctx)];
258
- const dynamicStart = systemParts.length;
259
-
260
- // SOUL.md is rendered by the `09-soul` workspace-backed section
261
- // (see templates/system-sections.ts) — no inline read needed here.
262
- const identityPath = getWorkspacePromptPath("IDENTITY.md");
263
- const bootstrapPath = getWorkspacePromptPath("BOOTSTRAP.md");
264
-
265
- const identity = readPromptFile(identityPath);
266
- const bootstrap = readPromptFile(bootstrapPath);
267
-
268
- const includeBootstrap = !!bootstrap && !options?.excludeBootstrap;
269
-
270
- // Template prompt files contain placeholder fields and meta-instructions
271
- // meant for the assistant to fill in during onboarding. When included
272
- // verbatim in the system prompt, the model can leak internal details and
273
- // narrate its own setup process instead of following the BOOTSTRAP.md
274
- // ritual. Detect unmodified templates by comparing against the bundled
275
- // source and skip them — SOUL.md provides sufficient personality defaults
276
- // until onboarding completes.
277
- const identityIsTemplate = isTemplateContent(identity, "IDENTITY.md");
278
-
279
- if (identity && (!identityIsTemplate || includeBootstrap)) {
280
- if (identityIsTemplate) {
281
- // During bootstrap the model needs to see the template structure
282
- // so it can produce a valid file_write with the right fields.
283
- systemParts.push(identity);
284
- } else {
285
- // Strip placeholder lines (e.g. "- **Name:** _(not yet chosen)_") so
286
- // the model doesn't treat unresolved fields as prompts to ask the user.
287
- const cleanedIdentity = identity
288
- .split("\n")
289
- .filter((line) => !/_\(not yet (?:chosen|established)\)_/.test(line))
290
- .join("\n");
291
- if (cleanedIdentity.trim()) {
292
- systemParts.push(cleanedIdentity);
293
- }
294
- }
295
- }
296
- if (options?.userPersona) systemParts.push(options.userPersona);
297
- if (options?.channelPersona) systemParts.push(options.channelPersona);
298
- if (includeBootstrap) {
299
- const userSlug = options?.userSlug ?? "default";
300
- const bootstrapWithSlug = bootstrap.replaceAll(
301
- "{{USER_PERSONA_FILE}}",
302
- `${userSlug}.md`,
303
- );
304
- let bootstrapContent = bootstrapWithSlug;
305
- const voiceBlock = options?.onboardingContext?.tone
306
- ? BOOTSTRAP_VOICE_BLOCKS[options.onboardingContext.tone]
307
- : undefined;
308
- if (voiceBlock) {
309
- bootstrapContent = voiceBlock + "\n\n" + bootstrapContent;
310
- }
311
- systemParts.push(
312
- "# First-Run Ritual\n\n" +
313
- "BOOTSTRAP.md is present — this is your first conversation. Follow its instructions.\n\n" +
314
- bootstrapContent,
315
- );
316
-
317
- if (options?.onboardingContext) {
318
- const n = normalizeOnboardingContext(options.onboardingContext);
319
- const lines: string[] = [
320
- "## First-Run User Context",
321
- "",
322
- "The user completed setup before this conversation.",
323
- "",
324
- "Known context:",
325
- ];
326
- if (n.preferredName) lines.push(`- Name: ${n.preferredName}`);
327
- if (n.commonWork.length)
328
- lines.push(`- Common work: ${n.commonWork.join("; ")}`);
329
- if (n.dailyTools.length)
330
- lines.push(`- Daily tools: ${n.dailyTools.join(", ")}`);
331
- if (n.assistantName)
332
- lines.push(`- Chosen assistant name: ${n.assistantName}`);
333
- if (n.tone) lines.push(`- Preferred initial voice: ${n.tone}`);
334
- if (n.googleConnected && n.googleServices?.length) {
335
- lines.push(
336
- `- Google connected: yes (${n.googleServices.join(", ")} access granted)`,
337
- );
338
- }
339
- lines.push(
340
- "",
341
- "Apply this context quietly. Do not recap it as a list unless the user asks.",
342
- );
343
- systemParts.push(lines.join("\n"));
344
- }
345
- }
346
- // Configuration section removed — workspace files are self-describing,
347
- // tool routing lives in tool descriptions.
348
- // External Communications Identity removed — guidance lives in messaging
349
- // and phone-calls skill SKILL.md files.
350
- const integrationSection = buildIntegrationSection();
351
- if (integrationSection) systemParts.push(integrationSection);
352
-
353
- // Journal entries are extracted into graph nodes by the memory pipeline.
354
- // Journal files remain writable on disk.
355
-
356
- return (
357
- systemParts.slice(0, dynamicStart).join("\n\n") +
358
- SYSTEM_PROMPT_CACHE_BOUNDARY +
359
- systemParts.slice(dynamicStart).join("\n\n")
360
- );
361
- }
362
-
363
- function buildIntegrationSection(): string {
364
- const entries: { provider: string; accountInfo?: string | null }[] = [];
365
-
366
- // Local (BYO) connections from the SQLite store.
367
- try {
368
- const local = listConnections().filter((c) => c.status === "active");
369
- entries.push(...local);
370
- } catch {
371
- // DB not available — skip local connections
372
- }
373
-
374
- // Platform-managed connections from the in-memory cache (populated at
375
- // daemon startup and refreshed periodically).
376
- const managed = getCachedManagedConnections();
377
- for (const mc of managed) {
378
- // Provider-level dedup is intentional: this section is a summary of
379
- // connected services for the system prompt, not an exhaustive account
380
- // list. Multiple accounts for the same provider (e.g. two Google
381
- // accounts) collapse into a single line to keep the prompt compact.
382
- if (!entries.some((e) => e.provider === mc.provider)) {
383
- entries.push(mc);
384
- }
385
- }
386
-
387
- if (entries.length === 0) return "";
388
-
389
- const lines = ["# Connected Services", ""];
390
- for (const conn of entries) {
391
- const state = conn.accountInfo
392
- ? `Connected (${conn.accountInfo})`
393
- : "Connected";
394
- lines.push(`- **${conn.provider}**: ${state}`);
395
- }
396
-
397
- return lines.join("\n");
317
+ // Every system-prompt block flows through the bundled section
318
+ // pipeline including runtime-computed entries like
319
+ // `14-connected-services` whose body is derived from live OAuth
320
+ // caches. The whole prompt is treated as a single cached block by
321
+ // the Anthropic provider; per-provider details live in each
322
+ // provider's client.
323
+ return renderWorkspaceSections(ctx).join("\n\n");
398
324
  }
399
325
 
400
326
  // Re-export from shared util so existing importers don't break.
401
327
  export { stripCommentLines } from "../util/strip-comment-lines.js";
402
328
 
403
- /**
404
- * Returns true when the prompt file content is still the unmodified template
405
- * shipped with the daemon. Compares the stripped workspace content against
406
- * the stripped bundled template source so the check stays accurate even if
407
- * templates are edited in future releases.
408
- */
409
- export function isTemplateContent(
410
- content: string | null,
411
- templateFileName: string,
412
- ): boolean {
413
- if (content == null) return false;
414
- const templatesDir = resolveBundledDir(
415
- import.meta.dirname ?? __dirname,
416
- "templates",
417
- "templates",
418
- );
419
- const templatePath = join(templatesDir, templateFileName);
420
- if (!existsSync(templatePath)) return false;
421
- try {
422
- const templateContent = stripCommentLines(
423
- readFileSync(templatePath, "utf-8"),
424
- );
425
- return content === templateContent;
426
- } catch {
427
- return false;
428
- }
429
- }
430
-
431
329
  export function readPromptFile(path: string): string | null {
432
330
  if (!existsSync(path)) return null;
433
331
 
@@ -444,14 +342,17 @@ export function readPromptFile(path: string): string | null {
444
342
 
445
343
  /**
446
344
  * Reads the core identity/personality prompt files (SOUL.md, IDENTITY.md)
447
- * and concatenates whichever exist. Returns null if none are present.
345
+ * and concatenates whichever exist, plus the guardian's user persona when
346
+ * one is resolvable. Returns null if none are present.
448
347
  *
449
- * This is useful for injecting identity context into subsystems (e.g. memory
450
- * extraction) that run outside the main system prompt pipeline.
348
+ * Used by subsystems (memory extraction, conversation starters,
349
+ * notification decisions) that run outside the per-turn pipeline and want
350
+ * the assistant's "view of themselves and their guardian" without a trust
351
+ * context. The guardian persona fold is what callers used to do manually
352
+ * by passing `userPersona: resolveGuardianPersona()` — folding it in here
353
+ * removes the duplicated dance at every call site.
451
354
  */
452
- export function buildCoreIdentityContext(opts?: {
453
- userPersona?: string | null;
454
- }): string | null {
355
+ export function buildCoreIdentityContext(): string | null {
455
356
  const parts: string[] = [];
456
357
  for (const file of PROMPT_FILES) {
457
358
  const content = readPromptFile(getWorkspacePromptPath(file));
@@ -462,6 +363,7 @@ export function buildCoreIdentityContext(opts?: {
462
363
  if (file !== "SOUL.md" && isTemplateContent(content, file)) continue;
463
364
  parts.push(content);
464
365
  }
465
- if (opts?.userPersona) parts.push(opts.userPersona);
366
+ const guardianPersona = resolveGuardianPersona();
367
+ if (guardianPersona) parts.push(guardianPersona);
466
368
  return parts.length > 0 ? parts.join("\n\n") : null;
467
369
  }
@@ -0,0 +1,37 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ import { resolveBundledDir } from "../util/bundled-asset.js";
5
+ import { stripCommentLines } from "../util/strip-comment-lines.js";
6
+
7
+ /**
8
+ * Returns true when the prompt file content is still the unmodified template
9
+ * shipped with the daemon. Compares the stripped workspace content against
10
+ * the stripped bundled template source so the check stays accurate even if
11
+ * templates are edited in future releases.
12
+ *
13
+ * Kept in a leaf module so the bundled section registry can depend on it
14
+ * without forming a cycle through `system-prompt.ts → sections.ts →
15
+ * templates/system-sections.ts`.
16
+ */
17
+ export function isTemplateContent(
18
+ content: string | null,
19
+ templateFileName: string,
20
+ ): boolean {
21
+ if (content == null) return false;
22
+ const templatesDir = resolveBundledDir(
23
+ import.meta.dirname ?? __dirname,
24
+ "templates",
25
+ "templates",
26
+ );
27
+ const templatePath = join(templatesDir, templateFileName);
28
+ if (!existsSync(templatePath)) return false;
29
+ try {
30
+ const templateContent = stripCommentLines(
31
+ readFileSync(templatePath, "utf-8"),
32
+ );
33
+ return content === templateContent;
34
+ } catch {
35
+ return false;
36
+ }
37
+ }