chore: initialize EverOS 1.0.0

md-first memory extraction framework for AI agents.

Markdown is the single source of truth; SQLite holds state and LanceDB
provides the rebuildable vector + BM25 + scalar index. The codebase follows
a single-direction DDD layering (entrypoints -> service -> memory -> infra,
with component / core / config cross-cutting) enforced by import-linter.

Engineering surface:
- Coding conventions in .claude/rules/ (path-scoped) and workflows in
  .claude/skills/ (/commit, /new-branch, /pr).
- GitHub Actions CI runs make lint + test + integration; pre-commit mirrors
  the gates locally (ruff, hygiene hooks, gitlint commit-msg).
- Commit messages follow Conventional Commits, enforced by gitlint.
- make lint also enforces datetime two-zone discipline and OpenAPI drift.
This commit is contained in:
Elliot Chen
2026-06-05 22:35:51 +08:00
commit 518b8eca85
636 changed files with 160553 additions and 0 deletions

View File

@ -0,0 +1,49 @@
"""Business markdown readers — symmetric with the writers.
Daily-log markdown is parsed via :class:`MarkdownReader` from ``core``
(the base reader returns frontmatter dict + body + entry markers, all
schema-agnostic). Reader classes here add the **business-aware
locator** layer:
* :class:`BaseDailyReader` + subclasses — bind a daily-log schema,
resolve ``(scope_id, date)`` to a file, locate entries by id,
and optionally upgrade to :class:`StructuredEntry`. Symmetric
with :class:`BaseDailyWriter`.
* :class:`AgentSkillReader` — reads ``SKILL.md`` and parses the
frontmatter into the caller-supplied ``AgentSkillFrontmatter``
subclass; also reads individual reference / script files.
* :class:`ProfileReader` — reads a fixed-name profile file
(``user.md`` / ``agent.md`` / ``soul.md`` / …) and parses its
frontmatter into the caller-supplied schema.
By design, no batch / list APIs live here: bulk enumeration for
prompt-budget or cross-record queries goes through sqlite/lancedb
(see the cascade daemon's index sync), not a markdown directory walk.
External usage::
from everos.infra.persistence.markdown.readers import (
BaseDailyReader,
EpisodeReader,
AgentSkillReader,
ProfileReader,
)
"""
from .agent_case_reader import AgentCaseReader as AgentCaseReader
from .agent_skill_reader import AgentSkillReader as AgentSkillReader
from .atomic_fact_reader import AtomicFactReader as AtomicFactReader
from .base import BaseDailyReader as BaseDailyReader
from .episode_reader import EpisodeReader as EpisodeReader
from .foresight_reader import ForesightReader as ForesightReader
from .profile_reader import ProfileReader as ProfileReader
__all__ = [
"AgentCaseReader",
"AgentSkillReader",
"AtomicFactReader",
"BaseDailyReader",
"EpisodeReader",
"ForesightReader",
"ProfileReader",
]

View File

@ -0,0 +1,31 @@
"""AgentCase daily-log reader — symmetric with :class:`AgentCaseWriter`."""
from __future__ import annotations
import datetime as _dt
from pathlib import Path
from everos.core.persistence import MemoryRoot
from ..mds import AgentCaseDailyFrontmatter
from .base import BaseDailyReader
class AgentCaseReader(BaseDailyReader):
"""Read agent-case daily-log files."""
schema = AgentCaseDailyFrontmatter
def __init__(self, root: MemoryRoot) -> None:
super().__init__(root)
def path_for(
self,
agent_id: str,
date: _dt.date | None = None,
*,
app_id: str = "default",
project_id: str = "default",
) -> Path:
"""Resolve the agent-case daily-log path under the <app>/<project> prefix."""
return super().path_for(agent_id, date, app_id=app_id, project_id=project_id)

View File

