API Redesign for @honcho/ai-sdk
Context
The current integration requires too much ceremony. Before calling generateText, a developer needs to understand workspaces, sessions, peers, observation configs, middleware, and wrapLanguageModel. Competing providers (Mem0, Supermemory, Hindsight) need at most a user ID. The package hasn’t been published yet, so we can rethink the API fundamentally.
SDK Migration: @honcho-ai/core → @honcho-ai/sdk
The current package depends on the deprecated @honcho-ai/core (v2.2.0) — a low-level auto-generated client targeting the v2 API. It requires a v3Fetch hack to rewrite URLs to the v3 API.
@honcho-ai/sdk (v2.0.1) is the current SDK — a DX-optimized wrapper that:
- Natively targets the v3 API (no URL rewriting needed)
- Has object-oriented API:
peer.chat(query)instead ofclient.workspaces.peers.chat(wsId, peerId, { query }) - Has
SessionContext.toOpenAI(assistant)/.toAnthropic(assistant)native converters - Has built-in lazy peer/session creation (
honcho.peer("id")handles getOrCreate) - Defaults
workspaceIdtoHONCHO_WORKSPACE_IDenv var - Has
peer.message(content)helper andsession.addMessages()
This migration eliminates most of the wrapper code in src/shared/context.ts.
Design: Flat Middleware Config
No session objects. One middleware function with flat config. Complexity is opt-in.
Middleware Config Shape
{
userId: string; // the user peer — observed, modeled (required)
sessionId?: string; // conversation thread — enables persistence + full context
assistantId?: string; // the AI peer — observer, generates (defaults to "assistant")
persistInput?: boolean; // also save user message on persist (default: true)
injectHistory?: boolean; // inject recent messages from Honcho (default: true)
formatContext?: Function; // custom context formatter
}Usage Examples
const honcho = createHoncho(); // zero-config: reads HONCHO_API_KEY + HONCHO_WORKSPACE_ID from env
// === Simplest: inject what Honcho knows about this user (no persistence) ===
await generateText({
model: openai('gpt-4o'),
middleware: honcho.middleware({ userId: 'user-123' }),
prompt: 'What should I focus on today?',
});
// === Add persistence: include sessionId ===
await generateText({
model: openai('gpt-4o'),
middleware: honcho.middleware({ userId: 'user-123', sessionId: 'chat-456' }),
prompt: 'What should I focus on today?',
});
// === Add tools: let the agent reason about the user ===
await generateText({
model: openai('gpt-4o'),
middleware: honcho.middleware({ userId: 'user-123', sessionId: 'chat-456' }),
tools: honcho.tools({ userId: 'user-123' }),
maxSteps: 3,
prompt: 'What should I focus on today?',
});
// === With messages array: disable history injection to avoid duplication ===
await generateText({
model: openai('gpt-4o'),
middleware: honcho.middleware({
userId: 'user-123',
sessionId: 'chat-456',
injectHistory: false, // developer owns conversation history
}),
messages: conversationHistory,
});
// === Multi-peer: specify who's generating ===
await generateText({
model: openai('gpt-4o'),
middleware: honcho.middleware({
assistantId: 'agent-coordinator',
userId: 'alice',
sessionId: 'group-123',
}),
prompt: aliceMessage,
});
// === Multi-peer: different agent, same session ===
await generateText({
model: openai('gpt-4o'),
middleware: honcho.middleware({
assistantId: 'agent-specialist',
userId: 'bob',
sessionId: 'group-123',
}),
prompt: bobMessage,
});
// === Multi-peer: developer manages input persistence ===
await honcho.send({ userId: 'alice', sessionId: 'group-123', content: aliceMessage });
await generateText({
model: openai('gpt-4o'),
middleware: honcho.middleware({
assistantId: 'coordinator',
userId: 'alice',
sessionId: 'group-123',
persistInput: false,
}),
prompt: aliceMessage,
});Key Design Decisions
1. Flat middleware, no session object
- No
honcho.session(), nocreateMultiAgentSession(). Onehoncho.middleware()call with flat config. userId= the observed peer (human user).assistantId= the observer/generator (AI). Defaults to"assistant".- For multi-peer, just change
assistantIdpergenerateTextcall. - Peers are created lazily with sensible defaults (
userId→observe_me: true,assistantId→observe_me: false, observe_others: true).
2. Persistence: output always, input via flag
- When
sessionIdis present, the middleware always saves the model’s output asassistantId’s message. persistInput(default:true) controls whether the user’s input is also saved asuserId’s message.- For multi-step tool loops with
persistInput: true, the user message is only saved on the first step (tool continuation detection). persistInput: falsefor cases where the developer saves messages themselves (avoids double-counting).
3. History injection via flag
injectHistory(default:true) controls whether recent messages from Honcho are injected into the system prompt.- Representation, peer card, and session summary are ALWAYS injected when available (these are Honcho metadata, not conversation history).
- Set
injectHistory: falsewhen passing amessagesarray togenerateTextto avoid duplication.
4. honcho.send() convenience
- One-liner to save a peer’s message to a session.
- Used when the developer manages input persistence themselves.
await honcho.send({ userId: 'alice', sessionId: 'group-123', content: aliceMessage });5. Sessions are optional, not auto-generated
- Without
sessionId: peer-only context (representation + card). No persistence, no assistant peer. - With
sessionId: full session context (+ summary + messages) and message persistence. - Developer provides their own session/conversation/thread ID. No auto-generation.
6. workspaceId optional with env var fallback
createHoncho()with zero args readsHONCHO_API_KEY+HONCHO_WORKSPACE_IDfrom env.- Throws eagerly if workspace ID not found.
7. Provider-level caching
middleware()andtools()with the same(userId, sessionId, assistantId)share the same internal state.- Cache keyed by composite key inside the
createHoncho()closure. - Peer
getOrCreatecalls are cached and idempotent.
8. userId / assistantId naming
userIdis clear about which peer it maps to (the user being observed).assistantIdis clear about the AI generating the response.- Multi-peer:
assistantIdspecifies which agent is generating,userIdspecifies who sent the input / who to fetch context about.
How the Middleware Works Internally
transformParams (before generation)
- Lazily ensure peers exist (
getOrCreatewith correct observation configs) - If
sessionId: lazily ensure session exists - Fetch context:
- Always: representation + peer card (via
peers.context()orsessions.context()) - If
sessionId: session summary - If
sessionId+injectHistory: true: recent messages
- Always: representation + peer card (via
- Format context (custom
formatContextor default XML formatter) - Inject into system prompt
wrapGenerate / wrapStream (after generation)
- If no
sessionId: no-op (no persistence) - If
persistInput: trueand not a tool continuation: extract last user message from prompt, save asuserId’s message - Save model output as
assistantId’s message - Errors handled via configurable
onError(default:console.warn)
Role mapping for injected history
When injectHistory: true, messages loaded from Honcho are labeled:
- Messages from
assistantId→[assistant] - Messages from everyone else →
[user](with peer ID label for multi-peer)
Implementation
Phase 0: SDK Migration (package.json, src/shared/context.ts)
- Replace
@honcho-ai/corewith@honcho-ai/sdkinpackage.json - Rewrite
createClient()insrc/shared/context.ts:new Honcho({ workspaceId, apiKey })— nov3Fetchhack- Remove
v3Fetchfunction entirely - SDK handles
HONCHO_WORKSPACE_IDandHONCHO_API_KEYenv var fallbacks natively
- The SDK provides:
honcho.peer(id)→ lazy getOrCreate, returnsPeerobject with.chat(),.search(),.getContext(),.message()honcho.session(id)→ lazy getOrCreate, returnsSessionobject with.context(),.addMessages(),.addPeers()session.context({ peer_perspective, peer_target, summary, tokens })→ returnsSessionContextwith.toOpenAI(assistant)/.toAnthropic(assistant)peer.chat(query, { target?, sessionId? })→ dialectic reasoning
Phase 1: Types (src/types.ts)
- Simplify
HonchoProviderOptions— remove redundant fields that the SDK handles (env var fallbacks) - Add middleware config type:
interface HonchoMiddlewareConfig { userId: string; sessionId?: string; assistantId?: string; // default: "assistant" persistInput?: boolean; // default: true injectHistory?: boolean; // default: true formatContext?: (context: SessionContext) => string; onError?: (error: unknown) => void; } - Add tools config type:
interface HonchoToolsConfig { userId: string; sessionId?: string; assistantId?: string; } - Remove old types:
ResolvedHonchoConfig,ResolvedSessionConfig,PeerRoleMap,HonchoCallOptions(no longer needed)
Phase 2: Middleware (src/ai-sdk/middleware.ts)
- Single
createMiddleware(honcho, config)function:transformParams:const userPeer = await honcho.peer(config.userId)(SDK handles lazy getOrCreate)- If
sessionId:const session = await honcho.session(config.sessionId)→session.context({ peer_perspective: assistantId, peer_target: userId, summary: true })→ format and inject - If no
sessionId:userPeer.getContext()→ inject representation + card only - Branch on
injectHistoryto include/exclude recent messages
wrapGenerate:- If no
sessionId: no-op - Build messages array: if
persistInput+ first step, include user message asuserId’s; always include output asassistantId’s session.addMessages(messages)
- If no
wrapStream: same but collect chunks via TransformStream
- Context formatting: use
SessionContext.toOpenAI(assistantId)for default formatting, or customformatContextif provided. Extract the system-prompt-relevant parts (representation, card, summary) and inject into system prompt. - Keep tool continuation detection for
persistInput: true
Phase 3: Tools (src/ai-sdk/tools.ts)
- Rewrite tools to use SDK’s object-oriented API:
honcho_chat:peer.chat(query, { target })instead ofclient.workspaces.peers.chat(wsId, peerId, { query })honcho_search:peer.search(query)instead ofclient.workspaces.peers.search(wsId, peerId, { query })honcho_context:session.context(...)instead ofclient.workspaces.sessions.context(wsId, sessionId, ...)honcho_get_representation:peer.representation()instead ofclient.workspaces.peers.representation(wsId, peerId, {})honcho_save_conclusion:peer.conclusionsOf(target).create(...)or similarhoncho_search_conclusions:honcho.conclusions.query(...)or similar
- Tool factory takes
Honchoinstance instead of{ client, workspaceId, defaultPeerId, ... }
Phase 4: Provider factory (src/ai-sdk/index.ts)
createHoncho(options?)→ createsHonchoinstance (SDK handles env vars)- Internal caching:
Map<string, { peer, session }>keyed by composite key middleware(config)→ creates middleware using cached peers/sessionstools(config)→ creates tools bound to userIdsend({ userId, sessionId, content })→session.addMessages([peer.message(content)])client→ expose underlyingHonchoinstance
Phase 5: Clean up
- Remove
src/ai-sdk/session.ts(flat middleware replaces session object) - Remove
src/multi-agent/index.ts(flat middleware withassistantIdreplaces multi-agent) - Remove
src/shared/converters.ts(SDK’stoOpenAI()/toAnthropic()replaces custom converters) - Simplify
src/shared/context.tsto justcreateClient()(most helpers now handled by SDK methods directly in middleware) - Update
src/index.tsexports - Update
src/shared/descriptions.ts: addassistantPeerId: 'assistant'toDEFAULTS - Update
tsup.config.tsandpackage.jsonexports: removemulti-agententry point
Phase 6: README rewrite
- Quick Start: 4 lines (createHoncho + generateText with middleware)
- “Add persistence”: add
sessionId - “Add tools”: add
honcho.tools()— highlighthoncho_chatas the killer feature - “Multi-peer”: change
assistantId - “Using with messages array”: set
injectHistory: false - “Direct SDK access”:
honcho.clientfor advanced usage, mentiontoOpenAI()/toAnthropic() - Move dreaming, identity to separate docs or “Advanced” section
- Remove all
wrapLanguageModelreferences
Phase 7: Tests
- Rewrite test suite for new API
- Context-only (no session): verify representation/card injected, no persistence
- With session: verify output persisted as assistantId’s message
- persistInput true/false
- injectHistory true/false
- Multi-peer with different assistantIds
- Tools: standalone and with middleware
honcho.send()- messages array (no duplication)
Files to Modify
| File | Change |
|---|---|
package.json | Replace @honcho-ai/core with @honcho-ai/sdk, remove multi-agent export |
tsup.config.ts | Remove multi-agent entry point |
src/types.ts | Simplify: remove old types, add HonchoMiddlewareConfig, HonchoToolsConfig |
src/ai-sdk/index.ts | Rewrite: flat middleware/tools, caching, send() |
src/ai-sdk/middleware.ts | Rewrite: single createMiddleware using SDK objects |
src/ai-sdk/tools.ts | Rewrite: use SDK’s object-oriented API |
src/shared/context.ts | Simplify: just createClient(), remove v3Fetch and wrapper functions |
src/shared/descriptions.ts | Add assistantPeerId default |
src/index.ts | Update exports |
README.md | Full rewrite with progressive disclosure |
src/test.ts | Rewrite for new API |
Files to Remove
| File | Reason |
|---|---|
src/ai-sdk/session.ts | Replaced by flat middleware config |
src/multi-agent/index.ts | Replaced by assistantId parameter on middleware |
src/shared/converters.ts | Replaced by SDK’s native toOpenAI()/toAnthropic() |
Files NOT Modified
| File | Reason |
|---|---|
src/openai/ | OpenAI adapter — may need minor updates for new SDK but separate concern |
src/dreaming/ | Kept as experimental, separate concern |
src/identity/ | Kept as experimental, separate concern |
Verification
npm run typecheck— all types resolve with new SDKnpm run build— tsup bundles cleanly- Test suite with
HONCHO_API_KEY+HONCHO_WORKSPACE_ID:- Context-only (no session)
- Session with persistence
- persistInput true/false
- injectHistory true/false
- Multi-peer with different assistantIds
- Tools standalone and with middleware
honcho.send()- messages array (no duplication)
- Verify
v3Fetchhack is gone — SDK natively speaks v3