Apply workspace routing to Guardian v2 classifier requests (#47162)

## Why

Classifier requests need to preserve their owning thread's workspace routing and account binding, including when reusing pooled connections or retrying after authentication changes.

## What changed

- Share the thread's `WorkspaceRoutingContext` with Guardian v2 and resolve routing before leasing HTTP or pooled WebSocket connections.
- Reuse WebSockets only when their connection key and authentication state still match. Cache HTTP transports by URL and redirect policy, rejecting redirects when routing requires it.
- Stop classification requests and retries when the account owner changes while allowing token refreshes for the same user and workspace.

## Testing

Add regression coverage for routing failures with prewarmed sockets, shared thread routing state, account switches during HTTP and WebSocket requests, same-account token refreshes, and HTTP redirect-policy cache changes.

GitOrigin-RevId: 7b3efcadb4797b6a2de2e28e7506b297b0728d69
This commit is contained in:
acrognale-oai
2026-09-22 02:33:29 +00:00
committed by copyberry
parent b23324986d
commit 30daed37ad
11 changed files with 575 additions and 24 deletions
+3 -1
View File
@@ -1613,6 +1613,8 @@ impl Session {
let mcp_resource_client = Arc::new(McpResourceClient::new(Arc::clone(&mcp_runtime)));
let extension_metrics =
extension_metrics::from_session_telemetry(session_telemetry.clone());
let workspace_routing = thread_extension_data
.get_or_init(|| config.workspace_routing_context());
for contributor in extensions.thread_lifecycle_contributors() {
contributor.on_thread_start(codex_extension_api::ThreadStartInput {
config: config.as_ref(),
@@ -1711,7 +1713,7 @@ impl Session {
.enabled(Feature::ConcurrentReasoningSummaries),
attestation_provider,
config.http_client_factory(),
config.workspace_routing_context(),
workspace_routing.as_ref().clone(),
)
.with_restored_history(matches!(
&initial_history,