shadok-ai 0.15.4 → 0.15.6

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.
package/dist/detect.d.ts CHANGED
@@ -80,6 +80,29 @@ export declare function inputHasProbe(screen: string, probe: string): boolean;
80
80
  * literal text isn't on screen to look for).
81
81
  */
82
82
  export declare function inputText(screen: string): string;
83
+ /**
84
+ * The WHOLE input box, not just its first line.
85
+ *
86
+ * `inputText` reads the `❯` line alone, which is everything there is while a
87
+ * prompt fits on one line — and nothing at all once it does not. A prompt that
88
+ * wraps, or one pasted into a box that was ALREADY occupied, puts the rest on
89
+ * continuation lines carrying no `❯`, so the line `inputText` watches stops
90
+ * moving while the box fills up.
91
+ *
92
+ * That blind spot is what turned an ordinary interrupt into "the text never
93
+ * appeared in the input box". Observed on a live pane, 300 ms apart: a prompt
94
+ * submitted and running, then an Escape (the Telegram bridge preempting a turn
95
+ * for the message typed right behind it) — and Claude Code **puts the
96
+ * interrupted prompt back into the box**. The next paste therefore landed under
97
+ * text that was already there, the `❯` line never changed, and six attempts
98
+ * stacked six copies of the message's header in the box while every check
99
+ * reported nothing had arrived.
100
+ *
101
+ * Reading the whole box fixes both halves at once: a paste that landed is
102
+ * visible wherever it landed, and a Ctrl-U that removed ONE line (which is all
103
+ * it removes) shows as progress instead of as a box that will not clear.
104
+ */
105
+ export declare function boxText(screen: string): string;
83
106
  /**
84
107
  * Why a screen refused a prompt — named, when the shape is recognisable.
85
108
  *
package/dist/detect.js CHANGED
@@ -123,6 +123,50 @@ export function inputText(screen) {
123
123
  }
124
124
  return inputLine.replace(/^\s*[❯!]\s*/, "").trim();
125
125
  }
