dsh-context-compression-improved 0.5.1 → 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 (45) hide show
  1. package/CHANGELOG.ja.md +36 -0
  2. package/CHANGELOG.ko.md +36 -0
  3. package/CHANGELOG.md +38 -0
  4. package/CHANGELOG.zh.md +32 -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 +16 -234
  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/profiles.ts +4 -27
  20. package/packages/selector/src/pruner/state.ts +50 -73
  21. package/packages/selector/src/pruner.ts +2402 -2730
  22. package/packages/selector/src/runtime/audit.ts +27 -21
  23. package/packages/selector/src/runtime/config.ts +6 -32
  24. package/packages/selector/src/runtime/tokenpilot/advisor-state.ts +16 -0
  25. package/packages/selector/src/runtime/tokenpilot/benefit.ts +200 -0
  26. package/packages/selector/src/runtime/types.ts +0 -17
  27. package/packages/selector/tests/preset-options-write.client.spec.ts +7 -23
  28. package/packages/selector/tests/runtime/advice-never-withholds.host.spec.ts +232 -0
  29. package/packages/selector/tests/runtime/audit.spec.ts +35 -21
  30. package/packages/selector/tests/runtime/deprecated-preset-options.spec.ts +96 -0
  31. package/packages/selector/tests/runtime/tokenpilot/benefit.spec.ts +217 -0
  32. package/packages/selector/tests/runtime/tokenpilot/profile-baseline.spec.ts +4 -5
  33. package/packages/selector/tests/settings-seat.client.spec.ts +16 -10
  34. package/packages/selector/src/client/ReviewOverlay.tsx +0 -320
  35. package/packages/selector/src/client/review-scope.ts +0 -16
  36. package/packages/selector/src/runtime/tokenpilot/proposal.ts +0 -267
  37. package/packages/selector/src/runtime/tokenpilot/review-queue.ts +0 -231
  38. package/packages/selector/src/runtime/tokenpilot/review-registry.ts +0 -117
  39. package/packages/selector/src/runtime/tokenpilot/review-storage.ts +0 -122
  40. package/packages/selector/tests/review-overlay.client.spec.tsx +0 -118
  41. package/packages/selector/tests/review-routes-registry.host.spec.ts +0 -142
  42. package/packages/selector/tests/review-routes.host.spec.ts +0 -290
  43. package/packages/selector/tests/runtime/tokenpilot/proposal.spec.ts +0 -393
  44. package/packages/selector/tests/runtime/tokenpilot/pruner-review.spec.ts +0 -382
  45. package/packages/selector/tests/runtime/tokenpilot/review-queue.spec.ts +0 -168
package/CHANGELOG.ja.md CHANGED
@@ -2,6 +2,42 @@
2
2
 
3
3
  > 完全な履歴(アップストリーム 0.1.0 以前を含む)は [CHANGELOG.md](CHANGELOG.md) を参照。このファイルはフォークの追加エントリーのみを翻訳したものです。 · [English](CHANGELOG.md) · [中文](CHANGELOG.zh.md) · [한국어](CHANGELOG.ko.md)
4
4
 
