use crate::stores::EventStore;
use agent_sdk_foundation::types::ThreadId;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use tokio_util::sync::CancellationToken;
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct ToolContextSeed {
pub thread_id: ThreadId,
pub turn: usize,
pub sequence_offset: u64,
#[serde(default)]
pub metadata: HashMap<String, serde_json::Value>,
}
impl ToolContextSeed {
#[must_use]
pub fn first_turn(thread_id: ThreadId) -> Self {
Self {
thread_id,
turn: 1,
sequence_offset: 0,
metadata: HashMap::new(),
}
}
#[must_use]
pub fn with_metadata(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
self.metadata.insert(key.into(), value);
self
}
}
pub struct HostDependencies {
pub event_store: Arc<dyn EventStore>,
pub cancel_token: CancellationToken,
pub subagent_semaphore: Option<Arc<tokio::sync::Semaphore>>,
}
pub trait ExecutionContextFactory<Ctx>: Send + Sync {
fn build(
&self,
seed: &ToolContextSeed,
app: Ctx,
deps: HostDependencies,
) -> crate::tools::ToolContext<Ctx>;
}
pub struct DefaultContextFactory;
impl<Ctx> ExecutionContextFactory<Ctx> for DefaultContextFactory {
fn build(
&self,
seed: &ToolContextSeed,
app: Ctx,
deps: HostDependencies,
) -> crate::tools::ToolContext<Ctx> {
crate::tools::ToolContext::from_seed(seed, app, deps)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn seed_first_turn_defaults() {
let seed = ToolContextSeed::first_turn(ThreadId::from_string("t-1"));
assert_eq!(seed.thread_id, ThreadId::from_string("t-1"));
assert_eq!(seed.turn, 1);
assert_eq!(seed.sequence_offset, 0);
assert!(seed.metadata.is_empty());
}
#[test]
fn seed_with_metadata() {
let seed = ToolContextSeed::first_turn(ThreadId::new())
.with_metadata("user_id", serde_json::json!("u-42"));
assert_eq!(
seed.metadata.get("user_id"),
Some(&serde_json::json!("u-42"))
);
}
#[test]
fn seed_round_trips_through_json() -> anyhow::Result<()> {
let original = ToolContextSeed {
thread_id: ThreadId::from_string("t-round-trip"),
turn: 5,
sequence_offset: 42,
metadata: {
let mut m = HashMap::new();
m.insert("key".into(), serde_json::json!("value"));
m
},
};
let json = serde_json::to_string(&original)?;
let recovered: ToolContextSeed = serde_json::from_str(&json)?;
assert_eq!(recovered, original);
Ok(())
}
}