@ -0,0 +1,161 @@
"""AgentSkillReader — typed read for the AgentSkill directory layout.
Pairs with :class:`AgentSkillWriter`:
- :meth:`read_main` reads ``SKILL.md`` and returns the caller's
:class:`AgentSkillFrontmatter` subclass instance + the Tier-2 body, so
the caller never deals with raw dicts.
- :meth:`read_reference` / :meth:`read_script` are plain text reads;
no frontmatter, no schema.
All three return ``None`` when the target is missing — readers do not
raise on absence, since "skill not yet created" is a normal state for
the upsert-style workflow. Callers that need to distinguish "missing"
from "empty body" check for ``None`` explicitly.
Path resolution mirrors :class:`AgentSkillWriter` and reads the same
ClassVars off :class:`AgentSkillFrontmatter`.
"""
from __future__ import annotations
from pathlib import Path
from typing import TypeVar
import anyio
from everos.core.persistence import MarkdownReader, MemoryRoot
from ..mds import AgentSkillFrontmatter
T = TypeVar("T", bound=AgentSkillFrontmatter)
class AgentSkillReader:
"""Single-skill reader for the directory + progressive-disclosure layout."""
def __init__(self, root: MemoryRoot) -> None:
self._root = root
# ── Public API ────────────────────────────────────────────────────────
async def read_main(
self,
agent_id: str,
skill_name: str,
*,
schema: type[T],
app_id: str = "default",
project_id: str = "default",
) -> tuple[T, str] | None:
"""Read ``SKILL.md`` and parse its frontmatter into ``schema``.
Args:
schema: Concrete :class:`AgentSkillFrontmatter` subclass. The
frontmatter dict is validated against this schema via
:meth:`pydantic.BaseModel.model_validate`; extra fields
ride along (chassis sets ``extra="allow"``).
Returns:
``(frontmatter, body)`` on success, ``None`` if the file
does not exist. ``body`` is the raw text after the closing
``---``; the trailing newline added by :class:`AgentSkillWriter`
is stripped to give the *logical* body back.
"""
path = self._main_path(agent_id, skill_name, app_id, project_id)
if not await anyio.Path(path).is_file():
return None
parsed = await MarkdownReader.read(path)
frontmatter = schema.model_validate(parsed.frontmatter)
body = parsed.body.rstrip("\n")
return frontmatter, body
async def read_reference(
self,
agent_id: str,
skill_name: str,
reference_name: str,
*,
app_id: str = "default",
project_id: str = "default",
) -> str | None:
"""Read ``references/<reference_name>.md`` verbatim, ``None`` if absent."""
path = self._reference_path(
agent_id, skill_name, reference_name, app_id, project_id
)
apath = anyio.Path(path)
if not await apath.is_file():
return None
text = await apath.read_text(encoding="utf-8")
return text.rstrip("\n")
async def read_script(
self,
agent_id: str,
skill_name: str,
script_filename: str,
*,
app_id: str = "default",
project_id: str = "default",
) -> str | None:
"""Read ``scripts/<script_filename>`` verbatim, ``None`` if absent.
Reading ≠ executing — this only returns the source text.
Sandboxing / exec-policy decisions belong to the caller.
"""
path = self._script_path(
agent_id, skill_name, script_filename, app_id, project_id
)
apath = anyio.Path(path)
if not await apath.is_file():
return None
text = await apath.read_text(encoding="utf-8")
return text.rstrip("\n")
# ── Internals — same shape as AgentSkillWriter ────────────────────────────
def _skill_dir(
self, agent_id: str, skill_name: str, app_id: str, project_id: str
) -> Path:
return (
self._root.agents_dir(app_id, project_id)
/ agent_id
/ AgentSkillFrontmatter.SKILLS_CONTAINER_NAME
/ f"{AgentSkillFrontmatter.SKILL_DIR_PREFIX}{skill_name}"
)
def _main_path(
self, agent_id: str, skill_name: str, app_id: str, project_id: str
) -> Path:
return (
self._skill_dir(agent_id, skill_name, app_id, project_id)
/ AgentSkillFrontmatter.SKILL_MAIN_FILENAME
)
def _reference_path(
self,
agent_id: str,
skill_name: str,
reference_name: str,
app_id: str,
project_id: str,
) -> Path:
return (
self._skill_dir(agent_id, skill_name, app_id, project_id)
/ AgentSkillFrontmatter.SKILL_REFERENCES_DIR_NAME
/ f"{reference_name}.md"
)
def _script_path(
self,
agent_id: str,
skill_name: str,
script_filename: str,
app_id: str,
project_id: str,
) -> Path:
return (
self._skill_dir(agent_id, skill_name, app_id, project_id)
/ AgentSkillFrontmatter.SKILL_SCRIPTS_DIR_NAME
/ script_filename
)

