@jinn-network/jinn-layer 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/dist/adapters/contribution-adapter.d.ts +18 -0
  2. package/dist/adapters/contribution-adapter.js +134 -0
  3. package/dist/adapters/corpus-adapter.d.ts +12 -0
  4. package/dist/adapters/corpus-adapter.js +212 -0
  5. package/dist/adapters/index.d.ts +17 -0
  6. package/dist/adapters/index.js +17 -0
  7. package/dist/adapters/json-map-store.d.ts +4 -0
  8. package/dist/adapters/json-map-store.js +31 -0
  9. package/dist/adapters/local-learning-adapter.d.ts +25 -0
  10. package/dist/adapters/local-learning-adapter.js +49 -0
  11. package/dist/adapters/skills-adapter.d.ts +8 -0
  12. package/dist/adapters/skills-adapter.js +55 -0
  13. package/dist/admission-policy.d.ts +38 -0
  14. package/dist/admission-policy.js +17 -0
  15. package/dist/bin/jinn-distill-mcp.d.ts +2 -0
  16. package/dist/bin/jinn-distill-mcp.js +10 -0
  17. package/dist/bin/jinn-layer.d.ts +7 -0
  18. package/dist/bin/jinn-layer.js +15 -0
  19. package/dist/bridge-fetch-evidence.d.ts +102 -0
  20. package/dist/bridge-fetch-evidence.js +677 -0
  21. package/dist/bridge-verdict-source.d.ts +47 -0
  22. package/dist/bridge-verdict-source.js +216 -0
  23. package/dist/bridge.d.ts +247 -0
  24. package/dist/bridge.js +439 -0
  25. package/dist/capture.d.ts +98 -0
  26. package/dist/capture.js +617 -0
  27. package/dist/cli.d.ts +148 -0
  28. package/dist/cli.js +2221 -0
  29. package/dist/cluster-selection.d.ts +34 -0
  30. package/dist/cluster-selection.js +135 -0
  31. package/dist/cluster.d.ts +89 -0
  32. package/dist/cluster.js +142 -0
  33. package/dist/consume.d.ts +139 -0
  34. package/dist/consume.js +280 -0
  35. package/dist/corpus-probes.d.ts +61 -0
  36. package/dist/corpus-probes.js +120 -0
  37. package/dist/corpus-store.d.ts +16 -0
  38. package/dist/corpus-store.js +101 -0
  39. package/dist/distill-captures.d.ts +56 -0
  40. package/dist/distill-captures.js +283 -0
  41. package/dist/distill-feedback.d.ts +20 -0
  42. package/dist/distill-feedback.js +30 -0
  43. package/dist/distill-llm.d.ts +138 -0
  44. package/dist/distill-llm.js +438 -0
  45. package/dist/distill-mcp-server.d.ts +30 -0
  46. package/dist/distill-mcp-server.js +281 -0
  47. package/dist/distill-mode.d.ts +62 -0
  48. package/dist/distill-mode.js +107 -0
  49. package/dist/distill-progress.d.ts +78 -0
  50. package/dist/distill-progress.js +69 -0
  51. package/dist/distill-prompt.d.ts +34 -0
  52. package/dist/distill-prompt.js +85 -0
  53. package/dist/distill-render.d.ts +123 -0
  54. package/dist/distill-render.js +303 -0
  55. package/dist/distill-runs.d.ts +28 -0
  56. package/dist/distill-runs.js +39 -0
  57. package/dist/distill-traces.d.ts +67 -0
  58. package/dist/distill-traces.js +211 -0
  59. package/dist/distill.d.ts +180 -0
  60. package/dist/distill.js +337 -0
  61. package/dist/distiller.d.ts +118 -0
  62. package/dist/distiller.js +142 -0
  63. package/dist/envelope.d.ts +9 -0
  64. package/dist/envelope.js +9 -0
  65. package/dist/eval-prep.d.ts +129 -0
  66. package/dist/eval-prep.js +523 -0
  67. package/dist/execution-publish.d.ts +62 -0
  68. package/dist/execution-publish.js +72 -0
  69. package/dist/gate.d.ts +39 -0
  70. package/dist/gate.js +106 -0
  71. package/dist/index.d.ts +49 -0
  72. package/dist/index.js +50 -0
  73. package/dist/ipfs-cid.d.ts +18 -0
  74. package/dist/ipfs-cid.js +149 -0
  75. package/dist/layer-default.d.ts +3 -0
  76. package/dist/layer-default.js +10 -0
  77. package/dist/ledger.d.ts +62 -0
  78. package/dist/ledger.js +104 -0
  79. package/dist/measurement.d.ts +90 -0
  80. package/dist/measurement.js +134 -0
  81. package/dist/path-safety.d.ts +19 -0
  82. package/dist/path-safety.js +303 -0
  83. package/dist/pipeline.d.ts +94 -0
  84. package/dist/pipeline.js +124 -0
  85. package/dist/plugin-wiring.d.ts +4 -0
  86. package/dist/plugin-wiring.js +54 -0
  87. package/dist/preview.d.ts +31 -0
  88. package/dist/preview.js +28 -0
  89. package/dist/process-contract.d.ts +303 -0
  90. package/dist/process-contract.js +118 -0
  91. package/dist/publish-skill.d.ts +86 -0
  92. package/dist/publish-skill.js +116 -0
  93. package/dist/publish.d.ts +294 -0
  94. package/dist/publish.js +1090 -0
  95. package/dist/scripts/scan-skills-registry.d.ts +24 -0
  96. package/dist/scripts/scan-skills-registry.js +207 -0
  97. package/dist/seed-import/curated-batch.d.ts +36 -0
  98. package/dist/seed-import/curated-batch.js +103 -0
  99. package/dist/seed-import/episode-execute.d.ts +50 -0
  100. package/dist/seed-import/episode-execute.js +235 -0
  101. package/dist/seed-import/episode-fetch.d.ts +119 -0
  102. package/dist/seed-import/episode-fetch.js +137 -0
  103. package/dist/seed-import/episode-plan.d.ts +11 -0
  104. package/dist/seed-import/episode-plan.js +27 -0
  105. package/dist/seed-import/episode-report.d.ts +39 -0
  106. package/dist/seed-import/episode-report.js +36 -0
  107. package/dist/seed-import/execute.d.ts +61 -0
  108. package/dist/seed-import/execute.js +245 -0
  109. package/dist/seed-import/fetch.d.ts +62 -0
  110. package/dist/seed-import/fetch.js +82 -0
  111. package/dist/seed-import/licence.d.ts +17 -0
  112. package/dist/seed-import/licence.js +36 -0
  113. package/dist/seed-import/plan.d.ts +10 -0
  114. package/dist/seed-import/plan.js +21 -0
  115. package/dist/seed-import/report.d.ts +34 -0
  116. package/dist/seed-import/report.js +31 -0
  117. package/dist/seed-import/state.d.ts +72 -0
  118. package/dist/seed-import/state.js +120 -0
  119. package/dist/signal.d.ts +36 -0
  120. package/dist/signal.js +48 -0
  121. package/dist/signing.d.ts +8 -0
  122. package/dist/signing.js +21 -0
  123. package/dist/skill-package.d.ts +70 -0
  124. package/dist/skill-package.js +141 -0
  125. package/dist/skill.d.ts +21 -0
  126. package/dist/skill.js +77 -0
  127. package/dist/snapshot-transcript.d.ts +54 -0
  128. package/dist/snapshot-transcript.js +117 -0
  129. package/dist/three-arm-run.d.ts +27 -0
  130. package/dist/three-arm-run.js +41 -0
  131. package/package.json +60 -0
