Skip to content
Featured workCompany Brain

Turning conversations into a business’s memory

How Company Brain turns chat into reviewable notes, retrieves what matters, and connects an assistant to the places a business already works.

Explore the code Sources & scope
An illustration of the idea behind Company Brain.

A small business already has a lot of its knowledge written down. It is in email threads, text messages, overdue invoices, and the notes somebody took after a call. The difficult part is making that information useful without giving the owner another system to maintain.

Company Brain is the part of my work at Silkroad Innovation Hub that tries to close that gap. The broader product is a managed AI assistant, configured for an individual business. My engineering work has been turning a LibreChat fork into something that can remember useful information, answer across channels, and propose work the owner can inspect.

This is a build account, through the September 3, 2026 repository snapshot. It includes working code, small demo evaluations, and unfinished pieces. The most useful progress came from fairly ordinary problems: deciding what belongs in memory, keeping three interfaces consistent, and making a text that says “send” mean exactly one thing.

Starting with a working chat application

I started from LibreChat because it already provided streaming replies, conversation history, authentication, and an agent execution layer. Rebuilding those would have delayed the parts specific to a business assistant.

The first commits in August added a dashboard, a to-do list, an approval queue, and a graph of company notes. There were three modes: ordinary chat, grounded search over the company brain, and a more deliberate research mode. The product name changed from Hermes to Silkroad during this first round.

The original architecture document described a separate service owning memory in Postgres and pgvector. That was a plan. The implementation grew inside the fork, using MongoDB for application records and vectors, with Markdown files as the curated knowledge store. Keeping those distinct matters: a design document can describe a sensible destination without describing the system that actually runs.

StageWhat changedWhat it made possible
August 13Dashboard, linked notes, approvals, background ingestionA visible memory system with proposed edits
August 30Retrieval, shared channel gateway, workflows, deployment toolingThe same business context across chat, email, and texts
September 2Local iMessage echo fixes and Gmail setup notesFewer transport surprises in live testing
September 3Photon transport, compact context snapshots, email decisions over textA shorter path from an owner's question to an answer or draft

Two kinds of memory

The central design separates a raw log from a curated vault.

The raw log records what entered the system, where it came from, and what happened during processing. It preserves the original message even when that message does not deserve a permanent note. The vault contains the smaller set of facts, relationships, and decisions that should be useful later.

A simplified flow looks like this:

Web chat / email / iMessage
           ↓
     Raw message log
           ↓
 Duplicate check → triage → distillation
           ↓
     Proposed note or to-do
           ↓
      Owner approval
           ↓
   Markdown vault + search index

The chat save wrapper appends to the log without making the reply wait for that append. A separate worker processes the queue, with a default 15-second interval and quiet window. Atomic claims keep workers from taking the same item, stale processing entries can be requeued, and an attempt cap prevents endless retries.

That separation is a responsiveness decision, with a tradeoff. Finishing a chat reply does not itself prove that its memory write succeeded. Failures need to remain visible in the queue and logs; a friendly response is not an ingestion receipt.

The gate first checks for a near-duplicate already resolved as applied or known. Its configurable threshold defaults to 0.95 on the retriever's score. That avoids another triage and distillation call, although retrieval can still incur embedding work. Calling this “zero-cost deduplication” would be inaccurate.

Triage then decides whether the message contains durable information, extracts possible action items, and flags instructions that appear directed at the AI. A flagged entry stops before distillation. Otherwise, a stronger model receives relevant existing notes and chooses whether the information is already known, should update a note, or should create one.

The default split uses a smaller model for triage and a larger model for distillation. The implementation calls an OpenAI-compatible client with OpenAI as its default endpoint; OpenRouter appears in earlier product planning. I do not have a controlled cost comparison supporting a percentage saving, so the defensible claim is narrower: expensive distillation only runs on the messages that survive earlier checks.

Both model calls have no tools. Proposed notes and extracted to-dos require approval by default. This reduces the consequences of a mistaken extraction, but a classifier and a prompt are not a proof that hostile content can never influence a later answer.

Why the vault is plain Markdown

A note has a type, optional structured fields, and links to other notes. The files are readable without the application. That makes an incorrect fact easier to inspect and makes memory less dependent on one database schema or chat interface.

The graph explorer is a second view of those files. It uses a force layout: notes become nodes, wikilinks become edges, and headings, highlighted facts, and tags supply smaller satellite nodes. Searching or selecting a node opens its note in a reader.

I like this because it gives the owner something concrete to inspect. “The assistant remembers your business” is vague. A note with a visible body and links can be reviewed and corrected.

The demo vault uses public information about Anduril, plus synthetic invoices for workflow testing. It is demonstration material, not evidence that Anduril uses the product. The graph shows connections encoded in notes; it does not establish that every relationship or extracted fact is correct.

Getting useful information back out

Memory ingestion and question answering need the same source of truth. Otherwise, the dashboard can show one fact while a channel assistant answers from an old prompt.

The August retrieval implementation searches both vault notes and recent raw messages. Embeddings are stored in MongoDB's brainvectors collection, loaded into an in-process cache, and ranked by cosine similarity with a small lexical bonus for matching titles or senders. Defaults include a 90-day raw-log window and a 20,000-vector cache cap. Those are operating bounds, not demonstrated scaling results.

Content hashes avoid re-embedding unchanged note chunks. The worker indexes notes it writes, and the API warms the index on startup. A brain_search tool makes retrieval available to the chat modes instead of maintaining a hand-written list of company facts in their prompts.

