@sema-agent/server 1.323.0 → 2.0.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/dist/approval-hmac.d.ts +40 -0
- package/dist/approval-hmac.js +67 -0
- package/dist/approval.d.ts +31 -0
- package/dist/approval.js +72 -4
- package/dist/audit.d.ts +26 -0
- package/dist/audit.js +40 -1
- package/dist/auth-bridge.d.ts +10 -0
- package/dist/auth-bridge.js +47 -4
- package/dist/auth-keys.d.ts +19 -0
- package/dist/auth-keys.js +13 -0
- package/dist/bake-runner/main.d.ts +5 -0
- package/dist/bake-runner/main.js +43 -4
- package/dist/bake-runner/protocol.d.ts +57 -0
- package/dist/bake-runner/protocol.js +58 -3
- package/dist/bake-runner/runner.d.ts +54 -0
- package/dist/bake-runner/runner.js +87 -5
- package/dist/bench/l8/artifact.d.ts +63 -0
- package/dist/bench/l8/artifact.js +61 -2
- package/dist/bench/l8/escape.d.ts +36 -0
- package/dist/bench/l8/escape.js +25 -0
- package/dist/bench/l8/index.d.ts +14 -0
- package/dist/bench/l8/index.js +14 -0
- package/dist/bench/l8/probes.d.ts +74 -0
- package/dist/bench/l8/probes.js +60 -0
- package/dist/bench/l8/run-probes.d.ts +43 -0
- package/dist/bench/l8/run-probes.js +45 -2
- package/dist/bench/s1/arms.d.ts +126 -0
- package/dist/bench/s1/arms.js +188 -13
- package/dist/bench/s1/live-deps.d.ts +66 -0
- package/dist/bench/s1/live-deps.js +249 -11
- package/dist/bench/s1/oracle.d.ts +49 -0
- package/dist/bench/s1/oracle.js +36 -1
- package/dist/bench/s1/repair-oracle-adapter.d.ts +33 -0
- package/dist/bench/s1/repair-oracle-adapter.js +30 -0
- package/dist/bench/s1/reviewer.d.ts +38 -0
- package/dist/bench/s1/reviewer.js +34 -0
- package/dist/bench/s1/row.d.ts +77 -0
- package/dist/bench/s1/row.js +62 -0
- package/dist/bench/s1/run-firm.d.ts +47 -0
- package/dist/bench/s1/run-firm.js +70 -4
- package/dist/bench/s1/runner-ctx.d.ts +71 -0
- package/dist/bench/s1/runner-ctx.js +36 -0
- package/dist/bench/s1/tasks.d.ts +108 -0
- package/dist/bench/s1/tasks.js +115 -0
- package/dist/boot-reclaim.d.ts +19 -0
- package/dist/boot-reclaim.js +20 -0
- package/dist/brain.d.ts +25 -0
- package/dist/brain.js +61 -1
- package/dist/budget.d.ts +66 -0
- package/dist/budget.js +128 -4
- package/dist/capabilities/builtin-tools.d.ts +3 -0
- package/dist/capabilities/builtin-tools.js +3 -0
- package/dist/capabilities/center-plugins.d.ts +17 -1
- package/dist/capabilities/center-plugins.js +45 -4
- package/dist/capabilities/center-prompts.d.ts +46 -0
- package/dist/capabilities/center-prompts.js +57 -3
- package/dist/capabilities/code-review-council.d.ts +28 -0
- package/dist/capabilities/code-review-council.js +40 -7
- package/dist/capabilities/collab-wire.d.ts +2 -0
- package/dist/capabilities/collab-workflows.d.ts +7 -0
- package/dist/capabilities/collab-workflows.js +44 -1
- package/dist/capabilities/oa-tools.d.ts +6 -0
- package/dist/capabilities/oa-tools.js +10 -0
- package/dist/capabilities/prompt.d.ts +14 -0
- package/dist/capabilities/prompt.js +14 -0
- package/dist/capabilities/prompts/code-review.d.ts +14 -0
- package/dist/capabilities/prompts/code-review.js +14 -0
- package/dist/capabilities/prompts/identity.d.ts +6 -0
- package/dist/capabilities/prompts/identity.js +6 -0
- package/dist/capabilities/prompts/team.d.ts +4 -0
- package/dist/capabilities/prompts/team.js +4 -0
- package/dist/capabilities/repo-tools.d.ts +11 -0
- package/dist/capabilities/repo-tools.js +8 -0
- package/dist/capabilities/sandbox-file-send.d.ts +95 -0
- package/dist/capabilities/sandbox-file-send.js +91 -3
- package/dist/capabilities/scenarios.d.ts +130 -0
- package/dist/capabilities/scenarios.js +180 -7
- package/dist/capabilities/select-environment-tool.d.ts +7 -0
- package/dist/capabilities/select-environment-tool.js +30 -0
- package/dist/capabilities/send-user-file-tool.d.ts +33 -0
- package/dist/capabilities/send-user-file-tool.js +65 -1
- package/dist/capabilities/skills.d.ts +29 -0
- package/dist/capabilities/skills.js +13 -2
- package/dist/capabilities/team.d.ts +22 -0
- package/dist/capabilities/team.js +22 -2
- package/dist/capabilities/tool-defer.d.ts +26 -0
- package/dist/capabilities/tool-defer.js +3 -0
- package/dist/config-center/apply-effective.d.ts +65 -0
- package/dist/config-center/apply-effective.js +204 -12
- package/dist/config-center/http-client.d.ts +34 -0
- package/dist/config-center/http-client.js +68 -0
- package/dist/config-center/restart-signal.d.ts +22 -0
- package/dist/config-center/restart-signal.js +39 -0
- package/dist/config-center/skills-mcp.d.ts +20 -1
- package/dist/config-center/skills-mcp.js +42 -7
- package/dist/config-center/types.d.ts +92 -1
- package/dist/config-lkg.d.ts +12 -0
- package/dist/config-lkg.js +41 -0
- package/dist/config-provider.d.ts +78 -0
- package/dist/config-provider.js +165 -2
- package/dist/config-types.d.ts +546 -0
- package/dist/config.d.ts +64 -0
- package/dist/config.js +455 -60
- package/dist/degenerate-instrument.d.ts +38 -0
- package/dist/degenerate-instrument.js +55 -3
- package/dist/elicitation.d.ts +39 -0
- package/dist/elicitation.js +87 -3
- package/dist/env-facts.d.ts +61 -0
- package/dist/env-facts.js +73 -7
- package/dist/fleet/fleet-bus.d.ts +185 -0
- package/dist/fleet/fleet-bus.js +333 -27
- package/dist/fleet/subagent-tail-bus.d.ts +11 -0
- package/dist/fleet/subagent-tail-bus.js +33 -2
- package/dist/fleet-client.d.ts +38 -0
- package/dist/fleet-client.js +44 -9
- package/dist/fleet-lease.d.ts +46 -0
- package/dist/fleet-lease.js +73 -5
- package/dist/hooks/branch-transcript.d.ts +8 -0
- package/dist/hooks/branch-transcript.js +33 -0
- package/dist/hooks/cc-stop-prompt.d.ts +42 -0
- package/dist/hooks/cc-stop-prompt.js +43 -1
- package/dist/hooks/hook-llm.d.ts +15 -0
- package/dist/hooks/hook-llm.js +47 -0
- package/dist/hooks/hook-runner.d.ts +89 -0
- package/dist/hooks/hook-runner.js +327 -18
- package/dist/http/idempotency.d.ts +31 -0
- package/dist/http/idempotency.js +34 -0
- package/dist/http/principal-gate.d.ts +17 -0
- package/dist/http/principal-gate.js +35 -3
- package/dist/http/route-ctx.d.ts +36 -0
- package/dist/http/routes/approvals-assistant.d.ts +25 -0
- package/dist/http/routes/approvals-assistant.js +253 -23
- package/dist/http/routes/attachments.js +22 -4
- package/dist/http/routes/capabilities.d.ts +7 -0
- package/dist/http/routes/capabilities.js +202 -1
- package/dist/http/routes/fleet.d.ts +6 -0
- package/dist/http/routes/fleet.js +94 -5
- package/dist/http/routes/images.d.ts +17 -0
- package/dist/http/routes/images.js +222 -14
- package/dist/http/routes/leader.d.ts +7 -0
- package/dist/http/routes/leader.js +10 -0
- package/dist/http/routes/memory-policy.d.ts +6 -0
- package/dist/http/routes/memory-policy.js +38 -6
- package/dist/http/routes/notify-wake.d.ts +10 -0
- package/dist/http/routes/notify-wake.js +19 -3
- package/dist/http/routes/observability.d.ts +6 -0
- package/dist/http/routes/observability.js +19 -2
- package/dist/http/routes/runs.d.ts +15 -0
- package/dist/http/routes/runs.js +464 -41
- package/dist/http/routes/session-sync.d.ts +15 -0
- package/dist/http/routes/session-sync.js +229 -19
- package/dist/http/routes/sessions-list.d.ts +6 -0
- package/dist/http/routes/sessions-list.js +35 -4
- package/dist/http/routes/sessions.d.ts +14 -0
- package/dist/http/routes/sessions.js +176 -31
- package/dist/http/routes/side-query.d.ts +8 -0
- package/dist/http/routes/side-query.js +26 -1
- package/dist/http/routes/tasks.d.ts +18 -0
- package/dist/http/routes/tasks.js +501 -38
- package/dist/http/routes/trace-usage.d.ts +10 -0
- package/dist/http/routes/trace-usage.js +66 -14
- package/dist/http/routes/workflows.d.ts +12 -0
- package/dist/http/routes/workflows.js +180 -19
- package/dist/http/run-meta.d.ts +11 -0
- package/dist/http/run-meta.js +6 -0
- package/dist/http/send.d.ts +19 -0
- package/dist/http/send.js +11 -0
- package/dist/http/server.d.ts +255 -0
- package/dist/http/server.js +921 -75
- package/dist/http/sse-log.d.ts +15 -0
- package/dist/http/sse-log.js +16 -0
- package/dist/http/tar.d.ts +9 -0
- package/dist/http/tar.js +21 -5
- package/dist/http/wire-gate.d.ts +9 -0
- package/dist/http/wire-gate.js +9 -0
- package/dist/http/wire-types.d.ts +187 -0
- package/dist/http/workspace-content.d.ts +8 -0
- package/dist/http/workspace-content.js +10 -0
- package/dist/images/bake-validate.d.ts +63 -0
- package/dist/images/bake-validate.js +71 -1
- package/dist/images/manifest.d.ts +19 -0
- package/dist/images/manifest.js +6 -1
- package/dist/index.d.ts +1 -5
- package/dist/index.js +10 -5
- package/dist/key-resolver.d.ts +23 -0
- package/dist/key-resolver.js +27 -2
- package/dist/leader/diffout.d.ts +23 -0
- package/dist/leader/diffout.js +8 -1
- package/dist/leader/diffup.d.ts +32 -0
- package/dist/leader/diffup.js +43 -0
- package/dist/leader/endpoint.d.ts +20 -0
- package/dist/leader/endpoint.js +25 -2
- package/dist/leader/fanout.d.ts +60 -0
- package/dist/leader/fanout.js +29 -3
- package/dist/leader/grader-env-factory.d.ts +74 -0
- package/dist/leader/grader-env-factory.js +62 -3
- package/dist/leader/leader.d.ts +97 -0
- package/dist/leader/leader.js +151 -4
- package/dist/leader/merge.d.ts +94 -0
- package/dist/leader/merge.js +112 -9
- package/dist/leader/planner.d.ts +52 -0
- package/dist/leader/planner.js +47 -3
- package/dist/leader/repair-oracle.d.ts +61 -0
- package/dist/leader/repair-oracle.js +59 -1
- package/dist/leader/repair-wire.d.ts +78 -0
- package/dist/leader/repair-wire.js +79 -2
- package/dist/leader/wire.d.ts +79 -0
- package/dist/leader/wire.js +255 -8
- package/dist/lsp/e2b-bridge.d.ts +16 -0
- package/dist/lsp/e2b-bridge.js +32 -4
- package/dist/lsp/e2b-manager.d.ts +10 -0
- package/dist/lsp/e2b-manager.js +18 -2
- package/dist/lsp/lsp-frames.d.ts +12 -0
- package/dist/lsp/lsp-frames.js +12 -0
- package/dist/lsp/manager.d.ts +12 -0
- package/dist/lsp/manager.js +35 -2
- package/dist/lsp/types.d.ts +10 -0
- package/dist/lsp/ws-transport.d.ts +12 -0
- package/dist/lsp/ws-transport.js +53 -5
- package/dist/lsp-evict.d.ts +14 -0
- package/dist/lsp-evict.js +15 -1
- package/dist/main.js +2132 -96
- package/dist/memory-export.d.ts +1 -0
- package/dist/memory-export.js +4 -0
- package/dist/memory-scope.d.ts +49 -0
- package/dist/memory-scope.js +87 -4
- package/dist/memory-sync-client.d.ts +24 -0
- package/dist/memory-sync-client.js +41 -2
- package/dist/memory-sync.d.ts +23 -0
- package/dist/memory-sync.js +70 -1
- package/dist/model-select.d.ts +39 -0
- package/dist/model-select.js +42 -1
- package/dist/observability/cost-quota.d.ts +20 -0
- package/dist/observability/cost-quota.js +4 -0
- package/dist/observability/cost-taxonomy.d.ts +45 -0
- package/dist/observability/cost-taxonomy.js +34 -0
- package/dist/observability/logger.d.ts +1 -0
- package/dist/observability/logger.js +6 -0
- package/dist/observability/metrics.d.ts +13 -0
- package/dist/observability/metrics.js +63 -0
- package/dist/observability/otel-exporter.d.ts +9 -0
- package/dist/observability/otel-exporter.js +11 -3
- package/dist/observability/principal-context.d.ts +9 -0
- package/dist/observability/principal-context.js +9 -0
- package/dist/observability/prompt-manifest.d.ts +37 -0
- package/dist/observability/prompt-manifest.js +35 -1
- package/dist/observability/rate-limit.d.ts +10 -0
- package/dist/observability/rate-limit.js +1 -0
- package/dist/observability/tool-trace.d.ts +24 -0
- package/dist/observability/tool-trace.js +41 -1
- package/dist/orchestration/hardened-vm-runner.d.ts +107 -0
- package/dist/orchestration/hardened-vm-runner.js +119 -4
- package/dist/orchestration/hardened-vm-worker-runner.d.ts +14 -0
- package/dist/orchestration/hardened-vm-worker-runner.js +26 -1
- package/dist/orchestration/hardened-vm-worker.js +27 -0
- package/dist/orchestration/subagent-steer.d.ts +44 -0
- package/dist/orchestration/subagent-steer.js +45 -1
- package/dist/orchestration/workflow-agent-steer.d.ts +54 -0
- package/dist/orchestration/workflow-agent-steer.js +80 -1
- package/dist/orchestration/workflow-completion-inbox.d.ts +189 -2
- package/dist/orchestration/workflow-completion-inbox.js +283 -32
- package/dist/orchestration/workflow-notify-journal.d.ts +132 -1
- package/dist/orchestration/workflow-notify-journal.js +187 -13
- package/dist/parked-decide.d.ts +61 -0
- package/dist/parked-decide.js +58 -1
- package/dist/per-task-image.d.ts +54 -0
- package/dist/per-task-image.js +41 -0
- package/dist/plan-cache-probe.d.ts +35 -0
- package/dist/plan-cache-probe.js +18 -3
- package/dist/plugins/approval-exemption-store.d.ts +7 -0
- package/dist/plugins/approval-exemption-store.js +28 -2
- package/dist/plugins/approval-store-sql.d.ts +64 -0
- package/dist/plugins/approval-store-sql.js +32 -0
- package/dist/plugins/background-agent-store-sql.d.ts +55 -0
- package/dist/plugins/background-agent-store-sql.js +91 -4
- package/dist/plugins/background-shell-support.d.ts +98 -0
- package/dist/plugins/background-shell-support.js +117 -13
- package/dist/plugins/blob-backend.d.ts +104 -0
- package/dist/plugins/blob-backend.js +163 -6
- package/dist/plugins/breaker-state-sql.d.ts +65 -1
- package/dist/plugins/breaker-state-sql.js +46 -8
- package/dist/plugins/caching-session-store.d.ts +42 -0
- package/dist/plugins/caching-session-store.js +63 -3
- package/dist/plugins/checkpoint-store-sql.d.ts +179 -0
- package/dist/plugins/checkpoint-store-sql.js +239 -11
- package/dist/plugins/file-outcome-sink.d.ts +4 -0
- package/dist/plugins/file-outcome-sink.js +9 -0
- package/dist/plugins/file-resume-anchor-store.d.ts +20 -0
- package/dist/plugins/file-resume-anchor-store.js +43 -4
- package/dist/plugins/file-run-store.d.ts +90 -0
- package/dist/plugins/file-run-store.js +145 -13
- package/dist/plugins/file-snapshot-store-sql.d.ts +133 -1
- package/dist/plugins/file-snapshot-store-sql.js +178 -10
- package/dist/plugins/file-workflow-journal-store.d.ts +10 -0
- package/dist/plugins/file-workflow-journal-store.js +10 -0
- package/dist/plugins/fork-routing-session-store.d.ts +47 -0
- package/dist/plugins/fork-routing-session-store.js +51 -3
- package/dist/plugins/host-platform.d.ts +69 -0
- package/dist/plugins/host-platform.js +81 -2
- package/dist/plugins/image-bake-store-sql.d.ts +136 -1
- package/dist/plugins/image-bake-store-sql.js +241 -7
- package/dist/plugins/image-index-sql.d.ts +46 -0
- package/dist/plugins/image-index-sql.js +113 -4
- package/dist/plugins/k8s-bg-scripts.d.ts +34 -0
- package/dist/plugins/k8s-bg-scripts.js +88 -4
- package/dist/plugins/k8s-exec-protocol.d.ts +33 -0
- package/dist/plugins/k8s-exec-protocol.js +41 -0
- package/dist/plugins/local-checkpoint-store.d.ts +40 -0
- package/dist/plugins/local-checkpoint-store.js +86 -7
- package/dist/plugins/local-session-store.d.ts +157 -0
- package/dist/plugins/local-session-store.js +289 -34
- package/dist/plugins/local-task-attachment-store.js +14 -2
- package/dist/plugins/mailbox-store-sql.d.ts +38 -0
- package/dist/plugins/mailbox-store-sql.js +39 -6
- package/dist/plugins/memory-engine-pg.d.ts +30 -0
- package/dist/plugins/memory-engine-pg.js +122 -4
- package/dist/plugins/memory-engine-tidb.d.ts +26 -0
- package/dist/plugins/memory-engine-tidb.js +115 -5
- package/dist/plugins/memory-engine-vector-util.d.ts +5 -0
- package/dist/plugins/memory-engine-vector-util.js +5 -0
- package/dist/plugins/memory-resume-anchor-store.d.ts +8 -0
- package/dist/plugins/memory-resume-anchor-store.js +17 -0
- package/dist/plugins/memory-run-store.d.ts +63 -0
- package/dist/plugins/memory-run-store.js +73 -8
- package/dist/plugins/memory-session-policy-store.d.ts +17 -0
- package/dist/plugins/memory-session-policy-store.js +17 -0
- package/dist/plugins/memory-sync-store-pg.d.ts +42 -0
- package/dist/plugins/memory-sync-store-pg.js +45 -2
- package/dist/plugins/memory-sync-store-tidb.d.ts +9 -0
- package/dist/plugins/memory-sync-store-tidb.js +31 -1
- package/dist/plugins/outcome-ledger-sql.d.ts +63 -0
- package/dist/plugins/outcome-ledger-sql.js +97 -3
- package/dist/plugins/pg-approval-store.d.ts +7 -0
- package/dist/plugins/pg-approval-store.js +7 -0
- package/dist/plugins/pg-breaker-state.d.ts +6 -0
- package/dist/plugins/pg-breaker-state.js +6 -0
- package/dist/plugins/pg-checkpoint-store.d.ts +8 -0
- package/dist/plugins/pg-checkpoint-store.js +8 -0
- package/dist/plugins/pg-cost-quota.d.ts +17 -0
- package/dist/plugins/pg-cost-quota.js +6 -0
- package/dist/plugins/pg-file-snapshot-store.d.ts +6 -0
- package/dist/plugins/pg-file-snapshot-store.js +6 -0
- package/dist/plugins/pg-image-bake.d.ts +9 -0
- package/dist/plugins/pg-image-bake.js +9 -0
- package/dist/plugins/pg-image-index.d.ts +9 -0
- package/dist/plugins/pg-image-index.js +9 -0
- package/dist/plugins/pg-outcome-ledger.d.ts +9 -0
- package/dist/plugins/pg-outcome-ledger.js +9 -0
- package/dist/plugins/pg-pool.d.ts +50 -0
- package/dist/plugins/pg-pool.js +78 -0
- package/dist/plugins/pg-query.d.ts +11 -0
- package/dist/plugins/pg-rate-limiter.d.ts +23 -0
- package/dist/plugins/pg-rate-limiter.js +12 -2
- package/dist/plugins/pg-resume-anchor-store.d.ts +5 -0
- package/dist/plugins/pg-resume-anchor-store.js +5 -0
- package/dist/plugins/pg-run-store.d.ts +7 -0
- package/dist/plugins/pg-run-store.js +7 -0
- package/dist/plugins/pg-safe-json.d.ts +13 -0
- package/dist/plugins/pg-safe-json.js +40 -4
- package/dist/plugins/pg-session-policy-store.d.ts +5 -0
- package/dist/plugins/pg-session-policy-store.js +5 -0
- package/dist/plugins/pg-session-storage.d.ts +72 -0
- package/dist/plugins/pg-session-storage.js +152 -20
- package/dist/plugins/pg-session-store.d.ts +10 -0
- package/dist/plugins/pg-session-store.js +10 -0
- package/dist/plugins/pg-tool-result-store.d.ts +7 -0
- package/dist/plugins/pg-tool-result-store.js +7 -0
- package/dist/plugins/pg-workflow-journal-store.d.ts +7 -0
- package/dist/plugins/pg-workflow-journal-store.js +7 -0
- package/dist/plugins/pg-workflow-run-store.d.ts +7 -0
- package/dist/plugins/pg-workflow-run-store.js +7 -0
- package/dist/plugins/posix-shell-fs.d.ts +3 -0
- package/dist/plugins/posix-shell-fs.js +31 -1
- package/dist/plugins/remote-env-adb.d.ts +25 -0
- package/dist/plugins/remote-env-adb.js +95 -12
- package/dist/plugins/remote-env-e2b.d.ts +146 -0
- package/dist/plugins/remote-env-e2b.js +371 -40
- package/dist/plugins/remote-env-file-error.d.ts +34 -0
- package/dist/plugins/remote-env-file-error.js +37 -0
- package/dist/plugins/remote-env-host.d.ts +151 -0
- package/dist/plugins/remote-env-host.js +468 -70
- package/dist/plugins/remote-env-k8s.d.ts +135 -0
- package/dist/plugins/remote-env-k8s.js +318 -32
- package/dist/plugins/remote-env-local-docker.d.ts +65 -0
- package/dist/plugins/remote-env-local-docker.js +149 -24
- package/dist/plugins/remote-env-ssh.d.ts +35 -0
- package/dist/plugins/remote-env-ssh.js +117 -20
- package/dist/plugins/remote-scratchpad.d.ts +38 -0
- package/dist/plugins/remote-scratchpad.js +32 -2
- package/dist/plugins/remote-shell.d.ts +22 -0
- package/dist/plugins/remote-shell.js +27 -1
- package/dist/plugins/resume-anchor-store-sql.d.ts +28 -0
- package/dist/plugins/resume-anchor-store-sql.js +11 -0
- package/dist/plugins/roster-store-sql.d.ts +27 -0
- package/dist/plugins/roster-store-sql.js +54 -0
- package/dist/plugins/run-store-sql.d.ts +194 -0
- package/dist/plugins/run-store-sql.js +210 -9
- package/dist/plugins/s3-presign.d.ts +20 -0
- package/dist/plugins/s3-presign.js +49 -2
- package/dist/plugins/scheduler-support.d.ts +12 -0
- package/dist/plugins/scheduler-support.js +75 -3
- package/dist/plugins/send-file-ledger.d.ts +15 -0
- package/dist/plugins/send-file-ledger.js +53 -4
- package/dist/plugins/send-user-file.d.ts +77 -0
- package/dist/plugins/send-user-file.js +92 -4
- package/dist/plugins/session-policy-store-sql.d.ts +15 -0
- package/dist/plugins/session-policy-store-sql.js +75 -3
- package/dist/plugins/session-store.d.ts +45 -0
- package/dist/plugins/session-store.js +51 -0
- package/dist/plugins/sql-driver.d.ts +77 -0
- package/dist/plugins/sql-driver.js +4 -0
- package/dist/plugins/sql-escape.d.ts +8 -0
- package/dist/plugins/sql-escape.js +8 -0
- package/dist/plugins/sql-row-helpers.d.ts +25 -0
- package/dist/plugins/sql-row-helpers.js +25 -0
- package/dist/plugins/store-backend.d.ts +103 -0
- package/dist/plugins/store-backend.js +120 -27
- package/dist/plugins/store-contracts.d.ts +62 -0
- package/dist/plugins/store-contracts.js +3 -0
- package/dist/plugins/task-attachment-store.d.ts +60 -0
- package/dist/plugins/task-attachment-store.js +49 -3
- package/dist/plugins/task-list-store-sql.d.ts +25 -0
- package/dist/plugins/task-list-store-sql.js +21 -0
- package/dist/plugins/tidb-approval-store.d.ts +6 -0
- package/dist/plugins/tidb-approval-store.js +6 -0
- package/dist/plugins/tidb-breaker-state.d.ts +5 -0
- package/dist/plugins/tidb-breaker-state.js +5 -0
- package/dist/plugins/tidb-checkpoint-store.d.ts +7 -0
- package/dist/plugins/tidb-checkpoint-store.js +7 -0
- package/dist/plugins/tidb-cost-quota.d.ts +6 -0
- package/dist/plugins/tidb-cost-quota.js +1 -0
- package/dist/plugins/tidb-file-snapshot-store.d.ts +6 -0
- package/dist/plugins/tidb-file-snapshot-store.js +6 -0
- package/dist/plugins/tidb-image-bake.d.ts +8 -0
- package/dist/plugins/tidb-image-bake.js +8 -0
- package/dist/plugins/tidb-image-index.d.ts +8 -0
- package/dist/plugins/tidb-image-index.js +8 -0
- package/dist/plugins/tidb-outcome-ledger.d.ts +10 -0
- package/dist/plugins/tidb-outcome-ledger.js +10 -0
- package/dist/plugins/tidb-pool.d.ts +56 -0
- package/dist/plugins/tidb-pool.js +181 -1
- package/dist/plugins/tidb-rate-limiter.d.ts +11 -0
- package/dist/plugins/tidb-rate-limiter.js +5 -2
- package/dist/plugins/tidb-resume-anchor-store.d.ts +5 -0
- package/dist/plugins/tidb-resume-anchor-store.js +5 -0
- package/dist/plugins/tidb-run-store.d.ts +7 -0
- package/dist/plugins/tidb-run-store.js +7 -0
- package/dist/plugins/tidb-session-policy-store.d.ts +5 -0
- package/dist/plugins/tidb-session-policy-store.js +5 -0
- package/dist/plugins/tidb-session-storage.d.ts +43 -0
- package/dist/plugins/tidb-session-storage.js +62 -3
- package/dist/plugins/tidb-session-store.d.ts +142 -0
- package/dist/plugins/tidb-session-store.js +264 -16
- package/dist/plugins/tidb-tool-result-store.d.ts +5 -0
- package/dist/plugins/tidb-tool-result-store.js +7 -0
- package/dist/plugins/tidb-workflow-journal-store.d.ts +7 -0
- package/dist/plugins/tidb-workflow-journal-store.js +7 -0
- package/dist/plugins/tidb-workflow-run-store.d.ts +8 -0
- package/dist/plugins/tidb-workflow-run-store.js +8 -0
- package/dist/plugins/tool-result-store-sql.d.ts +58 -0
- package/dist/plugins/tool-result-store-sql.js +38 -2
- package/dist/plugins/web-search.d.ts +35 -0
- package/dist/plugins/web-search.js +45 -9
- package/dist/plugins/workflow-journal-limits.d.ts +10 -0
- package/dist/plugins/workflow-journal-limits.js +10 -0
- package/dist/plugins/workflow-journal-store-sql.d.ts +78 -0
- package/dist/plugins/workflow-journal-store-sql.js +58 -2
- package/dist/plugins/workflow-run-store-sql.d.ts +84 -0
- package/dist/plugins/workflow-run-store-sql.js +84 -6
- package/dist/plugins/worktree-isolation.d.ts +45 -0
- package/dist/plugins/worktree-isolation.js +105 -6
- package/dist/plugins/write-behind-counter.d.ts +56 -1
- package/dist/plugins/write-behind-counter.js +53 -14
- package/dist/principal-jwt.d.ts +24 -0
- package/dist/principal-jwt.js +60 -5
- package/dist/project-memory.d.ts +22 -0
- package/dist/project-memory.js +144 -15
- package/dist/prompts-domain-validate.d.ts +28 -0
- package/dist/prompts-domain-validate.js +42 -1
- package/dist/question.d.ts +33 -0
- package/dist/question.js +69 -2
- package/dist/resource-suspend.d.ts +21 -0
- package/dist/resource-suspend.js +16 -0
- package/dist/router/route-orchestration.d.ts +78 -0
- package/dist/router/route-orchestration.js +77 -0
- package/dist/run-local.d.ts +32 -0
- package/dist/run-local.js +172 -8
- package/dist/runs.d.ts +189 -4
- package/dist/runs.js +465 -24
- package/dist/runtime-caps-resolver.d.ts +106 -0
- package/dist/runtime-caps-resolver.js +89 -4
- package/dist/runtime-governance.d.ts +84 -0
- package/dist/runtime-governance.js +127 -1
- package/dist/sandbox-pkg-source.d.ts +23 -0
- package/dist/sandbox-pkg-source.js +37 -0
- package/dist/sealed-key.d.ts +54 -0
- package/dist/sealed-key.js +68 -3
- package/dist/security.d.ts +108 -0
- package/dist/security.js +145 -6
- package/dist/sema-registry.d.ts +21 -0
- package/dist/sema-registry.js +21 -0
- package/dist/session-leaf-bus.d.ts +31 -0
- package/dist/session-leaf-bus.js +32 -0
- package/dist/session-sync-content.d.ts +46 -0
- package/dist/session-sync-content.js +47 -1
- package/dist/session-sync-kernel.d.ts +89 -0
- package/dist/session-sync-kernel.js +48 -3
- package/dist/session-sync.d.ts +125 -0
- package/dist/session-sync.js +181 -8
- package/dist/session-titler.d.ts +26 -0
- package/dist/session-titler.js +41 -7
- package/dist/session-watch.d.ts +53 -1
- package/dist/session-watch.js +73 -9
- package/dist/sighup-idle.d.ts +30 -0
- package/dist/sighup-idle.js +12 -1
- package/dist/spec-fields.d.ts +91 -3
- package/dist/spec-fields.js +116 -4
- package/dist/task-cwd.d.ts +50 -0
- package/dist/task-cwd.js +68 -1
- package/dist/task-mcp.d.ts +47 -0
- package/dist/task-mcp.js +29 -0
- package/dist/task-settings.d.ts +175 -0
- package/dist/task-settings.js +226 -11
- package/dist/task-workflow.d.ts +67 -0
- package/dist/task-workflow.js +75 -8
- package/dist/tool-approval.d.ts +91 -0
- package/dist/tool-approval.js +215 -0
- package/dist/trace/artifacts.d.ts +9 -0
- package/dist/trace/artifacts.js +30 -5
- package/dist/trace/core-keyset-guard.d.ts +18 -0
- package/dist/trace/ledger-sink.d.ts +43 -0
- package/dist/trace/ledger-sink.js +64 -5
- package/dist/trace/project.d.ts +133 -0
- package/dist/trace/project.js +189 -2
- package/dist/trace/redact.d.ts +19 -0
- package/dist/trace/redact.js +66 -9
- package/dist/usage-analytics.d.ts +26 -0
- package/dist/usage-analytics.js +26 -3
- package/package.json +1 -1
package/dist/leader/merge.d.ts
CHANGED
|
@@ -1,8 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2 MVP M3 — merge(b), the hard bone (design/50 §7, V2-MVP-PLAN.md, search [25]/[26], council round-1).
|
|
3
|
+
*
|
|
4
|
+
* The deterministic Coordinator integrates N completed workers' branches under FULL isolation:
|
|
5
|
+
* 1. pull each worker's patch OUT of its container (M2 `pullDiff` — no creds go in).
|
|
6
|
+
* 2. provision an EPHEMERAL no-net/no-creds integration sandbox (base pre-provisioned by the dep).
|
|
7
|
+
* 3. `git apply --3way` each patch in sequence (partial-apply → capture `.rej`, quarantine — council#3).
|
|
8
|
+
* 4. run the full test suite IN the sandbox (the gate; untrusted diff never touches the control plane — [26]§16).
|
|
9
|
+
* 5. green → the **Coordinator** (trusted control plane, holds creds — NOT the sandbox, council#1) takes the
|
|
10
|
+
* integrated patch and pushes with CAS `--force-with-lease=<ref>:<baseSha>` (push race → retry — council#2).
|
|
11
|
+
* 6. the sandbox is destroyed on ALL exit paths (council#15).
|
|
12
|
+
*
|
|
13
|
+
* This module is the ORCHESTRATION/control-flow (deps injected → mock-tested). The real git mechanics
|
|
14
|
+
* (apply/rebase/test/push in a real E2B container) are validated by the M5 real-E2B joint dogfood.
|
|
15
|
+
*/
|
|
1
16
|
import type { WorkerReport } from "./fanout.js";
|
|
2
17
|
import type { PullDiffResult } from "./diffout.js";
|
|
3
18
|
import { type RepairTerminal } from "@sema-agent/core";
|
|
4
19
|
import { type GraderHandle } from "./grader-env-factory.js";
|
|
20
|
+
/** A buffered command env for the integration sandbox (real E2B adapter adapts to this; mock in tests). */
|
|
5
21
|
export interface IntegrationEnv {
|
|
22
|
+
/** Run a command; never throws — failures surface as a non-zero exitCode. */
|
|
6
23
|
exec(command: string, opts?: {
|
|
7
24
|
cwd?: string;
|
|
8
25
|
}): Promise<{
|
|
@@ -12,8 +29,23 @@ export interface IntegrationEnv {
|
|
|
12
29
|
}>;
|
|
13
30
|
writeFile(path: string, content: string): Promise<void>;
|
|
14
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Bounded integration-repair (design: search 2026-06-14, Codex-reviewed). When the merged tree applies
|
|
34
|
+
* cleanly but the integration `testCmd` (the oracle gate) FAILS, the leader used to return terminal — a human
|
|
35
|
+
* read the build error and re-ran (the "11 runs / 6 fixes" loop was human-driven). This closes that gap: a
|
|
36
|
+
* bounded repair AGENT runs IN the already-merged sandbox (keeps the workers' good output + sees BOTH sides of
|
|
37
|
+
* a broken cross-worker contract, e.g. the ChannelShelf edge), edits the working tree (NOT commit/push), then
|
|
38
|
+
* mergeBranches re-runs `testCmd` to verify. Safe degradation: if repair exhausts rounds / errors, the result
|
|
39
|
+
* is today's `phase:"test"` failure plus repair metadata — never worse than terminal-fail. ⚠️ repair on a
|
|
40
|
+
* COMPILE-only `testCmd` can push green-but-incorrect code (oracle-quality, not repair, problem) — pair with a
|
|
41
|
+
* real hidden-oracle `measureCmd`/L3 when correctness (not just compile) must close.
|
|
42
|
+
*/
|
|
15
43
|
export interface IntegrationRepair {
|
|
44
|
+
/** Max repair attempts before giving up (each = one agent run + one testCmd re-run). */
|
|
16
45
|
maxRounds: number;
|
|
46
|
+
/** Run the repair agent against the live merged tree. Edits the working tree to make `testCmd` pass; MUST
|
|
47
|
+
* NOT commit or push (mergeBranches squashes worker+repair edits into one commit at the end). Returns its
|
|
48
|
+
* spend + a short note; mergeBranches re-runs `testCmd` to decide if the fix actually worked. */
|
|
17
49
|
run(ctx: {
|
|
18
50
|
round: number;
|
|
19
51
|
errorText: string;
|
|
@@ -24,8 +56,22 @@ export interface IntegrationRepair {
|
|
|
24
56
|
note?: string;
|
|
25
57
|
}>;
|
|
26
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Bounded merge-conflict resolution (search 2026-06-16). Symmetric with {@link IntegrationRepair}: when a worker's
|
|
61
|
+
* patch CONFLICTS on `git apply --3way` (overlapping edits a sibling already touched), run a bounded agent IN the
|
|
62
|
+
* integration sandbox to integrate THIS worker's patch — instead of the leader discarding ALL fan-out work and
|
|
63
|
+
* redoing the whole task as one fresh worker (collapseToSingle, leader.ts: "correctness over thrift; half-done
|
|
64
|
+
* adoption is future"). The agent merges the in-tree conflict markers + applies the `.rej` hunks; mergeBranches
|
|
65
|
+
* then VERIFIES no marker/.rej remains before continuing, and the downstream test gate still guards correctness.
|
|
66
|
+
* Wired by the caller (provisionIntegrationSandbox); when unset, an apply conflict stays terminal exactly as before
|
|
67
|
+
* (zero behaviour change). Reuses the IntegrationRepair sandbox + hands pattern → no new core/env seam (the seam
|
|
68
|
+
* design/68 §8 feared for "adopting N half-done workspaces" is unnecessary for the conflict case).
|
|
69
|
+
*/
|
|
27
70
|
export interface ConflictResolver {
|
|
71
|
+
/** Max resolution attempts before giving up (each = one agent run + one marker/.rej re-check). */
|
|
28
72
|
maxRounds: number;
|
|
73
|
+
/** Run the resolver agent against the live conflicted tree. Edits the working tree to integrate the worker's
|
|
74
|
+
* patch + removes the `.rej`/`.orig` files; MUST NOT commit / push / add (the merge squashes at the end). */
|
|
29
75
|
run(ctx: {
|
|
30
76
|
round: number;
|
|
31
77
|
workerId: string;
|
|
@@ -38,13 +84,22 @@ export interface ConflictResolver {
|
|
|
38
84
|
}>;
|
|
39
85
|
}
|
|
40
86
|
export interface MergeDeps {
|
|
87
|
+
/** Pull one worker's patch from ITS container (M2 pullDiff bound to the worker's env). */
|
|
41
88
|
pullWorkerDiff(report: WorkerReport): Promise<PullDiffResult>;
|
|
89
|
+
/** Provision a fresh ephemeral no-net/no-creds sandbox with the base repo at `baseSha` already in place.
|
|
90
|
+
* `repair` (optional) enables bounded integration-repair on a `testCmd` failure (see {@link IntegrationRepair});
|
|
91
|
+
* unset → a failing integration test is terminal (the pre-2026-06-14 behavior, unchanged). */
|
|
42
92
|
provisionIntegrationSandbox(): Promise<{
|
|
43
93
|
env: IntegrationEnv;
|
|
44
94
|
destroy: () => Promise<void>;
|
|
45
95
|
repair?: IntegrationRepair;
|
|
46
96
|
conflictResolver?: ConflictResolver;
|
|
47
97
|
}>;
|
|
98
|
+
/**
|
|
99
|
+
* The CREDENTIALED push, done by the Coordinator (control plane), NOT the sandbox (council#1). Takes the
|
|
100
|
+
* integrated patch (the sandbox's combined diff over base) + the base it forked from; applies to the
|
|
101
|
+
* Coordinator's own trusted clone and pushes `--force-with-lease=<ref>:<baseSha>`. `raced` = lease reject.
|
|
102
|
+
*/
|
|
48
103
|
push(integratedPatch: string, baseSha: string): Promise<{
|
|
49
104
|
ok: true;
|
|
50
105
|
ref: string;
|
|
@@ -53,16 +108,51 @@ export interface MergeDeps {
|
|
|
53
108
|
raced?: boolean;
|
|
54
109
|
error: string;
|
|
55
110
|
}>;
|
|
111
|
+
/** Repo dir inside the sandbox (default /repo). */
|
|
56
112
|
repoDir?: string;
|
|
113
|
+
/** Full test command run in the sandbox (the merge gate). */
|
|
57
114
|
testCmd: string;
|
|
115
|
+
/** Optional truecorrect MEASURE (design/59): a thorough hidden-oracle command run IN the integration sandbox
|
|
116
|
+
* (E2B) right after the gate passes — offloads the measure from the CPU-contended control plane (the
|
|
117
|
+
* frontier-multi oracle-timeout fix). NOT a gate (does not affect `ok`); the result lands in MergeResult.measure
|
|
118
|
+
* for the caller to read as the objective truly-correct signal. Same place + env as the gate, just a thornier test. */
|
|
58
119
|
measureCmd?: string;
|
|
120
|
+
/**
|
|
121
|
+
* LEADER-REPAIRLOOP-INTEGRATION §10.3 — push-hold-on-SIGNAL master flag (the wire sets it from
|
|
122
|
+
* `LEADER_MEASURE_GATES`, default OFF). When OFF (the default) the push path is BYTE-IDENTICAL to today: the
|
|
123
|
+
* `measureCmd` stays observe-only and a compile-only-green merge auto-pushes. When ON, a configured strong
|
|
124
|
+
* oracle that is NOT green HOLDS the push (→ `candidate_only`, no push): the hold keys on the SIGNAL, not a
|
|
125
|
+
* caller field — `strongOracleSeeded` (an oracleFiles-backed hidden held-out oracle is in the sandbox) OR
|
|
126
|
+
* (`measureCmd` present AND its measure did not pass). Auto-push only when every configured strong signal is
|
|
127
|
+
* GREEN; absent BOTH → legacy compile-only auto-push (§10.3). */
|
|
59
128
|
measureGates?: boolean;
|
|
129
|
+
/**
|
|
130
|
+
* §10.3 — set by the wire when an oracleFiles-backed hidden held-out oracle was seeded into the integration
|
|
131
|
+
* sandbox (`wire.ts` `injectOracles`). Under `measureGates`, a seeded-but-only-`testCmd`-green merge is a
|
|
132
|
+
* strong-oracle-UNCONFIRMED result → the push is HELD (`candidate_only`). The merge module cannot see the
|
|
133
|
+
* oracleFiles itself (a wire concern), so the wire reports their presence here. Default unset/false → today's
|
|
134
|
+
* behavior. */
|
|
60
135
|
strongOracleSeeded?: boolean;
|
|
136
|
+
/**
|
|
137
|
+
* §10.4 measure-drives-repair — the out-of-process measure grader (R2) + the changed-files allowlist. When set
|
|
138
|
+
* (and `measureGates` ON) a `measure`-FAIL re-seeds this SINGLE minted grader (`git reset --hard <integBase>` +
|
|
139
|
+
* `git clean -fdx`, re-apply the integrated patch) and re-runs the trusted `measureCmd` THERE — never in the
|
|
140
|
+
* repair-edited (possibly poisoned) merge sandbox (§10.4(i)). Before re-measuring, the integrated patch's
|
|
141
|
+
* changed-file list is mechanically checked against `allowlist` (the workers' planned `files` ∪ trusted spec
|
|
142
|
+
* dirs): an out-of-allowlist / build-config touch downgrades the candidate to `needs_human_oracle` (§10.4(ii)).
|
|
143
|
+
* The grader handle is bound by the wire (a re-seedable integration-equivalent sandbox). Unset → no measure
|
|
144
|
+
* grader; the measure stays observe-only (today's path). */
|
|
61
145
|
measureDrivesRepair?: {
|
|
146
|
+
/** The out-of-process grader handle (re-seedable; mirrors the integration sandbox, oracle-seeded). */
|
|
62
147
|
grader: GraderHandle;
|
|
148
|
+
/** The trusted, spec-derived measure steps run in the grader (NEVER worker-authored). */
|
|
63
149
|
measureSteps: import("@sema-agent/core").ExecStep[];
|
|
150
|
+
/** §10.4(ii): the changed-files allowlist = the workers' planned `files` ∪ trusted spec dirs. */
|
|
64
151
|
allowlist: string[];
|
|
152
|
+
/** Bounded measure-repair rounds (re-seed + re-measure). Each round optionally drives the existing
|
|
153
|
+
* integration `repair` agent first; 0/unset → grade once, no repair. */
|
|
65
154
|
maxRounds?: number;
|
|
155
|
+
/** Parse the integrated patch into a changed-file list (the wire binds `git apply --numstat` / name-only). */
|
|
66
156
|
changedFiles: (integratedPatch: string) => Promise<string[]>;
|
|
67
157
|
};
|
|
68
158
|
logger?: {
|
|
@@ -70,6 +160,9 @@ export interface MergeDeps {
|
|
|
70
160
|
info?: (m: string, x?: Record<string, unknown>) => void;
|
|
71
161
|
};
|
|
72
162
|
}
|
|
163
|
+
/** Integration-repair outcome (when `MergeDeps.provisionIntegrationSandbox` supplied a `repair`): how many
|
|
164
|
+
* rounds ran, whether the post-repair `testCmd` finally passed, and the repair spend. `outsidePlannedFiles`
|
|
165
|
+
* is honest accounting — repaired files are NOT attributed to the original disjoint workers. */
|
|
73
166
|
export interface RepairMeta {
|
|
74
167
|
rounds: number;
|
|
75
168
|
fixed: boolean;
|
|
@@ -105,5 +198,6 @@ export type MergeResult = {
|
|
|
105
198
|
reason?: string;
|
|
106
199
|
};
|
|
107
200
|
};
|
|
201
|
+
/** Integrate completed workers' branches. Never throws — failures are typed MergeResult. */
|
|
108
202
|
export declare function mergeBranches(reports: WorkerReport[], baseSha: string, deps: MergeDeps): Promise<MergeResult>;
|
|
109
203
|
//# sourceMappingURL=merge.d.ts.map
|
package/dist/leader/merge.js
CHANGED
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
import { delimitUntrusted } from "@sema-agent/core";
|
|
2
2
|
import { applyAndGrade, changedFilesOutOfAllowlist } from "./grader-env-factory.js";
|
|
3
|
-
const Q = (s) => `'${s.replace(/'/g, "'\\''")}'`;
|
|
3
|
+
const Q = (s) => `'${s.replace(/'/g, "'\\''")}'`; // single-quote for shell safety
|
|
4
|
+
/** Integrate completed workers' branches. Never throws — failures are typed MergeResult. */
|
|
4
5
|
export async function mergeBranches(reports, baseSha, deps) {
|
|
5
6
|
const repo = deps.repoDir ?? "/repo";
|
|
6
7
|
const mergeable = reports.filter((r) => r.status === "completed");
|
|
7
8
|
if (mergeable.length === 0) {
|
|
8
9
|
return { ok: false, phase: "no-workers", reason: "no completed workers to merge", quarantine: reports.map((r) => r.workerId) };
|
|
9
10
|
}
|
|
11
|
+
// The whole pipeline is wrapped so this function honours its "never throws" contract AND never leaks the
|
|
12
|
+
// integration sandbox: provisioning (which connects+seeds a billable VM) and pullWorkerDiff used to run
|
|
13
|
+
// OUTSIDE the try, so a throw there (e.g. seed failure) escaped as an exception and — worse — left a
|
|
14
|
+
// connected sandbox with no `destroy` ever called. `destroy` is captured the instant provisioning resolves,
|
|
15
|
+
// and the finally tears it down on every path (success / typed failure / thrown error).
|
|
10
16
|
let destroy;
|
|
17
|
+
// Track the current stage so a THROWN error is attributed to the right phase (not always "provision").
|
|
11
18
|
let phase = "diff-out";
|
|
12
19
|
try {
|
|
20
|
+
// 1. pull each worker's diff OUT of its container (M2). A failed diff quarantines that worker, aborts merge.
|
|
13
21
|
const patches = [];
|
|
14
22
|
for (const r of mergeable) {
|
|
15
23
|
const d = await deps.pullWorkerDiff(r);
|
|
@@ -18,24 +26,35 @@ export async function mergeBranches(reports, baseSha, deps) {
|
|
|
18
26
|
}
|
|
19
27
|
patches.push({ workerId: r.workerId, patch: d.patch });
|
|
20
28
|
}
|
|
29
|
+
// 2. ephemeral no-creds integration sandbox (destroyed in finally — council#15).
|
|
21
30
|
phase = "provision";
|
|
22
31
|
const integ = await deps.provisionIntegrationSandbox();
|
|
23
32
|
destroy = integ.destroy;
|
|
24
33
|
const { env } = integ;
|
|
34
|
+
// The sandbox's base commit ≠ the durable remote's `baseSha` (different repos). Capture the sandbox's
|
|
35
|
+
// pre-apply HEAD so `format-patch <integBase>..HEAD` captures the integration commit even though the
|
|
36
|
+
// commit advances the sandbox's branch (dogfood-surfaced: `main..HEAD` was empty). `baseSha` is used
|
|
37
|
+
// ONLY for the durable push CAS below.
|
|
25
38
|
const integBase = (await env.exec("git rev-parse HEAD", { cwd: repo })).stdout.trim();
|
|
39
|
+
// 3. apply each patch --3way (council#3: keeps conflict context; capture .rej on failure, quarantine).
|
|
26
40
|
phase = "apply";
|
|
27
41
|
const conflictsResolved = [];
|
|
28
|
-
let conflictResolverCostUsd = 0;
|
|
42
|
+
let conflictResolverCostUsd = 0; // 🔴 capture the bounded conflict-resolver agent's spend (was dropped → S1 TEAM-cost undercount)
|
|
29
43
|
for (const p of patches) {
|
|
30
44
|
const patchPath = `/tmp/${p.workerId}.patch`;
|
|
31
45
|
await env.writeFile(patchPath, p.patch);
|
|
32
46
|
const ap = await env.exec(`git apply --3way --whitespace=nowarn ${Q(patchPath)}`, { cwd: repo });
|
|
33
47
|
if (ap.exitCode !== 0) {
|
|
34
48
|
const rej = await env.exec(`find . -name '*.rej' -print -exec cat {} +`, { cwd: repo }).catch(() => ({ stdout: "", stderr: "", exitCode: -1 }));
|
|
49
|
+
// Bounded conflict-resolution (search 2026-06-16): a --3way conflict left markers/.rej in the tree. Instead
|
|
50
|
+
// of discarding ALL fan-out work + redoing single (leader collapseToSingle), run a bounded agent IN this
|
|
51
|
+
// sandbox to integrate THIS worker's patch (siblings already applied clean), gated by a marker/.rej re-check
|
|
52
|
+
// and the downstream test gate. Symmetric with integration-repair; OFF unless a conflictResolver is wired;
|
|
53
|
+
// degrades to the original apply-failure (collapse) on any leftover/agent-error — never worse than baseline.
|
|
35
54
|
const cr = integ.conflictResolver && integ.conflictResolver.maxRounds > 0
|
|
36
55
|
? await resolveApplyConflict(env, repo, p, rej.stdout, ap.stderr, integ.conflictResolver, deps)
|
|
37
56
|
: { resolved: false, costUsd: 0 };
|
|
38
|
-
conflictResolverCostUsd += cr.costUsd;
|
|
57
|
+
conflictResolverCostUsd += cr.costUsd; // count the spend even when resolution failed (tokens were spent)
|
|
39
58
|
if (!cr.resolved) {
|
|
40
59
|
deps.logger?.warn?.("merge_apply_failed", { workerId: p.workerId, exitCode: ap.exitCode });
|
|
41
60
|
return { ok: false, phase: "apply", reason: `git apply --3way failed for ${p.workerId} (conflict)`, quarantine: [p.workerId], rej: rej.stdout, details: ap.stderr.slice(0, 800), ...(conflictResolverCostUsd > 0 ? { conflictResolverCostUsd } : {}) };
|
|
@@ -43,8 +62,15 @@ export async function mergeBranches(reports, baseSha, deps) {
|
|
|
43
62
|
conflictsResolved.push(p.workerId);
|
|
44
63
|
}
|
|
45
64
|
}
|
|
65
|
+
// 4. full test suite IN the sandbox = the merge gate (untrusted diff stays sandboxed — [26]§16).
|
|
66
|
+
// testCmd is a caller-provided COMMAND LINE (e.g. "cd x && pytest"), run verbatim by design — NOT a single
|
|
67
|
+
// arg, so it is intentionally not Q()-quoted (council round-2 noted the asymmetry). Blast radius = the
|
|
68
|
+
// ephemeral no-creds sandbox; the caller owns the command (trusted control-plane config).
|
|
46
69
|
phase = "test";
|
|
47
70
|
let test = await env.exec(deps.testCmd, { cwd: repo });
|
|
71
|
+
// Bounded integration-repair (search 2026-06-14): a clean-merge-but-failing-build used to be terminal.
|
|
72
|
+
// If the sandbox supplied a `repair`, run the repair agent IN this merged tree (it edits the working tree;
|
|
73
|
+
// the squash commit below captures worker patches + repair together), then re-run testCmd. Up to maxRounds.
|
|
48
74
|
let repairMeta;
|
|
49
75
|
if (test.exitCode !== 0 && integ.repair && integ.repair.maxRounds > 0) {
|
|
50
76
|
let cost = 0;
|
|
@@ -58,7 +84,7 @@ export async function mergeBranches(reports, baseSha, deps) {
|
|
|
58
84
|
}
|
|
59
85
|
catch (e) {
|
|
60
86
|
deps.logger?.warn?.("integration_repair_error", { round: round + 1, err: e instanceof Error ? e.message : String(e) });
|
|
61
|
-
break;
|
|
87
|
+
break; // a thrown repair agent → stop repairing; fall through to the (still-failing) terminal return
|
|
62
88
|
}
|
|
63
89
|
test = await env.exec(deps.testCmd, { cwd: repo });
|
|
64
90
|
}
|
|
@@ -68,14 +94,29 @@ export async function mergeBranches(reports, baseSha, deps) {
|
|
|
68
94
|
if (test.exitCode !== 0) {
|
|
69
95
|
return { ok: false, phase: "test", reason: `integration test failed (exit ${test.exitCode})`, details: (test.stdout + "\n" + test.stderr).slice(-2000), ...(repairMeta ? { repair: repairMeta } : {}), ...(conflictResolverCostUsd > 0 ? { conflictResolverCostUsd } : {}) };
|
|
70
96
|
}
|
|
97
|
+
// 4b. truecorrect MEASURE (design/59) — run the thorough hidden oracle IN this sandbox (E2B), where the merged
|
|
98
|
+
// code already lives, NOT on the CPU-contended control plane (fixes the frontier-multi oracle spawn-timeout
|
|
99
|
+
// that thinned n). Pure observation: never gates `ok`, just reports into MergeResult.measure.
|
|
71
100
|
let measure;
|
|
72
101
|
if (deps.measureCmd) {
|
|
73
102
|
const mr = await env.exec(deps.measureCmd, { cwd: repo });
|
|
74
103
|
measure = mr.exitCode === 0 ? { pass: true } : { pass: false, reason: (mr.stdout + " " + mr.stderr).slice(-200) };
|
|
75
104
|
}
|
|
105
|
+
// 5. produce the integrated patch over base, hand to the Coordinator's credentialed push (council#1).
|
|
106
|
+
// Check the commit's exit code: if `git add -A`/commit fails, HEAD doesn't advance and `format-patch`
|
|
107
|
+
// still exits 0 with EMPTY output — pushing an empty patch would no-op or corrupt the merge silently.
|
|
76
108
|
phase = "push";
|
|
109
|
+
// If integration-repair ran, a repair agent may have created commits despite the instruction not to. Flatten
|
|
110
|
+
// any such commits back to the working tree (Codex review #2) so the single `commit` below squashes worker
|
|
111
|
+
// patches + repair edits into ONE integration commit — otherwise format-patch would emit the repair commit
|
|
112
|
+
// PLUS an empty leader commit, and the Coordinator's `git am` can choke on the empty patch. No-op when no
|
|
113
|
+
// repair commits exist (HEAD already == integBase).
|
|
77
114
|
if (repairMeta)
|
|
78
115
|
await env.exec(`git reset --soft ${Q(integBase)}`, { cwd: repo });
|
|
116
|
+
// Cost legs that MUST ride EVERY push-phase failure return (wq64gmm5e HIGH: TEAM C1 undercount). A
|
|
117
|
+
// conflictResolver and/or integration-repair may already have spent real tokens BEFORE any push-phase
|
|
118
|
+
// failure; dropping these on the commit/format-patch/empty/race/push-fail returns made captureTeamC1
|
|
119
|
+
// undercount the arm. Mirror the success/hold/test-fail returns exactly.
|
|
79
120
|
const costTail = { ...(repairMeta ? { repair: repairMeta } : {}), ...(conflictResolverCostUsd > 0 ? { conflictResolverCostUsd } : {}) };
|
|
80
121
|
const commit = await env.exec(`git add -A && git -c user.email=leader@local -c user.name=leader commit -m ${Q(`merge: integrate ${mergeable.map((r) => r.workerId).join("+")}`)} --allow-empty`, { cwd: repo });
|
|
81
122
|
if (commit.exitCode !== 0) {
|
|
@@ -88,9 +129,17 @@ export async function mergeBranches(reports, baseSha, deps) {
|
|
|
88
129
|
if (!integrated.stdout.trim()) {
|
|
89
130
|
return { ok: false, phase: "push", reason: "integrated patch is empty (nothing applied over base) — refusing to push", quarantine: mergeable.map((r) => r.workerId), ...costTail };
|
|
90
131
|
}
|
|
132
|
+
// ── §10.6 THE PUSH CHOKEPOINT + §10.3 push-hold-on-signal + §10.4 measure-drives-repair ──
|
|
133
|
+
// A single gate covering EVERY push path by construction (collapse `leader.ts:451` + N=1 `leader.ts:508`):
|
|
134
|
+
// the credentialed push fires ONLY when (a) no mergeable report carries a non-auto-accepting
|
|
135
|
+
// `repairTerminal` (§10.6 — the SAFE-tier `candidate_only` that the status collapse to "completed" would
|
|
136
|
+
// otherwise hide), AND (b) under `LEADER_MEASURE_GATES`, every configured strong oracle signal is GREEN
|
|
137
|
+
// (§10.3). When any of these HOLDS, the push is withheld and the held, DELIMITED candidate is surfaced for
|
|
138
|
+
// human acceptance (a candidate is never a silent push and never a silent drop — §5 invariant). With
|
|
139
|
+
// `measureGates` OFF and no report carrying a `repairTerminal`, this whole block is inert → byte-identical.
|
|
91
140
|
const evald = await evaluatePushHold(env, repo, mergeable, measure, integrated.stdout, deps);
|
|
92
141
|
if (evald.measure !== undefined)
|
|
93
|
-
measure = evald.measure;
|
|
142
|
+
measure = evald.measure; // §10.4: the out-of-process re-measure supersedes the observe-only one
|
|
94
143
|
if (evald.hold) {
|
|
95
144
|
const h = evald.hold;
|
|
96
145
|
deps.logger?.warn?.("merge_push_held", { terminal: h.terminal, reason: h.reason.slice(0, 200) });
|
|
@@ -113,51 +162,103 @@ export async function mergeBranches(reports, baseSha, deps) {
|
|
|
113
162
|
return { ok: true, ref: push.ref, merged: mergeable.map((r) => r.workerId), ...(measure ? { measure } : {}), ...(repairMeta ? { repair: repairMeta } : {}), ...(conflictsResolved.length > 0 ? { conflictsResolved } : {}), ...(conflictResolverCostUsd > 0 ? { conflictResolverCostUsd } : {}) };
|
|
114
163
|
}
|
|
115
164
|
catch (e) {
|
|
165
|
+
// Honour the "never throws" contract: a thrown pullWorkerDiff/provision/exec becomes a typed failure,
|
|
166
|
+
// attributed to the stage it threw in (not a blanket "provision").
|
|
116
167
|
return { ok: false, phase, reason: `merge aborted in ${phase}: ${e instanceof Error ? e.message : String(e)}`, quarantine: mergeable.map((r) => r.workerId) };
|
|
117
168
|
}
|
|
118
169
|
finally {
|
|
170
|
+
// council#15: destroy the sandbox on EVERY exit path. Guarded because we may bail (diff-out) or throw
|
|
171
|
+
// BEFORE provisioning — `destroy` is only set once provisionIntegrationSandbox has resolved.
|
|
119
172
|
if (destroy)
|
|
120
173
|
await destroy().catch(() => { });
|
|
121
174
|
}
|
|
122
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* §10.6 + §10.3 + §10.4 — the push chokepoint decision (pure-ish: reads the reports + the optional measure grader;
|
|
178
|
+
* never throws — a grader error falls back to a HOLD, never a silent push). Returns `{ hold }` to WITHHOLD the
|
|
179
|
+
* credentialed push, surfacing the held candidate; `{}` to allow it. Three independent hold triggers:
|
|
180
|
+
*
|
|
181
|
+
* §10.6 — any mergeable report carries a `repairTerminal ∉ {undefined, "fixed"}`. A SAFE-tier `candidate_only`
|
|
182
|
+
* maps to core verdict "PASS" → the report status collapses to "completed", so WITHOUT this gate the
|
|
183
|
+
* merge would push `testCmd`-green-but-strong-oracle-UNCONFIRMED work. This fires regardless of
|
|
184
|
+
* `measureGates` (the single-agent repair loop is itself OFF-by-default behind LEADER_REPAIR_LOOP — once
|
|
185
|
+
* a report carries a repairTerminal, the operator opted in; the candidate MUST be held). Covers the
|
|
186
|
+
* collapse-to-single and N=1 leader paths by construction.
|
|
187
|
+
*
|
|
188
|
+
* §10.3 — under `measureGates` ONLY: a configured STRONG oracle that is not GREEN holds the push. The hold keys
|
|
189
|
+
* on the SIGNAL not a caller field: (`strongOracleSeeded`) OR (`measureCmd` present AND the measure did
|
|
190
|
+
* not pass). Absent BOTH strong signals → no §10.3 hold (legacy compile-only auto-push). With
|
|
191
|
+
* `measureGates` OFF this trigger is inert (today's observe-only measure).
|
|
192
|
+
*
|
|
193
|
+
* §10.4 — under `measureGates` AND `measureDrivesRepair`: re-seed the SINGLE out-of-process grader, mechanically
|
|
194
|
+
* check the integrated patch's changed files against the allowlist (out-of-allowlist / build-config →
|
|
195
|
+
* downgrade to `needs_human_oracle`), then re-measure THERE (never the repair-poisoned merge sandbox).
|
|
196
|
+
* A still-failing re-measure holds (`candidate_only`); a passing re-measure clears the §10.3 measure
|
|
197
|
+
* signal (so the §10.3 trigger no longer holds on it).
|
|
198
|
+
*/
|
|
123
199
|
async function evaluatePushHold(env, repo, mergeable, observeMeasure, integratedPatch, deps) {
|
|
200
|
+
// §10.7 — the integrated patch is worker-authored code reaching a human-read HTTP body / durable-approval row.
|
|
124
201
|
const heldCandidate = () => delimitUntrusted("CANDIDATE PATCH (untrusted worker code)", integratedPatch);
|
|
202
|
+
// §10.6 — a non-auto-accepting single-agent repair terminal rode in on a report (status collapsed to
|
|
203
|
+
// "completed"). The strongest non-fixed terminal wins the label (it's the WHY surfaced to the human).
|
|
125
204
|
const carried = mergeable.map((r) => r.repairTerminal).filter((t) => t !== undefined && t !== "fixed");
|
|
126
205
|
if (carried.length > 0) {
|
|
127
|
-
const terminal = carried[0];
|
|
206
|
+
const terminal = carried[0]; // every mergeable solo report shares one repairTerminal (N=1 / collapse-solo)
|
|
128
207
|
return { hold: { reason: `single-agent repair terminal '${terminal}' — strong oracle unconfirmed, push held for human acceptance (§10.6)`, terminal, candidatePatch: heldCandidate() } };
|
|
129
208
|
}
|
|
209
|
+
// §10.3/§10.4 are OFF unless the master measure-gates flag is on → byte-identical default path.
|
|
130
210
|
if (!deps.measureGates)
|
|
131
211
|
return {};
|
|
132
212
|
let measure = observeMeasure;
|
|
213
|
+
// §10.4 measure-drives-repair: re-measure the integrated patch in a FRESH-seeded out-of-process grader, with the
|
|
214
|
+
// mechanical changed-files allowlist guard. Runs only when configured AND there is a measure to re-confirm.
|
|
133
215
|
if (deps.measureDrivesRepair && deps.measureCmd) {
|
|
134
216
|
const mdr = deps.measureDrivesRepair;
|
|
135
217
|
try {
|
|
218
|
+
// §10.4(ii) — mechanical allowlist FIRST (a fresh sandbox is necessary-not-sufficient: poison can ride
|
|
219
|
+
// INSIDE the integrated patch). An out-of-allowlist / build-config touch → downgrade to needs_human_oracle.
|
|
136
220
|
const changed = await mdr.changedFiles(integratedPatch);
|
|
137
221
|
const offending = changedFilesOutOfAllowlist(changed, mdr.allowlist);
|
|
138
222
|
if (offending.length > 0) {
|
|
139
223
|
deps.logger?.warn?.("merge_measure_allowlist_denied", { offending: offending.slice(0, 20) });
|
|
140
224
|
return { hold: { reason: `integrated patch touches out-of-allowlist / build-config paths (${offending.slice(0, 5).join(", ")}) — needs_human_oracle (§10.4(ii))`, terminal: "needs_human_oracle", candidatePatch: heldCandidate() }, measure };
|
|
141
225
|
}
|
|
226
|
+
// Re-seed the SINGLE grader + re-measure THERE (never the repair-edited merge sandbox — §10.4(i)). The
|
|
227
|
+
// existing in-sandbox `repair` agent already ran in `mergeBranches`; the grader re-measure is the
|
|
228
|
+
// authoritative correctness signal. (Bounded measure-repair rounds beyond the existing integration-repair
|
|
229
|
+
// are a future enable; v1 re-measures once.)
|
|
142
230
|
const grade = await applyAndGrade(mdr.grader, integratedPatch, mdr.measureSteps);
|
|
143
231
|
measure = grade.passed ? { pass: true } : { pass: false, reason: (grade.trace ?? "measure failed in out-of-process grader").slice(-200) };
|
|
144
232
|
deps.logger?.info?.("merge_measure_grader", { passed: grade.passed, applied: grade.applied });
|
|
145
233
|
}
|
|
146
234
|
catch (e) {
|
|
235
|
+
// Fail-CLOSED (never a silent push): a grader transport/exec error holds the push as needs_human_oracle.
|
|
147
236
|
deps.logger?.warn?.("merge_measure_grader_error", { err: e instanceof Error ? e.message : String(e) });
|
|
237
|
+
// §10.7 — surfaced reason is a trusted constant; the untrusted error detail is in the warn log above.
|
|
148
238
|
return { hold: { reason: `measure grader errored — needs_human_oracle (§10.4)`, terminal: "needs_human_oracle", candidatePatch: heldCandidate() }, measure };
|
|
149
239
|
}
|
|
150
240
|
}
|
|
241
|
+
// §10.3 — push-hold on the SIGNAL. Auto-push only when every configured strong signal is GREEN.
|
|
151
242
|
const measureFailing = deps.measureCmd !== undefined && measure?.pass !== true;
|
|
152
|
-
const strongOracleUnconfirmed = deps.strongOracleSeeded === true;
|
|
243
|
+
const strongOracleUnconfirmed = deps.strongOracleSeeded === true; // a hidden held-out oracle was seeded but only testCmd cleared
|
|
153
244
|
if (measureFailing || strongOracleUnconfirmed) {
|
|
245
|
+
// §10.7 — the surfaced `reason` reaches a human-read HTTP body / durable-approval row, so it is built from
|
|
246
|
+
// TRUSTED constants ONLY; the untrusted measure output tail (`measure.reason`) stays in the logs, NEVER
|
|
247
|
+
// interpolated into a human-/LLM-reachable string.
|
|
154
248
|
const why = measureFailing ? "measure did not pass" : "hidden held-out oracle seeded but only the compile gate cleared";
|
|
155
249
|
deps.logger?.warn?.("merge_push_held_signal", { why, measureReason: (measure?.reason ?? "").slice(0, 200) });
|
|
156
250
|
return { hold: { reason: `strong oracle unconfirmed: ${why} — candidate_only, push held (§10.3)`, terminal: "candidate_only", candidatePatch: heldCandidate() }, measure };
|
|
157
251
|
}
|
|
158
252
|
return { measure };
|
|
159
253
|
}
|
|
254
|
+
/** Bounded resolution of a `git apply --3way` conflict (search 2026-06-16). The failed apply left conflict markers
|
|
255
|
+
* in-tree and/or `.rej` files. Run the resolver agent up to maxRounds, then VERIFY the tree is conflict-free (no
|
|
256
|
+
* `<<<<<<<`/`>>>>>>>` markers, no `*.rej`). Returns true iff clean → the patch is treated as applied and the merge
|
|
257
|
+
* continues, KEEPING every other worker's clean work. Any agent error / leftover marker → false → the caller falls
|
|
258
|
+
* back to today's apply-failure (collapse-to-single). Never worse than the baseline. */
|
|
160
259
|
async function resolveApplyConflict(env, repo, p, rejText, applyStderr, resolver, deps) {
|
|
260
|
+
// 🔴 Accumulate + RETURN the resolver's spend (it used to return only a boolean → the cost was dropped, the S1
|
|
261
|
+
// TEAM-cost-undercount bug). Counts every round, including the round that throws (the tokens were spent).
|
|
161
262
|
let costUsd = 0;
|
|
162
263
|
const conflictText = `git apply --3way could not cleanly apply worker '${p.workerId}'.\n--- apply stderr ---\n${applyStderr.slice(0, 1500)}\n--- reject hunks (*.rej) ---\n${rejText.slice(0, 3000)}`;
|
|
163
264
|
for (let round = 0; round < resolver.maxRounds; round++) {
|
|
@@ -168,8 +269,10 @@ async function resolveApplyConflict(env, repo, p, rejText, applyStderr, resolver
|
|
|
168
269
|
}
|
|
169
270
|
catch (e) {
|
|
170
271
|
deps.logger?.warn?.("merge_conflict_repair_error", { workerId: p.workerId, err: e instanceof Error ? e.message : String(e) });
|
|
171
|
-
return { resolved: false, costUsd };
|
|
272
|
+
return { resolved: false, costUsd }; // a thrown resolver → give up, fall back to collapse
|
|
172
273
|
}
|
|
274
|
+
// Resolved iff NO conflict markers and NO .rej files remain under the repo. `grep -rIlE` LEADS the command so a
|
|
275
|
+
// mock/impl can distinguish this verify from the `.rej` CAPTURE in mergeBranches; non-empty stdout = still dirty.
|
|
173
276
|
const check = await env
|
|
174
277
|
.exec(`grep -rIlE '^(<<<<<<<|>>>>>>>)' . 2>/dev/null; find . -name '*.rej' 2>/dev/null | head -20`, { cwd: repo })
|
|
175
278
|
.catch(() => ({ exitCode: -1, stdout: "DIRTY", stderr: "" }));
|
|
@@ -179,6 +282,6 @@ async function resolveApplyConflict(env, repo, p, rejText, applyStderr, resolver
|
|
|
179
282
|
return { resolved: true, costUsd };
|
|
180
283
|
}
|
|
181
284
|
}
|
|
182
|
-
return { resolved: false, costUsd };
|
|
285
|
+
return { resolved: false, costUsd }; // still conflicted after maxRounds
|
|
183
286
|
}
|
|
184
287
|
//# sourceMappingURL=merge.js.map
|
package/dist/leader/planner.d.ts
CHANGED
|
@@ -1,23 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2 MVP — LLM Planner (design/50 §3: leader = LLM-planner that splits a DAG + deterministic Coordinator).
|
|
3
|
+
* Decomposes an objective into N **disjoint-file** sub-tasks via one model completion, then VALIDATES the plan
|
|
4
|
+
* deterministically (the trust boundary: an LLM proposes, the Coordinator enforces — service[19] leaderless-
|
|
5
|
+
* leader). Disjoint file-ownership is the invariant that lets workers run in parallel + merge without conflict.
|
|
6
|
+
*
|
|
7
|
+
* The model call is injected (`runPlan: prompt → text`) so this is pure + mock-tested; wire.ts binds it to a
|
|
8
|
+
* real Runner completion. Invalid/overlapping plans throw a clear error → the leader returns plan-failed
|
|
9
|
+
* (never a half-valid fan-out).
|
|
10
|
+
*/
|
|
1
11
|
import type { SubtaskSpec } from "./leader.js";
|
|
2
12
|
export interface PlanOptions {
|
|
13
|
+
/** Max sub-tasks the planner may emit (guards a runaway fan-out). Default 6. */
|
|
3
14
|
maxSubtasks?: number;
|
|
15
|
+
/** Extra repo/context guidance appended to the prompt (e.g. file layout, conventions). */
|
|
4
16
|
context?: string;
|
|
17
|
+
/** Deployment knob (design/68 §11 A6, correctness-first): `false` = never fan out — `routePlan` skips the
|
|
18
|
+
* router model call entirely and returns the deterministic single route (zero routing cost, zero risk of a
|
|
19
|
+
* false fan-out). Default (undefined/true) = the router decides. */
|
|
5
20
|
fanoutEnabled?: boolean;
|
|
21
|
+
/** Economic guard threshold (design/68 §7, search[83] ruling): an objective SHORTER than this never reaches
|
|
22
|
+
* the router — deterministic single (a small task can't be economically decomposable; false-single is the
|
|
23
|
+
* cheap error side). Default 64 — deliberately below the route corpus's smallest fan-out case. */
|
|
6
24
|
minFanoutChars?: number;
|
|
7
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Deterministic economic 防灾 guard (design/68 §7 / §11 D2, search [83] ruling (b)): the asymmetry is that a
|
|
28
|
+
* false fan-out is the -98% disaster while a false single merely runs slower — so every case decidable WITHOUT
|
|
29
|
+
* the model resolves to single, for free. Pure function (unit-testable); the semantic judgments (sequential/
|
|
30
|
+
* coupled/uncertain) stay with the router LLM, and "估不准回 single" is the collapse fallback (B3).
|
|
31
|
+
*/
|
|
8
32
|
export declare function economicGuard(objective: string, opts?: PlanOptions): {
|
|
9
33
|
single: true;
|
|
10
34
|
reason: string;
|
|
11
35
|
} | {
|
|
12
36
|
single: false;
|
|
13
37
|
};
|
|
38
|
+
/** The deterministic SINGLE route: one sub-task owning the whole objective (the same shape the router emits
|
|
39
|
+
* for its single decision — N=1 runs through the identical leader pipeline). */
|
|
14
40
|
export declare function singleRoute(objective: string): SubtaskSpec[];
|
|
41
|
+
/** The planning prompt: asks for a strict JSON array of disjoint-file sub-tasks. */
|
|
15
42
|
export declare function planPrompt(objective: string, opts?: PlanOptions): string;
|
|
43
|
+
/** Parse + VALIDATE a planner completion into SubtaskSpec[]. Throws on any violation (disjointness etc.). */
|
|
16
44
|
export declare function parsePlan(raw: string, opts?: PlanOptions): SubtaskSpec[];
|
|
45
|
+
/**
|
|
46
|
+
* Deterministically validate an array of sub-tasks (the trust boundary — see file header). Used both for the
|
|
47
|
+
* LLM planner output AND for caller-supplied `body.subtasks`, which would otherwise skip every invariant: the
|
|
48
|
+
* `branch` is interpolated into a worker-container shell command (`git checkout -b <branch>`), so an
|
|
49
|
+
* unvalidated branch (`x; curl …|sh`, `` `reboot` ``, `--upload-pack=…`) would inject; and the disjoint-file /
|
|
50
|
+
* unique-id invariants are what the Coordinator's parallel-merge depends on. Throws on any violation.
|
|
51
|
+
*/
|
|
17
52
|
export declare function validateSubtasks(arr: unknown, opts?: PlanOptions): SubtaskSpec[];
|
|
53
|
+
/** Run the planner: one model completion → validated disjoint-file sub-tasks. */
|
|
18
54
|
export declare function llmPlan(objective: string, runPlan: (prompt: string) => Promise<string>, opts?: PlanOptions): Promise<SubtaskSpec[]>;
|
|
55
|
+
/**
|
|
56
|
+
* The DIFFICULTY-ROUTER prompt (search [43], planner-light). Same disjoint-subtask contract as `planPrompt`,
|
|
57
|
+
* but the model FIRST routes: default to a SINGLE agent (one sub-task owning the whole task) and only fan out
|
|
58
|
+
* when the objective is CLEARLY + ECONOMICALLY decomposable (disjoint files, independent/non-sequential, each
|
|
59
|
+
* part buildable without most of the same context). The campaign proved forcing fan-out on a sequential/coupled
|
|
60
|
+
* task is a -98% disaster, so the bias is firmly toward single. The output is still a plain disjoint-file
|
|
61
|
+
* sub-task array — a 1-element array IS the single route (the leader runs N=1 through the same pipeline).
|
|
62
|
+
*/
|
|
19
63
|
export declare function routePrompt(objective: string, opts?: PlanOptions): string;
|
|
64
|
+
/** Run the difficulty router: one model completion → a routed plan (1 sub-task = single route, N = fan-out),
|
|
65
|
+
* validated by `parsePlan` (the same disjointness/shape invariants the Coordinator depends on). */
|
|
20
66
|
export declare function routePlan(objective: string, runRoute: (prompt: string) => Promise<string>, opts?: PlanOptions): Promise<SubtaskSpec[]>;
|
|
67
|
+
/**
|
|
68
|
+
* Decomposer quality gate (design/68 §5/§11 B1-B3): run the router, and on a rejected/unparseable plan retry
|
|
69
|
+
* the LLM AT MOST ONCE, then COLLAPSE TO SINGLE (never fail the task on a bad plan — single is almost always
|
|
70
|
+
* the right fallback, and a false fan-out is the -98% disaster; a plan failure must not be louder than that).
|
|
71
|
+
* `collapsed` carries the reason (observability: a collapse is a router-quality signal, not silent).
|
|
72
|
+
*/
|
|
21
73
|
export declare function routePlanWithFallback(objective: string, runRoute: (prompt: string) => Promise<string>, opts?: PlanOptions): Promise<{
|
|
22
74
|
subs: SubtaskSpec[];
|
|
23
75
|
collapsed?: string;
|
package/dist/leader/planner.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic economic 防灾 guard (design/68 §7 / §11 D2, search [83] ruling (b)): the asymmetry is that a
|
|
3
|
+
* false fan-out is the -98% disaster while a false single merely runs slower — so every case decidable WITHOUT
|
|
4
|
+
* the model resolves to single, for free. Pure function (unit-testable); the semantic judgments (sequential/
|
|
5
|
+
* coupled/uncertain) stay with the router LLM, and "估不准回 single" is the collapse fallback (B3).
|
|
6
|
+
*/
|
|
1
7
|
export function economicGuard(objective, opts = {}) {
|
|
2
8
|
if (opts.fanoutEnabled === false)
|
|
3
9
|
return { single: true, reason: "fan-out disabled by deployment (A6)" };
|
|
@@ -8,9 +14,12 @@ export function economicGuard(objective, opts = {}) {
|
|
|
8
14
|
}
|
|
9
15
|
return { single: false };
|
|
10
16
|
}
|
|
17
|
+
/** The deterministic SINGLE route: one sub-task owning the whole objective (the same shape the router emits
|
|
18
|
+
* for its single decision — N=1 runs through the identical leader pipeline). */
|
|
11
19
|
export function singleRoute(objective) {
|
|
12
20
|
return [{ workerId: "main", branch: "worker/main", files: ["."], spec: { objective } }];
|
|
13
21
|
}
|
|
22
|
+
/** The planning prompt: asks for a strict JSON array of disjoint-file sub-tasks. */
|
|
14
23
|
export function planPrompt(objective, opts = {}) {
|
|
15
24
|
const max = opts.maxSubtasks ?? 6;
|
|
16
25
|
return [
|
|
@@ -24,6 +33,7 @@ export function planPrompt(objective, opts = {}) {
|
|
|
24
33
|
`Rules: workerId unique + url-safe; files non-empty + disjoint across ALL sub-tasks; objective self-contained.`,
|
|
25
34
|
].filter(Boolean).join(" ");
|
|
26
35
|
}
|
|
36
|
+
/** Extract a JSON array from a model completion (tolerates ```json fences / surrounding prose). */
|
|
27
37
|
function extractJsonArray(raw) {
|
|
28
38
|
const fenced = raw.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
29
39
|
const candidate = (fenced ? fenced[1] : raw).trim();
|
|
@@ -38,11 +48,20 @@ function extractJsonArray(raw) {
|
|
|
38
48
|
throw new Error("planner output is not valid JSON");
|
|
39
49
|
}
|
|
40
50
|
}
|
|
51
|
+
/** Parse + VALIDATE a planner completion into SubtaskSpec[]. Throws on any violation (disjointness etc.). */
|
|
41
52
|
export function parsePlan(raw, opts = {}) {
|
|
42
53
|
return validateSubtasks(extractJsonArray(raw), opts);
|
|
43
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Deterministically validate an array of sub-tasks (the trust boundary — see file header). Used both for the
|
|
57
|
+
* LLM planner output AND for caller-supplied `body.subtasks`, which would otherwise skip every invariant: the
|
|
58
|
+
* `branch` is interpolated into a worker-container shell command (`git checkout -b <branch>`), so an
|
|
59
|
+
* unvalidated branch (`x; curl …|sh`, `` `reboot` ``, `--upload-pack=…`) would inject; and the disjoint-file /
|
|
60
|
+
* unique-id invariants are what the Coordinator's parallel-merge depends on. Throws on any violation.
|
|
61
|
+
*/
|
|
44
62
|
export function validateSubtasks(arr, opts = {}) {
|
|
45
63
|
const max = opts.maxSubtasks ?? 6;
|
|
64
|
+
// "plan" not "planner" — this also validates caller-supplied body.subtasks, not just LLM output.
|
|
46
65
|
if (!Array.isArray(arr) || arr.length === 0)
|
|
47
66
|
throw new Error("plan must be a non-empty array of sub-tasks");
|
|
48
67
|
if (arr.length > max)
|
|
@@ -70,6 +89,8 @@ export function validateSubtasks(arr, opts = {}) {
|
|
|
70
89
|
seenFiles.add(norm);
|
|
71
90
|
}
|
|
72
91
|
const branch = typeof e.branch === "string" && e.branch.trim() ? e.branch.trim() : `worker/${workerId}`;
|
|
92
|
+
// Reject a leading "-" (would be parsed as an option by `git checkout -b <branch>`), ".." (ref escape), and
|
|
93
|
+
// an over-long ref (git rejects it anyway; cap keeps the value tidy). The char set is shell-safe.
|
|
73
94
|
if (branch.length > 200 || !/^[A-Za-z0-9._/-]+$/.test(branch) || branch.startsWith("-") || branch.includes("..")) {
|
|
74
95
|
throw new Error(`sub-task ${workerId}: invalid branch`);
|
|
75
96
|
}
|
|
@@ -78,10 +99,19 @@ export function validateSubtasks(arr, opts = {}) {
|
|
|
78
99
|
}
|
|
79
100
|
return subs;
|
|
80
101
|
}
|
|
102
|
+
/** Run the planner: one model completion → validated disjoint-file sub-tasks. */
|
|
81
103
|
export async function llmPlan(objective, runPlan, opts = {}) {
|
|
82
104
|
const raw = await runPlan(planPrompt(objective, opts));
|
|
83
105
|
return parsePlan(raw, opts);
|
|
84
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* The DIFFICULTY-ROUTER prompt (search [43], planner-light). Same disjoint-subtask contract as `planPrompt`,
|
|
109
|
+
* but the model FIRST routes: default to a SINGLE agent (one sub-task owning the whole task) and only fan out
|
|
110
|
+
* when the objective is CLEARLY + ECONOMICALLY decomposable (disjoint files, independent/non-sequential, each
|
|
111
|
+
* part buildable without most of the same context). The campaign proved forcing fan-out on a sequential/coupled
|
|
112
|
+
* task is a -98% disaster, so the bias is firmly toward single. The output is still a plain disjoint-file
|
|
113
|
+
* sub-task array — a 1-element array IS the single route (the leader runs N=1 through the same pipeline).
|
|
114
|
+
*/
|
|
85
115
|
export function routePrompt(objective, opts = {}) {
|
|
86
116
|
const max = opts.maxSubtasks ?? 6;
|
|
87
117
|
return [
|
|
@@ -104,25 +134,39 @@ export function routePrompt(objective, opts = {}) {
|
|
|
104
134
|
`Rules: always a JSON ARRAY (SINGLE = one element, not a bare object); workerId unique + url-safe; files non-empty + DISJOINT across ALL elements; each objective self-contained.`,
|
|
105
135
|
].filter(Boolean).join(" ");
|
|
106
136
|
}
|
|
137
|
+
/** Run the difficulty router: one model completion → a routed plan (1 sub-task = single route, N = fan-out),
|
|
138
|
+
* validated by `parsePlan` (the same disjointness/shape invariants the Coordinator depends on). */
|
|
107
139
|
export async function routePlan(objective, runRoute, opts = {}) {
|
|
108
140
|
if (opts.fanoutEnabled === false)
|
|
109
|
-
return singleRoute(objective);
|
|
141
|
+
return singleRoute(objective); // A6: deploy-disabled fan-out → no router call
|
|
110
142
|
const raw = await runRoute(routePrompt(objective, opts));
|
|
111
143
|
return parsePlan(raw, opts);
|
|
112
144
|
}
|
|
145
|
+
/**
|
|
146
|
+
* Decomposer quality gate (design/68 §5/§11 B1-B3): run the router, and on a rejected/unparseable plan retry
|
|
147
|
+
* the LLM AT MOST ONCE, then COLLAPSE TO SINGLE (never fail the task on a bad plan — single is almost always
|
|
148
|
+
* the right fallback, and a false fan-out is the -98% disaster; a plan failure must not be louder than that).
|
|
149
|
+
* `collapsed` carries the reason (observability: a collapse is a router-quality signal, not silent).
|
|
150
|
+
*/
|
|
113
151
|
export async function routePlanWithFallback(objective, runRoute, opts = {}) {
|
|
114
|
-
const guard = economicGuard(objective, opts);
|
|
152
|
+
const guard = economicGuard(objective, opts); // deterministic 防灾经济 cases never pay for a router call
|
|
115
153
|
if (guard.single)
|
|
116
154
|
return { subs: singleRoute(objective) };
|
|
117
155
|
let firstErr;
|
|
118
|
-
let firstRaw = "";
|
|
156
|
+
let firstRaw = ""; // capture the raw completion of the FIRST failed attempt for diagnosis (see below)
|
|
119
157
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
158
|
+
// §5: no more than ONE LLM re-plan
|
|
120
159
|
const raw = await runRoute(routePrompt(objective, opts));
|
|
121
160
|
try {
|
|
122
161
|
return { subs: parsePlan(raw, opts) };
|
|
123
162
|
}
|
|
124
163
|
catch (e) {
|
|
125
164
|
firstErr ??= e;
|
|
165
|
+
// Capturing the raw output on collapse is load-bearing for diagnosis: a collapse logs only "not valid
|
|
166
|
+
// JSON", and the router runs on an in-memory (non-durable) session, so the raw completion is otherwise
|
|
167
|
+
// GONE — task#2d's flaky collapse could not be root-caused because nothing recorded the bad output. Keep
|
|
168
|
+
// a bounded head+tail so the next collapse is diagnosable (truncated/unterminated vs unescaped-quote vs
|
|
169
|
+
// prose) without dumping a 32k-token runaway in full.
|
|
126
170
|
if (!firstRaw)
|
|
127
171
|
firstRaw = raw.length > 600 ? `${raw.slice(0, 300)}…[${raw.length}ch]…${raw.slice(-150)}` : raw;
|
|
128
172
|
}
|