mod cli_test_util;
use objectiveai_sdk::agent::InlineAgentBaseWithFallbacksOrRemoteCommitOptional;
use objectiveai_sdk::cli::command::agents::message::RequestMessage;
use objectiveai_sdk::cli::command::agents::selector::{AgentRef, AgentSelector};
use objectiveai_sdk::cli::command::agents::spawn::{
Request as SpawnRequest, RequestDangerousAdvanced,
ResponseItem as SpawnResponseItem,
};
#[tokio::test]
async fn spawn_then_message_propagates_response_continuation() {
let executor = cli_test_util::executor().await;
let spawn_request = SpawnRequest {
path_type: objectiveai_sdk::cli::command::agents::spawn::Path::AgentsSpawn,
message: RequestMessage::Simple("first turn".to_string()),
agent: AgentSelector::Ref {
agent: AgentRef::Resolved(
serde_json::from_value::<InlineAgentBaseWithFallbacksOrRemoteCommitOptional>(
serde_json::json!({"upstream":"mock","output_mode":"instruction"}),
)
.expect("inline mock agent must deserialize"),
),
},
dangerous_advanced: Some(RequestDangerousAdvanced {
stream: Some(true),
seed: Some(42),
skip_lock: None,
}),
base: Default::default(),
};
let spawn_items: Vec<SpawnResponseItem> =
cli_test_util::collect_stream(&executor, spawn_request).await;
let spawn_chunk_aih = spawn_items
.iter()
.find_map(|item| match item {
SpawnResponseItem::Chunk(chunk) if !chunk.agent_instance_hierarchy.is_empty() => {
Some(chunk.agent_instance_hierarchy.clone())
}
_ => None,
})
.expect("agents spawn must emit a Chunk with a non-empty agent_instance_hierarchy");
let spawn_response_id = spawn_items
.iter()
.find_map(|item| match item {
SpawnResponseItem::Chunk(chunk) if !chunk.id.is_empty() => Some(chunk.id.clone()),
_ => None,
})
.expect("agents spawn must emit a Chunk with non-empty id");
cli_test_util::wait_for_agent(&executor, &spawn_chunk_aih).await;
let spawn_continuation = cli_test_util::read_continuation(&executor, &spawn_chunk_aih)
.await
.expect("spawn's agent_continuations row must carry a continuation");
let (parent, instance) = spawn_chunk_aih
.rsplit_once('/')
.map(|(p, i)| (Some(p.to_string()), i.to_string()))
.expect("spawn_chunk_aih must carry at least one '/'");
let resume_request = SpawnRequest {
path_type: objectiveai_sdk::cli::command::agents::spawn::Path::AgentsSpawn,
message: RequestMessage::Simple("follow up".to_string()),
agent: AgentSelector::Instance {
parent_agent_instance_hierarchy: parent,
agent_instance: instance,
},
dangerous_advanced: Some(RequestDangerousAdvanced {
stream: Some(true),
seed: Some(42),
skip_lock: None,
}),
base: Default::default(),
};
let resume_items: Vec<SpawnResponseItem> =
cli_test_util::collect_stream(&executor, resume_request).await;
let new_response_id = resume_items
.iter()
.find_map(|item| match item {
SpawnResponseItem::Chunk(chunk)
if !chunk.id.is_empty() && chunk.id != spawn_response_id =>
{
Some(chunk.id.clone())
}
_ => None,
})
.expect("resumed spawn must emit a Chunk with a fresh response id");
cli_test_util::wait_for_agent(&executor, &spawn_chunk_aih).await;
let request_continuation =
cli_test_util::read_request_continuation(&executor, &new_response_id)
.await
.expect("second turn's request body must carry a continuation");
assert_eq!(
request_continuation, spawn_continuation,
"second turn's request continuation must equal first turn's agent_continuations value",
);
}