A twelve-question demo evaluation recorded the expected note in the top three results for eleven questions. The miss involved a question about founders retrieving a different relevant note. This is a small retrieval check over a known vault. It does not mean the assistant answered eleven questions correctly, and it does not measure general reliability on a new business.

One unresolved edge is deletion. The retriever has an explicit removal operation, but vault synchronization does not automatically discover every note deleted from disk. Deleting a file and deleting every searchable representation of that file need to be one coherent operation.

Making the same assistant available over text

The channel gateway routes external questions through the application's normal agent path. A service credential authenticates the connector, a short-lived owner token supplies the application identity, and each external thread maps to a conversation. This keeps channel history inspectable in the web interface.

Channel runs have a default twelve-step budget and a ninety-second timeout. Later, I gave them a dedicated prompt and configuration without specialist-agent fan-out. A question arriving as a text usually needs a short answer, not a multi-agent report.

The first iMessage connector polled the local Messages database on a Mac. That was useful for development and passive ingestion, but it carried platform-specific problems. A September fix handles duplicate copies in self-chats so one owner message does not trigger repeated answers.

The later Photon connector gives the assistant a cloud iMessage transport. Its code rejects strangers and group chats before logging, checks the owner again before sending, and records provider message IDs for deduplication. The Mac connector remains an optional ingestion path; it is no longer the only way to support outbound iMessage from a Linux deployment.

There is still a delivery limitation. The stream processor exits when its input stream ends so the process supervisor can restart it. The documentation accepts a possible gap for messages arriving during downtime; it does not establish durable replay from the provider.

The latency work was mostly about the path

The development notes record an initial channel answer taking about thirteen seconds, and a later smoke test around three seconds after using the dedicated channel configuration and warming retrieval. Those observations are useful for finding friction, but they are not a latency distribution from production traffic.

The next change removed a tool round-trip for common questions. A compact snapshot adds open to-dos, headline facts from the notes, and recent inbox summaries directly to the channel prompt. The assistant can answer a simple question from that context and search when it needs more detail.

September notes report examples around 0.7–1.1 seconds for simple factual questions, roughly two seconds for an inbox summary, and 3.3 seconds to draft an email. Different questions and configurations make these examples unsuitable for a single speedup claim.

The snapshot also creates new responsibilities. Its size grows with the vault. Cached inbox context can be stale. Most significantly, the current snapshot includes sample dashboard finance figures until real integrations replace them. The code is a working demo, but demo context must remain clearly separated from a client's actual financial state.

Draft first, then decide

Email makes the boundary between proposing and acting especially clear.

The Gmail connector performs incremental synchronization using history IDs and identifies bulk mail before expensive processing. An email_draft tool creates a Gmail draft and an approval record. It does not send the message itself.

The owner can inspect the approval in the dashboard or text a short decision. The September implementation recognizes commands such as “send” and “scrap it” and applies the decision to the latest pending draft. Both routes use the same draft-decision service.

Recipient policy allows the owner's address, explicitly configured domains, and configured contacts. Before sending, the service reads the draft's current recipients and checks them again. That matters because a Gmail draft can change after the assistant originally created it.

The send path also requires a durable audit entry before calling Gmail. If that audit write fails, the send does not proceed. The audit distinguishes the pending action from its later success or failure.

The light version deliberately leaves a difficult interaction unfinished: choosing among several pending drafts. “Send it” is convenient only when “it” is unambiguous. Threaded replies and reading a specific email are also described as follow-up work, rather than capabilities I can claim are complete.

Work that happens without a prompt

Two scheduled workflows extend the assistant beyond answering questions. A morning brief summarizes available tasks and operational context. An invoice chase scans vault notes of type invoice and prepares follow-ups for overdue items.

The invoice source is currently the vault, not a completed QuickBooks or Mercury integration. The finance dashboard is sample-backed; live components include tasks, approvals, budget status, activity, and process health. Keeping those distinctions visible is part of making the product understandable.

Per-workflow policies let the owner see and reset approval history and control whether a workflow is enabled or eligible for automatic sending. A shared pause command stops channel responses and background processing. An hourly budget monitor can warn at multiples of an expected monthly spend, with hard pause separately configurable.

Deployment tooling prepares a client vault and configuration, with Compose or pm2 supervising the API, worker, connectors, and database. A health endpoint exposes heartbeats, and the repository includes MongoDB backup tooling. These are useful operational pieces; their existence alone does not establish a fleet size, uptime record, or successful restore history.

What this changed about how I build

The most interesting work was not choosing a model. It was making the system's intermediate states visible: this message was received, this fact was proposed, this draft is waiting, this connector is alive.

The next improvements follow from those states. Memory deletion needs to reach the index. Approval backlogs need a review experience that stays usable as volume increases. Text decisions need explicit draft identity once several drafts exist. Evaluation needs more than a dozen familiar questions, including corrections, ambiguous requests, and interrupted channel sessions.

I am still learning where a useful prototype ends and a dependable service begins. This project made that boundary concrete: an answer can sound right while its context is stale, and a successful demo can conceal a missing recovery path. Building the surrounding workflow is how I have started addressing that gap.

Sources and scope

This account is grounded in the Company Brain repository at commit 8a1cf6de, dated September 3, 2026. Historical timings and the retrieval result are recorded development observations, not measurements rerun for this article. No client messages, credentials, or private business records are reproduced.