@sema-agent/server 7.43.0 → 7.44.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/USAGE.md +18 -0
- package/dist/adoption/plan.js +31 -192
- package/dist/adoption/quiesce.js +3 -86
- package/dist/adoption/runner.js +3 -140
- package/dist/adoption/sql.js +0 -74
- package/dist/adoption/wire.js +0 -80
- package/dist/approval-ask-machine.js +0 -75
- package/dist/approval-card.js +0 -323
- package/dist/approval-deny-reasons.js +0 -42
- package/dist/approval-hmac.js +0 -35
- package/dist/approval-reconciler.js +8 -166
- package/dist/approval.js +4 -67
- package/dist/audit.js +1 -44
- package/dist/auth-bridge.js +4 -47
- package/dist/auth-keys.js +0 -23
- package/dist/bake-runner/main.js +4 -65
- package/dist/bake-runner/protocol.js +3 -58
- package/dist/bake-runner/runner.js +5 -91
- package/dist/bench/l8/artifact.js +2 -61
- package/dist/bench/l8/escape.js +0 -25
- package/dist/bench/l8/index.js +0 -14
- package/dist/bench/l8/probes.js +0 -60
- package/dist/bench/l8/run-probes.js +2 -45
- package/dist/bench/s1/arms.js +14 -189
- package/dist/bench/s1/live-deps.js +11 -253
- package/dist/bench/s1/oracle.js +1 -36
- package/dist/bench/s1/repair-oracle-adapter.js +0 -30
- package/dist/bench/s1/reviewer.js +0 -34
- package/dist/bench/s1/row.js +0 -62
- package/dist/bench/s1/run-firm.js +4 -70
- package/dist/bench/s1/runner-ctx.js +0 -40
- package/dist/bench/s1/tasks.js +0 -115
- package/dist/boot/adoption.js +0 -20
- package/dist/boot/budget-tracing.js +3 -55
- package/dist/boot/config-center.js +43 -551
- package/dist/boot/coordinators.js +1 -69
- package/dist/boot/crash-last.js +0 -18
- package/dist/boot/deferred-sandbox-path-env.js +3 -119
- package/dist/boot/execution-env.js +5 -171
- package/dist/boot/governance-seams.js +0 -92
- package/dist/boot/leader.js +0 -69
- package/dist/boot/limit-sync.js +0 -2
- package/dist/boot/memory-boundary.js +3 -91
- package/dist/boot/org-memory.js +1 -25
- package/dist/boot/parked-revive-gate.js +0 -150
- package/dist/boot/permission-rules-audit.js +1 -56
- package/dist/boot/reapers.js +15 -273
- package/dist/boot/resolve-spec.js +9 -768
- package/dist/boot/retention-lane.js +4 -87
- package/dist/boot/runner-deps.js +3 -297
- package/dist/boot/runtime-caps.js +0 -40
- package/dist/boot/session-faces.js +2 -133
- package/dist/boot/shutdown.js +9 -83
- package/dist/boot/side-query-lane.js +2 -137
- package/dist/boot/stores.js +15 -215
- package/dist/boot/task-list-lane.js +0 -18
- package/dist/boot/webfetch-summarize-lane.js +0 -47
- package/dist/boot/workflow-orchestration.js +3 -99
- package/dist/boot-reclaim.js +0 -20
- package/dist/bounded-session-map.js +0 -19
- package/dist/brain.js +2 -139
- package/dist/budget.js +9 -244
- package/dist/capabilities/builtin-tools.js +0 -3
- package/dist/capabilities/center-plugins.js +6 -51
- package/dist/capabilities/center-prompts.js +6 -71
- package/dist/capabilities/code-review-council.js +7 -40
- package/dist/capabilities/collab-workflows.js +1 -44
- package/dist/capabilities/hands-lane.js +0 -65
- package/dist/capabilities/memory-notice.js +0 -70
- package/dist/capabilities/prompt.js +0 -14
- package/dist/capabilities/prompts/code-review.js +0 -14
- package/dist/capabilities/prompts/identity.js +0 -6
- package/dist/capabilities/prompts/team.js +0 -4
- package/dist/capabilities/repo-tools.js +0 -34
- package/dist/capabilities/sandbox-file-send.js +3 -92
- package/dist/capabilities/scenarios.d.ts +0 -1
- package/dist/capabilities/scenarios.js +18 -279
- package/dist/capabilities/select-environment-tool.js +0 -32
- package/dist/capabilities/send-user-file-tool.js +2 -66
- package/dist/capabilities/skills.d.ts +1 -2
- package/dist/capabilities/skills.js +3 -21
- package/dist/capabilities/team.d.ts +3 -11
- package/dist/capabilities/team.js +3 -51
- package/dist/capabilities/tool-defer.js +0 -3
- package/dist/config-center/apply-effective.js +15 -330
- package/dist/config-center/apply-ledger.js +5 -38
- package/dist/config-center/facade.js +0 -41
- package/dist/config-center/hot-keys-registry.js +0 -20
- package/dist/config-center/http-client.js +2 -125
- package/dist/config-center/mcp-revocation.js +2 -34
- package/dist/config-center/read-face.js +0 -59
- package/dist/config-center/restart-signal.js +1 -85
- package/dist/config-center/skills-mcp.d.ts +1 -1
- package/dist/config-center/skills-mcp.js +9 -76
- package/dist/config-center/stage-limits.js +8 -35
- package/dist/config-invariants.js +0 -16
- package/dist/config-lkg.js +0 -42
- package/dist/config-provider.js +3 -186
- package/dist/config-types.js +0 -5
- package/dist/config.js +107 -1145
- package/dist/degenerate-instrument.js +3 -67
- package/dist/deployment-governance.js +0 -124
- package/dist/digest-form.js +0 -12
- package/dist/elicitation.js +3 -86
- package/dist/env-facts.js +7 -75
- package/dist/fleet/fleet-bus.js +34 -507
- package/dist/fleet/fleet-reconciler.js +9 -149
- package/dist/fleet/fleet-terminal-window.js +10 -178
- package/dist/fleet/subagent-tail-bus.js +3 -72
- package/dist/fleet-client.js +10 -70
- package/dist/fleet-lease.js +5 -79
- package/dist/git-api-kind.js +0 -3
- package/dist/governance-ask-marks.js +2 -78
- package/dist/hooks/branch-transcript.js +0 -74
- package/dist/hooks/cc-agent-hook-prompt.js +0 -29
- package/dist/hooks/cc-stop-prompt.js +1 -46
- package/dist/hooks/hook-llm.js +1 -53
- package/dist/hooks/hook-runner.js +20 -414
- package/dist/http/active-run-conflict.js +4 -129
- package/dist/http/cursor-fingerprint.d.ts +5 -0
- package/dist/http/cursor-fingerprint.js +5 -0
- package/dist/http/idempotency.js +0 -37
- package/dist/http/principal-gate.js +3 -40
- package/dist/http/route-ctx.js +0 -9
- package/dist/http/routes/a2a-serve.js +7 -319
- package/dist/http/routes/admin-config-refresh.js +0 -4
- package/dist/http/routes/admin-drain.js +0 -10
- package/dist/http/routes/adoption.js +1 -29
- package/dist/http/routes/agents-roster.js +1 -47
- package/dist/http/routes/approvals-assistant.js +32 -395
- package/dist/http/routes/attachments.js +4 -22
- package/dist/http/routes/capabilities.js +3 -420
- package/dist/http/routes/diagnostics.js +1 -81
- package/dist/http/routes/fleet.js +9 -185
- package/dist/http/routes/images.js +14 -239
- package/dist/http/routes/leader.js +0 -13
- package/dist/http/routes/memory-bundle.js +1 -61
- package/dist/http/routes/memory-policy.js +9 -99
- package/dist/http/routes/notify-wake.js +3 -37
- package/dist/http/routes/observability.js +2 -19
- package/dist/http/routes/retention-ops.js +2 -34
- package/dist/http/routes/rules.js +1 -93
- package/dist/http/routes/runs.js +64 -873
- package/dist/http/routes/session-sync.js +19 -258
- package/dist/http/routes/sessions-list.js +17 -43
- package/dist/http/routes/sessions.js +34 -226
- package/dist/http/routes/shared-memory.js +5 -36
- package/dist/http/routes/side-query.js +1 -87
- package/dist/http/routes/tasks.js +52 -722
- package/dist/http/routes/trace-usage.js +32 -195
- package/dist/http/routes/workflows.js +19 -195
- package/dist/http/run-meta.js +0 -6
- package/dist/http/send.js +0 -32
- package/dist/http/server.js +92 -1597
- package/dist/http/sse-lifecycle.js +2 -13
- package/dist/http/sse-log.js +3 -48
- package/dist/http/tar.js +5 -21
- package/dist/http/verify-rounds.js +0 -5
- package/dist/http/wire-gate.js +0 -9
- package/dist/http/workspace-content.js +0 -10
- package/dist/images/bake-validate.js +1 -70
- package/dist/images/manifest.js +1 -6
- package/dist/index.js +0 -21
- package/dist/key-resolver.js +2 -17
- package/dist/leader/diffout.js +1 -20
- package/dist/leader/diffup.js +0 -47
- package/dist/leader/endpoint.js +2 -57
- package/dist/leader/fanout.js +3 -45
- package/dist/leader/grader-env-factory.js +3 -72
- package/dist/leader/leader.js +5 -156
- package/dist/leader/merge.js +8 -115
- package/dist/leader/planner.js +3 -54
- package/dist/leader/repair-oracle.js +1 -60
- package/dist/leader/repair-wire.js +2 -79
- package/dist/leader/wire.js +8 -307
- package/dist/lsp/e2b-bridge.js +4 -64
- package/dist/lsp/e2b-manager.js +6 -94
- package/dist/lsp/lsp-frames.js +0 -12
- package/dist/lsp/manager.js +4 -96
- package/dist/lsp/ws-transport.js +5 -55
- package/dist/lsp-evict.js +1 -15
- package/dist/main.js +57 -765
- package/dist/memory-bundle-engine.js +0 -55
- package/dist/memory-export.js +0 -4
- package/dist/memory-posture.js +1 -15
- package/dist/memory-scope.js +11 -148
- package/dist/memory-sync-client.js +2 -44
- package/dist/memory-sync.js +1 -80
- package/dist/model-select.js +3 -80
- package/dist/observability/cost-quota.js +1 -17
- package/dist/observability/cost-taxonomy.js +0 -34
- package/dist/observability/fail-open.js +7 -86
- package/dist/observability/logger.js +0 -6
- package/dist/observability/metrics.js +0 -94
- package/dist/observability/otel-exporter.js +3 -13
- package/dist/observability/principal-context.js +0 -9
- package/dist/observability/prompt-manifest.js +1 -37
- package/dist/observability/rate-limit.js +0 -4
- package/dist/observability/secret-env-scrub.js +2 -56
- package/dist/observability/tool-trace.js +1 -70
- package/dist/orchestration/hardened-vm-runner.js +4 -118
- package/dist/orchestration/hardened-vm-worker-runner.js +1 -26
- package/dist/orchestration/hardened-vm-worker.js +0 -27
- package/dist/orchestration/subagent-steer.js +1 -45
- package/dist/orchestration/workflow-agent-steer.js +1 -80
- package/dist/orchestration/workflow-completion-inbox.js +32 -285
- package/dist/orchestration/workflow-notify-journal.js +16 -259
- package/dist/org-memory-admission.js +3 -47
- package/dist/parent-watch.js +2 -48
- package/dist/parked-decide.js +1 -109
- package/dist/per-task-image.js +0 -57
- package/dist/plan-cache-probe.js +3 -27
- package/dist/plugins/adoption-log-sql.js +2 -119
- package/dist/plugins/approval-ask-store-memory.js +3 -38
- package/dist/plugins/approval-ask-store-sql.js +6 -188
- package/dist/plugins/approval-exemption-store.js +2 -28
- package/dist/plugins/background-agent-store-sql.js +4 -105
- package/dist/plugins/background-shell-support.js +14 -122
- package/dist/plugins/blob-backend.js +6 -169
- package/dist/plugins/breaker-state-sql.js +8 -46
- package/dist/plugins/caching-session-store.js +4 -106
- package/dist/plugins/checkpoint-store-sql.js +18 -505
- package/dist/plugins/e2b-orphan-reclaim.js +0 -45
- package/dist/plugins/file-outcome-sink.js +0 -9
- package/dist/plugins/file-resume-anchor-store.js +4 -43
- package/dist/plugins/file-run-store.js +26 -364
- package/dist/plugins/file-snapshot-store-sql.js +10 -181
- package/dist/plugins/fork-routing-session-store.js +8 -111
- package/dist/plugins/host-platform.js +2 -91
- package/dist/plugins/image-bake-store-sql.js +7 -250
- package/dist/plugins/image-index-sql.js +4 -123
- package/dist/plugins/k8s-bg-scripts.js +4 -88
- package/dist/plugins/k8s-exec-protocol.js +0 -41
- package/dist/plugins/leader-run-store-sql.js +0 -101
- package/dist/plugins/local-checkpoint-store.js +8 -128
- package/dist/plugins/local-session-store.js +34 -296
- package/dist/plugins/local-task-attachment-store.js +2 -16
- package/dist/plugins/mailbox-store-sql.js +8 -65
- package/dist/plugins/memory-embedder-fingerprint.js +5 -166
- package/dist/plugins/memory-embedder.js +4 -70
- package/dist/plugins/memory-engine-pg.js +6 -170
- package/dist/plugins/memory-engine-tidb.js +7 -155
- package/dist/plugins/memory-engine-vector-util.js +0 -10
- package/dist/plugins/memory-key-guards.js +0 -34
- package/dist/plugins/memory-origin-law.js +0 -187
- package/dist/plugins/memory-resume-anchor-store.js +0 -17
- package/dist/plugins/memory-run-store.js +12 -89
- package/dist/plugins/memory-session-policy-store.js +0 -17
- package/dist/plugins/memory-sync-store-pg.js +4 -49
- package/dist/plugins/memory-sync-store-tidb.js +3 -35
- package/dist/plugins/outcome-ledger-sql.js +3 -97
- package/dist/plugins/permission-rule-store-file.js +5 -133
- package/dist/plugins/permission-rule-store-sql.d.ts +3 -0
- package/dist/plugins/permission-rule-store-sql.js +50 -307
- package/dist/plugins/pg-cost-quota.js +0 -7
- package/dist/plugins/pg-pool.js +0 -92
- package/dist/plugins/pg-rate-limiter.js +2 -13
- package/dist/plugins/pg-safe-json.js +4 -40
- package/dist/plugins/pg-session-storage.js +25 -189
- package/dist/plugins/posix-shell-fs.js +1 -31
- package/dist/plugins/remote-env-adb.js +12 -101
- package/dist/plugins/remote-env-e2b.js +40 -371
- package/dist/plugins/remote-env-file-error.js +0 -37
- package/dist/plugins/remote-env-host.js +63 -483
- package/dist/plugins/remote-env-k8s.js +32 -326
- package/dist/plugins/remote-env-local-docker.js +24 -157
- package/dist/plugins/remote-env-ssh.js +20 -128
- package/dist/plugins/remote-scratchpad.js +2 -32
- package/dist/plugins/remote-shell.js +1 -32
- package/dist/plugins/resume-anchor-store-sql.js +0 -11
- package/dist/plugins/retention-lane-store-sql.js +0 -108
- package/dist/plugins/retention-store-sql.js +4 -383
- package/dist/plugins/roster-store-sql.js +0 -55
- package/dist/plugins/run-store-sql.js +14 -251
- package/dist/plugins/s3-presign.js +2 -49
- package/dist/plugins/scheduler-support.js +3 -80
- package/dist/plugins/send-file-ledger.js +4 -53
- package/dist/plugins/send-user-file.js +4 -94
- package/dist/plugins/session-placement.js +1 -89
- package/dist/plugins/session-policy-store-sql.js +3 -81
- package/dist/plugins/session-store.js +0 -57
- package/dist/plugins/shared-memory-store-sql.js +4 -167
- package/dist/plugins/sql-driver.js +0 -17
- package/dist/plugins/sql-errors.js +0 -7
- package/dist/plugins/sql-escape.js +0 -8
- package/dist/plugins/sql-row-helpers.js +0 -25
- package/dist/plugins/store-backend.js +42 -222
- package/dist/plugins/store-contracts.js +2 -46
- package/dist/plugins/task-attachment-store.js +3 -49
- package/dist/plugins/task-list-store-sql.js +0 -79
- package/dist/plugins/tidb-cost-quota.js +1 -4
- package/dist/plugins/tidb-pool.js +1 -207
- package/dist/plugins/tidb-rate-limiter.js +3 -9
- package/dist/plugins/tidb-session-storage.js +4 -70
- package/dist/plugins/tidb-session-store.js +19 -341
- package/dist/plugins/tool-result-store-sql.js +4 -179
- package/dist/plugins/usage-window-store-sql.js +0 -8
- package/dist/plugins/web-search.js +10 -132
- package/dist/plugins/workflow-journal-store-sql.js +2 -58
- package/dist/plugins/workflow-run-store-sql.js +6 -91
- package/dist/plugins/worktree-isolation.js +6 -126
- package/dist/plugins/write-behind-counter.js +16 -75
- package/dist/principal-jwt.js +5 -60
- package/dist/project-memory.js +15 -146
- package/dist/prompts-domain-validate.js +1 -51
- package/dist/question.js +2 -111
- package/dist/resource-suspend.js +0 -18
- package/dist/router/route-orchestration.js +0 -77
- package/dist/rules-consent.d.ts +44 -5
- package/dist/rules-consent.js +75 -215
- package/dist/run-local.js +14 -381
- package/dist/runs.js +29 -617
- package/dist/runtime-caps-resolver.js +6 -132
- package/dist/runtime-governance.js +1 -232
- package/dist/sandbox-pkg-source.js +0 -37
- package/dist/sealed-key.js +3 -68
- package/dist/security.js +6 -291
- package/dist/session-leaf-bus.js +0 -32
- package/dist/session-sync-content.js +1 -69
- package/dist/session-sync-kernel.js +3 -60
- package/dist/session-sync.js +3 -66
- package/dist/session-titler.js +7 -41
- package/dist/session-watch.js +9 -73
- package/dist/shared-memory-scope-authorizer.js +0 -18
- package/dist/sighup-idle.js +1 -12
- package/dist/spec-fields.js +4 -143
- package/dist/store-live-probe.js +3 -49
- package/dist/task-a2a.js +1 -126
- package/dist/task-cwd.js +1 -103
- package/dist/task-mcp.js +1 -89
- package/dist/task-settings.js +10 -300
- package/dist/task-workflow.js +8 -75
- package/dist/tool-approval.d.ts +28 -1
- package/dist/tool-approval.js +70 -1430
- package/dist/trace/artifacts.js +5 -20
- package/dist/trace/engine-notice-wire.js +6 -138
- package/dist/trace/ledger-sink.js +7 -96
- package/dist/trace/project.js +4 -448
- package/dist/trace/redact.js +9 -77
- package/dist/turn-activity.js +1 -27
- package/dist/usage-analytics.js +4 -34
- package/dist/wall-clock-jump-guard.js +1 -68
- package/package.json +3 -3
- package/dist/capabilities/scenario-alias.d.ts +0 -27
- package/dist/capabilities/scenario-alias.js +0 -61
package/dist/leader/leader.js
CHANGED
|
@@ -1,34 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* v2 MVP M4 — the leader orchestrator (design/50 §3, V2-MVP-PLAN.md). Composes the proven pieces into one
|
|
3
|
-
* callable API: Planner → fanOut (M1) → diff-out (M2) → merge(b) (M3) → Coordinator push.
|
|
4
|
-
*
|
|
5
|
-
* Worker-env model (design/68 §8 F2(a)): preferred = FACTORY env (runner-owned lifecycle; durable suspend only
|
|
6
|
-
* yields a complete ownedEnv on this path) + diff-out via the worker's mandatory SELF-UPLOAD last step
|
|
7
|
-
* (`fetchDiff`, diffup.ts). Compat = the original static env the leader owns: provision up-front, pull each
|
|
8
|
-
* branch's diff OUT of the still-alive env (pullDiff), destroy every env in a `finally`.
|
|
9
|
-
* fanOut's runner just routes a sessionId to that worker's env-bound runner. This is the leaderless-leader
|
|
10
|
-
* pattern (service[19]): authority = owning the integration+verification point + disjoint file-ownership.
|
|
11
|
-
*
|
|
12
|
-
* Deps injected → mock-tested; the real-infra wiring (E2B envs + deepseek runners + git push) lives in the
|
|
13
|
-
* joint dogfood, which this generalizes.
|
|
14
|
-
*/
|
|
15
1
|
import { fanOut, isDurablePauseStatus } from "./fanout.js";
|
|
16
2
|
import { pullDiff } from "./diffout.js";
|
|
17
3
|
import { mergeBranches } from "./merge.js";
|
|
18
4
|
import { Runner, runWithVerification, resumeWithVerification, delimitUntrusted } from "@sema-agent/core";
|
|
19
|
-
/** design/74 Slice 6: a belt-and-suspenders bound on the leader's resource auto-resume loop. The PRIMARY bound
|
|
20
|
-
* is the resource ledger (core fails an exhausted resume fast) + core's per-task suspend-loop cap; this only
|
|
21
|
-
* guards against an unforeseen non-progressing loop. Generous so a legitimate many-slice budget never hits it. */
|
|
22
5
|
const MAX_RESUME_SLICES = 200;
|
|
23
|
-
/** design/74 Slice 6: drive a worker's resource-suspend auto-resume to a terminal (or bound) state. Exported
|
|
24
|
-
* for unit tests. Loops `resume(token, slice)` WHILE the result carries a `resource_limit` checkpoint gate —
|
|
25
|
-
* NOT by status (the verify wrapper may surface a suspend as failed-with-token). Exits on: a terminal result
|
|
26
|
-
* (completed/failed → no gate), an exhausted budget (core fails the resume fast → no token/gate), a
|
|
27
|
-
* human-approval gate (kind ≠ "resource_limit" — never auto-resumed), `signal.aborted`, or `maxSlices` (a
|
|
28
|
-
* belt; core's ledger fail-fast + per-task suspend-loop cap are the primary bounds). Returns the final result. */
|
|
29
|
-
// Generic over the result type so BOTH leader worker paths reuse it: the verify path (VerificationResult via
|
|
30
|
-
// runWithVerification/resumeWithVerification) AND the non-verify path (plain TaskResult via runTaskStream/
|
|
31
|
-
// runner.resume). The loop only reads `checkpointToken` / `checkpointGate`, both on TaskResult.
|
|
32
6
|
export async function autoResumeOnResource(initial, signal, resume, maxSlices = MAX_RESUME_SLICES) {
|
|
33
7
|
let vr = initial;
|
|
34
8
|
for (let slice = 0; vr.checkpointToken !== undefined && vr.checkpointGate?.kind === "resource_limit" && !signal.aborted && slice < maxSlices; slice++) {
|
|
@@ -36,10 +10,6 @@ export async function autoResumeOnResource(initial, signal, resume, maxSlices =
|
|
|
36
10
|
}
|
|
37
11
|
return vr;
|
|
38
12
|
}
|
|
39
|
-
/**
|
|
40
|
-
* Run a task to an integrated, pushed result. Never throws — failures (plan, provision, fan-out, merge) are
|
|
41
|
-
* captured in LeaderResult. ALWAYS destroys every provisioned worker env (leader owns the lifecycle).
|
|
42
|
-
*/
|
|
43
13
|
export async function runLeaderTask(task, durableBaseSha, deps) {
|
|
44
14
|
let subs;
|
|
45
15
|
try {
|
|
@@ -50,12 +20,10 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
|
|
|
50
20
|
}
|
|
51
21
|
if (subs.length === 0)
|
|
52
22
|
return { ok: false, reports: [], cancelled: false, error: "planner produced no sub-tasks" };
|
|
53
|
-
// difficulty router (search [43]): a 1-subtask plan IS the single route — run through the same N=1 pipeline.
|
|
54
23
|
const route = subs.length === 1 ? "single" : "fanout";
|
|
55
24
|
const workers = [];
|
|
56
|
-
let finalReports = [];
|
|
25
|
+
let finalReports = [];
|
|
57
26
|
try {
|
|
58
|
-
// Provision every worker env up-front (leader-owned; each outlives its task for diff-out).
|
|
59
27
|
for (const sub of subs) {
|
|
60
28
|
try {
|
|
61
29
|
workers.push(await deps.provisionWorker(sub));
|
|
@@ -66,22 +34,15 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
|
|
|
66
34
|
}
|
|
67
35
|
const byId = new Map(workers.map((w) => [w.workerId, w]));
|
|
68
36
|
const bySession = new Map(workers.map((w) => [w.sessionId, w]));
|
|
69
|
-
// fan-out: route each sessionId to its own env-bound runner (M1).
|
|
70
37
|
const runner = {
|
|
71
38
|
runTaskStream: (spec) => bySession.get(spec.sessionId).runner.runTaskStream(spec),
|
|
72
39
|
};
|
|
73
40
|
const toSubtask = (w) => ({
|
|
74
41
|
workerId: w.workerId, sessionId: w.sessionId, branch: w.branch, baseSha: w.baseSha, spec: w.spec,
|
|
75
42
|
});
|
|
76
|
-
// cancelOnSuspend = the unconditional C4 reaction: a suspended settle preempts the remaining fan-out.
|
|
77
43
|
const foBase = { runner, timeoutMs: deps.timeoutMs, maxConcurrency: deps.maxConcurrency, cancelOnSuspend: true, ...(deps.logger ? { logger: deps.logger } : {}) };
|
|
78
44
|
const runWave = async (sts) => {
|
|
79
45
|
if (!deps.verifyWorker) {
|
|
80
|
-
// design/74 Slice 6 (live-test bug): the NON-verify path ALSO needs the resource-suspend auto-resume.
|
|
81
|
-
// The auto-resume previously lived only in the verify path's runWorker, so a leader with NO verifier
|
|
82
|
-
// (e.g. the drill) ran via fanOut's plain runTaskStream — a resource-suspended worker was never resumed,
|
|
83
|
-
// just surfaced "suspended". Give this path its own runWorker: run, then auto-resume across slices
|
|
84
|
-
// (plain runner.resume, no verify), then map + WARN-log any non-completed worker (no silent failures).
|
|
85
46
|
return fanOut(sts, {
|
|
86
47
|
...foBase,
|
|
87
48
|
runWorker: async (st, signal) => {
|
|
@@ -97,22 +58,13 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
|
|
|
97
58
|
deps.logger?.warn?.("worker_failed", {
|
|
98
59
|
workerId: w.workerId, status: r.status, turns: r.stats?.turns ?? 0,
|
|
99
60
|
...(r.errorCode ? { errorCode: r.errorCode } : {}),
|
|
100
|
-
// A `blocked` worker carries its reason in `blockedReason`, NOT `errorMessage` — surface it too,
|
|
101
|
-
// else a report_blocked is a silent "worker blocked" with no WHY (clay no-silent-failures).
|
|
102
61
|
...(r.errorMessage ? { error: r.errorMessage.slice(0, 200) } : {}),
|
|
103
62
|
...(r.blockedReason ? { blockedReason: r.blockedReason.slice(0, 200) } : {}),
|
|
104
63
|
});
|
|
105
64
|
}
|
|
106
65
|
return {
|
|
107
66
|
...base,
|
|
108
|
-
|
|
109
|
-
// the auto-resume left unresumed (a non-resource gate, e.g. human-approval or a plan review) stays
|
|
110
|
-
// `suspended` so the leader's C4 surface/cancel reaction still fires; only completed/timeout pass
|
|
111
|
-
// through, else failed. 🔴 park 的判据取自 `fanout.ts` 的 `isDurablePauseStatus`(扫描P2):
|
|
112
|
-
// 原地手抄的 `=== "suspended"` 漏掉 `needs_review`(plan_review / dry-run 门),会把一条还
|
|
113
|
-
// 拿着 checkpoint、环境还暂停着的 worker 报成 failed —— 与本文件带验证器那条腿早就修过的
|
|
114
|
-
// 是同一个缺陷(round-3 review BLOCKER),当时只修了那半场。
|
|
115
|
-
status: r.status === "completed" ? "completed" : isDurablePauseStatus(r.status) ? "suspended" : "failed", // core 5.8.0:TaskResult 无 "timeout"(内部词表的 timeout 仍由 fanout 墙钟臂生产)
|
|
67
|
+
status: r.status === "completed" ? "completed" : isDurablePauseStatus(r.status) ? "suspended" : "failed",
|
|
116
68
|
stats: r.stats,
|
|
117
69
|
...(r.status === "completed" ? {} : { error: r.errorMessage ?? r.blockedReason ?? `worker ${r.status}` }),
|
|
118
70
|
...(r.checkpointGate ? { checkpointGate: r.checkpointGate } : {}),
|
|
@@ -121,13 +73,6 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
|
|
|
121
73
|
},
|
|
122
74
|
});
|
|
123
75
|
}
|
|
124
|
-
// design/54 §4 L3a (core 1.72 NATIVE): each worker = runWithVerification — run the module, then an
|
|
125
|
-
// ADVERSARIAL DECORRELATED verifier (a different model) tries to BREAK it (reads diff/workspace, runs
|
|
126
|
-
// probes), with a bounded fix-loop = native per-module sub-gate + repair. Replaces the hand-rolled
|
|
127
|
-
// moduleGate. verdict FAIL after maxRounds → status:"failed" (merge quarantines). Run cost incl verify+fix.
|
|
128
|
-
// Routed THROUGH fanOut (not an unbounded Promise.all — council): reuses the semaphore (maxConcurrency), the
|
|
129
|
-
// overall timeout→cancelAll, and the shared abort signal — passed into the spec so a hung/cancelled verify
|
|
130
|
-
// actually aborts (so the overall-timeout's `await work` can unblock).
|
|
131
76
|
const verify = deps.verifyWorker;
|
|
132
77
|
return fanOut(sts, {
|
|
133
78
|
...foBase,
|
|
@@ -136,27 +81,14 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
|
|
|
136
81
|
const base = { workerId: w.workerId, sessionId: w.sessionId, branch: w.branch, baseSha: w.baseSha };
|
|
137
82
|
const cfg = w.verifierTools ? { ...verify, verifierTools: w.verifierTools } : verify;
|
|
138
83
|
let vr = await runWithVerification(w.runner, { ...w.spec, sessionId: w.sessionId, signal }, cfg);
|
|
139
|
-
// design/74 Slice 6: auto-resume a RESOURCE-suspended worker (a per-slice budget/turn/walltime overrun)
|
|
140
|
-
// until terminal — the leader spends the worker's TOTAL budget across slices instead of failing at the
|
|
141
|
-
// first slice. Loop + exit conditions live in `autoResumeOnResource` (unit-tested).
|
|
142
84
|
vr = await autoResumeOnResource(vr, signal, async (token, slice) => {
|
|
143
85
|
deps.logger?.info?.("worker_resource_resume", { workerId: w.workerId, slice });
|
|
144
86
|
return resumeWithVerification(w.runner, token, { gate: "resource_limit", decision: "continue" }, { ...w.spec, sessionId: w.sessionId, signal }, w.spec.objective ?? "", cfg);
|
|
145
87
|
});
|
|
146
88
|
deps.logger?.info?.("verify_outcome", { workerId: w.workerId, status: vr.status, verdict: vr.verification.verdict, rounds: vr.verification.rounds, finding: (vr.verification.findings[0] ?? "").slice(0, 220) });
|
|
147
89
|
const pass = vr.status === "completed" && vr.verification.verdict === "PASS";
|
|
148
|
-
// round-3 review BLOCKER: a DURABLE suspend — resource OR human-approval — comes back from
|
|
149
|
-
// runWithVerification as failed-WITH-token (core mapNestedSuspend), so gating `stillSuspended` on
|
|
150
|
-
// `resource_limit` ONLY mis-routed a human-approval suspend to `failed` → the C4 surface/cancel
|
|
151
|
-
// reaction never fired and its paused env could be reaped. Any worker still carrying a checkpoint
|
|
152
|
-
// token is suspended (resumable); a genuine failure has no token. The resource auto-resume already
|
|
153
|
-
// ran above, so a still-tokened resource worker is one the leader couldn't fully resume (deadline).
|
|
154
90
|
const stillSuspended = vr.checkpointToken !== undefined;
|
|
155
91
|
const reportStatus = pass ? "completed" : stillSuspended ? "suspended" : "failed";
|
|
156
|
-
// No-silent-failures (clay 2026-06-15): WARN on any non-completed REPORT status — incl. a verify-FAIL
|
|
157
|
-
// on an impl that COMPLETED (vr.status "completed" but reportStatus "failed"), which the old
|
|
158
|
-
// `vr.status !== completed` guard missed. Carry the impl's reason: errorCode, errorMessage, AND
|
|
159
|
-
// blockedReason (a report_blocked worker's reason lives there, not in errorMessage), plus the verdict.
|
|
160
92
|
if (reportStatus !== "completed") {
|
|
161
93
|
deps.logger?.warn?.("worker_failed", {
|
|
162
94
|
workerId: w.workerId, status: reportStatus, turns: vr.stats?.turns ?? 0, verdict: vr.verification.verdict,
|
|
@@ -168,8 +100,6 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
|
|
|
168
100
|
return {
|
|
169
101
|
...base, status: reportStatus, stats: vr.stats,
|
|
170
102
|
moduleJudge: vr.verification.verdict === "PASS" ? "pass" : "fail", repairs: Math.max(0, vr.verification.rounds - 1),
|
|
171
|
-
// A verify FAIL/PARTIAL (the impl completed but the verifier rejected it) reports the verdict;
|
|
172
|
-
// an `unverified` (the impl didn't complete — suspend/block/error) reports the impl's own reason.
|
|
173
103
|
...(pass
|
|
174
104
|
? {}
|
|
175
105
|
: {
|
|
@@ -183,18 +113,8 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
|
|
|
183
113
|
},
|
|
184
114
|
});
|
|
185
115
|
};
|
|
186
|
-
// LEADER-REPAIRLOOP-INTEGRATION §10.6 — the single-agent repair loop's terminal + held candidate, lifted onto
|
|
187
|
-
// every terminal LeaderResult. Set ONLY when `deps.repairLoop` ran on a single-agent path (LEADER_REPAIR_LOOP
|
|
188
|
-
// on); undefined otherwise → the result is byte-identical to today. The `repairTerminal` ALSO rides on the
|
|
189
|
-
// solo worker's report (so the merge push chokepoint, merge.ts, holds an unconfirmed candidate by construction).
|
|
190
116
|
let repairTerminal;
|
|
191
117
|
let candidatePatch;
|
|
192
|
-
// Route a SINGLE provisioned worker through the opt-in `runRepairLoop` (when `deps.repairLoop` is set) instead
|
|
193
|
-
// of the plain fan-out map: GENERATE == one runner.runTask, GRADED by the isolated grader, capped at
|
|
194
|
-
// `candidate_only`. Attaches `repairTerminal` to the report (the chokepoint reads it) + lifts the held
|
|
195
|
-
// candidate. Falls back to `runWave([toSubtask(solo)])` (today's path) when the repair loop is not attached
|
|
196
|
-
// OR the per-worker resolve throws (never break the leader). Returns the same shape as `runWave` so the
|
|
197
|
-
// call-sites are a drop-in. §10.6: this covers BOTH single-agent paths (collapse + N=1) through one helper.
|
|
198
118
|
const runSolo = async (solo, sub) => {
|
|
199
119
|
if (!deps.repairLoop)
|
|
200
120
|
return runWave([toSubtask(solo)]);
|
|
@@ -207,14 +127,10 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
|
|
|
207
127
|
return { reports: [rr.report], cancelled: false };
|
|
208
128
|
}
|
|
209
129
|
catch (e) {
|
|
210
|
-
// The repair wire already maps a thrown engine error to a failed report; a throw HERE is a wire/resolve
|
|
211
|
-
// failure — degrade to the plain wave so the solo still runs (no silent break; warn the WHY).
|
|
212
130
|
deps.logger?.warn?.("repair_loop_dispatch_failed", { workerId: solo.workerId, err: e instanceof Error ? e.message : String(e) });
|
|
213
131
|
return runWave([toSubtask(solo)]);
|
|
214
132
|
}
|
|
215
133
|
};
|
|
216
|
-
// N=1 (route==="single"): the lone worker IS the single-agent path → route it through `runSolo`. With
|
|
217
|
-
// `deps.repairLoop` unset, `runSolo` delegates straight to `runWave` → byte-identical to today's `runWave(...)`.
|
|
218
134
|
const fo1 = route === "single" && workers.length === 1
|
|
219
135
|
? await runSolo(workers[0], subs[0])
|
|
220
136
|
: await runWave(workers.map(toSubtask));
|
|
@@ -222,17 +138,11 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
|
|
|
222
138
|
let cancelled = fo1.cancelled;
|
|
223
139
|
finalReports = reports;
|
|
224
140
|
const workerRepoDir = deps.workerRepoDir ?? deps.repoDir;
|
|
225
|
-
// One diff source per worker: self-uploaded (factory env, F2(a)) preferred; else pull from the static env.
|
|
226
141
|
const workerDiff = (w) => w.fetchDiff
|
|
227
142
|
? w.fetchDiff()
|
|
228
143
|
: w.diffEnv
|
|
229
144
|
? pullDiff(w.diffEnv, w.baseSha, { ref: w.branch, ...(workerRepoDir ? { cwd: workerRepoDir } : {}) })
|
|
230
145
|
: Promise.resolve({ ok: false, error: `worker ${w.workerId} has neither fetchDiff nor diffEnv` });
|
|
231
|
-
// design/74 Slice 6 (task#16, round-3 review BLOCKER): salvage the diff (belt auto-commit + diff-out) of
|
|
232
|
-
// workers that did NOT merge (failed / still-suspended) so their progress isn't silently dropped. Must run
|
|
233
|
-
// on EVERY terminal LeaderResult path — the main merge AND the early returns (surfaceSuspended, collapse) —
|
|
234
|
-
// not just the merge, else a suspended-preempted or collapsed run loses the non-completed workers' work.
|
|
235
|
-
// Best-effort: a fetch failure or an empty/paused-env diff is skipped; it NEVER blocks the result.
|
|
236
146
|
const salvageNonCompleted = async (rs) => {
|
|
237
147
|
const out = [];
|
|
238
148
|
await Promise.all(rs
|
|
@@ -247,19 +157,10 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
|
|
|
247
157
|
out.push({ workerId: r.workerId, sessionId: r.sessionId, patch: d.patch });
|
|
248
158
|
}
|
|
249
159
|
catch {
|
|
250
|
-
/* best-effort — a salvage fetch must not break the leader result */
|
|
251
160
|
}
|
|
252
161
|
}));
|
|
253
162
|
return out;
|
|
254
163
|
};
|
|
255
|
-
// design/52 L3a — per-module sub-gate + bounded repair (between fan-out and merge). Judge each completed
|
|
256
|
-
// worker's module diff vs its sub-objective; on fail, re-run that ONE worker with feedback (bounded), then
|
|
257
|
-
// re-judge. Still-failing modules → status:"failed" so merge quarantines them. Reaps per-module escapes the
|
|
258
|
-
// end-integration test can't see + restores cheap-team correctness via localized repair (search [38]).
|
|
259
|
-
// design/158 A13: lifted from an inline `if (deps.moduleGate) { await Promise.all(...) }` block into a named
|
|
260
|
-
// stage function (same closure scope/capture/call-site as before — behavior identical), alongside the other
|
|
261
|
-
// already-named internal stages of this function (runWave/runSolo/salvageNonCompleted/surfaceSuspended/
|
|
262
|
-
// collapseToSingle below).
|
|
263
164
|
const runModuleGate = async () => {
|
|
264
165
|
if (!deps.moduleGate)
|
|
265
166
|
return;
|
|
@@ -271,7 +172,7 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
|
|
|
271
172
|
for (let attempt = 0; attempt <= maxR; attempt++) {
|
|
272
173
|
const d = await workerDiff(w);
|
|
273
174
|
if (!d.ok)
|
|
274
|
-
return;
|
|
175
|
+
return;
|
|
275
176
|
const verdict = await deps.moduleGate.judge(sub, d.patch);
|
|
276
177
|
r.moduleJudge = verdict.ok ? "pass" : "fail";
|
|
277
178
|
r.repairs = attempt;
|
|
@@ -286,7 +187,7 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
|
|
|
286
187
|
try {
|
|
287
188
|
const rr = await w.runner.runTaskStream({ ...sub.spec, objective: deps.moduleGate.repairObjective(sub, verdict.reason ?? ""), sessionId: `${w.sessionId}-r${attempt + 1}` }).result();
|
|
288
189
|
if (r.stats && rr?.stats)
|
|
289
|
-
r.stats = { ...r.stats, tokens: (r.stats.tokens ?? 0) + (rr.stats.tokens ?? 0), costMicroUsd: (r.stats.costMicroUsd ?? 0) + (rr.stats.costMicroUsd ?? 0) };
|
|
190
|
+
r.stats = { ...r.stats, tokens: (r.stats.tokens ?? 0) + (rr.stats.tokens ?? 0), costMicroUsd: (r.stats.costMicroUsd ?? 0) + (rr.stats.costMicroUsd ?? 0) };
|
|
290
191
|
}
|
|
291
192
|
catch (e) {
|
|
292
193
|
r.status = "failed";
|
|
@@ -297,7 +198,6 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
|
|
|
297
198
|
}));
|
|
298
199
|
};
|
|
299
200
|
await runModuleGate();
|
|
300
|
-
// merge(b) deps: diff-out per worker (self-upload or static pull), integrate, Coordinator push.
|
|
301
201
|
const mergeDeps = {
|
|
302
202
|
pullWorkerDiff: async (r) => workerDiff(byId.get(r.workerId)),
|
|
303
203
|
provisionIntegrationSandbox: deps.provisionIntegrationSandbox,
|
|
@@ -305,23 +205,12 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
|
|
|
305
205
|
testCmd: deps.testCmd,
|
|
306
206
|
...(deps.measureCmd ? { measureCmd: deps.measureCmd } : {}),
|
|
307
207
|
...(deps.repoDir ? { repoDir: deps.repoDir } : {}),
|
|
308
|
-
// §10.3/§10.4 — the push-gate extras (OFF by default; set by the wire from LEADER_MEASURE_GATES).
|
|
309
208
|
...(deps.mergeGates?.measureGates ? { measureGates: true } : {}),
|
|
310
209
|
...(deps.mergeGates?.strongOracleSeeded ? { strongOracleSeeded: true } : {}),
|
|
311
210
|
...(deps.mergeGates?.measureDrivesRepair ? { measureDrivesRepair: deps.mergeGates.measureDrivesRepair } : {}),
|
|
312
211
|
...(deps.logger ? { logger: deps.logger } : {}),
|
|
313
212
|
};
|
|
314
|
-
// ── replan-lite (design/68 §6): deterministic reaction table, arbitrated in C6 priority order ──
|
|
315
|
-
// Spec priority: suspended > budget > merge-conflict > verify-fail > worker-failed; only the highest-
|
|
316
|
-
// priority reaction runs, and the check order below IS the arbitration (each returns; later checks never
|
|
317
|
-
// see its trigger). DETECTION order differs for merge-conflict: it is only observable AFTER mergeBranches
|
|
318
|
-
// runs, i.e. after the verify/failed reactions — which is spec-equivalent, because all three reactions are
|
|
319
|
-
// the same collapse-to-single (the ordering only affects the recorded `trigger` label, and a verify/failed
|
|
320
|
-
// collapse removes the conflicting diff before a merge could ever surface its conflict).
|
|
321
213
|
let redispatched;
|
|
322
|
-
// C4 (unconditional): a suspended worker preempted the wave (fan-out cancelOnSuspend already cancelled the
|
|
323
|
-
// rest). NO merge, NO redispatch — surface failed + the resume token; its checkpoint + paused env are intact
|
|
324
|
-
// (the finally below exempts it from the static-env destroy; factory envs are exempt by construction).
|
|
325
214
|
const surfaceSuspended = async (rs, wave) => {
|
|
326
215
|
const sus = wave.filter((r) => r.status === "suspended");
|
|
327
216
|
if (sus.length === 0)
|
|
@@ -336,16 +225,9 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
|
|
|
336
225
|
...(salvaged.length > 0 ? { salvaged } : {}),
|
|
337
226
|
};
|
|
338
227
|
};
|
|
339
|
-
// Collapse-to-single (at most once): one fresh worker redoes the whole task on a clean branch. The discarded
|
|
340
|
-
// workers' envs die via the normal lifecycle (factory finish / the static finally) — E1. The full env-layer
|
|
341
|
-
// "adopt N half-done workspaces" seam (design/68 §8) stays future; INSTEAD (search 2026-06-16) we REUSE the
|
|
342
|
-
// fan-out work the lightweight, zero-new-seam way — hand the collapse-solo the diffs the parallel workers
|
|
343
|
-
// already produced as reference so it adapts their correct output (copy the non-overlapping modules, resolve
|
|
344
|
-
// only the overlap) rather than re-deriving the whole task blind.
|
|
345
228
|
const collapseToSingle = async (trigger, reason, priorMerge) => {
|
|
346
229
|
deps.logger?.warn?.("replan_collapse_to_single", { trigger, reason: reason.slice(0, 300) });
|
|
347
230
|
const replan = { trigger, collapsed: reason.slice(0, 300), ...(redispatched ? { redispatched } : {}) };
|
|
348
|
-
// Best-effort gather of the fan-out workers' diffs (same source as salvage); an empty/failed diff is omitted.
|
|
349
231
|
const priorDiffs = [];
|
|
350
232
|
await Promise.all(reports.map(async (r) => {
|
|
351
233
|
const w = byId.get(r.workerId);
|
|
@@ -357,7 +239,6 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
|
|
|
357
239
|
priorDiffs.push({ workerId: r.workerId, status: r.status, patch: d.patch });
|
|
358
240
|
}
|
|
359
241
|
catch {
|
|
360
|
-
/* best-effort — a salvage fetch must not break the collapse path */
|
|
361
242
|
}
|
|
362
243
|
}));
|
|
363
244
|
if (priorDiffs.length > 0)
|
|
@@ -369,11 +250,8 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
|
|
|
369
250
|
``,
|
|
370
251
|
`--- PRIOR PARALLEL WORK — REUSE IT, do not re-derive from scratch ---`,
|
|
371
252
|
`${priorDiffs.length} worker(s) attempted this task in parallel; their changes could NOT be integrated cleanly (${reason.slice(0, 150)}). Each worker's diff over the base is below. COPY the correct, non-overlapping parts verbatim and produce ONE coherent change for the WHOLE task, resolving only the overlaps. Treat these as a strong starting point — fix anything wrong, but do not throw the work away.`,
|
|
372
|
-
// BL-30/BL-32 — each prior worker's diff is untrusted worker-authored code; delimit it so an injected
|
|
373
|
-
// instruction in a diff comment can't steer the collapse-solo agent (design/53 trifecta).
|
|
374
253
|
...priorDiffs.map((d) => delimitUntrusted(`PRIOR WORKER '${d.workerId}' (${d.status}) DIFF (untrusted)`, d.patch.slice(0, 8000))),
|
|
375
254
|
].join("\n");
|
|
376
|
-
// The synthetic collapse-solo sub-task (kept as a value so the §10.6 repair-loop dispatch can pass it).
|
|
377
255
|
const soloSub = { workerId: "collapse-solo", branch: "worker/collapse-solo", files: ["."], spec: { objective: soloObjective } };
|
|
378
256
|
let solo;
|
|
379
257
|
try {
|
|
@@ -386,18 +264,12 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
|
|
|
386
264
|
workers.push(solo);
|
|
387
265
|
byId.set(solo.workerId, solo);
|
|
388
266
|
bySession.set(solo.sessionId, solo);
|
|
389
|
-
// §10.6: collapse-to-single IS a single-agent path → route through `runSolo` (the opt-in repair loop when
|
|
390
|
-
// `deps.repairLoop` is set; the plain wave otherwise). `runSolo` sets the outer `repairTerminal`/
|
|
391
|
-
// `candidatePatch`, which are lifted onto this return so the chokepoint-held candidate reaches the caller.
|
|
392
267
|
const fo = await runSolo(solo, soloSub);
|
|
393
268
|
finalReports = [...reports, ...fo.reports];
|
|
394
269
|
const sus = await surfaceSuspended(finalReports, fo.reports);
|
|
395
270
|
if (sus)
|
|
396
271
|
return { ...sus, replan: { ...replan, trigger: "suspended" }, ...(repairTerminal ? { repairTerminal } : {}), ...(candidatePatch ? { candidatePatch } : {}) };
|
|
397
272
|
const mRaw = await mergeBranches(fo.reports, durableBaseSha, mergeDeps);
|
|
398
|
-
// wq64gmm5e: carry the FAILED prior merge's resolver spend across the collapse so captureTeamC1 sees the
|
|
399
|
-
// tokens it already burned (a --3way conflict that triggered collapse may have run a conflictResolver). Fold
|
|
400
|
-
// it into the collapse-merge's conflictResolverCostUsd; default path (no prior cost) is byte-identical.
|
|
401
273
|
const priorCost = priorMerge?.conflictResolverCostUsd ?? 0;
|
|
402
274
|
const m = priorCost > 0 ? { ...mRaw, conflictResolverCostUsd: (mRaw.conflictResolverCostUsd ?? 0) + priorCost } : mRaw;
|
|
403
275
|
const salvaged = await salvageNonCompleted(finalReports);
|
|
@@ -415,22 +287,16 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
|
|
|
415
287
|
const sus1 = await surfaceSuspended(reports, reports);
|
|
416
288
|
if (sus1)
|
|
417
289
|
return sus1;
|
|
418
|
-
// C5 budget: fan-out overspent → stop fanning, single continues (no expire of anything — C7 falls out of
|
|
419
|
-
// the suspended check above winning first).
|
|
420
290
|
if (deps.replan?.budgetUsd !== undefined) {
|
|
421
291
|
const spent = reports.reduce((s, r) => s + (r.stats?.costMicroUsd ?? 0) / 1e6, 0);
|
|
422
292
|
if (spent > deps.replan.budgetUsd) {
|
|
423
293
|
return collapseToSingle("budget", `fan-out spent $${spent.toFixed(4)} > budget $${deps.replan.budgetUsd}`);
|
|
424
294
|
}
|
|
425
295
|
}
|
|
426
|
-
// C2 verify FAIL: the bounded repair was already spent INSIDE the verify loop (runWithVerification rounds /
|
|
427
|
-
// moduleGate repairs) — a worker still failing its module gate collapses, it is not redispatched.
|
|
428
296
|
if (deps.replan && reports.some((r) => r.moduleJudge === "fail")) {
|
|
429
297
|
const failed = reports.filter((r) => r.moduleJudge === "fail").map((r) => r.workerId);
|
|
430
298
|
return collapseToSingle("verify-fail", `module verification failed after bounded repair: ${failed.join(", ")}`);
|
|
431
299
|
}
|
|
432
|
-
// C1 worker failed: redispatch ×1, same spec, FRESH env + clean base (provisionWorker provisions anew; the
|
|
433
|
-
// crashed worker's half-written workspace is never reused) → still failed = collapse-remainder-to-single.
|
|
434
300
|
if (deps.replan) {
|
|
435
301
|
const failedIds = reports.filter((r) => r.status === "failed").map((r) => r.workerId);
|
|
436
302
|
if (failedIds.length > 0) {
|
|
@@ -441,7 +307,7 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
|
|
|
441
307
|
for (const id of failedIds) {
|
|
442
308
|
const w = await deps.provisionWorker(subById.get(id));
|
|
443
309
|
fresh.push(w);
|
|
444
|
-
workers.push(w);
|
|
310
|
+
workers.push(w);
|
|
445
311
|
}
|
|
446
312
|
}
|
|
447
313
|
catch (e) {
|
|
@@ -465,28 +331,16 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
|
|
|
465
331
|
}
|
|
466
332
|
}
|
|
467
333
|
}
|
|
468
|
-
// merge(b): integrate + Coordinator push.
|
|
469
334
|
const merge = await mergeBranches(reports, durableBaseSha, mergeDeps);
|
|
470
|
-
// C3 merge conflict: merge(b)'s sequential --3way apply IS the serial replay; a --3way failure means an
|
|
471
|
-
// architecture-level conflict → collapse to single + RECORD (the design/52 data feed).
|
|
472
335
|
if (!merge.ok && merge.phase === "apply" && deps.replan) {
|
|
473
336
|
deps.logger?.warn?.("replan_merge_conflict", { reason: merge.reason, quarantine: merge.quarantine ?? [] });
|
|
474
337
|
return collapseToSingle("merge-conflict", merge.reason, merge);
|
|
475
338
|
}
|
|
476
|
-
// design/74 Slice 6 (task#16): salvage the progress of workers that did NOT merge (failed / still-
|
|
477
|
-
// suspended). The belt auto-commit + diff-out ran on env teardown, so a non-empty patch exists (in S3 for a
|
|
478
|
-
// factory worker, or pullable from a still-alive static env, before the `finally` destroys it) — surface it
|
|
479
|
-
// so the work isn't silently lost (the migration-run loss: a network-failed worker's commits vanished).
|
|
480
|
-
// Best-effort: a fetch failure or an empty/paused-env diff is skipped, and it NEVER blocks the merge result.
|
|
481
339
|
const salvaged = await salvageNonCompleted(reports);
|
|
482
340
|
return {
|
|
483
341
|
ok: merge.ok, reports, merge, cancelled, route,
|
|
484
342
|
...(redispatched ? { replan: { trigger: "worker-failed", redispatched } } : {}),
|
|
485
343
|
...(salvaged.length > 0 ? { salvaged } : {}),
|
|
486
|
-
// §10.6 — lift the single-agent repair loop's terminal (set by `runSolo` on the N=1 path) + the merge
|
|
487
|
-
// chokepoint's terminal onto the result so the HTTP status map (`endpoint.ts:75`) surfaces `needs_human`.
|
|
488
|
-
// `repairTerminal` (the loop's own) takes precedence over `merge.terminal` (the chokepoint's). Both undefined
|
|
489
|
-
// on the default path → byte-identical. The held `candidatePatch` (delimited at the wire) rides alongside.
|
|
490
344
|
...(repairTerminal ?? merge.terminal ? { repairTerminal: repairTerminal ?? merge.terminal } : {}),
|
|
491
345
|
...(candidatePatch
|
|
492
346
|
? { candidatePatch }
|
|
@@ -496,11 +350,6 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
|
|
|
496
350
|
};
|
|
497
351
|
}
|
|
498
352
|
finally {
|
|
499
|
-
// Static envs: the leader owns the lifecycle — destroy on EVERY path, EXCEPT a suspended worker's env
|
|
500
|
-
// (C4: destroying it kills the resume; its checkpoint + paused env belong to the single-agent resume path,
|
|
501
|
-
// orphan protection = the existing deadline reap). Factory envs carry no `destroy` (runner-owned).
|
|
502
|
-
// Keyed by sessionId, not workerId: a redispatched worker shares its predecessor's workerId — exempting by
|
|
503
|
-
// workerId would also spare the ORIGINAL failed worker's env (leak, council finding).
|
|
504
353
|
const suspendedSessions = new Set(finalReports.filter((r) => r.status === "suspended").map((r) => r.sessionId));
|
|
505
354
|
await Promise.allSettled(workers.filter((w) => w.destroy && !suspendedSessions.has(w.sessionId)).map((w) => w.destroy()));
|
|
506
355
|
}
|