@@ -0,0 +1,62 @@
1
+ import { type Artifact, type SignedEnvelope, type UnsignedEnvelope } from '@jinn-network/core';
2
+ export interface CapturePublishedBlob {
3
+ cid: string;
4
+ sha256?: string;
5
+ endpoint?: string;
6
+ priceUsdc?: string;
7
+ }
8
+ export interface CaptureEnvelopeAnchorInput {
9
+ metadataKey: string;
10
+ envelopeCid: string;
11
+ envelopeHash: `0x${string}`;
12
+ envelope: SignedEnvelope;
13
+ requireSuccessfulReceipt?: boolean;
14
+ onBroadcast?: (txHash: `0x${string}`) => void;
15
+ onPrepared?: (payloadHex: `0x${string}`) => void;
16
+ }
17
+ export interface CaptureEnvelopeAnchorResult {
18
+ txHash?: `0x${string}`;
19
+ blockNumber?: number | null;
20
+ gasUsed?: bigint | null;
21
+ feeWei?: bigint | null;
22
+ payloadHex?: `0x${string}`;
23
+ }
24
+ /**
25
+ * The capture-row facts the layer needs to wrap an episode or skill. This is
26
+ * deliberately narrower than the daemon's persistence row.
27
+ */
28
+ export interface LayerCaptureRow {
29
+ sessionId: string;
30
+ capturedAt: string;
31
+ originatingTool: {
32
+ name: string;
33
+ version?: string;
34
+ };
35
+ capturePath: string;
36
+ spanCount: number;
37
+ redactedSpanCount: number;
38
+ durationMs: number;
39
+ repoRemoteUrl?: string | null;
40
+ repoCommitHash?: string | null;
41
+ }
42
+ export interface BuildUnsignedCaptureEnvelopeArgs {
43
+ capture: LayerCaptureRow;
44
+ now: Date;
45
+ participant: {
46
+ safeAddress: `0x${string}`;
47
+ agentEoa: `0x${string}`;
48
+ };
49
+ signerAddress: `0x${string}`;
50
+ clientGitSha: string;
51
+ artifacts: Artifact[];
52
+ harnessBundleSha: string;
53
+ executorOverrides?: Partial<UnsignedEnvelope['executor']>;
54
+ solverType?: string;
55
+ role?: UnsignedEnvelope['role'];
56
+ }
57
+ /**
58
+ * Build the byte-compatible `jinn.execution.v1` wrapper previously supplied
59
+ * by the client capture publisher.
60
+ */
61
+ export declare function buildUnsignedCaptureEnvelope(args: BuildUnsignedCaptureEnvelopeArgs): UnsignedEnvelope;
62
+ export declare function sha256Hex(input: string | Uint8Array): string;
@@ -0,0 +1,72 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { UnsignedEnvelopeSchema, canonicalJson, } from '@jinn-network/core';
3
+ /**
4
+ * Build the byte-compatible `jinn.execution.v1` wrapper previously supplied
5
+ * by the client capture publisher.
6
+ */
7
+ export function buildUnsignedCaptureEnvelope(args) {
8
+ const { capture, now, participant, signerAddress, artifacts, harnessBundleSha, } = args;
9
+ const sessionProvenance = {
10
+ sessionId: capture.sessionId,
11
+ capturedAt: capture.capturedAt,
12
+ originatingTool: capture.originatingTool,
13
+ ...(capture.repoRemoteUrl || capture.repoCommitHash
14
+ ? {
15
+ repo: {
16
+ ...(capture.repoRemoteUrl
17
+ ? { remoteUrl: capture.repoRemoteUrl }
18
+ : {}),
19
+ ...(capture.repoCommitHash
20
+ ? { commitHash: capture.repoCommitHash }
21
+ : {}),
22
+ },
23
+ }
24
+ : {}),
25
+ license: { operatorAssertion: 'unspecified' },
26
+ };
27
+ const windowEnd = Math.floor(now.getTime() / 1000);
28
+ const runtimeBundleDigest = `sha256:${sha256Hex(canonicalJson({
29
+ tool: capture.originatingTool,
30
+ capturePath: capture.capturePath,
31
+ }))}`;
32
+ const executor = {
33
+ implName: capture.originatingTool.name,
34
+ implVersion: capture.originatingTool.version ?? 'unknown',
35
+ clientGitSha: args.clientGitSha,
36
+ codeDigest: `sha256:${harnessBundleSha}`,
37
+ runtimeBundleDigest,
38
+ plugins: [],
39
+ signingKey: { kind: 'agent-eoa', pubkey: signerAddress },
40
+ mode: 'train',
41
+ ...args.executorOverrides,
42
+ };
43
+ return UnsignedEnvelopeSchema.parse({
44
+ schemaVersion: 'jinn.execution.v1',
45
+ solverType: args.solverType ?? 'capture',
46
+ role: args.role ?? 'capture',
47
+ generatedAt: windowEnd,
48
+ sessionProvenance,
49
+ participant,
50
+ window: {
51
+ startTs: Math.max(0, windowEnd - Math.ceil(capture.durationMs / 1000)),
52
+ endTs: windowEnd,
53
+ },
54
+ executor,
55
+ evidenceTier: 'self-signed',
56
+ attestation: null,
57
+ trajectory: null,
58
+ artifacts,
59
+ payload: {
60
+ capture: {
61
+ sessionId: capture.sessionId,
62
+ capturePath: capture.capturePath,
63
+ spanCount: capture.spanCount,
64
+ redactedSpanCount: capture.redactedSpanCount,
65
+ durationMs: capture.durationMs,
66
+ },
67
+ },
68
+ });
69
+ }
70
+ export function sha256Hex(input) {
71
+ return createHash('sha256').update(input).digest('hex');
72
+ }
package/dist/gate.d.ts ADDED
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The promotion gate — which layer-1 evidence is eligible to distill
3
+ * (spec/2026-07-06-distillation-v1.md §6, D3/D10).
4
+ *
5
+ * Tiered eligibility: a `completed` + `evaluator-verified` trace is
6
+ * **pattern-eligible** (→ strategic pattern); a `failed` + `evaluator-verified`
7
+ * trace is **lesson-eligible** (→ failure lesson). Both require
8
+ * raw-evidence provenance (contributed or derived), not-held-out, and a
9
+ * passing redaction-health check. Noise (`INVALID`/`INDETERMINATE`/
10
+ * `abandoned`, `user-accepted` failures) is not distillable — the bridge (§8)
11
+ * already drops non-definitive verdicts, and this gate is the second,
12
+ * evidence-level guard.
13
+ *
14
+ * The redaction-health guard (the #1409 defence, corrected per the adversarial
15
+ * review): measure **intra-value placeholder density** (placeholder chars ÷
16
+ * content chars) over the **union of every stage's placeholder shape**, and only
17
+ * on values long enough for shredding to be the harm — a short fully-redacted
18
+ * value is a legitimate single-secret redaction, not prose-mangling.
19
+ */
20
+ import type { TraceEnvelopeV0 } from './envelope.js';
21
+ export type EligibilityTier = 'pattern' | 'lesson';
22
+ export interface RedactionHealth {
23
+ /** True when some long content value is mostly placeholder tokens. */
24
+ defaced: boolean;
25
+ /** The worst intra-value placeholder density seen (over length-qualifying values). */
26
+ maxValueDensity: number;
27
+ }
28
+ export interface EligibilityResult {
29
+ eligible: boolean;
30
+ tier: EligibilityTier | null;
31
+ reasons: string[];
32
+ redactionHealth: RedactionHealth;
33
+ }
34
+ export declare function redactionHealth(env: TraceEnvelopeV0, maxDensity?: number): RedactionHealth;
35
+ export declare function evaluateEligibility(env: TraceEnvelopeV0, opts?: {
36
+ heldOut?: boolean;
37
+ maxPlaceholderDensity?: number;
38
+ acceptUserAccepted?: boolean;
39
+ }): EligibilityResult;
package/dist/gate.js ADDED
@@ -0,0 +1,106 @@
1
+ /**
2
+ * The promotion gate — which layer-1 evidence is eligible to distill
3
+ * (spec/2026-07-06-distillation-v1.md §6, D3/D10).
4
+ *
5
+ * Tiered eligibility: a `completed` + `evaluator-verified` trace is
6
+ * **pattern-eligible** (→ strategic pattern); a `failed` + `evaluator-verified`
7
+ * trace is **lesson-eligible** (→ failure lesson). Both require
8
+ * raw-evidence provenance (contributed or derived), not-held-out, and a
9
+ * passing redaction-health check. Noise (`INVALID`/`INDETERMINATE`/
10
+ * `abandoned`, `user-accepted` failures) is not distillable — the bridge (§8)
11
+ * already drops non-definitive verdicts, and this gate is the second,
12
+ * evidence-level guard.
13
+ *
14
+ * The redaction-health guard (the #1409 defence, corrected per the adversarial
15
+ * review): measure **intra-value placeholder density** (placeholder chars ÷
16
+ * content chars) over the **union of every stage's placeholder shape**, and only
17
+ * on values long enough for shredding to be the harm — a short fully-redacted
18
+ * value is a legitimate single-secret redaction, not prose-mangling.
19
+ */
20
+ /**
21
+ * Union of the placeholder shapes the layer-1 pipeline emits (§6, §10).
22
+ * Module-level `const` with the `g` flag: safe here because `String.match`
23
+ * (used below, not `RegExp.exec` in a loop) ignores/resets `lastIndex` on each
24
+ * call — do not switch to `.exec()` without resetting `lastIndex`.
25
+ */
26
+ const PLACEHOLDER_RE = /\[SECRET:[^\]]*\]|\[PII:[^\]]*\]|\[EMAIL\]|\[[A-Z]+_\d+\]/g;
27
+ /** Below this length a fully-redacted value is a legit single-secret redaction, not shredding. */
28
+ const MIN_CONTENT_LEN = 64;
29
+ const DEFAULT_MAX_DENSITY = 0.4;
30
+ /**
31
+ * Content-bearing string values to check for defacement: every step attribute
32
+ * PLUS the scrubbed prose surfaces `task.summary` and `outcome.summary`. The
33
+ * summaries are exactly the fields the distiller reads first (§7) and the ones
34
+ * #1409 defaced, so guarding only step attributes would leave the module's main
35
+ * target uncovered.
36
+ *
37
+ * NOTE (v1 scope): only TOP-LEVEL string values are measured. `attributes` is
38
+ * `z.record(z.unknown())`, so a value may be a nested object/array — those are
39
+ * skipped. v1 bridged evidence carries flat string attributes (a patch, a
40
+ * verdict), so this is sufficient today; recursing into nested values is a
41
+ * follow-up if a harness ever emits structured attributes.
42
+ *
43
+ * NOTE (deliberate deviation from §6): §6 prefers driving the guard off the
44
+ * recorded redaction receipt (`redactedKeys`/`truncatedKeys`). We instead scan
45
+ * the union of placeholder SHAPES across all content-bearing values, because
46
+ * `task.summary`/`outcome.summary` have no per-key receipt and a uniform scan
47
+ * covers them. The trade-off is brittleness: a NEW placeholder shape must be
48
+ * added to PLACEHOLDER_RE or it is invisible to the density metric.
49
+ */
50
+ function contentBearingValues(env) {
51
+ const out = [];
52
+ for (const step of env.steps) {
53
+ for (const value of Object.values(step.attributes)) {
54
+ if (typeof value === 'string')
55
+ out.push(value);
56
+ }
57
+ }
58
+ if (typeof env.task.summary === 'string')
59
+ out.push(env.task.summary);
60
+ if (typeof env.outcome.summary === 'string')
61
+ out.push(env.outcome.summary);
62
+ return out;
63
+ }
64
+ export function redactionHealth(env, maxDensity = DEFAULT_MAX_DENSITY) {
65
+ let worst = 0;
66
+ for (const value of contentBearingValues(env)) {
67
+ if (value.length < MIN_CONTENT_LEN)
68
+ continue;
69
+ const placeholderChars = (value.match(PLACEHOLDER_RE) ?? []).reduce((n, m) => n + m.length, 0);
70
+ const density = placeholderChars / value.length;
71
+ if (density > worst)
72
+ worst = density;
73
+ }
74
+ return { defaced: worst > maxDensity, maxValueDensity: worst };
75
+ }
76
+ export function evaluateEligibility(env, opts = {}) {
77
+ const maxDensity = opts.maxPlaceholderDensity ?? DEFAULT_MAX_DENSITY;
78
+ const reasons = [];
79
+ const rh = redactionHealth(env, maxDensity);
80
+ if (env.provenance === 'imported') {
81
+ reasons.push('imported seeds are already-distilled layer-2, not raw evidence');
82
+ }
83
+ if (opts.heldOut) {
84
+ reasons.push('in the held-out cap-v0 slate');
85
+ }
86
+ if (rh.defaced) {
87
+ reasons.push(`defaced: placeholder density ${rh.maxValueDensity.toFixed(2)} exceeds ${maxDensity}`);
88
+ }
89
+ let tier = null;
90
+ const evaluatorVerified = env.outcome.verifiabilityTier === 'evaluator-verified';
91
+ const userAccepted = opts.acceptUserAccepted === true && env.outcome.verifiabilityTier === 'user-accepted';
92
+ const acceptedTier = evaluatorVerified || userAccepted;
93
+ if (env.outcome.status === 'completed' && acceptedTier) {
94
+ tier = 'pattern';
95
+ }
96
+ else if (env.outcome.status === 'failed' && acceptedTier) {
97
+ tier = 'lesson';
98
+ }
99
+ else {
100
+ reasons.push(`outcome not distillable (status=${env.outcome.status}, tier=${env.outcome.verifiabilityTier}; ` +
101
+ `need ${opts.acceptUserAccepted ? 'evaluator-verified or user-accepted' : 'evaluator-verified'} ` +
102
+ 'completed→pattern or failed→lesson)');
103
+ }
104
+ const eligible = reasons.length === 0 && tier !== null;
105
+ return { eligible, tier: eligible ? tier : null, reasons, redactionHealth: rh };
106
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * @jinn-network/jinn-layer — standalone plugin process and embeddable layer.
3
+ *
4
+ * v0 exposes the corpus consume path (search/get), the frozen layer-1 trace
5
+ * envelope read-compat schema, the capture path (scrub + preview), and the publish path
6
+ * (consent conversion + anchor + contribution ledger).
7
+ */
8
+ export { createHarnessLayer, DEFAULT_IPFS_GATEWAY_URL, type HarnessLayer, type HarnessLayerConfig, type ResolvedHarnessLayerConfig, type CorpusSearchHit, type CorpusRecord, type CorpusArtifact, } from './consume.js';
9
+ export { corpusProbes, enoughCorpusForRepo, CORPUS_ONBOARDING_K, type DoctorCheck, } from './corpus-probes.js';
10
+ export { capture, parseCapturedTask, CapturedTaskSchema, CaptureScrubError, PENDING_ENVELOPE_KIND, type CapturedTask, type CaptureOptions, type PendingEnvelope, type ScrubRedaction, } from './capture.js';
11
+ export { preview, stripBeforeValues, type ScrubReport, } from './preview.js';
12
+ export { computeSignal, type SignalInput, type SignalRow, type SignalOptions, } from './signal.js';
13
+ export { publish, PublishLedgerError, toPublishedEpisode, toTraceEnvelope, EPISODE_ARTIFACT_TYPE, TRACE_ENVELOPE_ARTIFACT_TYPE, type HarnessPublishDeps, type PublishedResult, type PublishOptions, type PublishResult, } from './publish.js';
14
+ export { extractSkill, type ExtractedSkill, } from './skill.js';
15
+ export { SKILL_ARTIFACT_TYPE, SkillArtifactV1Schema, SkillProvenanceSchema, SkillCompanionFileSchema, MAX_SKILL_FILES, MAX_SKILL_TOTAL_DECODED_BYTES, type SkillArtifactV1, type SkillProvenance, type SkillCompanionFile, } from '@jinn-network/core';
16
+ export { buildSkillMarkdown, parseSkillMarkdown, assertConformantName, SkillPackageMetaSchema, type SkillPackage, type SkillPackageMeta, } from './skill-package.js';
17
+ export { publishSkill, toSkillArtifactV1, SKILL_SOLVER_TYPE, type SkillPublishDeps, type PublishSkillResult, type SkillLifecycle, } from './publish-skill.js';
18
+ export { bridgeAttempts, toBridgeCapturedTask, repoFromInstanceId, buildBridgeEvidencePublisher, type AttemptRef, type BridgeEvidence, type BridgeDeps, type BridgeResult, } from './bridge.js';
19
+ export { evaluateEligibility, redactionHealth, type EligibilityTier, type EligibilityResult, type RedactionHealth, } from './gate.js';
20
+ export { distillClusters, lexicalContaminationScan, type DistillCluster, type DistillLLMOutput, type DistillDeps, type DistillResult, } from './distill.js';
21
+ export { createLocalDistiller, createNetworkDistiller, createLocalSkillSink, NetworkDistillerNotWiredError, type Distiller, type DistillerResult, type LocalDistillerDeps, type SkillSink, type HeldOutSlate, } from './distiller.js';
22
+ export { createClaudeDistiller, createClaudeMetaDistiller, createCodexDistiller, createCodexMetaDistiller, DEFAULT_MODEL as DEFAULT_CLAUDE_DISTILL_MODEL, DEFAULT_CODEX_MODEL, DISTILLER_CATALOG, } from './distill-llm.js';
23
+ export { readDistillMode, writeDistillMode, DEFAULT_DISTILL_MODE_PATH, type DistillMode, type DistillModeState, } from './distill-mode.js';
24
+ export { DEFAULT_CAPTURES_DIR, DEFAULT_DISTILL_CAPTURE_LIMIT, DEFAULT_EPISODES_DIR, DEFAULT_SKILLS_INSTALL_DIR, coveredSessionIds, episodeToCapturedTask, localSkillProvenance, loadRecentCaptures, loadRecentDistillSources, provenanceLabels, stagingDirFor, type DistillSourceOptions, } from './distill-captures.js';
25
+ export { clusterTraceCards, readTrace, searchTraceCards, traceCardFromCapture, type EvidenceTierEstimate, type EstimatedDistillCost, type LocalTraceCard, type TraceCandidate, type TraceEventView, type TraceReadMode, type TraceReadResult, type TraceSearchQuery, } from './distill-traces.js';
26
+ export { createDistillMcpServer, runLocalDistill, type DistillMcpDeps, type LocalDistillRunArgs, } from './distill-mcp-server.js';
27
+ export { DEFAULT_DISTILL_FEEDBACK_PATH, distillFeedbackPathFromEnv, readDistillFeedback, recordDistillFeedback, type DistillFeedbackInput, type DistillFeedbackRecord, type DistillFeedbackVerdict, } from './distill-feedback.js';
28
+ export { threeArmMeasurement, pilotPower, type ArmResult, type ArmComparison, type PilotPower, type ThreeArmResult, type ShipVerdict, } from './measurement.js';
29
+ export { createFileLedger, createMemoryLedger, ledger, toLedgerRow, LedgerEntrySchema, LedgerRowSchema, DEFAULT_LEDGER_PATH, type LedgerEntry, type LedgerRow, type LedgerStore, } from './ledger.js';
30
+ export { createGithubSeedSource, parseSeedListEntry, SeedSkillSchema, type SeedSkill, type SeedSource, type SeedListEntry, } from './seed-import/fetch.js';
31
+ export { checkLicence, IMPORT_LICENCE_ALLOWLIST, type LicenceVerdict } from './seed-import/licence.js';
32
+ export { plan } from './seed-import/plan.js';
33
+ export { execute, skillSlug, type ImportResult } from './seed-import/execute.js';
34
+ export { parseImportReport, renderImportReport, ImportReportSchema, type ImportReport, type ImportReportRow, } from './seed-import/report.js';
35
+ export { createLocalEpisodeSeedSource, episodeContentDigest, parseSeedEpisode, SeedEpisodeSchema, SEED_EPISODE_EXCERPT_LABELS, type SeedEpisode, type SeedEpisodeStep, type SeedEpisodeExcerptLabel, type EpisodeSource, } from './seed-import/episode-fetch.js';
36
+ export { planEpisodes } from './seed-import/episode-plan.js';
37
+ export { executeEpisodes, type EpisodeImportResult } from './seed-import/episode-execute.js';
38
+ export { parseEpisodeImportReport, renderEpisodeImportReport, EpisodeImportReportSchema, type EpisodeImportReport, type EpisodeImportReportRow, } from './seed-import/episode-report.js';
39
+ export { createMemorySeedImportState, createFileSeedImportState, hashSeedContent, DEFAULT_SEED_IMPORT_STATE_PATH, type SeedImportStateStore, type SeedPublicationRecord, type FileSeedImportStateOptions, } from './seed-import/state.js';
40
+ export { TraceEnvelopeV0Schema, TraceStepSchema, VerifiabilityTierSchema, OutcomeStatusSchema, parseTraceEnvelopeV0, TRACE_ENVELOPE_SCHEMA_VERSION, MAX_STEPS, MAX_STEP_ATTRIBUTES_BYTES, MAX_DISTRIBUTION_TAGS, VERIFIABILITY_TIERS, type TraceEnvelopeV0, type TraceStep, type VerifiabilityTier, type OutcomeStatus, } from './envelope.js';
41
+ export { CONTRIBUTION_STORE_FILE, CONTRIBUTION_STORE_SCHEMA_VERSION, CONTRIBUTION_PUBLICATION_SCHEMA_VERSION, CONTRIBUTION_PUBLICATION_DISABLED_FILE, DEFAULT_CONTRIBUTION_STATE_DIR, ContributionStore, resolveContributionStateDir, type ContributionLocalMetadata, type ContributionPublicationV1, type ContributionStoreLockOptions, type ContributionStoreOptions, type ContributionStoreRecordOptions, type ContributionStoreReferenceOptions, type ContributionStoreRecord, } from '@jinn-network/core';
42
+ export { PROCESS_CONTRACT_VERSION, SessionEndRequestV1Schema, SessionPickupRequestV1Schema, envelope as processEnvelope, sessionEndEnvelope, type ProcessEnvelope, type ProcessStatus, type SessionEndRequestV1, type SessionPickupEnvelope, type SessionPickupRequestV1, contributionLedgerRow, type ContributionLedgerRowV1, } from './process-contract.js';
43
+ export { resolveRetrievalMark, HAND_MARK_POLICY, type AdmissionPolicy, type AdmissionFacts, } from './admission-policy.js';
44
+ export { runJinnLayerCli, createBoundedIpfsJsonFetcher, type RunJinnLayerCliOptions, type DistillRunCliDeps, type DistillCliDeps, type DistillProvider, type JinnLayerWriter, } from './cli.js';
45
+ export { buildPluginDepsFromEnv, buildPluginFromEnv, } from './plugin-wiring.js';
46
+ export { createContributionAdapter, createContributionStatusStore, createCorpusAdapter, createEvidenceAdapter, createLocalLearningAdapter, createSkillsAdapter, } from './adapters/index.js';
47
+ export { SqliteCorpusStore } from './corpus-store.js';
48
+ export { extractSnapshotTranscript, findSolveTranscript, untarGz, unwrapDonation, MAX_SNAPSHOT_DECOMPRESSED_BYTES, type SolveTranscript, type TarEntry, } from './snapshot-transcript.js';
49
+ export { auditCuratedSeedBatch, CURATED_SEED_AUDIT_SCHEMA_VERSION, type AuditCuratedSeedBatchOptions, type CuratedSeedBatchAudit, type CuratedSeedRecordAudit, } from './seed-import/curated-batch.js';
package/dist/index.js ADDED
@@ -0,0 +1,50 @@
1
+ /**
2
+ * @jinn-network/jinn-layer — standalone plugin process and embeddable layer.
3
+ *
4
+ * v0 exposes the corpus consume path (search/get), the frozen layer-1 trace
5
+ * envelope read-compat schema, the capture path (scrub + preview), and the publish path
6
+ * (consent conversion + anchor + contribution ledger).
7
+ */
8
+ export { createHarnessLayer, DEFAULT_IPFS_GATEWAY_URL, } from './consume.js';
9
+ export { corpusProbes, enoughCorpusForRepo, CORPUS_ONBOARDING_K, } from './corpus-probes.js';
10
+ export { capture, parseCapturedTask, CapturedTaskSchema, CaptureScrubError, PENDING_ENVELOPE_KIND, } from './capture.js';
11
+ export { preview, stripBeforeValues, } from './preview.js';
12
+ export { computeSignal, } from './signal.js';
13
+ export { publish, PublishLedgerError, toPublishedEpisode, toTraceEnvelope, EPISODE_ARTIFACT_TYPE, TRACE_ENVELOPE_ARTIFACT_TYPE, } from './publish.js';
14
+ export { extractSkill, } from './skill.js';
15
+ export { SKILL_ARTIFACT_TYPE, SkillArtifactV1Schema, SkillProvenanceSchema, SkillCompanionFileSchema, MAX_SKILL_FILES, MAX_SKILL_TOTAL_DECODED_BYTES, } from '@jinn-network/core';
16
+ export { buildSkillMarkdown, parseSkillMarkdown, assertConformantName, SkillPackageMetaSchema, } from './skill-package.js';
17
+ export { publishSkill, toSkillArtifactV1, SKILL_SOLVER_TYPE, } from './publish-skill.js';
18
+ export { bridgeAttempts, toBridgeCapturedTask, repoFromInstanceId, buildBridgeEvidencePublisher, } from './bridge.js';
19
+ export { evaluateEligibility, redactionHealth, } from './gate.js';
20
+ export { distillClusters, lexicalContaminationScan, } from './distill.js';
21
+ export { createLocalDistiller, createNetworkDistiller, createLocalSkillSink, NetworkDistillerNotWiredError, } from './distiller.js';
22
+ export { createClaudeDistiller, createClaudeMetaDistiller, createCodexDistiller, createCodexMetaDistiller, DEFAULT_MODEL as DEFAULT_CLAUDE_DISTILL_MODEL, DEFAULT_CODEX_MODEL, DISTILLER_CATALOG, } from './distill-llm.js';
23
+ export { readDistillMode, writeDistillMode, DEFAULT_DISTILL_MODE_PATH, } from './distill-mode.js';
24
+ export { DEFAULT_CAPTURES_DIR, DEFAULT_DISTILL_CAPTURE_LIMIT, DEFAULT_EPISODES_DIR, DEFAULT_SKILLS_INSTALL_DIR, coveredSessionIds, episodeToCapturedTask, localSkillProvenance, loadRecentCaptures, loadRecentDistillSources, provenanceLabels, stagingDirFor, } from './distill-captures.js';
25
+ export { clusterTraceCards, readTrace, searchTraceCards, traceCardFromCapture, } from './distill-traces.js';
26
+ export { createDistillMcpServer, runLocalDistill, } from './distill-mcp-server.js';
27
+ export { DEFAULT_DISTILL_FEEDBACK_PATH, distillFeedbackPathFromEnv, readDistillFeedback, recordDistillFeedback, } from './distill-feedback.js';
28
+ export { threeArmMeasurement, pilotPower, } from './measurement.js';
29
+ export { createFileLedger, createMemoryLedger, ledger, toLedgerRow, LedgerEntrySchema, LedgerRowSchema, DEFAULT_LEDGER_PATH, } from './ledger.js';
30
+ export { createGithubSeedSource, parseSeedListEntry, SeedSkillSchema, } from './seed-import/fetch.js';
31
+ export { checkLicence, IMPORT_LICENCE_ALLOWLIST } from './seed-import/licence.js';
32
+ export { plan } from './seed-import/plan.js';
33
+ export { execute, skillSlug } from './seed-import/execute.js';
34
+ export { parseImportReport, renderImportReport, ImportReportSchema, } from './seed-import/report.js';
35
+ // Evidence-episode seed lane (issue #1771).
36
+ export { createLocalEpisodeSeedSource, episodeContentDigest, parseSeedEpisode, SeedEpisodeSchema, SEED_EPISODE_EXCERPT_LABELS, } from './seed-import/episode-fetch.js';
37
+ export { planEpisodes } from './seed-import/episode-plan.js';
38
+ export { executeEpisodes } from './seed-import/episode-execute.js';
39
+ export { parseEpisodeImportReport, renderEpisodeImportReport, EpisodeImportReportSchema, } from './seed-import/episode-report.js';
40
+ export { createMemorySeedImportState, createFileSeedImportState, hashSeedContent, DEFAULT_SEED_IMPORT_STATE_PATH, } from './seed-import/state.js';
41
+ export { TraceEnvelopeV0Schema, TraceStepSchema, VerifiabilityTierSchema, OutcomeStatusSchema, parseTraceEnvelopeV0, TRACE_ENVELOPE_SCHEMA_VERSION, MAX_STEPS, MAX_STEP_ATTRIBUTES_BYTES, MAX_DISTRIBUTION_TAGS, VERIFIABILITY_TIERS, } from './envelope.js';
42
+ export { CONTRIBUTION_STORE_FILE, CONTRIBUTION_STORE_SCHEMA_VERSION, CONTRIBUTION_PUBLICATION_SCHEMA_VERSION, CONTRIBUTION_PUBLICATION_DISABLED_FILE, DEFAULT_CONTRIBUTION_STATE_DIR, ContributionStore, resolveContributionStateDir, } from '@jinn-network/core';
43
+ export { PROCESS_CONTRACT_VERSION, SessionEndRequestV1Schema, SessionPickupRequestV1Schema, envelope as processEnvelope, sessionEndEnvelope, contributionLedgerRow, } from './process-contract.js';
44
+ export { resolveRetrievalMark, HAND_MARK_POLICY, } from './admission-policy.js';
45
+ export { runJinnLayerCli, createBoundedIpfsJsonFetcher, } from './cli.js';
46
+ export { buildPluginDepsFromEnv, buildPluginFromEnv, } from './plugin-wiring.js';
47
+ export { createContributionAdapter, createContributionStatusStore, createCorpusAdapter, createEvidenceAdapter, createLocalLearningAdapter, createSkillsAdapter, } from './adapters/index.js';
48
+ export { SqliteCorpusStore } from './corpus-store.js';
49
+ export { extractSnapshotTranscript, findSolveTranscript, untarGz, unwrapDonation, MAX_SNAPSHOT_DECOMPRESSED_BYTES, } from './snapshot-transcript.js';
50
+ export { auditCuratedSeedBatch, CURATED_SEED_AUDIT_SCHEMA_VERSION, } from './seed-import/curated-batch.js';
@@ -0,0 +1,18 @@
1
+ /** Kubo's default fixed chunk size and largest single raw-leaf block. */
2
+ export declare const MAX_RAW_IPFS_BLOCK_BYTES: number;
3
+ export declare const IPFS_RAW_CODEC = 85;
4
+ export declare const IPFS_DAG_PB_CODEC = 112;
5
+ /** Deterministic CIDv1 base16 identity for one exact raw sha2-256 block. */
6
+ export declare function rawSha256Cid(bodyBytes: Uint8Array): string;
7
+ export interface ParsedIpfsCid {
8
+ version: 0 | 1;
9
+ codec: typeof IPFS_RAW_CODEC | typeof IPFS_DAG_PB_CODEC;
10
+ sha256Digest: Uint8Array;
11
+ }
12
+ export declare function parseIpfsCid(value: string): ParsedIpfsCid | null;
13
+ export declare function isIpfsCid(value: string): boolean;
14
+ /**
15
+ * Assert the manifest boundary's strict address/content invariant before an
16
+ * irreversible anchor: canonical CIDv1 raw + sha2-256 over the exact bytes.
17
+ */
18
+ export declare function assertRawSha256CidMatches(cid: string, bodyBytes: Uint8Array): void;
@@ -0,0 +1,149 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import { createHash } from 'node:crypto';
3
+ /**
4
+ * CID validation for harness-layer network boundaries. Keep this package-local:
5
+ * the harness-layer ↔ client/src architecture seam is shrink-only.
6
+ *
7
+ * The live Autonolas registry emits CIDv1 raw objects, including the historical
8
+ * base16 form `f01551220<sha256>`. We deliberately support only the two codecs
9
+ * used by the Jinn/IPFS paths (raw and dag-pb) and only sha2-256 multihashes.
10
+ */
11
+ const BASE58BTC = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
12
+ const BASE32 = 'abcdefghijklmnopqrstuvwxyz234567';
13
+ /** Kubo's default fixed chunk size and largest single raw-leaf block. */
14
+ export const MAX_RAW_IPFS_BLOCK_BYTES = 256 * 1024;
15
+ export const IPFS_RAW_CODEC = 0x55;
16
+ export const IPFS_DAG_PB_CODEC = 0x70;
17
+ /** Deterministic CIDv1 base16 identity for one exact raw sha2-256 block. */
18
+ export function rawSha256Cid(bodyBytes) {
19
+ const digest = createHash('sha256').update(bodyBytes).digest('hex');
20
+ return `f01551220${digest}`;
21
+ }
22
+ export function parseIpfsCid(value) {
23
+ if (value.startsWith('Qm')) {
24
+ const bytes = decodeBase58(value);
25
+ // CIDv0 is specifically a dag-pb sha2-256 multihash.
26
+ if (bytes?.length !== 34
27
+ || bytes[0] !== 0x12
28
+ || bytes[1] !== 0x20) {
29
+ return null;
30
+ }
31
+ return {
32
+ version: 0,
33
+ codec: IPFS_DAG_PB_CODEC,
34
+ sha256Digest: bytes.slice(2),
35
+ };
36
+ }
37
+ let bytes;
38
+ if (value.startsWith('b')) {
39
+ // The supported 36-byte CIDv1 shape has one canonical 58-symbol base32
40
+ // encoding. Reject zero-extended textual aliases before decoding so CID
41
+ // strings remain safe cache/deduplication and metadata-key identities.
42
+ if (value.length !== 59)
43
+ return null;
44
+ bytes = decodeBase32(value.slice(1));
45
+ }
46
+ else if (value.startsWith('f')) {
47
+ const body = value.slice(1);
48
+ bytes = /^[0-9a-f]+$/u.test(body) ? decodeBase16(body) : null;
49
+ }
50
+ else if (value.startsWith('F')) {
51
+ const body = value.slice(1);
52
+ bytes = /^[0-9A-F]+$/u.test(body) ? decodeBase16(body) : null;
53
+ }
54
+ else {
55
+ return null;
56
+ }
57
+ // Supported CIDv1 values are exactly:
58
+ // version(01) + codec(raw=55|dag-pb=70) + sha2-256(12 20 <32 bytes>).
59
+ if (bytes?.length !== 36
60
+ || bytes[0] !== 0x01
61
+ || (bytes[1] !== IPFS_RAW_CODEC && bytes[1] !== IPFS_DAG_PB_CODEC)
62
+ || bytes[2] !== 0x12
63
+ || bytes[3] !== 0x20) {
64
+ return null;
65
+ }
66
+ return {
67
+ version: 1,
68
+ codec: bytes[1],
69
+ sha256Digest: bytes.slice(4),
70
+ };
71
+ }
72
+ export function isIpfsCid(value) {
73
+ return parseIpfsCid(value) !== null;
74
+ }
75
+ /**
76
+ * Assert the manifest boundary's strict address/content invariant before an
77
+ * irreversible anchor: canonical CIDv1 raw + sha2-256 over the exact bytes.
78
+ */
79
+ export function assertRawSha256CidMatches(cid, bodyBytes) {
80
+ const parsed = parseIpfsCid(cid);
81
+ if (!parsed) {
82
+ throw new Error('manifest CID must be a canonical CIDv1 raw value with a 32-byte sha2-256 multihash');
83
+ }
84
+ if (parsed.version !== 1) {
85
+ throw new Error(`manifest CID must use CIDv1, got version ${parsed.version}`);
86
+ }
87
+ if (parsed.codec !== IPFS_RAW_CODEC) {
88
+ throw new Error(`manifest CID must use the raw codec (0x55), got 0x${parsed.codec.toString(16)}`);
89
+ }
90
+ const expected = createHash('sha256').update(bodyBytes).digest();
91
+ if (!Buffer.from(parsed.sha256Digest).equals(expected)) {
92
+ throw new Error('manifest CID digest does not match the canonical body digest');
93
+ }
94
+ }
95
+ function decodeBase58(value) {
96
+ const bytes = [0];
97
+ for (const char of value) {
98
+ const digit = BASE58BTC.indexOf(char);
99
+ if (digit < 0)
100
+ return null;
101
+ let carry = digit;
102
+ for (let index = bytes.length - 1; index >= 0; index -= 1) {
103
+ const next = bytes[index] * 58 + carry;
104
+ bytes[index] = next & 0xff;
105
+ carry = next >>> 8;
106
+ }
107
+ while (carry > 0) {
108
+ bytes.unshift(carry & 0xff);
109
+ carry >>>= 8;
110
+ }
111
+ }
112
+ for (const char of value) {
113
+ if (char !== '1')
114
+ break;
115
+ bytes.unshift(0);
116
+ }
117
+ return Uint8Array.from(bytes);
118
+ }
119
+ function decodeBase32(value) {
120
+ const bytes = [];
121
+ let accumulator = 0;
122
+ let bits = 0;
123
+ for (const char of value) {
124
+ const digit = BASE32.indexOf(char);
125
+ if (digit < 0)
126
+ return null;
127
+ accumulator = (accumulator << 5) | digit;
128
+ bits += 5;
129
+ while (bits >= 8) {
130
+ bits -= 8;
131
+ bytes.push((accumulator >>> bits) & 0xff);
132
+ }
133
+ }
134
+ if (bits !== 0 && (accumulator & ((1 << bits) - 1)) !== 0)
135
+ return null;
136
+ return Uint8Array.from(bytes);
137
+ }
138
+ function decodeBase16(value) {
139
+ if (value.length === 0 || value.length % 2 !== 0)
140
+ return null;
141
+ const bytes = new Uint8Array(value.length / 2);
142
+ for (let index = 0; index < value.length; index += 2) {
143
+ const byte = Number.parseInt(value.slice(index, index + 2), 16);
144
+ if (!Number.isInteger(byte))
145
+ return null;
146
+ bytes[index / 2] = byte;
147
+ }
148
+ return bytes;
149
+ }
@@ -0,0 +1,3 @@
1
+ import { type HarnessLayer } from './consume.js';
2
+ /** Shared environment-resolved corpus layer for CLI and plugin composition. */
3
+ export declare function buildDefaultLayer(): HarnessLayer;
@@ -0,0 +1,10 @@
1
+ import { createHarnessLayer } from './consume.js';
2
+ /** Shared environment-resolved corpus layer for CLI and plugin composition. */
3
+ export function buildDefaultLayer() {
4
+ return createHarnessLayer({
5
+ ...(process.env['JINN_DISCOVERY_URL'] ? { discoveryUrl: process.env['JINN_DISCOVERY_URL'] } : {}),
6
+ ...(process.env['JINN_IPFS_GATEWAY_URL']
7
+ ? { ipfsGatewayUrl: process.env['JINN_IPFS_GATEWAY_URL'] }
8
+ : {}),
9
+ });
10
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Contribution ledger — the operator's local receipt of what left their
3
+ * machine (plan Task 4, issue #1311).
4
+ *
5
+ * Append-only JSONL at `~/.jinn-client/harness-layer/ledger.jsonl` by
6
+ * default. Entries carry only persistence-safe fields (the task summary is
7
+ * already scrubbed by capture; redaction `before` values never reach here).
8
+ * The Task 5 fork renders this; Task 7 aggregates over it.
9
+ */
10
+ import { z } from 'zod';
11
+ export declare const DEFAULT_LEDGER_PATH: string;
12
+ export declare const LedgerEntrySchema: z.ZodObject<{
13
+ ts: z.ZodISODateTime;
14
+ taskSummary: z.ZodString;
15
+ envelopeRef: z.ZodNullable<z.ZodString>;
16
+ anchorTx: z.ZodNullable<z.ZodString>;
17
+ verifiabilityTier: z.ZodEnum<{
18
+ "user-accepted": "user-accepted";
19
+ "tests-passed": "tests-passed";
20
+ "evaluator-verified": "evaluator-verified";
21
+ }>;
22
+ status: z.ZodEnum<{
23
+ published: "published";
24
+ "vetoed (local only)": "vetoed (local only)";
25
+ }>;
26
+ }, z.core.$strict>;
27
+ export type LedgerEntry = z.infer<typeof LedgerEntrySchema>;
28
+ /**
29
+ * Fork-shaped projection of a ledger entry — the row shape the TUI consumer
30
+ * (`apps/jinn-agent/plugins/jinn/ledger_view.py`, `rows_from_json`) reads.
31
+ * `state` is absent for published rows; the schema also accepts a `failed`
32
+ * state for forward-compat, even though `toLedgerRow` never emits it.
33
+ */
34
+ export declare const LedgerRowSchema: z.ZodObject<{
35
+ time: z.ZodString;
36
+ task: z.ZodString;
37
+ env: z.ZodNullable<z.ZodString>;
38
+ anchor: z.ZodNullable<z.ZodString>;
39
+ tier: z.ZodEnum<{
40
+ "user-accepted": "user-accepted";
41
+ "tests-passed": "tests-passed";
42
+ "evaluator-verified": "evaluator-verified";
43
+ }>;
44
+ state: z.ZodOptional<z.ZodEnum<{
45
+ failed: "failed";
46
+ vetoed: "vetoed";
47
+ }>>;
48
+ }, z.core.$strict>;
49
+ export type LedgerRow = z.infer<typeof LedgerRowSchema>;
50
+ /** Project an internal ledger entry into the fork-consumed row shape. */
51
+ export declare function toLedgerRow(entry: LedgerEntry): LedgerRow;
52
+ export interface LedgerStore {
53
+ append(entry: LedgerEntry): void;
54
+ /** Entries in append order. */
55
+ list(): LedgerEntry[];
56
+ }
57
+ /** In-memory ledger (tests, embedders that persist elsewhere). */
58
+ export declare function createMemoryLedger(): LedgerStore;
59
+ /** Append-only JSONL file ledger. Creates parent directories on first write. */
60
+ export declare function createFileLedger(path?: string): LedgerStore;
61
+ /** Read the ledger (plan Task 4's `ledger()` surface). Missing file ⇒ empty. */
62
+ export declare function ledger(path?: string): LedgerEntry[];