@botconnector/bccli 0.4.16 → 0.4.17

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 (50) hide show
  1. package/dist/{acp-JZW3XHE5.js → acp-HWHF25KU.js} +3 -3
  2. package/dist/agent.d.ts +11 -0
  3. package/dist/{args-XVO7TB4C.js → args-XA77XUMD.js} +4 -4
  4. package/dist/{chunk-INMSTUB2.js → chunk-2IP5F3EK.js} +4 -4
  5. package/dist/{chunk-S3QEZDKB.js → chunk-5CVHTYBQ.js} +3 -3
  6. package/dist/{chunk-KEKDRROL.js → chunk-6LO6YRDY.js} +1 -1
  7. package/dist/{chunk-JUC3ZOCV.js → chunk-6PJRWKVI.js} +2 -2
  8. package/dist/{chunk-KJIXA6MP.js → chunk-7OTFZK4B.js} +7 -7
  9. package/dist/{chunk-KDVL2NP6.js → chunk-7U7CMKKI.js} +1 -1
  10. package/dist/{chunk-ZJRZTMJF.js → chunk-A3NBWIEQ.js} +52 -15
  11. package/dist/{chunk-6SDRWEPN.js → chunk-FY543EQW.js} +1 -1
  12. package/dist/{chunk-R754XIBJ.js → chunk-IDZLHEVL.js} +2 -1
  13. package/dist/{chunk-7GM6NN5E.js → chunk-IF5RNXBI.js} +1 -1
  14. package/dist/{chunk-ISOKH5WB.js → chunk-M5T4AM4I.js} +3 -3
  15. package/dist/{chunk-7XD47KN5.js → chunk-NGFWBVHD.js} +1 -1
  16. package/dist/{chunk-6JK4IBGK.js → chunk-OCEQ2TRJ.js} +2 -2
  17. package/dist/{chunk-SWNRA3EW.js → chunk-RLSQAZBT.js} +4 -4
  18. package/dist/{chunk-Q42ZGVAQ.js → chunk-VPFCIUC6.js} +2 -2
  19. package/dist/{chunk-V5D6PJQN.js → chunk-XP5MTJN2.js} +1 -1
  20. package/dist/{chunk-3CYQYHPK.js → chunk-YGBTNDPK.js} +1 -1
  21. package/dist/{chunk-TLCL6DC7.js → chunk-ZXWRLN7E.js} +1 -1
  22. package/dist/cli.js +38 -38
  23. package/dist/{completion-Q7VHOPPV.js → completion-IRVEVW6H.js} +1 -1
  24. package/dist/{config-YYEAEN2C.js → config-OVWCUVY7.js} +3 -3
  25. package/dist/{config-T7MKGJVG.js → config-S3CZUIBB.js} +2 -2
  26. package/dist/{ghBotCli-7VUGI63S.js → ghBotCli-FYKJDJOD.js} +14 -14
  27. package/dist/{integrationCli-FBRSTF5F.js → integrationCli-GY73S2K7.js} +2 -2
  28. package/dist/{login-BSMUGW7L.js → login-KSN63EM4.js} +4 -4
  29. package/dist/{mcpCli-ZT7CRYMP.js → mcpCli-VWSCCDWT.js} +6 -6
  30. package/dist/{modelsCli-2ZRW4CC4.js → modelsCli-4Q6KFWV2.js} +6 -6
  31. package/dist/{oauthLogin-THSM6FE7.js → oauthLogin-JWXQZTMZ.js} +1 -1
  32. package/dist/{pluginsCli-EKQI4CJW.js → pluginsCli-7XWDLJEL.js} +2 -2
  33. package/dist/{prCli-FMQTOBIV.js → prCli-FH7N26D4.js} +1 -1
  34. package/dist/providerCli-YMG4G56R.js +15 -0
  35. package/dist/sdk.js +43 -5
  36. package/dist/{serve-V6YVZTNR.js → serve-HSFF6MGB.js} +3 -3
  37. package/dist/{serve-3BODPEPR.js → serve-HUQNVOSP.js} +15 -15
  38. package/dist/session-YM36TGU7.js +12 -0
  39. package/dist/setup-3YB6EKYX.js +24 -0
  40. package/dist/{statsCli-7MIQHLVW.js → statsCli-I4TSDU3X.js} +1 -1
  41. package/dist/{transferCli-SRSYQLSJ.js → transferCli-3B4LMOYI.js} +4 -4
  42. package/dist/ui/StatusBar.d.ts +6 -1
  43. package/dist/ui/liveCap.d.ts +21 -1
  44. package/dist/ui/markdownLayout.d.ts +23 -0
  45. package/dist/{ui-LZ4JGF2V.js → ui-TAZMRXRI.js} +187 -56
  46. package/dist/{update-P7MU4ZM7.js → update-5V425FIK.js} +2 -2
  47. package/package.json +1 -1
  48. package/dist/providerCli-3TFEKV4C.js +0 -15
  49. package/dist/session-LX4UNPCD.js +0 -12
  50. package/dist/setup-MFG3BWEU.js +0 -24
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-OK2USAXX.js";
5
5
  import {
6
6
  t
7
- } from "./chunk-R754XIBJ.js";
7
+ } from "./chunk-IDZLHEVL.js";
8
8
 
