@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,94 @@
1
+ /**
2
+ * Distillation pipeline orchestrator (spec/2026-07-06-distillation-v1.md §6–§8).
3
+ *
4
+ * Composes the run-time loop end to end: verdict source → **bridge** (ledger →
5
+ * layer-1 evidence, both polarities, exclusion + dedup) → **gate + cluster**
6
+ * (eligible evidence grouped by distinct instance/tier) → **distill** (patterns
7
+ * + lessons → layer-2 skills). Every I/O boundary is an injected port, so the
8
+ * whole loop runs green under stubs (the Tier-0 dry-run) and swaps to the real
9
+ * adapters (verdict-source / fetchEvidence / claude distiller / publishSkill) +
10
+ * `ANTHROPIC_API_KEY` for a Tier-1 run with no code change.
11
+ *
12
+ * The bridge and distiller are decoupled in production through the corpus; here
13
+ * the pipeline captures each bridged envelope via the `publishEvidence` port and
14
+ * hands the same envelopes to clustering, so it reuses the tested `bridgeAttempts`
15
+ * (exclusion/dedup) without a corpus round-trip or a double fetch.
16
+ */
17
+ import { type AttemptRef, type BridgeEvidence, type BridgeResult } from './bridge.js';
18
+ import { type HarnessPublishDeps } from './publish.js';
19
+ import { type TraceEnvelopeV0 } from './envelope.js';
20
+ import { type MetaCluster } from './cluster.js';
21
+ import { type DistillCluster, type DistillLLMOutput, type DistillResult, type MetaDistillLLMOutput, type MetaDistillResult } from './distill.js';
22
+ import type { SkillPackage } from './skill-package.js';
23
+ export interface PipelineDeps {
24
+ /** Ledger verdict-row source (both polarities). */
25
+ verdictSource: {
26
+ list: (args?: {
27
+ limit?: number;
28
+ }) => Promise<AttemptRef[]>;
29
+ };
30
+ /** Fetch the patch + task descriptor for an attempt. */
31
+ fetchEvidence: (ref: AttemptRef) => Promise<BridgeEvidence>;
32
+ /** Deps for the layer-1 publish (capture→publish). */
33
+ publishDeps: HarnessPublishDeps;
34
+ /** Bulk bridge records opt into a single manifest anchor; default stays per-record. */
35
+ anchorMode?: 'per-record' | 'manifest';
36
+ /** Add one explicit per-record control tx to a live manifest measurement run. */
37
+ measurePerRecordControl?: boolean;
38
+ /** The LLM distill port (jinn-skill-distill-prompt-v1 over a cluster). */
39
+ distill: (cluster: DistillCluster) => Promise<DistillLLMOutput>;
40
+ /** Publish a layer-2 skill package (Plan A publishSkill). */
41
+ publishSkill: (pkg: SkillPackage) => Promise<{
42
+ envelopeRef: string;
43
+ anchorTx: string | null;
44
+ }>;
45
+ /** Held-out slate (§12): excluded from the bridge AND the gate/cluster. */
46
+ slate: {
47
+ instanceIds: Set<string>;
48
+ repos?: Set<string>;
49
+ };
50
+ distribution?: string;
51
+ /** The distilling model, recorded in each skill's provenance (§5 auditability). */
52
+ distillModel?: string;
53
+ /** Enable stage-2 cross-instance meta-distill (issue #1463). Default off (opt-in). */
54
+ meta?: boolean;
55
+ /** The stage-2 meta LLM port. Required when `meta` is true. */
56
+ metaDistill?: (cluster: MetaCluster) => Promise<MetaDistillLLMOutput>;
57
+ /**
58
+ * Attempts retained per `(instance_id, polarity)` — the per-instance attempt
59
+ * group the distiller reasons over for group-relative distillation (#1478).
60
+ * Default 8. NOTE: for a group to be complete, `verdictSource.list` must
61
+ * return every attempt of the instance. Its no-limit default walks the full
62
+ * paged corpus; an explicit tight `limit` truncates in enrichment order and
63
+ * can split a group.
64
+ */
65
+ groupCap?: number;
66
+ limit?: number;
67
+ now?: () => Date;
68
+ }
69
+ export interface PipelineResult {
70
+ bridge: BridgeResult;
71
+ clusterCount: number;
72
+ distilled: DistillResult;
73
+ /**
74
+ * True when the verdict fetch returned exactly `limit` rows — a truncation
75
+ * signal. Under group retention (#1478) a truncated fetch means attempt groups
76
+ * may be PARTIAL (the source pages by enrichment order, not by instance), so
77
+ * `groupSize`/`nPass`/`nFail` understate the real group. The CLI surfaces this.
78
+ */
79
+ verdictsTruncated: boolean;
80
+ /** Stage-2 result — present only when `meta` was enabled. */
81
+ metaDistilled?: MetaDistillResult;
82
+ }
83
+ export interface PreparedDistillationEvidence {
84
+ bridge: BridgeResult;
85
+ collected: Array<{
86
+ ref: string;
87
+ instanceId: string;
88
+ env: TraceEnvelopeV0;
89
+ }>;
90
+ clusters: DistillCluster[];
91
+ verdictsTruncated: boolean;
92
+ }
93
+ export declare function prepareDistillationEvidence(deps: Pick<PipelineDeps, 'verdictSource' | 'fetchEvidence' | 'publishDeps' | 'anchorMode' | 'measurePerRecordControl' | 'slate' | 'groupCap' | 'limit' | 'now'>): Promise<PreparedDistillationEvidence>;
94
+ export declare function runDistillationPipeline(deps: PipelineDeps): Promise<PipelineResult>;
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Distillation pipeline orchestrator (spec/2026-07-06-distillation-v1.md §6–§8).
3
+ *
4
+ * Composes the run-time loop end to end: verdict source → **bridge** (ledger →
5
+ * layer-1 evidence, both polarities, exclusion + dedup) → **gate + cluster**
6
+ * (eligible evidence grouped by distinct instance/tier) → **distill** (patterns
7
+ * + lessons → layer-2 skills). Every I/O boundary is an injected port, so the
8
+ * whole loop runs green under stubs (the Tier-0 dry-run) and swaps to the real
9
+ * adapters (verdict-source / fetchEvidence / claude distiller / publishSkill) +
10
+ * `ANTHROPIC_API_KEY` for a Tier-1 run with no code change.
11
+ *
12
+ * The bridge and distiller are decoupled in production through the corpus; here
13
+ * the pipeline captures each bridged envelope via the `publishEvidence` port and
14
+ * hands the same envelopes to clustering, so it reuses the tested `bridgeAttempts`
15
+ * (exclusion/dedup) without a corpus round-trip or a double fetch.
16
+ */
17
+ import { buildBridgeManifestPublisher, bridgeAttempts, } from './bridge.js';
18
+ import { capture } from './capture.js';
19
+ import { publish, } from './publish.js';
20
+ import { buildLayer2ScrubPipeline } from '@jinn-network/core/scrub';
21
+ import { parseTraceEnvelopeV0 } from './envelope.js';
22
+ import { clusterEvidence, buildMetaClusters } from './cluster.js';
23
+ import { distillClusters, metaDistill } from './distill.js';
24
+ export async function prepareDistillationEvidence(deps) {
25
+ const layer2 = buildLayer2ScrubPipeline();
26
+ const slateIds = deps.slate.instanceIds;
27
+ const collected = [];
28
+ // The bridge's publish port: capture at layer-2 altitude, publish layer-1
29
+ // evidence, and collect the envelope for the gate/cluster stage.
30
+ const publishEvidence = async (task, ref) => {
31
+ const pending = await capture(task, { pipeline: layer2 });
32
+ const env = parseTraceEnvelopeV0({
33
+ ...pending.draft,
34
+ consent: { contributionConsent: true, scrubCompleted: true },
35
+ });
36
+ const published = await publish(pending, deps.publishDeps);
37
+ if (published.vetoed)
38
+ throw new Error('unexpected veto bridging evidence');
39
+ collected.push({ ref: published.envelopeRef, instanceId: ref.instanceId, env });
40
+ return { envelopeRef: published.envelopeRef, anchorTx: published.anchorTx };
41
+ };
42
+ let publishManifestBatch;
43
+ if (deps.anchorMode === 'manifest') {
44
+ if (!('publishManifestBody' in deps.publishDeps) ||
45
+ !('anchorManifest' in deps.publishDeps) ||
46
+ !('recordManifestAnchor' in deps.publishDeps)) {
47
+ throw new Error('manifest anchor mode requires manifest-capable publish deps');
48
+ }
49
+ const manifestDeps = deps.publishDeps;
50
+ const publisher = buildBridgeManifestPublisher(manifestDeps, {
51
+ pipeline: layer2,
52
+ ...(deps.measurePerRecordControl ? { measurePerRecordControl: true } : {}),
53
+ });
54
+ publishManifestBatch = async (candidates) => {
55
+ const result = await publisher(candidates);
56
+ if (result.publishedMembers.length !== result.memberRefs.length) {
57
+ throw new Error(`manifest publisher returned ${result.publishedMembers.length} envelopes for ${result.memberRefs.length} refs`);
58
+ }
59
+ for (let index = 0; index < result.publishedMembers.length; index += 1) {
60
+ const member = result.publishedMembers[index];
61
+ const candidate = candidates[index];
62
+ collected.push({
63
+ ref: result.memberRefs[index],
64
+ instanceId: candidate.ref.instanceId,
65
+ env: member.trace,
66
+ });
67
+ }
68
+ return result;
69
+ };
70
+ }
71
+ // 1. Bridge — ledger → layer-1 evidence (exclusion + dedup live in bridgeAttempts).
72
+ const refs = await deps.verdictSource.list({ ...(deps.limit !== undefined ? { limit: deps.limit } : {}) });
73
+ const verdictsTruncated = deps.limit !== undefined && refs.length >= deps.limit;
74
+ const bridge = await bridgeAttempts(refs, {
75
+ slateInstanceIds: slateIds,
76
+ fetchEvidence: deps.fetchEvidence,
77
+ publishEvidence,
78
+ anchorMode: deps.anchorMode ?? 'per-record',
79
+ ...(publishManifestBatch ? { publishManifestBatch } : {}),
80
+ groupCap: deps.groupCap ?? 8,
81
+ ...(deps.now ? { now: deps.now } : {}),
82
+ });
83
+ // 2. Gate + cluster — eligible evidence grouped by distinct (instance, tier).
84
+ // heldOut is defence-in-depth over the bridge's own exclusion.
85
+ const clusters = clusterEvidence(collected, { heldOut: (id) => slateIds.has(id) });
86
+ return { bridge, collected, clusters, verdictsTruncated };
87
+ }
88
+ export async function runDistillationPipeline(deps) {
89
+ const prepared = await prepareDistillationEvidence(deps);
90
+ const { bridge, clusters, verdictsTruncated } = prepared;
91
+ // 3. Distill — patterns + lessons → layer-2 skills (output scrub + contamination scan inside).
92
+ const distilled = await distillClusters(clusters, {
93
+ distill: deps.distill,
94
+ publishSkill: deps.publishSkill,
95
+ slate: deps.slate,
96
+ ...(deps.distribution ? { distribution: deps.distribution } : {}),
97
+ ...(deps.distillModel ? { distillModel: deps.distillModel } : {}),
98
+ ...(deps.now ? { now: deps.now } : {}),
99
+ });
100
+ // 4. Stage-2 (opt-in) — cross-instance meta-distill over the stage-1 skills
101
+ // this run just published. Reads only in-memory results (no corpus round-trip).
102
+ let metaDistilled;
103
+ if (deps.meta) {
104
+ if (!deps.metaDistill) {
105
+ throw new Error('runDistillationPipeline: meta is enabled but no metaDistill port was provided');
106
+ }
107
+ const clusterById = new Map(clusters.map((c) => [c.clusterId, c]));
108
+ const stage1 = distilled.published.map((p) => {
109
+ const cl = clusterById.get(p.clusterId);
110
+ if (!cl)
111
+ throw new Error(`meta: no originating cluster for ${p.clusterId}`);
112
+ return { clusterId: p.clusterId, skillKind: p.skillKind, pkg: p.pkg, evidenceRefs: cl.evidenceRefs, instanceIds: cl.instanceIds };
113
+ });
114
+ metaDistilled = await metaDistill(buildMetaClusters(stage1), {
115
+ metaDistill: deps.metaDistill,
116
+ publishSkill: deps.publishSkill,
117
+ slate: deps.slate,
118
+ ...(deps.distribution ? { distribution: deps.distribution } : {}),
119
+ ...(deps.distillModel ? { distillModel: deps.distillModel } : {}),
120
+ ...(deps.now ? { now: deps.now } : {}),
121
+ });
122
+ }
123
+ return { bridge, clusterCount: clusters.length, distilled, verdictsTruncated, ...(metaDistilled ? { metaDistilled } : {}) };
124
+ }
@@ -0,0 +1,4 @@
1
+ import { type JinnPlugin, type JinnPluginDeps } from '@jinn-network/plugin';
2
+ /** Assemble the real port set used by versioned process hosts. */
3
+ export declare function buildPluginDepsFromEnv(overrides?: Partial<JinnPluginDeps>): JinnPluginDeps;
4
+ export declare function buildPluginFromEnv(overrides?: Partial<JinnPluginDeps>): JinnPlugin;
@@ -0,0 +1,54 @@
1
+ import { createJinnPlugin } from '@jinn-network/plugin';
2
+ import { createContributionAdapter, createCorpusAdapter, createEvidenceAdapter, createLocalLearningAdapter, createSkillsAdapter, } from './adapters/index.js';
3
+ import { ContributionStore, defaultEvidenceIndexPath, reindexEvidenceStore, resolveContributionStateDir, } from '@jinn-network/core';
4
+ import { DEFAULT_EPISODES_DIR, DEFAULT_SKILLS_INSTALL_DIR, localSkillProvenance, stagingDirFor, } from './distill-captures.js';
5
+ import { buildDefaultLayer } from './layer-default.js';
6
+ function episodesDir() {
7
+ return process.env['JINN_LAYER_EPISODES_DIR'] ?? DEFAULT_EPISODES_DIR;
8
+ }
9
+ function skillsInstallDir() {
10
+ return process.env['JINN_LAYER_SKILLS_INSTALL_DIR'] ?? DEFAULT_SKILLS_INSTALL_DIR;
11
+ }
12
+ /** Assemble the real port set used by versioned process hosts. */
13
+ export function buildPluginDepsFromEnv(overrides = {}) {
14
+ const corpus = overrides.corpus ?? createCorpusAdapter({ layer: buildDefaultLayer() });
15
+ const evidenceDir = episodesDir();
16
+ const evidenceIndexPath = process.env['JINN_LAYER_EVIDENCE_INDEX_PATH']
17
+ ?? defaultEvidenceIndexPath(evidenceDir);
18
+ let refreshIndex = Promise.resolve();
19
+ const evidence = overrides.evidence ?? createEvidenceAdapter({
20
+ capturesDir: evidenceDir,
21
+ onStoreChanged: () => {
22
+ const refresh = () => {
23
+ const report = reindexEvidenceStore({
24
+ episodesDir: evidenceDir,
25
+ indexPath: evidenceIndexPath,
26
+ });
27
+ if (!report.indexUpdated) {
28
+ throw new Error(report.indexError ?? 'derived evidence index was not updated');
29
+ }
30
+ };
31
+ refreshIndex = refreshIndex.then(refresh, refresh);
32
+ return refreshIndex;
33
+ },
34
+ });
35
+ const contribution = overrides.contribution ?? createContributionAdapter({
36
+ statusStore: new ContributionStore({ stateDir: resolveContributionStateDir() }),
37
+ evidence,
38
+ });
39
+ const skills = overrides.skills ?? createSkillsAdapter({ installDir: skillsInstallDir() });
40
+ const localLearning = overrides.localLearning ?? createLocalLearningAdapter({
41
+ distiller: {
42
+ distill: async () => ({
43
+ clusterCount: 0,
44
+ distilled: { published: [], rejected: [], errors: [] },
45
+ }),
46
+ },
47
+ loadCaptures: async () => [],
48
+ listSkills: async () => localSkillProvenance(skillsInstallDir(), stagingDirFor(skillsInstallDir())),
49
+ });
50
+ return { corpus, evidence, contribution, localLearning, skills };
51
+ }
52
+ export function buildPluginFromEnv(overrides = {}) {
53
+ return createJinnPlugin(buildPluginDepsFromEnv(overrides));
54
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Harness-layer scrub preview — "see exactly what would leave your machine".
3
+ *
4
+ * `preview(pending)` renders a `PendingEnvelope` as a `ScrubReport`: the
5
+ * canonical EpisodeV1 payload exactly as it would publish, plus the redaction
6
+ * diff. Consent is the publish action; it is not duplicated inside the shared
7
+ * local/public evidence payload.
8
+ *
9
+ * `redactions[].before` values are the original (sensitive) content: local
10
+ * display only, NEVER persisted — anything that serialises a report for
11
+ * storage or transport must strip them (`stripBeforeValues`).
12
+ *
13
+ * Plan: docs/superpowers/plans/2026-07-02-jinn-harness-network-v0-plan.md
14
+ * Task 3 (issue #1310).
15
+ */
16
+ import type { EpisodeV1Write } from '@jinn-network/plugin';
17
+ import type { PendingEnvelope, ScrubRedaction } from './capture.js';
18
+ export interface ScrubReport {
19
+ /** The envelope exactly as it would publish (consent flags asserted). */
20
+ envelope: EpisodeV1Write;
21
+ /** The redaction diff. `before` is local-display-only, never persisted. */
22
+ redactions: ScrubRedaction[];
23
+ }
24
+ /** Render the pending envelope's scrub report. Pure and local — no I/O. */
25
+ export declare function preview(pending: PendingEnvelope): ScrubReport;
26
+ /**
27
+ * The persistence-safe projection of a redaction list: identical minus every
28
+ * `before` value. Use this for ANY serialised output (e.g. `--json`) — the
29
+ * originals exist for the operator's eyes on their own terminal, nothing else.
30
+ */
31
+ export declare function stripBeforeValues(redactions: ScrubRedaction[]): Array<Omit<ScrubRedaction, 'before'>>;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Harness-layer scrub preview — "see exactly what would leave your machine".
3
+ *
4
+ * `preview(pending)` renders a `PendingEnvelope` as a `ScrubReport`: the
5
+ * canonical EpisodeV1 payload exactly as it would publish, plus the redaction
6
+ * diff. Consent is the publish action; it is not duplicated inside the shared
7
+ * local/public evidence payload.
8
+ *
9
+ * `redactions[].before` values are the original (sensitive) content: local
10
+ * display only, NEVER persisted — anything that serialises a report for
11
+ * storage or transport must strip them (`stripBeforeValues`).
12
+ *
13
+ * Plan: docs/superpowers/plans/2026-07-02-jinn-harness-network-v0-plan.md
14
+ * Task 3 (issue #1310).
15
+ */
16
+ import { toPublishedEpisode } from './publish.js';
17
+ /** Render the pending envelope's scrub report. Pure and local — no I/O. */
18
+ export function preview(pending) {
19
+ return { envelope: toPublishedEpisode(pending), redactions: pending.redactions };
20
+ }
21
+ /**
22
+ * The persistence-safe projection of a redaction list: identical minus every
23
+ * `before` value. Use this for ANY serialised output (e.g. `--json`) — the
24
+ * originals exist for the operator's eyes on their own terminal, nothing else.
25
+ */
26
+ export function stripBeforeValues(redactions) {
27
+ return redactions.map(({ before: _before, ...rest }) => rest);
28
+ }
@@ -0,0 +1,303 @@
1
+ import { z } from 'zod';
2
+ import { type FirstTurnPickupResult, type ContributionLedgerEntry, type JinnPluginDeps, type SessionEndResult } from '@jinn-network/plugin';
3
+ export declare const PROCESS_CONTRACT_VERSION: 1;
4
+ export declare const PROCESS_STATUSES: readonly ["ok", "degraded", "unavailable"];
5
+ export type ProcessStatus = (typeof PROCESS_STATUSES)[number];
6
+ export interface ProcessEnvelope<T> {
7
+ contractVersion: typeof PROCESS_CONTRACT_VERSION;
8
+ status: ProcessStatus;
9
+ value?: T;
10
+ reason?: string;
11
+ }
12
+ export declare const SessionPickupRequestV1Schema: z.ZodObject<{
13
+ contractVersion: z.ZodLiteral<1>;
14
+ meta: z.ZodObject<{
15
+ sessionId: z.ZodString;
16
+ taskSummary: z.ZodString;
17
+ distributionTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
18
+ harness: z.ZodObject<{
19
+ name: z.ZodString;
20
+ version: z.ZodString;
21
+ }, z.core.$strict>;
22
+ model: z.ZodString;
23
+ tools: z.ZodArray<z.ZodString>;
24
+ skillsLoadout: z.ZodOptional<z.ZodArray<z.ZodString>>;
25
+ pickup: z.ZodOptional<z.ZodObject<{
26
+ enabled: z.ZodDefault<z.ZodBoolean>;
27
+ autoAdopt: z.ZodDefault<z.ZodBoolean>;
28
+ autoAdoptTier: z.ZodDefault<z.ZodEnum<{
29
+ "user-accepted": "user-accepted";
30
+ "tests-passed": "tests-passed";
31
+ "evaluator-verified": "evaluator-verified";
32
+ }>>;
33
+ maxCandidates: z.ZodDefault<z.ZodNumber>;
34
+ }, z.core.$strict>>;
35
+ repositorySlug: z.ZodOptional<z.ZodString>;
36
+ kind: z.ZodOptional<z.ZodEnum<{
37
+ user: "user";
38
+ "host-internal": "host-internal";
39
+ }>>;
40
+ parentSessionId: z.ZodOptional<z.ZodString>;
41
+ }, z.core.$strict>;
42
+ firstMessage: z.ZodString;
43
+ }, z.core.$strict>;
44
+ export declare const SessionEndRequestV1Schema: z.ZodObject<{
45
+ contractVersion: z.ZodLiteral<1>;
46
+ episode: z.ZodObject<{
47
+ schemaVersion: z.ZodLiteral<"jinn.episode.v1">;
48
+ episodeId: z.ZodString;
49
+ retrievalVisible: z.ZodDefault<z.ZodBoolean>;
50
+ session: z.ZodObject<{
51
+ sessionId: z.ZodString;
52
+ capturedAt: z.ZodISODateTime;
53
+ kind: z.ZodEnum<{
54
+ user: "user";
55
+ "host-internal": "host-internal";
56
+ }>;
57
+ parentSessionId: z.ZodOptional<z.ZodString>;
58
+ }, z.core.$strict>;
59
+ origin: z.ZodObject<{
60
+ writer: z.ZodString;
61
+ build: z.ZodString;
62
+ }, z.core.$strict>;
63
+ task: z.ZodObject<{
64
+ summary: z.ZodString;
65
+ distributionTags: z.ZodDefault<z.ZodArray<z.ZodString>>;
66
+ repositorySlug: z.ZodOptional<z.ZodString>;
67
+ baseCommit: z.ZodOptional<z.ZodString>;
68
+ createdAt: z.ZodOptional<z.ZodNumber>;
69
+ instanceId: z.ZodOptional<z.ZodString>;
70
+ }, z.core.$strict>;
71
+ trajectory: z.ZodArray<z.ZodObject<{
72
+ spanId: z.ZodString;
73
+ parentSpanId: z.ZodNullable<z.ZodString>;
74
+ kind: z.ZodEnum<{
75
+ "jinn.agent_turn": "jinn.agent_turn";
76
+ "jinn.tool_call": "jinn.tool_call";
77
+ }>;
78
+ name: z.ZodString;
79
+ startTimeUnixNano: z.ZodString;
80
+ endTimeUnixNano: z.ZodString;
81
+ attributes: z.ZodRecord<z.ZodString, z.ZodUnknown>;
82
+ redactedKeys: z.ZodDefault<z.ZodArray<z.ZodString>>;
83
+ truncatedKeys: z.ZodOptional<z.ZodArray<z.ZodString>>;
84
+ events: z.ZodOptional<z.ZodArray<z.ZodObject<{
85
+ timeUnixNano: z.ZodString;
86
+ name: z.ZodString;
87
+ attributes: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
88
+ }, z.core.$strict>>>;
89
+ status: z.ZodOptional<z.ZodObject<{
90
+ code: z.ZodEnum<{
91
+ UNSET: "UNSET";
92
+ OK: "OK";
93
+ ERROR: "ERROR";
94
+ }>;
95
+ message: z.ZodOptional<z.ZodString>;
96
+ }, z.core.$strict>>;
97
+ }, z.core.$strict>>;
98
+ environment: z.ZodObject<{
99
+ harness: z.ZodObject<{
100
+ name: z.ZodString;
101
+ version: z.ZodString;
102
+ }, z.core.$strict>;
103
+ model: z.ZodString;
104
+ tools: z.ZodArray<z.ZodString>;
105
+ skillsLoadout: z.ZodArray<z.ZodString>;
106
+ generatorModel: z.ZodOptional<z.ZodObject<{
107
+ id: z.ZodString;
108
+ provider: z.ZodOptional<z.ZodString>;
109
+ openWeights: z.ZodOptional<z.ZodBoolean>;
110
+ source: z.ZodEnum<{
111
+ stream: "stream";
112
+ config: "config";
113
+ }>;
114
+ }, z.core.$strict>>;
115
+ distributionClass: z.ZodOptional<z.ZodEnum<{
116
+ unknown: "unknown";
117
+ open: "open";
118
+ "restricted-tos": "restricted-tos";
119
+ }>>;
120
+ verifier: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
121
+ type: z.ZodLiteral<"f2p-p2p">;
122
+ failToPass: z.ZodArray<z.ZodString>;
123
+ passToPass: z.ZodArray<z.ZodString>;
124
+ evalSemanticsVersion: z.ZodString;
125
+ }, z.core.$strict>, z.ZodObject<{
126
+ type: z.ZodEnum<{
127
+ command: "command";
128
+ none: "none";
129
+ }>;
130
+ failToPass: z.ZodDefault<z.ZodArray<z.ZodString>>;
131
+ passToPass: z.ZodDefault<z.ZodArray<z.ZodString>>;
132
+ evalSemanticsVersion: z.ZodOptional<z.ZodString>;
133
+ }, z.core.$strict>], "type">>;
134
+ }, z.core.$strict>;
135
+ outcome: z.ZodPreprocess<z.ZodObject<{
136
+ status: z.ZodEnum<{
137
+ completed: "completed";
138
+ failed: "failed";
139
+ abandoned: "abandoned";
140
+ }>;
141
+ verificationStrength: z.ZodEnum<{
142
+ "user-accepted": "user-accepted";
143
+ "tests-passed": "tests-passed";
144
+ "evaluator-verified": "evaluator-verified";
145
+ }>;
146
+ summary: z.ZodOptional<z.ZodString>;
147
+ acceptedDiff: z.ZodOptional<z.ZodBoolean>;
148
+ testRuns: z.ZodOptional<z.ZodObject<{
149
+ passed: z.ZodNumber;
150
+ failed: z.ZodNumber;
151
+ }, z.core.$strict>>;
152
+ }, z.core.$strict>>;
153
+ cost: z.ZodObject<{
154
+ durationMs: z.ZodNumber;
155
+ tokens: z.ZodOptional<z.ZodObject<{
156
+ input: z.ZodNumber;
157
+ output: z.ZodNumber;
158
+ }, z.core.$strict>>;
159
+ usdEstimate: z.ZodOptional<z.ZodString>;
160
+ }, z.core.$strict>;
161
+ retention: z.ZodObject<{
162
+ policy: z.ZodEnum<{
163
+ "local-private": "local-private";
164
+ "contribution-eligible": "contribution-eligible";
165
+ }>;
166
+ }, z.core.$strict>;
167
+ provenance: z.ZodDefault<z.ZodEnum<{
168
+ contributed: "contributed";
169
+ imported: "imported";
170
+ "derived-from-history": "derived-from-history";
171
+ }>>;
172
+ lineage: z.ZodOptional<z.ZodObject<{
173
+ episodeId: z.ZodString;
174
+ mintRef: z.ZodOptional<z.ZodString>;
175
+ }, z.core.$strict>>;
176
+ attemptGroup: z.ZodOptional<z.ZodObject<{
177
+ groupId: z.ZodString;
178
+ attemptId: z.ZodString;
179
+ relatedAttemptRefs: z.ZodDefault<z.ZodArray<z.ZodString>>;
180
+ groupSize: z.ZodOptional<z.ZodNumber>;
181
+ nPass: z.ZodOptional<z.ZodNumber>;
182
+ nFail: z.ZodOptional<z.ZodNumber>;
183
+ }, z.core.$strict>>;
184
+ activity: z.ZodOptional<z.ZodObject<{
185
+ retrievalFired: z.ZodBoolean;
186
+ eligibleRefs: z.ZodArray<z.ZodString>;
187
+ deliveredRefs: z.ZodArray<z.ZodString>;
188
+ deliveryMode: z.ZodEnum<{
189
+ degraded: "degraded";
190
+ disabled: "disabled";
191
+ delivered: "delivered";
192
+ withheld: "withheld";
193
+ }>;
194
+ deliveredContentHash: z.ZodOptional<z.ZodString>;
195
+ searchedTerms: z.ZodDefault<z.ZodArray<z.ZodString>>;
196
+ providedRefs: z.ZodDefault<z.ZodArray<z.ZodString>>;
197
+ surfacedRefs: z.ZodDefault<z.ZodArray<z.ZodString>>;
198
+ fetchedRefs: z.ZodDefault<z.ZodArray<z.ZodString>>;
199
+ installedSkillRefs: z.ZodDefault<z.ZodArray<z.ZodString>>;
200
+ }, z.core.$strict>>;
201
+ eligibility: z.ZodOptional<z.ZodObject<{
202
+ eligible: z.ZodBoolean;
203
+ reason: z.ZodString;
204
+ checkedAt: z.ZodISODateTime;
205
+ }, z.core.$strict>>;
206
+ contributionCandidate: z.ZodOptional<z.ZodObject<{
207
+ testRuns: z.ZodArray<z.ZodObject<{
208
+ command: z.ZodString;
209
+ exitCode: z.ZodNumber;
210
+ at: z.ZodISODateTime;
211
+ }, z.core.$strict>>;
212
+ skillEvents: z.ZodArray<z.ZodObject<{
213
+ skillRef: z.ZodString;
214
+ action: z.ZodEnum<{
215
+ loaded: "loaded";
216
+ invoked: "invoked";
217
+ }>;
218
+ }, z.core.$strict>>;
219
+ schemaVersion: z.ZodLiteral<"jinn.contribution-candidate.v1">;
220
+ sourceId: z.ZodString;
221
+ repositorySlug: z.ZodString;
222
+ baseCommit: z.ZodString;
223
+ acceptedDiff: z.ZodString;
224
+ intermediateFailureDiffs: z.ZodArray<z.ZodString>;
225
+ publishMinedTasksConsent: z.ZodBoolean;
226
+ createdAt: z.ZodISODateTime;
227
+ }, z.core.$strict>>;
228
+ }, z.core.$strict>;
229
+ activity: z.ZodObject<{
230
+ retrievalFired: z.ZodBoolean;
231
+ eligibleRefs: z.ZodArray<z.ZodString>;
232
+ deliveredRefs: z.ZodArray<z.ZodString>;
233
+ deliveryMode: z.ZodEnum<{
234
+ degraded: "degraded";
235
+ disabled: "disabled";
236
+ delivered: "delivered";
237
+ withheld: "withheld";
238
+ }>;
239
+ deliveredContentHash: z.ZodOptional<z.ZodString>;
240
+ searchedTerms: z.ZodDefault<z.ZodArray<z.ZodString>>;
241
+ providedRefs: z.ZodDefault<z.ZodArray<z.ZodString>>;
242
+ surfacedRefs: z.ZodDefault<z.ZodArray<z.ZodString>>;
243
+ fetchedRefs: z.ZodDefault<z.ZodArray<z.ZodString>>;
244
+ installedSkillRefs: z.ZodDefault<z.ZodArray<z.ZodString>>;
245
+ }, z.core.$strict>;
246
+ eligibilityInputs: z.ZodObject<{
247
+ publicRepo: z.ZodOptional<z.ZodBoolean>;
248
+ acceptedDiff: z.ZodOptional<z.ZodBoolean>;
249
+ }, z.core.$strict>;
250
+ contributionCandidate: z.ZodOptional<z.ZodObject<{
251
+ testRuns: z.ZodArray<z.ZodObject<{
252
+ command: z.ZodString;
253
+ exitCode: z.ZodNumber;
254
+ at: z.ZodISODateTime;
255
+ }, z.core.$strict>>;
256
+ skillEvents: z.ZodArray<z.ZodObject<{
257
+ skillRef: z.ZodString;
258
+ action: z.ZodEnum<{
259
+ loaded: "loaded";
260
+ invoked: "invoked";
261
+ }>;
262
+ }, z.core.$strict>>;
263
+ schemaVersion: z.ZodLiteral<"jinn.contribution-candidate.v1">;
264
+ sourceId: z.ZodString;
265
+ repositorySlug: z.ZodString;
266
+ baseCommit: z.ZodString;
267
+ acceptedDiff: z.ZodString;
268
+ intermediateFailureDiffs: z.ZodArray<z.ZodString>;
269
+ publishMinedTasksConsent: z.ZodBoolean;
270
+ createdAt: z.ZodISODateTime;
271
+ }, z.core.$strict>>;
272
+ contributionVetoed: z.ZodOptional<z.ZodBoolean>;
273
+ }, z.core.$strict>;
274
+ export type SessionPickupRequestV1 = z.infer<typeof SessionPickupRequestV1Schema>;
275
+ export type SessionEndRequestV1 = z.infer<typeof SessionEndRequestV1Schema>;
276
+ export declare function envelope<T>(status: ProcessStatus, value?: T, reason?: string): ProcessEnvelope<T>;
277
+ export declare function sessionEndEnvelope(result: SessionEndResult): ProcessEnvelope<SessionEndResult>;
278
+ /** Track pickup port health without repeating requests merely to classify output. */
279
+ export declare function trackingCorpus(deps: JinnPluginDeps): {
280
+ deps: JinnPluginDeps;
281
+ status: () => ProcessStatus;
282
+ reason: () => string | undefined;
283
+ };
284
+ export type SessionPickupEnvelope = ProcessEnvelope<FirstTurnPickupResult>;
285
+ /**
286
+ * Preserve useful pickup evidence when one candidate-local corpus read is
287
+ * unavailable. A wholly unavailable corpus with no packets remains
288
+ * `unavailable`; partial success is `degraded` so hosts still consume the
289
+ * returned packets. Core-caught promise rejections also surface through the
290
+ * result's degraded reason even when the tracking wrapper observed no
291
+ * PortResult.
292
+ */
293
+ export declare function sessionPickupEnvelope(result: FirstTurnPickupResult, observedStatus: ProcessStatus, observedReason?: string): SessionPickupEnvelope;
294
+ export interface ContributionLedgerRowV1 {
295
+ time: string | null;
296
+ task: string;
297
+ env: string | null;
298
+ anchor: string | null;
299
+ tier: 'user-accepted' | 'tests-passed' | null;
300
+ state: ContributionLedgerEntry['status'];
301
+ }
302
+ /** Persistence-safe terminal projection. Local IDs and candidate evidence do not cross it. */
303
+ export declare function contributionLedgerRow(entry: ContributionLedgerEntry): ContributionLedgerRowV1;