Merge v3-t50: subagents use the models service, every test module in the bundle script (T50)

# Conflicts:
#	host/js/package.json
#	host/js/subagents-test-bundle.js
This commit is contained in:
2026-11-07 13:51:34 +00:00
9 changed files with 112 additions and 53 deletions
+22
View File
@@ -31809,6 +31809,28 @@ try {
assert(!(await harness.snapshot(LiveDoc, third.conversationId, BACKGROUND_CONTEXT))?.run, "Stop must end child");
assert((await reports()).length === 2, "Stop must suppress aborted report");
assert(text(await call({ action: "start", agent: "scout", task: "no", isolate: "worktree" })).includes("requires the workspaces extension"), "Worktree seam");
await fs.write("/src/sausage/agents/worker.md", "---\nname: worker\ndescription: Worker\nfamily: fast\ntools: []\n---\nWorker body");
await fs.mkdir("/src/sausage/.sausage");
await fs.mkdir("/src/sausage/.sausage/agents");
await fs.write("/src/sausage/.sausage/agents/worker.md", "---\nname: worker\naddTools: [hold]\n---\nOverlay body");
await fs.write("/src/sausage/agents/models.json", JSON.stringify({ default: { model: "faux/faux-1" }, fast: { model: "faux/faux-1", thinking: "low" } }));
await call({ action: "start", agent: "worker", task: "family one" });
const familyChild = (await children()).at(-1);
const firstAgent = await (await harness.conversation(familyChild.conversationId, BACKGROUND_CONTEXT)).agent(BACKGROUND_CONTEXT);
assert(firstAgent.model?.modelId === "faux-1" && firstAgent.thinkingLevel === "low", "Worker must resolve fast family");
assert(firstAgent.tools.map((tool) => tool.name).join(",") === "hold", "Project overlay must add a tool");
assert(firstAgent.instructions.includes("Worker body") && firstAgent.instructions.includes("Overlay body"), "Child must use overlaid instructions");
const provider2 = models.getProvider("faux"), template = models.getModels("faux")[0];
models.setProvider({ ...provider2, getModels: () => [template, { ...template, id: "second" }] });
await fs.write("/src/sausage/agents/models.json", JSON.stringify({ default: { model: "faux/faux-1" }, fast: { model: "faux/second", thinking: "high" } }));
await call({ action: "start", agent: "worker", task: "family two" });
const secondFamilyChild = (await children()).at(-1);
const secondAgent = await (await harness.conversation(secondFamilyChild.conversationId, BACKGROUND_CONTEXT)).agent(BACKGROUND_CONTEXT);
assert(secondAgent.model?.modelId === "second" && secondAgent.thinkingLevel === "high", "Next child must use edited family without restart");
globalThis.childPin = { provider: "faux", modelId: "faux-1" };
await call({ action: "start", agent: "worker", task: "pin" });
const pinnedChild = (await children()).at(-1);
assert((await (await harness.conversation(pinnedChild.conversationId, BACKGROUND_CONTEXT)).agent(BACKGROUND_CONTEXT)).model?.modelId === "faux-1", "models.pin must apply to subagents");
}
} finally {
gate.resolve();
+21
View File
@@ -106,5 +106,26 @@ try {
assert(!(await harness.snapshot(LiveDoc,third.conversationId,context))?.run,'Stop must end child');
assert((await reports()).length===2,'Stop must suppress aborted report');
assert(text(await call({action:'start',agent:'scout',task:'no',isolate:'worktree'})).includes('requires the workspaces extension'),'Worktree seam');
await fs.write('/src/sausage/agents/worker.md','---\nname: worker\ndescription: Worker\nfamily: fast\ntools: []\n---\nWorker body');
await fs.mkdir('/src/sausage/.sausage');await fs.mkdir('/src/sausage/.sausage/agents');
await fs.write('/src/sausage/.sausage/agents/worker.md','---\nname: worker\naddTools: [hold]\n---\nOverlay body');
await fs.write('/src/sausage/agents/models.json',JSON.stringify({default:{model:'faux/faux-1'},fast:{model:'faux/faux-1',thinking:'low'}}));
await call({action:'start',agent:'worker',task:'family one'});
const familyChild=(await children()).at(-1)!;
const firstAgent=await (await harness.conversation(familyChild.conversationId,context))!.agent(context);
assert(firstAgent.model?.modelId==='faux-1'&&firstAgent.thinkingLevel==='low','Worker must resolve fast family');
assert(firstAgent.tools.map((tool:any)=>tool.name).join(',')==='hold','Project overlay must add a tool');
assert(firstAgent.instructions.includes('Worker body')&&firstAgent.instructions.includes('Overlay body'),'Child must use overlaid instructions');
const provider=models.getProvider('faux')!, template=models.getModels('faux')[0];
models.setProvider({...provider,getModels:()=>[template,{...template,id:'second'}]});
await fs.write('/src/sausage/agents/models.json',JSON.stringify({default:{model:'faux/faux-1'},fast:{model:'faux/second',thinking:'high'}}));
await call({action:'start',agent:'worker',task:'family two'});
const secondFamilyChild=(await children()).at(-1)!;
const secondAgent=await (await harness.conversation(secondFamilyChild.conversationId,context))!.agent(context);
assert(secondAgent.model?.modelId==='second'&&secondAgent.thinkingLevel==='high','Next child must use edited family without restart');
globalThis.childPin={provider:'faux',modelId:'faux-1'};
await call({action:'start',agent:'worker',task:'pin'});
const pinnedChild=(await children()).at(-1)!;
assert((await (await harness.conversation(pinnedChild.conversationId,context))!.agent(context)).model?.modelId==='faux-1','models.pin must apply to subagents');
}
} finally {gate.resolve();parentGate.resolve();await loader.close();await harness.close(context);}
+18 -9
View File
@@ -40,22 +40,31 @@ fn prepare(dir: &Path) -> anyhow::Result<()> {
target.join("wasm32-wasip2/release/git.wasm"),
disk.join("bin/git.wasm"),
)?;
for directory in ["extensions/subagents", "extensions/core", "agents"] {
for directory in [
"extensions/subagents",
"extensions/models",
"extensions/core",
"agents",
] {
std::fs::create_dir_all(repo.join(directory))?;
}
for entry in std::fs::read_dir(source.join("extensions/subagents"))? {
let entry = entry?;
if entry.path().extension().is_some_and(|ext| ext == "ts") {
std::fs::copy(
entry.path(),
repo.join("extensions/subagents").join(entry.file_name()),
)?;
for extension in ["subagents", "models"] {
for entry in std::fs::read_dir(source.join("extensions").join(extension))? {
let entry = entry?;
if entry.path().extension().is_some_and(|ext| ext == "ts") {
std::fs::copy(
entry.path(),
repo.join("extensions")
.join(extension)
.join(entry.file_name()),
)?;
}
}
}
std::fs::create_dir_all(repo.join("extensions/probe"))?;
std::fs::write(
repo.join("extensions/probe/server.ts"),
"export default host => { globalThis.subagentService = host.need({name:'subagents'}); return {name:'probe'}; }",
"export default host => { globalThis.subagentService = host.need({name:'subagents'}); host.implement({name:'models.pin'}, {pin:async()=>globalThis.childPin}); return {name:'probe'}; }",
)?;
std::fs::copy(
source.join("extensions/core/agents.ts"),