@tangle-network/agent-eval 0.130.1 → 0.131.1
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/CHANGELOG.md +18 -0
- package/README.md +1 -0
- package/dist/agent-profile-cell-CCm3l2v2.d.ts +108 -0
- package/dist/agent-profile-cell-CCm3l2v2.d.ts.map +1 -0
- package/dist/analyst/index.d.ts +2 -2
- package/dist/analyst/index.js +1 -1
- package/dist/{analyze-runs-C1CavBMk.js → analyze-runs-BjPn_fOS.js} +2 -2
- package/dist/{analyze-runs-C1CavBMk.js.map → analyze-runs-BjPn_fOS.js.map} +1 -1
- package/dist/{analyze-runs-FsgYCinh.d.ts → analyze-runs-CBnCgfse.d.ts} +5 -5
- package/dist/{analyze-runs-FsgYCinh.d.ts.map → analyze-runs-CBnCgfse.d.ts.map} +1 -1
- package/dist/belief-state/index.d.ts +4 -4
- package/dist/benchmarks/index.d.ts +1 -1
- package/dist/benchmarks/index.js +1 -1
- package/dist/{benchmarks-BJgDGkAD.js → benchmarks-BDtRiOvE.js} +3 -3
- package/dist/{benchmarks-BJgDGkAD.js.map → benchmarks-BDtRiOvE.js.map} +1 -1
- package/dist/campaign/index.d.ts +3 -3
- package/dist/campaign/index.js +2 -2
- package/dist/{campaign-aKJt6emI.js → campaign-CLG6y9jF.js} +6 -5
- package/dist/{campaign-aKJt6emI.js.map → campaign-CLG6y9jF.js.map} +1 -1
- package/dist/{client-C97NMzqi.d.ts → client-w90OcvvR.d.ts} +4 -4
- package/dist/{client-C97NMzqi.d.ts.map → client-w90OcvvR.d.ts.map} +1 -1
- package/dist/{code-agent-session-DqqgOJaz.d.ts → code-agent-session-aYa3SGKz.d.ts} +2 -2
- package/dist/{code-agent-session-DqqgOJaz.d.ts.map → code-agent-session-aYa3SGKz.d.ts.map} +1 -1
- package/dist/concurrency-MUjT7VjM.js +109 -0
- package/dist/concurrency-MUjT7VjM.js.map +1 -0
- package/dist/contract/index.d.ts +63 -9
- package/dist/contract/index.d.ts.map +1 -1
- package/dist/contract/index.js +296 -50
- package/dist/contract/index.js.map +1 -1
- package/dist/control.d.ts +1 -1
- package/dist/control.js +1 -1
- package/dist/{default-registry-CNPo-Vsb.d.ts → default-registry-Cj1oUpLN.d.ts} +2 -2
- package/dist/{default-registry-CNPo-Vsb.d.ts.map → default-registry-Cj1oUpLN.d.ts.map} +1 -1
- package/dist/{eval-campaign-DEm6c8ru.js → eval-campaign-C2k-m4aY.js} +3 -2
- package/dist/{eval-campaign-DEm6c8ru.js.map → eval-campaign-C2k-m4aY.js.map} +1 -1
- package/dist/hosted/index.d.ts +2 -2
- package/dist/{index-2JJSA6-r2.d.ts → index-B11XCkdf2.d.ts} +2 -2
- package/dist/{index-2JJSA6-r2.d.ts.map → index-B11XCkdf2.d.ts.map} +1 -1
- package/dist/{index-BvnJuTGD.d.ts → index-DNXoNaFF.d.ts} +2 -2
- package/dist/{index-BvnJuTGD.d.ts.map → index-DNXoNaFF.d.ts.map} +1 -1
- package/dist/{index-6N0aYmpW.d.ts → index-D_F6VAKe.d.ts} +2 -2
- package/dist/{index-6N0aYmpW.d.ts.map → index-D_F6VAKe.d.ts.map} +1 -1
- package/dist/{index-Em67JBjs.d.ts → index-NPeSWD98.d.ts} +5 -5
- package/dist/{index-Em67JBjs.d.ts.map → index-NPeSWD98.d.ts.map} +1 -1
- package/dist/{index-CD_WZ_Xr.d.ts → index-VTypFU3t.d.ts} +5 -5
- package/dist/{index-CD_WZ_Xr.d.ts.map → index-VTypFU3t.d.ts.map} +1 -1
- package/dist/index.d.ts +18 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -13
- package/dist/index.js.map +1 -1
- package/dist/ledger-core/index.js +1 -1
- package/dist/{ledger-core-DtZz1RG0.js → ledger-core-eqaI3PCD.js} +2 -2
- package/dist/{ledger-core-DtZz1RG0.js.map → ledger-core-eqaI3PCD.js.map} +1 -1
- package/dist/meta-eval/index.d.ts +2 -2
- package/dist/{mint-yN2M2eh0.js → mint-D5_87M5L.js} +2 -2
- package/dist/{mint-yN2M2eh0.js.map → mint-D5_87M5L.js.map} +1 -1
- package/dist/multishot/index.d.ts +1 -1
- package/dist/openapi.json +1 -1
- package/dist/profile-cell.d.ts +2 -0
- package/dist/profile-cell.js +243 -0
- package/dist/profile-cell.js.map +1 -0
- package/dist/{propose-review-control-LhIWGzHl.js → propose-review-control-Bqb7daEJ.js} +2 -2
- package/dist/{propose-review-control-LhIWGzHl.js.map → propose-review-control-Bqb7daEJ.js.map} +1 -1
- package/dist/{release-report-mvB2G4_J.d.ts → release-report-Crg9oFJ0.d.ts} +3 -3
- package/dist/{release-report-mvB2G4_J.d.ts.map → release-report-Crg9oFJ0.d.ts.map} +1 -1
- package/dist/{release-report-Cz9NKH39.js → release-report-oWt9f2k-.js} +2 -2
- package/dist/{release-report-Cz9NKH39.js.map → release-report-oWt9f2k-.js.map} +1 -1
- package/dist/{replay-GnyotH0J.js → replay-D18-pBAA.js} +2 -2
- package/dist/{replay-GnyotH0J.js.map → replay-D18-pBAA.js.map} +1 -1
- package/dist/{replay-OoidtG1E.d.ts → replay-RE97Ckjl.d.ts} +2 -2
- package/dist/{replay-OoidtG1E.d.ts.map → replay-RE97Ckjl.d.ts.map} +1 -1
- package/dist/reporting.d.ts +3 -3
- package/dist/reporting.js +1 -1
- package/dist/{researcher-CwTdwXG1.d.ts → researcher-Q5rpPqZY.d.ts} +4 -3
- package/dist/researcher-Q5rpPqZY.d.ts.map +1 -0
- package/dist/{reward-hacking-qipEpKvY.js → reward-hacking-BEvjdUtD.js} +2 -2
- package/dist/{reward-hacking-qipEpKvY.js.map → reward-hacking-BEvjdUtD.js.map} +1 -1
- package/dist/{reward-hacking-eAnOsynk.d.ts → reward-hacking-CW-3HN0n.d.ts} +2 -2
- package/dist/{reward-hacking-eAnOsynk.d.ts.map → reward-hacking-CW-3HN0n.d.ts.map} +1 -1
- package/dist/rl.d.ts +5 -5
- package/dist/rl.js +4 -4
- package/dist/rollout/index.d.ts +1 -1
- package/dist/rollout/index.js +2 -2
- package/dist/{rollout-BOYjemfR.js → rollout-VEo41J0N.js} +2 -2
- package/dist/{rollout-BOYjemfR.js.map → rollout-VEo41J0N.js.map} +1 -1
- package/dist/{rubric-predictive-validity-Ku_clp_1.d.ts → rubric-predictive-validity-lXLmashy.d.ts} +2 -2
- package/dist/{rubric-predictive-validity-Ku_clp_1.d.ts.map → rubric-predictive-validity-lXLmashy.d.ts.map} +1 -1
- package/dist/{run-evidence-ClRX_8A9.d.ts → run-evidence-oByzm-dE.d.ts} +2 -2
- package/dist/{run-evidence-ClRX_8A9.d.ts.map → run-evidence-oByzm-dE.d.ts.map} +1 -1
- package/dist/{run-record-CnZu_gjl.d.ts → run-record-BJnYdTxO.d.ts} +3 -107
- package/dist/run-record-BJnYdTxO.d.ts.map +1 -0
- package/dist/run-record-CN8Zd21B.js +229 -0
- package/dist/run-record-CN8Zd21B.js.map +1 -0
- package/dist/{runtime-trajectory-BvSZcCHD.d.ts → runtime-trajectory-BXxG4lyi.d.ts} +2 -2
- package/dist/{runtime-trajectory-BvSZcCHD.d.ts.map → runtime-trajectory-BXxG4lyi.d.ts.map} +1 -1
- package/dist/{semantic-concept-judge-B6cWNJ2K.js → semantic-concept-judge-b5m3irbR.js} +2 -2
- package/dist/{semantic-concept-judge-B6cWNJ2K.js.map → semantic-concept-judge-b5m3irbR.js.map} +1 -1
- package/dist/{skill-usage-C_pXm7lP.d.ts → skill-usage-D5mlWdAJ.d.ts} +3 -3
- package/dist/{skill-usage-C_pXm7lP.d.ts.map → skill-usage-D5mlWdAJ.d.ts.map} +1 -1
- package/dist/{skillopt-optimization-method-CWKVTnks.d.ts → skillopt-optimization-method-D0MVjJdP.d.ts} +5 -5
- package/dist/{skillopt-optimization-method-CWKVTnks.d.ts.map → skillopt-optimization-method-D0MVjJdP.d.ts.map} +1 -1
- package/dist/{skillopt-optimization-method-CUmHkGbI.js → skillopt-optimization-method-eOJL2570.js} +4 -4
- package/dist/{skillopt-optimization-method-CUmHkGbI.js.map → skillopt-optimization-method-eOJL2570.js.map} +1 -1
- package/dist/{summary-report-Cj9gdw4i.d.ts → summary-report-CWwB_LiV.d.ts} +2 -2
- package/dist/{summary-report-Cj9gdw4i.d.ts.map → summary-report-CWwB_LiV.d.ts.map} +1 -1
- package/dist/traces.d.ts +2 -2
- package/dist/traces.js +1 -1
- package/dist/{types-k9tZGKUg.d.ts → types-CsD5nTfV.d.ts} +2 -2
- package/dist/{types-k9tZGKUg.d.ts.map → types-CsD5nTfV.d.ts.map} +1 -1
- package/docs/concepts.md +11 -0
- package/package.json +6 -1
- package/dist/concurrency-DIxRZF_J.js +0 -85
- package/dist/concurrency-DIxRZF_J.js.map +0 -1
- package/dist/researcher-CwTdwXG1.d.ts.map +0 -1
- package/dist/run-record-BuoE80Dq.js +0 -467
- package/dist/run-record-BuoE80Dq.js.map +0 -1
- package/dist/run-record-CnZu_gjl.d.ts.map +0 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as tryWithLedgerFileLock, c as probeAtomicFileLock, i as appendLedgerLine, l as tryAcquireAtomicFileLock, n as canonicalString, o as withLedgerFileLock, r as hashCanonical, s as AtomicFileLockError, t as FileLedgerJournal } from "../ledger-core-
|
|
1
|
+
import { a as tryWithLedgerFileLock, c as probeAtomicFileLock, i as appendLedgerLine, l as tryAcquireAtomicFileLock, n as canonicalString, o as withLedgerFileLock, r as hashCanonical, s as AtomicFileLockError, t as FileLedgerJournal } from "../ledger-core-eqaI3PCD.js";
|
|
2
2
|
export { AtomicFileLockError, FileLedgerJournal, appendLedgerLine, canonicalString, hashCanonical, probeAtomicFileLock, tryAcquireAtomicFileLock, tryWithLedgerFileLock, withLedgerFileLock };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as canonicalize } from "./pre-registration-DakwTRXk.js";
|
|
2
|
-
import { t as Mutex } from "./concurrency-
|
|
2
|
+
import { t as Mutex } from "./concurrency-MUjT7VjM.js";
|
|
3
3
|
import { closeSync, constants, existsSync, fsyncSync, linkSync, mkdirSync, openSync, readFileSync, renameSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
|
|
4
4
|
import { dirname, resolve } from "node:path";
|
|
5
5
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -385,4 +385,4 @@ var FileLedgerJournal = class {
|
|
|
385
385
|
//#endregion
|
|
386
386
|
export { tryWithLedgerFileLock as a, probeAtomicFileLock as c, appendLedgerLine as i, tryAcquireAtomicFileLock as l, canonicalString as n, withLedgerFileLock as o, hashCanonical as r, AtomicFileLockError as s, FileLedgerJournal as t };
|
|
387
387
|
|
|
388
|
-
//# sourceMappingURL=ledger-core-
|
|
388
|
+
//# sourceMappingURL=ledger-core-eqaI3PCD.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ledger-core-DtZz1RG0.js","names":[],"sources":["../src/ledger-core/atomic-file-lock.ts","../src/ledger-core/journal-file.ts","../src/ledger-core/journal.ts"],"sourcesContent":["/** Crash-safe filesystem lock shared by campaign persistence and run exclusion. */\n\nimport { randomUUID } from 'node:crypto'\nimport {\n closeSync,\n constants,\n existsSync,\n fsyncSync,\n linkSync,\n openSync,\n readFileSync,\n renameSync,\n unlinkSync,\n writeFileSync,\n} from 'node:fs'\nimport { hostname } from 'node:os'\n\nexport interface AtomicFileLockOwner {\n readonly pid: number\n readonly host: string\n readonly nonce: string\n}\n\nexport interface AtomicFileLock {\n readonly owner: AtomicFileLockOwner\n release(): void\n}\n\nexport type AtomicFileLockUnavailable =\n | { readonly acquired: false; readonly reason: 'held'; readonly holder: AtomicFileLockOwner }\n | { readonly acquired: false; readonly reason: 'recovery' }\n\nexport type AtomicFileLockAcquisition =\n | { readonly acquired: true; readonly lock: AtomicFileLock }\n | AtomicFileLockUnavailable\n\nexport interface AtomicFileLockOptions {\n readonly lockPath: string\n readonly pid?: number\n}\n\nexport class AtomicFileLockError extends Error {\n override readonly name = 'AtomicFileLockError'\n}\n\ntype OwnerState =\n | { readonly state: 'missing' }\n | { readonly state: 'stale'; readonly owner: AtomicFileLockOwner }\n | { readonly state: 'held'; readonly owner: AtomicFileLockOwner }\n\n/** Report whether another lock path prevents acquisition without modifying it. */\nexport function probeAtomicFileLock(\n options: AtomicFileLockOptions,\n): AtomicFileLockUnavailable | null {\n if (existsSync(recoveryPath(options.lockPath))) return { acquired: false, reason: 'recovery' }\n const state = ownerState(options.lockPath)\n if (state.state === 'held') {\n return { acquired: false, reason: 'held', holder: state.owner }\n }\n return null\n}\n\n/**\n * Try to acquire a complete, uniquely-owned lock inode.\n *\n * A hard link publishes fully-written owner metadata atomically. Stale-owner\n * removal is serialized so one reclaimer cannot delete a new owner's lock.\n */\nexport function tryAcquireAtomicFileLock(\n options: AtomicFileLockOptions,\n): AtomicFileLockAcquisition {\n const pid = options.pid ?? process.pid\n if (!Number.isSafeInteger(pid) || pid <= 0) {\n throw new AtomicFileLockError('atomic file lock pid must be a positive integer')\n }\n const owner: AtomicFileLockOwner = { pid, host: hostname(), nonce: randomUUID() }\n for (let attempt = 0; attempt < 8; attempt += 1) {\n if (existsSync(recoveryPath(options.lockPath))) {\n return { acquired: false, reason: 'recovery' }\n }\n if (tryLinkOwner(options.lockPath, owner, `acquire.${attempt}`)) {\n return acquired(options.lockPath, owner)\n }\n\n const holder = ownerState(options.lockPath)\n if (holder.state === 'missing') continue\n if (holder.state === 'held') {\n return { acquired: false, reason: 'held', holder: holder.owner }\n }\n\n const reclaimPath = recoveryPath(options.lockPath)\n if (!tryLinkOwner(reclaimPath, owner, `reclaim.${attempt}`)) {\n return { acquired: false, reason: 'recovery' }\n }\n try {\n const current = ownerState(options.lockPath)\n if (current.state === 'held') {\n return { acquired: false, reason: 'held', holder: current.owner }\n }\n if (current.state === 'stale') {\n const tombstone = `${options.lockPath}.stale.${owner.nonce}.${attempt}`\n try {\n renameSync(options.lockPath, tombstone)\n unlinkSync(tombstone)\n } catch (error) {\n if (!isMissing(error)) throw error\n }\n }\n if (tryLinkOwner(options.lockPath, owner, `recovered.${attempt}`)) {\n return acquired(options.lockPath, owner)\n }\n } finally {\n releaseOwnedPath(reclaimPath, owner)\n }\n }\n\n throw new AtomicFileLockError(`could not acquire atomic file lock ${options.lockPath}`)\n}\n\nfunction acquired(lockPath: string, owner: AtomicFileLockOwner): AtomicFileLockAcquisition {\n return {\n acquired: true,\n lock: {\n owner,\n release: () => releaseOwnedPath(lockPath, owner),\n },\n }\n}\n\nfunction tryLinkOwner(lockPath: string, owner: AtomicFileLockOwner, suffix: string): boolean {\n const ownerPath = `${lockPath}.${owner.pid}.${owner.nonce}.${suffix}.owner`\n const descriptor = openSync(\n ownerPath,\n constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY,\n 0o600,\n )\n try {\n writeFileSync(descriptor, `${canonicalOwner(owner)}\\n`, 'utf8')\n fsyncSync(descriptor)\n } catch (error) {\n closeSync(descriptor)\n unlinkIfExists(ownerPath)\n throw error\n }\n closeSync(descriptor)\n\n try {\n linkSync(ownerPath, lockPath)\n return true\n } catch (error) {\n if (!isAlreadyExists(error)) throw error\n return false\n } finally {\n unlinkIfExists(ownerPath)\n }\n}\n\nfunction ownerState(lockPath: string): OwnerState {\n const owner = readOwner(lockPath)\n if (!owner) return { state: 'missing' }\n if (owner.host !== hostname()) return { state: 'held', owner }\n try {\n process.kill(owner.pid, 0)\n return { state: 'held', owner }\n } catch (error) {\n if (isNoSuchProcess(error)) return { state: 'stale', owner }\n return { state: 'held', owner }\n }\n}\n\nfunction readOwner(lockPath: string): AtomicFileLockOwner | undefined {\n let contents: string\n try {\n contents = readFileSync(lockPath, 'utf8').trim()\n } catch (error) {\n if (isMissing(error)) return undefined\n throw error\n }\n\n let value: unknown\n try {\n value = JSON.parse(contents)\n } catch (error) {\n throw invalidOwner(lockPath, error)\n }\n if (!isOwner(value)) throw invalidOwner(lockPath)\n return value\n}\n\nfunction isOwner(value: unknown): value is AtomicFileLockOwner {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return false\n const record = value as Record<string, unknown>\n return (\n Object.keys(record).length === 3 &&\n Number.isSafeInteger(record.pid) &&\n Number(record.pid) > 0 &&\n typeof record.host === 'string' &&\n record.host.trim().length > 0 &&\n typeof record.nonce === 'string' &&\n record.nonce.trim().length > 0\n )\n}\n\nfunction invalidOwner(lockPath: string, cause?: unknown): AtomicFileLockError {\n const error = new AtomicFileLockError(\n `atomic file lock has an invalid owner (${lockPath}); refusing unsafe recovery`,\n )\n if (cause !== undefined) Object.defineProperty(error, 'cause', { value: cause })\n return error\n}\n\nfunction releaseOwnedPath(lockPath: string, owner: AtomicFileLockOwner): void {\n const current = readOwner(lockPath)\n if (!current) return\n if (canonicalOwner(current) !== canonicalOwner(owner)) {\n throw new AtomicFileLockError(`atomic file lock owner changed before release (${lockPath})`)\n }\n try {\n unlinkSync(lockPath)\n } catch (error) {\n if (!isMissing(error)) throw error\n }\n}\n\nfunction canonicalOwner(owner: AtomicFileLockOwner): string {\n return JSON.stringify({ host: owner.host, nonce: owner.nonce, pid: owner.pid })\n}\n\nfunction recoveryPath(lockPath: string): string {\n return `${lockPath}.reclaim`\n}\n\nfunction isAlreadyExists(error: unknown): boolean {\n return error instanceof Error && 'code' in error && error.code === 'EEXIST'\n}\n\nfunction isMissing(error: unknown): boolean {\n return error instanceof Error && 'code' in error && error.code === 'ENOENT'\n}\n\nfunction isNoSuchProcess(error: unknown): boolean {\n return error instanceof Error && 'code' in error && error.code === 'ESRCH'\n}\n\nfunction unlinkIfExists(path: string): void {\n try {\n unlinkSync(path)\n } catch (error) {\n if (!isMissing(error)) throw error\n }\n}\n","/** Filesystem durability and cross-process exclusion for append-only journal files. */\n\nimport { closeSync, constants, fsyncSync, mkdirSync, openSync, writeSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport { AtomicFileLockError, tryAcquireAtomicFileLock } from './atomic-file-lock'\n\n/** How a journal binding reports file-layer faults in its own error taxonomy. */\nexport interface LedgerFileContext {\n /** Noun used in error messages, e.g. 'search ledger'. */\n subject: string\n integrityError(message: string, options?: { cause?: unknown }): Error\n}\n\n/** Append one already-serialized line; the write is fsynced (file and directory)\n * before returning so an acknowledged append survives a crash. */\nexport function appendLedgerLine(path: string, line: string, context: LedgerFileContext): void {\n mkdirSync(dirname(path), { recursive: true })\n const fd = openSync(path, constants.O_CREAT | constants.O_WRONLY | constants.O_APPEND, 0o600)\n try {\n writeAll(fd, Buffer.from(line, 'utf8'), context)\n fsyncSync(fd)\n } finally {\n closeSync(fd)\n }\n fsyncDirectory(dirname(path))\n}\n\nexport function withLedgerFileLock<T>(path: string, context: LedgerFileContext, run: () => T): T {\n const result = tryWithLedgerFileLock(path, context, run)\n if (!result.acquired) {\n throw context.integrityError(`${context.subject} lock is held (${path})`)\n }\n return result.value\n}\n\nexport type FileLockResult<T> = { acquired: true; value: T } | { acquired: false }\n\nexport function tryWithLedgerFileLock<T>(\n path: string,\n context: LedgerFileContext,\n run: () => T,\n): FileLockResult<T> {\n mkdirSync(dirname(path), { recursive: true })\n const lockPath = `${path}.lock`\n try {\n const acquisition = tryAcquireAtomicFileLock({ lockPath })\n if (!acquisition.acquired) return { acquired: false }\n try {\n return { acquired: true, value: run() }\n } finally {\n acquisition.lock.release()\n }\n } catch (error) {\n if (error instanceof AtomicFileLockError) {\n throw context.integrityError(error.message, { cause: error })\n }\n throw error\n }\n}\n\nfunction writeAll(fd: number, bytes: Buffer, context: LedgerFileContext): void {\n let offset = 0\n while (offset < bytes.byteLength) {\n const written = writeSync(fd, bytes, offset, bytes.byteLength - offset)\n if (written <= 0) throw context.integrityError('filesystem wrote zero bytes')\n offset += written\n }\n}\n\nfunction fsyncDirectory(path: string): void {\n const fd = openSync(path, constants.O_RDONLY)\n try {\n fsyncSync(fd)\n } finally {\n closeSync(fd)\n }\n}\n","/**\n * Generic durable append-only journal with a SHA-256 hash chain.\n *\n * The file format is canonical JSONL: every row is the canonical JSON encoding\n * (object keys sorted recursively) of one entry, and every entry hashes the\n * previous entry's hash so the log is tamper-evident. Appends are serialized\n * in-process per path and across processes via a lock file, fsynced before\n * acknowledgement, and idempotent by `eventId`. A malformed, non-canonical,\n * truncated, reordered, or conflicting log fails loudly; a bad row is never\n * skipped.\n *\n * Domain vocabulary lives entirely in the consumer's codec: entry schema\n * validation, the constant header fields stamped into every entry, the error\n * taxonomy, and the state machine replayed over verified entries.\n */\n\nimport { createHash } from 'node:crypto'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { Mutex } from '../concurrency'\nimport { canonicalize } from '../pre-registration'\nimport { appendLedgerLine, type LedgerFileContext, withLedgerFileLock } from './journal-file'\n\nexport type LedgerHash = `sha256:${string}`\n\n/** Canonical JSON encoding: object keys sorted recursively. This is the byte\n * form that is hashed and stored, so identical values always encode identically. */\nexport function canonicalString(value: unknown): string {\n return JSON.stringify(canonicalize(value))\n}\n\nexport function hashCanonical(value: unknown): LedgerHash {\n return `sha256:${createHash('sha256').update(canonicalString(value)).digest('hex')}`\n}\n\n/** Minimum shape of a journal event: the idempotency key. */\nexport interface LedgerEventBase {\n eventId: string\n}\n\n/** Chain envelope every entry carries in addition to the journal's constant\n * header fields. `entryHash` covers the header, sequence, previous hash, and\n * event, so any rewrite of history invalidates every later entry. */\nexport interface LedgerChainFields<Event extends LedgerEventBase> {\n sequence: number\n previousHash: LedgerHash | null\n event: Event\n entryHash: LedgerHash\n}\n\nexport type LedgerEntryOf<Header extends object, Event extends LedgerEventBase> = Header &\n LedgerChainFields<Event>\n\nexport interface LedgerLineContext {\n path: string\n line: number\n}\n\n/** Domain state machine replayed over chain-verified entries in order.\n * `apply` throws (via the codec's error taxonomy) on an invalid transition;\n * `finish` builds the consumer's projection once every entry was applied. */\nexport interface LedgerProjector<Entry, Projection> {\n apply(entry: Entry, index: number): void\n finish(entries: Entry[]): Projection\n}\n\nexport interface LedgerJournalCodec<\n Header extends object,\n Event extends LedgerEventBase,\n Projection,\n> extends LedgerFileContext {\n /** Constant fields stamped into every entry and covered by its hash. */\n header: Header\n conflictError(message: string): Error\n /** Validate one parsed JSON row into a typed entry. Must reject malformed\n * rows and non-canonical event encodings with the binding's own errors;\n * the journal itself verifies the stored bytes and the hash chain. */\n parseEntry(raw: unknown, context: LedgerLineContext): LedgerEntryOf<Header, Event>\n /** Reject an entry whose constant header fields do not match this journal. */\n checkEntryHeader(entry: LedgerEntryOf<Header, Event>, index: number): void\n createProjector(): LedgerProjector<LedgerEntryOf<Header, Event>, Projection>\n}\n\nexport interface LedgerReplayResult<Entry, Projection> {\n entries: Entry[]\n projection: Projection\n}\n\nexport interface LedgerAppendResult<Entry, Projection> {\n entry: Entry\n /** False when the exact event was already durably present. */\n appended: boolean\n projection: Projection\n}\n\n// One async mutex per resolved journal path so concurrent appends from a single\n// process queue up instead of failing on the held cross-process lock file.\nconst journalMutexes = new Map<string, Mutex>()\n\nfunction mutexFor(path: string): Mutex {\n const existing = journalMutexes.get(path)\n if (existing) return existing\n const mutex = new Mutex()\n journalMutexes.set(path, mutex)\n return mutex\n}\n\n/** Durable filesystem journal. Construction performs no I/O; `append` and\n * `replay` validate the complete existing file under the locks. */\nexport class FileLedgerJournal<Header extends object, Event extends LedgerEventBase, Projection> {\n readonly path: string\n private readonly codec: LedgerJournalCodec<Header, Event, Projection>\n private readonly mutex: Mutex\n\n constructor(path: string, codec: LedgerJournalCodec<Header, Event, Projection>) {\n this.path = resolve(path)\n this.codec = codec\n this.mutex = mutexFor(this.path)\n }\n\n async replay(): Promise<LedgerReplayResult<LedgerEntryOf<Header, Event>, Projection>> {\n return this.mutex.runExclusive(() =>\n withLedgerFileLock(this.path, this.codec, () => this.replayLocked()),\n )\n }\n\n async append(\n event: Event,\n ): Promise<LedgerAppendResult<LedgerEntryOf<Header, Event>, Projection>> {\n return this.mutex.runExclusive(() =>\n withLedgerFileLock(this.path, this.codec, () => {\n const before = this.replayLocked()\n const existing = before.entries.find((entry) => entry.event.eventId === event.eventId)\n if (existing) {\n if (canonicalString(existing.event) !== canonicalString(event)) {\n throw this.codec.conflictError(\n `eventId ${event.eventId} already exists with different content`,\n )\n }\n return { entry: existing, appended: false, projection: before.projection }\n }\n\n const material = {\n ...this.codec.header,\n sequence: before.entries.length,\n previousHash: before.entries.at(-1)?.entryHash ?? null,\n event,\n }\n const entry = { ...material, entryHash: hashCanonical(material) }\n\n // Apply the state transition before spending an append: an entry the\n // projector rejects must never reach the durable file.\n const projection = this.project([...before.entries, entry])\n appendLedgerLine(this.path, `${canonicalString(entry)}\\n`, this.codec)\n return { entry, appended: true, projection }\n }),\n )\n }\n\n private replayLocked(): LedgerReplayResult<LedgerEntryOf<Header, Event>, Projection> {\n const entries = this.readEntries()\n return { entries, projection: this.project(entries) }\n }\n\n private readEntries(): LedgerEntryOf<Header, Event>[] {\n if (!existsSync(this.path)) return []\n const text = readFileSync(this.path, 'utf8')\n if (text.length === 0) return []\n if (!text.endsWith('\\n')) {\n throw this.codec.integrityError(\n `${this.codec.subject} ${this.path} has a truncated final record (missing newline)`,\n )\n }\n\n const lines = text.slice(0, -1).split('\\n')\n const entries: LedgerEntryOf<Header, Event>[] = []\n for (let index = 0; index < lines.length; index += 1) {\n const line = lines[index]!\n if (line.length === 0) {\n throw this.codec.integrityError(\n `${this.codec.subject} ${this.path} has a blank row at line ${index + 1}`,\n )\n }\n let raw: unknown\n try {\n raw = JSON.parse(line)\n } catch (error) {\n throw this.codec.integrityError(\n `${this.codec.subject} ${this.path} has invalid JSON at line ${index + 1}`,\n { cause: error },\n )\n }\n const entry = this.codec.parseEntry(raw, { path: this.path, line: index + 1 })\n if (line !== canonicalString(entry)) {\n throw this.codec.integrityError(\n `${this.codec.subject} ${this.path} has non-canonical bytes at line ${index + 1}`,\n )\n }\n entries.push(entry)\n }\n return entries\n }\n\n private project(entries: LedgerEntryOf<Header, Event>[]): Projection {\n const projector = this.codec.createProjector()\n const eventIds = new Set<string>()\n let expectedPrevious: LedgerHash | null = null\n for (let index = 0; index < entries.length; index += 1) {\n const entry = entries[index]!\n this.codec.checkEntryHeader(entry, index)\n if (entry.sequence !== index) {\n throw this.codec.integrityError(\n `entry ${entry.event.eventId} has sequence ${entry.sequence}, expected ${index}`,\n )\n }\n if (entry.previousHash !== expectedPrevious) {\n throw this.codec.integrityError(\n `entry ${entry.event.eventId} does not extend the previous hash`,\n )\n }\n const { entryHash: _entryHash, ...material } = entry\n const expectedHash = hashCanonical(material)\n if (entry.entryHash !== expectedHash) {\n throw this.codec.integrityError(\n `entry ${entry.event.eventId} hash mismatch: expected ${expectedHash}, got ${entry.entryHash}`,\n )\n }\n expectedPrevious = entry.entryHash\n if (eventIds.has(entry.event.eventId)) {\n throw this.codec.integrityError(\n `duplicate eventId ${entry.event.eventId} in durable ledger`,\n )\n }\n eventIds.add(entry.event.eventId)\n projector.apply(entry, index)\n }\n return projector.finish(entries)\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ledger-core-eqaI3PCD.js","names":[],"sources":["../src/ledger-core/atomic-file-lock.ts","../src/ledger-core/journal-file.ts","../src/ledger-core/journal.ts"],"sourcesContent":["/** Crash-safe filesystem lock shared by campaign persistence and run exclusion. */\n\nimport { randomUUID } from 'node:crypto'\nimport {\n closeSync,\n constants,\n existsSync,\n fsyncSync,\n linkSync,\n openSync,\n readFileSync,\n renameSync,\n unlinkSync,\n writeFileSync,\n} from 'node:fs'\nimport { hostname } from 'node:os'\n\nexport interface AtomicFileLockOwner {\n readonly pid: number\n readonly host: string\n readonly nonce: string\n}\n\nexport interface AtomicFileLock {\n readonly owner: AtomicFileLockOwner\n release(): void\n}\n\nexport type AtomicFileLockUnavailable =\n | { readonly acquired: false; readonly reason: 'held'; readonly holder: AtomicFileLockOwner }\n | { readonly acquired: false; readonly reason: 'recovery' }\n\nexport type AtomicFileLockAcquisition =\n | { readonly acquired: true; readonly lock: AtomicFileLock }\n | AtomicFileLockUnavailable\n\nexport interface AtomicFileLockOptions {\n readonly lockPath: string\n readonly pid?: number\n}\n\nexport class AtomicFileLockError extends Error {\n override readonly name = 'AtomicFileLockError'\n}\n\ntype OwnerState =\n | { readonly state: 'missing' }\n | { readonly state: 'stale'; readonly owner: AtomicFileLockOwner }\n | { readonly state: 'held'; readonly owner: AtomicFileLockOwner }\n\n/** Report whether another lock path prevents acquisition without modifying it. */\nexport function probeAtomicFileLock(\n options: AtomicFileLockOptions,\n): AtomicFileLockUnavailable | null {\n if (existsSync(recoveryPath(options.lockPath))) return { acquired: false, reason: 'recovery' }\n const state = ownerState(options.lockPath)\n if (state.state === 'held') {\n return { acquired: false, reason: 'held', holder: state.owner }\n }\n return null\n}\n\n/**\n * Try to acquire a complete, uniquely-owned lock inode.\n *\n * A hard link publishes fully-written owner metadata atomically. Stale-owner\n * removal is serialized so one reclaimer cannot delete a new owner's lock.\n */\nexport function tryAcquireAtomicFileLock(\n options: AtomicFileLockOptions,\n): AtomicFileLockAcquisition {\n const pid = options.pid ?? process.pid\n if (!Number.isSafeInteger(pid) || pid <= 0) {\n throw new AtomicFileLockError('atomic file lock pid must be a positive integer')\n }\n const owner: AtomicFileLockOwner = { pid, host: hostname(), nonce: randomUUID() }\n for (let attempt = 0; attempt < 8; attempt += 1) {\n if (existsSync(recoveryPath(options.lockPath))) {\n return { acquired: false, reason: 'recovery' }\n }\n if (tryLinkOwner(options.lockPath, owner, `acquire.${attempt}`)) {\n return acquired(options.lockPath, owner)\n }\n\n const holder = ownerState(options.lockPath)\n if (holder.state === 'missing') continue\n if (holder.state === 'held') {\n return { acquired: false, reason: 'held', holder: holder.owner }\n }\n\n const reclaimPath = recoveryPath(options.lockPath)\n if (!tryLinkOwner(reclaimPath, owner, `reclaim.${attempt}`)) {\n return { acquired: false, reason: 'recovery' }\n }\n try {\n const current = ownerState(options.lockPath)\n if (current.state === 'held') {\n return { acquired: false, reason: 'held', holder: current.owner }\n }\n if (current.state === 'stale') {\n const tombstone = `${options.lockPath}.stale.${owner.nonce}.${attempt}`\n try {\n renameSync(options.lockPath, tombstone)\n unlinkSync(tombstone)\n } catch (error) {\n if (!isMissing(error)) throw error\n }\n }\n if (tryLinkOwner(options.lockPath, owner, `recovered.${attempt}`)) {\n return acquired(options.lockPath, owner)\n }\n } finally {\n releaseOwnedPath(reclaimPath, owner)\n }\n }\n\n throw new AtomicFileLockError(`could not acquire atomic file lock ${options.lockPath}`)\n}\n\nfunction acquired(lockPath: string, owner: AtomicFileLockOwner): AtomicFileLockAcquisition {\n return {\n acquired: true,\n lock: {\n owner,\n release: () => releaseOwnedPath(lockPath, owner),\n },\n }\n}\n\nfunction tryLinkOwner(lockPath: string, owner: AtomicFileLockOwner, suffix: string): boolean {\n const ownerPath = `${lockPath}.${owner.pid}.${owner.nonce}.${suffix}.owner`\n const descriptor = openSync(\n ownerPath,\n constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY,\n 0o600,\n )\n try {\n writeFileSync(descriptor, `${canonicalOwner(owner)}\\n`, 'utf8')\n fsyncSync(descriptor)\n } catch (error) {\n closeSync(descriptor)\n unlinkIfExists(ownerPath)\n throw error\n }\n closeSync(descriptor)\n\n try {\n linkSync(ownerPath, lockPath)\n return true\n } catch (error) {\n if (!isAlreadyExists(error)) throw error\n return false\n } finally {\n unlinkIfExists(ownerPath)\n }\n}\n\nfunction ownerState(lockPath: string): OwnerState {\n const owner = readOwner(lockPath)\n if (!owner) return { state: 'missing' }\n if (owner.host !== hostname()) return { state: 'held', owner }\n try {\n process.kill(owner.pid, 0)\n return { state: 'held', owner }\n } catch (error) {\n if (isNoSuchProcess(error)) return { state: 'stale', owner }\n return { state: 'held', owner }\n }\n}\n\nfunction readOwner(lockPath: string): AtomicFileLockOwner | undefined {\n let contents: string\n try {\n contents = readFileSync(lockPath, 'utf8').trim()\n } catch (error) {\n if (isMissing(error)) return undefined\n throw error\n }\n\n let value: unknown\n try {\n value = JSON.parse(contents)\n } catch (error) {\n throw invalidOwner(lockPath, error)\n }\n if (!isOwner(value)) throw invalidOwner(lockPath)\n return value\n}\n\nfunction isOwner(value: unknown): value is AtomicFileLockOwner {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return false\n const record = value as Record<string, unknown>\n return (\n Object.keys(record).length === 3 &&\n Number.isSafeInteger(record.pid) &&\n Number(record.pid) > 0 &&\n typeof record.host === 'string' &&\n record.host.trim().length > 0 &&\n typeof record.nonce === 'string' &&\n record.nonce.trim().length > 0\n )\n}\n\nfunction invalidOwner(lockPath: string, cause?: unknown): AtomicFileLockError {\n const error = new AtomicFileLockError(\n `atomic file lock has an invalid owner (${lockPath}); refusing unsafe recovery`,\n )\n if (cause !== undefined) Object.defineProperty(error, 'cause', { value: cause })\n return error\n}\n\nfunction releaseOwnedPath(lockPath: string, owner: AtomicFileLockOwner): void {\n const current = readOwner(lockPath)\n if (!current) return\n if (canonicalOwner(current) !== canonicalOwner(owner)) {\n throw new AtomicFileLockError(`atomic file lock owner changed before release (${lockPath})`)\n }\n try {\n unlinkSync(lockPath)\n } catch (error) {\n if (!isMissing(error)) throw error\n }\n}\n\nfunction canonicalOwner(owner: AtomicFileLockOwner): string {\n return JSON.stringify({ host: owner.host, nonce: owner.nonce, pid: owner.pid })\n}\n\nfunction recoveryPath(lockPath: string): string {\n return `${lockPath}.reclaim`\n}\n\nfunction isAlreadyExists(error: unknown): boolean {\n return error instanceof Error && 'code' in error && error.code === 'EEXIST'\n}\n\nfunction isMissing(error: unknown): boolean {\n return error instanceof Error && 'code' in error && error.code === 'ENOENT'\n}\n\nfunction isNoSuchProcess(error: unknown): boolean {\n return error instanceof Error && 'code' in error && error.code === 'ESRCH'\n}\n\nfunction unlinkIfExists(path: string): void {\n try {\n unlinkSync(path)\n } catch (error) {\n if (!isMissing(error)) throw error\n }\n}\n","/** Filesystem durability and cross-process exclusion for append-only journal files. */\n\nimport { closeSync, constants, fsyncSync, mkdirSync, openSync, writeSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport { AtomicFileLockError, tryAcquireAtomicFileLock } from './atomic-file-lock'\n\n/** How a journal binding reports file-layer faults in its own error taxonomy. */\nexport interface LedgerFileContext {\n /** Noun used in error messages, e.g. 'search ledger'. */\n subject: string\n integrityError(message: string, options?: { cause?: unknown }): Error\n}\n\n/** Append one already-serialized line; the write is fsynced (file and directory)\n * before returning so an acknowledged append survives a crash. */\nexport function appendLedgerLine(path: string, line: string, context: LedgerFileContext): void {\n mkdirSync(dirname(path), { recursive: true })\n const fd = openSync(path, constants.O_CREAT | constants.O_WRONLY | constants.O_APPEND, 0o600)\n try {\n writeAll(fd, Buffer.from(line, 'utf8'), context)\n fsyncSync(fd)\n } finally {\n closeSync(fd)\n }\n fsyncDirectory(dirname(path))\n}\n\nexport function withLedgerFileLock<T>(path: string, context: LedgerFileContext, run: () => T): T {\n const result = tryWithLedgerFileLock(path, context, run)\n if (!result.acquired) {\n throw context.integrityError(`${context.subject} lock is held (${path})`)\n }\n return result.value\n}\n\nexport type FileLockResult<T> = { acquired: true; value: T } | { acquired: false }\n\nexport function tryWithLedgerFileLock<T>(\n path: string,\n context: LedgerFileContext,\n run: () => T,\n): FileLockResult<T> {\n mkdirSync(dirname(path), { recursive: true })\n const lockPath = `${path}.lock`\n try {\n const acquisition = tryAcquireAtomicFileLock({ lockPath })\n if (!acquisition.acquired) return { acquired: false }\n try {\n return { acquired: true, value: run() }\n } finally {\n acquisition.lock.release()\n }\n } catch (error) {\n if (error instanceof AtomicFileLockError) {\n throw context.integrityError(error.message, { cause: error })\n }\n throw error\n }\n}\n\nfunction writeAll(fd: number, bytes: Buffer, context: LedgerFileContext): void {\n let offset = 0\n while (offset < bytes.byteLength) {\n const written = writeSync(fd, bytes, offset, bytes.byteLength - offset)\n if (written <= 0) throw context.integrityError('filesystem wrote zero bytes')\n offset += written\n }\n}\n\nfunction fsyncDirectory(path: string): void {\n const fd = openSync(path, constants.O_RDONLY)\n try {\n fsyncSync(fd)\n } finally {\n closeSync(fd)\n }\n}\n","/**\n * Generic durable append-only journal with a SHA-256 hash chain.\n *\n * The file format is canonical JSONL: every row is the canonical JSON encoding\n * (object keys sorted recursively) of one entry, and every entry hashes the\n * previous entry's hash so the log is tamper-evident. Appends are serialized\n * in-process per path and across processes via a lock file, fsynced before\n * acknowledgement, and idempotent by `eventId`. A malformed, non-canonical,\n * truncated, reordered, or conflicting log fails loudly; a bad row is never\n * skipped.\n *\n * Domain vocabulary lives entirely in the consumer's codec: entry schema\n * validation, the constant header fields stamped into every entry, the error\n * taxonomy, and the state machine replayed over verified entries.\n */\n\nimport { createHash } from 'node:crypto'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { Mutex } from '../concurrency'\nimport { canonicalize } from '../pre-registration'\nimport { appendLedgerLine, type LedgerFileContext, withLedgerFileLock } from './journal-file'\n\nexport type LedgerHash = `sha256:${string}`\n\n/** Canonical JSON encoding: object keys sorted recursively. This is the byte\n * form that is hashed and stored, so identical values always encode identically. */\nexport function canonicalString(value: unknown): string {\n return JSON.stringify(canonicalize(value))\n}\n\nexport function hashCanonical(value: unknown): LedgerHash {\n return `sha256:${createHash('sha256').update(canonicalString(value)).digest('hex')}`\n}\n\n/** Minimum shape of a journal event: the idempotency key. */\nexport interface LedgerEventBase {\n eventId: string\n}\n\n/** Chain envelope every entry carries in addition to the journal's constant\n * header fields. `entryHash` covers the header, sequence, previous hash, and\n * event, so any rewrite of history invalidates every later entry. */\nexport interface LedgerChainFields<Event extends LedgerEventBase> {\n sequence: number\n previousHash: LedgerHash | null\n event: Event\n entryHash: LedgerHash\n}\n\nexport type LedgerEntryOf<Header extends object, Event extends LedgerEventBase> = Header &\n LedgerChainFields<Event>\n\nexport interface LedgerLineContext {\n path: string\n line: number\n}\n\n/** Domain state machine replayed over chain-verified entries in order.\n * `apply` throws (via the codec's error taxonomy) on an invalid transition;\n * `finish` builds the consumer's projection once every entry was applied. */\nexport interface LedgerProjector<Entry, Projection> {\n apply(entry: Entry, index: number): void\n finish(entries: Entry[]): Projection\n}\n\nexport interface LedgerJournalCodec<\n Header extends object,\n Event extends LedgerEventBase,\n Projection,\n> extends LedgerFileContext {\n /** Constant fields stamped into every entry and covered by its hash. */\n header: Header\n conflictError(message: string): Error\n /** Validate one parsed JSON row into a typed entry. Must reject malformed\n * rows and non-canonical event encodings with the binding's own errors;\n * the journal itself verifies the stored bytes and the hash chain. */\n parseEntry(raw: unknown, context: LedgerLineContext): LedgerEntryOf<Header, Event>\n /** Reject an entry whose constant header fields do not match this journal. */\n checkEntryHeader(entry: LedgerEntryOf<Header, Event>, index: number): void\n createProjector(): LedgerProjector<LedgerEntryOf<Header, Event>, Projection>\n}\n\nexport interface LedgerReplayResult<Entry, Projection> {\n entries: Entry[]\n projection: Projection\n}\n\nexport interface LedgerAppendResult<Entry, Projection> {\n entry: Entry\n /** False when the exact event was already durably present. */\n appended: boolean\n projection: Projection\n}\n\n// One async mutex per resolved journal path so concurrent appends from a single\n// process queue up instead of failing on the held cross-process lock file.\nconst journalMutexes = new Map<string, Mutex>()\n\nfunction mutexFor(path: string): Mutex {\n const existing = journalMutexes.get(path)\n if (existing) return existing\n const mutex = new Mutex()\n journalMutexes.set(path, mutex)\n return mutex\n}\n\n/** Durable filesystem journal. Construction performs no I/O; `append` and\n * `replay` validate the complete existing file under the locks. */\nexport class FileLedgerJournal<Header extends object, Event extends LedgerEventBase, Projection> {\n readonly path: string\n private readonly codec: LedgerJournalCodec<Header, Event, Projection>\n private readonly mutex: Mutex\n\n constructor(path: string, codec: LedgerJournalCodec<Header, Event, Projection>) {\n this.path = resolve(path)\n this.codec = codec\n this.mutex = mutexFor(this.path)\n }\n\n async replay(): Promise<LedgerReplayResult<LedgerEntryOf<Header, Event>, Projection>> {\n return this.mutex.runExclusive(() =>\n withLedgerFileLock(this.path, this.codec, () => this.replayLocked()),\n )\n }\n\n async append(\n event: Event,\n ): Promise<LedgerAppendResult<LedgerEntryOf<Header, Event>, Projection>> {\n return this.mutex.runExclusive(() =>\n withLedgerFileLock(this.path, this.codec, () => {\n const before = this.replayLocked()\n const existing = before.entries.find((entry) => entry.event.eventId === event.eventId)\n if (existing) {\n if (canonicalString(existing.event) !== canonicalString(event)) {\n throw this.codec.conflictError(\n `eventId ${event.eventId} already exists with different content`,\n )\n }\n return { entry: existing, appended: false, projection: before.projection }\n }\n\n const material = {\n ...this.codec.header,\n sequence: before.entries.length,\n previousHash: before.entries.at(-1)?.entryHash ?? null,\n event,\n }\n const entry = { ...material, entryHash: hashCanonical(material) }\n\n // Apply the state transition before spending an append: an entry the\n // projector rejects must never reach the durable file.\n const projection = this.project([...before.entries, entry])\n appendLedgerLine(this.path, `${canonicalString(entry)}\\n`, this.codec)\n return { entry, appended: true, projection }\n }),\n )\n }\n\n private replayLocked(): LedgerReplayResult<LedgerEntryOf<Header, Event>, Projection> {\n const entries = this.readEntries()\n return { entries, projection: this.project(entries) }\n }\n\n private readEntries(): LedgerEntryOf<Header, Event>[] {\n if (!existsSync(this.path)) return []\n const text = readFileSync(this.path, 'utf8')\n if (text.length === 0) return []\n if (!text.endsWith('\\n')) {\n throw this.codec.integrityError(\n `${this.codec.subject} ${this.path} has a truncated final record (missing newline)`,\n )\n }\n\n const lines = text.slice(0, -1).split('\\n')\n const entries: LedgerEntryOf<Header, Event>[] = []\n for (let index = 0; index < lines.length; index += 1) {\n const line = lines[index]!\n if (line.length === 0) {\n throw this.codec.integrityError(\n `${this.codec.subject} ${this.path} has a blank row at line ${index + 1}`,\n )\n }\n let raw: unknown\n try {\n raw = JSON.parse(line)\n } catch (error) {\n throw this.codec.integrityError(\n `${this.codec.subject} ${this.path} has invalid JSON at line ${index + 1}`,\n { cause: error },\n )\n }\n const entry = this.codec.parseEntry(raw, { path: this.path, line: index + 1 })\n if (line !== canonicalString(entry)) {\n throw this.codec.integrityError(\n `${this.codec.subject} ${this.path} has non-canonical bytes at line ${index + 1}`,\n )\n }\n entries.push(entry)\n }\n return entries\n }\n\n private project(entries: LedgerEntryOf<Header, Event>[]): Projection {\n const projector = this.codec.createProjector()\n const eventIds = new Set<string>()\n let expectedPrevious: LedgerHash | null = null\n for (let index = 0; index < entries.length; index += 1) {\n const entry = entries[index]!\n this.codec.checkEntryHeader(entry, index)\n if (entry.sequence !== index) {\n throw this.codec.integrityError(\n `entry ${entry.event.eventId} has sequence ${entry.sequence}, expected ${index}`,\n )\n }\n if (entry.previousHash !== expectedPrevious) {\n throw this.codec.integrityError(\n `entry ${entry.event.eventId} does not extend the previous hash`,\n )\n }\n const { entryHash: _entryHash, ...material } = entry\n const expectedHash = hashCanonical(material)\n if (entry.entryHash !== expectedHash) {\n throw this.codec.integrityError(\n `entry ${entry.event.eventId} hash mismatch: expected ${expectedHash}, got ${entry.entryHash}`,\n )\n }\n expectedPrevious = entry.entryHash\n if (eventIds.has(entry.event.eventId)) {\n throw this.codec.integrityError(\n `duplicate eventId ${entry.event.eventId} in durable ledger`,\n )\n }\n eventIds.add(entry.event.eventId)\n projector.apply(entry, index)\n }\n return projector.finish(entries)\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { a as OutcomeFilter, i as InMemoryOutcomeStore, n as FileSystemOutcomeStore, o as OutcomeStore, r as FileSystemOutcomeStoreOptions, t as DeploymentOutcome } from "../outcome-store-BYHIuO0e.js";
|
|
2
|
-
import { A as EvalMetricSpec, C as CalibrationPair, D as CorrelationResult, E as calibrationFromPairs, M as correlationStudy, O as CorrelationStudyOptions, S as CalibrationOptions, T as calibrationCurve, _ as snapshotFromAgreement, a as SentinelMetrics, b as validateSentinelSnapshot, c as SentinelSnapshot, d as SentinelTrend, f as SnapshotMeta, g as judgeSentinelReport, h as inMemorySentinelStore, i as SentinelMetricName, j as OutcomePair, k as CorrelationStudyResult, l as SentinelStore, m as fileSentinelStore, n as JudgeSentinelOptions, o as SentinelReport, p as evalHealthStamp, r as SENTINEL_METRIC_NAMES, s as SentinelSetOptions, t as EvalHealthStamp, u as SentinelThresholds, v as snapshotFromCalibration, w as CalibrationReport, x as CalibrationBin, y as snapshotFromSentinelSet } from "../index-
|
|
3
|
-
import { a as rubricPredictiveValidity, i as RubricRanking, n as RubricPredictiveValidityInput, r as RubricPredictiveValidityReport, t as RubricOutcomePair } from "../rubric-predictive-validity-
|
|
2
|
+
import { A as EvalMetricSpec, C as CalibrationPair, D as CorrelationResult, E as calibrationFromPairs, M as correlationStudy, O as CorrelationStudyOptions, S as CalibrationOptions, T as calibrationCurve, _ as snapshotFromAgreement, a as SentinelMetrics, b as validateSentinelSnapshot, c as SentinelSnapshot, d as SentinelTrend, f as SnapshotMeta, g as judgeSentinelReport, h as inMemorySentinelStore, i as SentinelMetricName, j as OutcomePair, k as CorrelationStudyResult, l as SentinelStore, m as fileSentinelStore, n as JudgeSentinelOptions, o as SentinelReport, p as evalHealthStamp, r as SENTINEL_METRIC_NAMES, s as SentinelSetOptions, t as EvalHealthStamp, u as SentinelThresholds, v as snapshotFromCalibration, w as CalibrationReport, x as CalibrationBin, y as snapshotFromSentinelSet } from "../index-D_F6VAKe.js";
|
|
3
|
+
import { a as rubricPredictiveValidity, i as RubricRanking, n as RubricPredictiveValidityInput, r as RubricPredictiveValidityReport, t as RubricOutcomePair } from "../rubric-predictive-validity-lXLmashy.js";
|
|
4
4
|
export { CalibrationBin, CalibrationOptions, CalibrationPair, CalibrationReport, CorrelationResult, CorrelationStudyOptions, CorrelationStudyResult, DeploymentOutcome, EvalHealthStamp, EvalMetricSpec, FileSystemOutcomeStore, FileSystemOutcomeStoreOptions, InMemoryOutcomeStore, JudgeSentinelOptions, OutcomeFilter, OutcomePair, OutcomeStore, RubricOutcomePair, RubricPredictiveValidityInput, RubricPredictiveValidityReport, RubricRanking, SENTINEL_METRIC_NAMES, SentinelMetricName, SentinelMetrics, SentinelReport, SentinelSetOptions, SentinelSnapshot, SentinelStore, SentinelThresholds, SentinelTrend, SnapshotMeta, calibrationCurve, calibrationFromPairs, correlationStudy, evalHealthStamp, fileSentinelStore, inMemorySentinelStore, judgeSentinelReport, rubricPredictiveValidity, snapshotFromAgreement, snapshotFromCalibration, snapshotFromSentinelSet, validateSentinelSnapshot };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { s as ValidationError } from "./errors-8YnH8WlF.js";
|
|
2
|
-
import { o as runTaskScore } from "./run-record-BuoE80Dq.js";
|
|
3
2
|
import { a as scoreOrigin, i as rolloutRewardFields } from "./reward-nw2xZGZG.js";
|
|
3
|
+
import { o as runTaskScore } from "./run-record-CN8Zd21B.js";
|
|
4
4
|
import { i as ROLLOUT_SCHEMA, s as assertMinted } from "./schema-C6DW4ZHR.js";
|
|
5
5
|
import { t as buildTrajectory } from "./trajectory-D_7rLrvE.js";
|
|
6
6
|
//#region src/rollout/mint.ts
|
|
@@ -198,4 +198,4 @@ async function mintRolloutRows(records, store, options = {}) {
|
|
|
198
198
|
//#endregion
|
|
199
199
|
export { mintRolloutRows as t };
|
|
200
200
|
|
|
201
|
-
//# sourceMappingURL=mint-
|
|
201
|
+
//# sourceMappingURL=mint-D5_87M5L.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mint-yN2M2eh0.js","names":[],"sources":["../src/rollout/mint.ts"],"sourcesContent":["/**\n * Rollout minting — `tangle.rollout.v1` lines joined from the records the\n * substrate ALREADY keeps. There is no separate rollout store: a rollout\n * is the JOIN of a RunRecord (identity, provenance, cost, outcome) with\n * its trace (spans share `runId`), projected into the canonical line.\n *\n * Composition, not duplication:\n * - identity/provenance → `RunRecord` (candidateId, splitTag, agentProfile, hashes)\n * - step structure → `buildTrajectory` over the shared TraceStore\n * - preference-pair export → `feedbackTrajectoryToOptimizerRow` (feedback-trajectory.ts)\n * - PRM / reward-model → `reward-model-export.ts`\n *\n * Anti-Goodhart invariant: a run whose `outcome.realness.gated` is true is\n * never exported with a positive reward OR with any of the numbers that reward\n * was computed from. The gate travels into the training data (`reward` forced\n * to 0, `realness_gated: true`) and the whole outcome is transformed by\n * `gateGamedOutcome` inside `assertMinted` below, which relocates `metrics` and\n * `verdict` to `provenance.gated_evidence`. Mint returns\n * `MintedRolloutLine[]`: the brand the training exporters require, which only\n * this function, `readRolloutLedger`, and an explicit `assertMinted` can mint.\n *\n * A record carrying NEITHER split score is REJECTED (`ValidationError`), never\n * minted at 0 — \"nobody graded this\" is not the same claim as \"graded a total\n * failure\", and a trainer reading 0 learns the second. Lines that already\n * carry `reward: null` (interchange imports, existing ledgers) remain valid on\n * the wire; only the RunRecord→line door refuses.\n *\n * Records without spans become labeled GAP LINES (messages: [],\n * provenance.gap) — present in the output AND surfaced in\n * `missingTraces`; a capture gap is a finding, never a silent omission.\n */\n\nimport { ValidationError } from '../errors'\nimport { type RunRecord, runTaskScore } from '../run-record'\nimport type { LlmSpan, Message, Span, ToolSpan } from '../trace/schema'\nimport type { TraceStore } from '../trace/store'\nimport { buildTrajectory } from '../trajectory'\nimport { rolloutRewardFields, scoreOrigin } from './reward'\nimport {\n assertMinted,\n type ChatMessage,\n type MintedRolloutLine,\n ROLLOUT_SCHEMA,\n type RolloutRole,\n type RolloutSplit,\n type RolloutStep,\n} from './schema'\n\n/** Redactor applied to every exported string (secrets, PII). Identity by default. */\nexport type RolloutScrubber = (text: string) => string\n\nexport interface MintRolloutOptions {\n scrub?: RolloutScrubber\n /** Cap steps per line (longest runs first drop middle steps). Default: no cap. */\n maxSteps?: number\n /** Role recorded on every minted line. Default 'agent' (a solo eval run). */\n role?: RolloutRole\n /** Task suite label. Default: the record's `experimentId`. */\n suite?: string\n /** Injected clock for deterministic output. */\n now?: () => Date\n}\n\nexport interface MintRolloutResult {\n rows: MintedRolloutLine[]\n /** runIds that had a RunRecord but no spans — emitted as gap lines AND listed here. */\n missingTraces: string[]\n}\n\nconst asText = (v: unknown, scrub: RolloutScrubber): string => {\n const s = typeof v === 'string' ? v : JSON.stringify(v)\n return scrub(s ?? '')\n}\n\nfunction projectStep(span: Span, scrub: RolloutScrubber): RolloutStep {\n const base: RolloutStep = {\n kind: span.kind,\n name: scrub(span.name),\n status: span.status,\n durationMs: span.endedAt !== undefined ? span.endedAt - span.startedAt : undefined,\n }\n if (span.kind === 'llm') {\n const llm = span as LlmSpan\n const last = llm.messages[llm.messages.length - 1]\n if (last) base.input = scrub(last.content)\n if (llm.output !== undefined) base.output = scrub(llm.output)\n } else if (span.kind === 'tool') {\n const tool = span as ToolSpan\n base.input = asText(tool.args, scrub)\n if (tool.result !== undefined) base.output = asText(tool.result, scrub)\n }\n return base\n}\n\n/** The final llm span's history + output is the completed conversation. */\nfunction finalConversation(spans: Span[], scrub: RolloutScrubber): ChatMessage[] {\n const llms = spans.filter((s): s is LlmSpan => s.kind === 'llm')\n const last = llms[llms.length - 1]\n if (!last) return []\n const messages: ChatMessage[] = last.messages.map((m: Message) => ({\n role: m.role,\n content: scrub(m.content),\n }))\n if (last.output !== undefined && last.output !== '') {\n messages.push({ role: 'assistant', content: scrub(last.output) })\n }\n return messages\n}\n\n// The reward derivations live in the leaf module `./reward` so gate and\n// reporting code can import them without dragging in the trace store; they are\n// re-exported here because the derivations shipped from this path.\nexport {\n isRealnessGated,\n observedScore,\n observedSplitScore,\n type ScoreOrigin,\n type ScorePreference,\n scoreOrigin,\n trainingReward,\n trainingScore,\n} from './reward'\n\nconst REWARD_SOURCE: Record<ReturnType<typeof scoreOrigin>, string> = {\n holdout: 'run-record/holdout-score',\n search: 'run-record/search-score',\n unscored: 'run-record/unscored',\n}\n\n/**\n * The mint door refuses an execution-only record: a missing training label is\n * not a zero reward, and not a mintable line either. Lines that already carry\n * `reward: null` — interchange imports, existing ledgers — stay valid on the\n * wire and keep their labeled gap; this guard is only about the\n * RunRecord→line door, where the producer can still be told to go score the\n * run instead of shipping an unlabeled row.\n */\nfunction requireTaskScore(record: RunRecord): void {\n if (runTaskScore(record) === undefined) {\n throw new ValidationError(`Cannot mint rollout for run ${record.runId}: task score is missing`)\n }\n}\n\nconst SPLIT_FROM_TAG: Record<RunRecord['splitTag'], RolloutSplit> = {\n search: 'search',\n dev: 'dev',\n holdout: 'holdout',\n}\n\nfunction mintLine(\n record: RunRecord,\n steps: RolloutStep[],\n messages: ChatMessage[],\n options: MintRolloutOptions,\n capturedAt: string,\n gap?: string,\n): MintedRolloutLine {\n // A missing task score is refused before anything is built: an\n // execution-only record has no training label, and a missing label is\n // neither a zero reward nor a mintable row.\n requireTaskScore(record)\n // `reward` and `realness_gated` come out of one call, so neither door into\n // the waist can write one and forget the other.\n const rewardFields = rolloutRewardFields(record)\n const uncaptured = record.costProvenance.kind === 'uncaptured'\n const terminalOutcome = record.terminalOutcome\n const isCompleted = terminalOutcome === 'succeeded' || terminalOutcome === 'failed'\n const isTruncated = terminalOutcome === 'cancelled' || terminalOutcome === 'incomplete'\n const terminalError =\n terminalOutcome === 'failed' ||\n terminalOutcome === 'cancelled' ||\n terminalOutcome === 'incomplete'\n ? (record.terminalFailureReason ?? `run ended ${terminalOutcome}`)\n : null\n // `assertMinted` rather than a cast: mint is the producer the whole gate\n // rests on, so it proves the line it just built is valid instead of asserting\n // it by fiat. The brand is unforgeable precisely because nobody casts to it.\n return assertMinted(\n {\n schema: ROLLOUT_SCHEMA,\n rollout_id: record.runId,\n parent_rollout_id: null,\n run_id: record.runId,\n experiment_id: record.experimentId,\n candidate_id: record.candidateId,\n generation: null,\n candidate_index: null,\n role: options.role ?? 'agent',\n task: {\n suite: options.suite ?? record.experimentId,\n instance_id: record.scenarioId,\n split: SPLIT_FROM_TAG[record.splitTag],\n seed: record.seed,\n rep: 0,\n },\n policy: {\n harness: null,\n harness_version: null,\n model: record.model,\n provider: null,\n profile_commit: record.commitSha,\n prompt_hash: record.promptHash,\n config_hash: record.configHash,\n agent_profile_cell_id: record.agentProfile?.cellId ?? null,\n sampling: null,\n },\n messages,\n tool_defs: [],\n ...(steps.length > 0 ? { steps } : {}),\n outcome: {\n ...rewardFields,\n reward_source: REWARD_SOURCE[scoreOrigin(record)],\n verdict: null,\n // A verbatim bulk copy, deliberately UNFILTERED here. `outcome.raw`\n // holds the per-layer verifier scores (`layer.*`) that the reward was\n // derived from, so on a gated run this dict is the reward signal in\n // component form — but filtering it at this call site is the pattern\n // that has now leaked twice, because the next producer to write a\n // reward-bearing field forgets. The gate is applied to the whole\n // outcome once, in `assertMinted` below (`gateGamedOutcome`), which\n // moves the block to `provenance.gated_evidence` when the run is gated\n // and leaves it here untouched when it is not.\n metrics: { ...record.outcome.raw },\n is_completed: isCompleted,\n is_truncated: isTruncated,\n error: terminalError,\n },\n cost: {\n usd: uncaptured ? null : record.costUsd,\n tokens_in: record.tokenUsage.input,\n tokens_out: record.tokenUsage.output,\n tokens_reasoning: record.tokenUsage.reasoning ?? null,\n cache_read: record.tokenUsage.cached ?? null,\n cache_write: record.tokenUsage.cacheWrite ?? null,\n wall_s: Math.round(record.wallMs / 1000),\n },\n artifacts: { patch_path: null, run_dir: null, transcript_ref: null },\n provenance: {\n captured_at: capturedAt,\n capture: 'mint',\n ...(gap !== undefined ? { gap } : {}),\n },\n },\n `minted rollout line for run ${record.runId}`,\n )\n}\n\n/**\n * Join RunRecords with their traces into canonical rollout lines. Records\n * without spans are emitted as labeled gap lines and reported in\n * `missingTraces`. Execution-only records without a task score are rejected\n * because a missing training label is not a zero reward.\n */\nexport async function mintRolloutRows(\n records: RunRecord[],\n store: TraceStore,\n options: MintRolloutOptions = {},\n): Promise<MintRolloutResult> {\n const scrub = options.scrub ?? ((t) => t)\n const capturedAt = (options.now?.() ?? new Date()).toISOString()\n const rows: MintedRolloutLine[] = []\n const missingTraces: string[] = []\n for (const record of records) {\n const trajectory = await buildTrajectory(store, record.runId)\n if (trajectory.steps.length === 0) {\n missingTraces.push(record.runId)\n rows.push(\n mintLine(record, [], [], options, capturedAt, 'no trace spans recorded for this runId'),\n )\n continue\n }\n let steps = trajectory.steps.map((s) => projectStep(s.span, scrub))\n if (options.maxSteps !== undefined && steps.length > options.maxSteps) {\n // Keep the head and tail — the middle of a long run is the least\n // informative for outcome attribution.\n const head = Math.ceil(options.maxSteps / 2)\n const tail = options.maxSteps - head\n steps = [...steps.slice(0, head), ...steps.slice(steps.length - tail)]\n }\n const conversation = finalConversation(\n trajectory.steps.map((s) => s.span),\n scrub,\n )\n const gap =\n conversation.length === 0 ? 'trace has no llm spans — no conversation to inline' : undefined\n rows.push(mintLine(record, steps, conversation, options, capturedAt, gap))\n }\n return { rows, missingTraces }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mint-D5_87M5L.js","names":[],"sources":["../src/rollout/mint.ts"],"sourcesContent":["/**\n * Rollout minting — `tangle.rollout.v1` lines joined from the records the\n * substrate ALREADY keeps. There is no separate rollout store: a rollout\n * is the JOIN of a RunRecord (identity, provenance, cost, outcome) with\n * its trace (spans share `runId`), projected into the canonical line.\n *\n * Composition, not duplication:\n * - identity/provenance → `RunRecord` (candidateId, splitTag, agentProfile, hashes)\n * - step structure → `buildTrajectory` over the shared TraceStore\n * - preference-pair export → `feedbackTrajectoryToOptimizerRow` (feedback-trajectory.ts)\n * - PRM / reward-model → `reward-model-export.ts`\n *\n * Anti-Goodhart invariant: a run whose `outcome.realness.gated` is true is\n * never exported with a positive reward OR with any of the numbers that reward\n * was computed from. The gate travels into the training data (`reward` forced\n * to 0, `realness_gated: true`) and the whole outcome is transformed by\n * `gateGamedOutcome` inside `assertMinted` below, which relocates `metrics` and\n * `verdict` to `provenance.gated_evidence`. Mint returns\n * `MintedRolloutLine[]`: the brand the training exporters require, which only\n * this function, `readRolloutLedger`, and an explicit `assertMinted` can mint.\n *\n * A record carrying NEITHER split score is REJECTED (`ValidationError`), never\n * minted at 0 — \"nobody graded this\" is not the same claim as \"graded a total\n * failure\", and a trainer reading 0 learns the second. Lines that already\n * carry `reward: null` (interchange imports, existing ledgers) remain valid on\n * the wire; only the RunRecord→line door refuses.\n *\n * Records without spans become labeled GAP LINES (messages: [],\n * provenance.gap) — present in the output AND surfaced in\n * `missingTraces`; a capture gap is a finding, never a silent omission.\n */\n\nimport { ValidationError } from '../errors'\nimport { type RunRecord, runTaskScore } from '../run-record'\nimport type { LlmSpan, Message, Span, ToolSpan } from '../trace/schema'\nimport type { TraceStore } from '../trace/store'\nimport { buildTrajectory } from '../trajectory'\nimport { rolloutRewardFields, scoreOrigin } from './reward'\nimport {\n assertMinted,\n type ChatMessage,\n type MintedRolloutLine,\n ROLLOUT_SCHEMA,\n type RolloutRole,\n type RolloutSplit,\n type RolloutStep,\n} from './schema'\n\n/** Redactor applied to every exported string (secrets, PII). Identity by default. */\nexport type RolloutScrubber = (text: string) => string\n\nexport interface MintRolloutOptions {\n scrub?: RolloutScrubber\n /** Cap steps per line (longest runs first drop middle steps). Default: no cap. */\n maxSteps?: number\n /** Role recorded on every minted line. Default 'agent' (a solo eval run). */\n role?: RolloutRole\n /** Task suite label. Default: the record's `experimentId`. */\n suite?: string\n /** Injected clock for deterministic output. */\n now?: () => Date\n}\n\nexport interface MintRolloutResult {\n rows: MintedRolloutLine[]\n /** runIds that had a RunRecord but no spans — emitted as gap lines AND listed here. */\n missingTraces: string[]\n}\n\nconst asText = (v: unknown, scrub: RolloutScrubber): string => {\n const s = typeof v === 'string' ? v : JSON.stringify(v)\n return scrub(s ?? '')\n}\n\nfunction projectStep(span: Span, scrub: RolloutScrubber): RolloutStep {\n const base: RolloutStep = {\n kind: span.kind,\n name: scrub(span.name),\n status: span.status,\n durationMs: span.endedAt !== undefined ? span.endedAt - span.startedAt : undefined,\n }\n if (span.kind === 'llm') {\n const llm = span as LlmSpan\n const last = llm.messages[llm.messages.length - 1]\n if (last) base.input = scrub(last.content)\n if (llm.output !== undefined) base.output = scrub(llm.output)\n } else if (span.kind === 'tool') {\n const tool = span as ToolSpan\n base.input = asText(tool.args, scrub)\n if (tool.result !== undefined) base.output = asText(tool.result, scrub)\n }\n return base\n}\n\n/** The final llm span's history + output is the completed conversation. */\nfunction finalConversation(spans: Span[], scrub: RolloutScrubber): ChatMessage[] {\n const llms = spans.filter((s): s is LlmSpan => s.kind === 'llm')\n const last = llms[llms.length - 1]\n if (!last) return []\n const messages: ChatMessage[] = last.messages.map((m: Message) => ({\n role: m.role,\n content: scrub(m.content),\n }))\n if (last.output !== undefined && last.output !== '') {\n messages.push({ role: 'assistant', content: scrub(last.output) })\n }\n return messages\n}\n\n// The reward derivations live in the leaf module `./reward` so gate and\n// reporting code can import them without dragging in the trace store; they are\n// re-exported here because the derivations shipped from this path.\nexport {\n isRealnessGated,\n observedScore,\n observedSplitScore,\n type ScoreOrigin,\n type ScorePreference,\n scoreOrigin,\n trainingReward,\n trainingScore,\n} from './reward'\n\nconst REWARD_SOURCE: Record<ReturnType<typeof scoreOrigin>, string> = {\n holdout: 'run-record/holdout-score',\n search: 'run-record/search-score',\n unscored: 'run-record/unscored',\n}\n\n/**\n * The mint door refuses an execution-only record: a missing training label is\n * not a zero reward, and not a mintable line either. Lines that already carry\n * `reward: null` — interchange imports, existing ledgers — stay valid on the\n * wire and keep their labeled gap; this guard is only about the\n * RunRecord→line door, where the producer can still be told to go score the\n * run instead of shipping an unlabeled row.\n */\nfunction requireTaskScore(record: RunRecord): void {\n if (runTaskScore(record) === undefined) {\n throw new ValidationError(`Cannot mint rollout for run ${record.runId}: task score is missing`)\n }\n}\n\nconst SPLIT_FROM_TAG: Record<RunRecord['splitTag'], RolloutSplit> = {\n search: 'search',\n dev: 'dev',\n holdout: 'holdout',\n}\n\nfunction mintLine(\n record: RunRecord,\n steps: RolloutStep[],\n messages: ChatMessage[],\n options: MintRolloutOptions,\n capturedAt: string,\n gap?: string,\n): MintedRolloutLine {\n // A missing task score is refused before anything is built: an\n // execution-only record has no training label, and a missing label is\n // neither a zero reward nor a mintable row.\n requireTaskScore(record)\n // `reward` and `realness_gated` come out of one call, so neither door into\n // the waist can write one and forget the other.\n const rewardFields = rolloutRewardFields(record)\n const uncaptured = record.costProvenance.kind === 'uncaptured'\n const terminalOutcome = record.terminalOutcome\n const isCompleted = terminalOutcome === 'succeeded' || terminalOutcome === 'failed'\n const isTruncated = terminalOutcome === 'cancelled' || terminalOutcome === 'incomplete'\n const terminalError =\n terminalOutcome === 'failed' ||\n terminalOutcome === 'cancelled' ||\n terminalOutcome === 'incomplete'\n ? (record.terminalFailureReason ?? `run ended ${terminalOutcome}`)\n : null\n // `assertMinted` rather than a cast: mint is the producer the whole gate\n // rests on, so it proves the line it just built is valid instead of asserting\n // it by fiat. The brand is unforgeable precisely because nobody casts to it.\n return assertMinted(\n {\n schema: ROLLOUT_SCHEMA,\n rollout_id: record.runId,\n parent_rollout_id: null,\n run_id: record.runId,\n experiment_id: record.experimentId,\n candidate_id: record.candidateId,\n generation: null,\n candidate_index: null,\n role: options.role ?? 'agent',\n task: {\n suite: options.suite ?? record.experimentId,\n instance_id: record.scenarioId,\n split: SPLIT_FROM_TAG[record.splitTag],\n seed: record.seed,\n rep: 0,\n },\n policy: {\n harness: null,\n harness_version: null,\n model: record.model,\n provider: null,\n profile_commit: record.commitSha,\n prompt_hash: record.promptHash,\n config_hash: record.configHash,\n agent_profile_cell_id: record.agentProfile?.cellId ?? null,\n sampling: null,\n },\n messages,\n tool_defs: [],\n ...(steps.length > 0 ? { steps } : {}),\n outcome: {\n ...rewardFields,\n reward_source: REWARD_SOURCE[scoreOrigin(record)],\n verdict: null,\n // A verbatim bulk copy, deliberately UNFILTERED here. `outcome.raw`\n // holds the per-layer verifier scores (`layer.*`) that the reward was\n // derived from, so on a gated run this dict is the reward signal in\n // component form — but filtering it at this call site is the pattern\n // that has now leaked twice, because the next producer to write a\n // reward-bearing field forgets. The gate is applied to the whole\n // outcome once, in `assertMinted` below (`gateGamedOutcome`), which\n // moves the block to `provenance.gated_evidence` when the run is gated\n // and leaves it here untouched when it is not.\n metrics: { ...record.outcome.raw },\n is_completed: isCompleted,\n is_truncated: isTruncated,\n error: terminalError,\n },\n cost: {\n usd: uncaptured ? null : record.costUsd,\n tokens_in: record.tokenUsage.input,\n tokens_out: record.tokenUsage.output,\n tokens_reasoning: record.tokenUsage.reasoning ?? null,\n cache_read: record.tokenUsage.cached ?? null,\n cache_write: record.tokenUsage.cacheWrite ?? null,\n wall_s: Math.round(record.wallMs / 1000),\n },\n artifacts: { patch_path: null, run_dir: null, transcript_ref: null },\n provenance: {\n captured_at: capturedAt,\n capture: 'mint',\n ...(gap !== undefined ? { gap } : {}),\n },\n },\n `minted rollout line for run ${record.runId}`,\n )\n}\n\n/**\n * Join RunRecords with their traces into canonical rollout lines. Records\n * without spans are emitted as labeled gap lines and reported in\n * `missingTraces`. Execution-only records without a task score are rejected\n * because a missing training label is not a zero reward.\n */\nexport async function mintRolloutRows(\n records: RunRecord[],\n store: TraceStore,\n options: MintRolloutOptions = {},\n): Promise<MintRolloutResult> {\n const scrub = options.scrub ?? ((t) => t)\n const capturedAt = (options.now?.() ?? new Date()).toISOString()\n const rows: MintedRolloutLine[] = []\n const missingTraces: string[] = []\n for (const record of records) {\n const trajectory = await buildTrajectory(store, record.runId)\n if (trajectory.steps.length === 0) {\n missingTraces.push(record.runId)\n rows.push(\n mintLine(record, [], [], options, capturedAt, 'no trace spans recorded for this runId'),\n )\n continue\n }\n let steps = trajectory.steps.map((s) => projectStep(s.span, scrub))\n if (options.maxSteps !== undefined && steps.length > options.maxSteps) {\n // Keep the head and tail — the middle of a long run is the least\n // informative for outcome attribution.\n const head = Math.ceil(options.maxSteps / 2)\n const tail = options.maxSteps - head\n steps = [...steps.slice(0, head), ...steps.slice(steps.length - tail)]\n }\n const conversation = finalConversation(\n trajectory.steps.map((s) => s.span),\n scrub,\n )\n const gap =\n conversation.length === 0 ? 'trace has no llm spans — no conversation to inline' : undefined\n rows.push(mintLine(record, steps, conversation, options, capturedAt, gap))\n }\n return { rows, missingTraces }\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { w as JudgeScore } from "../types-
|
|
1
|
+
import { w as JudgeScore } from "../types-CsD5nTfV.js";
|
|
2
2
|
import { o as MatrixResult } from "../index-DSC51roc.js";
|
|
3
3
|
import { AgentProfile } from "@tangle-network/agent-interface";
|
|
4
4
|
//#region src/multishot/types.d.ts
|
package/dist/openapi.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"openapi": "3.1.0",
|
|
3
3
|
"info": {
|
|
4
4
|
"title": "@tangle-network/agent-eval — wire protocol",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.131.1",
|
|
6
6
|
"description": "HTTP and stdio RPC interface to agent-eval. The TypeScript runtime is the source of truth; this spec is the contract that cross-language clients (Python, Rust, Go) generate from.\n\nWire-protocol version: 1.0.0. Bumps on breaking changes to request/response schemas.",
|
|
7
7
|
"contact": {
|
|
8
8
|
"name": "Tangle Network",
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { C as verifyAgentProfileCell, S as validateAgentProfileCell, _ as buildAgentInterfaceProfileCell, a as AgentProfileCellSchemaVersion, b as requireAgentProfileCell, c as AgentProfileHarness, d as AgentProfileKind, f as AgentProfileSource, g as assertRunAgentProfileCell, h as agentProfileCellKey, i as AgentProfileCellInput, l as AgentProfileJson, m as agentProfileCellHashMaterial, n as AgentInterfaceProfileLike, o as AgentProfileCellValidationError, p as AgentProfileSourceInput, r as AgentProfileCell, s as AgentProfileDimensionValue, t as AGENT_PROFILE_KINDS, u as AgentProfileJsonObject, v as buildAgentProfileCell, x as toAgentProfileJson, y as groupRunsByAgentProfileCell } from "./agent-profile-cell-CCm3l2v2.js";
|
|
2
|
+
export { AGENT_PROFILE_KINDS, AgentInterfaceProfileLike, AgentProfileCell, AgentProfileCellInput, AgentProfileCellSchemaVersion, AgentProfileCellValidationError, AgentProfileDimensionValue, AgentProfileHarness, AgentProfileJson, AgentProfileJsonObject, AgentProfileKind, AgentProfileSource, AgentProfileSourceInput, agentProfileCellHashMaterial, agentProfileCellKey, assertRunAgentProfileCell, buildAgentInterfaceProfileCell, buildAgentProfileCell, groupRunsByAgentProfileCell, requireAgentProfileCell, toAgentProfileJson, validateAgentProfileCell, verifyAgentProfileCell };
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { s as ValidationError } from "./errors-8YnH8WlF.js";
|
|
2
|
+
import { r as hashJson } from "./pre-registration-DakwTRXk.js";
|
|
3
|
+
//#region src/agent-profile-cell.ts
|
|
4
|
+
var AgentProfileCellValidationError = class extends ValidationError {
|
|
5
|
+
path;
|
|
6
|
+
constructor(message, path = "") {
|
|
7
|
+
super(path ? `${message} (at ${path})` : message);
|
|
8
|
+
this.path = path;
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
const SHA256_HEX = /^[0-9a-f]{64}$/;
|
|
12
|
+
const CELL_ID = /^agent-profile-cell:sha256:[0-9a-f]{64}$/;
|
|
13
|
+
async function buildAgentProfileCell(input) {
|
|
14
|
+
const material = await normalizeAgentProfileCellInput(input);
|
|
15
|
+
const cellId = `agent-profile-cell:sha256:${await hashJson(material)}`;
|
|
16
|
+
return {
|
|
17
|
+
...material,
|
|
18
|
+
cellId
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function agentProfileCellHashMaterial(cell) {
|
|
22
|
+
const { cellId: _cellId, ...material } = cell;
|
|
23
|
+
return normalizeAgentProfileCell(material);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Verify an `AgentProfileCell`'s `cellId` matches the sha256 of its hash-material fields, confirming the record has not been tampered with.
|
|
27
|
+
*/
|
|
28
|
+
async function verifyAgentProfileCell(cell) {
|
|
29
|
+
validateAgentProfileCell(cell);
|
|
30
|
+
return cell.cellId === `agent-profile-cell:sha256:${await hashJson(agentProfileCellHashMaterial(cell))}`;
|
|
31
|
+
}
|
|
32
|
+
function validateAgentProfileCell(input) {
|
|
33
|
+
if (input === null || typeof input !== "object") throw new AgentProfileCellValidationError("expected object");
|
|
34
|
+
const obj = input;
|
|
35
|
+
expectLiteral(obj.schemaVersion, "agent-profile-cell/v1", "schemaVersion");
|
|
36
|
+
if (typeof obj.cellId !== "string" || !CELL_ID.test(obj.cellId)) throw new AgentProfileCellValidationError("cellId must match agent-profile-cell:sha256:<64 lowercase hex chars>", "cellId");
|
|
37
|
+
expectString(obj.profileId, "profileId");
|
|
38
|
+
validateSource(obj.sourceProfile, "sourceProfile");
|
|
39
|
+
if (obj.harness !== void 0) validateHarness(obj.harness, "harness");
|
|
40
|
+
if (obj.model !== void 0) expectString(obj.model, "model");
|
|
41
|
+
if (obj.promptHash !== void 0) expectString(obj.promptHash, "promptHash");
|
|
42
|
+
if (obj.dimensions !== void 0) validateDimensions(obj.dimensions, "dimensions");
|
|
43
|
+
return input;
|
|
44
|
+
}
|
|
45
|
+
function requireAgentProfileCell(record) {
|
|
46
|
+
if (!record.agentProfile) throw new AgentProfileCellValidationError(`run "${record.runId}" is missing agentProfile; profile-cell grouping requires explicit profile identity`, "agentProfile");
|
|
47
|
+
return validateAgentProfileCell(record.agentProfile);
|
|
48
|
+
}
|
|
49
|
+
function agentProfileCellKey(record) {
|
|
50
|
+
return requireAgentProfileCell(record).cellId;
|
|
51
|
+
}
|
|
52
|
+
async function assertRunAgentProfileCell(record) {
|
|
53
|
+
const profile = requireAgentProfileCell(record);
|
|
54
|
+
if (!await verifyAgentProfileCell(profile)) throw new AgentProfileCellValidationError(`run "${record.runId}" has an agentProfile.cellId that does not match its content`, "agentProfile.cellId");
|
|
55
|
+
if (profile.model !== void 0 && profile.model !== record.model) throw new AgentProfileCellValidationError(`run "${record.runId}" agentProfile.model "${profile.model}" does not match model "${record.model}"`, "agentProfile.model");
|
|
56
|
+
if (profile.promptHash !== void 0 && profile.promptHash !== record.promptHash) throw new AgentProfileCellValidationError(`run "${record.runId}" agentProfile.promptHash "${profile.promptHash}" does not match promptHash "${record.promptHash}"`, "agentProfile.promptHash");
|
|
57
|
+
return profile;
|
|
58
|
+
}
|
|
59
|
+
function groupRunsByAgentProfileCell(records) {
|
|
60
|
+
const groups = /* @__PURE__ */ new Map();
|
|
61
|
+
for (const record of records) {
|
|
62
|
+
const key = agentProfileCellKey(record);
|
|
63
|
+
const bucket = groups.get(key);
|
|
64
|
+
if (bucket) bucket.push(record);
|
|
65
|
+
else groups.set(key, [record]);
|
|
66
|
+
}
|
|
67
|
+
return groups;
|
|
68
|
+
}
|
|
69
|
+
async function normalizeAgentProfileCellInput(input) {
|
|
70
|
+
return normalizeAgentProfileCell({
|
|
71
|
+
schemaVersion: "agent-profile-cell/v1",
|
|
72
|
+
profileId: input.profileId,
|
|
73
|
+
sourceProfile: await normalizeSourceInput(input.sourceProfile),
|
|
74
|
+
harness: input.harness,
|
|
75
|
+
model: input.model,
|
|
76
|
+
promptHash: input.promptHash,
|
|
77
|
+
dimensions: input.dimensions
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
function normalizeAgentProfileCell(input) {
|
|
81
|
+
return compactObject({
|
|
82
|
+
schemaVersion: "agent-profile-cell/v1",
|
|
83
|
+
profileId: requireNonEmpty(input.profileId, "profileId"),
|
|
84
|
+
sourceProfile: normalizeSource(input.sourceProfile),
|
|
85
|
+
harness: input.harness ? normalizeHarness(input.harness, "harness") : void 0,
|
|
86
|
+
model: optionalNonEmpty(input.model, "model"),
|
|
87
|
+
promptHash: optionalNonEmpty(input.promptHash, "promptHash"),
|
|
88
|
+
dimensions: input.dimensions ? nonEmptyRecord(normalizeDimensions(input.dimensions)) : void 0
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
async function normalizeSourceInput(input) {
|
|
92
|
+
const kind = requireNonEmpty(input.kind, "sourceProfile.kind");
|
|
93
|
+
if (input.hash !== void 0 && input.profile !== void 0) throw new AgentProfileCellValidationError("sourceProfile must provide either hash or profile, not both", "sourceProfile");
|
|
94
|
+
if (input.hash !== void 0) return {
|
|
95
|
+
kind,
|
|
96
|
+
hash: requireSha256Hex(input.hash, "sourceProfile.hash")
|
|
97
|
+
};
|
|
98
|
+
if (input.profile === void 0) throw new AgentProfileCellValidationError("sourceProfile must provide hash or profile", "sourceProfile");
|
|
99
|
+
assertJson(input.profile, "sourceProfile.profile");
|
|
100
|
+
return {
|
|
101
|
+
kind,
|
|
102
|
+
hash: await hashJson(input.profile)
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function normalizeSource(input) {
|
|
106
|
+
return {
|
|
107
|
+
kind: requireNonEmpty(input.kind, "sourceProfile.kind"),
|
|
108
|
+
hash: requireSha256Hex(input.hash, "sourceProfile.hash")
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function normalizeHarness(input, path) {
|
|
112
|
+
return compactObject({
|
|
113
|
+
id: requireNonEmpty(input.id, `${path}.id`),
|
|
114
|
+
version: optionalNonEmpty(input.version, `${path}.version`),
|
|
115
|
+
hash: optionalNonEmpty(input.hash, `${path}.hash`)
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
function normalizeDimensions(input) {
|
|
119
|
+
const out = {};
|
|
120
|
+
for (const key of Object.keys(input).sort()) {
|
|
121
|
+
const value = input[key];
|
|
122
|
+
requireNonEmpty(key, "dimensions.<key>");
|
|
123
|
+
if (value !== null && typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") throw new AgentProfileCellValidationError("expected primitive dimension value", `dimensions.${key}`);
|
|
124
|
+
if (typeof value === "number" && !Number.isFinite(value)) throw new AgentProfileCellValidationError("expected finite number", `dimensions.${key}`);
|
|
125
|
+
out[key] = value;
|
|
126
|
+
}
|
|
127
|
+
return out;
|
|
128
|
+
}
|
|
129
|
+
function compactObject(input) {
|
|
130
|
+
const out = {};
|
|
131
|
+
for (const [key, value] of Object.entries(input)) if (value !== void 0) out[key] = value;
|
|
132
|
+
return out;
|
|
133
|
+
}
|
|
134
|
+
function nonEmptyRecord(input) {
|
|
135
|
+
return Object.keys(input).length > 0 ? input : void 0;
|
|
136
|
+
}
|
|
137
|
+
function validateSource(value, path) {
|
|
138
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new AgentProfileCellValidationError("expected object", path);
|
|
139
|
+
const rec = value;
|
|
140
|
+
expectString(rec.kind, `${path}.kind`);
|
|
141
|
+
requireSha256Hex(rec.hash, `${path}.hash`);
|
|
142
|
+
}
|
|
143
|
+
function validateHarness(value, path) {
|
|
144
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new AgentProfileCellValidationError("expected object", path);
|
|
145
|
+
const rec = value;
|
|
146
|
+
expectString(rec.id, `${path}.id`);
|
|
147
|
+
if (rec.version !== void 0) expectString(rec.version, `${path}.version`);
|
|
148
|
+
if (rec.hash !== void 0) expectString(rec.hash, `${path}.hash`);
|
|
149
|
+
}
|
|
150
|
+
function validateDimensions(value, path) {
|
|
151
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new AgentProfileCellValidationError("expected object", path);
|
|
152
|
+
normalizeDimensions(value);
|
|
153
|
+
}
|
|
154
|
+
function assertJson(value, path) {
|
|
155
|
+
if (value === null) return;
|
|
156
|
+
const type = typeof value;
|
|
157
|
+
if (type === "string" || type === "boolean") return;
|
|
158
|
+
if (type === "number") {
|
|
159
|
+
if (!Number.isFinite(value)) throw new AgentProfileCellValidationError("expected finite number", path);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (Array.isArray(value)) {
|
|
163
|
+
value.forEach((item, index) => {
|
|
164
|
+
assertJson(item, `${path}[${index}]`);
|
|
165
|
+
});
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (type === "object") {
|
|
169
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
170
|
+
requireNonEmpty(key, `${path}.<key>`);
|
|
171
|
+
assertJson(nested, `${path}.${key}`);
|
|
172
|
+
}
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
throw new AgentProfileCellValidationError("expected JSON-compatible value", path);
|
|
176
|
+
}
|
|
177
|
+
function expectLiteral(value, expected, path) {
|
|
178
|
+
if (value !== expected) throw new AgentProfileCellValidationError(`expected ${expected}`, path);
|
|
179
|
+
}
|
|
180
|
+
function expectString(value, path) {
|
|
181
|
+
if (typeof value !== "string" || value.length === 0) throw new AgentProfileCellValidationError("expected non-empty string", path);
|
|
182
|
+
}
|
|
183
|
+
function requireNonEmpty(value, path) {
|
|
184
|
+
if (typeof value !== "string" || value.length === 0) throw new AgentProfileCellValidationError("expected non-empty string", path);
|
|
185
|
+
return value;
|
|
186
|
+
}
|
|
187
|
+
function optionalNonEmpty(value, path) {
|
|
188
|
+
if (value === void 0) return void 0;
|
|
189
|
+
return requireNonEmpty(value, path);
|
|
190
|
+
}
|
|
191
|
+
function requireSha256Hex(value, path) {
|
|
192
|
+
if (typeof value !== "string" || !SHA256_HEX.test(value)) throw new AgentProfileCellValidationError("expected 64 lowercase sha256 hex chars", path);
|
|
193
|
+
return value;
|
|
194
|
+
}
|
|
195
|
+
/** Canonical `sourceProfile.kind` values. Two products fingerprinting the
|
|
196
|
+
* same canonical profile MUST use the same kind for their cells to share
|
|
197
|
+
* `sourceProfile.hash`. Extend rather than create new strings — adding a
|
|
198
|
+
* new kind is a deliberate cross-product schema change. */
|
|
199
|
+
const AGENT_PROFILE_KINDS = {
|
|
200
|
+
/** A profile declared via `defineAgentProfile(...)` from
|
|
201
|
+
* `@tangle-network/agent-interface`. The default kind for router-backed
|
|
202
|
+
* and sandbox-backed products. */
|
|
203
|
+
AGENT_INTERFACE_PROFILE: "agent-interface-profile" };
|
|
204
|
+
/** Canonicalize an arbitrary value into `AgentProfileJson` by JSON
|
|
205
|
+
* round-trip. Throws when the value contains anything not representable
|
|
206
|
+
* as JSON (functions, BigInt, cycles) — non-portable profiles fail loud
|
|
207
|
+
* rather than silently dropping fields. */
|
|
208
|
+
function toAgentProfileJson(value) {
|
|
209
|
+
let serialized;
|
|
210
|
+
try {
|
|
211
|
+
serialized = JSON.stringify(value);
|
|
212
|
+
} catch (err) {
|
|
213
|
+
throw new AgentProfileCellValidationError(`agent profile must be JSON-serializable: ${err instanceof Error ? err.message : String(err)}`, "sourceProfile.profile");
|
|
214
|
+
}
|
|
215
|
+
if (serialized === void 0) throw new AgentProfileCellValidationError("agent profile must be JSON-serializable (got undefined after JSON.stringify)", "sourceProfile.profile");
|
|
216
|
+
return JSON.parse(serialized);
|
|
217
|
+
}
|
|
218
|
+
/** Higher-level helper that hard-codes the canonical
|
|
219
|
+
* `agent-interface-profile` kind plus the JSON canonicalization. Equivalent
|
|
220
|
+
* to calling `buildAgentProfileCell` with `profileId = \`${name}@${version}\``
|
|
221
|
+
* and `sourceProfile = { kind: AGENT_INTERFACE_PROFILE, profile: <round-tripped> }`.
|
|
222
|
+
*
|
|
223
|
+
* Use this from any product consuming an agent-interface `AgentProfile`; the
|
|
224
|
+
* manual `buildAgentProfileCell` call is reserved for advanced cases
|
|
225
|
+
* (custom kinds, pre-computed source hashes, alternate profileId
|
|
226
|
+
* conventions). */
|
|
227
|
+
async function buildAgentInterfaceProfileCell(profile, input) {
|
|
228
|
+
if (!profile || typeof profile !== "object") throw new AgentProfileCellValidationError("AgentProfile must be an object", "profile");
|
|
229
|
+
if (typeof profile.name !== "string" || profile.name.length === 0) throw new AgentProfileCellValidationError("AgentProfile must have a non-empty `name`", "profile.name");
|
|
230
|
+
if (typeof profile.version !== "string" || profile.version.length === 0) throw new AgentProfileCellValidationError("AgentProfile must have a non-empty `version`", "profile.version");
|
|
231
|
+
return buildAgentProfileCell({
|
|
232
|
+
...input,
|
|
233
|
+
profileId: `${profile.name}@${profile.version}`,
|
|
234
|
+
sourceProfile: {
|
|
235
|
+
kind: AGENT_PROFILE_KINDS.AGENT_INTERFACE_PROFILE,
|
|
236
|
+
profile: toAgentProfileJson(profile)
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
//#endregion
|
|
241
|
+
export { AGENT_PROFILE_KINDS, AgentProfileCellValidationError, agentProfileCellHashMaterial, agentProfileCellKey, assertRunAgentProfileCell, buildAgentInterfaceProfileCell, buildAgentProfileCell, groupRunsByAgentProfileCell, requireAgentProfileCell, toAgentProfileJson, validateAgentProfileCell, verifyAgentProfileCell };
|
|
242
|
+
|
|
243
|
+
//# sourceMappingURL=profile-cell.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile-cell.js","names":[],"sources":["../src/agent-profile-cell.ts"],"sourcesContent":["import type { AgentProfile } from '@tangle-network/agent-interface'\nimport { ValidationError } from './errors'\nimport { hashJson } from './pre-registration'\n\nexport type AgentProfileCellSchemaVersion = 'agent-profile-cell/v1'\n\nexport type AgentProfileJsonObject = { [key: string]: AgentProfileJson }\n\nexport type AgentProfileJson =\n | string\n | number\n | boolean\n | null\n | AgentProfileJson[]\n | AgentProfileJsonObject\n\nexport type AgentProfileDimensionValue = string | number | boolean | null\n\nexport interface AgentProfileSource {\n /** Runtime/profile contract being fingerprinted, e.g. `agent-interface-profile`. */\n kind: string\n /** sha256 over the canonical source profile object. */\n hash: string\n}\n\nexport interface AgentProfileSourceInput {\n kind: string\n /** Precomputed sha256 for callers that already sign their profile artifact. */\n hash?: string\n /** Full canonical runtime profile; hashed and then discarded from the cell. */\n profile?: AgentProfileJson\n}\n\nexport interface AgentProfileHarness {\n id: string\n version?: string\n hash?: string\n}\n\nexport interface AgentProfileCellInput {\n profileId: string\n sourceProfile: AgentProfileSourceInput\n harness?: AgentProfileHarness\n model?: string\n promptHash?: string\n dimensions?: Record<string, AgentProfileDimensionValue>\n}\n\nexport interface AgentProfileCell {\n schemaVersion: AgentProfileCellSchemaVersion\n cellId: string\n profileId: string\n sourceProfile: AgentProfileSource\n harness?: AgentProfileHarness\n model?: string\n promptHash?: string\n dimensions?: Record<string, AgentProfileDimensionValue>\n}\n\nexport class AgentProfileCellValidationError extends ValidationError {\n readonly path: string\n constructor(message: string, path = '') {\n super(path ? `${message} (at ${path})` : message)\n this.path = path\n }\n}\n\nconst SHA256_HEX = /^[0-9a-f]{64}$/\nconst CELL_ID = /^agent-profile-cell:sha256:[0-9a-f]{64}$/\n\nexport async function buildAgentProfileCell(\n input: AgentProfileCellInput,\n): Promise<AgentProfileCell> {\n const material = await normalizeAgentProfileCellInput(input)\n const cellId = `agent-profile-cell:sha256:${await hashJson(material)}`\n return { ...material, cellId }\n}\n\nexport function agentProfileCellHashMaterial(\n cell: AgentProfileCell,\n): Omit<AgentProfileCell, 'cellId'> {\n const { cellId: _cellId, ...material } = cell\n void _cellId\n return normalizeAgentProfileCell(material)\n}\n\n/**\n * Verify an `AgentProfileCell`'s `cellId` matches the sha256 of its hash-material fields, confirming the record has not been tampered with.\n */\nexport async function verifyAgentProfileCell(cell: AgentProfileCell): Promise<boolean> {\n validateAgentProfileCell(cell)\n return (\n cell.cellId ===\n `agent-profile-cell:sha256:${await hashJson(agentProfileCellHashMaterial(cell))}`\n )\n}\n\nexport function validateAgentProfileCell(input: unknown): AgentProfileCell {\n if (input === null || typeof input !== 'object') {\n throw new AgentProfileCellValidationError('expected object')\n }\n const obj = input as Record<string, unknown>\n expectLiteral(obj.schemaVersion, 'agent-profile-cell/v1', 'schemaVersion')\n if (typeof obj.cellId !== 'string' || !CELL_ID.test(obj.cellId)) {\n throw new AgentProfileCellValidationError(\n 'cellId must match agent-profile-cell:sha256:<64 lowercase hex chars>',\n 'cellId',\n )\n }\n expectString(obj.profileId, 'profileId')\n validateSource(obj.sourceProfile, 'sourceProfile')\n if (obj.harness !== undefined) validateHarness(obj.harness, 'harness')\n if (obj.model !== undefined) expectString(obj.model, 'model')\n if (obj.promptHash !== undefined) expectString(obj.promptHash, 'promptHash')\n if (obj.dimensions !== undefined) validateDimensions(obj.dimensions, 'dimensions')\n return input as AgentProfileCell\n}\n\nexport function requireAgentProfileCell(record: {\n runId: string\n agentProfile?: AgentProfileCell\n}): AgentProfileCell {\n if (!record.agentProfile) {\n throw new AgentProfileCellValidationError(\n `run \"${record.runId}\" is missing agentProfile; profile-cell grouping requires explicit profile identity`,\n 'agentProfile',\n )\n }\n return validateAgentProfileCell(record.agentProfile)\n}\n\nexport function agentProfileCellKey(record: {\n runId: string\n agentProfile?: AgentProfileCell\n}): string {\n return requireAgentProfileCell(record).cellId\n}\n\nexport async function assertRunAgentProfileCell(record: {\n runId: string\n model: string\n promptHash: string\n agentProfile?: AgentProfileCell\n}): Promise<AgentProfileCell> {\n const profile = requireAgentProfileCell(record)\n if (!(await verifyAgentProfileCell(profile))) {\n throw new AgentProfileCellValidationError(\n `run \"${record.runId}\" has an agentProfile.cellId that does not match its content`,\n 'agentProfile.cellId',\n )\n }\n if (profile.model !== undefined && profile.model !== record.model) {\n throw new AgentProfileCellValidationError(\n `run \"${record.runId}\" agentProfile.model \"${profile.model}\" does not match model \"${record.model}\"`,\n 'agentProfile.model',\n )\n }\n if (profile.promptHash !== undefined && profile.promptHash !== record.promptHash) {\n throw new AgentProfileCellValidationError(\n `run \"${record.runId}\" agentProfile.promptHash \"${profile.promptHash}\" does not match promptHash \"${record.promptHash}\"`,\n 'agentProfile.promptHash',\n )\n }\n return profile\n}\n\nexport function groupRunsByAgentProfileCell<\n T extends { runId: string; agentProfile?: AgentProfileCell },\n>(records: readonly T[]): Map<string, T[]> {\n const groups = new Map<string, T[]>()\n for (const record of records) {\n const key = agentProfileCellKey(record)\n const bucket = groups.get(key)\n if (bucket) bucket.push(record)\n else groups.set(key, [record])\n }\n return groups\n}\n\nasync function normalizeAgentProfileCellInput(\n input: AgentProfileCellInput,\n): Promise<Omit<AgentProfileCell, 'cellId'>> {\n return normalizeAgentProfileCell({\n schemaVersion: 'agent-profile-cell/v1',\n profileId: input.profileId,\n sourceProfile: await normalizeSourceInput(input.sourceProfile),\n harness: input.harness,\n model: input.model,\n promptHash: input.promptHash,\n dimensions: input.dimensions,\n })\n}\n\nfunction normalizeAgentProfileCell(\n input: Omit<AgentProfileCell, 'cellId'>,\n): Omit<AgentProfileCell, 'cellId'> {\n return compactObject({\n schemaVersion: 'agent-profile-cell/v1' as const,\n profileId: requireNonEmpty(input.profileId, 'profileId'),\n sourceProfile: normalizeSource(input.sourceProfile),\n harness: input.harness ? normalizeHarness(input.harness, 'harness') : undefined,\n model: optionalNonEmpty(input.model, 'model'),\n promptHash: optionalNonEmpty(input.promptHash, 'promptHash'),\n dimensions: input.dimensions\n ? nonEmptyRecord(normalizeDimensions(input.dimensions))\n : undefined,\n })\n}\n\nasync function normalizeSourceInput(input: AgentProfileSourceInput): Promise<AgentProfileSource> {\n const kind = requireNonEmpty(input.kind, 'sourceProfile.kind')\n if (input.hash !== undefined && input.profile !== undefined) {\n throw new AgentProfileCellValidationError(\n 'sourceProfile must provide either hash or profile, not both',\n 'sourceProfile',\n )\n }\n if (input.hash !== undefined) {\n return { kind, hash: requireSha256Hex(input.hash, 'sourceProfile.hash') }\n }\n if (input.profile === undefined) {\n throw new AgentProfileCellValidationError(\n 'sourceProfile must provide hash or profile',\n 'sourceProfile',\n )\n }\n assertJson(input.profile, 'sourceProfile.profile')\n return { kind, hash: await hashJson(input.profile) }\n}\n\nfunction normalizeSource(input: AgentProfileSource): AgentProfileSource {\n return {\n kind: requireNonEmpty(input.kind, 'sourceProfile.kind'),\n hash: requireSha256Hex(input.hash, 'sourceProfile.hash'),\n }\n}\n\nfunction normalizeHarness(input: AgentProfileHarness, path: string): AgentProfileHarness {\n return compactObject({\n id: requireNonEmpty(input.id, `${path}.id`),\n version: optionalNonEmpty(input.version, `${path}.version`),\n hash: optionalNonEmpty(input.hash, `${path}.hash`),\n })\n}\n\nfunction normalizeDimensions(\n input: Record<string, AgentProfileDimensionValue>,\n): Record<string, AgentProfileDimensionValue> {\n const out: Record<string, AgentProfileDimensionValue> = {}\n for (const key of Object.keys(input).sort()) {\n const value = input[key]\n requireNonEmpty(key, 'dimensions.<key>')\n if (\n value !== null &&\n typeof value !== 'string' &&\n typeof value !== 'number' &&\n typeof value !== 'boolean'\n ) {\n throw new AgentProfileCellValidationError(\n 'expected primitive dimension value',\n `dimensions.${key}`,\n )\n }\n if (typeof value === 'number' && !Number.isFinite(value)) {\n throw new AgentProfileCellValidationError('expected finite number', `dimensions.${key}`)\n }\n out[key] = value\n }\n return out\n}\n\nfunction compactObject<T extends Record<string, unknown>>(input: T): T {\n const out: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(input)) {\n if (value !== undefined) out[key] = value\n }\n return out as T\n}\n\nfunction nonEmptyRecord<T extends Record<string, unknown>>(input: T): T | undefined {\n return Object.keys(input).length > 0 ? input : undefined\n}\n\nfunction validateSource(value: unknown, path: string): void {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) {\n throw new AgentProfileCellValidationError('expected object', path)\n }\n const rec = value as Record<string, unknown>\n expectString(rec.kind, `${path}.kind`)\n requireSha256Hex(rec.hash, `${path}.hash`)\n}\n\nfunction validateHarness(value: unknown, path: string): void {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) {\n throw new AgentProfileCellValidationError('expected object', path)\n }\n const rec = value as Record<string, unknown>\n expectString(rec.id, `${path}.id`)\n if (rec.version !== undefined) expectString(rec.version, `${path}.version`)\n if (rec.hash !== undefined) expectString(rec.hash, `${path}.hash`)\n}\n\nfunction validateDimensions(value: unknown, path: string): void {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) {\n throw new AgentProfileCellValidationError('expected object', path)\n }\n normalizeDimensions(value as Record<string, AgentProfileDimensionValue>)\n}\n\nfunction assertJson(value: AgentProfileJson, path: string): void {\n if (value === null) return\n const type = typeof value\n if (type === 'string' || type === 'boolean') return\n if (type === 'number') {\n if (!Number.isFinite(value)) {\n throw new AgentProfileCellValidationError('expected finite number', path)\n }\n return\n }\n if (Array.isArray(value)) {\n value.forEach((item, index) => {\n assertJson(item, `${path}[${index}]`)\n })\n return\n }\n if (type === 'object') {\n for (const [key, nested] of Object.entries(value)) {\n requireNonEmpty(key, `${path}.<key>`)\n assertJson(nested, `${path}.${key}`)\n }\n return\n }\n throw new AgentProfileCellValidationError('expected JSON-compatible value', path)\n}\n\nfunction expectLiteral(value: unknown, expected: string, path: string): void {\n if (value !== expected) {\n throw new AgentProfileCellValidationError(`expected ${expected}`, path)\n }\n}\n\nfunction expectString(value: unknown, path: string): void {\n if (typeof value !== 'string' || value.length === 0) {\n throw new AgentProfileCellValidationError('expected non-empty string', path)\n }\n}\n\nfunction requireNonEmpty(value: string, path: string): string {\n if (typeof value !== 'string' || value.length === 0) {\n throw new AgentProfileCellValidationError('expected non-empty string', path)\n }\n return value\n}\n\nfunction optionalNonEmpty(value: string | undefined, path: string): string | undefined {\n if (value === undefined) return undefined\n return requireNonEmpty(value, path)\n}\n\nfunction requireSha256Hex(value: unknown, path: string): string {\n if (typeof value !== 'string' || !SHA256_HEX.test(value)) {\n throw new AgentProfileCellValidationError('expected 64 lowercase sha256 hex chars', path)\n }\n return value\n}\n\n// ── Consumer helpers ─────────────────────────────────────────────────\n//\n// Boilerplate every product consuming `buildAgentProfileCell` used to duplicate:\n//\n// 1. A `JSON.parse(JSON.stringify(value))` helper that canonicalizes an\n// arbitrary `@tangle-network/agent-interface` `AgentProfile` into the recursive\n// `AgentProfileJson` shape, with a fail-loud error when the profile\n// is not JSON-serializable.\n//\n// 2. The magic string `'agent-interface-profile'` for `sourceProfile.kind`.\n//\n// Both belong here so the cross-product cell join (same canonical profile\n// hashes to the same `sourceProfile.hash` across products) is enforced by\n// the type system, not by every consumer remembering to do it right.\n// See blueprint-agent issue tangle-network/agent-eval#82.\n\n/** Canonical `sourceProfile.kind` values. Two products fingerprinting the\n * same canonical profile MUST use the same kind for their cells to share\n * `sourceProfile.hash`. Extend rather than create new strings — adding a\n * new kind is a deliberate cross-product schema change. */\nexport const AGENT_PROFILE_KINDS = {\n /** A profile declared via `defineAgentProfile(...)` from\n * `@tangle-network/agent-interface`. The default kind for router-backed\n * and sandbox-backed products. */\n AGENT_INTERFACE_PROFILE: 'agent-interface-profile',\n} as const\n\nexport type AgentProfileKind = (typeof AGENT_PROFILE_KINDS)[keyof typeof AGENT_PROFILE_KINDS]\n\n/** Canonicalize an arbitrary value into `AgentProfileJson` by JSON\n * round-trip. Throws when the value contains anything not representable\n * as JSON (functions, BigInt, cycles) — non-portable profiles fail loud\n * rather than silently dropping fields. */\nexport function toAgentProfileJson(value: unknown): AgentProfileJson {\n let serialized: string | undefined\n try {\n serialized = JSON.stringify(value)\n } catch (err) {\n throw new AgentProfileCellValidationError(\n `agent profile must be JSON-serializable: ${err instanceof Error ? err.message : String(err)}`,\n 'sourceProfile.profile',\n )\n }\n if (serialized === undefined) {\n throw new AgentProfileCellValidationError(\n 'agent profile must be JSON-serializable (got undefined after JSON.stringify)',\n 'sourceProfile.profile',\n )\n }\n return JSON.parse(serialized) as AgentProfileJson\n}\n\n/** Canonical AgentProfile shape required when deriving a stable cell id. */\nexport type AgentInterfaceProfileLike = AgentProfile & { name: string; version: string }\n\n/** Higher-level helper that hard-codes the canonical\n * `agent-interface-profile` kind plus the JSON canonicalization. Equivalent\n * to calling `buildAgentProfileCell` with `profileId = \\`${name}@${version}\\``\n * and `sourceProfile = { kind: AGENT_INTERFACE_PROFILE, profile: <round-tripped> }`.\n *\n * Use this from any product consuming an agent-interface `AgentProfile`; the\n * manual `buildAgentProfileCell` call is reserved for advanced cases\n * (custom kinds, pre-computed source hashes, alternate profileId\n * conventions). */\nexport async function buildAgentInterfaceProfileCell(\n profile: AgentInterfaceProfileLike,\n input: Omit<AgentProfileCellInput, 'profileId' | 'sourceProfile'>,\n): Promise<AgentProfileCell> {\n if (!profile || typeof profile !== 'object') {\n throw new AgentProfileCellValidationError('AgentProfile must be an object', 'profile')\n }\n if (typeof profile.name !== 'string' || profile.name.length === 0) {\n throw new AgentProfileCellValidationError(\n 'AgentProfile must have a non-empty `name`',\n 'profile.name',\n )\n }\n if (typeof profile.version !== 'string' || profile.version.length === 0) {\n throw new AgentProfileCellValidationError(\n 'AgentProfile must have a non-empty `version`',\n 'profile.version',\n )\n }\n return buildAgentProfileCell({\n ...input,\n profileId: `${profile.name}@${profile.version}`,\n sourceProfile: {\n kind: AGENT_PROFILE_KINDS.AGENT_INTERFACE_PROFILE,\n profile: toAgentProfileJson(profile),\n },\n })\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { s as validateRunRecord } from "./run-record-BuoE80Dq.js";
|
|
2
1
|
import { t as TraceEmitter } from "./emitter-CPBAhxum.js";
|
|
2
|
+
import { s as validateRunRecord } from "./run-record-CN8Zd21B.js";
|
|
3
3
|
import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
4
4
|
import { dirname } from "node:path";
|
|
5
5
|
//#region src/action-policy.ts
|
|
@@ -1455,4 +1455,4 @@ function controlFailureClassFromVerification(verification) {
|
|
|
1455
1455
|
//#endregion
|
|
1456
1456
|
export { observeAll as _, jsonlReviewStore as a, scoreFromEvals as c, runAgentControlLoop as d, stopOnNoProgress as f, noProgressDetector as g, errorStreakDetector as h, inMemoryReviewStore as i, allCriticalPassed as l, subjectiveEval as m, runProposeReviewAsControlLoop as n, runProposeReview as o, stopOnRepeatedAction as p, createLlmReviewer as r, controlRunToRunRecord as s, controlFailureClassFromVerification as t, objectiveEval as u, repeatedActionDetector as v, evaluateActionPolicy as y };
|
|
1457
1457
|
|
|
1458
|
-
//# sourceMappingURL=propose-review-control-
|
|
1458
|
+
//# sourceMappingURL=propose-review-control-Bqb7daEJ.js.map
|