9
9
  // src/statsCli.ts
10
10
  import { existsSync, readdirSync, readFileSync, statSync } from "fs";
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  Session
4
- } from "./chunk-KEKDRROL.js";
5
- import "./chunk-6JK4IBGK.js";
6
- import "./chunk-7XD47KN5.js";
4
+ } from "./chunk-6LO6YRDY.js";
5
+ import "./chunk-OCEQ2TRJ.js";
6
+ import "./chunk-NGFWBVHD.js";
7
7
  import {
8
8
  t
9
- } from "./chunk-R754XIBJ.js";
9
+ } from "./chunk-IDZLHEVL.js";
10
10
 
11
11
  // src/transferCli.ts
12
12
  import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "fs";
@@ -1,8 +1,13 @@
1
1
  import type { PermissionMode } from '../config';
2
2
  import type { ReasoningLevel } from '../reasoning';
3
- export declare function StatusBar({ mode, tokens, busy, model, reasoning }: {
3
+ export declare function StatusBar({ mode, tokens, cached, estimated, busy, model, reasoning, }: {
4
4
  mode: PermissionMode;
5
+ /** Prompt tokens of the last model call: the size of the context, not the session total. */
5
6
  tokens: number;
7
+ /** Cache reads for the latest provider-reported model call. */
8
+ cached?: number;
9
+ /** Local chars/4 fallback rather than provider-reported usage. */
10
+ estimated?: boolean;
6
11
  busy: boolean;
7
12
  model?: string;
8
13
  reasoning?: ReasoningLevel;
@@ -1,9 +1,29 @@
1
+ /**
2
+ * Splits a frame budget between the live entries, newest first, so the entry being streamed is never the one dropped.
3
+ * A per-entry cap is not enough: an open thinking block and a streaming answer each fitting the budget still add up
4
+ * to a frame twice as tall as the terminal, and Ink rewrites (and duplicates) such a frame on every render.
5
+ */
6
+ export declare function liveRowCaps(wanted: number[], budget: number): number[];
7
+ /**
8
+ * Plain-text twin of `capLiveMarkdown`: keeps the tail that fits `maxRows` terminal rows and announces the rest.
9
+ * Used for thinking and tool output, which are drawn as plain wrapped text rather than markdown.
10
+ */
11
+ export declare function capLiveText(raw: string, options: {
12
+ isRealTty: boolean;
13
+ maxRows: number;
14
+ width: number;
15
+ }): string;
1
16
  /**
2
17
  * Live assistant text is drawn inside the Ink frame, so on a real terminal it is capped to the rows left after the
3
18
  * prompt and status bar; the hidden part is announced instead of silently dropped. Piped stdout has no rows to
4
19
  * respect (`bccli | tee log`, CI harnesses), so the text is kept whole there.
20
+ *
21
+ * The budget is in terminal rows, not source lines: a 40-line answer at 100 columns is 120 rows, and an Ink frame
22
+ * taller than the terminal makes every render rewrite the whole screen, which stamps duplicate copies of the same
23
+ * block into the scrollback.
5
24
  */
6
25
  export declare function capLiveMarkdown(raw: string, options: {
7
26
  isRealTty: boolean;
8
- maxLines: number;
27
+ maxRows: number;
28
+ width: number;
9
29
  }): string;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The line shapes `Markdown` draws. They live here so the renderer and the row counter below share one parse order:
3
+ * a drift between the two is what makes a live frame taller than the terminal believes.
4
+ */
5
+ export declare const HEADING: RegExp;
6
+ export declare const RULE: RegExp;
7
+ export declare const QUOTE: RegExp;
8
+ export declare const BULLET: RegExp;
9
+ export declare const NUMBERED: RegExp;
10
+ export declare const BULLETS: string[];
11
+ /** Marker width for a list item indented by `spaces`, matching `bulletFor` in Markdown.tsx. */
12
+ export declare const bulletFor: (spaces: number) => string;
13
+ /** Text rows a single unwrapped line occupies at `width` columns; Ink wraps on spaces, so this is the safe bound. */
14
+ export declare function wrappedRows(text: string, width: number): number;
15
+ /**
16
+ * Rows `Markdown` will draw for `text` at `width`. It mirrors the render loop in Markdown.tsx line for line —
17
+ * `markdownLayout.test.ts` renders real markdown and compares, so the two cannot drift unnoticed.
18
+ *
19
+ * A live assistant message has to be sized in the rows it really occupies, not in the lines its source has: at
20
+ * 100 columns a single paragraph is three rows, and a live frame that is taller than the terminal makes Ink rewrite
21
+ * the whole screen on every render, which stamps copies of the same answer into the scrollback.
22
+ */
23
+ export declare function markdownRowCount(text: string, width: number): number;
@@ -2,14 +2,14 @@
2
2
  import {
3
3
  CATALOG,
4
4
  fillTemplate
5
- } from "./chunk-JUC3ZOCV.js";
5
+ } from "./chunk-6PJRWKVI.js";
6
6
  import {
7
7
  MAX_VERIFY_ROUNDS,
8
8
  runVerify,
9
9
  toolLabel,
10
10
  verifyFollowup,
11
11
  worktreeListText
12
- } from "./chunk-S3QEZDKB.js";
12
+ } from "./chunk-5CVHTYBQ.js";
13
13
  import {
14
14
  BUILTIN_AGENTS,
15
15
  appendMemory,
@@ -24,7 +24,7 @@ import {
24
24
  runCommand,
25
25
  runHooks,
26
26
  validateGlobPattern
27
- } from "./chunk-KDVL2NP6.js";
27
+ } from "./chunk-7U7CMKKI.js";
28
28
  import "./chunk-PSO7R5AD.js";
29
29
  import {
30
30
  nextMode,
@@ -34,39 +34,39 @@ import {
34
34
  hasKey,
35
35
  providerName,
36
36
  writeGlobalConfig
37
- } from "./chunk-6SDRWEPN.js";
37
+ } from "./chunk-FY543EQW.js";
38
38
  import {
39
39
  Session
40
- } from "./chunk-KEKDRROL.js";
40
+ } from "./chunk-6LO6YRDY.js";
41
41
  import {
42
42
  contentText
43
- } from "./chunk-6JK4IBGK.js";
43
+ } from "./chunk-OCEQ2TRJ.js";
44
44
  import {
45
45
  REASONING_LEVELS,
46
46
  assertReasoningSupported,
47
47
  parseReasoningLevel,
48
48
  supportedReasoningLevels
49
- } from "./chunk-7XD47KN5.js";
49
+ } from "./chunk-NGFWBVHD.js";
50
50
  import {
51
51
  addGlobalServer,
52
52
  globalMcpPath,
53
53
  readMcpFile,
54
54
  removeGlobalServer
55
- } from "./chunk-Q42ZGVAQ.js";
55
+ } from "./chunk-VPFCIUC6.js";
56
56
  import {
57
57
  ACCENT,
58
58
  PRESETS,
59
59
  color,
60
60
  matchKeybind,
61
61
  removeCredential
62
- } from "./chunk-TLCL6DC7.js";
62
+ } from "./chunk-ZXWRLN7E.js";
63
63
  import {
64
64
  getLanguage,
65
65
  locale,
66
66
  parseLang,
67
67
  setLanguage,
68
68
  t
69
- } from "./chunk-R754XIBJ.js";
69
+ } from "./chunk-IDZLHEVL.js";
70
70
 
