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
@@ -0,0 +1,96 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import crypto from 'node:crypto';
4
+ import { atomicWrite } from './vault.mjs';
5
+ import { addTask, getTask, resolveQueueDir } from './task-envelope.mjs';
6
+ import { readLessonEntries, countLessonTurns, selectLessonTurns } from './lesson-sessions.mjs';
7
+ import { acquireBrainUpdateLock } from './brain-updates.mjs';
8
+
9
+ export const DEFAULT_LESSON_INTERVAL = 10;
10
+ const hash = value => crypto.createHash('sha256').update(value).digest('hex').slice(0, 12);
11
+
12
+ /** A local hook only queues work. It never calls a model or elects a leader. */
13
+ export function tickLessons({ brainDir, sessionId, transcriptPath, event = 'Stop', interval,
14
+ turnId, turnCount, harness = 'unknown', hold } = {}) {
15
+ if (!brainDir || !sessionId) throw new Error('Lesson review requires a brain and session id');
16
+ const sessionsDir = path.join(brainDir, 'sessions');
17
+ const configPath = path.join(brainDir, 'config', 'lessons.json');
18
+ if (fs.existsSync(configPath)) {
19
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
20
+ if (config.enabled === false) return { tasks: [], skipped: 'disabled' };
21
+ interval ??= config.interval;
22
+ if (hold === undefined) hold = config.holdWrites === true;
23
+ }
24
+ interval ??= DEFAULT_LESSON_INTERVAL;
25
+ if (!Number.isInteger(interval) || interval < 1) throw new Error('Lesson interval must be a positive integer');
26
+ hold ??= false;
27
+ const root = path.join(brainDir, 'lesson-reviews');
28
+ fs.mkdirSync(root, { recursive: true });
29
+ // Match writeSession's identity so native hooks and later ingest share a cursor.
30
+ const localTranscript = transcriptPath && path.dirname(path.resolve(transcriptPath)) === path.resolve(sessionsDir);
31
+ const key = transcriptPath ? (localTranscript ? path.basename(transcriptPath, '.jsonl') : hash(path.resolve(transcriptPath))) : hash(`${harness}:${sessionId}`);
32
+ const lock = path.join(root, `${key}.lock`);
33
+ const lockFd = acquireBrainUpdateLock(lock, { waitMs: 0 });
34
+ try {
35
+ const stateFile = path.join(root, `${key}.json`);
36
+ const state = fs.existsSync(stateFile) ? JSON.parse(fs.readFileSync(stateFile, 'utf8')) : { turns: 0, queuedThrough: 0 };
37
+ let entries;
38
+ const savedTranscript = path.join(sessionsDir, `${key}.jsonl`);
39
+ if (!transcriptPath && fs.existsSync(savedTranscript)) transcriptPath = savedTranscript;
40
+ if (transcriptPath) {
41
+ entries = readLessonEntries(transcriptPath);
42
+ turnCount = countLessonTurns(entries);
43
+ if (!localTranscript) {
44
+ fs.mkdirSync(sessionsDir, { recursive: true });
45
+ atomicWrite(path.join(sessionsDir, `${key}.jsonl`), entries.map(entry => JSON.stringify(entry)).join('\n') + '\n');
46
+ }
47
+ }
48
+ if (turnCount !== undefined) {
49
+ if (!Number.isInteger(turnCount) || turnCount < state.turns) throw new Error('Lesson transcript turn count went backwards');
50
+ state.turns = turnCount;
51
+ } else if (event === 'UserPromptSubmit' && (!turnId || turnId !== state.lastTurnId)) {
52
+ state.turns++;
53
+ state.lastTurnId = turnId;
54
+ }
55
+ const final = event === 'SessionEnd' || event === 'session-end';
56
+ const tasks = [];
57
+ // Prompt hooks count only. Stop has the completed response and tool results.
58
+ if (event !== 'UserPromptSubmit' && entries) {
59
+ while (state.turns - state.queuedThrough >= interval || (final && state.turns > state.queuedThrough)) {
60
+ const fromTurn = state.queuedThrough + 1;
61
+ const toTurn = Math.min(state.turns, state.queuedThrough + interval);
62
+ const slug = `lesson-review-${key}-${fromTurn}-${toTurn}`;
63
+ const queueDir = resolveQueueDir(brainDir);
64
+ if (!getTask(queueDir, slug) && !fs.existsSync(path.join(root, `${slug}.done`))) {
65
+ const snapshot = path.join(root, `${slug}.jsonl`);
66
+ atomicWrite(snapshot, selectLessonTurns(entries, { fromTurn, toTurn }).map(entry => JSON.stringify(entry)).join('\n') + '\n');
67
+ addTask({ slug, intent: `Review session ${sessionId}, turns ${fromTurn}–${toTurn}`,
68
+ kind: 'memory', executor: 'lesson-review', category: 'lesson-review', priority: 40,
69
+ origin: { agent: harness, session_id: sessionId },
70
+ payload: { sessionPath: snapshot, fromTurn, toTurn, hold, sessionId },
71
+ }, queueDir);
72
+ }
73
+ tasks.push(slug);
74
+ state.queuedThrough = toTurn;
75
+ }
76
+ }
77
+ atomicWrite(stateFile, JSON.stringify(state));
78
+ return { turns: state.turns, queuedThrough: state.queuedThrough, tasks };
79
+ } finally {
80
+ // Remove only the inode this process acquired. A dead hook's lock is reclaimed
81
+ // by the shared lock owner on the next tick.
82
+ try { if (fs.statSync(lock).ino === fs.fstatSync(lockFd).ino) fs.unlinkSync(lock); }
83
+ finally { fs.closeSync(lockFd); }
84
+ }
85
+ }
86
+
87
+ /** Session-ingest fallback for harnesses without a lifecycle hook. Every node calls this. */
88
+ export function enqueueSessionReviews({ brainDir, now = Date.now(), quietMs = 5 * 60_000, interval, hold } = {}) {
89
+ const dir = path.join(brainDir, 'sessions');
90
+ if (!fs.existsSync(dir)) return [];
91
+ return fs.readdirSync(dir).filter(name => name.endsWith('.jsonl')).map(name => {
92
+ const transcriptPath = path.join(dir, name);
93
+ return tickLessons({ brainDir, sessionId: path.basename(name, '.jsonl'), transcriptPath, interval, hold,
94
+ event: now - fs.statSync(transcriptPath).mtimeMs >= quietMs ? 'SessionEnd' : 'Stop', harness: 'session-ingest' });
95
+ });
96
+ }
@@ -5,6 +5,7 @@ import { writeFileSecure } from './secure-file.mjs';
5
5
  import { ensureProjectId } from './project-id.mjs';
