@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.
- package/lib/domain-description-3MIOYgVH.js +2105 -0
- package/lib/index.js +13 -1714
- package/lib/main-entry.js +360 -0
- package/lib/search-entry.js +88 -0
- package/lib/tool-CvUTwKvW.js +446 -0
- package/lib/types/assignment-queue.d.ts +1 -1
- package/lib/types/catalog-codec.d.ts +35 -0
- package/lib/types/description-refresh.d.ts +8 -0
- package/lib/types/domain-bootstrap.d.ts +24 -6
- package/lib/types/domain-generation-coordinator.d.ts +33 -0
- package/lib/types/domain-generation-llm.d.ts +56 -0
- package/lib/types/domain-policy-store.d.ts +24 -0
- package/lib/types/domain-projection.d.ts +1 -6
- package/lib/types/generation-composition.d.ts +38 -0
- package/lib/types/generation-input.d.ts +9 -0
- package/lib/types/index.d.ts +12 -41
- package/lib/types/main-entry.d.ts +36 -0
- package/lib/types/plugin.d.ts +0 -6
- package/lib/types/projection-adapter.d.ts +60 -0
- package/lib/types/projection-journal.d.ts +32 -0
- package/lib/types/projection-runtime.d.ts +63 -0
- package/lib/types/projection.d.ts +1 -2
- package/lib/types/search-entry.d.ts +19 -0
- package/lib/types/skill-index.d.ts +41 -0
- package/lib/types/tool.d.ts +3 -3
- package/lib/types/view-snapshot-store.d.ts +30 -0
- package/lib/types/view-state.d.ts +59 -0
- package/package.json +16 -9
- package/lib/types/shadow-evidence.d.ts +0 -22
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import MiniSearch from "minisearch";
|
|
4
|
+
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
5
|
+
//#region lib/types/identity.js
|
|
6
|
+
function sha256(value) {
|
|
7
|
+
return createHash("sha256").update(value).digest("hex");
|
|
8
|
+
}
|
|
9
|
+
function canonical(value) {
|
|
10
|
+
if (value instanceof Set) return { $set: [...value].map(canonical).sort((left, right) => JSON.stringify(left) < JSON.stringify(right) ? -1 : JSON.stringify(left) > JSON.stringify(right) ? 1 : 0) };
|
|
11
|
+
if (value instanceof Map) return { $map: [...value.entries()].map(([key, entry]) => [canonical(key), canonical(entry)]).sort((left, right) => JSON.stringify(left) < JSON.stringify(right) ? -1 : JSON.stringify(left) > JSON.stringify(right) ? 1 : 0) };
|
|
12
|
+
if (Array.isArray(value)) return value.map(canonical);
|
|
13
|
+
if (typeof value === "object" && value !== null) return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([key, entry]) => [key, canonical(entry)]));
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
function digest(value) {
|
|
17
|
+
return sha256(JSON.stringify(canonical(value)));
|
|
18
|
+
}
|
|
19
|
+
/** @returns stable key for the winning provider/source/name tuple. */
|
|
20
|
+
function skillKeyOf(summary) {
|
|
21
|
+
return JSON.stringify([
|
|
22
|
+
summary.provider,
|
|
23
|
+
summary.source,
|
|
24
|
+
summary.name
|
|
25
|
+
]);
|
|
26
|
+
}
|
|
27
|
+
/** @returns stable hash of the canonical cwd and optional scope key. */
|
|
28
|
+
function viewKeyOf(input) {
|
|
29
|
+
return digest([resolve(input.cwd), input.scopeKey ?? null]);
|
|
30
|
+
}
|
|
31
|
+
/** @returns hash of the three fields used by discovery/retrieval. */
|
|
32
|
+
function threeFieldHashOf(input) {
|
|
33
|
+
return digest([
|
|
34
|
+
input.name,
|
|
35
|
+
input.description,
|
|
36
|
+
input.whenToUse ?? null
|
|
37
|
+
]);
|
|
38
|
+
}
|
|
39
|
+
/** @returns normalized row for one skill summary. */
|
|
40
|
+
function skillIndexRowOf(summary, managed, protectedSkill = false, pinned = false) {
|
|
41
|
+
return {
|
|
42
|
+
skillKey: skillKeyOf(summary),
|
|
43
|
+
name: summary.name,
|
|
44
|
+
description: summary.description,
|
|
45
|
+
...summary.whenToUse === void 0 ? {} : { whenToUse: summary.whenToUse },
|
|
46
|
+
provider: summary.provider,
|
|
47
|
+
source: summary.source,
|
|
48
|
+
managed,
|
|
49
|
+
modelInvocable: summary.invocation.modelInvocable,
|
|
50
|
+
protected: protectedSkill,
|
|
51
|
+
pinned,
|
|
52
|
+
threeFieldHash: threeFieldHashOf(summary)
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/** @returns digest of the sorted rows plus completeness. */
|
|
56
|
+
function catalogRevisionOf(rows, complete) {
|
|
57
|
+
return digest([complete, rows.map((row) => [
|
|
58
|
+
row.skillKey,
|
|
59
|
+
row.name,
|
|
60
|
+
row.description,
|
|
61
|
+
row.whenToUse ?? null,
|
|
62
|
+
row.provider,
|
|
63
|
+
row.source,
|
|
64
|
+
row.managed,
|
|
65
|
+
row.modelInvocable,
|
|
66
|
+
row.protected,
|
|
67
|
+
row.pinned,
|
|
68
|
+
row.threeFieldHash
|
|
69
|
+
])]);
|
|
70
|
+
}
|
|
71
|
+
/** @returns digest of a concrete per-view snapshot. */
|
|
72
|
+
function viewCatalogDigestOf(viewKey, rows, complete) {
|
|
73
|
+
return digest([viewKey, catalogRevisionOf(rows, complete)]);
|
|
74
|
+
}
|
|
75
|
+
/** @returns digest of effective policy plus its revisions. */
|
|
76
|
+
function policyDigestOf(input) {
|
|
77
|
+
return digest([
|
|
78
|
+
input.globalPolicyRevision,
|
|
79
|
+
input.workspaceOverlayRevision ?? null,
|
|
80
|
+
input.scopeOverlayRevision ?? null,
|
|
81
|
+
input.effectivePolicy
|
|
82
|
+
]);
|
|
83
|
+
}
|
|
84
|
+
/** @returns view-cache identity for one projection. */
|
|
85
|
+
function projectionDigestOf(input) {
|
|
86
|
+
return digest([
|
|
87
|
+
input.viewKey,
|
|
88
|
+
input.mapVersion,
|
|
89
|
+
input.policyDigest,
|
|
90
|
+
input.viewCatalogDigest,
|
|
91
|
+
input.adapterVersion,
|
|
92
|
+
input.projectionSchemaVersion,
|
|
93
|
+
input.discoveryMode
|
|
94
|
+
]);
|
|
95
|
+
}
|
|
96
|
+
/** @returns view-cache identity for one search index. */
|
|
97
|
+
function searchIndexKeyOf(input) {
|
|
98
|
+
return digest([
|
|
99
|
+
input.viewKey,
|
|
100
|
+
input.viewCatalogDigest,
|
|
101
|
+
input.policyDigest,
|
|
102
|
+
input.tokenizerVersion,
|
|
103
|
+
input.indexSchemaVersion,
|
|
104
|
+
input.discoveryMode
|
|
105
|
+
]);
|
|
106
|
+
}
|
|
107
|
+
//#endregion
|
|
108
|
+
//#region lib/types/skill-index.js
|
|
109
|
+
function bySkillKey(left, right) {
|
|
110
|
+
return left.skillKey < right.skillKey ? -1 : left.skillKey > right.skillKey ? 1 : 0;
|
|
111
|
+
}
|
|
112
|
+
/** Read-only view model; it does not cache or own a provider. */
|
|
113
|
+
var SkillIndex = class {
|
|
114
|
+
source;
|
|
115
|
+
constructor(source) {
|
|
116
|
+
this.source = source;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Read one view from the source.
|
|
120
|
+
* @param query - cwd/scope and cancellation for this observation.
|
|
121
|
+
* @returns normalized rows and completeness; incomplete data is not hidden.
|
|
122
|
+
*/
|
|
123
|
+
async read(query) {
|
|
124
|
+
const [snapshot, managed, protectedKeys, pinnedKeys] = await Promise.all([
|
|
125
|
+
this.source.snapshot(query),
|
|
126
|
+
this.source.managedSkillKeys(),
|
|
127
|
+
this.source.protectedSkillKeys?.() ?? Promise.resolve(/* @__PURE__ */ new Set()),
|
|
128
|
+
this.source.pinnedSkillKeys?.() ?? Promise.resolve(/* @__PURE__ */ new Set())
|
|
129
|
+
]);
|
|
130
|
+
const rows = snapshot.skills.map((summary) => skillIndexRowOf(summary, managed.has(skillKeyOf(summary)), protectedKeys.has(skillKeyOf(summary)), pinnedKeys.has(skillKeyOf(summary)))).sort(bySkillKey);
|
|
131
|
+
return {
|
|
132
|
+
viewKey: viewKeyOf(query),
|
|
133
|
+
catalogRevision: catalogRevisionOf(rows, snapshot.complete),
|
|
134
|
+
viewCatalogDigest: viewCatalogDigestOf(viewKeyOf(query), rows, snapshot.complete),
|
|
135
|
+
rows,
|
|
136
|
+
complete: snapshot.complete
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
//#endregion
|
|
141
|
+
//#region lib/types/search.js
|
|
142
|
+
/** Tokenizer version; changing the algorithm requires rebuilding indexes. */
|
|
143
|
+
const SKILL_SEARCH_TOKENIZER_VERSION = "cjk-bigram-v1";
|
|
144
|
+
/** Fields indexed by skill search; no body/path. */
|
|
145
|
+
const SKILL_SEARCH_FIELDS = [
|
|
146
|
+
"name",
|
|
147
|
+
"description",
|
|
148
|
+
"whenToUse"
|
|
149
|
+
];
|
|
150
|
+
/** Index schema version; bump when indexed fields/options change. */
|
|
151
|
+
const SKILL_SEARCH_INDEX_SCHEMA_VERSION = "skill-search-v1";
|
|
152
|
+
const CJK = String.raw`[[\p{scx=Han}\p{scx=Hira}\p{scx=Kana}\p{scx=Hang}]&&[\p{L}\p{M}\p{N}]]`;
|
|
153
|
+
const RUN = new RegExp(String.raw`${CJK}+|[[\p{L}\p{M}\p{N}]--${CJK}]+`, "gv");
|
|
154
|
+
const CJK_START = new RegExp(String.raw`^${CJK}`, "v");
|
|
155
|
+
/**
|
|
156
|
+
* Split NFKC text into CJK overlapping bigrams plus tail, and ASCII/other runs.
|
|
157
|
+
* Parity source: plugin-market/src/search.ts searchTerms.
|
|
158
|
+
* @param text - indexed or queried text.
|
|
159
|
+
* @returns terms in text order; callers lowercase.
|
|
160
|
+
*/
|
|
161
|
+
function searchTerms(text) {
|
|
162
|
+
return [...text.normalize("NFKC").matchAll(RUN)].flatMap(([run]) => {
|
|
163
|
+
if (!CJK_START.test(run)) return [run];
|
|
164
|
+
const chars = Array.from(run);
|
|
165
|
+
return [...chars.slice(1).map((char, position) => chars[position] + char), chars.at(-1)];
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
function clampLimit(limit) {
|
|
169
|
+
if (limit === void 0) return 3;
|
|
170
|
+
return Math.min(20, Math.max(1, Math.trunc(limit)));
|
|
171
|
+
}
|
|
172
|
+
function build(rows) {
|
|
173
|
+
const engine = new MiniSearch({
|
|
174
|
+
fields: [...SKILL_SEARCH_FIELDS],
|
|
175
|
+
storeFields: [...SKILL_SEARCH_FIELDS],
|
|
176
|
+
tokenize: searchTerms,
|
|
177
|
+
processTerm: (term) => term.toLowerCase(),
|
|
178
|
+
searchOptions: {
|
|
179
|
+
boost: {
|
|
180
|
+
name: 3,
|
|
181
|
+
whenToUse: 2
|
|
182
|
+
},
|
|
183
|
+
prefix: true,
|
|
184
|
+
combineWith: "AND",
|
|
185
|
+
fuzzy: (term) => term.length >= 4 && !CJK_START.test(term) ? .2 : false,
|
|
186
|
+
maxFuzzy: 2
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
const seen = /* @__PURE__ */ new Set();
|
|
190
|
+
const documents = [];
|
|
191
|
+
for (const row of rows) {
|
|
192
|
+
if (!row.modelInvocable || seen.has(row.skillKey)) continue;
|
|
193
|
+
seen.add(row.skillKey);
|
|
194
|
+
documents.push({
|
|
195
|
+
id: row.skillKey,
|
|
196
|
+
name: row.name,
|
|
197
|
+
description: row.description,
|
|
198
|
+
whenToUse: row.whenToUse ?? ""
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
engine.addAll(documents);
|
|
202
|
+
return engine;
|
|
203
|
+
}
|
|
204
|
+
/** Per-view MiniSearch index; no global engine and no projection-only filtering. */
|
|
205
|
+
var SkillSearchIndex = class SkillSearchIndex {
|
|
206
|
+
engine;
|
|
207
|
+
staleState = false;
|
|
208
|
+
viewKey;
|
|
209
|
+
constructor(viewKey, engine) {
|
|
210
|
+
this.viewKey = viewKey;
|
|
211
|
+
this.engine = engine;
|
|
212
|
+
}
|
|
213
|
+
/** @returns an index over every modelInvocable row in this view. */
|
|
214
|
+
static fromRows(viewKey, rows) {
|
|
215
|
+
return new SkillSearchIndex(viewKey, build(rows));
|
|
216
|
+
}
|
|
217
|
+
/** Mark the current index stale before a rebuild finishes. */
|
|
218
|
+
markStale() {
|
|
219
|
+
this.staleState = true;
|
|
220
|
+
}
|
|
221
|
+
/** Rebuild from this view's full rows; a failed rebuild keeps the old engine stale. */
|
|
222
|
+
async rebuild(rows) {
|
|
223
|
+
this.staleState = true;
|
|
224
|
+
try {
|
|
225
|
+
this.engine = build(rows);
|
|
226
|
+
this.staleState = false;
|
|
227
|
+
return true;
|
|
228
|
+
} catch {
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
/** @returns hits, truncation, and whether the index is stale. */
|
|
233
|
+
async search(query, options) {
|
|
234
|
+
const limit = clampLimit(query.limit);
|
|
235
|
+
const terms = searchTerms(query.query);
|
|
236
|
+
if (terms.length === 0) return {
|
|
237
|
+
results: [],
|
|
238
|
+
truncated: false,
|
|
239
|
+
stale: this.staleState
|
|
240
|
+
};
|
|
241
|
+
let fallback = false;
|
|
242
|
+
let hits = this.engine.search(query.query, { combineWith: "AND" });
|
|
243
|
+
if (hits.length === 0 && terms.length > 1) {
|
|
244
|
+
const relaxed = this.engine.search(query.query, { combineWith: "OR" });
|
|
245
|
+
fallback = relaxed.length > 0;
|
|
246
|
+
hits = relaxed;
|
|
247
|
+
}
|
|
248
|
+
const allowed = options?.allowedSkillKeys;
|
|
249
|
+
const filtered = allowed === void 0 ? hits : hits.filter((hit) => allowed.has(String(hit.id)));
|
|
250
|
+
return {
|
|
251
|
+
results: filtered.slice(0, limit).map((hit) => {
|
|
252
|
+
const fields = new Set(Object.values(hit.match).flat());
|
|
253
|
+
return {
|
|
254
|
+
name: String(hit.name),
|
|
255
|
+
description: String(hit.description),
|
|
256
|
+
...hit.whenToUse === void 0 || hit.whenToUse === "" ? {} : { whenToUse: String(hit.whenToUse) },
|
|
257
|
+
matchedBy: fields.has("name") ? "name" : fields.has("whenToUse") ? "whenToUse" : "description"
|
|
258
|
+
};
|
|
259
|
+
}),
|
|
260
|
+
truncated: filtered.length > limit,
|
|
261
|
+
...fallback ? { fallback: true } : {},
|
|
262
|
+
stale: this.staleState
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
/** Per-view cache keyed by searchIndexKey; never shares one engine across views. */
|
|
267
|
+
var SkillSearchCache = class {
|
|
268
|
+
entries = /* @__PURE__ */ new Map();
|
|
269
|
+
/**
|
|
270
|
+
* Get or build one view index. A failed rebuild retains the previous engine
|
|
271
|
+
* and marks it stale; no previous engine means undefined.
|
|
272
|
+
* @param key - identity inputs for searchIndexKey.
|
|
273
|
+
* @param rows - current full rows for the view.
|
|
274
|
+
* @returns index plus stale state, or undefined when no index is available.
|
|
275
|
+
*/
|
|
276
|
+
async get(key, rows) {
|
|
277
|
+
const cacheKey = searchIndexKeyOf({
|
|
278
|
+
viewKey: key.viewKey,
|
|
279
|
+
viewCatalogDigest: key.viewCatalogDigest,
|
|
280
|
+
policyDigest: key.policyDigest,
|
|
281
|
+
tokenizerVersion: SKILL_SEARCH_TOKENIZER_VERSION,
|
|
282
|
+
indexSchemaVersion: SKILL_SEARCH_INDEX_SCHEMA_VERSION,
|
|
283
|
+
discoveryMode: key.discoveryMode
|
|
284
|
+
});
|
|
285
|
+
const existing = this.entries.get(cacheKey);
|
|
286
|
+
if (existing !== void 0) return {
|
|
287
|
+
index: existing.index,
|
|
288
|
+
stale: false
|
|
289
|
+
};
|
|
290
|
+
try {
|
|
291
|
+
const index = SkillSearchIndex.fromRows(key.viewKey, rows);
|
|
292
|
+
this.entries.set(cacheKey, {
|
|
293
|
+
key: cacheKey,
|
|
294
|
+
viewKey: key.viewKey,
|
|
295
|
+
index
|
|
296
|
+
});
|
|
297
|
+
return {
|
|
298
|
+
index,
|
|
299
|
+
stale: false
|
|
300
|
+
};
|
|
301
|
+
} catch {
|
|
302
|
+
const old = [...this.entries.values()].find((entry) => entry.viewKey === key.viewKey);
|
|
303
|
+
if (old === void 0) return void 0;
|
|
304
|
+
old.index.markStale();
|
|
305
|
+
return {
|
|
306
|
+
index: old.index,
|
|
307
|
+
stale: true
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
/** Drop every index for one view. */
|
|
312
|
+
invalidate(viewKey) {
|
|
313
|
+
for (const [cacheKey, entry] of this.entries) if (entry.viewKey === viewKey) this.entries.delete(cacheKey);
|
|
314
|
+
}
|
|
315
|
+
/** Drop every cached view index. */
|
|
316
|
+
clear() {
|
|
317
|
+
this.entries.clear();
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
//#endregion
|
|
321
|
+
//#region lib/types/tool.js
|
|
322
|
+
/** Static model-facing description; no dynamic domain list/tree. */
|
|
323
|
+
const SKILL_SEARCH_DESCRIPTION = "Search skill summaries by name, description, or whenToUse when coverage is uncertain. Results are summaries, not bodies; a matching name can be loaded directly. Empty results are inconclusive; the full catalog remains available.";
|
|
324
|
+
/** @returns response or a degraded hint; never silent empty on failure. */
|
|
325
|
+
async function searchWithDegraded(provider, query, allowedSkillKeys, agent, signal) {
|
|
326
|
+
if (provider === void 0) return {
|
|
327
|
+
results: [],
|
|
328
|
+
truncated: false,
|
|
329
|
+
degraded: true,
|
|
330
|
+
hint: "Skill search is unavailable. Use the full catalog or load an exact skill by name with skill(name)."
|
|
331
|
+
};
|
|
332
|
+
try {
|
|
333
|
+
return await provider.search(query, allowedSkillKeys === void 0 ? void 0 : { allowedSkillKeys }, agent, signal);
|
|
334
|
+
} catch {
|
|
335
|
+
return {
|
|
336
|
+
results: [],
|
|
337
|
+
truncated: false,
|
|
338
|
+
degraded: true,
|
|
339
|
+
hint: "Skill search failed. Use the full catalog or load an exact skill by name with skill(name)."
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
function toToolResponse(response) {
|
|
344
|
+
return {
|
|
345
|
+
results: response.results.map((hit) => ({
|
|
346
|
+
name: hit.name,
|
|
347
|
+
description: hit.description,
|
|
348
|
+
...hit.whenToUse === void 0 ? {} : { whenToUse: hit.whenToUse },
|
|
349
|
+
...hit.matchedBy === void 0 ? {} : { matchedBy: hit.matchedBy }
|
|
350
|
+
})),
|
|
351
|
+
truncated: response.truncated,
|
|
352
|
+
...response.fallback === void 0 ? {} : { fallback: response.fallback },
|
|
353
|
+
...response.stale === void 0 ? {} : { stale: response.stale },
|
|
354
|
+
...response.degraded === void 0 ? {} : { degraded: response.degraded },
|
|
355
|
+
...response.hint === void 0 ? {} : { hint: response.hint }
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Register skill_search on the optional tools service. The description/schema are static; the
|
|
360
|
+
* provider is the only dynamic dependency.
|
|
361
|
+
* platform-declaration anchor: tools is declared in core PLATFORM_SERVICE_PROBES.
|
|
362
|
+
* @param ctx - plugin context.
|
|
363
|
+
* @param provider - search service or undefined when unavailable.
|
|
364
|
+
* @returns tool disposer.
|
|
365
|
+
*/
|
|
366
|
+
function registerSkillSearchTool(ctx, provider) {
|
|
367
|
+
const tools = ctx.get("tools");
|
|
368
|
+
if (tools === void 0) return () => {};
|
|
369
|
+
return tools.register(defineTool({
|
|
370
|
+
name: "skill_search",
|
|
371
|
+
description: SKILL_SEARCH_DESCRIPTION,
|
|
372
|
+
parameters: {
|
|
373
|
+
query: {
|
|
374
|
+
type: "string",
|
|
375
|
+
required: true,
|
|
376
|
+
description: "Search terms; CJK and ASCII are supported."
|
|
377
|
+
},
|
|
378
|
+
limit: {
|
|
379
|
+
type: "integer",
|
|
380
|
+
description: "Maximum results; defaults to 3, capped at 20."
|
|
381
|
+
}
|
|
382
|
+
},
|
|
383
|
+
output: {
|
|
384
|
+
schema: {
|
|
385
|
+
type: "object",
|
|
386
|
+
additionalProperties: false,
|
|
387
|
+
properties: {
|
|
388
|
+
results: {
|
|
389
|
+
type: "array",
|
|
390
|
+
required: true,
|
|
391
|
+
items: {
|
|
392
|
+
type: "object",
|
|
393
|
+
additionalProperties: false,
|
|
394
|
+
properties: {
|
|
395
|
+
name: {
|
|
396
|
+
type: "string",
|
|
397
|
+
required: true
|
|
398
|
+
},
|
|
399
|
+
description: {
|
|
400
|
+
type: "string",
|
|
401
|
+
required: true
|
|
402
|
+
},
|
|
403
|
+
whenToUse: { type: "string" },
|
|
404
|
+
matchedBy: { type: "string" }
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
},
|
|
408
|
+
truncated: {
|
|
409
|
+
type: "boolean",
|
|
410
|
+
required: true
|
|
411
|
+
},
|
|
412
|
+
fallback: { type: "boolean" },
|
|
413
|
+
stale: { type: "boolean" },
|
|
414
|
+
degraded: { type: "boolean" },
|
|
415
|
+
hint: { type: "string" }
|
|
416
|
+
}
|
|
417
|
+
},
|
|
418
|
+
render: (_args, value) => [{
|
|
419
|
+
type: "text",
|
|
420
|
+
text: value.degraded === true ? "Skill search is degraded: " + (value.hint ?? "use the full catalog or skill(name).") : "Found " + value.results.length + " skill result(s)" + (value.fallback === true ? " (OR fallback)." : ".") + (value.stale === true ? " (stale index)." : "") + "\n" + value.results.map((result) => "- " + result.name + ": " + result.description + (result.whenToUse === void 0 ? "" : " | when: " + result.whenToUse)).join("\n")
|
|
421
|
+
}]
|
|
422
|
+
},
|
|
423
|
+
async execute(args, exec) {
|
|
424
|
+
try {
|
|
425
|
+
const allowed = await provider?.allowedSkillKeys?.(exec.agent, exec.signal);
|
|
426
|
+
return toToolResponse(await searchWithDegraded(provider, {
|
|
427
|
+
query: args.query,
|
|
428
|
+
limit: args.limit
|
|
429
|
+
}, allowed, exec.agent, exec.signal));
|
|
430
|
+
} catch (error) {
|
|
431
|
+
return {
|
|
432
|
+
results: [],
|
|
433
|
+
truncated: false,
|
|
434
|
+
degraded: true,
|
|
435
|
+
hint: "Skill search failed: " + (error instanceof Error ? error.message : String(error))
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}));
|
|
440
|
+
}
|
|
441
|
+
/** @returns true only for a search implementation; used by composition wiring. */
|
|
442
|
+
function isSkillSearch(value) {
|
|
443
|
+
return typeof value === "object" && value !== null && typeof value.search === "function";
|
|
444
|
+
}
|
|
445
|
+
//#endregion
|
|
446
|
+
export { skillKeyOf as _, SKILL_SEARCH_FIELDS as a, viewKeyOf as b, SkillSearchCache as c, SkillIndex as d, catalogRevisionOf as f, skillIndexRowOf as g, searchIndexKeyOf as h, searchWithDegraded as i, SkillSearchIndex as l, projectionDigestOf as m, isSkillSearch as n, SKILL_SEARCH_INDEX_SCHEMA_VERSION as o, policyDigestOf as p, registerSkillSearchTool as r, SKILL_SEARCH_TOKENIZER_VERSION as s, SKILL_SEARCH_DESCRIPTION as t, searchTerms as u, threeFieldHashOf as v, viewCatalogDigestOf as y };
|
|
@@ -3,7 +3,7 @@ import type { AssignmentQueue, AssignmentQueueEntry, AssignmentSubmission } from
|
|
|
3
3
|
/** Durable assignment queue; process-local cache over one JSON file. */
|
|
4
4
|
export declare class JsonAssignmentQueue implements AssignmentQueue {
|
|
5
5
|
private readonly entries;
|
|
6
|
-
private
|
|
6
|
+
private loadPromise;
|
|
7
7
|
private readonly io;
|
|
8
8
|
private readonly path;
|
|
9
9
|
constructor(io: EvolutionIoLike, path: string);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { UserMessage } from '@deepseek-ai/dsh-llm';
|
|
2
|
+
import type { CatalogEntry } from './projection.ts';
|
|
3
|
+
interface CatalogSource {
|
|
4
|
+
readonly kind: 'skill-catalog';
|
|
5
|
+
readonly form: 'catalog';
|
|
6
|
+
readonly update?: true;
|
|
7
|
+
readonly entries: readonly CatalogEntry[];
|
|
8
|
+
}
|
|
9
|
+
/** Captured platform catalog; base text and source are kept verbatim. */
|
|
10
|
+
export interface CatalogCapture {
|
|
11
|
+
readonly text: string;
|
|
12
|
+
readonly source: CatalogSource;
|
|
13
|
+
readonly entries: readonly CatalogEntry[];
|
|
14
|
+
readonly update: boolean;
|
|
15
|
+
}
|
|
16
|
+
/** @returns captured catalog when text has the known available_skills block. */
|
|
17
|
+
export declare function captureCatalog(message: UserMessage): CatalogCapture | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Replace only the available_skills block; preamble, footer, and update marker stay byte-identical.
|
|
20
|
+
* @param capture - platform message captured before projection.
|
|
21
|
+
* @param entries - projected entries.
|
|
22
|
+
* @returns a platform-sourced message with the same framing.
|
|
23
|
+
*/
|
|
24
|
+
export declare function renderCatalog(capture: CatalogCapture, entries: readonly CatalogEntry[]): UserMessage;
|
|
25
|
+
/**
|
|
26
|
+
* Build a replacement catalog when no current catalog message exists.
|
|
27
|
+
* @param sourceEntries - full platform entries; keeps the platform digest stable.
|
|
28
|
+
* @param entries - projected entries.
|
|
29
|
+
* @returns a skill-catalog message with update=true.
|
|
30
|
+
*/
|
|
31
|
+
export declare function renderCatalogUpdate(sourceEntries: readonly CatalogEntry[], entries: readonly CatalogEntry[]): UserMessage;
|
|
32
|
+
/** @returns the exact platform message captured before projection. */
|
|
33
|
+
export declare function restoreCatalog(capture: CatalogCapture): UserMessage;
|
|
34
|
+
export {};
|
|
35
|
+
//# sourceMappingURL=catalog-codec.d.ts.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { DomainMapStore } from './domain-map.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Re-render stale domain descriptions from their definition fields.
|
|
4
|
+
* @param store - domain map store.
|
|
5
|
+
* @returns number of refreshed domains.
|
|
6
|
+
*/
|
|
7
|
+
export declare function refreshStaleDescriptions(store: DomainMapStore): Promise<number>;
|
|
8
|
+
//# sourceMappingURL=description-refresh.d.ts.map
|
|
@@ -1,14 +1,32 @@
|
|
|
1
1
|
import type { DomainMapSnapshot, DomainMapStore } from './domain-map.ts';
|
|
2
|
-
import {
|
|
2
|
+
import type { DomainGenerationProposal, GenerationSkillFact } from './domain-generation.ts';
|
|
3
3
|
import type { SkillKey } from './identity.ts';
|
|
4
|
+
/** Real fact used by the production initial-map writer. */
|
|
5
|
+
export interface BootstrapSkillFact extends GenerationSkillFact {
|
|
6
|
+
readonly skillKey: SkillKey;
|
|
7
|
+
readonly threeFieldHash: string;
|
|
8
|
+
}
|
|
9
|
+
/** Provenance persisted with generated domains and edges. */
|
|
10
|
+
export interface BootstrapProvenance {
|
|
11
|
+
readonly promptVersion: string;
|
|
12
|
+
readonly sourceDigest: string;
|
|
13
|
+
readonly model: string;
|
|
14
|
+
readonly runId: string;
|
|
15
|
+
}
|
|
4
16
|
/**
|
|
5
|
-
* Write an initial LLM
|
|
6
|
-
* validation; no
|
|
7
|
-
* @param store -
|
|
8
|
-
* @param proposal -
|
|
9
|
-
* @param
|
|
17
|
+
* Write an initial LLM proposal using live skill facts. Code owns IDs and
|
|
18
|
+
* validation; no synthetic description/hash is written.
|
|
19
|
+
* @param store - map owner.
|
|
20
|
+
* @param proposal - 8–12 domain proposal.
|
|
21
|
+
* @param facts - real rows with stable keys and three-field hashes.
|
|
10
22
|
* @param expectedMapVersion - CAS precondition.
|
|
23
|
+
* @param provenance - prompt/run identity for audit.
|
|
11
24
|
* @returns committed snapshot.
|
|
12
25
|
*/
|
|
26
|
+
export declare function bootstrapDomainMapWithFacts(store: DomainMapStore, proposal: DomainGenerationProposal, facts: readonly BootstrapSkillFact[], expectedMapVersion: string, provenance: BootstrapProvenance): Promise<DomainMapSnapshot>;
|
|
27
|
+
/**
|
|
28
|
+
* Legacy fallback for callers that only have a name -> key mapping.
|
|
29
|
+
* Production must use bootstrapDomainMapWithFacts.
|
|
30
|
+
*/
|
|
13
31
|
export declare function bootstrapDomainMap(store: DomainMapStore, proposal: DomainGenerationProposal, skillKeys: Readonly<Record<string, SkillKey>>, expectedMapVersion: string): Promise<DomainMapSnapshot>;
|
|
14
32
|
//# sourceMappingURL=domain-bootstrap.d.ts.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { DomainMapSnapshot, DomainMapStore } from './domain-map.ts';
|
|
2
|
+
import { type BootstrapSkillFact } from './domain-bootstrap.ts';
|
|
3
|
+
import type { DomainGenerationPort } from './domain-generation-service.ts';
|
|
4
|
+
/** Input for one initial map generation from a live skill view. */
|
|
5
|
+
export interface GenerationRequest {
|
|
6
|
+
readonly mapScope: string;
|
|
7
|
+
readonly sourceDigest: string;
|
|
8
|
+
readonly expectedMapVersion: string;
|
|
9
|
+
readonly facts: readonly BootstrapSkillFact[];
|
|
10
|
+
readonly prompt: string;
|
|
11
|
+
readonly promptVersion: string;
|
|
12
|
+
readonly model: string;
|
|
13
|
+
readonly runId: string;
|
|
14
|
+
readonly signal?: AbortSignal | undefined;
|
|
15
|
+
}
|
|
16
|
+
/** Single-flight initial map generation; mapVersion CAS is the final arbiter. */
|
|
17
|
+
export declare class DomainGenerationCoordinator {
|
|
18
|
+
private readonly store;
|
|
19
|
+
private readonly port;
|
|
20
|
+
private readonly inflight;
|
|
21
|
+
private epoch;
|
|
22
|
+
constructor(store: DomainMapStore, port: DomainGenerationPort);
|
|
23
|
+
/** @returns current map version; increment makes older requests stale. */
|
|
24
|
+
get currentEpoch(): number;
|
|
25
|
+
/**
|
|
26
|
+
* Generate an initial map once per scope; concurrent calls share one job.
|
|
27
|
+
* @param request - live skills, stable keys, prompt, and CAS precondition.
|
|
28
|
+
* @returns committed or existing snapshot.
|
|
29
|
+
*/
|
|
30
|
+
ensure(request: GenerationRequest): Promise<DomainMapSnapshot>;
|
|
31
|
+
private run;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=domain-generation-coordinator.d.ts.map
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { BlockAssembler } from '@deepseek-ai/dsh-llm';
|
|
2
|
+
import type { ContextFormed, UserMessage } from '@deepseek-ai/dsh-llm';
|
|
3
|
+
import { type DomainGenerationProposal, type GenerationSkillFact } from './domain-generation.ts';
|
|
4
|
+
import { DOMAIN_GENERATION_PROMPT_VERSION } from './domain-generation-prompt.ts';
|
|
5
|
+
import type { DomainGenerationPort } from './domain-generation-service.ts';
|
|
6
|
+
declare module '@deepseek-ai/dsh-llm' {
|
|
7
|
+
interface MessageSourceMap {
|
|
8
|
+
'evolution-skill-generation': {
|
|
9
|
+
readonly kind: 'evolution-skill-generation';
|
|
10
|
+
readonly form: 'notice';
|
|
11
|
+
readonly summary: string;
|
|
12
|
+
} & ContextFormed;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/** Typed failure from LLM transport/parse/validation. */
|
|
16
|
+
export declare class DomainGenerationError extends Error {
|
|
17
|
+
readonly code: 'llm-unavailable' | 'timeout' | 'invalid-output' | 'invalid-proposal';
|
|
18
|
+
constructor(code: DomainGenerationError['code'], message: string);
|
|
19
|
+
}
|
|
20
|
+
type LlmChunk = Parameters<BlockAssembler['push']>[0];
|
|
21
|
+
/** Minimal platform LLM service used by the generation port. */
|
|
22
|
+
export interface LlmStreamService {
|
|
23
|
+
stream(options: {
|
|
24
|
+
readonly provider: string;
|
|
25
|
+
readonly model: string;
|
|
26
|
+
readonly messages: readonly UserMessage[];
|
|
27
|
+
readonly maxTokens: number;
|
|
28
|
+
readonly signal?: AbortSignal | undefined;
|
|
29
|
+
}): AsyncIterable<LlmChunk>;
|
|
30
|
+
}
|
|
31
|
+
/** @returns parsed proposal with the caller's skill set; throws on malformed/empty output. */
|
|
32
|
+
export declare function parseDomainGenerationProposal(text: string, skills: readonly GenerationSkillFact[]): DomainGenerationProposal;
|
|
33
|
+
/** Production LLM adapter; validates before returning, never writes the map. */
|
|
34
|
+
export declare class LlmDomainGenerationPort implements DomainGenerationPort {
|
|
35
|
+
private readonly llm;
|
|
36
|
+
private readonly options;
|
|
37
|
+
constructor(llm: LlmStreamService, options: {
|
|
38
|
+
readonly provider: string;
|
|
39
|
+
readonly model: string;
|
|
40
|
+
readonly maxTokens?: number | undefined;
|
|
41
|
+
readonly timeoutMs?: number | undefined;
|
|
42
|
+
readonly attempts?: number | undefined;
|
|
43
|
+
});
|
|
44
|
+
/**
|
|
45
|
+
* Generate one validated domain proposal.
|
|
46
|
+
* @param input - live facts, prompt, and cancellation.
|
|
47
|
+
* @returns validated proposal with 8–12 domains and known evidence.
|
|
48
|
+
*/
|
|
49
|
+
generate(input: {
|
|
50
|
+
readonly skills: readonly GenerationSkillFact[];
|
|
51
|
+
readonly prompt: string;
|
|
52
|
+
readonly signal?: AbortSignal | undefined;
|
|
53
|
+
}): Promise<DomainGenerationProposal>;
|
|
54
|
+
}
|
|
55
|
+
export { DOMAIN_GENERATION_PROMPT_VERSION };
|
|
56
|
+
//# sourceMappingURL=domain-generation-llm.d.ts.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type DomainPolicyLayer, type EffectiveDomainPolicy } from './domain-policy.ts';
|
|
2
|
+
/** Inputs for one policy resolution. */
|
|
3
|
+
export interface PolicyStoreInput {
|
|
4
|
+
readonly global: DomainPolicyLayer;
|
|
5
|
+
readonly workspace?: DomainPolicyLayer | undefined;
|
|
6
|
+
readonly scope?: DomainPolicyLayer | undefined;
|
|
7
|
+
readonly knownDomainIds?: ReadonlySet<string> | undefined;
|
|
8
|
+
}
|
|
9
|
+
/** Resolves global/workspace/scope policy without touching DomainMap or mapVersion. */
|
|
10
|
+
export declare class DomainPolicyStore {
|
|
11
|
+
private readonly input;
|
|
12
|
+
constructor(input: PolicyStoreInput);
|
|
13
|
+
/**
|
|
14
|
+
* @param knownDomainIds - optional live id set; overrides the construction-time set.
|
|
15
|
+
* @returns effective policy with unknown ids reported as degraded.
|
|
16
|
+
*/
|
|
17
|
+
resolve(knownDomainIds?: ReadonlySet<string> | undefined): EffectiveDomainPolicy;
|
|
18
|
+
/**
|
|
19
|
+
* @param knownDomainIds - optional live id set used for the effective policy.
|
|
20
|
+
* @returns stable digest of effective policy and all layer revisions.
|
|
21
|
+
*/
|
|
22
|
+
digest(knownDomainIds?: ReadonlySet<string> | undefined): string;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=domain-policy-store.d.ts.map
|
|
@@ -24,6 +24,7 @@ export interface DomainProjectionInput {
|
|
|
24
24
|
readonly tokenBudget?: number | undefined;
|
|
25
25
|
readonly now?: number | undefined;
|
|
26
26
|
readonly staleAfterHours?: number | undefined;
|
|
27
|
+
readonly descriptionStaleAfterHours?: number | undefined;
|
|
27
28
|
readonly maxVisibleDepth?: number | undefined;
|
|
28
29
|
}
|
|
29
30
|
/**
|
|
@@ -34,10 +35,4 @@ export interface DomainProjectionInput {
|
|
|
34
35
|
*/
|
|
35
36
|
export declare function hiddenRecoverable(hiddenNames: readonly string[], searchResultNames: ReadonlySet<string>): boolean;
|
|
36
37
|
export declare function buildDomainProjection(input: DomainProjectionInput): ProjectionResult;
|
|
37
|
-
/** @returns true when shadow evidence is sufficient to promote to on. */
|
|
38
|
-
export declare function canPromoteShadow(input: {
|
|
39
|
-
readonly samples: number;
|
|
40
|
-
readonly hiddenRecoverableRate: number;
|
|
41
|
-
readonly silentMisses: number;
|
|
42
|
-
}): boolean;
|
|
43
38
|
//# sourceMappingURL=domain-projection.d.ts.map
|