a desktop or cli session with no peerName in honcho.json and no gateway user id landed on a peer derived from the session key: user-default-<dir> for per-directory sessions, user-<channel>-<chat> for keyed ones. every directory got its own phantom peer, so the operator's turns and memory never reached their real peer and the injected representation went stale (#93326). _resolve_user_peer_id now raises HonchoPeerUnresolvedError instead of deriving a name. a peer is either the declared peerName or an identity the transport supplied. the provider records the failure, tells the model once that memory is off and which key to set, returns the same detail from tool calls, and stops retrying init because a missing config key does not heal mid-session. the memory-file migration gate loses its "no owner and no runtime identity" branch: that cohort no longer has a session to migrate into. whitespace-only peerName is treated as unset rather than sanitized to "--".
76 lines
3.8 KiB
Python
76 lines
3.8 KiB
Python
"""One-time upload of local memory files (MEMORY.md / USER.md / SOUL.md) into Honcho."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from pathlib import Path
|
|
|
|
from plugins.memory.honcho.session_auth import HonchoAuthError
|
|
|
|
logger = logging.getLogger("plugins.memory.honcho.session")
|
|
|
|
# (filename, upload name, description, peer kind) — peer kind picks user vs assistant peer.
|
|
_MEMORY_FILES = (
|
|
("MEMORY.md", "consolidated_memory.md", "Long-term agent notes and preferences", "user"),
|
|
("USER.md", "user_profile.md", "User profile and preferences", "user"),
|
|
("SOUL.md", "agent_soul.md", "Agent persona and identity configuration", "ai"),
|
|
)
|
|
|
|
|
|
class SessionMigrationMixin:
|
|
def migrate_memory_files(self, session_key: str, memory_dir: str) -> bool:
|
|
"""Upload MEMORY.md / USER.md / SOUL.md to Honcho when it activates on an instance with
|
|
locally consolidated memory; skips missing/empty files. True if at least one uploaded."""
|
|
memory_path = Path(memory_dir)
|
|
if not memory_path.exists():
|
|
return False
|
|
|
|
session = self._cached_session(session_key)
|
|
if not session:
|
|
logger.warning("No local session cached for '%s', skipping memory migration", session_key)
|
|
return False
|
|
if session.honcho_session_id not in self._sessions_cache:
|
|
logger.warning("No Honcho session cached for '%s', skipping memory migration", session_key)
|
|
return False
|
|
|
|
# Owner-scoped: these files describe the install owner; uploading them under another
|
|
# human's peer would make Honcho attribute the owner's facts to that person. The owner is
|
|
# the CONFIG peerName, never a re-resolution of the session's own peer (that would compare
|
|
# the triggering user to themselves). No declared owner: nobody can be proven to be the owner.
|
|
owner_peer_id = self._declared_owner_peer_id()
|
|
if owner_peer_id is None or session.user_peer_id != owner_peer_id:
|
|
logger.info("Skipping memory-file migration: session user peer '%s' is not the declared owner (peerName=%s)",
|
|
session.user_peer_id, owner_peer_id or "unset")
|
|
return False
|
|
|
|
uploaded = False
|
|
for filename, upload_name, description, target_kind in _MEMORY_FILES:
|
|
filepath = memory_path / filename
|
|
content = filepath.read_text(encoding="utf-8").strip() if filepath.exists() else ""
|
|
if not content:
|
|
continue
|
|
target_peer_id = session.user_peer_id if target_kind == "user" else session.assistant_peer_id
|
|
wrapped = ("<prior_memory_file>\n<context>\n"
|
|
"This file was consolidated from local conversations BEFORE Honcho was activated.\n"
|
|
f"{description}. Treat as foundational context for this user.\n"
|
|
f"</context>\n\n{content}\n</prior_memory_file>\n")
|
|
|
|
def _upload() -> None:
|
|
self._sdk_session(session.honcho_session_id).upload_file(
|
|
file=(upload_name, wrapped.encode("utf-8"), "text/plain"),
|
|
peer=self._get_or_create_peer(target_peer_id),
|
|
metadata={"source": "local_memory", "original_file": filename, "target_peer": target_kind},
|
|
)
|
|
|
|
try:
|
|
self._authed_call("memory migration upload", _upload)
|
|
logger.info("Uploaded %s to Honcho for %s (%s peer)", filename, session_key, target_kind)
|
|
uploaded = True
|
|
except HonchoAuthError:
|
|
logger.error("Honcho memory migration stopped after %s: auth failed", filename)
|
|
break
|
|
except Exception as e:
|
|
logger.error("Failed to upload %s to Honcho: %s", filename, e)
|
|
|
|
return uploaded
|