total-recall-brain 3.44.0 → 3.46.0

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 (502) hide show
  1. package/bin/total-recall.mjs +10 -6
  2. package/docs/ARCHITECTURE.md +10 -7
  3. package/docs/reference/cli-reference.md +13 -8
  4. package/frontend/dist/assets/index-BRYE1jmv.css +1 -0
  5. package/frontend/dist/assets/index-D1goIPjC.js +5598 -0
  6. package/frontend/dist/index.html +2 -2
  7. package/frontend/dist/sw.js +5 -4
  8. package/frontend/src/App.tsx +185 -238
  9. package/frontend/src/api/auth-inventory.ts +62 -0
  10. package/frontend/src/api/memory.ts +13 -8
  11. package/frontend/src/api/plugin-credentials.ts +23 -0
  12. package/frontend/src/api/plugins.ts +37 -53
  13. package/frontend/src/api/workspace-drawers.ts +97 -0
  14. package/frontend/src/components/BrainSelector.tsx +9 -3
  15. package/frontend/src/components/plugins/PluginPanel.tsx +83 -42
  16. package/frontend/src/components/plugins/PluginSettings.tsx +10 -20
  17. package/frontend/src/components/plugins/PluginWorkspace.tsx +367 -0
  18. package/frontend/src/components/plugins/ScopedPluginCommands.tsx +142 -0
  19. package/frontend/src/components/plugins/plugin-frame.ts +44 -33
  20. package/frontend/src/components/plugins/scoped-plugin-operations.ts +28 -0
  21. package/frontend/src/components/workspace/DrawerChat.tsx +110 -0
  22. package/frontend/src/components/workspace/DrawerLayout.tsx +196 -0
  23. package/frontend/src/components/workspace/DrawerMarkup.tsx +49 -0
  24. package/frontend/src/components/workspace/PluginChunkLibrary.tsx +92 -0
  25. package/frontend/src/components/workspace/PluginHome.tsx +76 -0
  26. package/frontend/src/components/workspace/WorkspaceDrawers.tsx +96 -0
  27. package/frontend/src/components/workspace/WorkspacePage.tsx +95 -0
  28. package/frontend/src/components/workspace/usePluginDesign.ts +38 -0
  29. package/frontend/src/index.css +409 -136
  30. package/frontend/src/pages/HelpPage.tsx +15 -46
  31. package/frontend/src/pages/MemoryPage.tsx +19 -36
  32. package/frontend/src/pages/SettingsPage.tsx +1 -1
  33. package/metadata.plugin.schema.json +53 -227
  34. package/package.json +1 -1
  35. package/scaffold/.agent/skills/plugin/SKILL.md +13 -11
  36. package/scaffold/.agent/skills/plugin/evals/README.md +14 -21
  37. package/scaffold/.agent/skills/plugin/evals/cases/01-fabricated-seed.mjs +15 -18
  38. package/scaffold/.agent/skills/plugin/evals/cases/02-reassuring-default.mjs +19 -24
  39. package/scaffold/.agent/skills/plugin/evals/cases/03-declared-template.mjs +12 -20
  40. package/scaffold/.agent/skills/plugin/evals/cases/04-home-collects-credential.mjs +35 -0
  41. package/scaffold/.agent/skills/plugin/evals/cases/05-host-leak-in-scaffold.mjs +8 -28
  42. package/scaffold/.agent/skills/plugin/evals/cases/06-correct-plugin.mjs +85 -47
  43. package/scaffold/.agent/skills/plugin/evals/run.mjs +17 -18
  44. package/scaffold/.agent/skills/plugin/prompts/05-credentials.md +4 -4
  45. package/scaffold/.agent/skills/plugin/prompts/07-scaffold.md +2 -2
  46. package/scaffold/.agent/skills/plugin/prompts/08-verify.md +4 -4
  47. package/scaffold/.agent/skills/plugin/references/anti-patterns.md +2 -2
  48. package/scaffold/.agent/skills/plugin/references/generative-home.md +38 -0
  49. package/scaffold/.agent/skills/plugin/references/scaffold.md +5 -5
  50. package/scaffold/.agent/skills/plugin/references/surfaces.md +13 -4
  51. package/scaffold/.agent/skills/plugin/scripts/record.mjs +75 -153
  52. package/scaffold/.agent/skills/plugin/subagents/plugin-reviewer.md +4 -4
  53. package/scaffold/.agent/skills/total-recall/plugins/auth/DESIGN.md +76 -0
  54. package/scaffold/.agent/skills/total-recall/plugins/auth/README.md +3 -1
  55. package/scaffold/.agent/skills/total-recall/plugins/auth/cli.mjs +20 -15
  56. package/scaffold/.agent/skills/total-recall/plugins/auth/package-lock.json +7 -7
  57. package/scaffold/.agent/skills/total-recall/plugins/auth/package.json +2 -1
  58. package/scaffold/.agent/skills/total-recall/plugins/auth/plugin.json +13 -51
  59. package/scaffold/.agent/skills/total-recall/plugins/auth/src/cli-secret.mjs +20 -1
  60. package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-store.mjs +12 -1
  61. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/access-overview.md +17 -0
  62. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/agent-login.md +20 -0
  63. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/baseline-audit.md +22 -0
  64. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/credential-repository-binding.md +17 -0
  65. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/credential-store-maintenance.md +17 -0
  66. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/dashboard-password.md +18 -0
  67. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/dashboard-sessions.md +18 -0
  68. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/endpoint-probe.md +23 -0
  69. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/google-sign-in.md +21 -0
  70. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/mesh-machines.md +16 -0
  71. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/passkey-enrollment.md +21 -0
  72. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/registered-brains.md +16 -0
  73. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/scoped-access-keys.md +17 -0
  74. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/secret-inventory.md +25 -0
  75. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/signing-key-rotation.md +22 -0
  76. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/home.js +375 -0
  77. package/scaffold/.agent/skills/total-recall/plugins/automations/BRICK.md +7 -0
  78. package/scaffold/.agent/skills/total-recall/plugins/automations/DESIGN.md +53 -0
  79. package/scaffold/.agent/skills/total-recall/plugins/automations/LICENSE +21 -0
  80. package/scaffold/.agent/skills/total-recall/plugins/automations/README.md +66 -0
  81. package/scaffold/.agent/skills/total-recall/plugins/automations/adapters.mjs +247 -0
  82. package/scaffold/.agent/skills/total-recall/plugins/automations/capability/index.mjs +16 -0
  83. package/scaffold/.agent/skills/total-recall/plugins/automations/cli.mjs +130 -0
  84. package/{src/core/plugin-cron.mjs → scaffold/.agent/skills/total-recall/plugins/automations/cron.mjs} +1 -3
  85. package/scaffold/.agent/skills/total-recall/plugins/automations/inventory.mjs +88 -0
  86. package/scaffold/.agent/skills/total-recall/plugins/automations/package-lock.json +7661 -0
  87. package/scaffold/.agent/skills/total-recall/plugins/automations/package.json +36 -0
  88. package/scaffold/.agent/skills/total-recall/plugins/automations/plugin.json +151 -0
  89. package/scaffold/.agent/skills/total-recall/plugins/automations/scheduler.mjs +246 -0
  90. package/scaffold/.agent/skills/total-recall/plugins/automations/ui/automations.js +2 -0
  91. package/scaffold/.agent/skills/total-recall/plugins/automations/ui/chunks/control-ledger.md +42 -0
  92. package/scaffold/.agent/skills/total-recall/plugins/automations/ui/chunks/local-inventory-triage.md +17 -0
  93. package/scaffold/.agent/skills/total-recall/plugins/automations/ui/chunks/pending-control.md +39 -0
  94. package/scaffold/.agent/skills/total-recall/plugins/automations/ui/chunks/run-evidence.md +41 -0
  95. package/scaffold/.agent/skills/total-recall/plugins/automations/ui/chunks/schedule-vs-run-evidence.md +17 -0
  96. package/scaffold/.agent/skills/total-recall/plugins/automations/ui/chunks/source-coverage.md +41 -0
  97. package/scaffold/.agent/skills/total-recall/plugins/automations/ui/home.js +278 -0
  98. package/scaffold/.agent/skills/total-recall/plugins/code-quality/DESIGN.md +53 -0
  99. package/scaffold/.agent/skills/total-recall/plugins/code-quality/cli.mjs +23 -0
  100. package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/run-core.mjs +5 -3
  101. package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/check.mjs +61 -7
  102. package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/report-data.mjs +40 -14
  103. package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/report.mjs +15 -40
  104. package/scaffold/.agent/skills/total-recall/plugins/code-quality/package-lock.json +7 -7
  105. package/scaffold/.agent/skills/total-recall/plugins/code-quality/package.json +2 -1
  106. package/scaffold/.agent/skills/total-recall/plugins/code-quality/plugin.json +40 -40
  107. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/config.md +15 -0
  108. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/detect.md +15 -0
  109. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/gate.md +15 -0
  110. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/init.md +15 -0
  111. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/report.md +17 -0
  112. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/run.md +17 -0
  113. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/snapshot.md +15 -0
  114. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/code-quality-report.mjs +55 -23
  115. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/home.mjs +214 -0
  116. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/DESIGN.md +146 -0
  117. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package-lock.json +7 -7
  118. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package.json +2 -1
  119. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/plugin.json +51 -34
  120. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/command.mjs +1 -1
  121. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/create.md +35 -0
  122. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/history.md +35 -0
  123. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/library.md +49 -0
  124. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/list.md +42 -0
  125. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/promote.md +40 -0
  126. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/read.md +25 -0
  127. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/remove.md +26 -0
  128. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/rollback.md +26 -0
  129. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/show.md +53 -0
  130. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/status.md +40 -0
  131. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/test-path.md +38 -0
  132. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/test.md +46 -0
  133. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/update.md +32 -0
  134. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/home.js +533 -0
  135. package/scaffold/.agent/skills/total-recall/plugins/embeddings/DESIGN.md +110 -0
  136. package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +7 -7
  137. package/scaffold/.agent/skills/total-recall/plugins/embeddings/package.json +2 -1
  138. package/scaffold/.agent/skills/total-recall/plugins/embeddings/plugin.json +13 -10
  139. package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/chunks/benchmark.md +49 -0
  140. package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/chunks/coverage.md +53 -0
  141. package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/chunks/models.md +43 -0
  142. package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/chunks/pull.md +41 -0
  143. package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/chunks/status.md +59 -0
  144. package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/home.js +353 -0
  145. package/scaffold/.agent/skills/total-recall/plugins/git/DESIGN.md +31 -0
  146. package/scaffold/.agent/skills/total-recall/plugins/git/README.md +136 -0
  147. package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/cli.mjs +140 -24
  148. package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/package-lock.json +9 -9
  149. package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/package.json +4 -2
  150. package/scaffold/.agent/skills/total-recall/plugins/git/plugin.json +151 -0
  151. package/scaffold/.agent/skills/total-recall/plugins/git/src/lib/context.mjs +94 -0
  152. package/scaffold/.agent/skills/total-recall/plugins/git/src/lib/phase3-spec-support.mjs +50 -0
  153. package/scaffold/.agent/skills/total-recall/plugins/git/src/lib/runtime.mjs +40 -0
  154. package/scaffold/.agent/skills/total-recall/plugins/git/src/lib/spec-support.mjs +51 -0
  155. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/_support.mjs +195 -0
  156. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/auth.mjs +201 -0
  157. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/bisect.mjs +40 -0
  158. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/cli-meta.mjs +159 -0
  159. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/commit-memory.mjs +54 -0
  160. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/commit.mjs +46 -0
  161. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/fleet.mjs +174 -0
  162. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/force-push.mjs +58 -0
  163. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/group.mjs +119 -0
  164. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/handoff.mjs +59 -0
  165. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/message.mjs +22 -0
  166. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/resolve.mjs +60 -0
  167. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/rules.mjs +81 -0
  168. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/secret-firewall.mjs +92 -0
  169. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/undo.mjs +156 -0
  170. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/unmerged.mjs +182 -0
  171. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/why.mjs +16 -0
  172. package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/change-comparison.md +27 -0
  173. package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/commit-identity.md +22 -0
  174. package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/create-worktree.md +25 -0
  175. package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/registered-worktrees.md +22 -0
  176. package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/remove-worktree.md +23 -0
  177. package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/repository-ledger.md +25 -0
  178. package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/unmerged-work.md +22 -0
  179. package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/upstream-divergence.md +17 -0
  180. package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/worktree-conflict-triage.md +17 -0
  181. package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/worktree-terminal.md +22 -0
  182. package/scaffold/.agent/skills/total-recall/plugins/git/ui/home.js +273 -0
  183. package/scaffold/.agent/skills/total-recall/plugins/mesh/DESIGN.md +35 -0
  184. package/scaffold/.agent/skills/total-recall/plugins/mesh/README.md +4 -5
  185. package/scaffold/.agent/skills/total-recall/plugins/mesh/cli.mjs +9 -3
  186. package/scaffold/.agent/skills/total-recall/plugins/mesh/package-lock.json +7 -7
  187. package/scaffold/.agent/skills/total-recall/plugins/mesh/package.json +2 -1
  188. package/scaffold/.agent/skills/total-recall/plugins/mesh/plugin.json +37 -39
  189. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/cli/mesh.mjs +5 -3
  190. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/active-device-dispatch.md +22 -0
  191. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/control-probe.md +25 -0
  192. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/control-server.md +22 -0
  193. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/credential-readiness.md +23 -0
  194. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/elected-leader.md +22 -0
  195. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/enrollment-key.md +22 -0
  196. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/file-receipt.md +25 -0
  197. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/fleet-execution.md +23 -0
  198. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/fleet-presence.md +23 -0
  199. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/interactive-ssh.md +22 -0
  200. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/local-enrollment.md +23 -0
  201. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/mesh-policy.md +23 -0
  202. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/network-atlas.md +25 -0
  203. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/node-roster.md +26 -0
  204. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/node-selection-proof.md +17 -0
  205. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/owner-observations.md +23 -0
  206. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/peer-path.md +23 -0
  207. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/pinned-leader.md +24 -0
  208. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/reachability-evidence-ladder.md +17 -0
  209. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/recorded-login.md +23 -0
  210. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/remote-execution.md +22 -0
  211. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/secret-grants.md +26 -0
  212. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/{card.js → home.js} +93 -22
  213. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/DESIGN.md +86 -0
  214. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/README.md +44 -9
  215. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/capability/BRICK.md +1 -1
  216. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/capability/index.mjs +18 -6
  217. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/cli.mjs +128 -24
  218. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package-lock.json +7 -7
  219. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package.json +3 -2
  220. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/plugin.json +188 -34
  221. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/skills/meta-harness/SKILL.md +17 -3
  222. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/adoption.mjs +2 -2
  223. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/alerts.mjs +3 -3
  224. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/cascade.mjs +92 -18
  225. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/chat.mjs +80 -0
  226. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/control.mjs +145 -0
  227. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/coordinator.mjs +42 -21
  228. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/dispatch-request.mjs +4 -2
  229. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/dispatch.mjs +33 -9
  230. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/documentation.mjs +55 -11
  231. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/executor.mjs +26 -4
  232. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/hermes.mjs +29 -1
  233. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/index.mjs +1 -1
  234. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/included-usage.mjs +1 -1
  235. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/jobs.mjs +84 -0
  236. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/learner.mjs +3 -12
  237. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/maintenance.mjs +17 -0
  238. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/mcp-server.mjs +228 -0
  239. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/process-manager.mjs +25 -2
  240. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/reasoning.mjs +1 -1
  241. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/remote-operation.mjs +5 -1
  242. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/review.mjs +34 -3
  243. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/reviews.mjs +25 -0
  244. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/router.mjs +1 -1
  245. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/claude.mjs +319 -0
  246. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/codex.mjs +387 -0
  247. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/result.mjs +67 -0
  248. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/resume.mjs +373 -0
  249. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/run.mjs +223 -0
  250. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session-jobs.mjs +72 -0
  251. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/setup.mjs +6 -3
  252. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/termination.mjs +16 -12
  253. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/threads.mjs +32 -1
  254. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/usage-queue.mjs +1 -1
  255. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/verify-ssss.mjs +3 -3
  256. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worker.mjs +285 -40
  257. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worktree.mjs +76 -9
  258. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/adapter-docs-freshness.md +16 -0
  259. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/chat-handoff.md +26 -0
  260. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/fleet-maintenance.md +21 -0
  261. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/harness-inventory.md +21 -0
  262. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/included-access.md +18 -0
  263. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/included-usage-verification.md +15 -0
  264. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/independent-review.md +17 -0
  265. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/managed-execution.md +20 -0
  266. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/official-documentation.md +18 -0
  267. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/quota-recovery.md +21 -0
  268. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/review-routing-decision.md +16 -0
  269. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/route-work.md +18 -0
  270. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/routing-evidence.md +18 -0
  271. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/run-ledger.md +18 -0
  272. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/home.js +198 -0
  273. package/scaffold/.agent/skills/total-recall/plugins/openwiki/DESIGN.md +75 -0
  274. package/scaffold/.agent/skills/total-recall/plugins/openwiki/README.md +2 -0
  275. package/scaffold/.agent/skills/total-recall/plugins/openwiki/cli.mjs +64 -32
  276. package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +7 -7
  277. package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +2 -1
  278. package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +10 -44
  279. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/generate.mjs +5 -4
  280. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/graph.mjs +3 -4
  281. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/ingest.mjs +23 -18
  282. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/mesh-vaults.mjs +8 -3
  283. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/refresh-diagnostics.mjs +44 -0
  284. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/refresh.mjs +123 -113
  285. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/search.mjs +3 -5
  286. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/token.mjs +21 -47
  287. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/upstream-watcher.mjs +17 -6
  288. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/auto-refresh.md +18 -0
  289. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/bootstrap-pages.md +18 -0
  290. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/generate-wiki.md +21 -0
  291. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/generator-settings.md +18 -0
  292. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/host-probe.md +18 -0
  293. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/ingest-preview.md +18 -0
  294. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/link-hygiene.md +21 -0
  295. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/link-topology.md +19 -0
  296. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/passage-search.md +19 -0
  297. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/propagation-plan.md +18 -0
  298. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/provider-credentials.md +18 -0
  299. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/refresh-report.md +18 -0
  300. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/task-evidence.md +22 -0
  301. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/upstream-record.md +18 -0
  302. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/watch-upstream.md +18 -0
  303. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/wiki-atlas.md +19 -0
  304. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/panel.js +261 -513
  305. package/scaffold/.agent/skills/total-recall/plugins/security/DESIGN.md +84 -0
  306. package/scaffold/.agent/skills/total-recall/plugins/security/README.md +4 -2
  307. package/scaffold/.agent/skills/total-recall/plugins/security/cli.mjs +7 -19
  308. package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +7 -7
  309. package/scaffold/.agent/skills/total-recall/plugins/security/package.json +2 -3
  310. package/scaffold/.agent/skills/total-recall/plugins/security/plugin.json +19 -52
  311. package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/device-evidence.md +21 -0
  312. package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/evidence-desk.md +18 -0
  313. package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/guided-device-review.md +17 -0
  314. package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/host-and-network.md +21 -0
  315. package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/https-headers.md +17 -0
  316. package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/mesh-coverage.md +18 -0
  317. package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/private-security-tools.md +16 -0
  318. package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/single-origin-header-check.md +17 -0
  319. package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/source-review.md +21 -0
  320. package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/ssh-permission-plan.md +17 -0
  321. package/scaffold/.agent/skills/total-recall/plugins/security/ui/home.js +283 -0
  322. package/scaffold/.agent/skills/total-recall/plugins/ssss/DESIGN.md +93 -0
  323. package/scaffold/.agent/skills/total-recall/plugins/ssss/package-lock.json +2 -2
  324. package/scaffold/.agent/skills/total-recall/plugins/ssss/package.json +2 -1
  325. package/scaffold/.agent/skills/total-recall/plugins/ssss/plugin.json +5 -3
  326. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/conformance.md +26 -0
  327. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/registry.md +37 -0
  328. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/repair.md +42 -0
  329. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/scaffold.md +33 -0
  330. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/status.md +43 -0
  331. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/validate.md +34 -0
  332. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/validation-diagnostics.md +17 -0
  333. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/workspace-registry-preflight.md +17 -0
  334. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/home.js +348 -0
  335. package/scaffold/.agent/skills/total-recall/references/operations.md +3 -1
  336. package/src/cli/agent.mjs +1 -1
  337. package/src/cli/api.mjs +3 -3
  338. package/src/cli/brief.mjs +3 -0
  339. package/src/cli/command.mjs +41 -2
  340. package/src/cli/config.mjs +16 -0
  341. package/src/cli/connect.mjs +2 -7
  342. package/src/cli/dream.mjs +93 -69
  343. package/src/cli/harness.mjs +4 -4
  344. package/src/cli/help.mjs +1 -1
  345. package/src/cli/init.mjs +0 -25
  346. package/src/cli/lessons.mjs +51 -0
  347. package/src/cli/memory-plugin.mjs +1 -1
  348. package/src/cli/plugin/chunks.mjs +145 -0
  349. package/src/cli/plugin/copies.mjs +64 -14
  350. package/src/cli/plugin/create.mjs +30 -59
  351. package/src/cli/plugin/index.mjs +31 -16
  352. package/src/cli/plugin/install.mjs +10 -15
  353. package/src/cli/plugin/remove.mjs +1 -1
  354. package/src/cli/plugin/search.mjs +2 -2
  355. package/src/cli/preflight.mjs +19 -6
  356. package/src/cli/proposals.mjs +1 -1
  357. package/src/cli/recall.mjs +66 -23
  358. package/src/cli/remember.mjs +3 -14
  359. package/src/cli/server.mjs +18 -0
  360. package/src/cli/setup.mjs +2 -9
  361. package/src/cli/start.mjs +1 -0
  362. package/src/cli/startup.mjs +19 -0
  363. package/src/cli/task.mjs +9 -2
  364. package/src/cli/update.mjs +6 -2
  365. package/src/core/access-ledger.mjs +78 -0
  366. package/src/core/agent-manager.mjs +2 -2
  367. package/src/core/app-deploy/design-tokens.mjs +14 -9
  368. package/src/core/app-deploy/ui-elements.mjs +1 -1
  369. package/src/core/brain-locate.mjs +15 -0
  370. package/src/core/brain-registry.mjs +5 -0
  371. package/src/core/brain-updates.mjs +35 -11
  372. package/src/core/claude-hooks.mjs +3 -1
  373. package/src/core/cleanup-task.mjs +15 -0
  374. package/src/core/cli-commands.mjs +2 -0
  375. package/src/core/cli-maintenance.mjs +4 -1
  376. package/src/core/cli-semantic-index.mjs +23 -3
  377. package/src/core/context-compiler.mjs +20 -0
  378. package/src/core/daemon-control.mjs +5 -1
  379. package/src/core/daemon-loop.mjs +39 -62
  380. package/src/core/dashboard-bundle.mjs +261 -0
  381. package/src/core/default-plugins.mjs +1 -1
  382. package/src/core/dream.mjs +33 -105
  383. package/src/core/ensure-dependencies.mjs +152 -0
  384. package/src/core/fast-recall.mjs +2 -2
  385. package/src/core/field-evaluation.mjs +23 -0
  386. package/src/core/gate-mode.mjs +1 -1
  387. package/src/core/harness-hooks.mjs +14 -2
  388. package/src/core/lesson-hooks.mjs +74 -0
  389. package/src/core/lesson-memory.mjs +24 -0
  390. package/src/core/lesson-sessions.mjs +52 -0
  391. package/src/core/lessons.mjs +96 -0
  392. package/src/core/memory-brain.mjs +30 -8
  393. package/src/core/meta-harness.mjs +2 -2
  394. package/src/core/optimizer.mjs +66 -165
  395. package/src/core/plugin-activation.mjs +63 -20
  396. package/src/core/plugin-contracts/{card.mjs → manifest.mjs} +26 -57
  397. package/src/core/plugin-contracts/startup-check.mjs +49 -0
  398. package/src/core/plugin-contracts/ui.mjs +9 -3
  399. package/src/core/plugin-distribution.mjs +6 -6
  400. package/src/core/plugin-lifecycle.mjs +58 -0
  401. package/src/core/plugin-loader.mjs +21 -8
  402. package/src/core/plugin-runner.mjs +21 -10
  403. package/src/core/plugin-runtime.mjs +40 -1
  404. package/src/core/plugin-startup-checks.mjs +44 -0
  405. package/src/core/plugin-state.mjs +19 -5
  406. package/src/core/plugin-store.mjs +64 -33
  407. package/src/core/post-mortem.mjs +52 -36
  408. package/src/core/preflight-gate.mjs +23 -2
  409. package/src/core/project-id.mjs +15 -5
  410. package/src/core/proposal-applier.mjs +31 -2
  411. package/src/core/repo-sync.mjs +4 -6
  412. package/src/core/required-plugins.mjs +57 -10
  413. package/src/core/rule-length.mjs +23 -0
  414. package/src/core/runtime.mjs +1 -1
  415. package/src/core/scheduler.mjs +35 -43
  416. package/src/core/schema.mjs +3 -2
  417. package/src/core/search.mjs +4 -3
  418. package/src/core/server-service.mjs +148 -0
  419. package/src/core/session-watcher.mjs +19 -1
  420. package/src/core/skill-gap.mjs +16 -0
  421. package/src/core/ssss-kernel-bridge.mjs +32 -4
  422. package/src/core/ssss-operation-service.mjs +1 -0
  423. package/src/core/startup-health.mjs +34 -4
  424. package/src/core/surface.mjs +65 -54
  425. package/src/core/task-executors.mjs +54 -106
  426. package/src/core/testing/plugin-fixture.mjs +22 -21
  427. package/src/core/throttled-fetch.mjs +25 -18
  428. package/src/core/ui-chunk-choice.mjs +77 -0
  429. package/src/core/ui-chunk.registry.json +23 -0
  430. package/src/core/ui-chunks.mjs +434 -0
  431. package/src/core/validated-write.mjs +1 -0
  432. package/src/core/vault-cache.mjs +58 -59
  433. package/src/core/vector-field.mjs +134 -192
  434. package/src/core/workspace-compositions.mjs +151 -0
  435. package/src/core/workspace-drawers.mjs +79 -0
  436. package/src/server/api.mjs +4 -27
  437. package/src/server/index.mjs +28 -21
  438. package/src/server/invoke-core-child.mjs +24 -0
  439. package/src/server/invoke-core-operations.mjs +102 -0
  440. package/src/server/memory-events.mjs +31 -0
  441. package/src/server/plugin-brain-targets.mjs +89 -0
  442. package/src/server/rest.mjs +2 -0
  443. package/src/server/route-inventory.mjs +3 -2
  444. package/src/server/route-manifest.json +47 -55
  445. package/src/server/routes/_shared.mjs +7 -14
  446. package/src/server/routes/brains.mjs +29 -62
  447. package/src/server/routes/context.mjs +0 -41
  448. package/src/server/routes/dashboard.mjs +26 -0
  449. package/src/server/routes/invoke.mjs +26 -1
  450. package/src/server/routes/plugins.mjs +154 -45
  451. package/src/server/routes/ssss.mjs +0 -169
  452. package/src/server/routes/system.mjs +0 -15
  453. package/src/server/routes/tasks.mjs +0 -26
  454. package/src/server/routes/ui-chunks.mjs +192 -0
  455. package/src/server/routes/vault.mjs +2 -23
  456. package/frontend/dist/assets/index-DD4qONvH.js +0 -5607
  457. package/frontend/dist/assets/index-DtwsDyFM.css +0 -1
  458. package/frontend/src/components/plugins/card/CardEngine.tsx +0 -108
  459. package/frontend/src/components/plugins/card/CardScenes.tsx +0 -103
  460. package/frontend/src/components/plugins/card/ExecutionPicker.tsx +0 -64
  461. package/frontend/src/components/plugins/card/args.ts +0 -28
  462. package/frontend/src/components/plugins/card/card.css +0 -32
  463. package/frontend/src/components/plugins/card/cli-line.ts +0 -13
  464. package/frontend/src/components/plugins/card/contract.ts +0 -91
  465. package/frontend/src/components/plugins/card/format.ts +0 -49
  466. package/frontend/src/components/plugins/card/runner-context.ts +0 -11
  467. package/frontend/src/components/plugins/card/runner.tsx +0 -43
  468. package/frontend/src/components/plugins/card/theme.ts +0 -35
  469. package/frontend/src/components/plugins/card/use-source.ts +0 -22
  470. package/frontend/src/components/plugins/card/widgets.tsx +0 -94
  471. package/frontend/src/components/plugins/workspace/WorkspaceHost.tsx +0 -23
  472. package/frontend/src/pages/PluginsPage.tsx +0 -1719
  473. package/scaffold/.agent/skills/plugin/evals/cases/04-card-collects-credential.mjs +0 -47
  474. package/scaffold/.agent/skills/plugin/prompts/06-card.md +0 -46
  475. package/scaffold/.agent/skills/plugin/references/card.md +0 -90
  476. package/scaffold/.agent/skills/plugin/subagents/card-designer.md +0 -60
  477. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/DESIGN.md +0 -67
  478. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/card.js +0 -1179
  479. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/DESIGN.md +0 -32
  480. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/card.js +0 -588
  481. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/card.js +0 -575
  482. package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/DESIGN.md +0 -32
  483. package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/card.js +0 -482
  484. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/README.md +0 -53
  485. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/plugin.json +0 -129
  486. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/ui/DESIGN.md +0 -100
  487. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/ui/card.js +0 -637
  488. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/DESIGN.md +0 -217
  489. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/DESIGN.md +0 -32
  490. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/card.js +0 -1276
  491. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/DESIGN.md +0 -45
  492. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/card.js +0 -1401
  493. package/scaffold/.agent/skills/total-recall/plugins/security/scripts/build-ui.mjs +0 -4
  494. package/scaffold/.agent/skills/total-recall/plugins/security/ui/DESIGN.md +0 -41
  495. package/scaffold/.agent/skills/total-recall/plugins/security/ui/card.js +0 -396
  496. package/scaffold/.agent/skills/total-recall/plugins/security/ui/panel.js +0 -98
  497. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/DESIGN.md +0 -15
  498. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/card.js +0 -182
  499. package/src/core/append-log.mjs +0 -131
  500. package/src/core/plugin-tasks.mjs +0 -173
  501. package/templates/onboarding-interview.md +0 -82
  502. /package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/LICENSE +0 -0
