Summary
MemorySourceType has four variants and the dashboard renders all four, but only three are reachable. Nothing in the codebase ever writes sourceType: "user", so a fact the operator stated is stored byte-identically to one an agent inferred on its own.
Small and self-contained. Filing separately because it stands on its own regardless of the larger proposal that motivated me to notice it (see #335).
Evidence
All references against upstream/master @ b27a6d76 (v0.38.0).
The type declares it — packages/plugin/src/features/magic-context/memory/types.ts:25:
export type MemorySourceType = "historian" | "agent" | "dreamer" | "user";
The dashboard already renders it — packages/dashboard/src/components/MemoryBrowser/MemoryBrowser.tsx:342-350:
case "historian": return "purple";
case "agent": return "blue";
case "dreamer": return "indigo";
case "user": return "green"; // ← unreachable
But every write site is one of the other three:
memory/promotion.ts:75 sourceType: "historian"
memory/memory-migration.ts:233 sourceType: "historian"
dreamer/retrospective-learnings.ts:190 sourceType: "dreamer"
tools/ctx-memory/tools.ts:291 return deps.sourceType ?? "agent"
A repo-wide search for sourceType: "user" across non-test .ts on master returns nothing. On my store (1,431 active memories) the live distribution is agent 677 / historian 467 / dreamer 287 / user 0.
Why it matters
When the operator says "always use X" and the agent records it with ctx_memory, the row lands as source_type='agent' — the same value as a memory the agent minted from its own reasoning with no human input. Downstream that means:
- The dashboard's green
user badge can never appear, so the operator cannot visually separate their own stated rules from agent inferences when auditing the store.
curate / verify cannot weight operator-stated facts differently. An agent inference and an operator instruction are equally eligible for archival. Given that archiving an operator-stated rule is strictly worse than archiving an agent guess, there is currently no signal to protect the former.
- Any future work wanting "operator said so" as a trust input has no column to read.
Scope
The type, the column (source_type TEXT DEFAULT 'historian', storage-db.ts:1047), and the dashboard arm all already exist — this is a write-path gap, not a schema change. No migration needed.
The design question is how ctx_memory decides a write is operator-attributed. getSourceType (tools.ts:290-292) already reads an injectable deps.sourceType, so the plumbing is there. Options, roughly in increasing cost:
- An explicit optional tool parameter (
attribution: "operator") the agent sets when recording something the operator stated. Cheapest; relies on the agent being honest, which is the same trust level as every other field it writes.
- Host-side inference at the write boundary — e.g. attribute to
user when the current turn's triggering message is a real (non-synthetic) user message. More robust, more machinery.
- Leave writes alone and treat the variant as dashboard-only vestigial — in which case the honest fix is deleting it from the type so it stops implying a distinction that does not exist.
I lean (1), but this is your call and I have not written any of it. Happy to send a PR for whichever shape you prefer, or to close this if (3) is the intent.
Correction on my own claim
I initially told a peer this variant did not exist at all, generalizing from its absence in live data to its absence in the schema. That was wrong — the enum arm and the dashboard rendering were there the whole time, which makes this a materially smaller ask than I first described. Verified from source before filing.
Summary
MemorySourceTypehas four variants and the dashboard renders all four, but only three are reachable. Nothing in the codebase ever writessourceType: "user", so a fact the operator stated is stored byte-identically to one an agent inferred on its own.Small and self-contained. Filing separately because it stands on its own regardless of the larger proposal that motivated me to notice it (see #335).
Evidence
All references against
upstream/master@b27a6d76(v0.38.0).The type declares it —
packages/plugin/src/features/magic-context/memory/types.ts:25:The dashboard already renders it —
packages/dashboard/src/components/MemoryBrowser/MemoryBrowser.tsx:342-350:But every write site is one of the other three:
A repo-wide search for
sourceType: "user"across non-test.tson master returns nothing. On my store (1,431 active memories) the live distribution isagent677 /historian467 /dreamer287 /user0.Why it matters
When the operator says "always use X" and the agent records it with
ctx_memory, the row lands assource_type='agent'— the same value as a memory the agent minted from its own reasoning with no human input. Downstream that means:userbadge can never appear, so the operator cannot visually separate their own stated rules from agent inferences when auditing the store.curate/verifycannot weight operator-stated facts differently. An agent inference and an operator instruction are equally eligible for archival. Given that archiving an operator-stated rule is strictly worse than archiving an agent guess, there is currently no signal to protect the former.Scope
The type, the column (
source_type TEXT DEFAULT 'historian',storage-db.ts:1047), and the dashboard arm all already exist — this is a write-path gap, not a schema change. No migration needed.The design question is how
ctx_memorydecides a write is operator-attributed.getSourceType(tools.ts:290-292) already reads an injectabledeps.sourceType, so the plumbing is there. Options, roughly in increasing cost:attribution: "operator") the agent sets when recording something the operator stated. Cheapest; relies on the agent being honest, which is the same trust level as every other field it writes.userwhen the current turn's triggering message is a real (non-synthetic) user message. More robust, more machinery.I lean (1), but this is your call and I have not written any of it. Happy to send a PR for whichever shape you prefer, or to close this if (3) is the intent.
Correction on my own claim
I initially told a peer this variant did not exist at all, generalizing from its absence in live data to its absence in the schema. That was wrong — the enum arm and the dashboard rendering were there the whole time, which makes this a materially smaller ask than I first described. Verified from source before filing.