Skip to content
Closed
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
16 changes: 14 additions & 2 deletions src/pinky_memory/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@ def _log(msg: str) -> None:
print(msg, file=sys.stderr, flush=True)


def _parse_reflection_type(type_str: str) -> ReflectionType:
"""Coerce a caller-supplied type to ReflectionType, defaulting to `fact`
on an unrecognized value instead of raising. Callers (including dream
runs) occasionally pass a plausible-sounding but invalid type; failing
hard here previously aborted the entire dream run (#session_log bug)."""
try:
return ReflectionType(type_str)
except ValueError:
_log(f"reflect: invalid type={type_str!r}, defaulting to 'fact'")
return ReflectionType.fact


# Strict agent-name slug for cross-agent memory targets (#614/#145). Mirrors
# the trust-boundary slug discipline tracked in #105 — defends the
# store-factory path resolution against traversal / injection even though the
Expand Down Expand Up @@ -369,7 +381,7 @@ def reflect(
"""
input_data = ReflectInput(
content=content,
type=ReflectionType(type),
type=_parse_reflection_type(type),
context=context,
project=project,
salience=salience,
Expand Down Expand Up @@ -430,7 +442,7 @@ def reflect_for(
s = _resolve_target_store(target_agent)
input_data = ReflectInput(
content=content,
type=ReflectionType(type),
type=_parse_reflection_type(type),
context=context,
project=project,
salience=salience,
Expand Down
10 changes: 10 additions & 0 deletions tests/test_memory_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,16 @@ def test_reflect_with_all_fields(self, srv):
assert result["salience"] == 5
assert result["type"] == "project_state"

def test_reflect_invalid_type_falls_back_to_fact(self, srv):
"""Regression: an unrecognized type (e.g. dream-hallucinated 'session_log')
must not raise — it previously crashed the whole dream run."""
result = json.loads(_tools(srv)["reflect"](
content="something the dream agent decided to call session_log",
type="session_log",
))
assert result["stored"] is True
assert result["type"] == "fact"

def test_reflect_supersedes(self, srv, store):
# First memory
r1 = json.loads(_tools(srv)["reflect"](content="old fact", type="fact"))
Expand Down