@bunny-agent/runner-cli 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.
@@ -1 +1 @@
1
- {"version":3,"file":"build-image.d.ts","sourceRoot":"","sources":["../src/build-image.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,iBAAiB;IAChC,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,IAAI,EAAE,OAAO,CAAC;CACf;AA2ED,wBAAsB,UAAU,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CA2EvE"}
1
+ {"version":3,"file":"build-image.d.ts","sourceRoot":"","sources":["../src/build-image.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,iBAAiB;IAChC,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,IAAI,EAAE,OAAO,CAAC;CACf;AA6ED,wBAAsB,UAAU,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CA2EvE"}
@@ -13,10 +13,12 @@ function getShippedDockerfile() {
13
13
  // Look for Dockerfile in several locations:
14
14
  // 1. Package root (apps/runner-cli/Dockerfile) — shipped with npm package
15
15
  // 2. docker/bunny-agent-claude/Dockerfile — monorepo development
16
+ // 3. CWD-relative docker/bunny-agent-claude/Dockerfile — global install invoked from repo root
16
17
  const packageRoot = getPackageRoot();
17
18
  const candidates = [
18
19
  join(packageRoot, "Dockerfile"),
19
20
  resolve(packageRoot, "..", "..", "docker", "bunny-agent-claude", "Dockerfile"),
21
+ resolve(process.cwd(), "docker", "bunny-agent-claude", "Dockerfile"),
20
22
  ];
21
23
  for (const p of candidates) {
22
24
  if (existsSync(p))
@@ -1 +1 @@
1
- {"version":3,"file":"build-image.js","sourceRoot":"","sources":["../src/build-image.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"build-image.js","sourceRoot":"","sources":["../src/build-image.ts"],"names":[],"mappings":"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"}
package/dist/bundle.mjs CHANGED
@@ -33,7 +33,8 @@ function getShippedDockerfile() {
33
33
  "docker",
34
34
  "bunny-agent-claude",
35
35
  "Dockerfile"
36
- )
36
+ ),
37
+ resolve(process.cwd(), "docker", "bunny-agent-claude", "Dockerfile")
37
38
  ];
38
39
  for (const p of candidates) {
39
40
  if (existsSync(p)) return p;
@@ -1273,9 +1274,9 @@ function createOpenCodeRunner(options = {}) {
1273
1274
 
1274
1275
  // ../../packages/runner-pi/dist/pi-runner.js
1275
1276
  import { appendFileSync as appendFileSync2, existsSync as existsSync5, unlinkSync as unlinkSync3 } from "node:fs";
1276
- import { join as join7 } from "node:path";
1277
+ import { join as join8 } from "node:path";
1277
1278
  import { getModel } from "@mariozechner/pi-ai";
1278
- import { AuthStorage, createAgentSession, ModelRegistry, SessionManager } from "@mariozechner/pi-coding-agent";
1279
+ import { AuthStorage, createAgentSession, ModelRegistry, SessionManager as SessionManager2 } from "@mariozechner/pi-coding-agent";
1279
1280
 
1280
1281
  // ../../packages/runner-pi/dist/bunny-agent-resource-loader.js
1281
1282
  import { existsSync as existsSync4 } from "node:fs";
@@ -1399,25 +1400,146 @@ var generateImageSchema = {
1399
1400
  },
1400
1401
  quality: {
1401
1402
  type: "string",
1402
- enum: ["standard", "hd"],
1403
- description: "Image quality (OpenAI only). Defaults to standard."
1403
+ enum: ["low", "medium", "high", "auto"],
1404
+ description: "Image quality. Defaults to auto."
1404
1405
  }
1405
1406
  },
1406
1407
  required: ["prompt"],
1407
1408
  additionalProperties: false
1408
1409
  };
1409
- async function resolveB64(item) {
1410
+ async function resolveB64(item, apiKey) {
1410
1411
  if (item.b64_json)
1411
1412
  return item.b64_json;
1412
- if (item.url) {
1413
- const res = await fetch(item.url);
1413
+ if (item.b64Json)
1414
+ return item.b64Json;
1415
+ if (item.image_base64)
1416
+ return item.image_base64;
1417
+ if (item.imageBase64)
1418
+ return item.imageBase64;
1419
+ if (item.base64)
1420
+ return item.base64;
1421
+ if (typeof item.image === "string")
1422
+ return item.image;
1423
+ if (item.image?.b64_json)
1424
+ return item.image.b64_json;
1425
+ if (item.image?.base64)
1426
+ return item.image.base64;
1427
+ const url = item.url ?? item.image_url ?? item.imageUrl ?? item.image?.url;
1428
+ if (url) {
1429
+ const headers = {};
1430
+ if (apiKey) {
1431
+ headers.Authorization = `Bearer ${apiKey}`;
1432
+ }
1433
+ const res = await fetch(url, { headers });
1414
1434
  if (res.ok)
1415
1435
  return Buffer.from(await res.arrayBuffer()).toString("base64");
1416
1436
  }
1417
1437
  return void 0;
1418
1438
  }
1419
- async function saveImageItem(item, filePath) {
1420
- const b64 = await resolveB64(item);
1439
+ function pickImageItem(response) {
1440
+ const tryFromObject = (value) => {
1441
+ if (!value || typeof value !== "object")
1442
+ return void 0;
1443
+ const obj = value;
1444
+ return {
1445
+ b64_json: obj.b64_json ?? obj.b64Json,
1446
+ b64Json: obj.b64Json,
1447
+ url: obj.url ?? obj.imageUrl,
1448
+ image_base64: obj.image_base64 ?? obj.imageBase64,
1449
+ imageBase64: obj.imageBase64,
1450
+ image_url: obj.image_url ?? obj.imageUrl,
1451
+ imageUrl: obj.imageUrl,
1452
+ base64: obj.base64,
1453
+ image: obj.image
1454
+ };
1455
+ };
1456
+ const asItem = (value) => {
1457
+ if (value == null)
1458
+ return void 0;
1459
+ if (typeof value === "string") {
1460
+ return { base64: value };
1461
+ }
1462
+ if (typeof value === "object") {
1463
+ const normalized = tryFromObject(value);
1464
+ if (normalized)
1465
+ return normalized;
1466
+ }
1467
+ return void 0;
1468
+ };
1469
+ const fromDataArray = Array.isArray(response.data) ? asItem(response.data[0]) : void 0;
1470
+ if (fromDataArray)
1471
+ return fromDataArray;
1472
+ const fromDataValue = asItem(response.data);
1473
+ if (fromDataValue)
1474
+ return fromDataValue;
1475
+ const responseRecord = response;
1476
+ const imagesValue = responseRecord.images;
1477
+ const outputValue = responseRecord.output;
1478
+ const fromImagesArray = Array.isArray(imagesValue) ? asItem(imagesValue[0]) : void 0;
1479
+ if (fromImagesArray)
1480
+ return fromImagesArray;
1481
+ const fromImagesValue = asItem(imagesValue);
1482
+ if (fromImagesValue)
1483
+ return fromImagesValue;
1484
+ const fromOutputArray = Array.isArray(outputValue) ? asItem(outputValue[0]) : void 0;
1485
+ if (fromOutputArray)
1486
+ return fromOutputArray;
1487
+ const fromOutputValue = asItem(outputValue);
1488
+ if (fromOutputValue)
1489
+ return fromOutputValue;
1490
+ const fromTopLevel = asItem(response);
1491
+ if (fromTopLevel)
1492
+ return fromTopLevel;
1493
+ const queue = [response];
1494
+ while (queue.length > 0) {
1495
+ const current = queue.shift();
1496
+ if (current == null)
1497
+ continue;
1498
+ if (typeof current === "string") {
1499
+ if (/^[A-Za-z0-9+/=]{32,}$/.test(current))
1500
+ return { base64: current };
1501
+ continue;
1502
+ }
1503
+ if (typeof current !== "object")
1504
+ continue;
1505
+ const normalized = tryFromObject(current);
1506
+ if (normalized) {
1507
+ 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));
1508
+ if (hasUsefulField)
1509
+ return normalized;
1510
+ }
1511
+ if (Array.isArray(current)) {
1512
+ queue.push(...current);
1513
+ continue;
1514
+ }
1515
+ for (const value of Object.values(current)) {
1516
+ queue.push(value);
1517
+ }
1518
+ }
1519
+ return {};
1520
+ }
1521
+ function detectImageMime(filePath) {
1522
+ const ext = extname(filePath).toLowerCase();
1523
+ if (ext === ".jpg" || ext === ".jpeg")
1524
+ return "image/jpeg";
1525
+ if (ext === ".webp")
1526
+ return "image/webp";
1527
+ if (ext === ".gif")
1528
+ return "image/gif";
1529
+ return "image/png";
1530
+ }
1531
+ function buildPolicySafeEditPrompt(prompt) {
1532
+ const riskyPattern = /\b(watermark|watermarks|logo|logos|copyright|brand mark|remove branding)\b/i;
1533
+ if (!riskyPattern.test(prompt)) {
1534
+ return { prompt, rewritten: false };
1535
+ }
1536
+ return {
1537
+ prompt: "Clean up distracting overlay text or marks naturally while preserving the original scene, style, and layout. Keep the result seamless and high quality.",
1538
+ rewritten: true
1539
+ };
1540
+ }
1541
+ async function saveImageItem(item, filePath, apiKey) {
1542
+ const b64 = await resolveB64(item, apiKey);
1421
1543
  if (!b64)
1422
1544
  return void 0;
1423
1545
  mkdirSync2(dirname3(filePath), { recursive: true });
@@ -1437,11 +1559,11 @@ function buildImageGenerateTool(cwd, imageModelId, baseUrl, apiKey) {
1437
1559
  ],
1438
1560
  // biome-ignore lint/suspicious/noExplicitAny: plain JSON Schema compatible with TypeBox TSchema
1439
1561
  parameters: generateImageSchema,
1440
- async execute(_toolCallId, params, _signal, _onUpdate) {
1562
+ async execute(_toolCallId, params, signal, _onUpdate) {
1441
1563
  const p = params;
1442
1564
  const prompt = p.prompt;
1443
1565
  const size = p.size ?? "1024x1024";
1444
- const quality = p.quality ?? "standard";
1566
+ const quality = p.quality ?? "auto";
1445
1567
  const rawFilename = p.filename;
1446
1568
  const filename = rawFilename ? extname(rawFilename) ? rawFilename : `${rawFilename}.png` : `image_${Date.now()}.png`;
1447
1569
  const filePath = join6(cwd, filename.replace(/[^a-zA-Z0-9_\-./]/g, "_"));
@@ -1458,24 +1580,28 @@ function buildImageGenerateTool(cwd, imageModelId, baseUrl, apiKey) {
1458
1580
  prompt,
1459
1581
  n: 1,
1460
1582
  size,
1461
- quality
1462
- })
1583
+ quality,
1584
+ response_format: "b64_json",
1585
+ output_format: "png"
1586
+ }),
1587
+ signal
1463
1588
  });
1464
1589
  if (!res.ok) {
1465
1590
  throw new Error(`Image generation failed (${res.status}): ${await res.text()}`);
1466
1591
  }
1467
1592
  const json = await res.json();
1468
- const item = json.data?.[0] ?? {};
1469
- const savedPath = await saveImageItem(item, filePath);
1593
+ const item = pickImageItem(json);
1594
+ const savedPath = await saveImageItem(item, filePath, apiKey);
1470
1595
  return {
1471
1596
  content: [
1472
1597
  {
1473
1598
  type: "text",
1474
- text: savedPath ?? "Image generated but could not be saved."
1599
+ text: savedPath ?? `Image generated but could not be saved: no image payload returned; image_model: ${imageModelId}`
1475
1600
  }
1476
1601
  ],
1477
1602
  details: {
1478
1603
  filePath: savedPath,
1604
+ ...json.usage != null ? { usage: { raw: { [imageModelId]: json.usage } } } : {},
1479
1605
  response: json
1480
1606
  }
1481
1607
  };
@@ -1491,6 +1617,498 @@ function buildImageGenerateTool(cwd, imageModelId, baseUrl, apiKey) {
1491
1617
  }
1492
1618
  };
1493
1619
  }
1620
+ var editImageSchema = {
1621
+ type: "object",
1622
+ properties: {
1623
+ image: {
1624
+ type: "string",
1625
+ description: "Path to the source image file to edit (relative to working directory or absolute)."
1626
+ },
1627
+ prompt: {
1628
+ type: "string",
1629
+ description: "Text description of the desired final image. Describe the full result, not just the change."
1630
+ },
1631
+ mask: {
1632
+ type: "string",
1633
+ 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."
1634
+ },
1635
+ filename: {
1636
+ type: "string",
1637
+ description: "Output filename with extension, e.g. 'edited_cat.png'. Defaults to a timestamp-based name."
1638
+ },
1639
+ size: {
1640
+ type: "string",
1641
+ enum: ["1024x1024", "1024x1536", "1536x1024", "auto"],
1642
+ description: "Output image dimensions. Optional; omit or set auto to let model decide."
1643
+ },
1644
+ quality: {
1645
+ type: "string",
1646
+ enum: ["low", "medium", "high", "auto"],
1647
+ description: "Image quality. Optional; omit or set auto to let model decide."
1648
+ }
1649
+ },
1650
+ required: ["image", "prompt"],
1651
+ additionalProperties: false
1652
+ };
1653
+ function buildMultipartBody(fields, files) {
1654
+ const boundary = `----SandagentBoundary${Date.now()}${Math.random().toString(36).slice(2)}`;
1655
+ const parts = [];
1656
+ for (const { name, value } of fields) {
1657
+ parts.push(Buffer.from(`--${boundary}\r
1658
+ Content-Disposition: form-data; name="${name}"\r
1659
+ \r
1660
+ ${value}\r
1661
+ `));
1662
+ }
1663
+ for (const { name, filename, buffer, mime } of files) {
1664
+ parts.push(Buffer.from(`--${boundary}\r
1665
+ Content-Disposition: form-data; name="${name}"; filename="${filename}"\r
1666
+ Content-Type: ${mime}\r
1667
+ \r
1668
+ `));
1669
+ parts.push(buffer);
1670
+ parts.push(Buffer.from("\r\n"));
1671
+ }
1672
+ parts.push(Buffer.from(`--${boundary}--\r
1673
+ `));
1674
+ return {
1675
+ body: Buffer.concat(parts),
1676
+ contentType: `multipart/form-data; boundary=${boundary}`
1677
+ };
1678
+ }
1679
+ function buildImageEditTool(cwd, imageModelId, baseUrl, apiKey) {
1680
+ return {
1681
+ name: "edit_image",
1682
+ label: "edit image",
1683
+ 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.",
1684
+ promptSnippet: "edit_image(image, prompt, mask?, filename?, size?, quality?) - edit an existing image",
1685
+ promptGuidelines: [
1686
+ "Use edit_image when the user wants to modify, retouch, or transform an existing image.",
1687
+ "The prompt should describe the full desired final image, not just the change.",
1688
+ "Provide the source image path. Use a mask image (PNG with transparent areas) to control where edits happen.",
1689
+ "Without a mask, the model decides what to change based on the prompt."
1690
+ ],
1691
+ // biome-ignore lint/suspicious/noExplicitAny: plain JSON Schema compatible with TypeBox TSchema
1692
+ parameters: editImageSchema,
1693
+ async execute(_toolCallId, params, signal, _onUpdate) {
1694
+ const { readFileSync: readFileSync4, existsSync: existsSync8 } = await import("node:fs");
1695
+ const { resolve: resolve4, basename: basename2 } = await import("node:path");
1696
+ const p = params;
1697
+ const imagePath = p.image;
1698
+ const prompt = p.prompt;
1699
+ const maskPath = p.mask;
1700
+ const size = p.size;
1701
+ const quality = p.quality;
1702
+ const rawFilename = p.filename;
1703
+ const safePrompt = buildPolicySafeEditPrompt(prompt);
1704
+ const resolvedImage = resolve4(cwd, imagePath);
1705
+ if (!existsSync8(resolvedImage)) {
1706
+ return {
1707
+ content: [
1708
+ {
1709
+ type: "text",
1710
+ text: `Image edit error: source image not found at ${resolvedImage}`
1711
+ }
1712
+ ],
1713
+ details: void 0
1714
+ };
1715
+ }
1716
+ const filename = rawFilename ? extname(rawFilename) ? rawFilename : `${rawFilename}.png` : `edited_${Date.now()}.png`;
1717
+ const filePath = join6(cwd, filename.replace(/[^a-zA-Z0-9_\-./]/g, "_"));
1718
+ try {
1719
+ const imageBuffer = readFileSync4(resolvedImage);
1720
+ const fields = [
1721
+ { name: "model", value: imageModelId },
1722
+ { name: "prompt", value: safePrompt.prompt },
1723
+ { name: "n", value: "1" },
1724
+ { name: "response_format", value: "b64_json" },
1725
+ { name: "output_format", value: "png" }
1726
+ ];
1727
+ if (size && size !== "auto") {
1728
+ fields.push({ name: "size", value: size });
1729
+ }
1730
+ if (quality && quality !== "auto") {
1731
+ fields.push({ name: "quality", value: quality });
1732
+ }
1733
+ const files = [
1734
+ {
1735
+ name: "image",
1736
+ filename: basename2(resolvedImage),
1737
+ buffer: imageBuffer,
1738
+ mime: detectImageMime(resolvedImage)
1739
+ }
1740
+ ];
1741
+ if (maskPath) {
1742
+ const resolvedMask = resolve4(cwd, maskPath);
1743
+ if (existsSync8(resolvedMask)) {
1744
+ files.push({
1745
+ name: "mask",
1746
+ filename: basename2(resolvedMask),
1747
+ buffer: readFileSync4(resolvedMask),
1748
+ mime: detectImageMime(resolvedMask)
1749
+ });
1750
+ }
1751
+ }
1752
+ const { body: multipartBody, contentType } = buildMultipartBody(fields, files);
1753
+ const url = `${baseUrl.replace(/\/$/, "")}/v1/images/edits`;
1754
+ const sendRequest = async (body, type) => {
1755
+ const res = await fetch(url, {
1756
+ method: "POST",
1757
+ headers: {
1758
+ "Content-Type": type,
1759
+ Authorization: `Bearer ${apiKey}`
1760
+ },
1761
+ body,
1762
+ signal
1763
+ });
1764
+ if (!res.ok) {
1765
+ throw new Error(`Image edit failed (${res.status}): ${await res.text()}`);
1766
+ }
1767
+ return await res.json();
1768
+ };
1769
+ let json = await sendRequest(multipartBody, contentType);
1770
+ const item = pickImageItem(json);
1771
+ let savedPath = await saveImageItem(item, filePath, apiKey);
1772
+ const firstResponseHasEmptyDataArray = Array.isArray(json.data) && json.data.length === 0;
1773
+ if (!savedPath && safePrompt.rewritten && firstResponseHasEmptyDataArray) {
1774
+ const retryFields = fields.map((f) => f.name === "prompt" ? {
1775
+ name: "prompt",
1776
+ value: "Remove only distracting overlay text artifacts naturally and keep all original content unchanged."
1777
+ } : f);
1778
+ const retryMultipart = buildMultipartBody(retryFields, files);
1779
+ json = await sendRequest(retryMultipart.body, retryMultipart.contentType);
1780
+ const retryItem = pickImageItem(json);
1781
+ savedPath = await saveImageItem(retryItem, filePath, apiKey);
1782
+ }
1783
+ return {
1784
+ content: [
1785
+ {
1786
+ type: "text",
1787
+ text: savedPath ?? `Image edited but could not be saved: no image payload returned; image_model: ${imageModelId}`
1788
+ }
1789
+ ],
1790
+ details: {
1791
+ filePath: savedPath,
1792
+ ...json.usage != null ? { usage: { raw: { [imageModelId]: json.usage } } } : {},
1793
+ response: json
1794
+ }
1795
+ };
1796
+ } catch (e) {
1797
+ const msg = e instanceof Error ? e.message : String(e);
1798
+ return {
1799
+ content: [
1800
+ { type: "text", text: `Image edit error: ${msg}` }
1801
+ ],
1802
+ details: void 0
1803
+ };
1804
+ }
1805
+ }
1806
+ };
1807
+ }
1808
+
1809
+ // ../../packages/runner-pi/dist/session-utils.js
1810
+ import { closeSync, fstatSync, openSync, readdirSync as readdirSync2, readSync, statSync as statSync2 } from "node:fs";
1811
+ import { join as join7 } from "node:path";
1812
+ import { SessionManager } from "@mariozechner/pi-coding-agent";
1813
+ var MAX_SESSION_FILE_BYTES = Number(process.env.SANDAGENT_MAX_SESSION_BYTES) || 10 * 1024 * 1024;
1814
+ function resolveSessionPathById(cwd, sessionId) {
1815
+ const tempMgr = SessionManager.create(cwd);
1816
+ const sessionsDir = tempMgr.getSessionDir();
1817
+ try {
1818
+ const suffix = `_${sessionId}.jsonl`;
1819
+ const match = readdirSync2(sessionsDir).find((f) => f.endsWith(suffix));
1820
+ return match ? join7(sessionsDir, match) : void 0;
1821
+ } catch {
1822
+ return void 0;
1823
+ }
1824
+ }
1825
+ function isSessionFileTooLarge(sessionPath2) {
1826
+ try {
1827
+ return statSync2(sessionPath2).size > MAX_SESSION_FILE_BYTES;
1828
+ } catch {
1829
+ return false;
1830
+ }
1831
+ }
1832
+ function readTailEntries(sessionPath2, tailBytes = 1024 * 1024) {
1833
+ let fd;
1834
+ try {
1835
+ fd = openSync(sessionPath2, "r");
1836
+ } catch {
1837
+ return [];
1838
+ }
1839
+ try {
1840
+ const fileSize = fstatSync(fd).size;
1841
+ const readStart = Math.max(0, fileSize - tailBytes);
1842
+ const readLen = fileSize - readStart;
1843
+ const buf = Buffer.alloc(readLen);
1844
+ readSync(fd, buf, 0, readLen, readStart);
1845
+ const tail = buf.toString("utf8");
1846
+ const entries = [];
1847
+ for (const line of tail.split("\n")) {
1848
+ const trimmed = line.trim();
1849
+ if (!trimmed)
1850
+ continue;
1851
+ try {
1852
+ entries.push(JSON.parse(trimmed));
1853
+ } catch {
1854
+ }
1855
+ }
1856
+ return entries;
1857
+ } finally {
1858
+ closeSync(fd);
1859
+ }
1860
+ }
1861
+ function extractSessionContext(sessionPath2) {
1862
+ const entries = readTailEntries(sessionPath2);
1863
+ if (entries.length === 0)
1864
+ return void 0;
1865
+ for (let i = entries.length - 1; i >= 0; i--) {
1866
+ const e = entries[i];
1867
+ if (e.type === "compaction" && typeof e.summary === "string") {
1868
+ return e.summary;
1869
+ }
1870
+ }
1871
+ const recentMessages = [];
1872
+ const MAX_MESSAGES = 6;
1873
+ for (let i = entries.length - 1; i >= 0 && recentMessages.length < MAX_MESSAGES; i--) {
1874
+ const e = entries[i];
1875
+ if (e.type !== "message")
1876
+ continue;
1877
+ const msg = e.message;
1878
+ if (!msg)
1879
+ continue;
1880
+ if (msg.role !== "user" && msg.role !== "assistant")
1881
+ continue;
1882
+ let text = "";
1883
+ if (typeof msg.content === "string") {
1884
+ text = msg.content;
1885
+ } else if (Array.isArray(msg.content)) {
1886
+ text = msg.content.filter((c) => c.type === "text" && c.text).map((c) => c.text).join("\n");
1887
+ }
1888
+ if (text) {
1889
+ recentMessages.unshift(`[${msg.role}]: ${text}`);
1890
+ }
1891
+ }
1892
+ if (recentMessages.length === 0)
1893
+ return void 0;
1894
+ return "## Previous Session Context (auto-extracted)\n\nThe following is the tail of the previous conversation:\n\n" + recentMessages.join("\n\n");
1895
+ }
1896
+
1897
+ // ../../packages/runner-pi/dist/usage-metadata.js
1898
+ function usageToMessageMetadata(usage) {
1899
+ return {
1900
+ input_tokens: usage.input,
1901
+ output_tokens: usage.output,
1902
+ cache_read_input_tokens: usage.cacheRead,
1903
+ cache_creation_input_tokens: usage.cacheWrite
1904
+ };
1905
+ }
1906
+ function accumulateToolUsage(tally, raw) {
1907
+ for (const [key, row] of Object.entries(raw)) {
1908
+ const existing = tally[key];
1909
+ if (existing) {
1910
+ for (const [field, val] of Object.entries(row)) {
1911
+ if (typeof val === "number")
1912
+ existing[field] = (existing[field] ?? 0) + val;
1913
+ }
1914
+ } else {
1915
+ const nums = {};
1916
+ for (const [field, val] of Object.entries(row)) {
1917
+ if (typeof val === "number")
1918
+ nums[field] = val;
1919
+ }
1920
+ tally[key] = nums;
1921
+ }
1922
+ }
1923
+ }
1924
+ function getUsageFromAgentEndMessages(messages) {
1925
+ for (let i = messages.length - 1; i >= 0; i--) {
1926
+ const m = messages[i];
1927
+ if (m.role === "assistant" && m.usage != null)
1928
+ return m.usage;
1929
+ }
1930
+ return void 0;
1931
+ }
1932
+
1933
+ // ../../packages/runner-pi/dist/stream-converter.js
1934
+ function emitStreamError(errorText) {
1935
+ const errorLine = "data: " + JSON.stringify({ type: "error", errorText }) + "\n\n";
1936
+ const finishLine = "data: " + JSON.stringify({ type: "finish", finishReason: "error" }) + "\n\n";
1937
+ return [errorLine, finishLine, "data: [DONE]\n\n"];
1938
+ }
1939
+ function extractToolResultText(result) {
1940
+ if (result !== null && typeof result === "object") {
1941
+ const r = result;
1942
+ if (Array.isArray(r.content) && r.content.length > 0) {
1943
+ const text = r.content.filter((c) => c.type === "text" && typeof c.text === "string").map((c) => c.text).join("\n");
1944
+ if (text.length > 0)
1945
+ return text;
1946
+ }
1947
+ }
1948
+ if (typeof result === "string")
1949
+ return result;
1950
+ try {
1951
+ return JSON.stringify(result);
1952
+ } catch {
1953
+ return String(result);
1954
+ }
1955
+ }
1956
+ function sseData(obj) {
1957
+ return "data: " + JSON.stringify(obj) + "\n\n";
1958
+ }
1959
+ var PiAISDKStreamConverter = class {
1960
+ constructor(options) {
1961
+ this.options = options;
1962
+ this.messageId = "msg_" + Date.now() + "_" + Math.random().toString(36).slice(2);
1963
+ this.toolUsageTally = {};
1964
+ this.activeTextPartId = null;
1965
+ this.hasStarted = false;
1966
+ this.hasFinished = false;
1967
+ }
1968
+ get finished() {
1969
+ return this.hasFinished;
1970
+ }
1971
+ forceError(errorText) {
1972
+ if (this.hasFinished)
1973
+ return [];
1974
+ return [...this.ensureStart(), ...this.finishError(errorText)];
1975
+ }
1976
+ handleEvent(event, aborted) {
1977
+ if (this.hasFinished)
1978
+ return [];
1979
+ const chunks = [...this.ensureStart()];
1980
+ if (event.type === "message_start") {
1981
+ const msg = event.message;
1982
+ if (msg?.role === "assistant")
1983
+ chunks.push(...this.endTextStreamIfOpen());
1984
+ return chunks;
1985
+ }
1986
+ if (event.type === "message_end")
1987
+ return chunks;
1988
+ if (event.type === "message_update") {
1989
+ const sub = event.assistantMessageEvent;
1990
+ if (sub.type === "text_start")
1991
+ chunks.push(...this.endTextStreamIfOpen(), ...this.openTextStream());
1992
+ else if (sub.type === "text_delta")
1993
+ chunks.push(...this.emitTextDelta(sub.delta));
1994
+ else if (sub.type === "toolcall_start")
1995
+ chunks.push(...this.endTextStreamIfOpen());
1996
+ return chunks;
1997
+ }
1998
+ if (event.type === "tool_execution_start") {
1999
+ chunks.push(...this.endTextStreamIfOpen());
2000
+ chunks.push(sseData({
2001
+ type: "tool-input-start",
2002
+ toolCallId: event.toolCallId,
2003
+ toolName: event.toolName,
2004
+ dynamic: true,
2005
+ providerExecuted: true
2006
+ }), sseData({
2007
+ type: "tool-input-available",
2008
+ toolCallId: event.toolCallId,
2009
+ toolName: event.toolName,
2010
+ input: event.args,
2011
+ dynamic: true,
2012
+ providerExecuted: true
2013
+ }));
2014
+ return chunks;
2015
+ }
2016
+ if (event.type === "tool_execution_end") {
2017
+ const output = this.options.redactText(this.options.normalizeToolOutput(event.result));
2018
+ const raw = event.result?.details?.usage?.raw;
2019
+ if (raw != null)
2020
+ accumulateToolUsage(this.toolUsageTally, raw);
2021
+ chunks.push(sseData({
2022
+ type: "tool-output-available",
2023
+ toolCallId: event.toolCallId,
2024
+ output,
2025
+ isError: event.isError,
2026
+ dynamic: true,
2027
+ providerExecuted: true
2028
+ }));
2029
+ return chunks;
2030
+ }
2031
+ if (event.type === "agent_end") {
2032
+ if (aborted) {
2033
+ chunks.push(...this.finishError("Run aborted by signal."));
2034
+ } else {
2035
+ const errorMsg = this.options.getErrorFromAgentEndMessages(event.messages);
2036
+ if (errorMsg)
2037
+ chunks.push(...this.finishError(errorMsg));
2038
+ else
2039
+ chunks.push(...this.finishSuccess(this.options.getUsageFromAgentEndMessages(event.messages)));
2040
+ }
2041
+ return chunks;
2042
+ }
2043
+ return chunks;
2044
+ }
2045
+ ensureStart() {
2046
+ if (this.hasStarted)
2047
+ return [];
2048
+ this.hasStarted = true;
2049
+ return [
2050
+ sseData({ type: "start", messageId: this.messageId }),
2051
+ sseData({
2052
+ type: "message-metadata",
2053
+ messageMetadata: { sessionId: this.options.sessionId }
2054
+ })
2055
+ ];
2056
+ }
2057
+ newTextPartId() {
2058
+ return "text_" + Date.now() + "_" + Math.random().toString(36).slice(2) + "_" + Math.random().toString(36).slice(2);
2059
+ }
2060
+ openTextStream() {
2061
+ this.activeTextPartId = this.newTextPartId();
2062
+ return [sseData({ type: "text-start", id: this.activeTextPartId })];
2063
+ }
2064
+ emitTextDelta(rawDelta) {
2065
+ const delta = rawDelta ? this.options.redactText(rawDelta) : void 0;
2066
+ if (!delta)
2067
+ return [];
2068
+ const startChunk = this.activeTextPartId == null ? this.openTextStream() : [];
2069
+ return [
2070
+ ...startChunk,
2071
+ sseData({ type: "text-delta", id: this.activeTextPartId, delta })
2072
+ ];
2073
+ }
2074
+ endTextStreamIfOpen() {
2075
+ if (this.activeTextPartId == null)
2076
+ return [];
2077
+ const id = this.activeTextPartId;
2078
+ this.activeTextPartId = null;
2079
+ return [sseData({ type: "text-end", id })];
2080
+ }
2081
+ finishSuccess(usage) {
2082
+ const chunks = [...this.endTextStreamIfOpen()];
2083
+ const raw = {};
2084
+ let chatUsage;
2085
+ if (usage) {
2086
+ const { id } = this.options.model;
2087
+ chatUsage = {
2088
+ type: "chat",
2089
+ ...usageToMessageMetadata(usage)
2090
+ };
2091
+ raw[id] = chatUsage;
2092
+ }
2093
+ for (const [key, tally] of Object.entries(this.toolUsageTally)) {
2094
+ raw[key] = { ...tally };
2095
+ }
2096
+ const finishPayload = {
2097
+ type: "finish",
2098
+ finishReason: "stop"
2099
+ };
2100
+ if (usage) {
2101
+ finishPayload.messageMetadata = { usage: { ...chatUsage, raw } };
2102
+ }
2103
+ chunks.push(sseData(finishPayload), "data: [DONE]\n\n");
2104
+ this.hasFinished = true;
2105
+ return chunks;
2106
+ }
2107
+ finishError(errorText) {
2108
+ this.hasFinished = true;
2109
+ return emitStreamError(errorText);
2110
+ }
2111
+ };
1494
2112
 
1495
2113
  // ../../packages/runner-pi/dist/tool-overrides.js
1496
2114
  import { createBashTool, createReadTool } from "@mariozechner/pi-coding-agent";
@@ -1500,7 +2118,7 @@ var braveProvider = {
1500
2118
  id: "brave",
1501
2119
  label: "Brave Search",
1502
2120
  envKeys: ["BRAVE_API_KEY"],
1503
- async search({ apiKey, query, count, country, freshness }) {
2121
+ async search({ apiKey, query, count, country, freshness, signal }) {
1504
2122
  const params = new URLSearchParams({
1505
2123
  q: query,
1506
2124
  count: String(Math.min(count, 20))
@@ -1514,7 +2132,8 @@ var braveProvider = {
1514
2132
  Accept: "application/json",
1515
2133
  "Accept-Encoding": "gzip",
1516
2134
  "X-Subscription-Token": apiKey
1517
- }
2135
+ },
2136
+ signal
1518
2137
  });
1519
2138
  if (!res.ok) {
1520
2139
  const body = await res.text().catch(() => "");
@@ -1535,14 +2154,14 @@ ${body}`);
1535
2154
  });
1536
2155
  }
1537
2156
  }
1538
- return results;
2157
+ return { results };
1539
2158
  }
1540
2159
  };
1541
2160
  var tavilyProvider = {
1542
2161
  id: "tavily",
1543
2162
  label: "Tavily",
1544
2163
  envKeys: ["TAVILY_API_KEY"],
1545
- async search({ apiKey, query, count }) {
2164
+ async search({ apiKey, query, count, signal }) {
1546
2165
  const res = await fetch("https://api.tavily.com/search", {
1547
2166
  method: "POST",
1548
2167
  headers: { "Content-Type": "application/json" },
@@ -1551,7 +2170,8 @@ var tavilyProvider = {
1551
2170
  query,
1552
2171
  max_results: Math.min(count, 10),
1553
2172
  include_answer: false
1554
- })
2173
+ }),
2174
+ signal
1555
2175
  });
1556
2176
  if (!res.ok) {
1557
2177
  const body = await res.text().catch(() => "");
@@ -1569,7 +2189,7 @@ ${body}`);
1569
2189
  });
1570
2190
  }
