@elizaos/plugin-agent-orchestrator 0.3.3 → 0.3.6
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/index.js +198 -3
- package/dist/index.js.map +9 -9
- package/package.json +1 -1
- package/dist/services/pty-service.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -490,6 +490,17 @@ function toDecisionHistory(taskCtx) {
|
|
|
490
490
|
reasoning: d.reasoning
|
|
491
491
|
}));
|
|
492
492
|
}
|
|
493
|
+
async function drainPendingTurnComplete(ctx, sessionId) {
|
|
494
|
+
const pendingData = ctx.pendingTurnComplete.get(sessionId);
|
|
495
|
+
if (!pendingData)
|
|
496
|
+
return;
|
|
497
|
+
ctx.pendingTurnComplete.delete(sessionId);
|
|
498
|
+
const taskCtx = ctx.tasks.get(sessionId);
|
|
499
|
+
if (!taskCtx || taskCtx.status !== "active")
|
|
500
|
+
return;
|
|
501
|
+
ctx.log(`Draining buffered turn-complete for "${taskCtx.label}"`);
|
|
502
|
+
await handleTurnComplete(ctx, sessionId, taskCtx, pendingData);
|
|
503
|
+
}
|
|
493
504
|
function formatDecisionResponse(decision) {
|
|
494
505
|
if (decision.action !== "respond")
|
|
495
506
|
return;
|
|
@@ -721,7 +732,8 @@ async function handleBlocked(ctx, sessionId, taskCtx, data) {
|
|
|
721
732
|
}
|
|
722
733
|
async function handleTurnComplete(ctx, sessionId, taskCtx, data) {
|
|
723
734
|
if (ctx.inFlightDecisions.has(sessionId)) {
|
|
724
|
-
ctx.log(`
|
|
735
|
+
ctx.log(`Buffering turn-complete for ${sessionId} (in-flight decision running)`);
|
|
736
|
+
ctx.pendingTurnComplete.set(sessionId, data);
|
|
725
737
|
return;
|
|
726
738
|
}
|
|
727
739
|
ctx.inFlightDecisions.add(sessionId);
|
|
@@ -914,6 +926,7 @@ async function handleAutonomousDecision(ctx, sessionId, taskCtx, promptText, rec
|
|
|
914
926
|
await executeDecision(ctx, sessionId, decision);
|
|
915
927
|
} finally {
|
|
916
928
|
ctx.inFlightDecisions.delete(sessionId);
|
|
929
|
+
await drainPendingTurnComplete(ctx, sessionId);
|
|
917
930
|
}
|
|
918
931
|
}
|
|
919
932
|
async function handleConfirmDecision(ctx, sessionId, taskCtx, promptText, recentOutput, promptType) {
|
|
@@ -989,6 +1002,7 @@ async function handleConfirmDecision(ctx, sessionId, taskCtx, promptText, recent
|
|
|
989
1002
|
});
|
|
990
1003
|
} finally {
|
|
991
1004
|
ctx.inFlightDecisions.delete(sessionId);
|
|
1005
|
+
await drainPendingTurnComplete(ctx, sessionId);
|
|
992
1006
|
}
|
|
993
1007
|
}
|
|
994
1008
|
var MAX_AUTO_RESPONSES = 10;
|
|
@@ -2495,11 +2509,15 @@ function setupDeferredTaskDelivery(ctx, session, task, agentType) {
|
|
|
2495
2509
|
}, VERIFY_DELAY_MS);
|
|
2496
2510
|
}
|
|
2497
2511
|
};
|
|
2512
|
+
const READY_TIMEOUT_MS = 30000;
|
|
2498
2513
|
let taskSent = false;
|
|
2514
|
+
let readyTimeout;
|
|
2499
2515
|
const sendTask = () => {
|
|
2500
2516
|
if (taskSent)
|
|
2501
2517
|
return;
|
|
2502
2518
|
taskSent = true;
|
|
2519
|
+
if (readyTimeout)
|
|
2520
|
+
clearTimeout(readyTimeout);
|
|
2503
2521
|
setTimeout(() => sendTaskWithRetry(0), settleMs);
|
|
2504
2522
|
if (ctx.usingBunWorker) {
|
|
2505
2523
|
ctx.manager.removeListener("session_ready", onReady);
|
|
@@ -2520,6 +2538,12 @@ function setupDeferredTaskDelivery(ctx, session, task, agentType) {
|
|
|
2520
2538
|
} else {
|
|
2521
2539
|
ctx.manager.on("session_ready", onReady);
|
|
2522
2540
|
}
|
|
2541
|
+
readyTimeout = setTimeout(() => {
|
|
2542
|
+
if (!taskSent) {
|
|
2543
|
+
ctx.log(`Session ${sid} — ready event not received within ${READY_TIMEOUT_MS}ms, forcing task delivery`);
|
|
2544
|
+
sendTask();
|
|
2545
|
+
}
|
|
2546
|
+
}, READY_TIMEOUT_MS);
|
|
2523
2547
|
}
|
|
2524
2548
|
}
|
|
2525
2549
|
function buildSpawnConfig(sessionId, options, workdir) {
|
|
@@ -2744,6 +2768,120 @@ async function classifyStallOutput(ctx) {
|
|
|
2744
2768
|
return null;
|
|
2745
2769
|
}
|
|
2746
2770
|
}
|
|
2771
|
+
function buildCombinedClassifyDecidePrompt(agentType, sessionId, output, taskContext, decisionHistory) {
|
|
2772
|
+
const historySection = decisionHistory.length > 0 ? `
|
|
2773
|
+
Previous decisions for this session:
|
|
2774
|
+
${decisionHistory.slice(-5).map((d, i) => ` ${i + 1}. [${d.event}] prompt="${d.promptText}" → ${d.action}${d.response ? ` ("${d.response}")` : ""} — ${d.reasoning}`).join(`
|
|
2775
|
+
`)}
|
|
2776
|
+
` : "";
|
|
2777
|
+
return `You are Milady, an AI orchestrator managing coding agent sessions. A ${agentType} coding agent (session: ${sessionId}) appears to have stalled — ` + `it has stopped producing output while in a busy state.
|
|
2778
|
+
|
|
2779
|
+
Original task: "${taskContext.originalTask}"
|
|
2780
|
+
Working directory: ${taskContext.workdir}
|
|
2781
|
+
Repository: ${taskContext.repo ?? "none (scratch directory)"}
|
|
2782
|
+
` + historySection + `
|
|
2783
|
+
Here is the recent terminal output:
|
|
2784
|
+
---
|
|
2785
|
+
${output.slice(-1500)}
|
|
2786
|
+
---
|
|
2787
|
+
|
|
2788
|
+
Classify what's happening AND decide how to respond. Read the output carefully.
|
|
2789
|
+
|
|
2790
|
+
Classification states:
|
|
2791
|
+
|
|
2792
|
+
` + `1. "task_complete" — The agent FINISHED its task and returned to its idle prompt. ` + `Strong indicators: a summary of completed work, timing info, ` + `or the agent's main prompt symbol (❯) appearing AFTER completion output.
|
|
2793
|
+
|
|
2794
|
+
` + `2. "waiting_for_input" — The agent is MID-TASK and blocked on a specific question or permission prompt. ` + `Examples: Y/n confirmation, file permission dialogs, tool approval prompts, interactive menus.
|
|
2795
|
+
|
|
2796
|
+
` + `3. "still_working" — The agent is actively processing (API call, compilation, thinking). ` + `No prompt or completion summary visible.
|
|
2797
|
+
|
|
2798
|
+
` + `4. "error" — The agent hit an error state (crash, unrecoverable error, stack trace).
|
|
2799
|
+
|
|
2800
|
+
` + `5. "tool_running" — The agent is using an external tool (browser automation, MCP tool, etc.).
|
|
2801
|
+
|
|
2802
|
+
` + `If "waiting_for_input", you must also decide how to respond. Guidelines:
|
|
2803
|
+
- IMPORTANT: If the prompt asks to approve access to files or directories OUTSIDE the working directory (${taskContext.workdir}), DECLINE the request. Respond with "n" and tell the agent: "That path is outside your workspace. Use ${taskContext.workdir} instead."
|
|
2804
|
+
- For tool approval prompts (file writes, shell commands), respond "y" or use "keys:enter".
|
|
2805
|
+
- For Y/n confirmations that align with the original task, respond "y".
|
|
2806
|
+
- For TUI menus, use "keys:enter" for default or "keys:down,enter" for non-default.
|
|
2807
|
+
- If the prompt asks for information NOT in the original task, set suggestedResponse to null (this will escalate to the human).
|
|
2808
|
+
- If a PR was just created, respond to review & verify test plan items before completing.
|
|
2809
|
+
|
|
2810
|
+
Respond with ONLY a JSON object:
|
|
2811
|
+
{"state": "...", "prompt": "...", "suggestedResponse": "..."}`;
|
|
2812
|
+
}
|
|
2813
|
+
async function classifyAndDecideForCoordinator(ctx) {
|
|
2814
|
+
const {
|
|
2815
|
+
sessionId,
|
|
2816
|
+
recentOutput,
|
|
2817
|
+
agentType,
|
|
2818
|
+
buffers,
|
|
2819
|
+
traceEntries,
|
|
2820
|
+
runtime,
|
|
2821
|
+
manager,
|
|
2822
|
+
metricsTracker,
|
|
2823
|
+
taskContext,
|
|
2824
|
+
decisionHistory = [],
|
|
2825
|
+
log
|
|
2826
|
+
} = ctx;
|
|
2827
|
+
metricsTracker.incrementStalls(agentType);
|
|
2828
|
+
let effectiveOutput = recentOutput;
|
|
2829
|
+
if (!recentOutput || recentOutput.trim().length < 200) {
|
|
2830
|
+
const ourBuffer = buffers.get(sessionId);
|
|
2831
|
+
if (ourBuffer && ourBuffer.length > 0) {
|
|
2832
|
+
const rawTail = ourBuffer.slice(-100).join(`
|
|
2833
|
+
`);
|
|
2834
|
+
const stripped = stripAnsi(rawTail);
|
|
2835
|
+
if (stripped.length > effectiveOutput.length) {
|
|
2836
|
+
effectiveOutput = stripped;
|
|
2837
|
+
log(`Using own buffer for combined classify+decide (${effectiveOutput.length} chars after stripping, pty-manager had ${recentOutput.length})`);
|
|
2838
|
+
}
|
|
2839
|
+
}
|
|
2840
|
+
}
|
|
2841
|
+
const systemPrompt = buildCombinedClassifyDecidePrompt(agentType, sessionId, effectiveOutput, taskContext, decisionHistory);
|
|
2842
|
+
if (ctx.debugSnapshots) {
|
|
2843
|
+
await writeStallSnapshot(sessionId, agentType, recentOutput, effectiveOutput, buffers, traceEntries, log);
|
|
2844
|
+
}
|
|
2845
|
+
try {
|
|
2846
|
+
log(`Stall detected for coordinator-managed ${sessionId}, combined classify+decide...`);
|
|
2847
|
+
const result = await runtime.useModel(ModelType.TEXT_SMALL, {
|
|
2848
|
+
prompt: systemPrompt
|
|
2849
|
+
});
|
|
2850
|
+
const jsonMatch = result.match(/\{[\s\S]*\}/);
|
|
2851
|
+
if (!jsonMatch) {
|
|
2852
|
+
log(`Combined classify+decide: no JSON in LLM response`);
|
|
2853
|
+
return null;
|
|
2854
|
+
}
|
|
2855
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
2856
|
+
const validStates = [
|
|
2857
|
+
"waiting_for_input",
|
|
2858
|
+
"still_working",
|
|
2859
|
+
"task_complete",
|
|
2860
|
+
"error",
|
|
2861
|
+
"tool_running"
|
|
2862
|
+
];
|
|
2863
|
+
if (!validStates.includes(parsed.state)) {
|
|
2864
|
+
log(`Combined classify+decide: invalid state "${parsed.state}"`);
|
|
2865
|
+
return null;
|
|
2866
|
+
}
|
|
2867
|
+
const mappedState = parsed.state === "tool_running" ? "still_working" : parsed.state;
|
|
2868
|
+
const classification = {
|
|
2869
|
+
state: mappedState,
|
|
2870
|
+
prompt: parsed.prompt,
|
|
2871
|
+
suggestedResponse: parsed.suggestedResponse
|
|
2872
|
+
};
|
|
2873
|
+
log(`Combined classify+decide for ${sessionId}: ${classification.state}${classification.suggestedResponse ? ` → "${classification.suggestedResponse}"` : ""}`);
|
|
2874
|
+
if (classification.state === "task_complete") {
|
|
2875
|
+
const session = manager?.get(sessionId);
|
|
2876
|
+
const durationMs = session?.startedAt ? Date.now() - new Date(session.startedAt).getTime() : 0;
|
|
2877
|
+
metricsTracker.recordCompletion(agentType, "classifier", durationMs);
|
|
2878
|
+
}
|
|
2879
|
+
return classification;
|
|
2880
|
+
} catch (err) {
|
|
2881
|
+
log(`Combined classify+decide failed: ${err}`);
|
|
2882
|
+
return null;
|
|
2883
|
+
}
|
|
2884
|
+
}
|
|
2747
2885
|
|
|
2748
2886
|
// src/services/swarm-coordinator.ts
|
|
2749
2887
|
init_ansi_utils();
|
|
@@ -2937,6 +3075,7 @@ class SwarmCoordinator {
|
|
|
2937
3075
|
supervisionLevel = "autonomous";
|
|
2938
3076
|
pendingDecisions = new Map;
|
|
2939
3077
|
inFlightDecisions = new Set;
|
|
3078
|
+
pendingTurnComplete = new Map;
|
|
2940
3079
|
chatCallback = null;
|
|
2941
3080
|
wsBroadcast = null;
|
|
2942
3081
|
agentDecisionCb = null;
|
|
@@ -3004,6 +3143,7 @@ class SwarmCoordinator {
|
|
|
3004
3143
|
this.tasks.clear();
|
|
3005
3144
|
this.pendingDecisions.clear();
|
|
3006
3145
|
this.inFlightDecisions.clear();
|
|
3146
|
+
this.pendingTurnComplete.clear();
|
|
3007
3147
|
this.unregisteredBuffer.clear();
|
|
3008
3148
|
this.lastSeenOutput.clear();
|
|
3009
3149
|
this.lastToolNotification.clear();
|
|
@@ -3698,6 +3838,38 @@ class PTYService {
|
|
|
3698
3838
|
async classifyStall(sessionId, recentOutput) {
|
|
3699
3839
|
const meta = this.sessionMetadata.get(sessionId);
|
|
3700
3840
|
const agentType = meta?.agentType ?? "unknown";
|
|
3841
|
+
if (meta?.coordinatorManaged && this.coordinator?.getSupervisionLevel() === "autonomous") {
|
|
3842
|
+
const taskCtx = this.coordinator.getTaskContext(sessionId);
|
|
3843
|
+
if (taskCtx) {
|
|
3844
|
+
return classifyAndDecideForCoordinator({
|
|
3845
|
+
sessionId,
|
|
3846
|
+
recentOutput,
|
|
3847
|
+
agentType,
|
|
3848
|
+
buffers: this.sessionOutputBuffers,
|
|
3849
|
+
traceEntries: this.traceEntries,
|
|
3850
|
+
runtime: this.runtime,
|
|
3851
|
+
manager: this.manager,
|
|
3852
|
+
metricsTracker: this.metricsTracker,
|
|
3853
|
+
debugSnapshots: this.serviceConfig.debug === true,
|
|
3854
|
+
log: (msg) => this.log(msg),
|
|
3855
|
+
taskContext: {
|
|
3856
|
+
sessionId: taskCtx.sessionId,
|
|
3857
|
+
agentType: taskCtx.agentType,
|
|
3858
|
+
label: taskCtx.label,
|
|
3859
|
+
originalTask: taskCtx.originalTask,
|
|
3860
|
+
workdir: taskCtx.workdir,
|
|
3861
|
+
repo: taskCtx.repo
|
|
3862
|
+
},
|
|
3863
|
+
decisionHistory: taskCtx.decisions.filter((d) => d.decision !== "auto_resolved").slice(-5).map((d) => ({
|
|
3864
|
+
event: d.event,
|
|
3865
|
+
promptText: d.promptText,
|
|
3866
|
+
action: d.decision,
|
|
3867
|
+
response: d.response,
|
|
3868
|
+
reasoning: d.reasoning
|
|
3869
|
+
}))
|
|
3870
|
+
});
|
|
3871
|
+
}
|
|
3872
|
+
}
|
|
3701
3873
|
const classification = await classifyStallOutput({
|
|
3702
3874
|
sessionId,
|
|
3703
3875
|
recentOutput,
|
|
@@ -4174,7 +4346,7 @@ async function handleMultiAgent(ctx, agentsParam) {
|
|
|
4174
4346
|
let specRequestedType = rawAgentType;
|
|
4175
4347
|
let specTask = spec;
|
|
4176
4348
|
const colonIdx = spec.indexOf(":");
|
|
4177
|
-
if (colonIdx > 0 && colonIdx < 20) {
|
|
4349
|
+
if (ctx.agentSelectionStrategy !== "fixed" && colonIdx > 0 && colonIdx < 20) {
|
|
4178
4350
|
const prefix = spec.slice(0, colonIdx).trim().toLowerCase();
|
|
4179
4351
|
const knownTypes = [
|
|
4180
4352
|
"claude",
|
|
@@ -4199,6 +4371,28 @@ async function handleMultiAgent(ctx, agentsParam) {
|
|
|
4199
4371
|
specAgentType = normalizeAgentType(prefix);
|
|
4200
4372
|
specTask = spec.slice(colonIdx + 1).trim();
|
|
4201
4373
|
}
|
|
4374
|
+
} else if (ctx.agentSelectionStrategy === "fixed" && colonIdx > 0 && colonIdx < 20) {
|
|
4375
|
+
const prefix = spec.slice(0, colonIdx).trim().toLowerCase();
|
|
4376
|
+
const knownTypes = [
|
|
4377
|
+
"claude",
|
|
4378
|
+
"claude-code",
|
|
4379
|
+
"claudecode",
|
|
4380
|
+
"codex",
|
|
4381
|
+
"openai",
|
|
4382
|
+
"gemini",
|
|
4383
|
+
"google",
|
|
4384
|
+
"aider",
|
|
4385
|
+
"pi",
|
|
4386
|
+
"pi-ai",
|
|
4387
|
+
"piai",
|
|
4388
|
+
"pi-coding-agent",
|
|
4389
|
+
"picodingagent",
|
|
4390
|
+
"shell",
|
|
4391
|
+
"bash"
|
|
4392
|
+
];
|
|
4393
|
+
if (knownTypes.includes(prefix)) {
|
|
4394
|
+
specTask = spec.slice(colonIdx + 1).trim();
|
|
4395
|
+
}
|
|
4202
4396
|
}
|
|
4203
4397
|
const specLabel = explicitLabel ? `${explicitLabel}-${i + 1}` : generateLabel(repo, specTask);
|
|
4204
4398
|
try {
|
|
@@ -4573,6 +4767,7 @@ var startCodingTaskAction = {
|
|
|
4573
4767
|
repo,
|
|
4574
4768
|
defaultAgentType,
|
|
4575
4769
|
rawAgentType,
|
|
4770
|
+
agentSelectionStrategy: ptyService.agentSelectionStrategy,
|
|
4576
4771
|
memoryContent,
|
|
4577
4772
|
approvalPreset,
|
|
4578
4773
|
explicitLabel
|
|
@@ -6350,5 +6545,5 @@ export {
|
|
|
6350
6545
|
CodingWorkspaceService
|
|
6351
6546
|
};
|
|
6352
6547
|
|
|
6353
|
-
//# debugId=
|
|
6548
|
+
//# debugId=D911125D768F397664756E2164756E21
|
|
6354
6549
|
//# sourceMappingURL=index.js.map
|