@siftd/connect-agent 0.2.2 → 0.2.4

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/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.2'; // Should match package.json
13
+ const VERSION = '0.2.4'; // Should match package.json
14
14
  const state = {
15
15
  intervalId: null,
16
16
  runnerId: null,
@@ -690,12 +690,20 @@ Be specific about what you want done.`,
690
690
  const maxRetries = 2;
691
691
  const id = `worker_${Date.now()}_${++this.jobCounter}`;
692
692
  const cwd = workingDir || this.workspaceDir;
693
- // Build prompt for worker
693
+ // Build prompt for worker with checkpoint instructions
694
694
  let prompt = task;
695
695
  if (context) {
696
696
  prompt = `Context: ${context}\n\nTask: ${task}`;
697
697
  }
698
- prompt += '\n\nKeep your response concise and focused on results.';
698
+ // Add checkpoint instructions to prevent data loss on timeout
699
+ prompt += `
700
+
701
+ IMPORTANT - Progress Saving:
702
+ - Output your findings as you go, don't wait until the end
703
+ - Print discoveries, file paths, and insights immediately as you find them
704
+ - If you're exploring multiple files, report on each one before moving to the next
705
+ - If you hit a timeout, the orchestrator should still have your partial findings
706
+ - Keep responses concise but don't batch output until the very end`;
699
707
  console.log(`[ORCHESTRATOR] Delegating to worker ${id}: ${task.slice(0, 80)}...`);
700
708
  return new Promise((resolve) => {
701
709
  const job = {
@@ -722,10 +730,20 @@ Be specific about what you want done.`,
722
730
  if (job.status === 'running') {
723
731
  job.status = 'timeout';
724
732
  job.endTime = Date.now();
725
- child.kill('SIGTERM');
733
+ // Send SIGINT first to allow graceful shutdown and output flush
734
+ child.kill('SIGINT');
735
+ // Give 5 seconds for graceful shutdown before SIGTERM
736
+ setTimeout(() => {
737
+ if (!child.killed) {
738
+ child.kill('SIGTERM');
739
+ }
740
+ }, 5000);
741
+ const partialOutput = job.output.trim();
726
742
  resolve({
727
743
  success: false,
728
- output: `Worker timed out after 5 minutes.\nPartial output:\n${job.output.slice(-1000)}`
744
+ output: partialOutput
745
+ ? `Worker timed out after 5 minutes. Partial findings:\n${partialOutput.slice(-2000)}`
746
+ : 'Worker timed out after 5 minutes with no output. The task may be too complex - try breaking it into smaller steps.'
729
747
  });
730
748
  }
731
749
  }, 5 * 60 * 1000);
@@ -9,7 +9,7 @@
9
9
  export type MessageHandler = (message: WebSocketMessage) => Promise<void>;
10
10
  export type StreamHandler = (chunk: string) => void;
11
11
  export interface WebSocketMessage {
12
- type: 'message' | 'interrupt' | 'ping' | 'connected';
12
+ type: 'message' | 'interrupt' | 'ping' | 'pong' | 'connected';
13
13
  id?: string;
14
14
  content?: string;
15
15
  timestamp?: number;
package/dist/websocket.js CHANGED
@@ -184,6 +184,9 @@ export class AgentWebSocket {
184
184
  case 'ping':
185
185
  this.sendToServer({ type: 'pong' });
186
186
  break;
187
+ case 'pong':
188
+ // Expected response to our ping, ignore silently
189
+ break;
187
190
  case 'connected':
188
191
  console.log('[WS] Server confirmed connection');
189
192
  break;
@@ -70,8 +70,16 @@ export class WorkerManager {
70
70
  this.saveJob(job);
71
71
  // Spawn Claude CLI process
72
72
  try {
73
+ // Add checkpoint instructions to prevent data loss on timeout
74
+ const enhancedTask = `${task}
75
+
76
+ IMPORTANT - Progress Saving:
77
+ - Output findings as you go, don't wait until the end
78
+ - Print discoveries and insights immediately as you find them
79
+ - Report on each file/step before moving to the next
80
+ - Keep responses concise but don't batch output`;
73
81
  // Escape single quotes in task for shell safety
74
- const escapedTask = task.replace(/'/g, "'\\''");
82
+ const escapedTask = enhancedTask.replace(/'/g, "'\\''");
75
83
  // Spawn using bash -l -c to ensure proper PATH resolution (NVM, etc.)
76
84
  // This is more reliable than shell: true which has quote escaping issues
77
85
  const child = spawn('bash', ['-l', '-c', `claude -p '${escapedTask}' --output-format text --dangerously-skip-permissions`], {
@@ -101,11 +109,18 @@ export class WorkerManager {
101
109
  // Set up timeout
102
110
  const timeoutHandle = setTimeout(() => {
103
111
  if (child.pid && !child.killed) {
104
- child.kill('SIGTERM');
112
+ // Send SIGINT first to allow graceful shutdown and output flush
113
+ child.kill('SIGINT');
114
+ // Give 5 seconds for graceful shutdown before SIGTERM
115
+ setTimeout(() => {
116
+ if (!child.killed) {
117
+ child.kill('SIGTERM');
118
+ }
119
+ }, 5000);
105
120
  job.status = 'timeout';
106
121
  job.error = `Job timed out after ${effectiveTimeout}ms`;
107
122
  job.completed = new Date().toISOString();
108
- job.result = stdout || stderr;
123
+ job.result = stdout.trim() || stderr.trim() || 'No output captured before timeout';
109
124
  this.saveJob(job);
110
125
  this.activeWorkers.delete(jobId);
111
126
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@siftd/connect-agent",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Master orchestrator agent - control Claude Code remotely via web",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",