agentbox-sdk 0.1.301 → 0.1.303

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.
@@ -2,7 +2,7 @@ import {
2
2
  createNormalizedEvent,
3
3
  normalizeRawAgentEvent,
4
4
  toAISDKStream
5
- } from "./chunk-ZOWBRUQR.js";
5
+ } from "./chunk-NWAXQ7UD.js";
6
6
  import {
7
7
  AgentBoxError,
8
8
  AsyncQueue,
@@ -19,7 +19,7 @@ import {
19
19
  sleep,
20
20
  time,
21
21
  waitFor
22
- } from "./chunk-INMA52FV.js";
22
+ } from "./chunk-AVXJMCBC.js";
23
23
  import {
24
24
  shellQuote
25
25
  } from "./chunk-NSJM57Z4.js";
@@ -998,6 +998,28 @@ var HostSetupTarget = class {
998
998
  }
999
999
  );
1000
1000
  }
1001
+ async probe(command, extraEnv) {
1002
+ return time(
1003
+ debugRuntime,
1004
+ `host probe ${shortLabel(command)}`,
1005
+ async () => {
1006
+ const handle = spawnCommand({
1007
+ command: process.env.SHELL || "sh",
1008
+ args: ["-c", command],
1009
+ cwd: this.cwd,
1010
+ env: {
1011
+ ...process.env,
1012
+ ...this.baseEnv,
1013
+ ...this.env,
1014
+ ...extraEnv ?? {}
1015
+ }
1016
+ });
1017
+ const exitCode = await handle.wait();
1018
+ return exitCode === 0;
1019
+ },
1020
+ (ok) => ({ ok })
1021
+ );
1022
+ }
1001
1023
  async cleanup() {
1002
1024
  await rm(this.layout.rootDir, { recursive: true, force: true });
1003
1025
  }
@@ -1052,13 +1074,34 @@ var SandboxSetupTarget = class {
1052
1074
  }
1053
1075
  });
1054
1076
  if (result && result.exitCode !== 0) {
1077
+ const output = result.combinedOutput?.trim();
1078
+ const detail = output ? `
1079
+ ${output}` : "";
1055
1080
  throw new Error(
1056
- `Sandbox setup command failed (${result.exitCode}): ${command}`
1081
+ `Sandbox setup command failed (${result.exitCode}): ${command}${detail}`
1057
1082
  );
1058
1083
  }
1059
1084
  }
1060
1085
  );
1061
1086
  }
1087
+ async probe(command, extraEnv) {
1088
+ return time(
1089
+ debugRuntime,
1090
+ `sandbox probe ${shortLabel(command)}`,
1091
+ async () => {
1092
+ const result = await this.options.sandbox?.run(command, {
1093
+ cwd: this.options.cwd,
1094
+ env: {
1095
+ ...this.options.env ?? {},
1096
+ ...this.env,
1097
+ ...extraEnv ?? {}
1098
+ }
1099
+ });
1100
+ return Boolean(result && result.exitCode === 0);
1101
+ },
1102
+ (ok) => ({ ok })
1103
+ );
1104
+ }
1062
1105
  async cleanup() {
1063
1106
  }
1064
1107
  };
@@ -1092,6 +1135,7 @@ import path5 from "path";
1092
1135
  var MANIFEST_FILENAME = "setup-manifest.json";
1093
1136
  var TARGET_MANIFEST_FILENAME = "setup-target.json";
1094
1137
  var INSTALL_SCRIPT_FILENAME = "install.sh";
1138
+ var SETUP_ID_FILENAME = "setup.id";
1095
1139
  var MANIFEST_VERSION = 1;
1096
1140
  function hashArtifact(artifact) {
1097
1141
  const hasher = createHash("sha256");
@@ -1110,6 +1154,74 @@ function computeTargetArtifacts(artifacts) {
1110
1154
  }
1111
1155
  return result;
1112
1156
  }
