dsh-context-compression-improved 0.5.0 → 0.5.2

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.
Files changed (50) hide show
  1. package/CHANGELOG.ja.md +51 -0
  2. package/CHANGELOG.ko.md +51 -0
  3. package/CHANGELOG.md +55 -0
  4. package/CHANGELOG.zh.md +45 -0
  5. package/package.json +1 -1
  6. package/packages/selector/lib/advisor-state.js +4 -231
  7. package/packages/selector/lib/client.d.ts +0 -24
  8. package/packages/selector/lib/client.js +6 -501
  9. package/packages/selector/lib/index.d.ts +4 -10
  10. package/packages/selector/lib/index.js +65 -235
  11. package/packages/selector/lib/pruner.d.ts +13 -248
  12. package/packages/selector/lib/pruner.js +148 -552
  13. package/packages/selector/src/client/EstimatorControls.tsx +277 -378
  14. package/packages/selector/src/client/index.ts +0 -17
  15. package/packages/selector/src/client/locales.ts +196 -234
  16. package/packages/selector/src/client/preset-options.ts +3 -2
  17. package/packages/selector/src/client/settings-section.tsx +8 -17
  18. package/packages/selector/src/index.ts +463 -710
  19. package/packages/selector/src/preset-overlay.ts +60 -1
  20. package/packages/selector/src/profiles.ts +4 -27
  21. package/packages/selector/src/pruner/state.ts +50 -73
  22. package/packages/selector/src/pruner.ts +2402 -2730
  23. package/packages/selector/src/runtime/audit.ts +27 -21
  24. package/packages/selector/src/runtime/config.ts +6 -32
  25. package/packages/selector/src/runtime/tokenpilot/advisor-state.ts +16 -0
  26. package/packages/selector/src/runtime/tokenpilot/benefit.ts +200 -0
  27. package/packages/selector/src/runtime/types.ts +0 -17
  28. package/packages/selector/tests/built/client-artifact.spec.ts +9 -5
  29. package/packages/selector/tests/preset-options-write.client.spec.ts +7 -23
  30. package/packages/selector/tests/runtime/advice-never-withholds.host.spec.ts +232 -0
  31. package/packages/selector/tests/runtime/audit.spec.ts +35 -21
  32. package/packages/selector/tests/runtime/deprecated-preset-options.spec.ts +96 -0
  33. package/packages/selector/tests/runtime/tokenpilot/benefit.spec.ts +217 -0
  34. package/packages/selector/tests/runtime/tokenpilot/profile-baseline.spec.ts +4 -5
  35. package/packages/selector/tests/settings-seat.client.spec.ts +16 -10
  36. package/packages/selector/tests/standing-generation.host.spec.ts +54 -5
  37. package/scripts/packed-components-smoke.mjs +30 -8
  38. package/scripts/packed-install-e2e.mjs +69 -15
  39. package/packages/selector/src/client/ReviewOverlay.tsx +0 -320
  40. package/packages/selector/src/client/review-scope.ts +0 -16
  41. package/packages/selector/src/runtime/tokenpilot/proposal.ts +0 -267
  42. package/packages/selector/src/runtime/tokenpilot/review-queue.ts +0 -231
  43. package/packages/selector/src/runtime/tokenpilot/review-registry.ts +0 -117
  44. package/packages/selector/src/runtime/tokenpilot/review-storage.ts +0 -122
  45. package/packages/selector/tests/review-overlay.client.spec.tsx +0 -118
  46. package/packages/selector/tests/review-routes-registry.host.spec.ts +0 -142
  47. package/packages/selector/tests/review-routes.host.spec.ts +0 -290
  48. package/packages/selector/tests/runtime/tokenpilot/proposal.spec.ts +0 -393
  49. package/packages/selector/tests/runtime/tokenpilot/pruner-review.spec.ts +0 -382
  50. package/packages/selector/tests/runtime/tokenpilot/review-queue.spec.ts +0 -168
