total-recall-brain 3.45.0 → 3.46.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (363) hide show
  1. package/bin/total-recall.mjs +7 -2
  2. package/frontend/dist/assets/index-BRYE1jmv.css +1 -0
  3. package/frontend/dist/assets/index-D1goIPjC.js +5598 -0
  4. package/frontend/dist/index.html +2 -2
  5. package/frontend/dist/sw.js +5 -4
  6. package/frontend/src/App.tsx +183 -236
  7. package/frontend/src/api/auth-inventory.ts +62 -0
  8. package/frontend/src/api/memory.ts +13 -8
  9. package/frontend/src/api/plugin-credentials.ts +23 -0
  10. package/frontend/src/api/plugins.ts +37 -53
  11. package/frontend/src/api/workspace-drawers.ts +97 -0
  12. package/frontend/src/components/BrainSelector.tsx +9 -3
  13. package/frontend/src/components/plugins/PluginPanel.tsx +83 -42
  14. package/frontend/src/components/plugins/PluginSettings.tsx +10 -20
  15. package/frontend/src/components/plugins/PluginWorkspace.tsx +367 -0
  16. package/frontend/src/components/plugins/ScopedPluginCommands.tsx +142 -0
  17. package/frontend/src/components/plugins/plugin-frame.ts +44 -33
  18. package/frontend/src/components/plugins/scoped-plugin-operations.ts +28 -0
  19. package/frontend/src/components/workspace/DrawerChat.tsx +110 -0
  20. package/frontend/src/components/workspace/DrawerLayout.tsx +196 -0
  21. package/frontend/src/components/workspace/DrawerMarkup.tsx +49 -0
  22. package/frontend/src/components/workspace/PluginChunkLibrary.tsx +92 -0
  23. package/frontend/src/components/workspace/PluginHome.tsx +76 -0
  24. package/frontend/src/components/workspace/WorkspaceDrawers.tsx +96 -0
  25. package/frontend/src/components/workspace/WorkspacePage.tsx +95 -0
  26. package/frontend/src/components/workspace/usePluginDesign.ts +38 -0
  27. package/frontend/src/index.css +409 -136
  28. package/frontend/src/pages/HelpPage.tsx +15 -46
  29. package/frontend/src/pages/MemoryPage.tsx +19 -36
  30. package/frontend/src/pages/SettingsPage.tsx +1 -1
  31. package/metadata.plugin.schema.json +53 -227
  32. package/package.json +1 -1
  33. package/scaffold/.agent/skills/plugin/SKILL.md +13 -11
  34. package/scaffold/.agent/skills/plugin/evals/README.md +14 -21
  35. package/scaffold/.agent/skills/plugin/evals/cases/01-fabricated-seed.mjs +15 -18
  36. package/scaffold/.agent/skills/plugin/evals/cases/02-reassuring-default.mjs +19 -24
  37. package/scaffold/.agent/skills/plugin/evals/cases/03-declared-template.mjs +12 -20
  38. package/scaffold/.agent/skills/plugin/evals/cases/04-home-collects-credential.mjs +35 -0
  39. package/scaffold/.agent/skills/plugin/evals/cases/05-host-leak-in-scaffold.mjs +8 -28
  40. package/scaffold/.agent/skills/plugin/evals/cases/06-correct-plugin.mjs +85 -47
  41. package/scaffold/.agent/skills/plugin/evals/run.mjs +17 -18
  42. package/scaffold/.agent/skills/plugin/prompts/05-credentials.md +4 -4
  43. package/scaffold/.agent/skills/plugin/prompts/07-scaffold.md +2 -2
  44. package/scaffold/.agent/skills/plugin/prompts/08-verify.md +4 -4
  45. package/scaffold/.agent/skills/plugin/references/anti-patterns.md +2 -2
  46. package/scaffold/.agent/skills/plugin/references/generative-home.md +38 -0
  47. package/scaffold/.agent/skills/plugin/references/scaffold.md +5 -5
  48. package/scaffold/.agent/skills/plugin/references/surfaces.md +13 -4
  49. package/scaffold/.agent/skills/plugin/scripts/record.mjs +75 -153
  50. package/scaffold/.agent/skills/plugin/subagents/plugin-reviewer.md +4 -4
  51. package/scaffold/.agent/skills/total-recall/plugins/auth/DESIGN.md +76 -0
  52. package/scaffold/.agent/skills/total-recall/plugins/auth/README.md +3 -1
  53. package/scaffold/.agent/skills/total-recall/plugins/auth/cli.mjs +20 -15
  54. package/scaffold/.agent/skills/total-recall/plugins/auth/package-lock.json +6 -6
  55. package/scaffold/.agent/skills/total-recall/plugins/auth/package.json +2 -1
  56. package/scaffold/.agent/skills/total-recall/plugins/auth/plugin.json +13 -51
  57. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/access-overview.md +17 -0
  58. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/agent-login.md +20 -0
  59. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/baseline-audit.md +22 -0
  60. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/credential-repository-binding.md +17 -0
  61. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/credential-store-maintenance.md +17 -0
  62. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/dashboard-password.md +18 -0
  63. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/dashboard-sessions.md +18 -0
  64. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/endpoint-probe.md +23 -0
  65. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/google-sign-in.md +21 -0
  66. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/mesh-machines.md +16 -0
  67. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/passkey-enrollment.md +21 -0
  68. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/registered-brains.md +16 -0
  69. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/scoped-access-keys.md +17 -0
  70. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/secret-inventory.md +25 -0
  71. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/signing-key-rotation.md +22 -0
  72. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/home.js +375 -0
  73. package/scaffold/.agent/skills/total-recall/plugins/automations/{ui/DESIGN.md → DESIGN.md} +32 -21
  74. package/scaffold/.agent/skills/total-recall/plugins/automations/adapters.mjs +17 -1
  75. package/scaffold/.agent/skills/total-recall/plugins/automations/cli.mjs +10 -1
  76. package/scaffold/.agent/skills/total-recall/plugins/automations/package-lock.json +6 -6
  77. package/scaffold/.agent/skills/total-recall/plugins/automations/package.json +4 -3
  78. package/scaffold/.agent/skills/total-recall/plugins/automations/plugin.json +11 -27
  79. package/scaffold/.agent/skills/total-recall/plugins/automations/ui/automations.js +2 -2
  80. package/scaffold/.agent/skills/total-recall/plugins/automations/ui/chunks/control-ledger.md +42 -0
  81. package/scaffold/.agent/skills/total-recall/plugins/automations/ui/chunks/local-inventory-triage.md +17 -0
  82. package/scaffold/.agent/skills/total-recall/plugins/automations/ui/chunks/pending-control.md +39 -0
  83. package/scaffold/.agent/skills/total-recall/plugins/automations/ui/chunks/run-evidence.md +41 -0
  84. package/scaffold/.agent/skills/total-recall/plugins/automations/ui/chunks/schedule-vs-run-evidence.md +17 -0
  85. package/scaffold/.agent/skills/total-recall/plugins/automations/ui/chunks/source-coverage.md +41 -0
  86. package/scaffold/.agent/skills/total-recall/plugins/automations/ui/{card.js → home.js} +32 -27
  87. package/scaffold/.agent/skills/total-recall/plugins/code-quality/DESIGN.md +53 -0
  88. package/scaffold/.agent/skills/total-recall/plugins/code-quality/cli.mjs +23 -0
  89. package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/run-core.mjs +4 -2
  90. package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/check.mjs +4 -2
  91. package/scaffold/.agent/skills/total-recall/plugins/code-quality/package-lock.json +6 -6
  92. package/scaffold/.agent/skills/total-recall/plugins/code-quality/package.json +2 -1
  93. package/scaffold/.agent/skills/total-recall/plugins/code-quality/plugin.json +40 -40
  94. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/config.md +15 -0
  95. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/detect.md +15 -0
  96. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/gate.md +15 -0
  97. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/init.md +15 -0
  98. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/report.md +17 -0
  99. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/run.md +17 -0
  100. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/snapshot.md +15 -0
  101. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/code-quality-report.mjs +55 -23
  102. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/home.mjs +214 -0
  103. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/DESIGN.md +146 -0
  104. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package-lock.json +6 -6
  105. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package.json +2 -1
  106. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/plugin.json +51 -34
  107. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/command.mjs +1 -1
  108. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/create.md +35 -0
  109. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/history.md +35 -0
  110. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/library.md +49 -0
  111. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/list.md +42 -0
  112. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/promote.md +40 -0
  113. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/read.md +25 -0
  114. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/remove.md +26 -0
  115. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/rollback.md +26 -0
  116. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/show.md +53 -0
  117. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/status.md +40 -0
  118. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/test-path.md +38 -0
  119. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/test.md +46 -0
  120. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/update.md +32 -0
  121. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/home.js +533 -0
  122. package/scaffold/.agent/skills/total-recall/plugins/embeddings/DESIGN.md +110 -0
  123. package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +6 -6
  124. package/scaffold/.agent/skills/total-recall/plugins/embeddings/package.json +2 -1
  125. package/scaffold/.agent/skills/total-recall/plugins/embeddings/plugin.json +13 -10
  126. package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/chunks/benchmark.md +49 -0
  127. package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/chunks/coverage.md +53 -0
  128. package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/chunks/models.md +43 -0
  129. package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/chunks/pull.md +41 -0
  130. package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/chunks/status.md +59 -0
  131. package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/home.js +353 -0
  132. package/scaffold/.agent/skills/total-recall/plugins/git/DESIGN.md +31 -0
  133. package/scaffold/.agent/skills/total-recall/plugins/git/README.md +25 -1
  134. package/scaffold/.agent/skills/total-recall/plugins/git/cli.mjs +5 -1
  135. package/scaffold/.agent/skills/total-recall/plugins/git/package-lock.json +6 -6
  136. package/scaffold/.agent/skills/total-recall/plugins/git/package.json +2 -1
  137. package/scaffold/.agent/skills/total-recall/plugins/git/plugin.json +17 -27
  138. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/commit-memory.mjs +30 -6
  139. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/fleet.mjs +24 -6
  140. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/secret-firewall.mjs +33 -7
  141. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/undo.mjs +29 -5
  142. package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/unmerged.mjs +182 -0
  143. package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/change-comparison.md +27 -0
  144. package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/commit-identity.md +22 -0
  145. package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/create-worktree.md +25 -0
  146. package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/registered-worktrees.md +22 -0
  147. package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/remove-worktree.md +23 -0
  148. package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/repository-ledger.md +25 -0
  149. package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/unmerged-work.md +22 -0
  150. package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/upstream-divergence.md +17 -0
  151. package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/worktree-conflict-triage.md +17 -0
  152. package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/worktree-terminal.md +22 -0
  153. package/scaffold/.agent/skills/total-recall/plugins/git/ui/home.js +273 -0
  154. package/scaffold/.agent/skills/total-recall/plugins/mesh/DESIGN.md +35 -0
  155. package/scaffold/.agent/skills/total-recall/plugins/mesh/README.md +4 -5
  156. package/scaffold/.agent/skills/total-recall/plugins/mesh/cli.mjs +9 -3
  157. package/scaffold/.agent/skills/total-recall/plugins/mesh/package-lock.json +6 -6
  158. package/scaffold/.agent/skills/total-recall/plugins/mesh/package.json +2 -1
  159. package/scaffold/.agent/skills/total-recall/plugins/mesh/plugin.json +37 -39
  160. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/cli/mesh.mjs +5 -3
  161. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/active-device-dispatch.md +22 -0
  162. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/control-probe.md +25 -0
  163. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/control-server.md +22 -0
  164. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/credential-readiness.md +23 -0
  165. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/elected-leader.md +22 -0
  166. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/enrollment-key.md +22 -0
  167. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/file-receipt.md +25 -0
  168. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/fleet-execution.md +23 -0
  169. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/fleet-presence.md +23 -0
  170. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/interactive-ssh.md +22 -0
  171. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/local-enrollment.md +23 -0
  172. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/mesh-policy.md +23 -0
  173. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/network-atlas.md +25 -0
  174. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/node-roster.md +26 -0
  175. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/node-selection-proof.md +17 -0
  176. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/owner-observations.md +23 -0
  177. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/peer-path.md +23 -0
  178. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/pinned-leader.md +24 -0
  179. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/reachability-evidence-ladder.md +17 -0
  180. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/recorded-login.md +23 -0
  181. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/remote-execution.md +22 -0
  182. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/secret-grants.md +26 -0
  183. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/{card.js → home.js} +93 -22
  184. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/DESIGN.md +86 -0
  185. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/README.md +19 -0
  186. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/cli.mjs +5 -1
  187. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package-lock.json +6 -6
  188. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package.json +2 -1
  189. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/plugin.json +43 -22
  190. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/chat.mjs +80 -0
  191. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/adapter-docs-freshness.md +16 -0
  192. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/chat-handoff.md +26 -0
  193. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/fleet-maintenance.md +21 -0
  194. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/harness-inventory.md +21 -0
  195. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/included-access.md +18 -0
  196. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/included-usage-verification.md +15 -0
  197. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/independent-review.md +17 -0
  198. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/managed-execution.md +20 -0
  199. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/official-documentation.md +18 -0
  200. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/quota-recovery.md +21 -0
  201. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/review-routing-decision.md +16 -0
  202. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/route-work.md +18 -0
  203. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/routing-evidence.md +18 -0
  204. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/run-ledger.md +18 -0
  205. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/home.js +198 -0
  206. package/scaffold/.agent/skills/total-recall/plugins/openwiki/DESIGN.md +75 -0
  207. package/scaffold/.agent/skills/total-recall/plugins/openwiki/README.md +2 -0
  208. package/scaffold/.agent/skills/total-recall/plugins/openwiki/cli.mjs +17 -14
  209. package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +6 -6
  210. package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +2 -1
  211. package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +10 -44
  212. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/generate.mjs +5 -4
  213. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/graph.mjs +3 -4
  214. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/ingest.mjs +23 -18
  215. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/mesh-vaults.mjs +8 -3
  216. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/refresh.mjs +6 -3
  217. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/search.mjs +3 -5
  218. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/token.mjs +21 -47
  219. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/upstream-watcher.mjs +17 -6
  220. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/auto-refresh.md +18 -0
  221. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/bootstrap-pages.md +18 -0
  222. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/generate-wiki.md +21 -0
  223. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/generator-settings.md +18 -0
  224. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/host-probe.md +18 -0
  225. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/ingest-preview.md +18 -0
  226. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/link-hygiene.md +21 -0
  227. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/link-topology.md +19 -0
  228. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/passage-search.md +19 -0
  229. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/propagation-plan.md +18 -0
  230. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/provider-credentials.md +18 -0
  231. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/refresh-report.md +18 -0
  232. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/task-evidence.md +22 -0
  233. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/upstream-record.md +18 -0
  234. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/watch-upstream.md +18 -0
  235. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/wiki-atlas.md +19 -0
  236. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/panel.js +261 -513
  237. package/scaffold/.agent/skills/total-recall/plugins/security/DESIGN.md +84 -0
  238. package/scaffold/.agent/skills/total-recall/plugins/security/README.md +4 -2
  239. package/scaffold/.agent/skills/total-recall/plugins/security/cli.mjs +7 -19
  240. package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +6 -6
  241. package/scaffold/.agent/skills/total-recall/plugins/security/package.json +2 -3
  242. package/scaffold/.agent/skills/total-recall/plugins/security/plugin.json +19 -52
  243. package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/device-evidence.md +21 -0
  244. package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/evidence-desk.md +18 -0
  245. package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/guided-device-review.md +17 -0
  246. package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/host-and-network.md +21 -0
  247. package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/https-headers.md +17 -0
  248. package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/mesh-coverage.md +18 -0
  249. package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/private-security-tools.md +16 -0
  250. package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/single-origin-header-check.md +17 -0
  251. package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/source-review.md +21 -0
  252. package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/ssh-permission-plan.md +17 -0
  253. package/scaffold/.agent/skills/total-recall/plugins/security/ui/home.js +283 -0
  254. package/scaffold/.agent/skills/total-recall/plugins/ssss/DESIGN.md +93 -0
  255. package/scaffold/.agent/skills/total-recall/plugins/ssss/package-lock.json +2 -2
  256. package/scaffold/.agent/skills/total-recall/plugins/ssss/package.json +2 -1
  257. package/scaffold/.agent/skills/total-recall/plugins/ssss/plugin.json +5 -3
  258. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/conformance.md +26 -0
  259. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/registry.md +37 -0
  260. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/repair.md +42 -0
  261. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/scaffold.md +33 -0
  262. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/status.md +43 -0
  263. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/validate.md +34 -0
  264. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/validation-diagnostics.md +17 -0
  265. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/workspace-registry-preflight.md +17 -0
  266. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/home.js +348 -0
  267. package/scaffold/.agent/skills/total-recall/references/operations.md +2 -0
  268. package/src/cli/api.mjs +3 -3
  269. package/src/cli/brief.mjs +3 -0
  270. package/src/cli/config.mjs +16 -0
  271. package/src/cli/connect.mjs +2 -7
  272. package/src/cli/plugin/chunks.mjs +145 -0
  273. package/src/cli/plugin/create.mjs +24 -52
  274. package/src/cli/plugin/index.mjs +20 -9
  275. package/src/cli/setup.mjs +2 -9
  276. package/src/cli/start.mjs +1 -0
  277. package/src/cli/startup.mjs +18 -0
  278. package/src/core/app-deploy/design-tokens.mjs +14 -9
  279. package/src/core/app-deploy/ui-elements.mjs +1 -1
  280. package/src/core/brain-registry.mjs +5 -0
  281. package/src/core/brain-updates.mjs +5 -1
  282. package/src/core/daemon-control.mjs +5 -1
  283. package/src/core/dashboard-bundle.mjs +261 -0
  284. package/src/core/default-plugin-refresh.mjs +39 -0
  285. package/src/core/ensure-dependencies.mjs +19 -15
  286. package/src/core/memory-brain.mjs +30 -8
  287. package/src/core/plugin-activation.mjs +63 -20
  288. package/src/core/plugin-contracts/{card.mjs → manifest.mjs} +23 -57
  289. package/src/core/plugin-contracts/startup-check.mjs +49 -0
  290. package/src/core/plugin-contracts/ui.mjs +9 -3
  291. package/src/core/plugin-distribution.mjs +6 -6
  292. package/src/core/plugin-lifecycle.mjs +58 -0
  293. package/src/core/plugin-loader.mjs +9 -5
  294. package/src/core/plugin-runner.mjs +21 -10
  295. package/src/core/plugin-startup-checks.mjs +44 -0
  296. package/src/core/plugin-state.mjs +19 -5
  297. package/src/core/plugin-store.mjs +30 -11
  298. package/src/core/project-id.mjs +15 -5
  299. package/src/core/required-plugins.mjs +31 -6
  300. package/src/core/schema.mjs +2 -0
  301. package/src/core/ssss-operation-service.mjs +1 -0
  302. package/src/core/startup-health.mjs +28 -3
  303. package/src/core/testing/plugin-fixture.mjs +22 -21
  304. package/src/core/throttled-fetch.mjs +25 -18
  305. package/src/core/ui-chunk-choice.mjs +77 -0
  306. package/src/core/ui-chunk.registry.json +23 -0
  307. package/src/core/ui-chunks.mjs +434 -0
  308. package/src/core/vault-cache.mjs +58 -59
  309. package/src/core/workspace-compositions.mjs +151 -0
  310. package/src/core/workspace-drawers.mjs +79 -0
  311. package/src/server/api.mjs +4 -27
  312. package/src/server/index.mjs +28 -21
  313. package/src/server/memory-events.mjs +31 -0
  314. package/src/server/plugin-brain-targets.mjs +89 -0
  315. package/src/server/rest.mjs +2 -0
  316. package/src/server/route-inventory.mjs +3 -2
  317. package/src/server/route-manifest.json +52 -4
  318. package/src/server/routes/_shared.mjs +7 -14
  319. package/src/server/routes/brains.mjs +29 -44
  320. package/src/server/routes/dashboard.mjs +26 -0
  321. package/src/server/routes/plugins.mjs +154 -45
  322. package/src/server/routes/ui-chunks.mjs +192 -0
  323. package/frontend/dist/assets/index-DACh1dhx.js +0 -5613
  324. package/frontend/dist/assets/index-DtwsDyFM.css +0 -1
  325. package/frontend/src/components/plugins/card/CardEngine.tsx +0 -108
  326. package/frontend/src/components/plugins/card/CardScenes.tsx +0 -103
  327. package/frontend/src/components/plugins/card/ExecutionPicker.tsx +0 -64
  328. package/frontend/src/components/plugins/card/args.ts +0 -28
  329. package/frontend/src/components/plugins/card/card.css +0 -32
  330. package/frontend/src/components/plugins/card/cli-line.ts +0 -13
  331. package/frontend/src/components/plugins/card/contract.ts +0 -91
  332. package/frontend/src/components/plugins/card/format.ts +0 -49
  333. package/frontend/src/components/plugins/card/runner-context.ts +0 -11
  334. package/frontend/src/components/plugins/card/runner.tsx +0 -43
  335. package/frontend/src/components/plugins/card/theme.ts +0 -35
  336. package/frontend/src/components/plugins/card/use-source.ts +0 -22
  337. package/frontend/src/components/plugins/card/widgets.tsx +0 -94
  338. package/frontend/src/components/plugins/workspace/WorkspaceHost.tsx +0 -23
  339. package/frontend/src/pages/PluginsPage.tsx +0 -1719
  340. package/scaffold/.agent/skills/plugin/evals/cases/04-card-collects-credential.mjs +0 -47
  341. package/scaffold/.agent/skills/plugin/prompts/06-card.md +0 -46
  342. package/scaffold/.agent/skills/plugin/references/card.md +0 -90
  343. package/scaffold/.agent/skills/plugin/subagents/card-designer.md +0 -60
  344. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/DESIGN.md +0 -67
  345. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/card.js +0 -1179
  346. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/DESIGN.md +0 -32
  347. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/card.js +0 -588
  348. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/card.js +0 -575
  349. package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/DESIGN.md +0 -32
  350. package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/card.js +0 -482
  351. package/scaffold/.agent/skills/total-recall/plugins/git/ui/DESIGN.md +0 -100
  352. package/scaffold/.agent/skills/total-recall/plugins/git/ui/card.js +0 -637
  353. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/DESIGN.md +0 -217
  354. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/DESIGN.md +0 -32
  355. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/card.js +0 -1276
  356. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/DESIGN.md +0 -45
  357. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/card.js +0 -1401
  358. package/scaffold/.agent/skills/total-recall/plugins/security/scripts/build-ui.mjs +0 -4
  359. package/scaffold/.agent/skills/total-recall/plugins/security/ui/DESIGN.md +0 -41
  360. package/scaffold/.agent/skills/total-recall/plugins/security/ui/card.js +0 -396
  361. package/scaffold/.agent/skills/total-recall/plugins/security/ui/panel.js +0 -98
  362. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/DESIGN.md +0 -15
  363. package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/card.js +0 -182
