@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,211 @@
1
+ function uniq(values) {
2
+ return [...new Set([...values].filter((v) => typeof v === 'string' && v.trim() !== '').map((v) => v.trim()))];
3
+ }
4
+ function oneLine(value, max = 240) {
5
+ if (typeof value !== 'string')
6
+ return undefined;
7
+ const text = value.replace(/\s+/g, ' ').trim();
8
+ if (!text)
9
+ return undefined;
10
+ return text.length > max ? `${text.slice(0, max - 1)}…` : text;
11
+ }
12
+ function parseJsonObject(value) {
13
+ if (typeof value !== 'string')
14
+ return null;
15
+ try {
16
+ const parsed = JSON.parse(value);
17
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
18
+ ? parsed
19
+ : null;
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ }
25
+ function commandFromStep(step) {
26
+ const args = parseJsonObject(step.attributes['tool.args']);
27
+ return oneLine(args?.['command'] ?? args?.['cmd'] ?? step.attributes['command']);
28
+ }
29
+ function fileFromStep(step) {
30
+ const args = parseJsonObject(step.attributes['tool.args']);
31
+ return oneLine(step.attributes['file.path'] ?? args?.['path'] ?? args?.['file']);
32
+ }
33
+ function eventKind(step) {
34
+ return typeof step.attributes['jinn.capture.event.kind'] === 'string'
35
+ ? step.attributes['jinn.capture.event.kind']
36
+ : step.name;
37
+ }
38
+ function eventText(step) {
39
+ return oneLine(step.attributes['message.content'] ??
40
+ step.attributes['tool.result'] ??
41
+ step.attributes['file.diff'] ??
42
+ commandFromStep(step) ??
43
+ fileFromStep(step));
44
+ }
45
+ function toolName(step) {
46
+ return oneLine(step.attributes['tool.name']);
47
+ }
48
+ function traceId(task) {
49
+ return `local-capture:${task.session.sessionId}`;
50
+ }
51
+ export function traceCardFromCapture(task) {
52
+ const commands = uniq(task.steps.map(commandFromStep));
53
+ const filesTouched = uniq(task.steps.map(fileFromStep));
54
+ const errorSnippets = uniq(task.steps.map((step) => {
55
+ const isError = step.attributes['tool.is_error'] === true || step.attributes['tool.is_error'] === 'true';
56
+ return isError ? oneLine(step.attributes['tool.result']) : undefined;
57
+ }));
58
+ const toolNames = uniq([
59
+ ...task.environment.tools,
60
+ ...task.steps.map(toolName),
61
+ ]);
62
+ const redactionFlags = task.steps.some((step) => step.redactedKeys.length > 0) ? ['redacted'] : [];
63
+ return {
64
+ traceId: traceId(task),
65
+ sessionId: task.session.sessionId,
66
+ sourceTool: task.environment.harness.name,
67
+ startedAt: task.session.capturedAt,
68
+ durationMs: task.cost.durationMs,
69
+ summary: task.task.summary,
70
+ tools: toolNames,
71
+ commands,
72
+ filesTouched,
73
+ errorSnippets,
74
+ outcome: task.outcome.status,
75
+ skillsUsed: [],
76
+ redactionFlags,
77
+ };
78
+ }
79
+ function eventView(step, includeAttributes = false) {
80
+ const out = {
81
+ spanId: step.spanId,
82
+ kind: eventKind(step),
83
+ name: step.name,
84
+ };
85
+ const text = eventText(step);
86
+ const tool = toolName(step);
87
+ if (text)
88
+ out.text = text;
89
+ if (tool)
90
+ out.toolName = tool;
91
+ if (includeAttributes)
92
+ out.attributes = step.attributes;
93
+ return out;
94
+ }
95
+ export function readTrace(task, opts) {
96
+ const base = { mode: opts.mode, traceId: traceId(task), summary: task.task.summary };
97
+ if (opts.mode === 'summary')
98
+ return base;
99
+ if (opts.mode === 'full_transcript' && !opts.allowFullTranscript) {
100
+ throw new Error('full transcript reads require explicit allowFullTranscript=true');
101
+ }
102
+ const events = task.steps.map((step) => eventView(step, opts.mode === 'full_transcript'));
103
+ if (opts.mode === 'events' || opts.mode === 'full_transcript')
104
+ return { ...base, events };
105
+ if (opts.mode === 'tool_calls') {
106
+ return {
107
+ ...base,
108
+ toolCalls: events.filter((event) => event.kind === 'tool-call' || event.toolName !== undefined),
109
+ };
110
+ }
111
+ const q = opts.query?.toLowerCase();
112
+ const filtered = q
113
+ ? events.filter((event) => JSON.stringify(event).toLowerCase().includes(q))
114
+ : events;
115
+ return { ...base, events: filtered.slice(0, opts.limit ?? 20) };
116
+ }
117
+ function cardText(card) {
118
+ return [
119
+ card.summary,
120
+ ...card.tools,
121
+ ...card.commands,
122
+ ...card.filesTouched,
123
+ ...card.errorSnippets,
124
+ card.outcome,
125
+ ].join('\n').toLowerCase();
126
+ }
127
+ export function searchTraceCards(cards, query) {
128
+ const matches = cards.filter((card) => {
129
+ if (query.query && !cardText(card).includes(query.query.toLowerCase()))
130
+ return false;
131
+ if (query.command && !card.commands.some((c) => c.toLowerCase().includes(query.command.toLowerCase())))
132
+ return false;
133
+ if (query.file && !card.filesTouched.some((f) => f.toLowerCase().includes(query.file.toLowerCase())))
134
+ return false;
135
+ if (query.error && !card.errorSnippets.some((e) => e.toLowerCase().includes(query.error.toLowerCase())))
136
+ return false;
137
+ if (query.tool && !card.tools.some((t) => t.toLowerCase().includes(query.tool.toLowerCase())))
138
+ return false;
139
+ if (query.outcome && card.outcome !== query.outcome)
140
+ return false;
141
+ return true;
142
+ });
143
+ return matches
144
+ .sort((a, b) => b.startedAt.localeCompare(a.startedAt))
145
+ .slice(0, query.limit ?? matches.length);
146
+ }
147
+ function estimateTokens(text) {
148
+ return Math.ceil(new TextEncoder().encode(text).length / 4);
149
+ }
150
+ function costFor(tokens) {
151
+ if (tokens < 20_000)
152
+ return 'low';
153
+ if (tokens < 80_000)
154
+ return 'medium';
155
+ return 'high';
156
+ }
157
+ function grouped(cards, key) {
158
+ const out = new Map();
159
+ for (const card of cards) {
160
+ for (const value of card[key]) {
161
+ const normalized = value.toLowerCase();
162
+ const group = out.get(normalized) ?? [];
163
+ group.push(card);
164
+ out.set(normalized, group);
165
+ }
166
+ }
167
+ return out;
168
+ }
169
+ export function clusterTraceCards(cards) {
170
+ const candidates = [];
171
+ const addGroups = (label, key, groups, priority) => {
172
+ for (const [signal, group] of groups) {
173
+ if (group.length < 2)
174
+ continue;
175
+ const tokenEstimate = estimateTokens(JSON.stringify(group));
176
+ candidates.push({
177
+ title: `${label}: ${group[0]?.[key === 'errors' ? 'errorSnippets' : key === 'commands' ? 'commands' : 'filesTouched']
178
+ .find((v) => v.toLowerCase() === signal) ?? signal}`,
179
+ traceIds: group.map((card) => card.traceId),
180
+ repeatedSignals: { [key]: [signal] },
181
+ evidenceTierEstimate: 'recurring-pattern',
182
+ estimatedSourceTokens: tokenEstimate,
183
+ estimatedDistillCost: costFor(tokenEstimate),
184
+ privacyFlags: uniq(group.flatMap((card) => card.redactionFlags)),
185
+ priority,
186
+ });
187
+ }
188
+ };
189
+ addGroups('Repeated error', 'errors', grouped(cards, 'errorSnippets'), 0);
190
+ addGroups('Repeated command', 'commands', grouped(cards, 'commands'), 1);
191
+ addGroups('Repeated file', 'files', grouped(cards, 'filesTouched'), 2);
192
+ if (candidates.length === 0 && cards.length === 1) {
193
+ const tokenEstimate = estimateTokens(JSON.stringify(cards[0]));
194
+ candidates.push({
195
+ title: cards[0].summary,
196
+ traceIds: [cards[0].traceId],
197
+ repeatedSignals: {},
198
+ evidenceTierEstimate: 'single-example',
199
+ estimatedSourceTokens: tokenEstimate,
200
+ estimatedDistillCost: costFor(tokenEstimate),
201
+ privacyFlags: cards[0].redactionFlags,
202
+ priority: 3,
203
+ });
204
+ }
205
+ return candidates
206
+ .sort((a, b) => b.traceIds.length - a.traceIds.length || a.priority - b.priority || a.title.localeCompare(b.title))
207
+ .map(({ priority, ...candidate }, index) => ({
208
+ candidateId: `local-candidate-${String(index + 1).padStart(3, '0')}`,
209
+ ...candidate,
210
+ }));
211
+ }
@@ -0,0 +1,180 @@
1
+ /**
2
+ * The distillation step — evidence clusters → layer-2 skills
3
+ * (spec/2026-07-06-distillation-v1.md §7, D4/D10).
4
+ *
5
+ * Scripted, single-shot, flat (recursion/hierarchy are v3). Three modes keyed to
6
+ * the cluster's tier (§7): pattern → strategic-pattern skill; lesson →
7
+ * failure-lesson skill (diagnosis, not prescription); contrastive → one skill
8
+ * from both polarities of an instance. Each skill is:
9
+ * 1. distilled by an injected LLM port (`deps.distill`) using
10
+ * `jinn-skill-distill-prompt-v1` (the port owns the model call);
11
+ * 2. **secret-scrubbed fail-closed** — the body is run through the layer-2
12
+ * pipeline; if the scrub would change anything (a secret was present), the
13
+ * skill is DROPPED, not published (so published bodies are provably clean
14
+ * AND never re-defaced, §7 step 4 / §10);
15
+ * 3. **contamination-scanned** against the held-out slate (§12 axis 3) — a
16
+ * body naming a slate instance/repo/PR is dropped;
17
+ * 4. **structurally gated** (§7 step 6, v0.5) — the fixed skeleton (five
18
+ * non-empty sections), the description anti-trigger ("Not for:"), and (for
19
+ * lessons) the imperative-counterfactual guard, all DETERMINISTIC (a deep
20
+ * quality judge is deferred admission-checking, §13);
21
+ * 5. published via `publishSkill()` with provenance back-links, the prompt
22
+ * SHA, and the auditability fields (distill model + token estimates, §5).
23
+ *
24
+ * Clustering is upstream (human-curated for v1, §7) — this consumes pre-formed
25
+ * clusters of eligible evidence.
26
+ */
27
+ import { type ScrubPipeline } from '@jinn-network/core/scrub';
28
+ import { type SkillPackage } from './skill-package.js';
29
+ import type { MetaCluster } from './cluster.js';
30
+ export interface DistillCluster {
31
+ clusterId: string;
32
+ /**
33
+ * `pattern` (successes), `lesson` (evaluator-confirmed failures), or
34
+ * `contrastive` (both polarities for one instance — the pass↔fail delta,
35
+ * §7 v0.5). A contrastive cluster carries evidence refs from BOTH polarities.
36
+ */
37
+ tier: 'pattern' | 'lesson' | 'contrastive';
38
+ /** Source evidence envelope refs → `metadata.jinn.provenance`. */
39
+ evidenceRefs: string[];
40
+ /** The distinct instance ids in the cluster (audit / dedup upstream). */
41
+ instanceIds: string[];
42
+ /** Opaque payload handed to the LLM port (the cluster's evidence). */
43
+ input: unknown;
44
+ }
45
+ export interface DistillLLMOutput {
46
+ name: string;
47
+ description: string;
48
+ body: string;
49
+ }
50
+ export interface DistillDeps {
51
+ /** The LLM port: runs jinn-skill-distill-prompt-v1 over the cluster. */
52
+ distill: (cluster: DistillCluster) => Promise<DistillLLMOutput>;
53
+ /** Publish a skill package as a corpus record (Plan A `publishSkill`). */
54
+ publishSkill: (pkg: SkillPackage) => Promise<{
55
+ envelopeRef: string;
56
+ anchorTx: string | null;
57
+ }>;
58
+ /** The held-out slate for the contamination scan (§12). */
59
+ slate: {
60
+ instanceIds: Set<string>;
61
+ repos?: Set<string>;
62
+ };
63
+ distribution?: string;
64
+ /** Evidence tier recorded on generated local skills. Defaults to evaluator-verified network posture. */
65
+ verifiabilityTier?: string;
66
+ /** Local lifecycle status for private generated skills. */
67
+ skillStatus?: 'experimental' | 'stable' | 'deprecated';
68
+ /** Local evidence tier for private generated skills. */
69
+ evidenceTier?: 'single-example' | 'recurring-pattern' | 'contrastive' | 'user-confirmed' | 'stable' | 'deprecated';
70
+ sourceTools?: string[];
71
+ targetTools?: string[];
72
+ /** The model that ran distillation — recorded in provenance (§5 auditability). */
73
+ distillModel?: string;
74
+ scrubPipeline?: ScrubPipeline;
75
+ now?: () => Date;
76
+ /** Progress seam (#1533): fired once before the loop with every cluster. */
77
+ onPlan?: (plan: ClusterPlanEntry[]) => void;
78
+ /** Progress seam (#1533): fired around each cluster's distill call. */
79
+ onCluster?: (ev: ClusterProgressEvent) => void;
80
+ }
81
+ /** One row of the up-front cluster plan handed to {@link DistillDeps.onPlan}. */
82
+ export interface ClusterPlanEntry {
83
+ clusterId: string;
84
+ /** 1-based position in the run. */
85
+ index: number;
86
+ captureCount: number;
87
+ /** The cluster's evidence refs — a consumer can map these back to sources. */
88
+ refs: string[];
89
+ }
90
+ /** A start/end progress event around one cluster's distill call (#1533). */
91
+ export interface ClusterProgressEvent {
92
+ phase: 'start' | 'end';
93
+ clusterId: string;
94
+ /** 1-based position in the run. */
95
+ index: number;
96
+ total: number;
97
+ /** End-phase only. */
98
+ outcome?: 'published' | 'rejected' | 'error';
99
+ skillName?: string;
100
+ reason?: string;
101
+ error?: string;
102
+ durationMs?: number;
103
+ }
104
+ export type SkillKind = 'strategic-pattern' | 'failure-lesson' | 'contrastive' | 'cross-instance';
105
+ export interface DistillResult {
106
+ published: Array<{
107
+ clusterId: string;
108
+ skillKind: SkillKind;
109
+ envelopeRef: string;
110
+ pkg: SkillPackage;
111
+ }>;
112
+ rejected: Array<{
113
+ clusterId: string;
114
+ reason: string;
115
+ }>;
116
+ errors: Array<{
117
+ clusterId: string;
118
+ error: string;
119
+ }>;
120
+ }
121
+ /**
122
+ * Scan a distilled body for held-out slate tokens (instance id / repo / PR#).
123
+ * Delimiter-bearing tokens match as substrings; bare repo names match only on
124
+ * word boundaries, so a common-word repo does not silently drop a clean skill.
125
+ */
126
+ export declare function lexicalContaminationScan(body: string, slate: {
127
+ instanceIds: Set<string>;
128
+ repos?: Set<string>;
129
+ }): {
130
+ contaminated: boolean;
131
+ hits: string[];
132
+ };
133
+ export declare function distillClusters(clusters: DistillCluster[], deps: DistillDeps): Promise<DistillResult>;
134
+ /** Stage-2 (cross-instance) LLM output: a distilled skill plus the source ids it corroborates. */
135
+ export interface MetaDistillLLMOutput extends DistillLLMOutput {
136
+ /** Opaque source ids (s1, s2, …) the model says corroborate the emitted rule. */
137
+ supports: string[];
138
+ }
139
+ export interface MetaDistillDeps {
140
+ /** The meta LLM port: runs jinn-skill-meta-distill-prompt-v1 over one meta-cluster. */
141
+ metaDistill: (cluster: MetaCluster) => Promise<MetaDistillLLMOutput>;
142
+ publishSkill: (pkg: SkillPackage) => Promise<{
143
+ envelopeRef: string;
144
+ anchorTx: string | null;
145
+ }>;
146
+ slate: {
147
+ instanceIds: Set<string>;
148
+ repos?: Set<string>;
149
+ };
150
+ distribution?: string;
151
+ distillModel?: string;
152
+ scrubPipeline?: ScrubPipeline;
153
+ now?: () => Date;
154
+ }
155
+ export interface MetaDistillResult {
156
+ published: Array<{
157
+ metaClusterId: string;
158
+ skillKind: 'cross-instance';
159
+ envelopeRef: string;
160
+ pkg: SkillPackage;
161
+ }>;
162
+ rejected: Array<{
163
+ metaClusterId: string;
164
+ reason: string;
165
+ }>;
166
+ errors: Array<{
167
+ metaClusterId: string;
168
+ error: string;
169
+ }>;
170
+ }
171
+ /**
172
+ * Stage-2 cross-instance meta-distill (issue #1463). For each same-polarity
173
+ * meta-cluster: run the meta LLM, keep only the sources it names in `supports`,
174
+ * require **≥2 distinct supporting instances**, union their layer-1 evidence
175
+ * CIDs into the provenance, and finish through the SAME `finalizeSkill` gate as
176
+ * stage-1 — so a meta-skill passes the identical scrub / contamination /
177
+ * structural checks. Additive and opt-in; never called unless the pipeline
178
+ * enables it.
179
+ */
180
+ export declare function metaDistill(clusters: MetaCluster[], deps: MetaDistillDeps): Promise<MetaDistillResult>;