fix: make MongoDB mandatory, fail loud instead of degrading
Reverse the best-effort error swallowing. MongoDB is core to PodMan's continual-learning story and must always be used, so a broken memory layer must surface immediately rather than silently masquerade as working (which is how observations stayed at 0 unnoticed). - agent verifies Mongo via initMemory() at boot; bad creds / unreachable Atlas now fail loudly before joining the room, not mid-demo - server exits on Mongo init failure instead of warning and limping on - getGitStates and onScreenFrame no longer swallow Mongo errors - memory persist() logs the failure and rethrows instead of warning Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AaCFWMkYQmTcuPsxaaACft
This commit is contained in:
@@ -14,6 +14,7 @@ import sharp from 'sharp';
|
|||||||
import { AccessToken } from 'livekit-server-sdk';
|
import { AccessToken } from 'livekit-server-sdk';
|
||||||
import { env } from './env.js';
|
import { env } from './env.js';
|
||||||
import { PodMan } from './agent/podman.js';
|
import { PodMan } from './agent/podman.js';
|
||||||
|
import { initMemory } from './memory/db.js';
|
||||||
|
|
||||||
const POD_ROOM = process.env.POD_ROOM ?? 'demo-pod';
|
const POD_ROOM = process.env.POD_ROOM ?? 'demo-pod';
|
||||||
const HERMES_IDENTITY = 'podman-hermes';
|
const HERMES_IDENTITY = 'podman-hermes';
|
||||||
@@ -30,6 +31,10 @@ async function agentToken(room: string): Promise<string> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
|
// MongoDB is mandatory. Verify the connection before joining the room so bad
|
||||||
|
// creds / unreachable Atlas fail loudly at boot, not silently mid-demo.
|
||||||
|
await initMemory();
|
||||||
|
|
||||||
const room = new Room();
|
const room = new Room();
|
||||||
const podman = new PodMan(room, POD_ROOM);
|
const podman = new PodMan(room, POD_ROOM);
|
||||||
await room.connect(env.LIVEKIT_URL, await agentToken(POD_ROOM), {
|
await room.connect(env.LIVEKIT_URL, await agentToken(POD_ROOM), {
|
||||||
|
|||||||
@@ -43,9 +43,6 @@ export class PodMan {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async onScreenFrame(engineerId: string, jpeg: Buffer): Promise<void> {
|
async onScreenFrame(engineerId: string, jpeg: Buffer): Promise<void> {
|
||||||
// Whole-frame guard: a Gemini/GitHub/Mongo failure on one frame must degrade
|
|
||||||
// gracefully, never crash the long-running live agent loop.
|
|
||||||
try {
|
|
||||||
const ctx = await analyzeFrame(engineerId, this.podId, jpeg);
|
const ctx = await analyzeFrame(engineerId, this.podId, jpeg);
|
||||||
this.contexts.set(engineerId, ctx);
|
this.contexts.set(engineerId, ctx);
|
||||||
await recordObservation(ctx);
|
await recordObservation(ctx);
|
||||||
@@ -61,9 +58,6 @@ export class PodMan {
|
|||||||
const github = await getGithubState(); // cached
|
const github = await getGithubState(); // cached
|
||||||
const collisions = detectCollisions([...this.contexts.values()], github);
|
const collisions = detectCollisions([...this.contexts.values()], github);
|
||||||
for (const collision of collisions) await this.handle(collision);
|
for (const collision of collisions) await this.handle(collision);
|
||||||
} catch (err) {
|
|
||||||
console.warn(`[agent] frame from ${engineerId} skipped: ${(err as Error).message}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handle(collision: Collision): Promise<void> {
|
private async handle(collision: Collision): Promise<void> {
|
||||||
|
|||||||
@@ -69,9 +69,8 @@ export async function getGitStates(podId: string): Promise<Map<string, GitState>
|
|||||||
recentCommit?: string | null;
|
recentCommit?: string | null;
|
||||||
gitUpdatedAt?: Date;
|
gitUpdatedAt?: Date;
|
||||||
}>('engineer_states');
|
}>('engineer_states');
|
||||||
const map = new Map<string, GitState>();
|
|
||||||
try {
|
|
||||||
const docs = await col.find({ podId }).toArray();
|
const docs = await col.find({ podId }).toArray();
|
||||||
|
const map = new Map<string, GitState>();
|
||||||
for (const doc of docs) {
|
for (const doc of docs) {
|
||||||
map.set(doc.name, {
|
map.set(doc.name, {
|
||||||
changedFiles: doc.changedFiles ?? [],
|
changedFiles: doc.changedFiles ?? [],
|
||||||
@@ -80,11 +79,6 @@ export async function getGitStates(podId: string): Promise<Map<string, GitState>
|
|||||||
gitUpdatedAt: doc.gitUpdatedAt ?? null,
|
gitUpdatedAt: doc.gitUpdatedAt ?? null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
// Best-effort: a Mongo hiccup degrades git fusion, it must not crash the
|
|
||||||
// live agent loop. Detection falls back to vision-only signals.
|
|
||||||
console.warn(`[memory] getGitStates failed: ${(err as Error).message}`);
|
|
||||||
}
|
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,14 +10,16 @@ import { enrichCollisionMemory } from './vectors.js';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Continual-learning memory: persist observations, collisions, interventions,
|
* Continual-learning memory: persist observations, collisions, interventions,
|
||||||
* and outcomes to MongoDB so later sessions get sharper. Writes are best-effort
|
* and outcomes to MongoDB so later sessions get sharper. MongoDB is mandatory —
|
||||||
* — a Mongo hiccup logs a warning rather than crashing the agent/server.
|
* a failed write is surfaced loudly and rethrown, never silently swallowed, so
|
||||||
|
* a broken memory layer can never masquerade as a working one.
|
||||||
*/
|
*/
|
||||||
async function persist(name: string, fn: () => Promise<unknown>): Promise<void> {
|
async function persist(name: string, fn: () => Promise<unknown>): Promise<void> {
|
||||||
try {
|
try {
|
||||||
await fn();
|
await fn();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn(`[memory] ${name} persist failed: ${(err as Error).message}`);
|
console.error(`[memory] ${name} persist FAILED: ${(err as Error).message}`);
|
||||||
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -177,7 +177,11 @@ http.listen(env.PORT, '0.0.0.0', () => {
|
|||||||
console.log(`[server] :${env.PORT}`);
|
console.log(`[server] :${env.PORT}`);
|
||||||
initMemory()
|
initMemory()
|
||||||
.then(() => seedDefaultPods())
|
.then(() => seedDefaultPods())
|
||||||
.catch((e) => console.warn(`[memory] init failed: ${(e as Error).message}`));
|
.catch((e) => {
|
||||||
|
// MongoDB is mandatory — do not run a half-dead API against a broken DB.
|
||||||
|
console.error(`[memory] init FAILED, exiting: ${(e as Error).message}`);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
let shuttingDown = false;
|
let shuttingDown = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user