@livx.cc/agentx 0.99.110 → 0.99.112

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/cli.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env bun
2
- import { o as RouterState, H as Hooks, p as RunResult, R as ReasoningEffort, C as ChannelMode, A as Agent } from './secondChannel-3IPP3Qgh.js';
2
+ import { o as RouterState, H as Hooks, p as RunResult, R as ReasoningEffort, C as ChannelMode, A as Agent } from './secondChannel-CJMY5EbL.js';
3
3
  import { IFilesystem } from '@livx.cc/wcli/core';
4
4
  import { M as Message, U as UserQuestion, H as HostBridge, c as ContentPart, o as MessageContent } from './tools-DZHZIyxh.js';
5
5
 
@@ -375,5 +375,9 @@ declare function runTurn(agent: Agent, store: SessionStore, session: SessionData
375
375
  }>;
376
376
  /** A failed duplex turn's cause, for the host to show: the provider's message, and for a missing key what to set. */
377
377
  declare function duplexError(e: unknown, reflexModel: string): string;
378
+ /** --keep-alive {type:'rollback'}: take back the turn that began at transcript index `start` — its user line, steers and
379
+ * (partial) reply leave this session and, on claude-code/*, Claude Code's own history too (rewindClaudeCodeSession) — so
380
+ * the next turn (a re-final's fuller text) runs warm, as if that one was never sent. Returns the messages dropped. */
381
+ declare function rollbackTurn(agent: Agent, store: Pick<SessionStore, 'save'>, session: SessionData, start: number): Promise<number>;
378
382
 
