use std::sync::{Arc, Mutex};
use io_harness::tools::{Tool, ToolFuture, Toolbox};
use io_harness::{
run_with, ApproveAll, Policy, RunOutcome, Store, TaskContract, ToolSpec, Verification,
};
use serde_json::{json, Value};
const ID: &str = "SHP-4417";
const STATUS: &str = "SHP-4417: held at Rotterdam, cleared 2026-08-01, next hop Felixstowe";
const DATUM: &str = "Rotterdam";
struct Shipments {
asked: Arc<Mutex<Vec<String>>>,
}
impl Tool for Shipments {
fn spec(&self) -> ToolSpec {
ToolSpec {
name: "lookup_shipment".into(),
description: "Look up the current status of one shipment by its id.".into(),
parameters: json!({
"type": "object",
"properties": {
"id": { "type": "string", "description": "A shipment id, e.g. SHP-4417." }
},
"required": ["id"]
}),
}
}
fn invoke<'a>(&'a self, arguments: &'a Value) -> ToolFuture<'a> {
let id = arguments
.get("id")
.and_then(|v| v.as_str())
.unwrap_or_default()
.to_string();
Box::pin(async move {
self.asked
.lock()
.expect("no panic holds this lock")
.push(id.clone());
if id == ID {
return Ok(STATUS.to_string());
}
Err(io_harness::Error::Config(format!(
"no shipment {id:?}; this table knows {ID}"
)))
})
}
}
#[tokio::main]
async fn main() -> io_harness::Result<()> {
let provider = io_harness::OpenRouter::from_env()?;
let dir = tempfile::tempdir()?;
std::fs::write(
dir.path().join("manifest.txt"),
format!("shipments we track:\n{ID}\nSHP-9002\n"),
)?;
let asked = Arc::new(Mutex::new(Vec::new()));
let toolbox = Toolbox::new().with(Shipments {
asked: Arc::clone(&asked),
});
let contract = TaskContract::workspace(
"manifest.txt lists the shipments we track. Use the lookup_shipment \
tool to get the current status of SHP-4417, then write the line it \
returns, verbatim, into status.txt with write_file.",
dir.path(),
Verification::WorkspaceFileContains {
file: "status.txt".into(),
needle: ID.into(),
},
)
.with_tools(toolbox)
.with_max_steps(6);
let policy = Policy::default()
.layer("app")
.allow_read("*")
.allow_write("*")
.allow_exec("lookup_shipment");
let store = Store::open(dir.path().join("runs.db"))?;
let result = run_with(&contract, &provider, &store, &policy, &ApproveAll).await?;
println!("outcome: {:?}", result.outcome);
println!("\ntool calls the trace recorded:");
for step in store.steps(result.run_id)? {
if step.tool_call.contains("lookup_shipment") {
println!(" step {}: {}", step.step, step.tool_call);
}
}
println!(" asked for: {:?}", asked.lock().expect("no poisoning"));
for e in store
.events(result.run_id)?
.iter()
.filter(|e| e.kind == "refusal")
{
println!(
" refused: {} {} (rule {})",
e.act,
e.target,
e.rule.clone().unwrap_or_else(|| "-".into())
);
}
if matches!(result.outcome, RunOutcome::Success { .. }) {
let written = std::fs::read_to_string(dir.path().join("status.txt"))?;
println!("\nstatus.txt: {}", written.trim());
println!(
" carries {DATUM:?}, which was on no disk the agent could reach: {}",
written.contains(DATUM)
);
} else {
eprintln!("note: the run did not reach success; see the trace above");
}
Ok(())
}