@goodandready/dsh-moa 0.2.13 → 0.2.14
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/docs/design/DESIGN.md +2 -0
- package/lib/client.js +106 -8
- package/lib/file-workspace.js +7 -5
- package/lib/moa-parser.js +3 -3
- package/lib/moa-prompts.js +5 -4
- package/lib/moa-runner.js +5 -5
- package/package.json +1 -1
package/docs/design/DESIGN.md
CHANGED
|
@@ -69,3 +69,5 @@
|
|
|
69
69
|
- 2026-09-12 (вечер) — Декомпозиция lib/moa-runner.js на специализированные модули (`lib/moa-prompts.js`, `lib/moa-parser.js`, `lib/moa-runner.js`) с соблюдением канонического лимита <= 800 строк. Реализация автоматической ротации истории при превышении 10 МБ (`rotateHistoryFileIfNeeded`) и неблокирующего асинхронного сохранения (`recordMoaRunAsync`). Настраиваемые таймауты кандидатов и агрегатора (`reference_timeout_sec`, `aggregator_timeout_sec`).
|
|
70
70
|
|
|
71
71
|
- 2026-09-13 — Расширение функционала MoA (Gitea Issue #59): интерактивный селектор альтернативного кандидата в чате (User Candidate Override: allow_candidate_override), пакет из 10 специализированных пресетов с ролями кандидатов (role_persona), локальный гейт синтаксической проверки (Syntax Pre-check Gate) с директивой автоисправления синтаксиса судьёй, режим взаимного рецензирования («Консилиум» / Peer Critique Round 2: peer_critique_enabled), фильтрация лидерборда по пресетам и выгрузка телеметрии в CSV/JSON.
|
|
72
|
+
|
|
73
|
+
- 2026-09-13 — Приведение локализации плагина в строгое соответствие со стандартами dhs-plugin-release-workflow и dsh-plugin-authoring (Gitea Issue #61): добавление полного китайского словаря (zh) в lib/client.js наряду с каноническим английским (en), удаление захардкоженных русских строк в серверной части (lib/moa-parser.js, lib/moa-runner.js), интернационализация эвристик в lib/file-workspace.js и lib/moa-prompts.js (en + zh), создание issue в goodandready/dsh-russian-lang для русификации.
|
package/lib/client.js
CHANGED
|
@@ -60,6 +60,11 @@ window.__ModuleLoader__.load({
|
|
|
60
60
|
'aggregator.override_label': 'Allow Candidate Override Actions',
|
|
61
61
|
'aggregator.override_hint': 'Keeps candidate workspaces in .moa to let you apply any candidate files via chat command.',
|
|
62
62
|
'proposers.role_label': 'Persona:',
|
|
63
|
+
'proposers.role_general': 'General',
|
|
64
|
+
'proposers.role_minimalist': 'Minimalist',
|
|
65
|
+
'proposers.role_robustness': 'Robustness',
|
|
66
|
+
'proposers.role_performance': 'Performance',
|
|
67
|
+
'proposers.role_tester': 'Tester',
|
|
63
68
|
'leaderboard.filter_all': 'All Presets',
|
|
64
69
|
'leaderboard.export_csv': 'Export CSV',
|
|
65
70
|
'leaderboard.export_json': 'Export JSON',
|
|
@@ -110,6 +115,99 @@ window.__ModuleLoader__.load({
|
|
|
110
115
|
'slash.desc': 'Run turn with Mixture of Agents ensemble synthesis (/moa [preset] <prompt>)',
|
|
111
116
|
}
|
|
112
117
|
|
|
118
|
+
const zh = {
|
|
119
|
+
title: '智能体混合 (MoA)',
|
|
120
|
+
description: '多模型协同编排:通过 /moa <提示词> 进行候选模型并行提案与主裁判综合。',
|
|
121
|
+
'header.title': '智能体混合 (MoA) 编排系统',
|
|
122
|
+
'header.sub': '由多个候选提案模型并行生成方案,再由领先的聚合裁判模型进行终审评测与代码提拔。完成后自动切回您的会话模型。',
|
|
123
|
+
'badge.online': 'MoA 已启用',
|
|
124
|
+
'badge.offline': '主机不可达',
|
|
125
|
+
'badge.preset': '预设:{name}',
|
|
126
|
+
'badge.models': '{count} 个可用模型',
|
|
127
|
+
'badge.candidates': '{count} 个提案模型',
|
|
128
|
+
'presets.title': '📋 预设管理',
|
|
129
|
+
'presets.desc': '选择当前活动预设或针对特定任务(如代码审查、数学推演、快速分流)创建自定义配置。',
|
|
130
|
+
'presets.active_badge': '默认激活',
|
|
131
|
+
'presets.set_default': '设为默认',
|
|
132
|
+
'presets.new_btn': '+ 新建预设',
|
|
133
|
+
'presets.del_btn': '删除',
|
|
134
|
+
'presets.del_confirm': '确定要删除预设“{name}”吗?',
|
|
135
|
+
'presets.create': '创建',
|
|
136
|
+
'presets.cancel': '取消',
|
|
137
|
+
'presets.name_placeholder': '新预设名称(例如:fast-audit)',
|
|
138
|
+
'aggregator.title': '⚖️ 领先模型 / 裁判(综合与审计)',
|
|
139
|
+
'aggregator.desc': '对比候选方案,消除差异,过滤幻觉,并综合生成最终生产级解决方案。',
|
|
140
|
+
'aggregator.temp_label': '裁判采样温度 (Temperature)',
|
|
141
|
+
'aggregator.temp_hint': '推荐:0.2 – 0.4,以确保严格的验证和确定性的代码综合。',
|
|
142
|
+
'aggregator.criteria_label': '裁判评测标准(可选):',
|
|
143
|
+
'aggregator.criteria_placeholder': '例如:优先考虑高性能、零外部依赖、完善的边界异常处理...',
|
|
144
|
+
'aggregator.questions_label': '对于宽泛任务先提出澄清问题(需求问卷)',
|
|
145
|
+
'aggregator.questions_hint': '启用时:对于简短或模糊的任务,裁判在生成代码前会先综合一份包含 2–4 个问题的选项清单。',
|
|
146
|
+
'aggregator.curator_label': '策展人综合与主组装模型建议',
|
|
147
|
+
'aggregator.curator_hint': '启用时:策展人会提炼每个候选方案的精华部分,应用反模式清单,并建议由哪个智能体模型组装最终方案。',
|
|
148
|
+
'aggregator.stream_label': '实时流式传输裁判/策展人思考过程',
|
|
149
|
+
'aggregator.stream_hint': '将裁判生成的思考过程实时推送到聊天框,实现零延迟首字响应。',
|
|
150
|
+
'aggregator.peer_label': '决策评议会:候选模型交叉互评(第二轮 Consilium)',
|
|
151
|
+
'aggregator.peer_hint': '在主裁判终审前,各候选模型互相审阅并改进彼此的方案代码。',
|
|
152
|
+
'aggregator.override_label': '允许手动候选方案提拔操作 (Override)',
|
|
153
|
+
'aggregator.override_hint': '在 .moa 目录中保留候选模型的工作区,允许通过聊天命令提拔任意候选模型的文件。',
|
|
154
|
+
'proposers.role_label': '工程角色画像:',
|
|
155
|
+
'proposers.role_general': '通用平衡 (General)',
|
|
156
|
+
'proposers.role_minimalist': '极简标准库 (Minimalist)',
|
|
157
|
+
'proposers.role_robustness': '防御鲁棒 (Robustness)',
|
|
158
|
+
'proposers.role_performance': '极致性能 (Performance)',
|
|
159
|
+
'proposers.role_tester': '测试驱动 (Tester)',
|
|
160
|
+
'leaderboard.filter_all': '全部预设',
|
|
161
|
+
'leaderboard.export_csv': '导出 CSV',
|
|
162
|
+
'leaderboard.export_json': '导出 JSON',
|
|
163
|
+
'aggregator.blind_label': '盲审模式(对裁判隐藏候选模型名称)',
|
|
164
|
+
'aggregator.blind_hint': '启用时:从裁判提示词中匿名化候选模型的提供商和名称,杜绝品牌偏见。',
|
|
165
|
+
'aggregator.timeout_label': '裁判综合超时时长(秒):',
|
|
166
|
+
'proposers.timeout_label': '候选模型生成超时时长(秒):',
|
|
167
|
+
'leaderboard.title': '🏆 模型胜率排行榜',
|
|
168
|
+
'leaderboard.desc': '基于历史记录的 MoA 裁判评测判决数据统计指标。',
|
|
169
|
+
'leaderboard.model': '模型',
|
|
170
|
+
'leaderboard.runs': '运行次数',
|
|
171
|
+
'leaderboard.wins': '胜出次数',
|
|
172
|
+
'leaderboard.winrate': '胜率',
|
|
173
|
+
'leaderboard.cost': '平均成本',
|
|
174
|
+
'leaderboard.empty': '暂无历史运行记录。',
|
|
175
|
+
'aggregator.fallbacks_title': '备用裁判链',
|
|
176
|
+
'aggregator.fallbacks_desc': '当主裁判触发速率限制或服务不可用时,自动顺序调用的备用模型。',
|
|
177
|
+
'aggregator.add_fallback_btn': '+ 添加备用裁判',
|
|
178
|
+
'proposers.quorum_label': '法定人数快进(防拖尾模型机制)',
|
|
179
|
+
'proposers.quorum_hint': '当 67% 以上候选模型完成时,开启缓冲定时器(默认 10 秒),不再死等卡顿的模型。',
|
|
180
|
+
'proposers.grace_label': '缓冲等待时长(秒):',
|
|
181
|
+
'proposers.title': '👥 候选提案模型 (Proposers)',
|
|
182
|
+
'proposers.desc': '并行生成互相独立的解决方案,最大化思路多样性与解空间覆盖度。',
|
|
183
|
+
'proposers.parallel_count': '并行提案模型数量',
|
|
184
|
+
'proposers.candidate': '候选模型',
|
|
185
|
+
'proposers.add_btn': '+ 添加候选模型',
|
|
186
|
+
'proposers.temp_label': '候选模型采样温度 (Temperature)',
|
|
187
|
+
'proposers.temp_hint': '推荐:0.6 – 0.8,鼓励创造性、发散性的独立探索。',
|
|
188
|
+
'stats.title': '📈 MoA 数据分析与遥测',
|
|
189
|
+
'stats.desc': '会话遥测、历史运行追踪及模型胜率统计。',
|
|
190
|
+
'stats.total_runs': '总运行次数',
|
|
191
|
+
'stats.avg_cost': '平均每次成本',
|
|
192
|
+
'stats.active_models': '已配置模型数',
|
|
193
|
+
'stats.preset_count': '预设总数',
|
|
194
|
+
'picker.select': '选择模型…',
|
|
195
|
+
'picker.search_placeholder': '按名称或提供商搜索模型…',
|
|
196
|
+
'picker.found': '找到',
|
|
197
|
+
'picker.of': '共',
|
|
198
|
+
'picker.clear': '清除',
|
|
199
|
+
'picker.none': '未找到匹配的模型。',
|
|
200
|
+
'picker.custom': '使用自定义:',
|
|
201
|
+
'actions.save': '保存配置',
|
|
202
|
+
'actions.saving': '正在保存…',
|
|
203
|
+
'actions.saved': '配置已成功保存!',
|
|
204
|
+
'actions.retry': '重新加载配置',
|
|
205
|
+
'badge.disabled': 'MoA 已禁用',
|
|
206
|
+
'config.enabled_label': '启用 MoA(斜杠命令与轮次拦截路由)',
|
|
207
|
+
'slash.desc': '使用智能体混合 (MoA) 协同综合运行此轮次 (/moa [预设] <提示词>)',
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
|
|
113
211
|
function makeT(dict, fallback) {
|
|
114
212
|
return function t(key, vars) {
|
|
115
213
|
let val = (dict && dict[key]) || (fallback && fallback[key]) || key
|
|
@@ -1110,11 +1208,11 @@ window.__ModuleLoader__.load({
|
|
|
1110
1208
|
})
|
|
1111
1209
|
},
|
|
1112
1210
|
},
|
|
1113
|
-
React.createElement('option', { value: 'general' }, 'General'),
|
|
1114
|
-
React.createElement('option', { value: 'minimalist' }, 'Minimalist'),
|
|
1115
|
-
React.createElement('option', { value: 'robustness' }, 'Robustness'),
|
|
1116
|
-
React.createElement('option', { value: 'performance' }, 'Performance'),
|
|
1117
|
-
React.createElement('option', { value: 'tester' }, 'Tester')
|
|
1211
|
+
React.createElement('option', { value: 'general' }, t('proposers.role_general') || 'General'),
|
|
1212
|
+
React.createElement('option', { value: 'minimalist' }, t('proposers.role_minimalist') || 'Minimalist'),
|
|
1213
|
+
React.createElement('option', { value: 'robustness' }, t('proposers.role_robustness') || 'Robustness'),
|
|
1214
|
+
React.createElement('option', { value: 'performance' }, t('proposers.role_performance') || 'Performance'),
|
|
1215
|
+
React.createElement('option', { value: 'tester' }, t('proposers.role_tester') || 'Tester')
|
|
1118
1216
|
),
|
|
1119
1217
|
currentRefs.length > 1 &&
|
|
1120
1218
|
React.createElement(
|
|
@@ -1229,7 +1327,7 @@ window.__ModuleLoader__.load({
|
|
|
1229
1327
|
className: 'moa-btn',
|
|
1230
1328
|
style: { height: 26, fontSize: 11, padding: '0 8px', textDecoration: 'none', display: 'inline-flex', alignItems: 'center' },
|
|
1231
1329
|
},
|
|
1232
|
-
'CSV'
|
|
1330
|
+
t('leaderboard.export_csv') || 'CSV'
|
|
1233
1331
|
),
|
|
1234
1332
|
React.createElement(
|
|
1235
1333
|
'a',
|
|
@@ -1239,7 +1337,7 @@ window.__ModuleLoader__.load({
|
|
|
1239
1337
|
className: 'moa-btn',
|
|
1240
1338
|
style: { height: 26, fontSize: 11, padding: '0 8px', textDecoration: 'none', display: 'inline-flex', alignItems: 'center' },
|
|
1241
1339
|
},
|
|
1242
|
-
'JSON'
|
|
1340
|
+
t('leaderboard.export_json') || 'JSON'
|
|
1243
1341
|
)
|
|
1244
1342
|
)
|
|
1245
1343
|
),
|
|
@@ -1628,7 +1726,7 @@ window.__ModuleLoader__.load({
|
|
|
1628
1726
|
|
|
1629
1727
|
if (typeof ctx.effect === 'function') {
|
|
1630
1728
|
ctx.effect(() => {
|
|
1631
|
-
const undo = [addLocale('en', en)]
|
|
1729
|
+
const undo = [addLocale('en', en), addLocale('zh', zh)]
|
|
1632
1730
|
return () => {
|
|
1633
1731
|
for (const off of undo) {
|
|
1634
1732
|
if (typeof off === 'function') {
|
package/lib/file-workspace.js
CHANGED
|
@@ -152,10 +152,10 @@ export function isRefinementTask(userPrompt = '', projectFiles = []) {
|
|
|
152
152
|
|
|
153
153
|
// Greenfield creation phrases - ALWAYS fresh task
|
|
154
154
|
const freshPhrases = [
|
|
155
|
-
'
|
|
156
|
-
'
|
|
155
|
+
'from scratch', 'new project', 'new app', 'create a new', 'build a new', 'generate a new',
|
|
156
|
+
'从头开始', '新建项目', '创建项目', '新应用', '新建应用',
|
|
157
|
+
'с нуля', 'новый проект', 'новое приложение', 'создай проект', 'создай приложение', 'создай игру', 'создай сервис',
|
|
157
158
|
'сделай проект', 'сделай приложение', 'сделай игру',
|
|
158
|
-
'create a new', 'build a new', 'generate a new',
|
|
159
159
|
]
|
|
160
160
|
if (freshPhrases.some((phrase) => p.includes(phrase))) {
|
|
161
161
|
return false
|
|
@@ -163,9 +163,11 @@ export function isRefinementTask(userPrompt = '', projectFiles = []) {
|
|
|
163
163
|
|
|
164
164
|
// Modification keywords
|
|
165
165
|
const modKeywords = [
|
|
166
|
-
'добавь', 'измени', 'поменяй', 'исправь', 'обнови', 'переделай', 'доработай', 'удали',
|
|
167
166
|
'add', 'change', 'update', 'fix', 'modify', 'refactor', 'improve', 'enhance', 'adjust',
|
|
168
|
-
'style', 'css', '
|
|
167
|
+
'style', 'css', 'color', 'theme', 'bug',
|
|
168
|
+
'添加', '修改', '更新', '修复', '重构', '改进', '调整', '样式', '颜色', '主题',
|
|
169
|
+
'добавь', 'измени', 'поменяй', 'исправь', 'обнови', 'переделай', 'доработай', 'удали',
|
|
170
|
+
'темн', 'светл', 'цвет', 'кнопк', 'баг',
|
|
169
171
|
]
|
|
170
172
|
|
|
171
173
|
return modKeywords.some((kw) => p.includes(kw))
|
package/lib/moa-parser.js
CHANGED
|
@@ -148,7 +148,7 @@ export function formatMoAResponse({ moaResult, presetName }) {
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
if (moaResult?.liveCanvas?.previewUrl) {
|
|
151
|
-
parts.push(`> 🎨 **Live Canvas**: [🚀
|
|
151
|
+
parts.push(`> 🎨 **Live Canvas**: [🚀 Open ${moaResult.liveCanvas.title || 'preview'} in Live Canvas](${moaResult.liveCanvas.previewUrl}) | [↗ Open in new tab](${moaResult.liveCanvas.previewUrl})`)
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
// Cost tracking card
|
|
@@ -166,7 +166,7 @@ export function formatMoAResponse({ moaResult, presetName }) {
|
|
|
166
166
|
parts.push('')
|
|
167
167
|
const cleanJudgeContent = hasPromoted
|
|
168
168
|
? stripOrSummarizeCode(moaResult?.content || '')
|
|
169
|
-
: (moaResult?.content || '(
|
|
169
|
+
: (moaResult?.content || '(no response)')
|
|
170
170
|
parts.push(cleanJudgeContent)
|
|
171
171
|
parts.push('')
|
|
172
172
|
}
|
|
@@ -177,7 +177,7 @@ export function formatMoAResponse({ moaResult, presetName }) {
|
|
|
177
177
|
parts.push('')
|
|
178
178
|
refs.forEach((ref, i) => {
|
|
179
179
|
const statusIcon = ref.ok ? '✅' : '⚠️'
|
|
180
|
-
const fileBadge = ref.files?.length ? ` (${ref.files.length}
|
|
180
|
+
const fileBadge = ref.files?.length ? ` (${ref.files.length} file${ref.files.length > 1 ? 's' : ''})` : ''
|
|
181
181
|
const costBadge = ref.costUsd > 0 ? ` [~\$${ref.costUsd.toFixed(4)}]` : ''
|
|
182
182
|
parts.push(`#### ${statusIcon} Model ${i + 1}: ${ref.label}${fileBadge}${costBadge}`)
|
|
183
183
|
parts.push('')
|
package/lib/moa-prompts.js
CHANGED
|
@@ -128,21 +128,22 @@ export function isBroadPromptRequiringQuestions(userPrompt = '', messages = [])
|
|
|
128
128
|
const p = userPrompt.trim()
|
|
129
129
|
const wordCount = p.split(/\s+/).length
|
|
130
130
|
|
|
131
|
-
if (/^(
|
|
131
|
+
if (/^(yes|no|1|2|3|4|ok|sure|是|否|好|да|нет|ок|погнали|давай)\b/i.test(p) && wordCount <= 5) {
|
|
132
132
|
return false
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
const hasRecentQuestion = messages.some((m) => {
|
|
136
136
|
const text = typeof m?.content === 'string' ? m.content : JSON.stringify(m?.content || '')
|
|
137
|
-
return text.includes('Уточнение требований') || text.includes('опросник') || text.includes('Вариант 1')
|
|
137
|
+
return text.includes('Clarification of Requirements') || text.includes('需求澄清') || text.includes('Уточнение требований') || text.includes('опросник') || text.includes('Option 1') || text.includes('Вариант 1')
|
|
138
138
|
})
|
|
139
139
|
if (hasRecentQuestion) {
|
|
140
140
|
return false
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
const creationTriggers = [
|
|
144
|
+
'make', 'build', 'create', 'generate', 'develop', 'design',
|
|
145
|
+
'制作', '创建', '构建', '开发', '设计',
|
|
144
146
|
'сделай', 'создай', 'напиши', 'разработай', 'придумай', 'реализуй',
|
|
145
|
-
'make', 'build', 'create', 'generate', 'develop',
|
|
146
147
|
]
|
|
147
148
|
const startsWithCreation = creationTriggers.some((t) => p.toLowerCase().startsWith(t))
|
|
148
149
|
|
|
@@ -150,7 +151,7 @@ export function isBroadPromptRequiringQuestions(userPrompt = '', messages = [])
|
|
|
150
151
|
return true
|
|
151
152
|
}
|
|
152
153
|
|
|
153
|
-
const vagueNouns = ['
|
|
154
|
+
const vagueNouns = ['app', 'game', 'tool', 'website', 'dashboard', 'widget', 'service', 'landing', 'calculator', '应用', '游戏', '工具', '网站', '仪表盘', '服务', 'приложение', 'игру', 'сервис', 'сайт', 'лендинг', 'калькулятор', 'виджет', 'дашборд']
|
|
154
155
|
if (vagueNouns.some((n) => p.toLowerCase().includes(n))) {
|
|
155
156
|
if (wordCount <= 12) return true
|
|
156
157
|
}
|
package/lib/moa-runner.js
CHANGED
|
@@ -396,11 +396,11 @@ export async function runMoAPipeline({
|
|
|
396
396
|
qUsage = estimateTokenCost(primaryJudge, qFallbackUsage, prices)
|
|
397
397
|
} catch (err) {
|
|
398
398
|
console.warn('[dsh-moa] Questionnaire synthesis failed, proceeding with fallback questions:', err)
|
|
399
|
-
questionsContent = `###
|
|
400
|
-
'1.
|
|
401
|
-
'2.
|
|
402
|
-
'3.
|
|
403
|
-
'
|
|
399
|
+
questionsContent = `### Clarification of Requirements: "${userPrompt}"\n\n` +
|
|
400
|
+
'1. **Architecture & Scope**: Single-file deliverable or multi-module project structure?\n' +
|
|
401
|
+
'2. **Design & Style**: Minimalist, dark mode, or clean neutral theme?\n' +
|
|
402
|
+
'3. **Functional Priorities**: Core MVP or comprehensive extended implementation?\n\n' +
|
|
403
|
+
'*Reply with your preferences (e.g. "1, 2") or proceed with defaults.*'
|
|
404
404
|
}
|
|
405
405
|
|
|
406
406
|
await cleanMoaWorkspaces(cwd)
|