Files
sausage/host/js/p2-test.ts
T

60 lines
3.3 KiB
TypeScript

// P2 rehearsal: real bash tools pause for two passkey approvals and resume durably.
import { BACKGROUND_CONTEXT as context } from '@earendil-works/chord/context';
import { createModels } from '@earendil-works/pi-ai/models';
import { fauxProvider, fauxAssistantMessage, fauxToolCall, fauxText } from '@earendil-works/pi-ai/providers/faux';
import { Harness, createRegistry } from '@earendil-works/pi-durable';
import { CodingTools } from '@earendil-works/pi-durable/tools';
import { environment } from './environment';
import { openStorage } from './sqlite';
import { CredentialRequests, startRequests } from './credentials';
const models = createModels();
const faux = fauxProvider({ tokensPerSecond: 100000 });
models.setProvider(faux.provider);
const bash = (command: string, id: string) => fauxAssistantMessage([fauxToolCall('bash', { command }, { id })], { stopReason: 'toolUse' });
const curl = `curl -i http://127.0.0.1:${globalThis.p2Http}/whoami`;
const push = 'cd /repo && git push origin main';
faux.setResponses([
bash(curl, 'curl-denied'), fauxAssistantMessage([fauxText('Wait for HTTP approval.')]),
bash(curl, 'curl-approved'), bash(push, 'ssh-denied'), fauxAssistantMessage([fauxText('Wait for SSH approval.')]),
bash(push, 'ssh-approved'), fauxAssistantMessage([fauxText('P2 complete.')]),
]);
const registry = createRegistry();
registry.install(CodingTools); registry.install(CredentialRequests);
const harness = await Harness.open(await openStorage(globalThis.storagePath), {
models, registry, env: (target: any) => environment('/', target.conversationId),
}, context);
const root = await harness.root(context, { agent: {
model: { provider: 'faux', modelId: 'faux-1' }, extensions: [CodingTools, CredentialRequests], cwd: '/',
} });
const stop = await startRequests(harness, context);
harness.resume();
await (await root.submit({ type: 'input', content: 'Rehearse P2', requestId: 'p2' }, context)).wait(context);
await Deno.core.ops.op_file('write', '/p2-http-idle', []);
const watch = await root.watch(context);
let sshIdle = false;
await new Promise<void>((resolve, reject) => {
const check = async (event: any) => {
if (event.kind !== 'value') return;
const transcript = JSON.stringify(event.value.entries);
if (!sshIdle && transcript.includes('Wait for SSH approval.')) {
sshIdle = true;
await harness.waitForIdle(context);
await Deno.core.ops.op_file('write', '/p2-ssh-idle', []);
}
const approvals = event.value.entries.filter((entry: any) => JSON.stringify(entry).includes('Your request for'));
if (approvals.length > 2) reject(new Error('Duplicate approval wake-up'));
if (transcript.includes('P2 complete.')) resolve();
};
watch.start(check); check({ kind: 'value', value: watch.value });
});
await harness.waitForIdle(context);
const transcript = JSON.stringify(watch.value.entries);
for (const expected of ['403', 'demo-api', 'demo-ssh', 'Bearer accepted', 'P2 complete.']) {
if (!transcript.includes(expected)) throw new Error(`Missing rehearsal result: ${expected}`);
}
if (watch.value.entries.filter((entry: any) => JSON.stringify(entry).includes('Your request for')).length !== 2) {
throw new Error('Expected exactly two approval inputs');
}
await Deno.core.ops.op_file('write', '/p2-transcript', [...new TextEncoder().encode(transcript)]);
await watch.stop(); await stop(); await harness.close(context);