use std::time::Duration;
use io_harness::{
run_with, ApproveAll, OpenRouter, Policy, RunOutcome, Store, TaskContract, Verification,
};
const BUGGY: &str = r#"const isOdd = require('is-odd');
function sum(numbers) {
let total = 1;
for (const n of numbers) {
total += n;
}
return total;
}
function oddsOnly(numbers) {
return numbers.filter((n) => isOdd(n));
}
module.exports = { sum, oddsOnly };
"#;
const TESTS: &str = r#"const assert = require('node:assert');
const { sum, oddsOnly } = require('./index.js');
assert.strictEqual(sum([]), 0, 'the sum of nothing is zero');
assert.strictEqual(sum([1, 2, 3]), 6);
assert.deepStrictEqual(oddsOnly([1, 2, 3, 4]), [1, 3]);
console.log('all assertions passed');
"#;
const PACKAGE_JSON: &str = r#"{
"name": "exec-live-fixture",
"version": "1.0.0",
"private": true,
"scripts": { "test": "node test.js" },
"dependencies": { "is-odd": "3.0.1" }
}
"#;
#[tokio::main]
async fn main() -> io_harness::Result<()> {
let root = std::env::temp_dir().join("io-harness-exec-live");
let _ = std::fs::remove_dir_all(&root);
std::fs::create_dir_all(&root)?;
std::fs::write(root.join("package.json"), PACKAGE_JSON)?;
std::fs::write(root.join("index.js"), BUGGY)?;
std::fs::write(root.join("test.js"), TESTS)?;
let contract = TaskContract::workspace(
"This JavaScript project's test suite does not pass. Install its dependencies, run \
the suite, work out what is wrong from the failure, and fix it. Change as little as \
possible: use edit_file rather than rewriting a whole file, and do not edit test.js.",
&root,
Verification::Command {
argv: vec!["npm".into(), "test".into()],
expect_exit: 0,
},
)
.with_max_steps(20)
.with_token_budget(400_000)
.with_exec_timeout(Duration::from_secs(300));
let policy = Policy::default()
.layer("app")
.allow_read("*")
.allow_write("*")
.allow_exec("npm*")
.allow_exec("node*")
.deny_exec("npm publish*")
.deny_exec("rm*")
.deny_write("test.js");
let provider = OpenRouter::from_env()?;
let store = Store::open(root.join("runs.db"))?;
let result = run_with(&contract, &provider, &store, &policy, &ApproveAll).await?;
println!("outcome: {:?}", result.outcome);
println!("provider: {:?}", store.provider(result.run_id)?);
if let Some(s) = result.summary(&store)? {
println!("spend: {} tokens", s.tokens);
}
println!("\n--- steps ---");
for step in store.steps(result.run_id)? {
println!("{:>3} {}", step.step, step.decision);
if !step.tool_call.is_empty() {
println!(" {}", step.tool_call);
}
}
println!("\n--- every exec row in policy_events ---");
for e in store.events(result.run_id)? {
if e.act == "exec" {
println!(
"step {:>3} {} {} (rule {:?}, layer {:?})",
e.step, e.kind, e.target, e.rule, e.layer
);
}
}
println!("\n--- index.js as the agent left it ---");
println!("{}", std::fs::read_to_string(root.join("index.js"))?);
assert_eq!(
std::fs::read_to_string(root.join("test.js"))?,
TESTS,
"the suite the run was judged against is the one it started with"
);
match result.outcome {
RunOutcome::Success { steps } => {
println!("\nverified by the project's own `npm test`, in {steps} steps")
}
other => println!("\ndid not reach a passing suite: {other:?}"),
}
Ok(())
}