@bunny-agent/runner-cli 0.9.29-beta.6 → 0.9.29-beta.7
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 +386 -230
- package/package.json +4 -4
package/dist/bundle.mjs
CHANGED
|
@@ -1274,9 +1274,9 @@ function createOpenCodeRunner(options = {}) {
|
|
|
1274
1274
|
|
|
1275
1275
|
// ../../packages/runner-pi/dist/pi-runner.js
|
|
1276
1276
|
import { appendFileSync as appendFileSync2, existsSync as existsSync5, unlinkSync as unlinkSync3 } from "node:fs";
|
|
1277
|
-
import { join as
|
|
1277
|
+
import { join as join8 } from "node:path";
|
|
1278
1278
|
import { getModel } from "@mariozechner/pi-ai";
|
|
1279
|
-
import { AuthStorage, createAgentSession, ModelRegistry, SessionManager } from "@mariozechner/pi-coding-agent";
|
|
1279
|
+
import { AuthStorage, createAgentSession, ModelRegistry, SessionManager as SessionManager2 } from "@mariozechner/pi-coding-agent";
|
|
1280
1280
|
|
|
1281
1281
|
// ../../packages/runner-pi/dist/bunny-agent-resource-loader.js
|
|
1282
1282
|
import { existsSync as existsSync4 } from "node:fs";
|
|
@@ -1400,8 +1400,8 @@ var generateImageSchema = {
|
|
|
1400
1400
|
},
|
|
1401
1401
|
quality: {
|
|
1402
1402
|
type: "string",
|
|
1403
|
-
enum: ["
|
|
1404
|
-
description: "Image quality
|
|
1403
|
+
enum: ["low", "medium", "high", "auto"],
|
|
1404
|
+
description: "Image quality. Defaults to auto."
|
|
1405
1405
|
}
|
|
1406
1406
|
},
|
|
1407
1407
|
required: ["prompt"],
|
|
@@ -1563,7 +1563,7 @@ function buildImageGenerateTool(cwd, imageModelId, baseUrl, apiKey) {
|
|
|
1563
1563
|
const p = params;
|
|
1564
1564
|
const prompt = p.prompt;
|
|
1565
1565
|
const size = p.size ?? "1024x1024";
|
|
1566
|
-
const quality = p.quality ?? "
|
|
1566
|
+
const quality = p.quality ?? "auto";
|
|
1567
1567
|
const rawFilename = p.filename;
|
|
1568
1568
|
const filename = rawFilename ? extname(rawFilename) ? rawFilename : `${rawFilename}.png` : `image_${Date.now()}.png`;
|
|
1569
1569
|
const filePath = join6(cwd, filename.replace(/[^a-zA-Z0-9_\-./]/g, "_"));
|
|
@@ -1601,6 +1601,7 @@ function buildImageGenerateTool(cwd, imageModelId, baseUrl, apiKey) {
|
|
|
1601
1601
|
],
|
|
1602
1602
|
details: {
|
|
1603
1603
|
filePath: savedPath,
|
|
1604
|
+
...json.usage != null ? { usage: { raw: { [imageModelId]: json.usage } } } : {},
|
|
1604
1605
|
response: json
|
|
1605
1606
|
}
|
|
1606
1607
|
};
|
|
@@ -1788,6 +1789,7 @@ function buildImageEditTool(cwd, imageModelId, baseUrl, apiKey) {
|
|
|
1788
1789
|
],
|
|
1789
1790
|
details: {
|
|
1790
1791
|
filePath: savedPath,
|
|
1792
|
+
...json.usage != null ? { usage: { raw: { [imageModelId]: json.usage } } } : {},
|
|
1791
1793
|
response: json
|
|
1792
1794
|
}
|
|
1793
1795
|
};
|
|
@@ -1804,6 +1806,310 @@ function buildImageEditTool(cwd, imageModelId, baseUrl, apiKey) {
|
|
|
1804
1806
|
};
|
|
1805
1807
|
}
|
|
1806
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
|
+
};
|
|
2112
|
+
|
|
1807
2113
|
// ../../packages/runner-pi/dist/tool-overrides.js
|
|
1808
2114
|
import { createBashTool, createReadTool } from "@mariozechner/pi-coding-agent";
|
|
1809
2115
|
|
|
@@ -1848,7 +2154,7 @@ ${body}`);
|
|
|
1848
2154
|
});
|
|
1849
2155
|
}
|
|
1850
2156
|
}
|
|
1851
|
-
return results;
|
|
2157
|
+
return { results };
|
|
1852
2158
|
}
|
|
1853
2159
|
};
|
|
1854
2160
|
var tavilyProvider = {
|
|
@@ -1883,7 +2189,7 @@ ${body}`);
|
|
|
1883
2189
|
});
|
|
1884
2190
|
}
|
|
1885
2191
|
}
|
|
1886
|
-
return results;
|
|
2192
|
+
return { results };
|
|
1887
2193
|
}
|
|
1888
2194
|
};
|
|
1889
2195
|
var AUTO_DETECT_ORDER = [braveProvider, tavilyProvider];
|
|
@@ -2030,7 +2336,7 @@ function buildWebSearchTool(env) {
|
|
|
2030
2336
|
let lastError;
|
|
2031
2337
|
for (const { provider, apiKey } of providers) {
|
|
2032
2338
|
try {
|
|
2033
|
-
const results = await provider.search({
|
|
2339
|
+
const { results } = await provider.search({
|
|
2034
2340
|
apiKey,
|
|
2035
2341
|
query,
|
|
2036
2342
|
count,
|
|
@@ -2038,11 +2344,21 @@ function buildWebSearchTool(env) {
|
|
|
2038
2344
|
freshness,
|
|
2039
2345
|
signal
|
|
2040
2346
|
});
|
|
2347
|
+
let fetchedPages = 0;
|
|
2041
2348
|
if (shouldFetchContent) {
|
|
2042
2349
|
for (const r of results) {
|
|
2043
2350
|
r.content = await fetchPageContent(r.link, signal);
|
|
2351
|
+
fetchedPages += 1;
|
|
2044
2352
|
}
|
|
2045
2353
|
}
|
|
2354
|
+
const usage = {
|
|
2355
|
+
raw: {
|
|
2356
|
+
[provider.id]: {
|
|
2357
|
+
requests: 1,
|
|
2358
|
+
fetchedPages
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
};
|
|
2046
2362
|
return {
|
|
2047
2363
|
content: [
|
|
2048
2364
|
{
|
|
@@ -2050,7 +2366,9 @@ function buildWebSearchTool(env) {
|
|
|
2050
2366
|
text: formatSearchResults(results, provider.label)
|
|
2051
2367
|
}
|
|
2052
2368
|
],
|
|
2053
|
-
details:
|
|
2369
|
+
details: {
|
|
2370
|
+
usage
|
|
2371
|
+
}
|
|
2054
2372
|
};
|
|
2055
2373
|
} catch (e) {
|
|
2056
2374
|
lastError = e;
|
|
@@ -2233,52 +2551,6 @@ function applyModelOverrides(model, provider, optionsEnv) {
|
|
|
2233
2551
|
model.baseUrl = anthropicBaseUrl;
|
|
2234
2552
|
}
|
|
2235
2553
|
}
|
|
2236
|
-
function emitStreamError(errorText) {
|
|
2237
|
-
return [
|
|
2238
|
-
`data: ${JSON.stringify({ type: "error", errorText })}
|
|
2239
|
-
|
|
2240
|
-
`,
|
|
2241
|
-
`data: ${JSON.stringify({ type: "finish", finishReason: "error" })}
|
|
2242
|
-
|
|
2243
|
-
`,
|
|
2244
|
-
"data: [DONE]\n\n"
|
|
2245
|
-
];
|
|
2246
|
-
}
|
|
2247
|
-
function extractToolResultText(result) {
|
|
2248
|
-
if (result !== null && typeof result === "object") {
|
|
2249
|
-
const r = result;
|
|
2250
|
-
if (Array.isArray(r.content) && r.content.length > 0) {
|
|
2251
|
-
const text = r.content.filter((c) => c.type === "text" && typeof c.text === "string").map((c) => c.text).join("\n");
|
|
2252
|
-
if (text.length > 0) {
|
|
2253
|
-
return text;
|
|
2254
|
-
}
|
|
2255
|
-
}
|
|
2256
|
-
}
|
|
2257
|
-
if (typeof result === "string")
|
|
2258
|
-
return result;
|
|
2259
|
-
try {
|
|
2260
|
-
return JSON.stringify(result);
|
|
2261
|
-
} catch {
|
|
2262
|
-
return String(result);
|
|
2263
|
-
}
|
|
2264
|
-
}
|
|
2265
|
-
function usageToMessageMetadata(usage) {
|
|
2266
|
-
return {
|
|
2267
|
-
input_tokens: usage.input,
|
|
2268
|
-
output_tokens: usage.output,
|
|
2269
|
-
cache_read_input_tokens: usage.cacheRead,
|
|
2270
|
-
cache_creation_input_tokens: usage.cacheWrite
|
|
2271
|
-
};
|
|
2272
|
-
}
|
|
2273
|
-
function getUsageFromAgentEndMessages(messages) {
|
|
2274
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
2275
|
-
const m = messages[i];
|
|
2276
|
-
if (m.role === "assistant" && m.usage != null) {
|
|
2277
|
-
return m.usage;
|
|
2278
|
-
}
|
|
2279
|
-
}
|
|
2280
|
-
return void 0;
|
|
2281
|
-
}
|
|
2282
2554
|
function getErrorFromAgentEndMessages(messages) {
|
|
2283
2555
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
2284
2556
|
const m = messages[i];
|
|
@@ -2294,7 +2566,7 @@ function traceRawMessage(debugCwd, data, reset = false, optionsEnv) {
|
|
|
2294
2566
|
if (!enabled)
|
|
2295
2567
|
return;
|
|
2296
2568
|
try {
|
|
2297
|
-
const file =
|
|
2569
|
+
const file = join8(debugCwd, "pi-message-stream-debug.json");
|
|
2298
2570
|
if (reset && existsSync5(file))
|
|
2299
2571
|
unlinkSync3(file);
|
|
2300
2572
|
const type = data !== null && typeof data === "object" ? data.type : void 0;
|
|
@@ -2361,13 +2633,26 @@ function createPiRunner(options = {}) {
|
|
|
2361
2633
|
const sessionManager = await (async () => {
|
|
2362
2634
|
if (resume !== void 0 && resume !== "") {
|
|
2363
2635
|
if (resume.includes("/")) {
|
|
2364
|
-
return
|
|
2636
|
+
return SessionManager2.open(resume);
|
|
2365
2637
|
}
|
|
2366
|
-
const
|
|
2367
|
-
|
|
2368
|
-
|
|
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);
|
|
2652
|
+
}
|
|
2653
|
+
return SessionManager2.create(cwd);
|
|
2369
2654
|
}
|
|
2370
|
-
return
|
|
2655
|
+
return SessionManager2.create(cwd);
|
|
2371
2656
|
})();
|
|
2372
2657
|
const resourceLoader = options.skillPaths ? new BunnyAgentResourceLoader({
|
|
2373
2658
|
cwd,
|
|
@@ -2423,165 +2708,34 @@ function createPiRunner(options = {}) {
|
|
|
2423
2708
|
}
|
|
2424
2709
|
try {
|
|
2425
2710
|
traceRawMessage(cwd, null, true, options.env);
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
if (imageParts.length > 0) {
|
|
2435
|
-
images = imageParts.map((p) => ({
|
|
2436
|
-
type: "image",
|
|
2437
|
-
data: p.data,
|
|
2438
|
-
mimeType: p.mimeType
|
|
2439
|
-
}));
|
|
2440
|
-
}
|
|
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);
|
|
2441
2719
|
}
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
let hasFinished = false;
|
|
2449
|
-
const imageToolUsage = { input_tokens: 0, output_tokens: 0 };
|
|
2450
|
-
const newTextPartId = () => `text_${Date.now()}_${Math.random().toString(36).slice(2)}_${Math.random().toString(36).slice(2)}`;
|
|
2451
|
-
let activeTextPartId = null;
|
|
2452
|
-
let textStreamOpen = false;
|
|
2453
|
-
const endTextStreamIfOpen = function* () {
|
|
2454
|
-
if (textStreamOpen && activeTextPartId != null) {
|
|
2455
|
-
yield `data: ${JSON.stringify({ type: "text-end", id: activeTextPartId })}
|
|
2456
|
-
|
|
2457
|
-
`;
|
|
2458
|
-
textStreamOpen = false;
|
|
2459
|
-
activeTextPartId = null;
|
|
2460
|
-
}
|
|
2461
|
-
};
|
|
2462
|
-
const beginTextStream = function* () {
|
|
2463
|
-
activeTextPartId = newTextPartId();
|
|
2464
|
-
yield `data: ${JSON.stringify({ type: "text-start", id: activeTextPartId })}
|
|
2465
|
-
|
|
2466
|
-
`;
|
|
2467
|
-
textStreamOpen = true;
|
|
2468
|
-
};
|
|
2469
|
-
const ensureStartEvent = async function* () {
|
|
2470
|
-
if (!hasStarted) {
|
|
2471
|
-
yield `data: ${JSON.stringify({ type: "start", messageId })}
|
|
2472
|
-
|
|
2473
|
-
`;
|
|
2474
|
-
yield `data: ${JSON.stringify({
|
|
2475
|
-
type: "message-metadata",
|
|
2476
|
-
messageMetadata: { sessionId: session.sessionId }
|
|
2477
|
-
})}
|
|
2478
|
-
|
|
2479
|
-
`;
|
|
2480
|
-
hasStarted = true;
|
|
2481
|
-
}
|
|
2482
|
-
};
|
|
2483
|
-
const finishSuccess = async function* (usage) {
|
|
2484
|
-
yield* endTextStreamIfOpen();
|
|
2485
|
-
const finishPayload = { type: "finish", finishReason: "stop" };
|
|
2486
|
-
const hasImageUsage = imageToolUsage.input_tokens > 0 || imageToolUsage.output_tokens > 0;
|
|
2487
|
-
if (usage != null || hasImageUsage) {
|
|
2488
|
-
const base = usage != null ? usageToMessageMetadata(usage) : {};
|
|
2489
|
-
finishPayload.messageMetadata = {
|
|
2490
|
-
usage: {
|
|
2491
|
-
...base,
|
|
2492
|
-
input_tokens: (base.input_tokens ?? 0) + imageToolUsage.input_tokens,
|
|
2493
|
-
output_tokens: (base.output_tokens ?? 0) + imageToolUsage.output_tokens
|
|
2494
|
-
}
|
|
2495
|
-
};
|
|
2496
|
-
}
|
|
2497
|
-
yield `data: ${JSON.stringify(finishPayload)}
|
|
2498
|
-
|
|
2499
|
-
`;
|
|
2500
|
-
yield "data: [DONE]\n\n";
|
|
2501
|
-
hasFinished = true;
|
|
2502
|
-
};
|
|
2503
|
-
const finishError = async function* (errorText) {
|
|
2504
|
-
for (const chunk of emitStreamError(errorText)) {
|
|
2505
|
-
yield chunk;
|
|
2506
|
-
}
|
|
2507
|
-
hasFinished = true;
|
|
2508
|
-
};
|
|
2720
|
+
return value;
|
|
2721
|
+
},
|
|
2722
|
+
normalizeToolOutput: extractToolResultText,
|
|
2723
|
+
getUsageFromAgentEndMessages,
|
|
2724
|
+
getErrorFromAgentEndMessages
|
|
2725
|
+
});
|
|
2509
2726
|
while (!isComplete || eventQueue.length > 0) {
|
|
2510
2727
|
while (eventQueue.length > 0) {
|
|
2511
2728
|
const event = eventQueue.shift();
|
|
2512
2729
|
traceRawMessage(cwd, event, false, options.env);
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
if (msg?.role === "assistant") {
|
|
2517
|
-
yield* endTextStreamIfOpen();
|
|
2518
|
-
}
|
|
2519
|
-
} else if (event.type === "message_update") {
|
|
2520
|
-
const sub = event.assistantMessageEvent;
|
|
2521
|
-
if (sub.type === "text_start") {
|
|
2522
|
-
yield* endTextStreamIfOpen();
|
|
2523
|
-
yield* beginTextStream();
|
|
2524
|
-
} else if (sub.type === "text_delta") {
|
|
2525
|
-
let delta = sub.delta;
|
|
2526
|
-
if (delta) {
|
|
2527
|
-
if (options.env && Object.keys(options.env).length > 0) {
|
|
2528
|
-
delta = redactSecrets(delta, options.env);
|
|
2529
|
-
}
|
|
2530
|
-
if (!textStreamOpen) {
|
|
2531
|
-
yield* beginTextStream();
|
|
2532
|
-
}
|
|
2533
|
-
yield `data: ${JSON.stringify({
|
|
2534
|
-
type: "text-delta",
|
|
2535
|
-
id: activeTextPartId,
|
|
2536
|
-
delta
|
|
2537
|
-
})}
|
|
2538
|
-
|
|
2539
|
-
`;
|
|
2540
|
-
}
|
|
2541
|
-
} else if (sub.type === "toolcall_start") {
|
|
2542
|
-
yield* endTextStreamIfOpen();
|
|
2543
|
-
}
|
|
2544
|
-
} else if (event.type === "tool_execution_start") {
|
|
2545
|
-
yield* endTextStreamIfOpen();
|
|
2546
|
-
yield `data: ${JSON.stringify({ type: "tool-input-start", toolCallId: event.toolCallId, toolName: event.toolName, dynamic: true, providerExecuted: true })}
|
|
2547
|
-
|
|
2548
|
-
`;
|
|
2549
|
-
yield `data: ${JSON.stringify({ type: "tool-input-available", toolCallId: event.toolCallId, toolName: event.toolName, input: event.args, dynamic: true, providerExecuted: true })}
|
|
2550
|
-
|
|
2551
|
-
`;
|
|
2552
|
-
} else if (event.type === "tool_execution_end") {
|
|
2553
|
-
let output = extractToolResultText(event.result);
|
|
2554
|
-
if (options.env && Object.keys(options.env).length > 0) {
|
|
2555
|
-
output = redactSecrets(output, options.env);
|
|
2556
|
-
}
|
|
2557
|
-
if ((event.toolName === "generate_image" || event.toolName === "edit_image") && event.result !== null && typeof event.result === "object") {
|
|
2558
|
-
const details = event.result.details;
|
|
2559
|
-
const u = details?.response?.usage;
|
|
2560
|
-
if (u) {
|
|
2561
|
-
imageToolUsage.input_tokens += u.input_tokens ?? 0;
|
|
2562
|
-
imageToolUsage.output_tokens += u.output_tokens ?? 0;
|
|
2563
|
-
}
|
|
2564
|
-
}
|
|
2565
|
-
yield `data: ${JSON.stringify({ type: "tool-output-available", toolCallId: event.toolCallId, output, isError: event.isError, dynamic: true, providerExecuted: true })}
|
|
2566
|
-
|
|
2567
|
-
`;
|
|
2568
|
-
} else if (event.type === "agent_end") {
|
|
2569
|
-
if (aborted) {
|
|
2570
|
-
yield* finishError("Run aborted by signal.");
|
|
2571
|
-
} else {
|
|
2572
|
-
const errorMsg = getErrorFromAgentEndMessages(event.messages);
|
|
2573
|
-
if (errorMsg) {
|
|
2574
|
-
yield* finishError(errorMsg);
|
|
2575
|
-
} else {
|
|
2576
|
-
const usage = getUsageFromAgentEndMessages(event.messages);
|
|
2577
|
-
yield* finishSuccess(usage);
|
|
2578
|
-
}
|
|
2579
|
-
}
|
|
2730
|
+
const chunks = streamConverter.handleEvent(event, aborted);
|
|
2731
|
+
for (const chunk of chunks) {
|
|
2732
|
+
yield chunk;
|
|
2580
2733
|
}
|
|
2581
2734
|
}
|
|
2582
|
-
if (aborted && !
|
|
2583
|
-
|
|
2584
|
-
|
|
2735
|
+
if (aborted && !streamConverter.finished) {
|
|
2736
|
+
for (const chunk of streamConverter.forceError("Run aborted by signal.")) {
|
|
2737
|
+
yield chunk;
|
|
2738
|
+
}
|
|
2585
2739
|
break;
|
|
2586
2740
|
}
|
|
2587
2741
|
if (!isComplete && eventQueue.length === 0) {
|
|
@@ -2590,22 +2744,24 @@ function createPiRunner(options = {}) {
|
|
|
2590
2744
|
});
|
|
2591
2745
|
}
|
|
2592
2746
|
}
|
|
2593
|
-
if (
|
|
2747
|
+
if (streamConverter.finished) {
|
|
2594
2748
|
return;
|
|
2595
2749
|
}
|
|
2596
2750
|
try {
|
|
2597
2751
|
await promptPromise;
|
|
2598
2752
|
} catch (error) {
|
|
2599
|
-
if (!
|
|
2600
|
-
yield* ensureStartEvent();
|
|
2753
|
+
if (!streamConverter.finished) {
|
|
2601
2754
|
const message = error instanceof Error ? error.message : "Pi agent run failed.";
|
|
2602
|
-
|
|
2755
|
+
for (const chunk of streamConverter.forceError(message)) {
|
|
2756
|
+
yield chunk;
|
|
2757
|
+
}
|
|
2603
2758
|
}
|
|
2604
2759
|
return;
|
|
2605
2760
|
}
|
|
2606
|
-
if (!
|
|
2607
|
-
|
|
2608
|
-
|
|
2761
|
+
if (!streamConverter.finished && session.agent.state.error) {
|
|
2762
|
+
for (const chunk of streamConverter.forceError(session.agent.state.error)) {
|
|
2763
|
+
yield chunk;
|
|
2764
|
+
}
|
|
2609
2765
|
}
|
|
2610
2766
|
} finally {
|
|
2611
2767
|
if (abortSignal) {
|
|
@@ -2625,11 +2781,11 @@ function createPiRunner(options = {}) {
|
|
|
2625
2781
|
|
|
2626
2782
|
// ../../packages/runner-harness/dist/session.js
|
|
2627
2783
|
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "node:fs";
|
|
2628
|
-
import { join as
|
|
2784
|
+
import { join as join9 } from "node:path";
|
|
2629
2785
|
var DIR = ".bunny-agent";
|
|
2630
2786
|
var FILE = "session-id";
|
|
2631
2787
|
function sessionPath(cwd) {
|
|
2632
|
-
return
|
|
2788
|
+
return join9(cwd, DIR, FILE);
|
|
2633
2789
|
}
|
|
2634
2790
|
function readSessionId(cwd) {
|
|
2635
2791
|
try {
|
|
@@ -2643,28 +2799,28 @@ function readSessionId(cwd) {
|
|
|
2643
2799
|
}
|
|
2644
2800
|
function writeSessionId(cwd, id) {
|
|
2645
2801
|
try {
|
|
2646
|
-
mkdirSync3(
|
|
2802
|
+
mkdirSync3(join9(cwd, DIR), { recursive: true });
|
|
2647
2803
|
writeFileSync4(sessionPath(cwd), id, "utf8");
|
|
2648
2804
|
} catch {
|
|
2649
2805
|
}
|
|
2650
2806
|
}
|
|
2651
2807
|
|
|
2652
2808
|
// ../../packages/runner-harness/dist/skills.js
|
|
2653
|
-
import { existsSync as existsSync7, readdirSync as
|
|
2809
|
+
import { existsSync as existsSync7, readdirSync as readdirSync3, statSync as statSync3 } from "node:fs";
|
|
2654
2810
|
import { homedir as homedir2 } from "node:os";
|
|
2655
|
-
import { join as
|
|
2811
|
+
import { join as join10 } from "node:path";
|
|
2656
2812
|
function discoverSkillPaths(cwd) {
|
|
2657
2813
|
const paths = [];
|
|
2658
2814
|
for (const base of [
|
|
2659
|
-
|
|
2660
|
-
|
|
2815
|
+
join10(cwd, "skills"),
|
|
2816
|
+
join10(homedir2(), ".bunny-agent", "skills")
|
|
2661
2817
|
]) {
|
|
2662
2818
|
if (!existsSync7(base))
|
|
2663
2819
|
continue;
|
|
2664
2820
|
try {
|
|
2665
|
-
for (const entry of
|
|
2666
|
-
const full =
|
|
2667
|
-
if (
|
|
2821
|
+
for (const entry of readdirSync3(base)) {
|
|
2822
|
+
const full = join10(base, entry);
|
|
2823
|
+
if (statSync3(full).isDirectory() && existsSync7(join10(full, "SKILL.md"))) {
|
|
2668
2824
|
paths.push(full);
|
|
2669
2825
|
}
|
|
2670
2826
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bunny-agent/runner-cli",
|
|
3
|
-
"version": "0.9.29-beta.
|
|
3
|
+
"version": "0.9.29-beta.7",
|
|
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,10 +55,10 @@
|
|
|
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-opencode": "0.6.2",
|
|
59
|
-
"@bunny-agent/runner-gemini": "0.6.2",
|
|
60
58
|
"@bunny-agent/runner-codex": "0.6.2",
|
|
61
|
-
"@bunny-agent/runner-
|
|
59
|
+
"@bunny-agent/runner-opencode": "0.6.2",
|
|
60
|
+
"@bunny-agent/runner-pi": "0.6.4-beta.0",
|
|
61
|
+
"@bunny-agent/runner-gemini": "0.6.2"
|
|
62
62
|
},
|
|
63
63
|
"scripts": {
|
|
64
64
|
"build": "tsc && pnpm bundle",
|