@livx.cc/agentx 0.99.107 → 0.99.109

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-BYW5qPAJ.js';
2
+ import { o as RouterState, H as Hooks, p as RunResult, R as ReasoningEffort, C as ChannelMode, A as Agent } from './secondChannel-3IPP3Qgh.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
 
package/dist/cli.js CHANGED
@@ -10509,6 +10509,14 @@ function nameScore(want, have) {
10509
10509
  var RESERVED_EVENT_MARKER = /\[task\b[^\]\n]*\b(?:completed|failed|progress|asks)\b/i;
10510
10510
  var RESERVED_EVENT_OPENER = /\[\s*task\b/i;
10511
10511
  var STAGE_DIRECTION_RE = /^\(\s*(?:(?:waiting|checking|searching|thinking|processing|loading|working|fetching|looking)\b[^)]*|[^)]*(?:\.\.\.|…)\s*)\)$/i;
10512
+ function hostContextBlock(context) {
10513
+ return `<host_context>
10514
+ ${context.trim()}
10515
+ </host_context>
10516
+ (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
+
10518
+ `;
10519
+ }
10512
10520
  function nowLine() {
10513
10521
  return `Current date and time: ${(/* @__PURE__ */ new Date()).toLocaleString("en-US", { weekday: "long", year: "numeric", month: "long", day: "numeric", hour: "numeric", minute: "2-digit", timeZoneName: "short" })}`;
10514
10522
  }
@@ -11002,9 +11010,9 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
11002
11010
  /** One reflex model call for a turn; a turn rejected for load (rateLimited) that has not spoken or dispatched yet is rolled
11003
11011
  * back and re-run once on `reflexFallbackModel` — a re-prompt of the same model (the dead-air nudge) only hits the
11004
11012
  * same limit and ends in "could you say that again?". */
11005
- async reflexSend(content) {
11013
+ async reflexSend(content, context) {
11006
11014
  const base = this.voice.transcript.length;
11007
- const res = await this.voice.send(content);
11015
+ const res = await this.sendWithContext(content, context);
11008
11016
  const fb = this.options.reflexFallbackModel, model = this.voice.options.model;
11009
11017
  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;
11010
11018
  log23.warn(`reflex ${model} rate-limited \u2014 re-running the turn on ${fb}`);
@@ -11012,11 +11020,25 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
11012
11020
  this.voice.transcript.length = base;
11013
11021
  this.voice.options.model = fb;
11014
11022
  try {
11015
- return await this.voice.send(content);
11023
+ return await this.sendWithContext(content, context);
11016
11024
  } finally {
11017
11025
  this.voice.options.model = model;
11018
11026
  }
11019
11027
  }
11028
+ /** The host's per-line context (call rules, browser context, earlier conversation) rides THIS call only, fenced as the app's —
11029
+ * inside the user turn it read as the user's own words (live #938: "repeat everything I said" recited the call rules). The
11030
+ * stored turn keeps only what the user said, so later turns, briefs and the saved session never carry it. A block, not a
11031
+ * 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;
11035
+ try {
11036
+ return await this.voice.send(hostContextBlock(context) + content);
11037
+ } finally {
11038
+ const m = this.voice.transcript.slice(base).find((x) => x.role === "user");
11039
+ if (m) m.content = content;
11040
+ }
11041
+ }
11020
11042
  /** The turn SAID it is on it ("got it — checking…") but called no tool and nothing is running: a promise nothing
11021
11043
  * keeps — the user waits on silence (live Para #798: a follow-up while the reflex believed a finished task still ran). */
11022
11044
  get hollowPromise() {
@@ -11220,6 +11242,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
11220
11242
  * second Act). The host gets `turn_replace` {id, replaces, rolledBack, followed?}: only a rollback drops the replaced
11221
11243
  * line; `followed` = the kept turn whose follow-up ran (absent: the merged text ran). */
11222
11244
  send(content, opts) {
11245
+ const ctx = opts?.context;
11223
11246
  this.clearHoldResume();
11224
11247
  this.holdResumed = false;
11225
11248
  if (opts?.replaces) this.replaceTurn(opts.replaces);
@@ -11245,7 +11268,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
11245
11268
  if (intent === "resume" && !reflexCut) return this.resumeParked();
11246
11269
  if (intent === "aside" || intent === "resume") {
11247
11270
  const told = intent === "aside" ? [...this.parkedRedeliver] : [];
11248
- return this.sendTurn(intent === "aside" ? content + this.asideNote(told) : content, true, rec, ext).then(async (r) => {
11271
+ return this.sendTurn(intent === "aside" ? content + this.asideNote(told) : content, true, rec, ext, ctx).then(async (r) => {
11249
11272
  if (r.finishReason === "aborted" || this.heldThisTurn) return r;
11250
11273
  const missed = unsaidClauses(told.map((p) => p.text), this.turnSpoken, this.options.decider);
11251
11274
  const turn = this.turn;
@@ -11260,10 +11283,10 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
11260
11283
  return r;
11261
11284
  });
11262
11285
  }
11263
- return this.sendTurn(content, false, rec, ext);
11286
+ return this.sendTurn(content, false, rec, ext, ctx);
11264
11287
  });
11265
11288
  }
11266
- return this.sendTurn(typeof content === "string" ? content + this.acceptNote(content) : content, false, rec, ext);
11289
+ return this.sendTurn(typeof content === "string" ? content + this.acceptNote(content) : content, false, rec, ext, ctx);
11267
11290
  }