@@ -31,8 +31,8 @@ import {
31
31
  } from './plugin-bundle.mjs';
32
32
  import { writeVfsDocument, patchVfsDocument, deleteVfsDocument, appendVfsEvent } from './ssss-operation-service.mjs';
33
33
  import { findVfsDocumentByPath } from './vfs-documents.mjs';
34
- import { readActivationRecord, projectActivationOverride } from './plugin-state.mjs';
35
- import { REQUIRED_INSTALLATION_PLUGIN_IDS } from './default-plugins.mjs';
34
+ import { readActivationRecord, projectActivationOverride, pluginActivation } from './plugin-state.mjs';
35
+ import { DEFAULT_PLUGIN_IDS, REQUIRED_INSTALLATION_PLUGIN_IDS } from './default-plugins.mjs';
36
36
 
37
37
  const execFileAsync = promisify(execFile);
38
38
  const ID_PATTERN = /^[a-z][a-z0-9-]{1,63}$/;
@@ -183,7 +183,7 @@ export async function patchPluginRecord(plugin, patches) {
183
183
  * setting so it follows the shared one again. The overlay never installs the
184
184
  * plugin: a repository without it uses the shared folder's copy.
185
185
  */
186
- export async function patchProjectActivationRecord(id, projectRoot, runtimeEnabled, { manifest = null } = {}) {
186
+ export async function patchProjectActivationRecord(id, projectRoot, runtimeEnabled, { manifest = null, onUse = false } = {}) {
187
187
  if (!ID_PATTERN.test(id)) throw new Error('Invalid plugin identity');
188
188
  const pluginsDir = projectPluginsDir(projectRoot);
189
189
  const vaultRoot = vaultForPluginsDir(pluginsDir);
@@ -194,7 +194,7 @@ export async function patchProjectActivationRecord(id, projectRoot, runtimeEnabl
194
194
  if (runtimeEnabled === null) {
195
195
  const existing = readProjectActivationRecord(id, projectRoot);
196
196
  if (!existing) return null;
197
- const { runtime_enabled: _dropped, ...frontmatter } = { ...(existing.frontmatter || {}) };
197
+ const { runtime_enabled: _dropped, runtime_on_use: _onUse, ...frontmatter } = { ...(existing.frontmatter || {}) };
198
198
  await writeVfsDocument(recordPath(id), {
199
199
  ...frontmatter,
200
200
  type: 'plugin_record',
@@ -210,7 +210,7 @@ export async function patchProjectActivationRecord(id, projectRoot, runtimeEnabl
210
210
  if (typeof runtimeEnabled !== 'boolean') throw new Error('Plugin activation requires a boolean or null');
211
211
  const previous = readProjectActivationRecord(id, projectRoot);
212
212
  if (previous) {
213
- return patchVfsDocument(recordPath(id), { runtime_enabled: runtimeEnabled, timestamp: now }, {
213
+ return patchVfsDocument(recordPath(id), { runtime_enabled: runtimeEnabled, runtime_on_use: onUse, timestamp: now }, {
214
214
  actorRole: 'system',
215
215
  intent: `Set repository activation for plugin ${id}`,
216
216
  vaultRoot
@@ -226,6 +226,7 @@ export async function patchProjectActivationRecord(id, projectRoot, runtimeEnabl
226
226
  category: 'plugins',
227
227
  tags: ['plugin', 'activation-override'],
228
228
  runtime_enabled: runtimeEnabled,
229
+ runtime_on_use: onUse,
229
230
  source: { kind: 'shared', ref: globalPluginsDir() }
230
231
  };
231
232
  await writeVfsDocument(recordPath(id), frontmatter, buildPluginDocumentBody(m, id), {
@@ -236,8 +237,19 @@ export async function patchProjectActivationRecord(id, projectRoot, runtimeEnabl
236
237
  return frontmatter;
237
238
  }
238
239
 
240
+ function describeActivation(plugin, projectRoot) {
241
+ const required = REQUIRED_INSTALLATION_PLUGIN_IDS.includes(plugin.id);
242
+ try {
243
+ const { state, enabled, mode } = pluginActivation(plugin, { projectRoot });
244
+ return { state, enabled, required, ...(mode ? { mode } : {}) };
245
+ } catch {
246
+ // An unreadable repository policy is an error, never shared enablement.
247
+ return { state: 'error', enabled: false, required };
248
+ }
249
+ }
250
+
239
251
  /** Facts about one installed plugin, merged with its record. */
240
- export function describePlugin(plugin) {
252
+ export function describePlugin(plugin, projectRoot = process.cwd()) {
241
253
  const record = readPluginRecord(plugin);
242
254
  const m = plugin.manifest || {};
243
255
  let current = null;
@@ -248,6 +260,7 @@ export function describePlugin(plugin) {
248
260
  const taskRuns = record?.task_runs || {};
249
261
  return {
250
262
  id: plugin.id,
263
+ core: DEFAULT_PLUGIN_IDS.includes(plugin.id),
251
264
  name: m.name || plugin.id,
252
265
  version: m.version || '0.0.0',
253
266
  description: m.description || '',
@@ -284,13 +297,18 @@ export function describePlugin(plugin) {
284
297
  ui: m.ui || null,
285
298
  configuration: m.configuration || null,
286
299
  manifest: m,
287
- card_face: record?.card_face || m.card_face || null,
288
- activation: { state: plugin.valid && record?.runtime_enabled !== false ? 'active' : 'inactive', enabled: plugin.valid && record?.runtime_enabled !== false }
300
+ activation: describeActivation(plugin, projectRoot)
289
301
  };
290
302
  }
291
303
 
292
- export function listInstalledPlugins(projectRoot = process.cwd()) {
293
- return discoverPlugins(projectRoot).map(describePlugin);
304
+ export function listInstalledPlugins(projectRoot = process.cwd(), { descriptions } = {}) {
305
+ return discoverPlugins(projectRoot).map(plugin => {
306
+ const description = descriptions?.get(plugin.dir);
307
+ if (description) return { ...description, activation: describeActivation(plugin, projectRoot) };
308
+ const result = describePlugin(plugin, projectRoot);
309
+ descriptions?.set(plugin.dir, result);
310
+ return result;
311
+ });
294
312
  }
295
313
 
296
314
  /** Bundled plugins shipped in the package, flagged with whether they are installed here. */
@@ -344,7 +362,8 @@ export async function recompileAfterPluginMutation(projectRoot = process.cwd(),
344
362
  // read on demand at preflight, so a shared plugin change compiles no other repository
345
363
  // (ON-DEMAND-CONTEXT-01; one install used to rebuild all registered brains).
346
364
  await compileLayer(compileSurface, projectRoot, isGlobal);
347
- if (isGlobal) {
365
+ // A shared change made from the dashboard's global brain has no repository to refresh.
366
+ if (isGlobal && projectRoot) {
348
367
  // The repository the command ran in refreshes its own catalog now, so its plugin library
349
368
  // never lags the change. That compile is cheap: entry files and vault indexes keep their
350
369
  // own triggers, so only the catalog and CLI index move.
@@ -7,6 +7,8 @@ import fs from 'node:fs';
7
7
  import path from 'node:path';
8
8
  import { randomUUID } from 'node:crypto';
9
9
 
10
+ const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
11
+
10
12
  function brainJsonPath(brainDir) {
11
13
  return path.join(brainDir, 'config', 'brain.json');
12
14
  }
@@ -35,21 +37,21 @@ export function readProjectId(brainDir) {
35
37
  * Return the project's id, creating a random UUID on first use.
36
38
  * @returns {{ project_id: string, created: boolean }}
37
39
  */
38
- export function ensureProjectId(brainDir) {
40
+ export function ensureProjectId(brainDir, { fallbackId } = {}) {
39
41
  const file = brainJsonPath(brainDir);
40
42
  let cfg;
41
43
  try { cfg = JSON.parse(fs.readFileSync(file, 'utf8')); }
42
44
  catch (err) { if (err.code === 'ENOENT') cfg = {}; else throw new Error('Existing project identity is unreadable or malformed'); }
43
45
  if (!cfg || typeof cfg !== 'object' || Array.isArray(cfg)) throw new Error('Existing project identity must be an object');
44
46
  if (cfg.project_id) return { project_id: cfg.project_id, created: false };
45
- cfg.project_id = randomUUID();
47
+ cfg.project_id = fallbackId || randomUUID();
46
48
  writeJsonAtomic(file, cfg);
47
49
  return { project_id: cfg.project_id, created: true };
48
50
  }
49
51
 
50
52
  /**
51
- * Give every registered project an id and make the registry agree with each
52
- * project's brain.json (the brain.json id wins).
53
+ * Make the registry agree with each project's brain.json. If the disk identity
54
+ * is missing, preserve a unique valid UUID already published in the registry.
53
55
  * @returns {{ total: number, created: number, synced: number, missing_brain: number }}
54
56
  */
55
57
  export function repairProjectIds(globalBrainDir) {
@@ -57,13 +59,21 @@ export function repairProjectIds(globalBrainDir) {
57
59
  const list = readJson(registryPath, []);
58
60
  const out = { total: 0, created: 0, synced: 0, missing_brain: 0 };
59
61
  if (!Array.isArray(list)) return out;
62
+ const owners = new Map();
63
+ for (const row of list) {
64
+ const onDisk = typeof row?.brainDir === 'string' ? readProjectId(row.brainDir) : null;
65
+ for (const id of new Set([row?.project_id, onDisk])) {
66
+ if (typeof id === 'string' && UUID.test(id)) owners.set(id, (owners.get(id) || 0) + 1);
67
+ }
68
+ }
60
69
  for (const row of list) {
61
70
  out.total++;
62
71
  if (!row?.brainDir || !fs.existsSync(row.brainDir)) {
63
72
  out.missing_brain++;
64
73
  continue;
65
74
  }
66
- const { project_id, created } = ensureProjectId(row.brainDir);
75
+ const fallbackId = owners.get(row.project_id) === 1 ? row.project_id : undefined;
76
+ const { project_id, created } = ensureProjectId(row.brainDir, { fallbackId });
67
77
  if (created) out.created++;
68
78
  if (row.project_id !== project_id) {
69
79
  row.project_id = project_id;
@@ -10,7 +10,7 @@
10
10
  import fs from 'node:fs';
11
11
  import path from 'node:path';
12
12
  import { createRequire } from 'node:module';
13
- import { readPluginsDir, globalPluginsDir, PACKAGE_ROOT, resolvePluginFile } from './plugin-loader.mjs';
13
+ import { readPluginsDir, globalPluginsDir, PACKAGE_ROOT, resolvePluginFile, validatePluginManifest } from './plugin-loader.mjs';
14
14
  import { DEFAULT_PLUGIN_IDS, REQUIRED_INSTALLATION_PLUGIN_IDS } from './default-plugins.mjs';
15
15
  import { comparePackageVersions } from './package-version.mjs';
16
16
  import { preparePluginDependencies } from './plugin-dependencies.mjs';
@@ -113,14 +113,39 @@ export function inspectRequiredPlugins(brainDir, { pluginsDir, sharedBrainDir =
113
113
  return { status: errors.length ? 'failed' : 'ready', errors, source, installations };
114
114
  }
115
115
 
116
- /** Only a copy that declares an older release than the bundled one is refreshed; damaged same-release copies still fail. */
116
+ const LEGACY_PRESENTATION_FIELDS = ['card', 'card_face', 'card_layout'];
117
+
118
+ function hasLegacyPresentation(manifest) {
119
+ return LEGACY_PRESENTATION_FIELDS.some(field => Object.hasOwn(manifest, field))
120
+ || (Array.isArray(manifest.ui?.elements) && manifest.ui.elements.some(element => ['card', 'card-face'].includes(element?.kind)));
121
+ }
122
+
123
+ function hasPluginFiles(directory, files) {
124
+ return files.every(file => fs.statSync(resolvePluginFile(directory, file)).isFile());
125
+ }
126
+
127
+ function runtimeFiles(manifest) {
128
+ return ['package.json', ...(manifest.cli ? [manifest.cli.handler] : []), ...Object.values(manifest.runtime?.modules || {})];
129
+ }
130
+
131
+ /** Refresh older copies and migrate the retired presentation of an otherwise intact same-release copy. */
117
132
  function isStaleInstall(target, source) {
118
133
  try {
119
134
  // A link is its owner's checkout. Replacing it would delete that repository's files, .git included.
120
135
  if (fs.lstatSync(target).isSymbolicLink()) return false;
121
- const installed = JSON.parse(fs.readFileSync(path.join(target, 'plugin.json'), 'utf8')).version;
122
- const bundled = JSON.parse(fs.readFileSync(path.join(source, 'plugin.json'), 'utf8')).version;
123
- return comparePackageVersions(installed, bundled) === -1;
136
+ const installed = JSON.parse(fs.readFileSync(path.join(target, 'plugin.json'), 'utf8'));
137
+ const bundled = JSON.parse(fs.readFileSync(path.join(source, 'plugin.json'), 'utf8'));
138
+ if (comparePackageVersions(installed.version, bundled.version) === -1) return true;
139
+ if (installed.version !== bundled.version || installed.id !== bundled.id || bundled.id !== path.basename(source)
140
+ || installed.cli?.command !== bundled.cli?.command || !hasLegacyPresentation(installed)
141
+ || hasLegacyPresentation(bundled) || !bundled.ui?.home || !validatePluginManifest(bundled).valid) return false;
142
+
143
+ const migrated = { ...installed, ui: bundled.ui };
144
+ for (const field of LEGACY_PRESENTATION_FIELDS) delete migrated[field];
145
+ if (!validatePluginManifest(migrated).valid || !hasPluginFiles(target, runtimeFiles(installed))) return false;
146
+ return hasPluginFiles(source, [
147
+ ...runtimeFiles(bundled), bundled.ui.design_tokens, ...bundled.ui.elements.map(element => element.module),
148
+ ]);
124
149
  } catch { return false; }
125
150
  }
126
151
 
@@ -165,7 +190,7 @@ function installDefaultsInto(brainDir, sourceDir) {
165
190
  fs.mkdirSync(path.dirname(target), { recursive: true });
166
191
  fs.cpSync(source, target, { recursive: true, force: false, errorOnExist: true });
167
192
  } else if (isStaleInstall(target, source)) {
168
- // A vendored copy that no longer validates (or predates the bundled release) is replaced; node_modules stays.
193
+ // Keep installed dependencies when refreshing the bundled plugin files.
169
194
  for (const entry of fs.readdirSync(target)) if (entry !== 'node_modules') fs.rmSync(path.join(target, entry), { recursive: true, force: true });
170
195
  for (const entry of fs.readdirSync(source)) if (entry !== 'node_modules') fs.cpSync(path.join(source, entry), path.join(target, entry), { recursive: true });
171
196
  }
@@ -1034,6 +1034,8 @@ export const PluginRecordSchema = z.object({
1034
1034
  task_runs: z.record(z.string()).optional(),
1035
1035
  /** Explicit mesh node for commands with selected-node placement. */
1036
1036
  task_nodes: z.record(z.string().min(1)).optional(),
1037
+ runtime_enabled: z.boolean().optional(),
1038
+ runtime_on_use: z.boolean().optional(),
1037
1039
  }).passthrough();
1038
1040
 
1039
1041
  const SECRET_KEY_PATTERN = /^(.*_)?(secret|token|password|api[_-]?key|private[_-]?key|credentials|auth[_-]?header|bearer)$/i;
@@ -46,6 +46,7 @@ export function patchVfsDocument(vfsPath, patches, options = {}) {
46
46
  type: 'patch',
47
47
  ...envelopeBase(vfsPath, options),
48
48
  patches,
49
+ ...(options.expectedBeforeHash === undefined ? {} : { expected_before_hash: options.expectedBeforeHash }),
49
50
  }, options);
50
51
  }
51
52
 
@@ -10,6 +10,23 @@ import { CLI_COMMANDS } from './cli-commands.mjs';
10
10
  const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
11
11
  export const cliEntry = path.join(packageRoot, 'bin/total-recall.mjs');
12
12
 
13
+ /** Whether this package's dashboard bundle is as new as its frontend sources (the server rebuilds it). */
14
+ async function defaultDashboardCheck() {
15
+ try {
16
+ const { checkDistFreshness, dashboardTargets } = await import('./dashboard-bundle.mjs');
17
+ if (!fs.existsSync(path.join(packageRoot, 'frontend', 'package.json'))) return { status: 'not-shipped' };
18
+ const result = await checkDistFreshness(dashboardTargets(packageRoot));
19
+ const status = result.ok ? 'current' : result.reason === 'missing-bundle' ? 'missing' : 'stale';
20
+ return {
21
+ status,
22
+ ...(result.bundle ? { built_at: new Date(result.bundle.mtimeMs).toISOString() } : {}),
23
+ ...(!result.ok && result.source ? { newer_source: path.relative(packageRoot, result.source.path) } : {}),
24
+ };
25
+ } catch (error) {
26
+ return { status: 'unknown', reason: error.message };
27
+ }
28
+ }
29
+
13
30
  export function runBounded(binary, args, { cwd = process.cwd(), timeoutMs = 15000, maxBytes = 262144 } = {}) {
14
31
  return new Promise(resolve => {
15
32
  let child, output = '', stderr = '', size = 0, reason = null, settled = false, timer, escalation;
@@ -81,8 +98,8 @@ export function processIdentity(pidFile, hint, { alive = pid => { try { process.
81
98
  }
82
99
 
83
100
  /** The single installation-aware daemon observation for status and lifecycle. */
84
- export function observeDaemon(brainDir, { identity = processIdentity } = {}) {
85
- const expected_entry = path.join(packageRoot, 'src', 'core', 'daemon-loop.mjs');
101
+ export function observeDaemon(brainDir, { identity = processIdentity, expectedEntry = path.join(packageRoot, 'src', 'core', 'daemon-loop.mjs') } = {}) {
102
+ const expected_entry = expectedEntry;
86
103
  return { ...identity(path.join(brainDir, 'daemon.pid'), expected_entry), expected_entry, source: 'local-pid' };
87
104
  }
88
105
 
@@ -130,7 +147,8 @@ export async function fetchStartup(url, options = {}, fetchImpl = fetch) {
130
147
  }
131
148
 
132
149
  export async function startupHealth({ cwd = process.cwd(), brainDir, brainUrl = null, token = null, brainId = null, ensure = false,
133
- appCheck = null, appStart = null, run = runBounded, fetchImpl = fetch, onDiskVersion = packageVersionOnDisk, listener = verifyListener, platform = process.platform, home = os.homedir(), identity = processIdentity } = {}) {
150
+ appCheck = null, appStart = null, run = runBounded, fetchImpl = fetch, onDiskVersion = packageVersionOnDisk, listener = verifyListener, platform = process.platform, home = os.homedir(), identity = processIdentity,
151
+ dashboardCheck = null, pluginChecks = null } = {}) {
134
152
  const actions = [];
135
153
  const serverFile = path.join(brainDir, 'server.pid');
136
154
  const report = { checked_at: new Date().toISOString(), server: { status: 'not-configured' }, brain: { status: 'unknown' }, daemon: { status: 'unknown' },
@@ -235,6 +253,13 @@ export async function startupHealth({ cwd = process.cwd(), brainDir, brainUrl =
235
253
  }
236
254
  const { inspectRequiredPlugins } = await import('./required-plugins.mjs');
237
255
  report.required_plugins = inspectRequiredPlugins(brainDir);
256
+ // Reported, not part of readiness: a stale dashboard or unfinished work still
257
+ // leaves the runtime usable, but neither may stay invisible.
258
+ report.dashboard = await (dashboardCheck ?? defaultDashboardCheck)();
259
+ report.plugin_checks = await (pluginChecks ?? (async () => {
260
+ const { runPluginStartupChecks } = await import('./plugin-startup-checks.mjs');
261
+ return runPluginStartupChecks({ cwd });
262
+ }))();
238
263
  report.shared_ready = report.required_plugins.status === 'ready' && report.server.status === 'ready' && report.brain.status === 'ready' && report.daemon.status === 'running' && report.ssss.status === 'available';
239
264
  report.ready_scope = appCheck ? 'shared-and-declared-current-repo-app' : 'shared-runtime-only';
240
265
  report.ready = report.shared_ready && (!appCheck || report.app.status === 'ready');
@@ -1,13 +1,13 @@
1
1
  /**
2
- * Fixture manifests for specs that exercise plumbing rather than card design.
2
+ * Fixture manifests for specs that exercise plumbing rather than Home design.
3
3
  *
4
- * Every real plugin has a bespoke card face declared in `ui.elements[]`, and the
4
+ * Every real plugin has a bespoke native Home declared in `ui.elements[]`, and the
5
5
  * validator enforces it. Specs that install, copy, vendor, discover or compile a
6
6
  * plugin still need a manifest that satisfies that contract, or they fail on a
7
- * rule they are not testing. This module supplies the smallest honest face so
7
+ * rule they are not testing. This module supplies a minimal native Home so
8
8
  * those specs keep asserting what they mean to assert.
9
9
  *
10
- * The face here is deliberately generic and exists only in tests. Production
10
+ * The Home here is deliberately generic and exists only in tests. Production
11
11
  * plugin UI is always hand-authored per plugin (invariants-b6e8fcac).
12
12
  */
13
13
  import fs from 'node:fs';
@@ -16,45 +16,46 @@ import path from 'node:path';
16
16
  const nodeFs = fs;
17
17
  const nodePath = path;
18
18
 
19
- /** The smallest valid card face. Declared once, as every plugin declares one. */
20
- export const FIXTURE_CARD_FACE = Object.freeze({
19
+ /** The smallest valid native Home. Declared once, as every plugin declares one. */
20
+ export const FIXTURE_HOME = Object.freeze({
21
+ home: 'home',
21
22
  design_tokens: 'ui/DESIGN.md',
22
23
  elements: [
23
24
  {
24
- id: 'card',
25
- kind: 'card',
26
- tag: 'tr-fixture-card',
27
- module: 'ui/card.js',
28
- description: 'Fixture card face',
25
+ id: 'home',
26
+ kind: 'panel',
27
+ tag: 'tr-fixture-home',
28
+ module: 'ui/home.js',
29
+ description: 'Fixture native Home',
29
30
  },
30
31
  ],
31
32
  });
32
33
 
33
- /** Source of the element module referenced by FIXTURE_CARD_FACE. */
34
- export const FIXTURE_CARD_SOURCE = 'export const tag = "tr-fixture-card";\n';
34
+ /** Source of the element module referenced by FIXTURE_HOME. */
35
+ export const FIXTURE_HOME_SOURCE = 'export const tag = "tr-fixture-home";\n';
35
36
 
36
37
  /**
37
- * Merge a bespoke face into a fixture manifest.
38
+ * Merge a native Home into a fixture manifest.
38
39
  *
39
- * Pass `ui: null` to omit the face deliberately, e.g. when asserting that a
40
+ * Pass `ui: null` to omit the Home deliberately, e.g. when asserting that a
40
41
  * plugin without one is rejected.
41
42
  */
42
- export function withFixtureFace(manifest = {}) {
43
+ export function withFixtureHome(manifest = {}) {
43
44
  if (manifest.ui === null) {
44
45
  const { ui: _omitted, ...rest } = manifest;
45
46
  return rest;
46
47
  }
47
- return { ...manifest, ui: manifest.ui ?? { ...FIXTURE_CARD_FACE } };
48
+ return { ...manifest, ui: manifest.ui ?? { ...FIXTURE_HOME } };
48
49
  }
49
50
 
50
51
  /**
51
- * Write `plugin.json` for a fixture plugin, declaring a face unless the manifest
52
+ * Write `plugin.json` for a fixture plugin, declaring a Home unless the manifest
52
53
  * explicitly opts out with `ui: null`.
53
54
  *
54
55
  * Returns the manifest that was written.
55
56
  */
56
57
  export function writeFixturePlugin(pluginDir, manifest, { fs = nodeFs, path = nodePath } = {}) {
57
- const written = withFixtureFace(manifest);
58
+ const written = withFixtureHome(manifest);
58
59
  nodeFs.writeFileSync(
59
60
  nodePath.join(pluginDir, 'plugin.json'),
60
61
  JSON.stringify(written, null, 2),
@@ -62,8 +63,8 @@ export function writeFixturePlugin(pluginDir, manifest, { fs = nodeFs, path = no
62
63
  if (written.ui?.elements?.length) {
63
64
  const uiDir = nodePath.join(pluginDir, 'ui');
64
65
  nodeFs.mkdirSync(uiDir, { recursive: true });
65
- nodeFs.writeFileSync(nodePath.join(uiDir, 'card.js'), FIXTURE_CARD_SOURCE);
66
- nodeFs.writeFileSync(nodePath.join(uiDir, 'DESIGN.md'), '# Fixture card\n');
66
+ nodeFs.writeFileSync(nodePath.join(uiDir, 'home.js'), FIXTURE_HOME_SOURCE);
67
+ nodeFs.writeFileSync(nodePath.join(uiDir, 'DESIGN.md'), '# Fixture Home\n');
67
68
  }
68
69
  return written;
69
70
  }
@@ -135,31 +135,38 @@ export async function loadFirewallPolicy(brainDir) {
135
135
 
136
136
  applyPolicy();
137
137
 
138
- // Watch the file directly if it exists.
139
- if (!policyWatcher && fs.existsSync(policyPath)) {
140
- policyWatcher = fs.watch(policyPath, (eventType) => {
141
- if (eventType === 'change') applyPolicy();
142
- });
143
- // Unref so this watcher does not prevent one-shot CLI commands (e.g.
144
- // `recall`) from exiting cleanly once their work is done.
145
- policyWatcher.unref();
146
- }
138
+ const watch = (target, onChange, clear) => {
139
+ try {
140
+ const watcher = fs.watch(target, onChange);
141
+ watcher.on('error', error => {
142
+ watcher.close();
143
+ clear(watcher);
144
+ logger.warn('throttled-fetch', `Network policy watcher unavailable: ${error.message}`);
145
+ });
146
+ watcher.unref();
147
+ return watcher;
148
+ } catch (error) {
149
+ logger.warn('throttled-fetch', `Network policy watcher unavailable: ${error.message}`);
150
+ return null;
151
+ }
152
+ };
153
+ const watchPolicy = () => {
154
+ if (policyWatcher || !fs.existsSync(policyPath)) return;
155
+ policyWatcher = watch(policyPath, event => {
156
+ if (event === 'change') applyPolicy();
157
+ }, watcher => { if (policyWatcher === watcher) policyWatcher = null; });
158
+ };
159
+ watchPolicy();
147
160
 
148
161
  // Also watch the parent directory so we pick up the policy file being
149
162
  // created after boot (e.g. first-run setups where the doc doesn't exist
150
163
  // yet). Attach the direct file watcher once it appears.
151
164
  if (!parentWatcher && fs.existsSync(systemDir)) {
152
- parentWatcher = fs.watch(systemDir, (eventType, filename) => {
165
+ parentWatcher = watch(systemDir, (_event, filename) => {
153
166
  if (filename !== 'network-policy.md') return;
154
167
  applyPolicy();
155
- if (!policyWatcher && fs.existsSync(policyPath)) {
156
- policyWatcher = fs.watch(policyPath, (evt) => {
157
- if (evt === 'change') applyPolicy();
158
- });
159
- policyWatcher.unref();
160
- }
161
- });
162
- parentWatcher.unref();
168
+ watchPolicy();
169
+ }, watcher => { if (parentWatcher === watcher) parentWatcher = null; });
163
170
  }
164
171
  } catch (err) {
165
172
  logger.error('throttled-fetch', `Failed to load network policy: ${err.message}`);
@@ -0,0 +1,77 @@
1
+ import { getPluginById } from './plugin-loader.mjs';
2
+ import { pluginActivation } from './plugin-state.mjs';
3
+
4
+ const decisionLimit = 20;
5
+
6
+ async function requestChoice(state, questions, { projectRoot = process.cwd(), brainDir } = {}) {
7
+ const plugin = getPluginById('decision', projectRoot);
8
+ if (!plugin?.valid || !pluginActivation(plugin, { projectRoot }).enabled ||
9
+ !plugin.manifest.cli?.subcommands?.some(command => command.name === 'run')) return null;
10
+ const { runPluginCommand } = await import('./plugin-runner.mjs');
11
+ const run = await runPluginCommand(plugin, {
12
+ subcommand: 'run',
13
+ args: ['--state', JSON.stringify(state), '--questions', JSON.stringify(questions), '--json'],
14
+ timeoutMs: 15_000,
15
+ cwd: projectRoot ?? brainDir ?? process.cwd(),
16
+ projectRoot,
17
+ });
18
+ if ((!run.ok && run.exitCode !== 4) || run.truncated) return null;
19
+ return JSON.parse(run.stdout);
20
+ }
21
+
22
+ export async function chooseUiChunk(chunks, query, surface, decide = requestChoice) {
23
+ const result = await chooseBlocks(chunks, query, surface, ['selected_chunk'], decide);
24
+ const { keys, ...selection } = result;
25
+ return { ...selection, key: keys[0] ?? null };
26
+ }
27
+
28
+ export async function chooseUiComposition(chunks, query, decide, target) {
29
+ const choose = decide ?? ((state, questions) => requestChoice(state, questions, target));
30
+ return chooseBlocks(chunks, query, 'page', ['selected_chunk', 'supporting_chunk', 'reference_chunk'], choose, false);
31
+ }
32
+
33
+ const instructions = {
34
+ selected_chunk: 'Choose the primary block that best accomplishes the requested intent.',
35
+ supporting_chunk: 'Choose an additional block that helps accomplish the same task without repeating the primary block.',
36
+ reference_chunk: 'Choose an optional readout or reference block that gives useful context for the task.',
37
+ };
38
+
39
+ async function chooseBlocks(chunks, query, surface, slots, decide, skipSingle = true) {
40
+ const shortlist = chunks.slice(0, decisionLimit);
41
+ if (!shortlist.length) return { source: 'lexical', keys: [], reason: 'no-matches' };
42
+ const fallback = { source: 'lexical', keys: shortlist.slice(0, slots.length).map(chunk => chunk.key) };
43
+ if (skipSingle && shortlist.length === 1) return { ...fallback, reason: 'single-match' };
44
+
45
+ const criteria = Object.fromEntries(shortlist.map(chunk => {
46
+ const description = [chunk.title, chunk.description, ...chunk.intents, ...(chunk.operations || []), ...(chunk.data || [])].join(' ').slice(0, 600);
47
+ return [chunk.key, description];
48
+ }));
49
+ criteria.none = 'No eligible chunk fits this intent.';
50
+ const questions = Object.fromEntries(slots.slice(0, shortlist.length).map(slot => [slot, {
51
+ type: 'choice',
52
+ instructions: `${instructions[slot]} Choose none when nothing fits.`,
53
+ criteria,
54
+ }]));
55
+ let result;
56
+ try {
57
+ result = await decide({ intent: query, surface: surface ?? null }, questions);
58
+ } catch {
59
+ return { ...fallback, reason: 'decision-unavailable' };
60
+ }
61
+ if (result?.ok !== true) return { ...fallback, reason: 'decision-unavailable' };
62
+ const eligible = new Set(shortlist.map(chunk => chunk.key));
63
+ const primary = result.answers?.selected_chunk;
64
+ if (!confident(primary)) return { ...fallback, reason: 'decision-abstained' };
65
+ if (primary.choice === 'none') return { source: 'decision', keys: [], reason: 'no-fit', confidence: primary.confidence };
66
+ if (!eligible.has(primary.choice)) return { source: 'decision', keys: [], reason: 'invalid-choice' };
67
+ const keys = new Set([primary.choice]);
68
+ for (const slot of Object.keys(questions).slice(1)) {
69
+ const answer = result.answers?.[slot];
70
+ if (confident(answer) && eligible.has(answer.choice)) keys.add(answer.choice);
71
+ }
72
+ return { source: 'decision', keys: [...keys], confidence: primary.confidence };
73
+ }
74
+
75
+ function confident(answer) {
76
+ return answer?.act === true && Number.isFinite(answer.confidence) && answer.confidence >= 0.5 && answer.confidence <= 1;
77
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "registry": "ui-chunk",
3
+ "extends": "ssss",
4
+ "version": "1.1.0",
5
+ "description": "Portable owner-authored HTML chunks for Total Recall workspace surfaces.",
6
+ "document_primitives": {
7
+ "chunk": {
8
+ "family": "capability",
9
+ "append_only": false,
10
+ "portability": "structural",
11
+ "filename_convention": "A kebab-case chunk ID under ui/chunks/ with the .md extension.",
12
+ "required_fields": ["type", "title", "description", "timestamp", "plugin", "id", "intents", "surfaces"],
13
+ "optional_fields": ["panel", "operations", "data"],
14
+ "immutable_fields": ["plugin", "id"],
15
+ "enums": { "surfaces": ["page", "left", "right", "bottom"] },
16
+ "patterns": {
17
+ "plugin": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
18
+ "id": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
19
+ },
20
+ "notes": "The body holds HTML. The host validates surface membership, file ownership and native panel bindings before publishing the catalog."
21
+ }
22
+ }
23
+ }