@bunny-agent/runner-cli 0.9.41 → 0.9.42
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/bundle.mjs +40 -321
- package/package.json +3 -3
package/dist/bundle.mjs
CHANGED
|
@@ -1277,283 +1277,16 @@ function createOpenCodeRunner(options = {}) {
|
|
|
1277
1277
|
};
|
|
1278
1278
|
}
|
|
1279
1279
|
|
|
1280
|
-
// ../../packages/runner-pi/dist/ask-user-question-tool.js
|
|
1281
|
-
import { createHash } from "node:crypto";
|
|
1282
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
1283
|
-
import { join as join5 } from "node:path";
|
|
1284
|
-
var TOOL_NAME = "ask_user_question";
|
|
1285
|
-
var APPROVAL_DIR = join5(".bunny-agent", "approvals");
|
|
1286
|
-
var ASK_USER_QUESTION_TIMEOUT_MS = 12e4;
|
|
1287
|
-
var ASK_USER_QUESTION_POLL_MS = 500;
|
|
1288
|
-
var MAX_QUESTIONS = 4;
|
|
1289
|
-
var MIN_OPTIONS = 2;
|
|
1290
|
-
var MAX_OPTIONS = 4;
|
|
1291
|
-
var MAX_HEADER_LEN = 12;
|
|
1292
|
-
var MAX_QUESTION_LEN = 500;
|
|
1293
|
-
var MAX_OPTION_LABEL_LEN = 80;
|
|
1294
|
-
var MAX_OPTION_DESCRIPTION_LEN = 240;
|
|
1295
|
-
var ESC = String.fromCharCode(27);
|
|
1296
|
-
var CSI = String.fromCharCode(155);
|
|
1297
|
-
var ST = String.fromCharCode(156);
|
|
1298
|
-
var OSC = String.fromCharCode(157);
|
|
1299
|
-
var BEL = String.fromCharCode(7);
|
|
1300
|
-
var ANSI_OSC_RE = new RegExp(`(?:${ESC}\\]|${OSC})[^${ESC}${ST}${BEL}]*(?:${BEL}|${ESC}\\\\|${ST})`, "g");
|
|
1301
|
-
var ANSI_CTL_RE = new RegExp(`(?:${ESC}\\[[0-?]*[ -/]*[@-~]|${CSI}[0-?]*[ -/]*[@-~]|${ESC}[@-Z\\\\\\-_])`, "g");
|
|
1302
|
-
var CTL_CHAR_RE = new RegExp(`[${String.fromCharCode(0)}-${String.fromCharCode(31)}${String.fromCharCode(127)}-${String.fromCharCode(159)}]+`, "g");
|
|
1303
|
-
var askUserQuestionSchema = {
|
|
1304
|
-
type: "object",
|
|
1305
|
-
required: ["questions"],
|
|
1306
|
-
properties: {
|
|
1307
|
-
questions: {
|
|
1308
|
-
type: "array",
|
|
1309
|
-
minItems: 1,
|
|
1310
|
-
maxItems: MAX_QUESTIONS,
|
|
1311
|
-
description: `Between 1 and ${MAX_QUESTIONS} questions to ask the user.`,
|
|
1312
|
-
items: {
|
|
1313
|
-
type: "object",
|
|
1314
|
-
required: ["question", "header", "multiSelect", "options"],
|
|
1315
|
-
properties: {
|
|
1316
|
-
question: {
|
|
1317
|
-
type: "string",
|
|
1318
|
-
description: "The full question to ask the user. Should be specific, end with a question mark, and avoid jargon the user has not used."
|
|
1319
|
-
},
|
|
1320
|
-
header: {
|
|
1321
|
-
type: "string",
|
|
1322
|
-
description: `Short label (max ${MAX_HEADER_LEN} chars) shown as a chip/tag in the UI. Examples: "Library", "Auth method", "Approach".`
|
|
1323
|
-
},
|
|
1324
|
-
multiSelect: {
|
|
1325
|
-
type: "boolean",
|
|
1326
|
-
description: "Set true when the choices are not mutually exclusive and the user may select more than one."
|
|
1327
|
-
},
|
|
1328
|
-
options: {
|
|
1329
|
-
type: "array",
|
|
1330
|
-
minItems: MIN_OPTIONS,
|
|
1331
|
-
maxItems: MAX_OPTIONS,
|
|
1332
|
-
description: `Between ${MIN_OPTIONS} and ${MAX_OPTIONS} mutually exclusive options. Do not add an "Other" option \u2014 the host adds one automatically.`,
|
|
1333
|
-
items: {
|
|
1334
|
-
type: "object",
|
|
1335
|
-
required: ["label", "description"],
|
|
1336
|
-
properties: {
|
|
1337
|
-
label: {
|
|
1338
|
-
type: "string",
|
|
1339
|
-
description: "Display text for the option (1-5 words). Should clearly describe the choice."
|
|
1340
|
-
},
|
|
1341
|
-
description: {
|
|
1342
|
-
type: "string",
|
|
1343
|
-
description: "What this option means or what happens if chosen \u2014 call out trade-offs."
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1347
|
-
}
|
|
1348
|
-
}
|
|
1349
|
-
}
|
|
1350
|
-
}
|
|
1351
|
-
}
|
|
1352
|
-
};
|
|
1353
|
-
var TOOL_DESCRIPTION = `Ask the user one or more multiple-choice questions when the task is genuinely ambiguous and you need user input to proceed (choosing between libraries, design approaches, trade-offs, etc.). Each question carries 2-${MAX_OPTIONS} options; up to ${MAX_QUESTIONS} questions per call. The user can also select an automatically-provided "Other" option to give free-form input. Use sparingly \u2014 prefer making a sensible default choice when the task is clear.`;
|
|
1354
|
-
function sanitizeText(value) {
|
|
1355
|
-
if (typeof value !== "string")
|
|
1356
|
-
return "";
|
|
1357
|
-
return value.replace(ANSI_OSC_RE, "").replace(ANSI_CTL_RE, "").replace(CTL_CHAR_RE, " ").replace(/\s+/g, " ").trim();
|
|
1358
|
-
}
|
|
1359
|
-
function truncate(value, max) {
|
|
1360
|
-
return value.length <= max ? value : `${value.slice(0, Math.max(0, max - 3))}...`;
|
|
1361
|
-
}
|
|
1362
|
-
function sanitizeQuestions(questions) {
|
|
1363
|
-
return questions.map((q) => ({
|
|
1364
|
-
question: truncate(sanitizeText(q.question), MAX_QUESTION_LEN),
|
|
1365
|
-
header: truncate(sanitizeText(q.header), MAX_HEADER_LEN),
|
|
1366
|
-
multiSelect: !!q.multiSelect,
|
|
1367
|
-
options: q.options.map((o) => ({
|
|
1368
|
-
label: truncate(sanitizeText(o.label), MAX_OPTION_LABEL_LEN),
|
|
1369
|
-
description: truncate(sanitizeText(o.description), MAX_OPTION_DESCRIPTION_LEN)
|
|
1370
|
-
}))
|
|
1371
|
-
}));
|
|
1372
|
-
}
|
|
1373
|
-
function readApproval(file) {
|
|
1374
|
-
try {
|
|
1375
|
-
const raw = readFileSync3(file, "utf-8");
|
|
1376
|
-
return JSON.parse(raw);
|
|
1377
|
-
} catch {
|
|
1378
|
-
return void 0;
|
|
1379
|
-
}
|
|
1380
|
-
}
|
|
1381
|
-
var TOOL_CALL_ID_PREFIX_LEN = 32;
|
|
1382
|
-
var TOOL_CALL_ID_HASH_LEN = 16;
|
|
1383
|
-
function sanitizeToolCallId(toolCallId) {
|
|
1384
|
-
const safePrefix = toolCallId.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, TOOL_CALL_ID_PREFIX_LEN);
|
|
1385
|
-
const hash = createHash("sha1").update(toolCallId).digest("hex").slice(0, TOOL_CALL_ID_HASH_LEN);
|
|
1386
|
-
return `${safePrefix}-${hash}`;
|
|
1387
|
-
}
|
|
1388
|
-
function safeUnlink(file) {
|
|
1389
|
-
try {
|
|
1390
|
-
if (existsSync4(file))
|
|
1391
|
-
unlinkSync3(file);
|
|
1392
|
-
} catch {
|
|
1393
|
-
}
|
|
1394
|
-
}
|
|
1395
|
-
async function pollApprovalFile(approvalFile, signal, options = {}) {
|
|
1396
|
-
const timeoutMs = options.timeoutMs ?? ASK_USER_QUESTION_TIMEOUT_MS;
|
|
1397
|
-
const pollMs = options.pollMs ?? ASK_USER_QUESTION_POLL_MS;
|
|
1398
|
-
const deadline = Date.now() + timeoutMs;
|
|
1399
|
-
let lastApproval;
|
|
1400
|
-
while (true) {
|
|
1401
|
-
if (signal?.aborted) {
|
|
1402
|
-
return { kind: "aborted", answers: lastApproval?.answers ?? {} };
|
|
1403
|
-
}
|
|
1404
|
-
const approval = readApproval(approvalFile);
|
|
1405
|
-
if (approval)
|
|
1406
|
-
lastApproval = approval;
|
|
1407
|
-
if (approval && approval.status !== "pending") {
|
|
1408
|
-
const answers = approval.answers ?? {};
|
|
1409
|
-
if (approval.status === "completed" && Object.keys(answers).length > 0) {
|
|
1410
|
-
return { kind: "answered", answers };
|
|
1411
|
-
}
|
|
1412
|
-
if (approval.status === "declined") {
|
|
1413
|
-
return { kind: "declined", answers, reason: approval.reason };
|
|
1414
|
-
}
|
|
1415
|
-
if (approval.status === "cancelled") {
|
|
1416
|
-
return { kind: "cancelled", answers, reason: approval.reason };
|
|
1417
|
-
}
|
|
1418
|
-
return {
|
|
1419
|
-
kind: "declined",
|
|
1420
|
-
answers,
|
|
1421
|
-
reason: approval.reason ?? "no_answer_provided"
|
|
1422
|
-
};
|
|
1423
|
-
}
|
|
1424
|
-
if (Date.now() >= deadline) {
|
|
1425
|
-
const partial = lastApproval?.answers ?? {};
|
|
1426
|
-
return {
|
|
1427
|
-
kind: Object.keys(partial).length > 0 ? "answered" : "timeout",
|
|
1428
|
-
answers: partial
|
|
1429
|
-
};
|
|
1430
|
-
}
|
|
1431
|
-
await sleepWithAbort(Math.min(pollMs, Math.max(0, deadline - Date.now())), signal);
|
|
1432
|
-
}
|
|
1433
|
-
}
|
|
1434
|
-
function sleepWithAbort(ms, signal) {
|
|
1435
|
-
return new Promise((resolve4) => {
|
|
1436
|
-
if (ms <= 0) {
|
|
1437
|
-
resolve4();
|
|
1438
|
-
return;
|
|
1439
|
-
}
|
|
1440
|
-
if (signal?.aborted) {
|
|
1441
|
-
resolve4();
|
|
1442
|
-
return;
|
|
1443
|
-
}
|
|
1444
|
-
const onAbort = () => {
|
|
1445
|
-
clearTimeout(timer);
|
|
1446
|
-
resolve4();
|
|
1447
|
-
};
|
|
1448
|
-
const timer = setTimeout(() => {
|
|
1449
|
-
signal?.removeEventListener("abort", onAbort);
|
|
1450
|
-
resolve4();
|
|
1451
|
-
}, ms);
|
|
1452
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1453
|
-
});
|
|
1454
|
-
}
|
|
1455
|
-
function formatAcceptedText(questions, answers) {
|
|
1456
|
-
const lines = ["User answered:"];
|
|
1457
|
-
for (const q of questions) {
|
|
1458
|
-
const raw = answers[q.question];
|
|
1459
|
-
const formatted = Array.isArray(raw) ? raw.join(", ") : typeof raw === "string" && raw.length > 0 ? raw : "(no answer)";
|
|
1460
|
-
lines.push(`Q: ${q.question}`);
|
|
1461
|
-
lines.push(`A: ${formatted}`);
|
|
1462
|
-
}
|
|
1463
|
-
return lines.join("\n");
|
|
1464
|
-
}
|
|
1465
|
-
function formatDeclinedText(reason, partial) {
|
|
1466
|
-
const partialNote = Object.keys(partial).length > 0 ? ` Partial answers received: ${JSON.stringify(partial)}.` : "";
|
|
1467
|
-
return `User did not answer the questions (reason: ${reason}). Make a sensible default choice and continue, or ask again if the choice is genuinely required.${partialNote}`;
|
|
1468
|
-
}
|
|
1469
|
-
function buildAskUserQuestionTool(opts) {
|
|
1470
|
-
const { cwd, timeoutMs, pollMs } = opts;
|
|
1471
|
-
return {
|
|
1472
|
-
name: TOOL_NAME,
|
|
1473
|
-
label: "ask_user_question",
|
|
1474
|
-
description: TOOL_DESCRIPTION,
|
|
1475
|
-
promptSnippet: "ask_user_question: ask the user 1-4 multiple-choice questions when the task is genuinely ambiguous.",
|
|
1476
|
-
promptGuidelines: [
|
|
1477
|
-
"Use ask_user_question only when the task is genuinely ambiguous and a user choice is required to proceed; otherwise pick a sensible default.",
|
|
1478
|
-
"Each question must carry 2-4 mutually exclusive options. Do not add an 'Other' option \u2014 the host adds one automatically."
|
|
1479
|
-
],
|
|
1480
|
-
// Pause other tool calls while a user prompt is open; otherwise pi may
|
|
1481
|
-
// surface output the user has not yet had a chance to react to.
|
|
1482
|
-
executionMode: "sequential",
|
|
1483
|
-
// biome-ignore lint/suspicious/noExplicitAny: TypeBox accepts plain JSON Schema literals here, see image-tools.ts.
|
|
1484
|
-
parameters: askUserQuestionSchema,
|
|
1485
|
-
async execute(toolCallId, params, signal) {
|
|
1486
|
-
const raw = params.questions ?? [];
|
|
1487
|
-
const sanitized = sanitizeQuestions(raw);
|
|
1488
|
-
const sanitizedInput = { questions: sanitized };
|
|
1489
|
-
const approvalDir = join5(cwd, APPROVAL_DIR);
|
|
1490
|
-
const approvalFile = join5(approvalDir, `${sanitizeToolCallId(toolCallId)}.json`);
|
|
1491
|
-
try {
|
|
1492
|
-
mkdirSync2(approvalDir, { recursive: true });
|
|
1493
|
-
const pending = {
|
|
1494
|
-
status: "pending",
|
|
1495
|
-
toolName: TOOL_NAME,
|
|
1496
|
-
input: sanitizedInput,
|
|
1497
|
-
questions: sanitized,
|
|
1498
|
-
answers: {}
|
|
1499
|
-
};
|
|
1500
|
-
writeFileSync3(approvalFile, JSON.stringify(pending));
|
|
1501
|
-
} catch (error) {
|
|
1502
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1503
|
-
return {
|
|
1504
|
-
content: [
|
|
1505
|
-
{
|
|
1506
|
-
type: "text",
|
|
1507
|
-
text: `ask_user_question failed to create approval file: ${message}`
|
|
1508
|
-
}
|
|
1509
|
-
],
|
|
1510
|
-
details: void 0
|
|
1511
|
-
};
|
|
1512
|
-
}
|
|
1513
|
-
let outcome;
|
|
1514
|
-
try {
|
|
1515
|
-
outcome = await pollApprovalFile(approvalFile, signal, {
|
|
1516
|
-
timeoutMs,
|
|
1517
|
-
pollMs
|
|
1518
|
-
});
|
|
1519
|
-
} finally {
|
|
1520
|
-
safeUnlink(approvalFile);
|
|
1521
|
-
}
|
|
1522
|
-
if (outcome.kind === "answered") {
|
|
1523
|
-
return {
|
|
1524
|
-
content: [
|
|
1525
|
-
{
|
|
1526
|
-
type: "text",
|
|
1527
|
-
text: formatAcceptedText(sanitized, outcome.answers)
|
|
1528
|
-
}
|
|
1529
|
-
],
|
|
1530
|
-
details: void 0
|
|
1531
|
-
};
|
|
1532
|
-
}
|
|
1533
|
-
const reason = outcome.kind === "timeout" ? "timeout" : outcome.kind === "aborted" ? "run_aborted" : outcome.reason ?? outcome.kind;
|
|
1534
|
-
return {
|
|
1535
|
-
content: [
|
|
1536
|
-
{
|
|
1537
|
-
type: "text",
|
|
1538
|
-
text: formatDeclinedText(reason, outcome.answers)
|
|
1539
|
-
}
|
|
1540
|
-
],
|
|
1541
|
-
details: void 0
|
|
1542
|
-
};
|
|
1543
|
-
}
|
|
1544
|
-
};
|
|
1545
|
-
}
|
|
1546
|
-
|
|
1547
1280
|
// ../../packages/runner-pi/dist/pi-runner.js
|
|
1548
|
-
import { appendFileSync as appendFileSync2, existsSync as
|
|
1549
|
-
import { join as
|
|
1281
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync5, unlinkSync as unlinkSync3 } from "node:fs";
|
|
1282
|
+
import { join as join8 } from "node:path";
|
|
1550
1283
|
import { getModel } from "@earendil-works/pi-ai";
|
|
1551
1284
|
import { AuthStorage, createAgentSession, ModelRegistry, SessionManager as SessionManager2 } from "@earendil-works/pi-coding-agent";
|
|
1552
1285
|
|
|
1553
1286
|
// ../../packages/runner-pi/dist/bunny-agent-resource-loader.js
|
|
1554
|
-
import { existsSync as
|
|
1287
|
+
import { existsSync as existsSync4 } from "node:fs";
|
|
1555
1288
|
import { homedir } from "node:os";
|
|
1556
|
-
import { isAbsolute, join as
|
|
1289
|
+
import { isAbsolute, join as join5, resolve as resolve2 } from "node:path";
|
|
1557
1290
|
import { DefaultResourceLoader, loadSkills } from "@earendil-works/pi-coding-agent";
|
|
1558
1291
|
var LOG_PREFIX = "[bunny-agent:pi]";
|
|
1559
1292
|
function logSkillLoad(cwd, agentDir, skillPaths, result) {
|
|
@@ -1565,7 +1298,7 @@ function logSkillLoad(cwd, agentDir, skillPaths, result) {
|
|
|
1565
1298
|
];
|
|
1566
1299
|
for (const raw of skillPaths) {
|
|
1567
1300
|
const abs = isAbsolute(raw) ? raw : resolve2(cwd, raw);
|
|
1568
|
-
lines.push(` ${raw} -> ${abs} (exists: ${
|
|
1301
|
+
lines.push(` ${raw} -> ${abs} (exists: ${existsSync4(abs) ? "yes" : "no"})`);
|
|
1569
1302
|
}
|
|
1570
1303
|
lines.push(` loaded skills: ${result.skills.length}`);
|
|
1571
1304
|
if (result.skills.length > 0) {
|
|
@@ -1583,7 +1316,7 @@ function logSkillLoad(cwd, agentDir, skillPaths, result) {
|
|
|
1583
1316
|
var BunnyAgentResourceLoader = class {
|
|
1584
1317
|
constructor(options = {}) {
|
|
1585
1318
|
this.cwd = options.cwd ?? process.cwd();
|
|
1586
|
-
this.agentDir = options.agentDir ??
|
|
1319
|
+
this.agentDir = options.agentDir ?? join5(homedir(), ".bunny", "agent");
|
|
1587
1320
|
this.skillPaths = options.skillPaths ?? [];
|
|
1588
1321
|
this.extraAppendPrompt = options.appendSystemPrompt;
|
|
1589
1322
|
this.delegate = new DefaultResourceLoader({
|
|
@@ -1640,8 +1373,8 @@ var BunnyAgentResourceLoader = class {
|
|
|
1640
1373
|
};
|
|
1641
1374
|
|
|
1642
1375
|
// ../../packages/runner-pi/dist/image-tools.js
|
|
1643
|
-
import { mkdirSync as
|
|
1644
|
-
import { dirname as dirname3, extname, join as
|
|
1376
|
+
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
1377
|
+
import { dirname as dirname3, extname, join as join6 } from "node:path";
|
|
1645
1378
|
var generateImageSchema = {
|
|
1646
1379
|
type: "object",
|
|
1647
1380
|
properties: {
|
|
@@ -1874,8 +1607,8 @@ async function saveImageItem(item, filePath, apiKey) {
|
|
|
1874
1607
|
const b64 = await resolveB64(item, apiKey);
|
|
1875
1608
|
if (!b64)
|
|
1876
1609
|
return void 0;
|
|
1877
|
-
|
|
1878
|
-
|
|
1610
|
+
mkdirSync2(dirname3(filePath), { recursive: true });
|
|
1611
|
+
writeFileSync3(filePath, Buffer.from(b64, "base64"));
|
|
1879
1612
|
return filePath;
|
|
1880
1613
|
}
|
|
1881
1614
|
function buildImageGenerateTool(cwd, imageModelId, baseUrl, apiKey) {
|
|
@@ -1902,7 +1635,7 @@ function buildImageGenerateTool(cwd, imageModelId, baseUrl, apiKey) {
|
|
|
1902
1635
|
const imageSize = p.imageSize;
|
|
1903
1636
|
const rawFilename = p.filename;
|
|
1904
1637
|
const filename = rawFilename ? extname(rawFilename) ? rawFilename : `${rawFilename}.png` : `image_${Date.now()}.png`;
|
|
1905
|
-
const filePath =
|
|
1638
|
+
const filePath = join6(cwd, filename.replace(/[^a-zA-Z0-9_\-./]/g, "_"));
|
|
1906
1639
|
try {
|
|
1907
1640
|
const url = `${baseUrl.replace(/\/$/, "")}/v1/images/generations`;
|
|
1908
1641
|
const res = await fetch(url, {
|
|
@@ -2050,7 +1783,7 @@ function buildImageEditTool(cwd, imageModelId, baseUrl, apiKey) {
|
|
|
2050
1783
|
// biome-ignore lint/suspicious/noExplicitAny: plain JSON Schema compatible with TypeBox TSchema
|
|
2051
1784
|
parameters: editImageSchema,
|
|
2052
1785
|
async execute(_toolCallId, params, signal, _onUpdate) {
|
|
2053
|
-
const { readFileSync:
|
|
1786
|
+
const { readFileSync: readFileSync4, existsSync: existsSync8 } = await import("node:fs");
|
|
2054
1787
|
const { resolve: resolve4, basename: basename2 } = await import("node:path");
|
|
2055
1788
|
const p = params;
|
|
2056
1789
|
const imagePath = p.image;
|
|
@@ -2063,7 +1796,7 @@ function buildImageEditTool(cwd, imageModelId, baseUrl, apiKey) {
|
|
|
2063
1796
|
const rawFilename = p.filename;
|
|
2064
1797
|
const safePrompt = buildPolicySafeEditPrompt(prompt);
|
|
2065
1798
|
const resolvedImage = resolve4(cwd, imagePath);
|
|
2066
|
-
if (!
|
|
1799
|
+
if (!existsSync8(resolvedImage)) {
|
|
2067
1800
|
return {
|
|
2068
1801
|
content: [
|
|
2069
1802
|
{
|
|
@@ -2075,9 +1808,9 @@ function buildImageEditTool(cwd, imageModelId, baseUrl, apiKey) {
|
|
|
2075
1808
|
};
|
|
2076
1809
|
}
|
|
2077
1810
|
const filename = rawFilename ? extname(rawFilename) ? rawFilename : `${rawFilename}.png` : `edited_${Date.now()}.png`;
|
|
2078
|
-
const filePath =
|
|
1811
|
+
const filePath = join6(cwd, filename.replace(/[^a-zA-Z0-9_\-./]/g, "_"));
|
|
2079
1812
|
try {
|
|
2080
|
-
const imageBuffer =
|
|
1813
|
+
const imageBuffer = readFileSync4(resolvedImage);
|
|
2081
1814
|
const fields = [
|
|
2082
1815
|
{ name: "model", value: imageModelId },
|
|
2083
1816
|
{ name: "prompt", value: safePrompt.prompt },
|
|
@@ -2107,11 +1840,11 @@ function buildImageEditTool(cwd, imageModelId, baseUrl, apiKey) {
|
|
|
2107
1840
|
];
|
|
2108
1841
|
if (maskPath) {
|
|
2109
1842
|
const resolvedMask = resolve4(cwd, maskPath);
|
|
2110
|
-
if (
|
|
1843
|
+
if (existsSync8(resolvedMask)) {
|
|
2111
1844
|
files.push({
|
|
2112
1845
|
name: "mask",
|
|
2113
1846
|
filename: basename2(resolvedMask),
|
|
2114
|
-
buffer:
|
|
1847
|
+
buffer: readFileSync4(resolvedMask),
|
|
2115
1848
|
mime: detectImageMime(resolvedMask)
|
|
2116
1849
|
});
|
|
2117
1850
|
}
|
|
@@ -2174,7 +1907,7 @@ function buildImageEditTool(cwd, imageModelId, baseUrl, apiKey) {
|
|
|
2174
1907
|
|
|
2175
1908
|
// ../../packages/runner-pi/dist/session-utils.js
|
|
2176
1909
|
import { closeSync, fstatSync, openSync, readdirSync as readdirSync2, readSync, statSync as statSync2 } from "node:fs";
|
|
2177
|
-
import { join as
|
|
1910
|
+
import { join as join7 } from "node:path";
|
|
2178
1911
|
import { SessionManager } from "@earendil-works/pi-coding-agent";
|
|
2179
1912
|
var MAX_SESSION_FILE_BYTES = Number(process.env.SANDAGENT_MAX_SESSION_BYTES) || 10 * 1024 * 1024;
|
|
2180
1913
|
function resolveSessionPathById(cwd, sessionId) {
|
|
@@ -2183,7 +1916,7 @@ function resolveSessionPathById(cwd, sessionId) {
|
|
|
2183
1916
|
try {
|
|
2184
1917
|
const suffix = `_${sessionId}.jsonl`;
|
|
2185
1918
|
const match = readdirSync2(sessionsDir).find((f) => f.endsWith(suffix));
|
|
2186
|
-
return match ?
|
|
1919
|
+
return match ? join7(sessionsDir, match) : void 0;
|
|
2187
1920
|
} catch {
|
|
2188
1921
|
return void 0;
|
|
2189
1922
|
}
|
|
@@ -2363,16 +2096,15 @@ var PiAISDKStreamConverter = class {
|
|
|
2363
2096
|
}
|
|
2364
2097
|
if (event.type === "tool_execution_start") {
|
|
2365
2098
|
chunks.push(...this.endTextStreamIfOpen());
|
|
2366
|
-
const toolCallId = sanitizeToolCallId(event.toolCallId);
|
|
2367
2099
|
chunks.push(sseData({
|
|
2368
2100
|
type: "tool-input-start",
|
|
2369
|
-
toolCallId,
|
|
2101
|
+
toolCallId: event.toolCallId,
|
|
2370
2102
|
toolName: event.toolName,
|
|
2371
2103
|
dynamic: true,
|
|
2372
2104
|
providerExecuted: true
|
|
2373
2105
|
}), sseData({
|
|
2374
2106
|
type: "tool-input-available",
|
|
2375
|
-
toolCallId,
|
|
2107
|
+
toolCallId: event.toolCallId,
|
|
2376
2108
|
toolName: event.toolName,
|
|
2377
2109
|
input: event.args,
|
|
2378
2110
|
dynamic: true,
|
|
@@ -2385,11 +2117,10 @@ var PiAISDKStreamConverter = class {
|
|
|
2385
2117
|
const raw = event.result?.details?.usage?.raw;
|
|
2386
2118
|
if (raw != null)
|
|
2387
2119
|
accumulateToolUsage(this.toolUsageTally, raw);
|
|
2388
|
-
const toolCallId = sanitizeToolCallId(event.toolCallId);
|
|
2389
2120
|
if (event.isError) {
|
|
2390
2121
|
chunks.push(sseData({
|
|
2391
2122
|
type: "tool-output-error",
|
|
2392
|
-
toolCallId,
|
|
2123
|
+
toolCallId: event.toolCallId,
|
|
2393
2124
|
errorText: output,
|
|
2394
2125
|
dynamic: true,
|
|
2395
2126
|
providerExecuted: true
|
|
@@ -2397,7 +2128,7 @@ var PiAISDKStreamConverter = class {
|
|
|
2397
2128
|
} else {
|
|
2398
2129
|
chunks.push(sseData({
|
|
2399
2130
|
type: "tool-output-available",
|
|
2400
|
-
toolCallId,
|
|
2131
|
+
toolCallId: event.toolCallId,
|
|
2401
2132
|
output,
|
|
2402
2133
|
dynamic: true,
|
|
2403
2134
|
providerExecuted: true
|
|
@@ -5620,12 +5351,6 @@ function resolveImageModelName(chatProvider, env) {
|
|
|
5620
5351
|
function getEnvValue(optionsEnv, name) {
|
|
5621
5352
|
return optionsEnv?.[name] ?? process.env[name];
|
|
5622
5353
|
}
|
|
5623
|
-
function parsePositiveInt(value) {
|
|
5624
|
-
if (value === void 0 || value === "")
|
|
5625
|
-
return void 0;
|
|
5626
|
-
const n = Number(value);
|
|
5627
|
-
return Number.isFinite(n) && n > 0 ? Math.floor(n) : void 0;
|
|
5628
|
-
}
|
|
5629
5354
|
function applyModelOverrides(model, provider, optionsEnv) {
|
|
5630
5355
|
if (model == null)
|
|
5631
5356
|
return;
|
|
@@ -5655,9 +5380,9 @@ function traceRawMessage(debugCwd, data, reset = false, optionsEnv) {
|
|
|
5655
5380
|
if (!enabled)
|
|
5656
5381
|
return;
|
|
5657
5382
|
try {
|
|
5658
|
-
const file =
|
|
5659
|
-
if (reset &&
|
|
5660
|
-
|
|
5383
|
+
const file = join8(debugCwd, "pi-message-stream-debug.json");
|
|
5384
|
+
if (reset && existsSync5(file))
|
|
5385
|
+
unlinkSync3(file);
|
|
5661
5386
|
const type = data !== null && typeof data === "object" ? data.type : void 0;
|
|
5662
5387
|
let payload = data;
|
|
5663
5388
|
try {
|
|
@@ -5760,12 +5485,6 @@ function createPiRunner(options = {}) {
|
|
|
5760
5485
|
customTools.push(buildImageGenerateTool(cwd, imageModelName, model.baseUrl, apiKey), buildImageEditTool(cwd, imageModelName, model.baseUrl, apiKey));
|
|
5761
5486
|
}
|
|
5762
5487
|
const toolRefDefinitions = options.toolRefs && options.toolRefs.length > 0 ? buildToolDefinitionsFromRefs(options.toolRefs) : [];
|
|
5763
|
-
const askTimeoutSeconds = parsePositiveInt(getEnvValue(options.env, "ASK_USER_QUESTION_TOOL_TIMEOUT"));
|
|
5764
|
-
const askUserQuestionTool = buildAskUserQuestionTool({
|
|
5765
|
-
cwd,
|
|
5766
|
-
timeoutMs: askTimeoutSeconds !== void 0 ? askTimeoutSeconds * 1e3 : void 0
|
|
5767
|
-
});
|
|
5768
|
-
customTools.push(askUserQuestionTool);
|
|
5769
5488
|
const { session } = await createAgentSession({
|
|
5770
5489
|
cwd,
|
|
5771
5490
|
model,
|
|
@@ -5874,47 +5593,47 @@ function createPiRunner(options = {}) {
|
|
|
5874
5593
|
}
|
|
5875
5594
|
|
|
5876
5595
|
// ../../packages/runner-harness/dist/session.js
|
|
5877
|
-
import { existsSync as
|
|
5878
|
-
import { join as
|
|
5596
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "node:fs";
|
|
5597
|
+
import { join as join9 } from "node:path";
|
|
5879
5598
|
var DIR = ".bunny-agent";
|
|
5880
5599
|
var FILE = "session-id";
|
|
5881
5600
|
function sessionPath(cwd) {
|
|
5882
|
-
return
|
|
5601
|
+
return join9(cwd, DIR, FILE);
|
|
5883
5602
|
}
|
|
5884
5603
|
function readSessionId(cwd) {
|
|
5885
5604
|
try {
|
|
5886
5605
|
const p = sessionPath(cwd);
|
|
5887
|
-
if (!
|
|
5606
|
+
if (!existsSync6(p))
|
|
5888
5607
|
return void 0;
|
|
5889
|
-
return
|
|
5608
|
+
return readFileSync3(p, "utf8").trim() || void 0;
|
|
5890
5609
|
} catch {
|
|
5891
5610
|
return void 0;
|
|
5892
5611
|
}
|
|
5893
5612
|
}
|
|
5894
5613
|
function writeSessionId(cwd, id) {
|
|
5895
5614
|
try {
|
|
5896
|
-
|
|
5897
|
-
|
|
5615
|
+
mkdirSync3(join9(cwd, DIR), { recursive: true });
|
|
5616
|
+
writeFileSync4(sessionPath(cwd), id, "utf8");
|
|
5898
5617
|
} catch {
|
|
5899
5618
|
}
|
|
5900
5619
|
}
|
|
5901
5620
|
|
|
5902
5621
|
// ../../packages/runner-harness/dist/skills.js
|
|
5903
|
-
import { existsSync as
|
|
5622
|
+
import { existsSync as existsSync7, readdirSync as readdirSync3, statSync as statSync3 } from "node:fs";
|
|
5904
5623
|
import { homedir as homedir2 } from "node:os";
|
|
5905
|
-
import { join as
|
|
5624
|
+
import { join as join10 } from "node:path";
|
|
5906
5625
|
function discoverSkillPaths(cwd) {
|
|
5907
5626
|
const paths = [];
|
|
5908
5627
|
for (const base of [
|
|
5909
|
-
|
|
5910
|
-
|
|
5628
|
+
join10(cwd, "skills"),
|
|
5629
|
+
join10(homedir2(), ".bunny-agent", "skills")
|
|
5911
5630
|
]) {
|
|
5912
|
-
if (!
|
|
5631
|
+
if (!existsSync7(base))
|
|
5913
5632
|
continue;
|
|
5914
5633
|
try {
|
|
5915
5634
|
for (const entry of readdirSync3(base)) {
|
|
5916
|
-
const full =
|
|
5917
|
-
if (statSync3(full).isDirectory() &&
|
|
5635
|
+
const full = join10(base, entry);
|
|
5636
|
+
if (statSync3(full).isDirectory() && existsSync7(join10(full, "SKILL.md"))) {
|
|
5918
5637
|
paths.push(full);
|
|
5919
5638
|
}
|
|
5920
5639
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bunny-agent/runner-cli",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.42",
|
|
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": {
|
|
@@ -53,10 +53,10 @@
|
|
|
53
53
|
"esbuild": "^0.27.2",
|
|
54
54
|
"typescript": "^5.3.0",
|
|
55
55
|
"vitest": "^1.6.1",
|
|
56
|
-
"@bunny-agent/runner-claude": "0.6.2",
|
|
57
56
|
"@bunny-agent/runner-codex": "0.6.2",
|
|
58
|
-
"@bunny-agent/runner-
|
|
57
|
+
"@bunny-agent/runner-claude": "0.6.2",
|
|
59
58
|
"@bunny-agent/runner-harness": "0.1.1-beta.0",
|
|
59
|
+
"@bunny-agent/runner-gemini": "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
|
},
|