@siftd/connect-agent 0.2.12 → 0.2.14

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/dist/agent.js CHANGED
@@ -197,14 +197,14 @@ export async function runAgent(pollInterval = 2000) {
197
197
  },
198
198
  });
199
199
  // Graceful shutdown
200
- process.on('SIGINT', () => {
200
+ process.on('SIGINT', async () => {
201
201
  console.log('\n[AGENT] Shutting down...');
202
202
  stopHeartbeat();
203
203
  if (wsClient) {
204
204
  wsClient.close();
205
205
  }
206
206
  if (orchestrator) {
207
- orchestrator.shutdown();
207
+ await orchestrator.shutdown();
208
208
  }
209
209
  process.exit(0);
210
210
  });
@@ -7,12 +7,30 @@
7
7
  *
8
8
  * No MCP. No new protocols. Just smart memory use.
9
9
  */
10
- import { AdvancedMemoryStore } from './memory-advanced.js';
10
+ interface MemoryStore {
11
+ remember(content: string, options?: {
12
+ type?: 'episodic' | 'semantic' | 'procedural' | 'working';
13
+ source?: string;
14
+ importance?: number;
15
+ tags?: string[];
16
+ }): Promise<string>;
17
+ search(query: string, options?: {
18
+ type?: 'episodic' | 'semantic' | 'procedural' | 'working';
19
+ limit?: number;
20
+ minImportance?: number;
21
+ }): Promise<{
22
+ id: string;
23
+ content: string;
24
+ importance: number;
25
+ tags?: string[];
26
+ }[]>;
27
+ forget?(id: string): Promise<boolean>;
28
+ }
11
29
  export declare class SystemIndexer {
12
30
  private memory;
13
31
  private home;
14
32
  private indexed;
15
- constructor(memory: AdvancedMemoryStore);
33
+ constructor(memory: MemoryStore);
16
34
  /**
17
35
  * Index the user's home directory structure into memory
18
36
  */
@@ -63,3 +81,4 @@ export declare class SystemIndexer {
63
81
  total: number;
64
82
  }>;
65
83
  }
84
+ export {};
@@ -345,7 +345,7 @@ export class SystemIndexer {
345
345
  type: 'semantic'
346
346
  });
347
347
  for (const mem of oldMemories) {
348
- if (mem.tags.includes('filesystem')) {
348
+ if (mem.tags?.includes('filesystem') && this.memory.forget) {
349
349
  await this.memory.forget(mem.id);
350
350
  }
351
351
  }
package/dist/heartbeat.js CHANGED
@@ -10,7 +10,7 @@ import { hostname } from 'os';
10
10
  import { createHash } from 'crypto';
11
11
  import { getServerUrl, getAgentToken, getUserId, isCloudMode } from './config.js';
12
12
  const HEARTBEAT_INTERVAL = 10000; // 10 seconds
13
- const VERSION = '0.2.12'; // Should match package.json
13
+ const VERSION = '0.2.14'; // Should match package.json
14
14
  const state = {
15
15
  intervalId: null,
16
16
  runnerId: null,
@@ -11,7 +11,6 @@ export declare class MasterOrchestrator {
11
11
  private model;
12
12
  private maxTokens;
13
13
  private memory;
14
- private postgresMemory;
15
14
  private scheduler;
16
15
  private indexer;
17
16
  private jobs;
@@ -20,13 +19,10 @@ export declare class MasterOrchestrator {
20
19
  private workspaceDir;
21
20
  private claudePath;
22
21
  private initialized;
23
- private usingPostgres;
24
- private voyageApiKey?;
25
22
  private bashTool;
26
23
  private webTools;
27
24
  private workerTools;
28
25
  private sharedState;
29
- private fileWatcher;
30
26
  private verboseMode;
31
27
  constructor(options: {
32
28
  apiKey: string;
@@ -123,26 +119,6 @@ export declare class MasterOrchestrator {
123
119
  * Stop a running server on a port
124
120
  */
125
121
  private executeStopServer;
126
- /**
127
- * Add a file watch rule
128
- */
129
- private executeAddWatchRule;
130
- /**
131
- * Remove a file watch rule
132
- */
133
- private executeRemoveWatchRule;
134
- /**
135
- * List all file watch rules
136
- */
137
- private executeListWatchRules;
138
- /**
139
- * Toggle a file watch rule
140
- */
141
- private executeToggleWatchRule;
142
- /**
143
- * Get file watcher status
144
- */
145
- private executeWatchStatus;
146
122
  /**
147
123
  * Format tool preview for user
148
124
  */
@@ -11,7 +11,6 @@ import { AdvancedMemoryStore } from './core/memory-advanced.js';
11
11
  import { PostgresMemoryStore, isPostgresConfigured } from './core/memory-postgres.js';
12
12
  import { TaskScheduler } from './core/scheduler.js';
13
13
  import { SystemIndexer } from './core/system-indexer.js';
14
- import { FileWatcher } from './core/file-watcher.js';
15
14
  import { BashTool } from './tools/bash.js';
16
15
  import { WebTools } from './tools/web.js';
17
16
  import { WorkerTools } from './tools/worker.js';
@@ -76,7 +75,6 @@ export class MasterOrchestrator {
76
75
  model;
77
76
  maxTokens;
78
77
  memory;
79
- postgresMemory = null;
80
78
  scheduler;
81
79
  indexer;
82
80
  jobs = new Map();
@@ -85,14 +83,11 @@ export class MasterOrchestrator {
85
83
  workspaceDir;
86
84
  claudePath;
87
85
  initialized = false;
88
- usingPostgres = false;
89
- voyageApiKey;
90
86
  // New tools from whatsapp-claude
91
87
  bashTool;
92
88
  webTools;
93
89
  workerTools;
94
90
  sharedState;
95
- fileWatcher;
96
91
  verboseMode = new Map(); // per-user verbose mode
97
92
  constructor(options) {
98
93
  this.client = new Anthropic({ apiKey: options.apiKey });
@@ -100,33 +95,32 @@ export class MasterOrchestrator {
100
95
  this.maxTokens = options.maxTokens || 4096;
101
96
  this.userId = options.userId;
102
97
  this.workspaceDir = options.workspaceDir || process.env.HOME || '/tmp';
103
- this.voyageApiKey = options.voyageApiKey;
104
98
  // Find claude binary - critical for worker delegation
105
99
  this.claudePath = this.findClaudeBinary();
106
100
  console.log(`[ORCHESTRATOR] Claude binary: ${this.claudePath}`);
107
- // Memory initialization is deferred to initialize() for async Postgres support
108
- // For now, create SQLite as fallback (will be replaced if Postgres is configured)
109
- this.memory = new AdvancedMemoryStore({
110
- dbPath: `memories_${options.userId}.db`,
111
- vectorPath: `./memory_vectors_${options.userId}`,
112
- voyageApiKey: options.voyageApiKey
113
- });
101
+ // Initialize memory - use Postgres if DATABASE_URL is set, otherwise SQLite
102
+ if (isPostgresConfigured()) {
103
+ console.log('[ORCHESTRATOR] DATABASE_URL detected, using PostgreSQL + pgvector');
104
+ this.memory = new PostgresMemoryStore({
105
+ connectionString: process.env.DATABASE_URL,
106
+ userId: options.userId
107
+ });
108
+ }
109
+ else {
110
+ this.memory = new AdvancedMemoryStore({
111
+ dbPath: `memories_${options.userId}.db`,
112
+ vectorPath: `./memory_vectors_${options.userId}`,
113
+ voyageApiKey: options.voyageApiKey
114
+ });
115
+ }
114
116
  this.scheduler = new TaskScheduler(`scheduled_${options.userId}.json`);
115
- // Create indexer (will be re-initialized after memory setup)
117
+ // Create indexer (will index on first initialize call)
116
118
  this.indexer = new SystemIndexer(this.memory);
117
119
  // Initialize new tools
118
120
  this.bashTool = new BashTool(this.workspaceDir);
119
121
  this.webTools = new WebTools();
120
122
  this.workerTools = new WorkerTools(this.workspaceDir);
121
123
  this.sharedState = new SharedState(this.workspaceDir);
122
- this.fileWatcher = new FileWatcher(this.workspaceDir);
123
- // Set up file watcher trigger handler
124
- this.fileWatcher.on('trigger', async ({ rule, event, action }) => {
125
- console.log(`[WATCHER] Triggered: ${rule.pattern} (${event.type}: ${event.path})`);
126
- // Auto-delegate to worker when file changes
127
- const task = `${action}\n\nTriggered by file change:\n- File: ${event.path}\n- Event: ${event.type}\n- Rule: ${rule.pattern}`;
128
- this.delegateToWorker(task, undefined, this.workspaceDir);
129
- });
130
124
  }
131
125
  /**
132
126
  * Initialize the orchestrator - indexes filesystem on first call
@@ -135,39 +129,14 @@ export class MasterOrchestrator {
135
129
  if (this.initialized)
136
130
  return;
137
131
  console.log('[ORCHESTRATOR] Initializing...');
138
- // Check for Postgres and switch memory backend if configured
139
- if (isPostgresConfigured()) {
140
- try {
141
- console.log('[ORCHESTRATOR] DATABASE_URL detected, using PostgreSQL + pgvector');
142
- this.postgresMemory = new PostgresMemoryStore({
143
- connectionString: process.env.DATABASE_URL,
144
- userId: this.userId
145
- });
146
- await this.postgresMemory.initialize();
147
- this.memory = this.postgresMemory;
148
- this.usingPostgres = true;
149
- // Re-create indexer with Postgres memory
150
- // Note: SystemIndexer expects AdvancedMemoryStore, so we skip indexing for Postgres
151
- console.log('[ORCHESTRATOR] Memory backend: PostgreSQL + pgvector');
152
- }
153
- catch (error) {
154
- console.error('[ORCHESTRATOR] PostgreSQL initialization failed, falling back to SQLite:', error);
155
- this.usingPostgres = false;
156
- }
157
- }
158
- else {
159
- console.log('[ORCHESTRATOR] Memory backend: SQLite + vectra (local)');
132
+ // Index the filesystem into memory
133
+ try {
134
+ const result = await this.indexer.indexHome();
135
+ console.log(`[ORCHESTRATOR] Filesystem indexed: ${result.projects} projects, ${result.directories} directories`);
160
136
  }
161
- // Index the filesystem into memory (only for SQLite - Postgres typically runs in cloud)
162
- if (!this.usingPostgres) {
163
- try {
164
- const result = await this.indexer.indexHome();
165
- console.log(`[ORCHESTRATOR] Filesystem indexed: ${result.projects} projects, ${result.directories} directories`);
166
- }
167
- catch (error) {
168
- console.error('[ORCHESTRATOR] Filesystem indexing failed:', error);
169
- // Continue anyway - indexing is a nice-to-have
170
- }
137
+ catch (error) {
138
+ console.error('[ORCHESTRATOR] Filesystem indexing failed:', error);
139
+ // Continue anyway - indexing is a nice-to-have
171
140
  }
172
141
  this.initialized = true;
173
142
  }
@@ -634,84 +603,6 @@ Be specific about what you want done.`,
634
603
  },
635
604
  required: ['port']
636
605
  }
637
- },
638
- // File watching tools for reactive workflows
639
- {
640
- name: 'add_watch_rule',
641
- description: 'Add a file watch rule to automatically trigger actions when files change. Useful for reactive workflows like auto-running tests when code changes.',
642
- input_schema: {
643
- type: 'object',
644
- properties: {
645
- pattern: {
646
- type: 'string',
647
- description: 'File path or directory to watch (e.g., "./src", "/path/to/file.ts")'
648
- },
649
- events: {
650
- type: 'array',
651
- items: { type: 'string', enum: ['add', 'change', 'unlink', 'addDir', 'unlinkDir'] },
652
- description: 'Events to watch for (default: ["change", "add"])'
653
- },
654
- action: {
655
- type: 'string',
656
- description: 'Task description to execute when triggered (sent to a worker)'
657
- },
658
- debounce_ms: {
659
- type: 'number',
660
- description: 'Debounce delay in milliseconds (default: 500)'
661
- }
662
- },
663
- required: ['pattern', 'action']
664
- }
665
- },
666
- {
667
- name: 'remove_watch_rule',
668
- description: 'Remove a file watch rule by its ID.',
669
- input_schema: {
670
- type: 'object',
671
- properties: {
672
- rule_id: {
673
- type: 'string',
674
- description: 'The ID of the watch rule to remove'
675
- }
676
- },
677
- required: ['rule_id']
678
- }
679
- },
680
- {
681
- name: 'list_watch_rules',
682
- description: 'List all active file watch rules.',
683
- input_schema: {
684
- type: 'object',
685
- properties: {},
686
- required: []
687
- }
688
- },
689
- {
690
- name: 'toggle_watch_rule',
691
- description: 'Enable or disable a file watch rule.',
692
- input_schema: {
693
- type: 'object',
694
- properties: {
695
- rule_id: {
696
- type: 'string',
697
- description: 'The ID of the watch rule to toggle'
698
- },
699
- enabled: {
700
- type: 'boolean',
701
- description: 'Whether to enable (true) or disable (false) the rule'
702
- }
703
- },
704
- required: ['rule_id', 'enabled']
705
- }
706
- },
707
- {
708
- name: 'watch_status',
709
- description: 'Get the status of the file watcher system.',
710
- input_schema: {
711
- type: 'object',
712
- properties: {},
713
- required: []
714
- }
715
606
  }
716
607
  ];
717
608
  }
@@ -791,22 +682,6 @@ Be specific about what you want done.`,
791
682
  case 'stop_local_server':
792
683
  result = await this.executeStopServer(input.port);
793
684
  break;
794
- // File watching tools
795
- case 'add_watch_rule':
796
- result = await this.executeAddWatchRule(input.pattern, input.action, input.events, input.debounce_ms);
797
- break;
798
- case 'remove_watch_rule':
799
- result = await this.executeRemoveWatchRule(input.rule_id);
800
- break;
801
- case 'list_watch_rules':
802
- result = await this.executeListWatchRules();
803
- break;
804
- case 'toggle_watch_rule':
805
- result = await this.executeToggleWatchRule(input.rule_id, input.enabled);
806
- break;
807
- case 'watch_status':
808
- result = await this.executeWatchStatus();
809
- break;
810
685
  default:
811
686
  result = { success: false, output: `Unknown tool: ${toolUse.name}` };
812
687
  }
@@ -1203,99 +1078,6 @@ This enables parallel workers to coordinate.`;
1203
1078
  return { success: false, output: `No server running on port ${port}` };
1204
1079
  }
1205
1080
  }
1206
- /**
1207
- * Add a file watch rule
1208
- */
1209
- async executeAddWatchRule(pattern, action, events, debounceMs) {
1210
- try {
1211
- const validEvents = ['add', 'change', 'unlink', 'addDir', 'unlinkDir'];
1212
- const eventList = events
1213
- ? events.filter((e) => validEvents.includes(e))
1214
- : ['change', 'add'];
1215
- const ruleId = this.fileWatcher.addRule({
1216
- pattern,
1217
- events: eventList,
1218
- action,
1219
- debounceMs
1220
- });
1221
- console.log(`[ORCHESTRATOR] Added watch rule ${ruleId}: ${pattern}`);
1222
- return {
1223
- success: true,
1224
- output: `Watch rule added:\n- ID: ${ruleId}\n- Pattern: ${pattern}\n- Events: ${eventList.join(', ')}\n- Action: ${action}\n- Debounce: ${debounceMs || 500}ms`
1225
- };
1226
- }
1227
- catch (error) {
1228
- const msg = error instanceof Error ? error.message : String(error);
1229
- return { success: false, output: `Failed to add watch rule: ${msg}` };
1230
- }
1231
- }
1232
- /**
1233
- * Remove a file watch rule
1234
- */
1235
- async executeRemoveWatchRule(ruleId) {
1236
- try {
1237
- const removed = this.fileWatcher.removeRule(ruleId);
1238
- if (removed) {
1239
- console.log(`[ORCHESTRATOR] Removed watch rule ${ruleId}`);
1240
- return { success: true, output: `Watch rule ${ruleId} removed` };
1241
- }
1242
- return { success: false, output: `Watch rule ${ruleId} not found` };
1243
- }
1244
- catch (error) {
1245
- const msg = error instanceof Error ? error.message : String(error);
1246
- return { success: false, output: `Failed to remove watch rule: ${msg}` };
1247
- }
1248
- }
1249
- /**
1250
- * List all file watch rules
1251
- */
1252
- async executeListWatchRules() {
1253
- try {
1254
- const rules = this.fileWatcher.listRules();
1255
- if (rules.length === 0) {
1256
- return { success: true, output: 'No watch rules configured.' };
1257
- }
1258
- const output = rules.map(r => `${r.enabled ? '✓' : '⏸'} ${r.id}\n Pattern: ${r.pattern}\n Events: ${r.events.join(', ')}\n Action: ${r.action.slice(0, 60)}...${r.lastTriggered ? `\n Last triggered: ${r.lastTriggered}` : ''}`).join('\n\n');
1259
- return { success: true, output: `Watch Rules (${rules.length}):\n\n${output}` };
1260
- }
1261
- catch (error) {
1262
- const msg = error instanceof Error ? error.message : String(error);
1263
- return { success: false, output: `Failed to list watch rules: ${msg}` };
1264
- }
1265
- }
1266
- /**
1267
- * Toggle a file watch rule
1268
- */
1269
- async executeToggleWatchRule(ruleId, enabled) {
1270
- try {
1271
- const toggled = this.fileWatcher.toggleRule(ruleId, enabled);
1272
- if (toggled) {
1273
- console.log(`[ORCHESTRATOR] ${enabled ? 'Enabled' : 'Disabled'} watch rule ${ruleId}`);
1274
- return { success: true, output: `Watch rule ${ruleId} ${enabled ? 'enabled' : 'disabled'}` };
1275
- }
1276
- return { success: false, output: `Watch rule ${ruleId} not found` };
1277
- }
1278
- catch (error) {
1279
- const msg = error instanceof Error ? error.message : String(error);
1280
- return { success: false, output: `Failed to toggle watch rule: ${msg}` };
1281
- }
1282
- }
1283
- /**
1284
- * Get file watcher status
1285
- */
1286
- async executeWatchStatus() {
1287
- try {
1288
- const status = this.fileWatcher.status();
1289
- return {
1290
- success: true,
1291
- output: `File Watcher Status:\n- Active watchers: ${status.activeWatchers}\n- Total rules: ${status.rules}\n- Enabled rules: ${status.enabledRules}`
1292
- };
1293
- }
1294
- catch (error) {
1295
- const msg = error instanceof Error ? error.message : String(error);
1296
- return { success: false, output: `Failed to get watcher status: ${msg}` };
1297
- }
1298
- }
1299
1081
  /**
1300
1082
  * Format tool preview for user
1301
1083
  */
@@ -1339,16 +1121,6 @@ This enables parallel workers to coordinate.`;
1339
1121
  return `Starting server on port ${input.port || 8080}...`;
1340
1122
  case 'stop_local_server':
1341
1123
  return `Stopping server on port ${input.port}...`;
1342
- case 'add_watch_rule':
1343
- return `Adding watch rule for ${input.pattern}...`;
1344
- case 'remove_watch_rule':
1345
- return `Removing watch rule ${input.rule_id}...`;
1346
- case 'list_watch_rules':
1347
- return 'Listing watch rules...';
1348
- case 'toggle_watch_rule':
1349
- return `${input.enabled ? 'Enabling' : 'Disabling'} watch rule ${input.rule_id}...`;
1350
- case 'watch_status':
1351
- return 'Getting watcher status...';
1352
1124
  default:
1353
1125
  return null;
1354
1126
  }
@@ -1369,13 +1141,11 @@ This enables parallel workers to coordinate.`;
1369
1141
  * Shutdown cleanly
1370
1142
  */
1371
1143
  async shutdown() {
1372
- this.fileWatcher.stopAll();
1373
1144
  this.scheduler.shutdown();
1374
- // Close memory store (Postgres is async, SQLite is sync)
1375
- if (this.usingPostgres && this.postgresMemory) {
1376
- await this.postgresMemory.close();
1145
+ if (this.memory instanceof PostgresMemoryStore) {
1146
+ await this.memory.close();
1377
1147
  }
1378
- else if ('close' in this.memory) {
1148
+ else {
1379
1149
  this.memory.close();
1380
1150
  }
1381
1151
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@siftd/connect-agent",
3
- "version": "0.2.12",
3
+ "version": "0.2.14",
4
4
  "description": "Master orchestrator agent - control Claude Code remotely via web",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",