@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
@@ -10,13 +10,12 @@
10
10
  * creation/reuse lifecycle — provider wiring, rate limiting, system
11
11
  * prompt assembly, and DB hydration. DaemonServer calls
12
12
  * {@link initConversationLifecycle} once at construction time to
13
- * supply the few remaining lifecycle references (evictor, CES client,
14
- * shared rate-limit timestamps, broadcast).
13
+ * supply the few remaining lifecycle references (evictor, shared
14
+ * rate-limit timestamps, broadcast).
15
15
  */
16
16
 
17
17
  import { resolveCallSiteConfig } from "../config/llm-resolver.js";
18
18
  import { getConfig } from "../config/loader.js";
19
- import type { CesClient } from "../credential-execution/client.js";
20
19
  import { buildSystemPrompt } from "../prompts/system-prompt.js";
21
20
  import { wrapWithCallSiteRouting } from "../providers/call-site-routing.js";
22
21
  import { resolveDefaultProvider } from "../providers/connection-resolution.js";
@@ -68,7 +67,6 @@ const conversationCreating = new Map<string, Promise<Conversation>>();
68
67
 
69
68
  /** Lifecycle refs injected once by DaemonServer at construction. */
70
69
  let _evictor: ConversationEvictor | null = null;
71
- let _cesClientPromise: Promise<CesClient | undefined> | undefined;
72
70
  let _sharedRequestTimestamps: number[] = [];
73
71
 
74
72
  /**
@@ -77,23 +75,12 @@ let _sharedRequestTimestamps: number[] = [];
77
75
  */
78
76
  export function initConversationLifecycle(refs: {
79
77
  evictor: ConversationEvictor;
80
- cesClientPromise?: Promise<CesClient | undefined>;
81
78
  sharedRequestTimestamps: number[];
82
79
  }): void {
83
80
  _evictor = refs.evictor;
84
- _cesClientPromise = refs.cesClientPromise;
85
81
  _sharedRequestTimestamps = refs.sharedRequestTimestamps;
86
82
  }
87
83
 
