@botconnector/bccli 0.4.10 → 0.4.11

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-IPHWC6CO.js} +20 -5
  2. package/dist/acp.d.ts +17 -1
  3. package/dist/args-K5AJFDCZ.js +13 -0
  4. package/dist/budget.d.ts +4 -1
  5. package/dist/{chunk-7HDBPDAD.js → chunk-2DFY43RP.js} +2 -2
  6. package/dist/{chunk-5QCCV2JM.js → chunk-6BXW7DGQ.js} +47 -18
  7. package/dist/{chunk-4WI5ON64.js → chunk-6KZIOICR.js} +1 -1
  8. package/dist/{chunk-3K66L37W.js → chunk-6M5T7N7A.js} +3 -3
  9. package/dist/{chunk-USFXL5BS.js → chunk-C2FUGQJX.js} +6 -6
  10. package/dist/{chunk-24KDXQZT.js → chunk-F54FDJBD.js} +3 -3
  11. package/dist/{chunk-ED5TTYQU.js → chunk-G5TG43R4.js} +3 -0
  12. package/dist/{chunk-67REFMSY.js → chunk-IHQGXDSG.js} +1 -1
  13. package/dist/{chunk-PGDYPOQW.js → chunk-JFYQCPXL.js} +4 -4
  14. package/dist/{chunk-ZWXKBFKW.js → chunk-MBAMDCCV.js} +23 -9
  15. package/dist/{chunk-CBD52BYV.js → chunk-N6APAYSF.js} +1 -1
  16. package/dist/{chunk-6N2BBLIT.js → chunk-NT2G5FSX.js} +2 -2
  17. package/dist/{chunk-3PUWH62K.js → chunk-OAXD3X4R.js} +34 -50
  18. package/dist/{chunk-VXRW5SYP.js → chunk-OOOS5RCT.js} +25 -14
  19. package/dist/{chunk-CIEPLTGS.js → chunk-WSZF2CTU.js} +4 -4
  20. package/dist/{chunk-MPYSYTSO.js → chunk-X24APPXO.js} +1 -1
  21. package/dist/{chunk-6RVAPAE5.js → chunk-XZ2TI5HV.js} +1 -1
  22. package/dist/{chunk-SXRQ2Q7A.js → chunk-Y6AZGGH7.js} +4 -4
  23. package/dist/{chunk-WFZKJY3R.js → chunk-YYTFJUO2.js} +3 -2
  24. package/dist/cli.js +39 -39
  25. package/dist/{completion-GEFRIQWV.js → completion-QVDGAYUY.js} +1 -1
  26. package/dist/{config-JLOH5M6B.js → config-7TQ2KVVX.js} +3 -3
  27. package/dist/{config-JKMYKNAT.js → config-RZY3XQZJ.js} +2 -2
  28. package/dist/{ghBotCli-WEVS6S6O.js → ghBotCli-HHJ4ANTH.js} +18 -16
  29. package/dist/{integrationCli-PKMICTHC.js → integrationCli-LNNZDGZX.js} +40 -5
  30. package/dist/{login-2TZRODUL.js → login-MQEZGF2C.js} +6 -4
  31. package/dist/login.d.ts +2 -0
  32. package/dist/{mcpCli-TAPKYGHO.js → mcpCli-CUIOYJ2A.js} +6 -6
  33. package/dist/{modelsCli-6PK7V2QZ.js → modelsCli-AAFHFI4H.js} +6 -6
  34. package/dist/{oauthLogin-UI34LQZU.js → oauthLogin-YFL2N4QA.js} +1 -1
  35. package/dist/{pluginsCli-RCF33U5J.js → pluginsCli-2QXXFLQA.js} +2 -2
  36. package/dist/{prCli-24PMW7NS.js → prCli-KSA73RCS.js} +1 -1
  37. package/dist/providerCli-TXMVPSW5.js +15 -0
  38. package/dist/sdk.js +103 -72
  39. package/dist/{serve-XNJI67DU.js → serve-EIQHEUYD.js} +3 -3
  40. package/dist/{serve-7BFCIEPJ.js → serve-QYDBFTHC.js} +16 -16
  41. package/dist/session-CQCQ7KAP.js +12 -0
  42. package/dist/setup-DBSGMWHM.js +24 -0
  43. package/dist/setup.d.ts +5 -0
  44. package/dist/{statsCli-QDHE4TKB.js → statsCli-EHNJIQ2Z.js} +2 -2
  45. package/dist/tools/bash.d.ts +2 -0
  46. package/dist/{transferCli-7OKKYGG2.js → transferCli-IXJ2O74Q.js} +4 -4
  47. package/dist/{ui-SZAIIJC5.js → ui-A2LASWDK.js} +14 -14
  48. package/dist/{update-JSNVVXPS.js → update-TR62JV3A.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
@@ -1,15 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  defaultLoginDeps,
4
+ echoFor,
4
5
  readLine,
5
6
  readSecret,
6
7
  runLogin
7
- } from "./chunk-ZWXKBFKW.js";
8
- import "./chunk-67REFMSY.js";
9
- import "./chunk-USFXL5BS.js";
10
- import "./chunk-ED5TTYQU.js";
8
+ } from "./chunk-MBAMDCCV.js";
9
+ import "./chunk-IHQGXDSG.js";
10
+ import "./chunk-C2FUGQJX.js";
11
+ import "./chunk-G5TG43R4.js";
11
12
  export {
12
13
  defaultLoginDeps,
14
+ echoFor,
13
15
  readLine,
14
16
  readSecret,
15
17
  runLogin
package/dist/login.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  export declare function readSecret(prompt: string): Promise<string>;
2
+ /** What readLine writes back to the terminal: printable input is visible, control keys are not. */
3
+ export declare function echoFor(ch: string): string | undefined;
2
4
  /** Prompt TANPA masking (kode otorisasi harus terlihat saat di-paste). Ctrl+C -> exit 130. */
3
5
  export declare function readLine(prompt: string): Promise<string>;
4
6
  import type { CliArgs } from './args';
@@ -2,30 +2,30 @@
2
2
  import {
3
3
  CATALOG,
4
4
  fillTemplate
5
- } from "./chunk-6N2BBLIT.js";
5
+ } from "./chunk-NT2G5FSX.js";
6
6
  import {
7
7
  authorizeMcpServer,
8
8
  clearTokens
9
- } from "./chunk-67REFMSY.js";
9
+ } from "./chunk-IHQGXDSG.js";
10
10
  import {
11
11
  addGlobalServer,
12
12
  globalMcpPath,
13
13
  readMcpFile,
14
14
  removeGlobalServer
15
- } from "./chunk-7HDBPDAD.js";
15
+ } from "./chunk-2DFY43RP.js";
16
16
  import {
17
17
  bccliHome
18
- } from "./chunk-USFXL5BS.js";
18
+ } from "./chunk-C2FUGQJX.js";
19
19
  import {
20
20
  t
21
- } from "./chunk-ED5TTYQU.js";
21
+ } from "./chunk-G5TG43R4.js";
22
22
 
