agent.libx.js 0.92.8 → 0.92.9
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/cli/cli.ts +13 -5
- package/dist/cli.js +25 -13
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +13 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/cli/cli.ts
CHANGED
|
@@ -990,14 +990,21 @@ async function repl(args: Args, ai: ChatLike, cfg: Partial<AgentConfig>, cwd: st
|
|
|
990
990
|
const duplexAsk = async (call: ToolUse): Promise<{ decision: 'allow' | 'deny' }> => {
|
|
991
991
|
if (args.voice && dx) {
|
|
992
992
|
const hint = summarizeCall(call.name, call.args).slice(0, 80);
|
|
993
|
+
// 'menu' mode: approve like a normal session — suspend the editor, pop the picker.
|
|
994
|
+
if ((cfg as any).voiceAskUi === 'menu') {
|
|
995
|
+
editorRef?.suspend();
|
|
996
|
+
const v = await selectMenu(process.stderr, { title: `? background worker asks to run ${call.name} ${hint}`, items: [{ label: 'Allow', value: 'y' }, { label: 'Deny', value: 'n' }], current: 'n' });
|
|
997
|
+
editorRef?.resume();
|
|
998
|
+
editorRef?.redrawNow();
|
|
999
|
+
return { decision: v === 'y' ? 'allow' : 'deny' };
|
|
1000
|
+
}
|
|
993
1001
|
// NB: perm asks are keyed perm-N (PermissionPolicy.ask carries no task identity), so a
|
|
994
1002
|
// cancelled task can't clean its parked perm question — bounded by askTimeoutMs → deny.
|
|
1003
|
+
// (Chrome prints once via the task_ask notify below — no extra line here.)
|
|
995
1004
|
const id = `perm-${++permSeq}`;
|
|
996
|
-
|
|
997
|
-
editorRef?.redrawNow();
|
|
998
|
-
const a = await dx.parkQuestion(id, `Permission: may the background worker run ${call.name}${hint ? ` (${hint})` : ''}? Yes or no.`);
|
|
1005
|
+
const a = await dx.parkQuestion(id, `Permission: may the background worker run ${call.name}${hint ? ` (${hint})` : ''}? Answer yes or no (you can also type it).`);
|
|
999
1006
|
const allow = /^\s*(y(es|ep|eah)?|sure|ok(ay)?|allow|go|approved?|do it)\b/i.test(a);
|
|
1000
|
-
err('\r\x1b[0J' +
|
|
1007
|
+
err('\r\x1b[0J' + (allow ? green(` ✓ allowed ${call.name}`) : yellow(` ⊘ denied ${call.name}`)) + dim(` (${a.trim() || 'no answer'})\n`));
|
|
1001
1008
|
editorRef?.redrawNow();
|
|
1002
1009
|
return { decision: allow ? 'allow' : 'deny' };
|
|
1003
1010
|
}
|
|
@@ -1060,7 +1067,8 @@ async function repl(args: Args, ai: ChatLike, cfg: Partial<AgentConfig>, cwd: st
|
|
|
1060
1067
|
// residue glued to 'agentx › '). task_done returned above; everything else falls through.
|
|
1061
1068
|
if (typeof e.kind === 'string' && e.kind.startsWith('task_')) {
|
|
1062
1069
|
spinner.stop();
|
|
1063
|
-
|
|
1070
|
+
// asks are decisions, not chatter — make them stand out (the voice also speaks them)
|
|
1071
|
+
err('\r\x1b[0J' + (e.kind === 'task_ask' ? yellow(` ? ${e.message} — answer by voice or type yes/no\n`) : dim(` · ${e.message}\n`)));
|
|
1064
1072
|
editorRef?.redrawNow();
|
|
1065
1073
|
return;
|
|
1066
1074
|
}
|
package/dist/cli.js
CHANGED
|
@@ -3589,6 +3589,7 @@ ${recent}` : brief;
|
|
|
3589
3589
|
let steps = 0;
|
|
3590
3590
|
let inflight = null;
|
|
3591
3591
|
const due = () => {
|
|
3592
|
+
if (this.pendingAsks.size) return void 0;
|
|
3592
3593
|
const rec = this.tasks.get(id);
|
|
3593
3594
|
return rec && rec.status === "running" && Date.now() - lastAt >= this.options.progressIntervalMs ? rec : void 0;
|
|
3594
3595
|
};
|
|
@@ -3872,7 +3873,7 @@ var VoiceEngineOptions = class {
|
|
|
3872
3873
|
* vocabulary) resume from the precise sample and are dropped. false disables. */
|
|
3873
3874
|
overlapPause = true;
|
|
3874
3875
|
/** sustained overlap ≥ this → cede the turn */
|
|
3875
|
-
overlapSustainMs =
|
|
3876
|
+
overlapSustainMs = 450;
|
|
3876
3877
|
/** quiet for this long while paused → resume, drop the interjection */
|
|
3877
3878
|
overlapResumeMs = 700;
|
|
3878
3879
|
/** energy floor for "overlap candidate" — must sit ABOVE typical room ambient (~110 rms measured;
|
|
@@ -3914,6 +3915,8 @@ var VoiceEngine = class {
|
|
|
3914
3915
|
// loud chunks since pause (sustain must be real sound, not two clicks)
|
|
3915
3916
|
overlapLastLoudAt = 0;
|
|
3916
3917
|
// continuity guard: a gap re-arms the onset (sparse noise ≠ sustained speech)
|
|
3918
|
+
loudTimes = [];
|
|
3919
|
+
// recent loud-chunk timestamps (sliding onset window)
|
|
3917
3920
|
resumeTimer = null;
|
|
3918
3921
|
constructor(options) {
|
|
3919
3922
|
this.options = { ...new VoiceEngineOptions(), ...options };
|
|
@@ -4133,15 +4136,18 @@ var VoiceEngine = class {
|
|
|
4133
4136
|
if (rms < o.overlapRms) return;
|
|
4134
4137
|
const t = now();
|
|
4135
4138
|
if (!this.pausedAt) {
|
|
4136
|
-
this.
|
|
4137
|
-
this.
|
|
4138
|
-
if (this.
|
|
4139
|
+
this.loudTimes = this.loudTimes.filter((x) => t - x < 400);
|
|
4140
|
+
this.loudTimes.push(t);
|
|
4141
|
+
if (this.loudTimes.length < 2) return;
|
|
4142
|
+
this.loudTimes = [];
|
|
4139
4143
|
this.pausedAt = t;
|
|
4144
|
+
this.overlapLoud = 2;
|
|
4145
|
+
this.overlapLastLoudAt = t;
|
|
4140
4146
|
this.player.pause();
|
|
4141
4147
|
this.armResume();
|
|
4142
4148
|
return;
|
|
4143
4149
|
}
|
|
4144
|
-
if (t - this.overlapLastLoudAt >
|
|
4150
|
+
if (t - this.overlapLastLoudAt > 450) {
|
|
4145
4151
|
this.pausedAt = t;
|
|
4146
4152
|
this.overlapLoud = 1;
|
|
4147
4153
|
this.overlapLastLoudAt = t;
|
|
@@ -4150,7 +4156,7 @@ var VoiceEngine = class {
|
|
|
4150
4156
|
}
|
|
4151
4157
|
this.overlapLastLoudAt = t;
|
|
4152
4158
|
this.overlapLoud++;
|
|
4153
|
-
if (t - this.pausedAt >= o.overlapSustainMs && this.overlapLoud >=
|
|
4159
|
+
if (t - this.pausedAt >= o.overlapSustainMs && this.overlapLoud >= 3) {
|
|
4154
4160
|
const phase = this.ctxOpen ? "speaking" : "drain";
|
|
4155
4161
|
this.interrupt();
|
|
4156
4162
|
this.options.onBargeIn(phase);
|
|
@@ -4174,6 +4180,7 @@ var VoiceEngine = class {
|
|
|
4174
4180
|
if (this.pausedAt && resume) this.player.resume?.();
|
|
4175
4181
|
this.pausedAt = 0;
|
|
4176
4182
|
this.overlapLoud = 0;
|
|
4183
|
+
this.loudTimes = [];
|
|
4177
4184
|
}
|
|
4178
4185
|
/** energy two-stage barge-in (heuristic tier only): spike over echo baseline → pause + confirm via STT */
|
|
4179
4186
|
handleLevel(rms) {
|
|
@@ -7966,13 +7973,17 @@ async function repl(args, ai, cfg, cwd) {
|
|
|
7966
7973
|
const duplexAsk = async (call) => {
|
|
7967
7974
|
if (args.voice && dx) {
|
|
7968
7975
|
const hint = summarizeCall(call.name, call.args).slice(0, 80);
|
|
7976
|
+
if (cfg.voiceAskUi === "menu") {
|
|
7977
|
+
editorRef?.suspend();
|
|
7978
|
+
const v = await selectMenu(process.stderr, { title: `? background worker asks to run ${call.name} ${hint}`, items: [{ label: "Allow", value: "y" }, { label: "Deny", value: "n" }], current: "n" });
|
|
7979
|
+
editorRef?.resume();
|
|
7980
|
+
editorRef?.redrawNow();
|
|
7981
|
+
return { decision: v === "y" ? "allow" : "deny" };
|
|
7982
|
+
}
|
|
7969
7983
|
const id = `perm-${++permSeq}`;
|
|
7970
|
-
|
|
7971
|
-
`));
|
|
7972
|
-
editorRef?.redrawNow();
|
|
7973
|
-
const a = await dx.parkQuestion(id, `Permission: may the background worker run ${call.name}${hint ? ` (${hint})` : ""}? Yes or no.`);
|
|
7984
|
+
const a = await dx.parkQuestion(id, `Permission: may the background worker run ${call.name}${hint ? ` (${hint})` : ""}? Answer yes or no (you can also type it).`);
|
|
7974
7985
|
const allow = /^\s*(y(es|ep|eah)?|sure|ok(ay)?|allow|go|approved?|do it)\b/i.test(a);
|
|
7975
|
-
err("\r\x1B[0J" +
|
|
7986
|
+
err("\r\x1B[0J" + (allow ? green(` \u2713 allowed ${call.name}`) : yellow(` \u2298 denied ${call.name}`)) + dim(` (${a.trim() || "no answer"})
|
|
7976
7987
|
`));
|
|
7977
7988
|
editorRef?.redrawNow();
|
|
7978
7989
|
return { decision: allow ? "allow" : "deny" };
|
|
@@ -8022,8 +8033,9 @@ async function repl(args, ai, cfg, cwd) {
|
|
|
8022
8033
|
}
|
|
8023
8034
|
if (typeof e.kind === "string" && e.kind.startsWith("task_")) {
|
|
8024
8035
|
spinner.stop();
|
|
8025
|
-
err("\r\x1B[0J" +
|
|
8026
|
-
`)
|
|
8036
|
+
err("\r\x1B[0J" + (e.kind === "task_ask" ? yellow(` ? ${e.message} \u2014 answer by voice or type yes/no
|
|
8037
|
+
`) : dim(` \xB7 ${e.message}
|
|
8038
|
+
`)));
|
|
8027
8039
|
editorRef?.redrawNow();
|
|
8028
8040
|
return;
|
|
8029
8041
|
}
|