dsh-context-compression-improved 0.4.0 → 0.5.0
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/CHANGELOG.ja.md +19 -0
- package/CHANGELOG.ko.md +19 -0
- package/CHANGELOG.md +21 -0
- package/CHANGELOG.zh.md +17 -0
- package/docs/installation.md +25 -1
- package/docs/installation.zh.md +24 -1
- package/package.json +1 -1
- package/packages/selector/lib/{review-registry.js → advisor-state.js} +105 -4
- package/packages/selector/lib/client.d.ts +7 -0
- package/packages/selector/lib/client.js +32 -2
- package/packages/selector/lib/index.d.ts +7 -0
- package/packages/selector/lib/index.js +101 -2
- package/packages/selector/lib/pruner.d.ts +82 -0
- package/packages/selector/lib/pruner.js +545 -11
- package/packages/selector/src/client/preset-options.ts +2 -0
- package/packages/selector/src/index.ts +108 -0
- package/packages/selector/src/profiles.ts +48 -0
- package/packages/selector/src/pruner/state.ts +3 -0
- package/packages/selector/src/pruner.ts +113 -0
- package/packages/selector/src/runtime/audit.ts +22 -0
- package/packages/selector/src/runtime/config.ts +58 -0
- package/packages/selector/src/runtime/tokenpilot/advisor-prompt.ts +188 -0
- package/packages/selector/src/runtime/tokenpilot/advisor-state.ts +133 -0
- package/packages/selector/src/runtime/tokenpilot/advisor.ts +419 -0
- package/packages/selector/src/runtime/tokenpilot/sidechannel.ts +24 -9
- package/packages/selector/src/runtime/types.ts +21 -0
- package/packages/selector/tests/advisor-report.host.spec.ts +223 -0
- package/packages/selector/tests/runtime/advisor-invariant.spec.ts +272 -0
- package/packages/selector/tests/runtime/advisor.spec.ts +226 -0
- package/packages/selector/tests/runtime/audit.spec.ts +44 -0
- package/packages/selector/tests/runtime/tokenpilot/profile-baseline.spec.ts +12 -0
package/CHANGELOG.ja.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
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.0 - 2026-09-20
|
|
6
|
+
|
|
7
|
+
### Added(追加)
|
|
8
|
+
|
|
9
|
+
- アドバイザリー関連度アドバイザー(統計と提案のみ、デフォルト無効):各ターン境界で
|
|
10
|
+
fire-and-forget のパスを実行し、最新の `todo/write` イベントからセッションの末端タスク
|
|
11
|
+
意味を要約し(todolist が無い場合は直近のユーザーテキストへフォールバック)、履歴
|
|
12
|
+
tool-result 候選の「内容+コメント意味 ↔ 現在タスク」の関連度を増分スコアリングし、
|
|
13
|
+
prefix-decay(characterPressure 重み付けの関連度平均の逆数)を算出します。関連度の低い
|
|
14
|
+
古いセグメントは `recertified` として記録されますが、これは将来の history 積極度判断への
|
|
15
|
+
提案入力に過ぎず——今回それを消費する経路は存在せず、アドバイザーの出力が着地すべき
|
|
16
|
+
reduction を抑制・遅延・書き換えすることは決してありません(専用の不変テストで固定)。
|
|
17
|
+
`presetOptions.advisor*` 設定キーで設定(`advisorMode` `''|'host'|'direct'`、既定 `''`;
|
|
18
|
+
direct チャネルは estimator のエンドポイントを再利用;`SideChannel` に任意の overrides
|
|
19
|
+
引数を追加し、設定を共有せずトランスポートを共有)。可観測性:新しい `advisor-outcome`
|
|
20
|
+
監査レコード(content-free、summary / scoring / decay の各フェーズごとに 1 件)と、読み
|
|
21
|
+
取り専用 HTTP ルート `GET .../advisor-report?sessionId=`(デプロイ opt-in フラグ
|
|
22
|
+
`advisorReportRoute`、review ルートと同じ骨格)。クライアント UI は今回意図的に未提供。
|
|
23
|
+
|
|
5
24
|
## 0.4.0 - 2026-09-20
|
|
6
25
|
|
|
7
26
|
### Fixed(修正)
|
package/CHANGELOG.ko.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
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.0 - 2026-09-20
|
|
6
|
+
|
|
7
|
+
### Added(추가)
|
|
8
|
+
|
|
9
|
+
- 어드바이저리 관련도 어드바이저(통계와 제안 전용, 기본 꺼짐): 모든 턴 경계에서
|
|
10
|
+
fire-and-forget 패스를 실행하여, 최신 `todo/write` 이벤트에서 세션의 후미 작업 의미를
|
|
11
|
+
요약하고(todolist 가 없으면 최근 사용자 텍스트로 폴백), 역사적 tool-result 후보의
|
|
12
|
+
"내용+주석 의미 ↔ 현재 작업" 관련도를 증분 스코어링하며, prefix-decay 수치(characterPressure
|
|
13
|
+
가중 관련도 평균의 역)를 계산합니다. 관련도가 낮은 오래된 세그먼트는 `recertified` 로
|
|
14
|
+
표시되지만 이는 향후 history 공격성 판단을 위한 제안 입력일 뿐——이번 라운드에서 이를
|
|
15
|
+
소비하는 경로는 없으며, 어드바이저 출력이 착지해야 할 reduction 을 억제·지연·재작성하는
|
|
16
|
+
일은 결코 없습니다(전용 불변 테스트로 고정). `presetOptions.advisor*` 설정 키로 구성
|
|
17
|
+
(`advisorMode` `''|'host'|'direct'`, 기본 `''`; direct 채널은 estimator 엔드포인트 재사용;
|
|
18
|
+
`SideChannel` 에 선택적 overrides 매개변수 추가로 설정 공유 없이 전송 계층 공유).
|
|
19
|
+
관측성: 새로운 `advisor-outcome` 감사 레코드(content-free, summary / scoring / decay 각
|
|
20
|
+
페이즈별 1개)와 읽기 전용 HTTP 라우트 `GET .../advisor-report?sessionId=`(배포 opt-in
|
|
21
|
+
플래그 `advisorReportRoute`, review 라우트와 동일한 뼈대). 클라이언트 UI 는 이번 라운드에서
|
|
22
|
+
의도적으로 제공하지 않습니다.
|
|
23
|
+
|
|
5
24
|
## 0.4.0 - 2026-09-20
|
|
6
25
|
|
|
7
26
|
### Fixed(수정)
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes use this file. The project follows semantic versioning after `0.1.0`.
|
|
4
4
|
|
|
5
|
+
## 0.5.0 - 2026-09-20
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Advisory relevance advisor (statistics and suggestions only, default off): at every turn
|
|
10
|
+
boundary a fire-and-forget pass summarizes the session's tail-task semantics from the
|
|
11
|
+
most recent `todo/write` event (falling back to recent user text), incrementally scores
|
|
12
|
+
historical tool-result candidates for content-and-comment relevance against the current
|
|
13
|
+
task, and computes a prefix-decay figure (character-pressure-weighted mean relevance).
|
|
14
|
+
Low-relevance old segments are marked `recertified` as suggestions for later
|
|
15
|
+
history-aggressiveness decisions — nothing this round consumes them, and advisor output
|
|
16
|
+
can never suppress, delay, or rewrite any reduction that would land (pinned by a
|
|
17
|
+
dedicated invariant test). Configure through the `presetOptions.advisor*` settings keys
|
|
18
|
+
(`advisorMode` `''|'host'|'direct'`, default `''`; the direct channel reuses the
|
|
19
|
+
estimator endpoint; `SideChannel` gained an optional overrides parameter so the
|
|
20
|
+
estimator's transport is shared without sharing its configuration). Observability: new
|
|
21
|
+
`advisor-outcome` audit records (content-free, one per phase: summary / scoring / decay)
|
|
22
|
+
and a read-only `GET .../advisor-report?sessionId=` HTTP route (opt-in deployment flag
|
|
23
|
+
`advisorReportRoute`, mirroring the review routes). Client UI is intentionally absent
|
|
24
|
+
this round.
|
|
25
|
+
|
|
5
26
|
## 0.4.0 - 2026-09-20
|
|
6
27
|
|
|
7
28
|
### Fixed
|
package/CHANGELOG.zh.md
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
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.0 - 2026-09-20
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- 建议型相关度 advisor(仅统计与建议,默认关闭):每个 turn 边界以 fire-and-forget 方式
|
|
10
|
+
运行一次 pass——从最近的 `todo/write` 事件总结尾部任务语义(无 todolist 时回退到最近
|
|
11
|
+
用户文本)、对历史 tool-result 候选做"内容+注释语义 ↔ 当前任务"的增量相关度打分、并
|
|
12
|
+
计算前缀腐化度(prefix-decay,按 characterPressure 加权的相关度均值取反)。低相关的
|
|
13
|
+
旧段标记为 `recertified`,仅作为后续 history 激进化的建议输入——本轮没有任何决策路径
|
|
14
|
+
消费它,且 advisor 输出绝不抑制、延迟或改写任何本应落地的 reduction(有专项不变量测试
|
|
15
|
+
钉死)。经 `presetOptions.advisor*` settings 键配置(`advisorMode` `''|'host'|'direct'`,
|
|
16
|
+
默认 `''`;direct 通道复用 estimator 端点;`SideChannel` 新增可选 overrides 参数,
|
|
17
|
+
共享传输层而不共享配置)。可观测性:新增 `advisor-outcome` 审计记录(content-free,
|
|
18
|
+
每阶段一条:summary / scoring / decay)与只读 HTTP 路由
|
|
19
|
+
`GET .../advisor-report?sessionId=`(部署级 opt-in 开关 `advisorReportRoute`,与 review
|
|
20
|
+
路由同骨架)。本轮刻意不提供客户端 UI。
|
|
21
|
+
|
|
5
22
|
## 0.4.0 - 2026-09-20
|
|
6
23
|
|
|
7
24
|
### Fixed
|
package/docs/installation.md
CHANGED
|
@@ -59,7 +59,31 @@ Open DeepSeek Harness settings → **Context compression selector**:
|
|
|
59
59
|
|
|
60
60
|
Like all selector settings, the value is frozen when a session first observes it — the gate affects newly observed sessions, never a task that is already running.
|
|
61
61
|
|
|
62
|
-
## 5.
|
|
62
|
+
## 5. Optional: the advisory relevance advisor
|
|
63
|
+
|
|
64
|
+
The plugin can keep statistics about how relevant the session's history still is — advisory only, it never decides or blocks anything. It is off by default; turn it on by editing the `presetOptions` section of the context-compression settings (settings JSON, no UI card this round):
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
"presetOptions": {
|
|
68
|
+
"advisorMode": "host",
|
|
69
|
+
"advisorRefreshTurns": 8,
|
|
70
|
+
"advisorScoreThreshold": 0.35,
|
|
71
|
+
"advisorSampleLimit": 16,
|
|
72
|
+
"advisorMinTokens": 250,
|
|
73
|
+
"advisorTimeoutMs": 8000
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`advisorMode: "host"` calls the harness `llm` service; `"direct"` reuses the estimator's
|
|
78
|
+
`estimatorBaseUrl` / `estimatorApiKey` / `estimatorModel` endpoint. At every turn boundary
|
|
79
|
+
the advisor (1) summarizes the current task from the most recent `todo/write` event,
|
|
80
|
+
(2) incrementally scores historical tool results for content-and-comment relevance, and
|
|
81
|
+
(3) records a prefix-decay figure. Results surface as `advisor-outcome` audit records and,
|
|
82
|
+
with the deployment flag `advisorReportRoute: true`, a read-only
|
|
83
|
+
`GET .../advisor-report?sessionId=` HTTP route. What the advisor reports can never
|
|
84
|
+
suppress, delay, or rewrite any reduction that would land.
|
|
85
|
+
|
|
86
|
+
## 6. Update or remove
|
|
63
87
|
|
|
64
88
|
```sh
|
|
65
89
|
# update: pull, rebuild, repack, and add the new tarball again
|
package/docs/installation.zh.md
CHANGED
|
@@ -57,7 +57,30 @@ dsh --profile web --dump-config
|
|
|
57
57
|
|
|
58
58
|
与所有选择器设置一致,取值在会话首次观察时冻结——这道门只影响新观察的会话,不会改变正在运行的任务。
|
|
59
59
|
|
|
60
|
-
## 5.
|
|
60
|
+
## 5. 可选:建议型相关度 advisor
|
|
61
|
+
|
|
62
|
+
插件可以对"会话历史还有多相关"做统计——仅建议性质,不做任何决策、也不阻断任何流程。默认关闭;本轮没有设置卡片,请直接编辑上下文压缩设置中的 `presetOptions` 段(settings JSON):
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
"presetOptions": {
|
|
66
|
+
"advisorMode": "host",
|
|
67
|
+
"advisorRefreshTurns": 8,
|
|
68
|
+
"advisorScoreThreshold": 0.35,
|
|
69
|
+
"advisorSampleLimit": 16,
|
|
70
|
+
"advisorMinTokens": 250,
|
|
71
|
+
"advisorTimeoutMs": 8000
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`advisorMode: "host"` 走 harness `llm` 服务;`"direct"` 复用 estimator 的
|
|
76
|
+
`estimatorBaseUrl` / `estimatorApiKey` / `estimatorModel` 端点。每个 turn 边界,advisor
|
|
77
|
+
会:(1) 从最近的 `todo/write` 事件总结当前任务语义;(2) 对历史 tool result 做"内容+注释
|
|
78
|
+
语义 ↔ 当前任务"的增量相关度打分;(3) 记录前缀腐化度(prefix-decay)。结果以
|
|
79
|
+
`advisor-outcome` 审计记录呈现;部署配置打开 `advisorReportRoute: true` 后,还可经只读
|
|
80
|
+
HTTP 路由 `GET .../advisor-report?sessionId=` 读取。advisor 报告的任何内容都不会抑制、
|
|
81
|
+
延迟或改写任何本应落地的 reduction。
|
|
82
|
+
|
|
83
|
+
## 6. 更新或卸载
|
|
61
84
|
|
|
62
85
|
```sh
|
|
63
86
|
# 更新:拉取、重建、重新打包、再次添加新 tarball
|
package/package.json
CHANGED
|
@@ -394,7 +394,13 @@ function parsePresetOptionsSettings(value) {
|
|
|
394
394
|
"reviewMode",
|
|
395
395
|
"reviewTimeoutTurns",
|
|
396
396
|
"cacheHitDiscountAlpha",
|
|
397
|
-
"reviewHighImpactTokens"
|
|
397
|
+
"reviewHighImpactTokens",
|
|
398
|
+
"advisorMode",
|
|
399
|
+
"advisorTimeoutMs",
|
|
400
|
+
"advisorRefreshTurns",
|
|
401
|
+
"advisorScoreThreshold",
|
|
402
|
+
"advisorSampleLimit",
|
|
403
|
+
"advisorMinTokens"
|
|
398
404
|
]);
|
|
399
405
|
const unknown = Object.keys(value).find((key) => !allowed.has(key));
|
|
400
406
|
if (unknown !== void 0) throw new TypeError(`Context-compression presetOptions: unknown key "${unknown}"`);
|
|
@@ -410,6 +416,18 @@ function parsePresetOptionsSettings(value) {
|
|
|
410
416
|
}
|
|
411
417
|
const estimatorMode = value.estimatorMode;
|
|
412
418
|
if (estimatorMode !== void 0 && estimatorMode !== "" && estimatorMode !== "host" && estimatorMode !== "direct") throw new TypeError("Context-compression presetOptions.estimatorMode must be \"\", \"host\", or \"direct\"");
|
|
419
|
+
const advisorMode = value.advisorMode;
|
|
420
|
+
if (advisorMode !== void 0 && advisorMode !== "" && advisorMode !== "host" && advisorMode !== "direct") throw new TypeError("Context-compression presetOptions.advisorMode must be \"\", \"host\", or \"direct\"");
|
|
421
|
+
const advisorTimeoutMs = value.advisorTimeoutMs;
|
|
422
|
+
if (advisorTimeoutMs !== void 0 && (typeof advisorTimeoutMs !== "number" || !Number.isSafeInteger(advisorTimeoutMs) || advisorTimeoutMs < 100 || advisorTimeoutMs > 6e4)) throw new TypeError("Context-compression presetOptions.advisorTimeoutMs must be an integer between 100 and 60000");
|
|
423
|
+
const advisorRefreshTurns = value.advisorRefreshTurns;
|
|
424
|
+
if (advisorRefreshTurns !== void 0 && (typeof advisorRefreshTurns !== "number" || !Number.isSafeInteger(advisorRefreshTurns) || advisorRefreshTurns < 1)) throw new TypeError("Context-compression presetOptions.advisorRefreshTurns must be an integer of at least 1");
|
|
425
|
+
const advisorScoreThreshold = value.advisorScoreThreshold;
|
|
426
|
+
if (advisorScoreThreshold !== void 0 && (typeof advisorScoreThreshold !== "number" || !Number.isFinite(advisorScoreThreshold) || advisorScoreThreshold <= 0 || advisorScoreThreshold >= 1)) throw new TypeError("Context-compression presetOptions.advisorScoreThreshold must be a number strictly between 0 and 1");
|
|
427
|
+
const advisorSampleLimit = value.advisorSampleLimit;
|
|
428
|
+
if (advisorSampleLimit !== void 0 && (typeof advisorSampleLimit !== "number" || !Number.isSafeInteger(advisorSampleLimit) || advisorSampleLimit < 1 || advisorSampleLimit > 64)) throw new TypeError("Context-compression presetOptions.advisorSampleLimit must be an integer between 1 and 64");
|
|
429
|
+
const advisorMinTokens = value.advisorMinTokens;
|
|
430
|
+
if (advisorMinTokens !== void 0 && (typeof advisorMinTokens !== "number" || !Number.isSafeInteger(advisorMinTokens) || advisorMinTokens < 1)) throw new TypeError("Context-compression presetOptions.advisorMinTokens must be a positive integer");
|
|
413
431
|
const estimatorTimeoutMs = value.estimatorTimeoutMs;
|
|
414
432
|
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");
|
|
415
433
|
const reviewTimeoutTurns = value.reviewTimeoutTurns;
|
|
@@ -442,6 +460,12 @@ function parsePresetOptionsSettings(value) {
|
|
|
442
460
|
if (reviewTimeoutTurns !== void 0) result.reviewTimeoutTurns = reviewTimeoutTurns;
|
|
443
461
|
if (cacheHitDiscountAlpha !== void 0) result.cacheHitDiscountAlpha = cacheHitDiscountAlpha;
|
|
444
462
|
if (reviewHighImpactTokens !== void 0) result.reviewHighImpactTokens = reviewHighImpactTokens;
|
|
463
|
+
if (advisorMode !== void 0) result.advisorMode = advisorMode;
|
|
464
|
+
if (advisorTimeoutMs !== void 0) result.advisorTimeoutMs = advisorTimeoutMs;
|
|
465
|
+
if (advisorRefreshTurns !== void 0) result.advisorRefreshTurns = advisorRefreshTurns;
|
|
466
|
+
if (advisorScoreThreshold !== void 0) result.advisorScoreThreshold = advisorScoreThreshold;
|
|
467
|
+
if (advisorSampleLimit !== void 0) result.advisorSampleLimit = advisorSampleLimit;
|
|
468
|
+
if (advisorMinTokens !== void 0) result.advisorMinTokens = advisorMinTokens;
|
|
445
469
|
return result;
|
|
446
470
|
}
|
|
447
471
|
/** Settings schema used by the user-facing profile selector. */
|
|
@@ -660,7 +684,15 @@ const PRESET_OPTION_DEFAULTS = deepFreeze({
|
|
|
660
684
|
reviewMode: false,
|
|
661
685
|
reviewTimeoutTurns: 6,
|
|
662
686
|
cacheHitDiscountAlpha: .1,
|
|
663
|
-
reviewHighImpactTokens: 4e3
|
|
687
|
+
reviewHighImpactTokens: 4e3,
|
|
688
|
+
advisor: {
|
|
689
|
+
mode: "",
|
|
690
|
+
timeoutMs: 8e3,
|
|
691
|
+
refreshTurns: 8,
|
|
692
|
+
scoreThreshold: .35,
|
|
693
|
+
sampleLimit: 16,
|
|
694
|
+
minTokens: 250
|
|
695
|
+
}
|
|
664
696
|
});
|
|
665
697
|
/**
|
|
666
698
|
* Merge persisted presetOptions overrides over the tokenpilot-inspired
|
|
@@ -680,7 +712,15 @@ function mergePresetOptions(overrides) {
|
|
|
680
712
|
reviewMode: overrides.reviewMode ?? PRESET_OPTION_DEFAULTS.reviewMode,
|
|
681
713
|
reviewTimeoutTurns: overrides.reviewTimeoutTurns ?? PRESET_OPTION_DEFAULTS.reviewTimeoutTurns,
|
|
682
714
|
cacheHitDiscountAlpha: overrides.cacheHitDiscountAlpha ?? PRESET_OPTION_DEFAULTS.cacheHitDiscountAlpha,
|
|
683
|
-
reviewHighImpactTokens: overrides.reviewHighImpactTokens ?? PRESET_OPTION_DEFAULTS.reviewHighImpactTokens
|
|
715
|
+
reviewHighImpactTokens: overrides.reviewHighImpactTokens ?? PRESET_OPTION_DEFAULTS.reviewHighImpactTokens,
|
|
716
|
+
advisor: {
|
|
717
|
+
mode: overrides.advisorMode ?? PRESET_OPTION_DEFAULTS.advisor.mode,
|
|
718
|
+
timeoutMs: overrides.advisorTimeoutMs ?? PRESET_OPTION_DEFAULTS.advisor.timeoutMs,
|
|
719
|
+
refreshTurns: overrides.advisorRefreshTurns ?? PRESET_OPTION_DEFAULTS.advisor.refreshTurns,
|
|
720
|
+
scoreThreshold: overrides.advisorScoreThreshold ?? PRESET_OPTION_DEFAULTS.advisor.scoreThreshold,
|
|
721
|
+
sampleLimit: overrides.advisorSampleLimit ?? PRESET_OPTION_DEFAULTS.advisor.sampleLimit,
|
|
722
|
+
minTokens: overrides.advisorMinTokens ?? PRESET_OPTION_DEFAULTS.advisor.minTokens
|
|
723
|
+
}
|
|
684
724
|
});
|
|
685
725
|
}
|
|
686
726
|
/**
|
|
@@ -1084,5 +1124,66 @@ function registerReviewPruner(pruner) {
|
|
|
1084
1124
|
function resolveReviewPruner() {
|
|
1085
1125
|
return live.values().next().value;
|
|
1086
1126
|
}
|
|
1127
|
+
const advisorStates = /* @__PURE__ */ new WeakMap();
|
|
1128
|
+
/**
|
|
1129
|
+
* The per-session advisor state, created on first touch.
|
|
1130
|
+
* @param session - the session to key the state on (by object identity).
|
|
1131
|
+
*/
|
|
1132
|
+
function getAdvisorState(session) {
|
|
1133
|
+
let state = advisorStates.get(session);
|
|
1134
|
+
if (state === void 0) {
|
|
1135
|
+
state = {
|
|
1136
|
+
todoVersion: void 0,
|
|
1137
|
+
summary: void 0,
|
|
1138
|
+
lastSummaryTurn: -1,
|
|
1139
|
+
watermarkSeq: 0,
|
|
1140
|
+
scores: /* @__PURE__ */ new Map(),
|
|
1141
|
+
recertified: /* @__PURE__ */ new Map(),
|
|
1142
|
+
failures: void 0,
|
|
1143
|
+
inFlight: false,
|
|
1144
|
+
lastDecay: void 0
|
|
1145
|
+
};
|
|
1146
|
+
advisorStates.set(session, state);
|
|
1147
|
+
}
|
|
1148
|
+
return state;
|
|
1149
|
+
}
|
|
1150
|
+
/**
|
|
1151
|
+
* Insert or refresh one score with LRU semantics: a re-touched seq moves to
|
|
1152
|
+
* the newest position, and the oldest entry is evicted once the map exceeds
|
|
1153
|
+
* {@link ADVISOR_SCORES_LIMIT}.
|
|
1154
|
+
*/
|
|
1155
|
+
function recordScore(state, seq, entry) {
|
|
1156
|
+
state.scores.delete(seq);
|
|
1157
|
+
state.scores.set(seq, entry);
|
|
1158
|
+
if (state.scores.size > 64) {
|
|
1159
|
+
const oldest = state.scores.keys().next();
|
|
1160
|
+
if (oldest.done !== true) state.scores.delete(oldest.value);
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
/**
|
|
1164
|
+
* Mark one seq as LLM-recertified low relevance (a suggestion for later
|
|
1165
|
+
* history-aggressiveness decisions, consumed by nothing in this round).
|
|
1166
|
+
* Bounded at {@link ADVISOR_RECERTIFIED_LIMIT} with the same LRU eviction.
|
|
1167
|
+
*/
|
|
1168
|
+
function recordRecertified(state, seq, turn) {
|
|
1169
|
+
state.recertified.delete(seq);
|
|
1170
|
+
state.recertified.set(seq, turn);
|
|
1171
|
+
if (state.recertified.size > 64) {
|
|
1172
|
+
const oldest = state.recertified.keys().next();
|
|
1173
|
+
if (oldest.done !== true) state.recertified.delete(oldest.value);
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
/**
|
|
1177
|
+
* Drop every cached artifact that depends on the task semantics: a changed
|
|
1178
|
+
* todo version invalidates the summary and makes all eligible candidates
|
|
1179
|
+
* rescore-worthy (the watermark alone would otherwise hide them).
|
|
1180
|
+
*/
|
|
1181
|
+
function invalidateOnTaskChange(state, todoVersion) {
|
|
1182
|
+
if (state.todoVersion === todoVersion) return false;
|
|
1183
|
+
state.todoVersion = todoVersion;
|
|
1184
|
+
state.summary = void 0;
|
|
1185
|
+
state.lastSummaryTurn = -1;
|
|
1186
|
+
return true;
|
|
1187
|
+
}
|
|
1087
1188
|
//#endregion
|
|
1088
|
-
export {
|
|
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 };
|
|
@@ -89,6 +89,13 @@ interface PresetOptionsSettings {
|
|
|
89
89
|
readonly reviewTimeoutTurns?: number;
|
|
90
90
|
readonly cacheHitDiscountAlpha?: number;
|
|
91
91
|
readonly reviewHighImpactTokens?: number;
|
|
92
|
+
/** Advisory advisor channel; `''` (the default) keeps the advisor off. */
|
|
93
|
+
readonly advisorMode?: '' | 'host' | 'direct';
|
|
94
|
+
readonly advisorTimeoutMs?: number;
|
|
95
|
+
readonly advisorRefreshTurns?: number;
|
|
96
|
+
readonly advisorScoreThreshold?: number;
|
|
97
|
+
readonly advisorSampleLimit?: number;
|
|
98
|
+
readonly advisorMinTokens?: number;
|
|
92
99
|
}
|
|
93
100
|
/** Durable settings section owned by this package. */
|
|
94
101
|
interface ContextCompressionSettings {
|
|
@@ -82,7 +82,13 @@ window.__ModuleLoader__.load({
|
|
|
82
82
|
"reviewMode",
|
|
83
83
|
"reviewTimeoutTurns",
|
|
84
84
|
"cacheHitDiscountAlpha",
|
|
85
|
-
"reviewHighImpactTokens"
|
|
85
|
+
"reviewHighImpactTokens",
|
|
86
|
+
"advisorMode",
|
|
87
|
+
"advisorTimeoutMs",
|
|
88
|
+
"advisorRefreshTurns",
|
|
89
|
+
"advisorScoreThreshold",
|
|
90
|
+
"advisorSampleLimit",
|
|
91
|
+
"advisorMinTokens"
|
|
86
92
|
]);
|
|
87
93
|
if (Object.keys(value).some((key) => !allowed.has(key))) return void 0;
|
|
88
94
|
for (const key of [
|
|
@@ -97,6 +103,8 @@ window.__ModuleLoader__.load({
|
|
|
97
103
|
}
|
|
98
104
|
const estimatorMode = value.estimatorMode;
|
|
99
105
|
if (estimatorMode !== void 0 && estimatorMode !== "" && estimatorMode !== "host" && estimatorMode !== "direct") return;
|
|
106
|
+
const advisorMode = value.advisorMode;
|
|
107
|
+
if (advisorMode !== void 0 && advisorMode !== "" && advisorMode !== "host" && advisorMode !== "direct") return;
|
|
100
108
|
for (const key of [
|
|
101
109
|
"estimatorProvider",
|
|
102
110
|
"estimatorModel",
|
|
@@ -114,6 +122,16 @@ window.__ModuleLoader__.load({
|
|
|
114
122
|
if (cacheHitDiscountAlpha !== void 0 && (typeof cacheHitDiscountAlpha !== "number" || !Number.isFinite(cacheHitDiscountAlpha) || cacheHitDiscountAlpha <= 0 || cacheHitDiscountAlpha >= 1)) return;
|
|
115
123
|
const reviewHighImpactTokens = value.reviewHighImpactTokens;
|
|
116
124
|
if (reviewHighImpactTokens !== void 0 && (typeof reviewHighImpactTokens !== "number" || !Number.isSafeInteger(reviewHighImpactTokens) || reviewHighImpactTokens < 0)) return;
|
|
125
|
+
const advisorTimeoutMs = value.advisorTimeoutMs;
|
|
126
|
+
if (advisorTimeoutMs !== void 0 && (typeof advisorTimeoutMs !== "number" || !Number.isSafeInteger(advisorTimeoutMs) || advisorTimeoutMs < 100 || advisorTimeoutMs > 6e4)) return;
|
|
127
|
+
const advisorRefreshTurns = value.advisorRefreshTurns;
|
|
128
|
+
if (advisorRefreshTurns !== void 0 && (typeof advisorRefreshTurns !== "number" || !Number.isSafeInteger(advisorRefreshTurns) || advisorRefreshTurns < 1)) return;
|
|
129
|
+
const advisorScoreThreshold = value.advisorScoreThreshold;
|
|
130
|
+
if (advisorScoreThreshold !== void 0 && (typeof advisorScoreThreshold !== "number" || !Number.isFinite(advisorScoreThreshold) || advisorScoreThreshold <= 0 || advisorScoreThreshold >= 1)) return;
|
|
131
|
+
const advisorSampleLimit = value.advisorSampleLimit;
|
|
132
|
+
if (advisorSampleLimit !== void 0 && (typeof advisorSampleLimit !== "number" || !Number.isSafeInteger(advisorSampleLimit) || advisorSampleLimit < 1 || advisorSampleLimit > 64)) return;
|
|
133
|
+
const advisorMinTokens = value.advisorMinTokens;
|
|
134
|
+
if (advisorMinTokens !== void 0 && (typeof advisorMinTokens !== "number" || !Number.isSafeInteger(advisorMinTokens) || advisorMinTokens < 1)) return;
|
|
117
135
|
const decoded = {};
|
|
118
136
|
if (value.dedupeToolResults !== void 0) decoded.dedupeToolResults = value.dedupeToolResults;
|
|
119
137
|
if (value.summaryLocator !== void 0) decoded.summaryLocator = value.summaryLocator;
|
|
@@ -129,6 +147,12 @@ window.__ModuleLoader__.load({
|
|
|
129
147
|
if (reviewTimeoutTurns !== void 0) decoded.reviewTimeoutTurns = reviewTimeoutTurns;
|
|
130
148
|
if (cacheHitDiscountAlpha !== void 0) decoded.cacheHitDiscountAlpha = cacheHitDiscountAlpha;
|
|
131
149
|
if (reviewHighImpactTokens !== void 0) decoded.reviewHighImpactTokens = reviewHighImpactTokens;
|
|
150
|
+
if (advisorMode !== void 0) decoded.advisorMode = advisorMode;
|
|
151
|
+
if (advisorTimeoutMs !== void 0) decoded.advisorTimeoutMs = advisorTimeoutMs;
|
|
152
|
+
if (advisorRefreshTurns !== void 0) decoded.advisorRefreshTurns = advisorRefreshTurns;
|
|
153
|
+
if (advisorScoreThreshold !== void 0) decoded.advisorScoreThreshold = advisorScoreThreshold;
|
|
154
|
+
if (advisorSampleLimit !== void 0) decoded.advisorSampleLimit = advisorSampleLimit;
|
|
155
|
+
if (advisorMinTokens !== void 0) decoded.advisorMinTokens = advisorMinTokens;
|
|
132
156
|
return decoded;
|
|
133
157
|
}
|
|
134
158
|
/**
|
|
@@ -1544,7 +1568,13 @@ window.__ModuleLoader__.load({
|
|
|
1544
1568
|
"reviewMode",
|
|
1545
1569
|
"reviewTimeoutTurns",
|
|
1546
1570
|
"cacheHitDiscountAlpha",
|
|
1547
|
-
"reviewHighImpactTokens"
|
|
1571
|
+
"reviewHighImpactTokens",
|
|
1572
|
+
"advisorMode",
|
|
1573
|
+
"advisorTimeoutMs",
|
|
1574
|
+
"advisorRefreshTurns",
|
|
1575
|
+
"advisorScoreThreshold",
|
|
1576
|
+
"advisorSampleLimit",
|
|
1577
|
+
"advisorMinTokens"
|
|
1548
1578
|
];
|
|
1549
1579
|
/**
|
|
1550
1580
|
* Plan the path ops one patch needs against the section currently stored.
|
|
@@ -24,6 +24,13 @@ interface Config {
|
|
|
24
24
|
* transport and simply never appears.
|
|
25
25
|
*/
|
|
26
26
|
reviewQueueRoute?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* 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.
|
|
32
|
+
*/
|
|
33
|
+
advisorReportRoute?: boolean;
|
|
27
34
|
}
|
|
28
35
|
/** Loader validation for the standalone Bundle opt-in. */
|
|
29
36
|
declare const Config: z<Config>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { d as ContextCompressionSettingsSchema, o as resolveReviewPruner, t as getAdvisorState, u as CONTEXT_COMPRESSION_SETTINGS_NAMESPACE } from "./advisor-state.js";
|
|
2
2
|
import z from "@deepseek-ai/schemastery";
|
|
3
3
|
import "@deepseek-ai/dsh-settings";
|
|
4
4
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
@@ -459,6 +459,7 @@ const CONTEXT_COMPRESSION_NAMESPACE = CONTEXT_COMPRESSION_SETTINGS_NAMESPACE;
|
|
|
459
459
|
const ESTIMATOR_CATALOG_ROUTES = ["/endpoint/dsh-context-compression-improved/estimator-catalog", "/api/dsh-context-compression-improved/estimator-catalog"];
|
|
460
460
|
const REVIEW_QUEUE_ROUTES = ["/endpoint/dsh-context-compression-improved/review-queue", "/api/dsh-context-compression-improved/review-queue"];
|
|
461
461
|
const REVIEW_DECIDE_ROUTES = ["/endpoint/dsh-context-compression-improved/review-decide", "/api/dsh-context-compression-improved/review-decide"];
|
|
462
|
+
const ADVISOR_REPORT_ROUTES = ["/endpoint/dsh-context-compression-improved/advisor-report", "/api/dsh-context-compression-improved/advisor-report"];
|
|
462
463
|
/**
|
|
463
464
|
* Resolve the review pipeline for the top-level routes.
|
|
464
465
|
*
|
|
@@ -694,6 +695,102 @@ function registerReviewQueueRoutes(ctx) {
|
|
|
694
695
|
log("warn", "context-compression webServer not active yet — review routes pending: %s", REVIEW_QUEUE_ROUTES.join(", "));
|
|
695
696
|
}
|
|
696
697
|
/**
|
|
698
|
+
* Serve the advisory advisor's read-only report route (same registration
|
|
699
|
+
* skeleton as the review routes):
|
|
700
|
+
*
|
|
701
|
+
* `GET .../advisor-report?sessionId=…` → the session's prefix-decay figure,
|
|
702
|
+
* the todolist-bound task summary, and the score distribution. Content-free
|
|
703
|
+
* by construction: task semantics are LLM-derived summaries, never message
|
|
704
|
+
* text, and no score reason or candidate preview is ever returned.
|
|
705
|
+
* Unknown session → 404; no agents service → 503. A session whose advisor
|
|
706
|
+
* never ran reports nulls and empty arrays, not an error.
|
|
707
|
+
*/
|
|
708
|
+
function registerAdvisorReportRoute(ctx) {
|
|
709
|
+
const readService = (name) => {
|
|
710
|
+
try {
|
|
711
|
+
return ctx.get(name);
|
|
712
|
+
} catch {
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
};
|
|
716
|
+
const log = (level, message, ...args) => {
|
|
717
|
+
console[level](message, ...args);
|
|
718
|
+
};
|
|
719
|
+
const getHandler = (req, res) => {
|
|
720
|
+
if (typeof readService("agents")?.get !== "function") {
|
|
721
|
+
reviewJson(res, 503, {
|
|
722
|
+
ok: false,
|
|
723
|
+
error: "advisor report unavailable"
|
|
724
|
+
});
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
let sessionId = "";
|
|
728
|
+
try {
|
|
729
|
+
sessionId = new URL(String(req.url ?? ""), "http://localhost").searchParams.get("sessionId") ?? "";
|
|
730
|
+
} catch {}
|
|
731
|
+
if (sessionId === "") {
|
|
732
|
+
reviewJson(res, 400, {
|
|
733
|
+
ok: false,
|
|
734
|
+
error: "sessionId is required"
|
|
735
|
+
});
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
const session = sessionFor(readService, sessionId);
|
|
739
|
+
if (session === void 0) {
|
|
740
|
+
reviewJson(res, 404, {
|
|
741
|
+
ok: false,
|
|
742
|
+
error: "unknown session"
|
|
743
|
+
});
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
const state = getAdvisorState(session);
|
|
747
|
+
reviewJson(res, 200, {
|
|
748
|
+
ok: true,
|
|
749
|
+
sessionId,
|
|
750
|
+
advisor: {
|
|
751
|
+
summary: state.summary ?? null,
|
|
752
|
+
decay: state.lastDecay?.decay ?? null,
|
|
753
|
+
weightedChars: state.lastDecay?.weightedChars ?? null,
|
|
754
|
+
decayTurn: state.lastDecay?.turn ?? null,
|
|
755
|
+
scores: [...state.scores].map(([seq, entry]) => ({
|
|
756
|
+
seq,
|
|
757
|
+
score: entry.score,
|
|
758
|
+
turn: entry.turn
|
|
759
|
+
})),
|
|
760
|
+
lowRelevanceSeqs: [...state.recertified.keys()]
|
|
761
|
+
}
|
|
762
|
+
});
|
|
763
|
+
};
|
|
764
|
+
const register = (webServer) => {
|
|
765
|
+
const disposers = [...ADVISOR_REPORT_ROUTES].map((path) => ({
|
|
766
|
+
path,
|
|
767
|
+
handler: getHandler
|
|
768
|
+
})).map((entry) => webServer.register({
|
|
769
|
+
kind: "exact",
|
|
770
|
+
path: entry.path,
|
|
771
|
+
handler: entry.handler
|
|
772
|
+
})).filter((off) => typeof off === "function");
|
|
773
|
+
ctx.effect(() => () => {
|
|
774
|
+
for (const off of disposers) off();
|
|
775
|
+
}, "contextCompressionSelector.advisor report route");
|
|
776
|
+
log("info", "context-compression advisor report route registered: %s", ADVISOR_REPORT_ROUTES.join(", "));
|
|
777
|
+
};
|
|
778
|
+
const active = asWebServer(readService("webServer"));
|
|
779
|
+
if (active !== void 0) {
|
|
780
|
+
register(active);
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
ctx.inject(["webServer"], (injected) => {
|
|
784
|
+
const webServer = asWebServer(injected.webServer);
|
|
785
|
+
if (webServer === void 0) {
|
|
786
|
+
log("warn", "context-compression webServer exposes no register() — advisor report route not registered");
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
register(webServer);
|
|
790
|
+
});
|
|
791
|
+
log("warn", "context-compression webServer not active yet — advisor report route pending: %s", ADVISOR_REPORT_ROUTES.join(", "));
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
697
794
|
* The one service the catalog route actually needs. `llm` and
|
|
698
795
|
* `agentDefaultModel` are payload enrichment the handler resolves per request,
|
|
699
796
|
* never reasons to withhold the route.
|
|
@@ -797,7 +894,8 @@ const SHARED_SETTINGS = Symbol.for("dsh-context-compression-improved/settings-re
|
|
|
797
894
|
const Config = z.object({
|
|
798
895
|
presetOverlay: z.boolean().default(false),
|
|
799
896
|
estimatorCatalogRoute: z.boolean().default(false),
|
|
800
|
-
reviewQueueRoute: z.boolean().default(false)
|
|
897
|
+
reviewQueueRoute: z.boolean().default(false),
|
|
898
|
+
advisorReportRoute: z.boolean().default(false)
|
|
801
899
|
});
|
|
802
900
|
/** Register the persisted default read by the currently mounted root pruner. */
|
|
803
901
|
function apply(ctx, config = {}) {
|
|
@@ -807,6 +905,7 @@ function apply(ctx, config = {}) {
|
|
|
807
905
|
});
|
|
808
906
|
if (config.estimatorCatalogRoute === true) registerEstimatorCatalogRoute(ctx);
|
|
809
907
|
if (config.reviewQueueRoute === true) registerReviewQueueRoutes(ctx);
|
|
908
|
+
if (config.advisorReportRoute === true) registerAdvisorReportRoute(ctx);
|
|
810
909
|
if (config.presetOverlay !== true) return;
|
|
811
910
|
ctx.inject(["agentPresets"], (presetsCtx) => {
|
|
812
911
|
const installation = decorateAgentPresets(presetsCtx.agentPresets, {
|