@goodandready/dsh-moa 0.2.41 → 0.2.43

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/lib/moa-runner.js CHANGED
@@ -18,7 +18,7 @@ export { parseWinnerIndex, parseRecommendedAssembler, parseMoACommand, stripOrSu
18
18
  export { estimateTokenCost, summarizeMoAUsage } from './pricing.js'
19
19
  export { REFERENCE_SYSTEM_PROMPT, callWithTransientRetry, runReferencesParallel } from './moa-candidates.js'
20
20
  export { executeTestGateForCandidates, runCandidateTestGate } from './moa-test-gate.js'
21
- export { streamMoATurn } from './moa-stream.js'
21
+ export { streamMoATurn, streamPromoteTurn } from './moa-stream.js'
22
22
  /**
23
23
  * Executes the full Mixture of Agents pipeline.
24
24
  */
@@ -38,7 +38,8 @@ export async function runMoAPipeline({
38
38
  webServerPort = null, timeMachineEngine = null, timeMachineUrl = null,
39
39
  force = isTestEnvironment() ? true : false, checkpoint = true, checkpointFn = null, runId: customRunId = null,
40
40
  }) {
41
- const promoteOpts = { webServerPort, timeMachineEngine, timeMachineUrl, force: Boolean(force), checkpoint, checkpointFn }
41
+ const runId = (customRunId && String(customRunId).trim()) || crypto.randomUUID()
42
+ const promoteOpts = { webServerPort, timeMachineEngine, timeMachineUrl, force: Boolean(force), checkpoint, checkpointFn, signal }
42
43
  if (signal?.aborted) return { error: new Error('Turn aborted') }
43
44
  const startTime = Date.now()
44
45
  // 1. Resolve configurations
@@ -58,7 +59,7 @@ export async function runMoAPipeline({
58
59
  const candidateRetries = 1, refTimeoutSec = typeof preset?.reference_timeout_sec === 'number' ? preset.reference_timeout_sec : 60
59
60
  const aggTimeoutSec = typeof preset?.aggregator_timeout_sec === 'number' ? preset.aggregator_timeout_sec : 180
60
61
  const isBlindEvaluation = Boolean(preset?.blind_evaluation), isPeerCritiqueEnabled = Boolean(preset?.peer_critique_enabled)
61
- const allowCandidateOverride = Boolean(preset?.allow_candidate_override), runId = customRunId || null
62
+ const allowCandidateOverride = Boolean(preset?.allow_candidate_override)
62
63
  // Feature 6: Budget Guardrail check
63
64
  const budgetCheck = applyBudgetGuardrails({
64
65
  references: referenceModels,
@@ -229,6 +230,7 @@ export async function runMoAPipeline({
229
230
  const durationMs = Date.now() - startTime
230
231
  try {
231
232
  await recordMoaRunAsync({
233
+ cwd,
232
234
  prompt: userPrompt,
233
235
  preset: preset?.name || 'default',
234
236
  isRefinement,
@@ -274,57 +276,47 @@ export async function runMoAPipeline({
274
276
  return { error: new Error('Turn aborted') }
275
277
  }
276
278
  const single = successfulRefs[0]
277
- let promotedFiles = []
279
+ let promotedFiles = [], promoteErr = null
278
280
  if (single.files?.length > 0) {
279
- promotedFiles = await promoteCandidateWorkspace(cwd, single.index, { ...promoteOpts, runId })
280
- } else {
281
+ try {
282
+ promotedFiles = await promoteCandidateWorkspace(cwd, single.index, { ...promoteOpts, runId, keepMoa: allowCandidateOverride })
283
+ } catch (e) { promoteErr = e }
284
+ } else if (!allowCandidateOverride) {
281
285
  await cleanMoaWorkspaces(cwd, { runId })
282
286
  }
287
+ if (signal?.aborted || promoteErr?.message?.includes('aborted') || promoteErr?.name === 'AbortError') {
288
+ if (!allowCandidateOverride) await cleanMoaWorkspaces(cwd, { runId })
289
+ return { error: new Error('Turn aborted') }
290
+ }
283
291
 
284
- const livePreview = await createPromotedPreview(liveCanvas, cwd, promotedFiles, referenceOutputs)
292
+ const livePreview = await createPromotedPreview(liveCanvas, cwd, promotedFiles, referenceOutputs, { winningIndex: single.index })
285
293
  const totalTokens = single.usage?.totalTokens || 0
286
294
  const totalCostUsd = Number((single.costUsd || 0).toFixed(5))
287
295
  const durationMs = Date.now() - startTime
288
296
 
289
297
  try {
290
298
  await recordMoaRunAsync({
291
- prompt: userPrompt,
292
- preset: preset?.name || 'default',
293
- isRefinement,
294
- isFastMode: true,
295
- candidates: candidatesForHistory(referenceOutputs),
296
- aggregator: null,
297
- winnerIndex: single.index,
298
- winnerModel: single.label,
299
- promotedFiles,
300
- totalTokens,
301
- totalCostUsd,
302
- durationMs,
299
+ id: runId, cwd, prompt: userPrompt, preset: preset?.name || 'default',
300
+ isRefinement, isFastMode: true, candidates: candidatesForHistory(referenceOutputs),
301
+ aggregator: null, winnerIndex: single.index, winnerModel: single.label,
302
+ promotedFiles, totalTokens, totalCostUsd, durationMs,
303
303
  }, historyFilePath || undefined)
304
304
  } catch (histErr) {
305
305
  logger.warn('[dsh-moa] Failed to record fast synthesis run in history:', histErr?.message || histErr)
306
306
  }
307
307
 
308
308
  return {
309
- kind: 'synthesis',
310
- content: single.text,
311
- aggregator: 'Fast Mode (Direct)',
312
- references: referenceOutputs,
313
- presetName: preset?.name || 'default',
314
- isRefinement,
315
- isFastMode: true,
316
- winningIndex: single.index,
317
- winnerModel: single.label,
318
- promotedFiles,
309
+ kind: 'synthesis', content: single.text, aggregator: 'Fast Mode (Direct)',
310
+ references: referenceOutputs, presetName: preset?.name || 'default',
311
+ isRefinement, isFastMode: true, winningIndex: single.index,
312
+ winnerModel: single.label, promotedFiles, runId,
319
313
  ...(livePreview ? { liveCanvas: livePreview } : {}),
320
314
  usage: {
321
- totalTokens,
322
- totalCostUsd,
315
+ totalTokens, totalCostUsd,
323
316
  candidates: referenceOutputs.map((r) => ({ label: r.label, usage: r.usage, costUsd: r.costUsd })),
324
317
  aggregator: { inputTokens: 0, outputTokens: 0, totalTokens: 0, costUsd: 0 },
325
318
  },
326
- skippedFiles: collectedCtx?.skippedFiles || 0,
327
- skippedList: collectedCtx?.skippedList || [],
319
+ skippedFiles: collectedCtx?.skippedFiles || 0, skippedList: collectedCtx?.skippedList || [],
328
320
  durationMs,
329
321
  }
330
322
  }
@@ -500,19 +492,29 @@ export async function runMoAPipeline({
500
492
  let promotedFiles = []
501
493
 
502
494
  const keepWorkspaces = allowCandidateOverride
503
- if (synthesizedFiles.length > 0) {
504
- await writeCandidateWorkspace(cwd, 'curator-synthesis', synthesizedFiles, { runId })
505
- promotedFiles = await promoteCandidateWorkspace(cwd, 'curator-synthesis', { ...promoteOpts, runId, keepMoa: keepWorkspaces })
506
- } else if (referenceOutputs[winningIndex - 1]?.files?.length > 0) {
507
- promotedFiles = await promoteCandidateWorkspace(cwd, winningIndex, { ...promoteOpts, runId, keepMoa: keepWorkspaces })
508
- } else if (successfulRefs[0]?.files?.length > 0) {
509
- const fallbackIdx = successfulRefs[0].index
510
- promotedFiles = await promoteCandidateWorkspace(cwd, fallbackIdx, { ...promoteOpts, runId, keepMoa: keepWorkspaces })
511
- } else if (!keepWorkspaces) {
512
- await cleanMoaWorkspaces(cwd, { runId })
495
+ if (signal?.aborted) {
496
+ if (!keepWorkspaces) await cleanMoaWorkspaces(cwd, { runId })
497
+ return { error: new Error('Turn aborted') }
498
+ }
499
+ try {
500
+ if (synthesizedFiles.length > 0) {
501
+ await writeCandidateWorkspace(cwd, 'curator-synthesis', synthesizedFiles, { runId })
502
+ promotedFiles = await promoteCandidateWorkspace(cwd, 'curator-synthesis', { ...promoteOpts, runId, keepMoa: keepWorkspaces })
503
+ } else if (referenceOutputs[winningIndex - 1]?.files?.length > 0) {
504
+ promotedFiles = await promoteCandidateWorkspace(cwd, winningIndex, { ...promoteOpts, runId, keepMoa: keepWorkspaces })
505
+ } else if (successfulRefs[0]?.files?.length > 0) {
506
+ const fallbackIdx = successfulRefs[0].index
507
+ promotedFiles = await promoteCandidateWorkspace(cwd, fallbackIdx, { ...promoteOpts, runId, keepMoa: keepWorkspaces })
508
+ } else if (!keepWorkspaces) {
509
+ await cleanMoaWorkspaces(cwd, { runId })
510
+ }
511
+ } catch (promoteErr) {
512
+ if (!keepWorkspaces) await cleanMoaWorkspaces(cwd, { runId })
513
+ if (signal?.aborted || promoteErr?.message === 'Turn aborted') return { error: new Error('Turn aborted') }
514
+ throw promoteErr
513
515
  }
514
516
 
515
- const livePreview = await createPromotedPreview(liveCanvas, cwd, promotedFiles, referenceOutputs)
517
+ const livePreview = await createPromotedPreview(liveCanvas, cwd, promotedFiles, referenceOutputs, { winningIndex, synthesizedFiles })
516
518
  const durationMs = Date.now() - startTime
517
519
  const usageSummary = summarizeMoAUsage(referenceOutputs, aggUsage, multiJudgeResult?.totalUsage)
518
520
  const winningRef = referenceOutputs[winningIndex - 1]
@@ -542,6 +544,7 @@ export async function runMoAPipeline({
542
544
  try {
543
545
  await recordMoaRunAsync({
544
546
  id: runId,
547
+ cwd,
545
548
  prompt: userPrompt,
546
549
  preset: preset?.name || 'default',
547
550
  isRefinement,
@@ -579,8 +582,7 @@ export async function runMoAPipeline({
579
582
  consensus: multiJudgeResult,
580
583
  ...(livePreview ? { liveCanvas: livePreview } : {}),
581
584
  usage: usageSummary,
582
- skippedFiles: collectedCtx?.skippedFiles || 0,
583
- skippedList: collectedCtx?.skippedList || [],
585
+ skippedFiles: collectedCtx?.skippedFiles || 0, skippedList: collectedCtx?.skippedList || [],
584
586
  durationMs,
585
587
  }
586
588
  }
package/lib/moa-stream.js CHANGED
@@ -3,7 +3,8 @@ import crypto from 'node:crypto'
3
3
  * Zero-latency Live Streaming Generator for MoA Turns
4
4
  */
5
5
 
6
- import { cleanMoaWorkspaces } from './file-workspace.js'
6
+ import { cleanMoaWorkspaces, promoteCandidateWorkspace } from './file-workspace.js'
7
+ import { isTestEnvironment, getMoaHistory } from './history.js'
7
8
  import { formatMoAResponse } from './moa-parser.js'
8
9
  import { runMoAPipeline } from './moa-runner.js'
9
10
 
@@ -56,6 +57,7 @@ export async function* streamMoATurn({ targetPreset, userPrompt, messages, callL
56
57
  timeMachineEngine,
57
58
  force,
58
59
  signal: combinedSignal,
60
+ runId,
59
61
  })
60
62
  .catch((err) => ({ error: err }))
61
63
  .finally(() => {
@@ -132,3 +134,59 @@ export async function* streamMoATurn({ targetPreset, userPrompt, messages, callL
132
134
  }
133
135
  }
134
136
  }
137
+
138
+ /**
139
+ * Streams a /moa promote candidate turn directly to chat with zero LLM calls (#177).
140
+ */
141
+ export async function* streamPromoteTurn({ runId, candidateIndex, cwd, webServerPort, timeMachineEngine, force = isTestEnvironment() }, options = {}) {
142
+ const signal = options?.signal
143
+ if (signal?.aborted) return
144
+
145
+ yield { type: 'block-start', index: 0, blockType: 'text' }
146
+
147
+ if (typeof candidateIndex !== 'number' || isNaN(candidateIndex) || candidateIndex < 1) {
148
+ const errText = '⚠️ **MoA Promote Error**: Valid candidate index required (>= 1).'
149
+ yield { type: 'text-delta', index: 0, text: errText }
150
+ yield { type: 'block-end', index: 0, block: { type: 'text', text: errText } }
151
+ yield { type: 'finish', reason: { kind: 'stop' } }
152
+ return
153
+ }
154
+
155
+ try {
156
+ const effectiveRunId = runId || getMoaHistory(1, 0).runs[0]?.id || null
157
+ yield { type: 'text-delta', index: 0, text: `🔄 *Promoting candidate ${candidateIndex}${effectiveRunId ? ` for run \`${effectiveRunId}\`` : ''}...*\n\n` }
158
+
159
+ const promotedFiles = await promoteCandidateWorkspace(cwd, candidateIndex, {
160
+ runId: effectiveRunId,
161
+ keepMoa: true,
162
+ port: webServerPort,
163
+ timeMachineEngine,
164
+ force: Boolean(force || options?.force),
165
+ })
166
+
167
+ if (!promotedFiles || promotedFiles.length === 0) {
168
+ const noFilesText = `⚠️ **MoA Promote Notice**: No candidate files found for candidate ${candidateIndex}${effectiveRunId ? ` in run \`${effectiveRunId}\`` : ''}. Workspace may have expired or run does not exist.`
169
+ yield { type: 'text-delta', index: 0, text: noFilesText }
170
+ yield { type: 'block-end', index: 0, block: { type: 'text', text: noFilesText } }
171
+ yield { type: 'finish', reason: { kind: 'stop' } }
172
+ return
173
+ }
174
+
175
+ const fileList = promotedFiles.map((f) => `\`${f}\``).join(', ')
176
+ const checkpointId = promotedFiles.checkpoint || null
177
+ let successMsg = `✅ **MoA Candidate ${candidateIndex} promoted successfully!**\n\n`
178
+ successMsg += `> 📦 **Files applied to workspace**: ${fileList}\n`
179
+ if (checkpointId) {
180
+ successMsg += `> 🛡️ **Time Machine Checkpoint**: \`${checkpointId}\`\n`
181
+ }
182
+
183
+ yield { type: 'text-delta', index: 0, text: successMsg }
184
+ yield { type: 'block-end', index: 0, block: { type: 'text', text: successMsg } }
185
+ yield { type: 'finish', reason: { kind: 'stop' } }
186
+ } catch (err) {
187
+ const errText = `⚠️ **MoA Promote Failed**: ${err?.message || String(err)}`
188
+ yield { type: 'text-delta', index: 0, text: errText }
189
+ yield { type: 'block-end', index: 0, block: { type: 'text', text: errText } }
190
+ yield { type: 'finish', reason: { kind: 'stop' } }
191
+ }
192
+ }
@@ -115,12 +115,23 @@ export async function runCandidateTestGate({
115
115
  stderr = customRes?.stderr || ''
116
116
  } else {
117
117
  const nodePath = path.join(cwd, 'node_modules')
118
+ const allowedReads = [
119
+ executionDir,
120
+ cwd,
121
+ ...(candidateDir && candidateDir !== executionDir ? [candidateDir] : []),
122
+ ...(process.platform === 'win32'
123
+ ? [process.env.SYSTEMROOT || 'C:\\Windows', path.dirname(process.execPath)]
124
+ : ['/usr', '/lib', '/lib64', '/etc', path.resolve(path.dirname(process.execPath), '..'), process.env.HOME ? path.join(process.env.HOME, '.npmrc') : null]),
125
+ ].filter(Boolean)
126
+ const permOpts = `--permission ${allowedReads.map((p) => `--allow-fs-read=${p}`).join(' ')} --allow-fs-write=${executionDir} --allow-child-process`
127
+
118
128
  const env = getScrubbedEnv({
119
129
  MOA_CANDIDATE_INDEX: String(candidateIndex),
120
130
  MOA_CANDIDATE_DIR: candidateDir,
121
131
  MOA_BASE_DIR: cwd,
122
132
  ...(runId ? { MOA_RUN_ID: String(runId) } : {}),
123
133
  NODE_PATH: process.env.NODE_PATH ? `${nodePath}:${process.env.NODE_PATH}` : nodePath,
134
+ NODE_OPTIONS: process.env.NODE_OPTIONS ? `${process.env.NODE_OPTIONS} ${permOpts}` : permOpts,
124
135
  })
125
136
 
126
137
  const runPromise = new Promise((resolve) => {
package/lib/routes.js CHANGED
@@ -356,7 +356,11 @@ ctx.effect(() => {
356
356
  }
357
357
  try {
358
358
  const u = new URL(req.url, 'http://localhost')
359
- const cwd = u.searchParams.get('cwd') || process.cwd()
359
+ const rawRunId = u.searchParams.get('runId')
360
+ const latestRun = (!rawRunId || rawRunId === 'latest' || rawRunId === 'active') ? getMoaHistory(1, 0).runs[0] : null
361
+ const runId = (rawRunId && rawRunId !== 'latest' && rawRunId !== 'active') ? rawRunId : latestRun?.id
362
+ const run = runId ? getMoaRunById(runId) : null
363
+ const cwd = run?.cwd || latestRun?.cwd || u.searchParams.get('cwd') || process.cwd()
360
364
  const sanitizeTarget = (val, def) => {
361
365
  const clean = String(val || '').trim().replace(/[^a-zA-Z0-9_-]/g, '')
362
366
  return clean || def
@@ -366,7 +370,6 @@ ctx.effect(() => {
366
370
  const rawFile = u.searchParams.get('file') || ''
367
371
  const file = (rawFile && isSafeRelativePath(rawFile)) ? rawFile : ''
368
372
 
369
- const runId = u.searchParams.get('runId') || undefined
370
373
  const fromFiles = await readCandidateFiles(cwd, from, { runId })
371
374
  const toFiles = await readCandidateFiles(cwd, to, { runId })
372
375
 
@@ -510,11 +513,14 @@ ctx.effect(() => {
510
513
  const raw = await readBody(req)
511
514
  const body = JSON.parse(raw || '{}')
512
515
  const prompt = body?.prompt || ''
516
+ const activePreset = (cfg.presets || []).find((p) => p.name === (cfg.default_preset || 'default')) || cfg.presets?.[0]
517
+ const routingModel = (activePreset?.smart_routing_model?.model && activePreset.smart_routing_model)
518
+ || (cfg.smart_routing_model?.model && cfg.smart_routing_model) || activePreset?.smart_routing_model || cfg.smart_routing_model
513
519
  const result = await resolvePresetForPrompt({
514
520
  prompt,
515
521
  presets: cfg.presets || [],
516
522
  defaultPreset: cfg.default_preset || 'default',
517
- routingModel: cfg.smart_routing_model,
523
+ routingModel,
518
524
  callLlm,
519
525
  enabled: cfg.enabled !== false,
520
526
  })
@@ -574,6 +580,7 @@ ctx.effect(() => {
574
580
  timeMachineEngine: timeMachineEngine || ctx.reflect?.get?.('timeMachineEngine', false),
575
581
  timeMachineUrl: payload.timeMachineUrl, force: payload.force,
576
582
  checkpoint: payload.checkpoint, checkpointFn: payload.checkpointFn,
583
+ runId: payload.runId,
577
584
  })
578
585
 
579
586
  if (result?.skippedFiles > 0) {
package/lib/updater.js CHANGED
@@ -43,18 +43,39 @@ export function isTrustedUpdateRequest(request) {
43
43
 
44
44
  /** General safe write check for plugin write endpoints (CSRF / Cross-Site protection) */
45
45
  export function isSafeWriteRequest(request) {
46
+ const remote = request?.socket?.remoteAddress || request?.connection?.remoteAddress;
47
+ const isLoop = !remote || isLoopback(remote);
48
+
46
49
  const site = header(request, 'sec-fetch-site');
47
50
  if (site === 'cross-site') return false;
51
+
48
52
  const origin = header(request, 'origin');
49
53
  const host = header(request, 'host');
54
+
55
+ // If non-loopback caller, require valid same-origin browser proof
56
+ if (!isLoop) {
57
+ if (!origin || !host) return false;
58
+ if (site && site !== 'same-origin' && site !== 'none') return false;
59
+ }
60
+
61
+ // Same-site without matching origin is rejected regardless of remote
62
+ if (site === 'same-site') {
63
+ if (!origin || !host) return false;
64
+ }
65
+
50
66
  if (origin) {
67
+ if (origin === 'null') return false;
51
68
  try {
52
69
  const url = new URL(origin);
53
70
  if (host && url.host !== host) return false;
71
+ if (!isLoop) {
72
+ if (url.protocol !== 'http:' && url.protocol !== 'https:') return false;
73
+ }
54
74
  } catch {
55
75
  return false;
56
76
  }
57
77
  }
78
+
58
79
  return true;
59
80
  }
60
81
 
@@ -353,6 +374,8 @@ export function registerPluginUpdater(ctx, options) {
353
374
  }
354
375
  await installExact(target, `${options.packageName}@${before.latestVersion}`, options);
355
376
  json(response, 200, {
377
+ ok: true,
378
+ updated: true,
356
379
  ...before,
357
380
  updatedVersion: before.latestVersion,
358
381
  restartRequired: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-moa",
3
- "version": "0.2.41",
3
+ "version": "0.2.43",
4
4
  "description": "Mixture of Agents (MoA) plugin for DeepSeek Harness with /moa slash command",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",