agent.libx.js 0.93.18 → 0.93.19

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/cli/cli.ts CHANGED
@@ -1035,6 +1035,9 @@ async function repl(args: Args, ai: ChatLike, cfg: Partial<AgentConfig>, cwd: st
1035
1035
  let dx: DuplexAgent | undefined;
1036
1036
  let voiceIO: VoiceIO | undefined; // real voice I/O (--voice + keys): mic→STT in, text_delta→TTS out
1037
1037
  let editorRef: LineEditor | undefined; // bound once the line editor exists — async chrome repaints the prompt via it
1038
+ // During a turn the user's type-ahead lives on a "stash ›" line (no active editor to own it). Async
1039
+ // chrome (streamed deltas, task events) lands on top of it — repaint the stash below, so it survives.
1040
+ let repaintStash: () => void = () => {};
1038
1041
  let workerOptions: AgentOptions | undefined;
1039
1042
  // Worker UI verbosity: 'full' = ⚙ tool chrome per worker step; 'minimal' = task events only
1040
1043
  // (started/progress/⦿ done). Voice defaults minimal (chrome is noise next to speech); /workers toggles live.
@@ -1110,6 +1113,13 @@ async function repl(args: Args, ai: ChatLike, cfg: Partial<AgentConfig>, cwd: st
1110
1113
  if (e.kind === 'text_delta' && voiceIO) {
1111
1114
  voiceIO.speakDelta(e.message);
1112
1115
  editorRef?.suspend(); // no-op when already suspended
1116
+ } else if (e.kind === 'text_delta' && stashBuf) {
1117
+ // TEXT mode with type-ahead pending: lift the sacred stash line, let the markdown stream
1118
+ // land, then repaint the stash below it (else the streamed ack overwrites what's being typed).
1119
+ process.stdout.write('\r\x1b[K');
1120
+ base.notify!(e);
1121
+ repaintStash();
1122
+ return;
1113
1123
  }
1114
1124
  if (e.kind === 'hold_filler' && voiceIO) {
1115
1125
  voiceIO.speakFiller(e.message);
@@ -1135,6 +1145,7 @@ async function repl(args: Args, ai: ChatLike, cfg: Partial<AgentConfig>, cwd: st
1135
1145
  if (lines.length > shown.length) err(dim(` … (+${lines.length - shown.length} more lines)\n`));
1136
1146
  duplexAccount(e.data); // worker tokens/cost are real spend — fold into /cost
1137
1147
  editorRef?.redrawNow();
1148
+ repaintStash(); // type-ahead survives the async ⦿ landing
1138
1149
  return;
1139
1150
  }
1140
1151
  // Remaining task_* events (started/progress/cancelled/error) land ASYNC at a live prompt —
@@ -1145,6 +1156,7 @@ async function repl(args: Args, ai: ChatLike, cfg: Partial<AgentConfig>, cwd: st
1145
1156
  // asks are decisions, not chatter — make them stand out (the voice also speaks them)
1146
1157
  err('\r\x1b[0J' + (e.kind === 'task_ask' ? yellow(` ? ${e.message} — answer by voice or type yes/no\n`) : dim(` · ${e.message}\n`)));
1147
1158
  editorRef?.redrawNow();
1159
+ repaintStash(); // type-ahead survives the async task event landing
1148
1160
  return;
1149
1161
  }
1150
1162
  base.notify!(e); // makeHost always provides notify (the HostBridge type just marks it optional)
@@ -1921,6 +1933,8 @@ async function repl(args: Args, ai: ChatLike, cfg: Partial<AgentConfig>, cwd: st
1921
1933
  const q = inputStash.length ? dim(` [${inputStash.length} queued]`) : '';
1922
1934
  err(`\r\x1b[K${dim(' stash › ')}${stashBuf}${q}`);
1923
1935
  };
1936
+ repaintStash = renderStashBuf; // async chrome repaints the type-ahead line through this
1937
+
1924
1938
  process.stdin.on('keypress', (_s, key) => {
1925
1939
  if (!activeTurn) return;
1926
1940
  if (key?.ctrl && key?.name === 'o') { toggleVerbose(); return; }
@@ -2064,9 +2078,12 @@ async function repl(args: Args, ai: ChatLike, cfg: Partial<AgentConfig>, cwd: st
2064
2078
  while (true) {
2065
2079
  // Double-Esc fired during the just-finished turn → open the jump-back picker now (turn has unwound).
2066
2080
  if (pendingRewind) { pendingRewind = false; const t = await rewindToMessage(); if (t !== undefined) prefill = t; }
2067
- aborting = false; stashBuf = ''; // re-arm cancel for the next turn
2081
+ aborting = false;
2082
+ const carry = stashBuf; stashBuf = ''; // type-ahead typed (not Enter'd) during the turn → carry it forward
2068
2083
  err('\n'); // blank line before the prompt (the editor renders on one line)
2069
- const initial = prefill; prefill = undefined; // consume the pending jump-back text (once)
2084
+ // Consume the pending jump-back text (once); else fall back to un-submitted type-ahead so a line
2085
+ // typed while the turn ran isn't lost — it lands editable in the fresh prompt (CC parity).
2086
+ const initial = prefill ?? (carry || undefined); prefill = undefined;
2070
2087
  // Dim status footer under the prompt (context% · cost). Constant while typing one line (transcript is
2071
2088
  // fixed until submit), so compute once per iteration. Hidden on a fresh REPL (no usage yet) to stay clean.
2072
2089
  const ctxTok = estimateTranscriptTokens(face.transcript); // expensive: compute once per iteration (not per keystroke)
package/dist/cli.js CHANGED
@@ -8600,6 +8600,8 @@ async function repl(args, ai, cfg, cwd) {
8600
8600
  let dx;
8601
8601
  let voiceIO;
8602
8602
  let editorRef;
8603
+ let repaintStash = () => {
8604
+ };
8603
8605
  let workerOptions;
8604
8606
  let workerChrome = "full";
8605
8607
  let duplexPersist = () => {
@@ -8655,6 +8657,11 @@ async function repl(args, ai, cfg, cwd) {
8655
8657
  if (e.kind === "text_delta" && voiceIO) {
8656
8658
  voiceIO.speakDelta(e.message);
8657
8659
  editorRef?.suspend();
8660
+ } else if (e.kind === "text_delta" && stashBuf) {
8661
+ process.stdout.write("\r\x1B[K");
8662
+ base.notify(e);
8663
+ repaintStash();
8664
+ return;
8658
8665
  }
8659
8666
  if (e.kind === "hold_filler" && voiceIO) {
8660
8667
  voiceIO.speakFiller(e.message);
@@ -8679,6 +8686,7 @@ async function repl(args, ai, cfg, cwd) {
8679
8686
  `));
8680
8687
  duplexAccount(e.data);
8681
8688
  editorRef?.redrawNow();
8689
+ repaintStash();
8682
8690
  return;
8683
8691
  }
8684
8692
  if (typeof e.kind === "string" && e.kind.startsWith("task_")) {
@@ -8687,6 +8695,7 @@ async function repl(args, ai, cfg, cwd) {
8687
8695
  `) : dim(` \xB7 ${e.message}
8688
8696
  `)));
8689
8697
  editorRef?.redrawNow();
8698
+ repaintStash();
8690
8699
  return;
8691
8700
  }
8692
8701
  base.notify(e);
@@ -9681,6 +9690,7 @@ ${extra}` : body);
9681
9690
  const q2 = inputStash.length ? dim(` [${inputStash.length} queued]`) : "";
9682
9691
  err(`\r\x1B[K${dim(" stash \u203A ")}${stashBuf}${q2}`);
9683
9692
  };
9693
+ repaintStash = renderStashBuf;
9684
9694
  process.stdin.on("keypress", (_s, key) => {
9685
9695
  if (!activeTurn) return;
9686
9696
  if (key?.ctrl && key?.name === "o") {
@@ -9870,9 +9880,10 @@ ${extra}` : body);
9870
9880
  if (t !== void 0) prefill = t;
9871
9881
  }
9872
9882
  aborting = false;
9883
+ const carry = stashBuf;
9873
9884
  stashBuf = "";
9874
9885
  err("\n");
9875
- const initial = prefill;
9886
+ const initial = prefill ?? (carry || void 0);
9876
9887
  prefill = void 0;
9877
9888
  const ctxTok = estimateTranscriptTokens(face.transcript);
9878
9889
  const ctxCap = face.options.maxTokens || 2e5;