@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/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-BYW5qPAJ.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-BYW5qPAJ.js';
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-3IPP3Qgh.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-3IPP3Qgh.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';
@@ -1275,6 +1275,11 @@ declare class DuplexAgent {
1275
1275
  * back and re-run once on `reflexFallbackModel` — a re-prompt of the same model (the dead-air nudge) only hits the
1276
1276
  * same limit and ends in "could you say that again?". */
1277
1277
  private reflexSend;
1278
+ /** The host's per-line context (call rules, browser context, earlier conversation) rides THIS call only, fenced as the app's —
1279
+ * inside the user turn it read as the user's own words (live #938: "repeat everything I said" recited the call rules). The
1280
+ * stored turn keeps only what the user said, so later turns, briefs and the saved session never carry it. A block, not a
1281
+ * mid-transcript system message: the Anthropic/Google adapters keep only the leading system message. */
1282
+ private sendWithContext;
1278
1283
  /** The turn SAID it is on it ("got it — checking…") but called no tool and nothing is running: a promise nothing
1279
1284
  * keeps — the user waits on silence (live Para #798: a follow-up while the reflex believed a finished task still ran). */
1280
1285
  private get hollowPromise();
@@ -1341,6 +1346,7 @@ declare class DuplexAgent {
1341
1346
  id?: string;
1342
1347
  replaces?: string;
1343
1348
  followUp?: string | Record<string, string>;
1349
+ context?: string;
1344
1350
  }): Promise<RunResult>;
1345
1351
  /** Host-named user turns (send opts.id), so a later send can replace one. Bounded; a replace that arrives
1346
1352
  * BEFORE its target (the host's dispatch hops can reorder) leaves a pre-superseded record behind. */
package/dist/index.js CHANGED
@@ -10177,6 +10177,14 @@ function nameScore(want, have) {
10177
10177
  var RESERVED_EVENT_MARKER = /\[task\b[^\]\n]*\b(?:completed|failed|progress|asks)\b/i;
10178
10178
  var RESERVED_EVENT_OPENER = /\[\s*task\b/i;
10179
10179
  var STAGE_DIRECTION_RE = /^\(\s*(?:(?:waiting|checking|searching|thinking|processing|loading|working|fetching|looking)\b[^)]*|[^)]*(?:\.\.\.|…)\s*)\)$/i;
10180
+ function hostContextBlock(context) {
10181
+ return `<host_context>
10182
+ ${context.trim()}
10183
+ </host_context>
10184
+ (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
+
10186
+ `;
10187
+ }
10180
10188
  function nowLine() {
10181
10189
  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" })}`;
10182
10190
  }
@@ -10670,9 +10678,9 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
10670
10678
  /** One reflex model call for a turn; a turn rejected for load (rateLimited) that has not spoken or dispatched yet is rolled
10671
10679
  * back and re-run once on `reflexFallbackModel` — a re-prompt of the same model (the dead-air nudge) only hits the
10672
10680
  * same limit and ends in "could you say that again?". */