5
+ ## 0.5.2 - 2026-09-20
6
+
7
+ ### Changed(変更)
8
+
9
+ - 人手ゲート付きレビューパイプライン(review gate、beta)を**廃止**しました。その意味論は
10
+ 「助言」に置き換わります。便益モデルは従来どおり 1 パスを 1 回の統合 mutation として
11
+ 価格付けしますが、算出したバンド(`profitable` / `high-impact` / `slow-payback` /
12
+ `unpriceable` / `not-worth-it`)は `reduction-advice` 監査レコードとして公開され、
13
+ advisor レポートルートにスナップショットされるだけになりました——reduction を保留・遅延・
14
+ 書き換えることはありません。この gate は機能自身の要件(削減は自動処理を妨げない)と矛盾し、
15
+ 出荷既定(`reviewMode` 有効 + 4,000 トークンの高影響しきい値に対し 8,192 トークンの
16
+ fresh トリガー)では fresh バッチを 100% 人手レビューへ迂回させ、有効化した利用者から
17
+ 自動経路を事実上奪っていました。助言のしきい値はモジュール定数(α `0.1`、高影響
18
+ `4,000` トークン)になりました。何もこれに従って動作しないため、設定項目ではなくなります。
19
+ - gate とともに削除:レビューキューとその `storageDomain` アダプタ、プロセス全体の
20
+ レジストリ、`review-queue` / `review-decide` の HTTP ルートと `reviewQueueRoute` 配置
21
+ フラグ、`shell.overlay` クライアントパネル、`reviewMode` / `reviewTimeoutTurns` /
22
+ `cacheHitDiscountAlpha` / `reviewHighImpactTokens` の設定キー、`review-outcome` 監査種別。
23
+ 4 つの設定キーは**両方のデコーダで引き続き受理され無視されます**——既存の文書(稼働中は
24
+ `reviewMode: false` を含む)はそのまま読み込まれ、設定カードも表示されます——そして解決済み
25
+ policy には決して到達しません。読み取り専用の `GET .../advisor-report` は `lastAdvice` も
26
+ 返します。
27
+
28
+ ### Added(追加)
29
+
30
+ - 廃止に対する回帰ピン:かつて全量を迂回させていた設定(`reviewMode: true`、
31
+ `reviewHighImpactTokens: 1`)で fresh バッチが**そのまま着地**し、`high-impact` の助言
32
+ レコードがそれを記述することをホスト統合テストで固定。ランタイムパーサとブラウザデコーダの
33
+ 両側で「受理して無視」を固定する非推奨契約テストも追加。
34
+
35
+ ### Rollback(ロールバック)
36
+
37
+ - gate を含む最後のリリースへ戻す:`npm dist-tag add
38
+ dsh-context-compression-improved@0.5.1 dsh-0.1.5 --registry https://registry.npmjs.org/`
39
+ の後、`dsh plugin --profile web add dsh-context-compression-improved@0.5.1`。
40
+
5
41
  ## 0.5.1 - 2026-09-20
6
42
 
7
43
  ### Fixed(修正)
package/CHANGELOG.ko.md CHANGED
@@ -2,6 +2,42 @@
2
2
 
3
3
  > 전체 히스토리(업스트림 0.1.0 이전 포함)는 [CHANGELOG.md](CHANGELOG.md)를 참고하세요. 이 파일은 포크의 추가 항목만 번역한 것입니다. · [English](CHANGELOG.md) · [中文](CHANGELOG.zh.md) · [日本語](CHANGELOG.ja.md)
4
4
 
5
+ ## 0.5.2 - 2026-09-20
6
+
7
+ ### Changed(변경)
8
+
9
+ - 사람이 승인하는 리뷰 파이프라인(review gate, beta)을 **폐기**했습니다. 그 의미론은
10
+ "조언"으로 대체됩니다. 이익 모델은 여전히 한 패스를 하나의 병합 mutation으로 가격
11
+ 책정하지만, 계산한 밴드(`profitable` / `high-impact` / `slow-payback` /
12
+ `unpriceable` / `not-worth-it`)는 이제 `reduction-advice` 감사 레코드로 게시되고 advisor
13
+ 리포트 라우트에 스냅샷될 뿐입니다——reduction을 보류·지연·재작성하지 않습니다. 이 gate는
14
+ 기능 자체의 요구(축소는 자동 처리를 막지 않는다)와 모순되었고, 출하 기본값(`reviewMode`
15
+ 켬 + 4,000 토큰 고영향 임계값 대 8,192 토큰 fresh 트리거)에서는 fresh 배치를 100% 사람
16
+ 리뷰로 우회시켜, 이를 켠 사용자에게서 자동 경로를 사실상 빼앗았습니다. 조언 임계값은 이제
17
+ 모듈 상수(α `0.1`, 고영향 `4,000` 토큰)입니다. 아무 경로도 이에 따라 동작하지 않으므로
18
+ 더 이상 설정 항목이 아닙니다.
19
+ - gate와 함께 제거: 리뷰 큐와 그 `storageDomain` 어댑터, 프로세스 전역 레지스트리,
20
+ `review-queue` / `review-decide` HTTP 라우트와 `reviewQueueRoute` 배포 플래그,
21
+ `shell.overlay` 클라이언트 패널, `reviewMode` / `reviewTimeoutTurns` /
22
+ `cacheHitDiscountAlpha` / `reviewHighImpactTokens` 설정 키, `review-outcome` 감사 종류.
23
+ 네 개의 설정 키는 **두 디코더 모두에서 계속 허용되고 무시됩니다**——기존 문서(운영 중인
24
+ 문서에는 `reviewMode: false`가 있음)는 그대로 로드되고 설정 카드도 렌더링됩니다——그리고
25
+ 해석된 policy에는 결코 도달하지 않습니다. 읽기 전용 `GET .../advisor-report`는
26
+ `lastAdvice`도 함께 제공합니다.
27
+
28
+ ### Added(추가)
29
+
30
+ - 폐기에 대한 회귀 고정: 예전에 전량을 우회시키던 설정(`reviewMode: true`,
31
+ `reviewHighImpactTokens: 1`)으로 fresh 배치가 **그대로 착지**하고 `high-impact` 조언
32
+ 레코드가 이를 설명함을 호스트 통합 테스트로 고정했습니다. 런타임 파서와 브라우저 디코더
33
+ 양쪽에서 "허용 후 무시"를 고정하는 폐기 계약 테스트도 추가했습니다.
34
+
35
+ ### Rollback(롤백)
36
+
37
+ - gate를 포함한 마지막 릴리스로 되돌리기: `npm dist-tag add
38
+ dsh-context-compression-improved@0.5.1 dsh-0.1.5 --registry https://registry.npmjs.org/`
39
+ 후 `dsh plugin --profile web add dsh-context-compression-improved@0.5.1`.
40
+
5
41
  ## 0.5.1 - 2026-09-20
