koneck 2.25.55 → 2.25.57

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.
@@ -129,6 +129,33 @@ export declare function askOptions(tool: string, prefix: string): AskOption[];
129
129
  * characters of one enormous line, which is a fragment of a sentence and tells nobody anything.
130
130
  */
131
131
  export declare function thinkingTail(text: string, lines: number, width: number): string[];
132
+ /**
133
+ * Where a paste belongs, given what is on screen.
134
+ *
135
+ * Pulled out as a decision of its own because it was wrong and invisible: the paste capture ran
136
+ * before every other branch and always routed to the composer, so pasting an API key into the
137
+ * prompt asking for one put the key in the message box — on screen, and one keystroke from being
138
+ * sent to a model.
139
+ */
140
+ export type PasteTarget = 'key-prompt' | 'composer' | 'drop';
141
+ export declare function pasteTarget(open: {
142
+ keyPrompt?: boolean;
143
+ picker?: boolean;
144
+ ask?: boolean;
145
+ }): PasteTarget;
146
+ /** Bytes as a person reads them, for a session listing. */
147
+ export declare function humanBytes(bytes: number): string;
148
+ /**
149
+ * What to call a session in a list.
150
+ *
151
+ * A name if one was given, otherwise the first thing asked of it — which is what people remember
152
+ * a piece of work by. The id is the last resort, because a timestamp identifies nothing.
153
+ */
154
+ export declare function sessionTitle(meta: {
155
+ name?: string;
156
+ task?: string;
157
+ id: string;
158
+ }): string;
132
159
  export declare function runInkChatMode(config: AgentConfig): Promise<void>;
133
160
  export {};
134
161
  //# 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;AAQ5D,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;AAyDD,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;AAsgGD,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":"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"}
package/dist/ink-chat.js CHANGED
@@ -36,6 +36,7 @@ import { AskQueue } from './ask-queue.js';
36
36
  import { planSummary } from './plan.js';
37
37
  import { logoFor } from './logo.js';
38
38
  import { describeActivity } from './activity.js';
39
+ import { redactSecrets, containsSecret } from './redact.js';
39
40
  import { renderTrajectory } from './trajectory.js';
40
41
  import { describeSandbox, resolveSandbox } from './sandbox.js';
41
42
  const CYAN = '#54D7E7';
