ci: pin qa parity tool profile

This commit is contained in:
Peter Steinberger
2026-04-22 05:19:25 +01:00
parent 0cd785d8a5
commit 764bb310f7
4 changed files with 89 additions and 7 deletions

View File

@@ -1015,7 +1015,84 @@ describe("qa mock openai server", () => {
{
content: [
{
text: "Protocol note: delegated fanout complete. Alpha=ALPHA-OK. Beta=BETA-OK.",
text: "subagent-1: ok\nsubagent-2: ok",
},
],
},
],
});
});
it("completes subagent fanout from a continuation turn without tool output", async () => {
const server = await startQaMockOpenAiServer({
host: "127.0.0.1",
port: 0,
});
cleanups.push(async () => {
await server.stop();
});
const prompt =
"Subagent fanout synthesis check: delegate two bounded subagents sequentially, then report both results together.";
const spawn = await fetch(`${server.baseUrl}/v1/responses`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
stream: true,
tools: [SESSIONS_SPAWN_TOOL],
input: [{ role: "user", content: [{ type: "input_text", text: prompt }] }],
}),
});
expect(spawn.status).toBe(200);
expect(await spawn.text()).toContain('\\"label\\":\\"qa-fanout-alpha\\"');
const secondSpawn = await fetch(`${server.baseUrl}/v1/responses`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
stream: true,
tools: [SESSIONS_SPAWN_TOOL],
input: [
{ role: "user", content: [{ type: "input_text", text: prompt }] },
{
type: "function_call_output",
output:
'{"status":"accepted","childSessionKey":"agent:qa:subagent:alpha","note":"ALPHA-OK"}',
},
],
}),
});
expect(secondSpawn.status).toBe(200);
expect(await secondSpawn.text()).toContain('\\"label\\":\\"qa-fanout-beta\\"');
const phaseOnlyFinal = await fetch(`${server.baseUrl}/v1/responses`, {
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify({
stream: false,
tools: [SESSIONS_SPAWN_TOOL],
input: [
{
role: "user",
content: [
{
type: "input_text",
text: "Continue.",
},
],
},
],
}),
});
expect(phaseOnlyFinal.status).toBe(200);
expect(await phaseOnlyFinal.json()).toMatchObject({
output: [
{
content: [
{
text: "subagent-1: ok\nsubagent-2: ok",
},
],
},

View File

@@ -745,12 +745,10 @@ function buildAssistantText(
if (/fanout worker beta/i.test(prompt)) {
return "BETA-OK";
}
if (
/subagent fanout synthesis check/i.test(prompt) &&
toolOutput &&
scenarioState.subagentFanoutPhase >= 2
) {
return "Protocol note: delegated fanout complete. Alpha=ALPHA-OK. Beta=BETA-OK.";
const fanoutCompleteReply = "subagent-1: ok\nsubagent-2: ok";
if (scenarioState.subagentFanoutPhase === 2 && prompt) {
scenarioState.subagentFanoutPhase = 3;
return fanoutCompleteReply;
}
if (toolOutput && (/\bdelegate\b/i.test(prompt) || /subagent handoff/i.test(prompt))) {
const compact = toolOutput.replace(/\s+/g, " ").trim() || "no delegated output";

View File

@@ -63,6 +63,7 @@ describe("buildQaGatewayConfig", () => {
expect(cfg.plugins?.entries?.["qa-channel"]).toEqual({ enabled: true });
expect(cfg.plugins?.entries?.openai).toBeUndefined();
expect(cfg.gateway?.reload?.deferralTimeoutMs).toBe(1_000);
expect(cfg.tools?.profile).toBe("coding");
expect(cfg.channels?.["qa-channel"]).toMatchObject({
enabled: true,
baseUrl: "http://127.0.0.1:43124",

View File

@@ -180,6 +180,12 @@ export function buildQaGatewayConfig(params: {
memory: {
backend: "builtin",
},
tools: {
// The parity scenarios are code-agent contracts: they must always expose
// file, image, memory, and subagent tools even when the surrounding
// environment defaults to a messaging-only profile.
profile: "coding",
},
...(gatewayModels
? {
models: {