@goodandready/dsh-moa 0.2.72 → 0.2.74

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -186,6 +186,15 @@ Validates markdown code blocks across all candidate outputs locally using JavaSc
186
186
  ### 20. Early Exit upon Semantic Consensus
187
187
  Compares normalized candidate proposals using token-level Sørensen-Dice similarity. When models arrive at identical or near-identical solutions (meeting or exceeding `early_exit_threshold`, default: `0.95`), the expensive Judge synthesis phase is automatically bypassed. The consensus solution is promoted directly to the workspace, saving up to 100% of judge token costs while accelerating pipeline latency. Configured via `early_exit_consensus_enabled: true` and `early_exit_threshold: 0.95`.
188
188
 
189
+ ### 21. Dynamic Consensus Threshold & Multi-Judge Escalation
190
+ Evaluates pairwise candidate solution divergence (Disagreement Metric, 0.0–1.0) using normalized Sørensen-Dice similarity. When candidate consensus is weak (disagreement meets or exceeds `escalation_disagreement_threshold`, default: `0.40`), the pipeline automatically escalates:
191
+ - `auto_upgrade`: Upgrades from a lightweight or fast aggregator to a designated flagship senior judge (`escalation_judge` or first senior fallback judge).
192
+ - `council_majority`: Convenes a 3-judge panel with majority consensus voting.
193
+ Dispatches `moa/judge-escalation` Cordis events and displays escalation decisions in live stream telemetry. Configured via `escalation_policy: 'single' | 'auto_upgrade' | 'council_majority'`, `escalation_disagreement_threshold: 0.40`, and `escalation_judge`.
194
+
195
+ ### 22. Self-Consistency Temperature Sampling for Single Models
196
+ Allows single-model configurations (such as a local Ollama/vLLM instance or a single API key) to leverage Mixture-of-Agents diversity. When enabled on a preset with 1 model slot, the runner expands it into $N$ parallel candidate streams with distinct temperatures (e.g. 0.2, 0.6, 0.9) and complementary personas (`proposer`, `challenger`, `minimalist`), calculates agreement ratio, and performs full MoA consensus synthesis. Dispatches `moa/self-consistency-expand` Cordis events. Configured via `self_consistency_enabled: true`, `self_consistency_samples: 3`, and `self_consistency_temperatures: [0.2, 0.6, 0.9]`.
197
+
189
198
  ### 6. Live Canvas 1-Click Preview (optional)
190
199
  If `@goodandready/dsh-live-canvas` is installed in the same profile, `dsh-moa` pushes the promoted HTML file to the Live Canvas REST contract (`POST /dsh-live-canvas/api/preview`, served by the same harness webServer) and appends a one-click preview link (`/dsh-live-canvas/sandbox/<id>`) to the answer. Without the plugin the step is skipped silently — no errors in the log, no dead links.
191
200
 
@@ -327,6 +336,8 @@ dsh-moa:
327
336
  | `/dsh-moa/run` | `POST` | Runs the full MoA pipeline over HTTP (400 when `enabled: false`) |
328
337
  | `/dsh-moa/diff` | `GET` | Computes line-by-line diff between candidate runs or curator synthesis |
329
338
  | `/dsh-moa/promote` | `POST` | Manually promotes candidate workspace files to project root |
339
+ | `/dsh-moa/cache` | `GET` | Returns semantic cache stats, hit/miss counters, and saved token metrics |
340
+ | `/dsh-moa/cache` | `POST` | Purges cached candidate generations with optional olderThanMs filter |
330
341
  | `/api/dsh-moa/update` | `POST` | One-click plugin updater from npm with safe-write verification |
331
342
 
332
343
  ---
package/README.ru.md CHANGED
@@ -186,6 +186,15 @@ graph TD
186
186
  ### 20. Ранний выход по семантическому консенсусу
187
187
  Сравнивает нормализованные решения кандидатов по токенному сходству Сёренсена-Дайса. Если решения моделей совпадают или практически идентичны (достигнут порог `early_exit_threshold`, по умолчанию 0.95), стадия вызова судьи (Judge) пропускается. Согласованный вариант немедленно продвигается в проект, экономя до 100% токенов судейской коллегии и сокращая задержку ответа. Управляется параметрами `early_exit_consensus_enabled: true` и `early_exit_threshold: 0.95`.
188
188
 
