@vellumai/assistant 0.8.12 → 0.9.0-staging.2

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 (442) hide show
  1. package/AGENTS.md +0 -14
  2. package/ARCHITECTURE.md +45 -45
  3. package/README.md +1 -1
  4. package/bun.lock +200 -154
  5. package/docs/architecture/integrations.md +3 -3
  6. package/docs/architecture/memory.md +2 -2
  7. package/docs/architecture/security.md +10 -10
  8. package/docs/runbook-trusted-contacts.md +12 -12
  9. package/docs/skills.md +6 -6
  10. package/docs/workflows-testing.md +221 -0
  11. package/docs/workflows.md +510 -0
  12. package/examples/plugins/echo/README.md +5 -5
  13. package/knip.json +2 -0
  14. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +105 -0
  15. package/node_modules/@vellumai/gateway-client/src/index.ts +12 -0
  16. package/openapi.yaml +7197 -5708
  17. package/package.json +8 -4
  18. package/scripts/generate-openapi.ts +66 -114
  19. package/src/__tests__/access-request-seed-content-blocks.test.ts +213 -0
  20. package/src/__tests__/adaptive-thinking-repair.test.ts +32 -3
  21. package/src/__tests__/agent-loop-output-hooks.test.ts +183 -0
  22. package/src/__tests__/agent-loop-regrowth-guard.test.ts +506 -0
  23. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
  24. package/src/__tests__/agent-wake-override-profile.test.ts +77 -0
  25. package/src/__tests__/app-compiler.test.ts +7 -1
  26. package/src/__tests__/app-dir-path-guard.test.ts +27 -3
  27. package/src/__tests__/app-executors.test.ts +43 -0
  28. package/src/__tests__/approval-cascade.test.ts +0 -5
  29. package/src/__tests__/approval-routes-http.test.ts +91 -0
  30. package/src/__tests__/assistant-stream-state.test.ts +107 -0
  31. package/src/__tests__/browser-fill-credential.test.ts +3 -3
  32. package/src/__tests__/bundled-skill-retrieval-guard.test.ts +1 -1
  33. package/src/__tests__/compaction-events.test.ts +63 -7
  34. package/src/__tests__/compaction-trail-store.test.ts +74 -1
  35. package/src/__tests__/compaction.benchmark.test.ts +63 -41
  36. package/src/__tests__/compactor-low-watermark-cut.test.ts +349 -0
  37. package/src/__tests__/context-window-manager-compact-retry.test.ts +64 -0
  38. package/src/__tests__/conversation-abort-tool-results.test.ts +0 -5
  39. package/src/__tests__/conversation-confirmation-signals.test.ts +0 -5
  40. package/src/__tests__/conversation-history-web-search.test.ts +7 -0
  41. package/src/__tests__/conversation-process-callsite.test.ts +0 -5
  42. package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -5
  43. package/src/__tests__/conversation-queue.test.ts +0 -5
  44. package/src/__tests__/conversation-slash-queue.test.ts +0 -5
  45. package/src/__tests__/conversation-slash-unknown.test.ts +0 -5
  46. package/src/__tests__/conversation-speed-override.test.ts +0 -5
  47. package/src/__tests__/conversation-surfaces-data-persist.test.ts +97 -0
  48. package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
  49. package/src/__tests__/conversation-usage.test.ts +2 -0
  50. package/src/__tests__/conversation-workspace-injection.test.ts +0 -5
  51. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -5
  52. package/src/__tests__/credential-broker-browser-fill.test.ts +2 -2
  53. package/src/__tests__/credential-broker-server-use.test.ts +2 -2
  54. package/src/__tests__/credential-broker.test.ts +1 -1
  55. package/src/__tests__/credential-prompt-route.test.ts +417 -0
  56. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  57. package/src/__tests__/credential-vault.test.ts +37 -0
  58. package/src/__tests__/db-schedule-syntax-migration.test.ts +24 -0
  59. package/src/__tests__/dynamic-page-surface.test.ts +219 -0
  60. package/src/__tests__/empty-state-greeting-cache.test.ts +94 -0
  61. package/src/__tests__/gateway-flag-listener.test.ts +24 -7
  62. package/src/__tests__/guardian-action-sweep.test.ts +56 -219
  63. package/src/__tests__/guardian-routing-invariants.test.ts +138 -0
  64. package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
  65. package/src/__tests__/list-messages-hidden-metadata.test.ts +99 -0
  66. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +87 -1
  67. package/src/__tests__/llm-resolver.test.ts +115 -0
  68. package/src/__tests__/managed-profile-guard.test.ts +6 -5
  69. package/src/__tests__/max-tokens-continue-hook.test.ts +184 -0
  70. package/src/__tests__/media-generate-image.test.ts +20 -9
  71. package/src/__tests__/mock-gateway-ipc.ts +23 -0
  72. package/src/__tests__/model-intents.test.ts +1 -1
  73. package/src/__tests__/normalize-onboarding.test.ts +26 -0
  74. package/src/__tests__/notification-decision-strategy.test.ts +4 -2
  75. package/src/__tests__/notification-telegram-adapter.test.ts +21 -3
  76. package/src/__tests__/pending-interactions-resolved-event.test.ts +62 -0
  77. package/src/__tests__/post-turn-tool-result-truncation.test.ts +72 -18
  78. package/src/__tests__/require-fresh-approval.test.ts +425 -1
  79. package/src/__tests__/resolve-app-id.test.ts +56 -0
  80. package/src/__tests__/runtime-events-sse-parity.test.ts +2 -0
  81. package/src/__tests__/schedule-routes-workflow-validation.test.ts +408 -0
  82. package/src/__tests__/schedule-routes.test.ts +257 -4
  83. package/src/__tests__/schedule-store.test.ts +60 -0
  84. package/src/__tests__/schedule-tools.test.ts +247 -2
  85. package/src/__tests__/skill-execute-input.test.ts +85 -0
  86. package/src/__tests__/skill-secret-handling-guard.test.ts +21 -20
  87. package/src/__tests__/skills.test.ts +3 -3
  88. package/src/__tests__/slack-app-setup-skill-regression.test.ts +1 -1
  89. package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
  90. package/src/__tests__/subagent-tool-gate-mode.test.ts +547 -0
  91. package/src/__tests__/system-prompt.test.ts +1 -1
  92. package/src/__tests__/task-progress-nudge-hook.test.ts +372 -0
  93. package/src/__tests__/task-scheduler.test.ts +299 -0
  94. package/src/__tests__/tool-approval-seed-content-blocks.test.ts +209 -0
  95. package/src/__tests__/tool-result-spool.test.ts +3 -1
  96. package/src/__tests__/workspace-migration-102-preserve-heartbeat-enabled-for-existing-workspaces.test.ts +181 -0
  97. package/src/__tests__/workspace-migration-103-upgrade-quality-profile-to-opus-4-8.test.ts +174 -0
  98. package/src/agent/compaction-circuit.ts +11 -0
  99. package/src/agent/loop.ts +181 -12
  100. package/src/api/constants/call-sites.ts +12 -0
  101. package/src/api/events/assistant-thinking-delta.ts +10 -0
  102. package/src/api/events/usage-update.ts +7 -0
  103. package/src/api/index.ts +4 -1
  104. package/src/api/responses/memory-v3-selection-log.ts +18 -11
  105. package/src/approvals/approval-primitive.ts +2 -2
  106. package/src/background-wake/background-wake-routes.test.ts +5 -2
  107. package/src/bundler/compiler-tools.ts +1 -1
  108. package/src/bundler/package-resolver.ts +0 -1
  109. package/src/calls/call-domain.ts +1 -1
  110. package/src/calls/guardian-action-sweep.ts +16 -93
  111. package/src/cli/AGENTS.md +4 -0
  112. package/src/cli/commands/__tests__/schedules.test.ts +430 -1
  113. package/src/cli/commands/credentials.ts +28 -24
  114. package/src/cli/commands/image-generation.ts +23 -9
  115. package/src/cli/commands/notifications.ts +1 -1
  116. package/src/cli/commands/plugins.ts +89 -46
  117. package/src/cli/commands/schedules.ts +384 -11
  118. package/src/cli/lib/__tests__/inspect-plugin.test.ts +69 -5
  119. package/src/cli/lib/__tests__/install-from-github.test.ts +15 -0
  120. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +81 -4
  121. package/src/cli/lib/inspect-plugin.ts +62 -1
  122. package/src/cli/lib/install-from-github.ts +52 -5
  123. package/src/cli/lib/upgrade-plugin.ts +18 -0
  124. package/src/config/__tests__/workflows-schema.test.ts +60 -0
  125. package/src/config/bundled-skills/acp/SKILL.md +2 -2
  126. package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
  127. package/src/config/bundled-skills/app-builder/tools/app-create.ts +6 -1
  128. package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +7 -1
  129. package/src/config/bundled-skills/app-builder/tools/app-refresh.ts +7 -1
  130. package/src/config/bundled-skills/app-builder/tools/app-update.ts +10 -1
  131. package/src/config/bundled-skills/image-studio/SKILL.md +66 -19
  132. package/src/config/bundled-skills/image-studio/TOOLS.json +1 -6
  133. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +22 -3
  134. package/src/config/bundled-skills/personal-page/SKILL.md +57 -0
  135. package/src/config/bundled-skills/personal-page/TOOLS.json +27 -0
  136. package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +17 -0
  137. package/src/config/bundled-skills/schedule/SKILL.md +7 -2
  138. package/src/config/bundled-skills/schedule/TOOLS.json +48 -4
  139. package/src/config/bundled-skills/workflows/SKILL.md +214 -0
  140. package/src/config/bundled-skills/workflows/TOOLS.json +84 -0
  141. package/src/config/bundled-skills/workflows/tools/manage-workflows.ts +12 -0
  142. package/src/config/bundled-skills/workflows/tools/run-workflow.ts +12 -0
  143. package/src/config/bundled-tool-registry.ts +14 -2
  144. package/src/config/call-site-defaults.ts +5 -0
  145. package/src/config/feature-flag-registry.json +12 -4
  146. package/src/config/llm-context-resolution.ts +8 -0
  147. package/src/config/llm-resolver.ts +30 -0
  148. package/src/config/preloaded-apps/personal-page/src/components/About.tsx +22 -0
  149. package/src/config/preloaded-apps/personal-page/src/components/App.tsx +16 -0
  150. package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +77 -0
  151. package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +57 -0
  152. package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +28 -0
  153. package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +234 -0
  154. package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +48 -0
  155. package/src/config/preloaded-apps/personal-page/src/components/media.ts +16 -0
  156. package/src/config/preloaded-apps/personal-page/src/index.html +20 -0
  157. package/src/config/preloaded-apps/personal-page/src/main.tsx +7 -0
  158. package/src/config/preloaded-apps/personal-page/src/profile-data.ts +82 -0
  159. package/src/config/preloaded-apps/personal-page/src/styles.css +759 -0
  160. package/src/config/schema.ts +2 -0
  161. package/src/config/schemas/call-site-catalog.ts +7 -0
  162. package/src/config/schemas/heartbeat.ts +4 -1
  163. package/src/config/schemas/llm.ts +33 -26
  164. package/src/config/schemas/memory-retrospective.ts +19 -0
  165. package/src/config/schemas/platform.ts +8 -0
  166. package/src/config/schemas/services.ts +5 -2
  167. package/src/config/schemas/workflows.ts +42 -0
  168. package/src/config/skills.ts +3 -3
  169. package/src/context/compactor.ts +273 -39
  170. package/src/context/post-turn-tool-result-truncation.ts +23 -6
  171. package/src/context/tool-result-spool.ts +12 -17
  172. package/src/credential-execution/executable-discovery.ts +1 -1
  173. package/src/credential-execution/process-manager.ts +37 -3
  174. package/src/credential-execution/prompted-credential.ts +205 -0
  175. package/src/daemon/conversation-agent-loop-handlers.ts +14 -0
  176. package/src/daemon/conversation-process.ts +11 -2
  177. package/src/daemon/conversation-surfaces.ts +167 -3
  178. package/src/daemon/conversation-tool-setup.ts +103 -26
  179. package/src/daemon/conversation-usage.ts +2 -0
  180. package/src/daemon/conversation.ts +115 -11
  181. package/src/daemon/handlers/shared.ts +26 -14
  182. package/src/daemon/host-cu-proxy.ts +15 -12
  183. package/src/daemon/host-file-proxy.ts +15 -12
  184. package/src/daemon/host-transfer-proxy.ts +30 -24
  185. package/src/daemon/lifecycle.ts +40 -3
  186. package/src/daemon/message-protocol.ts +3 -0
  187. package/src/daemon/message-types/messages.ts +2 -10
  188. package/src/daemon/message-types/workflows.ts +49 -0
  189. package/src/daemon/parse-actual-tokens-from-error.test.ts +62 -1
  190. package/src/daemon/parse-actual-tokens-from-error.ts +43 -4
  191. package/src/daemon/process-message.ts +6 -0
  192. package/src/daemon/tool-setup-types.ts +57 -0
  193. package/src/daemon/wake-conversation-ops.ts +18 -0
  194. package/src/heartbeat/heartbeat-run-store.ts +8 -2
  195. package/src/home/feed-types.ts +1 -1
  196. package/src/ipc/gateway-flag-listener.ts +28 -6
  197. package/src/mcp/mcp-auth-state.ts +8 -20
  198. package/src/media/__tests__/image-models.test.ts +57 -0
  199. package/src/media/image-models.ts +66 -0
  200. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +38 -0
  201. package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +12 -2
  202. package/src/memory/__tests__/memory-retrospective-job.test.ts +911 -34
  203. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +227 -5
  204. package/src/memory/__tests__/memory-retrospective-state.test.ts +195 -0
  205. package/src/memory/__tests__/preloaded-apps.test.ts +85 -0
  206. package/src/memory/auto-analysis-enqueue.ts +14 -1
  207. package/src/memory/compaction-log-store-clickhouse.ts +6 -4
  208. package/src/memory/conversation-crud.ts +9 -2
  209. package/src/memory/conversation-disk-view.ts +1 -1
  210. package/src/memory/conversation-queries.ts +22 -7
  211. package/src/memory/db-init.ts +20 -0
  212. package/src/memory/db-maintenance.ts +16 -0
  213. package/src/memory/embedding-runtime-manager.ts +1 -1
  214. package/src/memory/llm-request-log-source-clickhouse.ts +112 -14
  215. package/src/memory/llm-request-log-source-local.ts +19 -1
  216. package/src/memory/llm-request-log-source.ts +35 -6
  217. package/src/memory/llm-request-log-store.ts +90 -2
  218. package/src/memory/memory-retrospective-constants.ts +9 -0
  219. package/src/memory/memory-retrospective-enqueue.ts +3 -6
  220. package/src/memory/memory-retrospective-fork-boundary.ts +94 -0
  221. package/src/memory/memory-retrospective-job.ts +500 -208
  222. package/src/memory/memory-retrospective-startup-cleanup.ts +97 -19
  223. package/src/memory/memory-retrospective-state.ts +85 -2
  224. package/src/memory/migrations/281-memory-retrospective-remembered-log.ts +40 -0
  225. package/src/memory/migrations/282-schedule-inference-profile.test.ts +77 -0
  226. package/src/memory/migrations/282-schedule-inference-profile.ts +26 -0
  227. package/src/memory/migrations/283-memory-v3-selections-message-id-and-sections.test.ts +102 -0
  228. package/src/memory/migrations/283-memory-v3-selections-message-id-and-sections.ts +53 -0
  229. package/src/memory/migrations/284-workflow-runs.ts +51 -0
  230. package/src/memory/migrations/285-schedule-workflow-mode.ts +26 -0
  231. package/src/memory/migrations/286-workflow-run-trust.ts +27 -0
  232. package/src/memory/migrations/287-conversation-origin-channel-index.ts +15 -0
  233. package/src/memory/migrations/288-backfill-origin-channel-from-bindings.ts +43 -0
  234. package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +115 -0
  235. package/src/memory/migrations/290-schedule-capabilities.test.ts +77 -0
  236. package/src/memory/migrations/290-schedule-capabilities.ts +25 -0
  237. package/src/memory/migrations/__tests__/281-memory-retrospective-remembered-log.test.ts +96 -0
  238. package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +571 -0
  239. package/src/memory/migrations/index.ts +10 -0
  240. package/src/memory/preloaded-apps.ts +116 -0
  241. package/src/memory/schema/infrastructure.ts +4 -0
  242. package/src/memory/schema/memory-core.ts +4 -0
  243. package/src/memory/v2/__tests__/concept-page-frontmatter-schema.test.ts +45 -0
  244. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +11 -7
  245. package/src/memory/v2/__tests__/page-store.test.ts +13 -2
  246. package/src/memory/v2/__tests__/qdrant.test.ts +24 -0
  247. package/src/memory/v2/frontmatter-sweep.ts +7 -6
  248. package/src/memory/v2/page-store.ts +4 -3
  249. package/src/memory/v2/qdrant.ts +42 -3
  250. package/src/memory/v2/types.ts +16 -10
  251. package/src/messaging/draft-store.ts +1 -1
  252. package/src/notifications/access-request-copy.ts +200 -113
  253. package/src/notifications/adapters/slack.ts +250 -111
  254. package/src/notifications/adapters/telegram.ts +7 -44
  255. package/src/notifications/approval-card-builder.ts +93 -0
  256. package/src/notifications/broadcaster.ts +74 -0
  257. package/src/notifications/conversation-pairing.ts +8 -6
  258. package/src/notifications/copy-composer.ts +32 -26
  259. package/src/notifications/decision-engine.ts +59 -7
  260. package/src/notifications/guardian-question-mode.ts +145 -155
  261. package/src/notifications/home-feed-side-effect.ts +28 -11
  262. package/src/notifications/notification-utils.ts +66 -0
  263. package/src/notifications/signal.ts +6 -0
  264. package/src/notifications/tool-approval-copy.ts +142 -0
  265. package/src/notifications/types.ts +19 -0
  266. package/src/permissions/threshold.ts +11 -0
  267. package/src/plugin-api/types.ts +16 -4
  268. package/src/plugins/defaults/compaction/window-manager.ts +44 -0
  269. package/src/plugins/defaults/index.ts +46 -0
  270. package/src/plugins/defaults/max-tokens-continue/continue-state-store.ts +53 -0
  271. package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +80 -0
  272. package/src/plugins/defaults/max-tokens-continue/hooks/stop.ts +20 -0
  273. package/src/plugins/defaults/max-tokens-continue/package.json +14 -0
  274. package/src/plugins/defaults/memory-retrieval/hooks/__tests__/user-prompt-submit.test.ts +37 -0
  275. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +29 -1
  276. package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +8 -3
  277. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +4 -2
  278. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +28 -18
  279. package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +67 -0
  280. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +122 -22
  281. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +2 -0
  282. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +63 -1
  283. package/src/plugins/defaults/memory-v3-shadow/injector.ts +61 -18
  284. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +1 -1
  285. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +39 -10
  286. package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +34 -1
  287. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +112 -47
  288. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +78 -15
  289. package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +206 -0
  290. package/src/plugins/defaults/task-progress-nudge/package.json +15 -0
  291. package/src/prompts/__tests__/system-prompt.test.ts +100 -1
  292. package/src/prompts/__tests__/task-progress-hint-section.test.ts +5 -7
  293. package/src/prompts/normalize-onboarding.ts +2 -0
  294. package/src/prompts/persona-resolver.ts +3 -0
  295. package/src/prompts/system-prompt.ts +51 -2
  296. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +3 -1
  297. package/src/prompts/templates/system-sections.ts +8 -3
  298. package/src/providers/call-site-routing.ts +6 -3
  299. package/src/providers/fireworks/client.ts +3 -0
  300. package/src/providers/inference/auth.ts +52 -46
  301. package/src/providers/model-intents.ts +2 -2
  302. package/src/providers/openai/__tests__/coerce-object-args.test.ts +105 -0
  303. package/src/providers/openai/chat-completions-provider.ts +47 -9
  304. package/src/providers/openai/coerce-object-args.ts +104 -0
  305. package/src/providers/retry.ts +8 -5
  306. package/src/providers/types.ts +10 -0
  307. package/src/runtime/__tests__/agent-wake.test.ts +629 -7
  308. package/src/runtime/access-request-helper.ts +16 -9
  309. package/src/runtime/agent-wake.ts +302 -51
  310. package/src/runtime/assistant-stream-state.ts +141 -8
  311. package/src/runtime/background-job-runner.ts +9 -0
  312. package/src/runtime/channel-approval-types.ts +1 -0
  313. package/src/runtime/channel-invite-transports/telegram.ts +6 -5
  314. package/src/runtime/channel-invite-transports/voice.ts +2 -2
  315. package/src/runtime/channel-invite-types.ts +4 -2
  316. package/src/runtime/channel-retry-sweep.ts +19 -41
  317. package/src/runtime/finalize-event-delivery.ts +72 -0
  318. package/src/runtime/guardian-action-message-composer.ts +0 -54
  319. package/src/runtime/http-server.ts +6 -14
  320. package/src/runtime/http-types.ts +0 -1
  321. package/src/runtime/message-composer-types.ts +0 -9
  322. package/src/runtime/middleware/__tests__/rate-limiter.test.ts +63 -0
  323. package/src/runtime/middleware/auth.ts +27 -3
  324. package/src/runtime/middleware/rate-limiter.ts +28 -1
  325. package/src/runtime/migrations/vbundle-builder.ts +6 -5
  326. package/src/runtime/pending-interactions.ts +20 -1
  327. package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +232 -173
  328. package/src/runtime/routes/__tests__/plugins-routes.test.ts +18 -0
  329. package/src/runtime/routes/__tests__/retrospective-routes.test.ts +436 -0
  330. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +11 -0
  331. package/src/runtime/routes/approval-routes.ts +35 -8
  332. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -1
  333. package/src/runtime/routes/btw-routes.ts +37 -1
  334. package/src/runtime/routes/channel-delivery-routes.ts +11 -7
  335. package/src/runtime/routes/channel-route-definitions.ts +3 -0
  336. package/src/runtime/routes/channel-route-shared.ts +3 -1
  337. package/src/runtime/routes/consolidation-routes.ts +17 -13
  338. package/src/runtime/routes/conversation-compaction-routes.ts +159 -119
  339. package/src/runtime/routes/conversation-list-routes.ts +41 -4
  340. package/src/runtime/routes/conversation-query-routes.ts +196 -11
  341. package/src/runtime/routes/conversation-routes.ts +15 -1
  342. package/src/runtime/routes/credential-prompt-routes.ts +39 -17
  343. package/src/runtime/routes/empty-state-greeting-cache.ts +65 -0
  344. package/src/runtime/routes/heartbeat-routes.ts +18 -13
  345. package/src/runtime/routes/image-generation-routes.ts +20 -2
  346. package/src/runtime/routes/inbound-message-handler.ts +13 -12
  347. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +32 -31
  348. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +7 -5
  349. package/src/runtime/routes/inbound-stages/background-dispatch.ts +7 -21
  350. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +5 -5
  351. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +6 -15
  352. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +1 -1
  353. package/src/runtime/routes/index.ts +6 -0
  354. package/src/runtime/routes/log-export/AGENTS.md +1 -1
  355. package/src/runtime/routes/log-export/workspace-allowlist.ts +1 -1
  356. package/src/runtime/routes/migration-routes.ts +5 -9
  357. package/src/runtime/routes/plugins-routes.ts +26 -0
  358. package/src/runtime/routes/ps-routes.ts +10 -8
  359. package/src/runtime/routes/retrospective-routes.ts +235 -0
  360. package/src/runtime/routes/runs-pagination.ts +75 -0
  361. package/src/runtime/routes/schedule-routes.ts +367 -59
  362. package/src/runtime/routes/sounds-config-routes.ts +239 -0
  363. package/src/runtime/routes/surface-action-routes.ts +84 -4
  364. package/src/runtime/routes/workflow-routes.test.ts +372 -0
  365. package/src/runtime/routes/workflow-routes.ts +363 -0
  366. package/src/runtime/routes/workspace-routes.test.ts +61 -1
  367. package/src/runtime/routes/workspace-routes.ts +26 -1
  368. package/src/runtime/services/__tests__/analyze-conversation.test.ts +38 -0
  369. package/src/runtime/services/analyze-conversation.ts +26 -13
  370. package/src/schedule/inference-profile.ts +28 -0
  371. package/src/schedule/schedule-store.ts +152 -4
  372. package/src/schedule/scheduler-types.ts +6 -0
  373. package/src/schedule/scheduler.ts +96 -0
  374. package/src/security/secret-allowlist.ts +1 -1
  375. package/src/skills/path-classifier.ts +1 -1
  376. package/src/tools/apps/executors.ts +23 -0
  377. package/src/tools/apps/resolve-app-id.ts +42 -0
  378. package/src/tools/browser/browser-execution.ts +9 -11
  379. package/src/tools/credentials/broker.ts +4 -4
  380. package/src/tools/credentials/vault.ts +26 -137
  381. package/src/tools/executor.ts +69 -0
  382. package/src/tools/flag-gated-tools.test.ts +76 -0
  383. package/src/tools/permission-checker.ts +8 -1
  384. package/src/tools/registry.ts +51 -0
  385. package/src/tools/schedule/create.ts +77 -1
  386. package/src/tools/schedule/list.ts +1 -0
  387. package/src/tools/schedule/update.ts +73 -1
  388. package/src/tools/skills/execute.ts +56 -0
  389. package/src/tools/terminal/shell.ts +1 -1
  390. package/src/tools/ui-surface/definitions.ts +91 -2
  391. package/src/tools/workflows/manage-workflows.ts +183 -0
  392. package/src/tools/workflows/run-workflow.test.ts +442 -0
  393. package/src/tools/workflows/run-workflow.ts +88 -0
  394. package/src/types/onboarding-context.ts +2 -0
  395. package/src/usage/attribution.ts +24 -0
  396. package/src/util/canonicalize-identity.ts +12 -3
  397. package/src/util/platform.ts +17 -17
  398. package/src/watcher/__tests__/engine.test.ts +24 -0
  399. package/src/watcher/__tests__/telemetry.test.ts +135 -0
  400. package/src/watcher/engine.ts +7 -0
  401. package/src/watcher/telemetry.ts +74 -0
  402. package/src/workflows/capabilities.test.ts +365 -0
  403. package/src/workflows/capabilities.ts +359 -0
  404. package/src/workflows/deterministic-stringify.ts +27 -0
  405. package/src/workflows/engine-integration.test.ts +656 -0
  406. package/src/workflows/engine.test.ts +1144 -0
  407. package/src/workflows/engine.ts +1078 -0
  408. package/src/workflows/fanout-load.test.ts +168 -0
  409. package/src/workflows/journal-store.test.ts +369 -0
  410. package/src/workflows/journal-store.ts +470 -0
  411. package/src/workflows/leaf-runner.test.ts +704 -0
  412. package/src/workflows/leaf-runner.ts +589 -0
  413. package/src/workflows/library.test.ts +134 -0
  414. package/src/workflows/library.ts +124 -0
  415. package/src/workflows/run-manager.test.ts +711 -0
  416. package/src/workflows/run-manager.ts +593 -0
  417. package/src/workflows/sandbox-escape.test.ts +339 -0
  418. package/src/workflows/sandbox.test.ts +251 -0
  419. package/src/workflows/sandbox.ts +447 -0
  420. package/src/workspace/adaptive-thinking-repair.ts +33 -11
  421. package/src/workspace/migrations/021-move-signals-to-workspace.ts +1 -1
  422. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +1 -1
  423. package/src/workspace/migrations/026-backfill-install-meta.ts +1 -1
  424. package/src/workspace/migrations/030-seed-pkb-autoinject.ts +2 -1
  425. package/src/workspace/migrations/031-drop-user-md.ts +1 -4
  426. package/src/workspace/migrations/048-remove-workspace-hooks.ts +1 -1
  427. package/src/workspace/migrations/056-release-notes-inference-profile-reordering.ts +5 -2
  428. package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +1 -1
  429. package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +8 -2
  430. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +41 -14
  431. package/src/workspace/migrations/102-preserve-heartbeat-enabled-for-existing-workspaces.ts +69 -0
  432. package/src/workspace/migrations/103-upgrade-quality-profile-to-opus-4-8.ts +83 -0
  433. package/src/workspace/migrations/104-recheck-adaptive-thinking-model-implied-anthropic.ts +133 -0
  434. package/src/workspace/migrations/registry.ts +6 -0
  435. package/src/workspace/migrations/runner.ts +1 -1
  436. package/tsconfig.json +1 -1
  437. package/src/__tests__/guardian-action-copy-generator.test.ts +0 -200
  438. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -579
  439. package/src/__tests__/guardian-action-store.test.ts +0 -106
  440. package/src/daemon/guardian-action-generators.ts +0 -71
  441. package/src/memory/guardian-action-store.ts +0 -484
  442. package/src/runtime/guardian-action-grant-minter.ts +0 -150
