@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/sandbox-run.ts
CHANGED
|
@@ -13,11 +13,13 @@ import {
|
|
|
13
13
|
type AgentProfile,
|
|
14
14
|
type AgentRunSpec,
|
|
15
15
|
type OutputAdapter,
|
|
16
|
-
routerChatWithUsage,
|
|
17
16
|
} from '@tangle-network/agent-runtime/kernel'
|
|
18
|
-
|
|
19
|
-
//
|
|
17
|
+
import { parseExactAgentProfile } from '@tangle-network/agent-runtime'
|
|
18
|
+
// `BackendType` is the sandbox SDK's harness union and its canonical home. Runtime consumes it
|
|
19
|
+
// from there too; benchmark profiles use the same values as their exact harness identity.
|
|
20
20
|
import type { BackendType } from '@tangle-network/sandbox'
|
|
21
|
+
import { assertExecutableAgentProfile } from '../../src/runtime/supervise/model-policy'
|
|
22
|
+
import { benchRouterProfile, runBenchRouterTurn } from './router-turn'
|
|
21
23
|
|
|
22
24
|
/** Parse the agent's final answer from the event stream (harness-agnostic).
|
|
23
25
|
* The default deliverable; a benchmark whose artifact is a file overrides via
|
|
@@ -63,18 +65,17 @@ export const llmAnalyst = (cfg: { routerBaseUrl: string; routerKey: string; mode
|
|
|
63
65
|
.map((e) => (typeof e === 'string' ? e : JSON.stringify(e)))
|
|
64
66
|
.join('\n')
|
|
65
67
|
.slice(-2000)
|
|
66
|
-
const
|
|
68
|
+
const systemPrompt =
|
|
69
|
+
"You review an AI agent's previous attempt at a task. From the task, the attempt's output, and its execution trace ALONE, judge whether it correctly and completely solved the task. If you find a specific fault — a wrong value, a guessed API signature, a missing step, a misread requirement — name it and give the concrete correction in 1-3 sentences. Reply exactly 'no change needed' if the attempt looks correct and complete."
|
|
70
|
+
const turn = await runBenchRouterTurn(
|
|
67
71
|
{
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
72
|
+
routerBaseUrl: cfg.routerBaseUrl,
|
|
73
|
+
routerKey: cfg.routerKey,
|
|
74
|
+
profile: benchRouterProfile('sandbox-run-analyst', cfg.model, { systemPrompt }),
|
|
71
75
|
},
|
|
72
|
-
{
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
},
|
|
76
|
-
])
|
|
77
|
-
return content
|
|
76
|
+
`Task:\n${task ?? '(task unavailable)'}\n\nPrevious answer:\n${last?.output ?? '(none)'}\n\nTrace tail:\n${traceTail}`,
|
|
77
|
+
)
|
|
78
|
+
return turn.finalText
|
|
78
79
|
}
|
|
79
80
|
|
|
80
81
|
/** Cost-dial backend = the SDK's canonical `BackendType` (single source of truth; no local
|
|
@@ -83,43 +84,24 @@ export const llmAnalyst = (cfg: { routerBaseUrl: string; routerKey: string; mode
|
|
|
83
84
|
* agent runs — no per-backend worker. */
|
|
84
85
|
export type WorkerBackendType = BackendType
|
|
85
86
|
|
|
86
|
-
/** Build the standard sandbox `AgentRunSpec` for a benchmark
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
* the platform generates the in-box provider config keyed to
|
|
90
|
-
* `{env:OPENCODE_MODEL_API_KEY}`. Never pass an external router key into the
|
|
91
|
-
* box — the egress proxy rejects foreign credentials (403, empty output). */
|
|
87
|
+
/** Build the standard sandbox `AgentRunSpec` for a benchmark. The complete profile is the only
|
|
88
|
+
* behavioral input: its harness selects the box backend and its provider/model select inference.
|
|
89
|
+
* Extra sandbox env remains infrastructure, not a second model-selection path. */
|
|
92
90
|
export function sandboxAgentRun(opts: {
|
|
93
|
-
|
|
94
|
-
routerBaseUrl: string
|
|
95
|
-
backendType?: WorkerBackendType
|
|
96
|
-
/** In-box model provider. Default `openai` (registered models like gpt-4.1).
|
|
97
|
-
* Cheap router models (deepseek/kimi/glm) are not in opencode's `openai`
|
|
98
|
-
* registry and 404 in-box — pass `openai-compat` (generic passthrough). */
|
|
99
|
-
provider?: string
|
|
100
|
-
name?: string
|
|
91
|
+
profile: AgentProfile
|
|
101
92
|
taskToPrompt?: (task: string) => string
|
|
102
93
|
/** Extra box-level env (e.g. `TANGLE_SEARCH_DEFAULT_PROVIDER` to pin the in-box
|
|
103
94
|
* agent's web-search provider, provider keys like EXA_API_KEY). Allowlisted
|
|
104
95
|
* keys only reach the spawned CLI. Must NOT carry router/model credentials. */
|
|
105
96
|
env?: Record<string, string>
|
|
106
|
-
/** The developer's AgentProfile — the one knob for "which agent" (prompt / model /
|
|
107
|
-
* tools / mcp). Spread through verbatim; the backend cost-dial is tagged into
|
|
108
|
-
* metadata. Omitted ⇒ a minimal worker profile. */
|
|
109
|
-
profile?: AgentProfile
|
|
110
97
|
}): AgentRunSpec<string> {
|
|
111
|
-
const
|
|
112
|
-
|
|
98
|
+
const profile = parseExactAgentProfile(opts.profile, 'sandboxAgentRun profile')
|
|
99
|
+
assertExecutableAgentProfile(profile, 'sandboxAgentRun profile')
|
|
100
|
+
const name = profile.name ?? 'sandbox-worker'
|
|
113
101
|
return {
|
|
114
|
-
profile
|
|
102
|
+
profile,
|
|
115
103
|
name,
|
|
116
104
|
taskToPrompt: opts.taskToPrompt ?? ((t) => t),
|
|
117
|
-
sandboxOverrides: {
|
|
118
|
-
...(opts.env ? { env: opts.env } : {}),
|
|
119
|
-
backend: {
|
|
120
|
-
type: backendType,
|
|
121
|
-
model: { provider: opts.provider ?? 'openai', model: opts.model, baseUrl: opts.routerBaseUrl },
|
|
122
|
-
},
|
|
123
|
-
},
|
|
105
|
+
...(opts.env ? { sandboxOverrides: { env: opts.env } } : {}),
|
|
124
106
|
}
|
|
125
107
|
}
|
|
@@ -13,6 +13,12 @@
|
|
|
13
13
|
* `opencode/zai-coding-plan/glm-5.1`), so `harness` here is just the label.
|
|
14
14
|
*/
|
|
15
15
|
import { createExecutor } from '@tangle-network/agent-runtime/kernel'
|
|
16
|
+
import {
|
|
17
|
+
defineAgentProfileSecretRef,
|
|
18
|
+
harnessProviders,
|
|
19
|
+
type AgentProfile,
|
|
20
|
+
type HarnessType,
|
|
21
|
+
} from '@tangle-network/agent-interface'
|
|
16
22
|
import type { SearchArm } from './profiles'
|
|
17
23
|
import { armLabel } from './profiles'
|
|
18
24
|
import type { SearchCellResult } from './run.mts'
|
|
@@ -22,17 +28,63 @@ const nativeWebDisallowed = ['WebSearch', 'WebFetch', 'web_search', 'web_fetch',
|
|
|
22
28
|
|
|
23
29
|
/** Build the cli-bridge `agent_profile` for one arm (bridge dialect: disable via
|
|
24
30
|
* `metadata.disallowedTools`, search MCP via `mcp.<name>.transport:'http'`). */
|
|
25
|
-
function bridgeProfile(
|
|
26
|
-
|
|
27
|
-
|
|
31
|
+
function bridgeProfile(
|
|
32
|
+
arm: SearchArm,
|
|
33
|
+
routerSearchMcp: string,
|
|
34
|
+
tangleApiKey: string,
|
|
35
|
+
label: string,
|
|
36
|
+
harness: HarnessType,
|
|
37
|
+
wireModel: string,
|
|
38
|
+
): AgentProfile {
|
|
39
|
+
const prefix = `${harness}/`
|
|
40
|
+
if (!wireModel.startsWith(prefix)) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
`bridgeProfile: wire model '${wireModel}' must start with harness '${prefix}'`,
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
const modelPath = wireModel.slice(prefix.length)
|
|
46
|
+
const segments = modelPath.split('/').filter(Boolean)
|
|
47
|
+
if (segments.length === 0) {
|
|
48
|
+
throw new Error(`bridgeProfile: wire model '${wireModel}' has no model id`)
|
|
49
|
+
}
|
|
50
|
+
let provider: string
|
|
51
|
+
let model: string
|
|
52
|
+
if (segments.length === 1) {
|
|
53
|
+
const providers = harnessProviders(harness)
|
|
54
|
+
if (providers?.length !== 1) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
`bridgeProfile: wire model '${wireModel}' must include a provider for harness '${harness}'`,
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
provider = providers[0]!
|
|
60
|
+
model = segments[0]!
|
|
61
|
+
} else {
|
|
62
|
+
provider = segments[0]!
|
|
63
|
+
model = segments.slice(1).join('/')
|
|
64
|
+
}
|
|
65
|
+
const identity: AgentProfile = {
|
|
66
|
+
name: `search-bench-${label}`,
|
|
67
|
+
harness,
|
|
68
|
+
model: { provider, default: model },
|
|
69
|
+
}
|
|
70
|
+
if (arm === 'native') return identity
|
|
71
|
+
const base: AgentProfile = {
|
|
72
|
+
...identity,
|
|
73
|
+
metadata: { disallowedTools: nativeWebDisallowed },
|
|
74
|
+
}
|
|
28
75
|
if (arm === 'off') return base
|
|
76
|
+
if (!tangleApiKey) {
|
|
77
|
+
throw new Error(`bridgeProfile: provider arm requires TANGLE_API_KEY`)
|
|
78
|
+
}
|
|
29
79
|
return {
|
|
30
80
|
...base,
|
|
31
81
|
mcp: {
|
|
32
82
|
tangle_search: {
|
|
33
83
|
transport: 'http',
|
|
34
84
|
url: `${routerSearchMcp}?provider=${encodeURIComponent(arm.provider)}`,
|
|
35
|
-
headers: {
|
|
85
|
+
headers: {
|
|
86
|
+
Authorization: defineAgentProfileSecretRef('TANGLE_API_KEY', 'bearer'),
|
|
87
|
+
},
|
|
36
88
|
enabled: true,
|
|
37
89
|
},
|
|
38
90
|
},
|
|
@@ -58,7 +110,7 @@ export interface BridgeCfg {
|
|
|
58
110
|
export async function runBridgeCell(
|
|
59
111
|
cfg: BridgeCfg,
|
|
60
112
|
task: SearchTask,
|
|
61
|
-
harness:
|
|
113
|
+
harness: HarnessType,
|
|
62
114
|
arm: SearchArm,
|
|
63
115
|
): Promise<SearchCellResult> {
|
|
64
116
|
const startedAt = Date.now()
|
|
@@ -76,14 +128,20 @@ export async function runBridgeCell(
|
|
|
76
128
|
try {
|
|
77
129
|
// One harness turn through the unified bridge executor — same backend the
|
|
78
130
|
// loop path uses; this cell scorer just adds oracle scoring + citations.
|
|
131
|
+
const profile = bridgeProfile(
|
|
132
|
+
arm,
|
|
133
|
+
cfg.routerSearchMcp,
|
|
134
|
+
cfg.tangleApiKey,
|
|
135
|
+
`${harness}-${armId}`,
|
|
136
|
+
harness,
|
|
137
|
+
cfg.bridgeModels[harness] ?? harness,
|
|
138
|
+
)
|
|
79
139
|
const exec = createExecutor({
|
|
80
140
|
backend: 'bridge',
|
|
81
141
|
bridgeUrl: cfg.bridgeUrl,
|
|
82
142
|
bridgeBearer: cfg.bridgeBearer,
|
|
83
|
-
model: cfg.bridgeModels[harness] ?? harness,
|
|
84
|
-
agentProfile: bridgeProfile(arm, cfg.routerSearchMcp, cfg.tangleApiKey, `${harness}-${armId}`),
|
|
85
143
|
timeoutMs: cfg.timeoutMs ?? 300_000,
|
|
86
|
-
})({ profile
|
|
144
|
+
})({ profile, harness: null }, { signal: controller.signal, seams: {} })
|
|
87
145
|
// bridgeExecutor is one-shot (async execute resolves an ExecutorResult).
|
|
88
146
|
const artifact = (await exec.execute(taskToPrompt(task), controller.signal)) as {
|
|
89
147
|
out: unknown
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { writeFileSync } from 'node:fs'
|
|
13
13
|
import { runPool } from '../run-pool'
|
|
14
|
-
import {
|
|
14
|
+
import { runBenchRouterTurn } from '../router-turn'
|
|
15
15
|
import { freshTasks } from './tasks-fresh'
|
|
16
16
|
import { scoreTask, taskToPrompt } from './tasks'
|
|
17
17
|
|
|
@@ -28,9 +28,24 @@ async function main(): Promise<void> {
|
|
|
28
28
|
|
|
29
29
|
const outcomes = await runPool(freshTasks, conc, async (task) => {
|
|
30
30
|
try {
|
|
31
|
-
const res = await
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
const res = await runBenchRouterTurn(
|
|
32
|
+
{
|
|
33
|
+
routerBaseUrl: cfg.routerBaseUrl,
|
|
34
|
+
routerKey: cfg.routerKey,
|
|
35
|
+
profile: {
|
|
36
|
+
name: 'search-parametric-check',
|
|
37
|
+
model: { provider: 'tangle-router', default: model },
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
taskToPrompt(task),
|
|
41
|
+
)
|
|
42
|
+
const { score } = scoreTask(task, res.finalText)
|
|
43
|
+
return {
|
|
44
|
+
id: task.id,
|
|
45
|
+
score: score as 0 | 1 | null,
|
|
46
|
+
cost: res.usage.usdKnown === false ? undefined : res.usage.costUsd,
|
|
47
|
+
err: undefined as string | undefined,
|
|
48
|
+
}
|
|
34
49
|
} catch (err) {
|
|
35
50
|
return { id: task.id, score: null as 0 | 1 | null, cost: undefined, err: err instanceof Error ? err.message : String(err) }
|
|
36
51
|
}
|
|
@@ -17,7 +17,12 @@
|
|
|
17
17
|
* shape — a `transport:'http'` server at the router's `/v1/search/mcp` endpoint,
|
|
18
18
|
* provider pinned via the `?provider=` query param.
|
|
19
19
|
*/
|
|
20
|
-
import
|
|
20
|
+
import {
|
|
21
|
+
type AgentProfile,
|
|
22
|
+
type HarnessType,
|
|
23
|
+
agentProfileSchema,
|
|
24
|
+
defineAgentProfileSecretRef,
|
|
25
|
+
} from '@tangle-network/agent-interface'
|
|
21
26
|
|
|
22
27
|
export type SearchArm = 'native' | 'off' | { provider: string }
|
|
23
28
|
|
|
@@ -45,37 +50,45 @@ export interface BuildArmProfileArgs {
|
|
|
45
50
|
* foreign router credentials (403), so the provider arm needs the box-side
|
|
46
51
|
* credential flow before sandbox-backed provider runs are trustworthy. */
|
|
47
52
|
tangleApiKey: string
|
|
53
|
+
harness: HarnessType
|
|
54
|
+
model: string
|
|
55
|
+
provider: string
|
|
48
56
|
/** Optional name/metadata to merge. */
|
|
49
57
|
name?: string
|
|
50
58
|
metadata?: Record<string, unknown>
|
|
51
59
|
}
|
|
52
60
|
|
|
53
61
|
/**
|
|
54
|
-
* Build the
|
|
55
|
-
* Returned as a partial profile to be spread into `sandboxAgentRun({ profile })`.
|
|
62
|
+
* Build the complete executable AgentProfile for one search arm.
|
|
56
63
|
*/
|
|
57
64
|
export function buildArmProfile(args: BuildArmProfileArgs): AgentProfile {
|
|
58
65
|
const { arm, routerBaseUrl, tangleApiKey } = args
|
|
59
66
|
const base: AgentProfile = {
|
|
60
67
|
name: args.name ?? 'search-bench-worker',
|
|
68
|
+
harness: args.harness,
|
|
69
|
+
model: { provider: args.provider, default: args.model },
|
|
61
70
|
...(args.metadata ? { metadata: args.metadata } : {}),
|
|
62
|
-
}
|
|
71
|
+
}
|
|
63
72
|
|
|
64
73
|
if (arm === 'native') {
|
|
65
74
|
// Native web tools stay on (harness default). No search MCP. For codex,
|
|
66
75
|
// whose web_search ships off, explicitly enable it so the native arm is real.
|
|
67
|
-
return { ...base, tools: { web_search: true } }
|
|
76
|
+
return agentProfileSchema.parse({ ...base, tools: { web_search: true } })
|
|
68
77
|
}
|
|
69
78
|
|
|
70
79
|
if (arm === 'off') {
|
|
71
80
|
// No web access at all — the parametric floor (search contributes nothing).
|
|
72
|
-
return {
|
|
81
|
+
return agentProfileSchema.parse({
|
|
82
|
+
...base,
|
|
83
|
+
tools: { ...nativeWebToolsDisabled },
|
|
84
|
+
permission: { webfetch: 'deny' },
|
|
85
|
+
})
|
|
73
86
|
}
|
|
74
87
|
|
|
75
88
|
if (!tangleApiKey) {
|
|
76
89
|
throw new Error(`buildArmProfile: provider arm "${arm.provider}" requires a tangleApiKey for the search MCP`)
|
|
77
90
|
}
|
|
78
|
-
return {
|
|
91
|
+
return agentProfileSchema.parse({
|
|
79
92
|
...base,
|
|
80
93
|
tools: { ...nativeWebToolsDisabled },
|
|
81
94
|
permission: { webfetch: 'deny' },
|
|
@@ -83,11 +96,13 @@ export function buildArmProfile(args: BuildArmProfileArgs): AgentProfile {
|
|
|
83
96
|
tangle_search: {
|
|
84
97
|
transport: 'http',
|
|
85
98
|
url: routerSearchMcpUrl(arm.provider, routerBaseUrl),
|
|
86
|
-
headers: {
|
|
99
|
+
headers: {
|
|
100
|
+
Authorization: defineAgentProfileSecretRef('TANGLE_API_KEY', 'bearer'),
|
|
101
|
+
},
|
|
87
102
|
enabled: true,
|
|
88
103
|
},
|
|
89
104
|
},
|
|
90
|
-
}
|
|
105
|
+
})
|
|
91
106
|
}
|
|
92
107
|
|
|
93
108
|
/** Stable condition label for the corpus: `<harness>:<arm>`. */
|
package/src/search-bench/run.mts
CHANGED
|
@@ -135,14 +135,13 @@ async function runCell(
|
|
|
135
135
|
const timer = setTimeout(() => controller.abort(), cfg.timeoutMs ?? 300_000)
|
|
136
136
|
try {
|
|
137
137
|
const agentRun = sandboxAgentRun({
|
|
138
|
-
model: cfg.model,
|
|
139
|
-
routerBaseUrl: cfg.routerBaseUrl,
|
|
140
|
-
backendType: harness,
|
|
141
|
-
...(cfg.provider ? { provider: cfg.provider } : {}),
|
|
142
138
|
profile: buildArmProfile({
|
|
143
139
|
arm,
|
|
144
140
|
routerBaseUrl: cfg.routerBaseUrl,
|
|
145
141
|
tangleApiKey: cfg.tangleApiKey,
|
|
142
|
+
harness,
|
|
143
|
+
model: cfg.model,
|
|
144
|
+
provider: cfg.provider ?? 'openai',
|
|
146
145
|
name: `search-bench-${harness}-${armId}`,
|
|
147
146
|
metadata: { harness, arm: armId, taskId: task.id },
|
|
148
147
|
}),
|
|
@@ -37,12 +37,14 @@ import {
|
|
|
37
37
|
defaultStructuralRolloutPolicy,
|
|
38
38
|
runAgentic,
|
|
39
39
|
type StructuralRolloutResult,
|
|
40
|
+
defaultAnalystInstruction,
|
|
40
41
|
sandboxCheckRunner,
|
|
41
42
|
selectBestIndex,
|
|
42
43
|
structuralRollout,
|
|
43
44
|
visibleCheckScore,
|
|
44
45
|
} from '../../src/runtime/index'
|
|
45
46
|
import { basePrompt, type HumanEvalTask, loadHumanEval, runChecker } from './benchmarks/humaneval'
|
|
47
|
+
import { withBenchProfile } from './router-turn'
|
|
46
48
|
|
|
47
49
|
function must(name: string): string {
|
|
48
50
|
const v = process.env[name]
|
|
@@ -173,20 +175,34 @@ async function runTask(t: HumanEvalTask): Promise<TaskRow> {
|
|
|
173
175
|
name: 'humaneval-inert',
|
|
174
176
|
check: () => ({ passes: 0, total: 1, errored: 0 }),
|
|
175
177
|
})
|
|
178
|
+
const workerProfile = withBenchProfile(
|
|
179
|
+
{
|
|
180
|
+
name: 'humaneval-structural-worker',
|
|
181
|
+
harness: 'cli-base',
|
|
182
|
+
model: { provider: 'together', default: MODEL },
|
|
183
|
+
tools: { submit_answer: true },
|
|
184
|
+
},
|
|
185
|
+
{ systemPrompt, temperature: TEMP, maxTokens: MAX_TOKENS, maxTurns: 2 },
|
|
186
|
+
)
|
|
187
|
+
const analystProfile = withBenchProfile(
|
|
188
|
+
{
|
|
189
|
+
name: 'humaneval-structural-analyst',
|
|
190
|
+
harness: 'cli-base',
|
|
191
|
+
model: { provider: 'together', default: MODEL },
|
|
192
|
+
},
|
|
193
|
+
{ systemPrompt: defaultAnalystInstruction, temperature: TEMP, maxTokens: MAX_TOKENS },
|
|
194
|
+
)
|
|
176
195
|
const result = (await runAgentic({
|
|
177
196
|
surface,
|
|
178
197
|
task: {
|
|
179
198
|
id: t.taskId,
|
|
180
|
-
systemPrompt,
|
|
181
199
|
userPrompt: basePrompt(t),
|
|
182
200
|
meta: { entryPoint: t.entryPoint },
|
|
183
201
|
},
|
|
184
202
|
routerBaseUrl: BASE,
|
|
185
203
|
routerKey: must('TOGETHER_API_KEY'),
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
maxTokens: MAX_TOKENS,
|
|
189
|
-
innerTurns: 2,
|
|
204
|
+
workerProfile,
|
|
205
|
+
analystProfile,
|
|
190
206
|
strategy,
|
|
191
207
|
// The strategy's documented sizing: k samples + repair rounds + the check-author consult.
|
|
192
208
|
budget: policy.k + policy.repairRounds + 1,
|
package/src/supervisor-arena.mts
CHANGED
|
@@ -37,6 +37,7 @@ import { appendFileSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from
|
|
|
37
37
|
import { tmpdir } from 'node:os'
|
|
38
38
|
import { join } from 'node:path'
|
|
39
39
|
import { type HumanEvalTask, basePrompt, extractCode, loadHumanEval } from './benchmarks/humaneval'
|
|
40
|
+
import { runBenchRouterTurn } from './router-turn'
|
|
40
41
|
import { pool } from './stats.mts'
|
|
41
42
|
|
|
42
43
|
// ---------- pre-registered task sets (verbatim from the prereg; DO NOT EDIT) ----------
|
|
@@ -294,33 +295,40 @@ async function complete(cfg: ClientCfg, messages: Array<{ role: string; content:
|
|
|
294
295
|
let lastErr = ''
|
|
295
296
|
for (let attempt = 1; attempt <= 4; attempt += 1) {
|
|
296
297
|
if (attempt > 1) await new Promise((r) => setTimeout(r, 2000 * 2 ** attempt))
|
|
297
|
-
const ctl = new AbortController()
|
|
298
|
-
const timer = setTimeout(() => ctl.abort(), Number(process.env.LLM_TIMEOUT_MS ?? 240_000))
|
|
299
298
|
try {
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
299
|
+
const system = messages.find((message) => message.role === 'system')?.content
|
|
300
|
+
const result = await runBenchRouterTurn(
|
|
301
|
+
{
|
|
302
|
+
routerBaseUrl: cfg.base,
|
|
303
|
+
routerKey: cfg.key,
|
|
304
|
+
profile: {
|
|
305
|
+
name: 'supervisor-arena-agent',
|
|
306
|
+
harness: 'cli-base',
|
|
307
|
+
model: {
|
|
308
|
+
provider: 'tangle-router',
|
|
309
|
+
default: cfg.model,
|
|
310
|
+
metadata: { temperature: cfg.temperature, maxTokens: cfg.maxTokens },
|
|
311
|
+
},
|
|
312
|
+
...(system ? { prompt: { systemPrompt: system } } : {}),
|
|
313
|
+
},
|
|
314
|
+
timeoutMs: Number(process.env.LLM_TIMEOUT_MS ?? 240_000),
|
|
315
|
+
},
|
|
316
|
+
{ messages: messages.filter((message) => message.role !== 'system') },
|
|
317
|
+
)
|
|
318
|
+
if (result.usage.tokensKnown === false) throw new Error('provider omitted token usage')
|
|
319
|
+
const content = result.finalText
|
|
315
320
|
if (content.trim() === '') {
|
|
316
321
|
lastErr = 'empty content'
|
|
317
322
|
continue
|
|
318
323
|
}
|
|
319
|
-
return {
|
|
324
|
+
return {
|
|
325
|
+
content,
|
|
326
|
+
attempts: attempt,
|
|
327
|
+
tokensIn: result.usage.input,
|
|
328
|
+
tokensOut: result.usage.output,
|
|
329
|
+
}
|
|
320
330
|
} catch (e) {
|
|
321
331
|
lastErr = e instanceof Error ? e.message : String(e)
|
|
322
|
-
} finally {
|
|
323
|
-
clearTimeout(timer)
|
|
324
332
|
}
|
|
325
333
|
}
|
|
326
334
|
throw new Error(`completion failed after retries: ${lastErr}`)
|
package/src/swe-arena/arms.ts
CHANGED
|
@@ -17,6 +17,13 @@ import { chmod, mkdir, readdir, readFile, rm, writeFile } from 'node:fs/promises
|
|
|
17
17
|
import { join, resolve } from 'node:path'
|
|
18
18
|
import { homedir } from 'node:os'
|
|
19
19
|
import { fileURLToPath } from 'node:url'
|
|
20
|
+
import type { AgentProfile } from '@tangle-network/agent-interface'
|
|
21
|
+
import {
|
|
22
|
+
collectAgentTurn,
|
|
23
|
+
createExecutor,
|
|
24
|
+
streamAgentTurn,
|
|
25
|
+
type CollectedAgentTurn,
|
|
26
|
+
} from '@tangle-network/agent-runtime/kernel'
|
|
20
27
|
import { run, runOk, type RunResult, shq } from './proc'
|
|
21
28
|
import { materializeWorkspace } from './materialize'
|
|
22
29
|
import type { ArmSpec, SoloUsage } from './types'
|
|
@@ -192,12 +199,15 @@ export interface SoloArmSpec {
|
|
|
192
199
|
kind: 'solo'
|
|
193
200
|
/** Ledger/run-dir label, e.g. 'SOLO'. */
|
|
194
201
|
name: string
|
|
195
|
-
/**
|
|
196
|
-
|
|
202
|
+
/** Complete worker identity. Runtime reads harness/provider/model/prompt only from here. */
|
|
203
|
+
profile: AgentProfile
|
|
197
204
|
/** Appended to the problem statement. Default: the shared worker suffix. */
|
|
198
205
|
promptSuffix?: string
|
|
199
206
|
/** Whole-run ceiling. solo.sh used `timeout 1000` (s). */
|
|
200
207
|
timeoutMs?: number
|
|
208
|
+
/** Runtime bridge transport. Defaults to the CLI_BRIDGE environment variables. */
|
|
209
|
+
bridgeUrl?: string
|
|
210
|
+
bridgeBearer?: string
|
|
201
211
|
}
|
|
202
212
|
|
|
203
213
|
export interface SupervisorArmSpec {
|
|
@@ -271,8 +281,8 @@ export async function toArmIdentity(spec: ExecutableArmSpec): Promise<ArmSpec> {
|
|
|
271
281
|
return {
|
|
272
282
|
name: spec.name,
|
|
273
283
|
kind: 'solo',
|
|
274
|
-
env: { model: spec.model },
|
|
275
|
-
provenance: { repo: '
|
|
284
|
+
env: { model: spec.profile.model?.default ?? 'missing' },
|
|
285
|
+
provenance: { repo: spec.profile.harness ?? 'missing', commit: 'runtime-bridge' },
|
|
276
286
|
}
|
|
277
287
|
}
|
|
278
288
|
const loopsRepo = spec.loopsRepo ?? DEFAULT_LOOPS_REPO
|
|
@@ -450,7 +460,29 @@ async function verifyWorkspace(
|
|
|
450
460
|
return res.code
|
|
451
461
|
}
|
|
452
462
|
|
|
453
|
-
|
|
463
|
+
function runtimeSoloUsage(turn: CollectedAgentTurn): SoloUsage {
|
|
464
|
+
const calls = turn.events.filter((event) => event.type === 'llm_call')
|
|
465
|
+
const maxCtx = calls.reduce(
|
|
466
|
+
(max, event) => Math.max(max, (event.tokensIn ?? 0) + (event.tokensOut ?? 0)),
|
|
467
|
+
0,
|
|
468
|
+
)
|
|
469
|
+
const cacheRead = Number(turn.usage.promptCache?.readTokens ?? 0)
|
|
470
|
+
const cacheWrite = Number(turn.usage.promptCache?.writeTokens ?? 0)
|
|
471
|
+
const reasoning = turn.usage.reasoningTokens ?? 0
|
|
472
|
+
return {
|
|
473
|
+
steps: calls.length,
|
|
474
|
+
in: turn.usage.input,
|
|
475
|
+
out: turn.usage.output,
|
|
476
|
+
reasoning,
|
|
477
|
+
cache_w: Number.isFinite(cacheWrite) ? cacheWrite : 0,
|
|
478
|
+
cache_r: Number.isFinite(cacheRead) ? cacheRead : 0,
|
|
479
|
+
max_ctx: maxCtx,
|
|
480
|
+
oc_cost: turn.usage.costUsd ?? turn.usage.estimatedCostUsd ?? 0,
|
|
481
|
+
total_io: turn.usage.input + turn.usage.output + reasoning,
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/** Solo arm: materialize → exact Runtime profile turn → patch extract → verify. */
|
|
454
486
|
export async function runSoloArm(spec: SoloArmSpec, ctx: ArmRunContext): Promise<SoloArmResult> {
|
|
455
487
|
const runDir = join(ctx.outDir, 'runs', ctx.instanceId, spec.name)
|
|
456
488
|
const ws = join(runDir, 'ws')
|
|
@@ -469,18 +501,44 @@ export async function runSoloArm(spec: SoloArmSpec, ctx: ArmRunContext): Promise
|
|
|
469
501
|
})
|
|
470
502
|
|
|
471
503
|
const promptFile = join(runDir, 'prompt.txt')
|
|
472
|
-
|
|
504
|
+
const prompt = ctx.problemStatement + (spec.promptSuffix ?? WORKER_PROMPT_SUFFIX)
|
|
505
|
+
await writeFile(promptFile, prompt)
|
|
506
|
+
|
|
507
|
+
const bridgeUrl = spec.bridgeUrl ?? process.env.CLI_BRIDGE_URL ?? process.env.BRIDGE_URL
|
|
508
|
+
const bridgeBearer =
|
|
509
|
+
spec.bridgeBearer ?? process.env.CLI_BRIDGE_BEARER ?? process.env.BRIDGE_BEARER
|
|
510
|
+
if (!bridgeUrl || !bridgeBearer) {
|
|
511
|
+
throw new Error(
|
|
512
|
+
'runSoloArm requires CLI_BRIDGE_URL/BRIDGE_URL and CLI_BRIDGE_BEARER/BRIDGE_BEARER',
|
|
513
|
+
)
|
|
514
|
+
}
|
|
473
515
|
|
|
474
516
|
const t0 = Date.now()
|
|
475
|
-
const
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
517
|
+
const timeoutMs = spec.timeoutMs ?? 1_000_000
|
|
518
|
+
const factory = createExecutor({
|
|
519
|
+
backend: 'bridge',
|
|
520
|
+
bridgeUrl,
|
|
521
|
+
bridgeBearer,
|
|
522
|
+
cwd: ws,
|
|
523
|
+
timeoutMs,
|
|
524
|
+
})
|
|
525
|
+
const turn = await collectAgentTurn(
|
|
526
|
+
streamAgentTurn(
|
|
527
|
+
{ kind: 'executor', factory, profile: spec.profile },
|
|
528
|
+
prompt,
|
|
529
|
+
{ timeoutMs, ...(ctx.signal ? { signal: ctx.signal } : {}) },
|
|
530
|
+
),
|
|
479
531
|
)
|
|
480
532
|
const wall_s = Math.round((Date.now() - t0) / 1000)
|
|
481
|
-
await writeFile(
|
|
482
|
-
|
|
483
|
-
|
|
533
|
+
await writeFile(
|
|
534
|
+
join(runDir, 'runtime-events.jsonl'),
|
|
535
|
+
turn.events.map((event) => JSON.stringify(event)).join('\n') + '\n',
|
|
536
|
+
)
|
|
537
|
+
if (turn.status !== 'completed') {
|
|
538
|
+
throw new Error(
|
|
539
|
+
`runSoloArm ${ctx.instanceId}/${spec.name}: ${turn.error?.message ?? turn.status}`,
|
|
540
|
+
)
|
|
541
|
+
}
|
|
484
542
|
if (ctx.signal?.aborted) throw ctx.signal.reason
|
|
485
543
|
|
|
486
544
|
const patch = await extractPatch(ws, ctx.baseCommit, ctx.excludes, ctx.signal)
|
|
@@ -492,12 +550,12 @@ export async function runSoloArm(spec: SoloArmSpec, ctx: ArmRunContext): Promise
|
|
|
492
550
|
return {
|
|
493
551
|
arm: spec.name,
|
|
494
552
|
iid: ctx.instanceId,
|
|
495
|
-
oc_rc:
|
|
553
|
+
oc_rc: 0,
|
|
496
554
|
wall_s,
|
|
497
555
|
patch_lines: patchLineCount(patch),
|
|
498
556
|
verify_rc,
|
|
499
557
|
verify_pass: verify_rc === 0,
|
|
500
|
-
usage:
|
|
558
|
+
usage: runtimeSoloUsage(turn),
|
|
501
559
|
patchPath,
|
|
502
560
|
ws,
|
|
503
561
|
}
|