189
+ ### 21. Динамический порог консенсуса и эскалация судейства (Judge Escalation)
190
+ Оценивает степень расхождения между решениями моделей-кандидатов (метрика Disagreement Metric, 0.0–1.0) на основе попарного расстояния Сёренсена-Дайса. Если расхождение превышает порог (`escalation_disagreement_threshold`, по умолчанию 0.40), пайплайн выполняет адаптивную эскалацию:
191
+ - `auto_upgrade`: автоматический переход с базового/быстрого судьи на флагманскую старшую модель (`escalation_judge` или первый доступный резервный судья).
192
+ - `council_majority`: созыв коллегии из 3 судей с голосованием большинством голосов.
193
+ Генерирует событие `moa/judge-escalation` и выводит статус эскалации в телеметрию стрима.
194
+
195
+ ### 22. Самосогласованное температурное сэмплирование для одиночных моделей (Self-Consistency)
196
+ Позволяет пользователям с единственной моделью (локальная Ollama, vLLM или единственный API-ключ) получать преимущества ансамблевой валидации MoA. Автоматически разворачивает одиночный слот на $N$ параллельных запусков с различными температурами (`0.2, 0.6, 0.9`) и дополняющими друг друга инженерными персонами (`proposer`, `challenger`, `minimalist`), вычисляет коэффициент согласия (Self-Consistency Agreement) и синтезирует итоговый результат. Генерирует событие `moa/self-consistency-expand`.
197
+
189
198
  ### 6. Live Canvas 1-клик предпросмотр (опционально)
190
199
  Если в профиле установлен `@goodandready/dsh-live-canvas`, `dsh-moa` отправляет промоученный HTML-файл в REST-контракт Live Canvas (`POST /dsh-live-canvas/api/preview`, тот же webServer харнесса) и добавляет к ответу ссылку на предпросмотр (`/dsh-live-canvas/sandbox/<id>`). Без плагина шаг пропускается тихо — без ошибок в журнале и без битых ссылок.
191
200
 
@@ -327,6 +336,8 @@ dsh-moa:
327
336
  | `/dsh-moa/run` | `POST` | Запускает полный пайплайн MoA по HTTP (400 при `enabled: false`) |
328
337
  | `/dsh-moa/diff` | `GET` | Вычисляет построчный diff между решениями кандидатов или синтезом куратора |
329
338
  | `/dsh-moa/promote` | `POST` | Ручной промоушн файлов кандидата в корень проекта с созданием снимка |
339
+ | `/dsh-moa/cache` | `GET` | Статистика семантического кэша, число попаданий и сэкономленные токены |
340
+ | `/dsh-moa/cache` | `POST` | Очистка семантического кэша с опциональным фильтром olderThanMs |
330
341
  | `/api/dsh-moa/update` | `POST` | One-click обновление плагина из npm с проверкой безопасности источника |
331
342
 
332
343
  ---
package/README.zh.md CHANGED
@@ -163,6 +163,15 @@ graph TD
163
163
  ### 20. 语义共识提前退出机制 (Early Exit upon Semantic Consensus)
164
164
  基于 Token 级 Sørensen-Dice 相似度算法比对归一化后的候选方案。当多个候选模型给出高度一致的代码方案(达到或超过 `early_exit_threshold` 阈值,默认 0.95)时,自动跳过高耗费的裁判综合评审阶段,直接将共识方案提升至项目,在大幅缩短响应延迟的同时节省多达 100% 的裁判 Token 支出。通过 `early_exit_consensus_enabled: true` 与 `early_exit_threshold: 0.95` 配置。
165
165
 
166
+ ### 21. 动态共识阈值与裁判自适应升级 (Dynamic Consensus & Judge Escalation)
167
+ 基于归一化 Sørensen-Dice 计算候选方案间的分歧度 (Disagreement Metric 0.0–1.0)。当候选分歧超过设定阈值 (`escalation_disagreement_threshold`,默认 0.40) 时,流水线自动触发升级策略:
168
+ - `auto_upgrade`: 自动将普通/快速裁判升级为指定的高阶旗舰裁判 (`escalation_judge` 或首选候补裁判)。
169
+ - `council_majority`: 组建 3 裁判多数席裁决团。
170
+ 触发 `moa/judge-escalation` 事件并在实时流中呈现决策依据。
171
+
172
+ ### 22. 单模型自洽性温度采样 (Self-Consistency Temperature Sampling)
173
+ 专为仅配置单模型(如本地 Ollama/vLLM 或单一 API Key)的场景设计。开启后自动将单槽位扩展为 $N$ 个并行采样(分别采用 0.2、0.6、0.9 等递增温度与 `proposer`、`challenger`、`minimalist` 互补提示词),评估自洽一致性比率并进行全流程 MoA 综合聚合,触发 `moa/self-consistency-expand` 事件。
174
+
166
175
  ### 6. Live Canvas 一键预览(可选)
167
176
  若同一 profile 中安装了 `@goodandready/dsh-live-canvas`,`dsh-moa` 会将提升后的 HTML 文件推送到 Live Canvas 的 REST 契约(`POST /dsh-live-canvas/api/preview`,由同一 harness webServer 提供服务),并在回答中附上一键预览链接(`/dsh-live-canvas/sandbox/<id>`)。未安装该插件时此步骤静默跳过——日志无报错,也不会出现死链接。
