omnius 1.0.425 → 1.0.427

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/index.js CHANGED
@@ -29383,9 +29383,19 @@ function _extractSponsorFromMeshMessage(msg) {
29383
29383
  if (payload.type === 'sponsor.announce') return payload;
29384
29384
  return null;
29385
29385
  }
29386
+ var _profilePublishInFlight = false;
29387
+ var _profilePublishLastAt = 0;
29386
29388
  async function _publishAgentProfileSnapshot() {
29387
29389
  try {
29388
29390
  if (!connected || !nexus.network || !nexus.network.dht || !nexus.network.dht.registry) return;
29391
+ // DHT publishes on congested networks time out (observed as repeated
29392
+ // [dht:registry] TimeoutError storms). Promise.race below does NOT cancel
29393
+ // the underlying query, so overlapping calls stack concurrent DHT walks
29394
+ // and burn CPU. Single-flight + a 60s floor keeps one publish at a time.
29395
+ var _ppNow = Date.now();
29396
+ if (_profilePublishInFlight || _ppNow - _profilePublishLastAt < 60000) return;
29397
+ _profilePublishInFlight = true;
29398
+ _profilePublishLastAt = _ppNow;
29389
29399
  var node = nexus.network.node;
29390
29400
  var caps = typeof nexus.getRegisteredCapabilities === 'function' ? nexus.getRegisteredCapabilities() : [];
29391
29401
  var profile = {
@@ -29407,6 +29417,8 @@ async function _publishAgentProfileSnapshot() {
29407
29417
  ]);
29408
29418
  } catch (err) {
29409
29419
  dlog('profile publish failed: ' + (err.message || err));
29420
+ } finally {
29421
+ _profilePublishInFlight = false;
29410
29422
  }
29411
29423
  }
29412
29424
  async function _publishCapabilityRecord(name, details) {
@@ -31286,9 +31298,11 @@ async function handleCmd(cmd) {
31286
31298
 
31287
31299
  // Wait for input data to arrive (invoke protocol sends data after accept)
31288
31300
  var waitMs = 0;
31301
+ // 50ms poll (was a 10ms spin) — bounded to 5s; input latency is
31302
+ // network-dominated so the coarser tick is invisible.
31289
31303
  while (!inputDone && dataChunks.length === 0 && waitMs < 5000) {
31290
- await new Promise(function(r) { setTimeout(r, 10); });
31291
- waitMs += 10;
31304
+ await new Promise(function(r) { setTimeout(r, 50); });
31305
+ waitMs += 50;
31292
31306
  }
31293
31307
  prompt = dataChunks.join('');
31294
31308
  dlog('expose: received ' + dataChunks.length + ' chunks, prompt_len=' + prompt.length + ' inputDone=' + inputDone);
@@ -32228,8 +32242,11 @@ async function handleCmd(cmd) {
32228
32242
  }
32229
32243
  }
32230
32244
 
32231
- // Command polling loop check for cmd.json every 50ms (fast IPC)
32232
- // fs.existsSync + readFileSync costs ~0.01ms per call, negligible CPU at 50ms interval.
32245
+ // Command intake: fs.watch on the nexus dir delivers cmd.json changes
32246
+ // instantly (registered below); this interval is only a fallback for
32247
+ // platforms/filesystems where fs.watch drops events. 50ms polling here was
32248
+ // measured pinning CPU on deployed boxes — 1s fallback costs nothing since
32249
+ // the watcher provides the low-latency path.
32233
32250
  let lastCmdId = '';
32234
32251
  function checkCmd() {
32235
32252
  try {
@@ -32243,7 +32260,7 @@ function checkCmd() {
32243
32260
  });
32244
32261
  } catch {}
32245
32262
  }
32246
- setInterval(checkCmd, 50);
32263
+ setInterval(checkCmd, 1000);
32247
32264
  // Also watch for cmd.json changes for instant notification (best-effort)
