@siftd/connect-agent 0.2.14 → 0.2.16
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 +13 -0
- package/dist/heartbeat.js +1 -1
- package/dist/orchestrator.d.ts +28 -0
- package/dist/orchestrator.js +197 -52
- package/dist/tools/worker.d.ts +14 -0
- package/dist/tools/worker.js +33 -0
- package/dist/websocket.d.ts +5 -0
- package/dist/websocket.js +9 -0
- package/package.json +1 -1
package/dist/agent.js
CHANGED
|
@@ -211,6 +211,19 @@ export async function runAgent(pollInterval = 2000) {
|
|
|
211
211
|
// Try WebSocket first
|
|
212
212
|
wsClient = new AgentWebSocket();
|
|
213
213
|
const wsConnected = await wsClient.connect();
|
|
214
|
+
// Always set up worker status callback for progress bars (works with or without WebSocket)
|
|
215
|
+
if (orchestrator) {
|
|
216
|
+
orchestrator.setWorkerStatusCallback((workers) => {
|
|
217
|
+
if (wsClient?.connected()) {
|
|
218
|
+
wsClient.sendWorkersUpdate(workers);
|
|
219
|
+
}
|
|
220
|
+
// Log running workers count for visibility even without WebSocket
|
|
221
|
+
const running = workers.filter(w => w.status === 'running');
|
|
222
|
+
if (running.length > 0) {
|
|
223
|
+
console.log(`[WORKERS] ${running.length} running: ${running.map(w => `${w.id} (${w.progress}%)`).join(', ')}`);
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
}
|
|
214
227
|
if (wsConnected) {
|
|
215
228
|
console.log('[AGENT] Using WebSocket for real-time communication\n');
|
|
216
229
|
// Handle messages via WebSocket
|
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.
|
|
13
|
+
const VERSION = '0.2.16'; // Should match package.json
|
|
14
14
|
const state = {
|
|
15
15
|
intervalId: null,
|
|
16
16
|
runnerId: null,
|
package/dist/orchestrator.d.ts
CHANGED
|
@@ -6,6 +6,15 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { MessageParam } from '@anthropic-ai/sdk/resources/messages';
|
|
8
8
|
export type MessageSender = (message: string) => Promise<void>;
|
|
9
|
+
export interface WorkerStatus {
|
|
10
|
+
id: string;
|
|
11
|
+
task: string;
|
|
12
|
+
status: 'running' | 'completed' | 'failed' | 'timeout';
|
|
13
|
+
progress: number;
|
|
14
|
+
elapsed: number;
|
|
15
|
+
estimated: number;
|
|
16
|
+
}
|
|
17
|
+
export type WorkerStatusCallback = (workers: WorkerStatus[]) => void;
|
|
9
18
|
export declare class MasterOrchestrator {
|
|
10
19
|
private client;
|
|
11
20
|
private model;
|
|
@@ -15,6 +24,8 @@ export declare class MasterOrchestrator {
|
|
|
15
24
|
private indexer;
|
|
16
25
|
private jobs;
|
|
17
26
|
private jobCounter;
|
|
27
|
+
private workerStatusCallback;
|
|
28
|
+
private workerStatusInterval;
|
|
18
29
|
private userId;
|
|
19
30
|
private workspaceDir;
|
|
20
31
|
private claudePath;
|
|
@@ -36,6 +47,22 @@ export declare class MasterOrchestrator {
|
|
|
36
47
|
* Initialize the orchestrator - indexes filesystem on first call
|
|
37
48
|
*/
|
|
38
49
|
initialize(): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Set callback for worker status updates (for UI progress bars)
|
|
52
|
+
*/
|
|
53
|
+
setWorkerStatusCallback(callback: WorkerStatusCallback | null): void;
|
|
54
|
+
/**
|
|
55
|
+
* Get current status of all workers (from both delegateToWorker and spawn_worker)
|
|
56
|
+
*/
|
|
57
|
+
getWorkerStatus(): WorkerStatus[];
|
|
58
|
+
/**
|
|
59
|
+
* Broadcast worker status to callback
|
|
60
|
+
*/
|
|
61
|
+
private broadcastWorkerStatus;
|
|
62
|
+
/**
|
|
63
|
+
* Estimate task duration based on content
|
|
64
|
+
*/
|
|
65
|
+
private estimateTaskDuration;
|
|
39
66
|
/**
|
|
40
67
|
* Find the claude binary path
|
|
41
68
|
*/
|
|
@@ -76,6 +103,7 @@ export declare class MasterOrchestrator {
|
|
|
76
103
|
private processToolCalls;
|
|
77
104
|
/**
|
|
78
105
|
* Delegate task to Claude Code CLI worker with retry logic
|
|
106
|
+
* @param timeoutMs - Timeout in milliseconds (default: 30 minutes, max: 60 minutes)
|
|
79
107
|
*/
|
|
80
108
|
private delegateToWorker;
|
|
81
109
|
/**
|
package/dist/orchestrator.js
CHANGED
|
@@ -16,60 +16,63 @@ import { WebTools } from './tools/web.js';
|
|
|
16
16
|
import { WorkerTools } from './tools/worker.js';
|
|
17
17
|
import { SharedState } from './workers/shared-state.js';
|
|
18
18
|
import { getKnowledgeForPrompt } from './genesis/index.js';
|
|
19
|
-
const SYSTEM_PROMPT = `You
|
|
19
|
+
const SYSTEM_PROMPT = `You are a MASTER ORCHESTRATOR - NOT a worker. You delegate ALL file/code work to Claude Code CLI workers.
|
|
20
20
|
|
|
21
|
-
CRITICAL
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
CRITICAL IDENTITY:
|
|
22
|
+
- You are the BRAIN, not the hands
|
|
23
|
+
- You ORCHESTRATE workers - you don't do the work yourself
|
|
24
|
+
- You REMEMBER everything using your memory tools
|
|
25
|
+
- You PLAN and COORDINATE, then delegate execution
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
- bash: Run any shell command directly
|
|
28
|
-
- web_search: Search the internet for information
|
|
29
|
-
- fetch_url: Fetch and read web page content
|
|
30
|
-
- spawn_worker: Spawn background Claude Code workers for complex multi-step tasks
|
|
31
|
-
- delegate_to_worker: Delegate complex tasks that need full CLI autonomy
|
|
32
|
-
- remember/search_memory: Persistent memory across conversations
|
|
33
|
-
- start_local_server/open_browser: Serve and preview web projects
|
|
27
|
+
STRICT TOOL USAGE RULES:
|
|
34
28
|
|
|
35
|
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
- Quick checks or reads → bash
|
|
40
|
-
- Long-running background tasks → spawn_worker (non-blocking)
|
|
29
|
+
✅ USE bash ONLY for READ-ONLY operations:
|
|
30
|
+
- ls, pwd, cat, head, tail, find, which, echo, date
|
|
31
|
+
- git status, git log, git diff (viewing only)
|
|
32
|
+
- Checking if files/directories exist
|
|
41
33
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
34
|
+
❌ NEVER use bash for:
|
|
35
|
+
- Creating files (echo >, touch, mkdir)
|
|
36
|
+
- Editing files (sed, awk, tee)
|
|
37
|
+
- Moving/copying/deleting (mv, cp, rm)
|
|
38
|
+
- Installing packages (npm install, pip install)
|
|
39
|
+
- Running builds or tests
|
|
40
|
+
- ANY file modification whatsoever
|
|
46
41
|
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
✅ USE delegate_to_worker or spawn_worker for:
|
|
43
|
+
- Creating, editing, or deleting ANY files
|
|
44
|
+
- Running npm/pip/cargo install
|
|
45
|
+
- Building, testing, deploying
|
|
46
|
+
- Complex multi-step tasks
|
|
47
|
+
- ANY filesystem modification
|
|
49
48
|
|
|
50
|
-
|
|
51
|
-
-
|
|
52
|
-
-
|
|
53
|
-
-
|
|
54
|
-
-
|
|
55
|
-
- Remember things across conversations
|
|
56
|
-
- Schedule tasks for later
|
|
57
|
-
- Have real conversations
|
|
49
|
+
MEMORY IS MANDATORY:
|
|
50
|
+
- ALWAYS use search_memory before starting work
|
|
51
|
+
- ALWAYS use remember after significant actions
|
|
52
|
+
- Log your orchestration decisions to memory
|
|
53
|
+
- Learn from failures - remember what went wrong
|
|
58
54
|
|
|
59
|
-
|
|
60
|
-
-
|
|
61
|
-
-
|
|
62
|
-
-
|
|
63
|
-
-
|
|
64
|
-
|
|
65
|
-
|
|
55
|
+
WORKER ORCHESTRATION:
|
|
56
|
+
- spawn_worker: For parallel/background tasks (non-blocking)
|
|
57
|
+
- delegate_to_worker: For sequential tasks needing full autonomy
|
|
58
|
+
- ALWAYS include logging instructions in worker prompts
|
|
59
|
+
- Tell workers to save their logs to /tmp/worker-{job_id}.log
|
|
60
|
+
|
|
61
|
+
AFTER EVERY SIGNIFICANT ACTION:
|
|
62
|
+
1. Log it to memory with remember
|
|
63
|
+
2. Note what worked or failed
|
|
64
|
+
3. Update your understanding of the user's system
|
|
65
|
+
|
|
66
|
+
WHO YOU ARE:
|
|
67
|
+
Warm, helpful orchestrator. You coordinate, remember, and delegate. You're the persistent layer that makes AI feel personal - but you do it by managing workers, not by doing tasks yourself.
|
|
66
68
|
|
|
67
69
|
SLASH COMMANDS:
|
|
68
70
|
- /reset - Clear conversation and memory context
|
|
69
|
-
- /
|
|
71
|
+
- /mode - Show current mode and capabilities
|
|
72
|
+
- /memory - Show memory stats
|
|
70
73
|
- /help - Show available commands
|
|
71
74
|
|
|
72
|
-
You
|
|
75
|
+
You ACT through workers. You REMEMBER through memory. You NEVER do file operations directly.`;
|
|
73
76
|
export class MasterOrchestrator {
|
|
74
77
|
client;
|
|
75
78
|
model;
|
|
@@ -79,6 +82,8 @@ export class MasterOrchestrator {
|
|
|
79
82
|
indexer;
|
|
80
83
|
jobs = new Map();
|
|
81
84
|
jobCounter = 0;
|
|
85
|
+
workerStatusCallback = null;
|
|
86
|
+
workerStatusInterval = null;
|
|
82
87
|
userId;
|
|
83
88
|
workspaceDir;
|
|
84
89
|
claudePath;
|
|
@@ -140,6 +145,128 @@ export class MasterOrchestrator {
|
|
|
140
145
|
}
|
|
141
146
|
this.initialized = true;
|
|
142
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* Set callback for worker status updates (for UI progress bars)
|
|
150
|
+
*/
|
|
151
|
+
setWorkerStatusCallback(callback) {
|
|
152
|
+
this.workerStatusCallback = callback;
|
|
153
|
+
if (callback) {
|
|
154
|
+
// Start broadcasting status every 500ms when workers are running
|
|
155
|
+
if (!this.workerStatusInterval) {
|
|
156
|
+
this.workerStatusInterval = setInterval(() => {
|
|
157
|
+
this.broadcastWorkerStatus();
|
|
158
|
+
}, 500);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
// Stop broadcasting
|
|
163
|
+
if (this.workerStatusInterval) {
|
|
164
|
+
clearInterval(this.workerStatusInterval);
|
|
165
|
+
this.workerStatusInterval = null;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Get current status of all workers (from both delegateToWorker and spawn_worker)
|
|
171
|
+
*/
|
|
172
|
+
getWorkerStatus() {
|
|
173
|
+
const now = Date.now();
|
|
174
|
+
const workers = [];
|
|
175
|
+
// Include jobs from delegateToWorker (this.jobs)
|
|
176
|
+
for (const [id, job] of this.jobs) {
|
|
177
|
+
const elapsed = (now - job.startTime) / 1000;
|
|
178
|
+
const estimated = job.estimatedTime;
|
|
179
|
+
// Calculate progress: cap at 90% while running, 100% when done
|
|
180
|
+
let progress;
|
|
181
|
+
if (job.status === 'completed') {
|
|
182
|
+
progress = 100;
|
|
183
|
+
}
|
|
184
|
+
else if (job.status === 'failed' || job.status === 'timeout') {
|
|
185
|
+
progress = 100; // Show as complete even on failure
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
progress = Math.min(90, (elapsed / estimated) * 100);
|
|
189
|
+
}
|
|
190
|
+
workers.push({
|
|
191
|
+
id,
|
|
192
|
+
task: job.task.slice(0, 60),
|
|
193
|
+
status: job.status,
|
|
194
|
+
progress: Math.round(progress),
|
|
195
|
+
elapsed: Math.round(elapsed),
|
|
196
|
+
estimated: Math.round(estimated)
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
// Also include jobs from spawn_worker (workerTools)
|
|
200
|
+
try {
|
|
201
|
+
const spawnedWorkers = this.workerTools.getRunningWorkers();
|
|
202
|
+
for (const worker of spawnedWorkers) {
|
|
203
|
+
// Don't duplicate if already in this.jobs
|
|
204
|
+
if (workers.some(w => w.id === worker.id))
|
|
205
|
+
continue;
|
|
206
|
+
const elapsed = (now - worker.startTime) / 1000;
|
|
207
|
+
const estimated = worker.estimatedTime;
|
|
208
|
+
const progress = Math.min(90, (elapsed / estimated) * 100);
|
|
209
|
+
workers.push({
|
|
210
|
+
id: worker.id,
|
|
211
|
+
task: worker.task.slice(0, 60),
|
|
212
|
+
status: worker.status,
|
|
213
|
+
progress: Math.round(progress),
|
|
214
|
+
elapsed: Math.round(elapsed),
|
|
215
|
+
estimated: Math.round(estimated)
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
// WorkerTools not available, skip
|
|
221
|
+
}
|
|
222
|
+
return workers;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Broadcast worker status to callback
|
|
226
|
+
*/
|
|
227
|
+
broadcastWorkerStatus() {
|
|
228
|
+
if (!this.workerStatusCallback)
|
|
229
|
+
return;
|
|
230
|
+
const workers = this.getWorkerStatus();
|
|
231
|
+
// Only broadcast if there are running workers
|
|
232
|
+
const hasRunning = workers.some(w => w.status === 'running');
|
|
233
|
+
if (hasRunning || workers.length > 0) {
|
|
234
|
+
this.workerStatusCallback(workers);
|
|
235
|
+
}
|
|
236
|
+
// Clean up completed jobs after 3 seconds
|
|
237
|
+
const now = Date.now();
|
|
238
|
+
for (const [id, job] of this.jobs) {
|
|
239
|
+
if (job.status !== 'running' && job.endTime && (now - job.endTime) > 3000) {
|
|
240
|
+
this.jobs.delete(id);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Estimate task duration based on content
|
|
246
|
+
*/
|
|
247
|
+
estimateTaskDuration(task) {
|
|
248
|
+
const lower = task.toLowerCase();
|
|
249
|
+
// Complex tasks: 3 minutes
|
|
250
|
+
if (lower.includes('refactor') || lower.includes('implement') ||
|
|
251
|
+
lower.includes('create') || lower.includes('build') ||
|
|
252
|
+
lower.includes('write tests') || lower.includes('add tests')) {
|
|
253
|
+
return 180;
|
|
254
|
+
}
|
|
255
|
+
// Medium tasks: 90 seconds
|
|
256
|
+
if (lower.includes('fix') || lower.includes('update') ||
|
|
257
|
+
lower.includes('modify') || lower.includes('change') ||
|
|
258
|
+
lower.includes('add') || lower.includes('install')) {
|
|
259
|
+
return 90;
|
|
260
|
+
}
|
|
261
|
+
// Simple tasks: 45 seconds
|
|
262
|
+
if (lower.includes('find') || lower.includes('search') ||
|
|
263
|
+
lower.includes('read') || lower.includes('list') ||
|
|
264
|
+
lower.includes('check') || lower.includes('show')) {
|
|
265
|
+
return 45;
|
|
266
|
+
}
|
|
267
|
+
// Default: 60 seconds
|
|
268
|
+
return 60;
|
|
269
|
+
}
|
|
143
270
|
/**
|
|
144
271
|
* Find the claude binary path
|
|
145
272
|
*/
|
|
@@ -301,16 +428,21 @@ export class MasterOrchestrator {
|
|
|
301
428
|
*/
|
|
302
429
|
getToolDefinitions() {
|
|
303
430
|
return [
|
|
304
|
-
// Direct bash execution -
|
|
431
|
+
// Direct bash execution - READ-ONLY OPERATIONS ONLY
|
|
305
432
|
{
|
|
306
433
|
name: 'bash',
|
|
307
|
-
description:
|
|
434
|
+
description: `Execute a READ-ONLY shell command. ONLY use for viewing/checking - NEVER for modifications.
|
|
435
|
+
|
|
436
|
+
ALLOWED: ls, pwd, cat, head, tail, find, which, echo, date, git status, git log, git diff, test -f, test -d
|
|
437
|
+
FORBIDDEN: touch, mkdir, rm, mv, cp, echo >, tee, sed -i, npm install, pip install, ANY file creation/modification
|
|
438
|
+
|
|
439
|
+
For ANY file creation, editing, or system modification, use delegate_to_worker instead.`,
|
|
308
440
|
input_schema: {
|
|
309
441
|
type: 'object',
|
|
310
442
|
properties: {
|
|
311
443
|
command: {
|
|
312
444
|
type: 'string',
|
|
313
|
-
description: '
|
|
445
|
+
description: 'READ-ONLY bash command (no file modifications allowed)'
|
|
314
446
|
},
|
|
315
447
|
timeout: {
|
|
316
448
|
type: 'number',
|
|
@@ -698,9 +830,12 @@ Be specific about what you want done.`,
|
|
|
698
830
|
}
|
|
699
831
|
/**
|
|
700
832
|
* Delegate task to Claude Code CLI worker with retry logic
|
|
833
|
+
* @param timeoutMs - Timeout in milliseconds (default: 30 minutes, max: 60 minutes)
|
|
701
834
|
*/
|
|
702
|
-
async delegateToWorker(task, context, workingDir, retryCount = 0) {
|
|
835
|
+
async delegateToWorker(task, context, workingDir, retryCount = 0, timeoutMs) {
|
|
703
836
|
const maxRetries = 2;
|
|
837
|
+
// Default 30 min, max 60 min
|
|
838
|
+
const workerTimeout = Math.min(timeoutMs || 30 * 60 * 1000, 60 * 60 * 1000);
|
|
704
839
|
const id = `worker_${Date.now()}_${++this.jobCounter}`;
|
|
705
840
|
const cwd = workingDir || this.workspaceDir;
|
|
706
841
|
// Search for relevant memories to inject into worker prompt
|
|
@@ -757,13 +892,16 @@ If you need to share data with other workers or signal completion:
|
|
|
757
892
|
[MESSAGE] to=worker_xyz | content=Please review the changes I made
|
|
758
893
|
This enables parallel workers to coordinate.`;
|
|
759
894
|
console.log(`[ORCHESTRATOR] Delegating to worker ${id}: ${task.slice(0, 80)}...`);
|
|
895
|
+
// Estimate task duration based on content
|
|
896
|
+
const estimatedTime = this.estimateTaskDuration(task);
|
|
760
897
|
return new Promise((resolve) => {
|
|
761
898
|
const job = {
|
|
762
899
|
id,
|
|
763
900
|
task: task.slice(0, 200),
|
|
764
901
|
status: 'running',
|
|
765
902
|
startTime: Date.now(),
|
|
766
|
-
output: ''
|
|
903
|
+
output: '',
|
|
904
|
+
estimatedTime
|
|
767
905
|
};
|
|
768
906
|
// Escape single quotes in prompt for shell safety
|
|
769
907
|
// Replace ' with '\'' (end quote, escaped quote, start quote)
|
|
@@ -777,8 +915,8 @@ This enables parallel workers to coordinate.`;
|
|
|
777
915
|
});
|
|
778
916
|
job.process = child;
|
|
779
917
|
this.jobs.set(id, job);
|
|
780
|
-
//
|
|
781
|
-
const
|
|
918
|
+
// Configurable timeout (default 30 min, max 60 min)
|
|
919
|
+
const timeoutMinutes = Math.round(workerTimeout / 60000);
|
|
782
920
|
const timeout = setTimeout(() => {
|
|
783
921
|
if (job.status === 'running') {
|
|
784
922
|
job.status = 'timeout';
|
|
@@ -792,14 +930,16 @@ This enables parallel workers to coordinate.`;
|
|
|
792
930
|
}
|
|
793
931
|
}, 5000);
|
|
794
932
|
const partialOutput = job.output.trim();
|
|
933
|
+
// Check for log file that worker should have created
|
|
934
|
+
const logFile = `/tmp/worker-${id}-log.txt`;
|
|
795
935
|
resolve({
|
|
796
936
|
success: false,
|
|
797
937
|
output: partialOutput
|
|
798
|
-
? `Worker timed out after
|
|
799
|
-
:
|
|
938
|
+
? `Worker timed out after ${timeoutMinutes} minutes. Check ${logFile} for full logs. Partial findings:\n${partialOutput.slice(-3000)}`
|
|
939
|
+
: `Worker timed out after ${timeoutMinutes} minutes with no output. Check ${logFile} for any saved progress.`
|
|
800
940
|
});
|
|
801
941
|
}
|
|
802
|
-
},
|
|
942
|
+
}, workerTimeout);
|
|
803
943
|
child.stdout?.on('data', (data) => {
|
|
804
944
|
job.output += data.toString();
|
|
805
945
|
});
|
|
@@ -1141,6 +1281,11 @@ This enables parallel workers to coordinate.`;
|
|
|
1141
1281
|
* Shutdown cleanly
|
|
1142
1282
|
*/
|
|
1143
1283
|
async shutdown() {
|
|
1284
|
+
// Stop worker status broadcasting
|
|
1285
|
+
if (this.workerStatusInterval) {
|
|
1286
|
+
clearInterval(this.workerStatusInterval);
|
|
1287
|
+
this.workerStatusInterval = null;
|
|
1288
|
+
}
|
|
1144
1289
|
this.scheduler.shutdown();
|
|
1145
1290
|
if (this.memory instanceof PostgresMemoryStore) {
|
|
1146
1291
|
await this.memory.close();
|
package/dist/tools/worker.d.ts
CHANGED
|
@@ -33,4 +33,18 @@ export declare class WorkerTools {
|
|
|
33
33
|
* Clean up old jobs
|
|
34
34
|
*/
|
|
35
35
|
cleanupWorkers(): Promise<ToolResult>;
|
|
36
|
+
/**
|
|
37
|
+
* Get all running workers for progress tracking
|
|
38
|
+
*/
|
|
39
|
+
getRunningWorkers(): Array<{
|
|
40
|
+
id: string;
|
|
41
|
+
task: string;
|
|
42
|
+
status: string;
|
|
43
|
+
startTime: number;
|
|
44
|
+
estimatedTime: number;
|
|
45
|
+
}>;
|
|
46
|
+
/**
|
|
47
|
+
* Estimate task duration based on content
|
|
48
|
+
*/
|
|
49
|
+
private estimateTaskDuration;
|
|
36
50
|
}
|
package/dist/tools/worker.js
CHANGED
|
@@ -146,4 +146,37 @@ export class WorkerTools {
|
|
|
146
146
|
output: `Cleaned up ${cleaned} old job(s).`
|
|
147
147
|
};
|
|
148
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* Get all running workers for progress tracking
|
|
151
|
+
*/
|
|
152
|
+
getRunningWorkers() {
|
|
153
|
+
const jobs = this.manager.list({ status: 'running' });
|
|
154
|
+
return jobs.map(job => ({
|
|
155
|
+
id: job.id,
|
|
156
|
+
task: job.task,
|
|
157
|
+
status: job.status,
|
|
158
|
+
startTime: job.started ? new Date(job.started).getTime() : Date.now(),
|
|
159
|
+
// Estimate based on task content
|
|
160
|
+
estimatedTime: this.estimateTaskDuration(job.task)
|
|
161
|
+
}));
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Estimate task duration based on content
|
|
165
|
+
*/
|
|
166
|
+
estimateTaskDuration(task) {
|
|
167
|
+
const lower = task.toLowerCase();
|
|
168
|
+
if (lower.includes('refactor') || lower.includes('implement') ||
|
|
169
|
+
lower.includes('create') || lower.includes('build')) {
|
|
170
|
+
return 180; // 3 minutes
|
|
171
|
+
}
|
|
172
|
+
if (lower.includes('fix') || lower.includes('update') ||
|
|
173
|
+
lower.includes('modify') || lower.includes('add')) {
|
|
174
|
+
return 90; // 90 seconds
|
|
175
|
+
}
|
|
176
|
+
if (lower.includes('find') || lower.includes('search') ||
|
|
177
|
+
lower.includes('read') || lower.includes('list')) {
|
|
178
|
+
return 45; // 45 seconds
|
|
179
|
+
}
|
|
180
|
+
return 60; // default 1 minute
|
|
181
|
+
}
|
|
149
182
|
}
|
package/dist/websocket.d.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* - Streams responses as they're generated
|
|
7
7
|
* - Supports interruption and progress updates
|
|
8
8
|
*/
|
|
9
|
+
import type { WorkerStatus } from './orchestrator.js';
|
|
9
10
|
export type MessageHandler = (message: WebSocketMessage) => Promise<void>;
|
|
10
11
|
export type StreamHandler = (chunk: string) => void;
|
|
11
12
|
export interface WebSocketMessage {
|
|
@@ -57,6 +58,10 @@ export declare class AgentWebSocket {
|
|
|
57
58
|
* Send typing indicator
|
|
58
59
|
*/
|
|
59
60
|
sendTyping(isTyping: boolean): void;
|
|
61
|
+
/**
|
|
62
|
+
* Send workers status update for progress bars
|
|
63
|
+
*/
|
|
64
|
+
sendWorkersUpdate(workers: WorkerStatus[]): void;
|
|
60
65
|
/**
|
|
61
66
|
* Check if connected
|
|
62
67
|
*/
|
package/dist/websocket.js
CHANGED
|
@@ -159,6 +159,15 @@ export class AgentWebSocket {
|
|
|
159
159
|
isTyping
|
|
160
160
|
});
|
|
161
161
|
}
|
|
162
|
+
/**
|
|
163
|
+
* Send workers status update for progress bars
|
|
164
|
+
*/
|
|
165
|
+
sendWorkersUpdate(workers) {
|
|
166
|
+
this.sendToServer({
|
|
167
|
+
type: 'workers_update',
|
|
168
|
+
workers
|
|
169
|
+
});
|
|
170
|
+
}
|
|
162
171
|
/**
|
|
163
172
|
* Check if connected
|
|
164
173
|
*/
|