@elizaos/plugin-agent-orchestrator 0.3.4 → 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 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(`Skipping turn-complete assessment for ${sessionId} (in-flight)`);
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;
@@ -2754,6 +2768,120 @@ async function classifyStallOutput(ctx) {
2754
2768
  return null;
2755
2769
  }
2756
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
+ }
2757
2885
 
2758
2886
  // src/services/swarm-coordinator.ts
2759
2887
  init_ansi_utils();
@@ -2947,6 +3075,7 @@ class SwarmCoordinator {
2947
3075
  supervisionLevel = "autonomous";
2948
3076
  pendingDecisions = new Map;
2949
3077
  inFlightDecisions = new Set;
3078
+ pendingTurnComplete = new Map;
2950
3079
  chatCallback = null;
2951
3080
  wsBroadcast = null;
2952
3081
  agentDecisionCb = null;
@@ -3014,6 +3143,7 @@ class SwarmCoordinator {
3014
3143
  this.tasks.clear();
3015
3144
  this.pendingDecisions.clear();
3016
3145
  this.inFlightDecisions.clear();
3146
+ this.pendingTurnComplete.clear();
3017
3147
  this.unregisteredBuffer.clear();
3018
3148
  this.lastSeenOutput.clear();
3019
3149
  this.lastToolNotification.clear();
@@ -3708,6 +3838,38 @@ class PTYService {
3708
3838
  async classifyStall(sessionId, recentOutput) {
3709
3839
  const meta = this.sessionMetadata.get(sessionId);
3710
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
+ }
3711
3873
  const classification = await classifyStallOutput({
3712
3874
  sessionId,
3713
3875
  recentOutput,
@@ -4184,7 +4346,7 @@ async function handleMultiAgent(ctx, agentsParam) {
4184
4346
  let specRequestedType = rawAgentType;
4185
4347
  let specTask = spec;
4186
4348
  const colonIdx = spec.indexOf(":");
4187
- if (colonIdx > 0 && colonIdx < 20) {
4349
+ if (ctx.agentSelectionStrategy !== "fixed" && colonIdx > 0 && colonIdx < 20) {
4188
4350
  const prefix = spec.slice(0, colonIdx).trim().toLowerCase();
4189
4351
  const knownTypes = [
4190
4352
  "claude",
@@ -4209,6 +4371,28 @@ async function handleMultiAgent(ctx, agentsParam) {
4209
4371
  specAgentType = normalizeAgentType(prefix);
4210
4372
  specTask = spec.slice(colonIdx + 1).trim();
4211
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
+ }
4212
4396
  }
4213
4397
  const specLabel = explicitLabel ? `${explicitLabel}-${i + 1}` : generateLabel(repo, specTask);
4214
4398
  try {
@@ -4583,6 +4767,7 @@ var startCodingTaskAction = {
4583
4767
  repo,
4584
4768
  defaultAgentType,
4585
4769
  rawAgentType,
4770
+ agentSelectionStrategy: ptyService.agentSelectionStrategy,
4586
4771
  memoryContent,
4587
4772
  approvalPreset,
4588
4773
  explicitLabel
@@ -6360,5 +6545,5 @@ export {
6360
6545
  CodingWorkspaceService
6361
6546
  };
6362
6547
 
6363
- //# debugId=7296EE1D6046456D64756E2164756E21
6548
+ //# debugId=D911125D768F397664756E2164756E21
6364
6549
  //# sourceMappingURL=index.js.map