chore: sync workspace changes

This commit is contained in:
Nathan Flurry 2026-01-26 22:29:10 -08:00
parent 4b5b390b7f
commit 4083baa1c1
55 changed files with 2431 additions and 840 deletions

View file

@ -31,6 +31,7 @@ schemars.workspace = true
tracing.workspace = true
tracing-logfmt.workspace = true
tracing-subscriber.workspace = true
include_dir.workspace = true
[dev-dependencies]
http-body-util.workspace = true

View file

@ -0,0 +1,63 @@
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
fn main() {
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR"));
let root_dir = manifest_dir
.parent()
.and_then(Path::parent)
.and_then(Path::parent)
.expect("workspace root");
let dist_dir = root_dir
.join("frontend")
.join("packages")
.join("inspector")
.join("dist");
println!("cargo:rerun-if-env-changed=SANDBOX_AGENT_SKIP_INSPECTOR");
println!("cargo:rerun-if-changed={}", dist_dir.display());
let skip = env::var("SANDBOX_AGENT_SKIP_INSPECTOR").is_ok();
let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR"));
let out_file = out_dir.join("inspector_assets.rs");
if skip {
write_disabled(&out_file);
return;
}
if !dist_dir.exists() {
panic!(
"Inspector frontend missing at {}. Run `pnpm --filter @sandbox-agent/inspector build` (or `pnpm -C frontend/packages/inspector build`) or set SANDBOX_AGENT_SKIP_INSPECTOR=1 to skip embedding.",
dist_dir.display()
);
}
let dist_literal = quote_path(&dist_dir);
let contents = format!(
"pub const INSPECTOR_ENABLED: bool = true;\n\
pub fn inspector_dir() -> Option<&'static include_dir::Dir<'static>> {{\n\
Some(&INSPECTOR_DIR)\n\
}}\n\
static INSPECTOR_DIR: include_dir::Dir<'static> = include_dir::include_dir!(\"{}\");\n",
dist_literal
);
fs::write(&out_file, contents).expect("write inspector_assets.rs");
}
fn write_disabled(out_file: &Path) {
let contents = "pub const INSPECTOR_ENABLED: bool = false;\n\
pub fn inspector_dir() -> Option<&'static include_dir::Dir<'static>> {\n\
None\n\
}\n";
fs::write(out_file, contents).expect("write inspector_assets.rs");
}
fn quote_path(path: &Path) -> String {
path.to_str()
.expect("valid path")
.replace('\\', "\\\\")
.replace('"', "\\\"")
}

View file

@ -2,3 +2,4 @@
pub mod credentials;
pub mod router;
pub mod ui;

View file