379
- export { type PermMode, abortActiveTurn, apiKeysFromEnv, appendMemoryNote, cacheMultipliers, condenseReplay, costOf, duplexError, duplexModels, estimateTranscriptTokens, expandMentions, exportMarkdown, fmtUsd, formatHistory, formatStatus, formatTranscriptFull, jsonResult, loadBodifySecrets, loadInstallEnv, makeHost, mcpMentionResolver, mentionRefs, modelUsable, parseArgs, pastePathClassifier, readImageParts, readMultiline, resolvePermMode, runShellLine, runTurn, setMcpMentionResolver, summarizeResult, turnCost };
383
+ export { type PermMode, abortActiveTurn, apiKeysFromEnv, appendMemoryNote, cacheMultipliers, condenseReplay, costOf, duplexError, duplexModels, estimateTranscriptTokens, expandMentions, exportMarkdown, fmtUsd, formatHistory, formatStatus, formatTranscriptFull, jsonResult, loadBodifySecrets, loadInstallEnv, makeHost, mcpMentionResolver, mentionRefs, modelUsable, parseArgs, pastePathClassifier, readImageParts, readMultiline, resolvePermMode, rollbackTurn, runShellLine, runTurn, setMcpMentionResolver, summarizeResult, turnCost };
package/dist/cli.js CHANGED
@@ -3432,7 +3432,7 @@ function copyTextToClipboard(text, platform2 = process.platform) {
3432
3432
 
3433
3433
  // cli/cli.ts
3434
3434
  import { join as join16, resolve as resolve5, basename as basename4, extname, dirname as dirname6 } from "path";
3435
- import { AIClient as AIClient2, listModels, listProviders, getProviderFromModel, getModelInfo, resolveModel, isModelSupported, disposeCursorSessions, disposeClaudeCodeSessions } from "ai.libx.js";
3435
+ import { AIClient as AIClient2, listModels, listProviders, getProviderFromModel, getModelInfo, resolveModel, isModelSupported, disposeCursorSessions, disposeClaudeCodeSessions, rewindClaudeCodeSession } from "ai.libx.js";
3436
3436
 
3437
3437
  // src/ccBridge.ts
3438
3438
  var CC_BRIDGED = /* @__PURE__ */ new Set(["Recall", "Remember", "MemorySearch", "Forget"]);
@@ -5022,6 +5022,39 @@ var Agent = class _Agent {
5022
5022
  * steers over its own channel (claude-code's warm InputQueue via `providerOptions.steer`). Non-text
5023
5023
  * injections (images) stay queued for the step-boundary `drainInjections()` re-open — the text-only
5024
5024
  * steer channel can't carry them. Consumed DURING chat(); no overlap with drainInjections() (post-call). */
5025
+ /** claude-code/*: the providerOptions every step adds (and prewarm, so the session it starts is the one step 1 reuses). */
5026
+ claudeCodePo() {
5027
+ const o = this.options;
5028
+ if (!o.model.startsWith("claude-code/")) return void 0;
5029
+ const po = { steer: () => this.drainPendingSteers() };
5030
+ const bridged = (t) => o.ccBridge === "all" || CC_BRIDGED.has(t.name);
5031
+ if (o.providerOptions?.claudeCodeSession && this.activeTools.some(bridged)) {
5032
+ this.ccServer ??= ccToolServer("agentx", () => this.activeTools.filter(bridged), (n, a) => this.ccExec(n, a));
5033
+ po.mcpServers = { ...o.providerOptions?.mcpServers, agentx: this.ccServer };
5034
+ }
5035
+ const denyTools = claudeCodeDisallowedTools(o.permissions);
5036
+ if (denyTools.length) {
5037
+ const prior = o.providerOptions?.disallowedTools ?? [];
5038
+ po.disallowedTools = [.../* @__PURE__ */ new Set([...prior, ...denyTools])];
5039
+ }
5040
+ return po;
5041
+ }
5042
+ /** claude-code/* warm session: start it now (its process, MCP handshakes) so the first turn doesn't pay for it. Its
5043
+ * system prompt is fixed at the start, so it is prepared here (no task hint) — memoized, the turns reuse the same one.
5044
+ * No model call; the transcript gains only that system message. Other models: nothing to warm. */
5045
+ async prewarm() {
5046
+ const o = this.options;
5047
+ if (!o.model.startsWith("claude-code/") || !o.providerOptions?.claudeCodeSession) return;
5048
+ await this.ensureFs();
5049
+ const systemPrompt = await this.prepare(), startCtx = await this.fireSessionStart();
5050
+ const sys = { role: "system", content: systemPrompt + (startCtx ? "\n\n" + startCtx : "") };
5051
+ if (this.transcript[0]?.role === "system") this.transcript[0] = sys;
5052
+ else this.transcript.unshift(sys);
5053
+ const po = this.claudeCodePo(), frag = reasoningToChatFragment(o.model, o.reasoning);
5054
+ const r = await o.ai.chat({ model: o.model, messages: this.trimContext(), tools: [], stream: true, ...frag, ...o.promptCache ? { promptCache: true } : {}, providerOptions: { ...frag.providerOptions, ...o.providerOptions, ...po, prewarm: true } });
5055
+ for await (const _ of r) {
5056
+ }
5057
+ }
5025
5058
  drainPendingSteers() {
5026
5059
  if (!this.pendingInjections.length) return [];
5027
5060
  const texts = [];
@@ -5314,18 +5347,8 @@ var Agent = class _Agent {
5314
5347
  }
5315
5348
  };
5316
5349
  const cursorPo = isCursorWithTools ? { toolTimeoutMs: effectiveToolTimeoutMs, toolExecutor: hostExec } : void 0;
5317
- const claudeSteerPo = o.model.startsWith("claude-code/") ? { steer: () => this.drainPendingSteers() } : void 0;
5318
- const bridged = (t) => o.ccBridge === "all" || CC_BRIDGED.has(t.name);
5319
- if (claudeSteerPo && o.providerOptions?.claudeCodeSession && this.activeTools.some(bridged)) {
5320
- this.ccServer ??= ccToolServer("agentx", () => this.activeTools.filter(bridged), (n, a) => this.ccExec(n, a));
5321
- claudeSteerPo.mcpServers = { ...o.providerOptions?.mcpServers, agentx: this.ccServer };
5322
- }
5350
+ const claudeSteerPo = this.claudeCodePo();
5323
5351
  if (this.ccServer) this.ccExec = hostExec;
5324
- const denyTools = o.model.startsWith("claude-code/") ? claudeCodeDisallowedTools(o.permissions) : [];
5325
- if (denyTools.length) {
5326
- const prior = o.providerOptions?.disallowedTools ?? [];
5327
- claudeSteerPo.disallowedTools = [.../* @__PURE__ */ new Set([...prior, ...denyTools])];
5328
- }
5329
5352
  const cursorDeny = o.model.startsWith("cursor/") ? o.permissions?.denyRules(true) ?? [] : [];
5330
5353
  if (cursorDeny.length)
5331
5354
  throw new Error(`cursor/* cannot enforce --disallowedTools (${cursorDeny.join(", ")}): its native tools bypass them. Refusing to run \u2014 use a claude-code/* or API model, or drop --disallowedTools.`);
@@ -18837,7 +18860,10 @@ Flags:
18837
18860
  (unconsumed steers come back as the result's \`unsteered\`)
18838
18861
  --keep-alive with -p --input-format stream-json: stay up after a turn \u2014 each {"type":"user"} line while idle
18839
18862
  is the NEXT turn (warm: same session, model connection and MCP servers), one {type:result}
18840
- per turn; {"type":"abort"} stops the running turn; stdin EOF exits
18863
+ per turn; it boots (and warms a claude-code/* session) before the first line arrives;
18864
+ {"type":"abort"} stops the running turn (the next one is still warm); {"type":"rollback"}
18865
+ takes the last turn back (stopping it if running) \u2014 its user line and reply leave the
18866
+ session, Claude Code's own history included; stdin EOF exits
18841
18867
  --output-format <f> headless output: text (default) | json (one result object) | stream-json (NDJSON events: {type:text|thinking}\u2026 then {type:result})
18842
18868
  -v, --version print version and exit
18843
18869
  -h, --help
@@ -22574,10 +22600,14 @@ async function channelServe(args, ai, cfg) {
22574
22600
  sendTurn: (text, o) => out({ type: "turn", id: o.id, text, ...o.replaces ? { replaces: o.replaces } : {} }),
22575
22601
  abortTurn: (id) => out({ type: "abort", id }),
22576
22602
  onMetric: (m) => {
22577
- if (m.name === "first_sound" || m.name === "first_word" || m.name === "drop" || m.name === "hold") out({ type: "metric", ...m });
22603
+ if (m.name !== "lang") out({ type: "metric", ...m });
22578
22604
  }
22605
+ // (turn + fill too: the host's log shows which turns formed and which fill lines played)
22606
+ });
22607
+ const overlap2 = overlapResponder(ai, args.channelMode, (o) => {
22608
+ if (o.verdict === "cut") call.barge();
22609
+ out(o);
22579
22610
  });
22580
- const overlap2 = overlapResponder(ai, args.channelMode, out);
22581
22611
  out({ type: "ready", model, channelMode: args.channelMode, softBarge: softBarge(args.channelMode), call: true, ...notes.length ? { notes } : {} });
22582
22612
  const rl = createInterface({ input: process.stdin });
22583
22613
  rl.on("line", (line) => {
@@ -22604,7 +22634,7 @@ async function channelServe(args, ai, cfg) {
22604
22634
  }
22605
22635
  function readStdinSteers(keepAlive = false) {
22606
22636
  const early = [];
22607
- let sink, got, closed = false;
22637
+ let sink, got, closed = false, started = false;
22608
22638
  const first = new Promise((res) => {
22609
22639
  got = res;
22610
22640
  });
@@ -22620,7 +22650,17 @@ function readStdinSteers(keepAlive = false) {
22620
22650
  activeTurn?.abort();
22621
22651
  return;
22622
22652
  }
22653
+ if (keepAlive && m?.type === "rollback") {
22654
+ if (!started) return;
22655
+ activeTurn?.abort();
22656
+ if (got) {
22657
+ got({ type: "rollback" });
22658
+ got = void 0;
22659
+ } else early.push({ type: "rollback" });
22660
+ return;
22661
+ }
22623
22662
  if (m?.type !== "user" || typeof m.text !== "string" || !m.text.trim()) return;
22663
+ started = true;
22624
22664
  if (got) {
22625
22665
  got(m.text);
22626
22666
  got = void 0;
@@ -22640,9 +22680,21 @@ function readStdinSteers(keepAlive = false) {
22640
22680
  };
22641
22681
  return { first, steer: (to) => {
22642
22682
  sink = to;
22643
- early.splice(0).forEach(to);
22683
+ while (typeof early[0] === "string") to(early.shift());
22644
22684
  }, next };
22645
22685
  }
22686
+ async function rollbackTurn(agent, store, session, start) {
22687
+ if (start < 0 || start > agent.transcript.length) return 0;
22688
+ const dropped = agent.transcript.length - start;
22689
+ agent.transcript.splice(start);
22690
+ session.messages = agent.transcript;
22691
+ session.meta.updated = Date.now();
22692
+ session.meta.cc = void 0;
22693
+ const cc = agent.options.providerOptions?.claudeCodeSession;
22694
+ if (typeof cc === "string" && agent.options.model.startsWith("claude-code/")) await rewindClaudeCodeSession(cc);
22695
+ store.save(session);
22696
+ return dropped;
22697
+ }
22646
22698
  function readAllStdin() {
22647
22699
  return new Promise((res) => {
22648
22700
  let data = "";
@@ -22677,9 +22729,11 @@ async function main() {
22677
22729
  }
22678
22730
  if (args.debug) process.env.DEBUG ||= "*";
22679
22731
  const steers = args.print && args.inputFormat && !args.task ? readStdinSteers(args.keepAlive) : void 0;
22680
- if (steers) args.task = (await steers.first).trim();
22681
- else if (args.print && !args.task && !process.stdin.isTTY) args.task = (await readAllStdin()).trim();
22682
- if (args.print && !args.task) {
22732
+ const warmBoot = !!(steers && args.keepAlive);
22733
+ if (steers && !warmBoot) args.task = (await steers.first).trim();
22734
+ else if (warmBoot) {
22735
+ } else if (args.print && !args.task && !process.stdin.isTTY) args.task = (await readAllStdin()).trim();
22736
+ if (args.print && !args.task && !warmBoot) {
22683
22737
  console.error(red(" -p: empty prompt (no task argument and nothing on stdin)"));
22684
22738
  process.exit(2);
22685
22739
  }
@@ -22746,8 +22800,8 @@ async function main() {
22746
22800
  }
22747
22801
  }
22748
22802
  });
22749
- if (args.task) {
22750
- const compactArg = /^\/compact(?:\s+([\s\S]*))?$/.exec(args.task.trim());
22803
+ if (args.task || warmBoot) {
22804
+ const compactArg = /^\/compact(?:\s+([\s\S]*))?$/.exec((args.task ?? "").trim());
22751
22805
  if (compactArg) {
22752
22806
  const agent2 = await makeAgent(args, ai, cfg, [], []), store2 = new SessionStore(cwd), session2 = startSession(args, store2, agent2, cwd);
22753
22807
  const est = (m) => estimateTranscriptTokens(m, agent2.options.keepRecentImages, agent2.options.imageInput?.(agent2.options.model) !== false);
@@ -22770,8 +22824,22 @@ async function main() {
22770
22824
  process.once("SIGINT", () => activeTurn?.abort());
22771
22825
  steers?.steer((text) => agent.inject(text));
22772
22826
  const turn = (task) => runTurn(agent, store, session, task, void 0, cwd, autoRouteSend(args, agent, () => session, (m) => providerOptionsFor(m, cwd, cfg.mcpServers, mounted.map((x) => x.name))));
22773
- if (args.keepAlive && steers) {
22774
- for (let task = args.task; task.trim(); task = (await steers.next()).trim()) {
22827
+ if (warmBoot && steers) {
22828
+ bindClaudeCodeSession(agent, session);
22829
+ await agent.prewarm().catch((e) => err(dim(` (prewarm failed: ${e?.message ?? e})
22830
+ `)));
22831
+ let start = -1;
22832
+ for (let line = await steers.first; ; line = await steers.next()) {
22833
+ if (typeof line !== "string") {
22834
+ const n = await rollbackTurn(agent, store, session, start);
22835
+ start = -1;
22836
+ err(dim(` \u21B6 rolled back the last turn (${n} message${n === 1 ? "" : "s"})
22837
+ `));
22838
+ continue;
22839
+ }
22840
+ const task = line.trim();
22841
+ if (!task) break;
22842
+ start = agent.transcript.length;
22775
22843
  steers.steer((text) => agent.inject(text));
22776
22844
  const { res: res2 } = await turn(task);
22777
22845
  const left = agent.drainPendingSteers();
@@ -22841,6 +22909,7 @@ export {
22841
22909
  readImageParts,
22842
22910
  readMultiline,
22843
22911
  resolvePermMode,
22912
+ rollbackTurn,
22844
22913
  runShellLine,
22845
22914
  runTurn,
22846
22915
  setMcpMentionResolver,