@@ -20,11 +20,13 @@
20
20
 
21
21
  import fs from 'node:fs';
22
22
  import path from 'node:path';
23
- import { getNodes } from './vault-cache.mjs';
24
- import { getEmbedding, cosineSimilarity, nodeToEmbedText, loadEmbeddingsIndex, saveEmbeddingToIndex } from './embeddings.mjs';
23
+ import { loadNodes } from './vault.mjs';
24
+ import { getEmbedding, loadEmbeddingsIndex } from './embeddings.mjs';
25
25
  import { inferMemoryLayer } from './memory-layers.mjs';
26
26
  import { logger } from './logger.mjs';
27
27
  import { brainDir } from './config.mjs';
28
+ import { readAccess, recordAccess, lastUsedMs, activeDays, activeDaysSince } from './access-ledger.mjs';
29
+ import { atomicWrite } from './vault-files.mjs';
28
30
 
29
31
  const DERIVED_DIR = path.join(brainDir, 'memory-derived');
30
32
 
@@ -32,10 +34,6 @@ function fieldPath(derivedDir = DERIVED_DIR) {
32
34
  return path.join(derivedDir, 'vector-field.json');
33
35
  }
34
36
 
35
- function queryLogPath(derivedDir = DERIVED_DIR) {
36
- return path.join(derivedDir, 'query-log.jsonl');
37
- }
38
-
39
37
  const LN2 = Math.log(2);
