@livx.cc/agentx 0.99.110 → 0.99.111

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
@@ -22604,7 +22630,7 @@ async function channelServe(args, ai, cfg) {
22604
22630
  }
22605
22631
  function readStdinSteers(keepAlive = false) {
22606
22632
  const early = [];
22607
- let sink, got, closed = false;
22633
+ let sink, got, closed = false, started = false;
22608
22634
  const first = new Promise((res) => {
22609
22635
  got = res;
22610
22636
  });
@@ -22620,7 +22646,17 @@ function readStdinSteers(keepAlive = false) {
22620
22646
  activeTurn?.abort();
22621
22647
  return;
22622
22648
  }
22649
+ if (keepAlive && m?.type === "rollback") {
22650
+ if (!started) return;
22651
+ activeTurn?.abort();
22652
+ if (got) {
22653
+ got({ type: "rollback" });
22654
+ got = void 0;
22655
+ } else early.push({ type: "rollback" });
22656
+ return;
22657
+ }
22623
22658
  if (m?.type !== "user" || typeof m.text !== "string" || !m.text.trim()) return;
22659
+ started = true;
22624
22660
  if (got) {
22625
22661
  got(m.text);
22626
22662
  got = void 0;
@@ -22640,9 +22676,21 @@ function readStdinSteers(keepAlive = false) {
22640
22676
  };
22641
22677
  return { first, steer: (to) => {
22642
22678
  sink = to;
22643
- early.splice(0).forEach(to);
22679
+ while (typeof early[0] === "string") to(early.shift());
22644
22680
  }, next };
22645
22681
  }
22682
+ async function rollbackTurn(agent, store, session, start) {
22683
+ if (start < 0 || start > agent.transcript.length) return 0;
22684
+ const dropped = agent.transcript.length - start;
22685
+ agent.transcript.splice(start);
22686
+ session.messages = agent.transcript;
22687
+ session.meta.updated = Date.now();
22688
+ session.meta.cc = void 0;
22689
+ const cc = agent.options.providerOptions?.claudeCodeSession;
22690
+ if (typeof cc === "string" && agent.options.model.startsWith("claude-code/")) await rewindClaudeCodeSession(cc);
22691
+ store.save(session);
22692
+ return dropped;
22693
+ }
22646
22694
  function readAllStdin() {
22647
22695
  return new Promise((res) => {
22648
22696
  let data = "";
@@ -22677,9 +22725,11 @@ async function main() {
22677
22725
  }
22678
22726
  if (args.debug) process.env.DEBUG ||= "*";
22679
22727
  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) {
22728
+ const warmBoot = !!(steers && args.keepAlive);
22729
+ if (steers && !warmBoot) args.task = (await steers.first).trim();
22730
+ else if (warmBoot) {
22731
+ } else if (args.print && !args.task && !process.stdin.isTTY) args.task = (await readAllStdin()).trim();
22732
+ if (args.print && !args.task && !warmBoot) {
22683
22733
  console.error(red(" -p: empty prompt (no task argument and nothing on stdin)"));
22684
22734
  process.exit(2);
22685
22735
  }
@@ -22746,8 +22796,8 @@ async function main() {
22746
22796
  }
22747
22797
  }
22748
22798
  });
22749
- if (args.task) {
22750
- const compactArg = /^\/compact(?:\s+([\s\S]*))?$/.exec(args.task.trim());
22799
+ if (args.task || warmBoot) {
22800
+ const compactArg = /^\/compact(?:\s+([\s\S]*))?$/.exec((args.task ?? "").trim());
22751
22801
  if (compactArg) {
22752
22802
  const agent2 = await makeAgent(args, ai, cfg, [], []), store2 = new SessionStore(cwd), session2 = startSession(args, store2, agent2, cwd);
22753
22803
  const est = (m) => estimateTranscriptTokens(m, agent2.options.keepRecentImages, agent2.options.imageInput?.(agent2.options.model) !== false);
@@ -22770,8 +22820,22 @@ async function main() {
22770
22820
  process.once("SIGINT", () => activeTurn?.abort());
22771
22821
  steers?.steer((text) => agent.inject(text));
22772
22822
  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()) {
22823
+ if (warmBoot && steers) {
22824
+ bindClaudeCodeSession(agent, session);
22825
+ await agent.prewarm().catch((e) => err(dim(` (prewarm failed: ${e?.message ?? e})
22826
+ `)));
22827
+ let start = -1;
22828
+ for (let line = await steers.first; ; line = await steers.next()) {
22829
+ if (typeof line !== "string") {
22830
+ const n = await rollbackTurn(agent, store, session, start);
22831
+ start = -1;
22832
+ err(dim(` \u21B6 rolled back the last turn (${n} message${n === 1 ? "" : "s"})
22833
+ `));
22834
+ continue;
22835
+ }
22836
+ const task = line.trim();
22837
+ if (!task) break;
22838
+ start = agent.transcript.length;
22775
22839
  steers.steer((text) => agent.inject(text));
22776
22840
  const { res: res2 } = await turn(task);
22777
22841
  const left = agent.drainPendingSteers();
@@ -22841,6 +22905,7 @@ export {
22841
22905
  readImageParts,
22842
22906
  readMultiline,
22843
22907
  resolvePermMode,
22908
+ rollbackTurn,
22844
22909
  runShellLine,
22845
22910
  runTurn,
22846
22911
  setMcpMentionResolver,