@livx.cc/agentx 0.99.15 → 0.99.16

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.
@@ -370,6 +370,19 @@ declare class Agent {
370
370
  * `options.tools` — otherwise a constructor-passed tool resurrects on the next prepare()
371
371
  * rebuild (and re-adding it via addTools() would then DUPLICATE it in the wire schema). */
372
372
  removeTools(names: Set<string> | string[]): number;
373
+ /** Messages the host queued to steer the RUNNING turn (Claude-Code-style mid-turn input). The loop
374
+ * drains them at each step boundary — after tool results are appended, before the next model call —
375
+ * so they ride along with the latest tool results. A no-op between turns (nothing drains it). */
376
+ private pendingInjections;
377
+ /** Queue a user message into the in-flight turn. It lands as a `user` message at the next step
378
+ * boundary; if the model was about to stop, the injection re-opens the loop so it's never lost.
379
+ * Pushing onto `transcript` directly from outside would race the loop — this drains inside it. */
380
+ inject(content: MessageContent): void;
381
+ /** Drop any queued-but-unconsumed injections. The REPL calls this after a LOCAL command (/cmd, !bash)
382
+ * — which runs no send() to drain them — so a mid-command steer can't leak into the next real turn. */
383
+ clearInjections(): void;
384
+ /** Fold any host-injected messages into the transcript as user turns. Returns true if any landed. */
385
+ private drainInjections;
373
386
  constructor(options?: Partial<AgentOptions>);
374
387
  /** Build the tool context from the resolved fs + options. Idempotent-safe: called once (ctor or ensureFs). */
375
388
  private buildCtx;
package/dist/cli.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env bun
2
- import { H as Hooks, h as RunResult, R as ReasoningEffort, A as Agent } from './Agent-CsVuR2O0.js';
2
+ import { H as Hooks, h as RunResult, R as ReasoningEffort, A as Agent } from './Agent-CvLBalJE.js';
3
3
  import { IFilesystem } from '@livx.cc/wcli/core';
4
4
  import { M as Message, H as HostBridge, c as ContentPart, e as MessageContent } from './tools-DKf3hN4M.js';
5
5
 
package/dist/cli.js CHANGED
@@ -3406,6 +3406,27 @@ var Agent = class _Agent {
3406
3406
  if (this.options.tools) this.options.tools = this.options.tools.filter((t) => !s.has(t.name));
3407
3407
  return before - this.activeTools.length;
3408
3408
  }
3409
+ /** Messages the host queued to steer the RUNNING turn (Claude-Code-style mid-turn input). The loop
3410
+ * drains them at each step boundary — after tool results are appended, before the next model call —
3411
+ * so they ride along with the latest tool results. A no-op between turns (nothing drains it). */
3412
+ pendingInjections = [];
3413
+ /** Queue a user message into the in-flight turn. It lands as a `user` message at the next step
3414
+ * boundary; if the model was about to stop, the injection re-opens the loop so it's never lost.
3415
+ * Pushing onto `transcript` directly from outside would race the loop — this drains inside it. */
3416
+ inject(content) {
3417
+ this.pendingInjections.push(content);
3418
+ }
3419
+ /** Drop any queued-but-unconsumed injections. The REPL calls this after a LOCAL command (/cmd, !bash)
3420
+ * — which runs no send() to drain them — so a mid-command steer can't leak into the next real turn. */
3421
+ clearInjections() {
3422
+ this.pendingInjections.length = 0;
3423
+ }
3424
+ /** Fold any host-injected messages into the transcript as user turns. Returns true if any landed. */
3425
+ drainInjections() {
3426
+ if (!this.pendingInjections.length) return false;
3427
+ for (const content of this.pendingInjections.splice(0)) this.transcript.push({ role: "user", content });
3428
+ return true;
3429
+ }
3409
3430
  constructor(options) {
3410
3431
  this.options = { ...new AgentOptions(), ...options };
3411
3432
  if (this.options.fs) this.buildCtx();
@@ -3671,6 +3692,7 @@ var Agent = class _Agent {
3671
3692
  });
3672
3693
  }