10673
- async reflexSend(content) {
10681
+ async reflexSend(content, context) {
10674
10682
  const base = this.voice.transcript.length;
10675
- const res = await this.voice.send(content);
10683
+ const res = await this.sendWithContext(content, context);
10676
10684
  const fb = this.options.reflexFallbackModel, model = this.voice.options.model;
10677
10685
  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;
10678
10686
  log24.warn(`reflex ${model} rate-limited \u2014 re-running the turn on ${fb}`);
@@ -10680,11 +10688,25 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
10680
10688
  this.voice.transcript.length = base;
10681
10689
  this.voice.options.model = fb;
10682
10690
  try {
10683
- return await this.voice.send(content);
10691
+ return await this.sendWithContext(content, context);
10684
10692
  } finally {
10685
10693
  this.voice.options.model = model;
10686
10694
  }
10687
10695
  }
10696
+ /** The host's per-line context (call rules, browser context, earlier conversation) rides THIS call only, fenced as the app's —
10697
+ * inside the user turn it read as the user's own words (live #938: "repeat everything I said" recited the call rules). The
10698
+ * stored turn keeps only what the user said, so later turns, briefs and the saved session never carry it. A block, not a
10699
+ * mid-transcript system message: the Anthropic/Google adapters keep only the leading system message. */
10700
+ async sendWithContext(content, context) {
10701
+ if (!context?.trim() || typeof content !== "string") return this.voice.send(content);
10702
+ const base = this.voice.transcript.length;
10703
+ try {
10704
+ return await this.voice.send(hostContextBlock(context) + content);
10705
+ } finally {
10706
+ const m = this.voice.transcript.slice(base).find((x) => x.role === "user");
10707
+ if (m) m.content = content;
10708
+ }
10709
+ }
10688
10710
  /** The turn SAID it is on it ("got it — checking…") but called no tool and nothing is running: a promise nothing
10689
10711
  * keeps — the user waits on silence (live Para #798: a follow-up while the reflex believed a finished task still ran). */
10690
10712
  get hollowPromise() {
@@ -10888,6 +10910,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
10888
10910
  * second Act). The host gets `turn_replace` {id, replaces, rolledBack, followed?}: only a rollback drops the replaced
10889
10911
  * line; `followed` = the kept turn whose follow-up ran (absent: the merged text ran). */
10890
10912
  send(content, opts) {
10913
+ const ctx = opts?.context;
10891
10914
  this.clearHoldResume();
10892
10915
  this.holdResumed = false;
10893
10916
  if (opts?.replaces) this.replaceTurn(opts.replaces);
@@ -10913,7 +10936,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
10913
10936
  if (intent === "resume" && !reflexCut) return this.resumeParked();
10914
10937
  if (intent === "aside" || intent === "resume") {
10915
10938
  const told = intent === "aside" ? [...this.parkedRedeliver] : [];
10916
- return this.sendTurn(intent === "aside" ? content + this.asideNote(told) : content, true, rec, ext).then(async (r) => {
10939
+ return this.sendTurn(intent === "aside" ? content + this.asideNote(told) : content, true, rec, ext, ctx).then(async (r) => {
10917
10940
  if (r.finishReason === "aborted" || this.heldThisTurn) return r;
10918
10941
  const missed = unsaidClauses(told.map((p) => p.text), this.turnSpoken, this.options.decider);
10919
10942
  const turn = this.turn;
@@ -10928,10 +10951,10 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
10928
10951
  return r;
10929
10952
  });
10930
10953
  }
10931
- return this.sendTurn(content, false, rec, ext);
10954
+ return this.sendTurn(content, false, rec, ext, ctx);
10932
10955
  });
10933
10956
  }
10934
- return this.sendTurn(typeof content === "string" ? content + this.acceptNote(content) : content, false, rec, ext);
10957
+ return this.sendTurn(typeof content === "string" ? content + this.acceptNote(content) : content, false, rec, ext, ctx);
10935
10958
  }
10936
10959
  /** Host-named user turns (send opts.id), so a later send can replace one. Bounded; a replace that arrives
10937
10960
  * BEFORE its target (the host's dispatch hops can reorder) leaves a pre-superseded record behind. */
@@ -10999,7 +11022,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
10999
11022
  return { text: "", steps: 0, finishReason: "stop", messages: [] };
11000
11023
  });
11001
11024
  }