1157
+ function computeSetupId(parts) {
1158
+ const hasher = createHash("sha256");
1159
+ hasher.update(`v${MANIFEST_VERSION}
1160
+ `);
1161
+ for (const a of [...parts.artifacts ?? []].sort(
1162
+ (x, y) => x.path.localeCompare(y.path)
1163
+ )) {
1164
+ hasher.update(`a:${a.path}:${hashArtifact(a)}
1165
+ `);
1166
+ }
1167
+ for (const cmd of parts.installCommands ?? []) {
1168
+ hasher.update(`c:${hashCommand(cmd)}
1169
+ `);
1170
+ }
1171
+ if (parts.daemon) {
1172
+ hasher.update(
1173
+ `d:${parts.daemon.port}:${parts.daemon.healthPath}:${parts.daemon.expectedVersionMatch ?? ""}
1174
+ `
1175
+ );
1176
+ }
1177
+ for (const extra of parts.extras ?? []) {
1178
+ hasher.update(`x:${extra}
1179
+ `);
1180
+ }
1181
+ return hasher.digest("hex");
1182
+ }
1183
+ async function preflightSetup(target, setupId, daemon) {
1184
+ return time(
1185
+ debugSetup,
1186
+ `preflightSetup ${target.provider}`,
1187
+ async () => {
1188
+ const setupIdFile = path5.posix.join(
1189
+ target.layout.rootDir,
1190
+ SETUP_ID_FILENAME
1191
+ );
1192
+ const checks = [
1193
+ `[ -f ${shellQuote(setupIdFile)} ]`,
1194
+ `[ "$(cat ${shellQuote(setupIdFile)} 2>/dev/null)" = ${shellQuote(setupId)} ]`
1195
+ ];
1196
+ if (daemon) {
1197
+ const url = `http://127.0.0.1:${daemon.port}${daemon.healthPath}`;
1198
+ if (daemon.expectedVersionMatch) {
1199
+ checks.push(
1200
+ `curl -fsS --max-time 2 ${shellQuote(url)} 2>/dev/null | grep -q ${shellQuote(daemon.expectedVersionMatch)}`
1201
+ );
1202
+ } else {
1203
+ checks.push(
1204
+ `curl -fsS --max-time 2 ${shellQuote(url)} >/dev/null 2>&1`
1205
+ );
1206
+ }
1207
+ }
1208
+ return target.probe(checks.join(" && "));
1209
+ },
1210
+ (ok) => ({ ok })
1211
+ );
1212
+ }
1213
+ async function markSetupComplete(target, setupId) {
1214
+ await time(
1215
+ debugSetup,
1216
+ `markSetupComplete ${target.provider}`,
1217
+ () => target.runCommand(
1218
+ [
1219
+ `mkdir -p ${shellQuote(target.layout.rootDir)}`,
1220
+ `printf '%s' ${shellQuote(setupId)} > ${shellQuote(path5.posix.join(target.layout.rootDir, SETUP_ID_FILENAME))}`
1221
+ ].join(" && ")
1222
+ )
1223
+ );
1224
+ }
1113
1225
  function buildInstallScript(rootDir, installCommandsByKey) {
1114
1226
  const commandsB64 = Buffer.from(
1115
1227
  JSON.stringify(installCommandsByKey),
@@ -1445,7 +1557,10 @@ function extractCodexCostData(events) {
1445
1557
  let sawUsage = false;
1446
1558
  for (const event of events) {
1447
1559
  const params = asRecord(event.params);
1448
- const usageCandidate = asRecord(event.usage) ?? asRecord(params?.usage) ?? asRecord(params?.tokenUsage) ?? asRecord(asRecord(params?.turn)?.usage) ?? asRecord(asRecord(params?.turn)?.tokenUsage);
1560
+ const tokenUsage = asRecord(params?.tokenUsage);
1561
+ const turn = asRecord(params?.turn);
1562
+ const turnTokenUsage = asRecord(turn?.tokenUsage);
1563
+ const usageCandidate = asRecord(event.usage) ?? asRecord(params?.usage) ?? asRecord(tokenUsage?.last) ?? asRecord(tokenUsage?.total) ?? tokenUsage ?? asRecord(turn?.usage) ?? asRecord(turnTokenUsage?.last) ?? asRecord(turnTokenUsage?.total) ?? turnTokenUsage;
1449
1564
  if (usageCandidate) {
1450
1565
  sawUsage = true;
1451
1566
  mergeUsage(usage, usageCandidate);
@@ -1513,12 +1628,23 @@ function buildClaudeQueryOptions(params) {
1513
1628
  settings: params.settingsPath,
1514
1629
  extraArgs,
1515
1630
  includePartialMessages: true,
1631
+ thinking: { type: "adaptive", display: "summarized" },
1516
1632
  ...run.model ? { model: run.model } : {},
1517
1633
  ...run.reasoning ? { effort: run.reasoning } : {},
1518
1634
  ...provider?.permissionMode ? { permissionMode: provider.permissionMode } : {},
1519
1635
  ...provider?.permissionMode === "bypassPermissions" ? { allowDangerouslySkipPermissions: true } : {},
1520
1636
  ...provider?.allowedTools?.length ? { allowedTools: provider.allowedTools } : {},
1521
- ...run.resumeSessionId ? { resume: run.resumeSessionId } : {}
1637
+ ...run.resumeSessionId ? { resume: run.resumeSessionId } : {},
1638
+ // Fork-at-message: claude-agent-sdk natively supports slicing a
1639
+ // resumed transcript at a message UUID and writing the continuation
1640
+ // under a new session id when `forkSession: true` is set. The
1641
+ // captured message UUID comes from `SDKAssistantMessage.uuid`,
1642
+ // surfaced on normalized `message.started` events.
1643
+ ...run.forkSessionId ? {
1644
+ resume: run.forkSessionId,
1645
+ resumeSessionAt: run.forkAtMessageId,
1646
+ forkSession: true
1647
+ } : {}
1522
1648
  };
1523
1649
  }
1524
1650
  function toRawEvent(runId, payload, type) {
@@ -1921,6 +2047,11 @@ var ClaudeCodeAgentAdapter = class {
1921
2047
  /**
1922
2048
  * Sandbox-side preparation. Uploads `.claude/` artifacts and ensures
1923
2049
  * the daemon is running. `execute()` then dials the daemon directly.
2050
+ *
2051
+ * Warm-path short-circuit: a single no-upload `preflightSetup` probes
2052
+ * the `setup.id` marker + daemon `/__version` on loopback. If both
2053
+ * match what we'd produce, we skip the artifact upload AND the daemon
2054
+ * boot entirely.
1924
2055
  */
1925
2056
  async setup(request) {
1926
2057
  await time(debugClaude, "claude-code setup()", async () => {
@@ -1954,6 +2085,20 @@ var ClaudeCodeAgentAdapter = class {
1954
2085
  { path: settingsPath, content: JSON.stringify(hookSettings, null, 2) },
1955
2086
  { path: mcpConfigPath, content: mcpConfigJson }
1956
2087
  ];
2088
+ const daemonInfo = {
2089
+ port: DAEMON_PORT,
2090
+ healthPath: "/__version",
2091
+ expectedVersionMatch: DAEMON_PROTOCOL_VERSION
2092
+ };
2093
+ const setupId = computeSetupId({
2094
+ artifacts,
2095
+ installCommands,
2096
+ daemon: daemonInfo
2097
+ });
2098
+ if (await preflightSetup(target, setupId, daemonInfo)) {
2099
+ debugClaude("claude-code setup() preflight hit \u2014 skipping");
2100
+ return;
2101
+ }
1957
2102
  const env = { ...options.env ?? {}, ...target.env };
1958
2103
  await Promise.all([
1959
2104
  time(
@@ -1963,6 +2108,7 @@ var ClaudeCodeAgentAdapter = class {
1963
2108
  ),
1964
2109
  ensureClaudeCodeDaemon(options, env)
1965
2110
  ]);
2111
+ await markSetupComplete(target, setupId);
1966
2112
  });
1967
2113
  }
1968
2114
  async execute(request, sink) {
@@ -2092,6 +2238,8 @@ var ClaudeCodeAgentAdapter = class {
2092
2238
  let pendingMessages = 1;
2093
2239
  let firstStreamEventLogged = false;
2094
2240
  let firstTextDeltaLogged = false;
2241
+ let lastTerminalReason;
2242
+ let lastIsError = false;
2095
2243
  const rawPayloads = [];
2096
2244
  try {
2097
2245
  for await (const item of parseNdjsonStream(response.body)) {
@@ -2179,6 +2327,8 @@ var ClaudeCodeAgentAdapter = class {
2179
2327
  }
2180
2328
  if (message.type === "result") {
2181
2329
  const result = message;
2330
+ lastTerminalReason = result.terminal_reason;
2331
+ lastIsError = result.is_error;
2182
2332
  const resultText = result.subtype === "success" ? result.result : accumulatedText;
2183
2333
  if (resultText && resultText !== accumulatedText) {
2184
2334
  accumulatedText = resultText;
@@ -2189,22 +2339,43 @@ var ClaudeCodeAgentAdapter = class {
2189
2339
  }
2190
2340
  }
2191
2341
  const finalText = accumulatedText;
2192
- debugClaude(
2193
- "\u2605 run.completed (%dms since execute start) chars=%d",
2194
- Date.now() - executeStartedAt,
2195
- finalText.length
2196
- );
2197
- sink.emitEvent(
2198
- createNormalizedEvent(
2199
- "run.completed",
2200
- { provider: request.provider, runId: request.runId },
2201
- { text: finalText }
2202
- )
2203
- );
2204
- sink.complete({
2205
- text: finalText,
2206
- costData: extractClaudeCostData(rawPayloads)
2207
- });
2342
+ const isCancelled = lastTerminalReason === "aborted_streaming" || lastTerminalReason === "aborted_tools";
2343
+ const isError = !isCancelled && lastIsError;
2344
+ if (isCancelled) {
2345
+ debugClaude(
2346
+ "\u2605 run.cancelled (%dms since execute start) reason=%s",
2347
+ Date.now() - executeStartedAt,
2348
+ lastTerminalReason
2349
+ );
2350
+ sink.cancel({
2351
+ text: finalText,
2352
+ costData: extractClaudeCostData(rawPayloads)
2353
+ });
2354
+ } else if (isError) {
2355
+ debugClaude(
2356
+ "\u2605 run.error (%dms since execute start) reason=%s",
2357
+ Date.now() - executeStartedAt,
2358
+ lastTerminalReason
2359
+ );
2360
+ sink.fail(new Error(finalText || `claude-code run failed (terminal_reason: ${lastTerminalReason})`));
2361
+ } else {
2362
+ debugClaude(
2363
+ "\u2605 run.completed (%dms since execute start) chars=%d",
2364
+ Date.now() - executeStartedAt,
2365
+ finalText.length
2366
+ );
2367
+ sink.emitEvent(
2368
+ createNormalizedEvent(
2369
+ "run.completed",
2370
+ { provider: request.provider, runId: request.runId },
2371
+ { text: finalText }
2372
+ )
2373
+ );
2374
+ sink.complete({
2375
+ text: finalText,
2376
+ costData: extractClaudeCostData(rawPayloads)
2377
+ });
2378
+ }
2208
2379
  } finally {
2209
2380
  fetchAbort.abort();
2210
2381
  }
@@ -2487,7 +2658,8 @@ function buildThreadParams(cwd, options, request) {
2487
2658
  // Persist the rollout on disk so follow-up runs can call `thread/resume`.
2488
2659
  // `ephemeral: true` threads have no rollout file and resume fails with
2489
2660
  // "no rollout found for thread id ...".
2490
- experimentalRawEvents: true
2661
+ experimentalRawEvents: true,
2662
+ developerInstructions: request.run.systemPrompt ?? null
2491
2663
  };
2492
2664
  }
2493
2665
  function buildResumeParams(cwd, options, request) {
@@ -2496,7 +2668,18 @@ function buildResumeParams(cwd, options, request) {
2496
2668
  cwd,
2497
2669
  model: request.run.model ?? null,
2498
2670
  approvalPolicy: isInteractiveApproval(options) ? "untrusted" : "never",
2499
- sandbox: buildCodexSandboxMode(options)
2671
+ sandbox: buildCodexSandboxMode(options),
2672
+ developerInstructions: request.run.systemPrompt ?? null
2673
+ };
2674
+ }
2675
+ function buildForkParams(cwd, options, request) {
2676
+ return {
2677
+ threadId: request.run.forkSessionId,
2678
+ cwd,
2679
+ model: request.run.model ?? null,
2680
+ approvalPolicy: isInteractiveApproval(options) ? "untrusted" : "never",
2681
+ sandbox: buildCodexSandboxMode(options),
2682
+ developerInstructions: request.run.systemPrompt ?? null
2500
2683
  };
2501
2684
  }
2502
2685
  function buildTurnSandboxPolicy(options) {
@@ -2514,27 +2697,14 @@ function buildTurnSandboxPolicy(options) {
2514
2697
  networkAccess: "enabled"
2515
2698
  };
2516
2699
  }
2517
- function buildTurnCollaborationMode(request) {
2518
- const systemPrompt = request.run.systemPrompt;
2519
- if (!systemPrompt) {
2520
- return void 0;
2521
- }
2522
- return {
2523
- mode: "custom",
2524
- settings: {
2525
- developer_instructions: systemPrompt
2526
- }
2527
- };
2528
- }
2529
2700
  function buildCodexTurnStartParams(params) {
2530
- const { threadId, inputItems, request, turnStartOverrides } = params;
2701
+ const { threadId, inputItems, request } = params;
2531
2702
  const sandboxPolicy = buildTurnSandboxPolicy(request.options);
2532
2703
  return {
2533
2704
  threadId,
2534
2705
  input: inputItems,
2535
2706
  approvalPolicy: isInteractiveApproval(request.options) ? "untrusted" : "never",
2536
2707
  ...sandboxPolicy ? { sandboxPolicy } : {},
2537
- ...turnStartOverrides ?? {},
2538
2708
  model: request.run.model ?? null,
2539
2709
  effort: request.run.reasoning ?? null,
2540
2710
  outputSchema: null
@@ -2781,7 +2951,12 @@ async function ensureCodexLoginViaConfig(request, target) {
2781
2951
  [
2782
2952
  'if [ -z "${OPENAI_API_KEY:-}" ]; then exit 0; fi',
2783
2953
  'mkdir -p "${CODEX_HOME:-$HOME/.codex}"',
2784
- "printenv OPENAI_API_KEY | env -u XDG_CONFIG_HOME codex login --with-api-key"
2954
+ // Merge stderr into stdout so providers that don't surface stderr
2955
+ // (e.g. Daytona's `executeCommand`, which collapses to a single
2956
+ // `result` field) still propagate the underlying error message
2957
+ // back to the caller — otherwise a failed login leaves us with a
2958
+ // bare "exit 1" and no diagnostic.
2959
+ "printenv OPENAI_API_KEY | env -u XDG_CONFIG_HOME codex login --with-api-key 2>&1"
2785
2960
  ].join("; "),
2786
2961
  Object.keys(extraEnv).length > 0 ? extraEnv : void 0
2787
2962
  );
@@ -2887,12 +3062,26 @@ async function setupCodex(request) {
2887
3062
  ...sharedTarget.env,
2888
3063
  ...options.provider?.env ?? {}
2889
3064
  });
3065
+ const { artifacts: serverArtifacts } = buildArtifactsFor(sharedTarget);
3066
+ const { artifacts: skillArtifacts2, installCommands: installCommands2 } = await prepareSkillArtifacts(provider, options.skills, target2.layout);
3067
+ const daemonInfo = {
3068
+ port: REMOTE_CODEX_APP_SERVER_PORT,
3069
+ healthPath: "/readyz"
3070
+ };
3071
+ const setupId2 = computeSetupId({
3072
+ artifacts: [...serverArtifacts, ...skillArtifacts2],
3073
+ installCommands: installCommands2,
3074
+ daemon: daemonInfo
3075
+ });
3076
+ if (await preflightSetup(sharedTarget, setupId2, daemonInfo)) {
3077
+ debugCodex("codex remote setup() preflight hit \u2014 skipping");
3078
+ return;
3079
+ }
2890
3080
  await time(
2891
3081
  debugCodex,
2892
3082
  "ensureCodexLogin",
2893
3083
  () => ensureCodexLoginViaConfig(request, sharedTarget)
2894
3084
  );
2895
- const { artifacts: serverArtifacts } = buildArtifactsFor(sharedTarget);
2896
3085
  await applyDifferentialSetup(sharedTarget, serverArtifacts, []);
2897
3086
  const binary = options.provider?.binary ?? "codex";
2898
3087
  const pidFilePath = path9.posix.join(
@@ -2940,28 +3129,34 @@ async function setupCodex(request) {
2940
3129
  );
2941
3130
  }
2942
3131
  try {
2943
- const { artifacts: skillArtifacts2, installCommands: installCommands2 } = await prepareSkillArtifacts(provider, options.skills, target2.layout);
2944
3132
  await applyDifferentialSetup(target2, skillArtifacts2, installCommands2);
2945
3133
  } catch (error) {
2946
3134
  await target2.cleanup().catch(() => void 0);
2947
3135
  throw error;
2948
3136
  }
3137
+ await markSetupComplete(sharedTarget, setupId2);
2949
3138
  return;
2950
3139
  }
2951
3140
  const target = await createSetupTarget(provider, "shared-setup", options);
3141
+ const { artifacts: skillArtifacts, installCommands } = await prepareSkillArtifacts(provider, options.skills, target.layout);
3142
+ const { artifacts: configArtifacts } = buildArtifactsFor(target);
3143
+ const allArtifacts = [...skillArtifacts, ...configArtifacts];
3144
+ const setupId = computeSetupId({
3145
+ artifacts: allArtifacts,
3146
+ installCommands
3147
+ });
3148
+ if (await preflightSetup(target, setupId)) {
3149
+ debugCodex("codex local setup() preflight hit \u2014 skipping");
3150
+ return;
3151
+ }
2952
3152
  try {
2953
3153
  await ensureCodexLoginViaConfig(request, target);
2954
3154
  } catch (error) {
2955
3155
  await target.cleanup().catch(() => void 0);
2956
3156
  throw error;
2957
3157
  }
2958
- const { artifacts: skillArtifacts, installCommands } = await prepareSkillArtifacts(provider, options.skills, target.layout);
2959
- const { artifacts: configArtifacts } = buildArtifactsFor(target);
2960
- await applyDifferentialSetup(
2961
- target,
2962
- [...skillArtifacts, ...configArtifacts],
2963
- installCommands
2964
- );
3158
+ await applyDifferentialSetup(target, allArtifacts, installCommands);
3159
+ await markSetupComplete(target, setupId);
2965
3160
  }
2966
3161
  async function createRuntime(request, inputParts) {
2967
3162
  const options = request.options;
@@ -2998,8 +3193,7 @@ async function createRuntime(request, inputParts) {
2998
3193
  port: REMOTE_CODEX_APP_SERVER_PORT,
2999
3194
  codexDir
3000
3195
  },
3001
- inputItems,
3002
- turnStartOverrides: buildTurnCollaborationMode(request)
3196
+ inputItems
3003
3197
  };
3004
3198
  }
3005
3199
  const codexArgs = buildCodexCommandArgs(
@@ -3037,8 +3231,7 @@ async function createRuntime(request, inputParts) {
3037
3231
  await handle.kill();
3038
3232
  },
3039
3233
  raw: { handle, codexDir },
3040
- inputItems,
3041
- turnStartOverrides: buildTurnCollaborationMode(request)
3234
+ inputItems
3042
3235
  };
3043
3236
  }
3044
3237
  const processHandle = spawnCommand({
@@ -3060,8 +3253,7 @@ async function createRuntime(request, inputParts) {
3060
3253
  await processHandle.kill();
3061
3254
  },
3062
3255
  raw: { processHandle, codexDir },
3063
- inputItems,
3064
- turnStartOverrides: buildTurnCollaborationMode(request)
3256
+ inputItems
3065
3257
  };
3066
3258
  }
3067
3259
  async function buildCodexInputItems(options, inputParts) {
@@ -3118,7 +3310,9 @@ var CodexAgentAdapter = class {
3118
3310
  let rootThreadId;
3119
3311
  let turnId;
3120
3312
  let pendingTurns = 1;
3313
+ let abortInvoked = false;
3121
3314
  sink.setAbort(async () => {
3315
+ abortInvoked = true;
3122
3316
  const threadIdAtAbort = rootThreadId;
3123
3317
  const turnIdAtAbort = turnId;
3124
3318
  if (threadIdAtAbort && turnIdAtAbort) {
@@ -3164,8 +3358,8 @@ var CodexAgentAdapter = class {
3164
3358
  };
3165
3359
  sink.onMessage(sendTurn);
3166
3360
  const rawPayloads = [];
3361
+ let streamedText = "";
3167
3362
  const completion = new Promise((resolve, reject) => {
3168
- let finalText = "";
3169
3363
  void (async () => {
3170
3364
  let firstClientMessageLogged = false;
3171
3365
  for await (const message of client.messages()) {
@@ -3202,7 +3396,7 @@ var CodexAgentAdapter = class {
3202
3396
  for (const event of toNormalizedCodexEvents(request.runId, message)) {
3203
3397
  sink.emitEvent(event);
3204
3398
  if (event.type === "text.delta") {
3205
- finalText += event.delta;
3399
+ streamedText += event.delta;
3206
3400
  }
3207
3401
  }
3208
3402
  if (message.method === "thread/started" && !rootThreadId) {
@@ -3214,7 +3408,14 @@ var CodexAgentAdapter = class {
3214
3408
  if (message.method === "turn/completed" && (!message.params?.threadId || message.params.threadId === rootThreadId)) {
3215
3409
  pendingTurns--;
3216
3410
  if (pendingTurns <= 0) {
3217
- resolve({ text: finalText, turnId, threadId: rootThreadId });
3411
+ const turn = message.params?.turn;
3412
+ const interrupted = turn?.status === "interrupted";
3413
+ resolve({
3414
+ text: streamedText,
3415
+ turnId,
3416
+ threadId: rootThreadId,
3417
+ interrupted
3418
+ });
3218
3419
  return;
3219
3420
  }
3220
3421
  }
@@ -3241,13 +3442,27 @@ var CodexAgentAdapter = class {
3241
3442
  await client.notify("initialized", {});
3242
3443
  }
3243
3444
  const cwd = request.options.cwd ?? process.cwd();
3244
- const threadResponse = request.run.resumeSessionId ? await client.request(
3245
- "thread/resume",
3246
- buildResumeParams(cwd, request.options, request)
3247
- ) : await client.request(
3248
- "thread/start",
3249
- buildThreadParams(cwd, request.options, request)
3250
- );
3445
+ let threadResponse;
3446
+ let threadResultEventName;
3447
+ if (request.run.forkSessionId) {
3448
+ threadResponse = await client.request(
3449
+ "thread/fork",
3450
+ buildForkParams(cwd, request.options, request)
3451
+ );
3452
+ threadResultEventName = "thread/fork:result";
3453
+ } else if (request.run.resumeSessionId) {
3454
+ threadResponse = await client.request(
3455
+ "thread/resume",
3456
+ buildResumeParams(cwd, request.options, request)
3457
+ );
3458
+ threadResultEventName = "thread/resume:result";
3459
+ } else {
3460
+ threadResponse = await client.request(
3461
+ "thread/start",
3462
+ buildThreadParams(cwd, request.options, request)
3463
+ );
3464
+ threadResultEventName = "thread/start:result";
3465
+ }
3251
3466
  rootThreadId = threadResponse.thread.id;
3252
3467
  if ("bindThread" in client && typeof client.bindThread === "function") {
3253
3468
  client.bindThread(threadResponse.thread.id);
@@ -3255,28 +3470,79 @@ var CodexAgentAdapter = class {
3255
3470
  sink.setSessionId(threadResponse.thread.id);
3256
3471
  rawPayloads.push(threadResponse);
3257
3472
  sink.emitRaw(
3258
- toRawEvent2(
3259
- request.runId,
3260
- threadResponse,
3261
- request.run.resumeSessionId ? "thread/resume:result" : "thread/start:result"
3262
- )
3473
+ toRawEvent2(request.runId, threadResponse, threadResultEventName)
3263
3474
  );
3475
+ if (request.run.forkSessionId) {
3476
+ const targetTurnId = request.run.forkAtMessageId;
3477
+ const turns = threadResponse.thread.turns ?? [];
3478
+ const targetIndex = turns.findIndex((turn) => turn.id === targetTurnId);
3479
+ if (targetIndex < 0) {
3480
+ throw new Error(
3481
+ `Codex fork: turn id ${String(targetTurnId)} not found in source thread ${request.run.forkSessionId}.`
3482
+ );
3483
+ }
3484
+ const numTurns = turns.length - 1 - targetIndex;
3485
+ if (numTurns > 0) {
3486
+ const rollbackResponse = await client.request(
3487
+ "thread/rollback",
3488
+ { threadId: rootThreadId, numTurns }
3489
+ );
3490
+ rawPayloads.push(rollbackResponse);
3491
+ sink.emitRaw(
3492
+ toRawEvent2(
3493
+ request.runId,
3494
+ rollbackResponse,
3495
+ "thread/rollback:result"
3496
+ )
3497
+ );
3498
+ }
3499
+ }
3264
3500
  await client.request(
3265
3501
  "turn/start",
3266
3502
  buildCodexTurnStartParams({
3267
3503
  threadId: threadResponse.thread.id,
3268
3504
  inputItems: runtime.inputItems,
3269
- request,
3270
- turnStartOverrides: runtime.turnStartOverrides
3505
+ request
3271
3506
  })
3272
3507
  );
3273
- const { text } = await completion;
3274
- debugCodex(
3275
- "\u2605 run.completed (%dms since execute start) chars=%d",
3276
- Date.now() - executeStartedAt,
3277
- text?.length ?? 0
3278
- );
3279
- sink.complete({ text, costData: extractCodexCostData(rawPayloads) });
3508
+ let completionResult;
3509
+ let completionError;
3510
+ try {
3511
+ completionResult = await completion;
3512
+ } catch (err) {
3513
+ completionError = err;
3514
+ }
3515
+ if (completionError !== void 0) {
3516
+ if (abortInvoked) {
3517
+ debugCodex(
3518
+ "\u2605 run.cancelled (%dms since execute start)",
3519
+ Date.now() - executeStartedAt
3520
+ );
3521
+ sink.cancel({
3522
+ text: streamedText || void 0,
3523
+ costData: extractCodexCostData(rawPayloads)
3524
+ });
3525
+ } else {
3526
+ sink.fail(completionError);
3527
+ }
3528
+ } else {
3529
+ const { text, interrupted } = completionResult;
3530
+ if (abortInvoked || interrupted) {
3531
+ debugCodex(
3532
+ "\u2605 run.cancelled (%dms since execute start) interrupted=%s",
3533
+ Date.now() - executeStartedAt,
3534
+ interrupted
3535
+ );
3536
+ sink.cancel({ text, costData: extractCodexCostData(rawPayloads) });
3537
+ } else {
3538
+ debugCodex(
3539
+ "\u2605 run.completed (%dms since execute start) chars=%d",
3540
+ Date.now() - executeStartedAt,
3541
+ text?.length ?? 0
3542
+ );
3543
+ sink.complete({ text, costData: extractCodexCostData(rawPayloads) });
3544
+ }
3545
+ }
3280
3546
  } finally {
3281
3547
  await runtime.cleanup().catch(() => void 0);
3282
3548
  }
@@ -3500,12 +3766,11 @@ var OPEN_CODE_REASONING_LEVELS = ["low", "medium", "high", "xhigh"];
3500
3766
  function openCodeAgentSlug(reasoning) {
3501
3767
  return reasoning ? `agentbox-${reasoning}` : "agentbox";
3502
3768
  }
3503
- function buildOpenCodeConfig(options, systemPrompt, interactiveApproval) {
3769
+ function buildOpenCodeConfig(options, interactiveApproval) {
3504
3770
  const mcpConfig = buildOpenCodeMcpConfig(options.mcps);
3505
3771
  const commandsConfig = buildOpenCodeCommandsConfig(options.commands);
3506
3772
  const baseAgent = {
3507
3773
  mode: "primary",
3508
- prompt: systemPrompt,
3509
3774
  permission: buildOpenCodePermissionConfig(interactiveApproval),
3510
3775
  tools: {
3511
3776
  write: true,
@@ -3521,10 +3786,15 @@ function buildOpenCodeConfig(options, systemPrompt, interactiveApproval) {
3521
3786
  { ...baseAgent, reasoningEffort: level }
3522
3787
  ])
3523
3788
  );
3789
+ const googleBaseUrl = options.env?.GOOGLE_BASE_URL;
3524
3790
  return {
3525
3791
  $schema: "https://opencode.ai/config.json",
3526
3792
  ...mcpConfig ? { mcp: mcpConfig } : {},
3527
3793
  ...commandsConfig ? { command: commandsConfig } : {},
3794
+ provider: {
3795
+ openrouter: { options: { baseURL: "https://openrouter.ai/api/v1" } },
3796
+ ...googleBaseUrl ? { google: { options: { baseURL: googleBaseUrl } } } : {}
3797
+ },
3528
3798
  agent: {
3529
3799
  agentbox: baseAgent,
3530
3800
  ...reasoningVariants,
@@ -3537,19 +3807,6 @@ async function ensureSandboxOpenCodeServer(request) {
3537
3807
  const sandbox = request.options.sandbox;
3538
3808
  const options = request.options;
3539
3809
  const port = SANDBOX_OPENCODE_PORT;
3540
- const healthCheck = await time(
3541
- debugOpencode,
3542
- "health probe (warm path)",
3543
- () => sandbox.run(
3544
- `curl -fsS http://127.0.0.1:${port}/global/health >/dev/null 2>&1`,
3545
- { cwd: options.cwd, timeoutMs: 5e3 }
3546
- )
3547
- );
3548
- if (healthCheck.exitCode === 0) {
3549
- debugOpencode("opencode server already running \u2014 reusing");
3550
- return;
3551
- }
3552
- debugOpencode("opencode server not running \u2014 cold-spawning");
3553
3810
  const plugins = assertHooksSupported(request.provider, options);
3554
3811
  assertCommandsSupported(request.provider, options.commands);
3555
3812
  const interactiveApproval = isInteractiveApproval(options);
@@ -3570,26 +3827,32 @@ async function ensureSandboxOpenCodeServer(request) {
3570
3827
  const configPath = path10.join(target.layout.opencodeDir, "agentbox.json");
3571
3828
  const openCodeConfig = buildOpenCodeConfig(
3572
3829
  options,
3573
- request.config.systemPrompt ?? "",
3574
3830
  interactiveApproval
3575
3831
  );
3832
+ const allArtifacts = [
3833
+ ...skillArtifacts,
3834
+ ...pluginArtifacts,
3835
+ {
3836
+ path: configPath,
3837
+ content: JSON.stringify(openCodeConfig, null, 2)
3838
+ }
3839
+ ];
3840
+ const daemonInfo = { port, healthPath: "/global/health" };
3841
+ const setupId = computeSetupId({
3842
+ artifacts: allArtifacts,
3843
+ installCommands,
3844
+ daemon: daemonInfo
3845
+ });
3846
+ if (await preflightSetup(target, setupId, daemonInfo)) {
3847
+ debugOpencode("opencode setup() preflight hit \u2014 skipping");
3848
+ return;
3849
+ }
3576
3850
  const commonEnv = {
3577
3851
  OPENCODE_CONFIG: configPath,
3578
3852
  OPENCODE_CONFIG_DIR: target.layout.opencodeDir,
3579
3853
  OPENCODE_DISABLE_DEFAULT_PLUGINS: "true"
3580
3854
  };
3581
- await applyDifferentialSetup(
3582
- target,
3583
- [
3584
- ...skillArtifacts,
3585
- ...pluginArtifacts,
3586
- {
3587
- path: configPath,
3588
- content: JSON.stringify(openCodeConfig, null, 2)
3589
- }
3590
- ],
3591
- installCommands
3592
- );
3855
+ await applyDifferentialSetup(target, allArtifacts, installCommands);
3593
3856
  const binary = options.provider?.binary ?? "opencode";
3594
3857
  const pidFilePath = path10.posix.join(
3595
3858
  target.layout.rootDir,
@@ -3652,6 +3915,7 @@ async function ensureSandboxOpenCodeServer(request) {
3652
3915
  `OpenCode server did not become ready within ${SANDBOX_OPENCODE_READY_TIMEOUT_MS}ms.`
3653
3916
  );
3654
3917
  });
3918
+ await markSetupComplete(target, setupId);
3655
3919
  });
3656
3920
  }
3657
3921
  async function ensureLocalOpenCodeServer(request) {
@@ -3684,28 +3948,28 @@ async function ensureLocalOpenCodeServer(request) {
3684
3948
  target.layout.opencodeDir
3685
3949
  );
3686
3950
  const configPath = path10.join(target.layout.opencodeDir, "agentbox.json");
3687
- const openCodeConfig = buildOpenCodeConfig(
3688
- options,
3689
- request.config.systemPrompt ?? "",
3690
- interactiveApproval
3691
- );
3951
+ const openCodeConfig = buildOpenCodeConfig(options, interactiveApproval);
3692
3952
  const commonEnv = {
3693
3953
  OPENCODE_CONFIG: configPath,
3694
3954
  OPENCODE_CONFIG_DIR: target.layout.opencodeDir,
3695
3955
  OPENCODE_DISABLE_DEFAULT_PLUGINS: "true"
3696
3956
  };
3697
- await applyDifferentialSetup(
3698
- target,
3699
- [
3700
- ...skillArtifacts,
3701
- ...pluginArtifacts,
3702
- {
3703
- path: configPath,
3704
- content: JSON.stringify(openCodeConfig, null, 2)
3705
- }
3706
- ],
3957
+ const allArtifacts = [
3958
+ ...skillArtifacts,
3959
+ ...pluginArtifacts,
3960
+ {
3961
+ path: configPath,
3962
+ content: JSON.stringify(openCodeConfig, null, 2)
3963
+ }
3964
+ ];
3965
+ const setupId = computeSetupId({
3966
+ artifacts: allArtifacts,
3707
3967
  installCommands
3708
- );
3968
+ });
3969
+ const preflightHit = await preflightSetup(target, setupId);
3970
+ if (!preflightHit) {
3971
+ await applyDifferentialSetup(target, allArtifacts, installCommands);
3972
+ }
3709
3973
  spawnCommand({
3710
3974
  command: options.provider?.binary ?? "opencode",
3711
3975
  args: [
@@ -3727,6 +3991,7 @@ async function ensureLocalOpenCodeServer(request) {
3727
3991
  `http://127.0.0.1:${LOCAL_OPENCODE_PORT}/global/health`,
3728
3992
  { timeoutMs: LOCAL_OPENCODE_READY_TIMEOUT_MS }
3729
3993
  );
3994
+ await markSetupComplete(target, setupId);
3730
3995
  }
3731
3996
  async function setupOpenCode(request) {
3732
3997
  if (request.options.sandbox) {
@@ -3820,7 +4085,11 @@ var OpenCodeAgentAdapter = class {
3820
4085
  let sseTask;
3821
4086
  const dispatchAbort = new AbortController();
3822
4087
  let capturedSessionId;
4088
+ let sessionErrorFromSse;
4089
+ let sessionAbortedFromSse = false;
4090
+ let userAbortRequested = false;
3823
4091
  sink.setAbort(async () => {
4092
+ userAbortRequested = true;
3824
4093
  const sessionIdAtAbort = capturedSessionId;
3825
4094
  if (sessionIdAtAbort) {
3826
4095
  try {
@@ -3849,7 +4118,23 @@ var OpenCodeAgentAdapter = class {
3849
4118
  });
3850
4119
  try {
3851
4120
  const interactiveApproval = isInteractiveApproval(request.options);
3852
- const createdSession = request.run.resumeSessionId ? null : await fetchJson(
4121
+ let forkedSession = null;
4122
+ if (request.run.forkSessionId) {
4123
+ forkedSession = await fetchJson(
4124
+ `${runtime.baseUrl}/session/${encodeURIComponent(request.run.forkSessionId)}/fork`,
4125
+ {
4126
+ method: "POST",
4127
+ headers: {
4128
+ "content-type": "application/json",
4129
+ ...runtime.previewHeaders
4130
+ },
4131
+ body: JSON.stringify({
4132
+ messageID: request.run.forkAtMessageId
4133
+ })
4134
+ }
4135
+ );
4136
+ }
4137
+ const createdSession = request.run.resumeSessionId || forkedSession ? null : await fetchJson(
3853
4138
  `${runtime.baseUrl}/session`,
3854
4139
  {
3855
4140
  method: "POST",
@@ -3862,7 +4147,7 @@ var OpenCodeAgentAdapter = class {
3862
4147
  })
3863
4148
  }
3864
4149
  );
3865
- const sessionId = request.run.resumeSessionId ?? createdSession?.id ?? createdSession?.sessionId;
4150
+ const sessionId = request.run.resumeSessionId ?? forkedSession?.id ?? forkedSession?.sessionId ?? createdSession?.id ?? createdSession?.sessionId;
3866
4151
  if (!sessionId) {
3867
4152
  throw new Error("OpenCode did not return a session id.");
3868
4153
  }
@@ -3952,6 +4237,15 @@ var OpenCodeAgentAdapter = class {
3952
4237
  const properties = payloadRecord.properties;
3953
4238
  const eventSessionId = typeof properties?.sessionID === "string" ? properties.sessionID : void 0;
3954
4239
  if (!eventSessionId || eventSessionId === sessionId) {
4240
+ if (payloadRecord.type === "session.error") {
4241
+ const errData = properties?.error;
4242
+ if (errData?.name === "MessageAbortedError") {
4243
+ sessionAbortedFromSse = true;
4244
+ } else {
4245
+ const errMsg = typeof errData?.data?.message === "string" ? errData.data.message : typeof errData?.message === "string" ? errData.message : "OpenCode session error";
4246
+ sessionErrorFromSse = new Error(errMsg);
4247
+ }
4248
+ }
3955
4249
  debugOpencode(
3956
4250
  "\u2605 %s for session=%s \u2014 aborting in-flight dispatch",
3957
4251
  payloadRecord.type,
@@ -4040,6 +4334,11 @@ var OpenCodeAgentAdapter = class {
4040
4334
  },
4041
4335
  body: JSON.stringify({
4042
4336
  ...request.run.model ? { model: toOpenCodeModel(request.run.model) } : {},
4337
+ // Per-message system prompt — keeps systemPrompt out of
4338
+ // the on-disk agent config so changing it doesn't
4339
+ // invalidate setupId. Sent on every dispatch (the field
4340
+ // is per-message, not session-sticky).
4341
+ ...request.run.systemPrompt ? { system: request.run.systemPrompt } : {},
4043
4342
  agent: agentSlug,
4044
4343
  parts
4045
4344
  })
@@ -4121,30 +4420,48 @@ var OpenCodeAgentAdapter = class {
4121
4420
  pendingMessages++;
4122
4421
  void dispatchMessage(mapToOpenCodeParts(inputParts));
4123
4422
  await allDone;
4124
- if (dispatchError && !dispatchAbort.signal.aborted) {
4125
- throw dispatchError;
4423
+ if (userAbortRequested || sessionAbortedFromSse) {
4424
+ debugOpencode(
4425
+ "\u2605 run.cancelled (%dms since execute start)",
4426
+ Date.now() - executeStartedAt
4427
+ );
4428
+ sseAbort.abort();
4429
+ await sseTask;
4430
+ sink.cancel({
4431
+ text: streamedTextFromSse || void 0,
4432
+ costData: extractOpenCodeCostData(rawPayloads)
4433
+ });
4434
+ } else if (sessionErrorFromSse) {
4435
+ sseAbort.abort();
4436
+ await sseTask;
4437
+ sink.fail(sessionErrorFromSse);
4438
+ } else if (dispatchError && !(dispatchAbort.signal.aborted && dispatchError?.name === "AbortError")) {
4439
+ sseAbort.abort();
4440
+ await sseTask;
4441
+ sink.fail(dispatchError);
4442
+ } else {
4443
+ debugOpencode(
4444
+ "\u2605 run.completed (%dms since execute start) chars=%d",
4445
+ Date.now() - executeStartedAt,
4446
+ finalText.length
4447
+ );
4448
+ sink.emitEvent(
4449
+ createNormalizedEvent(
4450
+ "run.completed",
4451
+ {
4452
+ provider: request.provider,
4453
+ runId: request.runId
4454
+ },
4455
+ { text: finalText }
4456
+ )
4457
+ );
4458
+ sseAbort.abort();
4459
+ await sseTask;
4460
+ sink.complete({
4461
+ text: finalText,
4462
+ costData: extractOpenCodeCostData(rawPayloads)
4463
+ });
4126
4464
  }
4127
- debugOpencode(
4128
- "\u2605 run.completed (%dms since execute start) chars=%d",
4129
- Date.now() - executeStartedAt,
4130
- finalText.length
4131
- );
4132
- sink.emitEvent(
4133
- createNormalizedEvent(
4134
- "run.completed",
4135
- {
4136
- provider: request.provider,
4137
- runId: request.runId
4138
- },
4139
- { text: finalText }
4140
- )
4141
- );
4142
- sseAbort.abort();
4143
- await sseTask;
4144
- sink.complete({
4145
- text: finalText,
4146
- costData: extractOpenCodeCostData(rawPayloads)
4147
- });
4148
4465
  } finally {
4149
4466
  sseAbort.abort();
4150
4467
  if (sseTask) {
@@ -4308,17 +4625,14 @@ var AgentRunController = class {
4308
4625
  resolveSessionIdReady;
4309
4626
  rejectSessionIdReady;
4310
4627
  resolveFinished;
4311
- rejectFinished;
4312
4628
  constructor(provider, id) {
4313
4629
  this.provider = provider;
4314
4630
  this.id = id;
4315
4631
  let resolveFinished;
4316
- let rejectFinished;
4317
4632
  let resolveSessionIdReady;
4318
4633
  let rejectSessionIdReady;
4319
- this.finished = new Promise((resolve, reject) => {
4634
+ this.finished = new Promise((resolve) => {
4320
4635
  resolveFinished = resolve;
4321
- rejectFinished = reject;
4322
4636
  });
4323
4637
  this.sessionIdReady = new Promise((resolve, reject) => {
4324
4638
  resolveSessionIdReady = resolve;
@@ -4326,7 +4640,6 @@ var AgentRunController = class {
4326
4640
  });
4327
4641
  void this.sessionIdReady.catch(() => void 0);
4328
4642
  this.resolveFinished = resolveFinished;
4329
- this.rejectFinished = rejectFinished;
4330
4643
  this.resolveSessionIdReady = resolveSessionIdReady;
4331
4644
  this.rejectSessionIdReady = rejectSessionIdReady;
4332
4645
  }
@@ -4462,11 +4775,19 @@ var AgentRunController = class {
4462
4775
  this.eventQueue.finish();
4463
4776
  this.rawQueue.finish();
4464
4777
  if (!this.sessionId) {
4465
- const error = new Error(
4466
- "Agent run completed before a provider session id was set."
4467
- );
4468
- this.rejectSessionIdReady(error);
4469
- this.rejectFinished(error);
4778
+ const errorMsg = "Agent run completed before a provider session id was set.";
4779
+ this.rejectSessionIdReady(new Error(errorMsg));
4780
+ this.resolveFinished({
4781
+ id: this.id,
4782
+ provider: this.provider,
4783
+ sessionId: "",
4784
+ text: this.text,
4785
+ rawEvents: [...this.rawEventsList],
4786
+ events: [...this.events],
4787
+ costData: this.costData,
4788
+ isCancelled: false,
4789
+ error: errorMsg
4790
+ });
4470
4791
  return;
4471
4792
  }
4472
4793
  this.resolveFinished({
@@ -4476,7 +4797,44 @@ var AgentRunController = class {
4476
4797
  text: this.text,
4477
4798
  rawEvents: [...this.rawEventsList],
4478
4799
  events: [...this.events],
4479
- costData: this.costData
4800
+ costData: this.costData,
4801
+ isCancelled: false
4802
+ });
4803
+ }
4804
+ cancel(result) {
4805
+ if (this.settled) {
4806
+ return;
4807
+ }
4808
+ this.settled = true;
4809
+ this.clearPendingPermissions(
4810
+ new Error(
4811
+ "Agent run was cancelled before pending permission requests resolved."
4812
+ )
4813
+ );
4814
+ if (result?.text) {
4815
+ this.text = result.text;
4816
+ }
4817
+ if (result && "costData" in result) {
4818
+ this.costData = result.costData ?? null;
4819
+ }
4820
+ this.emitEvent(
4821
+ createNormalizedEvent(
4822
+ "run.cancelled",
4823
+ { provider: this.provider, runId: this.id },
4824
+ { text: this.text || void 0 }
4825
+ )
4826
+ );
4827
+ this.eventQueue.finish();
4828
+ this.rawQueue.finish();
4829
+ this.resolveFinished({
4830
+ id: this.id,
4831
+ provider: this.provider,
4832
+ sessionId: this.sessionId ?? "",
4833
+ text: this.text,
4834
+ rawEvents: [...this.rawEventsList],
4835
+ events: [...this.events],
4836
+ costData: this.costData,
4837
+ isCancelled: true
4480
4838
  });
4481
4839
  }
4482
4840
  fail(error) {
@@ -4503,7 +4861,17 @@ var AgentRunController = class {
4503
4861
  if (!this.sessionId) {
4504
4862
  this.rejectSessionIdReady(normalizedError);
4505
4863
  }
4506
- this.rejectFinished(normalizedError);
4864
+ this.resolveFinished({
4865
+ id: this.id,
4866
+ provider: this.provider,
4867
+ sessionId: this.sessionId ?? "",
4868
+ text: this.text,
4869
+ rawEvents: [...this.rawEventsList],
4870
+ events: [...this.events],
4871
+ costData: this.costData,
4872
+ isCancelled: false,
4873
+ error: normalizedError.message
4874
+ });
4507
4875
  }
4508
4876
  async abort() {
4509
4877
  this.abortRequested = true;
@@ -4558,7 +4926,7 @@ var Agent = class {
4558
4926
  * second `setup()` against the same sandbox does ~one round-trip of
4559
4927
  * work.
4560
4928
  */
4561
- async setup(config = {}) {
4929
+ async setup() {
4562
4930
  if (this.setupPromise) {
4563
4931
  await this.setupPromise;
4564
4932
  return;
@@ -4568,8 +4936,7 @@ var Agent = class {
4568
4936
  this.setupPromise = (async () => {
4569
4937
  await this.adapter.setup({
4570
4938
  provider: this.provider,
4571
- options: this.options,
4572
- config
4939
+ options: this.options
4573
4940
  });
4574
4941
  debugAgent(
4575
4942
  "setup() returned provider=%s after %dms",
@@ -4585,6 +4952,17 @@ var Agent = class {
4585
4952
  }
4586
4953
  }
4587
4954
  stream(runConfig) {
4955
+ if (runConfig.resumeSessionId && runConfig.forkSessionId) {
4956
+ throw new Error(
4957
+ "AgentRunConfig.resumeSessionId and forkSessionId are mutually exclusive."
4958
+ );
4959
+ }
4960
+ if (runConfig.forkSessionId && !runConfig.forkAtMessageId) {
4961
+ throw new Error("AgentRunConfig.forkSessionId requires forkAtMessageId.");
4962
+ }
4963
+ if (runConfig.forkAtMessageId && !runConfig.forkSessionId) {
4964
+ throw new Error("AgentRunConfig.forkAtMessageId requires forkSessionId.");
4965
+ }
4588
4966
  const runId = runConfig.runId ?? randomUUID2();
4589
4967
  const streamCalledAt = Date.now();
4590
4968
  debugAgent("stream() provider=%s runId=%s", this.provider, runId);