@skill-harness/adapters 0.11.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.
Files changed (117) hide show
  1. package/README.md +2 -1
  2. package/dist/archive-access.d.ts +58 -0
  3. package/dist/archive-access.js +61 -0
  4. package/dist/archive-checkpoint.d.ts +47 -0
  5. package/dist/archive-checkpoint.js +116 -0
  6. package/dist/archive-facts.d.ts +19 -0
  7. package/dist/archive-facts.js +20 -0
  8. package/dist/archive-observer.d.ts +21 -0
  9. package/dist/archive-observer.js +79 -0
  10. package/dist/archive-policy.d.ts +107 -0
  11. package/dist/archive-policy.js +135 -0
  12. package/dist/archive-read-capability.d.ts +49 -0
  13. package/dist/archive-read-capability.js +45 -0
  14. package/dist/archive-retention-policy.d.ts +52 -0
  15. package/dist/archive-retention-policy.js +49 -0
  16. package/dist/archived-work.d.ts +44 -0
  17. package/dist/archived-work.js +59 -0
  18. package/dist/blind-intervention.d.ts +29 -0
  19. package/dist/blind-intervention.js +211 -0
  20. package/dist/closed-schema.d.ts +6 -0
  21. package/dist/closed-schema.js +70 -14
  22. package/dist/codex-diagnostic-model.d.ts +14 -0
  23. package/dist/codex-diagnostic-model.js +39 -0
  24. package/dist/codex-host-observer.d.ts +111 -0
  25. package/dist/codex-host-observer.js +309 -0
  26. package/dist/codex-https.d.ts +7 -0
  27. package/dist/codex-https.js +221 -0
  28. package/dist/codex-model-profile.d.ts +38 -0
  29. package/dist/codex-model-profile.js +54 -0
  30. package/dist/codex-producer-ipc.d.ts +55 -0
  31. package/dist/codex-producer-ipc.js +67 -0
  32. package/dist/codex-sdk-transport.d.ts +50 -0
  33. package/dist/codex-sdk-transport.js +294 -0
  34. package/dist/codex-subscription.d.ts +114 -0
  35. package/dist/codex-subscription.js +268 -0
  36. package/dist/evidence-archive.d.ts +57 -0
  37. package/dist/evidence-archive.js +221 -0
  38. package/dist/execution-projection-schema.d.ts +10 -0
  39. package/dist/execution-projection-schema.js +30 -0
  40. package/dist/execution-retention-archive.d.ts +69 -0
  41. package/dist/execution-retention-archive.js +154 -0
  42. package/dist/generated/retention-v2-contract.d.ts +72 -0
  43. package/dist/generated/retention-v2-contract.js +105 -0
  44. package/dist/generated/retention-v2-json.d.ts +24 -0
  45. package/dist/generated/retention-v2-json.js +268 -0
  46. package/dist/generated/retention-v2-native-json.d.ts +2 -0
  47. package/dist/generated/retention-v2-native-json.js +33 -0
  48. package/dist/generated/retention-v2-native.d.ts +58 -0
  49. package/dist/generated/retention-v2-native.js +155 -0
  50. package/dist/generated/retention-v2-pin.d.ts +2 -0
  51. package/dist/generated/retention-v2-pin.js +4 -0
  52. package/dist/generated/retention-v2-types.d.ts +49 -0
  53. package/dist/generated/retention-v2-types.js +2 -0
  54. package/dist/generated/retention-v2-work-types.d.ts +260 -0
  55. package/dist/generated/retention-v2-work-types.js +13 -0
  56. package/dist/generated/work-v4/execution-id.d.ts +5 -0
  57. package/dist/generated/work-v4/execution-id.js +14 -0
  58. package/dist/generated/work-v4/json.d.ts +24 -0
  59. package/dist/generated/work-v4/json.js +268 -0
  60. package/dist/generated/work-v4/occurrences.d.ts +226 -0
  61. package/dist/generated/work-v4/occurrences.js +116 -0
  62. package/dist/generated/work-v4/pin.d.ts +1 -0
  63. package/dist/generated/work-v4/pin.js +3 -0
  64. package/dist/generated/work-v4/projection.d.ts +294 -0
  65. package/dist/generated/work-v4/projection.js +238 -0
  66. package/dist/generated/work-v4/reader.d.ts +24 -0
  67. package/dist/generated/work-v4/reader.js +109 -0
  68. package/dist/generated/work-v4/snapshot.d.ts +28 -0
  69. package/dist/generated/work-v4/snapshot.js +268 -0
  70. package/dist/generated/work-v4/types.d.ts +260 -0
  71. package/dist/generated/work-v4/types.js +13 -0
  72. package/dist/generated/work-v4/validation.d.ts +13 -0
  73. package/dist/generated/work-v4/validation.js +227 -0
  74. package/dist/index.d.ts +24 -0
  75. package/dist/index.js +24 -0
  76. package/dist/installed-review-session.d.ts +28 -0
  77. package/dist/installed-review-session.js +166 -0
  78. package/dist/intervention-run.d.ts +403 -0
  79. package/dist/intervention-run.js +169 -0
  80. package/dist/learning-case.d.ts +12 -0
  81. package/dist/learning-case.js +15 -0
  82. package/dist/learning-journal.d.ts +18 -0
  83. package/dist/learning-journal.js +256 -0
  84. package/dist/pi-daddy-ledger-v2.d.ts +4 -4
  85. package/dist/pi-daddy-ledger-v2.js +9 -7
  86. package/dist/pi-daddy-ledger-v3.d.ts +683 -0
  87. package/dist/pi-daddy-ledger-v3.js +961 -0
  88. package/dist/pi.js +115 -24
  89. package/dist/principal-payload-port.d.ts +25 -0
  90. package/dist/principal-payload-port.js +40 -0
  91. package/dist/producer-product.d.ts +70 -0
  92. package/dist/producer-product.js +186 -0
  93. package/dist/producer-review.d.ts +53 -0
  94. package/dist/producer-review.js +207 -0
  95. package/dist/prompt-capture-extension.d.ts +10 -0
  96. package/dist/prompt-capture-extension.js +26 -0
  97. package/dist/prompt-provenance.d.ts +22 -0
  98. package/dist/prompt-provenance.js +146 -0
  99. package/dist/reviewed-archive-export.d.ts +42 -0
  100. package/dist/reviewed-archive-export.js +78 -0
  101. package/dist/trajectory.d.ts +15 -9
  102. package/dist/trajectory.js +375 -55
  103. package/dist/trust-lifecycle.d.ts +128 -0
  104. package/dist/trust-lifecycle.js +200 -0
  105. package/dist/weekly-investigation.d.ts +237 -0
  106. package/dist/weekly-investigation.js +330 -0
  107. package/dist/work-candidates.d.ts +29 -0
  108. package/dist/work-candidates.js +78 -0
  109. package/dist/work-case-archive.d.ts +25 -0
  110. package/dist/work-case-archive.js +63 -0
  111. package/dist/work-case-review.d.ts +45 -0
  112. package/dist/work-case-review.js +220 -0
  113. package/dist/work-signal-cases.d.ts +15 -0
  114. package/dist/work-signal-cases.js +54 -0
  115. package/dist/work-signal-observation.d.ts +23 -0
  116. package/dist/work-signal-observation.js +89 -0
  117. package/package.json +4 -3
