diff --git a/hindsight-docs/docs/developer/configuration.md b/hindsight-docs/docs/developer/configuration.md
index eeb82c0d..687d8828 100644
--- a/hindsight-docs/docs/developer/configuration.md
+++ b/hindsight-docs/docs/developer/configuration.md
@@ -68,7 +68,7 @@ Hindsight supports three PostgreSQL vector extensions:
#### **pgvector** (HNSW - default)
- In-memory index using Hierarchical Navigable Small World algorithm
- Works well for most embeddings and dataset sizes
-- Fast for small-medium datasets (<10M vectors)
+- Fast for small-medium datasets (<10M vectors)
- Higher memory usage for large datasets
- Most widely deployed and supported
@@ -97,7 +97,7 @@ Hindsight supports three PostgreSQL vector extensions:
- When disk I/O is not a bottleneck
**When to use pgvector (HNSW):**
-- Small-medium datasets (<10M vectors)
+- Small-medium datasets (<10M vectors)
- Maximum query speed when all data fits in memory
- Simple nearest-neighbor queries without filters
- Standard PostgreSQL deployment preference
diff --git a/hindsight-docs/docs/sdks/integrations/ai-sdk.md b/hindsight-docs/docs/sdks/integrations/ai-sdk.md
deleted file mode 100644
index c538fc24..00000000
--- a/hindsight-docs/docs/sdks/integrations/ai-sdk.md
+++ /dev/null
@@ -1,366 +0,0 @@
----
-sidebar_position: 4
----
-
-# Vercel AI SDK
-
-Official Hindsight integration for the [Vercel AI SDK](https://ai-sdk.dev).
-
-## Features
-
-- **7 Memory Tools**: Core memory operations (retain, recall, reflect), mental models (create, query), documents (get), and directives (create)
-- **AI SDK 6 Native**: Works seamlessly with `generateText`, `streamText`, and `ToolLoopAgent`
-- **Multi-User Support**: Dynamic bank IDs per tool call for multi-user/multi-tenant scenarios
-- **Full Parameter Support**: Complete access to all Hindsight API parameters
-- **Type-Safe**: Full TypeScript support with Zod schemas for validation
-
-## Installation
-
-```bash
-npm install @vectorize-io/hindsight-ai-sdk @vectorize-io/hindsight-client ai zod
-```
-
-## Quick Start
-
-### 1. Set up your Hindsight client
-
-```typescript
-import { HindsightClient } from '@vectorize-io/hindsight-client';
-
-const hindsightClient = new HindsightClient({
- apiUrl: process.env.HINDSIGHT_API_URL || 'http://localhost:8000',
-});
-```
-
-### 2. Create Hindsight tools
-
-```typescript
-import { createHindsightTools } from '@vectorize-io/hindsight-ai-sdk';
-
-const tools = createHindsightTools({
- client: hindsightClient,
-});
-```
-
-### 3. Use with AI SDK
-
-```typescript
-import { generateText } from 'ai';
-import { anthropic } from '@ai-sdk/anthropic';
-
-const result = await generateText({
- model: anthropic('claude-sonnet-4-20250514'),
- tools,
- prompt: 'Remember that Alice loves hiking and prefers spicy food',
-});
-
-console.log(result.text);
-```
-
-## Memory Tools
-
-The integration provides seven tools that the AI model can use to manage memory:
-
-### `retain` - Store Information
-
-The model calls this tool to store information for future recall.
-
-**Parameters:**
-- `bankId` (required): Memory bank ID (usually the user ID)
-- `content` (required): Content to store
-- `documentId` (optional): Document ID for grouping/upserting related memories
-- `timestamp` (optional): ISO timestamp for when the memory occurred
-- `context` (optional): Additional context about the memory
-- `metadata` (optional): Key-value metadata for filtering
-
-**Example tool call:**
-```typescript
-{
- bankId: "user-123",
- content: "Alice loves hiking and goes to Yosemite every summer",
- context: "User preferences",
- timestamp: "2024-01-15T10:30:00Z"
-}
-```
-
-**Returns:**
-```typescript
-{
- success: true,
- itemsCount: 1
-}
-```
-
-### `recall` - Search Memories
-
-The model calls this tool to search for relevant information in memory.
-
-**Parameters:**
-- `bankId` (required): Memory bank ID
-- `query` (required): What to search for
-- `types` (optional): Filter by fact types (`['world', 'experience', 'opinion']`)
-- `maxTokens` (optional): Maximum tokens to return (default: 4096)
-- `budget` (optional): Processing budget - `'low'`, `'mid'`, or `'high'`
-- `queryTimestamp` (optional): Query from a specific time (ISO format)
-- `includeEntities` (optional): Include entity observations
-- `includeChunks` (optional): Include raw document chunks
-
-**Example tool call:**
-```typescript
-{
- bankId: "user-123",
- query: "What does Alice like to do outdoors?",
- types: ["world", "experience"],
- maxTokens: 2048,
- budget: "mid"
-}
-```
-
-**Returns:**
-```typescript
-{
- results: [
- {
- id: "mem-123",
- text: "Alice loves hiking",
- type: "world",
- entities: ["Alice"],
- context: "User preferences",
- occurred_start: "2024-01-15T10:30:00Z",
- document_id: "doc-456",
- metadata: { source: "chat" }
- }
- ],
- entities: {
- "Alice": {
- canonical_name: "Alice",
- mention_count: 15,
- observations: [...]
- }
- }
-}
-```
-
-### `reflect` - Synthesize Insights
-
-The model calls this tool to analyze memories and generate contextual insights.
-
-**Parameters:**
-- `bankId` (required): Memory bank ID
-- `query` (required): Question to reflect on
-- `context` (optional): Additional context for reflection
-- `budget` (optional): Processing budget - `'low'`, `'mid'`, or `'high'`
-
-**Example tool call:**
-```typescript
-{
- bankId: "user-123",
- query: "What outdoor activities does Alice enjoy?",
- context: "Planning a weekend trip",
- budget: "mid"
-}
-```
-
-**Returns:**
-```typescript
-{
- text: "Alice is an avid hiker who particularly enjoys visiting Yosemite National Park during summer months. She has expressed strong preferences for mountain trails over beach activities.",
- basedOn: [
- {
- id: "mem-123",
- text: "Alice loves hiking",
- type: "world",
- context: "User preferences",
- occurred_start: "2024-01-15T10:30:00Z"
- }
- ]
-}
-```
-
-### `createMentalModel` - Create Knowledge Consolidation
-
-The model calls this tool to create a mental model that automatically consolidates memories into structured knowledge.
-
-**Parameters:**
-- `bankId` (required): Memory bank ID
-- `mentalModelId` (optional): Custom ID for the mental model (auto-generated if not provided)
-- `name` (optional): Name for the mental model
-- `sourceQuery` (optional): Query defining which memories to consolidate
-- `tags` (optional): Tags for organizing mental models
-- `maxTokens` (optional): Maximum tokens for the content
-- `autoRefresh` (optional): Auto-refresh after new consolidations (default: false)
-
-**Example tool call:**
-```typescript
-{
- bankId: "user-123",
- name: "User Preferences",
- sourceQuery: "What are the user's preferences?",
- tags: ["preferences"],
- autoRefresh: true
-}
-```
-
-**Returns:**
-```typescript
-{
- mentalModelId: "mm-456",
- createdAt: "2024-01-15T10:30:00Z"
-}
-```
-
-### `queryMentalModel` - Retrieve Consolidated Knowledge
-
-The model calls this tool to retrieve synthesized insights from an existing mental model.
-
-**Parameters:**
-- `bankId` (required): Memory bank ID
-- `mentalModelId` (required): ID of the mental model to query
-
-**Example tool call:**
-```typescript
-{
- bankId: "user-123",
- mentalModelId: "mm-456"
-}
-```
-
-**Returns:**
-```typescript
-{
- content: "The user prefers outdoor activities, particularly hiking. They enjoy mountain trails and visit Yosemite regularly during summer.",
- name: "User Preferences",
- updatedAt: "2024-01-20T15:45:00Z"
-}
-```
-
-### `getDocument` - Retrieve Stored Document
-
-The model calls this tool to retrieve a stored document by its ID.
-
-**Parameters:**
-- `bankId` (required): Memory bank ID
-- `documentId` (required): ID of the document to retrieve
-
-**Example tool call:**
-```typescript
-{
- bankId: "user-123",
- documentId: "doc-789"
-}
-```
-
-**Returns:**
-```typescript
-{
- originalText: "User profile: Alice, Software Engineer, loves hiking...",
- id: "doc-789",
- createdAt: "2024-01-10T09:00:00Z",
- updatedAt: "2024-01-15T14:30:00Z"
-}
-```
-
-### `createDirective` - Create Behavioral Rule
-
-The model calls this tool to create a directive—a hard rule injected into prompts during reflect operations.
-
-**Parameters:**
-- `bankId` (required): Memory bank ID
-- `name` (required): Human-readable name for the directive
-- `content` (required): The directive text to inject
-- `priority` (optional): Higher priority directives are injected first (default: 0)
-- `isActive` (optional): Whether this directive is active (default: true)
-- `tags` (optional): Tags for filtering (e.g., user-specific directives)
-
-**Example tool call:**
-```typescript
-{
- bankId: "user-123",
- name: "Response Format",
- content: "Always provide responses in bullet-point format",
- priority: 10,
- tags: ["formatting"]
-}
-```
-
-**Returns:**
-```typescript
-{
- id: "dir-321",
- name: "Response Format",
- content: "Always provide responses in bullet-point format",
- tags: ["formatting"],
- createdAt: "2024-01-15T10:30:00Z"
-}
-```
-
-## Usage Examples
-
-### Using with `generateText`
-
-```typescript
-import { HindsightClient } from '@vectorize-io/hindsight-client';
-import { createHindsightTools } from '@vectorize-io/hindsight-ai-sdk';
-import { generateText } from 'ai';
-import { anthropic } from '@ai-sdk/anthropic';
-
-const hindsightClient = new HindsightClient({
- apiUrl: 'http://localhost:8000',
-});
-
-const tools = createHindsightTools({ client: hindsightClient });
-
-const result = await generateText({
- model: anthropic('claude-sonnet-4-20250514'),
- tools,
- system: `You are a helpful assistant with long-term memory. Use the recall tool to check for relevant memories before responding.`,
- prompt: 'Remember that Alice loves hiking and prefers spicy food',
-});
-
-console.log(result.text);
-```
-
-### Using with `streamText`
-
-```typescript
-import { streamText } from 'ai';
-
-const result = streamText({
- model: anthropic('claude-sonnet-4-20250514'),
- tools,
- system: `You have persistent memory. Use retain to store important information and recall to retrieve it.`,
- prompt: 'What do you know about Alice?',
-});
-
-for await (const chunk of result.textStream) {
- process.stdout.write(chunk);
-}
-```
-
-### Using with `ToolLoopAgent`
-
-```typescript
-import { ToolLoopAgent, stopWhen, stepCountIs } from 'ai';
-
-const agent = new ToolLoopAgent({
- model: anthropic('claude-sonnet-4-20250514'),
- tools,
- instructions: `You are a personal assistant with long-term memory. Always check recall before responding and use retain to store important information.`,
- stopWhen: stepCountIs(10),
-});
-
-const result = await agent.generate({
- prompt: 'What did I say I wanted to work on this week?',
-});
-```
-
-### Multi-User Support
-
-```typescript
-const result = await generateText({
- model: anthropic('claude-sonnet-4-20250514'),
- tools,
- system: `You are a helpful assistant. The user's ID is: ${userId}. Always pass this as the bankId parameter to memory tools.`,
- prompt: 'Remember that I prefer dark mode',
-});
-```
diff --git a/hindsight-docs/docs/sdks/integrations/ai-sdk.mdx b/hindsight-docs/docs/sdks/integrations/ai-sdk.mdx
new file mode 100644
index 00000000..0f9ee62b
--- /dev/null
+++ b/hindsight-docs/docs/sdks/integrations/ai-sdk.mdx
@@ -0,0 +1,97 @@
+---
+sidebar_position: 4
+---
+
+# Vercel AI SDK
+
+The `@vectorize-io/hindsight-ai-sdk` package integrates [Hindsight](https://hindsight.vectorize.io) memory with the [Vercel AI SDK](https://ai-sdk.dev). It provides five ready-to-use tools for retaining, recalling, and reflecting on long-term memories.
+
+import CodeSnippet from '@site/src/components/CodeSnippet';
+
+import aiSdkTs from '!!raw-loader!@site/examples/integrations/ai-sdk.ts';
+
+## Installation
+
+```bash
+npm install @vectorize-io/hindsight-ai-sdk @vectorize-io/hindsight-client ai
+```
+
+## Setup
+
+Create a Hindsight client and pass it to `createHindsightTools` along with a `bankId`. The `bankId` identifies the memory store for this session—typically a user ID.
+
+
+
+:::tip Per-request bank IDs
+In multi-user applications, create `tools` inside your request handler so each request closes over the correct `bankId`. See the [Next.js example](#in-a-nextjs-route-handler) below.
+:::
+
+## Usage
+
+### With `generateText`
+
+
+
+### With `streamText`
+
+
+
+### With `ToolLoopAgent`
+
+
+
+### In a Next.js Route Handler
+
+
+
+---
+
+## Tools Reference
+
+Five tools are registered. The `bankId` is fixed at creation time—the agent cannot change it.
+
+| Tool | What the agent provides | What the constructor controls |
+|------|------------------------|-------------------------------|
+| `retain` | `content`, `documentId`, `timestamp`, `context` | `async`, `tags`, `metadata` |
+| `recall` | `query`, `queryTimestamp` | `budget`, `types`, `maxTokens`, `includeEntities`, `includeChunks` |
+| `reflect` | `query`, `context` | `budget` |
+| `getMentalModel` | `mentalModelId` | — |
+| `getDocument` | `documentId` | — |
+
+**Why this split?** Semantic inputs (what to remember, what to search for) belong to the agent. Infrastructure concerns (cost budget, tagging strategy, async mode) belong to the application.
+
+---
+
+## Constructor Options
+
+All options except `client` and `bankId` are optional. Each tool's options are grouped under the tool name.
+
+
+
+### `retain`
+
+| Option | Type | Default | Description |
+|--------|------|---------|-------------|
+| `async` | `boolean` | `false` | Fire-and-forget — do not wait for ingestion to complete |
+| `tags` | `string[]` | — | Tags attached to every retained memory |
+| `metadata` | `Record` | — | Metadata attached to every retained memory |
+| `description` | `string` | built-in | Override the tool description shown to the model |
+
+### `recall`
+
+| Option | Type | Default | Description |
+|--------|------|---------|-------------|
+| `budget` | `'low' \| 'mid' \| 'high'` | `'mid'` | Controls retrieval depth and latency |
+| `types` | `('world' \| 'experience' \| 'observation')[]` | all | Restrict results to these fact types |
+| `maxTokens` | `number` | API default | Cap the total tokens returned |
+| `includeEntities` | `boolean` | `false` | Include entity observations in results |
+| `includeChunks` | `boolean` | `false` | Include raw source chunks in results |
+| `description` | `string` | built-in | Override the tool description shown to the model |
+
+### `reflect`
+
+| Option | Type | Default | Description |
+|--------|------|---------|-------------|
+| `budget` | `'low' \| 'mid' \| 'high'` | `'mid'` | Controls synthesis depth and latency |
+| `maxTokens` | `number` | API default | Maximum tokens for the response |
+| `description` | `string` | built-in | Override the tool description shown to the model |
diff --git a/hindsight-docs/examples/integrations/ai-sdk.ts b/hindsight-docs/examples/integrations/ai-sdk.ts
new file mode 100644
index 00000000..1983feb0
--- /dev/null
+++ b/hindsight-docs/examples/integrations/ai-sdk.ts
@@ -0,0 +1,122 @@
+/**
+ * Hindsight AI SDK integration examples
+ * These snippets are embedded in the documentation via CodeSnippet.
+ */
+
+// [docs:setup]
+import { HindsightClient } from '@vectorize-io/hindsight-client';
+import { createHindsightTools } from '@vectorize-io/hindsight-ai-sdk';
+
+const client = new HindsightClient({ baseUrl: 'http://localhost:8888' });
+
+const tools = createHindsightTools({
+ client,
+ bankId: 'user-123',
+});
+// [/docs:setup]
+
+// [docs:generate-text]
+import { generateText } from 'ai';
+import { openai } from '@ai-sdk/openai';
+
+const { text } = await generateText({
+ model: openai('gpt-4o'),
+ tools,
+ maxSteps: 5,
+ system: 'You are a helpful assistant with long-term memory.',
+ prompt: 'Remember that I prefer dark mode and large fonts.',
+});
+// [/docs:generate-text]
+
+// [docs:stream-text]
+import { streamText } from 'ai';
+
+const result = streamText({
+ model: openai('gpt-4o'),
+ tools,
+ maxSteps: 5,
+ system: 'You are a helpful assistant with long-term memory.',
+ prompt: 'What are my display preferences?',
+});
+
+for await (const chunk of result.textStream) {
+ process.stdout.write(chunk);
+}
+// [/docs:stream-text]
+
+// [docs:tool-loop-agent]
+import { generateText, ToolLoopAgent, stepCountIs } from 'ai';
+import { openai } from '@ai-sdk/openai';
+import { HindsightClient } from '@vectorize-io/hindsight-client';
+import { createHindsightTools } from '@vectorize-io/hindsight-ai-sdk';
+
+const client = new HindsightClient({ baseUrl: process.env.HINDSIGHT_API_URL! });
+
+const agent = new ToolLoopAgent({
+ model: openai('gpt-4o'),
+ tools: createHindsightTools({ client, bankId: 'user-123' }),
+ stopWhen: stepCountIs(10),
+ system: 'You are a helpful assistant with long-term memory.',
+});
+
+const result = await agent.generate({
+ prompt: 'Remember that my favorite editor is Neovim',
+});
+// [/docs:tool-loop-agent]
+
+// [docs:next-api-route]
+// app/api/chat/route.ts
+import { streamText } from 'ai';
+import { openai } from '@ai-sdk/openai';
+import { HindsightClient } from '@vectorize-io/hindsight-client';
+import { createHindsightTools } from '@vectorize-io/hindsight-ai-sdk';
+
+const hindsightClient = new HindsightClient({
+ baseUrl: process.env.HINDSIGHT_API_URL!,
+});
+
+export async function POST(req: Request) {
+ const { messages, userId } = await req.json();
+
+ // Tools are created per-request, closing over the current user's bankId
+ const tools = createHindsightTools({
+ client: hindsightClient,
+ bankId: userId,
+ });
+
+ return streamText({
+ model: openai('gpt-4o'),
+ tools,
+ maxSteps: 5,
+ system: 'You are a helpful assistant with long-term memory.',
+ messages,
+ }).toDataStreamResponse();
+}
+// [/docs:next-api-route]
+
+// [docs:constructor-options]
+const tools = createHindsightTools({
+ client,
+ bankId: userId,
+
+ retain: {
+ async: true, // fire-and-forget (default: false)
+ tags: ['env:prod', 'app:support'], // always attached to every retained memory
+ metadata: { version: '2.0' }, // always attached to every retained memory
+ },
+
+ recall: {
+ budget: 'high', // processing depth: low | mid | high (default: 'mid')
+ types: ['experience', 'world'], // restrict to these fact types (default: all)
+ maxTokens: 2048, // cap token budget (default: API default)
+ includeEntities: true, // include entity observations (default: false)
+ includeChunks: true, // include raw source chunks (default: false)
+ },
+
+ reflect: {
+ budget: 'mid', // processing depth (default: 'mid')
+ },
+
+});
+
+// [/docs:constructor-options]
diff --git a/hindsight-integrations/ai-sdk/package-lock.json b/hindsight-integrations/ai-sdk/package-lock.json
index e0756474..20045054 100644
--- a/hindsight-integrations/ai-sdk/package-lock.json
+++ b/hindsight-integrations/ai-sdk/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@vectorize-io/hindsight-ai-sdk",
- "version": "0.4.8",
+ "version": "0.4.11",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@vectorize-io/hindsight-ai-sdk",
- "version": "0.4.8",
+ "version": "0.4.11",
"license": "MIT",
"devDependencies": {
"@types/node": "^22.0.0",
diff --git a/hindsight-integrations/ai-sdk/src/tools/index.test.ts b/hindsight-integrations/ai-sdk/src/tools/index.test.ts
index b431cda1..4df6a2fd 100644
--- a/hindsight-integrations/ai-sdk/src/tools/index.test.ts
+++ b/hindsight-integrations/ai-sdk/src/tools/index.test.ts
@@ -13,31 +13,34 @@ describe('createHindsightTools', () => {
});
describe('tool creation', () => {
- it('should create all three tools', () => {
- const tools = createHindsightTools({ client: mockClient });
+ it('should create all tools', () => {
+ const tools = createHindsightTools({ client: mockClient, bankId: 'test-bank' });
expect(tools).toHaveProperty('retain');
expect(tools).toHaveProperty('recall');
expect(tools).toHaveProperty('reflect');
+ expect(tools).toHaveProperty('getMentalModel');
+ expect(tools).toHaveProperty('getDocument');
expect(typeof tools.retain.execute).toBe('function');
expect(typeof tools.recall.execute).toBe('function');
expect(typeof tools.reflect.execute).toBe('function');
});
it('should use default descriptions when not provided', () => {
- const tools = createHindsightTools({ client: mockClient });
+ const tools = createHindsightTools({ client: mockClient, bankId: 'test-bank' });
expect(tools.retain.description).toContain('Store information in long-term memory');
expect(tools.recall.description).toContain('Search memory for relevant information');
expect(tools.reflect.description).toContain('Analyze memories to form insights');
});
- it('should use custom descriptions when provided', () => {
+ it('should use custom descriptions from nested options', () => {
const tools = createHindsightTools({
client: mockClient,
- retainDescription: 'Custom retain description',
- recallDescription: 'Custom recall description',
- reflectDescription: 'Custom reflect description',
+ bankId: 'test-bank',
+ retain: { description: 'Custom retain description' },
+ recall: { description: 'Custom recall description' },
+ reflect: { description: 'Custom reflect description' },
});
expect(tools.retain.description).toBe('Custom retain description');
@@ -47,8 +50,8 @@ describe('createHindsightTools', () => {
});
describe('retain tool', () => {
- it('should call client.retain with correct parameters', async () => {
- const tools = createHindsightTools({ client: mockClient });
+ it('should call client.retain with agent inputs and constructor defaults', async () => {
+ const tools = createHindsightTools({ client: mockClient, bankId: 'test-bank' });
vi.mocked(mockClient.retain).mockResolvedValue({
success: true,
bank_id: 'test-bank',
@@ -56,21 +59,21 @@ describe('createHindsightTools', () => {
async: false,
});
- const result = await tools.retain.execute({
- bankId: 'test-bank',
- content: 'Test content',
- });
+ const result = await tools.retain.execute({ content: 'Test content' });
expect(mockClient.retain).toHaveBeenCalledWith('test-bank', 'Test content', {
documentId: undefined,
timestamp: undefined,
context: undefined,
+ tags: undefined,
+ metadata: undefined,
+ async: false,
});
expect(result).toEqual({ success: true, itemsCount: 5 });
});
- it('should pass optional parameters to client.retain', async () => {
- const tools = createHindsightTools({ client: mockClient });
+ it('should pass agent-provided optional inputs', async () => {
+ const tools = createHindsightTools({ client: mockClient, bankId: 'test-bank' });
vi.mocked(mockClient.retain).mockResolvedValue({
success: true,
bank_id: 'test-bank',
@@ -79,7 +82,6 @@ describe('createHindsightTools', () => {
});
await tools.retain.execute({
- bankId: 'test-bank',
content: 'Test content',
documentId: 'doc-123',
timestamp: '2024-01-01T00:00:00Z',
@@ -90,100 +92,123 @@ describe('createHindsightTools', () => {
documentId: 'doc-123',
timestamp: '2024-01-01T00:00:00Z',
context: 'Test context',
+ tags: undefined,
+ metadata: undefined,
+ async: false,
});
});
- it('should transform response correctly', async () => {
- const tools = createHindsightTools({ client: mockClient });
+ it('should apply constructor-level retain options', async () => {
+ const tools = createHindsightTools({
+ client: mockClient,
+ bankId: 'test-bank',
+ retain: {
+ async: true,
+ tags: ['env:prod', 'app:support'],
+ metadata: { version: '1.0' },
+ },
+ });
vi.mocked(mockClient.retain).mockResolvedValue({
success: true,
bank_id: 'test-bank',
- items_count: 10,
- async: false,
+ items_count: 1,
+ async: true,
});
- const result = await tools.retain.execute({
- bankId: 'test-bank',
- content: 'Test content',
- });
+ await tools.retain.execute({ content: 'Test content' });
- expect(result).toEqual({ success: true, itemsCount: 10 });
+ expect(mockClient.retain).toHaveBeenCalledWith('test-bank', 'Test content', {
+ documentId: undefined,
+ timestamp: undefined,
+ context: undefined,
+ tags: ['env:prod', 'app:support'],
+ metadata: { version: '1.0' },
+ async: true,
+ });
});
});
describe('recall tool', () => {
- it('should call client.recall with correct parameters', async () => {
- const tools = createHindsightTools({ client: mockClient });
+ it('should call client.recall with agent inputs and constructor defaults', async () => {
+ const tools = createHindsightTools({ client: mockClient, bankId: 'test-bank' });
vi.mocked(mockClient.recall).mockResolvedValue({
- results: [
- {
- id: 'fact-1',
- text: 'Test fact',
- type: 'preference',
- },
- ],
+ results: [{ id: 'fact-1', text: 'Test fact', type: 'preference' }],
});
- const result = await tools.recall.execute({
- bankId: 'test-bank',
- query: 'Test query',
- });
+ const result = await tools.recall.execute({ query: 'Test query' });
expect(mockClient.recall).toHaveBeenCalledWith('test-bank', 'Test query', {
types: undefined,
maxTokens: undefined,
- budget: undefined,
+ budget: 'mid',
queryTimestamp: undefined,
- includeEntities: undefined,
- includeChunks: undefined,
+ includeEntities: false,
+ includeChunks: false,
});
expect(result.results).toHaveLength(1);
expect(result.results[0].id).toBe('fact-1');
});
- it('should pass all optional parameters to client.recall', async () => {
- const tools = createHindsightTools({ client: mockClient });
- vi.mocked(mockClient.recall).mockResolvedValue({
- results: [],
- });
+ it('should pass agent-provided queryTimestamp', async () => {
+ const tools = createHindsightTools({ client: mockClient, bankId: 'test-bank' });
+ vi.mocked(mockClient.recall).mockResolvedValue({ results: [] });
await tools.recall.execute({
- bankId: 'test-bank',
query: 'Test query',
- types: ['preference', 'fact'],
- maxTokens: 1000,
- budget: 'high',
queryTimestamp: '2024-01-01T00:00:00Z',
- includeEntities: true,
- includeChunks: true,
});
+ expect(mockClient.recall).toHaveBeenCalledWith('test-bank', 'Test query', {
+ types: undefined,
+ maxTokens: undefined,
+ budget: 'mid',
+ queryTimestamp: '2024-01-01T00:00:00Z',
+ includeEntities: false,
+ includeChunks: false,
+ });
+ });
+
+ it('should apply constructor-level recall options', async () => {
+ const tools = createHindsightTools({
+ client: mockClient,
+ bankId: 'test-bank',
+ recall: {
+ types: ['preference', 'fact'],
+ maxTokens: 1000,
+ budget: 'high',
+ includeEntities: true,
+ includeChunks: true,
+ },
+ });
+ vi.mocked(mockClient.recall).mockResolvedValue({ results: [] });
+
+ await tools.recall.execute({ query: 'Test query' });
+
expect(mockClient.recall).toHaveBeenCalledWith('test-bank', 'Test query', {
types: ['preference', 'fact'],
maxTokens: 1000,
budget: 'high',
- queryTimestamp: '2024-01-01T00:00:00Z',
+ queryTimestamp: undefined,
includeEntities: true,
includeChunks: true,
});
});
it('should handle empty results', async () => {
- const tools = createHindsightTools({ client: mockClient });
- vi.mocked(mockClient.recall).mockResolvedValue({
- results: undefined as any,
- });
+ const tools = createHindsightTools({ client: mockClient, bankId: 'test-bank' });
+ vi.mocked(mockClient.recall).mockResolvedValue({ results: undefined as any });
- const result = await tools.recall.execute({
- bankId: 'test-bank',
- query: 'Test query',
- });
+ const result = await tools.recall.execute({ query: 'Test query' });
expect(result.results).toEqual([]);
});
it('should include entities when present', async () => {
- const tools = createHindsightTools({ client: mockClient });
+ const tools = createHindsightTools({
+ client: mockClient,
+ bankId: 'test-bank',
+ recall: { includeEntities: true },
+ });
const entities = {
'entity-1': {
entity_id: 'entity-1',
@@ -191,59 +216,39 @@ describe('createHindsightTools', () => {
observations: [{ text: 'Alice loves hiking' }],
},
};
+ vi.mocked(mockClient.recall).mockResolvedValue({ results: [], entities });
- vi.mocked(mockClient.recall).mockResolvedValue({
- results: [],
- entities,
- });
-
- const result = await tools.recall.execute({
- bankId: 'test-bank',
- query: 'Test query',
- includeEntities: true,
- });
+ const result = await tools.recall.execute({ query: 'Test query' });
expect(result.entities).toEqual(entities);
});
});
describe('reflect tool', () => {
- it('should call client.reflect with correct parameters', async () => {
- const tools = createHindsightTools({ client: mockClient });
+ it('should call client.reflect with agent inputs and constructor defaults', async () => {
+ const tools = createHindsightTools({ client: mockClient, bankId: 'test-bank' });
vi.mocked(mockClient.reflect).mockResolvedValue({
text: 'Reflection result',
- based_on: [
- {
- id: 'fact-1',
- text: 'Supporting fact',
- },
- ],
+ based_on: [{ id: 'fact-1', text: 'Supporting fact' }],
});
- const result = await tools.reflect.execute({
- bankId: 'test-bank',
- query: 'What are my preferences?',
- });
+ const result = await tools.reflect.execute({ query: 'What are my preferences?' });
expect(mockClient.reflect).toHaveBeenCalledWith('test-bank', 'What are my preferences?', {
context: undefined,
- budget: undefined,
+ budget: 'mid',
});
expect(result.text).toBe('Reflection result');
expect(result.basedOn).toHaveLength(1);
});
- it('should pass optional parameters to client.reflect', async () => {
- const tools = createHindsightTools({ client: mockClient });
- vi.mocked(mockClient.reflect).mockResolvedValue({
- text: 'Reflection result',
- });
+ it('should pass agent-provided context', async () => {
+ const tools = createHindsightTools({ client: mockClient, bankId: 'test-bank' });
+ vi.mocked(mockClient.reflect).mockResolvedValue({ text: 'Reflection result' });
await tools.reflect.execute({
- bankId: 'test-bank',
query: 'What are my preferences?',
context: 'User context',
- budget: 'mid',
});
expect(mockClient.reflect).toHaveBeenCalledWith('test-bank', 'What are my preferences?', {
@@ -252,44 +257,43 @@ describe('createHindsightTools', () => {
});
});
- it('should handle empty text response with fallback', async () => {
- const tools = createHindsightTools({ client: mockClient });
- vi.mocked(mockClient.reflect).mockResolvedValue({
- text: undefined as any,
- });
-
- const result = await tools.reflect.execute({
+ it('should apply constructor-level reflect budget', async () => {
+ const tools = createHindsightTools({
+ client: mockClient,
bankId: 'test-bank',
- query: 'Test query',
+ reflect: { budget: 'low' },
});
+ vi.mocked(mockClient.reflect).mockResolvedValue({ text: 'Reflection result' });
+
+ await tools.reflect.execute({ query: 'Test query' });
+
+ expect(mockClient.reflect).toHaveBeenCalledWith('test-bank', 'Test query', {
+ context: undefined,
+ budget: 'low',
+ });
+ });
+
+ it('should handle empty text response with fallback', async () => {
+ const tools = createHindsightTools({ client: mockClient, bankId: 'test-bank' });
+ vi.mocked(mockClient.reflect).mockResolvedValue({ text: undefined as any });
+
+ const result = await tools.reflect.execute({ query: 'Test query' });
expect(result.text).toBe('No insights available yet.');
});
it('should include basedOn facts when present', async () => {
- const tools = createHindsightTools({ client: mockClient });
+ const tools = createHindsightTools({ client: mockClient, bankId: 'test-bank' });
const basedOn = [
- {
- id: 'fact-1',
- text: 'User prefers spicy food',
- type: 'preference',
- },
- {
- id: 'fact-2',
- text: 'User is allergic to nuts',
- type: 'health',
- },
+ { id: 'fact-1', text: 'User prefers spicy food', type: 'preference' },
+ { id: 'fact-2', text: 'User is allergic to nuts', type: 'health' },
];
-
vi.mocked(mockClient.reflect).mockResolvedValue({
text: 'Based on your history, you prefer spicy Asian cuisine',
based_on: basedOn,
});
- const result = await tools.reflect.execute({
- bankId: 'test-bank',
- query: 'What do I like?',
- });
+ const result = await tools.reflect.execute({ query: 'What do I like?' });
expect(result.basedOn).toEqual(basedOn);
});
@@ -297,66 +301,70 @@ describe('createHindsightTools', () => {
describe('error handling', () => {
it('should propagate errors from client.retain', async () => {
- const tools = createHindsightTools({ client: mockClient });
- const error = new Error('Retain failed');
- vi.mocked(mockClient.retain).mockRejectedValue(error);
+ const tools = createHindsightTools({ client: mockClient, bankId: 'test-bank' });
+ vi.mocked(mockClient.retain).mockRejectedValue(new Error('Retain failed'));
- await expect(
- tools.retain.execute({
- bankId: 'test-bank',
- content: 'Test content',
- })
- ).rejects.toThrow('Retain failed');
+ await expect(tools.retain.execute({ content: 'Test content' })).rejects.toThrow('Retain failed');
});
it('should propagate errors from client.recall', async () => {
- const tools = createHindsightTools({ client: mockClient });
- const error = new Error('Recall failed');
- vi.mocked(mockClient.recall).mockRejectedValue(error);
+ const tools = createHindsightTools({ client: mockClient, bankId: 'test-bank' });
+ vi.mocked(mockClient.recall).mockRejectedValue(new Error('Recall failed'));
- await expect(
- tools.recall.execute({
- bankId: 'test-bank',
- query: 'Test query',
- })
- ).rejects.toThrow('Recall failed');
+ await expect(tools.recall.execute({ query: 'Test query' })).rejects.toThrow('Recall failed');
});
it('should propagate errors from client.reflect', async () => {
- const tools = createHindsightTools({ client: mockClient });
- const error = new Error('Reflect failed');
- vi.mocked(mockClient.reflect).mockRejectedValue(error);
+ const tools = createHindsightTools({ client: mockClient, bankId: 'test-bank' });
+ vi.mocked(mockClient.reflect).mockRejectedValue(new Error('Reflect failed'));
- await expect(
- tools.reflect.execute({
- bankId: 'test-bank',
- query: 'Test query',
- })
- ).rejects.toThrow('Reflect failed');
+ await expect(tools.reflect.execute({ query: 'Test query' })).rejects.toThrow('Reflect failed');
});
});
- describe('budget schema', () => {
- it('should accept valid budget values', async () => {
- const tools = createHindsightTools({ client: mockClient });
+ describe('budget defaults', () => {
+ it('should default recall budget to mid', async () => {
+ const tools = createHindsightTools({ client: mockClient, bankId: 'test-bank' });
+ vi.mocked(mockClient.recall).mockResolvedValue({ results: [] });
+
+ await tools.recall.execute({ query: 'Test' });
+
+ expect(mockClient.recall).toHaveBeenCalledWith('test-bank', 'Test', expect.objectContaining({ budget: 'mid' }));
+ });
+
+ it('should default reflect budget to mid', async () => {
+ const tools = createHindsightTools({ client: mockClient, bankId: 'test-bank' });
+ vi.mocked(mockClient.reflect).mockResolvedValue({ text: 'ok' });
+
+ await tools.reflect.execute({ query: 'Test' });
+
+ expect(mockClient.reflect).toHaveBeenCalledWith('test-bank', 'Test', expect.objectContaining({ budget: 'mid' }));
+ });
+
+ it('should accept low/mid/high budget values', async () => {
vi.mocked(mockClient.recall).mockResolvedValue({ results: [] });
for (const budget of ['low', 'mid', 'high'] as const) {
- await tools.recall.execute({
- bankId: 'test-bank',
- query: 'Test',
- budget,
- });
-
- expect(mockClient.recall).toHaveBeenCalledWith('test-bank', 'Test', {
- types: undefined,
- maxTokens: undefined,
- budget,
- queryTimestamp: undefined,
- includeEntities: undefined,
- includeChunks: undefined,
- });
+ const tools = createHindsightTools({ client: mockClient, bankId: 'test-bank', recall: { budget } });
+ await tools.recall.execute({ query: 'Test' });
+ expect(mockClient.recall).toHaveBeenCalledWith('test-bank', 'Test', expect.objectContaining({ budget }));
}
});
});
+
+ describe('bankId enforcement', () => {
+ it('should always use the bankId from constructor options', async () => {
+ const tools = createHindsightTools({ client: mockClient, bankId: 'forced-bank' });
+ vi.mocked(mockClient.retain).mockResolvedValue({
+ success: true,
+ bank_id: 'forced-bank',
+ items_count: 1,
+ async: false,
+ });
+
+ await tools.retain.execute({ content: 'Test' });
+
+ expect(mockClient.retain).toHaveBeenCalledWith('forced-bank', 'Test', expect.anything());
+ });
+ });
});
diff --git a/hindsight-integrations/ai-sdk/src/tools/index.ts b/hindsight-integrations/ai-sdk/src/tools/index.ts
index 47fd8d5b..6102c537 100644
--- a/hindsight-integrations/ai-sdk/src/tools/index.ts
+++ b/hindsight-integrations/ai-sdk/src/tools/index.ts
@@ -7,6 +7,12 @@ import { z } from 'zod';
export const BudgetSchema = z.enum(['low', 'mid', 'high']);
export type Budget = z.infer;
+/**
+ * Fact types for filtering recall results.
+ */
+export const FactTypeSchema = z.enum(['world', 'experience', 'observation']);
+export type FactType = z.infer;
+
/**
* Recall result item from Hindsight
*/
@@ -105,15 +111,6 @@ export interface MentalModelResponse {
trigger?: MentalModelTrigger;
}
-/**
- * Create mental model response from Hindsight
- */
-export interface CreateMentalModelResponse {
- mental_model_id: string;
- bank_id: string;
- created_at: string;
-}
-
/**
* Document response from Hindsight
*/
@@ -128,36 +125,6 @@ export interface DocumentResponse {
tags?: string[];
}
-/**
- * Directive response from Hindsight
- */
-export interface DirectiveResponse {
- id: string;
- bank_id: string;
- name: string;
- content: string;
- priority: number;
- is_active: boolean;
- tags: string[];
- created_at: string;
- updated_at: string;
-}
-
-/**
- * Create directive response from Hindsight
- */
-export interface CreateDirectiveResponse {
- id: string;
- bank_id: string;
- name: string;
- content: string;
- priority: number;
- is_active: boolean;
- tags: string[];
- created_at: string;
- updated_at: string;
-}
-
/**
* Hindsight client interface - matches @vectorize-io/hindsight-client
*/
@@ -179,7 +146,7 @@ export interface HindsightClient {
bankId: string,
query: string,
options?: {
- types?: string[];
+ types?: FactType[];
maxTokens?: number;
budget?: Budget;
trace?: boolean;
@@ -197,21 +164,10 @@ export interface HindsightClient {
options?: {
context?: string;
budget?: Budget;
+ maxTokens?: number;
}
): Promise;
- createMentalModel(
- bankId: string,
- options?: {
- id?: string;
- name?: string;
- sourceQuery?: string;
- tags?: string[];
- maxTokens?: number;
- trigger?: MentalModelTrigger;
- }
- ): Promise;
-
getMentalModel(
bankId: string,
mentalModelId: string
@@ -221,148 +177,125 @@ export interface HindsightClient {
bankId: string,
documentId: string
): Promise;
-
- createDirective(
- bankId: string,
- options: {
- name: string;
- content: string;
- priority?: number;
- isActive?: boolean;
- tags?: string[];
- }
- ): Promise;
-
- listDirectives(
- bankId: string,
- options?: {
- tags?: string[];
- tagsMatch?: 'any' | 'all' | 'exact';
- activeOnly?: boolean;
- limit?: number;
- offset?: number;
- }
- ): Promise<{ directives: DirectiveResponse[]; total: number }>;
}
export interface HindsightToolsOptions {
/** Hindsight client instance */
client: HindsightClient;
- /**
- * Custom description for the retain tool.
- */
- retainDescription?: string;
- /**
- * Custom description for the recall tool.
- */
- recallDescription?: string;
- /**
- * Custom description for the reflect tool.
- */
- reflectDescription?: string;
- /**
- * Custom description for the createMentalModel tool.
- */
- createMentalModelDescription?: string;
- /**
- * Custom description for the queryMentalModel tool.
- */
- queryMentalModelDescription?: string;
- /**
- * Custom description for the getDocument tool.
- */
- getDocumentDescription?: string;
+ /** Memory bank ID to use for all tool calls (e.g. the user ID) */
+ bankId: string;
+
+ /** Options for the retain tool */
+ retain?: {
+ /** Fire-and-forget retain without waiting for completion (default: false) */
+ async?: boolean;
+ /** Tags always attached to every retained memory (default: undefined) */
+ tags?: string[];
+ /** Metadata always attached to every retained memory (default: undefined) */
+ metadata?: Record;
+ /** Custom tool description */
+ description?: string;
+ };
+
+ /** Options for the recall tool */
+ recall?: {
+ /** Restrict results to these fact types: 'world', 'experience', 'observation' (default: undefined = all types) */
+ types?: FactType[];
+ /** Maximum tokens to return (default: undefined = API default) */
+ maxTokens?: number;
+ /** Processing budget controlling latency vs. depth (default: 'mid') */
+ budget?: Budget;
+ /** Include entity observations in results (default: false) */
+ includeEntities?: boolean;
+ /** Include raw source chunks in results (default: false) */
+ includeChunks?: boolean;
+ /** Custom tool description */
+ description?: string;
+ };
+
+ /** Options for the reflect tool */
+ reflect?: {
+ /** Processing budget controlling latency vs. depth (default: 'mid') */
+ budget?: Budget;
+ /** Maximum tokens for the response (default: undefined = API default) */
+ maxTokens?: number;
+ /** Custom tool description */
+ description?: string;
+ };
+
+ /** Options for the getMentalModel tool */
+ getMentalModel?: {
+ /** Custom tool description */
+ description?: string;
+ };
+
+ /** Options for the getDocument tool */
+ getDocument?: {
+ /** Custom tool description */
+ description?: string;
+ };
}
/**
* Creates AI SDK tools for Hindsight memory operations.
*
- * Features:
- * - Dynamic bank ID per call (supports multi-user/multi-bank scenarios)
- * - Full API parameter support for retain, recall, and reflect
- * - Ready to use with streamText, generateText, or ToolLoopAgent
+ * The bank ID and all infrastructure concerns (budget, tags, async mode, etc.)
+ * are fixed at creation time. The agent only controls semantic inputs:
+ * content, queries, names, and timestamps.
*
* @example
* ```ts
* const tools = createHindsightTools({
* client: hindsightClient,
+ * bankId: userId,
+ * recall: { budget: 'high', includeEntities: true },
+ * retain: { async: true, tags: ['env:prod'] },
* });
*
- * // Use with AI SDK
* const result = await generateText({
- * model: openai('gpt-4'),
+ * model: openai('gpt-4o'),
* tools,
- * prompt: 'Remember that Alice loves hiking',
+ * messages,
* });
* ```
*/
export function createHindsightTools({
client,
- retainDescription,
- recallDescription,
- reflectDescription,
- createMentalModelDescription,
- queryMentalModelDescription,
- getDocumentDescription,
+ bankId,
+ retain: retainOpts = {},
+ recall: recallOpts = {},
+ reflect: reflectOpts = {},
+ getMentalModel: getMentalModelOpts = {},
+ getDocument: getDocumentOpts = {},
}: HindsightToolsOptions) {
+ // Agent-controlled params only: content, timestamp, documentId, context
const retainParams = z.object({
- bankId: z.string().describe('Memory bank ID (usually the user ID)'),
content: z.string().describe('Content to store in memory'),
documentId: z.string().optional().describe('Optional document ID for grouping/upserting content'),
timestamp: z.string().optional().describe('Optional ISO timestamp for when the memory occurred'),
context: z.string().optional().describe('Optional context about the memory'),
- tags: z.array(z.string()).optional().describe('Optional tags for visibility scoping'),
- metadata: z.record(z.string(), z.string()).optional().describe('Optional user-defined metadata'),
});
+ // Agent-controlled params only: query, queryTimestamp
const recallParams = z.object({
- bankId: z.string().describe('Memory bank ID (usually the user ID)'),
query: z.string().describe('What to search for in memory'),
- types: z.array(z.string()).optional().describe('Filter by fact types'),
- maxTokens: z.number().optional().describe('Maximum tokens to return'),
- budget: BudgetSchema.optional().describe('Processing budget: low, mid, or high'),
queryTimestamp: z.string().optional().describe('Query from a specific point in time (ISO format)'),
- includeEntities: z.boolean().optional().describe('Include entity observations in results'),
- includeChunks: z.boolean().optional().describe('Include raw chunks in results'),
});
+ // Agent-controlled params only: query, context
const reflectParams = z.object({
- bankId: z.string().describe('Memory bank ID (usually the user ID)'),
query: z.string().describe('Question to reflect on based on memories'),
context: z.string().optional().describe('Additional context for the reflection'),
- budget: BudgetSchema.optional().describe('Processing budget: low, mid, or high'),
});
- const createMentalModelParams = z.object({
- bankId: z.string().describe('Memory bank ID (usually the user ID)'),
- mentalModelId: z.string().optional().describe('Optional custom ID for the mental model (auto-generated if not provided)'),
- name: z.string().optional().describe('Optional name for the mental model'),
- sourceQuery: z.string().optional().describe('Query to define what memories to consolidate'),
- tags: z.array(z.string()).optional().describe('Optional tags for organizing mental models'),
- maxTokens: z.number().optional().describe('Maximum tokens for the mental model content'),
- autoRefresh: z.boolean().optional().describe('Auto-refresh mental model after new consolidations (default: false)'),
- });
-
- const queryMentalModelParams = z.object({
- bankId: z.string().describe('Memory bank ID (usually the user ID)'),
- mentalModelId: z.string().describe('ID of the mental model to query'),
+ const getMentalModelParams = z.object({
+ mentalModelId: z.string().describe('ID of the mental model to retrieve'),
});
const getDocumentParams = z.object({
- bankId: z.string().describe('Memory bank ID (usually the user ID)'),
documentId: z.string().describe('ID of the document to retrieve'),
});
- const createDirectiveParams = z.object({
- bankId: z.string().describe('Memory bank ID (usually the user ID)'),
- name: z.string().describe('Human-readable name for the directive'),
- content: z.string().describe('The directive text to inject into prompts'),
- priority: z.number().optional().describe('Higher priority directives are injected first (default 0)'),
- isActive: z.boolean().optional().describe('Whether this directive is active (default true)'),
- tags: z.array(z.string()).optional().describe('Tags for filtering'),
- });
-
-
type RetainInput = z.infer;
type RetainOutput = { success: boolean; itemsCount: number };
@@ -372,37 +305,31 @@ export function createHindsightTools({
type ReflectInput = z.infer;
type ReflectOutput = { text: string; basedOn?: ReflectFact[] };
- type CreateMentalModelInput = z.infer;
- type CreateMentalModelOutput = { mentalModelId: string; createdAt: string };
-
- type QueryMentalModelInput = z.infer;
- type QueryMentalModelOutput = { content: string; name?: string; updatedAt: string };
+ type GetMentalModelInput = z.infer;
+ type GetMentalModelOutput = { content: string; name?: string; updatedAt: string };
type GetDocumentInput = z.infer;
type GetDocumentOutput = { originalText: string; id: string; createdAt: string; updatedAt: string } | null;
- type CreateDirectiveInput = z.infer;
- type CreateDirectiveOutput = { id: string; name: string; content: string; tags: string[]; createdAt: string };
-
return {
retain: tool({
description:
- retainDescription ??
+ retainOpts.description ??
`Store information in long-term memory. Use this when information should be remembered for future interactions, such as user preferences, facts, experiences, or important context.`,
inputSchema: retainParams,
execute: async (input) => {
console.log('[AI SDK Tool] Retain input:', {
- bankId: input.bankId,
+ bankId,
documentId: input.documentId,
- tags: input.tags,
hasContent: !!input.content,
});
- const result = await client.retain(input.bankId, input.content, {
+ const result = await client.retain(bankId, input.content, {
documentId: input.documentId,
timestamp: input.timestamp,
context: input.context,
- tags: input.tags,
- metadata: input.metadata as Record | undefined,
+ tags: retainOpts.tags,
+ metadata: retainOpts.metadata,
+ async: retainOpts.async ?? false,
});
return { success: result.success, itemsCount: result.items_count };
},
@@ -410,17 +337,17 @@ export function createHindsightTools({
recall: tool({
description:
- recallDescription ??
+ recallOpts.description ??
`Search memory for relevant information. Use this to find previously stored information that can help personalize responses or provide context.`,
inputSchema: recallParams,
execute: async (input) => {
- const result = await client.recall(input.bankId, input.query, {
- types: input.types,
- maxTokens: input.maxTokens,
- budget: input.budget,
+ const result = await client.recall(bankId, input.query, {
+ types: recallOpts.types,
+ maxTokens: recallOpts.maxTokens,
+ budget: recallOpts.budget ?? 'mid',
queryTimestamp: input.queryTimestamp,
- includeEntities: input.includeEntities,
- includeChunks: input.includeChunks,
+ includeEntities: recallOpts.includeEntities ?? false,
+ includeChunks: recallOpts.includeChunks ?? false,
});
return {
results: result.results ?? [],
@@ -431,13 +358,14 @@ export function createHindsightTools({
reflect: tool({
description:
- reflectDescription ??
+ reflectOpts.description ??
`Analyze memories to form insights and generate contextual answers. Use this to understand patterns, synthesize information, or answer questions that require reasoning over stored memories.`,
inputSchema: reflectParams,
execute: async (input) => {
- const result = await client.reflect(input.bankId, input.query, {
+ const result = await client.reflect(bankId, input.query, {
context: input.context,
- budget: input.budget,
+ budget: reflectOpts.budget ?? 'mid',
+ maxTokens: reflectOpts.maxTokens,
});
return {
text: result.text ?? 'No insights available yet.',
@@ -446,34 +374,13 @@ export function createHindsightTools({
},
}),
- createMentalModel: tool({
+ getMentalModel: tool({
description:
- createMentalModelDescription ??
- `Create a mental model that automatically consolidates memories into structured knowledge. Mental models are continuously updated as new memories are added, making them ideal for maintaining up-to-date user preferences, behavioral patterns, and accumulated wisdom.`,
- inputSchema: createMentalModelParams,
+ getMentalModelOpts.description ??
+ `Retrieve a mental model to get consolidated knowledge synthesized from memories. Mental models provide synthesized insights that are faster and more efficient to retrieve than searching through raw memories.`,
+ inputSchema: getMentalModelParams,
execute: async (input) => {
- const result = await client.createMentalModel(input.bankId, {
- id: input.mentalModelId,
- name: input.name,
- sourceQuery: input.sourceQuery,
- tags: input.tags,
- maxTokens: input.maxTokens,
- trigger: input.autoRefresh !== undefined ? { refresh_after_consolidation: input.autoRefresh } : undefined,
- });
- return {
- mentalModelId: result.mental_model_id,
- createdAt: result.created_at,
- };
- },
- }),
-
- queryMentalModel: tool({
- description:
- queryMentalModelDescription ??
- `Query an existing mental model to retrieve consolidated knowledge. Mental models provide synthesized insights from memories, making them faster and more efficient than searching through raw memories.`,
- inputSchema: queryMentalModelParams,
- execute: async (input) => {
- const result = await client.getMentalModel(input.bankId, input.mentalModelId);
+ const result = await client.getMentalModel(bankId, input.mentalModelId);
return {
content: result.content ?? 'No content available yet.',
name: result.name,
@@ -484,11 +391,11 @@ export function createHindsightTools({
getDocument: tool({
description:
- getDocumentDescription ??
+ getDocumentOpts.description ??
`Retrieve a stored document by its ID. Documents are used to store structured data like application state, user profiles, or any data that needs exact retrieval.`,
inputSchema: getDocumentParams,
execute: async (input) => {
- const result = await client.getDocument(input.bankId, input.documentId);
+ const result = await client.getDocument(bankId, input.documentId);
if (!result) {
return null;
}
@@ -501,27 +408,6 @@ export function createHindsightTools({
},
}),
- createDirective: tool({
- description:
- `Create a directive - a hard rule that is injected into prompts during reflect operations. Directives are explicit instructions that guide agent behavior. Use tags to control when directives are applied (e.g., user-specific directives with 'user:username' tags).`,
- inputSchema: createDirectiveParams,
- execute: async (input) => {
- const result = await client.createDirective(input.bankId, {
- name: input.name,
- content: input.content,
- priority: input.priority,
- isActive: input.isActive,
- tags: input.tags,
- });
- return {
- id: result.id,
- name: result.name,
- content: result.content,
- tags: result.tags,
- createdAt: result.created_at,
- };
- },
- }),
};
}