@skill-harness/adapters 0.12.0 → 0.13.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 +59 -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,268 @@
|
|
|
1
|
+
// GENERATED by scripts/vendor-execution-retention.mjs from pi-daddy 7c78769c47177b1972b09e1f5c5474ad44cd2cac. Do not hand-edit.
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { WorkInputError } from "./retention-v2-work-types.js";
|
|
4
|
+
export const WORK_EVENT_BYTES = 64 * 1024;
|
|
5
|
+
export const WORK_TEXT_BYTES = 16 * 1024 * 1024;
|
|
6
|
+
export const WORK_RECORDS = 10_000;
|
|
7
|
+
const MAX_DEPTH = 16;
|
|
8
|
+
const MAX_ARRAY = 256;
|
|
9
|
+
const invalid = () => { throw new WorkInputError("WORK_SCHEMA_INVALID"); };
|
|
10
|
+
const limit = () => { throw new WorkInputError("WORK_LIMIT_EXCEEDED"); };
|
|
11
|
+
function unicode(value) {
|
|
12
|
+
// RFC 8785 refuses lone UTF-16 surrogates, including ones decoded from escapes.
|
|
13
|
+
for (let i = 0; i < value.length; i++) {
|
|
14
|
+
const code = value.charCodeAt(i);
|
|
15
|
+
if (code >= 0xd800 && code <= 0xdbff) {
|
|
16
|
+
const next = value.charCodeAt(++i);
|
|
17
|
+
if (!(next >= 0xdc00 && next <= 0xdfff))
|
|
18
|
+
invalid();
|
|
19
|
+
}
|
|
20
|
+
else if (code >= 0xdc00 && code <= 0xdfff)
|
|
21
|
+
invalid();
|
|
22
|
+
}
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
/** Inspect descriptors rather than serializing: no getters/toJSON or lossy JSON coercion. */
|
|
26
|
+
export function ownWorkFields(value) {
|
|
27
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
28
|
+
invalid();
|
|
29
|
+
const prototype = Object.getPrototypeOf(value);
|
|
30
|
+
if (prototype !== Object.prototype && prototype !== null)
|
|
31
|
+
invalid();
|
|
32
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
33
|
+
for (const key of Reflect.ownKeys(descriptors)) {
|
|
34
|
+
if (typeof key !== "string")
|
|
35
|
+
return invalid();
|
|
36
|
+
const descriptor = descriptors[key];
|
|
37
|
+
if (!Object.hasOwn(descriptor, "value") || !descriptor.enumerable)
|
|
38
|
+
invalid();
|
|
39
|
+
unicode(key);
|
|
40
|
+
}
|
|
41
|
+
return descriptors;
|
|
42
|
+
}
|
|
43
|
+
/** Detach only the approved JSON value profile, before any serialization or validation use. */
|
|
44
|
+
export function copyWorkJson(value) {
|
|
45
|
+
const ancestors = new Set();
|
|
46
|
+
function copy(v, depth) {
|
|
47
|
+
if (v === null || typeof v === "boolean")
|
|
48
|
+
return v;
|
|
49
|
+
if (typeof v === "string")
|
|
50
|
+
return unicode(v);
|
|
51
|
+
if (typeof v === "number")
|
|
52
|
+
return Number.isSafeInteger(v) ? v : invalid();
|
|
53
|
+
if (typeof v !== "object")
|
|
54
|
+
return invalid();
|
|
55
|
+
if (depth > MAX_DEPTH)
|
|
56
|
+
limit();
|
|
57
|
+
if (ancestors.has(v))
|
|
58
|
+
invalid();
|
|
59
|
+
ancestors.add(v);
|
|
60
|
+
let result;
|
|
61
|
+
if (Array.isArray(v)) {
|
|
62
|
+
if (Object.getPrototypeOf(v) !== Array.prototype)
|
|
63
|
+
invalid();
|
|
64
|
+
if (v.length > MAX_ARRAY)
|
|
65
|
+
limit();
|
|
66
|
+
const fields = Object.getOwnPropertyDescriptors(v);
|
|
67
|
+
if (Reflect.ownKeys(fields).length !== v.length + 1)
|
|
68
|
+
invalid();
|
|
69
|
+
result = [];
|
|
70
|
+
for (let i = 0; i < v.length; i++) {
|
|
71
|
+
const d = fields[String(i)];
|
|
72
|
+
if (!d || !Object.hasOwn(d, "value") || !d.enumerable)
|
|
73
|
+
invalid();
|
|
74
|
+
result.push(copy(d.value, depth + 1));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
const fields = ownWorkFields(v);
|
|
79
|
+
result = {};
|
|
80
|
+
for (const key of Object.keys(fields)) {
|
|
81
|
+
Object.defineProperty(result, key, { value: copy(fields[key].value, depth + 1), enumerable: true, writable: true, configurable: true });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
ancestors.delete(v);
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
return copy(value, 1);
|
|
88
|
+
}
|
|
89
|
+
/** Exact decimal arithmetic BEFORE Number conversion, without unbounded exponent allocation. */
|
|
90
|
+
function integerToken(token) {
|
|
91
|
+
const match = /^(-?)(\d+)(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/.exec(token);
|
|
92
|
+
let digits = (match[2] + (match[3] ?? "")).replace(/^0+/, "");
|
|
93
|
+
if (!digits)
|
|
94
|
+
return 0;
|
|
95
|
+
let shift = BigInt(match[4] ?? "0") - BigInt((match[3] ?? "").length);
|
|
96
|
+
const trailing = /0*$/.exec(digits)[0].length;
|
|
97
|
+
digits = digits.slice(0, digits.length - trailing);
|
|
98
|
+
shift += BigInt(trailing);
|
|
99
|
+
if (shift < 0n || BigInt(digits.length) + shift > 16n)
|
|
100
|
+
invalid();
|
|
101
|
+
const exact = BigInt(digits) * (10n ** shift);
|
|
102
|
+
if (exact > BigInt(Number.MAX_SAFE_INTEGER))
|
|
103
|
+
invalid();
|
|
104
|
+
return Number(match[1] === "-" ? -exact : exact);
|
|
105
|
+
}
|
|
106
|
+
function checkMembers(node) {
|
|
107
|
+
if (node.type === "object") {
|
|
108
|
+
const names = new Set();
|
|
109
|
+
for (const [key, child] of node.entries) {
|
|
110
|
+
if (names.has(key))
|
|
111
|
+
throw new WorkInputError("WORK_DUPLICATE_MEMBER");
|
|
112
|
+
names.add(key);
|
|
113
|
+
checkMembers(child);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
else if (node.type === "array")
|
|
117
|
+
node.items.forEach(checkMembers);
|
|
118
|
+
}
|
|
119
|
+
function materialize(node) {
|
|
120
|
+
if (node.type === "number")
|
|
121
|
+
return integerToken(node.token);
|
|
122
|
+
if (node.type === "literal")
|
|
123
|
+
return typeof node.value === "string" ? unicode(node.value) : node.value;
|
|
124
|
+
if (node.type === "array")
|
|
125
|
+
return node.items.map(materialize);
|
|
126
|
+
return Object.fromEntries(node.entries.map(([key, child]) => [unicode(key), materialize(child)]));
|
|
127
|
+
}
|
|
128
|
+
/** Bounded syntax tree: syntax, decoded duplicates, then exact numeric/string profile. */
|
|
129
|
+
export function parseWorkJson(text) {
|
|
130
|
+
if (typeof text !== "string")
|
|
131
|
+
invalid();
|
|
132
|
+
if (Buffer.byteLength(text, "utf8") > WORK_EVENT_BYTES)
|
|
133
|
+
limit();
|
|
134
|
+
let at = 0;
|
|
135
|
+
const syntax = () => { throw new WorkInputError("WORK_JSON_INVALID"); };
|
|
136
|
+
const whitespace = () => { while (at < text.length && /[ \t\r\n]/.test(text[at]))
|
|
137
|
+
at++; };
|
|
138
|
+
function string() {
|
|
139
|
+
const start = at++;
|
|
140
|
+
while (at < text.length) {
|
|
141
|
+
const char = text[at++];
|
|
142
|
+
if (char === '"') {
|
|
143
|
+
// JSON.parse is used ONLY for one lexically delimited string token, never an object.
|
|
144
|
+
return JSON.parse(text.slice(start, at));
|
|
145
|
+
}
|
|
146
|
+
if (char.charCodeAt(0) < 0x20)
|
|
147
|
+
syntax();
|
|
148
|
+
if (char === "\\") {
|
|
149
|
+
const escape = text[at++];
|
|
150
|
+
if (escape === "u") {
|
|
151
|
+
if (!/^[0-9a-fA-F]{4}$/.test(text.slice(at, at + 4)))
|
|
152
|
+
syntax();
|
|
153
|
+
at += 4;
|
|
154
|
+
}
|
|
155
|
+
else if (!escape || !'"\\/bfnrt'.includes(escape))
|
|
156
|
+
syntax();
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return syntax();
|
|
160
|
+
}
|
|
161
|
+
function value(depth) {
|
|
162
|
+
whitespace();
|
|
163
|
+
const char = text[at];
|
|
164
|
+
if (char === '"')
|
|
165
|
+
return { type: "literal", value: string() };
|
|
166
|
+
if (char === "{" || char === "[") {
|
|
167
|
+
if (depth > MAX_DEPTH)
|
|
168
|
+
limit();
|
|
169
|
+
at++;
|
|
170
|
+
whitespace();
|
|
171
|
+
const object = char === "{";
|
|
172
|
+
const end = object ? "}" : "]";
|
|
173
|
+
const entries = [];
|
|
174
|
+
const items = [];
|
|
175
|
+
if (text[at] !== end)
|
|
176
|
+
for (;;) {
|
|
177
|
+
whitespace();
|
|
178
|
+
let key = "";
|
|
179
|
+
if (object) {
|
|
180
|
+
if (text[at] !== '"')
|
|
181
|
+
syntax();
|
|
182
|
+
key = string();
|
|
183
|
+
whitespace();
|
|
184
|
+
if (text[at++] !== ":")
|
|
185
|
+
syntax();
|
|
186
|
+
}
|
|
187
|
+
else if (items.length >= MAX_ARRAY)
|
|
188
|
+
limit();
|
|
189
|
+
const child = value(depth + 1);
|
|
190
|
+
if (object)
|
|
191
|
+
entries.push([key, child]);
|
|
192
|
+
else
|
|
193
|
+
items.push(child);
|
|
194
|
+
whitespace();
|
|
195
|
+
if (text[at] === end)
|
|
196
|
+
break;
|
|
197
|
+
if (text[at++] !== ",")
|
|
198
|
+
syntax();
|
|
199
|
+
}
|
|
200
|
+
at++;
|
|
201
|
+
return object ? { type: "object", entries } : { type: "array", items };
|
|
202
|
+
}
|
|
203
|
+
for (const [token, literal] of [["null", null], ["true", true], ["false", false]]) {
|
|
204
|
+
if (text.startsWith(token, at)) {
|
|
205
|
+
at += token.length;
|
|
206
|
+
return { type: "literal", value: literal };
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
const token = /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?/.exec(text.slice(at));
|
|
210
|
+
if (!token)
|
|
211
|
+
return syntax();
|
|
212
|
+
at += token[0].length;
|
|
213
|
+
return { type: "number", token: token[0] };
|
|
214
|
+
}
|
|
215
|
+
const result = value(1);
|
|
216
|
+
whitespace();
|
|
217
|
+
if (at !== text.length)
|
|
218
|
+
syntax();
|
|
219
|
+
checkMembers(result);
|
|
220
|
+
return materialize(result);
|
|
221
|
+
}
|
|
222
|
+
function emit(value) {
|
|
223
|
+
if (value === null || typeof value !== "object")
|
|
224
|
+
return JSON.stringify(value);
|
|
225
|
+
if (Array.isArray(value))
|
|
226
|
+
return `[${value.map(emit).join(",")}]`;
|
|
227
|
+
// Emit keys directly: reconstructing a JS object would reorder numeric-looking keys.
|
|
228
|
+
return `{${Object.keys(value).sort().map(key => `${JSON.stringify(key)}:${emit(value[key])}`).join(",")}}`;
|
|
229
|
+
}
|
|
230
|
+
/** RFC 8785 emission restricted to our validated JSON profile; not an archival-byte identity. */
|
|
231
|
+
export function canonicalWorkJson(value) { return emit(copyWorkJson(value)); }
|
|
232
|
+
export function workDigest(value) {
|
|
233
|
+
return createHash("sha256").update(canonicalWorkJson(value), "utf8").digest("hex");
|
|
234
|
+
}
|
|
235
|
+
/** @internal ONLY validated constituents / internally constructed plain results. Output collections
|
|
236
|
+
* are not wire records: they may contain >256 claims, problems, labels, or conflict alternatives.
|
|
237
|
+
* Never use this emitter in place of copyWorkJson/strict ingestion at an input boundary. */
|
|
238
|
+
export function workResultKey(value) { return emit(value); }
|
|
239
|
+
export function sortWorkResults(values) {
|
|
240
|
+
const rows = new Map([...values].map(value => [workResultKey(value), value]));
|
|
241
|
+
return [...rows].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([, value]) => value);
|
|
242
|
+
}
|
|
243
|
+
export function mergeWorkProblems(values) {
|
|
244
|
+
const rows = new Map();
|
|
245
|
+
for (const row of values) {
|
|
246
|
+
const id = workResultKey([row.code, row.reference]), prior = rows.get(id);
|
|
247
|
+
rows.set(id, { ...row, affectedObligations: sortWorkResults([...(prior?.affectedObligations ?? []), ...row.affectedObligations]) });
|
|
248
|
+
}
|
|
249
|
+
return sortWorkResults(rows.values());
|
|
250
|
+
}
|
|
251
|
+
export function mergeWorkConflicts(values) {
|
|
252
|
+
const rows = new Map();
|
|
253
|
+
for (const row of values) {
|
|
254
|
+
const id = workResultKey([row.kind, row.id]), prior = rows.get(id);
|
|
255
|
+
rows.set(id, { ...row, digests: sortWorkResults([...(prior?.digests ?? []), ...row.digests]),
|
|
256
|
+
affectedObligations: sortWorkResults([...(prior?.affectedObligations ?? []), ...row.affectedObligations]) });
|
|
257
|
+
}
|
|
258
|
+
return sortWorkResults(rows.values());
|
|
259
|
+
}
|
|
260
|
+
export function freezeWork(value) {
|
|
261
|
+
if (value !== null && typeof value === "object") {
|
|
262
|
+
for (const child of Object.values(value))
|
|
263
|
+
freezeWork(child);
|
|
264
|
+
Object.freeze(value);
|
|
265
|
+
}
|
|
266
|
+
return value;
|
|
267
|
+
}
|
|
268
|
+
//# sourceMappingURL=retention-v2-json.js.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// GENERATED by scripts/vendor-execution-retention.mjs from pi-daddy 7c78769c47177b1972b09e1f5c5474ad44cd2cac. Do not hand-edit.
|
|
2
|
+
/** Strict JSON framing for retention metadata/native records. Floats in native usage remain supported. */
|
|
3
|
+
export function parseRetentionJson(text, maxBytes = 64 * 1024) {
|
|
4
|
+
if (Buffer.byteLength(text) > maxBytes)
|
|
5
|
+
throw new TypeError("retention JSON exceeds bounds");
|
|
6
|
+
const value = JSON.parse(text);
|
|
7
|
+
const stack = [];
|
|
8
|
+
for (const match of text.matchAll(/"(?:[^"\\]|\\[\s\S])*"|[{}\[\],:]|[^\s{}\[\],:]+/g)) {
|
|
9
|
+
const token = match[0], top = stack.at(-1);
|
|
10
|
+
if (token === "{" || token === "[") {
|
|
11
|
+
if (stack.length >= 64)
|
|
12
|
+
throw new TypeError("retention JSON exceeds depth");
|
|
13
|
+
stack.push({ object: token === "{", key: token === "{", seen: new Set() });
|
|
14
|
+
}
|
|
15
|
+
else if (token === "}" || token === "]")
|
|
16
|
+
stack.pop();
|
|
17
|
+
else if (token === ",") {
|
|
18
|
+
if (top?.object)
|
|
19
|
+
top.key = true;
|
|
20
|
+
}
|
|
21
|
+
else if (token.startsWith('"') && top?.object && top.key) {
|
|
22
|
+
const key = JSON.parse(token);
|
|
23
|
+
if (top.seen.has(key))
|
|
24
|
+
throw new TypeError("duplicate retention JSON member");
|
|
25
|
+
top.seen.add(key);
|
|
26
|
+
top.key = false;
|
|
27
|
+
}
|
|
28
|
+
else if (/^-?\d/.test(token) && !Number.isFinite(Number(token)))
|
|
29
|
+
throw new TypeError("non-finite retention JSON number");
|
|
30
|
+
}
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=retention-v2-native-json.js.map
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export declare const ENV_NATIVE_SESSION_ROOT = "PI_GRANTS_NATIVE_SESSION_ROOT";
|
|
2
|
+
export declare const MAX_NATIVE_SESSION_BYTES: number;
|
|
3
|
+
export type NativeSessionSource = "herdr-id" | "herdr-path" | "pi-session-file" | "pi-session-manager";
|
|
4
|
+
export interface NativeSessionObservation {
|
|
5
|
+
source: NativeSessionSource | null;
|
|
6
|
+
status: "missing" | "verified" | "invalid" | "changed" | "truncated" | "unsupported";
|
|
7
|
+
sessionId: string | null;
|
|
8
|
+
sessionPath: string | null;
|
|
9
|
+
parentSessionPath: string | null;
|
|
10
|
+
branchLeafId: string | null;
|
|
11
|
+
branchState: "unknown" | "observed";
|
|
12
|
+
lastPersistedEntryId: string | null;
|
|
13
|
+
sha256: string | null;
|
|
14
|
+
reason: string | null;
|
|
15
|
+
}
|
|
16
|
+
export interface NativeSessionCapture {
|
|
17
|
+
observation: NativeSessionObservation;
|
|
18
|
+
bytes?: Buffer;
|
|
19
|
+
fileIdentity?: {
|
|
20
|
+
device: string;
|
|
21
|
+
inode: string;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/** Read-only methods of pi 0.84.2's public SessionManager; no runtime/session creation or model work. */
|
|
25
|
+
export interface NativeSessionManager {
|
|
26
|
+
getSessionFile(): string | undefined;
|
|
27
|
+
getSessionId(): string;
|
|
28
|
+
getLeafId(): string | null;
|
|
29
|
+
}
|
|
30
|
+
export declare const missingNativeSession: () => NativeSessionObservation;
|
|
31
|
+
/** Parse the retained native v3 bytes, never load/migrate/rewrite a session via SessionManager.open(). */
|
|
32
|
+
export declare function parseNativeSessionBytes(bytes: Uint8Array, input: {
|
|
33
|
+
source: NativeSessionSource;
|
|
34
|
+
path: string;
|
|
35
|
+
expectedSessionId?: string;
|
|
36
|
+
truncated?: boolean;
|
|
37
|
+
liveLeaf?: {
|
|
38
|
+
sessionId: string;
|
|
39
|
+
leafId: string | null;
|
|
40
|
+
};
|
|
41
|
+
}): NativeSessionCapture;
|
|
42
|
+
/** Private allowlisted files only, bounded non-following descriptor reads, no scan of session/auth dirs. */
|
|
43
|
+
export declare function readNativeSession(input: {
|
|
44
|
+
path: string;
|
|
45
|
+
source: NativeSessionSource;
|
|
46
|
+
expectedSessionId?: string;
|
|
47
|
+
allowedRoot?: string;
|
|
48
|
+
manager?: NativeSessionManager;
|
|
49
|
+
expectedFile?: {
|
|
50
|
+
device: string;
|
|
51
|
+
inode: string;
|
|
52
|
+
};
|
|
53
|
+
}): Promise<NativeSessionCapture>;
|
|
54
|
+
/** Herdr 0.8.2 protocol 20 AgentInfo.agent_session; never scrape terminal text, PID or names. */
|
|
55
|
+
export declare function herdrSessionReference(value: unknown, expectedPane: string): {
|
|
56
|
+
source: "herdr-id" | "herdr-path";
|
|
57
|
+
value: string;
|
|
58
|
+
} | null;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// GENERATED by scripts/vendor-execution-retention.mjs from pi-daddy 7c78769c47177b1972b09e1f5c5474ad44cd2cac. Do not hand-edit.
|
|
2
|
+
import { constants } from "node:fs";
|
|
3
|
+
import { open, lstat, realpath } from "node:fs/promises";
|
|
4
|
+
import { isAbsolute, relative, sep } from "node:path";
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
6
|
+
import { parseRetentionJson } from "./retention-v2-native-json.js";
|
|
7
|
+
export const ENV_NATIVE_SESSION_ROOT = "PI_GRANTS_NATIVE_SESSION_ROOT";
|
|
8
|
+
export const MAX_NATIVE_SESSION_BYTES = 1024 * 1024;
|
|
9
|
+
export const missingNativeSession = () => ({ source: null, status: "missing", sessionId: null,
|
|
10
|
+
sessionPath: null, parentSessionPath: null, branchLeafId: null, branchState: "unknown", lastPersistedEntryId: null,
|
|
11
|
+
sha256: null, reason: "native-session-unavailable" });
|
|
12
|
+
const id = (x) => typeof x === "string" && /^[a-zA-Z0-9_-]{1,128}$/.test(x);
|
|
13
|
+
const uuid = (x) => typeof x === "string" && /^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/i.test(x);
|
|
14
|
+
const obj = (x) => x !== null && typeof x === "object" && !Array.isArray(x);
|
|
15
|
+
const filePath = (x) => typeof x === "string" && x.length <= 4096 && isAbsolute(x) && !x.includes("\0");
|
|
16
|
+
/** Parse the retained native v3 bytes, never load/migrate/rewrite a session via SessionManager.open(). */
|
|
17
|
+
export function parseNativeSessionBytes(bytes, input) {
|
|
18
|
+
const observation = { ...missingNativeSession(), source: input.source, sessionPath: input.path };
|
|
19
|
+
const result = { observation };
|
|
20
|
+
const fail = (status, reason) => {
|
|
21
|
+
observation.status = status;
|
|
22
|
+
observation.reason = reason;
|
|
23
|
+
observation.branchLeafId = null;
|
|
24
|
+
observation.branchState = "unknown";
|
|
25
|
+
return result;
|
|
26
|
+
};
|
|
27
|
+
if (bytes.length > MAX_NATIVE_SESSION_BYTES)
|
|
28
|
+
return fail("truncated", "native-session-size-limit");
|
|
29
|
+
const raw = Buffer.from(bytes), newline = raw.indexOf(10);
|
|
30
|
+
let header;
|
|
31
|
+
try {
|
|
32
|
+
header = parseRetentionJson(new TextDecoder("utf-8", { fatal: true }).decode(raw.subarray(0, newline < 0 ? raw.length : newline)), MAX_NATIVE_SESSION_BYTES);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return fail("invalid", "native-session-header-invalid");
|
|
36
|
+
}
|
|
37
|
+
if (!obj(header) || header.type !== "session" || header.version !== 3 || !uuid(header.id) ||
|
|
38
|
+
!filePath(header.cwd) || typeof header.timestamp !== "string" || !Number.isFinite(Date.parse(header.timestamp)) ||
|
|
39
|
+
(header.parentSession !== undefined && !filePath(header.parentSession)))
|
|
40
|
+
return fail("invalid", "native-session-header-invalid");
|
|
41
|
+
if (input.expectedSessionId !== undefined && header.id !== input.expectedSessionId)
|
|
42
|
+
return fail("changed", "native-session-id-changed");
|
|
43
|
+
// Only validated session headers admit transcript bytes; arbitrary files are never archived here.
|
|
44
|
+
result.bytes = Buffer.from(bytes);
|
|
45
|
+
observation.sessionId = header.id;
|
|
46
|
+
observation.parentSessionPath = header.parentSession ?? null;
|
|
47
|
+
observation.sha256 = createHash("sha256").update(bytes).digest("hex");
|
|
48
|
+
if (input.truncated || raw.at(-1) !== 10)
|
|
49
|
+
return fail("truncated", "native-session-incomplete-bytes");
|
|
50
|
+
let text;
|
|
51
|
+
try {
|
|
52
|
+
text = new TextDecoder("utf-8", { fatal: true }).decode(raw);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return fail("invalid", "native-session-utf8-invalid");
|
|
56
|
+
}
|
|
57
|
+
const lines = text.split("\n");
|
|
58
|
+
const ids = new Set();
|
|
59
|
+
if (lines.length > 10002)
|
|
60
|
+
return fail("invalid", "native-session-entry-limit");
|
|
61
|
+
for (const line of lines.slice(1, -1)) {
|
|
62
|
+
let entry;
|
|
63
|
+
try {
|
|
64
|
+
entry = parseRetentionJson(line, MAX_NATIVE_SESSION_BYTES);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return fail("invalid", "native-session-entry-invalid");
|
|
68
|
+
}
|
|
69
|
+
if (!obj(entry) || typeof entry.type !== "string" || entry.type === "session" || !id(entry.id) || ids.has(entry.id) ||
|
|
70
|
+
!(entry.parentId === null || (id(entry.parentId) && ids.has(entry.parentId))) || typeof entry.timestamp !== "string" ||
|
|
71
|
+
!Number.isFinite(Date.parse(entry.timestamp)))
|
|
72
|
+
return fail("invalid", "native-session-parent-link-invalid");
|
|
73
|
+
ids.add(entry.id);
|
|
74
|
+
observation.lastPersistedEntryId = entry.id;
|
|
75
|
+
}
|
|
76
|
+
if (input.liveLeaf) {
|
|
77
|
+
if (input.liveLeaf.sessionId !== header.id || (input.liveLeaf.leafId !== null && !ids.has(input.liveLeaf.leafId))) {
|
|
78
|
+
return fail("changed", "native-session-live-leaf-mismatch");
|
|
79
|
+
}
|
|
80
|
+
observation.branchLeafId = input.liveLeaf.leafId;
|
|
81
|
+
observation.branchState = "observed";
|
|
82
|
+
}
|
|
83
|
+
observation.status = "verified";
|
|
84
|
+
observation.reason = observation.branchState === "unknown" ? "active-branch-unknown" : null;
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
/** Private allowlisted files only, bounded non-following descriptor reads, no scan of session/auth dirs. */
|
|
88
|
+
export async function readNativeSession(input) {
|
|
89
|
+
const gap = (status, reason) => ({ observation: {
|
|
90
|
+
...missingNativeSession(), source: input.source, status, reason, sessionPath: filePath(input.path) ? input.path : null,
|
|
91
|
+
} });
|
|
92
|
+
const root = input.allowedRoot ?? process.env[ENV_NATIVE_SESSION_ROOT];
|
|
93
|
+
if (!filePath(input.path) || !input.path.endsWith(".jsonl") || !root || !filePath(root))
|
|
94
|
+
return gap("unsupported", "native-session-private-root-required");
|
|
95
|
+
const within = (base, candidate) => { const p = relative(base, candidate); return p !== "" && p !== ".." && !p.startsWith(`..${sep}`) && !isAbsolute(p); };
|
|
96
|
+
if (!within(root, input.path))
|
|
97
|
+
return gap("unsupported", "native-session-outside-private-root");
|
|
98
|
+
let handle;
|
|
99
|
+
try {
|
|
100
|
+
const canonicalRoot = await realpath(root), canonicalFile = await realpath(input.path);
|
|
101
|
+
const rootStat = await lstat(canonicalRoot);
|
|
102
|
+
if (!rootStat.isDirectory() || (rootStat.mode & 0o077) !== 0 || (typeof process.getuid === "function" && rootStat.uid !== process.getuid()))
|
|
103
|
+
return gap("unsupported", "native-session-root-not-private");
|
|
104
|
+
if (!within(canonicalRoot, canonicalFile) || (await lstat(input.path)).isSymbolicLink())
|
|
105
|
+
return gap("unsupported", "native-session-path-alias");
|
|
106
|
+
handle = await open(input.path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
107
|
+
const before = await handle.stat();
|
|
108
|
+
if (!before.isFile() || before.nlink !== 1 || (typeof process.getuid === "function" && before.uid !== process.getuid())) {
|
|
109
|
+
return gap("unsupported", "native-session-not-private-regular-file");
|
|
110
|
+
}
|
|
111
|
+
const fileIdentity = { device: String(before.dev), inode: String(before.ino) };
|
|
112
|
+
if (input.expectedFile && (input.expectedFile.device !== fileIdentity.device || input.expectedFile.inode !== fileIdentity.inode))
|
|
113
|
+
return gap("changed", "native-session-file-replaced");
|
|
114
|
+
const live = input.manager ? { sessionId: input.manager.getSessionId(), leafId: input.manager.getLeafId(), path: input.manager.getSessionFile() } : undefined;
|
|
115
|
+
if (live && live.path !== input.path)
|
|
116
|
+
return gap("changed", "native-session-manager-path-changed");
|
|
117
|
+
const bytes = Buffer.alloc(Math.min(before.size, MAX_NATIVE_SESSION_BYTES));
|
|
118
|
+
let offset = 0;
|
|
119
|
+
while (offset < bytes.length) {
|
|
120
|
+
const read = await handle.read(bytes, offset, bytes.length - offset, offset);
|
|
121
|
+
if (!read.bytesRead)
|
|
122
|
+
break;
|
|
123
|
+
offset += read.bytesRead;
|
|
124
|
+
}
|
|
125
|
+
const after = await handle.stat(), location = await lstat(input.path);
|
|
126
|
+
if (before.dev !== location.dev || before.ino !== location.ino || before.size !== after.size || before.mtimeMs !== after.mtimeMs ||
|
|
127
|
+
before.ctimeMs !== after.ctimeMs || location.isSymbolicLink() || after.size !== location.size)
|
|
128
|
+
return gap("changed", "native-session-file-changed");
|
|
129
|
+
if (live && (input.manager.getSessionId() !== live.sessionId || input.manager.getLeafId() !== live.leafId || input.manager.getSessionFile() !== live.path)) {
|
|
130
|
+
return gap("changed", "native-session-live-state-changed");
|
|
131
|
+
}
|
|
132
|
+
return { ...parseNativeSessionBytes(bytes.subarray(0, offset), { ...input, liveLeaf: live,
|
|
133
|
+
truncated: before.size > MAX_NATIVE_SESSION_BYTES || offset !== before.size }), fileIdentity };
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
return gap(error.code === "ENOENT" ? "missing" : "invalid", "native-session-read-failed");
|
|
137
|
+
}
|
|
138
|
+
finally {
|
|
139
|
+
await handle?.close().catch(() => undefined);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/** Herdr 0.8.2 protocol 20 AgentInfo.agent_session; never scrape terminal text, PID or names. */
|
|
143
|
+
export function herdrSessionReference(value, expectedPane) {
|
|
144
|
+
if (!obj(value) || value.pane_id !== expectedPane || value.agent !== "pi" || !obj(value.agent_session))
|
|
145
|
+
return null;
|
|
146
|
+
const s = value.agent_session;
|
|
147
|
+
if (s.agent !== "pi" || typeof s.source !== "string" || s.source.length > 128)
|
|
148
|
+
return null;
|
|
149
|
+
if (s.kind === "id" && uuid(s.value))
|
|
150
|
+
return { source: "herdr-id", value: s.value };
|
|
151
|
+
if (s.kind === "path" && filePath(s.value))
|
|
152
|
+
return { source: "herdr-path", value: s.value };
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=retention-v2-native.js.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// GENERATED by scripts/vendor-execution-retention.mjs from pi-daddy 7c78769c47177b1972b09e1f5c5474ad44cd2cac. Do not hand-edit.
|
|
2
|
+
export const RETENTION_V2_COMMIT = "7c78769c47177b1972b09e1f5c5474ad44cd2cac";
|
|
3
|
+
export const RETENTION_V2_SCHEMA_SHA256 = "a0934a2f04b3f1fae0e4057fa2cb07ee26cd392937498d7fc9fb5b3bbd90632c";
|
|
4
|
+
//# sourceMappingURL=retention-v2-pin.js.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { NativeSessionObservation } from "./retention-v2-native.js";
|
|
2
|
+
export interface RetentionIdentity {
|
|
3
|
+
executionId: string;
|
|
4
|
+
parentExecutionId: string | null;
|
|
5
|
+
childId: string;
|
|
6
|
+
toolCallId: string | null;
|
|
7
|
+
executor: "process" | "herdr" | "check";
|
|
8
|
+
taskDigest: string | null;
|
|
9
|
+
definitionDigest: string | null;
|
|
10
|
+
configurationDigest: string;
|
|
11
|
+
workspaceId: string | null;
|
|
12
|
+
}
|
|
13
|
+
export interface RetainedContent {
|
|
14
|
+
status: "retained" | "missing";
|
|
15
|
+
path: string | null;
|
|
16
|
+
sha256: string | null;
|
|
17
|
+
bytes: number | null;
|
|
18
|
+
}
|
|
19
|
+
export interface ExecutionRetentionManifest {
|
|
20
|
+
version: "2.0";
|
|
21
|
+
archiveId: string;
|
|
22
|
+
identity: RetentionIdentity;
|
|
23
|
+
native: {
|
|
24
|
+
pid: number | null;
|
|
25
|
+
paneId: string | null;
|
|
26
|
+
agentName: string | null;
|
|
27
|
+
tabId: string | null;
|
|
28
|
+
sessionId: string | null;
|
|
29
|
+
sessionPath: string | null;
|
|
30
|
+
branchLeafId: string | null;
|
|
31
|
+
};
|
|
32
|
+
nativeSession: NativeSessionObservation;
|
|
33
|
+
state: "running" | "terminal";
|
|
34
|
+
outcome: {
|
|
35
|
+
code: number | null;
|
|
36
|
+
signal: string | null;
|
|
37
|
+
timedOut: boolean;
|
|
38
|
+
aborted: boolean;
|
|
39
|
+
truncated: boolean;
|
|
40
|
+
failed: boolean;
|
|
41
|
+
} | null;
|
|
42
|
+
content: Record<"stdout" | "stderr" | "paneSnapshot" | "checkReceipt" | "result" | "session", RetainedContent>;
|
|
43
|
+
coverage: {
|
|
44
|
+
complete: false;
|
|
45
|
+
losses: string[];
|
|
46
|
+
};
|
|
47
|
+
/** Retention is observation, never a work-v4 acceptance decision. */
|
|
48
|
+
acceptance: "not-assessed";
|
|
49
|
+
}
|