[codex] add roles to realtime append text (#27936)

## Summary

Add an explicit `user` or `developer` role to
`thread/realtime/appendText` and propagate it through the realtime input
queue into `conversation.item.create`. Older JSON clients that omit the
field continue to default to `user`.

This lets app-provided context such as memory retain developer authority
without bypassing app-server through a renderer-owned data channel. The
app-server schemas, API documentation, and focused protocol and
websocket coverage are updated with the new contract.

The Codex Apps consumer is tracked in
[openai/openai#1025261](https://github.com/openai/openai/pull/1025261).
This commit is contained in:
Alex Gamble
2026-06-12 15:05:37 -07:00
committed by GitHub
parent 9915d34684
commit 216dee1189
18 changed files with 176 additions and 50 deletions
@@ -628,6 +628,13 @@
}
]
},
"ConversationTextRole": {
"enum": [
"user",
"developer"
],
"type": "string"
},
"DynamicToolSpec": {
"properties": {
"deferLoading": {
@@ -8539,6 +8539,13 @@
"title": "ContextCompactedNotification",
"type": "object"
},
"ConversationTextRole": {
"enum": [
"user",
"developer"
],
"type": "string"
},
"CreditsSnapshot": {
"properties": {
"balance": {
@@ -4852,6 +4852,13 @@
"title": "ContextCompactedNotification",
"type": "object"
},
"ConversationTextRole": {
"enum": [
"user",
"developer"
],
"type": "string"
},
"CreditsSnapshot": {
"properties": {
"balance": {
@@ -0,0 +1,5 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type ConversationTextRole = "user" | "developer";
+1
View File
@@ -14,6 +14,7 @@ export type { CollaborationMode } from "./CollaborationMode";
export type { ContentItem } from "./ContentItem";
export type { ConversationGitInfo } from "./ConversationGitInfo";
export type { ConversationSummary } from "./ConversationSummary";
export type { ConversationTextRole } from "./ConversationTextRole";
export type { ExecCommandApprovalParams } from "./ExecCommandApprovalParams";
export type { ExecCommandApprovalResponse } from "./ExecCommandApprovalResponse";
export type { ExecPolicyAmendment } from "./ExecPolicyAmendment";
@@ -1,3 +1,4 @@
use codex_protocol::protocol::ConversationTextRole;
use codex_protocol::protocol::RealtimeAudioFrame as CoreRealtimeAudioFrame;
use codex_protocol::protocol::RealtimeConversationArchitecture;
use codex_protocol::protocol::RealtimeConversationVersion;
@@ -135,6 +136,8 @@ pub struct ThreadRealtimeAppendAudioResponse {}
pub struct ThreadRealtimeAppendTextParams {
pub thread_id: String,
pub text: String,
#[serde(default)]
pub role: ConversationTextRole,
}
/// EXPERIMENTAL - response for appending realtime text input.
@@ -27,6 +27,7 @@ use codex_protocol::permissions::FileSystemSandboxEntry as CoreFileSystemSandbox
use codex_protocol::permissions::FileSystemSpecialPath as CoreFileSystemSpecialPath;
use codex_protocol::protocol::AgentStatus as CoreAgentStatus;
use codex_protocol::protocol::AskForApproval as CoreAskForApproval;
use codex_protocol::protocol::ConversationTextRole;
use codex_protocol::protocol::GranularApprovalConfig as CoreGranularApprovalConfig;
use codex_protocol::protocol::NetworkAccess as CoreNetworkAccess;
use codex_protocol::request_permissions::RequestPermissionProfile as CoreRequestPermissionProfile;
@@ -3862,3 +3863,21 @@ fn turn_start_params_reject_relative_environment_cwd() {
"unexpected error: {err}"
);
}
#[test]
fn realtime_append_text_defaults_role_to_user() {
let params = serde_json::from_value::<ThreadRealtimeAppendTextParams>(json!({
"threadId": "thread_123",
"text": "hello",
}))
.expect("params should deserialize");
assert_eq!(
params,
ThreadRealtimeAppendTextParams {
thread_id: "thread_123".to_string(),
text: "hello".to_string(),
role: ConversationTextRole::User,
}
);
}