agentbox-sdk 0.1.318 → 0.1.322

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,8 +2,8 @@ import {
2
2
  Agent,
3
3
  agentboxRoot,
4
4
  getAgentLayout
5
- } from "../chunk-MDHCTHCY.js";
6
- import "../chunk-2DH5OVG4.js";
5
+ } from "../chunk-ZK5PDWOI.js";
6
+ import "../chunk-775FIGGL.js";
7
7
  import {
8
8
  AGENT_RESERVED_PORTS,
9
9
  collectAllAgentReservedPorts
@@ -189,9 +189,35 @@ function isRecord(value) {
189
189
  function clone(value) {
190
190
  return JSON.parse(JSON.stringify(value));
191
191
  }
192
+ function isCodexNoiseEvent(method) {
193
+ if (!method) return false;
194
+ if (method.startsWith("mcpServer/")) return true;
195
+ return method.toLowerCase().includes("delta");
196
+ }
192
197
  var CodexLogAssembler = class {
193
198
  byItemId = /* @__PURE__ */ new Map();
194
199
  textByItemId = /* @__PURE__ */ new Map();
200
+ // Non-item events (turn/completed usage, error notifications, thread
201
+ // responses). Returned by `process()` for the live channel; tracked here so
202
+ // `getSnapshots()` — the end-of-run persistence source — keeps them too.
203
+ passThroughSnapshots = [];
204
+ // First-seen chronological order across items and passthroughs.
205
+ snapshotOrder = [];
206
+ trackItem(itemId, snapshot) {
207
+ if (!this.byItemId.has(itemId)) {
208
+ this.snapshotOrder.push({ kind: "item", id: itemId });
209
+ }
210
+ this.byItemId.set(itemId, snapshot);
211
+ }
212
+ pushPassThrough(event) {
213
+ const passthrough = clone(event);
214
+ this.snapshotOrder.push({
215
+ kind: "passthrough",
216
+ index: this.passThroughSnapshots.length
217
+ });
218
+ this.passThroughSnapshots.push(passthrough);
219
+ return clone(passthrough);
220
+ }
195
221
  process(event) {
196
222
  if (!isRecord(event)) {
197
223
  return [];
@@ -201,7 +227,7 @@ var CodexLogAssembler = class {
201
227
  const item = isRecord(params.item) ? clone(params.item) : null;
202
228
  if (method === "item/started" || method === "item/updated" || method === "item/completed") {
203
229
  if (item && typeof item.id === "string") {
204
- this.byItemId.set(item.id, clone(event));
230
+ this.trackItem(item.id, clone(event));
205
231
  const text = typeof item.text === "string" ? item.text : void 0;
206
232
  if (text !== void 0) {
207
233
  this.textByItemId.set(item.id, text);
@@ -247,7 +273,8 @@ var CodexLogAssembler = class {
247
273
  })
248
274
  ];
249
275
  }
250
- return [clone(event)];
276
+ if (isCodexNoiseEvent(method)) return [];
277
+ return [this.pushPassThrough(event)];
251
278
  }
252
279
  /**
253
280
  * Repopulate state from a sequence of previously-assembled snapshots so the
@@ -256,12 +283,24 @@ var CodexLogAssembler = class {
256
283
  seed(snapshots) {
257
284
  this.byItemId.clear();
258
285
  this.textByItemId.clear();
286
+ this.passThroughSnapshots.length = 0;
287
+ this.snapshotOrder.length = 0;
259
288
  for (const snapshot of snapshots) {
260
289
  if (!isRecord(snapshot)) continue;
261
290
  const params = isRecord(snapshot.params) ? snapshot.params : {};
262
291
  const item = isRecord(params.item) ? params.item : null;
263
- if (!item || typeof item.id !== "string") continue;
264
- this.byItemId.set(item.id, clone(snapshot));
292
+ if (!item || typeof item.id !== "string") {
293
+ const method = typeof snapshot.method === "string" ? snapshot.method : void 0;
294
+ if (!isCodexNoiseEvent(method)) {
295
+ this.snapshotOrder.push({
296
+ kind: "passthrough",
297
+ index: this.passThroughSnapshots.length
298
+ });
299
+ this.passThroughSnapshots.push(clone(snapshot));
300
+ }
301
+ continue;
302
+ }
303
+ this.trackItem(item.id, clone(snapshot));
265
304
  const text = typeof item.text === "string" ? item.text : void 0;
266
305
  if (text !== void 0) {
267
306
  this.textByItemId.set(item.id, text);
@@ -288,11 +327,16 @@ var CodexLogAssembler = class {
288
327
  }
289
328
  }
290
329
  };
291
- this.byItemId.set(itemId, next);
330
+ this.trackItem(itemId, next);
292
331
  return clone(next);
293
332
  }
294
333
  getSnapshots() {
295
- return Array.from(this.byItemId.values()).map(clone);
334
+ const out = [];
335
+ for (const entry of this.snapshotOrder) {
336
+ const snapshot = entry.kind === "item" ? this.byItemId.get(entry.id) : this.passThroughSnapshots[entry.index];
337
+ if (snapshot) out.push(clone(snapshot));
338
+ }
339
+ return out;
296
340
  }
297
341
  };
298
342
  var OpenCodeLogAssembler = class {
@@ -314,6 +358,39 @@ var OpenCodeLogAssembler = class {
314
358
  childSessionToTaskCallId = /* @__PURE__ */ new Map();
315
359
  childMessageIdToTaskCallId = /* @__PURE__ */ new Map();
316
360
  messageIdToSessionId = /* @__PURE__ */ new Map();
361
+ // Part-less events (user `message.updated`, session lifecycle, errors).
362
+ // Returned by `process()` for the live channel; tracked here so
363
+ // `getSnapshots()` — the end-of-run persistence source — keeps them too.
364
+ // `message.updated` events are keyed by info.id (they re-emit on metadata
365
+ // updates, latest wins); everything else appends.
366
+ keyedPassThrough = /* @__PURE__ */ new Map();
367
+ passThroughSnapshots = [];
368
+ // First-seen chronological order across parts and passthroughs.
369
+ snapshotOrder = [];
370
+ trackPart(partId, snapshot) {
371
+ if (!this.byPartId.has(partId)) {
372
+ this.snapshotOrder.push({ kind: "part", id: partId });
373
+ }
374
+ this.byPartId.set(partId, snapshot);
375
+ }
376
+ pushPassThrough(event) {
377
+ const passthrough = clone(event);
378
+ const info = isRecord(event.properties) ? isRecord(event.properties.info) ? event.properties.info : null : null;
379
+ if (event.type === "message.updated" && info && typeof info.id === "string") {
380
+ const key = `message:${info.id}`;
381
+ if (!this.keyedPassThrough.has(key)) {
382
+ this.snapshotOrder.push({ kind: "keyed", key });
383
+ }
384
+ this.keyedPassThrough.set(key, passthrough);
385
+ return clone(passthrough);
386
+ }
387
+ this.snapshotOrder.push({
388
+ kind: "passthrough",
389
+ index: this.passThroughSnapshots.length
390
+ });
391
+ this.passThroughSnapshots.push(passthrough);
392
+ return clone(passthrough);
393
+ }
317
394
  process(event) {
318
395
  if (!isRecord(event)) {
319
396
  return [];
@@ -356,7 +433,7 @@ var OpenCodeLogAssembler = class {
356
433
  }
357
434
  if (type === "message.updated" && typeof info?.id === "string" && info.role === "user") {
358
435
  this.userMessageIds.add(info.id);
359
- return [clone(event)];
436
+ return [this.pushPassThrough(event)];
360
437
  }
361
438
  const parentTaskCallId = this.resolveParentTaskCallId(
362
439
  effectiveSid,
@@ -412,10 +489,10 @@ var OpenCodeLogAssembler = class {
412
489
  );
413
490
  }
414
491
  }
415
- this.byPartId.set(eventPart.id, enriched);
492
+ this.trackPart(eventPart.id, enriched);
416
493
  return [clone(enriched)];
417
494
  }
418
- return [clone(event)];
495
+ return [this.pushPassThrough(event)];
419
496
  }
420
497
  seed(snapshots) {
421
498
  this.userMessageIds.clear();
@@ -426,6 +503,9 @@ var OpenCodeLogAssembler = class {
426
503
  this.childSessionToTaskCallId.clear();
427
504
  this.childMessageIdToTaskCallId.clear();
428
505
  this.messageIdToSessionId.clear();
506
+ this.keyedPassThrough.clear();
507
+ this.passThroughSnapshots.length = 0;
508
+ this.snapshotOrder.length = 0;
429
509
  for (const snapshot of snapshots) {
430
510
  if (!isRecord(snapshot)) continue;
431
511
  const type = typeof snapshot.type === "string" ? snapshot.type : "";
@@ -440,11 +520,16 @@ var OpenCodeLogAssembler = class {
440
520
  }
441
521
  if (type === "message.updated" && typeof info?.id === "string" && info.role === "user") {
442
522
  this.userMessageIds.add(info.id);
523
+ this.pushPassThrough(snapshot);
443
524
  continue;
444
525
  }
445
526
  const part = isRecord(properties.part) ? properties.part : isRecord(snapshot.part) ? snapshot.part : null;
527
+ if (!part || typeof part.id !== "string") {
528
+ this.pushPassThrough(snapshot);
529
+ continue;
530
+ }
446
531
  if (part && typeof part.id === "string") {
447
- this.byPartId.set(part.id, clone(snapshot));
532
+ this.trackPart(part.id, clone(snapshot));
448
533
  if (typeof part.text === "string") {
449
534
  this.textByPartId.set(part.id, part.text);
450
535
  }
@@ -538,13 +623,27 @@ var OpenCodeLogAssembler = class {
538
623
  part
539
624
  }
540
625
  };
541
- this.byPartId.set(partId, next);
626
+ this.trackPart(partId, next);
542
627
  return clone(next);
543
628
  }
544
629
  getSnapshots() {
545
- return Array.from(this.byPartId.values()).map(clone);
630
+ const out = [];
631
+ for (const entry of this.snapshotOrder) {
632
+ const snapshot = entry.kind === "part" ? this.byPartId.get(entry.id) : entry.kind === "keyed" ? this.keyedPassThrough.get(entry.key) : this.passThroughSnapshots[entry.index];
633
+ if (snapshot) out.push(clone(snapshot));
634
+ }
635
+ return out;
546
636
  }
547
637
  };
638
+ function isClaudeNoiseEvent(event) {
639
+ const type = typeof event.type === "string" ? event.type : "";
640
+ if (type === "rate_limit_event" || type === "tool_progress") return true;
641
+ if (type === "system") {
642
+ const sub = typeof event.subtype === "string" ? event.subtype : "";
643
+ return sub === "status" || sub === "hook_started" || sub === "hook_response" || sub === "hook_progress" || sub === "api_retry" || sub === "auth_status";
644
+ }
645
+ return false;
646
+ }
548
647
  var ClaudeCodeLogAssembler = class {
549
648
  currentMessageId = null;
550
649
  textByMessageId = /* @__PURE__ */ new Map();
@@ -565,6 +664,12 @@ var ClaudeCodeLogAssembler = class {
565
664
  // insertion order so `getSnapshots()` returns the full trace, not just the
566
665
  // deduped assistant messages.
567
666
  passThroughSnapshots = [];
667
+ // First-seen chronological order of everything `getSnapshots()` returns:
668
+ // messages anchor at the position of their first event (message_start or
669
+ // first assistant block) and passthroughs at arrival. Without this the
670
+ // persisted trace would list every message first and every tool_result /
671
+ // system event after, scrambling replay order for sequential consumers.
672
+ snapshotOrder = [];
568
673
  process(event) {
569
674
  if (!isRecord(event)) return [];
570
675
  const type = typeof event.type === "string" ? event.type : "";
@@ -606,24 +711,32 @@ var ClaudeCodeLogAssembler = class {
606
711
  const message = isRecord(event.message) ? event.message : null;
607
712
  const id = message && typeof message.id === "string" ? message.id : null;
608
713
  if (!id || !message) {
609
- const passthrough2 = clone(event);
610
- this.passThroughSnapshots.push(passthrough2);
611
- return [clone(passthrough2)];
714
+ return [this.pushPassThrough(event)];
612
715
  }
613
716
  this.setParentToolUseId(id, event);
614
717
  const final = extractClaudeAssistantContent(message);
615
- this.textByMessageId.set(id, final.text);
616
- if (final.thinking) {
718
+ const streamedText = this.textByMessageId.get(id) ?? "";
719
+ if (final.text.length > streamedText.length) {
720
+ this.textByMessageId.set(id, final.text);
721
+ }
722
+ const streamedThinking = this.thinkingByMessageId.get(id) ?? "";
723
+ if (final.thinking.length > streamedThinking.length) {
617
724
  this.thinkingByMessageId.set(id, final.thinking);
618
725
  }
619
726
  this.mergeExtraBlocks(id, final.extraBlocks);
620
- const snapshot = this.upsertMessage(id);
621
- this.currentMessageId = null;
622
- return [snapshot];
727
+ return [this.upsertMessage(id)];
623
728
  }
729
+ if (isClaudeNoiseEvent(event)) return [];
730
+ return [this.pushPassThrough(event)];
731
+ }
732
+ pushPassThrough(event) {
624
733
  const passthrough = clone(event);
734
+ this.snapshotOrder.push({
735
+ kind: "passthrough",
736
+ index: this.passThroughSnapshots.length
737
+ });
625
738
  this.passThroughSnapshots.push(passthrough);
626
- return [clone(passthrough)];
739
+ return clone(passthrough);
627
740
  }
628
741
  seed(snapshots) {
629
742
  this.currentMessageId = null;
@@ -633,15 +746,26 @@ var ClaudeCodeLogAssembler = class {
633
746
  this.byMessageId.clear();
634
747
  this.extraBlocksByMessageId.clear();
635
748
  this.passThroughSnapshots.length = 0;
749
+ this.snapshotOrder.length = 0;
636
750
  for (const snapshot of snapshots) {
637
751
  if (!isRecord(snapshot)) continue;
638
752
  if (snapshot.type !== "message.updated") {
639
- this.passThroughSnapshots.push(clone(snapshot));
753
+ if (!isClaudeNoiseEvent(snapshot)) {
754
+ this.snapshotOrder.push({
755
+ kind: "passthrough",
756
+ index: this.passThroughSnapshots.length
757
+ });
758
+ this.passThroughSnapshots.push(clone(snapshot));
759
+ }
640
760
  continue;
641
761
  }
642
762
  const messageId = typeof snapshot.messageId === "string" ? snapshot.messageId : null;
643
763
  if (!messageId) continue;
764
+ if (!this.byMessageId.has(messageId)) {
765
+ this.snapshotOrder.push({ kind: "message", id: messageId });
766
+ }
644
767
  this.byMessageId.set(messageId, clone(snapshot));
768
+ this.currentMessageId = messageId;
645
769
  const parentToolUseId = typeof snapshot.parent_tool_use_id === "string" ? snapshot.parent_tool_use_id : null;
646
770
  this.parentToolUseIdByMessageId.set(messageId, parentToolUseId);
647
771
  const message = isRecord(snapshot.message) ? snapshot.message : null;
@@ -707,13 +831,18 @@ var ClaudeCodeLogAssembler = class {
707
831
  content
708
832
  }
709
833
  };
834
+ if (!this.byMessageId.has(messageId)) {
835
+ this.snapshotOrder.push({ kind: "message", id: messageId });
836
+ }
710
837
  this.byMessageId.set(messageId, next);
711
838
  return clone(next);
712
839
  }
713
840
  getSnapshots() {
714
841
  const out = [];
715
- for (const snapshot of this.byMessageId.values()) out.push(clone(snapshot));
716
- for (const snapshot of this.passThroughSnapshots) out.push(clone(snapshot));
842
+ for (const entry of this.snapshotOrder) {
843
+ const snapshot = entry.kind === "message" ? this.byMessageId.get(entry.id) : this.passThroughSnapshots[entry.index];
844
+ if (snapshot) out.push(clone(snapshot));
845
+ }
717
846
  return out;
718
847
  }
719
848
  };
@@ -254,6 +254,16 @@ function resolveSandboxResources(resources) {
254
254
  var DaytonaSandboxAdapter = class extends SandboxAdapter {
255
255
  client;
256
256
  sandbox;
257
+ /**
258
+ * Per-sandbox preview access token, captured from `getPreviewLink`.
259
+ * Daytona's preview proxy now requires this token to reach a sandbox's
260
+ * ports: unauthenticated requests get 307-redirected to an Auth0 login
261
+ * (which surfaces as a 307 on WebSocket upgrades and a 404 on plain GETs).
262
+ * The token is sandbox-level and stable across ports, so caching the most
263
+ * recent one is sufficient — every consumer calls `getPreviewLink` to build
264
+ * the URL right before reading `previewHeaders`. See {@link previewHeaders}.
265
+ */
266
+ previewToken;
257
267
  constructor(options) {
258
268
  super(options);
259
269
  this.client = new Daytona({
@@ -494,12 +504,26 @@ var DaytonaSandboxAdapter = class extends SandboxAdapter {
494
504
  }
495
505
  async openPort(port) {
496
506
  this.requireProvisioned();
497
- await this.requireSandbox().getPreviewLink(port);
507
+ const preview = await this.requireSandbox().getPreviewLink(port);
508
+ this.previewToken = preview.token;
509
+ }
510
+ /**
511
+ * Headers callers must attach to HTTP/WebSocket requests against this
512
+ * sandbox's preview URLs. Daytona private sandboxes gate their preview
513
+ * proxy behind `x-daytona-preview-token`; without it the proxy 307-redirects
514
+ * to Auth0, which breaks every provider (claude-code `/start` 404, codex WS
515
+ * "307", opencode `/session` 404). The token is captured lazily from
516
+ * `getPreviewLink`/`openPort`, both of which every consumer calls to build
517
+ * the URL immediately before reading these headers.
518
+ */
519
+ get previewHeaders() {
520
+ return this.previewToken ? { "x-daytona-preview-token": this.previewToken } : {};
498
521
  }
499
522
  async getPreviewLink(port) {
500
523
  this.requireProvisioned();
501
524
  const sandbox = this.requireSandbox();
502
525
  const preview = await sandbox.getPreviewLink(port);
526
+ this.previewToken = preview.token;
503
527
  return preview.url;
504
528
  }
505
529
  async uploadFile(content, targetPath) {
@@ -2,7 +2,7 @@ import {
2
2
  createNormalizedEvent,
3
3
  normalizeRawAgentEvent,
4
4
  toAISDKStream
5
- } from "./chunk-2DH5OVG4.js";
5
+ } from "./chunk-775FIGGL.js";
6
6
  import {
7
7
  AgentBoxError,
8
8
  AsyncQueue,
@@ -1244,16 +1244,23 @@ async function preflightSetup(target, setupId, daemon) {
1244
1244
  );
1245
1245
  }
1246
1246
  async function markSetupComplete(target, setupId) {
1247
- await time(
1248
- debugSetup,
1249
- `markSetupComplete ${target.provider}`,
1250
- () => target.runCommand(
1251
- [
1252
- `mkdir -p ${shellQuote(target.layout.rootDir)}`,
1253
- `printf '%s' ${shellQuote(setupId)} > ${shellQuote(path5.posix.join(target.layout.rootDir, SETUP_ID_FILENAME))}`
1254
- ].join(" && ")
1255
- )
1256
- );
1247
+ await time(debugSetup, `markSetupComplete ${target.provider}`, async () => {
1248
+ const setupIdFile = path5.posix.join(
1249
+ target.layout.rootDir,
1250
+ SETUP_ID_FILENAME
1251
+ );
1252
+ const result = await target.uploadAndRun(
1253
+ [{ path: setupIdFile, content: setupId }],
1254
+ "true"
1255
+ );
1256
+ if (result.exitCode !== 0) {
1257
+ const detail = result.combinedOutput?.trim();
1258
+ throw new Error(
1259
+ `markSetupComplete failed (${result.exitCode})${detail ? `
1260
+ ${detail}` : ""}`
1261
+ );
1262
+ }
1263
+ });
1257
1264
  }
1258
1265
  function buildInstallScript(rootDir, installCommandsByKey) {
1259
1266
  const commandsB64 = Buffer.from(
@@ -1641,6 +1648,8 @@ var DAEMON_PORT = 43180;
1641
1648
  var DAEMON_PATH = "/tmp/agentbox/claude-code/daemon.mjs";
1642
1649
  var DAEMON_LOG_PATH = "/tmp/agentbox/claude-code/daemon.log";
1643
1650
  var DAEMON_PID_PATH = "/tmp/agentbox/claude-code/daemon.pid";
1651
+ var DAEMON_READY_TIMEOUT_MS = 3e4;
1652
+ var DAEMON_READY_POLL_INTERVAL_MS = 250;
1644
1653
  function claudeConfigDir(options) {
1645
1654
  return path8.join(
1646
1655
  agentboxRoot(AgentProvider.ClaudeCode, Boolean(options.sandbox)),
@@ -2078,16 +2087,44 @@ async function ensureClaudeCodeDaemonUncached(options, env) {
2078
2087
  `Could not start claude-code daemon: ${launch.stderr || launch.combinedOutput || "(no output)"}`
2079
2088
  );
2080
2089
  }
2090
+ const deadline = Date.now() + DAEMON_READY_TIMEOUT_MS;
2091
+ let lastProbe = "";
2092
+ while (Date.now() < deadline) {
2093
+ const ready = await sandbox.run(
2094
+ `curl -fsS --max-time 1 http://127.0.0.1:${DAEMON_PORT}/__version 2>/dev/null`,
2095
+ { cwd: options.cwd, timeoutMs: 1e4 }
2096
+ );
2097
+ lastProbe = ready.combinedOutput.trim();
2098
+ if (ready.exitCode === 0 && lastProbe === DAEMON_PROTOCOL_VERSION) {
2099
+ return;
2100
+ }
2101
+ await sleep(DAEMON_READY_POLL_INTERVAL_MS);
2102
+ }
2103
+ const logTail = await sandbox.run(`tail -n 20 ${shellQuote(DAEMON_LOG_PATH)} 2>/dev/null`, {
2104
+ cwd: options.cwd
2105
+ }).catch(() => void 0);
2106
+ throw new Error(
2107
+ `claude-code daemon did not become ready within ${DAEMON_READY_TIMEOUT_MS}ms` + (lastProbe ? ` (last /__version response: ${lastProbe})` : "") + (logTail?.combinedOutput ? `
2108
+ ${logTail.combinedOutput}` : "")
2109
+ );
2081
2110
  });
2082
2111
  }
2083
2112
  var DAEMON_FIRST_REQUEST_RETRY_BUDGET_MS = 3e4;
2084
2113
  var DAEMON_FIRST_REQUEST_RETRY_INTERVAL_MS = 250;
2114
+ var TRANSIENT_PROXY_STATUSES = /* @__PURE__ */ new Set([404, 502, 503, 504]);
2085
2115
  async function fetchWithDaemonRetry(input, init) {
2086
2116
  const deadline = Date.now() + DAEMON_FIRST_REQUEST_RETRY_BUDGET_MS;
2087
2117
  let lastError;
2118
+ let lastResponse;
2088
2119
  while (Date.now() < deadline) {
2089
2120
  try {
2090
- return await fetch(input, init);
2121
+ const response = await fetch(input, init);
2122
+ if (TRANSIENT_PROXY_STATUSES.has(response.status)) {
2123
+ lastResponse = response;
2124
+ await sleep(DAEMON_FIRST_REQUEST_RETRY_INTERVAL_MS);
2125
+ continue;
2126
+ }
2127
+ return response;
2091
2128
  } catch (error) {
2092
2129
  lastError = error;
2093
2130
  const aborted = error?.name === "AbortError";
@@ -2097,6 +2134,9 @@ async function fetchWithDaemonRetry(input, init) {
2097
2134
  await sleep(DAEMON_FIRST_REQUEST_RETRY_INTERVAL_MS);
2098
2135
  }
2099
2136
  }
2137
+ if (lastResponse) {
2138
+ return lastResponse;
2139
+ }
2100
2140
  throw lastError ?? new Error("claude-code daemon request timed out");
2101
2141
  }
2102
2142
  async function* parseNdjsonStream(body) {
@@ -2328,6 +2368,7 @@ var ClaudeCodeAgentAdapter = class {
2328
2368
  )
2329
2369
  );
2330
2370
  let accumulatedText = "";
2371
+ let streamedThinkingChars = 0;
2331
2372
  let pendingMessages = 1;
2332
2373
  let firstStreamEventLogged = false;
2333
2374
  let firstTextDeltaLogged = false;
@@ -2399,8 +2440,15 @@ var ClaudeCodeAgentAdapter = class {
2399
2440
  );
2400
2441
  }
2401
2442
  const partial = message;
2443
+ if (partial.parent_tool_use_id) continue;
2444
+ const streamType = partial.event?.type;
2445
+ if (streamType === "message_start") {
2446
+ accumulatedText = "";
2447
+ streamedThinkingChars = 0;
2448
+ }
2402
2449
  const { text, thinking } = extractStreamDeltas(partial);
2403
2450
  if (thinking) {
2451
+ streamedThinkingChars += thinking.length;
2404
2452
  sink.emitEvent(
2405
2453
  createNormalizedEvent(
2406
2454
  "reasoning.delta",
@@ -2430,8 +2478,9 @@ var ClaudeCodeAgentAdapter = class {
2430
2478
  }
2431
2479
  if (message.type === "assistant") {
2432
2480
  const asst = message;
2481
+ if (asst.parent_tool_use_id) continue;
2433
2482
  const thinking = extractAssistantThinking(asst);
2434
- if (thinking) {
2483
+ if (thinking && streamedThinkingChars === 0) {
2435
2484
  sink.emitEvent(
2436
2485
  createNormalizedEvent(
2437
2486
  "reasoning.delta",
@@ -4138,42 +4187,79 @@ async function ensureSandboxOpenCodeServer(request) {
4138
4187
  `disown 2>/dev/null || true`
4139
4188
  ].join(" ")})`
4140
4189
  ].join(" && ");
4141
- await killSandboxOpenCodeServer(sandbox, pidFilePath, options.cwd, port);
4142
- const launchResult = await time(
4190
+ const OPENCODE_MAX_LAUNCH_ATTEMPTS = 4;
4191
+ const OPENCODE_RELAUNCH_BACKOFF_MS = 1e3;
4192
+ const readyDeadline = Date.now() + SANDBOX_OPENCODE_READY_TIMEOUT_MS;
4193
+ const pidAlive = `kill -0 "$(cat ${shellQuote(pidFilePath)} 2>/dev/null)" 2>/dev/null`;
4194
+ let lastLog = "";
4195
+ const becameReady = await time(
4143
4196
  debugOpencode,
4144
- "spawn opencode serve",
4145
- () => sandbox.run(launchCommand, {
4146
- cwd: options.cwd,
4147
- env: serveEnv,
4148
- timeoutMs: 2e4
4149
- })
4150
- );
4151
- if (launchResult.exitCode !== 0) {
4152
- await target.cleanup().catch(() => void 0);
4153
- throw new Error(
4154
- `Could not start OpenCode server: ${launchResult.combinedOutput || launchResult.stderr}`
4155
- );
4156
- }
4157
- await time(debugOpencode, "poll opencode until ready", async () => {
4158
- const readyDeadline = Date.now() + SANDBOX_OPENCODE_READY_TIMEOUT_MS;
4159
- let attempt = 0;
4160
- while (Date.now() < readyDeadline) {
4161
- attempt++;
4162
- const probe = await sandbox.run(
4163
- `curl -fsS http://127.0.0.1:${port}/global/health >/dev/null 2>&1`,
4164
- { cwd: options.cwd, timeoutMs: 5e3 }
4165
- );
4166
- if (probe.exitCode === 0) {
4167
- debugOpencode("ready after %d probe attempt(s)", attempt);
4168
- return;
4197
+ "launch + poll opencode until ready",
4198
+ async () => {
4199
+ for (let attempt = 1; attempt <= OPENCODE_MAX_LAUNCH_ATTEMPTS && Date.now() < readyDeadline; attempt++) {
4200
+ await killSandboxOpenCodeServer(
4201
+ sandbox,
4202
+ pidFilePath,
4203
+ options.cwd,
4204
+ port
4205
+ );
4206
+ const launchResult = await sandbox.run(launchCommand, {
4207
+ cwd: options.cwd,
4208
+ env: serveEnv,
4209
+ timeoutMs: 4e4
4210
+ });
4211
+ if (launchResult.exitCode !== 0) {
4212
+ await target.cleanup().catch(() => void 0);
4213
+ throw new Error(
4214
+ `Could not start OpenCode server: ${launchResult.combinedOutput || launchResult.stderr}`
4215
+ );
4216
+ }
4217
+ while (Date.now() < readyDeadline) {
4218
+ const probe = await sandbox.run(
4219
+ `curl -fsS http://127.0.0.1:${port}/global/health >/dev/null 2>&1`,
4220
+ { cwd: options.cwd, timeoutMs: 5e3 }
4221
+ );
4222
+ if (probe.exitCode === 0) {
4223
+ debugOpencode("ready on attempt %d", attempt);
4224
+ return true;
4225
+ }
4226
+ const alive = await sandbox.run(pidAlive, {
4227
+ cwd: options.cwd,
4228
+ timeoutMs: 5e3
4229
+ });
4230
+ if (alive.exitCode !== 0) {
4231
+ lastLog = (await sandbox.run(`tail -n 40 ${shellQuote(logFilePath)} 2>/dev/null`, {
4232
+ cwd: options.cwd
4233
+ }).catch(() => void 0))?.combinedOutput?.trim() ?? lastLog;
4234
+ debugOpencode(
4235
+ "opencode died on attempt %d/%d; relaunching. log:\n%s",
4236
+ attempt,
4237
+ OPENCODE_MAX_LAUNCH_ATTEMPTS,
4238
+ lastLog
4239
+ );
4240
+ break;
4241
+ }
4242
+ await sleep(500);
4243
+ }
4244
+ if (Date.now() >= readyDeadline) break;
4245
+ await sleep(OPENCODE_RELAUNCH_BACKOFF_MS);
4169
4246
  }
4170
- await sleep(500);
4247
+ return false;
4248
+ }
4249
+ );
4250
+ if (!becameReady) {
4251
+ if (!lastLog) {
4252
+ lastLog = (await sandbox.run(`tail -n 40 ${shellQuote(logFilePath)} 2>/dev/null`, {
4253
+ cwd: options.cwd
4254
+ }).catch(() => void 0))?.combinedOutput?.trim() ?? "";
4171
4255
  }
4172
4256
  await target.cleanup().catch(() => void 0);
4173
4257
  throw new Error(
4174
- `OpenCode server did not become ready within ${SANDBOX_OPENCODE_READY_TIMEOUT_MS}ms.`
4258
+ `OpenCode server did not become ready within ${SANDBOX_OPENCODE_READY_TIMEOUT_MS}ms.` + (lastLog ? `
4259
+ opencode log:
4260
+ ${lastLog}` : "")
4175
4261
  );
4176
- });
4262
+ }
4177
4263
  await markSetupComplete(target, setupId);
4178
4264
  });
4179
4265
  }
@@ -4,7 +4,7 @@ import {
4
4
  normalizeRawAgentEvent,
5
5
  toAISDKEvent,
6
6
  toAISDKStream
7
- } from "../chunk-2DH5OVG4.js";
7
+ } from "../chunk-775FIGGL.js";
8
8
  import "../chunk-GOFJNFAD.js";
9
9
  export {
10
10
  ProviderLogAssembler,
package/dist/index.js CHANGED
@@ -2,19 +2,19 @@ import {
2
2
  Agent,
3
3
  agentboxRoot,
4
4
  getAgentLayout
5
- } from "./chunk-MDHCTHCY.js";
5
+ } from "./chunk-ZK5PDWOI.js";
6
6
  import {
7
7
  ProviderLogAssembler,
8
8
  createNormalizedEvent,
9
9
  normalizeRawAgentEvent,
10
10
  toAISDKEvent,
11
11
  toAISDKStream
12
- } from "./chunk-2DH5OVG4.js";
12
+ } from "./chunk-775FIGGL.js";
13
13
  import {
14
14
  Sandbox,
15
15
  SandboxAdapter,
16
16
  buildGitCloneCommand
17
- } from "./chunk-HYHLKO3L.js";
17
+ } from "./chunk-T4AS2WEF.js";
18
18
  import {
19
19
  AGENT_RESERVED_PORTS,
20
20
  collectAllAgentReservedPorts
@@ -2,7 +2,7 @@ import {
2
2
  Sandbox,
3
3
  SandboxAdapter,
4
4
  buildGitCloneCommand
5
- } from "../chunk-HYHLKO3L.js";
5
+ } from "../chunk-T4AS2WEF.js";
6
6
  import "../chunk-AVXJMCBC.js";
7
7
  import "../chunk-NSJM57Z4.js";
8
8
  import {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentbox-sdk",
3
- "version": "0.1.318",
3
+ "version": "0.1.322",
4
4
  "description": "Swappable coding agents and sandbox providers for Bun and TypeScript.",
5
5
  "license": "MIT",
6
6
  "repository": {