@livx.cc/agentx 0.99.117 → 0.99.119
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 +1 -1
- package/dist/cli.js +152 -30
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +52 -8
- package/dist/index.js +148 -29
- package/dist/index.js.map +1 -1
- package/dist/{secondChannel-BCEr_QyM.d.ts → secondChannel-DepBV0Zv.d.ts} +6 -0
- package/package.json +1 -1
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-
|
|
2
|
+
import { o as RouterState, H as Hooks, p as RunResult, R as ReasoningEffort, C as ChannelMode, A as Agent } from './secondChannel-DepBV0Zv.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
|
@@ -5300,11 +5300,12 @@ var Agent = class _Agent {
|
|
|
5300
5300
|
let emptyNudged = false;
|
|
5301
5301
|
let closureNudged = false;
|
|
5302
5302
|
let lastProgressAt = start;
|
|
5303
|
+
const turnUser = this.transcript[this.transcript.length - 1];
|
|
5303
5304
|
const kill = (finishReason) => {
|
|
5304
5305
|
log8.warn(`kill-switch: ${finishReason} (steps=${steps}, tokens=${usage.totalTokens}, budgetTokens=${Math.round(spent)}, ms=${Date.now() - start - this.parkedMs}${finishReason === "timeout" ? `, idle=${Date.now() - lastProgressAt - this.parkedMs}ms` : ""}${this.parkedMs ? ` +${this.parkedMs} parked` : ""})`);
|
|
5305
5306
|
this.jobs.killAll(`run ended: ${finishReason}`, true);
|
|
5306
5307
|
const claims = this.survivingClaims();
|
|
5307
|
-
return { text: lastAssistantText(this.transcript), steps, finishReason, messages: this.transcript, usage, usageEstimated, ...finishReason === "budget" ? { budget: { spent: Math.round(spent), limit: o.maxTokens, stepPrompt } } : {}, ...claims.length ? { fabricatedToolClaims: claims } : {} };
|
|
5308
|
+
return { text: lastAssistantText(this.transcript, turnUser), steps, finishReason, messages: this.transcript, usage, usageEstimated, ...finishReason === "budget" ? { budget: { spent: Math.round(spent), limit: o.maxTokens, stepPrompt } } : {}, ...claims.length ? { fabricatedToolClaims: claims } : {} };
|
|
5308
5309
|
};
|
|
5309
5310
|
const overBudget = () => !!o.maxTokens && spent >= o.maxTokens;
|
|
5310
5311
|
const canNudge = () => steps < o.maxSteps && !overBudget();
|
|
@@ -6164,8 +6165,9 @@ function summarize(messages, focus) {
|
|
|
6164
6165
|
}
|
|
6165
6166
|
return lines.join("\n");
|
|
6166
6167
|
}
|
|
6167
|
-
function lastAssistantText(messages) {
|
|
6168
|
+
function lastAssistantText(messages, since) {
|
|
6168
6169
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
6170
|
+
if (since && messages[i] === since) return "";
|
|
6169
6171
|
if (messages[i].role === "assistant") return contentText(messages[i].content);
|
|
6170
6172
|
}
|
|
6171
6173
|
return "";
|
|
@@ -13434,11 +13436,26 @@ var SecondChannel = class {
|
|
|
13434
13436
|
}
|
|
13435
13437
|
/** Hard gates every line passes, whoever proposed it. */
|
|
13436
13438
|
allowed(k) {
|
|
13437
|
-
|
|
13438
|
-
|
|
13439
|
-
|
|
13440
|
-
|
|
13441
|
-
|
|
13439
|
+
return !this.blocked(k);
|
|
13440
|
+
}
|
|
13441
|
+
/** Why a line of kind `k` can't play now ('' = it can) — the call's ledger names a missing bridge's cause. */
|
|
13442
|
+
blocked(k) {
|
|
13443
|
+
if (!this.o.kinds.includes(k)) return "kind off";
|
|
13444
|
+
if (this.audioLocked) return `main audio (${this.main})`;
|
|
13445
|
+
if (this.inflight) return "a line still playing";
|
|
13446
|
+
if (this.o.now() - this.lastAt < this.o.minGapMs) return `${Math.round(this.o.now() - this.lastAt)}ms since the last line`;
|
|
13447
|
+
if (k === this.lastKind && (k !== "backchannel" || this.o.now() - this.lastAt < this.o.backchannelGapMs)) return "same kind as the last line";
|
|
13448
|
+
if (k === "backchannel") return this.o.aec && this.userSpeaking && this.main === "idle" && !this.capped() ? "" : "not a backchannel moment";
|
|
13449
|
+
if (k === "filler") return !this.userSpeaking && this.main === "idle" ? "" : "not a filler moment";
|
|
13450
|
+
return this.bridged ? "bridged already" : this.main === "queued" || this.main === "thinking" ? "" : `main ${this.main}`;
|
|
13451
|
+
}
|
|
13452
|
+
/** Why this turn gets no bridge by rule ('' = it may) — language, a quick/phatic turn, or blocked(). */
|
|
13453
|
+
bridgeBlock() {
|
|
13454
|
+
const w = words3(this.heard);
|
|
13455
|
+
if (!this.lang) return "language unknown";
|
|
13456
|
+
if (w.length <= 3 || w.length <= 6 && isPhatic(this.heard, this.bank)) return w.length <= 3 ? "quick turn" : "phatic";
|
|
13457
|
+
if (w.length < this.o.bridgeMinWords) return `under ${this.o.bridgeMinWords} words`;
|
|
13458
|
+
return this.blocked("bridge");
|
|
13442
13459
|
}
|
|
13443
13460
|
bcTimes = [];
|
|
13444
13461
|
capped() {
|
|
@@ -13485,7 +13502,10 @@ var SecondChannel = class {
|
|
|
13485
13502
|
return breath || /[,;:…]$/.test(this.heard);
|
|
13486
13503
|
}
|
|
13487
13504
|
async consider(trigger, pauseMs, breath = false, cue = null) {
|
|
13488
|
-
if (this.busy)
|
|
13505
|
+
if (this.busy) {
|
|
13506
|
+
if (trigger === "working") this.o.onEvent({ trigger, rule: "silence", ms: 0, acted: "silence", reason: "deciding another line" });
|
|
13507
|
+
return;
|
|
13508
|
+
}
|
|
13489
13509
|
const n0 = words3(this.heard).length;
|
|
13490
13510
|
this.decidedAt = n0;
|
|
13491
13511
|
if (!(this.o.anticipate && this.plan && trigger === "interim")) cue = null;
|
|
@@ -13495,7 +13515,10 @@ var SecondChannel = class {
|
|
|
13495
13515
|
log27.info("chatty mode without echo cancellation \u2014 normal timing (our lines would re-trigger the mic)");
|
|
13496
13516
|
}
|
|
13497
13517
|
const rule = this.rule(trigger, pauseMs, breath, cue);
|
|
13498
|
-
if (rule === "silence")
|
|
13518
|
+
if (rule === "silence") {
|
|
13519
|
+
if (trigger === "working") this.o.onEvent({ trigger, rule, ms: 0, acted: "silence", reason: this.bridgeBlock() || "rule" });
|
|
13520
|
+
return;
|
|
13521
|
+
}
|
|
13499
13522
|
this.busy = true;
|
|
13500
13523
|
const gen = this.gen, t0 = this.o.now();
|
|
13501
13524
|
const ev = (e) => this.o.onEvent({ trigger, rule, ms: Math.round(this.o.now() - t0), ...cue ? { cue: cue.line } : {}, ...e });
|
|
@@ -13689,6 +13712,9 @@ var VoiceCallOptions = class {
|
|
|
13689
13712
|
channel = {};
|
|
13690
13713
|
onMetric = () => {
|
|
13691
13714
|
};
|
|
13715
|
+
/** The host reports when a fill line's audio REALLY starts (heard()). Off: a line handed to `speak` counts as heard —
|
|
13716
|
+
* right only for a host whose speak() plays at once (a relay host would count lines its player later dropped). */
|
|
13717
|
+
hostAudio = false;
|
|
13692
13718
|
now = () => performance.now();
|
|
13693
13719
|
/** An aside (aside()) needs this long since the last one — a cue answered every time sounds scripted. */
|
|
13694
13720
|
asideGapMs = 8e3;
|
|
@@ -13708,11 +13734,17 @@ var VoiceCall = class {
|
|
|
13708
13734
|
} : {},
|
|
13709
13735
|
mode: o.mode,
|
|
13710
13736
|
aec: o.aec,
|
|
13711
|
-
|
|
13737
|
+
// v3 §3: first sound <1.2s p50 from the last word — the endpoint takes ~0.6s and the bridge's TTS ~0.2s, so the bridge
|
|
13738
|
+
// goes 300ms after the turn if MAIN is still silent (it always is that early: warm MAIN's first token is ≥1s), no veto
|
|
13739
|
+
bridgeDelayMs: 300,
|
|
13712
13740
|
instantBridge: true,
|
|
13713
|
-
// v3 §3: first sound <1.2s p50 — a bridge at 700ms if MAIN is still silent, no veto, no wait
|
|
13714
13741
|
...this.lang ? { lang: this.lang } : {},
|
|
13715
13742
|
...o.channel,
|
|
13743
|
+
// the ledger: a turn's bridge moment that stayed silent, and why (its wait then has no first sound until MAIN speaks)
|
|
13744
|
+
onEvent: (e) => {
|
|
13745
|
+
if (e.trigger === "working" && e.acted !== "spoken") o.onMetric({ name: "drop", ...this.cur ? { id: this.cur.id } : {}, kind: `no bridge: ${e.reason ?? e.acted}`, ...e.text ? { text: e.text } : {} });
|
|
13746
|
+
o.channel.onEvent?.(e);
|
|
13747
|
+
},
|
|
13716
13748
|
speak: (text, kind) => this.say(text, kind),
|
|
13717
13749
|
show: (text, kind) => o.show(text, kind),
|
|
13718
13750
|
cancel: () => o.cancel(),
|
|
@@ -13739,6 +13771,10 @@ var VoiceCall = class {
|
|
|
13739
13771
|
lastAudibleAt = -Infinity;
|
|
13740
13772
|
firstFinalAt = null;
|
|
13741
13773
|
// the first final of the turn being formed (hold/merge)
|
|
13774
|
+
lastWordAt = null;
|
|
13775
|
+
// the user's last word before the turn being formed (its latest final's)
|
|
13776
|
+
earlyHeard = null;
|
|
13777
|
+
// a fill line heard while that turn was still held
|
|
13742
13778
|
progressTimer = null;
|
|
13743
13779
|
ended = false;
|
|
13744
13780
|
get o() {
|
|
@@ -13767,12 +13803,37 @@ var VoiceCall = class {
|
|
|
13767
13803
|
this.follow(text);
|
|
13768
13804
|
this.fill.onInterim(text);
|
|
13769
13805
|
}
|
|
13770
|
-
/** STT final → TurnGate.
|
|
13771
|
-
|
|
13806
|
+
/** STT final → TurnGate. `lastWordAgoMs`: how long ago the user's last word of it ended (the STT's word clock,
|
|
13807
|
+
* bod-voice UtteranceMeta.lastWordAgoMs) — the turn's latency anchor; absent = the final's arrival. `lang`: the
|
|
13808
|
+
* language the STT identified for it (UtteranceMeta.language) — what the turn's words follow when they alone can't tell
|
|
13809
|
+
* (a short English line after a Hebrew one left the fill channel on Hebrew: its bridge was refused, the turn silent). */
|
|
13810
|
+
final(text, sttId, o = {}) {
|
|
13772
13811
|
if (this.ended) return;
|
|
13773
|
-
|
|
13812
|
+
const now4 = this.o.now(), { lastWordAgoMs } = o;
|
|
13813
|
+
this.firstFinalAt ??= now4;
|
|
13814
|
+
this.sttLang = o.lang ? primaryLang(o.lang) : null;
|
|
13815
|
+
this.lastWordAt = typeof lastWordAgoMs === "number" && lastWordAgoMs >= 0 ? now4 - lastWordAgoMs : null;
|
|
13816
|
+
this.finalAt = now4;
|
|
13774
13817
|
this.gate.final(text, sttId);
|
|
13775
13818
|
}
|
|
13819
|
+
finalAt = 0;
|
|
13820
|
+
sttLang = null;
|
|
13821
|
+
// the STT's language for the final being followed
|
|
13822
|
+
/** The host's player started a line's audio now (or in `inMs`): `fill`/`progress` = the fill channel's, `main` = MAIN's
|
|
13823
|
+
* reply (as main({type:'audio'})). With hostAudio this is what first_sound counts. */
|
|
13824
|
+
heard(by, inMs = 0) {
|
|
13825
|
+
if (this.ended) return;
|
|
13826
|
+
if (by === "main") return this.main({ type: "audio", ...this.cur ? { id: this.cur.id } : {} }, inMs);
|
|
13827
|
+
const at = this.o.now() + Math.max(0, inMs);
|
|
13828
|
+
if (this.gate.holding || !this.cur || this.cur.closed) {
|
|
13829
|
+
this.earlyHeard ??= { at, by };
|
|
13830
|
+
return;
|
|
13831
|
+
}
|
|
13832
|
+
this.lastAudibleAt = at;
|
|
13833
|
+
const c = this.cur;
|
|
13834
|
+
c.marks.fillAudio ??= at;
|
|
13835
|
+
this.sounded(c, by, at);
|
|
13836
|
+
}
|
|
13776
13837
|
/** A v2 host that decides turns itself: the user's line already went to MAIN (no TurnGate, no sendTurn) — the fill
|
|
13777
13838
|
* channel follows its language and covers the wait. */
|
|
13778
13839
|
hostTurn(text) {
|
|
@@ -13783,8 +13844,8 @@ var VoiceCall = class {
|
|
|
13783
13844
|
pause(ms) {
|
|
13784
13845
|
if (!this.ended) this.fill.onPause(ms);
|
|
13785
13846
|
}
|
|
13786
|
-
/** MAIN's stream. */
|
|
13787
|
-
main(ev) {
|
|
13847
|
+
/** MAIN's stream. (`inMs`: an audio event's start is that far ahead — the host player's queue.) */
|
|
13848
|
+
main(ev, inMs = 0) {
|
|
13788
13849
|
const c = this.cur;
|
|
13789
13850
|
if (!c || ev.id && ev.id !== c.id) return;
|
|
13790
13851
|
switch (ev.type) {
|
|
@@ -13793,6 +13854,8 @@ var VoiceCall = class {
|
|
|
13793
13854
|
return;
|
|
13794
13855
|
case "text":
|
|
13795
13856
|
if (!ev.text) return;
|
|
13857
|
+
c.marks.mainFirst ??= this.o.now();
|
|
13858
|
+
c.marks.mainText ??= this.o.now();
|
|
13796
13859
|
if (!c.streamed) {
|
|
13797
13860
|
c.streamed = true;
|
|
13798
13861
|
this.gate.replyStarted(c.id);
|
|
@@ -13802,25 +13865,32 @@ var VoiceCall = class {
|
|
|
13802
13865
|
this.fill.onMainTokens(ev.text);
|
|
13803
13866
|
return;
|
|
13804
13867
|
case "tool":
|
|
13868
|
+
c.marks.mainFirst ??= this.o.now();
|
|
13805
13869
|
this.gate.replyStarted(c.id);
|
|
13806
13870
|
void this.onTool(c, ev.name ?? "", ev.args);
|
|
13807
13871
|
return;
|
|
13808
13872
|
case "tool_done":
|
|
13809
13873
|
return;
|
|
13810
|
-
case "audio":
|
|
13811
|
-
|
|
13874
|
+
case "audio": {
|
|
13875
|
+
const at = this.o.now() + Math.max(0, inMs);
|
|
13876
|
+
this.lastAudibleAt = at;
|
|
13812
13877
|
this.fill.onAgentState("first_audio");
|
|
13813
|
-
this.sounded(c, "main");
|
|
13878
|
+
this.sounded(c, "main", at);
|
|
13814
13879
|
if (!c.worded) {
|
|
13815
13880
|
c.worded = true;
|
|
13816
|
-
|
|
13881
|
+
c.marks.mainAudio = at;
|
|
13882
|
+
this.o.onMetric({ name: "first_word", id: c.id, ms: Math.round(at - c.at), by: "main" });
|
|
13817
13883
|
}
|
|
13884
|
+
if (c.done) this.close(c, "answered");
|
|
13818
13885
|
return;
|
|
13886
|
+
}
|
|
13819
13887
|
case "done":
|
|
13820
13888
|
case "error":
|
|
13821
13889
|
c.running = false;
|
|
13890
|
+
c.done = true;
|
|
13822
13891
|
this.clearProgress();
|
|
13823
13892
|
this.fill.onAgentState("idle");
|
|
13893
|
+
if (c.worded) this.close(c, "answered");
|
|
13824
13894
|
return;
|
|
13825
13895
|
}
|
|
13826
13896
|
}
|
|
@@ -13837,6 +13907,7 @@ var VoiceCall = class {
|
|
|
13837
13907
|
this.cur.running = false;
|
|
13838
13908
|
this.o.abortTurn(this.cur.id);
|
|
13839
13909
|
}
|
|
13910
|
+
if (this.cur && !this.cur.worded) this.cur.end ??= "cut";
|
|
13840
13911
|
}
|
|
13841
13912
|
/** Soft barge-in: keep (a listener cue — no turn) or cut (→ barge()). */
|
|
13842
13913
|
async overlap(text) {
|
|
@@ -13866,6 +13937,7 @@ var VoiceCall = class {
|
|
|
13866
13937
|
return this.fill.drain();
|
|
13867
13938
|
}
|
|
13868
13939
|
end() {
|
|
13940
|
+
if (this.cur) this.close(this.cur);
|
|
13869
13941
|
this.ended = true;
|
|
13870
13942
|
this.gate.end();
|
|
13871
13943
|
this.clearProgress();
|
|
@@ -13874,24 +13946,39 @@ var VoiceCall = class {
|
|
|
13874
13946
|
/** Follow the user's language from their own words: a confident detection, else the current language if the words
|
|
13875
13947
|
* fit it, else unknown (silent). Pinned into the channel so its own detection never diverges. */
|
|
13876
13948
|
follow(text) {
|
|
13877
|
-
const
|
|
13949
|
+
const fits = (l2) => l2 && fitsLanguage(text, l2) ? l2 : null;
|
|
13950
|
+
const l = detectLanguage(text) ?? fits(this.sttLang) ?? fits(this.lang);
|
|
13878
13951
|
if (l !== this.lang) this.o.onMetric({ name: "lang", lang: l, text });
|
|
13879
13952
|
this.lang = l;
|
|
13880
13953
|
if (l) this.fill.setLanguage(l, true);
|
|
13881
13954
|
}
|
|
13882
13955
|
onTurn(t) {
|
|
13883
|
-
const
|
|
13956
|
+
const now4 = this.o.now(), lastWord = this.lastWordAt !== null;
|
|
13957
|
+
const at = this.lastWordAt ?? this.firstFinalAt ?? now4;
|
|
13958
|
+
const finalAt = this.finalAt || now4;
|
|
13884
13959
|
this.firstFinalAt = null;
|
|
13960
|
+
this.lastWordAt = null;
|
|
13961
|
+
if (this.cur) this.close(this.cur, t.replaces === this.cur.id ? "replaced" : void 0);
|
|
13885
13962
|
this.follow(t.text);
|
|
13886
13963
|
this.clearProgress();
|
|
13887
|
-
this.cur = { id: t.id, text: t.text, at, running: true, streamed: false, sounded: false, worded: false };
|
|
13964
|
+
this.cur = { id: t.id, text: t.text, at, finalAt, lastWord, running: true, streamed: false, sounded: false, worded: false, done: false, marks: { sent: now4 }, closed: false };
|
|
13965
|
+
const early = this.earlyHeard;
|
|
13966
|
+
this.earlyHeard = null;
|
|
13967
|
+
if (early && early.at >= at) {
|
|
13968
|
+
this.cur.marks.fillAudio = early.at;
|
|
13969
|
+
this.sounded(this.cur, early.by, early.at);
|
|
13970
|
+
}
|
|
13888
13971
|
this.progress.turn();
|
|
13889
13972
|
this.o.onMetric({ name: "turn", id: t.id, kind: t.kind, text: t.text });
|
|
13890
13973
|
this.o.sendTurn(t.text, { id: t.id, ...t.replaces ? { replaces: t.replaces } : {} });
|
|
13891
13974
|
if (t.kind === "new") this.fill.onUserTurn(t.text);
|
|
13892
13975
|
}
|
|
13893
13976
|
onDrop(text, why) {
|
|
13894
|
-
if (!this.gate.holding)
|
|
13977
|
+
if (!this.gate.holding) {
|
|
13978
|
+
this.firstFinalAt = null;
|
|
13979
|
+
this.lastWordAt = null;
|
|
13980
|
+
this.earlyHeard = null;
|
|
13981
|
+
}
|
|
13895
13982
|
this.o.onMetric({ name: "drop", kind: why, text });
|
|
13896
13983
|
}
|
|
13897
13984
|
/** Every fill/progress line passes here: only in the turn's known language, never after the call ended. */
|
|
@@ -13905,16 +13992,48 @@ var VoiceCall = class {
|
|
|
13905
13992
|
return false;
|
|
13906
13993
|
}
|
|
13907
13994
|
if (r === false) return false;
|
|
13908
|
-
this.lastAudibleAt = this.o.now();
|
|
13909
13995
|
this.o.onMetric({ name: "fill", kind, text, lang: this.lang });
|
|
13910
|
-
|
|
13911
|
-
|
|
13996
|
+
const c = this.cur && !this.cur.closed ? this.cur : void 0;
|
|
13997
|
+
const forTurn = !!c && (kind === "bridge" || kind === "progress" || kind === "filler" && c.running && !c.sounded);
|
|
13998
|
+
if (c && forTurn && c.marks.fill === void 0) {
|
|
13999
|
+
c.marks.fill = this.o.now();
|
|
14000
|
+
c.fillKind = kind;
|
|
14001
|
+
}
|
|
14002
|
+
if (this.o.hostAudio) return r;
|
|
14003
|
+
this.lastAudibleAt = this.o.now();
|
|
14004
|
+
if (c && forTurn) {
|
|
14005
|
+
c.marks.fillAudio ??= this.o.now();
|
|
14006
|
+
this.sounded(c, kind === "progress" ? "progress" : "fill");
|
|
14007
|
+
}
|
|
13912
14008
|
return r;
|
|
13913
14009
|
}
|
|
13914
|
-
sounded(c, by) {
|
|
14010
|
+
sounded(c, by, at = this.o.now()) {
|
|
13915
14011
|
if (c.sounded) return;
|
|
13916
14012
|
c.sounded = true;
|
|
13917
|
-
this.o.onMetric({ name: "first_sound", id: c.id, ms: Math.round(
|
|
14013
|
+
this.o.onMetric({ name: "first_sound", id: c.id, ms: Math.round(at - c.at), by });
|
|
14014
|
+
}
|
|
14015
|
+
/** The turn is over (answered + audible, replaced, the next turn, the call's end): its timeline, once. A turn whose MAIN
|
|
14016
|
+
* never became audible still reports first_word — with no ms and why (cut / replaced / silent) — so every turn has one. */
|
|
14017
|
+
close(c, end) {
|
|
14018
|
+
if (c.closed) return;
|
|
14019
|
+
c.closed = true;
|
|
14020
|
+
c.end = c.worded ? "answered" : end ?? c.end ?? "silent";
|
|
14021
|
+
if (!c.worded) this.o.onMetric({ name: "first_word", id: c.id, by: "main", kind: c.end });
|
|
14022
|
+
const rel = (t) => t === void 0 ? void 0 : Math.round(t - c.at);
|
|
14023
|
+
const m = c.marks, tl = {
|
|
14024
|
+
final: rel(c.finalAt),
|
|
14025
|
+
sent: rel(m.sent),
|
|
14026
|
+
fill: rel(m.fill),
|
|
14027
|
+
...c.fillKind ? { fillKind: c.fillKind } : {},
|
|
14028
|
+
fillAudio: rel(m.fillAudio),
|
|
14029
|
+
mainFirst: rel(m.mainFirst),
|
|
14030
|
+
mainText: rel(m.mainText),
|
|
14031
|
+
mainAudio: rel(m.mainAudio),
|
|
14032
|
+
lastWord: c.lastWord,
|
|
14033
|
+
end: c.end
|
|
14034
|
+
};
|
|
14035
|
+
for (const k of Object.keys(tl)) if (tl[k] === void 0) delete tl[k];
|
|
14036
|
+
this.o.onMetric({ name: "timeline", id: c.id, text: c.text, timeline: tl });
|
|
13918
14037
|
}
|
|
13919
14038
|
async onTool(c, name, args) {
|
|
13920
14039
|
const lang = this.lang;
|
|
@@ -22705,6 +22824,8 @@ async function channelServe(args, ai, cfg) {
|
|
|
22705
22824
|
channel: channelOptions(ai, process.env.AGENTX_CHANNEL_MODEL || model, args.voiceGender),
|
|
22706
22825
|
speak: () => true,
|
|
22707
22826
|
// the host plays {type:'channel'} lines (shown via show)
|
|
22827
|
+
hostAudio: true,
|
|
22828
|
+
// …and says when one really plays ({type:'heard'}) — a line its player drops never counts as sound
|
|
22708
22829
|
show: (text, kind) => out({ type: "channel", kind, text }),
|
|
22709
22830
|
sendTurn: (text, o) => out({ type: "turn", id: o.id, text, ...o.replaces ? { replaces: o.replaces } : {} }),
|
|
22710
22831
|
abortTurn: (id) => out({ type: "abort", id }),
|
|
@@ -22729,7 +22850,8 @@ async function channelServe(args, ai, cfg) {
|
|
|
22729
22850
|
if (m.type === "interim") {
|
|
22730
22851
|
if (typeof m.lang === "string") call.setLanguage(m.lang);
|
|
22731
22852
|
if (typeof m.text === "string") call.interim(m.text, Date.now(), typeof m.aec === "boolean" ? m.aec : void 0);
|
|
22732
|
-
} else if (m.type === "final" && typeof m.text === "string") call.final(m.text, m.sttId === void 0 ? void 0 : String(m.sttId));
|
|
22853
|
+
} else if (m.type === "final" && typeof m.text === "string") call.final(m.text, m.sttId === void 0 ? void 0 : String(m.sttId), { ...typeof m.lastWordAgoMs === "number" ? { lastWordAgoMs: m.lastWordAgoMs } : {}, ...typeof m.lang === "string" ? { lang: m.lang } : {} });
|
|
22854
|
+
else if (m.type === "heard" && ["fill", "progress", "main"].includes(m.by)) call.heard(m.by, typeof m.inMs === "number" ? m.inMs : 0);
|
|
22733
22855
|
else if (m.type === "main" && m.ev && typeof m.ev.type === "string") call.main(m.ev);
|
|
22734
22856
|
else if (m.type === "barge") call.barge();
|
|
22735
22857
|
else if (channelInput(call.fill, m)) return;
|