11268
11291
  /** Host-named user turns (send opts.id), so a later send can replace one. Bounded; a replace that arrives
11269
11292
  * BEFORE its target (the host's dispatch hops can reorder) leaves a pre-superseded record behind. */
@@ -11331,7 +11354,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
11331
11354
  return { text: "", steps: 0, finishReason: "stop", messages: [] };
11332
11355
  });
11333
11356
  }
11334
- sendTurn(content, keepParked = false, rec, ext) {
11357
+ sendTurn(content, keepParked = false, rec, ext, context) {
11335
11358
  const spec = this.spec;
11336
11359
  if (spec?.state === "pending") {
11337
11360
  if (typeof content === "string" && speculationConfirms(spec.text, content)) {
@@ -11384,7 +11407,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
11384
11407
  if (typeof content === "string") content += spellingNote(content);
11385
11408
  let res;
11386
11409
  try {
11387
- res = await this.reflexSend(content);
11410
+ res = await this.reflexSend(content, context);
11388
11411
  } finally {
11389
11412
  if (signal) {
11390
11413
  this.voice.options.signal = ext;
@@ -13022,6 +13045,9 @@ var SecondChannelOptions = class {
13022
13045
  /** A bridge waits this long after the user's turn is dispatched, and plays only if channel-1 has said nothing yet
13023
13046
  * (a fast reply/ack needs no bridge — one before it reads as the agent starting a different sentence). 0 = at once. */
13024
13047
  bridgeDelayMs = 1200;
13048
+ /** Duplex v3 (§3 first sound): a bridge skips the decider (the veto stays for backchannels/fillers) and speaks a
13049
+ * canned line at once; a generated one is started at dispatch and used only if it already landed. */
13050
+ instantBridge = false;
13025
13051
  /** No new interim for this long → a pause (hosts may also call onPause). */
13026
13052
  fillerPauseMs = 700;
13027
13053
  fillerMinWords = 4;
@@ -13093,6 +13119,8 @@ var SecondChannel = class {
13093
13119
  // the line the last barge actually cut (spoke())
13094
13120
  bridged = false;
13095
13121
  // one bridge per main turn
13122
+ bridgeDraft = null;
13123
+ // a bridge generated ahead, used only if ready in time
13096
13124
  recent = [];
13097
13125
  picks = /* @__PURE__ */ new Map();
13098
13126
  inflight = null;
@@ -13228,6 +13256,7 @@ var SecondChannel = class {
13228
13256
  if (this[k]) clearTimeout(this[k]);
13229
13257
  this[k] = null;
13230
13258
  }
13259
+ if (this.o.instantBridge) this.prefetchBridge();
13231
13260
  const wait = Math.max(this.o.bridgeDelayMs, this.o.minGapMs - (this.o.now() - this.lastAt));
13232
13261
  if (wait > 0) this.bridgeTimer = setTimeout(() => {
13233
13262
  this.bridgeTimer = null;
@@ -13360,7 +13389,7 @@ var SecondChannel = class {
13360
13389
  try {
13361
13390
  let kind = rule;
13362
13391
  let choice;
13363
- if (this.o.decide && !cue) {
13392
+ if (this.o.decide && !cue && !(rule === "bridge" && this.o.instantBridge)) {
13364
13393
  const ac = new AbortController();
13365
13394
  const d = await withBudget(this.o.decide(this.tail(), { ...this.state(pauseMs), rule }, ac.signal), this.o.decisionBudgetMs, "channel decision", ac);
13366
13395
  choice = d?.choice;
@@ -13394,7 +13423,24 @@ var SecondChannel = class {
13394
13423
  call_ms: Math.round(now4 - this.startedAt)
13395
13424
  };
13396
13425
  }
13426
+ /** Start writing this turn's bridge now, so it may be ready by bridgeDelayMs. The bridge never waits for it. */
13427
+ prefetchBridge() {
13428
+ this.bridgeDraft = null;
13429
+ if (!this.o.generate || !this.o.generateFor.includes("bridge") || !this.lang || !this.heard) return;
13430
+ const gen = this.gen, ac = new AbortController();
13431
+ const ctx = { kind: "bridge", heard: this.tail(), state: this.main, avoid: [...this.recent], lang: this.lang };
13432
+ void withBudget(this.o.generate(ctx, ac.signal), Math.max(this.o.generateBudgetMs, this.o.bridgeDelayMs), "bridge generate", ac).then((raw) => {
13433
+ const text = cleanLine(raw ?? "", ctx.lang);
13434
+ if (text && gen === this.gen) this.bridgeDraft = { gen, text };
13435
+ }).catch((e) => log27.debug(`bridge prefetch failed: ${e?.message ?? e}`));
13436
+ }
13397
13437
  async line(kind, react = true) {
13438
+ if (kind === "bridge" && this.o.instantBridge) {
13439
+ const d = this.bridgeDraft?.gen === this.gen ? this.bridgeDraft.text : "";
13440
+ this.bridgeDraft = null;
13441
+ if (d && !this.recent.some((r) => norm3(r) === norm3(d))) return d;
13442
+ return this.pick(kind);
13443
+ }
13398
13444
  if (this.o.generate && this.o.generateFor.includes(kind) && (kind !== "backchannel" || react)) {
13399
13445
  const ac = new AbortController();
13400
13446
  const ctx = { kind, heard: this.tail(), state: this.main, avoid: [...this.recent], lang: this.lang, ...kind === "backchannel" && this.o.reactionGapMs ? { react: true } : {} };
@@ -13544,6 +13590,9 @@ var VoiceCall = class {
13544
13590
  } : {},
13545
13591
  mode: o.mode,
13546
13592
  aec: o.aec,
13593
+ bridgeDelayMs: 700,
13594
+ instantBridge: true,
13595
+ // v3 §3: first sound <1.2s p50 — a bridge at 700ms if MAIN is still silent, no veto, no wait
13547
13596
  ...this.lang ? { lang: this.lang } : {},
13548
13597
  ...o.channel,
13549
13598
  speak: (text, kind) => this.say(text, kind),
@@ -22471,7 +22520,7 @@ async function duplexServe(args, ai, cfg, cwd) {
22471
22520
  const done2 = () => {
22472
22521
  if (seq === scTurn) sc?.onAgentState("idle");
22473
22522
  };
22474
- 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 })).then(({ res, error }) => {
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 }) => {
22475
22524
  const why = (res?.finishReason ?? "error") === "error" ? duplexError(res?.error ?? error, dx.options.reflexModel) : "";
22476
22525
  done2();
22477
22526
  out({ type: "turn_done", id, finishReason: res?.finishReason ?? "error", ...why ? { error: why } : {} });