harnery 0.43.0 → 0.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/README.md +3 -3
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +41 -48
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +38 -11
- package/dist/commands/diagnostics.d.ts.map +1 -1
- package/dist/commands/diagnostics.js +10 -0
- package/dist/commands/doctor.d.ts +12 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +22 -7
- package/dist/commands/governor.js +22 -10
- package/dist/commands/inbox.d.ts +1 -1
- package/dist/commands/inbox.d.ts.map +1 -1
- package/dist/commands/inbox.js +55 -2
- package/dist/commands/rm.d.ts.map +1 -1
- package/dist/commands/rm.js +28 -4
- package/dist/commands/servers.d.ts.map +1 -1
- package/dist/commands/servers.js +16 -1
- package/dist/commands/supervisor.d.ts.map +1 -1
- package/dist/commands/supervisor.js +7 -2
- package/dist/commands/toc.d.ts +14 -0
- package/dist/commands/toc.d.ts.map +1 -1
- package/dist/commands/toc.js +7 -4
- package/dist/commands/workflow.js +24 -30
- package/dist/core/agents/live-authority-v3.d.ts +1 -0
- package/dist/core/agents/live-authority-v3.d.ts.map +1 -1
- package/dist/core/agents/live-authority-v3.js +11 -0
- package/dist/core/agents/live-lifecycle-v3.d.ts +4 -2
- package/dist/core/agents/live-lifecycle-v3.d.ts.map +1 -1
- package/dist/core/agents/live-lifecycle-v3.js +28 -7
- package/dist/core/agents/mailbox.d.ts +9 -8
- package/dist/core/agents/mailbox.d.ts.map +1 -1
- package/dist/core/agents/mailbox.js +24 -17
- package/dist/core/agents/render/prompt-context.d.ts +2 -0
- package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
- package/dist/core/agents/render/prompt-context.js +8 -12
- package/dist/core/agents/render/session-context.d.ts +2 -8
- package/dist/core/agents/render/session-context.d.ts.map +1 -1
- package/dist/core/agents/render/session-context.js +7 -30
- package/dist/core/agents/rules/stop-hook.js +3 -3
- package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-writer.js +25 -14
- package/dist/core/agents/state/live-coordination-writer.d.ts.map +1 -1
- package/dist/core/agents/state/live-coordination-writer.js +5 -0
- package/dist/core/agents/state/pidmap.d.ts.map +1 -1
- package/dist/core/agents/state/pidmap.js +5 -0
- package/dist/core/agents/state/proc-start.d.ts +19 -0
- package/dist/core/agents/state/proc-start.d.ts.map +1 -1
- package/dist/core/agents/state/proc-start.js +41 -0
- package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
- package/dist/core/agents/state/stale-sweep.js +180 -70
- package/dist/core/agents/state/unchanged-file.d.ts +44 -0
- package/dist/core/agents/state/unchanged-file.d.ts.map +1 -0
- package/dist/core/agents/state/unchanged-file.js +390 -0
- package/dist/core/context/index.d.ts +22 -10
- package/dist/core/context/index.d.ts.map +1 -1
- package/dist/core/context/index.js +381 -119
- package/dist/core/context/observation.d.ts +26 -0
- package/dist/core/context/observation.d.ts.map +1 -0
- package/dist/core/context/observation.js +11 -0
- package/dist/core/diagnostics/bundle.d.ts.map +1 -1
- package/dist/core/diagnostics/bundle.js +25 -14
- package/dist/core/diagnostics/path-identity.d.ts +3 -0
- package/dist/core/diagnostics/path-identity.d.ts.map +1 -0
- package/dist/core/diagnostics/path-identity.js +12 -0
- package/dist/core/diagnostics/sanitize.d.ts.map +1 -1
- package/dist/core/diagnostics/sanitize.js +4 -3
- package/dist/core/events/v3/authority-outbox.d.ts.map +1 -1
- package/dist/core/events/v3/authority-outbox.js +29 -0
- package/dist/core/events/v3/generation-custody.d.ts +5 -0
- package/dist/core/events/v3/generation-custody.d.ts.map +1 -0
- package/dist/core/events/v3/generation-custody.js +50 -0
- package/dist/core/events/v3/producers/coordination-recorder.d.ts +1 -0
- package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/coordination-recorder.js +7 -0
- package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/recorder.js +77 -64
- package/dist/core/governor/planning.d.ts.map +1 -1
- package/dist/core/governor/planning.js +14 -1
- package/dist/core/governor/service-read.d.ts +10 -2
- package/dist/core/governor/service-read.d.ts.map +1 -1
- package/dist/core/governor/service-read.js +105 -22
- package/dist/core/governor/service.d.ts +20 -2
- package/dist/core/governor/service.d.ts.map +1 -1
- package/dist/core/governor/service.js +72 -47
- package/dist/core/hooks/cli.js +33 -5
- package/dist/core/inbox/contract.d.ts +2 -1
- package/dist/core/inbox/contract.d.ts.map +1 -1
- package/dist/core/inbox/index.d.ts +2 -0
- package/dist/core/inbox/index.d.ts.map +1 -1
- package/dist/core/inbox/index.js +2 -0
- package/dist/core/inbox/peer-messages.d.ts +37 -0
- package/dist/core/inbox/peer-messages.d.ts.map +1 -0
- package/dist/core/inbox/peer-messages.js +72 -0
- package/dist/core/inbox/service.d.ts +8 -1
- package/dist/core/inbox/service.d.ts.map +1 -1
- package/dist/core/inbox/service.js +67 -21
- package/dist/core/inbox/targets.d.ts +22 -0
- package/dist/core/inbox/targets.d.ts.map +1 -0
- package/dist/core/inbox/targets.js +108 -0
- package/dist/core/inbox/watcher.js +7 -3
- package/dist/core/journal/index.d.ts.map +1 -1
- package/dist/core/journal/index.js +17 -2
- package/dist/core/resources/contract.d.ts +3 -0
- package/dist/core/resources/contract.d.ts.map +1 -1
- package/dist/core/resources/service-status.d.ts +14 -1
- package/dist/core/resources/service-status.d.ts.map +1 -1
- package/dist/core/resources/service-status.js +23 -13
- package/dist/core/resources/service.d.ts +9 -1
- package/dist/core/resources/service.d.ts.map +1 -1
- package/dist/core/resources/service.js +47 -27
- package/dist/core/resources/status.d.ts +2 -5
- package/dist/core/resources/status.d.ts.map +1 -1
- package/dist/core/resources/status.js +36 -7
- package/dist/core/servers/index.d.ts +24 -13
- package/dist/core/servers/index.d.ts.map +1 -1
- package/dist/core/servers/index.js +132 -92
- package/dist/core/servers/tunnels.d.ts +3 -5
- package/dist/core/servers/tunnels.d.ts.map +1 -1
- package/dist/core/servers/tunnels.js +3 -9
- package/dist/core/supervisor/contract.d.ts +12 -0
- package/dist/core/supervisor/contract.d.ts.map +1 -1
- package/dist/core/supervisor/explanations.d.ts.map +1 -1
- package/dist/core/supervisor/explanations.js +2 -1
- package/dist/core/supervisor/findings.d.ts.map +1 -1
- package/dist/core/supervisor/findings.js +54 -9
- package/dist/core/supervisor/service.d.ts +9 -1
- package/dist/core/supervisor/service.d.ts.map +1 -1
- package/dist/core/supervisor/service.js +59 -29
- package/dist/core/supervisor/services.d.ts.map +1 -1
- package/dist/core/supervisor/services.js +22 -1
- package/dist/core/supervisor/status.d.ts +17 -2
- package/dist/core/supervisor/status.d.ts.map +1 -1
- package/dist/core/supervisor/status.js +70 -12
- package/dist/core/supervisor/timeline.d.ts.map +1 -1
- package/dist/core/supervisor/timeline.js +2 -1
- package/dist/core/work/state.js +7 -7
- package/dist/core/workflow/engine.d.ts.map +1 -1
- package/dist/core/workflow/engine.js +105 -32
- package/dist/core/workflow/failure-evidence.d.ts +16 -0
- package/dist/core/workflow/failure-evidence.d.ts.map +1 -0
- package/dist/core/workflow/failure-evidence.js +114 -0
- package/dist/core/workflow/index.d.ts +1 -1
- package/dist/core/workflow/index.d.ts.map +1 -1
- package/dist/core/workflow/index.js +1 -1
- package/dist/core/workflow/live-session-v3.d.ts +6 -0
- package/dist/core/workflow/live-session-v3.d.ts.map +1 -1
- package/dist/core/workflow/live-session-v3.js +126 -5
- package/dist/core/workflow/proof.d.ts +5 -5
- package/dist/core/workflow/proof.d.ts.map +1 -1
- package/dist/core/workflow/proof.js +115 -20
- package/dist/core/workflow/run-state.d.ts +3 -1
- package/dist/core/workflow/run-state.d.ts.map +1 -1
- package/dist/core/workflow/run-state.js +31 -27
- package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
- package/dist/core/workflow/spawn-claude.js +19 -0
- package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
- package/dist/core/workflow/spawn-codex.js +13 -0
- package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
- package/dist/core/workflow/spawn-cursor.js +19 -0
- package/dist/core/workflow/spawn-opencode.d.ts.map +1 -1
- package/dist/core/workflow/spawn-opencode.js +14 -0
- package/dist/core/workflow/types.d.ts +64 -17
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/index.d.ts +0 -1
- package/dist/core/workflow/workspaces/index.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/index.js +0 -1
- package/dist/core/workflow/workspaces/leases.d.ts +3 -1
- package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/leases.js +30 -16
- package/dist/core/workflow/workspaces/local-git.d.ts +6 -2
- package/dist/core/workflow/workspaces/local-git.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/local-git.js +270 -6
- package/dist/core/workflow/workspaces/ownership.d.ts +11 -0
- package/dist/core/workflow/workspaces/ownership.d.ts.map +1 -0
- package/dist/core/workflow/workspaces/ownership.js +194 -0
- package/dist/core/workflow/workspaces/reclaim.d.ts +35 -0
- package/dist/core/workflow/workspaces/reclaim.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/reclaim.js +14 -0
- package/dist/core/workflow/workspaces/recovery.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/recovery.js +21 -13
- package/dist/core/workflow/workspaces/state.d.ts +1 -0
- package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/state.js +21 -1
- package/dist/lib/browser/windows-chrome.d.ts +100 -12
- package/dist/lib/browser/windows-chrome.d.ts.map +1 -1
- package/dist/lib/browser/windows-chrome.js +262 -73
- package/dist/lib/docs-links.d.ts.map +1 -1
- package/dist/lib/docs-links.js +45 -2
- package/dist/lib/guarded-remove.d.ts +7 -0
- package/dist/lib/guarded-remove.d.ts.map +1 -1
- package/dist/lib/guarded-remove.js +106 -12
- package/dist/lib/identities/assume.d.ts.map +1 -1
- package/dist/lib/identities/assume.js +4 -0
- package/dist/lib/zip/index.d.ts +41 -0
- package/dist/lib/zip/index.d.ts.map +1 -0
- package/dist/lib/zip/index.js +398 -0
- package/package.json +7 -1
- package/src/commands/agents.ts +54 -63
- package/src/commands/browse.ts +50 -14
- package/src/commands/diagnostics.ts +10 -0
- package/src/commands/doctor.ts +26 -7
- package/src/commands/governor.ts +23 -10
- package/src/commands/inbox.ts +52 -2
- package/src/commands/rm.ts +43 -4
- package/src/commands/servers.ts +16 -4
- package/src/commands/supervisor.ts +7 -2
- package/src/commands/toc.ts +14 -6
- package/src/commands/workflow.ts +30 -30
- package/src/core/agents/live-authority-v3.ts +17 -0
- package/src/core/agents/live-lifecycle-v3.ts +52 -14
- package/src/core/agents/mailbox.ts +26 -17
- package/src/core/agents/render/prompt-context.ts +10 -12
- package/src/core/agents/render/session-context.ts +9 -32
- package/src/core/agents/rules/stop-hook.ts +3 -3
- package/src/core/agents/state/heartbeat-writer.ts +29 -12
- package/src/core/agents/state/live-coordination-writer.ts +29 -0
- package/src/core/agents/state/pidmap.ts +13 -0
- package/src/core/agents/state/proc-start.ts +57 -0
- package/src/core/agents/state/stale-sweep.ts +247 -82
- package/src/core/agents/state/unchanged-file.ts +456 -0
- package/src/core/context/index.ts +446 -136
- package/src/core/context/observation.ts +29 -0
- package/src/core/diagnostics/bundle.ts +23 -18
- package/src/core/diagnostics/path-identity.ts +15 -0
- package/src/core/diagnostics/sanitize.ts +4 -3
- package/src/core/events/v3/authority-outbox.ts +56 -0
- package/src/core/events/v3/generation-custody.ts +59 -0
- package/src/core/events/v3/producers/coordination-recorder.ts +15 -0
- package/src/core/events/v3/producers/recorder.ts +94 -68
- package/src/core/governor/planning.ts +20 -8
- package/src/core/governor/service-read.ts +128 -20
- package/src/core/governor/service.ts +119 -42
- package/src/core/hooks/cli.ts +33 -5
- package/src/core/inbox/contract.ts +2 -1
- package/src/core/inbox/index.ts +2 -0
- package/src/core/inbox/peer-messages.ts +94 -0
- package/src/core/inbox/service.ts +88 -22
- package/src/core/inbox/targets.ts +148 -0
- package/src/core/inbox/watcher.ts +8 -9
- package/src/core/journal/index.ts +19 -2
- package/src/core/resources/contract.ts +4 -0
- package/src/core/resources/service-status.ts +35 -12
- package/src/core/resources/service.ts +71 -24
- package/src/core/resources/status.ts +57 -8
- package/src/core/servers/index.ts +177 -91
- package/src/core/servers/tunnels.ts +3 -11
- package/src/core/supervisor/contract.ts +13 -0
- package/src/core/supervisor/explanations.ts +4 -1
- package/src/core/supervisor/findings.ts +68 -10
- package/src/core/supervisor/service.ts +88 -26
- package/src/core/supervisor/services.ts +26 -8
- package/src/core/supervisor/status.ts +89 -12
- package/src/core/supervisor/timeline.ts +2 -1
- package/src/core/work/state.ts +7 -7
- package/src/core/workflow/engine.ts +131 -49
- package/src/core/workflow/failure-evidence.ts +141 -0
- package/src/core/workflow/index.ts +0 -1
- package/src/core/workflow/live-session-v3.ts +146 -5
- package/src/core/workflow/proof.ts +134 -26
- package/src/core/workflow/run-state.ts +31 -29
- package/src/core/workflow/spawn-claude.ts +18 -0
- package/src/core/workflow/spawn-codex.ts +13 -0
- package/src/core/workflow/spawn-cursor.ts +18 -0
- package/src/core/workflow/spawn-opencode.ts +14 -0
- package/src/core/workflow/types.ts +51 -17
- package/src/core/workflow/workspaces/index.ts +0 -1
- package/src/core/workflow/workspaces/leases.ts +48 -17
- package/src/core/workflow/workspaces/local-git.ts +315 -5
- package/src/core/workflow/workspaces/ownership.ts +232 -0
- package/src/core/workflow/workspaces/reclaim.ts +58 -0
- package/src/core/workflow/workspaces/recovery.ts +25 -13
- package/src/core/workflow/workspaces/state.ts +20 -0
- package/src/lib/browser/windows-chrome.ts +346 -86
- package/src/lib/docs-links.ts +49 -2
- package/src/lib/guarded-remove.ts +132 -14
- package/src/lib/identities/assume.ts +4 -0
- package/src/lib/zip/index.ts +483 -0
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
3
|
-
import { join } from "node:path";
|
|
3
|
+
import { isAbsolute, join } from "node:path";
|
|
4
|
+
import { observationsComparable } from "../context/observation.js";
|
|
4
5
|
import { DIAGNOSTIC_ADVICE_SCHEMA_VERSION } from "../diagnostics/contract.js";
|
|
5
6
|
import { policyDigest } from "../policy/index.js";
|
|
6
7
|
import { isCanonicalWorkflowAttemptContext } from "./attempt-context.js";
|
|
7
8
|
import { stableDigest, writeImmutableJson } from "./durable-record.js";
|
|
9
|
+
import { failureEvidenceAllowsUncharged, isSpawnFailureEvidence } from "./failure-evidence.js";
|
|
8
10
|
import { readWorkflowRunManifest } from "./run-state.js";
|
|
9
11
|
import { EVIDENCE_KINDS, WORKFLOW_DIAGNOSTIC_ADMISSION_SCHEMA_VERSION, WORKFLOW_PROOF_SCHEMA_VERSION, } from "./types.js";
|
|
10
12
|
import { isCanonicalWorkflowWorkContext } from "./work-context.js";
|
|
@@ -125,15 +127,18 @@ export function digestResult(value, kind) {
|
|
|
125
127
|
* attempt was informative about the work — charge it. This also keeps a
|
|
126
128
|
* resumed run whose earlier segment did real work from being written off by
|
|
127
129
|
* a later environment failure.
|
|
128
|
-
* 3.
|
|
129
|
-
*
|
|
130
|
-
* hard stop.
|
|
130
|
+
* 3. Every participant must have failed with positive no-effect evidence,
|
|
131
|
+
* observed absent output and no refusal. A class alone is insufficient.
|
|
132
|
+
* Among those eligible failures, environment is the hard stop.
|
|
131
133
|
* 4. Anything else is undefined ⇒ a charged work failure, exactly as today.
|
|
132
134
|
*/
|
|
133
135
|
export function deriveRunFailureClass(status, agents) {
|
|
134
136
|
if (status !== "failed")
|
|
135
137
|
return undefined;
|
|
136
|
-
if (agents.
|
|
138
|
+
if (agents.length === 0 ||
|
|
139
|
+
agents.some((agent) => agent.status !== "failed" ||
|
|
140
|
+
(agent.class !== "environment" && agent.class !== "upstream") ||
|
|
141
|
+
!failureEvidenceAllowsUncharged(agent.failure_evidence))) {
|
|
137
142
|
return undefined;
|
|
138
143
|
}
|
|
139
144
|
const failed = agents.filter((agent) => agent.status === "failed");
|
|
@@ -262,6 +267,10 @@ export function readWorkflowProof(coordRoot, runId) {
|
|
|
262
267
|
proof.run.class !== "environment" &&
|
|
263
268
|
proof.run.class !== "upstream" &&
|
|
264
269
|
proof.run.class !== "decision") ||
|
|
270
|
+
!Array.isArray(proof.agents) ||
|
|
271
|
+
proof.agents.some((agent) => !agent ||
|
|
272
|
+
typeof agent !== "object" ||
|
|
273
|
+
(agent.failure_evidence !== undefined && !isSpawnFailureEvidence(agent.failure_evidence))) ||
|
|
265
274
|
(proof.run.work_context !== undefined &&
|
|
266
275
|
(!proof.run.work_item_id ||
|
|
267
276
|
proof.run.work_context.id !== proof.run.work_item_id ||
|
|
@@ -275,6 +284,20 @@ export function readWorkflowProof(coordRoot, runId) {
|
|
|
275
284
|
!isWorkspaceExecutionEvidence(proof.execution, runId, proof.run.status))) {
|
|
276
285
|
throw new Error(`workflow proof at ${path} has an unsupported or mismatched schema`);
|
|
277
286
|
}
|
|
287
|
+
if (repoComparisonProblem(proof.repository.before, proof.repository.after) !== undefined ||
|
|
288
|
+
proof.repository.before.dirty_paths_truncated ||
|
|
289
|
+
proof.repository.after.dirty_paths_truncated ||
|
|
290
|
+
!proof.repository.before.branch?.trim() ||
|
|
291
|
+
!proof.repository.after.branch?.trim() ||
|
|
292
|
+
!proof.repository.before.head?.trim() ||
|
|
293
|
+
!proof.repository.after.head?.trim()) {
|
|
294
|
+
proof.repository = buildRepoEvidence(proof.repository.before, proof.repository.after);
|
|
295
|
+
proof.unknowns = proof.unknowns.filter((item) => item.code !== "repository_drift_incomplete");
|
|
296
|
+
proof.unknowns.push({
|
|
297
|
+
code: "repository_drift_incomplete",
|
|
298
|
+
message: proof.repository.drift.note,
|
|
299
|
+
});
|
|
300
|
+
}
|
|
278
301
|
const manifestPath = join(coordRoot, ".harnery", "workflows", runId, "run.json");
|
|
279
302
|
if (proof.execution === undefined && !existsSync(manifestPath))
|
|
280
303
|
return proof;
|
|
@@ -369,11 +392,27 @@ export function renderWorkflowProof(proof) {
|
|
|
369
392
|
`${observation.advice.assessment.recommended_action}; action none`
|
|
370
393
|
: "admission: shadow not needed; no real child dispatch");
|
|
371
394
|
}
|
|
372
|
-
const
|
|
395
|
+
const comparable = repoComparisonProblem(proof.repository.before, proof.repository.after) === undefined;
|
|
396
|
+
const repo = comparable &&
|
|
397
|
+
!proof.repository.before.dirty_paths_truncated &&
|
|
398
|
+
!proof.repository.after.dirty_paths_truncated &&
|
|
399
|
+
proof.repository.before.branch?.trim() &&
|
|
400
|
+
proof.repository.after.branch?.trim() &&
|
|
401
|
+
proof.repository.before.head?.trim() &&
|
|
402
|
+
proof.repository.after.head?.trim()
|
|
403
|
+
? proof.repository
|
|
404
|
+
: buildRepoEvidence(proof.repository.before, proof.repository.after);
|
|
373
405
|
const drift = repo.drift;
|
|
374
|
-
|
|
375
|
-
`
|
|
376
|
-
|
|
406
|
+
if (!comparable) {
|
|
407
|
+
lines.push(`repository: drift unknown. ${drift.note}`);
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
const paths = drift.dirty_paths_added === null || drift.dirty_paths_cleared === null
|
|
411
|
+
? "dirty-path drift unknown"
|
|
412
|
+
: `${drift.dirty_paths_added.length} dirty added, ${drift.dirty_paths_cleared.length} cleared`;
|
|
413
|
+
lines.push(`repository: branch ${repo.before.branch ?? "unknown"} -> ${repo.after.branch ?? "unknown"}; ` +
|
|
414
|
+
`HEAD ${short(repo.before.head)} -> ${short(repo.after.head)}; ${paths}`);
|
|
415
|
+
}
|
|
377
416
|
if (proof.unknowns.length > 0) {
|
|
378
417
|
lines.push(`unknowns: ${proof.unknowns.length}`);
|
|
379
418
|
for (const unknown of proof.unknowns)
|
|
@@ -385,37 +424,93 @@ export function renderWorkflowProof(proof) {
|
|
|
385
424
|
function buildRepoEvidence(beforeRaw, afterRaw) {
|
|
386
425
|
const before = normalizeRepoSnapshot(beforeRaw);
|
|
387
426
|
const after = normalizeRepoSnapshot(afterRaw);
|
|
427
|
+
const problem = repoComparisonProblem(before, after);
|
|
428
|
+
if (problem !== undefined) {
|
|
429
|
+
return {
|
|
430
|
+
source: "engine",
|
|
431
|
+
before,
|
|
432
|
+
after,
|
|
433
|
+
drift: {
|
|
434
|
+
branch_changed: null,
|
|
435
|
+
head_changed: null,
|
|
436
|
+
dirty_paths_added: null,
|
|
437
|
+
dirty_paths_cleared: null,
|
|
438
|
+
dirty_paths_retained: null,
|
|
439
|
+
incomplete: true,
|
|
440
|
+
note: `Repository drift could not be verified: ${problem}. Re-check before editing.`,
|
|
441
|
+
},
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
const pathsComparable = !before.dirty_paths_truncated && !after.dirty_paths_truncated;
|
|
445
|
+
const branchKnown = Boolean(before.branch && after.branch);
|
|
446
|
+
const headKnown = Boolean(before.head && after.head);
|
|
388
447
|
const beforeDirty = new Set(before.dirty_paths);
|
|
389
448
|
const afterDirty = new Set(after.dirty_paths);
|
|
390
449
|
const retained = after.dirty_paths.filter((path) => beforeDirty.has(path));
|
|
391
|
-
const
|
|
450
|
+
const notes = [
|
|
451
|
+
...(!pathsComparable
|
|
452
|
+
? ["Dirty-path drift is unknown because a snapshot list was truncated."]
|
|
453
|
+
: []),
|
|
454
|
+
...(!branchKnown ? ["Branch drift is unknown because a snapshot has no branch."] : []),
|
|
455
|
+
...(!headKnown ? ["HEAD drift is unknown because a snapshot has no HEAD."] : []),
|
|
456
|
+
...(pathsComparable && retained.length > 0
|
|
457
|
+
? ["Snapshots cannot prove whether retained dirty paths changed during the run."]
|
|
458
|
+
: []),
|
|
459
|
+
];
|
|
460
|
+
const incomplete = notes.length > 0;
|
|
392
461
|
return {
|
|
393
462
|
source: "engine",
|
|
394
463
|
before,
|
|
395
464
|
after,
|
|
396
465
|
drift: {
|
|
397
|
-
branch_changed: before.branch !== after.branch,
|
|
398
|
-
head_changed: before.head !== after.head,
|
|
399
|
-
dirty_paths_added:
|
|
400
|
-
|
|
401
|
-
|
|
466
|
+
branch_changed: branchKnown ? before.branch !== after.branch : null,
|
|
467
|
+
head_changed: headKnown ? before.head !== after.head : null,
|
|
468
|
+
dirty_paths_added: pathsComparable
|
|
469
|
+
? after.dirty_paths.filter((path) => !beforeDirty.has(path))
|
|
470
|
+
: null,
|
|
471
|
+
dirty_paths_cleared: pathsComparable
|
|
472
|
+
? before.dirty_paths.filter((path) => !afterDirty.has(path))
|
|
473
|
+
: null,
|
|
474
|
+
dirty_paths_retained: pathsComparable ? retained : null,
|
|
402
475
|
incomplete,
|
|
403
|
-
note: incomplete
|
|
404
|
-
? "Snapshots cannot prove whether retained dirty paths changed during the run, and truncated lists may omit paths."
|
|
405
|
-
: undefined,
|
|
476
|
+
note: incomplete ? notes.join(" ") : undefined,
|
|
406
477
|
},
|
|
407
478
|
};
|
|
408
479
|
}
|
|
409
480
|
function normalizeRepoSnapshot(snapshot) {
|
|
410
481
|
return {
|
|
411
482
|
cwd: snapshot.cwd,
|
|
483
|
+
observation: snapshot.observation ?? "unavailable",
|
|
484
|
+
observation_detail: snapshot.observation
|
|
485
|
+
? clippedOptional(snapshot.observation_detail, 500)
|
|
486
|
+
: "No repository observation was recorded.",
|
|
412
487
|
root: snapshot.root,
|
|
413
|
-
branch: snapshot.branch,
|
|
414
|
-
head: snapshot.head,
|
|
488
|
+
branch: snapshot.branch?.trim() || undefined,
|
|
489
|
+
head: snapshot.head?.trim() || undefined,
|
|
415
490
|
dirty_paths: snapshot.dirty_paths,
|
|
416
491
|
dirty_paths_truncated: snapshot.dirty_paths_truncated,
|
|
417
492
|
};
|
|
418
493
|
}
|
|
494
|
+
function snapshotFailure(label, snapshot) {
|
|
495
|
+
return `${label} snapshot is ${snapshot.observation}${snapshot.observation_detail ? ` (${snapshot.observation_detail})` : ""}`;
|
|
496
|
+
}
|
|
497
|
+
function repoComparisonProblem(before, after) {
|
|
498
|
+
if (!observationsComparable(before, after)) {
|
|
499
|
+
return [
|
|
500
|
+
...(before.observation !== "ok" ? [snapshotFailure("before", before)] : []),
|
|
501
|
+
...(after.observation !== "ok" ? [snapshotFailure("after", after)] : []),
|
|
502
|
+
].join("; ");
|
|
503
|
+
}
|
|
504
|
+
const unknownRoots = [
|
|
505
|
+
...(!before.root || !isAbsolute(before.root) ? ["before"] : []),
|
|
506
|
+
...(!after.root || !isAbsolute(after.root) ? ["after"] : []),
|
|
507
|
+
];
|
|
508
|
+
if (unknownRoots.length > 0)
|
|
509
|
+
return `${unknownRoots.join(" and ")} snapshot repository root is unknown`;
|
|
510
|
+
if (before.root !== after.root)
|
|
511
|
+
return "snapshots name different repository roots";
|
|
512
|
+
return undefined;
|
|
513
|
+
}
|
|
419
514
|
function buildAdapterCoverage(agents, claims, attestations) {
|
|
420
515
|
return [...new Set(agents.map((agent) => agent.adapter))].map((adapter) => {
|
|
421
516
|
const adapterAgents = agents.filter((agent) => agent.adapter === adapter);
|
|
@@ -47,6 +47,8 @@ export declare function assertWorkflowRunResumable(coordRoot: string, runId: str
|
|
|
47
47
|
};
|
|
48
48
|
export declare function assertWorkflowScriptUnchanged(manifest: WorkflowRunManifest): void;
|
|
49
49
|
export declare function workflowRunManifestPath(coordRoot: string, runId: string): string;
|
|
50
|
-
/** Acquire an exclusive
|
|
50
|
+
/** Acquire an exclusive lease shared by resume and reclaim. Existing locks
|
|
51
|
+
* always refuse: a stale observation cannot authorize unlinking a new owner.
|
|
52
|
+
* Crash recovery requires an audited ownership check outside this operation. */
|
|
51
53
|
export declare function acquireWorkflowResumeLease(coordRoot: string, runId: string): () => void;
|
|
52
54
|
//# sourceMappingURL=run-state.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-state.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/run-state.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EACL,KAAK,gBAAgB,EAErB,KAAK,eAAe,EACpB,KAAK,mBAAmB,EAEzB,MAAM,oBAAoB,CAAC;AAM5B,OAAO,KAAK,EACV,sBAAsB,EACtB,iCAAiC,EACjC,yBAAyB,EACzB,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAGpB,OAAO,KAAK,EAEV,gBAAgB,EAChB,uCAAuC,EACxC,MAAM,uBAAuB,CAAC;AAO/B,eAAO,MAAM,oCAAoC,EAAG,CAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"run-state.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/run-state.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EACL,KAAK,gBAAgB,EAErB,KAAK,eAAe,EACpB,KAAK,mBAAmB,EAEzB,MAAM,oBAAoB,CAAC;AAM5B,OAAO,KAAK,EACV,sBAAsB,EACtB,iCAAiC,EACjC,yBAAyB,EACzB,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAGpB,OAAO,KAAK,EAEV,gBAAgB,EAChB,uCAAuC,EACxC,MAAM,uBAAuB,CAAC;AAO/B,eAAO,MAAM,oCAAoC,EAAG,CAAU,CAAC;AAI/D,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,OAAO,oCAAoC,CAAC;IAC5D,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,mBAAmB,CAAC;IACnC,eAAe,CAAC,EAAE,sBAAsB,CAAC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,iBAAiB,EAAE,YAAY,CAAC;IAChC,SAAS,EAAE;QACT,GAAG,EAAE,MAAM,CAAC;QACZ,eAAe,EAAE,MAAM,CAAC;QACxB,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;QACpB,oBAAoB,CAAC,EAAE,iCAAiC,CAAC;QACzD,iBAAiB,EAAE,OAAO,CAAC;QAC3B,iBAAiB,EAAE,OAAO,CAAC;QAC3B,aAAa,EAAE,MAAM,GAAG,MAAM,CAAC;QAC/B,kBAAkB,EAAE,MAAM,CAAC;QAC3B,SAAS,EAAE,eAAe,CAAC;QAC3B,cAAc,EAAE,mBAAmB,CAAC;QACpC,MAAM,CAAC,EAAE,gBAAgB,CAAC;QAC1B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,yBAAyB,CAAC,CAAC;QACxD,iBAAiB,CAAC,EAAE,gBAAgB,CAAC;QACrC,kBAAkB,CAAC,EAAE,uCAAuC,CAAC;KAC9D,CAAC;CACH;AAED,MAAM,WAAW,6BAA6B;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,mBAAmB,CAAC;CAC/B;AAED,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,6BAA6B,GAAG,MAAM,CAgBrF;AAED,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,mBAAmB,CAgD7F;AAED,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ;IAAE,QAAQ,EAAE,mBAAmB,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CA4BvD;AAED,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CASjF;AAED,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGhF;AAED;;gFAEgF;AAChF,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,IAAI,CA2CvF"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import { chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, statSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
3
3
|
import { hostname } from "node:os";
|
|
4
4
|
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
@@ -13,7 +13,6 @@ import { isCanonicalWorkflowWorkContext } from "./work-context.js";
|
|
|
13
13
|
import { assertWorkspaceManifestAuthority, isWorkspaceBinding, isWorkspaceCompatibilityExecutionEvidence, } from "./workspaces/validate.js";
|
|
14
14
|
export const WORKFLOW_RUN_MANIFEST_SCHEMA_VERSION = 2;
|
|
15
15
|
const MANIFEST_LIMIT_BYTES = 256 * 1024;
|
|
16
|
-
const FOREIGN_LEASE_STALE_MS = 24 * 60 * 60 * 1_000;
|
|
17
16
|
export function workflowScriptDigest(scriptPath) {
|
|
18
17
|
return createHash("sha256").update(readFileSync(scriptPath)).digest("hex");
|
|
19
18
|
}
|
|
@@ -70,6 +69,13 @@ export function readWorkflowRunManifest(coordRoot, runId) {
|
|
|
70
69
|
!validExecution(manifest.execution)) {
|
|
71
70
|
throw new Error(`workflow run manifest at ${path} has an unsupported or mismatched schema`);
|
|
72
71
|
}
|
|
72
|
+
if (manifest.repository_before.observation === undefined) {
|
|
73
|
+
manifest.repository_before = {
|
|
74
|
+
...manifest.repository_before,
|
|
75
|
+
observation: "unavailable",
|
|
76
|
+
observation_detail: "No repository observation was recorded.",
|
|
77
|
+
};
|
|
78
|
+
}
|
|
73
79
|
assertWorkspaceManifestAuthority(manifest, readManifestWorkspaceRequest(coordRoot, runId));
|
|
74
80
|
return manifest;
|
|
75
81
|
}
|
|
@@ -121,17 +127,25 @@ export function workflowRunManifestPath(coordRoot, runId) {
|
|
|
121
127
|
assertWorkflowRunId(runId);
|
|
122
128
|
return join(coordRoot, ".harnery", "workflows", runId, "run.json");
|
|
123
129
|
}
|
|
124
|
-
/** Acquire an exclusive
|
|
130
|
+
/** Acquire an exclusive lease shared by resume and reclaim. Existing locks
|
|
131
|
+
* always refuse: a stale observation cannot authorize unlinking a new owner.
|
|
132
|
+
* Crash recovery requires an audited ownership check outside this operation. */
|
|
125
133
|
export function acquireWorkflowResumeLease(coordRoot, runId) {
|
|
126
134
|
assertWorkflowRunId(runId);
|
|
127
135
|
const path = join(coordRoot, ".harnery", "workflows", runId, "resume.lock");
|
|
128
|
-
const owner = {
|
|
136
|
+
const owner = {
|
|
137
|
+
owner_id: randomUUID(),
|
|
138
|
+
pid: process.pid,
|
|
139
|
+
host: hostname(),
|
|
140
|
+
created_at: new Date().toISOString(),
|
|
141
|
+
};
|
|
142
|
+
const ownerBytes = `${JSON.stringify(owner)}\n`;
|
|
129
143
|
mkdirSync(dirname(path), { recursive: true });
|
|
130
144
|
const acquire = () => {
|
|
131
145
|
try {
|
|
132
146
|
const fd = openSync(path, "wx", stateFileMode());
|
|
133
147
|
try {
|
|
134
|
-
writeFileSync(fd,
|
|
148
|
+
writeFileSync(fd, ownerBytes, "utf8");
|
|
135
149
|
}
|
|
136
150
|
finally {
|
|
137
151
|
closeSync(fd);
|
|
@@ -147,21 +161,18 @@ export function acquireWorkflowResumeLease(coordRoot, runId) {
|
|
|
147
161
|
};
|
|
148
162
|
if (!acquire()) {
|
|
149
163
|
const existing = readResumeLease(path);
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
if (!acquire())
|
|
155
|
-
throw new Error(`workflow run ${runId} resume lease raced with another process`);
|
|
164
|
+
const holder = existing
|
|
165
|
+
? ` by pid ${existing.pid} on ${existing.host}; its liveness has not been verified`
|
|
166
|
+
: "; its owner could not be verified";
|
|
167
|
+
throw new Error(`workflow run ${runId} resume lease is already held${holder}`);
|
|
156
168
|
}
|
|
157
169
|
return () => {
|
|
158
170
|
try {
|
|
159
|
-
|
|
160
|
-
if (existing?.pid === owner.pid && existing.host === owner.host)
|
|
171
|
+
if (readFileSync(path, "utf8") === ownerBytes)
|
|
161
172
|
unlinkSync(path);
|
|
162
173
|
}
|
|
163
174
|
catch {
|
|
164
|
-
//
|
|
175
|
+
// Leave an unreadable or replaced lock untouched.
|
|
165
176
|
}
|
|
166
177
|
};
|
|
167
178
|
}
|
|
@@ -180,19 +191,6 @@ function readResumeLease(path) {
|
|
|
180
191
|
return null;
|
|
181
192
|
}
|
|
182
193
|
}
|
|
183
|
-
function resumeLeaseIsLive(lease) {
|
|
184
|
-
if (lease.host !== hostname()) {
|
|
185
|
-
const age = Date.now() - Date.parse(lease.created_at);
|
|
186
|
-
return Number.isFinite(age) && age < FOREIGN_LEASE_STALE_MS;
|
|
187
|
-
}
|
|
188
|
-
try {
|
|
189
|
-
process.kill(lease.pid, 0);
|
|
190
|
-
return true;
|
|
191
|
-
}
|
|
192
|
-
catch (error) {
|
|
193
|
-
return error.code === "EPERM";
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
194
|
function validExecution(value) {
|
|
197
195
|
if (!value || typeof value !== "object")
|
|
198
196
|
return false;
|
|
@@ -259,6 +257,12 @@ function validFrozenPolicy(policy, cwd) {
|
|
|
259
257
|
function validRepoSnapshot(value) {
|
|
260
258
|
return (Boolean(value) &&
|
|
261
259
|
typeof value === "object" &&
|
|
260
|
+
(value.observation === undefined ||
|
|
261
|
+
value.observation === "ok" ||
|
|
262
|
+
value.observation === "partial" ||
|
|
263
|
+
value.observation === "unavailable") &&
|
|
264
|
+
(value.observation_detail === undefined ||
|
|
265
|
+
(typeof value.observation_detail === "string" && value.observation_detail.length <= 500)) &&
|
|
262
266
|
isAbsolute(value.cwd) &&
|
|
263
267
|
(value.root === undefined || isAbsolute(value.root)) &&
|
|
264
268
|
(value.branch === undefined || typeof value.branch === "string") &&
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spawn-claude.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/spawn-claude.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"spawn-claude.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/spawn-claude.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAMhF,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAYrE,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,YAAY,GAAG,iBAAiB,CA0B1E;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,gBAAgB,GAAG,WAAW,CAexE;AA+ED,eAAO,MAAM,iBAAiB,EAAE,OAiB/B,CAAC"}
|
|
@@ -21,6 +21,7 @@ import { exec } from "../../lib/exec.js";
|
|
|
21
21
|
import { builtinAdapterProfile, validateAdapterEffort } from "../adapters/profiles.js";
|
|
22
22
|
import { notFoundError } from "./adapters.js";
|
|
23
23
|
import { buildChildEnv } from "./child-env.js";
|
|
24
|
+
import { observeSpawnFailure } from "./failure-evidence.js";
|
|
24
25
|
import { resolveFullAccessArgv, resolveSandboxProjection } from "./sandbox-projection.js";
|
|
25
26
|
import { isUpstreamFailureText, vendorFailureText } from "./spawn-failure.js";
|
|
26
27
|
export function buildClaudeInvocation(req) {
|
|
@@ -50,6 +51,24 @@ export function buildClaudeInvocation(req) {
|
|
|
50
51
|
return { argv };
|
|
51
52
|
}
|
|
52
53
|
export function normalizeClaudeResult(raw) {
|
|
54
|
+
const result = normalizeClaudeTransportResult(raw);
|
|
55
|
+
if (result.ok)
|
|
56
|
+
return result;
|
|
57
|
+
let text = raw.stdout.trim();
|
|
58
|
+
let source = "stdout";
|
|
59
|
+
try {
|
|
60
|
+
const envelope = JSON.parse(raw.stdout);
|
|
61
|
+
if (typeof envelope.result === "string" && envelope.result.length > 0) {
|
|
62
|
+
text = envelope.result;
|
|
63
|
+
source = "vendor_envelope";
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// Retain unstructured output without treating it as native authority.
|
|
68
|
+
}
|
|
69
|
+
return { ...result, text, failureEvidence: observeSpawnFailure(raw, text, source) };
|
|
70
|
+
}
|
|
71
|
+
function normalizeClaudeTransportResult(raw) {
|
|
53
72
|
if (raw.timedOut) {
|
|
54
73
|
return {
|
|
55
74
|
ok: false,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spawn-codex.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/spawn-codex.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAQH,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"spawn-codex.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/spawn-codex.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAQH,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAMhF,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAErE,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,YAAY,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,iBAAiB,CA6C9F;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,gBAAgB,GAAG,WAAW,CAUvE;AA4CD,eAAO,MAAM,YAAY,EAAE,OA8B1B,CAAC"}
|
|
@@ -24,6 +24,7 @@ import { exec } from "../../lib/exec.js";
|
|
|
24
24
|
import { builtinAdapterProfile, validateAdapterEffort } from "../adapters/profiles.js";
|
|
25
25
|
import { notFoundError } from "./adapters.js";
|
|
26
26
|
import { buildChildEnv } from "./child-env.js";
|
|
27
|
+
import { observeSpawnFailure } from "./failure-evidence.js";
|
|
27
28
|
import { resolveFullAccessArgv, resolveSandboxProjection } from "./sandbox-projection.js";
|
|
28
29
|
import { isUpstreamFailureText, vendorFailureText } from "./spawn-failure.js";
|
|
29
30
|
export function buildCodexInvocation(req, resultFile) {
|
|
@@ -71,6 +72,18 @@ export function buildCodexInvocation(req, resultFile) {
|
|
|
71
72
|
return { argv, resultFile };
|
|
72
73
|
}
|
|
73
74
|
export function normalizeCodexResult(raw) {
|
|
75
|
+
const result = normalizeCodexTransportResult(raw);
|
|
76
|
+
if (result.ok)
|
|
77
|
+
return result;
|
|
78
|
+
const finalText = raw.resultFileText?.trim();
|
|
79
|
+
const text = finalText || raw.stdout.trim();
|
|
80
|
+
return {
|
|
81
|
+
...result,
|
|
82
|
+
text,
|
|
83
|
+
failureEvidence: observeSpawnFailure(raw, text, finalText ? "final_message" : "stdout"),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function normalizeCodexTransportResult(raw) {
|
|
74
87
|
if (raw.timedOut) {
|
|
75
88
|
return {
|
|
76
89
|
ok: false,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spawn-cursor.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/spawn-cursor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"spawn-cursor.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/spawn-cursor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAMhF,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AASrE,gEAAgE;AAChE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB,CAWA;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,YAAY,GAAG,iBAAiB,CAsB1E;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,gBAAgB,GAAG,WAAW,CAexE;AAyDD,eAAO,MAAM,aAAa,EAAE,OAkB3B,CAAC"}
|
|
@@ -22,6 +22,7 @@ import { exec } from "../../lib/exec.js";
|
|
|
22
22
|
import { builtinAdapterProfile, validateAdapterEffort } from "../adapters/profiles.js";
|
|
23
23
|
import { notFoundError } from "./adapters.js";
|
|
24
24
|
import { buildChildEnv } from "./child-env.js";
|
|
25
|
+
import { observeSpawnFailure } from "./failure-evidence.js";
|
|
25
26
|
import { resolveFullAccessArgv, resolveSandboxProjection } from "./sandbox-projection.js";
|
|
26
27
|
import { isUpstreamFailureText, vendorFailureText } from "./spawn-failure.js";
|
|
27
28
|
/** Exported for unit tests (no live binary to test against). */
|
|
@@ -60,6 +61,24 @@ export function buildCursorInvocation(req) {
|
|
|
60
61
|
return { argv };
|
|
61
62
|
}
|
|
62
63
|
export function normalizeCursorResult(raw) {
|
|
64
|
+
const result = normalizeCursorTransportResult(raw);
|
|
65
|
+
if (result.ok)
|
|
66
|
+
return result;
|
|
67
|
+
let text = raw.stdout;
|
|
68
|
+
let source = "stdout";
|
|
69
|
+
try {
|
|
70
|
+
const envelope = JSON.parse(raw.stdout);
|
|
71
|
+
if (typeof envelope.result === "string" && envelope.result.length > 0) {
|
|
72
|
+
text = envelope.result;
|
|
73
|
+
source = "vendor_envelope";
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// Raw output remains evidence, not an inferred refusal or effect result.
|
|
78
|
+
}
|
|
79
|
+
return { ...result, text, failureEvidence: observeSpawnFailure(raw, text, source) };
|
|
80
|
+
}
|
|
81
|
+
function normalizeCursorTransportResult(raw) {
|
|
63
82
|
if (raw.timedOut) {
|
|
64
83
|
return {
|
|
65
84
|
ok: false,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spawn-opencode.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/spawn-opencode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"spawn-opencode.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/spawn-opencode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAMhF,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAQrE;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG;IACnD,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAoBA;AAED,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,YAAY,GAAG,iBAAiB,CAoB5E;AAED,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,gBAAgB,GAAG,WAAW,CAW1E;AA+CD,eAAO,MAAM,eAAe,EAAE,OAkB7B,CAAC"}
|
|
@@ -20,6 +20,7 @@ import { exec } from "../../lib/exec.js";
|
|
|
20
20
|
import { builtinAdapterProfile, validateAdapterEffort } from "../adapters/profiles.js";
|
|
21
21
|
import { notFoundError } from "./adapters.js";
|
|
22
22
|
import { buildChildEnv } from "./child-env.js";
|
|
23
|
+
import { observeSpawnFailure } from "./failure-evidence.js";
|
|
23
24
|
import { resolveFullAccessArgv, resolveSandboxProjection } from "./sandbox-projection.js";
|
|
24
25
|
import { isUpstreamFailureText, vendorFailureText } from "./spawn-failure.js";
|
|
25
26
|
/**
|
|
@@ -72,6 +73,19 @@ export function buildOpenCodeInvocation(req) {
|
|
|
72
73
|
return { argv };
|
|
73
74
|
}
|
|
74
75
|
export function normalizeOpenCodeResult(raw) {
|
|
76
|
+
const result = normalizeOpenCodeTransportResult(raw);
|
|
77
|
+
if (result.ok)
|
|
78
|
+
return result;
|
|
79
|
+
const parsed = parseOpenCodeStream(raw.stdout);
|
|
80
|
+
const text = parsed.text || raw.stdout.trim();
|
|
81
|
+
return {
|
|
82
|
+
...result,
|
|
83
|
+
text,
|
|
84
|
+
sessionId: parsed.sessionId,
|
|
85
|
+
failureEvidence: observeSpawnFailure(raw, text, parsed.text ? "vendor_envelope" : "stdout"),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function normalizeOpenCodeTransportResult(raw) {
|
|
75
89
|
if (raw.timedOut) {
|
|
76
90
|
return {
|
|
77
91
|
ok: false,
|
|
@@ -103,17 +103,21 @@ export interface WorkflowAgentProof {
|
|
|
103
103
|
session_id?: string;
|
|
104
104
|
result?: ResultDigest;
|
|
105
105
|
error?: string;
|
|
106
|
-
/**
|
|
107
|
-
*
|
|
108
|
-
* Absent ⇒ a work failure. Recorded even when a script's parallel() swallows
|
|
109
|
-
* the rejection, so the run-level class can still be derived from proof. */
|
|
106
|
+
/** Failed-spawn transport class. It is retained even when a script swallows
|
|
107
|
+
* the rejection; charging additionally requires validated failure evidence. */
|
|
110
108
|
class?: SpawnFailureClass;
|
|
109
|
+
/** Failure observations carried from the adapter. Missing observations are
|
|
110
|
+
* unknown and cannot authorize an uncharged attempt. */
|
|
111
|
+
failure_evidence?: SpawnFailureEvidence;
|
|
111
112
|
/** Which vendor sandbox mode this agent launched with and why (ADR 0192).
|
|
112
113
|
* Absent on a cached agent, which launched nothing. */
|
|
113
114
|
access?: WorkerAccessDecision;
|
|
114
115
|
}
|
|
115
116
|
export interface WorkflowRepoSnapshot {
|
|
116
117
|
cwd: string;
|
|
118
|
+
observation: "ok" | "unavailable" | "partial";
|
|
119
|
+
/** Bounded explanation of an unavailable or incomplete observation. */
|
|
120
|
+
observation_detail?: string;
|
|
117
121
|
root?: string;
|
|
118
122
|
branch?: string;
|
|
119
123
|
head?: string;
|
|
@@ -125,12 +129,13 @@ export interface WorkflowRepoEvidence {
|
|
|
125
129
|
before: WorkflowRepoSnapshot;
|
|
126
130
|
after: WorkflowRepoSnapshot;
|
|
127
131
|
drift: {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
132
|
+
/** Null means the snapshots do not establish this comparison. */
|
|
133
|
+
branch_changed: boolean | null;
|
|
134
|
+
head_changed: boolean | null;
|
|
135
|
+
dirty_paths_added: string[] | null;
|
|
136
|
+
dirty_paths_cleared: string[] | null;
|
|
137
|
+
dirty_paths_retained: string[] | null;
|
|
138
|
+
/** True when an observation, comparison, or retained-path change is unknown. */
|
|
134
139
|
incomplete: boolean;
|
|
135
140
|
note?: string;
|
|
136
141
|
};
|
|
@@ -305,15 +310,16 @@ export interface WorkflowSpecialistProfile {
|
|
|
305
310
|
maxTurns?: number;
|
|
306
311
|
}
|
|
307
312
|
/**
|
|
308
|
-
*
|
|
309
|
-
*
|
|
310
|
-
* default and is charged against the attempt budget exactly as before.
|
|
313
|
+
* The positively identified transport cause of a failed spawn (ADR 0046).
|
|
314
|
+
* This class alone proves neither absence of effects nor retry eligibility.
|
|
311
315
|
*
|
|
312
316
|
* - `environment`: the run never started — a precondition was missing (the
|
|
313
|
-
* vendor binary was absent).
|
|
317
|
+
* vendor binary was absent). With positive no-effect/output evidence it is
|
|
318
|
+
* uncharged and not retried: retrying an unchanged
|
|
314
319
|
* environment cannot help, so the work item stops and names the precondition.
|
|
315
320
|
* - `upstream`: the vendor was reached and refused (5xx, 429, circuit open).
|
|
316
|
-
*
|
|
321
|
+
* Retry/free-attempt eligibility also requires positive no-effect evidence,
|
|
322
|
+
* observed absent output, and no refusal. Unsupported evidence stays unknown.
|
|
317
323
|
*/
|
|
318
324
|
export type SpawnFailureClass = "environment" | "upstream";
|
|
319
325
|
/**
|
|
@@ -333,6 +339,43 @@ export type SpawnFailureClass = "environment" | "upstream";
|
|
|
333
339
|
* waiting for is a person who was never told.
|
|
334
340
|
*/
|
|
335
341
|
export type RunFailureClass = SpawnFailureClass | "decision";
|
|
342
|
+
/** Positive claims name the observation that supports them. A transport error
|
|
343
|
+
* or an empty reply is never evidence that no effects occurred. */
|
|
344
|
+
export type SpawnEffectEvidence = {
|
|
345
|
+
state: "unknown";
|
|
346
|
+
} | {
|
|
347
|
+
state: "none";
|
|
348
|
+
source: "spawn_errno";
|
|
349
|
+
reference: "ENOENT";
|
|
350
|
+
} | {
|
|
351
|
+
state: "none" | "observed";
|
|
352
|
+
source: "adapter_evidence";
|
|
353
|
+
reference: string;
|
|
354
|
+
};
|
|
355
|
+
export type SpawnRefusalEvidence = {
|
|
356
|
+
state: "unknown";
|
|
357
|
+
} | {
|
|
358
|
+
state: "refused";
|
|
359
|
+
source: "vendor_envelope";
|
|
360
|
+
reference: string;
|
|
361
|
+
};
|
|
362
|
+
export type SpawnOutputSource = "final_message" | "vendor_envelope" | "stdout";
|
|
363
|
+
export type SpawnPartialOutputEvidence = {
|
|
364
|
+
state: "unknown";
|
|
365
|
+
} | {
|
|
366
|
+
state: "absent";
|
|
367
|
+
source: SpawnOutputSource;
|
|
368
|
+
} | {
|
|
369
|
+
state: "present";
|
|
370
|
+
source: SpawnOutputSource;
|
|
371
|
+
bytes: number;
|
|
372
|
+
sha256: string;
|
|
373
|
+
};
|
|
374
|
+
export interface SpawnFailureEvidence {
|
|
375
|
+
effect: SpawnEffectEvidence;
|
|
376
|
+
refusal: SpawnRefusalEvidence;
|
|
377
|
+
partial_output: SpawnPartialOutputEvidence;
|
|
378
|
+
}
|
|
336
379
|
/** What a spawn adapter returns for one subagent run. */
|
|
337
380
|
export interface SpawnResult {
|
|
338
381
|
ok: boolean;
|
|
@@ -344,9 +387,13 @@ export interface SpawnResult {
|
|
|
344
387
|
durationMs: number;
|
|
345
388
|
/** Populated when ok=false. */
|
|
346
389
|
error?: string;
|
|
347
|
-
/**
|
|
348
|
-
*
|
|
390
|
+
/** Transport classification when ok=false; never sufficient by itself to
|
|
391
|
+
* grant free work or automatic redispatch. */
|
|
349
392
|
class?: SpawnFailureClass;
|
|
393
|
+
/** Transport classification alone grants neither a retry nor free work.
|
|
394
|
+
* Absent evidence is explicitly unknown. Positive adapter_evidence claims
|
|
395
|
+
* require a documented producer observation, never an HTTP/text heuristic. */
|
|
396
|
+
failureEvidence?: SpawnFailureEvidence;
|
|
350
397
|
}
|
|
351
398
|
export interface SpawnRequest {
|
|
352
399
|
prompt: string;
|