15 KiB
honcho-integration-spec
Comparison of Hermes Agent vs. openclaw-honcho — and a porting spec for bringing Hermes patterns into other Honcho integrations.
Overview
Two independent Honcho integrations have been built for two different agent runtimes: Hermes Agent (Python, baked into the runner) and openclaw-honcho (TypeScript plugin via hook/tool API). Both use the same Honcho peer paradigm — dual peer model, session.context(), peer.chat() — but they made different tradeoffs at every layer.
This document maps those tradeoffs and defines a porting spec: a set of Hermes-originated patterns, each stated as an integration-agnostic interface, that any Honcho integration can adopt regardless of runtime or language.
Scope Both integrations work correctly today. This spec is about the delta — patterns in Hermes that are worth propagating and patterns in openclaw-honcho that Hermes should eventually adopt. The spec is additive, not prescriptive.
Architecture comparison
Hermes: baked-in runner
Honcho is initialised directly inside AIAgent.__init__. There is no plugin boundary. Session management, context injection, async prefetch, and CLI surface are all first-class concerns of the runner. Context is injected once per session (baked into _cached_system_prompt) and never re-fetched mid-session — this maximises prefix cache hits at the LLM provider.
Turn flow:
user message
→ _honcho_prefetch() (reads cache — no HTTP)
→ _build_system_prompt() (first turn only, cached)
→ LLM call
→ response
→ _honcho_fire_prefetch() (daemon threads, turn end)
→ prefetch_context() thread ──┐
→ prefetch_dialectic() thread ─┴→ _context_cache / _dialectic_cache
openclaw-honcho: hook-based plugin
The plugin registers hooks against OpenClaw's event bus. Context is fetched synchronously inside before_prompt_build on every turn. Message capture happens in agent_end. The multi-agent hierarchy is tracked via subagent_spawned. This model is correct but every turn pays a blocking Honcho round-trip before the LLM call can begin.
Turn flow:
user message
→ before_prompt_build (BLOCKING HTTP — every turn)
→ session.context()
→ system prompt assembled
→ LLM call
→ response
→ agent_end hook
→ session.addMessages()
→ session.setMetadata()
Diff table
| Dimension | Hermes Agent | openclaw-honcho |
|---|---|---|
| Context injection timing | Once per session (cached). Zero HTTP on response path after turn 1. | Every turn, blocking. Fresh context per turn but adds latency. |
| Prefetch strategy | Daemon threads fire at turn end; consumed next turn from cache. | None. Blocking call at prompt-build time. |
| Dialectic (peer.chat) | Prefetched async; result injected into system prompt next turn. | On-demand via honcho_recall / honcho_analyze tools. |
| Reasoning level | Dynamic: scales with message length. Floor = config default. Cap = "high". | Fixed per tool: recall=minimal, analyze=medium. |
| Memory modes | user_memory_mode / agent_memory_mode: hybrid / honcho / local. |
None. Always writes to Honcho. |
| Write frequency | async (background queue), turn, session, N turns. | After every agent_end (no control). |
| AI peer identity | observe_me=True, seed_ai_identity(), get_ai_representation(), SOUL.md → AI peer. |
Agent files uploaded to agent peer at setup. No ongoing self-observation. |
| Context scope | User peer + AI peer representation, both injected. | User peer (owner) representation + conversation summary. peerPerspective on context call. |
| Session naming | per-directory / global / manual map / title-based. | Derived from platform session key. |
| Multi-agent | Single-agent only. | Parent observer hierarchy via subagent_spawned. |
| Tool surface | Single query_user_context tool (on-demand dialectic). |
6 tools: session, profile, search, context (fast) + recall, analyze (LLM). |
| Platform metadata | Not stripped. | Explicitly stripped before Honcho storage. |
| Message dedup | None. | lastSavedIndex in session metadata prevents re-sending. |
| CLI surface in prompt | Management commands injected into system prompt. Agent knows its own CLI. | Not injected. |
| AI peer name in identity | Replaces "Hermes Agent" in DEFAULT_AGENT_IDENTITY when configured. | Not implemented. |
| QMD / local file search | Not implemented. | Passthrough tools when QMD backend configured. |
| Workspace metadata | Not implemented. | agentPeerMap in workspace metadata tracks agent→peer ID. |
Patterns
Six patterns from Hermes are worth adopting in any Honcho integration. Each is described as an integration-agnostic interface.
Hermes contributes:
- Async prefetch (zero-latency)
- Dynamic reasoning level
- Per-peer memory modes
- AI peer identity formation
- Session naming strategies
- CLI surface injection
openclaw-honcho contributes back (Hermes should adopt):
lastSavedIndexdedup- Platform metadata stripping
- Multi-agent observer hierarchy
peerPerspectiveoncontext()- Tiered tool surface (fast/LLM)
- Workspace
agentPeerMap
Spec: async prefetch
Problem
Calling session.context() and peer.chat() synchronously before each LLM call adds 200–800ms of Honcho round-trip latency to every turn.
Pattern
Fire both calls as non-blocking background work at the end of each turn. Store results in a per-session cache keyed by session ID. At the start of the next turn, pop from cache — the HTTP is already done. First turn is cold (empty cache); all subsequent turns are zero-latency on the response path.
Interface contract
interface AsyncPrefetch {
// Fire context + dialectic fetches at turn end. Non-blocking.
firePrefetch(sessionId: string, userMessage: string): void;
// Pop cached results at turn start. Returns empty if cache is cold.
popContextResult(sessionId: string): ContextResult | null;
popDialecticResult(sessionId: string): string | null;
}
type ContextResult = {
representation: string;
card: string[];
aiRepresentation?: string; // AI peer context if enabled
summary?: string; // conversation summary if fetched
};
Implementation notes
- Python:
threading.Thread(daemon=True). Write todict[session_id, result]— GIL makes this safe for simple writes. - TypeScript:
Promisestored inMap<string, Promise<ContextResult>>. Await at pop time. If not resolved yet, return null — do not block. - The pop is destructive: clears the cache entry after reading so stale data never accumulates.
- Prefetch should also fire on first turn (even though it won't be consumed until turn 2).
openclaw-honcho adoption
Move session.context() from before_prompt_build to a post-agent_end background task. Store result in state.contextCache. In before_prompt_build, read from cache instead of calling Honcho. If cache is empty (turn 1), inject nothing — the prompt is still valid without Honcho context on the first turn.
Spec: dynamic reasoning level
Problem
Honcho's dialectic endpoint supports reasoning levels from minimal to max. A fixed level per tool wastes budget on simple queries and under-serves complex ones.
Pattern
Select the reasoning level dynamically based on the user's message. Use the configured default as a floor. Bump by message length. Cap auto-selection at high — never select max automatically.
Logic
< 120 chars → default (typically "low")
120–400 chars → one level above default (cap at "high")
> 400 chars → two levels above default (cap at "high")
Config key
Add dialecticReasoningLevel (string, default "low"). This sets the floor. The dynamic bump always applies on top.
openclaw-honcho adoption
Apply in honcho_recall and honcho_analyze: replace fixed reasoningLevel with the dynamic selector. honcho_recall uses floor "minimal", honcho_analyze uses floor "medium" — both still bump with message length.
Spec: per-peer memory modes
Problem
Users want independent control over whether user context and agent context are written locally, to Honcho, or both.
Modes
| Mode | Effect |
|---|---|
hybrid |
Write to both local files and Honcho (default) |
honcho |
Honcho only — disable corresponding local file writes |
local |
Local files only — skip Honcho sync for this peer |
Config schema
{
"memoryMode": "hybrid",
"userMemoryMode": "honcho",
"agentMemoryMode": "hybrid"
}
Resolution order: per-peer field wins → shorthand memoryMode → default "hybrid".
Effect on Honcho sync
userMemoryMode=local: skip adding user peer messages to HonchoagentMemoryMode=local: skip adding assistant peer messages to Honcho- Both local: skip
session.addMessages()entirely userMemoryMode=honcho: disable local USER.md writesagentMemoryMode=honcho: disable local MEMORY.md / SOUL.md writes
Spec: AI peer identity formation
Problem
Honcho builds the user's representation organically by observing what the user says. The same mechanism exists for the AI peer — but only if observe_me=True is set for the agent peer. Without it, the agent peer accumulates nothing.
Additionally, existing persona files (SOUL.md, IDENTITY.md) should seed the AI peer's Honcho representation at first activation.
Part A: observe_me=True for agent peer
await session.addPeers([
[ownerPeer.id, { observeMe: true, observeOthers: false }],
[agentPeer.id, { observeMe: true, observeOthers: true }], // was false
]);
One-line change. Foundational. Without it, the AI peer representation stays empty regardless of what the agent says.
Part B: seedAiIdentity()
async function seedAiIdentity(
agentPeer: Peer,
content: string,
source: string
): Promise<boolean> {
const wrapped = [
`<ai_identity_seed>`,
`<source>${source}</source>`,
``,
content.trim(),
`</ai_identity_seed>`,
].join("\n");
await agentPeer.addMessage("assistant", wrapped);
return true;
}
Part C: migrate agent files at setup
During honcho setup, upload agent-self files (SOUL.md, IDENTITY.md, AGENTS.md) to the agent peer via seedAiIdentity() instead of session.uploadFile(). This routes content through Honcho's observation pipeline.
Part D: AI peer name in identity
When the agent has a configured name, prepend it to the injected system prompt:
const namePrefix = agentName ? `You are ${agentName}.\n\n` : "";
return { systemPrompt: namePrefix + "## User Memory Context\n\n" + sections };
CLI surface
honcho identity <file> # seed from file
honcho identity --show # show current AI peer representation
Spec: session naming strategies
Problem
A single global session means every project shares the same Honcho context. Per-directory sessions provide isolation without requiring users to name sessions manually.
Strategies
| Strategy | Session key | When to use |
|---|---|---|
per-directory |
basename of CWD | Default. Each project gets its own session. |
global |
fixed string "global" |
Single cross-project session. |
| manual map | user-configured per path | sessions config map overrides directory basename. |
| title-based | sanitized session title | When agent supports named sessions set mid-conversation. |
Config schema
{
"sessionStrategy": "per-directory",
"sessionPeerPrefix": false,
"sessions": {
"/home/user/projects/foo": "foo-project"
}
}
CLI surface
honcho sessions # list all mappings
honcho map <name> # map cwd to session name
honcho map # no-arg = list mappings
Resolution order: manual map → session title → directory basename → platform key.
Spec: CLI surface injection
Problem
When a user asks "how do I change my memory settings?" the agent either hallucinates or says it doesn't know. The agent should know its own management interface.
Pattern
When Honcho is active, append a compact command reference to the system prompt. Keep it under 300 chars.
# Honcho memory integration
Active. Session: {sessionKey}. Mode: {mode}.
Management commands:
honcho status — show config + connection
honcho mode [hybrid|honcho|local] — show or set memory mode
honcho sessions — list session mappings
honcho map <name> — map directory to session
honcho identity [file] [--show] — seed or show AI identity
honcho setup — full interactive wizard
openclaw-honcho checklist
Ordered by impact:
- Async prefetch — move
session.context()out ofbefore_prompt_buildinto post-agent_endbackground Promise - observe_me=True for agent peer — one-line change in
session.addPeers() - Dynamic reasoning level — add helper; apply in
honcho_recallandhoncho_analyze; adddialecticReasoningLevelto config - Per-peer memory modes — add
userMemoryMode/agentMemoryModeto config; gate Honcho sync and local writes - seedAiIdentity() — add helper; use during setup migration for SOUL.md / IDENTITY.md
- Session naming strategies — add
sessionStrategy,sessionsmap,sessionPeerPrefix - CLI surface injection — append command reference to
before_prompt_buildreturn value - honcho identity subcommand — seed from file or
--showcurrent representation - AI peer name injection — if
aiPeername configured, prepend to injected system prompt - honcho mode / sessions / map — CLI parity with Hermes
Already done in openclaw-honcho (do not re-implement): lastSavedIndex dedup, platform metadata stripping, multi-agent parent observer, peerPerspective on context(), tiered tool surface, workspace agentPeerMap, QMD passthrough, self-hosted Honcho.
nanobot-honcho checklist
Greenfield integration. Start from openclaw-honcho's architecture and apply all Hermes patterns from day one.
Phase 1 — core correctness
- Dual peer model (owner + agent peer), both with
observe_me=True - Message capture at turn end with
lastSavedIndexdedup - Platform metadata stripping before Honcho storage
- Async prefetch from day one — do not implement blocking context injection
- Legacy file migration at first activation (USER.md → owner peer, SOUL.md →
seedAiIdentity())
Phase 2 — configuration
- Config schema:
apiKey,workspaceId,baseUrl,memoryMode,userMemoryMode,agentMemoryMode,dialecticReasoningLevel,sessionStrategy,sessions - Per-peer memory mode gating
- Dynamic reasoning level
- Session naming strategies
Phase 3 — tools and CLI
- Tool surface:
honcho_profile,honcho_recall,honcho_analyze,honcho_search,honcho_context - CLI:
setup,status,sessions,map,mode,identity - CLI surface injection into system prompt
- AI peer name wired into agent identity