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 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
- err('\r\x1b[0J' + yellow(` ? worker asks to run ${call.name}`) + dim(` ${hint} say yes/no (or it auto-denies)\n`));
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' + dim(` ${allow ? '✓ allowed' : '⊘ denied'} ${call.name} (${a.trim() || 'no answer'})\n`));
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
- err('\r\x1b[0J' + dim(` · ${e.message}\n`));
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 = 350;
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.overlapLoud = t - this.overlapLastLoudAt <= 60 ? this.overlapLoud + 1 : 1;
4137
- this.overlapLastLoudAt = t;
4138
- if (this.overlapLoud < 3) return;
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 > 300) {
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 >= 4) {
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
- err("\r\x1B[0J" + yellow(` ? worker asks to run ${call.name}`) + dim(` ${hint} \u2014 say yes/no (or it auto-denies)
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" + dim(` ${allow ? "\u2713 allowed" : "\u2298 denied"} ${call.name} (${a.trim() || "no answer"})
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" + dim(` \xB7 ${e.message}
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
  }