@lmzhen/dsh-evolution-skill-discovery 0.32.2 → 0.32.4
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/README.md +1 -1
- package/lib/main-entry.js +219 -609
- package/lib/types/description-refresh.d.ts +22 -1
- package/lib/types/domain-bootstrap.d.ts +1 -1
- package/lib/types/domain-map.d.ts +5 -0
- package/lib/types/domain-policy.d.ts +1 -1
- package/lib/types/domain-projection.d.ts +1 -9
- package/lib/types/generation-composition.d.ts +1 -1
- package/lib/types/index.d.ts +1 -4
- package/lib/types/main-entry.d.ts +1 -4
- package/lib/types/projection-adapter.d.ts +2 -2
- package/lib/types/projection-journal.d.ts +2 -0
- package/lib/types/projection-runtime.d.ts +19 -1
- package/lib/types/search.d.ts +6 -0
- package/lib/types/view-snapshot-store.d.ts +2 -0
- package/lib/types/view-state.d.ts +2 -4
- package/package.json +5 -7
- package/lib/types/assignment-queue.d.ts +0 -26
- package/lib/types/assignment-sidecar.d.ts +0 -33
- package/lib/types/assignment.d.ts +0 -131
- package/lib/types/worker.d.ts +0 -14
package/lib/main-entry.js
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import { isAbsolute, join, resolve } from "node:path";
|
|
2
2
|
import { createHash, randomUUID } from "node:crypto";
|
|
3
|
-
import { authorityFrom, evolutionIoAdapter, evolutionRoot, isRecord, makeSerialQueue, newSkillLibrary
|
|
3
|
+
import { authorityFrom, evolutionIoAdapter, evolutionRoot, installEvolutionProjections, isRecord, makeSerialQueue, newSkillLibrary } from "@lmzhen/dsh-evolution-core";
|
|
4
4
|
import { BlockAssembler, createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
5
5
|
import { escapeText } from "@deepseek-ai/dsh-skill";
|
|
6
6
|
import MiniSearch from "minisearch";
|
|
7
7
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
8
|
+
//#region lib/types/domain-map.js
|
|
9
|
+
/** The next CAS version. ONE implementation: non-numeric versions get a suffix,
|
|
10
|
+
* never NaN, and every writer advances the same way. */
|
|
11
|
+
function nextMapVersion(version) {
|
|
12
|
+
const numeric = Number(version);
|
|
13
|
+
return Number.isSafeInteger(numeric) && numeric >= 0 ? String(numeric + 1) : version + ".1";
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
8
16
|
//#region lib/types/domain-generation.js
|
|
9
17
|
/** @returns stable id derived from title, not the title itself. */
|
|
10
18
|
function mintDomainId(title) {
|
|
@@ -127,6 +135,7 @@ async function bootstrapDomainMapWithFacts(store, proposal, facts, expectedMapVe
|
|
|
127
135
|
descriptionHash: "bootstrap",
|
|
128
136
|
descriptionVersion: 1,
|
|
129
137
|
descriptionStatus: "fresh",
|
|
138
|
+
descriptionUpdatedAt: now,
|
|
130
139
|
provenance: {
|
|
131
140
|
source: "llm",
|
|
132
141
|
promptVersion: provenance.promptVersion,
|
|
@@ -156,7 +165,7 @@ async function bootstrapDomainMapWithFacts(store, proposal, facts, expectedMapVe
|
|
|
156
165
|
});
|
|
157
166
|
}
|
|
158
167
|
return store.commit(expectedMapVersion, {
|
|
159
|
-
mapVersion:
|
|
168
|
+
mapVersion: nextMapVersion(expectedMapVersion),
|
|
160
169
|
domains,
|
|
161
170
|
edges
|
|
162
171
|
});
|
|
@@ -236,11 +245,7 @@ const ALLOWED = new Set([
|
|
|
236
245
|
"disableDomains",
|
|
237
246
|
"revision"
|
|
238
247
|
]);
|
|
239
|
-
const MODES = new Set([
|
|
240
|
-
"off",
|
|
241
|
-
"auto",
|
|
242
|
-
"on"
|
|
243
|
-
]);
|
|
248
|
+
const MODES = new Set(["off", "on"]);
|
|
244
249
|
function checkLayer(layer) {
|
|
245
250
|
for (const key of Object.keys(layer)) if (!ALLOWED.has(key)) throw new DomainPolicyValidationError("unsupported policy field " + key);
|
|
246
251
|
if (layer.mode !== void 0 && !MODES.has(layer.mode)) throw new DomainPolicyValidationError("unsupported policy mode");
|
|
@@ -303,7 +308,7 @@ function canonical(value) {
|
|
|
303
308
|
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)]));
|
|
304
309
|
return value;
|
|
305
310
|
}
|
|
306
|
-
function digest
|
|
311
|
+
function digest(value) {
|
|
307
312
|
return sha256(JSON.stringify(canonical(value)));
|
|
308
313
|
}
|
|
309
314
|
/** @returns stable key for the winning provider/source/name tuple. */
|
|
@@ -316,11 +321,11 @@ function skillKeyOf(summary) {
|
|
|
316
321
|
}
|
|
317
322
|
/** @returns stable hash of the canonical cwd and optional scope key. */
|
|
318
323
|
function viewKeyOf(input) {
|
|
319
|
-
return digest
|
|
324
|
+
return digest([resolve(input.cwd), input.scopeKey ?? null]);
|
|
320
325
|
}
|
|
321
326
|
/** @returns hash of the three fields used by discovery/retrieval. */
|
|
322
327
|
function threeFieldHashOf(input) {
|
|
323
|
-
return digest
|
|
328
|
+
return digest([
|
|
324
329
|
input.name,
|
|
325
330
|
input.description,
|
|
326
331
|
input.whenToUse ?? null
|
|
@@ -344,7 +349,7 @@ function skillIndexRowOf(summary, managed, protectedSkill = false, pinned = fals
|
|
|
344
349
|
}
|
|
345
350
|
/** @returns digest of the sorted rows plus completeness. */
|
|
346
351
|
function catalogRevisionOf(rows, complete) {
|
|
347
|
-
return digest
|
|
352
|
+
return digest([complete, rows.map((row) => [
|
|
348
353
|
row.skillKey,
|
|
349
354
|
row.name,
|
|
350
355
|
row.description,
|
|
@@ -360,11 +365,11 @@ function catalogRevisionOf(rows, complete) {
|
|
|
360
365
|
}
|
|
361
366
|
/** @returns digest of a concrete per-view snapshot. */
|
|
362
367
|
function viewCatalogDigestOf(viewKey, rows, complete) {
|
|
363
|
-
return digest
|
|
368
|
+
return digest([viewKey, catalogRevisionOf(rows, complete)]);
|
|
364
369
|
}
|
|
365
370
|
/** @returns digest of effective policy plus its revisions. */
|
|
366
371
|
function policyDigestOf(input) {
|
|
367
|
-
return digest
|
|
372
|
+
return digest([
|
|
368
373
|
input.globalPolicyRevision,
|
|
369
374
|
input.workspaceOverlayRevision ?? null,
|
|
370
375
|
input.scopeOverlayRevision ?? null,
|
|
@@ -373,7 +378,7 @@ function policyDigestOf(input) {
|
|
|
373
378
|
}
|
|
374
379
|
/** @returns view-cache identity for one projection. */
|
|
375
380
|
function projectionDigestOf(input) {
|
|
376
|
-
return digest
|
|
381
|
+
return digest([
|
|
377
382
|
input.viewKey,
|
|
378
383
|
input.mapVersion,
|
|
379
384
|
input.policyDigest,
|
|
@@ -385,7 +390,7 @@ function projectionDigestOf(input) {
|
|
|
385
390
|
}
|
|
386
391
|
/** @returns view-cache identity for one search index. */
|
|
387
392
|
function searchIndexKeyOf(input) {
|
|
388
|
-
return digest
|
|
393
|
+
return digest([
|
|
389
394
|
input.viewKey,
|
|
390
395
|
input.viewCatalogDigest,
|
|
391
396
|
input.policyDigest,
|
|
@@ -661,6 +666,18 @@ var ProjectionJournal = class {
|
|
|
661
666
|
await this.io.writeText(this.path, JSON.stringify(this.cache));
|
|
662
667
|
});
|
|
663
668
|
}
|
|
669
|
+
/** Delete every record for one session/view after a successful restore. */
|
|
670
|
+
async removeView(sessionId, viewKey, _codecVersion) {
|
|
671
|
+
await this.serial(async () => {
|
|
672
|
+
const current = await this.read();
|
|
673
|
+
const records = Object.fromEntries(Object.entries(current.records).filter(([, record]) => record.sessionId !== sessionId || record.viewKey !== viewKey));
|
|
674
|
+
this.cache = {
|
|
675
|
+
schemaVersion: 1,
|
|
676
|
+
records
|
|
677
|
+
};
|
|
678
|
+
await this.io.writeText(this.path, JSON.stringify(this.cache));
|
|
679
|
+
});
|
|
680
|
+
}
|
|
664
681
|
};
|
|
665
682
|
//#endregion
|
|
666
683
|
//#region lib/types/domain-quality.js
|
|
@@ -863,7 +880,7 @@ function buildDomainProjection(input) {
|
|
|
863
880
|
const linkedKeys = new Set(input.snapshot.edges.filter((edge) => edge.status === "active" || edge.status === "pending").map((edge) => edge.skillKey));
|
|
864
881
|
const unclassifiedCount = input.rows.filter((row) => row.modelInvocable && !linkedKeys.has(row.skillKey)).length;
|
|
865
882
|
const domains = input.snapshot.domains.filter((node) => node.status === "active" || node.status === "provisional");
|
|
866
|
-
const cardDomains = domains.filter((node) => node.parentIds.length
|
|
883
|
+
const cardDomains = domains.filter((node) => node.parentIds.length === 0).filter((node) => !input.policy.disabled.has(node.id)).filter((node) => input.policy.pinned.has(node.id) || qualityPasses(node, domains)).sort((left, right) => left.id < right.id ? -1 : left.id > right.id ? 1 : 0).slice(0, Math.max(1, input.maxCards ?? 12));
|
|
867
884
|
const domainById = new Map(domains.map((node) => [node.id, node]));
|
|
868
885
|
const cards = cardDomains.map((node) => {
|
|
869
886
|
let managedModelInvocableCount = 0;
|
|
@@ -885,7 +902,7 @@ function buildDomainProjection(input) {
|
|
|
885
902
|
id: node.id,
|
|
886
903
|
title: node.name,
|
|
887
904
|
purpose: node.purpose.slice(0, 80),
|
|
888
|
-
descriptionStatus: node.descriptionStatus === "fresh" && input.descriptionStaleAfterHours !== void 0 && input.now !== void 0 && input.now - node.updatedAt > input.descriptionStaleAfterHours * 36e5 ? "stale" : node.descriptionStatus,
|
|
905
|
+
descriptionStatus: node.descriptionStatus === "fresh" && input.descriptionStaleAfterHours !== void 0 && input.now !== void 0 && input.now - (node.descriptionUpdatedAt ?? node.updatedAt) > input.descriptionStaleAfterHours * 36e5 ? "stale" : node.descriptionStatus,
|
|
889
906
|
triggers: node.triggers.slice(0, 3),
|
|
890
907
|
managedModelInvocableCount,
|
|
891
908
|
externalCatalogCount,
|
|
@@ -914,7 +931,7 @@ function buildDomainProjection(input) {
|
|
|
914
931
|
wouldHide.add(edge.skillKey);
|
|
915
932
|
}
|
|
916
933
|
const activeManaged = mode === "on" && !mapStale && input.searchAvailable !== false && input.policy.degraded.length === 0 ? wouldHide : /* @__PURE__ */ new Set();
|
|
917
|
-
|
|
934
|
+
return projectCatalog({
|
|
918
935
|
entries: input.entries,
|
|
919
936
|
rows: input.rows,
|
|
920
937
|
activeManagedSkillKeys: activeManaged,
|
|
@@ -929,7 +946,6 @@ function buildDomainProjection(input) {
|
|
|
929
946
|
discoveryMode: input.discoveryMode,
|
|
930
947
|
complete: true
|
|
931
948
|
});
|
|
932
|
-
return mode === "on" ? projected : projected;
|
|
933
949
|
}
|
|
934
950
|
//#endregion
|
|
935
951
|
//#region lib/types/skill-index.js
|
|
@@ -1100,8 +1116,9 @@ function restoreCapturedProjection(decision, capture) {
|
|
|
1100
1116
|
* @param result - projection result.
|
|
1101
1117
|
* @returns decision with replacement catalog plus optional context.
|
|
1102
1118
|
*/
|
|
1103
|
-
function applyReplacementDecision(decision, sourceEntries, result) {
|
|
1119
|
+
function applyReplacementDecision(decision, sourceEntries, result, seen) {
|
|
1104
1120
|
if (decision.kind !== "enter") return decision;
|
|
1121
|
+
if (seen?.has(result.digest) === true) return decision;
|
|
1105
1122
|
const update = renderCatalogUpdate(sourceEntries, result.catalogEntries);
|
|
1106
1123
|
const context = renderProjectionContext(result);
|
|
1107
1124
|
const messages = context === void 0 ? [...decision.messages, update] : [
|
|
@@ -1155,11 +1172,11 @@ function renderProjectionContext(result) {
|
|
|
1155
1172
|
* @param result - complete projection.
|
|
1156
1173
|
* @returns new decision; unchanged when no catalog exists.
|
|
1157
1174
|
*/
|
|
1158
|
-
function applyProjectedDecision(decision, capture, result) {
|
|
1175
|
+
function applyProjectedDecision(decision, capture, result, seen) {
|
|
1159
1176
|
const withCatalog = applyCapturedProjection(decision, capture, result.catalogEntries);
|
|
1160
1177
|
const context = renderProjectionContext(result);
|
|
1161
1178
|
if (context === void 0 || withCatalog.kind !== "enter") return withCatalog;
|
|
1162
|
-
return withCatalog.messages.some((message) => message.source.kind === "evolution-skill-projection" && message.source.digest === result.digest) ? withCatalog : {
|
|
1179
|
+
return withCatalog.messages.some((message) => message.source.kind === "evolution-skill-projection" && message.source.digest === result.digest) || seen?.has(result.digest) === true ? withCatalog : {
|
|
1163
1180
|
...withCatalog,
|
|
1164
1181
|
messages: [...withCatalog.messages, context]
|
|
1165
1182
|
};
|
|
@@ -1169,7 +1186,7 @@ function applyProjectedDecision(decision, capture, result) {
|
|
|
1169
1186
|
/**
|
|
1170
1187
|
* Derive one view's state without consulting another view.
|
|
1171
1188
|
* @param input - per-view health and identity.
|
|
1172
|
-
* @returns state with reasons;
|
|
1189
|
+
* @returns state with reasons; card rendering is gated by canProject.
|
|
1173
1190
|
*/
|
|
1174
1191
|
function deriveViewState(input) {
|
|
1175
1192
|
const reasons = [...input.reasons ?? []];
|
|
@@ -1261,7 +1278,7 @@ function mapStateOf(snapshot, staleAfterHours, now) {
|
|
|
1261
1278
|
return "ready";
|
|
1262
1279
|
}
|
|
1263
1280
|
function effectiveMode(mode) {
|
|
1264
|
-
return mode
|
|
1281
|
+
return mode;
|
|
1265
1282
|
}
|
|
1266
1283
|
function catalogText(message) {
|
|
1267
1284
|
return message.content.map((block) => block.type === "text" ? block.text ?? "" : "").join("");
|
|
@@ -1269,9 +1286,36 @@ function catalogText(message) {
|
|
|
1269
1286
|
function fullCatalogText(capture) {
|
|
1270
1287
|
return catalogText(renderCatalog(capture, capture.entries));
|
|
1271
1288
|
}
|
|
1272
|
-
function
|
|
1289
|
+
function captureOfFrame(frame) {
|
|
1290
|
+
return {
|
|
1291
|
+
text: frame.text,
|
|
1292
|
+
source: {
|
|
1293
|
+
kind: "skill-catalog",
|
|
1294
|
+
form: "catalog",
|
|
1295
|
+
...frame.update ? { update: true } : {},
|
|
1296
|
+
entries: frame.entries
|
|
1297
|
+
},
|
|
1298
|
+
entries: frame.entries,
|
|
1299
|
+
update: frame.update
|
|
1300
|
+
};
|
|
1301
|
+
}
|
|
1302
|
+
function sessionCatalogCapture(agent, history) {
|
|
1303
|
+
if (history !== void 0 && history.catalogs.length > 0) {
|
|
1304
|
+
let full;
|
|
1305
|
+
let projected;
|
|
1306
|
+
for (const frame of history.catalogs) {
|
|
1307
|
+
const capture = captureOfFrame(frame);
|
|
1308
|
+
projected = capture;
|
|
1309
|
+
if (catalogText({ content: [{
|
|
1310
|
+
type: "text",
|
|
1311
|
+
text: frame.text
|
|
1312
|
+
}] }) === fullCatalogText(capture)) full = capture;
|
|
1313
|
+
}
|
|
1314
|
+
return full ?? projected;
|
|
1315
|
+
}
|
|
1273
1316
|
const readEvents = agent.session.readEvents;
|
|
1274
1317
|
if (readEvents === void 0) return void 0;
|
|
1318
|
+
let full;
|
|
1275
1319
|
let projected;
|
|
1276
1320
|
for (const event of readEvents()) {
|
|
1277
1321
|
if (event.type !== "user/message" || typeof event.data !== "object" || event.data === null) continue;
|
|
@@ -1280,9 +1324,27 @@ function sessionCatalogCapture(agent) {
|
|
|
1280
1324
|
const capture = captureCatalog(message);
|
|
1281
1325
|
if (capture === void 0) continue;
|
|
1282
1326
|
projected = capture;
|
|
1283
|
-
if (catalogText(message) === fullCatalogText(capture))
|
|
1327
|
+
if (catalogText(message) === fullCatalogText(capture)) full = capture;
|
|
1284
1328
|
}
|
|
1285
|
-
return projected;
|
|
1329
|
+
return full ?? projected;
|
|
1330
|
+
}
|
|
1331
|
+
/** The full platform catalog carried by a (possibly projected) capture. */
|
|
1332
|
+
function fullCaptureOf(capture) {
|
|
1333
|
+
return captureCatalog(renderCatalog(capture, capture.entries));
|
|
1334
|
+
}
|
|
1335
|
+
/** Digest of every projection context already present in session history. */
|
|
1336
|
+
function sessionProjectionDigests(agent, history) {
|
|
1337
|
+
const digests = new Set(history?.digests ?? []);
|
|
1338
|
+
const readEvents = agent.session.readEvents;
|
|
1339
|
+
if (readEvents === void 0) return digests;
|
|
1340
|
+
for (const event of readEvents()) {
|
|
1341
|
+
if (event.type !== "user/message" || typeof event.data !== "object" || event.data === null) continue;
|
|
1342
|
+
const message = event.data;
|
|
1343
|
+
if (message.source.kind !== "evolution-skill-projection") continue;
|
|
1344
|
+
const digest = message.source.digest;
|
|
1345
|
+
if (typeof digest === "string") digests.add(digest);
|
|
1346
|
+
}
|
|
1347
|
+
return digests;
|
|
1286
1348
|
}
|
|
1287
1349
|
/** Per-view projection runtime; no global readiness and no history edits. */
|
|
1288
1350
|
var ProjectionRuntime = class {
|
|
@@ -1294,6 +1356,7 @@ var ProjectionRuntime = class {
|
|
|
1294
1356
|
at: 0
|
|
1295
1357
|
};
|
|
1296
1358
|
lastByAgent = /* @__PURE__ */ new Map();
|
|
1359
|
+
warnedMissingHistory = false;
|
|
1297
1360
|
constructor(ports) {
|
|
1298
1361
|
this.ports = ports;
|
|
1299
1362
|
}
|
|
@@ -1301,6 +1364,14 @@ var ProjectionRuntime = class {
|
|
|
1301
1364
|
report(agent) {
|
|
1302
1365
|
return agent === void 0 ? this.last : this.lastByAgent.get(String(agent.id)) ?? this.last;
|
|
1303
1366
|
}
|
|
1367
|
+
hasStructuralLog(agent) {
|
|
1368
|
+
return typeof agent.session.readEvents === "function";
|
|
1369
|
+
}
|
|
1370
|
+
warnMissingHistory() {
|
|
1371
|
+
if (this.warnedMissingHistory) return;
|
|
1372
|
+
this.warnedMissingHistory = true;
|
|
1373
|
+
this.ports.warn?.("evolution-skill-discovery: no sessionProjections history and no structural readEvents for this session — catalog restore/dedupe is unavailable; failing open (mount the projection registry to keep replacement dedupe)");
|
|
1374
|
+
}
|
|
1304
1375
|
mark(agent, action, digest, content = "") {
|
|
1305
1376
|
const report = {
|
|
1306
1377
|
action,
|
|
@@ -1317,10 +1388,17 @@ var ProjectionRuntime = class {
|
|
|
1317
1388
|
}
|
|
1318
1389
|
async restore(agent, viewKey, decision, currentCapture) {
|
|
1319
1390
|
const record = await this.ports.journal.latest(String(agent.id), viewKey, this.ports.codecVersion);
|
|
1320
|
-
const
|
|
1321
|
-
const
|
|
1322
|
-
const
|
|
1391
|
+
const history = this.ports.history?.(agent);
|
|
1392
|
+
const sessionBase = sessionCatalogCapture(agent, history);
|
|
1393
|
+
const fullSession = sessionBase === void 0 ? void 0 : sessionBase.text === fullCatalogText(sessionBase) ? sessionBase : fullCaptureOf(sessionBase);
|
|
1394
|
+
const projectedCurrent = currentCapture !== void 0 && currentCapture.text !== fullCatalogText(currentCapture);
|
|
1395
|
+
const fullCurrent = currentCapture === void 0 ? void 0 : fullCaptureOf(currentCapture);
|
|
1396
|
+
const durable = projectedCurrent ? record?.base ?? fullSession ?? fullCurrent : fullCurrent ?? fullSession ?? record?.base;
|
|
1323
1397
|
if (durable === void 0) {
|
|
1398
|
+
if (history === void 0 && !this.hasStructuralLog(agent)) {
|
|
1399
|
+
this.warnMissingHistory();
|
|
1400
|
+
return decision;
|
|
1401
|
+
}
|
|
1324
1402
|
this.mark(agent, "blocked", "");
|
|
1325
1403
|
return decision;
|
|
1326
1404
|
}
|
|
@@ -1328,7 +1406,7 @@ var ProjectionRuntime = class {
|
|
|
1328
1406
|
const previous = this.report(agent);
|
|
1329
1407
|
if (previous.action === "restored" && previous.content === fullText) return decision;
|
|
1330
1408
|
const restored = currentCapture === void 0 ? applyRestoreUpdate(decision, durable) : restoreCapturedProjection(decision, durable);
|
|
1331
|
-
|
|
1409
|
+
await this.ports.journal.removeView(String(agent.id), viewKey, this.ports.codecVersion);
|
|
1332
1410
|
this.mark(agent, "restored", record?.projectionDigest ?? "", fullText);
|
|
1333
1411
|
return restored;
|
|
1334
1412
|
}
|
|
@@ -1398,11 +1476,22 @@ var ProjectionRuntime = class {
|
|
|
1398
1476
|
if (this.ports.isFrozen !== void 0 && await this.ports.isFrozen(agent)) return decision;
|
|
1399
1477
|
const { view, snapshot, policy, policyDigest, state } = await this.readState(agent, signal);
|
|
1400
1478
|
const currentCapture = captureDecisionCatalog(decision);
|
|
1479
|
+
const history = this.ports.history?.(agent);
|
|
1480
|
+
if (history === void 0 && !this.hasStructuralLog(agent)) this.warnMissingHistory();
|
|
1481
|
+
const seen = new Set(sessionProjectionDigests(agent, history));
|
|
1482
|
+
if (decision.kind === "enter") for (const message of decision.messages) {
|
|
1483
|
+
if (message.source.kind !== "evolution-skill-projection") continue;
|
|
1484
|
+
const digest = message.source.digest;
|
|
1485
|
+
if (typeof digest === "string") seen.add(digest);
|
|
1486
|
+
}
|
|
1487
|
+
const previous = this.report(agent);
|
|
1488
|
+
if (previous.action === "applied" && previous.digest !== "") seen.add(previous.digest);
|
|
1401
1489
|
let capture = currentCapture;
|
|
1402
1490
|
if (capture === void 0) capture = (await this.ports.journal.latest(String(agent.id), view.viewKey, this.ports.codecVersion))?.base;
|
|
1403
|
-
if (capture === void 0) capture = sessionCatalogCapture(agent);
|
|
1491
|
+
if (capture === void 0) capture = sessionCatalogCapture(agent, history);
|
|
1404
1492
|
if (!canProject(state) || snapshot === void 0) return this.restore(agent, view.viewKey, decision, currentCapture);
|
|
1405
1493
|
if (capture === void 0) {
|
|
1494
|
+
if (history === void 0 && !this.hasStructuralLog(agent)) return decision;
|
|
1406
1495
|
this.mark(agent, "blocked", "");
|
|
1407
1496
|
return decision;
|
|
1408
1497
|
}
|
|
@@ -1431,8 +1520,8 @@ var ProjectionRuntime = class {
|
|
|
1431
1520
|
});
|
|
1432
1521
|
if (!projection.complete) return this.restore(agent, view.viewKey, decision, currentCapture);
|
|
1433
1522
|
const expectedText = renderCatalog(capture, projection.catalogEntries).content.map((block) => block.type === "text" ? block.text : "").join("");
|
|
1434
|
-
if (currentCapture !== void 0 && currentCapture.text === expectedText) return decision;
|
|
1435
|
-
|
|
1523
|
+
if (currentCapture !== void 0 && currentCapture.text === expectedText) return seen.has(projection.digest) ? decision : applyProjectedDecision(decision, currentCapture, projection, seen);
|
|
1524
|
+
if (currentCapture === void 0 && seen.has(projection.digest)) return decision;
|
|
1436
1525
|
if (currentCapture === void 0 && previous.action === "applied" && previous.digest === projection.digest && previous.content === expectedText) return decision;
|
|
1437
1526
|
await this.ports.journal.put({
|
|
1438
1527
|
sessionId: String(agent.id),
|
|
@@ -1445,7 +1534,7 @@ var ProjectionRuntime = class {
|
|
|
1445
1534
|
createdAt: Date.now()
|
|
1446
1535
|
});
|
|
1447
1536
|
this.mark(agent, "applied", projection.digest, expectedText);
|
|
1448
|
-
return currentCapture === void 0 ? applyReplacementDecision(decision, capture.source.entries, projection) : applyProjectedDecision(decision, capture, projection);
|
|
1537
|
+
return currentCapture === void 0 ? applyReplacementDecision(decision, capture.source.entries, projection, seen) : applyProjectedDecision(decision, capture, projection, seen);
|
|
1449
1538
|
}
|
|
1450
1539
|
};
|
|
1451
1540
|
//#endregion
|
|
@@ -1577,6 +1666,14 @@ var SkillSearchIndex = class SkillSearchIndex {
|
|
|
1577
1666
|
/** Per-view cache keyed by searchIndexKey; never shares one engine across views. */
|
|
1578
1667
|
var SkillSearchCache = class {
|
|
1579
1668
|
entries = /* @__PURE__ */ new Map();
|
|
1669
|
+
maxEntries;
|
|
1670
|
+
constructor(options = {}) {
|
|
1671
|
+
this.maxEntries = Math.max(1, Math.floor(options.maxEntries ?? 32));
|
|
1672
|
+
}
|
|
1673
|
+
/** @returns the current number of cached view indices (bounded by maxEntries). */
|
|
1674
|
+
get size() {
|
|
1675
|
+
return this.entries.size;
|
|
1676
|
+
}
|
|
1580
1677
|
/**
|
|
1581
1678
|
* Get or build one view index. A failed rebuild retains the previous engine
|
|
1582
1679
|
* and marks it stale; no previous engine means undefined.
|
|
@@ -1594,10 +1691,14 @@ var SkillSearchCache = class {
|
|
|
1594
1691
|
discoveryMode: key.discoveryMode
|
|
1595
1692
|
});
|
|
1596
1693
|
const existing = this.entries.get(cacheKey);
|
|
1597
|
-
if (existing !== void 0)
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1694
|
+
if (existing !== void 0) {
|
|
1695
|
+
this.entries.delete(cacheKey);
|
|
1696
|
+
this.entries.set(cacheKey, existing);
|
|
1697
|
+
return {
|
|
1698
|
+
index: existing.index,
|
|
1699
|
+
stale: false
|
|
1700
|
+
};
|
|
1701
|
+
}
|
|
1601
1702
|
try {
|
|
1602
1703
|
const index = SkillSearchIndex.fromRows(key.viewKey, rows);
|
|
1603
1704
|
this.entries.set(cacheKey, {
|
|
@@ -1605,6 +1706,11 @@ var SkillSearchCache = class {
|
|
|
1605
1706
|
viewKey: key.viewKey,
|
|
1606
1707
|
index
|
|
1607
1708
|
});
|
|
1709
|
+
while (this.entries.size > this.maxEntries) {
|
|
1710
|
+
const oldest = this.entries.keys().next().value;
|
|
1711
|
+
if (oldest === void 0) break;
|
|
1712
|
+
this.entries.delete(oldest);
|
|
1713
|
+
}
|
|
1608
1714
|
return {
|
|
1609
1715
|
index,
|
|
1610
1716
|
stale: false
|
|
@@ -1908,12 +2014,49 @@ function buildGenerationRequest(input) {
|
|
|
1908
2014
|
var ViewSnapshotStore = class {
|
|
1909
2015
|
io;
|
|
1910
2016
|
path;
|
|
2017
|
+
serial = Promise.resolve();
|
|
1911
2018
|
constructor(io, path) {
|
|
1912
2019
|
this.io = io;
|
|
1913
2020
|
this.path = path;
|
|
1914
2021
|
}
|
|
1915
2022
|
async read() {
|
|
1916
|
-
|
|
2023
|
+
return this.decode(await this.io.readText(this.path));
|
|
2024
|
+
}
|
|
2025
|
+
/** Persist one session snapshot; first writer wins unless overwrite is explicit. */
|
|
2026
|
+
async put(snapshot, options = {}) {
|
|
2027
|
+
const apply = (raw) => {
|
|
2028
|
+
const current = this.decode(raw);
|
|
2029
|
+
if (current.sessions[snapshot.sessionId] !== void 0 && options.overwrite !== true) return raw;
|
|
2030
|
+
const sessions = {
|
|
2031
|
+
...current.sessions,
|
|
2032
|
+
[snapshot.sessionId]: {
|
|
2033
|
+
viewKey: snapshot.viewKey,
|
|
2034
|
+
cwd: snapshot.cwd,
|
|
2035
|
+
viewCatalogDigest: snapshot.viewCatalogDigest,
|
|
2036
|
+
mapVersion: snapshot.mapVersion,
|
|
2037
|
+
configDigest: snapshot.configDigest,
|
|
2038
|
+
featureEpoch: snapshot.featureEpoch,
|
|
2039
|
+
createdAt: snapshot.createdAt
|
|
2040
|
+
}
|
|
2041
|
+
};
|
|
2042
|
+
return JSON.stringify({
|
|
2043
|
+
schemaVersion: 1,
|
|
2044
|
+
sessions
|
|
2045
|
+
});
|
|
2046
|
+
};
|
|
2047
|
+
if (this.io.transact !== void 0) {
|
|
2048
|
+
await this.io.transact(this.path, (current) => apply(current));
|
|
2049
|
+
return;
|
|
2050
|
+
}
|
|
2051
|
+
const task = async () => {
|
|
2052
|
+
const current = await this.io.readText(this.path);
|
|
2053
|
+
const next = apply(current);
|
|
2054
|
+
if (next !== null && next !== current) await this.io.writeText(this.path, next);
|
|
2055
|
+
};
|
|
2056
|
+
this.serial = this.serial.then(task, task);
|
|
2057
|
+
await this.serial;
|
|
2058
|
+
}
|
|
2059
|
+
decode(content) {
|
|
1917
2060
|
if (content === null) return {
|
|
1918
2061
|
schemaVersion: 1,
|
|
1919
2062
|
sessions: {}
|
|
@@ -1934,27 +2077,6 @@ var ViewSnapshotStore = class {
|
|
|
1934
2077
|
};
|
|
1935
2078
|
}
|
|
1936
2079
|
}
|
|
1937
|
-
/** Persist one session snapshot; first writer wins unless overwrite is explicit. */
|
|
1938
|
-
async put(snapshot, options = {}) {
|
|
1939
|
-
const current = await this.read();
|
|
1940
|
-
if (current.sessions[snapshot.sessionId] !== void 0 && options.overwrite !== true) return;
|
|
1941
|
-
const sessions = {
|
|
1942
|
-
...current.sessions,
|
|
1943
|
-
[snapshot.sessionId]: {
|
|
1944
|
-
viewKey: snapshot.viewKey,
|
|
1945
|
-
cwd: snapshot.cwd,
|
|
1946
|
-
viewCatalogDigest: snapshot.viewCatalogDigest,
|
|
1947
|
-
mapVersion: snapshot.mapVersion,
|
|
1948
|
-
configDigest: snapshot.configDigest,
|
|
1949
|
-
featureEpoch: snapshot.featureEpoch,
|
|
1950
|
-
createdAt: snapshot.createdAt
|
|
1951
|
-
}
|
|
1952
|
-
};
|
|
1953
|
-
await this.io.writeText(this.path, JSON.stringify({
|
|
1954
|
-
schemaVersion: 1,
|
|
1955
|
-
sessions
|
|
1956
|
-
}));
|
|
1957
|
-
}
|
|
1958
2080
|
/** @returns frozen snapshot for one session, if recorded. */
|
|
1959
2081
|
async get(sessionId) {
|
|
1960
2082
|
const entry = (await this.read()).sessions[sessionId];
|
|
@@ -1972,506 +2094,6 @@ var ViewSnapshotStore = class {
|
|
|
1972
2094
|
}
|
|
1973
2095
|
};
|
|
1974
2096
|
//#endregion
|
|
1975
|
-
//#region lib/types/assignment.js
|
|
1976
|
-
function digest(value) {
|
|
1977
|
-
return createHash("sha256").update(JSON.stringify(value)).digest("hex");
|
|
1978
|
-
}
|
|
1979
|
-
/** @returns idempotency key for one proposal observation. */
|
|
1980
|
-
function assignmentKey(input) {
|
|
1981
|
-
return digest([
|
|
1982
|
-
input.skillKey,
|
|
1983
|
-
input.threeFieldHash,
|
|
1984
|
-
input.proposalDigest,
|
|
1985
|
-
input.mapVersion
|
|
1986
|
-
]);
|
|
1987
|
-
}
|
|
1988
|
-
/** @returns stable proposal digest; undefined proposal is digest of null. */
|
|
1989
|
-
function proposalDigest(proposal) {
|
|
1990
|
-
return digest(proposal ?? null);
|
|
1991
|
-
}
|
|
1992
|
-
/** @returns a closed-set pending reason or undefined when the proposal can enqueue. */
|
|
1993
|
-
function validateProposal(proposal) {
|
|
1994
|
-
if (proposal.mode !== "existing" && proposal.mode !== "new") return "schema-invalid";
|
|
1995
|
-
if (typeof proposal.confidence !== "number" || proposal.confidence < 0 || proposal.confidence > 1 || typeof proposal.reason !== "string" || typeof proposal.mapVersion !== "string") return "schema-invalid";
|
|
1996
|
-
if (proposal.mode === "existing" && (typeof proposal.id !== "string" || proposal.id.length === 0)) return "invalid-id";
|
|
1997
|
-
if (proposal.mode === "new") {
|
|
1998
|
-
const proposed = proposal.proposed;
|
|
1999
|
-
if (proposed === void 0 || proposed.title.trim().length === 0 || proposed.purpose.trim().length === 0 || proposed.inScope.length === 0 || proposed.outOfScope.length === 0 || proposed.triggers.length === 0) return "new-domain-conflict";
|
|
2000
|
-
}
|
|
2001
|
-
}
|
|
2002
|
-
/** @returns true only when whenToUse is covered by in/out/triggers; names alone do not count. */
|
|
2003
|
-
function coveredByDomain(whenToUse, domain) {
|
|
2004
|
-
const text = whenToUse.toLowerCase();
|
|
2005
|
-
if (text.length === 0) return false;
|
|
2006
|
-
if (domain.outOfScope.some((value) => value.length > 0 && text.includes(value.toLowerCase()))) return false;
|
|
2007
|
-
return domain.triggers.some((value) => value.length > 0 && text.includes(value.toLowerCase())) || domain.inScope.some((value) => value.length > 0 && text.includes(value.toLowerCase()));
|
|
2008
|
-
}
|
|
2009
|
-
/** One assignment sidecar service; the only map write path for create. */
|
|
2010
|
-
var DomainAssignmentService = class {
|
|
2011
|
-
queue;
|
|
2012
|
-
store;
|
|
2013
|
-
options;
|
|
2014
|
-
constructor(queue, store, options) {
|
|
2015
|
-
this.queue = queue;
|
|
2016
|
-
this.store = store;
|
|
2017
|
-
this.options = options;
|
|
2018
|
-
}
|
|
2019
|
-
/** Stage a create proposal without queueing it. */
|
|
2020
|
-
stage(submission) {
|
|
2021
|
-
return {
|
|
2022
|
-
id: digest([
|
|
2023
|
-
"staged",
|
|
2024
|
-
submission.skillKey,
|
|
2025
|
-
submission.threeFieldHash
|
|
2026
|
-
]),
|
|
2027
|
-
submission
|
|
2028
|
-
};
|
|
2029
|
-
}
|
|
2030
|
-
/** Approve a staged create; only this call enqueues. */
|
|
2031
|
-
async approve(staged) {
|
|
2032
|
-
return this.submit(staged.submission);
|
|
2033
|
-
}
|
|
2034
|
-
/**
|
|
2035
|
-
* Submit a create proposal after SkillLibrary.create succeeded.
|
|
2036
|
-
* @param submission - skill identity, three-field hash, permission, proposal.
|
|
2037
|
-
* @returns queue/pending/unclassified; never rolls back create.
|
|
2038
|
-
*/
|
|
2039
|
-
async submit(submission) {
|
|
2040
|
-
if (!this.options.createClassificationEnabled) return {
|
|
2041
|
-
kind: "unclassified",
|
|
2042
|
-
reason: "feature-off"
|
|
2043
|
-
};
|
|
2044
|
-
if (!submission.modelInvocable) return {
|
|
2045
|
-
kind: "unclassified",
|
|
2046
|
-
reason: "not-model-invocable"
|
|
2047
|
-
};
|
|
2048
|
-
if (submission.proposal === void 0) {
|
|
2049
|
-
if (this.options.createCardsAvailable === true) {
|
|
2050
|
-
const reason = "schema-invalid";
|
|
2051
|
-
return {
|
|
2052
|
-
kind: "pending",
|
|
2053
|
-
entry: {
|
|
2054
|
-
key: digest([
|
|
2055
|
-
"missing-proposal",
|
|
2056
|
-
submission.skillKey,
|
|
2057
|
-
submission.threeFieldHash
|
|
2058
|
-
]),
|
|
2059
|
-
submission,
|
|
2060
|
-
proposalDigest: proposalDigest(void 0),
|
|
2061
|
-
createdAt: this.now(),
|
|
2062
|
-
status: "pending",
|
|
2063
|
-
pendingReason: reason
|
|
2064
|
-
},
|
|
2065
|
-
reason
|
|
2066
|
-
};
|
|
2067
|
-
}
|
|
2068
|
-
return {
|
|
2069
|
-
kind: "unclassified",
|
|
2070
|
-
reason: "no-proposal"
|
|
2071
|
-
};
|
|
2072
|
-
}
|
|
2073
|
-
const invalid = validateProposal(submission.proposal);
|
|
2074
|
-
const pdigest = proposalDigest(submission.proposal);
|
|
2075
|
-
const key = assignmentKey({
|
|
2076
|
-
skillKey: submission.skillKey,
|
|
2077
|
-
threeFieldHash: submission.threeFieldHash,
|
|
2078
|
-
proposalDigest: pdigest,
|
|
2079
|
-
mapVersion: submission.proposal.mapVersion
|
|
2080
|
-
});
|
|
2081
|
-
let queued;
|
|
2082
|
-
try {
|
|
2083
|
-
queued = await this.queue.enqueue({
|
|
2084
|
-
key,
|
|
2085
|
-
submission,
|
|
2086
|
-
proposalDigest: pdigest,
|
|
2087
|
-
createdAt: submission.now ?? this.now()
|
|
2088
|
-
});
|
|
2089
|
-
} catch {
|
|
2090
|
-
return {
|
|
2091
|
-
kind: "pending",
|
|
2092
|
-
entry: {
|
|
2093
|
-
key,
|
|
2094
|
-
submission,
|
|
2095
|
-
proposalDigest: pdigest,
|
|
2096
|
-
createdAt: submission.now ?? this.now(),
|
|
2097
|
-
status: "pending",
|
|
2098
|
-
pendingReason: "queue-unavailable"
|
|
2099
|
-
},
|
|
2100
|
-
reason: "queue-unavailable"
|
|
2101
|
-
};
|
|
2102
|
-
}
|
|
2103
|
-
if (invalid !== void 0) {
|
|
2104
|
-
await this.queue.update(key, "pending", invalid);
|
|
2105
|
-
return {
|
|
2106
|
-
kind: "pending",
|
|
2107
|
-
entry: {
|
|
2108
|
-
...queued.entry,
|
|
2109
|
-
status: "pending",
|
|
2110
|
-
pendingReason: invalid
|
|
2111
|
-
},
|
|
2112
|
-
reason: invalid
|
|
2113
|
-
};
|
|
2114
|
-
}
|
|
2115
|
-
return {
|
|
2116
|
-
kind: "queued",
|
|
2117
|
-
entry: queued.entry,
|
|
2118
|
-
duplicate: queued.duplicate
|
|
2119
|
-
};
|
|
2120
|
-
}
|
|
2121
|
-
/** Process one queued entry with the current map; CAS failure becomes pending. */
|
|
2122
|
-
async process(entry) {
|
|
2123
|
-
const proposal = entry.submission.proposal;
|
|
2124
|
-
if (proposal === void 0) return {
|
|
2125
|
-
kind: "pending",
|
|
2126
|
-
entry,
|
|
2127
|
-
reason: "schema-invalid"
|
|
2128
|
-
};
|
|
2129
|
-
const current = await this.store.read();
|
|
2130
|
-
if (proposal.mapVersion !== current.mapVersion) {
|
|
2131
|
-
await this.queue.update(entry.key, "pending", "map-version-stale");
|
|
2132
|
-
return {
|
|
2133
|
-
kind: "pending",
|
|
2134
|
-
entry: {
|
|
2135
|
-
...entry,
|
|
2136
|
-
status: "pending",
|
|
2137
|
-
pendingReason: "map-version-stale"
|
|
2138
|
-
},
|
|
2139
|
-
reason: "map-version-stale"
|
|
2140
|
-
};
|
|
2141
|
-
}
|
|
2142
|
-
try {
|
|
2143
|
-
const next = buildNext(current, entry, proposal, this.options.provisionalThreshold ?? 3);
|
|
2144
|
-
const committed = await this.store.commit(current.mapVersion, {
|
|
2145
|
-
mapVersion: next.mapVersion,
|
|
2146
|
-
domains: next.domains,
|
|
2147
|
-
edges: next.edges
|
|
2148
|
-
});
|
|
2149
|
-
const status = next.provisional ? "provisional" : "applied";
|
|
2150
|
-
await this.queue.update(entry.key, status);
|
|
2151
|
-
return {
|
|
2152
|
-
kind: status,
|
|
2153
|
-
entry: {
|
|
2154
|
-
...entry,
|
|
2155
|
-
status
|
|
2156
|
-
},
|
|
2157
|
-
mapVersion: committed.mapVersion
|
|
2158
|
-
};
|
|
2159
|
-
} catch (error) {
|
|
2160
|
-
if (error instanceof DomainMapVersionConflict) {
|
|
2161
|
-
await this.queue.update(entry.key, "pending", "map-version-stale");
|
|
2162
|
-
return {
|
|
2163
|
-
kind: "pending",
|
|
2164
|
-
entry: {
|
|
2165
|
-
...entry,
|
|
2166
|
-
status: "pending",
|
|
2167
|
-
pendingReason: "map-version-stale"
|
|
2168
|
-
},
|
|
2169
|
-
reason: "map-version-stale"
|
|
2170
|
-
};
|
|
2171
|
-
}
|
|
2172
|
-
const reason = error instanceof Error && (error.message === "unknown-domain" || error.message === "new-domain-conflict" || error.message === "schema-invalid") ? error.message : "queue-unavailable";
|
|
2173
|
-
await this.queue.update(entry.key, "pending", reason);
|
|
2174
|
-
return {
|
|
2175
|
-
kind: "pending",
|
|
2176
|
-
entry: {
|
|
2177
|
-
...entry,
|
|
2178
|
-
status: "pending",
|
|
2179
|
-
pendingReason: reason
|
|
2180
|
-
},
|
|
2181
|
-
reason
|
|
2182
|
-
};
|
|
2183
|
-
}
|
|
2184
|
-
}
|
|
2185
|
-
/** Manually confirm one provisional domain; no semantic reclassification. */
|
|
2186
|
-
async confirm(domainId) {
|
|
2187
|
-
const current = await this.store.read();
|
|
2188
|
-
const domain = current.domains.find((node) => node.id === domainId);
|
|
2189
|
-
if (domain === void 0 || domain.status !== "provisional") return false;
|
|
2190
|
-
const domains = current.domains.map((node) => node.id === domainId ? {
|
|
2191
|
-
...node,
|
|
2192
|
-
status: "active",
|
|
2193
|
-
updatedAt: this.now()
|
|
2194
|
-
} : node);
|
|
2195
|
-
await this.store.commit(current.mapVersion, {
|
|
2196
|
-
mapVersion: nextMapVersion$1(current.mapVersion),
|
|
2197
|
-
domains,
|
|
2198
|
-
edges: current.edges
|
|
2199
|
-
});
|
|
2200
|
-
return true;
|
|
2201
|
-
}
|
|
2202
|
-
now() {
|
|
2203
|
-
return this.options.now?.() ?? Date.now();
|
|
2204
|
-
}
|
|
2205
|
-
};
|
|
2206
|
-
function nextMapVersion$1(version) {
|
|
2207
|
-
const numeric = Number(version);
|
|
2208
|
-
return Number.isSafeInteger(numeric) && numeric >= 0 ? String(numeric + 1) : version + "+1";
|
|
2209
|
-
}
|
|
2210
|
-
function buildNext(current, entry, proposal, threshold) {
|
|
2211
|
-
const edge = {
|
|
2212
|
-
skillKey: entry.submission.skillKey,
|
|
2213
|
-
domainId: "",
|
|
2214
|
-
role: "primary",
|
|
2215
|
-
confidence: proposal.confidence,
|
|
2216
|
-
source: "create",
|
|
2217
|
-
skillHash: entry.submission.threeFieldHash,
|
|
2218
|
-
mapVersion: nextMapVersion$1(current.mapVersion),
|
|
2219
|
-
status: "active",
|
|
2220
|
-
assignedAt: entry.createdAt
|
|
2221
|
-
};
|
|
2222
|
-
const secondaries = (proposal.secondaryDomainIds ?? []).map((id) => {
|
|
2223
|
-
const secondary = current.domains.find((node) => node.id === id);
|
|
2224
|
-
if (secondary === void 0) throw new Error("unknown-domain");
|
|
2225
|
-
return {
|
|
2226
|
-
...edge,
|
|
2227
|
-
domainId: secondary.id,
|
|
2228
|
-
role: "secondary"
|
|
2229
|
-
};
|
|
2230
|
-
});
|
|
2231
|
-
if (proposal.mode === "existing") {
|
|
2232
|
-
const domain = current.domains.find((node) => node.id === proposal.id);
|
|
2233
|
-
if (domain === void 0) throw new Error("unknown-domain");
|
|
2234
|
-
if (!coveredByDomain(entry.submission.whenToUse ?? "", domain)) throw new Error("new-domain-conflict");
|
|
2235
|
-
const members = new Set(current.edges.filter((item) => item.domainId === domain.id).map((item) => item.skillKey));
|
|
2236
|
-
members.add(entry.submission.skillKey);
|
|
2237
|
-
const promote = domain.status === "provisional" && members.size >= threshold;
|
|
2238
|
-
const domains = promote ? current.domains.map((node) => node.id === domain.id ? {
|
|
2239
|
-
...node,
|
|
2240
|
-
status: "active",
|
|
2241
|
-
updatedAt: entry.createdAt
|
|
2242
|
-
} : node) : current.domains;
|
|
2243
|
-
return {
|
|
2244
|
-
mapVersion: nextMapVersion$1(current.mapVersion),
|
|
2245
|
-
domains,
|
|
2246
|
-
edges: [
|
|
2247
|
-
...current.edges,
|
|
2248
|
-
{
|
|
2249
|
-
...edge,
|
|
2250
|
-
domainId: domain.id
|
|
2251
|
-
},
|
|
2252
|
-
...secondaries.filter((secondary) => secondary.domainId !== domain.id)
|
|
2253
|
-
],
|
|
2254
|
-
provisional: domain.status === "provisional" && !promote
|
|
2255
|
-
};
|
|
2256
|
-
}
|
|
2257
|
-
const proposed = proposal.proposed;
|
|
2258
|
-
if (proposed === void 0) throw new Error("schema-invalid");
|
|
2259
|
-
const normalizedTitle = proposed.title.trim().toLowerCase();
|
|
2260
|
-
const existing = current.domains.find((node) => node.name.trim().toLowerCase() === normalizedTitle);
|
|
2261
|
-
if (existing !== void 0 && existing.status !== "provisional") throw new Error("new-domain-conflict");
|
|
2262
|
-
const id = existing?.id ?? mintDomainId(proposed.title);
|
|
2263
|
-
if (current.domains.some((node) => node.id !== id && node.triggers.some((trigger) => proposed.triggers.includes(trigger)))) throw new Error("new-domain-conflict");
|
|
2264
|
-
const domain = existing ?? {
|
|
2265
|
-
id,
|
|
2266
|
-
name: proposed.title,
|
|
2267
|
-
purpose: proposed.purpose,
|
|
2268
|
-
description: proposed.purpose,
|
|
2269
|
-
inScope: proposed.inScope,
|
|
2270
|
-
outOfScope: proposed.outOfScope,
|
|
2271
|
-
triggers: proposed.triggers,
|
|
2272
|
-
parentIds: [],
|
|
2273
|
-
status: "provisional",
|
|
2274
|
-
version: 1,
|
|
2275
|
-
createdAt: entry.createdAt,
|
|
2276
|
-
updatedAt: entry.createdAt,
|
|
2277
|
-
descriptionHash: "pending",
|
|
2278
|
-
descriptionVersion: 1,
|
|
2279
|
-
descriptionStatus: "fresh",
|
|
2280
|
-
provenance: { source: "create" },
|
|
2281
|
-
quality: {}
|
|
2282
|
-
};
|
|
2283
|
-
const members = new Set(current.edges.filter((item) => item.domainId === id).map((item) => item.skillKey));
|
|
2284
|
-
members.add(entry.submission.skillKey);
|
|
2285
|
-
const promote = members.size >= threshold;
|
|
2286
|
-
const finalDomain = promote ? {
|
|
2287
|
-
...domain,
|
|
2288
|
-
status: "active",
|
|
2289
|
-
updatedAt: entry.createdAt
|
|
2290
|
-
} : domain;
|
|
2291
|
-
return {
|
|
2292
|
-
mapVersion: nextMapVersion$1(current.mapVersion),
|
|
2293
|
-
domains: existing === void 0 ? [...current.domains, finalDomain] : current.domains.map((node) => node.id === id ? finalDomain : node),
|
|
2294
|
-
edges: [
|
|
2295
|
-
...current.edges,
|
|
2296
|
-
{
|
|
2297
|
-
...edge,
|
|
2298
|
-
domainId: id
|
|
2299
|
-
},
|
|
2300
|
-
...secondaries.filter((secondary) => secondary.domainId !== id)
|
|
2301
|
-
],
|
|
2302
|
-
provisional: !promote
|
|
2303
|
-
};
|
|
2304
|
-
}
|
|
2305
|
-
//#endregion
|
|
2306
|
-
//#region lib/types/assignment-queue.js
|
|
2307
|
-
const COLLECTION = "assignment-queue";
|
|
2308
|
-
/** Durable assignment queue; process-local cache over one JSON file. */
|
|
2309
|
-
var JsonAssignmentQueue = class {
|
|
2310
|
-
entries = /* @__PURE__ */ new Map();
|
|
2311
|
-
loadPromise;
|
|
2312
|
-
io;
|
|
2313
|
-
path;
|
|
2314
|
-
store;
|
|
2315
|
-
constructor(io, path, store) {
|
|
2316
|
-
this.io = io;
|
|
2317
|
-
this.path = path;
|
|
2318
|
-
this.store = store;
|
|
2319
|
-
}
|
|
2320
|
-
load() {
|
|
2321
|
-
this.loadPromise ??= (async () => {
|
|
2322
|
-
if (this.store !== void 0) {
|
|
2323
|
-
for (const entry of await this.store.list(COLLECTION)) this.entries.set(entry.key, entry);
|
|
2324
|
-
if (this.entries.size > 0) return;
|
|
2325
|
-
}
|
|
2326
|
-
const content = await this.io.readText(this.path);
|
|
2327
|
-
if (content === null) return;
|
|
2328
|
-
try {
|
|
2329
|
-
const parsed = JSON.parse(content);
|
|
2330
|
-
if (parsed.schemaVersion !== 1 || !Array.isArray(parsed.entries)) return;
|
|
2331
|
-
for (const entry of parsed.entries) {
|
|
2332
|
-
this.entries.set(entry.key, entry);
|
|
2333
|
-
if (this.store !== void 0) await this.store.transact(COLLECTION, entry.key, async () => entry);
|
|
2334
|
-
}
|
|
2335
|
-
} catch {}
|
|
2336
|
-
})();
|
|
2337
|
-
return this.loadPromise;
|
|
2338
|
-
}
|
|
2339
|
-
async persist() {
|
|
2340
|
-
if (this.store === void 0) await this.io.writeText(this.path, JSON.stringify({
|
|
2341
|
-
schemaVersion: 1,
|
|
2342
|
-
entries: [...this.entries.values()]
|
|
2343
|
-
}));
|
|
2344
|
-
}
|
|
2345
|
-
async enqueue(input) {
|
|
2346
|
-
await this.load();
|
|
2347
|
-
const existing = this.entries.get(input.key);
|
|
2348
|
-
if (existing !== void 0) return {
|
|
2349
|
-
entry: existing,
|
|
2350
|
-
duplicate: true
|
|
2351
|
-
};
|
|
2352
|
-
const entry = {
|
|
2353
|
-
...input,
|
|
2354
|
-
status: "queued"
|
|
2355
|
-
};
|
|
2356
|
-
if (this.store !== void 0) {
|
|
2357
|
-
let duplicate = false;
|
|
2358
|
-
let stored = entry;
|
|
2359
|
-
await this.store.transact(COLLECTION, input.key, async (current) => {
|
|
2360
|
-
if (current !== void 0) {
|
|
2361
|
-
duplicate = true;
|
|
2362
|
-
stored = current;
|
|
2363
|
-
return current;
|
|
2364
|
-
}
|
|
2365
|
-
return entry;
|
|
2366
|
-
});
|
|
2367
|
-
this.entries.set(input.key, stored);
|
|
2368
|
-
return {
|
|
2369
|
-
entry: stored,
|
|
2370
|
-
duplicate
|
|
2371
|
-
};
|
|
2372
|
-
}
|
|
2373
|
-
this.entries.set(input.key, entry);
|
|
2374
|
-
await this.persist();
|
|
2375
|
-
return {
|
|
2376
|
-
entry,
|
|
2377
|
-
duplicate: false
|
|
2378
|
-
};
|
|
2379
|
-
}
|
|
2380
|
-
async update(key, status, pendingReason) {
|
|
2381
|
-
await this.load();
|
|
2382
|
-
const entry = this.entries.get(key);
|
|
2383
|
-
if (entry === void 0) return;
|
|
2384
|
-
const nextEntry = {
|
|
2385
|
-
...entry,
|
|
2386
|
-
status,
|
|
2387
|
-
pendingReason
|
|
2388
|
-
};
|
|
2389
|
-
if (this.store !== void 0) await this.store.transact(COLLECTION, key, async (current) => current === void 0 ? nextEntry : {
|
|
2390
|
-
...current,
|
|
2391
|
-
status,
|
|
2392
|
-
pendingReason
|
|
2393
|
-
});
|
|
2394
|
-
this.entries.set(key, nextEntry);
|
|
2395
|
-
await this.persist();
|
|
2396
|
-
}
|
|
2397
|
-
list() {
|
|
2398
|
-
return [...this.entries.values()];
|
|
2399
|
-
}
|
|
2400
|
-
};
|
|
2401
|
-
//#endregion
|
|
2402
|
-
//#region lib/types/worker.js
|
|
2403
|
-
/** Drivable worker; the composition may call drain on a schedule. */
|
|
2404
|
-
var AssignmentWorker = class {
|
|
2405
|
-
service;
|
|
2406
|
-
queue;
|
|
2407
|
-
constructor(service, queue) {
|
|
2408
|
-
this.service = service;
|
|
2409
|
-
this.queue = queue;
|
|
2410
|
-
}
|
|
2411
|
-
/** Process queued entries once; returns processed count. */
|
|
2412
|
-
async drain(limit = 10) {
|
|
2413
|
-
const entries = this.queue.list().filter((entry) => entry.status === "queued").slice(0, Math.max(0, limit));
|
|
2414
|
-
for (const entry of entries) await this.service.process(entry);
|
|
2415
|
-
return entries.length;
|
|
2416
|
-
}
|
|
2417
|
-
};
|
|
2418
|
-
//#endregion
|
|
2419
|
-
//#region lib/types/assignment-sidecar.js
|
|
2420
|
-
function submitCreate(service, args, options) {
|
|
2421
|
-
if (typeof args.name !== "string" || args.name.length === 0) return void 0;
|
|
2422
|
-
const parsed = parseFrontmatter(typeof args.content === "string" ? args.content : "");
|
|
2423
|
-
const description = parsed?.frontmatter.description ?? "";
|
|
2424
|
-
const rawWhenToUse = parsed?.frontmatter.whenToUse;
|
|
2425
|
-
const whenToUse = typeof rawWhenToUse === "string" ? rawWhenToUse : void 0;
|
|
2426
|
-
const domain = args.domain;
|
|
2427
|
-
const proposal = typeof domain === "object" && domain !== null ? domain : void 0;
|
|
2428
|
-
return service.submit({
|
|
2429
|
-
skillKey: skillKeyOf({
|
|
2430
|
-
provider: options.provider ?? "dsh-evolution",
|
|
2431
|
-
source: options.source ?? "user-dsh",
|
|
2432
|
-
name: args.name
|
|
2433
|
-
}),
|
|
2434
|
-
threeFieldHash: threeFieldHashOf({
|
|
2435
|
-
name: args.name,
|
|
2436
|
-
description,
|
|
2437
|
-
...whenToUse === void 0 ? {} : { whenToUse }
|
|
2438
|
-
}),
|
|
2439
|
-
modelInvocable: parsed?.frontmatter["disable-model-invocation"] !== true,
|
|
2440
|
-
...whenToUse === void 0 ? {} : { whenToUse },
|
|
2441
|
-
...proposal === void 0 ? {} : { proposal }
|
|
2442
|
-
}).then(() => options.onSubmitted?.());
|
|
2443
|
-
}
|
|
2444
|
-
/**
|
|
2445
|
-
* Observe successful create calls and submit their optional domain proposal.
|
|
2446
|
-
* Direct creates submit immediately; staged creates submit only after approval.
|
|
2447
|
-
* @param ctx - plugin context with tools and approval events.
|
|
2448
|
-
* @param service - the one assignment service.
|
|
2449
|
-
* @param options - provider/source identity for skillKey.
|
|
2450
|
-
* @returns disposer for both listeners.
|
|
2451
|
-
*/
|
|
2452
|
-
function installAssignmentSidecar(ctx, service, options = {}) {
|
|
2453
|
-
const stopResult = ctx.on("tools/result", (exec, result) => {
|
|
2454
|
-
if (result.isError || exec.name !== "skill_manage" || exec.agent === void 0) return;
|
|
2455
|
-
if (result.value?.pending_id !== void 0) return;
|
|
2456
|
-
const args = exec.arguments;
|
|
2457
|
-
if (typeof args !== "object" || args === null) return;
|
|
2458
|
-
const record = args;
|
|
2459
|
-
if (record.action !== "create") return;
|
|
2460
|
-
submitCreate(service, record, options)?.catch(() => {});
|
|
2461
|
-
});
|
|
2462
|
-
const stopApproval = ctx.on("evolution/approval-decided", ({ record, action, ok }) => {
|
|
2463
|
-
if (!ok || action !== "approve" || record.kind !== "skill" || typeof record.args !== "object" || record.args === null) return;
|
|
2464
|
-
const raw = record.args;
|
|
2465
|
-
const operation = typeof raw.operation === "object" && raw.operation !== null ? raw.operation : raw;
|
|
2466
|
-
if (operation.action !== "create") return;
|
|
2467
|
-
submitCreate(service, operation, options)?.catch(() => {});
|
|
2468
|
-
});
|
|
2469
|
-
return () => {
|
|
2470
|
-
stopResult();
|
|
2471
|
-
stopApproval();
|
|
2472
|
-
};
|
|
2473
|
-
}
|
|
2474
|
-
//#endregion
|
|
2475
2097
|
//#region lib/types/awareness.js
|
|
2476
2098
|
/** Static 1–2 sentence awareness; no domain list/tree. */
|
|
2477
2099
|
const DISCOVERY_AWARENESS = "When skill coverage for a task is uncertain, the domain cards and skill_search can reduce it; search is optional, not a required step.";
|
|
@@ -2516,34 +2138,45 @@ function renderDomainDescription(domain) {
|
|
|
2516
2138
|
}
|
|
2517
2139
|
//#endregion
|
|
2518
2140
|
//#region lib/types/description-refresh.js
|
|
2519
|
-
function nextMapVersion(version) {
|
|
2520
|
-
const numeric = Number(version);
|
|
2521
|
-
return Number.isSafeInteger(numeric) && numeric >= 0 ? String(numeric + 1) : version + ".1";
|
|
2522
|
-
}
|
|
2523
2141
|
/**
|
|
2524
|
-
*
|
|
2142
|
+
* Mark aged descriptions stale AND refresh every stale description in ONE
|
|
2143
|
+
* read/commit. Session start used to call the two producers back to back, so a
|
|
2144
|
+
* single start bumped mapVersion twice (P2-14).
|
|
2525
2145
|
* @param store - domain map store.
|
|
2526
|
-
* @
|
|
2146
|
+
* @param maxAgeHours - age threshold; undefined disables the producer.
|
|
2147
|
+
* @param now - clock for deterministic tests.
|
|
2148
|
+
* @returns counts of marked and refreshed domains.
|
|
2527
2149
|
*/
|
|
2528
|
-
async function
|
|
2150
|
+
async function refreshAgedDescriptions(store, maxAgeHours, now = Date.now()) {
|
|
2151
|
+
if (maxAgeHours === void 0 || maxAgeHours < 0) return {
|
|
2152
|
+
marked: 0,
|
|
2153
|
+
refreshed: 0
|
|
2154
|
+
};
|
|
2529
2155
|
const snapshot = await store.read();
|
|
2530
|
-
const
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2156
|
+
const aged = new Set(snapshot.domains.filter((node) => node.descriptionStatus === "fresh" && (node.descriptionUpdatedAt ?? node.updatedAt) > 0 && now - (node.descriptionUpdatedAt ?? node.updatedAt) > maxAgeHours * 36e5).map((node) => node.id));
|
|
2157
|
+
const stale = snapshot.domains.filter((node) => node.descriptionStatus === "stale" || aged.has(node.id));
|
|
2158
|
+
if (stale.length === 0) return {
|
|
2159
|
+
marked: 0,
|
|
2160
|
+
refreshed: 0
|
|
2161
|
+
};
|
|
2162
|
+
const staleIds = new Set(stale.map((node) => node.id));
|
|
2163
|
+
const domains = snapshot.domains.map((node) => !staleIds.has(node.id) ? node : {
|
|
2534
2164
|
...node,
|
|
2535
2165
|
description: renderDomainDescription(node),
|
|
2536
2166
|
descriptionHash: descriptionHashOf(node, snapshot.domains.filter((child) => child.parentIds.includes(node.id))),
|
|
2537
2167
|
descriptionVersion: node.descriptionVersion + 1,
|
|
2538
2168
|
descriptionStatus: "fresh",
|
|
2539
|
-
|
|
2169
|
+
descriptionUpdatedAt: now
|
|
2540
2170
|
});
|
|
2541
2171
|
await store.commit(snapshot.mapVersion, {
|
|
2542
2172
|
mapVersion: nextMapVersion(snapshot.mapVersion),
|
|
2543
2173
|
domains,
|
|
2544
2174
|
edges: snapshot.edges
|
|
2545
2175
|
});
|
|
2546
|
-
return
|
|
2176
|
+
return {
|
|
2177
|
+
marked: aged.size,
|
|
2178
|
+
refreshed: stale.length
|
|
2179
|
+
};
|
|
2547
2180
|
}
|
|
2548
2181
|
//#endregion
|
|
2549
2182
|
//#region lib/types/main-entry.js
|
|
@@ -2604,6 +2237,7 @@ function searchUnavailable() {
|
|
|
2604
2237
|
}
|
|
2605
2238
|
/** Apply the full production discovery composition. */
|
|
2606
2239
|
function apply(ctx, config) {
|
|
2240
|
+
installEvolutionProjections(ctx);
|
|
2607
2241
|
if (typeof config.featureEpoch !== "string" || config.featureEpoch.length === 0) throw new Error("evolution-skill-discovery: featureEpoch is required");
|
|
2608
2242
|
const evolutionIo = ctx.evolutionIo;
|
|
2609
2243
|
const io = evolutionIoAdapter(() => evolutionIo.provider());
|
|
@@ -2629,7 +2263,7 @@ function apply(ctx, config) {
|
|
|
2629
2263
|
...config.generationAttempts === void 0 ? {} : { attempts: config.generationAttempts }
|
|
2630
2264
|
});
|
|
2631
2265
|
const searchCache = new SkillSearchCache();
|
|
2632
|
-
let searchHealthy =
|
|
2266
|
+
let searchHealthy = false;
|
|
2633
2267
|
const search = {
|
|
2634
2268
|
async allowedSkillKeys(agent, signal) {
|
|
2635
2269
|
if (mode === "off" || agent === void 0) return void 0;
|
|
@@ -2690,10 +2324,11 @@ function apply(ctx, config) {
|
|
|
2690
2324
|
});
|
|
2691
2325
|
if (mode === "off") return;
|
|
2692
2326
|
const map = new DomainMapFileStore(io, config.domainMapPath ?? join(root, "evolution", "domain-map.json"));
|
|
2327
|
+
const descriptionStaleAfterHours = config.descriptionStaleAfterHours ?? 168;
|
|
2693
2328
|
const journal = new ProjectionJournal(io, config.projectionJournalPath ?? join(root, "evolution", "projection-journal.json"));
|
|
2694
2329
|
const snapshots = new ViewSnapshotStore(io, config.viewSnapshotPath ?? join(root, "evolution", "view-snapshots.json"));
|
|
2695
2330
|
const globalPolicy = {
|
|
2696
|
-
mode
|
|
2331
|
+
mode,
|
|
2697
2332
|
pinDomains: config.pinDomains,
|
|
2698
2333
|
disableDomains: config.disableDomains
|
|
2699
2334
|
};
|
|
@@ -2721,6 +2356,7 @@ function apply(ctx, config) {
|
|
|
2721
2356
|
...scope === void 0 ? {} : { scope }
|
|
2722
2357
|
});
|
|
2723
2358
|
};
|
|
2359
|
+
const projections = ctx.get("sessionProjections");
|
|
2724
2360
|
const runtime = new ProjectionRuntime({
|
|
2725
2361
|
skillIndexSource: source,
|
|
2726
2362
|
map,
|
|
@@ -2728,6 +2364,8 @@ function apply(ctx, config) {
|
|
|
2728
2364
|
policyFor,
|
|
2729
2365
|
journal,
|
|
2730
2366
|
workingDirectory,
|
|
2367
|
+
history: (agent) => projections?.stateOf(agent.session, "evolutionProjectionHistory"),
|
|
2368
|
+
warn: (message) => ctx.logger.warn(message),
|
|
2731
2369
|
featureEpoch: config.featureEpoch,
|
|
2732
2370
|
codecVersion: "1",
|
|
2733
2371
|
adapterVersion: "1",
|
|
@@ -2737,24 +2375,9 @@ function apply(ctx, config) {
|
|
|
2737
2375
|
...config.tokenBudget === void 0 ? {} : { tokenBudget: config.tokenBudget },
|
|
2738
2376
|
searchAvailable: () => searchHealthy && config.searchEnabled !== false,
|
|
2739
2377
|
...config.staleAfterHours === void 0 ? {} : { staleAfterHours: config.staleAfterHours },
|
|
2740
|
-
|
|
2378
|
+
descriptionStaleAfterHours,
|
|
2741
2379
|
isFrozen: async (agent) => snapshots.isFrozen(String(agent.session.id), config.featureEpoch)
|
|
2742
2380
|
});
|
|
2743
|
-
const queue = new JsonAssignmentQueue(io, config.assignmentQueuePath ?? join(root, "evolution", "assignment-queue.json"), ctx.get("evolutionStateStore"));
|
|
2744
|
-
const assignment = new DomainAssignmentService(queue, map, {
|
|
2745
|
-
createClassificationEnabled: config.createClassificationEnabled ?? false,
|
|
2746
|
-
...config.provisionalThreshold === void 0 ? {} : { provisionalThreshold: config.provisionalThreshold },
|
|
2747
|
-
now: Date.now
|
|
2748
|
-
});
|
|
2749
|
-
const worker = new AssignmentWorker(assignment, queue);
|
|
2750
|
-
queue.load().then(() => worker.drain()).catch(() => {
|
|
2751
|
-
ctx.logger.warn("evolution-skill-discovery: assignment queue load failed; queued work stays for the next start");
|
|
2752
|
-
});
|
|
2753
|
-
installAssignmentSidecar(ctx, assignment, { onSubmitted: () => {
|
|
2754
|
-
worker.drain().catch((error) => {
|
|
2755
|
-
ctx.logger.warn("evolution-skill-discovery: assignment drain failed: " + (error instanceof Error ? error.message : String(error)));
|
|
2756
|
-
});
|
|
2757
|
-
} });
|
|
2758
2381
|
ctx.on("agent/created", async ({ agent, source: start }) => {
|
|
2759
2382
|
try {
|
|
2760
2383
|
installDiscoveryAwareness(agent.ctx);
|
|
@@ -2779,8 +2402,9 @@ function apply(ctx, config) {
|
|
|
2779
2402
|
featureEpoch: config.featureEpoch,
|
|
2780
2403
|
createdAt: Date.now()
|
|
2781
2404
|
});
|
|
2782
|
-
const
|
|
2783
|
-
if (
|
|
2405
|
+
const aged = await refreshAgedDescriptions(map, descriptionStaleAfterHours);
|
|
2406
|
+
if (aged.marked > 0) ctx.logger.info("evolution-skill-discovery: marked " + aged.marked + " aged domain descriptions stale");
|
|
2407
|
+
if (aged.refreshed > 0) ctx.logger.info("evolution-skill-discovery: refreshed " + aged.refreshed + " stale domain descriptions");
|
|
2784
2408
|
} catch (error) {
|
|
2785
2409
|
ctx.logger.warn("evolution-skill-discovery: initial view snapshot failed: " + (error instanceof Error ? error.message : String(error)));
|
|
2786
2410
|
}
|
|
@@ -2816,23 +2440,9 @@ function apply(ctx, config) {
|
|
|
2816
2440
|
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")
|
|
2817
2441
|
};
|
|
2818
2442
|
}
|
|
2819
|
-
if (action === "confirm") {
|
|
2820
|
-
const domainId = rawInput.trim().split(/\s+/)[1];
|
|
2821
|
-
if (domainId === void 0) return {
|
|
2822
|
-
kind: "error",
|
|
2823
|
-
text: "Usage: /skill-domain confirm <id>"
|
|
2824
|
-
};
|
|
2825
|
-
return await assignment.confirm(domainId) ? {
|
|
2826
|
-
kind: "success",
|
|
2827
|
-
text: "Confirmed domain " + domainId + "."
|
|
2828
|
-
} : {
|
|
2829
|
-
kind: "error",
|
|
2830
|
-
text: "Unknown or non-provisional domain: " + domainId
|
|
2831
|
-
};
|
|
2832
|
-
}
|
|
2833
2443
|
if (action !== "generate") return {
|
|
2834
2444
|
kind: "error",
|
|
2835
|
-
text: "Usage: /skill-domain status | generate
|
|
2445
|
+
text: "Usage: /skill-domain status | generate"
|
|
2836
2446
|
};
|
|
2837
2447
|
if (coordinator === void 0) return {
|
|
2838
2448
|
kind: "error",
|