koneck 2.25.51 → 2.25.53

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.
@@ -0,0 +1,30 @@
1
+ import type { Plan } from './plan.js';
2
+ /**
3
+ * What the model has done this session, gathered from the transcript already on screen.
4
+ *
5
+ * Asked for because a long run is opaque while it happens and unreadable afterwards: the tool
6
+ * calls are all there in scrollback, hundreds of lines apart, and nobody scrolls back through
7
+ * forty screens to count how many files were written.
8
+ *
9
+ * Built from the rows rather than from a second log kept alongside them. Two records of the same
10
+ * events drift, and the one on screen would be the one that was wrong.
11
+ */
12
+ export interface ActivityStep {
13
+ kind: 'tool' | 'say';
14
+ name?: string;
15
+ detail?: string;
16
+ ok?: boolean;
17
+ startedAt?: number;
18
+ endedAt?: number;
19
+ }
20
+ export interface ActivityRow {
21
+ role: string;
22
+ steps?: ActivityStep[];
23
+ text?: string;
24
+ }
25
+ export interface ActivityTotals {
26
+ turns: number;
27
+ totalTokens: number;
28
+ }
29
+ export declare function describeActivity(rows: readonly ActivityRow[], plan: Plan | null, totals: ActivityTotals): string;
30
+ //# sourceMappingURL=activity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activity.d.ts","sourceRoot":"","sources":["../src/activity.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC;;;;;;;;;GASG;AAEH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB;AAYD,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,SAAS,WAAW,EAAE,EAC5B,IAAI,EAAE,IAAI,GAAG,IAAI,EACjB,MAAM,EAAE,cAAc,GACrB,MAAM,CAuDR"}
@@ -0,0 +1,59 @@
1
+ import { planSummary } from './plan.js';
2
+ /** Files and commands are what people actually want to know about; the rest is counted. */
3
+ const FILE_TOOLS = new Set(['write_file', 'apply_diff', 'search_replace', 'move_file', 'delete_file']);
4
+ export function describeActivity(rows, plan, totals) {
5
+ const byTool = new Map();
6
+ const files = new Set();
7
+ const commands = [];
8
+ for (const row of rows) {
9
+ for (const step of row.steps ?? []) {
10
+ if (step.kind !== 'tool' || !step.name)
11
+ continue;
12
+ const tally = byTool.get(step.name) ?? { calls: 0, failed: 0, ms: 0 };
13
+ tally.calls++;
14
+ if (step.ok === false)
15
+ tally.failed++;
16
+ // Checked for presence, not truthiness: a step that began at timestamp 0 is a real step,
17
+ // and `if (startedAt)` silently drops it along with its timing.
18
+ if (step.startedAt !== undefined && step.endedAt !== undefined) {
19
+ tally.ms += Math.max(0, step.endedAt - step.startedAt);
20
+ }
21
+ byTool.set(step.name, tally);
22
+ if (FILE_TOOLS.has(step.name) && step.detail)
23
+ files.add(step.detail.split(/\s+/)[0] ?? step.detail);
24
+ if (step.name === 'execute_command' && step.detail)
25
+ commands.push(step.detail);
26
+ }
27
+ }
28
+ if (byTool.size === 0) {
29
+ return 'Nothing done yet this session — no tools have run.';
30
+ }
31
+ const lines = [];
32
+ lines.push(`**${totals.turns} turns**, ${totals.totalTokens.toLocaleString()} tokens`);
33
+ if (plan && plan.steps.length > 0)
34
+ lines.push(`**Plan** ${planSummary(plan)}`);
35
+ lines.push('');
36
+ lines.push('**Tools**');
37
+ for (const [name, tally] of [...byTool.entries()].sort((a, b) => b[1].calls - a[1].calls)) {
38
+ const seconds = tally.ms > 0 ? ` ${(tally.ms / 1000).toFixed(1)}s` : '';
39
+ const failed = tally.failed > 0 ? ` ${tally.failed} failed` : '';
40
+ lines.push(` ${name} × ${tally.calls}${seconds}${failed}`);
41
+ }
42
+ if (files.size > 0) {
43
+ lines.push('');
44
+ lines.push(`**Files changed** (${files.size})`);
45
+ for (const file of [...files].slice(0, 20))
46
+ lines.push(` ${file}`);
47
+ if (files.size > 20)
48
+ lines.push(` … and ${files.size - 20} more`);
49
+ }
50
+ if (commands.length > 0) {
51
+ lines.push('');
52
+ lines.push(`**Commands run** (${commands.length})`);
53
+ // The last few, because the recent ones are the ones being asked about.
54
+ for (const command of commands.slice(-8))
55
+ lines.push(` ${command.slice(0, 90)}`);
56
+ }
57
+ return lines.join('\n');
58
+ }
59
+ //# sourceMappingURL=activity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activity.js","sourceRoot":"","sources":["../src/activity.ts"],"names":[],"mappings":"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"}
@@ -121,6 +121,14 @@ interface AskOption {
121
121
  * would otherwise renumber the rows beneath it in one of the two paths and not the other.
122
122
  */
123
123
  export declare function askOptions(tool: string, prefix: string): AskOption[];
124
+ /**
125
+ * The last few lines of reasoning, wrapped and clipped for a glance.
126
+ *
127
+ * Reasoning arrives as one long stream with few newlines, so it is wrapped to the panel width
128
+ * first and then the tail is taken — clipping the raw text instead would show the last 200
129
+ * characters of one enormous line, which is a fragment of a sentence and tells nobody anything.
130
+ */
131
+ export declare function thinkingTail(text: string, lines: number, width: number): string[];
124
132
  export declare function runInkChatMode(config: AgentConfig): Promise<void>;
125
133
  export {};
126
134
  //# sourceMappingURL=ink-chat.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ink-chat.d.ts","sourceRoot":"","sources":["../src/ink-chat.tsx"],"names":[],"mappings":"AAgDA,OAAO,KAAK,EAAY,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAM5D,OAAO,KAAK,EAAE,WAAW,EAAiB,MAAM,YAAY,CAAC;AAoE7D,6FAA6F;AAC7F,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAM7C;AA0BD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA+BpE;AAED,qFAAqF;AACrF,eAAO,MAAM,cAAc,KAAK,CAAC;AAEjC;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,SAAiB,GAAG,MAAM,CAM9E;AA6CD;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAGnE;AAuBD;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,UAAU,CAAC;AAE3E,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CA2GzE,CAAC;AAEF,0CAA0C;AAC1C,iFAAiF;AACjF,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,KAAK,EAAE,MAAM,CAAC;IACd,8BAA8B;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAItD;AAED;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,UAAU,GAAG,QAAQ,CAMpD;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAIpF;AAsDD,UAAU,UAAU;IAAG,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE;AAUtG,qDAAqD;AACrD,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAK7E;AAeD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,EAAE,CAWlF;AAGD,iGAAiG;AACjG,UAAU,SAAS;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,SAAS,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,CAepE;AAo9FD,wBAAsB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAQvE"}
1
+ {"version":3,"file":"ink-chat.d.ts","sourceRoot":"","sources":["../src/ink-chat.tsx"],"names":[],"mappings":"AAgDA,OAAO,KAAK,EAAY,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAO5D,OAAO,KAAK,EAAE,WAAW,EAAiB,MAAM,YAAY,CAAC;AAoE7D,6FAA6F;AAC7F,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAM7C;AA0BD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA+BpE;AAED,qFAAqF;AACrF,eAAO,MAAM,cAAc,KAAK,CAAC;AAEjC;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,SAAiB,GAAG,MAAM,CAM9E;AA6CD;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAGnE;AAuBD;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,UAAU,CAAC;AAE3E,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CA2GzE,CAAC;AAEF,0CAA0C;AAC1C,iFAAiF;AACjF,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,KAAK,EAAE,MAAM,CAAC;IACd,8BAA8B;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAItD;AAED;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,UAAU,GAAG,QAAQ,CAMpD;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAIpF;AAwDD,UAAU,UAAU;IAAG,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE;AAUtG,qDAAqD;AACrD,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAK7E;AAeD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,EAAE,CAWlF;AAGD,iGAAiG;AACjG,UAAU,SAAS;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,SAAS,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,CAepE;AAQD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAMjF;AA8/FD,wBAAsB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAQvE"}
package/dist/ink-chat.js CHANGED
@@ -35,6 +35,7 @@ import { MODES as MODE_SPECS, modeSpec, decide, loadRules, saveRules, addRule, c
35
35
  import { AskQueue } from './ask-queue.js';
36
36
  import { planSummary } from './plan.js';
37
37
  import { logoFor } from './logo.js';
38
+ import { describeActivity } from './activity.js';
38
39
  import { describeSandbox, resolveSandbox } from './sandbox.js';
39
40
  const CYAN = '#54D7E7';
40
41
  const GREEN = '#9BCB8F';
@@ -443,6 +444,8 @@ const COMMANDS = [
443
444
  { cmd: '/cost', desc: 'Session cost' },
444
445
  { cmd: '/memory', desc: 'Show project memory (/memory clear wipes it)' },
445
446
  { cmd: '/altscreen', desc: 'Pin the prompt to the last row (own screen, own scrollback)' },
447
+ { cmd: '/thinking', desc: 'Show or hide the model\'s reasoning as it arrives' },
448
+ { cmd: '/activity', desc: 'What the model has done this session, and what it is on now' },
446
449
  { cmd: '/name', desc: 'Name this session, so /resume can find it by name' },
447
450
  { cmd: '/fork', desc: 'Copy this session and continue on the copy' },
448
451
  { cmd: '/archive', desc: 'Keep a session but hide it from /resume' },
@@ -538,6 +541,24 @@ export function askOptions(tool, prefix) {
538
541
  options.push({ key: 'n', label: 'refuse', rule: null, allow: false });
539
542
  return options;
540
543
  }
544
+ /** How much of a turn's reasoning to keep for display. Enough to read, not enough to scroll. */
545
+ const THINKING_TAIL_CHARS = 2_000;
546
+ /** Lines of reasoning shown live. More than this and it stops being a glance. */
547
+ const THINKING_LINES = 4;
548
+ /**
549
+ * The last few lines of reasoning, wrapped and clipped for a glance.
550
+ *
551
+ * Reasoning arrives as one long stream with few newlines, so it is wrapped to the panel width
552
+ * first and then the tail is taken — clipping the raw text instead would show the last 200
553
+ * characters of one enormous line, which is a fragment of a sentence and tells nobody anything.
554
+ */
555
+ export function thinkingTail(text, lines, width) {
556
+ const wrapped = text
557
+ .split('\n')
558
+ .flatMap(paragraph => wrapPreserving(paragraph, Math.max(20, width)))
559
+ .filter(line => line.trim() !== '');
560
+ return wrapped.slice(-lines);
561
+ }
541
562
  const MODES = MODE_SPECS.map(m => m.name);
542
563
  const HELP_TEXT = `
543
564
  Type / on an empty line to browse every command; keep typing to filter.
@@ -825,8 +846,13 @@ function App({ config: initialConfig }) {
825
846
  toolsWarnedRef.current = model;
826
847
  addSystem(toolsUnsupportedNotice(model));
827
848
  },
828
- onThinking: (_text, totalChars) => {
849
+ onThinking: (text, totalChars) => {
829
850
  thinkingRef.current = { chars: totalChars, at: Date.now() };
851
+ // The text was discarded before this, so the model's reasoning was never visible — only a
852
+ // count of it. Kept as a rolling tail rather than the whole thing: a reasoning model can
853
+ // produce tens of thousands of characters per turn, and what anyone watching wants is what
854
+ // it is thinking now.
855
+ thinkingTextRef.current = (thinkingTextRef.current + text).slice(-THINKING_TAIL_CHARS);
830
856
  lastActivityRef.current = Date.now();
831
857
  },
832
858
  onAgentProgress: (list) => { agentsRef.current = list; setAgents(list); },
@@ -855,6 +881,15 @@ function App({ config: initialConfig }) {
855
881
  const modeRef = useRef('auto');
856
882
  /** The run's current plan, so it is visible rather than buried in the transcript. */
857
883
  const [plan, setPlan] = useState(null);
884
+ /**
885
+ * The tail of what the model is reasoning about, and whether to show it.
886
+ *
887
+ * A ref rather than state: reasoning arrives in a flood, and setting state per chunk would
888
+ * repaint the screen faster than anyone can read it. The live region already redraws on a
889
+ * timer, which is the right rate for something being read rather than parsed.
890
+ */
891
+ const thinkingTextRef = useRef('');
892
+ const [showThinking, setShowThinking] = useState(true);
858
893
  /**
859
894
  * Whether the interface owns the whole screen.
860
895
  *
@@ -1737,6 +1772,20 @@ function App({ config: initialConfig }) {
1737
1772
  : 'Alt-screen off: back to writing into the terminal\'s own scrollback.');
1738
1773
  return;
1739
1774
  }
1775
+ case '/thinking': {
1776
+ const next = arg.trim() === 'off' ? false : arg.trim() === 'on' ? true : !showThinking;
1777
+ setShowThinking(next);
1778
+ addSystem(next
1779
+ ? 'Showing the model\'s reasoning as it arrives, under the working line.'
1780
+ : 'Hiding the reasoning. The token count stays on the working line.');
1781
+ return;
1782
+ }
1783
+ case '/activity': {
1784
+ // Built from the transcript that is already held rather than a second log kept in
1785
+ // parallel: two records of the same events drift, and the one shown would be the wrong one.
1786
+ addSystem(describeActivity(rows, plan, stats));
1787
+ return;
1788
+ }
1740
1789
  case '/memory': {
1741
1790
  // Clearable, because these notes are written by the model — including while it is
1742
1791
  // failing — and a wrong one is read back as fact by every later session in this folder.
@@ -2725,6 +2774,7 @@ function App({ config: initialConfig }) {
2725
2774
  streamedRef.current = '';
2726
2775
  replyStartedRef.current = false;
2727
2776
  thinkingRef.current = { chars: 0, at: 0 };
2777
+ thinkingTextRef.current = '';
2728
2778
  incompleteRef.current = null;
2729
2779
  lastActivityRef.current = Date.now();
2730
2780
  setProviderFrames(0);
@@ -2982,7 +3032,7 @@ function App({ config: initialConfig }) {
2982
3032
  : elapsedMs > 8_000 ? `no response from ${cfg.provider} yet`
2983
3033
  : 'starting', thinkingRef.current.chars > 0 && Date.now() - thinkingRef.current.at < 2_000
2984
3034
  ? ` | thinking, ${fmtTokens(Math.round(thinkingRef.current.chars / 4))} reasoning tokens`
2985
- : '', ")"] })] }) }), _jsx(Box, { paddingLeft: 2, children: _jsx(Text, { color: DIM, children: "esc to cancel and keep your input \u00B7 ctrl+c to exit" }) })] })) }), usagePane && (() => {
3035
+ : '', ")"] })] }) }), showThinking && thinkingTextRef.current.trim() !== '' && (_jsx(Box, { flexDirection: "column", paddingLeft: 2, children: thinkingTail(thinkingTextRef.current, THINKING_LINES, barWidth - 6).map((line, i) => (_jsx(Text, { color: DIM, italic: true, children: line }, i))) })), _jsx(Box, { paddingLeft: 2, children: _jsx(Text, { color: DIM, children: "esc to cancel and keep your input \u00B7 ctrl+c to exit" }) })] })) }), usagePane && (() => {
2986
3036
  const width = Math.min(barWidth, usagePane === 'settings' ? 96 : 66);
2987
3037
  const inner = width - 4;
2988
3038
  const BAR_W = Math.max(12, inner - 14);