71
71
  // src/ui/index.tsx
72
72
  import { render } from "ink";
@@ -538,15 +538,85 @@ function layoutTable(table, width) {
538
538
  const rule = [dim(widths.map((w) => "\u2500".repeat(w)).join(RULE_JOIN))];
539
539
  const head = renderRow(table.header, true);
540
540
  const body = table.rows.map((r) => renderRow(r, false));
541
- const wrappedRows = body.some((b) => b.height > 1);
541
+ const wrappedRows2 = body.some((b) => b.height > 1);
542
542
  const out = [...head.lines, rule];
543
543
  body.forEach((b, i) => {
544
- if (wrappedRows && i > 0) out.push(rule);
544
+ if (wrappedRows2 && i > 0) out.push(rule);
545
545
  out.push(...b.lines);
546
546
  });
547
547
  return out;
548
548
  }
549
549
 
550
+ // src/ui/markdownLayout.ts
551
+ var HEADING = /^\s{0,3}(#{1,6})\s+(.*?)\s*#*\s*$/;
552
+ var RULE = /^\s{0,3}([-*_])(?:\s*\1){2,}\s*$/;
553
+ var QUOTE = /^\s{0,3}((?:>\s?)+)(.*)$/;
554
+ var BULLET = /^(\s*)[-*+]\s+(.*)$/;
555
+ var NUMBERED = /^(\s*)(\d{1,9})[.)]\s+(.*)$/;
556
+ var BULLETS = ["\u2022", "\u25E6", "\u25AA"];
557
+ var bulletFor = (spaces) => BULLETS[spaces === 0 ? 0 : spaces <= 4 ? 1 : 2];
558
+ function wrappedRows(text, width) {
559
+ const usable = Math.max(1, width);
560
+ const lines = text.split("\n");
561
+ return lines.reduce((sum, line) => sum + Math.max(1, Math.ceil(displayWidth(line) / usable)), 0);
562
+ }
563
+ var plain = (line) => parseInline(line).map((seg) => seg.text).join("");
564
+ var hangingRows = (marker, body, width, padding = 0) => {
565
+ const textWidth = Math.max(10, width - padding - displayWidth(marker) - 1);
566
+ return wrappedRows(plain(body), textWidth);
567
+ };
568
+ function markdownRowCount(text, width) {
569
+ const lines = text.split("\n");
570
+ let rows = 0;
571
+ let inCode = false;
572
+ for (let i = 0; i < lines.length; i++) {
573
+ const line = lines[i];
574
+ if (line.trimStart().startsWith("```")) {
575
+ inCode = !inCode;
576
+ continue;
577
+ }
578
+ if (inCode) {
579
+ rows += wrappedRows(` ${line}`, width);
580
+ continue;
581
+ }
582
+ const table = parseTable(lines, i);
583
+ if (table) {
584
+ rows += layoutTable(table.table, width).length;
585
+ i = table.end - 1;
586
+ continue;
587
+ }
588
+ const heading = HEADING.exec(line);
589
+ if (heading) {
590
+ rows += wrappedRows(plain(heading[2]), width);
591
+ continue;
592
+ }
593
+ if (RULE.test(line)) {
594
+ rows += 1;
595
+ continue;
596
+ }
597
+ const quote = QUOTE.exec(line);
598
+ if (quote) {
599
+ rows += hangingRows("\u258E".repeat(quote[1].replace(/\s/g, "").length), quote[2], width);
600
+ continue;
601
+ }
602
+ const bullet = BULLET.exec(line);
603
+ if (bullet) {
604
+ const padding = Math.min(bullet[1].replace(/\t/g, " ").length, 16);
605
+ const task = /^\[([ xX])\]\s+(.*)$/.exec(bullet[2]);
606
+ rows += task ? hangingRows(task[1] === " " ? "\u2610" : "\u2611", task[2], width, padding) : hangingRows(bulletFor(padding), bullet[2], width, padding);
607
+ continue;
608
+ }
609
+ const numbered = NUMBERED.exec(line);
610
+ if (numbered) {
611
+ const padding = Math.min(numbered[1].replace(/\t/g, " ").length, 16);
612
+ rows += hangingRows(`${numbered[2]}.`, numbered[3], width, padding);
613
+ continue;
614
+ }
615
+ rows += line ? wrappedRows(plain(line), width) : 1;
616
+ }
617
+ return rows;
618
+ }
619
+
550
620
  // src/ui/tty.ts
