@goodandready/dsh-moa 0.2.71 → 0.2.73
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 +11 -0
- package/README.ru.md +11 -0
- package/README.zh.md +11 -0
- package/lib/client.js +94 -0
- package/lib/events.js +5 -0
- package/lib/moa-adversarial-tdd.js +4 -1
- package/lib/moa-ast-merge.js +4 -1
- package/lib/moa-schema.js +23 -0
- package/lib/moa-swarm.js +13 -4
- package/lib/pipeline/candidates.js +16 -3
- package/lib/pipeline/context-pruner.js +323 -0
- package/lib/pipeline/context.js +11 -0
- package/lib/pipeline/early-consensus.js +324 -0
- package/lib/pipeline/runner.js +89 -6
- package/lib/pipeline/semantic-cache.js +348 -0
- package/lib/pipeline/syntax-filter.js +324 -0
- package/lib/routes/cache.js +42 -0
- package/lib/routes.js +3 -0
- package/lib/tui-settings.js +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -180,6 +180,12 @@ Seamlessly retains synthesized baseline code from prior turns while pruning inte
|
|
|
180
180
|
### 18. Graceful Degradation & Local Fallback Resilience
|
|
181
181
|
When `local_fallback_enabled: true`, candidates that fail due to network outages, timeouts, or rate limits (429/500) automatically recover using configured local models (Ollama / MiniPC). Each fallback model operates with an independent execution budget and an isolated controller linked to the parent turn signal, preventing cloud timeouts from starving fallback models. Additionally, terminal errors and aborted streams are strictly classified, ensuring reliable error propagation and failover.
|
|
182
182
|
|
|
183
|
+
### 19. Zero-Token Local Pre-Judge Syntax Filter
|
|
184
|
+
Validates markdown code blocks across all candidate outputs locally using JavaScript/TypeScript AST checking, JSON parsing, and bracket/quote balance verification before calling the Judge LLM. When malformed candidates are detected, they are automatically disqualified or annotated with structured syntax error reports, saving judge tokens and preventing syntax errors from propagating. Configured via `local_preflight_check: true` (enabled by default).
|
|
185
|
+
|
|
186
|
+
### 20. Early Exit upon Semantic Consensus
|
|
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
|
+
|
|
183
189
|
### 6. Live Canvas 1-Click Preview (optional)
|
|
184
190
|
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.
|
|
185
191
|
|
|
@@ -289,6 +295,9 @@ dsh-moa:
|
|
|
289
295
|
| `presets[].quorum_enabled` | `boolean` | `false` | Straggler mitigation: proceed with synthesis once >= 60% candidates respond |
|
|
290
296
|
| `presets[].grace_period_sec` | `number` | `10` | Grace period in seconds to wait for stragglers after quorum is reached |
|
|
291
297
|
| `presets[].aggregator_fallbacks` | `array` | `[]` | Ordered fallback judge models tried if primary aggregator encounters transient errors |
|
|
298
|
+
| `presets[].local_preflight_check` | `boolean` | `true` | Zero-token local AST, JSON, and bracket balance verification for candidate code blocks before the Judge panel |
|
|
299
|
+
| `presets[].early_exit_consensus_enabled` | `boolean` | `false` | Bypass Judge evaluation and promote directly when candidate solutions reach high semantic consensus |
|
|
300
|
+
| `presets[].early_exit_threshold` | `number` | `0.95` | Token-level similarity threshold (0.50 to 1.00) required to trigger early exit consensus |
|
|
292
301
|
| `presets[].blind_evaluation` | `boolean` | `false` | Anonymize candidate model names for the judge/curator to eliminate family/brand bias |
|
|
293
302
|
| `presets[].reference_timeout_sec` | `number` | `60` | Per-candidate execution timeout in seconds |
|
|
294
303
|
| `presets[].aggregator_timeout_sec` | `number` | `180` | Aggregator/judge synthesis timeout in seconds |
|
|
@@ -318,6 +327,8 @@ dsh-moa:
|
|
|
318
327
|
| `/dsh-moa/run` | `POST` | Runs the full MoA pipeline over HTTP (400 when `enabled: false`) |
|
|
319
328
|
| `/dsh-moa/diff` | `GET` | Computes line-by-line diff between candidate runs or curator synthesis |
|
|
320
329
|
| `/dsh-moa/promote` | `POST` | Manually promotes candidate workspace files to project root |
|
|
330
|
+
| `/dsh-moa/cache` | `GET` | Returns semantic cache stats, hit/miss counters, and saved token metrics |
|
|
331
|
+
| `/dsh-moa/cache` | `POST` | Purges cached candidate generations with optional olderThanMs filter |
|
|
321
332
|
| `/api/dsh-moa/update` | `POST` | One-click plugin updater from npm with safe-write verification |
|
|
322
333
|
|
|
323
334
|
---
|
package/README.ru.md
CHANGED
|
@@ -180,6 +180,12 @@ graph TD
|
|
|
180
180
|
### 18. Плавная деградация и локальный фоллбэк
|
|
181
181
|
Опция `local_fallback_enabled` позволяет задать резервные локальные модели (Ollama, vLLM, MiniPC), к которым плагин бесшовно обращается при сбоях сетевых API, таймаутах или превышении лимитов rate limit (429/500). Каждая фоллбэк-модель запускается с независимым бюджетом времени и изолированным контроллером отмены, связанным с сигналом основного хода, предотвращая исчерпание времени ожидания из-за задержек облачных провайдеров. Стриминговые сбои и терминальные ошибки классифицируются типизированными исключениями с гарантированной защитой от позднего промоушна.
|
|
182
182
|
|
|
183
|
+
### 19. Локальный синтаксический шлюз кандидатов без расхода токенов
|
|
184
|
+
Локально парсит блоки кода из Markdown-ответов кандидатов и проверяет их синтаксис (JavaScript, TypeScript, JSON, Python) и баланс скобок/кавычек до вызова дорогостоящей модели-судьи. Кандидаты с синтаксическими ошибками дисквалифицируются или помечаются структурированными аннотациями для судьи, полностью исключая лишний расход токенов на заведомо невалидный код. Включается настройкой `local_preflight_check: true` (включена по умолчанию).
|
|
185
|
+
|
|
186
|
+
### 20. Ранний выход по семантическому консенсусу
|
|
187
|
+
Сравнивает нормализованные решения кандидатов по токенному сходству Сёренсена-Дайса. Если решения моделей совпадают или практически идентичны (достигнут порог `early_exit_threshold`, по умолчанию 0.95), стадия вызова судьи (Judge) пропускается. Согласованный вариант немедленно продвигается в проект, экономя до 100% токенов судейской коллегии и сокращая задержку ответа. Управляется параметрами `early_exit_consensus_enabled: true` и `early_exit_threshold: 0.95`.
|
|
188
|
+
|
|
183
189
|
### 6. Live Canvas 1-клик предпросмотр (опционально)
|
|
184
190
|
Если в профиле установлен `@goodandready/dsh-live-canvas`, `dsh-moa` отправляет промоученный HTML-файл в REST-контракт Live Canvas (`POST /dsh-live-canvas/api/preview`, тот же webServer харнесса) и добавляет к ответу ссылку на предпросмотр (`/dsh-live-canvas/sandbox/<id>`). Без плагина шаг пропускается тихо — без ошибок в журнале и без битых ссылок.
|
|
185
191
|
|
|
@@ -289,6 +295,9 @@ dsh-moa:
|
|
|
289
295
|
| `presets[].quorum_enabled` | `boolean` | `false` | Защита от зависших моделей (stragglers): запуск синтеза при ответе от >= 60% кандидатов |
|
|
290
296
|
| `presets[].grace_period_sec` | `number` | `10` | Грейс-период (в секундах) ожидания оставшихся моделей после достижения кворума |
|
|
291
297
|
| `presets[].aggregator_fallbacks` | `array` | `[]` | Список запасных моделей-судей при сбоях основной модели агрегатора |
|
|
298
|
+
| `presets[].local_preflight_check` | `boolean` | `true` | Локальная проверка AST, JSON и баланса скобок в блоках кода кандидатов без расхода токенов |
|
|
299
|
+
| `presets[].early_exit_consensus_enabled` | `boolean` | `false` | Пропуск этапа судьи и прямое продвижение при достижении семантического консенсуса кандидатов |
|
|
300
|
+
| `presets[].early_exit_threshold` | `number` | `0.95` | Порог токенного сходства (0.50–1.00) для срабатывания раннего выхода по консенсусу |
|
|
292
301
|
| `presets[].blind_evaluation` | `boolean` | `false` | Обезличивание имен кандидатов («Candidate 1», «Candidate 2») для исключения предвзятости судьи |
|
|
293
302
|
| `presets[].reference_timeout_sec` | `number` | `60` | Таймаут опроса каждого кандидата в секундах |
|
|
294
303
|
| `presets[].aggregator_timeout_sec` | `number` | `180` | Таймаут синтеза решения судьей в секундах |
|
|
@@ -318,6 +327,8 @@ dsh-moa:
|
|
|
318
327
|
| `/dsh-moa/run` | `POST` | Запускает полный пайплайн MoA по HTTP (400 при `enabled: false`) |
|
|
319
328
|
| `/dsh-moa/diff` | `GET` | Вычисляет построчный diff между решениями кандидатов или синтезом куратора |
|
|
320
329
|
| `/dsh-moa/promote` | `POST` | Ручной промоушн файлов кандидата в корень проекта с созданием снимка |
|
|
330
|
+
| `/dsh-moa/cache` | `GET` | Статистика семантического кэша, число попаданий и сэкономленные токены |
|
|
331
|
+
| `/dsh-moa/cache` | `POST` | Очистка семантического кэша с опциональным фильтром olderThanMs |
|
|
321
332
|
| `/api/dsh-moa/update` | `POST` | One-click обновление плагина из npm с проверкой безопасности источника |
|
|
322
333
|
|
|
323
334
|
---
|
package/README.zh.md
CHANGED
|
@@ -157,6 +157,12 @@ graph TD
|
|
|
157
157
|
### 18. 平滑降级与本地容灾恢复 (Local Fallback Resilience)
|
|
158
158
|
当 `local_fallback_enabled: true` 时,因网络中断、超时或速率限制(429/500)而失败的候选模型会自动使用配置的本地模型(Ollama / MiniPC)恢复。每个后备模型都拥有独立的超时执行配额与关联主 turn signal 的隔离取消控制器,防止云端超时耗尽本地恢复时间。同时严格检测终止错误与流中断,确保安全容灾且杜绝已取消 turn 的延迟文件提升。
|
|
159
159
|
|
|
160
|
+
### 19. 零 Token 本地预审语法网关 (Zero-Token Pre-Judge Syntax Filter)
|
|
161
|
+
在调用裁判模型之前,在本地解析所有候选方案的 Markdown 代码块,进行 JavaScript/TypeScript AST 语法校验、JSON 解析以及括号/引号匹配检查。检测到语法损坏的候选将被自动过滤或附带结构化语法错误注解,无需消耗模型 Token 即可杜绝语法缺陷代码流入最终裁决。通过 `local_preflight_check: true` 配置(默认开启)。
|
|
162
|
+
|
|
163
|
+
### 20. 语义共识提前退出机制 (Early Exit upon Semantic Consensus)
|
|
164
|
+
基于 Token 级 Sørensen-Dice 相似度算法比对归一化后的候选方案。当多个候选模型给出高度一致的代码方案(达到或超过 `early_exit_threshold` 阈值,默认 0.95)时,自动跳过高耗费的裁判综合评审阶段,直接将共识方案提升至项目,在大幅缩短响应延迟的同时节省多达 100% 的裁判 Token 支出。通过 `early_exit_consensus_enabled: true` 与 `early_exit_threshold: 0.95` 配置。
|
|
165
|
+
|
|
160
166
|
### 6. Live Canvas 一键预览(可选)
|
|
161
167
|
若同一 profile 中安装了 `@goodandready/dsh-live-canvas`,`dsh-moa` 会将提升后的 HTML 文件推送到 Live Canvas 的 REST 契约(`POST /dsh-live-canvas/api/preview`,由同一 harness webServer 提供服务),并在回答中附上一键预览链接(`/dsh-live-canvas/sandbox/<id>`)。未安装该插件时此步骤静默跳过——日志无报错,也不会出现死链接。
|
|
162
168
|
|
|
@@ -225,6 +231,9 @@ dsh-moa:
|
|
|
225
231
|
| `presets[].reference_models` | `array` | `[...]` | 并行提案阶段并发调用的顾问模型列表 |
|
|
226
232
|
| `presets[].aggregator` | `object` | `{...}` | 负责综合评审、代码审查与裁决胜出者的裁判模型 |
|
|
227
233
|
| `presets[].ask_clarifying_questions` | `boolean` | `true` | 针对宽泛需求合成澄清问卷(预设级别开关) |
|
|
234
|
+
| `presets[].local_preflight_check` | `boolean` | `true` | 在裁判评审前本地验证候选代码块的 AST、JSON 与括号平衡(零 Token 消耗) |
|
|
235
|
+
| `presets[].early_exit_consensus_enabled` | `boolean` | `false` | 当候选方案达成高语义共识时跳过裁判评审并直接提升获胜方案 |
|
|
236
|
+
| `presets[].early_exit_threshold` | `number` | `0.95` | 触发语义共识提前退出的 Token 相似度阈值(0.50 至 1.00) |
|
|
228
237
|
| `presets[].reference_temperature` / `.aggregator_temperature` | `number` | `0.6` / `0.4` | 顾问与裁判的采样温度 |
|
|
229
238
|
| `presets[].max_tokens` | `number` | `4096` | 每次模型调用的最大输出 Token 数 |
|
|
230
239
|
| `presets[].judge_criteria` | `string` | `""` | 传给裁判的可选附加评审准则 |
|
|
@@ -248,6 +257,8 @@ dsh-moa:
|
|
|
248
257
|
| `/dsh-moa/run` | `POST` | 通过 HTTP 运行完整 MoA 管线(`enabled: false` 时返回 400) |
|
|
249
258
|
| `/dsh-moa/diff` | `GET` | 计算候选运行结果或综合输出的行级别差异(支持按 runId 解析工作区) |
|
|
250
259
|
| `/dsh-moa/promote` | `POST` | 手动将候选工作区文件提升到项目根目录(带时间机器快照) |
|
|
260
|
+
| `/dsh-moa/cache` | `GET` | 返回语义缓存统计信息、命中次数与节省 Token 指标 |
|
|
261
|
+
| `/dsh-moa/cache` | `POST` | 清除语义缓存条目,支持 olderThanMs 过滤 |
|
|
251
262
|
| `/api/dsh-moa/update` | `POST` | 从 npm 一键更新插件,带安全写入源校验 |
|
|
252
263
|
|
|
253
264
|
---
|
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
|
|
@@ -185,6 +191,10 @@ window.__ModuleLoader__.load({
|
|
|
185
191
|
"aggregator.workbench_desc": "Judge scores each candidate file from 1 to 10 with pros/cons. Select desired files across candidates; top model harmonizes and glues them, followed by final judge audit.",
|
|
186
192
|
"aggregator.workbench_companion": "Companion Plugin:",
|
|
187
193
|
"aggregator.workbench_companion_note": "safe shadow Git checkpoints before file promotion",
|
|
194
|
+
"aggregator.preflight_syntax_title": "\u{1F50D} Pre-Judge Local Syntax Gate (Zero-Token Linter)",
|
|
195
|
+
"aggregator.preflight_syntax_desc": "Validate candidate code syntax locally before judge invocation to filter broken code without token costs.",
|
|
196
|
+
"aggregator.early_consensus_title": "\u26A1 Early Exit upon Semantic Consensus",
|
|
197
|
+
"aggregator.early_consensus_desc": "Bypass the expensive judge round when reference models converge on equivalent code solutions.",
|
|
188
198
|
"workbench.title": "\u{1F6E0}\uFE0F Interactive Synthesis Workbench",
|
|
189
199
|
"workbench.desc": "Inspect candidate file quality scores (1\u201310), mix and match files across proposals, harmonize with top-model glue, and run a final judge audit.",
|
|
190
200
|
"workbench.select_run": "MoA Run:",
|
|
@@ -361,6 +371,12 @@ window.__ModuleLoader__.load({
|
|
|
361
371
|
"config.smart_routing_desc": "\u5206\u6790\u4EFB\u52A1\u590D\u6742\u5EA6\u5E76\u81EA\u52A8\u5339\u914D\u6700\u4F73\u9884\u8BBE\u3002",
|
|
362
372
|
"config.smart_routing_model": "\u5206\u6D41\u51B3\u7B56\u6A21\u578B\uFF1A",
|
|
363
373
|
// Multi-Turn Context
|
|
374
|
+
"aggregator.context_pruning_title": "\u2702\uFE0F \u5019\u9009\u8DE8\u6A21\u578B\u4E0A\u4E0B\u6587\u7CBE\u7B80 (Judge \u4EC5\u5DEE\u5F02\u8865\u4E01)",
|
|
375
|
+
"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",
|
|
376
|
+
"aggregator.context_pruning_lines": "\u5DEE\u5F02\u4E0A\u4E0B\u6587\u884C\u6570:",
|
|
377
|
+
"aggregator.semantic_cache_title": "\u26A1 \u5019\u9009\u751F\u6210\u8BED\u4E49\u7F13\u5B58",
|
|
378
|
+
"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",
|
|
379
|
+
"aggregator.semantic_cache_ttl": "\u7F13\u5B58\u6709\u6548\u671F (\u5C0F\u65F6):",
|
|
364
380
|
"aggregator.multi_turn_title": "\u591A\u8F6E\u5BF9\u8BDD\u4E0A\u4E0B\u6587\u4E0E\u5DEE\u5F02\u526A\u679D",
|
|
365
381
|
"aggregator.multi_turn_desc": "\u8DE8\u8F6E\u6B21\u4FDD\u7559\u5BF9\u8BDD\u4E0A\u4E0B\u6587\u5E76\u526A\u679D\u91CD\u590D\u4EE3\u7801\u5757\u3002",
|
|
366
382
|
// UI States & Errors
|
|
@@ -379,6 +395,10 @@ window.__ModuleLoader__.load({
|
|
|
379
395
|
"aggregator.workbench_desc": "\u88C1\u5224\u4E3A\u6BCF\u4E2A\u5019\u9009\u6587\u4EF6\u6253\u5206 (1-10) \u5E76\u6807\u6CE8\u4F18\u7F3A\u70B9\u3002\u5728 UI \u4E2D\u81EA\u7531\u7EC4\u5408\u5404\u5019\u9009\u6587\u4EF6\uFF0C\u7531\u5F53\u8F6E\u6700\u9AD8\u5206\u6A21\u578B\u8FDB\u884C\u4EE3\u7801\u7C98\u5408\u7EDF\u4E00\uFF0C\u6700\u7EC8\u7531\u88C1\u5224\u590D\u5BA1\u3002",
|
|
380
396
|
"aggregator.workbench_companion": "\u914D\u5957\u63D2\u4EF6\uFF1A",
|
|
381
397
|
"aggregator.workbench_companion_note": "\u6587\u4EF6\u63D0\u5347\u524D\u5B89\u5168\u521B\u5EFA\u5F71\u5B50 Git \u5FEB\u7167",
|
|
398
|
+
"aggregator.preflight_syntax_title": "\u{1F50D} \u8BC4\u5BA1\u524D\u672C\u5730\u8BED\u6CD5\u68C0\u67E5\u95E8\u7981\uFF08\u96F6Token\u5F00\u9500\uFF09",
|
|
399
|
+
"aggregator.preflight_syntax_desc": "\u5728\u8C03\u7528\u88C1\u5224\u6A21\u578B\u524D\u5BF9\u5019\u9009\u4EE3\u7801\u8FDB\u884C\u672C\u5730\u8BED\u6CD5\u68C0\u67E5\uFF0C\u96F6Token\u5254\u9664\u8BED\u6CD5\u7834\u635F\u4EE3\u7801\u3002",
|
|
400
|
+
"aggregator.early_consensus_title": "\u26A1 \u8BED\u4E49\u5171\u8BC6\u5FEB\u901F\u76F4\u51FA\uFF08Early Exit\uFF09",
|
|
401
|
+
"aggregator.early_consensus_desc": "\u5F53\u591A\u5019\u9009\u6A21\u578B\u751F\u6210\u4E00\u81F4\u4EE3\u7801\uFF08\u76F8\u4F3C\u5EA6\u8FBE\u6807\uFF09\u65F6\uFF0C\u76F4\u63A5\u8FD4\u56DE\u5171\u8BC6\u89E3\uFF0C\u8DF3\u8FC7\u88C1\u5224\u9636\u6BB5\u8282\u7EA6Token\u4E0E\u8017\u65F6\u3002",
|
|
382
402
|
"workbench.title": "\u{1F6E0}\uFE0F \u4EA4\u4E92\u5F0F\u5408\u6210\u5DE5\u4F5C\u53F0",
|
|
383
403
|
"workbench.desc": "\u67E5\u770B\u5019\u9009\u6A21\u578B\u7684\u6587\u4EF6\u8D28\u91CF\u6253\u5206 (1\u201310)\uFF0C\u81EA\u7531\u6DF7\u642D\u7EC4\u5408\u76EE\u6807\u6587\u4EF6\uFF0C\u7531\u6700\u9AD8\u5206\u6A21\u578B\u7C98\u5408\u7EDF\u4E00\u5E76\u6267\u884C\u6700\u7EC8\u88C1\u5224\u590D\u5BA1\u3002",
|
|
384
404
|
"workbench.select_run": "MoA \u8FD0\u884C\uFF1A",
|
|
@@ -2312,6 +2332,43 @@ window.__ModuleLoader__.load({
|
|
|
2312
2332
|
)
|
|
2313
2333
|
)
|
|
2314
2334
|
),
|
|
2335
|
+
/* Pre-Judge Local Syntax Gate (#303) & Early Semantic Consensus (#299) */
|
|
2336
|
+
React.createElement(
|
|
2337
|
+
"div",
|
|
2338
|
+
{ 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 } },
|
|
2339
|
+
React.createElement(
|
|
2340
|
+
"label",
|
|
2341
|
+
{ style: { display: "flex", alignItems: "center", gap: 8, cursor: "pointer", fontSize: 13, fontWeight: 500 } },
|
|
2342
|
+
React.createElement("input", {
|
|
2343
|
+
type: "checkbox",
|
|
2344
|
+
checked: currentPreset.local_preflight_check !== false,
|
|
2345
|
+
onChange: (e) => updateCurrentPreset((p) => ({ ...p, local_preflight_check: e.target.checked })),
|
|
2346
|
+
style: { accentColor: "var(--dsw-alias-state-brand-primary, var(--dsw-alias-label-primary))", cursor: "pointer" }
|
|
2347
|
+
}),
|
|
2348
|
+
t("aggregator.preflight_syntax_title") || "\u{1F50D} Pre-Judge Local Syntax Gate (Zero-Token Linter)"
|
|
2349
|
+
),
|
|
2350
|
+
React.createElement(
|
|
2351
|
+
"div",
|
|
2352
|
+
{ style: { fontSize: 12, color: "var(--dsw-alias-label-secondary)", marginLeft: 22 } },
|
|
2353
|
+
t("aggregator.preflight_syntax_desc") || "Validate candidate code syntax locally before judge invocation to filter broken code without token costs."
|
|
2354
|
+
),
|
|
2355
|
+
React.createElement(
|
|
2356
|
+
"label",
|
|
2357
|
+
{ style: { display: "flex", alignItems: "center", gap: 8, cursor: "pointer", fontSize: 13, fontWeight: 500, marginTop: 4 } },
|
|
2358
|
+
React.createElement("input", {
|
|
2359
|
+
type: "checkbox",
|
|
2360
|
+
checked: Boolean(currentPreset.early_exit_consensus_enabled),
|
|
2361
|
+
onChange: (e) => updateCurrentPreset((p) => ({ ...p, early_exit_consensus_enabled: e.target.checked })),
|
|
2362
|
+
style: { accentColor: "var(--dsw-alias-state-brand-primary, var(--dsw-alias-label-primary))", cursor: "pointer" }
|
|
2363
|
+
}),
|
|
2364
|
+
t("aggregator.early_consensus_title") || "\u26A1 Early Exit upon Semantic Consensus"
|
|
2365
|
+
),
|
|
2366
|
+
React.createElement(
|
|
2367
|
+
"div",
|
|
2368
|
+
{ style: { fontSize: 12, color: "var(--dsw-alias-label-secondary)", marginLeft: 22 } },
|
|
2369
|
+
t("aggregator.early_consensus_desc") || "Bypass the expensive judge round when reference models converge on equivalent code solutions."
|
|
2370
|
+
)
|
|
2371
|
+
),
|
|
2315
2372
|
/* Multi-Turn Context & Diff Pruning (#176) */
|
|
2316
2373
|
React.createElement(
|
|
2317
2374
|
"div",
|
|
@@ -2333,6 +2390,43 @@ window.__ModuleLoader__.load({
|
|
|
2333
2390
|
t("aggregator.multi_turn_desc") || "Retain conversation context across turns while pruning duplicate code blocks."
|
|
2334
2391
|
)
|
|
2335
2392
|
),
|
|
2393
|
+
/* Context Pruning (#305) & Semantic Caching (#318) */
|
|
2394
|
+
React.createElement(
|
|
2395
|
+
"div",
|
|
2396
|
+
{ 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 } },
|
|
2397
|
+
React.createElement(
|
|
2398
|
+
"label",
|
|
2399
|
+
{ style: { display: "flex", alignItems: "center", gap: 8, cursor: "pointer", fontSize: 13, fontWeight: 500 } },
|
|
2400
|
+
React.createElement("input", {
|
|
2401
|
+
type: "checkbox",
|
|
2402
|
+
checked: currentPreset.context_pruning_enabled !== false,
|
|
2403
|
+
onChange: (e) => updateCurrentPreset((p) => ({ ...p, context_pruning_enabled: e.target.checked })),
|
|
2404
|
+
style: { accentColor: "var(--dsw-alias-state-brand-primary, var(--dsw-alias-label-primary))", cursor: "pointer" }
|
|
2405
|
+
}),
|
|
2406
|
+
t("aggregator.context_pruning_title") || "\u2702\uFE0F Cross-Candidate Context Pruning (Diff-Only Judge Context)"
|
|
2407
|
+
),
|
|
2408
|
+
React.createElement(
|
|
2409
|
+
"div",
|
|
2410
|
+
{ style: { fontSize: 12, color: "var(--dsw-alias-label-secondary)", marginLeft: 22 } },
|
|
2411
|
+
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%."
|
|
2412
|
+
),
|
|
2413
|
+
React.createElement(
|
|
2414
|
+
"label",
|
|
2415
|
+
{ style: { display: "flex", alignItems: "center", gap: 8, cursor: "pointer", fontSize: 13, fontWeight: 500, marginTop: 4 } },
|
|
2416
|
+
React.createElement("input", {
|
|
2417
|
+
type: "checkbox",
|
|
2418
|
+
checked: Boolean(currentPreset.semantic_cache_enabled),
|
|
2419
|
+
onChange: (e) => updateCurrentPreset((p) => ({ ...p, semantic_cache_enabled: e.target.checked })),
|
|
2420
|
+
style: { accentColor: "var(--dsw-alias-state-brand-primary, var(--dsw-alias-label-primary))", cursor: "pointer" }
|
|
2421
|
+
}),
|
|
2422
|
+
t("aggregator.semantic_cache_title") || "\u26A1 Semantic Caching for Candidate Generations"
|
|
2423
|
+
),
|
|
2424
|
+
React.createElement(
|
|
2425
|
+
"div",
|
|
2426
|
+
{ style: { fontSize: 12, color: "var(--dsw-alias-label-secondary)", marginLeft: 22 } },
|
|
2427
|
+
t("aggregator.semantic_cache_desc") || "Persists intermediate candidate outputs locally by deterministic SHA-256 fingerprint, eliminating duplicate API costs on retry or repeat requests."
|
|
2428
|
+
)
|
|
2429
|
+
),
|
|
2336
2430
|
/* Fallback Judges Chain */
|
|
2337
2431
|
React.createElement(
|
|
2338
2432
|
"div",
|
package/lib/events.js
CHANGED
|
@@ -5,8 +5,12 @@
|
|
|
5
5
|
export const MOA_EVENTS = {
|
|
6
6
|
RUN_START: 'moa/run-start',
|
|
7
7
|
CANDIDATE_COMPLETE: 'moa/candidate-complete',
|
|
8
|
+
SYNTAX_VALIDATION: 'moa/syntax-validation',
|
|
9
|
+
EARLY_CONSENSUS: 'moa/early-consensus',
|
|
8
10
|
CRITIQUE_COMPLETE: 'moa/critique-complete',
|
|
9
11
|
JUDGE_VERDICT: 'moa/judge-verdict',
|
|
12
|
+
CONTEXT_PRUNING: 'moa/context-pruning',
|
|
13
|
+
CACHE_HIT: 'moa/cache-hit',
|
|
10
14
|
RUN_FINISH: 'moa/run-finish',
|
|
11
15
|
}
|
|
12
16
|
|
|
@@ -28,3 +32,4 @@ export function emitEvent(ctx, eventName, payload) {
|
|
|
28
32
|
/* best-effort event dispatch: listener exceptions must never abort pipeline */
|
|
29
33
|
}
|
|
30
34
|
}
|
|
35
|
+
|
|
@@ -184,7 +184,9 @@ export async function runAdversarialTddLoop({
|
|
|
184
184
|
try {
|
|
185
185
|
const dest = assertPathContained(candDir, tf.relativePath)
|
|
186
186
|
await fs.rm(dest, { force: true })
|
|
187
|
-
} catch {
|
|
187
|
+
} catch (err) {
|
|
188
|
+
logger.debug('[dsh-moa:tdd] cleanup test file error:', err?.message || err)
|
|
189
|
+
}
|
|
188
190
|
}
|
|
189
191
|
|
|
190
192
|
if (gateRes) {
|
|
@@ -217,3 +219,4 @@ export async function runAdversarialTddLoop({
|
|
|
217
219
|
directive: results.length > 0 ? reportLines.join('\n') : '',
|
|
218
220
|
}
|
|
219
221
|
}
|
|
222
|
+
|
package/lib/moa-ast-merge.js
CHANGED
|
@@ -24,7 +24,9 @@ export function resolveContextLensService(ctx, explicitLens = null) {
|
|
|
24
24
|
const refl = ctx.reflect.get('contextLens') || ctx.reflect.get('dsh-context-lens')
|
|
25
25
|
if (refl) return refl
|
|
26
26
|
}
|
|
27
|
-
} catch {
|
|
27
|
+
} catch (err) {
|
|
28
|
+
logger.debug('[dsh-moa:ast] context-lens reflection error:', err?.message || err)
|
|
29
|
+
}
|
|
28
30
|
return null
|
|
29
31
|
}
|
|
30
32
|
|
|
@@ -228,3 +230,4 @@ export async function buildAstCompositeMergeDirective(candidates = [], options =
|
|
|
228
230
|
directive,
|
|
229
231
|
}
|
|
230
232
|
}
|
|
233
|
+
|
package/lib/moa-schema.js
CHANGED
|
@@ -76,6 +76,13 @@ export const PresetSchema = z.object({
|
|
|
76
76
|
speculative_moa_enabled: z.boolean().default(false),
|
|
77
77
|
speculative_draft_model: ModelSlotSchema.default({ provider: "", model: "" }),
|
|
78
78
|
adversarial_tdd_enabled: z.boolean().default(false),
|
|
79
|
+
local_preflight_check: z.boolean().default(true),
|
|
80
|
+
early_exit_consensus_enabled: z.boolean().default(false),
|
|
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),
|
|
79
86
|
})
|
|
80
87
|
|
|
81
88
|
export const Config = z.object({
|
|
@@ -98,6 +105,13 @@ export const Config = z.object({
|
|
|
98
105
|
speculative_moa_enabled: z.boolean().default(false).volatile(),
|
|
99
106
|
speculative_draft_model: ModelSlotSchema.default({ provider: "", model: "" }).volatile(),
|
|
100
107
|
adversarial_tdd_enabled: z.boolean().default(false).volatile(),
|
|
108
|
+
local_preflight_check: z.boolean().default(true).volatile(),
|
|
109
|
+
early_exit_consensus_enabled: z.boolean().default(false).volatile(),
|
|
110
|
+
early_exit_threshold: z.number().default(0.95).volatile(),
|
|
111
|
+
context_pruning_enabled: z.boolean().default(true).volatile(),
|
|
112
|
+
context_pruning_diff_lines: z.number().default(5).volatile(),
|
|
113
|
+
semantic_cache_enabled: z.boolean().default(false).volatile(),
|
|
114
|
+
semantic_cache_ttl_hours: z.number().default(168).volatile(),
|
|
101
115
|
smart_routing_model: ModelSlotSchema.default({ provider: '', model: '' }).volatile(),
|
|
102
116
|
prices: z.dict(PriceRow).default({}).volatile(),
|
|
103
117
|
presets: z.array(PresetSchema).default([
|
|
@@ -114,6 +128,10 @@ export const Config = z.object({
|
|
|
114
128
|
aggregator_temperature: 0.4,
|
|
115
129
|
max_tokens: 4096,
|
|
116
130
|
judge_criteria: '',
|
|
131
|
+
context_pruning_enabled: true,
|
|
132
|
+
context_pruning_diff_lines: 5,
|
|
133
|
+
semantic_cache_enabled: false,
|
|
134
|
+
semantic_cache_ttl_hours: 168,
|
|
117
135
|
},
|
|
118
136
|
{
|
|
119
137
|
name: 'fast',
|
|
@@ -127,6 +145,10 @@ export const Config = z.object({
|
|
|
127
145
|
aggregator_temperature: 0.2,
|
|
128
146
|
max_tokens: 4096,
|
|
129
147
|
judge_criteria: '',
|
|
148
|
+
context_pruning_enabled: true,
|
|
149
|
+
context_pruning_diff_lines: 5,
|
|
150
|
+
semantic_cache_enabled: false,
|
|
151
|
+
semantic_cache_ttl_hours: 168,
|
|
130
152
|
},
|
|
131
153
|
{
|
|
132
154
|
name: 'deep-reasoning',
|
|
@@ -159,3 +181,4 @@ export function plainConfig(value) {
|
|
|
159
181
|
}
|
|
160
182
|
return value
|
|
161
183
|
}
|
|
184
|
+
|
package/lib/moa-swarm.js
CHANGED
|
@@ -114,17 +114,23 @@ export async function cleanupSwarmWorktrees(cwd, runId, worktrees = []) {
|
|
|
114
114
|
if (wt.isGitWorktree) {
|
|
115
115
|
try {
|
|
116
116
|
await execFileAsync('git', ['worktree', 'remove', '--force', wt.worktreePath], { cwd, timeout: 8000 })
|
|
117
|
-
} catch {
|
|
117
|
+
} catch (err) {
|
|
118
|
+
logger.debug('[dsh-moa:swarm] cleanup worktree remove error:', err?.message || err)
|
|
119
|
+
}
|
|
118
120
|
}
|
|
119
121
|
try {
|
|
120
122
|
await fs.rm(wt.worktreePath, { recursive: true, force: true })
|
|
121
|
-
} catch {
|
|
123
|
+
} catch (err) {
|
|
124
|
+
logger.debug('[dsh-moa:swarm] cleanup rm error:', err?.message || err)
|
|
125
|
+
}
|
|
122
126
|
}
|
|
123
127
|
|
|
124
128
|
if (await isGitRepository(cwd)) {
|
|
125
129
|
try {
|
|
126
130
|
await execFileAsync('git', ['worktree', 'prune'], { cwd, timeout: 5000 })
|
|
127
|
-
} catch {
|
|
131
|
+
} catch (err) {
|
|
132
|
+
logger.debug('[dsh-moa:swarm] cleanup prune error:', err?.message || err)
|
|
133
|
+
}
|
|
128
134
|
}
|
|
129
135
|
|
|
130
136
|
const safeRunId = String(runId || '').replace(/[^a-zA-Z0-9_-]/g, '')
|
|
@@ -132,7 +138,9 @@ export async function cleanupSwarmWorktrees(cwd, runId, worktrees = []) {
|
|
|
132
138
|
const swarmRoot = path.join(cwd, '.moa', `swarm-${safeRunId}`)
|
|
133
139
|
try {
|
|
134
140
|
await fs.rm(swarmRoot, { recursive: true, force: true })
|
|
135
|
-
} catch {
|
|
141
|
+
} catch (err) {
|
|
142
|
+
logger.debug('[dsh-moa:swarm] cleanup swarmRoot error:', err?.message || err)
|
|
143
|
+
}
|
|
136
144
|
}
|
|
137
145
|
}
|
|
138
146
|
|
|
@@ -217,3 +225,4 @@ export async function runSubagentSwarm(references, messages, options = {}, callL
|
|
|
217
225
|
await cleanupSwarmWorktrees(cwd, runId, worktrees)
|
|
218
226
|
}
|
|
219
227
|
}
|
|
228
|
+
|
|
@@ -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
|
-
|
|
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 },
|
|
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
|
-
|
|
96
|
+
effectiveCallLlm,
|
|
84
97
|
onProgress
|
|
85
98
|
)
|
|
86
99
|
}
|