View File

@ -0,0 +1,31 @@
"""AtomicFact daily-log reader — symmetric with :class:`AtomicFactWriter`."""
from __future__ import annotations
import datetime as _dt
from pathlib import Path
from everos.core.persistence import MemoryRoot
from ..mds import AtomicFactDailyFrontmatter
from .base import BaseDailyReader
class AtomicFactReader(BaseDailyReader):
"""Read atomic-fact daily-log files."""
schema = AtomicFactDailyFrontmatter
def __init__(self, root: MemoryRoot) -> None:
super().__init__(root)
def path_for(
self,
owner_id: str,
date: _dt.date | None = None,
*,
app_id: str = "default",
project_id: str = "default",
) -> Path:
"""Resolve the atomic-fact daily-log path under the <app>/<project> prefix."""
return super().path_for(owner_id, date, app_id=app_id, project_id=project_id)

View File

@ -0,0 +1,177 @@
"""Base business reader for daily-log markdown files.
Symmetric to :class:`BaseDailyWriter`: reads the daily-log file for
a given ``(scope_id, date)``, locates entries by id within it, and
optionally upgrades them to :class:`StructuredEntry` so service-layer
callers don't have to re-do that plumbing each time.
Subclass usage::
class _MemcellReader(BaseDailyReader):
schema = UserMemcellDailyFrontmatter
reader = _MemcellReader(root)
parsed = reader.read_for("u_jason") # today's file
entry = reader.find_entry("u_jason", "umc_20260422_0001")
structured = reader.find_structured("u_jason", entry.id)
The reader does **not** typed-parse the file's frontmatter dict — the
schema is used only for path resolution (matching what the appender
writes). Frontmatter validation belongs to higher-level callers that
know the business rules.
Path resolution is identical to :class:`BaseDailyWriter` (same
``SCOPE_DIR`` / ``DIR_NAME`` / ``FILE_PREFIX`` ClassVars), so a
reader and writer bound to the same schema agree on every path.
"""
from __future__ import annotations
import datetime as _dt
from pathlib import Path
from typing import ClassVar
import anyio
from everos.component.utils.datetime import today_with_timezone
from everos.core.persistence import (
BaseFrontmatter,
Entry,
EntryId,
MarkdownReader,
MemoryRoot,
ParsedMarkdown,
StructuredEntry,
find_entry,
)
class BaseDailyReader:
"""Single-record reader for daily-log markdown files.
Subclasses bind a :class:`BaseFrontmatter` subclass via the
``schema`` ClassVar. The schema must declare ``SCOPE_DIR``,
``DIR_NAME``, and ``FILE_PREFIX`` (same set the appender uses); no
``ENTRY_ID_PREFIX`` requirement here because the reader takes the
entry id from the caller, not the schema.
"""
schema: ClassVar[type[BaseFrontmatter]] # subclass must declare
def __init__(self, root: MemoryRoot) -> None:
schema = getattr(type(self), "schema", None)
if schema is None:
raise TypeError(
f"{type(self).__name__} must declare a class-level ``schema`` attribute"
)
for attr in ("SCOPE_DIR", "DIR_NAME", "FILE_PREFIX"):
if not getattr(schema, attr, None):
raise TypeError(f"{schema.__name__} missing ClassVar {attr!r}")
self._root = root
# ── Public API ────────────────────────────────────────────────────────
async def read_for(
self,
scope_id: str,
date: _dt.date | None = None,
*,
app_id: str = "default",
project_id: str = "default",
) -> ParsedMarkdown | None:
"""Read the daily-log file for ``(scope_id, date)``.
Args:
scope_id: ``user_id`` or ``agent_id``.
date: Date bucket — defaults to today in the configured TZ.
app_id: App scope segment (defaults to the ``"default"`` space).
project_id: Project scope segment (defaults to ``"default"``).
Returns:
:class:`ParsedMarkdown` (frontmatter dict + body + entries),
or ``None`` when the file does not exist on disk. ``None``
avoids forcing every caller to wrap reads in try/except —
"no file yet" is a normal early state.
"""
path = self._resolve_path(
scope_id, date or today_with_timezone(), app_id, project_id
)
if not await anyio.Path(path).is_file():
return None
return await MarkdownReader.read(path)
async def find_entry(
self,
scope_id: str,
entry_id: str | EntryId,
*,
app_id: str = "default",
project_id: str = "default",
) -> Entry | None:
"""Locate the entry with ``entry_id`` inside its daily-log file.
The date bucket is taken from the entry id (an :class:`EntryId`
encodes its own date), so the caller doesn't pass a date.
Returns ``None`` if either the file or the entry is missing.
"""
eid = entry_id if isinstance(entry_id, EntryId) else EntryId.parse(entry_id)
eid_str = eid.format()
parsed = await self.read_for(
scope_id, eid.date, app_id=app_id, project_id=project_id
)
if parsed is None:
return None
return find_entry(parsed.body, eid_str)
async def find_structured(
self,
scope_id: str,
entry_id: str | EntryId,
*,
app_id: str = "default",
project_id: str = "default",
) -> StructuredEntry | None:
"""Locate the entry and parse its body as audit-form data.
Sugar over :meth:`find_entry` + :meth:`Entry.as_structured`.
Returns ``None`` if the entry is missing.
"""
entry = await self.find_entry(
scope_id, entry_id, app_id=app_id, project_id=project_id
)
if entry is None:
return None
return entry.as_structured()
def path_for(
self,
scope_id: str,
date: _dt.date | None = None,
*,
app_id: str = "default",
project_id: str = "default",
) -> Path:
"""Return the daily-log path for ``scope_id`` on ``date`` (today default).
Public counterpart of :meth:`_resolve_path` — symmetric with
:meth:`BaseDailyWriter.path_for`. Does not check existence.
"""
return self._resolve_path(
scope_id, date or today_with_timezone(), app_id, project_id
)
# ── Internals ─────────────────────────────────────────────────────────
def _resolve_path(
self, scope_id: str, date: _dt.date, app_id: str, project_id: str
) -> Path:
"""Build the daily-log path for ``scope_id`` on ``date``."""
# SCOPE_DIR ("users" / "agents") names the matching MemoryRoot method,
# which prepends the <app>/<project> business prefix.
scope_dir = getattr(self._root, f"{self.schema.SCOPE_DIR}_dir")
return (
scope_dir(app_id, project_id)
/ scope_id
/ self.schema.DIR_NAME
/ f"{self.schema.FILE_PREFIX}-{date.isoformat()}.md"
)

