@botconnector/bccli 0.4.10 → 0.4.12

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 (53) hide show
  1. package/dist/{acp-L4WGVJTY.js → acp-JZW3XHE5.js} +20 -5
  2. package/dist/acp.d.ts +17 -1
  3. package/dist/args-XVO7TB4C.js +13 -0
  4. package/dist/budget.d.ts +4 -1
  5. package/dist/{chunk-67REFMSY.js → chunk-3CYQYHPK.js} +1 -1
  6. package/dist/{chunk-VXRW5SYP.js → chunk-6JK4IBGK.js} +25 -14
  7. package/dist/{chunk-MPYSYTSO.js → chunk-6SDRWEPN.js} +1 -1
  8. package/dist/{chunk-4WI5ON64.js → chunk-7GM6NN5E.js} +1 -1
  9. package/dist/{chunk-6RVAPAE5.js → chunk-7XD47KN5.js} +1 -1
  10. package/dist/{chunk-SXRQ2Q7A.js → chunk-INMSTUB2.js} +4 -4
  11. package/dist/{chunk-CIEPLTGS.js → chunk-ISOKH5WB.js} +5 -5
  12. package/dist/{chunk-6N2BBLIT.js → chunk-JUC3ZOCV.js} +2 -2
  13. package/dist/{chunk-3PUWH62K.js → chunk-KDVL2NP6.js} +34 -50
  14. package/dist/{chunk-24KDXQZT.js → chunk-KEKDRROL.js} +3 -3
  15. package/dist/{chunk-ZWXKBFKW.js → chunk-KJIXA6MP.js} +23 -9
  16. package/dist/{chunk-CBD52BYV.js → chunk-OFMZ6CE6.js} +1 -1
  17. package/dist/{chunk-7HDBPDAD.js → chunk-Q42ZGVAQ.js} +2 -2
  18. package/dist/{chunk-ED5TTYQU.js → chunk-R754XIBJ.js} +11 -3
  19. package/dist/{chunk-3K66L37W.js → chunk-S3QEZDKB.js} +3 -3
  20. package/dist/{chunk-PGDYPOQW.js → chunk-SWNRA3EW.js} +4 -4
  21. package/dist/{chunk-USFXL5BS.js → chunk-TLCL6DC7.js} +6 -6
  22. package/dist/{chunk-WFZKJY3R.js → chunk-YYTFJUO2.js} +3 -2
  23. package/dist/{chunk-5QCCV2JM.js → chunk-ZJRZTMJF.js} +47 -18
  24. package/dist/cli.js +39 -39
  25. package/dist/{completion-GEFRIQWV.js → completion-Q7VHOPPV.js} +1 -1
  26. package/dist/{config-JKMYKNAT.js → config-T7MKGJVG.js} +2 -2
  27. package/dist/{config-JLOH5M6B.js → config-YYEAEN2C.js} +3 -3
  28. package/dist/{ghBotCli-WEVS6S6O.js → ghBotCli-7VUGI63S.js} +18 -16
  29. package/dist/{integrationCli-PKMICTHC.js → integrationCli-TO42MBZB.js} +192 -12
  30. package/dist/{login-2TZRODUL.js → login-BSMUGW7L.js} +6 -4
  31. package/dist/login.d.ts +2 -0
  32. package/dist/{mcpCli-TAPKYGHO.js → mcpCli-ZT7CRYMP.js} +6 -6
  33. package/dist/{modelsCli-6PK7V2QZ.js → modelsCli-2ZRW4CC4.js} +6 -6
  34. package/dist/{oauthLogin-UI34LQZU.js → oauthLogin-THSM6FE7.js} +1 -1
  35. package/dist/{pluginsCli-RCF33U5J.js → pluginsCli-EKQI4CJW.js} +2 -2
  36. package/dist/{prCli-24PMW7NS.js → prCli-FMQTOBIV.js} +1 -1
  37. package/dist/providerCli-3TFEKV4C.js +15 -0
  38. package/dist/sdk.js +111 -75
  39. package/dist/{serve-7BFCIEPJ.js → serve-DC6YICSK.js} +16 -16
  40. package/dist/{serve-XNJI67DU.js → serve-V6YVZTNR.js} +3 -3
  41. package/dist/session-LX4UNPCD.js +12 -0
  42. package/dist/setup-MFG3BWEU.js +24 -0
  43. package/dist/setup.d.ts +5 -0
  44. package/dist/{statsCli-QDHE4TKB.js → statsCli-7MIQHLVW.js} +2 -2
  45. package/dist/tools/bash.d.ts +2 -0
  46. package/dist/{transferCli-7OKKYGG2.js → transferCli-SRSYQLSJ.js} +4 -4
  47. package/dist/{ui-SZAIIJC5.js → ui-IMCUSB6Q.js} +14 -14
  48. package/dist/{update-JSNVVXPS.js → update-POT5W6MC.js} +2 -2
  49. package/package.json +1 -1
  50. package/dist/args-EW7XJVQN.js +0 -13
  51. package/dist/providerCli-XA4LTWRD.js +0 -15
  52. package/dist/session-VAPIHHUG.js +0 -12
  53. package/dist/setup-GPIMZOWE.js +0 -24
package/dist/sdk.js CHANGED
@@ -168,7 +168,7 @@ Usage:
168
168
  bccli import <file> import a session JSONL into the current project
169
169
  bccli pr <number> [--repo owner/name] [task] check out a GitHub PR and run a task on it
170
170
  bccli integrations show the status of external agent integrations
