Preserve Guardian sender context for cloud deliveries with orbit IDs (#58946)

## Why

Delegation messages containing `<source_orbit_id>` fail sender provenance parsing, leaving Guardian without the sender's instructions.

## What changed

Accept an optional `<source_orbit_id>` between `<source_thread_id>` and `<input>`, rejecting values containing `<`. Continue to derive sender authority from the source thread ID.

## Testing

Update the cross-host sender context scenario and snapshot to include orbit attribution. The scenario checks that sender evidence reaches Guardian after compaction and rollback without exposing it to the receiving agent.

GitOrigin-RevId: 463b18667384d30297245eedcd0f6bcbf683634e
This commit is contained in:
felixxia-oai
2026-11-02 18:43:57 +00:00
committed by copyberry
parent 4515bc6df0
commit 00df7f65ab
3 changed files with 16 additions and 8 deletions
@@ -47,10 +47,18 @@ impl LocalAgentRuntime {
.trim()
.strip_prefix("<source_thread_id>")?
.split_once("</source_thread_id>")?;
input
.trim()
.strip_prefix("<input>")?
.strip_suffix("</input>")?;
let input = input.trim();
// Cloud deliveries include Dot attribution, but authority comes from the source thread.
let input = if let Some(rest) = input.strip_prefix("<source_orbit_id>") {
let (orbit_id, input) = rest.split_once("</source_orbit_id>")?;
if orbit_id.contains('<') {
return None;
}
input.trim()
} else {
input
};
input.strip_prefix("<input>")?.strip_suffix("</input>")?;
ThreadId::from_string(source)
.ok()
.filter(|source| *source != receiver_thread_id)
@@ -130,7 +130,7 @@ async fn cross_host_sender_context(history_mode: ThreadHistoryMode) -> Result<()
let delivery: ResponseItem = serde_json::from_value(json!({
"type": "function_call_output", "id": "delivery-0",
"name": "send_message", "namespace": "cloud_threads",
"output": format!("<codex_delegation><source_thread_id>{sender_id}</source_thread_id><input>Inspect.</input></codex_delegation>"),
"output": format!("<codex_delegation><source_thread_id>{sender_id}</source_thread_id><source_orbit_id>orbit-source</source_orbit_id><input>Inspect.</input></codex_delegation>"),
}))?;
receiver
.codex
@@ -81,7 +81,7 @@ Settings: relative to window 2
</tools>
02:message/user:
<ENVIRONMENT_CONTEXT>
03:function_call_output/cloud_threads.send_message:<codex_delegation><source_thread_id><UUID 1></source_thread_id><input>Inspect.</input></codex_delegation>
03:function_call_output/cloud_threads.send_message:<codex_delegation><source_thread_id><UUID 1></source_thread_id><source_orbit_id>orbit-source</source_orbit_id><input>Inspect.</input></codex_delegation>
## Window 4 (after request 5: input diverged at item 00)
Settings: relative to window 3
@@ -117,7 +117,7 @@ Settings: relative to window 3
[03] >>> TRANSCRIPT START
[04] [1] tool cloud_threads.send_message result: <codex_delegation><source_thread_id><UUID 1></source_thread_id><input>Inspect.</input></codex_delegation>
[04] [1] tool cloud_threads.send_message result: <codex_delegation><source_thread_id><UUID 1></source_thread_...e_orbit_id><input>Inspect.</input></codex_delegation> [hash=d07acc49b7555746]
[05]
[2] tool exec_command call: {"cmd":"exit 0","sandbox_permissions":"require_escalated"}
@@ -166,6 +166,6 @@ Settings: same as window 3
</tools>
02:message/user:
<ENVIRONMENT_CONTEXT>
03:function_call_output/cloud_threads.send_message:<codex_delegation><source_thread_id><UUID 1></source_thread_id><input>Inspect.</input></codex_delegation>
03:function_call_output/cloud_threads.send_message:<codex_delegation><source_thread_id><UUID 1></source_thread_id><source_orbit_id>orbit-source</source_orbit_id><input>Inspect.</input></codex_delegation>
04:function_call/exec_command:{"cmd":"exit 0","sandbox_permissions":"require_escalated"}
05:function_call_output:exec_command failed: CreateProcess { message: "Rejected(\\"This action was rejected due to unacceptable r...e risk. Otherwise, stop and request user input.\\")" } [hash=099c82d6bbb0d161]