@klarkxy/dsh-model-hub 0.1.2 → 0.1.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 +8 -4
- package/docs/README.zh-CN.md +8 -4
- package/lib/client.inner.cjs +298 -115
- package/lib/client.inner.cjs.map +1 -1
- package/lib/client.js +298 -115
- package/lib/index.js +206 -182
- package/lib/index.js.map +1 -1
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +12 -12
package/lib/index.js
CHANGED
|
@@ -1,13 +1,45 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import Schema from "@deepseek-ai/schemastery";
|
|
4
4
|
import { registerHostRpc } from "@klarkxy/dsh-plugin-kit/host-rpc";
|
|
5
5
|
import { modelRouteRoleSchemastery, modelRouteSchemastery } from "@klarkxy/dsh-model-route/schemastery";
|
|
6
6
|
import { detectModelFields, modelRouteOverride, normalizeModelRoute } from "@klarkxy/dsh-model-route";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
7
|
+
import { collectModelSettings, notifyModelSettingsChanged } from "@klarkxy/dsh-plugin-kit/model-settings";
|
|
8
|
+
import { isSubagentSession, modelSettingsChangesIdentity } from "@klarkxy/dsh-plugin-kit/contracts";
|
|
9
|
+
import { createModelSettingsChanges } from "@klarkxy/dsh-plugin-kit/model-settings-changes";
|
|
10
|
+
import { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
11
|
+
//#region src/diagnostics.ts
|
|
12
|
+
/** Invalidation is constant-time. Only startup and explicit reads request a
|
|
13
|
+
* diagnostic; a burst shares one deferred run. Work never reschedules itself. */
|
|
14
|
+
function createProbeDiagnostics(run, onError) {
|
|
15
|
+
let dirty = true;
|
|
16
|
+
let stopped = false;
|
|
17
|
+
let timer;
|
|
18
|
+
let running;
|
|
19
|
+
return {
|
|
20
|
+
invalidate() {
|
|
21
|
+
if (!stopped) dirty = true;
|
|
22
|
+
},
|
|
23
|
+
request() {
|
|
24
|
+
if (stopped || !dirty || timer !== void 0 || running) return;
|
|
25
|
+
timer = setTimeout(() => {
|
|
26
|
+
timer = void 0;
|
|
27
|
+
if (stopped || !dirty) return;
|
|
28
|
+
dirty = false;
|
|
29
|
+
running = Promise.resolve().then(run).catch(onError).finally(() => {
|
|
30
|
+
running = void 0;
|
|
31
|
+
});
|
|
32
|
+
}, 0);
|
|
33
|
+
},
|
|
34
|
+
async dispose() {
|
|
35
|
+
stopped = true;
|
|
36
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
37
|
+
timer = void 0;
|
|
38
|
+
await running;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
11
43
|
//#region src/probe.ts
|
|
12
44
|
/**
|
|
13
45
|
* Pure probe-report builder for the model hub. Takes the host's
|
|
@@ -209,170 +241,10 @@ function createHubSettings(events, settings, ownNamespace = "@klarkxy/dsh-model-
|
|
|
209
241
|
}
|
|
210
242
|
//#endregion
|
|
211
243
|
//#region src/changes.ts
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name);
|
|
216
|
-
var __typeError = (msg) => {
|
|
217
|
-
throw TypeError(msg);
|
|
218
|
-
};
|
|
219
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, {
|
|
220
|
-
enumerable: true,
|
|
221
|
-
configurable: true,
|
|
222
|
-
writable: true,
|
|
223
|
-
value
|
|
224
|
-
}) : obj[key] = value;
|
|
225
|
-
var __name = (target, value) => __defProp(target, "name", {
|
|
226
|
-
value,
|
|
227
|
-
configurable: true
|
|
228
|
-
});
|
|
229
|
-
var __decoratorStart = (base) => [
|
|
230
|
-
,
|
|
231
|
-
,
|
|
232
|
-
,
|
|
233
|
-
__create(base?.[__knownSymbol("metadata")] ?? null)
|
|
234
|
-
];
|
|
235
|
-
var __decoratorStrings = [
|
|
236
|
-
"class",
|
|
237
|
-
"method",
|
|
238
|
-
"getter",
|
|
239
|
-
"setter",
|
|
240
|
-
"accessor",
|
|
241
|
-
"field",
|
|
242
|
-
"value",
|
|
243
|
-
"get",
|
|
244
|
-
"set"
|
|
245
|
-
];
|
|
246
|
-
var __expectFn = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError("Function expected") : fn;
|
|
247
|
-
var __decoratorContext = (kind, name, done, metadata, fns) => ({
|
|
248
|
-
kind: __decoratorStrings[kind],
|
|
249
|
-
name,
|
|
250
|
-
metadata,
|
|
251
|
-
addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null))
|
|
252
|
-
});
|
|
253
|
-
var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]);
|
|
254
|
-
var __runInitializers = (array, flags, self, value) => {
|
|
255
|
-
for (var i = 0, fns = array[flags >> 1], n = fns && fns.length; i < n; i++) flags & 1 ? fns[i].call(self) : value = fns[i].call(self, value);
|
|
256
|
-
return value;
|
|
257
|
-
};
|
|
258
|
-
var __decorateElement = (array, flags, name, decorators, target, extra) => {
|
|
259
|
-
var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16);
|
|
260
|
-
var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5];
|
|
261
|
-
var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []);
|
|
262
|
-
var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : {
|
|
263
|
-
get [name]() {
|
|
264
|
-
return __privateGet(this, extra);
|
|
265
|
-
},
|
|
266
|
-
set [name](x) {
|
|
267
|
-
return __privateSet(this, extra, x);
|
|
268
|
-
}
|
|
269
|
-
}, name));
|
|
270
|
-
k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name);
|
|
271
|
-
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
272
|
-
ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers);
|
|
273
|
-
if (k) {
|
|
274
|
-
ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => name in x };
|
|
275
|
-
if (k ^ 3) access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name];
|
|
276
|
-
if (k > 2) access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y;
|
|
277
|
-
}
|
|
278
|
-
it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? void 0 : {
|
|
279
|
-
get: desc.get,
|
|
280
|
-
set: desc.set
|
|
281
|
-
} : target, ctx), done._ = 1;
|
|
282
|
-
if (k ^ 4 || it === void 0) __expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it);
|
|
283
|
-
else if (typeof it !== "object" || it === null) __typeError("Object expected");
|
|
284
|
-
else __expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn);
|
|
285
|
-
}
|
|
286
|
-
return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
|
|
287
|
-
};
|
|
288
|
-
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
289
|
-
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
290
|
-
var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError("Cannot use the \"in\" operator on this value") : member.has(obj);
|
|
291
|
-
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
292
|
-
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
293
|
-
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
294
|
-
var _watch_dec;
|
|
295
|
-
var _a;
|
|
296
|
-
var _init;
|
|
297
|
-
async function* watchModelSettingsChanges(events, signal) {
|
|
298
|
-
signal.throwIfAborted();
|
|
299
|
-
const pending = /* @__PURE__ */ new Map();
|
|
300
|
-
let full = false;
|
|
301
|
-
let wake;
|
|
302
|
-
const changed = (ns, revision) => {
|
|
303
|
-
let frame;
|
|
304
|
-
try {
|
|
305
|
-
frame = parseModelSettingsChangeFrame({
|
|
306
|
-
kind: "changed",
|
|
307
|
-
ns,
|
|
308
|
-
revision
|
|
309
|
-
});
|
|
310
|
-
} catch {
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
if (!full) {
|
|
314
|
-
if (!pending.has(ns) && pending.size >= 256) {
|
|
315
|
-
pending.clear();
|
|
316
|
-
full = true;
|
|
317
|
-
} else pending.set(ns, frame);
|
|
318
|
-
}
|
|
319
|
-
wake?.();
|
|
320
|
-
};
|
|
321
|
-
const disposers = [];
|
|
322
|
-
const cleanup = () => {
|
|
323
|
-
for (const off of disposers.splice(0).reverse()) off();
|
|
324
|
-
pending.clear();
|
|
325
|
-
};
|
|
326
|
-
const aborted = () => {
|
|
327
|
-
cleanup();
|
|
328
|
-
wake?.();
|
|
329
|
-
};
|
|
330
|
-
signal.addEventListener("abort", aborted, { once: true });
|
|
331
|
-
try {
|
|
332
|
-
disposers.push(events.on(MODEL_SETTINGS_CHANGED_EVENT, ({ ns, revision }) => changed(ns, revision)));
|
|
333
|
-
disposers.push(events.on("settings/document-updated", changed));
|
|
334
|
-
signal.throwIfAborted();
|
|
335
|
-
yield { kind: "ready" };
|
|
336
|
-
while (true) {
|
|
337
|
-
signal.throwIfAborted();
|
|
338
|
-
if (full) {
|
|
339
|
-
full = false;
|
|
340
|
-
yield { kind: "ready" };
|
|
341
|
-
continue;
|
|
342
|
-
}
|
|
343
|
-
if (pending.size) {
|
|
344
|
-
const [ns, frame] = pending.entries().next().value;
|
|
345
|
-
pending.delete(ns);
|
|
346
|
-
yield frame;
|
|
347
|
-
continue;
|
|
348
|
-
}
|
|
349
|
-
await new Promise((resolve) => {
|
|
350
|
-
wake = resolve;
|
|
351
|
-
if (signal.aborted || full || pending.size) resolve();
|
|
352
|
-
});
|
|
353
|
-
wake = void 0;
|
|
354
|
-
}
|
|
355
|
-
} finally {
|
|
356
|
-
signal.removeEventListener("abort", aborted);
|
|
357
|
-
cleanup();
|
|
358
|
-
}
|
|
244
|
+
/** Model Hub forwards every settings namespace; feature plugins filter their own. */
|
|
245
|
+
function registerHubModelSettingsChanges(ctx) {
|
|
246
|
+
createModelSettingsChanges(ctx, modelSettingsChangesIdentity("@klarkxy/dsh-model-hub"));
|
|
359
247
|
}
|
|
360
|
-
var ModelSettingsChangesController = class extends (_a = TypertRemoteService, _watch_dec = [Remote({ mode: "stream" })], _a) {
|
|
361
|
-
constructor(ctx) {
|
|
362
|
-
super(ctx, MODEL_SETTINGS_CHANGES_REMOTE.descriptors[0].service, { namespace: MODEL_SETTINGS_CHANGES_REMOTE.descriptors[0].namespace });
|
|
363
|
-
__runInitializers(_init, 5, this);
|
|
364
|
-
__publicField(this, "lifetime", new AbortController());
|
|
365
|
-
ctx.effect(() => () => {
|
|
366
|
-
this.lifetime.abort(/* @__PURE__ */ new Error("Model settings sync owner unloaded"));
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
watch(signal) {
|
|
370
|
-
return watchModelSettingsChanges(this.ctx, AbortSignal.any([this.lifetime.signal, signal]));
|
|
371
|
-
}
|
|
372
|
-
};
|
|
373
|
-
_init = __decoratorStart(_a);
|
|
374
|
-
__decorateElement(_init, 1, "watch", _watch_dec, ModelSettingsChangesController);
|
|
375
|
-
__decoratorMetadata(_init, ModelSettingsChangesController);
|
|
376
248
|
//#endregion
|
|
377
249
|
//#region src/cast.ts
|
|
378
250
|
/**
|
|
@@ -457,9 +329,15 @@ function validateIndex(value) {
|
|
|
457
329
|
*/
|
|
458
330
|
function buildCastPrompt(enabled, routes) {
|
|
459
331
|
if (!enabled || !routes.length) return "";
|
|
460
|
-
const annotated = routes.filter((route) => typeof route.tendency === "string" && route.tendency);
|
|
461
|
-
|
|
462
|
-
|
|
332
|
+
const annotated = routes.filter((route) => typeof route.tendency === "string" && route.tendency.trim());
|
|
333
|
+
const guidance = [
|
|
334
|
+
"Subagent model selection:",
|
|
335
|
+
"When delegating an independent task, actively choose a suitable model instead of silently inheriting your own. If list_subagent_models and a delegation tool with provider/model fields are available, inspect the available routes before the first model-selectable delegation in this task, reuse those results for later delegations, and supply provider and model together explicitly.",
|
|
336
|
+
"For independent reviews, alternative approaches, or disputed conclusions, prefer a model different from your own when a suitable candidate is available. Choose for task fit and the user's constraints; using the same model is appropriate when it fits better or no suitable alternative is available. Do not create extra subtasks solely for model diversity.",
|
|
337
|
+
"The current Session's discovery results and tool schema are authoritative. Configured model notes may refer to routes unavailable in this Session. If discovery or model-selection fields are unavailable, use the supported delegation behavior; do not invent routes or pass unsupported fields to tools such as subagent_fork. When changing models, omit reasoning_effort unless you have verified a supported value."
|
|
338
|
+
];
|
|
339
|
+
if (annotated.length) guidance.push("User-provided task tendencies (advisory): prefer a matching tendency among available models. Models without a tendency remain general-purpose candidates; no model-specific strengths are assumed.", ...annotated.map((route) => `- ${route.provider}/${route.model}${route.reasoningEffort ? ` (reasoning effort: ${route.reasoningEffort})` : ""}: ${route.tendency}`));
|
|
340
|
+
return guidance.join("\n");
|
|
463
341
|
}
|
|
464
342
|
function createCast(deps) {
|
|
465
343
|
const castFile = () => {
|
|
@@ -683,7 +561,7 @@ function createCast(deps) {
|
|
|
683
561
|
return buildCastPrompt(snapshot.enabled, mergeTendencies(snapshot.routes, readTendencies().tendencies));
|
|
684
562
|
} catch (error) {
|
|
685
563
|
deps.logger?.warn("model-hub: cast tendencies unavailable: %s", error.message);
|
|
686
|
-
return
|
|
564
|
+
return buildCastPrompt(snapshot.enabled, snapshot.routes);
|
|
687
565
|
}
|
|
688
566
|
};
|
|
689
567
|
return {
|
|
@@ -697,6 +575,128 @@ function createCast(deps) {
|
|
|
697
575
|
};
|
|
698
576
|
}
|
|
699
577
|
//#endregion
|
|
578
|
+
//#region src/running.ts
|
|
579
|
+
/**
|
|
580
|
+
* dsh-model-hub — running subagent routes (host half).
|
|
581
|
+
*
|
|
582
|
+
* Read-only live view answering "which model and reasoning effort is each
|
|
583
|
+
* running subagent actually using". `subagent/start` and `subagent/end`
|
|
584
|
+
* maintain the live set; each child's effective route is read from its
|
|
585
|
+
* session epoch header (`session.requestHeader()`) and kept current through
|
|
586
|
+
* the session's own `request/header` events (reason `change` covers mid-run
|
|
587
|
+
* model switches). Children already running when this plugin (re)loads are
|
|
588
|
+
* recovered from the live agent registry via `isSubagentSession`.
|
|
589
|
+
*
|
|
590
|
+
* The effective route is session data recorded by the host; this tracker
|
|
591
|
+
* never writes to sessions or settings. Display-only invalidations reuse the
|
|
592
|
+
* model-settings change channel with RUNNING_NAMESPACE so open pages refetch.
|
|
593
|
+
*/
|
|
594
|
+
/** Invalidation namespace for the running-subagent view; carries no settings. */
|
|
595
|
+
const RUNNING_NAMESPACE = "@klarkxy/dsh-model-hub/running-subagents";
|
|
596
|
+
function text(value) {
|
|
597
|
+
return typeof value === "string" ? value.trim() : "";
|
|
598
|
+
}
|
|
599
|
+
/** Normalize one epoch header into the display route plus the adapter-default flag. */
|
|
600
|
+
function routeFromHeader(header) {
|
|
601
|
+
const config = header?.config;
|
|
602
|
+
const provider = text(config?.provider);
|
|
603
|
+
const model = text(config?.model);
|
|
604
|
+
if (!provider || !model) return {};
|
|
605
|
+
const effort = text(config?.reasoningEffort);
|
|
606
|
+
return {
|
|
607
|
+
route: effort ? {
|
|
608
|
+
provider,
|
|
609
|
+
model,
|
|
610
|
+
reasoningEffort: effort
|
|
611
|
+
} : {
|
|
612
|
+
provider,
|
|
613
|
+
model
|
|
614
|
+
},
|
|
615
|
+
...effort && header?.adapterDefaults?.reasoningEffort === true ? { effortIsAdapterDefault: true } : {}
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
function sameRoute(left, right) {
|
|
619
|
+
return left.route?.provider === right.route?.provider && left.route?.model === right.route?.model && left.route?.reasoningEffort === right.route?.reasoningEffort && left.effortIsAdapterDefault === right.effortIsAdapterDefault;
|
|
620
|
+
}
|
|
621
|
+
function createRunningTracker(deps) {
|
|
622
|
+
const entries = /* @__PURE__ */ new Map();
|
|
623
|
+
let revision = 0;
|
|
624
|
+
const bump = () => deps.notify(++revision);
|
|
625
|
+
const add = (id, info) => {
|
|
626
|
+
const session = (deps.agents?.get(id))?.session;
|
|
627
|
+
const next = routeFromHeader(session?.requestHeader?.());
|
|
628
|
+
const existing = entries.get(id);
|
|
629
|
+
if (existing) {
|
|
630
|
+
const providerName = info?.provider ?? existing.providerName;
|
|
631
|
+
const local = info?.local ?? existing.local;
|
|
632
|
+
const changed = existing.providerName !== providerName || existing.local !== local || !sameRoute(existing, next);
|
|
633
|
+
existing.providerName = providerName;
|
|
634
|
+
existing.local = local;
|
|
635
|
+
existing.route = next.route;
|
|
636
|
+
existing.effortIsAdapterDefault = next.effortIsAdapterDefault;
|
|
637
|
+
if (changed) bump();
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
entries.set(id, {
|
|
641
|
+
id,
|
|
642
|
+
local: info?.local !== false,
|
|
643
|
+
...info?.provider ? { providerName: info.provider } : {},
|
|
644
|
+
...typeof session?.header?.parentSession === "string" && session.header.parentSession ? { parentSessionId: session.header.parentSession } : {},
|
|
645
|
+
...typeof session?.header?.createdAt === "number" ? { startedAt: session.header.createdAt } : {},
|
|
646
|
+
route: next.route,
|
|
647
|
+
effortIsAdapterDefault: next.effortIsAdapterDefault
|
|
648
|
+
});
|
|
649
|
+
bump();
|
|
650
|
+
};
|
|
651
|
+
const remove = (id) => {
|
|
652
|
+
if (entries.delete(id)) bump();
|
|
653
|
+
};
|
|
654
|
+
try {
|
|
655
|
+
for (const agent of deps.agents?.list() ?? []) if (agent?.id && isSubagentSession(agent.session)) add(agent.id);
|
|
656
|
+
} catch {}
|
|
657
|
+
deps.events.on("subagent/start", (info) => {
|
|
658
|
+
if (typeof info?.id === "string" && info.id) add(info.id, info);
|
|
659
|
+
});
|
|
660
|
+
deps.events.on("subagent/end", (info) => {
|
|
661
|
+
if (typeof info?.id === "string") remove(info.id);
|
|
662
|
+
});
|
|
663
|
+
deps.events.on("agent/disposed", (payload) => {
|
|
664
|
+
if (typeof payload?.agent?.id === "string") remove(payload.agent.id);
|
|
665
|
+
});
|
|
666
|
+
deps.events.on("session/event", (session, event) => {
|
|
667
|
+
if (event?.type !== "request/header") return;
|
|
668
|
+
const id = typeof session?.id === "string" ? session.id : void 0;
|
|
669
|
+
const entry = id ? entries.get(id) : void 0;
|
|
670
|
+
if (!entry) return;
|
|
671
|
+
const next = routeFromHeader(event.data?.header);
|
|
672
|
+
if (sameRoute(entry, next)) return;
|
|
673
|
+
entry.route = next.route;
|
|
674
|
+
entry.effortIsAdapterDefault = next.effortIsAdapterDefault;
|
|
675
|
+
bump();
|
|
676
|
+
});
|
|
677
|
+
return { async list(signal) {
|
|
678
|
+
const pendingParents = /* @__PURE__ */ new Set();
|
|
679
|
+
for (const entry of entries.values()) if (entry.label === void 0 && entry.parentSessionId) pendingParents.add(entry.parentSessionId);
|
|
680
|
+
if (deps.subagents && pendingParents.size) await Promise.allSettled([...pendingParents].map(async (parent) => {
|
|
681
|
+
const children = await deps.subagents.listChildren(parent, signal);
|
|
682
|
+
const labels = new Map(children.map((child) => [child.id, child.label]));
|
|
683
|
+
for (const entry of entries.values()) {
|
|
684
|
+
if (entry.parentSessionId !== parent || entry.label !== void 0) continue;
|
|
685
|
+
const label = labels.get(entry.id);
|
|
686
|
+
if (typeof label === "string" && label.trim()) entry.label = label.trim();
|
|
687
|
+
}
|
|
688
|
+
}));
|
|
689
|
+
const snapshot = [...entries.values()].map((entry) => ({
|
|
690
|
+
...entry,
|
|
691
|
+
route: entry.route ? { ...entry.route } : void 0
|
|
692
|
+
})).sort((a, b) => (a.startedAt ?? 0) - (b.startedAt ?? 0) || a.id.localeCompare(b.id));
|
|
693
|
+
return {
|
|
694
|
+
revision,
|
|
695
|
+
entries: snapshot
|
|
696
|
+
};
|
|
697
|
+
} };
|
|
698
|
+
}
|
|
699
|
+
//#endregion
|
|
700
700
|
//#region src/index.ts
|
|
701
701
|
/**
|
|
702
702
|
* dsh-model-hub — cross-plugin model settings hub.
|
|
@@ -735,7 +735,7 @@ const Config = Schema.object({
|
|
|
735
735
|
}).volatile()
|
|
736
736
|
});
|
|
737
737
|
function apply(ctx) {
|
|
738
|
-
|
|
738
|
+
registerHubModelSettingsChanges(ctx);
|
|
739
739
|
const profile = ctx.get("profileContext");
|
|
740
740
|
const settings = ctx.settings;
|
|
741
741
|
const describe = () => settings.describe({ redactSecrets: true });
|
|
@@ -751,13 +751,20 @@ function apply(ctx) {
|
|
|
751
751
|
logger: ctx.logger,
|
|
752
752
|
notifyTendenciesChanged: (revision) => notifyModelSettingsChanged(ctx, CAST_TENDENCIES_NAMESPACE, revision)
|
|
753
753
|
});
|
|
754
|
+
const running = createRunningTracker({
|
|
755
|
+
events: ctx,
|
|
756
|
+
agents: ctx.get("agents"),
|
|
757
|
+
subagents: ctx.get("subagents"),
|
|
758
|
+
notify: (revision) => notifyModelSettingsChanged(ctx, RUNNING_NAMESPACE, revision)
|
|
759
|
+
});
|
|
754
760
|
ctx.systemPrompt.section({
|
|
755
761
|
name: "model-hub:subagent-cast",
|
|
756
762
|
order: ctx.systemPrompt.getSectionOrder("TOOL_SUBAGENT") + 1,
|
|
757
763
|
interpolate: false,
|
|
758
764
|
text: () => cast.promptText(() => ctx.get("subagentModelSelection")?.current())
|
|
759
765
|
});
|
|
760
|
-
const run = (
|
|
766
|
+
const run = async () => {
|
|
767
|
+
const reason = "requested";
|
|
761
768
|
let report;
|
|
762
769
|
try {
|
|
763
770
|
report = buildProbeReport(describe());
|
|
@@ -768,20 +775,33 @@ function apply(ctx) {
|
|
|
768
775
|
ctx.logger.info("model-hub: probe (%s): %d namespaces, %d with model fields; self-test extra-key=%s role=%s", reason, report.totalNamespaces, report.namespacesWithModelFields, report.selfTest.extraKey, report.selfTest.role);
|
|
769
776
|
if (profile?.dir) try {
|
|
770
777
|
const dir = join(profile.dir, "data", "model-hub");
|
|
771
|
-
|
|
772
|
-
|
|
778
|
+
await mkdir(dir, { recursive: true });
|
|
779
|
+
await writeFile(join(dir, "probe-report.json"), JSON.stringify(report, null, 2));
|
|
773
780
|
} catch (error) {
|
|
774
781
|
ctx.logger.warn("model-hub: could not write probe report: %s", error instanceof Error ? error.message : String(error));
|
|
775
782
|
}
|
|
776
783
|
};
|
|
777
|
-
run(
|
|
778
|
-
|
|
784
|
+
const diagnostics = createProbeDiagnostics(run, (error) => {
|
|
785
|
+
ctx.logger.warn("model-hub: diagnostics failed: %s", String(error));
|
|
786
|
+
});
|
|
787
|
+
ctx.effect(() => {
|
|
788
|
+
const off = ctx.on("settings/document-updated", () => diagnostics.invalidate());
|
|
789
|
+
diagnostics.request();
|
|
790
|
+
return async () => {
|
|
791
|
+
off();
|
|
792
|
+
await diagnostics.dispose();
|
|
793
|
+
};
|
|
794
|
+
});
|
|
779
795
|
ctx.effect(() => registerHostRpc(ctx, CHANNEL, async (endpoint, payload, signal) => {
|
|
780
796
|
try {
|
|
781
|
-
if (endpoint === "fields.list")
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
797
|
+
if (endpoint === "fields.list") {
|
|
798
|
+
const value = await hub.list(signal);
|
|
799
|
+
diagnostics.request();
|
|
800
|
+
return {
|
|
801
|
+
ok: true,
|
|
802
|
+
value
|
|
803
|
+
};
|
|
804
|
+
}
|
|
785
805
|
if (endpoint === "fields.set") return {
|
|
786
806
|
ok: true,
|
|
787
807
|
value: await hub.set(payload, signal)
|
|
@@ -812,6 +832,10 @@ function apply(ctx) {
|
|
|
812
832
|
ok: true,
|
|
813
833
|
value: cast.setTendency(row.provider, row.model, row.tendency, row.expectedRevision, signal)
|
|
814
834
|
};
|
|
835
|
+
if (endpoint === "cast.running") return {
|
|
836
|
+
ok: true,
|
|
837
|
+
value: await running.list(signal)
|
|
838
|
+
};
|
|
815
839
|
}
|
|
816
840
|
throw new Error(`Unknown endpoint: ${endpoint}`);
|
|
817
841
|
} catch (error) {
|