@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,129 @@
1
+ import type { AttemptRef, BridgeEvidence } from './bridge.js';
2
+ import type { TraceEnvelopeV0 } from './envelope.js';
3
+ import type { HarnessPublishDeps } from './publish.js';
4
+ import { type ClusterSelectionOptions, type ClusterScore } from './cluster-selection.js';
5
+ import { type MetaCluster } from './cluster.js';
6
+ import { type DistillCluster, type DistillLLMOutput, type DistillResult, type MetaDistillLLMOutput, type MetaDistillResult } from './distill.js';
7
+ export interface EvalPrepModel {
8
+ label: string;
9
+ model: string;
10
+ distill: (cluster: DistillCluster) => Promise<DistillLLMOutput>;
11
+ metaDistill?: (cluster: MetaCluster) => Promise<MetaDistillLLMOutput>;
12
+ }
13
+ export interface EvalPrepDeps {
14
+ verdictSource: {
15
+ list: (args?: {
16
+ limit?: number;
17
+ }) => Promise<AttemptRef[]>;
18
+ };
19
+ fetchEvidence: (ref: AttemptRef) => Promise<BridgeEvidence>;
20
+ publishDeps: HarnessPublishDeps;
21
+ slate: {
22
+ instanceIds: Set<string>;
23
+ repos?: Set<string>;
24
+ };
25
+ models: EvalPrepModel[];
26
+ outDir: string;
27
+ selection?: ClusterSelectionOptions;
28
+ selectOnly?: boolean;
29
+ meta?: boolean;
30
+ force?: boolean;
31
+ retryErrors?: boolean;
32
+ distribution?: string;
33
+ groupCap?: number;
34
+ concurrency?: number;
35
+ limit?: number;
36
+ now?: () => Date;
37
+ }
38
+ export interface FrozenClusterSelection {
39
+ clusterId: string;
40
+ tier: DistillCluster['tier'];
41
+ evidenceRefs: string[];
42
+ instanceIds: string[];
43
+ score: ClusterScore;
44
+ }
45
+ export interface EvalPrepModelManifest {
46
+ label: string;
47
+ model: string;
48
+ attemptedClusterIds: string[];
49
+ published: DistillResult['published'];
50
+ rejected: DistillResult['rejected'];
51
+ errors: DistillResult['errors'];
52
+ metaClusterIds?: string[];
53
+ metaDistilled?: MetaDistillResult;
54
+ }
55
+ export interface EvalPrepManifest {
56
+ schema: 'jinn.distill.eval-prep.v1';
57
+ generatedAt: string;
58
+ mode: 'local-only';
59
+ outDir: string;
60
+ limit?: number;
61
+ groupCap?: number;
62
+ concurrency?: number;
63
+ selectOnly: boolean;
64
+ meta: boolean;
65
+ clusterCount: number;
66
+ selectedClusterIds: string[];
67
+ models: Array<{
68
+ label: string;
69
+ model: string;
70
+ attemptedClusterIds: string[];
71
+ published: number;
72
+ rejected: number;
73
+ errors: number;
74
+ metaClusterIds?: string[];
75
+ metaPublished?: number;
76
+ metaRejected?: number;
77
+ metaErrors?: number;
78
+ }>;
79
+ bridge: {
80
+ bridged: number;
81
+ excludedHeldOut: number;
82
+ deduped: number;
83
+ errors: number;
84
+ verdictsTruncated: boolean;
85
+ };
86
+ }
87
+ export interface EvalPrepResult {
88
+ manifest: EvalPrepManifest;
89
+ selection: FrozenClusterSelection[];
90
+ models: EvalPrepModelManifest[];
91
+ }
92
+ export interface RawEvidenceRow {
93
+ ref: string;
94
+ instanceId: string;
95
+ env: TraceEnvelopeV0;
96
+ }
97
+ export type AttemptStatus = 'published' | 'rejected' | 'error';
98
+ export interface Stage1AttemptRecord {
99
+ schema: 'jinn.distill.eval-attempt.v1';
100
+ label: string;
101
+ model: string;
102
+ clusterId: string;
103
+ status: AttemptStatus;
104
+ completedAt?: string;
105
+ published?: DistillResult['published'][number];
106
+ rejected?: DistillResult['rejected'][number];
107
+ error?: DistillResult['errors'][number];
108
+ }
109
+ export interface MetaAttemptRecord {
110
+ schema: 'jinn.distill.eval-meta-attempt.v1';
111
+ label: string;
112
+ model: string;
113
+ metaClusterId: string;
114
+ sourceSignature: string;
115
+ status: AttemptStatus;
116
+ completedAt?: string;
117
+ published?: MetaDistillResult['published'][number];
118
+ rejected?: MetaDistillResult['rejected'][number];
119
+ error?: MetaDistillResult['errors'][number];
120
+ }
121
+ export declare function modelLabel(model: string): string;
122
+ export declare function assertAttemptedClusterIds(selectedClusterIds: string[], attemptedClusterIds: string[]): void;
123
+ export declare function attemptRecordFileName(id: string): string;
124
+ export declare function metaSourceSignature(cluster: MetaCluster): string;
125
+ export declare function reconstructSelectedClusters(frozen: FrozenClusterSelection[], rawRows: RawEvidenceRow[], slateInstanceIds: Set<string>): DistillCluster[];
126
+ export declare function rebuildModelManifestFromAttemptRecords(label: string, model: string, selectedClusterIds: string[], stage1Records: Stage1AttemptRecord[], metaRecords: MetaAttemptRecord[], opts?: {
127
+ metaClusterIds?: string[];
128
+ }): EvalPrepModelManifest;
129
+ export declare function runEvalPrep(deps: EvalPrepDeps): Promise<EvalPrepResult>;
@@ -0,0 +1,523 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { prepareDistillationEvidence } from './pipeline.js';
5
+ import { resolveClusterSelectionCaps, selectUsefulClusters, } from './cluster-selection.js';
6
+ import { buildMetaClusters, clusterEvidence, } from './cluster.js';
7
+ import { distillClusters, metaDistill, } from './distill.js';
8
+ import { buildSkillMarkdown } from './skill-package.js';
9
+ export function modelLabel(model) {
10
+ if (model === 'gpt-5.4-mini')
11
+ return 'mini';
12
+ if (model.includes('haiku'))
13
+ return 'haiku';
14
+ if (model.includes('opus'))
15
+ return 'opus';
16
+ const label = model.toLowerCase().replace(/[^a-z0-9._-]+/g, '-').replace(/^-+|-+$/g, '');
17
+ return label === '' ? 'model' : label;
18
+ }
19
+ export function assertAttemptedClusterIds(selectedClusterIds, attemptedClusterIds) {
20
+ if (selectedClusterIds.length !== attemptedClusterIds.length ||
21
+ selectedClusterIds.some((id, i) => id !== attemptedClusterIds[i])) {
22
+ throw new Error(`eval-prep invariant failed: attempted cluster ids ${JSON.stringify(attemptedClusterIds)} did not match frozen selection ${JSON.stringify(selectedClusterIds)}`);
23
+ }
24
+ }
25
+ export function attemptRecordFileName(id) {
26
+ return `${Buffer.from(id, 'utf8').toString('base64url')}.json`;
27
+ }
28
+ function stableJsonValue(value) {
29
+ if (Array.isArray(value))
30
+ return value.map(stableJsonValue);
31
+ if (value !== null && typeof value === 'object') {
32
+ const input = value;
33
+ const output = {};
34
+ for (const key of Object.keys(input).sort())
35
+ output[key] = stableJsonValue(input[key]);
36
+ return output;
37
+ }
38
+ return value;
39
+ }
40
+ function stableJson(value) {
41
+ return JSON.stringify(stableJsonValue(value));
42
+ }
43
+ export function metaSourceSignature(cluster) {
44
+ return createHash('sha256').update(stableJson({
45
+ metaClusterId: cluster.metaClusterId,
46
+ polarity: cluster.polarity,
47
+ gateTier: cluster.gateTier,
48
+ sources: cluster.sources.map((source) => ({
49
+ id: source.id,
50
+ name: source.name,
51
+ description: source.description,
52
+ body: source.body,
53
+ evidenceRefs: source.evidenceRefs,
54
+ instanceIds: source.instanceIds,
55
+ })),
56
+ })).digest('hex');
57
+ }
58
+ export function reconstructSelectedClusters(frozen, rawRows, slateInstanceIds) {
59
+ const clusters = clusterEvidence(rawRows, { heldOut: (id) => slateInstanceIds.has(id) });
60
+ const byId = new Map(clusters.map((cluster) => [cluster.clusterId, cluster]));
61
+ return frozen.map((row) => {
62
+ const cluster = byId.get(row.clusterId);
63
+ if (!cluster) {
64
+ throw new Error(`eval-prep resume: frozen cluster ${row.clusterId} could not be reconstructed from raw evidence`);
65
+ }
66
+ return cluster;
67
+ });
68
+ }
69
+ export function rebuildModelManifestFromAttemptRecords(label, model, selectedClusterIds, stage1Records, metaRecords, opts = {}) {
70
+ const stage1ById = new Map(stage1Records.map((record) => [record.clusterId, record]));
71
+ const published = [];
72
+ const rejected = [];
73
+ const errors = [];
74
+ const attemptedClusterIds = [];
75
+ for (const clusterId of selectedClusterIds) {
76
+ const record = stage1ById.get(clusterId);
77
+ if (!record)
78
+ continue;
79
+ attemptedClusterIds.push(clusterId);
80
+ if (record.status === 'published' && record.published)
81
+ published.push(record.published);
82
+ else if (record.status === 'rejected' && record.rejected)
83
+ rejected.push(record.rejected);
84
+ else if (record.status === 'error' && record.error)
85
+ errors.push(record.error);
86
+ }
87
+ const metaById = new Map(metaRecords.map((record) => [record.metaClusterId, record]));
88
+ const metaClusterIds = opts.metaClusterIds ?? [...metaById.keys()].sort();
89
+ const metaResult = { published: [], rejected: [], errors: [] };
90
+ for (const metaClusterId of metaClusterIds) {
91
+ const record = metaById.get(metaClusterId);
92
+ if (!record)
93
+ continue;
94
+ if (record.status === 'published' && record.published)
95
+ metaResult.published.push(record.published);
96
+ else if (record.status === 'rejected' && record.rejected)
97
+ metaResult.rejected.push(record.rejected);
98
+ else if (record.status === 'error' && record.error)
99
+ metaResult.errors.push(record.error);
100
+ }
101
+ return {
102
+ label,
103
+ model,
104
+ attemptedClusterIds,
105
+ published,
106
+ rejected,
107
+ errors,
108
+ ...(metaRecords.length > 0 || opts.metaClusterIds !== undefined ? { metaClusterIds, metaDistilled: metaResult } : {}),
109
+ };
110
+ }
111
+ function writeJson(path, value) {
112
+ writeFileSync(path, JSON.stringify(value, null, 2) + '\n');
113
+ }
114
+ function readJson(path) {
115
+ return JSON.parse(readFileSync(path, 'utf-8'));
116
+ }
117
+ function writeEvidenceJsonl(path, rows) {
118
+ writeFileSync(path, rows.map((row) => JSON.stringify(row)).join('\n') + (rows.length > 0 ? '\n' : ''));
119
+ }
120
+ function readEvidenceJsonl(path) {
121
+ const text = readFileSync(path, 'utf-8').trim();
122
+ if (text === '')
123
+ return [];
124
+ return text.split('\n').map((line) => JSON.parse(line));
125
+ }
126
+ function ensureUniqueLabels(models) {
127
+ const seen = new Set();
128
+ for (const model of models) {
129
+ if (seen.has(model.label))
130
+ throw new Error(`duplicate eval-prep model label: ${model.label}`);
131
+ seen.add(model.label);
132
+ }
133
+ }
134
+ function sameCaps(a, b) {
135
+ return a.maxClusters === b.maxClusters &&
136
+ a.maxContrastive === b.maxContrastive &&
137
+ a.maxLessons === b.maxLessons &&
138
+ a.maxPatterns === b.maxPatterns;
139
+ }
140
+ function describeCaps(caps) {
141
+ return `maxClusters=${caps.maxClusters}, maxContrastive=${caps.maxContrastive}, maxLessons=${caps.maxLessons}, maxPatterns=${caps.maxPatterns}`;
142
+ }
143
+ function validateResumeConfig(outDir, selectionFile, manifest, deps) {
144
+ const conflicts = [];
145
+ const requestedCaps = resolveClusterSelectionCaps(deps.selection ?? {});
146
+ if (!sameCaps(selectionFile.caps, requestedCaps)) {
147
+ conflicts.push(`selection caps existing (${describeCaps(selectionFile.caps)}) requested (${describeCaps(requestedCaps)})`);
148
+ }
149
+ if (manifest.limit !== deps.limit)
150
+ conflicts.push(`limit existing ${manifest.limit ?? 'unset'} requested ${deps.limit ?? 'unset'}`);
151
+ if (manifest.groupCap !== deps.groupCap)
152
+ conflicts.push(`group-cap existing ${manifest.groupCap ?? 'unset'} requested ${deps.groupCap ?? 'unset'}`);
153
+ if (conflicts.length > 0) {
154
+ throw new Error(`eval-prep output ${outDir} was created with a different frozen selection config: ${conflicts.join('; ')}. Pass --force to rebuild from scratch.`);
155
+ }
156
+ }
157
+ async function prepareOrLoadSelection(deps) {
158
+ const selectionPath = join(deps.outDir, 'selection.json');
159
+ const rawDir = join(deps.outDir, 'raw-evidence');
160
+ const rawPath = join(rawDir, 'evidence.jsonl');
161
+ const manifestPath = join(deps.outDir, 'manifest.json');
162
+ const hasSelection = existsSync(selectionPath);
163
+ const hasRaw = existsSync(rawPath);
164
+ if (hasSelection || hasRaw) {
165
+ if (!hasSelection || !hasRaw) {
166
+ throw new Error(`eval-prep output ${deps.outDir} has partial frozen artifacts; pass --force to rebuild`);
167
+ }
168
+ if (!existsSync(manifestPath)) {
169
+ throw new Error(`eval-prep output ${deps.outDir} cannot be resumed because manifest.json is missing; pass --force to rebuild legacy or partial output`);
170
+ }
171
+ const selectionFile = readJson(selectionPath);
172
+ const manifest = readJson(manifestPath);
173
+ validateResumeConfig(deps.outDir, selectionFile, manifest, deps);
174
+ const rawRows = readEvidenceJsonl(rawPath);
175
+ const selectedClusters = reconstructSelectedClusters(selectionFile.selected, rawRows, deps.slate.instanceIds);
176
+ const selectedClusterIds = selectedClusters.map((cluster) => cluster.clusterId);
177
+ assertAttemptedClusterIds(selectionFile.selected.map((row) => row.clusterId), selectedClusterIds);
178
+ return {
179
+ frozenSelection: selectionFile.selected,
180
+ selectedClusters,
181
+ selectedClusterIds,
182
+ clusterCount: manifest.clusterCount,
183
+ bridge: manifest.bridge,
184
+ dirty: false,
185
+ };
186
+ }
187
+ const prepared = await prepareDistillationEvidence({
188
+ verdictSource: deps.verdictSource,
189
+ fetchEvidence: deps.fetchEvidence,
190
+ publishDeps: deps.publishDeps,
191
+ slate: deps.slate,
192
+ ...(deps.groupCap !== undefined ? { groupCap: deps.groupCap } : {}),
193
+ ...(deps.limit !== undefined ? { limit: deps.limit } : {}),
194
+ ...(deps.now ? { now: deps.now } : {}),
195
+ });
196
+ const selected = selectUsefulClusters(prepared.clusters, deps.selection);
197
+ const selectedClusters = selected.selected.map((row) => row.cluster);
198
+ const selectedClusterIds = selectedClusters.map((cluster) => cluster.clusterId);
199
+ const frozenSelection = selected.selected.map((row) => ({
200
+ clusterId: row.cluster.clusterId,
201
+ tier: row.cluster.tier,
202
+ evidenceRefs: row.cluster.evidenceRefs,
203
+ instanceIds: row.cluster.instanceIds,
204
+ score: row.score,
205
+ }));
206
+ const selectionFile = {
207
+ schema: 'jinn.distill.eval-selection.v1',
208
+ caps: selected.caps,
209
+ selected: frozenSelection,
210
+ rejected: selected.rejected,
211
+ };
212
+ writeJson(selectionPath, selectionFile);
213
+ mkdirSync(rawDir, { recursive: true });
214
+ writeEvidenceJsonl(rawPath, prepared.collected);
215
+ writeJson(join(rawDir, 'manifest.json'), {
216
+ schema: 'jinn.distill.raw-evidence.v1',
217
+ count: prepared.collected.length,
218
+ refs: prepared.collected.map((row) => row.ref),
219
+ });
220
+ return {
221
+ frozenSelection,
222
+ selectedClusters,
223
+ selectedClusterIds,
224
+ clusterCount: prepared.clusters.length,
225
+ bridge: {
226
+ bridged: prepared.bridge.bridged.length,
227
+ excludedHeldOut: prepared.bridge.excludedHeldOut.length,
228
+ deduped: prepared.bridge.deduped.length,
229
+ errors: prepared.bridge.errors.length,
230
+ verdictsTruncated: prepared.verdictsTruncated,
231
+ },
232
+ dirty: true,
233
+ };
234
+ }
235
+ function buildManifest(deps, prepared, modelResults) {
236
+ const generatedAt = (deps.now?.() ?? new Date()).toISOString();
237
+ return {
238
+ schema: 'jinn.distill.eval-prep.v1',
239
+ generatedAt,
240
+ mode: 'local-only',
241
+ outDir: deps.outDir,
242
+ ...(deps.limit !== undefined ? { limit: deps.limit } : {}),
243
+ ...(deps.groupCap !== undefined ? { groupCap: deps.groupCap } : {}),
244
+ selectOnly: deps.selectOnly ?? false,
245
+ meta: deps.meta ?? false,
246
+ clusterCount: prepared.clusterCount,
247
+ selectedClusterIds: prepared.selectedClusterIds,
248
+ models: modelResults.map((m) => ({
249
+ label: m.label,
250
+ model: m.model,
251
+ attemptedClusterIds: m.attemptedClusterIds,
252
+ published: m.published.length,
253
+ rejected: m.rejected.length,
254
+ errors: m.errors.length,
255
+ ...(m.metaClusterIds ? { metaClusterIds: m.metaClusterIds } : {}),
256
+ ...(m.metaDistilled ? {
257
+ metaPublished: m.metaDistilled.published.length,
258
+ metaRejected: m.metaDistilled.rejected.length,
259
+ metaErrors: m.metaDistilled.errors.length,
260
+ } : {}),
261
+ })),
262
+ bridge: prepared.bridge,
263
+ };
264
+ }
265
+ function writeSkillPackage(dir, pkg) {
266
+ mkdirSync(dir, { recursive: true });
267
+ writeFileSync(join(dir, 'SKILL.md'), buildSkillMarkdown(pkg));
268
+ }
269
+ function attemptPath(dir, id) {
270
+ return join(dir, attemptRecordFileName(id));
271
+ }
272
+ function loadStage1Record(dir, label, model, clusterId) {
273
+ const path = attemptPath(dir, clusterId);
274
+ if (!existsSync(path))
275
+ return null;
276
+ const record = readJson(path);
277
+ if (record.schema !== 'jinn.distill.eval-attempt.v1' || record.label !== label || record.model !== model || record.clusterId !== clusterId) {
278
+ throw new Error(`eval-prep attempt record mismatch at ${path}`);
279
+ }
280
+ return record;
281
+ }
282
+ function loadMetaRecord(dir, label, model, metaClusterId) {
283
+ const path = attemptPath(dir, metaClusterId);
284
+ if (!existsSync(path))
285
+ return null;
286
+ const record = readJson(path);
287
+ if (record.schema !== 'jinn.distill.eval-meta-attempt.v1' || record.label !== label || record.model !== model || record.metaClusterId !== metaClusterId) {
288
+ throw new Error(`eval-prep meta-attempt record mismatch at ${path}`);
289
+ }
290
+ return record;
291
+ }
292
+ function stage1RecordFromResult(label, model, clusterId, result, completedAt) {
293
+ const published = result.published.find((row) => row.clusterId === clusterId);
294
+ if (published) {
295
+ return { schema: 'jinn.distill.eval-attempt.v1', label, model, clusterId, status: 'published', completedAt, published };
296
+ }
297
+ const rejected = result.rejected.find((row) => row.clusterId === clusterId);
298
+ if (rejected) {
299
+ return { schema: 'jinn.distill.eval-attempt.v1', label, model, clusterId, status: 'rejected', completedAt, rejected };
300
+ }
301
+ const error = result.errors.find((row) => row.clusterId === clusterId);
302
+ if (error) {
303
+ return { schema: 'jinn.distill.eval-attempt.v1', label, model, clusterId, status: 'error', completedAt, error };
304
+ }
305
+ return {
306
+ schema: 'jinn.distill.eval-attempt.v1',
307
+ label,
308
+ model,
309
+ clusterId,
310
+ status: 'error',
311
+ completedAt,
312
+ error: { clusterId, error: 'distill returned no terminal result for cluster' },
313
+ };
314
+ }
315
+ function metaRecordFromResult(label, model, metaClusterId, sourceSignature, result, completedAt) {
316
+ const published = result.published.find((row) => row.metaClusterId === metaClusterId);
317
+ if (published) {
318
+ return { schema: 'jinn.distill.eval-meta-attempt.v1', label, model, metaClusterId, sourceSignature, status: 'published', completedAt, published };
319
+ }
320
+ const rejected = result.rejected.find((row) => row.metaClusterId === metaClusterId);
321
+ if (rejected) {
322
+ return { schema: 'jinn.distill.eval-meta-attempt.v1', label, model, metaClusterId, sourceSignature, status: 'rejected', completedAt, rejected };
323
+ }
324
+ const error = result.errors.find((row) => row.metaClusterId === metaClusterId);
325
+ if (error) {
326
+ return { schema: 'jinn.distill.eval-meta-attempt.v1', label, model, metaClusterId, sourceSignature, status: 'error', completedAt, error };
327
+ }
328
+ return {
329
+ schema: 'jinn.distill.eval-meta-attempt.v1',
330
+ label,
331
+ model,
332
+ metaClusterId,
333
+ sourceSignature,
334
+ status: 'error',
335
+ completedAt,
336
+ error: { metaClusterId, error: 'meta-distill returned no terminal result for cluster' },
337
+ };
338
+ }
339
+ function ensurePublishedSkillFile(record, skillsDir) {
340
+ if (record.status !== 'published' || !record.published)
341
+ return false;
342
+ const skillPath = join(skillsDir, record.published.pkg.name, 'SKILL.md');
343
+ if (existsSync(skillPath))
344
+ return false;
345
+ writeSkillPackage(join(skillsDir, record.published.pkg.name), record.published.pkg);
346
+ return true;
347
+ }
348
+ function ensurePublishedMetaSkillFile(record, metaSkillsDir) {
349
+ if (record.status !== 'published' || !record.published)
350
+ return false;
351
+ const skillPath = join(metaSkillsDir, record.published.pkg.name, 'SKILL.md');
352
+ if (existsSync(skillPath))
353
+ return false;
354
+ writeSkillPackage(join(metaSkillsDir, record.published.pkg.name), record.published.pkg);
355
+ return true;
356
+ }
357
+ function stage1PublishedForMeta(selectedClusters, records) {
358
+ const clusterById = new Map(selectedClusters.map((cluster) => [cluster.clusterId, cluster]));
359
+ return records.flatMap((record) => {
360
+ if (record.status !== 'published' || !record.published)
361
+ return [];
362
+ const cluster = clusterById.get(record.clusterId);
363
+ if (!cluster)
364
+ throw new Error(`eval-prep meta: no originating cluster for ${record.clusterId}`);
365
+ return [{
366
+ clusterId: record.clusterId,
367
+ skillKind: record.published.skillKind,
368
+ pkg: record.published.pkg,
369
+ evidenceRefs: cluster.evidenceRefs,
370
+ instanceIds: cluster.instanceIds,
371
+ }];
372
+ });
373
+ }
374
+ function loadUnrequestedModelManifests(outDir, requestedLabels) {
375
+ const distilledDir = join(outDir, 'distilled');
376
+ if (!existsSync(distilledDir))
377
+ return [];
378
+ return readdirSync(distilledDir, { withFileTypes: true })
379
+ .filter((entry) => entry.isDirectory() && !requestedLabels.has(entry.name))
380
+ .map((entry) => join(distilledDir, entry.name, 'manifest.json'))
381
+ .filter((path) => existsSync(path))
382
+ .map((path) => readJson(path))
383
+ .sort((a, b) => a.label.localeCompare(b.label));
384
+ }
385
+ function createConcurrencyLimiter(limit) {
386
+ if (!Number.isInteger(limit) || limit <= 0) {
387
+ throw new Error(`eval-prep concurrency must be a positive integer (got ${limit})`);
388
+ }
389
+ let active = 0;
390
+ const queued = [];
391
+ const next = () => {
392
+ const start = queued.shift();
393
+ if (start)
394
+ start();
395
+ };
396
+ return {
397
+ run(work) {
398
+ return new Promise((resolve, reject) => {
399
+ const start = () => {
400
+ active += 1;
401
+ void work().then(resolve, reject).finally(() => {
402
+ active -= 1;
403
+ next();
404
+ });
405
+ };
406
+ if (active < limit)
407
+ start();
408
+ else
409
+ queued.push(start);
410
+ });
411
+ },
412
+ };
413
+ }
414
+ export async function runEvalPrep(deps) {
415
+ ensureUniqueLabels(deps.models);
416
+ if (deps.force)
417
+ rmSync(deps.outDir, { recursive: true, force: true });
418
+ mkdirSync(deps.outDir, { recursive: true });
419
+ const prepared = await prepareOrLoadSelection(deps);
420
+ const manifestPath = join(deps.outDir, 'manifest.json');
421
+ let dirty = prepared.dirty;
422
+ if (!existsSync(manifestPath)) {
423
+ writeJson(manifestPath, buildManifest(deps, prepared, []));
424
+ dirty = true;
425
+ }
426
+ const limiter = createConcurrencyLimiter(deps.concurrency ?? 1);
427
+ const modelResults = await Promise.all((deps.selectOnly ? [] : deps.models).map(async (model) => {
428
+ const modelDir = join(deps.outDir, 'distilled', model.label);
429
+ const skillsDir = join(modelDir, 'skills');
430
+ const metaSkillsDir = join(modelDir, 'meta-skills');
431
+ const attemptsDir = join(modelDir, 'attempts');
432
+ const metaAttemptsDir = join(modelDir, 'meta-attempts');
433
+ mkdirSync(skillsDir, { recursive: true });
434
+ mkdirSync(attemptsDir, { recursive: true });
435
+ let modelDirty = false;
436
+ const stage1Records = await Promise.all(prepared.selectedClusters.map(async (cluster) => {
437
+ const existing = loadStage1Record(attemptsDir, model.label, model.model, cluster.clusterId);
438
+ if (existing && !(deps.retryErrors && existing.status === 'error')) {
439
+ if (ensurePublishedSkillFile(existing, skillsDir)) {
440
+ modelDirty = true;
441
+ dirty = true;
442
+ }
443
+ return existing;
444
+ }
445
+ const publishSkill = async (pkg) => {
446
+ writeSkillPackage(join(skillsDir, pkg.name), pkg);
447
+ return { envelopeRef: `local:${model.label}:${pkg.name}`, anchorTx: null };
448
+ };
449
+ const result = await limiter.run(() => distillClusters([cluster], {
450
+ distill: model.distill,
451
+ publishSkill,
452
+ slate: deps.slate,
453
+ distribution: deps.distribution ?? 'coding',
454
+ distillModel: model.model,
455
+ ...(deps.now ? { now: deps.now } : {}),
456
+ }));
457
+ const record = stage1RecordFromResult(model.label, model.model, cluster.clusterId, result, (deps.now?.() ?? new Date()).toISOString());
458
+ writeJson(attemptPath(attemptsDir, cluster.clusterId), record);
459
+ modelDirty = true;
460
+ dirty = true;
461
+ return record;
462
+ }));
463
+ assertAttemptedClusterIds(prepared.selectedClusterIds, stage1Records.map((record) => record.clusterId));
464
+ let metaRecords = [];
465
+ let metaClusterIds;
466
+ if (deps.meta) {
467
+ if (!model.metaDistill) {
468
+ throw new Error(`eval-prep meta enabled but no metaDistill port was provided for ${model.label}`);
469
+ }
470
+ const metaDistillPort = model.metaDistill;
471
+ mkdirSync(metaSkillsDir, { recursive: true });
472
+ mkdirSync(metaAttemptsDir, { recursive: true });
473
+ const metaClusters = buildMetaClusters(stage1PublishedForMeta(prepared.selectedClusters, stage1Records));
474
+ metaClusterIds = metaClusters.map((cluster) => cluster.metaClusterId);
475
+ for (const cluster of metaClusters) {
476
+ const signature = metaSourceSignature(cluster);
477
+ const existing = loadMetaRecord(metaAttemptsDir, model.label, model.model, cluster.metaClusterId);
478
+ if (existing &&
479
+ existing.sourceSignature === signature &&
480
+ !(deps.retryErrors && existing.status === 'error')) {
481
+ if (ensurePublishedMetaSkillFile(existing, metaSkillsDir)) {
482
+ modelDirty = true;
483
+ dirty = true;
484
+ }
485
+ metaRecords.push(existing);
486
+ continue;
487
+ }
488
+ const publishMetaSkill = async (pkg) => {
489
+ writeSkillPackage(join(metaSkillsDir, pkg.name), pkg);
490
+ return { envelopeRef: `local:${model.label}:meta:${pkg.name}`, anchorTx: null };
491
+ };
492
+ const result = await limiter.run(() => metaDistill([cluster], {
493
+ metaDistill: metaDistillPort,
494
+ publishSkill: publishMetaSkill,
495
+ slate: deps.slate,
496
+ distribution: deps.distribution ?? 'coding',
497
+ distillModel: model.model,
498
+ ...(deps.now ? { now: deps.now } : {}),
499
+ }));
500
+ const record = metaRecordFromResult(model.label, model.model, cluster.metaClusterId, signature, result, (deps.now?.() ?? new Date()).toISOString());
501
+ writeJson(attemptPath(metaAttemptsDir, cluster.metaClusterId), record);
502
+ metaRecords.push(record);
503
+ modelDirty = true;
504
+ dirty = true;
505
+ }
506
+ }
507
+ const modelManifest = rebuildModelManifestFromAttemptRecords(model.label, model.model, prepared.selectedClusterIds, stage1Records, metaRecords, ...(metaClusterIds !== undefined ? [{ metaClusterIds }] : []));
508
+ assertAttemptedClusterIds(prepared.selectedClusterIds, modelManifest.attemptedClusterIds);
509
+ const modelManifestPath = join(modelDir, 'manifest.json');
510
+ if (modelDirty || !existsSync(modelManifestPath))
511
+ writeJson(modelManifestPath, modelManifest);
512
+ return modelManifest;
513
+ }));
514
+ const requestedLabels = new Set((deps.selectOnly ? [] : deps.models).map((model) => model.label));
515
+ const allModelResults = deps.selectOnly
516
+ ? modelResults
517
+ : [...loadUnrequestedModelManifests(deps.outDir, requestedLabels), ...modelResults]
518
+ .sort((a, b) => a.label.localeCompare(b.label));
519
+ const manifest = buildManifest(deps, prepared, allModelResults);
520
+ if (dirty || !existsSync(manifestPath))
521
+ writeJson(manifestPath, manifest);
522
+ return { manifest, selection: prepared.frozenSelection, models: allModelResults };
523
+ }