@@ -23,6 +23,7 @@ export const cronJobs = sqliteTable("cron_jobs", {
23
23
  maxRetries: integer("max_retries").notNull().default(3),
24
24
  retryBackoffMs: integer("retry_backoff_ms").notNull().default(60000),
25
25
  timeoutMs: integer("timeout_ms"), // script-mode execution timeout override (ms); null = use default
26
+ inferenceProfile: text("inference_profile"), // llm.profiles key for LLM-executed runs; null = default main-agent selection
26
27
  createdFromConversationId: text("created_from_conversation_id"),
27
28
  createdBy: text("created_by").notNull(), // 'agent' | 'user'
28
29
  mode: text("mode").notNull().default("execute"), // 'notify' | 'execute'
@@ -35,6 +36,9 @@ export const cronJobs = sqliteTable("cron_jobs", {
35
36
  .default(false), // reuse the same conversation across runs
36
37
  script: text("script"), // shell command for script mode (nullable, only used when mode = 'script')
37
38
  wakeConversationId: text("wake_conversation_id"), // target conversation for wake mode (nullable)
39
+ workflowName: text("workflow_name"), // saved workflow to trigger (nullable, only used when mode = 'workflow')
40
+ workflowArgsJson: text("workflow_args_json"), // JSON-encoded args passed to the workflow run (nullable)
41
+ capabilitiesJson: text("capabilities_json"), // JSON-encoded capability manifest for the run (nullable; null = hardcoded read-only manifest)
38
42
  createdAt: integer("created_at").notNull(),
39
43
  updatedAt: integer("updated_at").notNull(),
40
44
  });
@@ -106,6 +106,10 @@ export const memoryRetrospectiveState = sqliteTable(
106
106
  conversationId: text("conversation_id").primaryKey(),
107
107
  lastProcessedMessageId: text("last_processed_message_id").notNull(),
108
108
  lastRunAt: integer("last_run_at").notNull(),
109
+ // JSON array of strings — cumulative `remember` contents from prior
110
+ // retrospective passes (capped; see memory-retrospective-state.ts).
111
+ // NULL for rows that predate migration 281 or have no saves yet.
112
+ rememberedLog: text("remembered_log"),
109
113
  },
110
114
  );
111
115
 
@@ -0,0 +1,45 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import { ConceptPageFrontmatterSchema } from "../types.js";
4
+
5
+ /**
6
+ * Regression: the frontmatter schema is `.passthrough()`, not `.strict()`.
7
+ *
8
+ * Migrated/converted corpora carry leaked source-page fields the article model
9
+ * does not define (`date`, `sources`, `world`, `outcome`, `as_of`, …). Under
10
+ * `.strict()` every such page threw in `readPage()` and was silently dropped
11
+ * from BOTH the page index and the section dense lane (a bulk deploy of
12
+ * summary-less wiki pages lost ~45% of them this way). `.passthrough()` keeps
13
+ * them on disk and in the index; the frontmatter sweep still surfaces malformed
14
+ * pages.
15
+ */
16
+ describe("ConceptPageFrontmatterSchema", () => {
17
+ test("tolerates unknown frontmatter keys instead of throwing (passthrough)", () => {
18
+ const raw = {
19
+ summary: "A page about something.",
20
+ date: "2026-06-13",
21
+ sources: ["source-a", "source-b"],
22
+ world: "example-world",
23
+ outcome: "resolved",
24
+ as_of: "2026-06-13",
25
+ };
26
+ expect(() => ConceptPageFrontmatterSchema.parse(raw)).not.toThrow();
27
+ const parsed = ConceptPageFrontmatterSchema.parse(raw) as Record<
28
+ string,
29
+ unknown
30
+ >;
31
+ // Declared field is typed/kept...
32
+ expect(parsed.summary).toBe("A page about something.");
33
+ // ...and unknown keys pass through rather than being stripped or rejected.
34
+ expect(parsed.date).toBe("2026-06-13");
35
+ expect(parsed.sources).toEqual(["source-a", "source-b"]);
36
+ expect(parsed.outcome).toBe("resolved");
37
+ });
38
+
39
+ test("still applies declared-field defaults", () => {
40
+ const parsed = ConceptPageFrontmatterSchema.parse({});
41
+ expect(parsed.edges).toEqual([]);
42
+ expect(parsed.ref_files).toEqual([]);
43
+ expect(parsed.ref_urls).toEqual([]);
44
+ });
45
+ });
@@ -4,8 +4,8 @@
4
4
  * Coverage:
5
5
  * - v2 disabled → returns early, no warns, no throw.
6
6
  * - Empty workspace → no warns, no throw.
7
- * - One bad page (unknown frontmatter key) exactly one warn carrying
8
- * `errCode: "unrecognized_keys"` and the offending slug.
7
+ * - One bad page (malformed frontmatter wrong type on a declared field) →
8
+ * exactly one warn carrying `errCode: "invalid_type"` and the offending slug.
9
9
  * - Two bad + one good page → two warns; good page produces nothing.
10
10
  * - Malformed YAML → a warn surfaces; the sweep does not crash.
11
11
  */
@@ -59,7 +59,11 @@ function makeWorkspace(pages: Record<string, string>): string {
59
59
  }
60
60
 
61
61
  const goodPage = `---\nedges: []\nref_files: []\n---\nbody\n`;
62
- const badPage = `---\nedges: []\nref_files: []\nbogus_field: 1\n---\nbody\n`;
62
+ // Malformed: `edges` is a declared `z.array(z.string())` field, so a scalar
63
+ // value fails the schema with `invalid_type`. (An *unknown* key would NOT be
64
+ // bad — the schema is `.passthrough()`, so unknown keys are tolerated; the
65
+ // sweep only surfaces genuinely malformed pages that `readPage` would throw on.)
66
+ const badPage = `---\nedges: not-a-list\nref_files: []\n---\nbody\n`;
63
67
 
64
68
  describe("sweepConceptPageFrontmatter", () => {
65
69
  beforeEach(() => {
@@ -86,14 +90,14 @@ describe("sweepConceptPageFrontmatter", () => {
86
90
  }
87
91
  });
88
92
 
89
- test("one bad page emits exactly one unrecognized_keys warn", async () => {
93
+ test("one bad page emits exactly one invalid_type warn", async () => {
90
94
  const dir = makeWorkspace({ "bad-one": badPage });
91
95
  try {
92
96
  await sweepConceptPageFrontmatter(v2On, dir);
93
97
  expect(warnCalls).toHaveLength(1);
94
98
  expect(warnCalls[0].data.slug).toBe("bad-one");
95
- expect(warnCalls[0].data.errCode).toBe("unrecognized_keys");
96
- expect(warnCalls[0].data.errKeys).toEqual(["bogus_field"]);
99
+ expect(warnCalls[0].data.errCode).toBe("invalid_type");
100
+ expect(warnCalls[0].data.errPath).toEqual(["edges"]);
97
101
  } finally {
98
102
  rmSync(dir, { recursive: true, force: true });
99
103
  }
@@ -110,7 +114,7 @@ describe("sweepConceptPageFrontmatter", () => {
110
114
  const slugs = warnCalls.map((c) => c.data.slug).sort();
111
115
  expect(slugs).toEqual(["bad-a", "bad-b"]);
112
116
  for (const call of warnCalls) {
113
- expect(call.data.errCode).toBe("unrecognized_keys");
117
+ expect(call.data.errCode).toBe("invalid_type");
114
118
  }
115
119
  } finally {
116
120
  rmSync(dir, { recursive: true, force: true });
@@ -293,7 +293,11 @@ describe("writePage + readPage round-trip", () => {
293
293
  expect(read!.body).toBe(body);
294
294
  });
295
295
 
296
- test("readPage throws on unknown frontmatter keys instead of silently dropping them", async () => {
296
+ test("readPage tolerates unknown frontmatter keys and passes them through", async () => {
297
+ // The schema is `.passthrough()`, not `.strict()`: migrated/converted
298
+ // corpora carry leaked source-page fields the article model doesn't define.
299
+ // Unknown keys must pass through and stay on disk (loss-proof) rather than
300
+ // throwing and silently dropping the page from the index.
297
301
  const slug = "extra-keys";
298
302
  const raw =
299
303
  "---\nedges: []\nref_files: []\nunknown_field: oops\n---\nbody\n";
@@ -303,7 +307,14 @@ describe("writePage + readPage round-trip", () => {
303
307
  "utf-8",
304
308
  );
305
309
 
306
- await expect(readPage(workspaceDir, slug)).rejects.toThrow();
310
+ const read = await readPage(workspaceDir, slug);
311
+ expect(read).not.toBeNull();
312
+ expect(read!.frontmatter.edges).toEqual([]);
313
+ // Unknown key survives the round-trip instead of being rejected or stripped.
314
+ expect((read!.frontmatter as Record<string, unknown>).unknown_field).toBe(
315
+ "oops",
316
+ );
317
+ expect(read!.body).toBe("body\n");
307
318
  });
308
319
 
309
320
  test("writePage overwrites an existing page", async () => {
@@ -377,6 +377,30 @@ describe("memory v2 qdrant — collection lifecycle", () => {
377
377
  expect(params.sparse_vectors.summary_sparse).toBeDefined();
378
378
  });
379
379
 
380
+ test("detects a wrong-dimension dense vector on an existing collection and recreates", async () => {
381
+ // All required named vectors PRESENT, but the dense channels are sized to a
382
+ // different embedding dimension than the configured 384 (e.g. a 768-dim
383
+ // collection from a prior embedding model). Every upsert would fail with
384
+ // HTTP 400 until the collection is recreated — the missing-vector check
385
+ // alone never catches this.
386
+ state.collectionExistsBeforeCreate = true;
387
+ state.getCollectionInfo = {
388
+ config: {
389
+ params: {
390
+ vectors: { dense: { size: 768 }, summary_dense: { size: 768 } },
391
+ sparse_vectors: { sparse: {}, summary_sparse: {} },
392
+ },
393
+ },
394
+ };
395
+
396
+ const result = await ensureConceptPageCollection();
397
+
398
+ expect(state.getCollectionCalls).toBe(1);
399
+ expect(state.deleteCollectionCalls).toEqual([MEMORY_V2_COLLECTION]);
400
+ expect(state.createCollectionCalls).toBe(1);
401
+ expect(result).toEqual({ migrated: true });
402
+ });
403
+
380
404
  test("leaves a fully migrated collection untouched", async () => {
381
405
  // Default `getCollectionInfo` is FULL_SCHEMA_INFO — already migrated.
382
406
  state.collectionExistsBeforeCreate = true;
@@ -3,12 +3,13 @@
3
3
  // ---------------------------------------------------------------------------
4
4
  //
5
5
  // At daemon startup, walk every concept page on disk and validate its
6
- // frontmatter against `ConceptPageFrontmatterSchema` (which is `.strict()`).
7
- // Schema-drifted pages e.g. a user-authored file that adds a frontmatter
8
- // key the schema doesn't allow would otherwise stay invisible until the
9
- // page lands in a conversation's top-K and `renderInjectionBlock`'s
10
- // `Promise.all` rejects, silently no-op'ing V2 dynamic injection for the
11
- // whole turn. Surfacing them as `warn` log lines at boot turns that into a
6
+ // frontmatter against `ConceptPageFrontmatterSchema`. The schema is
7
+ // `.passthrough()`, so unknown keys are tolerated; what this sweep catches is
8
+ // genuinely malformed frontmatter a wrong type on a declared field, or YAML
9
+ // that doesn't parse. Such pages would otherwise stay invisible until one lands
10
+ // in a conversation's top-K and `renderInjectionBlock`'s `Promise.all` rejects
11
+ // (readPage throws), silently no-op'ing V2 dynamic injection for the whole
12
+ // turn. Surfacing them as `warn` log lines at boot turns that into a
12
13
  // debuggable signal.
13
14
  //
14
15
  // This sweep is intentionally separate from `rebuildConceptPageCorpusStats`:
@@ -192,9 +192,10 @@ export function slugFromConceptPath(
192
192
  /**
193
193
  * Split raw file contents into (frontmatter, body). If no frontmatter block
194
194
  * is present the entire input is treated as body and an empty frontmatter
195
- * block is returned (validated by `ConceptPageFrontmatterSchema` so any
196
- * unexpected shape bad types, extra junk — surfaces as a parse error to
197
- * the caller, not silent dropped data).
195
+ * block is returned (validated by `ConceptPageFrontmatterSchema` so a bad
196
+ * type on a declared field surfaces as a parse error to the caller, not
197
+ * silently dropped data). The schema is `.passthrough()`, so unknown keys are
198
+ * kept rather than rejected — migrated corpora carry leaked source-page fields.
198
199
  *
199
200
  * The schema's defaults guarantee `edges` and `ref_files` are always arrays
200
201
  * even on freshly created pages with empty frontmatter.
@@ -199,7 +199,8 @@ async function ensureConceptPageCollectionOnce(): Promise<{
199
199
  }
200
200
 
201
201
  const missing = missingNamedVectors(info);
202
- if (missing.length === 0) {
202
+ const wrongSize = wrongSizeNamedVectors(info, vectorSize);
203
+ if (missing.length === 0 && wrongSize.length === 0) {
203
204
  // Long-lived installs may predate the `kind` payload index; ensure
204
205
  // every required index exists before declaring the collection ready.
205
206
  await ensurePayloadIndexes();
@@ -208,8 +209,12 @@ async function ensureConceptPageCollectionOnce(): Promise<{
208
209
  }
209
210
 
210
211
  log.warn(
211
- { collection: MEMORY_V2_COLLECTION, missingNamedVectors: missing },
212
- "Memory v2 concept-page collection schema drift detected — deleting and recreating; embeddings will be regenerated by background reembed",
212
+ {
213
+ collection: MEMORY_V2_COLLECTION,
214
+ missingNamedVectors: missing,
215
+ wrongSizeNamedVectors: wrongSize,
216
+ },
217
+ "Memory v2 concept-page collection schema/dimension drift detected — deleting and recreating; embeddings will be regenerated by background reembed",
213
218
  );
214
219
  // Persist the reembed intent BEFORE the destructive delete so a crash
215
220
  // (or transient createCollection failure) between delete and recreate
@@ -366,6 +371,40 @@ function missingNamedVectors(
366
371
  return missing;
367
372
  }
368
373
 
374
+ /**
375
+ * Return the names of required dense named vectors that are PRESENT but sized
376
+ * to a different dimension than `expectedSize`. A collection created under a
377
+ * different embedding model (e.g. a 384-dim collection now serving a 3072-dim
378
+ * embedder) has all its named vectors but at the wrong dimension — every upsert
379
+ * then fails with HTTP 400, and the {@link missingNamedVectors} check alone
380
+ * never detects it. Combined with that check, this triggers the same
381
+ * destructive recreate + reembed path.
382
+ *
383
+ * Only dense vectors carry an explicit `size`; sparse vectors are dimensionless
384
+ * and are not checked. A single unnamed-vector shape (top-level `size`) or an
385
+ * unparseable response returns `[]`.
386
+ */
387
+ function wrongSizeNamedVectors(
388
+ info: Awaited<ReturnType<QdrantRestClient["getCollection"]>>,
389
+ expectedSize: number,
390
+ ): string[] {
391
+ const dense = info.config?.params?.vectors;
392
+ // Named-vectors map only — mirror the `!("size" in dense)` guard in
393
+ // `missingNamedVectors`. A single unnamed vector exposes `size` at the top
394
+ // level; its dimension is the collection's and is handled by the caller.
395
+ if (!dense || typeof dense !== "object" || "size" in dense) return [];
396
+ const map = dense as Record<string, unknown>;
397
+ const wrong: string[] = [];
398
+ for (const name of REQUIRED_DENSE_VECTORS) {
399
+ const entry = map[name];
400
+ if (entry && typeof entry === "object" && "size" in entry) {
401
+ const size = (entry as { size?: unknown }).size;
402
+ if (typeof size === "number" && size !== expectedSize) wrong.push(name);
403
+ }
404
+ }
405
+ return wrong;
406
+ }
407
+
369
408
  /**
370
409
  * Upsert a concept page's dense + sparse embedding. The point ID is derived
371
410
  * deterministically from the slug so subsequent calls for the same slug
@@ -42,17 +42,15 @@ export const ConceptPageFrontmatterSchema = z
42
42
  summary: z.string().optional(),
43
43
  leaves: z.array(z.string()).optional(),
44
44
  // Optional authored `"<target-slug> — <why>"` cross-links. Curated, first-class
45
- // edges for the memory-v3 edge lane. Declared here so `.strict()` does not
46
- // reject a page that uses `links:`, and so `renderPageContent` round-trips the
47
- // field (the edge graph reads it back from the rendered frontmatter).
45
+ // edges for the memory-v3 edge lane. Declared (rather than left to the
46
+ // `.passthrough()` catchall below) so it carries a real type and so
47
+ // `renderPageContent` round-trips the field (the edge graph reads it back
48
+ // from the rendered frontmatter).
48
49
  links: z.array(z.string()).optional(),
49
50
  // 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`).
51
+ // teaches and migrated corpora arrive in. Declared (not just tolerated by
52
+ // the catchall) so `renderPageContent` round-trips them a programmatic
53
+ // rewrite must not strip a page's `status:` draft marker or `title`.
56
54
  // `kind` and `status` stay free-form strings: their known values today
57
55
  // ("index", "cc-draft") are conventions of the article model, not
58
56
  // invariants this storage layer should enforce.
@@ -68,7 +66,15 @@ export const ConceptPageFrontmatterSchema = z
68
66
  // from `status:` (the article-model draft marker, e.g. "cc-draft").
69
67
  current: z.string().optional(),
70
68
  })
71
- .strict();
69
+ // `.passthrough()`, NOT `.strict()`: tolerate unknown frontmatter keys instead
70
+ // of throwing. Migrated/converted corpora carry leaked source-page fields the
71
+ // article model does not define (`date`, `sources`, `world`, `outcome`,
72
+ // `as_of`, …). Under `.strict()` every such page failed `readPage()` and was
73
+ // silently dropped from BOTH the page index and the section dense lane — a far
74
+ // worse failure than the schema drift strictness guarded against (a bulk
75
+ // wiki deploy lost ~45% of pages this way). Unknown keys pass through and stay
76
+ // on disk (loss-proof); the frontmatter sweep still surfaces malformed pages.
77
+ .passthrough();
72
78
 
73
79
  export type ConceptPageFrontmatter = z.infer<
74
80
  typeof ConceptPageFrontmatterSchema
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Local draft storage for messaging.
3
3
  *
4
- * Stores drafts at ~/.vellum/workspace/data/drafts/<platform>/<id>.json
4
+ * Stores drafts at $VELLUM_WORKSPACE_DIR/data/drafts/<platform>/<id>.json
5
5
  * Works across all platforms — Slack, Gmail, Discord, etc.
6
6
  */
7
7