harnery 0.45.0 → 0.45.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commander.js +0 -1
- package/dist/commands/files.d.ts +2 -2
- package/dist/commands/files.d.ts.map +1 -1
- package/dist/commands/files.js +4 -7
- package/dist/commands/servers.d.ts.map +1 -1
- package/dist/commands/servers.js +17 -11
- package/dist/commands/tunnel.d.ts +0 -15
- package/dist/commands/tunnel.d.ts.map +1 -1
- package/dist/commands/tunnel.js +153 -453
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +6 -45
- package/dist/core/agents/coord-client.d.ts.map +1 -1
- package/dist/core/agents/coord-client.js +0 -18
- package/dist/core/agents/live-authority-v3.d.ts +0 -1
- package/dist/core/agents/live-authority-v3.d.ts.map +1 -1
- package/dist/core/agents/live-authority-v3.js +0 -11
- package/dist/core/agents/live-lifecycle-v3.d.ts +2 -4
- package/dist/core/agents/live-lifecycle-v3.d.ts.map +1 -1
- package/dist/core/agents/live-lifecycle-v3.js +7 -28
- package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-writer.js +14 -25
- package/dist/core/agents/state/live-coordination-writer.d.ts.map +1 -1
- package/dist/core/agents/state/live-coordination-writer.js +0 -5
- package/dist/core/agents/state/pidmap.d.ts.map +1 -1
- package/dist/core/agents/state/pidmap.js +2 -7
- package/dist/core/agents/state/proc-start.d.ts +20 -31
- package/dist/core/agents/state/proc-start.d.ts.map +1 -1
- package/dist/core/agents/state/proc-start.js +37 -70
- package/dist/core/agents/state/service-process.d.ts +61 -0
- package/dist/core/agents/state/service-process.d.ts.map +1 -0
- package/dist/core/agents/state/service-process.js +143 -0
- package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
- package/dist/core/agents/state/stale-sweep.js +70 -180
- package/dist/core/artifacts/activity.d.ts +4 -1
- package/dist/core/artifacts/activity.d.ts.map +1 -1
- package/dist/core/artifacts/activity.js +8 -2
- package/dist/core/artifacts/delivery-card.d.ts.map +1 -1
- package/dist/core/artifacts/delivery-card.js +1 -2
- package/dist/core/artifacts/index.d.ts.map +1 -1
- package/dist/core/artifacts/index.js +38 -12
- package/dist/core/context/index.d.ts +6 -4
- package/dist/core/context/index.d.ts.map +1 -1
- package/dist/core/context/index.js +194 -256
- package/dist/core/context/observation.d.ts +9 -9
- package/dist/core/context/observation.d.ts.map +1 -1
- package/dist/core/context/observation.js +37 -6
- package/dist/core/events/v3/authority-outbox.d.ts.map +1 -1
- package/dist/core/events/v3/authority-outbox.js +0 -29
- package/dist/core/events/v3/producers/coordination-recorder.d.ts +0 -1
- package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/coordination-recorder.js +0 -7
- package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/recorder.js +64 -77
- package/dist/core/events/v3/runtime-build.d.ts +14 -0
- package/dist/core/events/v3/runtime-build.d.ts.map +1 -1
- package/dist/core/events/v3/runtime-build.js +44 -0
- package/dist/core/governor/index.d.ts +2 -1
- package/dist/core/governor/index.d.ts.map +1 -1
- package/dist/core/governor/index.js +2 -1
- package/dist/core/governor/service-read.d.ts +3 -10
- package/dist/core/governor/service-read.d.ts.map +1 -1
- package/dist/core/governor/service-read.js +10 -94
- package/dist/core/governor/service.d.ts +3 -7
- package/dist/core/governor/service.d.ts.map +1 -1
- package/dist/core/governor/service.js +17 -182
- package/dist/core/hooks/cli.js +78 -48
- package/dist/core/hooks/resolve/owner.js +2 -2
- package/dist/core/resources/sampler.js +2 -2
- package/dist/core/resources/service-status.d.ts +2 -14
- package/dist/core/resources/service-status.d.ts.map +1 -1
- package/dist/core/resources/service-status.js +3 -23
- package/dist/core/resources/service.d.ts +3 -7
- package/dist/core/resources/service.d.ts.map +1 -1
- package/dist/core/resources/service.js +12 -101
- package/dist/core/resources/status.d.ts.map +1 -1
- package/dist/core/resources/status.js +3 -2
- package/dist/core/semantic/service.d.ts.map +1 -1
- package/dist/core/semantic/service.js +9 -2
- package/dist/core/servers/index.d.ts +11 -10
- package/dist/core/servers/index.d.ts.map +1 -1
- package/dist/core/servers/index.js +82 -192
- 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 +2 -45
- package/dist/core/storage/durable-history.js +56 -22
- package/dist/core/storage/segments.d.ts.map +1 -1
- package/dist/core/storage/segments.js +73 -17
- package/dist/core/supervisor/service.d.ts +4 -7
- package/dist/core/supervisor/service.d.ts.map +1 -1
- package/dist/core/supervisor/service.js +23 -122
- package/dist/core/supervisor/services.js +2 -2
- package/dist/core/supervisor/status.d.ts +3 -17
- package/dist/core/supervisor/status.d.ts.map +1 -1
- package/dist/core/supervisor/status.js +7 -70
- package/dist/core/work/state.js +7 -7
- package/dist/core/workflow/engine.d.ts.map +1 -1
- package/dist/core/workflow/engine.js +23 -66
- package/dist/core/workflow/index.d.ts +3 -4
- package/dist/core/workflow/index.d.ts.map +1 -1
- package/dist/core/workflow/index.js +2 -2
- package/dist/core/workflow/live-session-v3.d.ts +0 -6
- package/dist/core/workflow/live-session-v3.d.ts.map +1 -1
- package/dist/core/workflow/live-session-v3.js +5 -126
- package/dist/core/workflow/proof.d.ts +4 -4
- package/dist/core/workflow/proof.d.ts.map +1 -1
- package/dist/core/workflow/proof.js +34 -86
- package/dist/core/workflow/resume-lock.d.ts +17 -0
- package/dist/core/workflow/resume-lock.d.ts.map +1 -0
- package/dist/core/workflow/resume-lock.js +158 -0
- package/dist/core/workflow/run-state.d.ts +0 -21
- package/dist/core/workflow/run-state.d.ts.map +1 -1
- package/dist/core/workflow/run-state.js +3 -174
- package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
- package/dist/core/workflow/spawn-claude.js +0 -19
- package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
- package/dist/core/workflow/spawn-codex.js +0 -13
- package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
- package/dist/core/workflow/spawn-cursor.js +0 -19
- package/dist/core/workflow/spawn-opencode.d.ts.map +1 -1
- package/dist/core/workflow/spawn-opencode.js +0 -14
- package/dist/core/workflow/types.d.ts +11 -54
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/leases.d.ts +4 -0
- package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/leases.js +1 -1
- package/dist/core/workflow/workspaces/local-git.js +91 -210
- package/dist/core/workflow/workspaces/ownership.d.ts +0 -7
- package/dist/core/workflow/workspaces/ownership.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/ownership.js +3 -130
- package/dist/core/workflow/workspaces/reclaim.d.ts +4 -7
- package/dist/core/workflow/workspaces/reclaim.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/reclaim.js +0 -4
- package/dist/core/workflow/workspaces/recovery.d.ts +1 -1
- package/dist/core/workflow/workspaces/recovery.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/recovery.js +1 -1
- package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/state.js +1 -4
- package/dist/lib/identities/assume.d.ts.map +1 -1
- package/dist/lib/identities/assume.js +0 -4
- package/dist/lib/tunnel/state.d.ts +6 -13
- package/dist/lib/tunnel/state.d.ts.map +1 -1
- package/dist/lib/tunnel/state.js +12 -58
- package/package.json +1 -6
- package/src/commander.ts +0 -10
- package/src/commands/files.ts +5 -13
- package/src/commands/servers.ts +23 -12
- package/src/commands/tunnel.ts +166 -533
- package/src/commands/workflow.ts +8 -58
- package/src/core/agents/coord-client.ts +0 -20
- package/src/core/agents/live-authority-v3.ts +0 -17
- package/src/core/agents/live-lifecycle-v3.ts +14 -52
- package/src/core/agents/state/heartbeat-writer.ts +12 -29
- package/src/core/agents/state/live-coordination-writer.ts +0 -29
- package/src/core/agents/state/pidmap.ts +2 -15
- package/src/core/agents/state/proc-start.ts +41 -69
- package/src/core/agents/state/service-process.ts +217 -0
- package/src/core/agents/state/stale-sweep.ts +82 -247
- package/src/core/artifacts/activity.ts +9 -2
- package/src/core/artifacts/delivery-card.ts +1 -7
- package/src/core/artifacts/index.ts +45 -12
- package/src/core/context/index.ts +207 -272
- package/src/core/context/observation.ts +40 -9
- package/src/core/events/v3/authority-outbox.ts +0 -56
- package/src/core/events/v3/producers/coordination-recorder.ts +0 -15
- package/src/core/events/v3/producers/recorder.ts +68 -94
- package/src/core/events/v3/runtime-build.ts +62 -0
- package/src/core/governor/index.ts +1 -2
- package/src/core/governor/service-read.ts +15 -114
- package/src/core/governor/service.ts +36 -220
- package/src/core/hooks/cli.ts +85 -47
- package/src/core/hooks/resolve/owner.ts +2 -2
- package/src/core/resources/sampler.ts +3 -2
- package/src/core/resources/service-status.ts +6 -35
- package/src/core/resources/service.ts +23 -133
- package/src/core/resources/status.ts +3 -2
- package/src/core/semantic/service.ts +8 -2
- package/src/core/servers/index.ts +86 -228
- package/src/core/servers/tunnels.ts +3 -55
- package/src/core/storage/durable-history.ts +52 -20
- package/src/core/storage/segments.ts +79 -17
- package/src/core/supervisor/service.ts +33 -155
- package/src/core/supervisor/services.ts +2 -2
- package/src/core/supervisor/status.ts +10 -85
- package/src/core/work/state.ts +7 -7
- package/src/core/workflow/engine.ts +22 -74
- package/src/core/workflow/index.ts +2 -13
- package/src/core/workflow/live-session-v3.ts +5 -146
- package/src/core/workflow/proof.ts +40 -104
- package/src/core/workflow/resume-lock.ts +179 -0
- package/src/core/workflow/run-state.ts +3 -207
- package/src/core/workflow/spawn-claude.ts +0 -18
- package/src/core/workflow/spawn-codex.ts +0 -13
- package/src/core/workflow/spawn-cursor.ts +0 -18
- package/src/core/workflow/spawn-opencode.ts +0 -14
- package/src/core/workflow/types.ts +11 -41
- package/src/core/workflow/workspaces/leases.ts +1 -1
- package/src/core/workflow/workspaces/local-git.ts +120 -239
- package/src/core/workflow/workspaces/ownership.ts +3 -158
- package/src/core/workflow/workspaces/reclaim.ts +3 -9
- package/src/core/workflow/workspaces/recovery.ts +1 -1
- package/src/core/workflow/workspaces/state.ts +1 -5
- package/src/lib/identities/assume.ts +0 -4
- package/src/lib/tunnel/state.ts +15 -70
- package/dist/commands/turn-rollback.d.ts +0 -5
- package/dist/commands/turn-rollback.d.ts.map +0 -1
- package/dist/commands/turn-rollback.js +0 -76
- package/dist/core/agents/state/process-handle.d.ts +0 -26
- package/dist/core/agents/state/process-handle.d.ts.map +0 -1
- package/dist/core/agents/state/process-handle.js +0 -121
- package/dist/core/agents/state/unchanged-file.d.ts +0 -44
- package/dist/core/agents/state/unchanged-file.d.ts.map +0 -1
- package/dist/core/agents/state/unchanged-file.js +0 -390
- package/dist/core/events/v3/generation-custody.d.ts +0 -5
- package/dist/core/events/v3/generation-custody.d.ts.map +0 -1
- package/dist/core/events/v3/generation-custody.js +0 -50
- package/dist/core/turn-rollback/content.d.ts +0 -53
- package/dist/core/turn-rollback/content.d.ts.map +0 -1
- package/dist/core/turn-rollback/content.js +0 -396
- package/dist/core/turn-rollback/index.d.ts +0 -72
- package/dist/core/turn-rollback/index.d.ts.map +0 -1
- package/dist/core/turn-rollback/index.js +0 -443
- package/dist/core/workflow/failure-evidence.d.ts +0 -16
- package/dist/core/workflow/failure-evidence.d.ts.map +0 -1
- package/dist/core/workflow/failure-evidence.js +0 -114
- package/dist/core/workflow/recovery-audit.d.ts +0 -30
- package/dist/core/workflow/recovery-audit.d.ts.map +0 -1
- package/dist/core/workflow/recovery-audit.js +0 -116
- package/src/commands/turn-rollback.ts +0 -111
- package/src/core/agents/state/process-handle.ts +0 -165
- package/src/core/agents/state/unchanged-file.ts +0 -456
- package/src/core/events/v3/generation-custody.ts +0 -59
- package/src/core/turn-rollback/content.ts +0 -499
- package/src/core/turn-rollback/index.ts +0 -580
- package/src/core/workflow/failure-evidence.ts +0 -141
- package/src/core/workflow/recovery-audit.ts +0 -181
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resume-lock.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/resume-lock.ts"],"names":[],"mappings":"AAqBA,MAAM,MAAM,wBAAwB,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;AAEnE,MAAM,WAAW,uBAAuB;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED;;+DAE+D;AAC/D,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,IAAI,CAsDvF;AAED,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAG/E;AAED;oFACoF;AACpF,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,uBAAuB,GAAG,wBAAwB,CAUjG;AAkCD,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ,uBAAuB,GAAG,IAAI,CAEhC"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// Kept apart from run-state.ts: the resume lock reads process identity, and
|
|
2
|
+
// run-state.ts is also reached from read-only dashboard modules.
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
import { chmodSync, closeSync, linkSync, lstatSync, mkdirSync, openSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
5
|
+
import { hostname } from "node:os";
|
|
6
|
+
import { dirname, join } from "node:path";
|
|
7
|
+
import { processStartToken, verifyProcessGeneration } from "../agents/state/proc-start.js";
|
|
8
|
+
import { stateFileMode } from "../storage/modes.js";
|
|
9
|
+
import { assertWorkflowRunId } from "./approvals.js";
|
|
10
|
+
/** Acquire the exclusive lease shared by resume and reclaim. A lock whose
|
|
11
|
+
* same-host owner has exited, or whose PID now names a different process, is
|
|
12
|
+
* retired automatically. Every other existing lock refuses. */
|
|
13
|
+
export function acquireWorkflowResumeLease(coordRoot, runId) {
|
|
14
|
+
const path = workflowResumeLockPath(coordRoot, runId);
|
|
15
|
+
const owner = {
|
|
16
|
+
owner_id: randomUUID(),
|
|
17
|
+
pid: process.pid,
|
|
18
|
+
host: hostname(),
|
|
19
|
+
created_at: new Date().toISOString(),
|
|
20
|
+
start_token: processStartToken(process.pid),
|
|
21
|
+
};
|
|
22
|
+
const ownerBytes = `${JSON.stringify(owner)}\n`;
|
|
23
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
24
|
+
const acquire = () => {
|
|
25
|
+
try {
|
|
26
|
+
const fd = openSync(path, "wx", stateFileMode());
|
|
27
|
+
try {
|
|
28
|
+
writeFileSync(fd, ownerBytes, "utf8");
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
closeSync(fd);
|
|
32
|
+
}
|
|
33
|
+
chmodSync(path, stateFileMode());
|
|
34
|
+
const identity = lstatSync(path);
|
|
35
|
+
return { device: identity.dev, inode: identity.ino };
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
if (error.code !== "EEXIST")
|
|
39
|
+
throw error;
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
const acquired = acquire() || (retireDeadResumeLock(path) && acquire());
|
|
44
|
+
if (!acquired) {
|
|
45
|
+
const existing = readWorkflowResumeLock(coordRoot, runId);
|
|
46
|
+
const holder = existing
|
|
47
|
+
? ` by pid ${existing.pid} on ${existing.host} (owner ${workflowResumeOwnerState(existing)})`
|
|
48
|
+
: "; its owner could not be read";
|
|
49
|
+
throw new Error(`workflow run ${runId} resume lease is already held${holder}`);
|
|
50
|
+
}
|
|
51
|
+
return () => {
|
|
52
|
+
try {
|
|
53
|
+
// Pin identity and bytes, so a stale closure never removes a replacement.
|
|
54
|
+
const identity = lstatSync(path);
|
|
55
|
+
if (identity.isFile() &&
|
|
56
|
+
identity.dev === acquired.device &&
|
|
57
|
+
identity.ino === acquired.inode &&
|
|
58
|
+
readFileSync(path, "utf8") === ownerBytes) {
|
|
59
|
+
unlinkSync(path);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// Leave an unreadable or replaced lock untouched.
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
export function workflowResumeLockPath(coordRoot, runId) {
|
|
68
|
+
assertWorkflowRunId(runId);
|
|
69
|
+
return join(coordRoot, ".harnery", "workflows", runId, "resume.lock");
|
|
70
|
+
}
|
|
71
|
+
/** Only a same-host owner can be proven gone: by ESRCH, or by a recorded start
|
|
72
|
+
* token that no longer matches its PID. Foreign and unreadable owners stay live. */
|
|
73
|
+
export function workflowResumeOwnerState(owner) {
|
|
74
|
+
if (owner.host !== hostname())
|
|
75
|
+
return "unknown";
|
|
76
|
+
try {
|
|
77
|
+
process.kill(owner.pid, 0);
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
const code = error.code;
|
|
81
|
+
if (code === "ESRCH")
|
|
82
|
+
return "dead";
|
|
83
|
+
if (code !== "EPERM")
|
|
84
|
+
return "unknown";
|
|
85
|
+
}
|
|
86
|
+
return verifyProcessGeneration(owner.pid, owner.start_token) === "different" ? "dead" : "live";
|
|
87
|
+
}
|
|
88
|
+
/** Move a dead owner's exact lock aside before deleting it. If a contender
|
|
89
|
+
* replaced it in between, the moved bytes (a fresh owner_id) differ, and that
|
|
90
|
+
* live lock is linked back. */
|
|
91
|
+
function retireDeadResumeLock(path) {
|
|
92
|
+
let bytes;
|
|
93
|
+
try {
|
|
94
|
+
bytes = readFileSync(path, "utf8");
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
return error.code === "ENOENT";
|
|
98
|
+
}
|
|
99
|
+
const owner = parseResumeLock(bytes);
|
|
100
|
+
if (!owner || workflowResumeOwnerState(owner) !== "dead")
|
|
101
|
+
return false;
|
|
102
|
+
const aside = `${path}.dead-${randomUUID()}`;
|
|
103
|
+
try {
|
|
104
|
+
renameSync(path, aside);
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
if (error.code === "ENOENT")
|
|
108
|
+
return true;
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
const exact = readFileSync(aside, "utf8") === bytes;
|
|
112
|
+
if (!exact) {
|
|
113
|
+
try {
|
|
114
|
+
linkSync(aside, path);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
// A third contender already holds the path; the moved owner's release
|
|
118
|
+
// finds a different inode and leaves that lock alone.
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
unlinkSync(aside);
|
|
122
|
+
return exact;
|
|
123
|
+
}
|
|
124
|
+
export function readWorkflowResumeLock(coordRoot, runId) {
|
|
125
|
+
return readResumeLease(workflowResumeLockPath(coordRoot, runId));
|
|
126
|
+
}
|
|
127
|
+
function readResumeLease(path) {
|
|
128
|
+
try {
|
|
129
|
+
return parseResumeLock(readFileSync(path, "utf8"));
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function parseResumeLock(bytes) {
|
|
136
|
+
try {
|
|
137
|
+
const value = JSON.parse(bytes);
|
|
138
|
+
return typeof value.pid === "number" &&
|
|
139
|
+
Number.isSafeInteger(value.pid) &&
|
|
140
|
+
value.pid > 0 &&
|
|
141
|
+
typeof value.host === "string" &&
|
|
142
|
+
value.host.length > 0 &&
|
|
143
|
+
validTimestamp(value.created_at)
|
|
144
|
+
? {
|
|
145
|
+
pid: value.pid,
|
|
146
|
+
host: value.host,
|
|
147
|
+
created_at: value.created_at,
|
|
148
|
+
start_token: typeof value.start_token === "string" ? value.start_token : null,
|
|
149
|
+
}
|
|
150
|
+
: null;
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function validTimestamp(value) {
|
|
157
|
+
return typeof value === "string" && value.length <= 40 && Number.isFinite(Date.parse(value));
|
|
158
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { RepoSnapshot } from "../context/index.js";
|
|
2
2
|
import { type NormalizedPolicy, type PolicyIsolation, type PolicyNetworkAccess } from "../policy/index.js";
|
|
3
|
-
import { type WorkflowRecoveryAuthorization } from "./recovery-audit.js";
|
|
4
3
|
import type { WorkflowAttemptContext, WorkflowDiagnosticAdmissionConfig, WorkflowSpecialistProfile, WorkflowWorkContext } from "./types.js";
|
|
5
4
|
import type { WorkspaceBinding, WorkspaceCompatibilityExecutionEvidence } from "./workspaces/index.js";
|
|
6
5
|
export declare const WORKFLOW_RUN_MANIFEST_SCHEMA_VERSION: 2;
|
|
@@ -48,24 +47,4 @@ export declare function assertWorkflowRunResumable(coordRoot: string, runId: str
|
|
|
48
47
|
};
|
|
49
48
|
export declare function assertWorkflowScriptUnchanged(manifest: WorkflowRunManifest): void;
|
|
50
49
|
export declare function workflowRunManifestPath(coordRoot: string, runId: string): string;
|
|
51
|
-
/** Acquire an exclusive lease shared by resume and reclaim. Existing locks
|
|
52
|
-
* always refuse; explicit audited recovery is a separate operation. */
|
|
53
|
-
export declare function acquireWorkflowResumeLease(coordRoot: string, runId: string): () => void;
|
|
54
|
-
export interface WorkflowResumeRecoveryOptions extends Partial<WorkflowRecoveryAuthorization> {
|
|
55
|
-
yes?: boolean;
|
|
56
|
-
lockSha256?: string;
|
|
57
|
-
}
|
|
58
|
-
export interface WorkflowResumeRecoveryResult {
|
|
59
|
-
action: "preview" | "unlocked";
|
|
60
|
-
run_id: string;
|
|
61
|
-
lock_sha256: string | null;
|
|
62
|
-
owner_state: "dead" | "live" | "unknown" | "absent";
|
|
63
|
-
owner: {
|
|
64
|
-
pid: number;
|
|
65
|
-
host: string;
|
|
66
|
-
created_at: string;
|
|
67
|
-
} | null;
|
|
68
|
-
recovery_id?: string;
|
|
69
|
-
}
|
|
70
|
-
export declare function recoverWorkflowResumeLease(coordRoot: string, runId: string, options: WorkflowResumeRecoveryOptions): WorkflowResumeRecoveryResult;
|
|
71
50
|
//# 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":"
|
|
1
|
+
{"version":3,"file":"run-state.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/run-state.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EACL,KAAK,gBAAgB,EAErB,KAAK,eAAe,EACpB,KAAK,mBAAmB,EAEzB,MAAM,oBAAoB,CAAC;AAK5B,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"}
|
|
@@ -1,17 +1,13 @@
|
|
|
1
|
-
import { createHash
|
|
2
|
-
import {
|
|
3
|
-
import { hostname } from "node:os";
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, statSync } from "node:fs";
|
|
4
3
|
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
5
4
|
import { normalizePolicy, policyDigest, } from "../policy/index.js";
|
|
6
|
-
import { stateFileMode } from "../storage/modes.js";
|
|
7
5
|
import { assertWorkflowRunId, readWorkflowApproval } from "./approvals.js";
|
|
8
6
|
import { isCanonicalWorkflowAttemptContext } from "./attempt-context.js";
|
|
9
|
-
import {
|
|
10
|
-
import { beginWorkflowRecovery } from "./recovery-audit.js";
|
|
7
|
+
import { readJsonRecord, writeImmutableJson } from "./durable-record.js";
|
|
11
8
|
import { normalizeWorkflowSpecialists } from "./specialists.js";
|
|
12
9
|
import { WORKFLOW_DIAGNOSTIC_ADMISSION_SCHEMA_VERSION } from "./types.js";
|
|
13
10
|
import { isCanonicalWorkflowWorkContext } from "./work-context.js";
|
|
14
|
-
import { acquireNoClobberLease } from "./workspaces/leases.js";
|
|
15
11
|
import { assertWorkspaceManifestAuthority, isWorkspaceBinding, isWorkspaceCompatibilityExecutionEvidence, } from "./workspaces/validate.js";
|
|
16
12
|
export const WORKFLOW_RUN_MANIFEST_SCHEMA_VERSION = 2;
|
|
17
13
|
const MANIFEST_LIMIT_BYTES = 256 * 1024;
|
|
@@ -129,173 +125,6 @@ export function workflowRunManifestPath(coordRoot, runId) {
|
|
|
129
125
|
assertWorkflowRunId(runId);
|
|
130
126
|
return join(coordRoot, ".harnery", "workflows", runId, "run.json");
|
|
131
127
|
}
|
|
132
|
-
/** Acquire an exclusive lease shared by resume and reclaim. Existing locks
|
|
133
|
-
* always refuse; explicit audited recovery is a separate operation. */
|
|
134
|
-
export function acquireWorkflowResumeLease(coordRoot, runId) {
|
|
135
|
-
assertWorkflowRunId(runId);
|
|
136
|
-
const path = join(coordRoot, ".harnery", "workflows", runId, "resume.lock");
|
|
137
|
-
const owner = {
|
|
138
|
-
owner_id: randomUUID(),
|
|
139
|
-
pid: process.pid,
|
|
140
|
-
host: hostname(),
|
|
141
|
-
created_at: new Date().toISOString(),
|
|
142
|
-
};
|
|
143
|
-
const ownerBytes = `${JSON.stringify(owner)}\n`;
|
|
144
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
145
|
-
const acquire = () => {
|
|
146
|
-
try {
|
|
147
|
-
const fd = openSync(path, "wx", stateFileMode());
|
|
148
|
-
try {
|
|
149
|
-
writeFileSync(fd, ownerBytes, "utf8");
|
|
150
|
-
fsyncSync(fd);
|
|
151
|
-
}
|
|
152
|
-
finally {
|
|
153
|
-
closeSync(fd);
|
|
154
|
-
}
|
|
155
|
-
chmodSync(path, stateFileMode());
|
|
156
|
-
fsyncParentDirectory(path);
|
|
157
|
-
const identity = lstatSync(path);
|
|
158
|
-
return { device: identity.dev, inode: identity.ino };
|
|
159
|
-
}
|
|
160
|
-
catch (error) {
|
|
161
|
-
if (error.code !== "EEXIST")
|
|
162
|
-
throw error;
|
|
163
|
-
return false;
|
|
164
|
-
}
|
|
165
|
-
};
|
|
166
|
-
const acquired = withResumeLeaseMutation(coordRoot, runId, acquire);
|
|
167
|
-
if (!acquired) {
|
|
168
|
-
const existing = readResumeLease(path);
|
|
169
|
-
const holder = existing
|
|
170
|
-
? ` by pid ${existing.pid} on ${existing.host}; its liveness has not been verified`
|
|
171
|
-
: "; its owner could not be verified";
|
|
172
|
-
throw new Error(`workflow run ${runId} resume lease is already held${holder}`);
|
|
173
|
-
}
|
|
174
|
-
return () => {
|
|
175
|
-
try {
|
|
176
|
-
// Only this live process can release this nonce. Acquisition never
|
|
177
|
-
// replaces a present lock, and unlock requires ESRCH for its owner, so
|
|
178
|
-
// neither supported writer can replace this lock during synchronous
|
|
179
|
-
// release. Taking their mutation gate would strand a live owner's lock
|
|
180
|
-
// on ordinary contention. Pin both identity and bytes for stale closures.
|
|
181
|
-
const identity = lstatSync(path);
|
|
182
|
-
if (identity.isFile() &&
|
|
183
|
-
identity.dev === acquired.device &&
|
|
184
|
-
identity.ino === acquired.inode &&
|
|
185
|
-
readFileSync(path, "utf8") === ownerBytes) {
|
|
186
|
-
unlinkSync(path);
|
|
187
|
-
fsyncParentDirectory(path);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
catch {
|
|
191
|
-
// Leave an unreadable or replaced lock untouched.
|
|
192
|
-
}
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
export function recoverWorkflowResumeLease(coordRoot, runId, options) {
|
|
196
|
-
assertWorkflowRunId(runId);
|
|
197
|
-
const path = join(coordRoot, ".harnery", "workflows", runId, "resume.lock");
|
|
198
|
-
const inspect = () => {
|
|
199
|
-
let lockSha256;
|
|
200
|
-
try {
|
|
201
|
-
const stat = lstatSync(path);
|
|
202
|
-
if (!stat.isFile() || stat.size > 8 * 1024)
|
|
203
|
-
throw new Error("resume lock is not a bounded regular file");
|
|
204
|
-
lockSha256 = stableDigest({
|
|
205
|
-
bytes: readFileSync(path, "utf8"),
|
|
206
|
-
device: stat.dev,
|
|
207
|
-
inode: stat.ino,
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
catch (error) {
|
|
211
|
-
if (error.code !== "ENOENT")
|
|
212
|
-
throw error;
|
|
213
|
-
return {
|
|
214
|
-
action: "preview",
|
|
215
|
-
run_id: runId,
|
|
216
|
-
lock_sha256: null,
|
|
217
|
-
owner_state: "absent",
|
|
218
|
-
owner: null,
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
const owner = readResumeLease(path);
|
|
222
|
-
let ownerState = "unknown";
|
|
223
|
-
if (owner?.host === hostname()) {
|
|
224
|
-
try {
|
|
225
|
-
process.kill(owner.pid, 0);
|
|
226
|
-
ownerState = "live";
|
|
227
|
-
}
|
|
228
|
-
catch (error) {
|
|
229
|
-
if (error.code === "ESRCH")
|
|
230
|
-
ownerState = "dead";
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
return {
|
|
234
|
-
action: "preview",
|
|
235
|
-
run_id: runId,
|
|
236
|
-
lock_sha256: lockSha256,
|
|
237
|
-
owner_state: ownerState,
|
|
238
|
-
owner,
|
|
239
|
-
};
|
|
240
|
-
};
|
|
241
|
-
if (!options.yes)
|
|
242
|
-
return inspect();
|
|
243
|
-
return withResumeLeaseMutation(coordRoot, runId, () => {
|
|
244
|
-
const observed = inspect();
|
|
245
|
-
if (observed.owner_state !== "dead")
|
|
246
|
-
throw new Error(`workflow resume unlock refused: owner is ${observed.owner_state}`);
|
|
247
|
-
if (!options.lockSha256 || options.lockSha256 !== observed.lock_sha256)
|
|
248
|
-
throw new Error("workflow resume unlock requires the exact current preview digest");
|
|
249
|
-
const audit = beginWorkflowRecovery(coordRoot, {
|
|
250
|
-
run_id: runId,
|
|
251
|
-
action: "unlock_resume",
|
|
252
|
-
target_sha256: options.lockSha256,
|
|
253
|
-
target: { lock: "resume.lock", owner: observed.owner },
|
|
254
|
-
actor: options.actor,
|
|
255
|
-
reason: options.reason,
|
|
256
|
-
});
|
|
257
|
-
const current = inspect();
|
|
258
|
-
if (current.lock_sha256 !== observed.lock_sha256 || current.owner_state !== "dead")
|
|
259
|
-
throw new Error("workflow resume lock authority changed before unlock");
|
|
260
|
-
audit.assertCurrent();
|
|
261
|
-
unlinkSync(path);
|
|
262
|
-
fsyncParentDirectory(path);
|
|
263
|
-
audit.complete();
|
|
264
|
-
return { ...observed, action: "unlocked", recovery_id: audit.intent.operation_id };
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
function withResumeLeaseMutation(coordRoot, runId, action) {
|
|
268
|
-
const authority = stableDigest({ run_id: runId, lock: "resume.lock" });
|
|
269
|
-
const lease = acquireNoClobberLease({
|
|
270
|
-
path: join(coordRoot, ".harnery", "workflows", runId, "resume-mutation.lease"),
|
|
271
|
-
scope: "workflow-resume-mutation",
|
|
272
|
-
authoritySha256: authority,
|
|
273
|
-
staleAfterMs: 300_000,
|
|
274
|
-
validateStaleOwner: (owner) => owner.authority_sha256 === authority,
|
|
275
|
-
});
|
|
276
|
-
try {
|
|
277
|
-
return action();
|
|
278
|
-
}
|
|
279
|
-
finally {
|
|
280
|
-
lease.release();
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
function readResumeLease(path) {
|
|
284
|
-
try {
|
|
285
|
-
const value = JSON.parse(readFileSync(path, "utf8"));
|
|
286
|
-
return typeof value.pid === "number" &&
|
|
287
|
-
Number.isSafeInteger(value.pid) &&
|
|
288
|
-
value.pid > 0 &&
|
|
289
|
-
typeof value.host === "string" &&
|
|
290
|
-
value.host.length > 0 &&
|
|
291
|
-
validTimestamp(value.created_at)
|
|
292
|
-
? { pid: value.pid, host: value.host, created_at: value.created_at }
|
|
293
|
-
: null;
|
|
294
|
-
}
|
|
295
|
-
catch {
|
|
296
|
-
return null;
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
128
|
function validExecution(value) {
|
|
300
129
|
if (!value || typeof value !== "object")
|
|
301
130
|
return false;
|
|
@@ -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;AAKhF,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,CA2ExE;AAED,eAAO,MAAM,iBAAiB,EAAE,OAiB/B,CAAC"}
|
|
@@ -21,7 +21,6 @@ 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";
|
|
25
24
|
import { resolveFullAccessArgv, resolveSandboxProjection } from "./sandbox-projection.js";
|
|
26
25
|
import { isUpstreamFailureText, vendorFailureText } from "./spawn-failure.js";
|
|
27
26
|
export function buildClaudeInvocation(req) {
|
|
@@ -51,24 +50,6 @@ export function buildClaudeInvocation(req) {
|
|
|
51
50
|
return { argv };
|
|
52
51
|
}
|
|
53
52
|
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) {
|
|
72
53
|
if (raw.timedOut) {
|
|
73
54
|
return {
|
|
74
55
|
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;AAKhF,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,CAwCvE;AAED,eAAO,MAAM,YAAY,EAAE,OA8B1B,CAAC"}
|
|
@@ -24,7 +24,6 @@ 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";
|
|
28
27
|
import { resolveFullAccessArgv, resolveSandboxProjection } from "./sandbox-projection.js";
|
|
29
28
|
import { isUpstreamFailureText, vendorFailureText } from "./spawn-failure.js";
|
|
30
29
|
export function buildCodexInvocation(req, resultFile) {
|
|
@@ -72,18 +71,6 @@ export function buildCodexInvocation(req, resultFile) {
|
|
|
72
71
|
return { argv, resultFile };
|
|
73
72
|
}
|
|
74
73
|
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) {
|
|
87
74
|
if (raw.timedOut) {
|
|
88
75
|
return {
|
|
89
76
|
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;AAKhF,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,CAqDxE;AAED,eAAO,MAAM,aAAa,EAAE,OAkB3B,CAAC"}
|
|
@@ -22,7 +22,6 @@ 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";
|
|
26
25
|
import { resolveFullAccessArgv, resolveSandboxProjection } from "./sandbox-projection.js";
|
|
27
26
|
import { isUpstreamFailureText, vendorFailureText } from "./spawn-failure.js";
|
|
28
27
|
/** Exported for unit tests (no live binary to test against). */
|
|
@@ -61,24 +60,6 @@ export function buildCursorInvocation(req) {
|
|
|
61
60
|
return { argv };
|
|
62
61
|
}
|
|
63
62
|
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) {
|
|
82
63
|
if (raw.timedOut) {
|
|
83
64
|
return {
|
|
84
65
|
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;AAKhF,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,CA2C1E;AAED,eAAO,MAAM,eAAe,EAAE,OAkB7B,CAAC"}
|
|
@@ -20,7 +20,6 @@ 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";
|
|
24
23
|
import { resolveFullAccessArgv, resolveSandboxProjection } from "./sandbox-projection.js";
|
|
25
24
|
import { isUpstreamFailureText, vendorFailureText } from "./spawn-failure.js";
|
|
26
25
|
/**
|
|
@@ -73,19 +72,6 @@ export function buildOpenCodeInvocation(req) {
|
|
|
73
72
|
return { argv };
|
|
74
73
|
}
|
|
75
74
|
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) {
|
|
89
75
|
if (raw.timedOut) {
|
|
90
76
|
return {
|
|
91
77
|
ok: false,
|
|
@@ -103,12 +103,11 @@ export interface WorkflowAgentProof {
|
|
|
103
103
|
session_id?: string;
|
|
104
104
|
result?: ResultDigest;
|
|
105
105
|
error?: string;
|
|
106
|
-
/**
|
|
107
|
-
*
|
|
106
|
+
/** Set on a failed agent whose spawn was classified uninformative about the
|
|
107
|
+
* work (ADR 0046): environment (binary absent) or upstream (vendor refused).
|
|
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. */
|
|
108
110
|
class?: SpawnFailureClass;
|
|
109
|
-
/** Failure observations carried from the adapter. Missing observations are
|
|
110
|
-
* unknown and cannot authorize an uncharged attempt. */
|
|
111
|
-
failure_evidence?: SpawnFailureEvidence;
|
|
112
111
|
/** Which vendor sandbox mode this agent launched with and why (ADR 0192).
|
|
113
112
|
* Absent on a cached agent, which launched nothing. */
|
|
114
113
|
access?: WorkerAccessDecision;
|
|
@@ -310,16 +309,15 @@ export interface WorkflowSpecialistProfile {
|
|
|
310
309
|
maxTurns?: number;
|
|
311
310
|
}
|
|
312
311
|
/**
|
|
313
|
-
*
|
|
314
|
-
*
|
|
312
|
+
* Why a failed run was uninformative about the work (ADR 0046). Absent means
|
|
313
|
+
* the attempt produced information about the work (or succeeded), which is the
|
|
314
|
+
* default and is charged against the attempt budget exactly as before.
|
|
315
315
|
*
|
|
316
316
|
* - `environment`: the run never started — a precondition was missing (the
|
|
317
|
-
* vendor binary was absent).
|
|
318
|
-
* uncharged and not retried: retrying an unchanged
|
|
317
|
+
* vendor binary was absent). Uncharged AND not retried: retrying an unchanged
|
|
319
318
|
* environment cannot help, so the work item stops and names the precondition.
|
|
320
319
|
* - `upstream`: the vendor was reached and refused (5xx, 429, circuit open).
|
|
321
|
-
*
|
|
322
|
-
* observed absent output, and no refusal. Unsupported evidence stays unknown.
|
|
320
|
+
* Uncharged, but retry stays available (bounded by max_uncharged_attempts).
|
|
323
321
|
*/
|
|
324
322
|
export type SpawnFailureClass = "environment" | "upstream";
|
|
325
323
|
/**
|
|
@@ -339,43 +337,6 @@ export type SpawnFailureClass = "environment" | "upstream";
|
|
|
339
337
|
* waiting for is a person who was never told.
|
|
340
338
|
*/
|
|
341
339
|
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
|
-
}
|
|
379
340
|
/** What a spawn adapter returns for one subagent run. */
|
|
380
341
|
export interface SpawnResult {
|
|
381
342
|
ok: boolean;
|
|
@@ -387,13 +348,9 @@ export interface SpawnResult {
|
|
|
387
348
|
durationMs: number;
|
|
388
349
|
/** Populated when ok=false. */
|
|
389
350
|
error?: string;
|
|
390
|
-
/**
|
|
391
|
-
*
|
|
351
|
+
/** Set when ok=false and the failure was positively identified as
|
|
352
|
+
* uninformative about the work. Absent ⇒ a work failure (charged). */
|
|
392
353
|
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;
|
|
397
354
|
}
|
|
398
355
|
export interface SpawnRequest {
|
|
399
356
|
prompt: string;
|