171
- bccli connect <agent> opencode, aider, cline, dsh, codex, claude-code, cursor, openai-cli, openai-sdk, openai-compatible, openclaw, hermes
171
+ bccli connect <agent> opencode, aider, cline, dsh, codex, claude-code, openclaw, hermes, crush, kilo, cursor, openai-cli, openai-sdk, openai-compatible
172
172
  bccli disconnect <agent> remove an integration and restore the previous config
173
173
 
174
174
  Options:
@@ -199,7 +199,7 @@ Pemakaian:
199
199
  bccli provider list|add <id>|remove <id> kelola provider (custom: --url <url> [--name N] [--key-env ENV])
200
200
  bccli mcp list|add <nama>|remove <nama>|auth <nama>|logout <nama>|serve [--base-url <url>] kelola server MCP, atau layani tools akun ini lewat stdio
201
201
  bccli integrations lihat status integrasi agent eksternal
202
- bccli connect <agent> opencode, aider, cline, dsh, codex, claude-code, cursor, openai-cli, openai-sdk, openai-compatible, openclaw, hermes
202
+ bccli connect <agent> opencode, aider, cline, dsh, codex, claude-code, openclaw, hermes, crush, kilo, cursor, openai-cli, openai-sdk, openai-compatible
203
203
  bccli disconnect <agent> lepas integrasi dan pulihkan config sebelumnya
204
204
 
205
205
  Opsi:
@@ -227,6 +227,7 @@ Opsi:
227
227
  "usageCap.usd must be a number greater than 0.": "usageCap.usd harus angka lebih dari 0.",
228
228
  "usageCap.usd requires usageCap.prices (USD per 1M tokens).": "usageCap.usd memerlukan usageCap.prices (USD per 1 juta token).",
229
229
  'usageCap.prices["{key}"] must have numeric input and output (USD per 1M tokens).': 'usageCap.prices["{key}"] harus punya input dan output numerik (USD per 1 juta token).',
230
+ 'usageCap.prices["{key}"].cachedInput must be a number >= 0 (USD per 1M tokens).': 'usageCap.prices["{key}"].cachedInput harus angka >= 0 (USD per 1 juta token).',
230
231
  "usageCap needs at least one of: tokens, usd.": "usageCap memerlukan setidaknya salah satu dari: tokens, usd.",
231
232
  'updateCheck must be "on" or "off".': 'updateCheck harus "on" atau "off".',
232
233
  "repoMap must be true or false.": "repoMap harus true atau false.",
@@ -376,7 +377,12 @@ Opsi:
376
377
  '-p mode needs a task, e.g. bccli -p "explain this repo"': 'Mode -p butuh tugas, contoh: bccli -p "jelaskan repo ini"',
377
378
  'Project MCP server "{name}" skipped (not approved yet; run interactive bccli once to approve it).': 'Server MCP project "{name}" dilewati (belum diizinkan; jalankan bccli interaktif sekali untuk menyetujuinya).',
378
379
  'Interactive mode needs a terminal. For scripts/CI use: bccli -p "task"': 'Mode interaktif butuh terminal. Untuk skrip/CI pakai: bccli -p "tugas"',
379
- "Agent must be one of: opencode, aider, cline, deepseek-harness (dsh), cursor, openai-cli, openai-sdk, openai-compatible, codex, claude-code, openclaw, hermes": "Agent harus salah satu dari: opencode, aider, cline, deepseek-harness (dsh), cursor, openai-cli, openai-sdk, openai-compatible, codex, claude-code, openclaw, hermes",
380
+ "Agent must be one of: opencode, aider, cline, deepseek-harness (dsh), cursor, openai-cli, openai-sdk, openai-compatible, codex, claude-code, openclaw, hermes, crush, kilo": "Agent harus salah satu dari: opencode, aider, cline, deepseek-harness (dsh), cursor, openai-cli, openai-sdk, openai-compatible, codex, claude-code, openclaw, hermes, crush, kilo",
381
+ "Crush already has a botconnector provider outside the BCCLI block: {path}": "Crush sudah memiliki provider botconnector di luar blok BCCLI: {path}",
382
+ "Crush is connected to BotConnector ({n} models available; large model {model}).": "Crush terhubung ke BotConnector ({n} model tersedia; model large {model}).",
383
+ "The Kilo global kilo.json is not valid JSON: {path}": "kilo.json global Kilo tidak valid JSON: {path}",
384
+ 'Kilo already has a provider "{id}" with a different endpoint.': 'Kilo sudah memiliki provider "{id}" dengan endpoint lain.',
385
+ "Kilo Code is connected to BotConnector ({n} models available; suggested model {model}).": "Kilo Code terhubung ke BotConnector ({n} model tersedia; model yang disarankan {model}).",
380
386
  "Cannot determine the home directory.": "Home directory tidak dapat ditentukan.",
381
387
  "No BotConnector API key yet. Run bccli login bc-cloud first.": "API key BotConnector belum tersedia. Jalankan bccli login bc-cloud terlebih dahulu.",
382
388
  "Could not load the BotConnector model catalog (HTTP {status}).": "Katalog model BotConnector gagal dimuat (HTTP {status}).",
@@ -412,6 +418,8 @@ Opsi:
412
418
  "API key: env {env} only (via key_env; a plaintext key in config.yaml is never written).": "API key: hanya env {env} (melalui key_env; key plaintext tidak pernah ditulis ke config.yaml).",
