@goodandready/dsh-moa 0.2.9 → 0.2.10
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 +64 -37
- package/docs/README.ru.md +64 -37
- package/docs/README.zh.md +70 -27
- package/docs/design/DESIGN.md +14 -6
- package/docs/plans/47-client-js-split-plan.md +57 -0
- package/lib/client.js +260 -85
- package/lib/file-workspace.js +3 -1
- package/lib/history.js +4 -1
- package/lib/index.js +66 -12
- package/lib/live-canvas.js +34 -0
- package/lib/moa-runner.js +528 -177
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { refreshCatalogInBackground } from './pricing.js'
|
|
|
6
6
|
* 1. User issues /moa <prompt>
|
|
7
7
|
* 2. Parallel candidate proposers write to isolated workspaces (.moa/candidate-X/)
|
|
8
8
|
* 3. Aggregator evaluates and synthesizes the optimal solution
|
|
9
|
-
* 4. Winner candidate files are promoted to the project root
|
|
9
|
+
* 4. Winner candidate files are promoted to the project root
|
|
10
10
|
* 5. Native cost tracking, history logging and leaderboard analytics
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -25,6 +25,8 @@ import {
|
|
|
25
25
|
getMoaRunById,
|
|
26
26
|
} from './history.js'
|
|
27
27
|
|
|
28
|
+
import { createLiveCanvasClient } from './live-canvas.js'
|
|
29
|
+
|
|
28
30
|
export const name = 'dsh-moa'
|
|
29
31
|
export const inject = ['webServer', 'llm', 'settings', 'sessions', 'tools']
|
|
30
32
|
|
|
@@ -45,13 +47,17 @@ export const PresetSchema = z.object({
|
|
|
45
47
|
name: z.string(),
|
|
46
48
|
enabled: z.boolean().default(true),
|
|
47
49
|
ask_clarifying_questions: z.boolean().default(true),
|
|
50
|
+
curator_synthesis: z.boolean().default(false),
|
|
51
|
+
stream_aggregator: z.boolean().default(true),
|
|
52
|
+
quorum_enabled: z.boolean().default(false),
|
|
53
|
+
grace_period_sec: z.number().default(10),
|
|
48
54
|
reference_models: z.array(ModelSlotSchema).default([]),
|
|
49
55
|
aggregator: ModelSlotSchema.default({ provider: 'codex', model: 'gpt-5.6-sol' }),
|
|
56
|
+
aggregator_fallbacks: z.array(ModelSlotSchema).default([]),
|
|
50
57
|
reference_temperature: z.number().default(0.6),
|
|
51
58
|
aggregator_temperature: z.number().default(0.4),
|
|
52
59
|
max_tokens: z.number().default(4096),
|
|
53
60
|
judge_criteria: z.string().default(''),
|
|
54
|
-
judge_mode: z.string().default('auto'),
|
|
55
61
|
})
|
|
56
62
|
|
|
57
63
|
export const Config = z.object({
|
|
@@ -72,7 +78,6 @@ export const Config = z.object({
|
|
|
72
78
|
aggregator_temperature: 0.4,
|
|
73
79
|
max_tokens: 4096,
|
|
74
80
|
judge_criteria: '',
|
|
75
|
-
judge_mode: 'auto',
|
|
76
81
|
},
|
|
77
82
|
{
|
|
78
83
|
name: 'fast',
|
|
@@ -86,7 +91,6 @@ export const Config = z.object({
|
|
|
86
91
|
aggregator_temperature: 0.2,
|
|
87
92
|
max_tokens: 4096,
|
|
88
93
|
judge_criteria: '',
|
|
89
|
-
judge_mode: 'auto',
|
|
90
94
|
},
|
|
91
95
|
{
|
|
92
96
|
name: 'deep-reasoning',
|
|
@@ -100,8 +104,7 @@ export const Config = z.object({
|
|
|
100
104
|
reference_temperature: 0.6,
|
|
101
105
|
aggregator_temperature: 0.2,
|
|
102
106
|
max_tokens: 8192,
|
|
103
|
-
judge_criteria: '
|
|
104
|
-
judge_mode: 'auto',
|
|
107
|
+
judge_criteria: '',
|
|
105
108
|
},
|
|
106
109
|
]),
|
|
107
110
|
})
|
|
@@ -153,7 +156,7 @@ export function apply(ctx, config) {
|
|
|
153
156
|
/**
|
|
154
157
|
* Unified LLM dispatch function using ctx.llm.prepareCall / stream
|
|
155
158
|
*/
|
|
156
|
-
const callLlm = async ({ provider, model, messages, temperature = 0.6, maxTokens = 4096 }) => {
|
|
159
|
+
const callLlm = async ({ provider, model, messages, temperature = 0.6, maxTokens = 4096, onStreamDelta }) => {
|
|
157
160
|
if (!ctx.llm) {
|
|
158
161
|
throw new Error('ctx.llm is not available in cordis context')
|
|
159
162
|
}
|
|
@@ -195,6 +198,9 @@ export function apply(ctx, config) {
|
|
|
195
198
|
for await (const chunk of stream) {
|
|
196
199
|
if (chunk.type === 'text-delta' && typeof chunk.text === 'string') {
|
|
197
200
|
fullText += chunk.text
|
|
201
|
+
if (typeof onStreamDelta === 'function') {
|
|
202
|
+
onStreamDelta(chunk.text)
|
|
203
|
+
}
|
|
198
204
|
} else if (chunk.type === 'block-end' && chunk.block?.text) {
|
|
199
205
|
if (!fullText) fullText = chunk.block.text
|
|
200
206
|
} else if (chunk.type === 'usage' && chunk.usage) {
|
|
@@ -217,6 +223,11 @@ export function apply(ctx, config) {
|
|
|
217
223
|
}
|
|
218
224
|
}
|
|
219
225
|
|
|
226
|
+
// Optional Live Canvas preview client: the harness port is resolved per
|
|
227
|
+
// call and any failure degrades to "no preview link" (live-canvas is not
|
|
228
|
+
// required to be installed).
|
|
229
|
+
const liveCanvas = createLiveCanvasClient({ getPort: () => ctx.webServer?.port })
|
|
230
|
+
|
|
220
231
|
// Route: /dsh-moa/status
|
|
221
232
|
ctx.effect(() => {
|
|
222
233
|
return ctx.webServer.register({
|
|
@@ -342,23 +353,35 @@ export function apply(ctx, config) {
|
|
|
342
353
|
const raw = await readBody(req)
|
|
343
354
|
const payload = JSON.parse(raw)
|
|
344
355
|
const newConfig = {
|
|
345
|
-
enabled: cfg.enabled !== false,
|
|
356
|
+
enabled: typeof payload.enabled === 'boolean' ? payload.enabled : cfg.enabled !== false,
|
|
346
357
|
default_preset: payload.default_preset || cfg.default_preset || 'default',
|
|
347
358
|
presets: Array.isArray(payload.presets) ? payload.presets : (cfg.presets || []),
|
|
348
359
|
}
|
|
349
360
|
|
|
361
|
+
// Validate the merged payload through the plugin schema before it
|
|
362
|
+
// reaches the settings storage: malformed payloads must be
|
|
363
|
+
// rejected with 400 instead of being persisted.
|
|
364
|
+
let validated
|
|
365
|
+
try {
|
|
366
|
+
validated = Config(structuredClone(newConfig))
|
|
367
|
+
} catch (verr) {
|
|
368
|
+
writeJson(res, 400, { ok: false, error: 'Invalid presets payload: ' + (verr?.message || String(verr)) })
|
|
369
|
+
return
|
|
370
|
+
}
|
|
371
|
+
|
|
350
372
|
// Persist via Cordis Settings API
|
|
351
373
|
if (settingsScope && typeof settingsScope.replace === 'function') {
|
|
352
|
-
await settingsScope.replace(
|
|
374
|
+
await settingsScope.replace(validated)
|
|
353
375
|
} else {
|
|
354
376
|
// In-memory fallback if settings service is unavailable
|
|
355
|
-
getConfig = () =>
|
|
377
|
+
getConfig = () => validated
|
|
356
378
|
}
|
|
357
379
|
|
|
358
380
|
writeJson(res, 200, {
|
|
359
381
|
ok: true,
|
|
360
|
-
|
|
361
|
-
|
|
382
|
+
enabled: validated.enabled !== false,
|
|
383
|
+
defaultPreset: validated.default_preset,
|
|
384
|
+
presets: validated.presets,
|
|
362
385
|
})
|
|
363
386
|
} catch (err) {
|
|
364
387
|
writeJson(res, 400, { ok: false, error: err?.message || String(err) })
|
|
@@ -414,6 +437,29 @@ export function apply(ctx, config) {
|
|
|
414
437
|
})
|
|
415
438
|
}, 'dsh-moa: leaderboard route')
|
|
416
439
|
|
|
440
|
+
// Route: /dsh-moa/runs/<id> (run replay by id)
|
|
441
|
+
ctx.effect(() => {
|
|
442
|
+
return ctx.webServer.register({
|
|
443
|
+
kind: 'prefix',
|
|
444
|
+
path: '/dsh-moa/runs/',
|
|
445
|
+
handler: (req, res) => {
|
|
446
|
+
if (req.method !== 'GET') {
|
|
447
|
+
writeJson(res, 405, { ok: false, error: 'GET required' })
|
|
448
|
+
return
|
|
449
|
+
}
|
|
450
|
+
const urlPath = req.url.split('?')[0]
|
|
451
|
+
const parts = urlPath.split('/').filter(Boolean)
|
|
452
|
+
const runId = parts[2] || ''
|
|
453
|
+
const run = runId ? getMoaRunById(runId) : null
|
|
454
|
+
if (!run) {
|
|
455
|
+
writeJson(res, 404, { ok: false, error: 'Run not found' })
|
|
456
|
+
return
|
|
457
|
+
}
|
|
458
|
+
writeJson(res, 200, { ok: true, run })
|
|
459
|
+
},
|
|
460
|
+
})
|
|
461
|
+
}, 'dsh-moa: run by id route')
|
|
462
|
+
|
|
417
463
|
// Route: /dsh-moa/run
|
|
418
464
|
ctx.effect(() => {
|
|
419
465
|
return ctx.webServer.register({
|
|
@@ -435,6 +481,10 @@ export function apply(ctx, config) {
|
|
|
435
481
|
}
|
|
436
482
|
|
|
437
483
|
const cfg = live()
|
|
484
|
+
if (cfg.enabled === false) {
|
|
485
|
+
writeJson(res, 400, { ok: false, error: 'MoA is disabled in settings' })
|
|
486
|
+
return
|
|
487
|
+
}
|
|
438
488
|
const presetName = payload.preset || cfg.default_preset || 'default'
|
|
439
489
|
const preset = (cfg.presets || []).find((p) => p.name === presetName) || cfg.presets?.[0]
|
|
440
490
|
|
|
@@ -444,6 +494,8 @@ export function apply(ctx, config) {
|
|
|
444
494
|
preset,
|
|
445
495
|
callLlm,
|
|
446
496
|
cwd: payload.cwd || process.cwd(),
|
|
497
|
+
prices: cfg.prices || {},
|
|
498
|
+
liveCanvas,
|
|
447
499
|
})
|
|
448
500
|
|
|
449
501
|
writeJson(res, 200, { ok: true, ...result })
|
|
@@ -511,6 +563,8 @@ export function apply(ctx, config) {
|
|
|
511
563
|
cwd: sessCwd,
|
|
512
564
|
aggregator: { provider: aggProvider, model: aggModel },
|
|
513
565
|
callLlm,
|
|
566
|
+
prices: cfg.prices || {},
|
|
567
|
+
liveCanvas,
|
|
514
568
|
}
|
|
515
569
|
|
|
516
570
|
if (event.signal) {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional Live Canvas preview client (@goodandready/dsh-live-canvas).
|
|
3
|
+
*
|
|
4
|
+
* The plugin posts the promoted HTML to live-canvas's own REST contract
|
|
5
|
+
* (POST /dsh-live-canvas/api/preview, served by the same harness webServer),
|
|
6
|
+
* so no cross-plugin service coupling or module imports are needed. Any
|
|
7
|
+
* failure — live-canvas not installed, endpoint missing, timeout, malformed
|
|
8
|
+
* response — degrades to null and the MoA answer simply carries no preview
|
|
9
|
+
* link, without errors in the harness log.
|
|
10
|
+
*/
|
|
11
|
+
export function createLiveCanvasClient({ getPort, fetchImpl = globalThis.fetch, timeoutMs = 3000 } = {}) {
|
|
12
|
+
if (typeof fetchImpl !== 'function') return null
|
|
13
|
+
return {
|
|
14
|
+
async createPreviewFromContent({ content, title, filePath } = {}) {
|
|
15
|
+
if (!content || typeof content !== 'string') return null
|
|
16
|
+
const port = typeof getPort === 'function' ? getPort() : getPort
|
|
17
|
+
if (!port) return null
|
|
18
|
+
try {
|
|
19
|
+
const res = await fetchImpl(`http://127.0.0.1:${port}/dsh-live-canvas/api/preview`, {
|
|
20
|
+
method: 'POST',
|
|
21
|
+
headers: { 'Content-Type': 'application/json' },
|
|
22
|
+
body: JSON.stringify({ content, title, filePath }),
|
|
23
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
24
|
+
})
|
|
25
|
+
if (!res.ok) return null
|
|
26
|
+
const data = await res.json()
|
|
27
|
+
if (!data || data.success !== true || !data.canvasId || !data.previewUrl) return null
|
|
28
|
+
return { canvasId: data.canvasId, previewUrl: data.previewUrl, title: title || 'Live Preview' }
|
|
29
|
+
} catch {
|
|
30
|
+
return null
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
}
|
|
34
|
+
}
|