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.
- package/bin/total-recall.mjs +10 -6
- package/docs/ARCHITECTURE.md +10 -7
- package/docs/reference/cli-reference.md +13 -8
- package/frontend/dist/assets/index-BRYE1jmv.css +1 -0
- package/frontend/dist/assets/index-D1goIPjC.js +5598 -0
- package/frontend/dist/index.html +2 -2
- package/frontend/dist/sw.js +5 -4
- package/frontend/src/App.tsx +185 -238
- package/frontend/src/api/auth-inventory.ts +62 -0
- package/frontend/src/api/memory.ts +13 -8
- package/frontend/src/api/plugin-credentials.ts +23 -0
- package/frontend/src/api/plugins.ts +37 -53
- package/frontend/src/api/workspace-drawers.ts +97 -0
- package/frontend/src/components/BrainSelector.tsx +9 -3
- package/frontend/src/components/plugins/PluginPanel.tsx +83 -42
- package/frontend/src/components/plugins/PluginSettings.tsx +10 -20
- package/frontend/src/components/plugins/PluginWorkspace.tsx +367 -0
- package/frontend/src/components/plugins/ScopedPluginCommands.tsx +142 -0
- package/frontend/src/components/plugins/plugin-frame.ts +44 -33
- package/frontend/src/components/plugins/scoped-plugin-operations.ts +28 -0
- package/frontend/src/components/workspace/DrawerChat.tsx +110 -0
- package/frontend/src/components/workspace/DrawerLayout.tsx +196 -0
- package/frontend/src/components/workspace/DrawerMarkup.tsx +49 -0
- package/frontend/src/components/workspace/PluginChunkLibrary.tsx +92 -0
- package/frontend/src/components/workspace/PluginHome.tsx +76 -0
- package/frontend/src/components/workspace/WorkspaceDrawers.tsx +96 -0
- package/frontend/src/components/workspace/WorkspacePage.tsx +95 -0
- package/frontend/src/components/workspace/usePluginDesign.ts +38 -0
- package/frontend/src/index.css +409 -136
- package/frontend/src/pages/HelpPage.tsx +15 -46
- package/frontend/src/pages/MemoryPage.tsx +19 -36
- package/frontend/src/pages/SettingsPage.tsx +1 -1
- package/metadata.plugin.schema.json +53 -227
- package/package.json +1 -1
- package/scaffold/.agent/skills/plugin/SKILL.md +13 -11
- package/scaffold/.agent/skills/plugin/evals/README.md +14 -21
- package/scaffold/.agent/skills/plugin/evals/cases/01-fabricated-seed.mjs +15 -18
- package/scaffold/.agent/skills/plugin/evals/cases/02-reassuring-default.mjs +19 -24
- package/scaffold/.agent/skills/plugin/evals/cases/03-declared-template.mjs +12 -20
- package/scaffold/.agent/skills/plugin/evals/cases/04-home-collects-credential.mjs +35 -0
- package/scaffold/.agent/skills/plugin/evals/cases/05-host-leak-in-scaffold.mjs +8 -28
- package/scaffold/.agent/skills/plugin/evals/cases/06-correct-plugin.mjs +85 -47
- package/scaffold/.agent/skills/plugin/evals/run.mjs +17 -18
- package/scaffold/.agent/skills/plugin/prompts/05-credentials.md +4 -4
- package/scaffold/.agent/skills/plugin/prompts/07-scaffold.md +2 -2
- package/scaffold/.agent/skills/plugin/prompts/08-verify.md +4 -4
- package/scaffold/.agent/skills/plugin/references/anti-patterns.md +2 -2
- package/scaffold/.agent/skills/plugin/references/generative-home.md +38 -0
- package/scaffold/.agent/skills/plugin/references/scaffold.md +5 -5
- package/scaffold/.agent/skills/plugin/references/surfaces.md +13 -4
- package/scaffold/.agent/skills/plugin/scripts/record.mjs +75 -153
- package/scaffold/.agent/skills/plugin/subagents/plugin-reviewer.md +4 -4
- package/scaffold/.agent/skills/total-recall/plugins/auth/DESIGN.md +76 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/README.md +3 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/cli.mjs +20 -15
- package/scaffold/.agent/skills/total-recall/plugins/auth/package-lock.json +7 -7
- package/scaffold/.agent/skills/total-recall/plugins/auth/package.json +2 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/plugin.json +13 -51
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/cli-secret.mjs +20 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-store.mjs +12 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/access-overview.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/agent-login.md +20 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/baseline-audit.md +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/credential-repository-binding.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/credential-store-maintenance.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/dashboard-password.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/dashboard-sessions.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/endpoint-probe.md +23 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/google-sign-in.md +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/mesh-machines.md +16 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/passkey-enrollment.md +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/registered-brains.md +16 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/scoped-access-keys.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/secret-inventory.md +25 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/chunks/signing-key-rotation.md +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/home.js +375 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/BRICK.md +7 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/DESIGN.md +53 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/LICENSE +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/README.md +66 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/adapters.mjs +247 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/capability/index.mjs +16 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/cli.mjs +130 -0
- package/{src/core/plugin-cron.mjs → scaffold/.agent/skills/total-recall/plugins/automations/cron.mjs} +1 -3
- package/scaffold/.agent/skills/total-recall/plugins/automations/inventory.mjs +88 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/package-lock.json +7661 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/package.json +36 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/plugin.json +151 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/scheduler.mjs +246 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/ui/automations.js +2 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/ui/chunks/control-ledger.md +42 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/ui/chunks/local-inventory-triage.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/ui/chunks/pending-control.md +39 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/ui/chunks/run-evidence.md +41 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/ui/chunks/schedule-vs-run-evidence.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/ui/chunks/source-coverage.md +41 -0
- package/scaffold/.agent/skills/total-recall/plugins/automations/ui/home.js +278 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/DESIGN.md +53 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/cli.mjs +23 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/run-core.mjs +5 -3
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/check.mjs +61 -7
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/report-data.mjs +40 -14
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/report.mjs +15 -40
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/package-lock.json +7 -7
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/package.json +2 -1
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/plugin.json +40 -40
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/config.md +15 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/detect.md +15 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/gate.md +15 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/init.md +15 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/report.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/run.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/chunks/snapshot.md +15 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/code-quality-report.mjs +55 -23
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/home.mjs +214 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/DESIGN.md +146 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package-lock.json +7 -7
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package.json +2 -1
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/plugin.json +51 -34
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/command.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/create.md +35 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/history.md +35 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/library.md +49 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/list.md +42 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/promote.md +40 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/read.md +25 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/remove.md +26 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/rollback.md +26 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/show.md +53 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/status.md +40 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/test-path.md +38 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/test.md +46 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/chunks/update.md +32 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/home.js +533 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/DESIGN.md +110 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +7 -7
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/package.json +2 -1
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/plugin.json +13 -10
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/chunks/benchmark.md +49 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/chunks/coverage.md +53 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/chunks/models.md +43 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/chunks/pull.md +41 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/chunks/status.md +59 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/home.js +353 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/DESIGN.md +31 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/README.md +136 -0
- package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/cli.mjs +140 -24
- package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/package-lock.json +9 -9
- package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/package.json +4 -2
- package/scaffold/.agent/skills/total-recall/plugins/git/plugin.json +151 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/lib/context.mjs +94 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/lib/phase3-spec-support.mjs +50 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/lib/runtime.mjs +40 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/lib/spec-support.mjs +51 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/_support.mjs +195 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/auth.mjs +201 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/bisect.mjs +40 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/cli-meta.mjs +159 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/commit-memory.mjs +54 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/commit.mjs +46 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/fleet.mjs +174 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/force-push.mjs +58 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/group.mjs +119 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/handoff.mjs +59 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/message.mjs +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/resolve.mjs +60 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/rules.mjs +81 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/secret-firewall.mjs +92 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/undo.mjs +156 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/unmerged.mjs +182 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/src/ops/why.mjs +16 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/change-comparison.md +27 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/commit-identity.md +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/create-worktree.md +25 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/registered-worktrees.md +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/remove-worktree.md +23 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/repository-ledger.md +25 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/unmerged-work.md +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/upstream-divergence.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/worktree-conflict-triage.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/ui/chunks/worktree-terminal.md +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/git/ui/home.js +273 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/DESIGN.md +35 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/README.md +4 -5
- package/scaffold/.agent/skills/total-recall/plugins/mesh/cli.mjs +9 -3
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package-lock.json +7 -7
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package.json +2 -1
- package/scaffold/.agent/skills/total-recall/plugins/mesh/plugin.json +37 -39
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/cli/mesh.mjs +5 -3
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/active-device-dispatch.md +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/control-probe.md +25 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/control-server.md +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/credential-readiness.md +23 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/elected-leader.md +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/enrollment-key.md +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/file-receipt.md +25 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/fleet-execution.md +23 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/fleet-presence.md +23 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/interactive-ssh.md +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/local-enrollment.md +23 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/mesh-policy.md +23 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/network-atlas.md +25 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/node-roster.md +26 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/node-selection-proof.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/owner-observations.md +23 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/peer-path.md +23 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/pinned-leader.md +24 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/reachability-evidence-ladder.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/recorded-login.md +23 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/remote-execution.md +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/chunks/secret-grants.md +26 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/{card.js → home.js} +93 -22
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/DESIGN.md +86 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/README.md +44 -9
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/capability/BRICK.md +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/capability/index.mjs +18 -6
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/cli.mjs +128 -24
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package-lock.json +7 -7
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package.json +3 -2
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/plugin.json +188 -34
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/skills/meta-harness/SKILL.md +17 -3
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/adoption.mjs +2 -2
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/alerts.mjs +3 -3
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/cascade.mjs +92 -18
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/chat.mjs +80 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/control.mjs +145 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/coordinator.mjs +42 -21
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/dispatch-request.mjs +4 -2
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/dispatch.mjs +33 -9
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/documentation.mjs +55 -11
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/executor.mjs +26 -4
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/hermes.mjs +29 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/index.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/included-usage.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/jobs.mjs +84 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/learner.mjs +3 -12
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/maintenance.mjs +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/mcp-server.mjs +228 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/process-manager.mjs +25 -2
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/reasoning.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/remote-operation.mjs +5 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/review.mjs +34 -3
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/reviews.mjs +25 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/router.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/claude.mjs +319 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/codex.mjs +387 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/result.mjs +67 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/resume.mjs +373 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session/run.mjs +223 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/session-jobs.mjs +72 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/setup.mjs +6 -3
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/termination.mjs +16 -12
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/threads.mjs +32 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/usage-queue.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/verify-ssss.mjs +3 -3
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worker.mjs +285 -40
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worktree.mjs +76 -9
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/adapter-docs-freshness.md +16 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/chat-handoff.md +26 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/fleet-maintenance.md +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/harness-inventory.md +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/included-access.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/included-usage-verification.md +15 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/independent-review.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/managed-execution.md +20 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/official-documentation.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/quota-recovery.md +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/review-routing-decision.md +16 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/route-work.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/routing-evidence.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/chunks/run-ledger.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/home.js +198 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/DESIGN.md +75 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/README.md +2 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/cli.mjs +64 -32
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +7 -7
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +2 -1
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +10 -44
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/generate.mjs +5 -4
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/graph.mjs +3 -4
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/ingest.mjs +23 -18
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/mesh-vaults.mjs +8 -3
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/refresh-diagnostics.mjs +44 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/refresh.mjs +123 -113
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/search.mjs +3 -5
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/token.mjs +21 -47
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/upstream-watcher.mjs +17 -6
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/auto-refresh.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/bootstrap-pages.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/generate-wiki.md +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/generator-settings.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/host-probe.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/ingest-preview.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/link-hygiene.md +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/link-topology.md +19 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/passage-search.md +19 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/propagation-plan.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/provider-credentials.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/refresh-report.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/task-evidence.md +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/upstream-record.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/watch-upstream.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/chunks/wiki-atlas.md +19 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/panel.js +261 -513
- package/scaffold/.agent/skills/total-recall/plugins/security/DESIGN.md +84 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/README.md +4 -2
- package/scaffold/.agent/skills/total-recall/plugins/security/cli.mjs +7 -19
- package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +7 -7
- package/scaffold/.agent/skills/total-recall/plugins/security/package.json +2 -3
- package/scaffold/.agent/skills/total-recall/plugins/security/plugin.json +19 -52
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/device-evidence.md +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/evidence-desk.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/guided-device-review.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/host-and-network.md +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/https-headers.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/mesh-coverage.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/private-security-tools.md +16 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/single-origin-header-check.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/source-review.md +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/chunks/ssh-permission-plan.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/home.js +283 -0
- package/scaffold/.agent/skills/total-recall/plugins/ssss/DESIGN.md +93 -0
- package/scaffold/.agent/skills/total-recall/plugins/ssss/package-lock.json +2 -2
- package/scaffold/.agent/skills/total-recall/plugins/ssss/package.json +2 -1
- package/scaffold/.agent/skills/total-recall/plugins/ssss/plugin.json +5 -3
- package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/conformance.md +26 -0
- package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/registry.md +37 -0
- package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/repair.md +42 -0
- package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/scaffold.md +33 -0
- package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/status.md +43 -0
- package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/validate.md +34 -0
- package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/validation-diagnostics.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/chunks/workspace-registry-preflight.md +17 -0
- package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/home.js +348 -0
- package/scaffold/.agent/skills/total-recall/references/operations.md +3 -1
- package/src/cli/agent.mjs +1 -1
- package/src/cli/api.mjs +3 -3
- package/src/cli/brief.mjs +3 -0
- package/src/cli/command.mjs +41 -2
- package/src/cli/config.mjs +16 -0
- package/src/cli/connect.mjs +2 -7
- package/src/cli/dream.mjs +93 -69
- package/src/cli/harness.mjs +4 -4
- package/src/cli/help.mjs +1 -1
- package/src/cli/init.mjs +0 -25
- package/src/cli/lessons.mjs +51 -0
- package/src/cli/memory-plugin.mjs +1 -1
- package/src/cli/plugin/chunks.mjs +145 -0
- package/src/cli/plugin/copies.mjs +64 -14
- package/src/cli/plugin/create.mjs +30 -59
- package/src/cli/plugin/index.mjs +31 -16
- package/src/cli/plugin/install.mjs +10 -15
- package/src/cli/plugin/remove.mjs +1 -1
- package/src/cli/plugin/search.mjs +2 -2
- package/src/cli/preflight.mjs +19 -6
- package/src/cli/proposals.mjs +1 -1
- package/src/cli/recall.mjs +66 -23
- package/src/cli/remember.mjs +3 -14
- package/src/cli/server.mjs +18 -0
- package/src/cli/setup.mjs +2 -9
- package/src/cli/start.mjs +1 -0
- package/src/cli/startup.mjs +19 -0
- package/src/cli/task.mjs +9 -2
- package/src/cli/update.mjs +6 -2
- package/src/core/access-ledger.mjs +78 -0
- package/src/core/agent-manager.mjs +2 -2
- package/src/core/app-deploy/design-tokens.mjs +14 -9
- package/src/core/app-deploy/ui-elements.mjs +1 -1
- package/src/core/brain-locate.mjs +15 -0
- package/src/core/brain-registry.mjs +5 -0
- package/src/core/brain-updates.mjs +35 -11
- package/src/core/claude-hooks.mjs +3 -1
- package/src/core/cleanup-task.mjs +15 -0
- package/src/core/cli-commands.mjs +2 -0
- package/src/core/cli-maintenance.mjs +4 -1
- package/src/core/cli-semantic-index.mjs +23 -3
- package/src/core/context-compiler.mjs +20 -0
- package/src/core/daemon-control.mjs +5 -1
- package/src/core/daemon-loop.mjs +39 -62
- package/src/core/dashboard-bundle.mjs +261 -0
- package/src/core/default-plugins.mjs +1 -1
- package/src/core/dream.mjs +33 -105
- package/src/core/ensure-dependencies.mjs +152 -0
- package/src/core/fast-recall.mjs +2 -2
- package/src/core/field-evaluation.mjs +23 -0
- package/src/core/gate-mode.mjs +1 -1
- package/src/core/harness-hooks.mjs +14 -2
- package/src/core/lesson-hooks.mjs +74 -0
- package/src/core/lesson-memory.mjs +24 -0
- package/src/core/lesson-sessions.mjs +52 -0
- package/src/core/lessons.mjs +96 -0
- package/src/core/memory-brain.mjs +30 -8
- package/src/core/meta-harness.mjs +2 -2
- package/src/core/optimizer.mjs +66 -165
- package/src/core/plugin-activation.mjs +63 -20
- package/src/core/plugin-contracts/{card.mjs → manifest.mjs} +26 -57
- package/src/core/plugin-contracts/startup-check.mjs +49 -0
- package/src/core/plugin-contracts/ui.mjs +9 -3
- package/src/core/plugin-distribution.mjs +6 -6
- package/src/core/plugin-lifecycle.mjs +58 -0
- package/src/core/plugin-loader.mjs +21 -8
- package/src/core/plugin-runner.mjs +21 -10
- package/src/core/plugin-runtime.mjs +40 -1
- package/src/core/plugin-startup-checks.mjs +44 -0
- package/src/core/plugin-state.mjs +19 -5
- package/src/core/plugin-store.mjs +64 -33
- package/src/core/post-mortem.mjs +52 -36
- package/src/core/preflight-gate.mjs +23 -2
- package/src/core/project-id.mjs +15 -5
- package/src/core/proposal-applier.mjs +31 -2
- package/src/core/repo-sync.mjs +4 -6
- package/src/core/required-plugins.mjs +57 -10
- package/src/core/rule-length.mjs +23 -0
- package/src/core/runtime.mjs +1 -1
- package/src/core/scheduler.mjs +35 -43
- package/src/core/schema.mjs +3 -2
- package/src/core/search.mjs +4 -3
- package/src/core/server-service.mjs +148 -0
- package/src/core/session-watcher.mjs +19 -1
- package/src/core/skill-gap.mjs +16 -0
- package/src/core/ssss-kernel-bridge.mjs +32 -4
- package/src/core/ssss-operation-service.mjs +1 -0
- package/src/core/startup-health.mjs +34 -4
- package/src/core/surface.mjs +65 -54
- package/src/core/task-executors.mjs +54 -106
- package/src/core/testing/plugin-fixture.mjs +22 -21
- package/src/core/throttled-fetch.mjs +25 -18
- package/src/core/ui-chunk-choice.mjs +77 -0
- package/src/core/ui-chunk.registry.json +23 -0
- package/src/core/ui-chunks.mjs +434 -0
- package/src/core/validated-write.mjs +1 -0
- package/src/core/vault-cache.mjs +58 -59
- package/src/core/vector-field.mjs +134 -192
- package/src/core/workspace-compositions.mjs +151 -0
- package/src/core/workspace-drawers.mjs +79 -0
- package/src/server/api.mjs +4 -27
- package/src/server/index.mjs +28 -21
- package/src/server/invoke-core-child.mjs +24 -0
- package/src/server/invoke-core-operations.mjs +102 -0
- package/src/server/memory-events.mjs +31 -0
- package/src/server/plugin-brain-targets.mjs +89 -0
- package/src/server/rest.mjs +2 -0
- package/src/server/route-inventory.mjs +3 -2
- package/src/server/route-manifest.json +47 -55
- package/src/server/routes/_shared.mjs +7 -14
- package/src/server/routes/brains.mjs +29 -62
- package/src/server/routes/context.mjs +0 -41
- package/src/server/routes/dashboard.mjs +26 -0
- package/src/server/routes/invoke.mjs +26 -1
- package/src/server/routes/plugins.mjs +154 -45
- package/src/server/routes/ssss.mjs +0 -169
- package/src/server/routes/system.mjs +0 -15
- package/src/server/routes/tasks.mjs +0 -26
- package/src/server/routes/ui-chunks.mjs +192 -0
- package/src/server/routes/vault.mjs +2 -23
- package/frontend/dist/assets/index-DD4qONvH.js +0 -5607
- package/frontend/dist/assets/index-DtwsDyFM.css +0 -1
- package/frontend/src/components/plugins/card/CardEngine.tsx +0 -108
- package/frontend/src/components/plugins/card/CardScenes.tsx +0 -103
- package/frontend/src/components/plugins/card/ExecutionPicker.tsx +0 -64
- package/frontend/src/components/plugins/card/args.ts +0 -28
- package/frontend/src/components/plugins/card/card.css +0 -32
- package/frontend/src/components/plugins/card/cli-line.ts +0 -13
- package/frontend/src/components/plugins/card/contract.ts +0 -91
- package/frontend/src/components/plugins/card/format.ts +0 -49
- package/frontend/src/components/plugins/card/runner-context.ts +0 -11
- package/frontend/src/components/plugins/card/runner.tsx +0 -43
- package/frontend/src/components/plugins/card/theme.ts +0 -35
- package/frontend/src/components/plugins/card/use-source.ts +0 -22
- package/frontend/src/components/plugins/card/widgets.tsx +0 -94
- package/frontend/src/components/plugins/workspace/WorkspaceHost.tsx +0 -23
- package/frontend/src/pages/PluginsPage.tsx +0 -1719
- package/scaffold/.agent/skills/plugin/evals/cases/04-card-collects-credential.mjs +0 -47
- package/scaffold/.agent/skills/plugin/prompts/06-card.md +0 -46
- package/scaffold/.agent/skills/plugin/references/card.md +0 -90
- package/scaffold/.agent/skills/plugin/subagents/card-designer.md +0 -60
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/DESIGN.md +0 -67
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/card.js +0 -1179
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/DESIGN.md +0 -32
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/card.js +0 -588
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/card.js +0 -575
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/DESIGN.md +0 -32
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/card.js +0 -482
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/README.md +0 -53
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/plugin.json +0 -129
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/ui/DESIGN.md +0 -100
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/ui/card.js +0 -637
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/DESIGN.md +0 -217
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/DESIGN.md +0 -32
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/card.js +0 -1276
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/DESIGN.md +0 -45
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/card.js +0 -1401
- package/scaffold/.agent/skills/total-recall/plugins/security/scripts/build-ui.mjs +0 -4
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/DESIGN.md +0 -41
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/card.js +0 -396
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/panel.js +0 -98
- package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/DESIGN.md +0 -15
- package/scaffold/.agent/skills/total-recall/plugins/ssss/ui/card.js +0 -182
- package/src/core/append-log.mjs +0 -131
- package/src/core/plugin-tasks.mjs +0 -173
- package/templates/onboarding-interview.md +0 -82
- /package/scaffold/.agent/skills/total-recall/plugins/{git-sentinel → git}/LICENSE +0 -0
package/scaffold/.agent/skills/total-recall/plugins/meta-harness/skills/meta-harness/SKILL.md
CHANGED
|
@@ -4,7 +4,7 @@ description: Discover, route, coordinate, review and run coding agent harnesses
|
|
|
4
4
|
repo_scoped: false
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
#
|
|
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`.
|
|
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
|
-
- `
|
|
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
|
|
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
|
|
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 = '
|
|
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: `
|
|
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: `
|
|
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
|
-
|
|
44
|
-
const
|
|
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 },
|
|
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.
|
|
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
|
-
|
|
57
|
-
|
|
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) ||
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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 (
|
|
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: '
|
|
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,
|
|
105
|
-
|
|
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 ? '
|
|
177
|
-
|
|
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
|
-
|
|
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 (
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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
|
-
|
|
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);
|