@skill-harness/adapters 0.17.0 → 0.19.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/dist/index.d.ts +0 -30
- package/dist/index.js +0 -30
- package/dist/model-pricing.d.ts +3 -0
- package/dist/model-pricing.js +24 -0
- package/dist/pi.js +26 -135
- package/package.json +5 -4
- package/prices/model-prices.json +43 -0
- package/dist/archive-access.d.ts +0 -58
- package/dist/archive-access.js +0 -61
- package/dist/archive-checkpoint.d.ts +0 -47
- package/dist/archive-checkpoint.js +0 -116
- package/dist/archive-facts.d.ts +0 -19
- package/dist/archive-facts.js +0 -20
- package/dist/archive-observer.d.ts +0 -21
- package/dist/archive-observer.js +0 -79
- package/dist/archive-policy.d.ts +0 -107
- package/dist/archive-policy.js +0 -135
- package/dist/archive-read-capability.d.ts +0 -49
- package/dist/archive-read-capability.js +0 -45
- package/dist/archive-retention-policy.d.ts +0 -52
- package/dist/archive-retention-policy.js +0 -49
- package/dist/archived-work.d.ts +0 -45
- package/dist/archived-work.js +0 -78
- package/dist/blind-intervention.d.ts +0 -35
- package/dist/blind-intervention.js +0 -218
- package/dist/codex-diagnostic-model.d.ts +0 -14
- package/dist/codex-diagnostic-model.js +0 -39
- package/dist/codex-host-observer.d.ts +0 -111
- package/dist/codex-host-observer.js +0 -309
- package/dist/codex-https.d.ts +0 -7
- package/dist/codex-https.js +0 -221
- package/dist/codex-model-profile.d.ts +0 -38
- package/dist/codex-model-profile.js +0 -54
- package/dist/codex-producer-ipc.d.ts +0 -55
- package/dist/codex-producer-ipc.js +0 -67
- package/dist/codex-sdk-transport.d.ts +0 -50
- package/dist/codex-sdk-transport.js +0 -294
- package/dist/codex-subscription.d.ts +0 -114
- package/dist/codex-subscription.js +0 -268
- package/dist/evidence-archive.d.ts +0 -65
- package/dist/evidence-archive.js +0 -225
- package/dist/execution-projection-schema.d.ts +0 -10
- package/dist/execution-projection-schema.js +0 -30
- package/dist/execution-retention-archive.d.ts +0 -69
- package/dist/execution-retention-archive.js +0 -154
- package/dist/generated/retention-v2-contract.d.ts +0 -72
- package/dist/generated/retention-v2-contract.js +0 -105
- package/dist/generated/retention-v2-json.d.ts +0 -24
- package/dist/generated/retention-v2-json.js +0 -268
- package/dist/generated/retention-v2-native-json.d.ts +0 -2
- package/dist/generated/retention-v2-native-json.js +0 -33
- package/dist/generated/retention-v2-native.d.ts +0 -58
- package/dist/generated/retention-v2-native.js +0 -155
- package/dist/generated/retention-v2-pin.d.ts +0 -2
- package/dist/generated/retention-v2-pin.js +0 -4
- package/dist/generated/retention-v2-types.d.ts +0 -49
- package/dist/generated/retention-v2-types.js +0 -2
- package/dist/generated/retention-v2-work-types.d.ts +0 -260
- package/dist/generated/retention-v2-work-types.js +0 -13
- package/dist/generated/work-v4/execution-id.d.ts +0 -5
- package/dist/generated/work-v4/execution-id.js +0 -14
- package/dist/generated/work-v4/json.d.ts +0 -24
- package/dist/generated/work-v4/json.js +0 -268
- package/dist/generated/work-v4/occurrences.d.ts +0 -226
- package/dist/generated/work-v4/occurrences.js +0 -116
- package/dist/generated/work-v4/pin.d.ts +0 -1
- package/dist/generated/work-v4/pin.js +0 -3
- package/dist/generated/work-v4/projection.d.ts +0 -294
- package/dist/generated/work-v4/projection.js +0 -238
- package/dist/generated/work-v4/reader.d.ts +0 -24
- package/dist/generated/work-v4/reader.js +0 -109
- package/dist/generated/work-v4/snapshot.d.ts +0 -28
- package/dist/generated/work-v4/snapshot.js +0 -268
- package/dist/generated/work-v4/types.d.ts +0 -260
- package/dist/generated/work-v4/types.js +0 -13
- package/dist/generated/work-v4/validation.d.ts +0 -13
- package/dist/generated/work-v4/validation.js +0 -227
- package/dist/installed-review-session.d.ts +0 -28
- package/dist/installed-review-session.js +0 -166
- package/dist/intervention-run.d.ts +0 -403
- package/dist/intervention-run.js +0 -169
- package/dist/learning-case.d.ts +0 -12
- package/dist/learning-case.js +0 -15
- package/dist/learning-journal.d.ts +0 -18
- package/dist/learning-journal.js +0 -256
- package/dist/learning-lifecycle.d.ts +0 -28
- package/dist/learning-lifecycle.js +0 -66
- package/dist/learning-trust-setup.d.ts +0 -64
- package/dist/learning-trust-setup.js +0 -55
- package/dist/learning-workspace.d.ts +0 -1084
- package/dist/learning-workspace.js +0 -705
- package/dist/principal-payload-port.d.ts +0 -25
- package/dist/principal-payload-port.js +0 -40
- package/dist/producer-product.d.ts +0 -70
- package/dist/producer-product.js +0 -186
- package/dist/producer-review.d.ts +0 -53
- package/dist/producer-review.js +0 -207
- package/dist/prompt-capture-extension.d.ts +0 -10
- package/dist/prompt-capture-extension.js +0 -26
- package/dist/prompt-provenance.d.ts +0 -22
- package/dist/prompt-provenance.js +0 -146
- package/dist/reviewed-archive-export.d.ts +0 -42
- package/dist/reviewed-archive-export.js +0 -78
- package/dist/session-observation.d.ts +0 -50
- package/dist/session-observation.js +0 -186
- package/dist/session-retention.d.ts +0 -48
- package/dist/session-retention.js +0 -121
- package/dist/trust-lifecycle.d.ts +0 -152
- package/dist/trust-lifecycle.js +0 -209
- package/dist/weekly-investigation.d.ts +0 -237
- package/dist/weekly-investigation.js +0 -330
- package/dist/work-candidates.d.ts +0 -29
- package/dist/work-candidates.js +0 -78
- package/dist/work-case-archive.d.ts +0 -25
- package/dist/work-case-archive.js +0 -63
- package/dist/work-case-review.d.ts +0 -45
- package/dist/work-case-review.js +0 -220
- package/dist/work-signal-cases.d.ts +0 -15
- package/dist/work-signal-cases.js +0 -54
- package/dist/work-signal-observation.d.ts +0 -23
- package/dist/work-signal-observation.js +0 -89
package/dist/evidence-archive.js
DELETED
|
@@ -1,225 +0,0 @@
|
|
|
1
|
-
import { closeSync, constants, fsyncSync, fstatSync, linkSync, lstatSync, mkdirSync, openSync, readSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
2
|
-
import { createHash, randomUUID } from "node:crypto";
|
|
3
|
-
import { join, parse, resolve, sep } from "node:path";
|
|
4
|
-
const LIMIT = 8 * 1024 * 1024;
|
|
5
|
-
const HASH = /^[a-f0-9]{64}$/;
|
|
6
|
-
const ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
|
|
7
|
-
const digest = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
|
8
|
-
const absent = (error) => error?.code === "ENOENT";
|
|
9
|
-
function fail(message) { throw new Error(`archive: ${message}`); }
|
|
10
|
-
function keys(value, expected) {
|
|
11
|
-
return !!value && typeof value === "object" && !Array.isArray(value)
|
|
12
|
-
&& Object.keys(value).length === expected.length && Object.keys(value).every(key => expected.includes(key));
|
|
13
|
-
}
|
|
14
|
-
function validReference(value) {
|
|
15
|
-
if (!keys(value, ["schema", "sourceId", "parser", "sha256", "bytes", "retention"]))
|
|
16
|
-
return false;
|
|
17
|
-
return value.schema === "archive-source-v1"
|
|
18
|
-
&& typeof value.sourceId === "string" && ID.test(value.sourceId)
|
|
19
|
-
&& keys(value.parser, ["id", "version"])
|
|
20
|
-
&& typeof value.parser.id === "string" && ID.test(value.parser.id)
|
|
21
|
-
&& typeof value.parser.version === "string" && ID.test(value.parser.version)
|
|
22
|
-
&& typeof value.sha256 === "string" && HASH.test(value.sha256)
|
|
23
|
-
&& Number.isSafeInteger(value.bytes) && Number(value.bytes) >= 0 && Number(value.bytes) <= LIMIT
|
|
24
|
-
&& typeof value.retention === "string" && ["exact", "redacted", "reference-only"].includes(value.retention);
|
|
25
|
-
}
|
|
26
|
-
/** Reject existing symlink ancestors. This is a trusted local-writer boundary, not an OS sandbox. */
|
|
27
|
-
function directory(path, create) {
|
|
28
|
-
if (!constants.O_NOFOLLOW || !constants.O_DIRECTORY || !constants.O_NONBLOCK)
|
|
29
|
-
fail("required filesystem flags unavailable");
|
|
30
|
-
const absolute = resolve(path);
|
|
31
|
-
let current = parse(absolute).root;
|
|
32
|
-
for (const part of absolute.slice(current.length).split(sep).filter(Boolean)) {
|
|
33
|
-
current = join(current, part);
|
|
34
|
-
let stat;
|
|
35
|
-
try {
|
|
36
|
-
stat = lstatSync(current);
|
|
37
|
-
}
|
|
38
|
-
catch (error) {
|
|
39
|
-
if (!absent(error) || !create)
|
|
40
|
-
throw error;
|
|
41
|
-
mkdirSync(current, { mode: 0o700 });
|
|
42
|
-
stat = lstatSync(current);
|
|
43
|
-
}
|
|
44
|
-
if (stat.isSymbolicLink())
|
|
45
|
-
fail("symlink directory refused");
|
|
46
|
-
if (!stat.isDirectory())
|
|
47
|
-
fail("non-directory path refused");
|
|
48
|
-
}
|
|
49
|
-
const stat = lstatSync(absolute);
|
|
50
|
-
if ((stat.mode & 0o077) !== 0)
|
|
51
|
-
fail("archive directory must be private");
|
|
52
|
-
if (process.getuid && stat.uid !== process.getuid())
|
|
53
|
-
fail("archive directory owner mismatch");
|
|
54
|
-
}
|
|
55
|
-
function syncDirectory(path) {
|
|
56
|
-
const fd = openSync(path, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
|
|
57
|
-
try {
|
|
58
|
-
fsyncSync(fd);
|
|
59
|
-
}
|
|
60
|
-
finally {
|
|
61
|
-
closeSync(fd);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
function readVerified(path, hash, limit = LIMIT) {
|
|
65
|
-
const fd = openSync(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
66
|
-
try {
|
|
67
|
-
const stat = fstatSync(fd);
|
|
68
|
-
if (!stat.isFile() || stat.size > limit || (stat.mode & 0o077) !== 0)
|
|
69
|
-
fail("invalid retained file");
|
|
70
|
-
const buffer = Buffer.alloc(limit + 1);
|
|
71
|
-
let size = 0;
|
|
72
|
-
while (size <= limit) {
|
|
73
|
-
const count = readSync(fd, buffer, size, buffer.length - size, size);
|
|
74
|
-
if (!count)
|
|
75
|
-
break;
|
|
76
|
-
size += count;
|
|
77
|
-
}
|
|
78
|
-
if (size > limit)
|
|
79
|
-
fail("retained byte limit exceeded");
|
|
80
|
-
const bytes = buffer.subarray(0, size);
|
|
81
|
-
if (digest(bytes) !== hash)
|
|
82
|
-
fail("content identity mismatch");
|
|
83
|
-
return bytes;
|
|
84
|
-
}
|
|
85
|
-
finally {
|
|
86
|
-
closeSync(fd);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
function put(root, category, bytes) {
|
|
90
|
-
const hash = digest(bytes);
|
|
91
|
-
const dir = join(root, category);
|
|
92
|
-
directory(dir, true);
|
|
93
|
-
const target = join(dir, hash);
|
|
94
|
-
try {
|
|
95
|
-
readVerified(target, hash);
|
|
96
|
-
return hash;
|
|
97
|
-
}
|
|
98
|
-
catch (error) {
|
|
99
|
-
if (!absent(error))
|
|
100
|
-
throw error;
|
|
101
|
-
}
|
|
102
|
-
const temporary = join(dir, `.pending-${randomUUID()}`);
|
|
103
|
-
let owned = false;
|
|
104
|
-
try {
|
|
105
|
-
const fd = openSync(temporary, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600);
|
|
106
|
-
owned = true;
|
|
107
|
-
try {
|
|
108
|
-
writeFileSync(fd, bytes);
|
|
109
|
-
fsyncSync(fd);
|
|
110
|
-
}
|
|
111
|
-
finally {
|
|
112
|
-
closeSync(fd);
|
|
113
|
-
}
|
|
114
|
-
try {
|
|
115
|
-
linkSync(temporary, target);
|
|
116
|
-
}
|
|
117
|
-
catch (error) {
|
|
118
|
-
if (error.code !== "EEXIST")
|
|
119
|
-
throw error;
|
|
120
|
-
readVerified(target, hash); // Never overwrite an existing corrupt or redirected object.
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
finally {
|
|
124
|
-
if (owned)
|
|
125
|
-
unlinkSync(temporary); // Only this invocation's own staging file.
|
|
126
|
-
}
|
|
127
|
-
syncDirectory(dir);
|
|
128
|
-
return hash;
|
|
129
|
-
}
|
|
130
|
-
/** External explicit ingestion only: no session writes, worker callbacks, network or automatic discovery. */
|
|
131
|
-
export function retainArchiveSource(root, input) {
|
|
132
|
-
if (!(input.bytes instanceof Uint8Array))
|
|
133
|
-
fail("bytes required");
|
|
134
|
-
if (input.bytes.byteLength > LIMIT)
|
|
135
|
-
fail("source byte limit exceeded");
|
|
136
|
-
const bytes = Buffer.from(input.bytes);
|
|
137
|
-
const reference = {
|
|
138
|
-
schema: "archive-source-v1", sourceId: input.sourceId,
|
|
139
|
-
parser: { id: input.parser?.id, version: input.parser?.version },
|
|
140
|
-
sha256: digest(bytes), bytes: bytes.length, retention: input.retention,
|
|
141
|
-
};
|
|
142
|
-
if (!validReference(reference))
|
|
143
|
-
fail("invalid metadata or retention policy");
|
|
144
|
-
directory(root, true);
|
|
145
|
-
if (reference.retention !== "reference-only")
|
|
146
|
-
put(root, "objects", bytes);
|
|
147
|
-
// A manifest becomes visible only after its required content has been persisted.
|
|
148
|
-
const manifestId = put(root, "manifests", Buffer.from(JSON.stringify(reference)));
|
|
149
|
-
syncDirectory(resolve(root));
|
|
150
|
-
return { manifestId, reference };
|
|
151
|
-
}
|
|
152
|
-
/** Metadata only for explicit archive inventory. Does not claim content availability. */
|
|
153
|
-
export function readArchiveSourceReference(root, manifestId) {
|
|
154
|
-
if (!HASH.test(manifestId))
|
|
155
|
-
return { status: 'error', reason: 'invalid manifest identity' };
|
|
156
|
-
try {
|
|
157
|
-
directory(root, false);
|
|
158
|
-
directory(join(root, 'manifests'), false);
|
|
159
|
-
const text = readVerified(join(root, 'manifests', manifestId), manifestId, 8192).toString('utf8');
|
|
160
|
-
const reference = JSON.parse(text);
|
|
161
|
-
if (!validReference(reference) || JSON.stringify(reference) !== text)
|
|
162
|
-
fail('invalid manifest');
|
|
163
|
-
return { status: 'available', reference };
|
|
164
|
-
}
|
|
165
|
-
catch (error) {
|
|
166
|
-
return absent(error) ? { status: 'missing', reason: 'manifest' } : { status: 'error', reason: 'invalid or inaccessible retained evidence' };
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
export function readArchiveSource(root, manifestId, maxBytes = LIMIT) {
|
|
170
|
-
if (!Number.isSafeInteger(maxBytes) || maxBytes < 0 || maxBytes > LIMIT)
|
|
171
|
-
return { status: "error", reason: "invalid read bound" };
|
|
172
|
-
try {
|
|
173
|
-
const manifest = readArchiveSourceReference(root, manifestId);
|
|
174
|
-
if (manifest.status !== 'available')
|
|
175
|
-
return manifest;
|
|
176
|
-
const { reference } = manifest;
|
|
177
|
-
if (reference.retention === "reference-only")
|
|
178
|
-
return { status: "missing", reason: "not-retained" };
|
|
179
|
-
if (reference.bytes > maxBytes)
|
|
180
|
-
return { status: "error", reason: "read bound exceeded" };
|
|
181
|
-
let bytes;
|
|
182
|
-
try {
|
|
183
|
-
directory(join(root, "objects"), false);
|
|
184
|
-
bytes = readVerified(join(root, "objects", reference.sha256), reference.sha256, maxBytes);
|
|
185
|
-
}
|
|
186
|
-
catch (error) {
|
|
187
|
-
if (absent(error))
|
|
188
|
-
return { status: "missing", reason: "content" };
|
|
189
|
-
throw error;
|
|
190
|
-
}
|
|
191
|
-
if (bytes.length !== reference.bytes)
|
|
192
|
-
fail("retained length mismatch");
|
|
193
|
-
return { status: "available", reference, bytes };
|
|
194
|
-
}
|
|
195
|
-
catch {
|
|
196
|
-
// No raw paths, content or exception payloads escape into exported diagnostics.
|
|
197
|
-
return { status: "error", reason: "invalid or inaccessible retained evidence" };
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
/** Syntax/byte coverage only. No semantic validation, branch inference, deduplication or acceptance. */
|
|
201
|
-
export function parseArchivedJsonl(bytes) {
|
|
202
|
-
if (bytes.byteLength > LIMIT)
|
|
203
|
-
fail("source byte limit exceeded");
|
|
204
|
-
const buffer = Buffer.from(bytes);
|
|
205
|
-
const records = [];
|
|
206
|
-
const errors = [];
|
|
207
|
-
let start = 0;
|
|
208
|
-
let line = 1;
|
|
209
|
-
while (start < buffer.length) {
|
|
210
|
-
const newline = buffer.indexOf(10, start);
|
|
211
|
-
if (newline < 0)
|
|
212
|
-
break;
|
|
213
|
-
try {
|
|
214
|
-
const text = new TextDecoder("utf-8", { fatal: true }).decode(buffer.subarray(start, newline));
|
|
215
|
-
records.push({ line, start, end: newline + 1, value: JSON.parse(text) });
|
|
216
|
-
}
|
|
217
|
-
catch {
|
|
218
|
-
errors.push({ line, start, reason: "invalid-json" });
|
|
219
|
-
}
|
|
220
|
-
start = newline + 1;
|
|
221
|
-
line++;
|
|
222
|
-
}
|
|
223
|
-
return { status: errors.length ? "error" : start === buffer.length ? "complete" : "partial", records, errors, completeBytes: start, pendingBytes: buffer.length - start };
|
|
224
|
-
}
|
|
225
|
-
//# sourceMappingURL=evidence-archive.js.map
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/** Read-model contract only. It cannot carry an acceptance or activation grant. */
|
|
2
|
-
export declare const EXECUTION_ARCHIVE_PROJECTION_SCHEMA: {
|
|
3
|
-
type: string;
|
|
4
|
-
properties: Record<string, object>;
|
|
5
|
-
required: string[];
|
|
6
|
-
additionalProperties: boolean;
|
|
7
|
-
$schema: string;
|
|
8
|
-
$id: string;
|
|
9
|
-
};
|
|
10
|
-
export declare function assertExecutionProjection(value: unknown): void;
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { Compile } from "typebox/compile";
|
|
2
|
-
const text = { type: "string", minLength: 1, maxLength: 512 };
|
|
3
|
-
const hash = { type: "string", pattern: "^[a-f0-9]{64}$" };
|
|
4
|
-
const nullable = (schema) => ({ anyOf: [schema, { type: "null" }] });
|
|
5
|
-
const list = (items) => ({ type: "array", items, maxItems: 4096, uniqueItems: true });
|
|
6
|
-
const closed = (properties) => ({ type: "object", properties, required: Object.keys(properties), additionalProperties: false });
|
|
7
|
-
function freeze(value) { if (value && typeof value === "object") {
|
|
8
|
-
Object.values(value).forEach(freeze);
|
|
9
|
-
Object.freeze(value);
|
|
10
|
-
} return value; }
|
|
11
|
-
/** Read-model contract only. It cannot carry an acceptance or activation grant. */
|
|
12
|
-
export const EXECUTION_ARCHIVE_PROJECTION_SCHEMA = freeze({
|
|
13
|
-
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
14
|
-
$id: "https://github.com/mojomanyana/skill-harness/contracts/execution-archive/v1/projection.schema.json",
|
|
15
|
-
...closed({ version: { const: "execution-archive-projection-v1" },
|
|
16
|
-
executions: { type: "array", maxItems: 4096, items: closed({
|
|
17
|
-
executionId: text, parentExecutionIds: list(nullable(text)), retainedSessionIds: list(text), activeBranch: { const: null },
|
|
18
|
-
toolCallIds: list(nullable(text)), archiveIds: list(text), runtime: { enum: ["running", "terminal", "conflict"] },
|
|
19
|
-
outcome: nullable(closed({ code: nullable({ type: "integer", minimum: -2147483648, maximum: 2147483647 }),
|
|
20
|
-
signal: nullable(text), timedOut: { type: "boolean" }, aborted: { type: "boolean" }, truncated: { type: "boolean" }, failed: { type: "boolean" } })),
|
|
21
|
-
sourceReferences: list(hash), issues: list(text), coverage: { const: "partial" }, acceptance: { const: "not-assessed" },
|
|
22
|
-
}) }, acceptance: { const: "not-assessed" },
|
|
23
|
-
}),
|
|
24
|
-
});
|
|
25
|
-
const compiled = Compile(EXECUTION_ARCHIVE_PROJECTION_SCHEMA);
|
|
26
|
-
export function assertExecutionProjection(value) {
|
|
27
|
-
if (!compiled.Check(value) || Buffer.byteLength(JSON.stringify(value)) > 8 * 1024 * 1024)
|
|
28
|
-
throw new Error("invalid or oversized execution archive projection");
|
|
29
|
-
}
|
|
30
|
-
//# sourceMappingURL=execution-projection-schema.js.map
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { RETENTION_CONTENT_KINDS } from "./generated/retention-v2-contract.js";
|
|
2
|
-
import type { ExecutionRetentionManifest } from "./generated/retention-v2-types.js";
|
|
3
|
-
export type RetentionContentKind = typeof RETENTION_CONTENT_KINDS[number];
|
|
4
|
-
export interface RetentionProjection {
|
|
5
|
-
version: "retention-projection-v1";
|
|
6
|
-
sourceSha256: string;
|
|
7
|
-
producerCommit: string;
|
|
8
|
-
archiveId: string;
|
|
9
|
-
identity: ExecutionRetentionManifest["identity"];
|
|
10
|
-
runtime: "running" | "terminal";
|
|
11
|
-
outcome: ExecutionRetentionManifest["outcome"];
|
|
12
|
-
content: Record<RetentionContentKind, {
|
|
13
|
-
status: "available" | "missing" | "mismatch";
|
|
14
|
-
sha256: string | null;
|
|
15
|
-
bytes: number | null;
|
|
16
|
-
}>;
|
|
17
|
-
sessionId: string | null;
|
|
18
|
-
reportedBranch: {
|
|
19
|
-
state: "unknown" | "observed";
|
|
20
|
-
leafId: string | null;
|
|
21
|
-
};
|
|
22
|
-
activeBranch: null;
|
|
23
|
-
coverage: "partial";
|
|
24
|
-
issues: string[];
|
|
25
|
-
acceptance: "not-assessed";
|
|
26
|
-
}
|
|
27
|
-
/** Caller already owns an exact-content policy. No paths from the producer are opened here. */
|
|
28
|
-
export declare function ingestRetainedExecution(root: string, input: {
|
|
29
|
-
manifest: Uint8Array;
|
|
30
|
-
blobs: ReadonlyMap<string, Uint8Array>;
|
|
31
|
-
retention: "exact";
|
|
32
|
-
sourceId?: string;
|
|
33
|
-
}): {
|
|
34
|
-
snapshotId: string;
|
|
35
|
-
projection: RetentionProjection;
|
|
36
|
-
};
|
|
37
|
-
/** Recompute availability from current retained bytes; a deleted blob never inherits its old available status. */
|
|
38
|
-
export declare function readRetainedExecution(root: string, snapshotId: string): {
|
|
39
|
-
snapshotId: string;
|
|
40
|
-
sourceId: string;
|
|
41
|
-
manifest: ExecutionRetentionManifest;
|
|
42
|
-
projection: RetentionProjection;
|
|
43
|
-
};
|
|
44
|
-
/** Order-independent joins, not an acceptance engine. Logical child names and native parents are not execution keys. */
|
|
45
|
-
export declare function projectRetainedExecutions(snapshots: readonly RetentionProjection[]): {
|
|
46
|
-
version: "execution-archive-projection-v1";
|
|
47
|
-
executions: {
|
|
48
|
-
executionId: string;
|
|
49
|
-
parentExecutionIds: (string | null)[];
|
|
50
|
-
retainedSessionIds: string[];
|
|
51
|
-
activeBranch: null;
|
|
52
|
-
toolCallIds: (string | null)[];
|
|
53
|
-
archiveIds: string[];
|
|
54
|
-
runtime: string;
|
|
55
|
-
outcome: {
|
|
56
|
-
code: number | null;
|
|
57
|
-
signal: string | null;
|
|
58
|
-
timedOut: boolean;
|
|
59
|
-
aborted: boolean;
|
|
60
|
-
truncated: boolean;
|
|
61
|
-
failed: boolean;
|
|
62
|
-
} | null;
|
|
63
|
-
sourceReferences: string[];
|
|
64
|
-
issues: string[];
|
|
65
|
-
coverage: "partial";
|
|
66
|
-
acceptance: "not-assessed";
|
|
67
|
-
}[];
|
|
68
|
-
acceptance: "not-assessed";
|
|
69
|
-
};
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import { parseExecutionRetentionManifest, RETENTION_CONTENT_KINDS } from "./generated/retention-v2-contract.js";
|
|
3
|
-
import { parseNativeSessionBytes } from "./generated/retention-v2-native.js";
|
|
4
|
-
import { RETENTION_V2_COMMIT } from "./generated/retention-v2-pin.js";
|
|
5
|
-
import { retainArchiveSource, readArchiveSource } from "./evidence-archive.js";
|
|
6
|
-
import { assertExecutionProjection } from "./execution-projection-schema.js";
|
|
7
|
-
const hash = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
|
8
|
-
const SHA = /^[a-f0-9]{64}$/;
|
|
9
|
-
const parseManifest = (bytes) => parseExecutionRetentionManifest(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
10
|
-
function project(manifest, manifestBytes, supplied) {
|
|
11
|
-
const content = {};
|
|
12
|
-
const issues = [...manifest.coverage.losses];
|
|
13
|
-
for (const kind of RETENTION_CONTENT_KINDS) {
|
|
14
|
-
const ref = manifest.content[kind], bytes = ref.path ? supplied.get(ref.path) : undefined;
|
|
15
|
-
const status = ref.status === "missing" || bytes === undefined ? "missing"
|
|
16
|
-
: bytes instanceof Uint8Array && bytes.byteLength === ref.bytes && hash(bytes) === ref.sha256 ? "available" : "mismatch";
|
|
17
|
-
content[kind] = { status, sha256: status === "available" ? ref.sha256 : null, bytes: status === "available" ? ref.bytes : null };
|
|
18
|
-
if (status !== "available")
|
|
19
|
-
issues.push(`content-${status}:${kind}`);
|
|
20
|
-
}
|
|
21
|
-
let sessionId = null;
|
|
22
|
-
if (content.session.status === "available") {
|
|
23
|
-
const native = manifest.nativeSession;
|
|
24
|
-
if (!native.source || !native.sessionPath || !native.sessionId)
|
|
25
|
-
issues.push("native-session-locator-unavailable");
|
|
26
|
-
else {
|
|
27
|
-
const parsed = parseNativeSessionBytes(supplied.get(manifest.content.session.path), {
|
|
28
|
-
source: native.source, path: native.sessionPath, expectedSessionId: native.sessionId,
|
|
29
|
-
truncated: native.status === "truncated",
|
|
30
|
-
...(native.branchState === "observed" ? { liveLeaf: { sessionId: native.sessionId, leafId: native.branchLeafId } } : {}),
|
|
31
|
-
});
|
|
32
|
-
// This checks retained format/chain consistency, never authenticates the manifest's live observation.
|
|
33
|
-
if (parsed.observation.status !== native.status || parsed.observation.sha256 !== native.sha256
|
|
34
|
-
|| parsed.observation.lastPersistedEntryId !== native.lastPersistedEntryId)
|
|
35
|
-
issues.push("native-session-inconsistent");
|
|
36
|
-
else
|
|
37
|
-
sessionId = parsed.observation.sessionId;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
if (manifest.nativeSession.branchState === "observed")
|
|
41
|
-
issues.push("active-branch-not-independently-verified");
|
|
42
|
-
return {
|
|
43
|
-
version: "retention-projection-v1", sourceSha256: hash(manifestBytes), producerCommit: RETENTION_V2_COMMIT,
|
|
44
|
-
archiveId: manifest.archiveId, identity: manifest.identity, runtime: manifest.state, outcome: manifest.outcome,
|
|
45
|
-
content, sessionId, reportedBranch: { state: manifest.nativeSession.branchState, leafId: manifest.nativeSession.branchLeafId },
|
|
46
|
-
activeBranch: null, coverage: "partial", issues: [...new Set(issues)].sort(), acceptance: "not-assessed",
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
/** Caller already owns an exact-content policy. No paths from the producer are opened here. */
|
|
50
|
-
export function ingestRetainedExecution(root, input) {
|
|
51
|
-
if (input.retention !== "exact")
|
|
52
|
-
throw new Error("native semantic ingestion requires explicit exact-content policy; archive redacted/reference-only data as opaque evidence");
|
|
53
|
-
const bytes = Buffer.from(input.manifest);
|
|
54
|
-
const manifest = parseManifest(bytes);
|
|
55
|
-
for (const ref of Object.values(manifest.content)) {
|
|
56
|
-
const supplied = ref.path ? input.blobs.get(ref.path) : undefined;
|
|
57
|
-
if (supplied !== undefined && (!(supplied instanceof Uint8Array) || supplied.byteLength > 1024 * 1024))
|
|
58
|
-
throw new Error("retention blob exceeds input bounds");
|
|
59
|
-
}
|
|
60
|
-
const projection = project(manifest, bytes, input.blobs);
|
|
61
|
-
const source = retainArchiveSource(root, { sourceId: input.sourceId ?? `retention-${manifest.archiveId}`, parser: { id: "pi-daddy-execution-retention", version: "2.0" }, retention: "exact", bytes });
|
|
62
|
-
const blobs = {};
|
|
63
|
-
for (const kind of RETENTION_CONTENT_KINDS) {
|
|
64
|
-
blobs[kind] = null;
|
|
65
|
-
const ref = manifest.content[kind];
|
|
66
|
-
if (!ref.path || input.blobs.get(ref.path) === undefined)
|
|
67
|
-
continue;
|
|
68
|
-
// Preserve bounded mismatched bytes as forensic input; their presence never makes them valid evidence.
|
|
69
|
-
blobs[kind] = retainArchiveSource(root, { sourceId: `${manifest.archiveId}-${kind}`, parser: { id: "opaque-bytes", version: "1" }, retention: "exact", bytes: input.blobs.get(ref.path) }).manifestId;
|
|
70
|
-
}
|
|
71
|
-
const snapshot = { version: "retention-archive-snapshot-v1", producerCommit: RETENTION_V2_COMMIT, manifestId: source.manifestId, blobs };
|
|
72
|
-
const stored = retainArchiveSource(root, { sourceId: `execution-${manifest.archiveId}`, parser: { id: "retention-archive-snapshot", version: "1" }, retention: "exact", bytes: Buffer.from(JSON.stringify(snapshot)) });
|
|
73
|
-
return { snapshotId: stored.manifestId, projection };
|
|
74
|
-
}
|
|
75
|
-
/** Recompute availability from current retained bytes; a deleted blob never inherits its old available status. */
|
|
76
|
-
export function readRetainedExecution(root, snapshotId) {
|
|
77
|
-
const stored = readArchiveSource(root, snapshotId);
|
|
78
|
-
if (stored.status !== "available" || stored.reference.retention !== "exact")
|
|
79
|
-
throw new Error("retention snapshot missing or invalid");
|
|
80
|
-
const text = stored.bytes.toString("utf8"), snapshot = JSON.parse(text);
|
|
81
|
-
if (JSON.stringify(snapshot) !== text || !snapshot || Object.keys(snapshot).sort().join() !== "blobs,manifestId,producerCommit,version"
|
|
82
|
-
|| snapshot.version !== "retention-archive-snapshot-v1" || snapshot.producerCommit !== RETENTION_V2_COMMIT || !SHA.test(snapshot.manifestId)
|
|
83
|
-
|| !snapshot.blobs || Object.keys(snapshot.blobs).sort().join() !== [...RETENTION_CONTENT_KINDS].sort().join()
|
|
84
|
-
|| Object.values(snapshot.blobs).some(id => id !== null && (typeof id !== "string" || !SHA.test(id))))
|
|
85
|
-
throw new Error("invalid retention snapshot");
|
|
86
|
-
const source = readArchiveSource(root, snapshot.manifestId);
|
|
87
|
-
if (source.status !== "available" || source.reference.retention !== "exact")
|
|
88
|
-
throw new Error("retained producer manifest missing or invalid");
|
|
89
|
-
const manifest = parseManifest(source.bytes);
|
|
90
|
-
const supplied = new Map();
|
|
91
|
-
for (const kind of RETENTION_CONTENT_KINDS) {
|
|
92
|
-
const id = snapshot.blobs[kind], ref = manifest.content[kind];
|
|
93
|
-
if (!id || !ref.path)
|
|
94
|
-
continue;
|
|
95
|
-
const content = readArchiveSource(root, id);
|
|
96
|
-
if (content.status === "available" && content.reference.retention === "exact")
|
|
97
|
-
supplied.set(ref.path, content.bytes);
|
|
98
|
-
}
|
|
99
|
-
return { snapshotId, sourceId: source.reference.sourceId, manifest, projection: project(manifest, source.bytes, supplied) };
|
|
100
|
-
}
|
|
101
|
-
/** Order-independent joins, not an acceptance engine. Logical child names and native parents are not execution keys. */
|
|
102
|
-
export function projectRetainedExecutions(snapshots) {
|
|
103
|
-
if (snapshots.length > 4096)
|
|
104
|
-
throw new Error("retention projection exceeds snapshot bound");
|
|
105
|
-
const unique = [...new Map(snapshots.map(p => [JSON.stringify(p), p])).values()];
|
|
106
|
-
const objectKey = (value) => JSON.stringify(Object.keys(value).sort().map(key => [key, value[key]]));
|
|
107
|
-
const groups = new Map();
|
|
108
|
-
for (const snapshot of unique) {
|
|
109
|
-
const id = snapshot.identity.executionId;
|
|
110
|
-
groups.set(id, [...(groups.get(id) ?? []), snapshot]);
|
|
111
|
-
}
|
|
112
|
-
const lineageIssue = (start) => {
|
|
113
|
-
const seen = new Set();
|
|
114
|
-
let id = start;
|
|
115
|
-
while (id !== null && groups.has(id)) {
|
|
116
|
-
if (seen.has(id))
|
|
117
|
-
return "cyclic-parentage";
|
|
118
|
-
seen.add(id);
|
|
119
|
-
const parents = [...new Set(groups.get(id).map(p => p.identity.parentExecutionId))];
|
|
120
|
-
if (parents.length !== 1)
|
|
121
|
-
return "ambiguous-parentage";
|
|
122
|
-
id = parents[0];
|
|
123
|
-
}
|
|
124
|
-
return null;
|
|
125
|
-
};
|
|
126
|
-
const executions = [...groups].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([executionId, values]) => {
|
|
127
|
-
const identities = new Set(values.map(p => objectKey(p.identity)));
|
|
128
|
-
const terminals = values.filter(p => p.runtime === "terminal");
|
|
129
|
-
const outcomes = new Set(terminals.map(p => objectKey(p.outcome)));
|
|
130
|
-
const issues = new Set(values.flatMap(p => p.issues));
|
|
131
|
-
if (identities.size > 1)
|
|
132
|
-
issues.add("execution-identity-conflict");
|
|
133
|
-
if (outcomes.size > 1)
|
|
134
|
-
issues.add("terminal-outcome-conflict");
|
|
135
|
-
const lineage = lineageIssue(executionId);
|
|
136
|
-
if (lineage)
|
|
137
|
-
issues.add(lineage);
|
|
138
|
-
const parentExecutionIds = [...new Set(values.map(p => p.identity.parentExecutionId))].sort();
|
|
139
|
-
const retainedSessionIds = [...new Set(values.map(p => p.sessionId).filter((id) => id !== null))].sort();
|
|
140
|
-
if (retainedSessionIds.length > 1)
|
|
141
|
-
issues.add("session-identity-conflict");
|
|
142
|
-
if (parentExecutionIds.some(id => id !== null && !groups.has(id)))
|
|
143
|
-
issues.add("parent-not-in-snapshot");
|
|
144
|
-
return { executionId, parentExecutionIds, retainedSessionIds, activeBranch: null, toolCallIds: [...new Set(values.map(p => p.identity.toolCallId))].sort(),
|
|
145
|
-
archiveIds: [...new Set(values.map(p => p.archiveId))].sort(),
|
|
146
|
-
runtime: identities.size > 1 || outcomes.size > 1 ? "conflict" : terminals.length ? "terminal" : "running",
|
|
147
|
-
outcome: identities.size === 1 && outcomes.size === 1 ? terminals[0].outcome : null,
|
|
148
|
-
sourceReferences: [...new Set(values.map(p => p.sourceSha256))].sort(), issues: [...issues].sort(), coverage: "partial", acceptance: "not-assessed" };
|
|
149
|
-
});
|
|
150
|
-
const projection = { version: "execution-archive-projection-v1", executions, acceptance: "not-assessed" };
|
|
151
|
-
assertExecutionProjection(projection);
|
|
152
|
-
return projection;
|
|
153
|
-
}
|
|
154
|
-
//# sourceMappingURL=execution-retention-archive.js.map
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import type { ExecutionRetentionManifest } from "./retention-v2-types.js";
|
|
2
|
-
export declare const RETENTION_CONTENT_KINDS: readonly ["stdout", "stderr", "paneSnapshot", "checkReceipt", "result", "session"];
|
|
3
|
-
export declare const RETENTION_SCHEMA: {
|
|
4
|
-
allOf: ({
|
|
5
|
-
if: {
|
|
6
|
-
properties: {
|
|
7
|
-
state: {
|
|
8
|
-
const: unknown;
|
|
9
|
-
};
|
|
10
|
-
nativeSession?: undefined;
|
|
11
|
-
};
|
|
12
|
-
};
|
|
13
|
-
then: {
|
|
14
|
-
properties: {
|
|
15
|
-
outcome: {
|
|
16
|
-
const: unknown;
|
|
17
|
-
};
|
|
18
|
-
nativeSession?: undefined;
|
|
19
|
-
native?: undefined;
|
|
20
|
-
};
|
|
21
|
-
};
|
|
22
|
-
else: {
|
|
23
|
-
properties: {
|
|
24
|
-
outcome: {
|
|
25
|
-
type: string;
|
|
26
|
-
};
|
|
27
|
-
};
|
|
28
|
-
};
|
|
29
|
-
} | {
|
|
30
|
-
if: {
|
|
31
|
-
properties: {
|
|
32
|
-
nativeSession: {
|
|
33
|
-
properties: {
|
|
34
|
-
branchState: {
|
|
35
|
-
const: unknown;
|
|
36
|
-
};
|
|
37
|
-
};
|
|
38
|
-
};
|
|
39
|
-
state?: undefined;
|
|
40
|
-
};
|
|
41
|
-
};
|
|
42
|
-
then: {
|
|
43
|
-
properties: {
|
|
44
|
-
nativeSession: {
|
|
45
|
-
properties: {
|
|
46
|
-
branchLeafId: {
|
|
47
|
-
const: unknown;
|
|
48
|
-
};
|
|
49
|
-
};
|
|
50
|
-
};
|
|
51
|
-
native: {
|
|
52
|
-
properties: {
|
|
53
|
-
branchLeafId: {
|
|
54
|
-
const: unknown;
|
|
55
|
-
};
|
|
56
|
-
};
|
|
57
|
-
};
|
|
58
|
-
outcome?: undefined;
|
|
59
|
-
};
|
|
60
|
-
};
|
|
61
|
-
else?: undefined;
|
|
62
|
-
})[];
|
|
63
|
-
type: string;
|
|
64
|
-
properties: Record<string, object>;
|
|
65
|
-
required: string[];
|
|
66
|
-
additionalProperties: boolean;
|
|
67
|
-
$schema: string;
|
|
68
|
-
$id: string;
|
|
69
|
-
};
|
|
70
|
-
/** Closed, detached versioned wire builder; never authenticates the supplied observation. */
|
|
71
|
-
export declare function buildExecutionRetentionManifest(value: unknown): ExecutionRetentionManifest;
|
|
72
|
-
export declare function parseExecutionRetentionManifest(text: string): ExecutionRetentionManifest;
|