@jinn-network/plugin 0.1.0-canary.3afae198
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 +53 -0
- package/process-contract.json +8 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Cheap eligibility candidate-verdict (architecture §5). Pure over the
|
|
2
|
+
* SessionOutcome facts the core already holds. Authoritative validation
|
|
3
|
+
* (Docker F2P/P2P, resolvable repo@commit) stays sidecar-side. */
|
|
4
|
+
import type { EligibilityVerdict } from './schemas/eligibility-verdict.js';
|
|
5
|
+
export interface EligibilityInputs {
|
|
6
|
+
status: 'completed' | 'failed' | 'abandoned';
|
|
7
|
+
verifiabilityTier: 'user-accepted' | 'tests-passed' | 'evaluator-verified';
|
|
8
|
+
retentionPolicy: 'local-private' | 'contribution-eligible';
|
|
9
|
+
publicRepo?: boolean;
|
|
10
|
+
acceptedDiff?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare function deriveEligibility(input: EligibilityInputs, checkedAt: string): EligibilityVerdict;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function deriveEligibility(input, checkedAt) {
|
|
2
|
+
if (input.status !== 'completed') {
|
|
3
|
+
return { eligible: false, reason: 'outcome not completed', checkedAt };
|
|
4
|
+
}
|
|
5
|
+
if (input.retentionPolicy !== 'contribution-eligible') {
|
|
6
|
+
return { eligible: false, reason: 'retention is local-private', checkedAt };
|
|
7
|
+
}
|
|
8
|
+
if (!input.acceptedDiff && !input.publicRepo) {
|
|
9
|
+
return { eligible: false, reason: 'no accepted diff signal', checkedAt };
|
|
10
|
+
}
|
|
11
|
+
return { eligible: true, reason: 'completed with accepted-diff/public-repo signal', checkedAt };
|
|
12
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** Derived history + explain views (product design §4.5). Owns no facts —
|
|
2
|
+
* recomputed from Evidence + Contribution + LocalLearning on every call, so
|
|
3
|
+
* deleting any cache leaves output identical (there is no cache). */
|
|
4
|
+
import { type ContributionPort } from './ports/contribution-port.js';
|
|
5
|
+
import type { EvidencePort } from './ports/evidence-port.js';
|
|
6
|
+
import type { LocalLearningPort } from './ports/local-learning-port.js';
|
|
7
|
+
import type { EligibilityVerdict } from './schemas/eligibility-verdict.js';
|
|
8
|
+
import type { HistoryEntry } from './schemas/history-entry.js';
|
|
9
|
+
export interface HistoryDeps {
|
|
10
|
+
evidence: EvidencePort;
|
|
11
|
+
contribution: ContributionPort;
|
|
12
|
+
localLearning: LocalLearningPort;
|
|
13
|
+
}
|
|
14
|
+
export interface HistoryResult {
|
|
15
|
+
entries: HistoryEntry[];
|
|
16
|
+
degraded: boolean;
|
|
17
|
+
unavailable: boolean;
|
|
18
|
+
reason?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface SessionExplanation {
|
|
21
|
+
sessionRef: string;
|
|
22
|
+
found: boolean;
|
|
23
|
+
searchedTerms: string[];
|
|
24
|
+
providedRefs: string[];
|
|
25
|
+
captureStatus: 'captured' | 'not-captured';
|
|
26
|
+
eligibility: EligibilityVerdict | null;
|
|
27
|
+
contributionState: {
|
|
28
|
+
status: HistoryEntry['contributionState']['status'];
|
|
29
|
+
anchorRef?: string;
|
|
30
|
+
};
|
|
31
|
+
degraded: boolean;
|
|
32
|
+
reason?: string;
|
|
33
|
+
}
|
|
34
|
+
export declare function foldHistory(deps: HistoryDeps): Promise<HistoryResult>;
|
|
35
|
+
export declare function foldExplain(sessionRef: string, deps: HistoryDeps): Promise<SessionExplanation>;
|
package/dist/history.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/** Derived history + explain views (product design §4.5). Owns no facts —
|
|
2
|
+
* recomputed from Evidence + Contribution + LocalLearning on every call, so
|
|
3
|
+
* deleting any cache leaves output identical (there is no cache). */
|
|
4
|
+
import { deriveContributionStatus, } from './ports/contribution-port.js';
|
|
5
|
+
import { unwrap } from './outcome.js';
|
|
6
|
+
/** Fail-open list unwrap that records a labelled reason on non-ok reads. */
|
|
7
|
+
function collect(res, label, fallback, reasons) {
|
|
8
|
+
const { value, reason } = unwrap(res, fallback);
|
|
9
|
+
if (reason !== undefined)
|
|
10
|
+
reasons.push(`${label}: ${reason}`);
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
function ledgerByEpisode(ledger) {
|
|
14
|
+
const map = new Map();
|
|
15
|
+
for (const row of ledger)
|
|
16
|
+
map.set(row.sourceId, row);
|
|
17
|
+
return map;
|
|
18
|
+
}
|
|
19
|
+
function contributionState(row, available = true) {
|
|
20
|
+
if (!available)
|
|
21
|
+
return { status: 'unavailable' };
|
|
22
|
+
if (!row)
|
|
23
|
+
return { status: 'none' };
|
|
24
|
+
const anchorRef = row.publicationRef ?? row.mintRef;
|
|
25
|
+
return {
|
|
26
|
+
status: deriveContributionStatus(row),
|
|
27
|
+
...(anchorRef !== undefined ? { anchorRef } : {}),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* New episodes persist the authoritative completion verdict. Historical v1
|
|
32
|
+
* records omit it, so reads stay backward compatible and honestly indeterminate
|
|
33
|
+
* rather than silently manufacturing a verdict from incomplete inputs.
|
|
34
|
+
*/
|
|
35
|
+
function episodeEligibility(ep) {
|
|
36
|
+
return ep.eligibility ?? null;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Canonical searched/provided activity from the new fields, falling back to
|
|
40
|
+
* the legacy surfaced/fetched refs for episodes captured before the rescope
|
|
41
|
+
* (rescope §3.6). An episode is "legacy" here when both new fields are empty
|
|
42
|
+
* AND at least one legacy field is not — a genuinely new episode with a
|
|
43
|
+
* nothing-found result has both sides empty, so the fallback is a no-op for it.
|
|
44
|
+
* Reads defensively (`?? []`): an `EvidencePort` is not required to
|
|
45
|
+
* schema-validate on read (the in-memory test double does not), so a record
|
|
46
|
+
* older than any given field must not crash the fold.
|
|
47
|
+
*/
|
|
48
|
+
function canonicalKnowledgeActivity(activity) {
|
|
49
|
+
if (!activity)
|
|
50
|
+
return { searchedTerms: [], providedRefs: [] };
|
|
51
|
+
const searchedTerms = activity.searchedTerms ?? [];
|
|
52
|
+
const providedRefs = activity.providedRefs ?? [];
|
|
53
|
+
const surfacedRefs = activity.surfacedRefs ?? [];
|
|
54
|
+
const fetchedRefs = activity.fetchedRefs ?? [];
|
|
55
|
+
const installedSkillRefs = activity.installedSkillRefs ?? [];
|
|
56
|
+
const isLegacy = searchedTerms.length === 0
|
|
57
|
+
&& providedRefs.length === 0
|
|
58
|
+
&& (surfacedRefs.length > 0 || fetchedRefs.length > 0 || installedSkillRefs.length > 0);
|
|
59
|
+
return isLegacy
|
|
60
|
+
? { searchedTerms: surfacedRefs, providedRefs: fetchedRefs }
|
|
61
|
+
: { searchedTerms, providedRefs };
|
|
62
|
+
}
|
|
63
|
+
function knowledgeCounts(activity) {
|
|
64
|
+
const canonical = canonicalKnowledgeActivity(activity);
|
|
65
|
+
return { surfaced: canonical.searchedTerms.length, used: canonical.providedRefs.length };
|
|
66
|
+
}
|
|
67
|
+
function skillsBySession(skills) {
|
|
68
|
+
const bySession = new Map();
|
|
69
|
+
for (const skill of skills) {
|
|
70
|
+
for (const sessionId of skill.sourceSessionIds) {
|
|
71
|
+
const rows = bySession.get(sessionId) ?? [];
|
|
72
|
+
if (!rows.some((row) => row.ref === skill.ref && row.state === skill.state)) {
|
|
73
|
+
rows.push({ ref: skill.ref, state: skill.state });
|
|
74
|
+
}
|
|
75
|
+
bySession.set(sessionId, rows);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return bySession;
|
|
79
|
+
}
|
|
80
|
+
export async function foldHistory(deps) {
|
|
81
|
+
const reasons = [];
|
|
82
|
+
const episodesResult = await deps.evidence.list();
|
|
83
|
+
const episodes = collect(episodesResult, 'evidence', [], reasons);
|
|
84
|
+
const ledgerResult = await deps.contribution.ledger();
|
|
85
|
+
const ledger = collect(ledgerResult, 'contribution', [], reasons);
|
|
86
|
+
const skillsResult = deps.localLearning.skills
|
|
87
|
+
? await deps.localLearning.skills()
|
|
88
|
+
: { status: 'unavailable', reason: 'skill provenance is unavailable' };
|
|
89
|
+
const skills = collect(skillsResult, 'localLearning', [], reasons);
|
|
90
|
+
const byEpisode = ledgerByEpisode(ledger);
|
|
91
|
+
const bySession = skillsBySession(skills);
|
|
92
|
+
const contributionAvailable = ledgerResult.status !== 'unavailable';
|
|
93
|
+
const skillsAvailable = skillsResult.status !== 'unavailable';
|
|
94
|
+
const entries = [...episodes]
|
|
95
|
+
.filter((ep) => ep.session.kind !== 'host-internal')
|
|
96
|
+
.sort((a, b) => b.session.capturedAt.localeCompare(a.session.capturedAt))
|
|
97
|
+
.map((ep) => {
|
|
98
|
+
const counts = knowledgeCounts(ep.activity);
|
|
99
|
+
return {
|
|
100
|
+
sessionId: ep.session.sessionId,
|
|
101
|
+
capturedAt: ep.session.capturedAt,
|
|
102
|
+
taskSummary: ep.task.summary,
|
|
103
|
+
knowledgeSurfaced: ep.activity ? counts.surfaced : null,
|
|
104
|
+
knowledgeUsed: ep.activity ? counts.used : null,
|
|
105
|
+
captureStatus: 'captured',
|
|
106
|
+
eligibility: episodeEligibility(ep),
|
|
107
|
+
contributionState: contributionState(byEpisode.get(ep.episodeId), contributionAvailable),
|
|
108
|
+
distilledSkills: skillsAvailable ? bySession.get(ep.session.sessionId) ?? [] : null,
|
|
109
|
+
};
|
|
110
|
+
});
|
|
111
|
+
const legacyFactsUnavailable = episodes.some((ep) => !ep.activity || !ep.eligibility);
|
|
112
|
+
if (legacyFactsUnavailable)
|
|
113
|
+
reasons.push('one or more episodes predate activity or eligibility facts');
|
|
114
|
+
const evidenceUnavailable = episodesResult.status === 'unavailable';
|
|
115
|
+
return reasons.length > 0
|
|
116
|
+
? { entries, degraded: true, unavailable: evidenceUnavailable, reason: reasons.join('; ') }
|
|
117
|
+
: { entries, degraded: false, unavailable: false };
|
|
118
|
+
}
|
|
119
|
+
export async function foldExplain(sessionRef, deps) {
|
|
120
|
+
const reasons = [];
|
|
121
|
+
const episodes = collect(await deps.evidence.list(), 'evidence', [], reasons);
|
|
122
|
+
const ledgerResult = await deps.contribution.ledger();
|
|
123
|
+
const ledger = collect(ledgerResult, 'contribution', [], reasons);
|
|
124
|
+
const episode = episodes.find((ep) => ep.session.sessionId === sessionRef);
|
|
125
|
+
const contribution = episode
|
|
126
|
+
? contributionState(ledger.find((row) => row.sourceId === episode.episodeId), ledgerResult.status !== 'unavailable')
|
|
127
|
+
: contributionState(undefined, ledgerResult.status !== 'unavailable');
|
|
128
|
+
const activity = canonicalKnowledgeActivity(episode?.activity);
|
|
129
|
+
return {
|
|
130
|
+
sessionRef,
|
|
131
|
+
found: Boolean(episode),
|
|
132
|
+
searchedTerms: activity.searchedTerms,
|
|
133
|
+
providedRefs: activity.providedRefs,
|
|
134
|
+
captureStatus: episode ? 'captured' : 'not-captured',
|
|
135
|
+
eligibility: episode ? episodeEligibility(episode) : null,
|
|
136
|
+
contributionState: contribution,
|
|
137
|
+
degraded: reasons.length > 0,
|
|
138
|
+
...(reasons.length > 0 ? { reason: reasons.join('; ') } : {}),
|
|
139
|
+
};
|
|
140
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type { PortResult } from './outcome.js';
|
|
2
|
+
export { ok, degraded, unavailable, valueOr, unwrap } from './outcome.js';
|
|
3
|
+
export { EPISODE_SCHEMA_VERSION, EpisodeV1Schema, EpisodeV1WriteSchema, SessionActivityFactsSchema, SessionActivityFactsWriteSchema, VERIFICATION_STRENGTHS, VerificationStrengthSchema, } from './schemas/episode.js';
|
|
4
|
+
export type { EpisodeV1, EpisodeV1Write, SessionActivityFacts, VerificationStrength, } from './schemas/episode.js';
|
|
5
|
+
export { CONTRIBUTION_CANDIDATE_SCHEMA_VERSION, ContributionCandidateV1ProjectionSchema, ContributionCandidateV1Schema, } from './schemas/contribution-candidate.js';
|
|
6
|
+
export type { ContributionCandidateV1 } from './schemas/contribution-candidate.js';
|
|
7
|
+
export { KnowledgeHitSchema } from './schemas/knowledge-hit.js';
|
|
8
|
+
export type { KnowledgeHit } from './schemas/knowledge-hit.js';
|
|
9
|
+
export { EligibilityVerdictSchema } from './schemas/eligibility-verdict.js';
|
|
10
|
+
export type { EligibilityVerdict } from './schemas/eligibility-verdict.js';
|
|
11
|
+
export { SessionSummarySchema } from './schemas/session-summary.js';
|
|
12
|
+
export type { SessionSummary } from './schemas/session-summary.js';
|
|
13
|
+
export { HistoryEntrySchema } from './schemas/history-entry.js';
|
|
14
|
+
export type { HistoryEntry } from './schemas/history-entry.js';
|
|
15
|
+
export type { CorpusPort, CorpusRecord, CorpusRecordStep } from './ports/corpus-port.js';
|
|
16
|
+
export type { EvidencePort, EvidenceListQuery, EvidenceRetentionPolicy } from './ports/evidence-port.js';
|
|
17
|
+
export { deriveContributionStatus } from './ports/contribution-port.js';
|
|
18
|
+
export type { ContributionPort, ContributionLedgerEntry, ContributionLocalState, ContributionPublicationState, ContributionState, ContributionStatus, ContributionStatusSnapshot, } from './ports/contribution-port.js';
|
|
19
|
+
export type { LocalLearningPort, LocalLearningRun, LocalLearningSkill, } from './ports/local-learning-port.js';
|
|
20
|
+
export type { SkillsPort, SkillRecord } from './ports/skills-port.js';
|
|
21
|
+
export { createJinnPlugin, JINN_PLUGIN_CONTRACT_VERSION, PluginSession } from './plugin.js';
|
|
22
|
+
export type { CompleteSessionEligibilityInputs, CompleteSessionInput, JinnPlugin, JinnPluginDeps, SessionMeta, FirstTurnPickupResult, ToolCallEvent, SessionOutcome, SessionEndResult, ContributionCompletionReceipt, ContributionPreview, } from './plugin.js';
|
|
23
|
+
export { PickupConfigSchema, DEFAULT_PICKUP_CONFIG, parsePickupConfig, TIER_ORDER } from './schemas/pickup-config.js';
|
|
24
|
+
export type { PickupConfig, Tier } from './schemas/pickup-config.js';
|
|
25
|
+
export { deriveRepositorySearchTerms, discriminatingTerms, deriveSearchTerms, classifyPayload, dedupeKnowledgeHits, scoreKnowledgeHit, selectKnowledgeHits, rankKnowledgeHits, MAX_SELECTED_PACKETS, } from './pickup.js';
|
|
26
|
+
export { KNOWLEDGE_PACKET_SCHEMA_VERSION, KnowledgePacketSchema, KnowledgePacketExcerptSchema, DEFAULT_PACKET_CHAR_BUDGET, projectKnowledgePacket, truncateLineBoundary, } from './schemas/knowledge-packet.js';
|
|
27
|
+
export type { KnowledgePacket, KnowledgePacketExcerpt, KnowledgePacketBudget, } from './schemas/knowledge-packet.js';
|
|
28
|
+
export { RETRIEVAL_VISIBLE_TAG, hasRetrievalMark } from './visibility.js';
|
|
29
|
+
export { deriveEligibility } from './eligibility.js';
|
|
30
|
+
export type { EligibilityInputs } from './eligibility.js';
|
|
31
|
+
export { foldHistory, foldExplain } from './history.js';
|
|
32
|
+
export type { HistoryResult, SessionExplanation, HistoryDeps } from './history.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { ok, degraded, unavailable, valueOr, unwrap } from './outcome.js';
|
|
2
|
+
export { EPISODE_SCHEMA_VERSION, EpisodeV1Schema, EpisodeV1WriteSchema, SessionActivityFactsSchema, SessionActivityFactsWriteSchema, VERIFICATION_STRENGTHS, VerificationStrengthSchema, } from './schemas/episode.js';
|
|
3
|
+
export { CONTRIBUTION_CANDIDATE_SCHEMA_VERSION, ContributionCandidateV1ProjectionSchema, ContributionCandidateV1Schema, } from './schemas/contribution-candidate.js';
|
|
4
|
+
export { KnowledgeHitSchema } from './schemas/knowledge-hit.js';
|
|
5
|
+
export { EligibilityVerdictSchema } from './schemas/eligibility-verdict.js';
|
|
6
|
+
export { SessionSummarySchema } from './schemas/session-summary.js';
|
|
7
|
+
export { HistoryEntrySchema } from './schemas/history-entry.js';
|
|
8
|
+
export { deriveContributionStatus } from './ports/contribution-port.js';
|
|
9
|
+
export { createJinnPlugin, JINN_PLUGIN_CONTRACT_VERSION, PluginSession } from './plugin.js';
|
|
10
|
+
export { PickupConfigSchema, DEFAULT_PICKUP_CONFIG, parsePickupConfig, TIER_ORDER } from './schemas/pickup-config.js';
|
|
11
|
+
export { deriveRepositorySearchTerms, discriminatingTerms, deriveSearchTerms, classifyPayload, dedupeKnowledgeHits, scoreKnowledgeHit, selectKnowledgeHits, rankKnowledgeHits, MAX_SELECTED_PACKETS, } from './pickup.js';
|
|
12
|
+
export { KNOWLEDGE_PACKET_SCHEMA_VERSION, KnowledgePacketSchema, KnowledgePacketExcerptSchema, DEFAULT_PACKET_CHAR_BUDGET, projectKnowledgePacket, truncateLineBoundary, } from './schemas/knowledge-packet.js';
|
|
13
|
+
export { RETRIEVAL_VISIBLE_TAG, hasRetrievalMark } from './visibility.js';
|
|
14
|
+
export { deriveEligibility } from './eligibility.js';
|
|
15
|
+
export { foldHistory, foldExplain } from './history.js';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Port result envelope (architecture spec §4). Adapters may throw; ports
|
|
3
|
+
* never do — every port method resolves a PortResult so a failure surfaces
|
|
4
|
+
* as a typed outcome, never a crash into the host session.
|
|
5
|
+
*/
|
|
6
|
+
export type PortResult<T> = {
|
|
7
|
+
status: 'ok';
|
|
8
|
+
value: T;
|
|
9
|
+
} | {
|
|
10
|
+
status: 'degraded';
|
|
11
|
+
reason: string;
|
|
12
|
+
value?: T;
|
|
13
|
+
} | {
|
|
14
|
+
status: 'unavailable';
|
|
15
|
+
reason: string;
|
|
16
|
+
};
|
|
17
|
+
export declare function ok<T>(value: T): PortResult<T>;
|
|
18
|
+
export declare function degraded<T>(reason: string, value?: T): PortResult<T>;
|
|
19
|
+
export declare function unavailable<T = never>(reason: string): PortResult<T>;
|
|
20
|
+
/** Fail-open value access: ok → value; degraded → value ?? fallback; unavailable → fallback. */
|
|
21
|
+
export declare function valueOr<T>(res: PortResult<T>, fallback: T): T;
|
|
22
|
+
/** Like valueOr, but also surfaces the failure reason so callers can collect it. */
|
|
23
|
+
export declare function unwrap<T>(res: PortResult<T>, fallback: T): {
|
|
24
|
+
value: T;
|
|
25
|
+
reason?: string;
|
|
26
|
+
};
|
package/dist/outcome.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export function ok(value) {
|
|
2
|
+
return { status: 'ok', value };
|
|
3
|
+
}
|
|
4
|
+
export function degraded(reason, value) {
|
|
5
|
+
return { status: 'degraded', reason, value };
|
|
6
|
+
}
|
|
7
|
+
export function unavailable(reason) {
|
|
8
|
+
return { status: 'unavailable', reason };
|
|
9
|
+
}
|
|
10
|
+
/** Fail-open value access: ok → value; degraded → value ?? fallback; unavailable → fallback. */
|
|
11
|
+
export function valueOr(res, fallback) {
|
|
12
|
+
if (res.status === 'ok')
|
|
13
|
+
return res.value;
|
|
14
|
+
if (res.status === 'degraded')
|
|
15
|
+
return res.value ?? fallback;
|
|
16
|
+
return fallback;
|
|
17
|
+
}
|
|
18
|
+
/** Like valueOr, but also surfaces the failure reason so callers can collect it. */
|
|
19
|
+
export function unwrap(res, fallback) {
|
|
20
|
+
if (res.status === 'ok')
|
|
21
|
+
return { value: res.value };
|
|
22
|
+
return { value: res.status === 'degraded' ? res.value ?? fallback : fallback, reason: res.reason };
|
|
23
|
+
}
|
package/dist/pickup.d.ts
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/** Evidence-first pickup policy (rescope design §3.3; lexical v2 term
|
|
2
|
+
* selection — #1791, #1790, #1789). No I/O: term derivation + selection
|
|
3
|
+
* only. Orchestration (search/get over ports, packet projection, rendering)
|
|
4
|
+
* lives in plugin.ts. */
|
|
5
|
+
import type { CorpusRecord } from './ports/corpus-port.js';
|
|
6
|
+
import type { KnowledgeHit } from './schemas/knowledge-hit.js';
|
|
7
|
+
export declare const STOPWORDS: ReadonlySet<string>;
|
|
8
|
+
/**
|
|
9
|
+
* The repository vocabulary shared by interactive pickup and repo-scoped
|
|
10
|
+
* corpus probes. A full `owner/repo` slug does not occur in record text; the
|
|
11
|
+
* repository name is the searchable term (#1790). Keep the minimum length in
|
|
12
|
+
* this one helper so non-session consumers cannot drift from pickup.
|
|
13
|
+
*/
|
|
14
|
+
export declare function deriveRepositorySearchTerms(repositorySlug?: string): string[];
|
|
15
|
+
/**
|
|
16
|
+
* Up to `maxTerms` deterministic lowercase search terms, in priority order
|
|
17
|
+
* (rescope §3.3, lexical v2 — #1791/#1790/#1789): backticked/quoted tokens
|
|
18
|
+
* (edge-stripped, near-verbatim); identifier-shaped tokens — a `/`-bearing
|
|
19
|
+
* one also contributes its path segments, right after the full token; the
|
|
20
|
+
* session repository's NAME (not its full slug, #1790); the remaining
|
|
21
|
+
* non-stopword tokens (>=4 chars) in MESSAGE ORDER — order of first
|
|
22
|
+
* appearance, not longest-first (#1791: length is not a retrievability
|
|
23
|
+
* signal against a corpus of short summaries and tags). Operates over the
|
|
24
|
+
* whole message (not just the first line). Deduplicated.
|
|
25
|
+
*/
|
|
26
|
+
export declare function deriveSearchTerms(message: string, repositorySlug?: string, maxTerms?: number): string[];
|
|
27
|
+
/** Ports classify_payload — 'skill' or 'unknown'. Prefers the explicit
|
|
28
|
+
* payloadKind classification field; falls back to hit.kind === 'skill'. */
|
|
29
|
+
export declare function classifyPayload(hit: Pick<KnowledgeHit, 'payloadKind' | 'kind'>): 'skill' | 'unknown';
|
|
30
|
+
/** Dedup by ref, then by content key (rescope §3.3 step 2). */
|
|
31
|
+
export declare function dedupeKnowledgeHits(hits: KnowledgeHit[]): KnowledgeHit[];
|
|
32
|
+
export declare const RELEVANCE_FLOOR = 2;
|
|
33
|
+
export declare const MAX_SELECTED_PACKETS = 2;
|
|
34
|
+
export declare const MAX_CONTENT_RESCORE_CANDIDATES = 3;
|
|
35
|
+
export interface ScoredKnowledgeHit {
|
|
36
|
+
hit: KnowledgeHit;
|
|
37
|
+
score: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The scoring vocabulary: `terms` minus the repository-name term (#1886).
|
|
41
|
+
*
|
|
42
|
+
* The repository name tags every record in an in-repo corpus, so it matches
|
|
43
|
+
* everything and discriminates nothing — yet it counted 1 toward
|
|
44
|
+
* `RELEVANCE_FLOOR`, halving the effective floor for any query issued inside
|
|
45
|
+
* the repository. It remains a *search* term (it is what surfaces
|
|
46
|
+
* repo-relevant records at all); it just no longer helps a record clear the
|
|
47
|
+
* floor. Callers pass the full list to search and this list to scoring.
|
|
48
|
+
*/
|
|
49
|
+
export declare function discriminatingTerms(terms: string[], repositorySlug?: string): string[];
|
|
50
|
+
/** Count of matched terms across `taskSummary + tags` (rescope §3.3 step 3,
|
|
51
|
+
* lexical v2 — #1790/#1791) — every match counts 1. The repository-slug
|
|
52
|
+
* match used to count 2, but the slug it matched against could never
|
|
53
|
+
* appear in a record's text (#1790); the repository's NAME is now a
|
|
54
|
+
* normal derived term (see `deriveSearchTerms` step 3) and scores like any
|
|
55
|
+
* other term, with no special case. */
|
|
56
|
+
export declare function scoreKnowledgeHit(hit: KnowledgeHit, terms: string[]): number;
|
|
57
|
+
/**
|
|
58
|
+
* Re-score an already plausible metadata candidate against the two concise,
|
|
59
|
+
* human-authored content fields available after `CorpusPort.get`: synthesis
|
|
60
|
+
* and step titles. Each original normalized term contributes at most one
|
|
61
|
+
* point across metadata + content, so repeating the same match in synthesis
|
|
62
|
+
* cannot manufacture relevance.
|
|
63
|
+
*/
|
|
64
|
+
export declare function scoreKnowledgeRecord(hit: KnowledgeHit, record: CorpusRecord, terms: string[]): number;
|
|
65
|
+
/** Return a copied, deterministically ranked scored-candidate list. */
|
|
66
|
+
export declare function rankScoredKnowledgeHits(candidates: ScoredKnowledgeHit[]): ScoredKnowledgeHit[];
|
|
67
|
+
/**
|
|
68
|
+
* Eligible, deduplicated candidates that have at least one metadata match.
|
|
69
|
+
* Score-1 results are intentionally retained here for the bounded content
|
|
70
|
+
* escalation; `rankKnowledgeHits` remains the honest floor-enforcing API.
|
|
71
|
+
*/
|
|
72
|
+
export declare function rankKnowledgeCandidates(hits: KnowledgeHit[], terms: string[]): ScoredKnowledgeHit[];
|
|
73
|
+
/**
|
|
74
|
+
* Full ranked candidate pool (rescope §3.3 selection policy): drop skill
|
|
75
|
+
* hits, dedup, score, apply the relevance floor (honest nothing-found below
|
|
76
|
+
* it), rank score desc → tier desc → recency desc — every candidate that
|
|
77
|
+
* clears the floor, not sliced to `MAX_SELECTED_PACKETS`.
|
|
78
|
+
*
|
|
79
|
+
* Content-level guards that can only run after a candidate's content is
|
|
80
|
+
* fetched (mono #1782: post-fetch skill-payload classification, empty-packet
|
|
81
|
+
* honesty) need to walk past a disqualified top candidate to the
|
|
82
|
+
* next-ranked one, so the orchestrator (`plugin.ts` `firstTurnPickup`) walks
|
|
83
|
+
* this unsliced list and does its own promotion-aware slicing.
|
|
84
|
+
* `selectKnowledgeHits` remains the pre-guards convenience wrapper for
|
|
85
|
+
* callers that only need the top slice.
|
|
86
|
+
*
|
|
87
|
+
* Also fail-closed allowlist-filters to `retrievalVisible === true` (#1824,
|
|
88
|
+
* W2) — absence of the field excludes the hit; this is the ranking-side half
|
|
89
|
+
* of the two-layer enforcement, `firstTurnPickup`'s post-fetch content guard
|
|
90
|
+
* is the other half.
|
|
91
|
+
*/
|
|
92
|
+
export declare function rankKnowledgeHits(hits: KnowledgeHit[], terms: string[]): KnowledgeHit[];
|
|
93
|
+
/**
|
|
94
|
+
* Evidence-first selection (rescope §3.3): the top `MAX_SELECTED_PACKETS` of
|
|
95
|
+
* `rankKnowledgeHits`. A caller that also needs to apply the post-fetch
|
|
96
|
+
* content-level guards (mono #1782) should walk `rankKnowledgeHits` directly
|
|
97
|
+
* instead — slicing here happens before those guards can run, so a
|
|
98
|
+
* candidate they disqualify would lose its slot rather than promoting the
|
|
99
|
+
* next-ranked one.
|
|
100
|
+
*/
|
|
101
|
+
export declare function selectKnowledgeHits(hits: KnowledgeHit[], terms: string[]): KnowledgeHit[];
|