378 lines
15 KiB
Markdown
378 lines
15 KiB
Markdown
# 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):**
|
||
- `lastSavedIndex` dedup
|
||
- Platform metadata stripping
|
||
- Multi-agent observer hierarchy
|
||
- `peerPerspective` on `context()`
|
||
- 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
|
||
|
||
```typescript
|
||
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 to `dict[session_id, result]` — GIL makes this safe for simple writes.
|
||
- **TypeScript:** `Promise` stored in `Map<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
|
||
|
||
```json
|
||
{
|
||
"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 Honcho
|
||
- `agentMemoryMode=local`: skip adding assistant peer messages to Honcho
|
||
- Both local: skip `session.addMessages()` entirely
|
||
- `userMemoryMode=honcho`: disable local USER.md writes
|
||
- `agentMemoryMode=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
|
||
|
||
```typescript
|
||
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()
|
||
|
||
```typescript
|
||
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:
|
||
|
||
```typescript
|
||
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
|
||
|
||
```json
|
||
{
|
||
"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 of `before_prompt_build` into post-`agent_end` background Promise
|
||
- [ ] **observe_me=True for agent peer** — one-line change in `session.addPeers()`
|
||
- [ ] **Dynamic reasoning level** — add helper; apply in `honcho_recall` and `honcho_analyze`; add `dialecticReasoningLevel` to config
|
||
- [ ] **Per-peer memory modes** — add `userMemoryMode` / `agentMemoryMode` to config; gate Honcho sync and local writes
|
||
- [ ] **seedAiIdentity()** — add helper; use during setup migration for SOUL.md / IDENTITY.md
|
||
- [ ] **Session naming strategies** — add `sessionStrategy`, `sessions` map, `sessionPeerPrefix`
|
||
- [ ] **CLI surface injection** — append command reference to `before_prompt_build` return value
|
||
- [ ] **honcho identity subcommand** — seed from file or `--show` current representation
|
||
- [ ] **AI peer name injection** — if `aiPeer` name 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 `lastSavedIndex` dedup
|
||
- [ ] 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
|