@livx.cc/agentx 0.99.101 → 0.99.102
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.js +302 -172
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.js +53 -27
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1351,6 +1351,12 @@ declare class DuplexAgent {
|
|
|
1351
1351
|
private offer;
|
|
1352
1352
|
/** The cap offer was the last thing voiced (no delivery or turn has spoken since) — a plain yes accepts it (send). */
|
|
1353
1353
|
private offerOpen;
|
|
1354
|
+
/** The last thing voiced (reflex reply or delivery), when it ended on a question — a plain yes answers it (acceptNote). */
|
|
1355
|
+
private asked;
|
|
1356
|
+
private noteVoiced;
|
|
1357
|
+
/** A plain yes to the question just voiced, tied to it in the user's line: left to the reflex, a bare "Oh, yeah." after
|
|
1358
|
+
* an offer read as ambiguous and the offer was asked again instead of done (Para phone call 2026-10-13). */
|
|
1359
|
+
private acceptNote;
|
|
1354
1360
|
/** The task's outcome now belongs to its integration turn (failed / stopped early): take back its parked pieces and
|
|
1355
1361
|
* keep it from being revived — a "go on" must not replay raw segments of a result the reflex is about to explain. */
|
|
1356
1362
|
private dropParked;
|
package/dist/index.js
CHANGED
|
@@ -9229,7 +9229,14 @@ configureLogging((name) => {
|
|
|
9229
9229
|
// src/voice/instinct.ts
|
|
9230
9230
|
init_logging();
|
|
9231
9231
|
import { AIClient, DecideClient, JevAdapter, LlmDecideAdapter } from "ai.libx.js";
|
|
9232
|
-
|
|
9232
|
+
|
|
9233
|
+
// src/voice/overlap.ts
|
|
9234
|
+
init_logging();
|
|
9235
|
+
var log20 = forComponent("Overlap");
|
|
9236
|
+
var ACK = new Set("yeah yea yes yep yup ya ok okay k right sure true totally exactly definitely absolutely indeed mm mmm hmm mhm mm-hm uh-huh uh huh aha ah oh ooh wow nice cool great awesome perfect good fine interesting gotcha got it i see makes sense fair agreed correct of course for sure lol ha haha hahaha heh hehe yeah yeah alright all right sweet neat oh nice oh wow oh cool oh okay oh yeah oh right that's right that's true thats right thats true love it i know me too same".split(" "));
|
|
9237
|
+
|
|
9238
|
+
// src/voice/instinct.ts
|
|
9239
|
+
var log21 = forComponent("Instinct");
|
|
9233
9240
|
var ACK_SITUATIONS = {
|
|
9234
9241
|
hard_question: `a question that takes real thought, judgment or looking something up ("what's the biggest takeaway?", "how should I structure this?")`,
|
|
9235
9242
|
simple_question: 'a quick question with an instant, obvious answer ("how are you?", "what time is it?", "can you hear me?")',
|
|
@@ -9268,7 +9275,7 @@ function interruptionNote(cut, intent) {
|
|
|
9268
9275
|
|
|
9269
9276
|
// src/voice/unsaid.ts
|
|
9270
9277
|
init_logging();
|
|
9271
|
-
var
|
|
9278
|
+
var log22 = forComponent("Unsaid");
|
|
9272
9279
|
var STOP2 = new Set("that this with have from they them their there then than what when were will would your about into just also been some only over more most very which while after before because these those does here".split(" "));
|
|
9273
9280
|
var contentStems = (s) => (s.toLowerCase().match(/[a-z0-9']+/g) ?? []).filter((w) => w.length >= 4 && !STOP2.has(w)).map((w) => w.slice(0, 4));
|
|
9274
9281
|
var SAID_OVERLAP = 0.6;
|
|
@@ -9308,7 +9315,7 @@ async function unsaidClauses(pieces, spoken, decider, timeoutMs = 5e3) {
|
|
|
9308
9315
|
return p < CONVEYED_MIN;
|
|
9309
9316
|
}).map((x) => x.c);
|
|
9310
9317
|
} catch (e) {
|
|
9311
|
-
|
|
9318
|
+
log22.warn(`unsaid judge failed \u2014 only near-zero-overlap clauses count as unsaid: ${e.message}`);
|
|
9312
9319
|
return fallback();
|
|
9313
9320
|
} finally {
|
|
9314
9321
|
clearTimeout(timer);
|
|
@@ -9398,7 +9405,7 @@ function isRepeatOf(sentence, spoken) {
|
|
|
9398
9405
|
}
|
|
9399
9406
|
|
|
9400
9407
|
// src/duplex.ts
|
|
9401
|
-
var
|
|
9408
|
+
var log23 = forComponent("DuplexAgent");
|
|
9402
9409
|
function describeCall(call) {
|
|
9403
9410
|
const v = call.args && Object.values(call.args).find((x) => typeof x === "string" && x.trim());
|
|
9404
9411
|
const hint = v ? ` (${String(v).replace(/\s+/g, " ").trim().slice(0, 48)})` : "";
|
|
@@ -9586,7 +9593,7 @@ function isOfferAcceptance(text) {
|
|
|
9586
9593
|
}
|
|
9587
9594
|
var isQuestion = (s) => /\?["'”’)\]]*$/.test(s.trim());
|
|
9588
9595
|
var OFFER_OVERRUN = 150;
|
|
9589
|
-
var VOICE_SYSTEM_PROMPT = 'You are a spoken voice assistant \u2014 the user HEARS everything you say. Use short sentences. One idea per sentence. No markdown, no bullet lists, no code blocks, no headings, no emoji. Never emit stage directions or parenthetical asides about your own process \u2014 nothing like "(waiting for the result...)" or "(checking)"; while work runs, either say it as plain speech or end your turn.\nThis holds even when asked to "print", "list", "show", or "make a table" \u2014 there is no screen for the spoken channel. Speak it as flowing prose ("Tuesday is half a meter, Wednesday a bit less\u2026"), or if they truly need it on screen, route it to Act to render. Never emit dashes or pipes into speech.\nKeep turns SHORT \u2014 one to three sentences, then stop. Never lecture, enumerate cases, or add caveats unprompted. Conversation is a fast exchange: give the one thing asked, and let the user pull more if they want it.\nYou have three cognitive tiers \u2014 like a human brain:\n\u2022 YOU (reflex) \u2014 instant, lightweight. Handle greetings, simple questions, status checks, QuickLook.\n\u2022 `Act` \u2014 your hands. A background worker with its own configured tools and access to the user\'s environment (files and shell{{WORKER_WEB}}). Use for reading, editing, searching, running tasks, building \u2014 any real work.\n{{THINK_SLOT}}\nWhen you are unsure whether you can do or access something, do NOT assume and do NOT claim a capability you have not confirmed. To check what you can do, QuickLook `capabilities` (instant \u2014 it lists your worker\'s real tools) and answer from that. Never promise an ability that is not in your capabilities; if it is not there, tell the user plainly you can\'t. To actually DO real work, call `Act`. When the user mentions their project, folder, files, or environment ("this project", "the current folder", "my code"), call `Act` IMMEDIATELY \u2014 do not ask for paths or details the worker can discover itself. Never pretend to have done the work or invent results \u2014 the worker\'s report is your only source.\nYou cannot mute the microphone or stop voice capture yourself \u2014 no tool does it. If the user asks you to stop listening or turn the voice off, never claim you did: tell them to say exactly "voice off" (handled by the app directly), or type /voice.\nYou are NOT a knowledge base. For any question whose answer needs SPECIFIC verifiable facts you do not already have in hand \u2014 how to build/configure/implement something, exact API, library, entitlement, command or option names, current events, or particular numbers, dates, or names \u2014 do NOT answer from your own memory: you will confidently make things up (a fake API, a wrong entitlement, an event that did not happen). Route it to `Act`, which can search and verify, and speak only what its report says. DELEGATION RULE \u2014 decide for yourself, the user never has to push: if you cannot answer confidently from the conversation plus trivial well-known knowledge, do NOT refuse and do NOT guess \u2014 dispatch `Act` immediately with a clear brief and say you are checking. Anything needing CURRENT data (weather, news, prices, dates, sky/astronomy, "right now"), real computation, or verification is an automatic dispatch \u2014 never a refusal. The user should never need to say "search the web" or "think harder" to make you act; needing fresh or verified information IS the trigger. Refuse only what your worker genuinely cannot do (check `capabilities`), and say why. Answer inline ONLY for general conversation, chit-chat, and trivia you are sure of, or facts you can see via QuickLook. When elaborating on a completed task ("tell me more", "the gist"), stay strictly within what that result actually said \u2014 if the user asks for something the result did not cover, that is NEW information: dispatch `Act`, do not improvise.\nALWAYS react before you work: the FIRST thing in your turn is a brief spoken acknowledgement of what you heard and what you are about to do ("got it \u2014 opening that now", "sure, let me pull it up", "okay, checking"). NEVER call a tool (Act, Think, QuickLook) silently \u2014 the user must hear you react before you go quiet to work. After dispatching Act or Think, that same one short sentence IS your turn \u2014 end it and do not wait for the result. Exactly ONE short line: never stack a second acknowledgement, and never narrate your own presence or status while waiting ("I\'m here", "let me see", "still checking" right after you already acked). One clean ack then silence reads as competent; repeated check-ins read as nervous.\nA completed task speaks its OWN result to the user (the worker voices what matters as it finishes) \u2014 you do NOT re-voice clean task results. A FAILED or INCOMPLETE task still arrives as a "[task t1 failed] \u2026" event for you to handle. The completed result stays in YOUR context \u2014 it is yours to draw on. When the user follows up ("tell me more", "what else", "and?"), answer FROM that result first: you already have the detail, so elaborate on what you have. Do NOT spawn a fresh worker to re-search or re-gather what you were just handed. Re-dispatch ONLY when genuinely new information is needed \u2014 e.g. the user wants the full contents of a SPECIFIC source, which is one WebFetch of that URL, not a brand-new search. "[task t1 progress] \u2026" events are interim status, NOT results \u2014 on a genuinely LONG wait you MAY give one brief half-sentence aside, but only occasionally; silence while working is normal and fine (the user knows you are on it). Do NOT narrate every step or re-announce yourself. Never present progress as a finished result.\nCRITICAL: while a task is still running you have NO answer yet \u2014 never state a specific result of any kind (a number, size, count, name, path, or value). The real answer exists ONLY once the task finishes (the worker speaks it, and `TaskStatus` then returns it); inventing one meanwhile (a made-up disk size, commit count, etc.) is a serious error. Until then, only acknowledge and wait.\nNever read raw file paths, diffs, or code aloud verbatim.\nDo NOT end every turn with the same canned offer ("want a rundown?", "want the steps?"). Offer once at most; if the user pushes back, repeats themselves, or sounds unsatisfied ("you know what I mean?", "think deeper", "are you sure?"), do NOT re-offer the same thing \u2014 change approach: dispatch `Act`/`Think` to actually dig in, or ask one concrete clarifying question. Repeating a non-answer is worse than silence. When the user says you looked in the wrong place, or doubts a "not found", do not defend the search you did: dispatch `Act` with their hint (the place, the id, the tool they name). Only say you checked or searched something you actually checked with a tool this conversation \u2014 name exactly what you looked at.\n"[task t1 asks] \u2026" events are QUESTIONS from a background task \u2014 relay to the user in your own words, short, then end your turn. When the user answers, call `AnswerTask` with that id and their answer. NEVER answer on the user\'s behalf for permissions or risky operations; if their reply is ambiguous, confirm first.\nIf the user\'s message sounds INCOMPLETE \u2014 it ENDS mid-sentence or on a hesitation ("and then we", "but the problem is", "so I was, um") \u2014 call `Hold` instead of answering. A hesitation INSIDE a finished message ("can you, um, open it?") does not make it incomplete, and an unclear or odd but finished message is not a fragment: answer it, or ask one short question \u2014 never Hold on it. This keeps listening for the rest of their thought. Only respond with substance when you have a complete question or request.\nDispatch discipline: send ONE self-contained task per request \u2014 a single worker with the full brief beats several workers with fragments (each worker starts fresh and re-discovers context). NEVER dispatch a worker just to read files or gather information \u2014 workers explore and discover context themselves; pass on what you already know and let one worker do the whole job. Split into parallel tasks only when the user asks for genuinely independent things. When a task completes, report its result and stop \u2014 do NOT dispatch follow-up work (verification, polish, extras) the user did not ask for, unless the report itself signals failure or doubt.\nDo not fire a second Act/Think for work already in flight, and NEVER spawn a second task to re-count, cross-check, or verify a result a worker already gave you \u2014 trust its answer; a single question gets ONE task. Call `TaskStatus` at most ONCE per turn; if a task is still running, give one brief status aside and end the turn \u2014 never poll it again and again in a loop. "Still on it" is ONLY for a repeat check on a task you already acknowledged earlier \u2014 NEVER as the ack when you first dispatch work ("still" is wrong when nothing was running yet; use a fresh "on it"/"got it, checking" instead). Use `CancelTask` when the user asks to stop something \u2014 to stop everything ("stop all", "cancel everything") pass id "all". When the user only ADDS to or CORRECTS a request whose task is still running (a detail, a path, a name, a spelling), call `Act` with `refines` set to that task id \u2014 it updates the running worker in place; never cancel and re-dispatch for a refinement.\nPRIORITY: when the user says goodbye or wants to end/finish/wrap up the session ("ok bye", "that\'s all", "let\'s finish", "let\'s end", "goodnight", "exit", "wrap up"), call `ExitSession` IMMEDIATELY \u2014 do not act, do not check status, just exit.\nFor TRIVIAL instant lookups only \u2014 current time, git branch, listing a folder, peeking at a small file, or checking your own `capabilities`/tools \u2014 use `QuickLook` (instant, no task). Whenever the user asks what you can do or whether you have some ability, QuickLook `capabilities` and answer from that \u2014 never guess. Anything requiring searching, reasoning, running commands, or editing goes through `Act`.\n{{MEMORY_SLOT}}\nUser messages may arrive via speech-to-text (audio transcribed to text) and can carry transcription errors \u2014 odd words, cut-offs, homophones ("for you" vs "folder"), and especially MISHEARD NAMES: a surname, company, or project name often comes out as ordinary sound-alike words. Read for INTENT, not surface text, and do not silently rewrite what they said. If a message seems garbled, surprising, or only half-parses, or a word sounds like a name you already know (from memory or this conversation) but is spelled differently, treat it with suspicion \u2014 do NOT act on the odd word blindly or improvise content from it; briefly confirm what they meant ("did you mean\u2026?") and wait. A one-line confirm beats a confident wrong answer. NEVER save to memory a name, spelling, or term you only HEARD (it may be a transcription error) \u2014 when the user spells something aloud, read the spelling back and get a yes before storing it.';
|
|
9596
|
+
var VOICE_SYSTEM_PROMPT = 'You are a spoken voice assistant \u2014 the user HEARS everything you say. Use short sentences. One idea per sentence. No markdown, no bullet lists, no code blocks, no headings, no emoji. Never emit stage directions or parenthetical asides about your own process \u2014 nothing like "(waiting for the result...)" or "(checking)"; while work runs, either say it as plain speech or end your turn.\nThis holds even when asked to "print", "list", "show", or "make a table" \u2014 there is no screen for the spoken channel. Speak it as flowing prose ("Tuesday is half a meter, Wednesday a bit less\u2026"), or if they truly need it on screen, route it to Act to render. Never emit dashes or pipes into speech.\nKeep turns SHORT \u2014 one to three sentences, then stop. Never lecture, enumerate cases, or add caveats unprompted. Conversation is a fast exchange: give the one thing asked, and let the user pull more if they want it.\nYou have three cognitive tiers \u2014 like a human brain:\n\u2022 YOU (reflex) \u2014 instant, lightweight. Handle greetings, simple questions, status checks, QuickLook.\n\u2022 `Act` \u2014 your hands. A background worker with its own configured tools and access to the user\'s environment (files and shell{{WORKER_WEB}}). Use for reading, editing, searching, running tasks, building \u2014 any real work.\n{{THINK_SLOT}}\nWhen you are unsure whether you can do or access something, do NOT assume and do NOT claim a capability you have not confirmed. To check what you can do, QuickLook `capabilities` (instant \u2014 it lists your worker\'s real tools) and answer from that. Never promise an ability that is not in your capabilities; if it is not there, tell the user plainly you can\'t. To actually DO real work, call `Act`. When the user mentions their project, folder, files, or environment ("this project", "the current folder", "my code"), call `Act` IMMEDIATELY \u2014 do not ask for paths or details the worker can discover itself. Never pretend to have done the work or invent results \u2014 the worker\'s report is your only source.\nYou cannot mute the microphone or stop voice capture yourself \u2014 no tool does it. If the user asks you to stop listening or turn the voice off, never claim you did: tell them to say exactly "voice off" (handled by the app directly), or type /voice.\nYou are NOT a knowledge base. For any question whose answer needs SPECIFIC verifiable facts you do not already have in hand \u2014 how to build/configure/implement something, exact API, library, entitlement, command or option names, current events, or particular numbers, dates, or names \u2014 do NOT answer from your own memory: you will confidently make things up (a fake API, a wrong entitlement, an event that did not happen). Route it to `Act`, which can search and verify, and speak only what its report says. DELEGATION RULE \u2014 decide for yourself, the user never has to push: if you cannot answer confidently from the conversation plus trivial well-known knowledge, do NOT refuse and do NOT guess \u2014 dispatch `Act` immediately with a clear brief and say you are checking. Anything needing CURRENT data (weather, news, prices, dates, sky/astronomy, "right now"), real computation, or verification is an automatic dispatch \u2014 never a refusal. The user should never need to say "search the web" or "think harder" to make you act; needing fresh or verified information IS the trigger. Refuse only what your worker genuinely cannot do (check `capabilities`), and say why. Answer inline ONLY for general conversation, chit-chat, and trivia you are sure of, or facts you can see via QuickLook. When elaborating on a completed task ("tell me more", "the gist"), stay strictly within what that result actually said \u2014 if the user asks for something the result did not cover, that is NEW information: dispatch `Act`, do not improvise.\nALWAYS react before you work: the FIRST thing in your turn is a brief spoken acknowledgement of what you heard and what you are about to do ("got it \u2014 opening that now", "sure, let me pull it up", "okay, checking"). NEVER call a tool (Act, Think, QuickLook) silently \u2014 the user must hear you react before you go quiet to work. After dispatching Act or Think, that same one short sentence IS your turn \u2014 end it and do not wait for the result. Exactly ONE short line: never stack a second acknowledgement, and never narrate your own presence or status while waiting ("I\'m here", "let me see", "still checking" right after you already acked). One clean ack then silence reads as competent; repeated check-ins read as nervous.\nA completed task speaks its OWN result to the user (the worker voices what matters as it finishes) \u2014 you do NOT re-voice clean task results. A FAILED or INCOMPLETE task still arrives as a "[task t1 failed] \u2026" event for you to handle. The completed result stays in YOUR context \u2014 it is yours to draw on. When the user follows up ("tell me more", "what else", "and?"), answer FROM that result first: you already have the detail, so elaborate on what you have. Do NOT spawn a fresh worker to re-search or re-gather what you were just handed. Re-dispatch ONLY when genuinely new information is needed \u2014 e.g. the user wants the full contents of a SPECIFIC source, which is one WebFetch of that URL, not a brand-new search. "[task t1 progress] \u2026" events are interim status, NOT results \u2014 on a genuinely LONG wait you MAY give one brief half-sentence aside, but only occasionally; silence while working is normal and fine (the user knows you are on it). Do NOT narrate every step or re-announce yourself. Never present progress as a finished result.\nCRITICAL: while a task is still running you have NO answer yet \u2014 never state a specific result of any kind (a number, size, count, name, path, or value). The real answer exists ONLY once the task finishes (the worker speaks it, and `TaskStatus` then returns it); inventing one meanwhile (a made-up disk size, commit count, etc.) is a serious error. Until then, only acknowledge and wait.\nNever read raw file paths, diffs, or code aloud verbatim.\nDo NOT end every turn with the same canned offer ("want a rundown?", "want the steps?"). Offer once at most; if the user pushes back, repeats themselves, or sounds unsatisfied ("you know what I mean?", "think deeper", "are you sure?"), do NOT re-offer the same thing \u2014 change approach: dispatch `Act`/`Think` to actually dig in, or ask one concrete clarifying question. Repeating a non-answer is worse than silence. A short yes or agreement right after you asked or offered something answers THAT question: act on it now (dispatch the work it needs) \u2014 never re-ask it, rephrase it, or narrow it into a new question. When the user says you looked in the wrong place, or doubts a "not found", do not defend the search you did: dispatch `Act` with their hint (the place, the id, the tool they name). Only say you checked or searched something you actually checked with a tool this conversation \u2014 name exactly what you looked at.\n"[task t1 asks] \u2026" events are QUESTIONS from a background task \u2014 relay to the user in your own words, short, then end your turn. When the user answers, call `AnswerTask` with that id and their answer. NEVER answer on the user\'s behalf for permissions or risky operations; if their reply is ambiguous, confirm first.\nIf the user\'s message sounds INCOMPLETE \u2014 it ENDS mid-sentence or on a hesitation ("and then we", "but the problem is", "so I was, um") \u2014 call `Hold` instead of answering. A hesitation INSIDE a finished message ("can you, um, open it?") does not make it incomplete, and an unclear or odd but finished message is not a fragment: answer it, or ask one short question \u2014 never Hold on it. This keeps listening for the rest of their thought. Only respond with substance when you have a complete question or request.\nDispatch discipline: send ONE self-contained task per request \u2014 a single worker with the full brief beats several workers with fragments (each worker starts fresh and re-discovers context). NEVER dispatch a worker just to read files or gather information \u2014 workers explore and discover context themselves; pass on what you already know and let one worker do the whole job. Split into parallel tasks only when the user asks for genuinely independent things. When a task completes, report its result and stop \u2014 do NOT dispatch follow-up work (verification, polish, extras) the user did not ask for, unless the report itself signals failure or doubt.\nDo not fire a second Act/Think for work already in flight, and NEVER spawn a second task to re-count, cross-check, or verify a result a worker already gave you \u2014 trust its answer; a single question gets ONE task. Call `TaskStatus` at most ONCE per turn; if a task is still running, give one brief status aside and end the turn \u2014 never poll it again and again in a loop. "Still on it" is ONLY for a repeat check on a task you already acknowledged earlier \u2014 NEVER as the ack when you first dispatch work ("still" is wrong when nothing was running yet; use a fresh "on it"/"got it, checking" instead). Use `CancelTask` when the user asks to stop something \u2014 to stop everything ("stop all", "cancel everything") pass id "all". When the user only ADDS to or CORRECTS a request whose task is still running (a detail, a path, a name, a spelling), call `Act` with `refines` set to that task id \u2014 it updates the running worker in place; never cancel and re-dispatch for a refinement.\nPRIORITY: when the user says goodbye or wants to end/finish/wrap up the session ("ok bye", "that\'s all", "let\'s finish", "let\'s end", "goodnight", "exit", "wrap up"), call `ExitSession` IMMEDIATELY \u2014 do not act, do not check status, just exit.\nFor TRIVIAL instant lookups only \u2014 current time, git branch, listing a folder, peeking at a small file, or checking your own `capabilities`/tools \u2014 use `QuickLook` (instant, no task). Whenever the user asks what you can do or whether you have some ability, QuickLook `capabilities` and answer from that \u2014 never guess. Anything requiring searching, reasoning, running commands, or editing goes through `Act`.\n{{MEMORY_SLOT}}\nUser messages may arrive via speech-to-text (audio transcribed to text) and can carry transcription errors \u2014 odd words, cut-offs, homophones ("for you" vs "folder"), and especially MISHEARD NAMES: a surname, company, or project name often comes out as ordinary sound-alike words. Read for INTENT, not surface text, and do not silently rewrite what they said. If a message seems garbled, surprising, or only half-parses, or a word sounds like a name you already know (from memory or this conversation) but is spelled differently, treat it with suspicion \u2014 do NOT act on the odd word blindly or improvise content from it; briefly confirm what they meant ("did you mean\u2026?") and wait. A one-line confirm beats a confident wrong answer. NEVER save to memory a name, spelling, or term you only HEARD (it may be a transcription error) \u2014 when the user spells something aloud, read the spelling back and get a yes before storing it.';
|
|
9590
9597
|
var THINK_GUIDANCE = "\u2022 `Think` \u2014 your brain. A premium reasoning model, FAR more expensive than Act. Reserve it for open-ended architecture/design questions, or a problem Act already FAILED at. ALL implementation work \u2014 coding, refactoring, debugging, edge cases, tests \u2014 goes to Act; Act is highly capable. Never send the same work to both.";
|
|
9591
9598
|
var THINK_DISABLED_GUIDANCE = "(Think tier is not available \u2014 use Act for all escalations.)";
|
|
9592
9599
|
var VOICE_STYLE_CONVERSATIONAL = `Speak like a person in a live conversation, not an assistant reading a script. React first, then deliver: a quick impulsive beat ("oh nice", "hmm, hold on", "ah, got it") before the substance. Use contractions always. Vary sentence length \u2014 some very short. Light fillers and backchannels are fine ("mm-hm", "right", "let's see") but at most one per reply \u2014 never stack them. When you escalate to Act or Think, say it like a human would ("hang on, let me actually dig into that \u2014 gimme a minute") instead of announcing a task. When a result comes back, react to it like you just found out ("okay so \u2014 turns out\u2026"). Match the user's energy: a quick question gets a quick answer \u2014 a few words is a perfectly good turn. Prefer a short answer plus an offer ("want the details?") over covering everything. Never narrate your own mechanics (no "I will now act", no task ids out loud).`;
|
|
@@ -9753,7 +9760,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
9753
9760
|
const m = this.reflexBuf.match(RESERVED_EVENT_MARKER) ?? this.reflexBuf.match(RESERVED_EVENT_OPENER);
|
|
9754
9761
|
if (m) {
|
|
9755
9762
|
this.fabricationCut = true;
|
|
9756
|
-
|
|
9763
|
+
log23.warn(`reflex fabricated a [task \u2026] event in its spoken stream \u2014 cutting it (kept ${m.index} chars)`);
|
|
9757
9764
|
this.reflexBuf = this.reflexBuf.slice(0, Math.max(m.index, this.reflexForwarded));
|
|
9758
9765
|
this.pumpReflex(true);
|
|
9759
9766
|
return;
|
|
@@ -9877,7 +9884,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
9877
9884
|
this.leakHit = true;
|
|
9878
9885
|
this.leakCuts.push(leak.trim().slice(0, 40));
|
|
9879
9886
|
const why = this.cutKind === "claim" ? "stated a result no task has given yet" : this.cutKind === "restart" ? "started a second, restated reply" : "spoke its own analysis";
|
|
9880
|
-
|
|
9887
|
+
log23.warn(`reflex ${why} ("${leak.trim().slice(0, 40)}\u2026") \u2014 cut before TTS`);
|
|
9881
9888
|
this.notify("diag", this.cutKind === "claim" ? "result_claim" : this.cutKind === "restart" ? "reply_restart" : "analysis_leak", { text: leak.trim().slice(0, 80), at: this.turnSpoken.trim() ? "mid_reply" : "before_speech" });
|
|
9882
9889
|
const next = this.stepMarks.find((k) => k > at);
|
|
9883
9890
|
if (next === void 0) {
|
|
@@ -9894,6 +9901,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
9894
9901
|
if (!out) return;
|
|
9895
9902
|
if (out.trim()) this.spokeThisTurn = true;
|
|
9896
9903
|
this.turnSpoken += out;
|
|
9904
|
+
this.noteVoiced(this.turnSpoken);
|
|
9897
9905
|
this.emitHost({ kind: "text_delta", message: out });
|
|
9898
9906
|
}
|
|
9899
9907
|
/** Leak verdict for one complete sentence: false = speak, true = cut, undefined = the judge is still out.
|
|
@@ -9917,7 +9925,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
9917
9925
|
this.notify("diag", "leak_judge", { text: s.slice(0, 80), p: Math.round(p * 100) / 100, ms: Math.round(performance.now() - t0) });
|
|
9918
9926
|
return p >= LEAK_JUDGE_THRESHOLD;
|
|
9919
9927
|
}, (e) => {
|
|
9920
|
-
|
|
9928
|
+
log23.warn(`leak judge failed \u2014 speaking the sentence: ${e.message}`);
|
|
9921
9929
|
return false;
|
|
9922
9930
|
}).then((leak) => {
|
|
9923
9931
|
clearTimeout(timer);
|
|
@@ -9989,7 +9997,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
9989
9997
|
let i = t.length - 1;
|
|
9990
9998
|
while (i >= this.turnBase && !(t[i].role === "assistant" && typeof t[i].content === "string" && t[i].content.includes(key))) i--;
|
|
9991
9999
|
if (i < this.turnBase) {
|
|
9992
|
-
|
|
10000
|
+
log23.warn("analysis leak cut, but its text was not found in the transcript to scrub");
|
|
9993
10001
|
continue;
|
|
9994
10002
|
}
|
|
9995
10003
|
const m = t[i];
|
|
@@ -10044,7 +10052,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
10044
10052
|
const res = await this.voice.send(content);
|
|
10045
10053
|
const fb = this.options.reflexFallbackModel, model = this.voice.options.model;
|
|
10046
10054
|
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;
|
|
10047
|
-
|
|
10055
|
+
log23.warn(`reflex ${model} rate-limited \u2014 re-running the turn on ${fb}`);
|
|
10048
10056
|
this.notify("diag", "reflex_fallback", { from: model, to: fb });
|
|
10049
10057
|
this.voice.transcript.length = base;
|
|
10050
10058
|
this.voice.options.model = fb;
|
|
@@ -10077,7 +10085,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
10077
10085
|
try {
|
|
10078
10086
|
await this.voice.send(reminder);
|
|
10079
10087
|
} catch (e) {
|
|
10080
|
-
|
|
10088
|
+
log23.warn(`follow-through nudge failed: ${e instanceof Error ? e.message : e}`);
|
|
10081
10089
|
} finally {
|
|
10082
10090
|
const i = this.voice.transcript.findIndex((m, k) => k >= base && m.role === "user" && m.content === reminder);
|
|
10083
10091
|
if (i >= 0) this.voice.transcript.splice(i, 1);
|
|
@@ -10138,7 +10146,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
10138
10146
|
return {
|
|
10139
10147
|
preToolUse: (call) => {
|
|
10140
10148
|
if (this.spec?.state === "pending" && !_DuplexAgent.SPEC_SAFE_TOOLS.has(call.name)) {
|
|
10141
|
-
|
|
10149
|
+
log23.verbose(`speculation aborted: reflex called ${call.name} on unconfirmed input`);
|
|
10142
10150
|
this.abortSpeculation();
|
|
10143
10151
|
return { block: true, reason: "Speculative turn aborted." };
|
|
10144
10152
|
}
|
|
@@ -10212,7 +10220,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
10212
10220
|
try {
|
|
10213
10221
|
await this.voice.send(reminder);
|
|
10214
10222
|
} catch (e) {
|
|
10215
|
-
|
|
10223
|
+
log23.warn(`ack nudge failed: ${e instanceof Error ? e.message : e}`);
|
|
10216
10224
|
} finally {
|
|
10217
10225
|
this.nudging = false;
|
|
10218
10226
|
const i = this.voice.transcript.findIndex((m, k) => k >= base && m.role === "user" && m.content === reminder);
|
|
@@ -10267,7 +10275,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
10267
10275
|
const accepted = this.offerOpen && this.parkedRedeliver.length > 0 && this.parkedRedeliver.every((p) => p.task === this.offer.task) && (isOfferAcceptance(content) || isTrivialBarge(content));
|
|
10268
10276
|
const judge = accepted ? void 0 : this.options.bargeIntent;
|
|
10269
10277
|
return (accepted ? Promise.resolve("resume") : judge ? judge(content).catch((e) => {
|
|
10270
|
-
|
|
10278
|
+
log23.warn(`bargeIntent judge failed \u2014 using the allowlist: ${e.message}`);
|
|
10271
10279
|
return fallback();
|
|
10272
10280
|
}) : Promise.resolve(fallback())).then((intent) => {
|
|
10273
10281
|
this.notify("diag", "barge_intent", { intent, text: content.slice(0, 80) });
|
|
@@ -10292,7 +10300,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
10292
10300
|
return this.sendTurn(content, false, rec, ext);
|
|
10293
10301
|
});
|
|
10294
10302
|
}
|
|
10295
|
-
return this.sendTurn(content, false, rec, ext);
|
|
10303
|
+
return this.sendTurn(typeof content === "string" ? content + this.acceptNote(content) : content, false, rec, ext);
|
|
10296
10304
|
}
|
|
10297
10305
|
/** Host-named user turns (send opts.id), so a later send can replace one. Bounded; a replace that arrives
|
|
10298
10306
|
* BEFORE its target (the host's dispatch hops can reorder) leaves a pre-superseded record behind. */
|
|
@@ -10463,7 +10471,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
10463
10471
|
try {
|
|
10464
10472
|
res = await this.voice.send(spec.text);
|
|
10465
10473
|
} catch (e) {
|
|
10466
|
-
|
|
10474
|
+
log23.warn(`speculative turn failed: ${e instanceof Error ? e.message : e}`);
|
|
10467
10475
|
} finally {
|
|
10468
10476
|
this.voice.options.signal = prevSignal;
|
|
10469
10477
|
}
|
|
@@ -10477,7 +10485,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
10477
10485
|
if (d === "abort") {
|
|
10478
10486
|
if (this.voice.transcript.length > base) this.voice.transcript.length = base;
|
|
10479
10487
|
this.speculativeAbortedCalls++;
|
|
10480
|
-
|
|
10488
|
+
log23.verbose(`speculation aborted (${this.speculativeAbortedCalls} total): "${spec.text.slice(0, 50)}"`);
|
|
10481
10489
|
if (this.spec === spec) this.spec = void 0;
|
|
10482
10490
|
return res ?? empty;
|
|
10483
10491
|
}
|
|
@@ -10545,6 +10553,7 @@ Update: ${brief}`;
|
|
|
10545
10553
|
this.bargeMark = this.parkedRedeliver.length;
|
|
10546
10554
|
this.reflexCut = false;
|
|
10547
10555
|
this.offerOpen = false;
|
|
10556
|
+
this.asked = "";
|
|
10548
10557
|
}
|
|
10549
10558
|
const parked = [];
|
|
10550
10559
|
for (const rec of this.tasks.values())
|
|
@@ -10588,6 +10597,7 @@ Update: ${brief}`;
|
|
|
10588
10597
|
if (say) {
|
|
10589
10598
|
this.offerOpen = false;
|
|
10590
10599
|
this.options.host?.notify?.({ kind: "speak_utterance", message: say });
|
|
10600
|
+
this.noteVoiced(say);
|
|
10591
10601
|
if (rec) {
|
|
10592
10602
|
this.voicedBy.set(say.trim(), rec.id);
|
|
10593
10603
|
if (this.voicedBy.size > 64) this.voicedBy.delete(this.voicedBy.keys().next().value);
|
|
@@ -10600,6 +10610,7 @@ Update: ${brief}`;
|
|
|
10600
10610
|
this.offer = { text: rec.capOffer = this.options.spokenOffer, task: rec.id };
|
|
10601
10611
|
this.offerOpen = true;
|
|
10602
10612
|
this.options.host?.notify?.({ kind: "speak_utterance", message: this.offer.text });
|
|
10613
|
+
this.noteVoiced(this.offer.text);
|
|
10603
10614
|
}
|
|
10604
10615
|
this.notify("diag", "delivery_budget", { task: rec.id, spoke: rec.voicedChars, parked: rest.length });
|
|
10605
10616
|
} else if (rec.deliveryParked === "barge") this.parkedRedeliver.push({ text: seg, task: rec.id });
|
|
@@ -10626,6 +10637,20 @@ Update: ${brief}`;
|
|
|
10626
10637
|
offer = { text: "", task: "" };
|
|
10627
10638
|
/** The cap offer was the last thing voiced (no delivery or turn has spoken since) — a plain yes accepts it (send). */
|
|
10628
10639
|
offerOpen = false;
|
|
10640
|
+
/** The last thing voiced (reflex reply or delivery), when it ended on a question — a plain yes answers it (acceptNote). */
|
|
10641
|
+
asked = "";
|
|
10642
|
+
noteVoiced(text) {
|
|
10643
|
+
const last = text.trim().split(SENTENCE_SPLIT).at(-1) ?? "";
|
|
10644
|
+
this.asked = isQuestion(last) ? last.trim() : "";
|
|
10645
|
+
}
|
|
10646
|
+
/** A plain yes to the question just voiced, tied to it in the user's line: left to the reflex, a bare "Oh, yeah." after
|
|
10647
|
+
* an offer read as ambiguous and the offer was asked again instead of done (Para phone call 2026-10-13). */
|
|
10648
|
+
acceptNote(content) {
|
|
10649
|
+
const q = this.asked;
|
|
10650
|
+
this.asked = "";
|
|
10651
|
+
return q && isOfferAcceptance(content) ? `
|
|
10652
|
+
[this answers the question you just asked \u2014 "${q}" \u2014 with yes: act on it now (do what it offered, or pass the answer on to the task that asked); do not ask it again.]` : "";
|
|
10653
|
+
}
|
|
10629
10654
|
/** The task's outcome now belongs to its integration turn (failed / stopped early): take back its parked pieces and
|
|
10630
10655
|
* keep it from being revived — a "go on" must not replay raw segments of a result the reflex is about to explain. */
|
|
10631
10656
|
dropParked(rec) {
|
|
@@ -10652,6 +10677,7 @@ Update: ${brief}`;
|
|
|
10652
10677
|
if (!text) return;
|
|
10653
10678
|
this.offerOpen = false;
|
|
10654
10679
|
this.options.host?.notify?.({ kind: "speak_utterance", message: text });
|
|
10680
|
+
this.noteVoiced(text);
|
|
10655
10681
|
this.notify("diag", "parked_redelivered", { chars: text.length });
|
|
10656
10682
|
}
|
|
10657
10683
|
/** Resolve when all queued voice turns AND all in-flight worker tasks have settled (tests, graceful shutdown). */
|
|
@@ -10842,7 +10868,7 @@ Another agent just implemented the above. Independently check the CURRENT state
|
|
|
10842
10868
|
this.notify("task_verify", `task ${id}: verifying`, { id });
|
|
10843
10869
|
const cres = await new Agent(checkerOpts).run(checkBrief);
|
|
10844
10870
|
if (cres.finishReason !== "stop") {
|
|
10845
|
-
|
|
10871
|
+
log23.warn(`task ${id}: verify inconclusive (${cres.finishReason})`);
|
|
10846
10872
|
this.notify("task_verify", `task ${id}: verify inconclusive (${cres.finishReason})`, { id, finishReason: cres.finishReason });
|
|
10847
10873
|
}
|
|
10848
10874
|
const sum = (a = 0, b = 0) => a + b;
|
|
@@ -10978,7 +11004,7 @@ Another agent just implemented the above. Independently check the CURRENT state
|
|
|
10978
11004
|
rec.status = "done";
|
|
10979
11005
|
rec.result = res.text;
|
|
10980
11006
|
const incomplete = res.finishReason !== "stop" && res.finishReason !== "empty";
|
|
10981
|
-
|
|
11007
|
+
log23.verbose(`task ${id} done (${res.steps} steps${incomplete ? `, INCOMPLETE: ${res.finishReason}` : ""})`);
|
|
10982
11008
|
this.notify("task_done", `task ${id} (${rec.label}) completed`, {
|
|
10983
11009
|
id,
|
|
10984
11010
|
text: res.text,
|
|
@@ -11012,14 +11038,14 @@ Another agent just implemented the above. Independently check the CURRENT state
|
|
|
11012
11038
|
foldIfSuperseded(rec, note) {
|
|
11013
11039
|
if (rec.deliveryParked !== "moved_on") return false;
|
|
11014
11040
|
if (note.trim()) this.voice.transcript.push({ role: "assistant", content: note });
|
|
11015
|
-
|
|
11041
|
+
log23.verbose(`task ${rec.id} settled but superseded (${rec.status}) \u2014 folded into transcript, not voiced`);
|
|
11016
11042
|
return true;
|
|
11017
11043
|
}
|
|
11018
11044
|
failTask(rec, msg) {
|
|
11019
11045
|
this.dropAsk(rec.id);
|
|
11020
11046
|
rec.status = "error";
|
|
11021
11047
|
rec.result = msg;
|
|
11022
|
-
|
|
11048
|
+
log23.warn(`task ${rec.id} failed: ${msg}`);
|
|
11023
11049
|
this.notify("task_error", `task ${rec.id} (${rec.label}) failed: ${msg}`, { id: rec.id });
|
|
11024
11050
|
if (this.foldIfSuperseded(rec, `The "${rec.label}" task failed: ${msg}`)) return;
|
|
11025
11051
|
this.dropParked(rec);
|
|
@@ -11317,7 +11343,7 @@ Another agent just implemented the above. Independently check the CURRENT state
|
|
|
11317
11343
|
init_llm();
|
|
11318
11344
|
init_logging();
|
|
11319
11345
|
init_models();
|
|
11320
|
-
var
|
|
11346
|
+
var log24 = forComponent("ModelRouter");
|
|
11321
11347
|
var ModelRouterOptions = class {
|
|
11322
11348
|
classifier = jevClassifier();
|
|
11323
11349
|
/** Capability tiers → model id (any provider). basic/standard run the main session; premium is only ever an offload. */
|
|
@@ -11401,10 +11427,10 @@ var ModelRouter = class {
|
|
|
11401
11427
|
if (tier === "premium") d.offload = this.state.offload && (c.sameThread ?? 0) >= o.sameThreadAt ? "continue" : "fresh";
|
|
11402
11428
|
} catch (e) {
|
|
11403
11429
|
const msg = e instanceof Error ? e.message : String(e);
|
|
11404
|
-
|
|
11430
|
+
log24.warn(`classifier failed, falling back to standard: ${msg}`);
|
|
11405
11431
|
d = { tier: "standard", model: o.models.standard, probs: {}, fallback: msg, classifyMs: Math.round(performance.now() - t0) };
|
|
11406
11432
|
}
|
|
11407
|
-
|
|
11433
|
+
log24.info(`route ${d.tier}${d.offload ? `(${d.offload})` : ""} \xB7 argmax=${d.choice ?? "-"} \xB7 p=${JSON.stringify(d.probs)}${d.sameThread != null ? ` \xB7 same=${d.sameThread.toFixed(2)}` : ""} \xB7 ${d.classifyMs}ms`);
|
|
11408
11434
|
return d;
|
|
11409
11435
|
}
|
|
11410
11436
|
/** Run one user turn on `agent` with routing. Returns a RunResult carrying `costUsd` + `routing`. */
|
|
@@ -11456,7 +11482,7 @@ ${text}`;
|
|
|
11456
11482
|
const answer = res.text || `(premium sub-agent finished with no reply; finishReason=${res.finishReason})`;
|
|
11457
11483
|
agent.transcript.push({ role: "user", content }, { role: "assistant", content: answer });
|
|
11458
11484
|
this.state.offload = { messages: child.transcript, mainLen: agent.transcript.length, question: text };
|
|
11459
|
-
|
|
11485
|
+
log24.info(`offload ${cont ? "continue" : "fresh"} done \xB7 ${res.finishReason} \xB7 ${res.steps} steps`);
|
|
11460
11486
|
const costUsd = o.costOf(main, briefUsage) + o.costOf(o.models.premium, res.usage);
|
|
11461
11487
|
return { ...res, text: answer, messages: agent.transcript, usage: addUsage(briefUsage, res.usage), costUsd, routing: d };
|
|
11462
11488
|
}
|
|
@@ -11472,7 +11498,7 @@ The request:
|
|
|
11472
11498
|
${text}`);
|
|
11473
11499
|
return { text: r.text.trim() || text, usage: r.usage };
|
|
11474
11500
|
} catch (e) {
|
|
11475
|
-
|
|
11501
|
+
log24.warn(`brief failed, sending the raw request: ${e instanceof Error ? e.message : String(e)}`);
|
|
11476
11502
|
return { text };
|
|
11477
11503
|
} finally {
|
|
11478
11504
|
agent.transcript = snap;
|
|
@@ -11517,7 +11543,7 @@ async function jevAvailable(opts = {}) {
|
|
|
11517
11543
|
const { env } = await loadJev(opts);
|
|
11518
11544
|
return JEV_KEYS.some((k) => env[k]?.trim());
|
|
11519
11545
|
} catch (e) {
|
|
11520
|
-
|
|
11546
|
+
log24.debug(`jev unavailable: ${e instanceof Error ? e.message : String(e)}`);
|
|
11521
11547
|
return false;
|
|
11522
11548
|
}
|
|
11523
11549
|
}
|