View File

@ -0,0 +1,41 @@
"""Episode daily-log reader — symmetric with :class:`EpisodeWriter`.
md is the source of truth for Episode memories; this reader gives
cascade / search / verification scripts a typed locator instead of
raw :class:`MarkdownReader` calls.
"""
from __future__ import annotations
import datetime as _dt
from pathlib import Path
from everos.core.persistence import MemoryRoot
from ..mds import EpisodeDailyFrontmatter
from .base import BaseDailyReader
class EpisodeReader(BaseDailyReader):
"""Read episode daily-log files."""
schema = EpisodeDailyFrontmatter
def __init__(self, root: MemoryRoot) -> None:
super().__init__(root)
def path_for(
self,
owner_id: str,
date: _dt.date | None = None,
*,
app_id: str = "default",
project_id: str = "default",
) -> Path:
"""Resolve the daily-log path for ``owner_id`` on ``date`` (today by default).
Mirrors :meth:`EpisodeWriter`'s path-resolution shape so callers
can locate the file written for a given owner / day (under the
``<app>/<project>`` prefix) without instantiating the writer.
"""
return super().path_for(owner_id, date, app_id=app_id, project_id=project_id)

View File

@ -0,0 +1,31 @@
"""Foresight daily-log reader — symmetric with :class:`ForesightWriter`."""
from __future__ import annotations
import datetime as _dt
from pathlib import Path
from everos.core.persistence import MemoryRoot
from ..mds import ForesightDailyFrontmatter
from .base import BaseDailyReader
class ForesightReader(BaseDailyReader):
"""Read foresight daily-log files."""
schema = ForesightDailyFrontmatter
def __init__(self, root: MemoryRoot) -> None:
super().__init__(root)
def path_for(
self,
owner_id: str,
date: _dt.date | None = None,
*,
app_id: str = "default",
project_id: str = "default",
) -> Path:
"""Resolve the foresight daily-log path under the <app>/<project> prefix."""
return super().path_for(owner_id, date, app_id=app_id, project_id=project_id)

