feat(agent): POST timeline events to vibn-frontend ingest API

- vibn-events-ingest.ts + emit() dual-write with session PATCH
- .env.example: VIBN_API_URL, AGENT_RUNNER_SECRET

Made-with: Cursor
This commit is contained in:
2026-04-01 11:48:57 -07:00
parent 1ff020cf53
commit 419af40ca2
7 changed files with 226 additions and 8 deletions

48
src/vibn-events-ingest.ts Normal file
View File

@@ -0,0 +1,48 @@
/**
* Push structured timeline events to vibn-frontend (Postgres via ingest API).
* Complements PATCH output lines — enables SSE replay without polling every line.
*/
import { randomUUID } from 'crypto';
export interface IngestEventInput {
type: string;
payload?: Record<string, unknown>;
ts?: string;
}
export async function ingestSessionEvents(
vibnApiUrl: string,
projectId: string,
sessionId: string,
events: IngestEventInput[]
): Promise<void> {
if (events.length === 0) return;
const secret = process.env.AGENT_RUNNER_SECRET ?? '';
const base = vibnApiUrl.replace(/\/$/, '');
const url = `${base}/api/projects/${projectId}/agent/sessions/${sessionId}/events`;
try {
const res = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-agent-runner-secret': secret,
},
body: JSON.stringify({
events: events.map((e) => ({
clientEventId: randomUUID(),
ts: e.ts ?? new Date().toISOString(),
type: e.type,
payload: e.payload ?? {},
})),
}),
});
if (!res.ok) {
const t = await res.text();
console.warn('[ingest-events]', res.status, t.slice(0, 240));
}
} catch (err) {
console.warn('[ingest-events]', err instanceof Error ? err.message : err);
}
}