3673
3694
  if (toolCalls.length === 0) {
3695
+ if (this.drainInjections()) continue;
3674
3696
  log4.verbose(`completed in ${steps} step(s)`);
3675
3697
  await this.ctx.jobs?.drain();
3676
3698
  await this.activeHooks?.onStop?.(res.content ?? "");
@@ -3695,6 +3717,7 @@ var Agent = class _Agent {
3695
3717
  }
3696
3718
  this.transcript.push({ role: "tool", tool_call_id: tc.id, name: tc.function.name, content });
3697
3719
  }
3720
+ this.drainInjections();
3698
3721
  }
3699
3722
  }
3700
3723
  /**
@@ -11740,6 +11763,13 @@ function installUpdate() {
11740
11763
  const r = spawnSync6("bun", ["add", "-g", "@livx.cc/agentx"], { stdio: "inherit" });
11741
11764
  return (r.status ?? 1) === 0;
11742
11765
  }
11766
+ var typeaheadOnLine = false;
11767
+ var clearTypeaheadLine = () => {
11768
+ if (typeaheadOnLine) {
11769
+ err("\r\x1B[2K");
11770
+ typeaheadOnLine = false;
11771
+ }
11772
+ };
11743
11773
  var spinner = /* @__PURE__ */ (() => {
11744
11774
  const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
11745
11775
  let timer;
@@ -11768,8 +11798,10 @@ var spinner = /* @__PURE__ */ (() => {
11768
11798
  clearInterval(timer);
11769
11799
  timer = void 0;
11770
11800
  err("\r\x1B[2K");
11801
+ typeaheadOnLine = false;
11771
11802
  }
11772
11803
  }
11804
+ // the tail (type-ahead) went with it
11773
11805
  };
11774
11806
  })();