View File

@ -0,0 +1,96 @@
"""ProfileReader — typed read for the single-file profile layout.
Pairs with :class:`ProfileWriter`. The schema (concrete profile
frontmatter class) is supplied per call; the reader pulls
``SCOPE_DIR`` + ``PROFILE_FILENAME`` ClassVars off it to build the
path, then ``MarkdownReader.read`` + ``schema.model_validate`` give
back a typed frontmatter instance plus the body string.
Returns ``None`` when the profile file does not exist — "not yet
written" is a normal early state for the upsert-style workflow.
"""
from __future__ import annotations
from pathlib import Path
from typing import TypeVar
import anyio
from everos.core.persistence import BaseFrontmatter, MarkdownReader, MemoryRoot
T = TypeVar("T", bound=BaseFrontmatter)
class ProfileReader:
"""Typed read for fixed-name profile markdown files."""
def __init__(self, root: MemoryRoot) -> None:
self._root = root
# ── Public API ────────────────────────────────────────────────────────
async def read(
self,
scope_id: str,
*,
schema: type[T],
app_id: str = "default",
project_id: str = "default",
) -> tuple[T, str] | None:
"""Read the profile file and parse its frontmatter into ``schema``.
Args:
scope_id: ``user_id`` or ``agent_id`` (must match the
schema's scope mixin).
schema: Concrete profile frontmatter class — must declare
``SCOPE_DIR`` (via scope mixin) and ``PROFILE_FILENAME``.
app_id: App scope segment (defaults to the ``"default"`` space).
project_id: Project scope segment (defaults to ``"default"``).
Returns:
``(frontmatter, body)`` on success; ``None`` if the file is
missing. ``body`` is the raw text after the closing ``---``
with the writer-added trailing newline stripped.
"""
path = self._resolve_path(scope_id, schema, app_id, project_id)
if not await anyio.Path(path).is_file():
return None
parsed = await MarkdownReader.read(path)
frontmatter = schema.model_validate(parsed.frontmatter)
body = parsed.body.rstrip("\n")
return frontmatter, body
def path_for(
self,
scope_id: str,
*,
schema: type[BaseFrontmatter],
app_id: str = "default",
project_id: str = "default",
) -> Path:
"""Return the profile path (no IO check)."""
return self._resolve_path(scope_id, schema, app_id, project_id)
# ── Internals — same shape as ProfileWriter ───────────────────────────
def _resolve_path(
self,
scope_id: str,
schema: type[BaseFrontmatter],
app_id: str,
project_id: str,
) -> Path:
scope_dir = getattr(schema, "SCOPE_DIR", "")
filename = getattr(schema, "PROFILE_FILENAME", None)
if not scope_dir:
raise TypeError(
f"{schema.__name__} missing ``SCOPE_DIR`` ClassVar — "
"must inherit a scope mixin (UserScopedFrontmatter / "
"AgentScopedFrontmatter)."
)
if not filename:
raise TypeError(f"{schema.__name__} missing ``PROFILE_FILENAME`` ClassVar.")
# SCOPE_DIR names the matching MemoryRoot method (<app>/<project> prefix).
scope_root = getattr(self._root, f"{scope_dir}_dir")(app_id, project_id)
return scope_root / scope_id / filename