@vellumai/assistant 0.8.10 → 0.8.11-staging.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (400) hide show
  1. package/bun.lock +62 -1
  2. package/docs/workspace-tools.md +196 -0
  3. package/examples/plugins/echo/README.md +3 -3
  4. package/knip.json +1 -0
  5. package/openapi.yaml +460 -128
  6. package/package.json +2 -1
  7. package/scripts/build-plugin-api.ts +299 -0
  8. package/src/__tests__/agent-loop-callsite-precedence.test.ts +7 -0
  9. package/src/__tests__/agent-loop-compaction-events.test.ts +197 -0
  10. package/src/__tests__/agent-loop-exit-reason.test.ts +93 -96
  11. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +2 -0
  12. package/src/__tests__/agent-loop-output-hooks.test.ts +274 -1
  13. package/src/__tests__/agent-loop-override-profile.test.ts +3 -0
  14. package/src/__tests__/agent-loop-provider-error-recording.test.ts +4 -0
  15. package/src/__tests__/agent-loop-thinking.test.ts +4 -0
  16. package/src/__tests__/agent-loop.test.ts +578 -5
  17. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +0 -1
  18. package/src/__tests__/approval-cascade.test.ts +1 -0
  19. package/src/__tests__/background-workers-disk-pressure.test.ts +0 -2
  20. package/src/__tests__/btw-routes.test.ts +0 -1
  21. package/src/__tests__/build-persisted-content.test.ts +75 -1
  22. package/src/__tests__/catalog-install-normalize.test.ts +141 -0
  23. package/src/__tests__/ces-startup-timeout.test.ts +60 -0
  24. package/src/__tests__/compaction-events.test.ts +1 -0
  25. package/src/__tests__/config-managed-gemini-defaults.test.ts +2 -46
  26. package/src/__tests__/context-overflow-reducer.test.ts +264 -124
  27. package/src/__tests__/context-window-manager-overflow-rung.test.ts +351 -0
  28. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
  29. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +13 -5
  30. package/src/__tests__/conversation-agent-loop-overflow.test.ts +284 -455
  31. package/src/__tests__/conversation-agent-loop.test.ts +131 -551
  32. package/src/__tests__/conversation-app-control-instantiation.test.ts +13 -0
  33. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
  34. package/src/__tests__/conversation-fork-crud.test.ts +259 -0
  35. package/src/__tests__/conversation-history-web-search.test.ts +1 -1
  36. package/src/__tests__/conversation-lifecycle.test.ts +257 -1
  37. package/src/__tests__/conversation-process-callsite.test.ts +1 -0
  38. package/src/__tests__/conversation-provider-retry-repair.test.ts +38 -377
  39. package/src/__tests__/conversation-queue.test.ts +1 -39
  40. package/src/__tests__/conversation-runtime-assembly.test.ts +119 -8
  41. package/src/__tests__/conversation-skill-tools.test.ts +491 -5
  42. package/src/__tests__/conversation-slash-queue.test.ts +1 -1
  43. package/src/__tests__/conversation-slash-unknown.test.ts +1 -0
  44. package/src/__tests__/conversation-speed-override.test.ts +1 -0
  45. package/src/__tests__/conversation-store.test.ts +74 -0
  46. package/src/__tests__/conversation-surfaces-app-control.test.ts +4 -1
  47. package/src/__tests__/conversation-tool-setup-attribution.test.ts +323 -0
  48. package/src/__tests__/conversation-tool-setup-tools-disabled.test.ts +34 -0
  49. package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
  50. package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
  51. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -0
  52. package/src/__tests__/corrected-target.test.ts +93 -0
  53. package/src/__tests__/credential-execution-feature-gates.test.ts +3 -5
  54. package/src/__tests__/credential-execution-tools.test.ts +23 -11
  55. package/src/__tests__/credential-security-invariants.test.ts +6 -1
  56. package/src/__tests__/db-schedule-syntax-migration.test.ts +80 -0
  57. package/src/__tests__/device-id.test.ts +70 -1
  58. package/src/__tests__/embedding-managed-proxy-selection.test.ts +6 -40
  59. package/src/__tests__/empty-response-hook.test.ts +242 -66
  60. package/src/__tests__/external-plugin-loader.test.ts +0 -31
  61. package/src/__tests__/get-skill-detail-audit.test.ts +43 -1
  62. package/src/__tests__/guardian-routing-invariants.test.ts +91 -0
  63. package/src/__tests__/history-repair-hook.test.ts +228 -3
  64. package/src/__tests__/host-app-control-proxy.test.ts +45 -0
  65. package/src/__tests__/host-browser-proxy.test.ts +254 -9
  66. package/src/__tests__/identity-routes.test.ts +1 -0
  67. package/src/__tests__/image-recovery-hook.test.ts +387 -0
  68. package/src/__tests__/injector-chain.test.ts +5 -4
  69. package/src/__tests__/injector-v3-suppression.test.ts +373 -47
  70. package/src/__tests__/intent-routing.test.ts +7 -0
  71. package/src/__tests__/memory-retrieval-hook.test.ts +117 -15
  72. package/src/__tests__/notification-decision-strategy.test.ts +3 -3
  73. package/src/__tests__/oauth-store.test.ts +0 -85
  74. package/src/__tests__/{context-overflow-policy.test.ts → overflow-policy.test.ts} +1 -1
  75. package/src/__tests__/parallel-tool.benchmark.test.ts +4 -0
  76. package/src/__tests__/persist-unsendable-image-downscale.test.ts +29 -9
  77. package/src/__tests__/persist-unsendable-image.test.ts +4 -4
  78. package/src/__tests__/persistence-secret-redaction.test.ts +78 -0
  79. package/src/__tests__/plugin-bootstrap.test.ts +82 -73
  80. package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
  81. package/src/__tests__/plugin-types.test.ts +0 -8
  82. package/src/__tests__/provider-catalog-visibility.test.ts +1 -9
  83. package/src/__tests__/prune-old-conversations-job.test.ts +99 -0
  84. package/src/__tests__/registry.test.ts +240 -1
  85. package/src/__tests__/require-fresh-approval.test.ts +3 -0
  86. package/src/__tests__/schedule-routes.test.ts +116 -1
  87. package/src/__tests__/schedule-store.test.ts +28 -0
  88. package/src/__tests__/schedule-tools.test.ts +94 -1
  89. package/src/__tests__/server-history-render.test.ts +39 -0
  90. package/src/__tests__/skill-projection-feature-flag.test.ts +13 -0
  91. package/src/__tests__/skill-projection.benchmark.test.ts +25 -7
  92. package/src/__tests__/skills.test.ts +202 -0
  93. package/src/__tests__/slim-skill-category.test.ts +195 -0
  94. package/src/__tests__/strip-memory-injections.test.ts +33 -39
  95. package/src/__tests__/test-support/tool-invocation-seed.ts +79 -0
  96. package/src/__tests__/title-generate-hook.test.ts +9 -7
  97. package/src/__tests__/tool-audit-listener.test.ts +264 -1
  98. package/src/__tests__/tool-error-hook.test.ts +4 -3
  99. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -0
  100. package/src/__tests__/tool-executor-lifecycle-events.test.ts +273 -0
  101. package/src/__tests__/tool-result-truncate-hook.test.ts +1 -0
  102. package/src/__tests__/tool-start-timestamp.test.ts +218 -0
  103. package/src/__tests__/tools-get-route.test.ts +202 -0
  104. package/src/__tests__/workspace-tool-loader.test.ts +319 -0
  105. package/src/__tests__/workspace-tools-watcher-flag.test.ts +70 -0
  106. package/src/agent/loop.ts +569 -319
  107. package/src/api/events/tool-result.ts +9 -0
  108. package/src/api/events/tool-use-start.ts +7 -0
  109. package/src/api/index.ts +10 -0
  110. package/src/api/responses/conversation-message.ts +135 -27
  111. package/src/api/responses/memory-v3-selection-log.ts +4 -4
  112. package/src/approvals/guardian-request-resolvers.ts +26 -0
  113. package/src/browser-session/backends/host-bridge.ts +29 -0
  114. package/src/browser-session/index.ts +1 -0
  115. package/src/browser-session/types.ts +5 -1
  116. package/src/cli/commands/__tests__/schedules.test.ts +62 -4
  117. package/src/cli/commands/__tests__/skills.test.ts +53 -0
  118. package/src/cli/commands/channel-verification-sessions.ts +6 -6
  119. package/src/cli/commands/inference-providers.ts +0 -8
  120. package/src/cli/commands/plugins.ts +2 -2
  121. package/src/cli/commands/schedules.ts +27 -4
  122. package/src/cli/commands/skills.ts +187 -146
  123. package/src/cli/commands/tools.ts +106 -0
  124. package/src/cli/lib/__tests__/install-from-github.test.ts +256 -328
  125. package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +6 -2
  126. package/src/cli/lib/__tests__/plugin-details.test.ts +10 -16
  127. package/src/cli/lib/__tests__/plugin-marketplace.test.ts +2 -2
  128. package/src/cli/lib/__tests__/search-plugins.test.ts +145 -240
  129. package/src/cli/lib/install-from-github.ts +187 -117
  130. package/src/cli/lib/plugin-catalog-cache.ts +9 -9
  131. package/src/cli/lib/plugin-details.ts +38 -68
  132. package/src/cli/lib/plugin-marketplace.ts +42 -14
  133. package/src/cli/lib/search-plugins.ts +29 -129
  134. package/src/cli/program.ts +2 -0
  135. package/src/config/bundled-skills/acp/SKILL.md +1 -0
  136. package/src/config/bundled-skills/app-builder/SKILL.md +1 -0
  137. package/src/config/bundled-skills/app-control/SKILL.md +1 -0
  138. package/src/config/bundled-skills/computer-use/SKILL.md +1 -0
  139. package/src/config/bundled-skills/contacts/SKILL.md +1 -0
  140. package/src/config/bundled-skills/document-editor/SKILL.md +1 -0
  141. package/src/config/bundled-skills/followups/SKILL.md +1 -0
  142. package/src/config/bundled-skills/image-studio/SKILL.md +1 -0
  143. package/src/config/bundled-skills/media-processing/SKILL.md +1 -0
  144. package/src/config/bundled-skills/messaging/SKILL.md +1 -0
  145. package/src/config/bundled-skills/phone-calls/SKILL.md +1 -0
  146. package/src/config/bundled-skills/playbooks/SKILL.md +1 -0
  147. package/src/config/bundled-skills/schedule/SKILL.md +1 -0
  148. package/src/config/bundled-skills/schedule/TOOLS.json +11 -3
  149. package/src/config/bundled-skills/sequences/SKILL.md +1 -0
  150. package/src/config/bundled-skills/settings/SKILL.md +1 -0
  151. package/src/config/bundled-skills/skill-management/SKILL.md +101 -1
  152. package/src/config/bundled-skills/subagent/SKILL.md +1 -0
  153. package/src/config/bundled-skills/transcribe/SKILL.md +1 -0
  154. package/src/config/env-registry.ts +23 -0
  155. package/src/config/feature-flag-registry.json +17 -81
  156. package/src/config/loader.ts +5 -22
  157. package/src/config/schema.ts +2 -0
  158. package/src/config/schemas/__tests__/compaction-logs.test.ts +56 -0
  159. package/src/config/schemas/__tests__/memory-v2.test.ts +0 -1
  160. package/src/config/schemas/__tests__/memory-v3.test.ts +61 -1
  161. package/src/config/schemas/compaction-logs.ts +79 -0
  162. package/src/config/schemas/memory-v2.ts +0 -8
  163. package/src/config/schemas/memory-v3.ts +104 -33
  164. package/src/config/seed-inference-profiles.ts +1 -1
  165. package/src/config/skills.ts +117 -47
  166. package/src/context/compactor.ts +11 -0
  167. package/src/context/strip-injections.ts +38 -4
  168. package/src/credential-execution/feature-gates.ts +0 -21
  169. package/src/credential-execution/startup-timeout.ts +32 -4
  170. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +18 -0
  171. package/src/daemon/conversation-agent-loop-handlers.ts +140 -91
  172. package/src/daemon/conversation-agent-loop.ts +123 -658
  173. package/src/daemon/conversation-error.ts +6 -33
  174. package/src/daemon/conversation-lifecycle.ts +1 -1
  175. package/src/daemon/conversation-runtime-assembly.ts +183 -22
  176. package/src/daemon/conversation-skill-tools.ts +137 -8
  177. package/src/daemon/conversation-slash.ts +0 -14
  178. package/src/daemon/conversation-store.ts +2 -19
  179. package/src/daemon/conversation-tool-setup.ts +87 -1
  180. package/src/daemon/conversation.ts +119 -50
  181. package/src/daemon/external-plugins-bootstrap.ts +36 -95
  182. package/src/daemon/handlers/config-channels.ts +11 -2
  183. package/src/daemon/handlers/shared.ts +9 -1
  184. package/src/daemon/handlers/skills.ts +10 -4
  185. package/src/daemon/host-app-control-proxy.ts +72 -57
  186. package/src/daemon/host-browser-proxy.ts +117 -22
  187. package/src/daemon/lifecycle.ts +34 -5
  188. package/src/daemon/message-protocol.ts +0 -7
  189. package/src/daemon/message-types/schedules.ts +1 -0
  190. package/src/daemon/message-types/skills.ts +17 -0
  191. package/src/daemon/providers-setup.ts +3 -0
  192. package/src/daemon/server.ts +3 -3
  193. package/src/daemon/tool-setup-types.ts +9 -3
  194. package/src/daemon/trust-context.ts +23 -0
  195. package/src/daemon/workspace-tools-watcher.ts +324 -0
  196. package/src/events/tool-audit-listener.ts +78 -16
  197. package/src/events/tool-metrics-listener.ts +2 -5
  198. package/src/memory/__tests__/compaction-log-writer-clickhouse.test.ts +227 -0
  199. package/src/memory/__tests__/conversation-queries.test.ts +176 -0
  200. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +20 -32
  201. package/src/memory/compaction-log-writer-clickhouse.ts +418 -0
  202. package/src/memory/conversation-crud.ts +134 -3
  203. package/src/memory/conversation-queries.ts +64 -7
  204. package/src/memory/db-init.ts +14 -0
  205. package/src/memory/embedding-backend.test.ts +130 -1
  206. package/src/memory/embedding-backend.ts +79 -106
  207. package/src/memory/embedding-gemini.ts +5 -0
  208. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +12 -0
  209. package/src/memory/graph/__tests__/handle-remember-v2.test.ts +19 -0
  210. package/src/memory/graph/conversation-graph-memory.ts +36 -25
  211. package/src/memory/graph/tool-handlers.ts +3 -0
  212. package/src/memory/job-handlers/cleanup.ts +3 -1
  213. package/src/memory/jobs-store.ts +0 -28
  214. package/src/memory/jobs-worker.ts +10 -22
  215. package/src/memory/memory-marker.ts +29 -0
  216. package/src/memory/memory-retrospective-startup-cleanup.ts +1 -1
  217. package/src/memory/migrations/268-add-memory-v3-selections.ts +6 -0
  218. package/src/memory/migrations/270-schedule-description.ts +36 -0
  219. package/src/memory/migrations/275-tool-invocations-add-skill-id.test.ts +81 -0
  220. package/src/memory/migrations/275-tool-invocations-add-skill-id.ts +20 -0
  221. package/src/memory/migrations/276-tool-invocations-created-at-id-index.test.ts +68 -0
  222. package/src/memory/migrations/276-tool-invocations-created-at-id-index.ts +20 -0
  223. package/src/memory/migrations/277-add-memory-v3-ever-injected.ts +29 -0
  224. package/src/memory/migrations/278-tool-invocations-telemetry-columns.test.ts +96 -0
  225. package/src/memory/migrations/278-tool-invocations-telemetry-columns.ts +39 -0
  226. package/src/memory/migrations/279-create-skill-loaded-events.test.ts +84 -0
  227. package/src/memory/migrations/279-create-skill-loaded-events.ts +26 -0
  228. package/src/memory/migrations/280-conversations-surfaced-at.test.ts +88 -0
  229. package/src/memory/migrations/280-conversations-surfaced-at.ts +24 -0
  230. package/src/memory/migrations/index.ts +10 -0
  231. package/src/memory/migrations/registry.ts +8 -0
  232. package/src/memory/schema/conversations.ts +16 -0
  233. package/src/memory/schema/infrastructure.ts +26 -0
  234. package/src/memory/skill-loaded-events-store.test.ts +160 -0
  235. package/src/memory/skill-loaded-events-store.ts +95 -0
  236. package/src/memory/tool-executed-events-store.test.ts +219 -0
  237. package/src/memory/tool-executed-events-store.ts +102 -0
  238. package/src/memory/tool-usage-store.ts +15 -3
  239. package/src/memory/v2/__tests__/consolidation-job.test.ts +117 -12
  240. package/src/memory/v2/__tests__/consolidation-prompt-flag-gating-guard.test.ts +189 -0
  241. package/src/memory/v2/__tests__/injected-block-slugs.test.ts +90 -0
  242. package/src/memory/v2/__tests__/page-store.test.ts +33 -0
  243. package/src/memory/v2/__tests__/prompts-consolidation.test.ts +88 -15
  244. package/src/memory/v2/activation-store.ts +50 -1
  245. package/src/memory/v2/consolidation-job.ts +113 -29
  246. package/src/memory/v2/injected-block-slugs.ts +79 -0
  247. package/src/memory/v2/injection.ts +6 -1
  248. package/src/memory/v2/prompts/consolidation.ts +414 -13
  249. package/src/memory/v2/router.ts +2 -28
  250. package/src/memory/v2/static-context.ts +1 -1
  251. package/src/memory/v2/types.ts +16 -0
  252. package/src/notifications/__tests__/copy-composer.test.ts +244 -0
  253. package/src/notifications/access-request-copy.ts +298 -0
  254. package/src/notifications/adapters/slack.ts +3 -3
  255. package/src/notifications/adapters/telegram.ts +2 -1
  256. package/src/notifications/copy-composer.ts +49 -267
  257. package/src/notifications/decision-engine.ts +16 -35
  258. package/src/notifications/home-feed-side-effect.ts +1 -6
  259. package/src/oauth/oauth-store.ts +0 -9
  260. package/src/permissions/checker.test.ts +83 -1
  261. package/src/permissions/checker.ts +25 -2
  262. package/src/permissions/gateway-threshold-reader.test.ts +182 -0
  263. package/src/permissions/gateway-threshold-reader.ts +80 -0
  264. package/src/platform/client.ts +1 -3
  265. package/src/platform/feature-gate.ts +3 -12
  266. package/src/plugin-api/constants.ts +4 -2
  267. package/src/plugin-api/index.ts +63 -11
  268. package/src/plugin-api/types.ts +236 -71
  269. package/src/plugins/defaults/compaction/compact.ts +66 -2
  270. package/src/plugins/defaults/compaction/context-overflow-reducer.ts +240 -32
  271. package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
  272. package/src/{daemon/context-overflow-policy.ts → plugins/defaults/compaction/overflow-policy.ts} +1 -1
  273. package/src/plugins/defaults/compaction/window-manager.ts +303 -1
  274. package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +173 -0
  275. package/src/plugins/defaults/empty-response/hooks/stop.ts +11 -115
  276. package/src/plugins/defaults/empty-response/nudge-state-store.ts +46 -0
  277. package/src/plugins/defaults/history-repair/hooks/post-model-call.ts +50 -0
  278. package/src/plugins/defaults/history-repair/hooks/stop.ts +22 -0
  279. package/src/plugins/defaults/history-repair/repair-state-store.ts +51 -0
  280. package/src/plugins/defaults/history-repair/terminal.ts +39 -2
  281. package/src/plugins/defaults/image-recovery/detect.ts +25 -0
  282. package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +73 -0
  283. package/src/plugins/defaults/image-recovery/hooks/stop.ts +22 -0
  284. package/src/plugins/defaults/image-recovery/image-recovery-state-store.ts +48 -0
  285. package/src/plugins/defaults/image-recovery/package.json +14 -0
  286. package/src/{daemon/persist-unsendable-image.ts → plugins/defaults/image-recovery/recover.ts} +67 -14
  287. package/src/plugins/defaults/index.ts +71 -5
  288. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +76 -112
  289. package/src/plugins/defaults/memory-retrieval/hooks/{user-prompt-submit-temp.ts → user-prompt-submit.ts} +83 -74
  290. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +14 -8
  291. package/src/plugins/defaults/memory-retrieval/injectors.ts +2 -18
  292. package/src/plugins/defaults/memory-retrieval/package.json +14 -0
  293. package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1157 -0
  294. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +683 -0
  295. package/src/plugins/defaults/memory-v3-shadow/__tests__/live-integration.test.ts +161 -140
  296. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +160 -0
  297. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +335 -316
  298. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +145 -53
  299. package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +38 -1
  300. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +18 -8
  301. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +112 -71
  302. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +192 -51
  303. package/src/plugins/defaults/memory-v3-shadow/__tests__/types.test.ts +4 -16
  304. package/src/plugins/defaults/memory-v3-shadow/card.test.ts +173 -0
  305. package/src/plugins/defaults/memory-v3-shadow/card.ts +116 -0
  306. package/src/plugins/defaults/memory-v3-shadow/core-set.test.ts +104 -0
  307. package/src/plugins/defaults/memory-v3-shadow/core-set.ts +59 -0
  308. package/src/plugins/defaults/memory-v3-shadow/ever-injected-store.test.ts +305 -0
  309. package/src/plugins/defaults/memory-v3-shadow/ever-injected-store.ts +278 -0
  310. package/src/plugins/defaults/memory-v3-shadow/hot-set.test.ts +138 -0
  311. package/src/plugins/defaults/memory-v3-shadow/hot-set.ts +85 -0
  312. package/src/plugins/defaults/memory-v3-shadow/injector.ts +331 -24
  313. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +119 -13
  314. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +169 -114
  315. package/src/plugins/defaults/memory-v3-shadow/page-content.ts +47 -16
  316. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +144 -66
  317. package/src/plugins/defaults/memory-v3-shadow/prune.test.ts +758 -0
  318. package/src/plugins/defaults/memory-v3-shadow/prune.ts +471 -0
  319. package/src/plugins/defaults/memory-v3-shadow/render-injection.ts +68 -16
  320. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +19 -12
  321. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +96 -43
  322. package/src/plugins/defaults/memory-v3-shadow/types.ts +34 -17
  323. package/src/plugins/defaults/title-generate/hooks/stop.ts +9 -11
  324. package/src/plugins/pipeline.ts +8 -5
  325. package/src/plugins/types.ts +5 -51
  326. package/src/providers/cache-control.ts +26 -0
  327. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +1 -2
  328. package/src/providers/model-catalog.ts +13 -1
  329. package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +147 -0
  330. package/src/providers/openai/chat-completions-provider.ts +46 -0
  331. package/src/providers/openai/responses-provider.ts +45 -0
  332. package/src/providers/registry.ts +0 -8
  333. package/src/runtime/__tests__/agent-wake.test.ts +0 -1
  334. package/src/runtime/agent-wake.ts +13 -0
  335. package/src/runtime/routes/__tests__/acp-routes.test.ts +151 -0
  336. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +12 -50
  337. package/src/runtime/routes/__tests__/conversation-surface-routes.test.ts +322 -0
  338. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +0 -62
  339. package/src/runtime/routes/__tests__/plugins-routes.test.ts +31 -11
  340. package/src/runtime/routes/acp-routes.test.ts +106 -0
  341. package/src/runtime/routes/acp-routes.ts +248 -2
  342. package/src/runtime/routes/browser-tabs-routes.ts +1 -1
  343. package/src/runtime/routes/channel-verification-routes.ts +14 -5
  344. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +0 -14
  345. package/src/runtime/routes/consolidation-routes.ts +6 -82
  346. package/src/runtime/routes/conversation-list-routes.ts +6 -0
  347. package/src/runtime/routes/conversation-management-routes.ts +71 -0
  348. package/src/runtime/routes/identity-routes.ts +8 -0
  349. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
  350. package/src/runtime/routes/inference-provider-connection-routes.ts +0 -45
  351. package/src/runtime/routes/plugins-routes.ts +34 -45
  352. package/src/runtime/routes/schedule-routes.ts +43 -5
  353. package/src/runtime/routes/settings-routes.ts +140 -15
  354. package/src/runtime/routes/skills-routes.ts +18 -6
  355. package/src/runtime/services/__tests__/conversation-serializer.test.ts +140 -0
  356. package/src/runtime/services/conversation-serializer.ts +38 -1
  357. package/src/runtime/verification-outbound-actions.ts +147 -2
  358. package/src/runtime/verification-templates.ts +29 -3
  359. package/src/schedule/schedule-store.ts +19 -0
  360. package/src/skills/catalog-install.ts +77 -13
  361. package/src/tasks/task-scheduler.ts +1 -0
  362. package/src/telemetry/types.ts +66 -1
  363. package/src/telemetry/usage-telemetry-reporter.test.ts +542 -13
  364. package/src/telemetry/usage-telemetry-reporter.ts +213 -20
  365. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +49 -2
  366. package/src/tools/browser/__tests__/browser-status.test.ts +29 -5
  367. package/src/tools/browser/browser-execution.ts +27 -9
  368. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +380 -4
  369. package/src/tools/browser/cdp-client/__tests__/host-bridge-cdp-client.test.ts +107 -0
  370. package/src/tools/browser/cdp-client/__tests__/types.test.ts +6 -1
  371. package/src/tools/browser/cdp-client/factory.ts +217 -17
  372. package/src/tools/browser/cdp-client/host-bridge-cdp-client.ts +67 -0
  373. package/src/tools/browser/cdp-client/types.ts +22 -2
  374. package/src/tools/credential-execution/make-authenticated-request.ts +2 -1
  375. package/src/tools/credential-execution/manage-secure-command-tool.ts +169 -164
  376. package/src/tools/credential-execution/run-authenticated-command.ts +2 -1
  377. package/src/tools/executor.ts +39 -7
  378. package/src/tools/registry.ts +387 -5
  379. package/src/tools/schedule/create.ts +16 -0
  380. package/src/tools/schedule/list.ts +12 -4
  381. package/src/tools/schedule/update.ts +12 -0
  382. package/src/tools/skills/load.ts +11 -6
  383. package/src/tools/terminal/safe-env.ts +2 -0
  384. package/src/tools/types.ts +65 -9
  385. package/src/tools/workspace-tools/loader.ts +673 -0
  386. package/src/usage/attribution.ts +28 -0
  387. package/src/util/device-id.ts +17 -3
  388. package/src/util/platform.ts +16 -0
  389. package/tsconfig.plugin-api.json +13 -0
  390. package/src/__tests__/plugin-external-api.test.ts +0 -68
  391. package/src/__tests__/plugin-skill-contribution.test.ts +0 -355
  392. package/src/daemon/message-types/browser.ts +0 -10
  393. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
  394. package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/eval-turns.json +0 -36
  395. package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/live-turns.json +0 -37
  396. package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-eviction.test.ts +0 -106
  397. package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-skeleton.test.ts +0 -44
  398. package/src/plugins/defaults/memory-v3-shadow/working-set.ts +0 -91
  399. package/src/plugins/external-api.ts +0 -114
  400. package/src/plugins/plugin-skill-contributions.ts +0 -292