88
- /**
89
- * Update the CES client promise after async initialization completes.
90
- */
91
- export function setCesClientPromise(
92
- p: Promise<CesClient | undefined> | undefined,
93
- ): void {
94
- _cesClientPromise = p;
95
- }
96
-
97
84
  function applyTransportMetadata(
98
85
  conversation: Conversation,
99
86
  options: ConversationCreateOptions | undefined,
@@ -177,9 +164,6 @@ export async function getOrCreateConversation(
177
164
  storedOptions?.systemPromptOverride ?? buildSystemPrompt();
178
165
  const maxTokens = storedOptions?.maxResponseTokens;
179
166
 
180
- const sharedCesClient = _cesClientPromise
181
- ? await _cesClientPromise
182
- : undefined;
183
167
  const newConversation = new Conversation(
184
168
  conversationId,
185
169
  provider,
@@ -188,7 +172,6 @@ export async function getOrCreateConversation(
188
172
  workingDir,
189
173
  {
190
174
  maxTokens,
191
- sharedCesClient,
192
175
  speedOverride: storedOptions?.speed,
193
176
  modelOverride: storedOptions?.modelOverride,
194
177
  },
@@ -13,6 +13,7 @@ import {
13
13
  } from "../channels/types.js";
14
14
  import { getIsPlatform } from "../config/env-registry.js";
15
15
  import { getConfig } from "../config/loader.js";
16
+ import type { LLMCallSite } from "../config/schemas/llm.js";
16
17
  import { getBindingByConversation } from "../memory/external-conversation-store.js";
17
18
  import type { PermissionPrompter } from "../permissions/prompter.js";
18
19
  import type { SecretPrompter } from "../permissions/secret-prompter.js";
@@ -34,6 +35,10 @@ import {
34
35
  type ToolExecutionResult,
35
36
  type ToolLifecycleEventHandler,
36
37
  } from "../tools/types.js";
38
+ import {
39
+ resolveUsageAttribution,
40
+ type UsageAttributionSnapshot,
41
+ } from "../usage/attribution.js";
37
42
  import { getLogger } from "../util/logger.js";
38
43
  import {
39
44
  projectSkillTools,
@@ -59,6 +64,45 @@ import {
59
64
  import type { ToolSetupContext } from "./tool-setup-types.js";
60
65
  export type { ToolSetupContext } from "./tool-setup-types.js";
61
66
 
67
+ // ── resolveConversationAttribution ───────────────────────────────────
68
+
69
+ /**
70
+ * Resolve the model attribution snapshot for the conversation at invocation
71
+ * time (provider/model/profile that issued the current turn). Mirrors how
72
+ * the agent-loop usage path builds its `UsageAttributionInput` — the
73
+ * current turn's call site (`runAgentLoopImpl` sets `ctx.currentCallSite`
74
+ * from `options.callSite`, defaulting to `mainAgent`) plus the
75
+ * conversation's per-turn override profile — so `profileSource` resolves
76
+ * to `call_site`/`conversation`/`active`/`default` exactly as `llm_usage`
77
+ * records do for the same turn (non-main turns like voice `callAgent` or
78
+ * `filingAgent` attribute their own call-site config, not the main
79
+ * agent's). The conversation id is threaded as the mix selection seed so
80
+ * mix-profile arms match what the dispatch path actually ran.
81
+ *
82
+ * Returns `null` on any failure: attribution is telemetry-only and must
83
+ * never break tool execution (or skill loads, which reuse this helper).
84
+ */
85
+ export function resolveConversationAttribution(
86
+ ctx: Pick<
87
+ ToolSetupContext,
88
+ "conversationId" | "currentCallSite" | "currentTurnOverrideProfile"
89
+ >,
90
+ ): UsageAttributionSnapshot | null {
91
+ try {
92
+ return resolveUsageAttribution({
93
+ callSite: ctx.currentCallSite ?? "mainAgent",
94
+ overrideProfile: ctx.currentTurnOverrideProfile ?? null,
95
+ selectionSeed: ctx.conversationId,
96
+ });
97
+ } catch (err) {
98
+ log.debug(
99
+ { err, conversationId: ctx.conversationId },
100
+ "Failed to resolve conversation attribution for telemetry (non-fatal)",
101
+ );
102
+ return null;
103
+ }
104
+ }
105
+
62
106
  // ── createToolExecutor ───────────────────────────────────────────────
63
107
 
64
108
  /**
@@ -128,9 +172,9 @@ export function createToolExecutor(
128
172
  diskPressureCleanupModeActive: ctx.diskPressureCleanupModeActive,
129
173
  toolUseId,
130
174
  isPlatformHosted: getIsPlatform(),
131
- cesClient: ctx.cesClient,
132
175
  transportInterface: ctx.transportInterface,
133
176
  overrideProfile: ctx.currentTurnOverrideProfile,
177
+ attribution: resolveConversationAttribution(ctx),
134
178
  onToolLifecycleEvent: handleToolLifecycleEvent,
135
179
  sendToClient: (msg) => {
136
180
  // Tool context's sendToClient uses a loose { type: string; [key: string]: unknown }
@@ -299,6 +343,12 @@ export interface SkillProjectionContext {
299
343
  readonly skillProjectionCache: SkillProjectionCache;
300
344
  readonly coreToolNames: Set<string>;
301
345
  allowedToolNames?: Set<string>;
346
+ /**
347
+ * Durable copy of the full tool set resolved on the most recent turn, used
348
+ * by read-only inventory queries. Set alongside {@link allowedToolNames}
349
+ * but, unlike that per-turn execution gate, never cleared at turn teardown.
350
+ */
351
+ lastResolvedToolNames?: Set<string>;
302
352
  /** When > 0, the resolveTools callback returns no tools at all. */
303
353
  toolsDisabledDepth: number;
304
354
  /** Channel capabilities — read lazily per turn for conditional tool filtering. */
@@ -326,6 +376,17 @@ export interface SkillProjectionContext {
326
376
  readonly transportInterface?: InterfaceId;
327
377
  /** Per-turn override profile, read by the switch_inference_profile tool injection. */
328
378
  currentTurnOverrideProfile?: string;
379
+ /**
380
+ * Conversation id for `skill_loaded` telemetry. Absent (e.g. minimal test
381
+ * contexts) disables telemetry recording in the skill projection.
382
+ */
383
+ readonly conversationId?: string;
384
+ /**
385
+ * The LLM call site driving the current turn (see
386
+ * {@link ToolSetupContext.currentCallSite}) — read per turn so skill_loaded
387
+ * telemetry attributes the provider/model/profile the turn actually ran on.
388
+ */
389
+ currentCallSite?: LLMCallSite;
329
390
  }
330
391
 
331
392
  // ── Conditional tool sets ────────────────────────────────────────────
@@ -437,6 +498,13 @@ export function isToolActiveForContext(
437
498
  ) {
438
499
  return false;
439
500
  }
501
+ if (name === "remember") {
502
+ try {
503
+ return getConfig().memory?.enabled !== false;
504
+ } catch {
505
+ return true;
506
+ }
507
+ }
440
508
  if (UI_SURFACE_TOOL_NAMES.has(name)) {
441
509
  if (
442
510
  ctx.channelCapabilities?.channel === "slack" &&
@@ -592,6 +660,19 @@ export function createResolveToolsCallback(
592
660
  preactivatedSkillIds: effectivePreactivated,
593
661
  previouslyActiveSkillIds: ctx.skillProjectionState,
594
662
  cache: ctx.skillProjectionCache,
663
+ // skill_loaded telemetry context — resolved per turn so attribution
664
+ // reflects the call site/profile the current turn actually runs on.
665
+ telemetry:
666
+ ctx.conversationId !== undefined
667
+ ? {
668
+ conversationId: ctx.conversationId,
669
+ attribution: resolveConversationAttribution({
670
+ conversationId: ctx.conversationId,
671
+ currentCallSite: ctx.currentCallSite,
672
+ currentTurnOverrideProfile: ctx.currentTurnOverrideProfile,
673
+ }),
674
+ }
675
+ : undefined,
595
676
  });
596
677
  const turnAllowed = new Set(allBaseDefs.map((d) => d.name));
597
678
  for (const name of projection.allowedToolNames) {
@@ -602,6 +683,11 @@ export function createResolveToolsCallback(
602
683
  if (excluded.has(name)) continue;
603
684
  turnAllowed.add(name);
604
685
  }
686
+ // Record the full resolved inventory durably for read-only queries before
687
+ // any degraded-mode narrowing below — `allowedToolNames` is the per-turn
688
+ // execution gate (cleared at teardown and restricted under disk pressure),
689
+ // whereas this snapshot answers "what tools does this conversation have".
690
+ ctx.lastResolvedToolNames = turnAllowed;
605
691
  if (ctx.diskPressureCleanupModeActive === true) {
606
692
  const cleanupDefs = allBaseDefs.filter((d) =>
607
693
  isDiskPressureCleanupToolName(d.name),
@@ -33,7 +33,6 @@ import {
33
33
  import { resolveCallSiteConfig } from "../config/llm-resolver.js";
34
34
  import { getConfig } from "../config/loader.js";
35
35
  import type { LLMCallSite, Speed } from "../config/schemas/llm.js";
36
- import type { CesClient } from "../credential-execution/client.js";
37
36
  import { EventBus } from "../events/bus.js";
38
37
  import type { AssistantDomainEvents } from "../events/domain-events.js";
39
38
  import { createToolAuditListener } from "../events/tool-audit-listener.js";
@@ -53,7 +52,7 @@ import {
53
52
  setConversationHistoryStrippedAt,
54
53
  } from "../memory/conversation-crud.js";
55
54
  import { ConversationGraphMemory } from "../memory/graph/conversation-graph-memory.js";
56
- import { shouldExposePersonalMemory } from "../memory/v2/static-context.js";
55
+ import { unwrapMemoryBlock, wrapMemoryBlock } from "../memory/memory-marker.js";
57
56
  import { PermissionPrompter } from "../permissions/prompter.js";
58
57
  import { SecretPrompter } from "../permissions/secret-prompter.js";
59
58
  import type { UserDecision } from "../permissions/types.js";
@@ -68,6 +67,11 @@ import {
68
67
  createContextSummaryMessage,
69
68
  } from "../plugins/defaults/compaction/window-manager.js";
70
69
  import { repairHistory } from "../plugins/defaults/history-repair/terminal.js";
70
+ import {
71
+ getPrunedSlugs,
72
+ MEMORY_V3_INJECTED_BLOCK_METADATA_KEY,
73
+ } from "../plugins/defaults/memory-v3-shadow/ever-injected-store.js";
74
+ import { filterPrunedCardSections } from "../plugins/defaults/memory-v3-shadow/prune.js";
71
75
  import {
72
76
  applyBootstrapTemplate,
73
77
  buildSystemPrompt,
@@ -129,6 +133,7 @@ import { MessageQueue } from "./conversation-queue-manager.js";
129
133
  import {
130
134
  type ChannelCapabilities,
131
135
  getSlackCompactionWatermarkForPrefix,
136
+ type InboundActorContext,
132
137
  loadSlackChronologicalContext,
133
138
  stripInjectionsForCompaction,
134
139
  } from "./conversation-runtime-assembly.js";
@@ -176,11 +181,10 @@ export type {
176
181
  QueueDrainReason,
177
182
  QueuePolicy,
178
183
  } from "./conversation-queue-manager.js";
179
- import { resolveTrustClass, type TrustContext } from "./trust-context.js";
184
+ import { isPersonalMemoryAllowed, type TrustContext } from "./trust-context.js";
180
185
 
181
186
  export interface ConversationConstructorOptions {
182
187
  maxTokens?: number;
183
- sharedCesClient?: CesClient;
184
188
  speedOverride?: Speed;
185
189
  cacheTtl?: "5m" | "1h";
186
190
  modelOverride?: string;
@@ -202,6 +206,15 @@ export class Conversation {
202
206
  /** @internal */ eventBus = new EventBus<AssistantDomainEvents>();
203
207
  /** @internal */ workingDir: string;
204
208
  /** @internal */ allowedToolNames?: Set<string>;
209
+ /**
210
+ * Durable copy of the full tool set resolved on the most recent turn,
211
+ * kept for read-only inventory queries. Unlike {@link allowedToolNames}
212
+ * — the per-turn execution gate the agent loop clears at turn teardown —
213
+ * this survives between turns so a query against an idle conversation
214
+ * still reports the skill/MCP tools it gained over its lifecycle.
215
+ * @internal
216
+ */
217
+ lastResolvedToolNames?: Set<string>;
205
218
  /** @internal */ diskPressureCleanupModeActive?: boolean;
206
219
  /** @internal */ toolsDisabledDepth = 0;
207
220
  /** @internal */ preactivatedSkillIds?: string[];
@@ -256,7 +269,6 @@ export class Conversation {
256
269
  * @internal
257
270
  */
258
271
  hostAppControlProxy?: HostAppControlProxy;
259
- /** @internal */ cesClient?: CesClient;
260
272
  /** @internal */ readonly queue = new MessageQueue();
261
273
  /** @internal */ currentActiveSurfaceId?: string;
262
274
  /** @internal */ currentPage?: string;
@@ -269,6 +281,18 @@ export class Conversation {
269
281
  * another actor's persona mid-turn.
270
282
  */
271
283
  /** @internal */ currentTurnTrustContext?: TrustContext;
284
+ /**
285
+ * The model-facing inbound actor context resolved once at turn start from
286
+ * {@link currentTurnTrustContext}. Frozen here because resolving it reads the
287
+ * live contact/member registry (member status/policy, contact notes,
288
+ * interaction count), which a contact tool or the guardian can mutate
289
+ * mid-turn; post-compaction re-injection reads this snapshot so it re-emits
290
+ * the actor context the turn's initial assembly saw rather than re-resolving
291
+ * against drifted registry state. `null` on guardian turns and when there is
292
+ * no trust context (the actor section is suppressed).
293
+ * @internal
294
+ */
295
+ currentTurnInboundActorContext?: InboundActorContext | null;
272
296
  /** @internal */ currentTurnChannelCapabilities?: ChannelCapabilities;
273
297
  /** @internal */ currentTurnOverrideProfile?: string;
274
298
  /** @internal */ toolRoutedProfile?: string;
@@ -477,13 +501,7 @@ export class Conversation {
477
501
  workingDir: string,
478
502
  options?: ConversationConstructorOptions,
479
503
  ) {
480
- const {
481
- maxTokens,
482
- sharedCesClient,
483
- speedOverride,
484
- cacheTtl,
485
- modelOverride,
486
- } = options ?? {};
504
+ const { maxTokens, speedOverride, cacheTtl, modelOverride } = options ?? {};
487
505
  this.conversationId = conversationId;
488
506
  this.systemPrompt = systemPrompt;
489
507
  this.provider = provider;
@@ -552,13 +570,6 @@ export class Conversation {
552
570
  const resolvedMainAgent = resolveCallSiteConfig("mainAgent", config.llm);
553
571
  this.streamThinking = resolvedMainAgent.thinking.streamThinking ?? false;
554
572
 
555
- // CES (Credential Execution Service) — use the shared server-level client.
556
- // The CES sidecar accepts exactly one bootstrap connection, so the
557
- // client is owned by DaemonServer and passed in here.
558
- if (sharedCesClient) {
559
- this.cesClient = sharedCesClient;
560
- }
561
-
562
573
  const resolveTools = createResolveToolsCallback(toolDefs, this);
563
574
 
564
575
  const configuredMaxTokens = maxTokens;
@@ -802,16 +813,30 @@ export class Conversation {
802
813
  preStrippedCount = boundary === -1 ? slicedDbMessages.length : boundary;
803
814
  }
804
815
 
805
- // Mirror the injection-time gate (`shouldExposePersonalMemory` in
806
- // `conversation-agent-loop.ts`) so background/local conversations
807
- // (sourceChannel `undefined` or `"vellum"`) can rehydrate the persisted
808
- // v2 static memory block. Use `resolveTrustClass` for parity with the
809
- // agent loop — it folds in the HTTP-auth-disabled dev bypass so
810
- // rehydration and injection agree on the effective trust class.
811
- const personalMemoryAllowed = shouldExposePersonalMemory({
812
- sourceChannel: this.trustContext?.sourceChannel,
813
- isTrustedActor: resolveTrustClass(this.trustContext) === "guardian",
814
- });
816
+ // The injection-time personal-memory gate, so background/local
817
+ // conversations (sourceChannel `undefined` or `"vellum"`) can rehydrate
818
+ // the persisted v2 static memory block. The shared helper folds in the
819
+ // HTTP-auth-disabled dev bypass so rehydration and injection agree on the
820
+ // effective trust class.
821
+ const personalMemoryAllowed = isPersonalMemoryAllowed(this.trustContext);
822
+ // Pruned v3 card slugs, read lazily on the first row that carries a v3
823
+ // block (most conversations carry none, so most loads never query). The
824
+ // prune valve marks cards pruned in the everInjected store instead of
825
+ // rewriting the persisted metadata, so the v3 rehydration splice below
826
+ // re-applies the filter on every load — that is what makes a prune
827
+ // survive daemon restarts. Defensive catch: a store failure degrades to
828
+ // an unfiltered (pre-prune) rehydration rather than a failed load.
829
+ let v3PrunedSlugsMemo: Set<string> | null = null;
830
+ const v3PrunedSlugs = (): Set<string> => {
831
+ if (v3PrunedSlugsMemo === null) {
832
+ try {
833
+ v3PrunedSlugsMemo = getPrunedSlugs(this.conversationId);
834
+ } catch {
835
+ v3PrunedSlugsMemo = new Set();
836
+ }
837
+ }
838
+ return v3PrunedSlugsMemo;
839
+ };
815
840
  const parsedMessages: Message[] = slicedDbMessages.map((m, index, arr) => {
816
841
  const isPreStripped = index < preStrippedCount;
817
842
  const role = m.role as "user" | "assistant";
@@ -842,18 +867,21 @@ export class Conversation {
842
867
  // so the resulting layout matches `applyRuntimeInjections`'s
843
868
  // after-memory-prefix splices in ascending injector order
844
869
  // (pkb-context 30, pkb-reminder 35, memory-v2-static 38,
845
- // now-md 40 — the v2 static block lands inside the memory
846
- // prefix, so now-md splices *after* it):
870
+ // now-md 40, memory-v3-shadow 1000 — the v2 static block lands
871
+ // inside the memory prefix, so now-md splices *after* it; the
872
+ // v3 card block is `<memory>`-wrapped and splices LAST, landing
873
+ // at the memory boundary after the `<info>` block but before
874
+ // now-md's earlier splice):
847
875
  // [<workspace>, <turn_context>, <memory>dynamic</memory>,
848
- // <info>v2static</info>, <NOW.md>, <system_reminder>,
849
- // <knowledge_base>, ...original]
876
+ // <info>v2static</info>, <memory>v3cards</memory>, <NOW.md>,
877
+ // <system_reminder>, <knowledge_base>, ...original]
850
878
  // The v2 static block is replayed verbatim from stored metadata,
851
879
  // so rows may carry either `<info>…</info>` or `<memory>…</memory>`
852
880
  // depending on when they were persisted.
853
881
  // Required so Anthropic's prefix cache keeps matching msg[0]
854
882
  // across daemon restart and conversation eviction. The tail
855
- // row only rehydrates `memoryInjectedBlock` — the next turn
856
- // re-injects the rest fresh.
883
+ // row only rehydrates `memoryInjectedBlock` and the v3 card
884
+ // block — the next turn re-injects the rest fresh.
857
885
  if (!isTail && typeof meta.pkbContextBlock === "string") {
858
886
  content = [
859
887
  { type: "text" as const, text: meta.pkbContextBlock },
@@ -875,10 +903,42 @@ export class Conversation {
875
903
  ];
876
904
  }
877
905
 
906
+ // The memory-v3 frozen card block (net-new compact cards) persists
907
+ // under its own key, stored UNWRAPPED like v2's dynamic block below.
908
+ // Rehydrated on ALL rows (tail included): the next turn injects only
909
+ // net-new cards — deduped via the v3 everInjected store — so this
910
+ // row's block must be back in history byte-identical for the dedup
911
+ // (and the provider prefix cache) to hold. A row carries at most one
912
+ // of the v3 and v2-dynamic keys (the user-prompt-submit hook
913
+ // persists them mutually exclusively). Spliced here — before the v2
914
+ // static and dynamic blocks — because prepends invert: executing
915
+ // first leaves it BELOW both in the final content, matching the
916
+ // live after-memory-prefix splice (order 1000 lands at the memory
917
+ // boundary, after `<info>` / `<memory>` prefix blocks).
918
+ // Pruned slugs' card sections are filtered out here (the metadata
919
+ // itself is never rewritten — auditable and reversible); an
920
+ // all-pruned block is skipped entirely, matching the live strip in
921
+ // `memory-v3-shadow/prune.ts`.
922
+ if (typeof meta[MEMORY_V3_INJECTED_BLOCK_METADATA_KEY] === "string") {
923
+ const v3Block = meta[
924
+ MEMORY_V3_INJECTED_BLOCK_METADATA_KEY
925
+ ] as string;
926
+ const v3Resident = filterPrunedCardSections(
927
+ unwrapMemoryBlock(v3Block),
928
+ v3PrunedSlugs(),
929
+ );
930
+ if (v3Resident.length > 0) {
931
+ content = [
932
+ { type: "text" as const, text: wrapMemoryBlock(v3Resident) },
933
+ ...content,
934
+ ];
935
+ }
936
+ }
937
+
878
938
  // The v2 static memory block (essentials/threads/recent/buffer
879
939
  // wrapped in either `<info>…</info>` or `<memory>…</memory>`)
880
940
  // carries personal user memory. Trust-gated to mirror
881
- // `shouldExposePersonalMemory` at injection time — untrusted-actor
941
+ // `isPersonalMemoryAllowed` at injection time — untrusted-actor
882
942
  // views must not read persisted personal memory back through
883
943
  // metadata. Skipped on the tail row because the next turn
884
944
  // re-injects fresh content on full-mode turns.
@@ -902,15 +962,12 @@ export class Conversation {
902
962
  // legitimate unwrapped payloads that happen to start with
903
963
  // "<memory>\n" or end with "\n</memory>".
904
964
  if (typeof meta.memoryInjectedBlock === "string") {
905
- const block = meta.memoryInjectedBlock;
906
- const inner =
907
- block.startsWith("<memory>\n") && block.endsWith("\n</memory>")
908
- ? block.slice("<memory>\n".length, -"\n</memory>".length)
909
- : block;
910
965
  content = [
911
966
  {
912
967
  type: "text" as const,
913
- text: `<memory>\n${inner}\n</memory>`,
968
+ text: wrapMemoryBlock(
969
+ unwrapMemoryBlock(meta.memoryInjectedBlock),
970
+ ),
914
971
  },
915
972
  ...content,
916
973
  ];
@@ -1044,14 +1101,13 @@ export class Conversation {
1044
1101
  async ensureActorScopedHistory(): Promise<void> {
1045
1102
  const currentTrustClass = this.trustContext?.trustClass;
1046
1103
  // `loadFromDb` gates personal-memory rehydration on `sourceChannel` too
1047
- // (via `shouldExposePersonalMemory`), so a same-trust-class reuse from a
1104
+ // (via `isPersonalMemoryAllowed`), so a same-trust-class reuse from a
1048
1105
  // different channel (e.g. internal `vellum` → remote channel) must also
1049
1106
  // trigger a reload. Otherwise stale personal-memory blocks can leak to
1050
1107
  // an untrusted remote turn, or be hidden when they should be present.
1051
- const currentPersonalMemoryAllowed = shouldExposePersonalMemory({
1052
- sourceChannel: this.trustContext?.sourceChannel,
1053
- isTrustedActor: resolveTrustClass(this.trustContext) === "guardian",
1054
- });
1108
+ const currentPersonalMemoryAllowed = isPersonalMemoryAllowed(
1109
+ this.trustContext,
1110
+ );
1055
1111
  if (
1056
1112
  this.loadedHistoryTrustClass === currentTrustClass &&
1057
1113
  this.loadedHistoryPersonalMemoryAllowed === currentPersonalMemoryAllowed
@@ -1207,9 +1263,6 @@ export class Conversation {
1207
1263
  this.hostCuProxy?.dispose();
1208
1264
  this.hostAppControlProxy?.dispose();
1209
1265
  this.hostAppControlProxy = undefined;
1210
- // CES client is owned by DaemonServer — just drop the reference.
1211
- // Do NOT close it here; the server manages the CES lifecycle.
1212
- this.cesClient = undefined;
1213
1266
  this.activeContextNodeIds = this.graphMemory.tracker.getActiveNodeIds();
1214
1267
  this.graphMemory.persistState();
1215
1268
  this.graphMemory.dispose();
@@ -1706,6 +1759,22 @@ export class Conversation {
1706
1759
  });
1707
1760
  }
1708
1761
 
1762
+ // ── Tools ────────────────────────────────────────────────────────
1763
+
1764
+ /**
1765
+ * The set of tool names available to this conversation as of its most
1766
+ * recent turn — including skill/MCP tools registered over the
1767
+ * conversation's lifecycle. Reads the durable {@link lastResolvedToolNames}
1768
+ * snapshot the `resolveTools` callback records each turn (which, unlike the
1769
+ * per-turn `allowedToolNames` gate, is not cleared at turn teardown); before
1770
+ * the first turn it falls back to the core tool set. This is a pure read: it
1771
+ * does not re-run `resolveTools`, which has registry/projection side effects
1772
+ * that must not fire outside a turn.
1773
+ */
1774
+ getRegisteredToolNames(): Set<string> {
1775
+ return new Set(this.lastResolvedToolNames ?? this.coreToolNames);
1776
+ }
1777
+
1709
1778
  // ── History ──────────────────────────────────────────────────────
1710
1779
 
1711
1780
  getMessages(): Message[] {