use crate::session::HarnessSession;
use base64::Engine;
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
use http::{HeaderMap, HeaderName, HeaderValue};
use percent_encoding::{AsciiSet, CONTROLS, utf8_percent_encode};
use snafu::{ResultExt, Snafu};
use tracing::warn;
#[derive(Debug, Snafu)]
#[snafu(module, visibility(pub(crate)))]
#[non_exhaustive]
pub enum HeaderError {
#[snafu(display("header value is not valid HTTP-header bytes"))]
InvalidValue {
source: http::header::InvalidHeaderValue,
},
}
pub const HOP_BY_HOP_HEADERS: &[&str] = &[
"connection",
"keep-alive",
"proxy-authenticate",
"proxy-authorization",
"te",
"trailers",
"transfer-encoding",
"upgrade",
];
pub const HEADER_PREFIX: &str = "x-tapes-";
pub const X_TAPES_HARNESS_ID: &str = "x-tapes-harness-id";
pub const X_TAPES_HARNESS_SESSION_ID: &str = "x-tapes-harness-session-id";
pub const X_TAPES_HARNESS_VERSION: &str = "x-tapes-harness-version";
pub const X_TAPES_CWD: &str = "x-tapes-cwd";
pub const X_TAPES_SESSION_NAME: &str = "x-tapes-session-name";
pub const X_TAPES_PARENT_HARNESS_SESSION_ID: &str = "x-tapes-parent-harness-session-id";
pub const X_TAPES_HARNESS_METADATA: &str = "x-tapes-harness-metadata";
pub const HARNESS_ID_UNKNOWN: &str = "unknown";
pub const HARNESS_ID_PI: &str = "pi";
pub const HARNESS_ID_CLAUDE: &str = "claude";
pub const HARNESS_ID_CODEX: &str = "codex";
pub const HARNESS_ID_CODEX_APP: &str = "codex-app";
pub const HARNESS_ID_OPENCODE: &str = "opencode";
pub const REQUEST_CAPTURE_CAP: usize = 32 * 1024 * 1024;
pub const X_TAPES_TOTAL_BUDGET: usize = 8 * 1024;
pub const X_TAPES_METADATA_RAW_CAP: usize = 4 * 1024;
pub const X_TAPES_SESSION_NAME_CAP: usize = 256;
const UTF8_VALUE_ESCAPE: &AsciiSet = &CONTROLS.add(b' ').add(b'%').add(b'"').add(b'\\').add(0x7f);
pub const CLAUDE_THREAD_ID_HEADERS: &[&str] = &["x-claude-code-agent-id"];
pub const CODEX_THREAD_ID_HEADER: &str = "thread-id";
pub const CODEX_SESSION_ID_HEADER: &str = "session-id";
pub const CODEX_PARENT_THREAD_ID_HEADER: &str = "x-codex-parent-thread-id";
pub const CODEX_TURN_METADATA_HEADER: &str = "x-codex-turn-metadata";
pub const OPENAI_SUBAGENT_HEADER: &str = "x-openai-subagent";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum HarnessThreadRule {
FirstPresent(&'static [&'static str]),
DivergentPair {
thread: &'static str,
session: &'static str,
},
}
impl HarnessThreadRule {
#[must_use]
pub fn resolve<'h>(&self, headers: &'h HeaderMap) -> Option<&'h str> {
match *self {
Self::FirstPresent(names) => names.iter().find_map(|name| header_str(headers, name)),
Self::DivergentPair { thread, session } => {
let thread_id = header_str(headers, thread)?;
let session_id = header_str(headers, session)?;
(thread_id != session_id).then_some(thread_id)
}
}
}
}
pub const HARNESS_THREAD_ID_RULES: &[HarnessThreadRule] = &[
HarnessThreadRule::FirstPresent(CLAUDE_THREAD_ID_HEADERS),
HarnessThreadRule::DivergentPair {
thread: CODEX_THREAD_ID_HEADER,
session: CODEX_SESSION_ID_HEADER,
},
];
fn header_str<'h>(headers: &'h HeaderMap, name: &str) -> Option<&'h str> {
headers
.get(name)
.and_then(|value| value.to_str().ok())
.filter(|value| !value.is_empty())
}
#[must_use]
pub fn thread_id(headers: &HeaderMap) -> Option<&str> {
HARNESS_THREAD_ID_RULES
.iter()
.find_map(|rule| rule.resolve(headers))
}
#[must_use]
pub fn is_hop_by_hop(name: &str) -> bool {
HOP_BY_HOP_HEADERS
.iter()
.any(|h| h.eq_ignore_ascii_case(name))
}
pub fn inject_session_envelope(
headers: &mut HeaderMap,
session: &impl HarnessSession,
parent_sid: Option<&str>,
) -> Result<(), HeaderError> {
inject_tapes_attribution(headers, TapesAttribution::from_session(session, parent_sid))
}
pub fn inject_unattributed_envelope(headers: &mut HeaderMap) -> Result<(), HeaderError> {
if has_complete_inbound_envelope(headers) {
return Ok(());
}
clear_tapes_headers(headers);
inject_tapes_attribution(headers, TapesAttribution::unknown())
}
pub struct TapesAttribution {
pub harness_id: String,
pub session_id: Option<String>,
pub version: Option<String>,
pub cwd: Option<String>,
pub name: Option<String>,
pub parent_sid: Option<String>,
pub metadata: serde_json::Map<String, serde_json::Value>,
}
impl TapesAttribution {
#[must_use]
pub fn unknown() -> Self {
Self {
harness_id: HARNESS_ID_UNKNOWN.to_owned(),
session_id: None,
version: None,
cwd: None,
name: None,
parent_sid: None,
metadata: serde_json::Map::new(),
}
}
#[must_use]
pub fn codex() -> Self {
Self::codex_with_metadata(serde_json::Map::new())
}
#[must_use]
pub fn codex_with_metadata(metadata: serde_json::Map<String, serde_json::Value>) -> Self {
Self {
harness_id: HARNESS_ID_CODEX.to_owned(),
session_id: None,
version: None,
cwd: None,
name: None,
parent_sid: None,
metadata,
}
}
#[must_use]
pub fn codex_session(
session_id: &str,
cwd: Option<&str>,
cli_version: Option<&str>,
metadata: serde_json::Map<String, serde_json::Value>,
) -> Self {
Self::codex_session_with_parent(session_id, None, cwd, cli_version, metadata)
}
#[must_use]
pub fn codex_session_with_parent(
session_id: &str,
parent_sid: Option<&str>,
cwd: Option<&str>,
cli_version: Option<&str>,
metadata: serde_json::Map<String, serde_json::Value>,
) -> Self {
Self {
harness_id: HARNESS_ID_CODEX.to_owned(),
session_id: Some(session_id.to_owned()),
version: cli_version.map(str::to_owned),
cwd: cwd.map(str::to_owned),
name: None,
parent_sid: parent_sid.map(str::to_owned),
metadata,
}
}
#[must_use]
pub fn from_headers(headers: &HeaderMap) -> Option<Self> {
let harness_id =
envelope_field(headers, X_TAPES_HARNESS_ID).filter(|id| id != HARNESS_ID_UNKNOWN)?;
let session_id = envelope_field(headers, X_TAPES_HARNESS_SESSION_ID)?;
Some(Self {
harness_id,
session_id: Some(session_id),
version: envelope_field(headers, X_TAPES_HARNESS_VERSION),
cwd: None,
name: None,
parent_sid: envelope_field(headers, X_TAPES_PARENT_HARNESS_SESSION_ID),
metadata: serde_json::Map::new(),
})
}
#[must_use]
pub fn from_session(session: &impl HarnessSession, parent_sid: Option<&str>) -> Self {
Self {
harness_id: session.harness_id().to_owned(),
session_id: Some(session.session_id().to_owned()),
version: session.version().map(str::to_owned),
cwd: session.cwd().map(str::to_owned),
name: session.name().map(str::to_owned),
parent_sid: parent_sid.map(str::to_owned),
metadata: session.metadata(),
}
}
}
pub fn inject_tapes_attribution(
headers: &mut HeaderMap,
attribution: TapesAttribution,
) -> Result<(), HeaderError> {
if attribution.harness_id == HARNESS_ID_UNKNOWN {
let value = HeaderValue::from_static(HARNESS_ID_UNKNOWN);
headers.insert(HeaderName::from_static(X_TAPES_HARNESS_ID), value);
return Ok(());
}
let mut budget = X_TAPES_TOTAL_BUDGET;
if let Err(err) = insert_required_ascii(
headers,
X_TAPES_HARNESS_ID,
&attribution.harness_id,
&mut budget,
) {
warn!(
harness_id = %attribution.harness_id,
error = ?err,
"tapes-headers: required X-Tapes-Harness-Id insert failed; falling back to unknown",
);
clear_tapes_headers(headers);
let value = HeaderValue::from_static(HARNESS_ID_UNKNOWN);
headers.insert(HeaderName::from_static(X_TAPES_HARNESS_ID), value);
return Ok(());
}
if let Some(session_id) = attribution.session_id.as_deref() {
try_insert_string(headers, X_TAPES_HARNESS_SESSION_ID, session_id, &mut budget);
}
if let Some(v) = attribution.version.as_deref() {
try_insert_string(headers, X_TAPES_HARNESS_VERSION, v, &mut budget);
}
if let Some(cwd) = attribution.cwd.as_deref() {
let encoded = utf8_percent_encode(cwd, UTF8_VALUE_ESCAPE).to_string();
try_insert_string(headers, X_TAPES_CWD, &encoded, &mut budget);
}
if let Some(name) = attribution.name.as_deref() {
try_insert_session_name(headers, name, &mut budget);
}
if let Some(parent) = attribution.parent_sid.as_deref() {
try_insert_string(
headers,
X_TAPES_PARENT_HARNESS_SESSION_ID,
parent,
&mut budget,
);
}
try_insert_metadata(headers, attribution.metadata, &mut budget);
Ok(())
}
#[must_use]
pub fn has_complete_inbound_envelope(headers: &HeaderMap) -> bool {
TapesAttribution::from_headers(headers).is_some()
}
fn envelope_field(headers: &HeaderMap, name: &str) -> Option<String> {
headers
.get(name)
.and_then(|value| value.to_str().ok())
.map(str::trim)
.filter(|value| !value.is_empty())
.map(str::to_owned)
}
fn clear_tapes_headers(headers: &mut HeaderMap) {
let to_remove: Vec<HeaderName> = headers
.keys()
.filter(|n| n.as_str().to_ascii_lowercase().starts_with(HEADER_PREFIX))
.cloned()
.collect();
for name in to_remove {
headers.remove(&name);
}
}
fn insert_required_ascii(
headers: &mut HeaderMap,
name: &'static str,
value: &str,
budget: &mut usize,
) -> Result<(), HeaderError> {
let val = HeaderValue::from_str(value).context(header_error::InvalidValueSnafu)?;
let cost = name.len() + value.len();
*budget = budget.saturating_sub(cost);
headers.insert(HeaderName::from_static(name), val);
Ok(())
}
fn try_insert_string(headers: &mut HeaderMap, name: &'static str, value: &str, budget: &mut usize) {
let cost = name.len() + value.len();
if cost > *budget {
return;
}
let Ok(val) = HeaderValue::from_str(value) else {
return;
};
*budget -= cost;
headers.insert(HeaderName::from_static(name), val);
}
fn try_insert_session_name(headers: &mut HeaderMap, name: &str, budget: &mut usize) {
let raw = if name.len() > X_TAPES_SESSION_NAME_CAP {
let mut end = X_TAPES_SESSION_NAME_CAP;
while end > 0 && !name.is_char_boundary(end) {
end -= 1;
}
&name[..end]
} else {
name
};
let encoded = utf8_percent_encode(raw, UTF8_VALUE_ESCAPE).to_string();
try_insert_string(headers, X_TAPES_SESSION_NAME, &encoded, budget);
}
fn try_insert_metadata(
headers: &mut HeaderMap,
obj: serde_json::Map<String, serde_json::Value>,
budget: &mut usize,
) {
if obj.is_empty() {
return;
}
let Ok(raw) = serde_json::to_vec(&serde_json::Value::Object(obj)) else {
return;
};
if raw.len() > X_TAPES_METADATA_RAW_CAP {
return;
}
let encoded = URL_SAFE_NO_PAD.encode(&raw);
try_insert_string(headers, X_TAPES_HARNESS_METADATA, &encoded, budget);
}
#[cfg(any(test, feature = "envelope-fixtures"))]
#[path = "envelope_fixtures.rs"]
pub mod fixtures;
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
use http::HeaderValue;
#[test]
fn hop_by_hop_list_matches_rfc7230() {
assert!(
is_hop_by_hop("Connection"),
"case-insensitive match for canonical-cased header"
);
assert!(is_hop_by_hop("transfer-encoding"), "lower-case input");
assert!(is_hop_by_hop("PROXY-AUTHENTICATE"), "upper-case input");
assert!(is_hop_by_hop("Keep-Alive"));
assert!(is_hop_by_hop("TE"));
assert!(is_hop_by_hop("Trailers"));
assert!(is_hop_by_hop("Upgrade"));
assert!(
!is_hop_by_hop("Content-Length"),
"end-to-end header is not hop-by-hop"
);
assert!(!is_hop_by_hop("Content-Type"));
assert!(!is_hop_by_hop("X-Paper-Auth"));
}
#[test]
fn request_capture_cap_matches_the_gateway_contract() {
assert_eq!(REQUEST_CAPTURE_CAP, 32 * 1024 * 1024);
}
#[test]
fn is_hop_by_hop_matches_every_listed_header_in_any_case() {
for name in HOP_BY_HOP_HEADERS {
let upper = name.to_ascii_uppercase();
let title: String = name
.split('-')
.map(|seg| {
let mut c = seg.chars();
match c.next() {
Some(first) => first.to_ascii_uppercase().to_string() + c.as_str(),
None => String::new(),
}
})
.collect::<Vec<_>>()
.join("-");
assert!(is_hop_by_hop(name), "lower-case `{name}` must match");
assert!(is_hop_by_hop(&upper), "upper-case `{upper}` must match");
assert!(is_hop_by_hop(&title), "title-case `{title}` must match");
}
for name in HOP_BY_HOP_HEADERS {
assert_eq!(
*name,
name.to_ascii_lowercase(),
"HOP_BY_HOP_HEADERS entries are listed lower-case",
);
}
}
struct SampleSession {
session_id: String,
cwd: Option<String>,
version: Option<String>,
peer_protocol: Option<i64>,
kind: Option<String>,
entrypoint: Option<String>,
name: Option<String>,
extra: serde_json::Map<String, serde_json::Value>,
}
impl HarnessSession for SampleSession {
fn harness_id(&self) -> &str {
HARNESS_ID_CLAUDE
}
fn session_id(&self) -> &str {
&self.session_id
}
fn version(&self) -> Option<&str> {
self.version.as_deref()
}
fn cwd(&self) -> Option<&str> {
self.cwd.as_deref()
}
fn name(&self) -> Option<&str> {
self.name.as_deref()
}
fn metadata(&self) -> serde_json::Map<String, serde_json::Value> {
let mut metadata = serde_json::Map::new();
if let Some(kind) = &self.kind {
metadata.insert("kind".to_owned(), serde_json::Value::String(kind.clone()));
}
if let Some(entrypoint) = &self.entrypoint {
metadata.insert(
"entrypoint".to_owned(),
serde_json::Value::String(entrypoint.clone()),
);
}
if let Some(pp) = self.peer_protocol {
metadata.insert(
"peerProtocol".to_owned(),
serde_json::Value::Number(pp.into()),
);
}
for (k, v) in &self.extra {
metadata.insert(k.clone(), v.clone());
}
metadata
}
}
fn sample_session() -> SampleSession {
SampleSession {
session_id: "eae77e15-c7d2-4883-b82e-251161f8eeb3".to_owned(),
cwd: Some("/Users/matt/code".to_owned()),
version: Some("2.1.145".to_owned()),
peer_protocol: Some(1),
kind: Some("interactive".to_owned()),
entrypoint: Some("cli".to_owned()),
name: Some("woo-names".to_owned()),
extra: serde_json::Map::new(),
}
}
#[test]
fn unattributed_envelope_is_the_unknown_sentinel_alone() {
let mut headers = HeaderMap::new();
inject_unattributed_envelope(&mut headers).unwrap();
assert_eq!(
headers.get(X_TAPES_HARNESS_ID).unwrap().to_str().unwrap(),
HARNESS_ID_UNKNOWN
);
assert!(!headers.contains_key(X_TAPES_HARNESS_SESSION_ID));
assert!(!headers.contains_key(X_TAPES_CWD));
assert!(!headers.contains_key(X_TAPES_SESSION_NAME));
assert!(!headers.contains_key(X_TAPES_HARNESS_METADATA));
}
#[test]
fn unattributed_envelope_names_no_fork_parent() {
let mut headers = HeaderMap::new();
inject_unattributed_envelope(&mut headers).unwrap();
assert!(!headers.contains_key(X_TAPES_PARENT_HARNESS_SESSION_ID));
}
#[test]
fn inject_tapes_attribution_codex_without_session_id() {
let mut headers = HeaderMap::new();
inject_tapes_attribution(&mut headers, TapesAttribution::codex()).unwrap();
assert_eq!(
headers.get(X_TAPES_HARNESS_ID).unwrap().to_str().unwrap(),
HARNESS_ID_CODEX
);
assert!(!headers.contains_key(X_TAPES_HARNESS_SESSION_ID));
assert!(!headers.contains_key(X_TAPES_HARNESS_METADATA));
}
#[test]
fn inject_tapes_attribution_codex_with_session_metadata() {
let mut headers = HeaderMap::new();
let mut metadata = serde_json::Map::new();
metadata.insert(
"originator".to_owned(),
serde_json::Value::String("codex-tui".to_owned()),
);
inject_tapes_attribution(
&mut headers,
TapesAttribution::codex_session(
"019ecd8e-4281-7353-8a00-09df678443b1",
Some("/Users/matt/code"),
Some("0.139.0"),
metadata,
),
)
.unwrap();
assert_eq!(
headers.get(X_TAPES_HARNESS_ID).unwrap().to_str().unwrap(),
HARNESS_ID_CODEX
);
assert_eq!(
headers
.get(X_TAPES_HARNESS_SESSION_ID)
.unwrap()
.to_str()
.unwrap(),
"019ecd8e-4281-7353-8a00-09df678443b1"
);
assert_eq!(
headers
.get(X_TAPES_HARNESS_VERSION)
.unwrap()
.to_str()
.unwrap(),
"0.139.0"
);
let raw = URL_SAFE_NO_PAD
.decode(
headers
.get(X_TAPES_HARNESS_METADATA)
.unwrap()
.to_str()
.unwrap(),
)
.unwrap();
let json: serde_json::Value = serde_json::from_slice(&raw).unwrap();
assert_eq!(json["originator"], "codex-tui");
}
#[test]
fn codex_constructors_fill_only_the_fields_their_arguments_name() {
let mut metadata = serde_json::Map::new();
metadata.insert("k".to_owned(), serde_json::Value::String("v".to_owned()));
let bare = TapesAttribution::codex_with_metadata(metadata.clone());
assert_eq!(bare.harness_id, HARNESS_ID_CODEX);
assert!(bare.session_id.is_none());
assert!(bare.parent_sid.is_none());
assert_eq!(bare.metadata, metadata);
let keyed = TapesAttribution::codex_session_with_parent(
"sid-1",
Some("parent-sid"),
Some("/tmp/x"),
Some("0.99.0"),
metadata.clone(),
);
assert_eq!(keyed.harness_id, HARNESS_ID_CODEX);
assert_eq!(keyed.session_id.as_deref(), Some("sid-1"));
assert_eq!(keyed.parent_sid.as_deref(), Some("parent-sid"));
assert_eq!(keyed.cwd.as_deref(), Some("/tmp/x"));
assert_eq!(keyed.version.as_deref(), Some("0.99.0"));
assert_eq!(keyed.metadata, metadata);
let plain = TapesAttribution::codex();
assert!(plain.session_id.is_none() && plain.metadata.is_empty());
let sessioned =
TapesAttribution::codex_session("sid-1", Some("/tmp/x"), Some("0.99.0"), metadata);
assert!(sessioned.parent_sid.is_none());
assert_eq!(sessioned.session_id.as_deref(), Some("sid-1"));
}
#[test]
fn unattributed_envelope_preserves_a_complete_inbound_one() {
let mut headers = HeaderMap::new();
headers.insert(X_TAPES_HARNESS_ID, HeaderValue::from_static(HARNESS_ID_PI));
headers.insert(
X_TAPES_HARNESS_SESSION_ID,
HeaderValue::from_static("paper-pi-test-session"),
);
inject_unattributed_envelope(&mut headers).unwrap();
assert_eq!(
headers.get(X_TAPES_HARNESS_ID).unwrap().to_str().unwrap(),
HARNESS_ID_PI
);
assert_eq!(
headers
.get(X_TAPES_HARNESS_SESSION_ID)
.unwrap()
.to_str()
.unwrap(),
"paper-pi-test-session"
);
}
#[test]
fn unattributed_envelope_replaces_a_partial_inbound_one() {
let mut headers = HeaderMap::new();
headers.insert(X_TAPES_HARNESS_ID, HeaderValue::from_static(HARNESS_ID_PI));
inject_unattributed_envelope(&mut headers).unwrap();
assert_eq!(
headers.get(X_TAPES_HARNESS_ID).unwrap().to_str().unwrap(),
HARNESS_ID_UNKNOWN
);
assert!(!headers.contains_key(X_TAPES_HARNESS_SESSION_ID));
}
#[test]
fn unattributed_envelope_clears_an_orphan_session_id() {
let mut headers = HeaderMap::new();
headers.insert(
X_TAPES_HARNESS_SESSION_ID,
HeaderValue::from_static("orphan-pi-session"),
);
inject_unattributed_envelope(&mut headers).unwrap();
assert_eq!(
headers.get(X_TAPES_HARNESS_ID).unwrap().to_str().unwrap(),
HARNESS_ID_UNKNOWN
);
assert!(!headers.contains_key(X_TAPES_HARNESS_SESSION_ID));
}
#[test]
fn session_envelope_is_well_formed() {
let mut headers = HeaderMap::new();
let session = sample_session();
inject_session_envelope(&mut headers, &session, None).unwrap();
assert_eq!(
headers.get(X_TAPES_HARNESS_ID).unwrap().to_str().unwrap(),
HARNESS_ID_CLAUDE
);
assert_eq!(
headers
.get(X_TAPES_HARNESS_SESSION_ID)
.unwrap()
.to_str()
.unwrap(),
session.session_id
);
assert_eq!(
headers
.get(X_TAPES_HARNESS_VERSION)
.unwrap()
.to_str()
.unwrap(),
"2.1.145"
);
assert_eq!(
headers.get(X_TAPES_CWD).unwrap().to_str().unwrap(),
"/Users/matt/code"
);
assert_eq!(
headers.get(X_TAPES_SESSION_NAME).unwrap().to_str().unwrap(),
"woo-names"
);
assert!(!headers.contains_key(X_TAPES_PARENT_HARNESS_SESSION_ID));
let encoded = headers
.get(X_TAPES_HARNESS_METADATA)
.unwrap()
.to_str()
.unwrap();
let raw = URL_SAFE_NO_PAD.decode(encoded).unwrap();
let json: serde_json::Value = serde_json::from_slice(&raw).unwrap();
assert_eq!(json["kind"], "interactive");
assert_eq!(json["entrypoint"], "cli");
assert_eq!(json["peerProtocol"], 1);
}
#[test]
fn session_envelope_attaches_parent_when_present() {
let mut headers = HeaderMap::new();
let session = sample_session();
inject_session_envelope(&mut headers, &session, Some("parent-sid-uuid")).unwrap();
assert_eq!(
headers
.get(X_TAPES_PARENT_HARNESS_SESSION_ID)
.unwrap()
.to_str()
.unwrap(),
"parent-sid-uuid"
);
}
#[test]
fn session_envelope_omits_unset_optionals() {
let mut headers = HeaderMap::new();
let mut session = sample_session();
session.cwd = None;
session.version = None;
session.name = None;
inject_session_envelope(&mut headers, &session, None).unwrap();
assert!(headers.contains_key(X_TAPES_HARNESS_ID));
assert!(headers.contains_key(X_TAPES_HARNESS_SESSION_ID));
assert!(!headers.contains_key(X_TAPES_CWD));
assert!(!headers.contains_key(X_TAPES_HARNESS_VERSION));
assert!(!headers.contains_key(X_TAPES_SESSION_NAME));
}
#[test]
fn session_envelope_percent_encodes_unicode_session_name() {
let mut headers = HeaderMap::new();
let mut session = sample_session();
session.name = Some("name with space \"quotes\" café".to_owned());
inject_session_envelope(&mut headers, &session, None).unwrap();
let v = headers
.get(X_TAPES_SESSION_NAME)
.unwrap()
.to_str()
.expect("encoded header is ASCII");
assert!(v.contains("%20"), "space is percent-encoded: {v}");
assert!(v.contains("%22"), "quote is percent-encoded: {v}");
assert!(
v.contains("%C3%A9"),
"non-ASCII is UTF-8 percent-encoded: {v}"
);
assert!(v.is_ascii(), "encoded value must be pure ASCII");
}
#[test]
fn session_envelope_truncates_session_name_at_utf8_boundary() {
let mut headers = HeaderMap::new();
let mut session = sample_session();
session.name = Some("ก".repeat(100));
inject_session_envelope(&mut headers, &session, None).unwrap();
let v = headers.get(X_TAPES_SESSION_NAME).unwrap().to_str().unwrap();
assert!(v.is_ascii(), "encoded value is ASCII");
assert_eq!(
v.len(),
85 * 9,
"85 codepoints survive truncation (raw=255 ≤ cap=256)",
);
}
#[test]
fn session_envelope_drops_oversize_metadata() {
let mut headers = HeaderMap::new();
let mut session = sample_session();
let huge: String = "x".repeat(5 * 1024);
session
.extra
.insert("hugeKnob".to_owned(), serde_json::Value::String(huge));
inject_session_envelope(&mut headers, &session, None).unwrap();
assert!(headers.contains_key(X_TAPES_HARNESS_ID));
assert!(headers.contains_key(X_TAPES_HARNESS_SESSION_ID));
assert!(headers.contains_key(X_TAPES_CWD));
assert!(
!headers.contains_key(X_TAPES_HARNESS_METADATA),
"metadata blob dropped when raw JSON exceeds 4 KiB cap",
);
}
#[test]
fn session_envelope_metadata_includes_extra_keys() {
let mut headers = HeaderMap::new();
let mut session = sample_session();
session.extra.insert(
"futureKnob".to_owned(),
serde_json::Value::String("preserved".to_owned()),
);
inject_session_envelope(&mut headers, &session, None).unwrap();
let encoded = headers
.get(X_TAPES_HARNESS_METADATA)
.unwrap()
.to_str()
.unwrap();
let raw = URL_SAFE_NO_PAD.decode(encoded).unwrap();
let json: serde_json::Value = serde_json::from_slice(&raw).unwrap();
assert_eq!(json["futureKnob"], "preserved");
assert_eq!(json["kind"], "interactive");
}
#[test]
fn session_envelope_metadata_empty_when_no_blob_fields() {
let mut headers = HeaderMap::new();
let mut session = sample_session();
session.kind = None;
session.entrypoint = None;
session.peer_protocol = None;
session.extra.clear();
inject_session_envelope(&mut headers, &session, None).unwrap();
assert!(!headers.contains_key(X_TAPES_HARNESS_METADATA));
assert!(headers.contains_key(X_TAPES_HARNESS_ID));
assert!(headers.contains_key(X_TAPES_HARNESS_SESSION_ID));
}
#[test]
fn session_envelope_escapes_control_bytes_in_cwd() {
let mut headers = HeaderMap::new();
let mut session = sample_session();
session.cwd = Some("/Users/matt\nwith-injection: yes".to_owned());
inject_session_envelope(&mut headers, &session, None).unwrap();
let v = headers.get(X_TAPES_CWD).unwrap().to_str().unwrap();
assert!(v.contains("%0A"), "newline is percent-encoded: {v}");
assert!(
!v.contains('\n'),
"no raw CR/LF survives into the header value: {v}",
);
assert!(v.is_ascii(), "encoded value must be pure ASCII");
assert!(headers.contains_key(X_TAPES_HARNESS_ID));
}
#[test]
fn session_envelope_percent_encodes_unicode_cwd() {
let mut headers = HeaderMap::new();
let mut session = sample_session();
session.cwd = Some("/Users/松本/code".to_owned());
inject_session_envelope(&mut headers, &session, None).unwrap();
let v = headers.get(X_TAPES_CWD).unwrap().to_str().unwrap();
assert!(v.is_ascii(), "encoded value must be pure ASCII");
assert!(v.contains("%E6%9D%BE"), "first codepoint encoded: {v}");
assert!(v.contains("%E6%9C%AC"), "second codepoint encoded: {v}");
assert!(v.starts_with("/Users/"), "ASCII prefix preserved: {v}");
assert!(v.ends_with("/code"), "ASCII suffix preserved: {v}");
}
#[test]
fn session_name_truncation_lands_at_or_below_cap_for_each_byte_offset() {
for raw_len in [254usize, 255, 256, 257, 258, 299, 300, 301] {
let ascii = "a".repeat(raw_len);
let mut h = HeaderMap::new();
let mut budget = X_TAPES_TOTAL_BUDGET;
try_insert_session_name(&mut h, &ascii, &mut budget);
let v = h.get(X_TAPES_SESSION_NAME).unwrap().to_str().unwrap();
assert!(v.is_ascii(), "ascii input @ {raw_len} yields ASCII");
let expected = raw_len.min(X_TAPES_SESSION_NAME_CAP);
assert_eq!(
v.len(),
expected,
"ascii input @ {raw_len}: encoded length must equal min(raw, cap)",
);
}
for n_copies in [84usize, 85, 86, 87, 100] {
let s = "ก".repeat(n_copies);
let raw_len = s.len();
assert_eq!(raw_len, n_copies * 3, "ก is 3 raw UTF-8 bytes");
let mut h = HeaderMap::new();
let mut budget = X_TAPES_TOTAL_BUDGET;
try_insert_session_name(&mut h, &s, &mut budget);
let v = h.get(X_TAPES_SESSION_NAME).unwrap().to_str().unwrap();
assert!(v.is_ascii(), "utf8 input @ {raw_len} yields ASCII");
let kept = if raw_len <= X_TAPES_SESSION_NAME_CAP {
n_copies
} else {
X_TAPES_SESSION_NAME_CAP / 3
};
assert_eq!(
v.len(),
kept * 9,
"utf8 input ({n_copies} × ก, raw={raw_len}): \
encoded length matches kept codepoints",
);
}
}
#[test]
fn clear_tapes_headers_removes_all_envelope_headers() {
let mut headers = HeaderMap::new();
headers.insert(
HeaderName::from_static(X_TAPES_HARNESS_ID),
HeaderValue::from_static("claude"),
);
headers.insert(
HeaderName::from_static(X_TAPES_HARNESS_SESSION_ID),
HeaderValue::from_static("sid"),
);
headers.insert(
HeaderName::from_static(X_TAPES_CWD),
HeaderValue::from_static("/tmp"),
);
headers.insert(
HeaderName::from_static(X_TAPES_HARNESS_METADATA),
HeaderValue::from_static("payload"),
);
headers.insert("authorization", HeaderValue::from_static("Bearer foo"));
clear_tapes_headers(&mut headers);
assert!(!headers.contains_key(X_TAPES_HARNESS_ID));
assert!(!headers.contains_key(X_TAPES_HARNESS_SESSION_ID));
assert!(!headers.contains_key(X_TAPES_CWD));
assert!(!headers.contains_key(X_TAPES_HARNESS_METADATA));
assert!(
headers.contains_key("authorization"),
"non-tapes headers are preserved"
);
}
const CODEX_ROOT: &str = "019f863d-0cd6-7ce2-b481-20abd683a14e";
const CODEX_CHILD: &str = "019f8713-2213-75e3-be33-36fd2f8dd384";
const CLAUDE_AGENT: &str = "agent-0a1b2c3d";
fn headers_from(pairs: &[(&'static str, &str)]) -> HeaderMap {
let mut headers = HeaderMap::new();
for (name, value) in pairs {
headers.insert(
HeaderName::from_static(name),
HeaderValue::from_str(value).unwrap(),
);
}
headers
}
#[test]
fn thread_id_reads_the_claude_subagent_header() {
let headers = headers_from(&[("x-claude-code-agent-id", CLAUDE_AGENT)]);
assert_eq!(thread_id(&headers), Some(CLAUDE_AGENT));
}
#[test]
fn thread_id_is_absent_on_a_main_thread_call() {
let headers = headers_from(&[("content-type", "application/json")]);
assert_eq!(thread_id(&headers), None);
}
#[test]
fn a_blank_thread_id_counts_as_absent() {
let headers = headers_from(&[("x-claude-code-agent-id", "")]);
assert_eq!(thread_id(&headers), None);
}
#[test]
fn a_codex_child_turn_resolves_to_its_thread_id() {
let headers = headers_from(&[("session-id", CODEX_ROOT), ("thread-id", CODEX_CHILD)]);
assert_eq!(thread_id(&headers), Some(CODEX_CHILD));
}
#[test]
fn a_codex_root_turn_has_no_thread_id() {
let headers = headers_from(&[("session-id", CODEX_ROOT), ("thread-id", CODEX_ROOT)]);
assert_eq!(thread_id(&headers), None);
}
#[test]
fn a_codex_session_id_alone_is_a_main_thread_call() {
let headers = headers_from(&[("session-id", CODEX_ROOT)]);
assert_eq!(thread_id(&headers), None);
}
#[test]
fn a_lone_thread_id_is_not_a_codex_shape() {
let headers = headers_from(&[("thread-id", CODEX_CHILD)]);
assert_eq!(thread_id(&headers), None);
}
#[test]
fn the_claude_rule_wins_over_a_codex_shaped_pair() {
let headers = headers_from(&[
("x-claude-code-agent-id", CLAUDE_AGENT),
("session-id", CODEX_ROOT),
("thread-id", CODEX_CHILD),
]);
assert_eq!(thread_id(&headers), Some(CLAUDE_AGENT));
}
#[test]
fn from_headers_reads_a_complete_inbound_envelope() {
let headers = headers_from(&[
(X_TAPES_HARNESS_ID, HARNESS_ID_PI),
(X_TAPES_HARNESS_SESSION_ID, "sess-1"),
(X_TAPES_HARNESS_VERSION, "1.2.3"),
(X_TAPES_PARENT_HARNESS_SESSION_ID, "sess-0"),
(X_TAPES_CWD, "%2Ftmp%2Fwork"),
(X_TAPES_HARNESS_METADATA, "e30"),
]);
let attribution = TapesAttribution::from_headers(&headers).expect("envelope is complete");
assert_eq!(attribution.harness_id, HARNESS_ID_PI);
assert_eq!(attribution.session_id.as_deref(), Some("sess-1"));
assert_eq!(attribution.version.as_deref(), Some("1.2.3"));
assert_eq!(attribution.parent_sid.as_deref(), Some("sess-0"));
assert_eq!(attribution.cwd, None);
assert_eq!(attribution.name, None);
assert!(attribution.metadata.is_empty());
}
#[test]
fn from_headers_rejects_incomplete_envelopes() {
let cases: &[(&str, Vec<(&'static str, &str)>)] = &[
("no headers at all", vec![]),
(
"harness id but no session id",
vec![(X_TAPES_HARNESS_ID, HARNESS_ID_PI)],
),
(
"session id but no harness id",
vec![(X_TAPES_HARNESS_SESSION_ID, "sess-1")],
),
(
"the unknown sentinel is not an identity",
vec![
(X_TAPES_HARNESS_ID, HARNESS_ID_UNKNOWN),
(X_TAPES_HARNESS_SESSION_ID, "sess-1"),
],
),
(
"a blank harness id",
vec![
(X_TAPES_HARNESS_ID, " "),
(X_TAPES_HARNESS_SESSION_ID, "sess-1"),
],
),
(
"a blank session id",
vec![
(X_TAPES_HARNESS_ID, HARNESS_ID_PI),
(X_TAPES_HARNESS_SESSION_ID, ""),
],
),
];
for (why, pairs) in cases {
let headers = headers_from(pairs);
assert!(
TapesAttribution::from_headers(&headers).is_none(),
"{why}: an incomplete envelope must not read back as an identity",
);
}
}
#[test]
fn from_headers_trims_envelope_values() {
let headers = headers_from(&[
(X_TAPES_HARNESS_ID, " pi "),
(X_TAPES_HARNESS_SESSION_ID, " sess-1 "),
]);
let attribution = TapesAttribution::from_headers(&headers).expect("padding is not absence");
assert_eq!(attribution.harness_id, HARNESS_ID_PI);
assert_eq!(attribution.session_id.as_deref(), Some("sess-1"));
}
#[test]
fn envelope_preservation_and_readback_apply_one_rule() {
let cases: &[Vec<(&'static str, &str)>] = &[
vec![],
vec![(X_TAPES_HARNESS_ID, HARNESS_ID_PI)],
vec![(X_TAPES_HARNESS_SESSION_ID, "sess-1")],
vec![
(X_TAPES_HARNESS_ID, HARNESS_ID_UNKNOWN),
(X_TAPES_HARNESS_SESSION_ID, "sess-1"),
],
vec![
(X_TAPES_HARNESS_ID, HARNESS_ID_PI),
(X_TAPES_HARNESS_SESSION_ID, ""),
],
vec![
(X_TAPES_HARNESS_ID, HARNESS_ID_PI),
(X_TAPES_HARNESS_SESSION_ID, "sess-1"),
],
];
for pairs in cases {
let mut headers = headers_from(pairs);
let readable = TapesAttribution::from_headers(&headers).is_some();
assert_eq!(
has_complete_inbound_envelope(&headers),
readable,
"the predicate and the reader disagree about {pairs:?}",
);
let before = headers.clone();
inject_unattributed_envelope(&mut headers).unwrap();
if readable {
assert_eq!(
headers.get(X_TAPES_HARNESS_ID),
before.get(X_TAPES_HARNESS_ID),
"a complete envelope was overwritten: {pairs:?}",
);
} else {
assert_eq!(
headers.get(X_TAPES_HARNESS_ID).unwrap(),
HARNESS_ID_UNKNOWN,
"an incomplete envelope was not replaced with the sentinel: {pairs:?}",
);
}
}
}
#[test]
fn exactly_one_rule_is_a_divergent_pair() {
let pairs: Vec<(&str, &str)> = HARNESS_THREAD_ID_RULES
.iter()
.filter_map(|rule| match rule {
HarnessThreadRule::DivergentPair { thread, session } => Some((*thread, *session)),
HarnessThreadRule::FirstPresent(_) => None,
})
.collect();
assert_eq!(
pairs,
vec![(CODEX_THREAD_ID_HEADER, CODEX_SESSION_ID_HEADER)],
);
}
}