code-graph-context 2.5.1 → 2.5.3
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.
|
@@ -23,6 +23,12 @@ const CLAIM_TASK_BY_ID_QUERY = `
|
|
|
23
23
|
// Only claim if all dependencies are complete
|
|
24
24
|
WHERE incompleteDeps = 0
|
|
25
25
|
|
|
26
|
+
// Acquire exclusive lock to prevent race conditions
|
|
27
|
+
CALL apoc.lock.nodes([t])
|
|
28
|
+
|
|
29
|
+
// Double-check status after acquiring lock
|
|
30
|
+
WITH t WHERE t.status IN ['available', 'blocked']
|
|
31
|
+
|
|
26
32
|
// Atomic claim
|
|
27
33
|
SET t.status = 'claimed',
|
|
28
34
|
t.claimedBy = $agentId,
|
|
@@ -57,11 +63,12 @@ const CLAIM_TASK_BY_ID_QUERY = `
|
|
|
57
63
|
`;
|
|
58
64
|
/**
|
|
59
65
|
* Query to claim the highest priority available task matching criteria
|
|
66
|
+
* Uses APOC locking to prevent race conditions between parallel workers
|
|
60
67
|
*/
|
|
61
68
|
const CLAIM_NEXT_TASK_QUERY = `
|
|
62
|
-
// Find available tasks
|
|
69
|
+
// Find available or blocked tasks (blocked tasks may have deps completed now)
|
|
63
70
|
MATCH (t:SwarmTask {projectId: $projectId, swarmId: $swarmId})
|
|
64
|
-
WHERE t.status
|
|
71
|
+
WHERE t.status IN ['available', 'blocked']
|
|
65
72
|
AND ($types IS NULL OR size($types) = 0 OR t.type IN $types)
|
|
66
73
|
AND ($minPriority IS NULL OR t.priorityScore >= $minPriority)
|
|
67
74
|
|
|
@@ -75,6 +82,12 @@ const CLAIM_NEXT_TASK_QUERY = `
|
|
|
75
82
|
ORDER BY t.priorityScore DESC, t.createdAt ASC
|
|
76
83
|
LIMIT 1
|
|
77
84
|
|
|
85
|
+
// Acquire exclusive lock to prevent race conditions
|
|
86
|
+
CALL apoc.lock.nodes([t])
|
|
87
|
+
|
|
88
|
+
// Double-check status after acquiring lock (another worker may have claimed it)
|
|
89
|
+
WITH t WHERE t.status IN ['available', 'blocked']
|
|
90
|
+
|
|
78
91
|
// Atomic claim
|
|
79
92
|
SET t.status = 'claimed',
|
|
80
93
|
t.claimedBy = $agentId,
|
|
@@ -11,7 +11,7 @@ import { createErrorResponse, createSuccessResponse, resolveProjectIdOrError, de
|
|
|
11
11
|
*/
|
|
12
12
|
const COMPLETE_TASK_QUERY = `
|
|
13
13
|
MATCH (t:SwarmTask {id: $taskId, projectId: $projectId})
|
|
14
|
-
WHERE t.status
|
|
14
|
+
WHERE t.status IN ['in_progress', 'claimed'] AND t.claimedBy = $agentId
|
|
15
15
|
|
|
16
16
|
SET t.status = 'completed',
|
|
17
17
|
t.completedAt = timestamp(),
|
|
@@ -29,17 +29,21 @@ const COMPLETE_TASK_QUERY = `
|
|
|
29
29
|
|
|
30
30
|
// Check if waiting tasks now have all dependencies completed
|
|
31
31
|
WITH t, collect(waiting) as waitingTasks
|
|
32
|
+
|
|
33
|
+
// Unblock tasks that have all dependencies met
|
|
32
34
|
UNWIND (CASE WHEN size(waitingTasks) = 0 THEN [null] ELSE waitingTasks END) as waiting
|
|
33
35
|
OPTIONAL MATCH (waiting)-[:DEPENDS_ON]->(otherDep:SwarmTask)
|
|
34
36
|
WHERE otherDep.status <> 'completed' AND otherDep.id <> t.id
|
|
35
37
|
WITH t, waiting, count(otherDep) as remainingDeps
|
|
36
|
-
WHERE waiting IS NOT NULL AND remainingDeps = 0
|
|
37
38
|
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
// Update status for tasks with no remaining deps (but don't filter out the row)
|
|
40
|
+
FOREACH (_ IN CASE WHEN waiting IS NOT NULL AND remainingDeps = 0 THEN [1] ELSE [] END |
|
|
41
|
+
SET waiting.status = 'available', waiting.updatedAt = timestamp()
|
|
42
|
+
)
|
|
41
43
|
|
|
42
|
-
WITH t,
|
|
44
|
+
WITH t, CASE WHEN waiting IS NOT NULL AND remainingDeps = 0 THEN waiting.id ELSE null END as unblockedId
|
|
45
|
+
WITH t, collect(unblockedId) as allUnblockedIds
|
|
46
|
+
WITH t, [id IN allUnblockedIds WHERE id IS NOT NULL] as unblockedTaskIds
|
|
43
47
|
|
|
44
48
|
RETURN t.id as id,
|
|
45
49
|
t.title as title,
|
|
@@ -112,16 +116,21 @@ const APPROVE_TASK_QUERY = `
|
|
|
112
116
|
|
|
113
117
|
// Check if waiting tasks now have all dependencies completed
|
|
114
118
|
WITH t, collect(waiting) as waitingTasks
|
|
119
|
+
|
|
120
|
+
// Unblock tasks that have all dependencies met
|
|
115
121
|
UNWIND (CASE WHEN size(waitingTasks) = 0 THEN [null] ELSE waitingTasks END) as waiting
|
|
116
122
|
OPTIONAL MATCH (waiting)-[:DEPENDS_ON]->(otherDep:SwarmTask)
|
|
117
123
|
WHERE otherDep.status <> 'completed' AND otherDep.id <> t.id
|
|
118
124
|
WITH t, waiting, count(otherDep) as remainingDeps
|
|
119
|
-
WHERE waiting IS NOT NULL AND remainingDeps = 0
|
|
120
125
|
|
|
121
|
-
|
|
122
|
-
|
|
126
|
+
// Update status for tasks with no remaining deps (but don't filter out the row)
|
|
127
|
+
FOREACH (_ IN CASE WHEN waiting IS NOT NULL AND remainingDeps = 0 THEN [1] ELSE [] END |
|
|
128
|
+
SET waiting.status = 'available', waiting.updatedAt = timestamp()
|
|
129
|
+
)
|
|
123
130
|
|
|
124
|
-
WITH t,
|
|
131
|
+
WITH t, CASE WHEN waiting IS NOT NULL AND remainingDeps = 0 THEN waiting.id ELSE null END as unblockedId
|
|
132
|
+
WITH t, collect(unblockedId) as allUnblockedIds
|
|
133
|
+
WITH t, [id IN allUnblockedIds WHERE id IS NOT NULL] as unblockedTaskIds
|
|
125
134
|
|
|
126
135
|
RETURN t.id as id,
|
|
127
136
|
t.title as title,
|
|
@@ -352,33 +352,129 @@ export const createSwarmOrchestrateTool = (server) => {
|
|
|
352
352
|
*/
|
|
353
353
|
function generateWorkerInstructions(swarmId, projectId, maxAgents, taskCount) {
|
|
354
354
|
const recommendedAgents = Math.min(maxAgents, Math.ceil(taskCount / 2), taskCount);
|
|
355
|
-
|
|
356
|
-
|
|
355
|
+
// Generate unique agent IDs for each worker
|
|
356
|
+
const agentIds = Array.from({ length: recommendedAgents }, (_, i) => `${swarmId}_worker_${i + 1}`);
|
|
357
|
+
const workerPrompt = `You are a swarm worker agent.
|
|
358
|
+
- Agent ID: {AGENT_ID}
|
|
359
|
+
- Swarm ID: ${swarmId}
|
|
360
|
+
- Project: ${projectId}
|
|
357
361
|
|
|
358
|
-
|
|
362
|
+
## CRITICAL RULES
|
|
363
|
+
1. NEVER fabricate node IDs - get them from graph tool responses
|
|
364
|
+
2. ALWAYS use the blackboard task queue (swarm_claim_task, swarm_complete_task)
|
|
365
|
+
3. Use graph tools (traverse_from_node, search_codebase) to understand context
|
|
366
|
+
4. Exit when swarm_claim_task returns "no_tasks"
|
|
359
367
|
|
|
360
|
-
|
|
361
|
-
Task({
|
|
362
|
-
subagent_type: "general-purpose",
|
|
363
|
-
prompt: "You are a swarm worker for swarm ${swarmId}. Project: ${projectId}.
|
|
368
|
+
## WORKFLOW - Follow these steps exactly:
|
|
364
369
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
+
### Step 1: Claim a task from the blackboard
|
|
371
|
+
swarm_claim_task({
|
|
372
|
+
projectId: "${projectId}",
|
|
373
|
+
swarmId: "${swarmId}",
|
|
374
|
+
agentId: "{AGENT_ID}"
|
|
375
|
+
})
|
|
376
|
+
// If returns "no_tasks" → exit, swarm is complete
|
|
377
|
+
// Otherwise you now own the returned task
|
|
370
378
|
|
|
371
|
-
|
|
372
|
-
|
|
379
|
+
### Step 2: Start working and check for conflicts
|
|
380
|
+
swarm_claim_task({
|
|
381
|
+
projectId: "${projectId}",
|
|
382
|
+
swarmId: "${swarmId}",
|
|
383
|
+
agentId: "{AGENT_ID}",
|
|
384
|
+
taskId: "<TASK_ID_FROM_STEP_1>",
|
|
385
|
+
action: "start"
|
|
373
386
|
})
|
|
387
|
+
|
|
388
|
+
// Check if another agent is working on related code
|
|
389
|
+
swarm_sense({
|
|
390
|
+
projectId: "${projectId}",
|
|
391
|
+
swarmId: "${swarmId}",
|
|
392
|
+
types: ["modifying", "warning"],
|
|
393
|
+
excludeAgentId: "{AGENT_ID}"
|
|
394
|
+
})
|
|
395
|
+
|
|
396
|
+
### Step 3: Understand the code context (USE GRAPH TOOLS!)
|
|
397
|
+
// Use traverse_from_node to see relationships and callers
|
|
398
|
+
traverse_from_node({
|
|
399
|
+
projectId: "${projectId}",
|
|
400
|
+
filePath: "<TARGET_FILE_FROM_TASK>",
|
|
401
|
+
maxDepth: 2,
|
|
402
|
+
includeCode: true
|
|
403
|
+
})
|
|
404
|
+
|
|
405
|
+
// Or search for related code
|
|
406
|
+
search_codebase({
|
|
407
|
+
projectId: "${projectId}",
|
|
408
|
+
query: "<WHAT_YOU_NEED_TO_UNDERSTAND>"
|
|
409
|
+
})
|
|
410
|
+
|
|
411
|
+
### Step 4: Do the work
|
|
412
|
+
- Use Read tool for full source code of files to modify
|
|
413
|
+
- Use Edit tool to make changes
|
|
414
|
+
- Mark nodes you're modifying:
|
|
415
|
+
swarm_pheromone({
|
|
416
|
+
projectId: "${projectId}",
|
|
417
|
+
nodeId: "<NODE_ID_FROM_GRAPH>",
|
|
418
|
+
type: "modifying",
|
|
419
|
+
agentId: "{AGENT_ID}",
|
|
420
|
+
swarmId: "${swarmId}"
|
|
421
|
+
})
|
|
422
|
+
|
|
423
|
+
### Step 5: Complete the task via blackboard
|
|
424
|
+
swarm_pheromone({
|
|
425
|
+
projectId: "${projectId}",
|
|
426
|
+
nodeId: "<NODE_ID>",
|
|
427
|
+
type: "completed",
|
|
428
|
+
agentId: "{AGENT_ID}",
|
|
429
|
+
swarmId: "${swarmId}",
|
|
430
|
+
data: { summary: "<WHAT_YOU_DID>" }
|
|
431
|
+
})
|
|
432
|
+
|
|
433
|
+
swarm_complete_task({
|
|
434
|
+
projectId: "${projectId}",
|
|
435
|
+
taskId: "<TASK_ID>",
|
|
436
|
+
agentId: "{AGENT_ID}",
|
|
437
|
+
action: "complete",
|
|
438
|
+
summary: "<DESCRIBE_WHAT_YOU_DID>",
|
|
439
|
+
filesChanged: ["<LIST_OF_FILES_YOU_MODIFIED>"]
|
|
440
|
+
})
|
|
441
|
+
|
|
442
|
+
### Step 6: Loop back to Step 1
|
|
443
|
+
Claim the next available task. Continue until no tasks remain.
|
|
444
|
+
|
|
445
|
+
## IF YOU GET STUCK
|
|
446
|
+
swarm_complete_task({
|
|
447
|
+
projectId: "${projectId}",
|
|
448
|
+
taskId: "<TASK_ID>",
|
|
449
|
+
agentId: "{AGENT_ID}",
|
|
450
|
+
action: "fail",
|
|
451
|
+
reason: "<WHY_YOU_ARE_STUCK>",
|
|
452
|
+
retryable: true
|
|
453
|
+
})
|
|
454
|
+
Then claim another task.`;
|
|
455
|
+
const taskCalls = agentIds.map(agentId => {
|
|
456
|
+
const prompt = workerPrompt.replace(/\{AGENT_ID\}/g, agentId);
|
|
457
|
+
return `Task({
|
|
458
|
+
subagent_type: "general-purpose",
|
|
459
|
+
run_in_background: false,
|
|
460
|
+
prompt: \`${prompt}\`
|
|
461
|
+
})`;
|
|
462
|
+
}).join('\n\n');
|
|
463
|
+
return `
|
|
464
|
+
## Worker Agent Instructions
|
|
465
|
+
|
|
466
|
+
Spawn ${recommendedAgents} worker agent(s) IN PARALLEL (all Task calls in ONE message):
|
|
467
|
+
|
|
468
|
+
\`\`\`javascript
|
|
469
|
+
${taskCalls}
|
|
374
470
|
\`\`\`
|
|
375
471
|
|
|
376
|
-
|
|
472
|
+
**CRITICAL:** Include ALL ${recommendedAgents} Task calls in a single message to run them in parallel.
|
|
377
473
|
|
|
378
474
|
## Monitoring Progress
|
|
379
475
|
|
|
380
476
|
Check swarm progress:
|
|
381
|
-
\`\`\`
|
|
477
|
+
\`\`\`javascript
|
|
382
478
|
swarm_get_tasks({
|
|
383
479
|
projectId: "${projectId}",
|
|
384
480
|
swarmId: "${swarmId}",
|
|
@@ -386,9 +482,9 @@ swarm_get_tasks({
|
|
|
386
482
|
})
|
|
387
483
|
\`\`\`
|
|
388
484
|
|
|
389
|
-
## Cleanup (after
|
|
485
|
+
## Cleanup (after all workers complete)
|
|
390
486
|
|
|
391
|
-
\`\`\`
|
|
487
|
+
\`\`\`javascript
|
|
392
488
|
swarm_cleanup({
|
|
393
489
|
projectId: "${projectId}",
|
|
394
490
|
swarmId: "${swarmId}"
|
package/package.json
CHANGED