pi-daddy 0.45.0 → 0.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/README.md +355 -45
- package/dist/executors/captured-execution.d.ts +2 -2
- package/dist/executors/captured-execution.d.ts.map +1 -1
- package/dist/executors/captured-execution.js +2 -2
- package/dist/executors/captured-execution.js.map +1 -1
- package/dist/executors/executor.d.ts.map +1 -1
- package/dist/executors/executor.js +4 -2
- package/dist/executors/executor.js.map +1 -1
- package/dist/executors/herdr-cli.d.ts +2 -0
- package/dist/executors/herdr-cli.d.ts.map +1 -1
- package/dist/executors/herdr-cli.js +2 -2
- package/dist/executors/herdr-cli.js.map +1 -1
- package/dist/executors/herdr-launcher.d.ts +2 -0
- package/dist/executors/herdr-launcher.d.ts.map +1 -0
- package/dist/executors/herdr-launcher.js +123 -0
- package/dist/executors/herdr-launcher.js.map +1 -0
- package/dist/executors/herdr-owned.d.ts +13 -0
- package/dist/executors/herdr-owned.d.ts.map +1 -0
- package/dist/executors/herdr-owned.js +229 -0
- package/dist/executors/herdr-owned.js.map +1 -0
- package/dist/executors/herdr-qualification.d.ts +6 -0
- package/dist/executors/herdr-qualification.d.ts.map +1 -0
- package/dist/executors/herdr-qualification.js +33 -0
- package/dist/executors/herdr-qualification.js.map +1 -0
- package/dist/executors/herdr-wire.d.ts +5 -0
- package/dist/executors/herdr-wire.d.ts.map +1 -0
- package/dist/executors/herdr-wire.js +47 -0
- package/dist/executors/herdr-wire.js.map +1 -0
- package/dist/governance/runtime-settlement-lock.d.ts +5 -0
- package/dist/governance/runtime-settlement-lock.d.ts.map +1 -0
- package/dist/governance/runtime-settlement-lock.js +72 -0
- package/dist/governance/runtime-settlement-lock.js.map +1 -0
- package/dist/governance/runtime-settlement.d.ts +22 -0
- package/dist/governance/runtime-settlement.d.ts.map +1 -0
- package/dist/governance/runtime-settlement.js +236 -0
- package/dist/governance/runtime-settlement.js.map +1 -0
- package/extensions/execute-child.ts +98 -74
- package/extensions/executor-session.ts +2 -1
- package/extensions/grants.ts +3 -0
- package/extensions/reload-environment.ts +8 -0
- package/extensions/runtime-snapshot.ts +123 -0
- package/extensions/session.ts +1 -1
- package/package.json +1 -1
- package/src/executors/captured-execution.ts +2 -1
- package/src/executors/executor.ts +4 -2
- package/src/executors/herdr-cli.ts +19 -12
- package/src/executors/herdr-launcher.ts +124 -0
- package/src/executors/herdr-owned.ts +260 -0
- package/src/executors/herdr-qualification.ts +33 -0
- package/src/executors/herdr-wire.ts +43 -0
- package/src/governance/runtime-settlement-lock.ts +76 -0
- package/src/governance/runtime-settlement.ts +271 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/** Local runtime facts for explicitly armed resume; no task text, grants, approval or model verdicts. */
|
|
2
|
+
import { lockRuntimeSettlement } from "./runtime-settlement-lock.js";
|
|
3
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
4
|
+
import { mkdirSync, realpathSync, lstatSync, openSync, writeFileSync, fsyncSync, closeSync, renameSync, unlinkSync, constants, readFileSync, } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { readBoundedTextSync } from "../kernel/bounded-read-sync.js";
|
|
7
|
+
import { isCapturedWorkerIdentity, readCapturedWorkerReceipt, sameWorkerIdentity } from "./captured-worker-record.js";
|
|
8
|
+
const MAX_RECORD = 4 * 1024 * 1024;
|
|
9
|
+
export const settlementHash = (value) => createHash("sha256").update(JSON.stringify(value)).digest("hex");
|
|
10
|
+
const ownerId = () => {
|
|
11
|
+
const stat = readFileSync("/proc/self/stat", "utf8");
|
|
12
|
+
return [
|
|
13
|
+
readFileSync("/proc/sys/kernel/random/boot_id", "utf8").trim(),
|
|
14
|
+
process.pid,
|
|
15
|
+
stat.slice(stat.lastIndexOf(")") + 2).split(" ")[19],
|
|
16
|
+
].join(":");
|
|
17
|
+
};
|
|
18
|
+
function ownerAlive(id) {
|
|
19
|
+
const [boot, pid, ticks] = id.split(":");
|
|
20
|
+
if (!/^\d+$/.test(pid ?? "") || !/^\d+$/.test(ticks ?? ""))
|
|
21
|
+
throw Error("invalid runtime journal owner");
|
|
22
|
+
if (readFileSync("/proc/sys/kernel/random/boot_id", "utf8").trim() !== boot)
|
|
23
|
+
return false;
|
|
24
|
+
try {
|
|
25
|
+
const text = readFileSync("/proc/" + pid + "/stat", "utf8");
|
|
26
|
+
return text.slice(text.lastIndexOf(")") + 2).split(" ")[19] === ticks;
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
if (error.code === "ENOENT")
|
|
30
|
+
return false;
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function validate(value, sessionId, cwd) {
|
|
35
|
+
const j = value;
|
|
36
|
+
if (!j ||
|
|
37
|
+
j.version !== 1 ||
|
|
38
|
+
j.sessionId !== sessionId ||
|
|
39
|
+
j.cwd !== cwd ||
|
|
40
|
+
!/^[a-f0-9-]{36}$/.test(j.ownerScope) ||
|
|
41
|
+
typeof j.ownerId !== "string" ||
|
|
42
|
+
!Array.isArray(j.executions) ||
|
|
43
|
+
j.executions.length > 10000)
|
|
44
|
+
throw Error("invalid runtime journal identity");
|
|
45
|
+
const ids = new Set();
|
|
46
|
+
for (const e of j.executions) {
|
|
47
|
+
if (!e ||
|
|
48
|
+
typeof e.id !== "string" ||
|
|
49
|
+
!e.id ||
|
|
50
|
+
e.id.length > 512 ||
|
|
51
|
+
ids.has(e.id) ||
|
|
52
|
+
!["pending", "settled", "not-started", "unknown"].includes(e.state) ||
|
|
53
|
+
(e.identity !== undefined && (!isCapturedWorkerIdentity(e.identity) || e.identity.executionId !== e.id)) ||
|
|
54
|
+
(e.receiptSha256 !== undefined && !/^[a-f0-9]{64}$/.test(e.receiptSha256)) ||
|
|
55
|
+
(e.state === "settled" && (!e.identity || !e.receiptSha256)) ||
|
|
56
|
+
(e.state === "not-started" && e.identity) ||
|
|
57
|
+
(e.problem !== undefined && typeof e.problem !== "string"))
|
|
58
|
+
throw Error("invalid runtime journal execution");
|
|
59
|
+
ids.add(e.id);
|
|
60
|
+
}
|
|
61
|
+
return j;
|
|
62
|
+
}
|
|
63
|
+
export async function openRuntimeSettlement(directory, sessionId, cwd) {
|
|
64
|
+
if (process.platform !== "linux" || process.arch !== "x64")
|
|
65
|
+
throw Error("runtime settlement requires Linux x64");
|
|
66
|
+
if (!sessionId || sessionId.length > 512 || cwd !== realpathSync(cwd))
|
|
67
|
+
throw Error("runtime session/cwd identity is unavailable");
|
|
68
|
+
const root = join(realpathSync(directory), "pi-daddy", "runtime-settlement");
|
|
69
|
+
mkdirSync(root, { recursive: true, mode: 0o700 });
|
|
70
|
+
if (realpathSync(root) !== root)
|
|
71
|
+
throw Error("runtime settlement root must not contain symlinks");
|
|
72
|
+
const info = lstatSync(root);
|
|
73
|
+
if (!info.isDirectory() || info.uid !== process.getuid() || info.mode & 0o077)
|
|
74
|
+
throw Error("runtime settlement root must be private");
|
|
75
|
+
const path = join(root, settlementHash([sessionId, cwd]) + ".json");
|
|
76
|
+
const lock = await lockRuntimeSettlement(path + ".lock");
|
|
77
|
+
try {
|
|
78
|
+
let journal;
|
|
79
|
+
try {
|
|
80
|
+
journal = validate(JSON.parse(readBoundedTextSync(path, { maxBytes: MAX_RECORD }).text), sessionId, cwd);
|
|
81
|
+
if (ownerAlive(journal.ownerId))
|
|
82
|
+
throw Error("prior runtime owner is still alive");
|
|
83
|
+
journal.ownerId = ownerId();
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
if (error.code !== "ENOENT")
|
|
87
|
+
throw error;
|
|
88
|
+
journal = { version: 1, sessionId, cwd, ownerScope: randomUUID(), ownerId: ownerId(), executions: [] };
|
|
89
|
+
}
|
|
90
|
+
let failed, active = new Set();
|
|
91
|
+
const save = () => {
|
|
92
|
+
let temporary, fd;
|
|
93
|
+
try {
|
|
94
|
+
if (!lock.healthy())
|
|
95
|
+
throw Error("runtime ownership lock was lost");
|
|
96
|
+
const text = JSON.stringify(journal) + "\n";
|
|
97
|
+
if (Buffer.byteLength(text) > MAX_RECORD)
|
|
98
|
+
throw Error("runtime settlement journal exceeds its bound");
|
|
99
|
+
temporary = path + "." + randomUUID();
|
|
100
|
+
fd = openSync(temporary, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600);
|
|
101
|
+
writeFileSync(fd, text);
|
|
102
|
+
fsyncSync(fd);
|
|
103
|
+
closeSync(fd);
|
|
104
|
+
fd = undefined;
|
|
105
|
+
renameSync(temporary, path);
|
|
106
|
+
const parent = openSync(root, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
|
|
107
|
+
try {
|
|
108
|
+
fsyncSync(parent);
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
closeSync(parent);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
failed = String(error);
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
finally {
|
|
119
|
+
if (fd !== undefined)
|
|
120
|
+
closeSync(fd);
|
|
121
|
+
if (temporary)
|
|
122
|
+
try {
|
|
123
|
+
unlinkSync(temporary);
|
|
124
|
+
}
|
|
125
|
+
catch { }
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
const entry = (id) => {
|
|
129
|
+
const e = journal.executions.find((e) => e.id === id);
|
|
130
|
+
if (!e)
|
|
131
|
+
throw Error("runtime execution was not registered");
|
|
132
|
+
return e;
|
|
133
|
+
};
|
|
134
|
+
save();
|
|
135
|
+
return {
|
|
136
|
+
ownerScope: journal.ownerScope,
|
|
137
|
+
ownerId: journal.ownerId,
|
|
138
|
+
sessionId,
|
|
139
|
+
cwd,
|
|
140
|
+
begin(id) {
|
|
141
|
+
if (failed)
|
|
142
|
+
throw Error(failed);
|
|
143
|
+
if (typeof id !== "string" ||
|
|
144
|
+
!id ||
|
|
145
|
+
id.length > 512 ||
|
|
146
|
+
journal.executions.some((e) => e.id === id) ||
|
|
147
|
+
journal.executions.length >= 10000)
|
|
148
|
+
throw Error("runtime execution id must be fresh and bounded");
|
|
149
|
+
journal.executions.push({ id, state: "pending" });
|
|
150
|
+
active.add(id);
|
|
151
|
+
save();
|
|
152
|
+
},
|
|
153
|
+
bind(identity) {
|
|
154
|
+
const e = entry(identity.executionId);
|
|
155
|
+
if (e.state !== "pending" || (e.identity && !sameWorkerIdentity(e.identity, identity)))
|
|
156
|
+
throw Error("runtime ownership changed");
|
|
157
|
+
e.identity = { ...identity };
|
|
158
|
+
save();
|
|
159
|
+
},
|
|
160
|
+
async finish(id, cleanup, problem) {
|
|
161
|
+
const e = entry(id);
|
|
162
|
+
if (problem)
|
|
163
|
+
e.problem = problem.slice(0, 2000);
|
|
164
|
+
try {
|
|
165
|
+
if (cleanup.state === "not-started" && !e.identity)
|
|
166
|
+
e.state = "not-started";
|
|
167
|
+
else if (cleanup.state === "settled" && e.identity && sameWorkerIdentity(e.identity, cleanup.identity)) {
|
|
168
|
+
const receipt = await readCapturedWorkerReceipt(e.identity);
|
|
169
|
+
if (receipt) {
|
|
170
|
+
e.state = "settled";
|
|
171
|
+
e.receiptSha256 = settlementHash(receipt);
|
|
172
|
+
}
|
|
173
|
+
else
|
|
174
|
+
e.state = "unknown";
|
|
175
|
+
}
|
|
176
|
+
else
|
|
177
|
+
e.state = "unknown";
|
|
178
|
+
save();
|
|
179
|
+
}
|
|
180
|
+
finally {
|
|
181
|
+
active.delete(id);
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
async snapshot() {
|
|
185
|
+
if (!lock.healthy())
|
|
186
|
+
failed = "runtime ownership lock was lost";
|
|
187
|
+
let changed = false;
|
|
188
|
+
for (const e of journal.executions) {
|
|
189
|
+
if (active.has(e.id) || !e.identity)
|
|
190
|
+
continue;
|
|
191
|
+
const receipt = await readCapturedWorkerReceipt(e.identity);
|
|
192
|
+
if (!receipt || (e.receiptSha256 && e.receiptSha256 !== settlementHash(receipt))) {
|
|
193
|
+
if (e.state === "settled") {
|
|
194
|
+
e.state = "unknown";
|
|
195
|
+
changed = true;
|
|
196
|
+
}
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (e.state !== "settled") {
|
|
200
|
+
e.state = "settled";
|
|
201
|
+
e.receiptSha256 = settlementHash(receipt);
|
|
202
|
+
changed = true;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (changed) {
|
|
206
|
+
try {
|
|
207
|
+
save();
|
|
208
|
+
}
|
|
209
|
+
catch { }
|
|
210
|
+
}
|
|
211
|
+
const outstanding = journal.executions.filter((e) => !["settled", "not-started"].includes(e.state) || e.problem);
|
|
212
|
+
const state = failed || outstanding.some((e) => !active.has(e.id))
|
|
213
|
+
? "unknown"
|
|
214
|
+
: active.size
|
|
215
|
+
? "busy"
|
|
216
|
+
: outstanding.length
|
|
217
|
+
? "unknown"
|
|
218
|
+
: "idle";
|
|
219
|
+
return {
|
|
220
|
+
ownerScope: journal.ownerScope,
|
|
221
|
+
ownerId: journal.ownerId,
|
|
222
|
+
evidenceDigest: settlementHash({ ownerScope: journal.ownerScope, executions: journal.executions }),
|
|
223
|
+
state,
|
|
224
|
+
settledExecutionIds: journal.executions.filter((e) => e.state === "settled").map((e) => e.id),
|
|
225
|
+
outstandingExecutionIds: outstanding.map((e) => e.id),
|
|
226
|
+
...(failed ? { reason: failed } : {}),
|
|
227
|
+
};
|
|
228
|
+
},
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
await lock.release();
|
|
233
|
+
throw error;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=runtime-settlement.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-settlement.js","sourceRoot":"","sources":["../../src/governance/runtime-settlement.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BoundedReadCleanupError } from "../src/kernel/bounded-read.ts";
|
|
2
2
|
import { retainDiscoveryCleanupFailure } from "./session.ts";
|
|
3
3
|
import type { CapacityReservation } from "../src/kernel/capacity.ts";
|
|
4
|
-
import {
|
|
4
|
+
import { runHerdrOwned } from "../src/executors/herdr-owned.ts";
|
|
5
5
|
import { runCapturedExecution } from "../src/executors/captured-execution.ts";
|
|
6
6
|
import type { ChildFinal } from "../src/executors/child-final.ts";
|
|
7
7
|
import type { CapturedWorkerCleanup } from "../src/kernel/captured-worker-contract.ts";
|
|
@@ -20,13 +20,12 @@ import {
|
|
|
20
20
|
ENV_CHILD_IDLE_TIMEOUT,
|
|
21
21
|
ENV_CHILD_TIMEOUT,
|
|
22
22
|
idleTimeoutFromEnv,
|
|
23
|
-
runChild,
|
|
24
23
|
timeoutFromEnv,
|
|
25
24
|
} from "../src/kernel/run-child.ts";
|
|
26
25
|
import { activitySessionFor } from "../src/executors/activity-session.ts";
|
|
27
26
|
import { processTreeActivity } from "../src/executors/process-activity.ts";
|
|
28
27
|
import { resolveWorkspace } from "../src/executors/herdr-cli.ts";
|
|
29
|
-
import { HerdrWriterCloseError
|
|
28
|
+
import { HerdrWriterCloseError } from "../src/executors/run-herdr.ts";
|
|
30
29
|
import { GovernanceRefusal, refusal, type StructuredRefusal } from "../src/kernel/refusals.ts";
|
|
31
30
|
import { ENV_HERDR_KEEP_PANE, type GrantsSession } from "./session.ts";
|
|
32
31
|
import { CHILD_ATTRIBUTION_ENV_KEYS, ENV_EPISODE_ID } from "../src/kernel/env-names.ts";
|
|
@@ -138,15 +137,49 @@ export interface ChildExecutionInput {
|
|
|
138
137
|
}
|
|
139
138
|
export async function executePlannedChild(input: ChildExecutionInput): Promise<DelegationOutcome> {
|
|
140
139
|
let executorEntered = false;
|
|
140
|
+
const runtime = input.session.reloadLifecycle?.runtimeSettlement;
|
|
141
|
+
let registered = false;
|
|
141
142
|
try {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
143
|
+
runtime?.begin(input.executionId);
|
|
144
|
+
registered = Boolean(runtime);
|
|
145
|
+
const result = await executePreparedChild(
|
|
146
|
+
input,
|
|
147
|
+
() => {
|
|
148
|
+
executorEntered = true;
|
|
149
|
+
},
|
|
150
|
+
runtime,
|
|
151
|
+
);
|
|
152
|
+
try {
|
|
153
|
+
await runtime?.finish(
|
|
154
|
+
input.executionId,
|
|
155
|
+
result.cleanup ?? { state: "unknown", reason: "executor omitted cleanup" },
|
|
156
|
+
result.control === "failed" ? (result.reason ?? "runtime control failed") : undefined,
|
|
157
|
+
);
|
|
158
|
+
} catch (error) {
|
|
159
|
+
return {
|
|
160
|
+
...result,
|
|
161
|
+
control: "failed",
|
|
162
|
+
reason: [result.reason, `runtime settlement record failed: ${String(error)}`].filter(Boolean).join("; "),
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
return result;
|
|
145
166
|
} catch (error) {
|
|
146
|
-
if (executorEntered) throw error;
|
|
147
|
-
// Every operation above the executor-entry marker is local setup: no helper or worker exists yet.
|
|
148
|
-
input.capacityReservation?.finalize({ state: "not-started", reason: "child setup failed before executor entry" });
|
|
149
167
|
const failures: unknown[] = [error];
|
|
168
|
+
if (registered)
|
|
169
|
+
try {
|
|
170
|
+
await runtime?.finish(input.executionId, {
|
|
171
|
+
state: executorEntered ? "unknown" : "not-started",
|
|
172
|
+
reason: String(error),
|
|
173
|
+
});
|
|
174
|
+
} catch (recordError) {
|
|
175
|
+
failures.push(recordError);
|
|
176
|
+
}
|
|
177
|
+
if (executorEntered) {
|
|
178
|
+
if (failures.length > 1) throw new AggregateError(failures, failures.map(String).join("; "));
|
|
179
|
+
throw error;
|
|
180
|
+
}
|
|
181
|
+
// Setup failures prove no worker started, even if recording that fact also failed.
|
|
182
|
+
input.capacityReservation?.finalize({ state: "not-started", reason: "child setup failed before executor entry" });
|
|
150
183
|
try {
|
|
151
184
|
const released = await releaseDelegationWorkspace({
|
|
152
185
|
prepared: input.preparedWorkspace,
|
|
@@ -174,6 +207,7 @@ export async function executePlannedChild(input: ChildExecutionInput): Promise<D
|
|
|
174
207
|
async function executePreparedChild(
|
|
175
208
|
input: ChildExecutionInput,
|
|
176
209
|
onExecutorEntry: () => void,
|
|
210
|
+
runtime?: import("../src/governance/runtime-settlement.ts").RuntimeSettlement,
|
|
177
211
|
): Promise<DelegationOutcome> {
|
|
178
212
|
const { session, plan, childId, executionId, parentExecutionId, preparedWorkspace, signal, onProgress } = input;
|
|
179
213
|
const ledgerPath = session.ledgerPath;
|
|
@@ -229,7 +263,7 @@ async function executePreparedChild(
|
|
|
229
263
|
const activitySession = await activitySessionFor(plan.args, executionId);
|
|
230
264
|
// Disposed on every path (review finding: it leaked on every throw), except when the operator keeps a Herdr pane,
|
|
231
265
|
// where the interactive pi in that pane is still alive and still appending to this file.
|
|
232
|
-
const keepPaneRequested =
|
|
266
|
+
const keepPaneRequested = false; // The qualified pane launcher exits; retained panes contain no live Pi writer.
|
|
233
267
|
try {
|
|
234
268
|
return await executeWithActivitySession();
|
|
235
269
|
} finally {
|
|
@@ -368,73 +402,63 @@ async function executePreparedChild(
|
|
|
368
402
|
}
|
|
369
403
|
};
|
|
370
404
|
try {
|
|
371
|
-
if (
|
|
372
|
-
throw new GovernanceRefusal(refusal("EXECUTOR_UNAVAILABLE",
|
|
405
|
+
if (session.executor.refusal)
|
|
406
|
+
throw new GovernanceRefusal(refusal("EXECUTOR_UNAVAILABLE", session.executor.refusal));
|
|
373
407
|
executorEntered = true;
|
|
374
408
|
onExecutorEntry();
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
executionId,
|
|
408
|
-
sessionPath: activitySession.path,
|
|
409
|
-
onReadCleanup,
|
|
410
|
-
onOwnership: async (identity) => {
|
|
411
|
-
input.capacityReservation?.bindOwnership(identity);
|
|
412
|
-
await preparedWorkspace?.lease.attachCapturedWorker(identity);
|
|
413
|
-
},
|
|
414
|
-
command: "pi",
|
|
415
|
-
args,
|
|
416
|
-
env: mergeChildEnv(process.env, plan.env),
|
|
417
|
-
cwd,
|
|
418
|
-
signal: executionSignal,
|
|
419
|
-
// SIGTERM gets only grace that fits INSIDE the recorded deadline. The independent hard timer
|
|
420
|
-
// prevents a delayed soft-timeout callback from starting a fresh grace period beyond that bound.
|
|
421
|
-
timeoutMs: Math.max(1, remainingTimeoutMs - terminationGraceMs),
|
|
422
|
-
hardDeadlineAt: Date.parse(deadlineAt),
|
|
423
|
-
idleTimeoutMs: configuredIdleMs,
|
|
424
|
-
activityProbe: probe,
|
|
425
|
-
onOutput: onProgress ? (chunk) => onProgress({ chunk }) : undefined,
|
|
426
|
-
onObservation: (stream, bytes) => retention.capture(stream, bytes),
|
|
427
|
-
onSpawn: (pid) => {
|
|
428
|
-
childPid = pid;
|
|
429
|
-
// Lease attachment is a security hook and may fail the spawn. The shared reporters isolate
|
|
430
|
-
// display exceptions before they reach this callback; runChild deliberately kills on any error
|
|
431
|
-
// here, so presentation must never be added directly without that reporter boundary.
|
|
409
|
+
let displayHerdr: ((text: string) => void) | undefined;
|
|
410
|
+
const output = await runCapturedExecution(
|
|
411
|
+
{
|
|
412
|
+
executionId,
|
|
413
|
+
sessionPath: activitySession.path,
|
|
414
|
+
onReadCleanup,
|
|
415
|
+
onOwnership: async (identity) => {
|
|
416
|
+
runtime?.bind(identity);
|
|
417
|
+
input.capacityReservation?.bindOwnership(identity);
|
|
418
|
+
await preparedWorkspace?.lease.attachCapturedWorker(identity);
|
|
419
|
+
},
|
|
420
|
+
command: "pi",
|
|
421
|
+
args,
|
|
422
|
+
env: mergeChildEnv(process.env, plan.env),
|
|
423
|
+
cwd,
|
|
424
|
+
signal: executionSignal,
|
|
425
|
+
// SIGTERM gets only grace that fits INSIDE the recorded deadline. The independent hard timer
|
|
426
|
+
// prevents a delayed soft-timeout callback from starting a fresh grace period beyond that bound.
|
|
427
|
+
timeoutMs: Math.max(1, remainingTimeoutMs - terminationGraceMs),
|
|
428
|
+
hardDeadlineAt: Date.parse(deadlineAt),
|
|
429
|
+
idleTimeoutMs: configuredIdleMs,
|
|
430
|
+
activityProbe: probe,
|
|
431
|
+
onOutput: (chunk) => {
|
|
432
|
+
displayHerdr?.(chunk);
|
|
433
|
+
onProgress?.({ chunk });
|
|
434
|
+
},
|
|
435
|
+
onObservation: (stream, bytes) => retention.capture(stream, bytes),
|
|
436
|
+
onSpawn: (pid) => {
|
|
437
|
+
childPid = pid;
|
|
438
|
+
// Lease attachment is a security hook and may fail the spawn. The shared reporters isolate
|
|
439
|
+
// display exceptions before they reach this callback; runChild deliberately kills on any error
|
|
440
|
+
// here, so presentation must never be added directly without that reporter boundary.
|
|
432
441
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
442
|
+
retention.native({ pid });
|
|
443
|
+
recordRunning(session.executor.kind);
|
|
444
|
+
onProgress?.({ state: "running" });
|
|
445
|
+
},
|
|
446
|
+
},
|
|
447
|
+
session.executor.kind === "herdr"
|
|
448
|
+
? (request) =>
|
|
449
|
+
runHerdrOwned(request, {
|
|
450
|
+
onDisplay: (display) => {
|
|
451
|
+
displayHerdr = display;
|
|
452
|
+
},
|
|
453
|
+
workspace: resolveWorkspace(process.env),
|
|
454
|
+
keepPane: process.env[ENV_HERDR_KEEP_PANE] === "1",
|
|
455
|
+
onPane: (paneId, tabId) => {
|
|
456
|
+
retention.native({ paneId, tabId });
|
|
457
|
+
onProgress?.({ paneId, state: "starting" });
|
|
458
|
+
},
|
|
459
|
+
})
|
|
460
|
+
: undefined,
|
|
461
|
+
);
|
|
438
462
|
|
|
439
463
|
if (sessionFlag >= 0) retention.observeSession({ source: "pi-session-file", value: plan.args[sessionFlag + 1] });
|
|
440
464
|
retention.capture("result", Buffer.from(output.text), true);
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { chooseExecutor, needsProbe, ENV_HERDR } from "../src/executors/executor.ts";
|
|
2
|
+
import { qualifyHerdr } from "../src/executors/herdr-qualification.ts";
|
|
2
3
|
import { probeHerdr } from "../src/executors/herdr-cli.ts";
|
|
3
4
|
import type { GrantsSession } from "./session.ts";
|
|
4
5
|
|
|
5
6
|
/** Settle one executor at session start; a later daemon change never relocates siblings. */
|
|
6
7
|
export async function resolveExecutor(session: GrantsSession): Promise<void> {
|
|
7
8
|
const raw = process.env[ENV_HERDR];
|
|
8
|
-
session.executor = chooseExecutor(raw, needsProbe(raw) ? await probeHerdr() : null);
|
|
9
|
+
session.executor = chooseExecutor(raw, needsProbe(raw) ? await qualifyHerdr(await probeHerdr()) : null);
|
|
9
10
|
}
|
package/extensions/grants.ts
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* spawn triggered the human prompt, and `childEnv` clamps it to the grant again on the way out. Each
|
|
21
21
|
* child derives its own grant from the tool array of its first provider request.
|
|
22
22
|
*/
|
|
23
|
+
import { registerRuntimeSnapshot } from "./runtime-snapshot.ts";
|
|
23
24
|
import { fileURLToPath } from "node:url";
|
|
24
25
|
import { homedir } from "node:os";
|
|
25
26
|
import { join, resolve } from "node:path";
|
|
@@ -80,6 +81,7 @@ export default function (pi: ExtensionAPI, trustedSession?: GrantsSession) {
|
|
|
80
81
|
const session = trustedSession ?? createGrantsSession(extensionPath, undefined, observerExtensionPath);
|
|
81
82
|
session.currentThinking = typeof pi.getThinkingLevel === "function" ? () => pi.getThinkingLevel() : undefined;
|
|
82
83
|
registerActivityTimeline(pi, session);
|
|
84
|
+
const runtimeSnapshot = registerRuntimeSnapshot(pi, session);
|
|
83
85
|
const dashboardPluginRoot = fileURLToPath(new URL("../herdr-plugin/", import.meta.url));
|
|
84
86
|
const dashboardPaths = defaultDashboardPaths(agentDir());
|
|
85
87
|
// Definitions are registered only after owner-bound session_start. Until then there is no delegation
|
|
@@ -215,6 +217,7 @@ export default function (pi: ExtensionAPI, trustedSession?: GrantsSession) {
|
|
|
215
217
|
// (R-60), and `probeHerdr` is documented as never throwing precisely so this is belt-and-braces.
|
|
216
218
|
try {
|
|
217
219
|
await resolveExecutor(session);
|
|
220
|
+
if (session.reloadLifecycle === reload.lifecycle) await runtimeSnapshot.bind(ctx);
|
|
218
221
|
} catch (error) {
|
|
219
222
|
if (session.reloadLifecycle !== reload.lifecycle) return undefined;
|
|
220
223
|
// Says what is actually true of the state left behind, which depends on the variable: with
|
|
@@ -4,6 +4,10 @@ import type { BoundedReadCleanupError } from "../src/kernel/bounded-read.ts";
|
|
|
4
4
|
|
|
5
5
|
/** The root baseline and latest child publication for one real Pi session owner. */
|
|
6
6
|
export interface ReloadLifecycle {
|
|
7
|
+
runtimeSnapshotUnsubscribe?: () => void;
|
|
8
|
+
runtimeSnapshotGeneration?: object;
|
|
9
|
+
runtimeSettlements?: Map<string, import("../src/governance/runtime-settlement.ts").RuntimeSettlement>;
|
|
10
|
+
runtimeSettlement?: import("../src/governance/runtime-settlement.ts").RuntimeSettlement;
|
|
7
11
|
root: Record<string, string | undefined>;
|
|
8
12
|
/** Never replaced for the same owner, even when an explicit root environment changes. */
|
|
9
13
|
capacity?: OwnerCapacity;
|
|
@@ -99,6 +103,10 @@ export function bindReloadLifecycle(
|
|
|
99
103
|
activityRootId: existing.activityRootId,
|
|
100
104
|
episodeId: existing.episodeId,
|
|
101
105
|
capacity: existing.capacity,
|
|
106
|
+
runtimeSettlement: existing.runtimeSettlement,
|
|
107
|
+
runtimeSettlements: existing.runtimeSettlements,
|
|
108
|
+
runtimeSnapshotUnsubscribe: existing.runtimeSnapshotUnsubscribe,
|
|
109
|
+
runtimeSnapshotGeneration: existing.runtimeSnapshotGeneration,
|
|
102
110
|
};
|
|
103
111
|
holder.owners.set(owner, existing);
|
|
104
112
|
}
|