@jinn-network/plugin 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.
- package/dist/eligibility.d.ts +12 -0
- package/dist/eligibility.js +12 -0
- package/dist/history.d.ts +35 -0
- package/dist/history.js +140 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +15 -0
- package/dist/outcome.d.ts +26 -0
- package/dist/outcome.js +23 -0
- package/dist/pickup.d.ts +101 -0
- package/dist/pickup.js +354 -0
- package/dist/plugin.d.ts +157 -0
- package/dist/plugin.js +586 -0
- package/dist/ports/contribution-port.d.ts +53 -0
- package/dist/ports/contribution-port.js +14 -0
- package/dist/ports/corpus-port.d.ts +63 -0
- package/dist/ports/corpus-port.js +1 -0
- package/dist/ports/evidence-port.d.ts +17 -0
- package/dist/ports/evidence-port.js +1 -0
- package/dist/ports/local-learning-port.d.ts +22 -0
- package/dist/ports/local-learning-port.js +1 -0
- package/dist/ports/skills-port.d.ts +12 -0
- package/dist/ports/skills-port.js +1 -0
- package/dist/schemas/contribution-candidate.d.ts +116 -0
- package/dist/schemas/contribution-candidate.js +63 -0
- package/dist/schemas/eligibility-verdict.d.ts +8 -0
- package/dist/schemas/eligibility-verdict.js +7 -0
- package/dist/schemas/episode.d.ts +432 -0
- package/dist/schemas/episode.js +452 -0
- package/dist/schemas/history-entry.d.ts +40 -0
- package/dist/schemas/history-entry.js +30 -0
- package/dist/schemas/knowledge-hit.d.ts +27 -0
- package/dist/schemas/knowledge-hit.js +23 -0
- package/dist/schemas/knowledge-packet.d.ts +79 -0
- package/dist/schemas/knowledge-packet.js +213 -0
- package/dist/schemas/pickup-config.d.ts +20 -0
- package/dist/schemas/pickup-config.js +34 -0
- package/dist/schemas/session-summary.d.ts +17 -0
- package/dist/schemas/session-summary.js +19 -0
- package/dist/testing/contract-kits.d.ts +12 -0
- package/dist/testing/contract-kits.js +201 -0
- package/dist/testing/in-memory-contribution.d.ts +93 -0
- package/dist/testing/in-memory-contribution.js +104 -0
- package/dist/testing/in-memory-corpus.d.ts +47 -0
- package/dist/testing/in-memory-corpus.js +54 -0
- package/dist/testing/in-memory-evidence.d.ts +335 -0
- package/dist/testing/in-memory-evidence.js +23 -0
- package/dist/testing/in-memory-local-learning.d.ts +25 -0
- package/dist/testing/in-memory-local-learning.js +30 -0
- package/dist/testing/in-memory-skills.d.ts +9 -0
- package/dist/testing/in-memory-skills.js +16 -0
- package/dist/testing.d.ts +6 -0
- package/dist/testing.js +8 -0
- package/dist/visibility.d.ts +7 -0
- package/dist/visibility.js +9 -0
- package/package.json +48 -0
- package/process-contract.json +8 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { deriveContributionStatus } from '../ports/contribution-port.js';
|
|
2
|
+
import { ok, unavailable } from '../outcome.js';
|
|
3
|
+
import { ContributionCandidateV1Schema, } from '../schemas/contribution-candidate.js';
|
|
4
|
+
function snapshot(state) {
|
|
5
|
+
return { ...state, status: deriveContributionStatus(state) };
|
|
6
|
+
}
|
|
7
|
+
export class InMemoryContributionPort {
|
|
8
|
+
records = new Map();
|
|
9
|
+
counter = 0;
|
|
10
|
+
async recordMineable(candidate, options) {
|
|
11
|
+
const parsed = ContributionCandidateV1Schema.safeParse(candidate);
|
|
12
|
+
if (!parsed.success)
|
|
13
|
+
return unavailable('invalid contribution candidate');
|
|
14
|
+
this.counter += 1;
|
|
15
|
+
const recordId = `record-${this.counter}`;
|
|
16
|
+
this.records.set(recordId, {
|
|
17
|
+
candidate: parsed.data,
|
|
18
|
+
state: {
|
|
19
|
+
localState: 'recorded',
|
|
20
|
+
publicationState: options?.publicationState ??
|
|
21
|
+
(parsed.data.publishMinedTasksConsent ? 'preview-required' : 'disabled'),
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
return ok({ recordId });
|
|
25
|
+
}
|
|
26
|
+
async ledger() {
|
|
27
|
+
return ok([...this.records.entries()].map(([recordId, record]) => ({
|
|
28
|
+
recordId,
|
|
29
|
+
sourceId: record.candidate.sourceId,
|
|
30
|
+
createdAt: record.candidate.createdAt,
|
|
31
|
+
verifiabilityTier: record.candidate.testRuns.some((run) => run.exitCode === 0)
|
|
32
|
+
? 'tests-passed'
|
|
33
|
+
: 'user-accepted',
|
|
34
|
+
repositorySlug: record.candidate.repositorySlug,
|
|
35
|
+
baseCommit: record.candidate.baseCommit,
|
|
36
|
+
...snapshot(record.state),
|
|
37
|
+
})));
|
|
38
|
+
}
|
|
39
|
+
async mintStatus(recordId) {
|
|
40
|
+
const record = this.records.get(recordId);
|
|
41
|
+
if (!record)
|
|
42
|
+
return unavailable(`no such record: ${recordId}`);
|
|
43
|
+
return ok(snapshot(record.state));
|
|
44
|
+
}
|
|
45
|
+
async authorize(recordId) {
|
|
46
|
+
const record = this.records.get(recordId);
|
|
47
|
+
if (!record)
|
|
48
|
+
return unavailable(`no such record: ${recordId}`);
|
|
49
|
+
if (record.state.publicationState === 'disabled') {
|
|
50
|
+
return unavailable(`publication disabled by consent: ${recordId}`);
|
|
51
|
+
}
|
|
52
|
+
if (record.state.publicationState === 'vetoed') {
|
|
53
|
+
return unavailable(`publication vetoed: ${recordId}`);
|
|
54
|
+
}
|
|
55
|
+
record.state.publicationState = 'queued';
|
|
56
|
+
return ok({ recordId, publicationState: 'queued', status: 'queued' });
|
|
57
|
+
}
|
|
58
|
+
async veto(recordId) {
|
|
59
|
+
const record = this.records.get(recordId);
|
|
60
|
+
if (!record)
|
|
61
|
+
return unavailable(`no such record: ${recordId}`);
|
|
62
|
+
record.state.publicationState = 'vetoed';
|
|
63
|
+
return ok({ recordId, publicationState: 'vetoed', status: 'vetoed' });
|
|
64
|
+
}
|
|
65
|
+
async disableUnpublished() {
|
|
66
|
+
const recordIds = [];
|
|
67
|
+
for (const [recordId, record] of this.records) {
|
|
68
|
+
if (record.state.publicationState === 'preview-required' || record.state.publicationState === 'queued') {
|
|
69
|
+
record.state.publicationState = 'disabled';
|
|
70
|
+
recordIds.push(recordId);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return ok({ recordIds });
|
|
74
|
+
}
|
|
75
|
+
/** Testing control for the sidecar-owned forward state. */
|
|
76
|
+
markMinted(recordId, mintRef) {
|
|
77
|
+
const record = this.records.get(recordId);
|
|
78
|
+
if (!record)
|
|
79
|
+
return;
|
|
80
|
+
record.state.localState = 'minted';
|
|
81
|
+
record.state.mintRef = mintRef;
|
|
82
|
+
}
|
|
83
|
+
/** Testing control for the sidecar-owned terminal local state. */
|
|
84
|
+
markRejected(recordId) {
|
|
85
|
+
const record = this.records.get(recordId);
|
|
86
|
+
if (!record)
|
|
87
|
+
return;
|
|
88
|
+
record.state.localState = 'rejected';
|
|
89
|
+
delete record.state.mintRef;
|
|
90
|
+
if (record.state.publicationState !== 'vetoed')
|
|
91
|
+
record.state.publicationState = 'disabled';
|
|
92
|
+
}
|
|
93
|
+
/** Testing control for the sidecar-owned outbound state. */
|
|
94
|
+
markPublished(recordId, publicationRef) {
|
|
95
|
+
const record = this.records.get(recordId);
|
|
96
|
+
if (!record)
|
|
97
|
+
return;
|
|
98
|
+
record.state.publicationState = 'published';
|
|
99
|
+
record.state.publicationRef = publicationRef;
|
|
100
|
+
}
|
|
101
|
+
getCandidate(recordId) {
|
|
102
|
+
return this.records.get(recordId)?.candidate;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { CorpusPort, CorpusRecord } from '../ports/corpus-port.js';
|
|
2
|
+
import type { KnowledgeHit } from '../schemas/knowledge-hit.js';
|
|
3
|
+
/**
|
|
4
|
+
* A seed record for `InMemoryCorpusPort`: a full content-bearing
|
|
5
|
+
* `CorpusRecord` plus the lightweight `KnowledgeHit` metadata a real corpus
|
|
6
|
+
* search would return for it (kind/title/tier/payloadKind/publishedAt — the
|
|
7
|
+
* fields not derivable from `CorpusRecord` alone).
|
|
8
|
+
*/
|
|
9
|
+
export type InMemoryCorpusSeed = CorpusRecord & Pick<KnowledgeHit, 'kind'> & Partial<Pick<KnowledgeHit, 'title' | 'tier' | 'payloadKind' | 'publishedAt'>>;
|
|
10
|
+
/** Map/array-backed, seedable — architecture spec §8. Seeds carry full
|
|
11
|
+
* content (`CorpusRecord`) so `get()` can return it directly; `search()`
|
|
12
|
+
* derives the lightweight `KnowledgeHit` view. */
|
|
13
|
+
export declare class InMemoryCorpusPort implements CorpusPort {
|
|
14
|
+
private readonly seed;
|
|
15
|
+
constructor(seed?: InMemoryCorpusSeed[]);
|
|
16
|
+
search(query: string): Promise<import("../outcome.js").PortResult<{
|
|
17
|
+
ref: string;
|
|
18
|
+
kind: "seed" | "trace" | "skill";
|
|
19
|
+
tags: string[];
|
|
20
|
+
title?: string | undefined;
|
|
21
|
+
snippet?: string | undefined;
|
|
22
|
+
score?: number | undefined;
|
|
23
|
+
tier?: "user-accepted" | "tests-passed" | "evaluator-verified" | undefined;
|
|
24
|
+
payloadKind?: "unknown" | "skill" | undefined;
|
|
25
|
+
origin?: string | undefined;
|
|
26
|
+
publishedAt?: number | undefined;
|
|
27
|
+
retrievalVisible?: boolean | undefined;
|
|
28
|
+
}[]>>;
|
|
29
|
+
get(ref: string): Promise<{
|
|
30
|
+
status: "unavailable";
|
|
31
|
+
reason: string;
|
|
32
|
+
} | {
|
|
33
|
+
status: "ok";
|
|
34
|
+
value: null;
|
|
35
|
+
} | {
|
|
36
|
+
status: "degraded";
|
|
37
|
+
reason: string;
|
|
38
|
+
value?: null | undefined;
|
|
39
|
+
} | {
|
|
40
|
+
status: "ok";
|
|
41
|
+
value: CorpusRecord;
|
|
42
|
+
} | {
|
|
43
|
+
status: "degraded";
|
|
44
|
+
reason: string;
|
|
45
|
+
value?: CorpusRecord | undefined;
|
|
46
|
+
}>;
|
|
47
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { ok } from '../outcome.js';
|
|
2
|
+
function toKnowledgeHit(seed) {
|
|
3
|
+
return {
|
|
4
|
+
ref: seed.ref,
|
|
5
|
+
kind: seed.kind,
|
|
6
|
+
...(seed.title !== undefined ? { title: seed.title } : {}),
|
|
7
|
+
snippet: seed.task.summary,
|
|
8
|
+
...(seed.tier !== undefined ? { tier: seed.tier } : {}),
|
|
9
|
+
...(seed.payloadKind !== undefined ? { payloadKind: seed.payloadKind } : {}),
|
|
10
|
+
tags: seed.tags,
|
|
11
|
+
origin: seed.origin,
|
|
12
|
+
...(seed.publishedAt !== undefined ? { publishedAt: seed.publishedAt } : {}),
|
|
13
|
+
retrievalVisible: seed.retrievalVisible,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/** Map/array-backed, seedable — architecture spec §8. Seeds carry full
|
|
17
|
+
* content (`CorpusRecord`) so `get()` can return it directly; `search()`
|
|
18
|
+
* derives the lightweight `KnowledgeHit` view. */
|
|
19
|
+
export class InMemoryCorpusPort {
|
|
20
|
+
seed;
|
|
21
|
+
constructor(seed = []) {
|
|
22
|
+
// Seeds default to retrieval-visible (#1824) so pre-allowlist scenarios
|
|
23
|
+
// keep exercising their own concern; a test proving exclusion sets
|
|
24
|
+
// `retrievalVisible: false` explicitly. Normalized once here so the
|
|
25
|
+
// search() and get() views cannot disagree about the default.
|
|
26
|
+
this.seed = seed.map((s) => ({ ...s, retrievalVisible: s.retrievalVisible ?? true }));
|
|
27
|
+
}
|
|
28
|
+
async search(query) {
|
|
29
|
+
// Naive token-overlap match, no ranking — a stand-in for the real
|
|
30
|
+
// corpus's capture-meta / manifest-scan search. Words under 4 chars are
|
|
31
|
+
// dropped so stopwords ("how", "do", "fix") don't cause spurious matches.
|
|
32
|
+
const words = query
|
|
33
|
+
.toLowerCase()
|
|
34
|
+
.split(/[^\p{L}\p{N}]+/u)
|
|
35
|
+
.filter((word) => word.length >= 4);
|
|
36
|
+
const hits = this.seed.filter((record) => {
|
|
37
|
+
const haystack = [record.ref, record.task.summary, ...record.tags]
|
|
38
|
+
.join(' ')
|
|
39
|
+
.toLowerCase();
|
|
40
|
+
return words.length === 0
|
|
41
|
+
? haystack.includes(query.toLowerCase())
|
|
42
|
+
: words.some((word) => haystack.includes(word));
|
|
43
|
+
});
|
|
44
|
+
return ok(hits.map(toKnowledgeHit));
|
|
45
|
+
}
|
|
46
|
+
async get(ref) {
|
|
47
|
+
const record = this.seed.find((r) => r.ref === ref);
|
|
48
|
+
if (!record)
|
|
49
|
+
return ok(null);
|
|
50
|
+
// Strip the KnowledgeHit-only fields — get() returns the CorpusRecord shape.
|
|
51
|
+
const { kind: _kind, title: _title, tier: _tier, payloadKind: _payloadKind, publishedAt: _publishedAt, ...corpusRecord } = record;
|
|
52
|
+
return ok(corpusRecord);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import type { EvidenceListQuery, EvidencePort, EvidenceRetentionPolicy } from '../ports/evidence-port.js';
|
|
2
|
+
import type { EpisodeV1 } from '../schemas/episode.js';
|
|
3
|
+
export declare class InMemoryEvidencePort implements EvidencePort {
|
|
4
|
+
private readonly retentionPolicy;
|
|
5
|
+
private readonly episodes;
|
|
6
|
+
constructor(retentionPolicy?: EvidenceRetentionPolicy);
|
|
7
|
+
put(episode: EpisodeV1): Promise<import("../outcome.js").PortResult<{
|
|
8
|
+
episodeId: string;
|
|
9
|
+
}>>;
|
|
10
|
+
get(episodeId: string): Promise<import("../outcome.js").PortResult<{
|
|
11
|
+
[x: string]: unknown;
|
|
12
|
+
schemaVersion: "jinn.episode.v1";
|
|
13
|
+
episodeId: string;
|
|
14
|
+
retrievalVisible: boolean;
|
|
15
|
+
session: {
|
|
16
|
+
[x: string]: unknown;
|
|
17
|
+
sessionId: string;
|
|
18
|
+
capturedAt: string;
|
|
19
|
+
kind: "user" | "host-internal";
|
|
20
|
+
parentSessionId?: string | undefined;
|
|
21
|
+
};
|
|
22
|
+
origin: "legacy-unstamped" | {
|
|
23
|
+
[x: string]: unknown;
|
|
24
|
+
writer: string;
|
|
25
|
+
build: string;
|
|
26
|
+
};
|
|
27
|
+
task: {
|
|
28
|
+
[x: string]: unknown;
|
|
29
|
+
summary: string;
|
|
30
|
+
distributionTags: string[];
|
|
31
|
+
repositorySlug?: string | undefined;
|
|
32
|
+
baseCommit?: string | undefined;
|
|
33
|
+
createdAt?: number | undefined;
|
|
34
|
+
instanceId?: string | undefined;
|
|
35
|
+
};
|
|
36
|
+
trajectory: {
|
|
37
|
+
[x: string]: unknown;
|
|
38
|
+
spanId: string;
|
|
39
|
+
parentSpanId: string | null;
|
|
40
|
+
kind: "jinn.agent_turn" | "jinn.tool_call";
|
|
41
|
+
name: string;
|
|
42
|
+
startTimeUnixNano: string;
|
|
43
|
+
endTimeUnixNano: string;
|
|
44
|
+
attributes: Record<string, unknown>;
|
|
45
|
+
redactedKeys: string[];
|
|
46
|
+
truncatedKeys?: string[] | undefined;
|
|
47
|
+
events?: {
|
|
48
|
+
timeUnixNano: string;
|
|
49
|
+
name: string;
|
|
50
|
+
attributes?: Record<string, unknown> | undefined;
|
|
51
|
+
}[] | undefined;
|
|
52
|
+
status?: {
|
|
53
|
+
code: "UNSET" | "OK" | "ERROR";
|
|
54
|
+
message?: string | undefined;
|
|
55
|
+
} | undefined;
|
|
56
|
+
}[];
|
|
57
|
+
environment: {
|
|
58
|
+
[x: string]: unknown;
|
|
59
|
+
harness: {
|
|
60
|
+
[x: string]: unknown;
|
|
61
|
+
name: string;
|
|
62
|
+
version: string;
|
|
63
|
+
};
|
|
64
|
+
model: string;
|
|
65
|
+
tools: string[];
|
|
66
|
+
skillsLoadout: string[];
|
|
67
|
+
generatorModel?: {
|
|
68
|
+
[x: string]: unknown;
|
|
69
|
+
id: string;
|
|
70
|
+
source: "stream" | "config";
|
|
71
|
+
provider?: string | undefined;
|
|
72
|
+
openWeights?: boolean | undefined;
|
|
73
|
+
} | undefined;
|
|
74
|
+
distributionClass?: "unknown" | "open" | "restricted-tos" | undefined;
|
|
75
|
+
verifier?: {
|
|
76
|
+
[x: string]: unknown;
|
|
77
|
+
type: "f2p-p2p";
|
|
78
|
+
failToPass: string[];
|
|
79
|
+
passToPass: string[];
|
|
80
|
+
evalSemanticsVersion: string;
|
|
81
|
+
} | {
|
|
82
|
+
[x: string]: unknown;
|
|
83
|
+
type: "command" | "none";
|
|
84
|
+
failToPass: string[];
|
|
85
|
+
passToPass: string[];
|
|
86
|
+
evalSemanticsVersion?: string | undefined;
|
|
87
|
+
} | undefined;
|
|
88
|
+
};
|
|
89
|
+
outcome: {
|
|
90
|
+
[x: string]: unknown;
|
|
91
|
+
status: "completed" | "failed" | "abandoned";
|
|
92
|
+
verificationStrength: "user-accepted" | "tests-passed" | "evaluator-verified";
|
|
93
|
+
testRuns?: {
|
|
94
|
+
[x: string]: unknown;
|
|
95
|
+
passed: number;
|
|
96
|
+
failed: number;
|
|
97
|
+
} | undefined;
|
|
98
|
+
summary?: string | undefined;
|
|
99
|
+
acceptedDiff?: boolean | undefined;
|
|
100
|
+
};
|
|
101
|
+
cost: {
|
|
102
|
+
[x: string]: unknown;
|
|
103
|
+
durationMs: number;
|
|
104
|
+
tokens?: {
|
|
105
|
+
[x: string]: unknown;
|
|
106
|
+
input: number;
|
|
107
|
+
output: number;
|
|
108
|
+
} | undefined;
|
|
109
|
+
usdEstimate?: string | undefined;
|
|
110
|
+
};
|
|
111
|
+
retention: {
|
|
112
|
+
[x: string]: unknown;
|
|
113
|
+
policy: "local-private" | "contribution-eligible";
|
|
114
|
+
};
|
|
115
|
+
provenance: "contributed" | "imported" | "derived-from-history";
|
|
116
|
+
lineage?: {
|
|
117
|
+
[x: string]: unknown;
|
|
118
|
+
episodeId: string;
|
|
119
|
+
mintRef?: string | undefined;
|
|
120
|
+
} | undefined;
|
|
121
|
+
attemptGroup?: {
|
|
122
|
+
[x: string]: unknown;
|
|
123
|
+
groupId: string;
|
|
124
|
+
attemptId: string;
|
|
125
|
+
relatedAttemptRefs: string[];
|
|
126
|
+
groupSize?: number | undefined;
|
|
127
|
+
nPass?: number | undefined;
|
|
128
|
+
nFail?: number | undefined;
|
|
129
|
+
} | undefined;
|
|
130
|
+
activity?: {
|
|
131
|
+
[x: string]: unknown;
|
|
132
|
+
retrievalFired: boolean;
|
|
133
|
+
eligibleRefs: string[];
|
|
134
|
+
deliveredRefs: string[];
|
|
135
|
+
deliveryMode: "degraded" | "disabled" | "delivered" | "withheld";
|
|
136
|
+
searchedTerms: string[];
|
|
137
|
+
providedRefs: string[];
|
|
138
|
+
surfacedRefs: string[];
|
|
139
|
+
fetchedRefs: string[];
|
|
140
|
+
installedSkillRefs: string[];
|
|
141
|
+
deliveredContentHash?: string | undefined;
|
|
142
|
+
} | undefined;
|
|
143
|
+
eligibility?: {
|
|
144
|
+
[x: string]: unknown;
|
|
145
|
+
eligible: boolean;
|
|
146
|
+
reason: string;
|
|
147
|
+
checkedAt: string;
|
|
148
|
+
} | undefined;
|
|
149
|
+
contributionCandidate?: {
|
|
150
|
+
[x: string]: unknown;
|
|
151
|
+
testRuns: {
|
|
152
|
+
[x: string]: unknown;
|
|
153
|
+
command: string;
|
|
154
|
+
exitCode: number;
|
|
155
|
+
at: string;
|
|
156
|
+
}[];
|
|
157
|
+
skillEvents: {
|
|
158
|
+
[x: string]: unknown;
|
|
159
|
+
skillRef: string;
|
|
160
|
+
action: "loaded" | "invoked";
|
|
161
|
+
}[];
|
|
162
|
+
schemaVersion: "jinn.contribution-candidate.v1";
|
|
163
|
+
sourceId: string;
|
|
164
|
+
repositorySlug: string;
|
|
165
|
+
baseCommit: string;
|
|
166
|
+
acceptedDiff: string;
|
|
167
|
+
intermediateFailureDiffs: string[];
|
|
168
|
+
publishMinedTasksConsent: boolean;
|
|
169
|
+
createdAt: string;
|
|
170
|
+
} | undefined;
|
|
171
|
+
} | null>>;
|
|
172
|
+
list(query?: EvidenceListQuery): Promise<import("../outcome.js").PortResult<{
|
|
173
|
+
[x: string]: unknown;
|
|
174
|
+
schemaVersion: "jinn.episode.v1";
|
|
175
|
+
episodeId: string;
|
|
176
|
+
retrievalVisible: boolean;
|
|
177
|
+
session: {
|
|
178
|
+
[x: string]: unknown;
|
|
179
|
+
sessionId: string;
|
|
180
|
+
capturedAt: string;
|
|
181
|
+
kind: "user" | "host-internal";
|
|
182
|
+
parentSessionId?: string | undefined;
|
|
183
|
+
};
|
|
184
|
+
origin: "legacy-unstamped" | {
|
|
185
|
+
[x: string]: unknown;
|
|
186
|
+
writer: string;
|
|
187
|
+
build: string;
|
|
188
|
+
};
|
|
189
|
+
task: {
|
|
190
|
+
[x: string]: unknown;
|
|
191
|
+
summary: string;
|
|
192
|
+
distributionTags: string[];
|
|
193
|
+
repositorySlug?: string | undefined;
|
|
194
|
+
baseCommit?: string | undefined;
|
|
195
|
+
createdAt?: number | undefined;
|
|
196
|
+
instanceId?: string | undefined;
|
|
197
|
+
};
|
|
198
|
+
trajectory: {
|
|
199
|
+
[x: string]: unknown;
|
|
200
|
+
spanId: string;
|
|
201
|
+
parentSpanId: string | null;
|
|
202
|
+
kind: "jinn.agent_turn" | "jinn.tool_call";
|
|
203
|
+
name: string;
|
|
204
|
+
startTimeUnixNano: string;
|
|
205
|
+
endTimeUnixNano: string;
|
|
206
|
+
attributes: Record<string, unknown>;
|
|
207
|
+
redactedKeys: string[];
|
|
208
|
+
truncatedKeys?: string[] | undefined;
|
|
209
|
+
events?: {
|
|
210
|
+
timeUnixNano: string;
|
|
211
|
+
name: string;
|
|
212
|
+
attributes?: Record<string, unknown> | undefined;
|
|
213
|
+
}[] | undefined;
|
|
214
|
+
status?: {
|
|
215
|
+
code: "UNSET" | "OK" | "ERROR";
|
|
216
|
+
message?: string | undefined;
|
|
217
|
+
} | undefined;
|
|
218
|
+
}[];
|
|
219
|
+
environment: {
|
|
220
|
+
[x: string]: unknown;
|
|
221
|
+
harness: {
|
|
222
|
+
[x: string]: unknown;
|
|
223
|
+
name: string;
|
|
224
|
+
version: string;
|
|
225
|
+
};
|
|
226
|
+
model: string;
|
|
227
|
+
tools: string[];
|
|
228
|
+
skillsLoadout: string[];
|
|
229
|
+
generatorModel?: {
|
|
230
|
+
[x: string]: unknown;
|
|
231
|
+
id: string;
|
|
232
|
+
source: "stream" | "config";
|
|
233
|
+
provider?: string | undefined;
|
|
234
|
+
openWeights?: boolean | undefined;
|
|
235
|
+
} | undefined;
|
|
236
|
+
distributionClass?: "unknown" | "open" | "restricted-tos" | undefined;
|
|
237
|
+
verifier?: {
|
|
238
|
+
[x: string]: unknown;
|
|
239
|
+
type: "f2p-p2p";
|
|
240
|
+
failToPass: string[];
|
|
241
|
+
passToPass: string[];
|
|
242
|
+
evalSemanticsVersion: string;
|
|
243
|
+
} | {
|
|
244
|
+
[x: string]: unknown;
|
|
245
|
+
type: "command" | "none";
|
|
246
|
+
failToPass: string[];
|
|
247
|
+
passToPass: string[];
|
|
248
|
+
evalSemanticsVersion?: string | undefined;
|
|
249
|
+
} | undefined;
|
|
250
|
+
};
|
|
251
|
+
outcome: {
|
|
252
|
+
[x: string]: unknown;
|
|
253
|
+
status: "completed" | "failed" | "abandoned";
|
|
254
|
+
verificationStrength: "user-accepted" | "tests-passed" | "evaluator-verified";
|
|
255
|
+
testRuns?: {
|
|
256
|
+
[x: string]: unknown;
|
|
257
|
+
passed: number;
|
|
258
|
+
failed: number;
|
|
259
|
+
} | undefined;
|
|
260
|
+
summary?: string | undefined;
|
|
261
|
+
acceptedDiff?: boolean | undefined;
|
|
262
|
+
};
|
|
263
|
+
cost: {
|
|
264
|
+
[x: string]: unknown;
|
|
265
|
+
durationMs: number;
|
|
266
|
+
tokens?: {
|
|
267
|
+
[x: string]: unknown;
|
|
268
|
+
input: number;
|
|
269
|
+
output: number;
|
|
270
|
+
} | undefined;
|
|
271
|
+
usdEstimate?: string | undefined;
|
|
272
|
+
};
|
|
273
|
+
retention: {
|
|
274
|
+
[x: string]: unknown;
|
|
275
|
+
policy: "local-private" | "contribution-eligible";
|
|
276
|
+
};
|
|
277
|
+
provenance: "contributed" | "imported" | "derived-from-history";
|
|
278
|
+
lineage?: {
|
|
279
|
+
[x: string]: unknown;
|
|
280
|
+
episodeId: string;
|
|
281
|
+
mintRef?: string | undefined;
|
|
282
|
+
} | undefined;
|
|
283
|
+
attemptGroup?: {
|
|
284
|
+
[x: string]: unknown;
|
|
285
|
+
groupId: string;
|
|
286
|
+
attemptId: string;
|
|
287
|
+
relatedAttemptRefs: string[];
|
|
288
|
+
groupSize?: number | undefined;
|
|
289
|
+
nPass?: number | undefined;
|
|
290
|
+
nFail?: number | undefined;
|
|
291
|
+
} | undefined;
|
|
292
|
+
activity?: {
|
|
293
|
+
[x: string]: unknown;
|
|
294
|
+
retrievalFired: boolean;
|
|
295
|
+
eligibleRefs: string[];
|
|
296
|
+
deliveredRefs: string[];
|
|
297
|
+
deliveryMode: "degraded" | "disabled" | "delivered" | "withheld";
|
|
298
|
+
searchedTerms: string[];
|
|
299
|
+
providedRefs: string[];
|
|
300
|
+
surfacedRefs: string[];
|
|
301
|
+
fetchedRefs: string[];
|
|
302
|
+
installedSkillRefs: string[];
|
|
303
|
+
deliveredContentHash?: string | undefined;
|
|
304
|
+
} | undefined;
|
|
305
|
+
eligibility?: {
|
|
306
|
+
[x: string]: unknown;
|
|
307
|
+
eligible: boolean;
|
|
308
|
+
reason: string;
|
|
309
|
+
checkedAt: string;
|
|
310
|
+
} | undefined;
|
|
311
|
+
contributionCandidate?: {
|
|
312
|
+
[x: string]: unknown;
|
|
313
|
+
testRuns: {
|
|
314
|
+
[x: string]: unknown;
|
|
315
|
+
command: string;
|
|
316
|
+
exitCode: number;
|
|
317
|
+
at: string;
|
|
318
|
+
}[];
|
|
319
|
+
skillEvents: {
|
|
320
|
+
[x: string]: unknown;
|
|
321
|
+
skillRef: string;
|
|
322
|
+
action: "loaded" | "invoked";
|
|
323
|
+
}[];
|
|
324
|
+
schemaVersion: "jinn.contribution-candidate.v1";
|
|
325
|
+
sourceId: string;
|
|
326
|
+
repositorySlug: string;
|
|
327
|
+
baseCommit: string;
|
|
328
|
+
acceptedDiff: string;
|
|
329
|
+
intermediateFailureDiffs: string[];
|
|
330
|
+
publishMinedTasksConsent: boolean;
|
|
331
|
+
createdAt: string;
|
|
332
|
+
} | undefined;
|
|
333
|
+
}[]>>;
|
|
334
|
+
retention(): Promise<import("../outcome.js").PortResult<EvidenceRetentionPolicy>>;
|
|
335
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ok } from '../outcome.js';
|
|
2
|
+
const DEFAULT_RETENTION = { policy: 'local-private', maxEpisodes: 200 };
|
|
3
|
+
export class InMemoryEvidencePort {
|
|
4
|
+
retentionPolicy;
|
|
5
|
+
episodes = new Map();
|
|
6
|
+
constructor(retentionPolicy = DEFAULT_RETENTION) {
|
|
7
|
+
this.retentionPolicy = retentionPolicy;
|
|
8
|
+
}
|
|
9
|
+
async put(episode) {
|
|
10
|
+
this.episodes.set(episode.episodeId, episode);
|
|
11
|
+
return ok({ episodeId: episode.episodeId });
|
|
12
|
+
}
|
|
13
|
+
async get(episodeId) {
|
|
14
|
+
return ok(this.episodes.get(episodeId) ?? null);
|
|
15
|
+
}
|
|
16
|
+
async list(query = {}) {
|
|
17
|
+
const all = [...this.episodes.values()];
|
|
18
|
+
return ok(query.limit ? all.slice(0, query.limit) : all);
|
|
19
|
+
}
|
|
20
|
+
async retention() {
|
|
21
|
+
return ok(this.retentionPolicy);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { LocalLearningPort, LocalLearningRun, LocalLearningSkill } from '../ports/local-learning-port.js';
|
|
2
|
+
export declare class InMemoryLocalLearningPort implements LocalLearningPort {
|
|
3
|
+
private readonly runs;
|
|
4
|
+
private readonly learnedSkills;
|
|
5
|
+
private counter;
|
|
6
|
+
run(_input: {
|
|
7
|
+
episodeIds: string[];
|
|
8
|
+
}): Promise<import("../outcome.js").PortResult<{
|
|
9
|
+
runId: string;
|
|
10
|
+
}>>;
|
|
11
|
+
status(runId: string): Promise<{
|
|
12
|
+
status: "unavailable";
|
|
13
|
+
reason: string;
|
|
14
|
+
} | {
|
|
15
|
+
status: "ok";
|
|
16
|
+
value: LocalLearningRun;
|
|
17
|
+
} | {
|
|
18
|
+
status: "degraded";
|
|
19
|
+
reason: string;
|
|
20
|
+
value?: LocalLearningRun | undefined;
|
|
21
|
+
}>;
|
|
22
|
+
list(): Promise<import("../outcome.js").PortResult<LocalLearningRun[]>>;
|
|
23
|
+
recordSkill(skill: LocalLearningSkill): void;
|
|
24
|
+
skills(): Promise<import("../outcome.js").PortResult<LocalLearningSkill[]>>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ok, unavailable } from '../outcome.js';
|
|
2
|
+
export class InMemoryLocalLearningPort {
|
|
3
|
+
runs = new Map();
|
|
4
|
+
learnedSkills = new Map();
|
|
5
|
+
counter = 0;
|
|
6
|
+
async run(_input) {
|
|
7
|
+
this.counter += 1;
|
|
8
|
+
const runId = `run-${this.counter}`;
|
|
9
|
+
this.runs.set(runId, { runId, state: 'pending' });
|
|
10
|
+
return ok({ runId });
|
|
11
|
+
}
|
|
12
|
+
async status(runId) {
|
|
13
|
+
const run = this.runs.get(runId);
|
|
14
|
+
if (!run)
|
|
15
|
+
return unavailable(`no such run: ${runId}`);
|
|
16
|
+
return ok(run);
|
|
17
|
+
}
|
|
18
|
+
async list() {
|
|
19
|
+
return ok([...this.runs.values()]);
|
|
20
|
+
}
|
|
21
|
+
recordSkill(skill) {
|
|
22
|
+
this.learnedSkills.set(skill.ref, {
|
|
23
|
+
...skill,
|
|
24
|
+
sourceSessionIds: [...skill.sourceSessionIds],
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
async skills() {
|
|
28
|
+
return ok([...this.learnedSkills.values()]);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { SkillRecord, SkillsPort } from '../ports/skills-port.js';
|
|
2
|
+
export declare class InMemorySkillsPort implements SkillsPort {
|
|
3
|
+
private readonly installed;
|
|
4
|
+
install(ref: string): Promise<import("../outcome.js").PortResult<SkillRecord>>;
|
|
5
|
+
list(): Promise<import("../outcome.js").PortResult<SkillRecord[]>>;
|
|
6
|
+
uninstall(ref: string): Promise<import("../outcome.js").PortResult<{
|
|
7
|
+
ref: string;
|
|
8
|
+
}>>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ok } from '../outcome.js';
|
|
2
|
+
export class InMemorySkillsPort {
|
|
3
|
+
installed = new Map();
|
|
4
|
+
async install(ref) {
|
|
5
|
+
const record = { ref, installedAt: new Date().toISOString() };
|
|
6
|
+
this.installed.set(ref, record);
|
|
7
|
+
return ok(record);
|
|
8
|
+
}
|
|
9
|
+
async list() {
|
|
10
|
+
return ok([...this.installed.values()]);
|
|
11
|
+
}
|
|
12
|
+
async uninstall(ref) {
|
|
13
|
+
this.installed.delete(ref);
|
|
14
|
+
return ok({ ref });
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { InMemoryCorpusPort } from './testing/in-memory-corpus.js';
|
|
2
|
+
export { InMemoryEvidencePort } from './testing/in-memory-evidence.js';
|
|
3
|
+
export { InMemoryContributionPort } from './testing/in-memory-contribution.js';
|
|
4
|
+
export { InMemoryLocalLearningPort } from './testing/in-memory-local-learning.js';
|
|
5
|
+
export { InMemorySkillsPort } from './testing/in-memory-skills.js';
|
|
6
|
+
export { describeCorpusPortContract, describeEvidencePortContract, describeContributionPortContract, describeLocalLearningPortContract, describeSkillsPortContract, } from './testing/contract-kits.js';
|
package/dist/testing.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// packages/plugin/src/testing.ts
|
|
2
|
+
// ./testing subpath entry — in-memory adapters + contract kits (S1-F1).
|
|
3
|
+
export { InMemoryCorpusPort } from './testing/in-memory-corpus.js';
|
|
4
|
+
export { InMemoryEvidencePort } from './testing/in-memory-evidence.js';
|
|
5
|
+
export { InMemoryContributionPort } from './testing/in-memory-contribution.js';
|
|
6
|
+
export { InMemoryLocalLearningPort } from './testing/in-memory-local-learning.js';
|
|
7
|
+
export { InMemorySkillsPort } from './testing/in-memory-skills.js';
|
|
8
|
+
export { describeCorpusPortContract, describeEvidencePortContract, describeContributionPortContract, describeLocalLearningPortContract, describeSkillsPortContract, } from './testing/contract-kits.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Retrieval-visibility mark (issue #1824, corpus-supply-design §5 W2): a
|
|
2
|
+
* reserved, deterministic distribution tag carried inside the signed
|
|
3
|
+
* jinn.trace-envelope.v0 content (task.distributionTags). Presence is the
|
|
4
|
+
* sole allowlist signal pickup enforcement keys on — absence excludes,
|
|
5
|
+
* fail-closed (opposite of the fail-open skill-detection guards). */
|
|
6
|
+
export declare const RETRIEVAL_VISIBLE_TAG: "retrieval:visible.v1";
|
|
7
|
+
export declare function hasRetrievalMark(tags: readonly string[]): boolean;
|