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
@@ -4,7 +4,7 @@ description: Discover, route, coordinate, review and run coding agent harnesses
4
4
  repo_scoped: false
5
5
  ---
6
6
 
7
- # Meta Harness
7
+ # Metaharness
8
8
 
9
9
  Coordinates coding agent harnesses across mesh nodes. `cli-agents` is an alias of `meta-harness`. Run `total-recall meta-harness --help` for flags.
10
10
 
@@ -12,7 +12,7 @@ Coordinates coding agent harnesses across mesh nodes. `cli-agents` is an alias o
12
12
  1. **Believe the status words.** `ready` was proved by a login probe. `unverified` has no probe, so only a completed run proves it. `needs_auth`, `blocked` and `exhausted` cannot take work; the reason is in `quotaNote` and in `excluded`.
13
13
  2. **No figure without a source.** `benchmarks` shows only fetched figures with their source and time. A dash means nobody measured it. Adapter quality and cost scores are configured priors, not measurements.
14
14
  3. **Writing happens on a branch.** `route --execute`, `coordinate --execute` and race work in their own worktree on a `meta-harness/...` branch. Nothing merges into your branch; review the branch and merge it yourself.
15
- 4. **Long runs go to the background.** The host stops plugin commands after 10 minutes, so add `--background` to any multi-stage `--execute`. It prints a log path that receives the final JSON, and a pipeline also records a thread. Stopping a run stops its agents and removes its worktrees, unfinished branches and open thread. A remote job (`--node` with an absolute `--cwd`) has a one-hour budget; `--timeout-ms` changes it.
15
+ 4. **Long runs go to the background.** The host stops plugin commands after 10 minutes, so add `--background` to any multi-stage `--execute`. Dispatch also accepts `--background`. The command prints a job ID and log path. The first log line and `jobs/<id>.pid` record its PID. Use `cancel <job>` to stop it. SIGTERM and SIGINT stop its agents and prevent further attempts. Changed work stays on its branch as a WIP commit; the result reports that branch. Worktrees are removed and progress becomes `cancelled`. The PID file is removed when the job ends. A pipeline also closes its thread. A remote job (`--node` with an absolute `--cwd`) has a one-hour budget; `--timeout-ms` changes it.
16
16
  5. **Cost.** dsh, hermes and other metered harnesses bill per token, and `route` warns when it picks one. `--jev` also makes a metered call.
17
17
 
18
18
  ## Commands
@@ -24,9 +24,23 @@ Coordinates coding agent harnesses across mesh nodes. `cli-agents` is an alias o
24
24
  - `decompose "<task>"`: the stages and which ready harness takes each.
25
25
  - `review [<git-range>] [--staged] [--author <id>] [--reviewer <id>] [--thread <id>]`: review by another model family. `NO_DIFF` means there was nothing to review.
26
26
  - `dispatch --stdin` with JSON `{agent, cwd, prompt, permissions, node, allowUnknownAuth}`: one bootstrapped run with a receipt.
27
- - `threads list|show|create|close|delete`, `learn [--emit]`, `alerts check|test [--dry-run]`, `adapter list|add|remove`.
27
+ - `cancel <job>`: stop a running local job by ID prefix or log path. Wait up to 60 seconds for its cancelled result. Finished and unknown jobs return an error. Use `watch <job>` to read progress.
28
+ - `reply <job> "<text>"`: steer the running turn or answer its oldest question. `approve <job> <request> allow|deny` answers an approval. Native requests and MCP requests use the same approval policy.
29
+ - `interrupt <job>`: stop the current turn and keep its session for `resume`. `goal <job> "<text>"` replaces the running job's goal.
30
+ - `fork <job> [--at <turn>] [--background]`: start a new job from the session history on a new branch. Selected turns require native support. `resume <job> "<text>" [--background]` continues a finished session on its saved branch. Both commands keep the parent's history.
31
+ - `route`, `coordinate` and `dispatch` accept `--goal`, `--max-turns`, `--max-budget-usd`, `--max-tokens`, `--result-schema <file>`, `--approvals auto|operator|rules` and `--question-timeout-ms`. Dispatch accepts the same fields in camelCase in its stdin JSON (`approvalPolicy` selects approvals). The default goal is the task. Completion requires a separate goal check with concrete evidence. File changes alone do not prove completion. A limit fails the job without escalation. Structured results get one correction turn if validation fails.
32
+ - `mcp [--approvals auto|operator|rules] [--question-timeout-ms <ms>]`: serve the job's `ask` and `approve` tools over stdio. Metaharness configures this server for Claude and Codex. `watch <job>` includes capabilities, usage, plans, diffs, hooks, model reroutes and waiting requests.
33
+ - A routed change gets a review from another model family after its gate and before its push. A blocking finding or an unreadable review stops the push. The job result contains the review.
34
+ - A token or USD cap requires measured usage. A harness that cannot report the required usage fails with `failureKind: limit`. A zero USD cap starts no agent turn. Goal checks and JSON corrections count toward job limits. JSON Schema assertions outside the portable validator's supported set fail validation.
35
+ - `threads list|show|create|close|delete`, `learn`, `alerts check|test [--dry-run]`, `adapter list|add|remove`.
36
+ - `reviews`: read the durable review ledger, including selected diff, commit, reviewer, verdict and disposition. Thread deletion does not remove reviews.
37
+ - `threads cleanup-fixtures [--dry-run]`: one-time cleanup of exact test fixture titles. The atomic SSSS record lists removed and retained IDs. Real end-to-end job threads stay in place.
28
38
  - `adoption [--node <id> | --all]`: inspect native gate activity and real managed run receipts separately from installation and login.
29
39
  - `docs-status`, `docs-refresh`, `docs-search --query <q>`, `docs-read`, `maintenance`, `setup`, `runtime <agent> <maintenance-command>`. Use dispatch for agent tasks and the service owner for gateway changes.
30
40
 
41
+ `setup --node <recorded-node>` places documentation maintenance, model maintenance
42
+ and quota alerts through the existing daemon. Maintenance attempts all stale
43
+ documentation sources and reports setup, gate and documentation failures separately.
44
+
31
45
  ## When something fails
32
46
  Read `failureKind` on the result. `quota` carries `resetsAt`, and routing avoids that harness until then. `auth` and `not-ready` mean the operator must sign in or install something. These do not count against a harness's pass rate; `agent` and `gate` failures do.
