@lmzhen/dsh-evolution-skill-discovery 0.31.1 → 0.32.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/lib/index.js DELETED
@@ -1,764 +0,0 @@
1
- import { $ as DomainMapCorruptError, A as ViewStateRegistry, B as renderProjectionContext, C as DOMAIN_GENERATION_PROMPT, D as DOMAIN_GENERATION_PROMPT_VERSION, E as DOMAIN_GENERATION_PROMPT_V2, F as applyProjectedDecision, G as restoreCatalog, H as captureCatalog, I as applyReplacementDecision, J as projectCatalog, K as buildDomainProjection, L as applyRestoreUpdate, M as canProject, N as deriveViewState, O as buildDomainGenerationPrompt, P as applyCapturedProjection, Q as DOMAIN_MAP_SCHEMA_VERSION, R as captureDecisionCatalog, S as parseDomainGenerationProposal, T as DOMAIN_GENERATION_PROMPT_V1, U as renderCatalog, V as restoreCapturedProjection, W as renderCatalogUpdate, X as evaluateDomainQuality, Y as sanitizeUntrustedText, Z as ProjectionJournal, _ as buildGenerationRequest, a as DISCOVERY_AWARENESS, at as mergeDomainPolicy, b as DomainGenerationError, c as AssignmentWorker, ct as bootstrapDomainMapWithFacts, d as MemoryAssignmentQueue, dt as validateDomainGeneration, et as DomainMapFileStore, f as assignmentKey, g as ViewSnapshotStore, h as validateProposal, i as shouldRegenerateDescription, it as DomainPolicyValidationError, j as canHide, k as ProjectionRuntime, l as JsonAssignmentQueue, lt as domainGenerationMode, m as proposalDigest, n as descriptionIsServable, nt as DomainMapVersionConflict, o as installDiscoveryAwareness, ot as DomainGenerationCoordinator, p as coveredByDomain, q as hiddenRecoverable, r as renderDomainDescription, rt as DomainPolicyStore, s as installAssignmentSidecar, st as bootstrapDomainMap, t as descriptionHashOf, tt as DomainMapValidationError, u as DomainAssignmentService, ut as mintDomainId, v as resolveGenerationPort, w as DOMAIN_GENERATION_PROMPT_HASH, x as LlmDomainGenerationPort, y as bootstrapFactsFromRows, z as findCatalogMessage } from "./domain-description-3MIOYgVH.js";
2
- import { _ as skillKeyOf, a as SKILL_SEARCH_FIELDS, b as viewKeyOf, c as SkillSearchCache, d as SkillIndex, f as catalogRevisionOf, g as skillIndexRowOf, h as searchIndexKeyOf, i as searchWithDegraded, l as SkillSearchIndex, m as projectionDigestOf, n as isSkillSearch, o as SKILL_SEARCH_INDEX_SCHEMA_VERSION, p as policyDigestOf, r as registerSkillSearchTool, s as SKILL_SEARCH_TOKENIZER_VERSION, t as SKILL_SEARCH_DESCRIPTION, u as searchTerms, v as threeFieldHashOf, y as viewCatalogDigestOf } from "./tool-CvUTwKvW.js";
3
- import { join } from "node:path";
4
- import { authorityFrom, evolutionIoAdapter, evolutionRoot, newSkillLibrary } from "@lmzhen/dsh-evolution-core";
5
- import { createUserMessage } from "@deepseek-ai/dsh-llm";
6
- //#region lib/types/adapter.js
7
- /** @returns true for the platform catalog message. */
8
- function isCatalogMessage(message) {
9
- return message.source.kind === "skill-catalog";
10
- }
11
- function escapeData(value) {
12
- return sanitizeUntrustedText(value, 2e3).text;
13
- }
14
- function renderCatalog$1(entries) {
15
- if (entries.length === 0) return [];
16
- return [
17
- "<system-reminder>",
18
- "A skill is a reusable set of task-specific instructions. The following skills are available in this session:",
19
- "<available_skills>",
20
- ...entries.map((entry) => "- " + escapeData(entry.name) + ": " + escapeData(entry.description)),
21
- "</available_skills>",
22
- "Call the skill tool with the exact name before acting.",
23
- "</system-reminder>"
24
- ];
25
- }
26
- /** Build the plugin-owned replacement message from a complete projection. */
27
- function projectionPresent(agent, digest) {
28
- try {
29
- const visible = new Set(agent.session.surface.nodes);
30
- for (const event of agent.session.snapshotEvents()) {
31
- if (event.type !== "user/message") continue;
32
- const data = event.data;
33
- if (data?.source?.kind !== "evolution-skill-projection" || data.source.digest !== digest) continue;
34
- if (visible.has(event.seq)) return true;
35
- }
36
- } catch {
37
- return false;
38
- }
39
- return false;
40
- }
41
- function renderProjection(result) {
42
- const text = [...renderCatalog$1(result.catalogEntries), ...result.context === "" ? [] : [result.context]].join("\n");
43
- if (text === "") return void 0;
44
- return createUserMessage({
45
- content: [{
46
- type: "text",
47
- text
48
- }],
49
- source: {
50
- kind: "evolution-skill-projection",
51
- entries: result.catalogEntries,
52
- hiddenNames: result.hiddenNames,
53
- digest: result.digest
54
- }
55
- });
56
- }
57
- /**
58
- * Atomically replace one platform catalog message with the plugin projection.
59
- * @param decision - the waterfall decision; only enter decisions are changed.
60
- * @param result - a complete projection; incomplete results are ignored.
61
- * @returns a new decision, or the original when no safe replacement exists.
62
- */
63
- function applyCatalogProjection(decision, result) {
64
- if (decision.kind !== "enter" || !result.complete) return decision;
65
- const index = decision.messages.findIndex(isCatalogMessage);
66
- if (index < 0) return decision;
67
- const replacement = renderProjection(result);
68
- const messages = [...decision.messages];
69
- if (replacement === void 0) messages.splice(index, 1);
70
- else messages[index] = replacement;
71
- return {
72
- ...decision,
73
- messages
74
- };
75
- }
76
- /**
77
- * Install the thin pre-step adapter. The listener is outer; it always calls
78
- * next, only touches the platform catalog message, and fails open.
79
- * @param ctx - plugin context.
80
- * @param provider - projection provider backed by SkillIndex/DomainMap.
81
- * @returns disposer from ctx.on.
82
- */
83
- function installCatalogProjection(ctx, provider, digestStore) {
84
- const projectedDigest = /* @__PURE__ */ new Map();
85
- return ctx.on("agent/pre-step", async ({ agent }, next) => {
86
- const decision = await next();
87
- if (decision.kind !== "enter") return decision;
88
- const catalog = decision.messages.find(isCatalogMessage);
89
- if (catalog === void 0) return decision;
90
- try {
91
- if (provider.shouldProject !== void 0 && !await provider.shouldProject(agent)) return decision;
92
- const result = await provider.project({
93
- agent,
94
- catalogEntries: catalog.source.entries
95
- });
96
- if (!result.complete) return decision;
97
- const agentKey = String(agent.id);
98
- if ((projectedDigest.get(agentKey) ?? await digestStore?.get(agentKey)) === result.digest && projectionPresent(agent, result.digest)) return {
99
- ...decision,
100
- messages: decision.messages.filter((message) => !isCatalogMessage(message))
101
- };
102
- const nextDecision = applyCatalogProjection(decision, result);
103
- if (nextDecision !== decision) {
104
- projectedDigest.set(agentKey, result.digest);
105
- await digestStore?.set(agentKey, result.digest);
106
- }
107
- return nextDecision;
108
- } catch {
109
- return decision;
110
- }
111
- }, { prepend: true });
112
- }
113
- //#endregion
114
- //#region lib/types/features.js
115
- const V0_DISCOVERY_FEATURES = Object.freeze({
116
- projectionEnabled: false,
117
- searchEnabled: false,
118
- createClassificationEnabled: false,
119
- discoveryMode: "off"
120
- });
121
- function canClassifyAtCreate(config, hasCreateDomainCards) {
122
- return config.createClassificationEnabled && hasCreateDomainCards;
123
- }
124
- //#endregion
125
- //#region lib/types/stages.js
126
- /** Ordered implementation stages from PLAN-DISC-001. */
127
- const DISCOVERY_STAGES = [
128
- "G0-projection",
129
- "G1-domain",
130
- "G2-projection",
131
- "G3-search",
132
- "G4-assignment",
133
- "G5-maintenance",
134
- "G6-ui",
135
- "G7-migration",
136
- "G8-release"
137
- ];
138
- function stageIndex(stage) {
139
- return DISCOVERY_STAGES.indexOf(stage);
140
- }
141
- function mayAdvance(current, next) {
142
- return stageIndex(next) === stageIndex(current) + 1;
143
- }
144
- //#endregion
145
- //#region lib/types/domain-view.js
146
- /**
147
- * Count visible skills by active domain membership.
148
- * @param snapshot - global domain facts.
149
- * @param visibleSkillKeys - skill keys visible in this view.
150
- * @returns per-domain member counts plus the unclassified virtual count.
151
- */
152
- function domainViewCounts(snapshot, visibleSkillKeys) {
153
- const byDomain = /* @__PURE__ */ new Map();
154
- const classified = /* @__PURE__ */ new Set();
155
- for (const edge of snapshot.edges) {
156
- if (edge.status !== "active" || !visibleSkillKeys.has(edge.skillKey)) continue;
157
- const set = byDomain.get(edge.domainId) ?? /* @__PURE__ */ new Set();
158
- set.add(edge.skillKey);
159
- byDomain.set(edge.domainId, set);
160
- if (edge.role === "primary") classified.add(edge.skillKey);
161
- }
162
- const counts = /* @__PURE__ */ new Map();
163
- for (const [domainId, members] of byDomain) counts.set(domainId, members.size);
164
- return {
165
- byDomain: counts,
166
- unclassified: [...visibleSkillKeys].filter((key) => !classified.has(key)).length
167
- };
168
- }
169
- //#endregion
170
- //#region lib/types/domain-metrics.js
171
- /**
172
- * @param snapshot - domain facts.
173
- * @param visibleSkillKeys - skills visible in this view.
174
- * @param labels - optional assignment labels.
175
- * @returns metrics; null means the denominator is zero/unmeasured.
176
- */
177
- function computeDomainMetrics(snapshot, visibleSkillKeys, labels, counters) {
178
- const visible = visibleSkillKeys.size;
179
- const primary = /* @__PURE__ */ new Set();
180
- const domainsWithActiveMembers = /* @__PURE__ */ new Set();
181
- let activeEdges = 0;
182
- let pendingEdges = 0;
183
- const memberships = /* @__PURE__ */ new Map();
184
- for (const edge of snapshot.edges) {
185
- if (edge.status === "pending") pendingEdges += 1;
186
- if (edge.status !== "active") continue;
187
- activeEdges += 1;
188
- if (!visibleSkillKeys.has(edge.skillKey)) continue;
189
- if (edge.role === "primary") primary.add(edge.skillKey);
190
- domainsWithActiveMembers.add(edge.domainId);
191
- memberships.set(edge.skillKey, (memberships.get(edge.skillKey) ?? 0) + 1);
192
- }
193
- const crossDomain = [...memberships.values()].filter((count) => count > 1).length;
194
- const emptyDomainCount = snapshot.domains.filter((domain) => !domainsWithActiveMembers.has(domain.id)).length;
195
- const assignmentAccuracy = labels === void 0 ? null : accuracy(labels);
196
- const staleDescriptions = snapshot.domains.filter((domain) => domain.descriptionStatus === "stale").length;
197
- const overAssigned = counters?.maxDomainsPerSkill === void 0 ? null : [...memberships.values()].filter((count) => count > counters.maxDomainsPerSkill).length;
198
- return {
199
- measured: visible > 0,
200
- visibleSkills: visible,
201
- coverage: visible === 0 ? null : primary.size / visible,
202
- unclassifiedRatio: visible === 0 ? null : (visible - primary.size) / visible,
203
- emptyDomainCount,
204
- crossDomainRatio: primary.size === 0 ? null : crossDomain / primary.size,
205
- pendingRate: activeEdges + pendingEdges === 0 ? null : pendingEdges / (activeEdges + pendingEdges),
206
- assignmentAccuracy,
207
- newDomainPrecision: labels?.newDomainIds === void 0 || labels.predictedNewDomainIds === void 0 || labels.newDomainIds.length === 0 ? null : precision(labels.newDomainIds, labels.predictedNewDomainIds),
208
- descriptionRewriteRate: counters?.descriptionEligible === void 0 || counters.descriptionEligible === 0 ? null : (counters.descriptionRewritten ?? 0) / counters.descriptionEligible,
209
- staleDescriptionRatio: snapshot.domains.length === 0 ? null : staleDescriptions / snapshot.domains.length,
210
- overAssignmentRate: overAssigned === null || visible === 0 ? null : overAssigned / visible,
211
- refusalRate: counters?.assignmentAttempts === void 0 || counters.assignmentAttempts === 0 ? null : (counters.refusedAssignments ?? 0) / counters.assignmentAttempts
212
- };
213
- }
214
- function accuracy(labels) {
215
- const keys = Object.keys(labels.expectedBySkill).filter((key) => labels.predictedBySkill[key] !== void 0);
216
- if (keys.length === 0) return null;
217
- return keys.filter((key) => labels.expectedBySkill[key] === labels.predictedBySkill[key]).length / keys.length;
218
- }
219
- function precision(expected, predicted) {
220
- if (predicted.length === 0) return null;
221
- const wanted = new Set(expected);
222
- return predicted.filter((id) => wanted.has(id)).length / predicted.length;
223
- }
224
- //#endregion
225
- //#region lib/types/session-projection.js
226
- /** @returns true only for a new, fully-enabled session in the current epoch. */
227
- function shouldHideCatalog(meta, currentFeatureEpoch) {
228
- return meta?.discoveryMode === "on" && meta.featureEpoch === currentFeatureEpoch;
229
- }
230
- /** @returns true when awareness may be emitted (shadow or on). */
231
- function shouldEmitAwareness(meta, currentFeatureEpoch) {
232
- return (meta?.discoveryMode === "on" || meta?.discoveryMode === "shadow") && meta.featureEpoch === currentFeatureEpoch;
233
- }
234
- /** @returns false when the same digest was already emitted in this session. */
235
- function shouldEmitDigest(previousDigest, nextDigest) {
236
- return previousDigest !== nextDigest;
237
- }
238
- /** @returns off at <=2200 tokens, on at >=3000, shadow between. */
239
- function projectionBudgetMode(catalogTokens, promoted = false) {
240
- if (catalogTokens <= 2200) return "off";
241
- if (promoted && catalogTokens >= 3e3) return "on";
242
- return "shadow";
243
- }
244
- /** @returns true when a resumed session already enabled discovery. */
245
- function shouldInstallAwarenessOnResume(record, featureEpoch) {
246
- return record !== void 0 && record.discoveryMode !== "off" && record.featureEpoch === featureEpoch;
247
- }
248
- //#endregion
249
- //#region lib/types/maintenance.js
250
- /** Maintenance defaults from PLAN/MAP-009. */
251
- const MAINTENANCE_DEFAULTS = Object.freeze({
252
- splitSkillThreshold: 25,
253
- coarsenDomainCount: 12,
254
- maxVisibleDepth: 3,
255
- minChildren: 2,
256
- minMembersPerChild: 3,
257
- coarsenMinDomains: 3,
258
- coarsenMaxDomains: 8
259
- });
260
- function directMembers(snapshot, domainId) {
261
- return new Set(snapshot.edges.filter((edge) => edge.status === "active" && edge.domainId === domainId).map((edge) => edge.skillKey));
262
- }
263
- function descendants(snapshot, domainId) {
264
- const result = /* @__PURE__ */ new Set();
265
- const queue = [domainId];
266
- while (queue.length > 0) {
267
- const current = queue.shift();
268
- for (const node of snapshot.domains) {
269
- if (!node.parentIds.includes(current) || result.has(node.id)) continue;
270
- result.add(node.id);
271
- queue.push(node.id);
272
- }
273
- }
274
- return result;
275
- }
276
- /**
277
- * Audit domain definitions/structure without mutating the map.
278
- * @param snapshot - domain facts.
279
- * @param visibleSkillKeys - optional view skills for unclassified count.
280
- * @returns per-domain audit plus map-level counts.
281
- */
282
- function auditDomainMap(snapshot, visibleSkillKeys) {
283
- const ids = new Set(snapshot.domains.map((node) => node.id));
284
- const primary = /* @__PURE__ */ new Set();
285
- for (const edge of snapshot.edges) if (edge.status === "active" && edge.role === "primary") primary.add(edge.skillKey);
286
- const domains = snapshot.domains.map((node) => {
287
- const direct = directMembers(snapshot, node.id);
288
- const indirect = /* @__PURE__ */ new Set();
289
- for (const child of descendants(snapshot, node.id)) for (const member of directMembers(snapshot, child)) indirect.add(member);
290
- const definitionComplete = node.purpose.trim().length > 0 && node.inScope.length > 0 && node.outOfScope.length > 0 && node.triggers.length > 0;
291
- return {
292
- domainId: node.id,
293
- definitionComplete,
294
- childCount: snapshot.domains.filter((child) => child.parentIds.includes(node.id)).length,
295
- directSkillCount: direct.size,
296
- indirectSkillCount: indirect.size,
297
- empty: direct.size === 0,
298
- orphan: node.parentIds.some((parent) => !ids.has(parent)),
299
- lastUpdated: node.updatedAt
300
- };
301
- });
302
- const unclassifiedCount = [...visibleSkillKeys ?? new Set(snapshot.edges.map((edge) => edge.skillKey))].filter((key) => !primary.has(key)).length;
303
- return {
304
- domains,
305
- domainCount: domains.length,
306
- unclassifiedCount
307
- };
308
- }
309
- /**
310
- * Find split candidates; no child domains are created and no skills are reassigned.
311
- * @param snapshot - domain facts.
312
- * @param threshold - minimum direct members to consider.
313
- * @returns candidates with blockers.
314
- */
315
- function splitCandidates(snapshot, threshold = MAINTENANCE_DEFAULTS.splitSkillThreshold) {
316
- return snapshot.domains.flatMap((node) => {
317
- const direct = directMembers(snapshot, node.id).size;
318
- if (direct < threshold) return [];
319
- const children = Math.min(MAINTENANCE_DEFAULTS.coarsenMaxDomains, Math.floor(direct / MAINTENANCE_DEFAULTS.minMembersPerChild));
320
- const blockers = [];
321
- if (children < MAINTENANCE_DEFAULTS.minChildren) blockers.push("fewer-than-2-children");
322
- return [{
323
- domainId: node.id,
324
- directSkillCount: direct,
325
- children,
326
- blockers
327
- }];
328
- });
329
- }
330
- /**
331
- * Find coarsen groups; only structure candidates, never edge rewrites.
332
- * @param snapshot - domain facts.
333
- * @param maxDomains - above this count coarsening is considered.
334
- * @returns groups of 3–8 domains, at least 2 groups.
335
- */
336
- function coarsenCandidates(snapshot, maxDomains = MAINTENANCE_DEFAULTS.coarsenDomainCount) {
337
- if (snapshot.domains.length <= maxDomains) return [];
338
- const ids = snapshot.domains.map((node) => node.id).sort();
339
- const size = Math.min(MAINTENANCE_DEFAULTS.coarsenMaxDomains, Math.max(MAINTENANCE_DEFAULTS.coarsenMinDomains, Math.ceil(ids.length / 2)));
340
- const groups = [];
341
- for (let index = 0; index < ids.length; index += size) groups.push({ domainIds: ids.slice(index, index + size) });
342
- return groups.length >= 2 ? groups : [];
343
- }
344
- //#endregion
345
- //#region lib/types/ui.js
346
- const POLICY_KEYS = new Set([
347
- "mode",
348
- "maxCards",
349
- "tokenBudget",
350
- "staleAfterHours",
351
- "pinDomains",
352
- "disableDomains"
353
- ]);
354
- const MODES = new Set([
355
- "auto",
356
- "off",
357
- "on"
358
- ]);
359
- /** @returns true when the policy only edits supported fields. */
360
- function isDiscoveryUserPolicy(value) {
361
- if (typeof value !== "object" || value === null) return false;
362
- const record = value;
363
- if (Object.keys(record).some((key) => !POLICY_KEYS.has(key))) return false;
364
- if (record.mode !== void 0 && (typeof record.mode !== "string" || !MODES.has(record.mode))) return false;
365
- if (record.maxCards !== void 0 && (typeof record.maxCards !== "number" || !Number.isSafeInteger(record.maxCards) || record.maxCards < 1)) return false;
366
- if (record.tokenBudget !== void 0 && (typeof record.tokenBudget !== "number" || !Number.isSafeInteger(record.tokenBudget) || record.tokenBudget < 1)) return false;
367
- if (record.staleAfterHours !== void 0 && (typeof record.staleAfterHours !== "number" || record.staleAfterHours <= 0)) return false;
368
- if (record.pinDomains !== void 0 && !Array.isArray(record.pinDomains)) return false;
369
- if (record.disableDomains !== void 0 && !Array.isArray(record.disableDomains)) return false;
370
- return true;
371
- }
372
- /** Static skill_manage description; no dynamic domain list/count/tree. */
373
- const SKILL_MANAGE_DESCRIPTION = "Manage skill content. create may include an optional domain proposal when create-side domain cards are available; no proposal means unclassified. Domain assignment never blocks skill creation and is stored as a sidecar.";
374
- /** @returns recomputable behavior metrics; null means no denominator. */
375
- function computeBehaviorMetrics(observations) {
376
- const total = observations.length;
377
- const shown = observations.filter((item) => item.awarenessShown).length;
378
- const searched = observations.filter((item) => item.searched).length;
379
- const successes = observations.filter((item) => item.taskSuccess).length;
380
- return {
381
- awarenessRecall: shown === 0 ? null : observations.filter((item) => item.awarenessShown && item.awarenessRecalled).length / shown,
382
- silentMissRate: shown === 0 ? null : observations.filter((item) => item.silentMiss).length / shown,
383
- overSearchRate: searched === 0 ? null : observations.filter((item) => item.searched && item.overSearch).length / searched,
384
- underSearchRate: searched === 0 ? null : observations.filter((item) => item.searched && item.underSearch).length / searched,
385
- wrongLoadRate: searched === 0 ? null : observations.filter((item) => item.searched && item.wrongLoad).length / searched,
386
- taskSuccess: total === 0 ? null : successes / total,
387
- tokens: observations.reduce((sum, item) => sum + item.tokens, 0),
388
- fallbackRate: searched === 0 ? null : observations.filter((item) => item.searched && item.fallback).length / searched,
389
- total
390
- };
391
- }
392
- //#endregion
393
- //#region lib/types/session-store.js
394
- /** Plugin-side session metadata; platform SessionHeader has no discovery fields. */
395
- var DiscoverySessionStore = class {
396
- io;
397
- path;
398
- constructor(io, path) {
399
- this.io = io;
400
- this.path = path;
401
- }
402
- async read() {
403
- const content = await this.io.readText(this.path);
404
- if (content === null) return {
405
- schemaVersion: 1,
406
- sessions: {}
407
- };
408
- try {
409
- const parsed = JSON.parse(content);
410
- return parsed.schemaVersion === 1 && typeof parsed.sessions === "object" && parsed.sessions !== null ? {
411
- schemaVersion: 1,
412
- sessions: parsed.sessions
413
- } : {
414
- schemaVersion: 1,
415
- sessions: {}
416
- };
417
- } catch {
418
- return {
419
- schemaVersion: 1,
420
- sessions: {}
421
- };
422
- }
423
- }
424
- /** @returns persisted metadata for one session. */
425
- async get(sessionId) {
426
- const entry = (await this.read()).sessions[sessionId];
427
- return entry === void 0 ? void 0 : {
428
- sessionId,
429
- ...entry
430
- };
431
- }
432
- /** Persist one session's mode/epoch; later writes win. */
433
- async put(record) {
434
- const current = await this.read();
435
- const previous = current.sessions[record.sessionId];
436
- const projectionDigest = record.projectionDigest ?? previous?.projectionDigest;
437
- const sessions = {
438
- ...current.sessions,
439
- [record.sessionId]: {
440
- discoveryMode: record.discoveryMode,
441
- featureEpoch: record.featureEpoch,
442
- ...projectionDigest === void 0 ? {} : { projectionDigest }
443
- }
444
- };
445
- await this.io.writeText(this.path, JSON.stringify({
446
- schemaVersion: 1,
447
- sessions
448
- }));
449
- }
450
- };
451
- /** @returns true only for a persisted enabled session in the current feature epoch. */
452
- async function shouldProjectPersistedSession(store, sessionId, currentFeatureEpoch) {
453
- return shouldHideCatalog(await store.get(sessionId), currentFeatureEpoch);
454
- }
455
- //#endregion
456
- //#region lib/types/runtime.js
457
- /** G2 adapter provider wired from ports; no global platform dependency. */
458
- var DiscoveryRuntime = class {
459
- ports;
460
- constructor(ports) {
461
- this.ports = ports;
462
- }
463
- /** @returns true only when persisted session metadata allows projection. */
464
- async shouldProject(agent) {
465
- return shouldEmitAwareness(await this.ports.sessionStore.get(agent.session.id), this.ports.featureEpoch);
466
- }
467
- /** @returns a complete projection for this agent's catalog entries. */
468
- async project(input) {
469
- const view = await new SkillIndex(this.ports.skillIndexSource).read({
470
- cwd: input.agent.session.header.cwd ?? "",
471
- scopeKey: String(input.agent.id),
472
- scope: input.agent
473
- });
474
- const snapshot = await this.ports.map.read();
475
- const estimatedTokens = Math.ceil(input.catalogEntries.reduce((sum, entry) => sum + entry.name.length + entry.description.length, 0) / 4);
476
- const promoted = typeof this.ports.promoted === "function" ? this.ports.promoted() : this.ports.promoted === true;
477
- const autoMode = this.ports.autoMode === true ? projectionBudgetMode(estimatedTokens, promoted) : this.ports.mode;
478
- const result = buildDomainProjection({
479
- entries: input.catalogEntries,
480
- rows: view.rows,
481
- complete: view.complete,
482
- snapshot,
483
- policy: this.ports.policy,
484
- viewKey: view.viewKey,
485
- scope: String(input.agent.id),
486
- mapVersion: snapshot.mapVersion,
487
- policyDigest: this.ports.policyDigest,
488
- viewCatalogDigest: view.viewCatalogDigest,
489
- adapterVersion: this.ports.adapterVersion,
490
- projectionSchemaVersion: this.ports.projectionSchemaVersion,
491
- discoveryMode: this.ports.autoMode === true ? autoMode ?? "shadow" : this.ports.discoveryMode,
492
- maxCards: this.ports.maxCards,
493
- tokenBudget: this.ports.tokenBudget,
494
- staleAfterHours: this.ports.staleAfterHours,
495
- maxVisibleDepth: this.ports.maxVisibleDepth,
496
- now: Date.now(),
497
- mode: autoMode,
498
- mapStale: this.ports.mapStale,
499
- searchAvailable: this.ports.searchAvailable
500
- });
501
- this.ports.onProjection?.(result);
502
- return result;
503
- }
504
- };
505
- //#endregion
506
- //#region lib/types/search-judge.js
507
- /** Validate a judge packet without accepting partial query coverage. */
508
- function validateJudgeRecord(record, expectedQueryIds) {
509
- const errors = [];
510
- if (record.schemaVersion !== 1 || record.querySet !== "eval-010" || record.judgeKind !== "model" && record.judgeKind !== "human") errors.push("schema-invalid");
511
- if (record.judgeId.trim().length === 0) errors.push("judge-id-missing");
512
- const seen = /* @__PURE__ */ new Set();
513
- for (const observation of record.observations) {
514
- if (!expectedQueryIds.includes(observation.queryId)) errors.push("unknown-query:" + observation.queryId);
515
- if (seen.has(observation.queryId)) errors.push("duplicate-query:" + observation.queryId);
516
- seen.add(observation.queryId);
517
- }
518
- for (const id of expectedQueryIds) if (!seen.has(id)) errors.push("missing-query:" + id);
519
- return errors;
520
- }
521
- /** @returns aggregate over model/human records; agreement only for shared queries. */
522
- function aggregateJudge(records, expectedQueryIds) {
523
- const model = records.find((record) => record.judgeKind === "model");
524
- const human = records.find((record) => record.judgeKind === "human");
525
- const modelLabels = new Map(model?.observations.map((item) => [item.queryId, item.relevant]) ?? []);
526
- const humanLabels = new Map(human?.observations.map((item) => [item.queryId, item.relevant]) ?? []);
527
- const union = [...new Set([...modelLabels.keys(), ...humanLabels.keys()])];
528
- const relevant = union.filter((id) => (modelLabels.get(id) ?? humanLabels.get(id)) === true).length;
529
- const shared = [...modelLabels.keys()].filter((id) => humanLabels.has(id));
530
- return {
531
- queryCount: expectedQueryIds.length,
532
- relevantRate: union.length === 0 ? null : relevant / union.length,
533
- agreementRate: shared.length === 0 ? null : shared.filter((id) => modelLabels.get(id) === humanLabels.get(id)).length / shared.length,
534
- hasModel: model !== void 0,
535
- hasHuman: human !== void 0
536
- };
537
- }
538
- //#endregion
539
- //#region lib/types/domain-generation-service.js
540
- /**
541
- * Generate and persist the initial map only when the map has no domains.
542
- * @param store - map owner.
543
- * @param port - external LLM adapter.
544
- * @param skills - name/description/whenToUse facts.
545
- * @param skillKeys - evidence name -> stable key.
546
- * @param expectedMapVersion - CAS precondition.
547
- * @returns committed snapshot, or existing snapshot when already bootstrapped.
548
- */
549
- async function initialDomainMap(store, port, skills, skillKeys, expectedMapVersion, signal, prompt = DOMAIN_GENERATION_PROMPT) {
550
- const current = await store.read();
551
- if (current.domains.length > 0) return current;
552
- return bootstrapDomainMap(store, await port.generate({
553
- skills,
554
- prompt,
555
- ...signal === void 0 ? {} : { signal }
556
- }), skillKeys, expectedMapVersion);
557
- }
558
- //#endregion
559
- //#region lib/types/plugin.js
560
- const name = "evolution-skill-discovery";
561
- const inject = [
562
- "skills",
563
- "evolutionIo",
564
- "systemPrompt",
565
- "tools"
566
- ];
567
- var PlatformSkillIndexSource = class {
568
- ctx;
569
- library;
570
- constructor(ctx, library) {
571
- this.ctx = ctx;
572
- this.library = library;
573
- }
574
- async snapshot(query) {
575
- const snapshot = await this.ctx.skills.snapshot({
576
- cwd: query.cwd,
577
- ...query.scope === void 0 ? {} : { scope: query.scope }
578
- });
579
- return {
580
- skills: snapshot.skills,
581
- complete: snapshot.complete
582
- };
583
- }
584
- async managedSkillKeys() {
585
- const summaries = await this.library.list();
586
- return new Set(summaries.filter((summary) => summary.managed).map((summary) => skillKeyOf({
587
- provider: "dsh-evolution",
588
- source: "user-dsh",
589
- name: summary.name
590
- })));
591
- }
592
- async protectedSkillKeys() {
593
- const summaries = await this.library.list();
594
- return new Set(summaries.filter((summary) => summary.protectedBy !== null || summary.protectionUnknown).map((summary) => skillKeyOf({
595
- provider: "dsh-evolution",
596
- source: "user-dsh",
597
- name: summary.name
598
- })));
599
- }
600
- async pinnedSkillKeys() {
601
- const summaries = await this.library.list();
602
- return new Set(summaries.filter((summary) => summary.protectedBy === "pinned").map((summary) => skillKeyOf({
603
- provider: "dsh-evolution",
604
- source: "user-dsh",
605
- name: summary.name
606
- })));
607
- }
608
- };
609
- /** Compose discovery services from platform ports; no catalog/provider replacement. */
610
- function apply(ctx, config) {
611
- if (typeof config.featureEpoch !== "string" || config.featureEpoch.length === 0) throw new Error("evolution-skill-discovery: featureEpoch is required");
612
- if (config.provisionalThreshold !== void 0 && (!Number.isSafeInteger(config.provisionalThreshold) || config.provisionalThreshold < 1)) throw new Error("evolution-skill-discovery: provisionalThreshold must be a positive integer");
613
- const evolutionIo = ctx.evolutionIo;
614
- const io = evolutionIoAdapter(() => evolutionIo.provider());
615
- const source = new PlatformSkillIndexSource(ctx, newSkillLibrary({
616
- config: {},
617
- io,
618
- ctx,
619
- authority: authorityFrom({ origin: "foreground" })
620
- }));
621
- const dshHome = evolutionRoot();
622
- const map = new DomainMapFileStore(io, config.domainMapPath ?? join(dshHome, "evolution", "domain-map.json"));
623
- if (config.domainGeneration !== void 0 && config.domainGenerationSkills !== void 0 && config.domainGenerationSkillKeys !== void 0) initialDomainMap(map, config.domainGeneration, config.domainGenerationSkills, config.domainGenerationSkillKeys, "0", void 0, buildDomainGenerationPrompt({
624
- ...config.domainMinDomains === void 0 ? {} : { minDomains: config.domainMinDomains },
625
- ...config.domainMaxDomains === void 0 ? {} : { maxDomains: config.domainMaxDomains }
626
- })).catch(() => {});
627
- if (config.bootstrapDomains !== void 0 && config.bootstrapSkillKeys !== void 0) bootstrapDomainMap(map, config.bootstrapDomains, config.bootstrapSkillKeys, "0").catch(() => {});
628
- const sessions = new DiscoverySessionStore(io, config.sessionPath ?? join(dshHome, "evolution", "discovery-sessions.json"));
629
- const policy = mergeDomainPolicy({
630
- mode: config.mode === "shadow" || config.mode === void 0 || config.mode === "auto" ? "auto" : config.mode,
631
- pinDomains: config.pinDomains,
632
- disableDomains: config.disableDomains
633
- });
634
- const policyDigest = policyDigestOf({
635
- globalPolicyRevision: "config",
636
- effectivePolicy: {
637
- mode: policy.mode,
638
- pinned: policy.pinned,
639
- disabled: policy.disabled
640
- }
641
- });
642
- const runtime = new DiscoveryRuntime({
643
- skillIndexSource: source,
644
- map,
645
- policy,
646
- sessionStore: sessions,
647
- featureEpoch: config.featureEpoch,
648
- mapVersion: "0",
649
- policyDigest,
650
- adapterVersion: "1",
651
- projectionSchemaVersion: "1",
652
- discoveryMode: config.mode === "auto" ? "shadow" : config.mode ?? "off",
653
- maxCards: config.maxCards,
654
- tokenBudget: config.tokenBudget ?? 1200,
655
- staleAfterHours: config.staleAfterHours,
656
- mode: config.mode === "auto" ? "shadow" : config.mode === "shadow" ? "shadow" : config.mode === "on" ? "on" : "off",
657
- autoMode: config.mode === "auto",
658
- mapStale: config.mapStale ?? false,
659
- searchAvailable: config.searchAvailable ?? true
660
- });
661
- if (config.mode !== "off") installDiscoveryAwareness(ctx);
662
- ctx.on("agent/created", async ({ agent, source }) => {
663
- try {
664
- if (source === "startup") await sessions.put({
665
- sessionId: String(agent.session.id),
666
- discoveryMode: config.mode === "on" ? "on" : config.mode === "shadow" || config.mode === "auto" ? "shadow" : "off",
667
- featureEpoch: config.featureEpoch
668
- });
669
- } catch {}
670
- });
671
- installCatalogProjection(ctx, runtime, {
672
- async get(agentKey) {
673
- return (await sessions.get(agentKey))?.projectionDigest;
674
- },
675
- async set(agentKey, digest) {
676
- const record = await sessions.get(agentKey);
677
- if (record !== void 0) await sessions.put({
678
- ...record,
679
- projectionDigest: digest
680
- });
681
- }
682
- });
683
- const queue = new JsonAssignmentQueue(io, join(dshHome, "evolution", "assignment-queue.json"));
684
- const assignment = new DomainAssignmentService(queue, map, {
685
- createClassificationEnabled: config.createClassificationEnabled ?? false,
686
- ...config.provisionalThreshold === void 0 ? {} : { provisionalThreshold: config.provisionalThreshold },
687
- now: Date.now
688
- });
689
- const worker = new AssignmentWorker(assignment, queue);
690
- queue.load().then(() => worker.drain()).catch(() => {});
691
- installAssignmentSidecar(ctx, assignment, { onSubmitted: () => {
692
- worker.drain().catch(() => {});
693
- } });
694
- const commandRuntime = ctx.get("commands");
695
- if (commandRuntime !== void 0) ctx.effect(() => commandRuntime.register({
696
- name: "skill-domain",
697
- description: "List provisional skill domains or confirm one by id.",
698
- async handler({ rawInput }) {
699
- const [action, domainId] = rawInput.trim().split(/\s+/);
700
- if (action === "list" || action === void 0) {
701
- const provisional = (await map.read()).domains.filter((node) => node.status === "provisional");
702
- return {
703
- kind: "success",
704
- text: provisional.length === 0 ? "No provisional domains." : "Provisional domains:\n" + provisional.map((node) => "- " + node.id + ": " + node.name).join("\n")
705
- };
706
- }
707
- if (action === "confirm" && domainId !== void 0) return await assignment.confirm(domainId) ? {
708
- kind: "success",
709
- text: "Confirmed domain " + domainId + "."
710
- } : {
711
- kind: "error",
712
- text: "Unknown or non-provisional domain: " + domainId
713
- };
714
- return {
715
- kind: "error",
716
- text: "Usage: /skill-domain list | /skill-domain confirm <id>"
717
- };
718
- }
719
- }));
720
- const searchCache = new SkillSearchCache();
721
- ctx.on("evolution/skill-mutated", () => {
722
- searchCache.clear();
723
- });
724
- registerSkillSearchTool(ctx, {
725
- async allowedSkillKeys(agent) {
726
- const view = await new SkillIndex(source).read({
727
- cwd: agent?.session.header.cwd ?? process.cwd(),
728
- scopeKey: agent === void 0 ? "tool" : String(agent.id),
729
- ...agent === void 0 ? {} : { scope: agent }
730
- });
731
- const snapshot = await map.read();
732
- const blocked = new Set(snapshot.edges.filter((edge) => edge.status === "active" && policy.disabled.has(edge.domainId)).map((edge) => edge.skillKey));
733
- return new Set(view.rows.filter((row) => row.modelInvocable && !blocked.has(row.skillKey)).map((row) => row.skillKey));
734
- },
735
- async search(query, options, agent) {
736
- const cwd = agent?.session.header.cwd ?? process.cwd();
737
- const view = await new SkillIndex(source).read({
738
- cwd,
739
- scopeKey: agent === void 0 ? "tool" : String(agent.id),
740
- ...agent === void 0 ? {} : { scope: agent }
741
- });
742
- const cacheKey = {
743
- viewKey: view.viewKey,
744
- viewCatalogDigest: view.viewCatalogDigest,
745
- policyDigest,
746
- discoveryMode: config.mode ?? "auto"
747
- };
748
- const cached = await searchCache.get(cacheKey, view.rows);
749
- if (cached === void 0) return {
750
- results: [],
751
- truncated: false,
752
- degraded: true,
753
- hint: "Skill search index is unavailable. Use skill(name) or the full catalog."
754
- };
755
- const result = await cached.index.search(query, options);
756
- return result.stale || cached.stale ? {
757
- ...result,
758
- stale: true
759
- } : result;
760
- }
761
- });
762
- }
763
- //#endregion
764
- export { AssignmentWorker, DISCOVERY_AWARENESS, DISCOVERY_STAGES, DOMAIN_GENERATION_PROMPT, DOMAIN_GENERATION_PROMPT_HASH, DOMAIN_GENERATION_PROMPT_V1, DOMAIN_GENERATION_PROMPT_V2, DOMAIN_GENERATION_PROMPT_VERSION, DOMAIN_MAP_SCHEMA_VERSION, DiscoveryRuntime, DiscoverySessionStore, DomainAssignmentService, DomainGenerationCoordinator, DomainGenerationError, DomainMapCorruptError, DomainMapFileStore, DomainMapValidationError, DomainMapVersionConflict, DomainPolicyStore, DomainPolicyValidationError, JsonAssignmentQueue, LlmDomainGenerationPort, MAINTENANCE_DEFAULTS, MemoryAssignmentQueue, ProjectionJournal, ProjectionRuntime, SKILL_MANAGE_DESCRIPTION, SKILL_SEARCH_DESCRIPTION, SKILL_SEARCH_FIELDS, SKILL_SEARCH_INDEX_SCHEMA_VERSION, SKILL_SEARCH_TOKENIZER_VERSION, SkillIndex, SkillSearchCache, SkillSearchIndex, V0_DISCOVERY_FEATURES, ViewSnapshotStore, ViewStateRegistry, aggregateJudge, apply, applyCapturedProjection, applyCatalogProjection, applyProjectedDecision, applyReplacementDecision, applyRestoreUpdate, assignmentKey, auditDomainMap, bootstrapDomainMap, bootstrapDomainMapWithFacts, bootstrapFactsFromRows, buildDomainGenerationPrompt, buildDomainProjection, buildGenerationRequest, canClassifyAtCreate, canHide, canProject, captureCatalog, captureDecisionCatalog, catalogRevisionOf, coarsenCandidates, computeBehaviorMetrics, computeDomainMetrics, coveredByDomain, deriveViewState, descriptionHashOf, descriptionIsServable, domainGenerationMode, domainViewCounts, evaluateDomainQuality, findCatalogMessage, hiddenRecoverable, initialDomainMap, inject, installAssignmentSidecar, installCatalogProjection, installDiscoveryAwareness, isDiscoveryUserPolicy, isSkillSearch, mayAdvance, mergeDomainPolicy, mintDomainId, name, parseDomainGenerationProposal, policyDigestOf, projectCatalog, projectionBudgetMode, projectionDigestOf, proposalDigest, registerSkillSearchTool, renderCatalog, renderCatalogUpdate, renderDomainDescription, renderProjectionContext, resolveGenerationPort, restoreCapturedProjection, restoreCatalog, sanitizeUntrustedText, searchIndexKeyOf, searchTerms, searchWithDegraded, shouldEmitAwareness, shouldEmitDigest, shouldHideCatalog, shouldInstallAwarenessOnResume, shouldProjectPersistedSession, shouldRegenerateDescription, skillIndexRowOf, skillKeyOf, splitCandidates, stageIndex, threeFieldHashOf, validateDomainGeneration, validateJudgeRecord, validateProposal, viewCatalogDigestOf, viewKeyOf };