1571
2191
  }
1572
- return results;
2192
+ return { results };
1573
2193
  }
1574
2194
  };
1575
2195
  var AUTO_DETECT_ORDER = [braveProvider, tavilyProvider];
@@ -1604,9 +2224,12 @@ var BROWSER_UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/53
1604
2224
  function htmlToText(html) {
1605
2225
  return html.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();
1606
2226
  }
1607
- async function fetchPageContent(url) {
2227
+ async function fetchPageContent(url, externalSignal) {
1608
2228
  const controller = new AbortController();
1609
2229
  const timeout = setTimeout(() => controller.abort(), 15e3);
2230
+ externalSignal?.addEventListener("abort", () => controller.abort(), {
2231
+ once: true
2232
+ });
1610
2233
  try {
1611
2234
  const res = await fetch(url, {
1612
2235
  headers: {
@@ -1703,7 +2326,7 @@ function buildWebSearchTool(env) {
1703
2326
  ],
1704
2327
  // biome-ignore lint/suspicious/noExplicitAny: plain JSON Schema compatible with TypeBox TSchema
1705
2328
  parameters: webSearchSchema,
1706
- async execute(_toolCallId, params, _signal, _onUpdate) {
2329
+ async execute(_toolCallId, params, signal, _onUpdate) {
1707
2330
  const p = params;
1708
2331
  const query = p.query;
1709
2332
  const count = p.count ?? 5;
@@ -1713,18 +2336,29 @@ function buildWebSearchTool(env) {
1713
2336
  let lastError;
1714
2337
  for (const { provider, apiKey } of providers) {
1715
2338
  try {
1716
- const results = await provider.search({
2339
+ const { results } = await provider.search({
1717
2340
  apiKey,
1718
2341
  query,
1719
2342
  count,
1720
2343
  country,
1721
- freshness
2344
+ freshness,
2345
+ signal
1722
2346
  });
2347
+ let fetchedPages = 0;
1723
2348
  if (shouldFetchContent) {
1724
2349
  for (const r of results) {
1725
- r.content = await fetchPageContent(r.link);
2350
+ r.content = await fetchPageContent(r.link, signal);
2351
+ fetchedPages += 1;
1726
2352
  }
1727
2353
  }
2354
+ const usage = {
2355
+ raw: {
2356
+ [provider.id]: {
2357
+ requests: 1,
2358
+ fetchedPages
2359
+ }
2360
+ }
2361
+ };
1728
2362
  return {
1729
2363
  content: [
1730
2364
  {
@@ -1732,7 +2366,9 @@ function buildWebSearchTool(env) {
1732
2366
  text: formatSearchResults(results, provider.label)
1733
2367
  }
1734
2368
  ],
1735
- details: void 0
2369
+ details: {
2370
+ usage
2371
+ }
1736
2372
  };
1737
2373
  } catch (e) {
1738
2374
  lastError = e;
@@ -1768,11 +2404,11 @@ function buildWebFetchTool() {
1768
2404
  ],
1769
2405
  // biome-ignore lint/suspicious/noExplicitAny: plain JSON Schema compatible with TypeBox TSchema
1770
2406
  parameters: webFetchSchema,
1771
- async execute(_toolCallId, params, _signal, _onUpdate) {
2407
+ async execute(_toolCallId, params, signal, _onUpdate) {
1772
2408
  const p = params;
1773
2409
  const url = p.url;
1774
2410
  try {
1775
- const content = await fetchPageContent(url);
2411
+ const content = await fetchPageContent(url, signal);
1776
2412
  return {
1777
2413
  content: [{ type: "text", text: content }],
1778
2414
  details: void 0
@@ -1915,52 +2551,6 @@ function applyModelOverrides(model, provider, optionsEnv) {
1915
2551
  model.baseUrl = anthropicBaseUrl;
1916
2552
  }
1917
2553
  }
1918
- function emitStreamError(errorText) {
1919
- return [
1920
- `data: ${JSON.stringify({ type: "error", errorText })}
1921
-
1922
- `,
1923
- `data: ${JSON.stringify({ type: "finish", finishReason: "error" })}
1924
-
1925
- `,
1926
- "data: [DONE]\n\n"
1927
- ];
1928
- }
1929
- function extractToolResultText(result) {
1930
- if (result !== null && typeof result === "object") {
1931
- const r = result;
1932
- if (Array.isArray(r.content) && r.content.length > 0) {
1933
- const text = r.content.filter((c) => c.type === "text" && typeof c.text === "string").map((c) => c.text).join("\n");
1934
- if (text.length > 0) {
1935
- return text;
1936
- }
1937
- }
1938
- }
1939
- if (typeof result === "string")
1940
- return result;
1941
- try {
1942
- return JSON.stringify(result);
1943
- } catch {
1944
- return String(result);
1945
- }
1946
- }
1947
- function usageToMessageMetadata(usage) {
1948
- return {
1949
- input_tokens: usage.input,
1950
- output_tokens: usage.output,
1951
- cache_read_input_tokens: usage.cacheRead,
1952
- cache_creation_input_tokens: usage.cacheWrite
1953
- };
1954
- }
1955
- function getUsageFromAgentEndMessages(messages) {
1956
- for (let i = messages.length - 1; i >= 0; i--) {
1957
- const m = messages[i];
1958
- if (m.role === "assistant" && m.usage != null) {
1959
- return m.usage;
1960
- }
1961
- }
1962
- return void 0;
1963
- }
1964
2554
  function getErrorFromAgentEndMessages(messages) {
1965
2555
  for (let i = messages.length - 1; i >= 0; i--) {
1966
2556
  const m = messages[i];
@@ -1976,7 +2566,7 @@ function traceRawMessage(debugCwd, data, reset = false, optionsEnv) {
1976
2566
  if (!enabled)
1977
2567
  return;
1978
2568
  try {
1979
- const file = join7(debugCwd, "pi-message-stream-debug.json");
2569
+ const file = join8(debugCwd, "pi-message-stream-debug.json");
1980
2570
  if (reset && existsSync5(file))
1981
2571
  unlinkSync3(file);
1982
2572
  const type = data !== null && typeof data === "object" ? data.type : void 0;
@@ -2043,13 +2633,26 @@ function createPiRunner(options = {}) {
2043
2633
  const sessionManager = await (async () => {
2044
2634
  if (resume !== void 0 && resume !== "") {
2045
2635
  if (resume.includes("/")) {
2046
- return SessionManager.open(resume);
2636
+ return SessionManager2.open(resume);
2637
+ }
2638
+ const sessionPath2 = resolveSessionPathById(cwd, resume);
2639
+ console.error(`${LOG_PREFIX2} resume: id=${resume} path=${sessionPath2 ?? "(not found)"}`);
2640
+ if (sessionPath2) {
2641
+ if (isSessionFileTooLarge(sessionPath2)) {
2642
+ const context = extractSessionContext(sessionPath2);
2643
+ console.error(`${LOG_PREFIX2} session file too large, starting fresh${context ? " (with context)" : ""}`);
2644
+ const newMgr = SessionManager2.create(cwd);
2645
+ if (context) {
2646
+ const firstId = newMgr.getEntries()[0]?.id ?? "";
2647
+ newMgr.appendCompaction(context, firstId, 0);
2648
+ }
2649
+ return newMgr;
2650
+ }
2651
+ return SessionManager2.open(sessionPath2);
2047
2652
  }
2048
- const sessions = await SessionManager.list(cwd);
2049
- const found = sessions.find((s) => s.id === resume);
2050
- return found ? SessionManager.open(found.path) : SessionManager.create(cwd);
2653
+ return SessionManager2.create(cwd);
2051
2654
  }
2052
- return SessionManager.create(cwd);
2655
+ return SessionManager2.create(cwd);
2053
2656
  })();
2054
2657
  const resourceLoader = options.skillPaths ? new BunnyAgentResourceLoader({
2055
2658
  cwd,
@@ -2065,7 +2668,7 @@ function createPiRunner(options = {}) {
2065
2668
  const customTools = options.env && Object.keys(options.env).length > 0 ? buildSecretAwareTools(cwd, options.env) : [];
2066
2669
  if (imageModelName) {
2067
2670
  const apiKey = await modelRegistry.authStorage.getApiKey(provider) ?? "";
2068
- customTools.push(buildImageGenerateTool(cwd, imageModelName, model.baseUrl, apiKey));
2671
+ customTools.push(buildImageGenerateTool(cwd, imageModelName, model.baseUrl, apiKey), buildImageEditTool(cwd, imageModelName, model.baseUrl, apiKey));
2069
2672
  }
2070
2673
  const { session } = await createAgentSession({
2071
2674
  cwd,
@@ -2105,165 +2708,34 @@ function createPiRunner(options = {}) {
2105
2708
  }
2106
2709
  try {
2107
2710
  traceRawMessage(cwd, null, true, options.env);
2108
- let promptText = userInput;
2109
- let images;
2110
- try {
2111
- if (userInput.startsWith("[") && userInput.endsWith("]")) {
2112
- const parsed = JSON.parse(userInput);
2113
- if (Array.isArray(parsed)) {
2114
- promptText = parsed.filter((p) => p.type === "text").map((p) => p.text).join("\n");
2115
- const imageParts = parsed.filter((p) => p.type === "image");
2116
- if (imageParts.length > 0) {
2117
- images = imageParts.map((p) => ({
2118
- type: "image",
2119
- data: p.data,
2120
- mimeType: p.mimeType
2121
- }));
2122
- }
2711
+ const promptText = userInput;
2712
+ const promptPromise = session.prompt(promptText);
2713
+ const streamConverter = new PiAISDKStreamConverter({
2714
+ sessionId: session.sessionId,
2715
+ model,
2716
+ redactText: (value) => {
2717
+ if (options.env && Object.keys(options.env).length > 0) {
2718
+ return redactSecrets(value, options.env);
2123
2719
  }
2124
- }
2125
- } catch (_e) {
2126
- }
2127
- const promptPromise = session.prompt(promptText, images ? { images } : void 0);
2128
- const messageId = `msg_${Date.now()}_${Math.random().toString(36).slice(2)}`;
2129
- let hasStarted = false;
2130
- let hasFinished = false;
2131
- const imageToolUsage = { input_tokens: 0, output_tokens: 0 };
2132
- const newTextPartId = () => `text_${Date.now()}_${Math.random().toString(36).slice(2)}_${Math.random().toString(36).slice(2)}`;
2133
- let activeTextPartId = null;
2134
- let textStreamOpen = false;
2135
- const endTextStreamIfOpen = function* () {
2136
- if (textStreamOpen && activeTextPartId != null) {
2137
- yield `data: ${JSON.stringify({ type: "text-end", id: activeTextPartId })}
2138
-
2139
- `;
2140
- textStreamOpen = false;
2141
- activeTextPartId = null;
2142
- }
2143
- };
2144
- const beginTextStream = function* () {
2145
- activeTextPartId = newTextPartId();
2146
- yield `data: ${JSON.stringify({ type: "text-start", id: activeTextPartId })}
2147
-
2148
- `;
2149
- textStreamOpen = true;
2150
- };
2151
- const ensureStartEvent = async function* () {
2152
- if (!hasStarted) {
2153
- yield `data: ${JSON.stringify({ type: "start", messageId })}
2154
-
2155
- `;
2156
- yield `data: ${JSON.stringify({
2157
- type: "message-metadata",
2158
- messageMetadata: { sessionId: session.sessionId }
2159
- })}
2160
-
2161
- `;
2162
- hasStarted = true;
2163
- }
2164
- };
2165
- const finishSuccess = async function* (usage) {
2166
- yield* endTextStreamIfOpen();
2167
- const finishPayload = { type: "finish", finishReason: "stop" };
2168
- const hasImageUsage = imageToolUsage.input_tokens > 0 || imageToolUsage.output_tokens > 0;
2169
- if (usage != null || hasImageUsage) {
2170
- const base = usage != null ? usageToMessageMetadata(usage) : {};
2171
- finishPayload.messageMetadata = {
2172
- usage: {
2173
- ...base,
2174
- input_tokens: (base.input_tokens ?? 0) + imageToolUsage.input_tokens,
2175
- output_tokens: (base.output_tokens ?? 0) + imageToolUsage.output_tokens
2176
- }
2177
- };
2178
- }
2179
- yield `data: ${JSON.stringify(finishPayload)}
2180
-
2181
- `;
2182
- yield "data: [DONE]\n\n";
2183
- hasFinished = true;
2184
- };
2185
- const finishError = async function* (errorText) {
2186
- for (const chunk of emitStreamError(errorText)) {
2187
- yield chunk;
2188
- }
2189
- hasFinished = true;
2190
- };
2720
+ return value;
2721
+ },
2722
+ normalizeToolOutput: extractToolResultText,
2723
+ getUsageFromAgentEndMessages,
2724
+ getErrorFromAgentEndMessages
2725
+ });
2191
2726
  while (!isComplete || eventQueue.length > 0) {
2192
2727
  while (eventQueue.length > 0) {
2193
2728
  const event = eventQueue.shift();
2194
2729
  traceRawMessage(cwd, event, false, options.env);
2195
- yield* ensureStartEvent();
2196
- if (event.type === "message_start") {
2197
- const msg = event.message;
2198
- if (msg?.role === "assistant") {
2199
- yield* endTextStreamIfOpen();
2200
- }
2201
- } else if (event.type === "message_update") {
2202
- const sub = event.assistantMessageEvent;
2203
- if (sub.type === "text_start") {
2204
- yield* endTextStreamIfOpen();
2205
- yield* beginTextStream();
2206
- } else if (sub.type === "text_delta") {
2207
- let delta = sub.delta;
2208
- if (delta) {
2209
- if (options.env && Object.keys(options.env).length > 0) {
2210
- delta = redactSecrets(delta, options.env);
2211
- }
2212
- if (!textStreamOpen) {
2213
- yield* beginTextStream();
2214
- }
2215
- yield `data: ${JSON.stringify({
2216
- type: "text-delta",
2217
- id: activeTextPartId,
2218
- delta
2219
- })}
2220
-
2221
- `;
2222
- }
2223
- } else if (sub.type === "toolcall_start") {
2224
- yield* endTextStreamIfOpen();
2225
- }
2226
- } else if (event.type === "tool_execution_start") {
2227
- yield* endTextStreamIfOpen();
2228
- yield `data: ${JSON.stringify({ type: "tool-input-start", toolCallId: event.toolCallId, toolName: event.toolName, dynamic: true, providerExecuted: true })}
2229
-
2230
- `;
2231
- yield `data: ${JSON.stringify({ type: "tool-input-available", toolCallId: event.toolCallId, toolName: event.toolName, input: event.args, dynamic: true, providerExecuted: true })}
2232
-
2233
- `;
2234
- } else if (event.type === "tool_execution_end") {
2235
- let output = extractToolResultText(event.result);
2236
- if (options.env && Object.keys(options.env).length > 0) {
2237
- output = redactSecrets(output, options.env);
2238
- }
2239
- if (event.toolName === "generate_image" && event.result !== null && typeof event.result === "object") {
2240
- const details = event.result.details;
2241
- const u = details?.response?.usage;
2242
- if (u) {
2243
- imageToolUsage.input_tokens += u.input_tokens ?? 0;
2244
- imageToolUsage.output_tokens += u.output_tokens ?? 0;
2245
- }
2246
- }
2247
- yield `data: ${JSON.stringify({ type: "tool-output-available", toolCallId: event.toolCallId, output, isError: event.isError, dynamic: true, providerExecuted: true })}
2248
-
2249
- `;
2250
- } else if (event.type === "agent_end") {
2251
- if (aborted) {
2252
- yield* finishError("Run aborted by signal.");
2253
- } else {
2254
- const errorMsg = getErrorFromAgentEndMessages(event.messages);
2255
- if (errorMsg) {
2256
- yield* finishError(errorMsg);
2257
- } else {
2258
- const usage = getUsageFromAgentEndMessages(event.messages);
2259
- yield* finishSuccess(usage);
2260
- }
2261
- }
2730
+ const chunks = streamConverter.handleEvent(event, aborted);
2731
+ for (const chunk of chunks) {
2732
+ yield chunk;
2262
2733
  }
2263
2734
  }
2264
- if (aborted && !hasFinished) {
2265
- yield* ensureStartEvent();
2266
- yield* finishError("Run aborted by signal.");
2735
+ if (aborted && !streamConverter.finished) {
2736
+ for (const chunk of streamConverter.forceError("Run aborted by signal.")) {
2737
+ yield chunk;
2738
+ }
2267
2739
  break;
2268
2740
  }
2269
2741
  if (!isComplete && eventQueue.length === 0) {
@@ -2272,22 +2744,24 @@ function createPiRunner(options = {}) {
2272
2744
  });
2273
2745
  }
2274
2746
  }
2275
- if (hasFinished) {
2747
+ if (streamConverter.finished) {
2276
2748
  return;
2277
2749
  }
2278
2750
  try {
2279
2751
  await promptPromise;
2280
2752
  } catch (error) {
2281
- if (!hasFinished) {
2282
- yield* ensureStartEvent();
2753
+ if (!streamConverter.finished) {
2283
2754
  const message = error instanceof Error ? error.message : "Pi agent run failed.";
2284
- yield* finishError(message);
2755
+ for (const chunk of streamConverter.forceError(message)) {
2756
+ yield chunk;
2757
+ }
2285
2758
  }
2286
2759
  return;
2287
2760
  }
2288
- if (!hasFinished && session.agent.state.error) {
2289
- yield* ensureStartEvent();
2290
- yield* finishError(session.agent.state.error);
2761
+ if (!streamConverter.finished && session.agent.state.error) {
2762
+ for (const chunk of streamConverter.forceError(session.agent.state.error)) {
2763
+ yield chunk;
2764
+ }
2291
2765
  }
2292
2766
  } finally {
2293
2767
  if (abortSignal) {
@@ -2307,11 +2781,11 @@ function createPiRunner(options = {}) {
2307
2781
 
2308
2782
  // ../../packages/runner-harness/dist/session.js
2309
2783
  import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "node:fs";
2310
- import { join as join8 } from "node:path";
2784
+ import { join as join9 } from "node:path";
2311
2785
  var DIR = ".bunny-agent";
2312
2786
  var FILE = "session-id";
2313
2787
  function sessionPath(cwd) {
2314
- return join8(cwd, DIR, FILE);
2788
+ return join9(cwd, DIR, FILE);
2315
2789
  }
2316
2790
  function readSessionId(cwd) {
2317
2791
  try {
@@ -2325,28 +2799,28 @@ function readSessionId(cwd) {
2325
2799
  }
2326
2800
  function writeSessionId(cwd, id) {
2327
2801
  try {
2328
- mkdirSync3(join8(cwd, DIR), { recursive: true });
2802
+ mkdirSync3(join9(cwd, DIR), { recursive: true });
2329
2803
  writeFileSync4(sessionPath(cwd), id, "utf8");
2330
2804
  } catch {
2331
2805
  }
2332
2806
  }
2333
2807
 
2334
2808
  // ../../packages/runner-harness/dist/skills.js
2335
- import { existsSync as existsSync7, readdirSync as readdirSync2, statSync as statSync2 } from "node:fs";
2809
+ import { existsSync as existsSync7, readdirSync as readdirSync3, statSync as statSync3 } from "node:fs";
2336
2810
  import { homedir as homedir2 } from "node:os";
2337
- import { join as join9 } from "node:path";
2811
+ import { join as join10 } from "node:path";
2338
2812
  function discoverSkillPaths(cwd) {
2339
2813
  const paths = [];
2340
2814
  for (const base of [
2341
- join9(cwd, "skills"),
2342
- join9(homedir2(), ".bunny-agent", "skills")
2815
+ join10(cwd, "skills"),
2816
+ join10(homedir2(), ".bunny-agent", "skills")
2343
2817
  ]) {
2344
2818
  if (!existsSync7(base))
2345
2819
  continue;
2346
2820
  try {
2347
- for (const entry of readdirSync2(base)) {
2348
- const full = join9(base, entry);
2349
- if (statSync2(full).isDirectory() && existsSync7(join9(full, "SKILL.md"))) {
2821
+ for (const entry of readdirSync3(base)) {
2822
+ const full = join10(base, entry);
2823
+ if (statSync3(full).isDirectory() && existsSync7(join10(full, "SKILL.md"))) {
2350
2824
  paths.push(full);
2351
2825
  }
2352
2826
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bunny-agent/runner-cli",
3
- "version": "0.9.28",
3
+ "version": "0.9.29-beta.10",
4
4
  "description": "BunnyAgent Runner CLI - Like gemini-cli or claude-code, runs in your local terminal with AI SDK UI streaming",
5
5
  "type": "module",
6
6
  "bin": {
@@ -55,8 +55,8 @@
55
55
  "vitest": "^1.6.1",
56
56
  "@bunny-agent/runner-harness": "0.1.1-beta.0",
57
57
  "@bunny-agent/runner-claude": "0.6.2",
58
- "@bunny-agent/runner-codex": "0.6.2",
59
58
  "@bunny-agent/runner-gemini": "0.6.2",
59
+ "@bunny-agent/runner-codex": "0.6.2",
60
60
  "@bunny-agent/runner-opencode": "0.6.2",
61
61
  "@bunny-agent/runner-pi": "0.6.4-beta.0"
62
62
  },