6
6
 
7
7
  function ensureDir(dir) { fs.mkdirSync(dir, { recursive: true }); }
8
+ const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
8
9
 
9
10
  export const VAULT_CATEGORIES = Object.freeze([
10
11
  'invariants',
@@ -18,17 +19,33 @@ export const VAULT_CATEGORIES = Object.freeze([
18
19
  'lore',
19
20
  ]);
20
21
 
22
+ function readBrainConfig(file) {
23
+ let current;
24
+ try { current = JSON.parse(fs.readFileSync(file, 'utf8')); }
25
+ catch (error) {
26
+ if (error.code === 'ENOENT') return {};
27
+ throw error;
28
+ }
29
+ if (!current || typeof current !== 'object' || Array.isArray(current)) {
30
+ throw new Error('Existing brain identity must be an object');
31
+ }
32
+ return current;
33
+ }
34
+
35
+ /** Update connection settings without replacing the existing brain identity. */
36
+ export function writeBrainConnection(brainDir, fields) {
37
+ const file = path.join(brainDir, 'config', 'brain.json');
38
+ const current = readBrainConfig(file);
39
+ ensureDir(path.dirname(file));
40
+ writeFileSecure(file, JSON.stringify({ ...current, ...fields }, null, 2), { encoding: 'utf8', mode: 0o600 });
41
+ return file;
42
+ }
43
+
21
44
  export function writeBrainIdentity(brainDir, { name, role = 'project', tags = [] } = {}) {
22
45
  const configDir = path.join(brainDir, 'config');
23
46
  ensureDir(configDir);
24
47
  const brainJsonPath = path.join(configDir, 'brain.json');
25
- let current = {};
26
- if (fs.existsSync(brainJsonPath)) {
27
- current = JSON.parse(fs.readFileSync(brainJsonPath, 'utf8'));
28
- if (!current || typeof current !== 'object' || Array.isArray(current)) {
29
- throw new Error('Existing brain identity must be an object');
30
- }
31
- }
48
+ const current = readBrainConfig(brainJsonPath);
32
49
  const next = {
33
50
  ...current,
34
51
  name: name || current.name || path.basename(path.resolve(brainDir, '../../..')),
@@ -54,12 +71,17 @@ export function registerProjectBrain(globalBrainDir, entry) {
54
71
 
55
72
  const abs = path.resolve(entry.path);
56
73
  const idx = list.findIndex((p) => path.resolve(p.path || '') === abs);
74
+ const existing = list[idx];
75
+ const candidateId = existing?.project_id;
76
+ const fallbackId = typeof candidateId === 'string' && UUID.test(candidateId) &&
77
+ existing.brainDir === entry.brainDir && list.filter(record => record.project_id === candidateId).length === 1
78
+ ? candidateId : undefined;
57
79
  const now = new Date().toISOString();
58
80
  const row = {
59
81
  name: entry.name,
60
82
  path: abs,
61
83
  brainDir: entry.brainDir,
62
- project_id: ensureProjectId(entry.brainDir).project_id,
84
+ project_id: ensureProjectId(entry.brainDir, { fallbackId }).project_id,
63
85
  full_brain: true,
64
86
  layer: 'project',
65
87
  tags: entry.tags || ['project-brain'],
@@ -1,11 +1,11 @@
1
- /** Compatibility facade only. Adapter contracts, auth, usage and execution belong to Meta Harness. */
1
+ /** Compatibility facade only. Adapter contracts, auth, usage and execution belong to Metaharness. */
2
2
  import { loadPluginModule } from './plugin-runtime.mjs';
3
3
 
4
4
  let owner, ownerError;
5
5
  try { owner = await loadPluginModule('meta-harness', 'harness-compat'); }
6
6
  catch (error) { ownerError = error; }
7
7
  function implementation() {
8
- if (!owner) throw new Error(`Meta Harness is unavailable: ${ownerError?.message || 'install the plugin'}`);
8
+ if (!owner) throw new Error(`Metaharness is unavailable: ${ownerError?.message || 'install the plugin'}`);
9
9
  return owner;
10
10
  }
11
11
 
@@ -4,8 +4,8 @@ import crypto from 'crypto';
4
4
  import matter from './frontmatter.mjs';
5
5
  import { writeNode } from './vault.mjs';
6
6
  import { getNodes } from './vault-cache.mjs';
7
+ import { readAccess, lastUsedMs, activeDaysSince } from './access-ledger.mjs';
7
8
  import { logger } from './logger.mjs';
8
- import { brainDir } from './config.mjs';
9
9
 
10
10
  /**
11
11
  * Total Recall Optimizer
@@ -166,129 +166,6 @@ export async function generateMemoryCleanupProposals(vaultDir) {
166
166
  return proposals;
167
167
  }
168
168
 
169
- /**
170
- * Scans skill execution logs (if available) and proposes improvements.
171
- */
172
- /**
173
- * Scan skill files for quality issues and propose improvements.
174
- * Looks at skill files that haven't been updated in >30 days and
175
- * checks for common issues: missing examples, vague instructions, outdated commands.
176
- */
177
- export async function generateSkillImprovementProposals(vaultDir) {
178
- const agentDir = path.join(path.dirname(vaultDir));
179
- const skillsDir = path.join(agentDir, 'skills');
180
- const proposals = [];
181
-
182
- if (!fs.existsSync(skillsDir)) return proposals;
183
-
184
- const now = Date.now();
185
- const staleCutoff = 30 * 24 * 60 * 60 * 1000; // 30 days
186
-
187
- try {
188
- const skillDirs = fs.readdirSync(skillsDir, { withFileTypes: true })
189
- .filter(d => d.isDirectory())
190
- .map(d => path.join(skillsDir, d.name, 'SKILL.md'))
191
- .filter(p => fs.existsSync(p));
192
-
193
- for (const skillFile of skillDirs) {
194
- const stat = fs.statSync(skillFile);
195
- const ageMs = now - stat.mtimeMs;
196
- if (ageMs < staleCutoff) continue;
197
-
198
- const raw = fs.readFileSync(skillFile, 'utf8');
199
- const hasExamples = /##\s*example/i.test(raw);
200
- const hasCaution = /caution|warning|never|must not/i.test(raw);
201
- const lineCount = raw.split('\n').length;
202
-
203
- const issues = [];
204
- if (!hasExamples) issues.push('No examples section');
205
- if (!hasCaution) issues.push('No caution/constraint section');
206
- if (lineCount < 20) issues.push('Very short — may lack detail');
207
-
208
- if (issues.length > 0) {
209
- const skillName = path.basename(path.dirname(skillFile));
210
- proposals.push(createProposal(
211
- 'skill-improvement',
212
- `Improve skill: ${skillName} (${issues.join(', ')})`,
213
- skillFile,
214
- `Skill file is ${Math.floor(ageMs / 86400000)} days old. Issues: ${issues.join('; ')}`,
215
- ));
216
- }
217
- }
218
- } catch (err) {
219
- logger.debug('optimizer: generateSkillImprovementProposals failed to read skillsDir', { err: err.message });
220
- }
221
-
222
- return proposals;
223
- }
224
-
225
- /**
226
- * Scan the task queue for stuck or stalled workflows.
227
- * Tasks that have been 'in-progress' for >1 hour are considered stalled.
228
- */
229
- export async function generateWorkflowRepairProposals(vaultDir) {
230
- const queueDir = path.join(brainDir, 'scheduler', 'queue');
231
- const proposals = [];
232
-
233
- if (!fs.existsSync(queueDir)) return proposals;
234
-
235
- const now = Date.now();
236
- const stalledCutoff = 60 * 60 * 1000; // 1 hour
237
-
238
- try {
239
- const files = fs.readdirSync(queueDir).filter(f => f.endsWith('.md'));
240
- for (const file of files) {
241
- const raw = fs.readFileSync(path.join(queueDir, file), 'utf8');
242
- const { data } = matter(raw);
243
- if (data.status !== 'in-progress') continue;
244
-
245
- const updatedAt = new Date(data.updated_at || data.created || 0).getTime();
246
- if (now - updatedAt < stalledCutoff) continue;
247
-
248
- proposals.push(createProposal(
249
- 'workflow-repair',
250
- `Stalled task: ${data.slug || file}`,
251
- path.join(queueDir, file),
252
- `Task has been in-progress for ${Math.floor((now - updatedAt) / 60000)} minutes. May need retry or cancellation.`,
253
- ));
254
- }
255
- } catch (err) {
256
- logger.debug('optimizer: generateWorkflowRepairProposals failed to read queueDir', { err: err.message });
257
- }
258
-
259
- return proposals;
260
- }
261
-
262
- /**
263
- * Analyze the vault's model usage patterns and propose routing improvements.
264
- * Flags cases where high-priority tasks were sent to the local LLM
265
- * that should have used a frontier model.
266
- */
267
- export async function generateModelRoutingProposals(vaultDir) {
268
- const proposals = [];
269
- const nodes = getNodes(vaultDir);
270
-
271
- // Find high-importance facts with low confidence — likely needed frontier model
272
- const underconfident = nodes.filter(n =>
273
- n.status === 'active' &&
274
- n.category === 'facts' &&
275
- n.importance >= 4 &&
276
- n.confidence < 0.6 &&
277
- n.source?.agent === 'fact-seeker',
278
- );
279
-
280
- for (const node of underconfident.slice(0, 5)) {
281
- proposals.push(createProposal(
282
- 'model-routing',
283
- `Re-research with frontier model: "${node.title}"`,
284
- node.slug,
285
- `High-importance fact (importance: ${node.importance}) has low confidence (${node.confidence}). Originally researched by local model. Frontier model may produce better synthesis.`,
286
- ));
287
- }
288
-
289
- return proposals;
290
- }
291
-
292
169
  /**
293
170
  * Nodes that are important but have gone untouched — computed on demand.
294
171
  *
@@ -310,21 +187,6 @@ export function findStaleNodes(vaultDir, { days = 30, minImportance = 4 } = {})
310
187
  .sort((a, b) => new Date(a.last_accessed) - new Date(b.last_accessed));
311
188
  }
312
189
 
313
- /**
314
- * @deprecated Wrote tickets nothing reads. Retained because the dream cycle's
315
- * master switch (ENABLE_STALE_KNOWLEDGE_REFRESH, off) still references it.
316
- * Stale nodes are surfaced to a human via findStaleNodes instead; nothing
317
- * re-researches them automatically (RESEARCH_SYSTEM2).
318
- */
319
- export async function generateStaleKnowledgeRefreshProposals(vaultDir) {
320
- return findStaleNodes(vaultDir, { days: 30, minImportance: 4 }).map(node => createProposal(
321
- 'stale-knowledge-refresh',
322
- `Refresh stale high-importance memory: ${node.title}`,
323
- node.slug,
324
- 'Node has not been accessed in over 30 days but maintains high importance. Requires verification against current state.',
325
- ));
326
- }
327
-
328
190
  /**
329
191
  * Local Eval Gate for Proposal Promotion
330
192
  * Uses the local runtime to evaluate the validity of a proposal.
@@ -379,6 +241,14 @@ export async function evaluateProposalGate(proposal, runtimeConfig, vaultDir = n
379
241
  return stamp('accepted');
380
242
  }
381
243
 
244
+ // smart-decay is verified mechanically against live state: the memory still exists, is
245
+ // active and unprotected, and the access ledger shows no use since it was judged and for
246
+ // the full decay age. Only then may it reach `accepted` and auto-apply (DECAY-01).
247
+ if (proposal.category === 'smart-decay') {
248
+ const reason = verifySmartDecay(proposal, vaultDir);
249
+ return reason ? stamp('rejected', { rejection_reason: reason }) : stamp('accepted');
250
+ }
251
+
382
252
  // stale-knowledge-refresh no longer produces tickets at all — the work is
383
253
  // handed to the research queue, which can actually perform it. If one is
384
254
  // somehow generated, it is not something to auto-accept.
@@ -422,41 +292,67 @@ Output ONLY valid JSON.`;
422
292
  *
423
293
  * @param {string} vaultDir
424
294
  * @param {object} runtimeConfig
425
- * @param {number} [ageThresholdDays=60] Minimum age before considering decay
295
+ * @param {number} [ageThresholdDays=60] Active days of the brain without a use before considering decay
426
296
  * @returns {object[]} decay proposals
427
297
  */
428
- export async function runSmartDecay(vaultDir, runtimeConfig, ageThresholdDays = 60) {
429
- const { callLocalRuntime } = await import('./runtime.mjs');
298
+ /** Active days (days the brain was used), never calendar days: an idle brain never decays. */
299
+ export const DECAY_AGE_DAYS = 60;
300
+
301
+ /**
302
+ * Why a smart-decay proposal may not be applied, or null when live state supports it.
303
+ * Shared by the gate and the applier so both check the same facts.
304
+ */
305
+ export function verifySmartDecay(proposal, vaultDir, { now = Date.now(), ageThresholdDays = DECAY_AGE_DAYS } = {}) {
306
+ if (!vaultDir) return 'No vault to verify against.';
307
+ const slug = String(proposal.target_path || '').trim();
308
+ const node = getNodes(vaultDir).find(n => n.slug === slug);
309
+ if (!node) return `Memory ${slug} no longer exists.`;
310
+ if (node.status !== 'active') return `Memory ${slug} is already ${node.status}.`;
311
+ if (isDecayProtected(node)) return `Memory ${slug} is protected from decay.`;
312
+ const ledger = readAccess(path.join(path.dirname(vaultDir), 'memory-derived'));
313
+ const used = lastUsedMs(node, ledger);
314
+ if (proposal.decay_last_used_at && used > new Date(proposal.decay_last_used_at).getTime()) return `Memory ${slug} was used after it was judged.`;
315
+ if (activeDaysSince(ledger, used) < ageThresholdDays) return `Memory ${slug} was used within the brain's last ${ageThresholdDays} active days.`;
316
+ return null;
317
+ }
318
+
319
+ /** Memories decay never touches: rules, absolute-priority and importance-5 memories. */
320
+ export function isDecayProtected(node) {
321
+ return node.priority === 'absolute' || node.importance >= 5
322
+ || ['invariants', 'preferences', 'anti-patterns'].includes(node.category);
323
+ }
324
+
325
+ /** Active, unprotected memories unused for at least `ageThresholdDays` of the brain's active days. */
326
+ export function decayCandidates(nodes, ledger, { now = Date.now(), ageThresholdDays = DECAY_AGE_DAYS } = {}) {
327
+ return nodes
328
+ .filter(n => n.status === 'active' && n.type !== 'proposal' && !isDecayProtected(n))
329
+ .filter(n => activeDaysSince(ledger, lastUsedMs(n, ledger)) >= ageThresholdDays)
330
+ .sort((a, b) => lastUsedMs(a, ledger) - lastUsedMs(b, ledger));
331
+ }
332
+
333
+ export async function runSmartDecay(vaultDir, runtimeConfig, ageThresholdDays = DECAY_AGE_DAYS, { derivedDir = path.join(path.dirname(vaultDir), 'memory-derived'), call } = {}) {
334
+ const callLocalRuntime = call || (await import('./runtime.mjs')).callLocalRuntime;
430
335
  const nodes = getNodes(vaultDir);
431
336
  const now = Date.now();
432
- const ageThresholdMs = ageThresholdDays * 24 * 60 * 60 * 1000;
337
+ const ledger = readAccess(derivedDir);
433
338
  const proposals = [];
434
339
 
435
- // Candidates: active, not absolute priority, not accessed recently
436
- const candidates = nodes.filter(n => {
437
- if (n.status !== 'active') return false;
438
- if (n.priority === 'absolute') return false;
439
- if (n.importance >= 5) return false;
440
- const lastAccessed = new Date(n.last_accessed || n.updated || 0).getTime();
441
- return (now - lastAccessed) > ageThresholdMs;
442
- });
443
-
444
- // Process up to 5 candidates per run to avoid excessive LLM calls
445
- const batch = candidates.slice(0, 5);
340
+ // Oldest unused first, up to 5 per run to bound model calls.
341
+ const batch = decayCandidates(nodes, ledger, { now, ageThresholdDays }).slice(0, 5);
446
342
 
447
343
  for (const node of batch) {
448
- const ageDays = Math.floor((now - new Date(node.last_accessed || node.updated || 0).getTime()) / (1000 * 60 * 60 * 24));
344
+ const ageDays = activeDaysSince(ledger, lastUsedMs(node, ledger));
449
345
 
450
346
  const prompt = [
451
347
  `## Memory Node: ${node.slug}`,
452
348
  `Title: "${node.title}"`,
453
- `Category: ${node.category} | Importance: ${node.importance} | Age: ${ageDays} days`,
349
+ `Category: ${node.category} | Importance: ${node.importance} | Unused for ${ageDays} active days of its brain`,
454
350
  `Tags: ${(node.tags || []).join(', ')}`,
455
351
  '',
456
352
  '## Body',
457
353
  (node.body || '').slice(0, 800),
458
354
  '',
459
- `This node has not been accessed in ${ageDays} days. Should it be retained, archived, or decayed?`,
355
+ `This node has not been used on the last ${ageDays} days its brain was active. Should it be retained, archived, or decayed?`,
460
356
  ].join('\n');
461
357
 
462
358
  try {
@@ -468,15 +364,20 @@ export async function runSmartDecay(vaultDir, runtimeConfig, ageThresholdDays =
468
364
  const verdict = result.verdict || 'RETAINED';
469
365
 
470
366
  if (verdict !== 'RETAINED') {
471
- proposals.push(createProposal(
472
- 'smart-decay',
473
- `Smart Decay [${verdict}]: "${node.title}"`,
474
- node.slug,
475
- `Age: ${ageDays} days. Reasoning: ${result.reasoning || 'No reasoning provided.'} Confidence adjustment: ${result.confidence_adjustment || 0}`,
476
- ));
367
+ proposals.push({
368
+ ...createProposal(
369
+ 'smart-decay',
370
+ `Smart Decay [${verdict}]: "${node.title}"`,
371
+ node.slug,
372
+ `Unused for ${ageDays} active days. Reasoning: ${result.reasoning || 'No reasoning provided.'} Confidence adjustment: ${result.confidence_adjustment || 0}`,
373
+ ),
374
+ // The gate re-checks that the memory has not been used since this judgement.
375
+ decay_verdict: verdict,
376
+ decay_last_used_at: new Date(lastUsedMs(node, ledger)).toISOString(),
377
+ });
477
378
  }
478
- } catch {
479
- // Skip failed evaluations silently
379
+ } catch (err) {
380
+ logger.warn('optimizer', `Smart decay could not evaluate ${node.slug}: ${err.message}`);
480
381
  }
481
382
  }
482
383
 
@@ -1,7 +1,10 @@
1
1
  /** Host activation policy; never pretends a provider or external daemon is healthy. */
2
- import { getPlugin } from './plugin-loader.mjs';
3
- import { patchPluginRecord, patchProjectActivationRecord, emit, vaultFor, recompileAfterPluginMutation } from './plugin-store.mjs';
4
- import { pluginActivation } from './plugin-state.mjs';
2
+ import path from 'node:path';
3
+ import { getPlugin, globalPluginsDir, projectPluginsDir, vaultForPluginsDir } from './plugin-loader.mjs';
4
+ import { patchPluginRecord, patchProjectActivationRecord, recordPath, emit, vaultFor, recompileAfterPluginMutation } from './plugin-store.mjs';
5
+ import { patchVfsDocument } from './ssss-operation-service.mjs';
6
+ import { pluginActivation, readActivationRecord, usesSharedPluginPolicy } from './plugin-state.mjs';
7
+ import { moveRepositoryPluginState, withRepositoryPluginLifecycle } from './plugin-lifecycle.mjs';
5
8
  import { REQUIRED_INSTALLATION_PLUGIN_IDS } from './default-plugins.mjs';
6
9
  export { pluginActivation };
7
10
 
@@ -14,38 +17,78 @@ export { pluginActivation };
14
17
  * following the shared setting (`clear: true`).
15
18
  */
16
19
  export async function setPluginActive(id, enabled, { projectRoot = process.cwd(), scope = 'project', clear = false } = {}) {
17
- if (enabled === false && REQUIRED_INSTALLATION_PLUGIN_IDS.includes(id)) {
20
+ const onUse = enabled === 'on-use';
21
+ if (onUse && (scope !== 'project' || !projectRoot || projectPluginsDir(projectRoot) === globalPluginsDir() || clear)) {
22
+ throw new Error('On use is available for individual repository brains.');
23
+ }
24
+ if ((enabled === false || onUse) && REQUIRED_INSTALLATION_PLUGIN_IDS.includes(id)) {
18
25
  throw new Error('Required plugin ' + id + ' cannot be disabled');
19
26
  }
20
27
  if (clear === true) {
21
28
  const plugin = getPlugin(id, projectRoot);
22
29
  if (!plugin?.valid) throw new Error(`Plugin ${id} is missing or invalid`);
23
- await patchProjectActivationRecord(id, projectRoot, null);
24
- await emit(vaultFor(plugin), id, 'activation-cleared', { projectRoot });
30
+ await withRepositoryPluginLifecycle(projectRoot, id, async brain => {
31
+ if (!usesSharedPluginPolicy(plugin) || pluginActivation(plugin, { projectRoot: null }).enabled) {
32
+ moveRepositoryPluginState(brain, id, 'restore');
33
+ }
34
+ await patchProjectActivationRecord(id, projectRoot, null);
35
+ await emit(vaultForPluginsDir(projectPluginsDir(projectRoot)), id, 'activation-cleared', {});
36
+ });
25
37
  const compilation = await recompileAfterPluginMutation(projectRoot, false);
26
38
  return { pluginId: id, activation: pluginActivation(plugin, { projectRoot }), scope: 'shared', compilation };
27
39
  }
28
- if (typeof enabled !== 'boolean') throw new Error('Plugin activation requires a boolean');
40
+ if (onUse) enabled = false;
41
+ if (typeof enabled !== 'boolean') throw new Error('Plugin activation requires a boolean or On use');
29
42
  const plugin = getPlugin(id, projectRoot);
30
43
  if (!plugin?.valid) throw new Error(`Plugin ${id} is missing or invalid`);
31
44
  if (scope === 'shared') {
32
- if (plugin.scope !== 'global') throw new Error(`Plugin ${id} is a ${plugin.scope} copy without a shared setting to change`);
33
- await patchPluginRecord(plugin, { runtime_enabled: enabled });
45
+ if (!usesSharedPluginPolicy(plugin)) throw new Error(`Plugin ${id} is a ${plugin.scope} copy without a shared setting to change`);
46
+ await patchPluginRecord(plugin, { runtime_enabled: enabled, runtime_on_use: false });
34
47
  await emit(vaultFor(plugin), id, enabled ? 'activated' : 'deactivated', { enabled, scope: 'shared' });
35
48
  const compilation = await recompileAfterPluginMutation(projectRoot, true);
36
49
  return { pluginId: id, activation: pluginActivation(plugin, { projectRoot }), scope: 'shared', compilation };
37
50
  }
38
51
  // Repository scope. A repository-owned copy already is that repository's setting.
39
- if (plugin.scope !== 'global') {
40
- await patchPluginRecord(plugin, { runtime_enabled: enabled });
41
- await emit(vaultFor(plugin), id, enabled ? 'activated' : 'deactivated', { enabled, scope: plugin.scope });
42
- // A project copy compiles the project's own surface and catalog. Only the
43
- // shared (scaffold) layer also rebuilds every registered project.
44
- const compilation = await recompileAfterPluginMutation(projectRoot, plugin.scope === 'scaffold');
45
- return { pluginId: id, activation: pluginActivation(plugin, { projectRoot }), scope: plugin.scope, compilation };
46
- }
47
- await patchProjectActivationRecord(id, projectRoot, enabled, { manifest: plugin.manifest });
48
- await emit(vaultFor(plugin), id, enabled ? 'activated' : 'deactivated', { enabled, projectRoot, scope: 'project' });
52
+ const event = onUse ? 'activation-on-use' : enabled ? 'activated' : 'deactivated';
53
+ const shared = usesSharedPluginPolicy(plugin);
54
+ const resultScope = shared ? 'project' : plugin.scope;
55
+ const vaultRoot = shared ? vaultForPluginsDir(projectPluginsDir(projectRoot)) : vaultFor(plugin);
56
+ const cleanup = await withRepositoryPluginLifecycle(projectRoot, id, async brain => {
57
+ if (onUse && !brain) throw new Error('On use is available for individual repository brains.');
58
+ let result = enabled ? moveRepositoryPluginState(brain, id, 'restore') : undefined;
59
+ if (shared) await patchProjectActivationRecord(id, projectRoot, enabled, { manifest: plugin.manifest, onUse });
60
+ else await patchPluginRecord(plugin, { runtime_enabled: enabled, runtime_on_use: onUse });
61
+ if (!enabled && !onUse) {
62
+ try { result = moveRepositoryPluginState(brain, id, 'archive'); }
63
+ catch (error) {
64
+ result = { ok: false, state: 'pending', error: error.code ? `Could not archive repository plugin state (${error.code}).` : error.message };
65
+ }
66
+ }
67
+ await emit(vaultRoot, id, event, { enabled, scope: resultScope, ...(result ? { cleanup: result.state } : {}) });
68
+ return result;
69
+ });
70
+ const compilation = await recompileAfterPluginMutation(projectRoot, false);
71
+ return { pluginId: id, activation: pluginActivation(plugin, { projectRoot }), scope: resultScope, compilation, ...(cleanup ? { cleanup } : {}) };
72
+ }
73
+
74
+ /** Claim one armed project policy through the SSSS compare-and-set writer. */
75
+ export async function activatePluginOnUse(id, { projectRoot = process.cwd() } = {}) {
76
+ if (!projectRoot || projectPluginsDir(projectRoot) === globalPluginsDir()) return null;
77
+ const plugin = getPlugin(id, projectRoot);
78
+ if (!plugin?.valid || pluginActivation(plugin, { projectRoot }).mode !== 'on-use') return null;
79
+ const vaultRoot = usesSharedPluginPolicy(plugin) ? vaultForPluginsDir(projectPluginsDir(projectRoot)) : vaultFor(plugin);
80
+ const cleanup = await withRepositoryPluginLifecycle(projectRoot, id, async brain => {
81
+ if (!brain) return null;
82
+ const current = readActivationRecord(path.join(vaultRoot, recordPath(id)), id, { withHash: true });
83
+ if (current?.record.runtime_on_use !== true) return null;
84
+ const result = moveRepositoryPluginState(brain, id, 'restore');
85
+ await patchVfsDocument(recordPath(id), { runtime_enabled: true, runtime_on_use: false, timestamp: new Date().toISOString() }, {
86
+ vaultRoot, expectedBeforeHash: current.hash, intent: 'Activate plugin on deliberate use',
87
+ });
88
+ await emit(vaultRoot, id, 'activated-on-use', { enabled: true, scope: 'project', cleanup: result.state });
89
+ return result;
90
+ });
91
+ if (!cleanup) return null;
49
92
  const compilation = await recompileAfterPluginMutation(projectRoot, false);
50
- return { pluginId: id, activation: pluginActivation(plugin, { projectRoot }), scope: 'project', compilation };
93
+ return { pluginId: id, activation: pluginActivation(plugin, { projectRoot }), scope: 'project', compilation, cleanup };
51
94
  }