@siftd/connect-agent 0.2.11 → 0.2.13

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.11'; // Should match package.json
13
+ const VERSION = '0.2.13'; // Should match package.json
14
14
  const state = {
15
15
  intervalId: null,
16
16
  runnerId: null,
@@ -23,7 +23,6 @@ export declare class MasterOrchestrator {
23
23
  private webTools;
24
24
  private workerTools;
25
25
  private sharedState;
26
- private fileWatcher;
27
26
  private verboseMode;
28
27
  constructor(options: {
29
28
  apiKey: string;
@@ -120,26 +119,6 @@ export declare class MasterOrchestrator {
120
119
  * Stop a running server on a port
121
120
  */
122
121
  private executeStopServer;
123
- /**
124
- * Add a file watch rule
125
- */
126
- private executeAddWatchRule;
127
- /**
128
- * Remove a file watch rule
129
- */
130
- private executeRemoveWatchRule;
131
- /**
132
- * List all file watch rules
133
- */
134
- private executeListWatchRules;
135
- /**
136
- * Toggle a file watch rule
137
- */
138
- private executeToggleWatchRule;
139
- /**
140
- * Get file watcher status
141
- */
142
- private executeWatchStatus;
143
122
  /**
144
123
  * Format tool preview for user
145
124
  */
@@ -149,5 +128,5 @@ export declare class MasterOrchestrator {
149
128
  /**
150
129
  * Shutdown cleanly
151
130
  */
152
- shutdown(): void;
131
+ shutdown(): Promise<void>;
153
132
  }
@@ -8,9 +8,9 @@ import Anthropic from '@anthropic-ai/sdk';
8
8
  import { spawn, execSync } from 'child_process';
9
9
  import { existsSync } from 'fs';
10
10
  import { AdvancedMemoryStore } from './core/memory-advanced.js';
11
+ import { PostgresMemoryStore, isPostgresConfigured } from './core/memory-postgres.js';
11
12
  import { TaskScheduler } from './core/scheduler.js';
12
13
  import { SystemIndexer } from './core/system-indexer.js';
13
- import { FileWatcher } from './core/file-watcher.js';
14
14
  import { BashTool } from './tools/bash.js';
15
15
  import { WebTools } from './tools/web.js';
16
16
  import { WorkerTools } from './tools/worker.js';
@@ -88,7 +88,6 @@ export class MasterOrchestrator {
88
88
  webTools;
89
89
  workerTools;
90
90
  sharedState;
91
- fileWatcher;
92
91
  verboseMode = new Map(); // per-user verbose mode
93
92
  constructor(options) {
94
93
  this.client = new Anthropic({ apiKey: options.apiKey });
@@ -99,12 +98,21 @@ export class MasterOrchestrator {
99
98
  // Find claude binary - critical for worker delegation
100
99
  this.claudePath = this.findClaudeBinary();
101
100
  console.log(`[ORCHESTRATOR] Claude binary: ${this.claudePath}`);
102
- // Initialize memory with user-specific paths
103
- this.memory = new AdvancedMemoryStore({
104
- dbPath: `memories_${options.userId}.db`,
105
- vectorPath: `./memory_vectors_${options.userId}`,
106
- voyageApiKey: options.voyageApiKey
107
- });
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
+ }
108
116
  this.scheduler = new TaskScheduler(`scheduled_${options.userId}.json`);
109
117
  // Create indexer (will index on first initialize call)
110
118
  this.indexer = new SystemIndexer(this.memory);
@@ -113,14 +121,6 @@ export class MasterOrchestrator {
113
121
  this.webTools = new WebTools();
114
122
  this.workerTools = new WorkerTools(this.workspaceDir);
115
123
  this.sharedState = new SharedState(this.workspaceDir);
116
- this.fileWatcher = new FileWatcher(this.workspaceDir);
117
- // Set up file watcher trigger handler
118
- this.fileWatcher.on('trigger', async ({ rule, event, action }) => {
119
- console.log(`[WATCHER] Triggered: ${rule.pattern} (${event.type}: ${event.path})`);
120
- // Auto-delegate to worker when file changes
121
- const task = `${action}\n\nTriggered by file change:\n- File: ${event.path}\n- Event: ${event.type}\n- Rule: ${rule.pattern}`;
122
- this.delegateToWorker(task, undefined, this.workspaceDir);
123
- });
124
124
  }
125
125
  /**
126
126
  * Initialize the orchestrator - indexes filesystem on first call
@@ -603,84 +603,6 @@ Be specific about what you want done.`,
603
603
  },
604
604
  required: ['port']
605
605
  }
606
- },
607
- // File watching tools for reactive workflows
608
- {
609
- name: 'add_watch_rule',
610
- description: 'Add a file watch rule to automatically trigger actions when files change. Useful for reactive workflows like auto-running tests when code changes.',
611
- input_schema: {
612
- type: 'object',
613
- properties: {
614
- pattern: {
615
- type: 'string',
616
- description: 'File path or directory to watch (e.g., "./src", "/path/to/file.ts")'
617
- },
618
- events: {
619
- type: 'array',
620
- items: { type: 'string', enum: ['add', 'change', 'unlink', 'addDir', 'unlinkDir'] },
621
- description: 'Events to watch for (default: ["change", "add"])'
622
- },
623
- action: {
624
- type: 'string',
625
- description: 'Task description to execute when triggered (sent to a worker)'
626
- },
627
- debounce_ms: {
628
- type: 'number',
629
- description: 'Debounce delay in milliseconds (default: 500)'
630
- }
631
- },
632
- required: ['pattern', 'action']
633
- }
634
- },
635
- {
636
- name: 'remove_watch_rule',
637
- description: 'Remove a file watch rule by its ID.',
638
- input_schema: {
639
- type: 'object',
640
- properties: {
641
- rule_id: {
642
- type: 'string',
643
- description: 'The ID of the watch rule to remove'
644
- }
645
- },
646
- required: ['rule_id']
647
- }
648
- },
649
- {
650
- name: 'list_watch_rules',
651
- description: 'List all active file watch rules.',
652
- input_schema: {
653
- type: 'object',
654
- properties: {},
655
- required: []
656
- }
657
- },
658
- {
659
- name: 'toggle_watch_rule',
660
- description: 'Enable or disable a file watch rule.',
661
- input_schema: {
662
- type: 'object',
663
- properties: {
664
- rule_id: {
665
- type: 'string',
666
- description: 'The ID of the watch rule to toggle'
667
- },
668
- enabled: {
669
- type: 'boolean',
670
- description: 'Whether to enable (true) or disable (false) the rule'
671
- }
672
- },
673
- required: ['rule_id', 'enabled']
674
- }
675
- },
676
- {
677
- name: 'watch_status',
678
- description: 'Get the status of the file watcher system.',
679
- input_schema: {
680
- type: 'object',
681
- properties: {},
682
- required: []
683
- }
684
606
  }
685
607
  ];
686
608
  }
@@ -760,22 +682,6 @@ Be specific about what you want done.`,
760
682
  case 'stop_local_server':
761
683
  result = await this.executeStopServer(input.port);
762
684
  break;
763
- // File watching tools
764
- case 'add_watch_rule':
765
- result = await this.executeAddWatchRule(input.pattern, input.action, input.events, input.debounce_ms);
766
- break;
767
- case 'remove_watch_rule':
768
- result = await this.executeRemoveWatchRule(input.rule_id);
769
- break;
770
- case 'list_watch_rules':
771
- result = await this.executeListWatchRules();
772
- break;
773
- case 'toggle_watch_rule':
774
- result = await this.executeToggleWatchRule(input.rule_id, input.enabled);
775
- break;
776
- case 'watch_status':
777
- result = await this.executeWatchStatus();
778
- break;
779
685
  default:
780
686
  result = { success: false, output: `Unknown tool: ${toolUse.name}` };
781
687
  }
@@ -1172,99 +1078,6 @@ This enables parallel workers to coordinate.`;
1172
1078
  return { success: false, output: `No server running on port ${port}` };
1173
1079
  }
1174
1080
  }
1175
- /**
1176
- * Add a file watch rule
1177
- */
1178
- async executeAddWatchRule(pattern, action, events, debounceMs) {
1179
- try {
1180
- const validEvents = ['add', 'change', 'unlink', 'addDir', 'unlinkDir'];
1181
- const eventList = events
1182
- ? events.filter((e) => validEvents.includes(e))
1183
- : ['change', 'add'];
1184
- const ruleId = this.fileWatcher.addRule({
1185
- pattern,
1186
- events: eventList,
1187
- action,
1188
- debounceMs
1189
- });
1190
- console.log(`[ORCHESTRATOR] Added watch rule ${ruleId}: ${pattern}`);
1191
- return {
1192
- success: true,
1193
- output: `Watch rule added:\n- ID: ${ruleId}\n- Pattern: ${pattern}\n- Events: ${eventList.join(', ')}\n- Action: ${action}\n- Debounce: ${debounceMs || 500}ms`
1194
- };
1195
- }
1196
- catch (error) {
1197
- const msg = error instanceof Error ? error.message : String(error);
1198
- return { success: false, output: `Failed to add watch rule: ${msg}` };
1199
- }
1200
- }
1201
- /**
1202
- * Remove a file watch rule
1203
- */
1204
- async executeRemoveWatchRule(ruleId) {
1205
- try {
1206
- const removed = this.fileWatcher.removeRule(ruleId);
1207
- if (removed) {
1208
- console.log(`[ORCHESTRATOR] Removed watch rule ${ruleId}`);
1209
- return { success: true, output: `Watch rule ${ruleId} removed` };
1210
- }
1211
- return { success: false, output: `Watch rule ${ruleId} not found` };
1212
- }
1213
- catch (error) {
1214
- const msg = error instanceof Error ? error.message : String(error);
1215
- return { success: false, output: `Failed to remove watch rule: ${msg}` };
1216
- }
1217
- }
1218
- /**
1219
- * List all file watch rules
1220
- */
1221
- async executeListWatchRules() {
1222
- try {
1223
- const rules = this.fileWatcher.listRules();
1224
- if (rules.length === 0) {
1225
- return { success: true, output: 'No watch rules configured.' };
1226
- }
1227
- 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');
1228
- return { success: true, output: `Watch Rules (${rules.length}):\n\n${output}` };
1229
- }
1230
- catch (error) {
1231
- const msg = error instanceof Error ? error.message : String(error);
1232
- return { success: false, output: `Failed to list watch rules: ${msg}` };
1233
- }
1234
- }
1235
- /**
1236
- * Toggle a file watch rule
1237
- */
1238
- async executeToggleWatchRule(ruleId, enabled) {
1239
- try {
1240
- const toggled = this.fileWatcher.toggleRule(ruleId, enabled);
1241
- if (toggled) {
1242
- console.log(`[ORCHESTRATOR] ${enabled ? 'Enabled' : 'Disabled'} watch rule ${ruleId}`);
1243
- return { success: true, output: `Watch rule ${ruleId} ${enabled ? 'enabled' : 'disabled'}` };
1244
- }
1245
- return { success: false, output: `Watch rule ${ruleId} not found` };
1246
- }
1247
- catch (error) {
1248
- const msg = error instanceof Error ? error.message : String(error);
1249
- return { success: false, output: `Failed to toggle watch rule: ${msg}` };
1250
- }
1251
- }
1252
- /**
1253
- * Get file watcher status
1254
- */
1255
- async executeWatchStatus() {
1256
- try {
1257
- const status = this.fileWatcher.status();
1258
- return {
1259
- success: true,
1260
- output: `File Watcher Status:\n- Active watchers: ${status.activeWatchers}\n- Total rules: ${status.rules}\n- Enabled rules: ${status.enabledRules}`
1261
- };
1262
- }
1263
- catch (error) {
1264
- const msg = error instanceof Error ? error.message : String(error);
1265
- return { success: false, output: `Failed to get watcher status: ${msg}` };
1266
- }
1267
- }
1268
1081
  /**
1269
1082
  * Format tool preview for user
1270
1083
  */
@@ -1308,16 +1121,6 @@ This enables parallel workers to coordinate.`;
1308
1121
  return `Starting server on port ${input.port || 8080}...`;
1309
1122
  case 'stop_local_server':
1310
1123
  return `Stopping server on port ${input.port}...`;
1311
- case 'add_watch_rule':
1312
- return `Adding watch rule for ${input.pattern}...`;
1313
- case 'remove_watch_rule':
1314
- return `Removing watch rule ${input.rule_id}...`;
1315
- case 'list_watch_rules':
1316
- return 'Listing watch rules...';
1317
- case 'toggle_watch_rule':
1318
- return `${input.enabled ? 'Enabling' : 'Disabling'} watch rule ${input.rule_id}...`;
1319
- case 'watch_status':
1320
- return 'Getting watcher status...';
1321
1124
  default:
1322
1125
  return null;
1323
1126
  }
@@ -1337,9 +1140,13 @@ This enables parallel workers to coordinate.`;
1337
1140
  /**
1338
1141
  * Shutdown cleanly
1339
1142
  */
1340
- shutdown() {
1341
- this.fileWatcher.stopAll();
1143
+ async shutdown() {
1342
1144
  this.scheduler.shutdown();
1343
- this.memory.close();
1145
+ if (this.memory instanceof PostgresMemoryStore) {
1146
+ await this.memory.close();
1147
+ }
1148
+ else {
1149
+ this.memory.close();
1150
+ }
1344
1151
  }
1345
1152
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@siftd/connect-agent",
3
- "version": "0.2.11",
3
+ "version": "0.2.13",
4
4
  "description": "Master orchestrator agent - control Claude Code remotely via web",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -43,7 +43,6 @@
43
43
  "conf": "^13.0.1",
44
44
  "node-cron": "^3.0.3",
45
45
  "ora": "^8.1.1",
46
- "pg": "^8.13.1",
47
46
  "vectra": "^0.9.0",
48
47
  "ws": "^8.18.3"
49
48
  },
@@ -51,7 +50,6 @@
51
50
  "@types/better-sqlite3": "^7.6.12",
52
51
  "@types/node": "^22.10.2",
53
52
  "@types/node-cron": "^3.0.11",
54
- "@types/pg": "^8.11.10",
55
53
  "@types/ws": "^8.18.1",
56
54
  "typescript": "^5.7.2"
57
55
  }