@lesliechan721/agent-skill-eval 0.1.0-beta.37 → 0.1.0-beta.39

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.
@@ -5,11 +5,13 @@ import fs from 'fs-extra';
5
5
  import { CodexEvalAttemptControl, EvalAppServerSession, extractAppServerThreadId, assertHostReadContainmentAvailable, hostReadContainmentProfile, } from './codex-eval-app-server.js';
6
6
  import { applyCodexEvalProviderEnvironment, createCodexEvalTemporaryRoot, materializeCodexEvalRuntimeHome, redactCodexEvalValue, } from './codex-eval-runtime-config.js';
7
7
  import { applyCodexEvalGitIsolation } from './codex-eval-isolation.js';
8
+ import { parseInstructionV2ReasoningEffort, parseInstructionV2Receipt, parseInstructionV2Transport, } from './instruction-v2.js';
8
9
  import { runProviderProcess } from './process.js';
9
10
  import { assertProviderRunPaths } from './run-paths.js';
10
11
  import { parseEvalProviderGrade, } from './custom-process.js';
11
12
  import { createCodexGrader, parseConversationGraderOutput } from './providers.js';
12
13
  import { parseWorkflowEvaluation, normalizeWorkflowChecks, WorkflowEvaluation, } from './codex-workflow-evaluation.js';
14
+ import { runWorkflowFixtureAction, validateWorkflowFixtureActions } from './codex-workflow-fixtures.js';
13
15
  const MAX_ADAPTER_OUTPUT_BYTES = 4 * 1024 * 1024;
14
16
  const MAX_REQUEST_TEXT_BYTES = 200_000;
15
17
  const MAX_ROLE_COUNT = 32;
@@ -70,10 +72,10 @@ async function assertWorkspaceRoleFile(workspace, relative) {
70
72
  throw new Error(`workflow role configFile must be a regular file: ${relative}`);
71
73
  }
72
74
  }
