@tangle-network/agent-bench 0.7.1 → 0.8.0
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/CHANGELOG.md +8 -0
- package/HARNESS.md +1 -1
- package/dist/adapters.js +2 -2
- package/dist/benchmarks/appworld.d.ts +61 -2
- package/dist/benchmarks/appworld.js +111 -40
- package/dist/benchmarks/appworld.js.map +1 -1
- package/dist/benchmarks/cadbench.js +1 -1
- package/dist/benchmarks/cadgenbench.js +1 -1
- package/dist/benchmarks/finresearchbench.js +16 -22
- package/dist/benchmarks/finresearchbench.js.map +1 -1
- package/dist/benchmarks/finsearchcomp.js +16 -22
- package/dist/benchmarks/finsearchcomp.js.map +1 -1
- package/dist/benchmarks/frames.js +18 -20
- package/dist/benchmarks/frames.js.map +1 -1
- package/dist/benchmarks/simpleqa.js +15 -19
- package/dist/benchmarks/simpleqa.js.map +1 -1
- package/dist/benchmarks/trata-hedge.js +20 -23
- package/dist/benchmarks/trata-hedge.js.map +1 -1
- package/dist/{cadbench-BrpwOU6A.js → cadbench-BLSyxR1N.js} +21 -20
- package/dist/cadbench-BLSyxR1N.js.map +1 -0
- package/dist/{cadgenbench-DF7hYHdl.js → cadgenbench-x2OFkf8y.js} +2 -2
- package/dist/cadgenbench-x2OFkf8y.js.map +1 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +10 -9
- package/dist/index.js.map +1 -1
- package/dist/router-turn-C2wMiDoo.js +31 -0
- package/dist/router-turn-C2wMiDoo.js.map +1 -0
- package/package.json +7 -7
- package/scripts/appworld_driver.py +1 -170
- package/scripts/run-package-tests.mjs +56 -36
- package/scripts/run-package-tests.test.mjs +33 -0
- package/scripts/trata-hedge/README.md +3 -3
- package/scripts/trata-hedge/run.sh +6 -2
- package/src/aec-gate.mts +30 -9
- package/src/agent-graphs-gen2.mts +523 -0
- package/src/agent-graphs-gen3.mts +660 -0
- package/src/agent-graphs-improve/offline-seams.mts +128 -0
- package/src/agent-graphs-improve.mts +747 -0
- package/src/atom-humaneval.mts +76 -55
- package/src/atom-mcp-e2e.mts +27 -13
- package/src/benchmarks/appworld.test.mts +119 -1
- package/src/benchmarks/appworld.ts +156 -33
- package/src/benchmarks/cadbench.ts +21 -8
- package/src/benchmarks/finresearchbench.ts +20 -16
- package/src/benchmarks/finsearchcomp.ts +20 -20
- package/src/benchmarks/frames.ts +19 -18
- package/src/benchmarks/simpleqa.ts +19 -17
- package/src/benchmarks/trata-hedge.ts +26 -18
- package/src/browser/ui-reviewer.ts +1 -1
- package/src/clbench-context-gate.mts +45 -9
- package/src/cloud-loop.mts +24 -6
- package/src/commit0-env-run.mts +6 -3
- package/src/commit0-env.ts +2 -3
- package/src/commit0-gate.mts +53 -24
- package/src/corpus.ts +3 -3
- package/src/david-attribution.mts +28 -9
- package/src/david-goliath.mts +31 -12
- package/src/egress-probe.mts +3 -3
- package/src/examples/lean-proof-gate.mts +10 -3
- package/src/examples/math-demo.mts +10 -3
- package/src/examples/strategy-demo.mts +10 -4
- package/src/fleet.mts +30 -8
- package/src/gate-cli.mts +9 -4
- package/src/gate.test.mts +7 -2
- package/src/gate.ts +23 -75
- package/src/generate-eval/certify.ts +13 -5
- package/src/hev-eval.mts +39 -10
- package/src/hev-improve.mts +28 -16
- package/src/hev-structural.mts +28 -20
- package/src/humaneval-gate.mts +24 -8
- package/src/humaneval-object-ablation.mts +52 -14
- package/src/humaneval-repair-gate.mts +53 -14
- package/src/mbpp-structural.mts +28 -20
- package/src/mcp-mount-probe.mts +23 -11
- package/src/official-optimizer-config.mts +61 -12
- package/src/official-optimizer-config.test.mts +72 -3
- package/src/quant-arena/profiles/quant-researcher.profile.json +6 -1
- package/src/quant-arena/quant-loop.mts +88 -61
- package/src/research-shot.ts +21 -11
- package/src/resolve-client.ts +6 -8
- package/src/rollout-ledger/backfill-swe-arena.test.mts +7 -2
- package/src/router-executor.ts +1 -1
- package/src/router-turn.ts +135 -0
- package/src/run-benchmarks.ts +7 -2
- package/src/sandbox-run.ts +23 -41
- package/src/search-bench/bridge.ts +66 -8
- package/src/search-bench/parametric-check.mts +19 -4
- package/src/search-bench/profiles.ts +24 -9
- package/src/search-bench/run.mts +3 -4
- package/src/smoke-structural-rollout.mts +21 -5
- package/src/supervisor-arena.mts +28 -20
- package/src/swe-arena/arms.ts +73 -15
- package/src/swe-arena/capacity.ts +44 -41
- package/src/swe-arena/diagnosis-ensemble.ts +51 -48
- package/src/swe-arena/execution.test.mts +12 -12
- package/src/swe-arena/gepa-seat.mts +12 -5
- package/src/swe-arena/gepa-seat.test.mts +45 -33
- package/src/swe-arena/outer-loop.mts +94 -70
- package/src/swe-arena/premeasured-from-cells.mts +16 -1
- package/src/swe-arena/premeasured-from-cells.test.mts +21 -1
- package/src/swe-arena/profiles/deepseek-author.profile.json +12 -0
- package/src/swe-arena/profiles/default-author.profile.json +9 -1
- package/src/swe-arena/proposer-fanout.mts +52 -99
- package/src/swe-arena/proposer-fanout.test.mts +92 -57
- package/src/swe-arena/proposer-provenance.mts +1 -1
- package/src/swe-code-improve.mts +30 -52
- package/src/swe-emit-patch.mts +26 -5
- package/src/swe-improve.mts +27 -8
- package/src/swe-jail.test.ts +128 -0
- package/src/swe-jail.ts +106 -22
- package/src/swe-local-proof.mts +27 -5
- package/src/swe-repro-calibrate.mts +5 -0
- package/src/swe-self-improve.mts +45 -10
- package/src/swe-stream.mts +68 -6
- package/src/swe-structural.mts +46 -7
- package/src/tb-container-executor.mts +38 -6
- package/src/tb-container-executor.test.mts +11 -3
- package/src/tb-supervisor-sidecar.mts +11 -27
- package/src/terminal-compare.ts +38 -18
- package/src/trata-gate.mts +26 -26
- package/src/trata-gepa.mts +25 -16
- package/src/trata-hedge-solve.mts +80 -0
- package/src/worker-blender.ts +21 -11
- package/src/worker-browser.ts +18 -5
- package/src/worker-build123d.ts +17 -8
- package/src/worker-cad.ts +31 -12
- package/src/worker.ts +1 -1
- package/tb_agents/opencode_router_agent.py +84 -42
- package/dist/cadbench-BrpwOU6A.js.map +0 -1
- package/dist/cadgenbench-DF7hYHdl.js.map +0 -1
- package/scripts/trata-hedge/solve.py +0 -83
package/src/swe-code-improve.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* META-HARNESS on the SWE scaffold — improve({ surface: 'code' }).
|
|
3
3
|
*
|
|
4
|
-
* A coding agent
|
|
4
|
+
* A Pi coding agent REWRITES the scaffold LOGIC under bench/src (the seed prompt/playbook,
|
|
5
5
|
* runAgentic strategy/params, context handling, retry/patch synthesis) with the MODEL (glm worker) +
|
|
6
6
|
* the TOOL surface (list/read/edit[/run]) + the JUDGE held FIXED, judged on the official swebench
|
|
7
7
|
* Docker verdict, gated on a held-out instance split. This is the DGM/meta-harness recipe: let the
|
|
@@ -10,13 +10,9 @@
|
|
|
10
10
|
* Wiring (all verified in this worktree):
|
|
11
11
|
* - improve()/codeProposerFor + rawTraceContext come from the LOCAL agent-runtime build, linked into
|
|
12
12
|
* this bench's node_modules (bench/node_modules/@tangle-network/agent-runtime -> /home/drew/code/agent-runtime).
|
|
13
|
-
* - The candidate proposer
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* `claude -p <prompt> --dangerously-skip-permissions` so the coding agent can actually edit the
|
|
17
|
-
* worktree. This is a harness-spawn fix, NOT a hand-authored scaffold edit — Claude still finds the
|
|
18
|
-
* lever itself from the traces.
|
|
19
|
-
* - Each candidate is a git worktree the driver forks off baseRef; Claude edits bench/src in place;
|
|
13
|
+
* - The candidate proposer supplies one exact Pi AgentProfile and runs it through Runtime's
|
|
14
|
+
* bridge executor. No local CLI shortcut or ambient model default exists.
|
|
15
|
+
* - Each candidate is a git worktree the driver forks off baseRef; Pi edits bench/src in place;
|
|
20
16
|
* `verify` (an import smoke of the edited scaffold) gates it before the expensive measurement.
|
|
21
17
|
* - MEASUREMENT: the code-aware agent fn shells into the candidate scaffold's OWN judge-free emit
|
|
22
18
|
* entrypoint (swe-emit-patch.mts) with cwd = the worktree, captures the unified diff, and returns
|
|
@@ -35,6 +31,7 @@ import { join } from 'node:path'
|
|
|
35
31
|
import { improve, agenticGenerator } from '@tangle-network/agent-runtime'
|
|
36
32
|
import type { ProposalFinding } from '@tangle-network/agent-eval'
|
|
37
33
|
import type { DispatchContext, JudgeConfig, Scenario } from '@tangle-network/agent-eval/contract'
|
|
34
|
+
import { agentProfileSchema } from '@tangle-network/agent-interface'
|
|
38
35
|
import { createSweBenchAdapter } from './benchmarks/swe-bench'
|
|
39
36
|
import type { BenchTask } from './benchmarks/types'
|
|
40
37
|
|
|
@@ -120,6 +117,22 @@ async function main(): Promise<void> {
|
|
|
120
117
|
const runDir = process.env.RUN_DIR ?? '/tmp/claude-1000/-home-drew-code-supervisor-lab/9ee6a456-a94f-474c-9888-b4afc9bc26bd/scratchpad/mh-run'
|
|
121
118
|
const emitTimeoutMs = Number(process.env.EMIT_TIMEOUT_MS ?? 600_000)
|
|
122
119
|
const harnessTimeoutMs = Number(process.env.HARNESS_TIMEOUT_MS ?? 900_000)
|
|
120
|
+
const bridgeUrl = process.env.CLI_BRIDGE_URL ?? process.env.BRIDGE_URL
|
|
121
|
+
const bridgeBearer = process.env.CLI_BRIDGE_BEARER ?? process.env.BRIDGE_BEARER
|
|
122
|
+
if (!bridgeUrl || !bridgeBearer) {
|
|
123
|
+
throw new Error('CLI_BRIDGE_URL/BRIDGE_URL and CLI_BRIDGE_BEARER/BRIDGE_BEARER are required')
|
|
124
|
+
}
|
|
125
|
+
const authorProfile = agentProfileSchema.parse({
|
|
126
|
+
name: 'swe-scaffold-author',
|
|
127
|
+
harness: 'pi',
|
|
128
|
+
model: {
|
|
129
|
+
provider: process.env.AUTHOR_PROVIDER ?? 'tangle-router',
|
|
130
|
+
default: process.env.AUTHOR_MODEL ?? 'glm-5.2',
|
|
131
|
+
},
|
|
132
|
+
prompt: {
|
|
133
|
+
systemPrompt: 'Improve the candidate worktree from measured failure evidence and verify it.',
|
|
134
|
+
},
|
|
135
|
+
})
|
|
123
136
|
mkdirSync(worktreeDir, { recursive: true })
|
|
124
137
|
mkdirSync(runDir, { recursive: true })
|
|
125
138
|
|
|
@@ -199,44 +212,6 @@ async function main(): Promise<void> {
|
|
|
199
212
|
},
|
|
200
213
|
}
|
|
201
214
|
|
|
202
|
-
// The corrected coding-harness spawn: `claude -p <prompt> --dangerously-skip-permissions`. The shipped
|
|
203
|
-
// runLocalHarness uses `claude --headless -p` (unknown option on this CLI). agenticGenerator ignores
|
|
204
|
-
// the return value (it reads worktree dirtiness), so a minimal result shape is enough.
|
|
205
|
-
const runHarness = (o: { cwd: string; taskPrompt: string; timeoutMs?: number; signal?: AbortSignal }): Promise<{ exitCode: number | null; stdout: string; stderr: string; killedBySignal: NodeJS.Signals | null; durationMs: number; timedOut: boolean }> => {
|
|
206
|
-
const started = Date.now()
|
|
207
|
-
return new Promise((resolve) => {
|
|
208
|
-
const child = spawn('claude', ['-p', o.taskPrompt, '--dangerously-skip-permissions'], {
|
|
209
|
-
cwd: o.cwd,
|
|
210
|
-
env: process.env,
|
|
211
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
212
|
-
})
|
|
213
|
-
let stdout = ''
|
|
214
|
-
let stderr = ''
|
|
215
|
-
let timedOut = false
|
|
216
|
-
child.stdout?.on('data', (d) => (stdout += String(d)))
|
|
217
|
-
child.stderr?.on('data', (d) => (stderr += String(d)))
|
|
218
|
-
const timer = setTimeout(() => {
|
|
219
|
-
timedOut = true
|
|
220
|
-
if (!child.killed) child.kill('SIGTERM')
|
|
221
|
-
}, o.timeoutMs ?? harnessTimeoutMs)
|
|
222
|
-
;(timer as { unref?: () => void }).unref?.()
|
|
223
|
-
const onAbort = () => {
|
|
224
|
-
if (!child.killed) child.kill('SIGTERM')
|
|
225
|
-
}
|
|
226
|
-
o.signal?.addEventListener('abort', onAbort, { once: true })
|
|
227
|
-
child.on('error', () => {
|
|
228
|
-
clearTimeout(timer)
|
|
229
|
-
resolve({ exitCode: 1, stdout, stderr: `${stderr}\n[spawn error]`, killedBySignal: null, durationMs: Date.now() - started, timedOut })
|
|
230
|
-
})
|
|
231
|
-
child.on('close', (code, signal) => {
|
|
232
|
-
clearTimeout(timer)
|
|
233
|
-
o.signal?.removeEventListener('abort', onAbort)
|
|
234
|
-
console.error(` [proposer:claude] exit=${code} wall=${Math.round((Date.now() - started) / 1000)}s out=${stdout.length}b`)
|
|
235
|
-
resolve({ exitCode: code, stdout, stderr, killedBySignal: signal, durationMs: Date.now() - started, timedOut })
|
|
236
|
-
})
|
|
237
|
-
})
|
|
238
|
-
}
|
|
239
|
-
|
|
240
215
|
// Domain prompt: name the EDIT BOUNDARY (scaffold logic only) + keep the raw-trace evidence discipline
|
|
241
216
|
// (agenticGenerator discards a raw-trace candidate that doesn't inspect a trace + write the diagnosis).
|
|
242
217
|
const buildPrompt = (args: { findings: ReadonlyArray<ProposalFinding> }): string => {
|
|
@@ -286,13 +261,16 @@ async function main(): Promise<void> {
|
|
|
286
261
|
}
|
|
287
262
|
|
|
288
263
|
const generator = agenticGenerator({
|
|
289
|
-
|
|
264
|
+
profile: authorProfile,
|
|
265
|
+
executorForWorktree: (cwd) => ({
|
|
266
|
+
backend: 'bridge',
|
|
267
|
+
bridgeUrl,
|
|
268
|
+
bridgeBearer,
|
|
269
|
+
cwd,
|
|
270
|
+
}),
|
|
290
271
|
verify,
|
|
291
272
|
timeoutMs: harnessTimeoutMs,
|
|
292
|
-
|
|
293
|
-
buildPrompt: buildPrompt as any,
|
|
294
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
295
|
-
runHarness: runHarness as any,
|
|
273
|
+
buildPrompt,
|
|
296
274
|
})
|
|
297
275
|
|
|
298
276
|
const scenarios: Scenario[] = allIds.map((id) => ({ id, kind: 'swe-bench-verified' }))
|
|
@@ -301,7 +279,7 @@ async function main(): Promise<void> {
|
|
|
301
279
|
const out = await improve({
|
|
302
280
|
surface: 'code',
|
|
303
281
|
gate: 'holdout',
|
|
304
|
-
code: { repoRoot: REPO_ROOT, baseRef, worktreeDir, generator },
|
|
282
|
+
code: { repoRoot: REPO_ROOT, baseRef, worktreeDir, profile: authorProfile, generator },
|
|
305
283
|
rawTraceContext: true,
|
|
306
284
|
runDir,
|
|
307
285
|
scenarios,
|
package/src/swe-emit-patch.mts
CHANGED
|
@@ -23,8 +23,9 @@
|
|
|
23
23
|
import { execFile } from 'node:child_process'
|
|
24
24
|
import { promisify } from 'node:util'
|
|
25
25
|
import type { AgenticSurface, ArtifactHandle, SurfaceScore } from '@tangle-network/agent-runtime/kernel'
|
|
26
|
-
import { refine, runAgentic } from '@tangle-network/agent-runtime/kernel'
|
|
26
|
+
import { defaultAnalystInstruction, refine, runAgentic } from '@tangle-network/agent-runtime/kernel'
|
|
27
27
|
import { createSweBenchEnvironment, SWE_SEED_PROMPT, SWE_SEED_PROMPT_WITH_RUN } from './swe-bench-env'
|
|
28
|
+
import { benchRouterProfile, withBenchProfile } from './router-turn'
|
|
28
29
|
|
|
29
30
|
const exec = promisify(execFile)
|
|
30
31
|
|
|
@@ -56,10 +57,31 @@ async function main(): Promise<void> {
|
|
|
56
57
|
|
|
57
58
|
const task = {
|
|
58
59
|
id: bt.id,
|
|
59
|
-
systemPrompt: enableRun ? SWE_SEED_PROMPT_WITH_RUN : SWE_SEED_PROMPT,
|
|
60
60
|
userPrompt: bt.prompt,
|
|
61
61
|
meta: { instanceId: bt.id },
|
|
62
62
|
}
|
|
63
|
+
const workerProfile = withBenchProfile(
|
|
64
|
+
{
|
|
65
|
+
name: 'swe-emit-patch-worker',
|
|
66
|
+
harness: 'cli-base',
|
|
67
|
+
model: { provider: 'tangle-router', default: model },
|
|
68
|
+
tools: {
|
|
69
|
+
list_files: true,
|
|
70
|
+
read_file: true,
|
|
71
|
+
edit_file: true,
|
|
72
|
+
...(enableRun ? { run: true } : {}),
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
systemPrompt: enableRun ? SWE_SEED_PROMPT_WITH_RUN : SWE_SEED_PROMPT,
|
|
77
|
+
maxTokens,
|
|
78
|
+
maxTurns: innerTurns,
|
|
79
|
+
},
|
|
80
|
+
)
|
|
81
|
+
const analystProfile = benchRouterProfile('swe-emit-patch-analyst', model, {
|
|
82
|
+
systemPrompt: defaultAnalystInstruction,
|
|
83
|
+
maxTokens,
|
|
84
|
+
})
|
|
63
85
|
|
|
64
86
|
// Capture the patch from inside score() (called during the refine loop, BEFORE the surface closes
|
|
65
87
|
// and rms the checkout). Keep the LATEST non-empty diff so accumulated refinements win and a later
|
|
@@ -85,9 +107,8 @@ async function main(): Promise<void> {
|
|
|
85
107
|
strategy: refine,
|
|
86
108
|
routerBaseUrl,
|
|
87
109
|
routerKey,
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
innerTurns,
|
|
110
|
+
workerProfile,
|
|
111
|
+
analystProfile,
|
|
91
112
|
budget,
|
|
92
113
|
})
|
|
93
114
|
const files = capturedPatch ? [...capturedPatch.matchAll(/^diff --git a\/(\S+)/gm)].map((m) => m[1]) : []
|
package/src/swe-improve.mts
CHANGED
|
@@ -30,10 +30,11 @@ import {
|
|
|
30
30
|
} from '@tangle-network/agent-runtime'
|
|
31
31
|
import {
|
|
32
32
|
canonicalCandidateDigest,
|
|
33
|
+
agentProfileSchema,
|
|
33
34
|
type AgentProfile,
|
|
34
35
|
} from '@tangle-network/agent-interface'
|
|
35
36
|
import type { AgenticSurface, ArtifactHandle, SurfaceScore } from '@tangle-network/agent-runtime/kernel'
|
|
36
|
-
import { refine, runAgentic } from '@tangle-network/agent-runtime/kernel'
|
|
37
|
+
import { defaultAnalystInstruction, refine, runAgentic } from '@tangle-network/agent-runtime/kernel'
|
|
37
38
|
import type { DispatchContext, JudgeConfig, Scenario } from '@tangle-network/agent-eval/contract'
|
|
38
39
|
import { createSweBenchAdapter } from './benchmarks/swe-bench'
|
|
39
40
|
import type { BenchTask } from './benchmarks/types'
|
|
@@ -43,6 +44,7 @@ import {
|
|
|
43
44
|
requiredTokenPricing,
|
|
44
45
|
} from './official-optimizer-config.mjs'
|
|
45
46
|
import { createSweBenchEnvironment, SWE_SEED_PROMPT, SWE_SEED_PROMPT_WITH_RUN } from './swe-bench-env'
|
|
47
|
+
import { benchRouterProfile, withBenchProfile } from './router-turn'
|
|
46
48
|
|
|
47
49
|
const exec = promisify(execFile)
|
|
48
50
|
|
|
@@ -70,6 +72,24 @@ async function main(): Promise<void> {
|
|
|
70
72
|
const enableRun = ['1', 'true', 'yes'].includes((process.env.RUN_TOOL ?? '').toLowerCase())
|
|
71
73
|
const SEED_PROMPT = enableRun ? SWE_SEED_PROMPT_WITH_RUN : SWE_SEED_PROMPT
|
|
72
74
|
const allIds = [...new Set([...trainIds, ...selectionIds, ...testIds])]
|
|
75
|
+
const workerProfile = withBenchProfile(
|
|
76
|
+
{
|
|
77
|
+
name: 'swe-agent-worker',
|
|
78
|
+
harness: 'cli-base',
|
|
79
|
+
model: { provider: 'tangle-router', default: workerModel },
|
|
80
|
+
tools: {
|
|
81
|
+
list_files: true,
|
|
82
|
+
read_file: true,
|
|
83
|
+
edit_file: true,
|
|
84
|
+
...(enableRun ? { run: true } : {}),
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
{ systemPrompt: SEED_PROMPT, maxTokens: workerMaxTokens, maxTurns: innerTurns },
|
|
88
|
+
)
|
|
89
|
+
const analystProfile = benchRouterProfile('swe-agent-analyst', workerModel, {
|
|
90
|
+
systemPrompt: defaultAnalystInstruction,
|
|
91
|
+
maxTokens: workerMaxTokens,
|
|
92
|
+
})
|
|
73
93
|
|
|
74
94
|
console.log('=== SWE-bench prompt optimization with official GEPA ===')
|
|
75
95
|
console.log(`worker=${workerModel} reflect=${reflectModel} router=${routerBaseUrl}`)
|
|
@@ -103,11 +123,12 @@ async function main(): Promise<void> {
|
|
|
103
123
|
// score() BEFORE runAgentic closes (rm) the workspace; its score is a cheap
|
|
104
124
|
// patch-exists proxy so the ONLY Docker run per cell is the improve judge.
|
|
105
125
|
const agent = async (candidate: ReadonlyAgentProfile, scenario: Scenario, ctx: DispatchContext): Promise<string | null> => {
|
|
106
|
-
const
|
|
126
|
+
const exactCandidate = agentProfileSchema.parse(candidate)
|
|
127
|
+
const promptText = exactCandidate.prompt?.systemPrompt
|
|
107
128
|
if (promptText === undefined) throw new Error('agent: candidate profile has no system prompt')
|
|
108
129
|
const bt = byId.get(scenario.id)
|
|
109
130
|
if (!bt) throw new Error(`agent: unknown scenario ${scenario.id}`)
|
|
110
|
-
const task = { id: bt.id,
|
|
131
|
+
const task = { id: bt.id, userPrompt: bt.prompt, meta: { instanceId: bt.id } }
|
|
111
132
|
let capturedPatch = ''
|
|
112
133
|
const stats = { list: 0, read: 0, edit_ok: 0, edit_fail: 0, run: 0, run_err: 0 }
|
|
113
134
|
const proxy: AgenticSurface = {
|
|
@@ -143,9 +164,8 @@ async function main(): Promise<void> {
|
|
|
143
164
|
strategy: refine,
|
|
144
165
|
routerBaseUrl,
|
|
145
166
|
routerKey,
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
innerTurns,
|
|
167
|
+
workerProfile: exactCandidate,
|
|
168
|
+
analystProfile,
|
|
149
169
|
budget: budgetShots,
|
|
150
170
|
}),
|
|
151
171
|
receipt: (result) => {
|
|
@@ -201,10 +221,9 @@ async function main(): Promise<void> {
|
|
|
201
221
|
},
|
|
202
222
|
}
|
|
203
223
|
|
|
204
|
-
const profile: AgentProfile = { name: 'swe-agent-glm46', prompt: { systemPrompt: SEED_PROMPT } }
|
|
205
224
|
const scenario = (id: string): Scenario => ({ id, kind: 'swe-bench-verified' })
|
|
206
225
|
|
|
207
|
-
const out = await improve(
|
|
226
|
+
const out = await improve(workerProfile, {
|
|
208
227
|
surface: 'prompt',
|
|
209
228
|
executionRef: canonicalCandidateDigest({
|
|
210
229
|
callback: 'bench/swe-improve',
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import test from 'node:test'
|
|
3
|
+
import { zaiChatRaw } from './swe-jail'
|
|
4
|
+
|
|
5
|
+
test('zaiChatRaw preserves the SWE transport shape through Runtime', async () => {
|
|
6
|
+
const originalFetch = globalThis.fetch
|
|
7
|
+
let requestBody: Record<string, unknown> | undefined
|
|
8
|
+
let requestUrl = ''
|
|
9
|
+
globalThis.fetch = async (input, init) => {
|
|
10
|
+
requestUrl = String(input)
|
|
11
|
+
requestBody = JSON.parse(String(init?.body)) as Record<string, unknown>
|
|
12
|
+
return new Response(
|
|
13
|
+
JSON.stringify({
|
|
14
|
+
choices: [
|
|
15
|
+
{
|
|
16
|
+
message: {
|
|
17
|
+
content: null,
|
|
18
|
+
tool_calls: [
|
|
19
|
+
{
|
|
20
|
+
id: 'call-1',
|
|
21
|
+
type: 'function',
|
|
22
|
+
function: { name: 'run', arguments: '{"command":"pwd"}' },
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
usage: { prompt_tokens: 11, completion_tokens: 3 },
|
|
29
|
+
}),
|
|
30
|
+
{ status: 200, headers: { 'content-type': 'application/json' } },
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const result = await zaiChatRaw(
|
|
36
|
+
{ base: 'http://router.test/v1', key: 'secret', timeoutMs: 1_000 },
|
|
37
|
+
{
|
|
38
|
+
model: 'deepseek-v4-flash',
|
|
39
|
+
messages: [{ role: 'user', content: 'inspect' }],
|
|
40
|
+
tools: [
|
|
41
|
+
{
|
|
42
|
+
type: 'function',
|
|
43
|
+
function: { name: 'run', parameters: { type: 'object' } },
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
tool_choice: 'required',
|
|
47
|
+
temperature: 0.1,
|
|
48
|
+
max_tokens: 32_768,
|
|
49
|
+
thinking: { type: 'enabled' },
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: 'swe-jail-test-worker',
|
|
53
|
+
harness: 'cli-base',
|
|
54
|
+
model: { provider: 'tangle-router', default: 'deepseek-v4-flash' },
|
|
55
|
+
tools: { run: true },
|
|
56
|
+
},
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
assert.equal(requestUrl, 'http://router.test/v1/chat/completions')
|
|
60
|
+
assert.deepEqual(requestBody, {
|
|
61
|
+
model: 'deepseek-v4-flash',
|
|
62
|
+
messages: [{ role: 'user', content: 'inspect' }],
|
|
63
|
+
tools: [
|
|
64
|
+
{
|
|
65
|
+
type: 'function',
|
|
66
|
+
function: { name: 'run', parameters: { type: 'object' } },
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
tool_choice: 'required',
|
|
70
|
+
temperature: 0.1,
|
|
71
|
+
max_tokens: 32_768,
|
|
72
|
+
thinking: { type: 'enabled' },
|
|
73
|
+
})
|
|
74
|
+
assert.equal(result.attempts, 1)
|
|
75
|
+
assert.deepEqual(result.json, {
|
|
76
|
+
choices: [
|
|
77
|
+
{
|
|
78
|
+
message: {
|
|
79
|
+
role: 'assistant',
|
|
80
|
+
content: null,
|
|
81
|
+
tool_calls: [
|
|
82
|
+
{
|
|
83
|
+
id: 'call-1',
|
|
84
|
+
type: 'function',
|
|
85
|
+
function: { name: 'run', arguments: '{"command":"pwd"}' },
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
finish_reason: 'tool_calls',
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
usage: { prompt_tokens: 11, completion_tokens: 3 },
|
|
93
|
+
})
|
|
94
|
+
} finally {
|
|
95
|
+
globalThis.fetch = originalFetch
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
test('zaiChatRaw refuses a local profile error without entering the retry ladder', async () => {
|
|
100
|
+
let fetchCalls = 0
|
|
101
|
+
const originalFetch = globalThis.fetch
|
|
102
|
+
globalThis.fetch = async () => {
|
|
103
|
+
fetchCalls += 1
|
|
104
|
+
throw new Error('fetch must not run')
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
await assert.rejects(
|
|
109
|
+
zaiChatRaw(
|
|
110
|
+
{
|
|
111
|
+
base: 'http://router.test/v1',
|
|
112
|
+
key: 'secret',
|
|
113
|
+
timeoutMs: 1_000,
|
|
114
|
+
maxAttempts: 7,
|
|
115
|
+
},
|
|
116
|
+
{ model: 'deepseek-v4-flash', messages: [{ role: 'user', content: 'inspect' }] },
|
|
117
|
+
{
|
|
118
|
+
name: 'incomplete-worker',
|
|
119
|
+
model: { provider: 'tangle-router', default: 'deepseek-v4-flash' },
|
|
120
|
+
},
|
|
121
|
+
),
|
|
122
|
+
/AgentProfile\.harness must be explicit/,
|
|
123
|
+
)
|
|
124
|
+
assert.equal(fetchCalls, 0)
|
|
125
|
+
} finally {
|
|
126
|
+
globalThis.fetch = originalFetch
|
|
127
|
+
}
|
|
128
|
+
})
|
package/src/swe-jail.ts
CHANGED
|
@@ -12,12 +12,15 @@ import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
|
|
12
12
|
import { tmpdir } from 'node:os'
|
|
13
13
|
import { join } from 'node:path'
|
|
14
14
|
import { promisify } from 'node:util'
|
|
15
|
+
import type { AgentProfile } from '@tangle-network/agent-interface'
|
|
16
|
+
import type { ToolSpec } from '@tangle-network/agent-runtime/kernel'
|
|
17
|
+
import { runBenchRouterTurn, withBenchProfile } from './router-turn'
|
|
15
18
|
|
|
16
19
|
const exec = promisify(execFile)
|
|
17
20
|
|
|
18
21
|
export const tail = (s: string, n: number): string => (s.length > n ? `…${s.slice(s.length - n)}` : s)
|
|
19
22
|
|
|
20
|
-
// ---------- zai chat client (
|
|
23
|
+
// ---------- zai chat client (Runtime transport; patient 429 ladder) ----------
|
|
21
24
|
|
|
22
25
|
export interface ZaiCfg {
|
|
23
26
|
base: string
|
|
@@ -27,12 +30,14 @@ export interface ZaiCfg {
|
|
|
27
30
|
* runs — past this instant, so a per-instance deadline reaches INTO the 429 ladder instead of
|
|
28
31
|
* letting a doomed retry sleep for another 240s after the instance was already written off. */
|
|
29
32
|
deadlineAt?: number
|
|
33
|
+
/** Caller-owned retry ladder length. Default 7. */
|
|
34
|
+
maxAttempts?: number
|
|
30
35
|
}
|
|
31
36
|
|
|
32
37
|
export interface ZaiRaw {
|
|
33
|
-
/** The
|
|
38
|
+
/** The OpenAI-compatible message and usage fields expected by existing SWE callers. */
|
|
34
39
|
json: Record<string, unknown>
|
|
35
|
-
/**
|
|
40
|
+
/** Runtime completion attempts spent (retries included). */
|
|
36
41
|
attempts: number
|
|
37
42
|
}
|
|
38
43
|
|
|
@@ -44,10 +49,39 @@ export interface ZaiRaw {
|
|
|
44
49
|
* tool_calls — is the glm reasoning path starving `content` when reasoning eats max_tokens, and is
|
|
45
50
|
* retried too (a tool_calls turn with empty content is a NORMAL tool-loop turn, not starvation).
|
|
46
51
|
*/
|
|
47
|
-
export async function zaiChatRaw(
|
|
52
|
+
export async function zaiChatRaw(
|
|
53
|
+
cfg: ZaiCfg,
|
|
54
|
+
body: Record<string, unknown>,
|
|
55
|
+
profile: AgentProfile,
|
|
56
|
+
): Promise<ZaiRaw> {
|
|
57
|
+
const {
|
|
58
|
+
model,
|
|
59
|
+
messages,
|
|
60
|
+
tools,
|
|
61
|
+
temperature,
|
|
62
|
+
max_tokens: maxTokens,
|
|
63
|
+
tool_choice: toolChoice,
|
|
64
|
+
...extraBody
|
|
65
|
+
} = body
|
|
66
|
+
if (typeof model !== 'string' || model.length === 0) {
|
|
67
|
+
throw new Error('completion body.model must be a non-empty string')
|
|
68
|
+
}
|
|
69
|
+
if (profile.model?.default !== model) {
|
|
70
|
+
throw new Error('completion body.model must equal AgentProfile.model.default')
|
|
71
|
+
}
|
|
72
|
+
if (!Array.isArray(messages)) throw new Error('completion body.messages must be an array')
|
|
73
|
+
const typedTools = Array.isArray(tools) ? (tools as ToolSpec[]) : []
|
|
74
|
+
const typedToolChoice =
|
|
75
|
+
toolChoice === 'auto' || toolChoice === 'required' || toolChoice === 'none'
|
|
76
|
+
? toolChoice
|
|
77
|
+
: undefined
|
|
48
78
|
let lastErr = ''
|
|
49
79
|
let delayBase = 2_000
|
|
50
|
-
|
|
80
|
+
const maxAttempts = cfg.maxAttempts ?? 7
|
|
81
|
+
if (!Number.isSafeInteger(maxAttempts) || maxAttempts < 1) {
|
|
82
|
+
throw new Error('ZaiCfg.maxAttempts must be a positive safe integer')
|
|
83
|
+
}
|
|
84
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
51
85
|
if (attempt > 1) {
|
|
52
86
|
const delay = Math.min(delayBase * 2 ** (attempt - 2), 240_000)
|
|
53
87
|
if (cfg.deadlineAt !== undefined && Date.now() + delay >= cfg.deadlineAt) {
|
|
@@ -63,30 +97,80 @@ export async function zaiChatRaw(cfg: ZaiCfg, body: Record<string, unknown>): Pr
|
|
|
63
97
|
const ctl = new AbortController()
|
|
64
98
|
const timer = setTimeout(() => ctl.abort(), perCallTimeout)
|
|
65
99
|
try {
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
100
|
+
const result = await runBenchRouterTurn(
|
|
101
|
+
{
|
|
102
|
+
routerBaseUrl: cfg.base,
|
|
103
|
+
routerKey: cfg.key,
|
|
104
|
+
profile: withBenchProfile(profile, {
|
|
105
|
+
...(typeof temperature === 'number' ? { temperature } : {}),
|
|
106
|
+
...(typeof maxTokens === 'number' ? { maxTokens } : {}),
|
|
107
|
+
...(typedToolChoice ? { toolChoice: typedToolChoice } : {}),
|
|
108
|
+
extraBody,
|
|
109
|
+
}),
|
|
110
|
+
tools: typedTools,
|
|
111
|
+
signal: ctl.signal,
|
|
112
|
+
},
|
|
113
|
+
{ messages: messages as Array<Record<string, unknown>> },
|
|
114
|
+
)
|
|
115
|
+
const toolCalls = result.toolCalls.map((call, index) => ({
|
|
116
|
+
id: call.id ?? `call_${index}`,
|
|
117
|
+
name: call.name,
|
|
118
|
+
arguments: call.arguments,
|
|
119
|
+
}))
|
|
120
|
+
const message = {
|
|
121
|
+
role: 'assistant',
|
|
122
|
+
content: toolCalls.length > 0 && result.finalText === '' ? null : result.finalText,
|
|
123
|
+
...(toolCalls.length > 0
|
|
124
|
+
? {
|
|
125
|
+
tool_calls: toolCalls.map((call) => ({
|
|
126
|
+
id: call.id,
|
|
127
|
+
type: 'function',
|
|
128
|
+
function: { name: call.name, arguments: call.arguments },
|
|
129
|
+
})),
|
|
130
|
+
}
|
|
131
|
+
: {}),
|
|
81
132
|
}
|
|
82
|
-
|
|
83
|
-
if (!hasToolCalls && String(msg.content ?? '').trim() === '') {
|
|
133
|
+
if (toolCalls.length === 0 && result.finalText.trim() === '') {
|
|
84
134
|
lastErr = 'empty content'
|
|
85
135
|
continue
|
|
86
136
|
}
|
|
137
|
+
const finalEvent = result.events.at(-1)
|
|
138
|
+
const finishReason =
|
|
139
|
+
finalEvent?.type === 'final'
|
|
140
|
+
? finalEvent.reason
|
|
141
|
+
: toolCalls.length > 0
|
|
142
|
+
? 'tool_calls'
|
|
143
|
+
: 'stop'
|
|
144
|
+
const json: Record<string, unknown> = {
|
|
145
|
+
choices: [
|
|
146
|
+
{
|
|
147
|
+
message,
|
|
148
|
+
finish_reason: finishReason,
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
...(result.usage.tokensKnown !== false
|
|
152
|
+
? {
|
|
153
|
+
usage: {
|
|
154
|
+
prompt_tokens: result.usage.input,
|
|
155
|
+
completion_tokens: result.usage.output,
|
|
156
|
+
},
|
|
157
|
+
}
|
|
158
|
+
: {}),
|
|
159
|
+
}
|
|
87
160
|
return { json, attempts: attempt }
|
|
88
161
|
} catch (e) {
|
|
89
162
|
lastErr = e instanceof Error ? e.message : String(e)
|
|
163
|
+
const status = Number(/router (\d+)/.exec(lastErr)?.[1])
|
|
164
|
+
const transientStatus =
|
|
165
|
+
Number.isFinite(status) &&
|
|
166
|
+
(status === 408 || status === 409 || status === 425 || status === 429 || status >= 500)
|
|
167
|
+
const transientTransport =
|
|
168
|
+
ctl.signal.aborted ||
|
|
169
|
+
/fetch failed|network|socket|ECONNRESET|ECONNREFUSED|ETIMEDOUT|EAI_AGAIN|operation was aborted/i.test(
|
|
170
|
+
lastErr,
|
|
171
|
+
)
|
|
172
|
+
if (!transientStatus && !transientTransport) throw e
|
|
173
|
+
delayBase = status === 429 ? 60_000 : 2_000
|
|
90
174
|
} finally {
|
|
91
175
|
clearTimeout(timer)
|
|
92
176
|
}
|
package/src/swe-local-proof.mts
CHANGED
|
@@ -21,9 +21,10 @@ import { tmpdir } from 'node:os'
|
|
|
21
21
|
import { join } from 'node:path'
|
|
22
22
|
import { promisify } from 'node:util'
|
|
23
23
|
import type { AgenticSurface, ArtifactHandle, SurfaceScore } from '@tangle-network/agent-runtime/kernel'
|
|
24
|
-
import { refine, runAgentic } from '@tangle-network/agent-runtime/kernel'
|
|
24
|
+
import { defaultAnalystInstruction, refine, runAgentic } from '@tangle-network/agent-runtime/kernel'
|
|
25
25
|
import type { BenchScore } from './benchmarks/types'
|
|
26
|
-
import { createSweBenchEnvironment } from './swe-bench-env'
|
|
26
|
+
import { createSweBenchEnvironment, SWE_SEED_PROMPT, SWE_SEED_PROMPT_WITH_RUN } from './swe-bench-env'
|
|
27
|
+
import { benchRouterProfile, withBenchProfile } from './router-turn'
|
|
27
28
|
|
|
28
29
|
const exec = promisify(execFile)
|
|
29
30
|
|
|
@@ -45,6 +46,28 @@ async function main(): Promise<void> {
|
|
|
45
46
|
console.log(`router=${routerBaseUrl}`)
|
|
46
47
|
|
|
47
48
|
const { environment, tasks, adapter } = await createSweBenchEnvironment(ids.length, { ids, enableRun })
|
|
49
|
+
const workerProfile = withBenchProfile(
|
|
50
|
+
{
|
|
51
|
+
name: 'swe-local-proof-worker',
|
|
52
|
+
harness: 'cli-base',
|
|
53
|
+
model: { provider: 'tangle-router', default: model },
|
|
54
|
+
tools: {
|
|
55
|
+
list_files: true,
|
|
56
|
+
read_file: true,
|
|
57
|
+
edit_file: true,
|
|
58
|
+
...(enableRun ? { run: true } : {}),
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
systemPrompt: enableRun ? SWE_SEED_PROMPT_WITH_RUN : SWE_SEED_PROMPT,
|
|
63
|
+
maxTokens,
|
|
64
|
+
maxTurns: innerTurns,
|
|
65
|
+
},
|
|
66
|
+
)
|
|
67
|
+
const analystProfile = benchRouterProfile('swe-local-proof-analyst', model, {
|
|
68
|
+
systemPrompt: defaultAnalystInstruction,
|
|
69
|
+
maxTokens,
|
|
70
|
+
})
|
|
48
71
|
const taskList = await tasks(0, ids.length)
|
|
49
72
|
const benchTaskById = new Map(
|
|
50
73
|
(await adapter.loadTasks({ ids, split: 'test' })).map((task) => [task.id, task]),
|
|
@@ -145,9 +168,8 @@ async function main(): Promise<void> {
|
|
|
145
168
|
strategy: refine,
|
|
146
169
|
routerBaseUrl,
|
|
147
170
|
routerKey,
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
innerTurns,
|
|
171
|
+
workerProfile,
|
|
172
|
+
analystProfile,
|
|
151
173
|
budget,
|
|
152
174
|
})
|
|
153
175
|
const rec = captured.get(task.id)
|
|
@@ -89,6 +89,11 @@ async function complete(messages: ChatMsg[]): Promise<Completion> {
|
|
|
89
89
|
const { json, attempts } = await zaiChatRaw(
|
|
90
90
|
{ base: ZAI_BASE, key: ZAI_KEY, timeoutMs: LLM_TIMEOUT_MS },
|
|
91
91
|
{ model: MODEL, max_tokens: MAX_TOKENS, temperature: TEMP, messages },
|
|
92
|
+
{
|
|
93
|
+
name: 'swe-reproduction-calibrator',
|
|
94
|
+
model: { provider: 'zai', default: MODEL, reasoningEffort: 'high' },
|
|
95
|
+
prompt: { systemPrompt: AUTHOR_SYSTEM },
|
|
96
|
+
},
|
|
92
97
|
)
|
|
93
98
|
const d = json as {
|
|
94
99
|
choices?: Array<{ message?: { content?: string } }>
|