Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion python/packages/core/agent_framework/_harness/_memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,7 @@ def list_topics(self, session: AgentSession, *, source_id: str) -> list[MemoryTo
topic_path.read_text(encoding="utf-8"),
fallback_topic=topic_path.stem.replace("-", " "),
)
record.slug = topic_path.stem
topics.append(record)
return sorted(topics, key=lambda record: (record.topic.lower(), record.updated_at))

Expand All @@ -831,7 +832,9 @@ def get_topic(self, session: AgentSession, *, source_id: str, topic: str) -> Mem
topic_path = self._topic_path(session, source_id=source_id, topic=topic)
if not topic_path.exists():
raise FileNotFoundError(f"No memory topic named '{topic}' was found for this owner.")
return MemoryTopicRecord.from_markdown(topic_path.read_text(encoding="utf-8"), fallback_topic=topic)
record = MemoryTopicRecord.from_markdown(topic_path.read_text(encoding="utf-8"), fallback_topic=topic)
record.slug = topic_path.stem
return record

def write_topic(self, session: AgentSession, record: MemoryTopicRecord, *, source_id: str) -> None:
"""Persist one topic memory file."""
Expand Down
54 changes: 54 additions & 0 deletions python/packages/core/tests/core/test_harness_memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
MemoryStore,
MemoryTopicRecord,
Message,
SessionContext,
)

from .test_filesystem import COLLIDING_IDENTIFIERS
Expand Down Expand Up @@ -134,6 +135,59 @@ def test_memory_topic_record_round_trips_through_dict_and_markdown() -> None:
assert "MemoryTopicRecord(" in repr(record)


@pytest.mark.parametrize("slug", [None, "tools", "Chosen Tools"])
def test_memory_file_store_preserves_topic_slug(tmp_path: Path, slug: str | None) -> None:
"""Reloaded topics keep their filename identity when the heading differs from the slug."""
session = AgentSession(session_id="session-1")
session.state["owner_id"] = "alice"
store = MemoryFileStore(tmp_path, owner_state_key="owner_id")
record = MemoryTopicRecord(
topic="Preferred Tools",
slug=slug,
summary="Use the preferred tools.",
memories=["Use Python for scripts."],
updated_at="2026-04-21T10:05:00+00:00",
session_ids=[session.session_id],
)
store.write_topic(session, record, source_id=DEFAULT_MEMORY_SOURCE_ID)

reopened_store = MemoryFileStore(tmp_path, owner_state_key="owner_id")
loaded = reopened_store.get_topic(session, source_id=DEFAULT_MEMORY_SOURCE_ID, topic=record.slug)
assert loaded == record
assert reopened_store.list_topics(session, source_id=DEFAULT_MEMORY_SOURCE_ID) == [record]

loaded.summary = "Use the updated tools."
reopened_store.write_topic(session, loaded, source_id=DEFAULT_MEMORY_SOURCE_ID)
assert reopened_store.list_topics(session, source_id=DEFAULT_MEMORY_SOURCE_ID) == [loaded]
assert reopened_store.get_topic(session, source_id=DEFAULT_MEMORY_SOURCE_ID, topic=record.slug) == loaded


async def test_memory_context_provider_loads_topic_with_custom_slug(tmp_path: Path) -> None:
"""The rebuilt index must point to the existing file so selected memory can be injected."""
session = AgentSession(session_id="session-1")
session.state["owner_id"] = "alice"
store = MemoryFileStore(tmp_path, owner_state_key="owner_id")
record = MemoryTopicRecord(
topic="Preferred Tools",
slug="tools",
summary="Use the preferred tools.",
memories=["Use Python for scripts."],
updated_at="2026-04-21T10:05:00+00:00",
)
store.write_topic(session, record, source_id=DEFAULT_MEMORY_SOURCE_ID)
provider = MemoryContextProvider(store=store)
context = SessionContext(session_id=session.session_id, input_messages=[Message("user", ["Which tools?"])])

await provider.before_run(agent=None, session=session, context=context, state={})

assert "topics/tools.md" in store.get_index_text(
session, source_id=DEFAULT_MEMORY_SOURCE_ID, line_limit=200, line_length=150
)
assert any(
"Use Python for scripts." in message.text for message in context.context_messages[DEFAULT_MEMORY_SOURCE_ID]
)


async def test_memory_file_store_writes_topics_index_state_and_transcripts(tmp_path) -> None:
"""The file-backed memory store should manage topics, ``MEMORY.md``, state, and transcript search."""
store = MemoryFileStore(
Expand Down
Loading