maka-agent 0.2.0-dev.3.20260830 → 0.2.0-dev.5.20260830

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.
Files changed (66) hide show
  1. package/dist/cli-core.js +1 -0
  2. package/dist/eval-bundle-path.js +1 -1
  3. package/dist/pi-tui-runner.js +3 -1
  4. package/dist/runtime-host-cli.js +37 -3
  5. package/dist/runtime-host-onboarding.js +10 -1
  6. package/dist/runtime-host-peer-mesh-management-command.js +17 -0
  7. package/dist/runtime-host-setup-command.js +33 -15
  8. package/dist/runtime-host-systemd-service.js +9 -3
  9. package/dist/runtime-host-tui-context.js +2 -6
  10. package/native/runtime-host-peer/prebuilds/darwin-arm64/maka_runtime_host_peer.node +0 -0
  11. package/native/runtime-host-peer/prebuilds/linux-arm64/maka_runtime_host_peer.node +0 -0
  12. package/native/runtime-host-peer/prebuilds/linux-x64/maka_runtime_host_peer.node +0 -0
  13. package/native/runtime-host-peer/prebuilds/win32-x64/maka_runtime_host_peer.node +0 -0
  14. package/node_modules/@maka/eval/dist/external-subject.js +1 -1
  15. package/node_modules/@maka/eval/dist/harbor-external-subject.js +2 -2
  16. package/node_modules/@maka/runtime/dist/sandbox/macos-seatbelt.js +68 -9
  17. package/node_modules/@maka/runtime-host/dist/client/peer-client.js +22 -3
  18. package/node_modules/@maka/runtime-host/dist/operator/peer-mesh-management-frame.js +14 -0
  19. package/{packages/eval/dist/index.js → node_modules/@maka/runtime-host/dist/peer-mesh/display-name.js} +12 -10
  20. package/node_modules/@maka/runtime-host/dist/peer-mesh/model.js +32 -21
  21. package/node_modules/@maka/runtime-host/dist/peer-mesh/node.js +207 -47
  22. package/node_modules/@maka/runtime-host/dist/peer-mesh/owner.js +2 -0
  23. package/node_modules/@maka/runtime-host/dist/peer-mesh/store.js +18 -5
  24. package/node_modules/@maka/runtime-host/dist/protocol/index.js +5 -1
  25. package/node_modules/@maka/runtime-host/dist/protocol/peer-mesh.js +59 -2
  26. package/node_modules/@maka/runtime-host/dist/server/access-authority.js +11 -7
  27. package/node_modules/@maka/runtime-host/dist/server/connection-session.js +3 -0
  28. package/node_modules/@maka/runtime-host/dist/server/execution-service.js +1 -0
  29. package/node_modules/@maka/runtime-host/dist/server/host-kernel.js +1 -1
  30. package/node_modules/@maka/runtime-host/dist/server/peer-mesh-authority.js +19 -0
  31. package/package.json +1 -2
  32. package/packages/eval/dist/attempt-store.js +0 -138
  33. package/packages/eval/dist/cli.js +0 -119
  34. package/packages/eval/dist/experiment-directory.js +0 -59
  35. package/packages/eval/dist/experiment.js +0 -49
  36. package/packages/eval/dist/external-subject.js +0 -488
  37. package/packages/eval/dist/harbor-external-subject.js +0 -929
  38. package/packages/eval/dist/harbor-maka-subject.js +0 -83
  39. package/packages/eval/dist/harness-environment.js +0 -80
  40. package/packages/eval/dist/harness-executor.js +0 -1013
  41. package/packages/eval/dist/install-preflight.js +0 -201
  42. package/packages/eval/dist/maka-artifacts.js +0 -126
  43. package/packages/eval/dist/maka-runtime-policy.js +0 -42
  44. package/packages/eval/dist/maka-subject.js +0 -268
  45. package/packages/eval/dist/metering-checkpoint.js +0 -80
  46. package/packages/eval/dist/provider-admission.js +0 -39
  47. package/packages/eval/dist/provider-metering.js +0 -67
  48. package/packages/eval/dist/provider-web-tool-surface.js +0 -74
  49. package/packages/eval/dist/relay-result-frame.js +0 -38
  50. package/packages/eval/dist/result.js +0 -94
  51. package/packages/eval/dist/runner.js +0 -290
  52. package/packages/eval/dist/spec.js +0 -135
  53. package/packages/eval/dist/toolchain-verification.js +0 -130
  54. package/packages/eval/harbor/deepseek-codex-models.json +0 -69
  55. package/packages/eval/harbor/deepseek-harness-profile/cordis.patch.yml +0 -140
  56. package/packages/eval/harbor/deepseek-harness-profile/cordis.yml +0 -18
  57. package/packages/eval/harbor/deepseek-harness-profile/package.json +0 -10
  58. package/packages/eval/harbor/docker-compose-egress-proxy.yaml +0 -57
  59. package/packages/eval/harbor/egress-proxy/Dockerfile +0 -25
  60. package/packages/eval/harbor/egress-proxy/entrypoint.sh +0 -62
  61. package/packages/eval/harbor/egress-proxy/network-policy +0 -148
  62. package/packages/eval/harbor/egress_filter.py +0 -435
  63. package/packages/eval/harbor/eval_framework.py +0 -40
  64. package/packages/eval/harbor/relay_agent.py +0 -686
  65. package/packages/eval/harbor/run_trial.py +0 -223
  66. package/packages/eval/package.json +0 -20
