open-agents-ai 0.138.0 → 0.138.2
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 +218 -16
- 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
|
|
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.
|
|
38774
|
-
*
|
|
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,
|
|
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.
|
|
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);
|
|
@@ -45073,6 +45151,8 @@ var init_stream_renderer = __esm({
|
|
|
45073
45151
|
startTime = 0;
|
|
45074
45152
|
/** Track if we're mid-tool-arg display */
|
|
45075
45153
|
inToolArgs = false;
|
|
45154
|
+
/** Optional callback to capture rendered lines for scrollback buffer */
|
|
45155
|
+
onRenderedLine = null;
|
|
45076
45156
|
/**
|
|
45077
45157
|
* Track accumulated content size for JSON blob detection.
|
|
45078
45158
|
* When a non-newline JSON blob exceeds this threshold, suppress rendering.
|
|
@@ -45260,9 +45340,18 @@ var init_stream_renderer = __esm({
|
|
|
45260
45340
|
this.writeRaw(dimText(prefix) + rendered + (hasNewline ? "\n" : ""));
|
|
45261
45341
|
this.lineStarted = !hasNewline;
|
|
45262
45342
|
}
|
|
45263
|
-
/** Write raw ANSI text to stdout */
|
|
45343
|
+
/** Write raw ANSI text to stdout and capture for scrollback */
|
|
45264
45344
|
writeRaw(text) {
|
|
45265
45345
|
process.stdout.write(text);
|
|
45346
|
+
if (this.onRenderedLine) {
|
|
45347
|
+
const parts = text.split("\n");
|
|
45348
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
45349
|
+
this.onRenderedLine(parts[i]);
|
|
45350
|
+
}
|
|
45351
|
+
if (parts.length === 1 && parts[0].length > 0) {
|
|
45352
|
+
this.onRenderedLine(parts[0]);
|
|
45353
|
+
}
|
|
45354
|
+
}
|
|
45266
45355
|
}
|
|
45267
45356
|
/** Flush partial buffer (non-newline-terminated tokens) */
|
|
45268
45357
|
flushPartial(kind) {
|
|
@@ -50677,6 +50766,12 @@ var init_status_bar = __esm({
|
|
|
50677
50766
|
};
|
|
50678
50767
|
active = false;
|
|
50679
50768
|
scrollRegionTop = 1;
|
|
50769
|
+
// Virtual scrollback buffer — stores content lines for Page Up/Down scrolling.
|
|
50770
|
+
// Since we're in alternate screen buffer, there's no native scrollback.
|
|
50771
|
+
_contentLines = [];
|
|
50772
|
+
_contentScrollOffset = 0;
|
|
50773
|
+
// 0 = live (bottom), >0 = scrolled back
|
|
50774
|
+
_contentMaxLines = 1e4;
|
|
50680
50775
|
stdinHooked = false;
|
|
50681
50776
|
/** COHERE distributed cognitive commons active flag */
|
|
50682
50777
|
_cohereActive = false;
|
|
@@ -51418,6 +51513,76 @@ var init_status_bar = __esm({
|
|
|
51418
51513
|
this.renderFooterAndPositionInput();
|
|
51419
51514
|
}
|
|
51420
51515
|
}
|
|
51516
|
+
// -----------------------------------------------------------------------
|
|
51517
|
+
// Content scrollback — virtual scroll through buffered output lines
|
|
51518
|
+
// -----------------------------------------------------------------------
|
|
51519
|
+
/** Record a content line for scrollback. Called by the stream renderer intercept. */
|
|
51520
|
+
bufferContentLine(line) {
|
|
51521
|
+
this._contentLines.push(line);
|
|
51522
|
+
if (this._contentLines.length > this._contentMaxLines) {
|
|
51523
|
+
this._contentLines.splice(0, this._contentLines.length - this._contentMaxLines);
|
|
51524
|
+
if (this._contentScrollOffset > 0) {
|
|
51525
|
+
this._contentScrollOffset = Math.min(this._contentScrollOffset, Math.max(0, this._contentLines.length - this.contentHeight));
|
|
51526
|
+
}
|
|
51527
|
+
}
|
|
51528
|
+
}
|
|
51529
|
+
/** Number of visible content rows */
|
|
51530
|
+
get contentHeight() {
|
|
51531
|
+
const rows = process.stdout.rows ?? 24;
|
|
51532
|
+
return Math.max(1, rows - (this.scrollRegionTop - 1) - this._currentFooterHeight);
|
|
51533
|
+
}
|
|
51534
|
+
/** Whether user has scrolled back from live */
|
|
51535
|
+
get isScrolledBack() {
|
|
51536
|
+
return this._contentScrollOffset > 0;
|
|
51537
|
+
}
|
|
51538
|
+
/** Scroll up through content history */
|
|
51539
|
+
scrollContentUp(lines = 1) {
|
|
51540
|
+
if (!this.active)
|
|
51541
|
+
return;
|
|
51542
|
+
const maxOffset = Math.max(0, this._contentLines.length - this.contentHeight);
|
|
51543
|
+
this._contentScrollOffset = Math.min(maxOffset, this._contentScrollOffset + lines);
|
|
51544
|
+
this.repaintContent();
|
|
51545
|
+
}
|
|
51546
|
+
/** Scroll down through content history */
|
|
51547
|
+
scrollContentDown(lines = 1) {
|
|
51548
|
+
if (!this.active)
|
|
51549
|
+
return;
|
|
51550
|
+
this._contentScrollOffset = Math.max(0, this._contentScrollOffset - lines);
|
|
51551
|
+
this.repaintContent();
|
|
51552
|
+
}
|
|
51553
|
+
/** Page up — scroll by visible height */
|
|
51554
|
+
pageUpContent() {
|
|
51555
|
+
this.scrollContentUp(Math.max(1, this.contentHeight - 2));
|
|
51556
|
+
}
|
|
51557
|
+
/** Page down — scroll by visible height */
|
|
51558
|
+
pageDownContent() {
|
|
51559
|
+
this.scrollContentDown(Math.max(1, this.contentHeight - 2));
|
|
51560
|
+
}
|
|
51561
|
+
/** Jump to live (End key) */
|
|
51562
|
+
jumpToLive() {
|
|
51563
|
+
this._contentScrollOffset = 0;
|
|
51564
|
+
this.repaintContent();
|
|
51565
|
+
}
|
|
51566
|
+
/** Repaint content area from buffer at current scroll position.
|
|
51567
|
+
* Uses direct cursor positioning (btop-style) — no DECSTBM scrolling. */
|
|
51568
|
+
repaintContent() {
|
|
51569
|
+
const h = this.contentHeight;
|
|
51570
|
+
const totalLines = this._contentLines.length;
|
|
51571
|
+
const startIdx = Math.max(0, totalLines - h - this._contentScrollOffset);
|
|
51572
|
+
let buf = "\x1B7\x1B[?25l";
|
|
51573
|
+
for (let row = 0; row < h; row++) {
|
|
51574
|
+
const lineIdx = startIdx + row;
|
|
51575
|
+
const line = lineIdx < totalLines ? this._contentLines[lineIdx] : "";
|
|
51576
|
+
const screenRow = this.scrollRegionTop + row;
|
|
51577
|
+
buf += `\x1B[${screenRow};1H\x1B[2K${line}`;
|
|
51578
|
+
}
|
|
51579
|
+
if (this._contentScrollOffset > 0) {
|
|
51580
|
+
const linesAbove = Math.max(0, totalLines - h - this._contentScrollOffset);
|
|
51581
|
+
buf += `\x1B[${this.scrollRegionTop};1H\x1B[7m \u2191 ${linesAbove} lines above (PgDn/End to return) \x1B[0m`;
|
|
51582
|
+
}
|
|
51583
|
+
buf += "\x1B8\x1B[?25h";
|
|
51584
|
+
process.stdout.write(buf);
|
|
51585
|
+
}
|
|
51421
51586
|
/**
|
|
51422
51587
|
* Clear the input row and position cursor there at column 1.
|
|
51423
51588
|
* Used by showPrompt() before readline writes the prompt.
|
|
@@ -52836,9 +53001,23 @@ ${entry.fullContent}`
|
|
|
52836
53001
|
return;
|
|
52837
53002
|
}
|
|
52838
53003
|
if (statusBar?.isActive) {
|
|
53004
|
+
const origWrite = process.stdout.write;
|
|
53005
|
+
const boundWrite = origWrite.bind(process.stdout);
|
|
53006
|
+
process.stdout.write = ((chunk, ...args) => {
|
|
53007
|
+
const text = typeof chunk === "string" ? chunk : new TextDecoder().decode(chunk);
|
|
53008
|
+
for (const line of text.split("\n")) {
|
|
53009
|
+
if (line.length > 0)
|
|
53010
|
+
statusBar.bufferContentLine(line);
|
|
53011
|
+
}
|
|
53012
|
+
return boundWrite(chunk, ...args);
|
|
53013
|
+
});
|
|
52839
53014
|
statusBar.beginContentWrite();
|
|
52840
|
-
|
|
52841
|
-
|
|
53015
|
+
try {
|
|
53016
|
+
fn();
|
|
53017
|
+
} finally {
|
|
53018
|
+
process.stdout.write = origWrite;
|
|
53019
|
+
statusBar.endContentWrite();
|
|
53020
|
+
}
|
|
52842
53021
|
} else {
|
|
52843
53022
|
fn();
|
|
52844
53023
|
}
|
|
@@ -53403,6 +53582,7 @@ async function startInteractive(config, repoPath) {
|
|
|
53403
53582
|
let adminSessionKey = null;
|
|
53404
53583
|
const callSubAgents = /* @__PURE__ */ new Map();
|
|
53405
53584
|
const streamRenderer = new StreamRenderer();
|
|
53585
|
+
streamRenderer.onRenderedLine = (line) => statusBar.bufferContentLine(line);
|
|
53406
53586
|
if (savedSettings.voice) {
|
|
53407
53587
|
voiceEngine.toggle().catch((err) => {
|
|
53408
53588
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -55766,6 +55946,28 @@ ${c2.dim("(Use /quit to exit)")}
|
|
|
55766
55946
|
process.stdin.on("keypress", (_str, key) => {
|
|
55767
55947
|
if (!key)
|
|
55768
55948
|
return;
|
|
55949
|
+
if (key.name === "pageup" || key.shift && key.name === "up") {
|
|
55950
|
+
statusBar.pageUpContent();
|
|
55951
|
+
return;
|
|
55952
|
+
}
|
|
55953
|
+
if (key.name === "pagedown" || key.shift && key.name === "down") {
|
|
55954
|
+
statusBar.pageDownContent();
|
|
55955
|
+
return;
|
|
55956
|
+
}
|
|
55957
|
+
if (key.name === "end" && key.ctrl) {
|
|
55958
|
+
statusBar.jumpToLive();
|
|
55959
|
+
return;
|
|
55960
|
+
}
|
|
55961
|
+
if (key.sequence) {
|
|
55962
|
+
if (key.sequence.includes("[<65;") || key.sequence === "\x1B[A" && key.shift) {
|
|
55963
|
+
statusBar.scrollContentUp(3);
|
|
55964
|
+
return;
|
|
55965
|
+
}
|
|
55966
|
+
if (key.sequence.includes("[<64;") || key.sequence === "\x1B[B" && key.shift) {
|
|
55967
|
+
statusBar.scrollContentDown(3);
|
|
55968
|
+
return;
|
|
55969
|
+
}
|
|
55970
|
+
}
|
|
55769
55971
|
if (key.name === "escape" && activeTask) {
|
|
55770
55972
|
if (!activeTask.runner.isPaused) {
|
|
55771
55973
|
activeTask.runner.pause();
|
package/package.json
CHANGED