Files
memind/scripts/check-goosed-v149-multiturn-provider.mjs
T
john 1d165bc6e3 feat(goose): complete v1.49 phase3 closeout gates and context fusion plan
Expand Goose v1.49 smoke coverage (memory chat, portal resume, page e2e,
multiturn provider), add canary memory policy lock, refresh baselines, and
ignore one-off evidence artifacts. Document headroom-based context runtime
fusion plan; include auth, scheduled-task, and wechat intent fixes on branch.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-09 18:11:39 +08:00

139 lines
4.5 KiB
JavaScript

#!/usr/bin/env node
/**
* Multi-turn provider smoke: update_provider then two consecutive /reply rounds.
*/
import { randomUUID } from 'node:crypto';
import { Readable } from 'node:stream';
import { Agent, fetch } from 'undici';
const port = process.env.GOOSE_V149_PORT || '18049';
const host = process.env.GOOSE_V149_HOST || '127.0.0.1';
const secret = process.env.GOOSE_SERVER__SECRET_KEY || 'local-v149-dev-secret';
const workingDir = process.env.GOOSE_V149_WORKING_DIR || process.cwd();
// Align with portal-resume default (300s); 90s was too tight under queued agent-run load.
const timeoutMs = Number(process.env.GOOSE_V149_MULTITURN_TIMEOUT_MS || 300_000);
const provider = process.env.GOOSE_V149_TEST_PROVIDER || 'custom_qwen3-flash';
const model = process.env.GOOSE_V149_TEST_MODEL || 'qwen3.8-flash';
const restartBetweenTurns = process.env.GOOSE_V149_MULTITURN_RESTART === '1';
const base = `https://${host}:${port}`;
const dispatcher = new Agent({ connect: { rejectUnauthorized: false } });
async function apiJson(pathname, body) {
const response = await fetch(`${base}${pathname}`, {
method: 'POST',
headers: {
'X-Secret-Key': secret,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
dispatcher,
});
const text = await response.text();
if (!response.ok) {
throw new Error(`${pathname} ${response.status}: ${text.slice(0, 400)}`);
}
return text.trim() ? JSON.parse(text) : {};
}
async function waitForTerminal(sessionId, requestId) {
const eventsResponse = await fetch(`${base}/sessions/${sessionId}/events`, {
headers: { Accept: 'text/event-stream', 'X-Secret-Key': secret },
dispatcher,
});
if (!eventsResponse.ok || !eventsResponse.body) {
throw new Error(`events ${eventsResponse.status}`);
}
const replyResponse = await fetch(`${base}/sessions/${sessionId}/reply`, {
method: 'POST',
headers: {
'X-Secret-Key': secret,
'Content-Type': 'application/json',
},
body: JSON.stringify({
request_id: requestId,
user_message: {
role: 'user',
created: Date.now(),
content: [{ type: 'text', text: `multiturn ping ${requestId.slice(0, 8)}` }],
metadata: { userVisible: true, agentVisible: true, displayText: 'multiturn ping' },
},
}),
dispatcher,
});
if (!replyResponse.ok) {
const text = await replyResponse.text().catch(() => '');
throw new Error(`reply ${replyResponse.status}: ${text.slice(0, 300)}`);
}
replyResponse.body?.cancel?.();
const reader = Readable.fromWeb(eventsResponse.body);
const decoder = new TextDecoder();
let buffer = '';
const deadline = Date.now() + timeoutMs;
for await (const chunk of reader) {
if (Date.now() > deadline) break;
buffer += decoder.decode(chunk, { stream: true });
const frames = buffer.split('\n\n');
buffer = frames.pop() ?? '';
for (const frame of frames) {
let data = '';
for (const line of frame.split('\n')) {
if (line.startsWith('data:')) data += line.slice(5).trim();
}
if (!data) continue;
let event;
try {
event = JSON.parse(data);
} catch {
continue;
}
const routingId = event.chat_request_id ?? event.request_id;
if (routingId && routingId !== requestId) continue;
if (event.type === 'Finish') return 'finish';
if (event.type === 'Error') {
return `error:${String(event.error ?? '').slice(0, 200)}`;
}
}
}
return 'timeout';
}
async function main() {
const session = await apiJson('/agent/start', { working_dir: workingDir });
if (!session?.id) throw new Error('missing session id');
await apiJson('/agent/update_provider', {
session_id: session.id,
provider,
model,
});
const outcomes = [];
for (let turn = 1; turn <= 2; turn += 1) {
const requestId = randomUUID();
const outcome = await waitForTerminal(session.id, requestId);
outcomes.push({ turn, requestId, outcome });
console.log(`GOOSE_V149_MULTITURN_EVENT: turn=${turn} outcome=${outcome}`);
if (outcome !== 'finish') {
throw new Error(`turn ${turn} failed: ${outcome}`);
}
if (restartBetweenTurns && turn === 1) {
await apiJson('/agent/restart', { session_id: session.id });
console.log('GOOSE_V149_MULTITURN_EVENT: restart=ok');
}
}
console.log(
`GOOSE_V149_MULTITURN_OK: session=${session.id} provider=${provider}/${model} `
+ `turns=${outcomes.length}`,
);
}
main().catch((error) => {
console.error(`GOOSE_V149_MULTITURN_FAIL: ${error.message}`);
process.exit(1);
});