@@ -95,8 +95,7 @@ window.__ModuleLoader__.load({
95
95
  "dedupeToolResults",
96
96
  "summaryLocator",
97
97
  "prefixStabilizer",
98
- "readState",
99
- "reviewMode"
98
+ "readState"
100
99
  ]) {
101
100
  const entry = value[key];
102
101
  if (entry !== void 0 && typeof entry !== "boolean") return void 0;
@@ -116,12 +115,6 @@ window.__ModuleLoader__.load({
116
115
  }
117
116
  const estimatorTimeoutMs = value.estimatorTimeoutMs;
118
117
  if (estimatorTimeoutMs !== void 0 && (typeof estimatorTimeoutMs !== "number" || !Number.isSafeInteger(estimatorTimeoutMs) || estimatorTimeoutMs < 100 || estimatorTimeoutMs > 6e4)) return;
119
- const reviewTimeoutTurns = value.reviewTimeoutTurns;
120
- if (reviewTimeoutTurns !== void 0 && (typeof reviewTimeoutTurns !== "number" || !Number.isSafeInteger(reviewTimeoutTurns) || reviewTimeoutTurns < 1)) return;
121
- const cacheHitDiscountAlpha = value.cacheHitDiscountAlpha;
122
- if (cacheHitDiscountAlpha !== void 0 && (typeof cacheHitDiscountAlpha !== "number" || !Number.isFinite(cacheHitDiscountAlpha) || cacheHitDiscountAlpha <= 0 || cacheHitDiscountAlpha >= 1)) return;
123
- const reviewHighImpactTokens = value.reviewHighImpactTokens;
124
- if (reviewHighImpactTokens !== void 0 && (typeof reviewHighImpactTokens !== "number" || !Number.isSafeInteger(reviewHighImpactTokens) || reviewHighImpactTokens < 0)) return;
125
118
  const advisorTimeoutMs = value.advisorTimeoutMs;
126
119
  if (advisorTimeoutMs !== void 0 && (typeof advisorTimeoutMs !== "number" || !Number.isSafeInteger(advisorTimeoutMs) || advisorTimeoutMs < 100 || advisorTimeoutMs > 6e4)) return;
127
120
  const advisorRefreshTurns = value.advisorRefreshTurns;
@@ -143,10 +136,6 @@ window.__ModuleLoader__.load({
143
136
  if (value.estimatorBaseUrl !== void 0) decoded.estimatorBaseUrl = value.estimatorBaseUrl;
144
137
  if (value.estimatorApiKey !== void 0) decoded.estimatorApiKey = value.estimatorApiKey;
145
138
  if (estimatorTimeoutMs !== void 0) decoded.estimatorTimeoutMs = estimatorTimeoutMs;
146
- if (value.reviewMode !== void 0) decoded.reviewMode = value.reviewMode;
147
- if (reviewTimeoutTurns !== void 0) decoded.reviewTimeoutTurns = reviewTimeoutTurns;
148
- if (cacheHitDiscountAlpha !== void 0) decoded.cacheHitDiscountAlpha = cacheHitDiscountAlpha;
149
- if (reviewHighImpactTokens !== void 0) decoded.reviewHighImpactTokens = reviewHighImpactTokens;
150
139
  if (advisorMode !== void 0) decoded.advisorMode = advisorMode;
151
140
  if (advisorTimeoutMs !== void 0) decoded.advisorTimeoutMs = advisorTimeoutMs;
152
141
  if (advisorRefreshTurns !== void 0) decoded.advisorRefreshTurns = advisorRefreshTurns;
@@ -1034,123 +1023,6 @@ window.__ModuleLoader__.load({
1034
1023
  ]
1035
1024
  });
1036
1025
  }
1037
- /**
1038
- * TokenPilot-inspired review-mode card (beta). When enabled, edge/high-impact
1039
- * reduction candidates queue for manual approval and execute in one merged
1040
- * batch at the next turn boundary; the numeric fields tune the benefit model.
1041
- * Numeric drafts commit on blur and only when they parse to a value the
1042
- * runtime schema accepts, so an invalid keystroke never disables the panel.
1043
- */
1044
- function ReviewModeControls({ options, disabled, save, settle, t }) {
1045
- const [turnsDraft, setTurnsDraft] = (0, react.useState)(String(options.reviewTimeoutTurns ?? 6));
1046
- const [alphaDraft, setAlphaDraft] = (0, react.useState)(String(options.cacheHitDiscountAlpha ?? .1));
1047
- const [highImpactDraft, setHighImpactDraft] = (0, react.useState)(String(options.reviewHighImpactTokens ?? 4e3));
1048
- const reviewMode = options.reviewMode ?? false;
1049
- const commit = (patch) => {
1050
- settle(() => save(patch));
1051
- };
1052
- const commitTurns = () => {
1053
- const next = Number(turnsDraft);
1054
- if (!Number.isSafeInteger(next) || next < 1 || next === (options.reviewTimeoutTurns ?? 6)) return;
1055
- commit({ reviewTimeoutTurns: next });
1056
- };
1057
- const commitAlpha = () => {
1058
- const next = Number(alphaDraft);
1059
- if (!Number.isFinite(next) || next <= 0 || next >= 1 || next === (options.cacheHitDiscountAlpha ?? .1)) return;
1060
- commit({ cacheHitDiscountAlpha: next });
1061
- };
1062
- const commitHighImpact = () => {
1063
- const next = Number(highImpactDraft);
1064
- if (!Number.isSafeInteger(next) || next < 0 || next === (options.reviewHighImpactTokens ?? 4e3)) return;
1065
- commit({ reviewHighImpactTokens: next });
1066
- };
1067
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1068
- className: _dsh_context_compression_css_466eb745356d_CompressionProfileSelector_module_css_default.autoCompact,
1069
- "aria-labelledby": "context-compression-review-title",
1070
- children: [
1071
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
1072
- id: "context-compression-review-title",
1073
- className: _dsh_context_compression_css_466eb745356d_CompressionProfileSelector_module_css_default.autoCompactTitle,
1074
- children: t("review.title")
1075
- }),
1076
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1077
- className: _dsh_context_compression_css_466eb745356d_CompressionProfileSelector_module_css_default.customNote,
1078
- children: t("review.description")
1079
- }),
1080
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1081
- className: _dsh_context_compression_css_466eb745356d_CompressionProfileSelector_module_css_default.field,
1082
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("review.enabled") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
1083
- value: reviewMode ? "on" : "off",
1084
- disabled,
1085
- onChange: (event) => {
1086
- settle(() => save({ reviewMode: event.currentTarget.value === "on" }));
1087
- },
1088
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1089
- value: "off",
1090
- children: t("review.enabled.off")
1091
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1092
- value: "on",
1093
- children: t("review.enabled.on")
1094
- })]
1095
- })]
1096
- }),
1097
- reviewMode ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
1098
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1099
- className: _dsh_context_compression_css_466eb745356d_CompressionProfileSelector_module_css_default.field,
1100
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("review.timeoutTurns") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1101
- type: "number",
1102
- min: 1,
1103
- step: 1,
1104
- value: turnsDraft,
1105
- disabled,
1106
- onChange: (event) => {
1107
- setTurnsDraft(event.currentTarget.value);
1108
- },
1109
- onBlur: commitTurns,
1110
- onKeyDown: (event) => {
1111
- if (event.key === "Enter") event.currentTarget.blur();
1112
- }
1113
- })]
1114
- }),
1115
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1116
- className: _dsh_context_compression_css_466eb745356d_CompressionProfileSelector_module_css_default.field,
1117
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("review.alpha") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1118
- type: "number",
1119
- min: .01,
1120
- max: .99,
1121
- step: .05,
1122
- value: alphaDraft,
1123
- disabled,
1124
- onChange: (event) => {
1125
- setAlphaDraft(event.currentTarget.value);
1126
- },
1127
- onBlur: commitAlpha,
1128
- onKeyDown: (event) => {
1129
- if (event.key === "Enter") event.currentTarget.blur();
1130
- }
1131
- })]
1132
- }),
1133
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1134
- className: _dsh_context_compression_css_466eb745356d_CompressionProfileSelector_module_css_default.field,
1135
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("review.highImpact") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1136
- type: "number",
1137
- min: 0,
1138
- step: 500,
1139
- value: highImpactDraft,
1140
- disabled,
1141
- onChange: (event) => {
1142
- setHighImpactDraft(event.currentTarget.value);
1143
- },
1144
- onBlur: commitHighImpact,
1145
- onKeyDown: (event) => {
1146
- if (event.key === "Enter") event.currentTarget.blur();
1147
- }
1148
- })]
1149
- })
1150
- ] }) : null
1151
- ]
1152
- });
1153
- }
1154
1026
  //#endregion
