Merge branch 'v3' into v3-t85
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
//! Native HTTP smoke test for host pages and the listener's CLI options.
|
||||
//! Host pages remain reachable. World config replaces listener flags on the CLI.
|
||||
use anyhow::Result;
|
||||
use sausage::{
|
||||
api::{Listener, Options},
|
||||
@@ -44,14 +44,16 @@ fn listener_serves_host_pages() -> Result<()> {
|
||||
.output()?;
|
||||
assert!(help.status.success());
|
||||
let help = std::str::from_utf8(&help.stdout)?;
|
||||
for option in [
|
||||
"--api-listen",
|
||||
"127.0.0.1:8090",
|
||||
"--api-rp-id",
|
||||
"--api-origin",
|
||||
"http://localhost:8090",
|
||||
] {
|
||||
assert!(help.contains(option));
|
||||
for option in ["--api-listen", "--api-rp-id", "--api-origin"] {
|
||||
assert!(!help.contains(option));
|
||||
}
|
||||
let missing = dir.path().join("missing-world");
|
||||
let run = std::process::Command::new(env!("CARGO_BIN_EXE_sausage"))
|
||||
.arg("run")
|
||||
.arg(&missing)
|
||||
.output()?;
|
||||
assert!(!run.status.success());
|
||||
assert!(std::str::from_utf8(&run.stderr)?.contains("sausage init"));
|
||||
assert!(!missing.join("world").exists());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -79,10 +79,12 @@ fn setup(directory: &Path) -> Result<()> {
|
||||
&Path::new(env!("CARGO_MANIFEST_DIR")).join("../extensions"),
|
||||
&repo.join("extensions"),
|
||||
)?;
|
||||
std::fs::create_dir_all(repo.join("agents"))?;
|
||||
std::fs::write(
|
||||
repo.join("agents/models.json"),
|
||||
r#"{"default":{"model":"faux/faux-1"}}"#,
|
||||
crate::common::write_config(
|
||||
directory,
|
||||
"standing",
|
||||
serde_json::json!([
|
||||
{"name":"standing","description":"Primary","model":"faux/faux-1","prompt":"Primary agent"}
|
||||
]),
|
||||
)?;
|
||||
ensure!(
|
||||
Command::new("git")
|
||||
|
||||
+17
-1
@@ -8,6 +8,22 @@ use std::{
|
||||
};
|
||||
pub static BUILD: Mutex<()> = Mutex::new(());
|
||||
|
||||
/// Keep the real shared prompt in every roster fixture, with an ephemeral approval port.
|
||||
pub fn write_config(dir: &Path, standing: &str, roster: serde_json::Value) -> anyhow::Result<()> {
|
||||
let mut config: serde_json::Value =
|
||||
serde_yaml_ng::from_str(include_str!("../../../config/config.yaml"))?;
|
||||
config["standing"] = serde_json::json!(standing);
|
||||
config["roster"] = roster;
|
||||
config["listener"]["address"] = serde_json::json!("127.0.0.1:0");
|
||||
let folder = dir.join("world/etc/sausage");
|
||||
std::fs::create_dir_all(&folder)?;
|
||||
std::fs::write(
|
||||
folder.join("config.yaml"),
|
||||
serde_yaml_ng::to_string(&config)?,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn copy_tree(source: &Path, destination: &Path) -> anyhow::Result<()> {
|
||||
std::fs::create_dir_all(destination)?;
|
||||
for entry in std::fs::read_dir(source)? {
|
||||
@@ -39,7 +55,7 @@ pub fn bundle_key(repo: &Path) -> anyhow::Result<String> {
|
||||
"host",
|
||||
"vfs",
|
||||
"userland",
|
||||
"agents",
|
||||
"config",
|
||||
"skills",
|
||||
"extensions",
|
||||
"Cargo.toml",
|
||||
|
||||
+67
-13
@@ -44,6 +44,70 @@ fn write(root: &Path, path: &str, text: &str) -> anyhow::Result<()> {
|
||||
std::fs::write(path, text)?;
|
||||
Ok(())
|
||||
}
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn init_seeds_config_tools_and_one_source_commit() -> anyhow::Result<()> {
|
||||
let dir = tempfile::tempdir()?;
|
||||
let bundle = dir.path().join("bundle");
|
||||
let target = crate::common::build_git();
|
||||
std::fs::create_dir_all(bundle.join("bin"))?;
|
||||
std::fs::copy(
|
||||
target.join("wasm32-wasip2/release/git.wasm"),
|
||||
bundle.join("bin/git.wasm"),
|
||||
)?;
|
||||
write(&bundle, "src/sausage/README.md", "World source")?;
|
||||
write(
|
||||
&bundle,
|
||||
"etc/sausage/config.yaml",
|
||||
include_str!("../../../config/config.yaml"),
|
||||
)?;
|
||||
let destination = dir.path().join("new-world");
|
||||
let initialized = std::process::Command::new(env!("CARGO_BIN_EXE_sausage"))
|
||||
.arg("init")
|
||||
.arg(&destination)
|
||||
.arg("--userland")
|
||||
.arg(&bundle)
|
||||
.env("PI_PROXY_BASE_URL", "https://proxy.test")
|
||||
.env("PI_PROXY_API_KEY", "fixture-key")
|
||||
.stdin(std::process::Stdio::null())
|
||||
.output()?;
|
||||
assert!(
|
||||
initialized.status.success(),
|
||||
"{}",
|
||||
String::from_utf8_lossy(&initialized.stderr)
|
||||
);
|
||||
let world = World::open(&destination)?;
|
||||
assert!(destination.join("world/bin/git").exists());
|
||||
let config = sausage::harness::config::Config::read(&world.root).await?;
|
||||
assert_eq!(config.listener.origin, "http://localhost:8090");
|
||||
assert_eq!(config.proxy.base_url, "https://proxy.test");
|
||||
assert_eq!(config.proxy.api_key, "fixture-key");
|
||||
let source = destination.join("world/src/sausage");
|
||||
for (args, expected) in [
|
||||
(vec!["symbolic-ref", "HEAD"], "refs/heads/main"),
|
||||
(vec!["rev-list", "--count", "HEAD"], "1"),
|
||||
] {
|
||||
let result = std::process::Command::new("git")
|
||||
.args(args)
|
||||
.current_dir(&source)
|
||||
.output()?;
|
||||
assert!(
|
||||
result.status.success(),
|
||||
"{}",
|
||||
String::from_utf8_lossy(&result.stderr)
|
||||
);
|
||||
assert_eq!(String::from_utf8(result.stdout)?.trim(), expected);
|
||||
}
|
||||
assert!(
|
||||
World::init(&destination, &bundle)
|
||||
.await
|
||||
.err()
|
||||
.unwrap()
|
||||
.to_string()
|
||||
.contains("already exists")
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn core_extensions() -> anyhow::Result<()> {
|
||||
let repo = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap();
|
||||
@@ -66,14 +130,9 @@ async fn core_extensions() -> anyhow::Result<()> {
|
||||
)?;
|
||||
}
|
||||
write(
|
||||
&source,
|
||||
"agents/worker.md",
|
||||
"---\nname: worker\ndescription: Worker\nfamily: fast\ntools: [read]\n---\nWorker body",
|
||||
)?;
|
||||
write(
|
||||
&source,
|
||||
"agents/models.json",
|
||||
r#"{"default":{"model":"faux/faux-1"},"fast":{"model":"faux/faux-1","thinking":"low"}}"#,
|
||||
&disk,
|
||||
"etc/sausage/config.yaml",
|
||||
include_str!("../../../config/config.yaml"),
|
||||
)?;
|
||||
write(
|
||||
&source,
|
||||
@@ -93,11 +152,6 @@ export default host=>{host.implement({name:'models.pin'},{pin:async()=>globalThi
|
||||
write(&disk, "project/.git/HEAD", "ref: refs/heads/main")?;
|
||||
write(&disk, "project/AGENTS.md", "Root instructions")?;
|
||||
write(&disk, "project/sub/AGENTS.md", "Nested instructions")?;
|
||||
write(
|
||||
&disk,
|
||||
"project/.sausage/agents/worker.md",
|
||||
"---\nname: worker\naddTools: [write]\n---\nProject body",
|
||||
)?;
|
||||
write(
|
||||
&disk,
|
||||
"project/.sausage/skills/project/SKILL.md",
|
||||
|
||||
+17
-27
@@ -50,33 +50,23 @@ fn prepare(dir: &Path) -> anyhow::Result<()> {
|
||||
repo.join("extensions/probe/server.ts"),
|
||||
"export default host => { globalThis.dreamHost=host; globalThis.dreamService=host.need({name:'dream'}); globalThis.dreamSubagents=host.need({name:'subagents'}); globalThis.dreamAutomations=host.need({name:'automations'}); return {name:'probe'}; }",
|
||||
)?;
|
||||
std::fs::create_dir_all(repo.join("agents"))?;
|
||||
std::fs::write(
|
||||
repo.join("agents/models.json"),
|
||||
r#"{"default":{"model":"faux/faux-1"},"fast":{"model":"faux/faux-1"}}"#,
|
||||
)?;
|
||||
for role in ["ultimate", "coordinator", "worker", "reviewer"] {
|
||||
let denial = if role == "worker" || role == "reviewer" {
|
||||
"denyTools: [subagent]\n"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
std::fs::write(
|
||||
repo.join(format!("agents/{role}.md")),
|
||||
format!(
|
||||
"---\nname: {role}\ndescription: P3 {role}\nisolate: none\n{denial}{}---\nP3 role:{role}\n",
|
||||
if role == "ultimate" {
|
||||
"resident: true\n"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
),
|
||||
)?;
|
||||
}
|
||||
std::fs::copy(
|
||||
source.join("agents/dream-pass.md"),
|
||||
repo.join("agents/dream-pass.md"),
|
||||
)?;
|
||||
let mut roster: Vec<serde_json::Value> = ["standing", "coordinator", "worker", "reviewer"].into_iter().map(|role| serde_json::json!({
|
||||
"name":role, "description":format!("P3 {role}"), "model":"faux/faux-1",
|
||||
"isolate":"none", "resident":role == "standing", "prompt":format!("P3 role:{role}"),
|
||||
"denyTools":if role == "worker" || role == "reviewer" {vec!["subagent"]} else {vec![]}
|
||||
})).collect();
|
||||
let defaults: serde_json::Value =
|
||||
serde_yaml_ng::from_str(include_str!("../../../config/config.yaml"))?;
|
||||
let mut pass = defaults["roster"]
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.find(|entry| entry["name"] == "dream-pass")
|
||||
.unwrap()
|
||||
.clone();
|
||||
pass["model"] = serde_json::json!("faux/faux-1");
|
||||
roster.push(pass);
|
||||
crate::common::write_config(dir, "standing", serde_json::json!(roster))?;
|
||||
let demo = disk.join("src/demo");
|
||||
std::fs::create_dir_all(&demo)?;
|
||||
std::fs::write(demo.join("README.md"), "P3 demo\n")?;
|
||||
|
||||
@@ -146,7 +146,7 @@ async fn proxy_child() -> anyhow::Result<()> {
|
||||
};
|
||||
let dir = std::path::Path::new(&dir);
|
||||
let source = format!(
|
||||
"globalThis.proxyOnly = true; const proxy = Deno.core.ops.op_model_proxy(); if (proxy.base_url !== 'https://proxy.invalid' || proxy.api_key !== 'faux-private-token') throw new Error('Wrong proxy item');\n{}",
|
||||
"globalThis.proxyOnly = true; const proxy = await Deno.core.ops.op_model_proxy(); if (proxy.base_url !== 'https://proxy.invalid' || proxy.api_key !== 'faux-private-token') throw new Error('Wrong proxy config');\n{}",
|
||||
include_str!("../../js/durable-test-bundle.js")
|
||||
);
|
||||
run_module(
|
||||
@@ -158,7 +158,7 @@ async fn proxy_child() -> anyhow::Result<()> {
|
||||
let error = run_module(
|
||||
World::open(dir)?,
|
||||
sausage::process::exec(World::open(dir)?)?,
|
||||
"throw new Error(Deno.core.ops.op_model_proxy().api_key)",
|
||||
"throw new Error((await Deno.core.ops.op_model_proxy()).api_key)",
|
||||
)
|
||||
.await
|
||||
.unwrap_err();
|
||||
@@ -168,8 +168,14 @@ async fn proxy_child() -> anyhow::Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn proxy_bootstrap_once() -> anyhow::Result<()> {
|
||||
fn proxy_config_ignores_environment() -> anyhow::Result<()> {
|
||||
let dir = std::env::temp_dir().join(format!("sausage-durable-proxy-{}", std::process::id()));
|
||||
let settings = dir.join("world/etc/sausage");
|
||||
std::fs::create_dir_all(&settings)?;
|
||||
std::fs::write(
|
||||
settings.join("config.yaml"),
|
||||
"proxy:\n base_url: https://proxy.invalid\n api_key: faux-private-token\n",
|
||||
)?;
|
||||
for start in 0..2 {
|
||||
let status = std::process::Command::new(std::env::current_exe()?)
|
||||
.args(["--exact", "durable::proxy_child", "--nocapture"])
|
||||
@@ -199,7 +205,7 @@ fn proxy_bootstrap_once() -> anyhow::Result<()> {
|
||||
.iter()
|
||||
.filter(|i| i.name == "llm-proxy")
|
||||
.count(),
|
||||
1
|
||||
0
|
||||
);
|
||||
for file in [
|
||||
"host/cred.sqlite",
|
||||
|
||||
@@ -26,21 +26,37 @@ fn prepare(dir: &Path) -> anyhow::Result<()> {
|
||||
repo.join("extensions/probe/server.ts"),
|
||||
"export default host => { globalThis.testHost = host; globalThis.subagentService = host.need({name:'subagents'}); globalThis.messageService = host.need({name:'messages'}); globalThis.lineageService = host.need({name:'lineage'}); host.implement({name:'conversations'}, {summaryModel: async () => globalThis.summaryRef, concludedIn: async () => new Set(), generatedTitle: async () => {}, setStanding: async () => {}, setAutomaticSummaries: async () => {}}); return {name:'probe'}; }",
|
||||
)?;
|
||||
std::fs::write(
|
||||
repo.join("agents/models.json"),
|
||||
r#"{"default":{"model":"faux/faux-1"}}"#,
|
||||
)?;
|
||||
std::fs::write(
|
||||
repo.join("agents/scout.md"),
|
||||
"---\nname: scout\ndescription: Search\nresident: true\ndenyTools: [subagent]\nisolate: none\n---\nScout instructions\n",
|
||||
)?;
|
||||
std::fs::write(
|
||||
repo.join("agents/ultimate.md"),
|
||||
"---\nname: ultimate\ndescription: Primary\nresident: true\nisolate: none\n---\nPrimary agent\n",
|
||||
)?;
|
||||
std::fs::write(
|
||||
repo.join("agents/coordinator.md"),
|
||||
"---\nname: coordinator\ndescription: Coordinate\nisolate: none\n---\nCoordinator agent\n",
|
||||
crate::common::write_config(
|
||||
dir,
|
||||
"standing",
|
||||
serde_json::json!([
|
||||
{
|
||||
"name": "scout",
|
||||
"description": "Search",
|
||||
"resident": true,
|
||||
"denyTools": [
|
||||
"subagent"
|
||||
],
|
||||
"isolate": "none",
|
||||
"model": "faux/faux-1",
|
||||
"prompt": "Scout instructions"
|
||||
},
|
||||
{
|
||||
"name": "standing",
|
||||
"description": "Primary",
|
||||
"resident": true,
|
||||
"isolate": "none",
|
||||
"model": "faux/faux-1",
|
||||
"prompt": "Primary agent"
|
||||
},
|
||||
{
|
||||
"name": "coordinator",
|
||||
"description": "Coordinate",
|
||||
"isolate": "none",
|
||||
"model": "faux/faux-1",
|
||||
"prompt": "Coordinator agent"
|
||||
}
|
||||
]),
|
||||
)?;
|
||||
for args in [
|
||||
vec!["init", "-b", "main"],
|
||||
|
||||
@@ -76,21 +76,37 @@ fn prepare(dir: &Path) -> anyhow::Result<()> {
|
||||
repo.join("extensions/probe/server.ts"),
|
||||
"export default host => { globalThis.testHost = host; globalThis.subagentService = host.need({name:'subagents'}); globalThis.messageService = host.need({name:'messages'}); globalThis.lineageService = host.need({name:'lineage'}); host.implement({name:'conversations'}, {summaryModel: async () => globalThis.summaryRef, concludedIn: async () => new Set(), generatedTitle: async () => {}, setStanding: async () => {}, setAutomaticSummaries: async () => {}}); return {name:'probe'}; }",
|
||||
)?;
|
||||
std::fs::write(
|
||||
repo.join("agents/models.json"),
|
||||
r#"{"default":{"model":"faux/faux-1"}}"#,
|
||||
)?;
|
||||
std::fs::write(
|
||||
repo.join("agents/scout.md"),
|
||||
"---\nname: scout\ndescription: Search\nresident: true\ndenyTools: [subagent]\nisolate: none\n---\nScout instructions\n",
|
||||
)?;
|
||||
std::fs::write(
|
||||
repo.join("agents/ultimate.md"),
|
||||
"---\nname: ultimate\ndescription: Primary\nresident: true\nisolate: none\n---\nPrimary agent\n",
|
||||
)?;
|
||||
std::fs::write(
|
||||
repo.join("agents/coordinator.md"),
|
||||
"---\nname: coordinator\ndescription: Coordinate\nisolate: none\n---\nCoordinator agent\n",
|
||||
crate::common::write_config(
|
||||
dir,
|
||||
"standing",
|
||||
serde_json::json!([
|
||||
{
|
||||
"name": "scout",
|
||||
"description": "Search",
|
||||
"resident": true,
|
||||
"denyTools": [
|
||||
"subagent"
|
||||
],
|
||||
"isolate": "none",
|
||||
"model": "faux/faux-1",
|
||||
"prompt": "Scout instructions"
|
||||
},
|
||||
{
|
||||
"name": "standing",
|
||||
"description": "Primary",
|
||||
"resident": true,
|
||||
"isolate": "none",
|
||||
"model": "faux/faux-1",
|
||||
"prompt": "Primary agent"
|
||||
},
|
||||
{
|
||||
"name": "coordinator",
|
||||
"description": "Coordinate",
|
||||
"isolate": "none",
|
||||
"model": "faux/faux-1",
|
||||
"prompt": "Coordinator agent"
|
||||
}
|
||||
]),
|
||||
)?;
|
||||
for args in [
|
||||
vec!["init", "-b", "main"],
|
||||
|
||||
@@ -114,12 +114,51 @@ fn channel_with_closed_stdin() -> anyhow::Result<()> {
|
||||
#[test]
|
||||
fn cli_stays_up_after_stdin_eof() -> anyhow::Result<()> {
|
||||
let dir = tempfile::tempdir()?;
|
||||
World::open(dir.path())?;
|
||||
let disk = dir.path().join("world");
|
||||
let target = crate::common::build_git();
|
||||
std::fs::create_dir_all(disk.join("bin"))?;
|
||||
for binary in ["git", "tar"] {
|
||||
std::fs::copy(
|
||||
target.join(format!("wasm32-wasip2/release/{binary}.wasm")),
|
||||
disk.join(format!("bin/{binary}.wasm")),
|
||||
)?;
|
||||
}
|
||||
let repo = disk.join("src/sausage");
|
||||
let source = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../extensions");
|
||||
for extension in ["core", "models", "subagents"] {
|
||||
crate::common::copy_tree(
|
||||
&source.join(extension),
|
||||
&repo.join("extensions").join(extension),
|
||||
)?;
|
||||
}
|
||||
crate::common::write_config(
|
||||
dir.path(),
|
||||
"console",
|
||||
serde_json::json!([
|
||||
{"name":"console","description":"Standing console","model":"openai/gpt-6.1-sol","prompt":"Keep the user conversation"}
|
||||
]),
|
||||
)?;
|
||||
for args in [
|
||||
vec!["init", "-b", "main"],
|
||||
vec!["add", "."],
|
||||
vec!["commit", "--no-gpg-sign", "-m", "EOF fixture"],
|
||||
] {
|
||||
assert!(
|
||||
Command::new("git")
|
||||
.args(args)
|
||||
.current_dir(&repo)
|
||||
.env("GIT_AUTHOR_NAME", "Test")
|
||||
.env("GIT_AUTHOR_EMAIL", "test@example.com")
|
||||
.env("GIT_COMMITTER_NAME", "Test")
|
||||
.env("GIT_COMMITTER_EMAIL", "test@example.com")
|
||||
.status()?
|
||||
.success()
|
||||
);
|
||||
}
|
||||
let mut child = Command::new(env!("CARGO_BIN_EXE_sausage"))
|
||||
.arg("run")
|
||||
.arg(dir.path())
|
||||
.args(["--api-listen", "127.0.0.1:0"])
|
||||
.env("PI_PROXY_BASE_URL", "https://proxy.invalid")
|
||||
.env("PI_PROXY_API_KEY", "faux-token")
|
||||
.env_remove("SAUSAGE_USERLAND")
|
||||
.stdin(Stdio::null())
|
||||
.spawn()?;
|
||||
@@ -134,7 +173,7 @@ fn cli_stays_up_after_stdin_eof() -> anyhow::Result<()> {
|
||||
r.get::<_, i64>(0)
|
||||
})
|
||||
.unwrap_or(0)
|
||||
> 0
|
||||
> 1
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
+31
-15
@@ -60,21 +60,37 @@ fn prepare(dir: &Path) -> anyhow::Result<()> {
|
||||
source.join("extensions/core/agents.ts"),
|
||||
repo.join("extensions/core/agents.ts"),
|
||||
)?;
|
||||
std::fs::write(
|
||||
repo.join("agents/models.json"),
|
||||
r#"{"default":{"model":"faux/faux-1"}}"#,
|
||||
)?;
|
||||
std::fs::write(
|
||||
repo.join("agents/scout.md"),
|
||||
"---\nname: scout\ndescription: Search\nresident: true\ndenyTools: [subagent]\nisolate: none\n---\nScout instructions\n",
|
||||
)?;
|
||||
std::fs::write(
|
||||
repo.join("agents/ultimate.md"),
|
||||
"---\nname: ultimate\ndescription: Primary\nresident: true\nisolate: none\n---\nPrimary agent\n",
|
||||
)?;
|
||||
std::fs::write(
|
||||
repo.join("agents/coordinator.md"),
|
||||
"---\nname: coordinator\ndescription: Coordinate\nmodel: faux/coordinator\nisolate: none\n---\nCoordinator agent\n",
|
||||
crate::common::write_config(
|
||||
dir,
|
||||
"standing",
|
||||
serde_json::json!([
|
||||
{
|
||||
"name": "scout",
|
||||
"description": "Search",
|
||||
"resident": true,
|
||||
"denyTools": [
|
||||
"subagent"
|
||||
],
|
||||
"isolate": "none",
|
||||
"model": "faux/faux-1",
|
||||
"prompt": "Scout instructions"
|
||||
},
|
||||
{
|
||||
"name": "standing",
|
||||
"description": "Primary",
|
||||
"resident": true,
|
||||
"isolate": "none",
|
||||
"model": "faux/faux-1",
|
||||
"prompt": "Primary agent"
|
||||
},
|
||||
{
|
||||
"name": "coordinator",
|
||||
"description": "Coordinate",
|
||||
"isolate": "none",
|
||||
"model": "faux/coordinator",
|
||||
"prompt": "Coordinator agent"
|
||||
}
|
||||
]),
|
||||
)?;
|
||||
for args in [
|
||||
vec!["init", "-b", "main"],
|
||||
|
||||
+8
-9
@@ -10,7 +10,7 @@ fn bundle() -> anyhow::Result<PathBuf> {
|
||||
let key = crate::common::bundle_key(&repo)?;
|
||||
let stamp = bundle.join(".source-key");
|
||||
if !bundle.join("bin/sh.wasm").exists()
|
||||
|| !bundle.join("src/sausage/agents/models.json").exists()
|
||||
|| !bundle.join("etc/sausage/config.yaml").exists()
|
||||
|| std::fs::read_to_string(&stamp).ok().as_deref() != Some(key.as_str())
|
||||
{
|
||||
if stamp.exists() {
|
||||
@@ -33,12 +33,11 @@ async fn bundle_copy_and_real_shell() -> anyhow::Result<()> {
|
||||
let repo = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("..");
|
||||
let bundle = bundle()?;
|
||||
let dir = tempfile::tempdir()?;
|
||||
let world = World::open_with_userland(dir.path(), Some(&bundle))?;
|
||||
let world = World::init(dir.path(), &bundle).await?;
|
||||
assert!(world.root.stat("/bin/sh.wasm", true).await?.size > 0);
|
||||
let snapshot = dir.path().join("world/src/sausage");
|
||||
for path in [
|
||||
"agents/models.json",
|
||||
"agents/ultimate.md",
|
||||
"config/config.yaml",
|
||||
"skills/README.md",
|
||||
"extensions/README.md",
|
||||
] {
|
||||
@@ -58,11 +57,11 @@ async fn bundle_copy_and_real_shell() -> anyhow::Result<()> {
|
||||
Ok(String::from_utf8(output.stdout)?.trim().to_owned())
|
||||
};
|
||||
let message = git(&snapshot, &["log", "-1", "--format=%s"])?;
|
||||
let revision = message.strip_prefix("Bundle source ").unwrap();
|
||||
for path in ["agents", "skills", "extensions"] {
|
||||
assert_eq!(message, "Initial world source");
|
||||
for path in ["config", "skills", "extensions"] {
|
||||
assert_eq!(
|
||||
git(&snapshot, &["rev-parse", &format!("HEAD:{path}")])?,
|
||||
git(&repo, &["rev-parse", &format!("{revision}:{path}")])?,
|
||||
git(&repo, &["rev-parse", &format!("HEAD:{path}")])?,
|
||||
"bundled {path} must match the source commit"
|
||||
);
|
||||
}
|
||||
@@ -197,7 +196,7 @@ async fn bundle_copy_and_real_shell() -> anyhow::Result<()> {
|
||||
);
|
||||
let history = String::from_utf8(history)?;
|
||||
assert_eq!(history.lines().count(), 1);
|
||||
assert!(history.contains("Bundle source "));
|
||||
assert!(history.contains("Initial world source"));
|
||||
let mut status = Vec::new();
|
||||
assert_eq!(
|
||||
exec.exec(
|
||||
@@ -312,7 +311,7 @@ impl sausage::exec::Exec for LoaderTiming {
|
||||
async fn bundled_extensions_load() -> anyhow::Result<()> {
|
||||
let bundle = bundle()?;
|
||||
let dir = tempfile::tempdir()?;
|
||||
let world = World::open_with_userland(dir.path(), Some(&bundle))?;
|
||||
let world = World::init(dir.path(), &bundle).await?;
|
||||
let root = world.root.clone();
|
||||
let exec = sausage::process::exec(World {
|
||||
root: world.root.clone(),
|
||||
|
||||
+6
-23
@@ -61,29 +61,12 @@ fn prepare(dir: &Path, all_extensions: bool) -> anyhow::Result<()> {
|
||||
repo.join("extensions/probe/server.ts"),
|
||||
"export default host => { globalThis.p3Models=host.need({name:'models'}); globalThis.p3Subagents=host.need({name:'subagents'}); globalThis.p3Workspaces=host.need({name:'workspaces'}); globalThis.p3Host=host; return {name:'probe'}; }",
|
||||
)?;
|
||||
std::fs::create_dir_all(repo.join("agents"))?;
|
||||
std::fs::write(
|
||||
repo.join("agents/models.json"),
|
||||
r#"{"default":{"model":"faux/faux-1"}}"#,
|
||||
)?;
|
||||
for role in ["ultimate", "coordinator", "worker", "reviewer"] {
|
||||
let denial = if role == "worker" || role == "reviewer" {
|
||||
"denyTools: [subagent]\n"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
std::fs::write(
|
||||
repo.join(format!("agents/{role}.md")),
|
||||
format!(
|
||||
"---\nname: {role}\ndescription: P3 {role}\nisolate: none\n{denial}{}---\nP3 role:{role}\n",
|
||||
if role == "ultimate" {
|
||||
"resident: true\n"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
),
|
||||
)?;
|
||||
}
|
||||
let roster: Vec<serde_json::Value> = ["standing", "coordinator", "worker", "reviewer"].into_iter().map(|role| serde_json::json!({
|
||||
"name":role, "description":format!("P3 {role}"), "model":"faux/faux-1",
|
||||
"isolate":"none", "resident":role == "standing", "prompt":format!("P3 role:{role}"),
|
||||
"denyTools":if role == "worker" || role == "reviewer" {vec!["subagent"]} else {vec![]}
|
||||
})).collect();
|
||||
crate::common::write_config(dir, "standing", serde_json::json!(roster))?;
|
||||
std::fs::write(
|
||||
repo.join("settings.json"),
|
||||
r#"{"lander":{"checks":["sh -c ': > /check-running; until test -f /check-release; do :; done; test -f feature.txt'"]}}"#,
|
||||
|
||||
+15
-7
@@ -73,6 +73,19 @@ fn prepare(dir: &Path) -> anyhow::Result<PathBuf> {
|
||||
}
|
||||
World::open_with_userland(dir, Some(&bundle))?;
|
||||
let repo = dir.join("world/src/sausage");
|
||||
for extension in ["core", "models", "subagents"] {
|
||||
crate::common::copy_tree(
|
||||
&source.join("extensions").join(extension),
|
||||
&repo.join("extensions").join(extension),
|
||||
)?;
|
||||
}
|
||||
crate::common::write_config(
|
||||
dir,
|
||||
"console",
|
||||
serde_json::json!([
|
||||
{"name":"console","description":"P4 console","model":"openai/gpt-6.1-sol","prompt":"Build the requested programs","tools":["bash"]}
|
||||
]),
|
||||
)?;
|
||||
fs::create_dir_all(repo.join("extensions/p4"))?;
|
||||
fs::write(
|
||||
repo.join("extensions/p4/server.ts"),
|
||||
@@ -102,18 +115,13 @@ impl Drop for Running {
|
||||
let _ = self.0.wait();
|
||||
}
|
||||
}
|
||||
fn round(dir: &Path, bundle: &Path, number: usize) -> anyhow::Result<()> {
|
||||
fn round(dir: &Path, number: usize) -> anyhow::Result<()> {
|
||||
let stdout = dir.join(format!("round-{number}.stdout"));
|
||||
let stderr = dir.join(format!("round-{number}.stderr"));
|
||||
let mut child = Running(
|
||||
Command::new(env!("CARGO_BIN_EXE_sausage"))
|
||||
.arg("run")
|
||||
.arg(dir)
|
||||
.arg("--userland")
|
||||
.arg(bundle)
|
||||
.args(["--api-listen", "127.0.0.1:0"])
|
||||
.env("PI_PROXY_BASE_URL", "https://proxy.invalid")
|
||||
.env("PI_PROXY_API_KEY", "faux-token")
|
||||
.env_remove("CREDENTIALS_DIRECTORY")
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(fs::File::create(&stdout)?)
|
||||
@@ -229,7 +237,7 @@ fn agent_builds_rust_and_node_twice() -> anyhow::Result<()> {
|
||||
let bundle = prepare(dir.path())?;
|
||||
let mut first_hits = 0;
|
||||
for number in 1..=2 {
|
||||
let outcome = round(dir.path(), &bundle, number);
|
||||
let outcome = round(dir.path(), number);
|
||||
let cleanup = boxes_removed(dir.path(), &bundle);
|
||||
if outcome.is_err() {
|
||||
for stream in ["stdout", "stderr"] {
|
||||
|
||||
@@ -96,6 +96,12 @@ async fn provider_transport_and_prices() -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
});
|
||||
let dir = tempfile::tempdir()?;
|
||||
let config = dir.path().join("world/etc/sausage");
|
||||
std::fs::create_dir_all(&config)?;
|
||||
std::fs::write(
|
||||
config.join("config.yaml"),
|
||||
serde_json::to_vec(&json!({"proxy":proxy}))?,
|
||||
)?;
|
||||
let source = format!(
|
||||
"globalThis.testProxy = {proxy};\n{}",
|
||||
include_str!("../../js/proxy-models-test-bundle.js")
|
||||
|
||||
@@ -51,13 +51,22 @@ fn prepare(dir: &Path) -> anyhow::Result<()> {
|
||||
source.join("extensions/core/agents.ts"),
|
||||
repo.join("extensions/core/agents.ts"),
|
||||
)?;
|
||||
std::fs::write(
|
||||
repo.join("agents/models.json"),
|
||||
r#"{"default":{"model":"faux/faux-1"}}"#,
|
||||
)?;
|
||||
std::fs::write(
|
||||
repo.join("agents/scout.md"),
|
||||
"---\nname: scout\ndescription: Search\nresident: true\ndenyTools: [subagent]\nisolate: none\n---\nScout instructions\n",
|
||||
crate::common::write_config(
|
||||
dir,
|
||||
"scout",
|
||||
serde_json::json!([
|
||||
{
|
||||
"name": "scout",
|
||||
"description": "Search",
|
||||
"resident": true,
|
||||
"denyTools": [
|
||||
"subagent"
|
||||
],
|
||||
"isolate": "none",
|
||||
"model": "faux/faux-1",
|
||||
"prompt": "Scout instructions"
|
||||
}
|
||||
]),
|
||||
)?;
|
||||
for args in [
|
||||
vec!["init", "-b", "main"],
|
||||
|
||||
+31
-15
@@ -62,21 +62,37 @@ fn prepare(dir: &Path) -> anyhow::Result<()> {
|
||||
source.join("extensions/conversations/api.ts"),
|
||||
repo.join("extensions/conversations/api.ts"),
|
||||
)?;
|
||||
std::fs::write(
|
||||
repo.join("agents/models.json"),
|
||||
r#"{"default":{"model":"faux/faux-1"}}"#,
|
||||
)?;
|
||||
std::fs::write(
|
||||
repo.join("agents/scout.md"),
|
||||
"---\nname: scout\ndescription: Search\nresident: true\ndenyTools: [subagent]\nisolate: none\n---\nScout instructions\n",
|
||||
)?;
|
||||
std::fs::write(
|
||||
repo.join("agents/ultimate.md"),
|
||||
"---\nname: ultimate\ndescription: Primary\nresident: true\nisolate: none\n---\nPrimary agent\n",
|
||||
)?;
|
||||
std::fs::write(
|
||||
repo.join("agents/coordinator.md"),
|
||||
"---\nname: coordinator\ndescription: Coordinate\nisolate: none\n---\nCoordinator agent\n",
|
||||
crate::common::write_config(
|
||||
dir,
|
||||
"standing",
|
||||
serde_json::json!([
|
||||
{
|
||||
"name": "scout",
|
||||
"description": "Search",
|
||||
"resident": true,
|
||||
"denyTools": [
|
||||
"subagent"
|
||||
],
|
||||
"isolate": "none",
|
||||
"model": "faux/faux-1",
|
||||
"prompt": "Scout instructions"
|
||||
},
|
||||
{
|
||||
"name": "standing",
|
||||
"description": "Primary",
|
||||
"resident": true,
|
||||
"isolate": "none",
|
||||
"model": "faux/faux-1",
|
||||
"prompt": "Primary agent"
|
||||
},
|
||||
{
|
||||
"name": "coordinator",
|
||||
"description": "Coordinate",
|
||||
"isolate": "none",
|
||||
"model": "faux/faux-1",
|
||||
"prompt": "Coordinator agent"
|
||||
}
|
||||
]),
|
||||
)?;
|
||||
for args in [
|
||||
vec!["init", "-b", "main"],
|
||||
|
||||
@@ -70,14 +70,12 @@ fn prepare(dir: &Path) -> anyhow::Result<()> {
|
||||
repo.join("extensions/probe/server.ts"),
|
||||
"export default host => { globalThis.workspaceService=host.need({name:'workspaces'}); globalThis.workspaceHost=host; return {name:'probe'}; }",
|
||||
)?;
|
||||
std::fs::create_dir_all(repo.join("agents"))?;
|
||||
std::fs::write(
|
||||
repo.join("agents/models.json"),
|
||||
r#"{"default":{"model":"faux/faux-1"}}"#,
|
||||
)?;
|
||||
std::fs::write(
|
||||
repo.join("agents/worker.md"),
|
||||
"---\nname: worker\ndescription: Implement\nisolate: worktree\n---\nImplement\n",
|
||||
crate::common::write_config(
|
||||
dir,
|
||||
"worker",
|
||||
serde_json::json!([
|
||||
{"name":"worker","description":"Implement","model":"faux/faux-1","isolate":"worktree","prompt":"Implement"}
|
||||
]),
|
||||
)?;
|
||||
std::fs::write(
|
||||
repo.join("settings.json"),
|
||||
|
||||
Reference in New Issue
Block a user