@elizaos/plugin-agent-orchestrator 0.3.9 → 0.3.10
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/actions/coding-task-handlers.d.ts.map +1 -1
- package/dist/api/hook-routes.d.ts +3 -2
- package/dist/api/hook-routes.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +383 -91
- package/dist/index.js.map +11 -11
- package/dist/services/pty-service.d.ts.map +1 -1
- package/dist/services/pty-session-io.d.ts.map +1 -1
- package/dist/services/swarm-coordinator-prompts.d.ts +23 -5
- package/dist/services/swarm-coordinator-prompts.d.ts.map +1 -1
- package/dist/services/swarm-coordinator.d.ts +51 -1
- package/dist/services/swarm-coordinator.d.ts.map +1 -1
- package/dist/services/swarm-decision-loop.d.ts.map +1 -1
- package/dist/services/swarm-idle-watchdog.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -99,7 +99,35 @@ var init_ansi_utils = __esm(() => {
|
|
|
99
99
|
});
|
|
100
100
|
|
|
101
101
|
// src/services/swarm-coordinator-prompts.ts
|
|
102
|
-
function
|
|
102
|
+
function buildSiblingSection(siblings) {
|
|
103
|
+
if (!siblings || siblings.length === 0)
|
|
104
|
+
return "";
|
|
105
|
+
return `
|
|
106
|
+
Other agents in this swarm:
|
|
107
|
+
` + siblings.map((s) => ` - [${s.status}] "${s.label}" (${s.agentType}): ${s.originalTask}`).join(`
|
|
108
|
+
`) + `
|
|
109
|
+
Use this context when the agent asks creative or architectural questions — ` + `your answer should be consistent with what sibling agents are working on.
|
|
110
|
+
`;
|
|
111
|
+
}
|
|
112
|
+
function buildSharedDecisionsSection(decisions) {
|
|
113
|
+
if (!decisions || decisions.length === 0)
|
|
114
|
+
return "";
|
|
115
|
+
return `
|
|
116
|
+
Key decisions made by other agents in this swarm:
|
|
117
|
+
` + decisions.slice(-10).map((d) => ` - [${d.agentLabel}] ${d.summary}`).join(`
|
|
118
|
+
`) + `
|
|
119
|
+
Align with these decisions for consistency — don't contradict them unless the task requires it.
|
|
120
|
+
`;
|
|
121
|
+
}
|
|
122
|
+
function buildSwarmContextSection(swarmContext) {
|
|
123
|
+
if (!swarmContext)
|
|
124
|
+
return "";
|
|
125
|
+
return `
|
|
126
|
+
Project context (from planning phase):
|
|
127
|
+
${swarmContext}
|
|
128
|
+
`;
|
|
129
|
+
}
|
|
130
|
+
function buildCoordinationPrompt(taskCtx, promptText, recentOutput, decisionHistory, siblingTasks, sharedDecisions, swarmContext) {
|
|
103
131
|
const historySection = decisionHistory.length > 0 ? `
|
|
104
132
|
Previous decisions for this session:
|
|
105
133
|
${decisionHistory.slice(-5).map((d, i) => ` ${i + 1}. [${d.event}] prompt="${d.promptText}" → ${d.action}${d.response ? ` ("${d.response}")` : ""} — ${d.reasoning}`).join(`
|
|
@@ -110,7 +138,7 @@ ${decisionHistory.slice(-5).map((d, i) => ` ${i + 1}. [${d.event}] prompt="${d.
|
|
|
110
138
|
` + `Original task: "${taskCtx.originalTask}"
|
|
111
139
|
` + `Working directory: ${taskCtx.workdir}
|
|
112
140
|
` + `Repository: ${taskCtx.repo ?? "none (scratch directory)"}
|
|
113
|
-
` + historySection + `
|
|
141
|
+
` + buildSwarmContextSection(swarmContext) + buildSiblingSection(siblingTasks) + buildSharedDecisionsSection(sharedDecisions) + historySection + `
|
|
114
142
|
Recent terminal output (last 50 lines):
|
|
115
143
|
` + `---
|
|
116
144
|
${recentOutput.slice(-3000)}
|
|
@@ -139,11 +167,12 @@ ${recentOutput.slice(-3000)}
|
|
|
139
167
|
` + `- Only use "complete" if the agent confirmed it verified ALL test plan items after creating the PR.
|
|
140
168
|
` + `- If the agent is asking for information that was NOT provided in the original task ` + `(e.g. which repository to use, project requirements, credentials), ESCALATE. ` + `The coordinator does not have this information — the human must provide it.
|
|
141
169
|
` + `- When in doubt, escalate — it's better to ask the human than to make a wrong choice.
|
|
170
|
+
` + `- If the agent's output reveals a significant decision that sibling agents should know about ` + `(e.g. chose a library, designed an API shape, picked a UI pattern, established a writing style, ` + `narrowed a research scope, made any choice that affects the shared project), ` + `include "keyDecision" with a brief one-line summary. Skip this for routine tool approvals.
|
|
142
171
|
|
|
143
172
|
` + `Respond with ONLY a JSON object:
|
|
144
|
-
` + `{"action": "respond|complete|escalate|ignore", "response": "...", "useKeys": false, "keys": [], "reasoning": "..."}`;
|
|
173
|
+
` + `{"action": "respond|complete|escalate|ignore", "response": "...", "useKeys": false, "keys": [], "reasoning": "...", "keyDecision": "..."}`;
|
|
145
174
|
}
|
|
146
|
-
function buildIdleCheckPrompt(taskCtx, recentOutput, idleMinutes, idleCheckNumber, maxIdleChecks, decisionHistory) {
|
|
175
|
+
function buildIdleCheckPrompt(taskCtx, recentOutput, idleMinutes, idleCheckNumber, maxIdleChecks, decisionHistory, siblingTasks, sharedDecisions, swarmContext) {
|
|
147
176
|
const historySection = decisionHistory.length > 0 ? `
|
|
148
177
|
Previous decisions for this session:
|
|
149
178
|
${decisionHistory.slice(-5).map((d, i) => ` ${i + 1}. [${d.event}] prompt="${d.promptText}" → ${d.action}${d.response ? ` ("${d.response}")` : ""} — ${d.reasoning}`).join(`
|
|
@@ -155,7 +184,7 @@ ${decisionHistory.slice(-5).map((d, i) => ` ${i + 1}. [${d.event}] prompt="${d.
|
|
|
155
184
|
` + `Working directory: ${taskCtx.workdir}
|
|
156
185
|
` + `Repository: ${taskCtx.repo ?? "none (scratch directory)"}
|
|
157
186
|
` + `Idle check: ${idleCheckNumber} of ${maxIdleChecks} (session will be force-escalated after ${maxIdleChecks})
|
|
158
|
-
` + historySection + `
|
|
187
|
+
` + buildSwarmContextSection(swarmContext) + buildSiblingSection(siblingTasks) + buildSharedDecisionsSection(sharedDecisions) + historySection + `
|
|
159
188
|
Recent terminal output (last 50 lines):
|
|
160
189
|
` + `---
|
|
161
190
|
${recentOutput.slice(-3000)}
|
|
@@ -179,11 +208,12 @@ ${recentOutput.slice(-3000)}
|
|
|
179
208
|
` + `- If the output shows an error or the agent seems stuck in a loop, escalate.
|
|
180
209
|
` + `- If the agent is clearly mid-operation (build output, test runner, git operations), use "ignore".
|
|
181
210
|
` + `- On check ${idleCheckNumber} of ${maxIdleChecks} — if unsure, lean toward "respond" with a nudge rather than "complete".
|
|
211
|
+
` + `- If the agent's output reveals a significant creative or architectural decision, ` + `include "keyDecision" with a brief one-line summary.
|
|
182
212
|
|
|
183
213
|
` + `Respond with ONLY a JSON object:
|
|
184
|
-
` + `{"action": "respond|complete|escalate|ignore", "response": "...", "useKeys": false, "keys": [], "reasoning": "..."}`;
|
|
214
|
+
` + `{"action": "respond|complete|escalate|ignore", "response": "...", "useKeys": false, "keys": [], "reasoning": "...", "keyDecision": "..."}`;
|
|
185
215
|
}
|
|
186
|
-
function buildTurnCompletePrompt(taskCtx, turnOutput, decisionHistory) {
|
|
216
|
+
function buildTurnCompletePrompt(taskCtx, turnOutput, decisionHistory, siblingTasks, sharedDecisions, swarmContext) {
|
|
187
217
|
const historySection = decisionHistory.length > 0 ? `
|
|
188
218
|
Previous decisions for this session:
|
|
189
219
|
${decisionHistory.slice(-5).map((d, i) => ` ${i + 1}. [${d.event}] prompt="${d.promptText}" → ${d.action}${d.response ? ` ("${d.response}")` : ""} — ${d.reasoning}`).join(`
|
|
@@ -194,7 +224,7 @@ ${decisionHistory.slice(-5).map((d, i) => ` ${i + 1}. [${d.event}] prompt="${d.
|
|
|
194
224
|
` + `Original task: "${taskCtx.originalTask}"
|
|
195
225
|
` + `Working directory: ${taskCtx.workdir}
|
|
196
226
|
` + `Repository: ${taskCtx.repo ?? "none (scratch directory)"}
|
|
197
|
-
` + historySection + `
|
|
227
|
+
` + buildSwarmContextSection(swarmContext) + buildSiblingSection(siblingTasks) + buildSharedDecisionsSection(sharedDecisions) + historySection + `
|
|
198
228
|
Output from this turn:
|
|
199
229
|
` + `---
|
|
200
230
|
${turnOutput.slice(-3000)}
|
|
@@ -227,11 +257,12 @@ ${turnOutput.slice(-3000)}
|
|
|
227
257
|
` + `- Keep follow-up instructions concise and specific.
|
|
228
258
|
` + `- When asking agents to verify work, prefer CLI tools (gh, curl, cat, git diff, etc.) over ` + `browser automation. Browser tools may not be available in headless environments and can cause delays.
|
|
229
259
|
` + `- Default to "respond" — only use "complete" when you're certain ALL work is done.
|
|
260
|
+
` + `- If the agent's output reveals a significant decision that sibling agents should know about ` + `(e.g. chose a library, designed an API shape, picked a UI pattern, established a writing style, ` + `narrowed a research scope, made any choice that affects the shared project), ` + `include "keyDecision" with a brief one-line summary. Skip this for routine tool approvals.
|
|
230
261
|
|
|
231
262
|
` + `Respond with ONLY a JSON object:
|
|
232
|
-
` + `{"action": "respond|complete|escalate|ignore", "response": "...", "useKeys": false, "keys": [], "reasoning": "..."}`;
|
|
263
|
+
` + `{"action": "respond|complete|escalate|ignore", "response": "...", "useKeys": false, "keys": [], "reasoning": "...", "keyDecision": "..."}`;
|
|
233
264
|
}
|
|
234
|
-
function buildBlockedEventMessage(taskCtx, promptText, recentOutput, decisionHistory) {
|
|
265
|
+
function buildBlockedEventMessage(taskCtx, promptText, recentOutput, decisionHistory, siblingTasks, sharedDecisions, swarmContext) {
|
|
235
266
|
const historySection = decisionHistory.length > 0 ? `
|
|
236
267
|
Previous decisions:
|
|
237
268
|
${decisionHistory.slice(-5).map((d, i) => ` ${i + 1}. [${d.event}] "${d.promptText}" → ${d.action}${d.response ? ` ("${d.response}")` : ""} — ${d.reasoning}`).join(`
|
|
@@ -242,7 +273,7 @@ ${decisionHistory.slice(-5).map((d, i) => ` ${i + 1}. [${d.event}] "${d.promptT
|
|
|
242
273
|
` + `Task: "${taskCtx.originalTask}"
|
|
243
274
|
` + `Workdir: ${taskCtx.workdir}
|
|
244
275
|
` + `Repo: ${taskCtx.repo ?? "none (scratch directory)"}
|
|
245
|
-
` + historySection + `
|
|
276
|
+
` + buildSwarmContextSection(swarmContext) + buildSiblingSection(siblingTasks) + buildSharedDecisionsSection(sharedDecisions) + historySection + `
|
|
246
277
|
Recent terminal output:
|
|
247
278
|
---
|
|
248
279
|
${recentOutput.slice(-3000)}
|
|
@@ -263,11 +294,13 @@ ${recentOutput.slice(-3000)}
|
|
|
263
294
|
` + `- If a PR was just created, respond to review & verify test plan items before completing.
|
|
264
295
|
` + `- When in doubt, escalate.
|
|
265
296
|
|
|
297
|
+
` + `If the agent's output reveals a significant decision that sibling agents should know about, include "keyDecision" with a brief summary.
|
|
298
|
+
|
|
266
299
|
` + `Include a JSON action block at the end of your response:
|
|
267
|
-
` + "```json\n" + `{"action": "respond|complete|escalate|ignore", "response": "...", "useKeys": false, "keys": [], "reasoning": "..."}
|
|
300
|
+
` + "```json\n" + `{"action": "respond|complete|escalate|ignore", "response": "...", "useKeys": false, "keys": [], "reasoning": "...", "keyDecision": "..."}
|
|
268
301
|
` + "```";
|
|
269
302
|
}
|
|
270
|
-
function buildTurnCompleteEventMessage(taskCtx, turnOutput, decisionHistory) {
|
|
303
|
+
function buildTurnCompleteEventMessage(taskCtx, turnOutput, decisionHistory, siblingTasks, sharedDecisions, swarmContext) {
|
|
271
304
|
const historySection = decisionHistory.length > 0 ? `
|
|
272
305
|
Previous decisions:
|
|
273
306
|
${decisionHistory.slice(-5).map((d, i) => ` ${i + 1}. [${d.event}] "${d.promptText}" → ${d.action}${d.response ? ` ("${d.response}")` : ""} — ${d.reasoning}`).join(`
|
|
@@ -278,7 +311,7 @@ ${decisionHistory.slice(-5).map((d, i) => ` ${i + 1}. [${d.event}] "${d.promptT
|
|
|
278
311
|
` + `Task: "${taskCtx.originalTask}"
|
|
279
312
|
` + `Workdir: ${taskCtx.workdir}
|
|
280
313
|
` + `Repo: ${taskCtx.repo ?? "none (scratch directory)"}
|
|
281
|
-
` + historySection + `
|
|
314
|
+
` + buildSwarmContextSection(swarmContext) + buildSiblingSection(siblingTasks) + buildSharedDecisionsSection(sharedDecisions) + historySection + `
|
|
282
315
|
Turn output:
|
|
283
316
|
---
|
|
284
317
|
${turnOutput.slice(-3000)}
|
|
@@ -298,9 +331,10 @@ ${turnOutput.slice(-3000)}
|
|
|
298
331
|
` + `- If a PR was just created, respond to review & verify test plan items.
|
|
299
332
|
` + `- When asking agents to verify work, prefer CLI tools (gh, curl, cat, etc.) over browser automation.
|
|
300
333
|
` + `- Default to "respond" — only "complete" when certain ALL work is done.
|
|
334
|
+
` + `- If the agent's output reveals a significant creative or architectural decision, include "keyDecision" with a brief summary.
|
|
301
335
|
|
|
302
336
|
` + `Include a JSON action block at the end of your response:
|
|
303
|
-
` + "```json\n" + `{"action": "respond|complete|escalate|ignore", "response": "...", "useKeys": false, "keys": [], "reasoning": "..."}
|
|
337
|
+
` + "```json\n" + `{"action": "respond|complete|escalate|ignore", "response": "...", "useKeys": false, "keys": [], "reasoning": "...", "keyDecision": "..."}
|
|
304
338
|
` + "```";
|
|
305
339
|
}
|
|
306
340
|
function parseCoordinationResponse(llmOutput) {
|
|
@@ -326,6 +360,9 @@ function parseCoordinationResponse(llmOutput) {
|
|
|
326
360
|
return null;
|
|
327
361
|
}
|
|
328
362
|
}
|
|
363
|
+
if (typeof parsed.keyDecision === "string" && parsed.keyDecision.trim()) {
|
|
364
|
+
result.keyDecision = parsed.keyDecision.trim().slice(0, 240);
|
|
365
|
+
}
|
|
329
366
|
return result;
|
|
330
367
|
} catch {
|
|
331
368
|
return null;
|
|
@@ -502,6 +539,57 @@ function toDecisionHistory(taskCtx) {
|
|
|
502
539
|
reasoning: d.reasoning
|
|
503
540
|
}));
|
|
504
541
|
}
|
|
542
|
+
function collectSiblings(ctx, currentSessionId) {
|
|
543
|
+
const siblings = [];
|
|
544
|
+
for (const [sid, task] of ctx.tasks) {
|
|
545
|
+
if (sid === currentSessionId)
|
|
546
|
+
continue;
|
|
547
|
+
siblings.push({
|
|
548
|
+
label: task.label,
|
|
549
|
+
agentType: task.agentType,
|
|
550
|
+
originalTask: task.originalTask,
|
|
551
|
+
status: task.status
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
return siblings;
|
|
555
|
+
}
|
|
556
|
+
function enrichWithSharedDecisions(ctx, sessionId, response) {
|
|
557
|
+
const taskCtx = ctx.tasks.get(sessionId);
|
|
558
|
+
if (!taskCtx)
|
|
559
|
+
return { response };
|
|
560
|
+
const allDecisions = ctx.sharedDecisions;
|
|
561
|
+
const lastSeen = taskCtx.lastSeenDecisionIndex;
|
|
562
|
+
const snapshotEnd = allDecisions.length;
|
|
563
|
+
if (lastSeen >= snapshotEnd)
|
|
564
|
+
return { response };
|
|
565
|
+
if (response.length < 20) {
|
|
566
|
+
return { response };
|
|
567
|
+
}
|
|
568
|
+
const unseen = allDecisions.slice(lastSeen, snapshotEnd);
|
|
569
|
+
const contextBlock = unseen.map((d) => `[${d.agentLabel}] ${d.summary}`).join("; ");
|
|
570
|
+
return {
|
|
571
|
+
response: `${response}
|
|
572
|
+
|
|
573
|
+
(Context from other agents: ${contextBlock})`,
|
|
574
|
+
snapshotIndex: snapshotEnd
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
function commitSharedDecisionIndex(ctx, sessionId, snapshotIndex) {
|
|
578
|
+
const taskCtx = ctx.tasks.get(sessionId);
|
|
579
|
+
if (taskCtx) {
|
|
580
|
+
taskCtx.lastSeenDecisionIndex = snapshotIndex;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
function recordKeyDecision(ctx, agentLabel, decision) {
|
|
584
|
+
if (!decision.keyDecision)
|
|
585
|
+
return;
|
|
586
|
+
ctx.sharedDecisions.push({
|
|
587
|
+
agentLabel,
|
|
588
|
+
summary: decision.keyDecision,
|
|
589
|
+
timestamp: Date.now()
|
|
590
|
+
});
|
|
591
|
+
ctx.log(`Shared decision from "${agentLabel}": ${decision.keyDecision}`);
|
|
592
|
+
}
|
|
505
593
|
async function drainPendingTurnComplete(ctx, sessionId) {
|
|
506
594
|
if (!ctx.pendingTurnComplete.has(sessionId))
|
|
507
595
|
return;
|
|
@@ -542,8 +630,16 @@ function checkAllTasksComplete(ctx) {
|
|
|
542
630
|
return;
|
|
543
631
|
const terminalStates = new Set(["completed", "stopped", "error"]);
|
|
544
632
|
const allDone = tasks.every((t) => terminalStates.has(t.status));
|
|
545
|
-
if (!allDone)
|
|
633
|
+
if (!allDone) {
|
|
634
|
+
const statuses = tasks.map((t) => `${t.label}=${t.status}`).join(", ");
|
|
635
|
+
ctx.log(`checkAllTasksComplete: not all done yet — ${statuses}`);
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
if (ctx.swarmCompleteNotified) {
|
|
639
|
+
ctx.log("checkAllTasksComplete: already notified — skipping");
|
|
546
640
|
return;
|
|
641
|
+
}
|
|
642
|
+
ctx.swarmCompleteNotified = true;
|
|
547
643
|
const completed = tasks.filter((t) => t.status === "completed");
|
|
548
644
|
const stopped = tasks.filter((t) => t.status === "stopped");
|
|
549
645
|
const errored = tasks.filter((t) => t.status === "error");
|
|
@@ -557,7 +653,7 @@ function checkAllTasksComplete(ctx) {
|
|
|
557
653
|
if (errored.length > 0) {
|
|
558
654
|
parts.push(`${errored.length} errored`);
|
|
559
655
|
}
|
|
560
|
-
ctx.
|
|
656
|
+
ctx.log(`checkAllTasksComplete: all ${tasks.length} tasks terminal (${parts.join(", ")}) — firing swarm_complete`);
|
|
561
657
|
ctx.broadcast({
|
|
562
658
|
type: "swarm_complete",
|
|
563
659
|
sessionId: "",
|
|
@@ -569,6 +665,34 @@ function checkAllTasksComplete(ctx) {
|
|
|
569
665
|
errored: errored.length
|
|
570
666
|
}
|
|
571
667
|
});
|
|
668
|
+
const swarmCompleteCb = ctx.getSwarmCompleteCallback();
|
|
669
|
+
const sendFallbackSummary = () => {
|
|
670
|
+
ctx.sendChatMessage(`All ${tasks.length} coding agents finished (${parts.join(", ")}). Review their work when you're ready.`, "coding-agent");
|
|
671
|
+
};
|
|
672
|
+
if (swarmCompleteCb) {
|
|
673
|
+
ctx.log("checkAllTasksComplete: swarm complete callback is wired — calling synthesis");
|
|
674
|
+
const taskSummaries = tasks.map((t) => ({
|
|
675
|
+
sessionId: t.sessionId,
|
|
676
|
+
label: t.label,
|
|
677
|
+
agentType: t.agentType,
|
|
678
|
+
originalTask: t.originalTask,
|
|
679
|
+
status: t.status,
|
|
680
|
+
completionSummary: t.completionSummary ?? ""
|
|
681
|
+
}));
|
|
682
|
+
withTimeout(Promise.resolve().then(() => swarmCompleteCb({
|
|
683
|
+
tasks: taskSummaries,
|
|
684
|
+
total: tasks.length,
|
|
685
|
+
completed: completed.length,
|
|
686
|
+
stopped: stopped.length,
|
|
687
|
+
errored: errored.length
|
|
688
|
+
})), DECISION_CB_TIMEOUT_MS, "swarmCompleteCb").catch((err) => {
|
|
689
|
+
ctx.log(`Swarm complete callback failed: ${err} — falling back to generic summary`);
|
|
690
|
+
sendFallbackSummary();
|
|
691
|
+
});
|
|
692
|
+
} else {
|
|
693
|
+
ctx.log("checkAllTasksComplete: no synthesis callback — sending generic message");
|
|
694
|
+
sendFallbackSummary();
|
|
695
|
+
}
|
|
572
696
|
}
|
|
573
697
|
async function fetchRecentOutput(ctx, sessionId, lines = 50) {
|
|
574
698
|
if (!ctx.ptyService)
|
|
@@ -580,7 +704,7 @@ async function fetchRecentOutput(ctx, sessionId, lines = 50) {
|
|
|
580
704
|
}
|
|
581
705
|
}
|
|
582
706
|
async function makeCoordinationDecision(ctx, taskCtx, promptText, recentOutput) {
|
|
583
|
-
const prompt = buildCoordinationPrompt(toContextSummary(taskCtx), promptText, recentOutput, toDecisionHistory(taskCtx));
|
|
707
|
+
const prompt = buildCoordinationPrompt(toContextSummary(taskCtx), promptText, recentOutput, toDecisionHistory(taskCtx), collectSiblings(ctx, taskCtx.sessionId), ctx.sharedDecisions, ctx.getSwarmContext());
|
|
584
708
|
try {
|
|
585
709
|
const result = await ctx.runtime.useModel(ModelType3.TEXT_SMALL, {
|
|
586
710
|
prompt
|
|
@@ -599,7 +723,11 @@ async function executeDecision(ctx, sessionId, decision) {
|
|
|
599
723
|
if (decision.useKeys && decision.keys) {
|
|
600
724
|
await ctx.ptyService.sendKeysToSession(sessionId, decision.keys);
|
|
601
725
|
} else if (decision.response !== undefined) {
|
|
602
|
-
|
|
726
|
+
const { response: enriched, snapshotIndex } = enrichWithSharedDecisions(ctx, sessionId, decision.response);
|
|
727
|
+
await ctx.ptyService.sendToSession(sessionId, enriched);
|
|
728
|
+
if (snapshotIndex !== undefined) {
|
|
729
|
+
commitSharedDecisionIndex(ctx, sessionId, snapshotIndex);
|
|
730
|
+
}
|
|
603
731
|
}
|
|
604
732
|
break;
|
|
605
733
|
case "complete": {
|
|
@@ -618,6 +746,9 @@ async function executeDecision(ctx, sessionId, decision) {
|
|
|
618
746
|
const rawOutput = await ctx.ptyService.getSessionOutput(sessionId, 50);
|
|
619
747
|
summary = extractCompletionSummary(rawOutput);
|
|
620
748
|
} catch {}
|
|
749
|
+
if (taskCtx) {
|
|
750
|
+
taskCtx.completionSummary = summary || decision.reasoning || "";
|
|
751
|
+
}
|
|
621
752
|
ctx.sendChatMessage(summary ? `Finished "${taskCtx?.label ?? sessionId}".
|
|
622
753
|
|
|
623
754
|
${summary}` : `Finished "${taskCtx?.label ?? sessionId}".`, "coding-agent");
|
|
@@ -759,7 +890,7 @@ async function handleTurnComplete(ctx, sessionId, taskCtx, data) {
|
|
|
759
890
|
}
|
|
760
891
|
let decision = null;
|
|
761
892
|
const decisionFromPipeline = false;
|
|
762
|
-
const prompt = buildTurnCompletePrompt(toContextSummary(taskCtx), turnOutput, toDecisionHistory(taskCtx));
|
|
893
|
+
const prompt = buildTurnCompletePrompt(toContextSummary(taskCtx), turnOutput, toDecisionHistory(taskCtx), collectSiblings(ctx, sessionId), ctx.sharedDecisions, ctx.getSwarmContext());
|
|
763
894
|
try {
|
|
764
895
|
const result = await ctx.runtime.useModel(ModelType3.TEXT_SMALL, {
|
|
765
896
|
prompt
|
|
@@ -784,6 +915,7 @@ async function handleTurnComplete(ctx, sessionId, taskCtx, data) {
|
|
|
784
915
|
response: formatDecisionResponse(decision),
|
|
785
916
|
reasoning: decision.reasoning
|
|
786
917
|
});
|
|
918
|
+
recordKeyDecision(ctx, taskCtx.label, decision);
|
|
787
919
|
ctx.broadcast({
|
|
788
920
|
type: "turn_assessment",
|
|
789
921
|
sessionId,
|
|
@@ -834,7 +966,7 @@ async function handleAutonomousDecision(ctx, sessionId, taskCtx, promptText, rec
|
|
|
834
966
|
decision = await makeCoordinationDecision(ctx, taskCtx, promptText, output);
|
|
835
967
|
} else {
|
|
836
968
|
if (agentDecisionCb) {
|
|
837
|
-
const eventMessage = buildBlockedEventMessage(toContextSummary(taskCtx), promptText, output, toDecisionHistory(taskCtx));
|
|
969
|
+
const eventMessage = buildBlockedEventMessage(toContextSummary(taskCtx), promptText, output, toDecisionHistory(taskCtx), collectSiblings(ctx, sessionId), ctx.sharedDecisions, ctx.getSwarmContext());
|
|
838
970
|
try {
|
|
839
971
|
decision = await withTimeout(agentDecisionCb(eventMessage, sessionId, taskCtx), DECISION_CB_TIMEOUT_MS, "agentDecisionCb");
|
|
840
972
|
if (decision)
|
|
@@ -882,6 +1014,7 @@ async function handleAutonomousDecision(ctx, sessionId, taskCtx, promptText, rec
|
|
|
882
1014
|
response: formatDecisionResponse(decision),
|
|
883
1015
|
reasoning: decision.reasoning
|
|
884
1016
|
});
|
|
1017
|
+
recordKeyDecision(ctx, taskCtx.label, decision);
|
|
885
1018
|
taskCtx.autoResolvedCount = 0;
|
|
886
1019
|
ctx.broadcast({
|
|
887
1020
|
type: "coordination_decision",
|
|
@@ -934,7 +1067,7 @@ async function handleConfirmDecision(ctx, sessionId, taskCtx, promptText, recent
|
|
|
934
1067
|
decision = await makeCoordinationDecision(ctx, taskCtx, promptText, output);
|
|
935
1068
|
} else {
|
|
936
1069
|
if (agentDecisionCb) {
|
|
937
|
-
const eventMessage = buildBlockedEventMessage(toContextSummary(taskCtx), promptText, output, toDecisionHistory(taskCtx));
|
|
1070
|
+
const eventMessage = buildBlockedEventMessage(toContextSummary(taskCtx), promptText, output, toDecisionHistory(taskCtx), collectSiblings(ctx, sessionId), ctx.sharedDecisions, ctx.getSwarmContext());
|
|
938
1071
|
try {
|
|
939
1072
|
decision = await withTimeout(agentDecisionCb(eventMessage, sessionId, taskCtx), DECISION_CB_TIMEOUT_MS, "agentDecisionCb");
|
|
940
1073
|
if (decision)
|
|
@@ -2388,7 +2521,7 @@ async function stopSession(ctx, sessionId, sessionMetadata, sessionWorkdirs, log
|
|
|
2388
2521
|
const workdir = sessionWorkdirs.get(sessionId);
|
|
2389
2522
|
if (workdir) {
|
|
2390
2523
|
try {
|
|
2391
|
-
await
|
|
2524
|
+
await cleanupAgentHooks(workdir, log);
|
|
2392
2525
|
} catch {}
|
|
2393
2526
|
}
|
|
2394
2527
|
sessionMetadata.delete(sessionId);
|
|
@@ -2398,17 +2531,27 @@ async function stopSession(ctx, sessionId, sessionMetadata, sessionWorkdirs, log
|
|
|
2398
2531
|
log(`Stopped session ${sessionId}`);
|
|
2399
2532
|
}
|
|
2400
2533
|
}
|
|
2401
|
-
async function
|
|
2402
|
-
const
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2534
|
+
async function cleanupAgentHooks(workdir, log) {
|
|
2535
|
+
const settingsPaths = [
|
|
2536
|
+
join(workdir, ".claude", "settings.json"),
|
|
2537
|
+
join(workdir, ".gemini", "settings.json")
|
|
2538
|
+
];
|
|
2539
|
+
for (const settingsPath of settingsPaths) {
|
|
2540
|
+
try {
|
|
2541
|
+
const raw = await readFile(settingsPath, "utf-8");
|
|
2542
|
+
const settings = JSON.parse(raw);
|
|
2543
|
+
if (!settings.hooks)
|
|
2544
|
+
continue;
|
|
2545
|
+
delete settings.hooks;
|
|
2546
|
+
await writeFile(settingsPath, JSON.stringify(settings, null, 2), "utf-8");
|
|
2547
|
+
log(`Cleaned up hooks from ${settingsPath}`);
|
|
2548
|
+
} catch (err) {
|
|
2549
|
+
const code = err.code;
|
|
2550
|
+
if (code !== "ENOENT") {
|
|
2551
|
+
log(`Failed to clean up hooks from ${settingsPath}: ${err}`);
|
|
2552
|
+
}
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2412
2555
|
}
|
|
2413
2556
|
function subscribeToOutput(ctx, sessionId, callback) {
|
|
2414
2557
|
if (ctx.usingBunWorker) {
|
|
@@ -3031,7 +3174,18 @@ async function handleIdleCheck(ctx, taskCtx, idleMinutes) {
|
|
|
3031
3174
|
response: d.response,
|
|
3032
3175
|
reasoning: d.reasoning
|
|
3033
3176
|
}));
|
|
3034
|
-
const
|
|
3177
|
+
const siblings = [];
|
|
3178
|
+
for (const [sid, task] of ctx.tasks) {
|
|
3179
|
+
if (sid === sessionId)
|
|
3180
|
+
continue;
|
|
3181
|
+
siblings.push({
|
|
3182
|
+
label: task.label,
|
|
3183
|
+
agentType: task.agentType,
|
|
3184
|
+
originalTask: task.originalTask,
|
|
3185
|
+
status: task.status
|
|
3186
|
+
});
|
|
3187
|
+
}
|
|
3188
|
+
const prompt = buildIdleCheckPrompt(contextSummary, recentOutput, idleMinutes, taskCtx.idleCheckCount, MAX_IDLE_CHECKS, decisionHistory, siblings, ctx.sharedDecisions, ctx.getSwarmContext());
|
|
3035
3189
|
let decision = null;
|
|
3036
3190
|
try {
|
|
3037
3191
|
const result = await ctx.runtime.useModel(ModelType4.TEXT_SMALL, {
|
|
@@ -3098,11 +3252,15 @@ class SwarmCoordinator {
|
|
|
3098
3252
|
chatCallback = null;
|
|
3099
3253
|
wsBroadcast = null;
|
|
3100
3254
|
agentDecisionCb = null;
|
|
3255
|
+
swarmCompleteCb = null;
|
|
3101
3256
|
unregisteredBuffer = new Map;
|
|
3102
3257
|
idleWatchdogTimer = null;
|
|
3103
3258
|
lastSeenOutput = new Map;
|
|
3104
3259
|
lastToolNotification = new Map;
|
|
3105
3260
|
_paused = false;
|
|
3261
|
+
sharedDecisions = [];
|
|
3262
|
+
_swarmContext = "";
|
|
3263
|
+
swarmCompleteNotified = false;
|
|
3106
3264
|
pauseBuffer = [];
|
|
3107
3265
|
pauseTimeout = null;
|
|
3108
3266
|
constructor(runtime) {
|
|
@@ -3116,6 +3274,20 @@ class SwarmCoordinator {
|
|
|
3116
3274
|
this.wsBroadcast = cb;
|
|
3117
3275
|
this.log("WS broadcast callback wired");
|
|
3118
3276
|
}
|
|
3277
|
+
setSwarmCompleteCallback(cb) {
|
|
3278
|
+
this.swarmCompleteCb = cb;
|
|
3279
|
+
this.log("Swarm complete callback wired");
|
|
3280
|
+
}
|
|
3281
|
+
getSwarmCompleteCallback() {
|
|
3282
|
+
return this.swarmCompleteCb;
|
|
3283
|
+
}
|
|
3284
|
+
setSwarmContext(context) {
|
|
3285
|
+
this._swarmContext = context;
|
|
3286
|
+
this.log(`Swarm context set (${context.length} chars)`);
|
|
3287
|
+
}
|
|
3288
|
+
getSwarmContext() {
|
|
3289
|
+
return this._swarmContext;
|
|
3290
|
+
}
|
|
3119
3291
|
setAgentDecisionCallback(cb) {
|
|
3120
3292
|
this.agentDecisionCb = cb;
|
|
3121
3293
|
this.log("Agent decision callback wired — events will route through Milaidy");
|
|
@@ -3167,6 +3339,9 @@ class SwarmCoordinator {
|
|
|
3167
3339
|
this.lastSeenOutput.clear();
|
|
3168
3340
|
this.lastToolNotification.clear();
|
|
3169
3341
|
this.agentDecisionCb = null;
|
|
3342
|
+
this.sharedDecisions.length = 0;
|
|
3343
|
+
this._swarmContext = "";
|
|
3344
|
+
this.swarmCompleteNotified = false;
|
|
3170
3345
|
this._paused = false;
|
|
3171
3346
|
if (this.pauseTimeout) {
|
|
3172
3347
|
clearTimeout(this.pauseTimeout);
|
|
@@ -3210,6 +3385,16 @@ class SwarmCoordinator {
|
|
|
3210
3385
|
}
|
|
3211
3386
|
}
|
|
3212
3387
|
registerTask(sessionId, context) {
|
|
3388
|
+
const allPreviousTerminal = this.tasks.size === 0 || Array.from(this.tasks.values()).every((t) => t.status === "completed" || t.status === "stopped" || t.status === "error");
|
|
3389
|
+
if (allPreviousTerminal) {
|
|
3390
|
+
this.swarmCompleteNotified = false;
|
|
3391
|
+
if (this.tasks.size > 0) {
|
|
3392
|
+
this.tasks.clear();
|
|
3393
|
+
this.sharedDecisions.length = 0;
|
|
3394
|
+
this._swarmContext = "";
|
|
3395
|
+
this.log("Cleared stale swarm state for new swarm");
|
|
3396
|
+
}
|
|
3397
|
+
}
|
|
3213
3398
|
this.tasks.set(sessionId, {
|
|
3214
3399
|
sessionId,
|
|
3215
3400
|
agentType: context.agentType,
|
|
@@ -3223,7 +3408,8 @@ class SwarmCoordinator {
|
|
|
3223
3408
|
registeredAt: Date.now(),
|
|
3224
3409
|
lastActivityAt: Date.now(),
|
|
3225
3410
|
idleCheckCount: 0,
|
|
3226
|
-
taskDelivered: false
|
|
3411
|
+
taskDelivered: false,
|
|
3412
|
+
lastSeenDecisionIndex: 0
|
|
3227
3413
|
});
|
|
3228
3414
|
this.broadcast({
|
|
3229
3415
|
type: "task_registered",
|
|
@@ -3378,7 +3564,9 @@ class SwarmCoordinator {
|
|
|
3378
3564
|
break;
|
|
3379
3565
|
}
|
|
3380
3566
|
case "stopped":
|
|
3381
|
-
taskCtx.status
|
|
3567
|
+
if (taskCtx.status !== "completed" && taskCtx.status !== "error") {
|
|
3568
|
+
taskCtx.status = "stopped";
|
|
3569
|
+
}
|
|
3382
3570
|
this.inFlightDecisions.delete(sessionId);
|
|
3383
3571
|
this.broadcast({
|
|
3384
3572
|
type: "stopped",
|
|
@@ -3711,6 +3899,7 @@ class PTYService {
|
|
|
3711
3899
|
this.log(`Failed to write approval config: ${err}`);
|
|
3712
3900
|
}
|
|
3713
3901
|
}
|
|
3902
|
+
const hookUrl = `http://localhost:${this.runtime.getSetting("SERVER_PORT") ?? "2138"}/api/coding-agents/hooks`;
|
|
3714
3903
|
if (resolvedAgentType === "claude") {
|
|
3715
3904
|
try {
|
|
3716
3905
|
const settingsPath = join2(workdir, ".claude", "settings.json");
|
|
@@ -3721,14 +3910,14 @@ class PTYService {
|
|
|
3721
3910
|
const permissions = settings.permissions ?? {};
|
|
3722
3911
|
permissions.allowedDirectories = [workdir];
|
|
3723
3912
|
settings.permissions = permissions;
|
|
3724
|
-
const serverPort = this.runtime.getSetting("SERVER_PORT") ?? "2138";
|
|
3725
3913
|
const adapter = this.getAdapter("claude");
|
|
3726
3914
|
const hookProtocol = adapter.getHookTelemetryProtocol({
|
|
3727
|
-
httpUrl:
|
|
3915
|
+
httpUrl: hookUrl,
|
|
3728
3916
|
sessionId
|
|
3729
3917
|
});
|
|
3730
3918
|
if (hookProtocol) {
|
|
3731
|
-
settings.hooks
|
|
3919
|
+
const existingHooks = settings.hooks ?? {};
|
|
3920
|
+
settings.hooks = { ...existingHooks, ...hookProtocol.settingsHooks };
|
|
3732
3921
|
this.log(`Injecting HTTP hooks for session ${sessionId}`);
|
|
3733
3922
|
}
|
|
3734
3923
|
await mkdir(dirname(settingsPath), { recursive: true });
|
|
@@ -3738,6 +3927,29 @@ class PTYService {
|
|
|
3738
3927
|
this.log(`Failed to write Claude settings: ${err}`);
|
|
3739
3928
|
}
|
|
3740
3929
|
}
|
|
3930
|
+
if (resolvedAgentType === "gemini") {
|
|
3931
|
+
try {
|
|
3932
|
+
const settingsPath = join2(workdir, ".gemini", "settings.json");
|
|
3933
|
+
let settings = {};
|
|
3934
|
+
try {
|
|
3935
|
+
settings = JSON.parse(await readFile2(settingsPath, "utf-8"));
|
|
3936
|
+
} catch {}
|
|
3937
|
+
const adapter = this.getAdapter("gemini");
|
|
3938
|
+
const hookProtocol = adapter.getHookTelemetryProtocol({
|
|
3939
|
+
httpUrl: hookUrl,
|
|
3940
|
+
sessionId
|
|
3941
|
+
});
|
|
3942
|
+
if (hookProtocol) {
|
|
3943
|
+
const existingHooks = settings.hooks ?? {};
|
|
3944
|
+
settings.hooks = { ...existingHooks, ...hookProtocol.settingsHooks };
|
|
3945
|
+
this.log(`Injecting Gemini CLI hooks for session ${sessionId}`);
|
|
3946
|
+
}
|
|
3947
|
+
await mkdir(dirname(settingsPath), { recursive: true });
|
|
3948
|
+
await writeFile2(settingsPath, JSON.stringify(settings, null, 2), "utf-8");
|
|
3949
|
+
} catch (err) {
|
|
3950
|
+
this.log(`Failed to write Gemini settings: ${err}`);
|
|
3951
|
+
}
|
|
3952
|
+
}
|
|
3741
3953
|
const spawnConfig = buildSpawnConfig(sessionId, {
|
|
3742
3954
|
...options,
|
|
3743
3955
|
agentType: resolvedAgentType,
|
|
@@ -3893,6 +4105,9 @@ class PTYService {
|
|
|
3893
4105
|
} else {
|
|
3894
4106
|
this.log(`Hook event for ${sessionId}: ${event} ${summary}`);
|
|
3895
4107
|
}
|
|
4108
|
+
if (this.manager && this.usingBunWorker) {
|
|
4109
|
+
this.manager.notifyHookEvent(sessionId, event).catch((err) => logger2.debug(`[PTYService] Failed to forward hook event to session: ${err}`));
|
|
4110
|
+
}
|
|
3896
4111
|
switch (event) {
|
|
3897
4112
|
case "tool_running":
|
|
3898
4113
|
this.emitEvent(sessionId, "tool_running", data);
|
|
@@ -3905,6 +4120,9 @@ class PTYService {
|
|
|
3905
4120
|
case "notification":
|
|
3906
4121
|
this.emitEvent(sessionId, "message", data);
|
|
3907
4122
|
break;
|
|
4123
|
+
case "session_end":
|
|
4124
|
+
this.emitEvent(sessionId, "stopped", { ...data, reason: "session_end" });
|
|
4125
|
+
break;
|
|
3908
4126
|
default:
|
|
3909
4127
|
break;
|
|
3910
4128
|
}
|
|
@@ -4295,7 +4513,8 @@ var spawnAgentAction = {
|
|
|
4295
4513
|
|
|
4296
4514
|
// src/actions/coding-task-handlers.ts
|
|
4297
4515
|
import {
|
|
4298
|
-
logger as logger5
|
|
4516
|
+
logger as logger5,
|
|
4517
|
+
ModelType as ModelType5
|
|
4299
4518
|
} from "@elizaos/core";
|
|
4300
4519
|
|
|
4301
4520
|
// src/actions/coding-task-helpers.ts
|
|
@@ -4371,6 +4590,65 @@ ${preview}` : `Agent "${label}" completed the task.`
|
|
|
4371
4590
|
|
|
4372
4591
|
// src/actions/coding-task-handlers.ts
|
|
4373
4592
|
var MAX_CONCURRENT_AGENTS = 8;
|
|
4593
|
+
var KNOWN_AGENT_PREFIXES = [
|
|
4594
|
+
"claude",
|
|
4595
|
+
"claude-code",
|
|
4596
|
+
"claudecode",
|
|
4597
|
+
"codex",
|
|
4598
|
+
"openai",
|
|
4599
|
+
"gemini",
|
|
4600
|
+
"google",
|
|
4601
|
+
"aider",
|
|
4602
|
+
"pi",
|
|
4603
|
+
"pi-ai",
|
|
4604
|
+
"piai",
|
|
4605
|
+
"pi-coding-agent",
|
|
4606
|
+
"picodingagent",
|
|
4607
|
+
"shell",
|
|
4608
|
+
"bash"
|
|
4609
|
+
];
|
|
4610
|
+
function stripAgentPrefix(spec) {
|
|
4611
|
+
const colonIdx = spec.indexOf(":");
|
|
4612
|
+
if (colonIdx <= 0 || colonIdx >= 20)
|
|
4613
|
+
return spec;
|
|
4614
|
+
const prefix = spec.slice(0, colonIdx).trim().toLowerCase();
|
|
4615
|
+
if (KNOWN_AGENT_PREFIXES.includes(prefix)) {
|
|
4616
|
+
return spec.slice(colonIdx + 1).trim();
|
|
4617
|
+
}
|
|
4618
|
+
return spec;
|
|
4619
|
+
}
|
|
4620
|
+
async function generateSwarmContext(runtime, subtasks, userRequest) {
|
|
4621
|
+
const taskList = subtasks.map((t, i) => ` ${i + 1}. ${t}`).join(`
|
|
4622
|
+
`);
|
|
4623
|
+
const prompt = `You are an AI orchestrator about to launch ${subtasks.length} parallel agents. ` + `Before they start, produce a brief shared context document so all agents stay aligned.
|
|
4624
|
+
|
|
4625
|
+
` + `User's request: "${userRequest}"
|
|
4626
|
+
|
|
4627
|
+
` + `Subtasks being assigned:
|
|
4628
|
+
${taskList}
|
|
4629
|
+
|
|
4630
|
+
` + `Generate a concise shared context brief (3-8 bullet points) covering:
|
|
4631
|
+
` + `- Project intent and overall goal
|
|
4632
|
+
` + `- Key constraints or preferences from the user's request
|
|
4633
|
+
` + `- Conventions all agents should follow (naming, style, patterns, tone)
|
|
4634
|
+
` + `- How subtasks relate to each other (dependencies, shared interfaces, etc.)
|
|
4635
|
+
` + `- Any decisions that should be consistent across all agents
|
|
4636
|
+
|
|
4637
|
+
` + `Only include what's relevant — skip categories that don't apply. ` + `Be specific and actionable, not generic. Keep it under 200 words.
|
|
4638
|
+
|
|
4639
|
+
` + `Output ONLY the bullet points, no preamble.`;
|
|
4640
|
+
try {
|
|
4641
|
+
const result = await runtime.useModel(ModelType5.TEXT_SMALL, {
|
|
4642
|
+
prompt,
|
|
4643
|
+
maxTokens: 400,
|
|
4644
|
+
temperature: 0.3
|
|
4645
|
+
});
|
|
4646
|
+
return result?.trim() || "";
|
|
4647
|
+
} catch (err) {
|
|
4648
|
+
logger5.warn(`Swarm context generation failed: ${err}`);
|
|
4649
|
+
return "";
|
|
4650
|
+
}
|
|
4651
|
+
}
|
|
4374
4652
|
async function handleMultiAgent(ctx, agentsParam) {
|
|
4375
4653
|
const {
|
|
4376
4654
|
runtime,
|
|
@@ -4418,6 +4696,13 @@ async function handleMultiAgent(ctx, agentsParam) {
|
|
|
4418
4696
|
text: `Launching ${agentSpecs.length} agents${repo ? ` on ${repo}` : ""}...`
|
|
4419
4697
|
});
|
|
4420
4698
|
}
|
|
4699
|
+
const cleanSubtasks = agentSpecs.map(stripAgentPrefix);
|
|
4700
|
+
const userRequest = message.content?.text ?? agentsParam;
|
|
4701
|
+
const swarmContext = agentSpecs.length > 1 ? await generateSwarmContext(runtime, cleanSubtasks, userRequest) : "";
|
|
4702
|
+
if (swarmContext) {
|
|
4703
|
+
const coordinator = getCoordinator(runtime);
|
|
4704
|
+
coordinator?.setSwarmContext(swarmContext);
|
|
4705
|
+
}
|
|
4421
4706
|
const results = [];
|
|
4422
4707
|
for (const [i, spec] of agentSpecs.entries()) {
|
|
4423
4708
|
let specAgentType = defaultAgentType;
|
|
@@ -4427,51 +4712,14 @@ async function handleMultiAgent(ctx, agentsParam) {
|
|
|
4427
4712
|
const colonIdx = spec.indexOf(":");
|
|
4428
4713
|
if (ctx.agentSelectionStrategy !== "fixed" && colonIdx > 0 && colonIdx < 20) {
|
|
4429
4714
|
const prefix = spec.slice(0, colonIdx).trim().toLowerCase();
|
|
4430
|
-
|
|
4431
|
-
"claude",
|
|
4432
|
-
"claude-code",
|
|
4433
|
-
"claudecode",
|
|
4434
|
-
"codex",
|
|
4435
|
-
"openai",
|
|
4436
|
-
"gemini",
|
|
4437
|
-
"google",
|
|
4438
|
-
"aider",
|
|
4439
|
-
"pi",
|
|
4440
|
-
"pi-ai",
|
|
4441
|
-
"piai",
|
|
4442
|
-
"pi-coding-agent",
|
|
4443
|
-
"picodingagent",
|
|
4444
|
-
"shell",
|
|
4445
|
-
"bash"
|
|
4446
|
-
];
|
|
4447
|
-
if (knownTypes.includes(prefix)) {
|
|
4715
|
+
if (KNOWN_AGENT_PREFIXES.includes(prefix)) {
|
|
4448
4716
|
specRequestedType = prefix;
|
|
4449
4717
|
specPiRequested = isPiAgentType(prefix);
|
|
4450
4718
|
specAgentType = normalizeAgentType(prefix);
|
|
4451
4719
|
specTask = spec.slice(colonIdx + 1).trim();
|
|
4452
4720
|
}
|
|
4453
4721
|
} else if (ctx.agentSelectionStrategy === "fixed" && colonIdx > 0 && colonIdx < 20) {
|
|
4454
|
-
|
|
4455
|
-
const knownTypes = [
|
|
4456
|
-
"claude",
|
|
4457
|
-
"claude-code",
|
|
4458
|
-
"claudecode",
|
|
4459
|
-
"codex",
|
|
4460
|
-
"openai",
|
|
4461
|
-
"gemini",
|
|
4462
|
-
"google",
|
|
4463
|
-
"aider",
|
|
4464
|
-
"pi",
|
|
4465
|
-
"pi-ai",
|
|
4466
|
-
"piai",
|
|
4467
|
-
"pi-coding-agent",
|
|
4468
|
-
"picodingagent",
|
|
4469
|
-
"shell",
|
|
4470
|
-
"bash"
|
|
4471
|
-
];
|
|
4472
|
-
if (knownTypes.includes(prefix)) {
|
|
4473
|
-
specTask = spec.slice(colonIdx + 1).trim();
|
|
4474
|
-
}
|
|
4722
|
+
specTask = stripAgentPrefix(spec);
|
|
4475
4723
|
}
|
|
4476
4724
|
const specLabel = explicitLabel ? `${explicitLabel}-${i + 1}` : generateLabel(repo, specTask);
|
|
4477
4725
|
try {
|
|
@@ -4504,7 +4752,12 @@ async function handleMultiAgent(ctx, agentsParam) {
|
|
|
4504
4752
|
}
|
|
4505
4753
|
}
|
|
4506
4754
|
const coordinator = getCoordinator(runtime);
|
|
4507
|
-
const
|
|
4755
|
+
const taskWithContext = swarmContext ? `${specTask}
|
|
4756
|
+
|
|
4757
|
+
--- Shared Context (from project planning) ---
|
|
4758
|
+
${swarmContext}
|
|
4759
|
+
--- End Shared Context ---` : specTask;
|
|
4760
|
+
const initialTask = specPiRequested ? toPiCommand(taskWithContext) : taskWithContext;
|
|
4508
4761
|
const displayType = specPiRequested ? "pi" : specAgentType;
|
|
4509
4762
|
const session = await ptyService.spawnSession({
|
|
4510
4763
|
name: `coding-${Date.now()}-${i}`,
|
|
@@ -6319,6 +6572,8 @@ async function handleHookRoutes(req, res, pathname, ctx) {
|
|
|
6319
6572
|
sendError(res, "Missing hook_event_name", 400);
|
|
6320
6573
|
return true;
|
|
6321
6574
|
}
|
|
6575
|
+
const toolName = payload.tool_name ?? payload.toolName;
|
|
6576
|
+
const notificationType = payload.notification_type ?? payload.notificationType;
|
|
6322
6577
|
const headerSessionId = req.headers["x-parallax-session-id"];
|
|
6323
6578
|
const sessionId = headerSessionId ? headerSessionId : payload.cwd ? ctx.ptyService.findSessionIdByCwd(payload.cwd) : undefined;
|
|
6324
6579
|
if (!sessionId) {
|
|
@@ -6334,13 +6589,13 @@ async function handleHookRoutes(req, res, pathname, ctx) {
|
|
|
6334
6589
|
}
|
|
6335
6590
|
});
|
|
6336
6591
|
ctx.ptyService.handleHookEvent(sessionId, "permission_approved", {
|
|
6337
|
-
tool:
|
|
6592
|
+
tool: toolName
|
|
6338
6593
|
});
|
|
6339
6594
|
return true;
|
|
6340
6595
|
}
|
|
6341
6596
|
case "PreToolUse": {
|
|
6342
6597
|
ctx.ptyService.handleHookEvent(sessionId, "tool_running", {
|
|
6343
|
-
toolName
|
|
6598
|
+
toolName,
|
|
6344
6599
|
source: "hook"
|
|
6345
6600
|
});
|
|
6346
6601
|
sendJson(res, {
|
|
@@ -6358,19 +6613,56 @@ async function handleHookRoutes(req, res, pathname, ctx) {
|
|
|
6358
6613
|
sendJson(res, {});
|
|
6359
6614
|
return true;
|
|
6360
6615
|
}
|
|
6361
|
-
case "
|
|
6362
|
-
ctx.ptyService.handleHookEvent(sessionId, "
|
|
6363
|
-
|
|
6364
|
-
message: payload.message
|
|
6616
|
+
case "TaskCompleted": {
|
|
6617
|
+
ctx.ptyService.handleHookEvent(sessionId, "task_complete", {
|
|
6618
|
+
source: "hook_task_completed"
|
|
6365
6619
|
});
|
|
6366
6620
|
sendJson(res, {});
|
|
6367
6621
|
return true;
|
|
6368
6622
|
}
|
|
6369
|
-
case "
|
|
6623
|
+
case "BeforeTool": {
|
|
6624
|
+
ctx.ptyService.handleHookEvent(sessionId, "tool_running", {
|
|
6625
|
+
toolName,
|
|
6626
|
+
source: "gemini_hook"
|
|
6627
|
+
});
|
|
6628
|
+
sendJson(res, { decision: "allow", continue: true });
|
|
6629
|
+
return true;
|
|
6630
|
+
}
|
|
6631
|
+
case "AfterTool": {
|
|
6632
|
+
ctx.ptyService.handleHookEvent(sessionId, "notification", {
|
|
6633
|
+
type: "tool_complete",
|
|
6634
|
+
message: `Tool ${toolName ?? "unknown"} finished`
|
|
6635
|
+
});
|
|
6636
|
+
sendJson(res, { continue: true });
|
|
6637
|
+
return true;
|
|
6638
|
+
}
|
|
6639
|
+
case "AfterAgent": {
|
|
6370
6640
|
ctx.ptyService.handleHookEvent(sessionId, "task_complete", {
|
|
6371
|
-
source: "
|
|
6641
|
+
source: "gemini_hook"
|
|
6372
6642
|
});
|
|
6373
|
-
sendJson(res, {});
|
|
6643
|
+
sendJson(res, { continue: true });
|
|
6644
|
+
return true;
|
|
6645
|
+
}
|
|
6646
|
+
case "SessionEnd": {
|
|
6647
|
+
ctx.ptyService.handleHookEvent(sessionId, "session_end", {
|
|
6648
|
+
source: "hook"
|
|
6649
|
+
});
|
|
6650
|
+
sendJson(res, { continue: true });
|
|
6651
|
+
return true;
|
|
6652
|
+
}
|
|
6653
|
+
case "Notification": {
|
|
6654
|
+
if (notificationType === "ToolPermission") {
|
|
6655
|
+
ctx.ptyService.handleHookEvent(sessionId, "permission_approved", {
|
|
6656
|
+
tool: toolName
|
|
6657
|
+
});
|
|
6658
|
+
sendJson(res, { decision: "allow", continue: true });
|
|
6659
|
+
return true;
|
|
6660
|
+
}
|
|
6661
|
+
ctx.ptyService.handleHookEvent(sessionId, "notification", {
|
|
6662
|
+
type: notificationType,
|
|
6663
|
+
message: payload.message
|
|
6664
|
+
});
|
|
6665
|
+
sendJson(res, { continue: true });
|
|
6374
6666
|
return true;
|
|
6375
6667
|
}
|
|
6376
6668
|
default: {
|
|
@@ -6714,5 +7006,5 @@ export {
|
|
|
6714
7006
|
CodingWorkspaceService
|
|
6715
7007
|
};
|
|
6716
7008
|
|
|
6717
|
-
//# debugId=
|
|
7009
|
+
//# debugId=D706621831E2790E64756E2164756E21
|
|
6718
7010
|
//# sourceMappingURL=index.js.map
|