1155
1027
  //#region src/client/settings-section.tsx
1156
1028
  /**
@@ -1254,19 +1126,13 @@ window.__ModuleLoader__.load({
1254
1126
  current !== "tokenpilot-inspired" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EstimatorInactiveNotice, {
1255
1127
  profile: t(`profile.${current}`),
1256
1128
  t
1257
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(EstimatorControls, {
1258
- options: state.value?.presetOptions ?? {},
1259
- disabled: busy || !state.writable || false,
1260
- save: savePresetOptions,
1261
- settle,
1262
- t
1263
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReviewModeControls, {
1129
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EstimatorControls, {
1264
1130
  options: state.value?.presetOptions ?? {},
1265
1131
  disabled: busy || !state.writable || false,
1266
1132
  save: savePresetOptions,
1267
1133
  settle,
1268
1134
  t
1269
- })] }),
1135
+ }),
1270
1136
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1271
1137
  className: _dsh_context_compression_css_466eb745356d_CompressionProfileSelector_module_css_default.pricing,
1272
1138
  children: t("pricing.disclosure")
@@ -1356,25 +1222,6 @@ window.__ModuleLoader__.load({
1356
1222
  "estimator.apiKey.set": "已设置 · 输入新值覆盖",
1357
1223
  "estimator.apiKey.clear": "清除",
1358
1224
  "estimator.apiKey.overwrite": "已设置保密值,输入新值并失焦即可覆盖。",
1359
- "review.title": "人工审查(beta)",
1360
- "review.description": "开启后,边缘区间与高影响的压缩候选不再自动执行,而是进入待审队列并在你批准后的下一个回合边界批量执行;未处理的提案超过过期轮数后自动作废。仅随「TokenPilot 启发模式」提供,默认关闭。",
1361
- "review.enabled": "审查模式",
1362
- "review.enabled.on": "开(提案等待人工批准)",
1363
- "review.enabled.off": "关(默认,全自动)",
1364
- "review.timeoutTurns": "提案过期轮数",
1365
- "review.alpha": "缓存命中折扣 α",
1366
- "review.highImpact": "高影响门槛(tokens)",
1367
- "review.badge": "待审",
1368
- "review.summary.autoApplied": "自动应用",
1369
- "review.summary.reviewApplied": "审查应用",
1370
- "review.summary.expired": "已过期",
1371
- "review.summary.voided": "已作废",
1372
- "review.row.payback": "回本轮数",
1373
- "review.row.expectedSaving": "预期节省",
1374
- "review.row.estimated": "估计",
1375
- "review.action.approve": "批准",
1376
- "review.action.reject": "驳回",
1377
- "review.action.ignore": "本会话忽略",
1378
1225
  "detail.tokenpilot-inspired": "在平衡模式之上叠加去重指针、恢复豁免、摘要定位块、前缀稳定与读取状态语义;估计器需另行配置端点",
1379
1226
  "profile.custom": "Custom/实验模式",
1380
1227
  "profile.native": "原生对照",
@@ -1471,25 +1318,6 @@ window.__ModuleLoader__.load({
1471
1318
  "estimator.apiKey.set": "Set · type a new value to overwrite",
1472
1319
  "estimator.apiKey.clear": "Clear",
1473
1320
  "estimator.apiKey.overwrite": "A secret is stored; type a new value and blur to overwrite it.",
1474
- "review.title": "Review mode (beta)",
1475
- "review.description": "When enabled, edge-band and high-impact reduction candidates no longer apply automatically: they queue for manual approval and execute in one merged batch at the next turn boundary after approval. Unhandled proposals expire after the configured number of turns. Ships with the TokenPilot-inspired profile only, off by default.",
1476
- "review.enabled": "Review mode",
1477
- "review.enabled.on": "On (proposals wait for manual approval)",
1478
- "review.enabled.off": "Off (default, fully automatic)",
1479
- "review.timeoutTurns": "Proposal expiry (turns)",
1480
- "review.alpha": "Cache-hit discount α",
1481
- "review.highImpact": "High-impact threshold (tokens)",
1482
- "review.badge": "Review",
1483
- "review.summary.autoApplied": "Auto-applied",
1484
- "review.summary.reviewApplied": "Review-applied",
1485
- "review.summary.expired": "Expired",
1486
- "review.summary.voided": "Voided",
1487
- "review.row.payback": "Payback",
1488
- "review.row.expectedSaving": "Expected saving",
1489
- "review.row.estimated": "estimated",
1490
- "review.action.approve": "Approve",
1491
- "review.action.reject": "Reject",
1492
- "review.action.ignore": "Ignore",
1493
1321
  "detail.tokenpilot-inspired": "Layered on Balanced: dedupe pointers, recovery exemption, summary locators, prefix stabilization, and read-state semantics; the estimator needs an endpoint configured separately",
1494
1322
  "profile.custom": "Custom / Experimental",
1495
1323
  "profile.native": "Native baseline",
@@ -1553,7 +1381,9 @@ window.__ModuleLoader__.load({
1553
1381
  //#region src/client/preset-options.ts
1554
1382
  /** The namespace key holding every tokenpilot-inspired sub-capability override. */
