diff --git a/skills/honcho-integration/SKILL.md b/skills/honcho-integration/SKILL.md index ba9f13cb..ade9ff4a 100644 --- a/skills/honcho-integration/SKILL.md +++ b/skills/honcho-integration/SKILL.md @@ -12,7 +12,17 @@ Honcho is an open source memory library for building stateful agents. It works w The key mental model: **Peers** are any participant — human or AI. Both are represented the same way. Observation settings (`observe_me`, `observe_others`) control which peers Honcho reasons about. Typically you want Honcho to model your users (`observe_me=True`) but not your AI assistant (`observe_me=False`). **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. -Your agent accesses this memory through `peer.chat(query)` (ask a natural language question, get a reasoned answer), `session.context()` (get formatted conversation history + representations), or both. +Your 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. + +## Reference map + +Follow the workflow below. Read a reference file only when you reach the step that needs it: + +| When you're… | Read | +| --- | --- | +| Writing the client/peer/session setup (init, peers, sessions, add messages) | `references/core-patterns.md` | +| Wiring how the AI reads context (tool call, pre-fetch, `context()`, streaming) | `references/agent-patterns.md` | +| Integrating into a bot framework (nanobot, openclaw, picoclaw, …) | `references/bot-frameworks.md` + `references/bot-frameworks//` | ## Integration Workflow @@ -34,7 +44,7 @@ Use Glob and Grep to find: - User/session models or types - API routes handling chat or conversation endpoints -> **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 `{baseDir}/references/bot-frameworks.md` for framework-specific integration guidance and check `{baseDir}/references/bot-frameworks//` for concrete reference implementations. +> **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//` for concrete reference implementations. ### Phase 2: Interview (REQUIRED) @@ -51,7 +61,7 @@ Ask about which entities should be Honcho peers: #### Question Set 2 - Integration Pattern -Ask how they want to use Honcho context: +Ask how they want to use Honcho context (see `references/agent-patterns.md` for the implementation of each): - header: "Pattern" - question: "How should your AI access Honcho's user context?" @@ -82,11 +92,11 @@ If they chose pre-fetch, ask what context matters: Based on interview responses, implement the integration: -1. Install the SDK -2. Create Honcho client initialization -3. Set up peer creation for identified entities -4. Implement the chosen integration pattern(s) -5. Add message storage after exchanges +1. Install the SDK (see [Installation](#installation)) +2. Create Honcho client initialization — `references/core-patterns.md` §1 +3. Set up peer creation for identified entities — `references/core-patterns.md` §2–3 +4. Implement the chosen integration pattern(s) — `references/agent-patterns.md` +5. Add message storage after exchanges — `references/core-patterns.md` §4 6. Update any existing conversation handlers ### Phase 4: Verification @@ -132,394 +142,7 @@ uv add honcho-ai bun add @honcho-ai/sdk ``` -## Sync vs Async - -**TypeScript** — The SDK is async by default. All methods return promises. No separate sync API. - -**Python** — The SDK provides both sync and async interfaces: - -- **Sync** (default): `from honcho import Honcho` — use in sync frameworks (Flask, Django, CLI scripts) -- **Async**: `from honcho import Honcho` with `.aio` namespace — use in async frameworks (FastAPI, Starlette, async workers) - -```python -# Sync usage (Flask, Django, scripts) -from honcho import Honcho -honcho = Honcho(workspace_id="my-app", api_key=os.environ["HONCHO_API_KEY"]) -peer = honcho.peer("user-123") -response = peer.chat("What does this user prefer?") - -# Async usage (FastAPI, Starlette) -from honcho import Honcho -honcho = Honcho(workspace_id="my-app", api_key=os.environ["HONCHO_API_KEY"]) -peer = await honcho.aio.peer("user-123") -response = await peer.aio.chat("What does this user prefer?") -``` - -Match the client to the framework — check whether the codebase uses `async def` handlers or sync `def` handlers and choose accordingly. The rest of this skill shows sync Python examples; swap to `.aio` equivalents for async codebases. - -## Core Integration Patterns - -### 1. Initialize with a Single Workspace - -Use ONE workspace for your entire application. The workspace name should reflect your app/product. - -**Python:** - -```python -from honcho import Honcho -import os - -# Sync client (Flask, Django, scripts) -honcho = Honcho( - workspace_id="your-app-name", - api_key=os.environ["HONCHO_API_KEY"], - environment="production" -) - -# Async client (FastAPI, Starlette) — use honcho.aio for all operations -# honcho.aio.peer(), honcho.aio.session(), etc. -``` - -**TypeScript:** - -```typescript -import { Honcho } from '@honcho-ai/sdk'; - -// All methods are async by default -const honcho = new Honcho({ - workspaceId: "your-app-name", - apiKey: process.env.HONCHO_API_KEY, - environment: "production" -}); -``` - -### 2. Create Peers for ALL Entities - -Create peers for **every entity** in your business logic - users AND AI assistants. - -**Python:** - -```python -from honcho.api_types import PeerConfig - -# Human users -user = honcho.peer("user-123") - -# AI assistants - set observe_me=False so Honcho doesn't model the AI -assistant = honcho.peer("assistant", configuration=PeerConfig(observe_me=False)) -support_bot = honcho.peer("support-bot", configuration=PeerConfig(observe_me=False)) -``` - -**TypeScript:** - -```typescript -// Human users -const user = await honcho.peer("user-123"); - -// AI assistants - set observeMe=false so Honcho doesn't model the AI -const assistant = await honcho.peer("assistant", { configuration: { observeMe: false } }); -const supportBot = await honcho.peer("support-bot", { configuration: { observeMe: false } }); -``` - -### 3. Multi-Peer Sessions - -Sessions can have multiple participants. Configure observation settings per-peer. - -**Python:** - -```python -from honcho.api_types import SessionPeerConfig - -session = honcho.session("conversation-123") - -# User is observed (Honcho builds a model of them) -user_config = SessionPeerConfig(observe_me=True, observe_others=True) - -# AI is NOT observed (no model built of the AI) -ai_config = SessionPeerConfig(observe_me=False, observe_others=True) - -session.add_peers([ - (user, user_config), - (assistant, ai_config) -]) -``` - -**TypeScript:** - -```typescript -const session = await honcho.session("conversation-123"); - -await session.addPeers([ - [user, { observeMe: true, observeOthers: true }], - [assistant, { observeMe: false, observeOthers: true }] -]); -``` - -### 4. Add Messages to Sessions - -**Python:** - -```python -session.add_messages([ - user.message("I'm having trouble with my account"), - assistant.message("I'd be happy to help. What seems to be the issue?"), - user.message("I can't reset my password") -]) -``` - -**TypeScript:** - -```typescript -await session.addMessages([ - user.message("I'm having trouble with my account"), - assistant.message("I'd be happy to help. What seems to be the issue?"), - user.message("I can't reset my password") -]); -``` - -## Using Honcho for AI Agents - -### Pattern A: Dialectic Chat as a Tool Call (Recommended for Agents) - -Make Honcho's chat endpoint available as a **tool** for your AI agent. This lets the agent query user context on-demand. - -**Python (OpenAI function calling):** - -```python -import openai -from honcho import Honcho - -honcho = Honcho(workspace_id="my-app", api_key=os.environ["HONCHO_API_KEY"]) - -# Define the tool for your agent -honcho_tool = { - "type": "function", - "function": { - "name": "query_user_context", - "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.", - "parameters": { - "type": "object", - "properties": { - "query": { - "type": "string", - "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?'" - } - }, - "required": ["query"] - } - } -} - -def handle_honcho_tool_call(user_id: str, query: str) -> str: - """Execute the Honcho chat tool call.""" - peer = honcho.peer(user_id) - return peer.chat(query) - -# Use in your agent loop -def run_agent(user_id: str, user_message: str): - messages = [{"role": "user", "content": user_message}] - - response = openai.chat.completions.create( - model="gpt-4", - messages=messages, - tools=[honcho_tool] - ) - - # Handle tool calls - if response.choices[0].message.tool_calls: - for tool_call in response.choices[0].message.tool_calls: - if tool_call.function.name == "query_user_context": - import json - args = json.loads(tool_call.function.arguments) - result = handle_honcho_tool_call(user_id, args["query"]) - # Continue conversation with tool result... -``` - -**TypeScript (OpenAI function calling):** - -```typescript -import OpenAI from 'openai'; -import { Honcho } from '@honcho-ai/sdk'; - -const honcho = new Honcho({ - workspaceId: "my-app", - apiKey: process.env.HONCHO_API_KEY -}); - -const honchoTool: OpenAI.ChatCompletionTool = { - type: "function", - function: { - name: "query_user_context", - description: "Query Honcho to retrieve relevant context about the user based on their history and preferences.", - parameters: { - type: "object", - properties: { - query: { - type: "string", - description: "A natural language question about the user" - } - }, - required: ["query"] - } - } -}; - -async function handleHonchoToolCall(userId: string, query: string): Promise { - const peer = await honcho.peer(userId); - return await peer.chat(query); -} -``` - -### Pattern B: Pre-fetch Context with Targeted Queries - -For simpler integrations, fetch user context before the LLM call using pre-defined queries. - -**Python:** - -```python -def get_user_context_for_prompt(user_id: str) -> dict: - """Fetch key user attributes via targeted Honcho queries.""" - peer = honcho.peer(user_id) - - return { - "communication_style": peer.chat("What communication style does this user prefer? Be concise."), - "expertise_level": peer.chat("What is this user's technical expertise level? Be concise."), - "current_goals": peer.chat("What are this user's current goals or priorities? Be concise."), - "preferences": peer.chat("What key preferences should I know about this user? Be concise.") - } - -def build_system_prompt(user_context: dict) -> str: - return f"""You are a helpful assistant. Here's what you know about this user: - -Communication style: {user_context['communication_style']} -Expertise level: {user_context['expertise_level']} -Current goals: {user_context['current_goals']} -Key preferences: {user_context['preferences']} - -Tailor your responses accordingly.""" -``` - -**TypeScript:** - -```typescript -async function getUserContextForPrompt(userId: string): Promise> { - const peer = await honcho.peer(userId); - - const [style, expertise, goals, preferences] = await Promise.all([ - peer.chat("What communication style does this user prefer? Be concise."), - peer.chat("What is this user's technical expertise level? Be concise."), - peer.chat("What are this user's current goals or priorities? Be concise."), - peer.chat("What key preferences should I know about this user? Be concise.") - ]); - - return { - communicationStyle: style, - expertiseLevel: expertise, - currentGoals: goals, - preferences: preferences - }; -} -``` - -### Pattern C: Get Context for LLM Integration - -Use `context()` for conversation history with built-in LLM formatting. - -**Python:** - -```python -import openai - -session = honcho.session("conversation-123") -user = honcho.peer("user-123") -assistant = honcho.peer("assistant", configuration=PeerConfig(observe_me=False)) - -# Get context formatted for your LLM -context = session.context( - tokens=2000, - peer_target=user.id, # Include representation of this user - summary=True # Include conversation summaries -) - -# Convert to OpenAI format -messages = context.to_openai(assistant=assistant) - -# Or Anthropic format -# messages = context.to_anthropic(assistant=assistant) - -# Add the new user message -messages.append({"role": "user", "content": "What should I focus on today?"}) - -response = openai.chat.completions.create( - model="gpt-4", - messages=messages -) - -# Store the exchange -session.add_messages([ - user.message("What should I focus on today?"), - assistant.message(response.choices[0].message.content) -]) -``` - -**TypeScript:** - -```typescript -import OpenAI from 'openai'; - -const session = await honcho.session("conversation-123"); -const user = await honcho.peer("user-123"); -const assistant = await honcho.peer("assistant", { configuration: { observeMe: false } }); - -// Get context formatted for your LLM -const context = await session.context({ - tokens: 2000, - peerTarget: user.id, // Include representation of this user - summary: true // Include conversation summaries -}); - -// Convert to OpenAI format -const messages = context.toOpenAI(assistant); - -// Or Anthropic format -// const messages = context.toAnthropic(assistant); - -// Add the new user message -messages.push({ role: "user", content: "What should I focus on today?" }); - -const openai = new OpenAI(); -const response = await openai.chat.completions.create({ - model: "gpt-4", - messages -}); - -// Store the exchange -await session.addMessages([ - user.message("What should I focus on today?"), - assistant.message(response.choices[0].message.content!) -]); -``` - -## Streaming Responses - -**Python:** - -```python -stream = peer.chat_stream("What do we know about this user?") - -for chunk in stream: - print(chunk, end="", flush=True) -``` - -**TypeScript:** - -```typescript -const stream = await peer.chatStream("What do we know about this user?"); - -for await (const chunk of stream) { - process.stdout.write(chunk); -} -``` +The 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`. ## Integration Checklist diff --git a/skills/honcho-integration/references/agent-patterns.md b/skills/honcho-integration/references/agent-patterns.md new file mode 100644 index 00000000..021df64f --- /dev/null +++ b/skills/honcho-integration/references/agent-patterns.md @@ -0,0 +1,257 @@ +# Agent Access Patterns + +How 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`. + +- **Pattern A — Dialectic chat as a tool call** (recommended for agents): the agent decides when to query context on-demand. +- **Pattern B — Pre-fetch with targeted queries**: fetch a fixed set of attributes before each LLM call. +- **Pattern C — `context()` for LLM integration**: inject conversation history + representation into the prompt. + +> **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. + +## Pattern A: Dialectic Chat as a Tool Call (Recommended for Agents) + +Make Honcho's chat endpoint available as a **tool** for your AI agent. This lets the agent query user context on-demand. + +**Python (OpenAI function calling):** + +```python +import openai +from honcho import Honcho + +honcho = Honcho(workspace_id="my-app", api_key=os.environ["HONCHO_API_KEY"]) + +# Define the tool for your agent +honcho_tool = { + "type": "function", + "function": { + "name": "query_user_context", + "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.", + "parameters": { + "type": "object", + "properties": { + "query": { + "type": "string", + "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?'" + } + }, + "required": ["query"] + } + } +} + +def handle_honcho_tool_call(user_id: str, query: str) -> str: + """Execute the Honcho chat tool call.""" + peer = honcho.peer(user_id) + return peer.chat(query) + +# Use in your agent loop +def run_agent(user_id: str, user_message: str): + messages = [{"role": "user", "content": user_message}] + + response = openai.chat.completions.create( + model="gpt-4", + messages=messages, + tools=[honcho_tool] + ) + + # Handle tool calls + if response.choices[0].message.tool_calls: + for tool_call in response.choices[0].message.tool_calls: + if tool_call.function.name == "query_user_context": + import json + args = json.loads(tool_call.function.arguments) + result = handle_honcho_tool_call(user_id, args["query"]) + # Continue conversation with tool result... +``` + +**TypeScript (OpenAI function calling):** + +```typescript +import OpenAI from 'openai'; +import { Honcho } from '@honcho-ai/sdk'; + +const honcho = new Honcho({ + workspaceId: "my-app", + apiKey: process.env.HONCHO_API_KEY +}); + +const honchoTool: OpenAI.ChatCompletionTool = { + type: "function", + function: { + name: "query_user_context", + description: "Query Honcho to retrieve relevant context about the user based on their history and preferences.", + parameters: { + type: "object", + properties: { + query: { + type: "string", + description: "A natural language question about the user" + } + }, + required: ["query"] + } + } +}; + +async function handleHonchoToolCall(userId: string, query: string): Promise { + const peer = await honcho.peer(userId); + return await peer.chat(query); +} +``` + +## Pattern B: Pre-fetch Context with Targeted Queries + +For simpler integrations, fetch user context before the LLM call using pre-defined queries. + +**Python:** + +```python +def get_user_context_for_prompt(user_id: str) -> dict: + """Fetch key user attributes via targeted Honcho queries.""" + peer = honcho.peer(user_id) + + return { + "communication_style": peer.chat("What communication style does this user prefer? Be concise."), + "expertise_level": peer.chat("What is this user's technical expertise level? Be concise."), + "current_goals": peer.chat("What are this user's current goals or priorities? Be concise."), + "preferences": peer.chat("What key preferences should I know about this user? Be concise.") + } + +def build_system_prompt(user_context: dict) -> str: + return f"""You are a helpful assistant. Here's what you know about this user: + +Communication style: {user_context['communication_style']} +Expertise level: {user_context['expertise_level']} +Current goals: {user_context['current_goals']} +Key preferences: {user_context['preferences']} + +Tailor your responses accordingly.""" +``` + +**TypeScript:** + +```typescript +async function getUserContextForPrompt(userId: string): Promise> { + const peer = await honcho.peer(userId); + + const [style, expertise, goals, preferences] = await Promise.all([ + peer.chat("What communication style does this user prefer? Be concise."), + peer.chat("What is this user's technical expertise level? Be concise."), + peer.chat("What are this user's current goals or priorities? Be concise."), + peer.chat("What key preferences should I know about this user? Be concise.") + ]); + + return { + communicationStyle: style, + expertiseLevel: expertise, + currentGoals: goals, + preferences: preferences + }; +} +``` + +## Pattern C: Get Context for LLM Integration + +Use `context()` for conversation history with built-in LLM formatting. This is a near-instant read — no dialectic reasoning — so it's the cheapest way to ground each turn. + +**Python:** + +```python +import openai + +session = honcho.session("conversation-123") +user = honcho.peer("user-123") +assistant = honcho.peer("assistant", configuration=PeerConfig(observe_me=False)) + +# Get context formatted for your LLM +context = session.context( + tokens=2000, + peer_target=user.id, # Include representation of this user + summary=True # Include conversation summaries +) + +# Convert to OpenAI format +messages = context.to_openai(assistant=assistant) + +# Or Anthropic format +# messages = context.to_anthropic(assistant=assistant) + +# Add the new user message +messages.append({"role": "user", "content": "What should I focus on today?"}) + +response = openai.chat.completions.create( + model="gpt-4", + messages=messages +) + +# Store the exchange +session.add_messages([ + user.message("What should I focus on today?"), + assistant.message(response.choices[0].message.content) +]) +``` + +**TypeScript:** + +```typescript +import OpenAI from 'openai'; + +const session = await honcho.session("conversation-123"); +const user = await honcho.peer("user-123"); +const assistant = await honcho.peer("assistant", { configuration: { observeMe: false } }); + +// Get context formatted for your LLM +const context = await session.context({ + tokens: 2000, + peerTarget: user.id, // Include representation of this user + summary: true // Include conversation summaries +}); + +// Convert to OpenAI format +const messages = context.toOpenAI(assistant); + +// Or Anthropic format +// const messages = context.toAnthropic(assistant); + +// Add the new user message +messages.push({ role: "user", content: "What should I focus on today?" }); + +const openai = new OpenAI(); +const response = await openai.chat.completions.create({ + model: "gpt-4", + messages +}); + +// Store the exchange +await session.addMessages([ + user.message("What should I focus on today?"), + assistant.message(response.choices[0].message.content!) +]); +``` + +### What `context()` returns + +`session.context()` bundles the session-local view you can drop straight into an LLM call: + +- **Recent messages** from the session, trimmed to the `tokens` budget. +- **Conversation summaries** when `summary=True` — the two-tier short/long summaries so older turns still count without spending the full token budget. +- **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). + +The `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. + +## Streaming Responses + +```python +stream = peer.chat_stream("What do we know about this user?") + +for chunk in stream: + print(chunk, end="", flush=True) +``` + +```typescript +const stream = await peer.chatStream("What do we know about this user?"); + +for await (const chunk of stream) { + process.stdout.write(chunk); +} +``` diff --git a/skills/honcho-integration/references/core-patterns.md b/skills/honcho-integration/references/core-patterns.md new file mode 100644 index 00000000..16f172ab --- /dev/null +++ b/skills/honcho-integration/references/core-patterns.md @@ -0,0 +1,146 @@ +# Core Integration Patterns + +The 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`. + +## Sync vs Async + +**TypeScript** — The SDK is async by default. All methods return promises. No separate sync API. + +**Python** — The SDK provides both sync and async interfaces: + +- **Sync** (default): `from honcho import Honcho` — use in sync frameworks (Flask, Django, CLI scripts) +- **Async**: `from honcho import Honcho` with `.aio` namespace — use in async frameworks (FastAPI, Starlette, async workers) + +```python +# Sync usage (Flask, Django, scripts) +from honcho import Honcho +honcho = Honcho(workspace_id="my-app", api_key=os.environ["HONCHO_API_KEY"]) +peer = honcho.peer("user-123") +response = peer.chat("What does this user prefer?") + +# Async usage (FastAPI, Starlette) +from honcho import Honcho +honcho = Honcho(workspace_id="my-app", api_key=os.environ["HONCHO_API_KEY"]) +peer = await honcho.aio.peer("user-123") +response = await peer.aio.chat("What does this user prefer?") +``` + +Match 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. + +## 1. Initialize with a Single Workspace + +Use ONE workspace for your entire application. The workspace name should reflect your app/product. + +**Python:** + +```python +from honcho import Honcho +import os + +# Sync client (Flask, Django, scripts) +honcho = Honcho( + workspace_id="your-app-name", + api_key=os.environ["HONCHO_API_KEY"], + environment="production" +) + +# Async client (FastAPI, Starlette) — use honcho.aio for all operations +# honcho.aio.peer(), honcho.aio.session(), etc. +``` + +**TypeScript:** + +```typescript +import { Honcho } from '@honcho-ai/sdk'; + +// All methods are async by default +const honcho = new Honcho({ + workspaceId: "your-app-name", + apiKey: process.env.HONCHO_API_KEY, + environment: "production" +}); +``` + +## 2. Create Peers for ALL Entities + +Create peers for **every entity** in your business logic - users AND AI assistants. + +**Python:** + +```python +from honcho.api_types import PeerConfig + +# Human users +user = honcho.peer("user-123") + +# AI assistants - set observe_me=False so Honcho doesn't model the AI +assistant = honcho.peer("assistant", configuration=PeerConfig(observe_me=False)) +support_bot = honcho.peer("support-bot", configuration=PeerConfig(observe_me=False)) +``` + +**TypeScript:** + +```typescript +// Human users +const user = await honcho.peer("user-123"); + +// AI assistants - set observeMe=false so Honcho doesn't model the AI +const assistant = await honcho.peer("assistant", { configuration: { observeMe: false } }); +const supportBot = await honcho.peer("support-bot", { configuration: { observeMe: false } }); +``` + +## 3. Multi-Peer Sessions + +Sessions can have multiple participants. Configure observation settings per-peer. + +**Python:** + +```python +from honcho.api_types import SessionPeerConfig + +session = honcho.session("conversation-123") + +# User is observed (Honcho builds a model of them) +user_config = SessionPeerConfig(observe_me=True, observe_others=True) + +# AI is NOT observed (no model built of the AI) +ai_config = SessionPeerConfig(observe_me=False, observe_others=True) + +session.add_peers([ + (user, user_config), + (assistant, ai_config) +]) +``` + +**TypeScript:** + +```typescript +const session = await honcho.session("conversation-123"); + +await session.addPeers([ + [user, { observeMe: true, observeOthers: true }], + [assistant, { observeMe: false, observeOthers: true }] +]); +``` + +## 4. Add Messages to Sessions + +**Python:** + +```python +session.add_messages([ + user.message("I'm having trouble with my account"), + assistant.message("I'd be happy to help. What seems to be the issue?"), + user.message("I can't reset my password") +]) +``` + +**TypeScript:** + +```typescript +await session.addMessages([ + user.message("I'm having trouble with my account"), + assistant.message("I'd be happy to help. What seems to be the issue?"), + user.message("I can't reset my password") +]); +``` diff --git a/skills/honcho-mcp/SKILL.md b/skills/honcho-mcp/SKILL.md index a84c69aa..5d6927c6 100644 --- a/skills/honcho-mcp/SKILL.md +++ b/skills/honcho-mcp/SKILL.md @@ -51,7 +51,7 @@ add_messages_to_session session_id: "" ## Speed: reads vs. reasoning - **`chat` is the slow one** — it runs the dialectic (live reasoning over the user's memory), so it takes a few seconds. Use it when you need a reasoned answer, not for every turn. -- **`get_context` / `get_peer_context` / `get_representation` / `search` are reads** — near-instantaneous. Reach for these first when you just need the current representation or history; only call `chat` when you actually need reasoning. +- **`get_session_context` / `get_peer_context` / `get_representation` / `search` are reads** — near-instantaneous. Reach for these first when you just need the current representation or history; only call `chat` when you actually need reasoning. ## Reasoning levels diff --git a/skills/honcho-memory/SKILL.md b/skills/honcho-memory/SKILL.md index b749829a..cba8ebfd 100644 --- a/skills/honcho-memory/SKILL.md +++ b/skills/honcho-memory/SKILL.md @@ -26,11 +26,21 @@ Reasoning happens **asynchronously**. After you record a turn, don't poll or wai Do this every conversation. It's the whole skill. 1. **Once per conversation** — make sure there's a session with you and the user as peers (observe the user, don't observe yourself). -2. **Before responding, when personalization helps** — pull the user's current context (`get_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. +2. **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. 3. **After every exchange** — record both the user's message and your reply. This is what makes Honcho learn. Don't skip it. Optionally, when you learn a durable fact you don't want to wait for background reasoning to surface, **store a conclusion** directly. +## What you get back when you recall + +Three ways to pull memory, cheapest first: + +- **Representation** (`get_representation`) — Honcho's synthesized understanding of the user as text, ready to drop straight into a system prompt. Near-instant read. +- **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. +- **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. + +The 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 live in the path skills (`honcho-mcp`, `honcho-cli`) and the [chat docs](https://honcho.dev/docs/v3/documentation/features/chat.md). + --- ## Pick your access path @@ -59,7 +69,7 @@ You need a Honcho API key — get one free at (starts w - **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. - **Scope sessions so reasoning actually fires.** Reasoning is token-batched per peer (~1,000 tokens within a session). 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. Many tiny sessions each stall below the threshold and never get reasoned over. - **Don't block on reasoning.** It's asynchronous. Respond now; the representation will be richer next time. -- **Reads are cheap; reasoning isn't.** Fetching the representation/context (`get_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. +- **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. - **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. - **One workspace per app/user-context.** Don't scatter the same user's memory across multiple workspaces. - **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). diff --git a/skills/migrate-honcho-py/SKILL.md b/skills/migrate-honcho-py/SKILL.md index c9f8320e..c8155357 100644 --- a/skills/migrate-honcho-py/SKILL.md +++ b/skills/migrate-honcho-py/SKILL.md @@ -21,6 +21,8 @@ This skill migrates code from `honcho` Python SDK v1.6.0 to v2.1.1 (required for ## Quick Migration +The four changes below are the structural breaks you hit first — apply them inline. The remaining renames and smaller changes are one-line lookups in the [Quick Reference Table](#quick-reference-table); the full before/after for every change lives in [DETAILED-CHANGES.md](DETAILED-CHANGES.md). + ### 1. Update async architecture ```python @@ -98,177 +100,21 @@ session.get_configuration() client.get_configuration() ``` -### 5. Update method names +### Changes 5–15 (at a glance) -```python -# Before -peer.working_rep() -peer.get_context() -peer.get_sessions() -session.get_context() -session.get_summaries() -session.get_messages() -session.get_peers() -session.get_peer_config() -client.get_peers() -client.get_sessions() -client.get_workspaces() +The remaining breaks are one-line mappings in the [Quick Reference Table](#quick-reference-table) below, with full before/after in [DETAILED-CHANGES.md](DETAILED-CHANGES.md): -# After -peer.representation() -peer.context() -peer.sessions() -session.context() -session.summaries() -session.messages() -session.peers() -session.get_peer_configuration() -client.peers() -client.sessions() -client.workspaces() -``` - -### 6. Update streaming - -```python -# Before -response = peer.chat("query", stream=True) -for chunk in response: - print(chunk, end="") - -# After -stream = peer.chat_stream("query") -for chunk in stream: - print(chunk, end="") -``` - -### 7. Update queue status (formerly deriver) - -```python -# Before -from honcho_core.types import DeriverStatus - -status = client.get_deriver_status() -status = client.poll_deriver_status(timeout=300.0) # Removed! - -# After -from honcho.api_types import QueueStatusResponse - -status = client.queue_status() -# poll_deriver_status removed - implement polling manually if needed -``` - -### 8. Update representation parameters - -```python -# Before -rep = peer.working_rep( - include_most_derived=True, - max_observations=50 -) - -# After -rep = peer.representation( - include_most_frequent=True, - max_conclusions=50 -) -``` - -### 9. Move update_message to session - -```python -# Before -updated = client.update_message(message=msg, metadata={"key": "value"}, session="sess-id") - -# After -updated = session.update_message(message=msg, metadata={"key": "value"}) -``` - -### 10. Update card() return type and method name - -```python -# Before -card: str = peer.card() # Returns str - -# After (v2.0.0+) -card: list[str] | None = peer.get_card() # Returns list[str] | None -if card: - print("\n".join(card)) - -# peer.card() still works but is deprecated — use get_card() - -# New in v2.0.1: set_card() -peer.set_card(["Prefers dark mode", "Located in US"]) -``` - -### 11. Strict input validation (v2.0.2+) - -All input models now reject unknown fields via `extra="forbid"` Pydantic validation. Previously, misspelled or extraneous fields were silently ignored. - -```python -# Before (v2.0.1 and earlier) — silently ignored -peer = client.peer("user-1", configuration=PeerConfig(observe_mee=True)) # typo silently ignored - -# After (v2.0.2+) — raises ValidationError -peer = client.peer("user-1", configuration=PeerConfig(observe_mee=True)) # ValidationError! -``` - -### 12. peer() and session() always make API calls (v2.1.0+) - -**Breaking**: `peer()` and `session()` now always make a get-or-create API call. Previously, calling without metadata/configuration returned a lazy object with no API call. - -```python -# Before (v2.0.x) — no API call without options -peer = client.peer("user-123") # Lazy, no network request - -# After (v2.1.0+) — always hits the API -peer = client.peer("user-123") # Makes POST to /peers (get-or-create) - -# Async -peer = await client.aio.peer("user-123") # Also always hits API -``` - -### 13. New properties and methods (v2.1.0+) - -```python -# created_at on Peer and Session -peer = client.peer("user-123") -print(peer.created_at) # datetime | None - -session = client.session("sess-1") -print(session.created_at) # datetime | None - -# is_active on Session -print(session.is_active) # bool | None - -# get_message() on Session -msg = session.get_message("msg-id") -# Async: msg = await session.aio.get_message("msg-id") -``` - -### 14. Pagination parameters on list methods (v2.1.0+) - -All list methods now accept `page`, `size`, and `reverse` parameters: - -```python -# Before (v2.0.x) — only filters -peers_page = client.peers(filters={"metadata": {"role": "admin"}}) - -# After (v2.1.0+) — pagination controls -peers_page = client.peers( - filters={"metadata": {"role": "admin"}}, - page=2, - size=25, - reverse=True -) - -# Works on: client.peers(), client.sessions(), peer.sessions(), -# session.messages(), scope.list() -``` - -### 15. Broader HTTP retry logic (v2.1.1+) - -The SDK now retries on `httpx.TimeoutException`, `httpx.NetworkError`, and `httpx.RemoteProtocolError` (previously only `httpx.TimeoutException` and `httpx.ConnectError`). These are mapped to the SDK's `TimeoutError` and `ConnectionError` respectively. No code changes needed — this is transparent. +- **5. Method renames** — `get_context()`→`context()`, `get_sessions()`→`sessions()`, `get_messages()`→`messages()`, etc. (drop the `get_` prefix) +- **6. Streaming** — `chat("q", stream=True)` → `chat_stream("q")` +- **7. Queue status** — `get_deriver_status()`→`queue_status()`; `poll_deriver_status()` removed (poll manually) +- **8. Representation params** — `include_most_derived=`→`include_most_frequent=`, `max_observations=`→`max_conclusions=` +- **9. `update_message`** — moved from `client.update_message(..., session=)` to `session.update_message(...)` +- **10. Card** — `card(): str` → `get_card(): list[str] | None`; new `set_card(list[str])` (v2.0.1); `card()` deprecated +- **11. Strict validation** (v2.0.2) — unknown config fields now raise `ValidationError` instead of being ignored +- **12. `peer()`/`session()`** (v2.1.0) — now always make a get-or-create API call (previously lazy) +- **13. New properties** (v2.1.0) — `created_at`, `session.is_active`, `session.get_message(id)` +- **14. Pagination** (v2.1.0) — `page=`, `size=`, `reverse=` on all list methods +- **15. Retries** (v2.1.1) — broader HTTP retry coverage; transparent, no code changes ## Quick Reference Table