40
38
 
41
39
  // ─── Half-Life Constants (milliseconds) ─────────────────────────────────────
@@ -74,12 +72,14 @@ const HALF_LIFE_TABLE = {
74
72
  */
75
73
  function computeHalfLife(node) {
76
74
  // Priority overrides category
77
- if (node.priority === 'absolute') return Infinity;
75
+ if (node.priority === 'absolute' || HALF_LIFE_TABLE[node.category] === Infinity) return Infinity;
76
+ const configuredDays = Number(node.decay?.half_life_days);
77
+ const categoryHL = configuredDays > 0 ? configuredDays * 86400000 :
78
+ (HALF_LIFE_TABLE[node.category] || HALF_LIFE_TABLE.fact);
78
79
  if (node.priority === 'high') {
79
- const categoryHL = HALF_LIFE_TABLE[node.category] || HALF_LIFE_TABLE.fact;
80
80
  return Math.max(categoryHL, HALF_LIFE_TABLE._high);
81
81
  }
82
- return HALF_LIFE_TABLE[node.category] || HALF_LIFE_TABLE.fact;
82
+ return categoryHL;
83
83
  }
84
84
 
85
85
  /**
@@ -120,7 +120,7 @@ function computeNovelty(accessCount) {
120
120
  /**
121
121
  * Compile the full vector field from the vault.
122
122
  *
123
- * 1. Embed every active node → position vectors
123
+ * 1. Read existing active-node embeddings → position vectors
124
124
  * 2. Compute half-life, trajectory, novelty per point
125
125
  * 3. Compute full pairwise covariance → coupling matrix
126
126
  * 4. Extract dependency graph
@@ -128,133 +128,97 @@ function computeNovelty(accessCount) {
128
128
  */
129
129
  export async function compileField({ vaultDir, derivedDir = DERIVED_DIR } = {}) {
130
130
  const startTime = Date.now();
131
- const allNodes = getNodes(vaultDir).filter(n => n.status === 'active');
131
+ const allNodes = loadNodes(vaultDir || path.join(brainDir, 'memory-vault')).filter(n => n.status === 'active');
132
132
  const totalNodes = allNodes.length;
133
133
  const now = Date.now();
134
134
 
135
135
  logger.info('vector-field', `Compiling field from ${totalNodes} nodes...`);
136
136
 
137
- // ── Load query log for trajectory computation ──
138
- const queryHistory = loadQueryHistory(derivedDir);
137
+ const ledger = readAccess(derivedDir);
139
138
 
140
- // ── Phase 1: Embed all nodes + compute physics ──
141
- const existingIndex = loadEmbeddingsIndex(derivedDir);
139
+ // ── Phase 1: Read stored vectors and compute physics ──
140
+ const existingIndex = fs.existsSync(path.join(derivedDir, 'embeddings.db')) ? loadEmbeddingsIndex(derivedDir) : {};
142
141
  const points = [];
143
- const newlyEmbedded = new Map();
144
- let embedded = 0;
145
142
  let cached = 0;
146
-
147
- const EMBED_CONCURRENCY = 5;
148
- let cursor = 0;
149
-
150
- async function embedWorker() {
151
- while (cursor < allNodes.length) {
152
- const i = cursor++;
153
- const node = allNodes[i];
154
- const text = nodeToEmbedText(node);
155
-
156
- let embedding;
157
- if (existingIndex[node.slug]?.embedding) {
158
- embedding = existingIndex[node.slug].embedding;
159
- cached++;
160
- } else {
161
- try {
162
- embedding = await getEmbedding(text);
163
- newlyEmbedded.set(node.slug, embedding);
164
- embedded++;
165
- } catch (err) {
166
- logger.error('vector-field', `Failed to embed ${node.slug}: ${err.message}`);
167
- continue;
168
- }
169
- }
170
-
171
- // ── Physics computation ──
172
- const decay = node.decay || {};
173
- const accessCount = decay.access_count || 0;
174
- const lastAccessed = node.last_accessed ? new Date(node.last_accessed).getTime() : 0;
175
- const createdAt = node.created ? new Date(node.created).getTime() : now;
176
- const ageMs = now - createdAt;
177
-
178
- // Half-life
179
- const halfLife = computeHalfLife(node);
180
- const lambda = decayConstant(halfLife);
181
-
182
- // Velocity: dR/dt from query log
183
- const { velocity, trajectory } = computeTrajectory(node.slug, queryHistory, now);
184
-
185
- // Novelty
186
- const novelty = computeNovelty(accessCount);
187
-
188
- // Peak relevance (baseline from importance + priority)
189
- const importanceWeight = (node.importance || 3) / 5;
190
- const priorityWeight = node.priority === 'absolute' ? 1.0 : node.priority === 'high' ? 0.8 : node.priority === 'normal' ? 0.5 : 0.3;
191
- const peakRelevance = Math.max(importanceWeight, priorityWeight);
192
-
193
- // Time since last access (for decay computation at sample time)
194
- const timeSinceAccess = lastAccessed > 0 ? now - lastAccessed : ageMs;
195
-
196
- // Dependencies: extract from node.related field
197
- const dependencies = node.related ? (Array.isArray(node.related) ? node.related : String(node.related).split(',').map(s => s.trim())) : [];
198
-
199
- // Precompute L2 norm
200
- let mag = 0;
201
- for (let j = 0; j < embedding.length; j++) mag += embedding[j] * embedding[j];
202
- mag = Math.sqrt(mag);
203
-
204
- points.push({
205
- slug: node.slug,
206
- title: node.title || node.slug,
207
- category: node.category || 'unknown',
208
- layer: inferMemoryLayer(node),
209
- embedding,
210
- magnitude: mag,
211
- // ── Physics ──
212
- half_life: isFinite(halfLife) ? halfLife : null, // null = infinite
213
- lambda, // decay constant
214
- peak_relevance: peakRelevance, // R₀
215
- velocity, // dR/dt
216
- trajectory, // d²R/dt² (acceleration)
217
- novelty, // information density
218
- // ── Metadata ──
219
- access_count: accessCount,
220
- last_accessed: lastAccessed,
221
- created_at: createdAt,
222
- time_since_access: timeSinceAccess,
223
- importance: node.importance || 3,
224
- priority: node.priority || 'normal',
225
- dependencies,
226
- body_length: (node.body || node.content || '').length,
227
- });
143
+ let missing = 0;
144
+ let dimensions = null;
145
+
146
+ for (const node of allNodes) {
147
+ const embedding = existingIndex[node.slug]?.embedding;
148
+ if (!Array.isArray(embedding) || !embedding.length || !embedding.every(Number.isFinite) ||
149
+ !embedding.some(value => value !== 0) || (dimensions !== null && embedding.length !== dimensions)) {
150
+ missing++;
151
+ continue;
228
152
  }
229
- }
230
-
231
- const workers = Array.from(
232
- { length: Math.min(EMBED_CONCURRENCY, allNodes.length) },
233
- () => embedWorker()
234
- );
235
- await Promise.all(workers);
236
-
237
- for (const [slug, embedding] of newlyEmbedded) {
238
- saveEmbeddingToIndex(derivedDir, slug, embedding);
153
+ dimensions ??= embedding.length;
154
+ cached++;
155
+
156
+ // ── Physics computation ──
157
+ const { accessCount, lastAccessed, elapsedMs, velocity, trajectory } = accessPhysics(node, ledger, now);
158
+ const createdAt = new Date(node.created || now).getTime();
159
+ const halfLife = computeHalfLife(node);
160
+ const lambda = decayConstant(halfLife);
161
+
162
+ // Novelty
163
+ const novelty = computeNovelty(accessCount);
164
+
165
+ // Peak relevance (baseline from importance + priority)
166
+ const importanceWeight = (node.importance || 3) / 5;
167
+ const priorityWeight = node.priority === 'absolute' ? 1.0 : node.priority === 'high' ? 0.8 : node.priority === 'normal' ? 0.5 : 0.3;
168
+ const peakRelevance = Math.max(importanceWeight, priorityWeight);
169
+
170
+ // Time since last access (for decay computation at sample time)
171
+ const timeSinceAccess = elapsedMs;
172
+
173
+ // Dependencies: extract from node.related field
174
+ const dependencies = node.related ? (Array.isArray(node.related) ? node.related : String(node.related).split(',').map(s => s.trim())) : [];
175
+
176
+ // Precompute L2 norm
177
+ let mag = 0;
178
+ for (let j = 0; j < embedding.length; j++) mag += embedding[j] * embedding[j];
179
+ mag = Math.sqrt(mag);
180
+
181
+ points.push({
182
+ slug: node.slug,
183
+ title: node.title || node.slug,
184
+ category: node.category || 'unknown',
185
+ layer: inferMemoryLayer(node),
186
+ embedding,
187
+ magnitude: mag,
188
+ // ── Physics ──
189
+ half_life: isFinite(halfLife) ? halfLife : null, // null = infinite
190
+ lambda, // decay constant
191
+ peak_relevance: peakRelevance, // R₀
192
+ velocity, // dR/dt
193
+ trajectory, // d²R/dt² (acceleration)
194
+ novelty, // information density
195
+ // ── Metadata ──
196
+ access_count: accessCount,
197
+ last_accessed: lastAccessed,
198
+ created_at: createdAt,
199
+ time_since_access: timeSinceAccess,
200
+ importance: node.importance || 3,
201
+ priority: node.priority || 'normal',
202
+ dependencies,
203
+ body_length: (node.body || node.content || '').length,
204
+ });
239
205
  }
240
206
 
241
207
  const embedMs = Date.now() - startTime;
242
- logger.info('vector-field', `Embedded ${embedded} new + ${cached} cached in ${embedMs}ms`);
208
+ logger.info('vector-field', `Loaded ${cached} existing vectors; ${missing} missing or incompatible`);
243
209
 
244
210
  // ── Phase 2: Pairwise covariance matrix ──
245
211
  const N = points.length;
246
212
  const slugIndex = points.map(p => p.slug);
247
- const covariance = new Array(N);
213
+ const covariance = Array.from({ length: N }, () => new Float32Array(N));
248
214
 
249
215
  for (let i = 0; i < N; i++) {
250
- covariance[i] = new Float32Array(N);
251
216
  for (let j = i; j < N; j++) {
252
217
  if (i === j) {
253
218
  covariance[i][j] = 1.0;
254
219
  } else {
255
220
  const sim = fastCosine(points[i].embedding, points[j].embedding, points[i].magnitude, points[j].magnitude);
256
221
  covariance[i][j] = sim;
257
- if (!covariance[j]) covariance[j] = new Float32Array(N);
258
222
  covariance[j][i] = sim;
259
223
  }
260
224
  }
@@ -274,7 +238,8 @@ export async function compileField({ vaultDir, derivedDir = DERIVED_DIR } = {})
274
238
  meta: {
275
239
  total_nodes: totalNodes,
276
240
  points_compiled: N,
277
- embedded_new: embedded,
241
+ embedded_new: 0,
242
+ missing_embeddings: missing,
278
243
  embedded_cached: cached,
279
244
  embed_ms: embedMs,
280
245
  covariance_ms: covMs,
@@ -289,54 +254,51 @@ export async function compileField({ vaultDir, derivedDir = DERIVED_DIR } = {})
289
254
 
290
255
  const outPath = fieldPath(derivedDir);
291
256
  fs.mkdirSync(path.dirname(outPath), { recursive: true });
292
- fs.writeFileSync(outPath, JSON.stringify(field));
257
+ atomicWrite(outPath, JSON.stringify(field));
293
258
 
294
259
  logger.info('vector-field', `Field compiled: ${N} points, ${N * N} couplings, ${field.meta.infinite_halflife} immortal, ${field.meta.with_dependencies} with deps, ${field.meta.compile_ms}ms`);
295
260
 
296
261
  return field;
297
262
  }
298
263
 
299
- // ─── Trajectory Computation ─────────────────────────────────────────────────
300
-
301
- /**
302
- * Compute velocity (dR/dt) and trajectory (d²R/dt²) from query log.
303
- * Uses 3 time windows to detect acceleration/deceleration.
304
- */
305
- function computeTrajectory(slug, queryHistory, now) {
306
- const WINDOW = 24 * 60 * 60 * 1000; // 1 day
307
-
308
- // Count hits in 3 windows: [0-24h], [24-48h], [48-72h]
309
- let w0 = 0, w1 = 0, w2 = 0;
310
- for (const entry of queryHistory) {
311
- if (!entry.hits?.includes(slug)) continue;
312
- const age = now - entry.t;
313
- if (age < WINDOW) w0++;
314
- else if (age < 2 * WINDOW) w1++;
315
- else if (age < 3 * WINDOW) w2++;
316
- }
317
-
318
- // Velocity = first derivative (recent vs previous)
319
- const velocity = (w0 - w1) * 0.1;
320
-
321
- // Trajectory = second derivative (acceleration of velocity)
322
- const v_prev = (w1 - w2) * 0.1;
323
- const trajectory = velocity - v_prev; // positive = accelerating, negative = decelerating
324
-
325
- return {
326
- velocity: Math.max(-1, Math.min(1, velocity)),
327
- trajectory: Math.max(-1, Math.min(1, trajectory)),
328
- };
264
+ /** Read live usage. Old ledgers have no daily counts, so their trend is unknown (zero). */
265
+ function accessPhysics(node, ledger, now) {
266
+ const entry = ledger[node.slug];
267
+ const accessCount = Math.max(0, Number(entry?.count ?? node.access_count ?? node.decay?.access_count) || 0);
268
+ const lastAccessed = lastUsedMs(node, ledger);
269
+ const days = activeDays(ledger).filter(day => day <= new Date(now).toISOString().slice(0, 10));
270
+ const counts = [0, 1, 2].map(offset => Number(entry?.daily_counts?.[days.at(-1 - offset)]) || 0);
271
+ const clamp = value => Math.max(-1, Math.min(1, value));
272
+ return { accessCount, lastAccessed,
273
+ elapsedMs: days.length ? activeDaysSince({ _active_days: days }, lastAccessed) * 86400000 : Math.max(0, now - lastAccessed),
274
+ velocity: clamp((counts[0] - counts[1]) * .1),
275
+ trajectory: clamp((counts[0] - 2 * counts[1] + counts[2]) * .1) };
329
276
  }
330
277
 
331
- function loadQueryHistory(derivedDir = DERIVED_DIR) {
332
- try {
333
- const logPath = queryLogPath(derivedDir);
334
- if (!fs.existsSync(logPath)) return [];
335
- const lines = fs.readFileSync(logPath, 'utf8').trim().split('\n');
336
- const cutoff = Date.now() - (3 * 24 * 60 * 60 * 1000); // only last 3 days
337
- return lines.map(l => { try { return JSON.parse(l); } catch { return null; } })
338
- .filter(e => e && e.t > cutoff);
339
- } catch { return []; }
278
+ /** Re-rank only recall candidates. No provider call or access write. */
279
+ export function rankWithField(results, { field, derivedDir = DERIVED_DIR, now = Date.now() } = {}) {
280
+ if (!field) throw new Error('Vector field not compiled. Run total-recall recall --compile-field first.');
281
+ const ledger = readAccess(derivedDir);
282
+ const indexes = new Map(field.points.map((point, index) => [point.slug, index]));
283
+ const ranked = results.map(result => {
284
+ const index = result.type === 'session' ? undefined : indexes.get(result.slug);
285
+ if (index === undefined) return result;
286
+ const point = field.points[index];
287
+ const physics = accessPhysics({ ...point, last_accessed: point.last_accessed, created: point.created_at }, ledger, now);
288
+ const relevance = effectiveRelevance(point.peak_relevance, point.lambda, physics.elapsedMs, physics.trajectory);
289
+ let coupling = 0, peers = 0;
290
+ for (const candidate of results) {
291
+ const other = candidate.type === 'session' ? undefined : indexes.get(candidate.slug);
292
+ if (other === undefined || other === index) continue;
293
+ coupling += Math.max(0, field.covariance[index][other]) * candidate.score;
294
+ peers++;
295
+ }
296
+ // The base score remains the main signal. Missing vectors and sessions retain their score.
297
+ const score = result.score * (.7 + .3 * relevance) + .05 * physics.velocity +
298
+ .05 * computeNovelty(physics.accessCount) + .15 * (peers ? coupling / peers : 0);
299
+ return { ...result, base_score: result.score, score: round4(score), field_score: round4(score) };
300
+ });
301
+ return ranked.sort((a, b) => b.score - a.score);
340
302
  }
341
303
 
342
304
  // ─── Fast Cosine ────────────────────────────────────────────────────────────
@@ -381,7 +343,14 @@ export async function sampleField({
381
343
  if (!field) throw new Error('Vector field not compiled. Run compileField() first.');
382
344
  }
383
345
 
384
- const { points, covariance, slugIndex } = field;
346
+ const ledger = readAccess(derivedDir);
347
+ const points = field.points.map(point => {
348
+ const usage = accessPhysics({ ...point, created: point.created_at }, ledger, now);
349
+ return { ...point, velocity: usage.velocity, trajectory: usage.trajectory,
350
+ novelty: computeNovelty(usage.accessCount), access_count: usage.accessCount,
351
+ last_accessed: usage.lastAccessed, time_since_access: usage.elapsedMs };
352
+ });
353
+ const { covariance } = field;
385
354
  const N = points.length;
386
355
  if (N === 0) return { results: [], query_embedding: [], sample_ms: 0, interference: [] };
387
356
 
@@ -401,7 +370,7 @@ export async function sampleField({
401
370
  const decayedScores = new Float64Array(N);
402
371
  for (let i = 0; i < N; i++) {
403
372
  const p = points[i];
404
- const elapsed = p.last_accessed > 0 ? now - p.last_accessed : p.time_since_access || 0;
373
+ const elapsed = p.time_since_access || 0;
405
374
  const decayedRelevance = effectiveRelevance(p.peak_relevance, p.lambda, elapsed, p.trajectory);
406
375
  // Blend: 70% similarity, 30% decayed relevance
407
376
  decayedScores[i] = (directScores[i] * 0.7) + (decayedRelevance * 0.3);
@@ -460,7 +429,7 @@ export async function sampleField({
460
429
  // ── Format results ──
461
430
  const results = topResults.map(({ index, score }) => {
462
431
  const p = points[index];
463
- const elapsed = p.last_accessed > 0 ? now - p.last_accessed : p.time_since_access || 0;
432
+ const elapsed = p.time_since_access || 0;
464
433
  const remainingHL = p.lambda > 0 ? Math.round((LN2 / p.lambda) / (24 * 60 * 60 * 1000)) : null;
465
434
  return {
466
435
  slug: p.slug,
@@ -485,7 +454,7 @@ export async function sampleField({
485
454
  });
486
455
 
487
456
  const sampleMs = Date.now() - startTime;
488
- logQuery(query, results.map(r => r.slug), derivedDir);
457
+ recordAccess(derivedDir, results.map(r => r.slug));
489
458
 
490
459
  logger.info('vector-field', `Sampled in ${sampleMs}ms: ${results.length} results, ${depsPulledIn.length} deps pulled, ${interference.length} interference pairs`);
491
460
 
@@ -557,52 +526,25 @@ function round4(n) { return Math.round(n * 10000) / 10000; }
557
526
  // ─── Field Loading ──────────────────────────────────────────────────────────
558
527
 
559
528
  let _cachedField = null;
560
- let _cachedFieldMtime = 0;
529
+ let _cachedFieldStamp = null;
561
530
 
562
531
  export function loadField(derivedDir = DERIVED_DIR) {
563
532
  try {
564
533
  const fp = fieldPath(derivedDir);
565
534
  if (!fs.existsSync(fp)) return null;
566
535
  const stat = fs.statSync(fp);
567
- if (_cachedField && stat.mtimeMs === _cachedFieldMtime) return _cachedField;
536
+ const stamp = `${path.resolve(fp)}:${stat.mtimeMs}:${stat.ctimeMs}:${stat.size}`;
537
+ if (_cachedField && stamp === _cachedFieldStamp) return _cachedField;
568
538
  const raw = JSON.parse(fs.readFileSync(fp, 'utf8'));
539
+ if (!Array.isArray(raw.points) || !Array.isArray(raw.covariance) ||
540
+ raw.covariance.length !== raw.points.length || raw.covariance.some(row =>
541
+ !Array.isArray(row) || row.length !== raw.points.length || !row.every(Number.isFinite))) return null;
569
542
  _cachedField = raw;
570
- _cachedFieldMtime = stat.mtimeMs;
543
+ _cachedFieldStamp = stamp;
571
544
  return raw;
572
545
  } catch { return null; }
573
546
  }
574
547
 
575
- // ─── Query Logging ──────────────────────────────────────────────────────────
576
-
577
- function logQuery(query, hitSlugs, derivedDir = DERIVED_DIR) {
578
- try {
579
- const logPath = queryLogPath(derivedDir);
580
- fs.mkdirSync(path.dirname(logPath), { recursive: true });
581
- fs.appendFileSync(logPath, JSON.stringify({ t: Date.now(), q: query.slice(0, 200), hits: hitSlugs.slice(0, 20) }) + '\n');
582
- } catch { /* fire and forget */ }
583
- }
584
-
585
- // ─── Velocity Recomputation ─────────────────────────────────────────────────
586
-
587
- /**
588
- * Recompute velocity + trajectory vectors from query log.
589
- * Call periodically (dream cycle) to update the field in-place.
590
- */
591
- export function recomputeVelocities(field, derivedDir = DERIVED_DIR) {
592
- if (!field) return field;
593
- const now = Date.now();
594
- const history = loadQueryHistory(derivedDir);
595
-
596
- for (const point of field.points) {
597
- const { velocity, trajectory } = computeTrajectory(point.slug, history, now);
598
- point.velocity = velocity;
599
- point.trajectory = trajectory;
600
- point.novelty = computeNovelty(point.access_count);
601
- }
602
-
603
- return field;
604
- }
605
-
606
548
  // ─── Field Statistics ───────────────────────────────────────────────────────
607
549
 
608
550
  export function fieldStats(derivedDir = DERIVED_DIR) {
@@ -0,0 +1,151 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { executePluginState, listPluginStates, PluginStateStore } from './plugin-state-store.mjs';
4
+ import { getUiChunk, UiChunkInputError } from './ui-chunks.mjs';
5
+ import { getPluginById } from './plugin-loader.mjs';
6
+ import { pluginActivation } from './plugin-state.mjs';
7
+
8
+ const OWNER = 'workspace-ui';
9
+ const SAVED_PAGES = 'workspace-pages';
10
+ const ID = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
11
+ const VERSION = /^[a-f0-9]{64}$/;
12
+ const MAX_PAGE_BLOCKS = 24;
13
+
14
+ function stateFile(brainDir, plugin) {
15
+ if (typeof plugin !== 'string' || plugin.length > 64 || !ID.test(plugin)) throw new UiChunkInputError('Invalid plugin ID.');
16
+ return path.join(brainDir, 'config', 'workspace-compositions', plugin + '.json');
17
+ }
18
+
19
+ function readCompositionState(brainDir, plugin, projectRoot) {
20
+ const store = new PluginStateStore(stateFile(brainDir, plugin), OWNER);
21
+ const owner = getPluginById(plugin, projectRoot);
22
+ if (!owner?.valid || !pluginActivation(owner, { projectRoot }).enabled) {
23
+ throw new UiChunkInputError('The destination plugin must be installed and enabled.');
24
+ }
25
+ const state = store.read({ keys: [] });
26
+ return { keys: state.keys, version: store.version };
27
+ }
28
+
29
+ function readPageBlocks(brainDir, keys, projectRoot) {
30
+ return keys.flatMap(key => {
31
+ const chunk = readPageBlock(brainDir, key, projectRoot);
32
+ if (!chunk) return [];
33
+ const { html: _html, ...metadata } = chunk;
34
+ return [metadata];
35
+ });
36
+ }
37
+
38
+ export function readWorkspaceComposition(brainDir, plugin, projectRoot = process.cwd()) {
39
+ const { keys, version } = readCompositionState(brainDir, plugin, projectRoot);
40
+ return { chunks: readPageBlocks(brainDir, keys, projectRoot), version };
41
+ }
42
+
43
+ function readPageBlock(brainDir, key, projectRoot) {
44
+ if (typeof key !== 'string') return null;
45
+ const [owner, id, ...rest] = key.split(':');
46
+ return ID.test(owner) && typeof id === 'string' && ID.test(id) && !rest.length
47
+ ? getUiChunk(owner, id, { surface: 'page', brainDir, projectRoot }) : null;
48
+ }
49
+
50
+ export async function publishWorkspaceComposition(brainDir, plugin, keys, version, projectRoot = process.cwd()) {
51
+ const file = stateFile(brainDir, plugin);
52
+ const owner = getPluginById(plugin, projectRoot);
53
+ if (!owner?.valid || !pluginActivation(owner, { projectRoot }).enabled ||
54
+ !keys.length || keys.length > 3 || keys.some(key => !readPageBlock(brainDir, key, projectRoot))) {
55
+ throw new UiChunkInputError('The selected page blocks changed. Compose the view again.');
56
+ }
57
+ return executePluginState({ file, plugin: OWNER, action: 'write', value: { keys }, expectedVersion: version });
58
+ }
59
+
60
+ function pageDirectory(brainDir) {
61
+ const config = path.join(brainDir, 'config');
62
+ const directory = path.join(config, SAVED_PAGES);
63
+ for (const candidate of [config, directory]) {
64
+ const stat = fs.lstatSync(candidate, { throwIfNoEntry: false });
65
+ if (stat && !stat.isDirectory()) throw new Error('Unsafe saved page directory');
66
+ }
67
+ return directory;
68
+ }
69
+
70
+ function savedPageFile(brainDir, id) {
71
+ if (typeof id !== 'string' || id.length > 64 || !ID.test(id)) throw new UiChunkInputError('Invalid saved page ID.');
72
+ return path.join(pageDirectory(brainDir), id + '.json');
73
+ }
74
+
75
+ function pageConflict(message) {
76
+ return Object.assign(new Error(message), { status: 409 });
77
+ }
78
+
79
+ export function listWorkspacePages(brainDir) {
80
+ return listPluginStates(pageDirectory(brainDir), SAVED_PAGES)
81
+ .map(({ value: { id, title }, version }) => ({ id, title, version }))
82
+ .sort((a, b) => a.title.localeCompare(b.title) || a.id.localeCompare(b.id));
83
+ }
84
+
85
+ export function readWorkspacePage(brainDir, id, projectRoot = process.cwd()) {
86
+ const store = new PluginStateStore(savedPageFile(brainDir, id), SAVED_PAGES);
87
+ const page = store.read(null);
88
+ if (!page) return null;
89
+ return { id, title: page.title, version: store.version, chunks: readPageBlocks(brainDir, page.keys, projectRoot) };
90
+ }
91
+
92
+ export function prepareWorkspacePage(brainDir, id, title) {
93
+ const file = savedPageFile(brainDir, id);
94
+ const name = typeof title === 'string' ? title.trim() : '';
95
+ if (!name || name.length > 120 || /[\u0000-\u001f\u007f-\u009f]/u.test(title)) {
96
+ throw new UiChunkInputError('Enter a saved page title of up to 120 characters.');
97
+ }
98
+ const store = new PluginStateStore(file, SAVED_PAGES);
99
+ if (store.read(null)) throw pageConflict('That saved page ID already exists.');
100
+ return { title: name, file };
101
+ }
102
+
103
+ function verifyPageBlocks(brainDir, keys, projectRoot) {
104
+ if (!Array.isArray(keys) || !keys.length || keys.length > MAX_PAGE_BLOCKS || new Set(keys).size !== keys.length) {
105
+ throw new UiChunkInputError(`Choose one to ${MAX_PAGE_BLOCKS} distinct page blocks.`);
106
+ }
107
+ const chunks = readPageBlocks(brainDir, keys, projectRoot);
108
+ if (chunks.length !== keys.length) throw pageConflict('The selected page blocks are no longer available.');
109
+ return chunks;
110
+ }
111
+
112
+ export async function createWorkspacePage(brainDir, { id, title, keys }, projectRoot = process.cwd()) {
113
+ const page = prepareWorkspacePage(brainDir, id, title);
114
+ const chunks = verifyPageBlocks(brainDir, keys, projectRoot);
115
+ try {
116
+ const { version } = await executePluginState({
117
+ file: page.file, plugin: SAVED_PAGES, action: 'write',
118
+ value: { id, title: page.title, keys }, expectedVersion: null,
119
+ });
120
+ return { id, title: page.title, version, chunks };
121
+ } catch (error) {
122
+ if (error.message.startsWith('Plugin state version conflict')) throw pageConflict('That saved page ID already exists.');
123
+ throw error;
124
+ }
125
+ }
126
+
127
+ export async function updateWorkspacePage(brainDir, { id, keys, revision }, projectRoot = process.cwd()) {
128
+ if (typeof revision !== 'string' || !VERSION.test(revision)) {
129
+ throw new UiChunkInputError('Supply the current saved page revision.');
130
+ }
131
+ const file = savedPageFile(brainDir, id);
132
+ const store = new PluginStateStore(file, SAVED_PAGES);
133
+ const page = store.read(null);
134
+ if (!page) throw Object.assign(new Error('Saved page unavailable'), { status: 404 });
135
+ if (store.version !== revision) throw pageConflict('The saved page changed. Read its current revision before editing.');
136
+ const chunks = verifyPageBlocks(brainDir, keys, projectRoot);
137
+ const { version } = await executePluginState({
138
+ file, plugin: SAVED_PAGES, action: 'write',
139
+ value: { id, title: page.title, keys }, expectedVersion: revision,
140
+ });
141
+ return { id, title: page.title, version, chunks };
142
+ }
143
+
144
+ export async function saveWorkspaceCompositionPage(brainDir, { id, title, sourcePlugin, sourceVersion }, projectRoot = process.cwd()) {
145
+ if (typeof sourceVersion !== 'string' || !VERSION.test(sourceVersion)) {
146
+ throw new UiChunkInputError('Supply the published Home revision to save.');
147
+ }
148
+ const { keys, version } = readCompositionState(brainDir, sourcePlugin, projectRoot);
149
+ if (version !== sourceVersion) throw pageConflict('The plugin Home changed. Read its current revision before saving.');
150
+ return createWorkspacePage(brainDir, { id, title, keys }, projectRoot);
151
+ }