@ -16,6 +16,7 @@ use sandbox_agent_core::router::{
};
use sandbox_agent_core::router::{AgentListResponse, AgentModesResponse, CreateSessionResponse, EventsResponse};
use sandbox_agent_core::router::build_router;
use sandbox_agent_core::ui;
use serde::Serialize;
use serde_json::Value;
use thiserror::Error;
@ -23,25 +24,42 @@ use tower_http::cors::{Any, CorsLayer};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
const API_PREFIX: &str = "/v1";
const DEFAULT_HOST: &str = "127.0.0.1";
const DEFAULT_PORT: u16 = 2468;
#[derive(Parser, Debug)]
#[command(name = "sandbox-agent")]
#[command(about = "Sandbox agent for managing coding agents", version)]
#[command(name = "sandbox-daemon", bin_name = "sandbox-agent")]
#[command(about = "Sandbox daemon for managing coding agents", version)]
struct Cli {
#[command(subcommand)]
command: Option<Command>,
#[arg(long, short = 'H', default_value = "127.0.0.1")]
host: String,
#[arg(long, short = 'p', default_value_t = 2468)]
port: u16,
#[arg(long, short = 't')]
#[arg(long, short = 't', global = true)]
token: Option<String>,
#[arg(long, short = 'n')]
#[arg(long, short = 'n', global = true)]
no_token: bool,
}
#[derive(Subcommand, Debug)]
enum Command {
/// Run the sandbox daemon HTTP server.
Server(ServerArgs),
/// Manage installed agents and their modes.
Agents(AgentsArgs),
/// Create sessions and interact with session events.
Sessions(SessionsArgs),
/// Inspect locally discovered credentials.
Credentials(CredentialsArgs),
}
#[derive(Args, Debug)]
struct ServerArgs {
#[arg(long, short = 'H', default_value = DEFAULT_HOST)]
host: String,
#[arg(long, short = 'p', default_value_t = DEFAULT_PORT)]
port: u16,
#[arg(long = "cors-allow-origin", short = 'O')]
cors_allow_origin: Vec<String>,
@ -56,16 +74,6 @@ struct Cli {
cors_allow_credentials: bool,
}
#[derive(Subcommand, Debug)]
enum Command {
/// Manage installed agents and their modes.
Agents(AgentsArgs),
/// Create sessions and interact with session events.
Sessions(SessionsArgs),
/// Inspect locally discovered credentials.
Credentials(CredentialsArgs),
}
#[derive(Args, Debug)]
struct AgentsArgs {
#[command(subcommand)]
@ -255,6 +263,8 @@ struct CredentialsExtractEnvArgs {
#[derive(Debug, Error)]
enum CliError {
#[error("missing command: run `sandbox-daemon server` to start the daemon")]
MissingCommand,
#[error("missing --token or --no-token for server mode")]
MissingToken,
#[error("invalid cors origin: {0}")]
@ -280,8 +290,9 @@ fn main() {
let cli = Cli::parse();
let result = match &cli.command {
Some(Command::Server(args)) => run_server(&cli, args),
Some(command) => run_client(command, &cli),
None => run_server(&cli),
None => Err(CliError::MissingCommand),
};
if let Err(err) = result {
@ -298,7 +309,7 @@ fn init_logging() {
.init();
}
fn run_server(cli: &Cli) -> Result<(), CliError> {
fn run_server(cli: &Cli, server: &ServerArgs) -> Result<(), CliError> {
let auth = if cli.no_token {
AuthConfig::disabled()
} else if let Some(token) = cli.token.clone() {
@ -312,11 +323,16 @@ fn run_server(cli: &Cli) -> Result<(), CliError> {
let state = AppState::new(auth, agent_manager);
let mut router = build_router(state);
if let Some(cors) = build_cors_layer(cli)? {
if let Some(cors) = build_cors_layer(server)? {
router = router.layer(cors);
}
let addr = format!("{}:{}", cli.host, cli.port);
let addr = format!("{}:{}", server.host, server.port);
let display_host = match server.host.as_str() {
"0.0.0.0" | "::" => "localhost",
other => other,
};
let inspector_url = format!("http://{}:{}/ui", display_host, server.port);
let runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
@ -325,6 +341,11 @@ fn run_server(cli: &Cli) -> Result<(), CliError> {
runtime.block_on(async move {
let listener = tokio::net::TcpListener::bind(&addr).await?;
tracing::info!(addr = %addr, "server listening");
if ui::is_enabled() {
tracing::info!(url = %inspector_url, "inspector ui available");
} else {
tracing::info!("inspector ui not embedded; set SANDBOX_AGENT_SKIP_INSPECTOR=1 to skip embedding during builds");
}
axum::serve(listener, router)
.await
.map_err(|err| CliError::Server(err.to_string()))
@ -339,6 +360,9 @@ fn default_install_dir() -> PathBuf {
fn run_client(command: &Command, cli: &Cli) -> Result<(), CliError> {
match command {
Command::Server(_) => Err(CliError::Server(
"server subcommand must be invoked as `sandbox-daemon server`".to_string(),
)),
Command::Agents(subcommand) => run_agents(&subcommand.command, cli),
Command::Sessions(subcommand) => run_sessions(&subcommand.command, cli),
Command::Credentials(subcommand) => run_credentials(&subcommand.command),
@ -663,11 +687,11 @@ fn available_providers(credentials: &ExtractedCredentials) -> Vec<String> {
providers
}
fn build_cors_layer(cli: &Cli) -> Result<Option<CorsLayer>, CliError> {
let has_config = !cli.cors_allow_origin.is_empty()
|| !cli.cors_allow_method.is_empty()
|| !cli.cors_allow_header.is_empty()
|| cli.cors_allow_credentials;
fn build_cors_layer(server: &ServerArgs) -> Result<Option<CorsLayer>, CliError> {
let has_config = !server.cors_allow_origin.is_empty()
|| !server.cors_allow_method.is_empty()
|| !server.cors_allow_header.is_empty()
|| server.cors_allow_credentials;
if !has_config {
return Ok(None);
@ -675,11 +699,11 @@ fn build_cors_layer(cli: &Cli) -> Result<Option<CorsLayer>, CliError> {
let mut cors = CorsLayer::new();
if cli.cors_allow_origin.is_empty() {
if server.cors_allow_origin.is_empty() {
cors = cors.allow_origin(Any);
} else {
let mut origins = Vec::new();
for origin in &cli.cors_allow_origin {
for origin in &server.cors_allow_origin {
let value = origin
.parse()
.map_err(|_| CliError::InvalidCorsOrigin(origin.clone()))?;
@ -688,11 +712,11 @@ fn build_cors_layer(cli: &Cli) -> Result<Option<CorsLayer>, CliError> {
cors = cors.allow_origin(origins);
}
if cli.cors_allow_method.is_empty() {
if server.cors_allow_method.is_empty() {
cors = cors.allow_methods(Any);
} else {
let mut methods = Vec::new();
for method in &cli.cors_allow_method {
for method in &server.cors_allow_method {
let parsed = method
.parse()
.map_err(|_| CliError::InvalidCorsMethod(method.clone()))?;
@ -701,11 +725,11 @@ fn build_cors_layer(cli: &Cli) -> Result<Option<CorsLayer>, CliError> {
cors = cors.allow_methods(methods);
}
if cli.cors_allow_header.is_empty() {
if server.cors_allow_header.is_empty() {
cors = cors.allow_headers(Any);
} else {
let mut headers = Vec::new();
for header in &cli.cors_allow_header {
for header in &server.cors_allow_header {
let parsed = header
.parse()
.map_err(|_| CliError::InvalidCorsHeader(header.clone()))?;
@ -714,7 +738,7 @@ fn build_cors_layer(cli: &Cli) -> Result<Option<CorsLayer>, CliError> {
cors = cors.allow_headers(headers);
}
if cli.cors_allow_credentials {
if server.cors_allow_credentials {
cors = cors.allow_credentials(true);
}
@ -732,7 +756,7 @@ impl ClientContext {
let endpoint = args
.endpoint
.clone()
.unwrap_or_else(|| format!("http://{}:{}", cli.host, cli.port));
.unwrap_or_else(|| format!("http://{}:{}", DEFAULT_HOST, DEFAULT_PORT));
let token = if cli.no_token { None } else { cli.token.clone() };
let client = HttpClient::builder().build()?;
Ok(Self {

View file

@ -46,7 +46,7 @@ use serde_json::{json, Value};
use tokio::sync::{broadcast, mpsc, Mutex};
use tokio_stream::wrappers::BroadcastStream;
use tokio::time::sleep;
use utoipa::{OpenApi, ToSchema};
use utoipa::{Modify, OpenApi, ToSchema};
use sandbox_agent_agent_management::agents::{
AgentError as ManagerError, AgentId, AgentManager, InstallOptions, SpawnOptions, StreamingSpawn,
@ -187,10 +187,21 @@ pub fn build_router(state: AppState) -> Router {
(name = "meta", description = "Service metadata"),
(name = "agents", description = "Agent management"),
(name = "sessions", description = "Session management")
)
),
modifiers(&ServerAddon)
)]
pub struct ApiDoc;
struct ServerAddon;
impl Modify for ServerAddon {
fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
openapi.servers = Some(vec![utoipa::openapi::Server::new(
"http://localhost:2468",
)]);
}
}
#[derive(Debug, thiserror::Error)]
pub enum ApiError {
#[error(transparent)]
@ -594,14 +605,14 @@ impl SessionManager {
let session = sessions.get_mut(session_id).ok_or_else(|| SandboxError::SessionNotFound {
session_id: session_id.to_string(),
})?;
if let Some(err) = session.ended_error() {
return Err(err);
}
if !session.take_question(question_id) {
return Err(SandboxError::InvalidRequest {
message: format!("unknown question id: {question_id}"),
});
}
if let Some(err) = session.ended_error() {
return Err(err);
}
(session.agent, session.agent_session_id.clone())
};
@ -628,14 +639,14 @@ impl SessionManager {
let session = sessions.get_mut(session_id).ok_or_else(|| SandboxError::SessionNotFound {
session_id: session_id.to_string(),
})?;
if let Some(err) = session.ended_error() {
return Err(err);
}
if !session.take_question(question_id) {
return Err(SandboxError::InvalidRequest {
message: format!("unknown question id: {question_id}"),
});
}
if let Some(err) = session.ended_error() {
return Err(err);
}
(session.agent, session.agent_session_id.clone())
};
@ -663,14 +674,14 @@ impl SessionManager {
let session = sessions.get_mut(session_id).ok_or_else(|| SandboxError::SessionNotFound {
session_id: session_id.to_string(),
})?;
if let Some(err) = session.ended_error() {
return Err(err);
}
if !session.take_permission(permission_id) {
return Err(SandboxError::InvalidRequest {
message: format!("unknown permission id: {permission_id}"),
});
}
if let Some(err) = session.ended_error() {
return Err(err);
}
let codex_metadata = if session.agent == AgentId::Codex {
session.events.iter().find_map(|event| {
if let UniversalEventData::PermissionAsked { permission_asked } = &event.data {
@ -858,47 +869,45 @@ impl SessionManager {
Ok(Ok(status)) if status.success() => {}
Ok(Ok(status)) => {
let message = format!("agent exited with status {:?}", status);
self.record_error(
&session_id,
message.clone(),
Some("process_exit".to_string()),
None,
)
if !terminate_early {
self.record_error(
&session_id,
message.clone(),
Some("process_exit".to_string()),
None,
)
.await;
}
self.mark_session_ended(&session_id, status.code(), &message)
.await;
}
Ok(Err(err)) => {
let message = format!("failed to wait for agent: {err}");
self.record_error(
&session_id,
message.clone(),
Some("process_wait_failed".to_string()),
None,
)
.await;
self.mark_session_ended(
&session_id,
None,
&message,
)
.await;
if !terminate_early {
self.record_error(
&session_id,
message.clone(),
Some("process_wait_failed".to_string()),
None,
)
.await;
}
self.mark_session_ended(&session_id, None, &message)
.await;
}
Err(err) => {
let message = format!("failed to join agent task: {err}");
self.record_error(
&session_id,
message.clone(),
Some("process_wait_failed".to_string()),
None,
)
.await;
self.mark_session_ended(
&session_id,
None,
&message,
)
.await;
if !terminate_early {
self.record_error(
&session_id,
message.clone(),
Some("process_wait_failed".to_string()),
None,
)
.await;
}
self.mark_session_ended(&session_id, None, &message)
.await;
}
}
}
@ -2179,15 +2188,22 @@ impl CodexAppServerState {
serde_json::from_value::<codex_schema::ServerNotification>(value.clone())
{
self.maybe_capture_thread_id(&notification);
let conversion = convert_codex::notification_to_universal(&notification);
let should_terminate = matches!(
notification,
codex_schema::ServerNotification::TurnCompleted(_)
| codex_schema::ServerNotification::Error(_)
);
CodexLineOutcome {
conversion: Some(conversion),
should_terminate,
if codex_should_emit_notification(&notification) {
let conversion = convert_codex::notification_to_universal(&notification);
CodexLineOutcome {
conversion: Some(conversion),
should_terminate,
}
} else {
CodexLineOutcome {
conversion: None,
should_terminate,
}
}
} else {
CodexLineOutcome::default()
@ -2369,6 +2385,20 @@ fn codex_sandbox_policy(mode: Option<&str>) -> Option<codex_schema::SandboxPolic
}
}
fn codex_should_emit_notification(notification: &codex_schema::ServerNotification) -> bool {
match notification {
codex_schema::ServerNotification::ThreadStarted(_)
| codex_schema::ServerNotification::TurnStarted(_)
| codex_schema::ServerNotification::Error(_) => true,
codex_schema::ServerNotification::ItemCompleted(params) => matches!(
params.item,
codex_schema::ThreadItem::UserMessage { .. }
| codex_schema::ThreadItem::AgentMessage { .. }
),
_ => false,
}
}
fn codex_request_to_universal(request: &codex_schema::ServerRequest) -> EventConversion {
match request {
codex_schema::ServerRequest::ItemCommandExecutionRequestApproval { id, params } => {

View file

@ -0,0 +1,81 @@
use std::path::Path;
use axum::body::Body;
use axum::extract::Path as AxumPath;
use axum::http::{header, HeaderValue, StatusCode};
use axum::response::{IntoResponse, Response};
use axum::routing::get;
use axum::Router;
include!(concat!(env!("OUT_DIR"), "/inspector_assets.rs"));
pub fn is_enabled() -> bool {
INSPECTOR_ENABLED
}
pub fn router() -> Router {
if !INSPECTOR_ENABLED {
return Router::new();
}
Router::new()
.route("/ui", get(handle_index))
.route("/ui/", get(handle_index))
.route("/ui/*path", get(handle_path))
}
async fn handle_index() -> Response {
serve_path("")
}
async fn handle_path(AxumPath(path): AxumPath<String>) -> Response {
serve_path(&path)
}
fn serve_path(path: &str) -> Response {
let Some(dir) = inspector_dir() else {
return StatusCode::NOT_FOUND.into_response();
};
let trimmed = path.trim_start_matches('/');
let target = if trimmed.is_empty() { "index.html" } else { trimmed };
if let Some(file) = dir.get_file(target) {
return file_response(file);
}
if !target.contains('.') {
if let Some(file) = dir.get_file("index.html") {
return file_response(file);
}
}
StatusCode::NOT_FOUND.into_response()
}
fn file_response(file: &include_dir::File) -> Response {
let mut response = Response::new(Body::from(file.contents().to_vec()));
*response.status_mut() = StatusCode::OK;
let content_type = content_type_for(file.path());
let value = HeaderValue::from_static(content_type);
response.headers_mut().insert(header::CONTENT_TYPE, value);
response
}
fn content_type_for(path: &Path) -> &'static str {
match path.extension().and_then(|ext| ext.to_str()) {
Some("html") => "text/html; charset=utf-8",
Some("js") => "text/javascript; charset=utf-8",
Some("css") => "text/css; charset=utf-8",
Some("svg") => "image/svg+xml",
Some("png") => "image/png",
Some("ico") => "image/x-icon",
Some("json") => "application/json",
Some("map") => "application/json",
Some("txt") => "text/plain; charset=utf-8",
Some("woff") => "font/woff",
Some("woff2") => "font/woff2",
Some("ttf") => "font/ttf",
Some("eot") => "application/vnd.ms-fontobject",
_ => "application/octet-stream",
}
}

View file

@ -0,0 +1,40 @@
use axum::body::Body;
use axum::http::{Request, StatusCode};
use http_body_util::BodyExt;
use sandbox_agent_agent_management::agents::AgentManager;
use sandbox_agent_core::router::{build_router, AppState, AuthConfig};
use sandbox_agent_core::ui;
use tempfile::TempDir;
use tower::util::ServiceExt;
#[tokio::test]
async fn serves_inspector_ui() {
if !ui::is_enabled() {
return;
}
let install_dir = TempDir::new().expect("create temp install dir");
let manager = AgentManager::new(install_dir.path()).expect("create agent manager");
let state = AppState::new(AuthConfig::disabled(), manager);
let app = build_router(state);
let request = Request::builder()
.uri("/ui")
.body(Body::empty())
.expect("build request");
let response = app
.oneshot(request)
.await
.expect("request handled");
assert_eq!(response.status(), StatusCode::OK);
let bytes = response
.into_body()
.collect()
.await
.expect("read body")
.to_bytes();
let body = String::from_utf8_lossy(&bytes);
assert!(body.contains("<!doctype html") || body.contains("<html"));
}

View file

@ -1,6 +1,6 @@
---
source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs
assertion_line: 995
assertion_line: 984
expression: normalize_events(&permission_events)
---
- agent: codex
@ -9,83 +9,28 @@ expression: normalize_events(&permission_events)
started:
message: session.created
- agent: codex
kind: message
message:
unparsed: true
kind: started
seq: 2
started:
message: thread/started
- agent: codex
kind: message
message:
unparsed: true
kind: started
seq: 3
started:
message: turn/started
- agent: codex
kind: message
message:
unparsed: true
parts:
- text: "<redacted>"
type: text
role: user
seq: 4
- agent: codex
kind: message
message:
unparsed: true
parts:
- text: "<redacted>"
type: text
role: assistant
seq: 5
- agent: codex
kind: message
message:
unparsed: true
seq: 6
- agent: codex
kind: message
message:
unparsed: true
seq: 7
- agent: codex
kind: message
message:
unparsed: true
seq: 8
- agent: codex
kind: message
message:
unparsed: true
seq: 9
- agent: codex
kind: message
message:
unparsed: true
seq: 10
- agent: codex
kind: message
message:
unparsed: true
seq: 11
- agent: codex
kind: message
message:
unparsed: true
seq: 12
- agent: codex
kind: message
message:
unparsed: true
seq: 13
- agent: codex
kind: message
message:
unparsed: true
seq: 14
- agent: codex
kind: message
message:
unparsed: true
seq: 15
- agent: codex
kind: message
message:
unparsed: true
seq: 16
- agent: codex
error:
kind: process_exit
message: agent exited with status ExitStatus(unix_wait_status(256))
kind: error
seq: 17

View file

@ -1,15 +1,11 @@
---
source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs
assertion_line: 1028
assertion_line: 1017
expression: "json!({ \"status\": status.as_u16(), \"payload\": payload, })"
---
payload:
agent: codex
detail: "agent process exited: codex"
details:
exitCode: 1
stderr: agent exited with status ExitStatus(unix_wait_status(256))
status: 500
title: Agent Process Exited
type: "urn:sandbox-agent:error:agent_process_exited"
status: 500
detail: "invalid request: unknown permission id: missing-permission"
status: 400
title: Invalid Request
type: "urn:sandbox-agent:error:invalid_request"
status: 400

View file

@ -1,6 +1,6 @@
---
source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs
assertion_line: 1117
assertion_line: 1106
expression: normalize_events(&reject_events)
---
- agent: codex
@ -9,83 +9,28 @@ expression: normalize_events(&reject_events)
started:
message: session.created
- agent: codex
kind: message
message:
unparsed: true
kind: started
seq: 2
started:
message: thread/started
- agent: codex
kind: message
message:
unparsed: true
kind: started
seq: 3
started:
message: turn/started
- agent: codex
kind: message
message:
unparsed: true
parts:
- text: "<redacted>"
type: text
role: user
seq: 4
- agent: codex
kind: message
message:
unparsed: true
parts:
- text: "<redacted>"
type: text
role: assistant
seq: 5
- agent: codex
kind: message
message:
unparsed: true
seq: 6
- agent: codex
kind: message
message:
unparsed: true
seq: 7
- agent: codex
kind: message
message:
unparsed: true
seq: 8
- agent: codex
kind: message
message:
unparsed: true
seq: 9
- agent: codex
kind: message
message:
unparsed: true
seq: 10
- agent: codex
kind: message
message:
unparsed: true
seq: 11
- agent: codex
kind: message
message:
unparsed: true
seq: 12
- agent: codex
kind: message
message:
unparsed: true
seq: 13
- agent: codex
kind: message
message:
unparsed: true
seq: 14
- agent: codex
kind: message
message:
unparsed: true
seq: 15
- agent: codex
kind: message
message:
unparsed: true
seq: 16
- agent: codex
error:
kind: process_exit
message: agent exited with status ExitStatus(unix_wait_status(256))
kind: error
seq: 17

View file

@ -1,15 +1,11 @@
---
source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs
assertion_line: 1150
assertion_line: 1139
expression: "json!({ \"status\": status.as_u16(), \"payload\": payload, })"
---
payload:
agent: codex
detail: "agent process exited: codex"
details:
exitCode: 1
stderr: agent exited with status ExitStatus(unix_wait_status(256))
status: 500
title: Agent Process Exited
type: "urn:sandbox-agent:error:agent_process_exited"
status: 500
detail: "invalid request: unknown question id: missing-question"
status: 400
title: Invalid Request
type: "urn:sandbox-agent:error:invalid_request"
status: 400

View file

@ -1,6 +1,6 @@
---
source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs
assertion_line: 1056
assertion_line: 1045
expression: normalize_events(&question_events)
---
- agent: codex
@ -9,83 +9,28 @@ expression: normalize_events(&question_events)
started:
message: session.created
- agent: codex
kind: message
message:
unparsed: true
kind: started
seq: 2
started:
message: thread/started
- agent: codex
kind: message
message:
unparsed: true
kind: started
seq: 3
started:
message: turn/started
- agent: codex
kind: message
message:
unparsed: true
parts:
- text: "<redacted>"
type: text
role: user
seq: 4
- agent: codex
kind: message
message:
unparsed: true
parts:
- text: "<redacted>"
type: text
role: assistant
seq: 5
- agent: codex
kind: message
message:
unparsed: true
seq: 6
- agent: codex
kind: message
message:
unparsed: true
seq: 7
- agent: codex
kind: message
message:
unparsed: true
seq: 8
- agent: codex
kind: message
message:
unparsed: true
seq: 9
- agent: codex
kind: message
message:
unparsed: true
seq: 10
- agent: codex
kind: message
message:
unparsed: true
seq: 11
- agent: codex
kind: message
message:
unparsed: true
seq: 12
- agent: codex
kind: message
message:
unparsed: true
seq: 13
- agent: codex
kind: message
message:
unparsed: true
seq: 14
- agent: codex
kind: message
message:
unparsed: true
seq: 15
- agent: codex
kind: message
message:
unparsed: true
seq: 16
- agent: codex
error:
kind: process_exit
message: agent exited with status ExitStatus(unix_wait_status(256))
kind: error
seq: 17

View file

@ -1,15 +1,11 @@
---
source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs
assertion_line: 1089
assertion_line: 1078
expression: "json!({ \"status\": status.as_u16(), \"payload\": payload, })"
---
payload:
agent: codex
detail: "agent process exited: codex"
details:
exitCode: 1
stderr: agent exited with status ExitStatus(unix_wait_status(256))
status: 500
title: Agent Process Exited
type: "urn:sandbox-agent:error:agent_process_exited"
status: 500
detail: "invalid request: unknown question id: missing-question"
status: 400
title: Invalid Request
type: "urn:sandbox-agent:error:invalid_request"
status: 400

View file

@ -1,6 +1,6 @@
---
source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs
assertion_line: 1219
assertion_line: 1214
expression: snapshot
---
session_a:
@ -10,86 +10,31 @@ session_a:
started:
message: session.created
- agent: codex
kind: message
message:
unparsed: true
kind: started
seq: 2
started:
message: thread/started
- agent: codex
kind: message
message:
unparsed: true
kind: started
seq: 3
started:
message: turn/started
- agent: codex
kind: message
message:
unparsed: true
parts:
- text: "<redacted>"
type: text
role: user
seq: 4
- agent: codex
kind: message
message:
unparsed: true
parts:
- text: "<redacted>"
type: text
role: assistant
seq: 5
- agent: codex
kind: message
message:
unparsed: true
seq: 6
- agent: codex
kind: message
message:
unparsed: true
seq: 7
- agent: codex
kind: message
message:
unparsed: true
seq: 8
- agent: codex
kind: message
message:
unparsed: true
seq: 9
- agent: codex
kind: message
message:
unparsed: true
seq: 10
- agent: codex
kind: message
message:
unparsed: true
seq: 11
- agent: codex
kind: message
message:
unparsed: true
seq: 12
- agent: codex
kind: message
message:
unparsed: true
seq: 13
- agent: codex
kind: message
message:
unparsed: true
seq: 14
- agent: codex
kind: message
message:
unparsed: true
seq: 15
- agent: codex
kind: message
message:
unparsed: true
seq: 16
- agent: codex
error:
kind: process_exit
message: agent exited with status ExitStatus(unix_wait_status(256))
kind: error
seq: 17
session_b:
- agent: codex
kind: started
@ -97,83 +42,28 @@ session_b:
started:
message: session.created
- agent: codex
kind: message
message:
unparsed: true
kind: started
seq: 2
started:
message: thread/started
- agent: codex
kind: message
message:
unparsed: true
kind: started
seq: 3
started:
message: turn/started
- agent: codex
kind: message
message:
unparsed: true
parts:
- text: "<redacted>"
type: text
role: user
seq: 4
- agent: codex
kind: message
message:
unparsed: true
parts:
- text: "<redacted>"
type: text
role: assistant
seq: 5
- agent: codex
kind: message
message:
unparsed: true
seq: 6
- agent: codex
kind: message
message:
unparsed: true
seq: 7
- agent: codex
kind: message
message:
unparsed: true
seq: 8
- agent: codex
kind: message
message:
unparsed: true
seq: 9
- agent: codex
kind: message
message:
unparsed: true
seq: 10
- agent: codex
kind: message
message:
unparsed: true
seq: 11
- agent: codex
kind: message
message:
unparsed: true
seq: 12
- agent: codex
kind: message
message:
unparsed: true
seq: 13
- agent: codex
kind: message
message:
unparsed: true
seq: 14
- agent: codex
kind: message
message:
unparsed: true
seq: 15
- agent: codex
kind: message
message:
unparsed: true
seq: 16
- agent: codex
error:
kind: process_exit
message: agent exited with status ExitStatus(unix_wait_status(256))
kind: error
seq: 17

View file

@ -1,6 +1,6 @@
---
source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs
assertion_line: 714
assertion_line: 697
expression: normalized
---
- agent: codex
@ -9,83 +9,28 @@ expression: normalized
started:
message: session.created
- agent: codex
kind: message
message:
unparsed: true
kind: started
seq: 2
started:
message: thread/started
- agent: codex
kind: message
message:
unparsed: true
kind: started
seq: 3
started:
message: turn/started
- agent: codex
kind: message
message:
unparsed: true
parts:
- text: "<redacted>"
type: text
role: user
seq: 4
- agent: codex
kind: message
message:
unparsed: true
parts:
- text: "<redacted>"
type: text
role: assistant
seq: 5
- agent: codex
kind: message
message:
unparsed: true
seq: 6
- agent: codex
kind: message
message:
unparsed: true
seq: 7
- agent: codex
kind: message
message:
unparsed: true
seq: 8
- agent: codex
kind: message
message:
unparsed: true
seq: 9
- agent: codex
kind: message
message:
unparsed: true
seq: 10
- agent: codex
kind: message
message:
unparsed: true
seq: 11
- agent: codex
kind: message
message:
unparsed: true
seq: 12
- agent: codex
kind: message
message:
unparsed: true
seq: 13
- agent: codex
kind: message
message:
unparsed: true
seq: 14
- agent: codex
kind: message
message:
unparsed: true
seq: 15
- agent: codex
kind: message
message:
unparsed: true
seq: 16
- agent: codex
error:
kind: process_exit
message: agent exited with status ExitStatus(unix_wait_status(256))
kind: error
seq: 17

View file

@ -1,6 +1,6 @@
---
source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs
assertion_line: 751
assertion_line: 734
expression: normalized
---
- agent: codex
@ -9,83 +9,28 @@ expression: normalized
started:
message: session.created
- agent: codex
kind: message
message:
unparsed: true
kind: started
seq: 2
started:
message: thread/started
- agent: codex
kind: message
message:
unparsed: true
kind: started
seq: 3
started:
message: turn/started
- agent: codex
kind: message
message:
unparsed: true
parts:
- text: "<redacted>"
type: text
role: user
seq: 4
- agent: codex
kind: message
message:
unparsed: true
parts:
- text: "<redacted>"
type: text
role: assistant
seq: 5
- agent: codex
kind: message
message:
unparsed: true
seq: 6
- agent: codex
kind: message
message:
unparsed: true
seq: 7
- agent: codex
kind: message
message:
unparsed: true
seq: 8
- agent: codex
kind: message
message:
unparsed: true
seq: 9
- agent: codex
kind: message
message:
unparsed: true
seq: 10
- agent: codex
kind: message
message:
unparsed: true
seq: 11
- agent: codex
kind: message
message:
unparsed: true
seq: 12
- agent: codex
kind: message
message:
unparsed: true
seq: 13
- agent: codex
kind: message
message:
unparsed: true
seq: 14
- agent: codex
kind: message
message:
unparsed: true
seq: 15
- agent: codex
kind: message
message:
unparsed: true
seq: 16
- agent: codex
error:
kind: process_exit
message: agent exited with status ExitStatus(unix_wait_status(256))
kind: error
seq: 17