@siftd/connect-agent 0.1.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,106 @@
1
+ # @siftd/connect-agent
2
+
3
+ Master orchestrator agent that connects to [Connect](https://connect.siftd.app) - control Claude Code remotely from any browser.
4
+
5
+ ## What is this?
6
+
7
+ Connect Agent runs on your machine and bridges the Connect web app to Claude Code CLI. It's not just a relay - it's a **master orchestrator** with:
8
+
9
+ - **Persistent Memory** - Remembers your preferences, projects, and context across sessions
10
+ - **Worker Delegation** - Delegates complex tasks to Claude Code CLI workers
11
+ - **Task Scheduling** - Schedule tasks for future execution
12
+ - **Semantic Search** - Finds relevant memories by meaning, not just keywords
13
+
14
+ ## Quick Start
15
+
16
+ ```bash
17
+ # 1. Go to https://connect.siftd.app and get a pairing code
18
+
19
+ # 2. Pair your machine (with API key for full orchestrator mode)
20
+ npx @siftd/connect-agent pair <CODE> --api-key <YOUR_ANTHROPIC_API_KEY>
21
+
22
+ # 3. Start the agent
23
+ npx @siftd/connect-agent start
24
+ ```
25
+
26
+ ## Modes
27
+
28
+ ### Orchestrator Mode (Recommended)
29
+ When you provide an Anthropic API key, the agent runs as a **master orchestrator**:
30
+ - Uses Claude API directly for the orchestration layer
31
+ - Maintains persistent memory about you and your projects
32
+ - Delegates file/code work to Claude Code CLI workers
33
+ - Schedules future tasks
34
+
35
+ ### Simple Relay Mode
36
+ Without an API key, the agent acts as a simple relay to `claude -p --continue`.
37
+
38
+ ## Commands
39
+
40
+ ```bash
41
+ # Pair with the web app
42
+ connect-agent pair <CODE> [--api-key <KEY>]
43
+
44
+ # Start processing messages
45
+ connect-agent start [--interval <ms>]
46
+
47
+ # Check status
48
+ connect-agent status
49
+
50
+ # Set API key (without re-pairing)
51
+ connect-agent set-key <KEY>
52
+
53
+ # Clear configuration
54
+ connect-agent logout
55
+ ```
56
+
57
+ ## Special Commands (via web chat)
58
+
59
+ - `/reset` or `/clear` - Clear conversation history
60
+ - `/mode` - Check current mode (orchestrator vs simple)
61
+ - `/memory` - Show memory statistics
62
+
63
+ ## How It Works
64
+
65
+ ```
66
+ ┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
67
+ │ Browser │────▶│ Connect Server │◀────│ Connect Agent │
68
+ │ (any device) │ │ (connect.siftd) │ │ (your machine) │
69
+ └─────────────────┘ └──────────────────┘ └────────┬────────┘
70
+
71
+
72
+ ┌─────────────────┐
73
+ │ Master │
74
+ │ Orchestrator │
75
+ │ (memory, sched) │
76
+ └────────┬────────┘
77
+ │ delegates
78
+
79
+ ┌─────────────────┐
80
+ │ Claude Code CLI │
81
+ │ (file ops, code)│
82
+ └─────────────────┘
83
+ ```
84
+
85
+ The orchestrator understands it's the **master**, not a worker. It:
86
+ 1. Receives your message from the web
87
+ 2. Searches memory for relevant context
88
+ 3. Decides what needs to be done
89
+ 4. Delegates actual work to Claude Code CLI
90
+ 5. Synthesizes results into a clear response
91
+ 6. Remembers important things for next time
92
+
93
+ ## Environment Variables
94
+
95
+ - `ANTHROPIC_API_KEY` - Alternative to `--api-key` flag
96
+ - `VOYAGE_API_KEY` - (Optional) For better semantic search embeddings
97
+
98
+ ## Requirements
99
+
100
+ - Node.js 18+
101
+ - [Claude Code CLI](https://claude.ai/code) installed and authenticated
102
+ - Anthropic API key (for orchestrator mode)
103
+
104
+ ## License
105
+
106
+ MIT
package/dist/agent.js CHANGED
@@ -1,17 +1,47 @@
1
1
  import { spawn } from 'child_process';
2
2
  import { pollMessages, sendResponse } from './api.js';
3
+ import { getUserId, getAnthropicApiKey, isCloudMode, getDeploymentInfo } from './config.js';
4
+ import { MasterOrchestrator } from './orchestrator.js';
5
+ import { AgentWebSocket } from './websocket.js';
6
+ import { startHeartbeat, stopHeartbeat, getHeartbeatState } from './heartbeat.js';
3
7
  // Strip ANSI escape codes for clean output
4
8
  function stripAnsi(str) {
5
9
  return str.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, '');
6
10
  }
11
+ // Conversation history for orchestrator mode
12
+ let conversationHistory = [];
13
+ let orchestrator = null;
14
+ let wsClient = null;
7
15
  /**
8
- * Send a message to Claude Code and get the response
9
- * Uses --continue to maintain conversation context
16
+ * Initialize the orchestrator if API key is available
10
17
  */
11
- async function sendToClaude(input) {
12
- return new Promise((resolve, reject) => {
18
+ function initOrchestrator() {
19
+ // Check env first, then stored config
20
+ const apiKey = process.env.ANTHROPIC_API_KEY || getAnthropicApiKey();
21
+ const userId = getUserId();
22
+ if (!apiKey) {
23
+ console.log('[AGENT] No API key - using simple relay mode');
24
+ console.log('[AGENT] To enable orchestrator: pair with --api-key flag');
25
+ return null;
26
+ }
27
+ if (!userId) {
28
+ console.log('[AGENT] No userId configured - using simple relay mode');
29
+ return null;
30
+ }
31
+ console.log('[AGENT] Initializing Master Orchestrator...');
32
+ return new MasterOrchestrator({
33
+ apiKey,
34
+ userId,
35
+ workspaceDir: process.env.HOME || '/tmp',
36
+ voyageApiKey: process.env.VOYAGE_API_KEY
37
+ });
38
+ }
39
+ /**
40
+ * Simple relay mode - just pipes to Claude Code CLI
41
+ */
42
+ async function sendToClaudeSimple(input) {
43
+ return new Promise((resolve) => {
13
44
  console.log(`\n[CLAUDE] Sending: ${input.substring(0, 80)}...`);
14
- // Use -p (print mode) with --continue to maintain context
15
45
  const claude = spawn('claude', ['-p', '--continue'], {
16
46
  cwd: process.env.HOME,
17
47
  env: process.env,
@@ -27,7 +57,6 @@ async function sendToClaude(input) {
27
57
  claude.stderr?.on('data', (data) => {
28
58
  const text = data.toString();
29
59
  errorOutput += text;
30
- // Only print stderr if it's not just status info
31
60
  if (!text.includes('Checking') && !text.includes('Connected')) {
32
61
  process.stderr.write(text);
33
62
  }
@@ -48,43 +77,193 @@ async function sendToClaude(input) {
48
77
  console.error(`\n[CLAUDE] Failed to start: ${err.message}`);
49
78
  resolve(`Error: Failed to start Claude - ${err.message}`);
50
79
  });
51
- // Send the input to claude's stdin
52
80
  if (claude.stdin) {
53
81
  claude.stdin.write(input);
54
82
  claude.stdin.end();
55
83
  }
56
84
  });
57
85
  }
86
+ /**
87
+ * Orchestrator mode - uses the master orchestrator with memory
88
+ * Supports WebSocket streaming for real-time progress updates
89
+ * @param apiKey Optional per-request API key from user
90
+ */
91
+ async function sendToOrchestrator(input, orch, messageId, apiKey) {
92
+ console.log(`\n[ORCHESTRATOR] Processing: ${input.substring(0, 80)}...`);
93
+ // Send typing indicator via WebSocket
94
+ if (wsClient?.connected()) {
95
+ wsClient.sendTyping(true);
96
+ }
97
+ try {
98
+ const response = await orch.processMessage(input, conversationHistory, async (msg) => {
99
+ // Progress callback - log to console and send via WebSocket
100
+ console.log(`[ORCHESTRATOR] ${msg}`);
101
+ if (wsClient?.connected() && messageId) {
102
+ wsClient.sendProgress(messageId, msg);
103
+ }
104
+ }, apiKey // Pass per-request API key
105
+ );
106
+ // Update conversation history (only if response is non-empty)
107
+ if (response && response.trim()) {
108
+ conversationHistory.push({ role: 'user', content: input });
109
+ conversationHistory.push({ role: 'assistant', content: response });
110
+ // Keep history manageable (last 20 exchanges)
111
+ if (conversationHistory.length > 40) {
112
+ conversationHistory = conversationHistory.slice(-40);
113
+ }
114
+ }
115
+ else {
116
+ console.log('[ORCHESTRATOR] Empty response - not adding to conversation history');
117
+ }
118
+ console.log(`\n[ORCHESTRATOR] Response: ${response.substring(0, 80)}...`);
119
+ // Stop typing indicator
120
+ if (wsClient?.connected()) {
121
+ wsClient.sendTyping(false);
122
+ }
123
+ return response;
124
+ }
125
+ catch (error) {
126
+ console.error('[ORCHESTRATOR] Error:', error);
127
+ if (wsClient?.connected()) {
128
+ wsClient.sendTyping(false);
129
+ }
130
+ return `Error: ${error instanceof Error ? error.message : 'Unknown error'}`;
131
+ }
132
+ }
58
133
  export async function processMessage(message) {
59
134
  console.log(`\n[WEB] >>> ${message.content}`);
60
- try {
61
- const response = await sendToClaude(message.content);
135
+ // Handle special commands
136
+ const content = message.content.trim().toLowerCase();
137
+ if (content === '/reset' || content === '/clear') {
138
+ conversationHistory = [];
139
+ return 'Conversation history cleared.';
140
+ }
141
+ if (content === '/mode') {
142
+ const wsStatus = wsClient?.connected() ? 'WebSocket' : 'Polling';
143
+ const hbState = getHeartbeatState();
144
+ const runnerInfo = hbState.runnerId ? ` [${hbState.runnerId}]` : '';
145
+ return orchestrator
146
+ ? `Running in ORCHESTRATOR mode (${wsStatus})${runnerInfo} with memory and delegation`
147
+ : `Running in SIMPLE mode (${wsStatus})${runnerInfo} - direct Claude Code relay`;
148
+ }
149
+ if (content === '/memory' && orchestrator) {
150
+ // Trigger memory stats
151
+ const response = await orchestrator.processMessage('Show me my memory stats', [], undefined, message.apiKey // Use per-message API key if provided
152
+ );
62
153
  return response;
63
154
  }
155
+ try {
156
+ if (orchestrator) {
157
+ return await sendToOrchestrator(message.content, orchestrator, message.id, message.apiKey);
158
+ }
159
+ else {
160
+ return await sendToClaudeSimple(message.content);
161
+ }
162
+ }
64
163
  catch (error) {
65
164
  console.error('Error:', error);
66
165
  return `Error: ${error instanceof Error ? error.message : 'Unknown error'}`;
67
166
  }
68
167
  }
69
168
  export async function runAgent(pollInterval = 2000) {
70
- console.log('╔══════════════════════════════════════════╗');
71
- console.log('║ Connect Agent - Claude Code Relay ║');
72
- console.log('╚══════════════════════════════════════════╝\n');
73
- console.log('[AGENT] Polling for web messages...\n');
74
- while (true) {
75
- try {
76
- const { messages } = await pollMessages();
77
- for (const msg of messages) {
78
- const response = await processMessage(msg);
79
- await sendResponse(msg.id, response);
80
- console.log(`[AGENT] Response sent (${response.length} chars)`);
169
+ // Initialize orchestrator
170
+ orchestrator = initOrchestrator();
171
+ const mode = orchestrator ? 'ORCHESTRATOR' : 'SIMPLE RELAY';
172
+ const deployment = getDeploymentInfo();
173
+ console.log('╔══════════════════════════════════════════════════╗');
174
+ console.log('║ Connect Agent - Master Orchestrator ║');
175
+ console.log(`║ Mode: ${mode.padEnd(41)}║`);
176
+ console.log(`║ Deployment: ${deployment.mode.toUpperCase().padEnd(35)}║`);
177
+ console.log('╚══════════════════════════════════════════════════╝\n');
178
+ if (isCloudMode()) {
179
+ console.log(`[AGENT] Running in CLOUD mode`);
180
+ console.log(`[AGENT] Server: ${deployment.server}`);
181
+ }
182
+ if (orchestrator) {
183
+ console.log('[AGENT] Features: Memory, Scheduling, Worker Delegation');
184
+ // Initialize orchestrator (indexes filesystem into memory)
185
+ await orchestrator.initialize();
186
+ }
187
+ // Start heartbeat to maintain presence in runner registry
188
+ const runnerType = isCloudMode() ? 'vm' : 'local';
189
+ startHeartbeat({
190
+ runnerType,
191
+ capabilities: orchestrator ? ['orchestrator', 'memory'] : [],
192
+ onError: (error) => {
193
+ // Log only significant errors
194
+ if (error.message.includes('401') || error.message.includes('403')) {
195
+ console.error('[AGENT] Heartbeat auth failed - may need to re-pair');
81
196
  }
197
+ },
198
+ });
199
+ // Graceful shutdown
200
+ process.on('SIGINT', () => {
201
+ console.log('\n[AGENT] Shutting down...');
202
+ stopHeartbeat();
203
+ if (wsClient) {
204
+ wsClient.close();
205
+ }
206
+ if (orchestrator) {
207
+ orchestrator.shutdown();
82
208
  }
83
- catch (error) {
84
- if (error instanceof Error && !error.message.includes('ECONNREFUSED')) {
209
+ process.exit(0);
210
+ });
211
+ // Try WebSocket first
212
+ wsClient = new AgentWebSocket();
213
+ const wsConnected = await wsClient.connect();
214
+ if (wsConnected) {
215
+ console.log('[AGENT] Using WebSocket for real-time communication\n');
216
+ // Handle messages via WebSocket
217
+ wsClient.onMessage(async (wsMsg) => {
218
+ if (wsMsg.type === 'message' && wsMsg.content && wsMsg.id) {
219
+ const message = {
220
+ id: wsMsg.id,
221
+ content: wsMsg.content,
222
+ timestamp: wsMsg.timestamp || Date.now(),
223
+ apiKey: wsMsg.apiKey // Pass through per-request API key
224
+ };
225
+ const response = await processMessage(message);
226
+ // Send response via WebSocket
227
+ wsClient.sendResponse(wsMsg.id, response);
228
+ console.log(`[AGENT] Response sent via WebSocket (${response.length} chars)`);
229
+ }
230
+ });
231
+ // Keep process alive - WebSocket handles messages
232
+ console.log('[AGENT] Listening for WebSocket messages...\n');
233
+ // Always poll for messages (socket-store.ts stores messages that WebSocket doesn't see)
234
+ while (true) {
235
+ await new Promise(resolve => setTimeout(resolve, pollInterval));
236
+ try {
237
+ const { messages } = await pollMessages();
238
+ for (const msg of messages) {
239
+ const response = await processMessage(msg);
240
+ await sendResponse(msg.id, response);
241
+ console.log(`[AGENT] Response sent (${response.length} chars)`);
242
+ }
243
+ }
244
+ catch (error) {
85
245
  console.error('[AGENT] Poll error:', error.message);
86
246
  }
87
247
  }
88
- await new Promise(resolve => setTimeout(resolve, pollInterval));
248
+ }
249
+ else {
250
+ // Fall back to polling
251
+ console.log('[AGENT] WebSocket unavailable, using HTTP polling\n');
252
+ while (true) {
253
+ try {
254
+ const { messages } = await pollMessages();
255
+ for (const msg of messages) {
256
+ const response = await processMessage(msg);
257
+ await sendResponse(msg.id, response);
258
+ console.log(`[AGENT] Response sent (${response.length} chars)`);
259
+ }
260
+ }
261
+ catch (error) {
262
+ if (error instanceof Error && !error.message.includes('ECONNREFUSED')) {
263
+ console.error('[AGENT] Poll error:', error.message);
264
+ }
265
+ }
266
+ await new Promise(resolve => setTimeout(resolve, pollInterval));
267
+ }
89
268
  }
90
269
  }
package/dist/api.d.ts CHANGED
@@ -2,6 +2,7 @@ export interface Message {
2
2
  id: string;
3
3
  content: string;
4
4
  timestamp: number;
5
+ apiKey?: string;
5
6
  }
6
7
  export interface ConnectResponse {
7
8
  success: boolean;
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { program } from 'commander';
3
3
  import ora from 'ora';
4
- import { getServerUrl, setServerUrl, setAgentToken, setUserId, isConfigured, clearConfig, getConfigPath, } from './config.js';
4
+ import { getServerUrl, setServerUrl, setAgentToken, setUserId, setAnthropicApiKey, getAnthropicApiKey, isConfigured, clearConfig, getConfigPath, } from './config.js';
5
5
  import { connectWithPairingCode, checkConnection } from './api.js';
6
6
  import { runAgent } from './agent.js';
7
7
  program
@@ -13,6 +13,7 @@ program
13
13
  .description('Pair with Connect web app using a pairing code')
14
14
  .argument('<code>', 'The 6-character pairing code from the web app')
15
15
  .option('-s, --server <url>', 'Server URL', getServerUrl())
16
+ .option('-k, --api-key <key>', 'Anthropic API key for orchestrator mode')
16
17
  .action(async (code, options) => {
17
18
  const spinner = ora('Connecting to server...').start();
18
19
  try {
@@ -26,9 +27,18 @@ program
26
27
  }
27
28
  setAgentToken(result.agentToken);
28
29
  setUserId(result.userId);
30
+ // Store API key if provided
31
+ if (options.apiKey) {
32
+ setAnthropicApiKey(options.apiKey);
33
+ }
29
34
  spinner.succeed('Paired successfully!');
30
35
  console.log(`\nAgent is now connected to ${getServerUrl()}`);
31
- console.log('Run "connect-agent start" to begin processing messages.');
36
+ if (options.apiKey) {
37
+ console.log('Orchestrator mode enabled (API key saved)');
38
+ }
39
+ console.log('\nTo start the agent, run one of:');
40
+ console.log(' npx @siftd/connect-agent start');
41
+ console.log(' (or install globally: npm install -g @siftd/connect-agent)');
32
42
  }
33
43
  catch (error) {
34
44
  spinner.fail(`Connection failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
@@ -64,6 +74,7 @@ program
64
74
  console.log(`Config file: ${getConfigPath()}`);
65
75
  console.log(`Server URL: ${getServerUrl()}`);
66
76
  console.log(`Configured: ${isConfigured() ? 'Yes' : 'No'}`);
77
+ console.log(`Orchestrator mode: ${getAnthropicApiKey() ? 'Yes (API key saved)' : 'No (simple relay)'}`);
67
78
  if (isConfigured()) {
68
79
  const spinner = ora('Checking connection...').start();
69
80
  const connected = await checkConnection();
@@ -90,4 +101,12 @@ program
90
101
  setServerUrl(url);
91
102
  console.log(`Server URL set to: ${url}`);
92
103
  });
104
+ program
105
+ .command('set-key')
106
+ .description('Set Anthropic API key for orchestrator mode')
107
+ .argument('<key>', 'Your Anthropic API key')
108
+ .action((key) => {
109
+ setAnthropicApiKey(key);
110
+ console.log('API key saved. Orchestrator mode will be enabled on next start.');
111
+ });
93
112
  program.parse();
package/dist/config.d.ts CHANGED
@@ -1,9 +1,23 @@
1
+ /**
2
+ * Check if running in cloud mode (environment variables take precedence)
3
+ */
4
+ export declare function isCloudMode(): boolean;
1
5
  export declare function getServerUrl(): string;
2
6
  export declare function setServerUrl(url: string): void;
3
7
  export declare function getAgentToken(): string | null;
4
8
  export declare function setAgentToken(token: string | null): void;
5
9
  export declare function getUserId(): string | null;
6
10
  export declare function setUserId(id: string | null): void;
11
+ export declare function getAnthropicApiKey(): string | null;
12
+ export declare function setAnthropicApiKey(key: string | null): void;
7
13
  export declare function isConfigured(): boolean;
8
14
  export declare function clearConfig(): void;
9
15
  export declare function getConfigPath(): string;
16
+ /**
17
+ * Get deployment mode info for logging
18
+ */
19
+ export declare function getDeploymentInfo(): {
20
+ mode: string;
21
+ server: string;
22
+ userId: string | null;
23
+ };
package/dist/config.js CHANGED
@@ -5,14 +5,22 @@ const config = new Conf({
5
5
  serverUrl: 'https://connect.siftd.app',
6
6
  },
7
7
  });
8
+ /**
9
+ * Check if running in cloud mode (environment variables take precedence)
10
+ */
11
+ export function isCloudMode() {
12
+ return !!(process.env.CONNECT_SERVER_URL && process.env.CONNECT_AGENT_TOKEN && process.env.CONNECT_USER_ID);
13
+ }
8
14
  export function getServerUrl() {
9
- return config.get('serverUrl');
15
+ // Environment variable takes precedence (cloud mode)
16
+ return process.env.CONNECT_SERVER_URL || config.get('serverUrl');
10
17
  }
11
18
  export function setServerUrl(url) {
12
19
  config.set('serverUrl', url);
13
20
  }
14
21
  export function getAgentToken() {
15
- return config.get('agentToken') ?? null;
22
+ // Environment variable takes precedence (cloud mode)
23
+ return process.env.CONNECT_AGENT_TOKEN || config.get('agentToken') || null;
16
24
  }
17
25
  export function setAgentToken(token) {
18
26
  if (token === null) {
@@ -23,7 +31,8 @@ export function setAgentToken(token) {
23
31
  }
24
32
  }
25
33
  export function getUserId() {
26
- return config.get('userId') ?? null;
34
+ // Environment variable takes precedence (cloud mode)
35
+ return process.env.CONNECT_USER_ID || config.get('userId') || null;
27
36
  }
28
37
  export function setUserId(id) {
29
38
  if (id === null) {
@@ -33,13 +42,37 @@ export function setUserId(id) {
33
42
  config.set('userId', id);
34
43
  }
35
44
  }
45
+ export function getAnthropicApiKey() {
46
+ // Environment variable takes precedence (cloud mode)
47
+ // Note: ANTHROPIC_API_KEY is also checked in orchestrator.ts
48
+ return process.env.ANTHROPIC_API_KEY || config.get('anthropicApiKey') || null;
49
+ }
50
+ export function setAnthropicApiKey(key) {
51
+ if (key === null) {
52
+ config.delete('anthropicApiKey');
53
+ }
54
+ else {
55
+ config.set('anthropicApiKey', key);
56
+ }
57
+ }
36
58
  export function isConfigured() {
37
59
  return !!getAgentToken() && !!getUserId();
38
60
  }
39
61
  export function clearConfig() {
40
62
  config.delete('agentToken');
41
63
  config.delete('userId');
64
+ config.delete('anthropicApiKey');
42
65
  }
43
66
  export function getConfigPath() {
44
67
  return config.path;
45
68
  }
69
+ /**
70
+ * Get deployment mode info for logging
71
+ */
72
+ export function getDeploymentInfo() {
73
+ return {
74
+ mode: isCloudMode() ? 'cloud' : 'local',
75
+ server: getServerUrl(),
76
+ userId: getUserId(),
77
+ };
78
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Advanced Memory System
3
+ *
4
+ * Features:
5
+ * - Hierarchical memory types (episodic, semantic, procedural)
6
+ * - Vector embeddings for semantic search
7
+ * - Importance scoring with temporal decay
8
+ * - Memory associations/links
9
+ * - Reflection and consolidation
10
+ */
11
+ export type MemoryType = 'episodic' | 'semantic' | 'procedural' | 'working';
12
+ export interface Memory {
13
+ id: string;
14
+ type: MemoryType;
15
+ content: string;
16
+ summary?: string;
17
+ source: string;
18
+ timestamp: string;
19
+ lastAccessed: string;
20
+ importance: number;
21
+ accessCount: number;
22
+ decayRate: number;
23
+ associations: string[];
24
+ tags: string[];
25
+ hasEmbedding: boolean;
26
+ }
27
+ export declare class AdvancedMemoryStore {
28
+ private db;
29
+ private vectorIndex;
30
+ private vectorPath;
31
+ private embedder;
32
+ private config;
33
+ constructor(options?: {
34
+ dbPath?: string;
35
+ vectorPath?: string;
36
+ voyageApiKey?: string;
37
+ maxMemories?: number;
38
+ });
39
+ private init;
40
+ private initVectorIndex;
41
+ remember(content: string, options?: {
42
+ type?: MemoryType;
43
+ source?: string;
44
+ importance?: number;
45
+ tags?: string[];
46
+ associations?: string[];
47
+ summary?: string;
48
+ }): Promise<string>;
49
+ search(query: string, options?: {
50
+ limit?: number;
51
+ type?: MemoryType;
52
+ minImportance?: number;
53
+ includeAssociations?: boolean;
54
+ }): Promise<Memory[]>;
55
+ private textSearch;
56
+ getById(id: string): Memory | null;
57
+ getByType(type: MemoryType, limit?: number): Memory[];
58
+ recall(id: string): Memory | null;
59
+ forget(id: string): Promise<boolean>;
60
+ updateImportance(id: string, importance: number): void;
61
+ associate(id1: string, id2: string): void;
62
+ applyDecay(): number;
63
+ reflect(): Promise<{
64
+ insights: string[];
65
+ consolidations: number;
66
+ memoriesProcessed: number;
67
+ }>;
68
+ stats(): {
69
+ total: number;
70
+ byType: Record<MemoryType, number>;
71
+ avgImportance: number;
72
+ totalAssociations: number;
73
+ oldestMemory: string | null;
74
+ newestMemory: string | null;
75
+ };
76
+ list(limit?: number): Memory[];
77
+ getRecent(limit?: number): Memory[];
78
+ private addEmbedding;
79
+ private createAssociations;
80
+ private removeAssociations;
81
+ private recordAccess;
82
+ private inferMemoryType;
83
+ private calculateInitialImportance;
84
+ private getDecayRateForType;
85
+ private extractTags;
86
+ private consolidateMemories;
87
+ private findCommonWords;
88
+ private pruneIfNeeded;
89
+ private rowToMemory;
90
+ close(): void;
91
+ }