23
23
  // src/mcpCli.ts
24
24
  async function runMcpCommand(args, deps) {
25
25
  const home = bccliHome(deps.env);
26
26
  const [action = "list", name] = args.subArgs;
27
27
  if (action === "serve") {
28
- const { runMcpServe } = await import("./serve-XNJI67DU.js");
28
+ const { runMcpServe } = await import("./serve-EIQHEUYD.js");
29
29
  return runMcpServe(args, deps);
30
30
  }
31
31
  const installed = readMcpFile(globalMcpPath(home));
@@ -1,17 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  listAllModels
4
- } from "./chunk-SXRQ2Q7A.js";
5
- import "./chunk-MPYSYTSO.js";
6
- import "./chunk-VXRW5SYP.js";
7
- import "./chunk-6RVAPAE5.js";
4
+ } from "./chunk-Y6AZGGH7.js";
5
+ import "./chunk-X24APPXO.js";
6
+ import "./chunk-OOOS5RCT.js";
7
+ import "./chunk-XZ2TI5HV.js";
8
8
  import {
9
9
  loadConfig,
10
10
  resolveModel
11
- } from "./chunk-USFXL5BS.js";
11
+ } from "./chunk-C2FUGQJX.js";
12
12
  import {
13
13
  t
14
- } from "./chunk-ED5TTYQU.js";
14
+ } from "./chunk-G5TG43R4.js";
15
15
 
16
16
  // src/modelsCli.ts
17
17
  async function runModelsCommand(args, deps) {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  t
4
- } from "./chunk-ED5TTYQU.js";
4
+ } from "./chunk-G5TG43R4.js";
5
5
 