11002
- sendTurn(content, keepParked = false, rec, ext) {
11025
+ sendTurn(content, keepParked = false, rec, ext, context) {
11003
11026
  const spec = this.spec;
11004
11027
  if (spec?.state === "pending") {
11005
11028
  if (typeof content === "string" && speculationConfirms(spec.text, content)) {
@@ -11052,7 +11075,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
11052
11075
  if (typeof content === "string") content += spellingNote(content);
11053
11076
  let res;
11054
11077
  try {
11055
- res = await this.reflexSend(content);
11078
+ res = await this.reflexSend(content, context);
11056
11079
  } finally {
11057
11080
  if (signal) {
11058
11081
  this.voice.options.signal = ext;
@@ -12690,6 +12713,9 @@ var SecondChannelOptions = class {
12690
12713
  /** A bridge waits this long after the user's turn is dispatched, and plays only if channel-1 has said nothing yet
12691
12714
  * (a fast reply/ack needs no bridge — one before it reads as the agent starting a different sentence). 0 = at once. */
12692
12715
  bridgeDelayMs = 1200;
12716
+ /** Duplex v3 (§3 first sound): a bridge skips the decider (the veto stays for backchannels/fillers) and speaks a
12717
+ * canned line at once; a generated one is started at dispatch and used only if it already landed. */
12718
+ instantBridge = false;
12693
12719
  /** No new interim for this long → a pause (hosts may also call onPause). */
12694
12720
  fillerPauseMs = 700;
12695
12721
  fillerMinWords = 4;
@@ -12761,6 +12787,8 @@ var SecondChannel = class {
12761
12787
  // the line the last barge actually cut (spoke())
12762
12788
  bridged = false;
12763
12789
  // one bridge per main turn
12790
+ bridgeDraft = null;
12791
+ // a bridge generated ahead, used only if ready in time
12764
12792
  recent = [];
12765
12793
  picks = /* @__PURE__ */ new Map();
12766
12794
  inflight = null;
@@ -12896,6 +12924,7 @@ var SecondChannel = class {
12896
12924
  if (this[k]) clearTimeout(this[k]);
12897
12925
  this[k] = null;
12898
12926
  }
12927
+ if (this.o.instantBridge) this.prefetchBridge();
12899
12928
  const wait = Math.max(this.o.bridgeDelayMs, this.o.minGapMs - (this.o.now() - this.lastAt));
12900
12929
  if (wait > 0) this.bridgeTimer = setTimeout(() => {
12901
12930
  this.bridgeTimer = null;
@@ -13028,7 +13057,7 @@ var SecondChannel = class {
13028
13057
  try {
13029
13058
  let kind = rule;
13030
13059
  let choice;
13031
- if (this.o.decide && !cue) {
13060
+ if (this.o.decide && !cue && !(rule === "bridge" && this.o.instantBridge)) {
13032
13061
  const ac = new AbortController();
13033
13062
  const d = await withBudget(this.o.decide(this.tail(), { ...this.state(pauseMs), rule }, ac.signal), this.o.decisionBudgetMs, "channel decision", ac);
13034
13063
  choice = d?.choice;
@@ -13062,7 +13091,24 @@ var SecondChannel = class {
13062
13091
  call_ms: Math.round(now3 - this.startedAt)
13063
13092
  };
13064
13093
  }
13094
+ /** Start writing this turn's bridge now, so it may be ready by bridgeDelayMs. The bridge never waits for it. */
13095
+ prefetchBridge() {
13096
+ this.bridgeDraft = null;
13097
+ if (!this.o.generate || !this.o.generateFor.includes("bridge") || !this.lang || !this.heard) return;
13098
+ const gen = this.gen, ac = new AbortController();
13099
+ const ctx = { kind: "bridge", heard: this.tail(), state: this.main, avoid: [...this.recent], lang: this.lang };
13100
+ void withBudget(this.o.generate(ctx, ac.signal), Math.max(this.o.generateBudgetMs, this.o.bridgeDelayMs), "bridge generate", ac).then((raw) => {
13101
+ const text = cleanLine(raw ?? "", ctx.lang);
13102
+ if (text && gen === this.gen) this.bridgeDraft = { gen, text };
13103
+ }).catch((e) => log28.debug(`bridge prefetch failed: ${e?.message ?? e}`));
13104
+ }
13065
13105
  async line(kind, react = true) {
13106
+ if (kind === "bridge" && this.o.instantBridge) {
13107
+ const d = this.bridgeDraft?.gen === this.gen ? this.bridgeDraft.text : "";
13108
+ this.bridgeDraft = null;
13109
+ if (d && !this.recent.some((r) => norm3(r) === norm3(d))) return d;
13110
+ return this.pick(kind);
13111
+ }
13066
13112
  if (this.o.generate && this.o.generateFor.includes(kind) && (kind !== "backchannel" || react)) {
13067
13113
  const ac = new AbortController();
13068
13114
  const ctx = { kind, heard: this.tail(), state: this.main, avoid: [...this.recent], lang: this.lang, ...kind === "backchannel" && this.o.reactionGapMs ? { react: true } : {} };
@@ -13212,6 +13258,9 @@ var VoiceCall = class {
13212
13258
  } : {},
13213
13259
  mode: o.mode,
13214
13260
  aec: o.aec,
13261
+ bridgeDelayMs: 700,
13262
+ instantBridge: true,
13263
+ // v3 §3: first sound <1.2s p50 — a bridge at 700ms if MAIN is still silent, no veto, no wait
13215
13264
  ...this.lang ? { lang: this.lang } : {},
13216
13265
  ...o.channel,
13217
13266
  speak: (text, kind) => this.say(text, kind),