mirror of
https://github.com/openai/codex.git
synced 2026-10-18 17:18:52 +00:00
Add per-session code-mode execution limits (#37114)
## What changed - Add `create_session_with_limits` and session-scoped cell execution limits. - Clamp execute and wait yield times to the session's `max_yield_time_ms` without terminating the running cell. - Negotiate support with remote code-mode hosts and include non-default limits in `session/open`, while keeping unlimited sessions compatible with hosts and providers that do not support limits. ## Testing - Cover yield-time clamping, zero-timeout behavior, and isolation between sessions. - Cover wire serialization, capability negotiation, unsupported hosts, and shared process-host execution. GitOrigin-RevId: 9517321cd605bb87f93eeaa6ba331cc2e346e582
This commit is contained in:
@@ -8,6 +8,7 @@ use std::sync::atomic::Ordering;
|
||||
|
||||
use codex_code_mode_protocol::CellId;
|
||||
use codex_code_mode_protocol::CodeModeSession;
|
||||
use codex_code_mode_protocol::CodeModeSessionCellExecutionLimits;
|
||||
use codex_code_mode_protocol::CodeModeSessionDelegate;
|
||||
use codex_code_mode_protocol::CodeModeSessionProvider;
|
||||
use codex_code_mode_protocol::CodeModeSessionProviderFuture;
|
||||
@@ -85,7 +86,15 @@ impl CodeModeSessionProvider for ProcessOwnedCodeModeSessionProvider {
|
||||
&'a self,
|
||||
delegate: Arc<dyn CodeModeSessionDelegate>,
|
||||
) -> CodeModeSessionProviderFuture<'a> {
|
||||
Box::pin(create_host_session(delegate, self.process_host()))
|
||||
self.create_session_with_limits(delegate, CodeModeSessionCellExecutionLimits::default())
|
||||
}
|
||||
|
||||
fn create_session_with_limits<'a>(
|
||||
&'a self,
|
||||
delegate: Arc<dyn CodeModeSessionDelegate>,
|
||||
limits: CodeModeSessionCellExecutionLimits,
|
||||
) -> CodeModeSessionProviderFuture<'a> {
|
||||
Box::pin(create_host_session(delegate, self.process_host(), limits))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +109,14 @@ impl CodeModeSessionProvider for DisabledCodeModeSessionProvider {
|
||||
) -> CodeModeSessionProviderFuture<'a> {
|
||||
Box::pin(async { Err("code-mode host is disabled".to_string()) })
|
||||
}
|
||||
|
||||
fn create_session_with_limits<'a>(
|
||||
&'a self,
|
||||
delegate: Arc<dyn CodeModeSessionDelegate>,
|
||||
_limits: CodeModeSessionCellExecutionLimits,
|
||||
) -> CodeModeSessionProviderFuture<'a> {
|
||||
self.create_session(delegate)
|
||||
}
|
||||
}
|
||||
|
||||
impl WebSocketCodeModeSessionProvider {
|
||||
@@ -129,15 +146,28 @@ impl CodeModeSessionProvider for WebSocketCodeModeSessionProvider {
|
||||
&'a self,
|
||||
delegate: Arc<dyn CodeModeSessionDelegate>,
|
||||
) -> CodeModeSessionProviderFuture<'a> {
|
||||
Box::pin(create_host_session(delegate, Arc::clone(&self.host)))
|
||||
self.create_session_with_limits(delegate, CodeModeSessionCellExecutionLimits::default())
|
||||
}
|
||||
|
||||
fn create_session_with_limits<'a>(
|
||||
&'a self,
|
||||
delegate: Arc<dyn CodeModeSessionDelegate>,
|
||||
limits: CodeModeSessionCellExecutionLimits,
|
||||
) -> CodeModeSessionProviderFuture<'a> {
|
||||
Box::pin(create_host_session(
|
||||
delegate,
|
||||
Arc::clone(&self.host),
|
||||
limits,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
async fn create_host_session(
|
||||
delegate: Arc<dyn CodeModeSessionDelegate>,
|
||||
host: Arc<OwnedCodeModeHost>,
|
||||
limits: CodeModeSessionCellExecutionLimits,
|
||||
) -> Result<Arc<dyn CodeModeSession>, String> {
|
||||
let session = ProcessOwnedCodeModeSession::with_host(delegate, host);
|
||||
let session = ProcessOwnedCodeModeSession::with_host(delegate, host, limits);
|
||||
session.connection().await?;
|
||||
Ok(Arc::new(session))
|
||||
}
|
||||
@@ -244,6 +274,7 @@ struct SessionBinding {
|
||||
struct SessionInner {
|
||||
host: Arc<OwnedCodeModeHost>,
|
||||
delegate: Arc<dyn CodeModeSessionDelegate>,
|
||||
limits: CodeModeSessionCellExecutionLimits,
|
||||
state: StdMutex<SessionState>,
|
||||
next_generation: AtomicU64,
|
||||
shutdown_requested: AtomicBool,
|
||||
@@ -263,14 +294,20 @@ impl ProcessOwnedCodeModeSession {
|
||||
Arc::new(OwnedCodeModeHost::new(
|
||||
InstallContext::current().code_mode_host_program(),
|
||||
)),
|
||||
CodeModeSessionCellExecutionLimits::default(),
|
||||
)
|
||||
}
|
||||
|
||||
fn with_host(delegate: Arc<dyn CodeModeSessionDelegate>, host: Arc<OwnedCodeModeHost>) -> Self {
|
||||
fn with_host(
|
||||
delegate: Arc<dyn CodeModeSessionDelegate>,
|
||||
host: Arc<OwnedCodeModeHost>,
|
||||
limits: CodeModeSessionCellExecutionLimits,
|
||||
) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(SessionInner {
|
||||
host,
|
||||
delegate,
|
||||
limits,
|
||||
state: StdMutex::new(SessionState::New),
|
||||
next_generation: AtomicU64::new(1),
|
||||
shutdown_requested: AtomicBool::new(false),
|
||||
@@ -361,7 +398,11 @@ impl SessionInner {
|
||||
let result = match self.host.connection().await {
|
||||
Ok(connection) => {
|
||||
let cleanup = connection
|
||||
.open_session(remote.clone(), Arc::clone(&self.delegate))
|
||||
.open_session(
|
||||
remote.clone(),
|
||||
Arc::clone(&self.delegate),
|
||||
self.limits.clone(),
|
||||
)
|
||||
.await;
|
||||
cleanup.map(|cleanup| SessionBinding {
|
||||
connection,
|
||||
|
||||
@@ -10,6 +10,7 @@ use std::sync::atomic::Ordering;
|
||||
use std::time::Duration;
|
||||
|
||||
use codex_code_mode_protocol::CellId;
|
||||
use codex_code_mode_protocol::CodeModeSessionCellExecutionLimits;
|
||||
use codex_code_mode_protocol::CodeModeSessionDelegate;
|
||||
use codex_code_mode_protocol::ExecuteRequest;
|
||||
use codex_code_mode_protocol::StartedCell;
|
||||
@@ -28,6 +29,7 @@ use codex_code_mode_protocol::host::MAX_FRAME_BYTES;
|
||||
use codex_code_mode_protocol::host::MAX_PENDING_DELEGATE_CALLS;
|
||||
use codex_code_mode_protocol::host::ProtocolVersion;
|
||||
use codex_code_mode_protocol::host::RequestId;
|
||||
use codex_code_mode_protocol::host::SESSION_RESOURCE_LIMITS_CAPABILITY;
|
||||
use codex_code_mode_protocol::host::SupportedProtocolVersions;
|
||||
use codex_code_mode_protocol::host::TransportLane;
|
||||
use codex_http_client::HttpClientFactory;
|
||||
@@ -120,6 +122,7 @@ pub(super) struct Connection {
|
||||
alive: Arc<AtomicBool>,
|
||||
failure: Arc<std::sync::Mutex<Option<String>>>,
|
||||
cancellation: CancellationToken,
|
||||
capabilities: CapabilitySet,
|
||||
}
|
||||
|
||||
struct CallerCancellation {
|
||||
@@ -285,11 +288,14 @@ impl Connection {
|
||||
let handshake = async {
|
||||
let dual_capability =
|
||||
Capability::new(DUAL_WEBSOCKET_CAPABILITY).map_err(|error| error.to_string())?;
|
||||
let session_limits_capability = Capability::new(SESSION_RESOURCE_LIMITS_CAPABILITY)
|
||||
.map_err(|error| error.to_string())?;
|
||||
let optional_capabilities = if bulk_connection_options.is_some() {
|
||||
CapabilitySet::try_new([dual_capability.clone()])
|
||||
CapabilitySet::try_new([dual_capability.clone(), session_limits_capability])
|
||||
.map_err(|error| error.to_string())?
|
||||
} else {
|
||||
CapabilitySet::empty()
|
||||
CapabilitySet::try_new([session_limits_capability])
|
||||
.map_err(|error| error.to_string())?
|
||||
};
|
||||
let hello = ClientHello::new(
|
||||
SupportedProtocolVersions::try_new([ProtocolVersion::V1])
|
||||
@@ -310,7 +316,8 @@ impl Connection {
|
||||
Some(HostToClient::HostHello(hello))
|
||||
if hello.selected_version() == ProtocolVersion::V1 =>
|
||||
{
|
||||
if hello.capabilities().contains(&dual_capability) {
|
||||
let capabilities = hello.capabilities().clone();
|
||||
let bulk_token = if capabilities.contains(&dual_capability) {
|
||||
hello
|
||||
.bulk_connection_token()
|
||||
.map(str::to_string)
|
||||
@@ -318,12 +325,15 @@ impl Connection {
|
||||
"code-mode host advertised dual websockets without a pairing token"
|
||||
.to_string()
|
||||
})
|
||||
.map(Some)
|
||||
.map(Some)?
|
||||
} else if hello.bulk_connection_token().is_some() {
|
||||
Err("code-mode host returned an unexpected bulk pairing token".to_string())
|
||||
return Err(
|
||||
"code-mode host returned an unexpected bulk pairing token".to_string()
|
||||
);
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
None
|
||||
};
|
||||
Ok((capabilities, bulk_token))
|
||||
}
|
||||
Some(HostToClient::HandshakeRejected { reason }) => {
|
||||
Err(format!("code-mode host rejected the handshake: {reason:?}"))
|
||||
@@ -344,8 +354,8 @@ impl Connection {
|
||||
));
|
||||
}
|
||||
};
|
||||
let bulk_token = match handshake_result {
|
||||
Ok(token) => token,
|
||||
let (capabilities, bulk_token) = match handshake_result {
|
||||
Ok(negotiated) => negotiated,
|
||||
Err(err) => {
|
||||
let _ = writer.close().await;
|
||||
owner.close().await;
|
||||
@@ -460,6 +470,7 @@ impl Connection {
|
||||
alive,
|
||||
failure,
|
||||
cancellation,
|
||||
capabilities,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -478,13 +489,25 @@ impl Connection {
|
||||
&self,
|
||||
session: RemoteSession,
|
||||
delegate: Arc<dyn CodeModeSessionDelegate>,
|
||||
limits: CodeModeSessionCellExecutionLimits,
|
||||
) -> Result<SessionCleanup, String> {
|
||||
if limits != CodeModeSessionCellExecutionLimits::default()
|
||||
&& !self
|
||||
.capabilities
|
||||
.iter()
|
||||
.any(|capability| capability.as_str() == SESSION_RESOURCE_LIMITS_CAPABILITY)
|
||||
{
|
||||
return Err(format!(
|
||||
"code-mode host does not support session resource limits: missing `{SESSION_RESOURCE_LIMITS_CAPABILITY}` capability"
|
||||
));
|
||||
}
|
||||
let cleanup = SessionCleanup::new();
|
||||
let cancellation = CallerCancellation::new();
|
||||
let (response_tx, response_rx) = oneshot::channel();
|
||||
self.send(DriverCommand::OpenSession {
|
||||
session,
|
||||
delegate,
|
||||
limits,
|
||||
cleanup: cleanup.clone(),
|
||||
caller_cancellation: cancellation.token(),
|
||||
response_tx,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use codex_code_mode_protocol::CellId;
|
||||
use codex_code_mode_protocol::CodeModeSessionCellExecutionLimits;
|
||||
use codex_code_mode_protocol::CodeModeSessionDelegate;
|
||||
use codex_code_mode_protocol::ExecuteRequest;
|
||||
use codex_code_mode_protocol::WaitOutcome;
|
||||
@@ -8,6 +9,7 @@ use codex_code_mode_protocol::WaitRequest;
|
||||
use codex_code_mode_protocol::host::ClientToHost;
|
||||
use codex_code_mode_protocol::host::EncodedFrame;
|
||||
use codex_code_mode_protocol::host::HostRequest;
|
||||
use codex_code_mode_protocol::host::WireSessionCellExecutionLimits;
|
||||
use codex_code_mode_protocol::host::WireWaitRequest;
|
||||
use tokio::sync::oneshot;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
@@ -28,10 +30,18 @@ impl ConnectionDriver {
|
||||
DriverCommand::OpenSession {
|
||||
session,
|
||||
delegate,
|
||||
limits,
|
||||
cleanup,
|
||||
caller_cancellation,
|
||||
response_tx,
|
||||
} => self.open_session(session, delegate, cleanup, caller_cancellation, response_tx),
|
||||
} => self.open_session(
|
||||
session,
|
||||
delegate,
|
||||
limits,
|
||||
cleanup,
|
||||
caller_cancellation,
|
||||
response_tx,
|
||||
),
|
||||
DriverCommand::Execute {
|
||||
session,
|
||||
request,
|
||||
@@ -60,6 +70,7 @@ impl ConnectionDriver {
|
||||
&mut self,
|
||||
session: RemoteSession,
|
||||
delegate: Arc<dyn CodeModeSessionDelegate>,
|
||||
limits: CodeModeSessionCellExecutionLimits,
|
||||
cleanup: super::cleanup::SessionCleanup,
|
||||
caller_cancellation: CancellationToken,
|
||||
response_tx: oneshot::Sender<Result<(), String>>,
|
||||
@@ -71,6 +82,15 @@ impl ConnectionDriver {
|
||||
)));
|
||||
return true;
|
||||
}
|
||||
let limits = match WireSessionCellExecutionLimits::try_from(limits) {
|
||||
Ok(limits) => limits,
|
||||
Err(error) => {
|
||||
let _ = response_tx.send(Err(format!(
|
||||
"failed to encode code-mode session execution limits: {error}"
|
||||
)));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
let request_id = match self.requests.allocate_id() {
|
||||
Ok(id) => id,
|
||||
Err(err) => {
|
||||
@@ -82,6 +102,8 @@ impl ConnectionDriver {
|
||||
id: request_id,
|
||||
request: HostRequest::OpenSession {
|
||||
session_id: session.id.clone(),
|
||||
cell_execution_limits: (limits != WireSessionCellExecutionLimits::default())
|
||||
.then_some(limits),
|
||||
},
|
||||
};
|
||||
let frame = match EncodedFrame::encode(&message) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use codex_code_mode_protocol::CellId;
|
||||
use codex_code_mode_protocol::CodeModeSessionCellExecutionLimits;
|
||||
use codex_code_mode_protocol::CodeModeSessionDelegate;
|
||||
use codex_code_mode_protocol::ExecuteRequest;
|
||||
use codex_code_mode_protocol::RuntimeResponse;
|
||||
@@ -30,6 +31,7 @@ pub(in crate::remote_session::connection) enum DriverCommand {
|
||||
OpenSession {
|
||||
session: RemoteSession,
|
||||
delegate: Arc<dyn CodeModeSessionDelegate>,
|
||||
limits: CodeModeSessionCellExecutionLimits,
|
||||
cleanup: SessionCleanup,
|
||||
caller_cancellation: CancellationToken,
|
||||
response_tx: oneshot::Sender<Result<(), String>>,
|
||||
|
||||
@@ -7,16 +7,19 @@ use std::time::Duration;
|
||||
|
||||
use codex_code_mode_protocol::CellId;
|
||||
use codex_code_mode_protocol::CodeModeNestedToolCall;
|
||||
use codex_code_mode_protocol::CodeModeSessionCellExecutionLimits;
|
||||
use codex_code_mode_protocol::CodeModeSessionDelegate;
|
||||
use codex_code_mode_protocol::ExecuteRequest;
|
||||
use codex_code_mode_protocol::NotificationFuture;
|
||||
use codex_code_mode_protocol::ToolInvocationFuture;
|
||||
use codex_code_mode_protocol::WaitRequest;
|
||||
use codex_code_mode_protocol::host::CapabilitySet;
|
||||
use codex_code_mode_protocol::host::ClientToHost;
|
||||
use codex_code_mode_protocol::host::DelegateRequest;
|
||||
use codex_code_mode_protocol::host::DelegateRequestId;
|
||||
use codex_code_mode_protocol::host::DelegateResponse;
|
||||
use codex_code_mode_protocol::host::EncodedFrame;
|
||||
use codex_code_mode_protocol::host::HostRequest;
|
||||
use codex_code_mode_protocol::host::HostResponse;
|
||||
use codex_code_mode_protocol::host::HostToClient;
|
||||
use codex_code_mode_protocol::host::MAX_PENDING_DELEGATE_CALLS;
|
||||
@@ -25,6 +28,7 @@ use codex_code_mode_protocol::host::SessionId;
|
||||
use codex_code_mode_protocol::host::WireNestedToolCall;
|
||||
use codex_code_mode_protocol::host::WireResult;
|
||||
use codex_code_mode_protocol::host::WireRuntimeResponse;
|
||||
use codex_code_mode_protocol::host::WireSessionCellExecutionLimits;
|
||||
use codex_code_mode_protocol::host::WireWaitOutcome;
|
||||
use codex_protocol::ToolName;
|
||||
use pretty_assertions::assert_eq;
|
||||
@@ -95,6 +99,7 @@ impl DriverHarness {
|
||||
.send(DriverCommand::OpenSession {
|
||||
session: session.clone(),
|
||||
delegate,
|
||||
limits: Default::default(),
|
||||
cleanup: cleanup.clone(),
|
||||
caller_cancellation: CancellationToken::new(),
|
||||
response_tx,
|
||||
@@ -190,6 +195,50 @@ impl Drop for DriverHarness {
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn open_session_includes_nondefault_cell_execution_limits() {
|
||||
let mut harness = DriverHarness::start();
|
||||
let session = remote_session();
|
||||
let limits = CodeModeSessionCellExecutionLimits {
|
||||
max_yield_time_ms: Some(250),
|
||||
max_heap_size_bytes: Some(16 * 1024 * 1024),
|
||||
};
|
||||
let (response_tx, _response_rx) = oneshot::channel();
|
||||
|
||||
harness
|
||||
.command_tx
|
||||
.send(DriverCommand::OpenSession {
|
||||
session: session.clone(),
|
||||
delegate: Arc::new(RecordingDelegate::default()),
|
||||
limits,
|
||||
cleanup: SessionCleanup::new(),
|
||||
caller_cancellation: CancellationToken::new(),
|
||||
response_tx,
|
||||
})
|
||||
.await
|
||||
.expect("limited session open command");
|
||||
let frame = harness
|
||||
.outgoing_rx
|
||||
.recv()
|
||||
.await
|
||||
.expect("limited session open frame");
|
||||
|
||||
assert_eq!(
|
||||
EncodedFrame::decode_framed::<ClientToHost>(&frame.into_framed_bytes())
|
||||
.expect("decode limited session open request"),
|
||||
ClientToHost::Request {
|
||||
id: RequestId::new(/*value*/ 1),
|
||||
request: HostRequest::OpenSession {
|
||||
session_id: session.id,
|
||||
cell_execution_limits: Some(WireSessionCellExecutionLimits {
|
||||
max_yield_time_ms: Some(250),
|
||||
max_heap_size_bytes: Some(16 * 1024 * 1024),
|
||||
}),
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct RecordingDelegate {
|
||||
closed_cells: StdMutex<Vec<CellId>>,
|
||||
@@ -518,6 +567,7 @@ async fn dropped_open_waiter_shuts_down_committed_session() {
|
||||
.send(DriverCommand::OpenSession {
|
||||
session: session.clone(),
|
||||
delegate: Arc::new(RecordingDelegate::default()),
|
||||
limits: Default::default(),
|
||||
cleanup,
|
||||
caller_cancellation: CancellationToken::new(),
|
||||
response_tx: open_tx,
|
||||
@@ -1352,6 +1402,7 @@ async fn queued_remote_wait_times_out_and_invalidates_the_connection() {
|
||||
alive: Arc::clone(&harness.alive),
|
||||
failure: Arc::clone(&harness.failure),
|
||||
cancellation: harness.cancellation.clone(),
|
||||
capabilities: CapabilitySet::empty(),
|
||||
};
|
||||
let response = tokio::spawn(async move {
|
||||
let result = connection
|
||||
@@ -1397,6 +1448,7 @@ async fn queued_remote_termination_times_out_and_invalidates_the_connection() {
|
||||
alive: Arc::clone(&harness.alive),
|
||||
failure: Arc::clone(&harness.failure),
|
||||
cancellation: harness.cancellation.clone(),
|
||||
capabilities: CapabilitySet::empty(),
|
||||
};
|
||||
let response = tokio::spawn(async move {
|
||||
let result = connection
|
||||
|
||||
@@ -3,6 +3,7 @@ use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use codex_code_mode_protocol::CodeModeSessionCellExecutionLimits;
|
||||
use codex_code_mode_protocol::CodeModeSessionProvider;
|
||||
use codex_code_mode_protocol::ExecuteRequest;
|
||||
use codex_code_mode_protocol::FunctionCallOutputContentItem;
|
||||
@@ -17,6 +18,7 @@ use codex_code_mode_protocol::host::HostRequest;
|
||||
use codex_code_mode_protocol::host::HostResponse;
|
||||
use codex_code_mode_protocol::host::HostToClient;
|
||||
use codex_code_mode_protocol::host::ProtocolVersion;
|
||||
use codex_code_mode_protocol::host::SESSION_RESOURCE_LIMITS_CAPABILITY;
|
||||
use codex_code_mode_protocol::host::WireCellId;
|
||||
use codex_code_mode_protocol::host::WireContentItem;
|
||||
use codex_code_mode_protocol::host::WireResult;
|
||||
@@ -117,13 +119,19 @@ async fn websocket_provider_executes_over_shared_connector() {
|
||||
let request = EncodedFrame::decode_framed::<ClientToHost>(&frame)
|
||||
.expect("websocket test host should decode a framed protocol message");
|
||||
let responses = match request {
|
||||
ClientToHost::ClientHello(_) => vec![HostToClient::HostHello(HostHello::new(
|
||||
ProtocolVersion::V1,
|
||||
CapabilitySet::empty(),
|
||||
))],
|
||||
ClientToHost::ClientHello(hello) => {
|
||||
let capability = Capability::new(SESSION_RESOURCE_LIMITS_CAPABILITY)
|
||||
.expect("session-limit capability");
|
||||
assert!(hello.optional_capabilities().contains(&capability));
|
||||
assert_eq!(hello.required_capabilities(), &CapabilitySet::empty());
|
||||
vec![HostToClient::HostHello(HostHello::new(
|
||||
ProtocolVersion::V1,
|
||||
CapabilitySet::empty(),
|
||||
))]
|
||||
}
|
||||
ClientToHost::Request {
|
||||
id,
|
||||
request: HostRequest::OpenSession { session_id },
|
||||
request: HostRequest::OpenSession { session_id, .. },
|
||||
} => vec![HostToClient::Response {
|
||||
id,
|
||||
result: WireResult::Ok {
|
||||
@@ -192,6 +200,32 @@ async fn websocket_provider_executes_over_shared_connector() {
|
||||
.create_session(Arc::new(NoopCodeModeSessionDelegate))
|
||||
.await
|
||||
.expect("shared websocket connector should open a code-mode session");
|
||||
let error = provider
|
||||
.create_session_with_limits(
|
||||
Arc::new(NoopCodeModeSessionDelegate),
|
||||
CodeModeSessionCellExecutionLimits {
|
||||
max_yield_time_ms: Some(250),
|
||||
max_heap_size_bytes: None,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.err()
|
||||
.expect("legacy host should reject a limited session");
|
||||
assert_eq!(
|
||||
error,
|
||||
format!(
|
||||
"code-mode host does not support session resource limits: missing `{SESSION_RESOURCE_LIMITS_CAPABILITY}` capability"
|
||||
)
|
||||
);
|
||||
let second_session = provider
|
||||
.create_session(Arc::new(NoopCodeModeSessionDelegate))
|
||||
.await
|
||||
.expect("rejecting limited sessions should preserve the shared legacy-host connection");
|
||||
second_session
|
||||
.shutdown()
|
||||
.await
|
||||
.expect("second unlimited session should shut down");
|
||||
drop(second_session);
|
||||
let response = session
|
||||
.execute(ExecuteRequest {
|
||||
tool_call_id: "shared-websocket".to_string(),
|
||||
@@ -261,6 +295,13 @@ async fn websocket_provider_fails_when_a_negotiated_bulk_connection_is_unavailab
|
||||
let capability =
|
||||
Capability::new(DUAL_WEBSOCKET_CAPABILITY).expect("dual websocket capability");
|
||||
assert!(hello.optional_capabilities().contains(&capability));
|
||||
let session_limits_capability =
|
||||
Capability::new(SESSION_RESOURCE_LIMITS_CAPABILITY).expect("session-limit capability");
|
||||
assert!(
|
||||
hello
|
||||
.optional_capabilities()
|
||||
.contains(&session_limits_capability)
|
||||
);
|
||||
let hello = HostToClient::HostHello(
|
||||
HostHello::new(
|
||||
ProtocolVersion::V1,
|
||||
|
||||
Reference in New Issue
Block a user