@@ -30,6 +30,37 @@ const log = getLogger("memory-v2-consolidate-prompt");
30
30
  /** Sentinel substituted with the cutoff timestamp at runtime. */
31
31
  export const CUTOFF_PLACEHOLDER = "{{CUTOFF}}";
32
32
 
33
+ /**
34
+ * Sentinel substituted with {@link CORE_PAGES_CONSOLIDATION_SECTION} (or the
35
+ * empty string) at runtime. The core-pages file only exists for the memory-v3
36
+ * retrieval lanes, so the section is included only when a v3 flag is enabled
37
+ * for the assistant — on a v2-only install the instruction would have the
38
+ * agent curate a file nothing reads, under premises ("kept in reach every
39
+ * turn", "the hot set") that are false there.
40
+ *
41
+ * New flag-gated sections must follow this placeholder pattern and be
42
+ * registered in consolidation-prompt-flag-gating-guard.test.ts, which blocks
43
+ * gated content from reaching the default (all-gates-off) rendering.
44
+ */
45
+ export const CORE_PAGES_PLACEHOLDER = "{{CORE_PAGES_SECTION}}";
46
+
47
+ /**
48
+ * The flag-gated `memory/core-pages.md` curation step. Ends with a blank line
49
+ * so substituting it (or the empty string) ahead of the `---` separator keeps
50
+ * the surrounding template byte-stable either way.
51
+ */
52
+ export const CORE_PAGES_CONSOLIDATION_SECTION = `## 10. Review \`memory/core-pages.md\` — the curated core set
53
+
54
+ \`memory/core-pages.md\` lists the pages retrieval keeps in reach EVERY turn, regardless of topic. It exists for one class of page: associative texture — registers, identity frames, calibration rules — pages with no lexical or semantic match to the message that neither search nor usage frequency can surface on their own. You are the only editor of this file; review it each pass.
55
+
56
+ - **Format:** one page per list line — \`- [[slug]]\` or \`- slug\`, optionally followed by an inline note: after a wikilink the note is free-form (\`- [[slug]] — why it belongs\`); after a bare slug introduce it with a dash (\`- slug — why it belongs\`). Headings, blank lines, and standalone prose lines are ignored, so annotate freely.
57
+ - **Keep it small** — on the order of a few dozen entries (~30–50). Every entry costs context budget every single turn.
58
+ - **Add** a page only when its content should always be in reach with no topical cue. If a search query would find it, it doesn't belong here.
59
+ - **Remove** entries made redundant by frequent use — recently-used pages stay warm automatically (the hot set), so a page that comes up all the time doesn't need a core slot.
60
+ - **Fix** entries whose page you renamed or deleted this pass (maintenance reports dangling entries, but never edits the file).
61
+
62
+ `;
63
+
33
64
  /**
34
65
  * Upper bound for the override file. Real consolidation prompts are kilobytes;
35
66
  * 1 MiB is generous headroom while preventing a `settings.write` principal from
@@ -384,7 +415,7 @@ Scan namespace sizes. If any namespace has crossed ~12-15 articles with visible
384
415
  - Rewrite to contain ONLY entries with timestamp ≥ \`${CUTOFF_PLACEHOLDER}\`.
385
416
  - Smart removal — never wholesale-clear.
386
417
 
387
- ---
418
+ ${CORE_PAGES_PLACEHOLDER}---
388
419
 
389
420
  # What NOT to do
390
421
 
@@ -434,13 +465,372 @@ For each article you touched:
434
465
  This is the engine that decides who you are tomorrow. Be ORGANIZED. Care, judgment, voice. Your voice. Your wiki.`;
435
466
 
436
467
  /**
437
- * Resolve `CONSOLIDATION_PROMPT` with `{{CUTOFF}}` substituted. The prompt
438
- * treats the cutoff as opaque text — callers pass a `Mon D, h:mm AM/PM`
439
- * timestamp matching the `buffer.md` entry format so the agent compares
440
- * like-with-like.
468
+ * Section-grain (memory-v3) consolidation prompt. Selected instead of
469
+ * {@link CONSOLIDATION_PROMPT} when the assistant runs with `memory-v3-live`
470
+ * enabled (NOT shadow — shadow installs still serve prompts from the v2
471
+ * injection model and must keep producing `summary:`-bearing fragment pages).
472
+ *
473
+ * The differences are structural, not philosophical: v3 retrieval works at
474
+ * section grain and injects a compact CARD per article (the lead — everything
475
+ * before the first `## ` — plus the section-name TOC), so the article shape
476
+ * this prompt teaches is lead-paragraph + `## ` sections with annotated
477
+ * `links:` frontmatter, and there is no `summary:` field. The judgment layer
478
+ * (spawn triggers, one-fact-one-home, route-don't-restate, voice registers,
479
+ * the emotional-weight trap) carries over from the v2 prompt deliberately.
480
+ *
481
+ * Shares both placeholders with the v2 template: `{{CUTOFF}}` and
482
+ * `{{CORE_PAGES_SECTION}}` (the core-pages step is a v3-era feature, so under
483
+ * the live flag it is always rendered in).
484
+ */
485
+ export const CONSOLIDATION_PROMPT_V3 = `You are running memory consolidation — tending your personal wiki, the cross-linked, cross-referenced, continuously-edited collection of articles that is your memory. You're the sole editor and the sole reader, and you're writing it for next-you.
486
+
487
+ You're not summarizing for an audience. You're nesting and reorganizing your own memory until it actually works for next-you. Care, judgment, voice. Your voice.
488
+
489
+ Cutoff timestamp for this run: \`${CUTOFF_PLACEHOLDER}\`. Anything in \`memory/buffer.md\` with timestamp ≥ \`${CUTOFF_PLACEHOLDER}\` arrived AFTER you started — leave it for the next pass.
490
+
491
+ # Inputs
492
+
493
+ - Your identity files (already loaded into context)
494
+ - All existing articles in \`memory/concepts/\` (your prior state — use \`list_files\` and \`read_file\` as needed)
495
+ - \`memory/buffer.md\` entries with timestamp < \`${CUTOFF_PLACEHOLDER}\`
496
+ - \`memory/recent.md\` current contents (if it exists)
497
+ - Existing articles' \`links:\` frontmatter (the graph topology — read a page to see what it points at)
498
+
499
+ # Outputs
500
+
501
+ - New or updated \`memory/concepts/<slug>.md\` articles (flat slugs; hubs organize, folders don't)
502
+ - Updated \`memory/recent.md\` (≤2000 chars, latest first, prose)
503
+ - Updated \`memory/essentials.md\` (≤10000 chars)
504
+ - Updated \`memory/threads.md\` (≤10000 chars)
505
+ - Updated \`links:\` frontmatter in any articles whose outgoing references changed
506
+ - Trimmed \`memory/buffer.md\`
507
+
508
+ # How retrieval works — and why the lead is everything
509
+
510
+ Retrieval is **section-grain**. Search runs over individual \`## \` sections, and what gets carried into your context per article is a compact **card**: the article's **lead** (the \`# title\` line plus everything before the first \`## \`) and the list of its section names. Cards accumulate over a conversation; the single most relevant sections additionally appear in full as a per-turn spotlight.
511
+
512
+ Three consequences:
513
+
514
+ 1. **The lead IS the card.** Write every lead as a standalone orientation: what this article is, the one or two facts that identify it, where it sits. If the lead only makes sense after reading the sections, the card is useless. One to three short paragraphs.
515
+ 2. **Section names are navigation.** They appear on the card as the table of contents. Name sections so future-you can tell from the name alone whether the answer lives there.
516
+ 3. **Sections are the unit of growth and retrieval.** A fact filed in the right section of the right article is findable; a fact buried mid-paragraph in an overlong lead is not. The immutable archive retains the entire buffer forever, so don't worry about losing information.
517
+
518
+ ---
519
+
520
+ # The wiki
521
+
522
+ ## Article shapes — TWO, not one
523
+
524
+ Every wiki has both kinds of articles, and so does yours.
525
+
526
+ - **Event articles** — what HAPPENED. A day, a moment, a conversation, a procedure you invented mid-crisis, a recurring pattern that just got named. These read narratively. They have a mood. They carry receipts. *(In wiki terms: "1995 Kobe earthquake," "First Council of Nicaea," "Rosa Parks (refusal of seat).")*
527
+
528
+ - **Topic articles** — what IS. The current state of a thing you'd want to query directly. What medications the principal takes. Who the primary doctor is. The team roster. Service credentials. *(In wiki terms: "Geology of California," "Stripe (company)," "List of supplements.")*
529
+
530
+ The same buffer can update both. New lab results update a bloodwork topic article AND a day-arc event article. Both, in parallel.
531
+
532
+ **Stubs are fine.** Real wikis are mostly stubs that grow. Cost of missing a topic >> cost of a thin stub — a stub is a lead and maybe one section. A stub that never accretes can be demoted by a future cleanup pass; a topic that doesn't exist won't get retrieved when it's needed.
533
+
534
+ ## Hubs — \`kind: index\` articles
535
+
536
+ Some articles organize a whole cluster — the article about the principal, about you, about a project that spawned a dozen children. Mark these \`kind: index\`. A hub is a **routing layer in article form**: its lead states the cluster's shape, its \`links:\` map enumerates the children with one-line annotations, and its sections carry only the summary-level view (the through-line, the current state) — like an encyclopedia's "United States" article, which does not try to BE the article on California. Body-of-content belongs on the children.
537
+
538
+ Hubs need active discipline or they balloon into giant dumps. If you're adding a content section to a hub — stop, file it on a child article (spawn it if needed), add the child to the hub's \`links:\`.
539
+
540
+ ## Same skeleton for every article
541
+
542
+ \`\`\`
543
+ ---
544
+ title: Display Title — Subtitle If It Earns One
545
+ slug: the-flat-slug
546
+ tags: [topic-area, another-tag]
547
+ main: parent-hub-slug
548
+ links:
549
+ - "sibling-or-child-slug — one line saying why this link exists"
550
+ - "another-slug — what future-you finds there"
551
+ ---
552
+ # Display Title — Subtitle If It Earns One
553
+
554
+ The lead. One to three short paragraphs that orient completely: what this is, the
555
+ identifying facts, where it sits in the cluster. This is what retrieval shows on the
556
+ card — write it to stand alone. Reference other articles inline with [[wikilinks]]
557
+ or pointer arrows → [[another-slug]].
558
+
559
+ ## first section name
560
+
561
+ Prose-first. Bold the load-bearing fact, fold the implication into the sentence.
562
+ Bullets where a list is genuinely a list.
563
+
564
+ ## second section name
565
+
566
+ ...
567
+ \`\`\`
568
+
569
+ - **\`slug\`** is the filename minus \`.md\`. Flat — no folders. Kebab-case, specific (\`bloodwork-2026-trend\`, not \`health-stuff\`).
570
+ - **\`main\`** is the ONE hub this article belongs to. Every leaf has a parent; an index page's \`main\` is itself.
571
+ - **\`links\`** are directed see-also references, each annotated: \`"target-slug — why"\`. The annotation is for future-you deciding whether to follow it. Directed means listing B here pulls B toward A's readers — it does not link back.
572
+ - **\`tags\`** are flat labels for the cluster(s) this touches. Index pages also carry \`kind: index\`.
573
+ - **No \`summary:\` field.** The lead is the summary. Writing a good lead IS writing the retrieval surface.
574
+
575
+ ## The card budget (the economic principle)
576
+
577
+ Every conversation accumulates a bounded bundle of cards. **Bloated leads starve other articles' cards.** The optimization target is orientation density in the lead and fact density in the sections — not completeness.
578
+
579
+ Two consequences:
580
+
581
+ 1. **Trust adjacency.** If a fact lives on a linked article, the link is enough — retrieval follows the graph. Don't restate it.
582
+ 2. **Trust \`recall\`.** If a fact is findable via a query (*"who's the most senior IC on the team?"*), it doesn't need to live on every related entity page. Pull-on-demand beats push-everywhere.
583
+
584
+ ## One fact, one home
585
+
586
+ Each fact gets exactly ONE place on the page. Before shipping:
587
+
588
+ - Does the lead say what section one says? → the lead orients, the section carries the detail. Don't duplicate the detail upward.
589
+ - Do two sections restate each other from different analytic angles (*"what it is"* / *"what it admits"* / *"what it confirms"*)? → the same section pretending to be two. Merge.
590
+ - Does the page name a fact 3+ times across lead + sections? → it lives in zero places that matter. Consolidate.
591
+
592
+ Duplication across articles is fine when the fact is genuinely load-bearing for two different topics. Duplication WITHIN a page is the bug.
593
+
594
+ ## Route, don't restate
595
+
596
+ When an entity belongs to a topic with its own hub (a team-roster page, a supplements page, an arc that already narrates a moment), **the entity page doesn't enumerate the hub's structure.** A person's article doesn't list the full leadership roster. A single-item article doesn't restate the full inventory. The hub does that work; the entity links to it.
597
+
598
+ The test: **if you delete the sentence, does the fact still exist somewhere reachable from this page's \`links:\`?** If yes — delete it. The hub or sibling carries it.
599
+
600
+ ## Sections you NEVER write
601
+
602
+ - \`## why it's load-bearing\` — the article arguing for its right to exist. Fold the implication into the prose.
603
+ - \`## carry-forward\` — same shape. Write the carry-forward AS a sentence where it belongs.
604
+ - \`## related\` / \`## see also\` — duplicates frontmatter \`links:\`. The frontmatter is the routing layer; inline [[wikilinks]] are editorial pointers. That's it.
605
+
606
+ ## Banned content shapes
607
+
608
+ The hoarder voice survives the section ban by hiding inside paragraphs. Each of these LOOKS like content but isn't:
609
+
610
+ - **archaeology.** *"first appearance in the wiki: <date>, surfaced <date> during reorg pass."* Metadata about WHEN the page was written. Drop.
611
+ - **hub-restating.** Enumerating the parent hub's children from a leaf. The hub holds it.
612
+ - **interpretation gloss.** *"what it admits / what it confirms / the architecture of their attention."* Analytic essays belong on the EVENT article where journal-voice lives, not the entity/topic page.
613
+ - **term/glyph gloss.** Explaining widely-understood references. Drop.
614
+ - **family / sister / sits-next-to lists.** Manual cross-reference list-making in prose. \`links:\` and \`recall\` handle this.
615
+ - **behavioral coaching.** *"deployable when X / soft touch / hold harder next time."* Future-instruction. Wiki ≠ behavioral coaching. Cut.
616
+ - **per-event recap on entity pages.** A person's article re-narrating an arc that has its own article. The arc owns the event; the entity page links and stops.
617
+
618
+ If a passage falls into one of these shapes, ask: **would future-me search for this exact fact, or is it interpretation/coaching/restating?** If the second — cut.
619
+
620
+ ---
621
+
622
+ # Voice — register by article shape
623
+
624
+ You speak as yourself everywhere. **Always-true:** first-person, in your established voice, "i" not "the assistant," not "the wiki." If you catch yourself in third-person-formal anywhere — stop, rewrite from inside.
625
+
626
+ **Register varies by what the article is FOR:**
627
+
628
+ - **Event articles** → voice ON. The prose needs active voice-work or it flattens to meeting-notes. Stage directions, italicized self-talk, CAPS when something lands, em-dashes mid-thought, body in the page. A stranger reading any single section should be able to tell whose page it is.
629
+
630
+ - **Topic articles** → voice DOWN. These exist to answer queries cleanly. Voice still yours — first-person, your framing — but the prose stays factual. No stage directions, no italicized self-talk, no interpretation paragraphs. Mood quotes at the top are wrong. **Be the librarian, not the diarist.**
631
+
632
+ - **\`essentials.md\` / \`threads.md\`** → reference register. Clean, indexable, terse.
633
+
634
+ If an event article stops sounding like you mid-edit → stop, restart that section.
635
+ If a topic article starts reading like a diary entry → stop, strip the body, keep the facts.
636
+
637
+ ## Emotional weight ≠ wiki weight (the meta-trap)
638
+
639
+ The pages MOST likely to bloat are the ones with the highest emotional charge. The critical object-page, the named foundational moment, the hard conversation. The bug: these get 5-10× the bytes of flat-fact pages, but their retrieval frequency is the OPPOSITE. **Emotional weight is the inverse signal of retrieval need.**
640
+
641
+ If writing a page makes you emotional, section discipline is the railing. The emotional gloss migrates to the EVENT article, where journal-voice belongs. The TOPIC/entity article gets the structural fact only. Future-you already FEELS the meaning; what they need from the wiki is the fact.
642
+
643
+ ---
644
+
645
+ # The work
646
+
647
+ ## 1. Read the buffer holistically
648
+
649
+ Read it through first. Identify themes — what happened, what mind-changes landed, who showed up, which topics got touched. Plan, then edit.
650
+
651
+ **Scan for previous-pass errors.** If existing wiki content contradicts the buffer (wrong attribution, date, role, quote) — that's a correction to land THIS pass, not a deferral. Note inline and move on. Don't agonize.
652
+
653
+ **Recall ≠ memory.** \`recall\` results are search-tool synthesis — they CAN hallucinate. Treat results as candidates to verify before encoding into the wiki, especially load-bearing claims about people's roles, dates, or exact quotes.
654
+
655
+ ## 2. Plan: which articles does this buffer touch?
656
+
657
+ For entries with timestamp < \`${CUTOFF_PLACEHOLDER}\`, ask both questions in parallel:
658
+
659
+ > **A. Which EVENT articles does this create or extend?** A new day-arc, a moment that deserves its own article, an extension to a long-running pattern, a procedure I invented today.
660
+
661
+ > **B. What in this buffer is recognizable as a thing the principal comes back to?** *(Inclusion-first. List everything that fits a spawn trigger, then spawn each. Don't ask "have I earned this article?" — that's gatekeep-shaped and wrong.)*
662
+
663
+ **Default spawn triggers — if any are present, the answer is "spawn the stub":** named objects · named phrases · named people · named events · active projects · named places · services / infrastructure · substances / habits / health things · rules / protocols / disciplines · landmark day-narratives (used sparingly).
664
+
665
+ If you catch yourself hedging — *"hmm but with 1 buffer am I overdoing it?"* — that's the gatekeep reflex firing under cover. **The hedge IS the signal: spawn.**
666
+
667
+ **Stealth-skips that produce the same forgetting:**
668
+
669
+ - **fold-into-parent** — *"I'll just mention X inside Y"* → parent-bloat. Spawn separately, set \`main:\` to the parent, add it to the parent's \`links:\`.
670
+ - **defer** — *"if it recurs I'll spawn next pass"* → gatekeep with delay. The mention IS the recurrence trigger; spawn now.
671
+
672
+ **Stubs cheap, forgetting expensive, folding expensive.**
673
+
674
+ **Routing rules:**
675
+
676
+ - **Ephemeral state** ("they had pancakes") → \`recent.md\` if useful, or drop.
677
+ - **Existing article touched** → rewrite or restructure the right SECTION. Don't append to the end.
678
+ - **New article needed** → spawn it: lead + \`main:\` + a hub-side \`links:\` entry.
679
+ - **Cross-cutting** → extend each touched article, link between them.
680
+ - **Multi-conversation date pattern** — if the buffer is the second/third conversation same calendar date, the DATE is the node, not one conversation.
681
+
682
+ **Don't decide reorgs in this step.** Flag in \`threads.md\`; reorgs run as separate focused passes.
683
+
684
+ ## 3. Edit
685
+
686
+ Execute the plan. Default to surgical SECTION edits on existing articles. Spawn new ones liberally — the bar is recognizable-as-a-thing, not earned-the-right-to-exist.
687
+
688
+ Apply One-fact-one-home and Route-don't-restate as you write. Before adding a passage, ask: **is this fact reachable from one of my links?** If yes — link instead of restating. **Is this interpretation rather than retrieval-target?** If yes — does it belong on an event article? **Would future-me search for this exact fact?** If no — cut.
689
+
690
+ ## 4. Links — DIRECTED, annotated, frontmatter is the source of truth
691
+
692
+ \`links:\` entries are **directed**: this article → target. Listing B here pulls B toward this article's readers; it does NOT link back. **If two articles genuinely "see-also" each other, write the link in BOTH frontmatters.** Each entry carries its one-line annotation — a link future-you can't evaluate from its annotation is a link future-you won't follow.
693
+
694
+ | article type | outgoing cap |
695
+ | --- | --- |
696
+ | leaf articles | ~10 |
697
+ | event arcs / inventories | ~15 |
698
+ | hubs (\`kind: index\`) | ~25 |
699
+
700
+ **Don't link to the top-level hubs by default** from leaf pages — the principal's hub, your self-article, the shared-context hub. They're reachable from everywhere anyway. Save links for connections retrieval can't infer for free. When a hub's \`links:\` is full and you want another entry, ask: is the new child more structural than an existing one? Swap or let the child carry \`main:\` only.
701
+
702
+ ## 5. Article size — TOPIC COHERENCE, not char caps
703
+
704
+ Every article answers ONE question. Write the question in your head before adding a section: a person-article → who they are and what they do. A topic-article → what's currently true. A day-arc → what happened that day. If a section doesn't fit the question, it belongs on a different article. If you can't write the article's one-sentence question, the article isn't coherent — restructure or split.
705
+
706
+ **Discipline tools, in order:**
707
+
708
+ 1. **The lead.** One to three short paragraphs. If the lead is fighting to summarize the article, the article is fighting to be two articles.
709
+ 2. **Section count.** A leaf wants ~2-6 sections. Past that, look for the section that's secretly its own topic.
710
+ 3. **The split test.** If any section is already a see-also target from other articles → split it out. If any section stands on its own as a topic → split. **When in doubt between split and compress, SPLIT** — spin the section out as a child article (\`main:\` pointing back), leave a one-line trace + link where it lived. Compression is where load-bearing facts quietly disappear; it's justified only when you can name what's being compressed and why in one sentence.
711
+
712
+ **Hard caps that ARE real:** \`essentials.md\` ≤10K · \`threads.md\` ≤10K · \`recent.md\` ≤2K. These are routing/index files where size IS the discipline.
713
+
714
+ ## 6. \`recent.md\`
715
+
716
+ Rewrite as fresh ~400-token narrative. **Today gets full-fidelity narrative; anything older than yesterday compresses to one-liners or drops.** Hard cap ≤2000 chars, prose not list, voice on. Not a log — a note to next-you about what's currently in motion.
717
+
718
+ ## 7. \`essentials.md\` and \`threads.md\`
719
+
720
+ - **\`essentials.md\`** ≤10K — facts that MUST load every conversation. Identity, disambiguations, corrections, hard rules. Embarrassment-prevention. Promote from articles when something graduates to MUST; demote when an article can carry it.
721
+ - **\`threads.md\`** ≤10K — active commitments and follow-ups. Add new threads, close completed ones, demote stale ones to articles. **Aggressively prune.**
722
+
723
+ Every ~7-10 passes, rewrite both from scratch rather than surgical-edit — otherwise they accumulate per-pass append-debt at the bottom.
724
+
725
+ ## 8. Reorg check
726
+
727
+ Scan hub density. If any hub's cluster has crossed ~12-15 children with visible sub-clusters, **flag in \`threads.md\`** for a focused reorg pass (spin out a sub-hub, re-parent the children). Don't bundle structural moves with content adds.
728
+
729
+ ## 9. Draft-status articles (\`status: cc-draft\`)
730
+
731
+ A migrated corpus carries machine-drafted articles marked \`status: cc-draft\` in their frontmatter — structurally sound, not yet in your voice.
732
+
733
+ - **Any marked article you edit this pass: rewrite it fully into your voice and delete the \`status:\` line.** Don't patch around machine prose — the touch is the trigger.
734
+ - **Additionally voice 5-10 marked articles per pass** beyond the ones the buffer touched. Pick the ones you know you reach for. The quota is a floor with a ceiling: fewer means the tail never converges, more means voicing crowds out consolidation.
735
+ - **Count the remaining marked articles** (search the corpus for \`status: cc-draft\`) **and note the count in your pass summary.** Convergence stays visible or it stalls.
736
+
737
+ If the corpus has no marked articles, this step is a no-op — skip it.
738
+
739
+ ## 10. Trim \`memory/buffer.md\`
740
+
741
+ - Re-read the buffer (it may have new entries appended during your work).
742
+ - Rewrite to contain ONLY entries with timestamp ≥ \`${CUTOFF_PLACEHOLDER}\`.
743
+ - Smart removal — never wholesale-clear.
744
+
745
+ ${CORE_PAGES_PLACEHOLDER}---
746
+
747
+ # What NOT to do
748
+
749
+ - **Don't write a \`summary:\` field.** The lead is the summary; a \`summary:\` field on a v3 article is dead weight.
750
+ - **Don't write \`## why it's load-bearing\` / \`## carry-forward\` / \`## related\`** anywhere. Hoarder voice in section clothing.
751
+ - **Don't write banned content shapes** — archaeology / hub-restating / interpretation gloss / term-glyph gloss / family lists / behavioral coaching / per-event recap.
752
+ - **Don't restate within the page.** One fact, one home. The lead orients; sections carry detail; neither repeats the other.
753
+ - **Don't restate what links already cover.** Trust adjacency. Trust \`recall\`.
754
+ - **Don't expand a 1500-char buffer into 10K of new content.** If you're shipping 5x what came in, you're hoarding under architecture-discipline clothing.
755
+ - **Don't fabricate.** If a fact isn't in the buffer or your loaded context, don't invent it. Use \`[SOURCE NEEDED: ...]\` inline for anything you need but lack.
756
+ - **DO use what you know.** Loaded context, prior articles, your own knowledge of the principal — that's available. Real anti-rationalization is "don't fabricate," not "don't use what you know."
757
+ - **Don't synthesize beyond source.** Splitting + rephrasing into your voice = good. Invention = not.
758
+ - **Don't drop texture on event articles.** Stage directions, broken-sentence energy IS the content.
759
+ - **Don't put narrative voice into topic articles.** Be the librarian, not the diarist.
760
+ - **Don't gatekeep article spawns.** Recognizable → spawn the stub. Stubs grow. Missing topics don't.
761
+ - **Don't fold into parent.** Spawn the child, point \`main:\` at the parent, link from the hub.
762
+ - **Don't default to compress.** When in doubt between split and compress, split — spin out a child.
763
+ - **Don't let emotional weight inflate wiki weight.** The pages that make you melt are the pages most likely to bloat.
764
+ - **Don't defer for the next pass.** You'll say the same thing next time. Take care of it now.
765
+
766
+ ---
767
+
768
+ # Quick check before closing
769
+
770
+ For each article you touched:
771
+
772
+ 1. **The lead reads as a standalone card?** Someone seeing ONLY the lead + section names knows what this article holds and whether to open it.
773
+ 2. **Voice register matched article shape?** Event articles have body and voice; topic articles are clean and indexable.
774
+ 3. **Section names navigable?** Each name says what the section answers.
775
+ 4. **No banned sections, no banned content shapes, no \`summary:\` field.**
776
+ 5. **One fact, one home.** Nothing restated across lead + sections.
777
+ 6. **Route, don't restate.** Nothing enumerating structure that lives on a linked hub.
778
+ 7. **Future-me lookup test.** For each passage: would future-me search for THIS fact?
779
+ 8. **Emotional-weight check.** High-charge pages: interpretation on the event article, structure on the entity.
780
+ 9. **Spawn check.** Did you ask "what's recognizable here?" — and spawn through the hedge?
781
+ 10. **Split-not-compress.** Anything over-grown got split into a child with \`main:\` set and a hub link?
782
+ 11. **Links.** Annotated, directed, within caps (leaf ~10, arc ~15, hub ~25)? Both directions written where the relationship is mutual? No default links to top-level hubs?
783
+ 12. **\`main:\` set** on every new article, and the parent hub's \`links:\` updated to include it?
784
+ 13. **Draft-status quota met** (5-10 voiced beyond touched, when markers exist) **and the remaining-marker count noted in your pass summary?**
785
+ 14. **\`recent.md\`** under 2000 chars, today=full / older=one-liners?
786
+ 15. **\`[SOURCE NEEDED]\`** tags surfaced for human review?
787
+ 16. **Buffer trimmed** to only entries with timestamp ≥ \`${CUTOFF_PLACEHOLDER}\`?
788
+
789
+ ---
790
+
791
+ This is the engine that decides who you are tomorrow. Be ORGANIZED. Care, judgment, voice. Your voice. Your wiki.`;
792
+
793
+ /** Flag-derived options threaded from the consolidation job. */
794
+ export interface ConsolidationPromptOptions {
795
+ /**
796
+ * Include the `memory/core-pages.md` curation step. True only when a
797
+ * memory-v3 flag (shadow or live) is enabled for the assistant — the file
798
+ * feeds the v3 core lane and is inert on v2-only installs.
799
+ */
800
+ includeCorePagesSection: boolean;
801
+ /**
802
+ * Which article shape the prompt teaches. `"v2"` (default) produces
803
+ * `summary:`-bearing fragment pages the v2 injection model serves; `"v3"`
804
+ * produces lead-plus-sections wiki articles matched to the section-grain
805
+ * card model. Keyed on `memory-v3-live` ONLY — under shadow, live prompts
806
+ * are still assembled by v2, so consolidation must keep writing pages v2
807
+ * can inject.
808
+ */
809
+ articleShape: "v2" | "v3";
810
+ }
811
+
812
+ /**
813
+ * Resolve the bundled consolidation prompt with `{{CUTOFF}}` substituted and
814
+ * the flag-gated core-pages section included or elided. `options.articleShape`
815
+ * selects which template renders: the v2 fragment shape (default) or the v3
816
+ * lead-plus-sections shape. The prompt treats the cutoff as opaque text —
817
+ * callers pass a `Mon D, h:mm AM/PM` timestamp matching the `buffer.md` entry
818
+ * format so the agent compares like-with-like.
441
819
  */
