// Durable, background credential waits. Admission and completion share one SQLite commit. import { defineDoc, defineTask, defineExtension } from '@earendil-works/pi-durable'; const Requests = defineDoc<{ ids: string[] }>({ kind: 'sausage.credential.requests', version: 1, scope: 'conversation', history: 'latest', fork: 'initial', initial: () => ({ ids: [] }), checkpointWhen: () => false, }); const Wait = defineTask<{ id: string }, { phase: 'wait' }, null>({ name: 'sausage.credential.wait', version: 1, initial: () => ({ phase: 'wait' }), phases: { wait: async (task, runtime, context) => { const request = Deno.core.ops.op_credential_requests().find((r: any) => r.id === task.input.id); if (!request || request.state === 'pending') { await runtime.sleepUntil(Date.now() + 1000, { phase: 'wait' }, context); return; } const conversation = (await runtime.conversation(runtime.conversationId, context))!; await runtime.commit(async (tx) => { await conversation.submit(tx, { type: 'input', content: request.message, requestId: `credential:${request.id}` }); return { status: 'terminal', outcome: { status: 'completed', result: null } }; }, context); }, }, abort: (_task, runtime, context) => runtime.commit(() => ({ status: 'terminal', outcome: { status: 'aborted' } }), context), }); export const CredentialRequests = defineExtension({ name: 'sausage.credentials', tasks: [Wait] }); export async function discoverRequests(harness: any, context: any) { for (const request of Deno.core.ops.op_credential_requests()) { const conversation = await harness.conversation(Number(request.conversation), context); if (!conversation) continue; await conversation.commit(async (tx: any) => { const recorded = await tx.doc(Requests, conversation.id); if (recorded.ids.includes(request.id)) return; await tx.createTask(Wait, { id: request.id }, { ownership: { kind: 'conversation' }, background: true }); recorded.ids.push(request.id); }, context); } } export async function startRequests(harness: any, context: any) { await discoverRequests(harness, context); // Filed notifications start waits. The deadline reconciles other handles and lost notifications. let active = true; const pending = (async () => { while (active) { await Deno.core.ops.op_credential_next(); if (active) await discoverRequests(harness, context); } })(); return async () => { active = false; await pending; }; }