@@ -448,6 +449,7 @@ const COMMANDS = [
448
449
  { cmd: '/thinking', desc: 'Show or hide the model\'s reasoning as it arrives' },
449
450
  { cmd: '/activity', desc: 'What the model has done this session, and what it is on now' },
450
451
  { cmd: '/trajectory', desc: 'Where the time went: per-request timings, tool durations, waits' },
452
+ { cmd: '/update', desc: 'Check for a new version and update' },
451
453
  { cmd: '/name', desc: 'Name this session, so /resume can find it by name' },
452
454
  { cmd: '/fork', desc: 'Copy this session and continue on the copy' },
453
455
  { cmd: '/archive', desc: 'Keep a session but hide it from /resume' },
@@ -561,13 +563,44 @@ export function thinkingTail(text, lines, width) {
561
563
  .filter(line => line.trim() !== '');
562
564
  return wrapped.slice(-lines);
563
565
  }
566
+ export function pasteTarget(open) {
567
+ if (open.keyPrompt)
568
+ return 'key-prompt';
569
+ // A picker and a permission question both take single keys. A pasted blob is not an answer to
570
+ // either, and answering something nobody chose is worse than ignoring it.
571
+ if (open.picker || open.ask)
572
+ return 'drop';
573
+ return 'composer';
574
+ }
575
+ /** Bytes as a person reads them, for a session listing. */
576
+ export function humanBytes(bytes) {
577
+ if (bytes < 1024)
578
+ return `${bytes}B`;
579
+ if (bytes < 1024 * 1024)
580
+ return `${(bytes / 1024).toFixed(1)}KB`;
581
+ return `${(bytes / 1024 / 1024).toFixed(1)}MB`;
582
+ }
583
+ /**
584
+ * What to call a session in a list.
585
+ *
586
+ * A name if one was given, otherwise the first thing asked of it — which is what people remember
587
+ * a piece of work by. The id is the last resort, because a timestamp identifies nothing.
588
+ */
589
+ export function sessionTitle(meta) {
590
+ if (meta.name)
591
+ return meta.name;
592
+ const task = redactSecrets(meta.task ?? '').replace(/\s+/g, ' ').trim();
593
+ if (!task || task === '(chat session)')
594
+ return meta.id;
595
+ return task.length > 64 ? task.slice(0, 63) + '…' : task;
596
+ }
564
597
  const MODES = MODE_SPECS.map(m => m.name);
565
598
  const HELP_TEXT = `
566
599
  Type / on an empty line to browse every command; keep typing to filter.
567
600
 
568
601
  Slash commands:
569
602
  /help Show this help
570
- (shift+u) Updates and a key guide, in a side panel
603
+ /update Updates and a key guide, in a side panel
571
604
  /models [filter] Browse models from the connected provider and switch
572
605
  /connect [provider] Connect a provider (picker when no name given)
573
606
  /connect <name> <url> Declare a provider KONECK does not ship with, e.g.
@@ -1232,8 +1265,12 @@ function App({ config: initialConfig }) {
1232
1265
  async function doSave() {
1233
1266
  const s = await activeSession.p.catch(() => null);
1234
1267
  const firstUser = s?.messages.find((m) => m.role === 'user');
1268
+ // Masked before it is written. A key pasted into the composer became the first user message,
1269
+ // which is where this title comes from — so it was persisted to disk and then printed in the
1270
+ // resume picker in full.
1235
1271
  const task = firstUser && typeof firstUser.content === 'string'
1236
- ? firstUser.content.slice(0, 120) : '(chat session)';
1272
+ ? redactSecrets(firstUser.content).slice(0, 120)
1273
+ : '(chat session)';
1237
1274
  // Both of these come from the live session rather than from React state: a save fired at the
1238
1275
  // end of a turn runs inside a closure that captured state as it was when the turn began, so
1239
1276
  // reading `stats` here recorded turn counts one behind and, after a resume, the wrong id.
@@ -1243,8 +1280,14 @@ function App({ config: initialConfig }) {
1243
1280
  // runs every turn would otherwise wipe a name the moment the agent replied.
1244
1281
  const existing = await loadSession(cfg.cwd, id).then(r => r.meta).catch(() => null);
1245
1282
  const livePlan = s?.plan?.steps.length ? { steps: s.plan.steps } : undefined;
1283
+ // Captured when saving, because a branch cannot be recovered from the file later — by then the
1284
+ // checkout has moved on.
1285
+ const branch = await execa('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd: cfg.cwd, reject: false })
1286
+ .then(r => r.stdout.trim())
1287
+ .catch(() => '');
1246
1288
  return saveSession(cfg.cwd, id, {
1247
1289
  ...(livePlan ? { plan: livePlan } : {}),
1290
+ ...(branch && branch !== 'HEAD' ? { branch } : {}),
1248
1291
  ...(existing?.name ? { name: existing.name } : {}),
1249
1292
  ...(existing?.archived ? { archived: existing.archived } : {}),
1250
1293
  ...(existing?.forkedFrom ? { forkedFrom: existing.forkedFrom } : {}),
@@ -1782,6 +1825,10 @@ function App({ config: initialConfig }) {
1782
1825
  : 'Hiding the reasoning. The token count stays on the working line.');
1783
1826
  return;
1784
1827
  }
1828
+ case '/update': {
1829
+ void openUpdatePane();
1830
+ return;
1831
+ }
1785
1832
  case '/trajectory': {
1786
1833
  // The timings the transcript cannot answer for: where the time went, which tool was slow,
1787
1834
  // how much of a long turn was spent waiting for a first token rather than generating.
@@ -1936,13 +1983,21 @@ function App({ config: initialConfig }) {
1936
1983
  return;
1937
1984
  }
1938
1985
  if (pick === '') {
1986
+ // The title comes first because that is what tells two sessions apart; the id is a
1987
+ // timestamp and says only when. Everything under it is context: how long ago, which
1988
+ // branch, how much work. Masked on display as well as on write, since the files already
1989
+ // on disk were written before that existed and one of them holds a key.
1939
1990
  openPicker('resume', saved.slice(0, 20).map((x, i) => ({
1940
1991
  value: x.id,
1941
- label: x.name ?? x.id,
1942
- // Ordered by last write, so the first entry is the one you were last in — said out
1943
- // loud rather than left to be inferred from a timestamp buried in the id.
1944
- desc: `${i === 0 ? 'latest · ' : ''}${relativeAge(x.updatedAt)} ` +
1945
- `${x.turns} turns ${x.totalTokens.toLocaleString()} tok ${x.task.slice(0, 36)}`,
1992
+ label: sessionTitle(x),
1993
+ desc: [
1994
+ i === 0 ? 'latest' : relativeAge(x.updatedAt),
1995
+ ...(i === 0 ? [relativeAge(x.updatedAt)] : []),
1996
+ ...(x.branch ? [x.branch] : []),
1997
+ `${x.turns} turn${x.turns === 1 ? '' : 's'}`,
1998
+ ...(x.totalTokens > 0 ? [`${humanTokens(x.totalTokens)} tok`] : []),
1999
+ ...(x.sizeBytes ? [humanBytes(x.sizeBytes)] : []),
2000
+ ].join(' · '),
1946
2001
  })));
1947
2002
  return;
1948
2003
  }
@@ -2253,6 +2308,15 @@ function App({ config: initialConfig }) {
2253
2308
  function filePaste(text) {
2254
2309
  const clean = text.replace(/\r\n?/g, '\n');
2255
2310
  const lines = clean.split('\n').length;
2311
+ // Whatever owns the keyboard gets the paste.
2312
+ const target = pasteTarget({ keyPrompt: Boolean(keyPrompt), picker: Boolean(picker), ask: Boolean(ask) });
2313
+ if (target === 'drop')
2314
+ return;
2315
+ if (target === 'key-prompt') {
2316
+ // A key pasted from a manager often carries a trailing newline; it is not part of the key.
2317
+ setKeyPrompt(p => (p ? { ...p, value: p.value + clean.replace(/\s+$/, '') } : p));
2318
+ return;
2319
+ }
2256
2320
  if (!clean.includes('\n') && clean.length <= 200) {
2257
2321
  setDraft(d => d.slice(0, caret) + clean + d.slice(caret));
2258
2322
  setCaret(c => c + clean.length);
@@ -2576,15 +2640,11 @@ function App({ config: initialConfig }) {
2576
2640
  }
2577
2641
  }
2578
2642
  // Shift+U → the update and guide panel.
2579
- if (key.shift && (input === 'U' || input === 'u')) {
2580
- if (updatePane) {
2581
- setUpdatePane(null);
2582
- setUpdating('idle');
2583
- }
2584
- else
2585
- void openUpdatePane();
2586
- return;
2587
- }
2643
+ // There is deliberately no shortcut to open this any more. It was shift+u, which is exactly
2644
+ // how a capital U is typed: the pane opened instead of the letter appearing, and there was no
2645
+ // way to write one at all. /update opens it, which is discoverable from /help and the palette
2646
+ // rather than from a habit, and cannot collide with anything anyone types.
2647
+ //
2588
2648
  // Inside the panel: u applies the update, esc closes it.
2589
2649
  if (updatePane) {
2590
2650
  if (key.escape) {
@@ -2764,6 +2824,13 @@ function App({ config: initialConfig }) {
2764
2824
  setPasteCount(0);
2765
2825
  // Paths that really exist are shortened to their name for the transcript only. The model
2766
2826
  // still receives the full path, which it needs in order to open the file.
2827
+ // Said out loud rather than quietly sent. A key in a prompt reaches the provider, the
2828
+ // session file and the scrollback, and the only useful moment to mention it is now.
2829
+ if (containsSecret(task)) {
2830
+ addSystem('That looks like an API key or token. It has been sent as typed — treat it as exposed ' +
2831
+ 'and rotate it. Keys belong in /key or an environment variable, where they are not ' +
2832
+ 'written into the session log.');
2833
+ }
2767
2834
  const mentions = findMentions(task, pathKind);
2768
2835
  const forDisplay = pasteForDisplay(mentions.length ? shortenMentions(task, mentions) : task);
2769
2836
  addRow({ role: 'user', text: forDisplay });
@@ -3231,7 +3298,7 @@ function App({ config: initialConfig }) {
3231
3298
  }
3232
3299
  lines.push('');
3233
3300
  lines.push('---');
3234
- lines.push('**Keys** shift+u update · tab analytics · shift+tab mode · ctrl+c exit');
3301
+ lines.push('**Keys** tab analytics · shift+tab mode · ctrl+c exit · /update for this panel');
3235
3302
  lines.push('**Editing** arrows move · ctrl+a/e ends · ctrl+w word · ctrl+u/k cut');
3236
3303
  lines.push('**Sessions** /sessions · /send <repo> <task> · /ask <repo> <q> · /resume');
3237
3304
  lines.push('**Changelog** github.com/Gubevu/konech/commits/main');