@@ -90,9 +90,9 @@ export async function inspectFleetAdoption({ node, inventory = listTargets, read
90
90
  argv: ['total-recall', 'meta-harness', 'adoption', '--json'],
91
91
  });
92
92
  try { report = JSON.parse(result.output); } catch { /* The installed CLI did not supply an adoption report. */ }
93
- if (report?.error === 'Unknown operation: adoption') return { ...row, status: 'update-required', error: 'The installed Meta Harness does not support adoption reports.' };
93
+ if (report?.error === 'Unknown operation: adoption') return { ...row, status: 'update-required', error: 'The installed Metaharness does not support adoption reports.' };
94
94
  if (!report || !Array.isArray(report.agents) || typeof report.pluginVersion !== 'string' || typeof report.ok !== 'boolean' || (report.ok && !result.ok)) {
95
- return { ...row, status: 'adoption-unavailable', error: 'The installed Meta Harness did not return an adoption report. Verify its version and CLI availability.' };
95
+ return { ...row, status: 'adoption-unavailable', error: 'The installed Metaharness did not return an adoption report. Verify its version and CLI availability.' };
96
96
  }
97
97
  }
98
98
  return { ...row, status: report.ok ? 'inspected' : 'partial', report };
@@ -4,7 +4,7 @@ import { readReceipts, activeQuotaWalls } from './learner.mjs';
4
4
 
5
5
  const SEVERITY = { info: 'info', warning: 'high', high: 'high', critical: 'critical' };
6
6
 