73
- export async function parseCodexWorkflowRequest(value, allowedModels, workspace) {
75
+ export async function parseCodexWorkflowRequest(value, allowedModels, workspace, fixtures) {
74
76
  if (!isRecord(value))
75
77
  throw new Error('workflow prepare stdout must be one JSON object.');
76
- exactKeys(value, ['version', 'prompt', 'sandbox', 'model', 'developerInstructions', 'roles', 'multiAgent'], ['evaluation'], 'workflow prepare stdout');
78
+ exactKeys(value, ['version', 'prompt', 'sandbox', 'model', 'developerInstructions', 'roles', 'multiAgent'], ['evaluation', 'transport', 'reasoningEffort'], 'workflow prepare stdout');
77
79
  if (value.version !== 1)
78
80
  throw new Error('workflow prepare stdout version must be 1.');
79
81
  if (value.sandbox !== 'read-only' && value.sandbox !== 'workspace-write') {
@@ -121,14 +123,16 @@ export async function parseCodexWorkflowRequest(value, allowedModels, workspace)
121
123
  if (multiAgent.protocol === 'v1'
122
124
  && (multiAgent.hideSpawnAgentMetadata || multiAgent.exposeSpawnAgentModelOverrides))
123
125
  throw new Error('workflow request V1 metadata fields must be false.');
124
- return {
126
+ const request = {
125
127
  version: 1,
126
128
  prompt: boundedString(value.prompt, 'workflow request prompt', MAX_REQUEST_TEXT_BYTES),
127
129
  sandbox: value.sandbox,
128
130
  model: value.model,
131
+ ...(value.reasoningEffort === undefined ? {} : { reasoningEffort: parseInstructionV2ReasoningEffort(value.reasoningEffort) }),
129
132
  developerInstructions: boundedString(value.developerInstructions, 'workflow request developerInstructions', MAX_REQUEST_TEXT_BYTES),
130
133
  roles,
131
134
  ...(value.evaluation === undefined ? {} : { evaluation: parseWorkflowEvaluation(value.evaluation) }),
135
+ ...(value.transport === undefined ? {} : { transport: await parseInstructionV2Transport(value.transport, workspace) }),
132
136
  multiAgent: {
133
137
  protocol: multiAgent.protocol,
134
138
  maxDepth: multiAgent.maxDepth,
@@ -136,6 +140,28 @@ export async function parseCodexWorkflowRequest(value, allowedModels, workspace)
136
140
  exposeSpawnAgentModelOverrides: multiAgent.exposeSpawnAgentModelOverrides,
137
141
  },
138
142
  };
143
+ if (request.transport) {
144
+ const external = request.transport.request;
145
+ if (external.instructions.text !== request.developerInstructions || external.task !== request.prompt
146
+ || external.run.access !== request.sandbox || (external.run.model ?? null) !== request.model
147
+ || external.run.reasoningEffort !== request.reasoningEffort) {
148
+ throw new Error('instruction-v2 request must match workflow instructions, task, access, model, and reasoningEffort.');
149
+ }
150
+ if (request.evaluation?.interactions.length || request.evaluation?.setupPrompt !== undefined) {
151
+ throw new Error('instruction-v2 transport does not support evaluation interactions or setupPrompt.');
152
+ }
153
+ }
154
+ const actions = [
155
+ ...(request.evaluation?.setupActions ?? []),
156
+ ...(request.evaluation?.finalActions ?? []),
157
+ ...(request.evaluation?.interactions.flatMap((interaction) => interaction.action ? [interaction.action] : []) ?? []),
158
+ ];
159
+ if (actions.length) {
160
+ if (!fixtures)
161
+ throw new Error('workflow fixture actions require the Eval-owned fixture root.');
162
+ await validateWorkflowFixtureActions(actions, fixtures);
163
+ }
164
+ return request;
139
165
  }
140
166
  function parseJsonObject(stdout, label) {
141
167
  let value;
@@ -295,6 +321,7 @@ function workflowConfig(request, workspace) {
295
321
  `features.multi_agent_v2.expose_spawn_agent_model_overrides=${String(request.multiAgent.exposeSpawnAgentModelOverrides)}`,
296
322
  `agents.max_depth=${request.multiAgent.maxDepth}`,
297
323
  `developer_instructions=${configValue(request.developerInstructions)}`,
324
+ ...(request.reasoningEffort ? [`model_reasoning_effort=${configValue(request.reasoningEffort)}`] : []),
298
325
  ...request.roles.flatMap((role) => [
299
326
  `agents.${role.name}.description=${configValue(role.description)}`,
300
327
  `agents.${role.name}.config_file=${configValue(path.join(workspace, role.configFile))}`,
@@ -1456,7 +1483,7 @@ export async function normalizeCodexWorkflowRollouts(options) {
1456
1483
  leafCalls: collaborationCalls.filter((call) => call.threadId !== options.rootThreadId
1457
1484
  || (call.linkedThreadId !== undefined && descendants.has(call.linkedThreadId))),
1458
1485
  skillLoads,
1459
- trace: selected.flatMap((record) => record.trace.map((event) => sanitizeTraceValue(event, options.runtime, [
1486
+ trace: selected.flatMap((record) => record.trace.map((event) => sanitizeTraceValue(isRecord(event) ? { ...event, threadId: record.threadId, parentThreadId: record.parentThreadId } : event, options.runtime, [
1460
1487
  ...isolatedPaths,
1461
1488
  ...selected.flatMap((entry) => entry.agentPath
1462
1489
  ? [{ path: entry.agentPath, marker: '[agent-path]' }]
@@ -1478,6 +1505,203 @@ function sandboxPolicy(request) {
1478
1505
  excludeSlashTmp: false,
1479
1506
  };
1480
1507
  }
1508
+ function workflowFixtureRunner(input, control) {
1509
+ return (action) => runWorkflowFixtureAction({
1510
+ action, workspace: input.input.workspacePath, fixtures: input.input.fixturesPath,
1511
+ output: input.input.outputDir, evalRoot: input.evalRoot, sourceEnv: input.sourceEnv, runtime: input.codexRuntime,
1512
+ timeoutMs: control.remaining('workflow fixture action'), signal: control.signal,
1513
+ processManagement: input.processManagement,
1514
+ ...(input.processTree ? { processTree: input.processTree } : {}),
1515
+ ...(input.protectedEvalRoots ? { protectedEvalRoots: input.protectedEvalRoots } : {}),
1516
+ });
1517
+ }
1518
+ async function readInstructionV2File(root, relative, limit) {
1519
+ relativeWorkspacePath(relative, 'instruction-v2 capture path');
1520
+ let target = root;
1521
+ for (const part of relative.split('/')) {
1522
+ target = path.join(target, part);
1523
+ if ((await fs.lstat(target)).isSymbolicLink())
1524
+ throw new Error('instruction-v2 capture must not contain symbolic links.');
1525
+ }
1526
+ const stat = await fs.lstat(target);
1527
+ if (!stat.isFile() || stat.size > limit)
1528
+ throw new Error('instruction-v2 capture is not a bounded regular file.');
1529
+ return fs.readFile(target, 'utf8');
1530
+ }
1531
+ const instructionV2Transport = {
1532
+ async run(input) {
1533
+ const selected = input.request.transport;
1534
+ if (!path.isAbsolute(input.codexBin))
1535
+ throw new Error('instruction-v2 requires the Engine-resolved absolute Codex executable.');
1536
+ const control = new CodexEvalAttemptControl({
1537
+ label: 'instruction-v2 workflow', timeoutMs: input.timeoutMs,
1538
+ ...(input.signal ? { signal: input.signal } : {}),
1539
+ });
1540
+ const temporaryRoot = await createCodexEvalTemporaryRoot('instruction-v2-workflow-');
1541
+ const runtimeRoot = await fs.realpath(temporaryRoot.root);
1542
+ const codexHome = path.join(runtimeRoot, 'codex-home');
1543
+ const runDirectory = path.join(runtimeRoot, 'run');
1544
+ const env = {
1545
+ ...buildEngineEnvironment(input.sourceEnv, runtimeRoot, codexHome, input.codexRuntime),
1546
+ AW_SUBAGENT_RUN_DIR: runDirectory,
1547
+ AW_SUBAGENT_RESULT_FILE: path.join(runDirectory, 'result.json'),
1548
+ AW_SUBAGENT_TRACE_FILE: path.join(runDirectory, 'codex-events.jsonl'),
1549
+ AW_SUBAGENT_CODEX_BINARY: input.codexBin,
1550
+ };
1551
+ const startedAt = Date.now();
1552
+ let cleaned = false;
1553
+ let judgeNumber = 0;
1554
+ const judgeUsage = [];
1555
+ const grader = input.request.evaluation ? createCodexGrader({
1556
+ sourceEnv: input.sourceEnv, codexRuntime: input.codexRuntime, codexBin: input.codexBin,
1557
+ ...(input.request.model ? { model: input.request.model } : {}), processManagement: input.processManagement,
1558
+ }) : undefined;
1559
+ const evaluation = input.request.evaluation ? new WorkflowEvaluation(input.request.evaluation, input.input.workspacePath, async (prompt) => {
1560
+ const outputDir = path.join(input.input.outputDir, 'workflow-judges', String(++judgeNumber));
1561
+ await fs.ensureDir(outputDir);
1562
+ const result = await grader({ prompt, outputDir, timeoutMs: control.remaining('external semantic check'), signal: control.signal });
1563
+ if (!result.mechanicalPassed)
1564
+ throw new Error('external workflow semantic grader did not complete its read-only checks.');
1565
+ judgeUsage.push(result.usage);
1566
+ return parseConversationGraderOutput(result.output);
1567
+ }, workflowFixtureRunner(input, control)) : undefined;
1568
+ try {
1569
+ await Promise.all([
1570
+ fs.ensureDir(env.HOME), fs.ensureDir(env.TMPDIR), fs.mkdir(runDirectory, { mode: 0o700 }),
1571
+ materializeCodexEvalRuntimeHome(input.codexRuntime, codexHome, { trustedProject: input.input.workspacePath }),
1572
+ ]);
1573
+ await evaluation?.runSetupActions();
1574
+ const before = await snapshotWorkspace(input.input.workspacePath);
1575
+ let containment;
1576
+ if (input.request.evaluation) {
1577
+ containment = hostReadContainmentProfile({
1578
+ forbiddenHostRoots: [input.input.fixturesPath, input.input.outputDir],
1579
+ writableRoots: [runtimeRoot, input.input.workspacePath],
1580
+ unwritableRoots: input.request.sandbox === 'read-only' ? [input.input.workspacePath] : [],
1581
+ }) + '\n' + [...new Set([input.evalRoot, ...(input.protectedEvalRoots ?? [])])].map((root) => `(deny file-read* (require-all (subpath ${JSON.stringify(root)}) (require-not (subpath ${JSON.stringify(input.input.workspacePath)}))))`).join('\n');
1582
+ for (let ancestor = path.dirname(input.input.workspacePath); ancestor !== path.dirname(ancestor); ancestor = path.dirname(ancestor)) {
1583
+ containment += `\n(allow file-read-metadata (literal ${JSON.stringify(ancestor)}))`;
1584
+ }
1585
+ await assertHostReadContainmentAvailable(containment);
1586
+ }
1587
+ evaluation?.observe({ method: 'evaluation/externalSubmission', runId: selected.request.run.id }, null);
1588
+ const processResult = await runProviderProcess({
1589
+ executable: containment ? '/usr/bin/sandbox-exec' : selected.command[0],
1590
+ args: containment ? ['-p', containment, ...selected.command] : [...selected.command.slice(1)],
1591
+ cwd: input.input.workspacePath, env, stdin: JSON.stringify(selected.request),
1592
+ timeoutMs: control.remaining('external execution'), maxBufferBytes: MAX_ADAPTER_OUTPUT_BYTES,
1593
+ signal: control.signal,
1594
+ }, {}, {
1595
+ containment: input.processManagement.mode === 'unmanaged-confirmed' ? 'unmanaged-confirmed' : 'verified-observed',
1596
+ ...(input.processManagement.mode === 'unmanaged-confirmed' ? {} : { processTree: input.processTree }),
1597
+ });
1598
+ let receipt = null;
1599
+ let diagnostic = null;
1600
+ try {
1601
+ receipt = parseInstructionV2Receipt(JSON.parse(await readInstructionV2File(runDirectory, 'result.json', 64 * 1024)));
1602
+ }
1603
+ catch (error) {
1604
+ diagnostic = `External result receipt unavailable: ${error.message}`;
1605
+ }
1606
+ const trace = [];
1607
+ if (receipt?.traceFile) {
1608
+ try {
1609
+ const raw = await readInstructionV2File(runDirectory, receipt.traceFile, 8 * 1024 * 1024);
1610
+ const lines = raw.split(/\r?\n/u).filter((line) => line.trim());
1611
+ if (lines.length > 5_000)
1612
+ throw new Error('instruction-v2 trace event limit exceeded.');
1613
+ for (const line of lines)
1614
+ trace.push(JSON.parse(line));
1615
+ }
1616
+ catch (error) {
1617
+ diagnostic ??= `External trace unavailable: ${error.message}`;
1618
+ trace.length = 0;
1619
+ }
1620
+ }
1621
+ const completed = receipt?.outcome === 'completed' && receipt.providerTerminal === 'turn.completed'
1622
+ && receipt.instructionChannel !== 'user-prefix'
1623
+ && receipt.instructionsRevision === selected.request.instructions.revision
1624
+ && processResult.exitCode === 0 && processResult.signal === null
1625
+ && !processResult.timedOut && !processResult.cancelled && !processResult.maxBufferExceeded
1626
+ && receipt.generationStarted !== false && receipt.scopeViolation !== true
1627
+ && (receipt.localExitCode === undefined || receipt.localExitCode === 0)
1628
+ && (input.processManagement.mode === 'unmanaged-confirmed' || processResult.gradingEvidenceAccepted)
1629
+ && processResult.stdout.trim() !== '';
1630
+ const status = completed ? 'completed' : receipt?.outcome === 'failed'
1631
+ && (receipt.providerTerminal === 'turn.failed' || receipt.generationStarted === false) ? 'failed' : 'unknown';
1632
+ if (!completed)
1633
+ diagnostic ??= 'External execution lacks compatible channel, instruction revision, terminal, normal exit, or final text evidence.';
1634
+ const after = await snapshotWorkspace(input.input.workspacePath);
1635
+ await temporaryRoot.cleanup();
1636
+ cleaned = true;
1637
+ const usage = standardUsage(receipt?.usage);
1638
+ const evidence = {
1639
+ version: 1, rootThreadId: receipt?.providerSessionId ?? selected.request.run.id,
1640
+ completion: { status, turnStatus: receipt?.providerTerminal ?? 'unknown', durationMs: Date.now() - startedAt },
1641
+ termination: {
1642
+ reason: status,
1643
+ providerProcessCleanupConfirmed: input.processManagement.mode !== 'unmanaged-confirmed'
1644
+ && processResult.gradingEvidenceAccepted,
1645
+ },
1646
+ usage, finalMessage: processResult.stdout, workspaceDiff: workspaceDiff(before, after),
1647
+ skillLoads: [], threadGraph: [], collaborationCalls: [], leafCalls: [],
1648
+ cleanup: { isolatedCodexHomeRemoved: true },
1649
+ transport: {
1650
+ type: 'instruction-v2', request: selected.request, command: selected.command, receipt,
1651
+ traceAvailable: trace.length > 0,
1652
+ observationLimits: [
1653
+ 'Provider JSONL events are captured as supplied; complete file-access attribution and nested delegation coverage are not established.',
1654
+ 'A file diff proves observed final content, not absence of intermediate writes. Model statements do not prove read-only behavior, independent history, or OS isolation.',
1655
+ 'Provider trace is read after process exit. Artifact snapshots measure before submission and after cleanup; replayed event order does not supply intermediate snapshots.',
1656
+ ],
1657
+ diagnostic, exitCode: processResult.exitCode, signal: processResult.signal, stderr: processResult.stderr,
1658
+ },
1659
+ };
1660
+ if (completed && evaluation) {
1661
+ for (const frame of trace)
1662
+ evaluation.observe({ method: 'evaluation/externalTraceCapture', capturedAfterExit: true, frame }, evidence.rootThreadId);
1663
+ const messages = [
1664
+ { type: 'response_item', payload: { type: 'message', role: 'user', content: [{ type: 'input_text', text: selected.request.task }] } },
1665
+ ...trace,
1666
+ { type: 'response_item', payload: { type: 'agent_message', text: processResult.stdout } },
1667
+ ];
1668
+ try {
1669
+ evidence.evaluation = await evaluation.finish(evidence, messages);
1670
+ if (!trace.length) {
1671
+ evidence.evaluation.complete = false;
1672
+ evidence.evaluation.checks = evaluation.request.checks.map(({ name }) => ({
1673
+ name, verdict: 'unknown', evidence: 'External provider tool trace is unavailable; behavior assertions cannot be established from final text.',
1674
+ }));
1675
+ }
1676
+ }
1677
+ catch (error) {
1678
+ evidence.evaluation = {
1679
+ complete: false, checks: [], observations: evaluation.observations,
1680
+ snapshots: evaluation.snapshots, decisions: evaluation.decisions,
1681
+ fixtureActions: evaluation.fixtureActions, measurementStart: evaluation.measurementStart,
1682
+ };
1683
+ await writeWorkflowArtifacts(input.input.outputDir, redactCodexEvalValue({ evidence, trace, modelOutput: processResult.stdout, usage }, input.codexRuntime));
1684
+ throw error;
1685
+ }
1686
+ const totals = [usage, ...judgeUsage].map(standardUsage);
1687
+ evidence.usage = totals.every((value) => value !== null) ? {
1688
+ inputTokens: totals.reduce((sum, value) => sum + value.inputTokens, 0),
1689
+ outputTokens: totals.reduce((sum, value) => sum + value.outputTokens, 0),
1690
+ } : null;
1691
+ }
1692
+ return redactCodexEvalValue({ evidence, trace, modelOutput: processResult.stdout, usage: evidence.usage }, input.codexRuntime);
1693
+ }
1694
+ finally {
1695
+ try {
1696
+ if (!cleaned)
1697
+ await temporaryRoot.cleanup();
1698
+ }
1699
+ finally {
1700
+ control.dispose();
1701
+ }
1702
+ }
1703
+ },
1704
+ };
1481
1705
  const defaultTransport = {
1482
1706
  async run(input) {
1483
1707
  const control = new CodexEvalAttemptControl({
@@ -1486,8 +1710,10 @@ const defaultTransport = {
1486
1710
  ...(input.signal ? { signal: input.signal } : {}),
1487
1711
  });
1488
1712
  const temporaryRoot = await createCodexEvalTemporaryRoot('codex-workflow-');
1489
- const codexHome = path.join(temporaryRoot.root, 'codex-home');
1490
- const env = buildEngineEnvironment(input.sourceEnv, temporaryRoot.root, codexHome, input.codexRuntime);
1713
+ const runtimeRoot = await fs.realpath(temporaryRoot.root);
1714
+ const codexHome = path.join(runtimeRoot, 'codex-home');
1715
+ const env = buildEngineEnvironment(input.sourceEnv, runtimeRoot, codexHome, input.codexRuntime);
1716
+ env.AW_SUBAGENT_CODEX_BINARY = input.codexBin;
1491
1717
  let session;
1492
1718
  let cleanupConfirmed = false;
1493
1719
  let codexHomeRemoved = false;
@@ -1512,7 +1738,7 @@ const defaultTransport = {
1512
1738
  throw new Error('workflow semantic grader did not complete its read-only checks.');
1513
1739
  judgeUsage.push(result.usage);
1514
1740
  return parseConversationGraderOutput(result.output);
1515
- }) : undefined;
1741
+ }, workflowFixtureRunner(input, control)) : undefined;
1516
1742
  let removeHandler;
1517
1743
  let removeFailureHandler;
1518
1744
  try {
@@ -1523,16 +1749,21 @@ const defaultTransport = {
1523
1749
  trustedProject: input.input.workspacePath,
1524
1750
  }),
1525
1751
  ]);
1752
+ await evaluation?.runSetupActions();
1526
1753
  const before = await snapshotWorkspace(input.input.workspacePath);
1527
1754
  let containment;
1528
1755
  if (evaluation) {
1529
1756
  containment = hostReadContainmentProfile({
1530
1757
  forbiddenHostRoots: [input.input.fixturesPath, input.input.outputDir],
1531
- writableRoots: [temporaryRoot.root, input.input.workspacePath],
1758
+ writableRoots: [runtimeRoot, input.input.workspacePath],
1532
1759
  unwritableRoots: input.request.sandbox === 'read-only' ? [input.input.workspacePath] : [],
1533
1760
  }) + '\n' + [...new Set([input.evalRoot, ...(input.protectedEvalRoots ?? [])])].map((root) => `(deny file-read* (require-all (subpath ${JSON.stringify(root)}) (require-not (subpath ${JSON.stringify(input.input.workspacePath)}))))`).join('\n');
1761
+ for (let ancestor = path.dirname(input.input.workspacePath); ancestor !== path.dirname(ancestor); ancestor = path.dirname(ancestor)) {
1762
+ containment += `\n(allow file-read-metadata (literal ${JSON.stringify(ancestor)}))`;
1763
+ }
1534
1764
  await assertHostReadContainmentAvailable(containment);
1535
1765
  }
1766
+ const configOverrides = workflowConfig(input.request, input.input.workspacePath);
1536
1767
  session = new EvalAppServerSession({
1537
1768
  codexBin: input.codexBin,
1538
1769
  cwd: input.input.workspacePath,
@@ -1541,7 +1772,7 @@ const defaultTransport = {
1541
1772
  sensitiveEnvironmentNames: input.codexRuntime.sensitiveEnvironmentNames,
1542
1773
  isolation: 'standard',
1543
1774
  processManagement: input.processManagement,
1544
- configOverrides: workflowConfig(input.request, input.input.workspacePath),
1775
+ configOverrides,
1545
1776
  ...(containment ? { hostReadContainmentProfile: containment } : {}),
1546
1777
  });
1547
1778
  await session.initialize(control.rpcTimeout('initialize'));
@@ -1565,7 +1796,7 @@ const defaultTransport = {
1565
1796
  const isolatedPaths = [
1566
1797
  { path: codexHome, marker: '[codex-home]' },
1567
1798
  { path: env.HOME, marker: '[isolated-home]' },
1568
- { path: temporaryRoot.root, marker: '[runtime-root]' },
1799
+ { path: runtimeRoot, marker: '[runtime-root]' },
1569
1800
  ];
1570
1801
  frames.push(sanitizeTraceValue(frame, input.codexRuntime, isolatedPaths, input.request.multiAgent.protocol === 'v2'));
1571
1802
  const method = typeof frame.method === 'string' ? frame.method : '';
@@ -1680,7 +1911,7 @@ const defaultTransport = {
1680
1911
  const isolatedPaths = [
1681
1912
  { path: codexHome, marker: '[codex-home]' },
1682
1913
  { path: env.HOME, marker: '[isolated-home]' },
1683
- { path: temporaryRoot.root, marker: '[runtime-root]' },
1914
+ { path: runtimeRoot, marker: '[runtime-root]' },
1684
1915
  ];
1685
1916
  const rollout = await normalizeCodexWorkflowRollouts({
1686
1917
  codexHome,
@@ -1719,10 +1950,34 @@ const defaultTransport = {
1719
1950
  collaborationCalls: rollout.collaborationCalls,
1720
1951
  leafCalls: rollout.leafCalls,
1721
1952
  cleanup: { isolatedCodexHomeRemoved: true },
1953
+ launch: {
1954
+ instructionChannel: 'developer', developerInstructions: input.request.developerInstructions,
1955
+ instructionsRevision: createHash('sha256').update(input.request.developerInstructions).digest('hex'),
1956
+ model: input.request.model, reasoningEffort: input.request.reasoningEffort ?? null,
1957
+ sandbox: input.request.sandbox, roles: input.request.roles, configOverrides,
1958
+ hostRoleAdoption: 'unknown',
1959
+ },
1722
1960
  };
1723
1961
  if (evaluation) {
1724
1962
  // Subject processes are stopped before final snapshots and independent grading.
1725
- evidence.evaluation = await evaluation.finish(evidence, rollout.trace);
1963
+ try {
1964
+ evidence.evaluation = await evaluation.finish(evidence, rollout.trace);
1965
+ }
1966
+ catch (error) {
1967
+ evidence.evaluation = {
1968
+ complete: false, checks: [], observations: evaluation.observations,
1969
+ snapshots: evaluation.snapshots, decisions: evaluation.decisions,
1970
+ fixtureActions: evaluation.fixtureActions,
1971
+ measurementStart: evaluation.measurementStart,
1972
+ };
1973
+ await writeWorkflowArtifacts(input.input.outputDir, {
1974
+ evidence: session.scrub(redactCodexEvalValue(evidence, input.codexRuntime)),
1975
+ trace: session.scrub(redactCodexEvalValue([...frames, ...rollout.trace], input.codexRuntime)),
1976
+ modelOutput: redactCodexEvalValue(finalMessage, input.codexRuntime),
1977
+ usage: redactCodexEvalValue(evidence.usage, input.codexRuntime),
1978
+ });
1979
+ throw error;
1980
+ }
1726
1981
  const totals = [usage, ...judgeUsage].map(standardUsage);
1727
1982
  if (totals.some((value) => value === null))
1728
1983
  throw new Error('workflow evaluation lacks required judge usage evidence.');
@@ -1848,13 +2103,13 @@ export async function executeCodexWorkflowProvider(options) {
1848
2103
  control.throwIfStopped('prepare');
1849
2104
  throw error;
1850
2105
  }
1851
- const request = await parseCodexWorkflowRequest(prepared, options.provider.allowedModels, paths.workspacePath);
1852
- if (request.model === null && request.multiAgent.protocol === 'v1') {
2106
+ const request = await parseCodexWorkflowRequest(prepared, options.provider.allowedModels, paths.workspacePath, paths.fixturesPath);
2107
+ if (!request.transport && request.model === null && request.multiAgent.protocol === 'v1') {
1853
2108
  request.model = 'gpt-5.6-luna';
1854
2109
  }
1855
2110
  let execution;
1856
2111
  try {
1857
- execution = await (options.transport ?? defaultTransport).run({
2112
+ execution = await (options.transport ?? (request.transport ? instructionV2Transport : defaultTransport)).run({
1858
2113
  request,
1859
2114
  input: { ...options.input, ...paths, timeoutMs: control.remaining('Codex execution') },
1860
2115
  evalRoot: paths.evalRoot,
@@ -1863,6 +2118,7 @@ export async function executeCodexWorkflowProvider(options) {
1863
2118
  sourceEnv,
1864
2119
  timeoutMs: control.remaining('Codex execution'),
1865
2120
  processManagement,
2121
+ ...(options.processTree ? { processTree: options.processTree } : {}),
1866
2122
  ...(options.protectedEvalRoots ? { protectedEvalRoots: options.protectedEvalRoots } : {}),
1867
2123
  signal: control.signal,
1868
2124
  });
@@ -1871,13 +2127,16 @@ export async function executeCodexWorkflowProvider(options) {
1871
2127
  control.throwIfStopped('Codex execution');
1872
2128
  throw error;
1873
2129
  }
1874
- control.throwIfStopped('evidence collection');
1875
2130
  const sanitized = redactCodexEvalValue(execution, options.codexRuntime);
1876
2131
  const usage = standardUsage(sanitized.usage);
1877
- if (!usage || !standardUsage(sanitized.evidence.usage)) {
2132
+ if (!request.transport && (!usage || !standardUsage(sanitized.evidence.usage))) {
1878
2133
  throw new Error('codex-workflow completed without required token usage evidence.');
1879
2134
  }
1880
2135
  await writeWorkflowArtifacts(paths.outputDir, sanitized);
2136
+ control.throwIfStopped('evidence collection');
2137
+ if (sanitized.evidence.completion.status !== 'completed') {
2138
+ throw new Error(`instruction-v2 workflow execution is ${sanitized.evidence.completion.status}; no grade was accepted.`);
2139
+ }
1881
2140
  const beforeGrade = await artifactDigests(paths.outputDir);
1882
2141
  let gradeValue;
1883
2142
  try {