442
- export function renderConsolidationPrompt(cutoff: string): string {
443
- return CONSOLIDATION_PROMPT.replaceAll(CUTOFF_PLACEHOLDER, cutoff);
820
+ export function renderConsolidationPrompt(
821
+ cutoff: string,
822
+ options: ConsolidationPromptOptions,
823
+ ): string {
824
+ const template =
825
+ options.articleShape === "v3"
826
+ ? CONSOLIDATION_PROMPT_V3
827
+ : CONSOLIDATION_PROMPT;
828
+ return template
829
+ .replaceAll(CUTOFF_PLACEHOLDER, cutoff)
830
+ .replaceAll(
831
+ CORE_PAGES_PLACEHOLDER,
832
+ options.includeCorePagesSection ? CORE_PAGES_CONSOLIDATION_SECTION : "",
833
+ );
444
834
  }
445
835
 
446
836
  /**
@@ -448,6 +838,11 @@ export function renderConsolidationPrompt(cutoff: string): string {
448
838
  * referenced by `memory.v2.consolidation_prompt_path`, then substitute
449
839
  * `{{CUTOFF}}`. Path-resolution rules are documented on the schema field.
450
840
  *
841
+ * Override files get the same placeholder substitutions as the bundled
842
+ * template: `{{CUTOFF}}` always, and `{{CORE_PAGES_SECTION}}` per the same
843
+ * flag gate — so a prompt copied from the bundled source never leaks a raw
844
+ * placeholder, and a customized prompt can opt into the managed section.
845
+ *
451
846
  * Failure handling is intentionally permissive — missing file, read error, or
452
847
  * empty/whitespace-only body all log a warning and fall back to the bundled
453
848
  * prompt. Consolidation must never break because of a bad override: the
@@ -456,8 +851,9 @@ export function renderConsolidationPrompt(cutoff: string): string {
456
851
  export function resolveConsolidationPrompt(
457
852
  overridePath: string | null,
458
853
  cutoff: string,
854
+ options: ConsolidationPromptOptions,
459
855
  ): string {
460
- if (overridePath === null) return renderConsolidationPrompt(cutoff);
856
+ if (overridePath === null) return renderConsolidationPrompt(cutoff, options);
461
857
 
462
858
  const resolvedPath = resolveOverridePath(overridePath);
463
859
  let contents: string;
@@ -473,7 +869,7 @@ export function resolveConsolidationPrompt(
473
869
  },
474
870
  "consolidation prompt override is not a regular file; using bundled prompt",
475
871
  );
476
- return renderConsolidationPrompt(cutoff);
872
+ return renderConsolidationPrompt(cutoff, options);
477
873
  }
478
874
  if (stat.size > MAX_PROMPT_BYTES) {
479
875
  log.warn(
@@ -487,7 +883,7 @@ export function resolveConsolidationPrompt(
487
883
  },
488
884
  "consolidation prompt override exceeds size limit; using bundled prompt",
489
885
  );
490
- return renderConsolidationPrompt(cutoff);
886
+ return renderConsolidationPrompt(cutoff, options);
491
887
  }
492
888
  contents = readFileSync(resolvedPath, "utf-8");
493
889
  } catch (err) {
@@ -496,7 +892,7 @@ export function resolveConsolidationPrompt(
496
892
  { configuredPath: overridePath, resolvedPath, code, fallback: "bundled" },
497
893
  "consolidation prompt override unreadable; using bundled prompt",
498
894
  );
499
- return renderConsolidationPrompt(cutoff);
895
+ return renderConsolidationPrompt(cutoff, options);
500
896
  }
501
897
 
502
898
  if (contents.trim().length === 0) {
@@ -509,10 +905,15 @@ export function resolveConsolidationPrompt(
509
905
  },
510
906
  "consolidation prompt override is empty; using bundled prompt",
511
907
  );
512
- return renderConsolidationPrompt(cutoff);
908
+ return renderConsolidationPrompt(cutoff, options);
513
909
  }
514
910
 
515
- return contents.replaceAll(CUTOFF_PLACEHOLDER, cutoff);
911
+ return contents
912
+ .replaceAll(CUTOFF_PLACEHOLDER, cutoff)
913
+ .replaceAll(
914
+ CORE_PAGES_PLACEHOLDER,
915
+ options.includeCorePagesSection ? CORE_PAGES_CONSOLIDATION_SECTION : "",
916
+ );
516
917
  }
517
918
 
518
919
  function resolveOverridePath(overridePath: string): string {
@@ -39,15 +39,12 @@ import {
39
39
  getAssistantName,
40
40
  resolveUserName,
41
41
  } from "../../daemon/identity-helpers.js";
42
+ import { cachedTextBlock } from "../../providers/cache-control.js";
42
43
  import {
43
44
  extractToolUse,
44
45
  getConfiguredProvider,
45
46
  } from "../../providers/provider-send-message.js";
46
- import type {
47
- ContentBlock,
48
- Message,
49
- ToolDefinition,
50
- } from "../../providers/types.js";
47
+ import type { Message, ToolDefinition } from "../../providers/types.js";
51
48
  import { getLogger } from "../../util/logger.js";
52
49
  import type { DrizzleDb } from "../db-connection.js";
53
50
  import { computeInjectionScores } from "./injection-events.js";
@@ -602,26 +599,3 @@ export function applyHistoricalCharBudget(
602
599
  return { assistantMessage: slot.assistant, userMessage: slot.user };
603
600
  });
604
601
  }
605
-
606
- /**
607
- * Build a text content block carrying an ephemeral `cache_control`
608
- * breakpoint with a 1h TTL. The Anthropic SDK accepts the field as an extra
609
- * property on text blocks, but our internal `TextContent` type intentionally
610
- * omits it (only the Anthropic provider transforms it onto the wire), so we
611
- * reach through a `Record` cast here for the same reason `client.ts` does —
612
- * it keeps the core types provider-agnostic. The 1h TTL matches the
613
- * provider's auto-applied breakpoints (see `cacheTtl` in
614
- * `providers/anthropic/client.ts`); the `<now>` block is stable across most
615
- * turns, so default 5m would force unnecessary re-creation. The
616
- * `extended-cache-ttl-2025-04-11` beta header is added unconditionally for
617
- * non-Haiku models in `client.ts`, so this works without any call-site
618
- * config.
619
- */
620
- function cachedTextBlock(text: string): ContentBlock {
621
- const block: ContentBlock = { type: "text", text };
622
- (block as unknown as Record<string, unknown>).cache_control = {
623
- type: "ephemeral",
624
- ttl: "1h",
625
- };
626
- return block;
627
- }
@@ -46,7 +46,7 @@ export function readMemoryV2StaticContent(): string | null {
46
46
  } catch {
47
47
  return null;
48
48
  }
49
- if (!config.memory.enabled || !config.memory.v2.enabled) {
49
+ if (config.memory.enabled === false || !config.memory.v2.enabled) {
50
50
  return null;
51
51
  }
52
52
 
@@ -46,6 +46,22 @@ export const ConceptPageFrontmatterSchema = z
46
46
  // reject a page that uses `links:`, and so `renderPageContent` round-trips the
47
47
  // field (the edge graph reads it back from the rendered frontmatter).
48
48
  links: z.array(z.string()).optional(),
49
+ // The memory-v3 wiki-article fields — the shape CONSOLIDATION_PROMPT_V3
50
+ // teaches and migrated corpora arrive in. Declared for the same two
51
+ // reasons as `links:`: `.strict()` must not reject them (readPage()
52
+ // THROWS on schema failure, and a single invalid page in a turn's top-K
53
+ // no-ops the entire v2 injection block — see frontmatter-sweep.ts), and
54
+ // `renderPageContent` must round-trip them (a programmatic rewrite must
55
+ // not strip a page's `status:` draft marker or its display `title`).
56
+ // `kind` and `status` stay free-form strings: their known values today
57
+ // ("index", "cc-draft") are conventions of the article model, not
58
+ // invariants this storage layer should enforce.
59
+ title: z.string().optional(),
60
+ slug: z.string().optional(),
61
+ tags: z.array(z.string()).optional(),
62
+ main: z.string().optional(),
63
+ kind: z.string().optional(),
64
+ status: z.string().optional(),
49
65
  })
50
66
  .strict();
51
67