@skill-harness/adapters 0.24.2 → 0.24.3
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/closed-schema.js +24 -4
- package/dist/pi-daddy-record-v1-contract.d.ts +968 -0
- package/dist/pi-daddy-record-v1-contract.js +1343 -0
- package/dist/pi-daddy-record-v1.d.ts +8 -0
- package/dist/pi-daddy-record-v1.js +130 -0
- package/dist/trajectory.d.ts +1 -0
- package/dist/trajectory.js +7 -3
- package/package.json +2 -2
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type TrajectoryEventV1 } from "@skill-harness/core";
|
|
2
|
+
/**
|
|
3
|
+
* Current native grants.jsonl ingestion. Envelope and body contracts are separate,
|
|
4
|
+
* exact producer pins. An invalid line rejects the entire source, never a green
|
|
5
|
+
* intact prefix. Digests detect inconsistency, not authenticity or billing truth.
|
|
6
|
+
* One normalized observation per native body: no synthetic grants/approvals.
|
|
7
|
+
*/
|
|
8
|
+
export declare function normalizePiDaddyRecordLedgerV1(raw: string): TrajectoryEventV1[];
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/** Read the separately pinned pi-daddy 0.44.3 record format. No compatibility guessing. */
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { TRAJECTORY_EVENT_VERSION, deserializeTrajectoryEvents, redactArgs, redactText } from "@skill-harness/core";
|
|
4
|
+
import { assertSupportedSchemaV3, declaredPropertyNames, validateClosedSchemaV3 } from "./closed-schema.js";
|
|
5
|
+
import { PI_DADDY_RECORD_V1_SCHEMA, PI_DADDY_RECORD_V1_GOVERNANCE_SCHEMA, PI_DADDY_RECORD_V1_COMMIT } from "./pi-daddy-record-v1-contract.js";
|
|
6
|
+
const sha = (text) => createHash("sha256").update(text, "utf8").digest("hex");
|
|
7
|
+
function canonical(value) {
|
|
8
|
+
if (value === null || typeof value !== "object")
|
|
9
|
+
return JSON.stringify(value);
|
|
10
|
+
if (Array.isArray(value))
|
|
11
|
+
return `[${value.map(canonical).join(",")}]`;
|
|
12
|
+
return `{${Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, item]) => `${JSON.stringify(key)}:${canonical(item)}`).join(",")}}`;
|
|
13
|
+
}
|
|
14
|
+
function object(value) { return value && typeof value === "object" && !Array.isArray(value) ? value : undefined; }
|
|
15
|
+
function text(value) { return typeof value === "string" ? value : undefined; }
|
|
16
|
+
/** Reject sensitive authority/identity text instead of silently changing what it names. */
|
|
17
|
+
function hasSensitiveIdentity(value) {
|
|
18
|
+
if (typeof value === "string")
|
|
19
|
+
return redactText(value) !== value;
|
|
20
|
+
if (Array.isArray(value))
|
|
21
|
+
return value.some(hasSensitiveIdentity);
|
|
22
|
+
if (value !== null && typeof value === "object") {
|
|
23
|
+
return Object.entries(value).some(([key, item]) => redactText(key) !== key || hasSensitiveIdentity(item));
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
const schemaNames = new WeakMap();
|
|
28
|
+
function validate(schema, value, label) {
|
|
29
|
+
if (!schemaNames.has(schema)) {
|
|
30
|
+
assertSupportedSchemaV3(schema, label);
|
|
31
|
+
schemaNames.set(schema, declaredPropertyNames(schema));
|
|
32
|
+
}
|
|
33
|
+
const issues = validateClosedSchemaV3(schema, value, { knownFieldNames: schemaNames.get(schema) });
|
|
34
|
+
if (issues.length)
|
|
35
|
+
throw new Error(`${label}: ${redactText(issues[0].path || "(record)")} ${redactText(issues[0].message)} [pi-daddy ${PI_DADDY_RECORD_V1_COMMIT.slice(0, 12)}]`);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Current native grants.jsonl ingestion. Envelope and body contracts are separate,
|
|
39
|
+
* exact producer pins. An invalid line rejects the entire source, never a green
|
|
40
|
+
* intact prefix. Digests detect inconsistency, not authenticity or billing truth.
|
|
41
|
+
* One normalized observation per native body: no synthetic grants/approvals.
|
|
42
|
+
*/
|
|
43
|
+
export function normalizePiDaddyRecordLedgerV1(raw) {
|
|
44
|
+
if (!raw || !raw.endsWith("\n"))
|
|
45
|
+
throw new Error("pi-daddy record-v1 source is empty or has an unterminated tail");
|
|
46
|
+
const lines = raw.slice(0, -1).split("\n");
|
|
47
|
+
const events = [];
|
|
48
|
+
const highWater = new Map();
|
|
49
|
+
const ids = new Set();
|
|
50
|
+
let previous = null;
|
|
51
|
+
for (const [index, line] of lines.entries()) {
|
|
52
|
+
const where = `pi-daddy record-v1 line ${index + 1}`;
|
|
53
|
+
let parsed;
|
|
54
|
+
try {
|
|
55
|
+
parsed = JSON.parse(line);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
throw new Error(`${where}: malformed JSON`);
|
|
59
|
+
}
|
|
60
|
+
validate(PI_DADDY_RECORD_V1_SCHEMA, parsed, where);
|
|
61
|
+
const record = parsed;
|
|
62
|
+
if (record.seq !== index + 1)
|
|
63
|
+
throw new Error(`${where}: sequence gap`);
|
|
64
|
+
if (record.prev !== previous)
|
|
65
|
+
throw new Error(`${where}: previous line hash mismatch`);
|
|
66
|
+
const { digest, ...unsigned } = record;
|
|
67
|
+
if (sha(canonical(unsigned)) !== digest)
|
|
68
|
+
throw new Error(`${where}: record digest mismatch`);
|
|
69
|
+
if (ids.has(record.id))
|
|
70
|
+
throw new Error(`${where}: duplicate record identity`);
|
|
71
|
+
ids.add(record.id);
|
|
72
|
+
validate(PI_DADDY_RECORD_V1_GOVERNANCE_SCHEMA, record.body, `${where} governance body`);
|
|
73
|
+
const body = record.body;
|
|
74
|
+
const native = body.event;
|
|
75
|
+
const expectedKind = native === "capability_decision" ? "capability" : native === "child_lifecycle" ? "lifecycle" : native === "workspace_lease" ? "lease" : "fact";
|
|
76
|
+
if (record.kind !== expectedKind)
|
|
77
|
+
throw new Error(`${where}: envelope kind disagrees with governance event`);
|
|
78
|
+
if (body.executionId !== undefined && body.parentExecutionId === body.executionId)
|
|
79
|
+
throw new Error(`${where}: execution cannot be its own parent`);
|
|
80
|
+
const at = body.ts;
|
|
81
|
+
const stream = text(body.executionId) ?? text(body.episodeId) ?? native;
|
|
82
|
+
const instant = Date.parse(at);
|
|
83
|
+
if (!Number.isFinite(instant))
|
|
84
|
+
throw new Error(`${where}: unsupported native timestamp`);
|
|
85
|
+
if (instant < (highWater.get(stream) ?? -Infinity))
|
|
86
|
+
throw new Error(`${where}: native timestamps move backwards within execution`);
|
|
87
|
+
highWater.set(stream, instant);
|
|
88
|
+
const uses = object(body.approvalUses);
|
|
89
|
+
for (const use of Object.values(uses ?? {})) {
|
|
90
|
+
const item = use;
|
|
91
|
+
if (item.remaining > item.max)
|
|
92
|
+
throw new Error(`${where}: approval remaining exceeds maximum`);
|
|
93
|
+
}
|
|
94
|
+
// These arrays name capability authority, even when retained only as attributes.
|
|
95
|
+
for (const field of ["requested", "parentGrant", "effective", "denied", "clipped", "gatedBlocked", "approved"]) {
|
|
96
|
+
if (hasSensitiveIdentity(body[field]))
|
|
97
|
+
throw new Error(`${where}: capability identity contains sensitive content`);
|
|
98
|
+
}
|
|
99
|
+
const correlation = object(body.correlation) ?? {};
|
|
100
|
+
const definition = object(body.definitionDigest) ?? {};
|
|
101
|
+
const refusal = object(body.refusal) ?? {};
|
|
102
|
+
const lifecycle = { starting: "child_started", running: "child_running", completed: "child_completed", failed: "child_failed" };
|
|
103
|
+
const type = native === "child_lifecycle" ? lifecycle[body.state]
|
|
104
|
+
: native === "capability_decision" && body.blocked === true ? "child_spawn_refused" : native;
|
|
105
|
+
const defined = (value) => Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
|
|
106
|
+
const attributes = redactArgs({ ...body, record_seq: record.seq, record_id: record.id, record_digest: record.digest, record_at: record.at, record_format: 1, native_event: native, contract_commit: PI_DADDY_RECORD_V1_COMMIT });
|
|
107
|
+
const event = defined({
|
|
108
|
+
event_version: TRAJECTORY_EVENT_VERSION, seq: index + 1, type, source: "pi-daddy-record-v1", at,
|
|
109
|
+
run_id: text(correlation.run_id), task_id: text(correlation.task_id), context_id: text(correlation.context_id), phase: text(correlation.phase),
|
|
110
|
+
workspace_id: text(body.workspaceId), parent_id: text(body.parentId), child_id: text(body.childId),
|
|
111
|
+
execution_id: text(body.executionId), parent_execution_id: body.parentExecutionId, task_from_execution_id: text(body.taskFromExecutionId),
|
|
112
|
+
deadline_at: text(body.deadlineAt), exit_code: Number.isInteger(body.exitCode) ? body.exitCode : undefined,
|
|
113
|
+
requested_capabilities: Array.isArray(body.requested) ? [...new Set(body.requested)] : undefined,
|
|
114
|
+
effective_capabilities: Array.isArray(body.effective) ? [...new Set(body.effective)] : undefined,
|
|
115
|
+
refusal_code: text(refusal.code),
|
|
116
|
+
digests: defined({ task: text(body.taskDigest), definition: text(definition.sha256), skill_definition: text(body.definitionHash) }), attributes,
|
|
117
|
+
});
|
|
118
|
+
// Free text was already redacted above. Check every retained string and map key,
|
|
119
|
+
// including normalized identity arrays that deliberately preserve exact values.
|
|
120
|
+
if (hasSensitiveIdentity(event))
|
|
121
|
+
throw new Error(`${where}: normalized identity contains sensitive content`);
|
|
122
|
+
events.push(event);
|
|
123
|
+
previous = sha(line);
|
|
124
|
+
}
|
|
125
|
+
if (!deserializeTrajectoryEvents(events.map(event => JSON.stringify(event)).join("\n") + "\n")) {
|
|
126
|
+
throw new Error("pi-daddy record-v1 cannot represent this source in the normalized trajectory contract");
|
|
127
|
+
}
|
|
128
|
+
return events;
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=pi-daddy-record-v1.js.map
|
package/dist/trajectory.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ExecutionTraceV1, TrajectoryEventSource, TrajectoryEventV1 } from "@skill-harness/core";
|
|
2
|
+
export { normalizePiDaddyRecordLedgerV1 } from "./pi-daddy-record-v1.js";
|
|
2
3
|
export interface CollectedTrajectorySources {
|
|
3
4
|
events: TrajectoryEventV1[];
|
|
4
5
|
errors: string[];
|
package/dist/trajectory.js
CHANGED
|
@@ -3,6 +3,8 @@ import { readFileSync, readdirSync } from "node:fs";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { TRAJECTORY_EVENT_VERSION, deserializeTrajectoryEvents, matchesGlob, redactArgs, redactText } from "@skill-harness/core";
|
|
5
5
|
import { assertSupportedSchema, assertSupportedSchemaV3, declaredPropertyNames, validateClosedSchema, validateClosedSchemaV3, } from "./closed-schema.js";
|
|
6
|
+
import { normalizePiDaddyRecordLedgerV1 } from "./pi-daddy-record-v1.js";
|
|
7
|
+
export { normalizePiDaddyRecordLedgerV1 } from "./pi-daddy-record-v1.js";
|
|
6
8
|
import { PI_DADDY_CONTRACT_COMMIT, PI_DADDY_LEDGER_V2_SCHEMA } from "./pi-daddy-ledger-v2.js";
|
|
7
9
|
import { PI_DADDY_LEDGER_V3_CONTRACT_COMMIT, PI_DADDY_LEDGER_V3_SCHEMA } from "./pi-daddy-ledger-v3.js";
|
|
8
10
|
/** Read and normalize declared workspace-local native ledger files. */
|
|
@@ -33,14 +35,16 @@ export function collectTrajectorySources(cwd, sources) {
|
|
|
33
35
|
? normalizePiDaddyLegacyLedger(text)
|
|
34
36
|
: source.adapter === "pi-daddy-ledger-v3"
|
|
35
37
|
? normalizePiDaddyLedgerV3(text)
|
|
36
|
-
:
|
|
38
|
+
: source.adapter === "pi-daddy-record-v1"
|
|
39
|
+
? normalizePiDaddyRecordLedgerV1(text)
|
|
40
|
+
: deserializeTrajectoryEvents(text);
|
|
37
41
|
if (!normalized)
|
|
38
42
|
throw new Error("normalized-v1 source is empty, malformed, or unsupported");
|
|
39
43
|
const times = normalized.map((event) => validTime(event.at) ? Date.parse(event.at) : null);
|
|
40
44
|
if (times.every((time) => time !== null)) {
|
|
41
45
|
const highWaterByStream = new Map();
|
|
42
46
|
for (let index = 0; index < times.length; index += 1) {
|
|
43
|
-
const stream = source.adapter === "pi-daddy-v1" || source.adapter === "pi-daddy-ledger-v3"
|
|
47
|
+
const stream = source.adapter === "pi-daddy-v1" || source.adapter === "pi-daddy-ledger-v3" || source.adapter === "pi-daddy-record-v1"
|
|
44
48
|
? normalizedPiDaddyStreamKey(normalized[index], index)
|
|
45
49
|
: "source";
|
|
46
50
|
const highWater = highWaterByStream.get(stream);
|
|
@@ -425,7 +429,7 @@ function piDaddyStreamKey(record, index) {
|
|
|
425
429
|
function normalizedPiDaddyStreamKey(event, index) {
|
|
426
430
|
if (event.source === "pi-daddy-0.17")
|
|
427
431
|
return JSON.stringify(["legacy", event.child_id ?? `missing-child:${index}`]);
|
|
428
|
-
if (event.source === "pi-daddy-v3") {
|
|
432
|
+
if (event.source === "pi-daddy-v3" || event.source === "pi-daddy-record-v1") {
|
|
429
433
|
return event.workflow_fact_id
|
|
430
434
|
? JSON.stringify(["v3-fact", event.workflow_fact_id])
|
|
431
435
|
: JSON.stringify(["v3-execution", event.execution_id ?? `missing-execution:${index}`]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skill-harness/adapters",
|
|
3
|
-
"version": "0.24.
|
|
3
|
+
"version": "0.24.3",
|
|
4
4
|
"description": "skill-harness Pi runner and judge adapter (internal API)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -42,6 +42,6 @@
|
|
|
42
42
|
"prepack": "node ../../scripts/release-pack.mjs --guard"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@skill-harness/core": "0.24.
|
|
45
|
+
"@skill-harness/core": "0.24.3"
|
|
46
46
|
}
|
|
47
47
|
}
|