@skill-harness/adapters 0.12.0 → 0.14.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/archive-access.d.ts +58 -0
- package/dist/archive-access.js +61 -0
- package/dist/archive-checkpoint.d.ts +47 -0
- package/dist/archive-checkpoint.js +116 -0
- package/dist/archive-facts.d.ts +19 -0
- package/dist/archive-facts.js +20 -0
- package/dist/archive-observer.d.ts +21 -0
- package/dist/archive-observer.js +79 -0
- package/dist/archive-policy.d.ts +107 -0
- package/dist/archive-policy.js +135 -0
- package/dist/archive-read-capability.d.ts +49 -0
- package/dist/archive-read-capability.js +45 -0
- package/dist/archive-retention-policy.d.ts +52 -0
- package/dist/archive-retention-policy.js +49 -0
- package/dist/archived-work.d.ts +44 -0
- package/dist/archived-work.js +64 -0
- package/dist/blind-intervention.d.ts +29 -0
- package/dist/blind-intervention.js +211 -0
- package/dist/codex-diagnostic-model.d.ts +14 -0
- package/dist/codex-diagnostic-model.js +39 -0
- package/dist/codex-host-observer.d.ts +111 -0
- package/dist/codex-host-observer.js +309 -0
- package/dist/codex-https.d.ts +7 -0
- package/dist/codex-https.js +221 -0
- package/dist/codex-model-profile.d.ts +38 -0
- package/dist/codex-model-profile.js +54 -0
- package/dist/codex-producer-ipc.d.ts +55 -0
- package/dist/codex-producer-ipc.js +67 -0
- package/dist/codex-sdk-transport.d.ts +50 -0
- package/dist/codex-sdk-transport.js +294 -0
- package/dist/codex-subscription.d.ts +114 -0
- package/dist/codex-subscription.js +268 -0
- package/dist/evidence-archive.d.ts +57 -0
- package/dist/evidence-archive.js +221 -0
- package/dist/execution-projection-schema.d.ts +10 -0
- package/dist/execution-projection-schema.js +30 -0
- package/dist/execution-retention-archive.d.ts +69 -0
- package/dist/execution-retention-archive.js +154 -0
- package/dist/generated/retention-v2-contract.d.ts +72 -0
- package/dist/generated/retention-v2-contract.js +105 -0
- package/dist/generated/retention-v2-json.d.ts +24 -0
- package/dist/generated/retention-v2-json.js +268 -0
- package/dist/generated/retention-v2-native-json.d.ts +2 -0
- package/dist/generated/retention-v2-native-json.js +33 -0
- package/dist/generated/retention-v2-native.d.ts +58 -0
- package/dist/generated/retention-v2-native.js +155 -0
- package/dist/generated/retention-v2-pin.d.ts +2 -0
- package/dist/generated/retention-v2-pin.js +4 -0
- package/dist/generated/retention-v2-types.d.ts +49 -0
- package/dist/generated/retention-v2-types.js +2 -0
- package/dist/generated/retention-v2-work-types.d.ts +260 -0
- package/dist/generated/retention-v2-work-types.js +13 -0
- package/dist/generated/work-v4/execution-id.d.ts +5 -0
- package/dist/generated/work-v4/execution-id.js +14 -0
- package/dist/generated/work-v4/json.d.ts +24 -0
- package/dist/generated/work-v4/json.js +268 -0
- package/dist/generated/work-v4/occurrences.d.ts +226 -0
- package/dist/generated/work-v4/occurrences.js +116 -0
- package/dist/generated/work-v4/pin.d.ts +1 -0
- package/dist/generated/work-v4/pin.js +3 -0
- package/dist/generated/work-v4/projection.d.ts +294 -0
- package/dist/generated/work-v4/projection.js +238 -0
- package/dist/generated/work-v4/reader.d.ts +24 -0
- package/dist/generated/work-v4/reader.js +109 -0
- package/dist/generated/work-v4/snapshot.d.ts +28 -0
- package/dist/generated/work-v4/snapshot.js +268 -0
- package/dist/generated/work-v4/types.d.ts +260 -0
- package/dist/generated/work-v4/types.js +13 -0
- package/dist/generated/work-v4/validation.d.ts +13 -0
- package/dist/generated/work-v4/validation.js +227 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +22 -0
- package/dist/installed-review-session.d.ts +28 -0
- package/dist/installed-review-session.js +166 -0
- package/dist/intervention-run.d.ts +403 -0
- package/dist/intervention-run.js +169 -0
- package/dist/learning-case.d.ts +12 -0
- package/dist/learning-case.js +15 -0
- package/dist/learning-journal.d.ts +18 -0
- package/dist/learning-journal.js +256 -0
- package/dist/principal-payload-port.d.ts +25 -0
- package/dist/principal-payload-port.js +40 -0
- package/dist/producer-product.d.ts +70 -0
- package/dist/producer-product.js +186 -0
- package/dist/producer-review.d.ts +53 -0
- package/dist/producer-review.js +207 -0
- package/dist/reviewed-archive-export.d.ts +42 -0
- package/dist/reviewed-archive-export.js +78 -0
- package/dist/trust-lifecycle.d.ts +128 -0
- package/dist/trust-lifecycle.js +200 -0
- package/dist/weekly-investigation.d.ts +237 -0
- package/dist/weekly-investigation.js +330 -0
- package/dist/work-candidates.d.ts +29 -0
- package/dist/work-candidates.js +78 -0
- package/dist/work-case-archive.d.ts +25 -0
- package/dist/work-case-archive.js +63 -0
- package/dist/work-case-review.d.ts +45 -0
- package/dist/work-case-review.js +220 -0
- package/dist/work-signal-cases.d.ts +15 -0
- package/dist/work-signal-cases.js +54 -0
- package/dist/work-signal-observation.d.ts +23 -0
- package/dist/work-signal-observation.js +89 -0
- package/package.json +3 -2
|
@@ -0,0 +1,10 @@
|
|
|
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;
|
|
@@ -0,0 +1,30 @@
|
|
|
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
|
|
@@ -0,0 +1,69 @@
|
|
|
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
|
+
};
|
|
@@ -0,0 +1,154 @@
|
|
|
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
|
|
@@ -0,0 +1,72 @@
|
|
|
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;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// GENERATED by scripts/vendor-execution-retention.mjs from pi-daddy 7c78769c47177b1972b09e1f5c5474ad44cd2cac. Do not hand-edit.
|
|
2
|
+
import { Compile } from "typebox/compile";
|
|
3
|
+
import { parseWorkJson } from "./retention-v2-json.js";
|
|
4
|
+
const string = (maxLength = 512) => ({ type: "string", minLength: 1, maxLength, pattern: "^[^\\u0000-\\u001f\\u007f]+$" });
|
|
5
|
+
const nullable = (schema) => ({ anyOf: [schema, { type: "null" }] });
|
|
6
|
+
const literal = (value) => ({ const: value });
|
|
7
|
+
const closed = (properties) => ({ type: "object", properties, required: Object.keys(properties), additionalProperties: false });
|
|
8
|
+
const hash = { type: "string", pattern: "^[a-f0-9]{64}$" };
|
|
9
|
+
const integer = { type: "integer", minimum: 0, maximum: Number.MAX_SAFE_INTEGER };
|
|
10
|
+
const uuid = { type: "string", pattern: "^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$" };
|
|
11
|
+
export const RETENTION_CONTENT_KINDS = ["stdout", "stderr", "paneSnapshot", "checkReceipt", "result", "session"];
|
|
12
|
+
export const RETENTION_SCHEMA = freeze({
|
|
13
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
14
|
+
$id: "https://pi-daddy.local/contracts/execution-retention/v2/manifest.schema.json",
|
|
15
|
+
...closed({
|
|
16
|
+
version: literal("2.0"), archiveId: uuid,
|
|
17
|
+
identity: closed({ executionId: string(), parentExecutionId: nullable(string()), childId: string(), toolCallId: nullable(string()),
|
|
18
|
+
executor: { enum: ["process", "herdr", "check"] }, taskDigest: nullable(hash), definitionDigest: nullable(hash),
|
|
19
|
+
configurationDigest: hash, workspaceId: nullable(string()) }),
|
|
20
|
+
native: closed({ pid: nullable({ type: "integer", minimum: 1, maximum: Number.MAX_SAFE_INTEGER }), paneId: nullable(string(4096)),
|
|
21
|
+
agentName: nullable(string(4096)), tabId: nullable(string(4096)), sessionId: nullable(uuid), sessionPath: nullable(string(4096)), branchLeafId: nullable(string(128)) }),
|
|
22
|
+
nativeSession: closed({ source: { enum: [null, "herdr-id", "herdr-path", "pi-session-file", "pi-session-manager"] },
|
|
23
|
+
status: { enum: ["missing", "verified", "invalid", "changed", "truncated", "unsupported"] },
|
|
24
|
+
sessionId: nullable(uuid), sessionPath: nullable(string(4096)), parentSessionPath: nullable(string(4096)),
|
|
25
|
+
branchLeafId: nullable(string(128)), branchState: { enum: ["unknown", "observed"] }, lastPersistedEntryId: nullable(string(128)),
|
|
26
|
+
sha256: nullable(hash), reason: nullable(string()) }),
|
|
27
|
+
state: { enum: ["running", "terminal"] },
|
|
28
|
+
outcome: nullable(closed({ code: nullable({ type: "integer", minimum: -2147483648, maximum: 2147483647 }), signal: nullable(string(64)),
|
|
29
|
+
timedOut: { type: "boolean" }, aborted: { type: "boolean" }, truncated: { type: "boolean" }, failed: { type: "boolean" } })),
|
|
30
|
+
content: closed(Object.fromEntries(RETENTION_CONTENT_KINDS.map(kind => [kind, { oneOf: [
|
|
31
|
+
closed({ status: literal("missing"), path: literal(null), sha256: literal(null), bytes: literal(null) }),
|
|
32
|
+
closed({ status: literal("retained"), path: { type: "string", pattern: `^${kind}-[a-f0-9]{64}\\.bin$` }, sha256: hash,
|
|
33
|
+
bytes: { ...integer, maximum: 1024 * 1024 } }),
|
|
34
|
+
] }]))),
|
|
35
|
+
coverage: closed({ complete: literal(false), losses: { type: "array", items: string(), maxItems: 64, uniqueItems: true } }),
|
|
36
|
+
acceptance: literal("not-assessed"),
|
|
37
|
+
}),
|
|
38
|
+
allOf: [
|
|
39
|
+
{ if: { properties: { state: literal("running") } }, then: { properties: { outcome: literal(null) } },
|
|
40
|
+
else: { properties: { outcome: { type: "object" } } } },
|
|
41
|
+
{ if: { properties: { nativeSession: { properties: { branchState: literal("unknown") } } } },
|
|
42
|
+
then: { properties: { nativeSession: { properties: { branchLeafId: literal(null) } }, native: { properties: { branchLeafId: literal(null) } } } } },
|
|
43
|
+
],
|
|
44
|
+
});
|
|
45
|
+
const validator = Compile(RETENTION_SCHEMA);
|
|
46
|
+
function freeze(value) { if (value && typeof value === "object") {
|
|
47
|
+
for (const child of Object.values(value))
|
|
48
|
+
freeze(child);
|
|
49
|
+
Object.freeze(value);
|
|
50
|
+
} return value; }
|
|
51
|
+
/** Closed, detached versioned wire builder; never authenticates the supplied observation. */
|
|
52
|
+
export function buildExecutionRetentionManifest(value) {
|
|
53
|
+
// Reject non-JSON objects/accessors instead of invoking serialization hooks while collecting evidence.
|
|
54
|
+
let nodes = 0;
|
|
55
|
+
const inspect = (x, depth) => {
|
|
56
|
+
if (++nodes > 4096 || depth > 16)
|
|
57
|
+
throw new TypeError("retention manifest exceeds bounds");
|
|
58
|
+
if (x === null || typeof x === "string" || typeof x === "boolean" || (typeof x === "number" && Number.isSafeInteger(x)))
|
|
59
|
+
return;
|
|
60
|
+
if (!x || typeof x !== "object" || (!Array.isArray(x) && Object.getPrototypeOf(x) !== Object.prototype && Object.getPrototypeOf(x) !== null))
|
|
61
|
+
throw new TypeError("retention manifest must be JSON data");
|
|
62
|
+
if (Array.isArray(x) && (Object.getPrototypeOf(x) !== Array.prototype || x.length > 256 || Reflect.ownKeys(x).length !== x.length + 1 ||
|
|
63
|
+
Array.from({ length: x.length }, (_, i) => String(i)).some(key => !Object.hasOwn(x, key))))
|
|
64
|
+
throw new TypeError("retention arrays must be dense plain JSON arrays");
|
|
65
|
+
for (const key of Reflect.ownKeys(x)) {
|
|
66
|
+
if (Array.isArray(x) && key === "length")
|
|
67
|
+
continue;
|
|
68
|
+
const d = Object.getOwnPropertyDescriptor(x, key);
|
|
69
|
+
if (typeof key !== "string" || !d.enumerable || !("value" in d))
|
|
70
|
+
throw new TypeError("retention manifest must be plain JSON data");
|
|
71
|
+
inspect(d.value, depth + 1);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
inspect(value, 0);
|
|
75
|
+
if (!validator.Check(value))
|
|
76
|
+
throw new TypeError("invalid execution-retention 2.0 manifest");
|
|
77
|
+
const m = JSON.parse(JSON.stringify(value));
|
|
78
|
+
if (m.native.sessionId !== m.nativeSession.sessionId || m.native.sessionPath !== m.nativeSession.sessionPath || m.native.branchLeafId !== m.nativeSession.branchLeafId) {
|
|
79
|
+
throw new TypeError("native session projection mismatch");
|
|
80
|
+
}
|
|
81
|
+
for (const kind of RETENTION_CONTENT_KINDS) {
|
|
82
|
+
const ref = m.content[kind];
|
|
83
|
+
if (ref.status === "retained" && ref.path !== `${kind}-${ref.sha256}.bin`)
|
|
84
|
+
throw new TypeError("retention content identity mismatch");
|
|
85
|
+
}
|
|
86
|
+
if (m.nativeSession.branchState === "observed" && (m.nativeSession.source !== "pi-session-manager" || m.nativeSession.status !== "verified"))
|
|
87
|
+
throw new TypeError("unverified active native branch");
|
|
88
|
+
if (m.nativeSession.status === "verified" && (!m.nativeSession.sessionId || !m.nativeSession.sha256 || m.content.session.sha256 !== m.nativeSession.sha256))
|
|
89
|
+
throw new TypeError("verified native session requires retained bytes");
|
|
90
|
+
return freeze(m);
|
|
91
|
+
}
|
|
92
|
+
export function parseExecutionRetentionManifest(text) {
|
|
93
|
+
if (Buffer.byteLength(text) > 64 * 1024)
|
|
94
|
+
throw new TypeError("retention manifest exceeds bounds");
|
|
95
|
+
// Manifest numbers are integers; use the existing exact-token parser before lossy Number conversion.
|
|
96
|
+
let value;
|
|
97
|
+
try {
|
|
98
|
+
value = parseWorkJson(text);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
throw new TypeError("invalid retention JSON");
|
|
102
|
+
}
|
|
103
|
+
return buildExecutionRetentionManifest(value);
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=retention-v2-contract.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type WorkFrozen, type WorkProblem, type WorkConflict } from "./retention-v2-work-types.js";
|
|
2
|
+
export declare const WORK_EVENT_BYTES: number;
|
|
3
|
+
export declare const WORK_TEXT_BYTES: number;
|
|
4
|
+
export declare const WORK_RECORDS = 10000;
|
|
5
|
+
export type WorkJson = null | boolean | number | string | WorkJson[] | {
|
|
6
|
+
[key: string]: WorkJson;
|
|
7
|
+
};
|
|
8
|
+
/** Inspect descriptors rather than serializing: no getters/toJSON or lossy JSON coercion. */
|
|
9
|
+
export declare function ownWorkFields(value: unknown): Record<string, PropertyDescriptor>;
|
|
10
|
+
/** Detach only the approved JSON value profile, before any serialization or validation use. */
|
|
11
|
+
export declare function copyWorkJson(value: unknown): WorkJson;
|
|
12
|
+
/** Bounded syntax tree: syntax, decoded duplicates, then exact numeric/string profile. */
|
|
13
|
+
export declare function parseWorkJson(text: string): WorkJson;
|
|
14
|
+
/** RFC 8785 emission restricted to our validated JSON profile; not an archival-byte identity. */
|
|
15
|
+
export declare function canonicalWorkJson(value: unknown): string;
|
|
16
|
+
export declare function workDigest(value: unknown): string;
|
|
17
|
+
/** @internal ONLY validated constituents / internally constructed plain results. Output collections
|
|
18
|
+
* are not wire records: they may contain >256 claims, problems, labels, or conflict alternatives.
|
|
19
|
+
* Never use this emitter in place of copyWorkJson/strict ingestion at an input boundary. */
|
|
20
|
+
export declare function workResultKey<T>(value: T): string;
|
|
21
|
+
export declare function sortWorkResults<T>(values: Iterable<T>): T[];
|
|
22
|
+
export declare function mergeWorkProblems(values: readonly WorkFrozen<WorkProblem>[]): WorkFrozen<WorkProblem>[];
|
|
23
|
+
export declare function mergeWorkConflicts(values: readonly WorkFrozen<WorkConflict>[]): WorkFrozen<WorkConflict>[];
|
|
24
|
+
export declare function freezeWork<T>(value: T): WorkFrozen<T>;
|