6
42
 
7
43
  ### Fixed(수정)
package/CHANGELOG.md CHANGED
@@ -2,6 +2,44 @@
2
2
 
3
3
  All notable changes use this file. The project follows semantic versioning after `0.1.0`.
4
4
 
5
+ ## 0.5.2 - 2026-09-20
6
+
7
+ ### Changed
8
+
9
+ - The human-gated review pipeline (beta) is **retired**. Its semantics are replaced by
10
+ advice: the benefit model still prices every pass as ONE merged mutation, but the band it
11
+ computes (`profitable` / `high-impact` / `slow-payback` / `unpriceable` / `not-worth-it`)
12
+ is now published as a `reduction-advice` audit record and snapshotted onto the advisor
13
+ report route — it never withholds, delays, or rewrites a reduction. The gate contradicted
14
+ the feature's own requirement (a reduction must never block automatic processing) and, with
15
+ the shipped defaults (`reviewMode` on plus a 4,000-token high-impact threshold against an
16
+ 8,192-token fresh trigger), diverted 100% of a fresh batch into human review, leaving the
17
+ automatic path effectively off for anyone who opted in. The advice thresholds are now
18
+ module constants (α `0.1`, high-impact `4,000` tokens): nothing acts on them, so they are
19
+ no longer settings.
20
+ - Removed with the gate: the review queue and its `storageDomain` adapter, the process-wide
21
+ registry, the `review-queue` / `review-decide` HTTP routes and the `reviewQueueRoute`
22
+ deployment flag, the `shell.overlay` client panel, the `reviewMode` /
23
+ `reviewTimeoutTurns` / `cacheHitDiscountAlpha` / `reviewHighImpactTokens` settings keys,
24
+ and the `review-outcome` audit kind. The four settings keys stay ACCEPTED and IGNORED by
25
+ both decoders — an existing document (the live one carries `reviewMode: false`) still loads
26
+ and still renders its settings card — and never reach the resolved policy. The read-only
27
+ `GET .../advisor-report` route additionally serves `lastAdvice`.
28
+
29
+ ### Added
30
+
31
+ - Regression pins for the retirement: a host-integration spec drives the exact settings that
32
+ used to divert everything (`reviewMode: true`, `reviewHighImpactTokens: 1`) and asserts the
33
+ fresh batch LANDS, described by a `high-impact` advice record; a deprecation-contract spec
34
+ pins accept-and-ignore for the retired keys on both the runtime parser and the browser
35
+ decoder.
36
+
37
+ ### Rollback
38
+
39
+ - Reinstall the last release that still ships the gate: `npm dist-tag add
40
+ dsh-context-compression-improved@0.5.1 dsh-0.1.5 --registry https://registry.npmjs.org/`
41
+ then `dsh plugin --profile web add dsh-context-compression-improved@0.5.1`.
42
+
5
43
  ## 0.5.1 - 2026-09-20
6
44
 
7
45
  ### Fixed
package/CHANGELOG.zh.md CHANGED
@@ -2,6 +2,38 @@
2
2
 
3
3
  > 完整历史(含上游 0.1.0 及更早版本)见 [CHANGELOG.md](CHANGELOG.md)。本文件只翻译本 fork 的新增条目。 · [English](CHANGELOG.md) · [日本語](CHANGELOG.ja.md) · [한국어](CHANGELOG.ko.md)
