@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/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as AgentOptions, H as Hooks, p as RunResult, f as Decider, A as Agent, i as PermissionPolicy, L as LineKind, C as ChannelMode, q as SecondChannelOptions, S as SecondChannel } from './secondChannel-
|
|
2
|
-
export { b as ChatFragment, c as Classification, d as Classifier, e as CompactResult, D as DEFAULT_MUTATING, g as Decision, M as ModelRouter, h as ModelRouterOptions, P as PermissionOptions, j as PermissionRule, k as PreToolUseDecision, R as ReasoningEffort, l as RecordingHooks, m as RecordingLifecycle, n as RouteDecision, o as RouterState, T as Tier, r as ToolUse, s as ToolUseMeta, t as claudeCodeDisallowedTools, u as composeHooks, v as estimateTokens, w as jevAvailable, x as jevClassifier, y as pickTier, z as planMode, B as reasoningToChatFragment } from './secondChannel-
|
|
1
|
+
import { a as AgentOptions, H as Hooks, p as RunResult, f as Decider, A as Agent, i as PermissionPolicy, L as LineKind, C as ChannelMode, q as SecondChannelOptions, S as SecondChannel } from './secondChannel-CJMY5EbL.js';
|
|
2
|
+
export { b as ChatFragment, c as Classification, d as Classifier, e as CompactResult, D as DEFAULT_MUTATING, g as Decision, M as ModelRouter, h as ModelRouterOptions, P as PermissionOptions, j as PermissionRule, k as PreToolUseDecision, R as ReasoningEffort, l as RecordingHooks, m as RecordingLifecycle, n as RouteDecision, o as RouterState, T as Tier, r as ToolUse, s as ToolUseMeta, t as claudeCodeDisallowedTools, u as composeHooks, v as estimateTokens, w as jevAvailable, x as jevClassifier, y as pickTier, z as planMode, B as reasoningToChatFragment } from './secondChannel-CJMY5EbL.js';
|
|
3
3
|
export { MODEL_ALIASES, ModelSwitch, ResolveModelSwitchOpts, latestClaude, modelShortLabel, resolveModelAlias, resolveModelSwitch } from './models.js';
|
|
4
4
|
import { IFilesystem, FileMetadata } from '@livx.cc/wcli/core';
|
|
5
5
|
export { CommandExecutor, FileMetadata, IFilesystem, IndexedDbFilesystem, MemFilesystem, registerHeadlessCommands } from '@livx.cc/wcli/core';
|
|
@@ -1119,6 +1119,12 @@ declare class DuplexAgentOptions {
|
|
|
1119
1119
|
/** User-scope memory dir for global facts (type=user/feedback). Forwarded to Remember's routing. */
|
|
1120
1120
|
memoryUserDir?: string;
|
|
1121
1121
|
}
|
|
1122
|
+
/** The host side of a user line: `context` = volatile per-line rules (they stay in force for later reflex calls — results,
|
|
1123
|
+
* reminders — until the next line replaces them); `note` = persistent background, stored fenced in that turn. */
|
|
1124
|
+
type HostInfo = {
|
|
1125
|
+
context?: string;
|
|
1126
|
+
note?: string;
|
|
1127
|
+
};
|
|
1122
1128
|
declare const VOICE_SYSTEM_PROMPT: string;
|
|
1123
1129
|
/**
|
|
1124
1130
|
* The duplex orchestrator. `send()` enqueues a user turn on the reflex agent; `Act`/`Think`
|
|
@@ -1280,6 +1286,8 @@ declare class DuplexAgent {
|
|
|
1280
1286
|
* stored turn keeps only what the user said, so later turns, briefs and the saved session never carry it. A block, not a
|
|
1281
1287
|
* mid-transcript system message: the Anthropic/Google adapters keep only the leading system message. */
|
|
1282
1288
|
private sendWithContext;
|
|
1289
|
+
/** The last line's host context (call rules, browser context): every reflex call runs under it, not only the user's line. */
|
|
1290
|
+
private lastContext?;
|
|
1283
1291
|
/** The turn SAID it is on it ("got it — checking…") but called no tool and nothing is running: a promise nothing
|
|
1284
1292
|
* keeps — the user waits on silence (live Para #798: a follow-up while the reflex believed a finished task still ran). */
|
|
1285
1293
|
private get hollowPromise();
|
|
@@ -1346,8 +1354,7 @@ declare class DuplexAgent {
|
|
|
1346
1354
|
id?: string;
|
|
1347
1355
|
replaces?: string;
|
|
1348
1356
|
followUp?: string | Record<string, string>;
|
|
1349
|
-
|
|
1350
|
-
}): Promise<RunResult>;
|
|
1357
|
+
} & HostInfo): Promise<RunResult>;
|
|
1351
1358
|
/** Host-named user turns (send opts.id), so a later send can replace one. Bounded; a replace that arrives
|
|
1352
1359
|
* BEFORE its target (the host's dispatch hops can reorder) leaves a pre-superseded record behind. */
|
|
1353
1360
|
private userTurns;
|
package/dist/index.js
CHANGED
|
@@ -4913,6 +4913,39 @@ var Agent = class _Agent {
|
|
|
4913
4913
|
* steers over its own channel (claude-code's warm InputQueue via `providerOptions.steer`). Non-text
|
|
4914
4914
|
* injections (images) stay queued for the step-boundary `drainInjections()` re-open — the text-only
|
|
4915
4915
|
* steer channel can't carry them. Consumed DURING chat(); no overlap with drainInjections() (post-call). */
|
|
4916
|
+
/** claude-code/*: the providerOptions every step adds (and prewarm, so the session it starts is the one step 1 reuses). */
|
|
4917
|
+
claudeCodePo() {
|
|
4918
|
+
const o = this.options;
|
|
4919
|
+
if (!o.model.startsWith("claude-code/")) return void 0;
|
|
4920
|
+
const po = { steer: () => this.drainPendingSteers() };
|
|
4921
|
+
const bridged = (t) => o.ccBridge === "all" || CC_BRIDGED.has(t.name);
|
|
4922
|
+
if (o.providerOptions?.claudeCodeSession && this.activeTools.some(bridged)) {
|
|
4923
|
+
this.ccServer ??= ccToolServer("agentx", () => this.activeTools.filter(bridged), (n, a) => this.ccExec(n, a));
|
|
4924
|
+
po.mcpServers = { ...o.providerOptions?.mcpServers, agentx: this.ccServer };
|
|
4925
|
+
}
|
|
4926
|
+
const denyTools = claudeCodeDisallowedTools(o.permissions);
|
|
4927
|
+
if (denyTools.length) {
|
|
4928
|
+
const prior = o.providerOptions?.disallowedTools ?? [];
|
|
4929
|
+
po.disallowedTools = [.../* @__PURE__ */ new Set([...prior, ...denyTools])];
|
|
4930
|
+
}
|
|
4931
|
+
return po;
|
|
4932
|
+
}
|
|
4933
|
+
/** claude-code/* warm session: start it now (its process, MCP handshakes) so the first turn doesn't pay for it. Its
|
|
4934
|
+
* system prompt is fixed at the start, so it is prepared here (no task hint) — memoized, the turns reuse the same one.
|
|
4935
|
+
* No model call; the transcript gains only that system message. Other models: nothing to warm. */
|
|
4936
|
+
async prewarm() {
|
|
4937
|
+
const o = this.options;
|
|
4938
|
+
if (!o.model.startsWith("claude-code/") || !o.providerOptions?.claudeCodeSession) return;
|
|
4939
|
+
await this.ensureFs();
|
|
4940
|
+
const systemPrompt = await this.prepare(), startCtx = await this.fireSessionStart();
|
|
4941
|
+
const sys = { role: "system", content: systemPrompt + (startCtx ? "\n\n" + startCtx : "") };
|
|
4942
|
+
if (this.transcript[0]?.role === "system") this.transcript[0] = sys;
|
|
4943
|
+
else this.transcript.unshift(sys);
|
|
4944
|
+
const po = this.claudeCodePo(), frag = reasoningToChatFragment(o.model, o.reasoning);
|
|
4945
|
+
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 } });
|
|
4946
|
+
for await (const _ of r) {
|
|
4947
|
+
}
|
|
4948
|
+
}
|
|
4916
4949
|
drainPendingSteers() {
|
|
4917
4950
|
if (!this.pendingInjections.length) return [];
|
|
4918
4951
|
const texts = [];
|
|
@@ -5205,18 +5238,8 @@ var Agent = class _Agent {
|
|
|
5205
5238
|
}
|
|
5206
5239
|
};
|
|
5207
5240
|
const cursorPo = isCursorWithTools ? { toolTimeoutMs: effectiveToolTimeoutMs, toolExecutor: hostExec } : void 0;
|
|
5208
|
-
const claudeSteerPo =
|
|
5209
|
-
const bridged = (t) => o.ccBridge === "all" || CC_BRIDGED.has(t.name);
|
|
5210
|
-
if (claudeSteerPo && o.providerOptions?.claudeCodeSession && this.activeTools.some(bridged)) {
|
|
5211
|
-
this.ccServer ??= ccToolServer("agentx", () => this.activeTools.filter(bridged), (n, a) => this.ccExec(n, a));
|
|
5212
|
-
claudeSteerPo.mcpServers = { ...o.providerOptions?.mcpServers, agentx: this.ccServer };
|
|
5213
|
-
}
|
|
5241
|
+
const claudeSteerPo = this.claudeCodePo();
|
|
5214
5242
|
if (this.ccServer) this.ccExec = hostExec;
|
|
5215
|
-
const denyTools = o.model.startsWith("claude-code/") ? claudeCodeDisallowedTools(o.permissions) : [];
|
|
5216
|
-
if (denyTools.length) {
|
|
5217
|
-
const prior = o.providerOptions?.disallowedTools ?? [];
|
|
5218
|
-
claudeSteerPo.disallowedTools = [.../* @__PURE__ */ new Set([...prior, ...denyTools])];
|
|
5219
|
-
}
|
|
5220
5243
|
const cursorDeny = o.model.startsWith("cursor/") ? o.permissions?.denyRules(true) ?? [] : [];
|
|
5221
5244
|
if (cursorDeny.length)
|
|
5222
5245
|
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.`);
|
|
@@ -10183,6 +10206,14 @@ ${context.trim()}
|
|
|
10183
10206
|
</host_context>
|
|
10184
10207
|
(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.)
|
|
10185
10208
|
|
|
10209
|
+
`;
|
|
10210
|
+
}
|
|
10211
|
+
function hostNoteBlock(note) {
|
|
10212
|
+
return `<host_note>
|
|
10213
|
+
${note.trim()}
|
|
10214
|
+
</host_note>
|
|
10215
|
+
(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.)
|
|
10216
|
+
|
|
10186
10217
|
`;
|
|
10187
10218
|
}
|
|
10188
10219
|
function nowLine() {
|
|
@@ -10678,9 +10709,9 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
10678
10709
|
/** One reflex model call for a turn; a turn rejected for load (rateLimited) that has not spoken or dispatched yet is rolled
|
|
10679
10710
|
* back and re-run once on `reflexFallbackModel` — a re-prompt of the same model (the dead-air nudge) only hits the
|
|
10680
10711
|
* same limit and ends in "could you say that again?". */
|
|
10681
|
-
async reflexSend(content,
|
|
10682
|
-
const base = this.voice.transcript.length;
|
|
10683
|
-
const res = await this.sendWithContext(content, context);
|
|
10712
|
+
async reflexSend(content, note) {
|
|
10713
|
+
const base = this.voice.transcript.length, context = this.lastContext;
|
|
10714
|
+
const res = await this.sendWithContext(content, context, note);
|
|
10684
10715
|
const fb = this.options.reflexFallbackModel, model = this.voice.options.model;
|
|
10685
10716
|
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;
|
|
10686
10717
|
log24.warn(`reflex ${model} rate-limited \u2014 re-running the turn on ${fb}`);
|
|
@@ -10688,7 +10719,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
10688
10719
|
this.voice.transcript.length = base;
|
|
10689
10720
|
this.voice.options.model = fb;
|
|
10690
10721
|
try {
|
|
10691
|
-
return await this.sendWithContext(content, context);
|
|
10722
|
+
return await this.sendWithContext(content, context, note);
|
|
10692
10723
|
} finally {
|
|
10693
10724
|
this.voice.options.model = model;
|
|
10694
10725
|
}
|
|
@@ -10697,16 +10728,18 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
10697
10728
|
* inside the user turn it read as the user's own words (live #938: "repeat everything I said" recited the call rules). The
|
|
10698
10729
|
* stored turn keeps only what the user said, so later turns, briefs and the saved session never carry it. A block, not a
|
|
10699
10730
|
* mid-transcript system message: the Anthropic/Google adapters keep only the leading system message. */
|
|
10700
|
-
async sendWithContext(content, context) {
|
|
10701
|
-
if (
|
|
10702
|
-
const base = this.voice.transcript.length;
|
|
10731
|
+
async sendWithContext(content, context, note) {
|
|
10732
|
+
if (typeof content !== "string" || !context?.trim() && !note?.trim()) return this.voice.send(content);
|
|
10733
|
+
const stored = (note?.trim() ? hostNoteBlock(note) : "") + content, base = this.voice.transcript.length;
|
|
10703
10734
|
try {
|
|
10704
|
-
return await this.voice.send(hostContextBlock(context) +
|
|
10735
|
+
return await this.voice.send((context?.trim() ? hostContextBlock(context) : "") + stored);
|
|
10705
10736
|
} finally {
|
|
10706
10737
|
const m = this.voice.transcript.slice(base).find((x) => x.role === "user");
|
|
10707
|
-
if (m) m.content =
|
|
10738
|
+
if (m) m.content = stored;
|
|
10708
10739
|
}
|
|
10709
10740
|
}
|
|
10741
|
+
/** The last line's host context (call rules, browser context): every reflex call runs under it, not only the user's line. */
|
|
10742
|
+
lastContext;
|
|
10710
10743
|
/** The turn SAID it is on it ("got it — checking…") but called no tool and nothing is running: a promise nothing
|
|
10711
10744
|
* keeps — the user waits on silence (live Para #798: a follow-up while the reflex believed a finished task still ran). */
|
|
10712
10745
|
get hollowPromise() {
|
|
@@ -10728,7 +10761,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
10728
10761
|
this.stepMarks = [];
|
|
10729
10762
|
this.dropJudge();
|
|
10730
10763
|
try {
|
|
10731
|
-
await this.
|
|
10764
|
+
await this.sendWithContext(reminder, this.lastContext);
|
|
10732
10765
|
} catch (e) {
|
|
10733
10766
|
log24.warn(`follow-through nudge failed: ${e instanceof Error ? e.message : e}`);
|
|
10734
10767
|
} finally {
|
|
@@ -10871,7 +10904,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
10871
10904
|
if (this.dispatchAt >= 0) this.dispatchAt = 0;
|
|
10872
10905
|
this.nudging = true;
|
|
10873
10906
|
try {
|
|
10874
|
-
await this.
|
|
10907
|
+
await this.sendWithContext(reminder, this.lastContext);
|
|
10875
10908
|
} catch (e) {
|
|
10876
10909
|
log24.warn(`ack nudge failed: ${e instanceof Error ? e.message : e}`);
|
|
10877
10910
|
} finally {
|
|
@@ -10910,7 +10943,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
10910
10943
|
* second Act). The host gets `turn_replace` {id, replaces, rolledBack, followed?}: only a rollback drops the replaced
|
|
10911
10944
|
* line; `followed` = the kept turn whose follow-up ran (absent: the merged text ran). */
|
|
10912
10945
|
send(content, opts) {
|
|
10913
|
-
const ctx = opts
|
|
10946
|
+
const ctx = opts && (opts.context !== void 0 || opts.note !== void 0) ? { context: opts.context, note: opts.note } : void 0;
|
|
10914
10947
|
this.clearHoldResume();
|
|
10915
10948
|
this.holdResumed = false;
|
|
10916
10949
|
if (opts?.replaces) this.replaceTurn(opts.replaces);
|
|
@@ -11022,12 +11055,17 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
11022
11055
|
return { text: "", steps: 0, finishReason: "stop", messages: [] };
|
|
11023
11056
|
});
|
|
11024
11057
|
}
|
|
11025
|
-
sendTurn(content, keepParked = false, rec, ext,
|
|
11058
|
+
sendTurn(content, keepParked = false, rec, ext, host) {
|
|
11026
11059
|
const spec = this.spec;
|
|
11027
11060
|
if (spec?.state === "pending") {
|
|
11028
11061
|
if (typeof content === "string" && speculationConfirms(spec.text, content)) {
|
|
11029
11062
|
spec.state = "confirmed";
|
|
11030
11063
|
spec.finalText = content;
|
|
11064
|
+
if (host?.context !== void 0) this.lastContext = host.context;
|
|
11065
|
+
if (host?.note?.trim()) {
|
|
11066
|
+
const m = [...this.voice.transcript].reverse().find((x) => x.role === "user" && x.content === spec.text);
|
|
11067
|
+
if (m) m.content = hostNoteBlock(host.note) + spec.text;
|
|
11068
|
+
}
|
|
11031
11069
|
if (rec) {
|
|
11032
11070
|
spec.rec = rec;
|
|
11033
11071
|
rec.ctl.signal.addEventListener("abort", () => spec.ctl.abort(), { once: true });
|
|
@@ -11074,8 +11112,9 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
11074
11112
|
}
|
|
11075
11113
|
if (typeof content === "string") content += spellingNote(content);
|
|
11076
11114
|
let res;
|
|
11115
|
+
if (host?.context !== void 0) this.lastContext = host.context;
|
|
11077
11116
|
try {
|
|
11078
|
-
res = await this.reflexSend(content,
|
|
11117
|
+
res = await this.reflexSend(content, host?.note);
|
|
11079
11118
|
} finally {
|
|
11080
11119
|
if (signal) {
|
|
11081
11120
|
this.voice.options.signal = ext;
|
|
@@ -11124,7 +11163,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
11124
11163
|
this.voice.options.signal = spec.ctl.signal;
|
|
11125
11164
|
let res;
|
|
11126
11165
|
try {
|
|
11127
|
-
res = await this.
|
|
11166
|
+
res = await this.sendWithContext(spec.text, this.lastContext);
|
|
11128
11167
|
} catch (e) {
|
|
11129
11168
|
log24.warn(`speculative turn failed: ${e instanceof Error ? e.message : e}`);
|
|
11130
11169
|
} finally {
|
|
@@ -11403,7 +11442,7 @@ Update: ${brief}`;
|
|
|
11403
11442
|
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");
|
|
11404
11443
|
}
|
|
11405
11444
|
buildBrief(brief, tier = "act", deliver = true) {
|
|
11406
|
-
const recent = this.recentExcerpt();
|
|
11445
|
+
const recent = this.recentExcerpt(), host = this.lastContext?.trim();
|
|
11407
11446
|
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." : "";
|
|
11408
11447
|
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." : "") : "";
|
|
11409
11448
|
return `${nowLine()}.
|
|
@@ -11411,7 +11450,10 @@ Update: ${brief}`;
|
|
|
11411
11450
|
` + (recent ? `${brief}
|
|
11412
11451
|
|
|
11413
11452
|
## Recent conversation (for context)
|
|
11414
|
-
${recent}` : brief) +
|
|
11453
|
+
${recent}` : brief) + (host ? `
|
|
11454
|
+
|
|
11455
|
+
## The app's context for this line (not the user's words)
|
|
11456
|
+
${host}` : "") + verify + deliverContract;
|
|
11415
11457
|
}
|
|
11416
11458
|
/** Spawn a detached worker for task `id`; its settlement notifies + enqueues the re-voice turn. */
|
|
11417
11459
|
spawnWorker(id, label, briefText, tier, brief, followUp) {
|