@bunny-agent/runner-cli 0.9.62-release.2 → 0.9.63-beta.2

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.
Files changed (2) hide show
  1. package/dist/bundle.mjs +208 -237
  2. package/package.json +5 -5
package/dist/bundle.mjs CHANGED
@@ -39551,10 +39551,10 @@ function createMcpExtension(config4) {
39551
39551
  }
39552
39552
 
39553
39553
  // ../../packages/runner-pi/dist/pi-runner.js
39554
- import { appendFileSync as appendFileSync2, existsSync as existsSync9, unlinkSync as unlinkSync7 } from "node:fs";
39555
- import { join as join13 } from "node:path";
39554
+ import { appendFileSync as appendFileSync2, existsSync as existsSync8, unlinkSync as unlinkSync7 } from "node:fs";
39555
+ import { isAbsolute, join as join12, resolve as resolve2 } from "node:path";
39556
39556
  import { getBuiltinModel } from "@earendil-works/pi-ai/providers/all";
39557
- import { createAgentSession, createCodingTools, ModelRuntime, SessionManager as SessionManager2 } from "@earendil-works/pi-coding-agent";
39557
+ import { CONFIG_DIR_NAME, createAgentSession, DefaultResourceLoader, getAgentDir, ModelRuntime, SessionManager as SessionManager2, SettingsManager } from "@earendil-works/pi-coding-agent";
39558
39558
 
39559
39559
  // ../../packages/runner-pi/dist/apply-patch-shim.js
39560
39560
  import { chmodSync, existsSync as existsSync5, mkdirSync as mkdirSync4, renameSync, writeFileSync as writeFileSync5 } from "node:fs";
@@ -39848,105 +39848,9 @@ function buildApplyPatchTool(cwd) {
39848
39848
  };
39849
39849
  }
39850
39850
 
39851
- // ../../packages/runner-pi/dist/bunny-agent-resource-loader.js
39852
- import { existsSync as existsSync7 } from "node:fs";
39853
- import { homedir } from "node:os";
39854
- import { isAbsolute, join as join9, resolve as resolve2 } from "node:path";
39855
- import { DefaultResourceLoader, loadSkills } from "@earendil-works/pi-coding-agent";
39856
- var LOG_PREFIX = "[bunny-agent:pi]";
39857
- function logSkillLoad(cwd, agentDir, skillPaths, result) {
39858
- const lines = [
39859
- `${LOG_PREFIX} loadSkills`,
39860
- ` cwd: ${cwd}`,
39861
- ` agentDir: ${agentDir}`,
39862
- ` extra skillPaths (${skillPaths.length}):`
39863
- ];
39864
- for (const raw of skillPaths) {
39865
- const abs = isAbsolute(raw) ? raw : resolve2(cwd, raw);
39866
- lines.push(` ${raw} -> ${abs} (exists: ${existsSync7(abs) ? "yes" : "no"})`);
39867
- }
39868
- lines.push(` loaded skills: ${result.skills.length}`);
39869
- if (result.skills.length > 0) {
39870
- lines.push(` skill names: ${result.skills.map((s) => s.name).join(", ")}`);
39871
- }
39872
- if (result.diagnostics.length > 0) {
39873
- lines.push(` diagnostics (${result.diagnostics.length}):`);
39874
- for (const d of result.diagnostics) {
39875
- const pathPart = d.path !== void 0 ? ` path=${d.path}` : "";
39876
- lines.push(` [${d.type}] ${d.message}${pathPart}`);
39877
- }
39878
- }
39879
- console.error(lines.join("\n"));
39880
- }
39881
- var BunnyAgentResourceLoader = class {
39882
- constructor(options = {}) {
39883
- this.cwd = options.cwd ?? process.cwd();
39884
- this.agentDir = options.agentDir ?? join9(homedir(), ".bunny", "agent");
39885
- this.skillPaths = options.skillPaths ?? [];
39886
- this.extraAppendPrompt = options.appendSystemPrompt;
39887
- this.delegate = new DefaultResourceLoader({
39888
- cwd: this.cwd,
39889
- agentDir: this.agentDir,
39890
- settingsManager: options.settingsManager,
39891
- extensionFactories: options.extensionFactories
39892
- });
39893
- }
39894
- async reload() {
39895
- await this.delegate.reload();
39896
- this.cachedSkills = void 0;
39897
- }
39898
- getSkills() {
39899
- if (!this.cachedSkills) {
39900
- this.cachedSkills = loadSkills({
39901
- cwd: this.cwd,
39902
- agentDir: this.agentDir,
39903
- skillPaths: this.skillPaths,
39904
- includeDefaults: true
39905
- });
39906
- if (this.skillPaths.length > 0) {
39907
- logSkillLoad(this.cwd, this.agentDir, this.skillPaths, this.cachedSkills);
39908
- }
39909
- }
39910
- return this.cachedSkills;
39911
- }
39912
- // Delegate all other methods
39913
- getExtensions() {
39914
- return this.delegate.getExtensions();
39915
- }
39916
- getPrompts() {
39917
- return this.delegate.getPrompts();
39918
- }
39919
- getThemes() {
39920
- return this.delegate.getThemes();
39921
- }
39922
- getAgentsFiles() {
39923
- return this.delegate.getAgentsFiles();
39924
- }
39925
- getSystemPrompt() {
39926
- return this.delegate.getSystemPrompt();
39927
- }
39928
- getSystemPromptSource() {
39929
- return this.delegate.getSystemPromptSource();
39930
- }
39931
- getAppendSystemPromptSources() {
39932
- return this.delegate.getAppendSystemPromptSources();
39933
- }
39934
- getAppendSystemPrompt() {
39935
- const base = this.delegate.getAppendSystemPrompt();
39936
- if (this.extraAppendPrompt) {
39937
- console.error(`${LOG_PREFIX} getAppendSystemPrompt: appending extra prompt (${this.extraAppendPrompt.length} chars)`);
39938
- return [...base, this.extraAppendPrompt];
39939
- }
39940
- return base;
39941
- }
39942
- extendResources(paths) {
39943
- this.delegate.extendResources(paths);
39944
- }
39945
- };
39946
-
39947
39851
  // ../../packages/runner-pi/dist/image-tools.js