4
4
 
5
+ ## 0.5.2 - 2026-09-20
6
+
7
+ ### Changed
8
+
9
+ - **退役**人工审查管线(review gate,beta)。其语义由"建议"取代:收益模型仍按"整批一次
10
+ mutation"定价,但它算出的分带(`profitable` / `high-impact` / `slow-payback` /
11
+ `unpriceable` / `not-worth-it`)现在只作为 `reduction-advice` 审计记录发布,并在 advisor
12
+ 报告路由上留一份快照——绝不扣留、延迟或改写任何 reduction。该 gate 与本功能自身的要求
13
+ (缩减不阻断自动处理)相矛盾,且在出厂默认下(`reviewMode` 开 + 4000 token 高影响阈值
14
+ 对 8192 token 的 fresh 门槛)会把整批 fresh 100% 改道进人审,等于让选择开启它的用户失去
15
+ 自动路径。建议阈值改为模块常量(α `0.1`、高影响 `4000` token):没有任何路径依据它们
16
+ 行动,因此不再作为配置项。
17
+ - 随 gate 一并移除:审查队列及其 `storageDomain` 适配器、进程级注册表、
18
+ `review-queue` / `review-decide` 两条 HTTP 路由与 `reviewQueueRoute` 部署开关、
19
+ `shell.overlay` 客户端浮窗、`reviewMode` / `reviewTimeoutTurns` /
20
+ `cacheHitDiscountAlpha` / `reviewHighImpactTokens` 四个 settings 键,以及
21
+ `review-outcome` 审计类型。这四个键在**两个解码器**中仍被接受但被忽略——既有配置文档
22
+ (线上即带 `reviewMode: false`)照常加载、设置卡照常渲染——且永不进入已解析的 policy。
23
+ 只读路由 `GET .../advisor-report` 额外提供 `lastAdvice`。
24
+
25
+ ### Added
26
+
27
+ - 退役的回归钉桩:一条宿主集成测试用**当年会全量改道的那套配置**(`reviewMode: true`、
28
+ `reviewHighImpactTokens: 1`)断言 fresh 批次**照常落地**并附带 `high-impact` 建议记录;
29
+ 另一条弃用契约测试在运行时解析器与浏览器解码器两侧钉死"接受但忽略"。
30
+
31
+ ### Rollback
32
+
33
+ - 回退到仍带 gate 的最后一个版本:`npm dist-tag add
34
+ dsh-context-compression-improved@0.5.1 dsh-0.1.5 --registry https://registry.npmjs.org/`,
35
+ 然后 `dsh plugin --profile web add dsh-context-compression-improved@0.5.1`。
36
+
5
37
  ## 0.5.1 - 2026-09-20
6
38
 
7
39
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-context-compression-improved",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org",
6
6
  "access": "public",
