Metadata-Version: 2.4
Name: memdsl
Version: 0.1.0
Summary: Agent memory as normative source code: a lintable, queryable memory DSL for LLM agents
Author: liyuan
License: MIT
License-File: LICENSE
Keywords: agent,context-engineering,dsl,llm,memory
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.9
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == 'dev'
Description-Content-Type: text/markdown

# memdsl

[English](#english) | [中文](#中文)

## English

**Agent memory as normative source code.**

Most agent memory systems store what you said. Almost none of them
distinguish what an agent *must obey* from what it *may consider*. memdsl
treats long-term memory as a small, typed, lintable source language —
`.mem` files you can read, review, diff, version, and test — where a
`boundary` is a rule to enforce, not a fact to recall.

```mem
preference schedule.deep_work_mornings {
  subject: User
  claim: "Prefers deep work in the morning; meetings after 2pm."
  force: strong
  scope: scheduling
  evidence { source: chat quote: "Stop booking me into morning meetings." }
}

boundary privacy.no_family_in_public {
  subject: User
  rule: "Never include family member names or details in public-facing content."
  force: hard
  scope: global
  exceptions: [user_explicit_override]
  evidence { source: chat quote: "Anything about my family stays out of blog posts. Always." }
}
```

The difference matters: a preference shapes suggestions; a boundary binds.
Flat embeddings of "user said X" erase exactly this distinction, which is
why agents violate rules their memory technically "contains".

### 30 seconds: same question, one declaration apart

Ask an agent to *"draft a public blog post about building Aurora as a
working parent."* A similarity-ranked memory gives it the old chats where
the kids' names appear — and nothing that ranks a rule above a reminiscence.
The retrieved snippets are all just CONTEXT, so the names end up in the post:

```text
retrieved: "...shipped the sync fix after bedtime, Theo finally sleeping..."
retrieved: "aurora beta feedback thread..."
```

With the boundary declared, the evidence pack puts the rule where it cannot
be missed — in MUST, above every piece of context, whether or not the query
lexically matched it:

```console
$ memdsl query examples/alex/ -q "draft a public blog post about aurora"
MUST
- [boundary:privacy.no_family_in_public] Never include family member names
  or details in public-facing content. (exceptions: ['user_explicit_override'])

CONTEXT
- [state:aurora.beta_progress] Private beta with 40 testers; ...
```

Feed the pack to the LLM as context and the compliant behavior is to write
the post *without* family details — and to be able to cite
`boundary:privacy.no_family_in_public` as the reason. The pack is the
prompt; the MUST layer is what changes the answer.

### Lint it, query it

Memory that can be wrong deserves a linter:

```console
$ memdsl lint examples/lint-demo/
broken.mem:7:  error[unresolved_symbol]        subject 'User.Barista' is not a declared entity
broken.mem:20: error[missing_evidence]         active fact 'home.timezone' has no evidence block
broken.mem:28: warning[type_force_mismatch]    preference uses force: hard; promote it to a boundary
broken.mem:41: warning[boundary_without_exception]  confirm it is truly unconditional
broken.mem:53: warning[stale_state]            as_of 2025-11-02 is older than 180 days

6 declarations, 2 error(s), 3 warning(s)
```

Queries return a **layered evidence pack**, not a hit list. Hard
boundaries surface even when the query doesn't lexically match them;
declared conflicts are shown, not averaged away; open issues surface as
gaps instead of being hallucinated over:

```console
$ memdsl query examples/alex/ -q "should aurora keep the free tier"
# resolved subjects: Project.Aurora
MUST
- [boundary:privacy.no_family_in_public] Never include family member names ...

SHOULD
- (none)

CONTEXT
- [decision:aurora.pricing_free_tier] Keep a permanent free tier.
- [state:aurora.beta_progress] Private beta with 40 testers; sync is the top complaint. (as_of 2026-06-20)
- [goal:aurora.revenue_target_2026] Reach $2k MRR from Aurora by end of 2026.

CONFLICT
- [decision:aurora.pricing_free_tier] conflicts_with [aurora.revenue_target_2026]

MISSING
- open issue [open_issue:aurora.pricing_undecided]: Paid tier pricing is undecided: $5 flat vs usage-based.
```

Feed that pack — MUST/SHOULD/CONTEXT/CONFLICT/MISSING — to your LLM as
context. Every line carries a declaration id, so answers are citable and
auditable back to source and evidence.

### Install

```console
pip install memdsl        # or: pip install -e . from a checkout
memdsl lint examples/alex/
memdsl query examples/alex/ -q "plan tomorrow morning"
memdsl explain examples/alex/ decision:aurora.db_postgres_migration
```

Zero runtime dependencies. Python 3.9+.

Note: `memdsl lint examples/alex/` reports **one intentional warning** —
Alex's `schedule.no_meetings_before_10` boundary declares no `exceptions`,
and the linter asks you to confirm it is truly unconditional. That nudge
is the feature; a clean run is `examples/mira/`.

### What's in the box

- **A tiny declarative language** (`.mem`): typed declarations
  (`entity`, `fact`, `preference`, `boundary`, `principle`, `decision`,
  `state`, `open_issue`) with force, scope, evidence, relations
  (`supersedes`, `conflicts_with`, `refines`, ...), and lifecycle status.
  Full grammar and semantics in [docs/SPEC.md](docs/SPEC.md).
- **A linter** with ten diagnostics: dangling symbols, missing evidence,
  ambiguous aliases, stale states, boundaries without exceptions,
  preferences masquerading as laws, unmarked supersede chains.
- **A query executor** implementing the EvidencePack contract, plus
  `explain` for tracing one declaration's relations and provenance.
- **Synthetic example personas** (`examples/`) — fictional users "Alex"
  and "Mira" — and a deliberately broken file for the linter demo.

### What memdsl is *not*

- **Not a replacement for [Mem0](https://github.com/mem0ai/mem0),
  [Zep](https://www.getzep.com/), or
  [LangMem](https://langchain-ai.github.io/langmem/).** Those are
  retrieval/extraction platforms. memdsl is a *source format and
  contract* for the layer above: what memory means, how strongly it
  binds, and how it is maintained. You could compile `.mem` files into
  any of them.
- **Not a retrieval engine.** The reference executor is deliberately
  naive lexical matching — enough to demonstrate the contract. Production
  use should plug BM25/embeddings behind the same EvidencePack interface.
  Do not benchmark toy retrieval and conclude the format failed.
- **Not an auto-writer.** v0.1 has no write pipeline. The spec (§10)
  describes a gated, human-reviewed write path; automation should be
  earned with audited metrics, not assumed.

### Does the approach work?

Early evidence from the private system this was extracted from
(DigitalSelf, single-user): on a 100-question eval over the author's real
long-term memory, DSL-structured retrieval nearly doubled top-1 precision
against the same system's tuned RAG baseline (**0.57 vs 0.30**; hit rate
0.67 vs 0.53). On public conversational benchmarks (LongMemEval, LoCoMo)
it performs at parity with baselines under a retrieval-only harness whose
target mapping is still being audited — we explicitly do *not* claim
public-benchmark wins. Current honest costs: seconds-level query latency
at scale in the private implementation (being moved to write-time
compilation), and n=1 personalization. A reproducible benchmark report
will be published separately.

The interesting unmeasured dimension — and the reason this exists — is
**compliance, not recall**: existing memory benchmarks test whether an
agent can *find* a fact, none test whether it *respects* a boundary. A
boundary-compliance benchmark is the roadmap's centerpiece.

### Related work

Typed/structured agent memory is converging fast:
[MemIR](https://arxiv.org/abs/2605.25869) (typed memory IR, provenance-role
separation), [Zep/Graphiti](https://www.getzep.com/) (temporal knowledge
graphs with fact validity windows), [A-Mem](https://arxiv.org/abs/2502.12110)
(Zettelkasten-style linked notes), [MemOS](https://arxiv.org/abs/2507.03724)
(memory scheduling), and the `CLAUDE.md`/`AGENTS.md` culture of local,
reviewable context files. memdsl's position in that landscape: local-first
plain-text source, an explicit **normative layer** (force + boundaries +
exceptions + MUST/SHOULD rendering), and code-style diagnostics as a
first-class surface.

### Roadmap

- Target-mapping audit + reproducible benchmark report
- Boundary-compliance benchmark (does the agent *respect* MUST items?)
- Pluggable retrieval backends (BM25, embeddings) behind the EvidencePack contract
- Module directory compilation for query planning
- Gated write pipeline with review queue

---

Today, memdsl defines memory as typed, auditable source code. Future
runtimes can navigate these declarations the way developers navigate
code — following relations, inspecting evidence, tracing history, and
asking for missing information instead of guessing.

### License

Code: [MIT](LICEN