* fix(ci): resolve all CI failures — unversioned integrations, test retries - Move integration docs to separate unversioned docs plugin (docs-integrations/) so new integrations don't need to be duplicated across versioned_docs - Remove integration pages from versioned_docs (v0.3, v0.4) — sidebar entries now use links instead of doc refs - Add missing title/description SEO frontmatter to autogen.md - Add retry logic (2 attempts) to test-doc-examples.sh for transient LLM timeouts - Add pytest-rerunfailures to test-api with --reruns 2 for flaky Gemini-dependent integration tests * ci: retrigger * fix: graph entity inheritance, SyncTaskBackend error propagation, fact_type test regressions - Fix observation entity inheritance in get_graph_data: the unit_entities query only fetched entities for visible observation IDs, not their source memory IDs, so the inheritance loop always found an empty entity_map - Remove error swallowing in SyncTaskBackend._execute_task so test failures surface instead of being silently logged - Wrap remaining consolidation submission call sites with try/except since consolidation is non-critical for those operations - Fix test_sync_backend test to expect errors to propagate - Remove fact_type=["world"] filter from test_document_upsert_behavior and test_mentioned_at_from_context_string (same PR #848 regression) - Remove flaky marker from consolidation test (now deterministic)
5.5 KiB
| sidebar_position | title | description |
|---|---|---|
| 5 | Vercel Chat SDK Persistent Memory with Hindsight | Integration | Give your Vercel Chat SDK bot persistent, per-user memory across Slack, Discord, Teams, and more. Single handler wrapper, no custom plumbing required. |
Vercel Chat SDK
We built @vectorize-io/hindsight-chat to give Vercel Chat SDK bots persistent, per-user memory with a single handler wrapper. The integration works across Slack, Discord, Teams, Google Chat, GitHub, and Linear — no custom plumbing required.
Installation
npm install @vectorize-io/hindsight-chat
Quick Start
import { Chat } from 'chat';
import { HindsightClient } from '@vectorize-io/hindsight-client';
import { withHindsightChat } from '@vectorize-io/hindsight-chat';
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
const chat = new Chat({ connectors: [/* your connectors */] });
const hindsight = new HindsightClient({ apiKey: process.env.HINDSIGHT_API_KEY });
chat.onNewMention(
withHindsightChat(
{
client: hindsight,
bankId: (msg) => msg.author.userId, // per-user memory
},
async (thread, message, ctx) => {
await thread.subscribe();
const result = await streamText({
model: openai('gpt-4o'),
system: ctx.memoriesAsSystemPrompt(),
messages: [{ role: 'user', content: message.text }],
});
// Stream the response
const chunks: string[] = [];
for await (const chunk of result.textStream) {
chunks.push(chunk);
}
const fullResponse = chunks.join('');
await thread.post(fullResponse);
// Store the conversation in memory
await ctx.retain(
`User: ${message.text}\nAssistant: ${fullResponse}`
);
}
)
);
Configuration
withHindsightChat(options, handler)
withHindsightChat wraps your existing Chat SDK handler and injects memory context automatically. It returns a standard handler (thread, message) => Promise<void> so it drops in without changing your handler signature.
Options
| Option | Type | Default | Description |
|---|---|---|---|
client |
HindsightClient |
required | Hindsight client instance |
bankId |
string | (msg) => string |
required | Memory bank ID or resolver function |
recall.enabled |
boolean |
true |
Auto-recall memories before handler |
recall.budget |
'low' | 'mid' | 'high' |
'mid' |
Processing budget for recall |
recall.maxTokens |
number |
API default | Max tokens for recall results |
recall.types |
FactType[] |
all | Filter to specific fact types |
recall.includeEntities |
boolean |
true |
Include entity observations |
retain.enabled |
boolean |
false |
Auto-retain inbound messages |
retain.async |
boolean |
true |
Fire-and-forget retain |
retain.tags |
string[] |
– | Tags for retained memories |
retain.metadata |
Record<string, string> |
– | Metadata for retained memories |
Context (ctx)
We inject a third ctx argument into your handler that exposes the full Hindsight memory API scoped to the current user's bank:
| Property/Method | Description |
|---|---|
ctx.bankId |
Resolved bank ID |
ctx.memories |
Array of recalled memories |
ctx.entities |
Entity observations (or null) |
ctx.memoriesAsSystemPrompt(options?) |
Format memories for LLM system prompt |
ctx.retain(content, options?) |
Store content in memory |
ctx.recall(query, options?) |
Search memories |
ctx.reflect(query, options?) |
Reason over memories |
Examples
Subscribed Message Handler
chat.onSubscribedMessage(
withHindsightChat(
{
client: hindsight,
bankId: (msg) => msg.author.userId,
recall: { budget: 'high', maxTokens: 1000 },
},
async (thread, message, ctx) => {
const result = await generateText({
model: openai('gpt-4o'),
system: ctx.memoriesAsSystemPrompt(),
messages: [{ role: 'user', content: message.text }],
});
await thread.post(result.text);
}
)
);
Auto-Retain Inbound Messages
chat.onNewMention(
withHindsightChat(
{
client: hindsight,
bankId: (msg) => msg.author.userId,
retain: { enabled: true, tags: ['slack', 'inbound'] },
},
async (thread, message, ctx) => {
// Inbound message is already being retained automatically
const result = await generateText({
model: openai('gpt-4o'),
system: ctx.memoriesAsSystemPrompt(),
messages: [{ role: 'user', content: message.text }],
});
await thread.post(result.text);
// Retain the assistant response separately
await ctx.retain(`Assistant: ${result.text}`, {
tags: ['slack', 'outbound'],
});
}
)
);
Static Bank ID (Shared Memory)
// All users share the same memory bank
chat.onNewMention(
withHindsightChat(
{ client: hindsight, bankId: 'shared-team-memory' },
async (thread, message, ctx) => {
// ...
}
)
);
Error Handling
We designed the integration so that memory failures never break your bot. Auto-recall and auto-retain errors are caught internally, logged as warnings, and the handler continues with empty memories. Manual ctx.retain(), ctx.recall(), and ctx.reflect() calls propagate errors normally so you can handle them as needed.