551
621
  function isTtyStream(stream) {
552
622
  return typeof stream === "object" && stream !== null && stream.isTTY === true;
@@ -584,13 +654,6 @@ function segment(seg, key) {
584
654
  }
585
655
  }
586
656
  var inline = (line) => parseInline(line).map(segment);
587
- var HEADING = /^\s{0,3}(#{1,6})\s+(.*?)\s*#*\s*$/;
588
- var RULE = /^\s{0,3}([-*_])(?:\s*\1){2,}\s*$/;
589
- var QUOTE = /^\s{0,3}((?:>\s?)+)(.*)$/;
590
- var BULLET = /^(\s*)[-*+]\s+(.*)$/;
591
- var NUMBERED = /^(\s*)(\d{1,9})[.)]\s+(.*)$/;
592
- var BULLETS = ["\u2022", "\u25E6", "\u25AA"];
593
- var bulletFor = (spaces) => BULLETS[spaces === 0 ? 0 : spaces <= 4 ? 1 : 2];
594
657
  function hanging(key, marker, body, width, padding = 0) {
595
658
  const textWidth = Math.max(10, width - padding - displayWidth(marker) - 1);
596
659
  return /* @__PURE__ */ jsxs(Box, { paddingLeft: padding, children: [
@@ -1456,23 +1519,63 @@ var LABEL = {
1456
1519
  function formatTokens(n) {
1457
1520
  return n >= 1e3 ? `${Math.round(n / 1e3)}k` : String(n);
1458
1521
  }
1459
- function StatusBar({ mode, tokens, busy, model, reasoning = "auto" }) {
1522
+ function StatusBar({
1523
+ mode,
1524
+ tokens,
1525
+ cached = 0,
1526
+ estimated = false,
1527
+ busy,
1528
+ model,
1529
+ reasoning = "auto"
1530
+ }) {
1460
1531
  const danger = mode === "allowAll";
1461
- return /* @__PURE__ */ jsx11(Text11, { color: danger ? color("red") : void 0, dimColor: !danger, children: ` ${LABEL[mode]}${model ? ` \xB7 ${model}` : ""} \xB7 reasoning: ${reasoning} \xB7 ${t("shift+tab switch mode")} \xB7 ${t("{n} tokens", { n: formatTokens(tokens) })} \xB7 ${busy ? t("esc to cancel") : t("/ for commands")}` });
1532
+ const cacheText = cached > 0 ? ` \xB7 ${t("{n} cached", { n: formatTokens(cached) })}` : "";
1533
+ return /* @__PURE__ */ jsx11(Text11, { color: danger ? color("red") : void 0, dimColor: !danger, children: ` ${LABEL[mode]}${model ? ` \xB7 ${model}` : ""} \xB7 reasoning: ${reasoning} \xB7 ${t("shift+tab switch mode")} \xB7 ${estimated ? "~" : ""}${t("{n} tokens in context", { n: formatTokens(tokens) })}${cacheText} \xB7 ${busy ? t("esc to cancel") : t("/ for commands")}` });
1462
1534
  }
1463
1535
 
1464
1536
  // src/ui/liveCap.ts
1537
+ function liveRowCaps(wanted, budget) {
1538
+ const caps = new Array(wanted.length).fill(0);
1539
+ let room = Math.max(0, budget);
1540
+ for (let i = wanted.length - 1; i >= 0; i--) {
1541
+ const take = Math.max(0, Math.min(wanted[i], room));
1542
+ caps[i] = take;
1543
+ room -= take;
1544
+ }
1545
+ return caps;
1546
+ }
1547
+ function capLiveText(raw, options) {
1548
+ if (!options.isRealTty) return raw;
1549
+ if (options.maxRows <= 0) return "";
1550
+ if (wrappedRows(raw, options.width) <= options.maxRows) return raw;
1551
+ const lines = raw.split("\n");
1552
+ const room = Math.max(1, options.maxRows - 1);
1553
+ let kept = 0;
1554
+ while (kept < lines.length && wrappedRows(lines.slice(lines.length - kept - 1).join("\n"), options.width) <= room) kept++;
1555
+ const hidden = lines.length - kept;
1556
+ return `\u2026 (${hidden} lines above) \u2026
1557
+ ${lines.slice(hidden).join("\n")}`;
1558
+ }
1465
1559
  function capLiveMarkdown(raw, options) {
1466
1560
  if (!options.isRealTty) return raw;
1561
+ if (options.maxRows <= 0) return "";
1562
+ if (markdownRowCount(raw, options.width) <= options.maxRows) return raw;
1467
1563
  const lines = raw.split("\n");
1468
- if (lines.length <= options.maxLines) return raw;
1469
- const hidden = lines.length - options.maxLines;
1470
- const headFence = lines.slice(0, hidden).find((line) => line.trimStart().startsWith("```"));
1471
- const prefix = headFence ? `${headFence}
1472
- \u2026 (${hidden} lines above) \u2026
1473
- ` : `\u2026 (${hidden} lines above) \u2026
1474
- `;
1475
- return `${prefix}${lines.slice(-options.maxLines).join("\n")}`;
1564
+ const room = Math.max(1, options.maxRows - 1);
1565
+ let lo = 0;
1566
+ let hi = lines.length;
1567
+ while (lo < hi) {
1568
+ const mid = Math.floor((lo + hi) / 2);
1569
+ if (markdownRowCount(lines.slice(mid).join("\n"), options.width) <= room) hi = mid;
1570
+ else lo = mid + 1;
1571
+ }
1572
+ if (lo >= lines.length) return `\u2026 (${lines.length} lines above) \u2026`;
1573
+ const dropped = lines.slice(0, lo);
1574
+ const fences = dropped.filter((line) => line.trimStart().startsWith("```"));
1575
+ const opener = fences.length % 2 === 1 ? fences.at(-1) : void 0;
1576
+ return `${opener ? `${opener}
1577
+ ` : ""}\u2026 (${lo} lines above) \u2026
1578
+ ${lines.slice(lo).join("\n")}`;
1476
1579
  }
1477
1580
 
1478
1581
  // src/ui/McpMenu.tsx
@@ -1792,7 +1895,9 @@ function App({ runtime, initialPrompt, version, updateMessage }) {
1792
1895
  const [startedAt, setStartedAt] = useState10(0);
1793
1896
  const [mode, setMode] = useState10(runtime.agent.permissions.mode);
1794
1897
  const [reasoning, setReasoning] = useState10(runtime.reasoning);
1795
- const [tokens, setTokens] = useState10(runtime.agent.totalUsage.inputTokens + runtime.agent.totalUsage.outputTokens);
1898
+ const [tokens, setTokens] = useState10(runtime.agent.lastInputTokens);
1899
+ const [cachedTokens, setCachedTokens] = useState10(runtime.agent.totalUsage.cachedInputTokens ?? 0);
1900
+ const [tokensEstimated, setTokensEstimated] = useState10(runtime.agent.lastInputEstimated);
1796
1901
  const [asks, setAsks] = useState10([]);
1797
1902
  const pending = asks[0] ?? null;
1798
1903
  const askId = useRef2(0);
@@ -1817,7 +1922,8 @@ function App({ runtime, initialPrompt, version, updateMessage }) {
1817
1922
  const { stdin } = useStdin();
1818
1923
  const { stdout } = useStdout2();
1819
1924
  const realStdout = isRealTty(stdout);
1820
- const maxLiveLines = Math.max(8, streamRows(stdout) - 10);
1925
+ const liveWidth = Math.max(20, streamColumns(stdout) - 3);
1926
+ const liveRowBudget = Math.max(8, streamRows(stdout) - 10);
1821
1927
  const maxSplitIdxRef = useRef2(0);
1822
1928
  const refreshStatic = useCallback(() => {
1823
1929
  if (realStdout) {
@@ -1827,23 +1933,33 @@ function App({ runtime, initialPrompt, version, updateMessage }) {
1827
1933
  setStaticKey((k2) => k2 + 1);
1828
1934
  }, [stdout, realStdout]);
1829
1935
  let splitIdx = Math.max(maxSplitIdxRef.current, lastUserIdx > 0 ? lastUserIdx : 0);
1936
+ const entryRows = (e) => {
1937
+ if (e.kind === "thinking") {
1938
+ return showThinking ? Math.min(liveRowBudget, wrappedRows(e.text, liveWidth) + 3) : 2;
1939
+ }
1940
+ if (e.kind === "tool") {
1941
+ return showToolOutput ? Math.min(liveRowBudget, wrappedRows(e.output ?? e.display ?? "", liveWidth) + 2) : 5;
1942
+ }
1943
+ if (e.kind === "assistant") return markdownRowCount(e.text, liveWidth) + 1;
1944
+ if (e.kind === "notice" || e.kind === "user") return wrappedRows(e.text, Math.max(20, liveWidth - 2)) + 1;
1945
+ return 2;
1946
+ };
1830
1947
  if (realStdout) {
1831
- const entryLines = (e) => {
1832
- if (e.kind === "thinking") return showThinking ? Math.min(maxLiveLines, e.text.split("\n").length + 2) : 2;
1833
- if (e.kind === "tool") return showToolOutput ? Math.min(maxLiveLines, (e.output ?? e.display ?? "").split("\n").length + 2) : 5;
1834
- if (e.kind === "assistant" || e.kind === "notice" || e.kind === "user") return e.text.split("\n").length + 1;
1835
- return 2;
1836
- };
1837
- const liveLines = transcript.live.reduce((acc, e) => acc + entryLines(e), 0);
1838
- let doneLines = transcript.done.slice(splitIdx).reduce((acc, e) => acc + entryLines(e), 0);
1839
- while (splitIdx < transcript.done.length - 1 && doneLines + liveLines > maxLiveLines) {
1840
- doneLines -= entryLines(transcript.done[splitIdx]);
1948
+ const liveLines = transcript.live.reduce((acc, e) => acc + entryRows(e), 0);
1949
+ let doneLines = transcript.done.slice(splitIdx).reduce((acc, e) => acc + entryRows(e), 0);
1950
+ while (splitIdx < transcript.done.length && doneLines + liveLines > liveRowBudget) {
1951
+ doneLines -= entryRows(transcript.done[splitIdx]);
1841
1952
  splitIdx++;
1842
1953
  }
1843
1954
  maxSplitIdxRef.current = splitIdx;
1844
1955
  }
1845
1956
  const staticDone = transcript.done.slice(0, splitIdx);
1846
1957
  const activeTurnDone = transcript.done.slice(splitIdx);
1958
+ const doneRows = realStdout ? activeTurnDone.reduce((acc, e) => acc + entryRows(e), 0) : 0;
1959
+ const liveCaps = transcript.live.map(() => Number.POSITIVE_INFINITY);
1960
+ if (realStdout) {
1961
+ for (const [i, cap] of liveRowCaps(transcript.live.map(entryRows), liveRowBudget - doneRows).entries()) liveCaps[i] = cap;
1962
+ }
1847
1963
  const activeTurnEntries = [...activeTurnDone, ...transcript.live];
1848
1964
  const lastThinkingEntryId = activeTurnEntries.findLast((e) => e.kind === "thinking" && !!e.text.trim())?.id;
1849
1965
  const hasThinking = lastThinkingEntryId !== void 0 || transcript.done.some((e) => e.kind === "thinking" && !!e.text.trim());
@@ -1869,7 +1985,13 @@ function App({ runtime, initialPrompt, version, updateMessage }) {
1869
1985
  lastTool.current = { tool: event.tool, target: toolTargets.current.get(event.id) ?? "", output: event.output };
1870
1986
  if ((event.tool === "edit" || event.tool === "write") && !event.isError) editedInTurn.current = true;
1871
1987
  }
1872
- if (event.type === "usage") setTokens(event.inputTokens + event.outputTokens);
1988
+ if (event.type === "usage") {
1989
+ if (event.lastInputTokens !== void 0) {
1990
+ setTokens(event.lastInputTokens);
1991
+ setCachedTokens(event.lastCachedInputTokens ?? 0);
1992
+ setTokensEstimated(event.estimated ?? false);
1993
+ }
1994
+ }
1873
1995
  if (event.type === "budgetExceeded") {
1874
1996
  notice(
1875
1997
  t("Budget exceeded ({kind}): {used} of {limit}. Further model calls are blocked; raise usageCap in ~/.bccli/config.json.", {
@@ -1979,6 +2101,8 @@ function App({ runtime, initialPrompt, version, updateMessage }) {
1979
2101
  setCancelling(false);
1980
2102
  runtime.agent.clear();
1981
2103
  setTokens(0);
2104
+ setCachedTokens(0);
2105
+ setTokensEstimated(false);
1982
2106
  setShowToolOutput(false);
1983
2107
  setTranscript({ done: [entry({ kind: "header" })], live: [] });
1984
2108
  refreshStatic();
@@ -1986,7 +2110,10 @@ function App({ runtime, initialPrompt, version, updateMessage }) {
1986
2110
  return;
1987
2111
  case "cost": {
1988
2112
  const u = runtime.agent.totalUsage;
1989
- notice(t("{input} tokens in \xB7 {output} tokens out", { input: u.inputTokens.toLocaleString(locale()), output: u.outputTokens.toLocaleString(locale()) }));
2113
+ const cache = u.cachedInputTokens ?? 0;
2114
+ notice(
2115
+ t("{input} tokens in \xB7 {output} tokens out", { input: u.inputTokens.toLocaleString(locale()), output: u.outputTokens.toLocaleString(locale()) }) + (cache > 0 ? ` \xB7 ${t("{n} cached", { n: cache.toLocaleString(locale()) })}` : "") + ` \xB7 ${tokensEstimated ? "~" : ""}${t("{n} tokens in context", { n: runtime.agent.lastInputTokens.toLocaleString(locale()) })}`
2116
+ );
1990
2117
  return;
1991
2118
  }
1992
2119
  case "tasks":
@@ -2081,6 +2208,8 @@ function App({ runtime, initialPrompt, version, updateMessage }) {
2081
2208
  setCancelling(false);
2082
2209
  runtime.newSession();
2083
2210
  setTokens(0);
2211
+ setCachedTokens(0);
2212
+ setTokensEstimated(false);
2084
2213
  setShowToolOutput(false);
2085
2214
  setTranscript({ done: [entry({ kind: "header" })], live: [] });
2086
2215
  refreshStatic();
@@ -2116,7 +2245,9 @@ function App({ runtime, initialPrompt, version, updateMessage }) {
2116
2245
  if (!chosen) return;
2117
2246
  runtime.resume(chosen.session);
2118
2247
  setModelLabel(labelFor(runtime, runtime.modelRef));
2119
- setTokens(0);
2248
+ setTokens(runtime.agent.lastInputTokens);
2249
+ setCachedTokens(0);
2250
+ setTokensEstimated(runtime.agent.lastInputEstimated);
2120
2251
  setShowToolOutput(false);
2121
2252
  setTranscript({ done: [entry({ kind: "header" }), ...replayEntries(chosen.session.loadHistory())], live: [] });
2122
2253
  refreshStatic();
@@ -2429,7 +2560,7 @@ function App({ runtime, initialPrompt, version, updateMessage }) {
2429
2560
  notice(t("Unknown command: /{name}. Type /help.", { name }), "warn");
2430
2561
  }
2431
2562
  },
2432
- [runtime, notice, exit, version, runTurn, ask, refreshStatic, toggleThinking]
2563
+ [runtime, notice, exit, version, runTurn, ask, refreshStatic, toggleThinking, tokensEstimated]
2433
2564
  );
2434
2565
  const runSlash = useCallback(
2435
2566
  (name, args) => runSlashCommand(name, args).catch((error) => notice(`/${name}: ${error.message}`, "error")),
@@ -2673,10 +2804,11 @@ ARGUMENTS: ${slash.args}`);
2673
2804
  pending?.resolve(a);
2674
2805
  setAsks((q) => q.slice(1));
2675
2806
  };
2676
- const capLive = (raw) => capLiveMarkdown(raw, { isRealTty: realStdout, maxLines: maxLiveLines });
2807
+ const capLive = (raw, rows) => capLiveMarkdown(raw, { isRealTty: realStdout, maxRows: rows, width: liveWidth });
2677
2808
  const cwd = runtime.cwd.startsWith(homedir()) ? `~${runtime.cwd.slice(homedir().length)}` : runtime.cwd;
2678
- const renderEntry = (e) => {
2809
+ const renderEntry = (e, liveRows) => {
2679
2810
  const isLive = transcript.live.some((le) => le.id === e.id);
2811
+ if (isLive && liveRows === 0) return null;
2680
2812
  switch (e.kind) {
2681
2813
  case "header":
2682
2814
  return /* @__PURE__ */ jsx16(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsxs12(Text16, { children: [
@@ -2687,7 +2819,7 @@ ARGUMENTS: ${slash.args}`);
2687
2819
  case "user":
2688
2820
  return /* @__PURE__ */ jsx16(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: `> ${e.text}` }) }, e.id);
2689
2821
  case "assistant": {
2690
- const displayText = isLive ? capLive(e.text) : e.text;
2822
+ const displayText = isLive ? capLive(e.text, liveRows ?? liveRowBudget) : e.text;
2691
2823
  if (e.cont) {
2692
2824
  return displayText ? /* @__PURE__ */ jsx16(Box13, { paddingLeft: 2, children: /* @__PURE__ */ jsx16(Markdown, { text: displayText }) }, e.id) : null;
2693
2825
  }
@@ -2702,8 +2834,7 @@ ARGUMENTS: ${slash.args}`);
2702
2834
  if (!text) return null;
2703
2835
  const allLines = text.split("\n");
2704
2836
  const lines = allLines.length;
2705
- const shownText = isLive && realStdout && allLines.length > maxLiveLines ? `\u2026 (${allLines.length - maxLiveLines} lines above) \u2026
2706
- ${allLines.slice(-maxLiveLines).join("\n")}` : text;
2837
+ const shownText = isLive && showThinking ? capLiveText(text, { isRealTty: realStdout, maxRows: (liveRows ?? liveRowBudget) - 3, width: liveWidth }) : text;
2707
2838
  const isTarget = e.id === lastThinkingEntryId;
2708
2839
  return /* @__PURE__ */ jsxs12(
2709
2840
  Box13,
@@ -2746,9 +2877,9 @@ ${allLines.slice(-maxLiveLines).join("\n")}` : text;
2746
2877
  const activeSpinnerSummary = activeToolEntry ? `${toolLabel(activeToolEntry.tool)}${activeTarget ? ` ${activeTarget.length > 20 ? `${activeTarget.slice(0, 20)}\u2026` : activeTarget}` : ""}` : "";
2747
2878
  const activeToolSummary = activeToolEntry ? `${toolLabel(activeToolEntry.tool)}${activeTarget ? ` ${activeTarget.length > 68 ? `${activeTarget.slice(0, 68)}\u2026` : activeTarget}` : ""}` : "";
2748
2879
  return /* @__PURE__ */ jsxs12(Box13, { ref: liveRootRef, flexDirection: "column", children: [
2749
- /* @__PURE__ */ jsx16(Static, { items: staticDone, children: renderEntry }, `static-${staticKey}`),
2750
- activeTurnDone.map(renderEntry),
2751
- transcript.live.map(renderEntry),
2880
+ /* @__PURE__ */ jsx16(Static, { items: staticDone, children: (e) => renderEntry(e) }, `static-${staticKey}`),
2881
+ activeTurnDone.map((e) => renderEntry(e)),
2882
+ transcript.live.map((e, i) => renderEntry(e, liveCaps[i])),
2752
2883
  busy && !pending && !planAsk ? /* @__PURE__ */ jsxs12(
2753
2884
  Box13,
2754
2885
  {
@@ -2820,7 +2951,7 @@ ${allLines.slice(-maxLiveLines).join("\n")}` : text;
2820
2951
  /* @__PURE__ */ jsx16(TodoList, { items: todos }),
2821
2952
  queued.map((q, i) => /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: t(" \u23F3 queued: {q}", { q }) }, `${i}-${q}`)),
2822
2953
  /* @__PURE__ */ jsx16(PromptInput, { disabled: !!pending || !!picker || !!providerMenu || !!prompt || !!planAsk || !!mcpMenu || !!listPicker, history, cwd: runtime.cwd, onSubmit: onPrompt, onEmptyTab: toggleThinking, extraCommands, injected, vim: runtime.config.editor === "vim" }),
2823
- /* @__PURE__ */ jsx16(StatusBar, { mode, tokens, busy, model: modelLabel, reasoning })
2954
+ /* @__PURE__ */ jsx16(StatusBar, { mode, tokens, cached: cachedTokens, estimated: tokensEstimated, busy, model: modelLabel, reasoning })
2824
2955
  ] });
2825
2956
  }
2826
2957
 
@@ -2868,7 +2999,7 @@ function installLegacyMouseFilter(source) {
2868
2999
  import { jsx as jsx17 } from "react/jsx-runtime";
2869
3000
  async function startInteractive(rt, opts) {
2870
3001
  if (opts.resume) {
2871
- const { Session: Session2 } = await import("./session-LX4UNPCD.js");
3002
+ const { Session: Session2 } = await import("./session-YM36TGU7.js");
2872
3003
  const list = Session2.list(rt.home, rt.cwd).slice(0, 20);
2873
3004
  if (!list.length) console.log(t("No sessions in this folder yet; starting a new one."));
2874
3005
  else {
@@ -2884,7 +3015,7 @@ async function startInteractive(rt, opts) {
2884
3015
  if (chosen) rt.resume(chosen.session);
2885
3016
  }
2886
3017
  }
2887
- const { serversToStart, setProjectTrust } = await import("./config-YYEAEN2C.js");
3018
+ const { serversToStart, setProjectTrust } = await import("./config-OVWCUVY7.js");
2888
3019
  const plan = serversToStart(rt.home, rt.cwd);
2889
3020
  if (plan.needTrust.length) {
2890
3021
  const { createInterface } = await import("readline/promises");
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  VERSION
4
- } from "./chunk-V5D6PJQN.js";
4
+ } from "./chunk-XP5MTJN2.js";
5
5
  import {
6
6
  t
7
- } from "./chunk-R754XIBJ.js";
7
+ } from "./chunk-IDZLHEVL.js";
8
8
 
9
9
  // src/update.ts
10
10
  import { spawnSync } from "child_process";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botconnector/bccli",
3
- "version": "0.4.16",
3
+ "version": "0.4.17",
4
4
  "description": "BCCLI — BotConnector AI coding agent for the terminal",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,15 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- addProviderKey,
4
- runProviderCommand
5
- } from "./chunk-SWNRA3EW.js";
6
- import "./chunk-INMSTUB2.js";
7
- import "./chunk-6SDRWEPN.js";
8
- import "./chunk-6JK4IBGK.js";
9
- import "./chunk-7XD47KN5.js";
10
- import "./chunk-TLCL6DC7.js";
11
- import "./chunk-R754XIBJ.js";
12
- export {
13
- addProviderKey,
14
- runProviderCommand
15
- };
@@ -1,12 +0,0 @@
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
- };
@@ -1,24 +0,0 @@
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
- };