mirror of
https://github.com/openai/codex.git
synced 2026-09-20 12:47:38 +00:00
## What changed - Add feature-gated `/voice`, `/voice mute`, and `/voice stop` commands with local WebRTC audio and app-server signaling. - Show live transcripts, conversation status, and microphone and speaker levels. Preserve captions and undelivered answers across thread switches. - Speak final answers from voice handoffs while keeping delegated reasoning and commentary hidden and typed answers unspoken. - Retry eligible startup failures once and clean up voice sessions on thread switches and disconnects. - Stop voice and block late handoffs after a misalignment policy violation. Remove realtime event payloads and spoken text from receipt and debug logs. ## Testing Add coverage for voice command mapping, rejected starts, disconnect cleanup, delegated final-answer speech, late handoff rejection, and transcript and footer rendering. GitOrigin-RevId: 11def1f86b0e023b21d0d92072d6636ccad89815
71 lines
2.3 KiB
Rust
71 lines
2.3 KiB
Rust
//! Protect connection credentials in diagnostics without changing signaling data.
|
|
|
|
use super::AppCommand;
|
|
use codex_protocol::ThreadId;
|
|
use pretty_assertions::assert_eq;
|
|
use serde_json::json;
|
|
|
|
#[test]
|
|
fn realtime_offer_is_redacted_in_diagnostics_and_preserved_for_signaling() {
|
|
let thread_id =
|
|
ThreadId::from_string("00000000-0000-0000-0000-000000000001").expect("valid thread id");
|
|
let original_offer =
|
|
"v=0\r\na=ice-ufrag:private-user\r\na=ice-pwd:private-credential\r\n".to_string();
|
|
let command = AppCommand::RealtimeConversationStart {
|
|
thread_id,
|
|
offer_sdp: original_offer.clone().into(),
|
|
};
|
|
|
|
assert_eq!(
|
|
format!("{command:?}"),
|
|
format!("RealtimeConversationStart {{ thread_id: {thread_id:?}, offer_sdp: <redacted> }}")
|
|
);
|
|
assert_eq!(
|
|
serde_json::to_value(&command).expect("command diagnostics serialize"),
|
|
json!({
|
|
"RealtimeConversationStart": {
|
|
"thread_id": thread_id,
|
|
"offer_sdp": "<redacted>"
|
|
}
|
|
})
|
|
);
|
|
|
|
let AppCommand::RealtimeConversationStart { offer_sdp, .. } = command else {
|
|
panic!("expected the start command");
|
|
};
|
|
assert_eq!(String::from(offer_sdp), original_offer);
|
|
}
|
|
|
|
#[test]
|
|
fn realtime_speech_is_redacted_in_diagnostics_and_preserved_for_signaling() {
|
|
let thread_id =
|
|
ThreadId::from_string("00000000-0000-0000-0000-000000000001").expect("valid thread id");
|
|
let original_text = "Private spoken answer".to_string();
|
|
let command = AppCommand::RealtimeConversationSpeech {
|
|
thread_id,
|
|
attempt_id: 1,
|
|
input_generation: 2,
|
|
delivery_id: 3,
|
|
text: original_text.clone().into(),
|
|
};
|
|
|
|
assert!(!format!("{command:?}").contains(&original_text));
|
|
assert_eq!(
|
|
serde_json::to_value(&command).expect("command diagnostics serialize"),
|
|
json!({
|
|
"RealtimeConversationSpeech": {
|
|
"thread_id": thread_id,
|
|
"attempt_id": 1,
|
|
"input_generation": 2,
|
|
"delivery_id": 3,
|
|
"text": "<redacted>"
|
|
}
|
|
})
|
|
);
|
|
|
|
let AppCommand::RealtimeConversationSpeech { text, .. } = command else {
|
|
panic!("expected the speech command");
|
|
};
|
|
assert_eq!(String::from(text), original_text);
|
|
}
|