pi-daddy 0.17.1 → 0.18.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 +54 -0
- package/README.md +54 -25
- package/dist/approval-prompt.d.ts +3 -1
- package/dist/approval-prompt.d.ts.map +1 -1
- package/dist/approval-prompt.js +1 -1
- package/dist/approval-prompt.js.map +1 -1
- package/dist/approval-store.d.ts.map +1 -1
- package/dist/approval-store.js +4 -1
- package/dist/approval-store.js.map +1 -1
- package/dist/approval.d.ts +20 -2
- package/dist/approval.d.ts.map +1 -1
- package/dist/approval.js +26 -6
- package/dist/approval.js.map +1 -1
- package/dist/chain.d.ts +6 -1
- package/dist/chain.d.ts.map +1 -1
- package/dist/chain.js +1 -1
- package/dist/chain.js.map +1 -1
- package/dist/check-runner.d.ts +61 -0
- package/dist/check-runner.d.ts.map +1 -0
- package/dist/check-runner.js +237 -0
- package/dist/check-runner.js.map +1 -0
- package/dist/correlation.d.ts +90 -0
- package/dist/correlation.d.ts.map +1 -0
- package/dist/correlation.js +183 -0
- package/dist/correlation.js.map +1 -0
- package/dist/delegate-types.d.ts +140 -0
- package/dist/delegate-types.d.ts.map +1 -0
- package/dist/delegate-types.js +8 -0
- package/dist/delegate-types.js.map +1 -0
- package/dist/delegate.d.ts +4 -128
- package/dist/delegate.d.ts.map +1 -1
- package/dist/delegate.js +68 -72
- package/dist/delegate.js.map +1 -1
- package/dist/delegation-approval.d.ts +36 -0
- package/dist/delegation-approval.d.ts.map +1 -0
- package/dist/delegation-approval.js +67 -0
- package/dist/delegation-approval.js.map +1 -0
- package/dist/git-identity.d.ts +13 -0
- package/dist/git-identity.d.ts.map +1 -0
- package/dist/git-identity.js +44 -0
- package/dist/git-identity.js.map +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/lease-record.d.ts +78 -0
- package/dist/lease-record.d.ts.map +1 -0
- package/dist/lease-record.js +52 -0
- package/dist/lease-record.js.map +1 -0
- package/dist/ledger-events.d.ts +87 -0
- package/dist/ledger-events.d.ts.map +1 -0
- package/dist/ledger-events.js +45 -0
- package/dist/ledger-events.js.map +1 -0
- package/dist/ledger-report.d.ts +19 -12
- package/dist/ledger-report.d.ts.map +1 -1
- package/dist/ledger-report.js +81 -1
- package/dist/ledger-report.js.map +1 -1
- package/dist/ledger.d.ts +44 -1
- package/dist/ledger.d.ts.map +1 -1
- package/dist/ledger.js +20 -2
- package/dist/ledger.js.map +1 -1
- package/dist/refusals.d.ts +16 -0
- package/dist/refusals.d.ts.map +1 -0
- package/dist/refusals.js +51 -0
- package/dist/refusals.js.map +1 -0
- package/dist/run-child.d.ts +4 -0
- package/dist/run-child.d.ts.map +1 -1
- package/dist/run-child.js +21 -1
- package/dist/run-child.js.map +1 -1
- package/dist/run-herdr.d.ts +7 -0
- package/dist/run-herdr.d.ts.map +1 -1
- package/dist/run-herdr.js +41 -12
- package/dist/run-herdr.js.map +1 -1
- package/dist/workspace-lease.d.ts +28 -0
- package/dist/workspace-lease.d.ts.map +1 -0
- package/dist/workspace-lease.js +276 -0
- package/dist/workspace-lease.js.map +1 -0
- package/dist/workspace.d.ts +32 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +79 -0
- package/dist/workspace.js.map +1 -0
- package/extensions/approval-banking.ts +66 -0
- package/extensions/approvals.ts +95 -7
- package/extensions/chain-approval-facts.ts +51 -0
- package/extensions/chain-ledger.ts +48 -0
- package/extensions/delegate-chain.ts +115 -81
- package/extensions/delegation.ts +81 -23
- package/extensions/execute-child.ts +289 -0
- package/extensions/fanout-outcome.ts +97 -0
- package/extensions/run-delegation.ts +130 -120
- package/extensions/session.ts +4 -0
- package/extensions/workspace-runtime.ts +165 -0
- package/package.json +17 -1
- package/src/approval-prompt.ts +4 -2
- package/src/approval-store.ts +4 -1
- package/src/approval.ts +47 -14
- package/src/chain.ts +3 -1
- package/src/check-runner.ts +341 -0
- package/src/correlation.ts +260 -0
- package/src/delegate-types.ts +144 -0
- package/src/delegate.ts +80 -179
- package/src/delegation-approval.ts +99 -0
- package/src/git-identity.ts +52 -0
- package/src/index.ts +50 -0
- package/src/lease-record.ts +119 -0
- package/src/ledger-events.ts +138 -0
- package/src/ledger-report.ts +90 -2
- package/src/ledger.ts +68 -3
- package/src/refusals.ts +66 -0
- package/src/run-child.ts +23 -1
- package/src/run-herdr.ts +41 -11
- package/src/workspace-lease.ts +303 -0
- package/src/workspace.ts +135 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { type CorrelationMetadata } from "./correlation.ts";
|
|
2
|
+
import { type ValidatedWorkspace, type WorkspaceAccess } from "./workspace.ts";
|
|
3
|
+
export interface CheckDefinition {
|
|
4
|
+
/** Absolute operator-owned executable. PATH lookup is deliberately not part of the contract. */
|
|
5
|
+
executable: string;
|
|
6
|
+
/** Exact argv elements. Never a shell command string. */
|
|
7
|
+
argv: string[];
|
|
8
|
+
/** Fixed non-sensitive environment values. */
|
|
9
|
+
env?: Record<string, string>;
|
|
10
|
+
/** Explicit inherited names. Sensitive names are still removed. */
|
|
11
|
+
inherit_env?: string[];
|
|
12
|
+
timeout_ms?: number;
|
|
13
|
+
max_output_bytes?: number;
|
|
14
|
+
/** Checks are assumed capable of writing unless the operator declares read. */
|
|
15
|
+
workspace_access?: WorkspaceAccess;
|
|
16
|
+
}
|
|
17
|
+
export interface CheckRegistry {
|
|
18
|
+
version: 1;
|
|
19
|
+
checks: Record<string, CheckDefinition>;
|
|
20
|
+
}
|
|
21
|
+
export interface CheckReceipt {
|
|
22
|
+
schema_version: "1.0";
|
|
23
|
+
receipt_id: string;
|
|
24
|
+
check_id: string;
|
|
25
|
+
executable: string;
|
|
26
|
+
executable_sha256: string;
|
|
27
|
+
argv: string[];
|
|
28
|
+
argv_sha256: string;
|
|
29
|
+
cwd: string;
|
|
30
|
+
cwd_sha256: string;
|
|
31
|
+
started_at: string;
|
|
32
|
+
ended_at: string;
|
|
33
|
+
exit_code: number | null;
|
|
34
|
+
signal: NodeJS.Signals | null;
|
|
35
|
+
timed_out: boolean;
|
|
36
|
+
aborted: boolean;
|
|
37
|
+
truncated: boolean;
|
|
38
|
+
output_sha256: string;
|
|
39
|
+
workspace_id: string;
|
|
40
|
+
workspace_access: WorkspaceAccess;
|
|
41
|
+
head_sha: string;
|
|
42
|
+
tree_sha: string;
|
|
43
|
+
correlation?: CorrelationMetadata;
|
|
44
|
+
}
|
|
45
|
+
export declare function buildCheckEnvironment(definition: CheckDefinition, inherited?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
46
|
+
export declare function runNamedCheck(input: {
|
|
47
|
+
checkId: string;
|
|
48
|
+
registry: CheckRegistry;
|
|
49
|
+
workspace: ValidatedWorkspace;
|
|
50
|
+
correlation?: CorrelationMetadata;
|
|
51
|
+
inheritedEnv?: NodeJS.ProcessEnv;
|
|
52
|
+
signal?: AbortSignal;
|
|
53
|
+
leaseDir?: string;
|
|
54
|
+
ledgerPath?: string;
|
|
55
|
+
}): Promise<{
|
|
56
|
+
output: string;
|
|
57
|
+
exitCode: number | null;
|
|
58
|
+
signal: NodeJS.Signals | null;
|
|
59
|
+
receipt: CheckReceipt;
|
|
60
|
+
}>;
|
|
61
|
+
//# sourceMappingURL=check-runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-runner.d.ts","sourceRoot":"","sources":["../src/check-runner.ts"],"names":[],"mappings":"AAIA,OAAO,EAAwB,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAUlF,OAAO,EAGL,KAAK,kBAAkB,EACvB,KAAK,eAAe,EAGrB,MAAM,gBAAgB,CAAC;AAExB,MAAM,WAAW,eAAe;IAC9B,gGAAgG;IAChG,UAAU,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,8CAA8C;IAC9C,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,+EAA+E;IAC/E,gBAAgB,CAAC,EAAE,eAAe,CAAC;CACpC;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,CAAC,CAAC;IACX,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;CACzC;AAED,MAAM,WAAW,YAAY;IAC3B,cAAc,EAAE,KAAK,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,eAAe,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,mBAAmB,CAAC;CACnC;AAKD,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,eAAe,EAC3B,SAAS,GAAE,MAAM,CAAC,UAAwB,GACzC,MAAM,CAAC,UAAU,CAWnB;AA4BD,wBAAsB,aAAa,CAAC,KAAK,EAAE;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,aAAa,CAAC;IACxB,SAAS,EAAE,kBAAkB,CAAC;IAC9B,WAAW,CAAC,EAAE,mBAAmB,CAAC;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACjC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC;IACV,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;IAC9B,OAAO,EAAE,YAAY,CAAC;CACvB,CAAC,CAqND"}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { chmod, mkdtemp, readFile, realpath, rm, stat, writeFile } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { basename, isAbsolute, join } from "node:path";
|
|
5
|
+
import { normaliseCorrelation } from "./correlation.js";
|
|
6
|
+
import { appendLedgerEvent, buildWorkspaceLeaseEvent, LEDGER_VERSION, } from "./ledger.js";
|
|
7
|
+
import { computeGitCandidateIdentity } from "./git-identity.js";
|
|
8
|
+
import { runChild } from "./run-child.js";
|
|
9
|
+
import { GovernanceRefusal, refusal } from "./refusals.js";
|
|
10
|
+
import { acquireWorkspaceLease, defaultWorkspaceLeaseDir, leaseAcquisitionOutcome, leaseReleaseLedgerOutcome, } from "./workspace.js";
|
|
11
|
+
const SAFE_ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
12
|
+
const SENSITIVE_ENV_NAME = /(?:^|_)(?:TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|AUTH|COOKIE|PRIVATE_KEY|API_KEY)(?:_|$)|^PI_GRANTS_|^(?:AWS|GITHUB|GITLAB|AZURE|GOOGLE)_/i;
|
|
13
|
+
export function buildCheckEnvironment(definition, inherited = process.env) {
|
|
14
|
+
const env = {};
|
|
15
|
+
for (const name of definition.inherit_env ?? []) {
|
|
16
|
+
if (!SAFE_ENV_NAME.test(name) || SENSITIVE_ENV_NAME.test(name))
|
|
17
|
+
continue;
|
|
18
|
+
if (inherited[name] !== undefined)
|
|
19
|
+
env[name] = inherited[name];
|
|
20
|
+
}
|
|
21
|
+
for (const [name, value] of Object.entries(definition.env ?? {})) {
|
|
22
|
+
if (!SAFE_ENV_NAME.test(name) || SENSITIVE_ENV_NAME.test(name))
|
|
23
|
+
continue;
|
|
24
|
+
env[name] = value;
|
|
25
|
+
}
|
|
26
|
+
return env;
|
|
27
|
+
}
|
|
28
|
+
function validateDefinition(checkId, value) {
|
|
29
|
+
if (!value) {
|
|
30
|
+
throw new GovernanceRefusal(refusal("CHECK_NOT_CONFIGURED", `check ${JSON.stringify(checkId)} is not present in operator-owned configuration`, { check_id: checkId }));
|
|
31
|
+
}
|
|
32
|
+
if (!isAbsolute(value.executable) || !Array.isArray(value.argv) || value.argv.some((item) => typeof item !== "string")) {
|
|
33
|
+
throw new GovernanceRefusal(refusal("CHECK_CONFIGURATION_INVALID", `check ${JSON.stringify(checkId)} requires an absolute executable and argv string array`, { check_id: checkId }));
|
|
34
|
+
}
|
|
35
|
+
for (const bound of [value.timeout_ms, value.max_output_bytes]) {
|
|
36
|
+
if (bound !== undefined && (!Number.isInteger(bound) || bound <= 0)) {
|
|
37
|
+
throw new GovernanceRefusal(refusal("CHECK_CONFIGURATION_INVALID", `check ${JSON.stringify(checkId)} has a non-positive runtime bound`, { check_id: checkId }));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
42
|
+
function receiptId(receipt) {
|
|
43
|
+
return createHash("sha256").update(JSON.stringify(receipt), "utf8").digest("hex");
|
|
44
|
+
}
|
|
45
|
+
export async function runNamedCheck(input) {
|
|
46
|
+
if (input.registry?.version !== 1 || !input.registry.checks || typeof input.registry.checks !== "object") {
|
|
47
|
+
throw new GovernanceRefusal(refusal("CHECK_CONFIGURATION_INVALID", "check registry must contain {version:1, checks:{...}}"));
|
|
48
|
+
}
|
|
49
|
+
const definition = validateDefinition(input.checkId, input.registry.checks[input.checkId]);
|
|
50
|
+
let executable;
|
|
51
|
+
try {
|
|
52
|
+
executable = await realpath(definition.executable);
|
|
53
|
+
if (!(await stat(executable)).isFile())
|
|
54
|
+
throw new Error("not a file");
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
throw new GovernanceRefusal(refusal("CHECK_CONFIGURATION_INVALID", `check ${JSON.stringify(input.checkId)} executable is not a real file (${String(error)})`, { check_id: input.checkId }));
|
|
58
|
+
}
|
|
59
|
+
const argvSha256 = createHash("sha256").update(JSON.stringify(definition.argv), "utf8").digest("hex");
|
|
60
|
+
const cwdSha256 = createHash("sha256").update(input.workspace.root, "utf8").digest("hex");
|
|
61
|
+
const access = definition.workspace_access ?? "write";
|
|
62
|
+
// Evidence checks coordinate exclusively so their pre/post candidate identities cannot overlap another
|
|
63
|
+
// governed writer. `access` still records what the configured executable is intended to do.
|
|
64
|
+
const leaseAccess = "write";
|
|
65
|
+
const ownerId = `check:${input.checkId}:${randomUUID()}`;
|
|
66
|
+
const correlation = normaliseCorrelation(input.correlation);
|
|
67
|
+
let lease;
|
|
68
|
+
let stagedDir;
|
|
69
|
+
/** Failures of best-effort RECORDS, reported alongside whatever actually happened — never instead of it. */
|
|
70
|
+
const notes = [];
|
|
71
|
+
let releaseReason = "failed";
|
|
72
|
+
try {
|
|
73
|
+
try {
|
|
74
|
+
lease = await acquireWorkspaceLease({
|
|
75
|
+
workspace: input.workspace, access: leaseAccess, leaseDir: input.leaseDir ?? defaultWorkspaceLeaseDir(),
|
|
76
|
+
ownerId, signal: input.signal,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
if (input.ledgerPath)
|
|
81
|
+
await appendLedgerEvent({ path: input.ledgerPath, strict: true }, buildWorkspaceLeaseEvent({
|
|
82
|
+
childId: ownerId, workspaceId: input.workspace.workspaceId, root: input.workspace.root, access: leaseAccess,
|
|
83
|
+
outcome: "refused",
|
|
84
|
+
correlation, now: new Date(),
|
|
85
|
+
...(error instanceof GovernanceRefusal
|
|
86
|
+
? { refusal: { code: error.code, message: error.message, ...(error.details ? { details: error.details } : {}) } }
|
|
87
|
+
: {}),
|
|
88
|
+
}));
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
if (input.ledgerPath) {
|
|
92
|
+
try {
|
|
93
|
+
await appendLedgerEvent({ path: input.ledgerPath, strict: true }, buildWorkspaceLeaseEvent({
|
|
94
|
+
childId: ownerId, workspaceId: input.workspace.workspaceId, root: input.workspace.root, access: leaseAccess,
|
|
95
|
+
outcome: leaseAcquisitionOutcome(leaseAccess, lease.recovered), recovered: lease.recovered,
|
|
96
|
+
correlation, now: new Date(),
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
await lease.release("ledger-failed");
|
|
101
|
+
lease = undefined;
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const before = await computeGitCandidateIdentity(input.workspace);
|
|
106
|
+
for (const [name, supplied, actual] of [
|
|
107
|
+
["head_sha", correlation?.head_sha, before.headSha], ["tree_sha", correlation?.tree_sha, before.treeSha],
|
|
108
|
+
]) {
|
|
109
|
+
if (supplied && supplied !== actual)
|
|
110
|
+
throw new GovernanceRefusal(refusal("CHECK_IDENTITY_MISMATCH", `check ${JSON.stringify(input.checkId)} ${name} does not match the validated workspace`, { check_id: input.checkId, supplied, actual }));
|
|
111
|
+
}
|
|
112
|
+
const executableBytes = await readFile(executable);
|
|
113
|
+
const executableSha256 = createHash("sha256").update(executableBytes).digest("hex");
|
|
114
|
+
stagedDir = await mkdtemp(join(tmpdir(), "pi-daddy-check-exec-"));
|
|
115
|
+
const stagedExecutable = join(stagedDir, basename(executable));
|
|
116
|
+
await writeFile(stagedExecutable, executableBytes, { mode: 0o700 });
|
|
117
|
+
await chmod(stagedExecutable, 0o700);
|
|
118
|
+
const leaseAbort = new AbortController();
|
|
119
|
+
let leaseLost = false;
|
|
120
|
+
lease.lost.then(() => { leaseLost = true; leaseAbort.abort(); });
|
|
121
|
+
const checkSignal = input.signal ? AbortSignal.any([input.signal, leaseAbort.signal]) : leaseAbort.signal;
|
|
122
|
+
const started = new Date();
|
|
123
|
+
const result = await runChild({
|
|
124
|
+
command: "setpriv", args: ["--pdeathsig", "KILL", "--", stagedExecutable, ...definition.argv],
|
|
125
|
+
env: buildCheckEnvironment(definition, input.inheritedEnv), cwd: input.workspace.root,
|
|
126
|
+
signal: checkSignal, timeoutMs: definition.timeout_ms, maxOutputBytes: definition.max_output_bytes,
|
|
127
|
+
onSpawn: (pid) => lease.attachProcess(pid),
|
|
128
|
+
});
|
|
129
|
+
const ended = new Date();
|
|
130
|
+
releaseReason = input.signal?.aborted ? "cancelled" : result.timedOut ? "timeout" : result.aborted ? "failed" : "completed";
|
|
131
|
+
// Every throw below re-stamps this. Without that, a check refused for CHECK_IDENTITY_MISMATCH still
|
|
132
|
+
// wrote `released / completed` from the `finally` — the LAST lease event for that owner — so the trail
|
|
133
|
+
// still read like a clean run and `verifyLedger` still counted a clean release. The R-114 fix added a
|
|
134
|
+
// refusal line beside the misleading one instead of correcting it.
|
|
135
|
+
if (result.spawnError)
|
|
136
|
+
throw new GovernanceRefusal(refusal("EXECUTOR_UNAVAILABLE", `check ${JSON.stringify(input.checkId)} could not start its constrained executor`, { check_id: input.checkId }));
|
|
137
|
+
if (leaseLost)
|
|
138
|
+
throw new GovernanceRefusal(refusal("WORKSPACE_LEASE_STALE", `check ${JSON.stringify(input.checkId)} lost its workspace lease while running`, { check_id: input.checkId }));
|
|
139
|
+
const after = await computeGitCandidateIdentity(input.workspace);
|
|
140
|
+
if (leaseLost)
|
|
141
|
+
throw new GovernanceRefusal(refusal("WORKSPACE_LEASE_STALE", `check ${JSON.stringify(input.checkId)} lost its workspace lease before receipt`, { check_id: input.checkId }));
|
|
142
|
+
if (before.headSha !== after.headSha || before.treeSha !== after.treeSha) {
|
|
143
|
+
throw new GovernanceRefusal(refusal("CHECK_IDENTITY_MISMATCH", `check ${JSON.stringify(input.checkId)} workspace changed while it ran`, { check_id: input.checkId }));
|
|
144
|
+
}
|
|
145
|
+
// The candidate is now measured and the process is dead. End the lease before writing the receipt so
|
|
146
|
+
// lease loss cannot race a blocked ledger append and leave behind evidence from an unprotected run.
|
|
147
|
+
// Record what release actually DID. This asserted `released` unconditionally and threw the outcome
|
|
148
|
+
// away, so the evidence path reproduced the very defect the outcome union was added to expose: a lost
|
|
149
|
+
// or unrecorded handover ledgered as a clean one, with `lease = undefined` stopping the `finally` from
|
|
150
|
+
// ever noticing (R-100/R-103, found while reviewing the fix for R-99).
|
|
151
|
+
const releaseOutcome = await lease.release(releaseReason);
|
|
152
|
+
lease = undefined;
|
|
153
|
+
if (leaseLost)
|
|
154
|
+
throw new GovernanceRefusal(refusal("WORKSPACE_LEASE_STALE", `check ${JSON.stringify(input.checkId)} lost its workspace lease before receipt`, { check_id: input.checkId }));
|
|
155
|
+
if (input.ledgerPath)
|
|
156
|
+
await appendLedgerEvent({ path: input.ledgerPath, strict: true }, buildWorkspaceLeaseEvent({
|
|
157
|
+
childId: ownerId, workspaceId: input.workspace.workspaceId, root: input.workspace.root, access: leaseAccess,
|
|
158
|
+
outcome: leaseReleaseLedgerOutcome(releaseOutcome, releaseReason), releaseReason, correlation, now: new Date(),
|
|
159
|
+
}));
|
|
160
|
+
const body = {
|
|
161
|
+
schema_version: "1.0", check_id: input.checkId, executable, executable_sha256: executableSha256,
|
|
162
|
+
argv: [...definition.argv], argv_sha256: argvSha256, cwd: input.workspace.root, cwd_sha256: cwdSha256,
|
|
163
|
+
started_at: started.toISOString(), ended_at: ended.toISOString(), exit_code: result.code,
|
|
164
|
+
signal: result.signal ?? null, timed_out: result.timedOut, aborted: result.aborted, truncated: result.truncated,
|
|
165
|
+
output_sha256: createHash("sha256").update(result.text, "utf8").digest("hex"),
|
|
166
|
+
workspace_id: input.workspace.workspaceId, workspace_access: access,
|
|
167
|
+
head_sha: before.headSha, tree_sha: before.treeSha, ...(correlation ? { correlation } : {}),
|
|
168
|
+
};
|
|
169
|
+
const receipt = { receipt_id: receiptId(body), ...body };
|
|
170
|
+
if (input.ledgerPath) {
|
|
171
|
+
const event = {
|
|
172
|
+
ledgerVersion: LEDGER_VERSION, event: "check_receipt", ts: ended.toISOString(), childId: ownerId,
|
|
173
|
+
receiptId: receipt.receipt_id, workspaceId: input.workspace.workspaceId, checkId: input.checkId,
|
|
174
|
+
treeSha: receipt.tree_sha, ...(correlation ? { correlation } : {}),
|
|
175
|
+
};
|
|
176
|
+
await appendLedgerEvent({ path: input.ledgerPath, strict: true }, event);
|
|
177
|
+
}
|
|
178
|
+
return { output: result.text, exitCode: result.code, signal: result.signal ?? null, receipt };
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
// The run did not complete, so the release must not be recorded as if it had. Without this the
|
|
182
|
+
// `finally` wrote `released / completed` as the LAST lease event for a refused check, so the trail
|
|
183
|
+
// still read like a clean run and `verifyLedger` still counted a clean release — the R-114 fix put a
|
|
184
|
+
// refusal line beside the misleading one instead of correcting it.
|
|
185
|
+
if (releaseReason === "completed")
|
|
186
|
+
releaseReason = "refused";
|
|
187
|
+
// The single most important negative result this feature can produce — CHECK_IDENTITY_MISMATCH, "the
|
|
188
|
+
// workspace changed while the check ran" — used to leave NO ledger evidence at all, while the
|
|
189
|
+
// `finally` recorded `released, reason: completed`, so the trail read like a clean run (R-114).
|
|
190
|
+
if (input.ledgerPath) {
|
|
191
|
+
await appendLedgerEvent({
|
|
192
|
+
path: input.ledgerPath,
|
|
193
|
+
// Best effort, and loud: this records a failure, so it must not replace the failure. A strict
|
|
194
|
+
// append that threw here would substitute "ledger write failed" for the evidence-integrity
|
|
195
|
+
// refusal that actually happened (R-115).
|
|
196
|
+
strict: false,
|
|
197
|
+
// NOT empty. `onFailure` exists because a non-strict append is a choice not to fail closed and
|
|
198
|
+
// never a choice to be silent — the comment above says "loud", and an empty callback is the
|
|
199
|
+
// definition of swallowing. This guards the evidence record itself, so losing it silently is
|
|
200
|
+
// how a mutated workspace came to leave a trail that read like a clean run.
|
|
201
|
+
onFailure: (cause) => { notes.push(`check refusal record failed: ${String(cause)}`); },
|
|
202
|
+
}, buildWorkspaceLeaseEvent({
|
|
203
|
+
childId: ownerId, workspaceId: input.workspace.workspaceId, root: input.workspace.root, access: leaseAccess,
|
|
204
|
+
outcome: "refused",
|
|
205
|
+
refusal: error instanceof GovernanceRefusal
|
|
206
|
+
? { code: error.code, message: error.message, ...(error.details ? { details: error.details } : {}) }
|
|
207
|
+
: refusal("CHECK_IDENTITY_UNAVAILABLE", `check ${JSON.stringify(input.checkId)} failed (${String(error)})`),
|
|
208
|
+
correlation, now: new Date(),
|
|
209
|
+
}));
|
|
210
|
+
}
|
|
211
|
+
throw error;
|
|
212
|
+
}
|
|
213
|
+
finally {
|
|
214
|
+
if (stagedDir)
|
|
215
|
+
await rm(stagedDir, { recursive: true, force: true });
|
|
216
|
+
if (lease) {
|
|
217
|
+
// `release()` cannot throw (R-99), and the record of it is best-effort for the same reason as
|
|
218
|
+
// above: a `finally` that throws destroys whatever the caller was already returning or raising.
|
|
219
|
+
const outcome = await lease.release(releaseReason);
|
|
220
|
+
if (input.ledgerPath)
|
|
221
|
+
await appendLedgerEvent({
|
|
222
|
+
path: input.ledgerPath,
|
|
223
|
+
strict: false,
|
|
224
|
+
onFailure: (cause) => { notes.push(`check lease release record failed: ${String(cause)}`); },
|
|
225
|
+
}, buildWorkspaceLeaseEvent({
|
|
226
|
+
childId: ownerId, workspaceId: input.workspace.workspaceId, root: input.workspace.root, access: leaseAccess,
|
|
227
|
+
outcome: outcome === "lost"
|
|
228
|
+
? "lost"
|
|
229
|
+
: outcome === "released-unrecorded"
|
|
230
|
+
? "released-unrecorded"
|
|
231
|
+
: releaseReason === "timeout" ? "timeout" : "released",
|
|
232
|
+
releaseReason, correlation, now: new Date(),
|
|
233
|
+
}));
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
//# sourceMappingURL=check-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-runner.js","sourceRoot":"","sources":["../src/check-runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { DefinitionDigest } from "./definitions.ts";
|
|
2
|
+
import type { Capability } from "./resolve.ts";
|
|
3
|
+
export type JsonPrimitive = string | number | boolean | null;
|
|
4
|
+
export type JsonValue = JsonPrimitive | JsonValue[] | {
|
|
5
|
+
[key: string]: JsonValue;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Non-authoritative workflow metadata copied onto runtime and ledger events.
|
|
9
|
+
*
|
|
10
|
+
* pi-daddy deliberately does not interpret assurance labels, sources, scope selectors, timestamps or
|
|
11
|
+
* sequence floors. In particular, a digest-looking value here never substitutes for a digest computed by
|
|
12
|
+
* the planner. The snake_case names are the wire vocabulary used by external controllers.
|
|
13
|
+
*/
|
|
14
|
+
export interface CorrelationMetadata {
|
|
15
|
+
schema_version?: string;
|
|
16
|
+
run_id?: string;
|
|
17
|
+
task_id?: string;
|
|
18
|
+
workspace_id?: string;
|
|
19
|
+
context_id?: string;
|
|
20
|
+
phase?: string;
|
|
21
|
+
assurance?: string;
|
|
22
|
+
assurance_effective?: string;
|
|
23
|
+
policy_label?: string;
|
|
24
|
+
assurance_source?: string;
|
|
25
|
+
assurance_scope?: JsonValue;
|
|
26
|
+
activated_at?: string;
|
|
27
|
+
plan_digest?: string;
|
|
28
|
+
definition_digest?: string;
|
|
29
|
+
task_digest?: string;
|
|
30
|
+
base_sha?: string;
|
|
31
|
+
head_sha?: string;
|
|
32
|
+
tree_sha?: string;
|
|
33
|
+
event_seq?: number;
|
|
34
|
+
last_change_seq?: number;
|
|
35
|
+
last_authority_seq?: number;
|
|
36
|
+
check_receipt_id?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Snapshot bounded JSON metadata so a caller cannot mutate a record after planning, and so nothing
|
|
40
|
+
* unbounded or undeclared can reach the ledger through it.
|
|
41
|
+
*
|
|
42
|
+
* Refuses with a stable code rather than a bare `Error`: this is reachable from a model-facing tool
|
|
43
|
+
* parameter on all three delegation tools, and it used to throw outside every try, producing a governed
|
|
44
|
+
* refusal with no code and no ledger line at all (R-112).
|
|
45
|
+
*/
|
|
46
|
+
export declare function normaliseCorrelation(input: CorrelationMetadata | undefined): CorrelationMetadata | undefined;
|
|
47
|
+
export declare function digestTask(task: string): string;
|
|
48
|
+
export declare function digestCapabilities(capabilities: readonly Capability[]): string;
|
|
49
|
+
/** Trusted, internally-computed scope of one gated approval. */
|
|
50
|
+
export interface ApprovalBinding {
|
|
51
|
+
version: "1";
|
|
52
|
+
task_sha256: string;
|
|
53
|
+
requested_sha256: string;
|
|
54
|
+
effective_sha256: string;
|
|
55
|
+
requested: Capability[];
|
|
56
|
+
effective: Capability[];
|
|
57
|
+
definition_sha256?: string;
|
|
58
|
+
workspace_id?: string;
|
|
59
|
+
context_id?: string;
|
|
60
|
+
parent_id: string;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Builds the binding from TRUSTED values only. It deliberately does not take a `CorrelationMetadata`.
|
|
64
|
+
*
|
|
65
|
+
* It used to read `workspace_id` and `context_id` straight out of caller-supplied correlation into what
|
|
66
|
+
* the comment above calls the trusted scope, and a bound approval could then be spent outside the
|
|
67
|
+
* workspace it named (R-110): approve a delegation carrying a real, registry-validated `workspace` spec,
|
|
68
|
+
* then re-issue the identical task with `correlation.workspace_id` set and NO `workspace` field — no
|
|
69
|
+
* registry lookup, no lease, the parent's own cwd, and every digest still matching. The guard that would
|
|
70
|
+
* have caught it can only fire when there is a spec to disagree with.
|
|
71
|
+
*
|
|
72
|
+
* `workspaceId` must therefore be the id of a workspace that was actually resolved and leased.
|
|
73
|
+
* `contextId` is a caller-declared label that nothing validates; it is included because it can only
|
|
74
|
+
* ever NARROW a binding, and a mismatch fails closed.
|
|
75
|
+
*/
|
|
76
|
+
export declare function buildApprovalBinding(input: {
|
|
77
|
+
task: string;
|
|
78
|
+
requested: readonly Capability[];
|
|
79
|
+
effective: readonly Capability[];
|
|
80
|
+
definitionSha256?: DefinitionDigest["sha256"] | string;
|
|
81
|
+
parentId: string;
|
|
82
|
+
/** Id of a workspace this delegation was actually routed to and holds a lease for. */
|
|
83
|
+
workspaceId?: string;
|
|
84
|
+
/** Caller-declared label. Narrows only; never a claim that anything was enforced. */
|
|
85
|
+
contextId?: string;
|
|
86
|
+
}): ApprovalBinding;
|
|
87
|
+
export declare function approvalBindingDigest(binding: ApprovalBinding): string;
|
|
88
|
+
export declare function isApprovalBinding(value: unknown): value is ApprovalBinding;
|
|
89
|
+
export declare function approvalBindingsEqual(a: ApprovalBinding | undefined, b: ApprovalBinding | undefined): boolean;
|
|
90
|
+
//# sourceMappingURL=correlation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"correlation.d.ts","sourceRoot":"","sources":["../src/correlation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { GovernanceRefusal, refusal } from "./refusals.js";
|
|
3
|
+
const MAX_CORRELATION_BYTES = 32 * 1024;
|
|
4
|
+
/**
|
|
5
|
+
* Per-field bound on the STRING fields — most are an id, a digest, a label or a timestamp. It does not
|
|
6
|
+
* apply to the three sequence numbers or to `assurance_scope`, which are checked separately; an earlier
|
|
7
|
+
* version of this comment claimed it covered every declared field.
|
|
8
|
+
*/
|
|
9
|
+
const MAX_CORRELATION_FIELD_CHARS = 512;
|
|
10
|
+
/** `assurance_scope` is the one structured field, so it gets its own, larger bound. */
|
|
11
|
+
const MAX_CORRELATION_SCOPE_BYTES = 4 * 1024;
|
|
12
|
+
/**
|
|
13
|
+
* The exact field set of the pinned schema 1.0 contract. Anything else is refused by name.
|
|
14
|
+
*
|
|
15
|
+
* This is a whitelist rather than a size cap because `correlation` is a MODEL-FACING tool parameter that
|
|
16
|
+
* is copied verbatim onto every append-only ledger event. `src/ledger.ts` states the invariant — capability
|
|
17
|
+
* ids, counts and identifiers only, never prompts, tool arguments or results — and ADR-0034 repeats that the
|
|
18
|
+
* ledger must never carry task text. A 32 KB "bounded JSON object" with no key whitelist and no per-field
|
|
19
|
+
* length bound satisfied neither: `assurance_scope` was declared `Type.Any()`, undeclared keys survived the
|
|
20
|
+
* round trip, and every string was unbounded, so a model could write 32 KB of arbitrary text into the
|
|
21
|
+
* ledger through it (R-111). Refusing an unknown key is also the loud option: if upstream adds a field, the
|
|
22
|
+
* refusal names it, which is an actionable break rather than a silent secrets sink.
|
|
23
|
+
*/
|
|
24
|
+
const CORRELATION_FIELDS = new Set([
|
|
25
|
+
"schema_version", "run_id", "task_id", "workspace_id", "context_id", "phase", "assurance",
|
|
26
|
+
"assurance_effective", "policy_label", "assurance_source", "assurance_scope", "activated_at",
|
|
27
|
+
"plan_digest", "definition_digest", "task_digest", "base_sha", "head_sha", "tree_sha",
|
|
28
|
+
"event_seq", "last_change_seq", "last_authority_seq", "check_receipt_id",
|
|
29
|
+
]);
|
|
30
|
+
const CORRELATION_NUMERIC = new Set([
|
|
31
|
+
"event_seq", "last_change_seq", "last_authority_seq",
|
|
32
|
+
]);
|
|
33
|
+
function correlationRefusal(message, details) {
|
|
34
|
+
return new GovernanceRefusal(refusal("CORRELATION_INVALID", `correlation metadata: ${message}`, details));
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Size refusals get their own code, because they call for a different response: "you sent too much" is
|
|
38
|
+
* retryable by truncating, "you sent a field we do not recognise" is not. `CORRELATION_TOO_LARGE` was
|
|
39
|
+
* declared in the taxonomy and constructed nowhere, so the union advertised a distinction the code did not
|
|
40
|
+
* make — and the enumeration test kept the dead member green.
|
|
41
|
+
*/
|
|
42
|
+
function correlationTooLarge(message, details) {
|
|
43
|
+
return new GovernanceRefusal(refusal("CORRELATION_TOO_LARGE", `correlation metadata: ${message}`, details));
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Snapshot bounded JSON metadata so a caller cannot mutate a record after planning, and so nothing
|
|
47
|
+
* unbounded or undeclared can reach the ledger through it.
|
|
48
|
+
*
|
|
49
|
+
* Refuses with a stable code rather than a bare `Error`: this is reachable from a model-facing tool
|
|
50
|
+
* parameter on all three delegation tools, and it used to throw outside every try, producing a governed
|
|
51
|
+
* refusal with no code and no ledger line at all (R-112).
|
|
52
|
+
*/
|
|
53
|
+
export function normaliseCorrelation(input) {
|
|
54
|
+
if (input === undefined)
|
|
55
|
+
return undefined;
|
|
56
|
+
let encoded;
|
|
57
|
+
try {
|
|
58
|
+
encoded = JSON.stringify(input);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
throw correlationRefusal(`must be JSON serializable (${String(error)})`);
|
|
62
|
+
}
|
|
63
|
+
if (encoded === undefined)
|
|
64
|
+
throw correlationRefusal("must be a JSON object");
|
|
65
|
+
if (Buffer.byteLength(encoded) > MAX_CORRELATION_BYTES) {
|
|
66
|
+
throw correlationTooLarge(`exceeds ${MAX_CORRELATION_BYTES} bytes`, { limit: MAX_CORRELATION_BYTES });
|
|
67
|
+
}
|
|
68
|
+
const parsed = JSON.parse(encoded);
|
|
69
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
70
|
+
throw correlationRefusal("must be a JSON object");
|
|
71
|
+
}
|
|
72
|
+
const source = parsed;
|
|
73
|
+
const undeclared = Object.keys(source).filter((key) => !CORRELATION_FIELDS.has(key));
|
|
74
|
+
if (undeclared.length > 0) {
|
|
75
|
+
throw correlationRefusal(`carries fields outside the pinned schema 1.0 contract: ${undeclared.sort().join(", ")}`, { undeclared: undeclared.sort().join(",") });
|
|
76
|
+
}
|
|
77
|
+
const output = {};
|
|
78
|
+
for (const [key, value] of Object.entries(source)) {
|
|
79
|
+
if (value === undefined || value === null)
|
|
80
|
+
continue;
|
|
81
|
+
if (key === "assurance_scope") {
|
|
82
|
+
const size = Buffer.byteLength(JSON.stringify(value) ?? "");
|
|
83
|
+
if (size > MAX_CORRELATION_SCOPE_BYTES) {
|
|
84
|
+
throw correlationTooLarge(`assurance_scope exceeds ${MAX_CORRELATION_SCOPE_BYTES} bytes`, { limit: MAX_CORRELATION_SCOPE_BYTES, actual: size });
|
|
85
|
+
}
|
|
86
|
+
output[key] = value;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (CORRELATION_NUMERIC.has(key)) {
|
|
90
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
91
|
+
throw correlationRefusal(`${key} must be a finite number`, { field: key });
|
|
92
|
+
}
|
|
93
|
+
output[key] = value;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (typeof value !== "string")
|
|
97
|
+
throw correlationRefusal(`${key} must be a string`, { field: key });
|
|
98
|
+
if (value.length > MAX_CORRELATION_FIELD_CHARS) {
|
|
99
|
+
throw correlationTooLarge(`${key} exceeds ${MAX_CORRELATION_FIELD_CHARS} characters`, { field: key, limit: MAX_CORRELATION_FIELD_CHARS, actual: value.length });
|
|
100
|
+
}
|
|
101
|
+
output[key] = value;
|
|
102
|
+
}
|
|
103
|
+
return output;
|
|
104
|
+
}
|
|
105
|
+
export function digestTask(task) {
|
|
106
|
+
return createHash("sha256").update(task, "utf8").digest("hex");
|
|
107
|
+
}
|
|
108
|
+
export function digestCapabilities(capabilities) {
|
|
109
|
+
const canonical = [...new Set(capabilities)].sort();
|
|
110
|
+
return createHash("sha256").update(JSON.stringify(canonical), "utf8").digest("hex");
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Builds the binding from TRUSTED values only. It deliberately does not take a `CorrelationMetadata`.
|
|
114
|
+
*
|
|
115
|
+
* It used to read `workspace_id` and `context_id` straight out of caller-supplied correlation into what
|
|
116
|
+
* the comment above calls the trusted scope, and a bound approval could then be spent outside the
|
|
117
|
+
* workspace it named (R-110): approve a delegation carrying a real, registry-validated `workspace` spec,
|
|
118
|
+
* then re-issue the identical task with `correlation.workspace_id` set and NO `workspace` field — no
|
|
119
|
+
* registry lookup, no lease, the parent's own cwd, and every digest still matching. The guard that would
|
|
120
|
+
* have caught it can only fire when there is a spec to disagree with.
|
|
121
|
+
*
|
|
122
|
+
* `workspaceId` must therefore be the id of a workspace that was actually resolved and leased.
|
|
123
|
+
* `contextId` is a caller-declared label that nothing validates; it is included because it can only
|
|
124
|
+
* ever NARROW a binding, and a mismatch fails closed.
|
|
125
|
+
*/
|
|
126
|
+
export function buildApprovalBinding(input) {
|
|
127
|
+
const requested = [...new Set(input.requested)].sort();
|
|
128
|
+
const effective = [...new Set(input.effective)].sort();
|
|
129
|
+
return {
|
|
130
|
+
version: "1",
|
|
131
|
+
task_sha256: digestTask(input.task),
|
|
132
|
+
requested_sha256: digestCapabilities(requested),
|
|
133
|
+
effective_sha256: digestCapabilities(effective),
|
|
134
|
+
requested,
|
|
135
|
+
effective,
|
|
136
|
+
...(input.definitionSha256 ? { definition_sha256: input.definitionSha256 } : {}),
|
|
137
|
+
...(input.workspaceId ? { workspace_id: input.workspaceId } : {}),
|
|
138
|
+
...(input.contextId ? { context_id: input.contextId } : {}),
|
|
139
|
+
parent_id: input.parentId,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
export function approvalBindingDigest(binding) {
|
|
143
|
+
const ordered = {
|
|
144
|
+
version: binding.version,
|
|
145
|
+
task_sha256: binding.task_sha256,
|
|
146
|
+
requested_sha256: binding.requested_sha256,
|
|
147
|
+
effective_sha256: binding.effective_sha256,
|
|
148
|
+
requested: binding.requested,
|
|
149
|
+
effective: binding.effective,
|
|
150
|
+
definition_sha256: binding.definition_sha256 ?? null,
|
|
151
|
+
workspace_id: binding.workspace_id ?? null,
|
|
152
|
+
context_id: binding.context_id ?? null,
|
|
153
|
+
parent_id: binding.parent_id,
|
|
154
|
+
};
|
|
155
|
+
return createHash("sha256").update(JSON.stringify(ordered), "utf8").digest("hex");
|
|
156
|
+
}
|
|
157
|
+
export function isApprovalBinding(value) {
|
|
158
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
159
|
+
return false;
|
|
160
|
+
const binding = value;
|
|
161
|
+
return binding.version === "1" &&
|
|
162
|
+
[binding.task_sha256, binding.requested_sha256, binding.effective_sha256].every((digest) => typeof digest === "string" && /^[a-f0-9]{64}$/i.test(digest)) &&
|
|
163
|
+
Array.isArray(binding.requested) && binding.requested.every((item) => typeof item === "string") &&
|
|
164
|
+
Array.isArray(binding.effective) && binding.effective.every((item) => typeof item === "string") &&
|
|
165
|
+
typeof binding.parent_id === "string" &&
|
|
166
|
+
["definition_sha256", "workspace_id", "context_id"].every((key) => {
|
|
167
|
+
const value = binding[key];
|
|
168
|
+
return value === undefined || typeof value === "string";
|
|
169
|
+
}) &&
|
|
170
|
+
// Self-consistency. This guard's only trust boundary is a binding parsed off DISK
|
|
171
|
+
// (`approval-store.ts`), so an internally contradictory record — digests that do not match the
|
|
172
|
+
// capability arrays sitting beside them — must be unrepresentable rather than merely unlikely.
|
|
173
|
+
binding.requested_sha256 === digestCapabilities(binding.requested) &&
|
|
174
|
+
binding.effective_sha256 === digestCapabilities(binding.effective);
|
|
175
|
+
}
|
|
176
|
+
export function approvalBindingsEqual(a, b) {
|
|
177
|
+
if (a === undefined || b === undefined)
|
|
178
|
+
return a === b;
|
|
179
|
+
if (!isApprovalBinding(a) || !isApprovalBinding(b))
|
|
180
|
+
return false;
|
|
181
|
+
return approvalBindingDigest(a) === approvalBindingDigest(b);
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=correlation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"correlation.js","sourceRoot":"","sources":["../src/correlation.ts"],"names":[],"mappings":"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"}
|