@livx.cc/agentx 0.99.98 → 0.99.100
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 +50 -23
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3432,7 +3432,7 @@ function copyTextToClipboard(text, platform2 = process.platform) {
|
|
|
3432
3432
|
|
|
3433
3433
|
// cli/cli.ts
|
|
3434
3434
|
import { join as join16, resolve as resolve5, basename as basename4, extname, dirname as dirname6 } from "path";
|
|
3435
|
-
import { AIClient
|
|
3435
|
+
import { AIClient as AIClient2, listModels, listProviders, getProviderFromModel, getModelInfo, resolveModel, isModelSupported, disposeCursorSessions, disposeClaudeCodeSessions } from "ai.libx.js";
|
|
3436
3436
|
|
|
3437
3437
|
// src/ccBridge.ts
|
|
3438
3438
|
var CC_BRIDGED = /* @__PURE__ */ new Set(["Recall", "Remember", "MemorySearch"]);
|
|
@@ -9251,12 +9251,20 @@ configureLogging((name) => {
|
|
|
9251
9251
|
|
|
9252
9252
|
// src/voice/instinct.ts
|
|
9253
9253
|
init_logging();
|
|
9254
|
+
import { AIClient, DecideClient, JevAdapter, LlmDecideAdapter } from "ai.libx.js";
|
|
9254
9255
|
var log19 = forComponent("Instinct");
|
|
9255
9256
|
function voiceDeciderOptions(env = process.env) {
|
|
9256
9257
|
if (env.TYPESAFE_AI_KEY) return { apiKey: env.TYPESAFE_AI_KEY };
|
|
9257
9258
|
if (env.GROQ_API_KEY) return { provider: "llm", model: "groq/openai/gpt-oss-120b", apiKey: env.GROQ_API_KEY };
|
|
9258
9259
|
return void 0;
|
|
9259
9260
|
}
|
|
9261
|
+
function voiceDecider(env = process.env, timeoutMs = 1500) {
|
|
9262
|
+
const o = voiceDeciderOptions(env);
|
|
9263
|
+
if (!o) return void 0;
|
|
9264
|
+
const ai = new AIClient();
|
|
9265
|
+
const llm = new LlmDecideAdapter({ client: { chat: (c) => ai.chat({ reasoningEffort: "low", ...c }) } });
|
|
9266
|
+
return new DecideClient({ ...o, timeoutMs, adapters: { typesafe: new JevAdapter(), llm } });
|
|
9267
|
+
}
|
|
9260
9268
|
var InstinctOptions = class {
|
|
9261
9269
|
decider;
|
|
9262
9270
|
/** Partial unchanged this long (a micro-pause) → consult. */
|
|
@@ -14065,7 +14073,7 @@ Reply JSON only: {"turn":"...","density":"...","cues":[{"when":["phrase",...],"l
|
|
|
14065
14073
|
var log31 = forComponent("SecondChannel");
|
|
14066
14074
|
var SecondChannelOptions = class {
|
|
14067
14075
|
/** Refiner (Jev/fast LLM): the choice over CHANNEL_ACTIONS, or null on failure. Absent → the rule alone decides.
|
|
14068
|
-
* `state.rule` is the kind the rule proposed (
|
|
14076
|
+
* `state.rule` is the kind the rule proposed (fastChannelDecider answers yes/no on it). `signal` aborts a late call. */
|
|
14069
14077
|
decide;
|
|
14070
14078
|
/** Fast-model line writer (see fastLineGenerator). Absent or late → the canned bank. */
|
|
14071
14079
|
generate;
|
|
@@ -14094,8 +14102,12 @@ var SecondChannelOptions = class {
|
|
|
14094
14102
|
decisionBudgetMs = 300;
|
|
14095
14103
|
generateBudgetMs = 500;
|
|
14096
14104
|
minGapMs = 2500;
|
|
14097
|
-
/** Backchannel after backchannel needs this gap (
|
|
14098
|
-
|
|
14105
|
+
/** Backchannel after backchannel needs this gap (~4-6 cues in a 30s monologue — Para phone call 2026-10-13: 5s left
|
|
14106
|
+
* one cue in a long statement). */
|
|
14107
|
+
backchannelGapMs = 3e3;
|
|
14108
|
+
/** The user resumes within this long of their last words after a line of theirs was dispatched: the same statement goes
|
|
14109
|
+
* on (an STT endpoint mid-monologue) — its words still count toward the backchannel guards, and it is no fresh request. */
|
|
14110
|
+
continueMs = 3e3;
|
|
14099
14111
|
/** A decision whose partial grew by this many words while in flight is stale → dropped. */
|
|
14100
14112
|
staleWords = 3;
|
|
14101
14113
|
microWords = 30;
|
|
@@ -14137,11 +14149,12 @@ var SecondChannelOptions = class {
|
|
|
14137
14149
|
random = Math.random;
|
|
14138
14150
|
};
|
|
14139
14151
|
var words2 = (s) => s.trim() ? s.trim().split(/\s+/) : [];
|
|
14140
|
-
function withBudget(p, ms, what) {
|
|
14152
|
+
function withBudget(p, ms, what, ac) {
|
|
14141
14153
|
let t;
|
|
14142
14154
|
const late = new Promise((r) => {
|
|
14143
14155
|
t = setTimeout(() => {
|
|
14144
14156
|
log31.debug(`${what}: over the ${ms}ms budget \u2014 dropped`);
|
|
14157
|
+
ac?.abort();
|
|
14145
14158
|
r(null);
|
|
14146
14159
|
}, ms);
|
|
14147
14160
|
});
|
|
@@ -14182,6 +14195,11 @@ var SecondChannel = class {
|
|
|
14182
14195
|
inflight = null;
|
|
14183
14196
|
startedAt;
|
|
14184
14197
|
lang = null;
|
|
14198
|
+
carried = 0;
|
|
14199
|
+
// words of this statement already dispatched (a monologue split by STT endpoints)
|
|
14200
|
+
floorWords = 0;
|
|
14201
|
+
// carried + the last dispatched line's words
|
|
14202
|
+
lastHeardAt = -Infinity;
|
|
14185
14203
|
pinned = false;
|
|
14186
14204
|
// anticipate: the plan for the user's current/next turn (turnNo bumps per dispatched turn → a late plan is dropped)
|
|
14187
14205
|
turnNo = 0;
|
|
@@ -14229,6 +14247,8 @@ var SecondChannel = class {
|
|
|
14229
14247
|
onInterim(text) {
|
|
14230
14248
|
const t = text.trim();
|
|
14231
14249
|
if (!t || t === this.heard) return;
|
|
14250
|
+
if (this.main !== "idle" || !this.heard) this.carried = this.o.now() - this.lastHeardAt < this.o.continueMs ? this.floorWords : 0;
|
|
14251
|
+
this.lastHeardAt = this.o.now();
|
|
14232
14252
|
if (this.main !== "idle") this.onAgentState("idle");
|
|
14233
14253
|
this.heard = t;
|
|
14234
14254
|
this.detect(t);
|
|
@@ -14310,6 +14330,7 @@ var SecondChannel = class {
|
|
|
14310
14330
|
/** The user's final line was dispatched to the main agent (its words are the bridge's micro-context). */
|
|
14311
14331
|
onUserTurn(text) {
|
|
14312
14332
|
this.heard = text.trim();
|
|
14333
|
+
this.floorWords = this.carried + words2(this.heard).length;
|
|
14313
14334
|
this.lastUser = this.heard;
|
|
14314
14335
|
this.detect(this.heard);
|
|
14315
14336
|
this.main = "idle";
|
|
@@ -14391,8 +14412,10 @@ var SecondChannel = class {
|
|
|
14391
14412
|
* sentence end only deep into a story — a short request's end is the end of their turn (replay: "mm-hm" right after
|
|
14392
14413
|
* "Check hacker news for recent news.", just before the final). */
|
|
14393
14414
|
backchannelMoment(n, breath) {
|
|
14394
|
-
if (
|
|
14395
|
-
if (
|
|
14415
|
+
if (/\?$/.test(this.heard) || pendingQuestion(this.heard)) return false;
|
|
14416
|
+
if (this.carried) n += this.carried;
|
|
14417
|
+
else if (n < this.o.requestHoldWords && requestOpener(this.heard)) return false;
|
|
14418
|
+
if (n < this.o.backchannelMinWords) return false;
|
|
14396
14419
|
if (/[.!]$/.test(this.heard)) return n >= 2 * this.o.backchannelMinWords;
|
|
14397
14420
|
return breath || /[,;:…]$/.test(this.heard);
|
|
14398
14421
|
}
|
|
@@ -14411,7 +14434,8 @@ var SecondChannel = class {
|
|
|
14411
14434
|
let kind = rule;
|
|
14412
14435
|
let choice;
|
|
14413
14436
|
if (this.o.decide && !cue) {
|
|
14414
|
-
const
|
|
14437
|
+
const ac = new AbortController();
|
|
14438
|
+
const d = await withBudget(this.o.decide(this.tail(), { ...this.state(pauseMs), rule }, ac.signal), this.o.decisionBudgetMs, "channel decision", ac);
|
|
14415
14439
|
choice = d?.choice;
|
|
14416
14440
|
if (choice && choice in CHANNEL_ACTIONS && (choice === "silence" || this.allowed(choice))) kind = choice;
|
|
14417
14441
|
else if (choice && choice in CHANNEL_ACTIONS) log31.debug(`decider chose ${choice} for a ${rule} moment \u2014 the rule stands`);
|
|
@@ -14491,22 +14515,28 @@ var SecondChannel = class {
|
|
|
14491
14515
|
return true;
|
|
14492
14516
|
}
|
|
14493
14517
|
};
|
|
14494
|
-
|
|
14495
|
-
|
|
14496
|
-
|
|
14497
|
-
|
|
14498
|
-
|
|
14518
|
+
var FAST_ASK = {
|
|
14519
|
+
backchannel: 'The user is still talking and just took a breath. Would an attentive human listener say a tiny "mm-hm" now? Yes ONLY if they are telling a story/explaining across several clauses. No if it is a request, command, question, or they are mid-request.',
|
|
14520
|
+
filler: 'The user went quiet mid-turn. Would a human listener make a soft "hmm\u2026" now? Yes ONLY if their sentence is unfinished and they are searching for words. No if they said a complete request, question, answer, thanks or remark (the reply comes next).',
|
|
14521
|
+
bridge: `The user finished and the assistant's answer is taking a moment. Would a human say a short "let me check\u2014" now? Yes if they asked for real work or a substantial/hard question. No for small talk, a greeting, thanks, an ok/yes/agreement.`
|
|
14522
|
+
};
|
|
14523
|
+
function fastChannelDecider(ai, model = "groq/qwen/qwen3.8-27b") {
|
|
14524
|
+
return async (heard, state, signal) => {
|
|
14525
|
+
const kind = state.rule;
|
|
14526
|
+
if (!(kind in FAST_ASK)) return null;
|
|
14527
|
+
const prompt = `Live voice call. ${FAST_ASK[kind]}
|
|
14528
|
+
So far the user said: "${heard}"
|
|
14529
|
+
Answer yes or no.`;
|
|
14499
14530
|
const reasoning = /qwen/.test(model) ? { providerOptions: { reasoning_effort: "none" } } : /gpt-oss/.test(model) ? { providerOptions: { reasoning_effort: "low" } } : {};
|
|
14500
|
-
const r = await ai.chat({ model, messages: [{ role: "user", content: prompt }], stream: false, maxTokens: /gpt-oss/.test(model) ? 200 : 3, ...reasoning });
|
|
14531
|
+
const r = await ai.chat({ model, messages: [{ role: "user", content: prompt }], stream: false, signal, temperature: 0, maxTokens: /gpt-oss/.test(model) ? 200 : 3, ...reasoning });
|
|
14501
14532
|
const a = (r.content ?? "").trim().toLowerCase();
|
|
14502
|
-
return /^yes/.test(a) ? { choice:
|
|
14533
|
+
return /^yes/.test(a) ? { choice: kind } : /^no/.test(a) ? { choice: "silence" } : null;
|
|
14503
14534
|
};
|
|
14504
14535
|
}
|
|
14505
14536
|
function channelDecide(ai, instinct, env = process.env) {
|
|
14506
14537
|
const model = env.AGENTX_CHANNEL_DECIDER_MODEL || void 0;
|
|
14507
|
-
|
|
14508
|
-
|
|
14509
|
-
return async (heard, state) => bc && state.rule === "backchannel" ? bc(heard, state) : instinct ? instinct.channel(heard, state) : null;
|
|
14538
|
+
if (model || env.GROQ_API_KEY) return fastChannelDecider(ai, model);
|
|
14539
|
+
return instinct ? (heard, state) => instinct.channel(heard, state) : void 0;
|
|
14510
14540
|
}
|
|
14511
14541
|
var KIND_HINT2 = {
|
|
14512
14542
|
backchannel: 'a tiny listener cue while they keep talking ("mm-hm", "right", "yeah") \u2014 shows you follow, never takes the turn',
|
|
@@ -21266,10 +21296,7 @@ function duplexError(e, reflexModel) {
|
|
|
21266
21296
|
return code && !msg.includes(String(code)) ? `${msg} (${code})` : msg;
|
|
21267
21297
|
}
|
|
21268
21298
|
var HOLD_RESUME_MS = 4e3;
|
|
21269
|
-
var leakDecider = () =>
|
|
21270
|
-
const o = voiceDeciderOptions();
|
|
21271
|
-
return o ? new DecideClient(o) : void 0;
|
|
21272
|
-
};
|
|
21299
|
+
var leakDecider = () => voiceDecider();
|
|
21273
21300
|
function makeSecondChannel(ai, reflexModel, opts) {
|
|
21274
21301
|
const decider = leakDecider();
|
|
21275
21302
|
const instinct = decider ? new Instinct({ decider }) : void 0;
|
|
@@ -21536,7 +21563,7 @@ async function main() {
|
|
|
21536
21563
|
console.error(red("No provider key found. Set ANTHROPIC_API_KEY (or OPENAI_API_KEY / GOOGLE_API_KEY / GROQ_API_KEY) in ~/.agent/.env, or set BODIFY_API_KEY + BODIFY_APP_ID to pull from Bodify."));
|
|
21537
21564
|
process.exit(1);
|
|
21538
21565
|
}
|
|
21539
|
-
const ai = new
|
|
21566
|
+
const ai = new AIClient2({
|
|
21540
21567
|
apiKeys,
|
|
21541
21568
|
...cfg.baseUrls ? { baseUrls: cfg.baseUrls } : {},
|
|
21542
21569
|
retry: {
|