39948
39852
  import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "node:fs";
39949
- import { dirname as dirname3, extname, join as join10 } from "node:path";
39853
+ import { dirname as dirname3, extname, join as join9 } from "node:path";
39950
39854
  var generateImageSchema = {
39951
39855
  type: "object",
39952
39856
  properties: {
@@ -40207,7 +40111,7 @@ function buildImageGenerateTool(cwd, imageModelId, baseUrl, apiKey) {
40207
40111
  const imageSize = p.imageSize;
40208
40112
  const rawFilename = p.filename;
40209
40113
  const filename = rawFilename ? extname(rawFilename) ? rawFilename : `${rawFilename}.png` : `image_${Date.now()}.png`;
40210
- const filePath = join10(cwd, filename.replace(/[^a-zA-Z0-9_\-./]/g, "_"));
40114
+ const filePath = join9(cwd, filename.replace(/[^a-zA-Z0-9_\-./]/g, "_"));
40211
40115
  try {
40212
40116
  const url3 = `${baseUrl.replace(/\/$/, "")}/v1/images/generations`;
40213
40117
  const res = await fetch(url3, {
@@ -40355,7 +40259,7 @@ function buildImageEditTool(cwd, imageModelId, baseUrl, apiKey) {
40355
40259
  // biome-ignore lint/suspicious/noExplicitAny: plain JSON Schema compatible with TypeBox TSchema
40356
40260
  parameters: editImageSchema,
40357
40261
  async execute(_toolCallId, params, signal, _onUpdate) {
40358
- const { readFileSync: readFileSync8, existsSync: existsSync13 } = await import("node:fs");
40262
+ const { readFileSync: readFileSync8, existsSync: existsSync12 } = await import("node:fs");
40359
40263
  const { resolve: resolve5, basename: basename3 } = await import("node:path");
40360
40264
  const p = params;
40361
40265
  const imagePath = p.image;
@@ -40368,7 +40272,7 @@ function buildImageEditTool(cwd, imageModelId, baseUrl, apiKey) {
40368
40272
  const rawFilename = p.filename;
40369
40273
  const safePrompt = buildPolicySafeEditPrompt(prompt);
40370
40274
  const resolvedImage = resolve5(cwd, imagePath);
40371
- if (!existsSync13(resolvedImage)) {
40275
+ if (!existsSync12(resolvedImage)) {
40372
40276
  return {
40373
40277
  content: [
40374
40278
  {
@@ -40380,7 +40284,7 @@ function buildImageEditTool(cwd, imageModelId, baseUrl, apiKey) {
40380
40284
  };
40381
40285
  }
40382
40286
  const filename = rawFilename ? extname(rawFilename) ? rawFilename : `${rawFilename}.png` : `edited_${Date.now()}.png`;
40383
- const filePath = join10(cwd, filename.replace(/[^a-zA-Z0-9_\-./]/g, "_"));
40287
+ const filePath = join9(cwd, filename.replace(/[^a-zA-Z0-9_\-./]/g, "_"));
40384
40288
  try {
40385
40289
  const imageBuffer = readFileSync8(resolvedImage);
40386
40290
  const fields = [
@@ -40412,7 +40316,7 @@ function buildImageEditTool(cwd, imageModelId, baseUrl, apiKey) {
40412
40316
  ];
40413
40317
  if (maskPath) {
40414
40318
  const resolvedMask = resolve5(cwd, maskPath);
40415
- if (existsSync13(resolvedMask)) {
40319
+ if (existsSync12(resolvedMask)) {
40416
40320
  files.push({
40417
40321
  name: "mask",
40418
40322
  filename: basename3(resolvedMask),
@@ -40479,7 +40383,7 @@ function buildImageEditTool(cwd, imageModelId, baseUrl, apiKey) {
40479
40383
 
40480
40384
  // ../../packages/runner-pi/dist/session-utils.js
40481
40385
  import { closeSync, fstatSync, openSync, readdirSync, readSync, statSync } from "node:fs";
40482
- import { join as join11 } from "node:path";
40386
+ import { join as join10 } from "node:path";
40483
40387
  import { SessionManager } from "@earendil-works/pi-coding-agent";
40484
40388
  var MAX_SESSION_FILE_BYTES = Number(process.env.SANDAGENT_MAX_SESSION_BYTES) || 10 * 1024 * 1024;
40485
40389
  function resolveSessionPathById(cwd, sessionIdOrPath) {
@@ -40490,7 +40394,7 @@ function resolveSessionPathById(cwd, sessionIdOrPath) {
40490
40394
  try {
40491
40395
  const suffix = `_${sessionIdOrPath}.jsonl`;
40492
40396
  const match = readdirSync(sessionsDir).find((f) => f.endsWith(suffix));
40493
- return match ? join11(sessionsDir, match) : void 0;
40397
+ return match ? join10(sessionsDir, match) : void 0;
40494
40398
  } catch {
40495
40399
  return void 0;
40496
40400
  }
@@ -43629,37 +43533,37 @@ function buildAskUserQuestionTool(options) {
43629
43533
  }
43630
43534
  };
43631
43535
  }
43632
- function wrapToolWithApproval(tool, options) {
43536
+ function createToolApprovalExtension(options) {
43633
43537
  return {
43634
- name: tool.name,
43635
- label: tool.label ?? tool.name,
43636
- description: tool.description,
43637
- parameters: tool.parameters,
43638
- async execute(toolCallId, params, signal, onUpdate, ctx) {
43639
- const decision = await waitForApproval({
43640
- cwd: options.cwd,
43641
- toolCallId,
43642
- toolName: tool.name,
43643
- input: params,
43644
- signal: signal ?? options.fallbackSignal,
43645
- pollIntervalMs: options.pollIntervalMs,
43646
- timeoutMs: options.timeoutMs
43538
+ name: "bunny-agent-tool-approval",
43539
+ factory: (pi) => {
43540
+ pi.on("tool_call", async (event, ctx) => {
43541
+ if (isAskUserQuestionToolName(event.toolName) || event.parentToolCallId !== void 0) {
43542
+ return void 0;
43543
+ }
43544
+ const decision = await waitForApproval({
43545
+ cwd: options.cwd,
43546
+ toolCallId: event.toolCallId,
43547
+ toolName: event.toolName,
43548
+ input: event.input,
43549
+ signal: ctx.signal ?? options.fallbackSignal,
43550
+ pollIntervalMs: options.pollIntervalMs,
43551
+ timeoutMs: options.timeoutMs
43552
+ });
43553
+ if (decision.behavior === "deny") {
43554
+ return {
43555
+ block: true,
43556
+ reason: `Tool "${event.toolName}" was not approved: ${decision.message}`
43557
+ };
43558
+ }
43559
+ return void 0;
43647
43560
  });
43648
- if (decision.behavior === "deny") {
43649
- throw new Error(`Tool "${tool.name}" was not approved: ${decision.message}`);
43650
- }
43651
- return tool.execute(toolCallId, params, signal, onUpdate, ctx);
43652
43561
  }
43653
43562
  };
43654
43563
  }
43655
- function gateToolsForApproval(tools, bypass, options) {
43656
- if (bypass)
43657
- return tools;
43658
- return tools.map((tool) => wrapToolWithApproval(tool, options));
43659
- }
43660
43564
 
43661
43565
  // ../../packages/runner-pi/dist/tool-overrides.js
43662
- import { createBashTool, createReadTool } from "@earendil-works/pi-coding-agent";
43566
+ import { createBashToolDefinition } from "@earendil-works/pi-coding-agent";
43663
43567
 
43664
43568
  // ../../packages/runner-pi/dist/web-tools.js
43665
43569
  var braveProvider = {
@@ -44081,11 +43985,11 @@ function buildWebFetchTool() {
44081
43985
 
44082
43986
  // ../../packages/runner-pi/dist/tool-overrides.js
44083
43987
  function redactSecrets(text, secrets) {
44084
- if (Object.keys(secrets).length === 0)
43988
+ const values = secretValues(secrets).filter((v) => text.includes(v));
43989
+ if (values.length === 0)
44085
43990
  return text;
44086
43991
  let result = text;
44087
43992
  const escapeRegex3 = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
44088
- const values = Object.values(secrets).filter((v) => v.length >= 8).filter((v) => !/^\//.test(v) && !/^[A-Za-z]:[/\\]/.test(v)).sort((a, b) => b.length - a.length);
44089
43993
  for (const v of values) {
44090
43994
  const ev = escapeRegex3(v);
44091
43995
  result = result.replace(new RegExp(`^\\S+=.*${ev}.*$\\n?`, "gm"), "");
@@ -44095,19 +43999,61 @@ function redactSecrets(text, secrets) {
44095
43999
  result = result.replace(/\n{3,}/g, "\n\n");
44096
44000
  return result.trim();
44097
44001
  }
44098
- function redactResultContent(result, secrets) {
44099
- if (result?.content && Array.isArray(result.content)) {
44100
- result.content = result.content.map((c) => c.type === "text" && typeof c.text === "string" ? { ...c, text: redactSecrets(c.text, secrets) } : c);
44002
+ function secretValues(secrets) {
44003
+ return Object.values(secrets).filter((v) => v.length >= 8).filter((v) => !/^\//.test(v) && !/^[A-Za-z]:[/\\]/.test(v)).sort((a, b) => b.length - a.length);
44004
+ }
44005
+ function maskSecrets(value, values) {
44006
+ if (typeof value === "string") {
44007
+ let masked = value;
44008
+ for (const v of values)
44009
+ masked = masked.split(v).join("***");
44010
+ return masked;
44011
+ }
44012
+ if (Array.isArray(value)) {
44013
+ const next = value.map((item) => maskSecrets(item, values));
44014
+ return next.some((item, i) => item !== value[i]) ? next : value;
44015
+ }
44016
+ if (value !== null && typeof value === "object") {
44017
+ let changed = false;
44018
+ const next = {};
44019
+ for (const [key, item] of Object.entries(value)) {
44020
+ next[key] = maskSecrets(item, values);
44021
+ if (next[key] !== item)
44022
+ changed = true;
44023
+ }
44024
+ return changed ? next : value;
44101
44025
  }
44026
+ return value;
44102
44027
  }
44028
+ var ENV_OPTIONS_WITH_VALUE = /* @__PURE__ */ new Set([
44029
+ "-u",
44030
+ "--unset",
44031
+ "-C",
44032
+ "--chdir",
44033
+ "-S",
44034
+ "--split-string"
44035
+ ]);
44103
44036
  function isEnvDumpCommand(command) {
44104
44037
  const cmd = command.replace(/\s+/g, " ").trim();
44105
- return /(?:^|[|;&])\s*(?:env|printenv|export\s+-p|declare\s+-x)\b/.test(cmd);
44038
+ if (/(?:^|[|;&])\s*(?:printenv|export\s+-p|declare\s+-x)\b/.test(cmd)) {
44039
+ return true;
44040
+ }
44041
+ for (const match of cmd.matchAll(/(?:^|[|;&])\s*env(?=$|[\s|;&>])([^|;&>]*)/g)) {
44042
+ const words = match[1].trim().split(" ").filter(Boolean);
44043
+ let i = 0;
44044
+ while (i < words.length && (words[i].startsWith("-") || /^[A-Za-z_]\w*=/.test(words[i]))) {
44045
+ i += ENV_OPTIONS_WITH_VALUE.has(words[i]) ? 2 : 1;
44046
+ }
44047
+ if (i >= words.length || isEnvDumpCommand(words.slice(i).join(" "))) {
44048
+ return true;
44049
+ }
44050
+ }
44051
+ return false;
44106
44052
  }
44107
- function buildEnvInjectedBashTool(cwd, extraEnv, opts = {}) {
44053
+ function buildEnvInjectedBashTool(cwd, opts = {}) {
44108
44054
  const safeEnv = opts.systemEnv ?? {};
44109
44055
  const pathPrepend = opts.pathPrepend;
44110
- const bashAgentTool = createBashTool(cwd, {
44056
+ return createBashToolDefinition(cwd, {
44111
44057
  spawnHook: (ctx) => {
44112
44058
  const env = {
44113
44059
  ...ctx.env,
@@ -44120,50 +44066,45 @@ function buildEnvInjectedBashTool(cwd, extraEnv, opts = {}) {
44120
44066
  return { ...ctx, env };
44121
44067
  }
44122
44068
  });
44123
- return {
44124
- name: bashAgentTool.name,
44125
- label: bashAgentTool.label ?? "bash",
44126
- description: bashAgentTool.description,
44127
- // biome-ignore lint/suspicious/noExplicitAny: TypeBox schema from pi internals
44128
- parameters: bashAgentTool.parameters,
44129
- async execute(toolCallId, params, signal, onUpdate) {
44130
- const command = params.command ?? "";
44131
- if (isEnvDumpCommand(command)) {
44132
- return {
44133
- content: [
44134
- {
44135
- type: "text",
44136
- text: "Command blocked: printing or redirecting environment variables is not allowed."
44137
- }
44138
- ],
44139
- details: void 0
44140
- };
44141
- }
44142
- const result = await bashAgentTool.execute(toolCallId, params, signal, onUpdate);
44143
- redactResultContent(result, extraEnv);
44144
- return result;
44145
- }
44146
- };
44147
44069
  }
44148
- function buildSecretRedactingReadTool(cwd, secrets) {
44149
- const readAgentTool = createReadTool(cwd);
44070
+ var ENV_DUMP_BLOCKED_MESSAGE = "Command blocked: printing or redirecting environment variables is not allowed.";
44071
+ function createSecretGuardExtension(secrets) {
44150
44072
  return {
44151
- name: readAgentTool.name,
44152
- label: readAgentTool.label ?? "read",
44153
- description: readAgentTool.description,
44154
- // biome-ignore lint/suspicious/noExplicitAny: TypeBox schema from pi internals
44155
- parameters: readAgentTool.parameters,
44156
- async execute(toolCallId, params, signal, onUpdate) {
44157
- const result = await readAgentTool.execute(toolCallId, params, signal, onUpdate);
44158
- redactResultContent(result, secrets);
44159
- return result;
44073
+ name: "bunny-agent-secret-guard",
44074
+ factory: (pi) => {
44075
+ pi.on("tool_call", (event) => {
44076
+ if (event.toolName === "bash" && typeof event.input.command === "string" && isEnvDumpCommand(event.input.command)) {
44077
+ return { block: true, reason: ENV_DUMP_BLOCKED_MESSAGE };
44078
+ }
44079
+ return void 0;
44080
+ });
44081
+ const values = secretValues(secrets);
44082
+ pi.on("tool_result", (event) => {
44083
+ if (values.length === 0)
44084
+ return void 0;
44085
+ let contentChanged = false;
44086
+ const content = event.content.map((part) => {
44087
+ if (part.type !== "text")
44088
+ return part;
44089
+ const text = redactSecrets(part.text, secrets);
44090
+ if (text === part.text)
44091
+ return part;
44092
+ contentChanged = true;
44093
+ return { ...part, text };
44094
+ });
44095
+ const details = maskSecrets(event.details, values);
44096
+ const structuredContent = maskSecrets(event.structuredContent, values);
44097
+ if (!contentChanged && details === event.details && structuredContent === event.structuredContent) {
44098
+ return void 0;
44099
+ }
44100
+ return { content, details, structuredContent };
44101
+ });
44160
44102
  }
44161
44103
  };
44162
44104
  }
44163
44105
  function buildSecretAwareTools(cwd, secrets, opts = {}) {
44164
44106
  const tools = [
44165
- buildEnvInjectedBashTool(cwd, secrets, opts),
44166
- buildSecretRedactingReadTool(cwd, secrets),
44107
+ buildEnvInjectedBashTool(cwd, opts),
44167
44108
  buildWebFetchTool()
44168
44109
  ];
44169
44110
  if (resolveSearchProvider(secrets)) {
@@ -44275,7 +44216,7 @@ function serializeResult2(result) {
44275
44216
  }
44276
44217
 
44277
44218
  // ../../packages/runner-pi/dist/pi-runner.js
44278
- var LOG_PREFIX2 = "[bunny-agent:pi]";
44219
+ var LOG_PREFIX = "[bunny-agent:pi]";
44279
44220
  var DEFAULT_DYNAMIC_MODEL_PROFILE = {
44280
44221
  contextWindow: 128e3,
44281
44222
  maxTokens: 16384,
@@ -44403,8 +44344,8 @@ function traceRawMessage(debugCwd, data, reset = false, optionsEnv) {
44403
44344
  if (!enabled)
44404
44345
  return;
44405
44346
  try {
44406
- const file3 = join13(debugCwd, "pi-message-stream-debug.json");
44407
- if (reset && existsSync9(file3))
44347
+ const file3 = join12(debugCwd, "pi-message-stream-debug.json");
44348
+ if (reset && existsSync8(file3))
44408
44349
  unlinkSync7(file3);
44409
44350
  const type = data !== null && typeof data === "object" ? data.type : void 0;
44410
44351
  let payload = data;
@@ -44418,6 +44359,39 @@ function traceRawMessage(debugCwd, data, reset = false, optionsEnv) {
44418
44359
  } catch {
44419
44360
  }
44420
44361
  }
44362
+ async function createPiResourceLoader(options) {
44363
+ const { systemPrompt } = options;
44364
+ const defaultSkillDirs = [
44365
+ join12(options.agentDir, "skills"),
44366
+ join12(options.cwd, CONFIG_DIR_NAME, "skills")
44367
+ ].filter((dir) => existsSync8(dir));
44368
+ const loader = new DefaultResourceLoader({
44369
+ cwd: options.cwd,
44370
+ agentDir: options.agentDir,
44371
+ settingsManager: options.settingsManager,
44372
+ noSkills: true,
44373
+ additionalSkillPaths: [...defaultSkillDirs, ...options.skillPaths ?? []],
44374
+ appendSystemPromptOverride: systemPrompt ? (base) => [...base, systemPrompt] : void 0,
44375
+ extensionFactories: options.extensionFactories
44376
+ });
44377
+ await loader.reload();
44378
+ if (options.skillPaths && options.skillPaths.length > 0) {
44379
+ logSkillLoad(options.cwd, options.skillPaths, loader.getSkills());
44380
+ }
44381
+ return loader;
44382
+ }
44383
+ function logSkillLoad(cwd, skillPaths, result) {
44384
+ const lines = [`${LOG_PREFIX} loadSkills cwd=${cwd}`];
44385
+ for (const raw of skillPaths) {
44386
+ const abs = isAbsolute(raw) ? raw : resolve2(cwd, raw);
44387
+ lines.push(` ${raw} -> ${abs} (exists: ${existsSync8(abs) ? "yes" : "no"})`);
44388
+ }
44389
+ lines.push(` loaded skills (${result.skills.length}): ${result.skills.map((s) => s.name).join(", ")}`);
44390
+ for (const d of result.diagnostics) {
44391
+ lines.push(` [${d.type}] ${d.message}${d.path !== void 0 ? ` path=${d.path}` : ""}`);
44392
+ }
44393
+ console.error(lines.join("\n"));
44394
+ }
44421
44395
  function createPiRunner(options = {}) {
44422
44396
  const modelSpec = options.model;
44423
44397
  if (modelSpec == null || modelSpec.trim() === "") {
@@ -44431,6 +44405,8 @@ function createPiRunner(options = {}) {
44431
44405
  const mcpExtension = shouldEnableMcp(options.mcpConfig, options.allowedTools) ? createMcpExtension(options.mcpConfig) : void 0;
44432
44406
  return {
44433
44407
  async *run(userInput) {
44408
+ const agentDir = getAgentDir();
44409
+ const settingsManager = SettingsManager.create(cwd, agentDir);
44434
44410
  const modelRuntime = await ModelRuntime.create({
44435
44411
  modelsPath: null,
44436
44412
  allowModelNetwork: false
@@ -44477,7 +44453,7 @@ function createPiRunner(options = {}) {
44477
44453
  const sessionManager = await (async () => {
44478
44454
  if (forkFrom) {
44479
44455
  const sourcePath = resolveSessionPathById(cwd, forkFrom);
44480
- console.error(`${LOG_PREFIX2} fork: sourceId=${forkFrom} sourcePath=${sourcePath ?? "(not found)"}`);
44456
+ console.error(`${LOG_PREFIX} fork: sourceId=${forkFrom} sourcePath=${sourcePath ?? "(not found)"}`);
44481
44457
  if (!sourcePath) {
44482
44458
  throw new Error(`Pi runner: forkFrom source session not found: ${forkFrom}`);
44483
44459
  }
@@ -44485,11 +44461,11 @@ function createPiRunner(options = {}) {
44485
44461
  }
44486
44462
  if (resume) {
44487
44463
  const sessionPath2 = resolveSessionPathById(cwd, resume);
44488
- console.error(`${LOG_PREFIX2} resume: id=${resume} path=${sessionPath2 ?? "(not found)"}`);
44464
+ console.error(`${LOG_PREFIX} resume: id=${resume} path=${sessionPath2 ?? "(not found)"}`);
44489
44465
  if (sessionPath2) {
44490
44466
  if (isSessionFileTooLarge(sessionPath2)) {
44491
44467
  const context = extractSessionContext(sessionPath2);
44492
- console.error(`${LOG_PREFIX2} session file too large, starting fresh${context ? " (with context)" : ""}`);
44468
+ console.error(`${LOG_PREFIX} session file too large, starting fresh${context ? " (with context)" : ""}`);
44493
44469
  const newMgr = SessionManager2.create(cwd);
44494
44470
  if (context) {
44495
44471
  const firstId = newMgr.getEntries()[0]?.id ?? "";
@@ -44503,24 +44479,14 @@ function createPiRunner(options = {}) {
44503
44479
  }
44504
44480
  return SessionManager2.create(cwd);
44505
44481
  })();
44506
- const resourceLoader = options.skillPaths || options.systemPrompt || mcpExtension ? new BunnyAgentResourceLoader({
44507
- cwd,
44508
- skillPaths: options.skillPaths,
44509
- appendSystemPrompt: options.systemPrompt,
44510
- extensionFactories: mcpExtension ? [mcpExtension] : void 0
44511
- }) : void 0;
44512
- if (options.skillPaths && options.skillPaths.length > 0) {
44513
- console.error(`${LOG_PREFIX2} runner: cwd=${cwd} skillPaths=${JSON.stringify(options.skillPaths)}`);
44514
- }
44515
- if (resourceLoader) {
44516
- await resourceLoader.reload();
44517
- }
44518
44482
  const applyPatchShimDir = ensureApplyPatchShim();
44519
44483
  const customTools = options.env && Object.keys(options.env).length > 0 ? buildSecretAwareTools(cwd, options.env, {
44520
44484
  systemEnv: options.systemEnv,
44521
44485
  pathPrepend: applyPatchShimDir
44522
44486
  }) : applyPatchShimDir ? [
44523
- buildEnvInjectedBashTool(cwd, {}, { pathPrepend: applyPatchShimDir })
44487
+ buildEnvInjectedBashTool(cwd, {
44488
+ pathPrepend: applyPatchShimDir
44489
+ })
44524
44490
  ] : [];
44525
44491
  if (provider === "openai") {
44526
44492
  customTools.push(buildApplyPatchTool(cwd));
@@ -44541,29 +44507,34 @@ function createPiRunner(options = {}) {
44541
44507
  cwd,
44542
44508
  fallbackSignal: options.abortController?.signal
44543
44509
  };
44544
- let regularTools = [
44545
- ...applyAllowedTools(customTools, allowedTools),
44546
- ...toolRefDefinitions
44547
- ];
44548
- if (!bypassApproval) {
44549
- const overriddenNames = new Set(regularTools.map((tool) => tool.name));
44550
- const builtinTools = applyAllowedTools(createCodingTools(cwd), allowedTools).filter((tool) => !overriddenNames.has(tool.name));
44551
- regularTools = [...regularTools, ...builtinTools];
44552
- }
44553
- const gatedTools = gateToolsForApproval(regularTools, bypassApproval, approvalGate);
44554
44510
  const askUserQuestionEnabled = allowedTools === void 0 || allowedTools.includes(ASK_USER_QUESTION_TOOL_NAME);
44555
44511
  const previousModel = sessionManager.buildSessionContext?.().model;
44556
44512
  const thinkingLevel = resolveInitialThinkingLevel(options.effort, previousModel, model);
44513
+ const resourceLoader = await createPiResourceLoader({
44514
+ cwd,
44515
+ agentDir,
44516
+ settingsManager,
44517
+ skillPaths: options.skillPaths,
44518
+ systemPrompt: options.systemPrompt,
44519
+ extensionFactories: [
44520
+ createSecretGuardExtension(options.env ?? {}),
44521
+ ...bypassApproval ? [] : [createToolApprovalExtension(approvalGate)],
44522
+ ...mcpExtension ? [mcpExtension] : []
44523
+ ]
44524
+ });
44557
44525
  const { session } = await createAgentSession({
44558
44526
  cwd,
44527
+ agentDir,
44559
44528
  model,
44560
44529
  sessionManager,
44530
+ settingsManager,
44561
44531
  modelRuntime,
44562
44532
  resourceLoader,
44563
44533
  thinkingLevel,
44564
44534
  tools: allowedTools,
44565
44535
  customTools: [
44566
- ...gatedTools,
44536
+ ...applyAllowedTools(customTools, allowedTools),
44537
+ ...toolRefDefinitions,
44567
44538
  ...askUserQuestionEnabled ? [
44568
44539
  buildAskUserQuestionTool({
44569
44540
  ...approvalGate,
@@ -44696,17 +44667,17 @@ function createPiRunner(options = {}) {
44696
44667
  }
44697
44668
 
44698
44669
  // ../../packages/runner-harness/dist/session.js
44699
- import { existsSync as existsSync10, mkdirSync as mkdirSync8, readFileSync as readFileSync6, writeFileSync as writeFileSync9 } from "node:fs";
44700
- import { join as join14 } from "node:path";
44670
+ import { existsSync as existsSync9, mkdirSync as mkdirSync8, readFileSync as readFileSync6, writeFileSync as writeFileSync9 } from "node:fs";
44671
+ import { join as join13 } from "node:path";
44701
44672
  var DIR = ".bunny-agent";
44702
44673
  var FILE = "session-id";
44703
44674
  function sessionPath(cwd) {
44704
- return join14(cwd, DIR, FILE);
44675
+ return join13(cwd, DIR, FILE);
44705
44676
  }
44706
44677
  function readSessionId(cwd) {
44707
44678
  try {
44708
44679
  const p = sessionPath(cwd);
44709
- if (!existsSync10(p))
44680
+ if (!existsSync9(p))
44710
44681
  return void 0;
44711
44682
  return readFileSync6(p, "utf8").trim() || void 0;
44712
44683
  } catch {
@@ -44715,28 +44686,28 @@ function readSessionId(cwd) {
44715
44686
  }
44716
44687
  function writeSessionId(cwd, id) {
44717
44688
  try {
44718
- mkdirSync8(join14(cwd, DIR), { recursive: true });
44689
+ mkdirSync8(join13(cwd, DIR), { recursive: true });
44719
44690
  writeFileSync9(sessionPath(cwd), id, "utf8");
44720
44691
  } catch {
44721
44692
  }
44722
44693
  }
44723
44694
 
44724
44695
  // ../../packages/runner-harness/dist/skills.js
44725
- import { existsSync as existsSync11, readdirSync as readdirSync2, statSync as statSync2 } from "node:fs";
44726
- import { homedir as homedir2 } from "node:os";
44727
- import { join as join15 } from "node:path";
44696
+ import { existsSync as existsSync10, readdirSync as readdirSync2, statSync as statSync2 } from "node:fs";
44697
+ import { homedir } from "node:os";
44698
+ import { join as join14 } from "node:path";
44728
44699
  function discoverSkillPaths(cwd) {
44729
44700
  const paths = [];
44730
44701
  for (const base of [
44731
- join15(cwd, "skills"),
44732
- join15(homedir2(), ".bunny-agent", "skills")
44702
+ join14(cwd, "skills"),
44703
+ join14(homedir(), ".bunny-agent", "skills")
44733
44704
  ]) {
44734
- if (!existsSync11(base))
44705
+ if (!existsSync10(base))
44735
44706
  continue;
44736
44707
  try {
44737
44708
  for (const entry of readdirSync2(base)) {
44738
- const full = join15(base, entry);
44739
- if (statSync2(full).isDirectory() && existsSync11(join15(full, "SKILL.md"))) {
44709
+ const full = join14(base, entry);
44710
+ if (statSync2(full).isDirectory() && existsSync10(join14(full, "SKILL.md"))) {
44740
44711
  paths.push(full);
44741
44712
  }
44742
44713
  }
@@ -45270,14 +45241,14 @@ async function runAcpAgent(options) {
45270
45241
  import { execSync } from "node:child_process";
45271
45242
  import {
45272
45243
  copyFileSync,
45273
- existsSync as existsSync12,
45244
+ existsSync as existsSync11,
45274
45245
  mkdirSync as mkdirSync9,
45275
45246
  readdirSync as readdirSync3,
45276
45247
  readFileSync as readFileSync7,
45277
45248
  statSync as statSync3,
45278
45249
  writeFileSync as writeFileSync10
45279
45250
  } from "node:fs";
45280
- import { basename as basename2, dirname as dirname5, join as join16, resolve as resolve3 } from "node:path";
45251
+ import { basename as basename2, dirname as dirname5, join as join15, resolve as resolve3 } from "node:path";
45281
45252
  import { fileURLToPath as fileURLToPath2 } from "node:url";
45282
45253
  function getPackageRoot() {
45283
45254
  const thisDir = dirname5(fileURLToPath2(import.meta.url));
@@ -45286,7 +45257,7 @@ function getPackageRoot() {
45286
45257
  function getShippedDockerfile() {
45287
45258
  const packageRoot = getPackageRoot();
45288
45259
  const candidates = [
45289
- join16(packageRoot, "Dockerfile"),
45260
+ join15(packageRoot, "Dockerfile"),
45290
45261
  resolve3(
45291
45262
  packageRoot,
45292
45263
  "..",
@@ -45298,7 +45269,7 @@ function getShippedDockerfile() {
45298
45269
  resolve3(process.cwd(), "docker", "bunny-agent-claude", "Dockerfile")
45299
45270
  ];
45300
45271
  for (const p of candidates) {
45301
- if (existsSync12(p)) return p;
45272
+ if (existsSync11(p)) return p;
45302
45273
  }
45303
45274
  console.error(
45304
45275
  `\u274C Dockerfile not found. Searched:
@@ -45319,7 +45290,7 @@ function ensureDocker() {
45319
45290
  }
45320
45291
  function resolveTemplatePath(template) {
45321
45292
  const abs = resolve3(process.cwd(), template);
45322
- if (!existsSync12(abs)) {
45293
+ if (!existsSync11(abs)) {
45323
45294
  console.error(`\u274C Template directory not found: ${abs}`);
45324
45295
  process.exit(1);
45325
45296
  }
@@ -45328,8 +45299,8 @@ function resolveTemplatePath(template) {
45328
45299
  function copyDirSync(src, dest) {
45329
45300
  mkdirSync9(dest, { recursive: true });
45330
45301
  for (const entry of readdirSync3(src)) {
45331
- const srcPath = join16(src, entry);
45332
- const destPath = join16(dest, entry);
45302
+ const srcPath = join15(src, entry);
45303
+ const destPath = join15(dest, entry);
45333
45304
  if (statSync3(srcPath).isDirectory()) {
45334
45305
  copyDirSync(srcPath, destPath);
45335
45306
  } else {
@@ -45349,23 +45320,23 @@ async function buildImage(opts) {
45349
45320
  console.log(` Push: ${opts.push}`);
45350
45321
  console.log("");
45351
45322
  ensureDocker();
45352
- const buildContext = join16(process.cwd(), ".docker-staging");
45323
+ const buildContext = join15(process.cwd(), ".docker-staging");
45353
45324
  mkdirSync9(buildContext, { recursive: true });
45354
45325
  let dockerfile = readFileSync7(getShippedDockerfile(), "utf8");
45355
45326
  if (templatePath && templateName) {
45356
- const destDir = join16(buildContext, "templates", templateName);
45327
+ const destDir = join15(buildContext, "templates", templateName);
45357
45328
  mkdirSync9(destDir, { recursive: true });
45358
- const claudeMd = join16(templatePath, "CLAUDE.md");
45359
- if (existsSync12(claudeMd))
45360
- copyFileSync(claudeMd, join16(destDir, "CLAUDE.md"));
45361
- const claudeDir = join16(templatePath, ".claude");
45362
- if (existsSync12(claudeDir)) copyDirSync(claudeDir, join16(destDir, ".claude"));
45329
+ const claudeMd = join15(templatePath, "CLAUDE.md");
45330
+ if (existsSync11(claudeMd))
45331
+ copyFileSync(claudeMd, join15(destDir, "CLAUDE.md"));
45332
+ const claudeDir = join15(templatePath, ".claude");
45333
+ if (existsSync11(claudeDir)) copyDirSync(claudeDir, join15(destDir, ".claude"));
45363
45334
  let copyLines = "\n# Template files\nRUN mkdir -p /opt/bunny-agent/templates";
45364
- if (existsSync12(join16(destDir, "CLAUDE.md"))) {
45335
+ if (existsSync11(join15(destDir, "CLAUDE.md"))) {
45365
45336
  copyLines += `
45366
45337
  COPY templates/${templateName}/CLAUDE.md /opt/bunny-agent/templates/CLAUDE.md`;
45367
45338
  }
45368
- if (existsSync12(join16(destDir, ".claude"))) {
45339
+ if (existsSync11(join15(destDir, ".claude"))) {
45369
45340
  copyLines += `
45370
45341
  COPY templates/${templateName}/.claude /opt/bunny-agent/templates/.claude`;
45371
45342
  }
@@ -45374,10 +45345,10 @@ COPY templates/${templateName}/.claude /opt/bunny-agent/templates/.claude`;
45374
45345
  CMD `);
45375
45346
  console.log("\u{1F9E9} Injected template files into Dockerfile");
45376
45347
  }
45377
- writeFileSync10(join16(buildContext, "Dockerfile"), dockerfile);
45348
+ writeFileSync10(join15(buildContext, "Dockerfile"), dockerfile);
45378
45349
  console.log("\u{1F433} Building Docker image...");
45379
45350
  run(
45380
- `docker build --platform=${opts.platform} -t ${localImage} -f ${join16(buildContext, "Dockerfile")} ${buildContext}`
45351
+ `docker build --platform=${opts.platform} -t ${localImage} -f ${join15(buildContext, "Dockerfile")} ${buildContext}`
45381
45352
  );
45382
45353
  console.log(`
45383
45354
  \u2705 Image built: ${localImage}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bunny-agent/runner-cli",
3
- "version": "0.9.62-release.2",
3
+ "version": "0.9.63-beta.2",
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": {
@@ -60,14 +60,14 @@
60
60
  "typescript": "^5.3.0",
61
61
  "vitest": "^1.6.1",
62
62
  "@bunny-agent/apply-patch": "0.1.0",
63
- "@bunny-agent/runner-claude": "0.6.2",
64
63
  "@bunny-agent/runner-codex": "0.6.2",
65
- "@bunny-agent/runner-copilot": "0.1.0",
64
+ "@bunny-agent/runner-claude": "0.6.2",
66
65
  "@bunny-agent/runner-gemini": "0.6.2",
67
- "@bunny-agent/runner-opencode": "0.6.2",
68
66
  "@bunny-agent/runner-harness": "0.1.1-beta.0",
69
- "@bunny-agent/manager": "0.9.62-release.2",
67
+ "@bunny-agent/runner-copilot": "0.1.0",
68
+ "@bunny-agent/runner-opencode": "0.6.2",
70
69
  "@bunny-agent/runner-pi": "0.6.4-beta.0",
70
+ "@bunny-agent/manager": "0.9.63-beta.2",
71
71
  "@bunny-agent/server-acp": "0.1.0"
72
72
  },
73
73
  "scripts": {