@ouro.bot/cli 0.1.0-alpha.891 → 0.1.0-alpha.893
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/changelog.json +12 -0
- package/deploy/unraid/sanctuary.ouro/bundle-meta.json +1 -1
- package/deploy/unraid/sanctuary.xml +1 -1
- package/dist/heart/cmux-config.js +102 -0
- package/dist/heart/daemon/runtime-logging.js +1 -1
- package/dist/heart/daemon/sense-manager.js +23 -6
- package/dist/heart/identity.js +4 -1
- package/dist/heart/private-runtime/policy.js +8 -0
- package/dist/heart/sense-truth.js +2 -0
- package/dist/heart/turn-context.js +7 -0
- package/dist/mind/prompt.js +7 -0
- package/dist/repertoire/guardrails.js +6 -0
- package/dist/repertoire/tools-cmux.js +277 -0
- package/dist/repertoire/tools.js +4 -1
- package/dist/senses/cmux/attention.js +263 -0
- package/dist/senses/cmux/client.js +196 -0
- package/dist/senses/cmux/redact.js +37 -0
- package/dist/senses/cmux/sense.js +248 -0
- package/dist/senses/cmux-entry.js +77 -0
- package/dist/senses/voice/local-audio-devices.js +52 -7
- package/dist/senses/voice/local-audio-transport.js +22 -1
- package/dist/senses/voice/twilio-phone.js +17 -4
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
|
@@ -137,6 +137,23 @@ class LocalAudioDeviceTransport {
|
|
|
137
137
|
async leave(reason = "left") {
|
|
138
138
|
await this.finish(reason);
|
|
139
139
|
}
|
|
140
|
+
/** Live playback goes through ffmpeg, which names devices by index: find BlackHole 2ch's index now. */
|
|
141
|
+
async resolveLivePlayback() {
|
|
142
|
+
const command = this.deps.ffmpegPath ?? "ffmpeg";
|
|
143
|
+
const list = this.deps.listAudioToolboxDevices ?? (() => (0, local_audio_devices_1.defaultListAudioToolboxDevices)(command));
|
|
144
|
+
let listing;
|
|
145
|
+
try {
|
|
146
|
+
listing = await list();
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
throw new Error(`Local audio routing is not ready:\n- Live playback needs ffmpeg (${errorText(error)}): brew install ffmpeg`);
|
|
150
|
+
}
|
|
151
|
+
const index = (0, local_audio_devices_1.findAudioToolboxDeviceIndex)(listing, local_audio_devices_1.PLAYBACK_DEVICE_NAME);
|
|
152
|
+
if (index === undefined) {
|
|
153
|
+
throw new Error(`Local audio routing is not ready:\n- ffmpeg does not list the playback device "${local_audio_devices_1.PLAYBACK_DEVICE_NAME}". Install it with brew install --cask blackhole-2ch, then restart macOS audio or reboot.`);
|
|
154
|
+
}
|
|
155
|
+
return { command, args: (0, local_audio_devices_1.ffmpegPlaybackArgs)(index) };
|
|
156
|
+
}
|
|
140
157
|
async startUnchecked() {
|
|
141
158
|
const { request, deps } = this;
|
|
142
159
|
if (!deps.bridgeOptions.openaiRealtime?.apiKey?.trim()) {
|
|
@@ -144,10 +161,14 @@ class LocalAudioDeviceTransport {
|
|
|
144
161
|
}
|
|
145
162
|
const files = request.files;
|
|
146
163
|
const soxPath = deps.soxPath;
|
|
164
|
+
const livePlayback = files ? undefined : await this.resolveLivePlayback();
|
|
165
|
+
if (this.finished)
|
|
166
|
+
return;
|
|
147
167
|
this.devices = new local_audio_devices_1.LocalAudioDevices({
|
|
148
168
|
spawner: deps.spawner,
|
|
149
169
|
captureArgs: files ? (0, local_audio_devices_1.fileCaptureArgs)(files.inputPath) : (0, local_audio_devices_1.soxCaptureArgs)(local_audio_devices_1.CAPTURE_DEVICE_NAME),
|
|
150
|
-
playbackArgs:
|
|
170
|
+
playbackArgs: livePlayback ? livePlayback.args : (0, local_audio_devices_1.filePlaybackArgs)(files.outputPath),
|
|
171
|
+
...(livePlayback ? { playbackCommand: livePlayback.command } : {}),
|
|
151
172
|
pidFile: deps.pidFile,
|
|
152
173
|
soxPath,
|
|
153
174
|
killGroup: deps.killGroup,
|
|
@@ -417,13 +417,19 @@ function isSafeVoiceFriendId(id) {
|
|
|
417
417
|
* (the store's display-name fallback is never used for voice). Everything else resolves from the
|
|
418
418
|
* phone number, or from the call itself when there is none.
|
|
419
419
|
*/
|
|
420
|
+
function friendHasPhone(friend, phone) {
|
|
421
|
+
return friend.externalIds.some((externalId) => (0, phone_1.normalizeTwilioE164PhoneNumber)(externalId.externalId) === phone);
|
|
422
|
+
}
|
|
420
423
|
async function resolveVoiceFriendContext(options, input) {
|
|
421
424
|
const agentRoot = resolveTwilioPhoneAgentRoot(options);
|
|
422
425
|
const friendStore = new friends_1.FileFriendStore(path.join(agentRoot, "friends"));
|
|
423
426
|
const explicitFriendId = input.friendId?.trim();
|
|
427
|
+
const remotePhone = (0, phone_1.normalizeTwilioE164PhoneNumber)(input.remotePhone);
|
|
424
428
|
if (explicitFriendId && isSafeVoiceFriendId(explicitFriendId)) {
|
|
425
429
|
const existing = await friendStore.get(explicitFriendId);
|
|
426
|
-
|
|
430
|
+
// An outbound job names a friend, but the person who answers is whoever has the dialed number:
|
|
431
|
+
// a number that is not on the friend's record gets resolved like any other caller.
|
|
432
|
+
if (existing && existing.id === explicitFriendId && (!remotePhone || friendHasPhone(existing, remotePhone))) {
|
|
427
433
|
return {
|
|
428
434
|
friendId: existing.id,
|
|
429
435
|
friendStore,
|
|
@@ -431,7 +437,6 @@ async function resolveVoiceFriendContext(options, input) {
|
|
|
431
437
|
};
|
|
432
438
|
}
|
|
433
439
|
}
|
|
434
|
-
const remotePhone = (0, phone_1.normalizeTwilioE164PhoneNumber)(input.remotePhone);
|
|
435
440
|
const provider = remotePhone ? "imessage-handle" : "local";
|
|
436
441
|
const externalId = remotePhone || friendIdFromCaller("", input.callSid);
|
|
437
442
|
const resolver = new friends_1.FriendResolver(friendStore, {
|
|
@@ -481,6 +486,14 @@ function twilioPhoneVoiceSessionKey(options) {
|
|
|
481
486
|
return `twilio-phone-line-${lineSegment}`;
|
|
482
487
|
return `twilio-phone-${safeSegment(options.callSid ?? "incoming")}`;
|
|
483
488
|
}
|
|
489
|
+
/**
|
|
490
|
+
* A Realtime `response.create` that carries `instructions` replaces the session instructions for
|
|
491
|
+
* that response, so a greeting sent that way knows nothing about who the agent is. Without this
|
|
492
|
+
* line the model introduced itself as "ChatGPT" in 3 of 6 live calls.
|
|
493
|
+
*/
|
|
494
|
+
function realtimeGreetingInstructions(agentName, prompt) {
|
|
495
|
+
return `You are ${agentName}. If you introduce yourself, use that name; never call yourself ChatGPT or an OpenAI assistant.\n${prompt}`;
|
|
496
|
+
}
|
|
484
497
|
function callConnectedPrompt(params) {
|
|
485
498
|
const from = params.From?.trim();
|
|
486
499
|
const to = params.To?.trim();
|
|
@@ -2098,7 +2111,7 @@ class TwilioOpenAIRealtimeMediaStreamSession {
|
|
|
2098
2111
|
createdAt: new Date().toISOString(),
|
|
2099
2112
|
}, { From: this.to, To: this.from })
|
|
2100
2113
|
: callConnectedPrompt({ From: this.from, To: this.to });
|
|
2101
|
-
this.requestRealtimeResponse({ instructions: promptText });
|
|
2114
|
+
this.requestRealtimeResponse({ instructions: realtimeGreetingInstructions(this.options.agentName, promptText) });
|
|
2102
2115
|
}
|
|
2103
2116
|
handleMedia(media) {
|
|
2104
2117
|
const payload = stringField(media?.payload);
|
|
@@ -3350,7 +3363,7 @@ class OpenAISipPhoneSession {
|
|
|
3350
3363
|
return;
|
|
3351
3364
|
this.initialGreetingSent = true;
|
|
3352
3365
|
this.requestRealtimeResponse({
|
|
3353
|
-
instructions: openAISipCallConnectedPrompt(this.metadata, this.options.openaiRealtime?.voiceStyle),
|
|
3366
|
+
instructions: realtimeGreetingInstructions(this.options.agentName, openAISipCallConnectedPrompt(this.metadata, this.options.openaiRealtime?.voiceStyle)),
|
|
3354
3367
|
});
|
|
3355
3368
|
}
|
|
3356
3369
|
handleOpenAIMessage(raw) {
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ouro.bot/cli",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.893",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@ouro.bot/cli",
|
|
9
|
-
"version": "0.1.0-alpha.
|
|
9
|
+
"version": "0.1.0-alpha.893",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@anthropic-ai/sdk": "^0.78.0",
|
|
12
12
|
"@azure/identity": "^4.13.0",
|