126
+ /**
127
+ * Where the input box ENDS: its bottom border, or — for a screen that carries
128
+ * no frame (a narrow pane, an older render) — the status line under it. That
129
+ * line moves on its own (`⏵⏵ auto mode on · 1 shell · ↓ to manage`), and read
130
+ * as part of the box it would pass for a paste that landed.
131
+ */
132
+ const BOX_END = /^\s*(?:[─━]{10,}|⏵⏵|⏸|\?\s+for shortcuts)/;
133
+ /**
134
+ * The WHOLE input box, not just its first line.
135
+ *
136
+ * `inputText` reads the `❯` line alone, which is everything there is while a
137
+ * prompt fits on one line — and nothing at all once it does not. A prompt that
138
+ * wraps, or one pasted into a box that was ALREADY occupied, puts the rest on
139
+ * continuation lines carrying no `❯`, so the line `inputText` watches stops
140
+ * moving while the box fills up.
141
+ *
142
+ * That blind spot is what turned an ordinary interrupt into "the text never
143
+ * appeared in the input box". Observed on a live pane, 300 ms apart: a prompt
144
+ * submitted and running, then an Escape (the Telegram bridge preempting a turn
145
+ * for the message typed right behind it) — and Claude Code **puts the
146
+ * interrupted prompt back into the box**. The next paste therefore landed under
147
+ * text that was already there, the `❯` line never changed, and six attempts
148
+ * stacked six copies of the message's header in the box while every check
149
+ * reported nothing had arrived.
150
+ *
151
+ * Reading the whole box fixes both halves at once: a paste that landed is
152
+ * visible wherever it landed, and a Ctrl-U that removed ONE line (which is all
153
+ * it removes) shows as progress instead of as a box that will not clear.
154
+ */
155
+ export function boxText(screen) {
156
+ const lines = screen.split("\n");
157
+ let start = -1;
158
+ for (let i = 0; i < lines.length; i++) {
159
+ if (lines[i].trimStart().startsWith("❯") || /^!\s/.test(lines[i]))
160
+ start = i;
161
+ }
162
+ if (start === -1)
163
+ return "";
164
+ const out = [lines[start].replace(/^\s*[❯!]\s*/, "")];
165
+ // Everything up to the box's bottom border belongs to the box.
166
+ for (let i = start + 1; i < lines.length && !BOX_END.test(lines[i]); i++)
167
+ out.push(lines[i]);
168
+ return out.join("\n").trim();
169
+ }
126
170
  /**
127
171
  * Why a screen refused a prompt — named, when the shape is recognisable.
128
172
  *
@@ -145,6 +189,15 @@ export function describeStuckScreen(screen) {
145
189
  return "the screen is a masked input field (long runs of asterisks), so the pasted text is never readable back";
146
190
  if (/^\s*❯\s*\d+\.\s+\S/m.test(screen))
147
191
  return "the agent is waiting on a question, not on a prompt — answer it first";
192
+ // Last, because the three above are more specific and a full box is often
193
+ // their consequence rather than their cause. MULTI-LINE only: at rest the box
194
+ // shows the previous message as a one-line suggestion, and blaming that ghost
195
+ // would be the confident wrong answer this function exists to avoid. Text
196
+ // Claude Code put back after an interrupt keeps its ⟦header⟧ line, so it is
197
+ // the multi-line one.
198
+ const box = boxText(screen);
199
+ if (box.includes("\n"))
200
+ return `the input box was not empty — it still held ${box.split("\n").length} lines beginning ${JSON.stringify(box.split("\n")[0].slice(0, 50))}, which is what Claude Code puts back there when a turn is interrupted`;
148
201
  return null;
149
202
  }
150
203
  /**
@@ -270,15 +323,25 @@ export async function moveToOption(pilot, n) {
270
323
  * "Landed" means the box DIFFERS from what it showed just before the paste, not
271
324
  * that it is non-empty: an empty box is not always blank. Before the first
272
325
  * prompt of a session, Claude Code (seen on 2.1.285) fills it with an example —
273
- * `❯ Try "refactor <filepath>"` — which `inputText` reads like typed text, so
274
- * "non-empty" was true before anything was pasted, and a paste the TUI dropped
275
- * was followed by Enter on a box holding nothing. The example also comes back
276
- * after a Ctrl-U, so "cleared" likewise means "back to that resting state".
277
- * Comparing with the state read just before is still content-agnostic (a
278
- * collapsed "[Pasted text +N lines]" differs from it as well as the text would)
279
- * and needs no list of hint strings, which would rot with the next release. The
280
- * baseline is re-read on every attempt, after the recovery has run, so a retry
281
- * that pastes the same text again is compared with the box it pastes into.
326
+ * `❯ Try "refactor <filepath>"` — which reads like typed text, so "non-empty"
327
+ * was true before anything was pasted, and a paste the TUI dropped was followed
328
+ * by Enter on a box holding nothing. The example also comes back after a
329
+ * Ctrl-U, so "cleared" likewise means "back to that resting state". Comparing
330
+ * with the state read just before is content-agnostic (a collapsed "[Pasted
331
+ * text +N lines]" differs from it as well as the text would) and needs no list
332
+ * of hint strings, which would rot with the next release. The baseline is
333
+ * re-read on every attempt, after the recovery has run, so a retry that pastes
334
+ * the same text again is compared with the box it pastes into.
335
+ *
336
+ * Every one of those reads is `boxText`, the WHOLE box, and that is the
337
+ * difference between this working and the failure it was reported for. With
338
+ * `inputText` — the `❯` line alone — a box that already held something was
339
+ * invisible: the paste landed on a continuation line, the watched line never
340
+ * moved, and the recovery's "is it clear?" compared that same unmoving line and
341
+ * answered yes without clearing anything. Six attempts then stacked six copies
342
+ * of the message's header into the box and the submit failed. Two Telegram
343
+ * messages a second apart is all it takes to get there, because an interrupted
344
+ * prompt is put BACK in the box (see `boxText`).
282
345
  *
283
346
  * It lives here, next to `idleStep` and `moveToOption`, for the reason those do:
284
347
  * it was written twice (node-pty and tmux), and could only be exercised by
@@ -290,11 +353,68 @@ const MAX_CLEARS = 40;
290
353
  /** How long one press is given to show. Above the tmux mirror's fast poll
291
354
  * (`SCREEN_FAST_MS`), or a press that worked would read as one that did not. */
