@goodandready/dsh-moa 0.2.43 → 0.2.44
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/client.js +1 -1
- package/lib/file-workspace.js +0 -10
- package/lib/moa-budget.js +4 -0
- package/lib/moa-candidates.js +0 -1
- package/lib/moa-llm.js +11 -0
- package/lib/moa-runner.js +40 -35
- package/lib/moa-test-gate.js +27 -2
- package/lib/routes.js +8 -14
- package/package.json +1 -1
package/lib/client.js
CHANGED
package/lib/file-workspace.js
CHANGED
|
@@ -78,16 +78,6 @@ export function resolveCandidateFolder(baseDir, candidateTarget, options = {}) {
|
|
|
78
78
|
const sub = /^\d+$/.test(sanitizedTarget) ? `candidate-${sanitizedTarget}` : (sanitizedTarget || 'candidate-1')
|
|
79
79
|
const rawRunId = typeof options === 'string' ? options : options?.runId
|
|
80
80
|
const safeRunId = rawRunId ? String(rawRunId).trim().replace(/[^a-zA-Z0-9_-]/g, '') : null
|
|
81
|
-
const moaPath = path.resolve(baseDir, '.moa')
|
|
82
|
-
try {
|
|
83
|
-
const lst = fsSync.lstatSync(moaPath)
|
|
84
|
-
if (lst.isSymbolicLink()) {
|
|
85
|
-
const realMoa = fsSync.realpathSync(moaPath), realBase = fsSync.realpathSync(path.resolve(baseDir))
|
|
86
|
-
if (realMoa !== realBase && !realMoa.startsWith(realBase + path.sep)) fsSync.unlinkSync(moaPath)
|
|
87
|
-
}
|
|
88
|
-
} catch (err) {
|
|
89
|
-
if (err?.code !== 'ENOENT') throw err
|
|
90
|
-
}
|
|
91
81
|
const moaRoot = assertPathContained(baseDir, '.moa')
|
|
92
82
|
const runRoot = safeRunId ? assertPathContained(moaRoot, safeRunId) : moaRoot
|
|
93
83
|
return { moaRoot, runRoot, safeRunId, sub, candidateDir: assertPathContained(runRoot, sub) }
|
package/lib/moa-budget.js
CHANGED
|
@@ -40,6 +40,7 @@ export function applyBudgetGuardrails({
|
|
|
40
40
|
action = 'trim', // 'trim' | 'abort'
|
|
41
41
|
prices = {},
|
|
42
42
|
promptLength = 1000,
|
|
43
|
+
projectContext = '',
|
|
43
44
|
messages = [],
|
|
44
45
|
maxTokens = 2048,
|
|
45
46
|
peerCritiqueEnabled = false,
|
|
@@ -57,6 +58,9 @@ export function applyBudgetGuardrails({
|
|
|
57
58
|
|
|
58
59
|
// Calculate full input tokens from userPrompt length + messages context
|
|
59
60
|
let contextChars = Number(promptLength) || 1000
|
|
61
|
+
if (typeof projectContext === 'string' && projectContext.length > 0 && !String(promptLength).includes(String(projectContext.length))) {
|
|
62
|
+
contextChars += projectContext.length
|
|
63
|
+
}
|
|
60
64
|
if (Array.isArray(messages) && messages.length > 0) {
|
|
61
65
|
for (const m of messages) {
|
|
62
66
|
if (typeof m?.content === 'string') {
|
package/lib/moa-candidates.js
CHANGED
|
@@ -122,7 +122,6 @@ export async function runReferencesParallel(references, messages, options = {},
|
|
|
122
122
|
})
|
|
123
123
|
reject(new Error(`Timeout after ${Math.round(timeoutMs / 1000)}s`))
|
|
124
124
|
}, timeoutMs)
|
|
125
|
-
candidateTimer?.unref?.()
|
|
126
125
|
})
|
|
127
126
|
|
|
128
127
|
const res = await Promise.race([callPromise, timeoutPromise])
|
package/lib/moa-llm.js
CHANGED
|
@@ -139,3 +139,14 @@ export function createLlmCaller(ctx) {
|
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
export function callConfigEquals(a, b) {
|
|
145
|
+
if (!a || !b) return false
|
|
146
|
+
if (a.provider !== b.provider) return false
|
|
147
|
+
if (a.model !== b.model) return false
|
|
148
|
+
if (a.temperature !== b.temperature) return false
|
|
149
|
+
if (a.maxTokens !== b.maxTokens) return false
|
|
150
|
+
if (a.reasoningEffort !== b.reasoningEffort) return false
|
|
151
|
+
return true
|
|
152
|
+
}
|
package/lib/moa-runner.js
CHANGED
|
@@ -60,14 +60,33 @@ export async function runMoAPipeline({
|
|
|
60
60
|
const aggTimeoutSec = typeof preset?.aggregator_timeout_sec === 'number' ? preset.aggregator_timeout_sec : 180
|
|
61
61
|
const isBlindEvaluation = Boolean(preset?.blind_evaluation), isPeerCritiqueEnabled = Boolean(preset?.peer_critique_enabled)
|
|
62
62
|
const allowCandidateOverride = Boolean(preset?.allow_candidate_override)
|
|
63
|
-
//
|
|
63
|
+
// 1. Collect project context for refinement tasks
|
|
64
|
+
const collectedCtx = await collectProjectContext(cwd, 16000)
|
|
65
|
+
const isRefinement = Boolean(collectedCtx?.files?.length > 0 && isRefinementTask(userPrompt, collectedCtx.files))
|
|
66
|
+
let projectContext = ''
|
|
67
|
+
if (collectedCtx?.skippedFiles > 0 && typeof onProgress === 'function') {
|
|
68
|
+
const preview = collectedCtx.skippedList.slice(0, 3).join(', ')
|
|
69
|
+
const more = collectedCtx.skippedList.length > 3 ? ` (+${collectedCtx.skippedList.length - 3})` : ''
|
|
70
|
+
onProgress(`⚠️ *Workspace scan note: ${collectedCtx.skippedFiles} file(s) skipped (${preview}${more})*\n`)
|
|
71
|
+
}
|
|
72
|
+
if (isRefinement) {
|
|
73
|
+
if (typeof onProgress === 'function') {
|
|
74
|
+
onProgress('🔍 *Reading project files for refinement context...*\n')
|
|
75
|
+
}
|
|
76
|
+
projectContext = formatProjectContext(collectedCtx.files, {
|
|
77
|
+
skippedFiles: collectedCtx.skippedFiles,
|
|
78
|
+
skippedList: collectedCtx.skippedList,
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
// 2. Budget Guardrail check
|
|
64
82
|
const budgetCheck = applyBudgetGuardrails({
|
|
65
83
|
references: referenceModels,
|
|
66
84
|
enabled: preset?.budget_guard_enabled,
|
|
67
85
|
maxBudgetUsd: preset?.max_budget_usd,
|
|
68
86
|
action: preset?.budget_action || 'trim',
|
|
69
87
|
prices,
|
|
70
|
-
promptLength: userPrompt?.length || 1000,
|
|
88
|
+
promptLength: (userPrompt?.length || 1000) + (projectContext ? projectContext.length : 0),
|
|
89
|
+
projectContext,
|
|
71
90
|
messages,
|
|
72
91
|
maxTokens,
|
|
73
92
|
peerCritiqueEnabled: isPeerCritiqueEnabled,
|
|
@@ -75,20 +94,12 @@ export async function runMoAPipeline({
|
|
|
75
94
|
judgeModels: preset?.multi_judge_enabled ? preset?.judge_models : [],
|
|
76
95
|
})
|
|
77
96
|
if (!budgetCheck.allowed) {
|
|
78
|
-
return {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
aggregator: slotLabel(primaryJudge),
|
|
82
|
-
references: [],
|
|
83
|
-
presetName: preset?.name || 'default',
|
|
84
|
-
isRefinement: false,
|
|
85
|
-
isFastMode,
|
|
86
|
-
winningIndex: 0,
|
|
87
|
-
winnerModel: 'none',
|
|
88
|
-
promotedFiles: [],
|
|
97
|
+
return { kind: 'failure', content: `🛑 Budget Guardrail Abort: ${budgetCheck.reason}`,
|
|
98
|
+
aggregator: slotLabel(primaryJudge), references: [], presetName: preset?.name || 'default',
|
|
99
|
+
isRefinement, isFastMode, winningIndex: 0, winnerModel: 'none', promotedFiles: [],
|
|
89
100
|
usage: { totalTokens: 0, totalCostUsd: 0, candidates: [], aggregator: { totalTokens: 0, costUsd: 0 } },
|
|
90
|
-
skippedFiles: 0,
|
|
91
|
-
skippedList: [],
|
|
101
|
+
skippedFiles: collectedCtx?.skippedFiles || 0,
|
|
102
|
+
skippedList: collectedCtx?.skippedList || [],
|
|
92
103
|
durationMs: Date.now() - startTime,
|
|
93
104
|
}
|
|
94
105
|
}
|
|
@@ -96,24 +107,6 @@ export async function runMoAPipeline({
|
|
|
96
107
|
if (budgetCheck.action === 'trim' && typeof onProgress === 'function') {
|
|
97
108
|
onProgress(`✂️ *${budgetCheck.reason}*\n`)
|
|
98
109
|
}
|
|
99
|
-
// 2. Collect project context for refinement tasks
|
|
100
|
-
const collectedCtx = await collectProjectContext(cwd, 16000)
|
|
101
|
-
const isRefinement = Boolean(collectedCtx?.files?.length > 0 && isRefinementTask(userPrompt, collectedCtx.files))
|
|
102
|
-
let projectContext = ''
|
|
103
|
-
if (collectedCtx?.skippedFiles > 0 && typeof onProgress === 'function') {
|
|
104
|
-
const preview = collectedCtx.skippedList.slice(0, 3).join(', ')
|
|
105
|
-
const more = collectedCtx.skippedList.length > 3 ? ` (+${collectedCtx.skippedList.length - 3})` : ''
|
|
106
|
-
onProgress(`⚠️ *Workspace scan note: ${collectedCtx.skippedFiles} file(s) skipped (${preview}${more})*\n`)
|
|
107
|
-
}
|
|
108
|
-
if (isRefinement) {
|
|
109
|
-
if (typeof onProgress === 'function') {
|
|
110
|
-
onProgress('🔍 *Reading project files for refinement context...*\n')
|
|
111
|
-
}
|
|
112
|
-
projectContext = formatProjectContext(collectedCtx.files, {
|
|
113
|
-
skippedFiles: collectedCtx.skippedFiles,
|
|
114
|
-
skippedList: collectedCtx.skippedList,
|
|
115
|
-
})
|
|
116
|
-
}
|
|
117
110
|
// 3. Build prompts & Feature 8 multi-turn context
|
|
118
111
|
const priorTurnBaseline = preset?.multi_turn_enabled !== false ? extractPriorTurnBaseline(messages) : null
|
|
119
112
|
const prunedHistory = preset?.multi_turn_enabled !== false ? pruneMultiTurnMessages(messages) : messages
|
|
@@ -284,9 +277,21 @@ export async function runMoAPipeline({
|
|
|
284
277
|
} else if (!allowCandidateOverride) {
|
|
285
278
|
await cleanMoaWorkspaces(cwd, { runId })
|
|
286
279
|
}
|
|
287
|
-
if (
|
|
280
|
+
if (promoteErr) {
|
|
288
281
|
if (!allowCandidateOverride) await cleanMoaWorkspaces(cwd, { runId })
|
|
289
|
-
|
|
282
|
+
if (signal?.aborted || promoteErr?.message?.includes('aborted') || promoteErr?.name === 'AbortError' || promoteErr?.message === 'Turn aborted') {
|
|
283
|
+
return { error: new Error('Turn aborted') }
|
|
284
|
+
}
|
|
285
|
+
return { kind: 'failure', error: promoteErr,
|
|
286
|
+
content: '🛑 Checkpoint or Promotion Failure: ' + (promoteErr?.message || String(promoteErr)),
|
|
287
|
+
aggregator: 'Fast Mode (Direct)', references: referenceOutputs,
|
|
288
|
+
presetName: preset?.name || 'default', isRefinement, isFastMode: true,
|
|
289
|
+
winningIndex: single.index, winnerModel: single.label, promotedFiles: [], runId,
|
|
290
|
+
usage: { totalTokens: 0, totalCostUsd: 0, candidates: [], aggregator: { totalTokens: 0, costUsd: 0 } },
|
|
291
|
+
skippedFiles: collectedCtx?.skippedFiles || 0,
|
|
292
|
+
skippedList: collectedCtx?.skippedList || [],
|
|
293
|
+
durationMs: Date.now() - startTime,
|
|
294
|
+
}
|
|
290
295
|
}
|
|
291
296
|
|
|
292
297
|
const livePreview = await createPromotedPreview(liveCanvas, cwd, promotedFiles, referenceOutputs, { winningIndex: single.index })
|
package/lib/moa-test-gate.js
CHANGED
|
@@ -121,9 +121,9 @@ export async function runCandidateTestGate({
|
|
|
121
121
|
...(candidateDir && candidateDir !== executionDir ? [candidateDir] : []),
|
|
122
122
|
...(process.platform === 'win32'
|
|
123
123
|
? [process.env.SYSTEMROOT || 'C:\\Windows', path.dirname(process.execPath)]
|
|
124
|
-
: ['/usr', '/lib', '/lib64', '/etc', path.resolve(path.dirname(process.execPath), '..')
|
|
124
|
+
: ['/usr', '/lib', '/lib64', '/etc', path.resolve(path.dirname(process.execPath), '..')]),
|
|
125
125
|
].filter(Boolean)
|
|
126
|
-
const permOpts = `--permission ${allowedReads.map((p) => `--allow-fs-read=${p}`).join(' ')} --allow-fs-write=${executionDir}
|
|
126
|
+
const permOpts = `--permission ${allowedReads.map((p) => `--allow-fs-read=${p}`).join(' ')} --allow-fs-write=${executionDir}`
|
|
127
127
|
|
|
128
128
|
const env = getScrubbedEnv({
|
|
129
129
|
MOA_CANDIDATE_INDEX: String(candidateIndex),
|
|
@@ -134,6 +134,31 @@ export async function runCandidateTestGate({
|
|
|
134
134
|
NODE_OPTIONS: process.env.NODE_OPTIONS ? `${process.env.NODE_OPTIONS} ${permOpts}` : permOpts,
|
|
135
135
|
})
|
|
136
136
|
|
|
137
|
+
const allowedRoots = [executionDir, cwd, ...(candidateDir && candidateDir !== executionDir ? [candidateDir] : [])]
|
|
138
|
+
const cmdTokens = resolvedCmd.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) || []
|
|
139
|
+
for (const rawToken of cmdTokens) {
|
|
140
|
+
const token = rawToken.replace(/^["']|["']$/g, '')
|
|
141
|
+
if (path.isAbsolute(token)) {
|
|
142
|
+
if (/^\/(usr\/(bin|lib|include)|bin|lib|lib64|etc)/.test(token) && !/\.(txt|json|env|npmrc|key|ssh|md)/i.test(token)) {
|
|
143
|
+
continue
|
|
144
|
+
}
|
|
145
|
+
const real = path.resolve(token)
|
|
146
|
+
const allowed = allowedRoots.some((d) => d && (real === d || real.startsWith(d + path.sep)))
|
|
147
|
+
if (!allowed) {
|
|
148
|
+
const durationMs = Date.now() - startTime
|
|
149
|
+
return {
|
|
150
|
+
candidateIndex,
|
|
151
|
+
passed: false,
|
|
152
|
+
exitCode: 1,
|
|
153
|
+
durationMs,
|
|
154
|
+
command: resolvedCmd,
|
|
155
|
+
output: 'Access denied: test command specifies path outside workspace sandbox',
|
|
156
|
+
summary: `FAIL (code 1, ${durationMs}ms)`,
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
137
162
|
const runPromise = new Promise((resolve) => {
|
|
138
163
|
let child
|
|
139
164
|
let timeoutHandle = null
|
package/lib/routes.js
CHANGED
|
@@ -2,12 +2,7 @@
|
|
|
2
2
|
* DeepSeek Harness Mixture of Agents (MoA) — HTTP Route Handlers
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
getMoaHistory,
|
|
7
|
-
getMoaLeaderboard,
|
|
8
|
-
getMoaRunById,
|
|
9
|
-
exportMoaHistory,
|
|
10
|
-
} from './history.js'
|
|
5
|
+
import { getMoaHistory, getMoaLeaderboard, getMoaRunById, exportMoaHistory } from './history.js'
|
|
11
6
|
import { promoteCandidateWorkspace, readCandidateFiles, computeLineDiff, isSafeRelativePath } from './file-workspace.js'
|
|
12
7
|
import { isSafeWriteRequest } from './updater.js'
|
|
13
8
|
import { runMoAPipeline } from './moa-runner.js'
|
|
@@ -18,10 +13,7 @@ let _modelsCache = { timestamp: 0, models: [] }
|
|
|
18
13
|
const MODELS_CACHE_TTL = 60 * 1000
|
|
19
14
|
|
|
20
15
|
function writeJson(res, status, body) {
|
|
21
|
-
res.writeHead(status, {
|
|
22
|
-
'Content-Type': 'application/json; charset=utf-8',
|
|
23
|
-
'Cache-Control': 'no-store',
|
|
24
|
-
})
|
|
16
|
+
res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8', 'Cache-Control': 'no-store' })
|
|
25
17
|
res.end(JSON.stringify(body))
|
|
26
18
|
}
|
|
27
19
|
|
|
@@ -30,10 +22,7 @@ function readBody(req) {
|
|
|
30
22
|
let acc = ''
|
|
31
23
|
req.on('data', (chunk) => {
|
|
32
24
|
acc += chunk
|
|
33
|
-
if (acc.length > 5 * 1024 * 1024) {
|
|
34
|
-
req.destroy()
|
|
35
|
-
reject(new Error('Payload Too Large'))
|
|
36
|
-
}
|
|
25
|
+
if (acc.length > 5 * 1024 * 1024) { req.destroy(); reject(new Error('Payload Too Large')) }
|
|
37
26
|
})
|
|
38
27
|
req.on('end', () => resolve(acc))
|
|
39
28
|
req.on('error', reject)
|
|
@@ -587,6 +576,11 @@ ctx.effect(() => {
|
|
|
587
576
|
ctx.logger?.warn?.(`[dsh-moa] Workspace scan skipped ${result.skippedFiles} file(s): ${result.skippedList.join(', ')}`)
|
|
588
577
|
}
|
|
589
578
|
|
|
579
|
+
if (result?.error) {
|
|
580
|
+
writeJson(res, 500, { ok: false, error: result.error?.message || String(result.error) })
|
|
581
|
+
return
|
|
582
|
+
}
|
|
583
|
+
|
|
590
584
|
writeJson(res, 200, { ok: true, ...result })
|
|
591
585
|
} catch (err) {
|
|
592
586
|
writeJson(res, 500, { ok: false, error: err?.message || String(err) })
|