Repository navigation
Replies: 4 comments 6 replies
|
Super cool idea. I gotta jump into this rabbit hole soon! |
|
So back to the interface layer, seems one part of the interface is some sort of session synchronization. As is, based on configuration, all conversation data/metadata driven by Agor would be sent to this memory repository. Unclear whether that's only configured globally and/or per user. Some potential "config hell" here, layering on top of the already complex Agor configuration. Streams? Batches? Clearly one could write an offline batch sync, where message history out of Agor would dump or get injected into that memory system. Streaming would be more challenging but potentially doable. Important question remains, is this user-driven config, or admin/global configuration or a mix of both. For active recall, seems a normal link to the external memory system, through MCP, would work. Note that Agor is already fairly self-aware (agents have access to session history, knowledge base (vector-searchable, ...)), but doesn't "dream", meaning it doesn't do active memory management outside of knowledge base indexing. One option to consider is a comparable sub-system: session indexing, fact extraction, ... That has the clear limitation of being self-scoped, only aware of itself, so if a large team uses an array of different tools, an external, centralized memory store offers advantages over Agor who would potentially only know about itself. |
|
Disclosure, I maintain one of the category of tool you would be plugging in here, a local first memory server for coding agents at github.com/samvallad33/vestige, AGPL, so take the rest as one data point rather than a vote for a specific provider. The distinction you are drawing between explicit knowledge and modeled understanding is the right one, and it maps to something worth deciding early, whether the provider is allowed to write back automatically or only ever gets read from. Automatic write back is where most of the actual risk lives, since a wrong inference about who someone is or what they meant becomes durable context for every future session with that person, and it can be hard to tell apart from something the team actually taught the teammate. One thing that helped for me was gating the write path on how surprising the new observation is relative to what is already stored, so routine restatements get skipped and genuinely novel or conflicting claims get flagged rather than silently accepted, and a correction supersedes the old claim instead of sitting next to it as an unrelated new fact. If it is useful while scoping the interface, happy to compare notes on where that gating approach held up and where it did not. A feature flag with nothing changing when unconfigured is the right default for something that touches modeled understanding of real people. |
|
Here's the interface sketch as promised. Written against 0.24.5, grounded in a month of running Honcho behind a production reviewer agent plus Agor teammates on a nightly distillation loop. Tear it apart. Design constraints (from operating this, not preference)
The interfaceinterface MemoryProvider {
// Capture. Called by the daemon after a task completes, with only the
// messages that task produced. Fire-and-forget: failures are logged,
// never block the session. The provider keeps its own state, so it
// never needs the whole context window.
observe(input: {
sessionId: string;
branchId: string; // the teammate, if the branch is one
userId: string; // the human peer
messages: MessageDelta[]; // only what's new since the last observe
}): Promise<void>;
// Recall. Exposed to sessions as MCP tools (same pattern as the built-in
// agor server, so no new plumbing concept). Question in, short
// synthesized answer out. Never a page of raw memories.
query(input: {
question: string;
scope: { userId?: string; branchId?: string; sessionId?: string };
maxTokens?: number;
}): Promise<{ answer: string; citations?: string[] }>;
// Optional session-start context ("who am I talking to, what's live").
// Computed once at session creation, hard token budget, stable for the
// session's lifetime, so cache-safe. Providers may return null.
primer(input: {
userId: string;
branchId: string;
budgetTokens: number;
}): Promise<string | null>;
}Recall modes (all three are
|
| Mode | Caller | Default? |
|---|---|---|
| Tools | The agent, mid-conversation, when it decides it needs memory | Yes: bounded by agent judgment, cache-safe append |
| 🧠 button | The human; the answer comes back as a reviewable, editable block, approved into the context | Opt-in UI (a widget, basically) |
| Passive | The daemon, appending a budgeted block before a prompt | Opt-in, per teammate, hard maxTokens |
Sync shape
Batches, small ones: one observe() per completed task. Ordered, retry-friendly, and memory doesn't need sub-second freshness (our loop deliberately waits for sessions to go quiet before learning from them). History import is the same call at a bigger size: an admin-triggered backfill(scope, since) walking old sessions through observe() in order. One mechanism for both.
Config: three layers, each with one owner
| Layer | Owner | Decides |
|---|---|---|
| Deployment | operator (config.yaml, where secrets already live) | which provider exists, endpoint, credentials |
| Teammate | whoever configures the teammate (branch config, like mcp_server_ids today) |
on/off, and mode: tools only / plus primer / plus passive budget |
| Human user | the person themselves | consent to being modeled as an individual; without it the provider receives only role-anonymous data |
The third layer is a consent flag, not a tuning knob. We run exactly this split in production for GDPR reasons, and it must be enforced at the observe() call site (daemon filters before shipping), not by provider goodwill.
Write path and provenance
The only Agor-to-provider write is observe(). Whether the provider derives inferences from what it observes is provider-internal policy, but the interface keeps taught and inferred distinguishable forever:
query()answers carry provenance: each claim is taught (traceable to an explicit statement) or derived (the provider's inference), with citations either way. A wrong inference must never be able to masquerade as something the team actually said.- Corrections supersede rather than accumulate: a contradicted claim gets replaced (history kept), not filed next to its correction as an unrelated fact. Provider-side gating of the write path (skip routine restatements, flag novel or conflicting claims for review) is the right home for this, per the discussion above.
Reference implementation: Honcho
The mapping is nearly 1:1, which is what makes it a good reference backend: observe appends messages to a Honcho session (an async worker digests them on a cheap model tier), query is the dialectic endpoint (natural-language question over derived conclusions, which carry citations), primer is Honcho's token-budgeted context block. Honest labeling: we run the observe + tools-mode query halves in production today; primer and passive are capabilities we haven't exercised, and they're the part a branch would prove out.
What native does (the default)
observe is a no-op (the framework's teammate-memory tools keep working as today), query delegates to existing teammate memory / Knowledge search, primer returns null. Zero behavioral change for existing deployments. And if Agor later grows its own "dream" subsystem (session indexing, fact extraction), it grows behind this same interface as a smarter native provider, without affecting anyone who chose an external store.
Open questions
- Tenancy: provider instances per tenant, or tenant passed in scope?
- Primer vs USER.md: complement or replacement when a provider supplies one?
- Failure posture: provider down means sessions proceed memory-less with a logged warning (our vote), or fail loudly?
- Where the per-user consent flag lives so the daemon can enforce it at the
observe()call site: user preferences feel right, but the filtering has to happen before anything ships.
Uh oh!
There was an error while loading. Please reload this page.
Summary
Teammates currently get durable memory through their Knowledge-base namespace. That works well for explicit, shared knowledge: decisions, runbooks, things the team teaches them. What's missing is a seam for modeled memory, meaning an external backend that observes conversations, builds an evolving picture of the people a teammate works with (and of what the teammate itself has learned), and injects that synthesis into context when a session starts.
This proposal adds a small
MemoryProviderinterface so teams can plug in a self-hosted backend like Honcho, Letta or Mem0 per teammate. When no provider is configured, nothing changes. I'm offering to build the interface and a Honcho reference implementation behind a feature flag.Motivation
Long-lived teammates on the Message Gateway talk to the same colleagues for weeks. Two kinds of memory matter there:
Right now the only route to the second kind is attaching a memory backend's tools via MCP. That works, but it is agent-initiated only. The teammate has to remember to remember. There is no ambient ingestion of conversations into the backend and no context injection at session start, and those two things are what make external memory feel like memory instead of a database the agent occasionally queries.
Prior art
Hermes Agent ships this as a first-class plugin system: eight external memory providers, one active at a time next to the built-in memory. When a provider is active, Hermes automatically injects provider context into the system prompt, prefetches relevant memories in the background before each turn, and syncs conversation turns to the provider afterwards. Write cadence is configurable (async, per turn, per session, or every N turns).
Two design details there translate directly to Agor:
The interface shape is also converging across the ecosystem. The same backends ship integrations for several harnesses already, so this is a proven seam.
Closer to home, a memory layer came up in #147 (the Zep sub-thread), where the open questions were where such a layer plugs into the framework and how to scope and namespace it. This proposal is one concrete answer to both: the daemon's message pipeline and context assembly as the plug points, scoped per teammate, with gateway identity as the namespace key for people.
Proposed design
Names are illustrative. Happy to be redirected on where this should live.
Wiring, based on reading the architecture docs (corrections welcome):
ingestMessagesas an after-create hook on the messages service in the daemon. FeathersJS hooks look like the natural mechanism here. Enqueued, so it never sits in the request path.getContextcalled wherever teammate context gets assembled before a session starts, prepended as its own block.Configuration per teammate, next to skills / MCP / gateway / schedule:
Non-goals:
Why this needs a small seam in core
The fair question is whether MCP tools already cover this. Partially, yes, but only in the agent-initiated form described above. The two touchpoints that make memory ambient, the message pipeline and context assembly, are not reachable from outside the daemon. Everything else can live in provider packages. So the ask is deliberately small: one seam, no change to default behavior, and providers stay out of the core path unless configured. Since these backends already ship integrations for other harnesses, a stable interface here is also likely to attract provider contributions beyond Honcho.
Offer
I'll contribute the interface, the Feathers wiring, and the Honcho reference implementation behind a feature flag, plus docs. Types centralized in
packages/coreper the contribution guide, and unit tests included since I saw those are explicitly wanted. I'd develop it in Agor itself, branch per feature on our instance, which seems to be the spirit of the house.For context: we run self-hosted Honcho in production at Voys (Dutch telecom) behind Hermes-based agents, a GitLab MR reviewer and a voicemail agent. So we'd dogfood this with a real workload. We also have GDPR and works-council review experience with exactly this class of feature, if that's useful for the docs.
Questions for maintainers:
All reactions