From bd6163d9e323d41b45742aa56d9bc206e7177611 Mon Sep 17 00:00:00 2001 From: ajspig <46900795+ajspig@users.noreply.github.com> Date: Mon, 10 Aug 2026 15:06:07 -0400 Subject: [PATCH] docs: adding honcho-memory skill (#784) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs: adding honcho-memory skill * fix: skills to point at llm friendly content * docs: split honcho-mcp skill out of honcho-memory; address PR review Restructure honcho-memory into a concepts/strategy hub that routes to per-connection path skills, and add a dedicated honcho-mcp skill holding the MCP-tool mechanics that previously lived inline. Addresses review feedback on #784: - honcho-memory step 2 now leads with fast context reads, with chat as the slower escalation - honcho-mcp adds a "Speed: reads vs reasoning" section, describes what each context call returns, and a reasoning-levels table - get_representation framed as a contextualized snapshot insertable into a system prompt - drop schedule_dream from the tool table (manual escape hatch, not routine guidance) - prune queue-status references from honcho-cli; document honcho-mcp in vibecoding skill registry Co-Authored-By: Claude Opus 4.8 (1M context) * refactor(skills): move skills to canonical top-level skills/ with .claude symlink Establish a single source of truth for agent skills. The real files now live in the top-level skills/ directory (the publishing convention used by Vercel, Supabase, and Cloudflare, and the tree Honcho's `npx skills add` distributes). .claude/skills becomes a symlink to ../skills so Claude Code discovery keeps working off the one tree — eliminating the parallel-copy sync burden. Co-Authored-By: Claude Opus 4.8 (1M context) * fix: splitting context into references & verifying content is consistent. * docs: fixing core language * docs: fixing core language * fix: language about observe_others * chore: adding .agents folder for codex * fix: add instructions.md into the mcp server & delete mcp skill in favor of including it in honcho-memory. * chore: remove migrate docs (can be found on older versions) --------- Co-authored-by: Claude Opus 4.8 (1M context) --- .agents/skills | 1 + .claude/skills | 1 + .claude/skills/honcho-integration/SKILL.md | 554 ---------------- .../migrate-honcho-py/DETAILED-CHANGES.md | 607 ------------------ .../migrate-honcho-py/MIGRATION-CHECKLIST.md | 155 ----- .claude/skills/migrate-honcho-py/SKILL.md | 358 ----------- .../migrate-honcho-ts/DETAILED-CHANGES.md | 583 ----------------- .../migrate-honcho-ts/MIGRATION-CHECKLIST.md | 147 ----- .claude/skills/migrate-honcho-ts/SKILL.md | 330 ---------- README.md | 2 +- .../documentation/introduction/vibecoding.mdx | 20 +- docs/v3/guides/integrations/mcp.mdx | 14 +- mcp/instructions.md | 10 + mcp/src/instructions.d.ts | 5 + mcp/src/server.ts | 12 +- mcp/wrangler.toml | 5 + .../skills => skills}/honcho-cli/SKILL.md | 6 +- skills/honcho-integration/SKILL.md | 179 ++++++ .../references/agent-patterns.md | 257 ++++++++ .../references/bot-frameworks.md | 2 +- .../bot-frameworks/nanobot/client.py | 0 .../bot-frameworks/nanobot/honcho_tool.py | 0 .../bot-frameworks/nanobot/session.py | 5 +- .../references/core-patterns.md | 157 +++++ skills/honcho-memory/SKILL.md | 89 +++ {.claude/skills => skills}/verify/SKILL.md | 0 26 files changed, 729 insertions(+), 2770 deletions(-) create mode 120000 .agents/skills create mode 120000 .claude/skills delete mode 100644 .claude/skills/honcho-integration/SKILL.md delete mode 100644 .claude/skills/migrate-honcho-py/DETAILED-CHANGES.md delete mode 100644 .claude/skills/migrate-honcho-py/MIGRATION-CHECKLIST.md delete mode 100644 .claude/skills/migrate-honcho-py/SKILL.md delete mode 100644 .claude/skills/migrate-honcho-ts/DETAILED-CHANGES.md delete mode 100644 .claude/skills/migrate-honcho-ts/MIGRATION-CHECKLIST.md delete mode 100644 .claude/skills/migrate-honcho-ts/SKILL.md create mode 100644 mcp/src/instructions.d.ts rename {.claude/skills => skills}/honcho-cli/SKILL.md (92%) create mode 100644 skills/honcho-integration/SKILL.md create mode 100644 skills/honcho-integration/references/agent-patterns.md rename {.claude/skills => skills}/honcho-integration/references/bot-frameworks.md (97%) rename {.claude/skills => skills}/honcho-integration/references/bot-frameworks/nanobot/client.py (100%) rename {.claude/skills => skills}/honcho-integration/references/bot-frameworks/nanobot/honcho_tool.py (100%) rename {.claude/skills => skills}/honcho-integration/references/bot-frameworks/nanobot/session.py (98%) create mode 100644 skills/honcho-integration/references/core-patterns.md create mode 100644 skills/honcho-memory/SKILL.md rename {.claude/skills => skills}/verify/SKILL.md (100%) diff --git a/.agents/skills b/.agents/skills new file mode 120000 index 00000000..42c5394a --- /dev/null +++ b/.agents/skills @@ -0,0 +1 @@ +../skills \ No newline at end of file diff --git a/.claude/skills b/.claude/skills new file mode 120000 index 00000000..42c5394a --- /dev/null +++ b/.claude/skills @@ -0,0 +1 @@ +../skills \ No newline at end of file diff --git a/.claude/skills/honcho-integration/SKILL.md b/.claude/skills/honcho-integration/SKILL.md deleted file mode 100644 index 9ed50194..00000000 --- a/.claude/skills/honcho-integration/SKILL.md +++ /dev/null @@ -1,554 +0,0 @@ ---- -name: honcho-integration -description: Integrate Honcho memory and social cognition into existing Python or TypeScript codebases. Use when adding Honcho SDK, setting up peers, configuring sessions, implementing the dialectic chat endpoint for AI agents, or wiring Honcho into bot frameworks (nanobot, openclaw, picoclaw, etc). -allowed-tools: Read, Glob, Grep, Bash(uv:*), Bash(bun:*), Bash(npm:*), Edit, Write, WebFetch, AskUserQuestion ---- - -# Honcho Integration Guide - -## What is Honcho - -Honcho 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. - -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. - -## Integration Workflow - -Follow these phases in order: - -### Phase 1: Codebase Exploration - -Before asking the user anything, explore the codebase to understand: - -1. **Language & Framework**: Is this Python or TypeScript? What frameworks are used (FastAPI, Express, Next.js, etc.)? -2. **Existing AI/LLM code**: Search for existing LLM integrations (OpenAI, Anthropic, LangChain, etc.) -3. **Entity structure**: Identify users, agents, bots, or other entities that interact -4. **Session/conversation handling**: How does the app currently manage conversations? -5. **Message flow**: Where are messages sent/received? What's the request/response cycle? - -Use Glob and Grep to find: - -- `**/*.py` or `**/*.ts` files with "openai", "anthropic", "llm", "chat", "message" -- 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. - -### Phase 2: Interview (REQUIRED) - -After exploring the codebase, use the **AskUserQuestion** tool to clarify integration requirements. Ask these questions (adapt based on what you learned in Phase 1): - -#### Question Set 1 - Entities & Peers - -Ask about which entities should be Honcho peers: - -- header: "Peers" -- question: "Which entities should Honcho track and build representations for?" -- options based on what you found (e.g., "End users only", "Users + AI assistant", "Users + multiple AI agents", "All participants including third-party services") -- Include a follow-up if they have multiple AI agents: should any AI peers be observed? - -#### Question Set 2 - Integration Pattern - -Ask how they want to use Honcho context: - -- header: "Pattern" -- question: "How should your AI access Honcho's user context?" -- options: - - "Tool call (Recommended)" - "Agent queries Honcho on-demand via function calling" - - "Pre-fetch" - "Fetch user context before each LLM call with predefined queries" - - "context()" - "Include conversation history and representations in prompt" - - "Multiple patterns" - "Combine approaches for different use cases" - -#### Question Set 3 - Session Structure - -Ask about conversation structure: - -- header: "Sessions" -- question: "How should conversations map to Honcho sessions?" -- 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") - -#### Question Set 4 - Specific Queries (if using pre-fetch pattern) - -If they chose pre-fetch, ask what context matters: - -- header: "Context" -- question: "What user context should be fetched for the AI?" -- multiSelect: true -- options: "Communication style", "Expertise level", "Goals/priorities", "Preferences", "Recent activity summary", "Custom queries" - -### Phase 3: Implementation - -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 -6. Update any existing conversation handlers - -### Phase 4: Verification - -- If the Honcho CLI is available, run `honcho doctor` to confirm connectivity before testing the integration code -- Use `honcho peer list` and `honcho peer chat` to verify peers exist and the dialectic endpoint works independently of the integration -- Ensure all message exchanges are stored to Honcho -- Verify AI peers have `observe_me=False` (unless user specifically wants AI observation) -- Check that the workspace ID is consistent across the codebase -- Confirm environment variable for API key is documented - ---- - -## Before You Start - -1. **Check the latest SDK versions** at - - Python SDK: `honcho-ai` - - TypeScript SDK: `@honcho-ai/sdk` - -2. **Get an API key** ask the user to get a Honcho API key from and add it to the environment. - -3. **Verify with the CLI** (optional but recommended). If the user has the Honcho CLI installed (`pip install honcho-cli`), they can validate their setup before writing any integration code: - - ```bash - honcho init # persist API key + URL to ~/.honcho/config.json - honcho doctor # verify connectivity, config, workspace health - honcho peer chat # test the dialectic endpoint interactively - ``` - - This is the fastest way to confirm the API key and URL are correct before debugging SDK code. - -## Installation - -### Python (use uv) - -```bash -uv add honcho-ai -``` - -### TypeScript (use bun) - -```bash -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); -} -``` - -## Integration Checklist - -When integrating Honcho into an existing codebase: - -- [ ] Install SDK with `uv add honcho-ai` (Python) or `bun add @honcho-ai/sdk` (TypeScript) -- [ ] Set up `HONCHO_API_KEY` environment variable -- [ ] Initialize Honcho client with a single workspace ID -- [ ] Create peers for all entities (users AND AI assistants) -- [ ] Set `observe_me=False` for AI peers -- [ ] Configure sessions with appropriate peer observation settings -- [ ] Choose integration pattern: - - [ ] Tool call pattern for agentic systems - - [ ] Pre-fetch pattern for simpler integrations - - [ ] context() for conversation history -- [ ] Store messages after each exchange to build user models -- [ ] (Optional) Run `honcho doctor` to verify connectivity before testing integration code -- [ ] (Optional) Use `honcho peer chat` to test dialectic queries independently - -## Common Mistakes to Avoid - -1. **Multiple workspaces**: Use ONE workspace per application -2. **Forgetting AI peers**: Create peers for AI assistants, not just users -3. **Observing AI peers**: Set `observe_me=False` for AI peers unless you specifically want Honcho to model your AI's behavior -4. **Not storing messages**: Always call `add_messages()` to feed Honcho's reasoning engine -5. **Blocking on processing**: Messages are processed asynchronously — don't poll or wait for reasoning to complete before continuing - -## Resources - -- Documentation: -- Latest SDK versions: -- API Reference: diff --git a/.claude/skills/migrate-honcho-py/DETAILED-CHANGES.md b/.claude/skills/migrate-honcho-py/DETAILED-CHANGES.md deleted file mode 100644 index 74e93a4b..00000000 --- a/.claude/skills/migrate-honcho-py/DETAILED-CHANGES.md +++ /dev/null @@ -1,607 +0,0 @@ -# Detailed API Changes - -## 1. Async Client Architecture (Major Change) - -The separate `AsyncHoncho`, `AsyncPeer`, and `AsyncSession` classes have been removed. Use the `.aio` accessor instead. - -### Before (v1.6.0) - -```python -from honcho import Honcho, AsyncHoncho, AsyncPeer, AsyncSession - -# Sync client -client = Honcho() - -# Async client - separate class -async_client = AsyncHoncho() -peer = await async_client.peer("user-123") -response = await peer.chat("query") -``` - -### After (v2.0.0) - -```python -from honcho import Honcho - -# Single client with .aio accessor for async operations -client = Honcho() - -# Sync operations -peer = client.peer("user-123") -response = peer.chat("query") - -# Async operations via .aio accessor -peer = await client.aio.peer("user-123") -response = await peer.aio.chat("query") - -# Async iteration -async for p in client.aio.peers(): - print(p.id) -``` - -**Migration steps:** - -1. Remove all `AsyncHoncho`, `AsyncPeer`, `AsyncSession` imports -2. Replace `AsyncHoncho()` with `Honcho()` and use `.aio` accessor -3. Replace `AsyncPeer` type hints with `Peer` -4. Replace `AsyncSession` type hints with `Session` -5. Access async methods via `.aio` property on instances - ---- - -## 2. Observations → Conclusions (Terminology Change) - -### Before (v1.6.0) - -```python -from honcho import Observation, ObservationScope, AsyncObservationScope - -# Access observations -scope = peer.observations -scope = peer.observations_of("other-peer") - -# List observations -obs_list = scope.list() - -# Query observations -results = scope.query("preferences") - -# Create observations -scope.create([{"content": "User likes dark mode", "session_id": "sess-1"}]) - -# Get representation from observations -rep = scope.get_representation() -``` - -### After (v2.0.0) - -```python -from honcho import Conclusion, ConclusionScope, ConclusionScopeAio - -# Access conclusions -scope = peer.conclusions -scope = peer.conclusions_of("other-peer") - -# List conclusions (now returns SyncPage, not list) -conclusions_page = scope.list() -for conclusion in conclusions_page: - print(conclusion.content) - -# Query conclusions -results = scope.query("preferences") - -# Create conclusions -scope.create([{"content": "User likes dark mode", "session_id": "sess-1"}]) - -# Get representation from conclusions -rep = scope.representation() # Returns str, not Representation object -``` - ---- - -## 3. Representation Type Change (Major Change) - -The `Representation` class has been removed. Representations are now simple strings. - -### Before (v1.6.0) - -```python -from honcho import Representation, ExplicitObservation, DeductiveObservation - -# Get working representation -rep: Representation = peer.working_rep() - -# Access explicit and deductive observations -for obs in rep.explicit: - print(obs.content, obs.created_at) - -for obs in rep.deductive: - print(obs.conclusion, obs.premises) - -# Check if empty -if rep.is_empty(): - print("No observations") - -# Merge representations -rep.merge_representation(other_rep) - -# Diff representations -diff = rep.diff_representation(other_rep) - -# String formatting -print(str(rep)) -print(rep.str_no_timestamps()) -print(rep.format_as_markdown()) -``` - -### After (v2.0.0) - -```python -# Get representation - now returns str directly -rep: str = peer.representation() - -# It's just a string now -print(rep) - -# Check if empty -if not rep: - print("No conclusions") -``` - -**Removed methods:** - -- `.explicit` property -- `.deductive` property -- `.is_empty()` -- `.merge_representation()` -- `.diff_representation()` -- `.str_no_timestamps()` -- `.format_as_markdown()` - ---- - -## 4. Configuration Parameter Rename - -All `config` parameters have been renamed to `configuration`, and configuration types are now strongly typed. - -### Before (v1.6.0) - -```python -# Creating resources with config -peer = client.peer("user-1", config={"observe_me": True}) -session = client.session("sess-1", config={"some_setting": True}) - -# Getting/setting config -config = peer.get_config() -peer.set_config({"observe_me": False}) - -config = session.get_config() -session.set_config({"some_setting": False}) - -config = client.get_config() -client.set_config({"workspace_setting": True}) - -# Message config parameter -msg = peer.message("Hello", config={"reasoning": {"enabled": True}}) -``` - -### After (v2.0.0) - -```python -from honcho.api_types import PeerConfig, SessionConfiguration, WorkspaceConfiguration - -# Creating resources with configuration (typed) -peer = client.peer("user-1", configuration=PeerConfig(observe_me=True)) -session = client.session("sess-1", configuration=SessionConfiguration()) - -# Getting/setting configuration (returns typed objects) -config: PeerConfig = peer.get_configuration() -peer.set_configuration(PeerConfig(observe_me=False)) - -config: SessionConfiguration = session.get_configuration() -session.set_configuration(SessionConfiguration()) - -config: WorkspaceConfiguration = client.get_configuration() -client.set_configuration(WorkspaceConfiguration()) - -# Message configuration parameter -msg = peer.message("Hello", configuration={"reasoning": {"enabled": True}}) -``` - ---- - -## 5. Streaming Chat API Change - -### Before (v1.6.0) - -```python -# Streaming via parameter -response = peer.chat("query", stream=True) -for chunk in response: - print(chunk, end="") - -final = response.get_final_response() -``` - -### After (v2.0.0) - -```python -# Streaming via separate method -stream = peer.chat_stream("query") -for chunk in stream: - print(chunk, end="") - -final = stream.get_final_response() - -# Non-streaming (no stream parameter needed) -response = peer.chat("query") -``` - ---- - -## 6. Deriver Status → Queue Status - -### Before (v1.6.0) - -```python -from honcho_core.types import DeriverStatus - -# Get status -status: DeriverStatus = client.get_deriver_status() -status = session.get_deriver_status() - -# Poll until complete -status = client.poll_deriver_status(timeout=300.0) -status = session.poll_deriver_status(timeout=300.0) - -# Access fields -print(status.pending_work_units) -print(status.in_progress_work_units) -``` - -### After (v2.0.0) - -```python -from honcho.api_types import QueueStatusResponse - -# Get status -status: QueueStatusResponse = client.queue_status() -status = session.queue_status() - -# Access fields (same as before) -print(status.pending_work_units) -print(status.in_progress_work_units) - -# poll_deriver_status has been removed - implement polling manually if needed: -import time - -def poll_until_complete(client, timeout=300.0): - start = time.time() - while time.time() - start < timeout: - status = client.queue_status() - if status.pending_work_units == 0 and status.in_progress_work_units == 0: - return status - time.sleep(1) - raise TimeoutError("Queue processing did not complete in time") -``` - ---- - -## 7. PeerContext Changes - -### Before (v1.6.0) - -```python -from honcho import PeerContext - -context: PeerContext = peer.get_context() - -# Access representation (was Representation object) -rep: Representation = context.representation -if rep: - print(rep.explicit) - print(rep.deductive) -``` - -### After (v2.0.0) - -```python -from honcho.api_types import PeerContextResponse - -context: PeerContextResponse = peer.context() - -# Access representation (now str) -rep: str | None = context.representation -if rep: - print(rep) -``` - ---- - -## 8. Card Method Return Type Change - -### Before (v1.6.0) - -```python -# card() returned str (joined with newlines) -card: str = peer.card() -print(card) # "line1\nline2\nline3" -``` - -### After (v2.0.0) - -```python -# card() returns list[str] | None -card: list[str] | None = peer.card() -if card: - print("\n".join(card)) # Join manually if needed -``` - ---- - -## 9. Message Update Location Change - -### Before (v1.6.0) - -```python -# Update message via client -updated = client.update_message( - message=msg, - metadata={"key": "value"}, - session="session-id" # Required if message is string ID -) -``` - -### After (v2.0.0) - -```python -# Update message via session -updated = session.update_message( - message=msg, - metadata={"key": "value"} -) -``` - ---- - -## 10. Removed: `core` Property - -### Before (v1.6.0) - -```python -# Access underlying Stainless-generated client -core_client = client.core -workspace = client.core.workspaces.get_or_create(id="custom-workspace") -``` - -### After (v2.0.0) - -```python -# The `core` property has been removed -# The SDK no longer uses a Stainless-generated client internally -# Use the SDK's public API directly -``` - ---- - -## 11. Environment Changes - -### Before (v1.6.0) - -```python -# Three environments available -client = Honcho(environment="local") -client = Honcho(environment="production") -client = Honcho(environment="demo") -``` - -### After (v2.0.0) - -```python -# Only two environments -client = Honcho(environment="local") -client = Honcho(environment="production") -# "demo" environment has been removed -``` - ---- - -## 12. Reasoning Level Parameter (New Feature) - -The chat method now supports a `reasoning_level` parameter: - -```python -# New in v2.0.0 -response = peer.chat( - "complex query", - reasoning_level="high" # "minimal", "low", "medium", "high", "max" -) - -stream = peer.chat_stream( - "complex query", - reasoning_level="max" -) -``` - ---- - -## 13. Import Changes Summary - -### Removed Imports - -```python -# These no longer exist in v2.0.0 -from honcho import AsyncHoncho # Use Honcho with .aio accessor -from honcho import AsyncPeer # Use Peer with .aio accessor -from honcho import AsyncSession # Use Session with .aio accessor -from honcho import Observation # Renamed to Conclusion -from honcho import ObservationScope # Renamed to ConclusionScope -from honcho import AsyncObservationScope # Renamed to ConclusionScopeAio -from honcho import Representation # Removed (now str) -from honcho import ExplicitObservation # Removed -from honcho import DeductiveObservation # Removed -from honcho import PeerContext # Use PeerContextResponse from api_types -``` - -### New Imports - -```python -from honcho import Conclusion, ConclusionScope -from honcho import ConclusionScopeAio -from honcho import HonchoAio, PeerAio, SessionAio # For type hints -from honcho import MessageCreateParams, Message - -# Typed configuration classes -from honcho.api_types import ( - PeerConfig, - SessionConfiguration, - WorkspaceConfiguration, - SessionPeerConfig, - QueueStatusResponse, - PeerContextResponse, -) -``` - -### Message Type Import Changes - -```python -# Before -from honcho_core.types.workspaces.sessions import MessageCreateParam -from honcho_core.types.workspaces.sessions.message import Message -from honcho.session import SessionPeerConfig - -# After -from honcho import Message, MessageCreateParams # Note: plural "Params" -from honcho.api_types import SessionPeerConfig -``` - -**Note:** `MessageCreateParam` (singular) is now `MessageCreateParams` (plural). - ---- - -## 14. Card Method Deprecation and set_card (v2.0.1) - -### Before (v2.0.0) - -```python -card: list[str] | None = peer.card() -``` - -### After (v2.0.1+) - -```python -# get_card() is the preferred method -card: list[str] | None = peer.get_card() - -# card() still works but emits a deprecation warning -card = peer.card() # Deprecated - -# New: set_card() -updated = peer.set_card(["Fact 1", "Fact 2"]) -updated = peer.set_card(["Fact 1"], target="other-peer") - -# Async variants -card = await peer.aio.get_card() -await peer.aio.set_card(["Fact 1"]) -``` - ---- - -## 15. Strict Input Validation (v2.0.2) - -All Pydantic input models now use `extra="forbid"`, raising `ValidationError` for unknown fields. - -```python -from honcho.api_types import PeerConfig - -# This now raises ValidationError instead of silently ignoring the typo -PeerConfig(observe_mee=True) # ValidationError: extra fields not permitted -``` - ---- - -## 16. peer() and session() Always Make API Calls (v2.1.0) - -### Before (v2.0.x) - -```python -# Without options: lazy object, no API call -peer = client.peer("user-123") -# peer.created_at was None - -# With options: made API call -peer = client.peer("user-123", metadata={"key": "value"}) -``` - -### After (v2.1.0+) - -```python -# Always makes a get-or-create API call -peer = client.peer("user-123") -# peer.created_at is now always populated - -# Async -peer = await client.aio.peer("user-123") -``` - -All Peer/Session objects now have `created_at` populated immediately after construction. - ---- - -## 17. New Properties: created_at, is_active (v2.1.0) - -```python -# Peer -peer = client.peer("user-123") -print(peer.created_at) # datetime | None - -# Session -session = client.session("sess-1") -print(session.created_at) # datetime | None -print(session.is_active) # bool | None - -# These are refreshed by get_metadata(), get_configuration(), and refresh() -peer.refresh() -session.refresh() -``` - ---- - -## 18. get_message() on Session (v2.1.0) - -```python -# Fetch a single message by ID -msg = session.get_message("msg-abc123") -print(msg.content, msg.created_at) - -# Async -msg = await session.aio.get_message("msg-abc123") -``` - ---- - -## 19. Pagination Parameters (v2.1.0) - -All list methods now accept `page`, `size`, and `reverse`: - -```python -# Defaults: page=1, size=50, reverse=False -peers_page = client.peers(page=2, size=25, reverse=True) - -# Returns SyncPage / AsyncPage with: -print(peers_page.total) # Total items -print(peers_page.pages) # Total pages -print(peers_page.has_next_page()) - -# Works on: -# client.peers(), client.sessions() -# peer.sessions() -# session.messages() -# scope.list() -``` - ---- - -## 20. Broader HTTP Retry Logic (v2.1.1) - -The SDK now catches `httpx.NetworkError` and `httpx.RemoteProtocolError` for retry in addition to `httpx.TimeoutException` and `httpx.ConnectError`. This is transparent — no code changes needed. diff --git a/.claude/skills/migrate-honcho-py/MIGRATION-CHECKLIST.md b/.claude/skills/migrate-honcho-py/MIGRATION-CHECKLIST.md deleted file mode 100644 index ef3254da..00000000 --- a/.claude/skills/migrate-honcho-py/MIGRATION-CHECKLIST.md +++ /dev/null @@ -1,155 +0,0 @@ -# Migration Checklist - -Use this checklist to track migration progress. Copy into your working notes and check off items as completed. - -## Dependencies - -- [ ] Update `honcho` package to v2.1.1 -- [ ] Remove any `honcho-core` imports - -## Async Architecture Changes - -- [ ] Remove `AsyncHoncho` imports → use `Honcho` with `.aio` accessor -- [ ] Remove `AsyncPeer` imports → use `Peer` with `.aio` accessor -- [ ] Remove `AsyncSession` imports → use `Session` with `.aio` accessor -- [ ] Update all async client usage to use `.aio` accessor pattern -- [ ] Update type hints: `AsyncPeer` → `Peer`, `AsyncSession` → `Session` - -## Terminology: Observations → Conclusions - -- [ ] Replace `Observation` import with `Conclusion` -- [ ] Replace `ObservationScope` import with `ConclusionScope` -- [ ] Replace `AsyncObservationScope` import with `ConclusionScopeAio` -- [ ] Replace `.observations` property with `.conclusions` -- [ ] Replace `.observations_of()` method with `.conclusions_of()` -- [ ] Replace `.get_representation()` with `.representation()` - -## Representation Changes - -- [ ] Remove `Representation` import (now returns `str`) -- [ ] Remove `ExplicitObservation` import -- [ ] Remove `DeductiveObservation` import -- [ ] Replace `working_rep()` with `representation()` -- [ ] Update type hints from `Representation` to `str` -- [ ] Remove `.explicit` property access -- [ ] Remove `.deductive` property access -- [ ] Replace `.is_empty()` checks with `not rep` -- [ ] Remove `.merge_representation()` calls -- [ ] Remove `.diff_representation()` calls -- [ ] Remove `.str_no_timestamps()` calls -- [ ] Remove `.format_as_markdown()` calls - -## Configuration Changes - -- [ ] Replace all `config=` parameters with `configuration=` -- [ ] Replace `.get_config()` with `.get_configuration()` -- [ ] Replace `.set_config()` with `.set_configuration()` -- [ ] Rename `.get_peer_config()` → `.get_peer_configuration()` -- [ ] Rename `.set_peer_config()` → `.set_peer_configuration()` -- [ ] Import typed config classes from `honcho.api_types` if needed: - - [ ] `PeerConfig` - - [ ] `SessionConfiguration` - - [ ] `WorkspaceConfiguration` - -## Method Renames - -### Peer Methods - -- [ ] `peer.working_rep()` → `peer.representation()` -- [ ] `peer.get_context()` → `peer.context()` -- [ ] `peer.get_sessions()` → `peer.sessions()` -- [ ] `peer.chat(stream=True)` → `peer.chat_stream()` - -### Session Methods - -- [ ] `session.get_context()` → `session.context()` -- [ ] `session.get_summaries()` → `session.summaries()` -- [ ] `session.get_messages()` → `session.messages()` -- [ ] `session.get_peers()` → `session.peers()` -- [ ] `session.get_peer_config()` → `session.get_peer_configuration()` -- [ ] `session.set_peer_config()` → `session.set_peer_configuration()` -- [ ] `session.working_rep()` → `session.representation()` -- [ ] `session.get_deriver_status()` → `session.queue_status()` -- [ ] Remove `session.poll_deriver_status()` calls - -### Client Methods - -- [ ] `client.get_peers()` → `client.peers()` -- [ ] `client.get_sessions()` → `client.sessions()` -- [ ] `client.get_workspaces()` → `client.workspaces()` -- [ ] `client.get_deriver_status()` → `client.queue_status()` -- [ ] Remove `client.poll_deriver_status()` calls -- [ ] Move `client.update_message()` → `session.update_message()` - -## Parameter Renames - -- [ ] `include_most_derived=` → `include_most_frequent=` -- [ ] `max_observations=` → `max_conclusions=` -- [ ] `last_user_message=` → `search_query=` - -## Return Type Changes - -- [ ] Handle `card()` returning `list[str] | None` instead of `str` -- [ ] Handle `.list()` on conclusions returning `SyncPage` instead of `list` - -## Removed Features - -- [ ] Remove any usage of `client.core` property -- [ ] Remove usage of `"demo"` environment (only `"local"` and `"production"` remain) -- [ ] Implement custom polling if you were using `poll_deriver_status()` - -## Type Import Updates - -- [ ] Replace `PeerContext` import with `PeerContextResponse` from `honcho.api_types` -- [ ] Replace `DeriverStatus` import with `QueueStatusResponse` from `honcho.api_types` -- [ ] Replace `MessageCreateParam` with `MessageCreateParams` (plural) -- [ ] Move `SessionPeerConfig` import from `honcho.session` to `honcho.api_types` - -## Exception Handling (Optional) - -- [ ] Update exception handling to use new exception types if needed: - - `HonchoError`, `APIError`, `BadRequestError`, `AuthenticationError` - - `PermissionDeniedError`, `NotFoundError`, `ConflictError` - - `UnprocessableEntityError`, `RateLimitError`, `ServerError` - - `TimeoutError`, `ConnectionError` - -## Card Method Updates (v2.0.1) - -- [ ] Replace `peer.card()` with `peer.get_card()` (card() is deprecated) -- [ ] Use `peer.set_card(list[str])` if setting peer cards - -## Strict Validation (v2.0.2) - -- [ ] Verify no input models pass unknown/misspelled fields (now raises `ValidationError`) -- [ ] Check for typos in `PeerConfig`, `SessionConfiguration`, `WorkspaceConfiguration` fields - -## peer() / session() API Call Change (v2.1.0) - -- [ ] Update code that relied on lazy `peer()` / `session()` — they now always make API calls -- [ ] Add `await` if using async and previously didn't need it for lazy construction - -## New Properties (v2.1.0) - -- [ ] Use `peer.created_at` / `session.created_at` where creation time is needed -- [ ] Use `session.is_active` where session active status is needed - -## New Methods (v2.1.0) - -- [ ] Use `session.get_message(message_id)` to fetch single messages by ID - -## Pagination Parameters (v2.1.0) - -- [ ] Add `page`, `size`, `reverse` parameters to list calls where needed: - - [ ] `client.peers()` - - [ ] `client.sessions()` - - [ ] `peer.sessions()` - - [ ] `session.messages()` - - [ ] `scope.list()` - -## Final Verification - -- [ ] Run type checker (mypy/pyright) with no errors -- [ ] Run tests -- [ ] Verify async operations work with `.aio` accessor -- [ ] Verify streaming functionality works with `chat_stream()` -- [ ] Verify configuration changes take effect diff --git a/.claude/skills/migrate-honcho-py/SKILL.md b/.claude/skills/migrate-honcho-py/SKILL.md deleted file mode 100644 index c9f8320e..00000000 --- a/.claude/skills/migrate-honcho-py/SKILL.md +++ /dev/null @@ -1,358 +0,0 @@ ---- -name: migrate-honcho -description: Migrates Honcho Python SDK code from v1.6.0 to v2.1.1. Use when upgrading honcho package, fixing breaking changes after upgrade, or when errors mention AsyncHoncho, observations, Representation class, .core property, or get_config methods. ---- - -# Honcho Python SDK Migration (v1.6.0 → v2.1.1) - -## Overview - -This skill migrates code from `honcho` Python SDK v1.6.0 to v2.1.1 (required for Honcho 3.0.0+). - -**Key breaking changes:** - -- `AsyncHoncho`/`AsyncPeer`/`AsyncSession` removed → use `.aio` accessor -- "Observation" → "Conclusion" terminology -- `Representation` class removed (returns `str` now) -- `get_config`/`set_config` → `get_configuration`/`set_configuration` -- Streaming via `chat_stream()` instead of `chat(stream=True)` -- `poll_deriver_status()` removed -- `.core` property removed - -## Quick Migration - -### 1. Update async architecture - -```python -# Before -from honcho import AsyncHoncho, AsyncPeer, AsyncSession - -async_client = AsyncHoncho() -peer = await async_client.peer("user-123") -response = await peer.chat("query") - -# After -from honcho import Honcho - -client = Honcho() -peer = await client.aio.peer("user-123") -response = await peer.aio.chat("query") - -# Async iteration -async for p in client.aio.peers(): - print(p.id) -``` - -### 2. Replace observations with conclusions - -```python -# Before -from honcho import Observation, ObservationScope, AsyncObservationScope - -scope = peer.observations -scope = peer.observations_of("other-peer") -rep = scope.get_representation() - -# After -from honcho import Conclusion, ConclusionScope, ConclusionScopeAio - -scope = peer.conclusions -scope = peer.conclusions_of("other-peer") -rep = scope.representation() # Returns str -``` - -### 3. Update representation handling - -```python -# Before -from honcho import Representation, ExplicitObservation, DeductiveObservation - -rep: Representation = peer.working_rep() -print(rep.explicit) -print(rep.deductive) -if rep.is_empty(): - print("No observations") - -# After -rep: str = peer.representation() -print(rep) # Just a string now -if not rep: - print("No conclusions") -``` - -### 4. Rename configuration methods - -```python -# Before -config = peer.get_config() -peer.set_config({"observe_me": False}) -session.get_config() -client.get_config() - -# After -from honcho.api_types import PeerConfig, SessionConfiguration, WorkspaceConfiguration - -config = peer.get_configuration() -peer.set_configuration(PeerConfig(observe_me=False)) -session.get_configuration() -client.get_configuration() -``` - -### 5. Update method names - -```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() - -# 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. - -## Quick Reference Table - -| v1.6.0 | v2.0.0 | -|--------|--------| -| `AsyncHoncho()` | `Honcho()` + `.aio` accessor | -| `AsyncPeer` | `Peer` + `.aio` accessor | -| `AsyncSession` | `Session` + `.aio` accessor | -| `Observation` | `Conclusion` | -| `ObservationScope` | `ConclusionScope` | -| `AsyncObservationScope` | `ConclusionScopeAio` | -| `Representation` | `str` | -| `.observations` | `.conclusions` | -| `.observations_of()` | `.conclusions_of()` | -| `.get_config()` | `.get_configuration()` | -| `.set_config()` | `.set_configuration()` | -| `.working_rep()` | `.representation()` | -| `.get_context()` | `.context()` | -| `.get_sessions()` | `.sessions()` | -| `.get_peers()` | `.peers()` | -| `.get_messages()` | `.messages()` | -| `.get_summaries()` | `.summaries()` | -| `.get_deriver_status()` | `.queue_status()` | -| `.poll_deriver_status()` | *(removed)* | -| `.get_peer_config()` | `.get_peer_configuration()` | -| `.set_peer_config()` | `.set_peer_configuration()` | -| `client.update_message()` | `session.update_message()` | -| `peer.card()` | `peer.get_card()` *(card() deprecated)* | -| *(new)* | `peer.set_card(list[str])` | -| `chat(stream=True)` | `chat_stream()` | -| `include_most_derived=` | `include_most_frequent=` | -| `max_observations=` | `max_conclusions=` | -| `last_user_message=` | `search_query=` | -| `config=` | `configuration=` | -| `PeerContext` | `PeerContextResponse` | -| `DeriverStatus` | `QueueStatusResponse` | -| `client.core` | *(removed)* | -| *(new v2.1.0)* | `peer.created_at` / `session.created_at` | -| *(new v2.1.0)* | `session.is_active` | -| *(new v2.1.0)* | `session.get_message(id)` | -| *(new v2.1.0)* | `page=`, `size=`, `reverse=` on list methods | - -## Detailed Reference - -For comprehensive details on each change, see: - -- [DETAILED-CHANGES.md](DETAILED-CHANGES.md) - Full API change documentation -- [MIGRATION-CHECKLIST.md](MIGRATION-CHECKLIST.md) - Step-by-step checklist - -## New Exception Types - -```python -from honcho import ( - HonchoError, - APIError, - BadRequestError, - AuthenticationError, - PermissionDeniedError, - NotFoundError, - ConflictError, - UnprocessableEntityError, - RateLimitError, - ServerError, - TimeoutError, - ConnectionError, -) -``` - -## New Import Locations - -```python -# Configuration types -from honcho.api_types import ( - PeerConfig, - SessionConfiguration, - WorkspaceConfiguration, - SessionPeerConfig, - QueueStatusResponse, - PeerContextResponse, -) - -# Async type hints -from honcho import HonchoAio, PeerAio, SessionAio - -# Message types (note: Params is plural now) -from honcho import Message, MessageCreateParams -``` diff --git a/.claude/skills/migrate-honcho-ts/DETAILED-CHANGES.md b/.claude/skills/migrate-honcho-ts/DETAILED-CHANGES.md deleted file mode 100644 index 9133756e..00000000 --- a/.claude/skills/migrate-honcho-ts/DETAILED-CHANGES.md +++ /dev/null @@ -1,583 +0,0 @@ -# Detailed API Changes - -## Client Changes - -### `.core` Property Removed - -The `.core` property (which exposed the raw `@honcho-ai/core` client) has been removed. Use `.http` for advanced HTTP access. - -```typescript -// Before -const workspace = await client.core.workspaces.getOrCreate({ id: 'my-workspace' }) - -// After - SDK handles workspace creation automatically -// For advanced usage: -const response = await client.http.post('/v3/workspaces', { body: { id: 'my-workspace' } }) -``` - -### Listing Methods Return Type Changes - -- `workspaces()` now returns `Page` instead of `string[]` -- `session.peers()` now returns `Peer[]` instead of `Page` - -```typescript -const workspacePage = await honcho.workspaces() -for (const id of workspacePage.items) { - console.log(id) -} -``` - -### `updateMessage()` Moved to Session - -```typescript -// Before -await honcho.updateMessage(message, { key: 'value' }, session) - -// After -await session.updateMessage(message, { key: 'value' }) -``` - -### `config` Option Renamed to `configuration` - -```typescript -// Before -const peer = await honcho.peer('user-id', { config: { observe_me: true } }) -const session = await honcho.session('session-id', { config: { ... } }) - -// After -const peer = await honcho.peer('user-id', { configuration: { observeMe: true } }) -const session = await honcho.session('session-id', { configuration: { reasoning: { enabled: true } } }) -``` - ---- - -## Peer Changes - -### Streaming API - -The `stream` option on `chat()` has been removed. Use `chatStream()` instead. - -```typescript -// Before -const stream = await peer.chat('Hello', { stream: true }) -for await (const chunk of stream) { - process.stdout.write(chunk) -} - -// After -const stream = await peer.chatStream('Hello') -for await (const chunk of stream) { - process.stdout.write(chunk) -} -``` - -Non-streaming `chat()` now only returns `string | null`: - -```typescript -const response = await peer.chat('Hello') // Returns string | null -``` - -### New `reasoningLevel` Option - -```typescript -const response = await peer.chat('Complex question', { - reasoningLevel: 'high' // 'minimal' | 'low' | 'medium' | 'high' | 'max' -}) -``` - -### `workingRep()` Renamed to `representation()` - -```typescript -// Before -const rep = await peer.workingRep(session, target, options) -console.log(rep.toString()) -console.log(rep.explicit) -console.log(rep.deductive) - -// After -const rep = await peer.representation({ - session, - target, - searchQuery: options?.searchQuery, - maxConclusions: options?.maxObservations, - includeMostFrequent: options?.includeMostDerived, -}) -console.log(rep) // Returns string directly -``` - -### `getContext()` Renamed to `context()` - -Options are now passed as a single object: - -```typescript -// Before -const ctx = await peer.getContext(target, options) - -// After -const ctx = await peer.context({ target, ...options }) -``` - -### `card()` Return Type Changed - -```typescript -// Before -const card = await peer.card(target) // Returns string - -// After -const card = await peer.card(target) // Returns string[] | null -``` - -### `message()` Options Changed - -```typescript -// Before -const msg = peer.message('Hello', { - metadata: { key: 'value' }, - configuration: { deriver: { enabled: true } }, - created_at: '2024-01-01T00:00:00Z' -}) -// Returns ValidatedMessageCreate with peer_id, created_at - -// After -const msg = peer.message('Hello', { - metadata: { key: 'value' }, - configuration: { reasoning: { enabled: true } }, - createdAt: '2024-01-01T00:00:00Z' -}) -// Returns MessageInput with peerId, createdAt -``` - -### `PeerContext.representation` Type Changed - -```typescript -// Before -const ctx = await peer.getContext() -if (ctx.representation) { - console.log(ctx.representation.explicit) // Representation object - console.log(ctx.representation.deductive) -} - -// After -const ctx = await peer.context() -if (ctx.representation) { - console.log(ctx.representation) // Now a string -} -``` - ---- - -## Session Changes - -### `getPeers()` Return Type Changed - -```typescript -// Before -const peers = await session.getPeers() // Returns Page - -// After -const peers = await session.peers() // Returns Peer[] -``` - -### `getContext()` Renamed to `context()` - -```typescript -// Before -const ctx = await session.getContext({ - summary: true, - peerTarget: user, - peerPerspective: assistant, - lastUserMessage: "What are my preferences?", - representationOptions: { - maxObservations: 50, - includeMostDerived: true - } -}) - -// After -const ctx = await session.context({ - summary: true, - peerTarget: user, - peerPerspective: assistant, - searchQuery: "What are my preferences?", - representationOptions: { - maxConclusions: 50, - includeMostFrequent: true - } -}) -``` - -### `SessionPeerConfig` Uses camelCase and Methods Renamed - -```typescript -// Before -await session.setPeerConfig(peer, { - observe_me: true, - observe_others: false -}) -const config = await session.peerConfig(peer) - -// After -await session.setPeerConfiguration(peer, { - observeMe: true, - observeOthers: false -}) -const config = await session.getPeerConfiguration(peer) -``` - ---- - -## Message Changes - -### Message Properties Use camelCase - -```typescript -// Before (from @honcho-ai/core) -message.peer_id -message.session_id -message.workspace_id -message.created_at -message.token_count - -// After -message.peerId -message.sessionId -message.workspaceId -message.createdAt -message.tokenCount -``` - -### MessageInput Type - -```typescript -// Before -interface ValidatedMessageCreate { - peer_id: string - content: string - metadata?: Record - configuration?: Record - created_at?: string -} - -// After -interface MessageInput { - peerId: string - content: string - metadata?: Record - configuration?: MessageConfiguration - createdAt?: string -} -``` - ---- - -## Streaming Changes - -### `DialecticStreamDelta` Removed - -```typescript -// Before -import { DialecticStreamDelta, DialecticStreamChunk } from '@honcho-ai/sdk' - -// After -import { DialecticStreamChunk, DialecticStreamResponse } from '@honcho-ai/sdk' -``` - ---- - -## Configuration Changes - -### Workspace Configuration - -Configurations are now strongly typed objects instead of `Record`. - -```typescript -// Before -await honcho.setConfig({ - deriver: { enabled: true }, - some_custom_key: 'value' -}) - -// After -await honcho.setConfiguration({ - reasoning: { - enabled: true, - customInstructions: 'Be concise' - }, - peerCard: { - use: true, - create: true - }, - summary: { - enabled: true, - messagesPerShortSummary: 20, - messagesPerLongSummary: 60 - }, - dream: { - enabled: true - } -}) -``` - -### Peer Configuration - -```typescript -// Before -await peer.setConfig({ observe_me: false }) - -// After -await peer.setConfiguration({ observeMe: false }) -``` - -### Message Configuration - -```typescript -// Before -peer.message('Hello', { - configuration: { - deriver: { enabled: true } - } -}) - -// After -peer.message('Hello', { - configuration: { - reasoning: { - enabled: true, - customInstructions: 'Focus on emotions' - } - } -}) -``` - ---- - -## Type Changes - -### Removed Exports - -- `Observation` (use `Conclusion`) -- `ObservationScope` (use `ConclusionScope`) -- `ObservationData`, `ObservationCreateParam`, `ObservationQueryParams` -- `Representation`, `RepresentationData`, `RepresentationOptions` (class removed) -- `ExplicitObservation`, `DeductiveObservation` -- `DialecticStreamDelta` -- `DeriverStatusOptions` (use `QueueStatusOptions`) -- `MessageCreate` (use `MessageInput`) -- `WorkingRepParams` - -### New Exports - -```typescript -import { - // Domain classes - Conclusion, - ConclusionScope, - ConclusionCreateParams, - - // Error types - HonchoError, - AuthenticationError, - BadRequestError, - NotFoundError, - PermissionDeniedError, - RateLimitError, - ConflictError, - UnprocessableEntityError, - ServerError, - ConnectionError, - TimeoutError, - - // Message types - Message, - MessageInput, - - // Configuration types - WorkspaceConfig, - SessionConfig, - PeerConfig, - SessionPeerConfig, - MessageConfiguration, - ReasoningConfig, - PeerCardConfig, - SummaryConfig, - DreamConfig, - - // API response types - QueueStatus, - QueueStatusOptions, - RepresentationOptions, - ConclusionQueryParams, - ConclusionResponse, -} from '@honcho-ai/sdk' -``` - -### SummaryData Type Changed - -```typescript -// Before -interface SummaryData { - content: string - message_id: string - summary_type: string - created_at: string - token_count: number -} - -// After -interface SummaryData { - content: string - messageId: string - summaryType: string - createdAt: string - tokenCount: number -} -``` - ---- - -## Post-v2.0.0 Changes - ---- - -## Card Method Deprecation and setCard (v2.0.1) - -### Before (v2.0.0) - -```typescript -const card = await peer.card(target) // string[] | null -``` - -### After (v2.0.1+) - -```typescript -// getCard() is the preferred method -const card = await peer.getCard(target) // string[] | null - -// card() still works but is deprecated -const card = await peer.card(target) // Deprecated - -// New: setCard() -const updated = await peer.setCard(['Fact 1', 'Fact 2']) -const updated = await peer.setCard(['Fact 1'], targetPeer) -``` - ---- - -## Strict Input Validation (v2.0.2) - -Client constructor and all input schemas now use `.strict()` Zod validation. - -```typescript -// Before (v2.0.1) — silently ignored -const honcho = new Honcho({ baseUrl: 'http://...' }) // typo fell back to default - -// After (v2.0.2+) — ZodError thrown -const honcho = new Honcho({ baseUrl: 'http://...' }) // ZodError: Unrecognized key "baseUrl" -``` - ---- - -## peer() and session() Always Make API Calls (v2.1.0) - -### Before (v2.0.x) - -```typescript -// Without options: lazy object, no API call -const peer = honcho.peer('user-123') - -// With options: made API call -const peer = await honcho.peer('user-123', { metadata: { key: 'value' } }) -``` - -### After (v2.1.0+) - -```typescript -// Always makes a get-or-create API call -const peer = await honcho.peer('user-123') -// peer.createdAt is now always populated -``` - ---- - -## New Properties: createdAt, isActive (v2.1.0) - -```typescript -// Peer -const peer = await honcho.peer('user-123') -console.log(peer.createdAt) // string | undefined - -// Session -const session = await honcho.session('sess-1') -console.log(session.createdAt) // string | undefined -console.log(session.isActive) // boolean | undefined - -// Refreshed by getMetadata(), getConfiguration(), and refresh() -await session.refresh() -``` - ---- - -## getMessage() on Session (v2.1.0) - -```typescript -// Fetch a single message by ID -const msg = await session.getMessage('msg-abc123') -console.log(msg.content, msg.createdAt) -``` - ---- - -## Pagination Parameters (v2.1.0) - -All list methods now accept `page`, `size`, and `reverse`: - -```typescript -// Defaults: page=1, size=50, reverse=false -const peersPage = await honcho.peers({ - filters: { metadata: { role: 'admin' } }, - page: 2, - size: 25, - reverse: true -}) - -// Page properties: -console.log(peersPage.total) // Total items -console.log(peersPage.pages) // Total pages -console.log(peersPage.hasNextPage) // boolean - -// Works on: -// honcho.peers(), honcho.sessions(), honcho.workspaces() -// peer.sessions() -// session.messages() -// scope.list() -``` - ---- - -## searchQuery Moved in context() (v2.1.0) - -### Before (v2.0.x) - -```typescript -const ctx = await session.context({ - searchQuery: 'What are my preferences?', - representationOptions: { maxConclusions: 50 } -}) -``` - -### After (v2.1.0+) - -```typescript -const ctx = await session.context({ - representationOptions: { - searchQuery: 'What are my preferences?', - maxConclusions: 50 - } -}) -``` - ---- - -## Broader Fetch Retry Logic (v2.1.1) - -The SDK now retries on all `TypeError` network failures (connection resets, DNS errors, etc.) instead of only those containing `'fetch'` in the error message. This is transparent — no code changes needed. diff --git a/.claude/skills/migrate-honcho-ts/MIGRATION-CHECKLIST.md b/.claude/skills/migrate-honcho-ts/MIGRATION-CHECKLIST.md deleted file mode 100644 index 7abdd277..00000000 --- a/.claude/skills/migrate-honcho-ts/MIGRATION-CHECKLIST.md +++ /dev/null @@ -1,147 +0,0 @@ -# Migration Checklist - -Use this checklist to track migration progress. Copy into your working notes and check off items as completed. - -## Dependencies - -- [ ] Remove `@honcho-ai/core` from dependencies -- [ ] Update `@honcho-ai/sdk` to v2.1.1 - -## Client-Level Changes - -- [ ] Replace all `.core` usages with `.http` or remove -- [ ] Rename `getConfig()` → `getConfiguration()` -- [ ] Rename `setConfig()` → `setConfiguration()` -- [ ] Rename `getPeers()` → `peers()` -- [ ] Rename `getSessions()` → `sessions()` -- [ ] Rename `getWorkspaces()` → `workspaces()` (returns `Page` now) -- [ ] Rename `getDeriverStatus()` → `queueStatus()` -- [ ] Remove `pollDeriverStatus()` calls entirely (no replacement—do not rely on queue being empty) -- [ ] Move `updateMessage()` calls from client to session - -## Peer-Level Changes - -- [ ] Replace `peer.chat(q, { stream: true })` with `peer.chatStream(q)` -- [ ] Rename `getSessions()` → `sessions()` -- [ ] Rename `getConfig()` → `getConfiguration()` -- [ ] Rename `setConfig()` → `setConfiguration()` -- [ ] Rename `peerConfig()` → `getPeerConfiguration()` -- [ ] Rename `setPeerConfig()` → `setPeerConfiguration()` -- [ ] Rename `workingRep()` → `representation()` (returns string now) -- [ ] Rename `getContext()` → `context()` -- [ ] Replace `observations` → `conclusions` -- [ ] Replace `observationsOf()` → `conclusionsOf()` -- [ ] Handle `card()` returning `string[] | null` instead of `string` - -## Session-Level Changes - -- [ ] Rename `getPeers()` → `peers()` (returns `Peer[]` now, not `Page`) -- [ ] Rename `getMessages()` → `messages()` -- [ ] Rename `getConfig()` → `getConfiguration()` -- [ ] Rename `setConfig()` → `setConfiguration()` -- [ ] Rename `getContext()` → `context()` -- [ ] Rename `getSummaries()` → `summaries()` -- [ ] Rename `getDeriverStatus()` → `queueStatus()` -- [ ] Remove `pollDeriverStatus()` calls entirely (no replacement—do not rely on queue being empty) -- [ ] Rename `workingRep()` → `representation()` (returns string now) - -## Terminology Changes - -- [ ] Rename `maxObservations` → `maxConclusions` -- [ ] Rename `includeMostDerived` → `includeMostFrequent` -- [ ] Rename `lastUserMessage` → `searchQuery` -- [ ] Rename `Observation` type → `Conclusion` -- [ ] Rename `ObservationScope` type → `ConclusionScope` - -## snake_case → camelCase - -- [ ] Update all `{ config: ... }` to `{ configuration: ... }` -- [ ] Update `observe_me` → `observeMe` -- [ ] Update `observe_others` → `observeOthers` -- [ ] Update `created_at` → `createdAt` -- [ ] Update message property access: - - [ ] `peer_id` → `peerId` - - [ ] `session_id` → `sessionId` - - [ ] `workspace_id` → `workspaceId` - - [ ] `created_at` → `createdAt` - - [ ] `token_count` → `tokenCount` -- [ ] Update summary property access: - - [ ] `message_id` → `messageId` - - [ ] `summary_type` → `summaryType` - -## Configuration Objects - -- [ ] Update workspace configuration to typed structure -- [ ] Update session configuration to typed structure -- [ ] Update peer configuration to typed structure -- [ ] Replace `deriver` config with `reasoning` config - -## Error Handling - -- [ ] Update error handling to use new error types if needed - -## Type Imports - -- [ ] Remove imports of deleted types: - - `Observation`, `ObservationScope`, `ObservationData` - - `Representation`, `RepresentationData` - - `ExplicitObservation`, `DeductiveObservation` - - `DialecticStreamDelta` - - `DeriverStatusOptions` - - `MessageCreate`, `ValidatedMessageCreate` - - `WorkingRepParams` -- [ ] Add imports of new types as needed: - - `Conclusion`, `ConclusionScope` - - `MessageInput` - - `QueueStatusOptions` - - Error types - -## Representation Handling - -- [ ] Remove usage of `Representation` class methods (`.explicit`, `.deductive`, `.isEmpty()`, `.diff()`) -- [ ] Handle representation as plain string - -## Card Method Updates (v2.0.1) - -- [ ] Replace `peer.card()` with `peer.getCard()` (card() is deprecated) -- [ ] Use `peer.setCard(string[])` if setting peer cards - -## Strict Validation (v2.0.2) - -- [ ] Verify no constructor options or input schemas pass unknown/misspelled fields (now throws `ZodError`) -- [ ] Check for `baseUrl` vs `baseURL` typo in Honcho constructor - -## peer() / session() API Call Change (v2.1.0) - -- [ ] Update code that relied on lazy `peer()` / `session()` — they now always make API calls -- [ ] Ensure all `peer()` and `session()` calls are `await`ed - -## New Properties (v2.1.0) - -- [ ] Use `peer.createdAt` / `session.createdAt` where creation time is needed -- [ ] Use `session.isActive` where session active status is needed - -## New Methods (v2.1.0) - -- [ ] Use `session.getMessage(messageId)` to fetch single messages by ID - -## Pagination Parameters (v2.1.0) - -- [ ] Add `page`, `size`, `reverse` parameters to list calls where needed: - - [ ] `honcho.peers()` - - [ ] `honcho.sessions()` - - [ ] `honcho.workspaces()` - - [ ] `peer.sessions()` - - [ ] `session.messages()` - - [ ] `scope.list()` - -## searchQuery Location Change (v2.1.0) - -- [ ] Move `searchQuery` from top-level `context()` options to `representationOptions.searchQuery` - -## Final Verification - -- [ ] Run TypeScript compiler with no errors -- [ ] Run tests -- [ ] Verify streaming functionality works -- [ ] Verify configuration changes take effect diff --git a/.claude/skills/migrate-honcho-ts/SKILL.md b/.claude/skills/migrate-honcho-ts/SKILL.md deleted file mode 100644 index 5de5e973..00000000 --- a/.claude/skills/migrate-honcho-ts/SKILL.md +++ /dev/null @@ -1,330 +0,0 @@ ---- -name: migrate-honcho-ts -description: Migrates Honcho TypeScript SDK code from v1.6.0 to v2.1.1. Use when upgrading @honcho-ai/sdk, fixing breaking changes after upgrade, or when errors mention removed APIs like .core, getConfig, observations, or snake_case properties. ---- - -# Honcho TypeScript SDK Migration (v1.6.0 → v2.1.1) - -## Overview - -This skill migrates code from `@honcho-ai/sdk` v1.6.0 to v2.1.1 (required for Honcho 3.0.0+). - -**Key breaking changes:** - -- `@honcho-ai/core` dependency removed -- "Observation" → "Conclusion" terminology -- "Deriver" → "Queue" terminology -- `getConfig`/`setConfig` → `getConfiguration`/`setConfiguration` -- `snake_case` → `camelCase` throughout -- Streaming via `chatStream()` instead of `chat({ stream: true })` -- `Representation` class removed (returns string now) - -## Quick Migration - -### 1. Update dependencies - -Remove `@honcho-ai/core` from package.json. The SDK now has its own HTTP client. - -### 2. Replace `.core` with `.http` - -```typescript -// Before -const workspace = await client.core.workspaces.getOrCreate({ id: 'my-workspace' }) - -// After -const response = await client.http.post('/v3/workspaces', { body: { id: 'my-workspace' } }) -``` - -### 3. Rename configuration methods - -```typescript -// Before -await honcho.getConfig() -await honcho.setConfig({ key: 'value' }) -await peer.getConfig() -await session.getConfig() - -// After -await honcho.getConfiguration() -await honcho.setConfiguration({ reasoning: { enabled: true } }) -await peer.getConfiguration() -await session.getConfiguration() -``` - -### 4. Rename listing methods - -```typescript -// Before -const peers = await honcho.getPeers() -const sessions = await honcho.getSessions() -const workspaces = await honcho.getWorkspaces() // string[] - -// After -const peers = await honcho.peers() -const sessions = await honcho.sessions() -const workspaces = await honcho.workspaces() // Page -``` - -### 5. Update streaming - -```typescript -// Before -const stream = await peer.chat('Hello', { stream: true }) - -// After -const stream = await peer.chatStream('Hello') -``` - -### 6. Update observations → conclusions - -```typescript -// Before -peer.observations -peer.observationsOf('bob') -maxObservations: 50 -includeMostDerived: true - -// After -peer.conclusions -peer.conclusionsOf('bob') -maxConclusions: 50 -includeMostFrequent: true -``` - -### 7. Update queue status methods - -```typescript -// Before -await honcho.getDeriverStatus({ observer: peer }) -await honcho.pollDeriverStatus({ timeoutMs: 60000 }) // REMOVE - see note below - -// After -await honcho.queueStatus({ observer: peer }) -// pollDeriverStatus() has no replacement - see note below -``` - -**Important:** `pollDeriverStatus()` and its polling pattern have been removed entirely. Do not rely on the queue ever being empty. The queue is a continuous processing system—new messages may arrive at any time, and waiting for "completion" is not a valid pattern. If your code previously polled for queue completion, redesign it to work without that assumption. - -### 8. Convert snake_case to camelCase - -```typescript -// Before -message.peer_id -message.session_id -message.created_at -message.token_count -{ observe_me: true, observe_others: false } -{ created_at: '2024-01-01' } - -// After -message.peerId -message.sessionId -message.createdAt -message.tokenCount -{ observeMe: true, observeOthers: false } -{ createdAt: '2024-01-01' } -``` - -### 9. Update representation calls - -```typescript -// Before -const rep = await peer.workingRep(session, target, options) -console.log(rep.explicit) // ExplicitObservation[] -console.log(rep.deductive) // DeductiveObservation[] - -// After -const rep = await peer.representation({ session, target, ...options }) -console.log(rep) // string -``` - -### 10. Move updateMessage to session - -```typescript -// Before -await honcho.updateMessage(message, metadata, session) - -// After -await session.updateMessage(message, metadata) -``` - -### 11. Update card() to getCard() (v2.0.1+) - -```typescript -// Before -const card = await peer.card(target) - -// After (v2.0.1+) -const card = await peer.getCard(target) // Returns string[] | null - -// peer.card() still works but is deprecated — use getCard() - -// New: setPeerCard / setCard -await peer.setCard(['Prefers dark mode', 'Located in US']) -``` - -### 12. Strict input validation (v2.0.2+) - -Client constructor and all input schemas now reject unknown options via `.strict()` Zod validation. - -```typescript -// Before (v2.0.1 and earlier) — silently ignored -const honcho = new Honcho({ baseUrl: 'http://...' }) // typo: baseUrl vs baseURL — silently fell back to default - -// After (v2.0.2+) — throws ZodError -const honcho = new Honcho({ baseUrl: 'http://...' }) // ZodError! Use baseURL -``` - -### 13. 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. - -```typescript -// Before (v2.0.x) — no API call without options -const session = honcho.session('my-session') // Lazy, no network request - -// After (v2.1.0+) — always hits the API -const session = await honcho.session('my-session') // Makes POST to /sessions (get-or-create) -``` - -### 14. New properties and methods (v2.1.0+) - -```typescript -// createdAt on Peer and Session -const peer = await honcho.peer('user-123') -console.log(peer.createdAt) // string | undefined - -const session = await honcho.session('sess-1') -console.log(session.createdAt) // string | undefined - -// isActive on Session -console.log(session.isActive) // boolean | undefined - -// getMessage() on Session -const msg = await session.getMessage('msg-id') -``` - -### 15. Pagination parameters on list methods (v2.1.0+) - -All list methods now accept `page`, `size`, and `reverse` parameters: - -```typescript -// Before (v2.0.x) — only filters -const peers = await honcho.peers({ metadata: { role: 'admin' } }) - -// After (v2.1.0+) — pagination controls via options object -const peers = await honcho.peers({ - filters: { metadata: { role: 'admin' } }, - page: 2, - size: 25, - reverse: true -}) - -// Legacy raw-filter form still works: -const peers = await honcho.peers({ metadata: { role: 'admin' } }) - -// Works on: honcho.peers(), honcho.sessions(), honcho.workspaces(), -// peer.sessions(), session.messages(), scope.list() -``` - -### 16. searchQuery moved in context() (v2.1.0+) - -**Breaking**: `searchQuery` removed from top-level `context()` options. Use `representationOptions.searchQuery` instead. - -```typescript -// Before (v2.0.x) -await session.context({ searchQuery: '...' }) - -// After (v2.1.0+) -await session.context({ representationOptions: { searchQuery: '...' } }) -``` - -### 17. Broader fetch retry logic (v2.1.1+) - -The SDK now retries on all `TypeError` network failures (connection resets, DNS errors, etc.) instead of only those with `'fetch'` in the message. No code changes needed — this is transparent. - -## Quick Reference Table - -| v1.6.0 | v2.0.0 | -|--------|--------| -| `client.core` | `client.http` | -| `getConfig()` | `getConfiguration()` | -| `setConfig()` | `setConfiguration()` | -| `getPeers()` | `peers()` | -| `getSessions()` | `sessions()` | -| `getWorkspaces()` | `workspaces()` | -| `getDeriverStatus()` | `queueStatus()` | -| `pollDeriverStatus()` | *Removed - do not poll* | -| `peer.chat(q, { stream: true })` | `peer.chatStream(q)` | -| `peer.workingRep()` | `peer.representation()` | -| `peer.getContext()` | `peer.context()` | -| `peer.observations` | `peer.conclusions` | -| `peer.observationsOf()` | `peer.conclusionsOf()` | -| `session.getPeers()` | `session.peers()` | -| `session.getMessages()` | `session.messages()` | -| `session.getSummaries()` | `session.summaries()` | -| `session.getContext()` | `session.context()` | -| `session.workingRep()` | `session.representation()` | -| `session.peerConfig()` | `session.getPeerConfiguration()` | -| `session.setPeerConfig()` | `session.setPeerConfiguration()` | -| `{ timeoutMs: 60000 }` | `{ timeout: 60000 }` | -| `{ maxObservations: 50 }` | `{ maxConclusions: 50 }` | -| `{ includeMostDerived }` | `{ includeMostFrequent }` | -| `{ lastUserMessage }` | `{ searchQuery }` | -| `{ config: ... }` | `{ configuration: ... }` | -| `message.peer_id` | `message.peerId` | -| `message.created_at` | `message.createdAt` | -| `peer.card()` | `peer.getCard()` *(card() deprecated)* | -| *(new)* | `peer.setCard(string[])` | -| `Observation` | `Conclusion` | -| `ObservationScope` | `ConclusionScope` | -| *(new v2.1.0)* | `peer.createdAt` / `session.createdAt` | -| *(new v2.1.0)* | `session.isActive` | -| *(new v2.1.0)* | `session.getMessage(id)` | -| *(new v2.1.0)* | `page`, `size`, `reverse` on list methods | -| `context({ searchQuery })` | `context({ representationOptions: { searchQuery } })` | - -## Detailed Reference - -For comprehensive details on each change, see: - -- [DETAILED-CHANGES.md](DETAILED-CHANGES.md) - Full API change documentation -- [MIGRATION-CHECKLIST.md](MIGRATION-CHECKLIST.md) - Step-by-step checklist - -## New Error Types - -```typescript -import { - HonchoError, - AuthenticationError, - BadRequestError, - NotFoundError, - PermissionDeniedError, - RateLimitError, - ConflictError, - UnprocessableEntityError, - ServerError, - ConnectionError, - TimeoutError -} from '@honcho-ai/sdk' -``` - -## New Configuration Types - -Configurations are now strongly typed: - -```typescript -await honcho.setConfiguration({ - reasoning: { - enabled: true, - customInstructions: 'Be concise' - }, - peerCard: { use: true, create: true }, - summary: { - enabled: true, - messagesPerShortSummary: 20, - messagesPerLongSummary: 60 - }, - dream: { enabled: true } -}) -``` diff --git a/README.md b/README.md index e62bce37..bef96c61 100644 --- a/README.md +++ b/README.md @@ -226,7 +226,7 @@ For wiring the Honcho SDK into an existing application, install the integration npx skills add plastic-labs/honcho ``` -Then invoke `/honcho-integration` in Claude Code (or `/honcho-dev:integrate` via the plugin marketplace). Details: [agentic development guide](https://honcho.dev/docs/v3/documentation/introduction/vibecoding). +Then invoke `/honcho-integration` in Claude Code (or `/honcho-dev:integrate` via the plugin marketplace). The same command also installs the memory skills — `honcho-memory` (concepts: the recall/record loop, session and peer strategy, plus how to connect and drive an MCP-connected Honcho) and `honcho-cli` (inspecting and debugging a deployment). Details: [agentic development guide](https://honcho.dev/docs/v3/documentation/introduction/vibecoding). ### Other MCP clients diff --git a/docs/v3/documentation/introduction/vibecoding.mdx b/docs/v3/documentation/introduction/vibecoding.mdx index 36e6661d..cd5e8ac6 100644 --- a/docs/v3/documentation/introduction/vibecoding.mdx +++ b/docs/v3/documentation/introduction/vibecoding.mdx @@ -122,7 +122,9 @@ npx skills add plastic-labs/honcho ``` ```bash Install as Claude Skill Manually -curl -o ~/.claude/skills/honcho-integration.md https://raw.githubusercontent.com/plastic-labs/honcho/main/docs/SKILL.md +mkdir -p ~/.claude/skills/honcho-integration +curl -o ~/.claude/skills/honcho-integration/SKILL.md \ + https://raw.githubusercontent.com/plastic-labs/honcho/main/.claude/skills/honcho-integration/SKILL.md ``` @@ -139,24 +141,16 @@ curl -o ~/.claude/skills/honcho-integration.md https://raw.githubusercontent.com Invoke with `/honcho-integration` in your coding agent. +#### honcho-memory + +**Concepts & strategy for using Honcho at runtime.** The hub skill: it teaches the recall → respond → record loop and session and peer design — the durable model behind using Honcho as memory, independent of how you're connected — plus how to connect via MCP and drive the [MCP tools](#mcp-server). Use this when your agent already has Honcho available and you want it to remember the user across conversations — as opposed to `honcho-integration`, which adds the SDK to a codebase. (An MCP-connected agent also receives usage guidance directly from the server on connect.) + #### honcho-cli **For inspection & debugging.** Teaches your coding agent the right commands and flags for the [honcho CLI](#cli) — peer memory, session context, queue status, dialectic quality. Invoke implicitly when you ask your agent to inspect a Honcho deployment. -#### migrate-honcho-py / migrate-honcho-ts - -**For SDK upgrades.** Migrates code from v1.6.0 to v2.0.0 (required for Honcho 3.0.0+). Use when upgrading the SDK or seeing errors about removed APIs like `observations`, `Representation`, `.core`, or `get_config`. - -Both skills handle: terminology changes (`Observation` → `Conclusion`), `Representation` class removal, method renames, and streaming API updates. - -| Python | TypeScript | -|--------|------------| -| `/migrate-honcho-py` | `/migrate-honcho-ts` | -| `AsyncHoncho` → `.aio` accessor | `@honcho-ai/core` removal | -| | `snake_case` → `camelCase` | - --- ## Universal Starter Prompt diff --git a/docs/v3/guides/integrations/mcp.mdx b/docs/v3/guides/integrations/mcp.mdx index 10bfdfd0..05ced5d0 100644 --- a/docs/v3/guides/integrations/mcp.mdx +++ b/docs/v3/guides/integrations/mcp.mdx @@ -295,19 +295,11 @@ Example with all headers (Claude Desktop format): --- -## Available Tools +## Using the Tools -The recommended flow for a standard conversation uses `create_session` + `add_messages_to_session` + `chat`. See the [full instructions](https://raw.githubusercontent.com/plastic-labs/honcho/refs/heads/main/mcp/instructions.md) for a complete walkthrough. +Once connected, the Honcho MCP server tells your assistant how to use the tools automatically — it ships usage instructions (the recall → respond → record loop, the full tool list, and reasoning levels) on connect, so there's nothing extra to configure. -**Workspace** — `inspect_workspace`, `list_workspaces`, `search`, `get_metadata`, `set_metadata` - -**Peers** — `create_peer`, `list_peers`, `chat`, `get_peer_card`, `set_peer_card`, `get_peer_context`, `get_representation` - -**Sessions** — `create_session`, `list_sessions`, `delete_session`, `clone_session`, `add_peers_to_session`, `remove_peers_from_session`, `get_session_peers`, `inspect_session`, `add_messages_to_session`, `get_session_messages`, `get_session_message`, `get_session_context` - -**Conclusions** — `list_conclusions`, `query_conclusions`, `create_conclusions`, `delete_conclusion` - -**System** — `schedule_dream`, `get_queue_status` +If you want to read that guidance yourself, it's the [full instructions](https://raw.githubusercontent.com/plastic-labs/honcho/refs/heads/main/mcp/instructions.md). --- diff --git a/mcp/instructions.md b/mcp/instructions.md index 10cae48d..2abc05ff 100644 --- a/mcp/instructions.md +++ b/mcp/instructions.md @@ -70,6 +70,16 @@ add_messages_to_session --- +## Best Practices + +- **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. +- **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. +- **`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. +- **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. +- **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. + +--- + ## General Tools The full API for advanced use cases. diff --git a/mcp/src/instructions.d.ts b/mcp/src/instructions.d.ts new file mode 100644 index 00000000..1cde2fab --- /dev/null +++ b/mcp/src/instructions.d.ts @@ -0,0 +1,5 @@ +// Markdown files are bundled as text strings via the wrangler `Text` rule. +declare module "*.md" { + const content: string; + export default content; +} diff --git a/mcp/src/server.ts b/mcp/src/server.ts index 6bbd5e08..363cf9c0 100644 --- a/mcp/src/server.ts +++ b/mcp/src/server.ts @@ -5,12 +5,16 @@ import { register as registerPeerTools } from "./tools/peers.js"; import { register as registerSessionTools } from "./tools/sessions.js"; import { register as registerConclusionTools } from "./tools/conclusions.js"; import { register as registerSystemTools } from "./tools/system.js"; +import instructions from "../instructions.md"; export function createServer(ctx: ToolContext): McpServer { - const server = new McpServer({ - name: "Honcho MCP Server", - version: "3.0.0", - }); + const server = new McpServer( + { + name: "Honcho MCP Server", + version: "3.0.0", + }, + { instructions }, + ); registerWorkspaceTools(server, ctx); registerPeerTools(server, ctx); diff --git a/mcp/wrangler.toml b/mcp/wrangler.toml index c83d240f..987b0b6b 100644 --- a/mcp/wrangler.toml +++ b/mcp/wrangler.toml @@ -3,6 +3,11 @@ main = "src/index.ts" compatibility_date = "2024-12-09" compatibility_flags = ["nodejs_compat"] +# Bundle Markdown (e.g. instructions.md) as text strings so it can be imported. +rules = [ + { type = "Text", globs = ["**/*.md"], fallthrough = true }, +] + [env.production] name = "honcho-mcp" diff --git a/.claude/skills/honcho-cli/SKILL.md b/skills/honcho-cli/SKILL.md similarity index 92% rename from .claude/skills/honcho-cli/SKILL.md rename to skills/honcho-cli/SKILL.md index e2276669..716d6227 100644 --- a/.claude/skills/honcho-cli/SKILL.md +++ b/skills/honcho-cli/SKILL.md @@ -1,6 +1,6 @@ --- name: honcho-cli -description: Inspect and debug Honcho workspaces via the `honcho` CLI. Use when investigating peer representations, memory state, session context, queue status, or dialectic quality — any task that requires introspection of a Honcho deployment. +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 --- @@ -30,7 +30,6 @@ allowed-tools: Bash(honcho:*), Bash(jq:*), Read, Grep - Run `honcho peer inspect` before `honcho peer chat` to understand context. - Use `honcho session context` to see exactly what an agent receives. - Never run `honcho workspace delete` without `honcho workspace inspect` first. -- Check queue status when derivation seems stalled. - Compare peer card with conclusions to understand memory state. ## Inspection tour @@ -82,9 +81,6 @@ honcho peer search "query" --json # Is observation enabled? honcho peer inspect --json | jq '.configuration' -# Is the deriver queue processing messages? -honcho workspace queue-status --json - # What conclusions exist? honcho conclusion list --observer --json honcho conclusion search "expected topic" --observer --json diff --git a/skills/honcho-integration/SKILL.md b/skills/honcho-integration/SKILL.md new file mode 100644 index 00000000..6b6706a2 --- /dev/null +++ b/skills/honcho-integration/SKILL.md @@ -0,0 +1,179 @@ +--- +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 +--- + +# Honcho Integration Guide + +## What is Honcho + +Honcho 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. + +The 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. + +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 + +Follow these phases in order: + +### Phase 1: Codebase Exploration + +Before asking the user anything, explore the codebase to understand: + +1. **Language & Framework**: Is this Python or TypeScript? What frameworks are used (FastAPI, Express, Next.js, etc.)? +2. **Existing AI/LLM code**: Search for existing LLM integrations (OpenAI, Anthropic, LangChain, etc.) +3. **Entity structure**: Identify users, agents, bots, or other entities that interact +4. **Session/conversation handling**: How does the app currently manage conversations? +5. **Message flow**: Where are messages sent/received? What's the request/response cycle? + +Use Glob and Grep to find: + +- `**/*.py` or `**/*.ts` files with "openai", "anthropic", "llm", "chat", "message" +- 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 `references/bot-frameworks.md` for framework-specific integration guidance and check `references/bot-frameworks//` for concrete reference implementations. + +### Phase 2: Interview (REQUIRED) + +After exploring the codebase, use the **AskUserQuestion** tool to clarify integration requirements. Ask these questions (adapt based on what you learned in Phase 1): + +#### Question Set 1 - Entities & Peers + +Ask about which entities should be Honcho peers: + +- header: "Peers" +- question: "Which entities should Honcho track and build representations for?" +- options based on what you found (e.g., "End users only", "Users + AI assistant", "Users + multiple AI agents", "All participants including third-party services") +- Include a follow-up if they have multiple AI agents: should any AI peers be observed? + +#### Question Set 2 - Integration Pattern + +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?" +- options: + - "Tool call (Recommended)" - "Agent queries Honcho on-demand via function calling" + - "Pre-fetch" - "Fetch user context before each LLM call with predefined queries" + - "context()" - "Include conversation history and representations in prompt" + - "Multiple patterns" - "Combine approaches for different use cases" + +#### Question Set 3 - Session Structure + +Ask about conversation structure: + +- header: "Sessions" +- question: "How should conversations map to Honcho sessions?" +- 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") + +#### Question Set 4 - Specific Queries (if using pre-fetch pattern) + +If they chose pre-fetch, ask what context matters: + +- header: "Context" +- question: "What user context should be fetched for the AI?" +- multiSelect: true +- options: "Communication style", "Expertise level", "Goals/priorities", "Preferences", "Recent activity summary", "Custom queries" + +### Phase 3: Implementation + +Based on interview responses, implement the integration: + +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 + +- If the Honcho CLI is available, run `honcho doctor` to confirm connectivity before testing the integration code +- Use `honcho peer list` and `honcho peer chat` to verify peers exist and the dialectic endpoint works independently of the integration +- Ensure all message exchanges are stored to Honcho +- Verify deterministic bot peers have `observe_me=False`; AI-assistant peers can keep observation on (it's fine to model them) +- Check that the workspace ID is consistent across the codebase +- Confirm environment variable for API key is documented + +--- + +## Before You Start + +1. **Check the latest SDK versions** at + - Python SDK: `honcho-ai` + - TypeScript SDK: `@honcho-ai/sdk` + +2. **Get an API key** ask the user to get a Honcho API key from and add it to the environment. + +3. **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: + + ```bash + honcho init # persist API key + URL to ~/.honcho/config.json + honcho doctor # verify connectivity, config, workspace health + honcho peer chat # test the dialectic endpoint interactively + ``` + + This is the fastest way to confirm the API key and URL are correct before debugging SDK code. + +## Installation + +### Python (use uv) + +```bash +uv add honcho-ai +``` + +### TypeScript (use bun) + +```bash +bun add @honcho-ai/sdk +``` + +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 + +When integrating Honcho into an existing codebase: + +- [ ] Install SDK with `uv add honcho-ai` (Python) or `bun add @honcho-ai/sdk` (TypeScript) +- [ ] Set up `HONCHO_API_KEY` environment variable +- [ ] Initialize Honcho client with a single workspace ID +- [ ] Create peers for all entities (users AND AI assistants) +- [ ] Set `observe_me=False` for deterministic bot peers (optional for AI assistants — fine to leave observation on) +- [ ] Configure sessions with appropriate peer observation settings +- [ ] Choose integration pattern: + - [ ] Tool call pattern for agentic systems + - [ ] Pre-fetch pattern for simpler integrations + - [ ] context() for conversation history +- [ ] Store messages after each exchange to build user models +- [ ] (Optional) Run `honcho doctor` to verify connectivity before testing integration code +- [ ] (Optional) Use `honcho peer chat` to test dialectic queries independently + +## Common Mistakes to Avoid + +1. **Multiple workspaces**: Use ONE workspace per application +2. **Forgetting AI peers**: Create peers for AI assistants, not just users +3. **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. +4. **Not storing messages**: Always call `add_messages()` to feed Honcho's reasoning engine +5. **Blocking on processing**: Messages are processed asynchronously — don't poll or wait for reasoning to complete before continuing + +## Resources + +- Documentation (LLM-friendly index): +- Latest SDK versions: +- API Reference: + +> Tip: append `.md` to any Honcho docs URL to fetch the raw Markdown version. diff --git a/skills/honcho-integration/references/agent-patterns.md b/skills/honcho-integration/references/agent-patterns.md new file mode 100644 index 00000000..ca1865d9 --- /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**: 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 + +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 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") + +# 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"); + +// 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/.claude/skills/honcho-integration/references/bot-frameworks.md b/skills/honcho-integration/references/bot-frameworks.md similarity index 97% rename from .claude/skills/honcho-integration/references/bot-frameworks.md rename to skills/honcho-integration/references/bot-frameworks.md index 16e7c24b..874217d0 100644 --- a/.claude/skills/honcho-integration/references/bot-frameworks.md +++ b/skills/honcho-integration/references/bot-frameworks.md @@ -127,7 +127,7 @@ Key patterns (shared): - IDs sanitized to `^[a-zA-Z0-9_-]+` (Honcho requirement) - User peer: `observe_me=True, observe_others=True` -- Assistant peer: `observe_me=False, observe_others=True` +- 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`. If references exist for this framework, use them directly from `{baseDir}/references/bot-frameworks//`. diff --git a/.claude/skills/honcho-integration/references/bot-frameworks/nanobot/client.py b/skills/honcho-integration/references/bot-frameworks/nanobot/client.py similarity index 100% rename from .claude/skills/honcho-integration/references/bot-frameworks/nanobot/client.py rename to skills/honcho-integration/references/bot-frameworks/nanobot/client.py diff --git a/.claude/skills/honcho-integration/references/bot-frameworks/nanobot/honcho_tool.py b/skills/honcho-integration/references/bot-frameworks/nanobot/honcho_tool.py similarity index 100% rename from .claude/skills/honcho-integration/references/bot-frameworks/nanobot/honcho_tool.py rename to skills/honcho-integration/references/bot-frameworks/nanobot/honcho_tool.py diff --git a/.claude/skills/honcho-integration/references/bot-frameworks/nanobot/session.py b/skills/honcho-integration/references/bot-frameworks/nanobot/session.py similarity index 98% rename from .claude/skills/honcho-integration/references/bot-frameworks/nanobot/session.py rename to skills/honcho-integration/references/bot-frameworks/nanobot/session.py index b5ebc081..f340f2a8 100644 --- a/.claude/skills/honcho-integration/references/bot-frameworks/nanobot/session.py +++ b/skills/honcho-integration/references/bot-frameworks/nanobot/session.py @@ -137,7 +137,10 @@ class HonchoSessionManager: session = self.honcho.session(session_id) - # Configure peer observation settings + # Configure peer observation settings. + # observe_me=False on the assistant skips modeling it — optional for an + # AI assistant (fine to leave observation on); it's really only needed + # for deterministic bots, where there's nothing meaningful to model. from honcho.api_types import SessionPeerConfig user_config = SessionPeerConfig(observe_me=True, observe_others=True) ai_config = SessionPeerConfig(observe_me=False, observe_others=True) diff --git a/skills/honcho-integration/references/core-patterns.md b/skills/honcho-integration/references/core-patterns.md new file mode 100644 index 00000000..2c6a84df --- /dev/null +++ b/skills/honcho-integration/references/core-patterns.md @@ -0,0 +1,157 @@ +# 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 (observed by default) +user = honcho.peer("user-123") + +# AI assistants can be observed too — leave observe_me on (the default) if you +# want a model of the assistant. +assistant = honcho.peer("assistant") + +# Deterministic bots (scripted/rule-based) - set observe_me=False; there's +# nothing meaningful for Honcho to model. +notification_bot = honcho.peer("notification-bot", configuration=PeerConfig(observe_me=False)) +``` + +**TypeScript:** + +```typescript +// Human users (observed by default) +const user = await honcho.peer("user-123"); + +// AI assistants can be observed too — leave observeMe on (the default) if you +// want a model of the assistant. +const assistant = await honcho.peer("assistant"); + +// Deterministic bots (scripted/rule-based) - set observeMe=false; there's +// nothing meaningful for Honcho to model. +const notificationBot = await honcho.peer("notification-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) + +# A deterministic bot is NOT observed (no model built of it). An AI assistant +# could stay observed instead — observe_me defaults to True. +bot_config = SessionPeerConfig(observe_me=False, observe_others=True) + +session.add_peers([ + (user, user_config), + (notification_bot, bot_config) +]) +``` + +**TypeScript:** + +```typescript +const session = await honcho.session("conversation-123"); + +await session.addPeers([ + // A deterministic bot isn't observed; an AI assistant could stay observed + // instead (observeMe defaults to true). + [user, { observeMe: true, observeOthers: true }], + [notificationBot, { 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-memory/SKILL.md b/skills/honcho-memory/SKILL.md new file mode 100644 index 00000000..4c19decc --- /dev/null +++ b/skills/honcho-memory/SKILL.md @@ -0,0 +1,89 @@ +--- +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. +--- + +# Using Honcho as Memory + +Honcho 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. + +This 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. + +> **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. + +## The mental model + +- **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). +- **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). +- **Message** — the raw turns you feed in. No messages → no reasoning → no memory. +- **Conclusion** — a fact Honcho derived (or you stored) about a peer. Conclusions power the representation. +- **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. + +Reasoning 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. + +## The loop: recall → respond → record + +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_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 are in the [chat docs](https://honcho.dev/docs/v3/documentation/features/chat.md). + +--- + +## Pick your access path + +The 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. + +1. **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: . +2. **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. +3. **`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?). +4. **Embedding Honcho into your own codebase** (not just using a connected instance) → use the **`honcho-integration`** skill. + +If 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. + +--- + +## Setup (if not connected yet) + +You need a Honcho API key — get one free at (starts with `hch-`). Then connect via the path you picked above — a purpose-built integration (recommended), or a raw connection: + +- **MCP** — point your client at `https://mcp.honcho.dev` with two headers: `Authorization: Bearer hch-your-key-here` and `X-Honcho-User-Name: YourName` (what Honcho should call the user). Optional: `X-Honcho-Assistant-Name` (default `Assistant`) and `X-Honcho-Workspace-ID` (default `default`; set it to isolate memory per project). 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. +- **CLI** — use the `honcho-cli` skill. + +--- + +## Rules of thumb + +- **Always record turns.** Memory only grows from messages you feed in. Recording is the one non-optional step. +- **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. +- **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 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. +- **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_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). + +## Resources + +These 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 ). + +- Full docs index (for agents): +- All integrations & plugins: +- MCP server & client setup: +- Full MCP usage walkthrough: +- Agent development overview: +- CLI reference: diff --git a/.claude/skills/verify/SKILL.md b/skills/verify/SKILL.md similarity index 100% rename from .claude/skills/verify/SKILL.md rename to skills/verify/SKILL.md