@skill-harness/adapters 0.17.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +0 -30
- package/dist/index.js +0 -30
- package/dist/pi.js +21 -133
- package/package.json +2 -3
- 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
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { PROMPT_NORMALIZATION_RULE, type PromptMechanism, type PromptProvenance } from "@skill-harness/core";
|
|
2
|
-
export { PROMPT_NORMALIZATION_RULE } from "@skill-harness/core";
|
|
3
|
-
/** Registry entry 1: replace exactly Pi's dynamic cwd line, preserving every other byte. */
|
|
4
|
-
export declare function normalizePromptPayload(value: unknown, rule: typeof PROMPT_NORMALIZATION_RULE): unknown;
|
|
5
|
-
export declare function promptCaptureIsTrusted(extensionCount: number, hasRuntimeInjection?: boolean): boolean;
|
|
6
|
-
export interface AuthenticatedPromptObservation {
|
|
7
|
-
observation: PromptProvenance;
|
|
8
|
-
mac: string;
|
|
9
|
-
}
|
|
10
|
-
export interface AuthenticatedPromptSummary {
|
|
11
|
-
summary: {
|
|
12
|
-
count: number;
|
|
13
|
-
};
|
|
14
|
-
mac: string;
|
|
15
|
-
}
|
|
16
|
-
export declare function authenticatePromptObservation(observation: PromptProvenance, authenticationKey: string): AuthenticatedPromptObservation;
|
|
17
|
-
export declare function authenticatePromptSummary(count: number, authenticationKey: string): AuthenticatedPromptSummary;
|
|
18
|
-
export declare function verifyPromptSummary(value: unknown, expectedCount: number, authenticationKey: string): boolean;
|
|
19
|
-
/** Compute observation from final provider payload prompt bytes; caller supplies content, never status. */
|
|
20
|
-
export declare function observeProviderPayload(payload: unknown, contract: string, mechanism: PromptMechanism, requestIndex: number): PromptProvenance;
|
|
21
|
-
/** Parent-side binding: never accept contract identity or status from the child observer. */
|
|
22
|
-
export declare function bindPromptObservation(value: unknown, contract: string, mechanism: PromptMechanism, requestIndex: number, authenticationKey: string, observerRequestIndex?: number): PromptProvenance;
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import { createHash, createHmac, timingSafeEqual } from "node:crypto";
|
|
2
|
-
import { PROMPT_NORMALIZATION_FLAGS, PROMPT_NORMALIZATION_PATTERN, PROMPT_NORMALIZATION_REPLACEMENT, PROMPT_NORMALIZATION_RULE } from "@skill-harness/core";
|
|
3
|
-
export { PROMPT_NORMALIZATION_RULE } from "@skill-harness/core";
|
|
4
|
-
function sha(bytes) {
|
|
5
|
-
return createHash("sha256").update(bytes, "utf8").digest("hex");
|
|
6
|
-
}
|
|
7
|
-
/** Registry entry 1: replace exactly Pi's dynamic cwd line, preserving every other byte. */
|
|
8
|
-
export function normalizePromptPayload(value, rule) {
|
|
9
|
-
if (rule !== PROMPT_NORMALIZATION_RULE)
|
|
10
|
-
throw new Error(`unknown prompt normalization rule ${String(rule)}`);
|
|
11
|
-
if (typeof value === "string")
|
|
12
|
-
return value.replace(new RegExp(PROMPT_NORMALIZATION_PATTERN, PROMPT_NORMALIZATION_FLAGS), PROMPT_NORMALIZATION_REPLACEMENT);
|
|
13
|
-
if (Array.isArray(value))
|
|
14
|
-
return value.map((entry) => normalizePromptPayload(entry, rule));
|
|
15
|
-
if (value && typeof value === "object")
|
|
16
|
-
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, normalizePromptPayload(entry, rule)]));
|
|
17
|
-
return value;
|
|
18
|
-
}
|
|
19
|
-
function countInStrings(value, needle) {
|
|
20
|
-
if (typeof value === "string") {
|
|
21
|
-
if (!needle)
|
|
22
|
-
return 0;
|
|
23
|
-
let count = 0, at = 0;
|
|
24
|
-
while ((at = value.indexOf(needle, at)) !== -1) {
|
|
25
|
-
count++;
|
|
26
|
-
at += needle.length;
|
|
27
|
-
}
|
|
28
|
-
return count;
|
|
29
|
-
}
|
|
30
|
-
if (Array.isArray(value))
|
|
31
|
-
return value.reduce((sum, entry) => sum + countInStrings(entry, needle), 0);
|
|
32
|
-
if (value && typeof value === "object") {
|
|
33
|
-
const record = value;
|
|
34
|
-
// A scenario may quote the contract. User-authored text is stimulus, never
|
|
35
|
-
// delivery, but providers also place tool results inside role=user messages.
|
|
36
|
-
if (record.role === "user") {
|
|
37
|
-
const content = Array.isArray(record.content) ? record.content : Array.isArray(record.parts) ? record.parts : [];
|
|
38
|
-
return content
|
|
39
|
-
.filter(block => block && typeof block === "object" && (["tool_result", "tool_response", "function_response"].includes(String(block.type ?? "")) ||
|
|
40
|
-
"functionResponse" in block || "function_response" in block))
|
|
41
|
-
.reduce((sum, block) => sum + countInStrings(block, needle), 0);
|
|
42
|
-
}
|
|
43
|
-
return Object.values(record).reduce((sum, entry) => sum + countInStrings(entry, needle), 0);
|
|
44
|
-
}
|
|
45
|
-
return 0;
|
|
46
|
-
}
|
|
47
|
-
const PROMPT_FIELDS = new Set(["instructions", "input", "system", "messages", "prompt", "contents"]);
|
|
48
|
-
function promptProjection(payload) {
|
|
49
|
-
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
50
|
-
return null;
|
|
51
|
-
const record = payload;
|
|
52
|
-
const projected = Object.fromEntries(Object.entries(record).filter(([key]) => PROMPT_FIELDS.has(key)));
|
|
53
|
-
const config = record.config && typeof record.config === "object" ? record.config : null;
|
|
54
|
-
if (config?.systemInstruction !== undefined)
|
|
55
|
-
projected.systemInstruction = config.systemInstruction;
|
|
56
|
-
return Object.keys(projected).length ? projected : null;
|
|
57
|
-
}
|
|
58
|
-
export function promptCaptureIsTrusted(extensionCount, hasRuntimeInjection = false) {
|
|
59
|
-
return extensionCount === 0 && !hasRuntimeInjection;
|
|
60
|
-
}
|
|
61
|
-
function observationMac(observation, authenticationKey) {
|
|
62
|
-
return createHmac("sha256", authenticationKey).update(JSON.stringify(observation)).digest("hex");
|
|
63
|
-
}
|
|
64
|
-
export function authenticatePromptObservation(observation, authenticationKey) {
|
|
65
|
-
return { observation, mac: observationMac(observation, authenticationKey) };
|
|
66
|
-
}
|
|
67
|
-
export function authenticatePromptSummary(count, authenticationKey) {
|
|
68
|
-
const summary = { count };
|
|
69
|
-
return { summary, mac: createHmac("sha256", authenticationKey).update(JSON.stringify(summary)).digest("hex") };
|
|
70
|
-
}
|
|
71
|
-
function validMac(supplied, expected) {
|
|
72
|
-
if (typeof supplied !== "string" || !/^[a-f0-9]{64}$/i.test(supplied))
|
|
73
|
-
return false;
|
|
74
|
-
const suppliedBytes = Buffer.from(supplied, "hex"), expectedBytes = Buffer.from(expected, "hex");
|
|
75
|
-
return suppliedBytes.length === expectedBytes.length && timingSafeEqual(suppliedBytes, expectedBytes);
|
|
76
|
-
}
|
|
77
|
-
export function verifyPromptSummary(value, expectedCount, authenticationKey) {
|
|
78
|
-
if (!value || typeof value !== "object")
|
|
79
|
-
return false;
|
|
80
|
-
const envelope = value;
|
|
81
|
-
if (!envelope.summary || envelope.summary.count !== expectedCount)
|
|
82
|
-
return false;
|
|
83
|
-
const expected = createHmac("sha256", authenticationKey).update(JSON.stringify(envelope.summary)).digest("hex");
|
|
84
|
-
return validMac(envelope.mac, expected);
|
|
85
|
-
}
|
|
86
|
-
function statusFor(occurrences, mechanism, observable) {
|
|
87
|
-
if (!observable)
|
|
88
|
-
return "ERROR";
|
|
89
|
-
return occurrences === (mechanism === "none" ? 0 : 1) ? "PASS" : "NOT-MEASURED";
|
|
90
|
-
}
|
|
91
|
-
/** Compute observation from final provider payload prompt bytes; caller supplies content, never status. */
|
|
92
|
-
export function observeProviderPayload(payload, contract, mechanism, requestIndex) {
|
|
93
|
-
const projection = promptProjection(payload);
|
|
94
|
-
const raw = JSON.stringify(projection ?? {});
|
|
95
|
-
const normalized = JSON.stringify(normalizePromptPayload(projection ?? {}, PROMPT_NORMALIZATION_RULE));
|
|
96
|
-
const occurrences = countInStrings(projection ?? {}, contract);
|
|
97
|
-
const observable = projection !== null;
|
|
98
|
-
return {
|
|
99
|
-
capture_version: "prompt-provenance-v1",
|
|
100
|
-
request_index: requestIndex,
|
|
101
|
-
raw_sha256: sha(raw),
|
|
102
|
-
normalized_sha256: sha(normalized),
|
|
103
|
-
normalization_rule: PROMPT_NORMALIZATION_RULE,
|
|
104
|
-
bytes: Buffer.byteLength(raw),
|
|
105
|
-
contract_sha256: sha(contract),
|
|
106
|
-
contract_bytes: Buffer.byteLength(contract),
|
|
107
|
-
contract_occurrences: occurrences,
|
|
108
|
-
mechanism,
|
|
109
|
-
status: statusFor(occurrences, mechanism, observable),
|
|
110
|
-
...(observable ? {} : { error: "provider payload has no supported model-visible prompt field" }),
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
/** Parent-side binding: never accept contract identity or status from the child observer. */
|
|
114
|
-
export function bindPromptObservation(value, contract, mechanism, requestIndex, authenticationKey, observerRequestIndex = requestIndex) {
|
|
115
|
-
const fallback = { ...observeProviderPayload({}, contract, mechanism, requestIndex), status: "ERROR" };
|
|
116
|
-
if (!value || typeof value !== "object")
|
|
117
|
-
return { ...fallback, error: "prompt observer emitted a non-object record" };
|
|
118
|
-
const envelope = value;
|
|
119
|
-
if (!envelope.observation)
|
|
120
|
-
return { ...fallback, error: "prompt observation authentication missing" };
|
|
121
|
-
const expectedMac = observationMac(envelope.observation, authenticationKey);
|
|
122
|
-
if (!validMac(envelope.mac, expectedMac))
|
|
123
|
-
return { ...fallback, error: "prompt observation authentication failed" };
|
|
124
|
-
const record = envelope.observation;
|
|
125
|
-
if (record.request_index !== observerRequestIndex)
|
|
126
|
-
return { ...fallback, error: "prompt observation replay or ordering mismatch" };
|
|
127
|
-
const expectedDigest = sha(contract), expectedBytes = Buffer.byteLength(contract);
|
|
128
|
-
const valid = record.capture_version === "prompt-provenance-v1" &&
|
|
129
|
-
record.normalization_rule === PROMPT_NORMALIZATION_RULE && record.mechanism === mechanism &&
|
|
130
|
-
record.contract_sha256 === expectedDigest && record.contract_bytes === expectedBytes &&
|
|
131
|
-
typeof record.raw_sha256 === "string" && /^[a-f0-9]{64}$/i.test(record.raw_sha256) &&
|
|
132
|
-
typeof record.normalized_sha256 === "string" && /^[a-f0-9]{64}$/i.test(record.normalized_sha256) &&
|
|
133
|
-
Number.isInteger(record.bytes) && record.bytes >= 0 &&
|
|
134
|
-
Number.isInteger(record.contract_occurrences) && record.contract_occurrences >= 0;
|
|
135
|
-
if (!valid)
|
|
136
|
-
return { ...fallback, error: "prompt observation failed parent contract/provenance binding" };
|
|
137
|
-
const occurrences = record.contract_occurrences;
|
|
138
|
-
return {
|
|
139
|
-
capture_version: "prompt-provenance-v1", request_index: requestIndex,
|
|
140
|
-
raw_sha256: record.raw_sha256, normalized_sha256: record.normalized_sha256,
|
|
141
|
-
normalization_rule: PROMPT_NORMALIZATION_RULE, bytes: record.bytes,
|
|
142
|
-
contract_sha256: expectedDigest, contract_bytes: expectedBytes, contract_occurrences: occurrences,
|
|
143
|
-
mechanism, status: statusFor(occurrences, mechanism, true),
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
//# sourceMappingURL=prompt-provenance.js.map
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { type GovernedArchiveRoute } from './archive-access.js';
|
|
2
|
-
export interface ReviewedExportPolicy {
|
|
3
|
-
archiveRoot: string;
|
|
4
|
-
access?: GovernedArchiveRoute;
|
|
5
|
-
manifestIds: string[];
|
|
6
|
-
destination: string;
|
|
7
|
-
expiresAt: number;
|
|
8
|
-
redact: string[];
|
|
9
|
-
maxBytes: number;
|
|
10
|
-
}
|
|
11
|
-
/** Duration is a cooperative access lease, not physical erasure or revocation of
|
|
12
|
-
* previously exported bytes. Existing archive/history is NEVER deleted. */
|
|
13
|
-
export declare function createReviewedArchiveExport(path: string, raw: ReviewedExportPolicy, policyPermissions: string[]): {
|
|
14
|
-
preview(now: number): {
|
|
15
|
-
content: string;
|
|
16
|
-
digest: string;
|
|
17
|
-
expiresAt: number;
|
|
18
|
-
};
|
|
19
|
-
export(previewDigest: string, reviewPermissions: string[], now: number): {
|
|
20
|
-
delivery: "local-reviewed-file";
|
|
21
|
-
previewDigest: string;
|
|
22
|
-
sha256: string;
|
|
23
|
-
bytes: number;
|
|
24
|
-
expiresAt: number;
|
|
25
|
-
physicalErasure: boolean;
|
|
26
|
-
};
|
|
27
|
-
};
|
|
28
|
-
export declare function openReviewedArchiveExport(path: string): {
|
|
29
|
-
preview(now: number): {
|
|
30
|
-
content: string;
|
|
31
|
-
digest: string;
|
|
32
|
-
expiresAt: number;
|
|
33
|
-
};
|
|
34
|
-
export(previewDigest: string, reviewPermissions: string[], now: number): {
|
|
35
|
-
delivery: "local-reviewed-file";
|
|
36
|
-
previewDigest: string;
|
|
37
|
-
sha256: string;
|
|
38
|
-
bytes: number;
|
|
39
|
-
expiresAt: number;
|
|
40
|
-
physicalErasure: boolean;
|
|
41
|
-
};
|
|
42
|
-
};
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { constants, openSync, closeSync, writeSync, fsyncSync, realpathSync, lstatSync } from 'node:fs';
|
|
2
|
-
import { dirname, isAbsolute, resolve } from 'node:path';
|
|
3
|
-
import { createHash } from 'node:crypto';
|
|
4
|
-
import { learningJournal, learningCopy, learningHash, learningFile } from './learning-journal.js';
|
|
5
|
-
import { readGovernedArchive } from './archive-access.js';
|
|
6
|
-
import { readArchiveSource } from './evidence-archive.js';
|
|
7
|
-
const sha = (b) => createHash('sha256').update(b).digest('hex');
|
|
8
|
-
const credential = /(?:ghp_|github_pat_|sk-(?:live-|proj-)?)[A-Za-z0-9_-]{20,}|-----BEGIN [A-Z ]*PRIVATE KEY-----|authorization\s*:\s*bearer\s+\S+/i;
|
|
9
|
-
/** Duration is a cooperative access lease, not physical erasure or revocation of
|
|
10
|
-
* previously exported bytes. Existing archive/history is NEVER deleted. */
|
|
11
|
-
export function createReviewedArchiveExport(path, raw, policyPermissions) {
|
|
12
|
-
const policy = learningCopy(raw), digest = learningHash(policy);
|
|
13
|
-
if (!policyPermissions.includes(digest))
|
|
14
|
-
throw Error('exact export policy permission required');
|
|
15
|
-
if (!isAbsolute(policy.archiveRoot) || !isAbsolute(policy.destination) || !Number.isSafeInteger(policy.expiresAt) || policy.expiresAt < 0 || !Number.isSafeInteger(policy.maxBytes) || policy.maxBytes < 1 || policy.maxBytes > 1024 * 1024 || !Array.isArray(policy.manifestIds) || !policy.manifestIds.length || policy.manifestIds.length > 16 || new Set(policy.manifestIds).size !== policy.manifestIds.length || policy.manifestIds.some(s => !/^[a-f0-9]{64}$/.test(s)) || !Array.isArray(policy.redact) || policy.redact.length > 64 || policy.redact.some(s => typeof s !== 'string' || !s.length || s.length > 4096))
|
|
16
|
-
throw Error('bounded reviewed export policy required');
|
|
17
|
-
learningJournal(path, { type: 'reviewed-export-v1', policy, policyDigest: digest });
|
|
18
|
-
return openReviewedArchiveExport(path);
|
|
19
|
-
}
|
|
20
|
-
export function openReviewedArchiveExport(path) {
|
|
21
|
-
const journal = learningJournal(path), first = journal.read()[0].value, policy = first.policy;
|
|
22
|
-
if (first.type !== 'reviewed-export-v1' || learningHash(policy) !== first.policyDigest)
|
|
23
|
-
throw Error('export policy changed');
|
|
24
|
-
const clock = (now) => { const rows = journal.read(), last = Math.max(0, ...rows.map(r => Number(r.value.now ?? 0))); if (!Number.isSafeInteger(now) || now < last)
|
|
25
|
-
throw Error('export clock rewind'); journal.append(rows.at(-1).id, { type: 'access', now }); if (now >= policy.expiresAt)
|
|
26
|
-
throw Error('export access lease expired'); };
|
|
27
|
-
const render = () => { let total = 0; const sources = policy.manifestIds.map(id => { const read = policy.access ? readGovernedArchive(policy.archiveRoot, id, policy.maxBytes - total, policy.access) : readArchiveSource(policy.archiveRoot, id, policy.maxBytes - total); if (read.status !== 'available')
|
|
28
|
-
throw Error('export source unavailable or over bound'); total += read.bytes.length; let content = new TextDecoder('utf-8', { fatal: true }).decode(read.bytes); for (const literal of policy.redact)
|
|
29
|
-
content = content.split(literal).join('[REDACTED]'); if (credential.test(content))
|
|
30
|
-
throw Error('credential pattern remains; revise explicit redaction policy'); return { manifestId: id, sourceSha256: sha(read.bytes), exportSha256: sha(content), representation: 'reviewed-view-not-authority', content }; }); const content = JSON.stringify({ version: 'reviewed-archive-export-v1', sources }, null, 2) + '\n'; if (Buffer.byteLength(content) > 2 * policy.maxBytes + 16384)
|
|
31
|
-
throw Error('redacted export size bound'); return { content, digest: learningHash({ policyDigest: first.policyDigest, content }), expiresAt: policy.expiresAt }; };
|
|
32
|
-
return { preview(now) { clock(now); const preview = render(), rows = journal.read(); journal.append(rows.at(-1).id, { type: 'previewed', digest: preview.digest, now }); return preview; },
|
|
33
|
-
export(previewDigest, reviewPermissions, now) {
|
|
34
|
-
clock(now);
|
|
35
|
-
if (!reviewPermissions.includes(previewDigest))
|
|
36
|
-
throw Error('exact reviewed preview permission required');
|
|
37
|
-
const rows = journal.read();
|
|
38
|
-
if (!rows.some(r => r.value.type === 'previewed' && r.value.digest === previewDigest))
|
|
39
|
-
throw Error('preview must precede export');
|
|
40
|
-
if (rows.some(r => r.value.type === 'export-claimed'))
|
|
41
|
-
throw Error('export already claimed; no automatic retry');
|
|
42
|
-
const preview = render();
|
|
43
|
-
if (preview.digest !== previewDigest)
|
|
44
|
-
throw Error('preview/source changed');
|
|
45
|
-
const parent = dirname(policy.destination), stat = lstatSync(parent);
|
|
46
|
-
if (!stat.isDirectory() || stat.isSymbolicLink() || realpathSync(parent) !== resolve(parent) || (stat.mode & 0o077) || (process.getuid && stat.uid !== process.getuid()))
|
|
47
|
-
throw Error('private owned export destination required');
|
|
48
|
-
const claim = journal.append(rows.at(-1).id, { type: 'export-claimed', digest: previewDigest, now });
|
|
49
|
-
const data = Buffer.from(preview.content), fd = openSync(policy.destination, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600);
|
|
50
|
-
try {
|
|
51
|
-
let offset = 0;
|
|
52
|
-
while (offset < data.length) {
|
|
53
|
-
const n = writeSync(fd, data, offset, data.length - offset);
|
|
54
|
-
if (!n)
|
|
55
|
-
throw Error('export write stalled');
|
|
56
|
-
offset += n;
|
|
57
|
-
}
|
|
58
|
-
fsyncSync(fd);
|
|
59
|
-
}
|
|
60
|
-
finally {
|
|
61
|
-
closeSync(fd);
|
|
62
|
-
}
|
|
63
|
-
const dir = openSync(parent, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
|
|
64
|
-
try {
|
|
65
|
-
fsyncSync(dir);
|
|
66
|
-
}
|
|
67
|
-
finally {
|
|
68
|
-
closeSync(dir);
|
|
69
|
-
}
|
|
70
|
-
if (!learningFile(policy.destination, data.length).equals(data))
|
|
71
|
-
throw Error('export readback mismatch');
|
|
72
|
-
const receipt = { delivery: 'local-reviewed-file', previewDigest, sha256: sha(data), bytes: data.length, expiresAt: policy.expiresAt, physicalErasure: false };
|
|
73
|
-
journal.append(claim.id, { type: 'exported', receipt, now });
|
|
74
|
-
return receipt;
|
|
75
|
-
},
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
//# sourceMappingURL=reviewed-archive-export.js.map
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
type Row = Record<string, any>;
|
|
2
|
-
export interface SessionTimelineEntry {
|
|
3
|
-
line: number;
|
|
4
|
-
timestamp: string | null;
|
|
5
|
-
kind: string;
|
|
6
|
-
excerpt: string;
|
|
7
|
-
}
|
|
8
|
-
export interface SessionObservation {
|
|
9
|
-
version: 'pi-session-observation-v1';
|
|
10
|
-
sessionId: string;
|
|
11
|
-
cwd: string;
|
|
12
|
-
timeline: SessionTimelineEntry[];
|
|
13
|
-
toolCalls: Record<string, number>;
|
|
14
|
-
userMessages: number;
|
|
15
|
-
assistantMessages: number;
|
|
16
|
-
modelRecords: {
|
|
17
|
-
provider: string;
|
|
18
|
-
model: string;
|
|
19
|
-
messages: number;
|
|
20
|
-
}[];
|
|
21
|
-
usage: {
|
|
22
|
-
fields: Record<string, {
|
|
23
|
-
sum: number;
|
|
24
|
-
reportedMessages: number;
|
|
25
|
-
}>;
|
|
26
|
-
assistantMessages: number;
|
|
27
|
-
scope: 'this-transcript-only';
|
|
28
|
-
};
|
|
29
|
-
lastActivity: string | null;
|
|
30
|
-
lastAssistantStop: string | null;
|
|
31
|
-
gaps: string[];
|
|
32
|
-
}
|
|
33
|
-
export declare function sessionText(bytes: Uint8Array): string;
|
|
34
|
-
export declare function sessionRows(bytes: Uint8Array): Row[];
|
|
35
|
-
/** Chronological visible history, including abandoned branches. Never interprets prose as approval. */
|
|
36
|
-
export declare function observePiSession(bytes: Uint8Array): SessionObservation;
|
|
37
|
-
export interface SessionAttempt {
|
|
38
|
-
executionId: string;
|
|
39
|
-
parentExecutionId: string | null;
|
|
40
|
-
logicalChildId: string;
|
|
41
|
-
agent: string | null;
|
|
42
|
-
state: string;
|
|
43
|
-
pane: string | null;
|
|
44
|
-
startedAt: string | null;
|
|
45
|
-
endedAt: string | null;
|
|
46
|
-
runningSeconds: number | null;
|
|
47
|
-
}
|
|
48
|
-
export declare function observeSessionLedger(bytes: Uint8Array): SessionAttempt[];
|
|
49
|
-
export declare function sessionScope(observation: SessionObservation, sourceSha256: string): string;
|
|
50
|
-
export {};
|
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
import { learningHash } from './learning-journal.js';
|
|
2
|
-
import { normalizePiDaddyLedgerV3 } from './trajectory.js';
|
|
3
|
-
export function sessionText(bytes) {
|
|
4
|
-
if (bytes.byteLength > 8 * 1024 * 1024)
|
|
5
|
-
throw Error('session source exceeds 8 MiB');
|
|
6
|
-
return new TextDecoder('utf-8', { fatal: true }).decode(bytes);
|
|
7
|
-
}
|
|
8
|
-
export function sessionRows(bytes) {
|
|
9
|
-
const text = sessionText(bytes);
|
|
10
|
-
if (!text.endsWith('\n'))
|
|
11
|
-
throw Error('incomplete session/ledger line; retry after the writer settles');
|
|
12
|
-
const lines = text.slice(0, -1).split('\n');
|
|
13
|
-
if (lines.length > 4096)
|
|
14
|
-
throw Error('session source exceeds 4096 records');
|
|
15
|
-
return lines.map((line, i) => {
|
|
16
|
-
let row;
|
|
17
|
-
try {
|
|
18
|
-
row = JSON.parse(line);
|
|
19
|
-
}
|
|
20
|
-
catch {
|
|
21
|
-
throw Error('invalid JSON record at line ' + (i + 1));
|
|
22
|
-
}
|
|
23
|
-
if (!row || typeof row !== 'object' || Array.isArray(row))
|
|
24
|
-
throw Error('object record required at line ' + (i + 1));
|
|
25
|
-
return row;
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
function value(v, max = 512) {
|
|
29
|
-
if (typeof v !== 'string' || !v.trim() || v.length > max)
|
|
30
|
-
throw Error('bounded session identifier required');
|
|
31
|
-
return v;
|
|
32
|
-
}
|
|
33
|
-
const stamp = (v) => typeof v === 'string' && Number.isFinite(Date.parse(v)) ? v : null;
|
|
34
|
-
const excerpt = (v) => v.length > 512 ? v.slice(0, 512) + ' … [excerpt; inspect retained source for complete text]' : v;
|
|
35
|
-
/** Chronological visible history, including abandoned branches. Never interprets prose as approval. */
|
|
36
|
-
export function observePiSession(bytes) {
|
|
37
|
-
const rows = sessionRows(bytes), header = rows[0];
|
|
38
|
-
if (header?.type !== 'session' || header.version !== 3)
|
|
39
|
-
throw Error('Pi session format version 3 required');
|
|
40
|
-
const sessionId = value(header.id), cwd = value(header.cwd, 4096), ids = new Set();
|
|
41
|
-
const timeline = [], tools = new Map(), models = new Map();
|
|
42
|
-
const fields = {};
|
|
43
|
-
let userMessages = 0, assistantMessages = 0, lastAssistantStop = null, lastActivity = null, opaque = 0;
|
|
44
|
-
const add = (i, row, kind, text) => timeline.push({ line: i + 1, timestamp: stamp(row.timestamp), kind, excerpt: excerpt(text) });
|
|
45
|
-
rows.forEach((row, i) => {
|
|
46
|
-
if (i && row.type === 'session')
|
|
47
|
-
throw Error('multiple session headers');
|
|
48
|
-
if (row.id !== undefined) {
|
|
49
|
-
const id = value(row.id);
|
|
50
|
-
if (ids.has(id))
|
|
51
|
-
throw Error('duplicate session record id');
|
|
52
|
-
ids.add(id);
|
|
53
|
-
}
|
|
54
|
-
lastActivity = stamp(row.timestamp) ?? lastActivity;
|
|
55
|
-
if (row.type === 'model_change') {
|
|
56
|
-
add(i, row, 'model-setting', [row.provider, row.modelId].filter(x => typeof x === 'string').join('/'));
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
if (row.type === 'thinking_level_change') {
|
|
60
|
-
add(i, row, 'thinking-setting', String(row.thinkingLevel ?? 'unknown'));
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
if (row.type !== 'message')
|
|
64
|
-
return;
|
|
65
|
-
const m = row.message;
|
|
66
|
-
if (!m || typeof m !== 'object' || Array.isArray(m))
|
|
67
|
-
throw Error('invalid session message');
|
|
68
|
-
const content = typeof m.content === 'string' ? [{ type: 'text', text: m.content }] : m.content;
|
|
69
|
-
if (!Array.isArray(content))
|
|
70
|
-
throw Error('invalid session message content');
|
|
71
|
-
const text = content.filter(c => c?.type === 'text' && typeof c.text === 'string').map(c => c.text).join('\n');
|
|
72
|
-
if (m.role === 'user') {
|
|
73
|
-
userMessages++;
|
|
74
|
-
add(i, row, 'user-message', text);
|
|
75
|
-
}
|
|
76
|
-
else if (m.role === 'assistant') {
|
|
77
|
-
assistantMessages++;
|
|
78
|
-
lastAssistantStop = typeof m.stopReason === 'string' ? m.stopReason : null;
|
|
79
|
-
const provider = typeof m.provider === 'string' ? m.provider : 'unknown', model = typeof m.model === 'string' ? m.model : 'unknown';
|
|
80
|
-
const key = JSON.stringify([provider, model]), record = models.get(key) ?? { provider, model, messages: 0 };
|
|
81
|
-
record.messages++;
|
|
82
|
-
models.set(key, record);
|
|
83
|
-
if (text)
|
|
84
|
-
add(i, row, 'assistant-message', text);
|
|
85
|
-
for (const field of ['input', 'output', 'cacheRead', 'cacheWrite', 'totalTokens']) {
|
|
86
|
-
const n = m.usage?.[field];
|
|
87
|
-
if (n === undefined)
|
|
88
|
-
continue;
|
|
89
|
-
if (!Number.isSafeInteger(n) || n < 0)
|
|
90
|
-
throw Error('invalid provider-reported usage');
|
|
91
|
-
const f = fields[field] ?? { sum: 0, reportedMessages: 0 };
|
|
92
|
-
f.sum += n;
|
|
93
|
-
f.reportedMessages++;
|
|
94
|
-
if (!Number.isSafeInteger(f.sum))
|
|
95
|
-
throw Error('usage sum overflow');
|
|
96
|
-
fields[field] = f;
|
|
97
|
-
}
|
|
98
|
-
for (const c of content)
|
|
99
|
-
if (c?.type === 'toolCall') {
|
|
100
|
-
const name = value(c.name, 128);
|
|
101
|
-
tools.set(name, (tools.get(name) ?? 0) + 1);
|
|
102
|
-
add(i, row, 'tool-call', name);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
else if (m.role === 'toolResult')
|
|
106
|
-
add(i, row, 'tool-result', String(m.toolName ?? 'unknown') + (m.isError === true ? ' — tool reported error' : ' — result recorded; success not inferred'));
|
|
107
|
-
else
|
|
108
|
-
opaque++;
|
|
109
|
-
});
|
|
110
|
-
const gaps = [
|
|
111
|
-
'Retrospective chronological transcript; branch selection and live continuity are not established.',
|
|
112
|
-
'Messages and tool results are evidence to inspect, not independently verified decisions, tests or acceptance.',
|
|
113
|
-
'Only explicit inputs are captured. Source revision, initial workspace and external user feedback may be missing.',
|
|
114
|
-
'Usage is provider-reported repeated-request usage, not unique context, billing or complete parent/child accounting.',
|
|
115
|
-
];
|
|
116
|
-
if (opaque)
|
|
117
|
-
gaps.push(opaque + ' message(s) with other roles omitted from the timeline; retained source is complete.');
|
|
118
|
-
if (!assistantMessages || Object.values(fields).some(f => f.reportedMessages < assistantMessages) || Object.keys(fields).length < 5)
|
|
119
|
-
gaps.push('Usage fields are missing; sums cover only reported messages.');
|
|
120
|
-
if (lastAssistantStop !== 'stop')
|
|
121
|
-
gaps.push('No final assistant stop observed; this snapshot may represent unfinished work.');
|
|
122
|
-
return { version: 'pi-session-observation-v1', sessionId, cwd, timeline, toolCalls: Object.fromEntries(tools), userMessages, assistantMessages, modelRecords: [...models.values()], usage: { fields, assistantMessages, scope: 'this-transcript-only' }, lastActivity, lastAssistantStop, gaps };
|
|
123
|
-
}
|
|
124
|
-
export function observeSessionLedger(bytes) {
|
|
125
|
-
const rows = sessionRows(bytes);
|
|
126
|
-
normalizePiDaddyLedgerV3(sessionText(bytes));
|
|
127
|
-
const attempts = new Map(), decisions = new Set(), lifecycles = new Set(), deadlines = new Map(), displayed = new Set();
|
|
128
|
-
for (const row of rows) {
|
|
129
|
-
if (!['capability_decision', 'child_lifecycle', 'workspace_lease'].includes(row.event) || String(row.childId).startsWith('check:'))
|
|
130
|
-
continue;
|
|
131
|
-
const id = value(row.executionId), old = attempts.get(id);
|
|
132
|
-
if (old && (old.parentExecutionId !== row.parentExecutionId || old.logicalChildId !== row.childId))
|
|
133
|
-
throw Error('execution identity changed');
|
|
134
|
-
const a = old ?? { executionId: id, parentExecutionId: row.parentExecutionId, logicalChildId: row.childId, agent: null, state: 'unknown', pane: null, startedAt: null, endedAt: null, runningSeconds: null };
|
|
135
|
-
if (row.event === 'workspace_lease') {
|
|
136
|
-
attempts.set(id, a);
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
displayed.add(id);
|
|
140
|
-
if (row.event === 'capability_decision') {
|
|
141
|
-
if (decisions.has(id))
|
|
142
|
-
throw Error('duplicate capability decision for one execution');
|
|
143
|
-
decisions.add(id);
|
|
144
|
-
a.agent = row.agentType;
|
|
145
|
-
a.state = row.blocked ? 'refused' : a.state === 'unknown' ? 'authorised' : a.state;
|
|
146
|
-
}
|
|
147
|
-
else {
|
|
148
|
-
if (a.endedAt)
|
|
149
|
-
throw Error('lifecycle after terminal execution');
|
|
150
|
-
if (lifecycles.has(id) && row.state === 'starting')
|
|
151
|
-
throw Error('duplicate starting lifecycle event');
|
|
152
|
-
if (deadlines.has(id) && stamp(row.deadlineAt) && deadlines.get(id) !== row.deadlineAt)
|
|
153
|
-
throw Error('lifecycle deadline changed');
|
|
154
|
-
if (stamp(row.deadlineAt))
|
|
155
|
-
deadlines.set(id, row.deadlineAt);
|
|
156
|
-
lifecycles.add(id);
|
|
157
|
-
a.state = row.state;
|
|
158
|
-
a.pane = row.herdrPaneId ?? a.pane;
|
|
159
|
-
if (row.state === 'running')
|
|
160
|
-
a.startedAt = a.startedAt ?? row.ts;
|
|
161
|
-
if (['completed', 'failed'].includes(row.state))
|
|
162
|
-
a.endedAt = row.ts;
|
|
163
|
-
}
|
|
164
|
-
if (a.startedAt && a.endedAt) {
|
|
165
|
-
a.runningSeconds = (Date.parse(a.endedAt) - Date.parse(a.startedAt)) / 1000;
|
|
166
|
-
if (a.runningSeconds < 0)
|
|
167
|
-
throw Error('negative execution duration');
|
|
168
|
-
}
|
|
169
|
-
attempts.set(id, a);
|
|
170
|
-
}
|
|
171
|
-
for (const a of attempts.values()) {
|
|
172
|
-
const seen = new Set();
|
|
173
|
-
let cursor = a;
|
|
174
|
-
while (cursor) {
|
|
175
|
-
if (seen.has(cursor.executionId))
|
|
176
|
-
throw Error('execution parent cycle');
|
|
177
|
-
seen.add(cursor.executionId);
|
|
178
|
-
cursor = cursor.parentExecutionId ? attempts.get(cursor.parentExecutionId) : undefined;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
return [...attempts.values()].filter(a => displayed.has(a.executionId));
|
|
182
|
-
}
|
|
183
|
-
export function sessionScope(observation, sourceSha256) {
|
|
184
|
-
return learningHash({ kind: 'retrospective-pi-session', sessionId: observation.sessionId, sourceSha256 });
|
|
185
|
-
}
|
|
186
|
-
//# sourceMappingURL=session-observation.js.map
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { observeSessionLedger, type SessionObservation } from './session-observation.js';
|
|
2
|
-
export interface SessionSourceSelection {
|
|
3
|
-
kind: 'parent' | 'child' | 'ledger' | 'feedback' | 'artifact';
|
|
4
|
-
path: string;
|
|
5
|
-
executionId?: string;
|
|
6
|
-
}
|
|
7
|
-
export interface Source {
|
|
8
|
-
kind: SessionSourceSelection['kind'];
|
|
9
|
-
path: string;
|
|
10
|
-
sha256: string;
|
|
11
|
-
bytes: number;
|
|
12
|
-
executionId: string | null;
|
|
13
|
-
}
|
|
14
|
-
export interface SessionImportPreview {
|
|
15
|
-
version: 'session-import-preview-v1';
|
|
16
|
-
digest: string;
|
|
17
|
-
sources: Source[];
|
|
18
|
-
parent: SessionObservation;
|
|
19
|
-
children: {
|
|
20
|
-
source: number;
|
|
21
|
-
executionId: string | null;
|
|
22
|
-
session: SessionObservation;
|
|
23
|
-
}[];
|
|
24
|
-
attempts: ReturnType<typeof observeSessionLedger>;
|
|
25
|
-
gaps: string[];
|
|
26
|
-
acceptance: 'not-assessed';
|
|
27
|
-
}
|
|
28
|
-
/** Explicit local selection only. Does not scan session directories, follow links, or contact workers. */
|
|
29
|
-
export declare function prepareSessionImport(selections: SessionSourceSelection[]): SessionImportPreview;
|
|
30
|
-
export declare function retainSessionImport(root: string, preview: SessionImportPreview, expectedDigest: string): string;
|
|
31
|
-
export declare function readRetainedSession(root: string, manifestId: string): {
|
|
32
|
-
acceptance: "not-assessed";
|
|
33
|
-
parent: SessionObservation;
|
|
34
|
-
children: {
|
|
35
|
-
source: number;
|
|
36
|
-
executionId: string | null;
|
|
37
|
-
session: SessionObservation;
|
|
38
|
-
}[];
|
|
39
|
-
attempts: import("./session-observation.js").SessionAttempt[];
|
|
40
|
-
gaps: string[];
|
|
41
|
-
version: "retrospective-session-v1";
|
|
42
|
-
manifestId: string;
|
|
43
|
-
selectionDigest: string;
|
|
44
|
-
sources: (Source & {
|
|
45
|
-
manifestId: string;
|
|
46
|
-
})[];
|
|
47
|
-
};
|
|
48
|
-
export declare function readRetainedSessionSource(root: string, manifestId: string, index: number): Buffer;
|