@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 +6 -2
- package/dist/cli.js +126 -40
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +11 -4
- package/dist/index.js +70 -28
- package/dist/index.js.map +1 -1
- package/dist/{secondChannel-3IPP3Qgh.d.ts → secondChannel-CJMY5EbL.d.ts} +6 -0
- package/package.json +2 -2
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-
|
|
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 =
|
|
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,
|
|
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 (
|
|
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) +
|
|
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 =
|
|
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.
|
|
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.
|
|
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
|
|
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,
|
|
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,
|
|
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.
|
|
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) +
|
|
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;
|
|
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
|
|
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
|
-
|
|
22660
|
-
|
|
22661
|
-
if (
|
|
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 (
|
|
22753
|
-
|
|
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,
|