@skill-harness/adapters 0.16.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 -28
- package/dist/index.js +0 -28
- 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 -930
- package/dist/learning-workspace.js +0 -663
- 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/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,152 +0,0 @@
|
|
|
1
|
-
import { type CalibrationComponent, type CalibrationPrediction, type ExposurePolicy } from '@skill-harness/core';
|
|
2
|
-
import { type LearningCaseReference } from './learning-case.js';
|
|
3
|
-
export interface TrustLifecycleInput {
|
|
4
|
-
archiveRoot: string;
|
|
5
|
-
component: CalibrationComponent;
|
|
6
|
-
seed: string;
|
|
7
|
-
maxUnflagged: number;
|
|
8
|
-
cohort: {
|
|
9
|
-
incidentId: string;
|
|
10
|
-
manifestId: string;
|
|
11
|
-
flagged: boolean;
|
|
12
|
-
split: 'calibration' | 'heldout' | 'tuning';
|
|
13
|
-
}[];
|
|
14
|
-
exposure: ExposurePolicy | null;
|
|
15
|
-
}
|
|
16
|
-
export interface TrustReferenceOutcome {
|
|
17
|
-
kind: 'prediction' | 'unflagged';
|
|
18
|
-
targetId: string;
|
|
19
|
-
value: boolean;
|
|
20
|
-
evidenceManifestId: string;
|
|
21
|
-
referenceId: string;
|
|
22
|
-
}
|
|
23
|
-
export declare const trustPolicyDigest: (input: TrustLifecycleInput) => string;
|
|
24
|
-
export declare const trustOutcomeDigest: (outcome: TrustReferenceOutcome) => string;
|
|
25
|
-
export declare function createTrustLifecycle(directory: string, input: TrustLifecycleInput, authorizedPolicyDigests: readonly string[]): {
|
|
26
|
-
inspect: (now: number) => {
|
|
27
|
-
policyId: string;
|
|
28
|
-
component: CalibrationComponent;
|
|
29
|
-
calibration: {
|
|
30
|
-
version: "factory-calibration-v1";
|
|
31
|
-
reports: import("@skill-harness/core").CalibrationReport[];
|
|
32
|
-
excludedTuning: number;
|
|
33
|
-
referenceBasis: "independently-supplied-host-context";
|
|
34
|
-
advisoryOnly: true;
|
|
35
|
-
};
|
|
36
|
-
sampledIncidentIds: string[];
|
|
37
|
-
unflagged: {
|
|
38
|
-
sampled: number;
|
|
39
|
-
resolved: number;
|
|
40
|
-
misses: number;
|
|
41
|
-
unresolved: number;
|
|
42
|
-
conflicted: number;
|
|
43
|
-
};
|
|
44
|
-
attentionUsed: number;
|
|
45
|
-
retired: boolean;
|
|
46
|
-
exposures: {
|
|
47
|
-
mode: "ask" | "silent" | "retire";
|
|
48
|
-
reason: string;
|
|
49
|
-
id: string;
|
|
50
|
-
}[];
|
|
51
|
-
tip: string;
|
|
52
|
-
grantExpansion: false;
|
|
53
|
-
};
|
|
54
|
-
configuration: () => TrustLifecycleInput;
|
|
55
|
-
previewExposure: (now: number) => {
|
|
56
|
-
mode: "silent" | "ask" | "retire";
|
|
57
|
-
reason: string;
|
|
58
|
-
advisoryOnly: true;
|
|
59
|
-
} | {
|
|
60
|
-
mode: "retire";
|
|
61
|
-
reason: string;
|
|
62
|
-
} | {
|
|
63
|
-
mode: "silent";
|
|
64
|
-
reason: string;
|
|
65
|
-
};
|
|
66
|
-
history: () => import("./learning-journal.js").LearningEvent[];
|
|
67
|
-
previewCaseOutcome: (predictionId: string, reference: LearningCaseReference) => {
|
|
68
|
-
outcome: TrustReferenceOutcome;
|
|
69
|
-
caseReference: LearningCaseReference;
|
|
70
|
-
digest: string;
|
|
71
|
-
tip: string;
|
|
72
|
-
};
|
|
73
|
-
linkCaseOutcome(predictionId: string, reference: LearningCaseReference, authorizedDigests: readonly string[], referenceAuthor?: string): void;
|
|
74
|
-
predict(p: CalibrationPrediction): void;
|
|
75
|
-
correct(prior: string, p: CalibrationPrediction, reason: string): void;
|
|
76
|
-
outcome(value: TrustReferenceOutcome, authorizedReferenceDigests: readonly string[], referenceAuthor?: string): void;
|
|
77
|
-
expose(id: string, now: number): {
|
|
78
|
-
replayed: boolean;
|
|
79
|
-
mode: "ask" | "silent" | "retire";
|
|
80
|
-
reason: string;
|
|
81
|
-
} | {
|
|
82
|
-
replayed: boolean;
|
|
83
|
-
mode: "silent" | "ask" | "retire";
|
|
84
|
-
reason: string;
|
|
85
|
-
advisoryOnly: true;
|
|
86
|
-
};
|
|
87
|
-
};
|
|
88
|
-
/** Existing calibration is recomputed from durable scoped predictions and independently permitted
|
|
89
|
-
* retained labels. Applied exposure is an attention reservation, not proof a human saw a question. */
|
|
90
|
-
export declare function openTrustLifecycle(directory: string): {
|
|
91
|
-
inspect: (now: number) => {
|
|
92
|
-
policyId: string;
|
|
93
|
-
component: CalibrationComponent;
|
|
94
|
-
calibration: {
|
|
95
|
-
version: "factory-calibration-v1";
|
|
96
|
-
reports: import("@skill-harness/core").CalibrationReport[];
|
|
97
|
-
excludedTuning: number;
|
|
98
|
-
referenceBasis: "independently-supplied-host-context";
|
|
99
|
-
advisoryOnly: true;
|
|
100
|
-
};
|
|
101
|
-
sampledIncidentIds: string[];
|
|
102
|
-
unflagged: {
|
|
103
|
-
sampled: number;
|
|
104
|
-
resolved: number;
|
|
105
|
-
misses: number;
|
|
106
|
-
unresolved: number;
|
|
107
|
-
conflicted: number;
|
|
108
|
-
};
|
|
109
|
-
attentionUsed: number;
|
|
110
|
-
retired: boolean;
|
|
111
|
-
exposures: {
|
|
112
|
-
mode: "ask" | "silent" | "retire";
|
|
113
|
-
reason: string;
|
|
114
|
-
id: string;
|
|
115
|
-
}[];
|
|
116
|
-
tip: string;
|
|
117
|
-
grantExpansion: false;
|
|
118
|
-
};
|
|
119
|
-
configuration: () => TrustLifecycleInput;
|
|
120
|
-
previewExposure: (now: number) => {
|
|
121
|
-
mode: "silent" | "ask" | "retire";
|
|
122
|
-
reason: string;
|
|
123
|
-
advisoryOnly: true;
|
|
124
|
-
} | {
|
|
125
|
-
mode: "retire";
|
|
126
|
-
reason: string;
|
|
127
|
-
} | {
|
|
128
|
-
mode: "silent";
|
|
129
|
-
reason: string;
|
|
130
|
-
};
|
|
131
|
-
history: () => import("./learning-journal.js").LearningEvent[];
|
|
132
|
-
previewCaseOutcome: (predictionId: string, reference: LearningCaseReference) => {
|
|
133
|
-
outcome: TrustReferenceOutcome;
|
|
134
|
-
caseReference: LearningCaseReference;
|
|
135
|
-
digest: string;
|
|
136
|
-
tip: string;
|
|
137
|
-
};
|
|
138
|
-
linkCaseOutcome(predictionId: string, reference: LearningCaseReference, authorizedDigests: readonly string[], referenceAuthor?: string): void;
|
|
139
|
-
predict(p: CalibrationPrediction): void;
|
|
140
|
-
correct(prior: string, p: CalibrationPrediction, reason: string): void;
|
|
141
|
-
outcome(value: TrustReferenceOutcome, authorizedReferenceDigests: readonly string[], referenceAuthor?: string): void;
|
|
142
|
-
expose(id: string, now: number): {
|
|
143
|
-
replayed: boolean;
|
|
144
|
-
mode: "ask" | "silent" | "retire";
|
|
145
|
-
reason: string;
|
|
146
|
-
} | {
|
|
147
|
-
replayed: boolean;
|
|
148
|
-
mode: "silent" | "ask" | "retire";
|
|
149
|
-
reason: string;
|
|
150
|
-
advisoryOnly: true;
|
|
151
|
-
};
|
|
152
|
-
};
|
package/dist/trust-lifecycle.js
DELETED
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
import { calibratePredictions, recommendExposure } from '@skill-harness/core';
|
|
2
|
-
import { isAbsolute } from 'node:path';
|
|
3
|
-
import { readArchiveSource } from './evidence-archive.js';
|
|
4
|
-
import { learningCopy, learningHash, learningJournal, registerLearningStore, verifyLearningStore } from './learning-journal.js';
|
|
5
|
-
import { readLearningCase } from './learning-case.js';
|
|
6
|
-
export const trustPolicyDigest = (input) => learningHash({ version: 'trust-lifecycle-v1', input });
|
|
7
|
-
export const trustOutcomeDigest = (outcome) => learningHash({ version: 'trust-reference-v1', outcome });
|
|
8
|
-
function evidence(root, id) { const result = readArchiveSource(root, id); if (result.status !== 'available' || !['exact', 'redacted'].includes(result.reference.retention))
|
|
9
|
-
throw Error('independent retained evidence unavailable'); return result; }
|
|
10
|
-
const text = (v) => typeof v === 'string' && v.length > 0 && v.length <= 512 && !/[\u0000-\u001f\u007f]/.test(v);
|
|
11
|
-
function validate(input) {
|
|
12
|
-
if (!isAbsolute(input.archiveRoot) || Object.keys(input).sort().join() !== 'archiveRoot,cohort,component,exposure,maxUnflagged,seed' || !/^([a-f0-9]{64})$/.test(input.seed) || !Number.isSafeInteger(input.maxUnflagged) || input.maxUnflagged < 0 || input.maxUnflagged > 32 || !Array.isArray(input.cohort) || !input.cohort.length || input.cohort.length > 1024 || new Set(input.cohort.map(c => c.incidentId)).size !== input.cohort.length)
|
|
13
|
-
throw Error('invalid frozen trust cohort');
|
|
14
|
-
for (const c of input.cohort) {
|
|
15
|
-
if (Object.keys(c).sort().join() !== 'flagged,incidentId,manifestId,split' || !text(c.incidentId) || typeof c.flagged !== 'boolean' || !['calibration', 'heldout', 'tuning'].includes(c.split))
|
|
16
|
-
throw Error('invalid cohort row');
|
|
17
|
-
evidence(input.archiveRoot, c.manifestId);
|
|
18
|
-
}
|
|
19
|
-
const dummy = { id: 'validation', incidentId: 'validation', component: input.component, kind: input.component.kind === 'detector' ? 'positive' : 'approval', split: 'calibration' };
|
|
20
|
-
calibratePredictions([dummy]);
|
|
21
|
-
if (input.exposure) {
|
|
22
|
-
if (learningHash(input.exposure.component) !== learningHash(input.component))
|
|
23
|
-
throw Error('policy component scope mismatch');
|
|
24
|
-
const report = calibratePredictions([{ ...dummy, kind: input.exposure.kind, split: input.exposure.split }]).reports[0];
|
|
25
|
-
recommendExposure(report, input.exposure, 0);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
export function createTrustLifecycle(directory, input, authorizedPolicyDigests) {
|
|
29
|
-
const safe = learningCopy(input);
|
|
30
|
-
validate(safe);
|
|
31
|
-
const policyId = trustPolicyDigest(safe);
|
|
32
|
-
if (!authorizedPolicyDigests.includes(policyId))
|
|
33
|
-
throw Error('independent predeclared policy authority required');
|
|
34
|
-
const sampledIncidentIds = safe.cohort.filter(c => !c.flagged && c.split !== 'tuning').sort((a, b) => learningHash([safe.seed, a.incidentId]).localeCompare(learningHash([safe.seed, b.incidentId]))).slice(0, safe.maxUnflagged).map(c => c.incidentId);
|
|
35
|
-
const initial = { type: 'initial', kind: 'trust-lifecycle-v1', input: safe, policyId, sampledIncidentIds };
|
|
36
|
-
registerLearningStore(safe.archiveRoot, 'trust', policyId, directory, learningHash(initial));
|
|
37
|
-
try {
|
|
38
|
-
learningJournal(directory, initial);
|
|
39
|
-
}
|
|
40
|
-
catch (e) {
|
|
41
|
-
if (e.code !== 'EEXIST')
|
|
42
|
-
throw e;
|
|
43
|
-
const old = learningJournal(directory).read()[0].value;
|
|
44
|
-
if (learningHash(old) !== learningHash(initial))
|
|
45
|
-
throw Error('trust policy already predeclared');
|
|
46
|
-
}
|
|
47
|
-
return openTrustLifecycle(directory);
|
|
48
|
-
}
|
|
49
|
-
/** Existing calibration is recomputed from durable scoped predictions and independently permitted
|
|
50
|
-
* retained labels. Applied exposure is an attention reservation, not proof a human saw a question. */
|
|
51
|
-
export function openTrustLifecycle(directory) {
|
|
52
|
-
const journal = learningJournal(directory), initial = journal.read()[0].value;
|
|
53
|
-
if (initial.kind !== 'trust-lifecycle-v1' || initial.type !== 'initial')
|
|
54
|
-
throw Error('wrong trust journal');
|
|
55
|
-
const input = initial.input, sampledIncidentIds = initial.sampledIncidentIds;
|
|
56
|
-
validate(input);
|
|
57
|
-
const replay = () => {
|
|
58
|
-
verifyLearningStore(input.archiveRoot, 'trust', String(initial.policyId), directory, learningHash(initial));
|
|
59
|
-
const history = journal.read(), predictions = new Map(), allIds = new Set(), outcomes = new Map(), caseLinks = new Map(), exposures = new Map();
|
|
60
|
-
let retired = false, lastExposureAt = 0;
|
|
61
|
-
for (const { value: v } of history.slice(1)) {
|
|
62
|
-
if (v.type === 'prediction' || v.type === 'correction') {
|
|
63
|
-
const p = v.prediction;
|
|
64
|
-
calibratePredictions([p]);
|
|
65
|
-
if (allIds.has(p.id))
|
|
66
|
-
throw Error('duplicate prediction identity');
|
|
67
|
-
const c = input.cohort.find(c => c.incidentId === p.incidentId);
|
|
68
|
-
if (!c?.flagged || c.split !== p.split || learningHash(p.component) !== learningHash(input.component))
|
|
69
|
-
throw Error('prediction scope mismatch');
|
|
70
|
-
if (v.type === 'correction') {
|
|
71
|
-
const old = predictions.get(String(v.prior));
|
|
72
|
-
if (!old || old.incidentId !== p.incidentId)
|
|
73
|
-
throw Error('stale prediction correction');
|
|
74
|
-
predictions.delete(String(v.prior));
|
|
75
|
-
}
|
|
76
|
-
predictions.set(p.id, p);
|
|
77
|
-
allIds.add(p.id);
|
|
78
|
-
}
|
|
79
|
-
else if (v.type === 'outcome') {
|
|
80
|
-
const r = v.outcome;
|
|
81
|
-
if (outcomes.has(r.referenceId))
|
|
82
|
-
throw Error('duplicate reference identity');
|
|
83
|
-
outcomes.set(r.referenceId, r);
|
|
84
|
-
if (v.caseReference)
|
|
85
|
-
caseLinks.set(r.referenceId, v.caseReference);
|
|
86
|
-
}
|
|
87
|
-
else if (v.type === 'exposure') {
|
|
88
|
-
const id = String(v.id), result = v.result;
|
|
89
|
-
if (typeof v.now !== 'number' || !Number.isFinite(v.now) || v.now < lastExposureAt || exposures.has(id) || !['ask', 'silent', 'retire'].includes(result.mode))
|
|
90
|
-
throw Error('invalid exposure history');
|
|
91
|
-
lastExposureAt = v.now;
|
|
92
|
-
exposures.set(id, result);
|
|
93
|
-
retired ||= result.mode === 'retire';
|
|
94
|
-
}
|
|
95
|
-
else
|
|
96
|
-
throw Error('unknown trust history event');
|
|
97
|
-
}
|
|
98
|
-
return { history, predictions, allIds, outcomes, caseLinks, exposures, retired, lastExposureAt, tip: history.at(-1).id };
|
|
99
|
-
};
|
|
100
|
-
replay();
|
|
101
|
-
const inspect = (now) => {
|
|
102
|
-
if (!Number.isFinite(now) || now < 0)
|
|
103
|
-
throw Error('valid policy clock required');
|
|
104
|
-
const s = replay(), labels = [];
|
|
105
|
-
for (const r of s.outcomes.values()) {
|
|
106
|
-
evidence(input.archiveRoot, r.evidenceManifestId);
|
|
107
|
-
const p = s.predictions.get(r.targetId), link = s.caseLinks.get(r.referenceId);
|
|
108
|
-
if (link && !readLearningCase(input.archiveRoot, link).matched)
|
|
109
|
-
continue;
|
|
110
|
-
if (r.kind === 'prediction' && p)
|
|
111
|
-
labels.push({ ...p, id: r.referenceId, correct: r.value });
|
|
112
|
-
}
|
|
113
|
-
for (const p of s.predictions.values())
|
|
114
|
-
evidence(input.archiveRoot, input.cohort.find(c => c.incidentId === p.incidentId).manifestId);
|
|
115
|
-
const calibration = calibratePredictions([...s.predictions.values()], { outcomes: labels });
|
|
116
|
-
let misses = 0, resolved = 0, conflicted = 0;
|
|
117
|
-
for (const id of sampledIncidentIds) {
|
|
118
|
-
evidence(input.archiveRoot, input.cohort.find(c => c.incidentId === id).manifestId);
|
|
119
|
-
const values = new Set([...s.outcomes.values()].filter(r => r.kind === 'unflagged' && r.targetId === id).map(r => r.value));
|
|
120
|
-
if (values.size === 1) {
|
|
121
|
-
resolved++;
|
|
122
|
-
if (values.has(true))
|
|
123
|
-
misses++;
|
|
124
|
-
}
|
|
125
|
-
else if (values.size > 1)
|
|
126
|
-
conflicted++;
|
|
127
|
-
}
|
|
128
|
-
return { policyId: String(initial.policyId), component: learningCopy(input.component), calibration, sampledIncidentIds: [...sampledIncidentIds], unflagged: { sampled: sampledIncidentIds.length, resolved, misses, unresolved: sampledIncidentIds.length - resolved, conflicted }, attentionUsed: [...s.exposures.values()].filter(e => e.mode === 'ask').length, retired: s.retired, exposures: [...s.exposures].map(([id, result]) => ({ id, ...result })), tip: s.tip, grantExpansion: false };
|
|
129
|
-
};
|
|
130
|
-
const prediction = (p) => { const copy = learningCopy(p); calibratePredictions([copy]); const row = input.cohort.find(c => c.incidentId === copy.incidentId); if (!row?.flagged || row.split !== copy.split || learningHash(copy.component) !== learningHash(input.component))
|
|
131
|
-
throw Error('prediction scope mismatch'); evidence(input.archiveRoot, row.manifestId); return copy; };
|
|
132
|
-
const previewCaseOutcome = (predictionId, reference) => {
|
|
133
|
-
const ref = learningCopy(reference), s = replay(), p = s.predictions.get(predictionId), row = p && input.cohort.find(c => c.incidentId === p.incidentId), resolved = readLearningCase(input.archiveRoot, ref);
|
|
134
|
-
if (!p || p.kind !== 'positive' || row?.manifestId !== ref.manifestId || !resolved.matched || !['confirmed_defect', 'expected_behavior'].includes(resolved.current.disposition) || learningHash(resolved.candidate.detector) !== learningHash({ id: p.component.id, version: p.component.version, population: p.component.population }))
|
|
135
|
-
throw Error('current independent case outcome scope required');
|
|
136
|
-
const outcome = { kind: 'prediction', targetId: p.id, value: resolved.current.disposition === 'confirmed_defect', evidenceManifestId: ref.manifestId, referenceId: ref.decisionId };
|
|
137
|
-
return { outcome, caseReference: ref, digest: learningHash({ outcome, caseReference: ref }), tip: s.tip };
|
|
138
|
-
};
|
|
139
|
-
const previewExposure = (now) => {
|
|
140
|
-
const view = inspect(now), policy = input.exposure ? { ...input.exposure, attentionRemaining: Math.max(0, input.exposure.attentionRemaining - view.attentionUsed) } : null;
|
|
141
|
-
const report = view.calibration.reports.find(r => policy && r.kind === policy.kind && r.split === policy.split);
|
|
142
|
-
return view.retired ? { mode: 'retire', reason: 'previously-retired' } : report ? recommendExposure(report, policy, now) : { mode: 'silent', reason: policy ? 'insufficient-independent-evidence' : 'policy-unavailable' };
|
|
143
|
-
};
|
|
144
|
-
return { inspect, configuration: () => learningCopy(input), previewExposure, history: () => journal.read(), previewCaseOutcome,
|
|
145
|
-
linkCaseOutcome(predictionId, reference, authorizedDigests, referenceAuthor) {
|
|
146
|
-
if (referenceAuthor !== undefined && !text(referenceAuthor))
|
|
147
|
-
throw Error('explicit reference author required');
|
|
148
|
-
const preview = previewCaseOutcome(predictionId, reference);
|
|
149
|
-
if (!authorizedDigests.includes(preview.digest))
|
|
150
|
-
throw Error('independent exact reference authority required');
|
|
151
|
-
const s = replay();
|
|
152
|
-
if (s.tip !== preview.tip)
|
|
153
|
-
throw Error('stale case outcome snapshot');
|
|
154
|
-
const old = s.outcomes.get(preview.outcome.referenceId);
|
|
155
|
-
if (old) {
|
|
156
|
-
if (trustOutcomeDigest(old) !== trustOutcomeDigest(preview.outcome) || learningHash(s.caseLinks.get(old.referenceId) ?? null) !== learningHash(reference))
|
|
157
|
-
throw Error('reference identity conflict');
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
journal.append(s.tip, { type: 'outcome', outcome: preview.outcome, caseReference: preview.caseReference, authorityDigest: preview.digest, ...(referenceAuthor === undefined ? {} : { referenceAuthor }) });
|
|
161
|
-
},
|
|
162
|
-
predict(p) { const safe = prediction(p), s = replay(); if (s.allIds.has(safe.id)) {
|
|
163
|
-
if (learningHash(s.predictions.get(safe.id) ?? null) !== learningHash(safe))
|
|
164
|
-
throw Error('prediction identity conflict');
|
|
165
|
-
return;
|
|
166
|
-
} journal.append(s.tip, { type: 'prediction', prediction: safe }); },
|
|
167
|
-
correct(prior, p, reason) { const safe = prediction(p), s = replay(), old = s.predictions.get(prior); if (!old || old.incidentId !== safe.incidentId || s.allIds.has(safe.id) || !text(reason))
|
|
168
|
-
throw Error('stale or invalid prediction correction'); journal.append(s.tip, { type: 'correction', prior, prediction: safe, reason }); },
|
|
169
|
-
outcome(value, authorizedReferenceDigests, referenceAuthor) {
|
|
170
|
-
if (referenceAuthor !== undefined && !text(referenceAuthor))
|
|
171
|
-
throw Error('explicit reference author required');
|
|
172
|
-
const r = learningCopy(value);
|
|
173
|
-
if (Object.keys(r).sort().join() !== 'evidenceManifestId,kind,referenceId,targetId,value' || !['prediction', 'unflagged'].includes(r.kind) || !text(r.targetId) || !text(r.referenceId) || typeof r.value !== 'boolean')
|
|
174
|
-
throw Error('invalid reference outcome');
|
|
175
|
-
const digest = trustOutcomeDigest(r);
|
|
176
|
-
if (!authorizedReferenceDigests.includes(digest))
|
|
177
|
-
throw Error('independent exact reference authority required');
|
|
178
|
-
evidence(input.archiveRoot, r.evidenceManifestId);
|
|
179
|
-
const s = replay();
|
|
180
|
-
if (r.kind === 'prediction' ? !s.predictions.has(r.targetId) : !sampledIncidentIds.includes(r.targetId))
|
|
181
|
-
throw Error('outcome outside current prediction or frozen sample');
|
|
182
|
-
const old = s.outcomes.get(r.referenceId);
|
|
183
|
-
if (old) {
|
|
184
|
-
if (trustOutcomeDigest(old) !== digest)
|
|
185
|
-
throw Error('reference identity conflict');
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
journal.append(s.tip, { type: 'outcome', outcome: r, authorityDigest: digest, ...(referenceAuthor === undefined ? {} : { referenceAuthor }) });
|
|
189
|
-
},
|
|
190
|
-
expose(id, now) {
|
|
191
|
-
if (!text(id))
|
|
192
|
-
throw Error('exposure identity required');
|
|
193
|
-
const s = replay(), existing = s.exposures.get(id);
|
|
194
|
-
if (existing)
|
|
195
|
-
return { ...learningCopy(existing), replayed: true };
|
|
196
|
-
if (now < s.lastExposureAt)
|
|
197
|
-
throw Error('policy clock moved backwards');
|
|
198
|
-
const view = inspect(now);
|
|
199
|
-
if (view.tip !== s.tip)
|
|
200
|
-
throw Error('stale exposure snapshot');
|
|
201
|
-
const policy = input.exposure ? { ...input.exposure, attentionRemaining: Math.max(0, input.exposure.attentionRemaining - view.attentionUsed) } : null;
|
|
202
|
-
const report = view.calibration.reports.find(r => policy && r.kind === policy.kind && r.split === policy.split);
|
|
203
|
-
const result = s.retired ? { mode: 'retire', reason: 'previously-retired' } : report ? recommendExposure(report, policy, now) : { mode: 'silent', reason: policy ? 'insufficient-independent-evidence' : 'policy-unavailable' };
|
|
204
|
-
journal.append(s.tip, { type: 'exposure', id, now, result, policyId: initial.policyId, evidenceTip: s.tip });
|
|
205
|
-
return { ...learningCopy(result), replayed: false };
|
|
206
|
-
}
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
//# sourceMappingURL=trust-lifecycle.js.map
|