@livx.cc/agentx 0.99.109 → 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.`);
@@ -10515,6 +10538,14 @@ ${context.trim()}
10515
10538
  </host_context>
10516
10539
  (Above: context and instructions from the app \u2014 NOT the user's words. Never read it aloud, quote it, or attribute it to the user. The user's line follows.)
10517
10540
 
10541
+ `;
10542
+ }
10543
+ function hostNoteBlock(note) {
10544
+ return `<host_note>
10545
+ ${note.trim()}
10546
+ </host_note>
10547
+ (Above: background from the app \u2014 NOT the user's words on this line. Never read it aloud as theirs. The user's line follows.)
10548
+
10518
10549
  `;
10519
10550
  }
10520
10551
  function nowLine() {
@@ -11010,9 +11041,9 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
11010
11041
  /** One reflex model call for a turn; a turn rejected for load (rateLimited) that has not spoken or dispatched yet is rolled
11011
11042
  * back and re-run once on `reflexFallbackModel` — a re-prompt of the same model (the dead-air nudge) only hits the
11012
11043
  * same limit and ends in "could you say that again?". */
11013
- async reflexSend(content, context) {
11014
- const base = this.voice.transcript.length;
11015
- const res = await this.sendWithContext(content, context);
11044
+ async reflexSend(content, note) {
11045
+ const base = this.voice.transcript.length, context = this.lastContext;
11046
+ const res = await this.sendWithContext(content, context, note);
11016
11047
  const fb = this.options.reflexFallbackModel, model = this.voice.options.model;
11017
11048
  if (res.finishReason !== "error" || !rateLimited(res.error) || !fb || fb === model || fb.includes("/") && model.includes("/") && fb.split("/")[0] !== model.split("/")[0] || this.turnDispatched || this.turnSpoken.trim()) return res;
11018
11049
  log23.warn(`reflex ${model} rate-limited \u2014 re-running the turn on ${fb}`);
@@ -11020,7 +11051,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
11020
11051
  this.voice.transcript.length = base;
11021
11052
  this.voice.options.model = fb;
11022
11053
  try {
11023
- return await this.sendWithContext(content, context);
11054
+ return await this.sendWithContext(content, context, note);
11024
11055
  } finally {
11025
11056
  this.voice.options.model = model;
11026
11057
  }
@@ -11029,16 +11060,18 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
11029
11060
  * inside the user turn it read as the user's own words (live #938: "repeat everything I said" recited the call rules). The
11030
11061
  * stored turn keeps only what the user said, so later turns, briefs and the saved session never carry it. A block, not a
11031
11062
  * mid-transcript system message: the Anthropic/Google adapters keep only the leading system message. */
11032
- async sendWithContext(content, context) {
11033
- if (!context?.trim() || typeof content !== "string") return this.voice.send(content);
11034
- const base = this.voice.transcript.length;
11063
+ async sendWithContext(content, context, note) {
11064
+ if (typeof content !== "string" || !context?.trim() && !note?.trim()) return this.voice.send(content);
11065
+ const stored = (note?.trim() ? hostNoteBlock(note) : "") + content, base = this.voice.transcript.length;
11035
11066
  try {
11036
- return await this.voice.send(hostContextBlock(context) + content);
11067
+ return await this.voice.send((context?.trim() ? hostContextBlock(context) : "") + stored);
11037
11068
  } finally {
11038
11069
  const m = this.voice.transcript.slice(base).find((x) => x.role === "user");
11039
- if (m) m.content = content;
11070
+ if (m) m.content = stored;
11040
11071
  }
11041
11072
  }
11073
+ /** The last line's host context (call rules, browser context): every reflex call runs under it, not only the user's line. */
11074
+ lastContext;
11042
11075
  /** The turn SAID it is on it ("got it — checking…") but called no tool and nothing is running: a promise nothing
11043
11076
  * keeps — the user waits on silence (live Para #798: a follow-up while the reflex believed a finished task still ran). */
11044
11077
  get hollowPromise() {
@@ -11060,7 +11093,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
11060
11093
  this.stepMarks = [];
11061
11094
  this.dropJudge();
11062
11095
  try {
11063
- await this.voice.send(reminder);
11096
+ await this.sendWithContext(reminder, this.lastContext);
11064
11097
  } catch (e) {
11065
11098
  log23.warn(`follow-through nudge failed: ${e instanceof Error ? e.message : e}`);
11066
11099
  } finally {
@@ -11203,7 +11236,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
11203
11236
  if (this.dispatchAt >= 0) this.dispatchAt = 0;
11204
11237
  this.nudging = true;
11205
11238
  try {
11206
- await this.voice.send(reminder);
11239
+ await this.sendWithContext(reminder, this.lastContext);
11207
11240
  } catch (e) {
11208
11241
  log23.warn(`ack nudge failed: ${e instanceof Error ? e.message : e}`);
11209
11242
  } finally {
@@ -11242,7 +11275,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
11242
11275
  * second Act). The host gets `turn_replace` {id, replaces, rolledBack, followed?}: only a rollback drops the replaced
11243
11276
  * line; `followed` = the kept turn whose follow-up ran (absent: the merged text ran). */
11244
11277
  send(content, opts) {
11245
- const ctx = opts?.context;
11278
+ const ctx = opts && (opts.context !== void 0 || opts.note !== void 0) ? { context: opts.context, note: opts.note } : void 0;
11246
11279
  this.clearHoldResume();
11247
11280
  this.holdResumed = false;
11248
11281
  if (opts?.replaces) this.replaceTurn(opts.replaces);
@@ -11354,12 +11387,17 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
11354
11387
  return { text: "", steps: 0, finishReason: "stop", messages: [] };
11355
11388
  });
11356
11389
  }
11357
- sendTurn(content, keepParked = false, rec, ext, context) {
11390
+ sendTurn(content, keepParked = false, rec, ext, host) {
11358
11391
  const spec = this.spec;
11359
11392
  if (spec?.state === "pending") {
11360
11393
  if (typeof content === "string" && speculationConfirms(spec.text, content)) {
11361
11394
  spec.state = "confirmed";
11362
11395
  spec.finalText = content;
11396
+ if (host?.context !== void 0) this.lastContext = host.context;
11397
+ if (host?.note?.trim()) {
11398
+ const m = [...this.voice.transcript].reverse().find((x) => x.role === "user" && x.content === spec.text);
11399
+ if (m) m.content = hostNoteBlock(host.note) + spec.text;
11400
+ }
11363
11401
  if (rec) {
11364
11402
  spec.rec = rec;
11365
11403
  rec.ctl.signal.addEventListener("abort", () => spec.ctl.abort(), { once: true });
@@ -11406,8 +11444,9 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
11406
11444
  }
11407
11445
  if (typeof content === "string") content += spellingNote(content);
11408
11446
  let res;
11447
+ if (host?.context !== void 0) this.lastContext = host.context;
11409
11448
  try {
11410
- res = await this.reflexSend(content, context);
11449
+ res = await this.reflexSend(content, host?.note);
11411
11450
  } finally {
11412
11451
  if (signal) {
11413
11452
  this.voice.options.signal = ext;
@@ -11456,7 +11495,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
11456
11495
  this.voice.options.signal = spec.ctl.signal;
11457
11496
  let res;
11458
11497
  try {
11459
- res = await this.voice.send(spec.text);
11498
+ res = await this.sendWithContext(spec.text, this.lastContext);
11460
11499
  } catch (e) {
11461
11500
  log23.warn(`speculative turn failed: ${e instanceof Error ? e.message : e}`);
11462
11501
  } finally {
@@ -11735,7 +11774,7 @@ Update: ${brief}`;
11735
11774
  return this.voice.transcript.filter((m) => (m.role === "user" || m.role === "assistant") && contentText(m.content).trim()).slice(-turns).map((m) => `${m.role}: ${contentText(m.content)}`).join("\n");
11736
11775
  }
11737
11776
  buildBrief(brief, tier = "act", deliver = true) {
11738
- const recent = this.recentExcerpt();
11777
+ const recent = this.recentExcerpt(), host = this.lastContext?.trim();
11739
11778
  const verify = tier === "act" ? "\n\nBefore reporting done: re-read what you changed and check it against EVERY requirement above \u2014 fix any gap first. Your report is trusted without review. A command exiting 0 or echoing its input means it was ACCEPTED, not that it worked: before you say a web address was opened or reached, confirm it exists (its host resolves in DNS and answers an HTTP request); if it does not, report that it does not exist instead of success, and name the address you tried." : "";
11740
11779
  const deliverContract = deliver ? '\n\n## DELIVER (spoken delivery)\nYou are reporting back to a user who is LISTENING. Stream your work normally \u2014 your prose is the written work record and detail, and is NOT spoken. Wrap anything the user should HEAR in <spoken>\u2026</spoken> tags. LEAD WITH the actual content they asked for: if they asked for a specific piece of content \u2014 a value, a name, the actual lines, the writing itself \u2014 that content goes INSIDE the <spoken> tags, not a remark about it. Your FIRST <spoken> segment is substantive \u2014 never a greeting or an acknowledgement (the front-end has already acked; do not double-ack). Keep spoken text concise and natural for the ear: short sentences, no markdown. Never read a file path, URL, command or code aloud, nor spell one out ("slash \u2026 slash \u2026") \u2014 say it the way a person would, by the name of the folder, project, file or site; the exact text stays in your written prose. ' + (this.options.spokenBudgetChars ? `Speak a SHORT summary only: at most three short sentences with the one thing that matters most, then ONE short offer that names what else you have, as a question they can answer in a word \u2014 under ${this.options.spokenBudgetChars} characters in all, the offer included. Everything else stays in your written prose (it is kept and they can ask for it); spoken text past that budget is cut and held until they ask. ` : "") + `NEVER enumerate in speech \u2014 no numbered or bulleted lists ("One. \u2026 Two. \u2026" is robotic). Deliver multiple items as flowing conversation with brief connective phrasing ("here's one\u2026", "and another\u2026", "oh, and\u2026"), pausing between items with sentence breaks, not numbers.` + (this.options.emotionTags ? " Inside <spoken>, you may prefix a sentence with an inline [emotion] tag (e.g. [excited], [curious]) to color how it is voiced \u2014 only when it genuinely fits, and vary it; [laughter] gives a natural laugh." : "") : "";
11741
11780
  return `${nowLine()}.
@@ -11743,7 +11782,10 @@ Update: ${brief}`;
11743
11782
  ` + (recent ? `${brief}
11744
11783
 
11745
11784
  ## Recent conversation (for context)
11746
- ${recent}` : brief) + verify + deliverContract;
11785
+ ${recent}` : brief) + (host ? `
11786
+
11787
+ ## The app's context for this line (not the user's words)
11788
+ ${host}` : "") + verify + deliverContract;
11747
11789
  }
11748
11790
  /** Spawn a detached worker for task `id`; its settlement notifies + enqueues the re-voice turn. */
11749
11791
  spawnWorker(id, label, briefText, tier, brief, followUp) {
@@ -18812,11 +18854,16 @@ Flags:
18812
18854
  --timeout <sec> wall-clock kill-switch (default 120)
18813
18855
  --input-format stream-json with --duplex|--voice + --output-format stream-json: headless duplex for a host app
18814
18856
  (NDJSON turns/barge-ins on stdin, reflex text + worker speech + task events on stdout)
18857
+ a user line's {context, note} = host info fenced as the app's (context: per-line rules, kept for
18858
+ results/reminders; note: persistent background) \u2014 never stored as the user's words
18815
18859
  with -p: the first {"type":"user","text"} line is the prompt; later ones STEER the running turn
18816
18860
  (unconsumed steers come back as the result's \`unsteered\`)
18817
18861
  --keep-alive with -p --input-format stream-json: stay up after a turn \u2014 each {"type":"user"} line while idle
18818
18862
  is the NEXT turn (warm: same session, model connection and MCP servers), one {type:result}
18819
- 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
18820
18867
  --output-format <f> headless output: text (default) | json (one result object) | stream-json (NDJSON events: {type:text|thinking}\u2026 then {type:result})
18821
18868
  -v, --version print version and exit
18822
18869
  -h, --help
@@ -22520,7 +22567,7 @@ async function duplexServe(args, ai, cfg, cwd) {
22520
22567
  const done2 = () => {
22521
22568
  if (seq === scTurn) sc?.onAgentState("idle");
22522
22569
  };
22523
- void runTurn(dx.voice, store, session, m.text + note, void 0, cwd, (c) => dx.send(c, { id, replaces: m.replaces ? String(m.replaces) : void 0, followUp: typeof m.followUp === "string" || m.followUp && typeof m.followUp === "object" ? m.followUp : void 0, context: typeof m.context === "string" ? m.context : void 0 })).then(({ res, error }) => {
22570
+ void runTurn(dx.voice, store, session, m.text + note, void 0, cwd, (c) => dx.send(c, { id, replaces: m.replaces ? String(m.replaces) : void 0, followUp: typeof m.followUp === "string" || m.followUp && typeof m.followUp === "object" ? m.followUp : void 0, context: typeof m.context === "string" ? m.context : void 0, note: typeof m.note === "string" ? m.note : void 0 })).then(({ res, error }) => {
22524
22571
  const why = (res?.finishReason ?? "error") === "error" ? duplexError(res?.error ?? error, dx.options.reflexModel) : "";
22525
22572
  done2();
22526
22573
  out({ type: "turn_done", id, finishReason: res?.finishReason ?? "error", ...why ? { error: why } : {} });
@@ -22583,7 +22630,7 @@ async function channelServe(args, ai, cfg) {
22583
22630
  }
22584
22631
  function readStdinSteers(keepAlive = false) {
22585
22632
  const early = [];
22586
- let sink, got, closed = false;
22633
+ let sink, got, closed = false, started = false;
22587
22634
  const first = new Promise((res) => {
22588
22635
  got = res;
22589
22636
  });
@@ -22599,7 +22646,17 @@ function readStdinSteers(keepAlive = false) {
22599
22646
  activeTurn?.abort();
22600
22647
  return;
22601
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
+ }
22602
22658
  if (m?.type !== "user" || typeof m.text !== "string" || !m.text.trim()) return;
22659
+ started = true;
22603
22660
  if (got) {
22604
22661
  got(m.text);
22605
22662
  got = void 0;
@@ -22619,9 +22676,21 @@ function readStdinSteers(keepAlive = false) {
22619
22676
  };
22620
22677
  return { first, steer: (to) => {
22621
22678
  sink = to;
22622
- early.splice(0).forEach(to);
22679
+ while (typeof early[0] === "string") to(early.shift());
22623
22680
  }, next };
22624
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
+ }
22625
22694
  function readAllStdin() {
22626
22695
  return new Promise((res) => {
22627
22696
  let data = "";
@@ -22656,9 +22725,11 @@ async function main() {
22656
22725
  }
22657
22726
  if (args.debug) process.env.DEBUG ||= "*";
22658
22727
  const steers = args.print && args.inputFormat && !args.task ? readStdinSteers(args.keepAlive) : void 0;
22659
- if (steers) args.task = (await steers.first).trim();
22660
- else if (args.print && !args.task && !process.stdin.isTTY) args.task = (await readAllStdin()).trim();
22661
- 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) {
22662
22733
  console.error(red(" -p: empty prompt (no task argument and nothing on stdin)"));
22663
22734
  process.exit(2);
22664
22735
  }
@@ -22725,8 +22796,8 @@ async function main() {
22725
22796
  }
22726
22797
  }
22727
22798
  });
22728
- if (args.task) {
22729
- 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());
22730
22801
  if (compactArg) {
22731
22802
  const agent2 = await makeAgent(args, ai, cfg, [], []), store2 = new SessionStore(cwd), session2 = startSession(args, store2, agent2, cwd);
22732
22803
  const est = (m) => estimateTranscriptTokens(m, agent2.options.keepRecentImages, agent2.options.imageInput?.(agent2.options.model) !== false);
@@ -22749,8 +22820,22 @@ async function main() {
22749
22820
  process.once("SIGINT", () => activeTurn?.abort());
22750
22821
  steers?.steer((text) => agent.inject(text));
22751
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))));
22752
- if (args.keepAlive && steers) {
22753
- 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;
22754
22839
  steers.steer((text) => agent.inject(text));
22755
22840
  const { res: res2 } = await turn(task);
22756
22841
  const left = agent.drainPendingSteers();
@@ -22820,6 +22905,7 @@ export {
22820
22905
  readImageParts,
22821
22906
  readMultiline,
22822
22907
  resolvePermMode,
22908
+ rollbackTurn,
22823
22909
  runShellLine,
22824
22910
  runTurn,
22825
22911
  setMcpMentionResolver,