6
6
  // src/oauthLogin.ts
7
7
  import { createHash, randomBytes } from "crypto";
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  bccliHome,
4
4
  globalConfigPath
5
- } from "./chunk-USFXL5BS.js";
5
+ } from "./chunk-C2FUGQJX.js";
6
6
  import {
7
7
  t
8
- } from "./chunk-ED5TTYQU.js";
8
+ } from "./chunk-G5TG43R4.js";
9
9
 
10
10
  // src/pluginsCli.ts
11
11
  import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "fs";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  t
4
- } from "./chunk-ED5TTYQU.js";
4
+ } from "./chunk-G5TG43R4.js";
5
5
 
6
6
  // src/prCli.ts
7
7
  import { spawn } from "child_process";
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ addProviderKey,
4
+ runProviderCommand
5
+ } from "./chunk-JFYQCPXL.js";
6
+ import "./chunk-Y6AZGGH7.js";
7
+ import "./chunk-X24APPXO.js";
8
+ import "./chunk-OOOS5RCT.js";
9
+ import "./chunk-XZ2TI5HV.js";
10
+ import "./chunk-C2FUGQJX.js";
11
+ import "./chunk-G5TG43R4.js";
12
+ export {
13
+ addProviderKey,
14
+ runProviderCommand
15
+ };
package/dist/sdk.js CHANGED
@@ -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.",
@@ -412,6 +413,8 @@ Opsi:
412
413
  "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
414
  "{target}: not managed by BCCLI yet.": "{target}: belum dikelola oleh BCCLI.",
414
415
  "{target}: the BotConnector integration was removed and the config restored.": "{target}: integrasi BotConnector dilepas dan config dipulihkan.",
416
+ '{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.',
417
+ "{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
418
  "Custom commands:": "Perintah custom:",
416
419
  "Skills:": "Skill:",
417
420
  "shift+tab switch permission mode esc cancel the turn": "shift+tab ganti mode izin esc batalkan giliran",
@@ -617,6 +620,9 @@ function resolvedUsageCap(global) {
617
620
  if (!price || !nonNegative(price.input) || !nonNegative(price.output)) {
618
621
  throw new ConfigError(t('usageCap.prices["{key}"] must have numeric input and output (USD per 1M tokens).', { key }));
619
622
  }
623
+ if (price.cachedInput !== void 0 && !nonNegative(price.cachedInput)) {
624
+ throw new ConfigError(t('usageCap.prices["{key}"].cachedInput must be a number >= 0 (USD per 1M tokens).', { key }));
625
+ }
620
626
  }
621
627
  }
622
628
  if (tokens2 === void 0 && usd === void 0) throw new ConfigError(t("usageCap needs at least one of: tokens, usd."));
