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,1013 +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 } from 'node:child_process';
20
- import { createHash, randomBytes } from 'node:crypto';
21
- import { once } from 'node:events';
22
- import { createReadStream } from 'node:fs';
23
- import { chmod, lstat, mkdir, readFile, readdir, unlink, writeFile } from 'node:fs/promises';
24
- import { createServer } from 'node:net';
25
- import { basename, dirname, join, posix, relative, resolve, sep } from 'node:path';
26
- import { createInterface } from 'node:readline';
27
- import { decodeJsonObject } from './experiment.js';
28
- import { BUNDLED_HARNESS_RELAY_ROOT, createHarnessPreparationEnvironment, resolveRealPathWithinRoot, } from './harness-environment.js';
29
- import { preflightHarnessInstallation, } from './install-preflight.js';
30
- import { MAKA_RUNTIME_ARTIFACT_PATH, MAKA_SUBJECT_STDERR_PATH, MAKA_SUBJECT_STDOUT_PATH, } from './maka-artifacts.js';
31
- const PIER_FRAMEWORK_LOG_MOUNTS = Object.freeze([
32
- { directory: 'agent', target: '/logs/agent' },
33
- { directory: 'verifier', target: '/logs/verifier' },
34
- { directory: 'artifacts', target: '/logs/artifacts' },
35
- ]);
36
- export function createHarborExecutor(config, specPath) {
37
- return createHarnessExecutor('harbor', config, specPath);
38
- }
39
- export function createPierExecutor(config, specPath) {
40
- return createHarnessExecutor('pier', config, specPath);
41
- }
42
- function createHarnessExecutor(framework, config, specPath) {
43
- const options = decodeHarnessOptions(config, framework);
44
- const executor = {
45
- kind: framework,
46
- preflight: (input, dependencies) => preflightHarnessInstallation({
47
- framework,
48
- options,
49
- specPath,
50
- subjectCredentialNames: input.subjectCredentialNames,
51
- ...(input.signal ? { signal: input.signal } : {}),
52
- }, dependencies),
53
- validate: (cell) => {
54
- decodeTask(framework, options, cell);
55
- },
56
- runAttempt: (input, operation) => runHarnessAttempt(framework, options, specPath, input, operation),
57
- };
58
- return executor;
59
- }
60
- async function runHarnessAttempt(framework, options, specPath, { cell, subjectCredentialNames, signal, }, operation) {
61
- if (signal?.aborted)
62
- return notStarted('cancelled');
63
- let prepared;
64
- try {
65
- prepared = await startTrial(framework, options, specPath, cell, subjectCredentialNames, signal);
66
- }
67
- catch {
68
- return notStarted('preparation-failed');
69
- }
70
- if (prepared.kind === 'not_started')
71
- return prepared;
72
- const state = prepared.state;
73
- let decision = false;
74
- let value;
75
- let hasValue = false;
76
- let hostCancellationObserved = false;
77
- let verificationConfirmedBeforeCancellation = false;
78
- let finalizationEvidence;
79
- let cleanupAction;
80
- let cleanupEvidence;
81
- let cleanup;
82
- const terminate = () => {
83
- cleanupAction ??= state.used ? 'abort' : 'terminate-unused';
84
- cleanup ??= (async () => {
85
- const evidence = await waitForTrial(state.child, {
86
- phase: state.used ? 'abort' : 'unused',
87
- deadlineMs: state.used ? RELAY_SETTLEMENT_DEADLINE_MS : TERM_SETTLEMENT_DEADLINE_MS,
88
- });
89
- if (evidence.outcome === 'unsettled')
90
- await state.closeRelay();
91
- return evidence;
92
- })();
93
- return cleanup;
94
- };
95
- const onAbort = () => {
96
- hostCancellationObserved = true;
97
- void terminate();
98
- };
99
- signal?.addEventListener('abort', onAbort, { once: true });
100
- if (signal?.aborted)
101
- onAbort();
102
- const decide = () => {
103
- if (decision)
104
- return true;
105
- if (!sendRelayMessage(state.transport, 'decision', { token: state.token, kind: 'verify' }, true)) {
106
- void terminate();
107
- return false;
108
- }
109
- decision = true;
110
- return true;
111
- };
112
- try {
113
- value = await operation({
114
- context: relayContext(state, signal),
115
- verify: async () => {
116
- if (!decide()) {
117
- cleanupEvidence = await terminate();
118
- finalizationEvidence = cleanupEvidence;
119
- throw new Error('relay verify decision was not delivered');
120
- }
121
- const completed = cleanup
122
- ? await cleanup
123
- : await waitForTrial(state.child, { phase: 'completion' });
124
- finalizationEvidence = completed;
125
- if (!finalizationConfirmed(completed))
126
- throw new Error('Trial did not finalize cleanly');
127
- const verification = await readVerification(state, cell, framework, Boolean(options.egressProxy));
128
- verificationConfirmedBeforeCancellation = !hostCancellationObserved;
129
- return verification;
130
- },
131
- });
132
- hasValue = true;
133
- }
134
- finally {
135
- signal?.removeEventListener('abort', onAbort);
136
- if (!decision || cleanup) {
137
- cleanupEvidence = await terminate();
138
- finalizationEvidence = cleanupEvidence;
139
- }
140
- await state.closeRelay();
141
- }
142
- if (hasValue &&
143
- (state.transport.failure || cleanupAction || state.diagnostic?.category !== 'none')) {
144
- value = {
145
- ...value,
146
- artifacts: [
147
- ...value.artifacts,
148
- ...(state.transport.failure
149
- ? [{ kind: 'executor-relay', ...state.transport.failure }]
150
- : []),
151
- ...(cleanupAction
152
- ? [
153
- {
154
- kind: 'executor-cleanup',
155
- action: cleanupAction,
156
- phase: cleanupEvidence.phase,
157
- deadlineMs: cleanupEvidence.deadlineMs,
158
- escalation: cleanupEvidence.escalation,
159
- outcome: cleanupEvidence.outcome,
160
- },
161
- ]
162
- : []),
163
- ...(state.diagnostic && state.diagnostic.category !== 'none'
164
- ? [{ kind: 'executor-relay-result', ...state.diagnostic }]
165
- : []),
166
- ],
167
- };
168
- }
169
- if (hostCancellationObserved && !verificationConfirmedBeforeCancellation) {
170
- return hasValue
171
- ? { kind: 'indeterminate', cause: 'host-cancelled', value }
172
- : { kind: 'indeterminate', cause: 'host-cancelled' };
173
- }
174
- if (!finalizationEvidence || !finalizationConfirmed(finalizationEvidence)) {
175
- return hasValue
176
- ? { kind: 'indeterminate', cause: 'cleanup-unconfirmed', value }
177
- : { kind: 'indeterminate', cause: 'cleanup-unconfirmed' };
178
- }
179
- if (!hasValue)
180
- throw new Error('executor operation did not settle');
181
- return { kind: 'settled', value };
182
- }
183
- function relayContext(state, signal) {
184
- const resultToken = randomBytes(16).toString('hex');
185
- const metadata = {
186
- trialName: state.trialName,
187
- trialPath: state.trialPath,
188
- meteringSecret: resultToken,
189
- };
190
- return {
191
- cwd: state.cwd,
192
- taskInput: state.taskInput,
193
- metadata,
194
- ...(signal ? { signal } : {}),
195
- execute: async (input) => {
196
- signal?.throwIfAborted();
197
- if (state.used)
198
- throw new Error('Trial already executed its subject');
199
- state.used = true;
200
- const credentials = Object.fromEntries(Object.entries(input.credentialEnvironment).map(([target, source]) => {
201
- const value = state.credentials[source];
202
- if (value === undefined)
203
- throw new Error(`credential ${source} was not admitted`);
204
- return [target, value];
205
- }));
206
- if (!sendRelayMessage(state.transport, 'execute', {
207
- token: state.token,
208
- kind: 'execute',
209
- command: input.command,
210
- args: input.args,
211
- environment: mergeExecutionEnvironment(state.executionEnvironment, input.environment ?? {}),
212
- credentials,
213
- resultToken,
214
- captureStdout: input.captureStdout ?? true,
215
- })) {
216
- throw new Error('relay transport is unavailable');
217
- }
218
- state.transport.stage = 'receive';
219
- let executed;
220
- try {
221
- executed = await readLine(state.lines);
222
- }
223
- catch {
224
- state.transport.failure ??= {
225
- stage: 'receive',
226
- category: 'protocol-error',
227
- delivery: 'unknown',
228
- };
229
- throw new Error('relay execution result was unavailable');
230
- }
231
- if (executed.token !== state.token ||
232
- executed.kind !== 'executed' ||
233
- (executed.termination !== 'exited' && executed.termination !== 'framework_timeout') ||
234
- typeof executed.exitCode !== 'number' ||
235
- typeof executed.stdout !== 'string' ||
236
- !validProcessDiagnostic(executed.diagnostic)) {
237
- state.transport.failure ??= {
238
- stage: 'receive',
239
- category: 'protocol-error',
240
- delivery: 'unknown',
241
- };
242
- throw new Error('relay returned an invalid execution result');
243
- }
244
- state.diagnostic = executed.diagnostic;
245
- return {
246
- termination: executed.termination,
247
- exitCode: executed.exitCode,
248
- stdout: executed.stdout,
249
- diagnostic: executed.diagnostic,
250
- };
251
- },
252
- };
253
- }
254
- function validProcessDiagnostic(value) {
255
- if (!value || typeof value !== 'object' || Array.isArray(value))
256
- return false;
257
- const diagnostic = value;
258
- const fields = Object.keys(diagnostic);
259
- return ((diagnostic.category === 'none' && fields.length === 1) ||
260
- ([
261
- 'unstructured-output',
262
- 'result-frame-missing',
263
- 'result-frame-invalid',
264
- 'result-frame-ambiguous',
265
- 'result-frame-oversize',
266
- 'execution-scope-unavailable',
267
- ].includes(String(diagnostic.category)) &&
268
- fields.length === 3 &&
269
- typeof diagnostic.bytes === 'number' &&
270
- Number.isSafeInteger(diagnostic.bytes) &&
271
- diagnostic.bytes >= 0 &&
272
- typeof diagnostic.sha256 === 'string' &&
273
- /^[0-9a-f]{64}$/u.test(diagnostic.sha256)));
274
- }
275
- async function startTrial(framework, options, specPath, cell, subjectCredentialNames, signal) {
276
- const credentials = requireCredentials(cell.subject.credentials);
277
- const token = randomBytes(24).toString('hex');
278
- const trialsRoot = resolve(process.env[options.trialsRootEnv]);
279
- await mkdir(trialsRoot, { recursive: true, mode: 0o700 });
280
- await chmod(trialsRoot, 0o700);
281
- const trialName = `${safeName(cell.id)}-${randomBytes(6).toString('hex')}`;
282
- const configPath = join(trialsRoot, `${trialName}.json`);
283
- const trialPath = join(trialsRoot, trialName);
284
- const task = decodeTask(framework, options, cell);
285
- const timeoutMultiplier = positive(cell.budget.timeoutMultiplier, 'budget.timeoutMultiplier');
286
- const egressPaths = await resolveEgressPaths(options);
287
- const environmentConfig = resolveEnvironmentConfig(options, egressPaths, framework, trialPath);
288
- const networkPolicyPath = egressPaths?.networkPolicyPath;
289
- const executionEnvironment = {
290
- ...UNATTENDED_EXECUTION_ENVIRONMENT,
291
- ...egressExecutionEnvironment(options.egressProxy),
292
- };
293
- const environment = createHarnessPreparationEnvironment({
294
- subjectCredentialNames: [...subjectCredentialNames, ...cell.subject.credentials],
295
- declared: options.preparationEnvironment,
296
- ...(options.egressProxy && networkPolicyPath
297
- ? {
298
- egress: {
299
- allowedHost: options.egressProxy.allowedHost,
300
- networkPolicyPath,
301
- },
302
- }
303
- : {}),
304
- });
305
- const server = createServer();
306
- const connections = new Set();
307
- server.on('connection', (socket) => {
308
- connections.add(socket);
309
- socket.on('error', () => undefined);
310
- socket.once('close', () => connections.delete(socket));
311
- });
312
- let child;
313
- let serverClosed;
314
- const stopServer = (destroyConnections = false) => {
315
- serverClosed ??= closeServer(server);
316
- if (destroyConnections) {
317
- for (const socket of connections)
318
- socket.destroy();
319
- }
320
- return serverClosed;
321
- };
322
- let stage = 'spawn';
323
- try {
324
- server.listen(0, '127.0.0.1');
325
- await once(server, 'listening');
326
- const address = server.address();
327
- if (!address || typeof address === 'string')
328
- throw new Error('relay did not bind TCP');
329
- await writeFile(configPath, `${JSON.stringify({
330
- task,
331
- trial_name: trialName,
332
- trials_dir: trialsRoot,
333
- timeout_multiplier: timeoutMultiplier,
334
- agent: {
335
- import_path: 'relay_agent:RelayAgent',
336
- kwargs: {
337
- relay_host: '127.0.0.1',
338
- relay_port: address.port,
339
- relay_token: token,
340
- teardown_timeout_ms: PYTHON_TEARDOWN_DEADLINE_MS,
341
- },
342
- },
343
- environment: environmentConfig,
344
- ...(options.egressProxy
345
- ? {
346
- artifacts: [
347
- {
348
- source: '/opt/maka-egress-state/hits.jsonl',
349
- destination: EGRESS_AUDIT_DESTINATION,
350
- service: 'maka-eval-mitmproxy',
351
- },
352
- ],
353
- }
354
- : {}),
355
- })}\n`, { flag: 'wx', mode: 0o600 });
356
- child = spawn(process.env[options.pythonPathEnv], [
357
- join(BUNDLED_HARNESS_RELAY_ROOT, 'run_trial.py'),
358
- framework,
359
- options.frameworkVersion,
360
- configPath,
361
- ], { cwd: dirname(specPath), env: environment, stdio: 'ignore' });
362
- await once(child, 'spawn', signal ? { signal } : undefined);
363
- stage = 'exit-before-ready';
364
- const exitedBeforeReady = once(child, 'exit').then(([code]) => {
365
- throw new Error(`Trial exited before Agent.run (${code})`);
366
- });
367
- const connectionWait = new AbortController();
368
- const connectionSignal = signal
369
- ? AbortSignal.any([signal, connectionWait.signal])
370
- : connectionWait.signal;
371
- let socket;
372
- try {
373
- socket = await Promise.race([
374
- once(server, 'connection', { signal: connectionSignal }).then(([connected]) => connected),
375
- exitedBeforeReady,
376
- ]);
377
- }
378
- finally {
379
- connectionWait.abort();
380
- }
381
- const relayClosed = stopServer();
382
- stage = 'ready-decode';
383
- const transport = createRelayTransport(socket);
384
- transport.stage = 'receive';
385
- const lines = createInterface({ input: socket, crlfDelay: Number.POSITIVE_INFINITY })[Symbol.asyncIterator]();
386
- const ready = await abortable(Promise.race([readLine(lines), exitedBeforeReady]), signal);
387
- if (ready.token !== token ||
388
- ready.kind !== 'ready' ||
389
- typeof ready.instruction !== 'string' ||
390
- typeof ready.cwd !== 'string' ||
391
- !ready.cwd.startsWith('/')) {
392
- throw new Error('relay returned an invalid ready message');
393
- }
394
- return {
395
- kind: 'ready',
396
- state: {
397
- child,
398
- transport,
399
- closeRelay: async () => {
400
- for (const connection of connections)
401
- connection.destroy();
402
- await relayClosed;
403
- },
404
- lines,
405
- token,
406
- trialName,
407
- trialPath,
408
- taskInput: ready.instruction,
409
- credentials,
410
- cwd: ready.cwd,
411
- executionEnvironment,
412
- used: false,
413
- },
414
- };
415
- }
416
- catch (error) {
417
- const relayClosed = stopServer(true);
418
- if (child?.pid !== undefined) {
419
- await waitForTrial(child, {
420
- phase: 'unused',
421
- deadlineMs: TERM_SETTLEMENT_DEADLINE_MS,
422
- });
423
- }
424
- await relayClosed;
425
- await unlink(configPath).catch(() => undefined);
426
- await mkdir(trialPath, { recursive: true, mode: 0o700 });
427
- const diagnosticPath = 'preparation-error.json';
428
- const code = preparationCode(stage, child?.exitCode ?? null, signal);
429
- await writeFile(join(trialPath, diagnosticPath), `${JSON.stringify({
430
- stage,
431
- framework,
432
- code,
433
- errorCode: safeErrorCode(error),
434
- exitCode: child?.exitCode ?? null,
435
- signal: child?.signalCode ?? null,
436
- })}\n`, { flag: 'wx', mode: 0o600 });
437
- return notStarted(code, [
438
- { kind: 'executor-preparation', framework, trialName, path: diagnosticPath },
439
- ]);
440
- }
441
- }
442
- function createRelayTransport(socket) {
443
- const transport = { socket, stage: 'ready' };
444
- socket.on('error', (error) => {
445
- transport.failure ??= {
446
- stage: transport.stage,
447
- category: relayTransportCategory(error.code),
448
- delivery: 'unknown',
449
- };
450
- });
451
- return transport;
452
- }
453
- function sendRelayMessage(transport, stage, value, end = false) {
454
- transport.stage = stage;
455
- const socket = transport.socket;
456
- if (socket.destroyed || !socket.writable || socket.writableEnded || transport.failure) {
457
- transport.failure = {
458
- stage,
459
- category: transport.failure?.category ?? 'peer-closed',
460
- delivery: 'not-delivered',
461
- };
462
- return false;
463
- }
464
- try {
465
- const payload = `${JSON.stringify(value)}\n`;
466
- if (end)
467
- socket.end(payload);
468
- else {
469
- socket.write(payload, (error) => {
470
- if (!error)
471
- return;
472
- transport.failure = {
473
- stage,
474
- category: relayTransportCategory(error.code),
475
- delivery: 'unknown',
476
- };
477
- });
478
- }
479
- return true;
480
- }
481
- catch (error) {
482
- transport.failure = {
483
- stage,
484
- category: relayTransportCategory(error.code),
485
- delivery: 'not-delivered',
486
- };
487
- return false;
488
- }
489
- }
490
- function relayTransportCategory(code) {
491
- if (code === 'EPIPE')
492
- return 'broken-pipe';
493
- if (code === 'ECONNRESET')
494
- return 'connection-reset';
495
- if (code === 'ERR_STREAM_WRITE_AFTER_END')
496
- return 'peer-closed';
497
- return 'transport-error';
498
- }
499
- function notStarted(code, artifacts = []) {
500
- return { kind: 'not_started', code, artifacts };
501
- }
502
- function preparationCode(stage, exitCode, signal) {
503
- if (signal?.aborted)
504
- return 'cancelled';
505
- if (stage === 'exit-before-ready' && exitCode === 78)
506
- return 'framework-version-mismatch';
507
- if (stage === 'spawn')
508
- return 'spawn-failed';
509
- if (stage === 'ready-decode')
510
- return 'invalid-ready';
511
- return 'exit-before-ready';
512
- }
513
- function mergeExecutionEnvironment(required, subject) {
514
- const overlap = Object.keys(required).filter((name) => Object.hasOwn(subject, name));
515
- if (overlap.length > 0) {
516
- throw new Error(`subject environment overrides Eval execution environment: ${overlap.join(', ')}`);
517
- }
518
- return { ...subject, ...required };
519
- }
520
- // Every subject runs unattended in a fresh container, where a package manager
521
- // that stops to ask a question is indistinguishable from one that hung. That is
522
- // a property of the environment, not of any one arm: tasks install packages, and
523
- // subjects only decide when.
524
- const UNATTENDED_EXECUTION_ENVIRONMENT = {
525
- DEBIAN_FRONTEND: 'noninteractive',
526
- TZ: 'Etc/UTC',
527
- };
528
- function egressExecutionEnvironment(options) {
529
- if (!options)
530
- return {};
531
- const noProxy = '127.0.0.1,localhost';
532
- return {
533
- HTTP_PROXY: options.proxyUrl,
534
- HTTPS_PROXY: options.proxyUrl,
535
- http_proxy: options.proxyUrl,
536
- https_proxy: options.proxyUrl,
537
- NO_PROXY: noProxy,
538
- no_proxy: noProxy,
539
- SSL_CERT_FILE: options.containerCaPath,
540
- REQUESTS_CA_BUNDLE: options.containerCaPath,
541
- CURL_CA_BUNDLE: options.containerCaPath,
542
- GIT_SSL_CAINFO: options.containerCaPath,
543
- NODE_EXTRA_CA_CERTS: options.containerCaPath,
544
- };
545
- }
546
- function safeErrorCode(error) {
547
- const code = error instanceof Error ? error.code : undefined;
548
- return code && ['ENOENT', 'EACCES', 'EPERM'].includes(code) ? code : null;
549
- }
550
- async function closeServer(server) {
551
- if (!server.listening)
552
- return;
553
- await new Promise((resolveClose, rejectClose) => {
554
- server.close((error) => (error ? rejectClose(error) : resolveClose()));
555
- });
556
- }
557
- export const EGRESS_AUDIT_DESTINATION = 'egress-hits.jsonl';
558
- export const EGRESS_AUDIT_ARTIFACT_PATH = `artifacts/${EGRESS_AUDIT_DESTINATION}`;
559
- export function collectEgressAuditArtifact(audit, expected) {
560
- if (!expected)
561
- return { missing: false, failureReason: null, artifacts: [] };
562
- if (audit === undefined) {
563
- return {
564
- missing: true,
565
- failureReason: 'egress audit log missing',
566
- artifacts: [{ kind: 'egress-audit-missing', path: EGRESS_AUDIT_ARTIFACT_PATH }],
567
- };
568
- }
569
- const forensics = inspectEgressAudit(audit);
570
- return {
571
- missing: false,
572
- failureReason: null,
573
- artifacts: [
574
- {
575
- kind: 'egress-audit',
576
- path: EGRESS_AUDIT_ARTIFACT_PATH,
577
- bytes: audit.byteLength,
578
- sha256: `sha256:${createHash('sha256').update(audit).digest('hex')}`,
579
- truncated: forensics.truncated,
580
- policyErrorCount: forensics.policyErrorCount,
581
- malformedLineCount: forensics.malformedLineCount,
582
- },
583
- ],
584
- };
585
- }
586
- function inspectEgressAudit(audit) {
587
- let truncated = false;
588
- let policyErrorCount = 0;
589
- let malformedLineCount = 0;
590
- for (const line of audit.toString('utf8').split(/\r?\n/)) {
591
- const trimmed = line.trim();
592
- if (!trimmed)
593
- continue;
594
- let record;
595
- try {
596
- record = JSON.parse(trimmed);
597
- }
598
- catch {
599
- malformedLineCount += 1;
600
- continue;
601
- }
602
- if (!record || typeof record !== 'object' || Array.isArray(record)) {
603
- malformedLineCount += 1;
604
- continue;
605
- }
606
- const ruleId = record.ruleId;
607
- if (ruleId === 'audit_truncated')
608
- truncated = true;
609
- if (ruleId === 'policy_error')
610
- policyErrorCount += 1;
611
- }
612
- return { truncated, policyErrorCount, malformedLineCount };
613
- }
614
- async function readVerification(state, cell, framework, expectEgressAudit) {
615
- const result = JSON.parse(await readFile(join(state.trialPath, 'result.json'), 'utf8'));
616
- const score = result.verifier_result?.rewards?.[rewardKey(cell)] ?? null;
617
- const subjectException = ['AgentTimeoutError', 'NonZeroAgentExitCodeError'].includes(String(result.exception_info?.exception_type));
618
- if (result.exception_info && !subjectException) {
619
- throw new Error('Trial failed outside subject execution');
620
- }
621
- const egressAuditPath = join(state.trialPath, EGRESS_AUDIT_ARTIFACT_PATH);
622
- let egressAudit;
623
- try {
624
- egressAudit = await readFile(egressAuditPath);
625
- }
626
- catch (error) {
627
- const code = error.code;
628
- if (expectEgressAudit && code !== 'ENOENT') {
629
- return {
630
- status: 'infra_failed',
631
- score,
632
- failureReason: `failed to read egress audit log ${egressAuditPath}${code ? ` (${code})` : ''}`,
633
- artifacts: [
634
- { kind: 'trial', framework: cell.executor.kind, trialName: state.trialName },
635
- ...(await collectedArtifactInventory(state.trialPath, framework)),
636
- { kind: 'egress-audit-unreadable', path: EGRESS_AUDIT_ARTIFACT_PATH },
637
- ],
638
- };
639
- }
640
- }
641
- const audit = collectEgressAuditArtifact(egressAudit, expectEgressAudit);
642
- return {
643
- status: audit.failureReason
644
- ? 'infra_failed'
645
- : score === null
646
- ? 'infra_failed'
647
- : subjectException
648
- ? 'subject_failed'
649
- : 'completed',
650
- score,
651
- failureReason: audit.failureReason ?? (score === null ? 'verifier produced no reward' : null),
652
- artifacts: [
653
- { kind: 'trial', framework: cell.executor.kind, trialName: state.trialName },
654
- ...(await collectedArtifactInventory(state.trialPath, framework)),
655
- ...audit.artifacts,
656
- ],
657
- };
658
- }
659
- async function collectedArtifactInventory(trialPath, framework) {
660
- const root = framework === 'pier'
661
- ? join(trialPath, 'artifacts')
662
- : join(trialPath, 'artifacts', 'logs', 'artifacts');
663
- const files = [];
664
- const targets = [
665
- join(root, basename(MAKA_RUNTIME_ARTIFACT_PATH)),
666
- join(root, basename(MAKA_SUBJECT_STDOUT_PATH)),
667
- join(root, basename(MAKA_SUBJECT_STDERR_PATH)),
668
- ];
669
- for (const target of targets) {
670
- await walkCollectedArtifacts(trialPath, target, files).catch((error) => {
671
- if (error.code !== 'ENOENT')
672
- throw error;
673
- });
674
- }
675
- return files.sort((left, right) => String(left.path).localeCompare(String(right.path)));
676
- }
677
- async function walkCollectedArtifacts(trialPath, current, files) {
678
- const metadata = await lstat(current);
679
- if (metadata.isSymbolicLink())
680
- return;
681
- if (metadata.isFile()) {
682
- const hash = createHash('sha256');
683
- for await (const chunk of createReadStream(current))
684
- hash.update(chunk);
685
- files.push({
686
- kind: 'collected-artifact',
687
- path: relative(trialPath, current).split(sep).join('/'),
688
- bytes: metadata.size,
689
- sha256: `sha256:${hash.digest('hex')}`,
690
- });
691
- return;
692
- }
693
- if (!metadata.isDirectory())
694
- return;
695
- for (const entry of await readdir(current, { withFileTypes: true })) {
696
- const path = join(current, entry.name);
697
- if (entry.isSymbolicLink())
698
- continue;
699
- await walkCollectedArtifacts(trialPath, path, files);
700
- }
701
- }
702
- function decodeHarnessOptions(value, framework) {
703
- if (!Object.hasOwn(value, 'preparationEnvironment')) {
704
- throw new Error('executor.config.preparationEnvironment is required');
705
- }
706
- const fields = [
707
- 'frameworkVersion',
708
- 'pythonPathEnv',
709
- 'trialsRootEnv',
710
- 'environment',
711
- 'preparationEnvironment',
712
- 'mounts',
713
- ];
714
- // Only the Harbor branch of run_trial.py applies the namespace policy, so a
715
- // pier spec declaring egressProxy would set the proxy up and inject its
716
- // environment while enforcement silently did not exist.
717
- if (framework === 'pier' && Object.hasOwn(value, 'egressProxy')) {
718
- throw new Error('executor.config.egressProxy is Harbor-only: pier does not apply the subject namespace policy');
719
- }
720
- if (Object.hasOwn(value, 'egressProxy'))
721
- fields.push('egressProxy');
722
- if (framework === 'pier')
723
- fields.push('tasksRootEnv');
724
- const options = exact(value, fields, 'executor.config');
725
- const preparationEnvironment = array(options.preparationEnvironment, 'preparationEnvironment').map((name, index) => machinePathEnv(name, `preparationEnvironment[${index}]`));
726
- if (new Set(preparationEnvironment).size !== preparationEnvironment.length) {
727
- throw new Error('preparationEnvironment must contain unique names');
728
- }
729
- const decoded = {
730
- frameworkVersion: text(options.frameworkVersion, 'frameworkVersion'),
731
- pythonPathEnv: machinePathEnv(options.pythonPathEnv, 'pythonPathEnv'),
732
- trialsRootEnv: machinePathEnv(options.trialsRootEnv, 'trialsRootEnv'),
733
- environment: decodeJsonObject(options.environment, 'environment'),
734
- preparationEnvironment,
735
- ...(Object.hasOwn(options, 'egressProxy')
736
- ? { egressProxy: decodeEgressProxy(options.egressProxy) }
737
- : {}),
738
- mounts: array(options.mounts, 'mounts').map((mount, index) => decodeMount(mount, index)),
739
- ...(framework === 'pier'
740
- ? { tasksRootEnv: machinePathEnv(options.tasksRootEnv, 'tasksRootEnv') }
741
- : {}),
742
- };
743
- if (framework === 'pier') {
744
- const reservedTargets = PIER_FRAMEWORK_LOG_MOUNTS.map((mount) => mount.target);
745
- const collision = decoded.mounts.find((mount) => {
746
- const target = posix.normalize(mount.target);
747
- return reservedTargets.some((reserved) => target === reserved || target.startsWith(`${reserved}/`));
748
- });
749
- if (collision) {
750
- throw new Error(`Pier mount target ${collision.target} is reserved for framework logs`);
751
- }
752
- }
753
- for (const name of [
754
- decoded.pythonPathEnv,
755
- decoded.trialsRootEnv,
756
- decoded.tasksRootEnv,
757
- decoded.egressProxy?.composeSourceEnv,
758
- ]) {
759
- if (name && !process.env[name])
760
- throw new Error(`machine path ${name} is unavailable`);
761
- }
762
- return decoded;
763
- }
764
- function decodeEgressProxy(value) {
765
- const proxy = exact(value, [
766
- 'composeSourceEnv',
767
- 'composeRelativePath',
768
- 'networkPolicyRelativePath',
769
- 'proxyUrl',
770
- 'allowedHost',
771
- 'containerCaPath',
772
- ], 'egressProxy');
773
- const proxyUrl = text(proxy.proxyUrl, 'egressProxy.proxyUrl');
774
- if (!URL.canParse(proxyUrl) || new URL(proxyUrl).protocol !== 'http:') {
775
- throw new Error('egressProxy.proxyUrl must be an HTTP proxy URL');
776
- }
777
- return {
778
- composeSourceEnv: machinePathEnv(proxy.composeSourceEnv, 'egressProxy.composeSourceEnv'),
779
- composeRelativePath: relativePath(proxy.composeRelativePath, 'egressProxy.composeRelativePath'),
780
- networkPolicyRelativePath: relativePath(proxy.networkPolicyRelativePath, 'egressProxy.networkPolicyRelativePath'),
781
- proxyUrl,
782
- allowedHost: hostName(proxy.allowedHost, 'egressProxy.allowedHost'),
783
- containerCaPath: absolute(proxy.containerCaPath, 'egressProxy.containerCaPath'),
784
- };
785
- }
786
- function decodeMount(value, index) {
787
- const mount = exact(value, ['sourceEnv', 'target', 'readOnly'], `mounts[${index}]`);
788
- const sourceEnv = machinePathEnv(mount.sourceEnv, `mounts[${index}].sourceEnv`);
789
- if (!process.env[sourceEnv])
790
- throw new Error(`machine path ${sourceEnv} is unavailable`);
791
- if (mount.readOnly !== true)
792
- throw new Error(`mounts[${index}] must be read-only`);
793
- return {
794
- sourceEnv,
795
- target: absolute(mount.target, `mounts[${index}].target`),
796
- readOnly: true,
797
- };
798
- }
799
- function resolveMounts(mounts) {
800
- return mounts.map((mount) => ({
801
- type: 'bind',
802
- source: resolve(process.env[mount.sourceEnv]),
803
- target: mount.target,
804
- read_only: true,
805
- }));
806
- }
807
- function resolveEnvironmentConfig(options, egressPaths, framework, trialPath) {
808
- const configuredMounts = resolveMounts(options.mounts);
809
- const mounts = framework === 'pier'
810
- ? [
811
- ...configuredMounts,
812
- ...PIER_FRAMEWORK_LOG_MOUNTS.map(({ directory, target }) => ({
813
- type: 'bind',
814
- source: join(trialPath, directory),
815
- target,
816
- })),
817
- ]
818
- : configuredMounts;
819
- const base = { ...options.environment, mounts };
820
- if (!options.egressProxy)
821
- return base;
822
- if (!egressPaths)
823
- throw new Error('egress proxy paths are unavailable');
824
- return { ...base, extra_docker_compose: [egressPaths.composePath] };
825
- }
826
- async function resolveEgressPaths(options) {
827
- if (!options.egressProxy)
828
- return undefined;
829
- const source = resolve(process.env[options.egressProxy.composeSourceEnv]);
830
- const [composePath, networkPolicyPath] = await Promise.all([
831
- resolveRealPathWithinRoot(source, options.egressProxy.composeRelativePath, 'egress proxy compose path'),
832
- resolveRealPathWithinRoot(source, options.egressProxy.networkPolicyRelativePath, 'egress network policy path'),
833
- ]);
834
- return { composePath, networkPolicyPath };
835
- }
836
- function decodeTask(framework, options, cell) {
837
- if (framework === 'harbor') {
838
- const benchmark = exact(cell.benchmark.config, ['repository'], 'benchmark.config');
839
- const task = exact(cell.task.config, ['harbor'], 'task.config');
840
- const harbor = exact(task.harbor, ['path'], 'task.config.harbor');
841
- const revision = text(cell.benchmark.version, 'benchmark.version');
842
- if (!/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/iu.test(revision)) {
843
- throw new Error('Harbor benchmark.version must be a complete Git commit');
844
- }
845
- return {
846
- path: text(harbor.path, 'task.config.harbor.path'),
847
- git_url: text(benchmark.repository, 'benchmark.config.repository'),
848
- git_commit_id: revision,
849
- };
850
- }
851
- const task = exact(cell.task.config, ['pier'], 'task.config');
852
- const pier = exact(task.pier, ['path'], 'task.config.pier');
853
- const root = resolve(process.env[options.tasksRootEnv]);
854
- const path = resolve(root, text(pier.path, 'task.config.pier.path'));
855
- const fromRoot = relative(root, path);
856
- if (fromRoot === '..' || fromRoot.startsWith(`..${sep}`))
857
- throw new Error('Pier task escapes tasksRoot');
858
- return { path };
859
- }
860
- function rewardKey(cell) {
861
- return text(exact(cell.verifier, ['reward'], 'verifier').reward, 'verifier.reward');
862
- }
863
- function requireCredentials(names) {
864
- return Object.freeze(Object.fromEntries(names.map((name) => {
865
- if (!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(name))
866
- throw new Error(`invalid credential name ${name}`);
867
- const value = process.env[name];
868
- if (!value)
869
- throw new Error(`subject credential ${name} is required`);
870
- return [name, value];
871
- })));
872
- }
873
- async function readLine(lines) {
874
- const line = await lines.next();
875
- if (line.done)
876
- throw new Error('relay closed before settlement');
877
- return JSON.parse(line.value);
878
- }
879
- async function abortable(operation, signal) {
880
- if (!signal)
881
- return operation;
882
- signal.throwIfAborted();
883
- return new Promise((resolveOperation, rejectOperation) => {
884
- let settled = false;
885
- const settle = (callback) => {
886
- if (settled)
887
- return;
888
- settled = true;
889
- signal.removeEventListener('abort', onAbort);
890
- callback();
891
- };
892
- const onAbort = () => settle(() => rejectOperation(signal.reason));
893
- signal.addEventListener('abort', onAbort, { once: true });
894
- void operation.then((value) => settle(() => resolveOperation(value)), (error) => settle(() => rejectOperation(error)));
895
- });
896
- }
897
- const RELAY_SETTLEMENT_DEADLINE_MS = 120_000;
898
- const PYTHON_TEARDOWN_DEADLINE_MS = 110_000;
899
- const TERM_SETTLEMENT_DEADLINE_MS = 20_000;
900
- const KILL_SETTLEMENT_DEADLINE_MS = 5_000;
901
- async function waitForTrial(child, wait) {
902
- const exit = child.exitCode !== null || child.signalCode !== null
903
- ? Promise.resolve({ code: child.exitCode, signal: child.signalCode })
904
- : once(child, 'exit').then(([code, childSignal]) => ({ code, signal: childSignal }));
905
- if (wait.phase === 'completion') {
906
- const completed = await exit;
907
- return {
908
- phase: wait.phase,
909
- deadlineMs: null,
910
- escalation: 'none',
911
- outcome: completed.code === 0 && completed.signal === null ? 'completed' : 'failed',
912
- };
913
- }
914
- child.kill('SIGTERM');
915
- const terminated = await within(exit, wait.deadlineMs);
916
- if (terminated) {
917
- return {
918
- phase: wait.phase,
919
- deadlineMs: wait.deadlineMs,
920
- escalation: 'term',
921
- outcome: terminated.code === 0 && terminated.signal === null ? 'confirmed' : 'terminated',
922
- };
923
- }
924
- child.kill('SIGKILL');
925
- const killed = await within(exit, KILL_SETTLEMENT_DEADLINE_MS);
926
- if (killed) {
927
- return {
928
- phase: wait.phase,
929
- deadlineMs: wait.deadlineMs,
930
- escalation: 'kill',
931
- outcome: 'killed',
932
- };
933
- }
934
- child.unref();
935
- return {
936
- phase: wait.phase,
937
- deadlineMs: wait.deadlineMs,
938
- escalation: 'detached',
939
- outcome: 'unsettled',
940
- };
941
- }
942
- function finalizationConfirmed(evidence) {
943
- return evidence.outcome === 'completed' || evidence.outcome === 'confirmed';
944
- }
945
- async function within(operation, timeoutMs) {
946
- let timer;
947
- try {
948
- return await Promise.race([
949
- operation,
950
- new Promise((resolve) => {
951
- timer = setTimeout(resolve, timeoutMs);
952
- }),
953
- ]);
954
- }
955
- finally {
956
- if (timer)
957
- clearTimeout(timer);
958
- }
959
- }
960
- function exact(value, fields, where) {
961
- if (!value || typeof value !== 'object' || Array.isArray(value))
962
- throw new Error(`${where} must be an object`);
963
- const record = value;
964
- if (fields.some((field) => !Object.hasOwn(record, field)) ||
965
- Object.keys(record).some((field) => !fields.includes(field))) {
966
- throw new Error(`${where} fields are invalid`);
967
- }
968
- return record;
969
- }
970
- function array(value, where) {
971
- if (!Array.isArray(value))
972
- throw new Error(`${where} must be an array`);
973
- return value;
974
- }
975
- function text(value, where) {
976
- if (typeof value !== 'string' || value.length === 0)
977
- throw new Error(`${where} is required`);
978
- return value;
979
- }
980
- function machinePathEnv(value, where) {
981
- const name = text(value, where);
982
- if (!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(name))
983
- throw new Error(`${where} is invalid`);
984
- return name;
985
- }
986
- function relativePath(value, where) {
987
- const path = text(value, where);
988
- if (path.startsWith('/') || path === '..' || path.startsWith(`..${sep}`)) {
989
- throw new Error(`${where} must stay within its source root`);
990
- }
991
- return path;
992
- }
993
- function hostName(value, where) {
994
- const host = text(value, where).toLowerCase();
995
- if (!/^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/u.test(host)) {
996
- throw new Error(`${where} must be a hostname`);
997
- }
998
- return host;
999
- }
1000
- function absolute(value, where) {
1001
- const path = text(value, where);
1002
- if (!path.startsWith('/'))
1003
- throw new Error(`${where} must be absolute`);
1004
- return path;
1005
- }
1006
- function positive(value, where) {
1007
- if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0)
1008
- throw new Error(`${where} must be positive`);
1009
- return value;
1010
- }
1011
- function safeName(value) {
1012
- return value.replaceAll(/[^A-Za-z0-9._-]/gu, '-').slice(0, 80);
1013
- }