stability: helper rows always show working before completed (the held row is sent first), agent-children tests order frames by arrival not by the clock and use generous bounds, request-flow waits for a slow host start

This commit is contained in:
Niko1221 committed 2026-10-16 16:05:42 +02:00
1 parent 9550318e34
commit 60bfe0693a
3 files changed
+16 -8

No files matched your search

+2
View File
@@ -353,6 +353,8 @@ export function createAgentBridge({ dataDir, componentsDir = null, bundledDir =
emitRow(child, true);
const fold = item => {
if (turn.finished) return;
// A helper that finishes before its throttled "working" row went out would show queued -> completed and never "working": send the held row first.
if (item.type === 'event' && item.event.type === 'turn/end' && child.rowPending) emitRow(child, true);
const { frames, row } = cp.consume(item);
for (const f of frames) emit(f);
if (item.type === 'event' && item.event.type === 'turn/end') { child.done = true; child.endedAt = Date.now(); emitRow(child, true); armSettle(); return; }
+9 -6
View File
@@ -109,10 +109,13 @@ test('two helpers run at the same time as two rows, the run ends when both have
assert.ok(history.at(-1).tokens > 0 && history.at(-1).durationMs > 0);
assert.match(history[1]?.description ?? history[0].description, /SCRIPT:slow 40/, 'the row names its task');
}
// concurrent: both were working before either finished
const firstDone = Math.min(...frames.filter(f => f.type === 'agent' && f.agent.status === 'completed').map(f => f.at));
const bothWorking = [...byId.values()].every(history => history.some(a => a.status === 'working'));
assert.ok(bothWorking && frames.filter(f => f.type === 'agent' && f.agent.status === 'working').every(f => f.at < firstDone + 50));
// concurrent: both were working before either finished (by the order the frames arrived in, not by the clock: a loaded computer delays a frame, it does not reorder it)
const agentFrames = frames.filter(f => f.type === 'agent');
const firstDone = agentFrames.findIndex(f => f.agent.status === 'completed');
for (const id of byId.keys()) {
const working = agentFrames.findIndex(f => f.agent.id === id && f.agent.status === 'working');
assert.ok(working >= 0 && working < firstDone, `helper ${id} was working before the first helper finished`);
}
assert.equal(frames.filter(f => f.type === 'agent-tool').length, 0, 'no card for the delegation tool itself');
assert.match(text(frames), /Noted/, 'the parent answered after the helpers reported back');
assert.equal(frames.at(-1).type, 'done');
@@ -161,14 +164,14 @@ test('Stop on the chat stops its helpers, ends the run quickly and starts no fur
const r = await rig(t);
const started = Date.now();
let stopped = null;
const { frames } = await r.run('stopper', 'SCRIPT:spawn 2 slow 400', { onFrame: f => {
const { frames } = await r.run('stopper', 'SCRIPT:spawn 2 slow 2000', { onFrame: f => {
if (f.type === 'agent' && f.agent.status === 'working' && !stopped) stopped = new Promise(res => setTimeout(res, 300)).then(() => r.bridge.stop('stopper'));
} });
const result = await stopped;
assert.equal(result.stopped, true);
const final = [...rows(frames).values()].map(h => h.at(-1).status);
assert.deepEqual(final, ['cancelled', 'cancelled']);
assert.ok(Date.now() - started < 12_000, 'seconds, not the 16 s the helpers would have taken');
assert.ok(Date.now() - started < 40_000, 'seconds, not the 80 s the helpers would have taken (a generous bound: a loaded computer is slow, never that slow)');
const done = frames.at(-1);
assert.equal(done.type, 'done'); assert.equal(done.cancelled, true);
assert.equal(r.bridge.isRunning('stopper'), false);
+5 -2
View File
@@ -77,7 +77,7 @@ test('a stream without usage never presents chunks as tokens', async () => {
} finally { await close(fixture); }
});
test('Serving cancellation stops active and queued demo requests; test keys enforce access', { timeout: 15000 }, async () => {
test('Serving cancellation stops active and queued demo requests; test keys enforce access', { timeout: 90000 }, async () => {
const home = await mkdtemp(join(tmpdir(), 'strata-request-host-'));
await mkdir(join(home, 'data'));
const id = 'qwen38-q4_k_m';
@@ -90,7 +90,10 @@ test('Serving cancellation stops active and queued demo requests; test keys enfo
return { status: res.status, data: await res.json() };
};
try {
for (let i = 0; i < 80; i++) { try { if ((await call('/api/health')).status === 200) break; } catch {} await wait(50); }
// A host started on a loaded computer can take many seconds to listen; wait for it, and say so if it never does (a 4 s wait made this test flaky).
let up = false;
for (let i = 0; i < 600 && !up; i++) { try { up = (await call('/api/health')).status === 200; } catch {} if (!up) await wait(50); }
assert.ok(up, 'the host did not start listening within 30 s');
const stream = () => fetch(base + '/api/chat/stream', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ message: 'Explain this test in detail' }) }).then(r => r.text());
const first = stream(); await wait(100); const second = stream(); await wait(100);
let rows = (await call('/api/serving')).data.requests;