@@ -704,7 +710,7 @@ function loadConfig(cwd, env = process.env) {
704
710
  const project = readJsonConfig(join(cwd, ".bccli", "config.json"));
705
711
  const known = { ...DEFAULT_CONFIG.providers, ...global.providers };
706
712
  const fromProject = untrustedProviders(project, known);
707
- const rawFallbacks = project.fallbackModels ?? global.fallbackModels;
713
+ const rawFallbacks = global.fallbackModels;
708
714
  const fallbackModels = Array.isArray(rawFallbacks) ? rawFallbacks.filter((m) => typeof m === "string" && m.includes("/")) : void 0;
709
715
  return {
710
716
  model: project.model ?? global.model ?? DEFAULT_CONFIG.model,
@@ -884,7 +890,7 @@ var Permissions = class {
884
890
  }
885
891
  /** Rules that "[a] allow for this session" would add; undefined when it must always be asked. */
886
892
  rulesFor(req) {
887
- if (requiresSensitiveToolApproval(req) && (req.kind === "read" || process.env.BC_TOOL_POLICY_ENFORCE === "true"))
893
+ if (requiresSensitiveToolApproval(req) && (req.kind === "read" || req.kind === "edit" || process.env.BC_TOOL_POLICY_ENFORCE === "true"))
888
894
  return void 0;
889
895
  if (req.kind === "edit") return this.editInProject(req.target) ? ["edit(project)"] : void 0;
890
896
  if (req.kind === "fetch") {
@@ -906,7 +912,8 @@ var Permissions = class {
906
912
  }
907
913
  check(req) {
908
914
  if (this.mode === "plan" && req.kind !== "read") return "deny";
909
- if (req.kind === "read" && this.mode !== "allowAll" && requiresSensitiveToolApproval(req)) return "ask";
915
+ if ((req.kind === "read" || req.kind === "edit") && this.mode !== "allowAll" && requiresSensitiveToolApproval(req))
916
+ return "ask";
910
917
  if (process.env.BC_TOOL_POLICY_ENFORCE === "true" && requiresSensitiveToolApproval(req)) return "ask";
911
918
  if (req.kind === "read") return "allow";
912
919
  if (this.mode === "allowAll") return "allow";
@@ -1541,7 +1548,7 @@ function hasPendingProgrammaticReplay(messages) {
1541
1548
  }
1542
1549
  return pending;
1543
1550
  }
1544
- async function readResponsesStream(res, onText, onThinking) {
1551
+ async function readResponsesStream(res, onText, onThinking, signal) {
1545
1552
  let text = "";
1546
1553
  let thinking = "";
1547
1554
  let usage;
@@ -1643,17 +1650,28 @@ async function readResponsesStream(res, onText, onThinking) {
1643
1650
  );
1644
1651
  }
1645
1652
  };
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" };
1653
+ const iterator = res.body[Symbol.asyncIterator]();
1654
+ const onAbort = () => void iterator.return?.();
1655
+ if (signal?.aborted) onAbort();
1656
+ else signal?.addEventListener("abort", onAbort);
1657
+ try {
1658
+ for (; ; ) {
1659
+ const step = await iterator.next();
1660
+ if (step.done) break;
1661
+ buffer += decoder.decode(step.value, { stream: true });
1662
+ let newline = buffer.indexOf("\n");
1663
+ while (newline >= 0 && loopAt < 0) {
1664
+ handle(buffer.slice(0, newline).trim());
1665
+ buffer = buffer.slice(newline + 1);
1666
+ newline = buffer.indexOf("\n");
1667
+ }
1668
+ if (loopAt >= 0) {
1669
+ return { text: text.slice(0, loopAt), toolCalls: [], usage, finishReason: "repetition" };
1670
+ }
1656
1671
  }
1672
+ } finally {
1673
+ signal?.removeEventListener("abort", onAbort);
1674
+ void iterator.return?.();
1657
1675
  }
1658
1676
  handle(buffer.trim());
1659
1677
  return {
@@ -2097,7 +2115,7 @@ function createProvider(options) {
2097
2115
  );
2098
2116
  if ((res2.headers.get("content-type") ?? "").includes("application/json")) return readResponsesJson(res2, onThinking);
2099
2117
  if (!res2.body) throw new ProviderError(t("Empty response from the provider"));
2100
- return readResponsesStream(res2, onText, onThinking);
2118
+ return readResponsesStream(res2, onText, onThinking, signal);
2101
2119
  }
2102
2120
  const res = await post(
2103
2121
  "/chat/completions",
@@ -2379,60 +2397,39 @@ function darwinProfile(cwd, network) {
2379
2397
  network ? "(allow network*)" : "(deny network*)"
2380
2398
  ].join("\n");
2381
2399
  }
2400
+ function bwrapArgv(cwd, network, command) {
2401
+ return [
2402
+ "bwrap",
2403
+ "--ro-bind",
2404
+ "/",
2405
+ "/",
2406
+ "--dev-bind",
2407
+ "/dev",
2408
+ "/dev",
2409
+ "--proc",
2410
+ "/proc",
2411
+ "--bind",
2412
+ cwd,
2413
+ cwd,
2414
+ "--tmpfs",
2415
+ "/tmp",
2416
+ "--die-with-parent",
2417
+ ...network ? [] : ["--unshare-net"],
2418
+ "--",
2419
+ "/bin/bash",
2420
+ "-c",
2421
+ command
2422
+ ];
2423
+ }
2382
2424
  function sandboxArgv(command, opts) {
2383
2425
  if (opts.platform === "darwin") {
2384
2426
  return ["sandbox-exec", "-p", darwinProfile(opts.cwd, opts.network), "/bin/bash", "-c", command];
2385
2427
  }
2386
2428
  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
- ];
2429
+ return bwrapArgv(opts.cwd, opts.network, command);
2409
2430
  }
2410
2431
  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
- ];
2432
+ return ["wsl.exe", "--", ...bwrapArgv(winPathToWsl(opts.cwd), opts.network, command)];
2436
2433
  }
2437
2434
  return void 0;
2438
2435
  }
