@bunny-agent/daemon 0.9.28 → 0.9.29-beta.10

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.
package/dist/nextjs.js CHANGED
@@ -116838,7 +116838,7 @@ var require_core3 = __commonJS({
116838
116838
  return match2 && match2.index === 0;
116839
116839
  }
116840
116840
  var BACKREF_RE = /\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;
116841
- function join38(regexps, separator = "|") {
116841
+ function join39(regexps, separator = "|") {
116842
116842
  let numCaptures = 0;
116843
116843
  return regexps.map((regex2) => {
116844
116844
  numCaptures += 1;
@@ -117142,7 +117142,7 @@ var require_core3 = __commonJS({
117142
117142
  this.exec = () => null;
117143
117143
  }
117144
117144
  const terminators = this.regexes.map((el) => el[1]);
117145
- this.matcherRe = langRe(join38(terminators), true);
117145
+ this.matcherRe = langRe(join39(terminators), true);
117146
117146
  this.lastIndex = 0;
117147
117147
  }
117148
117148
  /** @param {string} s */
@@ -168060,14 +168060,14 @@ var require_graceful_fs = __commonJS({
168060
168060
  return close;
168061
168061
  })(fs16.close);
168062
168062
  fs16.closeSync = (function(fs$closeSync) {
168063
- function closeSync2(fd) {
168063
+ function closeSync3(fd) {
168064
168064
  fs$closeSync.apply(fs16, arguments);
168065
168065
  resetQueue();
168066
168066
  }
168067
- Object.defineProperty(closeSync2, previousSymbol, {
168067
+ Object.defineProperty(closeSync3, previousSymbol, {
168068
168068
  value: fs$closeSync
168069
168069
  });
168070
- return closeSync2;
168070
+ return closeSync3;
168071
168071
  })(fs16.closeSync);
168072
168072
  if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || "")) {
168073
168073
  process.on("exit", function() {
@@ -206492,10 +206492,15 @@ async function fsStat(state, q2) {
206492
206492
  const root = resolveVolumeRoot(state, q2.volume);
206493
206493
  const target = resolveUnderRoot(root, q2.path);
206494
206494
  const stat6 = await fs2.stat(target);
206495
+ const created_at = msToIsoOrNull(stat6.birthtimeMs) ?? msToIsoOrNull(stat6.ctimeMs);
206495
206496
  return ok({
206496
206497
  path: target,
206497
206498
  is_dir: stat6.isDirectory(),
206498
- size: stat6.isFile() ? stat6.size : 0
206499
+ size: stat6.isFile() ? stat6.size : 0,
206500
+ created_at,
206501
+ modified_at: msToIsoOrNull(
206502
+ stat6.mtimeMs
206503
+ )
206499
206504
  });
206500
206505
  }
206501
206506
  async function fsExists(state, q2) {
@@ -208375,7 +208380,7 @@ function createOpenCodeRunner(options2 = {}) {
208375
208380
 
208376
208381
  // ../../packages/runner-pi/dist/pi-runner.js
208377
208382
  import { appendFileSync as appendFileSync5, existsSync as existsSync28, unlinkSync as unlinkSync6 } from "node:fs";
208378
- import { join as join35 } from "node:path";
208383
+ import { join as join36 } from "node:path";
208379
208384
 
208380
208385
  // ../../node_modules/.pnpm/@mariozechner+pi-ai@0.64.0_ws@8.19.0_zod@4.3.6/node_modules/@mariozechner/pi-ai/dist/index.js
208381
208386
  var dist_exports = {};
@@ -263361,25 +263366,146 @@ var generateImageSchema = {
263361
263366
  },
263362
263367
  quality: {
263363
263368
  type: "string",
263364
- enum: ["standard", "hd"],
263365
- description: "Image quality (OpenAI only). Defaults to standard."
263369
+ enum: ["low", "medium", "high", "auto"],
263370
+ description: "Image quality. Defaults to auto."
263366
263371
  }
263367
263372
  },
263368
263373
  required: ["prompt"],
263369
263374
  additionalProperties: false
263370
263375
  };
263371
- async function resolveB64(item) {
263376
+ async function resolveB64(item, apiKey) {
263372
263377
  if (item.b64_json)
263373
263378
  return item.b64_json;
263374
- if (item.url) {
263375
- const res = await fetch(item.url);
263379
+ if (item.b64Json)
263380
+ return item.b64Json;
263381
+ if (item.image_base64)
263382
+ return item.image_base64;
263383
+ if (item.imageBase64)
263384
+ return item.imageBase64;
263385
+ if (item.base64)
263386
+ return item.base64;
263387
+ if (typeof item.image === "string")
263388
+ return item.image;
263389
+ if (item.image?.b64_json)
263390
+ return item.image.b64_json;
263391
+ if (item.image?.base64)
263392
+ return item.image.base64;
263393
+ const url = item.url ?? item.image_url ?? item.imageUrl ?? item.image?.url;
263394
+ if (url) {
263395
+ const headers = {};
263396
+ if (apiKey) {
263397
+ headers.Authorization = `Bearer ${apiKey}`;
263398
+ }
263399
+ const res = await fetch(url, { headers });
263376
263400
  if (res.ok)
263377
263401
  return Buffer.from(await res.arrayBuffer()).toString("base64");
263378
263402
  }
263379
263403
  return void 0;
263380
263404
  }
263381
- async function saveImageItem(item, filePath) {
263382
- const b64 = await resolveB64(item);
263405
+ function pickImageItem(response) {
263406
+ const tryFromObject = (value2) => {
263407
+ if (!value2 || typeof value2 !== "object")
263408
+ return void 0;
263409
+ const obj = value2;
263410
+ return {
263411
+ b64_json: obj.b64_json ?? obj.b64Json,
263412
+ b64Json: obj.b64Json,
263413
+ url: obj.url ?? obj.imageUrl,
263414
+ image_base64: obj.image_base64 ?? obj.imageBase64,
263415
+ imageBase64: obj.imageBase64,
263416
+ image_url: obj.image_url ?? obj.imageUrl,
263417
+ imageUrl: obj.imageUrl,
263418
+ base64: obj.base64,
263419
+ image: obj.image
263420
+ };
263421
+ };
263422
+ const asItem = (value2) => {
263423
+ if (value2 == null)
263424
+ return void 0;
263425
+ if (typeof value2 === "string") {
263426
+ return { base64: value2 };
263427
+ }
263428
+ if (typeof value2 === "object") {
263429
+ const normalized = tryFromObject(value2);
263430
+ if (normalized)
263431
+ return normalized;
263432
+ }
263433
+ return void 0;
263434
+ };
263435
+ const fromDataArray = Array.isArray(response.data) ? asItem(response.data[0]) : void 0;
263436
+ if (fromDataArray)
263437
+ return fromDataArray;
263438
+ const fromDataValue = asItem(response.data);
263439
+ if (fromDataValue)
263440
+ return fromDataValue;
263441
+ const responseRecord = response;
263442
+ const imagesValue = responseRecord.images;
263443
+ const outputValue = responseRecord.output;
263444
+ const fromImagesArray = Array.isArray(imagesValue) ? asItem(imagesValue[0]) : void 0;
263445
+ if (fromImagesArray)
263446
+ return fromImagesArray;
263447
+ const fromImagesValue = asItem(imagesValue);
263448
+ if (fromImagesValue)
263449
+ return fromImagesValue;
263450
+ const fromOutputArray = Array.isArray(outputValue) ? asItem(outputValue[0]) : void 0;
263451
+ if (fromOutputArray)
263452
+ return fromOutputArray;
263453
+ const fromOutputValue = asItem(outputValue);
263454
+ if (fromOutputValue)
263455
+ return fromOutputValue;
263456
+ const fromTopLevel = asItem(response);
263457
+ if (fromTopLevel)
263458
+ return fromTopLevel;
263459
+ const queue = [response];
263460
+ while (queue.length > 0) {
263461
+ const current = queue.shift();
263462
+ if (current == null)
263463
+ continue;
263464
+ if (typeof current === "string") {
263465
+ if (/^[A-Za-z0-9+/=]{32,}$/.test(current))
263466
+ return { base64: current };
263467
+ continue;
263468
+ }
263469
+ if (typeof current !== "object")
263470
+ continue;
263471
+ const normalized = tryFromObject(current);
263472
+ if (normalized) {
263473
+ const hasUsefulField = Boolean(normalized.b64_json ?? normalized.b64Json ?? normalized.image_base64 ?? normalized.imageBase64 ?? normalized.base64 ?? normalized.url ?? normalized.image_url ?? normalized.imageUrl ?? (typeof normalized.image === "string" ? normalized.image : normalized.image?.b64_json ?? normalized.image?.base64 ?? normalized.image?.url));
263474
+ if (hasUsefulField)
263475
+ return normalized;
263476
+ }
263477
+ if (Array.isArray(current)) {
263478
+ queue.push(...current);
263479
+ continue;
263480
+ }
263481
+ for (const value2 of Object.values(current)) {
263482
+ queue.push(value2);
263483
+ }
263484
+ }
263485
+ return {};
263486
+ }
263487
+ function detectImageMime(filePath) {
263488
+ const ext2 = extname2(filePath).toLowerCase();
263489
+ if (ext2 === ".jpg" || ext2 === ".jpeg")
263490
+ return "image/jpeg";
263491
+ if (ext2 === ".webp")
263492
+ return "image/webp";
263493
+ if (ext2 === ".gif")
263494
+ return "image/gif";
263495
+ return "image/png";
263496
+ }
263497
+ function buildPolicySafeEditPrompt(prompt) {
263498
+ const riskyPattern = /\b(watermark|watermarks|logo|logos|copyright|brand mark|remove branding)\b/i;
263499
+ if (!riskyPattern.test(prompt)) {
263500
+ return { prompt, rewritten: false };
263501
+ }
263502
+ return {
263503
+ prompt: "Clean up distracting overlay text or marks naturally while preserving the original scene, style, and layout. Keep the result seamless and high quality.",
263504
+ rewritten: true
263505
+ };
263506
+ }
263507
+ async function saveImageItem(item, filePath, apiKey) {
263508
+ const b64 = await resolveB64(item, apiKey);
263383
263509
  if (!b64)
263384
263510
  return void 0;
263385
263511
  mkdirSync10(dirname19(filePath), { recursive: true });
@@ -263399,11 +263525,11 @@ function buildImageGenerateTool(cwd, imageModelId, baseUrl, apiKey) {
263399
263525
  ],
263400
263526
  // biome-ignore lint/suspicious/noExplicitAny: plain JSON Schema compatible with TypeBox TSchema
263401
263527
  parameters: generateImageSchema,
263402
- async execute(_toolCallId, params, _signal, _onUpdate) {
263528
+ async execute(_toolCallId, params, signal, _onUpdate) {
263403
263529
  const p = params;
263404
263530
  const prompt = p.prompt;
263405
263531
  const size = p.size ?? "1024x1024";
263406
- const quality = p.quality ?? "standard";
263532
+ const quality = p.quality ?? "auto";
263407
263533
  const rawFilename = p.filename;
263408
263534
  const filename = rawFilename ? extname2(rawFilename) ? rawFilename : `${rawFilename}.png` : `image_${Date.now()}.png`;
263409
263535
  const filePath = join34(cwd, filename.replace(/[^a-zA-Z0-9_\-./]/g, "_"));
@@ -263420,24 +263546,28 @@ function buildImageGenerateTool(cwd, imageModelId, baseUrl, apiKey) {
263420
263546
  prompt,
263421
263547
  n: 1,
263422
263548
  size,
263423
- quality
263424
- })
263549
+ quality,
263550
+ response_format: "b64_json",
263551
+ output_format: "png"
263552
+ }),
263553
+ signal
263425
263554
  });
263426
263555
  if (!res.ok) {
263427
263556
  throw new Error(`Image generation failed (${res.status}): ${await res.text()}`);
263428
263557
  }
263429
263558
  const json = await res.json();
263430
- const item = json.data?.[0] ?? {};
263431
- const savedPath = await saveImageItem(item, filePath);
263559
+ const item = pickImageItem(json);
263560
+ const savedPath = await saveImageItem(item, filePath, apiKey);
263432
263561
  return {
263433
263562
  content: [
263434
263563
  {
263435
263564
  type: "text",
263436
- text: savedPath ?? "Image generated but could not be saved."
263565
+ text: savedPath ?? `Image generated but could not be saved: no image payload returned; image_model: ${imageModelId}`
263437
263566
  }
263438
263567
  ],
263439
263568
  details: {
263440
263569
  filePath: savedPath,
263570
+ ...json.usage != null ? { usage: { raw: { [imageModelId]: json.usage } } } : {},
263441
263571
  response: json
263442
263572
  }
263443
263573
  };
@@ -263453,13 +263583,504 @@ function buildImageGenerateTool(cwd, imageModelId, baseUrl, apiKey) {
263453
263583
  }
263454
263584
  };
263455
263585
  }
263586
+ var editImageSchema = {
263587
+ type: "object",
263588
+ properties: {
263589
+ image: {
263590
+ type: "string",
263591
+ description: "Path to the source image file to edit (relative to working directory or absolute)."
263592
+ },
263593
+ prompt: {
263594
+ type: "string",
263595
+ description: "Text description of the desired final image. Describe the full result, not just the change."
263596
+ },
263597
+ mask: {
263598
+ type: "string",
263599
+ description: "Optional path to a mask image (PNG with transparent areas indicating where to edit). If omitted, the model decides what to change based on the prompt."
263600
+ },
263601
+ filename: {
263602
+ type: "string",
263603
+ description: "Output filename with extension, e.g. 'edited_cat.png'. Defaults to a timestamp-based name."
263604
+ },
263605
+ size: {
263606
+ type: "string",
263607
+ enum: ["1024x1024", "1024x1536", "1536x1024", "auto"],
263608
+ description: "Output image dimensions. Optional; omit or set auto to let model decide."
263609
+ },
263610
+ quality: {
263611
+ type: "string",
263612
+ enum: ["low", "medium", "high", "auto"],
263613
+ description: "Image quality. Optional; omit or set auto to let model decide."
263614
+ }
263615
+ },
263616
+ required: ["image", "prompt"],
263617
+ additionalProperties: false
263618
+ };
263619
+ function buildMultipartBody(fields, files) {
263620
+ const boundary = `----SandagentBoundary${Date.now()}${Math.random().toString(36).slice(2)}`;
263621
+ const parts = [];
263622
+ for (const { name, value: value2 } of fields) {
263623
+ parts.push(Buffer.from(`--${boundary}\r
263624
+ Content-Disposition: form-data; name="${name}"\r
263625
+ \r
263626
+ ${value2}\r
263627
+ `));
263628
+ }
263629
+ for (const { name, filename, buffer, mime } of files) {
263630
+ parts.push(Buffer.from(`--${boundary}\r
263631
+ Content-Disposition: form-data; name="${name}"; filename="${filename}"\r
263632
+ Content-Type: ${mime}\r
263633
+ \r
263634
+ `));
263635
+ parts.push(buffer);
263636
+ parts.push(Buffer.from("\r\n"));
263637
+ }
263638
+ parts.push(Buffer.from(`--${boundary}--\r
263639
+ `));
263640
+ return {
263641
+ body: Buffer.concat(parts),
263642
+ contentType: `multipart/form-data; boundary=${boundary}`
263643
+ };
263644
+ }
263645
+ function buildImageEditTool(cwd, imageModelId, baseUrl, apiKey) {
263646
+ return {
263647
+ name: "edit_image",
263648
+ label: "edit image",
263649
+ description: "Edit an existing image based on a text prompt. Optionally use a mask to control which areas to modify. Saves the result to disk and returns the file path.",
263650
+ promptSnippet: "edit_image(image, prompt, mask?, filename?, size?, quality?) - edit an existing image",
263651
+ promptGuidelines: [
263652
+ "Use edit_image when the user wants to modify, retouch, or transform an existing image.",
263653
+ "The prompt should describe the full desired final image, not just the change.",
263654
+ "Provide the source image path. Use a mask image (PNG with transparent areas) to control where edits happen.",
263655
+ "Without a mask, the model decides what to change based on the prompt."
263656
+ ],
263657
+ // biome-ignore lint/suspicious/noExplicitAny: plain JSON Schema compatible with TypeBox TSchema
263658
+ parameters: editImageSchema,
263659
+ async execute(_toolCallId, params, signal, _onUpdate) {
263660
+ const { readFileSync: readFileSync24, existsSync: existsSync31 } = await import("node:fs");
263661
+ const { resolve: resolve14, basename: basename12 } = await import("node:path");
263662
+ const p = params;
263663
+ const imagePath = p.image;
263664
+ const prompt = p.prompt;
263665
+ const maskPath = p.mask;
263666
+ const size = p.size;
263667
+ const quality = p.quality;
263668
+ const rawFilename = p.filename;
263669
+ const safePrompt = buildPolicySafeEditPrompt(prompt);
263670
+ const resolvedImage = resolve14(cwd, imagePath);
263671
+ if (!existsSync31(resolvedImage)) {
263672
+ return {
263673
+ content: [
263674
+ {
263675
+ type: "text",
263676
+ text: `Image edit error: source image not found at ${resolvedImage}`
263677
+ }
263678
+ ],
263679
+ details: void 0
263680
+ };
263681
+ }
263682
+ const filename = rawFilename ? extname2(rawFilename) ? rawFilename : `${rawFilename}.png` : `edited_${Date.now()}.png`;
263683
+ const filePath = join34(cwd, filename.replace(/[^a-zA-Z0-9_\-./]/g, "_"));
263684
+ try {
263685
+ const imageBuffer = readFileSync24(resolvedImage);
263686
+ const fields = [
263687
+ { name: "model", value: imageModelId },
263688
+ { name: "prompt", value: safePrompt.prompt },
263689
+ { name: "n", value: "1" },
263690
+ { name: "response_format", value: "b64_json" },
263691
+ { name: "output_format", value: "png" }
263692
+ ];
263693
+ if (size && size !== "auto") {
263694
+ fields.push({ name: "size", value: size });
263695
+ }
263696
+ if (quality && quality !== "auto") {
263697
+ fields.push({ name: "quality", value: quality });
263698
+ }
263699
+ const files = [
263700
+ {
263701
+ name: "image",
263702
+ filename: basename12(resolvedImage),
263703
+ buffer: imageBuffer,
263704
+ mime: detectImageMime(resolvedImage)
263705
+ }
263706
+ ];
263707
+ if (maskPath) {
263708
+ const resolvedMask = resolve14(cwd, maskPath);
263709
+ if (existsSync31(resolvedMask)) {
263710
+ files.push({
263711
+ name: "mask",
263712
+ filename: basename12(resolvedMask),
263713
+ buffer: readFileSync24(resolvedMask),
263714
+ mime: detectImageMime(resolvedMask)
263715
+ });
263716
+ }
263717
+ }
263718
+ const { body: multipartBody, contentType } = buildMultipartBody(fields, files);
263719
+ const url = `${baseUrl.replace(/\/$/, "")}/v1/images/edits`;
263720
+ const sendRequest = async (body, type) => {
263721
+ const res = await fetch(url, {
263722
+ method: "POST",
263723
+ headers: {
263724
+ "Content-Type": type,
263725
+ Authorization: `Bearer ${apiKey}`
263726
+ },
263727
+ body,
263728
+ signal
263729
+ });
263730
+ if (!res.ok) {
263731
+ throw new Error(`Image edit failed (${res.status}): ${await res.text()}`);
263732
+ }
263733
+ return await res.json();
263734
+ };
263735
+ let json = await sendRequest(multipartBody, contentType);
263736
+ const item = pickImageItem(json);
263737
+ let savedPath = await saveImageItem(item, filePath, apiKey);
263738
+ const firstResponseHasEmptyDataArray = Array.isArray(json.data) && json.data.length === 0;
263739
+ if (!savedPath && safePrompt.rewritten && firstResponseHasEmptyDataArray) {
263740
+ const retryFields = fields.map((f3) => f3.name === "prompt" ? {
263741
+ name: "prompt",
263742
+ value: "Remove only distracting overlay text artifacts naturally and keep all original content unchanged."
263743
+ } : f3);
263744
+ const retryMultipart = buildMultipartBody(retryFields, files);
263745
+ json = await sendRequest(retryMultipart.body, retryMultipart.contentType);
263746
+ const retryItem = pickImageItem(json);
263747
+ savedPath = await saveImageItem(retryItem, filePath, apiKey);
263748
+ }
263749
+ return {
263750
+ content: [
263751
+ {
263752
+ type: "text",
263753
+ text: savedPath ?? `Image edited but could not be saved: no image payload returned; image_model: ${imageModelId}`
263754
+ }
263755
+ ],
263756
+ details: {
263757
+ filePath: savedPath,
263758
+ ...json.usage != null ? { usage: { raw: { [imageModelId]: json.usage } } } : {},
263759
+ response: json
263760
+ }
263761
+ };
263762
+ } catch (e2) {
263763
+ const msg = e2 instanceof Error ? e2.message : String(e2);
263764
+ return {
263765
+ content: [
263766
+ { type: "text", text: `Image edit error: ${msg}` }
263767
+ ],
263768
+ details: void 0
263769
+ };
263770
+ }
263771
+ }
263772
+ };
263773
+ }
263774
+
263775
+ // ../../packages/runner-pi/dist/session-utils.js
263776
+ import { closeSync as closeSync2, fstatSync, openSync as openSync2, readdirSync as readdirSync12, readSync as readSync2, statSync as statSync13 } from "node:fs";
263777
+ import { join as join35 } from "node:path";
263778
+ var MAX_SESSION_FILE_BYTES = Number(process.env.SANDAGENT_MAX_SESSION_BYTES) || 10 * 1024 * 1024;
263779
+ function resolveSessionPathById(cwd, sessionId) {
263780
+ const tempMgr = SessionManager.create(cwd);
263781
+ const sessionsDir = tempMgr.getSessionDir();
263782
+ try {
263783
+ const suffix = `_${sessionId}.jsonl`;
263784
+ const match2 = readdirSync12(sessionsDir).find((f3) => f3.endsWith(suffix));
263785
+ return match2 ? join35(sessionsDir, match2) : void 0;
263786
+ } catch {
263787
+ return void 0;
263788
+ }
263789
+ }
263790
+ function isSessionFileTooLarge(sessionPath2) {
263791
+ try {
263792
+ return statSync13(sessionPath2).size > MAX_SESSION_FILE_BYTES;
263793
+ } catch {
263794
+ return false;
263795
+ }
263796
+ }
263797
+ function readTailEntries(sessionPath2, tailBytes = 1024 * 1024) {
263798
+ let fd;
263799
+ try {
263800
+ fd = openSync2(sessionPath2, "r");
263801
+ } catch {
263802
+ return [];
263803
+ }
263804
+ try {
263805
+ const fileSize = fstatSync(fd).size;
263806
+ const readStart = Math.max(0, fileSize - tailBytes);
263807
+ const readLen = fileSize - readStart;
263808
+ const buf = Buffer.alloc(readLen);
263809
+ readSync2(fd, buf, 0, readLen, readStart);
263810
+ const tail = buf.toString("utf8");
263811
+ const entries = [];
263812
+ for (const line of tail.split("\n")) {
263813
+ const trimmed = line.trim();
263814
+ if (!trimmed)
263815
+ continue;
263816
+ try {
263817
+ entries.push(JSON.parse(trimmed));
263818
+ } catch {
263819
+ }
263820
+ }
263821
+ return entries;
263822
+ } finally {
263823
+ closeSync2(fd);
263824
+ }
263825
+ }
263826
+ function extractSessionContext(sessionPath2) {
263827
+ const entries = readTailEntries(sessionPath2);
263828
+ if (entries.length === 0)
263829
+ return void 0;
263830
+ for (let i2 = entries.length - 1; i2 >= 0; i2--) {
263831
+ const e2 = entries[i2];
263832
+ if (e2.type === "compaction" && typeof e2.summary === "string") {
263833
+ return e2.summary;
263834
+ }
263835
+ }
263836
+ const recentMessages = [];
263837
+ const MAX_MESSAGES = 6;
263838
+ for (let i2 = entries.length - 1; i2 >= 0 && recentMessages.length < MAX_MESSAGES; i2--) {
263839
+ const e2 = entries[i2];
263840
+ if (e2.type !== "message")
263841
+ continue;
263842
+ const msg = e2.message;
263843
+ if (!msg)
263844
+ continue;
263845
+ if (msg.role !== "user" && msg.role !== "assistant")
263846
+ continue;
263847
+ let text = "";
263848
+ if (typeof msg.content === "string") {
263849
+ text = msg.content;
263850
+ } else if (Array.isArray(msg.content)) {
263851
+ text = msg.content.filter((c) => c.type === "text" && c.text).map((c) => c.text).join("\n");
263852
+ }
263853
+ if (text) {
263854
+ recentMessages.unshift(`[${msg.role}]: ${text}`);
263855
+ }
263856
+ }
263857
+ if (recentMessages.length === 0)
263858
+ return void 0;
263859
+ return "## Previous Session Context (auto-extracted)\n\nThe following is the tail of the previous conversation:\n\n" + recentMessages.join("\n\n");
263860
+ }
263861
+
263862
+ // ../../packages/runner-pi/dist/usage-metadata.js
263863
+ function usageToMessageMetadata(usage) {
263864
+ return {
263865
+ input_tokens: usage.input,
263866
+ output_tokens: usage.output,
263867
+ cache_read_input_tokens: usage.cacheRead,
263868
+ cache_creation_input_tokens: usage.cacheWrite
263869
+ };
263870
+ }
263871
+ function accumulateToolUsage(tally, raw) {
263872
+ for (const [key, row] of Object.entries(raw)) {
263873
+ const existing = tally[key];
263874
+ if (existing) {
263875
+ for (const [field, val] of Object.entries(row)) {
263876
+ if (typeof val === "number")
263877
+ existing[field] = (existing[field] ?? 0) + val;
263878
+ }
263879
+ } else {
263880
+ const nums = {};
263881
+ for (const [field, val] of Object.entries(row)) {
263882
+ if (typeof val === "number")
263883
+ nums[field] = val;
263884
+ }
263885
+ tally[key] = nums;
263886
+ }
263887
+ }
263888
+ }
263889
+ function getUsageFromAgentEndMessages(messages) {
263890
+ for (let i2 = messages.length - 1; i2 >= 0; i2--) {
263891
+ const m2 = messages[i2];
263892
+ if (m2.role === "assistant" && m2.usage != null)
263893
+ return m2.usage;
263894
+ }
263895
+ return void 0;
263896
+ }
263897
+
263898
+ // ../../packages/runner-pi/dist/stream-converter.js
263899
+ function emitStreamError(errorText) {
263900
+ const errorLine = "data: " + JSON.stringify({ type: "error", errorText }) + "\n\n";
263901
+ const finishLine = "data: " + JSON.stringify({ type: "finish", finishReason: "error" }) + "\n\n";
263902
+ return [errorLine, finishLine, "data: [DONE]\n\n"];
263903
+ }
263904
+ function extractToolResultText(result) {
263905
+ if (result !== null && typeof result === "object") {
263906
+ const r2 = result;
263907
+ if (Array.isArray(r2.content) && r2.content.length > 0) {
263908
+ const text = r2.content.filter((c) => c.type === "text" && typeof c.text === "string").map((c) => c.text).join("\n");
263909
+ if (text.length > 0)
263910
+ return text;
263911
+ }
263912
+ }
263913
+ if (typeof result === "string")
263914
+ return result;
263915
+ try {
263916
+ return JSON.stringify(result);
263917
+ } catch {
263918
+ return String(result);
263919
+ }
263920
+ }
263921
+ function sseData(obj) {
263922
+ return "data: " + JSON.stringify(obj) + "\n\n";
263923
+ }
263924
+ var PiAISDKStreamConverter = class {
263925
+ constructor(options2) {
263926
+ this.options = options2;
263927
+ this.messageId = "msg_" + Date.now() + "_" + Math.random().toString(36).slice(2);
263928
+ this.toolUsageTally = {};
263929
+ this.activeTextPartId = null;
263930
+ this.hasStarted = false;
263931
+ this.hasFinished = false;
263932
+ }
263933
+ get finished() {
263934
+ return this.hasFinished;
263935
+ }
263936
+ forceError(errorText) {
263937
+ if (this.hasFinished)
263938
+ return [];
263939
+ return [...this.ensureStart(), ...this.finishError(errorText)];
263940
+ }
263941
+ handleEvent(event, aborted) {
263942
+ if (this.hasFinished)
263943
+ return [];
263944
+ const chunks = [...this.ensureStart()];
263945
+ if (event.type === "message_start") {
263946
+ const msg = event.message;
263947
+ if (msg?.role === "assistant")
263948
+ chunks.push(...this.endTextStreamIfOpen());
263949
+ return chunks;
263950
+ }
263951
+ if (event.type === "message_end")
263952
+ return chunks;
263953
+ if (event.type === "message_update") {
263954
+ const sub = event.assistantMessageEvent;
263955
+ if (sub.type === "text_start")
263956
+ chunks.push(...this.endTextStreamIfOpen(), ...this.openTextStream());
263957
+ else if (sub.type === "text_delta")
263958
+ chunks.push(...this.emitTextDelta(sub.delta));
263959
+ else if (sub.type === "toolcall_start")
263960
+ chunks.push(...this.endTextStreamIfOpen());
263961
+ return chunks;
263962
+ }
263963
+ if (event.type === "tool_execution_start") {
263964
+ chunks.push(...this.endTextStreamIfOpen());
263965
+ chunks.push(sseData({
263966
+ type: "tool-input-start",
263967
+ toolCallId: event.toolCallId,
263968
+ toolName: event.toolName,
263969
+ dynamic: true,
263970
+ providerExecuted: true
263971
+ }), sseData({
263972
+ type: "tool-input-available",
263973
+ toolCallId: event.toolCallId,
263974
+ toolName: event.toolName,
263975
+ input: event.args,
263976
+ dynamic: true,
263977
+ providerExecuted: true
263978
+ }));
263979
+ return chunks;
263980
+ }
263981
+ if (event.type === "tool_execution_end") {
263982
+ const output = this.options.redactText(this.options.normalizeToolOutput(event.result));
263983
+ const raw = event.result?.details?.usage?.raw;
263984
+ if (raw != null)
263985
+ accumulateToolUsage(this.toolUsageTally, raw);
263986
+ chunks.push(sseData({
263987
+ type: "tool-output-available",
263988
+ toolCallId: event.toolCallId,
263989
+ output,
263990
+ isError: event.isError,
263991
+ dynamic: true,
263992
+ providerExecuted: true
263993
+ }));
263994
+ return chunks;
263995
+ }
263996
+ if (event.type === "agent_end") {
263997
+ if (aborted) {
263998
+ chunks.push(...this.finishError("Run aborted by signal."));
263999
+ } else {
264000
+ const errorMsg = this.options.getErrorFromAgentEndMessages(event.messages);
264001
+ if (errorMsg)
264002
+ chunks.push(...this.finishError(errorMsg));
264003
+ else
264004
+ chunks.push(...this.finishSuccess(this.options.getUsageFromAgentEndMessages(event.messages)));
264005
+ }
264006
+ return chunks;
264007
+ }
264008
+ return chunks;
264009
+ }
264010
+ ensureStart() {
264011
+ if (this.hasStarted)
264012
+ return [];
264013
+ this.hasStarted = true;
264014
+ return [
264015
+ sseData({ type: "start", messageId: this.messageId }),
264016
+ sseData({
264017
+ type: "message-metadata",
264018
+ messageMetadata: { sessionId: this.options.sessionId }
264019
+ })
264020
+ ];
264021
+ }
264022
+ newTextPartId() {
264023
+ return "text_" + Date.now() + "_" + Math.random().toString(36).slice(2) + "_" + Math.random().toString(36).slice(2);
264024
+ }
264025
+ openTextStream() {
264026
+ this.activeTextPartId = this.newTextPartId();
264027
+ return [sseData({ type: "text-start", id: this.activeTextPartId })];
264028
+ }
264029
+ emitTextDelta(rawDelta) {
264030
+ const delta = rawDelta ? this.options.redactText(rawDelta) : void 0;
264031
+ if (!delta)
264032
+ return [];
264033
+ const startChunk = this.activeTextPartId == null ? this.openTextStream() : [];
264034
+ return [
264035
+ ...startChunk,
264036
+ sseData({ type: "text-delta", id: this.activeTextPartId, delta })
264037
+ ];
264038
+ }
264039
+ endTextStreamIfOpen() {
264040
+ if (this.activeTextPartId == null)
264041
+ return [];
264042
+ const id = this.activeTextPartId;
264043
+ this.activeTextPartId = null;
264044
+ return [sseData({ type: "text-end", id })];
264045
+ }
264046
+ finishSuccess(usage) {
264047
+ const chunks = [...this.endTextStreamIfOpen()];
264048
+ const raw = {};
264049
+ let chatUsage;
264050
+ if (usage) {
264051
+ const { id } = this.options.model;
264052
+ chatUsage = {
264053
+ type: "chat",
264054
+ ...usageToMessageMetadata(usage)
264055
+ };
264056
+ raw[id] = chatUsage;
264057
+ }
264058
+ for (const [key, tally] of Object.entries(this.toolUsageTally)) {
264059
+ raw[key] = { ...tally };
264060
+ }
264061
+ const finishPayload = {
264062
+ type: "finish",
264063
+ finishReason: "stop"
264064
+ };
264065
+ if (usage) {
264066
+ finishPayload.messageMetadata = { usage: { ...chatUsage, raw } };
264067
+ }
264068
+ chunks.push(sseData(finishPayload), "data: [DONE]\n\n");
264069
+ this.hasFinished = true;
264070
+ return chunks;
264071
+ }
264072
+ finishError(errorText) {
264073
+ this.hasFinished = true;
264074
+ return emitStreamError(errorText);
264075
+ }
264076
+ };
263456
264077
 
263457
264078
  // ../../packages/runner-pi/dist/web-tools.js
263458
264079
  var braveProvider = {
263459
264080
  id: "brave",
263460
264081
  label: "Brave Search",
263461
264082
  envKeys: ["BRAVE_API_KEY"],
263462
- async search({ apiKey, query, count, country, freshness }) {
264083
+ async search({ apiKey, query, count, country, freshness, signal }) {
263463
264084
  const params = new URLSearchParams({
263464
264085
  q: query,
263465
264086
  count: String(Math.min(count, 20))
@@ -263473,7 +264094,8 @@ var braveProvider = {
263473
264094
  Accept: "application/json",
263474
264095
  "Accept-Encoding": "gzip",
263475
264096
  "X-Subscription-Token": apiKey
263476
- }
264097
+ },
264098
+ signal
263477
264099
  });
263478
264100
  if (!res.ok) {
263479
264101
  const body = await res.text().catch(() => "");
@@ -263494,14 +264116,14 @@ ${body}`);
263494
264116
  });
263495
264117
  }
263496
264118
  }
263497
- return results;
264119
+ return { results };
263498
264120
  }
263499
264121
  };
263500
264122
  var tavilyProvider = {
263501
264123
  id: "tavily",
263502
264124
  label: "Tavily",
263503
264125
  envKeys: ["TAVILY_API_KEY"],
263504
- async search({ apiKey, query, count }) {
264126
+ async search({ apiKey, query, count, signal }) {
263505
264127
  const res = await fetch("https://api.tavily.com/search", {
263506
264128
  method: "POST",
263507
264129
  headers: { "Content-Type": "application/json" },
@@ -263510,7 +264132,8 @@ var tavilyProvider = {
263510
264132
  query,
263511
264133
  max_results: Math.min(count, 10),
263512
264134
  include_answer: false
263513
- })
264135
+ }),
264136
+ signal
263514
264137
  });
263515
264138
  if (!res.ok) {
263516
264139
  const body = await res.text().catch(() => "");
@@ -263528,7 +264151,7 @@ ${body}`);
263528
264151
  });
263529
264152
  }
263530
264153
  }
263531
- return results;
264154
+ return { results };
263532
264155
  }
263533
264156
  };
263534
264157
  var AUTO_DETECT_ORDER = [braveProvider, tavilyProvider];
@@ -263563,9 +264186,12 @@ var BROWSER_UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/53
263563
264186
  function htmlToText(html2) {
263564
264187
  return html2.replace(/<(script|style|noscript)[^>]*>[\s\S]*?<\/\1>/gi, "").replace(/<br\s*\/?>/gi, "\n").replace(/<\/(p|div|h[1-6]|li|tr)>/gi, "\n").replace(/<(p|div|h[1-6]|li|tr)[^>]*>/gi, "\n").replace(/<[^>]+>/g, "").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&nbsp;/g, " ").replace(/[ \t]+/g, " ").replace(/\n{3,}/g, "\n\n").trim();
263565
264188
  }
263566
- async function fetchPageContent(url) {
264189
+ async function fetchPageContent(url, externalSignal) {
263567
264190
  const controller = new AbortController();
263568
264191
  const timeout = setTimeout(() => controller.abort(), 15e3);
264192
+ externalSignal?.addEventListener("abort", () => controller.abort(), {
264193
+ once: true
264194
+ });
263569
264195
  try {
263570
264196
  const res = await fetch(url, {
263571
264197
  headers: {
@@ -263662,7 +264288,7 @@ function buildWebSearchTool(env2) {
263662
264288
  ],
263663
264289
  // biome-ignore lint/suspicious/noExplicitAny: plain JSON Schema compatible with TypeBox TSchema
263664
264290
  parameters: webSearchSchema,
263665
- async execute(_toolCallId, params, _signal, _onUpdate) {
264291
+ async execute(_toolCallId, params, signal, _onUpdate) {
263666
264292
  const p = params;
263667
264293
  const query = p.query;
263668
264294
  const count = p.count ?? 5;
@@ -263672,18 +264298,29 @@ function buildWebSearchTool(env2) {
263672
264298
  let lastError;
263673
264299
  for (const { provider, apiKey } of providers) {
263674
264300
  try {
263675
- const results = await provider.search({
264301
+ const { results } = await provider.search({
263676
264302
  apiKey,
263677
264303
  query,
263678
264304
  count,
263679
264305
  country,
263680
- freshness
264306
+ freshness,
264307
+ signal
263681
264308
  });
264309
+ let fetchedPages = 0;
263682
264310
  if (shouldFetchContent) {
263683
264311
  for (const r2 of results) {
263684
- r2.content = await fetchPageContent(r2.link);
264312
+ r2.content = await fetchPageContent(r2.link, signal);
264313
+ fetchedPages += 1;
263685
264314
  }
263686
264315
  }
264316
+ const usage = {
264317
+ raw: {
264318
+ [provider.id]: {
264319
+ requests: 1,
264320
+ fetchedPages
264321
+ }
264322
+ }
264323
+ };
263687
264324
  return {
263688
264325
  content: [
263689
264326
  {
@@ -263691,7 +264328,9 @@ function buildWebSearchTool(env2) {
263691
264328
  text: formatSearchResults(results, provider.label)
263692
264329
  }
263693
264330
  ],
263694
- details: void 0
264331
+ details: {
264332
+ usage
264333
+ }
263695
264334
  };
263696
264335
  } catch (e2) {
263697
264336
  lastError = e2;
@@ -263727,11 +264366,11 @@ function buildWebFetchTool() {
263727
264366
  ],
263728
264367
  // biome-ignore lint/suspicious/noExplicitAny: plain JSON Schema compatible with TypeBox TSchema
263729
264368
  parameters: webFetchSchema,
263730
- async execute(_toolCallId, params, _signal, _onUpdate) {
264369
+ async execute(_toolCallId, params, signal, _onUpdate) {
263731
264370
  const p = params;
263732
264371
  const url = p.url;
263733
264372
  try {
263734
- const content = await fetchPageContent(url);
264373
+ const content = await fetchPageContent(url, signal);
263735
264374
  return {
263736
264375
  content: [{ type: "text", text: content }],
263737
264376
  details: void 0
@@ -263874,52 +264513,6 @@ function applyModelOverrides(model, provider, optionsEnv) {
263874
264513
  model.baseUrl = anthropicBaseUrl;
263875
264514
  }
263876
264515
  }
263877
- function emitStreamError(errorText) {
263878
- return [
263879
- `data: ${JSON.stringify({ type: "error", errorText })}
263880
-
263881
- `,
263882
- `data: ${JSON.stringify({ type: "finish", finishReason: "error" })}
263883
-
263884
- `,
263885
- "data: [DONE]\n\n"
263886
- ];
263887
- }
263888
- function extractToolResultText(result) {
263889
- if (result !== null && typeof result === "object") {
263890
- const r2 = result;
263891
- if (Array.isArray(r2.content) && r2.content.length > 0) {
263892
- const text = r2.content.filter((c) => c.type === "text" && typeof c.text === "string").map((c) => c.text).join("\n");
263893
- if (text.length > 0) {
263894
- return text;
263895
- }
263896
- }
263897
- }
263898
- if (typeof result === "string")
263899
- return result;
263900
- try {
263901
- return JSON.stringify(result);
263902
- } catch {
263903
- return String(result);
263904
- }
263905
- }
263906
- function usageToMessageMetadata(usage) {
263907
- return {
263908
- input_tokens: usage.input,
263909
- output_tokens: usage.output,
263910
- cache_read_input_tokens: usage.cacheRead,
263911
- cache_creation_input_tokens: usage.cacheWrite
263912
- };
263913
- }
263914
- function getUsageFromAgentEndMessages(messages) {
263915
- for (let i2 = messages.length - 1; i2 >= 0; i2--) {
263916
- const m2 = messages[i2];
263917
- if (m2.role === "assistant" && m2.usage != null) {
263918
- return m2.usage;
263919
- }
263920
- }
263921
- return void 0;
263922
- }
263923
264516
  function getErrorFromAgentEndMessages(messages) {
263924
264517
  for (let i2 = messages.length - 1; i2 >= 0; i2--) {
263925
264518
  const m2 = messages[i2];
@@ -263935,7 +264528,7 @@ function traceRawMessage(debugCwd, data, reset = false, optionsEnv) {
263935
264528
  if (!enabled)
263936
264529
  return;
263937
264530
  try {
263938
- const file = join35(debugCwd, "pi-message-stream-debug.json");
264531
+ const file = join36(debugCwd, "pi-message-stream-debug.json");
263939
264532
  if (reset && existsSync28(file))
263940
264533
  unlinkSync6(file);
263941
264534
  const type = data !== null && typeof data === "object" ? data.type : void 0;
@@ -264004,9 +264597,22 @@ function createPiRunner(options2 = {}) {
264004
264597
  if (resume.includes("/")) {
264005
264598
  return SessionManager.open(resume);
264006
264599
  }
264007
- const sessions = await SessionManager.list(cwd);
264008
- const found = sessions.find((s2) => s2.id === resume);
264009
- return found ? SessionManager.open(found.path) : SessionManager.create(cwd);
264600
+ const sessionPath2 = resolveSessionPathById(cwd, resume);
264601
+ console.error(`${LOG_PREFIX2} resume: id=${resume} path=${sessionPath2 ?? "(not found)"}`);
264602
+ if (sessionPath2) {
264603
+ if (isSessionFileTooLarge(sessionPath2)) {
264604
+ const context = extractSessionContext(sessionPath2);
264605
+ console.error(`${LOG_PREFIX2} session file too large, starting fresh${context ? " (with context)" : ""}`);
264606
+ const newMgr = SessionManager.create(cwd);
264607
+ if (context) {
264608
+ const firstId = newMgr.getEntries()[0]?.id ?? "";
264609
+ newMgr.appendCompaction(context, firstId, 0);
264610
+ }
264611
+ return newMgr;
264612
+ }
264613
+ return SessionManager.open(sessionPath2);
264614
+ }
264615
+ return SessionManager.create(cwd);
264010
264616
  }
264011
264617
  return SessionManager.create(cwd);
264012
264618
  })();
@@ -264024,7 +264630,7 @@ function createPiRunner(options2 = {}) {
264024
264630
  const customTools = options2.env && Object.keys(options2.env).length > 0 ? buildSecretAwareTools(cwd, options2.env) : [];
264025
264631
  if (imageModelName) {
264026
264632
  const apiKey = await modelRegistry2.authStorage.getApiKey(provider) ?? "";
264027
- customTools.push(buildImageGenerateTool(cwd, imageModelName, model.baseUrl, apiKey));
264633
+ customTools.push(buildImageGenerateTool(cwd, imageModelName, model.baseUrl, apiKey), buildImageEditTool(cwd, imageModelName, model.baseUrl, apiKey));
264028
264634
  }
264029
264635
  const { session } = await createAgentSession({
264030
264636
  cwd,
@@ -264064,165 +264670,34 @@ function createPiRunner(options2 = {}) {
264064
264670
  }
264065
264671
  try {
264066
264672
  traceRawMessage(cwd, null, true, options2.env);
264067
- let promptText = userInput;
264068
- let images;
264069
- try {
264070
- if (userInput.startsWith("[") && userInput.endsWith("]")) {
264071
- const parsed = JSON.parse(userInput);
264072
- if (Array.isArray(parsed)) {
264073
- promptText = parsed.filter((p) => p.type === "text").map((p) => p.text).join("\n");
264074
- const imageParts = parsed.filter((p) => p.type === "image");
264075
- if (imageParts.length > 0) {
264076
- images = imageParts.map((p) => ({
264077
- type: "image",
264078
- data: p.data,
264079
- mimeType: p.mimeType
264080
- }));
264081
- }
264673
+ const promptText = userInput;
264674
+ const promptPromise = session.prompt(promptText);
264675
+ const streamConverter = new PiAISDKStreamConverter({
264676
+ sessionId: session.sessionId,
264677
+ model,
264678
+ redactText: (value2) => {
264679
+ if (options2.env && Object.keys(options2.env).length > 0) {
264680
+ return redactSecrets(value2, options2.env);
264082
264681
  }
264083
- }
264084
- } catch (_e2) {
264085
- }
264086
- const promptPromise = session.prompt(promptText, images ? { images } : void 0);
264087
- const messageId = `msg_${Date.now()}_${Math.random().toString(36).slice(2)}`;
264088
- let hasStarted = false;
264089
- let hasFinished = false;
264090
- const imageToolUsage = { input_tokens: 0, output_tokens: 0 };
264091
- const newTextPartId = () => `text_${Date.now()}_${Math.random().toString(36).slice(2)}_${Math.random().toString(36).slice(2)}`;
264092
- let activeTextPartId = null;
264093
- let textStreamOpen = false;
264094
- const endTextStreamIfOpen = function* () {
264095
- if (textStreamOpen && activeTextPartId != null) {
264096
- yield `data: ${JSON.stringify({ type: "text-end", id: activeTextPartId })}
264097
-
264098
- `;
264099
- textStreamOpen = false;
264100
- activeTextPartId = null;
264101
- }
264102
- };
264103
- const beginTextStream = function* () {
264104
- activeTextPartId = newTextPartId();
264105
- yield `data: ${JSON.stringify({ type: "text-start", id: activeTextPartId })}
264106
-
264107
- `;
264108
- textStreamOpen = true;
264109
- };
264110
- const ensureStartEvent = async function* () {
264111
- if (!hasStarted) {
264112
- yield `data: ${JSON.stringify({ type: "start", messageId })}
264113
-
264114
- `;
264115
- yield `data: ${JSON.stringify({
264116
- type: "message-metadata",
264117
- messageMetadata: { sessionId: session.sessionId }
264118
- })}
264119
-
264120
- `;
264121
- hasStarted = true;
264122
- }
264123
- };
264124
- const finishSuccess = async function* (usage) {
264125
- yield* endTextStreamIfOpen();
264126
- const finishPayload = { type: "finish", finishReason: "stop" };
264127
- const hasImageUsage = imageToolUsage.input_tokens > 0 || imageToolUsage.output_tokens > 0;
264128
- if (usage != null || hasImageUsage) {
264129
- const base = usage != null ? usageToMessageMetadata(usage) : {};
264130
- finishPayload.messageMetadata = {
264131
- usage: {
264132
- ...base,
264133
- input_tokens: (base.input_tokens ?? 0) + imageToolUsage.input_tokens,
264134
- output_tokens: (base.output_tokens ?? 0) + imageToolUsage.output_tokens
264135
- }
264136
- };
264137
- }
264138
- yield `data: ${JSON.stringify(finishPayload)}
264139
-
264140
- `;
264141
- yield "data: [DONE]\n\n";
264142
- hasFinished = true;
264143
- };
264144
- const finishError = async function* (errorText) {
264145
- for (const chunk of emitStreamError(errorText)) {
264146
- yield chunk;
264147
- }
264148
- hasFinished = true;
264149
- };
264682
+ return value2;
264683
+ },
264684
+ normalizeToolOutput: extractToolResultText,
264685
+ getUsageFromAgentEndMessages,
264686
+ getErrorFromAgentEndMessages
264687
+ });
264150
264688
  while (!isComplete || eventQueue.length > 0) {
264151
264689
  while (eventQueue.length > 0) {
264152
264690
  const event = eventQueue.shift();
264153
264691
  traceRawMessage(cwd, event, false, options2.env);
264154
- yield* ensureStartEvent();
264155
- if (event.type === "message_start") {
264156
- const msg = event.message;
264157
- if (msg?.role === "assistant") {
264158
- yield* endTextStreamIfOpen();
264159
- }
264160
- } else if (event.type === "message_update") {
264161
- const sub = event.assistantMessageEvent;
264162
- if (sub.type === "text_start") {
264163
- yield* endTextStreamIfOpen();
264164
- yield* beginTextStream();
264165
- } else if (sub.type === "text_delta") {
264166
- let delta = sub.delta;
264167
- if (delta) {
264168
- if (options2.env && Object.keys(options2.env).length > 0) {
264169
- delta = redactSecrets(delta, options2.env);
264170
- }
264171
- if (!textStreamOpen) {
264172
- yield* beginTextStream();
264173
- }
264174
- yield `data: ${JSON.stringify({
264175
- type: "text-delta",
264176
- id: activeTextPartId,
264177
- delta
264178
- })}
264179
-
264180
- `;
264181
- }
264182
- } else if (sub.type === "toolcall_start") {
264183
- yield* endTextStreamIfOpen();
264184
- }
264185
- } else if (event.type === "tool_execution_start") {
264186
- yield* endTextStreamIfOpen();
264187
- yield `data: ${JSON.stringify({ type: "tool-input-start", toolCallId: event.toolCallId, toolName: event.toolName, dynamic: true, providerExecuted: true })}
264188
-
264189
- `;
264190
- yield `data: ${JSON.stringify({ type: "tool-input-available", toolCallId: event.toolCallId, toolName: event.toolName, input: event.args, dynamic: true, providerExecuted: true })}
264191
-
264192
- `;
264193
- } else if (event.type === "tool_execution_end") {
264194
- let output = extractToolResultText(event.result);
264195
- if (options2.env && Object.keys(options2.env).length > 0) {
264196
- output = redactSecrets(output, options2.env);
264197
- }
264198
- if (event.toolName === "generate_image" && event.result !== null && typeof event.result === "object") {
264199
- const details = event.result.details;
264200
- const u = details?.response?.usage;
264201
- if (u) {
264202
- imageToolUsage.input_tokens += u.input_tokens ?? 0;
264203
- imageToolUsage.output_tokens += u.output_tokens ?? 0;
264204
- }
264205
- }
264206
- yield `data: ${JSON.stringify({ type: "tool-output-available", toolCallId: event.toolCallId, output, isError: event.isError, dynamic: true, providerExecuted: true })}
264207
-
264208
- `;
264209
- } else if (event.type === "agent_end") {
264210
- if (aborted) {
264211
- yield* finishError("Run aborted by signal.");
264212
- } else {
264213
- const errorMsg = getErrorFromAgentEndMessages(event.messages);
264214
- if (errorMsg) {
264215
- yield* finishError(errorMsg);
264216
- } else {
264217
- const usage = getUsageFromAgentEndMessages(event.messages);
264218
- yield* finishSuccess(usage);
264219
- }
264220
- }
264692
+ const chunks = streamConverter.handleEvent(event, aborted);
264693
+ for (const chunk of chunks) {
264694
+ yield chunk;
264221
264695
  }
264222
264696
  }
264223
- if (aborted && !hasFinished) {
264224
- yield* ensureStartEvent();
264225
- yield* finishError("Run aborted by signal.");
264697
+ if (aborted && !streamConverter.finished) {
264698
+ for (const chunk of streamConverter.forceError("Run aborted by signal.")) {
264699
+ yield chunk;
264700
+ }
264226
264701
  break;
264227
264702
  }
264228
264703
  if (!isComplete && eventQueue.length === 0) {
@@ -264231,22 +264706,24 @@ function createPiRunner(options2 = {}) {
264231
264706
  });
264232
264707
  }
264233
264708
  }
264234
- if (hasFinished) {
264709
+ if (streamConverter.finished) {
264235
264710
  return;
264236
264711
  }
264237
264712
  try {
264238
264713
  await promptPromise;
264239
264714
  } catch (error) {
264240
- if (!hasFinished) {
264241
- yield* ensureStartEvent();
264715
+ if (!streamConverter.finished) {
264242
264716
  const message = error instanceof Error ? error.message : "Pi agent run failed.";
264243
- yield* finishError(message);
264717
+ for (const chunk of streamConverter.forceError(message)) {
264718
+ yield chunk;
264719
+ }
264244
264720
  }
264245
264721
  return;
264246
264722
  }
264247
- if (!hasFinished && session.agent.state.error) {
264248
- yield* ensureStartEvent();
264249
- yield* finishError(session.agent.state.error);
264723
+ if (!streamConverter.finished && session.agent.state.error) {
264724
+ for (const chunk of streamConverter.forceError(session.agent.state.error)) {
264725
+ yield chunk;
264726
+ }
264250
264727
  }
264251
264728
  } finally {
264252
264729
  if (abortSignal) {
@@ -264266,11 +264743,11 @@ function createPiRunner(options2 = {}) {
264266
264743
 
264267
264744
  // ../../packages/runner-harness/dist/session.js
264268
264745
  import { existsSync as existsSync29, mkdirSync as mkdirSync11, readFileSync as readFileSync23, writeFileSync as writeFileSync14 } from "node:fs";
264269
- import { join as join36 } from "node:path";
264746
+ import { join as join37 } from "node:path";
264270
264747
  var DIR = ".bunny-agent";
264271
264748
  var FILE = "session-id";
264272
264749
  function sessionPath(cwd) {
264273
- return join36(cwd, DIR, FILE);
264750
+ return join37(cwd, DIR, FILE);
264274
264751
  }
264275
264752
  function readSessionId(cwd) {
264276
264753
  try {
@@ -264284,28 +264761,28 @@ function readSessionId(cwd) {
264284
264761
  }
264285
264762
  function writeSessionId(cwd, id) {
264286
264763
  try {
264287
- mkdirSync11(join36(cwd, DIR), { recursive: true });
264764
+ mkdirSync11(join37(cwd, DIR), { recursive: true });
264288
264765
  writeFileSync14(sessionPath(cwd), id, "utf8");
264289
264766
  } catch {
264290
264767
  }
264291
264768
  }
264292
264769
 
264293
264770
  // ../../packages/runner-harness/dist/skills.js
264294
- import { existsSync as existsSync30, readdirSync as readdirSync12, statSync as statSync13 } from "node:fs";
264771
+ import { existsSync as existsSync30, readdirSync as readdirSync13, statSync as statSync14 } from "node:fs";
264295
264772
  import { homedir as homedir14 } from "node:os";
264296
- import { join as join37 } from "node:path";
264773
+ import { join as join38 } from "node:path";
264297
264774
  function discoverSkillPaths(cwd) {
264298
264775
  const paths = [];
264299
264776
  for (const base of [
264300
- join37(cwd, "skills"),
264301
- join37(homedir14(), ".bunny-agent", "skills")
264777
+ join38(cwd, "skills"),
264778
+ join38(homedir14(), ".bunny-agent", "skills")
264302
264779
  ]) {
264303
264780
  if (!existsSync30(base))
264304
264781
  continue;
264305
264782
  try {
264306
- for (const entry of readdirSync12(base)) {
264307
- const full = join37(base, entry);
264308
- if (statSync13(full).isDirectory() && existsSync30(join37(full, "SKILL.md"))) {
264783
+ for (const entry of readdirSync13(base)) {
264784
+ const full = join38(base, entry);
264785
+ if (statSync14(full).isDirectory() && existsSync30(join38(full, "SKILL.md"))) {
264309
264786
  paths.push(full);
264310
264787
  }
264311
264788
  }
@@ -264426,7 +264903,8 @@ function codingRunStream(req, env2) {
264426
264903
  cwd: req.cwd ?? process.env.BUNNY_AGENT_ROOT ?? "/workspace",
264427
264904
  yolo: req.yolo,
264428
264905
  env: env2,
264429
- abortController
264906
+ abortController,
264907
+ autoInject: false
264430
264908
  });
264431
264909
  for await (const chunk of stream2) {
264432
264910
  controller.enqueue(encoder.encode(chunk));