32248
32265
  try {
32249
32266
  fsWatch(nexusDir, { persistent: false }, function(evType, filename) {
@@ -32629,18 +32646,35 @@ process.on('unhandledRejection', (reason) => {
32629
32646
  } catch {}
32630
32647
  try {
32631
32648
  if (nexus.nats && typeof nexus.nats.subscribe === 'function') {
32649
+ var _peersFileLastWrite = 0;
32632
32650
  nexus.nats.subscribe(function(announcement) {
32633
32651
  if (!announcement || !announcement.peerId) return;
32634
32652
  if (announcement.peerId === nexus.peerId) return; // skip self
32635
- discoveredPeers[announcement.peerId] = {
32653
+ // Peers re-announce every 10-30s. Persisting the full JSON map and
32654
+ // logging on EVERY announcement turned steady-state discovery into
32655
+ // constant file I/O + log churn. Only react to NEW peers or
32656
+ // material changes; lastSeen bumps persist on a 30s throttle.
32657
+ var prev = discoveredPeers[announcement.peerId];
32658
+ var next = {
32636
32659
  peerId: announcement.peerId,
32637
32660
  agentName: announcement.agentName || '',
32638
32661
  capabilities: announcement.capabilities || [],
32639
32662
  multiaddrs: announcement.multiaddrs || [],
32640
32663
  lastSeen: Date.now(),
32641
32664
  };
32642
- try { writeFileSync(discoveredPeersFile, JSON.stringify(discoveredPeers, null, 2)); } catch {}
32643
- dlog('NATS peer discovered: ' + String(announcement.peerId).slice(0, 20) + ' caps=' + (announcement.capabilities || []).length);
32665
+ var changed = !prev
32666
+ || prev.agentName !== next.agentName
32667
+ || JSON.stringify(prev.capabilities) !== JSON.stringify(next.capabilities)
32668
+ || JSON.stringify(prev.multiaddrs) !== JSON.stringify(next.multiaddrs);
32669
+ discoveredPeers[announcement.peerId] = next;
32670
+ var nowMs = Date.now();
32671
+ if (changed || nowMs - _peersFileLastWrite >= 30000) {
32672
+ _peersFileLastWrite = nowMs;
32673
+ try { writeFileSync(discoveredPeersFile, JSON.stringify(discoveredPeers, null, 2)); } catch {}
32674
+ }
32675
+ if (changed) {
32676
+ dlog('NATS peer discovered: ' + String(announcement.peerId).slice(0, 20) + ' caps=' + (announcement.capabilities || []).length);
32677
+ }
32644
32678
  });
32645
32679
  dlog('NATS peer discovery subscription active');
32646
32680
  }
@@ -603585,7 +603619,9 @@ var init_listen = __esm({
603585
603619
  case "transcript":
603586
603620
  this.emit("transcript", {
603587
603621
  text: evt.text,
603588
- isFinal: evt.isFinal ?? false
603622
+ isFinal: evt.isFinal ?? false,
603623
+ consensus: typeof evt.consensus === "boolean" ? evt.consensus : void 0,
603624
+ doa: typeof evt.doa === "number" ? evt.doa : void 0
603589
603625
  });
603590
603626
  break;
603591
603627
  case "consensus_rejected":
@@ -603982,7 +604018,7 @@ var init_listen = __esm({
603982
604018
  this.lastTranscriptTime = Date.now();
603983
604019
  this.pendingText = evt.text.trim();
603984
604020
  updateListenLiveState({ lastTranscript: this.pendingText, lastTranscriptAt: this.lastTranscriptTime });
603985
- this.emit("transcript", this.pendingText, evt.isFinal);
604021
+ this.emit("transcript", this.pendingText, evt.isFinal, { consensus: evt.consensus, doa: evt.doa });
603986
604022
  if (this.config.mode === "auto") this.resetSilenceTimer();
603987
604023
  });
603988
604024
  fallback.on("error", (err) => {
@@ -617195,6 +617231,25 @@ function rms(arr) {
617195
617231
  return Math.sqrt(sum / arr.length);
617196
617232
  }
617197
617233
 
617234
+ // Linear resampler: browsers frequently IGNORE the requested AudioContext
617235
+ // sample rate and run at the hardware rate (typically 48kHz). Sending those
617236
+ // frames as-if-16kHz made the audio unpack 3x slow/pitch-dropped server-side,
617237
+ // wrecking ASR. Always resample the actual context rate down to 16kHz.
617238
+ function resampleTo16k(input, srcRate) {
617239
+ if (srcRate === 16000) return input;
617240
+ const ratio = srcRate / 16000;
617241
+ const outLen = Math.floor(input.length / ratio);
617242
+ const out = new Float32Array(outLen);
617243
+ for (let i = 0; i < outLen; i++) {
617244
+ const pos = i * ratio;
617245
+ const i0 = Math.floor(pos);
617246
+ const i1 = Math.min(input.length - 1, i0 + 1);
617247
+ const frac = pos - i0;
617248
+ out[i] = input[i0] * (1 - frac) + input[i1] * frac;
617249
+ }
617250
+ return out;
617251
+ }
617252
+
617198
617253
  async function toggleMic() {
617199
617254
  if (micActive) { stopMic(); return; }
617200
617255
  try {
@@ -617203,17 +617258,19 @@ async function toggleMic() {
617203
617258
  audio: { sampleRate: 16000, channelCount: 1, echoCancellation: true, noiseSuppression: true }
617204
617259
  });
617205
617260
  const source = micCtx.createMediaStreamSource(micStream);
617261
+ const actualRate = micCtx.sampleRate; // may be 48000 regardless of the request
617206
617262
  scriptProcessor = micCtx.createScriptProcessor(4096, 1, 1);
617207
617263
  scriptProcessor.onaudioprocess = (e) => {
617208
617264
  if (!micActive || !ws || ws.readyState !== 1) return;
617209
- const input = e.inputBuffer.getChannelData(0);
617265
+ const raw = e.inputBuffer.getChannelData(0);
617266
+ const input = resampleTo16k(raw, actualRate);
617210
617267
  const int16 = new Int16Array(input.length);
617211
617268
  for (let i = 0; i < input.length; i++) {
617212
617269
  const s = Math.max(-1, Math.min(1, input[i]));
617213
617270
  int16[i] = s < 0 ? s * 0x8000 : s * 0x7FFF;
617214
617271
  }
617215
617272
  ws.send(int16.buffer);
617216
- micLevel = Math.min(1, rms(input) * 5);
617273
+ micLevel = Math.min(1, rms(raw) * 5);
617217
617274
  };
617218
617275
  source.connect(scriptProcessor);
617219
617276
  scriptProcessor.connect(micCtx.destination);
@@ -617222,7 +617279,8 @@ async function toggleMic() {
617222
617279
  micBtn.textContent = 'stop';
617223
617280
  micBtn.classList.add('active');
617224
617281
  hintEl.textContent = 'listening...';
617225
- ws.send(JSON.stringify({ type: 'mic_start', username: 'web-user' }));
617282
+ // Declare the post-resample rate (and the raw context rate for debugging)
617283
+ ws.send(JSON.stringify({ type: 'mic_start', username: 'web-user', sampleRate: 16000, contextRate: actualRate }));
617226
617284
  } catch (err) {
617227
617285
  hintEl.textContent = 'mic error: ' + err.message;
617228
617286
  }
@@ -617595,6 +617653,23 @@ connectPP();
617595
617653
  </body>
617596
617654
  </html>`;
617597
617655
  }
617656
+ function resamplePcm16Mono(pcm, srcRate, dstRate) {
617657
+ if (srcRate === dstRate || srcRate <= 0 || dstRate <= 0) return pcm;
617658
+ const srcSamples = Math.floor(pcm.length / 2);
617659
+ if (srcSamples === 0) return pcm;
617660
+ const ratio = srcRate / dstRate;
617661
+ const outSamples = Math.max(1, Math.floor(srcSamples / ratio));
617662
+ const out = Buffer.allocUnsafe(outSamples * 2);
617663
+ for (let i2 = 0; i2 < outSamples; i2++) {
617664
+ const pos = i2 * ratio;
617665
+ const i0 = Math.min(srcSamples - 1, Math.floor(pos));
617666
+ const i1 = Math.min(srcSamples - 1, i0 + 1);
617667
+ const frac = pos - i0;
617668
+ const sample = pcm.readInt16LE(i0 * 2) * (1 - frac) + pcm.readInt16LE(i1 * 2) * frac;
617669
+ out.writeInt16LE(Math.max(-32768, Math.min(32767, Math.round(sample))), i2 * 2);
617670
+ }
617671
+ return out;
617672
+ }
617598
617673
  function renderVoiceSessionStart(tunnelUrl) {
617599
617674
  renderBoxedBlock({
617600
617675
  title: "☁ Live Voice Session",
@@ -617902,17 +617977,26 @@ var init_voice_session = __esm({
617902
617977
  clearInterval(keepaliveInterval);
617903
617978
  }
617904
617979
  }, 1e4);
617980
+ let clientPcmRate = 16e3;
617905
617981
  ws.on("message", (data, isBinary) => {
617906
617982
  if (isBinary) {
617907
617983
  if (!this.ttsSpeaking) {
617908
- this.onUserAudio?.(Buffer.isBuffer(data) ? data : Buffer.from(data), clientId);
617984
+ let pcm = Buffer.isBuffer(data) ? data : Buffer.from(data);
617985
+ if (clientPcmRate !== 16e3 && clientPcmRate > 0) {
617986
+ pcm = resamplePcm16Mono(pcm, clientPcmRate, 16e3);
617987
+ }
617988
+ this.onUserAudio?.(pcm, clientId);
617909
617989
  }
617910
617990
  } else {
617911
617991
  try {
617912
617992
  const msg = JSON.parse(data.toString());
617913
- if (msg.type === "mic_start" && msg.username) {
617914
- const entry = this.state.connectedUsers.get(clientId);
617915
- if (entry) entry.username = msg.username;
617993
+ if (msg.type === "mic_start") {
617994
+ if (msg.username) {
617995
+ const entry = this.state.connectedUsers.get(clientId);
617996
+ if (entry) entry.username = msg.username;
617997
+ }
617998
+ const declared = Number(msg.sampleRate);
617999
+ clientPcmRate = Number.isFinite(declared) && declared >= 8e3 && declared <= 192e3 ? declared : 16e3;
617916
618000
  }
617917
618001
  } catch {
617918
618002
  }
@@ -697480,6 +697564,9 @@ Rules:
697480
697564
  // VAD segment capture
697481
697565
  captureBuffer = "";
697482
697566
  captureStartTime = 0;
697567
+ consensusMetadataSeen = false;
697568
+ consensusText = null;
697569
+ consensusSignalScore = null;
697483
697570
  silenceTimer = null;
697484
697571
  maxSegmentTimer = null;
697485
697572
  lastSignalScore = null;
@@ -697570,18 +697657,25 @@ Rules:
697570
697657
  let isFinal;
697571
697658
  let snr;
697572
697659
  let confidence2;
697660
+ let consensus;
697573
697661
  if (typeof args[0] === "object" && args[0] !== null) {
697574
697662
  const evt = args[0];
697575
697663
  text2 = evt.text ?? "";
697576
697664
  isFinal = evt.isFinal ?? false;
697577
697665
  snr = evt.snr;
697578
697666
  confidence2 = evt.confidence;
697667
+ consensus = typeof evt.consensus === "boolean" ? evt.consensus : void 0;
697579
697668
  } else {
697580
697669
  text2 = String(args[0] ?? "");
697581
697670
  isFinal = Boolean(args[1]);
697671
+ const meta = args[2];
697672
+ if (meta && typeof meta === "object") {
697673
+ const m2 = meta;
697674
+ if (typeof m2.consensus === "boolean") consensus = m2.consensus;
697675
+ }
697582
697676
  }
697583
697677
  if (!text2.trim()) return;
697584
- this.handleTranscript(text2.trim(), isFinal, snr, confidence2);
697678
+ this.handleTranscript(text2.trim(), isFinal, snr, confidence2, consensus);
697585
697679
  };
697586
697680
  this._onError = (err) => {
697587
697681
  const msg = err instanceof Error ? err.message : String(err);
@@ -697648,7 +697742,7 @@ Rules:
697648
697742
  // ---------------------------------------------------------------------------
697649
697743
  // Transcript handling — VAD-style segment capture (Voryn pattern)
697650
697744
  // ---------------------------------------------------------------------------
697651
- handleTranscript(text2, isFinal, snr, confidence2) {
697745
+ handleTranscript(text2, isFinal, snr, confidence2, consensus) {
697652
697746
  if (!this.active) return;
697653
697747
  if (this._state !== "LISTENING" && this._state !== "CAPTURING") {
697654
697748
  return;
@@ -697661,7 +697755,7 @@ Rules:
697661
697755
  }
697662
697756
  if (this._state === "LISTENING") {
697663
697757
  this.setState("CAPTURING");
697664
- this.captureBuffer = "";
697758
+ this.resetCaptureState();
697665
697759
  this.captureStartTime = Date.now();
697666
697760
  this.maxSegmentTimer = setTimeout(() => {
697667
697761
  if (this._state === "CAPTURING") {
@@ -697669,8 +697763,18 @@ Rules:
697669
697763
  }
697670
697764
  }, MAX_SEGMENT_MS);
697671
697765
  }
697672
- this.captureBuffer = text2;
697673
- this.lastSignalScore = typeof snr === "number" && !Number.isNaN(snr) ? clamp0116(snr) : computeSignalFromText(text2, confidence2);
697766
+ const signalScore = typeof snr === "number" && !Number.isNaN(snr) ? clamp0116(snr) : computeSignalFromText(text2, confidence2);
697767
+ this.lastSignalScore = signalScore;
697768
+ if (consensus !== void 0) {
697769
+ this.consensusMetadataSeen = true;
697770
+ if (consensus === true) {
697771
+ this.consensusText = text2;
697772
+ this.consensusSignalScore = signalScore;
697773
+ this.captureBuffer = text2;
697774
+ }
697775
+ } else {
697776
+ this.captureBuffer = text2;
697777
+ }
697674
697778
  this.emit("snr", { score: this.lastSignalScore });
697675
697779
  this.onPartialTranscript(text2);
697676
697780
  if (this.silenceTimer) clearTimeout(this.silenceTimer);
@@ -697681,11 +697785,22 @@ Rules:
697681
697785
  }
697682
697786
  }, waitMs);
697683
697787
  }
697788
+ resetCaptureState() {
697789
+ this.captureBuffer = "";
697790
+ this.captureStartTime = 0;
697791
+ this.lastSignalScore = null;
697792
+ this.consensusMetadataSeen = false;
697793
+ this.consensusText = null;
697794
+ this.consensusSignalScore = null;
697795
+ }
697684
697796
  // ---------------------------------------------------------------------------
697685
697797
  // Segment finalization → Transcribing → Thinking → Speaking
697686
697798
  // ---------------------------------------------------------------------------
697687
697799
  finalizeSegment() {
697688
- const text2 = this.captureBuffer.trim();
697800
+ const rawText = this.captureBuffer.trim();
697801
+ const requiresConsensus = this.consensusMetadataSeen;
697802
+ const text2 = requiresConsensus ? (this.consensusText ?? "").trim() : rawText;
697803
+ const signalScore = requiresConsensus ? this.consensusSignalScore ?? this.lastSignalScore : this.lastSignalScore;
697689
697804
  if (this.silenceTimer) {
697690
697805
  clearTimeout(this.silenceTimer);
697691
697806
  this.silenceTimer = null;
@@ -697694,18 +697809,20 @@ Rules:
697694
697809
  clearTimeout(this.maxSegmentTimer);
697695
697810
  this.maxSegmentTimer = null;
697696
697811
  }
697697
- this.captureBuffer = "";
697812
+ this.resetCaptureState();
697698
697813
  if (!text2) {
697814
+ if (requiresConsensus && rawText) {
697815
+ if (this.debugSnr) this.onStatus(`Ignoring utterance without dual-ASR consensus: ${truncateForLog(rawText, 48)}`);
697816
+ this.emit("consensusFiltered", { text: rawText });
697817
+ }
697699
697818
  this.setState("LISTENING");
697700
697819
  return;
697701
697820
  }
697702
- const score = Math.min(this.lastSignalScore ?? 1, computeSignalFromText(text2));
697821
+ const score = Math.min(signalScore ?? 1, computeSignalFromText(text2));
697703
697822
  if (score < MIN_SIGNAL_SCORE || NOISE_ONLY_RE.test(text2)) {
697704
697823
  if (this.debugSnr) this.onStatus(`Ignoring low-signal utterance (SNR:${score.toFixed(2)}): ${truncateForLog(text2, 48)}`);
697705
697824
  this.emit("snrFiltered", { score, text: text2 });
697706
697825
  this.setState("LISTENING");
697707
- this.captureBuffer = "";
697708
- this.lastSignalScore = null;
697709
697826
  return;
697710
697827
  }
697711
697828
  this.setState("TRANSCRIBING");
@@ -76,10 +76,12 @@ def emit_error(msg: str):
76
76
  emit({"type": "error", "message": msg})
77
77
 
78
78
 
79
- def emit_transcript(text: str, is_final: bool = False, doa=None):
79
+ def emit_transcript(text: str, is_final: bool = False, doa=None, consensus=None):
80
80
  event = {"type": "transcript", "text": text, "isFinal": is_final}
81
81
  if doa is not None:
82
82
  event["doa"] = doa
83
+ if consensus is not None:
84
+ event["consensus"] = bool(consensus)
83
85
  emit(event)
84
86
 
85
87
  # ---------------------------------------------------------------------------
@@ -441,7 +443,7 @@ def main():
441
443
  result = run_transcribe(model, amplify(samples))
442
444
  text = _segment_filtered_text(result)
443
445
  if text:
444
- emit_transcript(text, is_final=False)
446
+ emit_transcript(text, is_final=False, consensus=False)
445
447
  except Exception as e:
446
448
  emit_error(f"Transcription error: {e}")
447
449
 
@@ -481,7 +483,14 @@ def main():
481
483
  if text == last_final_text:
482
484
  return
483
485
  last_final_text = text
484
- emit_transcript(text, is_final=True, doa=tuning.doa() if tuning else None)
486
+ # consensus=True only when BOTH models ran and converged — the
487
+ # downstream voicechat gate requires convergence for pass-through.
488
+ emit_transcript(
489
+ text,
490
+ is_final=True,
491
+ doa=tuning.doa() if tuning else None,
492
+ consensus=consensus_model is not None,
493
+ )
485
494
  except Exception as e:
486
495
  emit_error(f"Transcription error: {e}")
487
496
 
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.425",
3
+ "version": "1.0.427",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.425",
9
+ "version": "1.0.427",
10
10
  "bundleDependencies": [
11
11
  "image-to-ascii"
12
12
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.425",
3
+ "version": "1.0.427",
4
4
  "description": "AI coding agent powered by open-source models (Ollama/vLLM) — interactive TUI with agentic tool-calling loop",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",