@@ -2472,7 +2469,7 @@ function killAllCommands() {
2472
2469
  }
2473
2470
  var backgrounds = /* @__PURE__ */ new Map();
2474
2471
  var nextBackgroundId = 1;
2475
- function startBackground(command, cwd, env, shell) {
2472
+ function startBackground(command, cwd, env, shell, maxRetained) {
2476
2473
  const child = spawnChild(command, cwd, env, shell);
2477
2474
  const task = {
2478
2475
  id: nextBackgroundId++,
@@ -2493,6 +2490,10 @@ function startBackground(command, cwd, env, shell) {
2493
2490
  if (child.pid !== void 0) killProcessTree(child.pid);
2494
2491
  };
2495
2492
  backgrounds.set(task.id, task);
2493
+ for (const [id, other] of backgrounds) {
2494
+ if (backgrounds.size <= maxRetained) break;
2495
+ if (id !== task.id && other.status === "exited") backgrounds.delete(id);
2496
+ }
2496
2497
  const onData = (data) => {
2497
2498
  let s = data;
2498
2499
  if (task.head.length < HALF) {
@@ -2691,6 +2692,7 @@ var SHELL_NOTE = {
2691
2692
  function createBashTool(options = {}) {
2692
2693
  const offline = options.networkPolicy === "offline";
2693
2694
  const sandbox = options.sandbox === true;
2695
+ const maxRetained = options.maxRetainedTasks ?? 50;
2694
2696
  const platform = options.platform ?? process.platform;
2695
2697
  const shell = resolveShell({ platform, env: process.env, exists: existsSync3, ...options.shellDeps });
2696
2698
  const childEnv = offline ? offlineEnv() : void 0;
@@ -2737,7 +2739,7 @@ Network access is off (networkPolicy "offline"): internet commands such as curl,
2737
2739
  }
2738
2740
  if (input.background) {
2739
2741
  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);
2742
+ const task = startBackground(exec, ctx.cwd, childEnv, shell, maxRetained);
2741
2743
  return {
2742
2744
  output: `Background task #${task.id} started: ${input.command}
2743
2745
  Poll it with {task_id: ${task.id}}; stop it with {task_id: ${task.id}, kill: true}.`
@@ -3607,6 +3609,12 @@ async function readTextResult(input, buffer) {
3607
3609
  const lines = buffer.toString("utf8").split(/\r?\n/);
3608
3610
  const start = (input.offset ?? 1) - 1;
3609
3611
  const limit = input.limit ?? 2e3;
3612
+ if (start >= lines.length) {
3613
+ return {
3614
+ output: `offset ${input.offset} is past the end of ${input.path} (${lines.length} line(s)); start at 1.`,
3615
+ isError: true
3616
+ };
3617
+ }
3610
3618
  const requested = lines.slice(start, start + limit);
3611
3619
  const shown = [];
3612
3620
  let size = 0;
@@ -3688,7 +3696,9 @@ var writeTool = defineTool({
3688
3696
  target: (input) => input.path,
3689
3697
  async validate(input, ctx) {
3690
3698
  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;
3699
+ if (existsSync4(abs) && !ctx.readFiles.has(abs)) return `${input.path} already exists. Read it with read before overwriting it.`;
3700
+ if (isFileModifiedSinceRead(ctx.readFiles, abs)) return `${input.path} has been modified since it was last read. Read it again before overwriting it.`;
3701
+ return void 0;
3692
3702
  },
3693
3703
  async preview(input, ctx) {
3694
3704
  const abs = resolvePath(ctx.cwd, input.path);
@@ -3700,6 +3710,9 @@ var writeTool = defineTool({
3700
3710
  if (existsSync4(abs) && !ctx.readFiles.has(abs)) {
3701
3711
  return { output: `${input.path} already exists. Read it with read before overwriting it.`, isError: true };
3702
3712
  }
3713
+ if (isFileModifiedSinceRead(ctx.readFiles, abs)) {
3714
+ return { output: `${input.path} has been modified since it was last read. Read it again before overwriting it.`, isError: true };
3715
+ }
3703
3716
  try {
3704
3717
  await ctx.checkpoint?.(abs);
3705
3718
  await mkdir(dirname4(abs), { recursive: true });
@@ -3809,7 +3822,9 @@ function budgetStatus(usage, cap, model) {
3809
3822
  if (cap.usd !== void 0) {
3810
3823
  const price = priceFor(cap, model);
3811
3824
  if (price) {
3812
- const usd = (usage.inputTokens * price.input + usage.outputTokens * price.output) / 1e6;
3825
+ const cached = Math.max(0, Math.min(usage.cachedInputTokens ?? 0, usage.inputTokens));
3826
+ const cachedRate = price.cachedInput ?? price.input;
3827
+ const usd = ((usage.inputTokens - cached) * price.input + cached * cachedRate + usage.outputTokens * price.output) / 1e6;
3813
3828
  if (usd >= cap.usd) return { kind: "usd", used: usd, limit: cap.usd };
3814
3829
  }
3815
3830
  }
@@ -4999,7 +5014,7 @@ async function addProviderKey(providerId, key, deps) {
4999
5014
  // src/session.ts
5000
5015
  import { randomBytes as randomBytes2 } from "crypto";
5001
5016
  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";
5017
+ import { basename as basename3, dirname as dirname8, join as join10 } from "path";
5003
5018
  function projectDir(home, cwd) {
5004
5019
  const slug = cwd.replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(-100) || "root";
5005
5020
  return join10(home, "sessions", slug);
@@ -5025,7 +5040,6 @@ var Session = class _Session {
5025
5040
  }
5026
5041
  static create(home, cwd, now = /* @__PURE__ */ new Date()) {
5027
5042
  const dir = projectDir(home, cwd);
5028
- mkdirSync4(dir, { recursive: true });
5029
5043
  const stamp = now.toISOString().replace(/[:.]/g, "-");
5030
5044
  return new _Session(join10(dir, `${stamp}-${randomBytes2(3).toString("hex")}.jsonl`));
5031
5045
  }
@@ -5042,6 +5056,7 @@ var Session = class _Session {
5042
5056
  return _Session.list(home, cwd)[0]?.session;
5043
5057
  }
5044
5058
  write(record) {
5059
+ mkdirSync4(dirname8(this.file), { recursive: true });
5045
5060
  appendFileSync(this.file, `${JSON.stringify(record)}
5046
5061
  `);
5047
5062
  }
@@ -6074,6 +6089,15 @@ function loadPlugins(specs, opts) {
6074
6089
 
6075
6090
  // src/setup.ts
6076
6091
  var TOOL_RULES = { bash: "bash", edit: "edit", write: "edit", fetch: "fetch" };
6092
+ var pluginCache = /* @__PURE__ */ new Map();
6093
+ function loadPluginsOnce(specs, opts) {
6094
+ const key = JSON.stringify({ specs, home: opts.home, cwd: opts.cwd });
6095
+ const cached = pluginCache.get(key);
6096
+ if (cached) return cached;
6097
+ const loaded = loadPlugins(specs, opts);
6098
+ pluginCache.set(key, loaded);
6099
+ return loaded;
6100
+ }
6077
6101
  function lunaPtcAutoEnabled(env) {
6078
6102
  const value = String(env.BCCLI_LUNA_PTC ?? "").trim().toLowerCase();
6079
6103
  return !["0", "false", "off", "no"].includes(value);
@@ -6081,6 +6105,7 @@ function lunaPtcAutoEnabled(env) {
6081
6105
  function createRuntime(opts) {
6082
6106
  const env = opts.env ?? process.env;
6083
6107
  const home = bccliHome(env);
6108
+ const persistSession = opts.persistSession !== false;
6084
6109
  let config = loadConfig(opts.cwd, env);
6085
6110
  let modelRef = opts.args.model ?? config.model;
6086
6111
  let reasoning = opts.args.reasoning ?? config.reasoning;
@@ -6169,7 +6194,7 @@ function createRuntime(opts) {
6169
6194
  createRepoMapTool()
6170
6195
  ];
6171
6196
  if (config.lsp?.servers.length) baseTools.push(createDiagnosticsTool(config.lsp.servers));
6172
- const plugins = loadPlugins(config.plugins ?? [], {
6197
+ const plugins = loadPluginsOnce(config.plugins ?? [], {
6173
6198
  home,
6174
6199
  cwd: opts.cwd,
6175
6200
  env,
@@ -6207,9 +6232,15 @@ function createRuntime(opts) {
6207
6232
  usageCap: config.usageCap,
6208
6233
  budgetModel: modelRef,
6209
6234
  contextWindow: contextWindowFor(modelRef, env),
6210
- onMessage: (m) => session.append(m),
6211
- onUsage: (u) => session.appendUsage({ ...u, model: modelRef }),
6212
- onReset: () => session.reset(),
6235
+ onMessage: (m) => {
6236
+ if (persistSession) session.append(m);
6237
+ },
6238
+ onUsage: (u) => {
6239
+ if (persistSession) session.appendUsage({ ...u, model: modelRef });
6240
+ },
6241
+ onReset: () => {
6242
+ if (persistSession) session.reset();
6243
+ },
6213
6244
  onTurnStart: () => {
6214
6245
  for (let i = turnMarks.length - 1; i >= 0 && turnMarks[i] > agent.messages.length; i--) {
6215
6246
  turnMarks.splice(i, 1);
@@ -3,15 +3,15 @@ import {
3
3
  createFetchTool,
4
4
  createWebSearchTool,
5
5
  isPrivateHost
6
- } from "./chunk-4WI5ON64.js";
6
+ } from "./chunk-6KZIOICR.js";
7
7
  import "./chunk-PSO7R5AD.js";
8
8
  import {
9
9
  loadConfig,
10
10
  readCredentials
11
- } from "./chunk-USFXL5BS.js";
11
+ } from "./chunk-C2FUGQJX.js";
12
12
  import {
13
13
  t
14
- } from "./chunk-ED5TTYQU.js";
14
+ } from "./chunk-G5TG43R4.js";
15
15
 
16
16
  // src/mcpServer/serve.ts
17
17
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  createRuntime
4
- } from "./chunk-5QCCV2JM.js";
4
+ } from "./chunk-6BXW7DGQ.js";
5
5
  import "./chunk-OK2USAXX.js";
6
- import "./chunk-3PUWH62K.js";
7
- import "./chunk-4WI5ON64.js";
6
+ import "./chunk-OAXD3X4R.js";
7
+ import "./chunk-6KZIOICR.js";
8
8
  import "./chunk-PSO7R5AD.js";
9
9
  import {
10
10
  VERSION
11
- } from "./chunk-CBD52BYV.js";
11
+ } from "./chunk-N6APAYSF.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-WSZF2CTU.js";
21
+ import "./chunk-YYTFJUO2.js";
22
+ import "./chunk-JFYQCPXL.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-Y6AZGGH7.js";
26
+ import "./chunk-X24APPXO.js";
27
+ import "./chunk-IHQGXDSG.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-F54FDJBD.js";
31
+ import "./chunk-OOOS5RCT.js";
32
+ import "./chunk-XZ2TI5HV.js";
33
+ import "./chunk-2DFY43RP.js";
34
34
  import {
35
35
  bccliHome,
36
36
  loadConfig
37
- } from "./chunk-USFXL5BS.js";
38
- import "./chunk-ED5TTYQU.js";
37
+ } from "./chunk-C2FUGQJX.js";
38
+ import "./chunk-G5TG43R4.js";
39
39
 
40
40
  // src/serve.ts
41
41
  import { randomBytes } from "crypto";
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ Session,
4
+ pruneSessions
5
+ } from "./chunk-F54FDJBD.js";
6
+ import "./chunk-OOOS5RCT.js";
7
+ import "./chunk-XZ2TI5HV.js";
8
+ import "./chunk-G5TG43R4.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-6BXW7DGQ.js";
6
+ import "./chunk-OK2USAXX.js";
7
+ import "./chunk-OAXD3X4R.js";
8
+ import "./chunk-6KZIOICR.js";
9
+ import "./chunk-PSO7R5AD.js";
10
+ import "./chunk-YYTFJUO2.js";
11
+ import "./chunk-JFYQCPXL.js";
12
+ import "./chunk-Y6AZGGH7.js";
13
+ import "./chunk-X24APPXO.js";
14
+ import "./chunk-IHQGXDSG.js";
15
+ import "./chunk-F54FDJBD.js";
16
+ import "./chunk-OOOS5RCT.js";
17
+ import "./chunk-XZ2TI5HV.js";
18
+ import "./chunk-2DFY43RP.js";
19
+ import "./chunk-C2FUGQJX.js";
20
+ import "./chunk-G5TG43R4.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-G5TG43R4.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>;