@lmzhen/dsh-evolution-skill-health 0.30.37 → 0.30.38
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/client.js +164 -27
- package/lib/index.js +2 -0
- package/lib/types/client/Panel.d.ts +22 -1
- package/lib/types/client/seats.d.ts +6 -0
- package/lib/types/routes.d.ts +7 -1
- package/package.json +6 -6
package/lib/client.js
CHANGED
|
@@ -288,10 +288,14 @@ window.__ModuleLoader__.load({
|
|
|
288
288
|
"title": "技能健康",
|
|
289
289
|
"hint": "体检引擎的结论,按规则聚合。未标定的阈值只报规则名、不报数字;读不到的技能按「不可读」列出,不当作没问题。",
|
|
290
290
|
"refresh": "刷新",
|
|
291
|
+
"readAt.unknown": "扫描时间未知",
|
|
292
|
+
"scan.complete": "扫描完成",
|
|
293
|
+
"scan.unavailable": "扫描不可用",
|
|
294
|
+
"protected": "受保护,写入被拒绝。",
|
|
291
295
|
"loading": "读取中…",
|
|
292
296
|
"group.byRule": "按规则",
|
|
293
297
|
"group.bySkill": "按技能",
|
|
294
|
-
"verdict.
|
|
298
|
+
"verdict.healthy": "正常",
|
|
295
299
|
"verdict.warn": "警告",
|
|
296
300
|
"verdict.needs-restructure": "需重构",
|
|
297
301
|
"verdict.error": "错误",
|
|
@@ -304,7 +308,21 @@ window.__ModuleLoader__.load({
|
|
|
304
308
|
"proposal.outcome.owner-gap": "owner 缺口",
|
|
305
309
|
"proposal.owner.covered": "有覆盖 owner",
|
|
306
310
|
"proposal.owner.owner-gap": "无覆盖 owner",
|
|
311
|
+
"proposal.status.proposed": "状态:提议",
|
|
312
|
+
"proposal.status.previewed": "状态:已预览",
|
|
313
|
+
"proposal.status.stale": "状态:已过期",
|
|
314
|
+
"proposal.status.dismissed": "状态:已忽略",
|
|
315
|
+
"proposal.reusable": "可提取",
|
|
316
|
+
"proposal.kept": "保留正文",
|
|
317
|
+
"proposal.warnings": "警告",
|
|
318
|
+
"proposal.copy": "复制命令",
|
|
307
319
|
"counts": "{ok} 正常 · {warn} 警告 · {error} 错误 · {unreadable} 不可读",
|
|
320
|
+
"col.skill": "技能",
|
|
321
|
+
"col.findings": "证据",
|
|
322
|
+
"findings.none": "无规则命中",
|
|
323
|
+
"dims.body": "正文",
|
|
324
|
+
"dims.support": "支撑文件",
|
|
325
|
+
"dims.unknown": "未知",
|
|
308
326
|
"col.rule": "规则",
|
|
309
327
|
"col.skills": "涉及技能",
|
|
310
328
|
"col.count": "次数",
|
|
@@ -321,7 +339,11 @@ window.__ModuleLoader__.load({
|
|
|
321
339
|
"failure.forbidden": "平台拒绝了这个请求(身份不是 owner)。",
|
|
322
340
|
"failure.unmounted": "该功能没有挂载。",
|
|
323
341
|
"failure.server": "读取失败,可以重试。",
|
|
324
|
-
"fallback.hint": "可以改用命令:{command}"
|
|
342
|
+
"fallback.hint": "可以改用命令:{command}",
|
|
343
|
+
"action.history": "查看历史",
|
|
344
|
+
"action.copy": "复制命令",
|
|
345
|
+
"action.extract": "提取",
|
|
346
|
+
"action.extract.reason": "命令/预览面未就绪,暂不可用。"
|
|
325
347
|
};
|
|
326
348
|
/** English dictionary. */
|
|
327
349
|
const en = {
|
|
@@ -330,10 +352,14 @@ window.__ModuleLoader__.load({
|
|
|
330
352
|
"title": "Skill Health",
|
|
331
353
|
"hint": "What the health engine found, aggregated by rule. A rule whose threshold is not calibrated yet is named without a number; skills that cannot be read are listed as unreadable, never as fine.",
|
|
332
354
|
"refresh": "Refresh",
|
|
355
|
+
"readAt.unknown": "Scan time unknown",
|
|
356
|
+
"scan.complete": "Scan complete",
|
|
357
|
+
"scan.unavailable": "Scan unavailable",
|
|
358
|
+
"protected": "Protected; writes are refused.",
|
|
333
359
|
"loading": "Loading…",
|
|
334
360
|
"group.byRule": "By rule",
|
|
335
361
|
"group.bySkill": "By skill",
|
|
336
|
-
"verdict.
|
|
362
|
+
"verdict.healthy": "OK",
|
|
337
363
|
"verdict.warn": "Warn",
|
|
338
364
|
"verdict.needs-restructure": "Needs restructure",
|
|
339
365
|
"verdict.error": "Error",
|
|
@@ -346,7 +372,21 @@ window.__ModuleLoader__.load({
|
|
|
346
372
|
"proposal.outcome.owner-gap": "owner-gap",
|
|
347
373
|
"proposal.owner.covered": "covered owner",
|
|
348
374
|
"proposal.owner.owner-gap": "no covering owner",
|
|
375
|
+
"proposal.status.proposed": "status: proposed",
|
|
376
|
+
"proposal.status.previewed": "status: previewed",
|
|
377
|
+
"proposal.status.stale": "status: stale",
|
|
378
|
+
"proposal.status.dismissed": "status: dismissed",
|
|
379
|
+
"proposal.reusable": "reusable",
|
|
380
|
+
"proposal.kept": "kept body",
|
|
381
|
+
"proposal.warnings": "warnings",
|
|
382
|
+
"proposal.copy": "Copy command",
|
|
349
383
|
"counts": "{ok} ok · {warn} warn · {error} error · {unreadable} unreadable",
|
|
384
|
+
"col.skill": "Skill",
|
|
385
|
+
"col.findings": "Evidence",
|
|
386
|
+
"findings.none": "No rule hit",
|
|
387
|
+
"dims.body": "Body",
|
|
388
|
+
"dims.support": "Support files",
|
|
389
|
+
"dims.unknown": "unknown",
|
|
350
390
|
"col.rule": "Rule",
|
|
351
391
|
"col.skills": "Skills",
|
|
352
392
|
"col.count": "Count",
|
|
@@ -362,7 +402,11 @@ window.__ModuleLoader__.load({
|
|
|
362
402
|
"failure.forbidden": "The platform refused this request (not the owner principal).",
|
|
363
403
|
"failure.unmounted": "This feature is not mounted.",
|
|
364
404
|
"failure.server": "The read failed; you can retry.",
|
|
365
|
-
"fallback.hint": "Command fallback: {command}"
|
|
405
|
+
"fallback.hint": "Command fallback: {command}",
|
|
406
|
+
"action.history": "History",
|
|
407
|
+
"action.copy": "Copy command",
|
|
408
|
+
"action.extract": "Extract",
|
|
409
|
+
"action.extract.reason": "The command/preview surface is not ready yet."
|
|
366
410
|
};
|
|
367
411
|
//#endregion
|
|
368
412
|
//#region src/client/styles.ts
|
|
@@ -435,7 +479,7 @@ window.__ModuleLoader__.load({
|
|
|
435
479
|
*/
|
|
436
480
|
/** Kinds in the order the summary reads them. */
|
|
437
481
|
const KINDS = [
|
|
438
|
-
"
|
|
482
|
+
"healthy",
|
|
439
483
|
"warn",
|
|
440
484
|
"needs-restructure",
|
|
441
485
|
"error",
|
|
@@ -463,15 +507,31 @@ window.__ModuleLoader__.load({
|
|
|
463
507
|
"needs-restructure",
|
|
464
508
|
"warn",
|
|
465
509
|
"unreadable",
|
|
466
|
-
"
|
|
510
|
+
"healthy"
|
|
467
511
|
];
|
|
468
|
-
let worst = "
|
|
512
|
+
let worst = "healthy";
|
|
469
513
|
for (const row of face.rows) {
|
|
470
514
|
if (!skills.includes(row.name)) continue;
|
|
471
515
|
if (order.indexOf(row.verdict) < order.indexOf(worst)) worst = row.verdict;
|
|
472
516
|
}
|
|
473
517
|
return worst;
|
|
474
518
|
}
|
|
519
|
+
/** H4: the evidence queue by skill, before the by-rule summary. */
|
|
520
|
+
function skillQueue(face, t) {
|
|
521
|
+
const order = [
|
|
522
|
+
"error",
|
|
523
|
+
"needs-restructure",
|
|
524
|
+
"warn",
|
|
525
|
+
"unreadable",
|
|
526
|
+
"healthy"
|
|
527
|
+
];
|
|
528
|
+
const rows = [...face.rows].sort((a, b) => order.indexOf(a.verdict) - order.indexOf(b.verdict) || a.name.localeCompare(b.name));
|
|
529
|
+
return (0, react.createElement)("div", { "data-role": "skill-queue" }, (0, react.createElement)("h3", { "data-role": "group-title" }, t("group.bySkill")), ...rows.map((row) => (0, react.createElement)("div", {
|
|
530
|
+
key: row.name,
|
|
531
|
+
"data-role": "skill-row",
|
|
532
|
+
"data-tone": row.verdict
|
|
533
|
+
}, (0, react.createElement)("span", { "data-role": "skill-name" }, row.name), (0, react.createElement)("span", { "data-role": "skill-verdict" }, t("verdict." + row.verdict)), (0, react.createElement)("span", { "data-role": "skill-findings" }, row.findings.length === 0 ? t("findings.none") : row.findings.map((finding) => finding.rule).join(" / ")), (0, react.createElement)("span", { "data-role": "skill-dims" }, t("dims.body") + ": " + String(row.dims?.bodyChars ?? t("dims.unknown")) + " · " + t("dims.support") + ": " + String(row.dims?.supportFiles ?? t("dims.unknown"))))));
|
|
534
|
+
}
|
|
475
535
|
/** The rule table: a grid so counts line up, with a tone bar per row and skill pills. */
|
|
476
536
|
function ruleTable(face, t) {
|
|
477
537
|
const rows = byRule(face.rows);
|
|
@@ -492,6 +552,31 @@ window.__ModuleLoader__.load({
|
|
|
492
552
|
title: name
|
|
493
553
|
}, name))), cell("rule-count", entry.count === 0 ? t("count.notCalibrated") : String(entry.count)))));
|
|
494
554
|
}
|
|
555
|
+
/** Per-skill actions: open history, copy the CLI command, and the disabled extract entry. */
|
|
556
|
+
function skillActions(face, t) {
|
|
557
|
+
const names = [...new Set(face.rows.map((row) => row.name))];
|
|
558
|
+
const copy = (name) => {
|
|
559
|
+
const text = "/evolution skill history " + name;
|
|
560
|
+
if (typeof navigator !== "undefined" && navigator.clipboard !== void 0) navigator.clipboard.writeText(text).catch(() => {});
|
|
561
|
+
};
|
|
562
|
+
return (0, react.createElement)("div", { "data-role": "skill-actions" }, ...names.map((name) => (0, react.createElement)("div", {
|
|
563
|
+
key: name,
|
|
564
|
+
"data-role": "skill-action-row"
|
|
565
|
+
}, (0, react.createElement)("span", { "data-role": "skill-action-name" }, name), (0, react.createElement)("button", {
|
|
566
|
+
type: "button",
|
|
567
|
+
"data-role": "action-history",
|
|
568
|
+
disabled: face.openHistory === void 0,
|
|
569
|
+
onClick: () => {
|
|
570
|
+
face.openHistory?.(name);
|
|
571
|
+
}
|
|
572
|
+
}, t("action.history")), (0, react.createElement)("button", {
|
|
573
|
+
type: "button",
|
|
574
|
+
"data-role": "action-copy",
|
|
575
|
+
onClick: () => {
|
|
576
|
+
copy(name);
|
|
577
|
+
}
|
|
578
|
+
}, t("action.copy")))));
|
|
579
|
+
}
|
|
495
580
|
/**
|
|
496
581
|
* Render the panel.
|
|
497
582
|
* @param props - the face (`{ rows, truncated, openHistory? }`) and the locale seat's translator.
|
|
@@ -520,49 +605,97 @@ window.__ModuleLoader__.load({
|
|
|
520
605
|
const t = face.t;
|
|
521
606
|
const [view, setView] = (0, react.useState)(face);
|
|
522
607
|
const [failed, setFailed] = (0, react.useState)(void 0);
|
|
523
|
-
(0, react.
|
|
524
|
-
|
|
525
|
-
|
|
608
|
+
const [loading, setLoading] = (0, react.useState)(face.loadRows !== void 0 && face.loaded !== true);
|
|
609
|
+
const [lastReadAt, setLastReadAt] = (0, react.useState)(face.readAt);
|
|
610
|
+
const [scan, setScan] = (0, react.useState)(face.scan);
|
|
611
|
+
/** One read trigger for first paint and the refresh button; the latest answer wins. */
|
|
612
|
+
const load = () => {
|
|
613
|
+
const read = face.loadRows;
|
|
614
|
+
if (read === void 0) return () => {};
|
|
526
615
|
let live = true;
|
|
527
|
-
|
|
616
|
+
setLoading(true);
|
|
617
|
+
read().then((outcome) => {
|
|
528
618
|
if (!live) return;
|
|
529
|
-
if (outcome.ok)
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
619
|
+
if (outcome.ok) {
|
|
620
|
+
const payload = outcome.payload;
|
|
621
|
+
setView({
|
|
622
|
+
...face,
|
|
623
|
+
rows: payload.rows,
|
|
624
|
+
truncated: payload.truncated,
|
|
625
|
+
proposals: payload.proposals ?? [],
|
|
626
|
+
loaded: true,
|
|
627
|
+
...payload.proposalStatus === void 0 ? {} : { proposalStatus: payload.proposalStatus }
|
|
628
|
+
});
|
|
629
|
+
setLastReadAt(payload.readAt);
|
|
630
|
+
setScan(payload.scan);
|
|
631
|
+
} else setFailed(outcome.failure);
|
|
538
632
|
}).catch(() => {
|
|
539
633
|
if (live) setFailed({
|
|
540
634
|
kind: "server",
|
|
541
635
|
status: 0
|
|
542
636
|
});
|
|
637
|
+
}).finally(() => {
|
|
638
|
+
if (live) setLoading(false);
|
|
543
639
|
});
|
|
544
640
|
return () => {
|
|
545
641
|
live = false;
|
|
546
642
|
};
|
|
547
|
-
}
|
|
643
|
+
};
|
|
644
|
+
(0, react.useEffect)(() => load(), [face]);
|
|
548
645
|
const counts = new Map(KINDS.map((kind) => [kind, view.rows.filter((row) => row.verdict === kind).length]));
|
|
549
646
|
const stats = (0, react.createElement)("div", { "data-role": "stats" }, KINDS.map((kind) => (0, react.createElement)("div", {
|
|
550
647
|
key: kind,
|
|
551
648
|
"data-role": "stat",
|
|
552
649
|
"data-tone": kind
|
|
553
650
|
}, (0, react.createElement)("div", { "data-role": "stat-value" }, String(counts.get(kind) ?? 0)), (0, react.createElement)("div", { "data-role": "stat-label" }, t("verdict." + kind)))));
|
|
554
|
-
return (0, react.createElement)("section", { className: CSS_TAG_ID }, (0, react.createElement)("
|
|
555
|
-
|
|
556
|
-
"data-role": "
|
|
557
|
-
|
|
651
|
+
return (0, react.createElement)("section", { className: CSS_TAG_ID }, (0, react.createElement)("div", { "data-role": "header" }, (0, react.createElement)("span", { "data-role": "read-at" }, lastReadAt === void 0 ? t("readAt.unknown") : new Date(lastReadAt).toLocaleString()), (0, react.createElement)("span", { "data-role": "scan-status" }, t("scan." + (scan?.status ?? "unavailable"))), (0, react.createElement)("button", {
|
|
652
|
+
type: "button",
|
|
653
|
+
"data-role": "refresh",
|
|
654
|
+
disabled: loading,
|
|
655
|
+
onClick: () => {
|
|
656
|
+
load();
|
|
657
|
+
}
|
|
658
|
+
}, t("refresh"))), (0, react.createElement)("p", { "data-role": "hint" }, t("hint")), stats, loading ? (0, react.createElement)("p", { "data-role": "note" }, t("loading")) : null, view.truncated ? (0, react.createElement)("p", { "data-role": "note" }, t("scan.truncated")) : null, failed === void 0 ? null : (0, react.createElement)("div", { "data-role": "note" }, failureBlock(t, failed)), view.loaded === true && view.rows.length === 0 ? (0, react.createElement)("p", { "data-role": "note" }, t("empty.noneReported")) : null, view.proposalStatus === "unavailable" && (view.proposals ?? []).length === 0 ? (0, react.createElement)("p", { "data-role": "note" }, t("proposal.unavailable")) : null, (view.proposals ?? []).length > 0 ? (0, react.createElement)("div", { "data-role": "proposals" }, (0, react.createElement)("h3", { "data-role": "group-title" }, t("group.proposals")), ...(view.proposals ?? []).map((proposal) => {
|
|
659
|
+
const heading = proposal.parts?.[0]?.heading;
|
|
660
|
+
const target = proposal.owner.mode === "covered" ? proposal.owner.name : proposal.owner.neededOwner;
|
|
661
|
+
const command = heading === void 0 || target === void 0 ? void 0 : "/evolution extract " + proposal.source + " " + target + " \"" + heading + "\" --plan";
|
|
662
|
+
const copyCommand = () => {
|
|
663
|
+
if (command !== void 0 && typeof navigator !== "undefined" && navigator.clipboard !== void 0) navigator.clipboard.writeText(command).catch(() => {});
|
|
664
|
+
};
|
|
665
|
+
return (0, react.createElement)("div", {
|
|
666
|
+
key: proposal.id,
|
|
667
|
+
"data-role": "proposal"
|
|
668
|
+
}, (0, react.createElement)("p", { "data-role": "proposal-summary" }, proposal.source + " — " + t("proposal.outcome." + proposal.outcome) + " · " + (proposal.owner.mode === "covered" ? t("proposal.owner.covered") + (proposal.owner.name === void 0 ? "" : ": " + proposal.owner.name) : t("proposal.owner.owner-gap") + (proposal.owner.neededOwner === void 0 ? "" : ": " + proposal.owner.neededOwner))), (0, react.createElement)("p", { "data-role": "proposal-status" }, t("proposal.status." + proposal.status)), proposal.provenance === void 0 ? null : (0, react.createElement)("p", { "data-role": "proposal-provenance" }, proposal.provenance.promptVersion + (proposal.provenance.model === void 0 ? "" : " · " + proposal.provenance.model) + " · " + proposal.provenance.runId + " · " + new Date(proposal.provenance.at).toLocaleString() + (proposal.provenance.promptHash === void 0 ? "" : " · #" + proposal.provenance.promptHash.slice(0, 8))), proposal.residual === void 0 || proposal.residual.trim() === "" ? null : (0, react.createElement)("p", { "data-role": "proposal-kept" }, t("proposal.kept") + ": " + proposal.residual.trim().slice(0, 160)), (proposal.parts ?? []).length === 0 ? null : (0, react.createElement)("p", { "data-role": "proposal-reusable" }, t("proposal.reusable") + ": " + (proposal.parts ?? []).map((part) => part.heading).join("、")), (proposal.warnings ?? []).length === 0 ? null : (0, react.createElement)("p", { "data-role": "proposal-warnings" }, t("proposal.warnings") + ": " + (proposal.warnings ?? []).join(";")), command === void 0 ? null : (0, react.createElement)("p", { "data-role": "proposal-command" }, (0, react.createElement)("code", {}, command), (0, react.createElement)("button", {
|
|
669
|
+
type: "button",
|
|
670
|
+
"data-role": "proposal-copy",
|
|
671
|
+
onClick: copyCommand
|
|
672
|
+
}, t("proposal.copy"))));
|
|
673
|
+
})) : null, skillQueue(view, t), (0, react.createElement)("h3", { "data-role": "group-title" }, t("group.byRule")), ruleTable(view, t), skillActions(view, t), view.rows.filter((row) => row.verdict === "unreadable").map((row) => (0, react.createElement)("p", {
|
|
558
674
|
key: row.name + ":unreadable",
|
|
559
675
|
"data-role": "note"
|
|
560
|
-
}, row.name + " — " + t("unreadable.note"))))
|
|
676
|
+
}, row.name + " — " + t("unreadable.note"))), view.rows.filter((row) => row.protection !== void 0 && row.protection !== null).map((row) => (0, react.createElement)("p", {
|
|
677
|
+
key: row.name + ":protected",
|
|
678
|
+
"data-role": "note"
|
|
679
|
+
}, row.name + " — " + t("protected"))));
|
|
680
|
+
}
|
|
681
|
+
//#endregion
|
|
682
|
+
//#region src/client/seats.ts
|
|
683
|
+
/** Fail loud when the injected client seats are absent, instead of leaving a PENDING row. */
|
|
684
|
+
function requireHealthSeats(ctx) {
|
|
685
|
+
if (ctx.slots === void 0) throw new Error("skill-health: the client slots seat is not mounted");
|
|
686
|
+
}
|
|
687
|
+
/** The one place the panel crosses into the platform layout seat. */
|
|
688
|
+
function openSkillHistory(ctx) {
|
|
689
|
+
ctx.layout.selectPanel("skill-history");
|
|
561
690
|
}
|
|
562
691
|
//#endregion
|
|
563
692
|
//#region src/client/index.ts
|
|
564
693
|
/** Required client services: the slot registry and the locale seat. */
|
|
565
|
-
const inject = [
|
|
694
|
+
const inject = [
|
|
695
|
+
"slots",
|
|
696
|
+
"locale",
|
|
697
|
+
"layout"
|
|
698
|
+
];
|
|
566
699
|
function PanelIcon(props = {}) {
|
|
567
700
|
const size = typeof props.size === "number" && props.size > 0 ? props.size : 16;
|
|
568
701
|
return (0, react.createElement)("svg", {
|
|
@@ -582,6 +715,7 @@ window.__ModuleLoader__.load({
|
|
|
582
715
|
* @param ctx - the client context; it must carry the two seats above.
|
|
583
716
|
*/
|
|
584
717
|
function apply(ctx) {
|
|
718
|
+
requireHealthSeats(ctx);
|
|
585
719
|
registerSidebarPanel(ctx, {
|
|
586
720
|
id: PANEL_ID,
|
|
587
721
|
order: 40,
|
|
@@ -603,6 +737,9 @@ window.__ModuleLoader__.load({
|
|
|
603
737
|
loaded: false,
|
|
604
738
|
proposalStatus: "unavailable",
|
|
605
739
|
loadRows: rows,
|
|
740
|
+
openHistory: () => {
|
|
741
|
+
openSkillHistory(ctx);
|
|
742
|
+
},
|
|
606
743
|
t
|
|
607
744
|
})
|
|
608
745
|
});
|
package/lib/index.js
CHANGED
|
@@ -32,6 +32,8 @@ function toRouteRows(rows) {
|
|
|
32
32
|
...finding.severity === void 0 ? {} : { severity: finding.severity },
|
|
33
33
|
...finding.message === void 0 ? {} : { message: finding.message }
|
|
34
34
|
})),
|
|
35
|
+
...row.protection === void 0 || row.protection === null ? {} : { protection: row.protection },
|
|
36
|
+
...row.hardCapHits === void 0 ? {} : { hardCapHits: row.hardCapHits },
|
|
35
37
|
...row.dims === void 0 || row.dims === null ? {} : { dims: {
|
|
36
38
|
bodyChars: row.dims.bodyChars ?? null,
|
|
37
39
|
bodyTokens: row.dims.bodyTokens ?? null,
|
|
@@ -32,9 +32,13 @@ export interface HealthDims {
|
|
|
32
32
|
/** One skill row. `readable: false` is its own verdict — never folded into `ok`. */
|
|
33
33
|
export interface HealthRow {
|
|
34
34
|
name: string;
|
|
35
|
-
verdict: '
|
|
35
|
+
verdict: 'healthy' | 'warn' | 'error' | 'unreadable' | 'needs-restructure';
|
|
36
36
|
findings: HealthFinding[];
|
|
37
37
|
dims?: HealthDims | null;
|
|
38
|
+
/** Non-null when a protection marker owns the skill. */
|
|
39
|
+
protection?: string | null;
|
|
40
|
+
/** Mechanical hard-cap hits; null = event log unreadable, never 0-as-unknown. */
|
|
41
|
+
hardCapHits?: number | null;
|
|
38
42
|
}
|
|
39
43
|
/** One semantic proposal as the health lane projects it; never generated by this panel. */
|
|
40
44
|
export interface HealthProposal {
|
|
@@ -47,6 +51,18 @@ export interface HealthProposal {
|
|
|
47
51
|
neededOwner?: string;
|
|
48
52
|
};
|
|
49
53
|
status: 'proposed' | 'previewed' | 'stale' | 'dismissed';
|
|
54
|
+
parts?: Array<{
|
|
55
|
+
heading: string;
|
|
56
|
+
}>;
|
|
57
|
+
residual?: string;
|
|
58
|
+
warnings?: string[];
|
|
59
|
+
provenance?: {
|
|
60
|
+
promptVersion: string;
|
|
61
|
+
promptHash?: string;
|
|
62
|
+
model?: string;
|
|
63
|
+
runId: string;
|
|
64
|
+
at: number;
|
|
65
|
+
};
|
|
50
66
|
}
|
|
51
67
|
/** What the sidebar hands the component. */
|
|
52
68
|
export interface HealthFace {
|
|
@@ -63,6 +79,11 @@ export interface HealthFace {
|
|
|
63
79
|
loaded?: boolean;
|
|
64
80
|
/** Semantic proposals are a separate lane; unavailable must not read as "nothing to extract". */
|
|
65
81
|
proposalStatus?: 'available' | 'unavailable' | 'stale';
|
|
82
|
+
/** Host scan time and status, when the route supplied them. */
|
|
83
|
+
readAt?: number;
|
|
84
|
+
scan?: {
|
|
85
|
+
status: 'complete' | 'unavailable' | 'truncated';
|
|
86
|
+
};
|
|
66
87
|
/** The seat's translator for this bundle's dictionary — the panel never reaches for a service. */
|
|
67
88
|
t: (key: string) => string;
|
|
68
89
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Context as ClientContext } from '@deepseek-ai/cordis';
|
|
2
|
+
/** Fail loud when the injected client seats are absent, instead of leaving a PENDING row. */
|
|
3
|
+
export declare function requireHealthSeats(ctx: ClientContext): void;
|
|
4
|
+
/** The one place the panel crosses into the platform layout seat. */
|
|
5
|
+
export declare function openSkillHistory(ctx: ClientContext): void;
|
|
6
|
+
//# sourceMappingURL=seats.d.ts.map
|
package/lib/types/routes.d.ts
CHANGED
|
@@ -25,12 +25,16 @@ export interface RouteDims {
|
|
|
25
25
|
churnReads: number | null;
|
|
26
26
|
stampDensityPerKb: number | null;
|
|
27
27
|
}
|
|
28
|
-
/** One row as the client sees it. `readable: false` becomes its own verdict, never `
|
|
28
|
+
/** One row as the client sees it. `readable: false` becomes its own verdict, never `healthy`. */
|
|
29
29
|
export interface RouteRow {
|
|
30
30
|
name: string;
|
|
31
31
|
verdict: string;
|
|
32
32
|
findings: RouteFinding[];
|
|
33
33
|
dims?: RouteDims | null;
|
|
34
|
+
/** Non-null when a protection marker owns the skill; the panel marks the row. */
|
|
35
|
+
protection?: string | null;
|
|
36
|
+
/** Mechanical hard-cap-hit count; null when the event log was unreadable. */
|
|
37
|
+
hardCapHits?: number | null;
|
|
34
38
|
}
|
|
35
39
|
/**
|
|
36
40
|
* Project the curator's health view into what the panel renders.
|
|
@@ -50,6 +54,8 @@ export declare function toRouteRows(rows: ReadonlyArray<{
|
|
|
50
54
|
dims?: (Partial<RouteDims> & {
|
|
51
55
|
readable?: boolean;
|
|
52
56
|
}) | null;
|
|
57
|
+
protection?: string | null;
|
|
58
|
+
hardCapHits?: number | null;
|
|
53
59
|
}>): RouteRow[];
|
|
54
60
|
/**
|
|
55
61
|
* Build the panel's route in the platform's exact FetchRoute shape.
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
]
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
|
-
"version": "0.30.
|
|
14
|
+
"version": "0.30.38",
|
|
15
15
|
"publishConfig": {
|
|
16
16
|
"access": "public"
|
|
17
17
|
},
|
|
@@ -42,18 +42,18 @@
|
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"@deepseek-ai/cordis": "^4.0.5",
|
|
44
44
|
"react": "^18.2.0",
|
|
45
|
-
"@lmzhen/dsh-evolution-core": "^0.30.
|
|
46
|
-
"@lmzhen/dsh-evolution-curator": "^0.30.
|
|
45
|
+
"@lmzhen/dsh-evolution-core": "^0.30.38",
|
|
46
|
+
"@lmzhen/dsh-evolution-curator": "^0.30.38"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@deepseek-ai/cordis": "^4.0.5",
|
|
50
50
|
"@types/react": "~18.3.1",
|
|
51
51
|
"react": "^18.2.0",
|
|
52
|
-
"@lmzhen/dsh-evolution-core": "^0.30.
|
|
53
|
-
"@lmzhen/dsh-evolution-curator": "^0.30.
|
|
52
|
+
"@lmzhen/dsh-evolution-core": "^0.30.38",
|
|
53
|
+
"@lmzhen/dsh-evolution-curator": "^0.30.38"
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@lmzhen/dsh-evolution-client-kit": "^0.30.
|
|
56
|
+
"@lmzhen/dsh-evolution-client-kit": "^0.30.38"
|
|
57
57
|
},
|
|
58
58
|
"engines": {
|
|
59
59
|
"dsh": ">=0.2.1-rc.2 <0.2.2-0"
|