Sentinel Memory
An incident-response agent whose memory is a transactional database rather than a context window — it remembers consequences, not conversations.
What it does
- Counterfactual card surfacing the closest historical precedent, its similarity score and what it cost
- Retrieval telemetry shown in the UI — cosine distance, latency, index versus exact scan
- Four-write atomic approval under SERIALIZABLE isolation with application-owned retry
- Agent handoff rebuilding incident state entirely from the database
Problem
During industrial incidents, personnel rotate and agents restart. Each handoff loses the record of what was already attempted and what it caused, so a reasonable-looking action gets repeated — including the one that did damage last time. An agent whose memory is a context window has the same failure mode, and reaches it faster.
Motivation
The narrower question behind the project was not "how do we give agents memory?" but what specifically does an incident agent need to remember? The answer turned out not to be conversations. It was consequences: what a given action, taken in a given situation, actually caused.
Architecture
A three-tier system with no ORM, no separate vector database, no queue and no cache layer:
- Client — Next.js App Router UI (React 19, Tailwind CSS).
- Server — Node.js API routes holding the orchestration logic.
- Services — CockroachDB Cloud for persistence, Amazon Bedrock for reasoning and embeddings.
The agent loop lives in a single file: embed the proposed action → retrieve precedents from CockroachDB by cosine distance → ground the Bedrock call on what came back → validate the structured response with Zod → persist the recommendation together with the memory IDs it cited. Every step writes a durable memory event, including the retrieval itself, so the timeline shows that memory was consulted rather than merely that an answer appeared.
The one architectural decision that mattered was making DataStore,
EmbeddingProvider and Reasoner interfaces rather than integrations.
Each has a real implementation and a clearly labelled local fallback, which
makes the interface previewable without credentials and the transaction
logic unit-testable without a live cluster.
Method
Episodic and semantic memory are treated as two problems, not one.
Episodic memory records what happened in an incident, in order; semantic
memory records action_taken → outcome → lesson_learned as distinct
fields. Storing incident summaries and embedding them gets you "similar
incidents"; storing the action-outcome triple gets you "this specific action
caused this specific damage", which is the question a responder actually has.
Keeping both in one database means retrieval returns the memory and its provenance — incident code, severity, date — in a single query, and the human decision that follows is transactional against the same rows.
Safety is enforced after the model, not requested from it. The prompt
states the rules; enforceSafetyFloor then runs on every response, forces
human approval on any high-risk physical action regardless of what the model
returned, and filters citations down to memory IDs that were genuinely
retrieved, so a hallucinated reference cannot reach a responder.
Results
- 94 tests covering validation, retrieval, transactions, agents and health checks — none requiring a live cluster or an AWS account.
- The approval path commits four writes in one transaction with
SELECT … FOR UPDATElocking and application-owned40001retry with exponential backoff and full jitter, with tests proving rollback leaves the store unchanged. - The handoff demonstration reconstructs full incident state from the database alone after the primary agent disconnects — nothing is read from a transcript.
Error analysis
Two failure modes documented during development:
- Retrieval returned one story instead of three. Vector search over a corpus where a single past incident contributes four memories returns four angles on that one incident and crowds out every other precedent. Fixed by over-fetching and then capping per source incident in application code.
- The local fallback embedder produces honest but unimpressive scores. Bag-of-words similarity between a short query and a long document is bounded, landing around 0.4–0.65 where a learned model would give 0.85–0.95. The raw cosine is kept and explained rather than rescaled to look better.
Limitations
Documented in the repository rather than hidden:
- No authentication and no RBAC — every visitor acts as the configured
responder, which is exactly as trustworthy as the audit trail's
decided_byfield then is. - Rate limiting is in-process and therefore not multi-instance safe.
- Seeded incidents are synthetic, not real incident reports.
- The vector index is version-gated (CockroachDB v25.2 preview, GA in v25.3+); where it cannot be created the app degrades to an exact scan and says so in the health endpoint and the UI.
Responsible use
A prototype built during a hackathon, seeded with synthetic incidents. It is a demonstration of a memory architecture, not an operational safety system, and the repository's limitations section is written to keep that distinction clear. Every fallback — in-memory store, local reasoner, deterministic embeddings — is labelled in the UI, the health endpoint and every API response, so nothing claims to be an integration it isn't.
Running it yourself
Runs locally with no credentials, using an in-memory store and local reasoning:
git clone https://github.com/ParishruthiGanesh/sentinel-memory.git
cd sentinel-memory
npm install
cp .env.example .env.local
npm run dev
To wire up real services, set DATABASE_URL and run npm run db:migrate
and npm run db:seed, then set AWS_REGION and BEDROCK_MODEL_ID.
curl -s localhost:3000/api/health reports which implementation is actually
serving each interface.
Future work
- Close the learning loop — capture whether an approved action produced its predicted outcome and write that back as a new memory.
- Authentication and RBAC, without which
decided_bycannot be trusted. - Database-enforced immutability for memory and audit events.
- Real ingestion from SCADA/historian feeds instead of manual entry.
- Retrieval evaluation with a labelled set and precision@k tracking.
Screenshots