package/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # @skill-harness/adapters
2
2
 
3
3
  Harness adapters for [skill-harness](https://github.com/mojomanyana/skill-harness):
4
- the `pi` runner + `claude-code` judge routing.
4
+ the `pi` runner + `claude-code` judge routing, the frozen pi-daddy 0.17/v2
5
+ normalizer, and the separately pinned production ledger-v3 normalizer.
5
6
 
6
7
  Internal — use the `skill-harness` CLI. See the repo for docs.
7
8
 
@@ -0,0 +1,58 @@
1
+ export interface ArchiveAccessPolicy {
2
+ archiveRoot: string;
3
+ id: string;
4
+ expiresAt: number;
5
+ maxCalls: number;
6
+ maxBytes: number;
7
+ purposes: string[];
8
+ }
9
+ export interface ArchiveConsent {
10
+ manifestId: string;
11
+ purpose: string;
12
+ expiresAt: number;
13
+ }
14
+ export interface GovernedArchiveRoute {
15
+ directory: string;
16
+ purpose: string;
17
+ }
18
+ export declare function createArchiveAccess(path: string, raw: ArchiveAccessPolicy, permissions: string[]): {
19
+ inspect: () => {
20
+ policy: ArchiveAccessPolicy;
21
+ lastObservedNow: number;
22
+ consents: {
23
+ id: unknown;
24
+ grant: ArchiveConsent;
25
+ state: string;
26
+ }[];
27
+ calls: number;
28
+ bytes: number;
29
+ pending: number;
30
+ };
31
+ previewConsent: (g: ArchiveConsent) => string;
32
+ previewRevocation: (id: string) => string;
33
+ consent(raw: ArchiveConsent, permissions: string[], now: number): string;
34
+ revoke(id: string, permissions: string[], now: number): void;
35
+ read(manifestId: string, purpose: string, maxBytes: number, now?: number): import("./evidence-archive.js").ArchiveRead;
36
+ };
37
+ /** Shared cooperative access owner. No history deletion, authentication, OS
38
+ * deadline guarantee, or reinterpretation of legacy raw archive APIs. */
39
+ export declare function openArchiveAccess(path: string): {
40
+ inspect: () => {
41
+ policy: ArchiveAccessPolicy;
42
+ lastObservedNow: number;
43
+ consents: {
44
+ id: unknown;
45
+ grant: ArchiveConsent;
46
+ state: string;
47
+ }[];
48
+ calls: number;
49
+ bytes: number;
50
+ pending: number;
51
+ };
52
+ previewConsent: (g: ArchiveConsent) => string;
53
+ previewRevocation: (id: string) => string;
54
+ consent(raw: ArchiveConsent, permissions: string[], now: number): string;
55
+ revoke(id: string, permissions: string[], now: number): void;
56
+ read(manifestId: string, purpose: string, maxBytes: number, now?: number): import("./evidence-archive.js").ArchiveRead;
57
+ };
58
+ export declare function readGovernedArchive(root: string, manifestId: string, maxBytes: number, route: GovernedArchiveRoute, now?: number): import("./evidence-archive.js").ArchiveRead;
@@ -0,0 +1,61 @@
1
+ import { learningCopy, learningHash, learningJournal, registerLearningStore, verifyLearningStore } from './learning-journal.js';
2
+ import { readArchiveSource } from './evidence-archive.js';
3
+ export function createArchiveAccess(path, raw, permissions) {
4
+ const policy = learningCopy(raw), binding = learningHash(policy);
5
+ if (!permissions.includes(binding))
6
+ throw Error('exact archive policy permission required');
7
+ validate(policy);
8
+ registerLearningStore(policy.archiveRoot, 'access', learningHash(policy.id), path, binding);
9
+ learningJournal(path, { type: 'archive-access-v1', policy, binding });
10
+ return openArchiveAccess(path);
11
+ }
12
+ function validate(p) { if (typeof p.id !== 'string' || !p.id.length || p.id.length > 128 || !Number.isSafeInteger(p.expiresAt) || p.expiresAt < 0 || !Number.isSafeInteger(p.maxCalls) || p.maxCalls < 1 || p.maxCalls > 1024 || !Number.isSafeInteger(p.maxBytes) || p.maxBytes < 1 || p.maxBytes > 64 * 1024 * 1024 || !Array.isArray(p.purposes) || !p.purposes.length || p.purposes.length > 16 || new Set(p.purposes).size !== p.purposes.length || p.purposes.some(s => typeof s !== 'string' || !s.length || s.length > 128))
13
+ throw Error('bounded archive access policy required'); }
14
+ /** Shared cooperative access owner. No history deletion, authentication, OS
15
+ * deadline guarantee, or reinterpretation of legacy raw archive APIs. */
16
+ export function openArchiveAccess(path) {
17
+ const journal = learningJournal(path), first = journal.read()[0].value, policy = learningCopy(first.policy);
18
+ validate(policy);
19
+ if (first.type !== 'archive-access-v1' || learningHash(policy) !== first.binding)
20
+ throw Error('archive policy changed');
21
+ const history = () => { verifyLearningStore(policy.archiveRoot, 'access', learningHash(policy.id), path, String(first.binding)); return journal.read(); };
22
+ const clock = (now) => { const rows = history(); if (!Number.isSafeInteger(now) || now < Math.max(0, ...rows.map(e => Number(e.value.now ?? 0))))
23
+ throw Error('archive access clock rewind'); journal.append(rows.at(-1).id, { type: 'clock', now }); if (now >= policy.expiresAt)
24
+ throw Error('archive access expired'); };
25
+ const consent = (raw) => { const g = learningCopy(raw); if (!/^[a-f0-9]{64}$/.test(g.manifestId) || !policy.purposes.includes(g.purpose) || !Number.isSafeInteger(g.expiresAt) || g.expiresAt < 0 || g.expiresAt > policy.expiresAt)
26
+ throw Error('consent outside policy'); return g; };
27
+ const digest = (g) => learningHash({ policy: String(first.binding), consent: consent(g) });
28
+ const revokeDigest = (id) => learningHash({ policy: String(first.binding), revoke: id });
29
+ const inspect = () => { const rows = history(), claims = rows.filter(e => e.value.type === 'read-claim'), lastObservedNow = Math.max(0, ...rows.map(e => Number(e.value.now ?? 0))); return { policy: learningCopy(policy), lastObservedNow, consents: rows.filter(e => e.value.type === 'consented').map(e => { const grant = e.value.grant; return { id: e.value.consentId, grant: learningCopy(grant), state: rows.some(r => r.value.type === 'revoked' && r.value.consentId === e.value.consentId) ? 'revoked' : lastObservedNow >= Math.min(policy.expiresAt, grant.expiresAt) ? 'expired' : 'not-revoked-at-last-observed-clock' }; }), calls: claims.length, bytes: claims.reduce((n, e) => { const settled = rows.find(s => s.value.type === 'read-settled' && s.value.claim === e.id); return n + Number(settled ? settled.value.bytes : e.value.reserved); }, 0), pending: claims.filter(e => !rows.some(s => s.value.type === 'read-settled' && s.value.claim === e.id)).length }; };
30
+ return { inspect, previewConsent: digest, previewRevocation: revokeDigest,
31
+ consent(raw, permissions, now) { clock(now); const g = consent(raw), id = digest(g), rows = history(); if (!permissions.includes(id) || now >= g.expiresAt)
32
+ throw Error('exact current consent permission required'); if (rows.some(e => e.value.type === 'revoked' && e.value.consentId === id))
33
+ throw Error('consent revoked'); if (!rows.some(e => e.value.type === 'consented' && e.value.consentId === id))
34
+ journal.append(rows.at(-1).id, { type: 'consented', consentId: id, grant: g, now }); return id; },
35
+ revoke(id, permissions, now) { clock(now); const rows = history(); if (!permissions.includes(revokeDigest(id)) || !rows.some(e => e.value.type === 'consented' && e.value.consentId === id))
36
+ throw Error('exact revocation permission required'); if (!rows.some(e => e.value.type === 'revoked' && e.value.consentId === id))
37
+ journal.append(rows.at(-1).id, { type: 'revoked', consentId: id, now }); },
38
+ read(manifestId, purpose, maxBytes, now = Date.now()) {
39
+ clock(now);
40
+ const rows = history(), valid = rows.filter(e => e.value.type === 'consented').filter(e => { const g = e.value.grant; return g.manifestId === manifestId && g.purpose === purpose && now < g.expiresAt && !rows.some(r => r.value.type === 'revoked' && r.value.consentId === e.value.consentId); });
41
+ if (valid.length !== 1)
42
+ throw Error('unique active manifest/purpose consent required');
43
+ if (!Number.isSafeInteger(maxBytes) || maxBytes < 0 || maxBytes > 8 * 1024 * 1024)
44
+ throw Error('archive read bound');
45
+ const used = inspect();
46
+ if (used.calls >= policy.maxCalls || maxBytes > policy.maxBytes - used.bytes)
47
+ throw Error('shared archive budget exhausted');
48
+ const claim = journal.append(rows.at(-1).id, { type: 'read-claim', manifestId, purpose, consentId: valid[0].value.consentId, reserved: maxBytes, now });
49
+ const result = readArchiveSource(policy.archiveRoot, manifestId, maxBytes);
50
+ journal.append(claim.id, { type: 'read-settled', claim: claim.id, bytes: result.status === 'available' ? result.bytes.length : 0, status: result.status, now });
51
+ return result;
52
+ },
53
+ };
54
+ }
55
+ export function readGovernedArchive(root, manifestId, maxBytes, route, now = Date.now()) {
56
+ const access = openArchiveAccess(route.directory);
57
+ if (access.inspect().policy.archiveRoot !== root)
58
+ throw Error('governed archive root mismatch');
59
+ return access.read(manifestId, route.purpose, maxBytes, now);
60
+ }
61
+ //# sourceMappingURL=archive-access.js.map
@@ -0,0 +1,47 @@
1
+ import { type ArchiveRead, type ArchiveSourceInput, type ArchiveRetention } from "./evidence-archive.js";
2
+ export type SnapshotChange = "initial" | "append" | "replaced" | "unknown";
3
+ export interface ArchiveCheckpoint {
4
+ version: "archive-checkpoint-v1";
5
+ sourceId: string;
6
+ sourceManifestId: string;
7
+ sourceSha256: string;
8
+ sourceBytes: number;
9
+ previousCheckpointId: string | null;
10
+ lineage: string;
11
+ declaredParser: {
12
+ id: string;
13
+ version: string;
14
+ };
15
+ reader: "jsonl-syntax-v1";
16
+ retention: ArchiveRetention;
17
+ change: SnapshotChange;
18
+ syntax: "complete" | "partial" | "error" | "unavailable";
19
+ completeBytes: number;
20
+ pendingBytes: number;
21
+ invalidLines: number;
22
+ issues: string[];
23
+ activeBranch: null;
24
+ acceptance: "not-assessed";
25
+ }
26
+ export interface ArchivedRecord {
27
+ id: string;
28
+ line: number;
29
+ start: number;
30
+ end: number;
31
+ value: unknown;
32
+ }
33
+ export interface CheckpointRead {
34
+ checkpoint: ArchiveCheckpoint;
35
+ source: ArchiveRead;
36
+ records: ArchivedRecord[];
37
+ }
38
+ /** Restart from the immutable checkpoint ID. This never advances a worker or invokes a gate. */
39
+ export declare function readArchiveCheckpoint(root: string, checkpointId: string): CheckpointRead;
40
+ /** Caller supplies an explicit snapshot, policy and prior receipt. No inferred active branch or acceptance. */
41
+ export declare function ingestArchiveSnapshot(root: string, input: ArchiveSourceInput & {
42
+ previousCheckpointId?: string;
43
+ }): CheckpointRead & {
44
+ checkpointId: string;
45
+ change: SnapshotChange | "repeat";
46
+ newRecords: ArchivedRecord[];
47
+ };
@@ -0,0 +1,116 @@
1
+ import { createHash } from "node:crypto";
2
+ import { parseArchivedJsonl, readArchiveSource, retainArchiveSource, } from "./evidence-archive.js";
3
+ const hash = (value) => createHash("sha256").update(value).digest("hex");
4
+ const SHA = /^[a-f0-9]{64}$/;
5
+ const LIMIT = 8 * 1024 * 1024;
6
+ const parserEqual = (a, b) => a.id === b.id && a.version === b.version;
7
+ function reject() { throw new Error("invalid archive checkpoint"); }
8
+ function parseCheckpoint(bytes) {
9
+ const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
10
+ const v = JSON.parse(text);
11
+ if (JSON.stringify(v) !== text)
12
+ reject();
13
+ const names = ["version", "sourceId", "sourceManifestId", "sourceSha256", "sourceBytes", "previousCheckpointId", "lineage", "declaredParser", "reader", "retention", "change", "syntax", "completeBytes", "pendingBytes", "invalidLines", "issues", "activeBranch", "acceptance"];
14
+ if (!v || Object.keys(v).length !== names.length || Object.keys(v).some(k => !names.includes(k))
15
+ || v.version !== "archive-checkpoint-v1" || v.reader !== "jsonl-syntax-v1"
16
+ || typeof v.sourceId !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(v.sourceId)
17
+ || ![v.sourceManifestId, v.sourceSha256, v.lineage].every(h => typeof h === "string" && SHA.test(h))
18
+ || !(v.previousCheckpointId === null || (typeof v.previousCheckpointId === "string" && SHA.test(v.previousCheckpointId)))
19
+ || ![v.sourceBytes, v.completeBytes, v.pendingBytes, v.invalidLines].every(n => Number.isSafeInteger(n) && n >= 0 && n <= LIMIT)
20
+ || v.completeBytes + v.pendingBytes !== v.sourceBytes
21
+ || !v.declaredParser || Object.keys(v.declaredParser).sort().join() !== "id,version"
22
+ || ![v.declaredParser.id, v.declaredParser.version].every(s => typeof s === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(s))
23
+ || !["exact", "redacted", "reference-only"].includes(v.retention)
24
+ || !["initial", "append", "replaced", "unknown"].includes(v.change)
25
+ || !["complete", "partial", "error", "unavailable"].includes(v.syntax)
26
+ || (v.retention === "reference-only" && (v.syntax !== "unavailable" || v.completeBytes !== 0 || v.invalidLines !== 0))
27
+ || !Array.isArray(v.issues) || v.issues.length > 16 || v.issues.some(i => typeof i !== "string" || i.length > 128)
28
+ || v.activeBranch !== null || v.acceptance !== "not-assessed")
29
+ reject();
30
+ return v;
31
+ }
32
+ function records(checkpoint, source) {
33
+ if (source.status !== "available")
34
+ return [];
35
+ const parsed = parseArchivedJsonl(source.bytes);
36
+ return parsed.records.map(record => ({
37
+ ...record, id: hash(JSON.stringify([checkpoint.sourceId, checkpoint.lineage, record.start, hash(source.bytes.subarray(record.start, record.end))])),
38
+ }));
39
+ }
40
+ /** Restart from the immutable checkpoint ID. This never advances a worker or invokes a gate. */
41
+ export function readArchiveCheckpoint(root, checkpointId) {
42
+ const stored = readArchiveSource(root, checkpointId);
43
+ if (stored.status !== "available" || stored.reference.retention !== "exact")
44
+ reject();
45
+ const checkpoint = parseCheckpoint(stored.bytes);
46
+ const source = readArchiveSource(root, checkpoint.sourceManifestId);
47
+ if (source.status === "available") {
48
+ const ref = source.reference;
49
+ if (ref.sourceId !== checkpoint.sourceId || ref.sha256 !== checkpoint.sourceSha256 || ref.bytes !== checkpoint.sourceBytes
50
+ || ref.retention !== checkpoint.retention || !parserEqual(ref.parser, checkpoint.declaredParser))
51
+ reject();
52
+ const actual = parseArchivedJsonl(source.bytes);
53
+ if (actual.status !== checkpoint.syntax || actual.completeBytes !== checkpoint.completeBytes
54
+ || actual.pendingBytes !== checkpoint.pendingBytes || actual.errors.length !== checkpoint.invalidLines)
55
+ reject();
56
+ }
57
+ return { checkpoint, source, records: records(checkpoint, source) };
58
+ }
59
+ /** Caller supplies an explicit snapshot, policy and prior receipt. No inferred active branch or acceptance. */
60
+ export function ingestArchiveSnapshot(root, input) {
61
+ // Inspect the prior bytes BEFORE storing this snapshot, so reacquisition cannot disguise a prior gap.
62
+ const previous = input.previousCheckpointId ? readArchiveCheckpoint(root, input.previousCheckpointId) : undefined;
63
+ if (previous && previous.checkpoint.sourceId !== input.sourceId)
64
+ throw new Error("checkpoint source mismatch");
65
+ const captured = retainArchiveSource(root, input);
66
+ const source = readArchiveSource(root, captured.manifestId);
67
+ const issues = [];
68
+ let change = previous ? "unknown" : "initial";
69
+ let lineage = captured.manifestId;
70
+ if (previous) {
71
+ if (!parserEqual(previous.checkpoint.declaredParser, captured.reference.parser) || previous.checkpoint.retention !== input.retention) {
72
+ issues.push("parser-or-representation-changed");
73
+ }
74
+ else if (previous.source.status !== "available") {
75
+ issues.push("previous-content-unavailable");
76
+ }
77
+ else if (input.retention !== "exact") {
78
+ issues.push("non-exact-continuity-unknown");
79
+ }
80
+ else if (source.status === "available") {
81
+ if (captured.manifestId === previous.checkpoint.sourceManifestId) {
82
+ return { ...previous, checkpointId: input.previousCheckpointId, change: "repeat", newRecords: [] };
83
+ }
84
+ if (source.bytes.length >= previous.source.bytes.length && source.bytes.subarray(0, previous.source.bytes.length).equals(previous.source.bytes)) {
85
+ change = "append";
86
+ lineage = previous.checkpoint.lineage;
87
+ }
88
+ else {
89
+ change = "replaced";
90
+ issues.push("source-replaced");
91
+ }
92
+ }
93
+ }
94
+ if (input.retention === "redacted")
95
+ issues.push("redacted-representation");
96
+ if (source.status !== "available")
97
+ issues.push("content-not-retained");
98
+ const syntax = source.status === "available" ? parseArchivedJsonl(source.bytes) : undefined;
99
+ const checkpoint = {
100
+ version: "archive-checkpoint-v1", sourceId: input.sourceId,
101
+ sourceManifestId: captured.manifestId, sourceSha256: captured.reference.sha256, sourceBytes: captured.reference.bytes,
102
+ previousCheckpointId: input.previousCheckpointId ?? null, lineage,
103
+ declaredParser: captured.reference.parser, reader: "jsonl-syntax-v1", retention: input.retention, change,
104
+ syntax: syntax?.status ?? "unavailable", completeBytes: syntax?.completeBytes ?? 0,
105
+ pendingBytes: syntax?.pendingBytes ?? captured.reference.bytes, invalidLines: syntax?.errors.length ?? 0,
106
+ issues, activeBranch: null, acceptance: "not-assessed",
107
+ };
108
+ const result = retainArchiveSource(root, {
109
+ sourceId: `checkpoint-${hash(input.sourceId)}`, parser: { id: "archive-checkpoint", version: "1" },
110
+ retention: "exact", bytes: Buffer.from(JSON.stringify(checkpoint)),
111
+ });
112
+ const all = records(checkpoint, source);
113
+ return { checkpointId: result.manifestId, checkpoint, source, records: all, change,
114
+ newRecords: change === "append" ? all.filter(record => record.start >= previous.checkpoint.completeBytes) : all };
115
+ }
116
+ //# sourceMappingURL=archive-checkpoint.js.map
@@ -0,0 +1,19 @@
1
+ import type { WorkSignalFacts } from '@skill-harness/core';
2
+ import { type GovernedArchiveRoute } from './archive-access.js';
3
+ /** Operational fixed-order fact adapter, not arbitrary model/host fact inference.
4
+ * The caller supplies independent scope/source pins; all actual bytes use consent. */
5
+ export declare function readFixedOrderFacts(root: string, manifestId: string, route: GovernedArchiveRoute, raw: {
6
+ producerCommit: string;
7
+ scopeDigest: string;
8
+ population: string;
9
+ obligations: string[];
10
+ }, now?: number): {
11
+ facts: WorkSignalFacts;
12
+ source: {
13
+ manifestId: string;
14
+ sha256: string;
15
+ producerCommit: string;
16
+ };
17
+ unknownNodes: any;
18
+ acceptance: "not-assessed";
19
+ };
@@ -0,0 +1,20 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { readGovernedArchive } from './archive-access.js';
3
+ import { learningCopy } from './learning-journal.js';
4
+ /** Operational fixed-order fact adapter, not arbitrary model/host fact inference.
5
+ * The caller supplies independent scope/source pins; all actual bytes use consent. */
6
+ export function readFixedOrderFacts(root, manifestId, route, raw, now = Date.now()) {
7
+ const expected = learningCopy(raw);
8
+ if (!/^[a-f0-9]{40}$/.test(expected.producerCommit) || !/^[a-f0-9]{64}$/.test(expected.scopeDigest) || typeof expected.population !== 'string' || !expected.population.length || expected.population.length > 512 || /[\u0000-\u001f\u007f]/.test(expected.population) || !Array.isArray(expected.obligations) || expected.obligations.length > 128 || expected.obligations.some(h => !/^[a-f0-9]{64}$/.test(h)))
9
+ throw Error('explicit fact source/scope pins required');
10
+ const read = readGovernedArchive(root, manifestId, 1024 * 1024, route, now);
11
+ if (read.status !== 'available' || read.reference.retention !== 'exact' || read.reference.parser.id !== 'factory-order-readback' || read.reference.parser.version !== '1')
12
+ throw Error('exact governed order readback required');
13
+ const value = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(read.bytes));
14
+ if (value.kind !== 'actual-fixed-profile-order' || value.producerCommit !== expected.producerCommit || !Array.isArray(value.nodes) || !value.nodes.length || value.nodes.length > 128 || value.nodes.some((n) => !n || !expected.obligations.includes(n.obligation?.digest) || typeof n.state !== 'string'))
15
+ throw Error('order facts source/scope mismatch');
16
+ const digest = createHash('sha256').update(read.bytes).digest('hex');
17
+ const facts = { scopeDigest: expected.scopeDigest, version: 'fixed-order-facts-v1', population: expected.population, expectedWaits: [], checkpoints: [], violations: value.nodes.filter((n) => n.state === 'exhausted').map((n) => ({ obligationDigest: n.obligation.digest, status: 'FAIL', evidence: digest })), priorAccepted: [] };
18
+ return { facts, source: { manifestId, sha256: digest, producerCommit: expected.producerCommit }, unknownNodes: value.nodes.filter((n) => !['satisfied', 'exhausted'].includes(n.state)).length, acceptance: 'not-assessed' };
19
+ }
20
+ //# sourceMappingURL=archive-facts.js.map
@@ -0,0 +1,21 @@
1
+ import { ingestPolicySource } from "./archive-policy.js";
2
+ export interface ArchiveObserverOptions {
3
+ policyPath: string;
4
+ sourceId: string;
5
+ intervalMs: number;
6
+ maxPolls: number;
7
+ previousCheckpointId?: string;
8
+ signal?: AbortSignal;
9
+ /** Optional synchronous metadata consumer, never a worker hook. Its failure does not stop capture. */
10
+ onObservation?: (value: ReturnType<typeof ingestPolicySource>) => void;
11
+ }
12
+ /** Bounded external file observation. No worker process, socket, session API, prompt or control channel is used. */
13
+ export declare function observeArchiveSource(options: ArchiveObserverOptions): Promise<{
14
+ observerId: `${string}-${string}-${string}-${string}-${string}`;
15
+ polls: number;
16
+ lastCheckpointId: string | null;
17
+ receiptId: string;
18
+ stopped: "aborted" | "poll-limit" | "policy-change";
19
+ gaps: string[];
20
+ workerInteractions: 0;
21
+ }>;
@@ -0,0 +1,79 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { archivePolicyBinding, ingestPolicySource } from "./archive-policy.js";
3
+ import { retainArchiveSource } from "./evidence-archive.js";
4
+ function cadence(ms, signal) {
5
+ return new Promise(resolve => {
6
+ if (signal?.aborted) {
7
+ resolve();
8
+ return;
9
+ }
10
+ const finish = () => { clearTimeout(timer); signal?.removeEventListener("abort", finish); resolve(); };
11
+ const timer = setTimeout(finish, ms);
12
+ signal?.addEventListener("abort", finish, { once: true });
13
+ });
14
+ }
15
+ /** Bounded external file observation. No worker process, socket, session API, prompt or control channel is used. */
16
+ export async function observeArchiveSource(options) {
17
+ if (!Number.isSafeInteger(options.intervalMs) || options.intervalMs < 1 || options.intervalMs > 60000
18
+ || !Number.isSafeInteger(options.maxPolls) || options.maxPolls < 1 || options.maxPolls > 128)
19
+ throw new Error("bounded observer interval/poll count required");
20
+ const binding = archivePolicyBinding(options.policyPath, options.sourceId), observerId = randomUUID(), startedAt = new Date().toISOString();
21
+ let lastCheckpointId = options.previousCheckpointId ?? null, polls = 0, receiptId = "", consumer = options.onObservation;
22
+ let stopped = "poll-limit", terminal = false;
23
+ const gaps = new Set();
24
+ const records = [];
25
+ const persist = () => retainArchiveSource(binding.archiveRoot, {
26
+ sourceId: `observer-${observerId}`, parser: { id: "archive-observer", version: "1" }, retention: "exact",
27
+ bytes: Buffer.from(JSON.stringify({ version: "archive-observer-v1", observerId, policySha256: binding.policySha256, sourceId: binding.sourceId,
28
+ startedAt, polls, lastCheckpointId, state: terminal ? "terminal" : "running", stopped: terminal ? stopped : null, gaps: [...gaps].sort(), records, workerInteractions: 0, continuity: "bounded-observations-not-complete-live-coverage" })),
29
+ }).manifestId;
30
+ for (let i = 0; i < options.maxPolls; i++) {
31
+ if (options.signal?.aborted) {
32
+ stopped = "aborted";
33
+ break;
34
+ }
35
+ try {
36
+ if (archivePolicyBinding(options.policyPath, options.sourceId).policySha256 !== binding.policySha256)
37
+ throw new Error("changed");
38
+ }
39
+ catch {
40
+ gaps.add("policy-changed-or-unavailable");
41
+ stopped = "policy-change";
42
+ break;
43
+ }
44
+ polls++;
45
+ try {
46
+ const observation = ingestPolicySource(options.policyPath, options.sourceId, lastCheckpointId ?? undefined, binding.policySha256);
47
+ lastCheckpointId = observation.checkpointId;
48
+ records.push({ poll: polls, at: new Date().toISOString(), checkpointId: lastCheckpointId, state: "captured" });
49
+ if (consumer) {
50
+ try {
51
+ const returned = consumer(observation);
52
+ if (returned && typeof returned.then === "function") {
53
+ gaps.add("async-consumer-outcome-unobserved");
54
+ consumer = undefined;
55
+ // The unsupported asynchronous outcome is already an explicit durable gap; never await it on capture.
56
+ void Promise.resolve(returned).catch(() => undefined);
57
+ }
58
+ }
59
+ catch {
60
+ gaps.add("observation-consumer-failed");
61
+ consumer = undefined;
62
+ }
63
+ }
64
+ }
65
+ catch {
66
+ gaps.add("source-observation-failed");
67
+ records.push({ poll: polls, at: new Date().toISOString(), checkpointId: lastCheckpointId, state: "gap" });
68
+ }
69
+ receiptId = persist();
70
+ if (i + 1 < options.maxPolls)
71
+ await cadence(options.intervalMs, options.signal);
72
+ }
73
+ if (options.signal?.aborted)
74
+ stopped = "aborted";
75
+ terminal = true;
76
+ receiptId = persist();
77
+ return { observerId, polls, lastCheckpointId, receiptId, stopped, gaps: [...gaps].sort(), workerInteractions: 0 };
78
+ }
79
+ //# sourceMappingURL=archive-observer.js.map
@@ -0,0 +1,107 @@
1
+ import { type ArchiveRetention } from "./evidence-archive.js";
2
+ export interface PolicySource {
3
+ id: string;
4
+ path: string;
5
+ parser: {
6
+ id: string;
7
+ version: string;
8
+ };
9
+ contentPolicy?: "manifest-only" | "referenced-blobs";
10
+ }
11
+ export interface ArchiveIngestionPolicy {
12
+ version: "archive-policy-v1" | "archive-policy-v2";
13
+ id: string;
14
+ revision: string;
15
+ sourceRoot: string;
16
+ archiveRoot: string;
17
+ maxBytes: number;
18
+ retention: ArchiveRetention;
19
+ expiresAt: string;
20
+ sources: PolicySource[];
21
+ }
22
+ /** Host-only policy binding for bounded external observation; no source bytes are read. */
23
+ export declare function archivePolicyBinding(policyPath: string, sourceId: string): Readonly<{
24
+ policySha256: string;
25
+ archiveRoot: string;
26
+ sourceId: string;
27
+ archiveSourceId: string;
28
+ expiresAt: string;
29
+ }>;
30
+ /** Explicit operator-selected file policy. Not a credential, authorization attestation or hostile-agent sandbox. */
31
+ export declare function ingestPolicySource(policyPath: string, sourceId: string, previousCheckpointId?: string, expectedPolicySha256?: string): {
32
+ change: string;
33
+ policyReceiptId: string;
34
+ kind: "execution-retention-v2";
35
+ checkpointId: string;
36
+ policySha256: string;
37
+ sourceId: string;
38
+ archiveSourceId: string;
39
+ sourceSha256: string;
40
+ sourceStatus: "available";
41
+ retention: "exact";
42
+ syntax: "not-applicable";
43
+ runtime: "running" | "terminal";
44
+ content: Record<"session" | "stdout" | "stderr" | "paneSnapshot" | "checkReceipt" | "result", {
45
+ status: "available" | "missing" | "mismatch";
46
+ sha256: string | null;
47
+ bytes: number | null;
48
+ }>;
49
+ activeBranch: null;
50
+ coverage: "partial";
51
+ issueCount: number;
52
+ acceptance: "not-assessed";
53
+ } | {
54
+ sourceId: string;
55
+ archiveSourceId: string;
56
+ change: import("./archive-checkpoint.js").SnapshotChange | "repeat";
57
+ policyReceiptId: string;
58
+ checkpointId: string;
59
+ policySha256: string;
60
+ sourceSha256: string;
61
+ sourceStatus: "available" | "missing" | "error";
62
+ retention: ArchiveRetention;
63
+ syntax: "error" | "complete" | "partial" | "unavailable";
64
+ completeBytes: number;
65
+ pendingBytes: number;
66
+ invalidLines: number;
67
+ issues: string[];
68
+ activeBranch: null;
69
+ acceptance: "not-assessed";
70
+ };
71
+ /** Metadata-only CLI/read-model output; no payload, local path or policy file is exported. */
72
+ export declare function inspectPolicyCheckpoint(policyPath: string, sourceId: string, checkpointId: string): {
73
+ kind: "execution-retention-v2";
74
+ checkpointId: string;
75
+ policySha256: string;
76
+ sourceId: string;
77
+ archiveSourceId: string;
78
+ sourceSha256: string;
79
+ sourceStatus: "available";
80
+ retention: "exact";
81
+ syntax: "not-applicable";
82
+ runtime: "running" | "terminal";
83
+ content: Record<"session" | "stdout" | "stderr" | "paneSnapshot" | "checkReceipt" | "result", {
84
+ status: "available" | "missing" | "mismatch";
85
+ sha256: string | null;
86
+ bytes: number | null;
87
+ }>;
88
+ activeBranch: null;
89
+ coverage: "partial";
90
+ issueCount: number;
91
+ acceptance: "not-assessed";
92
+ } | {
93
+ sourceId: string;
94
+ archiveSourceId: string;
95
+ checkpointId: string;
96
+ policySha256: string;
97
+ sourceSha256: string;
98
+ sourceStatus: "available" | "missing" | "error";
99
+ retention: ArchiveRetention;
100
+ syntax: "error" | "complete" | "partial" | "unavailable";
101
+ completeBytes: number;
102
+ pendingBytes: number;
103
+ invalidLines: number;
104
+ issues: string[];
105
+ activeBranch: null;
106
+ acceptance: "not-assessed";
107
+ };