54 lines
2.8 KiB
JavaScript
54 lines
2.8 KiB
JavaScript
#!/usr/bin/env node
|
|
// Mock choir: ask, get a proposal, approve it, see the tempo move.
|
|
const ws = new WebSocket('ws://127.0.0.1:4317');
|
|
const pending = new Map();
|
|
const waiters = [];
|
|
let proposal = null;
|
|
let resolved = null;
|
|
let history = null;
|
|
const waitFor = (predicate) => new Promise((resolve, reject) => {
|
|
const timer = setTimeout(() => reject(new Error('timeout')), 20000);
|
|
waiters.push({ predicate, resolve: (value) => { clearTimeout(timer); resolve(value); } });
|
|
const existing = messages.find(predicate);
|
|
if (existing) resolve(existing);
|
|
});
|
|
const messages = [];
|
|
ws.onopen = () => ws.send(JSON.stringify({ type: 'hello', protocol: 1, token: 'dev', client: 'agent-smoke' }));
|
|
ws.onmessage = (message) => {
|
|
if (typeof message.data !== 'string') return;
|
|
const parsed = JSON.parse(message.data);
|
|
messages.push(parsed);
|
|
if (parsed.type === 'agent_history') history = parsed.messages;
|
|
if (parsed.type === 'event' && parsed.event?.kind === 'agent_proposal') proposal = parsed.event;
|
|
if (parsed.type === 'event' && parsed.event?.kind === 'agent_resolved') resolved = parsed.event;
|
|
if (parsed.type === 'result') pending.set(parsed.id, parsed.result);
|
|
for (const waiter of [...waiters]) {
|
|
if (waiter.predicate(parsed)) { waiters.splice(waiters.indexOf(waiter), 1); waiter.resolve(parsed); }
|
|
}
|
|
};
|
|
const send = (id, command) => ws.send(JSON.stringify({ type: 'command', id, command }));
|
|
await waitFor((message) => message.type === 'snapshot');
|
|
send(1, { type: 'agent_ask', text: 'raise the tempo a little' });
|
|
await waitFor((message) => message.type === 'event' && message.event?.kind === 'agent_proposal');
|
|
if (!proposal) throw new Error('no proposal');
|
|
if (!proposal.commands.some((command) => command.type === 'set_tempo')) throw new Error('expected a tempo proposal');
|
|
const before = pending.get(1)?.state?.transport?.bpm;
|
|
send(2, { type: 'agent_approve', id: proposal.id });
|
|
await waitFor((message) => message.type === 'event' && message.event?.kind === 'agent_resolved');
|
|
const state = pending.get(2)?.state;
|
|
if (!resolved?.applied) throw new Error('proposal was not applied');
|
|
if (state?.transport?.bpm !== 132) throw new Error(`tempo did not move: ${state?.transport?.bpm}`);
|
|
const userLines = messages.filter((message) => message.type === 'event' && message.event?.kind === 'agent_message' && message.event.message.role === 'user').length;
|
|
const agentLines = messages.filter((message) => message.type === 'event' && message.event?.kind === 'agent_message' && message.event.message.role === 'agent').length;
|
|
console.log(JSON.stringify({
|
|
history: history?.length ?? 0,
|
|
proposal: { id: proposal.id, commands: proposal.commands, description: proposal.description },
|
|
resolved,
|
|
bpm: state.transport.bpm,
|
|
userLines,
|
|
agentLines,
|
|
journal: state.journal_len
|
|
}, null, 2));
|
|
ws.close();
|
|
console.log('agent smoke passed');
|