292
355
  const CLEAR_STEP_MS = 400;
356
+ /**
357
+ * Empty the input box, one Ctrl-U at a time.
358
+ *
359
+ * Ctrl-U clears ONE LINE, not the box (seen on 2.1.296): a two-line paste takes
360
+ * three presses — the last line, the newline, the first line. Progress cannot be
361
+ * read off `inputText`, which sees the first line only and therefore stays put
362
+ * while everything under it goes; `boxText` is what makes each press visible.
363
+ * A run of presses that leaves the SCREEN untouched is a box with nothing left
364
+ * to clear (or one that refuses to), and is given up on rather than hammered.
365
+ */
366
+ async function emptyBox(pilot, clearMs) {
367
+ let stuck = 0;
368
+ for (let press = 0; press < MAX_CLEARS && stuck < 2; press++) {
369
+ const shown = boxText(pilot.screen());
370
+ if (shown === "")
371
+ return;
372
+ pilot.clearInput();
373
+ try {
374
+ await pilot.waitFor((s) => boxText(s) !== shown, { timeoutMs: Math.min(clearMs, CLEAR_STEP_MS) });
375
+ stuck = 0;
376
+ }
377
+ catch {
378
+ // Two presses that moved nothing: there is nothing left to clear. That is
379
+ // also the resting state of a FRESH session, whose box shows an example
380
+ // (`❯ Try "create a util logging.py that…"`) that Ctrl-U leaves exactly
381
+ // as it is — verified on 2.1.301, where it neither clears nor rotates. So
382
+ // the first prompt of a session pays two harmless presses, and every
383
+ // later one pays nothing: an empty box is returned on the first read.
384
+ stuck++;
385
+ }
386
+ }
387
+ }
388
+ /**
389
+ * Did what the box held before the paste survive it?
390
+ *
391
+ * Compares the FIRST line only, and a bounded slice of it: a long prompt makes
392
+ * the box wrap and scroll, so the rest of `before` may no longer be on screen
393
+ * even though nothing was replaced.
394
+ *
395
+ * The cost of this reading is one wasted attempt when you send the very same
396
+ * message twice in a row ("ok", "oui") — the suggestion ghost then carries your
397
+ * own words, so the paste looks like it joined them. The box is cleared, the
398
+ * retry pastes into an empty one, and the message goes out. That beats the
399
+ * alternative, which charges every single message a Ctrl-U and a 400 ms wait
400
+ * to find out what is in a box that is almost always only a ghost.
401
+ */
402
+ function survives(before, now) {
403
+ const head = before.split("\n")[0].trim().slice(0, 40);
404
+ return head.length > 0 && now.includes(head);
405
+ }
293
406
  export async function typeIntoBox(pilot, text, { attempts = 6, appearMs = 2_000, settleMs = 400, clearMs = 1_000, prelude } = {}) {
294
407
  for (let attempt = 0; attempt < attempts; attempt++) {
295
- // Only "" and the resting state count as an empty box; anything else the
296
- // box shows once we paste is our paste (whole, or collapsed).
297
- const before = inputText(pilot.screen());
408
+ // "Landed" means the box DIFFERS from what it showed just before, not that
409
+ // it is non-empty: an empty box is not always blank. Before the first prompt
410
+ // of a session, Claude Code (seen on 2.1.285) fills it with an example —
411
+ // `❯ Try "refactor <filepath>"` — so "non-empty" was true before anything
412
+ // was pasted, and a paste the TUI dropped was followed by Enter on a box
413
+ // holding nothing. Comparing with the state read just before stays
414
+ // content-agnostic (a collapsed "[Pasted text +N lines]" differs from it as
415
+ // well as the text would) and needs no list of hint strings, which would rot
416
+ // with the next release. Re-read on every attempt, after a recovery has run.
417
+ const before = boxText(pilot.screen());
298
418
  // N+1 pastes, and the LAST must be the message. Claude Code decides whether
299
419
  // a paste stays inline when it is the CURRENT one and leaves every earlier
300
420
  // one as it then became, so pasting separately is what puts the injected
@@ -309,66 +429,45 @@ export async function typeIntoBox(pilot, text, { attempts = 6, appearMs = 2_000,
309
429
  for (const chunk of prelude ? (Array.isArray(prelude) ? prelude : [prelude]) : []) {
310
430
  if (!chunk)
311
431
  continue;
312
- const seen = pilot.screen();
432
+ const seen = boxText(pilot.screen());
313
433
  pilot.paste(chunk);
314
434
  try {
315
- // The SCREEN, not `inputText`: that helper reads the `❯` line alone, so
316
- // once a chunk has filled it every later one lands on a continuation
317
- // line and the box's visible text stops moving.
318
- await pilot.waitFor((s) => s !== seen, { timeoutMs: appearMs });
435
+ await pilot.waitFor((s) => boxText(s) !== seen, { timeoutMs: appearMs });
319
436
  }
320
437
  catch {
321
438
  // A chunk never showed. Fall through and paste the rest anyway: losing
322
439
  // the context is survivable, losing the message is not.
323
440
  }
324
441
  }
325
- // The SCREEN, not `inputText`: the box's visible text is read off its `❯`
326
- // line alone, so once a prelude has filled that line everything appended
327
- // after it lands on continuation lines and `inputText` stops moving. Waiting
328
- // on it then times out on a paste that arrived perfectly — which is exactly
329
- // what the first version of this did, on every prelude long enough to wrap.
330
- const afterPrelude = pilot.screen();
442
+ const afterPrelude = boxText(pilot.screen());
331
443
  pilot.paste(text);
332
444
  try {
445
+ // With no prelude the middle term is the same check as before, unchanged.
333
446
  await pilot.waitFor((s) => {
334
- const now = inputText(s);
335
- if (now === "" || now === before)
336
- return false; // nothing in the box yet
337
- // With no prelude this is the original condition, unchanged.
338
- return !prelude || s !== afterPrelude;
447
+ const now = boxText(s);
448
+ return now !== "" && now !== before && now !== afterPrelude;
339
449
  }, { timeoutMs: appearMs });
340
- return true;
341
450
  }
342
451
  catch {
343
452
  await delay(settleMs); // let a paste that is still in flight land
344
- const isClear = (s) => {
345
- const now = inputText(s);
346
- return now === "" || now === before;
347
- };
348
- // `before` (not `beforeText`) on purpose: a retry re-pastes the prelude,
349
- // so the box has to come back to what it was before EITHER paste.
350
- // Ctrl-U clears ONE line, not the box (seen on 2.1.296): a two-line paste
351
- // takes three presses — the last line, the newline, the first line. Two
352
- // presses left the first line behind and the next attempt pasted after it,
353
- // which is how a prompt arrived as its header twice and its text nowhere.
354
- // So press until the box reads clear. Progress cannot be read off the box:
355
- // `inputText` sees its FIRST line only, which stays put while the lines
356
- // under it go. The whole screen is what moves, so a run of presses that
357
- // leaves it untouched is a screen with nothing to clear — given up on, and
358
- // left to the next attempt rather than pasted into blindly.
359
- let stuck = 0;
360
- for (let press = 0; press < MAX_CLEARS && stuck < 3; press++) {
361
- const shown = pilot.screen();
362
- pilot.clearInput();
363
- try {
364
- await pilot.waitFor(isClear, { timeoutMs: Math.min(clearMs, CLEAR_STEP_MS) });
365
- break;
366
- }
367
- catch {
368
- stuck = pilot.screen() === shown ? stuck + 1 : 0;
369
- }
370
- }
453
+ await emptyBox(pilot, clearMs);
454
+ continue;
371
455
  }
456
+ // It is in the box. On its own, or on top of what was already there?
457
+ //
458
+ // A box that reads non-empty is usually a GHOST: Claude Code offers the
459
+ // previous message as a greyed-out suggestion, and any paste wipes it out
460
+ // (verified on 2.1.301 — typing replaces it, while Ctrl-U, Ctrl-K and
461
+ // Escape all leave it exactly where it is, and backspacing to empty brings
462
+ // it back). After an interrupt, though, the box holds the real prompt the
463
+ // TUI put back, and pasting there submits two messages glued into one.
464
+ // A plain screen capture renders the two identically, so this does not
465
+ // guess: it pastes, then asks whether what was there is still there.
466
+ if (!survives(before, boxText(pilot.screen())))
467
+ return true;
468
+ // It joined the leftovers. Throw the attempt away — no settle delay, the
469
+ // paste has demonstrably landed — and start again on an empty box.
470
+ await emptyBox(pilot, clearMs);
372
471
  }
373
472
  return false;
374
473
  }
@@ -1 +1 @@
1
- {"version":3,"file":"detect.js","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"detect.js","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"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"}
package/dist/server.js CHANGED
@@ -3049,6 +3049,41 @@ function dialogSettleKey(screen) {
3049
3049
  const d = detectDialog(screen);
3050
3050
  return d && !SUBMIT_PAGE.test(screen) ? dialogKey(d) : null;
3051
3051
  }
3052
+ /**
3053
+ * Type a prompt into the TUI, holding any interrupt that arrives meanwhile.
3054
+ *
3055
+ * The window is small and it is exactly the one two fast Telegram messages fall
3056
+ * into: the first is still being pasted when the second is refused as `busy`,
3057
+ * and the bridge answers a refusal with an Escape. Pressed then, it deletes the
3058
+ * text being typed instead of stopping a turn — and an empty box is what
3059
+ * `submit` reads as "sent", so the first message disappears without a word.
3060
+ * Held until the prompt is really in, it does what it was asked to do.
3061
+ */
3062
+ async function submitGuarded(s, text, prelude) {
3063
+ s.submitting = true;
3064
+ let sent = false;
3065
+ try {
3066
+ await s.pilot.submit(text, prelude);
3067
+ sent = true;
3068
+ }
3069
+ finally {
3070
+ s.submitting = false;
3071
+ const held = s.pendingInterrupt;
3072
+ s.pendingInterrupt = undefined;
3073
+ if (held) {
3074
+ // Only a submit that LANDED started a turn to interrupt. After one that
3075
+ // failed the key is still worth pressing — it clears the box someone is
3076
+ // waiting on — but claiming a turn was interrupted would hand the next
3077
+ // one `finishTurn`'s fast settle bar on no evidence at all.
3078
+ if (sent)
3079
+ s.interruptedAt = Date.now();
3080
+ try {
3081
+ s.pilot.press(held);
3082
+ }
3083
+ catch { /* pane gone */ }
3084
+ }
3085
+ }
3086
+ }
3052
3087
  async function finishTurn(s) {
3053
3088
  s.busy = true;
3054
3089
  // Turn found already running (not started by us): typically after a server
@@ -3787,7 +3822,7 @@ wss.on("connection", (ws, req) => {
3787
3822
  if (rows.length)
3788
3823
  ledgerPrelude = formatLedgerChunks(rows, total);
3789
3824
  }
3790
- await session.pilot.submit(submitText, ledgerPrelude);
3825
+ await submitGuarded(session, submitText, ledgerPrelude);
3791
3826
  if (ledgerSeen !== null) {
3792
3827
  session.ledgerSeenAt = ledgerSeen;
3793
3828
  // The live channel list prunes the watermarks of agents that are
@@ -3824,7 +3859,7 @@ wss.on("connection", (ws, req) => {
3824
3859
  // "did the text appear?" check holds in shell mode too.
3825
3860
  session.pilot.write("!");
3826
3861
  await session.pilot.waitFor(isShellMode, { timeoutMs: 5_000 });
3827
- await session.pilot.submit(command);
3862
+ await submitGuarded(session, command);
3828
3863
  }
3829
3864
  catch (e) {
3830
3865
  // Leave no stray `!` behind: a half-entered shell mode would turn the
@@ -3948,6 +3983,12 @@ wss.on("connection", (ws, req) => {
3948
3983
  // the end state is known, so `finishTurn` may stop proving it the
3949
3984
  // slow way. Only while busy — an escape at rest ends no turn.
3950
3985
  if ((msg.key === "escape" || msg.key === "ctrl-c") && session.busy) {
3986
+ // ...but not while the prompt is still being typed: there the
3987
+ // key would delete the text rather than stop a turn. Hold it.
3988
+ if (session.submitting) {
3989
+ session.pendingInterrupt = msg.key;
3990
+ break;
3991
+ }
3951
3992
  session.interruptedAt = Date.now();
3952
3993
  }
3953
3994
  session.pilot.press(msg.key);