1555
1383
  const PRESET_OPTIONS_KEY = "presetOptions";
1556
- /** Every field a patch may address, in the schema's own order. */
1384
+ /** Every field a patch may address, in the schema's own order. The retired
1385
+ * review-gate keys are deliberately absent: nothing writes them any more, and
1386
+ * a stored document that still carries them is tolerated by the decoders. */
1557
1387
  const PRESET_OPTION_KEYS = [
1558
1388
  "dedupeToolResults",
1559
1389
  "summaryLocator",
@@ -1565,10 +1395,6 @@ window.__ModuleLoader__.load({
1565
1395
  "estimatorBaseUrl",
1566
1396
  "estimatorApiKey",
1567
1397
  "estimatorTimeoutMs",
1568
- "reviewMode",
1569
- "reviewTimeoutTurns",
1570
- "cacheHitDiscountAlpha",
1571
- "reviewHighImpactTokens",
1572
1398
  "advisorMode",
1573
1399
  "advisorTimeoutMs",
1574
1400
  "advisorRefreshTurns",
@@ -1628,314 +1454,6 @@ window.__ModuleLoader__.load({
1628
1454
  });
1629
1455
  }
1630
1456
  //#endregion
1631
- //#region src/client/ReviewOverlay.tsx
1632
- /**
1633
- * TokenPilot-inspired R4: the review floating window.
1634
- *
1635
- * Mounted on the host `shell.overlay` slot (dsh-tidychat precedent: the layer
1636
- * is click-through by default and only the card opts back in), showing a
1637
- * bottom-right badge while any session has pending proposals and a card with
1638
- * the four-state summary row plus one row per proposal. Every 10s it polls the
1639
- * review-queue route; when the queue is empty or review mode is off the
1640
- * component renders null, so it never disturbs the session.
1641
- *
1642
- * Styles carry the `dsh-cc-review-` prefix and ride a one-shot style tag.
1643
- */
1644
- const QUEUE_ROUTES = ["/api/dsh-context-compression-improved/review-queue", "/endpoint/dsh-context-compression-improved/review-queue"];
1645
- const DECIDE_ROUTES = ["/api/dsh-context-compression-improved/review-decide", "/endpoint/dsh-context-compression-improved/review-decide"];
1646
- const CSS = `
1647
- .dsh-cc-review-badge {
1648
- position: fixed;
1649
- right: 20px;
1650
- bottom: 20px;
1651
- z-index: 70;
1652
- pointer-events: auto;
1653
- box-sizing: border-box;
1654
- min-width: 34px;
1655
- height: 34px;
1656
- padding: 0 10px;
1657
- border-radius: 17px;
1658
- border: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.4));
1659
- background: var(--dsw-alias-bg-layer-3, #fff);
1660
- color: var(--dsw-alias-label-primary, #222);
1661
- font-size: 13px;
1662
- display: flex;
1663
- align-items: center;
1664
- justify-content: center;
1665
- gap: 6px;
1666
- cursor: pointer;
1667
- box-shadow: 0 6px 18px rgba(0, 0, 0, 0.14);
1668
- }
1669
- .dsh-cc-review-card {
1670
- position: fixed;
1671
- right: 20px;
1672
- bottom: 62px;
1673
- z-index: 70;
1674
- pointer-events: auto;
1675
- box-sizing: border-box;
1676
- width: min(420px, calc(100vw - 40px));
1677
- max-height: min(60vh, 520px);
1678
- overflow: auto;
1679
- background: var(--dsw-alias-bg-layer-3, #fff);
1680
- border: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.4));
1681
- border-radius: 12px;
1682
- box-shadow: 0 12px 32px rgba(0, 0, 0, 0.18);
1683
- padding: 12px 14px;
1684
- color: var(--dsw-alias-label-primary, #222);
1685
- font-size: 13px;
1686
- }
1687
- .dsh-cc-review-title {
1688
- font-weight: 600;
1689
- margin: 0 0 6px;
1690
- font-size: 13px;
1691
- }
1692
- .dsh-cc-review-summary {
1693
- display: flex;
1694
- flex-wrap: wrap;
1695
- gap: 4px 12px;
1696
- color: var(--dsw-alias-label-tertiary, #888);
1697
- font-size: 12px;
1698
- margin-bottom: 8px;
1699
- }
1700
- .dsh-cc-review-row {
1701
- border-top: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.25));
1702
- padding: 8px 0;
1703
- }
1704
- .dsh-cc-review-row-meta {
1705
- color: var(--dsw-alias-label-tertiary, #888);
1706
- font-size: 12px;
1707
- margin-bottom: 4px;
1708
- }
1709
- .dsh-cc-review-actions {
1710
- display: flex;
1711
- gap: 8px;
1712
- }
1713
- .dsh-cc-review-btn {
1714
- appearance: none;
1715
- border: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.4));
1716
- background: transparent;
1717
- color: inherit;
1718
- border-radius: 6px;
1719
- padding: 3px 10px;
1720
- font-size: 12px;
1721
- cursor: pointer;
1722
- }
1723
- .dsh-cc-review-btn-primary {
1724
- background: var(--dsw-alias-state-business-primary, #3b82f6);
1725
- border-color: transparent;
1726
- color: #fff;
1727
- }
1728
- `;
1729
- function injectOnce() {
1730
- const tag = document.createElement("style");
1731
- tag.setAttribute("data-plugin-css", "dsh-context-compression-improved-review");
1732
- tag.textContent = CSS;
1733
- document.head.appendChild(tag);
1734
- return () => {
1735
- tag.remove();
1736
- };
1737
- }
1738
- async function fetchJson(route, init) {
1739
- const response = await fetch(route, {
1740
- headers: { "cache-control": "no-cache" },
1741
- ...init
1742
- });
1743
- if (!response.ok) return void 0;
1744
- return response.json();
1745
- }
1746
- async function pollQueue() {
1747
- for (const route of QUEUE_ROUTES) {
1748
- const body = await fetchJson(route);
1749
- if (body?.ok === true) return {
1750
- pending: body.pending ?? [],
1751
- summary: body.summary
1752
- };
1753
- }
1754
- }
1755
- async function postDecide(proposal, decision) {
1756
- for (const route of DECIDE_ROUTES) try {
1757
- const response = await fetch(route, {
1758
- method: "POST",
1759
- headers: { "content-type": "application/json" },
1760
- body: JSON.stringify({
1761
- sessionId: proposal.sessionId,
1762
- proposalId: proposal.id,
1763
- decision
1764
- })
1765
- });
1766
- if (response.status !== 404) return response.ok;
1767
- } catch {}
1768
- return false;
1769
- }
1770
- /**
1771
- * Slot factory helper: the client entry is a .ts file and cannot carry JSX,
1772
- * so the element construction lives here.
1773
- */
1774
- function renderReviewOverlay(scope, t) {
1775
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReviewOverlay, {
1776
- scope,
1777
- t
1778
- });
1779
- }
1780
- /**
1781
- * The floating window itself: renders null (and stays silent) while review
1782
- * mode is off or nothing is pending.
1783
- */
1784
- function ReviewOverlay({ scope, t }) {
1785
- const [reviewMode, setReviewMode] = (0, react.useState)(false);
1786
- const [pending, setPending] = (0, react.useState)([]);
1787
- const [summary, setSummary] = (0, react.useState)();
1788
- const [expanded, setExpanded] = (0, react.useState)(false);
1789
- const [busy, setBusy] = (0, react.useState)(false);
1790
- (0, react.useEffect)(injectOnce, []);
1791
- (0, react.useEffect)(() => {
1792
- const pull = () => {
1793
- try {
1794
- const snapshot = scope.getSnapshot();
1795
- setReviewMode(snapshot.status === "ready" && snapshot.value?.presetOptions?.reviewMode === true);
1796
- } catch {
1797
- setReviewMode(false);
1798
- }
1799
- };
1800
- pull();
1801
- let unsubscribe;
1802
- try {
1803
- unsubscribe = scope.subscribe(pull);
1804
- } catch {
1805
- unsubscribe = () => {};
1806
- }
1807
- return unsubscribe;
1808
- }, [scope]);
1809
- (0, react.useEffect)(() => {
1810
- if (!reviewMode) return;
1811
- let alive = true;
1812
- let timer;
1813
- const tick = () => {
1814
- pollQueue().then((result) => {
1815
- if (!alive) return;
1816
- setPending(result?.pending ?? []);
1817
- setSummary(result?.summary);
1818
- timer = setTimeout(tick, 1e4);
1819
- });
1820
- };
1821
- tick();
1822
- return () => {
1823
- alive = false;
1824
- if (timer !== void 0) clearTimeout(timer);
1825
- };
1826
- }, [reviewMode]);
1827
- if (!reviewMode || pending.length === 0) return null;
1828
- const decide = (proposal, decision) => {
1829
- setBusy(true);
1830
- postDecide(proposal, decision).then(() => {
1831
- return pollQueue().then((result) => {
1832
- setPending(result?.pending ?? []);
1833
- setSummary(result?.summary);
1834
- setBusy(false);
1835
- });
1836
- }).catch(() => {
1837
- setBusy(false);
1838
- });
1839
- };
1840
- const seqRange = (proposal) => {
1841
- const seqs = proposal.items.map((item) => item.seq);
1842
- const min = Math.min(...seqs);
1843
- const max = Math.max(...seqs);
1844
- return min === max ? `#${String(min)}` : `#${String(min)}–#${String(max)}`;
1845
- };
1846
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [expanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1847
- className: "dsh-cc-review-card",
1848
- children: [
1849
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1850
- className: "dsh-cc-review-title",
1851
- children: t("review.title")
1852
- }),
1853
- summary !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1854
- className: "dsh-cc-review-summary",
1855
- children: [
1856
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
1857
- t("review.summary.autoApplied"),
1858
- ": ",
1859
- String(summary.autoApplied)
1860
- ] }),
1861
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
1862
- t("review.summary.reviewApplied"),
1863
- ": ",
1864
- String(summary.reviewApplied)
1865
- ] }),
1866
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
1867
- t("review.summary.expired"),
1868
- ": ",
1869
- String(summary.expired)
1870
- ] }),
1871
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
1872
- t("review.summary.voided"),
1873
- ": ",
1874
- String(summary.voided)
1875
- ] })
1876
- ]
1877
- }) : null,
1878
- pending.map((proposal) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1879
- className: "dsh-cc-review-row",
1880
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1881
- className: "dsh-cc-review-row-meta",
1882
- children: [
1883
- proposal.kind,
1884
- " · ",
1885
- seqRange(proposal),
1886
- " · R ≈ ",
1887
- String(proposal.benefit.recoveredTokens),
1888
- proposal.benefit.paybackTurns !== void 0 ? ` · ${t("review.row.payback")}: ${String(Math.round(proposal.benefit.paybackTurns * 100) / 100)}` : "",
1889
- proposal.benefit.expectedSaving !== void 0 ? ` · ${t("review.row.expectedSaving")}: ${String(Math.round(proposal.benefit.expectedSaving))} (${t("review.row.estimated")})` : ""
1890
- ]
1891
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1892
- className: "dsh-cc-review-actions",
1893
- children: [
1894
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1895
- type: "button",
1896
- className: "dsh-cc-review-btn dsh-cc-review-btn-primary",
1897
- disabled: busy,
1898
- onClick: () => {
1899
- decide(proposal, "approved");
1900
- },
1901
- children: t("review.action.approve")
1902
- }),
1903
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1904
- type: "button",
1905
- className: "dsh-cc-review-btn",
1906
- disabled: busy,
1907
- onClick: () => {
1908
- decide(proposal, "rejected");
1909
- },
1910
- children: t("review.action.reject")
1911
- }),
1912
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1913
- type: "button",
1914
- className: "dsh-cc-review-btn",
1915
- disabled: busy,
1916
- onClick: () => {
1917
- decide(proposal, "ignored");
1918
- },
1919
- children: t("review.action.ignore")
1920
- })
1921
- ]
1922
- })]
1923
- }, proposal.id))
1924
- ]
1925
- }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1926
- type: "button",
1927
- className: "dsh-cc-review-badge",
1928
- onClick: () => {
1929
- setExpanded((value) => !value);
1930
- },
1931
- children: [
1932
- t("review.badge"),
1933
- " ",
1934
- String(pending.length)
1935
- ]
1936
- })] });
1937
- }
1938
- //#endregion
1939
1457
  //#region src/client/index.ts