413
419
  "{target}: not managed by BCCLI yet.": "{target}: belum dikelola oleh BCCLI.",
414
420
  "{target}: the BotConnector integration was removed and the config restored.": "{target}: integrasi BotConnector dilepas dan config dipulihkan.",
421
+ '{target}: {path} changed after BCCLI connected it, so the old backup was not copied over it. Remove the BCCLI part (provider "botconnector" or the marked block) from that file by hand.': '{target}: {path} berubah setelah BCCLI menyambungkannya, jadi backup lama tidak ditimpa ke file itu. Hapus bagian BCCLI (provider "botconnector" atau blok bertanda) dari file tersebut secara manual.',
422
+ "{target}: the BotConnector integration was removed. {n} file(s) were left untouched (see above); delete the BCCLI entries in them by hand for a clean removal.": "{target}: integrasi BotConnector dilepas. {n} file tidak disentuh (lihat di atas); hapus entri BCCLI di dalamnya secara manual agar bersih.",
415
423
  "Custom commands:": "Perintah custom:",
416
424
  "Skills:": "Skill:",
417
425
  "shift+tab switch permission mode esc cancel the turn": "shift+tab ganti mode izin esc batalkan giliran",
@@ -617,6 +625,9 @@ function resolvedUsageCap(global) {
617
625
  if (!price || !nonNegative(price.input) || !nonNegative(price.output)) {
618
626
  throw new ConfigError(t('usageCap.prices["{key}"] must have numeric input and output (USD per 1M tokens).', { key }));
619
627
  }
628
+ if (price.cachedInput !== void 0 && !nonNegative(price.cachedInput)) {
629
+ throw new ConfigError(t('usageCap.prices["{key}"].cachedInput must be a number >= 0 (USD per 1M tokens).', { key }));
630
+ }
620
631
  }
621
632
  }
622
633
  if (tokens2 === void 0 && usd === void 0) throw new ConfigError(t("usageCap needs at least one of: tokens, usd."));