7
- export async function notifyAlert({ title = 'Meta Harness', message = '', severity = 'info', eventId, dryRun = false } = {}) {
7
+ export async function notifyAlert({ title = 'Metaharness', message = '', severity = 'info', eventId, dryRun = false } = {}) {
8
8
  const args = ['alerts', 'send', '--title', title, '--message', message, '--severity', SEVERITY[severity] || 'info', '--source', 'meta-harness'];
9
9
  if (eventId) args.push('--event-id', eventId);
10
10
  if (dryRun) args.push('--dry-run');
@@ -20,13 +20,13 @@ export async function notifyAlert({ title = 'Meta Harness', message = '', severi
20
20
  export async function checkAlerts({ quotas = {}, threshold = 85, dryRun = false } = {}) {
21
21
  const walls = activeQuotaWalls(await readReceipts());
22
22
  const candidates = Object.entries(walls).map(([harness, wall]) => ({
23
- title: `Meta Harness: ${harness} is out of quota`,
23
+ title: `Metaharness: ${harness} is out of quota`,
24
24
  message: `${wall.message || 'Quota wall observed.'} Routing avoids ${harness} until ${wall.until}.`,
25
25
  severity: 'warning', eventId: `meta-harness-wall-${harness}-${wall.until}`,
26
26
  }));
27
27
  for (const [id, quota] of Object.entries(quotas)) {
28
28
  if (typeof quota?.usagePercent === 'number' && quota.usagePercent > threshold && quota.billingModel !== 'local') candidates.push({
29
- title: `Meta Harness: ${id} is at ${quota.usagePercent}%`, message: `Measured usage on ${id} is above ${threshold}%.`,
29
+ title: `Metaharness: ${id} is at ${quota.usagePercent}%`, message: `Measured usage on ${id} is above ${threshold}%.`,
30
30
  severity: 'warning', eventId: `meta-harness-usage-${id}-${new Date().toISOString().slice(0, 10)}`,
31
31
  });
32
32
  }
@@ -9,18 +9,46 @@ import { recordRunReceipt } from './learner.mjs';
9
9
  import { resolvePool, localExecutionNode } from './pool.mjs';
10
10
  import { routeTask } from './router.mjs';
11
11
  import { runQualityGate } from './gate.mjs';
12
- import { isGitRepo, createWorktree, removeWorktree, commitAll, linkDependencies, deleteBranch } from './worktree.mjs';
12
+ import { isGitRepo, isDirty, pushBranch, createWorktree, removeWorktree, commitAll, linkDependencies, deleteBranch, provideDependencies } from './worktree.mjs';
13
13
  import { forwardRemoteOperation } from './remote-operation.mjs';
14
14
  import { queueIncludedJob } from './usage-queue.mjs';
15
+ import { configDir } from './paths.mjs';
16
+ import path from 'node:path';
17
+ import { cancellationResult } from './worker.mjs';
18
+ import { executeCodeReview, reviewBlocksPush } from './review.mjs';
19
+ import { sessionOptions } from './session/run.mjs';
20
+ import fs from 'node:fs';
21
+ import { jobPaths } from './control.mjs';
22
+
23
+ // A routed attempt gets time in proportion to the work the route measured (A-101); --timeout-ms overrides.
24
+ export const ROUTED_TIMEOUT_MS = { local: 15 * 60_000, budget: 30 * 60_000, frontier: 90 * 60_000 };
25
+
26
+ /**
27
+ * What Metaharness itself recorded about this job, for the reviewer (A-122): questions the agent
28
+ * asked, the operator's replies, the goal check and the gate. The reviewer sees only the diff
29
+ * otherwise and cannot verify claims about how the job ran.
30
+ */
31
+ export function reviewEvidence(jobId, attempt = {}) {
32
+ const evidence = { goalCheck: attempt.goalCheck ?? null, gate: attempt.gate ? { status: attempt.gate.status, passed: attempt.gate.passed } : null, questions: [], replies: [] };
33
+ if (!jobId) return evidence;
34
+ let paths;
35
+ try { paths = jobPaths(jobId); } catch { return evidence; }
36
+ const lines = file => { try { return fs.readFileSync(file, 'utf8').split('\n').filter(Boolean).map(l => { try { return JSON.parse(l); } catch { return null; } }).filter(Boolean); } catch { return []; } };
37
+ for (const e of lines(paths.events)) if (e.kind === 'question') evidence.questions.push({ requestId: e.requestId, questions: e.questions || e.text, source: e.source, t: e.t });
38
+ for (const c of lines(paths.inbox)) if (c.type === 'reply' || c.command === 'reply') evidence.replies.push({ requestId: c.requestId || null, text: c.text, t: c.t });
39
+ return evidence;
40
+ }
15
41
 
16
42
  export async function executeRoutedTask(task, options = {}) {
43
+ if (cancellationResult()) return cancellationResult();
17
44
  const remote = await forwardRemoteOperation('route', task, { ...options, execute: true });
18
45
  if (remote) return remote;
19
46
  if (options.node) localExecutionNode(options);
20
47
  const cwd = options.cwd || process.cwd();
48
+ const controls = sessionOptions({ ...options, cwd });
21
49
  if (!isGitRepo(cwd)) throw new Error('Routed execution writes on its own branch, so it must run inside a git repository');
22
50
  const route = options.route || await routeTask(task, options);
23
- const replay = { kind: 'route', gate: options.gate === true, gateOptions: options.gateOptions };
51
+ const replay = { kind: 'route', gate: options.gate === true, gateOptions: options.gateOptions, controls };
24
52
  if (!route.selected && route.deferred) {
25
53
  const blocked = { ok: false, failureKind: 'quota', error: 'The included usage allowance is exhausted.', resetsAt: route.deferred.resetsAt };
26
54
  const job = await queueIncludedJob({ agent: route.deferred.harness, cwd, prompt: task }, blocked,
@@ -35,34 +63,50 @@ export async function executeRoutedTask(task, options = {}) {
35
63
  const attempts = [];
36
64
  const alerts = [];
37
65
  const tried = new Set();
38
- let harness = route.selected, diagnostic = null, previous = null;
66
+ let harness = route.selected, diagnostic = null, previous = null, carried = null;
67
+ const timeoutMs = options.timeoutMs || ROUTED_TIMEOUT_MS[route.tier] || ROUTED_TIMEOUT_MS.budget;
39
68
 
40
69
  for (let index = 1; index <= maxAttempts && harness; index += 1) {
70
+ if (cancellationResult()) return { route, attempts, alerts, ...cancellationResult() };
41
71
  tried.add(harness);
42
72
  const branch = `meta-harness/${id}-${index}-${harness}`;
43
- const directory = createWorktree(cwd, { branch });
44
- const attempt = { harness, branch: null, commit: null, gate: null, escalatedFrom: previous };
73
+ // An escalation continues from the previous attempt's unfinished work instead of starting over (A-102).
74
+ const directory = createWorktree(cwd, { branch, ...(carried ? { ref: carried } : {}) });
75
+ // A-107: each attempt streams its agent's events here live; the path is announced first.
76
+ const progressFile = options.progressDir === false ? undefined
77
+ : path.join(options.progressDir || path.join(configDir(), 'jobs'), `${id}-${index}-${harness}.events.jsonl`);
78
+ const attempt = { harness, branch: null, commit: null, gate: null, escalatedFrom: previous, ...(progressFile ? { progressFile } : {}) };
45
79
  attempts.push(attempt);
80
+ if (progressFile) console.error(JSON.stringify({ progress: progressFile, attempt: index, harness }));
81
+ // A-109: the agent gets the repository's dependencies in its own checkout, so its tests run.
46
82
  let stopped;
47
83
  try {
84
+ attempt.dependencies = (options.provideDependencies || provideDependencies)(cwd, directory);
48
85
  const prompt = `Work only in this checkout. Do not commit; the harness commits your changes.\nTask:\n${task}${diagnostic ? `\n\nA previous attempt by ${previous} failed. Diagnostic:\n${diagnostic}` : ''}`;
49
- const result = await dispatchWithReceipt({ agent: harness, node, cwd: directory, contextCwd: cwd, contextTask: task, prompt, permissions: { read: true, write: true }, allowUnknownAuth: options.allowUnknownAuth === true }, { dispatchFn: options.dispatchFn, role: 'Routed', complexityScore: route.complexity?.score, escalatedFrom: previous, replay, retryId: options.retryId });
86
+ const result = await dispatchWithReceipt({ agent: harness, node, branch, cwd: directory, contextCwd: cwd, contextTask: task, prompt, permissions: { read: true, write: true }, timeoutMs,
87
+ ...controls, goal: controls.goal || task,
88
+ ...(progressFile ? { progressFile } : {}), allowUnknownAuth: options.allowUnknownAuth === true }, { dispatchFn: options.dispatchFn, role: 'Routed', complexityScore: route.complexity?.score, escalatedFrom: previous, replay, retryId: options.retryId });
50
89
  attempt.ok = result.ok === true;
90
+ if (result.cancelled) stopped = result;
51
91
  attempt.durationMs = result.durationMs;
52
- attempt.output = result.response || null;
92
+ attempt.timeoutMs = timeoutMs;
93
+ attempt.output = result.response || result.partialOutput || null;
94
+ if (result.sessionId) { attempt.sessionId = result.sessionId; attempt.branch = branch; }
95
+ attempt.goalCheck = result.goalCheck;
96
+ attempt.structured = result.structured;
97
+ attempt.usage = result.usage;
98
+ if (!attempt.ok && result.stderr) attempt.stderr = result.stderr;
53
99
  if (result.ok) {
54
100
  attempt.commit = commitAll(directory, `meta-harness: ${task.slice(0, 60)}\n\nAgent: ${harness}`);
55
101
  if (attempt.commit) attempt.branch = branch;
56
- else {
57
- attempt.ok = false; attempt.error = 'The agent reported success but changed no files.';
58
- await recordRunReceipt({ harness, role: 'Implementer', task, success: false, failureKind: 'no-change', error: attempt.error });
59
- }
102
+ // Keep the session branch even when the goal required no repository edit.
103
+ else if (result.sessionId) attempt.branch = branch;
60
104
  } else {
61
105
  attempt.error = result.error;
62
106
  attempt.failureKind = result.failureKind;
63
- if (result.queued || result.queueError || (result.failureKind === 'quota' && options.retryId) || result.failureKind === 'billing-policy') stopped = result;
107
+ if (result.queued || result.queueError || (result.failureKind === 'quota' && options.retryId) || ['billing-policy', 'limit', 'interrupted'].includes(result.failureKind)) stopped = result;
64
108
  }
65
- if (attempt.ok && options.gate) {
109
+ if (attempt.ok && options.gate && !cancellationResult()) {
66
110
  linkDependencies(cwd, directory);
67
111
  attempt.gate = await (options.gateFn || runQualityGate)(directory, options.gateOptions);
68
112
  // A gate that could not run says nothing about this agent's work, so it does not escalate.
@@ -74,16 +118,46 @@ export async function executeRoutedTask(task, options = {}) {
74
118
  await recordRunReceipt({ harness, role: 'Gate', task, success: attempt.gate.passed, failureKind: attempt.gate.passed ? null : 'gate', error: attempt.gate.passed ? null : attempt.gate.summary, gate: attempt.gate.status });
75
119
  attempt.ok = attempt.gate.passed;
76
120
  }
121
+ if (attempt.ok && attempt.commit && !cancellationResult()) {
122
+ attempt.review = await (options.reviewFn || executeCodeReview)({ cwd, author: harness, task,
123
+ evidence: reviewEvidence(options.jobId || process.env.TR_METAHARNESS_JOB, attempt),
124
+ target: `HEAD...${attempt.commit}`, dispatchFn: options.dispatchFn, reviewer: options.reviewer,
125
+ allowUnknownAuth: options.allowUnknownAuth === true, availableHarnesses: options.availableHarnesses });
126
+ if (reviewBlocksPush(attempt.review)) {
127
+ attempt.ok = false; attempt.failureKind = 'review';
128
+ attempt.error = attempt.review.error || JSON.stringify(attempt.review.review?.findings || []) || 'Review blocked the push.';
129
+ }
130
+ }
77
131
  } catch (error) {
78
132
  attempt.ok = false;
79
133
  attempt.error = String(error.message);
80
134
  } finally {
81
- removeWorktree(cwd, directory);
82
- // Only the branch this run hands back survives; the attempt record keeps what happened.
83
- if (!attempt.ok) { deleteBranch(cwd, branch); attempt.branch = null; }
135
+ if (cancellationResult()) {
136
+ Object.assign(attempt, cancellationResult());
137
+ stopped = cancellationResult();
138
+ }
139
+ // A failed attempt's edits are kept as an unfinished commit on its branch for review and for the
140
+ // next attempt to continue from; only an attempt that changed nothing loses its branch (A-102).
141
+ if (!attempt.ok && !attempt.commit) {
142
+ let wip = null;
143
+ try { wip = commitAll(directory, `meta-harness: unfinished attempt by ${harness}\n\n${String(attempt.error || '').slice(0, 300)}`); }
144
+ catch (error) { attempt.wipError = String(error.message); }
145
+ if (wip) { attempt.wip = wip; attempt.branch = branch; carried = wip; }
146
+ } else if (!attempt.ok && attempt.commit) carried = attempt.commit;
147
+ // Edits that could not be committed are never deleted (A-114): the checkout and branch stay for recovery.
148
+ if (attempt.wipError && isDirty(directory)) { attempt.keptWorktree = directory; attempt.branch = branch; }
149
+ else {
150
+ removeWorktree(cwd, directory);
151
+ if (!attempt.ok && !attempt.wip && !attempt.commit && !attempt.sessionId) { deleteBranch(cwd, branch); attempt.branch = null; }
152
+ }
84
153
  }
85
154
 
86
- if (attempt.ok) return { ok: true, route, gateApplied: Boolean(options.gate), branch: attempt.branch, commit: attempt.commit, harness, attempts, alerts };
155
+ if (stopped?.cancelled) return { ...stopped, route, attempts, alerts, branch: attempt.branch, commit: attempt.commit, wip: attempt.wip, harness };
156
+ if (attempt.ok) {
157
+ const push = options.push === false ? { pushed: false, reason: 'push disabled for this run' } : pushBranch(cwd, attempt.branch);
158
+ return { ok: true, route, goalCheck: attempt.goalCheck, structured: attempt.structured, review: attempt.review,
159
+ gateApplied: Boolean(options.gate), branch: attempt.branch, commit: attempt.commit, push, harness, attempts, alerts };
160
+ }
87
161
  if (stopped) return { ...stopped, route, attempts, alerts };
88
162
 
89
163
  const remaining = pool.filter(item => !tried.has(item));
@@ -91,7 +165,7 @@ export async function executeRoutedTask(task, options = {}) {
91
165
  diagnostic = attempt.gate?.summary || attempt.error || 'The previous attempt failed.';
92
166
  previous = harness;
93
167
  harness = next?.recommendedHarness && !tried.has(next.recommendedHarness) ? next.recommendedHarness : null;
94
- if (harness) alerts.push(await notifyAlert({ title: 'Meta Harness: escalating', message: `${previous} failed on "${task.slice(0, 60)}"; escalating to ${harness}.`, severity: 'warning' }));
168
+ if (harness) alerts.push(await notifyAlert({ title: 'Metaharness: escalating', message: `${previous} failed on "${task.slice(0, 60)}"; escalating to ${harness}.`, severity: 'warning' }));
95
169
  }
96
170
  return { ok: false, route, gateApplied: Boolean(options.gate), attempts, alerts, error: 'No attempt passed.' };
97
171
  }
@@ -0,0 +1,80 @@
1
+ import path from 'node:path';
2
+ import { configDir } from './paths.mjs';
3
+ import { PluginStateStore } from './ssss-store.mjs';
4
+ import { getActiveAdapters } from './worker.mjs';
5
+
6
+ const CODEX_DRAFT = 'codex://threads/new';
7
+ const MAX_SELECTION_BYTES = 16_384;
8
+ const MAX_DRAFT_LINK_LENGTH = 8_192;
9
+ const UNSAFE_TEXT = /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u;
10
+ const BRAIN_ID = /^(?:global|project:[^,]+|tenant:[a-zA-Z0-9._-]+)$/u;
11
+ const ADAPTER_ID = /^[a-z][a-z0-9-]{0,63}$/;
12
+
13
+ // Chat preferences follow the dashboard's brain. The auth store override is
14
+ // exclusively for credentials and may point outside an isolated dashboard.
15
+ const chatStore = () => new PluginStateStore(
16
+ path.join(configDir({ ...process.env, TR_SECRETS_BRAIN: undefined }), 'chat-default.json'),
17
+ 'meta-harness',
18
+ );
19
+ const displayAdapter = ({ id, name }) => ({ id, name });
20
+
21
+ function brainContext(selection) {
22
+ if (typeof selection !== 'string' || Buffer.byteLength(selection) > MAX_SELECTION_BYTES || UNSAFE_TEXT.test(selection)) {
23
+ throw new Error('Chat requires a bounded comma-separated list of brain IDs');
24
+ }
25
+ const ids = [...new Set(selection.split(',').map(id => id.trim()))];
26
+ if (ids.some(id => !BRAIN_ID.test(id))) throw new Error('Chat brain IDs must use global, project:<name>, or tenant:<name>');
27
+ return `Use this Total Recall brain selection for Chat: ${JSON.stringify(ids)}. Resolve the selected IDs through Total Recall before retrieving or writing memory.`;
28
+ }
29
+
30
+ function launchUrl(adapterId, value) {
31
+ if (value === undefined) return undefined;
32
+ const message = 'Use an HTTPS launch URL without credentials, a query or a fragment, or a Codex Chat link for Codex';
33
+ if (typeof value !== 'string' || value.length > 2048 || UNSAFE_TEXT.test(value) || /\s|\\/u.test(value)) {
34
+ throw new Error(message);
35
+ }
36
+ if (adapterId === 'codex' && /^codex:\/\/threads\/[a-zA-Z0-9_-]{1,128}$/.test(value)) return value;
37
+ if (!value.startsWith('https://') || /[?#]/.test(value)) throw new Error(message);
38
+ const url = URL.parse(value);
39
+ if (!url?.hostname || url.username || url.password) throw new Error(message);
40
+ return url.href;
41
+ }
42
+
43
+ function resolveTarget(settings, adapters, context) {
44
+ const choices = adapters.map(displayAdapter);
45
+ if (!settings) return { ok: true, configured: false, choices, context };
46
+ const adapter = adapters.find(({ id }) => id === settings.adapterId);
47
+ if (!adapter) {
48
+ return {
49
+ ok: true, configured: false, choices, context,
50
+ warning: 'The saved Chat harness is unavailable. Choose a new default.',
51
+ };
52
+ }
53
+
54
+ const target = launchUrl(adapter.id, settings.url) ?? (adapter.id === 'codex' ? CODEX_DRAFT : undefined);
55
+ let href = target;
56
+ if (target === CODEX_DRAFT) {
57
+ const draft = `${target}?prompt=${encodeURIComponent(context)}`;
58
+ if (draft.length <= MAX_DRAFT_LINK_LENGTH) href = draft;
59
+ }
60
+ return { ok: true, configured: true, choices, context, harness: displayAdapter(adapter), ...(href ? { href } : {}) };
61
+ }
62
+
63
+ /** Return a draft target. The caller decides when to open it and the operator submits in the harness. */
64
+ export function chatTarget(brains = 'global') {
65
+ const context = brainContext(brains);
66
+ const settings = chatStore().read(null);
67
+ if (settings !== null && (!settings || typeof settings.adapterId !== 'string' || !ADAPTER_ID.test(settings.adapterId))) {
68
+ throw new Error('The saved Chat default is invalid');
69
+ }
70
+ return resolveTarget(settings, getActiveAdapters(), context);
71
+ }
72
+
73
+ /** Only the adapter and optional launch URL are persistent. */
74
+ export function setChatDefault(adapterId, url) {
75
+ const adapters = getActiveAdapters();
76
+ if (!adapters.some(({ id }) => id === adapterId)) throw new Error('Choose a registered Metaharness adapter for Chat');
77
+ const settings = { adapterId, url: launchUrl(adapterId, url) };
78
+ chatStore().write(settings);
79
+ return resolveTarget(settings, adapters, brainContext('global'));
80
+ }
@@ -0,0 +1,145 @@
1
+ /** Job commands and events. The inbox is append-only; ack events are its cursor. */
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { randomUUID } from 'node:crypto';
5
+ import { execFile } from 'node:child_process';
6
+ import { promisify } from 'node:util';
7
+ import { configDir } from './paths.mjs';
8
+ import { resolveJob } from './jobs.mjs';
9
+
10
+ const execute = promisify(execFile);
11
+ export function jobPaths(job) {
12
+ const id = typeof job === 'string' ? job : job?.id || job?.job;
13
+ if (typeof id !== 'string' || !/^[a-z0-9-]+$/.test(id)) throw new Error('Invalid job ID.');
14
+ const base = path.join(configDir(), 'jobs', id);
15
+ return { id, inbox: `${base}.inbox.jsonl`, events: `${base}.events.jsonl` };
16
+ }
17
+
18
+ function readLines(file) {
19
+ let data;
20
+ try { data = fs.readFileSync(file, 'utf8'); }
21
+ catch (error) { if (error.code === 'ENOENT') return []; throw error; }
22
+ // An append from another process can still be incomplete.
23
+ return data.split('\n').slice(0, -1).filter(Boolean).map(line => JSON.parse(line));
24
+ }
25
+
26
+ export function appendEvent(job, event) {
27
+ const { events } = jobPaths(job);
28
+ fs.mkdirSync(path.dirname(events), { recursive: true, mode: 0o700 });
29
+ const entry = { ...event, t: new Date().toISOString() };
30
+ fs.appendFileSync(events, JSON.stringify(entry) + '\n', { mode: 0o600 });
31
+ return entry;
32
+ }
33
+
34
+ /** Read all requests, including requests older than watch's --tail. */
35
+ export function waitingRequests(job) {
36
+ const file = typeof job === 'string' && job.endsWith('.events.jsonl') ? job : jobPaths(job).events;
37
+ const waiting = new Map();
38
+ for (const event of readLines(file)) {
39
+ if (['question', 'approval'].includes(event.kind) && event.requestId) waiting.set(event.requestId, event);
40
+ if (event.kind === 'ack' && event.requestId && event.ok !== false) waiting.delete(event.requestId);
41
+ }
42
+ return [...waiting.values()];
43
+ }
44
+
45
+ function normalizeCommand(command) {
46
+ if (!command || typeof command !== 'object' || Array.isArray(command)) throw new Error('A job command is required.');
47
+ const type = command.type || command.command || command.kind;
48
+ if (!['reply', 'approve', 'interrupt', 'goal', 'fork'].includes(type)) throw new Error('Unsupported job command.');
49
+ if (['reply', 'goal'].includes(type) && (typeof command.text !== 'string' || !command.text.trim())) throw new Error(`${type} requires text.`);
50
+ if (type === 'approve' && (typeof command.requestId !== 'string' || !command.requestId || !['allow', 'deny'].includes(command.value))) {
51
+ throw new Error('approve requires a request ID and allow or deny.');
52
+ }
53
+ if (command.requestId !== undefined && (typeof command.requestId !== 'string' || !command.requestId)) throw new Error('Invalid request ID.');
54
+ return { ...command, type, command: type, id: command.id || randomUUID(), t: new Date().toISOString() };
55
+ }
56
+
57
+ const quote = value => `'${String(value).replaceAll("'", "'\\''")}'`;
58
+
59
+ /** Resolve prefixes through the same owner as cancel. Remote delivery never writes a local inbox. */
60
+ export async function appendCommand(target, command, { local = false, execFileFn = execute } = {}) {
61
+ const job = resolveJob(typeof target === 'string' ? target : target?.id || target?.job);
62
+ if (job.result) throw new Error(`Job ${job.id} has finished.`);
63
+ const entry = normalizeCommand(command);
64
+ const node = job.record?.node;
65
+ if (!local && node && node !== 'local') {
66
+ if (typeof node !== 'string' || node.startsWith('-') || /[\0\r\n]/.test(node)) throw new Error('Invalid job node.');
67
+ const args = [entry.type, job.record.remoteJob || job.id];
68
+ if (entry.type === 'approve') args.push(entry.requestId, entry.value);
69
+ else if (!['interrupt', 'fork'].includes(entry.type)) args.push(entry.text);
70
+ if (entry.type === 'fork' && entry.atTurn) args.push('--at', entry.atTurn);
71
+ args.push('--local-delivery', '--json');
72
+ const remote = `total-recall meta-harness ${args.map(quote).join(' ')}`;
73
+ const result = await execFileFn(process.env.TOTAL_RECALL_BIN || 'total-recall', ['mesh', 'exec', node, remote],
74
+ { encoding: 'utf8', timeout: 60_000, maxBuffer: 1024 * 1024 });
75
+ if (result.exitCode && result.exitCode !== 0) throw new Error(result.stderr || 'Remote command failed.');
76
+ // mesh exec may wrap the remote result. Transport failures must not look like delivery.
77
+ let report;
78
+ try { report = JSON.parse(result.stdout); } catch { /* mesh can return plain command output */ }
79
+ if (report?.ok === false) throw new Error(report.error || 'Remote command failed.');
80
+ return { ok: true, job: job.id, node, remote: true, command: entry, ...(report ? { delivery: report } : {}) };
81
+ }
82
+ const waiting = waitingRequests(job.id);
83
+ const reserved = new Set(readLines(jobPaths(job.id).inbox).map(row => row.requestId).filter(Boolean));
84
+ const request = entry.requestId ? waiting.find(row => row.requestId === entry.requestId)
85
+ : entry.type === 'reply' ? waiting.find(row => row.kind === 'question' && !reserved.has(row.requestId)) : null;
86
+ if (entry.requestId && !request) throw new Error(`No waiting request matches ${entry.requestId}.`);
87
+ if (request && entry.type === 'approve' && request.kind !== 'approval') throw new Error('The request is not an approval.');
88
+ if (request && entry.type === 'reply' && request.kind !== 'question') throw new Error('The request is not a question.');
89
+ if (request) {
90
+ if (reserved.has(request.requestId)) throw new Error('The request already has an answer in the inbox.');
91
+ entry.requestId = request.requestId;
92
+ if (request.source === 'mcp') entry.recipient = 'mcp';
93
+ // A deferred question is answered by the session runner, which starts the next turn with it.
94
+ else if (request.source === 'deferred') entry.recipient = 'deferred';
95
+ }
96
+ const { inbox } = jobPaths(job.id);
97
+ fs.mkdirSync(path.dirname(inbox), { recursive: true, mode: 0o700 });
98
+ fs.appendFileSync(inbox, JSON.stringify(entry) + '\n', { mode: 0o600 });
99
+ return { ok: true, job: job.id, command: entry };
100
+ }
101
+
102
+ /**
103
+ * One consumer handles commands in file order. A false return leaves a command for
104
+ * another consumer. MCP answers have their own recipient so a worker cannot also
105
+ * send them to the driver. Await close() before releasing the driver.
106
+ */
107
+ export function tailInbox(job, onCommand, { pollMs = 50, recipient = 'driver', onError = () => {} } = {}) {
108
+ const { inbox, events } = jobPaths(job);
109
+ fs.mkdirSync(path.dirname(inbox), { recursive: true, mode: 0o700 });
110
+ fs.closeSync(fs.openSync(inbox, 'a', 0o600));
111
+ let closed = false, running = null;
112
+ const seen = new Set();
113
+ const poll = () => {
114
+ if (closed || running) return running;
115
+ running = (async () => {
116
+ const acknowledged = new Set(readLines(events).filter(row => row.kind === 'ack' && row.commandId).map(row => row.commandId));
117
+ for (const command of readLines(inbox)) {
118
+ if (closed) break;
119
+ if (!command.id || seen.has(command.id) || acknowledged.has(command.id) || (command.recipient || 'driver') !== recipient) continue;
120
+ let result;
121
+ try { result = await onCommand(command); }
122
+ catch (error) {
123
+ // A failed command is reported once; do not repeat an agent side effect.
124
+ seen.add(command.id);
125
+ appendEvent(job, { kind: 'ack', command: command.type || command.command, commandId: command.id,
126
+ requestId: command.requestId, ok: false, text: error.message });
127
+ onError(error);
128
+ continue;
129
+ }
130
+ if (result === false || result?.handled === false) continue;
131
+ seen.add(command.id);
132
+ appendEvent(job, { kind: 'ack', command: command.type || command.command, commandId: command.id,
133
+ requestId: command.requestId, ok: true, text: `Handled ${command.type || command.command}`, ...(result?.via ? { via: result.via } : {}) });
134
+ }
135
+ })().catch(onError).finally(() => { running = null; });
136
+ return running;
137
+ };
138
+ const timer = setInterval(poll, pollMs);
139
+ poll();
140
+ const close = async () => { closed = true; clearInterval(timer); await running; };
141
+ // A function is convenient for existing watcher callers; properties support explicit lifecycle use.
142
+ close.close = close;
143
+ close.poll = poll;
144
+ return close;
145
+ }
@@ -16,6 +16,8 @@ import { isGitRepo, isDirty, headCommit, createWorktree, removeWorktree, commitA
16
16
  import { lastJsonWith } from './json-reply.mjs';
17
17
  import { runQualityGate } from './gate.mjs';
18
18
  import { forwardRemoteOperation } from './remote-operation.mjs';
19
+ import { cancellationResult } from './worker.mjs';
20
+ import { sessionOptions } from './session/run.mjs';
19
21
 
20
22
  const OPEN_THREADS = new Set();
21
23
  const MAX_BLUEPRINT = 20000;
@@ -101,9 +103,12 @@ async function runPipeline(task, poolInfo, options) {
101
103
  const branch = options.branch || `meta-harness/${thread.id}`;
102
104
  const run = async (stage, harness, prompt, { workdir = cwd, permissions, role = stage.role } = {}) => {
103
105
  stage.status = 'running';
104
- const result = await dispatchWithReceipt({ agent: harness.id, node: 'local', cwd: workdir, contextCwd: cwd, contextTask: task, prompt, permissions: permissions || stage.permissions, allowUnknownAuth: options.allowUnknownAuth === true }, { dispatchFn: options.dispatchFn, role, complexityScore: complexity.score });
105
- stage.status = result.ok ? 'completed' : result.queued ? 'queued' : 'failed';
106
+ const result = await dispatchWithReceipt({ agent: harness.id, node: 'local', cwd: workdir, contextCwd: cwd, contextTask: task, prompt,
107
+ ...sessionOptions({ ...options, cwd }), goal: stage.role === 'Implementer' ? options.goal || task : prompt.slice(0, 4000),
108
+ permissions: permissions || stage.permissions, allowUnknownAuth: options.allowUnknownAuth === true }, { dispatchFn: options.dispatchFn, role, complexityScore: complexity.score });
109
+ stage.status = result.cancelled ? 'cancelled' : result.ok ? 'completed' : result.queued ? 'queued' : 'failed';
106
110
  stage.output = result.response || result.error || null;
111
+ for (const key of ['goalCheck', 'structured', 'sessionId', 'progressFile', 'usage']) if (result[key] !== undefined) stage[key] = result[key];
107
112
  stage.failureKind = result.ok ? undefined : result.failureKind;
108
113
  if (result.queueId) stage.queueId = result.queueId;
109
114
  appendThreadTurn(thread.id, { role: role.toLowerCase(), agent: harness.id, content: stage.output || '' });
@@ -128,17 +133,14 @@ async function runPipeline(task, poolInfo, options) {
128
133
  const implementer = await run(stage('Implementer'), roles.implementer, `[ROLE: IMPLEMENTER]\nWork only in this checkout. Implement the task and its tests. Do not commit; the harness commits your changes.\nTask:\n${task}\n\nBlueprint:\n${blueprint}`, { workdir: implementerDir });
129
134
  if (!implementer.ok) { ok = false; return finish(); }
130
135
  commit = commitAll(implementerDir, `meta-harness: ${task.slice(0, 60)}\n\nImplementer: ${roles.implementer.id}`);
131
- if (!commit) {
132
- Object.assign(stage('Implementer'), { status: 'failed', failureKind: 'agent', output: 'The Implementer reported success but changed no files.' });
133
- await recordRunReceipt({ harness: roles.implementer.id, role: 'Implementer', task, success: false, failureKind: 'no-change', error: 'The Implementer reported success but changed no files.' });
134
- ok = false; return finish();
135
- }
136
+ if (!commit) commit = baseCommit;
136
137
  stage('Implementer').commit = commit;
137
138
  updateThread(thread.id, { branch, worktreePath: null });
138
139
 
139
140
  if (options.gate) {
140
141
  linkDependencies(cwd, implementerDir);
141
142
  gate = await (options.gateFn || runQualityGate)(implementerDir, options.gateOptions);
143
+ if (cancellationResult()) { ok = false; return finish(); }
142
144
  if (!gate.passed) { ok = false; stage('Verifier').status = 'skipped'; stage('Verifier').output = `Quality gate did not pass: ${gate.summary}`; return finish(); }
143
145
  }
144
146
 
@@ -165,16 +167,24 @@ async function runPipeline(task, poolInfo, options) {
165
167
  return finish();
166
168
  } finally {
167
169
  removeWorktree(cwd, implementerDir);
168
- if (implementerDir && (!commit || !ok)) deleteBranch(cwd, branch);
170
+ if (implementerDir && (!commit || (!ok && !cancellationResult()))) deleteBranch(cwd, branch);
169
171
  }
170
172
 
171
173
  function finish() {
174
+ const cancelled = cancellationResult();
175
+ if (cancelled) {
176
+ ok = false;
177
+ if (implementerDir) commit = commitAll(implementerDir, 'meta-harness: unfinished pipeline from a cancelled job') || commit;
178
+ }
172
179
  return (async () => {
173
180
  for (const item of stages) if (item.status === 'planned') item.status = 'skipped';
174
181
  OPEN_THREADS.delete(thread.id);
175
- updateThread(thread.id, { status: ok ? 'completed' : 'failed', branch: ok && commit ? branch : null, commit, baseCommit });
176
- const alert = await notifyAlert({ title: ok ? 'Meta Harness: pipeline passed' : 'Meta Harness: pipeline failed', message: `${task.slice(0, 80)}${commit ? ` (branch ${branch})` : ''}`, severity: ok ? 'info' : 'warning' });
177
- return { ...base, ok, threadId: thread.id, branch: ok && commit ? branch : null, commit, baseCommit, gate, warnings, durationMs: Date.now() - started, alert };
182
+ updateThread(thread.id, { status: ok ? 'completed' : 'failed', branch: (ok || cancelled) && commit ? branch : null, commit, baseCommit });
183
+ const alert = cancelled ? null : await notifyAlert({ title: ok ? 'Metaharness: pipeline passed' : 'Metaharness: pipeline failed', message: `${task.slice(0, 80)}${commit ? ` (branch ${branch})` : ''}`, severity: ok ? 'info' : 'warning' });
184
+ const limited = stages.find(item => item.failureKind === 'limit');
185
+ return { ...base, ok, ...cancelled, ...(limited ? { failureKind: 'limit', error: limited.output } : {}), goalCheck: stage('Implementer')?.goalCheck, structured: stage('Implementer')?.structured,
186
+ sessionId: stage('Implementer')?.sessionId, progressFile: stage('Implementer')?.progressFile,
187
+ threadId: thread.id, branch: (ok || cancelled) && commit ? branch : null, commit, baseCommit, gate, warnings, durationMs: Date.now() - started, alert };
178
188
  })();
179
189
  }
180
190
  }
@@ -187,14 +197,15 @@ async function runCouncil(task, poolInfo, options) {
187
197
  if (!seats.length) throw new Error('No harness can take work right now');
188
198
  const cwd = options.cwd || process.cwd();
189
199
  const votes = await Promise.all(seats.map(async harness => {
190
- const result = await dispatchWithReceipt({ agent: harness, node: 'local', cwd, contextTask: task, prompt: `[ROLE: COUNCIL MEMBER]\nDo not change any file. Assess this proposal, with its trade-offs and risks:\n${task}\n\nEnd your reply with exactly one JSON object: {"vote":"APPROVE"|"REJECT","reason":"..."}`, permissions: READ, allowUnknownAuth: options.allowUnknownAuth === true }, { dispatchFn: options.dispatchFn, role: 'Council' });
191
- return { harness, ok: result.ok === true, ...(result.ok ? parseVote(result.response) : { vote: 'ABSTAIN', reason: null, error: result.error }),
200
+ const result = await dispatchWithReceipt({ agent: harness, node: 'local', cwd, contextTask: task, ...sessionOptions({ ...options, cwd }), prompt: `[ROLE: COUNCIL MEMBER]\nDo not change any file. Assess this proposal, with its trade-offs and risks:\n${task}\n\nEnd your reply with exactly one JSON object: {"vote":"APPROVE"|"REJECT","reason":"..."}`, permissions: READ, allowUnknownAuth: options.allowUnknownAuth === true }, { dispatchFn: options.dispatchFn, role: 'Council' });
201
+ return { harness, ok: result.ok === true, failureKind: result.failureKind, ...(result.ok ? parseVote(result.response) : { vote: 'ABSTAIN', reason: null, error: result.error }),
192
202
  ...(result.queueId ? { queueId: result.queueId } : {}), feedback: result.response || null };
193
203
  }));
194
204
  const count = vote => votes.filter(item => item.vote === vote).length;
195
205
  const majority = votes.length / 2;
196
206
  const consensus = count('APPROVE') > majority ? 'approved' : count('REJECT') > majority ? 'rejected' : 'no-consensus';
197
- return { ok: votes.some(item => item.ok), mode: 'council', task, executedLive: true, consensus, excluded: poolInfo.excluded, votes };
207
+ const limited = votes.some(item => item.failureKind === 'limit');
208
+ return { ok: !limited && votes.some(item => item.ok), ...(limited ? { failureKind: 'limit' } : {}), ...cancellationResult(), mode: 'council', task, executedLive: true, consensus, excluded: poolInfo.excluded, votes };
198
209
  }
199
210
 
200
211
  async function runRace(task, poolInfo, options) {
@@ -212,12 +223,20 @@ async function runRace(task, poolInfo, options) {
212
223
  try {
213
224
  const started = Date.now();
214
225
  directory = createWorktree(cwd, { branch });
215
- const result = await dispatchWithReceipt({ agent: harness, node: 'local', cwd: directory, contextCwd: cwd, contextTask: task, prompt: `[ROLE: IMPLEMENTER]\nWork only in this checkout. Implement the task and its tests. Do not commit.\nTask:\n${task}`, permissions: WRITE, allowUnknownAuth: options.allowUnknownAuth === true }, { dispatchFn: options.dispatchFn, role: 'Race' });
216
- const commit = result.ok ? commitAll(directory, `meta-harness race: ${task.slice(0, 60)}\n\nAgent: ${harness}`) : null;
217
- if (commit && options.gate) linkDependencies(cwd, directory);
218
- const gate = commit ? await (options.gateFn || runQualityGate)(directory, options.gateOptions) : null;
219
- keep = Boolean(commit) && gate?.passed === true;
220
- return { harness, branch: keep ? branch : null, commit: keep ? commit : null, ok: keep, gate, completedAt: Date.now(), durationMs: Date.now() - started, error: result.ok ? (commit ? (keep ? undefined : 'The quality gate failed') : 'No files changed') : result.error,
226
+ const result = await dispatchWithReceipt({ agent: harness, node: 'local', cwd: directory, contextCwd: cwd, contextTask: task, ...sessionOptions({ ...options, cwd }), goal: options.goal || task, prompt: `[ROLE: IMPLEMENTER]\nWork only in this checkout. Implement the task and its tests. Do not commit.\nTask:\n${task}`, permissions: WRITE, allowUnknownAuth: options.allowUnknownAuth === true }, { dispatchFn: options.dispatchFn, role: 'Race' });
227
+ const commit = result.ok || result.cancelled ? commitAll(directory, `meta-harness race: ${task.slice(0, 60)}\n\nAgent: ${harness}`) : null;
228
+ if (result.cancelled) {
229
+ keep = Boolean(commit);
230
+ return { ...result, harness, branch: keep ? branch : null, wip: commit };
231
+ }
232
+ if (result.ok && options.gate) linkDependencies(cwd, directory);
233
+ const gate = result.ok ? await (options.gateFn || runQualityGate)(directory, options.gateOptions) : null;
234
+ if (cancellationResult()) {
235
+ keep = Boolean(commit);
236
+ return { ...cancellationResult(), harness, branch: keep ? branch : null, commit };
237
+ }
238
+ keep = result.ok === true && gate?.passed === true;
239
+ return { harness, branch: keep ? branch : null, commit: keep ? commit : null, ok: keep, gate, goalCheck: result.goalCheck, structured: result.structured, failureKind: result.failureKind, completedAt: Date.now(), durationMs: Date.now() - started, error: result.ok ? keep ? undefined : 'The quality gate failed' : result.error,
221
240
  ...(result.queueId ? { queueId: result.queueId } : {}) };
222
241
  } catch (error) {
223
242
  return { harness, branch: null, commit: null, ok: false, gate: null, durationMs: null, error: String(error.message) };
@@ -227,10 +246,12 @@ async function runRace(task, poolInfo, options) {
227
246
  }
228
247
  }));
229
248
  const finishers = entries.filter(entry => entry.ok).sort((a, b) => a.completedAt - b.completedAt);
230
- return { ok: finishers.length > 0, mode: 'race', task, executedLive: true, gateApplied: true, verification: finishers.length ? 'passed' : 'failed', winner: finishers[0]?.harness || null, excluded: poolInfo.excluded, candidates: entries };
249
+ const limited = entries.some(item => item.failureKind === 'limit');
250
+ return { ok: !limited && finishers.length > 0, ...(limited ? { failureKind: 'limit' } : {}), ...cancellationResult(), mode: 'race', task, executedLive: true, gateApplied: true, verification: !limited && finishers.length ? 'passed' : 'failed', winner: limited ? null : finishers[0]?.harness || null, excluded: poolInfo.excluded, candidates: entries };
231
251
  }
232
252
 
233
253
  export async function coordinateTask(taskDescription, options = {}) {
254
+ if (cancellationResult()) return cancellationResult();
234
255
  const task = String(taskDescription || '').trim();
235
256
  if (!task) throw new Error('Task description is required for coordination');
236
257
  const remote = await forwardRemoteOperation('coordinate', task, options);