11775
11807
  var setTermTitle = (t) => {
@@ -11947,7 +11979,7 @@ REPL slash commands: /help /version /tools /permissions /status /cost /context /
11947
11979
  REPL completion: type / (commands+skills) or @ (files) for a LIVE menu \u2014 \u2191/\u2193 select, \u23CE/Tab accept, Esc dismiss.
11948
11980
  REPL multi-line: Option/Alt+Enter inserts a newline, or end a line with \\ to continue. Esc cancels a running turn / clears the input line; double-Esc jumps back to edit a previous message.
11949
11981
  REPL shortcuts: Shift+Tab cycles permission posture (ask \u2192 accept-edits \u2192 plan) \xB7 Alt+T toggles reasoning \xB7 Alt+P switches model \xB7 Ctrl+O toggles verbose tool output \xB7 Ctrl+X Ctrl+E edits the buffer in $EDITOR \xB7 \u2192 or Tab accepts the dim history ghost-suggestion \xB7 Alt+S/Ctrl+S stash/unstash.
11950
- REPL stash: type while a turn is running \u2192 Enter queues it (auto-submits when the turn finishes). Alt+S (or Ctrl+S) with text stashes it; on an empty prompt pops the next entry for editing.
11982
+ REPL steer/stash: type while a turn is running \u2192 Enter STEERS it (the text reaches the model at its next step, alongside the next tool result). Alt+S (or Ctrl+S) with text stashes it for AFTER the turn instead; on an empty prompt pops the next stashed entry for editing.
11951
11983
  REPL editing (emacs/readline): Ctrl-A/E line start/end \xB7 Ctrl-B/F char \xB7 Alt-B/F or Alt/Ctrl-\u2190/\u2192 word \xB7 Ctrl-W kill word \xB7 Ctrl-U/K kill to start/end \xB7 Ctrl-Y yank \xB7 Ctrl-_ undo \xB7 Alt-D kill word fwd \xB7 Ctrl-L clear screen. Set editorMode:'vim' (or /config) for modal vim editing.
11952
11984
  REPL paste: large/multi-line pastes collapse to a [Pasted text +N lines] preview (expands on send); a pasted image/file path attaches as [Image]/[File]; Ctrl-V or /paste grabs a clipboard image (macOS).`;
11953
11985
  function newestModel() {
@@ -12035,9 +12067,11 @@ function makeHost(format = "text", opts) {
12035
12067
  notify(e) {
12036
12068
  if (e.kind === "turn_start") {
12037
12069
  activitySeen.clear();
12070
+ typeaheadOnLine = false;
12038
12071
  return;
12039
12072
  }
12040
12073
  spinner.stop();
12074
+ clearTypeaheadLine();
12041
12075
  if (e.kind === "text_delta") {
12042
12076
  flushLookupGroup();
12043
12077
  if (streamJson) process.stdout.write(JSON.stringify({ type: "text", text: e.message }) + "\n");
@@ -12184,7 +12218,10 @@ function displayHooks(fs, opts) {
12184
12218
  if (call.name === "TodoWrite" && Array.isArray(call.args?.todos)) latestTodos = call.args.todos;
12185
12219
  if (!on()) return;
12186
12220
  if (bg) err("\r\x1B[0J");
12187
- else spinner.stop();
12221
+ else {
12222
+ spinner.stop();
12223
+ clearTypeaheadLine();
12224
+ }
12188
12225
  flush();
12189
12226
  if (!bg && !verboseOutput && LOOKUP.has(call.name)) {
12190
12227
  pendingHeader = header(call);
@@ -12205,7 +12242,10 @@ function displayHooks(fs, opts) {
12205
12242
  async postToolUse(call, result) {
12206
12243
  if (!on()) return;
12207
12244
  if (bg) err("\r\x1B[0J");
12208
- else spinner.stop();
12245
+ else {
12246
+ spinner.stop();
12247
+ clearTypeaheadLine();
12248
+ }
12209
12249
  try {
12210
12250
  if (pendingHeader) {
12211
12251
  const t = String(result).replace(/\s+$/, "");
@@ -14662,6 +14702,7 @@ ${task}`;
14662
14702
  const renderStashBuf = () => {
14663
14703
  if (spinner.active) return;
14664
14704
  err(`\r\x1B[K${sEd.buf ? " " + stashView() : ""}`);
14705
+ typeaheadOnLine = !!sEd.buf;
14665
14706
  };
14666
14707
  repaintStash = renderStashBuf;
14667
14708
  keyInput.on("keypress", (_s, key) => {
@@ -14694,11 +14735,18 @@ ${task}`;
14694
14735
  if (action === "submit") {
14695
14736
  const text = sEd.expand().trim();
14696
14737
  sEd.reset();
14738
+ typeaheadOnLine = false;
14697
14739
  if (text) {
14698
- inputStash.push(text);
14699
14740
  const view = text.replace(/\n+/g, " \u23CE ");
14700
- err(`\r\x1B[K${green(" \u2713 stashed")} ${dim(`#${inputStash.length}: ${view.slice(0, 50)}${view.length > 50 ? "\u2026" : ""}`)}
14741
+ if ((activeTurn || dispatchPending) && !duplex) {
14742
+ agent.inject(text);
14743
+ err(`\r\x1B[K${bold(cyan(" \u21B3 steering \u203A"))} ${cyan(`${view.slice(0, 60)}${view.length > 60 ? "\u2026" : ""}`)}
14744
+ `);
14745
+ } else {
14746
+ inputStash.push(text);
14747
+ err(`\r\x1B[K${green(" \u2713 stashed")} ${dim(`#${inputStash.length}: ${view.slice(0, 50)}${view.length > 50 ? "\u2026" : ""}`)}
14701
14748
  `);
14749
+ }
14702
14750
  }
14703
14751
  return;
14704
14752
  }
@@ -15079,6 +15127,7 @@ ${out}
15079
15127
  }
15080
15128
  let quit = await dispatchLine(line) === "quit";
15081
15129
  const wasLocal = /^[!#/]/.test(line);
15130
+ if (wasLocal) agent.clearInjections();
15082
15131
  while (!quit && !wasLocal && inputStash.length) {
15083
15132
  const next = inputStash.shift();
15084
15133
  const nview = next.replace(/\n+/g, " \u23CE ");