@@ -704,7 +715,7 @@ function loadConfig(cwd, env = process.env) {
704
715
  const project = readJsonConfig(join(cwd, ".bccli", "config.json"));
705
716
  const known = { ...DEFAULT_CONFIG.providers, ...global.providers };
706
717
  const fromProject = untrustedProviders(project, known);
707
- const rawFallbacks = project.fallbackModels ?? global.fallbackModels;
718
+ const rawFallbacks = global.fallbackModels;
708
719
  const fallbackModels = Array.isArray(rawFallbacks) ? rawFallbacks.filter((m) => typeof m === "string" && m.includes("/")) : void 0;
709
720
  return {
710
721
  model: project.model ?? global.model ?? DEFAULT_CONFIG.model,
@@ -884,7 +895,7 @@ var Permissions = class {
884
895
  }
885
896
  /** Rules that "[a] allow for this session" would add; undefined when it must always be asked. */
886
897
  rulesFor(req) {
887
- if (requiresSensitiveToolApproval(req) && (req.kind === "read" || process.env.BC_TOOL_POLICY_ENFORCE === "true"))
898
+ if (requiresSensitiveToolApproval(req) && (req.kind === "read" || req.kind === "edit" || process.env.BC_TOOL_POLICY_ENFORCE === "true"))
888
899
  return void 0;
889
900
  if (req.kind === "edit") return this.editInProject(req.target) ? ["edit(project)"] : void 0;
890
901
  if (req.kind === "fetch") {
@@ -906,7 +917,8 @@ var Permissions = class {
906
917
  }
907
918
  check(req) {
908
919
  if (this.mode === "plan" && req.kind !== "read") return "deny";
909
- if (req.kind === "read" && this.mode !== "allowAll" && requiresSensitiveToolApproval(req)) return "ask";
920
+ if ((req.kind === "read" || req.kind === "edit") && this.mode !== "allowAll" && requiresSensitiveToolApproval(req))
921
+ return "ask";
910
922
  if (process.env.BC_TOOL_POLICY_ENFORCE === "true" && requiresSensitiveToolApproval(req)) return "ask";
911
923
  if (req.kind === "read") return "allow";
912
924
  if (this.mode === "allowAll") return "allow";
@@ -1541,7 +1553,7 @@ function hasPendingProgrammaticReplay(messages) {
1541
1553
  }
1542
1554
  return pending;
1543
1555
  }
1544
- async function readResponsesStream(res, onText, onThinking) {
1556
+ async function readResponsesStream(res, onText, onThinking, signal) {
1545
1557
  let text = "";
1546
1558
  let thinking = "";
1547
1559
  let usage;
@@ -1643,17 +1655,28 @@ async function readResponsesStream(res, onText, onThinking) {
1643
1655
  );
1644
1656
  }
1645
1657
  };
1646
- for await (const chunk of res.body) {
1647
- buffer += decoder.decode(chunk, { stream: true });
1648
- let newline = buffer.indexOf("\n");
1649
- while (newline >= 0 && loopAt < 0) {
1650
- handle(buffer.slice(0, newline).trim());
1651
- buffer = buffer.slice(newline + 1);
1652
- newline = buffer.indexOf("\n");
1653
- }
1654
- if (loopAt >= 0) {
1655
- return { text: text.slice(0, loopAt), toolCalls: [], usage, finishReason: "repetition" };
1658
+ const iterator = res.body[Symbol.asyncIterator]();
1659
+ const onAbort = () => void iterator.return?.();
1660
+ if (signal?.aborted) onAbort();
1661
+ else signal?.addEventListener("abort", onAbort);
1662
+ try {
1663
+ for (; ; ) {
1664
+ const step = await iterator.next();
1665
+ if (step.done) break;
1666
+ buffer += decoder.decode(step.value, { stream: true });
1667
+ let newline = buffer.indexOf("\n");
1668
+ while (newline >= 0 && loopAt < 0) {
1669
+ handle(buffer.slice(0, newline).trim());
1670
+ buffer = buffer.slice(newline + 1);
1671
+ newline = buffer.indexOf("\n");
1672
+ }
1673
+ if (loopAt >= 0) {
1674
+ return { text: text.slice(0, loopAt), toolCalls: [], usage, finishReason: "repetition" };
1675
+ }
1656
1676
  }
1677
+ } finally {
1678
+ signal?.removeEventListener("abort", onAbort);
1679
+ void iterator.return?.();
1657
1680
  }
1658
1681
  handle(buffer.trim());
1659
1682
  return {
@@ -2097,7 +2120,7 @@ function createProvider(options) {
2097
2120
  );
2098
2121
  if ((res2.headers.get("content-type") ?? "").includes("application/json")) return readResponsesJson(res2, onThinking);
2099
2122
  if (!res2.body) throw new ProviderError(t("Empty response from the provider"));
2100
- return readResponsesStream(res2, onText, onThinking);
2123
+ return readResponsesStream(res2, onText, onThinking, signal);
2101
2124
  }
2102
2125
  const res = await post(
2103
2126
  "/chat/completions",
@@ -2379,60 +2402,39 @@ function darwinProfile(cwd, network) {
2379
2402
  network ? "(allow network*)" : "(deny network*)"
2380
2403
  ].join("\n");
2381
2404
  }
2405
+ function bwrapArgv(cwd, network, command) {
2406
+ return [
2407
+ "bwrap",
2408
+ "--ro-bind",
2409
+ "/",
2410
+ "/",
2411
+ "--dev-bind",
2412
+ "/dev",
2413
+ "/dev",
2414
+ "--proc",
2415
+ "/proc",
2416
+ "--bind",
2417
+ cwd,
2418
+ cwd,
2419
+ "--tmpfs",
2420
+ "/tmp",
2421
+ "--die-with-parent",
2422
+ ...network ? [] : ["--unshare-net"],
2423
+ "--",
2424
+ "/bin/bash",
2425
+ "-c",
2426
+ command
2427
+ ];
2428
+ }
2382
2429
  function sandboxArgv(command, opts) {
2383
2430
  if (opts.platform === "darwin") {
2384
2431
  return ["sandbox-exec", "-p", darwinProfile(opts.cwd, opts.network), "/bin/bash", "-c", command];
2385
2432
  }
2386
2433
  if (opts.platform === "linux") {
2387
- return [
2388
- "bwrap",
2389
- "--ro-bind",
2390
- "/",
2391
- "/",
2392
- "--dev-bind",
2393
- "/dev",
2394
- "/dev",
2395
- "--proc",
2396
- "/proc",
2397
- "--bind",
2398
- opts.cwd,
2399
- opts.cwd,
2400
- "--tmpfs",
2401
- "/tmp",
2402
- "--die-with-parent",
2403
- ...opts.network ? [] : ["--unshare-net"],
2404
- "--",
2405
- "/bin/bash",
2406
- "-c",
2407
- command
2408
- ];
2434
+ return bwrapArgv(opts.cwd, opts.network, command);
2409
2435
  }
2410
2436
  if (opts.platform === "win32" && (opts.wslBwrap || process.env.BCCLI_WIN32_WSL_SANDBOX === "1")) {
2411
- const wslCwd = winPathToWsl(opts.cwd);
2412
- return [
2413
- "wsl.exe",
2414
- "--",
2415
- "bwrap",
2416
- "--ro-bind",
2417
- "/",
2418
- "/",
2419
- "--dev-bind",
2420
- "/dev",
2421
- "/dev",
2422
- "--proc",
2423
- "/proc",
2424
- "--bind",
2425
- wslCwd,
2426
- wslCwd,
2427
- "--tmpfs",
2428
- "/tmp",
2429
- "--die-with-parent",
2430
- ...opts.network ? [] : ["--unshare-net"],
2431
- "--",
2432
- "/bin/bash",
2433
- "-c",
2434
- command
2435
- ];
2437
+ return ["wsl.exe", "--", ...bwrapArgv(winPathToWsl(opts.cwd), opts.network, command)];
2436
2438
  }
2437
2439
  return void 0;
2438
2440
  }
@@ -2472,7 +2474,7 @@ function killAllCommands() {
2472
2474
  }
2473
2475
  var backgrounds = /* @__PURE__ */ new Map();
2474
2476
  var nextBackgroundId = 1;
2475
- function startBackground(command, cwd, env, shell) {
2477
+ function startBackground(command, cwd, env, shell, maxRetained) {
2476
2478
  const child = spawnChild(command, cwd, env, shell);
2477
2479
  const task = {
2478
2480
  id: nextBackgroundId++,
@@ -2493,6 +2495,10 @@ function startBackground(command, cwd, env, shell) {
2493
2495
  if (child.pid !== void 0) killProcessTree(child.pid);
2494
2496
  };
2495
2497
  backgrounds.set(task.id, task);
2498
+ for (const [id, other] of backgrounds) {
2499
+ if (backgrounds.size <= maxRetained) break;
2500
+ if (id !== task.id && other.status === "exited") backgrounds.delete(id);
2501
+ }
2496
2502
  const onData = (data) => {
2497
2503
  let s = data;
2498
2504
  if (task.head.length < HALF) {
@@ -2691,6 +2697,7 @@ var SHELL_NOTE = {
2691
2697
  function createBashTool(options = {}) {
2692
2698
  const offline = options.networkPolicy === "offline";
2693
2699
  const sandbox = options.sandbox === true;
2700
+ const maxRetained = options.maxRetainedTasks ?? 50;
2694
2701
  const platform = options.platform ?? process.platform;
2695
2702
  const shell = resolveShell({ platform, env: process.env, exists: existsSync3, ...options.shellDeps });
2696
2703
  const childEnv = offline ? offlineEnv() : void 0;
@@ -2737,7 +2744,7 @@ Network access is off (networkPolicy "offline"): internet commands such as curl,
2737
2744
  }
2738
2745
  if (input.background) {
2739
2746
  if (!input.command) return { output: 'The "command" argument is required to start a background task.', isError: true };
2740
- const task = startBackground(exec, ctx.cwd, childEnv, shell);
2747
+ const task = startBackground(exec, ctx.cwd, childEnv, shell, maxRetained);
2741
2748
  return {
2742
2749
  output: `Background task #${task.id} started: ${input.command}
2743
2750
  Poll it with {task_id: ${task.id}}; stop it with {task_id: ${task.id}, kill: true}.`
@@ -3607,6 +3614,12 @@ async function readTextResult(input, buffer) {
3607
3614
  const lines = buffer.toString("utf8").split(/\r?\n/);
3608
3615
  const start = (input.offset ?? 1) - 1;
3609
3616
  const limit = input.limit ?? 2e3;
3617
+ if (start >= lines.length) {
3618
+ return {
3619
+ output: `offset ${input.offset} is past the end of ${input.path} (${lines.length} line(s)); start at 1.`,
3620
+ isError: true
3621
+ };
3622
+ }
3610
3623
  const requested = lines.slice(start, start + limit);
3611
3624
  const shown = [];
3612
3625
  let size = 0;
@@ -3688,7 +3701,9 @@ var writeTool = defineTool({
3688
3701
  target: (input) => input.path,
3689
3702
  async validate(input, ctx) {
3690
3703
  const abs = resolvePath(ctx.cwd, input.path);
3691
- return existsSync4(abs) && !ctx.readFiles.has(abs) ? `${input.path} already exists. Read it with read before overwriting it.` : void 0;
3704
+ if (existsSync4(abs) && !ctx.readFiles.has(abs)) return `${input.path} already exists. Read it with read before overwriting it.`;
3705
+ if (isFileModifiedSinceRead(ctx.readFiles, abs)) return `${input.path} has been modified since it was last read. Read it again before overwriting it.`;
3706
+ return void 0;
3692
3707
  },
3693
3708
  async preview(input, ctx) {
3694
3709
  const abs = resolvePath(ctx.cwd, input.path);
@@ -3700,6 +3715,9 @@ var writeTool = defineTool({
3700
3715
  if (existsSync4(abs) && !ctx.readFiles.has(abs)) {
3701
3716
  return { output: `${input.path} already exists. Read it with read before overwriting it.`, isError: true };
3702
3717
  }
3718
+ if (isFileModifiedSinceRead(ctx.readFiles, abs)) {
3719
+ return { output: `${input.path} has been modified since it was last read. Read it again before overwriting it.`, isError: true };
3720
+ }
3703
3721
  try {
3704
3722
  await ctx.checkpoint?.(abs);
3705
3723
  await mkdir(dirname4(abs), { recursive: true });
@@ -3809,7 +3827,9 @@ function budgetStatus(usage, cap, model) {
3809
3827
  if (cap.usd !== void 0) {
3810
3828
  const price = priceFor(cap, model);
3811
3829
  if (price) {
3812
- const usd = (usage.inputTokens * price.input + usage.outputTokens * price.output) / 1e6;
3830
+ const cached = Math.max(0, Math.min(usage.cachedInputTokens ?? 0, usage.inputTokens));
3831
+ const cachedRate = price.cachedInput ?? price.input;
3832
+ const usd = ((usage.inputTokens - cached) * price.input + cached * cachedRate + usage.outputTokens * price.output) / 1e6;
3813
3833
  if (usd >= cap.usd) return { kind: "usd", used: usd, limit: cap.usd };
3814
3834
  }
3815
3835
  }
@@ -4999,7 +5019,7 @@ async function addProviderKey(providerId, key, deps) {
4999
5019
  // src/session.ts
5000
5020
  import { randomBytes as randomBytes2 } from "crypto";
5001
5021
  import { appendFileSync, existsSync as existsSync10, mkdirSync as mkdirSync4, readdirSync as readdirSync3, readFileSync as readFileSync7, rmSync as rmSync2, statSync as statSync7 } from "fs";
5002
- import { basename as basename3, join as join10 } from "path";
5022
+ import { basename as basename3, dirname as dirname8, join as join10 } from "path";
5003
5023
  function projectDir(home, cwd) {
5004
5024
  const slug = cwd.replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(-100) || "root";
5005
5025
  return join10(home, "sessions", slug);
@@ -5025,7 +5045,6 @@ var Session = class _Session {
5025
5045
  }
5026
5046
  static create(home, cwd, now = /* @__PURE__ */ new Date()) {
5027
5047
  const dir = projectDir(home, cwd);
5028
- mkdirSync4(dir, { recursive: true });
5029
5048
  const stamp = now.toISOString().replace(/[:.]/g, "-");
5030
5049
  return new _Session(join10(dir, `${stamp}-${randomBytes2(3).toString("hex")}.jsonl`));
5031
5050
  }
@@ -5042,6 +5061,7 @@ var Session = class _Session {
5042
5061
  return _Session.list(home, cwd)[0]?.session;
5043
5062
  }
5044
5063
  write(record) {
5064
+ mkdirSync4(dirname8(this.file), { recursive: true });
5045
5065
  appendFileSync(this.file, `${JSON.stringify(record)}
5046
5066
  `);
5047
5067
  }
@@ -6074,6 +6094,15 @@ function loadPlugins(specs, opts) {
6074
6094
 
6075
6095
  // src/setup.ts
6076
6096
  var TOOL_RULES = { bash: "bash", edit: "edit", write: "edit", fetch: "fetch" };
6097
+ var pluginCache = /* @__PURE__ */ new Map();
6098
+ function loadPluginsOnce(specs, opts) {
6099
+ const key = JSON.stringify({ specs, home: opts.home, cwd: opts.cwd });
6100
+ const cached = pluginCache.get(key);
6101
+ if (cached) return cached;
6102
+ const loaded = loadPlugins(specs, opts);
6103
+ pluginCache.set(key, loaded);
6104
+ return loaded;
6105
+ }
6077
6106
  function lunaPtcAutoEnabled(env) {
6078
6107
  const value = String(env.BCCLI_LUNA_PTC ?? "").trim().toLowerCase();
6079
6108
  return !["0", "false", "off", "no"].includes(value);
@@ -6081,6 +6110,7 @@ function lunaPtcAutoEnabled(env) {
6081
6110
  function createRuntime(opts) {
6082
6111
  const env = opts.env ?? process.env;
6083
6112
  const home = bccliHome(env);
6113
+ const persistSession = opts.persistSession !== false;
6084
6114
  let config = loadConfig(opts.cwd, env);
6085
6115
  let modelRef = opts.args.model ?? config.model;
6086
6116
  let reasoning = opts.args.reasoning ?? config.reasoning;
@@ -6169,7 +6199,7 @@ function createRuntime(opts) {
6169
6199
  createRepoMapTool()
6170
6200
  ];
6171
6201
  if (config.lsp?.servers.length) baseTools.push(createDiagnosticsTool(config.lsp.servers));
6172
- const plugins = loadPlugins(config.plugins ?? [], {
6202
+ const plugins = loadPluginsOnce(config.plugins ?? [], {
6173
6203
  home,
6174
6204
  cwd: opts.cwd,
6175
6205
  env,
@@ -6207,9 +6237,15 @@ function createRuntime(opts) {
6207
6237
  usageCap: config.usageCap,
6208
6238
  budgetModel: modelRef,
6209
6239
  contextWindow: contextWindowFor(modelRef, env),
6210
- onMessage: (m) => session.append(m),
6211
- onUsage: (u) => session.appendUsage({ ...u, model: modelRef }),
6212
- onReset: () => session.reset(),
6240
+ onMessage: (m) => {
6241
+ if (persistSession) session.append(m);
6242
+ },
6243
+ onUsage: (u) => {
6244
+ if (persistSession) session.appendUsage({ ...u, model: modelRef });
6245
+ },
6246
+ onReset: () => {
6247
+ if (persistSession) session.reset();
6248
+ },
6213
6249
  onTurnStart: () => {
6214
6250
  for (let i = turnMarks.length - 1; i >= 0 && turnMarks[i] > agent.messages.length; i--) {
6215
6251
  turnMarks.splice(i, 1);
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  createRuntime
4
- } from "./chunk-5QCCV2JM.js";
4
+ } from "./chunk-ZJRZTMJF.js";
5
5
  import "./chunk-OK2USAXX.js";
6
- import "./chunk-3PUWH62K.js";
7
- import "./chunk-4WI5ON64.js";
6
+ import "./chunk-KDVL2NP6.js";
7
+ import "./chunk-7GM6NN5E.js";
8
8
  import "./chunk-PSO7R5AD.js";
9
9
  import {
10
10
  VERSION
11
- } from "./chunk-CBD52BYV.js";
11
+ } from "./chunk-OFMZ6CE6.js";
12
12
  import {
13
13
  LOOPBACK_BIND,
14
14
  MAX_FRAME_BYTES,
@@ -17,25 +17,25 @@ import {
17
17
  parseClientFrame,
18
18
  randomToken,
19
19
  trustFence
20
- } from "./chunk-CIEPLTGS.js";
21
- import "./chunk-WFZKJY3R.js";
22
- import "./chunk-PGDYPOQW.js";
20
+ } from "./chunk-ISOKH5WB.js";
21
+ import "./chunk-YYTFJUO2.js";
22
+ import "./chunk-SWNRA3EW.js";
23
23
  import {
24
24
  listAllModels
25
- } from "./chunk-SXRQ2Q7A.js";
26
- import "./chunk-MPYSYTSO.js";
27
- import "./chunk-67REFMSY.js";
25
+ } from "./chunk-INMSTUB2.js";
26
+ import "./chunk-6SDRWEPN.js";
27
+ import "./chunk-3CYQYHPK.js";
28
28
  import {
29
29
  Session
30
- } from "./chunk-24KDXQZT.js";
31
- import "./chunk-VXRW5SYP.js";
32
- import "./chunk-6RVAPAE5.js";
33
- import "./chunk-7HDBPDAD.js";
30
+ } from "./chunk-KEKDRROL.js";
31
+ import "./chunk-6JK4IBGK.js";
32
+ import "./chunk-7XD47KN5.js";
33
+ import "./chunk-Q42ZGVAQ.js";
34
34
  import {
35
35
  bccliHome,
36
36
  loadConfig
37
- } from "./chunk-USFXL5BS.js";
38
- import "./chunk-ED5TTYQU.js";
37
+ } from "./chunk-TLCL6DC7.js";
38
+ import "./chunk-R754XIBJ.js";
39
39
 
40
40
  // src/serve.ts
41
41
  import { randomBytes } from "crypto";
@@ -3,15 +3,15 @@ import {
3
3
  createFetchTool,
4
4
  createWebSearchTool,
5
5
  isPrivateHost
6
- } from "./chunk-4WI5ON64.js";
6
+ } from "./chunk-7GM6NN5E.js";
7
7
  import "./chunk-PSO7R5AD.js";
8
8
  import {
9
9
  loadConfig,
10
10
  readCredentials
11
- } from "./chunk-USFXL5BS.js";
11
+ } from "./chunk-TLCL6DC7.js";
12
12
  import {
13
13
  t
14
- } from "./chunk-ED5TTYQU.js";
14
+ } from "./chunk-R754XIBJ.js";
15
15
 
16
16
  // src/mcpServer/serve.ts
17
17
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ Session,
4
+ pruneSessions
5
+ } from "./chunk-KEKDRROL.js";
6
+ import "./chunk-6JK4IBGK.js";
7
+ import "./chunk-7XD47KN5.js";
8
+ import "./chunk-R754XIBJ.js";
9
+ export {
10
+ Session,
11
+ pruneSessions
12
+ };
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ createRuntime,
4
+ lunaPtcAutoEnabled
5
+ } from "./chunk-ZJRZTMJF.js";
6
+ import "./chunk-OK2USAXX.js";
7
+ import "./chunk-KDVL2NP6.js";
8
+ import "./chunk-7GM6NN5E.js";
9
+ import "./chunk-PSO7R5AD.js";
10
+ import "./chunk-YYTFJUO2.js";
11
+ import "./chunk-SWNRA3EW.js";
12
+ import "./chunk-INMSTUB2.js";
13
+ import "./chunk-6SDRWEPN.js";
14
+ import "./chunk-3CYQYHPK.js";
15
+ import "./chunk-KEKDRROL.js";
16
+ import "./chunk-6JK4IBGK.js";
17
+ import "./chunk-7XD47KN5.js";
18
+ import "./chunk-Q42ZGVAQ.js";
19
+ import "./chunk-TLCL6DC7.js";
20
+ import "./chunk-R754XIBJ.js";
21
+ export {
22
+ createRuntime,
23
+ lunaPtcAutoEnabled
24
+ };
package/dist/setup.d.ts CHANGED
@@ -71,4 +71,9 @@ export declare function createRuntime(opts: {
71
71
  userHome?: string;
72
72
  /** Override langkah agent (mis. githubBot.stepLimit) — menang atas config.maxSteps. */
73
73
  maxSteps?: number | null;
74
+ /**
75
+ * Write the conversation to ~/.bccli/sessions (default true). One-shot hosts such as
76
+ * `gh-bot` turn it off: a session log per webhook comment is never resumed.
77
+ */
78
+ persistSession?: boolean;
74
79
  }): Runtime;
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-OK2USAXX.js";
5
5
  import {
6
6
  t
7
- } from "./chunk-ED5TTYQU.js";
7
+ } from "./chunk-R754XIBJ.js";
8
8
 
9
9
  // src/statsCli.ts
10
10
  import { existsSync, readdirSync, readFileSync, statSync } from "fs";
@@ -124,7 +124,7 @@ function aggregateSessions(home, opts) {
124
124
  row.usageOutputTokens += usageOut;
125
125
  row.cachedInputTokens += cachedIn;
126
126
  row.cacheWriteTokens += cacheWrite;
127
- row.sessionTotals.push(usageIn + usageOut + cachedIn + cacheWrite);
127
+ row.sessionTotals.push(usageIn + usageOut);
128
128
  } else {
129
129
  row.inputTokens += estIn;
130
130
  row.outputTokens += estOut;
@@ -34,6 +34,8 @@ export interface BashToolOptions {
34
34
  platform?: NodeJS.Platform;
35
35
  /** Test hook: fake environment / file lookup used to pick the Windows shell. */
36
36
  shellDeps?: Partial<ShellDeps>;
37
+ /** How many finished background tasks are kept for polling (default 50). */
38
+ maxRetainedTasks?: number;
37
39
  }
38
40
  export declare function createBashTool(options?: BashToolOptions): import("./types").Tool<z.ZodObject<{
39
41
  command: z.ZodOptional<z.ZodString>;
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  Session
4
- } from "./chunk-24KDXQZT.js";
5
- import "./chunk-VXRW5SYP.js";
6
- import "./chunk-6RVAPAE5.js";
4
+ } from "./chunk-KEKDRROL.js";
5
+ import "./chunk-6JK4IBGK.js";
6
+ import "./chunk-7XD47KN5.js";
7
7
  import {
8
8
  t
9
- } from "./chunk-ED5TTYQU.js";
9
+ } from "./chunk-R754XIBJ.js";
10
10
 
11
11
  // src/transferCli.ts
12
12
  import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "fs";
@@ -2,14 +2,14 @@
2
2
  import {
3
3
  CATALOG,
4
4
  fillTemplate
5
- } from "./chunk-6N2BBLIT.js";
5
+ } from "./chunk-JUC3ZOCV.js";
6
6
  import {
7
7
  MAX_VERIFY_ROUNDS,
8
8
  runVerify,
9
9
  toolLabel,
10
10
  verifyFollowup,
11
11
  worktreeListText
12
- } from "./chunk-3K66L37W.js";
12
+ } from "./chunk-S3QEZDKB.js";
13
13
  import {
14
14
  BUILTIN_AGENTS,
15
15
  appendMemory,
@@ -24,49 +24,49 @@ import {
24
24
  runCommand,
25
25
  runHooks,
26
26
  validateGlobPattern
27
- } from "./chunk-3PUWH62K.js";
27
+ } from "./chunk-KDVL2NP6.js";
28
28
  import "./chunk-PSO7R5AD.js";
29
29
  import {
30
30
  nextMode,
31
31
  requiresSensitiveToolApproval
32
- } from "./chunk-WFZKJY3R.js";
32
+ } from "./chunk-YYTFJUO2.js";
33
33
  import {
34
34
  hasKey,
35
35
  providerName,
36
36
  writeGlobalConfig
37
- } from "./chunk-MPYSYTSO.js";
37
+ } from "./chunk-6SDRWEPN.js";
38
38
  import {
39
39
  Session
40
- } from "./chunk-24KDXQZT.js";
40
+ } from "./chunk-KEKDRROL.js";
41
41
  import {
42
42
  contentText
43
- } from "./chunk-VXRW5SYP.js";
43
+ } from "./chunk-6JK4IBGK.js";
44
44
  import {
45
45
  REASONING_LEVELS,
46
46
  assertReasoningSupported,
47
47
  parseReasoningLevel,
48
48
  supportedReasoningLevels
49
- } from "./chunk-6RVAPAE5.js";
49
+ } from "./chunk-7XD47KN5.js";
50
50
  import {
51
51
  addGlobalServer,
52
52
  globalMcpPath,
53
53
  readMcpFile,
54
54
  removeGlobalServer
55
- } from "./chunk-7HDBPDAD.js";
55
+ } from "./chunk-Q42ZGVAQ.js";
56
56
  import {
57
57
  ACCENT,
58
58
  PRESETS,
59
59
  color,
60
60
  matchKeybind,
61
61
  removeCredential
62
- } from "./chunk-USFXL5BS.js";
62
+ } from "./chunk-TLCL6DC7.js";
63
63
  import {
64
64
  getLanguage,
65
65
  locale,
66
66
  parseLang,
67
67
  setLanguage,
68
68
  t
69
- } from "./chunk-ED5TTYQU.js";
69
+ } from "./chunk-R754XIBJ.js";
70
70
 
71
71
  // src/ui/index.tsx
72
72
  import { render } from "ink";
@@ -2805,7 +2805,7 @@ ${allLines.slice(-maxLiveLines).join("\n")}` : text;
2805
2805
  const activeSpinnerSummary = activeToolEntry ? `${toolLabel(activeToolEntry.tool)}${activeTarget ? ` ${activeTarget.length > 20 ? `${activeTarget.slice(0, 20)}\u2026` : activeTarget}` : ""}` : "";
2806
2806
  const activeToolSummary = activeToolEntry ? `${toolLabel(activeToolEntry.tool)}${activeTarget ? ` ${activeTarget.length > 68 ? `${activeTarget.slice(0, 68)}\u2026` : activeTarget}` : ""}` : "";
2807
2807
  return /* @__PURE__ */ jsxs12(Box13, { ref: liveRootRef, flexDirection: "column", children: [
2808
- /* @__PURE__ */ jsx16(Static, { items: staticDone, children: renderEntry }, staticKey),
2808
+ /* @__PURE__ */ jsx16(Static, { items: staticDone, children: renderEntry }, `static-${staticKey}`),
2809
2809
  activeTurnDone.map(renderEntry),
2810
2810
  transcript.live.map(renderEntry),
2811
2811
  busy && !pending && !planAsk ? /* @__PURE__ */ jsxs12(
@@ -2887,7 +2887,7 @@ ${allLines.slice(-maxLiveLines).join("\n")}` : text;
2887
2887
  import { jsx as jsx17 } from "react/jsx-runtime";
2888
2888
  async function startInteractive(rt, opts) {
2889
2889
  if (opts.resume) {
2890
- const { Session: Session2 } = await import("./session-VAPIHHUG.js");
2890
+ const { Session: Session2 } = await import("./session-LX4UNPCD.js");
2891
2891
  const list = Session2.list(rt.home, rt.cwd).slice(0, 20);
2892
2892
  if (!list.length) console.log(t("No sessions in this folder yet; starting a new one."));
2893
2893
  else {
@@ -2903,7 +2903,7 @@ async function startInteractive(rt, opts) {
2903
2903
  if (chosen) rt.resume(chosen.session);
2904
2904
  }
2905
2905
  }
2906
- const { serversToStart, setProjectTrust } = await import("./config-JLOH5M6B.js");
2906
+ const { serversToStart, setProjectTrust } = await import("./config-YYEAEN2C.js");
2907
2907
  const plan = serversToStart(rt.home, rt.cwd);
2908
2908
  if (plan.needTrust.length) {
2909
2909
  const { createInterface } = await import("readline/promises");