168
177
 
@@ -257,6 +266,8 @@ dsh-moa:
257
266
  | `/dsh-moa/run` | `POST` | 通过 HTTP 运行完整 MoA 管线(`enabled: false` 时返回 400) |
258
267
  | `/dsh-moa/diff` | `GET` | 计算候选运行结果或综合输出的行级别差异(支持按 runId 解析工作区) |
259
268
  | `/dsh-moa/promote` | `POST` | 手动将候选工作区文件提升到项目根目录(带时间机器快照) |
269
+ | `/dsh-moa/cache` | `GET` | 返回语义缓存统计信息、命中次数与节省 Token 指标 |
270
+ | `/dsh-moa/cache` | `POST` | 清除语义缓存条目,支持 olderThanMs 过滤 |
260
271
  | `/api/dsh-moa/update` | `POST` | 从 npm 一键更新插件,带安全写入源校验 |
261
272
 
262
273
  ---
package/lib/client.js CHANGED
@@ -167,6 +167,12 @@ window.__ModuleLoader__.load({
167
167
  "config.smart_routing_desc": "Analyze task complexity and automatically select the optimal preset.",
168
168
  "config.smart_routing_model": "Routing Model:",
169
169
  // Multi-Turn Context
170
+ "aggregator.context_pruning_title": "\u2702\uFE0F Cross-Candidate Context Pruning (Diff-Only Judge Context)",
171
+ "aggregator.context_pruning_desc": "Replaces unchanged base project files with compact unified diffs (---/+++ hunks) in the Judge prompt, slashing prompt tokens by up to 85%.",
172
+ "aggregator.context_pruning_lines": "Context lines around changes:",
173
+ "aggregator.semantic_cache_title": "\u26A1 Semantic Caching for Candidate Generations",
174
+ "aggregator.semantic_cache_desc": "Persists intermediate candidate outputs locally by deterministic SHA-256 fingerprint, eliminating duplicate API costs on retry or repeat requests.",
175
+ "aggregator.semantic_cache_ttl": "Cache TTL (hours):",
170
176
  "aggregator.multi_turn_title": "Multi-Turn Context & Diff Pruning",
171
177
  "aggregator.multi_turn_desc": "Retain conversation context across turns while pruning duplicate code blocks.",
172
178
  // UI States & Errors
@@ -207,7 +213,18 @@ window.__ModuleLoader__.load({
207
213
  "workbench.promote_btn": "Promote Harmonized Files to Workspace",
208
214
  "workbench.promoted_ok": "\u2705 Harmonized deliverable promoted to workspace root!",
209
215
  "workbench.pros": "Pros:",
210
- "workbench.cons": "Cons:"
216
+ "workbench.cons": "Cons:",
217
+ "aggregator.self_consistency_title": "\u{1F3B2} Self-Consistency Temperature Sampling (Single-Model Diversity)",
218
+ "aggregator.self_consistency_desc": "Expands a single reference model into multiple parallel samples with varied temperatures and personas, bringing MoA benefits to single-model setups.",
219
+ "aggregator.self_consistency_samples": "Samples:",
220
+ "aggregator.escalation_title": "\u2696\uFE0F Dynamic Consensus Threshold & Judge Escalation",
221
+ "aggregator.escalation_desc": "Detects divergence across candidates. When consensus is weak, automatically upgrades to a senior flagship judge or convenes a 3-judge panel.",
222
+ "aggregator.escalation_policy": "Escalation Policy:",
223
+ "aggregator.escalation_policy_single": "Standard Single Judge (No Escalation)",
224
+ "aggregator.escalation_policy_upgrade": "Auto-Upgrade to Senior Judge on Divergence",
225
+ "aggregator.escalation_policy_council": "Convene 3-Judge Majority Council on Divergence",
226
+ "aggregator.escalation_threshold": "Divergence Threshold:",
227
+ "aggregator.escalation_judge_label": "Senior Judge:"
211
228
  };
212
229
  const zh = {
213
230
  title: "\u667A\u80FD\u4F53\u6DF7\u5408 (MoA)",
@@ -365,6 +382,12 @@ window.__ModuleLoader__.load({
365
382
  "config.smart_routing_desc": "\u5206\u6790\u4EFB\u52A1\u590D\u6742\u5EA6\u5E76\u81EA\u52A8\u5339\u914D\u6700\u4F73\u9884\u8BBE\u3002",
366
383
  "config.smart_routing_model": "\u5206\u6D41\u51B3\u7B56\u6A21\u578B\uFF1A",
367
384
  // Multi-Turn Context
385
+ "aggregator.context_pruning_title": "\u2702\uFE0F \u5019\u9009\u8DE8\u6A21\u578B\u4E0A\u4E0B\u6587\u7CBE\u7B80 (Judge \u4EC5\u5DEE\u5F02\u8865\u4E01)",
386
+ "aggregator.context_pruning_desc": "\u5728 Judge \u63D0\u793A\u8BCD\u4E2D\u5C06\u672A\u4FEE\u6539\u7684\u57FA\u7840\u9879\u76EE\u6587\u4EF6\u66FF\u6362\u4E3A\u7D27\u51D1\u7684\u7EDF\u4E00\u5DEE\u5F02\u5757 (---/+++)\uFF0C\u5728\u5927\u6587\u4EF6\u573A\u666F\u4E0B\u524A\u51CF\u9AD8\u8FBE 85% \u7684\u63D0\u793A\u8BCD Token\u3002",
387
+ "aggregator.context_pruning_lines": "\u5DEE\u5F02\u4E0A\u4E0B\u6587\u884C\u6570:",
388
+ "aggregator.semantic_cache_title": "\u26A1 \u5019\u9009\u751F\u6210\u8BED\u4E49\u7F13\u5B58",
389
+ "aggregator.semantic_cache_desc": "\u901A\u8FC7\u786E\u5B9A\u7684 SHA-256 \u6307\u7EB9\u5728\u672C\u5730\u6301\u4E45\u5316\u5019\u9009\u6A21\u578B\u4E2D\u95F4\u8F93\u51FA\uFF0C\u5728\u91CD\u8BD5\u6216\u91CD\u590D\u8FED\u4EE3\u4E2D\u6D88\u9664\u5197\u4F59 API \u8D39\u7528\u3002",
390
+ "aggregator.semantic_cache_ttl": "\u7F13\u5B58\u6709\u6548\u671F (\u5C0F\u65F6):",
368
391
  "aggregator.multi_turn_title": "\u591A\u8F6E\u5BF9\u8BDD\u4E0A\u4E0B\u6587\u4E0E\u5DEE\u5F02\u526A\u679D",
369
392
  "aggregator.multi_turn_desc": "\u8DE8\u8F6E\u6B21\u4FDD\u7559\u5BF9\u8BDD\u4E0A\u4E0B\u6587\u5E76\u526A\u679D\u91CD\u590D\u4EE3\u7801\u5757\u3002",
370
393
  // UI States & Errors
@@ -405,7 +428,18 @@ window.__ModuleLoader__.load({
405
428
  "workbench.promote_btn": "\u63D0\u5347\u7C98\u5408\u7ED3\u679C\u81F3\u9879\u76EE\u6839\u76EE\u5F55",
406
429
  "workbench.promoted_ok": "\u2705 \u7C98\u5408\u65B9\u6848\u5DF2\u6210\u529F\u63D0\u5347\u81F3\u9879\u76EE\u5DE5\u4F5C\u533A\uFF01",
407
430
  "workbench.pros": "\u4F18\u52BF\uFF1A",
408
- "workbench.cons": "\u4E0D\u8DB3\uFF1A"
431
+ "workbench.cons": "\u4E0D\u8DB3\uFF1A",
432
+ "aggregator.self_consistency_title": "\u{1F3B2} \u5355\u6A21\u578B\u81EA\u6D3D\u6027\u6E29\u5EA6\u91C7\u6837 (Self-Consistency)",
433
+ "aggregator.self_consistency_desc": "\u5728\u4EC5\u914D\u7F6E\u5355\u6A21\u578B\u65F6\uFF0C\u751F\u6210\u4E0D\u540C\u6E29\u5EA6\u4E0E\u89D2\u8272\u7684\u591A\u4E2A\u5E76\u884C\u5019\u9009\u65B9\u6848\uFF0C\u8BA9\u672C\u5730 Ollama \u6216\u5355 Key \u4E5F\u4EAB\u6709 MoA \u805A\u5408\u4F18\u52BF\u3002",
434
+ "aggregator.self_consistency_samples": "\u91C7\u6837\u6570:",
435
+ "aggregator.escalation_title": "\u2696\uFE0F \u52A8\u6001\u5171\u8BC6\u9608\u503C\u4E0E\u88C1\u5224\u5347\u7EA7 (Judge Escalation)",
436
+ "aggregator.escalation_desc": "\u68C0\u6D4B\u5019\u9009\u65B9\u6848\u95F4\u7684\u5206\u6B67\u5EA6\u3002\u5F53\u5171\u8BC6\u8F83\u5F31\u65F6\uFF0C\u81EA\u52A8\u5347\u7EA7\u81F3\u9AD8\u9636\u65D7\u8230\u88C1\u5224\u6216\u7EC4\u5EFA 3 \u88C1\u5224\u591A\u6570\u5E2D\u3002",
437
+ "aggregator.escalation_policy": "\u5347\u7EA7\u7B56\u7565:",
438
+ "aggregator.escalation_policy_single": "\u6807\u51C6\u5355\u88C1\u5224 (\u4E0D\u5347\u7EA7)",
439
+ "aggregator.escalation_policy_upgrade": "\u5206\u6B67\u65F6\u81EA\u52A8\u5347\u7EA7\u81F3\u9AD8\u9636\u88C1\u5224",
440
+ "aggregator.escalation_policy_council": "\u5206\u6B67\u65F6\u7EC4\u5EFA 3 \u88C1\u5224\u591A\u6570\u51B3\u7B56\u5E2D",
441
+ "aggregator.escalation_threshold": "\u5206\u6B67\u9608\u503C:",
442
+ "aggregator.escalation_judge_label": "\u9AD8\u9636\u88C1\u5224\u6A21\u578B:"
409
443
  };
410
444
  function makeT(dict, fallback) {
411
445
  return function t(key, vars) {
@@ -2378,6 +2412,153 @@ window.__ModuleLoader__.load({
2378
2412
  t("aggregator.multi_turn_desc") || "Retain conversation context across turns while pruning duplicate code blocks."
2379
2413
  )
2380
2414
  ),
2415
+ /* Context Pruning (#305) & Semantic Caching (#318) */
2416
+ React.createElement(
2417
+ "div",
2418
+ { style: { display: "flex", flexDirection: "column", gap: 6, padding: "8px 12px", background: "var(--dsw-alias-bg-layer-2)", borderRadius: 8, border: "1px solid var(--dsw-alias-border-l2)", marginTop: 8 } },
2419
+ React.createElement(
2420
+ "label",
2421
+ { style: { display: "flex", alignItems: "center", gap: 8, cursor: "pointer", fontSize: 13, fontWeight: 500 } },
2422
+ React.createElement("input", {
2423
+ type: "checkbox",
2424
+ checked: currentPreset.context_pruning_enabled !== false,
2425
+ onChange: (e) => updateCurrentPreset((p) => ({ ...p, context_pruning_enabled: e.target.checked })),
2426
+ style: { accentColor: "var(--dsw-alias-state-brand-primary, var(--dsw-alias-label-primary))", cursor: "pointer" }
2427
+ }),
2428
+ t("aggregator.context_pruning_title") || "\u2702\uFE0F Cross-Candidate Context Pruning (Diff-Only Judge Context)"
2429
+ ),
2430
+ React.createElement(
2431
+ "div",
2432
+ { style: { fontSize: 12, color: "var(--dsw-alias-label-secondary)", marginLeft: 22 } },
2433
+ t("aggregator.context_pruning_desc") || "Replaces unchanged base project files with compact unified diffs (---/+++ hunks) in the Judge prompt, slashing prompt tokens by up to 85%."
2434
+ ),
2435
+ React.createElement(
2436
+ "label",
2437
+ { style: { display: "flex", alignItems: "center", gap: 8, cursor: "pointer", fontSize: 13, fontWeight: 500, marginTop: 4 } },
2438
+ React.createElement("input", {
2439
+ type: "checkbox",
2440
+ checked: Boolean(currentPreset.semantic_cache_enabled),
2441
+ onChange: (e) => updateCurrentPreset((p) => ({ ...p, semantic_cache_enabled: e.target.checked })),
2442
+ style: { accentColor: "var(--dsw-alias-state-brand-primary, var(--dsw-alias-label-primary))", cursor: "pointer" }
2443
+ }),
2444
+ t("aggregator.semantic_cache_title") || "\u26A1 Semantic Caching for Candidate Generations"
2445
+ ),
2446
+ React.createElement(
2447
+ "div",
2448
+ { style: { fontSize: 12, color: "var(--dsw-alias-label-secondary)", marginLeft: 22 } },
2449
+ t("aggregator.semantic_cache_desc") || "Persists intermediate candidate outputs locally by deterministic SHA-256 fingerprint, eliminating duplicate API costs on retry or repeat requests."
2450
+ )
2451
+ ),
2452
+ /* Self-Consistency Temperature Sampling (#301) */
2453
+ React.createElement(
2454
+ "div",
2455
+ { style: { display: "flex", flexDirection: "column", gap: 6, padding: "8px 12px", background: "var(--dsw-alias-bg-layer-2)", borderRadius: 8, border: "1px solid var(--dsw-alias-border-l2)", marginTop: 8 } },
2456
+ React.createElement(
2457
+ "label",
2458
+ { style: { display: "flex", alignItems: "center", gap: 8, cursor: "pointer", fontSize: 13, fontWeight: 500 } },
2459
+ React.createElement("input", {
2460
+ type: "checkbox",
2461
+ checked: Boolean(currentPreset.self_consistency_enabled),
2462
+ onChange: (e) => updateCurrentPreset((p) => ({ ...p, self_consistency_enabled: e.target.checked })),
2463
+ style: { accentColor: "var(--dsw-alias-state-brand-primary, var(--dsw-alias-label-primary))", cursor: "pointer" }
2464
+ }),
2465
+ t("aggregator.self_consistency_title") || "\u{1F3B2} Self-Consistency Temperature Sampling (Single-Model Diversity)"
2466
+ ),
2467
+ React.createElement(
2468
+ "div",
2469
+ { style: { fontSize: 12, color: "var(--dsw-alias-label-secondary)", marginLeft: 22 } },
2470
+ t("aggregator.self_consistency_desc") || "Expands a single reference model into multiple parallel samples with varied temperatures and personas, bringing MoA benefits to single-model setups."
2471
+ ),
2472
+ currentPreset.self_consistency_enabled && React.createElement(
2473
+ "div",
2474
+ { style: { marginLeft: 22, marginTop: 4, display: "flex", alignItems: "center", gap: 12 } },
2475
+ React.createElement("span", { style: { fontSize: 12, color: "var(--dsw-alias-label-secondary)" } }, (t("aggregator.self_consistency_samples") || "Samples:") + " " + (currentPreset.self_consistency_samples || 3)),
2476
+ React.createElement("input", {
2477
+ type: "range",
2478
+ min: 2,
2479
+ max: 5,
2480
+ value: currentPreset.self_consistency_samples || 3,
2481
+ onChange: (e) => updateCurrentPreset((p) => ({ ...p, self_consistency_samples: Number(e.target.value) })),
2482
+ style: { width: 100, accentColor: "var(--dsw-alias-state-brand-primary, var(--dsw-alias-label-primary))" }
2483
+ })
2484
+ )
2485
+ ),
2486
+ /* Dynamic Consensus Threshold & Multi-Judge Escalation (#300) */
2487
+ React.createElement(
2488
+ "div",
2489
+ { style: { display: "flex", flexDirection: "column", gap: 6, padding: "8px 12px", background: "var(--dsw-alias-bg-layer-2)", borderRadius: 8, border: "1px solid var(--dsw-alias-border-l2)", marginTop: 8 } },
2490
+ React.createElement(
2491
+ "div",
2492
+ { style: { fontSize: 13, fontWeight: 600, color: "var(--dsw-alias-label-primary)" } },
2493
+ t("aggregator.escalation_title") || "\u2696\uFE0F Dynamic Consensus Threshold & Judge Escalation"
2494
+ ),
2495
+ React.createElement(
2496
+ "div",
2497
+ { style: { fontSize: 12, color: "var(--dsw-alias-label-secondary)" } },
2498
+ t("aggregator.escalation_desc") || "Detects divergence across candidates. When consensus is weak, automatically upgrades to a senior flagship judge or convenes a 3-judge panel."
2499
+ ),
2500
+ React.createElement(
2501
+ "div",
2502
+ { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 4 } },
2503
+ React.createElement("span", { style: { fontSize: 12, color: "var(--dsw-alias-label-secondary)" } }, t("aggregator.escalation_policy") || "Escalation Policy:"),
2504
+ React.createElement(
2505
+ "select",
2506
+ {
2507
+ value: currentPreset.escalation_policy || "single",
2508
+ onChange: (e) => updateCurrentPreset((p) => ({ ...p, escalation_policy: e.target.value })),
2509
+ style: {
2510
+ padding: "4px 8px",
2511
+ fontSize: 12,
2512
+ borderRadius: 6,
2513
+ border: "1px solid var(--dsw-alias-border-l2)",
2514
+ background: "var(--dsw-alias-bg-layer-1)",
2515
+ color: "var(--dsw-alias-label-primary)"
2516
+ }
2517
+ },
2518
+ React.createElement("option", { value: "single" }, t("aggregator.escalation_policy_single") || "Standard Single Judge (No Escalation)"),
2519
+ React.createElement("option", { value: "auto_upgrade" }, t("aggregator.escalation_policy_upgrade") || "Auto-Upgrade to Senior Judge on Divergence"),
2520
+ React.createElement("option", { value: "council_majority" }, t("aggregator.escalation_policy_council") || "Convene 3-Judge Majority Council on Divergence")
2521
+ )
2522
+ ),
2523
+ currentPreset.escalation_policy && currentPreset.escalation_policy !== "single" && React.createElement(
2524
+ "div",
2525
+ { style: { display: "flex", flexDirection: "column", gap: 6, marginTop: 4 } },
2526
+ React.createElement(
2527
+ "div",
2528
+ { style: { display: "flex", alignItems: "center", gap: 12 } },
2529
+ React.createElement("span", { style: { fontSize: 12, color: "var(--dsw-alias-label-secondary)" } }, (t("aggregator.escalation_threshold") || "Divergence Threshold:") + " " + Math.round((currentPreset.escalation_disagreement_threshold ?? 0.4) * 100) + "%"),
2530
+ React.createElement("input", {
2531
+ type: "range",
2532
+ min: 10,
2533
+ max: 90,
2534
+ value: Math.round((currentPreset.escalation_disagreement_threshold ?? 0.4) * 100),
2535
+ onChange: (e) => updateCurrentPreset((p) => ({ ...p, escalation_disagreement_threshold: Number(e.target.value) / 100 })),
2536
+ style: { width: 120, accentColor: "var(--dsw-alias-state-brand-primary, var(--dsw-alias-label-primary))" }
2537
+ })
2538
+ ),
2539
+ currentPreset.escalation_policy === "auto_upgrade" && React.createElement(
2540
+ "div",
2541
+ { style: { display: "flex", alignItems: "center", gap: 8 } },
2542
+ React.createElement("span", { style: { fontSize: 12, color: "var(--dsw-alias-label-secondary)" } }, t("aggregator.escalation_judge_label") || "Senior Judge:"),
2543
+ React.createElement(
2544
+ "div",
2545
+ { style: { flex: 1, maxWidth: 300 } },
2546
+ React.createElement(SearchableModelPicker, {
2547
+ provider: currentPreset.escalation_judge?.provider || "",
2548
+ model: currentPreset.escalation_judge?.model || "",
2549
+ onChange: (prov, mod) => {
2550
+ updateCurrentPreset((p) => ({
2551
+ ...p,
2552
+ escalation_judge: { provider: prov, model: mod }
2553
+ }));
2554
+ },
2555
+ availableModels,
2556
+ t
2557
+ })
2558
+ )
2559
+ )
2560
+ )
2561
+ ),
2381
2562
  /* Fallback Judges Chain */
2382
2563
  React.createElement(
2383
2564
  "div",
package/lib/events.js CHANGED
@@ -7,8 +7,12 @@ export const MOA_EVENTS = {
7
7
  CANDIDATE_COMPLETE: 'moa/candidate-complete',
8
8
  SYNTAX_VALIDATION: 'moa/syntax-validation',
9
9
  EARLY_CONSENSUS: 'moa/early-consensus',
10
+ JUDGE_ESCALATION: 'moa/judge-escalation',
11
+ SELF_CONSISTENCY_EXPAND: 'moa/self-consistency-expand',
10
12
  CRITIQUE_COMPLETE: 'moa/critique-complete',
11
13
  JUDGE_VERDICT: 'moa/judge-verdict',
14
+ CONTEXT_PRUNING: 'moa/context-pruning',
15
+ CACHE_HIT: 'moa/cache-hit',
12
16
  RUN_FINISH: 'moa/run-finish',
13
17
  }
14
18
 
@@ -30,4 +34,4 @@ export function emitEvent(ctx, eventName, payload) {
30
34
  /* best-effort event dispatch: listener exceptions must never abort pipeline */
31
35
  }
32
36
  }
33
-
37
+
package/lib/moa-schema.js CHANGED
@@ -79,6 +79,16 @@ export const PresetSchema = z.object({
79
79
  local_preflight_check: z.boolean().default(true),
80
80
  early_exit_consensus_enabled: z.boolean().default(false),
81
81
  early_exit_threshold: z.number().default(0.95),
82
+ context_pruning_enabled: z.boolean().default(true),
83
+ context_pruning_diff_lines: z.number().default(5),
84
+ semantic_cache_enabled: z.boolean().default(false),
85
+ semantic_cache_ttl_hours: z.number().default(168),
86
+ self_consistency_enabled: z.boolean().default(false),
87
+ self_consistency_samples: z.number().default(3),
88
+ self_consistency_temperatures: z.array(z.number()).default([0.2, 0.6, 0.9]),
89
+ escalation_policy: z.string().default('single'),
90
+ escalation_disagreement_threshold: z.number().default(0.40),
91
+ escalation_judge: ModelSlotSchema.default({ provider: '', model: '' }),
82
92
  })
83
93
 
84
94
  export const Config = z.object({
@@ -104,6 +114,16 @@ export const Config = z.object({
104
114
  local_preflight_check: z.boolean().default(true).volatile(),
105
115
  early_exit_consensus_enabled: z.boolean().default(false).volatile(),
106
116
  early_exit_threshold: z.number().default(0.95).volatile(),
117
+ context_pruning_enabled: z.boolean().default(true).volatile(),
118
+ context_pruning_diff_lines: z.number().default(5).volatile(),
119
+ semantic_cache_enabled: z.boolean().default(false).volatile(),
120
+ semantic_cache_ttl_hours: z.number().default(168).volatile(),
121
+ self_consistency_enabled: z.boolean().default(false).volatile(),
122
+ self_consistency_samples: z.number().default(3).volatile(),
123
+ self_consistency_temperatures: z.array(z.number()).default([0.2, 0.6, 0.9]).volatile(),
124
+ escalation_policy: z.string().default('single').volatile(),
125
+ escalation_disagreement_threshold: z.number().default(0.40).volatile(),
126
+ escalation_judge: ModelSlotSchema.default({ provider: '', model: '' }).volatile(),
107
127
  smart_routing_model: ModelSlotSchema.default({ provider: '', model: '' }).volatile(),
108
128
  prices: z.dict(PriceRow).default({}).volatile(),
109
129
  presets: z.array(PresetSchema).default([
@@ -120,6 +140,10 @@ export const Config = z.object({
120
140
  aggregator_temperature: 0.4,
121
141
  max_tokens: 4096,
122
142
  judge_criteria: '',
143
+ context_pruning_enabled: true,
144
+ context_pruning_diff_lines: 5,
145
+ semantic_cache_enabled: false,
146
+ semantic_cache_ttl_hours: 168,
123
147
  },
124
148
  {
125
149
  name: 'fast',
@@ -133,6 +157,10 @@ export const Config = z.object({
133
157
  aggregator_temperature: 0.2,
134
158
  max_tokens: 4096,
135
159
  judge_criteria: '',
160
+ context_pruning_enabled: true,
161
+ context_pruning_diff_lines: 5,
162
+ semantic_cache_enabled: false,
163
+ semantic_cache_ttl_hours: 168,
136
164
  },
137
165
  {
138
166
  name: 'deep-reasoning',
@@ -165,4 +193,4 @@ export function plainConfig(value) {
165
193
  }
166
194
  return value
167
195
  }
168
-
196
+
@@ -1,3 +1,4 @@
1
+ import { createCachingCallLlm } from './semantic-cache.js'
1
2
  import { runSpeculativeMoa } from '../moa-speculative.js'
2
3
  import { runAdversarialTddLoop } from '../moa-adversarial-tdd.js'
3
4
  import { assignAdaptivePersonas } from '../moa-adaptive-personas.js'
@@ -33,7 +34,19 @@ export async function stageCandidateRound1({
33
34
  testGateEnabled,
34
35
  testCommand,
35
36
  execFn,
37
+ semanticCacheEnabled,
38
+ semanticCacheTtlHours,
39
+ ctx = null,
36
40
  }) {
41
+ const isCacheEnabled = Boolean(preset?.semantic_cache_enabled || semanticCacheEnabled)
42
+ const effectiveCallLlm = isCacheEnabled
43
+ ? createCachingCallLlm(callLlm, {
44
+ enabled: true,
45
+ ttlMs: (semanticCacheTtlHours || preset?.semantic_cache_ttl_hours || 168) * 60 * 60 * 1000,
46
+ cwd,
47
+ ctx,
48
+ })
49
+ : callLlm
37
50
  const lastMsgText = enrichedMessages[enrichedMessages.length - 1]?.content || ''
38
51
  const adaptiveRefs = preset?.adaptive_personas_enabled
39
52
  ? assignAdaptivePersonas(effectiveReferences, typeof lastMsgText === 'string' ? lastMsgText : '').references
@@ -52,7 +65,7 @@ export async function stageCandidateRound1({
52
65
  prices,
53
66
  signal,
54
67
  },
55
- callLlm,
68
+ effectiveCallLlm,
56
69
  onProgress
57
70
  )
58
71
  if (!specRes.fallbackToStandard && Array.isArray(specRes.referenceOutputs)) {
@@ -62,7 +75,7 @@ export async function stageCandidateRound1({
62
75
 
63
76
  if (!referenceOutputs) {
64
77
  referenceOutputs = preset?.subagent_swarm_enabled
65
- ? await runSubagentSwarm(adaptiveRefs, enrichedMessages, { temperature: refTemp, maxTokens, signal, cwd, runId }, callLlm, onProgress)
78
+ ? await runSubagentSwarm(adaptiveRefs, enrichedMessages, { temperature: refTemp, maxTokens, signal, cwd, runId }, effectiveCallLlm, onProgress)
66
79
  : await runReferencesParallel(
67
80
  adaptiveRefs,
68
81
  enrichedMessages,
@@ -80,7 +93,7 @@ export async function stageCandidateRound1({
80
93
  maxRetries: candidateRetries,
81
94
  signal,
82
95
  },
83
- callLlm,
96
+ effectiveCallLlm,
84
97
  onProgress
85
98
  )
86
99
  }