1940
1458
  const inject = [
1941
1459
  "slots",
@@ -1989,19 +1507,6 @@ window.__ModuleLoader__.load({
1989
1507
  } catch (error) {
1990
1508
  console.warn("[dsh-context-compression-improved] settings.section 注册失败(新宿主已收编):", error);
1991
1509
  }
1992
- try {
1993
- ctx.slots.inject("shell.overlay", () => ctx.slots.register({
1994
- name: "shell.overlay",
1995
- id: "context-compression-review"
1996
- }, () => {
1997
- return renderReviewOverlay(ctx.settingsScope.bind({
1998
- namespace: NS,
1999
- decode: decodeSettings
2000
- }), ctx.locale.bind(NS));
2001
- }));
2002
- } catch (error) {
2003
- console.warn("[dsh-context-compression-improved] shell.overlay 注册失败(宿主无浮层或已收编):", error);
2004
- }
2005
1510
  }
2006
1511
  //#endregion
2007
1512
  exports.apply = apply;
@@ -17,18 +17,12 @@ interface Config {
17
17
  * covers both arrival orders.
18
18
  */
19
19
  estimatorCatalogRoute?: boolean;
20
- /**
21
- * Register the review pipeline's HTTP routes (pending-queue read + decide
22
- * write) on this row. Same Bundle opt-in semantics as
23
- * `estimatorCatalogRoute`; without the routes the floating window has no
24
- * transport and simply never appears.
25
- */
26
- reviewQueueRoute?: boolean;
27
20
  /**
28
21
  * Register the advisory advisor's read-only HTTP report route (decay
29
- * figure, task summary, score distribution). Same Bundle opt-in semantics
30
- * as `reviewQueueRoute`; the advisor itself stays off until the user turns
31
- * it on through the `presetOptions.advisor*` settings keys.
22
+ * figure, task summary, score distribution, last benefit-model advice).
23
+ * Same Bundle opt-in semantics as `estimatorCatalogRoute`; the advisor
24
+ * itself stays off until the user turns it on through the
25
+ * `presetOptions.advisor*` settings keys.
32
26
  */
33
27
  advisorReportRoute?: boolean;
34
28
  }