@@ -408,8 +408,7 @@ function parsePresetOptionsSettings(value) {
408
408
  "dedupeToolResults",
409
409
  "summaryLocator",
410
410
  "prefixStabilizer",
411
- "readState",
412
- "reviewMode"
411
+ "readState"
413
412
  ]) {
414
413
  const entry = value[key];
415
414
  if (entry !== void 0 && typeof entry !== "boolean") throw new TypeError(`Context-compression presetOptions.${key} must be a boolean`);
@@ -430,12 +429,6 @@ function parsePresetOptionsSettings(value) {
430
429
  if (advisorMinTokens !== void 0 && (typeof advisorMinTokens !== "number" || !Number.isSafeInteger(advisorMinTokens) || advisorMinTokens < 1)) throw new TypeError("Context-compression presetOptions.advisorMinTokens must be a positive integer");
431
430
  const estimatorTimeoutMs = value.estimatorTimeoutMs;
432
431
  if (estimatorTimeoutMs !== void 0 && (typeof estimatorTimeoutMs !== "number" || !Number.isSafeInteger(estimatorTimeoutMs) || estimatorTimeoutMs < 100 || estimatorTimeoutMs > 6e4)) throw new TypeError("Context-compression presetOptions.estimatorTimeoutMs must be an integer between 100 and 60000");
433
- const reviewTimeoutTurns = value.reviewTimeoutTurns;
434
- if (reviewTimeoutTurns !== void 0 && (typeof reviewTimeoutTurns !== "number" || !Number.isSafeInteger(reviewTimeoutTurns) || reviewTimeoutTurns < 1)) throw new TypeError("Context-compression presetOptions.reviewTimeoutTurns must be an integer of at least 1");
435
- const cacheHitDiscountAlpha = value.cacheHitDiscountAlpha;
436
- if (cacheHitDiscountAlpha !== void 0 && (typeof cacheHitDiscountAlpha !== "number" || !Number.isFinite(cacheHitDiscountAlpha) || cacheHitDiscountAlpha <= 0 || cacheHitDiscountAlpha >= 1)) throw new TypeError("Context-compression presetOptions.cacheHitDiscountAlpha must be a number strictly between 0 and 1");
437
- const reviewHighImpactTokens = value.reviewHighImpactTokens;
438
- if (reviewHighImpactTokens !== void 0 && (typeof reviewHighImpactTokens !== "number" || !Number.isSafeInteger(reviewHighImpactTokens) || reviewHighImpactTokens < 0)) throw new TypeError("Context-compression presetOptions.reviewHighImpactTokens must be a non-negative integer");
439
432
  for (const key of [
440
433
  "estimatorProvider",
441
434
  "estimatorModel",
@@ -456,10 +449,6 @@ function parsePresetOptionsSettings(value) {
456
449
  if (value.estimatorBaseUrl !== void 0) result.estimatorBaseUrl = value.estimatorBaseUrl;
457
450
  if (value.estimatorApiKey !== void 0) result.estimatorApiKey = value.estimatorApiKey;
458
451
  if (estimatorTimeoutMs !== void 0) result.estimatorTimeoutMs = estimatorTimeoutMs;
459
- if (value.reviewMode !== void 0) result.reviewMode = value.reviewMode;
460
- if (reviewTimeoutTurns !== void 0) result.reviewTimeoutTurns = reviewTimeoutTurns;
461
- if (cacheHitDiscountAlpha !== void 0) result.cacheHitDiscountAlpha = cacheHitDiscountAlpha;
462
- if (reviewHighImpactTokens !== void 0) result.reviewHighImpactTokens = reviewHighImpactTokens;
463
452
  if (advisorMode !== void 0) result.advisorMode = advisorMode;
464
453
  if (advisorTimeoutMs !== void 0) result.advisorTimeoutMs = advisorTimeoutMs;
465
454
  if (advisorRefreshTurns !== void 0) result.advisorRefreshTurns = advisorRefreshTurns;
@@ -681,10 +670,6 @@ const PRESET_OPTION_DEFAULTS = deepFreeze({
681
670
  prefixStabilizer: true,
682
671
  readState: true,
683
672
  estimator: { mode: "" },
684
- reviewMode: false,
685
- reviewTimeoutTurns: 6,
686
- cacheHitDiscountAlpha: .1,
687
- reviewHighImpactTokens: 4e3,
688
673
  advisor: {
689
674
  mode: "",
690
675
  timeoutMs: 8e3,
@@ -709,10 +694,6 @@ function mergePresetOptions(overrides) {
709
694
  prefixStabilizer: overrides.prefixStabilizer ?? PRESET_OPTION_DEFAULTS.prefixStabilizer,
710
695
  readState: overrides.readState ?? PRESET_OPTION_DEFAULTS.readState,
711
696
  estimator: { mode: overrides.estimatorMode ?? PRESET_OPTION_DEFAULTS.estimator.mode },
712
- reviewMode: overrides.reviewMode ?? PRESET_OPTION_DEFAULTS.reviewMode,
713
- reviewTimeoutTurns: overrides.reviewTimeoutTurns ?? PRESET_OPTION_DEFAULTS.reviewTimeoutTurns,
714
- cacheHitDiscountAlpha: overrides.cacheHitDiscountAlpha ?? PRESET_OPTION_DEFAULTS.cacheHitDiscountAlpha,
715
- reviewHighImpactTokens: overrides.reviewHighImpactTokens ?? PRESET_OPTION_DEFAULTS.reviewHighImpactTokens,
716
697
  advisor: {
717
698
  mode: overrides.advisorMode ?? PRESET_OPTION_DEFAULTS.advisor.mode,
718
699
  timeoutMs: overrides.advisorTimeoutMs ?? PRESET_OPTION_DEFAULTS.advisor.timeoutMs,
@@ -915,215 +896,6 @@ function assertPositiveInteger(name, value) {
915
896
  function assertNonNegativeInteger(name, value) {
916
897
  if (!Number.isSafeInteger(value) || value < 0) throw new Error(`ToolResultPruneConfig: ${name} (${String(value)}) must be a non-negative safe integer`);
917
898
  }
918
- //#endregion
919
- //#region src/runtime/tokenpilot/review-queue.ts
920
- /** In-memory store: the fail-open fallback when no durable seam is available. */
921
- var MemoryReviewStore = class {
922
- sessions = /* @__PURE__ */ new Map();
923
- load(sessionId) {
924
- return this.sessions.get(sessionId);
925
- }
926
- save(sessionId, record) {
927
- this.sessions.set(sessionId, record);
928
- }
929
- ids() {
930
- return [...this.sessions.keys()];
931
- }
932
- };
933
- var ReviewQueue = class {
934
- store;
935
- options;
936
- constructor(store, options) {
937
- this.store = store;
938
- this.options = options;
939
- }
940
- /**
941
- * Fail-open store access: a throwing seam must never break the compression
942
- * pipeline. Reads degrade to "no stored record"; writes degrade to losing
943
- * durability for that call (the store itself is expected to warn).
944
- */
945
- safeLoad(sessionId) {
946
- try {
947
- return this.store.load(sessionId);
948
- } catch {
949
- return;
950
- }
951
- }
952
- safeSave(sessionId, record) {
953
- try {
954
- this.store.save(sessionId, record);
955
- } catch {}
956
- }
957
- sessionRecord(sessionId) {
958
- return this.safeLoad(sessionId) ?? {
959
- version: 1,
960
- proposals: []
961
- };
962
- }
963
- /**
964
- * Queue one classified proposal. A repeated classification of the same
965
- * content re-does nothing but refresh the patience clock, so re-enqueue
966
- * cannot duplicate a live proposal.
967
- * @returns `false` when an identical live proposal already exists.
968
- */
969
- enqueue(sessionId, skeleton, turnIndex) {
970
- const record = this.sessionRecord(sessionId);
971
- const existing = record.proposals.find((entry) => entry.id === skeleton.id);
972
- if (existing !== void 0 && existing.status !== "expired") {
973
- existing.lastTurnIndex = turnIndex;
974
- this.safeSave(sessionId, record);
975
- return false;
976
- }
977
- const proposal = {
978
- id: skeleton.id,
979
- sessionId,
980
- kind: skeleton.kind,
981
- items: skeleton.items.map((item) => ({ ...item })),
982
- benefit: { ...skeleton.benefit },
983
- status: "pending",
984
- enqueuedTurn: turnIndex,
985
- lastTurnIndex: turnIndex
986
- };
987
- this.safeSave(sessionId, {
988
- version: 1,
989
- proposals: [...record.proposals.filter((entry) => entry.id !== skeleton.id), proposal]
990
- });
991
- return true;
992
- }
993
- /** Live pending proposals of one session, oldest enqueue first. */
994
- listPending(sessionId) {
995
- return this.sessionRecord(sessionId).proposals.filter((entry) => entry.status === "pending").sort((left, right) => left.enqueuedTurn - right.enqueuedTurn);
996
- }
997
- /** Approved proposals waiting for the next turn-boundary batch. */
998
- listApproved(sessionId) {
999
- return this.sessionRecord(sessionId).proposals.filter((entry) => entry.status === "approved").sort((left, right) => left.enqueuedTurn - right.enqueuedTurn);
1000
- }
1001
- /**
1002
- * Transition one pending proposal. Idempotent: deciding an unknown id or a
1003
- * non-pending proposal changes nothing and reports the miss.
1004
- */
1005
- decide(sessionId, id, decision) {
1006
- const record = this.sessionRecord(sessionId);
1007
- const proposal = record.proposals.find((entry) => entry.id === id);
1008
- if (proposal === void 0) return {
1009
- ok: false,
1010
- reason: "unknown-proposal"
1011
- };
1012
- if (proposal.status !== "pending") return {
1013
- ok: false,
1014
- reason: "not-pending"
1015
- };
1016
- proposal.status = decision;
1017
- this.safeSave(sessionId, record);
1018
- return { ok: true };
1019
- }
1020
- /**
1021
- * Expire every pending proposal whose patience has run out at this turn
1022
- * boundary. Expired proposals are removed from the store (the summary view
1023
- * aggregates them from the audit log instead).
1024
- * @returns the expired proposals, for the caller's audit emission.
1025
- */
1026
- expireTurn(sessionId, turnIndex) {
1027
- const record = this.sessionRecord(sessionId);
1028
- const keep = [];
1029
- const expired = [];
1030
- for (const proposal of record.proposals) {
1031
- if (proposal.status === "pending" && turnIndex - proposal.lastTurnIndex > this.options.timeoutTurns) {
1032
- expired.push({
1033
- ...proposal,
1034
- status: "expired"
1035
- });
1036
- continue;
1037
- }
1038
- keep.push(proposal);
1039
- }
1040
- if (expired.length > 0) this.safeSave(sessionId, {
1041
- version: 1,
1042
- proposals: keep
1043
- });
1044
- return expired;
1045
- }
1046
- /**
1047
- * Settle an approved proposal with its execution receipt and retire it from
1048
- * the live store. The caller is responsible for auditing the receipt; the
1049
- * queue only records which proposal left and why.
1050
- */
1051
- recordReceipt(sessionId, id, receipt) {
1052
- const record = this.sessionRecord(sessionId);
1053
- const proposal = record.proposals.find((entry) => entry.id === id);
1054
- if (proposal === void 0 || proposal.status !== "approved") return void 0;
1055
- this.safeSave(sessionId, {
1056
- version: 1,
1057
- proposals: record.proposals.filter((entry) => entry.id !== id)
1058
- });
1059
- return {
1060
- ...proposal,
1061
- receipt
1062
- };
1063
- }
1064
- };
1065
- //#endregion
1066
- //#region src/runtime/tokenpilot/review-registry.ts
1067
- /**
1068
- * Scope-independent handle on the live review pipeline.
1069
- *
1070
- * The R4 HTTP routes are registered on the plugin's TOP-LEVEL fiber
1071
- * (`cordis.patch.yml` → `context-compression-improved-estimator-catalog`),
1072
- * but every `ToolResultPruner` is mounted inside an agent preset's isolated
1073
- * group — `canonicalCompressionRows()` declares
1074
- * `isolate: { compaction: true, toolResultPruner: true }` — so
1075
- * `ctx.get('toolResultPruner')` at the top level is always `undefined` and the
1076
- * queue route could only ever answer 503 "review pipeline unavailable".
1077
- *
1078
- * Ownership, not transport, was in the wrong place: the queue records are
1079
- * already keyed by session id, so the store belongs to the plugin rather than
1080
- * to one pruner instance. Every instance shares one store and publishes itself
1081
- * here, which lets a top-level reader reach whichever instance currently holds
1082
- * a session's proposals.
1083
- *
1084
- * The durable seam already behaves this way — `REVIEW_STORAGE_DOMAIN` /
1085
- * `REVIEW_STORAGE_TABLE` are constants, so every instance opens the same table.
1086
- * Only the in-memory fallback was per-instance, and that is what this module
1087
- * makes shared.
1088
- *
1089
- * @module dsh-context-compression-improved/review-registry
1090
- */
1091
- /**
1092
- * The one in-memory fallback every pruner instance starts from. Session ids are
1093
- * globally unique and `ReviewSessionRecord` is keyed by them, so a single store
1094
- * is semantically identical to one store per instance — except that a reader
1095
- * reaching any instance now observes every session.
1096
- */
1097
- const sharedStore = new MemoryReviewStore();
1098
- const live = /* @__PURE__ */ new Set();
1099
- /** The process-wide review queue store shared by every pruner instance. */
1100
- function sharedReviewStore() {
1101
- return sharedStore;
1102
- }
1103
- /**
1104
- * Publish one pruner instance for scope-independent readers.
1105
- * @param pruner - the instance to publish.
1106
- * @returns the disposer removing it, for `ctx.effect`.
1107
- */
1108
- function registerReviewPruner(pruner) {
1109
- live.add(pruner);
1110
- return () => {
1111
- live.delete(pruner);
1112
- };
1113
- }
1114
- /**
1115
- * Resolve a live pruner instance for the top-level routes.
1116
- *
1117
- * Any instance can serve an aggregate read because the store is shared, and a
1118
- * session-scoped read is answered from that same store. Instances that have
1119
- * upgraded to the durable seam read the same table, so the answer does not
1120
- * depend on which instance this happens to return.
1121
- *
1122
- * @returns a live instance, or `undefined` when no preset has been composed yet.
1123
- */
1124
- function resolveReviewPruner() {
1125
- return live.values().next().value;
1126
- }
1127
899
  const advisorStates = /* @__PURE__ */ new WeakMap();
1128
900
  /**
1129
901
  * The per-session advisor state, created on first touch.
@@ -1141,7 +913,8 @@ function getAdvisorState(session) {
1141
913
  recertified: /* @__PURE__ */ new Map(),
1142
914
  failures: void 0,
1143
915
  inFlight: false,
1144
- lastDecay: void 0
916
+ lastDecay: void 0,
917
+ lastAdvice: void 0
1145
918
  };
1146
919
  advisorStates.set(session, state);
1147
920
  }
@@ -1186,4 +959,4 @@ function invalidateOnTaskChange(state, todoVersion) {
1186
959
  return true;
1187
960
  }
1188
961
  //#endregion
1189
- export { DEFAULT_CUSTOM_COMPRESSION_POLICY as C, COMPRESSION_PROFILES as D, deepFreeze as E, CustomCompressionPolicySchema as S, assertNever as T, isCompressionProfile as _, registerReviewPruner as a, resolveConfig as b, ReviewQueue as c, ContextCompressionSettingsSchema as d, DEFAULTS as f, codePointLength as g, charsToTokens as h, recordScore as i, AUTO_COMPACT_THRESHOLD_LIMITS as l, charsForTokens as m, invalidateOnTaskChange as n, resolveReviewPruner as o, PRUNE_MARKER as p, recordRecertified as r, sharedReviewStore as s, getAdvisorState as t, CONTEXT_COMPRESSION_SETTINGS_NAMESPACE as u, isValidAutoCompactThresholdPercent as v, resolveCustomPolicy as w, resolvePolicy as x, parseContextCompressionSettings as y };
962
+ export { COMPRESSION_PROFILES as C, deepFreeze as S, resolvePolicy as _, AUTO_COMPACT_THRESHOLD_LIMITS as a, resolveCustomPolicy as b, DEFAULTS as c, charsToTokens as d, codePointLength as f, resolveConfig as g, parseContextCompressionSettings as h, recordScore as i, PRUNE_MARKER as l, isValidAutoCompactThresholdPercent as m, invalidateOnTaskChange as n, CONTEXT_COMPRESSION_SETTINGS_NAMESPACE as o, isCompressionProfile as p, recordRecertified as r, ContextCompressionSettingsSchema as s, getAdvisorState as t, charsForTokens as u, CustomCompressionPolicySchema as v, assertNever as x, DEFAULT_CUSTOM_COMPRESSION_POLICY as y };
@@ -84,11 +84,6 @@ interface PresetOptionsSettings {
84
84
  readonly estimatorBaseUrl?: string;
85
85
  readonly estimatorApiKey?: string;
86
86
  readonly estimatorTimeoutMs?: number;
87
- /** Review-mode overrides (beta); mirrors the runtime PresetOptions.reviewMode. */
88
- readonly reviewMode?: boolean;
89
- readonly reviewTimeoutTurns?: number;
90
- readonly cacheHitDiscountAlpha?: number;
91
- readonly reviewHighImpactTokens?: number;
92
87
  /** Advisory advisor channel; `''` (the default) keeps the advisor off. */
93
88
  readonly advisorMode?: '' | 'host' | 'direct';
94
89
  readonly advisorTimeoutMs?: number;
@@ -153,25 +148,6 @@ declare const zh: {
153
148
  'estimator.apiKey.set': string;
154
149
  'estimator.apiKey.clear': string;
155
150
  'estimator.apiKey.overwrite': string;
156
- 'review.title': string;
157
- 'review.description': string;
158
- 'review.enabled': string;
159
- 'review.enabled.on': string;
160
- 'review.enabled.off': string;
161
- 'review.timeoutTurns': string;
162
- 'review.alpha': string;
163
- 'review.highImpact': string;
164
- 'review.badge': string;
165
- 'review.summary.autoApplied': string;
166
- 'review.summary.reviewApplied': string;
167
- 'review.summary.expired': string;
168
- 'review.summary.voided': string;
169
- 'review.row.payback': string;
170
- 'review.row.expectedSaving': string;
171
- 'review.row.estimated': string;
172
- 'review.action.approve': string;
173
- 'review.action.reject': string;
174
- 'review.action.ignore': string;
175
151
  'detail.tokenpilot-inspired': string;
176
152
  'profile.custom': string;
177
153
  'profile.native': string;