Files
podman/backend/src/memory/store.ts
T
Ramis 745f0032f7 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
2026-06-27 20:34:27 -07:00

75 lines
2.5 KiB
TypeScript

import type {
EngineerContext,
Collision,
Intervention,
InterventionOutcome,
InterventionStatus,
} from '@podman/shared';
import { collections } from './db.js';
import { enrichCollisionMemory } from './vectors.js';
/**
* Continual-learning memory: persist observations, collisions, interventions,
* and outcomes to MongoDB so later sessions get sharper. MongoDB is mandatory —
* 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> {
try {
await fn();
} catch (err) {
console.error(`[memory] ${name} persist FAILED: ${(err as Error).message}`);
throw err;
}
}
export async function recordObservation(ctx: EngineerContext): Promise<void> {
await persist('observation', async () =>
(await collections()).observations.insertOne({ ...ctx }),
);
}
export async function recordCollision(collision: Collision): Promise<void> {
await persist('collision', async () =>
(await collections()).collisions.insertOne(await enrichCollisionMemory(collision)),
);
}
export async function recordIntervention(intervention: Intervention): Promise<void> {
await persist('intervention', async () =>
(await collections()).interventions.insertOne({ ...intervention }),
);
}
export async function updateInterventionStatus(
interventionId: string,
status: InterventionStatus,
): Promise<void> {
await persist('intervention ack', async () =>
(await collections()).interventions.updateOne({ id: interventionId }, { $set: { status } }),
);
}
export async function recordOutcome(outcome: InterventionOutcome): Promise<void> {
await persist('outcome', async () => {
const c = await collections();
await c.outcomes.insertOne({ ...outcome });
await c.interventions.updateOne(
{ id: outcome.interventionId },
{ $set: { status: outcome.accepted ? 'accepted' : 'dismissed' } },
);
});
}
/** Document counts per collection — used by the /api/memory/stats endpoint. */
export async function memoryStats(): Promise<Record<string, number>> {
const c = await collections();
const [observations, collisions, interventions, outcomes] = await Promise.all([
c.observations.estimatedDocumentCount(),
c.collisions.estimatedDocumentCount(),
c.interventions.estimatedDocumentCount(),
c.outcomes.estimatedDocumentCount(),
]);
return { observations, collisions, interventions, outcomes };
}