@livx.cc/agentx 0.99.108 → 0.99.110

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
@@ -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`
@@ -1275,6 +1281,13 @@ declare class DuplexAgent {
1275
1281
  * back and re-run once on `reflexFallbackModel` — a re-prompt of the same model (the dead-air nudge) only hits the
1276
1282
  * same limit and ends in "could you say that again?". */
1277
1283
  private reflexSend;
1284
+ /** The host's per-line context (call rules, browser context, earlier conversation) rides THIS call only, fenced as the app's —
1285
+ * inside the user turn it read as the user's own words (live #938: "repeat everything I said" recited the call rules). The
1286
+ * stored turn keeps only what the user said, so later turns, briefs and the saved session never carry it. A block, not a
1287
+ * mid-transcript system message: the Anthropic/Google adapters keep only the leading system message. */
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?;
1278
1291
  /** The turn SAID it is on it ("got it — checking…") but called no tool and nothing is running: a promise nothing
1279
1292
  * keeps — the user waits on silence (live Para #798: a follow-up while the reflex believed a finished task still ran). */
1280
1293
  private get hollowPromise();
@@ -1341,7 +1354,7 @@ declare class DuplexAgent {
1341
1354
  id?: string;
1342
1355
  replaces?: string;
1343
1356
  followUp?: string | Record<string, string>;
1344
- }): Promise<RunResult>;
1357
+ } & HostInfo): Promise<RunResult>;
1345
1358
  /** Host-named user turns (send opts.id), so a later send can replace one. Bounded; a replace that arrives
1346
1359
  * BEFORE its target (the host's dispatch hops can reorder) leaves a pre-superseded record behind. */
1347
1360
  private userTurns;
package/dist/index.js CHANGED
@@ -10177,6 +10177,22 @@ 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
+ }
10188
+ function hostNoteBlock(note) {
10189
+ return `<host_note>
10190
+ ${note.trim()}
10191
+ </host_note>
10192
+ (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.)
10193
+
10194
+ `;
10195
+ }
10180
10196
  function nowLine() {
10181
10197
  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
10198
  }
@@ -10670,9 +10686,9 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
10670
10686
  /** One reflex model call for a turn; a turn rejected for load (rateLimited) that has not spoken or dispatched yet is rolled
10671
10687
  * back and re-run once on `reflexFallbackModel` — a re-prompt of the same model (the dead-air nudge) only hits the
10672
10688
  * same limit and ends in "could you say that again?". */
10673
- async reflexSend(content) {
10674
- const base = this.voice.transcript.length;
10675
- const res = await this.voice.send(content);
10689
+ async reflexSend(content, note) {
10690
+ const base = this.voice.transcript.length, context = this.lastContext;
10691
+ const res = await this.sendWithContext(content, context, note);
10676
10692
  const fb = this.options.reflexFallbackModel, model = this.voice.options.model;
10677
10693
  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
10694
  log24.warn(`reflex ${model} rate-limited \u2014 re-running the turn on ${fb}`);
@@ -10680,11 +10696,27 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
10680
10696
  this.voice.transcript.length = base;
10681
10697
  this.voice.options.model = fb;
10682
10698
  try {
10683
- return await this.voice.send(content);
10699
+ return await this.sendWithContext(content, context, note);
10684
10700
  } finally {
10685
10701
  this.voice.options.model = model;
10686
10702
  }
10687
10703
  }
10704
+ /** The host's per-line context (call rules, browser context, earlier conversation) rides THIS call only, fenced as the app's —
10705
+ * inside the user turn it read as the user's own words (live #938: "repeat everything I said" recited the call rules). The
10706
+ * stored turn keeps only what the user said, so later turns, briefs and the saved session never carry it. A block, not a
10707
+ * mid-transcript system message: the Anthropic/Google adapters keep only the leading system message. */
10708
+ async sendWithContext(content, context, note) {
10709
+ if (typeof content !== "string" || !context?.trim() && !note?.trim()) return this.voice.send(content);
10710
+ const stored = (note?.trim() ? hostNoteBlock(note) : "") + content, base = this.voice.transcript.length;
10711
+ try {
10712
+ return await this.voice.send((context?.trim() ? hostContextBlock(context) : "") + stored);
10713
+ } finally {
10714
+ const m = this.voice.transcript.slice(base).find((x) => x.role === "user");
10715
+ if (m) m.content = stored;
10716
+ }
10717
+ }
10718
+ /** The last line's host context (call rules, browser context): every reflex call runs under it, not only the user's line. */
10719
+ lastContext;
10688
10720
  /** The turn SAID it is on it ("got it — checking…") but called no tool and nothing is running: a promise nothing
10689
10721
  * keeps — the user waits on silence (live Para #798: a follow-up while the reflex believed a finished task still ran). */
10690
10722
  get hollowPromise() {
@@ -10706,7 +10738,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
10706
10738
  this.stepMarks = [];
10707
10739
  this.dropJudge();
10708
10740
  try {
10709
- await this.voice.send(reminder);
10741
+ await this.sendWithContext(reminder, this.lastContext);
10710
10742
  } catch (e) {
10711
10743
  log24.warn(`follow-through nudge failed: ${e instanceof Error ? e.message : e}`);
10712
10744
  } finally {
@@ -10849,7 +10881,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
10849
10881
  if (this.dispatchAt >= 0) this.dispatchAt = 0;
10850
10882
  this.nudging = true;
10851
10883
  try {
10852
- await this.voice.send(reminder);
10884
+ await this.sendWithContext(reminder, this.lastContext);
10853
10885
  } catch (e) {
10854
10886
  log24.warn(`ack nudge failed: ${e instanceof Error ? e.message : e}`);
10855
10887
  } finally {
@@ -10888,6 +10920,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
10888
10920
  * second Act). The host gets `turn_replace` {id, replaces, rolledBack, followed?}: only a rollback drops the replaced
10889
10921
  * line; `followed` = the kept turn whose follow-up ran (absent: the merged text ran). */
10890
10922
  send(content, opts) {
10923
+ const ctx = opts && (opts.context !== void 0 || opts.note !== void 0) ? { context: opts.context, note: opts.note } : void 0;
10891
10924
  this.clearHoldResume();
10892
10925
  this.holdResumed = false;
10893
10926
  if (opts?.replaces) this.replaceTurn(opts.replaces);
@@ -10913,7 +10946,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
10913
10946
  if (intent === "resume" && !reflexCut) return this.resumeParked();
10914
10947
  if (intent === "aside" || intent === "resume") {
10915
10948
  const told = intent === "aside" ? [...this.parkedRedeliver] : [];
10916
- return this.sendTurn(intent === "aside" ? content + this.asideNote(told) : content, true, rec, ext).then(async (r) => {
10949
+ return this.sendTurn(intent === "aside" ? content + this.asideNote(told) : content, true, rec, ext, ctx).then(async (r) => {
10917
10950
  if (r.finishReason === "aborted" || this.heldThisTurn) return r;
10918
10951
  const missed = unsaidClauses(told.map((p) => p.text), this.turnSpoken, this.options.decider);
10919
10952
  const turn = this.turn;
@@ -10928,10 +10961,10 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
10928
10961
  return r;
10929
10962
  });
10930
10963
  }
10931
- return this.sendTurn(content, false, rec, ext);
10964
+ return this.sendTurn(content, false, rec, ext, ctx);
10932
10965
  });
10933
10966
  }
10934
- return this.sendTurn(typeof content === "string" ? content + this.acceptNote(content) : content, false, rec, ext);
10967
+ return this.sendTurn(typeof content === "string" ? content + this.acceptNote(content) : content, false, rec, ext, ctx);
10935
10968
  }
10936
10969
  /** Host-named user turns (send opts.id), so a later send can replace one. Bounded; a replace that arrives
10937
10970
  * BEFORE its target (the host's dispatch hops can reorder) leaves a pre-superseded record behind. */
@@ -10999,12 +11032,17 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
10999
11032
  return { text: "", steps: 0, finishReason: "stop", messages: [] };
11000
11033
  });
11001
11034
  }
11002
- sendTurn(content, keepParked = false, rec, ext) {
11035
+ sendTurn(content, keepParked = false, rec, ext, host) {
11003
11036
  const spec = this.spec;
11004
11037
  if (spec?.state === "pending") {
11005
11038
  if (typeof content === "string" && speculationConfirms(spec.text, content)) {
11006
11039
  spec.state = "confirmed";
11007
11040
  spec.finalText = content;
11041
+ if (host?.context !== void 0) this.lastContext = host.context;
11042
+ if (host?.note?.trim()) {
11043
+ const m = [...this.voice.transcript].reverse().find((x) => x.role === "user" && x.content === spec.text);
11044
+ if (m) m.content = hostNoteBlock(host.note) + spec.text;
11045
+ }
11008
11046
  if (rec) {
11009
11047
  spec.rec = rec;
11010
11048
  rec.ctl.signal.addEventListener("abort", () => spec.ctl.abort(), { once: true });
@@ -11051,8 +11089,9 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
11051
11089
  }
11052
11090
  if (typeof content === "string") content += spellingNote(content);
11053
11091
  let res;
11092
+ if (host?.context !== void 0) this.lastContext = host.context;
11054
11093
  try {
11055
- res = await this.reflexSend(content);
11094
+ res = await this.reflexSend(content, host?.note);
11056
11095
  } finally {
11057
11096
  if (signal) {
11058
11097
  this.voice.options.signal = ext;
@@ -11101,7 +11140,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
11101
11140
  this.voice.options.signal = spec.ctl.signal;
11102
11141
  let res;
11103
11142
  try {
11104
- res = await this.voice.send(spec.text);
11143
+ res = await this.sendWithContext(spec.text, this.lastContext);
11105
11144
  } catch (e) {
11106
11145
  log24.warn(`speculative turn failed: ${e instanceof Error ? e.message : e}`);
11107
11146
  } finally {
@@ -11380,7 +11419,7 @@ Update: ${brief}`;
11380
11419
  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");
11381
11420
  }
11382
11421
  buildBrief(brief, tier = "act", deliver = true) {
11383
- const recent = this.recentExcerpt();
11422
+ const recent = this.recentExcerpt(), host = this.lastContext?.trim();
11384
11423
  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." : "";
11385
11424
  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." : "") : "";
11386
11425
  return `${nowLine()}.
@@ -11388,7 +11427,10 @@ Update: ${brief}`;
11388
11427
  ` + (recent ? `${brief}
11389
11428
 
11390
11429
  ## Recent conversation (for context)
11391
- ${recent}` : brief) + verify + deliverContract;
11430
+ ${recent}` : brief) + (host ? `
11431
+
11432
+ ## The app's context for this line (not the user's words)
11433
+ ${host}` : "") + verify + deliverContract;
11392
11434
  }
11393
11435
  /** Spawn a detached worker for task `id`; its settlement notifies + enqueues the re-voice turn. */
11394
11436
  spawnWorker(id, label, briefText, tier, brief, followUp) {