@@ -1,929 +0,0 @@
1
- /*
2
- * Licensed to the Apache Software Foundation (ASF) under one
3
- * or more contributor license agreements. See the NOTICE file
4
- * distributed with this work for additional information
5
- * regarding copyright ownership. The ASF licenses this file
6
- * to you under the Apache License, Version 2.0 (the
7
- * "License"); you may not use this file except in compliance
8
- * with the License. You may obtain a copy of the License at
9
- *
10
- * http://www.apache.org/licenses/LICENSE-2.0
11
- *
12
- * Unless required by applicable law or agreed to in writing,
13
- * software distributed under the License is distributed on an
14
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
- * KIND, either express or implied. See the License for the
16
- * specific language governing permissions and limitations
17
- * under the License.
18
- */
19
- import { spawn, spawnSync } from 'node:child_process';
20
- import { createHash } from 'node:crypto';
21
- import { readFileSync, rmSync, statSync } from 'node:fs';
22
- import { chmod, copyFile, mkdir, writeFile } from 'node:fs/promises';
23
- import { signMeteringCheckpoint, writeJsonAtomic } from './metering-checkpoint.js';
24
- import { createServer } from 'node:http';
25
- import { basename, join } from 'node:path';
26
- import { Agent, fetch as undiciFetch, ProxyAgent } from 'undici';
27
- import { decodePreverifiedToolchain, isExternalProfile, } from './toolchain-verification.js';
28
- import { isInferenceAdmissionEvent } from './provider-admission.js';
29
- import { DEEPSEEK_V4_FLASH_COST, deriveMetering, } from './provider-metering.js';
30
- import { removeEvalWebTools } from './provider-web-tool-surface.js';
31
- import { takeRelayResultToken, writeRelayResult } from './relay-result-frame.js';
32
- const resultToken = takeRelayResultToken();
33
- // The profile directory this wrapper writes. `dsh` has no environment variable
34
- // for profile selection, so the spec repeats this name in `--profile`; the
35
- // lifecycle test pins the two together.
36
- const DEEPSEEK_HARNESS_PROFILE = 'maka-eval';
37
- const PROVIDER_USAGE_CHECKPOINT_SCHEMA = 'maka.external_provider_usage.v2';
38
- const CLASSIFIABLE_RECORD_LIMIT_BYTES = 16 * 1024 * 1024;
39
- // One preparer per admissible profile, returning the path of any credential
40
- // file it wrote. Keying the table by ExternalProfile is what makes adding a
41
- // profile to TOOLCHAIN_IDENTITIES without preparing it a compile error, rather
42
- // than a silent fallthrough into whichever branch happened to be written last.
43
- const PROFILE_PREPARERS = {
44
- codex: async ({ env, home, root, proxyBaseUrl }) => {
45
- env.OPENAI_API_KEY = 'maka-eval-local';
46
- env.CODEX_HOME = home;
47
- const catalog = rooted(root, '/opt/maka-agent/packages/eval/harbor/deepseek-codex-models.json');
48
- await writeFile(join(home, 'config.toml'), [
49
- 'model_provider = "maka-http"',
50
- `model_catalog_json = ${JSON.stringify(catalog)}`,
51
- 'model_reasoning_effort = "max"',
52
- '',
53
- '[model_providers.maka-http]',
54
- 'name = "Maka Eval HTTP"',
55
- `base_url = ${JSON.stringify(proxyBaseUrl)}`,
56
- 'wire_api = "responses"',
57
- 'requires_openai_auth = true',
58
- 'supports_websockets = false',
59
- '',
60
- ].join('\n'), { mode: 0o600 });
61
- await writePolicy(rooted(root, '/etc/codex'), 'requirements.toml', 'allowed_web_search_modes = ["disabled"]\n');
62
- },
63
- 'claude-code': async ({ env, home, root, proxyBaseUrl }) => {
64
- env.ANTHROPIC_BASE_URL = proxyBaseUrl;
65
- env.ANTHROPIC_API_KEY = 'maka-eval-local';
66
- env.CLAUDE_CODE_HOME = home;
67
- await writePolicy(rooted(root, '/etc/claude-code'), 'managed-settings.json', '{"permissions":{"deny":["WebSearch","WebFetch","FetchURL"]}}\n');
68
- },
69
- reasonix: async ({ env, home, proxyBaseUrl, executableArgs }) => {
70
- const modelReference = executableArgs[executableArgs.indexOf('--model') + 1];
71
- const effort = executableArgs[executableArgs.indexOf('--effort') + 1];
72
- const [provider, model] = modelReference?.split('/') ?? [];
73
- if (provider !== 'maka-proxy' || !model || !effort) {
74
- throw new Error('Reasonix provider settings are incomplete');
75
- }
76
- await writeFile(join(home, 'config.toml'), `default_model = ${JSON.stringify(modelReference)}\n\n[[providers]]\nname = "maka-proxy"\nkind = "openai"\nbase_url = ${JSON.stringify(proxyBaseUrl)}\nmodels = [${JSON.stringify(model)}]\napi_key_env = "OPENAI_API_KEY"\neffort = ${JSON.stringify(effort)}\n\n[tools]\nenabled = ["bash", "read_file", "write_file", "edit_file", "glob", "grep"]\n\n[sandbox]\nbash = "off"\n`, { mode: 0o600 });
77
- const path = join(home, '.env');
78
- await writeFile(path, 'OPENAI_API_KEY=maka-eval-local\n', { mode: 0o600 });
79
- env.OPENAI_API_KEY = 'maka-eval-local';
80
- env.REASONIX_HOME = home;
81
- return path;
82
- },
83
- opencode: async ({ env, home, proxyBaseUrl }) => {
84
- env.DEEPSEEK_API_KEY = 'maka-eval-local';
85
- env.DEEPSEEK_BASE_URL = proxyBaseUrl;
86
- env.OPENCODE_CONFIG = join(home, 'opencode.json');
87
- env.XDG_DATA_HOME = join(home, 'data');
88
- env.XDG_CONFIG_HOME = join(home, 'config');
89
- env.XDG_STATE_HOME = join(home, 'state');
90
- await writeFile(env.OPENCODE_CONFIG, `${JSON.stringify({
91
- $schema: 'https://opencode.ai/config.json',
92
- provider: {
93
- deepseek: {
94
- options: {
95
- apiKey: '{env:DEEPSEEK_API_KEY}',
96
- baseURL: '{env:DEEPSEEK_BASE_URL}',
97
- },
98
- models: {
99
- 'deepseek-v4-flash': {
100
- name: 'DeepSeek V4 Flash',
101
- limit: {
102
- context: 1_048_576,
103
- output: 131_072,
104
- },
105
- options: {
106
- thinking: { type: 'enabled' },
107
- effort: 'max',
108
- },
109
- variants: {
110
- max: {
111
- thinking: { type: 'enabled' },
112
- effort: 'max',
113
- },
114
- },
115
- },
116
- },
117
- },
118
- },
119
- permission: {
120
- webfetch: 'deny',
121
- websearch: 'deny',
122
- },
123
- })}\n`, { mode: 0o600 });
124
- },
125
- 'kimi-code': async ({ env, home, proxyBaseUrl }) => {
126
- env.KIMI_CODE_HOME = home;
127
- env.KIMI_MODEL_NAME = 'deepseek-v4-flash';
128
- env.KIMI_MODEL_API_KEY = 'maka-eval-local';
129
- env.KIMI_MODEL_PROVIDER_TYPE = 'kimi';
130
- env.KIMI_MODEL_BASE_URL = proxyBaseUrl;
131
- env.KIMI_MODEL_MAX_CONTEXT_SIZE = '1048576';
132
- env.KIMI_MODEL_MAX_OUTPUT_SIZE = '131072';
133
- env.KIMI_MODEL_MAX_COMPLETION_TOKENS = '131072';
134
- env.KIMI_MODEL_ADAPTIVE_THINKING = 'true';
135
- env.KIMI_MODEL_THINKING_EFFORT = 'max';
136
- await writeFile(join(home, 'config.toml'), [
137
- 'telemetry = false',
138
- 'default_permission_mode = "auto"',
139
- '',
140
- '[background]',
141
- 'keep_alive_on_exit = true',
142
- '',
143
- '[permission]',
144
- 'rules = [',
145
- ' { decision = "deny", scope = "user", pattern = "WebSearch", reason = "Disabled for eval parity" },',
146
- ' { decision = "deny", scope = "user", pattern = "WebFetch", reason = "Disabled for eval parity" },',
147
- ' { decision = "deny", scope = "user", pattern = "FetchURL", reason = "Disabled for eval parity" },',
148
- ']',
149
- '',
150
- ].join('\n'), { mode: 0o600 });
151
- },
152
- pi: async ({ env, home, proxyBaseUrl }) => {
153
- env.OPENAI_API_KEY = 'maka-eval-local';
154
- env.PI_CODING_AGENT_DIR = home;
155
- env.PI_CODING_AGENT_SESSION_DIR = join(home, 'sessions');
156
- env.PI_OFFLINE = '1';
157
- env.PI_TELEMETRY = '0';
158
- await writeFile(join(home, 'models.json'), `${JSON.stringify({
159
- providers: {
160
- 'maka-proxy': {
161
- baseUrl: `${proxyBaseUrl.replace(/\/+$/u, '')}/v1`,
162
- api: 'openai-responses',
163
- apiKey: '$OPENAI_API_KEY',
164
- authHeader: true,
165
- models: [
166
- {
167
- id: 'deepseek-v4-flash',
168
- name: 'DeepSeek V4 Flash',
169
- reasoning: true,
170
- thinkingLevelMap: {
171
- off: null,
172
- minimal: null,
173
- low: null,
174
- medium: null,
175
- high: 'high',
176
- xhigh: null,
177
- max: 'max',
178
- },
179
- input: ['text'],
180
- contextWindow: 1_048_576,
181
- maxTokens: 131_072,
182
- cost: { ...DEEPSEEK_V4_FLASH_COST },
183
- },
184
- ],
185
- },
186
- },
187
- })}\n`, { mode: 0o600 });
188
- await writeFile(join(home, 'settings.json'), `${JSON.stringify({
189
- enableInstallTelemetry: false,
190
- defaultProjectTrust: 'never',
191
- })}\n`, { mode: 0o600 });
192
- },
193
- 'deepseek-harness': async ({ env, home, root, proxyBaseUrl }) => {
194
- // The arm supplies its own profile rather than patching a stock one, so the
195
- // composition names every entry the model can observe. DSH_HOME must be
196
- // writable: the harness links its bundled packages into
197
- // profiles/node_modules on first use, which it does offline.
198
- env.DSH_HOME = join(home, 'dsh');
199
- const profile = join(env.DSH_HOME, 'profiles', DEEPSEEK_HARNESS_PROFILE);
200
- await mkdir(profile, { recursive: true, mode: 0o700 });
201
- const source = rooted(root, '/opt/maka-agent/packages/eval/harbor/deepseek-harness-profile');
202
- for (const file of ['package.json', 'cordis.yml', 'cordis.patch.yml']) {
203
- await copyFile(join(source, file), join(profile, file));
204
- }
205
- env.DEEPSEEK_API_KEY = 'maka-eval-local';
206
- env.DEEPSEEK_BASE_URL = proxyBaseUrl;
207
- // The harness kills every descendant of its persistent PTY on shutdown,
208
- // which removes a task's own background service before the shared-environment
209
- // verifier can reach it. What the verifier scores is the environment the
210
- // task was left in, so no framework — this one, Maka, or the relay — may
211
- // edit it after the subject stops. The patched subprocess package honours
212
- // this flag by closing the shell without killing the process group.
213
- env.DSH_PRESERVE_BACKGROUND_PROCESSES = '1';
214
- },
215
- zcode: async ({ env, home, proxyBaseUrl }) => {
216
- const zcodeHome = join(home, '.zcode');
217
- const configRoot = join(zcodeHome, 'cli');
218
- await mkdir(configRoot, { recursive: true, mode: 0o700 });
219
- env.ZCODE_HOME = zcodeHome;
220
- env.ZCODE_DATA_BASE_DIR = zcodeHome;
221
- env.DEEPSEEK_API_KEY = 'maka-eval-local';
222
- await writeFile(join(configRoot, 'config.json'), `${JSON.stringify({
223
- provider: {
224
- deepseek: {
225
- kind: 'openai-compatible',
226
- name: 'DeepSeek',
227
- options: {
228
- baseURL: proxyBaseUrl,
229
- includeUsage: true,
230
- },
231
- models: {
232
- 'deepseek-v4-flash': {
233
- name: 'DeepSeek V4 Flash',
234
- supportsReasoning: true,
235
- supportsToolCall: true,
236
- contextWindow: 1_048_576,
237
- maxOutputTokens: 131_072,
238
- reasoning: {
239
- enabled: true,
240
- levels: ['high', 'max'],
241
- defaultLevel: 'max',
242
- providerOptionsByLevel: {
243
- max: {
244
- reasoningEffort: 'max',
245
- thinking: { type: 'enabled' },
246
- },
247
- },
248
- },
249
- },
250
- },
251
- },
252
- },
253
- model: 'deepseek/deepseek-v4-flash',
254
- permission: {
255
- mode: 'yolo',
256
- disallowedTools: ['WebSearch', 'WebFetch', 'FetchURL'],
257
- },
258
- features: { memory: false, mcp: false },
259
- plugins: { enabled: false },
260
- skills: { enabled: false },
261
- })}\n`, { mode: 0o600 });
262
- },
263
- };
264
- const [rawProfile, baseUrl, systemRoot, command, ...args] = process.argv.slice(2);
265
- if (!isExternalProfile(rawProfile))
266
- throw new Error('external subject profile is required');
267
- const profile = rawProfile;
268
- if (!command)
269
- throw new Error('external subject command is required');
270
- if (!baseUrl || !URL.canParse(baseUrl))
271
- throw new Error('external subject base URL is required');
272
- if (!systemRoot?.startsWith('/'))
273
- throw new Error('external subject system root is required');
274
- let credentialPath;
275
- let child;
276
- let stopped = false;
277
- const stop = (signal) => {
278
- stopped = true;
279
- removeCredential();
280
- child?.kill(signal);
281
- };
282
- const terminate = () => stop('SIGTERM');
283
- const interrupt = () => stop('SIGINT');
284
- process.once('SIGTERM', terminate);
285
- process.once('SIGINT', interrupt);
286
- const logsRoot = rooted(systemRoot, '/logs/agent');
287
- const statePath = join(logsRoot, `${profile}.wrapper-state.json`);
288
- const usagePath = join(logsRoot, `${profile}.provider-usage.json`);
289
- const artifacts = [];
290
- let usage = null;
291
- let costUsd = null;
292
- let status = 'infra_failed';
293
- let failureReason = 'external subject setup failed';
294
- try {
295
- await mkdir(logsRoot, { recursive: true, mode: 0o700 });
296
- await writeState('started');
297
- const identity = decodePreverifiedToolchain(profile, systemRoot, command);
298
- artifacts.push({ kind: 'toolchain', profile, ...identity });
299
- await writeState('toolchain_verified');
300
- const key = process.env.OPENAI_API_KEY ?? process.env.ANTHROPIC_API_KEY ?? process.env.DEEPSEEK_API_KEY;
301
- delete process.env.OPENAI_API_KEY;
302
- delete process.env.ANTHROPIC_API_KEY;
303
- delete process.env.DEEPSEEK_API_KEY;
304
- if (!key)
305
- throw new Error('external subject credential is missing');
306
- const proxy = await startMeteringProxy(baseUrl, key, profile === 'claude-code', profile, usagePath);
307
- try {
308
- await writeState('proxy_started');
309
- const prepared = await prepareProfile(profile, proxy.baseUrl, systemRoot, args);
310
- credentialPath = prepared.credentialPath;
311
- if (profile === 'claude-code')
312
- prepareClaudeWorkspace(systemRoot, prepared.home);
313
- const result = await runChild(command, args, prepared.env, profile, async (exitCode) => {
314
- proxy.stopAccepting();
315
- if (exitCode !== undefined)
316
- await writeState('child_exited', { exitCode });
317
- });
318
- child = undefined;
319
- const metering = await proxy.report();
320
- const derived = deriveMetering(metering);
321
- usage = metering.usage;
322
- costUsd = derived.costUsd;
323
- const classified = classifyExecution(profile, result.exitCode, result.stdout, metering.admittedRequests);
324
- status = stopped ? 'indeterminate' : classified.status;
325
- failureReason = stopped ? 'external subject cancelled' : classified.failureReason;
326
- await writeState('result_ready', {
327
- status,
328
- requests: metering.requests,
329
- admittedRequests: metering.admittedRequests,
330
- usageRequests: metering.usageRequests,
331
- removedWebTools: metering.removedWebTools,
332
- });
333
- artifacts.push({ kind: 'external-process', profile, exitCode: result.exitCode }, streamArtifact('stdout', profile, result.stdout), streamArtifact('stderr', profile, result.stderr), {
334
- kind: 'provider-metering',
335
- profile,
336
- requests: metering.requests,
337
- admittedRequests: metering.admittedRequests,
338
- usageRequests: metering.usageRequests,
339
- usageComplete: derived.usageComplete,
340
- removedWebTools: metering.removedWebTools,
341
- models: metering.models,
342
- toolNames: metering.toolNames,
343
- }, fileArtifact('wrapper-state', statePath, profile));
344
- }
345
- finally {
346
- await proxy.close();
347
- }
348
- }
349
- catch (error) {
350
- status = stopped ? 'indeterminate' : 'infra_failed';
351
- failureReason = stopped
352
- ? 'external subject cancelled'
353
- : safeFailure(error, 'external subject setup failed');
354
- await writeState('setup_failed', { failureReason }).catch(() => undefined);
355
- }
356
- finally {
357
- process.removeListener('SIGTERM', terminate);
358
- process.removeListener('SIGINT', interrupt);
359
- removeCredential();
360
- }
361
- // A process's exit code reports what it did, and anything that can read only
362
- // the exit code — the relay, a log, an operator — should read the same status
363
- // the frame carries. The adapter still prefers the frame where it has one; this
364
- // is what it falls back to when the frame carries no status of its own.
365
- process.exitCode = status === 'completed' ? 0 : 1;
366
- writeRelayResult(resultToken, {
367
- schemaVersion: 'maka.external_subject_result.v2',
368
- status,
369
- failureReason,
370
- usage,
371
- costUsd,
372
- artifacts,
373
- });
374
- async function prepareProfile(selected, proxyBaseUrl, root, executableArgs) {
375
- const env = { ...process.env };
376
- const home = rooted(root, `/tmp/maka-eval-${selected}`);
377
- await mkdir(home, { recursive: true, mode: 0o700 });
378
- env.HOME = home;
379
- const credential = await PROFILE_PREPARERS[selected]({
380
- env,
381
- home,
382
- root,
383
- proxyBaseUrl,
384
- executableArgs,
385
- });
386
- return { env, home, ...(credential ? { credentialPath: credential } : {}) };
387
- }
388
- async function runChild(executable, executableArgs, env, selected, onExit) {
389
- const running = spawn(executable, executableArgs, {
390
- env,
391
- stdio: ['ignore', 'pipe', 'pipe'],
392
- });
393
- child = running;
394
- const stdout = classifyStream(running.stdout, selected);
395
- const stderr = captureStreamDiagnostic(running.stderr);
396
- const exitCode = await new Promise((resolveExit, reject) => {
397
- let settled = false;
398
- running.once('error', (error) => {
399
- if (settled)
400
- return;
401
- settled = true;
402
- void onExit().then(() => reject(error), reject);
403
- });
404
- running.once('exit', (code) => {
405
- if (settled)
406
- return;
407
- settled = true;
408
- const exitCode = code ?? 1;
409
- void onExit(exitCode).then(() => resolveExit(exitCode), reject);
410
- });
411
- });
412
- const [stdoutResult, stderrResult] = await Promise.all([stdout, stderr]);
413
- return { exitCode, stdout: stdoutResult, stderr: stderrResult };
414
- }
415
- async function classifyStream(input, selected) {
416
- const digest = createHash('sha256');
417
- const decoder = new TextDecoder();
418
- let observedBytes = 0;
419
- let buffered = '';
420
- let completed = false;
421
- let reportedError = false;
422
- let nonempty = false;
423
- let recordTooLarge = false;
424
- const consume = (line) => {
425
- nonempty ||= line.trim().length > 0;
426
- const signal = classifyLine(selected, line);
427
- completed ||= signal.completed;
428
- reportedError ||= signal.reportedError;
429
- };
430
- for await (const chunk of input) {
431
- const bytes = Buffer.from(chunk);
432
- observedBytes += bytes.byteLength;
433
- digest.update(bytes);
434
- buffered += decoder.decode(bytes, { stream: true });
435
- let newline = buffered.indexOf('\n');
436
- while (newline >= 0) {
437
- consume(buffered.slice(0, newline));
438
- buffered = buffered.slice(newline + 1);
439
- newline = buffered.indexOf('\n');
440
- }
441
- if (Buffer.byteLength(buffered) > CLASSIFIABLE_RECORD_LIMIT_BYTES) {
442
- recordTooLarge = true;
443
- buffered = '';
444
- }
445
- }
446
- buffered += decoder.decode();
447
- if (buffered)
448
- consume(buffered);
449
- const streamDiagnostic = { observedBytes, sha256: digest.digest('hex') };
450
- if (recordTooLarge) {
451
- return {
452
- ...streamDiagnostic,
453
- classification: 'record-too-large',
454
- limitBytes: CLASSIFIABLE_RECORD_LIMIT_BYTES,
455
- };
456
- }
457
- return {
458
- ...streamDiagnostic,
459
- classification: 'classified',
460
- completed,
461
- reportedError,
462
- nonempty,
463
- };
464
- }
465
- async function captureStreamDiagnostic(input) {
466
- const digest = createHash('sha256');
467
- let observedBytes = 0;
468
- for await (const chunk of input) {
469
- const bytes = Buffer.from(chunk);
470
- observedBytes += bytes.byteLength;
471
- digest.update(bytes);
472
- }
473
- return { observedBytes, sha256: digest.digest('hex') };
474
- }
475
- function streamArtifact(kind, profile, capture) {
476
- return {
477
- kind,
478
- profile,
479
- bytes: capture.observedBytes,
480
- sha256: capture.sha256,
481
- ...('classification' in capture &&
482
- capture.classification === 'record-too-large' &&
483
- 'limitBytes' in capture
484
- ? { classification: capture.classification, limitBytes: capture.limitBytes }
485
- : {}),
486
- };
487
- }
488
- function prepareClaudeWorkspace(root, home) {
489
- if (root !== '/')
490
- return;
491
- const result = spawnSync('/bin/chown', ['-R', '1000:1000', process.cwd(), home, '/logs/agent']);
492
- if (result.status !== 0)
493
- throw new Error('Claude Code workspace ownership setup failed');
494
- if (!process.setgid || !process.setuid) {
495
- throw new Error('Claude Code user isolation is unavailable');
496
- }
497
- process.setgid(1000);
498
- process.setuid(1000);
499
- }
500
- function classifyExecution(selected, exitCode, output, admittedRequests) {
501
- if (output.classification === 'record-too-large') {
502
- return {
503
- status: 'infra_failed',
504
- failureReason: `${selected} output record exceeded the classification limit`,
505
- };
506
- }
507
- // Neither harness emits a structured completion event: zcode and the DeepSeek
508
- // Harness one-shot CLI print the final assistant message and nothing else, so
509
- // a clean exit with output is the whole signal on offer. Scores come from the verifier either way; this
510
- // only decides whether the attempt reads as completed or failed.
511
- const completed = output.completed ||
512
- ((selected === 'zcode' || selected === 'deepseek-harness') &&
513
- exitCode === 0 &&
514
- output.nonempty);
515
- const reportedError = output.reportedError;
516
- if (admittedRequests === 0) {
517
- return { status: 'infra_failed', failureReason: `${selected} failed before model admission` };
518
- }
519
- if (completed && !reportedError) {
520
- return { status: 'completed', failureReason: null };
521
- }
522
- return { status: 'failed', failureReason: `${selected} execution failed` };
523
- }
524
- function classifyLine(selected, line) {
525
- if (!line.trim())
526
- return { completed: false, reportedError: false };
527
- let completed = false;
528
- let reportedError = false;
529
- try {
530
- const event = JSON.parse(line);
531
- if (selected === 'codex') {
532
- completed ||= event.type === 'turn.completed';
533
- reportedError ||= event.type === 'turn.failed';
534
- }
535
- else if (selected === 'claude-code') {
536
- if (event.type === 'result') {
537
- completed ||= event.is_error === false;
538
- reportedError ||= event.is_error === true;
539
- }
540
- }
541
- else if (selected === 'reasonix') {
542
- completed ||= event.type === 'result' && event.is_error !== true;
543
- reportedError ||= event.type === 'result' && event.is_error === true;
544
- }
545
- else if (selected === 'opencode') {
546
- completed ||= event.type === 'step_finish';
547
- reportedError ||= event.type === 'error';
548
- }
549
- else if (selected === 'kimi-code') {
550
- completed ||= event.role === 'assistant';
551
- reportedError ||= event.role === 'error' || event.type === 'error';
552
- }
553
- else if (selected === 'pi') {
554
- completed ||= event.type === 'agent_end';
555
- const message = isRecord(event.message) ? event.message : undefined;
556
- if (message?.role === 'assistant') {
557
- reportedError ||= message.stopReason === 'error' || message.stopReason === 'aborted';
558
- }
559
- }
560
- }
561
- catch {
562
- return { completed: false, reportedError: false };
563
- }
564
- return { completed, reportedError };
565
- }
566
- async function startMeteringProxy(upstreamBaseUrl, upstreamKey, anthropic, selected, checkpointPath) {
567
- const total = zeroUsage();
568
- let requests = 0;
569
- let inFlightRequests = 0;
570
- let admittedRequests = 0;
571
- let usageRequests = 0;
572
- let removedWebTools = 0;
573
- let settled = false;
574
- const requestModels = new Set();
575
- const observedToolNames = new Set();
576
- const snapshot = () => ({
577
- usage: usageRequests > 0 ? { ...total, totalTokens: total.inputTokens + total.outputTokens } : null,
578
- settled,
579
- requests,
580
- inFlightRequests,
581
- admittedRequests,
582
- usageRequests,
583
- removedWebTools,
584
- models: [...requestModels].sort(),
585
- toolNames: [...observedToolNames].sort(),
586
- });
587
- // The wrapper's own result frame is lost when the process is killed rather
588
- // than asked to stop, and a run that is cut off is exactly the run with the
589
- // most tokens spent. The checkpoint is therefore written at the start and the
590
- // settlement of every request, so whatever survives on disk is a truthful
591
- // lower bound at worst. Writes are serialized through one chain: two
592
- // concurrent requests must not interleave into a half-written file.
593
- // A failed write leaves the previous snapshot on disk, which is still a
594
- // truthful lower bound. It must not do more than that: an unhandled rejection
595
- // here would propagate down the chain and turn a run that finished into an
596
- // infrastructure failure over a file that exists only in case the run does
597
- // not finish.
598
- let checkpointWrites = Promise.resolve();
599
- const persistCheckpoint = () => {
600
- const value = signMeteringCheckpoint({
601
- schemaVersion: PROVIDER_USAGE_CHECKPOINT_SCHEMA,
602
- profile: selected,
603
- ...snapshot(),
604
- }, resultToken);
605
- checkpointWrites = checkpointWrites.then(() => writeJsonAtomic(checkpointPath, value).catch(() => undefined));
606
- return checkpointWrites;
607
- };
608
- await persistCheckpoint();
609
- // Undici defaults to aborting a request after 300s without headers or body
610
- // bytes. A reasoning model can legitimately be quiet for longer, and killing
611
- // it here would turn a slow answer into an infrastructure failure. The
612
- // benchmark's own agent timeout stays the only deadline.
613
- const timeouts = { headersTimeout: 0, bodyTimeout: 0 };
614
- const dispatcher = process.env.HTTPS_PROXY
615
- ? new ProxyAgent({ uri: process.env.HTTPS_PROXY, ...timeouts })
616
- : new Agent(timeouts);
617
- const active = new Set();
618
- let accepting = true;
619
- const stopAccepting = () => {
620
- accepting = false;
621
- };
622
- const server = createServer((request, response) => {
623
- // Refused rather than counted: a request the proxy declined was never
624
- // admitted by the provider and never billed, so it is not part of what
625
- // this run spent.
626
- if (!accepting) {
627
- response.statusCode = 503;
628
- response.end(JSON.stringify({ error: 'provider proxy has settled' }));
629
- return;
630
- }
631
- const operation = (async () => {
632
- requests += 1;
633
- inFlightRequests += 1;
634
- try {
635
- await persistCheckpoint();
636
- const projected = removeEvalWebTools(await readRequest(request));
637
- removedWebTools += projected.removed;
638
- if (projected.model)
639
- requestModels.add(projected.model);
640
- for (const name of projected.toolNames)
641
- observedToolNames.add(name);
642
- const target = joinUpstream(upstreamBaseUrl, request.url ?? '/');
643
- const headers = {};
644
- for (const [name, value] of Object.entries(request.headers)) {
645
- if (value === undefined ||
646
- ['host', 'content-length', 'connection', 'authorization', 'x-api-key'].includes(name.toLowerCase()))
647
- continue;
648
- headers[name] = Array.isArray(value) ? value.join(', ') : value;
649
- }
650
- if (anthropic)
651
- headers['x-api-key'] = upstreamKey;
652
- else
653
- headers.authorization = `Bearer ${upstreamKey}`;
654
- const upstream = await undiciFetch(target, {
655
- method: request.method,
656
- headers,
657
- body: projected.body.length === 0 ? undefined : new Uint8Array(projected.body),
658
- dispatcher,
659
- });
660
- response.statusCode = upstream.status;
661
- upstream.headers.forEach((value, name) => {
662
- if (!['content-length', 'content-encoding', 'transfer-encoding', 'connection'].includes(name))
663
- response.setHeader(name, value);
664
- });
665
- const parser = usageParser(anthropic);
666
- let counted = false;
667
- const admit = async () => {
668
- if (counted || !upstream.ok || !parser.admitted())
669
- return;
670
- counted = true;
671
- admittedRequests += 1;
672
- await persistCheckpoint();
673
- };
674
- try {
675
- // Admission is a fact about the provider, not about this request
676
- // finishing: once the stream carries the event, the model work has
677
- // been done and billed whatever happens to this process next. It is
678
- // therefore recorded and persisted the moment it is observed, so a
679
- // checkpoint written mid-stream already knows. `admitted` is
680
- // monotonic and `counted` makes the increment idempotent, so this
681
- // costs one write per request rather than one per chunk.
682
- if (upstream.body) {
683
- const reader = upstream.body.getReader();
684
- for (;;) {
685
- const next = await reader.read();
686
- if (next.done)
687
- break;
688
- const chunk = Buffer.from(next.value);
689
- parser.push(chunk.toString('utf8'));
690
- // Before the chunk is handed to a downstream this process does not
691
- // control: once it is written, a disconnect or a kill can end this
692
- // request at any point, and the admission would be lost with it.
693
- await admit();
694
- response.write(chunk);
695
- }
696
- }
697
- response.end();
698
- }
699
- finally {
700
- const parsed = parser.finish();
701
- await admit();
702
- if (counted && parsed.usage) {
703
- usageRequests += 1;
704
- addUsage(total, parsed.usage);
705
- }
706
- }
707
- }
708
- finally {
709
- inFlightRequests -= 1;
710
- await persistCheckpoint();
711
- }
712
- })().catch((error) => {
713
- response.statusCode = 502;
714
- response.end(JSON.stringify({ error: safeFailure(error, 'provider proxy failed') }));
715
- });
716
- active.add(operation);
717
- void operation.finally(() => active.delete(operation));
718
- });
719
- await listen(server);
720
- const address = server.address();
721
- if (!address || typeof address === 'string')
722
- throw new Error('provider proxy did not bind');
723
- return {
724
- baseUrl: `http://127.0.0.1:${address.port}`,
725
- stopAccepting,
726
- report: async () => {
727
- // Admission closes before anything is drained, so settlement is made
728
- // true rather than asserted about a moment. The subject's own process has
729
- // exited by now, but the processes it left behind have not — they run in
730
- // this container and this proxy is still bound — and a request accepted
731
- // after the flag was set would leave a checkpoint claiming settlement
732
- // with work in flight. Closing the socket would not do this: it stops new
733
- // connections while leaving established ones free to send another
734
- // request, and it waits on idle keep-alive sockets that may never close.
735
- stopAccepting();
736
- await Promise.allSettled([...active]);
737
- // Settlement is something this process observes; it is not recoverable
738
- // from the counts. A checkpoint whose last successful write happened
739
- // mid-run also has no requests in flight as of that write, so a reader
740
- // cannot tell it from this one except by being told.
741
- settled = true;
742
- await persistCheckpoint();
743
- return snapshot();
744
- },
745
- close: async () => {
746
- stopAccepting();
747
- await Promise.allSettled([...active]);
748
- await checkpointWrites;
749
- await closeServer(server);
750
- await dispatcher.close();
751
- },
752
- };
753
- }
754
- function usageParser(anthropic) {
755
- let buffered = '';
756
- let current = null;
757
- let admitted = false;
758
- const consume = (line) => {
759
- const raw = line.trim().replace(/^data:\s*/u, '');
760
- if (!raw || raw === '[DONE]')
761
- return;
762
- try {
763
- const value = JSON.parse(raw);
764
- admitted ||= isInferenceAdmissionEvent(value, anthropic);
765
- current = mergeUsage(current, usageFromEvent(value, anthropic));
766
- }
767
- catch {
768
- return;
769
- }
770
- };
771
- return {
772
- push(text) {
773
- buffered += text;
774
- let newline = buffered.indexOf('\n');
775
- while (newline >= 0) {
776
- consume(buffered.slice(0, newline));
777
- buffered = buffered.slice(newline + 1);
778
- newline = buffered.indexOf('\n');
779
- }
780
- },
781
- admitted() {
782
- return admitted;
783
- },
784
- finish() {
785
- if (buffered.trim())
786
- consume(buffered);
787
- return { usage: current, admitted };
788
- },
789
- };
790
- }
791
- function usageFromEvent(event, anthropic) {
792
- const response = event.type === 'response.completed' && isRecord(event.response) ? event.response : event;
793
- const message = event.type === 'message_start' && isRecord(event.message) ? event.message : response;
794
- const raw = isRecord(message.usage) ? message.usage : isRecord(event.usage) ? event.usage : null;
795
- if (!raw)
796
- return null;
797
- const input = number(raw.input_tokens ?? raw.prompt_tokens);
798
- const output = number(raw.output_tokens ?? raw.completion_tokens);
799
- const inputDetails = isRecord(raw.input_tokens_details)
800
- ? raw.input_tokens_details
801
- : isRecord(raw.prompt_tokens_details)
802
- ? raw.prompt_tokens_details
803
- : {};
804
- const outputDetails = isRecord(raw.output_tokens_details)
805
- ? raw.output_tokens_details
806
- : isRecord(raw.completion_tokens_details)
807
- ? raw.completion_tokens_details
808
- : {};
809
- // `prompt_cache_hit_tokens` is where DeepSeek's chat-completions responses
810
- // put cache reads, and the runtime's own adapter reads it ahead of the
811
- // OpenAI-shaped field. Omitting it here would not lose the tokens — they
812
- // stay in `prompt_tokens`, and would then be billed as uncached input at
813
- // fifty times the cached rate.
814
- const cacheRead = number(raw.cache_read_input_tokens ?? raw.prompt_cache_hit_tokens ?? inputDetails.cached_tokens);
815
- const cacheWrite = number(raw.cache_creation_input_tokens ??
816
- raw.cache_write_input_tokens ??
817
- inputDetails.cache_write_tokens ??
818
- inputDetails.cache_creation_tokens);
819
- const reasoning = number(outputDetails.reasoning_tokens);
820
- const totalInput = anthropic ? input + cacheRead + cacheWrite : input;
821
- return {
822
- inputTokens: totalInput,
823
- outputTokens: output,
824
- cacheReadTokens: cacheRead,
825
- cacheWriteTokens: cacheWrite,
826
- reasoningTokens: reasoning,
827
- totalTokens: totalInput + output,
828
- };
829
- }
830
- function mergeUsage(left, right) {
831
- if (!right)
832
- return left;
833
- if (!left)
834
- return right;
835
- return {
836
- inputTokens: Math.max(left.inputTokens, right.inputTokens),
837
- outputTokens: Math.max(left.outputTokens, right.outputTokens),
838
- cacheReadTokens: Math.max(left.cacheReadTokens, right.cacheReadTokens),
839
- cacheWriteTokens: Math.max(left.cacheWriteTokens, right.cacheWriteTokens),
840
- reasoningTokens: Math.max(left.reasoningTokens, right.reasoningTokens),
841
- totalTokens: Math.max(left.totalTokens, right.totalTokens),
842
- };
843
- }
844
- function addUsage(target, value) {
845
- target.inputTokens += value.inputTokens;
846
- target.outputTokens += value.outputTokens;
847
- target.cacheReadTokens += value.cacheReadTokens;
848
- target.cacheWriteTokens += value.cacheWriteTokens;
849
- target.reasoningTokens += value.reasoningTokens;
850
- target.totalTokens = target.inputTokens + target.outputTokens;
851
- }
852
- function zeroUsage() {
853
- return {
854
- inputTokens: 0,
855
- outputTokens: 0,
856
- cacheReadTokens: 0,
857
- cacheWriteTokens: 0,
858
- reasoningTokens: 0,
859
- totalTokens: 0,
860
- };
861
- }
862
- function readRequest(request) {
863
- return new Promise((resolveBody, reject) => {
864
- const chunks = [];
865
- request.on('data', (chunk) => chunks.push(Buffer.from(chunk)));
866
- request.once('end', () => resolveBody(Buffer.concat(chunks)));
867
- request.once('error', reject);
868
- });
869
- }
870
- function listen(server) {
871
- return new Promise((resolveListen, reject) => {
872
- server.once('error', reject);
873
- server.listen(0, '127.0.0.1', resolveListen);
874
- });
875
- }
876
- function closeServer(server) {
877
- return new Promise((resolveClose) => server.close(() => resolveClose()));
878
- }
879
- function joinUpstream(baseUrl, requestUrl) {
880
- const base = new URL(baseUrl);
881
- const request = new URL(requestUrl, 'http://maka-eval.local');
882
- base.pathname = `${base.pathname.replace(/\/$/u, '')}/${request.pathname.replace(/^\//u, '')}`;
883
- base.search = request.search;
884
- return base.toString();
885
- }
886
- function fileArtifact(kind, path, selected) {
887
- const bytes = statSync(path).size;
888
- return {
889
- kind,
890
- profile: selected,
891
- path: `/logs/agent/${basename(path)}`,
892
- bytes,
893
- sha256: createHash('sha256').update(readFileSync(path)).digest('hex'),
894
- };
895
- }
896
- async function writePolicy(directory, name, contents) {
897
- await mkdir(directory, { recursive: true, mode: 0o755 });
898
- await chmod(directory, 0o755);
899
- const path = join(directory, name);
900
- await writeFile(path, contents, { mode: 0o644 });
901
- await chmod(path, 0o644);
902
- }
903
- function rooted(root, absolutePath) {
904
- return root === '/' ? absolutePath : join(root, absolutePath.replace(/^\//u, ''));
905
- }
906
- function removeCredential() {
907
- if (credentialPath)
908
- rmSync(credentialPath, { force: true });
909
- }
910
- function safeFailure(error, fallback) {
911
- if (!(error instanceof Error))
912
- return fallback;
913
- if (/toolchain (?:fingerprint|checksum|path)/u.test(error.message))
914
- return error.message;
915
- if (/credential is missing/u.test(error.message))
916
- return error.message;
917
- if (/workspace ownership/u.test(error.message))
918
- return error.message;
919
- return fallback;
920
- }
921
- function isRecord(value) {
922
- return value !== null && typeof value === 'object' && !Array.isArray(value);
923
- }
924
- function number(value) {
925
- return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : 0;
926
- }
927
- async function writeState(phase, detail = {}) {
928
- await writeFile(statePath, `${JSON.stringify({ schemaVersion: 1, profile, phase, ...detail })}\n`, { mode: 0o600 });
929
- }