diff --git a/honcho_integration/session.py b/honcho_integration/session.py index 4a49ca430..384d42f57 100644 --- a/honcho_integration/session.py +++ b/honcho_integration/session.py @@ -805,6 +805,41 @@ class HonchoSessionManager: logger.debug("Honcho search_context failed: %s", e) return "" + def create_conclusion(self, session_key: str, content: str) -> bool: + """Write a conclusion about the user back to Honcho. + + Conclusions are facts the AI peer observes about the user — + preferences, corrections, clarifications, project context. + They feed into the user's peer card and representation. + + Args: + session_key: Session to associate the conclusion with. + content: The conclusion text (e.g. "User prefers dark mode"). + + Returns: + True on success, False on failure. + """ + if not content or not content.strip(): + return False + + session = self._cache.get(session_key) + if not session: + logger.warning("No session cached for '%s', skipping conclusion", session_key) + return False + + assistant_peer = self._get_or_create_peer(session.assistant_peer_id) + try: + conclusions_scope = assistant_peer.conclusions_of(session.user_peer_id) + conclusions_scope.create([{ + "content": content.strip(), + "session_id": session.honcho_session_id, + }]) + logger.info("Created conclusion for %s: %s", session_key, content[:80]) + return True + except Exception as e: + logger.error("Failed to create conclusion: %s", e) + return False + def seed_ai_identity(self, session_key: str, content: str, source: str = "manual") -> bool: """ Seed the AI peer's Honcho representation from text content. diff --git a/run_agent.py b/run_agent.py index 0984f703d..fb20f0671 100644 --- a/run_agent.py +++ b/run_agent.py @@ -1595,22 +1595,24 @@ class AIAgent: ) elif recall_mode == "tools": honcho_block += ( - "Memory tools (most capable first; use cheaper tools when sufficient):\n" - " query_user_context — dialectic Q&A, LLM-synthesized answer\n" + "Memory tools:\n" + " query_user_context — ask Honcho a question, LLM-synthesized answer\n" " honcho_search — semantic search, raw excerpts, no LLM\n" - " honcho_profile — peer card, key facts, no LLM\n" + " honcho_profile — user's peer card, key facts, no LLM\n" + " honcho_conclude — write a fact about the user to memory\n" ) - else: # auto + else: # hybrid honcho_block += ( "Honcho context (user representation, peer card, and recent session summary) " "is pre-loaded into this system prompt below. Use it to answer continuity " "questions ('where were we?', 'what were we working on?') WITHOUT calling " "any tools. Only call memory tools when you need information beyond what is " "already present in the Honcho Memory section.\n" - "Memory tools (most capable first; use cheaper tools when sufficient):\n" - " query_user_context — dialectic Q&A, LLM-synthesized answer\n" + "Memory tools:\n" + " query_user_context — ask Honcho a question, LLM-synthesized answer\n" " honcho_search — semantic search, raw excerpts, no LLM\n" - " honcho_profile — peer card, key facts, no LLM\n" + " honcho_profile — user's peer card, key facts, no LLM\n" + " honcho_conclude — write a fact about the user to memory\n" ) honcho_block += ( "Management commands (refer users here instead of explaining manually):\n" diff --git a/tools/honcho_tools.py b/tools/honcho_tools.py index 62987dc60..311b03745 100644 --- a/tools/honcho_tools.py +++ b/tools/honcho_tools.py @@ -164,6 +164,49 @@ def _handle_query_user_context(args: dict, **kw) -> str: return json.dumps({"error": f"Failed to query user context: {e}"}) +# ── honcho_conclude ── + +_CONCLUDE_SCHEMA = { + "name": "honcho_conclude", + "description": ( + "Write a conclusion about the user back to Honcho's memory. " + "Conclusions are persistent facts that build the user's profile — " + "preferences, corrections, clarifications, project context, or anything " + "the user tells you that should be remembered across sessions. " + "Use this when the user explicitly states a preference, corrects you, " + "or shares something they want remembered. " + "Examples: 'User prefers dark mode', 'User's project uses Python 3.11', " + "'User corrected: their name is spelled Eri not Eric'." + ), + "parameters": { + "type": "object", + "properties": { + "conclusion": { + "type": "string", + "description": "A factual statement about the user to persist in memory.", + } + }, + "required": ["conclusion"], + }, +} + + +def _handle_honcho_conclude(args: dict, **kw) -> str: + conclusion = args.get("conclusion", "") + if not conclusion: + return json.dumps({"error": "Missing required parameter: conclusion"}) + if not _session_manager or not _session_key: + return json.dumps({"error": "Honcho is not active for this session."}) + try: + ok = _session_manager.create_conclusion(_session_key, conclusion) + if ok: + return json.dumps({"result": f"Conclusion saved: {conclusion}"}) + return json.dumps({"error": "Failed to save conclusion."}) + except Exception as e: + logger.error("Error creating Honcho conclusion: %s", e) + return json.dumps({"error": f"Failed to save conclusion: {e}"}) + + # ── Registration ── from tools.registry import registry @@ -191,3 +234,11 @@ registry.register( handler=_handle_query_user_context, check_fn=_check_honcho_available, ) + +registry.register( + name="honcho_conclude", + toolset="honcho", + schema=_CONCLUDE_SCHEMA, + handler=_handle_honcho_conclude, + check_fn=_check_honcho_available, +)