@lmzhen/dsh-evolution-skill-discovery 0.30.43 → 0.31.1

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.
@@ -0,0 +1,360 @@
1
+ import { O as buildDomainGenerationPrompt, Z as ProjectionJournal, _ as buildGenerationRequest, c as AssignmentWorker, et as DomainMapFileStore, g as ViewSnapshotStore, k as ProjectionRuntime, l as JsonAssignmentQueue, o as installDiscoveryAwareness, ot as DomainGenerationCoordinator, r as renderDomainDescription, rt as DomainPolicyStore, s as installAssignmentSidecar, t as descriptionHashOf, u as DomainAssignmentService, v as resolveGenerationPort } from "./domain-description-3MIOYgVH.js";
2
+ import { _ as skillKeyOf, c as SkillSearchCache, d as SkillIndex, r as registerSkillSearchTool } from "./tool-CvUTwKvW.js";
3
+ import { isAbsolute, join, resolve } from "node:path";
4
+ import { randomUUID } from "node:crypto";
5
+ import { authorityFrom, evolutionIoAdapter, evolutionRoot, newSkillLibrary } from "@lmzhen/dsh-evolution-core";
6
+ //#region lib/types/description-refresh.js
7
+ function nextMapVersion(version) {
8
+ const numeric = Number(version);
9
+ return Number.isSafeInteger(numeric) && numeric >= 0 ? String(numeric + 1) : version + ".1";
10
+ }
11
+ /**
12
+ * Re-render stale domain descriptions from their definition fields.
13
+ * @param store - domain map store.
14
+ * @returns number of refreshed domains.
15
+ */
16
+ async function refreshStaleDescriptions(store) {
17
+ const snapshot = await store.read();
18
+ const stale = snapshot.domains.filter((node) => node.descriptionStatus === "stale");
19
+ if (stale.length === 0) return 0;
20
+ const now = Date.now();
21
+ const domains = snapshot.domains.map((node) => node.descriptionStatus !== "stale" ? node : {
22
+ ...node,
23
+ description: renderDomainDescription(node),
24
+ descriptionHash: descriptionHashOf(node, snapshot.domains.filter((child) => child.parentIds.includes(node.id))),
25
+ descriptionVersion: node.descriptionVersion + 1,
26
+ descriptionStatus: "fresh",
27
+ updatedAt: now
28
+ });
29
+ await store.commit(snapshot.mapVersion, {
30
+ mapVersion: nextMapVersion(snapshot.mapVersion),
31
+ domains,
32
+ edges: snapshot.edges
33
+ });
34
+ return stale.length;
35
+ }
36
+ //#endregion
37
+ //#region lib/types/main-entry.js
38
+ const name = "evolution-skill-discovery";
39
+ const inject = [
40
+ "skills",
41
+ "workingDirectory",
42
+ "tools",
43
+ "evolutionIo",
44
+ "systemPrompt"
45
+ ];
46
+ var PlatformSource = class {
47
+ ctx;
48
+ library;
49
+ constructor(ctx, library) {
50
+ this.ctx = ctx;
51
+ this.library = library;
52
+ }
53
+ async snapshot(query) {
54
+ const snapshot = await this.ctx.skills.snapshot({
55
+ cwd: query.cwd,
56
+ ...query.scope === void 0 ? {} : { scope: query.scope }
57
+ });
58
+ return {
59
+ skills: snapshot.skills,
60
+ complete: snapshot.complete
61
+ };
62
+ }
63
+ async managedSkillKeys() {
64
+ return new Set((await this.library.list()).filter((summary) => summary.managed).map((summary) => skillKeyOf({
65
+ provider: "dsh-evolution",
66
+ source: "user-dsh",
67
+ name: summary.name
68
+ })));
69
+ }
70
+ async protectedSkillKeys() {
71
+ return new Set((await this.library.list()).filter((summary) => summary.protectedBy !== null || summary.protectionUnknown).map((summary) => skillKeyOf({
72
+ provider: "dsh-evolution",
73
+ source: "user-dsh",
74
+ name: summary.name
75
+ })));
76
+ }
77
+ async pinnedSkillKeys() {
78
+ return new Set((await this.library.list()).filter((summary) => summary.protectedBy === "pinned").map((summary) => skillKeyOf({
79
+ provider: "dsh-evolution",
80
+ source: "user-dsh",
81
+ name: summary.name
82
+ })));
83
+ }
84
+ };
85
+ function searchUnavailable() {
86
+ return {
87
+ results: [],
88
+ truncated: false,
89
+ degraded: true,
90
+ hint: "Skill search is unavailable without a session working directory."
91
+ };
92
+ }
93
+ /** Apply the full production discovery composition. */
94
+ function apply(ctx, config) {
95
+ if (typeof config.featureEpoch !== "string" || config.featureEpoch.length === 0) throw new Error("evolution-skill-discovery: featureEpoch is required");
96
+ const evolutionIo = ctx.evolutionIo;
97
+ const io = evolutionIoAdapter(() => evolutionIo.provider());
98
+ const source = new PlatformSource(ctx, newSkillLibrary({
99
+ config: {},
100
+ io,
101
+ ctx,
102
+ authority: authorityFrom({ origin: "foreground" })
103
+ }));
104
+ const index = new SkillIndex(source);
105
+ const workingDirectory = ctx.get("workingDirectory");
106
+ if (workingDirectory === void 0) throw new Error("evolution-skill-discovery: workingDirectory service is required");
107
+ const root = evolutionRoot();
108
+ let mode = config.mode ?? "off";
109
+ let generation = void 0;
110
+ if (mode !== "off") generation = resolveGenerationPort({
111
+ mode,
112
+ ...ctx.get("llm") === void 0 ? {} : { llm: ctx.get("llm") },
113
+ provider: config.generationProvider ?? "deepseek-official",
114
+ model: config.generationModel ?? "deepseek-flash",
115
+ ...config.generationTimeoutMs === void 0 ? {} : { timeoutMs: config.generationTimeoutMs },
116
+ ...config.generationMaxTokens === void 0 ? {} : { maxTokens: config.generationMaxTokens },
117
+ ...config.generationAttempts === void 0 ? {} : { attempts: config.generationAttempts }
118
+ });
119
+ const searchCache = new SkillSearchCache();
120
+ let searchHealthy = true;
121
+ const search = {
122
+ async allowedSkillKeys(agent, signal) {
123
+ if (mode === "off" || agent === void 0) return void 0;
124
+ const cwd = await workingDirectory.ensure(agent, signal);
125
+ const view = await index.read({
126
+ cwd,
127
+ scopeKey: String(agent.id),
128
+ scope: agent,
129
+ signal
130
+ });
131
+ let snapshot;
132
+ try {
133
+ snapshot = await map.read();
134
+ } catch {
135
+ return;
136
+ }
137
+ const known = new Set(snapshot.domains.map((node) => node.id));
138
+ const effective = (await policyFor(cwd)).resolve(known);
139
+ const blocked = new Set(snapshot.edges.filter((edge) => edge.status === "active" && effective.disabled.has(edge.domainId)).map((edge) => edge.skillKey));
140
+ return new Set(view.rows.filter((row) => row.modelInvocable && !blocked.has(row.skillKey)).map((row) => row.skillKey));
141
+ },
142
+ async search(query, options, agent, signal) {
143
+ if (agent === void 0) return searchUnavailable();
144
+ const cwd = await workingDirectory.ensure(agent, signal);
145
+ const view = await index.read({
146
+ cwd,
147
+ scopeKey: String(agent.id),
148
+ scope: agent,
149
+ signal
150
+ });
151
+ const viewPolicy = mode === "off" ? void 0 : await policyFor(cwd);
152
+ const cached = await searchCache.get({
153
+ viewKey: view.viewKey,
154
+ viewCatalogDigest: view.viewCatalogDigest,
155
+ policyDigest: viewPolicy?.digest() ?? "off",
156
+ discoveryMode: mode
157
+ }, view.rows);
158
+ if (cached === void 0) {
159
+ searchHealthy = false;
160
+ return {
161
+ results: [],
162
+ truncated: false,
163
+ degraded: true,
164
+ hint: "Skill search index is unavailable. Use the full catalog or skill(name)."
165
+ };
166
+ }
167
+ searchHealthy = true;
168
+ const result = await cached.index.search(query, options);
169
+ return result.stale || cached.stale ? {
170
+ ...result,
171
+ stale: true
172
+ } : result;
173
+ }
174
+ };
175
+ if (config.searchEnabled !== false) ctx.effect(() => registerSkillSearchTool(ctx, search));
176
+ ctx.on("evolution/skill-mutated", () => {
177
+ searchCache.clear();
178
+ });
179
+ if (mode === "off") return;
180
+ const map = new DomainMapFileStore(io, config.domainMapPath ?? join(root, "evolution", "domain-map.json"));
181
+ const journal = new ProjectionJournal(io, config.projectionJournalPath ?? join(root, "evolution", "projection-journal.json"));
182
+ const snapshots = new ViewSnapshotStore(io, config.viewSnapshotPath ?? join(root, "evolution", "view-snapshots.json"));
183
+ const globalPolicy = {
184
+ mode: mode === "shadow" ? "auto" : mode,
185
+ pinDomains: config.pinDomains,
186
+ disableDomains: config.disableDomains
187
+ };
188
+ const policy = new DomainPolicyStore({
189
+ global: globalPolicy,
190
+ ...config.workspacePolicy === void 0 ? {} : { workspace: config.workspacePolicy },
191
+ ...config.scopePolicy === void 0 ? {} : { scope: config.scopePolicy }
192
+ });
193
+ const readLayer = async (cwd, path) => {
194
+ if (path === void 0) return void 0;
195
+ try {
196
+ const text = await io.readText(isAbsolute(path) ? path : resolve(cwd, path));
197
+ return text === null ? void 0 : JSON.parse(text);
198
+ } catch (error) {
199
+ ctx.logger.warn("evolution-skill-discovery: policy layer unreadable: " + (error instanceof Error ? error.message : String(error)));
200
+ return;
201
+ }
202
+ };
203
+ const policyFor = async (cwd) => {
204
+ const workspace = await readLayer(cwd, config.workspacePolicyPath) ?? config.workspacePolicy;
205
+ const scope = await readLayer(cwd, config.scopePolicyPath) ?? config.scopePolicy;
206
+ return new DomainPolicyStore({
207
+ global: globalPolicy,
208
+ ...workspace === void 0 ? {} : { workspace },
209
+ ...scope === void 0 ? {} : { scope }
210
+ });
211
+ };
212
+ const runtime = new ProjectionRuntime({
213
+ skillIndexSource: source,
214
+ map,
215
+ policy,
216
+ policyFor,
217
+ journal,
218
+ workingDirectory,
219
+ featureEpoch: config.featureEpoch,
220
+ codecVersion: "1",
221
+ adapterVersion: "1",
222
+ projectionSchemaVersion: "1",
223
+ mode,
224
+ ...config.maxCards === void 0 ? {} : { maxCards: config.maxCards },
225
+ ...config.tokenBudget === void 0 ? {} : { tokenBudget: config.tokenBudget },
226
+ searchAvailable: () => searchHealthy && config.searchEnabled !== false,
227
+ ...config.staleAfterHours === void 0 ? {} : { staleAfterHours: config.staleAfterHours },
228
+ ...config.descriptionStaleAfterHours === void 0 ? {} : { descriptionStaleAfterHours: config.descriptionStaleAfterHours },
229
+ isFrozen: async (agent) => snapshots.isFrozen(String(agent.session.id), config.featureEpoch)
230
+ });
231
+ const queue = new JsonAssignmentQueue(io, config.assignmentQueuePath ?? join(root, "evolution", "assignment-queue.json"));
232
+ const assignment = new DomainAssignmentService(queue, map, {
233
+ createClassificationEnabled: config.createClassificationEnabled ?? false,
234
+ ...config.provisionalThreshold === void 0 ? {} : { provisionalThreshold: config.provisionalThreshold },
235
+ now: Date.now
236
+ });
237
+ const worker = new AssignmentWorker(assignment, queue);
238
+ queue.load().then(() => worker.drain()).catch(() => {
239
+ ctx.logger.warn("evolution-skill-discovery: assignment queue load failed; queued work stays for the next start");
240
+ });
241
+ installAssignmentSidecar(ctx, assignment, { onSubmitted: () => {
242
+ worker.drain().catch((error) => {
243
+ ctx.logger.warn("evolution-skill-discovery: assignment drain failed: " + (error instanceof Error ? error.message : String(error)));
244
+ });
245
+ } });
246
+ ctx.on("agent/created", async ({ agent, source: start }) => {
247
+ try {
248
+ installDiscoveryAwareness(agent.ctx);
249
+ } catch (error) {
250
+ ctx.logger.warn("evolution-skill-discovery: awareness install failed: " + (error instanceof Error ? error.message : String(error)));
251
+ }
252
+ if (start !== "startup") return;
253
+ try {
254
+ const cwd = await workingDirectory.ensure(agent);
255
+ const view = await index.read({
256
+ cwd,
257
+ scopeKey: String(agent.id),
258
+ scope: agent
259
+ });
260
+ await snapshots.put({
261
+ sessionId: String(agent.session.id),
262
+ viewKey: view.viewKey,
263
+ cwd,
264
+ viewCatalogDigest: view.viewCatalogDigest,
265
+ mapVersion: "0",
266
+ configDigest: "production",
267
+ featureEpoch: config.featureEpoch,
268
+ createdAt: Date.now()
269
+ });
270
+ const refreshed = await refreshStaleDescriptions(map);
271
+ if (refreshed > 0) ctx.logger.info("evolution-skill-discovery: refreshed " + refreshed + " stale domain descriptions");
272
+ } catch (error) {
273
+ ctx.logger.warn("evolution-skill-discovery: initial view snapshot failed: " + (error instanceof Error ? error.message : String(error)));
274
+ }
275
+ });
276
+ ctx.on("agent/pre-step", async ({ agent, signal }, next) => {
277
+ const decision = await next();
278
+ try {
279
+ return await runtime.project(agent, decision, signal);
280
+ } catch (error) {
281
+ ctx.logger.warn("evolution-skill-discovery: projection failed open: " + (error instanceof Error ? error.message : String(error)));
282
+ return decision;
283
+ }
284
+ }, { prepend: true });
285
+ const coordinator = generation === void 0 ? void 0 : new DomainGenerationCoordinator(map, generation);
286
+ const commandRuntime = ctx.get("commands");
287
+ if (commandRuntime !== void 0) ctx.effect(() => commandRuntime.register({
288
+ name: "skill-domain",
289
+ description: "Generate or inspect the skill domain map.",
290
+ async handler({ rawInput, agent }) {
291
+ const [action] = rawInput.trim().split(/\s+/);
292
+ if (action === "status") {
293
+ const statusCwd = await workingDirectory.ensure(agent);
294
+ const view = await index.read({
295
+ cwd: statusCwd,
296
+ scopeKey: String(agent.id),
297
+ scope: agent
298
+ });
299
+ const rows = view.rows.slice(0, 12).map((row) => row.name + " | provider=" + row.provider + " source=" + row.source + " managed=" + String(row.managed) + " invocable=" + String(row.modelInvocable) + " protected=" + String(row.protected) + " pinned=" + String(row.pinned) + " key=" + row.skillKey + " hash=" + row.threeFieldHash);
300
+ const state = await runtime.inspect(agent);
301
+ const report = runtime.report(agent);
302
+ return {
303
+ kind: "success",
304
+ text: "discovery mode=" + mode + "; view=" + view.viewKey + "; phase=" + state.phase + " map=" + state.map + " search=" + state.search + " policy=" + state.policy + " last=" + report.action + (state.reasons.length === 0 ? "" : " reasons=" + state.reasons.join(",")) + "\n" + rows.join("\n")
305
+ };
306
+ }
307
+ if (action === "confirm") {
308
+ const domainId = rawInput.trim().split(/\s+/)[1];
309
+ if (domainId === void 0) return {
310
+ kind: "error",
311
+ text: "Usage: /skill-domain confirm <id>"
312
+ };
313
+ return await assignment.confirm(domainId) ? {
314
+ kind: "success",
315
+ text: "Confirmed domain " + domainId + "."
316
+ } : {
317
+ kind: "error",
318
+ text: "Unknown or non-provisional domain: " + domainId
319
+ };
320
+ }
321
+ if (action !== "generate") return {
322
+ kind: "error",
323
+ text: "Usage: /skill-domain status | generate | confirm <id>"
324
+ };
325
+ if (coordinator === void 0) return {
326
+ kind: "error",
327
+ text: "Generation is unavailable: no llm/generation port is mounted."
328
+ };
329
+ if (config.mapReferenceCwd === void 0) return {
330
+ kind: "error",
331
+ text: "No mapReferenceCwd configured; refusing to generate a global map from the caller view."
332
+ };
333
+ const view = await index.read({
334
+ cwd: config.mapReferenceCwd,
335
+ scopeKey: config.mapReferenceScope ?? String(agent.id),
336
+ scope: agent
337
+ });
338
+ const request = buildGenerationRequest({
339
+ mapScope: "global",
340
+ sourceDigest: view.viewCatalogDigest,
341
+ mapVersion: (await map.read()).mapVersion,
342
+ rows: view.rows,
343
+ prompt: buildDomainGenerationPrompt({
344
+ minDomains: 8,
345
+ maxDomains: 8
346
+ }),
347
+ promptVersion: "v2",
348
+ model: config.generationModel ?? "deepseek-flash",
349
+ runId: randomUUID()
350
+ });
351
+ const result = await coordinator.ensure(request);
352
+ return {
353
+ kind: "success",
354
+ text: "Domain map generated: " + result.domains.length + " domains, mapVersion=" + result.mapVersion
355
+ };
356
+ }
357
+ }));
358
+ }
359
+ //#endregion
360
+ export { apply, inject, name };
@@ -0,0 +1,88 @@
1
+ import { c as SkillSearchCache, d as SkillIndex, r as registerSkillSearchTool } from "./tool-CvUTwKvW.js";
2
+ //#region lib/types/search-entry.js
3
+ const name = "evolution-skill-discovery";
4
+ const inject = [
5
+ "skills",
6
+ "workingDirectory",
7
+ "tools"
8
+ ];
9
+ var PlatformSearchIndexSource = class {
10
+ ctx;
11
+ constructor(ctx) {
12
+ this.ctx = ctx;
13
+ }
14
+ async snapshot(query) {
15
+ const snapshot = await this.ctx.skills.snapshot({
16
+ cwd: query.cwd,
17
+ ...query.scope === void 0 ? {} : { scope: query.scope }
18
+ });
19
+ return {
20
+ skills: snapshot.skills,
21
+ complete: snapshot.complete
22
+ };
23
+ }
24
+ async managedSkillKeys() {
25
+ return /* @__PURE__ */ new Set();
26
+ }
27
+ async protectedSkillKeys() {
28
+ return /* @__PURE__ */ new Set();
29
+ }
30
+ async pinnedSkillKeys() {
31
+ return /* @__PURE__ */ new Set();
32
+ }
33
+ };
34
+ function unavailable() {
35
+ return {
36
+ results: [],
37
+ truncated: false,
38
+ degraded: true,
39
+ hint: "Skill search is unavailable without a session working directory."
40
+ };
41
+ }
42
+ /**
43
+ * Compose the search-only discovery surface.
44
+ * @param ctx - plugin context with skills, workingDirectory, and tools.
45
+ * @param config - search-only configuration.
46
+ */
47
+ function apply(ctx, config) {
48
+ if (typeof config.featureEpoch !== "string" || config.featureEpoch.length === 0) throw new Error("evolution-skill-discovery: featureEpoch is required");
49
+ if (config.mode !== void 0 && config.mode !== "off") ctx.logger.warn("evolution-skill-discovery: mode=" + config.mode + " is temporarily search-only; domain/projection is disabled until its producer is installed");
50
+ if (config.searchEnabled === false) return;
51
+ const source = new PlatformSearchIndexSource(ctx);
52
+ const cache = new SkillSearchCache();
53
+ const index = new SkillIndex(source);
54
+ ctx.effect(() => registerSkillSearchTool(ctx, { async search(query, options, agent, signal) {
55
+ if (agent === void 0) return unavailable();
56
+ const workingDirectory = ctx.get("workingDirectory");
57
+ if (workingDirectory === void 0) return unavailable();
58
+ const cwd = await workingDirectory.ensure(agent, signal);
59
+ const view = await index.read({
60
+ cwd,
61
+ scopeKey: String(agent.id),
62
+ scope: agent,
63
+ signal
64
+ });
65
+ const cached = await cache.get({
66
+ viewKey: view.viewKey,
67
+ viewCatalogDigest: view.viewCatalogDigest,
68
+ policyDigest: "search-only",
69
+ discoveryMode: "off"
70
+ }, view.rows);
71
+ if (cached === void 0) return {
72
+ results: [],
73
+ truncated: false,
74
+ degraded: true,
75
+ hint: "Skill search index is unavailable. Use the full catalog or skill(name)."
76
+ };
77
+ const result = await cached.index.search(query, options);
78
+ return result.stale || cached.stale ? {
79
+ ...result,
80
+ stale: true
81
+ } : result;
82
+ } }));
83
+ ctx.on("evolution/skill-mutated", () => {
84
+ cache.clear();
85
+ });
86
+ }
87
+ //#endregion
88
+ export { apply, inject, name };