{"owner":"plastic-labs","repo":"honcho","hasSkills":true,"totalSkillsCount":10,"totalTokensCount":20520,"categories":["anthropic-skill","root-instruction","claude-rule"],"hasMcp":true,"mcpConfig":{"mcpServers":{"honcho":{"command":"npx","args":["-y","@modelcontextprotocol/server-honcho"]}}},"found":["skills/honcho-cli/SKILL.md","examples/zo/SKILL.md","mcp/instructions.md","skills/verify/SKILL.md","skills/honcho-integration/SKILL.md","skills/honcho-memory/SKILL.md","CLAUDE.md","skills/honcho-integration/references/core-patterns.md","skills/honcho-integration/references/agent-patterns.md","skills/honcho-integration/references/bot-frameworks.md"],"skills":{"skills/honcho-cli/SKILL.md":"---\nname: honcho-cli\ndescription: Inspect and debug Honcho workspaces via the `honcho` CLI. Use when investigating peer representations, memory state, session context, or dialectic quality — any task that requires introspection of a Honcho deployment, including verifying that a recall/record memory loop is actually working.\nallowed-tools: Bash(honcho:*), Bash(jq:*), Read, Grep\n---\n\n# Honcho CLI\n\n`honcho` wraps the Honcho Python SDK with agent-friendly defaults: JSON output, structured errors, input validation. Use it to inspect workspace state, debug peer memory, and diagnose the dialectic.\n\n## Output & config\n\n- **TTY**: human-readable tables (default when interactive)\n- **Piped / `--json`**: JSON — collection commands emit arrays, single-resource commands emit objects\n- **Exit codes**: `0` success · `1` client error (bad input, not found) · `2` server error · `3` auth error\n- **Config**: `~/.honcho/config.json` (shared with other Honcho tools). The CLI owns `apiKey` and `environmentUrl` at the top level; run `honcho init` to confirm or set them. Per-command scope (workspace / peer / session) is via `-w` / `-p` / `-s` flags or `HONCHO_*` env vars.\n\n## Command groups\n\n- `honcho config` — CLI configuration\n- `honcho start` / `stop` / `status` — local Docker stack (does not change `environmentUrl`). First start pins the Honcho image digest and writes `config.toml` into the profile. Pass `--setup basic` or `--setup advanced` for an interactive config wizard (TTY only; writes `.env` overrides). `honcho status` lists every profile; pass `--profile` for one.\n- `honcho workspace` — inspect, delete, search, chat\n- `honcho peer` — inspect, card, chat, search\n- `honcho session` — inspect, view (transcript), context, summaries\n- `honcho scope` — list, create, inspect, sessions, add-sessions, remove-session, status\n- `honcho message` — list and get\n- `honcho conclusion` — list, search, create, delete\n\n## Rules\n\n- Always pass `--json` when processing output programmatically.\n- Run `honcho peer inspect` before `honcho peer chat` to understand context.\n- Use `honcho session context` to see exactly what an agent receives.\n- Never run `honcho workspace delete` without `honcho workspace inspect` first.\n- Compare peer card with conclusions to understand memory state.\n- After `honcho scope add-sessions`, check `honcho scope status <name>` before a scoped `chat`: a `pending` backfill means the scope has not caught up yet, not that there is nothing to recall.\n- `honcho start` does not rewrite `environmentUrl`. Use `HONCHO_BASE_URL=http://127.0.0.1:8000` to talk to local stack.\n\n## Inspection tour\n\nWhen orienting to a Honcho deployment, walk outside-in:\n\n### 1. Understand the workspace\n\n```bash\nhoncho workspace inspect --json\n```\n\n### 2. Find the peer\n\n```bash\nhoncho peer list --json\nhoncho peer inspect <peer_id> --json\n```\n\n### 3. Check peer's memory\n\n```bash\nhoncho peer card <peer_id> --json\nhoncho conclusion list --observer <peer_id> --json\nhoncho conclusion search \"topic\" --observer <peer_id> --json\n```\n\n### 4. Debug a session\n\n```bash\nhoncho session inspect <session_id> --json\nhoncho session view <session_id> --last 20 --json\nhoncho session view <session_id> --page 2 --size 50 --json\nhoncho message list <session_id> --last 20 --json\nhoncho session context <session_id> --json\nhoncho session summaries <session_id> --json\n```\n\n### 5. Search across workspace\n\n```bash\nhoncho workspace search \"query\" --json\nhoncho workspace chat \"what themes show up across peers?\" --json\nhoncho workspace chat \"what happened in this project?\" --scope my-project --json\nhoncho peer search <peer_id> \"query\" --json\n```\n\n### 6. Bound recall with a scope\n\n```bash\nhoncho scope list --json\nhoncho scope inspect <name> --json                       # members + backfill summary\nhoncho scope create <name> --sessions <id1>,<id2> --json\nhoncho scope status <name> --json                        # wait until nothing is pending\nhoncho workspace chat \"what happened here?\" --scope <name> --json\n```\n\n## Debugging playbook\n\n### Peer not learning?\n\n```bash\n# Is observation enabled?\nhoncho peer inspect <peer_id> --json | jq '.configuration'\n\n# What conclusions exist?\nhoncho conclusion list --observer <peer_id> --json\nhoncho conclusion search \"expected topic\" --observer <peer_id> --json\n```\n\n### Session context looks wrong?\n\n```bash\n# Raw context an agent would receive\nhoncho session context <session_id> --json\n\n# Summaries feeding the context\nhoncho session summaries <session_id> --json\n\n# Recent message history\nhoncho message list <session_id> --last 50 --json\n```\n\n### Dialectic giving bad answers?\n\n```bash\n# What the peer card says\nhoncho peer card <peer_id> --json\n\n# Conclusions on the specific topic\nhoncho conclusion search \"topic\" --observer <peer_id> --json\n\n# Exercise the dialectic directly\nhoncho peer chat <peer_id> \"what do you know about X?\" --json\nhoncho peer chat <peer_id> \"what happened here?\" --scope my-project --json\nhoncho workspace chat \"what themes show up across peers?\" --json\n```\n","examples/zo/SKILL.md":"---\nname: honcho-memory\ndescription: Gives AI agents persistent memory across conversations using Honcho. Automatically saves and retrieves user context so the AI remembers preferences, history, and facts between sessions. Use when you need the AI to remember past conversations, recall what a user has told it, inject relevant context into prompts, or manage separate memory spaces for different topics.\nlicense: AGPL-3.0\ncompatibility: Requires Python 3.9+, honcho-ai>=2.1.0, and a Honcho API key from honcho.dev. Set HONCHO_API_KEY and optionally HONCHO_WORKSPACE_ID in your environment.\nmetadata:\n  author: plastic-labs\n  version: \"0.1.0\"\n  honcho-sdk: \"2.1.0\"\n---\n\n# Honcho Memory Skill\n\nThis skill provides three tools for storing and retrieving AI memory using [Honcho](https://honcho.dev).\n\n## Setup\n\n1. Get a Honcho API key at [honcho.dev](https://honcho.dev).\n2. Set environment variables:\n\n   ```\n   HONCHO_API_KEY=your-api-key\n   HONCHO_WORKSPACE_ID=default   # optional, defaults to \"default\"\n   ```\n\n3. Install dependencies:\n\n   ```\n   pip install honcho-ai python-dotenv\n   ```\n\n## Tools\n\n### `save_memory`\n\nSaves a conversation turn (user or assistant message) to Honcho.\n\n**When to use:** After every message exchange to build up the user's memory.\n\n```python\nfrom tools.save_memory import save_memory\n\nsave_memory(\n    user_id=\"alice\",           # unique user identifier\n    content=\"I love hiking\",   # message text\n    role=\"user\",               # \"user\" or \"assistant\"\n    session_id=\"chat-1\",       # conversation session ID\n    assistant_id=\"assistant\"   # optional: assistant peer ID (default: \"assistant\")\n)\n```\n\n### `query_memory`\n\nAsks a natural language question against stored memory using Honcho's Dialectic API.\n\n**When to use:** When the user asks \"do you remember...?\", or when you need to recall facts about the user before responding.\n\n```python\nfrom tools.query_memory import query_memory\n\nanswer = query_memory(\n    user_id=\"alice\",\n    query=\"What are Alice's hobbies?\",\n    session_id=\"chat-1\"   # optional: scope to a session\n)\n# Returns: \"Alice enjoys hiking.\"\n```\n\n### `get_context`\n\nRetrieves recent conversation history formatted for direct use in an LLM API call.\n\n**When to use:** At the start of each LLM call to inject relevant context from past conversations.\n\n```python\nfrom tools.get_context import get_context\n\nmessages = get_context(\n    user_id=\"alice\",\n    session_id=\"chat-1\",\n    assistant_id=\"assistant\",\n    tokens=4000              # max tokens to include\n)\n# Returns: [{\"role\": \"user\", \"content\": \"...\"}, ...]\n```\n\n## Concept Mapping\n\n| Zo Computer | Honcho |\n|---|---|\n| Account | Workspace |\n| User | Peer |\n| Conversation | Session |\n| Message | Message |\n\n## Example: Full Conversation Flow\n\n```python\nfrom tools.save_memory import save_memory\nfrom tools.query_memory import query_memory\nfrom tools.get_context import get_context\n\nuser_id = \"alice\"\nsession_id = \"session-1\"\n\n# 1. Save user message\nsave_memory(user_id, \"I'm learning Rust and love rock climbing\", \"user\", session_id)\n\n# 2. Save assistant reply\nsave_memory(user_id, \"That's great! Both require patience.\", \"assistant\", session_id)\n\n# 3. In a later session, recall what you know\nprint(query_memory(user_id, \"What does Alice do in her free time?\"))\n# → \"Alice is learning Rust and enjoys rock climbing.\"\n\n# 4. Get context window for next LLM call\nmessages = get_context(user_id, session_id, \"assistant\", tokens=4000)\n```\n","mcp/instructions.md":"# Honcho MCP Server — Instructions\n\nHoncho keeps a model of the people and agents in a conversation (\"peers\") and answers questions about them. Two modes use this connection, and they need different tools.\n\n- **Recall** — read what Honcho already knows: `chat`, `workspace_chat`, `search`, `get_representation`, `get_peer_context`, `list_conclusions`. Nothing is written. This is the usual case when the user's own application writes to Honcho and this conversation only reads from it.\n- **Memory store** — this conversation is itself what Honcho learns from, because the user asked for it to be remembered or set Honcho up as this assistant's memory. Adds `create_session`, `create_peer`, `add_peers_to_session`, and `add_messages_to_session`.\n\nIf the user hasn't asked for this conversation to be recorded, stay in recall mode.\n\n## Setting up a session\n\nIn memory-store mode, a session holds the conversation and the peers in it:\n\n```\ncreate_session\n  workspace_id: \"<workspace-id>\"\n  session_id: \"<unique-id>\"\n```\n\nThen add peers to the session:\n\n```\ncreate_peer\n  workspace_id: \"<workspace-id>\"\n  peer_id: \"<user-name>\"\n\ncreate_peer\n  workspace_id: \"<workspace-id>\"\n  peer_id: \"Assistant\"\n\nadd_peers_to_session\n  workspace_id: \"<workspace-id>\"\n  session_id: \"<session_id>\"\n  peers:\n    - peer_id: \"<user-name>\"\n      observe_me: true\n      observe_others: true\n    - peer_id: \"Assistant\"\n      observe_me: false\n      observe_others: true\n```\n\nReuse that `session_id` for the rest of the conversation.\n\n## Recording messages\n\n`add_messages_to_session` appends messages to a tracked session:\n\n```\nadd_messages_to_session\n  workspace_id: \"<workspace-id>\"\n  session_id: \"<session_id>\"\n  messages:\n    - peer_id: \"<user-name>\"\n      content: \"<the user's message>\"\n    - peer_id: \"Assistant\"\n      content: \"<your response>\"\n```\n\nRecord both sides of each exchange as it happens. Honcho can only learn from\nmessages it has, and gaps in a session produce a weaker representation than a\ncomplete one.\n\n## Asking what Honcho knows\n\n`chat` answers a question about one peer, grounded in everything Honcho has learned across that peer's conversations:\n\n```\nchat\n  workspace_id: \"<workspace-id>\"\n  peer_id: \"Assistant\"\n  query: \"What communication style does this user prefer?\"\n  target_peer_id: \"<user-name>\"\n  session_id: \"<session_id>\"\n```\n\nIt runs live reasoning and takes a few seconds, so reach for it when the answer would change your response. `workspace_chat` does the same across all peers.\n\nQuestions it answers well:\n\n- \"What does this message reveal about the user's communication preferences?\"\n- \"How formal or casual should I be?\"\n- \"What is the user really asking for beyond their explicit question?\"\n- \"What emotional state might the user be in right now?\"\n\n---\n\n## Best Practices\n\n- **Group messages into coherent context buckets** — give each distinct context its own `session_id` (a chat thread, a project, a channel) and reuse that same `session_id` for every turn within it, rather than minting a new one per turn. Honcho reasons over the messages in a session together, so keeping a context's messages in one bucket produces a coherent representation; scattering them across sessions fragments it.\n- **Use one stable `peer_id` per real person**, reused across every session and channel. A fresh or per-channel ID (`user-web` vs. `user-discord`) builds separate, weaker representations instead of one.\n- **`observe_me: false` skips building a model of a peer** — reserve it for deterministic bots (nothing meaningful to model). For a real AI assistant it's fine to leave observation on.\n- **Reasoning is asynchronous** — don't poll or wait for it to finish before responding. A brand-new or low-volume peer legitimately has little to show yet.\n- **Reach for reads before `chat`** — `get_session_context` / `get_peer_context` / `get_representation` / `search` are near-instant; `chat` runs live reasoning and takes a few seconds. Use `chat` only when you need a reasoned answer.\n\n---\n\n## Key Concepts\n\n### Peers\n\nA **peer** is any participant — human or AI. Each peer has a unique ID within the workspace.\n\n### Sessions\n\nA **session** is a conversation context. Sessions track message history, manage which peers participate, and provide context retrieval for LLMs.\n\n### Conclusions\n\n**Conclusions** are facts and observations that Honcho derives from conversations. They power the representation — Honcho's understanding of a peer.\n\nEvery conclusion carries its own attribution. `level` says how it was reached: `explicit` conclusions are extracted straight from messages, while `deductive`, `inductive` and `contradiction` conclusions are derived while dreaming. `source_ids` names the conclusions a derived one was built from, and is null for explicit ones. `times_derived` counts how many times Honcho independently reached the same conclusion — a rough confidence signal.\n\nThose fields make the reasoning tree walkable in both directions: `get_conclusions` on a conclusion's `source_ids` steps down toward the explicit facts it rests on, and `get_derived_conclusions` steps up to whatever was built on top of it. Walk down before correcting a fact, and up before deleting one.\n\n### Evidence\n\n`chat` and `workspace_chat` accept `include_evidence`. With it, the answer arrives alongside the conclusions and messages the agent read and the tools it called. This is collated from what the agent actually accessed rather than reported by the model, so it over-reports — a listed conclusion was read, which is not proof the answer leaned on it. It costs no extra model tokens, so reach for it whenever an answer needs auditing.\n\n### Representations\n\nA **representation** is a formatted text summary built from a peer's conclusions. Query it with `get_representation` or `chat`.\n\n### Peer Cards\n\nA **peer card** is a compact list of biographical facts about a peer, automatically maintained by Honcho (or manually via `set_peer_card`).\n\n### Reasoning Level\n\nSeveral tools accept an optional `reasoning_level` parameter (`minimal`, `low`, `medium`, `high`, `max`). Higher levels produce more thorough answers but take longer and cost more. Default is `low`. Use `minimal` for the fastest lookups; use `high` or `max` when depth matters.\n\n### Dreams\n\nA **dream** is a background memory-consolidation process. It reviews conclusions, merges redundancies, and generates higher-level insights. Schedule one with `schedule_dream` after long conversations.\n","skills/verify/SKILL.md":"---\nname: verify\ndescription: Build, launch, and drive a local Honcho stack to verify a change at its runtime surface (the /v3 HTTP API and the deriver queue). Use when verifying a diff or confirming a change works in the running app.\n---\n\n# Verifying changes in Honcho\n\n## Prerequisites\n\nDocker Compose is the preferred way to run the stack — `docker-compose.yml` at\nthe repo root brings up Postgres (pgvector), Redis, the API server, and the\nderiver worker together. If Docker Compose isn't available, the stack can also\nrun directly on the host (see Launch below); you'll need Postgres with pgvector\nand Redis reachable, plus a `.env` with connection strings and an LLM provider\nkey for any flow that hits a model (deriver, dialectic, dreamer).\n\nWorking in a worktree? It carries neither `.env` nor `node_modules`. Copy\n`.env` from the main checkout (that's where the provider keys live), and run\n`bun install` in `sdks/typescript` if you'll run the full test suite —\notherwise the pre-push gate fails on a phantom `Cannot find package 'zod'`.\n\n## Launch\n\nFirst check whether the stack is already running via Docker Compose:\n\n```bash\ndocker compose ps          # look for api / deriver / database / redis\ndocker compose up -d       # start it if not\n```\n\nA running stack is not the same as *your branch's code* running — check the\nCREATED column; the images may be weeks old. For verifying a diff, the cheap\npath is to reuse the stack's Postgres/Redis containers but run the branch's\nAPI as a host process on a spare port:\n\n```bash\nuv run uvicorn src.main:app --port 8901   # branch code, stack's DB/Redis\nuv run python -m src.deriver              # if the diff touches the worker\n```\n\nThis avoids both an image rebuild and the port/project-name conflicts a second\ncompose stack in a worktree would cause. Without Docker at all, run the two\nprocesses the same way against host Postgres + Redis (API default port 8000\nvia `uv run fastapi dev src/main.py`).\n\nWhen reading server logs, note that with the main `.env` the telemetry emitter\nspams connection warnings at an unreachable endpoint — `grep -v\ntelemetry.emitter` before looking for the real error.\n\n## Drive it\n\nPrefer driving through the `honcho-cli` skill; fall back to the SDKs\n(`sdks/python`, `sdks/typescript`), then raw REST against `/v3`, in that order\nwhen a method isn't available at the higher level. The `honcho-integration`\nskill covers how to use the SDKs.\n\nWhen a change updates endpoints or makes schema changes, verify across all\nthree surfaces — CLI, SDKs, and REST — since they can drift independently.\n\nFor LLM-path changes, the fastest synchronous surface is dialectic at the\n`minimal` reasoning level: send a couple of messages, then hit\n`/v3/.../peers/{peer_id}/chat` and observe the response.\n\n## Configuration\n\nConfiguration is central to both driving the app and running tests: it's how\nAPI keys reach the server and how a config-related change gets exercised at\nall. Settings come from environment variables or files, with precedence\nenv > `.env` > `config.toml` > defaults. To verify a configuration change,\nset the relevant option through one of these layers, restart the affected\nprocess, and observe the behavioral difference at the surface — the same\nmechanism lets you point provider base URLs, model choices, and timeouts at\ncredentials and proxies you actually have.\n\nTwo concrete levers: deep-nested settings (e.g.\n`[dialectic.levels.minimal.model_config.overrides.provider_params]`) are\nmiserable as env vars — drop a partial `config.toml` in the repo root instead.\nAnd for load-time config behavior, `uv run python -c \"import src.config; ...\"`\nis faster than booting the server.\n\n## Test suites\n\nThree test types matter here. All run in CI, but they're also runnable locally\nwith whatever API keys and configuration you have — Honcho's config surface is\nlarge, so options like per-agent timeouts and provider base URLs can be pointed\nat your own keys/proxies to exercise a change:\n\n```bash\n# Unit tests (pytest; spins up its own infra via fixtures)\nuv run pytest tests/\n\n# Unified tests — step-based end-to-end flows defined in JSON\n# (config hierarchy, multi-turn interactions, LLM-as-judge assertions)\nuv run python -m tests.unified.run\nuv run python -m tests.unified.run --test-dir tests/unified/test_cases\n\n# Live LLM tests — real provider calls, for testing specific backends.\n# Needs provider API keys AND model vars — without LIVE_LLM_*_MODELS the\n# tests silently deselect that provider. See tests/live_llm/README.md.\nexport LLM_ANTHROPIC_API_KEY=...\nexport LIVE_LLM_ANTHROPIC_45_PLUS_MODELS=claude-sonnet-4-5\nuv run pytest tests/live_llm -n 0 --live-llm --no-header -q\n```\n","skills/honcho-integration/SKILL.md":"---\nname: honcho-integration\ndescription: Integrate Honcho memory into existing Python or TypeScript codebases. Use when adding Honcho SDK, setting up peers, configuring sessions, and accessing Honcho's representation.\nallowed-tools: Read, Glob, Grep, Bash(uv:*), Bash(bun:*), Bash(npm:*), Edit, Write, WebFetch, AskUserQuestion\n---\n\n# Honcho Integration Guide\n\n## What is Honcho\n\nHoncho is an open source memory library for building stateful agents. It works with any model, framework, or architecture. You send Honcho the messages from your conversations, and custom reasoning models process them in the background — extracting premises, drawing conclusions, and building rich representations of each participant over time. Your agent can then query those representations on-demand (\"What does this user care about?\", \"How technical is this person?\") and get grounded, reasoned answers.\n\nThe key mental model: **Peers** are any participant — human or AI. Both are represented the same way. `observe_me` is a peer-level flag (`PeerConfig`) controlling whether Honcho forms a representation of *that* peer; typically you want Honcho to model your users (`observe_me=True`) but not anything with deterministic behavior (`observe_me=False`). `observe_others` is a separate per-peer `SessionPeerConfig` setting that controls whether that peer forms representations of the *other* participants in a session. **Sessions** scope conversations between peers. **Messages** are the raw data you feed in — Honcho reasons about them asynchronously and stores the results as the peer's **representation**. No messages means no reasoning means no memory.\n\nYour agent accesses this memory through `peer.chat(query)` (ask a natural language question, get a reasoned answer — a few seconds of live reasoning) or `session.context()` (near-instant read of formatted history + representation). Prefer `context()` for per-turn grounding; use `chat()` when you need a reasoned answer.\n\n## Reference map\n\nFollow the workflow below. Read a reference file only when you reach the step that needs it:\n\n| When you're… | Read |\n| --- | --- |\n| Writing the client/peer/session setup (init, peers, sessions, add messages) | `references/core-patterns.md` |\n| Wiring how the AI reads context (tool call, pre-fetch, `context()`, streaming) | `references/agent-patterns.md` |\n| Integrating into a bot framework (nanobot, openclaw, picoclaw, …) | `references/bot-frameworks.md` + `references/bot-frameworks/<framework>/` |\n\n## Integration Workflow\n\nFollow these phases in order:\n\n### Phase 1: Codebase Exploration\n\nBefore asking the user anything, explore the codebase to understand:\n\n1. **Language & Framework**: Is this Python or TypeScript? What frameworks are used (FastAPI, Express, Next.js, etc.)?\n2. **Existing AI/LLM code**: Search for existing LLM integrations (OpenAI, Anthropic, LangChain, etc.)\n3. **Entity structure**: Identify users, agents, bots, or other entities that interact\n4. **Session/conversation handling**: How does the app currently manage conversations?\n5. **Message flow**: Where are messages sent/received? What's the request/response cycle?\n\nUse Glob and Grep to find:\n\n- `**/*.py` or `**/*.ts` files with \"openai\", \"anthropic\", \"llm\", \"chat\", \"message\"\n- User/session models or types\n- API routes handling chat or conversation endpoints\n\n> **Bot framework detected?** If the codebase is built around an agent loop, tool registry, session manager, and message bus (e.g., nanobot, openclaw, picoclaw), read `references/bot-frameworks.md` for framework-specific integration guidance and check `references/bot-frameworks/<framework>/` for concrete reference implementations.\n\n### Phase 2: Interview (REQUIRED)\n\nAfter exploring the codebase, use the **AskUserQuestion** tool to clarify integration requirements. Ask these questions (adapt based on what you learned in Phase 1):\n\n#### Question Set 1 - Entities & Peers\n\nAsk about which entities should be Honcho peers:\n\n- header: \"Peers\"\n- question: \"Which entities should Honcho track and build representations for?\"\n- options based on what you found (e.g., \"End users only\", \"Users + AI assistant\", \"Users + multiple AI agents\", \"All participants including third-party services\")\n- Include a follow-up if they have multiple AI agents: should any AI peers be observed?\n\n#### Question Set 2 - Integration Pattern\n\nAsk how they want to use Honcho context (see `references/agent-patterns.md` for the implementation of each):\n\n- header: \"Pattern\"\n- question: \"How should your AI access Honcho's user context?\"\n- options:\n  - \"Tool call (Recommended)\" - \"Agent queries Honcho on-demand via function calling\"\n  - \"Pre-fetch\" - \"Fetch user context before each LLM call with predefined queries\"\n  - \"context()\" - \"Include conversation history and representations in prompt\"\n  - \"Multiple patterns\" - \"Combine approaches for different use cases\"\n\n#### Question Set 3 - Session Structure\n\nAsk about conversation structure:\n\n- header: \"Sessions\"\n- question: \"How should conversations map to Honcho sessions?\"\n- options based on their app (e.g., \"One session per chat thread\", \"One session per user\", \"Multiple users per session (group chat)\", \"Custom session logic\")\n\n#### Question Set 4 - Specific Queries (if using pre-fetch pattern)\n\nIf they chose pre-fetch, ask what context matters:\n\n- header: \"Context\"\n- question: \"What user context should be fetched for the AI?\"\n- multiSelect: true\n- options: \"Communication style\", \"Expertise level\", \"Goals/priorities\", \"Preferences\", \"Recent activity summary\", \"Custom queries\"\n\n### Phase 3: Implementation\n\nBased on interview responses, implement the integration:\n\n1. Install the SDK (see [Installation](#installation))\n2. Create Honcho client initialization — `references/core-patterns.md` §1\n3. Set up peer creation for identified entities — `references/core-patterns.md` §2–3\n4. Implement the chosen integration pattern(s) — `references/agent-patterns.md`\n5. Add message storage after exchanges — `references/core-patterns.md` §4\n6. Update any existing conversation handlers\n\n### Phase 4: Verification\n\n- If the Honcho CLI is available, run `honcho doctor` to confirm connectivity before testing the integration code\n- Use `honcho peer list` and `honcho peer chat` to verify peers exist and the dialectic endpoint works independently of the integration\n- Ensure all message exchanges are stored to Honcho\n- Verify deterministic bot peers have `observe_me=False`; AI-assistant peers can keep observation on (it's fine to model them)\n- Check that the workspace ID is consistent across the codebase\n- Confirm environment variable for API key is documented\n\n---\n\n## Before You Start\n\n1. **Check the latest SDK versions** at <https://honcho.dev/docs/changelog/introduction.md>\n   - Python SDK: `honcho-ai`\n   - TypeScript SDK: `@honcho-ai/sdk`\n\n2. **Get an API key** ask the user to get a Honcho API key from <https://app.honcho.dev> and add it to the environment.\n\n3. **Verify with the CLI** (optional but recommended). If the user has the Honcho CLI installed (`uv install honcho-cli`), they can validate their setup before writing any integration code:\n\n   ```bash\n   honcho init          # persist API key + URL to ~/.honcho/config.json\n   honcho doctor        # verify connectivity, config, workspace health\n   honcho peer chat     # test the dialectic endpoint interactively\n   ```\n\n   This is the fastest way to confirm the API key and URL are correct before debugging SDK code.\n\n## Installation\n\n### Python (use uv)\n\n```bash\nuv add honcho-ai\n```\n\n### TypeScript (use bun)\n\n```bash\nbun add @honcho-ai/sdk\n```\n\nThe SDK is sync-by-default in Python (with an `.aio` async namespace) and async-only in TypeScript — match the client to your framework. Full sync/async guidance and the base client/peer/session/message code are in `references/core-patterns.md`.\n\n## Integration Checklist\n\nWhen integrating Honcho into an existing codebase:\n\n- [ ] Install SDK with `uv add honcho-ai` (Python) or `bun add @honcho-ai/sdk` (TypeScript)\n- [ ] Set up `HONCHO_API_KEY` environment variable\n- [ ] Initialize Honcho client with a single workspace ID\n- [ ] Create peers for all entities (users AND AI assistants)\n- [ ] Set `observe_me=False` for deterministic bot peers (optional for AI assistants — fine to leave observation on)\n- [ ] Configure sessions with appropriate peer observation settings\n- [ ] Choose integration pattern:\n  - [ ] Tool call pattern for agentic systems\n  - [ ] Pre-fetch pattern for simpler integrations\n  - [ ] context() for conversation history\n- [ ] Store messages after each exchange to build user models\n- [ ] (Optional) Run `honcho doctor` to verify connectivity before testing integration code\n- [ ] (Optional) Use `honcho peer chat` to test dialectic queries independently\n\n## Common Mistakes to Avoid\n\n1. **Multiple workspaces**: Use ONE workspace per application\n2. **Forgetting AI peers**: Create peers for AI assistants, not just users\n3. **Modeling bots**: Set `observe_me=False` for deterministic bots (scripted output — nothing meaningful to model). For AI assistants it's fine to leave observation on; turning it off is an optional optimization when you only care about the user.\n4. **Not storing messages**: Always call `add_messages()` to feed Honcho's reasoning engine\n5. **Blocking on processing**: Messages are processed asynchronously — don't poll or wait for reasoning to complete before continuing\n\n## Resources\n\n- Documentation (LLM-friendly index): <https://honcho.dev/docs/llms.txt>\n- Latest SDK versions: <https://honcho.dev/docs/changelog/introduction.md>\n- API Reference: <https://honcho.dev/docs/v3/api-reference/introduction.md>\n\n> Tip: append `.md` to any Honcho docs URL to fetch the raw Markdown version.\n","skills/honcho-memory/SKILL.md":"---\nname: honcho-memory\ndescription: Concepts and strategy for using a connected Honcho as persistent memory of the user — the recall/record loop and session and peer design. Start here to understand how Honcho memory works, then connect — via a first-class integration for your environment if one exists (preferred), or raw MCP tools (covered here) or the honcho-cli skill (CLI). For embedding the SDK into a codebase, use honcho-integration.\n---\n\n# Using Honcho as Memory\n\nHoncho is a memory layer for agents. You feed it the messages from your conversations; in the background it reasons over them and builds a **representation** of each participant. At any point you can ask it natural-language questions about the user (\"How technical are they?\", \"What are they trying to do?\") and get grounded, reasoned answers.\n\nThis skill is for when Honcho is **already connected** to you and you want to use it. If you're instead adding Honcho to a codebase from scratch, use the `honcho-integration` skill.\n\n> **What's durable vs. what to look up.** The concepts and the recall/record loop below change rarely — rely on them. Specifics that change often — the exact set of integrations, tool names, install commands, headers, and defaults — are illustrative here; treat the linked docs (and your own live tool list) as authoritative and fetch them when the details matter.\n\n## The mental model\n\n- **Peer** — any participant, human or AI. You and the user are both peers. Honcho builds a representation of peers it observes (typically the user, not you).\n- **Session** — one conversation thread; messages live in sessions. Honcho reasons over the messages in a session together, so scope each session to one coherent context (a conversation, channel, task, or project) and keep that context's turns in the same session rather than fragmenting them across many thin ones. For low-volume or trickle inputs, append to one ongoing session rather than spinning up a new one each time. See [design patterns](https://honcho.dev/docs/v3/documentation/core-concepts/design-patterns.md) and [reasoning](https://honcho.dev/docs/v3/documentation/core-concepts/reasoning.md).\n- **Message** — the raw turns you feed in. No messages → no reasoning → no memory.\n- **Conclusion** — a fact Honcho derived (or you stored) about a peer. Conclusions power the representation.\n- **Representation / peer card** — the synthesized understanding of a peer, queryable via `chat`. A peer's representation **accumulates across every session** it appears in — that's the cross-conversation memory. Session-scoped data (recent messages, summaries) stays local to one session.\n\nReasoning happens **asynchronously**. After you record a turn, don't poll or wait — the representation updates in the background and is richer next time you ask.\n\n## The loop: recall → respond → record\n\nDo this every conversation. It's the whole skill.\n\n1. **Once per conversation** — make sure there's a session with you and the user as peers (observe the user, don't observe yourself).\n2. **Before responding, when personalization helps** — pull the user's current context (`get_session_context` / `get_representation`) or search past messages (`search`) — these are fast reads. For a reasoned answer to a specific question, ask the dialectic (`chat`) — that one takes a few seconds, so use it when it earns its keep.\n3. **After every exchange** — record both the user's message and your reply. This is what makes Honcho learn. Don't skip it.\n\nOptionally, when you learn a durable fact you don't want to wait for background reasoning to surface, **store a conclusion** directly.\n\n## What you get back when you recall\n\nThree ways to pull memory, cheapest first:\n\n- **Representation** (`get_representation`) — Honcho's synthesized understanding of the user as text, ready to drop straight into a system prompt. Near-instant read.\n- **Context** (`get_session_context`) — the fuller session view: a session summary + recent messages covering the conversation, and — *only if you target a peer* — that peer's representation folded in. Without a peer target it's session-local (recent turns + summary) and carries no cross-conversation memory. Near-instant read.\n- **Dialectic** (`chat`) — a *reasoned* natural-language answer to a specific question (\"How does this user like to receive feedback?\"). Runs live reasoning, so it takes a few seconds. Use it when a plain read won't answer the question.\n\nThe dialectic (`chat`) also takes a **reasoning level** that trades speed for depth — from `minimal` (fast factual lookup) through `low` (the default balance) to `max` (deep synthesis for the hardest questions). Pick the lowest level that answers the question; higher levels are slower and cost more. The full level-by-level table and model routing are in the [chat docs](https://honcho.dev/docs/v3/documentation/features/chat.md).\n\n---\n\n## Pick your access path\n\nThe loop is the same; the mechanics depend on how you reach Honcho. **Prefer a purpose-built integration over wiring up raw MCP yourself** — they handle sessions, peers, and the record loop for you, stay current, and are tuned per environment.\n\n1. **A first-class integration exists for your environment? Use it.** In Claude Code, install the [Claude Code plugin](https://honcho.dev/docs/v3/guides/integrations/claude-code.md) (`/plugin marketplace add plastic-labs/claude-honcho`) for persistent memory out of the box; there are also plugins/integrations for [OpenCode](https://honcho.dev/docs/v3/guides/integrations/opencode.md), LangGraph, CrewAI, Discord, and more. Browse the always-current list: <https://honcho.dev/docs/v3/guides/overview.md>.\n2. **No integration, but you have MCP tools** (`create_session`, `add_messages_to_session`, `chat`, …) → drive them with the loop above. The MCP server injects its own usage guide on connect, so there's nothing extra to load; to connect a client yourself, see [Setup](#setup-if-not-connected-yet) below. This is the fallback for connected agents.\n3. **`honcho` CLI available** in a terminal → use the **`honcho-cli`** skill — for the recall/record loop, and for verifying that memory is actually building (did messages land? is the representation growing? why doesn't it remember me?).\n4. **Embedding Honcho into your own codebase** (not just using a connected instance) → use the **`honcho-integration`** skill.\n\nIf you're unsure, list your available tools and look for Honcho memory tools (an MCP connection) before falling back to the CLI. Even on the MCP path, the `honcho-cli` skill is the best way to **verify the loop is working** if memory seems off.\n\n---\n\n## Setup (if not connected yet)\n\nYou need a Honcho API key — get one free at <https://app.honcho.dev> (starts with `hch-`). Then connect via the path you picked above — a purpose-built integration (recommended), or a raw connection:\n\n- **MCP** — point your client at `https://mcp.honcho.dev` with `Authorization: Bearer hch-your-key-here`. Optional `X-Honcho-Workspace-ID` fills the `workspace_id` tool argument when omitted; otherwise pass `workspace_id` on each call (use `list_workspaces` to discover IDs). Restart the client fully after adding config. Per-client config snippets (Claude Desktop, Cursor, Codex, Windsurf, VS Code, Cline, Zed) are in the [MCP integration guide](https://honcho.dev/docs/v3/guides/integrations/mcp.md). Once connected, the server tells your assistant how to use the tools automatically.\n- **CLI** — use the `honcho-cli` skill.\n\n---\n\n## Rules of thumb\n\n- **Always record turns.** Memory only grows from messages you feed in. Recording is the one non-optional step.\n- **Modeling the assistant is optional.** Setting `observe_me: false` on the assistant peer skips building a model of it — required only for deterministic bots (scripted output, nothing meaningful to model). For an AI assistant it's fine to leave observation on if you also want a model of the agent.\n- **One stable peer ID per entity.** Reuse the same `peer_id` for a person across every session and channel; splitting them (`user`, `user-web`, `user-discord`) builds separate representations and fragments memory.\n- **Scope sessions to coherent context buckets.** Honcho reasons over a session's messages together. Scope a session to one active interaction (per-conversation, per-channel, per-task, per-project); create a new one when context genuinely resets (new topic, new day), reuse it while context should keep accumulating. Keeping a context's turns in one session produces a coherent representation; scattering them fragments it.\n- **Don't block on reasoning.** It's asynchronous. Respond now; the representation will be richer next time.\n- **Reads are cheap; reasoning isn't.** Fetching the representation/context (`get_session_context`, `get_representation`, `search`) is a near-instant read — use it freely. The dialectic (`chat`) runs live reasoning and takes a few seconds, so save it for when you genuinely need a reasoned answer, not every turn.\n- **Check before you store.** Background reasoning derives most conclusions automatically. Store a conclusion manually only for a durable fact you want available immediately; `list`/`query` first to avoid duplicates.\n- **One workspace per app/user-context.** Don't scatter the same user's memory across multiple workspaces.\n- **Unify memory across tools with a shared workspace + peer ID.** To give one user continuous memory across several apps or agents (e.g. Claude Code, Cursor, your own app), point them at the same workspace and reuse the same peer ID — that shared ID is what links the representation. See [Unified Memory Setup](https://honcho.dev/docs/v3/guides/recipes/unified-memory-setup.md).\n\n## Resources\n\nThese are the LLM-friendly Markdown versions (append `.md` to any Honcho docs URL to get the raw Markdown; the full machine-readable index is at <https://honcho.dev/docs/llms.txt>).\n\n- Full docs index (for agents): <https://honcho.dev/docs/llms.txt>\n- All integrations & plugins: <https://honcho.dev/docs/v3/guides/overview.md>\n- MCP server & client setup: <https://honcho.dev/docs/v3/guides/integrations/mcp.md>\n- Full MCP usage walkthrough: <https://raw.githubusercontent.com/plastic-labs/honcho/refs/heads/main/mcp/instructions.md>\n- Agent development overview: <https://honcho.dev/docs/v3/documentation/introduction/vibecoding.md>\n- CLI reference: <https://honcho.dev/docs/v3/documentation/reference/cli.md>\n","CLAUDE.md":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n**Before opening a PR or filing an issue**, read [If you're an agent](CONTRIBUTING.md#if-youre-an-agent) in `CONTRIBUTING.md`. Every PR needs a linked issue labelled `maintainer-approved`, and issues follow [Filing an issue](CONTRIBUTING.md#filing-an-issue). Run the `pre-pr` skill (`skills/pre-pr/`) before opening a PR.\n\n# Honcho Overview\n\n## What is Honcho?\n\nHoncho is an infrastructure layer for building AI agents with memory and social cognition. Its primary purposes include:\n\n- Imbuing agents with a sense of identity\n- Personalizing user experiences through understanding user psychology\n- Providing a Chat Endpoint (the Dialectic agent) that injects personal context just-in-time\n- Supporting development of LLM-powered applications that adapt to end users\n- Enabling multi-peer sessions where multiple participants (users or agents) can interact\n\nHoncho leverages the inherent reasoning capabilities of LLMs to build coherent models of user psychology over time, enabling more personalized and effective AI interactions.\n\n## Core Concepts\n\n### Peer Paradigm\n\nHoncho uses a peer-based model where both users and agents are represented as \"peers\". This unified approach enables:\n\n- Multi-participant sessions with mixed human and AI agents\n- Configurable observation settings (which peers observe which others)\n- Flexible identity management for all participants\n\n### Key Primitives\n\n- **Workspace** (formerly App): The root organizational unit containing all resources\n- **Peer** (formerly User): Any participant in the system (human or AI)\n- **Session**: A conversation context that can involve multiple peers\n- **Message**: Data units that can represent communication between peers OR arbitrary data ingested by a peer to enhance its global representation\n- **Collections & Documents**: Internal vector storage for peer representations. Collections are keyed by `(observer, observed)` peer pairs. Collections/Documents are not directly exposed via API, but the observations stored within them are exposed as **Conclusions** (see `/v3/.../conclusions` endpoints).\n\n## Architecture Overview\n\n### API Structure\n\nAll API routes follow the pattern: `/v3/{resource}/{id}/{action}`. Most \"list/search\" endpoints are `POST` so they can accept rich filter bodies.\n\n- **Workspaces**: Create, list, update, search\n- **Peers**: Create, list, update, chat (dialectic), messages, representation\n- **Sessions**: Create, list, update, delete, clone, manage peers, get context\n- **Messages**: Create (batch up to 100), upload (file), list, get, update\n- **Conclusions**: Create, list, query (semantic search), delete — the API-facing name for observations stored in `(observer, observed)` collections\n- **Keys**: Create scoped JWTs\n- **Webhooks**: Register endpoint, list, delete, test\n\n### Key Features\n\n#### Chat Endpoint (Dialectic agent) (`/peers/{peer_id}/chat`)\n\n- Provides bespoke responses informed by the representation\n- Integrates long-term facts from vector storage\n- Supports streaming responses\n- Configurable LLM providers\n\n#### Message Processing Pipeline\n\n1. Messages created via API (batch or single)\n2. Enqueued for background processing:\n   - `representation`: Update peer's context\n   - `summary`: Create session summaries\n3. Session-based queue processing ensures order\n4. Results stored internally in vector DB\n\n### Configuration\n\n- Hierarchical config: config.toml + environment variables\n- Database settings with connection pooling\n- Multiple LLM provider support\n- Background worker (deriver) settings\n- Authentication can be toggled on/off\n\n## Development Guide\n\n### Commands\n\n- Setup: `uv sync`\n- Run server: `uv run fastapi dev src/main.py`\n- Run tests: `uv run pytest tests/`\n- Run single test: `uv run pytest tests/path/to/test_file.py::test_function`\n- Linting: `uv run ruff check src/`\n- Typechecking: `uv run basedpyright`\n- Format code: `uv run ruff format src/`\n\n### SDK Testing\n\n#### TypeScript SDK\n\nThe TypeScript SDK tests run against a real Honcho server. `bun run test` (not bare `bun test`, which has no server to talk to) starts one for them: `scripts/run_ts_sdk_tests.py` creates a throwaway database on the Postgres named by `DB_CONNECTION_URI`, migrates it, starts the API and the mock LLM provider (`src/mock_provider`), runs `bun test`, and tears it all down. No provider key is needed. They are not part of `uv run pytest`.\n\n```bash\ncd sdks/typescript && bun run test             # whole suite\ncd sdks/typescript && bun run test peer.test   # bun test args pass through\n```\n\n**To type-check the TypeScript SDK (this is fine to run directly):**\n\n```bash\ncd sdks/typescript && bun run tsc --noEmit\n```\n\n### Code Style\n\n- Follow isort conventions with absolute imports preferred\n- Use explicit type hints with SQLAlchemy mapped_column annotations\n- snake_case for variables/functions; PascalCase for classes\n- Line length: 88 chars (Black compatible)\n- Explicit error handling with appropriate exception types\n- Docstrings: Use Google style docstrings\n- **Never hold a DB session during external calls** (LLM, embedding, HTTP). If a function needs both a DB session and an external call result, compute the external result first and pass it as a parameter. This avoids tying up DB connections during slow network I/O. Use `tracked_db` for short-lived, DB-only operations; pass a shared session when multiple DB-only calls can reuse one connection.\n- **Never write through a read-only session** (`tracked_db(..., read_only=True)`, `get_read_db`, `ReadSessionLocal`). These run in AUTOCOMMIT mode with no transaction: writes are NOT blocked by the database — they silently commit immediately, and `begin_nested()` savepoints break. There is no runtime guard; this is enforced by convention only. Use `read_only=True` strictly for SELECT-only windows; anything that mutates (including get-or-create paths) must use a regular write session.\n\n#### Multi-row locking and deadlocks\n\nTables written concurrently by more than one worker — `documents` (deriver, dreamer, scope backfill/removal, reconciler) and `queue` (every deriver replica) — deadlock when two writers touch an overlapping row set in different orders. Rules:\n\n- **A multi-row `SELECT ... FOR UPDATE` MUST carry an explicit `ORDER BY <pk>`.** Without it Postgres locks in scan order, which differs per plan, so two writers with overlapping sets can cycle. `_apply_document_row_updates` in `src/crud/document.py` is the reference implementation.\n- **`WHERE id IN (...)` does NOT impose an order**, so sorting the Python list is a no-op — the list order is discarded and the planner picks `Bitmap Heap Scan` (ctid order), `Index Scan` (id order), or `Seq Scan` per invocation. Deterministic ordering requires either a preceding `SELECT ... ORDER BY id FOR UPDATE` or `WHERE id IN (SELECT id ... ORDER BY id FOR UPDATE)`.\n- **`Document.id` is a random nanoid** (`models.py`), so id order is uncorrelated with physical order — an unordered predicate `UPDATE`/`DELETE` is roughly a coin flip against an id-ordered locker per row pair, not a rare edge case. (`QueueItem.id` is an integer identity, so there id order is also chronological.)\n- **Prefer no lock at all.** A single `UPDATE ... WHERE <predicate>` acquires row locks as it writes and has no separate lock phase to get wrong. Reach for `FOR UPDATE` only when a value must be read, computed in Python, and written back — that read-modify-write is the only reason `_apply_document_row_updates` locks (it replaced a server-side `func.greatest()`), and `populate_existing=True` is required with it so the identity map doesn't serve a stale pre-lock value. Server-side expressions (`func.greatest`, the JSONB `-` operator) avoid the lock entirely; see `_clear_work_unit_retry_attempts` in `src/deriver/queue_manager.py`.\n- `FOR UPDATE SKIP LOCKED` (the reconciler's claim pattern) never waits, so it cannot be a deadlock partner — but holding those locks across an external call still stalls other writers. See the \"never hold a DB session during external calls\" rule above.\n\n#### Auth scoping\n\n- **Named-scope keys (`sc` plus `w`) require explicit `allow_scope=True` on supported routes.** This grants both recall and ingestion within that scope, not peer-member write access. Recall handlers must bind their projection to the token; session routes check active scope membership. Keep the named-scope route allowlist in `tests/routes/test_auth_route_policy.py` synchronized. Never allow restricted keys to enroll unrelated existing sessions or fall back to global recall.\n\n- **`allow_member_read=True` (in `require_auth(...)`) is read-only — NEVER set it on a route that mutates state.** It lets a peer-scoped key reach a session route when its peer is an active member of the session, so on a mutating route it would hand any session member write access (message injection, config mutation, deletion). HTTP method is not a reliable read/write signal here (some read routes use POST for a richer body), so this is enforced by an explicit allowlist in `tests/routes/test_auth_route_policy.py` — adding the flag to a new route fails that test until you consciously add the route to `EXPECTED_MEMBER_READ_ROUTES`, and you must never add a mutating method there.\n- **When a member-read route is keyed by another sub-resource** (e.g. `peers/{peer_id}/config`), the handler must additionally confirm a peer-scoped caller only reads its OWN resource (`jwt_params.p == peer_id`, else raise `AuthenticationException`). Membership grants session access, not access to a co-member's data. See `get_peer_config` in `src/routers/sessions.py`.\n\n### Runtime Architecture\n\nHoncho runs as two cooperating processes that share a Postgres database and Redis cache:\n\n- **API server** (`uv run fastapi dev src/main.py`) — handles HTTP, enqueues background work, returns immediately. Hosts the **Dialectic** agent inline (synchronous tool loop during chat requests).\n- **Deriver worker** (`uv run python -m src.deriver`) — long-running queue consumer (uvloop). Runs the **Deriver**, **Summarizer**, and **Dreamer** off the queue. Can run multiple instances (`DERIVER_WORKERS`). Also hosts an in-process **Reconciler scheduler** (`src/reconciler/`) that periodically embeds messages with `sync_state='pending'` in `MessageEmbedding` and cleans up stale queue items — embedding generation is decoupled from message creation by design.\n\n### Agent Architecture\n\nHoncho uses several specialized LLM agents. They share tool definitions and the LLM client abstraction in `src/utils/agent_tools.py` + `src/llm/`.\n\n> **Terminology:** what users see as **conclusions** (the public API surface and the term we use in documentation) is called **observations** in code symbols — `create_observations`, `delete_observations`, `get_observation_context`, etc. Doc prose below uses \"conclusions\"; references to actual code symbols stay as \"observations.\"\n\n#### 1. Deriver (`src/deriver/`)\n\n**Role**: Memory formation through content ingestion.\n\nThe Deriver processes batches of incoming messages and extracts conclusions about peers. The current architecture is \"minimal deriver\" — a **single LLM call** per batch using structured output, not an agentic tool loop. This trades flexibility for cost and predictability.\n\n- **Trigger**: Messages enqueued by `src/deriver/enqueue.py` on message create; consumed by `src/deriver/queue_manager.py` → `consumer.process_item()` → `deriver.process_representation_tasks_batch()`.\n- **Output**: Explicit conclusions (direct facts) and deductive conclusions (inferences) saved to `(observer, observed)` collections.\n- **Entry point**: `src/deriver/__main__.py` → `queue_manager.main()`.\n- **Prompts**: `src/deriver/prompts.py` (`minimal_deriver_prompt`).\n- **Custom instructions**: per-workspace/peer guidance can be threaded into the prompt via reasoning configuration; `DERIVER__MAX_CUSTOM_INSTRUCTIONS_TOKENS` caps the addition (default 2000) and `DERIVER__MAX_INPUT_TOKENS` defaults to 25000 to make room.\n\n#### 2. Dialectic (`src/dialectic/`)\n\n**Role**: Analysis and recall for answering queries.\n\nThe Dialectic answers questions about peers by strategically gathering context from memory. It is the only tool-using agent on the synchronous request path — it loops over `DIALECTIC_TOOLS` until it has enough context to answer. (The Dreamer specialists also use tools, but run off the queue.)\n\n- **Trigger**: API call to `POST /v3/.../peers/{peer_id}/chat`.\n- **Tools** (see `DIALECTIC_TOOLS` in `src/utils/agent_tools.py`): `search_memory`, `search_messages`, `get_observation_context`, `grep_messages`, `get_messages_by_date_range`, `search_messages_temporal`, `get_reasoning_chain`. At the `minimal` reasoning level, a reduced set (`DIALECTIC_TOOLS_MINIMAL`) is used: just `search_memory` + `search_messages`.\n- **Reasoning levels**: 5 tiers — `minimal`, `low`, `medium`, `high`, `max` — each with its own model config (see `DialecticLevelSettings` in `src/config.py`).\n- **Output**: Natural language response grounded in gathered context. Supports SSE streaming.\n- **Entry point**: `src/dialectic/chat.py` → `agentic_chat()` / `agentic_chat_stream()` → `DialecticAgent` (in `src/dialectic/core.py`).\n\n#### 3. Dreamer (`src/dreamer/`)\n\n**Role**: Consolidation and self-improvement of memory.\n\nThe Dreamer is an orchestrated multi-specialist system that runs during scheduled \"dreams\" to consolidate conclusions and build reasoning trees.\n\n- **Trigger**: Scheduled via `DreamScheduler` (`src/dreamer/dream_scheduler.py`) or explicit dream task on the queue.\n- **Strategy**: Surprisal-based prioritization (`src/dreamer/surprisal.py`) selects which conclusions to expand. The orchestrator (`orchestrator.run_dream`) runs two specialist phases:\n  1. **DeductionSpecialist** (`specialists.py`) — produces deductive conclusions from explicit conclusions. Tools: `get_recent_observations`, `search_memory`, `search_messages`, `create_observations_deductive`, `delete_observations`, `update_peer_card`.\n  2. **InductionSpecialist** — produces inductive conclusions from explicit + deductive conclusions. Tools: same discovery set + `create_observations_inductive`, `update_peer_card`.\n- **Reasoning trees** (`src/dreamer/trees/`, migration `f1a2b3c4d5e6_add_reasoning_tree_columns`): each conclusion links to its premises and downstream conclusions, enabling `get_reasoning_chain` traversal at recall time.\n- **Output**: Deductive/inductive conclusions, consolidated redundancies, updated peer cards.\n- **Entry point**: `src/dreamer/orchestrator.py` → `process_dream()` (the package-level export from `src/dreamer/__init__.py`), which wraps `run_dream()`.\n\n#### 4. Summarizer (`src/utils/summarizer.py`)\n\n**Role**: Two-tier session summarization (direct LLM call — no agentic tools).\n\n- **Trigger**: Runs as part of the queue pipeline alongside representation tasks.\n- **Tiers**: short summary every `SUMMARY_MESSAGES_PER_SHORT_SUMMARY` messages (default 20); long summary every `SUMMARY_MESSAGES_PER_LONG_SUMMARY` (default 60). Token caps configurable via `SUMMARY_MAX_TOKENS_SHORT` / `SUMMARY_MAX_TOKENS_LONG`.\n\n#### Shared Agent Infrastructure\n\n- **Tool definitions** (`src/utils/agent_tools.py`): unified `TOOLS` dict; per-agent lists (`DIALECTIC_TOOLS`, `DIALECTIC_TOOLS_MINIMAL`, `DREAMER_TOOLS`, `DEDUCTION_SPECIALIST_TOOLS`, `INDUCTION_SPECIALIST_TOOLS`).\n- **LLM subsystem** (`src/llm/`): provider-agnostic `honcho_llm_call()`. Backends in `src/llm/backends/` (`anthropic.py`, `gemini.py`, `openai.py`). Includes prompt caching (`caching.py`), structured output (`structured_output.py`), tool loop (`tool_loop.py`), history adapters for cross-provider message formats, and a model registry. Per-retry provider selection is pinned via an `AttemptPlan` so stream-final retries don't bounce back to primary after the tool loop has settled on fallback.\n- **Per-agent model config**: each agent has its own `MODEL_CONFIG` in `src/config.py` with fallback chains (see `ConfiguredModelSettings`, `FallbackModelSettings`).\n- **Telemetry**: cloudevents in `src/telemetry/events/` cover API routes, dialectic, dream, deletion, reconciliation, representation, and per-call LLM accounting (`llm.py` — `LLMCallCompletedEvent` fires once per provider hit with full cost-attribution context). High-volume events are sampled deterministically per `run_id` via `TelemetrySettings.HIGH_VOLUME_SAMPLE_RATE`.\n- **Prometheus metrics** (`src/telemetry/prometheus/`): every metric carries a `namespace` label and every recorder is fail-soft (a metrics error never propagates into a request or a worker loop). Counter children with a *bounded* label domain are zero-initialized per process at startup — `initialize_bounded_metrics(instance_type=...)`, called from the `src/main.py` lifespan (`api`) and `src/deriver/__main__.py` (`deriver`) — so an absent series means a broken scrape rather than \"nothing happened\". Two consequences worth knowing before touching telemetry:\n  - **Adding a `BaseEvent` subclass requires adding its `_event_type` to `ALL_EVENT_TYPES`** in `src/telemetry/events/__init__.py` (and to `HIGH_VOLUME_EVENT_TYPES` if `_volume_class == \"high_volume\"`). Enforced by the drift guards in `tests/telemetry/test_metric_zero_init.py`, which assert set-equality against the discovered subclasses.\n  - **A service-wide, non-additive gauge must be refreshed by every replica on its own timer**, and aggregated with `max()`/`avg()`, never `sum()`. `message_embeddings_pending` is the example: it reports a DB-global count, so it is driven from `ReconcilerScheduler._scheduler_loop` (runs on all replicas) rather than from the work-unit-deduped reconciliation cycle — otherwise, combined with the zero-init, every replica that never won the work unit would export a confident permanent `0`.\n\n### Project Structure\n\n```\nsrc/\n├── main.py              # FastAPI app: middleware, routers, lifespan, exception handlers\n├── models.py            # SQLAlchemy ORM models (Workspace/Peer/Session/Message/\n│                        #   MessageEmbedding/Collection/Document/QueueItem/...)\n├── config.py            # Pydantic-settings configuration (very large; see README)\n├── db.py                # Engine + session/context management (request_context var)\n├── dependencies.py      # FastAPI DI (tracked_db, etc.)\n├── exceptions.py        # Custom exception types (HonchoException + subclasses)\n├── security.py          # JWT authentication\n├── embedding_client.py  # Embedding provider client (configurable dimensions\n│                        #   via EMBEDDING_MODEL_CONFIG__DIMENSIONS_MODE)\n├── schemas/             # Pydantic schemas\n│   ├── api.py            # Public API request/response schemas\n│   ├── configuration.py  # Per-resource configuration schemas\n│   └── internal.py       # Internal-only schemas (queue payloads, etc.)\n├── crud/                # Per-resource DB operations\n│   ├── collection.py, deriver.py, document.py, message.py\n│   ├── peer.py, peer_card.py, representation.py  (RepresentationManager)\n│   ├── session.py, webhook.py, workspace.py\n├── routers/             # FastAPI route handlers (all under /v3)\n│   ├── workspaces.py, peers.py (dialectic /chat lives here), sessions.py\n│   ├── messages.py, conclusions.py, keys.py, webhooks.py\n├── dialectic/           # Dialectic agent — runs inline per chat request\n│   ├── chat.py           # agentic_chat() / agentic_chat_stream()\n│   ├── core.py           # DialecticAgent (the tool-loop driver)\n│   └── prompts.py\n├── deriver/             # Background queue consumer (separate process)\n│   ├── __main__.py       # `python -m src.deriver` entry point\n│   ├── queue_manager.py  # QueueManager + main() loop\n│   ├── consumer.py       # process_item dispatcher (representation / deletion / reconciler)\n│   ├── deriver.py        # \"minimal deriver\" — single-LLM-call batch processor\n│   ├── enqueue.py        # API → queue producer\n│   └── prompts.py\n├── dreamer/             # Memory consolidation (runs off the queue)\n│   ├── orchestrator.py   # run_dream() / process_dream()\n│   ├── specialists.py    # DeductionSpecialist + InductionSpecialist\n│   ├── dream_scheduler.py\n│   ├── surprisal.py      # Surprisal-based conclusion prioritization\n│   └── trees/            # Reasoning-tree primitives\n├── reconciler/          # In-process scheduler hosted by the deriver worker\n│   ├── scheduler.py      # ReconcilerScheduler (started from queue_manager.py)\n│   ├── sync_vectors.py   # Embeds MessageEmbedding rows with sync_state='pending'\n│   └── queue_cleanup.py  # Removes stale queue items\n├── llm/                 # Provider-agnostic LLM client subsystem\n│   ├── api.py, backend.py, executor.py, runtime.py, registry.py\n│   ├── caching.py, structured_output.py, tool_loop.py, conversation.py\n│   ├── history_adapters.py, request_builder.py, credentials.py, types.py\n│   └── backends/         # anthropic.py, gemini.py, openai.py\n├── cache/               # Redis cache abstraction (cashews-backed)\n│   └── client.py\n├── vector_store/        # Optional external vector stores (pgvector is default,\n│   │                    #   implemented via MessageEmbedding/Document in models+crud)\n│   ├── lancedb.py\n│   └── turbopuffer.py\n├── telemetry/           # Observability\n│   ├── emitter.py        # CloudEvents emitter\n│   ├── logging.py        # Logging helpers + route-template extraction\n│   ├── metrics_collector.py, reasoning_traces.py, sentry.py\n│   ├── events/           # Event type definitions\n│   └── prometheus/       # Prometheus metric definitions\n├── utils/               # Cross-cutting utilities\n│   ├── agent_tools.py    # Tool definitions + per-agent tool lists\n│   ├── summarizer.py     # Two-tier session summarizer\n│   ├── representation.py # Representation formatting (distinct from crud/representation.py)\n│   ├── search.py, filter.py, formatting.py\n│   ├── tokens.py         # tiktoken-based counting\n│   ├── pagination.py     # List pagination: opt-in cursor mode + the cached offset shim\n│   ├── work_unit.py, queue_payload.py\n│   ├── config_helpers.py, json_parser.py, files.py\n│   └── types.py\n└── webhooks/            # Webhook delivery\n    ├── events.py\n    └── webhook_delivery.py\n```\n\n- Tests in pytest with fixtures in tests/conftest.py; subdirs mirror src/ (`tests/deriver/`, `tests/dialectic/`, etc.) plus `tests/bench/` (perf benchmarks), `tests/integration/`, `tests/live_llm/` (gated by `--live-llm`), and `tests/unified/` (the unified runner).\n- Use environment variables via python-dotenv (.env). Config precedence: env > .env > config.toml > defaults.\n\n### Database Design\n\n- All tables use text IDs (nanoid format) as primary keys\n- Composite foreign keys for multi-tenant relationships\n- Feature flags on workspace, peer, and session levels\n- Token counting on messages for usage tracking\n- JSONB metadata fields for extensibility\n- HNSW indexes for vector similarity search\n- **A `Select` that feeds a list endpoint MUST have a total ORDER BY ending in a unique column** (e.g. `created_at, id`). `paginate_offset_or_cursor` (`src/utils/pagination.py`) seeks by the sort key in both cursor mode and the offset shim; without a unique tail, rows that share a sort key are skipped or repeated across pages.\n\n### Key Architectural Decisions\n\n1. **Peer Paradigm**: humans and AI agents are unified as \"Peers\"; many-to-many with Sessions. Internal vector storage (Collections/Documents) is keyed by `(observer, observed)` peer pairs — the same mechanism powers self-representation (`observer == observed`) and cross-peer modeling.\n2. **Multi-Peer Sessions**: Sessions can have multiple participants with different observation settings.\n3. **API server / worker split**: API enqueues, deriver worker process consumes. Never block HTTP on LLM work. The Reconciler runs as an in-process scheduler inside the deriver, handling async embedding sync and queue cleanup.\n4. **\"Minimal\" deriver**: memory formation is a single structured-output LLM call per batch, not an agentic tool loop. Predictable cost, lower latency. The Dialectic is the one true tool-using agent.\n5. **Provider-agnostic LLM layer** (`src/llm/`): all model calls go through `honcho_llm_call()`. Backends (`anthropic`, `gemini`, `openai`) sit behind a registry; per-agent `MODEL_CONFIG` with fallback chains is resolved at call time.\n6. **Dialectic reasoning tiers**: 5 levels (`minimal` → `max`); each level has its own model config and tool set (`minimal` uses a reduced toolset).\n7. **Hybrid search**: Postgres FTS (GIN index on `to_tsvector('english', content)`, OR'd with a `content ILIKE` fallback backed by a `pg_trgm` GIN index; without pg_trgm the index is skipped and the ILIKE scans) + vector similarity (HNSW on `MessageEmbedding.embedding`). `MessageEmbedding` is a separate table from `Message` with its own `sync_state` so embedding is decoupled from message creation.\n8. **Pluggable external vector stores**: defaults to pgvector inline; can swap to turbopuffer or lancedb (`VECTOR_STORE_*` config; `src/vector_store/`).\n9. **Composite-FK multi-tenancy**: `workspace_name` participates in nearly every composite FK. Cross-workspace data leakage is structurally impossible at the schema level.\n10. **Scoped Authentication**: JWTs can be scoped to workspace, peer, or session level.\n11. **Batch Operations**: Bulk message creation up to 100 messages per request.\n12. **Session History**: Two-tier summarization — short every `SUMMARY_MESSAGES_PER_SHORT_SUMMARY` (default 20), long every `SUMMARY_MESSAGES_PER_LONG_SUMMARY` (default 60).\n\n### Error Handling\n\n- Custom exceptions defined in src/exceptions.py\n- Use specific exception types (ResourceNotFoundException, ValidationException, etc.)\n- Proper logging with context instead of print statements\n- Global exception handlers defined in main.py\n- See docs/contributing/error-handling.mdx for details\n\n### Notes\n\n- Always use `uv run` or `uv` to prefix any commands related to python to ensure you use the virtual environment\n","skills/honcho-integration/references/core-patterns.md":"# Core Integration Patterns\n\nThe base SDK boilerplate for any Honcho integration: choosing sync vs async, initializing the client, creating peers, configuring sessions, and adding messages. Read this once you've chosen your entities and session structure (Phases 1–2). For the agent-facing recall patterns (tool call, pre-fetch, `context()`), see `agent-patterns.md`.\n\n## Sync vs Async\n\n**TypeScript** — The SDK is async by default. All methods return promises. No separate sync API.\n\n**Python** — The SDK provides both sync and async interfaces:\n\n- **Sync** (default): `from honcho import Honcho` — use in sync frameworks (Flask, Django, CLI scripts)\n- **Async**: `from honcho import Honcho` with `.aio` namespace — use in async frameworks (FastAPI, Starlette, async workers)\n\n```python\n# Sync usage (Flask, Django, scripts)\nfrom honcho import Honcho\nhoncho = Honcho(workspace_id=\"my-app\", api_key=os.environ[\"HONCHO_API_KEY\"])\npeer = honcho.peer(\"user-123\")\nresponse = peer.chat(\"What does this user prefer?\")\n\n# Async usage (FastAPI, Starlette)\nfrom honcho import Honcho\nhoncho = Honcho(workspace_id=\"my-app\", api_key=os.environ[\"HONCHO_API_KEY\"])\npeer = await honcho.aio.peer(\"user-123\")\nresponse = await peer.aio.chat(\"What does this user prefer?\")\n```\n\nMatch the client to the framework — check whether the codebase uses `async def` handlers or sync `def` handlers and choose accordingly. The examples below show sync Python; swap to `.aio` equivalents for async codebases.\n\n## 1. Initialize with a Single Workspace\n\nUse ONE workspace for your entire application. The workspace name should reflect your app/product.\n\n**Python:**\n\n```python\nfrom honcho import Honcho\nimport os\n\n# Sync client (Flask, Django, scripts)\nhoncho = Honcho(\n    workspace_id=\"your-app-name\",\n    api_key=os.environ[\"HONCHO_API_KEY\"],\n    environment=\"production\"\n)\n\n# Async client (FastAPI, Starlette) — use honcho.aio for all operations\n# honcho.aio.peer(), honcho.aio.session(), etc.\n```\n\n**TypeScript:**\n\n```typescript\nimport { Honcho } from '@honcho-ai/sdk';\n\n// All methods are async by default\nconst honcho = new Honcho({\n    workspaceId: \"your-app-name\",\n    apiKey: process.env.HONCHO_API_KEY,\n    environment: \"production\"\n});\n```\n\n## 2. Create Peers for ALL Entities\n\nCreate peers for **every entity** in your business logic - users AND AI assistants.\n\n**Python:**\n\n```python\nfrom honcho.api_types import PeerConfig\n\n# Human users (observed by default)\nuser = honcho.peer(\"user-123\")\n\n# AI assistants can be observed too — leave observe_me on (the default) if you\n# want a model of the assistant.\nassistant = honcho.peer(\"assistant\")\n\n# Deterministic bots (scripted/rule-based) - set observe_me=False; there's\n# nothing meaningful for Honcho to model.\nnotification_bot = honcho.peer(\"notification-bot\", configuration=PeerConfig(observe_me=False))\n```\n\n**TypeScript:**\n\n```typescript\n// Human users (observed by default)\nconst user = await honcho.peer(\"user-123\");\n\n// AI assistants can be observed too — leave observeMe on (the default) if you\n// want a model of the assistant.\nconst assistant = await honcho.peer(\"assistant\");\n\n// Deterministic bots (scripted/rule-based) - set observeMe=false; there's\n// nothing meaningful for Honcho to model.\nconst notificationBot = await honcho.peer(\"notification-bot\", { configuration: { observeMe: false } });\n```\n\n## 3. Multi-Peer Sessions\n\nSessions can have multiple participants. Configure observation settings per-peer.\n\n**Python:**\n\n```python\nfrom honcho.api_types import SessionPeerConfig\n\nsession = honcho.session(\"conversation-123\")\n\n# User is observed (Honcho builds a model of them)\nuser_config = SessionPeerConfig(observe_me=True, observe_others=True)\n\n# A deterministic bot is NOT observed (no model built of it). An AI assistant\n# could stay observed instead — observe_me defaults to True.\nbot_config = SessionPeerConfig(observe_me=False, observe_others=True)\n\nsession.add_peers([\n    (user, user_config),\n    (notification_bot, bot_config)\n])\n```\n\n**TypeScript:**\n\n```typescript\nconst session = await honcho.session(\"conversation-123\");\n\nawait session.addPeers([\n    // A deterministic bot isn't observed; an AI assistant could stay observed\n    // instead (observeMe defaults to true).\n    [user, { observeMe: true, observeOthers: true }],\n    [notificationBot, { observeMe: false, observeOthers: true }]\n]);\n```\n\n## 4. Add Messages to Sessions\n\n**Python:**\n\n```python\nsession.add_messages([\n    user.message(\"I'm having trouble with my account\"),\n    assistant.message(\"I'd be happy to help. What seems to be the issue?\"),\n    user.message(\"I can't reset my password\")\n])\n```\n\n**TypeScript:**\n\n```typescript\nawait session.addMessages([\n    user.message(\"I'm having trouble with my account\"),\n    assistant.message(\"I'd be happy to help. What seems to be the issue?\"),\n    user.message(\"I can't reset my password\")\n]);\n```\n","skills/honcho-integration/references/agent-patterns.md":"# Agent Access Patterns\n\nHow your AI accesses Honcho's user context. Pick based on the interview answer to Question Set 2. These build on the client/peer/session setup in `core-patterns.md`.\n\n- **Pattern A — Dialectic chat as a tool call**: the agent decides when to query context on-demand.\n- **Pattern B — Pre-fetch with targeted queries**: fetch a fixed set of attributes before each LLM call.\n- **Pattern C — `context()` for LLM integration**: inject conversation history + representation into the prompt.\n\n> **Speed note.** `chat()` runs live dialectic reasoning (a few seconds) — Patterns A and B call it. `context()` (Pattern C) is a near-instant read. Prefer `context()` for per-turn grounding; reach for `chat()` when you genuinely need a reasoned answer.\n\n## Pattern A: Dialectic Chat as a Tool Call\n\nMake Honcho's chat endpoint available as a **tool** for your AI agent. This lets the agent query user context on-demand.\n\n**Python (OpenAI function calling):**\n\n```python\nimport openai\nfrom honcho import Honcho\n\nhoncho = Honcho(workspace_id=\"my-app\", api_key=os.environ[\"HONCHO_API_KEY\"])\n\n# Define the tool for your agent\nhoncho_tool = {\n    \"type\": \"function\",\n    \"function\": {\n        \"name\": \"query_user_context\",\n        \"description\": \"Query Honcho to retrieve relevant context about the user based on their history and preferences. Use this when you need to understand the user's background, preferences, past interactions, or goals.\",\n        \"parameters\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"query\": {\n                    \"type\": \"string\",\n                    \"description\": \"A natural language question about the user, e.g. 'What are this user's main goals?' or 'What communication style does this user prefer?'\"\n                }\n            },\n            \"required\": [\"query\"]\n        }\n    }\n}\n\ndef handle_honcho_tool_call(user_id: str, query: str) -> str:\n    \"\"\"Execute the Honcho chat tool call.\"\"\"\n    peer = honcho.peer(user_id)\n    return peer.chat(query)\n\n# Use in your agent loop\ndef run_agent(user_id: str, user_message: str):\n    messages = [{\"role\": \"user\", \"content\": user_message}]\n\n    response = openai.chat.completions.create(\n        model=\"gpt-4\",\n        messages=messages,\n        tools=[honcho_tool]\n    )\n\n    # Handle tool calls\n    if response.choices[0].message.tool_calls:\n        for tool_call in response.choices[0].message.tool_calls:\n            if tool_call.function.name == \"query_user_context\":\n                import json\n                args = json.loads(tool_call.function.arguments)\n                result = handle_honcho_tool_call(user_id, args[\"query\"])\n                # Continue conversation with tool result...\n```\n\n**TypeScript (OpenAI function calling):**\n\n```typescript\nimport OpenAI from 'openai';\nimport { Honcho } from '@honcho-ai/sdk';\n\nconst honcho = new Honcho({\n    workspaceId: \"my-app\",\n    apiKey: process.env.HONCHO_API_KEY\n});\n\nconst honchoTool: OpenAI.ChatCompletionTool = {\n    type: \"function\",\n    function: {\n        name: \"query_user_context\",\n        description: \"Query Honcho to retrieve relevant context about the user based on their history and preferences.\",\n        parameters: {\n            type: \"object\",\n            properties: {\n                query: {\n                    type: \"string\",\n                    description: \"A natural language question about the user\"\n                }\n            },\n            required: [\"query\"]\n        }\n    }\n};\n\nasync function handleHonchoToolCall(userId: string, query: string): Promise<string> {\n    const peer = await honcho.peer(userId);\n    return await peer.chat(query);\n}\n```\n\n## Pattern B: Pre-fetch Context with Targeted Queries\n\nFor simpler integrations, fetch user context before the LLM call using pre-defined queries.\n\n**Python:**\n\n```python\ndef get_user_context_for_prompt(user_id: str) -> dict:\n    \"\"\"Fetch key user attributes via targeted Honcho queries.\"\"\"\n    peer = honcho.peer(user_id)\n\n    return {\n        \"communication_style\": peer.chat(\"What communication style does this user prefer? Be concise.\"),\n        \"expertise_level\": peer.chat(\"What is this user's technical expertise level? Be concise.\"),\n        \"current_goals\": peer.chat(\"What are this user's current goals or priorities? Be concise.\"),\n        \"preferences\": peer.chat(\"What key preferences should I know about this user? Be concise.\")\n    }\n\ndef build_system_prompt(user_context: dict) -> str:\n    return f\"\"\"You are a helpful assistant. Here's what you know about this user:\n\nCommunication style: {user_context['communication_style']}\nExpertise level: {user_context['expertise_level']}\nCurrent goals: {user_context['current_goals']}\nKey preferences: {user_context['preferences']}\n\nTailor your responses accordingly.\"\"\"\n```\n\n**TypeScript:**\n\n```typescript\nasync function getUserContextForPrompt(userId: string): Promise<Record<string, string>> {\n    const peer = await honcho.peer(userId);\n\n    const [style, expertise, goals, preferences] = await Promise.all([\n        peer.chat(\"What communication style does this user prefer? Be concise.\"),\n        peer.chat(\"What is this user's technical expertise level? Be concise.\"),\n        peer.chat(\"What are this user's current goals or priorities? Be concise.\"),\n        peer.chat(\"What key preferences should I know about this user? Be concise.\")\n    ]);\n\n    return {\n        communicationStyle: style,\n        expertiseLevel: expertise,\n        currentGoals: goals,\n        preferences: preferences\n    };\n}\n```\n\n## Pattern C: Get Context for LLM Integration\n\nUse `context()` for conversation history with built-in LLM formatting. This is a near-instant read so it's the cheapest way to ground each turn.\n\n**Python:**\n\n```python\nimport openai\n\nsession = honcho.session(\"conversation-123\")\nuser = honcho.peer(\"user-123\")\nassistant = honcho.peer(\"assistant\")\n\n# Get context formatted for your LLM\ncontext = session.context(\n    tokens=2000,\n    peer_target=user.id,  # Include representation of this user\n    summary=True           # Include conversation summaries\n)\n\n# Convert to OpenAI format\nmessages = context.to_openai(assistant=assistant)\n\n# Or Anthropic format\n# messages = context.to_anthropic(assistant=assistant)\n\n# Add the new user message\nmessages.append({\"role\": \"user\", \"content\": \"What should I focus on today?\"})\n\nresponse = openai.chat.completions.create(\n    model=\"gpt-4\",\n    messages=messages\n)\n\n# Store the exchange\nsession.add_messages([\n    user.message(\"What should I focus on today?\"),\n    assistant.message(response.choices[0].message.content)\n])\n```\n\n**TypeScript:**\n\n```typescript\nimport OpenAI from 'openai';\n\nconst session = await honcho.session(\"conversation-123\");\nconst user = await honcho.peer(\"user-123\");\nconst assistant = await honcho.peer(\"assistant\");\n\n// Get context formatted for your LLM\nconst context = await session.context({\n    tokens: 2000,\n    peerTarget: user.id,  // Include representation of this user\n    summary: true          // Include conversation summaries\n});\n\n// Convert to OpenAI format\nconst messages = context.toOpenAI(assistant);\n\n// Or Anthropic format\n// const messages = context.toAnthropic(assistant);\n\n// Add the new user message\nmessages.push({ role: \"user\", content: \"What should I focus on today?\" });\n\nconst openai = new OpenAI();\nconst response = await openai.chat.completions.create({\n    model: \"gpt-4\",\n    messages\n});\n\n// Store the exchange\nawait session.addMessages([\n    user.message(\"What should I focus on today?\"),\n    assistant.message(response.choices[0].message.content!)\n]);\n```\n\n### What `context()` returns\n\n`session.context()` bundles the session-local view you can drop straight into an LLM call:\n\n- **Recent messages** from the session, trimmed to the `tokens` budget.\n- **Conversation summaries** when `summary=True` — the two-tier short/long summaries so older turns still count without spending the full token budget.\n- **The target peer's representation** when you pass `peer_target` — Honcho's synthesized understanding of that user, folded in. Omit it and you get session-local context only (no cross-session memory).\n\nThe `to_openai()` / `to_anthropic()` helpers format all of that as a `messages` array for the respective provider. Pass your `assistant` peer so its turns are tagged as the assistant role.\n\n## Streaming Responses\n\n```python\nstream = peer.chat_stream(\"What do we know about this user?\")\n\nfor chunk in stream:\n    print(chunk, end=\"\", flush=True)\n```\n\n```typescript\nconst stream = await peer.chatStream(\"What do we know about this user?\");\n\nfor await (const chunk of stream) {\n    process.stdout.write(chunk);\n}\n```\n","skills/honcho-integration/references/bot-frameworks.md":"# Honcho Integration for Bot Frameworks\n\nThis reference extends the main honcho-integration skill for **bot frameworks** — applications built around an agent loop, session manager, tool registry, and message bus (e.g., nanobot, openclaw, picoclaw).\n\n## Supported Frameworks\n\nWhen a known framework is detected, use concrete reference implementations from `{baseDir}/references/bot-frameworks/<framework>/`.\n\n| Framework | Status | Reference Dir |\n|-----------|--------|---------------|\n| [nanobot](https://github.com/HKUDS/nanobot) | concrete references | `bot-frameworks/nanobot/` |\n| openclaw | planned | -- |\n| picoclaw | planned | -- |\n\nFor unknown frameworks, adapt the general pattern below to the codebase's architecture.\n\n## Phase 1: Explore (bot-specific)\n\nIn addition to the main skill's Phase 1, identify these bot-specific components:\n\n1. **Agent loop**: Where messages are processed (look for `while` loops calling an LLM)\n2. **Session manager**: How conversation history is stored (JSONL files, database, in-memory)\n3. **Tool registry**: How tools/functions are registered for the LLM to call\n4. **Message bus**: How inbound/outbound messages are routed between channels and the agent\n5. **Config system**: How the bot loads configuration (JSON, YAML, env vars, pydantic models, zod schemas)\n6. **CLI entry points**: How the bot is started (commands, gateway, agent modes)\n\nIf the framework matches a known one (e.g., nanobot), pull the concrete references from `{baseDir}/references/bot-frameworks/<framework>/` and use them as the implementation target.\n\n## Phase 2: Interview (bot-specific)\n\nIn addition to the main skill's interview questions, ask about:\n\n- **Peer model**: Who are the participants? (typically: one user peer per channel:chat_id, one shared assistant peer)\n- **Session granularity**: One session per chat? Per user? Per channel?\n- **Workspace ID**: What namespace for this bot's Honcho data?\n- **Feature flag**: Should Honcho be opt-in (default `false`) or opt-out (default `true`)?\n\n## Phase 3: Implement (bot-specific)\n\n### Step 1: Add dependency\n\n**Python:** Add `honcho-ai>=2.0.1`. If the framework supports optional dependencies, make it optional:\n\n```toml\n[project.optional-dependencies]\nhoncho = [\"honcho-ai>=2.0.1\"]\n```\n\n**TypeScript:** Add `@honcho-ai/sdk`:\n\n```bash\nbun add @honcho-ai/sdk\n# or npm install @honcho-ai/sdk\n```\n\nIf the framework supports optional peer dependencies:\n\n```json\n{\n  \"peerDependencies\": {\n    \"@honcho-ai/sdk\": \">=2.0.1\"\n  },\n  \"peerDependenciesMeta\": {\n    \"@honcho-ai/sdk\": { \"optional\": true }\n  }\n}\n```\n\n### Step 2: Add config schema\n\nAdd a Honcho config section to the bot's configuration system:\n\n**Python:**\n\n```python\nclass HonchoConfig(BaseModel):\n    \"\"\"Honcho AI-native memory integration (optional feature flag).\"\"\"\n    enabled: bool = False  # or True for Honcho-first deployments\n    workspace_id: str = \"default\"\n    prefetch: bool = True  # inject user context into system prompts\n    context_tokens: int | None = None\n    environment: str = \"production\"\n```\n\n**TypeScript:**\n\n```typescript\ninterface HonchoConfig {\n  /** Honcho AI-native memory integration (optional feature flag). */\n  enabled: boolean;        // default: false, or true for Honcho-first deployments\n  workspaceId: string;     // default: \"default\"\n  prefetch: boolean;       // default: true — inject user context into system prompts\n  contextTokens?: number;\n  environment: string;     // default: \"production\"\n}\n\nconst defaultHonchoConfig: HonchoConfig = {\n  enabled: false,\n  workspaceId: \"default\",\n  prefetch: true,\n  environment: \"production\",\n};\n```\n\n### Step 3: Create the honcho package\n\nCreate a honcho integration package with:\n\n- **Client singleton** (`client.py` / `client.ts`): Lazy initialization, deferred imports, `getHonchoClient()` factory\n- **Session manager** (`session.py` / `session.ts`): Maps bot sessions to Honcho sessions with peer configuration\n- **Agent tool** (`honcho_tool.py` / `honchoTool.ts`): Tool the agent can call to query user context via `peer.chat()`\n\nKey patterns (Python):\n\n- `from __future__ import annotations` + `TYPE_CHECKING` for all honcho imports\n- Runtime imports inside functions (never top-level) so the bot doesn't crash without `honcho-ai`\n- Wrap in `try/except ImportError` for graceful degradation\n\nKey patterns (TypeScript):\n\n- Use dynamic `import()` for honcho SDK (never top-level `import ... from`) so the bot doesn't crash without `@honcho-ai/sdk`\n- Use `import type { ... }` for type-only imports that are erased at runtime\n- Wrap in `try/catch` for graceful degradation when the SDK is missing\n\nKey patterns (shared):\n\n- IDs sanitized to `^[a-zA-Z0-9_-]+` (Honcho requirement)\n- User peer: `observe_me=True, observe_others=True`\n- Assistant/bot peer: `observe_others=True`; set `observe_me=False` only for deterministic bots (scripted output — nothing to model). AI-assistant bots can keep `observe_me=True`.\n\nIf references exist for this framework, use them directly from `{baseDir}/references/bot-frameworks/<framework>/`.\n\n### Step 4: Wire into the agent loop\n\nAdd these integration points to the agent loop:\n\n1. **Tool registration** (at startup): If `honcho.enabled` and `HONCHO_API_KEY` set, initialize client + register Honcho tools.\n\n   **Python:** Wrap in `try/except ImportError` for graceful degradation.\n   **TypeScript:** Use dynamic `import()` inside a `try/catch` block.\n\n2. **Context setup** (per message): Set session context on Honcho tools, ensure Honcho session exists.\n\n3. **Prefetch** (per message): Call `session.context()` to get user representation and inject into system prompt before the LLM call.\n\n4. **Sync** (after response): After saving to local session, sync the user+assistant message pair to Honcho.\n\n   **Python:**\n\n   ```python\n   session.add_messages([\n       user_peer.message(user_input),\n       assistant_peer.message(assistant_response),\n   ])\n   ```\n\n   **TypeScript:**\n\n   ```typescript\n   await session.addMessages([\n       userPeer.message(userInput),\n       assistantPeer.message(assistantResponse),\n   ]);\n   ```\n\n5. **Migration** (on first activation): If Honcho session is empty but local session has history, upload prior messages as a file via `session.upload_file()` (Python) or `session.uploadFile()` (TypeScript). Also upload `MEMORY.md` and `HISTORY.md` if they exist (from frameworks with local memory consolidation). Archive originals after successful upload.\n\n### Step 5: Pass config through CLI\n\nPass `honcho_config` to every agent loop instantiation in the CLI commands.\n\n### Step 6: Migration support\n\nWhen Honcho activates on an instance with existing local data, migrate automatically:\n\n- **Session messages** (JSONL files): Format as XML transcript, upload via `session.upload_file()` (Python) or `session.uploadFile()` (TypeScript)\n- **Consolidated memory** (MEMORY.md, HISTORY.md): Upload as tagged files with context annotations\n- **Archive originals**: Move to `migrated/` subdirectory after successful upload\n- **Idempotent**: Skip if Honcho session already has messages\n\n## Phase 4: Verify (bot-specific)\n\nAfter integration, verify:\n\n- [ ] Bot starts normally without the Honcho SDK installed (no import errors)\n- [ ] Bot starts normally with the SDK but without `HONCHO_API_KEY` (graceful skip)\n- [ ] With both present and `enabled=true`, logs show \"Honcho tools registered\"\n- [ ] User context is prefetched and visible in system prompts\n- [ ] Messages sync to Honcho after each exchange\n- [ ] Local session migration works on first Honcho activation\n- [ ] Memory file migration works for MEMORY.md/HISTORY.md (if applicable)\n\n## Bot-Specific Patterns\n\n- **Lazy imports everywhere**:\n  - Python: `from __future__ import annotations` + `TYPE_CHECKING` for type hints, runtime imports inside functions\n  - TypeScript: `import type { ... }` for type-only imports, dynamic `import()` for runtime access\n- **Feature flag gating**: Always check `config.enabled` AND `HONCHO_API_KEY` / `process.env.HONCHO_API_KEY` before touching Honcho\n- **Graceful degradation**:\n  - Python: `try/except ImportError` and generic `Exception` catches with logger warnings, never crash the bot\n  - TypeScript: `try/catch` around dynamic `import()` with logger warnings, never crash the bot\n- **Sanitize IDs**: Honcho requires `^[a-zA-Z0-9_-]+` — replace colons, dots, spaces with dashes\n- **Sync after success**: Only mark messages as synced after the API call succeeds, not before\n- **Cache consistency**: When creating aliased sessions, store under both original and derived keys\n"},"files":{"skills/honcho-cli/SKILL.md":"---\nname: honcho-cli\ndescription: Inspect and debug Honcho workspaces via the `honcho` CLI. Use when investigating peer representations, memory state, session context, or dialectic quality — any task that requires introspection of a Honcho deployment, including verifying that a recall/record memory loop is actually working.\nallowed-tools: Bash(honcho:*), Bash(jq:*), Read, Grep\n---\n\n# Honcho CLI\n\n`honcho` wraps the Honcho Python SDK with agent-friendly defaults: JSON output, structured errors, input validation. Use it to inspect workspace state, debug peer memory, and diagnose the dialectic.\n\n## Output & config\n\n- **TTY**: human-readable tables (default when interactive)\n- **Piped / `--json`**: JSON — collection commands emit arrays, single-resource commands emit objects\n- **Exit codes**: `0` success · `1` client error (bad input, not found) · `2` server error · `3` auth error\n- **Config**: `~/.honcho/config.json` (shared with other Honcho tools). The CLI owns `apiKey` and `environmentUrl` at the top level; run `honcho init` to confirm or set them. Per-command scope (workspace / peer / session) is via `-w` / `-p` / `-s` flags or `HONCHO_*` env vars.\n\n## Command groups\n\n- `honcho config` — CLI configuration\n- `honcho start` / `stop` / `status` — local Docker stack (does not change `environmentUrl`). First start pins the Honcho image digest and writes `config.toml` into the profile. Pass `--setup basic` or `--setup advanced` for an interactive config wizard (TTY only; writes `.env` overrides). `honcho status` lists every profile; pass `--profile` for one.\n- `honcho workspace` — inspect, delete, search, chat\n- `honcho peer` — inspect, card, chat, search\n- `honcho session` — inspect, view (transcript), context, summaries\n- `honcho scope` — list, create, inspect, sessions, add-sessions, remove-session, status\n- `honcho message` — list and get\n- `honcho conclusion` — list, search, create, delete\n\n## Rules\n\n- Always pass `--json` when processing output programmatically.\n- Run `honcho peer inspect` before `honcho peer chat` to understand context.\n- Use `honcho session context` to see exactly what an agent receives.\n- Never run `honcho workspace delete` without `honcho workspace inspect` first.\n- Compare peer card with conclusions to understand memory state.\n- After `honcho scope add-sessions`, check `honcho scope status <name>` before a scoped `chat`: a `pending` backfill means the scope has not caught up yet, not that there is nothing to recall.\n- `honcho start` does not rewrite `environmentUrl`. Use `HONCHO_BASE_URL=http://127.0.0.1:8000` to talk to local stack.\n\n## Inspection tour\n\nWhen orienting to a Honcho deployment, walk outside-in:\n\n### 1. Understand the workspace\n\n```bash\nhoncho workspace inspect --json\n```\n\n### 2. Find the peer\n\n```bash\nhoncho peer list --json\nhoncho peer inspect <peer_id> --json\n```\n\n### 3. Check peer's memory\n\n```bash\nhoncho peer card <peer_id> --json\nhoncho conclusion list --observer <peer_id> --json\nhoncho conclusion search \"topic\" --observer <peer_id> --json\n```\n\n### 4. Debug a session\n\n```bash\nhoncho session inspect <session_id> --json\nhoncho session view <session_id> --last 20 --json\nhoncho session view <session_id> --page 2 --size 50 --json\nhoncho message list <session_id> --last 20 --json\nhoncho session context <session_id> --json\nhoncho session summaries <session_id> --json\n```\n\n### 5. Search across workspace\n\n```bash\nhoncho workspace search \"query\" --json\nhoncho workspace chat \"what themes show up across peers?\" --json\nhoncho workspace chat \"what happened in this project?\" --scope my-project --json\nhoncho peer search <peer_id> \"query\" --json\n```\n\n### 6. Bound recall with a scope\n\n```bash\nhoncho scope list --json\nhoncho scope inspect <name> --json                       # members + backfill summary\nhoncho scope create <name> --sessions <id1>,<id2> --json\nhoncho scope status <name> --json                        # wait until nothing is pending\nhoncho workspace chat \"what happened here?\" --scope <name> --json\n```\n\n## Debugging playbook\n\n### Peer not learning?\n\n```bash\n# Is observation enabled?\nhoncho peer inspect <peer_id> --json | jq '.configuration'\n\n# What conclusions exist?\nhoncho conclusion list --observer <peer_id> --json\nhoncho conclusion search \"expected topic\" --observer <peer_id> --json\n```\n\n### Session context looks wrong?\n\n```bash\n# Raw context an agent would receive\nhoncho session context <session_id> --json\n\n# Summaries feeding the context\nhoncho session summaries <session_id> --json\n\n# Recent message history\nhoncho message list <session_id> --last 50 --json\n```\n\n### Dialectic giving bad answers?\n\n```bash\n# What the peer card says\nhoncho peer card <peer_id> --json\n\n# Conclusions on the specific topic\nhoncho conclusion search \"topic\" --observer <peer_id> --json\n\n# Exercise the dialectic directly\nhoncho peer chat <peer_id> \"what do you know about X?\" --json\nhoncho peer chat <peer_id> \"what happened here?\" --scope my-project --json\nhoncho workspace chat \"what themes show up across peers?\" --json\n```\n","examples/zo/SKILL.md":"---\nname: honcho-memory\ndescription: Gives AI agents persistent memory across conversations using Honcho. Automatically saves and retrieves user context so the AI remembers preferences, history, and facts between sessions. Use when you need the AI to remember past conversations, recall what a user has told it, inject relevant context into prompts, or manage separate memory spaces for different topics.\nlicense: AGPL-3.0\ncompatibility: Requires Python 3.9+, honcho-ai>=2.1.0, and a Honcho API key from honcho.dev. Set HONCHO_API_KEY and optionally HONCHO_WORKSPACE_ID in your environment.\nmetadata:\n  author: plastic-labs\n  version: \"0.1.0\"\n  honcho-sdk: \"2.1.0\"\n---\n\n# Honcho Memory Skill\n\nThis skill provides three tools for storing and retrieving AI memory using [Honcho](https://honcho.dev).\n\n## Setup\n\n1. Get a Honcho API key at [honcho.dev](https://honcho.dev).\n2. Set environment variables:\n\n   ```\n   HONCHO_API_KEY=your-api-key\n   HONCHO_WORKSPACE_ID=default   # optional, defaults to \"default\"\n   ```\n\n3. Install dependencies:\n\n   ```\n   pip install honcho-ai python-dotenv\n   ```\n\n## Tools\n\n### `save_memory`\n\nSaves a conversation turn (user or assistant message) to Honcho.\n\n**When to use:** After every message exchange to build up the user's memory.\n\n```python\nfrom tools.save_memory import save_memory\n\nsave_memory(\n    user_id=\"alice\",           # unique user identifier\n    content=\"I love hiking\",   # message text\n    role=\"user\",               # \"user\" or \"assistant\"\n    session_id=\"chat-1\",       # conversation session ID\n    assistant_id=\"assistant\"   # optional: assistant peer ID (default: \"assistant\")\n)\n```\n\n### `query_memory`\n\nAsks a natural language question against stored memory using Honcho's Dialectic API.\n\n**When to use:** When the user asks \"do you remember...?\", or when you need to recall facts about the user before responding.\n\n```python\nfrom tools.query_memory import query_memory\n\nanswer = query_memory(\n    user_id=\"alice\",\n    query=\"What are Alice's hobbies?\",\n    session_id=\"chat-1\"   # optional: scope to a session\n)\n# Returns: \"Alice enjoys hiking.\"\n```\n\n### `get_context`\n\nRetrieves recent conversation history formatted for direct use in an LLM API call.\n\n**When to use:** At the start of each LLM call to inject relevant context from past conversations.\n\n```python\nfrom tools.get_context import get_context\n\nmessages = get_context(\n    user_id=\"alice\",\n    session_id=\"chat-1\",\n    assistant_id=\"assistant\",\n    tokens=4000              # max tokens to include\n)\n# Returns: [{\"role\": \"user\", \"content\": \"...\"}, ...]\n```\n\n## Concept Mapping\n\n| Zo Computer | Honcho |\n|---|---|\n| Account | Workspace |\n| User | Peer |\n| Conversation | Session |\n| Message | Message |\n\n## Example: Full Conversation Flow\n\n```python\nfrom tools.save_memory import save_memory\nfrom tools.query_memory import query_memory\nfrom tools.get_context import get_context\n\nuser_id = \"alice\"\nsession_id = \"session-1\"\n\n# 1. Save user message\nsave_memory(user_id, \"I'm learning Rust and love rock climbing\", \"user\", session_id)\n\n# 2. Save assistant reply\nsave_memory(user_id, \"That's great! Both require patience.\", \"assistant\", session_id)\n\n# 3. In a later session, recall what you know\nprint(query_memory(user_id, \"What does Alice do in her free time?\"))\n# → \"Alice is learning Rust and enjoys rock climbing.\"\n\n# 4. Get context window for next LLM call\nmessages = get_context(user_id, session_id, \"assistant\", tokens=4000)\n```\n","mcp/instructions.md":"# Honcho MCP Server — Instructions\n\nHoncho keeps a model of the people and agents in a conversation (\"peers\") and answers questions about them. Two modes use this connection, and they need different tools.\n\n- **Recall** — read what Honcho already knows: `chat`, `workspace_chat`, `search`, `get_representation`, `get_peer_context`, `list_conclusions`. Nothing is written. This is the usual case when the user's own application writes to Honcho and this conversation only reads from it.\n- **Memory store** — this conversation is itself what Honcho learns from, because the user asked for it to be remembered or set Honcho up as this assistant's memory. Adds `create_session`, `create_peer`, `add_peers_to_session`, and `add_messages_to_session`.\n\nIf the user hasn't asked for this conversation to be recorded, stay in recall mode.\n\n## Setting up a session\n\nIn memory-store mode, a session holds the conversation and the peers in it:\n\n```\ncreate_session\n  workspace_id: \"<workspace-id>\"\n  session_id: \"<unique-id>\"\n```\n\nThen add peers to the session:\n\n```\ncreate_peer\n  workspace_id: \"<workspace-id>\"\n  peer_id: \"<user-name>\"\n\ncreate_peer\n  workspace_id: \"<workspace-id>\"\n  peer_id: \"Assistant\"\n\nadd_peers_to_session\n  workspace_id: \"<workspace-id>\"\n  session_id: \"<session_id>\"\n  peers:\n    - peer_id: \"<user-name>\"\n      observe_me: true\n      observe_others: true\n    - peer_id: \"Assistant\"\n      observe_me: false\n      observe_others: true\n```\n\nReuse that `session_id` for the rest of the conversation.\n\n## Recording messages\n\n`add_messages_to_session` appends messages to a tracked session:\n\n```\nadd_messages_to_session\n  workspace_id: \"<workspace-id>\"\n  session_id: \"<session_id>\"\n  messages:\n    - peer_id: \"<user-name>\"\n      content: \"<the user's message>\"\n    - peer_id: \"Assistant\"\n      content: \"<your response>\"\n```\n\nRecord both sides of each exchange as it happens. Honcho can only learn from\nmessages it has, and gaps in a session produce a weaker representation than a\ncomplete one.\n\n## Asking what Honcho knows\n\n`chat` answers a question about one peer, grounded in everything Honcho has learned across that peer's conversations:\n\n```\nchat\n  workspace_id: \"<workspace-id>\"\n  peer_id: \"Assistant\"\n  query: \"What communication style does this user prefer?\"\n  target_peer_id: \"<user-name>\"\n  session_id: \"<session_id>\"\n```\n\nIt runs live reasoning and takes a few seconds, so reach for it when the answer would change your response. `workspace_chat` does the same across all peers.\n\nQuestions it answers well:\n\n- \"What does this message reveal about the user's communication preferences?\"\n- \"How formal or casual should I be?\"\n- \"What is the user really asking for beyond their explicit question?\"\n- \"What emotional state might the user be in right now?\"\n\n---\n\n## Best Practices\n\n- **Group messages into coherent context buckets** — give each distinct context its own `session_id` (a chat thread, a project, a channel) and reuse that same `session_id` for every turn within it, rather than minting a new one per turn. Honcho reasons over the messages in a session together, so keeping a context's messages in one bucket produces a coherent representation; scattering them across sessions fragments it.\n- **Use one stable `peer_id` per real person**, reused across every session and channel. A fresh or per-channel ID (`user-web` vs. `user-discord`) builds separate, weaker representations instead of one.\n- **`observe_me: false` skips building a model of a peer** — reserve it for deterministic bots (nothing meaningful to model). For a real AI assistant it's fine to leave observation on.\n- **Reasoning is asynchronous** — don't poll or wait for it to finish before responding. A brand-new or low-volume peer legitimately has little to show yet.\n- **Reach for reads before `chat`** — `get_session_context` / `get_peer_context` / `get_representation` / `search` are near-instant; `chat` runs live reasoning and takes a few seconds. Use `chat` only when you need a reasoned answer.\n\n---\n\n## Key Concepts\n\n### Peers\n\nA **peer** is any participant — human or AI. Each peer has a unique ID within the workspace.\n\n### Sessions\n\nA **session** is a conversation context. Sessions track message history, manage which peers participate, and provide context retrieval for LLMs.\n\n### Conclusions\n\n**Conclusions** are facts and observations that Honcho derives from conversations. They power the representation — Honcho's understanding of a peer.\n\nEvery conclusion carries its own attribution. `level` says how it was reached: `explicit` conclusions are extracted straight from messages, while `deductive`, `inductive` and `contradiction` conclusions are derived while dreaming. `source_ids` names the conclusions a derived one was built from, and is null for explicit ones. `times_derived` counts how many times Honcho independently reached the same conclusion — a rough confidence signal.\n\nThose fields make the reasoning tree walkable in both directions: `get_conclusions` on a conclusion's `source_ids` steps down toward the explicit facts it rests on, and `get_derived_conclusions` steps up to whatever was built on top of it. Walk down before correcting a fact, and up before deleting one.\n\n### Evidence\n\n`chat` and `workspace_chat` accept `include_evidence`. With it, the answer arrives alongside the conclusions and messages the agent read and the tools it called. This is collated from what the agent actually accessed rather than reported by the model, so it over-reports — a listed conclusion was read, which is not proof the answer leaned on it. It costs no extra model tokens, so reach for it whenever an answer needs auditing.\n\n### Representations\n\nA **representation** is a formatted text summary built from a peer's conclusions. Query it with `get_representation` or `chat`.\n\n### Peer Cards\n\nA **peer card** is a compact list of biographical facts about a peer, automatically maintained by Honcho (or manually via `set_peer_card`).\n\n### Reasoning Level\n\nSeveral tools accept an optional `reasoning_level` parameter (`minimal`, `low`, `medium`, `high`, `max`). Higher levels produce more thorough answers but take longer and cost more. Default is `low`. Use `minimal` for the fastest lookups; use `high` or `max` when depth matters.\n\n### Dreams\n\nA **dream** is a background memory-consolidation process. It reviews conclusions, merges redundancies, and generates higher-level insights. Schedule one with `schedule_dream` after long conversations.\n","skills/verify/SKILL.md":"---\nname: verify\ndescription: Build, launch, and drive a local Honcho stack to verify a change at its runtime surface (the /v3 HTTP API and the deriver queue). Use when verifying a diff or confirming a change works in the running app.\n---\n\n# Verifying changes in Honcho\n\n## Prerequisites\n\nDocker Compose is the preferred way to run the stack — `docker-compose.yml` at\nthe repo root brings up Postgres (pgvector), Redis, the API server, and the\nderiver worker together. If Docker Compose isn't available, the stack can also\nrun directly on the host (see Launch below); you'll need Postgres with pgvector\nand Redis reachable, plus a `.env` with connection strings and an LLM provider\nkey for any flow that hits a model (deriver, dialectic, dreamer).\n\nWorking in a worktree? It carries neither `.env` nor `node_modules`. Copy\n`.env` from the main checkout (that's where the provider keys live), and run\n`bun install` in `sdks/typescript` if you'll run the full test suite —\notherwise the pre-push gate fails on a phantom `Cannot find package 'zod'`.\n\n## Launch\n\nFirst check whether the stack is already running via Docker Compose:\n\n```bash\ndocker compose ps          # look for api / deriver / database / redis\ndocker compose up -d       # start it if not\n```\n\nA running stack is not the same as *your branch's code* running — check the\nCREATED column; the images may be weeks old. For verifying a diff, the cheap\npath is to reuse the stack's Postgres/Redis containers but run the branch's\nAPI as a host process on a spare port:\n\n```bash\nuv run uvicorn src.main:app --port 8901   # branch code, stack's DB/Redis\nuv run python -m src.deriver              # if the diff touches the worker\n```\n\nThis avoids both an image rebuild and the port/project-name conflicts a second\ncompose stack in a worktree would cause. Without Docker at all, run the two\nprocesses the same way against host Postgres + Redis (API default port 8000\nvia `uv run fastapi dev src/main.py`).\n\nWhen reading server logs, note that with the main `.env` the telemetry emitter\nspams connection warnings at an unreachable endpoint — `grep -v\ntelemetry.emitter` before looking for the real error.\n\n## Drive it\n\nPrefer driving through the `honcho-cli` skill; fall back to the SDKs\n(`sdks/python`, `sdks/typescript`), then raw REST against `/v3`, in that order\nwhen a method isn't available at the higher level. The `honcho-integration`\nskill covers how to use the SDKs.\n\nWhen a change updates endpoints or makes schema changes, verify across all\nthree surfaces — CLI, SDKs, and REST — since they can drift independently.\n\nFor LLM-path changes, the fastest synchronous surface is dialectic at the\n`minimal` reasoning level: send a couple of messages, then hit\n`/v3/.../peers/{peer_id}/chat` and observe the response.\n\n## Configuration\n\nConfiguration is central to both driving the app and running tests: it's how\nAPI keys reach the server and how a config-related change gets exercised at\nall. Settings come from environment variables or files, with precedence\nenv > `.env` > `config.toml` > defaults. To verify a configuration change,\nset the relevant option through one of these layers, restart the affected\nprocess, and observe the behavioral difference at the surface — the same\nmechanism lets you point provider base URLs, model choices, and timeouts at\ncredentials and proxies you actually have.\n\nTwo concrete levers: deep-nested settings (e.g.\n`[dialectic.levels.minimal.model_config.overrides.provider_params]`) are\nmiserable as env vars — drop a partial `config.toml` in the repo root instead.\nAnd for load-time config behavior, `uv run python -c \"import src.config; ...\"`\nis faster than booting the server.\n\n## Test suites\n\nThree test types matter here. All run in CI, but they're also runnable locally\nwith whatever API keys and configuration you have — Honcho's config surface is\nlarge, so options like per-agent timeouts and provider base URLs can be pointed\nat your own keys/proxies to exercise a change:\n\n```bash\n# Unit tests (pytest; spins up its own infra via fixtures)\nuv run pytest tests/\n\n# Unified tests — step-based end-to-end flows defined in JSON\n# (config hierarchy, multi-turn interactions, LLM-as-judge assertions)\nuv run python -m tests.unified.run\nuv run python -m tests.unified.run --test-dir tests/unified/test_cases\n\n# Live LLM tests — real provider calls, for testing specific backends.\n# Needs provider API keys AND model vars — without LIVE_LLM_*_MODELS the\n# tests silently deselect that provider. See tests/live_llm/README.md.\nexport LLM_ANTHROPIC_API_KEY=...\nexport LIVE_LLM_ANTHROPIC_45_PLUS_MODELS=claude-sonnet-4-5\nuv run pytest tests/live_llm -n 0 --live-llm --no-header -q\n```\n","skills/honcho-integration/SKILL.md":"---\nname: honcho-integration\ndescription: Integrate Honcho memory into existing Python or TypeScript codebases. Use when adding Honcho SDK, setting up peers, configuring sessions, and accessing Honcho's representation.\nallowed-tools: Read, Glob, Grep, Bash(uv:*), Bash(bun:*), Bash(npm:*), Edit, Write, WebFetch, AskUserQuestion\n---\n\n# Honcho Integration Guide\n\n## What is Honcho\n\nHoncho is an open source memory library for building stateful agents. It works with any model, framework, or architecture. You send Honcho the messages from your conversations, and custom reasoning models process them in the background — extracting premises, drawing conclusions, and building rich representations of each participant over time. Your agent can then query those representations on-demand (\"What does this user care about?\", \"How technical is this person?\") and get grounded, reasoned answers.\n\nThe key mental model: **Peers** are any participant — human or AI. Both are represented the same way. `observe_me` is a peer-level flag (`PeerConfig`) controlling whether Honcho forms a representation of *that* peer; typically you want Honcho to model your users (`observe_me=True`) but not anything with deterministic behavior (`observe_me=False`). `observe_others` is a separate per-peer `SessionPeerConfig` setting that controls whether that peer forms representations of the *other* participants in a session. **Sessions** scope conversations between peers. **Messages** are the raw data you feed in — Honcho reasons about them asynchronously and stores the results as the peer's **representation**. No messages means no reasoning means no memory.\n\nYour agent accesses this memory through `peer.chat(query)` (ask a natural language question, get a reasoned answer — a few seconds of live reasoning) or `session.context()` (near-instant read of formatted history + representation). Prefer `context()` for per-turn grounding; use `chat()` when you need a reasoned answer.\n\n## Reference map\n\nFollow the workflow below. Read a reference file only when you reach the step that needs it:\n\n| When you're… | Read |\n| --- | --- |\n| Writing the client/peer/session setup (init, peers, sessions, add messages) | `references/core-patterns.md` |\n| Wiring how the AI reads context (tool call, pre-fetch, `context()`, streaming) | `references/agent-patterns.md` |\n| Integrating into a bot framework (nanobot, openclaw, picoclaw, …) | `references/bot-frameworks.md` + `references/bot-frameworks/<framework>/` |\n\n## Integration Workflow\n\nFollow these phases in order:\n\n### Phase 1: Codebase Exploration\n\nBefore asking the user anything, explore the codebase to understand:\n\n1. **Language & Framework**: Is this Python or TypeScript? What frameworks are used (FastAPI, Express, Next.js, etc.)?\n2. **Existing AI/LLM code**: Search for existing LLM integrations (OpenAI, Anthropic, LangChain, etc.)\n3. **Entity structure**: Identify users, agents, bots, or other entities that interact\n4. **Session/conversation handling**: How does the app currently manage conversations?\n5. **Message flow**: Where are messages sent/received? What's the request/response cycle?\n\nUse Glob and Grep to find:\n\n- `**/*.py` or `**/*.ts` files with \"openai\", \"anthropic\", \"llm\", \"chat\", \"message\"\n- User/session models or types\n- API routes handling chat or conversation endpoints\n\n> **Bot framework detected?** If the codebase is built around an agent loop, tool registry, session manager, and message bus (e.g., nanobot, openclaw, picoclaw), read `references/bot-frameworks.md` for framework-specific integration guidance and check `references/bot-frameworks/<framework>/` for concrete reference implementations.\n\n### Phase 2: Interview (REQUIRED)\n\nAfter exploring the codebase, use the **AskUserQuestion** tool to clarify integration requirements. Ask these questions (adapt based on what you learned in Phase 1):\n\n#### Question Set 1 - Entities & Peers\n\nAsk about which entities should be Honcho peers:\n\n- header: \"Peers\"\n- question: \"Which entities should Honcho track and build representations for?\"\n- options based on what you found (e.g., \"End users only\", \"Users + AI assistant\", \"Users + multiple AI agents\", \"All participants including third-party services\")\n- Include a follow-up if they have multiple AI agents: should any AI peers be observed?\n\n#### Question Set 2 - Integration Pattern\n\nAsk how they want to use Honcho context (see `references/agent-patterns.md` for the implementation of each):\n\n- header: \"Pattern\"\n- question: \"How should your AI access Honcho's user context?\"\n- options:\n  - \"Tool call (Recommended)\" - \"Agent queries Honcho on-demand via function calling\"\n  - \"Pre-fetch\" - \"Fetch user context before each LLM call with predefined queries\"\n  - \"context()\" - \"Include conversation history and representations in prompt\"\n  - \"Multiple patterns\" - \"Combine approaches for different use cases\"\n\n#### Question Set 3 - Session Structure\n\nAsk about conversation structure:\n\n- header: \"Sessions\"\n- question: \"How should conversations map to Honcho sessions?\"\n- options based on their app (e.g., \"One session per chat thread\", \"One session per user\", \"Multiple users per session (group chat)\", \"Custom session logic\")\n\n#### Question Set 4 - Specific Queries (if using pre-fetch pattern)\n\nIf they chose pre-fetch, ask what context matters:\n\n- header: \"Context\"\n- question: \"What user context should be fetched for the AI?\"\n- multiSelect: true\n- options: \"Communication style\", \"Expertise level\", \"Goals/priorities\", \"Preferences\", \"Recent activity summary\", \"Custom queries\"\n\n### Phase 3: Implementation\n\nBased on interview responses, implement the integration:\n\n1. Install the SDK (see [Installation](#installation))\n2. Create Honcho client initialization — `references/core-patterns.md` §1\n3. Set up peer creation for identified entities — `references/core-patterns.md` §2–3\n4. Implement the chosen integration pattern(s) — `references/agent-patterns.md`\n5. Add message storage after exchanges — `references/core-patterns.md` §4\n6. Update any existing conversation handlers\n\n### Phase 4: Verification\n\n- If the Honcho CLI is available, run `honcho doctor` to confirm connectivity before testing the integration code\n- Use `honcho peer list` and `honcho peer chat` to verify peers exist and the dialectic endpoint works independently of the integration\n- Ensure all message exchanges are stored to Honcho\n- Verify deterministic bot peers have `observe_me=False`; AI-assistant peers can keep observation on (it's fine to model them)\n- Check that the workspace ID is consistent across the codebase\n- Confirm environment variable for API key is documented\n\n---\n\n## Before You Start\n\n1. **Check the latest SDK versions** at <https://honcho.dev/docs/changelog/introduction.md>\n   - Python SDK: `honcho-ai`\n   - TypeScript SDK: `@honcho-ai/sdk`\n\n2. **Get an API key** ask the user to get a Honcho API key from <https://app.honcho.dev> and add it to the environment.\n\n3. **Verify with the CLI** (optional but recommended). If the user has the Honcho CLI installed (`uv install honcho-cli`), they can validate their setup before writing any integration code:\n\n   ```bash\n   honcho init          # persist API key + URL to ~/.honcho/config.json\n   honcho doctor        # verify connectivity, config, workspace health\n   honcho peer chat     # test the dialectic endpoint interactively\n   ```\n\n   This is the fastest way to confirm the API key and URL are correct before debugging SDK code.\n\n## Installation\n\n### Python (use uv)\n\n```bash\nuv add honcho-ai\n```\n\n### TypeScript (use bun)\n\n```bash\nbun add @honcho-ai/sdk\n```\n\nThe SDK is sync-by-default in Python (with an `.aio` async namespace) and async-only in TypeScript — match the client to your framework. Full sync/async guidance and the base client/peer/session/message code are in `references/core-patterns.md`.\n\n## Integration Checklist\n\nWhen integrating Honcho into an existing codebase:\n\n- [ ] Install SDK with `uv add honcho-ai` (Python) or `bun add @honcho-ai/sdk` (TypeScript)\n- [ ] Set up `HONCHO_API_KEY` environment variable\n- [ ] Initialize Honcho client with a single workspace ID\n- [ ] Create peers for all entities (users AND AI assistants)\n- [ ] Set `observe_me=False` for deterministic bot peers (optional for AI assistants — fine to leave observation on)\n- [ ] Configure sessions with appropriate peer observation settings\n- [ ] Choose integration pattern:\n  - [ ] Tool call pattern for agentic systems\n  - [ ] Pre-fetch pattern for simpler integrations\n  - [ ] context() for conversation history\n- [ ] Store messages after each exchange to build user models\n- [ ] (Optional) Run `honcho doctor` to verify connectivity before testing integration code\n- [ ] (Optional) Use `honcho peer chat` to test dialectic queries independently\n\n## Common Mistakes to Avoid\n\n1. **Multiple workspaces**: Use ONE workspace per application\n2. **Forgetting AI peers**: Create peers for AI assistants, not just users\n3. **Modeling bots**: Set `observe_me=False` for deterministic bots (scripted output — nothing meaningful to model). For AI assistants it's fine to leave observation on; turning it off is an optional optimization when you only care about the user.\n4. **Not storing messages**: Always call `add_messages()` to feed Honcho's reasoning engine\n5. **Blocking on processing**: Messages are processed asynchronously — don't poll or wait for reasoning to complete before continuing\n\n## Resources\n\n- Documentation (LLM-friendly index): <https://honcho.dev/docs/llms.txt>\n- Latest SDK versions: <https://honcho.dev/docs/changelog/introduction.md>\n- API Reference: <https://honcho.dev/docs/v3/api-reference/introduction.md>\n\n> Tip: append `.md` to any Honcho docs URL to fetch the raw Markdown version.\n","skills/honcho-memory/SKILL.md":"---\nname: honcho-memory\ndescription: Concepts and strategy for using a connected Honcho as persistent memory of the user — the recall/record loop and session and peer design. Start here to understand how Honcho memory works, then connect — via a first-class integration for your environment if one exists (preferred), or raw MCP tools (covered here) or the honcho-cli skill (CLI). For embedding the SDK into a codebase, use honcho-integration.\n---\n\n# Using Honcho as Memory\n\nHoncho is a memory layer for agents. You feed it the messages from your conversations; in the background it reasons over them and builds a **representation** of each participant. At any point you can ask it natural-language questions about the user (\"How technical are they?\", \"What are they trying to do?\") and get grounded, reasoned answers.\n\nThis skill is for when Honcho is **already connected** to you and you want to use it. If you're instead adding Honcho to a codebase from scratch, use the `honcho-integration` skill.\n\n> **What's durable vs. what to look up.** The concepts and the recall/record loop below change rarely — rely on them. Specifics that change often — the exact set of integrations, tool names, install commands, headers, and defaults — are illustrative here; treat the linked docs (and your own live tool list) as authoritative and fetch them when the details matter.\n\n## The mental model\n\n- **Peer** — any participant, human or AI. You and the user are both peers. Honcho builds a representation of peers it observes (typically the user, not you).\n- **Session** — one conversation thread; messages live in sessions. Honcho reasons over the messages in a session together, so scope each session to one coherent context (a conversation, channel, task, or project) and keep that context's turns in the same session rather than fragmenting them across many thin ones. For low-volume or trickle inputs, append to one ongoing session rather than spinning up a new one each time. See [design patterns](https://honcho.dev/docs/v3/documentation/core-concepts/design-patterns.md) and [reasoning](https://honcho.dev/docs/v3/documentation/core-concepts/reasoning.md).\n- **Message** — the raw turns you feed in. No messages → no reasoning → no memory.\n- **Conclusion** — a fact Honcho derived (or you stored) about a peer. Conclusions power the representation.\n- **Representation / peer card** — the synthesized understanding of a peer, queryable via `chat`. A peer's representation **accumulates across every session** it appears in — that's the cross-conversation memory. Session-scoped data (recent messages, summaries) stays local to one session.\n\nReasoning happens **asynchronously**. After you record a turn, don't poll or wait — the representation updates in the background and is richer next time you ask.\n\n## The loop: recall → respond → record\n\nDo this every conversation. It's the whole skill.\n\n1. **Once per conversation** — make sure there's a session with you and the user as peers (observe the user, don't observe yourself).\n2. **Before responding, when personalization helps** — pull the user's current context (`get_session_context` / `get_representation`) or search past messages (`search`) — these are fast reads. For a reasoned answer to a specific question, ask the dialectic (`chat`) — that one takes a few seconds, so use it when it earns its keep.\n3. **After every exchange** — record both the user's message and your reply. This is what makes Honcho learn. Don't skip it.\n\nOptionally, when you learn a durable fact you don't want to wait for background reasoning to surface, **store a conclusion** directly.\n\n## What you get back when you recall\n\nThree ways to pull memory, cheapest first:\n\n- **Representation** (`get_representation`) — Honcho's synthesized understanding of the user as text, ready to drop straight into a system prompt. Near-instant read.\n- **Context** (`get_session_context`) — the fuller session view: a session summary + recent messages covering the conversation, and — *only if you target a peer* — that peer's representation folded in. Without a peer target it's session-local (recent turns + summary) and carries no cross-conversation memory. Near-instant read.\n- **Dialectic** (`chat`) — a *reasoned* natural-language answer to a specific question (\"How does this user like to receive feedback?\"). Runs live reasoning, so it takes a few seconds. Use it when a plain read won't answer the question.\n\nThe dialectic (`chat`) also takes a **reasoning level** that trades speed for depth — from `minimal` (fast factual lookup) through `low` (the default balance) to `max` (deep synthesis for the hardest questions). Pick the lowest level that answers the question; higher levels are slower and cost more. The full level-by-level table and model routing are in the [chat docs](https://honcho.dev/docs/v3/documentation/features/chat.md).\n\n---\n\n## Pick your access path\n\nThe loop is the same; the mechanics depend on how you reach Honcho. **Prefer a purpose-built integration over wiring up raw MCP yourself** — they handle sessions, peers, and the record loop for you, stay current, and are tuned per environment.\n\n1. **A first-class integration exists for your environment? Use it.** In Claude Code, install the [Claude Code plugin](https://honcho.dev/docs/v3/guides/integrations/claude-code.md) (`/plugin marketplace add plastic-labs/claude-honcho`) for persistent memory out of the box; there are also plugins/integrations for [OpenCode](https://honcho.dev/docs/v3/guides/integrations/opencode.md), LangGraph, CrewAI, Discord, and more. Browse the always-current list: <https://honcho.dev/docs/v3/guides/overview.md>.\n2. **No integration, but you have MCP tools** (`create_session`, `add_messages_to_session`, `chat`, …) → drive them with the loop above. The MCP server injects its own usage guide on connect, so there's nothing extra to load; to connect a client yourself, see [Setup](#setup-if-not-connected-yet) below. This is the fallback for connected agents.\n3. **`honcho` CLI available** in a terminal → use the **`honcho-cli`** skill — for the recall/record loop, and for verifying that memory is actually building (did messages land? is the representation growing? why doesn't it remember me?).\n4. **Embedding Honcho into your own codebase** (not just using a connected instance) → use the **`honcho-integration`** skill.\n\nIf you're unsure, list your available tools and look for Honcho memory tools (an MCP connection) before falling back to the CLI. Even on the MCP path, the `honcho-cli` skill is the best way to **verify the loop is working** if memory seems off.\n\n---\n\n## Setup (if not connected yet)\n\nYou need a Honcho API key — get one free at <https://app.honcho.dev> (starts with `hch-`). Then connect via the path you picked above — a purpose-built integration (recommended), or a raw connection:\n\n- **MCP** — point your client at `https://mcp.honcho.dev` with `Authorization: Bearer hch-your-key-here`. Optional `X-Honcho-Workspace-ID` fills the `workspace_id` tool argument when omitted; otherwise pass `workspace_id` on each call (use `list_workspaces` to discover IDs). Restart the client fully after adding config. Per-client config snippets (Claude Desktop, Cursor, Codex, Windsurf, VS Code, Cline, Zed) are in the [MCP integration guide](https://honcho.dev/docs/v3/guides/integrations/mcp.md). Once connected, the server tells your assistant how to use the tools automatically.\n- **CLI** — use the `honcho-cli` skill.\n\n---\n\n## Rules of thumb\n\n- **Always record turns.** Memory only grows from messages you feed in. Recording is the one non-optional step.\n- **Modeling the assistant is optional.** Setting `observe_me: false` on the assistant peer skips building a model of it — required only for deterministic bots (scripted output, nothing meaningful to model). For an AI assistant it's fine to leave observation on if you also want a model of the agent.\n- **One stable peer ID per entity.** Reuse the same `peer_id` for a person across every session and channel; splitting them (`user`, `user-web`, `user-discord`) builds separate representations and fragments memory.\n- **Scope sessions to coherent context buckets.** Honcho reasons over a session's messages together. Scope a session to one active interaction (per-conversation, per-channel, per-task, per-project); create a new one when context genuinely resets (new topic, new day), reuse it while context should keep accumulating. Keeping a context's turns in one session produces a coherent representation; scattering them fragments it.\n- **Don't block on reasoning.** It's asynchronous. Respond now; the representation will be richer next time.\n- **Reads are cheap; reasoning isn't.** Fetching the representation/context (`get_session_context`, `get_representation`, `search`) is a near-instant read — use it freely. The dialectic (`chat`) runs live reasoning and takes a few seconds, so save it for when you genuinely need a reasoned answer, not every turn.\n- **Check before you store.** Background reasoning derives most conclusions automatically. Store a conclusion manually only for a durable fact you want available immediately; `list`/`query` first to avoid duplicates.\n- **One workspace per app/user-context.** Don't scatter the same user's memory across multiple workspaces.\n- **Unify memory across tools with a shared workspace + peer ID.** To give one user continuous memory across several apps or agents (e.g. Claude Code, Cursor, your own app), point them at the same workspace and reuse the same peer ID — that shared ID is what links the representation. See [Unified Memory Setup](https://honcho.dev/docs/v3/guides/recipes/unified-memory-setup.md).\n\n## Resources\n\nThese are the LLM-friendly Markdown versions (append `.md` to any Honcho docs URL to get the raw Markdown; the full machine-readable index is at <https://honcho.dev/docs/llms.txt>).\n\n- Full docs index (for agents): <https://honcho.dev/docs/llms.txt>\n- All integrations & plugins: <https://honcho.dev/docs/v3/guides/overview.md>\n- MCP server & client setup: <https://honcho.dev/docs/v3/guides/integrations/mcp.md>\n- Full MCP usage walkthrough: <https://raw.githubusercontent.com/plastic-labs/honcho/refs/heads/main/mcp/instructions.md>\n- Agent development overview: <https://honcho.dev/docs/v3/documentation/introduction/vibecoding.md>\n- CLI reference: <https://honcho.dev/docs/v3/documentation/reference/cli.md>\n","CLAUDE.md":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n**Before opening a PR or filing an issue**, read [If you're an agent](CONTRIBUTING.md#if-youre-an-agent) in `CONTRIBUTING.md`. Every PR needs a linked issue labelled `maintainer-approved`, and issues follow [Filing an issue](CONTRIBUTING.md#filing-an-issue). Run the `pre-pr` skill (`skills/pre-pr/`) before opening a PR.\n\n# Honcho Overview\n\n## What is Honcho?\n\nHoncho is an infrastructure layer for building AI agents with memory and social cognition. Its primary purposes include:\n\n- Imbuing agents with a sense of identity\n- Personalizing user experiences through understanding user psychology\n- Providing a Chat Endpoint (the Dialectic agent) that injects personal context just-in-time\n- Supporting development of LLM-powered applications that adapt to end users\n- Enabling multi-peer sessions where multiple participants (users or agents) can interact\n\nHoncho leverages the inherent reasoning capabilities of LLMs to build coherent models of user psychology over time, enabling more personalized and effective AI interactions.\n\n## Core Concepts\n\n### Peer Paradigm\n\nHoncho uses a peer-based model where both users and agents are represented as \"peers\". This unified approach enables:\n\n- Multi-participant sessions with mixed human and AI agents\n- Configurable observation settings (which peers observe which others)\n- Flexible identity management for all participants\n\n### Key Primitives\n\n- **Workspace** (formerly App): The root organizational unit containing all resources\n- **Peer** (formerly User): Any participant in the system (human or AI)\n- **Session**: A conversation context that can involve multiple peers\n- **Message**: Data units that can represent communication between peers OR arbitrary data ingested by a peer to enhance its global representation\n- **Collections & Documents**: Internal vector storage for peer representations. Collections are keyed by `(observer, observed)` peer pairs. Collections/Documents are not directly exposed via API, but the observations stored within them are exposed as **Conclusions** (see `/v3/.../conclusions` endpoints).\n\n## Architecture Overview\n\n### API Structure\n\nAll API routes follow the pattern: `/v3/{resource}/{id}/{action}`. Most \"list/search\" endpoints are `POST` so they can accept rich filter bodies.\n\n- **Workspaces**: Create, list, update, search\n- **Peers**: Create, list, update, chat (dialectic), messages, representation\n- **Sessions**: Create, list, update, delete, clone, manage peers, get context\n- **Messages**: Create (batch up to 100), upload (file), list, get, update\n- **Conclusions**: Create, list, query (semantic search), delete — the API-facing name for observations stored in `(observer, observed)` collections\n- **Keys**: Create scoped JWTs\n- **Webhooks**: Register endpoint, list, delete, test\n\n### Key Features\n\n#### Chat Endpoint (Dialectic agent) (`/peers/{peer_id}/chat`)\n\n- Provides bespoke responses informed by the representation\n- Integrates long-term facts from vector storage\n- Supports streaming responses\n- Configurable LLM providers\n\n#### Message Processing Pipeline\n\n1. Messages created via API (batch or single)\n2. Enqueued for background processing:\n   - `representation`: Update peer's context\n   - `summary`: Create session summaries\n3. Session-based queue processing ensures order\n4. Results stored internally in vector DB\n\n### Configuration\n\n- Hierarchical config: config.toml + environment variables\n- Database settings with connection pooling\n- Multiple LLM provider support\n- Background worker (deriver) settings\n- Authentication can be toggled on/off\n\n## Development Guide\n\n### Commands\n\n- Setup: `uv sync`\n- Run server: `uv run fastapi dev src/main.py`\n- Run tests: `uv run pytest tests/`\n- Run single test: `uv run pytest tests/path/to/test_file.py::test_function`\n- Linting: `uv run ruff check src/`\n- Typechecking: `uv run basedpyright`\n- Format code: `uv run ruff format src/`\n\n### SDK Testing\n\n#### TypeScript SDK\n\nThe TypeScript SDK tests run against a real Honcho server. `bun run test` (not bare `bun test`, which has no server to talk to) starts one for them: `scripts/run_ts_sdk_tests.py` creates a throwaway database on the Postgres named by `DB_CONNECTION_URI`, migrates it, starts the API and the mock LLM provider (`src/mock_provider`), runs `bun test`, and tears it all down. No provider key is needed. They are not part of `uv run pytest`.\n\n```bash\ncd sdks/typescript && bun run test             # whole suite\ncd sdks/typescript && bun run test peer.test   # bun test args pass through\n```\n\n**To type-check the TypeScript SDK (this is fine to run directly):**\n\n```bash\ncd sdks/typescript && bun run tsc --noEmit\n```\n\n### Code Style\n\n- Follow isort conventions with absolute imports preferred\n- Use explicit type hints with SQLAlchemy mapped_column annotations\n- snake_case for variables/functions; PascalCase for classes\n- Line length: 88 chars (Black compatible)\n- Explicit error handling with appropriate exception types\n- Docstrings: Use Google style docstrings\n- **Never hold a DB session during external calls** (LLM, embedding, HTTP). If a function needs both a DB session and an external call result, compute the external result first and pass it as a parameter. This avoids tying up DB connections during slow network I/O. Use `tracked_db` for short-lived, DB-only operations; pass a shared session when multiple DB-only calls can reuse one connection.\n- **Never write through a read-only session** (`tracked_db(..., read_only=True)`, `get_read_db`, `ReadSessionLocal`). These run in AUTOCOMMIT mode with no transaction: writes are NOT blocked by the database — they silently commit immediately, and `begin_nested()` savepoints break. There is no runtime guard; this is enforced by convention only. Use `read_only=True` strictly for SELECT-only windows; anything that mutates (including get-or-create paths) must use a regular write session.\n\n#### Multi-row locking and deadlocks\n\nTables written concurrently by more than one worker — `documents` (deriver, dreamer, scope backfill/removal, reconciler) and `queue` (every deriver replica) — deadlock when two writers touch an overlapping row set in different orders. Rules:\n\n- **A multi-row `SELECT ... FOR UPDATE` MUST carry an explicit `ORDER BY <pk>`.** Without it Postgres locks in scan order, which differs per plan, so two writers with overlapping sets can cycle. `_apply_document_row_updates` in `src/crud/document.py` is the reference implementation.\n- **`WHERE id IN (...)` does NOT impose an order**, so sorting the Python list is a no-op — the list order is discarded and the planner picks `Bitmap Heap Scan` (ctid order), `Index Scan` (id order), or `Seq Scan` per invocation. Deterministic ordering requires either a preceding `SELECT ... ORDER BY id FOR UPDATE` or `WHERE id IN (SELECT id ... ORDER BY id FOR UPDATE)`.\n- **`Document.id` is a random nanoid** (`models.py`), so id order is uncorrelated with physical order — an unordered predicate `UPDATE`/`DELETE` is roughly a coin flip against an id-ordered locker per row pair, not a rare edge case. (`QueueItem.id` is an integer identity, so there id order is also chronological.)\n- **Prefer no lock at all.** A single `UPDATE ... WHERE <predicate>` acquires row locks as it writes and has no separate lock phase to get wrong. Reach for `FOR UPDATE` only when a value must be read, computed in Python, and written back — that read-modify-write is the only reason `_apply_document_row_updates` locks (it replaced a server-side `func.greatest()`), and `populate_existing=True` is required with it so the identity map doesn't serve a stale pre-lock value. Server-side expressions (`func.greatest`, the JSONB `-` operator) avoid the lock entirely; see `_clear_work_unit_retry_attempts` in `src/deriver/queue_manager.py`.\n- `FOR UPDATE SKIP LOCKED` (the reconciler's claim pattern) never waits, so it cannot be a deadlock partner — but holding those locks across an external call still stalls other writers. See the \"never hold a DB session during external calls\" rule above.\n\n#### Auth scoping\n\n- **Named-scope keys (`sc` plus `w`) require explicit `allow_scope=True` on supported routes.** This grants both recall and ingestion within that scope, not peer-member write access. Recall handlers must bind their projection to the token; session routes check active scope membership. Keep the named-scope route allowlist in `tests/routes/test_auth_route_policy.py` synchronized. Never allow restricted keys to enroll unrelated existing sessions or fall back to global recall.\n\n- **`allow_member_read=True` (in `require_auth(...)`) is read-only — NEVER set it on a route that mutates state.** It lets a peer-scoped key reach a session route when its peer is an active member of the session, so on a mutating route it would hand any session member write access (message injection, config mutation, deletion). HTTP method is not a reliable read/write signal here (some read routes use POST for a richer body), so this is enforced by an explicit allowlist in `tests/routes/test_auth_route_policy.py` — adding the flag to a new route fails that test until you consciously add the route to `EXPECTED_MEMBER_READ_ROUTES`, and you must never add a mutating method there.\n- **When a member-read route is keyed by another sub-resource** (e.g. `peers/{peer_id}/config`), the handler must additionally confirm a peer-scoped caller only reads its OWN resource (`jwt_params.p == peer_id`, else raise `AuthenticationException`). Membership grants session access, not access to a co-member's data. See `get_peer_config` in `src/routers/sessions.py`.\n\n### Runtime Architecture\n\nHoncho runs as two cooperating processes that share a Postgres database and Redis cache:\n\n- **API server** (`uv run fastapi dev src/main.py`) — handles HTTP, enqueues background work, returns immediately. Hosts the **Dialectic** agent inline (synchronous tool loop during chat requests).\n- **Deriver worker** (`uv run python -m src.deriver`) — long-running queue consumer (uvloop). Runs the **Deriver**, **Summarizer**, and **Dreamer** off the queue. Can run multiple instances (`DERIVER_WORKERS`). Also hosts an in-process **Reconciler scheduler** (`src/reconciler/`) that periodically embeds messages with `sync_state='pending'` in `MessageEmbedding` and cleans up stale queue items — embedding generation is decoupled from message creation by design.\n\n### Agent Architecture\n\nHoncho uses several specialized LLM agents. They share tool definitions and the LLM client abstraction in `src/utils/agent_tools.py` + `src/llm/`.\n\n> **Terminology:** what users see as **conclusions** (the public API surface and the term we use in documentation) is called **observations** in code symbols — `create_observations`, `delete_observations`, `get_observation_context`, etc. Doc prose below uses \"conclusions\"; references to actual code symbols stay as \"observations.\"\n\n#### 1. Deriver (`src/deriver/`)\n\n**Role**: Memory formation through content ingestion.\n\nThe Deriver processes batches of incoming messages and extracts conclusions about peers. The current architecture is \"minimal deriver\" — a **single LLM call** per batch using structured output, not an agentic tool loop. This trades flexibility for cost and predictability.\n\n- **Trigger**: Messages enqueued by `src/deriver/enqueue.py` on message create; consumed by `src/deriver/queue_manager.py` → `consumer.process_item()` → `deriver.process_representation_tasks_batch()`.\n- **Output**: Explicit conclusions (direct facts) and deductive conclusions (inferences) saved to `(observer, observed)` collections.\n- **Entry point**: `src/deriver/__main__.py` → `queue_manager.main()`.\n- **Prompts**: `src/deriver/prompts.py` (`minimal_deriver_prompt`).\n- **Custom instructions**: per-workspace/peer guidance can be threaded into the prompt via reasoning configuration; `DERIVER__MAX_CUSTOM_INSTRUCTIONS_TOKENS` caps the addition (default 2000) and `DERIVER__MAX_INPUT_TOKENS` defaults to 25000 to make room.\n\n#### 2. Dialectic (`src/dialectic/`)\n\n**Role**: Analysis and recall for answering queries.\n\nThe Dialectic answers questions about peers by strategically gathering context from memory. It is the only tool-using agent on the synchronous request path — it loops over `DIALECTIC_TOOLS` until it has enough context to answer. (The Dreamer specialists also use tools, but run off the queue.)\n\n- **Trigger**: API call to `POST /v3/.../peers/{peer_id}/chat`.\n- **Tools** (see `DIALECTIC_TOOLS` in `src/utils/agent_tools.py`): `search_memory`, `search_messages`, `get_observation_context`, `grep_messages`, `get_messages_by_date_range`, `search_messages_temporal`, `get_reasoning_chain`. At the `minimal` reasoning level, a reduced set (`DIALECTIC_TOOLS_MINIMAL`) is used: just `search_memory` + `search_messages`.\n- **Reasoning levels**: 5 tiers — `minimal`, `low`, `medium`, `high`, `max` — each with its own model config (see `DialecticLevelSettings` in `src/config.py`).\n- **Output**: Natural language response grounded in gathered context. Supports SSE streaming.\n- **Entry point**: `src/dialectic/chat.py` → `agentic_chat()` / `agentic_chat_stream()` → `DialecticAgent` (in `src/dialectic/core.py`).\n\n#### 3. Dreamer (`src/dreamer/`)\n\n**Role**: Consolidation and self-improvement of memory.\n\nThe Dreamer is an orchestrated multi-specialist system that runs during scheduled \"dreams\" to consolidate conclusions and build reasoning trees.\n\n- **Trigger**: Scheduled via `DreamScheduler` (`src/dreamer/dream_scheduler.py`) or explicit dream task on the queue.\n- **Strategy**: Surprisal-based prioritization (`src/dreamer/surprisal.py`) selects which conclusions to expand. The orchestrator (`orchestrator.run_dream`) runs two specialist phases:\n  1. **DeductionSpecialist** (`specialists.py`) — produces deductive conclusions from explicit conclusions. Tools: `get_recent_observations`, `search_memory`, `search_messages`, `create_observations_deductive`, `delete_observations`, `update_peer_card`.\n  2. **InductionSpecialist** — produces inductive conclusions from explicit + deductive conclusions. Tools: same discovery set + `create_observations_inductive`, `update_peer_card`.\n- **Reasoning trees** (`src/dreamer/trees/`, migration `f1a2b3c4d5e6_add_reasoning_tree_columns`): each conclusion links to its premises and downstream conclusions, enabling `get_reasoning_chain` traversal at recall time.\n- **Output**: Deductive/inductive conclusions, consolidated redundancies, updated peer cards.\n- **Entry point**: `src/dreamer/orchestrator.py` → `process_dream()` (the package-level export from `src/dreamer/__init__.py`), which wraps `run_dream()`.\n\n#### 4. Summarizer (`src/utils/summarizer.py`)\n\n**Role**: Two-tier session summarization (direct LLM call — no agentic tools).\n\n- **Trigger**: Runs as part of the queue pipeline alongside representation tasks.\n- **Tiers**: short summary every `SUMMARY_MESSAGES_PER_SHORT_SUMMARY` messages (default 20); long summary every `SUMMARY_MESSAGES_PER_LONG_SUMMARY` (default 60). Token caps configurable via `SUMMARY_MAX_TOKENS_SHORT` / `SUMMARY_MAX_TOKENS_LONG`.\n\n#### Shared Agent Infrastructure\n\n- **Tool definitions** (`src/utils/agent_tools.py`): unified `TOOLS` dict; per-agent lists (`DIALECTIC_TOOLS`, `DIALECTIC_TOOLS_MINIMAL`, `DREAMER_TOOLS`, `DEDUCTION_SPECIALIST_TOOLS`, `INDUCTION_SPECIALIST_TOOLS`).\n- **LLM subsystem** (`src/llm/`): provider-agnostic `honcho_llm_call()`. Backends in `src/llm/backends/` (`anthropic.py`, `gemini.py`, `openai.py`). Includes prompt caching (`caching.py`), structured output (`structured_output.py`), tool loop (`tool_loop.py`), history adapters for cross-provider message formats, and a model registry. Per-retry provider selection is pinned via an `AttemptPlan` so stream-final retries don't bounce back to primary after the tool loop has settled on fallback.\n- **Per-agent model config**: each agent has its own `MODEL_CONFIG` in `src/config.py` with fallback chains (see `ConfiguredModelSettings`, `FallbackModelSettings`).\n- **Telemetry**: cloudevents in `src/telemetry/events/` cover API routes, dialectic, dream, deletion, reconciliation, representation, and per-call LLM accounting (`llm.py` — `LLMCallCompletedEvent` fires once per provider hit with full cost-attribution context). High-volume events are sampled deterministically per `run_id` via `TelemetrySettings.HIGH_VOLUME_SAMPLE_RATE`.\n- **Prometheus metrics** (`src/telemetry/prometheus/`): every metric carries a `namespace` label and every recorder is fail-soft (a metrics error never propagates into a request or a worker loop). Counter children with a *bounded* label domain are zero-initialized per process at startup — `initialize_bounded_metrics(instance_type=...)`, called from the `src/main.py` lifespan (`api`) and `src/deriver/__main__.py` (`deriver`) — so an absent series means a broken scrape rather than \"nothing happened\". Two consequences worth knowing before touching telemetry:\n  - **Adding a `BaseEvent` subclass requires adding its `_event_type` to `ALL_EVENT_TYPES`** in `src/telemetry/events/__init__.py` (and to `HIGH_VOLUME_EVENT_TYPES` if `_volume_class == \"high_volume\"`). Enforced by the drift guards in `tests/telemetry/test_metric_zero_init.py`, which assert set-equality against the discovered subclasses.\n  - **A service-wide, non-additive gauge must be refreshed by every replica on its own timer**, and aggregated with `max()`/`avg()`, never `sum()`. `message_embeddings_pending` is the example: it reports a DB-global count, so it is driven from `ReconcilerScheduler._scheduler_loop` (runs on all replicas) rather than from the work-unit-deduped reconciliation cycle — otherwise, combined with the zero-init, every replica that never won the work unit would export a confident permanent `0`.\n\n### Project Structure\n\n```\nsrc/\n├── main.py              # FastAPI app: middleware, routers, lifespan, exception handlers\n├── models.py            # SQLAlchemy ORM models (Workspace/Peer/Session/Message/\n│                        #   MessageEmbedding/Collection/Document/QueueItem/...)\n├── config.py            # Pydantic-settings configuration (very large; see README)\n├── db.py                # Engine + session/context management (request_context var)\n├── dependencies.py      # FastAPI DI (tracked_db, etc.)\n├── exceptions.py        # Custom exception types (HonchoException + subclasses)\n├── security.py          # JWT authentication\n├── embedding_client.py  # Embedding provider client (configurable dimensions\n│                        #   via EMBEDDING_MODEL_CONFIG__DIMENSIONS_MODE)\n├── schemas/             # Pydantic schemas\n│   ├── api.py            # Public API request/response schemas\n│   ├── configuration.py  # Per-resource configuration schemas\n│   └── internal.py       # Internal-only schemas (queue payloads, etc.)\n├── crud/                # Per-resource DB operations\n│   ├── collection.py, deriver.py, document.py, message.py\n│   ├── peer.py, peer_card.py, representation.py  (RepresentationManager)\n│   ├── session.py, webhook.py, workspace.py\n├── routers/             # FastAPI route handlers (all under /v3)\n│   ├── workspaces.py, peers.py (dialectic /chat lives here), sessions.py\n│   ├── messages.py, conclusions.py, keys.py, webhooks.py\n├── dialectic/           # Dialectic agent — runs inline per chat request\n│   ├── chat.py           # agentic_chat() / agentic_chat_stream()\n│   ├── core.py           # DialecticAgent (the tool-loop driver)\n│   └── prompts.py\n├── deriver/             # Background queue consumer (separate process)\n│   ├── __main__.py       # `python -m src.deriver` entry point\n│   ├── queue_manager.py  # QueueManager + main() loop\n│   ├── consumer.py       # process_item dispatcher (representation / deletion / reconciler)\n│   ├── deriver.py        # \"minimal deriver\" — single-LLM-call batch processor\n│   ├── enqueue.py        # API → queue producer\n│   └── prompts.py\n├── dreamer/             # Memory consolidation (runs off the queue)\n│   ├── orchestrator.py   # run_dream() / process_dream()\n│   ├── specialists.py    # DeductionSpecialist + InductionSpecialist\n│   ├── dream_scheduler.py\n│   ├── surprisal.py      # Surprisal-based conclusion prioritization\n│   └── trees/            # Reasoning-tree primitives\n├── reconciler/          # In-process scheduler hosted by the deriver worker\n│   ├── scheduler.py      # ReconcilerScheduler (started from queue_manager.py)\n│   ├── sync_vectors.py   # Embeds MessageEmbedding rows with sync_state='pending'\n│   └── queue_cleanup.py  # Removes stale queue items\n├── llm/                 # Provider-agnostic LLM client subsystem\n│   ├── api.py, backend.py, executor.py, runtime.py, registry.py\n│   ├── caching.py, structured_output.py, tool_loop.py, conversation.py\n│   ├── history_adapters.py, request_builder.py, credentials.py, types.py\n│   └── backends/         # anthropic.py, gemini.py, openai.py\n├── cache/               # Redis cache abstraction (cashews-backed)\n│   └── client.py\n├── vector_store/        # Optional external vector stores (pgvector is default,\n│   │                    #   implemented via MessageEmbedding/Document in models+crud)\n│   ├── lancedb.py\n│   └── turbopuffer.py\n├── telemetry/           # Observability\n│   ├── emitter.py        # CloudEvents emitter\n│   ├── logging.py        # Logging helpers + route-template extraction\n│   ├── metrics_collector.py, reasoning_traces.py, sentry.py\n│   ├── events/           # Event type definitions\n│   └── prometheus/       # Prometheus metric definitions\n├── utils/               # Cross-cutting utilities\n│   ├── agent_tools.py    # Tool definitions + per-agent tool lists\n│   ├── summarizer.py     # Two-tier session summarizer\n│   ├── representation.py # Representation formatting (distinct from crud/representation.py)\n│   ├── search.py, filter.py, formatting.py\n│   ├── tokens.py         # tiktoken-based counting\n│   ├── pagination.py     # List pagination: opt-in cursor mode + the cached offset shim\n│   ├── work_unit.py, queue_payload.py\n│   ├── config_helpers.py, json_parser.py, files.py\n│   └── types.py\n└── webhooks/            # Webhook delivery\n    ├── events.py\n    └── webhook_delivery.py\n```\n\n- Tests in pytest with fixtures in tests/conftest.py; subdirs mirror src/ (`tests/deriver/`, `tests/dialectic/`, etc.) plus `tests/bench/` (perf benchmarks), `tests/integration/`, `tests/live_llm/` (gated by `--live-llm`), and `tests/unified/` (the unified runner).\n- Use environment variables via python-dotenv (.env). Config precedence: env > .env > config.toml > defaults.\n\n### Database Design\n\n- All tables use text IDs (nanoid format) as primary keys\n- Composite foreign keys for multi-tenant relationships\n- Feature flags on workspace, peer, and session levels\n- Token counting on messages for usage tracking\n- JSONB metadata fields for extensibility\n- HNSW indexes for vector similarity search\n- **A `Select` that feeds a list endpoint MUST have a total ORDER BY ending in a unique column** (e.g. `created_at, id`). `paginate_offset_or_cursor` (`src/utils/pagination.py`) seeks by the sort key in both cursor mode and the offset shim; without a unique tail, rows that share a sort key are skipped or repeated across pages.\n\n### Key Architectural Decisions\n\n1. **Peer Paradigm**: humans and AI agents are unified as \"Peers\"; many-to-many with Sessions. Internal vector storage (Collections/Documents) is keyed by `(observer, observed)` peer pairs — the same mechanism powers self-representation (`observer == observed`) and cross-peer modeling.\n2. **Multi-Peer Sessions**: Sessions can have multiple participants with different observation settings.\n3. **API server / worker split**: API enqueues, deriver worker process consumes. Never block HTTP on LLM work. The Reconciler runs as an in-process scheduler inside the deriver, handling async embedding sync and queue cleanup.\n4. **\"Minimal\" deriver**: memory formation is a single structured-output LLM call per batch, not an agentic tool loop. Predictable cost, lower latency. The Dialectic is the one true tool-using agent.\n5. **Provider-agnostic LLM layer** (`src/llm/`): all model calls go through `honcho_llm_call()`. Backends (`anthropic`, `gemini`, `openai`) sit behind a registry; per-agent `MODEL_CONFIG` with fallback chains is resolved at call time.\n6. **Dialectic reasoning tiers**: 5 levels (`minimal` → `max`); each level has its own model config and tool set (`minimal` uses a reduced toolset).\n7. **Hybrid search**: Postgres FTS (GIN index on `to_tsvector('english', content)`, OR'd with a `content ILIKE` fallback backed by a `pg_trgm` GIN index; without pg_trgm the index is skipped and the ILIKE scans) + vector similarity (HNSW on `MessageEmbedding.embedding`). `MessageEmbedding` is a separate table from `Message` with its own `sync_state` so embedding is decoupled from message creation.\n8. **Pluggable external vector stores**: defaults to pgvector inline; can swap to turbopuffer or lancedb (`VECTOR_STORE_*` config; `src/vector_store/`).\n9. **Composite-FK multi-tenancy**: `workspace_name` participates in nearly every composite FK. Cross-workspace data leakage is structurally impossible at the schema level.\n10. **Scoped Authentication**: JWTs can be scoped to workspace, peer, or session level.\n11. **Batch Operations**: Bulk message creation up to 100 messages per request.\n12. **Session History**: Two-tier summarization — short every `SUMMARY_MESSAGES_PER_SHORT_SUMMARY` (default 20), long every `SUMMARY_MESSAGES_PER_LONG_SUMMARY` (default 60).\n\n### Error Handling\n\n- Custom exceptions defined in src/exceptions.py\n- Use specific exception types (ResourceNotFoundException, ValidationException, etc.)\n- Proper logging with context instead of print statements\n- Global exception handlers defined in main.py\n- See docs/contributing/error-handling.mdx for details\n\n### Notes\n\n- Always use `uv run` or `uv` to prefix any commands related to python to ensure you use the virtual environment\n","skills/honcho-integration/references/core-patterns.md":"# Core Integration Patterns\n\nThe base SDK boilerplate for any Honcho integration: choosing sync vs async, initializing the client, creating peers, configuring sessions, and adding messages. Read this once you've chosen your entities and session structure (Phases 1–2). For the agent-facing recall patterns (tool call, pre-fetch, `context()`), see `agent-patterns.md`.\n\n## Sync vs Async\n\n**TypeScript** — The SDK is async by default. All methods return promises. No separate sync API.\n\n**Python** — The SDK provides both sync and async interfaces:\n\n- **Sync** (default): `from honcho import Honcho` — use in sync frameworks (Flask, Django, CLI scripts)\n- **Async**: `from honcho import Honcho` with `.aio` namespace — use in async frameworks (FastAPI, Starlette, async workers)\n\n```python\n# Sync usage (Flask, Django, scripts)\nfrom honcho import Honcho\nhoncho = Honcho(workspace_id=\"my-app\", api_key=os.environ[\"HONCHO_API_KEY\"])\npeer = honcho.peer(\"user-123\")\nresponse = peer.chat(\"What does this user prefer?\")\n\n# Async usage (FastAPI, Starlette)\nfrom honcho import Honcho\nhoncho = Honcho(workspace_id=\"my-app\", api_key=os.environ[\"HONCHO_API_KEY\"])\npeer = await honcho.aio.peer(\"user-123\")\nresponse = await peer.aio.chat(\"What does this user prefer?\")\n```\n\nMatch the client to the framework — check whether the codebase uses `async def` handlers or sync `def` handlers and choose accordingly. The examples below show sync Python; swap to `.aio` equivalents for async codebases.\n\n## 1. Initialize with a Single Workspace\n\nUse ONE workspace for your entire application. The workspace name should reflect your app/product.\n\n**Python:**\n\n```python\nfrom honcho import Honcho\nimport os\n\n# Sync client (Flask, Django, scripts)\nhoncho = Honcho(\n    workspace_id=\"your-app-name\",\n    api_key=os.environ[\"HONCHO_API_KEY\"],\n    environment=\"production\"\n)\n\n# Async client (FastAPI, Starlette) — use honcho.aio for all operations\n# honcho.aio.peer(), honcho.aio.session(), etc.\n```\n\n**TypeScript:**\n\n```typescript\nimport { Honcho } from '@honcho-ai/sdk';\n\n// All methods are async by default\nconst honcho = new Honcho({\n    workspaceId: \"your-app-name\",\n    apiKey: process.env.HONCHO_API_KEY,\n    environment: \"production\"\n});\n```\n\n## 2. Create Peers for ALL Entities\n\nCreate peers for **every entity** in your business logic - users AND AI assistants.\n\n**Python:**\n\n```python\nfrom honcho.api_types import PeerConfig\n\n# Human users (observed by default)\nuser = honcho.peer(\"user-123\")\n\n# AI assistants can be observed too — leave observe_me on (the default) if you\n# want a model of the assistant.\nassistant = honcho.peer(\"assistant\")\n\n# Deterministic bots (scripted/rule-based) - set observe_me=False; there's\n# nothing meaningful for Honcho to model.\nnotification_bot = honcho.peer(\"notification-bot\", configuration=PeerConfig(observe_me=False))\n```\n\n**TypeScript:**\n\n```typescript\n// Human users (observed by default)\nconst user = await honcho.peer(\"user-123\");\n\n// AI assistants can be observed too — leave observeMe on (the default) if you\n// want a model of the assistant.\nconst assistant = await honcho.peer(\"assistant\");\n\n// Deterministic bots (scripted/rule-based) - set observeMe=false; there's\n// nothing meaningful for Honcho to model.\nconst notificationBot = await honcho.peer(\"notification-bot\", { configuration: { observeMe: false } });\n```\n\n## 3. Multi-Peer Sessions\n\nSessions can have multiple participants. Configure observation settings per-peer.\n\n**Python:**\n\n```python\nfrom honcho.api_types import SessionPeerConfig\n\nsession = honcho.session(\"conversation-123\")\n\n# User is observed (Honcho builds a model of them)\nuser_config = SessionPeerConfig(observe_me=True, observe_others=True)\n\n# A deterministic bot is NOT observed (no model built of it). An AI assistant\n# could stay observed instead — observe_me defaults to True.\nbot_config = SessionPeerConfig(observe_me=False, observe_others=True)\n\nsession.add_peers([\n    (user, user_config),\n    (notification_bot, bot_config)\n])\n```\n\n**TypeScript:**\n\n```typescript\nconst session = await honcho.session(\"conversation-123\");\n\nawait session.addPeers([\n    // A deterministic bot isn't observed; an AI assistant could stay observed\n    // instead (observeMe defaults to true).\n    [user, { observeMe: true, observeOthers: true }],\n    [notificationBot, { observeMe: false, observeOthers: true }]\n]);\n```\n\n## 4. Add Messages to Sessions\n\n**Python:**\n\n```python\nsession.add_messages([\n    user.message(\"I'm having trouble with my account\"),\n    assistant.message(\"I'd be happy to help. What seems to be the issue?\"),\n    user.message(\"I can't reset my password\")\n])\n```\n\n**TypeScript:**\n\n```typescript\nawait session.addMessages([\n    user.message(\"I'm having trouble with my account\"),\n    assistant.message(\"I'd be happy to help. What seems to be the issue?\"),\n    user.message(\"I can't reset my password\")\n]);\n```\n","skills/honcho-integration/references/agent-patterns.md":"# Agent Access Patterns\n\nHow your AI accesses Honcho's user context. Pick based on the interview answer to Question Set 2. These build on the client/peer/session setup in `core-patterns.md`.\n\n- **Pattern A — Dialectic chat as a tool call**: the agent decides when to query context on-demand.\n- **Pattern B — Pre-fetch with targeted queries**: fetch a fixed set of attributes before each LLM call.\n- **Pattern C — `context()` for LLM integration**: inject conversation history + representation into the prompt.\n\n> **Speed note.** `chat()` runs live dialectic reasoning (a few seconds) — Patterns A and B call it. `context()` (Pattern C) is a near-instant read. Prefer `context()` for per-turn grounding; reach for `chat()` when you genuinely need a reasoned answer.\n\n## Pattern A: Dialectic Chat as a Tool Call\n\nMake Honcho's chat endpoint available as a **tool** for your AI agent. This lets the agent query user context on-demand.\n\n**Python (OpenAI function calling):**\n\n```python\nimport openai\nfrom honcho import Honcho\n\nhoncho = Honcho(workspace_id=\"my-app\", api_key=os.environ[\"HONCHO_API_KEY\"])\n\n# Define the tool for your agent\nhoncho_tool = {\n    \"type\": \"function\",\n    \"function\": {\n        \"name\": \"query_user_context\",\n        \"description\": \"Query Honcho to retrieve relevant context about the user based on their history and preferences. Use this when you need to understand the user's background, preferences, past interactions, or goals.\",\n        \"parameters\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"query\": {\n                    \"type\": \"string\",\n                    \"description\": \"A natural language question about the user, e.g. 'What are this user's main goals?' or 'What communication style does this user prefer?'\"\n                }\n            },\n            \"required\": [\"query\"]\n        }\n    }\n}\n\ndef handle_honcho_tool_call(user_id: str, query: str) -> str:\n    \"\"\"Execute the Honcho chat tool call.\"\"\"\n    peer = honcho.peer(user_id)\n    return peer.chat(query)\n\n# Use in your agent loop\ndef run_agent(user_id: str, user_message: str):\n    messages = [{\"role\": \"user\", \"content\": user_message}]\n\n    response = openai.chat.completions.create(\n        model=\"gpt-4\",\n        messages=messages,\n        tools=[honcho_tool]\n    )\n\n    # Handle tool calls\n    if response.choices[0].message.tool_calls:\n        for tool_call in response.choices[0].message.tool_calls:\n            if tool_call.function.name == \"query_user_context\":\n                import json\n                args = json.loads(tool_call.function.arguments)\n                result = handle_honcho_tool_call(user_id, args[\"query\"])\n                # Continue conversation with tool result...\n```\n\n**TypeScript (OpenAI function calling):**\n\n```typescript\nimport OpenAI from 'openai';\nimport { Honcho } from '@honcho-ai/sdk';\n\nconst honcho = new Honcho({\n    workspaceId: \"my-app\",\n    apiKey: process.env.HONCHO_API_KEY\n});\n\nconst honchoTool: OpenAI.ChatCompletionTool = {\n    type: \"function\",\n    function: {\n        name: \"query_user_context\",\n        description: \"Query Honcho to retrieve relevant context about the user based on their history and preferences.\",\n        parameters: {\n            type: \"object\",\n            properties: {\n                query: {\n                    type: \"string\",\n                    description: \"A natural language question about the user\"\n                }\n            },\n            required: [\"query\"]\n        }\n    }\n};\n\nasync function handleHonchoToolCall(userId: string, query: string): Promise<string> {\n    const peer = await honcho.peer(userId);\n    return await peer.chat(query);\n}\n```\n\n## Pattern B: Pre-fetch Context with Targeted Queries\n\nFor simpler integrations, fetch user context before the LLM call using pre-defined queries.\n\n**Python:**\n\n```python\ndef get_user_context_for_prompt(user_id: str) -> dict:\n    \"\"\"Fetch key user attributes via targeted Honcho queries.\"\"\"\n    peer = honcho.peer(user_id)\n\n    return {\n        \"communication_style\": peer.chat(\"What communication style does this user prefer? Be concise.\"),\n        \"expertise_level\": peer.chat(\"What is this user's technical expertise level? Be concise.\"),\n        \"current_goals\": peer.chat(\"What are this user's current goals or priorities? Be concise.\"),\n        \"preferences\": peer.chat(\"What key preferences should I know about this user? Be concise.\")\n    }\n\ndef build_system_prompt(user_context: dict) -> str:\n    return f\"\"\"You are a helpful assistant. Here's what you know about this user:\n\nCommunication style: {user_context['communication_style']}\nExpertise level: {user_context['expertise_level']}\nCurrent goals: {user_context['current_goals']}\nKey preferences: {user_context['preferences']}\n\nTailor your responses accordingly.\"\"\"\n```\n\n**TypeScript:**\n\n```typescript\nasync function getUserContextForPrompt(userId: string): Promise<Record<string, string>> {\n    const peer = await honcho.peer(userId);\n\n    const [style, expertise, goals, preferences] = await Promise.all([\n        peer.chat(\"What communication style does this user prefer? Be concise.\"),\n        peer.chat(\"What is this user's technical expertise level? Be concise.\"),\n        peer.chat(\"What are this user's current goals or priorities? Be concise.\"),\n        peer.chat(\"What key preferences should I know about this user? Be concise.\")\n    ]);\n\n    return {\n        communicationStyle: style,\n        expertiseLevel: expertise,\n        currentGoals: goals,\n        preferences: preferences\n    };\n}\n```\n\n## Pattern C: Get Context for LLM Integration\n\nUse `context()` for conversation history with built-in LLM formatting. This is a near-instant read so it's the cheapest way to ground each turn.\n\n**Python:**\n\n```python\nimport openai\n\nsession = honcho.session(\"conversation-123\")\nuser = honcho.peer(\"user-123\")\nassistant = honcho.peer(\"assistant\")\n\n# Get context formatted for your LLM\ncontext = session.context(\n    tokens=2000,\n    peer_target=user.id,  # Include representation of this user\n    summary=True           # Include conversation summaries\n)\n\n# Convert to OpenAI format\nmessages = context.to_openai(assistant=assistant)\n\n# Or Anthropic format\n# messages = context.to_anthropic(assistant=assistant)\n\n# Add the new user message\nmessages.append({\"role\": \"user\", \"content\": \"What should I focus on today?\"})\n\nresponse = openai.chat.completions.create(\n    model=\"gpt-4\",\n    messages=messages\n)\n\n# Store the exchange\nsession.add_messages([\n    user.message(\"What should I focus on today?\"),\n    assistant.message(response.choices[0].message.content)\n])\n```\n\n**TypeScript:**\n\n```typescript\nimport OpenAI from 'openai';\n\nconst session = await honcho.session(\"conversation-123\");\nconst user = await honcho.peer(\"user-123\");\nconst assistant = await honcho.peer(\"assistant\");\n\n// Get context formatted for your LLM\nconst context = await session.context({\n    tokens: 2000,\n    peerTarget: user.id,  // Include representation of this user\n    summary: true          // Include conversation summaries\n});\n\n// Convert to OpenAI format\nconst messages = context.toOpenAI(assistant);\n\n// Or Anthropic format\n// const messages = context.toAnthropic(assistant);\n\n// Add the new user message\nmessages.push({ role: \"user\", content: \"What should I focus on today?\" });\n\nconst openai = new OpenAI();\nconst response = await openai.chat.completions.create({\n    model: \"gpt-4\",\n    messages\n});\n\n// Store the exchange\nawait session.addMessages([\n    user.message(\"What should I focus on today?\"),\n    assistant.message(response.choices[0].message.content!)\n]);\n```\n\n### What `context()` returns\n\n`session.context()` bundles the session-local view you can drop straight into an LLM call:\n\n- **Recent messages** from the session, trimmed to the `tokens` budget.\n- **Conversation summaries** when `summary=True` — the two-tier short/long summaries so older turns still count without spending the full token budget.\n- **The target peer's representation** when you pass `peer_target` — Honcho's synthesized understanding of that user, folded in. Omit it and you get session-local context only (no cross-session memory).\n\nThe `to_openai()` / `to_anthropic()` helpers format all of that as a `messages` array for the respective provider. Pass your `assistant` peer so its turns are tagged as the assistant role.\n\n## Streaming Responses\n\n```python\nstream = peer.chat_stream(\"What do we know about this user?\")\n\nfor chunk in stream:\n    print(chunk, end=\"\", flush=True)\n```\n\n```typescript\nconst stream = await peer.chatStream(\"What do we know about this user?\");\n\nfor await (const chunk of stream) {\n    process.stdout.write(chunk);\n}\n```\n","skills/honcho-integration/references/bot-frameworks.md":"# Honcho Integration for Bot Frameworks\n\nThis reference extends the main honcho-integration skill for **bot frameworks** — applications built around an agent loop, session manager, tool registry, and message bus (e.g., nanobot, openclaw, picoclaw).\n\n## Supported Frameworks\n\nWhen a known framework is detected, use concrete reference implementations from `{baseDir}/references/bot-frameworks/<framework>/`.\n\n| Framework | Status | Reference Dir |\n|-----------|--------|---------------|\n| [nanobot](https://github.com/HKUDS/nanobot) | concrete references | `bot-frameworks/nanobot/` |\n| openclaw | planned | -- |\n| picoclaw | planned | -- |\n\nFor unknown frameworks, adapt the general pattern below to the codebase's architecture.\n\n## Phase 1: Explore (bot-specific)\n\nIn addition to the main skill's Phase 1, identify these bot-specific components:\n\n1. **Agent loop**: Where messages are processed (look for `while` loops calling an LLM)\n2. **Session manager**: How conversation history is stored (JSONL files, database, in-memory)\n3. **Tool registry**: How tools/functions are registered for the LLM to call\n4. **Message bus**: How inbound/outbound messages are routed between channels and the agent\n5. **Config system**: How the bot loads configuration (JSON, YAML, env vars, pydantic models, zod schemas)\n6. **CLI entry points**: How the bot is started (commands, gateway, agent modes)\n\nIf the framework matches a known one (e.g., nanobot), pull the concrete references from `{baseDir}/references/bot-frameworks/<framework>/` and use them as the implementation target.\n\n## Phase 2: Interview (bot-specific)\n\nIn addition to the main skill's interview questions, ask about:\n\n- **Peer model**: Who are the participants? (typically: one user peer per channel:chat_id, one shared assistant peer)\n- **Session granularity**: One session per chat? Per user? Per channel?\n- **Workspace ID**: What namespace for this bot's Honcho data?\n- **Feature flag**: Should Honcho be opt-in (default `false`) or opt-out (default `true`)?\n\n## Phase 3: Implement (bot-specific)\n\n### Step 1: Add dependency\n\n**Python:** Add `honcho-ai>=2.0.1`. If the framework supports optional dependencies, make it optional:\n\n```toml\n[project.optional-dependencies]\nhoncho = [\"honcho-ai>=2.0.1\"]\n```\n\n**TypeScript:** Add `@honcho-ai/sdk`:\n\n```bash\nbun add @honcho-ai/sdk\n# or npm install @honcho-ai/sdk\n```\n\nIf the framework supports optional peer dependencies:\n\n```json\n{\n  \"peerDependencies\": {\n    \"@honcho-ai/sdk\": \">=2.0.1\"\n  },\n  \"peerDependenciesMeta\": {\n    \"@honcho-ai/sdk\": { \"optional\": true }\n  }\n}\n```\n\n### Step 2: Add config schema\n\nAdd a Honcho config section to the bot's configuration system:\n\n**Python:**\n\n```python\nclass HonchoConfig(BaseModel):\n    \"\"\"Honcho AI-native memory integration (optional feature flag).\"\"\"\n    enabled: bool = False  # or True for Honcho-first deployments\n    workspace_id: str = \"default\"\n    prefetch: bool = True  # inject user context into system prompts\n    context_tokens: int | None = None\n    environment: str = \"production\"\n```\n\n**TypeScript:**\n\n```typescript\ninterface HonchoConfig {\n  /** Honcho AI-native memory integration (optional feature flag). */\n  enabled: boolean;        // default: false, or true for Honcho-first deployments\n  workspaceId: string;     // default: \"default\"\n  prefetch: boolean;       // default: true — inject user context into system prompts\n  contextTokens?: number;\n  environment: string;     // default: \"production\"\n}\n\nconst defaultHonchoConfig: HonchoConfig = {\n  enabled: false,\n  workspaceId: \"default\",\n  prefetch: true,\n  environment: \"production\",\n};\n```\n\n### Step 3: Create the honcho package\n\nCreate a honcho integration package with:\n\n- **Client singleton** (`client.py` / `client.ts`): Lazy initialization, deferred imports, `getHonchoClient()` factory\n- **Session manager** (`session.py` / `session.ts`): Maps bot sessions to Honcho sessions with peer configuration\n- **Agent tool** (`honcho_tool.py` / `honchoTool.ts`): Tool the agent can call to query user context via `peer.chat()`\n\nKey patterns (Python):\n\n- `from __future__ import annotations` + `TYPE_CHECKING` for all honcho imports\n- Runtime imports inside functions (never top-level) so the bot doesn't crash without `honcho-ai`\n- Wrap in `try/except ImportError` for graceful degradation\n\nKey patterns (TypeScript):\n\n- Use dynamic `import()` for honcho SDK (never top-level `import ... from`) so the bot doesn't crash without `@honcho-ai/sdk`\n- Use `import type { ... }` for type-only imports that are erased at runtime\n- Wrap in `try/catch` for graceful degradation when the SDK is missing\n\nKey patterns (shared):\n\n- IDs sanitized to `^[a-zA-Z0-9_-]+` (Honcho requirement)\n- User peer: `observe_me=True, observe_others=True`\n- Assistant/bot peer: `observe_others=True`; set `observe_me=False` only for deterministic bots (scripted output — nothing to model). AI-assistant bots can keep `observe_me=True`.\n\nIf references exist for this framework, use them directly from `{baseDir}/references/bot-frameworks/<framework>/`.\n\n### Step 4: Wire into the agent loop\n\nAdd these integration points to the agent loop:\n\n1. **Tool registration** (at startup): If `honcho.enabled` and `HONCHO_API_KEY` set, initialize client + register Honcho tools.\n\n   **Python:** Wrap in `try/except ImportError` for graceful degradation.\n   **TypeScript:** Use dynamic `import()` inside a `try/catch` block.\n\n2. **Context setup** (per message): Set session context on Honcho tools, ensure Honcho session exists.\n\n3. **Prefetch** (per message): Call `session.context()` to get user representation and inject into system prompt before the LLM call.\n\n4. **Sync** (after response): After saving to local session, sync the user+assistant message pair to Honcho.\n\n   **Python:**\n\n   ```python\n   session.add_messages([\n       user_peer.message(user_input),\n       assistant_peer.message(assistant_response),\n   ])\n   ```\n\n   **TypeScript:**\n\n   ```typescript\n   await session.addMessages([\n       userPeer.message(userInput),\n       assistantPeer.message(assistantResponse),\n   ]);\n   ```\n\n5. **Migration** (on first activation): If Honcho session is empty but local session has history, upload prior messages as a file via `session.upload_file()` (Python) or `session.uploadFile()` (TypeScript). Also upload `MEMORY.md` and `HISTORY.md` if they exist (from frameworks with local memory consolidation). Archive originals after successful upload.\n\n### Step 5: Pass config through CLI\n\nPass `honcho_config` to every agent loop instantiation in the CLI commands.\n\n### Step 6: Migration support\n\nWhen Honcho activates on an instance with existing local data, migrate automatically:\n\n- **Session messages** (JSONL files): Format as XML transcript, upload via `session.upload_file()` (Python) or `session.uploadFile()` (TypeScript)\n- **Consolidated memory** (MEMORY.md, HISTORY.md): Upload as tagged files with context annotations\n- **Archive originals**: Move to `migrated/` subdirectory after successful upload\n- **Idempotent**: Skip if Honcho session already has messages\n\n## Phase 4: Verify (bot-specific)\n\nAfter integration, verify:\n\n- [ ] Bot starts normally without the Honcho SDK installed (no import errors)\n- [ ] Bot starts normally with the SDK but without `HONCHO_API_KEY` (graceful skip)\n- [ ] With both present and `enabled=true`, logs show \"Honcho tools registered\"\n- [ ] User context is prefetched and visible in system prompts\n- [ ] Messages sync to Honcho after each exchange\n- [ ] Local session migration works on first Honcho activation\n- [ ] Memory file migration works for MEMORY.md/HISTORY.md (if applicable)\n\n## Bot-Specific Patterns\n\n- **Lazy imports everywhere**:\n  - Python: `from __future__ import annotations` + `TYPE_CHECKING` for type hints, runtime imports inside functions\n  - TypeScript: `import type { ... }` for type-only imports, dynamic `import()` for runtime access\n- **Feature flag gating**: Always check `config.enabled` AND `HONCHO_API_KEY` / `process.env.HONCHO_API_KEY` before touching Honcho\n- **Graceful degradation**:\n  - Python: `try/except ImportError` and generic `Exception` catches with logger warnings, never crash the bot\n  - TypeScript: `try/catch` around dynamic `import()` with logger warnings, never crash the bot\n- **Sanitize IDs**: Honcho requires `^[a-zA-Z0-9_-]+` — replace colons, dots, spaces with dashes\n- **Sync after success**: Only mark messages as synced after the API call succeeds, not before\n- **Cache consistency**: When creating aliased sessions, store under both original and derived keys\n"},"items":[{"name":"instructions.md","path":"mcp/instructions.md","rawUrl":"https://raw.githubusercontent.com/plastic-labs/honcho/HEAD/mcp/instructions.md","title":"mcp - Project Instructions & Agent Workflow","category":"root-instruction","format":"markdown","content":"# Honcho MCP Server — Instructions\n\nHoncho keeps a model of the people and agents in a conversation (\"peers\") and answers questions about them. Two modes use this connection, and they need different tools.\n\n- **Recall** — read what Honcho already knows: `chat`, `workspace_chat`, `search`, `get_representation`, `get_peer_context`, `list_conclusions`. Nothing is written. This is the usual case when the user's own application writes to Honcho and this conversation only reads from it.\n- **Memory store** — this conversation is itself what Honcho learns from, because the user asked for it to be remembered or set Honcho up as this assistant's memory. Adds `create_session`, `create_peer`, `add_peers_to_session`, and `add_messages_to_session`.\n\nIf the user hasn't asked for this conversation to be recorded, stay in recall mode.\n\n## Setting up a session\n\nIn memory-store mode, a session holds the conversation and the peers in it:\n\n```\ncreate_session\n  workspace_id: \"<workspace-id>\"\n  session_id: \"<unique-id>\"\n```\n\nThen add peers to the session:\n\n```\ncreate_peer\n  workspace_id: \"<workspace-id>\"\n  peer_id: \"<user-name>\"\n\ncreate_peer\n  workspace_id: \"<workspace-id>\"\n  peer_id: \"Assistant\"\n\nadd_peers_to_session\n  workspace_id: \"<workspace-id>\"\n  session_id: \"<session_id>\"\n  peers:\n    - peer_id: \"<user-name>\"\n      observe_me: true\n      observe_others: true\n    - peer_id: \"Assistant\"\n      observe_me: false\n      observe_others: true\n```\n\nReuse that `session_id` for the rest of the conversation.\n\n## Recording messages\n\n`add_messages_to_session` appends messages to a tracked session:\n\n```\nadd_messages_to_session\n  workspace_id: \"<workspace-id>\"\n  session_id: \"<session_id>\"\n  messages:\n    - peer_id: \"<user-name>\"\n      content: \"<the user's message>\"\n    - peer_id: \"Assistant\"\n      content: \"<your response>\"\n```\n\nRecord both sides of each exchange as it happens. Honcho can only learn from\nmessages it has, and gaps in a session produce a weaker representation than a\ncomplete one.\n\n## Asking what Honcho knows\n\n`chat` answers a question about one peer, grounded in everything Honcho has learned across that peer's conversations:\n\n```\nchat\n  workspace_id: \"<workspace-id>\"\n  peer_id: \"Assistant\"\n  query: \"What communication style does this user prefer?\"\n  target_peer_id: \"<user-name>\"\n  session_id: \"<session_id>\"\n```\n\nIt runs live reasoning and takes a few seconds, so reach for it when the answer would change your response. `workspace_chat` does the same across all peers.\n\nQuestions it answers well:\n\n- \"What does this message reveal about the user's communication preferences?\"\n- \"How formal or casual should I be?\"\n- \"What is the user really asking for beyond their explicit question?\"\n- \"What emotional state might the user be in right now?\"\n\n---\n\n## Best Practices\n\n- **Group messages into coherent context buckets** — give each distinct context its own `session_id` (a chat thread, a project, a channel) and reuse that same `session_id` for every turn within it, rather than minting a new one per turn. Honcho reasons over the messages in a session together, so keeping a context's messages in one bucket produces a coherent representation; scattering them across sessions fragments it.\n- **Use one stable `peer_id` per real person**, reused across every session and channel. A fresh or per-channel ID (`user-web` vs. `user-discord`) builds separate, weaker representations instead of one.\n- **`observe_me: false` skips building a model of a peer** — reserve it for deterministic bots (nothing meaningful to model). For a real AI assistant it's fine to leave observation on.\n- **Reasoning is asynchronous** — don't poll or wait for it to finish before responding. A brand-new or low-volume peer legitimately has little to show yet.\n- **Reach for reads before `chat`** — `get_session_context` / `get_peer_context` / `get_representation` / `search` are near-instant; `chat` runs live reasoning and takes a few seconds. Use `chat` only when you need a reasoned answer.\n\n---\n\n## Key Concepts\n\n### Peers\n\nA **peer** is any participant — human or AI. Each peer has a unique ID within the workspace.\n\n### Sessions\n\nA **session** is a conversation context. Sessions track message history, manage which peers participate, and provide context retrieval for LLMs.\n\n### Conclusions\n\n**Conclusions** are facts and observations that Honcho derives from conversations. They power the representation — Honcho's understanding of a peer.\n\nEvery conclusion carries its own attribution. `level` says how it was reached: `explicit` conclusions are extracted straight from messages, while `deductive`, `inductive` and `contradiction` conclusions are derived while dreaming. `source_ids` names the conclusions a derived one was built from, and is null for explicit ones. `times_derived` counts how many times Honcho independently reached the same conclusion — a rough confidence signal.\n\nThose fields make the reasoning tree walkable in both directions: `get_conclusions` on a conclusion's `source_ids` steps down toward the explicit facts it rests on, and `get_derived_conclusions` steps up to whatever was built on top of it. Walk down before correcting a fact, and up before deleting one.\n\n### Evidence\n\n`chat` and `workspace_chat` accept `include_evidence`. With it, the answer arrives alongside the conclusions and messages the agent read and the tools it called. This is collated from what the agent actually accessed rather than reported by the model, so it over-reports — a listed conclusion was read, which is not proof the answer leaned on it. It costs no extra model tokens, so reach for it whenever an answer needs auditing.\n\n### Representations\n\nA **representation** is a formatted text summary built from a peer's conclusions. Query it with `get_representation` or `chat`.\n\n### Peer Cards\n\nA **peer card** is a compact list of biographical facts about a peer, automatically maintained by Honcho (or manually via `set_peer_card`).\n\n### Reasoning Level\n\nSeveral tools accept an optional `reasoning_level` parameter (`minimal`, `low`, `medium`, `high`, `max`). Higher levels produce more thorough answers but take longer and cost more. Default is `low`. Use `minimal` for the fastest lookups; use `high` or `max` when depth matters.\n\n### Dreams\n\nA **dream** is a background memory-consolidation process. It reviews conclusions, merges redundancies, and generates higher-level insights. Schedule one with `schedule_dream` after long conversations.\n","isInternal":false,"tokens":1440,"sizeBytes":6485},{"name":"SKILL.md","path":"examples/zo/SKILL.md","rawUrl":"https://raw.githubusercontent.com/plastic-labs/honcho/HEAD/examples/zo/SKILL.md","title":"honcho-memory","category":"anthropic-skill","format":"markdown","content":"---\nname: honcho-memory\ndescription: Gives AI agents persistent memory across conversations using Honcho. Automatically saves and retrieves user context so the AI remembers preferences, history, and facts between sessions. Use when you need the AI to remember past conversations, recall what a user has told it, inject relevant context into prompts, or manage separate memory spaces for different topics.\nlicense: AGPL-3.0\ncompatibility: Requires Python 3.9+, honcho-ai>=2.1.0, and a Honcho API key from honcho.dev. Set HONCHO_API_KEY and optionally HONCHO_WORKSPACE_ID in your environment.\nmetadata:\n  author: plastic-labs\n  version: \"0.1.0\"\n  honcho-sdk: \"2.1.0\"\n---\n\n# Honcho Memory Skill\n\nThis skill provides three tools for storing and retrieving AI memory using [Honcho](https://honcho.dev).\n\n## Setup\n\n1. Get a Honcho API key at [honcho.dev](https://honcho.dev).\n2. Set environment variables:\n\n   ```\n   HONCHO_API_KEY=your-api-key\n   HONCHO_WORKSPACE_ID=default   # optional, defaults to \"default\"\n   ```\n\n3. Install dependencies:\n\n   ```\n   pip install honcho-ai python-dotenv\n   ```\n\n## Tools\n\n### `save_memory`\n\nSaves a conversation turn (user or assistant message) to Honcho.\n\n**When to use:** After every message exchange to build up the user's memory.\n\n```python\nfrom tools.save_memory import save_memory\n\nsave_memory(\n    user_id=\"alice\",           # unique user identifier\n    content=\"I love hiking\",   # message text\n    role=\"user\",               # \"user\" or \"assistant\"\n    session_id=\"chat-1\",       # conversation session ID\n    assistant_id=\"assistant\"   # optional: assistant peer ID (default: \"assistant\")\n)\n```\n\n### `query_memory`\n\nAsks a natural language question against stored memory using Honcho's Dialectic API.\n\n**When to use:** When the user asks \"do you remember...?\", or when you need to recall facts about the user before responding.\n\n```python\nfrom tools.query_memory import query_memory\n\nanswer = query_memory(\n    user_id=\"alice\",\n    query=\"What are Alice's hobbies?\",\n    session_id=\"chat-1\"   # optional: scope to a session\n)\n# Returns: \"Alice enjoys hiking.\"\n```\n\n### `get_context`\n\nRetrieves recent conversation history formatted for direct use in an LLM API call.\n\n**When to use:** At the start of each LLM call to inject relevant context from past conversations.\n\n```python\nfrom tools.get_context import get_context\n\nmessages = get_context(\n    user_id=\"alice\",\n    session_id=\"chat-1\",\n    assistant_id=\"assistant\",\n    tokens=4000              # max tokens to include\n)\n# Returns: [{\"role\": \"user\", \"content\": \"...\"}, ...]\n```\n\n## Concept Mapping\n\n| Zo Computer | Honcho |\n|---|---|\n| Account | Workspace |\n| User | Peer |\n| Conversation | Session |\n| Message | Message |\n\n## Example: Full Conversation Flow\n\n```python\nfrom tools.save_memory import save_memory\nfrom tools.query_memory import query_memory\nfrom tools.get_context import get_context\n\nuser_id = \"alice\"\nsession_id = \"session-1\"\n\n# 1. Save user message\nsave_memory(user_id, \"I'm learning Rust and love rock climbing\", \"user\", session_id)\n\n# 2. Save assistant reply\nsave_memory(user_id, \"That's great! Both require patience.\", \"assistant\", session_id)\n\n# 3. In a later session, recall what you know\nprint(query_memory(user_id, \"What does Alice do in her free time?\"))\n# → \"Alice is learning Rust and enjoys rock climbing.\"\n\n# 4. Get context window for next LLM call\nmessages = get_context(user_id, session_id, \"assistant\", tokens=4000)\n```\n","frontmatter":{"name":"honcho-memory","description":"Gives AI agents persistent memory across conversations using Honcho. Automatically saves and retrieves user context so the AI remembers preferences, history, and facts between sessions. Use when you need the AI to remember past conversations, recall what a user has told it, inject relevant context into prompts, or manage separate memory spaces for different topics.","license":"AGPL-3.0","compatibility":"Requires Python 3.9+, honcho-ai>=2.1.0, and a Honcho API key from honcho.dev. Set HONCHO_API_KEY and optionally HONCHO_WORKSPACE_ID in your environment.","metadata":{"author":"plastic-labs","version":"0.1.0","honcho-sdk":"2.1.0"}},"isInternal":false,"tokens":840,"sizeBytes":3451},{"name":"SKILL.md","path":"skills/honcho-cli/SKILL.md","rawUrl":"https://raw.githubusercontent.com/plastic-labs/honcho/HEAD/skills/honcho-cli/SKILL.md","title":"honcho-cli","category":"anthropic-skill","format":"markdown","content":"---\nname: honcho-cli\ndescription: Inspect and debug Honcho workspaces via the `honcho` CLI. Use when investigating peer representations, memory state, session context, or dialectic quality — any task that requires introspection of a Honcho deployment, including verifying that a recall/record memory loop is actually working.\nallowed-tools: Bash(honcho:*), Bash(jq:*), Read, Grep\n---\n\n# Honcho CLI\n\n`honcho` wraps the Honcho Python SDK with agent-friendly defaults: JSON output, structured errors, input validation. Use it to inspect workspace state, debug peer memory, and diagnose the dialectic.\n\n## Output & config\n\n- **TTY**: human-readable tables (default when interactive)\n- **Piped / `--json`**: JSON — collection commands emit arrays, single-resource commands emit objects\n- **Exit codes**: `0` success · `1` client error (bad input, not found) · `2` server error · `3` auth error\n- **Config**: `~/.honcho/config.json` (shared with other Honcho tools). The CLI owns `apiKey` and `environmentUrl` at the top level; run `honcho init` to confirm or set them. Per-command scope (workspace / peer / session) is via `-w` / `-p` / `-s` flags or `HONCHO_*` env vars.\n\n## Command groups\n\n- `honcho config` — CLI configuration\n- `honcho start` / `stop` / `status` — local Docker stack (does not change `environmentUrl`). First start pins the Honcho image digest and writes `config.toml` into the profile. Pass `--setup basic` or `--setup advanced` for an interactive config wizard (TTY only; writes `.env` overrides). `honcho status` lists every profile; pass `--profile` for one.\n- `honcho workspace` — inspect, delete, search, chat\n- `honcho peer` — inspect, card, chat, search\n- `honcho session` — inspect, view (transcript), context, summaries\n- `honcho scope` — list, create, inspect, sessions, add-sessions, remove-session, status\n- `honcho message` — list and get\n- `honcho conclusion` — list, search, create, delete\n\n## Rules\n\n- Always pass `--json` when processing output programmatically.\n- Run `honcho peer inspect` before `honcho peer chat` to understand context.\n- Use `honcho session context` to see exactly what an agent receives.\n- Never run `honcho workspace delete` without `honcho workspace inspect` first.\n- Compare peer card with conclusions to understand memory state.\n- After `honcho scope add-sessions`, check `honcho scope status <name>` before a scoped `chat`: a `pending` backfill means the scope has not caught up yet, not that there is nothing to recall.\n- `honcho start` does not rewrite `environmentUrl`. Use `HONCHO_BASE_URL=http://127.0.0.1:8000` to talk to local stack.\n\n## Inspection tour\n\nWhen orienting to a Honcho deployment, walk outside-in:\n\n### 1. Understand the workspace\n\n```bash\nhoncho workspace inspect --json\n```\n\n### 2. Find the peer\n\n```bash\nhoncho peer list --json\nhoncho peer inspect <peer_id> --json\n```\n\n### 3. Check peer's memory\n\n```bash\nhoncho peer card <peer_id> --json\nhoncho conclusion list --observer <peer_id> --json\nhoncho conclusion search \"topic\" --observer <peer_id> --json\n```\n\n### 4. Debug a session\n\n```bash\nhoncho session inspect <session_id> --json\nhoncho session view <session_id> --last 20 --json\nhoncho session view <session_id> --page 2 --size 50 --json\nhoncho message list <session_id> --last 20 --json\nhoncho session context <session_id> --json\nhoncho session summaries <session_id> --json\n```\n\n### 5. Search across workspace\n\n```bash\nhoncho workspace search \"query\" --json\nhoncho workspace chat \"what themes show up across peers?\" --json\nhoncho workspace chat \"what happened in this project?\" --scope my-project --json\nhoncho peer search <peer_id> \"query\" --json\n```\n\n### 6. Bound recall with a scope\n\n```bash\nhoncho scope list --json\nhoncho scope inspect <name> --json                       # members + backfill summary\nhoncho scope create <name> --sessions <id1>,<id2> --json\nhoncho scope status <name> --json                        # wait until nothing is pending\nhoncho workspace chat \"what happened here?\" --scope <name> --json\n```\n\n## Debugging playbook\n\n### Peer not learning?\n\n```bash\n# Is observation enabled?\nhoncho peer inspect <peer_id> --json | jq '.configuration'\n\n# What conclusions exist?\nhoncho conclusion list --observer <peer_id> --json\nhoncho conclusion search \"expected topic\" --observer <peer_id> --json\n```\n\n### Session context looks wrong?\n\n```bash\n# Raw context an agent would receive\nhoncho session context <session_id> --json\n\n# Summaries feeding the context\nhoncho session summaries <session_id> --json\n\n# Recent message history\nhoncho message list <session_id> --last 50 --json\n```\n\n### Dialectic giving bad answers?\n\n```bash\n# What the peer card says\nhoncho peer card <peer_id> --json\n\n# Conclusions on the specific topic\nhoncho conclusion search \"topic\" --observer <peer_id> --json\n\n# Exercise the dialectic directly\nhoncho peer chat <peer_id> \"what do you know about X?\" --json\nhoncho peer chat <peer_id> \"what happened here?\" --scope my-project --json\nhoncho workspace chat \"what themes show up across peers?\" --json\n```\n","frontmatter":{"name":"honcho-cli","description":"Inspect and debug Honcho workspaces via the `honcho` CLI. Use when investigating peer representations, memory state, session context, or dialectic quality — any task that requires introspection of a Honcho deployment, including verifying that a recall/record memory loop is actually working.","allowed-tools":"Bash(honcho:*), Bash(jq:*), Read, Grep","allowedTools":["Bash(honcho:*),","Bash(jq:*),","Read,","Grep"]},"allowedTools":["Bash(honcho:*),","Bash(jq:*),","Read,","Grep"],"isInternal":false,"tokens":1291,"sizeBytes":5051},{"name":"agent-patterns.md","path":"skills/honcho-integration/references/agent-patterns.md","rawUrl":"https://raw.githubusercontent.com/plastic-labs/honcho/HEAD/skills/honcho-integration/references/agent-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Agent Access Patterns\n\nHow your AI accesses Honcho's user context. Pick based on the interview answer to Question Set 2. These build on the client/peer/session setup in `core-patterns.md`.\n\n- **Pattern A — Dialectic chat as a tool call**: the agent decides when to query context on-demand.\n- **Pattern B — Pre-fetch with targeted queries**: fetch a fixed set of attributes before each LLM call.\n- **Pattern C — `context()` for LLM integration**: inject conversation history + representation into the prompt.\n\n> **Speed note.** `chat()` runs live dialectic reasoning (a few seconds) — Patterns A and B call it. `context()` (Pattern C) is a near-instant read. Prefer `context()` for per-turn grounding; reach for `chat()` when you genuinely need a reasoned answer.\n\n## Pattern A: Dialectic Chat as a Tool Call\n\nMake Honcho's chat endpoint available as a **tool** for your AI agent. This lets the agent query user context on-demand.\n\n**Python (OpenAI function calling):**\n\n```python\nimport openai\nfrom honcho import Honcho\n\nhoncho = Honcho(workspace_id=\"my-app\", api_key=os.environ[\"HONCHO_API_KEY\"])\n\n# Define the tool for your agent\nhoncho_tool = {\n    \"type\": \"function\",\n    \"function\": {\n        \"name\": \"query_user_context\",\n        \"description\": \"Query Honcho to retrieve relevant context about the user based on their history and preferences. Use this when you need to understand the user's background, preferences, past interactions, or goals.\",\n        \"parameters\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"query\": {\n                    \"type\": \"string\",\n                    \"description\": \"A natural language question about the user, e.g. 'What are this user's main goals?' or 'What communication style does this user prefer?'\"\n                }\n            },\n            \"required\": [\"query\"]\n        }\n    }\n}\n\ndef handle_honcho_tool_call(user_id: str, query: str) -> str:\n    \"\"\"Execute the Honcho chat tool call.\"\"\"\n    peer = honcho.peer(user_id)\n    return peer.chat(query)\n\n# Use in your agent loop\ndef run_agent(user_id: str, user_message: str):\n    messages = [{\"role\": \"user\", \"content\": user_message}]\n\n    response = openai.chat.completions.create(\n        model=\"gpt-4\",\n        messages=messages,\n        tools=[honcho_tool]\n    )\n\n    # Handle tool calls\n    if response.choices[0].message.tool_calls:\n        for tool_call in response.choices[0].message.tool_calls:\n            if tool_call.function.name == \"query_user_context\":\n                import json\n                args = json.loads(tool_call.function.arguments)\n                result = handle_honcho_tool_call(user_id, args[\"query\"])\n                # Continue conversation with tool result...\n```\n\n**TypeScript (OpenAI function calling):**\n\n```typescript\nimport OpenAI from 'openai';\nimport { Honcho } from '@honcho-ai/sdk';\n\nconst honcho = new Honcho({\n    workspaceId: \"my-app\",\n    apiKey: process.env.HONCHO_API_KEY\n});\n\nconst honchoTool: OpenAI.ChatCompletionTool = {\n    type: \"function\",\n    function: {\n        name: \"query_user_context\",\n        description: \"Query Honcho to retrieve relevant context about the user based on their history and preferences.\",\n        parameters: {\n            type: \"object\",\n            properties: {\n                query: {\n                    type: \"string\",\n                    description: \"A natural language question about the user\"\n                }\n            },\n            required: [\"query\"]\n        }\n    }\n};\n\nasync function handleHonchoToolCall(userId: string, query: string): Promise<string> {\n    const peer = await honcho.peer(userId);\n    return await peer.chat(query);\n}\n```\n\n## Pattern B: Pre-fetch Context with Targeted Queries\n\nFor simpler integrations, fetch user context before the LLM call using pre-defined queries.\n\n**Python:**\n\n```python\ndef get_user_context_for_prompt(user_id: str) -> dict:\n    \"\"\"Fetch key user attributes via targeted Honcho queries.\"\"\"\n    peer = honcho.peer(user_id)\n\n    return {\n        \"communication_style\": peer.chat(\"What communication style does this user prefer? Be concise.\"),\n        \"expertise_level\": peer.chat(\"What is this user's technical expertise level? Be concise.\"),\n        \"current_goals\": peer.chat(\"What are this user's current goals or priorities? Be concise.\"),\n        \"preferences\": peer.chat(\"What key preferences should I know about this user? Be concise.\")\n    }\n\ndef build_system_prompt(user_context: dict) -> str:\n    return f\"\"\"You are a helpful assistant. Here's what you know about this user:\n\nCommunication style: {user_context['communication_style']}\nExpertise level: {user_context['expertise_level']}\nCurrent goals: {user_context['current_goals']}\nKey preferences: {user_context['preferences']}\n\nTailor your responses accordingly.\"\"\"\n```\n\n**TypeScript:**\n\n```typescript\nasync function getUserContextForPrompt(userId: string): Promise<Record<string, string>> {\n    const peer = await honcho.peer(userId);\n\n    const [style, expertise, goals, preferences] = await Promise.all([\n        peer.chat(\"What communication style does this user prefer? Be concise.\"),\n        peer.chat(\"What is this user's technical expertise level? Be concise.\"),\n        peer.chat(\"What are this user's current goals or priorities? Be concise.\"),\n        peer.chat(\"What key preferences should I know about this user? Be concise.\")\n    ]);\n\n    return {\n        communicationStyle: style,\n        expertiseLevel: expertise,\n        currentGoals: goals,\n        preferences: preferences\n    };\n}\n```\n\n## Pattern C: Get Context for LLM Integration\n\nUse `context()` for conversation history with built-in LLM formatting. This is a near-instant read so it's the cheapest way to ground each turn.\n\n**Python:**\n\n```python\nimport openai\n\nsession = honcho.session(\"conversation-123\")\nuser = honcho.peer(\"user-123\")\nassistant = honcho.peer(\"assistant\")\n\n# Get context formatted for your LLM\ncontext = session.context(\n    tokens=2000,\n    peer_target=user.id,  # Include representation of this user\n    summary=True           # Include conversation summaries\n)\n\n# Convert to OpenAI format\nmessages = context.to_openai(assistant=assistant)\n\n# Or Anthropic format\n# messages = context.to_anthropic(assistant=assistant)\n\n# Add the new user message\nmessages.append({\"role\": \"user\", \"content\": \"What should I focus on today?\"})\n\nresponse = openai.chat.completions.create(\n    model=\"gpt-4\",\n    messages=messages\n)\n\n# Store the exchange\nsession.add_messages([\n    user.message(\"What should I focus on today?\"),\n    assistant.message(response.choices[0].message.content)\n])\n```\n\n**TypeScript:**\n\n```typescript\nimport OpenAI from 'openai';\n\nconst session = await honcho.session(\"conversation-123\");\nconst user = await honcho.peer(\"user-123\");\nconst assistant = await honcho.peer(\"assistant\");\n\n// Get context formatted for your LLM\nconst context = await session.context({\n    tokens: 2000,\n    peerTarget: user.id,  // Include representation of this user\n    summary: true          // Include conversation summaries\n});\n\n// Convert to OpenAI format\nconst messages = context.toOpenAI(assistant);\n\n// Or Anthropic format\n// const messages = context.toAnthropic(assistant);\n\n// Add the new user message\nmessages.push({ role: \"user\", content: \"What should I focus on today?\" });\n\nconst openai = new OpenAI();\nconst response = await openai.chat.completions.create({\n    model: \"gpt-4\",\n    messages\n});\n\n// Store the exchange\nawait session.addMessages([\n    user.message(\"What should I focus on today?\"),\n    assistant.message(response.choices[0].message.content!)\n]);\n```\n\n### What `context()` returns\n\n`session.context()` bundles the session-local view you can drop straight into an LLM call:\n\n- **Recent messages** from the session, trimmed to the `tokens` budget.\n- **Conversation summaries** when `summary=True` — the two-tier short/long summaries so older turns still count without spending the full token budget.\n- **The target peer's representation** when you pass `peer_target` — Honcho's synthesized understanding of that user, folded in. Omit it and you get session-local context only (no cross-session memory).\n\nThe `to_openai()` / `to_anthropic()` helpers format all of that as a `messages` array for the respective provider. Pass your `assistant` peer so its turns are tagged as the assistant role.\n\n## Streaming Responses\n\n```python\nstream = peer.chat_stream(\"What do we know about this user?\")\n\nfor chunk in stream:\n    print(chunk, end=\"\", flush=True)\n```\n\n```typescript\nconst stream = await peer.chatStream(\"What do we know about this user?\");\n\nfor await (const chunk of stream) {\n    process.stdout.write(chunk);\n}\n```\n","isInternal":false,"tokens":1920,"sizeBytes":8666},{"name":"bot-frameworks.md","path":"skills/honcho-integration/references/bot-frameworks.md","rawUrl":"https://raw.githubusercontent.com/plastic-labs/honcho/HEAD/skills/honcho-integration/references/bot-frameworks.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Honcho Integration for Bot Frameworks\n\nThis reference extends the main honcho-integration skill for **bot frameworks** — applications built around an agent loop, session manager, tool registry, and message bus (e.g., nanobot, openclaw, picoclaw).\n\n## Supported Frameworks\n\nWhen a known framework is detected, use concrete reference implementations from `{baseDir}/references/bot-frameworks/<framework>/`.\n\n| Framework | Status | Reference Dir |\n|-----------|--------|---------------|\n| [nanobot](https://github.com/HKUDS/nanobot) | concrete references | `bot-frameworks/nanobot/` |\n| openclaw | planned | -- |\n| picoclaw | planned | -- |\n\nFor unknown frameworks, adapt the general pattern below to the codebase's architecture.\n\n## Phase 1: Explore (bot-specific)\n\nIn addition to the main skill's Phase 1, identify these bot-specific components:\n\n1. **Agent loop**: Where messages are processed (look for `while` loops calling an LLM)\n2. **Session manager**: How conversation history is stored (JSONL files, database, in-memory)\n3. **Tool registry**: How tools/functions are registered for the LLM to call\n4. **Message bus**: How inbound/outbound messages are routed between channels and the agent\n5. **Config system**: How the bot loads configuration (JSON, YAML, env vars, pydantic models, zod schemas)\n6. **CLI entry points**: How the bot is started (commands, gateway, agent modes)\n\nIf the framework matches a known one (e.g., nanobot), pull the concrete references from `{baseDir}/references/bot-frameworks/<framework>/` and use them as the implementation target.\n\n## Phase 2: Interview (bot-specific)\n\nIn addition to the main skill's interview questions, ask about:\n\n- **Peer model**: Who are the participants? (typically: one user peer per channel:chat_id, one shared assistant peer)\n- **Session granularity**: One session per chat? Per user? Per channel?\n- **Workspace ID**: What namespace for this bot's Honcho data?\n- **Feature flag**: Should Honcho be opt-in (default `false`) or opt-out (default `true`)?\n\n## Phase 3: Implement (bot-specific)\n\n### Step 1: Add dependency\n\n**Python:** Add `honcho-ai>=2.0.1`. If the framework supports optional dependencies, make it optional:\n\n```toml\n[project.optional-dependencies]\nhoncho = [\"honcho-ai>=2.0.1\"]\n```\n\n**TypeScript:** Add `@honcho-ai/sdk`:\n\n```bash\nbun add @honcho-ai/sdk\n# or npm install @honcho-ai/sdk\n```\n\nIf the framework supports optional peer dependencies:\n\n```json\n{\n  \"peerDependencies\": {\n    \"@honcho-ai/sdk\": \">=2.0.1\"\n  },\n  \"peerDependenciesMeta\": {\n    \"@honcho-ai/sdk\": { \"optional\": true }\n  }\n}\n```\n\n### Step 2: Add config schema\n\nAdd a Honcho config section to the bot's configuration system:\n\n**Python:**\n\n```python\nclass HonchoConfig(BaseModel):\n    \"\"\"Honcho AI-native memory integration (optional feature flag).\"\"\"\n    enabled: bool = False  # or True for Honcho-first deployments\n    workspace_id: str = \"default\"\n    prefetch: bool = True  # inject user context into system prompts\n    context_tokens: int | None = None\n    environment: str = \"production\"\n```\n\n**TypeScript:**\n\n```typescript\ninterface HonchoConfig {\n  /** Honcho AI-native memory integration (optional feature flag). */\n  enabled: boolean;        // default: false, or true for Honcho-first deployments\n  workspaceId: string;     // default: \"default\"\n  prefetch: boolean;       // default: true — inject user context into system prompts\n  contextTokens?: number;\n  environment: string;     // default: \"production\"\n}\n\nconst defaultHonchoConfig: HonchoConfig = {\n  enabled: false,\n  workspaceId: \"default\",\n  prefetch: true,\n  environment: \"production\",\n};\n```\n\n### Step 3: Create the honcho package\n\nCreate a honcho integration package with:\n\n- **Client singleton** (`client.py` / `client.ts`): Lazy initialization, deferred imports, `getHonchoClient()` factory\n- **Session manager** (`session.py` / `session.ts`): Maps bot sessions to Honcho sessions with peer configuration\n- **Agent tool** (`honcho_tool.py` / `honchoTool.ts`): Tool the agent can call to query user context via `peer.chat()`\n\nKey patterns (Python):\n\n- `from __future__ import annotations` + `TYPE_CHECKING` for all honcho imports\n- Runtime imports inside functions (never top-level) so the bot doesn't crash without `honcho-ai`\n- Wrap in `try/except ImportError` for graceful degradation\n\nKey patterns (TypeScript):\n\n- Use dynamic `import()` for honcho SDK (never top-level `import ... from`) so the bot doesn't crash without `@honcho-ai/sdk`\n- Use `import type { ... }` for type-only imports that are erased at runtime\n- Wrap in `try/catch` for graceful degradation when the SDK is missing\n\nKey patterns (shared):\n\n- IDs sanitized to `^[a-zA-Z0-9_-]+` (Honcho requirement)\n- User peer: `observe_me=True, observe_others=True`\n- Assistant/bot peer: `observe_others=True`; set `observe_me=False` only for deterministic bots (scripted output — nothing to model). AI-assistant bots can keep `observe_me=True`.\n\nIf references exist for this framework, use them directly from `{baseDir}/references/bot-frameworks/<framework>/`.\n\n### Step 4: Wire into the agent loop\n\nAdd these integration points to the agent loop:\n\n1. **Tool registration** (at startup): If `honcho.enabled` and `HONCHO_API_KEY` set, initialize client + register Honcho tools.\n\n   **Python:** Wrap in `try/except ImportError` for graceful degradation.\n   **TypeScript:** Use dynamic `import()` inside a `try/catch` block.\n\n2. **Context setup** (per message): Set session context on Honcho tools, ensure Honcho session exists.\n\n3. **Prefetch** (per message): Call `session.context()` to get user representation and inject into system prompt before the LLM call.\n\n4. **Sync** (after response): After saving to local session, sync the user+assistant message pair to Honcho.\n\n   **Python:**\n\n   ```python\n   session.add_messages([\n       user_peer.message(user_input),\n       assistant_peer.message(assistant_response),\n   ])\n   ```\n\n   **TypeScript:**\n\n   ```typescript\n   await session.addMessages([\n       userPeer.message(userInput),\n       assistantPeer.message(assistantResponse),\n   ]);\n   ```\n\n5. **Migration** (on first activation): If Honcho session is empty but local session has history, upload prior messages as a file via `session.upload_file()` (Python) or `session.uploadFile()` (TypeScript). Also upload `MEMORY.md` and `HISTORY.md` if they exist (from frameworks with local memory consolidation). Archive originals after successful upload.\n\n### Step 5: Pass config through CLI\n\nPass `honcho_config` to every agent loop instantiation in the CLI commands.\n\n### Step 6: Migration support\n\nWhen Honcho activates on an instance with existing local data, migrate automatically:\n\n- **Session messages** (JSONL files): Format as XML transcript, upload via `session.upload_file()` (Python) or `session.uploadFile()` (TypeScript)\n- **Consolidated memory** (MEMORY.md, HISTORY.md): Upload as tagged files with context annotations\n- **Archive originals**: Move to `migrated/` subdirectory after successful upload\n- **Idempotent**: Skip if Honcho session already has messages\n\n## Phase 4: Verify (bot-specific)\n\nAfter integration, verify:\n\n- [ ] Bot starts normally without the Honcho SDK installed (no import errors)\n- [ ] Bot starts normally with the SDK but without `HONCHO_API_KEY` (graceful skip)\n- [ ] With both present and `enabled=true`, logs show \"Honcho tools registered\"\n- [ ] User context is prefetched and visible in system prompts\n- [ ] Messages sync to Honcho after each exchange\n- [ ] Local session migration works on first Honcho activation\n- [ ] Memory file migration works for MEMORY.md/HISTORY.md (if applicable)\n\n## Bot-Specific Patterns\n\n- **Lazy imports everywhere**:\n  - Python: `from __future__ import annotations` + `TYPE_CHECKING` for type hints, runtime imports inside functions\n  - TypeScript: `import type { ... }` for type-only imports, dynamic `import()` for runtime access\n- **Feature flag gating**: Always check `config.enabled` AND `HONCHO_API_KEY` / `process.env.HONCHO_API_KEY` before touching Honcho\n- **Graceful degradation**:\n  - Python: `try/except ImportError` and generic `Exception` catches with logger warnings, never crash the bot\n  - TypeScript: `try/catch` around dynamic `import()` with logger warnings, never crash the bot\n- **Sanitize IDs**: Honcho requires `^[a-zA-Z0-9_-]+` — replace colons, dots, spaces with dashes\n- **Sync after success**: Only mark messages as synced after the API call succeeds, not before\n- **Cache consistency**: When creating aliased sessions, store under both original and derived keys\n","isInternal":false,"tokens":2044,"sizeBytes":8559},{"name":"core-patterns.md","path":"skills/honcho-integration/references/core-patterns.md","rawUrl":"https://raw.githubusercontent.com/plastic-labs/honcho/HEAD/skills/honcho-integration/references/core-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Core Integration Patterns\n\nThe base SDK boilerplate for any Honcho integration: choosing sync vs async, initializing the client, creating peers, configuring sessions, and adding messages. Read this once you've chosen your entities and session structure (Phases 1–2). For the agent-facing recall patterns (tool call, pre-fetch, `context()`), see `agent-patterns.md`.\n\n## Sync vs Async\n\n**TypeScript** — The SDK is async by default. All methods return promises. No separate sync API.\n\n**Python** — The SDK provides both sync and async interfaces:\n\n- **Sync** (default): `from honcho import Honcho` — use in sync frameworks (Flask, Django, CLI scripts)\n- **Async**: `from honcho import Honcho` with `.aio` namespace — use in async frameworks (FastAPI, Starlette, async workers)\n\n```python\n# Sync usage (Flask, Django, scripts)\nfrom honcho import Honcho\nhoncho = Honcho(workspace_id=\"my-app\", api_key=os.environ[\"HONCHO_API_KEY\"])\npeer = honcho.peer(\"user-123\")\nresponse = peer.chat(\"What does this user prefer?\")\n\n# Async usage (FastAPI, Starlette)\nfrom honcho import Honcho\nhoncho = Honcho(workspace_id=\"my-app\", api_key=os.environ[\"HONCHO_API_KEY\"])\npeer = await honcho.aio.peer(\"user-123\")\nresponse = await peer.aio.chat(\"What does this user prefer?\")\n```\n\nMatch the client to the framework — check whether the codebase uses `async def` handlers or sync `def` handlers and choose accordingly. The examples below show sync Python; swap to `.aio` equivalents for async codebases.\n\n## 1. Initialize with a Single Workspace\n\nUse ONE workspace for your entire application. The workspace name should reflect your app/product.\n\n**Python:**\n\n```python\nfrom honcho import Honcho\nimport os\n\n# Sync client (Flask, Django, scripts)\nhoncho = Honcho(\n    workspace_id=\"your-app-name\",\n    api_key=os.environ[\"HONCHO_API_KEY\"],\n    environment=\"production\"\n)\n\n# Async client (FastAPI, Starlette) — use honcho.aio for all operations\n# honcho.aio.peer(), honcho.aio.session(), etc.\n```\n\n**TypeScript:**\n\n```typescript\nimport { Honcho } from '@honcho-ai/sdk';\n\n// All methods are async by default\nconst honcho = new Honcho({\n    workspaceId: \"your-app-name\",\n    apiKey: process.env.HONCHO_API_KEY,\n    environment: \"production\"\n});\n```\n\n## 2. Create Peers for ALL Entities\n\nCreate peers for **every entity** in your business logic - users AND AI assistants.\n\n**Python:**\n\n```python\nfrom honcho.api_types import PeerConfig\n\n# Human users (observed by default)\nuser = honcho.peer(\"user-123\")\n\n# AI assistants can be observed too — leave observe_me on (the default) if you\n# want a model of the assistant.\nassistant = honcho.peer(\"assistant\")\n\n# Deterministic bots (scripted/rule-based) - set observe_me=False; there's\n# nothing meaningful for Honcho to model.\nnotification_bot = honcho.peer(\"notification-bot\", configuration=PeerConfig(observe_me=False))\n```\n\n**TypeScript:**\n\n```typescript\n// Human users (observed by default)\nconst user = await honcho.peer(\"user-123\");\n\n// AI assistants can be observed too — leave observeMe on (the default) if you\n// want a model of the assistant.\nconst assistant = await honcho.peer(\"assistant\");\n\n// Deterministic bots (scripted/rule-based) - set observeMe=false; there's\n// nothing meaningful for Honcho to model.\nconst notificationBot = await honcho.peer(\"notification-bot\", { configuration: { observeMe: false } });\n```\n\n## 3. Multi-Peer Sessions\n\nSessions can have multiple participants. Configure observation settings per-peer.\n\n**Python:**\n\n```python\nfrom honcho.api_types import SessionPeerConfig\n\nsession = honcho.session(\"conversation-123\")\n\n# User is observed (Honcho builds a model of them)\nuser_config = SessionPeerConfig(observe_me=True, observe_others=True)\n\n# A deterministic bot is NOT observed (no model built of it). An AI assistant\n# could stay observed instead — observe_me defaults to True.\nbot_config = SessionPeerConfig(observe_me=False, observe_others=True)\n\nsession.add_peers([\n    (user, user_config),\n    (notification_bot, bot_config)\n])\n```\n\n**TypeScript:**\n\n```typescript\nconst session = await honcho.session(\"conversation-123\");\n\nawait session.addPeers([\n    // A deterministic bot isn't observed; an AI assistant could stay observed\n    // instead (observeMe defaults to true).\n    [user, { observeMe: true, observeOthers: true }],\n    [notificationBot, { observeMe: false, observeOthers: true }]\n]);\n```\n\n## 4. Add Messages to Sessions\n\n**Python:**\n\n```python\nsession.add_messages([\n    user.message(\"I'm having trouble with my account\"),\n    assistant.message(\"I'd be happy to help. What seems to be the issue?\"),\n    user.message(\"I can't reset my password\")\n])\n```\n\n**TypeScript:**\n\n```typescript\nawait session.addMessages([\n    user.message(\"I'm having trouble with my account\"),\n    assistant.message(\"I'd be happy to help. What seems to be the issue?\"),\n    user.message(\"I can't reset my password\")\n]);\n```\n","isInternal":false,"tokens":1172,"sizeBytes":4890},{"name":"SKILL.md","path":"skills/honcho-integration/SKILL.md","rawUrl":"https://raw.githubusercontent.com/plastic-labs/honcho/HEAD/skills/honcho-integration/SKILL.md","title":"honcho-integration","category":"anthropic-skill","format":"markdown","content":"---\nname: honcho-integration\ndescription: Integrate Honcho memory into existing Python or TypeScript codebases. Use when adding Honcho SDK, setting up peers, configuring sessions, and accessing Honcho's representation.\nallowed-tools: Read, Glob, Grep, Bash(uv:*), Bash(bun:*), Bash(npm:*), Edit, Write, WebFetch, AskUserQuestion\n---\n\n# Honcho Integration Guide\n\n## What is Honcho\n\nHoncho is an open source memory library for building stateful agents. It works with any model, framework, or architecture. You send Honcho the messages from your conversations, and custom reasoning models process them in the background — extracting premises, drawing conclusions, and building rich representations of each participant over time. Your agent can then query those representations on-demand (\"What does this user care about?\", \"How technical is this person?\") and get grounded, reasoned answers.\n\nThe key mental model: **Peers** are any participant — human or AI. Both are represented the same way. `observe_me` is a peer-level flag (`PeerConfig`) controlling whether Honcho forms a representation of *that* peer; typically you want Honcho to model your users (`observe_me=True`) but not anything with deterministic behavior (`observe_me=False`). `observe_others` is a separate per-peer `SessionPeerConfig` setting that controls whether that peer forms representations of the *other* participants in a session. **Sessions** scope conversations between peers. **Messages** are the raw data you feed in — Honcho reasons about them asynchronously and stores the results as the peer's **representation**. No messages means no reasoning means no memory.\n\nYour agent accesses this memory through `peer.chat(query)` (ask a natural language question, get a reasoned answer — a few seconds of live reasoning) or `session.context()` (near-instant read of formatted history + representation). Prefer `context()` for per-turn grounding; use `chat()` when you need a reasoned answer.\n\n## Reference map\n\nFollow the workflow below. Read a reference file only when you reach the step that needs it:\n\n| When you're… | Read |\n| --- | --- |\n| Writing the client/peer/session setup (init, peers, sessions, add messages) | `references/core-patterns.md` |\n| Wiring how the AI reads context (tool call, pre-fetch, `context()`, streaming) | `references/agent-patterns.md` |\n| Integrating into a bot framework (nanobot, openclaw, picoclaw, …) | `references/bot-frameworks.md` + `references/bot-frameworks/<framework>/` |\n\n## Integration Workflow\n\nFollow these phases in order:\n\n### Phase 1: Codebase Exploration\n\nBefore asking the user anything, explore the codebase to understand:\n\n1. **Language & Framework**: Is this Python or TypeScript? What frameworks are used (FastAPI, Express, Next.js, etc.)?\n2. **Existing AI/LLM code**: Search for existing LLM integrations (OpenAI, Anthropic, LangChain, etc.)\n3. **Entity structure**: Identify users, agents, bots, or other entities that interact\n4. **Session/conversation handling**: How does the app currently manage conversations?\n5. **Message flow**: Where are messages sent/received? What's the request/response cycle?\n\nUse Glob and Grep to find:\n\n- `**/*.py` or `**/*.ts` files with \"openai\", \"anthropic\", \"llm\", \"chat\", \"message\"\n- User/session models or types\n- API routes handling chat or conversation endpoints\n\n> **Bot framework detected?** If the codebase is built around an agent loop, tool registry, session manager, and message bus (e.g., nanobot, openclaw, picoclaw), read `references/bot-frameworks.md` for framework-specific integration guidance and check `references/bot-frameworks/<framework>/` for concrete reference implementations.\n\n### Phase 2: Interview (REQUIRED)\n\nAfter exploring the codebase, use the **AskUserQuestion** tool to clarify integration requirements. Ask these questions (adapt based on what you learned in Phase 1):\n\n#### Question Set 1 - Entities & Peers\n\nAsk about which entities should be Honcho peers:\n\n- header: \"Peers\"\n- question: \"Which entities should Honcho track and build representations for?\"\n- options based on what you found (e.g., \"End users only\", \"Users + AI assistant\", \"Users + multiple AI agents\", \"All participants including third-party services\")\n- Include a follow-up if they have multiple AI agents: should any AI peers be observed?\n\n#### Question Set 2 - Integration Pattern\n\nAsk how they want to use Honcho context (see `references/agent-patterns.md` for the implementation of each):\n\n- header: \"Pattern\"\n- question: \"How should your AI access Honcho's user context?\"\n- options:\n  - \"Tool call (Recommended)\" - \"Agent queries Honcho on-demand via function calling\"\n  - \"Pre-fetch\" - \"Fetch user context before each LLM call with predefined queries\"\n  - \"context()\" - \"Include conversation history and representations in prompt\"\n  - \"Multiple patterns\" - \"Combine approaches for different use cases\"\n\n#### Question Set 3 - Session Structure\n\nAsk about conversation structure:\n\n- header: \"Sessions\"\n- question: \"How should conversations map to Honcho sessions?\"\n- options based on their app (e.g., \"One session per chat thread\", \"One session per user\", \"Multiple users per session (group chat)\", \"Custom session logic\")\n\n#### Question Set 4 - Specific Queries (if using pre-fetch pattern)\n\nIf they chose pre-fetch, ask what context matters:\n\n- header: \"Context\"\n- question: \"What user context should be fetched for the AI?\"\n- multiSelect: true\n- options: \"Communication style\", \"Expertise level\", \"Goals/priorities\", \"Preferences\", \"Recent activity summary\", \"Custom queries\"\n\n### Phase 3: Implementation\n\nBased on interview responses, implement the integration:\n\n1. Install the SDK (see [Installation](#installation))\n2. Create Honcho client initialization — `references/core-patterns.md` §1\n3. Set up peer creation for identified entities — `references/core-patterns.md` §2–3\n4. Implement the chosen integration pattern(s) — `references/agent-patterns.md`\n5. Add message storage after exchanges — `references/core-patterns.md` §4\n6. Update any existing conversation handlers\n\n### Phase 4: Verification\n\n- If the Honcho CLI is available, run `honcho doctor` to confirm connectivity before testing the integration code\n- Use `honcho peer list` and `honcho peer chat` to verify peers exist and the dialectic endpoint works independently of the integration\n- Ensure all message exchanges are stored to Honcho\n- Verify deterministic bot peers have `observe_me=False`; AI-assistant peers can keep observation on (it's fine to model them)\n- Check that the workspace ID is consistent across the codebase\n- Confirm environment variable for API key is documented\n\n---\n\n## Before You Start\n\n1. **Check the latest SDK versions** at <https://honcho.dev/docs/changelog/introduction.md>\n   - Python SDK: `honcho-ai`\n   - TypeScript SDK: `@honcho-ai/sdk`\n\n2. **Get an API key** ask the user to get a Honcho API key from <https://app.honcho.dev> and add it to the environment.\n\n3. **Verify with the CLI** (optional but recommended). If the user has the Honcho CLI installed (`uv install honcho-cli`), they can validate their setup before writing any integration code:\n\n   ```bash\n   honcho init          # persist API key + URL to ~/.honcho/config.json\n   honcho doctor        # verify connectivity, config, workspace health\n   honcho peer chat     # test the dialectic endpoint interactively\n   ```\n\n   This is the fastest way to confirm the API key and URL are correct before debugging SDK code.\n\n## Installation\n\n### Python (use uv)\n\n```bash\nuv add honcho-ai\n```\n\n### TypeScript (use bun)\n\n```bash\nbun add @honcho-ai/sdk\n```\n\nThe SDK is sync-by-default in Python (with an `.aio` async namespace) and async-only in TypeScript — match the client to your framework. Full sync/async guidance and the base client/peer/session/message code are in `references/core-patterns.md`.\n\n## Integration Checklist\n\nWhen integrating Honcho into an existing codebase:\n\n- [ ] Install SDK with `uv add honcho-ai` (Python) or `bun add @honcho-ai/sdk` (TypeScript)\n- [ ] Set up `HONCHO_API_KEY` environment variable\n- [ ] Initialize Honcho client with a single workspace ID\n- [ ] Create peers for all entities (users AND AI assistants)\n- [ ] Set `observe_me=False` for deterministic bot peers (optional for AI assistants — fine to leave observation on)\n- [ ] Configure sessions with appropriate peer observation settings\n- [ ] Choose integration pattern:\n  - [ ] Tool call pattern for agentic systems\n  - [ ] Pre-fetch pattern for simpler integrations\n  - [ ] context() for conversation history\n- [ ] Store messages after each exchange to build user models\n- [ ] (Optional) Run `honcho doctor` to verify connectivity before testing integration code\n- [ ] (Optional) Use `honcho peer chat` to test dialectic queries independently\n\n## Common Mistakes to Avoid\n\n1. **Multiple workspaces**: Use ONE workspace per application\n2. **Forgetting AI peers**: Create peers for AI assistants, not just users\n3. **Modeling bots**: Set `observe_me=False` for deterministic bots (scripted output — nothing meaningful to model). For AI assistants it's fine to leave observation on; turning it off is an optional optimization when you only care about the user.\n4. **Not storing messages**: Always call `add_messages()` to feed Honcho's reasoning engine\n5. **Blocking on processing**: Messages are processed asynchronously — don't poll or wait for reasoning to complete before continuing\n\n## Resources\n\n- Documentation (LLM-friendly index): <https://honcho.dev/docs/llms.txt>\n- Latest SDK versions: <https://honcho.dev/docs/changelog/introduction.md>\n- API Reference: <https://honcho.dev/docs/v3/api-reference/introduction.md>\n\n> Tip: append `.md` to any Honcho docs URL to fetch the raw Markdown version.\n","frontmatter":{"name":"honcho-integration","description":"Integrate Honcho memory into existing Python or TypeScript codebases. Use when adding Honcho SDK, setting up peers, configuring sessions, and accessing Honcho's representation.","allowed-tools":"Read, Glob, Grep, Bash(uv:*), Bash(bun:*), Bash(npm:*), Edit, Write, WebFetch, AskUserQuestion","allowedTools":["Read,","Glob,","Grep,","Bash(uv:*),","Bash(bun:*),","Bash(npm:*),","Edit,","Write,","WebFetch,","AskUserQuestion"]},"allowedTools":["Read,","Glob,","Grep,","Bash(uv:*),","Bash(bun:*),","Bash(npm:*),","Edit,","Write,","WebFetch,","AskUserQuestion"],"isInternal":false,"tokens":2205,"sizeBytes":9765},{"name":"SKILL.md","path":"skills/honcho-memory/SKILL.md","rawUrl":"https://raw.githubusercontent.com/plastic-labs/honcho/HEAD/skills/honcho-memory/SKILL.md","title":"honcho-memory","category":"anthropic-skill","format":"markdown","content":"---\nname: honcho-memory\ndescription: Concepts and strategy for using a connected Honcho as persistent memory of the user — the recall/record loop and session and peer design. Start here to understand how Honcho memory works, then connect — via a first-class integration for your environment if one exists (preferred), or raw MCP tools (covered here) or the honcho-cli skill (CLI). For embedding the SDK into a codebase, use honcho-integration.\n---\n\n# Using Honcho as Memory\n\nHoncho is a memory layer for agents. You feed it the messages from your conversations; in the background it reasons over them and builds a **representation** of each participant. At any point you can ask it natural-language questions about the user (\"How technical are they?\", \"What are they trying to do?\") and get grounded, reasoned answers.\n\nThis skill is for when Honcho is **already connected** to you and you want to use it. If you're instead adding Honcho to a codebase from scratch, use the `honcho-integration` skill.\n\n> **What's durable vs. what to look up.** The concepts and the recall/record loop below change rarely — rely on them. Specifics that change often — the exact set of integrations, tool names, install commands, headers, and defaults — are illustrative here; treat the linked docs (and your own live tool list) as authoritative and fetch them when the details matter.\n\n## The mental model\n\n- **Peer** — any participant, human or AI. You and the user are both peers. Honcho builds a representation of peers it observes (typically the user, not you).\n- **Session** — one conversation thread; messages live in sessions. Honcho reasons over the messages in a session together, so scope each session to one coherent context (a conversation, channel, task, or project) and keep that context's turns in the same session rather than fragmenting them across many thin ones. For low-volume or trickle inputs, append to one ongoing session rather than spinning up a new one each time. See [design patterns](https://honcho.dev/docs/v3/documentation/core-concepts/design-patterns.md) and [reasoning](https://honcho.dev/docs/v3/documentation/core-concepts/reasoning.md).\n- **Message** — the raw turns you feed in. No messages → no reasoning → no memory.\n- **Conclusion** — a fact Honcho derived (or you stored) about a peer. Conclusions power the representation.\n- **Representation / peer card** — the synthesized understanding of a peer, queryable via `chat`. A peer's representation **accumulates across every session** it appears in — that's the cross-conversation memory. Session-scoped data (recent messages, summaries) stays local to one session.\n\nReasoning happens **asynchronously**. After you record a turn, don't poll or wait — the representation updates in the background and is richer next time you ask.\n\n## The loop: recall → respond → record\n\nDo this every conversation. It's the whole skill.\n\n1. **Once per conversation** — make sure there's a session with you and the user as peers (observe the user, don't observe yourself).\n2. **Before responding, when personalization helps** — pull the user's current context (`get_session_context` / `get_representation`) or search past messages (`search`) — these are fast reads. For a reasoned answer to a specific question, ask the dialectic (`chat`) — that one takes a few seconds, so use it when it earns its keep.\n3. **After every exchange** — record both the user's message and your reply. This is what makes Honcho learn. Don't skip it.\n\nOptionally, when you learn a durable fact you don't want to wait for background reasoning to surface, **store a conclusion** directly.\n\n## What you get back when you recall\n\nThree ways to pull memory, cheapest first:\n\n- **Representation** (`get_representation`) — Honcho's synthesized understanding of the user as text, ready to drop straight into a system prompt. Near-instant read.\n- **Context** (`get_session_context`) — the fuller session view: a session summary + recent messages covering the conversation, and — *only if you target a peer* — that peer's representation folded in. Without a peer target it's session-local (recent turns + summary) and carries no cross-conversation memory. Near-instant read.\n- **Dialectic** (`chat`) — a *reasoned* natural-language answer to a specific question (\"How does this user like to receive feedback?\"). Runs live reasoning, so it takes a few seconds. Use it when a plain read won't answer the question.\n\nThe dialectic (`chat`) also takes a **reasoning level** that trades speed for depth — from `minimal` (fast factual lookup) through `low` (the default balance) to `max` (deep synthesis for the hardest questions). Pick the lowest level that answers the question; higher levels are slower and cost more. The full level-by-level table and model routing are in the [chat docs](https://honcho.dev/docs/v3/documentation/features/chat.md).\n\n---\n\n## Pick your access path\n\nThe loop is the same; the mechanics depend on how you reach Honcho. **Prefer a purpose-built integration over wiring up raw MCP yourself** — they handle sessions, peers, and the record loop for you, stay current, and are tuned per environment.\n\n1. **A first-class integration exists for your environment? Use it.** In Claude Code, install the [Claude Code plugin](https://honcho.dev/docs/v3/guides/integrations/claude-code.md) (`/plugin marketplace add plastic-labs/claude-honcho`) for persistent memory out of the box; there are also plugins/integrations for [OpenCode](https://honcho.dev/docs/v3/guides/integrations/opencode.md), LangGraph, CrewAI, Discord, and more. Browse the always-current list: <https://honcho.dev/docs/v3/guides/overview.md>.\n2. **No integration, but you have MCP tools** (`create_session`, `add_messages_to_session`, `chat`, …) → drive them with the loop above. The MCP server injects its own usage guide on connect, so there's nothing extra to load; to connect a client yourself, see [Setup](#setup-if-not-connected-yet) below. This is the fallback for connected agents.\n3. **`honcho` CLI available** in a terminal → use the **`honcho-cli`** skill — for the recall/record loop, and for verifying that memory is actually building (did messages land? is the representation growing? why doesn't it remember me?).\n4. **Embedding Honcho into your own codebase** (not just using a connected instance) → use the **`honcho-integration`** skill.\n\nIf you're unsure, list your available tools and look for Honcho memory tools (an MCP connection) before falling back to the CLI. Even on the MCP path, the `honcho-cli` skill is the best way to **verify the loop is working** if memory seems off.\n\n---\n\n## Setup (if not connected yet)\n\nYou need a Honcho API key — get one free at <https://app.honcho.dev> (starts with `hch-`). Then connect via the path you picked above — a purpose-built integration (recommended), or a raw connection:\n\n- **MCP** — point your client at `https://mcp.honcho.dev` with `Authorization: Bearer hch-your-key-here`. Optional `X-Honcho-Workspace-ID` fills the `workspace_id` tool argument when omitted; otherwise pass `workspace_id` on each call (use `list_workspaces` to discover IDs). Restart the client fully after adding config. Per-client config snippets (Claude Desktop, Cursor, Codex, Windsurf, VS Code, Cline, Zed) are in the [MCP integration guide](https://honcho.dev/docs/v3/guides/integrations/mcp.md). Once connected, the server tells your assistant how to use the tools automatically.\n- **CLI** — use the `honcho-cli` skill.\n\n---\n\n## Rules of thumb\n\n- **Always record turns.** Memory only grows from messages you feed in. Recording is the one non-optional step.\n- **Modeling the assistant is optional.** Setting `observe_me: false` on the assistant peer skips building a model of it — required only for deterministic bots (scripted output, nothing meaningful to model). For an AI assistant it's fine to leave observation on if you also want a model of the agent.\n- **One stable peer ID per entity.** Reuse the same `peer_id` for a person across every session and channel; splitting them (`user`, `user-web`, `user-discord`) builds separate representations and fragments memory.\n- **Scope sessions to coherent context buckets.** Honcho reasons over a session's messages together. Scope a session to one active interaction (per-conversation, per-channel, per-task, per-project); create a new one when context genuinely resets (new topic, new day), reuse it while context should keep accumulating. Keeping a context's turns in one session produces a coherent representation; scattering them fragments it.\n- **Don't block on reasoning.** It's asynchronous. Respond now; the representation will be richer next time.\n- **Reads are cheap; reasoning isn't.** Fetching the representation/context (`get_session_context`, `get_representation`, `search`) is a near-instant read — use it freely. The dialectic (`chat`) runs live reasoning and takes a few seconds, so save it for when you genuinely need a reasoned answer, not every turn.\n- **Check before you store.** Background reasoning derives most conclusions automatically. Store a conclusion manually only for a durable fact you want available immediately; `list`/`query` first to avoid duplicates.\n- **One workspace per app/user-context.** Don't scatter the same user's memory across multiple workspaces.\n- **Unify memory across tools with a shared workspace + peer ID.** To give one user continuous memory across several apps or agents (e.g. Claude Code, Cursor, your own app), point them at the same workspace and reuse the same peer ID — that shared ID is what links the representation. See [Unified Memory Setup](https://honcho.dev/docs/v3/guides/recipes/unified-memory-setup.md).\n\n## Resources\n\nThese are the LLM-friendly Markdown versions (append `.md` to any Honcho docs URL to get the raw Markdown; the full machine-readable index is at <https://honcho.dev/docs/llms.txt>).\n\n- Full docs index (for agents): <https://honcho.dev/docs/llms.txt>\n- All integrations & plugins: <https://honcho.dev/docs/v3/guides/overview.md>\n- MCP server & client setup: <https://honcho.dev/docs/v3/guides/integrations/mcp.md>\n- Full MCP usage walkthrough: <https://raw.githubusercontent.com/plastic-labs/honcho/refs/heads/main/mcp/instructions.md>\n- Agent development overview: <https://honcho.dev/docs/v3/documentation/introduction/vibecoding.md>\n- CLI reference: <https://honcho.dev/docs/v3/documentation/reference/cli.md>\n","frontmatter":{"name":"honcho-memory","description":"Concepts and strategy for using a connected Honcho as persistent memory of the user — the recall/record loop and session and peer design. Start here to understand how Honcho memory works, then connect — via a first-class integration for your environment if one exists (preferred), or raw MCP tools (covered here) or the honcho-cli skill (CLI). For embedding the SDK into a codebase, use honcho-integration."},"isInternal":false,"tokens":2378,"sizeBytes":10489},{"name":"SKILL.md","path":"skills/verify/SKILL.md","rawUrl":"https://raw.githubusercontent.com/plastic-labs/honcho/HEAD/skills/verify/SKILL.md","title":"verify","category":"anthropic-skill","format":"markdown","content":"---\nname: verify\ndescription: Build, launch, and drive a local Honcho stack to verify a change at its runtime surface (the /v3 HTTP API and the deriver queue). Use when verifying a diff or confirming a change works in the running app.\n---\n\n# Verifying changes in Honcho\n\n## Prerequisites\n\nDocker Compose is the preferred way to run the stack — `docker-compose.yml` at\nthe repo root brings up Postgres (pgvector), Redis, the API server, and the\nderiver worker together. If Docker Compose isn't available, the stack can also\nrun directly on the host (see Launch below); you'll need Postgres with pgvector\nand Redis reachable, plus a `.env` with connection strings and an LLM provider\nkey for any flow that hits a model (deriver, dialectic, dreamer).\n\nWorking in a worktree? It carries neither `.env` nor `node_modules`. Copy\n`.env` from the main checkout (that's where the provider keys live), and run\n`bun install` in `sdks/typescript` if you'll run the full test suite —\notherwise the pre-push gate fails on a phantom `Cannot find package 'zod'`.\n\n## Launch\n\nFirst check whether the stack is already running via Docker Compose:\n\n```bash\ndocker compose ps          # look for api / deriver / database / redis\ndocker compose up -d       # start it if not\n```\n\nA running stack is not the same as *your branch's code* running — check the\nCREATED column; the images may be weeks old. For verifying a diff, the cheap\npath is to reuse the stack's Postgres/Redis containers but run the branch's\nAPI as a host process on a spare port:\n\n```bash\nuv run uvicorn src.main:app --port 8901   # branch code, stack's DB/Redis\nuv run python -m src.deriver              # if the diff touches the worker\n```\n\nThis avoids both an image rebuild and the port/project-name conflicts a second\ncompose stack in a worktree would cause. Without Docker at all, run the two\nprocesses the same way against host Postgres + Redis (API default port 8000\nvia `uv run fastapi dev src/main.py`).\n\nWhen reading server logs, note that with the main `.env` the telemetry emitter\nspams connection warnings at an unreachable endpoint — `grep -v\ntelemetry.emitter` before looking for the real error.\n\n## Drive it\n\nPrefer driving through the `honcho-cli` skill; fall back to the SDKs\n(`sdks/python`, `sdks/typescript`), then raw REST against `/v3`, in that order\nwhen a method isn't available at the higher level. The `honcho-integration`\nskill covers how to use the SDKs.\n\nWhen a change updates endpoints or makes schema changes, verify across all\nthree surfaces — CLI, SDKs, and REST — since they can drift independently.\n\nFor LLM-path changes, the fastest synchronous surface is dialectic at the\n`minimal` reasoning level: send a couple of messages, then hit\n`/v3/.../peers/{peer_id}/chat` and observe the response.\n\n## Configuration\n\nConfiguration is central to both driving the app and running tests: it's how\nAPI keys reach the server and how a config-related change gets exercised at\nall. Settings come from environment variables or files, with precedence\nenv > `.env` > `config.toml` > defaults. To verify a configuration change,\nset the relevant option through one of these layers, restart the affected\nprocess, and observe the behavioral difference at the surface — the same\nmechanism lets you point provider base URLs, model choices, and timeouts at\ncredentials and proxies you actually have.\n\nTwo concrete levers: deep-nested settings (e.g.\n`[dialectic.levels.minimal.model_config.overrides.provider_params]`) are\nmiserable as env vars — drop a partial `config.toml` in the repo root instead.\nAnd for load-time config behavior, `uv run python -c \"import src.config; ...\"`\nis faster than booting the server.\n\n## Test suites\n\nThree test types matter here. All run in CI, but they're also runnable locally\nwith whatever API keys and configuration you have — Honcho's config surface is\nlarge, so options like per-agent timeouts and provider base URLs can be pointed\nat your own keys/proxies to exercise a change:\n\n```bash\n# Unit tests (pytest; spins up its own infra via fixtures)\nuv run pytest tests/\n\n# Unified tests — step-based end-to-end flows defined in JSON\n# (config hierarchy, multi-turn interactions, LLM-as-judge assertions)\nuv run python -m tests.unified.run\nuv run python -m tests.unified.run --test-dir tests/unified/test_cases\n\n# Live LLM tests — real provider calls, for testing specific backends.\n# Needs provider API keys AND model vars — without LIVE_LLM_*_MODELS the\n# tests silently deselect that provider. See tests/live_llm/README.md.\nexport LLM_ANTHROPIC_API_KEY=...\nexport LIVE_LLM_ANTHROPIC_45_PLUS_MODELS=claude-sonnet-4-5\nuv run pytest tests/live_llm -n 0 --live-llm --no-header -q\n```\n","frontmatter":{"name":"verify","description":"Build, launch, and drive a local Honcho stack to verify a change at its runtime surface (the /v3 HTTP API and the deriver queue). Use when verifying a diff or confirming a change works in the running app."},"isInternal":false,"tokens":1135,"sizeBytes":4702},{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/plastic-labs/honcho/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n**Before opening a PR or filing an issue**, read [If you're an agent](CONTRIBUTING.md#if-youre-an-agent) in `CONTRIBUTING.md`. Every PR needs a linked issue labelled `maintainer-approved`, and issues follow [Filing an issue](CONTRIBUTING.md#filing-an-issue). Run the `pre-pr` skill (`skills/pre-pr/`) before opening a PR.\n\n# Honcho Overview\n\n## What is Honcho?\n\nHoncho is an infrastructure layer for building AI agents with memory and social cognition. Its primary purposes include:\n\n- Imbuing agents with a sense of identity\n- Personalizing user experiences through understanding user psychology\n- Providing a Chat Endpoint (the Dialectic agent) that injects personal context just-in-time\n- Supporting development of LLM-powered applications that adapt to end users\n- Enabling multi-peer sessions where multiple participants (users or agents) can interact\n\nHoncho leverages the inherent reasoning capabilities of LLMs to build coherent models of user psychology over time, enabling more personalized and effective AI interactions.\n\n## Core Concepts\n\n### Peer Paradigm\n\nHoncho uses a peer-based model where both users and agents are represented as \"peers\". This unified approach enables:\n\n- Multi-participant sessions with mixed human and AI agents\n- Configurable observation settings (which peers observe which others)\n- Flexible identity management for all participants\n\n### Key Primitives\n\n- **Workspace** (formerly App): The root organizational unit containing all resources\n- **Peer** (formerly User): Any participant in the system (human or AI)\n- **Session**: A conversation context that can involve multiple peers\n- **Message**: Data units that can represent communication between peers OR arbitrary data ingested by a peer to enhance its global representation\n- **Collections & Documents**: Internal vector storage for peer representations. Collections are keyed by `(observer, observed)` peer pairs. Collections/Documents are not directly exposed via API, but the observations stored within them are exposed as **Conclusions** (see `/v3/.../conclusions` endpoints).\n\n## Architecture Overview\n\n### API Structure\n\nAll API routes follow the pattern: `/v3/{resource}/{id}/{action}`. Most \"list/search\" endpoints are `POST` so they can accept rich filter bodies.\n\n- **Workspaces**: Create, list, update, search\n- **Peers**: Create, list, update, chat (dialectic), messages, representation\n- **Sessions**: Create, list, update, delete, clone, manage peers, get context\n- **Messages**: Create (batch up to 100), upload (file), list, get, update\n- **Conclusions**: Create, list, query (semantic search), delete — the API-facing name for observations stored in `(observer, observed)` collections\n- **Keys**: Create scoped JWTs\n- **Webhooks**: Register endpoint, list, delete, test\n\n### Key Features\n\n#### Chat Endpoint (Dialectic agent) (`/peers/{peer_id}/chat`)\n\n- Provides bespoke responses informed by the representation\n- Integrates long-term facts from vector storage\n- Supports streaming responses\n- Configurable LLM providers\n\n#### Message Processing Pipeline\n\n1. Messages created via API (batch or single)\n2. Enqueued for background processing:\n   - `representation`: Update peer's context\n   - `summary`: Create session summaries\n3. Session-based queue processing ensures order\n4. Results stored internally in vector DB\n\n### Configuration\n\n- Hierarchical config: config.toml + environment variables\n- Database settings with connection pooling\n- Multiple LLM provider support\n- Background worker (deriver) settings\n- Authentication can be toggled on/off\n\n## Development Guide\n\n### Commands\n\n- Setup: `uv sync`\n- Run server: `uv run fastapi dev src/main.py`\n- Run tests: `uv run pytest tests/`\n- Run single test: `uv run pytest tests/path/to/test_file.py::test_function`\n- Linting: `uv run ruff check src/`\n- Typechecking: `uv run basedpyright`\n- Format code: `uv run ruff format src/`\n\n### SDK Testing\n\n#### TypeScript SDK\n\nThe TypeScript SDK tests run against a real Honcho server. `bun run test` (not bare `bun test`, which has no server to talk to) starts one for them: `scripts/run_ts_sdk_tests.py` creates a throwaway database on the Postgres named by `DB_CONNECTION_URI`, migrates it, starts the API and the mock LLM provider (`src/mock_provider`), runs `bun test`, and tears it all down. No provider key is needed. They are not part of `uv run pytest`.\n\n```bash\ncd sdks/typescript && bun run test             # whole suite\ncd sdks/typescript && bun run test peer.test   # bun test args pass through\n```\n\n**To type-check the TypeScript SDK (this is fine to run directly):**\n\n```bash\ncd sdks/typescript && bun run tsc --noEmit\n```\n\n### Code Style\n\n- Follow isort conventions with absolute imports preferred\n- Use explicit type hints with SQLAlchemy mapped_column annotations\n- snake_case for variables/functions; PascalCase for classes\n- Line length: 88 chars (Black compatible)\n- Explicit error handling with appropriate exception types\n- Docstrings: Use Google style docstrings\n- **Never hold a DB session during external calls** (LLM, embedding, HTTP). If a function needs both a DB session and an external call result, compute the external result first and pass it as a parameter. This avoids tying up DB connections during slow network I/O. Use `tracked_db` for short-lived, DB-only operations; pass a shared session when multiple DB-only calls can reuse one connection.\n- **Never write through a read-only session** (`tracked_db(..., read_only=True)`, `get_read_db`, `ReadSessionLocal`). These run in AUTOCOMMIT mode with no transaction: writes are NOT blocked by the database — they silently commit immediately, and `begin_nested()` savepoints break. There is no runtime guard; this is enforced by convention only. Use `read_only=True` strictly for SELECT-only windows; anything that mutates (including get-or-create paths) must use a regular write session.\n\n#### Multi-row locking and deadlocks\n\nTables written concurrently by more than one worker — `documents` (deriver, dreamer, scope backfill/removal, reconciler) and `queue` (every deriver replica) — deadlock when two writers touch an overlapping row set in different orders. Rules:\n\n- **A multi-row `SELECT ... FOR UPDATE` MUST carry an explicit `ORDER BY <pk>`.** Without it Postgres locks in scan order, which differs per plan, so two writers with overlapping sets can cycle. `_apply_document_row_updates` in `src/crud/document.py` is the reference implementation.\n- **`WHERE id IN (...)` does NOT impose an order**, so sorting the Python list is a no-op — the list order is discarded and the planner picks `Bitmap Heap Scan` (ctid order), `Index Scan` (id order), or `Seq Scan` per invocation. Deterministic ordering requires either a preceding `SELECT ... ORDER BY id FOR UPDATE` or `WHERE id IN (SELECT id ... ORDER BY id FOR UPDATE)`.\n- **`Document.id` is a random nanoid** (`models.py`), so id order is uncorrelated with physical order — an unordered predicate `UPDATE`/`DELETE` is roughly a coin flip against an id-ordered locker per row pair, not a rare edge case. (`QueueItem.id` is an integer identity, so there id order is also chronological.)\n- **Prefer no lock at all.** A single `UPDATE ... WHERE <predicate>` acquires row locks as it writes and has no separate lock phase to get wrong. Reach for `FOR UPDATE` only when a value must be read, computed in Python, and written back — that read-modify-write is the only reason `_apply_document_row_updates` locks (it replaced a server-side `func.greatest()`), and `populate_existing=True` is required with it so the identity map doesn't serve a stale pre-lock value. Server-side expressions (`func.greatest`, the JSONB `-` operator) avoid the lock entirely; see `_clear_work_unit_retry_attempts` in `src/deriver/queue_manager.py`.\n- `FOR UPDATE SKIP LOCKED` (the reconciler's claim pattern) never waits, so it cannot be a deadlock partner — but holding those locks across an external call still stalls other writers. See the \"never hold a DB session during external calls\" rule above.\n\n#### Auth scoping\n\n- **Named-scope keys (`sc` plus `w`) require explicit `allow_scope=True` on supported routes.** This grants both recall and ingestion within that scope, not peer-member write access. Recall handlers must bind their projection to the token; session routes check active scope membership. Keep the named-scope route allowlist in `tests/routes/test_auth_route_policy.py` synchronized. Never allow restricted keys to enroll unrelated existing sessions or fall back to global recall.\n\n- **`allow_member_read=True` (in `require_auth(...)`) is read-only — NEVER set it on a route that mutates state.** It lets a peer-scoped key reach a session route when its peer is an active member of the session, so on a mutating route it would hand any session member write access (message injection, config mutation, deletion). HTTP method is not a reliable read/write signal here (some read routes use POST for a richer body), so this is enforced by an explicit allowlist in `tests/routes/test_auth_route_policy.py` — adding the flag to a new route fails that test until you consciously add the route to `EXPECTED_MEMBER_READ_ROUTES`, and you must never add a mutating method there.\n- **When a member-read route is keyed by another sub-resource** (e.g. `peers/{peer_id}/config`), the handler must additionally confirm a peer-scoped caller only reads its OWN resource (`jwt_params.p == peer_id`, else raise `AuthenticationException`). Membership grants session access, not access to a co-member's data. See `get_peer_config` in `src/routers/sessions.py`.\n\n### Runtime Architecture\n\nHoncho runs as two cooperating processes that share a Postgres database and Redis cache:\n\n- **API server** (`uv run fastapi dev src/main.py`) — handles HTTP, enqueues background work, returns immediately. Hosts the **Dialectic** agent inline (synchronous tool loop during chat requests).\n- **Deriver worker** (`uv run python -m src.deriver`) — long-running queue consumer (uvloop). Runs the **Deriver**, **Summarizer**, and **Dreamer** off the queue. Can run multiple instances (`DERIVER_WORKERS`). Also hosts an in-process **Reconciler scheduler** (`src/reconciler/`) that periodically embeds messages with `sync_state='pending'` in `MessageEmbedding` and cleans up stale queue items — embedding generation is decoupled from message creation by design.\n\n### Agent Architecture\n\nHoncho uses several specialized LLM agents. They share tool definitions and the LLM client abstraction in `src/utils/agent_tools.py` + `src/llm/`.\n\n> **Terminology:** what users see as **conclusions** (the public API surface and the term we use in documentation) is called **observations** in code symbols — `create_observations`, `delete_observations`, `get_observation_context`, etc. Doc prose below uses \"conclusions\"; references to actual code symbols stay as \"observations.\"\n\n#### 1. Deriver (`src/deriver/`)\n\n**Role**: Memory formation through content ingestion.\n\nThe Deriver processes batches of incoming messages and extracts conclusions about peers. The current architecture is \"minimal deriver\" — a **single LLM call** per batch using structured output, not an agentic tool loop. This trades flexibility for cost and predictability.\n\n- **Trigger**: Messages enqueued by `src/deriver/enqueue.py` on message create; consumed by `src/deriver/queue_manager.py` → `consumer.process_item()` → `deriver.process_representation_tasks_batch()`.\n- **Output**: Explicit conclusions (direct facts) and deductive conclusions (inferences) saved to `(observer, observed)` collections.\n- **Entry point**: `src/deriver/__main__.py` → `queue_manager.main()`.\n- **Prompts**: `src/deriver/prompts.py` (`minimal_deriver_prompt`).\n- **Custom instructions**: per-workspace/peer guidance can be threaded into the prompt via reasoning configuration; `DERIVER__MAX_CUSTOM_INSTRUCTIONS_TOKENS` caps the addition (default 2000) and `DERIVER__MAX_INPUT_TOKENS` defaults to 25000 to make room.\n\n#### 2. Dialectic (`src/dialectic/`)\n\n**Role**: Analysis and recall for answering queries.\n\nThe Dialectic answers questions about peers by strategically gathering context from memory. It is the only tool-using agent on the synchronous request path — it loops over `DIALECTIC_TOOLS` until it has enough context to answer. (The Dreamer specialists also use tools, but run off the queue.)\n\n- **Trigger**: API call to `POST /v3/.../peers/{peer_id}/chat`.\n- **Tools** (see `DIALECTIC_TOOLS` in `src/utils/agent_tools.py`): `search_memory`, `search_messages`, `get_observation_context`, `grep_messages`, `get_messages_by_date_range`, `search_messages_temporal`, `get_reasoning_chain`. At the `minimal` reasoning level, a reduced set (`DIALECTIC_TOOLS_MINIMAL`) is used: just `search_memory` + `search_messages`.\n- **Reasoning levels**: 5 tiers — `minimal`, `low`, `medium`, `high`, `max` — each with its own model config (see `DialecticLevelSettings` in `src/config.py`).\n- **Output**: Natural language response grounded in gathered context. Supports SSE streaming.\n- **Entry point**: `src/dialectic/chat.py` → `agentic_chat()` / `agentic_chat_stream()` → `DialecticAgent` (in `src/dialectic/core.py`).\n\n#### 3. Dreamer (`src/dreamer/`)\n\n**Role**: Consolidation and self-improvement of memory.\n\nThe Dreamer is an orchestrated multi-specialist system that runs during scheduled \"dreams\" to consolidate conclusions and build reasoning trees.\n\n- **Trigger**: Scheduled via `DreamScheduler` (`src/dreamer/dream_scheduler.py`) or explicit dream task on the queue.\n- **Strategy**: Surprisal-based prioritization (`src/dreamer/surprisal.py`) selects which conclusions to expand. The orchestrator (`orchestrator.run_dream`) runs two specialist phases:\n  1. **DeductionSpecialist** (`specialists.py`) — produces deductive conclusions from explicit conclusions. Tools: `get_recent_observations`, `search_memory`, `search_messages`, `create_observations_deductive`, `delete_observations`, `update_peer_card`.\n  2. **InductionSpecialist** — produces inductive conclusions from explicit + deductive conclusions. Tools: same discovery set + `create_observations_inductive`, `update_peer_card`.\n- **Reasoning trees** (`src/dreamer/trees/`, migration `f1a2b3c4d5e6_add_reasoning_tree_columns`): each conclusion links to its premises and downstream conclusions, enabling `get_reasoning_chain` traversal at recall time.\n- **Output**: Deductive/inductive conclusions, consolidated redundancies, updated peer cards.\n- **Entry point**: `src/dreamer/orchestrator.py` → `process_dream()` (the package-level export from `src/dreamer/__init__.py`), which wraps `run_dream()`.\n\n#### 4. Summarizer (`src/utils/summarizer.py`)\n\n**Role**: Two-tier session summarization (direct LLM call — no agentic tools).\n\n- **Trigger**: Runs as part of the queue pipeline alongside representation tasks.\n- **Tiers**: short summary every `SUMMARY_MESSAGES_PER_SHORT_SUMMARY` messages (default 20); long summary every `SUMMARY_MESSAGES_PER_LONG_SUMMARY` (default 60). Token caps configurable via `SUMMARY_MAX_TOKENS_SHORT` / `SUMMARY_MAX_TOKENS_LONG`.\n\n#### Shared Agent Infrastructure\n\n- **Tool definitions** (`src/utils/agent_tools.py`): unified `TOOLS` dict; per-agent lists (`DIALECTIC_TOOLS`, `DIALECTIC_TOOLS_MINIMAL`, `DREAMER_TOOLS`, `DEDUCTION_SPECIALIST_TOOLS`, `INDUCTION_SPECIALIST_TOOLS`).\n- **LLM subsystem** (`src/llm/`): provider-agnostic `honcho_llm_call()`. Backends in `src/llm/backends/` (`anthropic.py`, `gemini.py`, `openai.py`). Includes prompt caching (`caching.py`), structured output (`structured_output.py`), tool loop (`tool_loop.py`), history adapters for cross-provider message formats, and a model registry. Per-retry provider selection is pinned via an `AttemptPlan` so stream-final retries don't bounce back to primary after the tool loop has settled on fallback.\n- **Per-agent model config**: each agent has its own `MODEL_CONFIG` in `src/config.py` with fallback chains (see `ConfiguredModelSettings`, `FallbackModelSettings`).\n- **Telemetry**: cloudevents in `src/telemetry/events/` cover API routes, dialectic, dream, deletion, reconciliation, representation, and per-call LLM accounting (`llm.py` — `LLMCallCompletedEvent` fires once per provider hit with full cost-attribution context). High-volume events are sampled deterministically per `run_id` via `TelemetrySettings.HIGH_VOLUME_SAMPLE_RATE`.\n- **Prometheus metrics** (`src/telemetry/prometheus/`): every metric carries a `namespace` label and every recorder is fail-soft (a metrics error never propagates into a request or a worker loop). Counter children with a *bounded* label domain are zero-initialized per process at startup — `initialize_bounded_metrics(instance_type=...)`, called from the `src/main.py` lifespan (`api`) and `src/deriver/__main__.py` (`deriver`) — so an absent series means a broken scrape rather than \"nothing happened\". Two consequences worth knowing before touching telemetry:\n  - **Adding a `BaseEvent` subclass requires adding its `_event_type` to `ALL_EVENT_TYPES`** in `src/telemetry/events/__init__.py` (and to `HIGH_VOLUME_EVENT_TYPES` if `_volume_class == \"high_volume\"`). Enforced by the drift guards in `tests/telemetry/test_metric_zero_init.py`, which assert set-equality against the discovered subclasses.\n  - **A service-wide, non-additive gauge must be refreshed by every replica on its own timer**, and aggregated with `max()`/`avg()`, never `sum()`. `message_embeddings_pending` is the example: it reports a DB-global count, so it is driven from `ReconcilerScheduler._scheduler_loop` (runs on all replicas) rather than from the work-unit-deduped reconciliation cycle — otherwise, combined with the zero-init, every replica that never won the work unit would export a confident permanent `0`.\n\n### Project Structure\n\n```\nsrc/\n├── main.py              # FastAPI app: middleware, routers, lifespan, exception handlers\n├── models.py            # SQLAlchemy ORM models (Workspace/Peer/Session/Message/\n│                        #   MessageEmbedding/Collection/Document/QueueItem/...)\n├── config.py            # Pydantic-settings configuration (very large; see README)\n├── db.py                # Engine + session/context management (request_context var)\n├── dependencies.py      # FastAPI DI (tracked_db, etc.)\n├── exceptions.py        # Custom exception types (HonchoException + subclasses)\n├── security.py          # JWT authentication\n├── embedding_client.py  # Embedding provider client (configurable dimensions\n│                        #   via EMBEDDING_MODEL_CONFIG__DIMENSIONS_MODE)\n├── schemas/             # Pydantic schemas\n│   ├── api.py            # Public API request/response schemas\n│   ├── configuration.py  # Per-resource configuration schemas\n│   └── internal.py       # Internal-only schemas (queue payloads, etc.)\n├── crud/                # Per-resource DB operations\n│   ├── collection.py, deriver.py, document.py, message.py\n│   ├── peer.py, peer_card.py, representation.py  (RepresentationManager)\n│   ├── session.py, webhook.py, workspace.py\n├── routers/             # FastAPI route handlers (all under /v3)\n│   ├── workspaces.py, peers.py (dialectic /chat lives here), sessions.py\n│   ├── messages.py, conclusions.py, keys.py, webhooks.py\n├── dialectic/           # Dialectic agent — runs inline per chat request\n│   ├── chat.py           # agentic_chat() / agentic_chat_stream()\n│   ├── core.py           # DialecticAgent (the tool-loop driver)\n│   └── prompts.py\n├── deriver/             # Background queue consumer (separate process)\n│   ├── __main__.py       # `python -m src.deriver` entry point\n│   ├── queue_manager.py  # QueueManager + main() loop\n│   ├── consumer.py       # process_item dispatcher (representation / deletion / reconciler)\n│   ├── deriver.py        # \"minimal deriver\" — single-LLM-call batch processor\n│   ├── enqueue.py        # API → queue producer\n│   └── prompts.py\n├── dreamer/             # Memory consolidation (runs off the queue)\n│   ├── orchestrator.py   # run_dream() / process_dream()\n│   ├── specialists.py    # DeductionSpecialist + InductionSpecialist\n│   ├── dream_scheduler.py\n│   ├── surprisal.py      # Surprisal-based conclusion prioritization\n│   └── trees/            # Reasoning-tree primitives\n├── reconciler/          # In-process scheduler hosted by the deriver worker\n│   ├── scheduler.py      # ReconcilerScheduler (started from queue_manager.py)\n│   ├── sync_vectors.py   # Embeds MessageEmbedding rows with sync_state='pending'\n│   └── queue_cleanup.py  # Removes stale queue items\n├── llm/                 # Provider-agnostic LLM client subsystem\n│   ├── api.py, backend.py, executor.py, runtime.py, registry.py\n│   ├── caching.py, structured_output.py, tool_loop.py, conversation.py\n│   ├── history_adapters.py, request_builder.py, credentials.py, types.py\n│   └── backends/         # anthropic.py, gemini.py, openai.py\n├── cache/               # Redis cache abstraction (cashews-backed)\n│   └── client.py\n├── vector_store/        # Optional external vector stores (pgvector is default,\n│   │                    #   implemented via MessageEmbedding/Document in models+crud)\n│   ├── lancedb.py\n│   └── turbopuffer.py\n├── telemetry/           # Observability\n│   ├── emitter.py        # CloudEvents emitter\n│   ├── logging.py        # Logging helpers + route-template extraction\n│   ├── metrics_collector.py, reasoning_traces.py, sentry.py\n│   ├── events/           # Event type definitions\n│   └── prometheus/       # Prometheus metric definitions\n├── utils/               # Cross-cutting utilities\n│   ├── agent_tools.py    # Tool definitions + per-agent tool lists\n│   ├── summarizer.py     # Two-tier session summarizer\n│   ├── representation.py # Representation formatting (distinct from crud/representation.py)\n│   ├── search.py, filter.py, formatting.py\n│   ├── tokens.py         # tiktoken-based counting\n│   ├── pagination.py     # List pagination: opt-in cursor mode + the cached offset shim\n│   ├── work_unit.py, queue_payload.py\n│   ├── config_helpers.py, json_parser.py, files.py\n│   └── types.py\n└── webhooks/            # Webhook delivery\n    ├── events.py\n    └── webhook_delivery.py\n```\n\n- Tests in pytest with fixtures in tests/conftest.py; subdirs mirror src/ (`tests/deriver/`, `tests/dialectic/`, etc.) plus `tests/bench/` (perf benchmarks), `tests/integration/`, `tests/live_llm/` (gated by `--live-llm`), and `tests/unified/` (the unified runner).\n- Use environment variables via python-dotenv (.env). Config precedence: env > .env > config.toml > defaults.\n\n### Database Design\n\n- All tables use text IDs (nanoid format) as primary keys\n- Composite foreign keys for multi-tenant relationships\n- Feature flags on workspace, peer, and session levels\n- Token counting on messages for usage tracking\n- JSONB metadata fields for extensibility\n- HNSW indexes for vector similarity search\n- **A `Select` that feeds a list endpoint MUST have a total ORDER BY ending in a unique column** (e.g. `created_at, id`). `paginate_offset_or_cursor` (`src/utils/pagination.py`) seeks by the sort key in both cursor mode and the offset shim; without a unique tail, rows that share a sort key are skipped or repeated across pages.\n\n### Key Architectural Decisions\n\n1. **Peer Paradigm**: humans and AI agents are unified as \"Peers\"; many-to-many with Sessions. Internal vector storage (Collections/Documents) is keyed by `(observer, observed)` peer pairs — the same mechanism powers self-representation (`observer == observed`) and cross-peer modeling.\n2. **Multi-Peer Sessions**: Sessions can have multiple participants with different observation settings.\n3. **API server / worker split**: API enqueues, deriver worker process consumes. Never block HTTP on LLM work. The Reconciler runs as an in-process scheduler inside the deriver, handling async embedding sync and queue cleanup.\n4. **\"Minimal\" deriver**: memory formation is a single structured-output LLM call per batch, not an agentic tool loop. Predictable cost, lower latency. The Dialectic is the one true tool-using agent.\n5. **Provider-agnostic LLM layer** (`src/llm/`): all model calls go through `honcho_llm_call()`. Backends (`anthropic`, `gemini`, `openai`) sit behind a registry; per-agent `MODEL_CONFIG` with fallback chains is resolved at call time.\n6. **Dialectic reasoning tiers**: 5 levels (`minimal` → `max`); each level has its own model config and tool set (`minimal` uses a reduced toolset).\n7. **Hybrid search**: Postgres FTS (GIN index on `to_tsvector('english', content)`, OR'd with a `content ILIKE` fallback backed by a `pg_trgm` GIN index; without pg_trgm the index is skipped and the ILIKE scans) + vector similarity (HNSW on `MessageEmbedding.embedding`). `MessageEmbedding` is a separate table from `Message` with its own `sync_state` so embedding is decoupled from message creation.\n8. **Pluggable external vector stores**: defaults to pgvector inline; can swap to turbopuffer or lancedb (`VECTOR_STORE_*` config; `src/vector_store/`).\n9. **Composite-FK multi-tenancy**: `workspace_name` participates in nearly every composite FK. Cross-workspace data leakage is structurally impossible at the schema level.\n10. **Scoped Authentication**: JWTs can be scoped to workspace, peer, or session level.\n11. **Batch Operations**: Bulk message creation up to 100 messages per request.\n12. **Session History**: Two-tier summarization — short every `SUMMARY_MESSAGES_PER_SHORT_SUMMARY` (default 20), long every `SUMMARY_MESSAGES_PER_LONG_SUMMARY` (default 60).\n\n### Error Handling\n\n- Custom exceptions defined in src/exceptions.py\n- Use specific exception types (ResourceNotFoundException, ValidationException, etc.)\n- Proper logging with context instead of print statements\n- Global exception handlers defined in main.py\n- See docs/contributing/error-handling.mdx for details\n\n### Notes\n\n- Always use `uv run` or `uv` to prefix any commands related to python to ensure you use the virtual environment\n","isInternal":false,"tokens":6095,"sizeBytes":26607}],"systemPromptSnippet":"<yakaai_skills repo=\"plastic-labs/honcho\">\n<!-- File: mcp/instructions.md (Tokens: ~1440 | Category: root-instruction) -->\n# Honcho MCP Server — Instructions\n\nHoncho keeps a model of the people and agents in a conversation (\"peers\") and answers questions about them. Two modes use this connection, and they need different tools.\n\n- **Recall** — read what Honcho already knows: `chat`, `workspace_chat`, `search`, `get_representation`, `get_peer_context`, `list_conclusions`. Nothing is written. This is the usual case when the user's own application writes to Honcho and this conversation only reads from it.\n- **Memory store** — this conversation is itself what Honcho learns from, because the user asked for it to be remembered or set Honcho up as this assistant's memory. Adds `create_session`, `create_peer`, `add_peers_to_session`, and `add_messages_to_session`.\n\nIf the user hasn't asked for this conversation to be recorded, stay in recall mode.\n\n## Setting up a session\n\nIn memory-store mode, a session holds the conversation and the peers in it:\n\n```\ncreate_session\n  workspace_id: \"<workspace-id>\"\n  session_id: \"<unique-id>\"\n```\n\nThen add peers to the session:\n\n```\ncreate_peer\n  workspace_id: \"<workspace-id>\"\n  peer_id: \"<user-name>\"\n\ncreate_peer\n  workspace_id: \"<workspace-id>\"\n  peer_id: \"Assistant\"\n\nadd_peers_to_session\n  workspace_id: \"<workspace-id>\"\n  session_id: \"<session_id>\"\n  peers:\n    - peer_id: \"<user-name>\"\n      observe_me: true\n      observe_others: true\n    - peer_id: \"Assistant\"\n      observe_me: false\n      observe_others: true\n```\n\nReuse that `session_id` for the rest of the conversation.\n\n## Recording messages\n\n`add_messages_to_session` appends messages to a tracked session:\n\n```\nadd_messages_to_session\n  workspace_id: \"<workspace-id>\"\n  session_id: \"<session_id>\"\n  messages:\n    - peer_id: \"<user-name>\"\n      content: \"<the user's message>\"\n    - peer_id: \"Assistant\"\n      content: \"<your response>\"\n```\n\nRecord both sides of each exchange as it happens. Honcho can only learn from\nmessages it has, and gaps in a session produce a weaker representation than a\ncomplete one.\n\n## Asking what Honcho knows\n\n`chat` answers a question about one peer, grounded in everything Honcho has learned across that peer's conversations:\n\n```\nchat\n  workspace_id: \"<workspace-id>\"\n  peer_id: \"Assistant\"\n  query: \"What communication style does this user prefer?\"\n  target_peer_id: \"<user-name>\"\n  session_id: \"<session_id>\"\n```\n\nIt runs live reasoning and takes a few seconds, so reach for it when the answer would change your response. `workspace_chat` does the same across all peers.\n\nQuestions it answers well:\n\n- \"What does this message reveal about the user's communication preferences?\"\n- \"How formal or casual should I be?\"\n- \"What is the user really asking for beyond their explicit question?\"\n- \"What emotional state might the user be in right now?\"\n\n---\n\n## Best Practices\n\n- **Group messages into coherent context buckets** — give each distinct context its own `session_id` (a chat thread, a project, a channel) and reuse that same `session_id` for every turn within it, rather than minting a new one per turn. Honcho reasons over the messages in a session together, so keeping a context's messages in one bucket produces a coherent representation; scattering them across sessions fragments it.\n- **Use one stable `peer_id` per real person**, reused across every session and channel. A fresh or per-channel ID (`user-web` vs. `user-discord`) builds separate, weaker representations instead of one.\n- **`observe_me: false` skips building a model of a peer** — reserve it for deterministic bots (nothing meaningful to model). For a real AI assistant it's fine to leave observation on.\n- **Reasoning is asynchronous** — don't poll or wait for it to finish before responding. A brand-new or low-volume peer legitimately has little to show yet.\n- **Reach for reads before `chat`** — `get_session_context` / `get_peer_context` / `get_representation` / `search` are near-instant; `chat` runs live reasoning and takes a few seconds. Use `chat` only when you need a reasoned answer.\n\n---\n\n## Key Concepts\n\n### Peers\n\nA **peer** is any participant — human or AI. Each peer has a unique ID within the workspace.\n\n### Sessions\n\nA **session** is a conversation context. Sessions track message history, manage which peers participate, and provide context retrieval for LLMs.\n\n### Conclusions\n\n**Conclusions** are facts and observations that Honcho derives from conversations. They power the representation — Honcho's understanding of a peer.\n\nEvery conclusion carries its own attribution. `level` says how it was reached: `explicit` conclusions are extracted straight from messages, while `deductive`, `inductive` and `contradiction` conclusions are derived while dreaming. `source_ids` names the conclusions a derived one was built from, and is null for explicit ones. `times_derived` counts how many times Honcho independently reached the same conclusion — a rough confidence signal.\n\nThose fields make the reasoning tree walkable in both directions: `get_conclusions` on a conclusion's `source_ids` steps down toward the explicit facts it rests on, and `get_derived_conclusions` steps up to whatever was built on top of it. Walk down before correcting a fact, and up before deleting one.\n\n### Evidence\n\n`chat` and `workspace_chat` accept `include_evidence`. With it, the answer arrives alongside the conclusions and messages the agent read and the tools it called. This is collated from what the agent actually accessed rather than reported by the model, so it over-reports — a listed conclusion was read, which is not proof the answer leaned on it. It costs no extra model tokens, so reach for it whenever an answer needs auditing.\n\n### Representations\n\nA **representation** is a formatted text summary built from a peer's conclusions. Query it with `get_representation` or `chat`.\n\n### Peer Cards\n\nA **peer card** is a compact list of biographical facts about a peer, automatically maintained by Honcho (or manually via `set_peer_card`).\n\n### Reasoning Level\n\nSeveral tools accept an optional `reasoning_level` parameter (`minimal`, `low`, `medium`, `high`, `max`). Higher levels produce more thorough answers but take longer and cost more. Default is `low`. Use `minimal` for the fastest lookups; use `high` or `max` when depth matters.\n\n### Dreams\n\nA **dream** is a background memory-consolidation process. It reviews conclusions, merges redundancies, and generates higher-level insights. Schedule one with `schedule_dream` after long conversations.\n\n<!-- File: examples/zo/SKILL.md (Tokens: ~840 | Category: anthropic-skill) -->\n---\nname: honcho-memory\ndescription: Gives AI agents persistent memory across conversations using Honcho. Automatically saves and retrieves user context so the AI remembers preferences, history, and facts between sessions. Use when you need the AI to remember past conversations, recall what a user has told it, inject relevant context into prompts, or manage separate memory spaces for different topics.\nlicense: AGPL-3.0\ncompatibility: Requires Python 3.9+, honcho-ai>=2.1.0, and a Honcho API key from honcho.dev. Set HONCHO_API_KEY and optionally HONCHO_WORKSPACE_ID in your environment.\nmetadata:\n  author: plastic-labs\n  version: \"0.1.0\"\n  honcho-sdk: \"2.1.0\"\n---\n\n# Honcho Memory Skill\n\nThis skill provides three tools for storing and retrieving AI memory using [Honcho](https://honcho.dev).\n\n## Setup\n\n1. Get a Honcho API key at [honcho.dev](https://honcho.dev).\n2. Set environment variables:\n\n   ```\n   HONCHO_API_KEY=your-api-key\n   HONCHO_WORKSPACE_ID=default   # optional, defaults to \"default\"\n   ```\n\n3. Install dependencies:\n\n   ```\n   pip install honcho-ai python-dotenv\n   ```\n\n## Tools\n\n### `save_memory`\n\nSaves a conversation turn (user or assistant message) to Honcho.\n\n**When to use:** After every message exchange to build up the user's memory.\n\n```python\nfrom tools.save_memory import save_memory\n\nsave_memory(\n    user_id=\"alice\",           # unique user identifier\n    content=\"I love hiking\",   # message text\n    role=\"user\",               # \"user\" or \"assistant\"\n    session_id=\"chat-1\",       # conversation session ID\n    assistant_id=\"assistant\"   # optional: assistant peer ID (default: \"assistant\")\n)\n```\n\n### `query_memory`\n\nAsks a natural language question against stored memory using Honcho's Dialectic API.\n\n**When to use:** When the user asks \"do you remember...?\", or when you need to recall facts about the user before responding.\n\n```python\nfrom tools.query_memory import query_memory\n\nanswer = query_memory(\n    user_id=\"alice\",\n    query=\"What are Alice's hobbies?\",\n    session_id=\"chat-1\"   # optional: scope to a session\n)\n# Returns: \"Alice enjoys hiking.\"\n```\n\n### `get_context`\n\nRetrieves recent conversation history formatted for direct use in an LLM API call.\n\n**When to use:** At the start of each LLM call to inject relevant context from past conversations.\n\n```python\nfrom tools.get_context import get_context\n\nmessages = get_context(\n    user_id=\"alice\",\n    session_id=\"chat-1\",\n    assistant_id=\"assistant\",\n    tokens=4000              # max tokens to include\n)\n# Returns: [{\"role\": \"user\", \"content\": \"...\"}, ...]\n```\n\n## Concept Mapping\n\n| Zo Computer | Honcho |\n|---|---|\n| Account | Workspace |\n| User | Peer |\n| Conversation | Session |\n| Message | Message |\n\n## Example: Full Conversation Flow\n\n```python\nfrom tools.save_memory import save_memory\nfrom tools.query_memory import query_memory\nfrom tools.get_context import get_context\n\nuser_id = \"alice\"\nsession_id = \"session-1\"\n\n# 1. Save user message\nsave_memory(user_id, \"I'm learning Rust and love rock climbing\", \"user\", session_id)\n\n# 2. Save assistant reply\nsave_memory(user_id, \"That's great! Both require patience.\", \"assistant\", session_id)\n\n# 3. In a later session, recall what you know\nprint(query_memory(user_id, \"What does Alice do in her free time?\"))\n# → \"Alice is learning Rust and enjoys rock climbing.\"\n\n# 4. Get context window for next LLM call\nmessages = get_context(user_id, session_id, \"assistant\", tokens=4000)\n```\n\n<!-- File: skills/honcho-cli/SKILL.md (Tokens: ~1291 | Category: anthropic-skill) -->\n---\nname: honcho-cli\ndescription: Inspect and debug Honcho workspaces via the `honcho` CLI. Use when investigating peer representations, memory state, session context, or dialectic quality — any task that requires introspection of a Honcho deployment, including verifying that a recall/record memory loop is actually working.\nallowed-tools: Bash(honcho:*), Bash(jq:*), Read, Grep\n---\n\n# Honcho CLI\n\n`honcho` wraps the Honcho Python SDK with agent-friendly defaults: JSON output, structured errors, input validation. Use it to inspect workspace state, debug peer memory, and diagnose the dialectic.\n\n## Output & config\n\n- **TTY**: human-readable tables (default when interactive)\n- **Piped / `--json`**: JSON — collection commands emit arrays, single-resource commands emit objects\n- **Exit codes**: `0` success · `1` client error (bad input, not found) · `2` server error · `3` auth error\n- **Config**: `~/.honcho/config.json` (shared with other Honcho tools). The CLI owns `apiKey` and `environmentUrl` at the top level; run `honcho init` to confirm or set them. Per-command scope (workspace / peer / session) is via `-w` / `-p` / `-s` flags or `HONCHO_*` env vars.\n\n## Command groups\n\n- `honcho config` — CLI configuration\n- `honcho start` / `stop` / `status` — local Docker stack (does not change `environmentUrl`). First start pins the Honcho image digest and writes `config.toml` into the profile. Pass `--setup basic` or `--setup advanced` for an interactive config wizard (TTY only; writes `.env` overrides). `honcho status` lists every profile; pass `--profile` for one.\n- `honcho workspace` — inspect, delete, search, chat\n- `honcho peer` — inspect, card, chat, search\n- `honcho session` — inspect, view (transcript), context, summaries\n- `honcho scope` — list, create, inspect, sessions, add-sessions, remove-session, status\n- `honcho message` — list and get\n- `honcho conclusion` — list, search, create, delete\n\n## Rules\n\n- Always pass `--json` when processing output programmatically.\n- Run `honcho peer inspect` before `honcho peer chat` to understand context.\n- Use `honcho session context` to see exactly what an agent receives.\n- Never run `honcho workspace delete` without `honcho workspace inspect` first.\n- Compare peer card with conclusions to understand memory state.\n- After `honcho scope add-sessions`, check `honcho scope status <name>` before a scoped `chat`: a `pending` backfill means the scope has not caught up yet, not that there is nothing to recall.\n- `honcho start` does not rewrite `environmentUrl`. Use `HONCHO_BASE_URL=http://127.0.0.1:8000` to talk to local stack.\n\n## Inspection tour\n\nWhen orienting to a Honcho deployment, walk outside-in:\n\n### 1. Understand the workspace\n\n```bash\nhoncho workspace inspect --json\n```\n\n### 2. Find the peer\n\n```bash\nhoncho peer list --json\nhoncho peer inspect <peer_id> --json\n```\n\n### 3. Check peer's memory\n\n```bash\nhoncho peer card <peer_id> --json\nhoncho conclusion list --observer <peer_id> --json\nhoncho conclusion search \"topic\" --observer <peer_id> --json\n```\n\n### 4. Debug a session\n\n```bash\nhoncho session inspect <session_id> --json\nhoncho session view <session_id> --last 20 --json\nhoncho session view <session_id> --page 2 --size 50 --json\nhoncho message list <session_id> --last 20 --json\nhoncho session context <session_id> --json\nhoncho session summaries <session_id> --json\n```\n\n### 5. Search across workspace\n\n```bash\nhoncho workspace search \"query\" --json\nhoncho workspace chat \"what themes show up across peers?\" --json\nhoncho workspace chat \"what happened in this project?\" --scope my-project --json\nhoncho peer search <peer_id> \"query\" --json\n```\n\n### 6. Bound recall with a scope\n\n```bash\nhoncho scope list --json\nhoncho scope inspect <name> --json                       # members + backfill summary\nhoncho scope create <name> --sessions <id1>,<id2> --json\nhoncho scope status <name> --json                        # wait until nothing is pending\nhoncho workspace chat \"what happened here?\" --scope <name> --json\n```\n\n## Debugging playbook\n\n### Peer not learning?\n\n```bash\n# Is observation enabled?\nhoncho peer inspect <peer_id> --json | jq '.configuration'\n\n# What conclusions exist?\nhoncho conclusion list --observer <peer_id> --json\nhoncho conclusion search \"expected topic\" --observer <peer_id> --json\n```\n\n### Session context looks wrong?\n\n```bash\n# Raw context an agent would receive\nhoncho session context <session_id> --json\n\n# Summaries feeding the context\nhoncho session summaries <session_id> --json\n\n# Recent message history\nhoncho message list <session_id> --last 50 --json\n```\n\n### Dialectic giving bad answers?\n\n```bash\n# What the peer card says\nhoncho peer card <peer_id> --json\n\n# Conclusions on the specific topic\nhoncho conclusion search \"topic\" --observer <peer_id> --json\n\n# Exercise the dialectic directly\nhoncho peer chat <peer_id> \"what do you know about X?\" --json\nhoncho peer chat <peer_id> \"what happened here?\" --scope my-project --json\nhoncho workspace chat \"what themes show up across peers?\" --json\n```\n\n<!-- File: skills/honcho-integration/references/agent-patterns.md (Tokens: ~1920 | Category: anthropic-skill) -->\n# Agent Access Patterns\n\nHow your AI accesses Honcho's user context. Pick based on the interview answer to Question Set 2. These build on the client/peer/session setup in `core-patterns.md`.\n\n- **Pattern A — Dialectic chat as a tool call**: the agent decides when to query context on-demand.\n- **Pattern B — Pre-fetch with targeted queries**: fetch a fixed set of attributes before each LLM call.\n- **Pattern C — `context()` for LLM integration**: inject conversation history + representation into the prompt.\n\n> **Speed note.** `chat()` runs live dialectic reasoning (a few seconds) — Patterns A and B call it. `context()` (Pattern C) is a near-instant read. Prefer `context()` for per-turn grounding; reach for `chat()` when you genuinely need a reasoned answer.\n\n## Pattern A: Dialectic Chat as a Tool Call\n\nMake Honcho's chat endpoint available as a **tool** for your AI agent. This lets the agent query user context on-demand.\n\n**Python (OpenAI function calling):**\n\n```python\nimport openai\nfrom honcho import Honcho\n\nhoncho = Honcho(workspace_id=\"my-app\", api_key=os.environ[\"HONCHO_API_KEY\"])\n\n# Define the tool for your agent\nhoncho_tool = {\n    \"type\": \"function\",\n    \"function\": {\n        \"name\": \"query_user_context\",\n        \"description\": \"Query Honcho to retrieve relevant context about the user based on their history and preferences. Use this when you need to understand the user's background, preferences, past interactions, or goals.\",\n        \"parameters\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"query\": {\n                    \"type\": \"string\",\n                    \"description\": \"A natural language question about the user, e.g. 'What are this user's main goals?' or 'What communication style does this user prefer?'\"\n                }\n            },\n            \"required\": [\"query\"]\n        }\n    }\n}\n\ndef handle_honcho_tool_call(user_id: str, query: str) -> str:\n    \"\"\"Execute the Honcho chat tool call.\"\"\"\n    peer = honcho.peer(user_id)\n    return peer.chat(query)\n\n# Use in your agent loop\ndef run_agent(user_id: str, user_message: str):\n    messages = [{\"role\": \"user\", \"content\": user_message}]\n\n    response = openai.chat.completions.create(\n        model=\"gpt-4\",\n        messages=messages,\n        tools=[honcho_tool]\n    )\n\n    # Handle tool calls\n    if response.choices[0].message.tool_calls:\n        for tool_call in response.choices[0].message.tool_calls:\n            if tool_call.function.name == \"query_user_context\":\n                import json\n                args = json.loads(tool_call.function.arguments)\n                result = handle_honcho_tool_call(user_id, args[\"query\"])\n                # Continue conversation with tool result...\n```\n\n**TypeScript (OpenAI function calling):**\n\n```typescript\nimport OpenAI from 'openai';\nimport { Honcho } from '@honcho-ai/sdk';\n\nconst honcho = new Honcho({\n    workspaceId: \"my-app\",\n    apiKey: process.env.HONCHO_API_KEY\n});\n\nconst honchoTool: OpenAI.ChatCompletionTool = {\n    type: \"function\",\n    function: {\n        name: \"query_user_context\",\n        description: \"Query Honcho to retrieve relevant context about the user based on their history and preferences.\",\n        parameters: {\n            type: \"object\",\n            properties: {\n                query: {\n                    type: \"string\",\n                    description: \"A natural language question about the user\"\n                }\n            },\n            required: [\"query\"]\n        }\n    }\n};\n\nasync function handleHonchoToolCall(userId: string, query: string): Promise<string> {\n    const peer = await honcho.peer(userId);\n    return await peer.chat(query);\n}\n```\n\n## Pattern B: Pre-fetch Context with Targeted Queries\n\nFor simpler integrations, fetch user context before the LLM call using pre-defined queries.\n\n**Python:**\n\n```python\ndef get_user_context_for_prompt(user_id: str) -> dict:\n    \"\"\"Fetch key user attributes via targeted Honcho queries.\"\"\"\n    peer = honcho.peer(user_id)\n\n    return {\n        \"communication_style\": peer.chat(\"What communication style does this user prefer? Be concise.\"),\n        \"expertise_level\": peer.chat(\"What is this user's technical expertise level? Be concise.\"),\n        \"current_goals\": peer.chat(\"What are this user's current goals or priorities? Be concise.\"),\n        \"preferences\": peer.chat(\"What key preferences should I know about this user? Be concise.\")\n    }\n\ndef build_system_prompt(user_context: dict) -> str:\n    return f\"\"\"You are a helpful assistant. Here's what you know about this user:\n\nCommunication style: {user_context['communication_style']}\nExpertise level: {user_context['expertise_level']}\nCurrent goals: {user_context['current_goals']}\nKey preferences: {user_context['preferences']}\n\nTailor your responses accordingly.\"\"\"\n```\n\n**TypeScript:**\n\n```typescript\nasync function getUserContextForPrompt(userId: string): Promise<Record<string, string>> {\n    const peer = await honcho.peer(userId);\n\n    const [style, expertise, goals, preferences] = await Promise.all([\n        peer.chat(\"What communication style does this user prefer? Be concise.\"),\n        peer.chat(\"What is this user's technical expertise level? Be concise.\"),\n        peer.chat(\"What are this user's current goals or priorities? Be concise.\"),\n        peer.chat(\"What key preferences should I know about this user? Be concise.\")\n    ]);\n\n    return {\n        communicationStyle: style,\n        expertiseLevel: expertise,\n        currentGoals: goals,\n        preferences: preferences\n    };\n}\n```\n\n## Pattern C: Get Context for LLM Integration\n\nUse `context()` for conversation history with built-in LLM formatting. This is a near-instant read so it's the cheapest way to ground each turn.\n\n**Python:**\n\n```python\nimport openai\n\nsession = honcho.session(\"conversation-123\")\nuser = honcho.peer(\"user-123\")\nassistant = honcho.peer(\"assistant\")\n\n# Get context formatted for your LLM\ncontext = session.context(\n    tokens=2000,\n    peer_target=user.id,  # Include representation of this user\n    summary=True           # Include conversation summaries\n)\n\n# Convert to OpenAI format\nmessages = context.to_openai(assistant=assistant)\n\n# Or Anthropic format\n# messages = context.to_anthropic(assistant=assistant)\n\n# Add the new user message\nmessages.append({\"role\": \"user\", \"content\": \"What should I focus on today?\"})\n\nresponse = openai.chat.completions.create(\n    model=\"gpt-4\",\n    messages=messages\n)\n\n# Store the exchange\nsession.add_messages([\n    user.message(\"What should I focus on today?\"),\n    assistant.message(response.choices[0].message.content)\n])\n```\n\n**TypeScript:**\n\n```typescript\nimport OpenAI from 'openai';\n\nconst session = await honcho.session(\"conversation-123\");\nconst user = await honcho.peer(\"user-123\");\nconst assistant = await honcho.peer(\"assistant\");\n\n// Get context formatted for your LLM\nconst context = await session.context({\n    tokens: 2000,\n    peerTarget: user.id,  // Include representation of this user\n    summary: true          // Include conversation summaries\n});\n\n// Convert to OpenAI format\nconst messages = context.toOpenAI(assistant);\n\n// Or Anthropic format\n// const messages = context.toAnthropic(assistant);\n\n// Add the new user message\nmessages.push({ role: \"user\", content: \"What should I focus on today?\" });\n\nconst openai = new OpenAI();\nconst response = await openai.chat.completions.create({\n    model: \"gpt-4\",\n    messages\n});\n\n// Store the exchange\nawait session.addMessages([\n    user.message(\"What should I focus on today?\"),\n    assistant.message(response.choices[0].message.content!)\n]);\n```\n\n### What `context()` returns\n\n`session.context()` bundles the session-local view you can drop straight into an LLM call:\n\n- **Recent messages** from the session, trimmed to the `tokens` budget.\n- **Conversation summaries** when `summary=True` — the two-tier short/long summaries so older turns still count without spending the full token budget.\n- **The target peer's representation** when you pass `peer_target` — Honcho's synthesized understanding of that user, folded in. Omit it and you get session-local context only (no cross-session memory).\n\nThe `to_openai()` / `to_anthropic()` helpers format all of that as a `messages` array for the respective provider. Pass your `assistant` peer so its turns are tagged as the assistant role.\n\n## Streaming Responses\n\n```python\nstream = peer.chat_stream(\"What do we know about this user?\")\n\nfor chunk in stream:\n    print(chunk, end=\"\", flush=True)\n```\n\n```typescript\nconst stream = await peer.chatStream(\"What do we know about this user?\");\n\nfor await (const chunk of stream) {\n    process.stdout.write(chunk);\n}\n```\n\n<!-- File: skills/honcho-integration/references/bot-frameworks.md (Tokens: ~2044 | Category: anthropic-skill) -->\n# Honcho Integration for Bot Frameworks\n\nThis reference extends the main honcho-integration skill for **bot frameworks** — applications built around an agent loop, session manager, tool registry, and message bus (e.g., nanobot, openclaw, picoclaw).\n\n## Supported Frameworks\n\nWhen a known framework is detected, use concrete reference implementations from `{baseDir}/references/bot-frameworks/<framework>/`.\n\n| Framework | Status | Reference Dir |\n|-----------|--------|---------------|\n| [nanobot](https://github.com/HKUDS/nanobot) | concrete references | `bot-frameworks/nanobot/` |\n| openclaw | planned | -- |\n| picoclaw | planned | -- |\n\nFor unknown frameworks, adapt the general pattern below to the codebase's architecture.\n\n## Phase 1: Explore (bot-specific)\n\nIn addition to the main skill's Phase 1, identify these bot-specific components:\n\n1. **Agent loop**: Where messages are processed (look for `while` loops calling an LLM)\n2. **Session manager**: How conversation history is stored (JSONL files, database, in-memory)\n3. **Tool registry**: How tools/functions are registered for the LLM to call\n4. **Message bus**: How inbound/outbound messages are routed between channels and the agent\n5. **Config system**: How the bot loads configuration (JSON, YAML, env vars, pydantic models, zod schemas)\n6. **CLI entry points**: How the bot is started (commands, gateway, agent modes)\n\nIf the framework matches a known one (e.g., nanobot), pull the concrete references from `{baseDir}/references/bot-frameworks/<framework>/` and use them as the implementation target.\n\n## Phase 2: Interview (bot-specific)\n\nIn addition to the main skill's interview questions, ask about:\n\n- **Peer model**: Who are the participants? (typically: one user peer per channel:chat_id, one shared assistant peer)\n- **Session granularity**: One session per chat? Per user? Per channel?\n- **Workspace ID**: What namespace for this bot's Honcho data?\n- **Feature flag**: Should Honcho be opt-in (default `false`) or opt-out (default `true`)?\n\n## Phase 3: Implement (bot-specific)\n\n### Step 1: Add dependency\n\n**Python:** Add `honcho-ai>=2.0.1`. If the framework supports optional dependencies, make it optional:\n\n```toml\n[project.optional-dependencies]\nhoncho = [\"honcho-ai>=2.0.1\"]\n```\n\n**TypeScript:** Add `@honcho-ai/sdk`:\n\n```bash\nbun add @honcho-ai/sdk\n# or npm install @honcho-ai/sdk\n```\n\nIf the framework supports optional peer dependencies:\n\n```json\n{\n  \"peerDependencies\": {\n    \"@honcho-ai/sdk\": \">=2.0.1\"\n  },\n  \"peerDependenciesMeta\": {\n    \"@honcho-ai/sdk\": { \"optional\": true }\n  }\n}\n```\n\n### Step 2: Add config schema\n\nAdd a Honcho config section to the bot's configuration system:\n\n**Python:**\n\n```python\nclass HonchoConfig(BaseModel):\n    \"\"\"Honcho AI-native memory integration (optional feature flag).\"\"\"\n    enabled: bool = False  # or True for Honcho-first deployments\n    workspace_id: str = \"default\"\n    prefetch: bool = True  # inject user context into system prompts\n    context_tokens: int | None = None\n    environment: str = \"production\"\n```\n\n**TypeScript:**\n\n```typescript\ninterface HonchoConfig {\n  /** Honcho AI-native memory integration (optional feature flag). */\n  enabled: boolean;        // default: false, or true for Honcho-first deployments\n  workspaceId: string;     // default: \"default\"\n  prefetch: boolean;       // default: true — inject user context into system prompts\n  contextTokens?: number;\n  environment: string;     // default: \"production\"\n}\n\nconst defaultHonchoConfig: HonchoConfig = {\n  enabled: false,\n  workspaceId: \"default\",\n  prefetch: true,\n  environment: \"production\",\n};\n```\n\n### Step 3: Create the honcho package\n\nCreate a honcho integration package with:\n\n- **Client singleton** (`client.py` / `client.ts`): Lazy initialization, deferred imports, `getHonchoClient()` factory\n- **Session manager** (`session.py` / `session.ts`): Maps bot sessions to Honcho sessions with peer configuration\n- **Agent tool** (`honcho_tool.py` / `honchoTool.ts`): Tool the agent can call to query user context via `peer.chat()`\n\nKey patterns (Python):\n\n- `from __future__ import annotations` + `TYPE_CHECKING` for all honcho imports\n- Runtime imports inside functions (never top-level) so the bot doesn't crash without `honcho-ai`\n- Wrap in `try/except ImportError` for graceful degradation\n\nKey patterns (TypeScript):\n\n- Use dynamic `import()` for honcho SDK (never top-level `import ... from`) so the bot doesn't crash without `@honcho-ai/sdk`\n- Use `import type { ... }` for type-only imports that are erased at runtime\n- Wrap in `try/catch` for graceful degradation when the SDK is missing\n\nKey patterns (shared):\n\n- IDs sanitized to `^[a-zA-Z0-9_-]+` (Honcho requirement)\n- User peer: `observe_me=True, observe_others=True`\n- Assistant/bot peer: `observe_others=True`; set `observe_me=False` only for deterministic bots (scripted output — nothing to model). AI-assistant bots can keep `observe_me=True`.\n\nIf references exist for this framework, use them directly from `{baseDir}/references/bot-frameworks/<framework>/`.\n\n### Step 4: Wire into the agent loop\n\nAdd these integration points to the agent loop:\n\n1. **Tool registration** (at startup): If `honcho.enabled` and `HONCHO_API_KEY` set, initialize client + register Honcho tools.\n\n   **Python:** Wrap in `try/except ImportError` for graceful degradation.\n   **TypeScript:** Use dynamic `import()` inside a `try/catch` block.\n\n2. **Context setup** (per message): Set session context on Honcho tools, ensure Honcho session exists.\n\n3. **Prefetch** (per message): Call `session.context()` to get user representation and inject into system prompt before the LLM call.\n\n4. **Sync** (after response): After saving to local session, sync the user+assistant message pair to Honcho.\n\n   **Python:**\n\n   ```python\n   session.add_messages([\n       user_peer.message(user_input),\n       assistant_peer.message(assistant_response),\n   ])\n   ```\n\n   **TypeScript:**\n\n   ```typescript\n   await session.addMessages([\n       userPeer.message(userInput),\n       assistantPeer.message(assistantResponse),\n   ]);\n   ```\n\n5. **Migration** (on first activation): If Honcho session is empty but local session has history, upload prior messages as a file via `session.upload_file()` (Python) or `session.uploadFile()` (TypeScript). Also upload `MEMORY.md` and `HISTORY.md` if they exist (from frameworks with local memory consolidation). Archive originals after successful upload.\n\n### Step 5: Pass config through CLI\n\nPass `honcho_config` to every agent loop instantiation in the CLI commands.\n\n### Step 6: Migration support\n\nWhen Honcho activates on an instance with existing local data, migrate automatically:\n\n- **Session messages** (JSONL files): Format as XML transcript, upload via `session.upload_file()` (Python) or `session.uploadFile()` (TypeScript)\n- **Consolidated memory** (MEMORY.md, HISTORY.md): Upload as tagged files with context annotations\n- **Archive originals**: Move to `migrated/` subdirectory after successful upload\n- **Idempotent**: Skip if Honcho session already has messages\n\n## Phase 4: Verify (bot-specific)\n\nAfter integration, verify:\n\n- [ ] Bot starts normally without the Honcho SDK installed (no import errors)\n- [ ] Bot starts normally with the SDK but without `HONCHO_API_KEY` (graceful skip)\n- [ ] With both present and `enabled=true`, logs show \"Honcho tools registered\"\n- [ ] User context is prefetched and visible in system prompts\n- [ ] Messages sync to Honcho after each exchange\n- [ ] Local session migration works on first Honcho activation\n- [ ] Memory file migration works for MEMORY.md/HISTORY.md (if applicable)\n\n## Bot-Specific Patterns\n\n- **Lazy imports everywhere**:\n  - Python: `from __future__ import annotations` + `TYPE_CHECKING` for type hints, runtime imports inside functions\n  - TypeScript: `import type { ... }` for type-only imports, dynamic `import()` for runtime access\n- **Feature flag gating**: Always check `config.enabled` AND `HONCHO_API_KEY` / `process.env.HONCHO_API_KEY` before touching Honcho\n- **Graceful degradation**:\n  - Python: `try/except ImportError` and generic `Exception` catches with logger warnings, never crash the bot\n  - TypeScript: `try/catch` around dynamic `import()` with logger warnings, never crash the bot\n- **Sanitize IDs**: Honcho requires `^[a-zA-Z0-9_-]+` — replace colons, dots, spaces with dashes\n- **Sync after success**: Only mark messages as synced after the API call succeeds, not before\n- **Cache consistency**: When creating aliased sessions, store under both original and derived keys\n\n<!-- File: skills/honcho-integration/references/core-patterns.md (Tokens: ~1172 | Category: anthropic-skill) -->\n# Core Integration Patterns\n\nThe base SDK boilerplate for any Honcho integration: choosing sync vs async, initializing the client, creating peers, configuring sessions, and adding messages. Read this once you've chosen your entities and session structure (Phases 1–2). For the agent-facing recall patterns (tool call, pre-fetch, `context()`), see `agent-patterns.md`.\n\n## Sync vs Async\n\n**TypeScript** — The SDK is async by default. All methods return promises. No separate sync API.\n\n**Python** — The SDK provides both sync and async interfaces:\n\n- **Sync** (default): `from honcho import Honcho` — use in sync frameworks (Flask, Django, CLI scripts)\n- **Async**: `from honcho import Honcho` with `.aio` namespace — use in async frameworks (FastAPI, Starlette, async workers)\n\n```python\n# Sync usage (Flask, Django, scripts)\nfrom honcho import Honcho\nhoncho = Honcho(workspace_id=\"my-app\", api_key=os.environ[\"HONCHO_API_KEY\"])\npeer = honcho.peer(\"user-123\")\nresponse = peer.chat(\"What does this user prefer?\")\n\n# Async usage (FastAPI, Starlette)\nfrom honcho import Honcho\nhoncho = Honcho(workspace_id=\"my-app\", api_key=os.environ[\"HONCHO_API_KEY\"])\npeer = await honcho.aio.peer(\"user-123\")\nresponse = await peer.aio.chat(\"What does this user prefer?\")\n```\n\nMatch the client to the framework — check whether the codebase uses `async def` handlers or sync `def` handlers and choose accordingly. The examples below show sync Python; swap to `.aio` equivalents for async codebases.\n\n## 1. Initialize with a Single Workspace\n\nUse ONE workspace for your entire application. The workspace name should reflect your app/product.\n\n**Python:**\n\n```python\nfrom honcho import Honcho\nimport os\n\n# Sync client (Flask, Django, scripts)\nhoncho = Honcho(\n    workspace_id=\"your-app-name\",\n    api_key=os.environ[\"HONCHO_API_KEY\"],\n    environment=\"production\"\n)\n\n# Async client (FastAPI, Starlette) — use honcho.aio for all operations\n# honcho.aio.peer(), honcho.aio.session(), etc.\n```\n\n**TypeScript:**\n\n```typescript\nimport { Honcho } from '@honcho-ai/sdk';\n\n// All methods are async by default\nconst honcho = new Honcho({\n    workspaceId: \"your-app-name\",\n    apiKey: process.env.HONCHO_API_KEY,\n    environment: \"production\"\n});\n```\n\n## 2. Create Peers for ALL Entities\n\nCreate peers for **every entity** in your business logic - users AND AI assistants.\n\n**Python:**\n\n```python\nfrom honcho.api_types import PeerConfig\n\n# Human users (observed by default)\nuser = honcho.peer(\"user-123\")\n\n# AI assistants can be observed too — leave observe_me on (the default) if you\n# want a model of the assistant.\nassistant = honcho.peer(\"assistant\")\n\n# Deterministic bots (scripted/rule-based) - set observe_me=False; there's\n# nothing meaningful for Honcho to model.\nnotification_bot = honcho.peer(\"notification-bot\", configuration=PeerConfig(observe_me=False))\n```\n\n**TypeScript:**\n\n```typescript\n// Human users (observed by default)\nconst user = await honcho.peer(\"user-123\");\n\n// AI assistants can be observed too — leave observeMe on (the default) if you\n// want a model of the assistant.\nconst assistant = await honcho.peer(\"assistant\");\n\n// Deterministic bots (scripted/rule-based) - set observeMe=false; there's\n// nothing meaningful for Honcho to model.\nconst notificationBot = await honcho.peer(\"notification-bot\", { configuration: { observeMe: false } });\n```\n\n## 3. Multi-Peer Sessions\n\nSessions can have multiple participants. Configure observation settings per-peer.\n\n**Python:**\n\n```python\nfrom honcho.api_types import SessionPeerConfig\n\nsession = honcho.session(\"conversation-123\")\n\n# User is observed (Honcho builds a model of them)\nuser_config = SessionPeerConfig(observe_me=True, observe_others=True)\n\n# A deterministic bot is NOT observed (no model built of it). An AI assistant\n# could stay observed instead — observe_me defaults to True.\nbot_config = SessionPeerConfig(observe_me=False, observe_others=True)\n\nsession.add_peers([\n    (user, user_config),\n    (notification_bot, bot_config)\n])\n```\n\n**TypeScript:**\n\n```typescript\nconst session = await honcho.session(\"conversation-123\");\n\nawait session.addPeers([\n    // A deterministic bot isn't observed; an AI assistant could stay observed\n    // instead (observeMe defaults to true).\n    [user, { observeMe: true, observeOthers: true }],\n    [notificationBot, { observeMe: false, observeOthers: true }]\n]);\n```\n\n## 4. Add Messages to Sessions\n\n**Python:**\n\n```python\nsession.add_messages([\n    user.message(\"I'm having trouble with my account\"),\n    assistant.message(\"I'd be happy to help. What seems to be the issue?\"),\n    user.message(\"I can't reset my password\")\n])\n```\n\n**TypeScript:**\n\n```typescript\nawait session.addMessages([\n    user.message(\"I'm having trouble with my account\"),\n    assistant.message(\"I'd be happy to help. What seems to be the issue?\"),\n    user.message(\"I can't reset my password\")\n]);\n```\n\n<!-- File: skills/honcho-integration/SKILL.md (Tokens: ~2205 | Category: anthropic-skill) -->\n---\nname: honcho-integration\ndescription: Integrate Honcho memory into existing Python or TypeScript codebases. Use when adding Honcho SDK, setting up peers, configuring sessions, and accessing Honcho's representation.\nallowed-tools: Read, Glob, Grep, Bash(uv:*), Bash(bun:*), Bash(npm:*), Edit, Write, WebFetch, AskUserQuestion\n---\n\n# Honcho Integration Guide\n\n## What is Honcho\n\nHoncho is an open source memory library for building stateful agents. It works with any model, framework, or architecture. You send Honcho the messages from your conversations, and custom reasoning models process them in the background — extracting premises, drawing conclusions, and building rich representations of each participant over time. Your agent can then query those representations on-demand (\"What does this user care about?\", \"How technical is this person?\") and get grounded, reasoned answers.\n\nThe key mental model: **Peers** are any participant — human or AI. Both are represented the same way. `observe_me` is a peer-level flag (`PeerConfig`) controlling whether Honcho forms a representation of *that* peer; typically you want Honcho to model your users (`observe_me=True`) but not anything with deterministic behavior (`observe_me=False`). `observe_others` is a separate per-peer `SessionPeerConfig` setting that controls whether that peer forms representations of the *other* participants in a session. **Sessions** scope conversations between peers. **Messages** are the raw data you feed in — Honcho reasons about them asynchronously and stores the results as the peer's **representation**. No messages means no reasoning means no memory.\n\nYour agent accesses this memory through `peer.chat(query)` (ask a natural language question, get a reasoned answer — a few seconds of live reasoning) or `session.context()` (near-instant read of formatted history + representation). Prefer `context()` for per-turn grounding; use `chat()` when you need a reasoned answer.\n\n## Reference map\n\nFollow the workflow below. Read a reference file only when you reach the step that needs it:\n\n| When you're… | Read |\n| --- | --- |\n| Writing the client/peer/session setup (init, peers, sessions, add messages) | `references/core-patterns.md` |\n| Wiring how the AI reads context (tool call, pre-fetch, `context()`, streaming) | `references/agent-patterns.md` |\n| Integrating into a bot framework (nanobot, openclaw, picoclaw, …) | `references/bot-frameworks.md` + `references/bot-frameworks/<framework>/` |\n\n## Integration Workflow\n\nFollow these phases in order:\n\n### Phase 1: Codebase Exploration\n\nBefore asking the user anything, explore the codebase to understand:\n\n1. **Language & Framework**: Is this Python or TypeScript? What frameworks are used (FastAPI, Express, Next.js, etc.)?\n2. **Existing AI/LLM code**: Search for existing LLM integrations (OpenAI, Anthropic, LangChain, etc.)\n3. **Entity structure**: Identify users, agents, bots, or other entities that interact\n4. **Session/conversation handling**: How does the app currently manage conversations?\n5. **Message flow**: Where are messages sent/received? What's the request/response cycle?\n\nUse Glob and Grep to find:\n\n- `**/*.py` or `**/*.ts` files with \"openai\", \"anthropic\", \"llm\", \"chat\", \"message\"\n- User/session models or types\n- API routes handling chat or conversation endpoints\n\n> **Bot framework detected?** If the codebase is built around an agent loop, tool registry, session manager, and message bus (e.g., nanobot, openclaw, picoclaw), read `references/bot-frameworks.md` for framework-specific integration guidance and check `references/bot-frameworks/<framework>/` for concrete reference implementations.\n\n### Phase 2: Interview (REQUIRED)\n\nAfter exploring the codebase, use the **AskUserQuestion** tool to clarify integration requirements. Ask these questions (adapt based on what you learned in Phase 1):\n\n#### Question Set 1 - Entities & Peers\n\nAsk about which entities should be Honcho peers:\n\n- header: \"Peers\"\n- question: \"Which entities should Honcho track and build representations for?\"\n- options based on what you found (e.g., \"End users only\", \"Users + AI assistant\", \"Users + multiple AI agents\", \"All participants including third-party services\")\n- Include a follow-up if they have multiple AI agents: should any AI peers be observed?\n\n#### Question Set 2 - Integration Pattern\n\nAsk how they want to use Honcho context (see `references/agent-patterns.md` for the implementation of each):\n\n- header: \"Pattern\"\n- question: \"How should your AI access Honcho's user context?\"\n- options:\n  - \"Tool call (Recommended)\" - \"Agent queries Honcho on-demand via function calling\"\n  - \"Pre-fetch\" - \"Fetch user context before each LLM call with predefined queries\"\n  - \"context()\" - \"Include conversation history and representations in prompt\"\n  - \"Multiple patterns\" - \"Combine approaches for different use cases\"\n\n#### Question Set 3 - Session Structure\n\nAsk about conversation structure:\n\n- header: \"Sessions\"\n- question: \"How should conversations map to Honcho sessions?\"\n- options based on their app (e.g., \"One session per chat thread\", \"One session per user\", \"Multiple users per session (group chat)\", \"Custom session logic\")\n\n#### Question Set 4 - Specific Queries (if using pre-fetch pattern)\n\nIf they chose pre-fetch, ask what context matters:\n\n- header: \"Context\"\n- question: \"What user context should be fetched for the AI?\"\n- multiSelect: true\n- options: \"Communication style\", \"Expertise level\", \"Goals/priorities\", \"Preferences\", \"Recent activity summary\", \"Custom queries\"\n\n### Phase 3: Implementation\n\nBased on interview responses, implement the integration:\n\n1. Install the SDK (see [Installation](#installation))\n2. Create Honcho client initialization — `references/core-patterns.md` §1\n3. Set up peer creation for identified entities — `references/core-patterns.md` §2–3\n4. Implement the chosen integration pattern(s) — `references/agent-patterns.md`\n5. Add message storage after exchanges — `references/core-patterns.md` §4\n6. Update any existing conversation handlers\n\n### Phase 4: Verification\n\n- If the Honcho CLI is available, run `honcho doctor` to confirm connectivity before testing the integration code\n- Use `honcho peer list` and `honcho peer chat` to verify peers exist and the dialectic endpoint works independently of the integration\n- Ensure all message exchanges are stored to Honcho\n- Verify deterministic bot peers have `observe_me=False`; AI-assistant peers can keep observation on (it's fine to model them)\n- Check that the workspace ID is consistent across the codebase\n- Confirm environment variable for API key is documented\n\n---\n\n## Before You Start\n\n1. **Check the latest SDK versions** at <https://honcho.dev/docs/changelog/introduction.md>\n   - Python SDK: `honcho-ai`\n   - TypeScript SDK: `@honcho-ai/sdk`\n\n2. **Get an API key** ask the user to get a Honcho API key from <https://app.honcho.dev> and add it to the environment.\n\n3. **Verify with the CLI** (optional but recommended). If the user has the Honcho CLI installed (`uv install honcho-cli`), they can validate their setup before writing any integration code:\n\n   ```bash\n   honcho init          # persist API key + URL to ~/.honcho/config.json\n   honcho doctor        # verify connectivity, config, workspace health\n   honcho peer chat     # test the dialectic endpoint interactively\n   ```\n\n   This is the fastest way to confirm the API key and URL are correct before debugging SDK code.\n\n## Installation\n\n### Python (use uv)\n\n```bash\nuv add honcho-ai\n```\n\n### TypeScript (use bun)\n\n```bash\nbun add @honcho-ai/sdk\n```\n\nThe SDK is sync-by-default in Python (with an `.aio` async namespace) and async-only in TypeScript — match the client to your framework. Full sync/async guidance and the base client/peer/session/message code are in `references/core-patterns.md`.\n\n## Integration Checklist\n\nWhen integrating Honcho into an existing codebase:\n\n- [ ] Install SDK with `uv add honcho-ai` (Python) or `bun add @honcho-ai/sdk` (TypeScript)\n- [ ] Set up `HONCHO_API_KEY` environment variable\n- [ ] Initialize Honcho client with a single workspace ID\n- [ ] Create peers for all entities (users AND AI assistants)\n- [ ] Set `observe_me=False` for deterministic bot peers (optional for AI assistants — fine to leave observation on)\n- [ ] Configure sessions with appropriate peer observation settings\n- [ ] Choose integration pattern:\n  - [ ] Tool call pattern for agentic systems\n  - [ ] Pre-fetch pattern for simpler integrations\n  - [ ] context() for conversation history\n- [ ] Store messages after each exchange to build user models\n- [ ] (Optional) Run `honcho doctor` to verify connectivity before testing integration code\n- [ ] (Optional) Use `honcho peer chat` to test dialectic queries independently\n\n## Common Mistakes to Avoid\n\n1. **Multiple workspaces**: Use ONE workspace per application\n2. **Forgetting AI peers**: Create peers for AI assistants, not just users\n3. **Modeling bots**: Set `observe_me=False` for deterministic bots (scripted output — nothing meaningful to model). For AI assistants it's fine to leave observation on; turning it off is an optional optimization when you only care about the user.\n4. **Not storing messages**: Always call `add_messages()` to feed Honcho's reasoning engine\n5. **Blocking on processing**: Messages are processed asynchronously — don't poll or wait for reasoning to complete before continuing\n\n## Resources\n\n- Documentation (LLM-friendly index): <https://honcho.dev/docs/llms.txt>\n- Latest SDK versions: <https://honcho.dev/docs/changelog/introduction.md>\n- API Reference: <https://honcho.dev/docs/v3/api-reference/introduction.md>\n\n> Tip: append `.md` to any Honcho docs URL to fetch the raw Markdown version.\n\n<!-- File: skills/honcho-memory/SKILL.md (Tokens: ~2378 | Category: anthropic-skill) -->\n---\nname: honcho-memory\ndescription: Concepts and strategy for using a connected Honcho as persistent memory of the user — the recall/record loop and session and peer design. Start here to understand how Honcho memory works, then connect — via a first-class integration for your environment if one exists (preferred), or raw MCP tools (covered here) or the honcho-cli skill (CLI). For embedding the SDK into a codebase, use honcho-integration.\n---\n\n# Using Honcho as Memory\n\nHoncho is a memory layer for agents. You feed it the messages from your conversations; in the background it reasons over them and builds a **representation** of each participant. At any point you can ask it natural-language questions about the user (\"How technical are they?\", \"What are they trying to do?\") and get grounded, reasoned answers.\n\nThis skill is for when Honcho is **already connected** to you and you want to use it. If you're instead adding Honcho to a codebase from scratch, use the `honcho-integration` skill.\n\n> **What's durable vs. what to look up.** The concepts and the recall/record loop below change rarely — rely on them. Specifics that change often — the exact set of integrations, tool names, install commands, headers, and defaults — are illustrative here; treat the linked docs (and your own live tool list) as authoritative and fetch them when the details matter.\n\n## The mental model\n\n- **Peer** — any participant, human or AI. You and the user are both peers. Honcho builds a representation of peers it observes (typically the user, not you).\n- **Session** — one conversation thread; messages live in sessions. Honcho reasons over the messages in a session together, so scope each session to one coherent context (a conversation, channel, task, or project) and keep that context's turns in the same session rather than fragmenting them across many thin ones. For low-volume or trickle inputs, append to one ongoing session rather than spinning up a new one each time. See [design patterns](https://honcho.dev/docs/v3/documentation/core-concepts/design-patterns.md) and [reasoning](https://honcho.dev/docs/v3/documentation/core-concepts/reasoning.md).\n- **Message** — the raw turns you feed in. No messages → no reasoning → no memory.\n- **Conclusion** — a fact Honcho derived (or you stored) about a peer. Conclusions power the representation.\n- **Representation / peer card** — the synthesized understanding of a peer, queryable via `chat`. A peer's representation **accumulates across every session** it appears in — that's the cross-conversation memory. Session-scoped data (recent messages, summaries) stays local to one session.\n\nReasoning happens **asynchronously**. After you record a turn, don't poll or wait — the representation updates in the background and is richer next time you ask.\n\n## The loop: recall → respond → record\n\nDo this every conversation. It's the whole skill.\n\n1. **Once per conversation** — make sure there's a session with you and the user as peers (observe the user, don't observe yourself).\n2. **Before responding, when personalization helps** — pull the user's current context (`get_session_context` / `get_representation`) or search past messages (`search`) — these are fast reads. For a reasoned answer to a specific question, ask the dialectic (`chat`) — that one takes a few seconds, so use it when it earns its keep.\n3. **After every exchange** — record both the user's message and your reply. This is what makes Honcho learn. Don't skip it.\n\nOptionally, when you learn a durable fact you don't want to wait for background reasoning to surface, **store a conclusion** directly.\n\n## What you get back when you recall\n\nThree ways to pull memory, cheapest first:\n\n- **Representation** (`get_representation`) — Honcho's synthesized understanding of the user as text, ready to drop straight into a system prompt. Near-instant read.\n- **Context** (`get_session_context`) — the fuller session view: a session summary + recent messages covering the conversation, and — *only if you target a peer* — that peer's representation folded in. Without a peer target it's session-local (recent turns + summary) and carries no cross-conversation memory. Near-instant read.\n- **Dialectic** (`chat`) — a *reasoned* natural-language answer to a specific question (\"How does this user like to receive feedback?\"). Runs live reasoning, so it takes a few seconds. Use it when a plain read won't answer the question.\n\nThe dialectic (`chat`) also takes a **reasoning level** that trades speed for depth — from `minimal` (fast factual lookup) through `low` (the default balance) to `max` (deep synthesis for the hardest questions). Pick the lowest level that answers the question; higher levels are slower and cost more. The full level-by-level table and model routing are in the [chat docs](https://honcho.dev/docs/v3/documentation/features/chat.md).\n\n---\n\n## Pick your access path\n\nThe loop is the same; the mechanics depend on how you reach Honcho. **Prefer a purpose-built integration over wiring up raw MCP yourself** — they handle sessions, peers, and the record loop for you, stay current, and are tuned per environment.\n\n1. **A first-class integration exists for your environment? Use it.** In Claude Code, install the [Claude Code plugin](https://honcho.dev/docs/v3/guides/integrations/claude-code.md) (`/plugin marketplace add plastic-labs/claude-honcho`) for persistent memory out of the box; there are also plugins/integrations for [OpenCode](https://honcho.dev/docs/v3/guides/integrations/opencode.md), LangGraph, CrewAI, Discord, and more. Browse the always-current list: <https://honcho.dev/docs/v3/guides/overview.md>.\n2. **No integration, but you have MCP tools** (`create_session`, `add_messages_to_session`, `chat`, …) → drive them with the loop above. The MCP server injects its own usage guide on connect, so there's nothing extra to load; to connect a client yourself, see [Setup](#setup-if-not-connected-yet) below. This is the fallback for connected agents.\n3. **`honcho` CLI available** in a terminal → use the **`honcho-cli`** skill — for the recall/record loop, and for verifying that memory is actually building (did messages land? is the representation growing? why doesn't it remember me?).\n4. **Embedding Honcho into your own codebase** (not just using a connected instance) → use the **`honcho-integration`** skill.\n\nIf you're unsure, list your available tools and look for Honcho memory tools (an MCP connection) before falling back to the CLI. Even on the MCP path, the `honcho-cli` skill is the best way to **verify the loop is working** if memory seems off.\n\n---\n\n## Setup (if not connected yet)\n\nYou need a Honcho API key — get one free at <https://app.honcho.dev> (starts with `hch-`). Then connect via the path you picked above — a purpose-built integration (recommended), or a raw connection:\n\n- **MCP** — point your client at `https://mcp.honcho.dev` with `Authorization: Bearer hch-your-key-here`. Optional `X-Honcho-Workspace-ID` fills the `workspace_id` tool argument when omitted; otherwise pass `workspace_id` on each call (use `list_workspaces` to discover IDs). Restart the client fully after adding config. Per-client config snippets (Claude Desktop, Cursor, Codex, Windsurf, VS Code, Cline, Zed) are in the [MCP integration guide](https://honcho.dev/docs/v3/guides/integrations/mcp.md). Once connected, the server tells your assistant how to use the tools automatically.\n- **CLI** — use the `honcho-cli` skill.\n\n---\n\n## Rules of thumb\n\n- **Always record turns.** Memory only grows from messages you feed in. Recording is the one non-optional step.\n- **Modeling the assistant is optional.** Setting `observe_me: false` on the assistant peer skips building a model of it — required only for deterministic bots (scripted output, nothing meaningful to model). For an AI assistant it's fine to leave observation on if you also want a model of the agent.\n- **One stable peer ID per entity.** Reuse the same `peer_id` for a person across every session and channel; splitting them (`user`, `user-web`, `user-discord`) builds separate representations and fragments memory.\n- **Scope sessions to coherent context buckets.** Honcho reasons over a session's messages together. Scope a session to one active interaction (per-conversation, per-channel, per-task, per-project); create a new one when context genuinely resets (new topic, new day), reuse it while context should keep accumulating. Keeping a context's turns in one session produces a coherent representation; scattering them fragments it.\n- **Don't block on reasoning.** It's asynchronous. Respond now; the representation will be richer next time.\n- **Reads are cheap; reasoning isn't.** Fetching the representation/context (`get_session_context`, `get_representation`, `search`) is a near-instant read — use it freely. The dialectic (`chat`) runs live reasoning and takes a few seconds, so save it for when you genuinely need a reasoned answer, not every turn.\n- **Check before you store.** Background reasoning derives most conclusions automatically. Store a conclusion manually only for a durable fact you want available immediately; `list`/`query` first to avoid duplicates.\n- **One workspace per app/user-context.** Don't scatter the same user's memory across multiple workspaces.\n- **Unify memory across tools with a shared workspace + peer ID.** To give one user continuous memory across several apps or agents (e.g. Claude Code, Cursor, your own app), point them at the same workspace and reuse the same peer ID — that shared ID is what links the representation. See [Unified Memory Setup](https://honcho.dev/docs/v3/guides/recipes/unified-memory-setup.md).\n\n## Resources\n\nThese are the LLM-friendly Markdown versions (append `.md` to any Honcho docs URL to get the raw Markdown; the full machine-readable index is at <https://honcho.dev/docs/llms.txt>).\n\n- Full docs index (for agents): <https://honcho.dev/docs/llms.txt>\n- All integrations & plugins: <https://honcho.dev/docs/v3/guides/overview.md>\n- MCP server & client setup: <https://honcho.dev/docs/v3/guides/integrations/mcp.md>\n- Full MCP usage walkthrough: <https://raw.githubusercontent.com/plastic-labs/honcho/refs/heads/main/mcp/instructions.md>\n- Agent development overview: <https://honcho.dev/docs/v3/documentation/introduction/vibecoding.md>\n- CLI reference: <https://honcho.dev/docs/v3/documentation/reference/cli.md>\n\n<!-- File: skills/verify/SKILL.md (Tokens: ~1135 | Category: anthropic-skill) -->\n---\nname: verify\ndescription: Build, launch, and drive a local Honcho stack to verify a change at its runtime surface (the /v3 HTTP API and the deriver queue). Use when verifying a diff or confirming a change works in the running app.\n---\n\n# Verifying changes in Honcho\n\n## Prerequisites\n\nDocker Compose is the preferred way to run the stack — `docker-compose.yml` at\nthe repo root brings up Postgres (pgvector), Redis, the API server, and the\nderiver worker together. If Docker Compose isn't available, the stack can also\nrun directly on the host (see Launch below); you'll need Postgres with pgvector\nand Redis reachable, plus a `.env` with connection strings and an LLM provider\nkey for any flow that hits a model (deriver, dialectic, dreamer).\n\nWorking in a worktree? It carries neither `.env` nor `node_modules`. Copy\n`.env` from the main checkout (that's where the provider keys live), and run\n`bun install` in `sdks/typescript` if you'll run the full test suite —\notherwise the pre-push gate fails on a phantom `Cannot find package 'zod'`.\n\n## Launch\n\nFirst check whether the stack is already running via Docker Compose:\n\n```bash\ndocker compose ps          # look for api / deriver / database / redis\ndocker compose up -d       # start it if not\n```\n\nA running stack is not the same as *your branch's code* running — check the\nCREATED column; the images may be weeks old. For verifying a diff, the cheap\npath is to reuse the stack's Postgres/Redis containers but run the branch's\nAPI as a host process on a spare port:\n\n```bash\nuv run uvicorn src.main:app --port 8901   # branch code, stack's DB/Redis\nuv run python -m src.deriver              # if the diff touches the worker\n```\n\nThis avoids both an image rebuild and the port/project-name conflicts a second\ncompose stack in a worktree would cause. Without Docker at all, run the two\nprocesses the same way against host Postgres + Redis (API default port 8000\nvia `uv run fastapi dev src/main.py`).\n\nWhen reading server logs, note that with the main `.env` the telemetry emitter\nspams connection warnings at an unreachable endpoint — `grep -v\ntelemetry.emitter` before looking for the real error.\n\n## Drive it\n\nPrefer driving through the `honcho-cli` skill; fall back to the SDKs\n(`sdks/python`, `sdks/typescript`), then raw REST against `/v3`, in that order\nwhen a method isn't available at the higher level. The `honcho-integration`\nskill covers how to use the SDKs.\n\nWhen a change updates endpoints or makes schema changes, verify across all\nthree surfaces — CLI, SDKs, and REST — since they can drift independently.\n\nFor LLM-path changes, the fastest synchronous surface is dialectic at the\n`minimal` reasoning level: send a couple of messages, then hit\n`/v3/.../peers/{peer_id}/chat` and observe the response.\n\n## Configuration\n\nConfiguration is central to both driving the app and running tests: it's how\nAPI keys reach the server and how a config-related change gets exercised at\nall. Settings come from environment variables or files, with precedence\nenv > `.env` > `config.toml` > defaults. To verify a configuration change,\nset the relevant option through one of these layers, restart the affected\nprocess, and observe the behavioral difference at the surface — the same\nmechanism lets you point provider base URLs, model choices, and timeouts at\ncredentials and proxies you actually have.\n\nTwo concrete levers: deep-nested settings (e.g.\n`[dialectic.levels.minimal.model_config.overrides.provider_params]`) are\nmiserable as env vars — drop a partial `config.toml` in the repo root instead.\nAnd for load-time config behavior, `uv run python -c \"import src.config; ...\"`\nis faster than booting the server.\n\n## Test suites\n\nThree test types matter here. All run in CI, but they're also runnable locally\nwith whatever API keys and configuration you have — Honcho's config surface is\nlarge, so options like per-agent timeouts and provider base URLs can be pointed\nat your own keys/proxies to exercise a change:\n\n```bash\n# Unit tests (pytest; spins up its own infra via fixtures)\nuv run pytest tests/\n\n# Unified tests — step-based end-to-end flows defined in JSON\n# (config hierarchy, multi-turn interactions, LLM-as-judge assertions)\nuv run python -m tests.unified.run\nuv run python -m tests.unified.run --test-dir tests/unified/test_cases\n\n# Live LLM tests — real provider calls, for testing specific backends.\n# Needs provider API keys AND model vars — without LIVE_LLM_*_MODELS the\n# tests silently deselect that provider. See tests/live_llm/README.md.\nexport LLM_ANTHROPIC_API_KEY=...\nexport LIVE_LLM_ANTHROPIC_45_PLUS_MODELS=claude-sonnet-4-5\nuv run pytest tests/live_llm -n 0 --live-llm --no-header -q\n```\n\n<!-- File: CLAUDE.md (Tokens: ~6095 | Category: claude-rule) -->\n# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n**Before opening a PR or filing an issue**, read [If you're an agent](CONTRIBUTING.md#if-youre-an-agent) in `CONTRIBUTING.md`. Every PR needs a linked issue labelled `maintainer-approved`, and issues follow [Filing an issue](CONTRIBUTING.md#filing-an-issue). Run the `pre-pr` skill (`skills/pre-pr/`) before opening a PR.\n\n# Honcho Overview\n\n## What is Honcho?\n\nHoncho is an infrastructure layer for building AI agents with memory and social cognition. Its primary purposes include:\n\n- Imbuing agents with a sense of identity\n- Personalizing user experiences through understanding user psychology\n- Providing a Chat Endpoint (the Dialectic agent) that injects personal context just-in-time\n- Supporting development of LLM-powered applications that adapt to end users\n- Enabling multi-peer sessions where multiple participants (users or agents) can interact\n\nHoncho leverages the inherent reasoning capabilities of LLMs to build coherent models of user psychology over time, enabling more personalized and effective AI interactions.\n\n## Core Concepts\n\n### Peer Paradigm\n\nHoncho uses a peer-based model where both users and agents are represented as \"peers\". This unified approach enables:\n\n- Multi-participant sessions with mixed human and AI agents\n- Configurable observation settings (which peers observe which others)\n- Flexible identity management for all participants\n\n### Key Primitives\n\n- **Workspace** (formerly App): The root organizational unit containing all resources\n- **Peer** (formerly User): Any participant in the system (human or AI)\n- **Session**: A conversation context that can involve multiple peers\n- **Message**: Data units that can represent communication between peers OR arbitrary data ingested by a peer to enhance its global representation\n- **Collections & Documents**: Internal vector storage for peer representations. Collections are keyed by `(observer, observed)` peer pairs. Collections/Documents are not directly exposed via API, but the observations stored within them are exposed as **Conclusions** (see `/v3/.../conclusions` endpoints).\n\n## Architecture Overview\n\n### API Structure\n\nAll API routes follow the pattern: `/v3/{resource}/{id}/{action}`. Most \"list/search\" endpoints are `POST` so they can accept rich filter bodies.\n\n- **Workspaces**: Create, list, update, search\n- **Peers**: Create, list, update, chat (dialectic), messages, representation\n- **Sessions**: Create, list, update, delete, clone, manage peers, get context\n- **Messages**: Create (batch up to 100), upload (file), list, get, update\n- **Conclusions**: Create, list, query (semantic search), delete — the API-facing name for observations stored in `(observer, observed)` collections\n- **Keys**: Create scoped JWTs\n- **Webhooks**: Register endpoint, list, delete, test\n\n### Key Features\n\n#### Chat Endpoint (Dialectic agent) (`/peers/{peer_id}/chat`)\n\n- Provides bespoke responses informed by the representation\n- Integrates long-term facts from vector storage\n- Supports streaming responses\n- Configurable LLM providers\n\n#### Message Processing Pipeline\n\n1. Messages created via API (batch or single)\n2. Enqueued for background processing:\n   - `representation`: Update peer's context\n   - `summary`: Create session summaries\n3. Session-based queue processing ensures order\n4. Results stored internally in vector DB\n\n### Configuration\n\n- Hierarchical config: config.toml + environment variables\n- Database settings with connection pooling\n- Multiple LLM provider support\n- Background worker (deriver) settings\n- Authentication can be toggled on/off\n\n## Development Guide\n\n### Commands\n\n- Setup: `uv sync`\n- Run server: `uv run fastapi dev src/main.py`\n- Run tests: `uv run pytest tests/`\n- Run single test: `uv run pytest tests/path/to/test_file.py::test_function`\n- Linting: `uv run ruff check src/`\n- Typechecking: `uv run basedpyright`\n- Format code: `uv run ruff format src/`\n\n### SDK Testing\n\n#### TypeScript SDK\n\nThe TypeScript SDK tests run against a real Honcho server. `bun run test` (not bare `bun test`, which has no server to talk to) starts one for them: `scripts/run_ts_sdk_tests.py` creates a throwaway database on the Postgres named by `DB_CONNECTION_URI`, migrates it, starts the API and the mock LLM provider (`src/mock_provider`), runs `bun test`, and tears it all down. No provider key is needed. They are not part of `uv run pytest`.\n\n```bash\ncd sdks/typescript && bun run test             # whole suite\ncd sdks/typescript && bun run test peer.test   # bun test args pass through\n```\n\n**To type-check the TypeScript SDK (this is fine to run directly):**\n\n```bash\ncd sdks/typescript && bun run tsc --noEmit\n```\n\n### Code Style\n\n- Follow isort conventions with absolute imports preferred\n- Use explicit type hints with SQLAlchemy mapped_column annotations\n- snake_case for variables/functions; PascalCase for classes\n- Line length: 88 chars (Black compatible)\n- Explicit error handling with appropriate exception types\n- Docstrings: Use Google style docstrings\n- **Never hold a DB session during external calls** (LLM, embedding, HTTP). If a function needs both a DB session and an external call result, compute the external result first and pass it as a parameter. This avoids tying up DB connections during slow network I/O. Use `tracked_db` for short-lived, DB-only operations; pass a shared session when multiple DB-only calls can reuse one connection.\n- **Never write through a read-only session** (`tracked_db(..., read_only=True)`, `get_read_db`, `ReadSessionLocal`). These run in AUTOCOMMIT mode with no transaction: writes are NOT blocked by the database — they silently commit immediately, and `begin_nested()` savepoints break. There is no runtime guard; this is enforced by convention only. Use `read_only=True` strictly for SELECT-only windows; anything that mutates (including get-or-create paths) must use a regular write session.\n\n#### Multi-row locking and deadlocks\n\nTables written concurrently by more than one worker — `documents` (deriver, dreamer, scope backfill/removal, reconciler) and `queue` (every deriver replica) — deadlock when two writers touch an overlapping row set in different orders. Rules:\n\n- **A multi-row `SELECT ... FOR UPDATE` MUST carry an explicit `ORDER BY <pk>`.** Without it Postgres locks in scan order, which differs per plan, so two writers with overlapping sets can cycle. `_apply_document_row_updates` in `src/crud/document.py` is the reference implementation.\n- **`WHERE id IN (...)` does NOT impose an order**, so sorting the Python list is a no-op — the list order is discarded and the planner picks `Bitmap Heap Scan` (ctid order), `Index Scan` (id order), or `Seq Scan` per invocation. Deterministic ordering requires either a preceding `SELECT ... ORDER BY id FOR UPDATE` or `WHERE id IN (SELECT id ... ORDER BY id FOR UPDATE)`.\n- **`Document.id` is a random nanoid** (`models.py`), so id order is uncorrelated with physical order — an unordered predicate `UPDATE`/`DELETE` is roughly a coin flip against an id-ordered locker per row pair, not a rare edge case. (`QueueItem.id` is an integer identity, so there id order is also chronological.)\n- **Prefer no lock at all.** A single `UPDATE ... WHERE <predicate>` acquires row locks as it writes and has no separate lock phase to get wrong. Reach for `FOR UPDATE` only when a value must be read, computed in Python, and written back — that read-modify-write is the only reason `_apply_document_row_updates` locks (it replaced a server-side `func.greatest()`), and `populate_existing=True` is required with it so the identity map doesn't serve a stale pre-lock value. Server-side expressions (`func.greatest`, the JSONB `-` operator) avoid the lock entirely; see `_clear_work_unit_retry_attempts` in `src/deriver/queue_manager.py`.\n- `FOR UPDATE SKIP LOCKED` (the reconciler's claim pattern) never waits, so it cannot be a deadlock partner — but holding those locks across an external call still stalls other writers. See the \"never hold a DB session during external calls\" rule above.\n\n#### Auth scoping\n\n- **Named-scope keys (`sc` plus `w`) require explicit `allow_scope=True` on supported routes.** This grants both recall and ingestion within that scope, not peer-member write access. Recall handlers must bind their projection to the token; session routes check active scope membership. Keep the named-scope route allowlist in `tests/routes/test_auth_route_policy.py` synchronized. Never allow restricted keys to enroll unrelated existing sessions or fall back to global recall.\n\n- **`allow_member_read=True` (in `require_auth(...)`) is read-only — NEVER set it on a route that mutates state.** It lets a peer-scoped key reach a session route when its peer is an active member of the session, so on a mutating route it would hand any session member write access (message injection, config mutation, deletion). HTTP method is not a reliable read/write signal here (some read routes use POST for a richer body), so this is enforced by an explicit allowlist in `tests/routes/test_auth_route_policy.py` — adding the flag to a new route fails that test until you consciously add the route to `EXPECTED_MEMBER_READ_ROUTES`, and you must never add a mutating method there.\n- **When a member-read route is keyed by another sub-resource** (e.g. `peers/{peer_id}/config`), the handler must additionally confirm a peer-scoped caller only reads its OWN resource (`jwt_params.p == peer_id`, else raise `AuthenticationException`). Membership grants session access, not access to a co-member's data. See `get_peer_config` in `src/routers/sessions.py`.\n\n### Runtime Architecture\n\nHoncho runs as two cooperating processes that share a Postgres database and Redis cache:\n\n- **API server** (`uv run fastapi dev src/main.py`) — handles HTTP, enqueues background work, returns immediately. Hosts the **Dialectic** agent inline (synchronous tool loop during chat requests).\n- **Deriver worker** (`uv run python -m src.deriver`) — long-running queue consumer (uvloop). Runs the **Deriver**, **Summarizer**, and **Dreamer** off the queue. Can run multiple instances (`DERIVER_WORKERS`). Also hosts an in-process **Reconciler scheduler** (`src/reconciler/`) that periodically embeds messages with `sync_state='pending'` in `MessageEmbedding` and cleans up stale queue items — embedding generation is decoupled from message creation by design.\n\n### Agent Architecture\n\nHoncho uses several specialized LLM agents. They share tool definitions and the LLM client abstraction in `src/utils/agent_tools.py` + `src/llm/`.\n\n> **Terminology:** what users see as **conclusions** (the public API surface and the term we use in documentation) is called **observations** in code symbols — `create_observations`, `delete_observations`, `get_observation_context`, etc. Doc prose below uses \"conclusions\"; references to actual code symbols stay as \"observations.\"\n\n#### 1. Deriver (`src/deriver/`)\n\n**Role**: Memory formation through content ingestion.\n\nThe Deriver processes batches of incoming messages and extracts conclusions about peers. The current architecture is \"minimal deriver\" — a **single LLM call** per batch using structured output, not an agentic tool loop. This trades flexibility for cost and predictability.\n\n- **Trigger**: Messages enqueued by `src/deriver/enqueue.py` on message create; consumed by `src/deriver/queue_manager.py` → `consumer.process_item()` → `deriver.process_representation_tasks_batch()`.\n- **Output**: Explicit conclusions (direct facts) and deductive conclusions (inferences) saved to `(observer, observed)` collections.\n- **Entry point**: `src/deriver/__main__.py` → `queue_manager.main()`.\n- **Prompts**: `src/deriver/prompts.py` (`minimal_deriver_prompt`).\n- **Custom instructions**: per-workspace/peer guidance can be threaded into the prompt via reasoning configuration; `DERIVER__MAX_CUSTOM_INSTRUCTIONS_TOKENS` caps the addition (default 2000) and `DERIVER__MAX_INPUT_TOKENS` defaults to 25000 to make room.\n\n#### 2. Dialectic (`src/dialectic/`)\n\n**Role**: Analysis and recall for answering queries.\n\nThe Dialectic answers questions about peers by strategically gathering context from memory. It is the only tool-using agent on the synchronous request path — it loops over `DIALECTIC_TOOLS` until it has enough context to answer. (The Dreamer specialists also use tools, but run off the queue.)\n\n- **Trigger**: API call to `POST /v3/.../peers/{peer_id}/chat`.\n- **Tools** (see `DIALECTIC_TOOLS` in `src/utils/agent_tools.py`): `search_memory`, `search_messages`, `get_observation_context`, `grep_messages`, `get_messages_by_date_range`, `search_messages_temporal`, `get_reasoning_chain`. At the `minimal` reasoning level, a reduced set (`DIALECTIC_TOOLS_MINIMAL`) is used: just `search_memory` + `search_messages`.\n- **Reasoning levels**: 5 tiers — `minimal`, `low`, `medium`, `high`, `max` — each with its own model config (see `DialecticLevelSettings` in `src/config.py`).\n- **Output**: Natural language response grounded in gathered context. Supports SSE streaming.\n- **Entry point**: `src/dialectic/chat.py` → `agentic_chat()` / `agentic_chat_stream()` → `DialecticAgent` (in `src/dialectic/core.py`).\n\n#### 3. Dreamer (`src/dreamer/`)\n\n**Role**: Consolidation and self-improvement of memory.\n\nThe Dreamer is an orchestrated multi-specialist system that runs during scheduled \"dreams\" to consolidate conclusions and build reasoning trees.\n\n- **Trigger**: Scheduled via `DreamScheduler` (`src/dreamer/dream_scheduler.py`) or explicit dream task on the queue.\n- **Strategy**: Surprisal-based prioritization (`src/dreamer/surprisal.py`) selects which conclusions to expand. The orchestrator (`orchestrator.run_dream`) runs two specialist phases:\n  1. **DeductionSpecialist** (`specialists.py`) — produces deductive conclusions from explicit conclusions. Tools: `get_recent_observations`, `search_memory`, `search_messages`, `create_observations_deductive`, `delete_observations`, `update_peer_card`.\n  2. **InductionSpecialist** — produces inductive conclusions from explicit + deductive conclusions. Tools: same discovery set + `create_observations_inductive`, `update_peer_card`.\n- **Reasoning trees** (`src/dreamer/trees/`, migration `f1a2b3c4d5e6_add_reasoning_tree_columns`): each conclusion links to its premises and downstream conclusions, enabling `get_reasoning_chain` traversal at recall time.\n- **Output**: Deductive/inductive conclusions, consolidated redundancies, updated peer cards.\n- **Entry point**: `src/dreamer/orchestrator.py` → `process_dream()` (the package-level export from `src/dreamer/__init__.py`), which wraps `run_dream()`.\n\n#### 4. Summarizer (`src/utils/summarizer.py`)\n\n**Role**: Two-tier session summarization (direct LLM call — no agentic tools).\n\n- **Trigger**: Runs as part of the queue pipeline alongside representation tasks.\n- **Tiers**: short summary every `SUMMARY_MESSAGES_PER_SHORT_SUMMARY` messages (default 20); long summary every `SUMMARY_MESSAGES_PER_LONG_SUMMARY` (default 60). Token caps configurable via `SUMMARY_MAX_TOKENS_SHORT` / `SUMMARY_MAX_TOKENS_LONG`.\n\n#### Shared Agent Infrastructure\n\n- **Tool definitions** (`src/utils/agent_tools.py`): unified `TOOLS` dict; per-agent lists (`DIALECTIC_TOOLS`, `DIALECTIC_TOOLS_MINIMAL`, `DREAMER_TOOLS`, `DEDUCTION_SPECIALIST_TOOLS`, `INDUCTION_SPECIALIST_TOOLS`).\n- **LLM subsystem** (`src/llm/`): provider-agnostic `honcho_llm_call()`. Backends in `src/llm/backends/` (`anthropic.py`, `gemini.py`, `openai.py`). Includes prompt caching (`caching.py`), structured output (`structured_output.py`), tool loop (`tool_loop.py`), history adapters for cross-provider message formats, and a model registry. Per-retry provider selection is pinned via an `AttemptPlan` so stream-final retries don't bounce back to primary after the tool loop has settled on fallback.\n- **Per-agent model config**: each agent has its own `MODEL_CONFIG` in `src/config.py` with fallback chains (see `ConfiguredModelSettings`, `FallbackModelSettings`).\n- **Telemetry**: cloudevents in `src/telemetry/events/` cover API routes, dialectic, dream, deletion, reconciliation, representation, and per-call LLM accounting (`llm.py` — `LLMCallCompletedEvent` fires once per provider hit with full cost-attribution context). High-volume events are sampled deterministically per `run_id` via `TelemetrySettings.HIGH_VOLUME_SAMPLE_RATE`.\n- **Prometheus metrics** (`src/telemetry/prometheus/`): every metric carries a `namespace` label and every recorder is fail-soft (a metrics error never propagates into a request or a worker loop). Counter children with a *bounded* label domain are zero-initialized per process at startup — `initialize_bounded_metrics(instance_type=...)`, called from the `src/main.py` lifespan (`api`) and `src/deriver/__main__.py` (`deriver`) — so an absent series means a broken scrape rather than \"nothing happened\". Two consequences worth knowing before touching telemetry:\n  - **Adding a `BaseEvent` subclass requires adding its `_event_type` to `ALL_EVENT_TYPES`** in `src/telemetry/events/__init__.py` (and to `HIGH_VOLUME_EVENT_TYPES` if `_volume_class == \"high_volume\"`). Enforced by the drift guards in `tests/telemetry/test_metric_zero_init.py`, which assert set-equality against the discovered subclasses.\n  - **A service-wide, non-additive gauge must be refreshed by every replica on its own timer**, and aggregated with `max()`/`avg()`, never `sum()`. `message_embeddings_pending` is the example: it reports a DB-global count, so it is driven from `ReconcilerScheduler._scheduler_loop` (runs on all replicas) rather than from the work-unit-deduped reconciliation cycle — otherwise, combined with the zero-init, every replica that never won the work unit would export a confident permanent `0`.\n\n### Project Structure\n\n```\nsrc/\n├── main.py              # FastAPI app: middleware, routers, lifespan, exception handlers\n├── models.py            # SQLAlchemy ORM models (Workspace/Peer/Session/Message/\n│                        #   MessageEmbedding/Collection/Document/QueueItem/...)\n├── config.py            # Pydantic-settings configuration (very large; see README)\n├── db.py                # Engine + session/context management (request_context var)\n├── dependencies.py      # FastAPI DI (tracked_db, etc.)\n├── exceptions.py        # Custom exception types (HonchoException + subclasses)\n├── security.py          # JWT authentication\n├── embedding_client.py  # Embedding provider client (configurable dimensions\n│                        #   via EMBEDDING_MODEL_CONFIG__DIMENSIONS_MODE)\n├── schemas/             # Pydantic schemas\n│   ├── api.py            # Public API request/response schemas\n│   ├── configuration.py  # Per-resource configuration schemas\n│   └── internal.py       # Internal-only schemas (queue payloads, etc.)\n├── crud/                # Per-resource DB operations\n│   ├── collection.py, deriver.py, document.py, message.py\n│   ├── peer.py, peer_card.py, representation.py  (RepresentationManager)\n│   ├── session.py, webhook.py, workspace.py\n├── routers/             # FastAPI route handlers (all under /v3)\n│   ├── workspaces.py, peers.py (dialectic /chat lives here), sessions.py\n│   ├── messages.py, conclusions.py, keys.py, webhooks.py\n├── dialectic/           # Dialectic agent — runs inline per chat request\n│   ├── chat.py           # agentic_chat() / agentic_chat_stream()\n│   ├── core.py           # DialecticAgent (the tool-loop driver)\n│   └── prompts.py\n├── deriver/             # Background queue consumer (separate process)\n│   ├── __main__.py       # `python -m src.deriver` entry point\n│   ├── queue_manager.py  # QueueManager + main() loop\n│   ├── consumer.py       # process_item dispatcher (representation / deletion / reconciler)\n│   ├── deriver.py        # \"minimal deriver\" — single-LLM-call batch processor\n│   ├── enqueue.py        # API → queue producer\n│   └── prompts.py\n├── dreamer/             # Memory consolidation (runs off the queue)\n│   ├── orchestrator.py   # run_dream() / process_dream()\n│   ├── specialists.py    # DeductionSpecialist + InductionSpecialist\n│   ├── dream_scheduler.py\n│   ├── surprisal.py      # Surprisal-based conclusion prioritization\n│   └── trees/            # Reasoning-tree primitives\n├── reconciler/          # In-process scheduler hosted by the deriver worker\n│   ├── scheduler.py      # ReconcilerScheduler (started from queue_manager.py)\n│   ├── sync_vectors.py   # Embeds MessageEmbedding rows with sync_state='pending'\n│   └── queue_cleanup.py  # Removes stale queue items\n├── llm/                 # Provider-agnostic LLM client subsystem\n│   ├── api.py, backend.py, executor.py, runtime.py, registry.py\n│   ├── caching.py, structured_output.py, tool_loop.py, conversation.py\n│   ├── history_adapters.py, request_builder.py, credentials.py, types.py\n│   └── backends/         # anthropic.py, gemini.py, openai.py\n├── cache/               # Redis cache abstraction (cashews-backed)\n│   └── client.py\n├── vector_store/        # Optional external vector stores (pgvector is default,\n│   │                    #   implemented via MessageEmbedding/Document in models+crud)\n│   ├── lancedb.py\n│   └── turbopuffer.py\n├── telemetry/           # Observability\n│   ├── emitter.py        # CloudEvents emitter\n│   ├── logging.py        # Logging helpers + route-template extraction\n│   ├── metrics_collector.py, reasoning_traces.py, sentry.py\n│   ├── events/           # Event type definitions\n│   └── prometheus/       # Prometheus metric definitions\n├── utils/               # Cross-cutting utilities\n│   ├── agent_tools.py    # Tool definitions + per-agent tool lists\n│   ├── summarizer.py     # Two-tier session summarizer\n│   ├── representation.py # Representation formatting (distinct from crud/representation.py)\n│   ├── search.py, filter.py, formatting.py\n│   ├── tokens.py         # tiktoken-based counting\n│   ├── pagination.py     # List pagination: opt-in cursor mode + the cached offset shim\n│   ├── work_unit.py, queue_payload.py\n│   ├── config_helpers.py, json_parser.py, files.py\n│   └── types.py\n└── webhooks/            # Webhook delivery\n    ├── events.py\n    └── webhook_delivery.py\n```\n\n- Tests in pytest with fixtures in tests/conftest.py; subdirs mirror src/ (`tests/deriver/`, `tests/dialectic/`, etc.) plus `tests/bench/` (perf benchmarks), `tests/integration/`, `tests/live_llm/` (gated by `--live-llm`), and `tests/unified/` (the unified runner).\n- Use environment variables via python-dotenv (.env). Config precedence: env > .env > config.toml > defaults.\n\n### Database Design\n\n- All tables use text IDs (nanoid format) as primary keys\n- Composite foreign keys for multi-tenant relationships\n- Feature flags on workspace, peer, and session levels\n- Token counting on messages for usage tracking\n- JSONB metadata fields for extensibility\n- HNSW indexes for vector similarity search\n- **A `Select` that feeds a list endpoint MUST have a total ORDER BY ending in a unique column** (e.g. `created_at, id`). `paginate_offset_or_cursor` (`src/utils/pagination.py`) seeks by the sort key in both cursor mode and the offset shim; without a unique tail, rows that share a sort key are skipped or repeated across pages.\n\n### Key Architectural Decisions\n\n1. **Peer Paradigm**: humans and AI agents are unified as \"Peers\"; many-to-many with Sessions. Internal vector storage (Collections/Documents) is keyed by `(observer, observed)` peer pairs — the same mechanism powers self-representation (`observer == observed`) and cross-peer modeling.\n2. **Multi-Peer Sessions**: Sessions can have multiple participants with different observation settings.\n3. **API server / worker split**: API enqueues, deriver worker process consumes. Never block HTTP on LLM work. The Reconciler runs as an in-process scheduler inside the deriver, handling async embedding sync and queue cleanup.\n4. **\"Minimal\" deriver**: memory formation is a single structured-output LLM call per batch, not an agentic tool loop. Predictable cost, lower latency. The Dialectic is the one true tool-using agent.\n5. **Provider-agnostic LLM layer** (`src/llm/`): all model calls go through `honcho_llm_call()`. Backends (`anthropic`, `gemini`, `openai`) sit behind a registry; per-agent `MODEL_CONFIG` with fallback chains is resolved at call time.\n6. **Dialectic reasoning tiers**: 5 levels (`minimal` → `max`); each level has its own model config and tool set (`minimal` uses a reduced toolset).\n7. **Hybrid search**: Postgres FTS (GIN index on `to_tsvector('english', content)`, OR'd with a `content ILIKE` fallback backed by a `pg_trgm` GIN index; without pg_trgm the index is skipped and the ILIKE scans) + vector similarity (HNSW on `MessageEmbedding.embedding`). `MessageEmbedding` is a separate table from `Message` with its own `sync_state` so embedding is decoupled from message creation.\n8. **Pluggable external vector stores**: defaults to pgvector inline; can swap to turbopuffer or lancedb (`VECTOR_STORE_*` config; `src/vector_store/`).\n9. **Composite-FK multi-tenancy**: `workspace_name` participates in nearly every composite FK. Cross-workspace data leakage is structurally impossible at the schema level.\n10. **Scoped Authentication**: JWTs can be scoped to workspace, peer, or session level.\n11. **Batch Operations**: Bulk message creation up to 100 messages per request.\n12. **Session History**: Two-tier summarization — short every `SUMMARY_MESSAGES_PER_SHORT_SUMMARY` (default 20), long every `SUMMARY_MESSAGES_PER_LONG_SUMMARY` (default 60).\n\n### Error Handling\n\n- Custom exceptions defined in src/exceptions.py\n- Use specific exception types (ResourceNotFoundException, ValidationException, etc.)\n- Proper logging with context instead of print statements\n- Global exception handlers defined in main.py\n- See docs/contributing/error-handling.mdx for details\n\n### Notes\n\n- Always use `uv run` or `uv` to prefix any commands related to python to ensure you use the virtual environment\n</yakaai_skills>"}