open-agents-ai 0.138.0 → 0.138.1

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.
Files changed (2) hide show
  1. package/dist/index.js +91 -13
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -38765,20 +38765,21 @@ var init_voice = __esm({
38765
38765
  speak(text, emotion) {
38766
38766
  const pitchBias = emotion ? emotionToPitchBias(emotion, this.starkMode, this.autistMode) : 0;
38767
38767
  const speedFactor = emotion ? emotionToSpeedFactor(emotion, this.starkMode, this.autistMode) : 1;
38768
- this.enqueueSpeech(text, 1, 1 + pitchBias, speedFactor);
38768
+ this.enqueueSpeech(text, 1, 1 + pitchBias, speedFactor, 0.6);
38769
38769
  }
38770
38770
  /**
38771
- * Speak text at reduced volume with pitch shift to indicate
38771
+ * Speak text at reduced volume with narrow stereo placement to indicate
38772
38772
  * subordinate/secondary output (sub-agent activity, tool narration, etc.).
38773
- * Volume: 55% of primary. Base pitch: 0.92x (lower).
38774
- * Emotion further modulates pitch and speed on top of the subordinate base.
38773
+ * Volume: 55% of primary. Stereo delay: 0.15ms (narrow — "recessed/behind").
38774
+ * No pitch shift depth is conveyed by stereo narrowness + lower volume.
38775
+ * Emotion still modulates pitch and speed on top.
38775
38776
  */
38776
38777
  speakSubordinate(text, emotion) {
38777
38778
  const pitchBias = emotion ? emotionToPitchBias(emotion, this.starkMode, this.autistMode) : 0;
38778
38779
  const speedFactor = emotion ? emotionToSpeedFactor(emotion, this.starkMode, this.autistMode) : 1;
38779
- this.enqueueSpeech(text, 0.55, 0.92 + pitchBias, speedFactor);
38780
+ this.enqueueSpeech(text, 0.55, 1 + pitchBias, speedFactor, 0.15);
38780
38781
  }
38781
- enqueueSpeech(text, volume, pitchFactor, speedFactor = 1) {
38782
+ enqueueSpeech(text, volume, pitchFactor, speedFactor = 1, stereoDelayMs = 0.6) {
38782
38783
  if (!this.enabled || !this.ready)
38783
38784
  return;
38784
38785
  const chunks = this.chunkText(text);
@@ -38786,7 +38787,7 @@ var init_voice = __esm({
38786
38787
  this.speakQueue.length = 0;
38787
38788
  }
38788
38789
  for (const chunk of chunks) {
38789
- this.speakQueue.push({ text: chunk, volume, pitchFactor, speedFactor });
38790
+ this.speakQueue.push({ text: chunk, volume, pitchFactor, speedFactor, stereoDelayMs });
38790
38791
  }
38791
38792
  if (!this.speaking) {
38792
38793
  this.drainQueue().catch(() => {
@@ -38966,7 +38967,7 @@ var init_voice = __esm({
38966
38967
  }
38967
38968
  isFirst = false;
38968
38969
  try {
38969
- await this.synthesizeAndPlay(item.text, item.volume, item.pitchFactor, item.speedFactor);
38970
+ await this.synthesizeAndPlay(item.text, item.volume, item.pitchFactor, item.speedFactor, item.stereoDelayMs);
38970
38971
  } catch {
38971
38972
  }
38972
38973
  }
@@ -38978,9 +38979,9 @@ var init_voice = __esm({
38978
38979
  // -------------------------------------------------------------------------
38979
38980
  // Synthesis pipeline
38980
38981
  // -------------------------------------------------------------------------
38981
- async synthesizeAndPlay(text, volume = 1, pitchFactor = 1, speedFactor = 1) {
38982
+ async synthesizeAndPlay(text, volume = 1, pitchFactor = 1, speedFactor = 1, stereoDelayMs = 0.6) {
38982
38983
  if (this.luxttsActive) {
38983
- await this.synthesizeWithLuxtts(text, volume, pitchFactor, speedFactor);
38984
+ await this.synthesizeWithLuxtts(text, volume, pitchFactor, speedFactor, stereoDelayMs);
38984
38985
  return;
38985
38986
  }
38986
38987
  if (this.mlxActive) {
@@ -39026,6 +39027,7 @@ var init_voice = __esm({
39026
39027
  if (pitchFactor !== 1) {
39027
39028
  audioData = this.resamplePitch(audioData, pitchFactor);
39028
39029
  }
39030
+ const stereo = this.applyStereoDelay(audioData, sampleRate, stereoDelayMs);
39029
39031
  if (this.onPCMOutput) {
39030
39032
  const int16 = new Int16Array(audioData.length);
39031
39033
  for (let i = 0; i < audioData.length; i++) {
@@ -39035,7 +39037,7 @@ var init_voice = __esm({
39035
39037
  this.onPCMOutput(Buffer.from(int16.buffer), sampleRate);
39036
39038
  }
39037
39039
  const wavPath = join50(tmpdir9(), `oa-voice-${Date.now()}.wav`);
39038
- this.writeWav(audioData, sampleRate, wavPath);
39040
+ this.writeStereoWav(stereo.left, stereo.right, sampleRate, wavPath);
39039
39041
  await this.playWav(wavPath);
39040
39042
  try {
39041
39043
  unlinkSync8(wavPath);
@@ -39096,6 +39098,63 @@ var init_voice = __esm({
39096
39098
  }
39097
39099
  return out;
39098
39100
  }
39101
+ /**
39102
+ * Apply interaural time delay (ITD) to create stereo depth from mono audio.
39103
+ *
39104
+ * The right channel is delayed by `delayMs` milliseconds relative to the left.
39105
+ * This creates a perceived spatial position — wider delay = more "present/forward",
39106
+ * narrower delay = more "recessed/behind". Based on Blauert's duplex theory:
39107
+ * ITDs below ~1.5ms produce lateralization without audible echo.
39108
+ *
39109
+ * Typical values:
39110
+ * 0.6ms — primary voice, wide stereo presence ("speaking to you")
39111
+ * 0.15ms — secondary voice, narrow/centered ("background, recessed")
39112
+ */
39113
+ applyStereoDelay(mono, sampleRate, delayMs) {
39114
+ const delaySamples = Math.round(delayMs / 1e3 * sampleRate);
39115
+ const outLen = mono.length + delaySamples;
39116
+ const left = new Float32Array(outLen);
39117
+ const right = new Float32Array(outLen);
39118
+ left.set(mono, 0);
39119
+ right.set(mono, delaySamples);
39120
+ return { left, right };
39121
+ }
39122
+ /**
39123
+ * Write a stereo (2-channel interleaved) WAV file from left/right Float32 channels.
39124
+ * PCM 16-bit, same sample rate as the model output.
39125
+ */
39126
+ writeStereoWav(left, right, sampleRate, path) {
39127
+ const numChannels = 2;
39128
+ const bitsPerSample = 16;
39129
+ const numSamples = Math.min(left.length, right.length);
39130
+ const byteRate = sampleRate * numChannels * (bitsPerSample / 8);
39131
+ const blockAlign = numChannels * (bitsPerSample / 8);
39132
+ const dataSize = numSamples * numChannels * (bitsPerSample / 8);
39133
+ const buffer = Buffer.alloc(44 + dataSize);
39134
+ buffer.write("RIFF", 0);
39135
+ buffer.writeUInt32LE(36 + dataSize, 4);
39136
+ buffer.write("WAVE", 8);
39137
+ buffer.write("fmt ", 12);
39138
+ buffer.writeUInt32LE(16, 16);
39139
+ buffer.writeUInt16LE(1, 20);
39140
+ buffer.writeUInt16LE(numChannels, 22);
39141
+ buffer.writeUInt32LE(sampleRate, 24);
39142
+ buffer.writeUInt32LE(byteRate, 28);
39143
+ buffer.writeUInt16LE(blockAlign, 32);
39144
+ buffer.writeUInt16LE(bitsPerSample, 34);
39145
+ buffer.write("data", 36);
39146
+ buffer.writeUInt32LE(dataSize, 40);
39147
+ let pos = 44;
39148
+ for (let i = 0; i < numSamples; i++) {
39149
+ const lSample = Math.max(-1, Math.min(1, left[i]));
39150
+ const rSample = Math.max(-1, Math.min(1, right[i]));
39151
+ buffer.writeInt16LE(lSample < 0 ? lSample * 32768 : lSample * 32767, pos);
39152
+ pos += 2;
39153
+ buffer.writeInt16LE(rSample < 0 ? rSample * 32768 : rSample * 32767, pos);
39154
+ pos += 2;
39155
+ }
39156
+ writeFileSync14(path, buffer);
39157
+ }
39099
39158
  // -------------------------------------------------------------------------
39100
39159
  // Phonemization
39101
39160
  // -------------------------------------------------------------------------
@@ -39790,9 +39849,9 @@ if __name__ == '__main__':
39790
39849
  /**
39791
39850
  * Synthesize and play audio using LuxTTS voice cloning.
39792
39851
  * Speed is passed natively to LuxTTS. Pitch is post-processed via resampling.
39793
- * Volume is applied via WAV sample scaling.
39852
+ * Volume is applied via WAV sample scaling. Stereo ITD applied for depth.
39794
39853
  */
39795
- async synthesizeWithLuxtts(text, volume = 1, pitchFactor = 1, speedFactor = 1) {
39854
+ async synthesizeWithLuxtts(text, volume = 1, pitchFactor = 1, speedFactor = 1, stereoDelayMs = 0.6) {
39796
39855
  if (!this.luxttsCloneRef || !existsSync35(this.luxttsCloneRef)) {
39797
39856
  return;
39798
39857
  }
@@ -39857,6 +39916,25 @@ if __name__ == '__main__':
39857
39916
  } catch {
39858
39917
  }
39859
39918
  }
39919
+ if (stereoDelayMs > 0) {
39920
+ try {
39921
+ const wavData = readFileSync24(wavPath);
39922
+ if (wavData.length > 44) {
39923
+ const sampleRate = wavData.readUInt32LE(24);
39924
+ const numChannels = wavData.readUInt16LE(22);
39925
+ if (numChannels === 1) {
39926
+ const int16 = new Int16Array(wavData.buffer, wavData.byteOffset + 44, (wavData.length - 44) / 2);
39927
+ const float32 = new Float32Array(int16.length);
39928
+ for (let i = 0; i < int16.length; i++) {
39929
+ float32[i] = int16[i] / 32768;
39930
+ }
39931
+ const stereo = this.applyStereoDelay(float32, sampleRate, stereoDelayMs);
39932
+ this.writeStereoWav(stereo.left, stereo.right, sampleRate, wavPath);
39933
+ }
39934
+ }
39935
+ } catch {
39936
+ }
39937
+ }
39860
39938
  await this.playWav(wavPath);
39861
39939
  try {
39862
39940
  unlinkSync8(wavPath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.138.0",
3
+ "version": "0.138.1",
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",