@elizaos/plugin-agent-orchestrator 0.3.2 → 0.3.4

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
@@ -1,12 +1,16 @@
1
- import {createRequire} from "node:module";
1
+ import { createRequire } from "node:module";
2
2
  var __defProp = Object.defineProperty;
3
+ var __returnValue = (v) => v;
4
+ function __exportSetter(name, newValue) {
5
+ this[name] = __returnValue.bind(null, newValue);
6
+ }
3
7
  var __export = (target, all) => {
4
8
  for (var name in all)
5
9
  __defProp(target, name, {
6
10
  get: all[name],
7
11
  enumerable: true,
8
12
  configurable: true,
9
- set: (newValue) => all[name] = () => newValue
13
+ set: __exportSetter.bind(all, name)
10
14
  });
11
15
  };
12
16
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
@@ -21,7 +25,8 @@ function stripAnsi(raw) {
21
25
  }
22
26
  function cleanForChat(raw) {
23
27
  const stripped = applyAnsiStrip(raw);
24
- return stripped.replace(TUI_DECORATIVE, " ").replace(/\xa0/g, " ").split("\n").filter((line) => {
28
+ return stripped.replace(TUI_DECORATIVE, " ").replace(/\xa0/g, " ").split(`
29
+ `).filter((line) => {
25
30
  const trimmed = line.trim();
26
31
  if (!trimmed)
27
32
  return false;
@@ -32,7 +37,10 @@ function cleanForChat(raw) {
32
37
  if (!/[a-zA-Z0-9]/.test(trimmed))
33
38
  return false;
34
39
  return true;
35
- }).map((line) => line.replace(/ {2,}/g, " ").trim()).filter((line) => line.length > 0).join("\n").replace(/\n{3,}/g, "\n\n").trim();
40
+ }).map((line) => line.replace(/ {2,}/g, " ").trim()).filter((line) => line.length > 0).join(`
41
+ `).replace(/\n{3,}/g, `
42
+
43
+ `).trim();
36
44
  }
37
45
  function extractCompletionSummary(raw) {
38
46
  const stripped = applyAnsiStrip(raw);
@@ -57,7 +65,8 @@ function extractCompletionSummary(raw) {
57
65
  for (const m of diffStat)
58
66
  lines.push(m.trim());
59
67
  }
60
- return lines.join("\n");
68
+ return lines.join(`
69
+ `);
61
70
  }
62
71
  function extractDevServerUrl(raw) {
63
72
  const stripped = applyAnsiStrip(raw);
@@ -71,7 +80,8 @@ function captureTaskResponse(sessionId, buffers, markers) {
71
80
  return "";
72
81
  const responseLines = buffer.slice(marker);
73
82
  markers.delete(sessionId);
74
- return cleanForChat(responseLines.join("\n"));
83
+ return cleanForChat(responseLines.join(`
84
+ `));
75
85
  }
76
86
  var CURSOR_MOVEMENT, CURSOR_POSITION, ERASE, OSC, ALL_ANSI, CONTROL_CHARS, ORPHAN_SGR, LONG_SPACES, TUI_DECORATIVE, LOADING_LINE, STATUS_LINE;
77
87
  var init_ansi_utils = __esm(() => {
@@ -90,54 +100,208 @@ var init_ansi_utils = __esm(() => {
90
100
 
91
101
  // src/services/swarm-coordinator-prompts.ts
92
102
  function buildCoordinationPrompt(taskCtx, promptText, recentOutput, decisionHistory) {
93
- const historySection = decisionHistory.length > 0 ? `\nPrevious decisions for this session:\n${decisionHistory.slice(-5).map((d, i) => ` ${i + 1}. [${d.event}] prompt="${d.promptText}" \u2192 ${d.action}${d.response ? ` ("${d.response}")` : ""} \u2014 ${d.reasoning}`).join("\n")}\n` : "";
94
- return `You are Milady, an AI orchestrator managing a swarm of coding agents. ` + `A ${taskCtx.agentType} coding agent ("${taskCtx.label}", session: ${taskCtx.sessionId}) ` + `is blocked and waiting for input.\n\n` + `Original task: "${taskCtx.originalTask}"\n` + `Working directory: ${taskCtx.workdir}\n` + `Repository: ${taskCtx.repo ?? "none (scratch directory)"}\n` + historySection + `\nRecent terminal output (last 50 lines):\n` + `---\n${recentOutput.slice(-3000)}\n---\n\n` + `The agent is showing this blocking prompt:\n` + `"${promptText}"\n\n` + `Decide how to respond. Your options:\n\n` + `1. "respond" \u2014 Send a response to unblock the agent. For text prompts (Y/n, questions), ` + `set "response" to the text to send. For TUI menus or interactive prompts that need ` + `special keys, set "useKeys": true and "keys" to the key sequence ` + `(e.g. ["enter"], ["down","enter"], ["y","enter"]).\n\n` + `2. "complete" \u2014 The original task has been fulfilled. The agent has finished its work ` + `(e.g. code written, PR created, tests passed) and is back at the idle prompt. ` + `Use this when the terminal output shows the task objectives have been met.\n\n` + `3. "escalate" \u2014 The prompt requires human judgment (e.g. design decisions, ` + `ambiguous requirements, security-sensitive actions). Do NOT respond yourself.\n\n` + `4. "ignore" \u2014 The prompt is not actually blocking or is already being handled.
103
+ const historySection = decisionHistory.length > 0 ? `
104
+ Previous decisions for this session:
105
+ ${decisionHistory.slice(-5).map((d, i) => ` ${i + 1}. [${d.event}] prompt="${d.promptText}" → ${d.action}${d.response ? ` ("${d.response}")` : ""} — ${d.reasoning}`).join(`
106
+ `)}
107
+ ` : "";
108
+ return `You are Milady, an AI orchestrator managing a swarm of coding agents. ` + `A ${taskCtx.agentType} coding agent ("${taskCtx.label}", session: ${taskCtx.sessionId}) ` + `is blocked and waiting for input.
109
+
110
+ ` + `Original task: "${taskCtx.originalTask}"
111
+ ` + `Working directory: ${taskCtx.workdir}
112
+ ` + `Repository: ${taskCtx.repo ?? "none (scratch directory)"}
113
+ ` + historySection + `
114
+ Recent terminal output (last 50 lines):
115
+ ` + `---
116
+ ${recentOutput.slice(-3000)}
117
+ ---
118
+
119
+ ` + `The agent is showing this blocking prompt:
120
+ ` + `"${promptText}"
121
+
122
+ ` + `Decide how to respond. Your options:
123
+
124
+ ` + `1. "respond" — Send a response to unblock the agent. For text prompts (Y/n, questions), ` + `set "response" to the text to send. For TUI menus or interactive prompts that need ` + `special keys, set "useKeys": true and "keys" to the key sequence ` + `(e.g. ["enter"], ["down","enter"], ["y","enter"]).
125
+
126
+ ` + `2. "complete" — The original task has been fulfilled. The agent has finished its work ` + `(e.g. code written, PR created, tests passed) and is back at the idle prompt. ` + `Use this when the terminal output shows the task objectives have been met.
95
127
 
96
- ` + `Guidelines:\n` + `- IMPORTANT: If the prompt asks to approve access to files or directories OUTSIDE the working ` + `directory (${taskCtx.workdir}), DECLINE the request and REDIRECT the agent. Do NOT approve ` + `access to paths like /etc, ~/.ssh, ~/, /tmp, or any path that doesn't start with the working ` + `directory. Instead, respond with "n" (or the decline option) and tell the agent: ` + `"That path is outside your workspace. Use ${taskCtx.workdir} instead \u2014 ` + `create any files or directories you need there." This keeps the agent moving without ` + `granting out-of-scope access. The coordinator will also notify the human in case ` + `broader access was intended.\n` + `- For tool approval prompts (file writes, shell commands, etc.), respond "y" or use keys:["enter"] to approve.\n` + `- For Y/n confirmations that align with the original task, respond "y".\n` + `- For design questions or choices that could go either way, escalate.\n` + `- For error recovery prompts, try to respond if the path forward is clear.\n` + `- If the output shows a PR was just created (e.g. "Created pull request #N"), do NOT use "complete" yet. ` + `Instead respond with "Review your PR, run each test plan item to verify it works, update the PR to check off each item, then confirm all items pass".\n` + `- Only use "complete" if the agent confirmed it verified ALL test plan items after creating the PR.\n` + `- 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 \u2014 the human must provide it.
97
- ` + `- When in doubt, escalate \u2014 it's better to ask the human than to make a wrong choice.
128
+ ` + `3. "escalate" The prompt requires human judgment (e.g. design decisions, ` + `ambiguous requirements, security-sensitive actions). Do NOT respond yourself.
98
129
 
99
- ` + `Respond with ONLY a JSON object:\n` + `{"action": "respond|complete|escalate|ignore", "response": "...", "useKeys": false, "keys": [], "reasoning": "..."}`;
130
+ ` + `4. "ignore" The prompt is not actually blocking or is already being handled.
131
+
132
+ ` + `Guidelines:
133
+ ` + `- IMPORTANT: If the prompt asks to approve access to files or directories OUTSIDE the working ` + `directory (${taskCtx.workdir}), DECLINE the request and REDIRECT the agent. Do NOT approve ` + `access to paths like /etc, ~/.ssh, ~/, /tmp, or any path that doesn't start with the working ` + `directory. Instead, respond with "n" (or the decline option) and tell the agent: ` + `"That path is outside your workspace. Use ${taskCtx.workdir} instead — ` + `create any files or directories you need there." This keeps the agent moving without ` + `granting out-of-scope access. The coordinator will also notify the human in case ` + `broader access was intended.
134
+ ` + `- For tool approval prompts (file writes, shell commands, etc.), respond "y" or use keys:["enter"] to approve.
135
+ ` + `- For Y/n confirmations that align with the original task, respond "y".
136
+ ` + `- For design questions or choices that could go either way, escalate.
137
+ ` + `- For error recovery prompts, try to respond if the path forward is clear.
138
+ ` + `- If the output shows a PR was just created (e.g. "Created pull request #N"), do NOT use "complete" yet. ` + `Instead respond with "Review your PR, run each test plan item to verify it works, update the PR to check off each item, then confirm all items pass".
139
+ ` + `- Only use "complete" if the agent confirmed it verified ALL test plan items after creating the PR.
140
+ ` + `- 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
+ ` + `- When in doubt, escalate — it's better to ask the human than to make a wrong choice.
142
+
143
+ ` + `Respond with ONLY a JSON object:
144
+ ` + `{"action": "respond|complete|escalate|ignore", "response": "...", "useKeys": false, "keys": [], "reasoning": "..."}`;
100
145
  }
101
146
  function buildIdleCheckPrompt(taskCtx, recentOutput, idleMinutes, idleCheckNumber, maxIdleChecks, decisionHistory) {
102
- const historySection = decisionHistory.length > 0 ? `\nPrevious decisions for this session:\n${decisionHistory.slice(-5).map((d, i) => ` ${i + 1}. [${d.event}] prompt="${d.promptText}" \u2192 ${d.action}${d.response ? ` ("${d.response}")` : ""} \u2014 ${d.reasoning}`).join("\n")}\n` : "";
103
- return `You are Milady, an AI orchestrator managing a swarm of coding agents. ` + `A ${taskCtx.agentType} coding agent ("${taskCtx.label}", session: ${taskCtx.sessionId}) ` + `has been idle for ${idleMinutes} minutes with no events or output changes.\n\n` + `Original task: "${taskCtx.originalTask}"\n` + `Working directory: ${taskCtx.workdir}\n` + `Repository: ${taskCtx.repo ?? "none (scratch directory)"}\n` + `Idle check: ${idleCheckNumber} of ${maxIdleChecks} (session will be force-escalated after ${maxIdleChecks})\n` + historySection + `\nRecent terminal output (last 50 lines):\n` + `---\n${recentOutput.slice(-3000)}\n---\n\n` + `The session has gone silent. Analyze the terminal output and decide:\n\n` + `1. "complete" \u2014 The task is FULLY done. ALL objectives in the original task were met ` + `AND the final deliverable is visible in the output (e.g. a PR URL was printed, or the ` + `task explicitly did not require a PR). The agent is back at the idle prompt.\n\n` + `2. "respond" \u2014 The agent appears stuck or waiting for input that wasn't detected ` + `as a blocking prompt. Send a message to nudge it (e.g. "continue", or answer a question ` + `visible in the output). If code was committed but no PR was created yet, respond with ` + `"please create a pull request with your changes" or similar.\n\n` + `3. "escalate" \u2014 Something looks wrong or unclear. The human should review.
147
+ const historySection = decisionHistory.length > 0 ? `
148
+ Previous decisions for this session:
149
+ ${decisionHistory.slice(-5).map((d, i) => ` ${i + 1}. [${d.event}] prompt="${d.promptText}" → ${d.action}${d.response ? ` ("${d.response}")` : ""} — ${d.reasoning}`).join(`
150
+ `)}
151
+ ` : "";
152
+ return `You are Milady, an AI orchestrator managing a swarm of coding agents. ` + `A ${taskCtx.agentType} coding agent ("${taskCtx.label}", session: ${taskCtx.sessionId}) ` + `has been idle for ${idleMinutes} minutes with no events or output changes.
153
+
154
+ ` + `Original task: "${taskCtx.originalTask}"
155
+ ` + `Working directory: ${taskCtx.workdir}
156
+ ` + `Repository: ${taskCtx.repo ?? "none (scratch directory)"}
157
+ ` + `Idle check: ${idleCheckNumber} of ${maxIdleChecks} (session will be force-escalated after ${maxIdleChecks})
158
+ ` + historySection + `
159
+ Recent terminal output (last 50 lines):
160
+ ` + `---
161
+ ${recentOutput.slice(-3000)}
162
+ ---
163
+
164
+ ` + `The session has gone silent. Analyze the terminal output and decide:
165
+
166
+ ` + `1. "complete" — The task is FULLY done. ALL objectives in the original task were met ` + `AND the final deliverable is visible in the output (e.g. a PR URL was printed, or the ` + `task explicitly did not require a PR). The agent is back at the idle prompt.
167
+
168
+ ` + `2. "respond" — The agent appears stuck or waiting for input that wasn't detected ` + `as a blocking prompt. Send a message to nudge it (e.g. "continue", or answer a question ` + `visible in the output). If code was committed but no PR was created yet, respond with ` + `"please create a pull request with your changes" or similar.
104
169
 
105
- ` + `4. "ignore" \u2014 The agent is still actively working (e.g. compiling, running tests, ` + `pushing to remote, creating a PR). The idle period is expected and it will produce output soon.\n\n` + `Guidelines:\n` + `- IMPORTANT: Do NOT mark "complete" if the original task involves creating a PR and no PR URL ` + `(e.g. github.com/...pull/...) appears in the output. Instead use "respond" to nudge the agent ` + `to create the PR.\n` + `- Do NOT mark "complete" just because code was committed \u2014 commits alone don't finish a task ` + `that requires a PR.\n` + `- Network operations (git push, gh pr create, API calls) can cause several minutes of silence \u2014 ` + `prefer "ignore" for early idle checks if the agent was mid-workflow.\n` + `- If the output ends with a command prompt (\$ or >) and ALL task objectives are confirmed met, use "complete".\n` + `- If the output shows an error or the agent seems stuck in a loop, escalate.\n` + `- If the agent is clearly mid-operation (build output, test runner, git operations), use "ignore".\n` + `- On check ${idleCheckNumber} of ${maxIdleChecks} \u2014 if unsure, lean toward "respond" with a nudge rather than "complete".
170
+ ` + `3. "escalate" Something looks wrong or unclear. The human should review.
106
171
 
107
- ` + `Respond with ONLY a JSON object:\n` + `{"action": "respond|complete|escalate|ignore", "response": "...", "useKeys": false, "keys": [], "reasoning": "..."}`;
172
+ ` + `4. "ignore" The agent is still actively working (e.g. compiling, running tests, ` + `pushing to remote, creating a PR). The idle period is expected and it will produce output soon.
173
+
174
+ ` + `Guidelines:
175
+ ` + `- IMPORTANT: Do NOT mark "complete" if the original task involves creating a PR and no PR URL ` + `(e.g. github.com/...pull/...) appears in the output. Instead use "respond" to nudge the agent ` + `to create the PR.
176
+ ` + `- Do NOT mark "complete" just because code was committed — commits alone don't finish a task ` + `that requires a PR.
177
+ ` + `- Network operations (git push, gh pr create, API calls) can cause several minutes of silence — ` + `prefer "ignore" for early idle checks if the agent was mid-workflow.
178
+ ` + `- If the output ends with a command prompt ($ or >) and ALL task objectives are confirmed met, use "complete".
179
+ ` + `- If the output shows an error or the agent seems stuck in a loop, escalate.
180
+ ` + `- If the agent is clearly mid-operation (build output, test runner, git operations), use "ignore".
181
+ ` + `- On check ${idleCheckNumber} of ${maxIdleChecks} — if unsure, lean toward "respond" with a nudge rather than "complete".
182
+
183
+ ` + `Respond with ONLY a JSON object:
184
+ ` + `{"action": "respond|complete|escalate|ignore", "response": "...", "useKeys": false, "keys": [], "reasoning": "..."}`;
108
185
  }
109
186
  function buildTurnCompletePrompt(taskCtx, turnOutput, decisionHistory) {
110
- const historySection = decisionHistory.length > 0 ? `\nPrevious decisions for this session:\n${decisionHistory.slice(-5).map((d, i) => ` ${i + 1}. [${d.event}] prompt="${d.promptText}" \u2192 ${d.action}${d.response ? ` ("${d.response}")` : ""} \u2014 ${d.reasoning}`).join("\n")}\n` : "";
111
- return `You are Milady, an AI orchestrator managing a swarm of coding agents. ` + `A ${taskCtx.agentType} coding agent ("${taskCtx.label}", session: ${taskCtx.sessionId}) ` + `just finished a turn and is back at the idle prompt waiting for input.\n\n` + `Original task: "${taskCtx.originalTask}"\n` + `Working directory: ${taskCtx.workdir}\n` + `Repository: ${taskCtx.repo ?? "none (scratch directory)"}\n` + historySection + `\nOutput from this turn:\n` + `---\n${turnOutput.slice(-3000)}\n---\n\n` + `The agent completed a turn. Decide if the OVERALL task is done or if more work is needed.\n\n` + `IMPORTANT: Coding agents work in multiple turns. A single turn completing does NOT mean ` + `the task is done. You must verify that EVERY objective in the original task has been addressed ` + `in the output before declaring "complete".\n\n` + `Your options:\n\n` + `1. "respond" \u2014 The agent finished a step but the overall task is NOT done yet. ` + `Send a follow-up instruction to continue. Set "response" to the next instruction ` + `(e.g. "Now run the tests", "Create a PR with these changes", "Continue with the next part"). ` + `THIS IS THE DEFAULT \u2014 most turns are intermediate steps, not the final result.
187
+ const historySection = decisionHistory.length > 0 ? `
188
+ Previous decisions for this session:
189
+ ${decisionHistory.slice(-5).map((d, i) => ` ${i + 1}. [${d.event}] prompt="${d.promptText}" → ${d.action}${d.response ? ` ("${d.response}")` : ""} — ${d.reasoning}`).join(`
190
+ `)}
191
+ ` : "";
192
+ return `You are Milady, an AI orchestrator managing a swarm of coding agents. ` + `A ${taskCtx.agentType} coding agent ("${taskCtx.label}", session: ${taskCtx.sessionId}) ` + `just finished a turn and is back at the idle prompt waiting for input.
193
+
194
+ ` + `Original task: "${taskCtx.originalTask}"
195
+ ` + `Working directory: ${taskCtx.workdir}
196
+ ` + `Repository: ${taskCtx.repo ?? "none (scratch directory)"}
197
+ ` + historySection + `
198
+ Output from this turn:
199
+ ` + `---
200
+ ${turnOutput.slice(-3000)}
201
+ ---
202
+
203
+ ` + `The agent completed a turn. Decide if the OVERALL task is done or if more work is needed.
204
+
205
+ ` + `IMPORTANT: Coding agents work in multiple turns. A single turn completing does NOT mean ` + `the task is done. You must verify that EVERY objective in the original task has been addressed ` + `in the output before declaring "complete".
206
+
207
+ ` + `Your options:
208
+
209
+ ` + `1. "respond" — The agent finished a step but the overall task is NOT done yet. ` + `Send a follow-up instruction to continue. Set "response" to the next instruction ` + `(e.g. "Now run the tests", "Create a PR with these changes", "Continue with the next part"). ` + `THIS IS THE DEFAULT — most turns are intermediate steps, not the final result.
112
210
 
113
- ` + `2. "complete" \u2014 The original task objectives have ALL been fully met. For repo-based tasks, ` + `this means code was written, changes were committed, pushed, AND a pull request was created. ` + `Only use this when you can point to specific evidence in the output for EVERY objective ` + `(e.g. "Created pull request #N" in the output).\n\n` + `3. "escalate" \u2014 Something looks wrong or you're unsure whether the task is complete. ` + `Let the human decide.\n\n` + `4. "ignore" \u2014 Should not normally be used here.
211
+ ` + `2. "complete" The original task objectives have ALL been fully met. For repo-based tasks, ` + `this means code was written, changes were committed, pushed, AND a pull request was created. ` + `Only use this when you can point to specific evidence in the output for EVERY objective ` + `(e.g. "Created pull request #N" in the output).
114
212
 
115
- ` + `Guidelines:\n` + `- BEFORE choosing "complete", enumerate each objective from the original task and verify ` + `evidence in the output. If ANY objective lacks evidence, use "respond" with the missing work.\n` + `- A PR being created does NOT mean the task is done \u2014 check that the PR covers ALL requested changes.
116
- ` + `- If the task mentions multiple features/fixes, verify EACH one is addressed, not just the first.\n` + `- If the agent only analyzed code or read files, it hasn't done the actual work yet \u2014 send a follow-up.
117
- ` + `- If the agent wrote code but didn't test it and testing seems appropriate, ask it to run tests.\n` + `- If the output shows errors or failed tests, send a follow-up to fix them.\n` + `- IMPORTANT: If the working directory is a git repository clone (not a scratch dir), the agent ` + `MUST commit its changes, push them, and create a pull request before the task can be "complete". ` + `If the output only shows code edits with no git commit or PR, respond with "Now commit your changes, push, and create a pull request".\n` + `- IMPORTANT: Creating a PR is NOT the final step. If this is the turn where the PR was created ` + `(i.e. "Created pull request" or a PR URL appears for the FIRST time and no previous decision ` + `already sent a review follow-up), respond with "Review your PR, run each test plan item to verify ` + `it works, update the PR to check off each item, then confirm all items pass".\n` + `- If a previous decision ALREADY sent a review/verification follow-up (check the decision history), ` + `and the agent has now responded with its review results, you MAY mark "complete" if the agent ` + `indicates the work is done (e.g. "Done", "verified", "all checks pass", "Here's what I did", ` + `or a clear summary of completed work). Do NOT require exact phrases \u2014 use judgment.
118
- ` + `- Keep follow-up instructions concise and specific.\n` + `- 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.\n` + `- Default to "respond" \u2014 only use "complete" when you're certain ALL work is done.
213
+ ` + `3. "escalate" Something looks wrong or you're unsure whether the task is complete. ` + `Let the human decide.
119
214
 
120
- ` + `Respond with ONLY a JSON object:\n` + `{"action": "respond|complete|escalate|ignore", "response": "...", "useKeys": false, "keys": [], "reasoning": "..."}`;
215
+ ` + `4. "ignore" Should not normally be used here.
216
+
217
+ ` + `Guidelines:
218
+ ` + `- BEFORE choosing "complete", enumerate each objective from the original task and verify ` + `evidence in the output. If ANY objective lacks evidence, use "respond" with the missing work.
219
+ ` + `- A PR being created does NOT mean the task is done — check that the PR covers ALL requested changes.
220
+ ` + `- If the task mentions multiple features/fixes, verify EACH one is addressed, not just the first.
221
+ ` + `- If the agent only analyzed code or read files, it hasn't done the actual work yet — send a follow-up.
222
+ ` + `- If the agent wrote code but didn't test it and testing seems appropriate, ask it to run tests.
223
+ ` + `- If the output shows errors or failed tests, send a follow-up to fix them.
224
+ ` + `- IMPORTANT: If the working directory is a git repository clone (not a scratch dir), the agent ` + `MUST commit its changes, push them, and create a pull request before the task can be "complete". ` + `If the output only shows code edits with no git commit or PR, respond with "Now commit your changes, push, and create a pull request".
225
+ ` + `- IMPORTANT: Creating a PR is NOT the final step. If this is the turn where the PR was created ` + `(i.e. "Created pull request" or a PR URL appears for the FIRST time and no previous decision ` + `already sent a review follow-up), respond with "Review your PR, run each test plan item to verify ` + `it works, update the PR to check off each item, then confirm all items pass".
226
+ ` + `- If a previous decision ALREADY sent a review/verification follow-up (check the decision history), ` + `and the agent has now responded with its review results, you MAY mark "complete" if the agent ` + `indicates the work is done (e.g. "Done", "verified", "all checks pass", "Here's what I did", ` + `or a clear summary of completed work). Do NOT require exact phrases — use judgment.
227
+ ` + `- Keep follow-up instructions concise and specific.
228
+ ` + `- 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
+ ` + `- Default to "respond" — only use "complete" when you're certain ALL work is done.
230
+
231
+ ` + `Respond with ONLY a JSON object:
232
+ ` + `{"action": "respond|complete|escalate|ignore", "response": "...", "useKeys": false, "keys": [], "reasoning": "..."}`;
121
233
  }
122
234
  function buildBlockedEventMessage(taskCtx, promptText, recentOutput, decisionHistory) {
123
- const historySection = decisionHistory.length > 0 ? `\nPrevious decisions:\n${decisionHistory.slice(-5).map((d, i) => ` ${i + 1}. [${d.event}] "${d.promptText}" \u2192 ${d.action}${d.response ? ` ("${d.response}")` : ""} \u2014 ${d.reasoning}`).join("\n")}\n` : "";
124
- return `[Coding Agent Event] A ${taskCtx.agentType} agent ("${taskCtx.label}") is blocked and waiting for input.\n\n` + `Task: "${taskCtx.originalTask}"\n` + `Workdir: ${taskCtx.workdir}\n` + `Repo: ${taskCtx.repo ?? "none (scratch directory)"}\n` + historySection + `\nRecent terminal output:\n---\n${recentOutput.slice(-3000)}\n---\n\n` + `Blocking prompt: "${promptText}"\n\n` + `Decide how to handle this. Options:\n` + `- "respond" \u2014 send text or keys to unblock the agent
125
- ` + `- "complete" \u2014 the task is fully done
126
- ` + `- "escalate" \u2014 you need the user's input
127
- ` + `- "ignore" \u2014 not actually blocking
235
+ const historySection = decisionHistory.length > 0 ? `
236
+ Previous decisions:
237
+ ${decisionHistory.slice(-5).map((d, i) => ` ${i + 1}. [${d.event}] "${d.promptText}" ${d.action}${d.response ? ` ("${d.response}")` : ""} — ${d.reasoning}`).join(`
238
+ `)}
239
+ ` : "";
240
+ return `[Coding Agent Event] A ${taskCtx.agentType} agent ("${taskCtx.label}") is blocked and waiting for input.
241
+
242
+ ` + `Task: "${taskCtx.originalTask}"
243
+ ` + `Workdir: ${taskCtx.workdir}
244
+ ` + `Repo: ${taskCtx.repo ?? "none (scratch directory)"}
245
+ ` + historySection + `
246
+ Recent terminal output:
247
+ ---
248
+ ${recentOutput.slice(-3000)}
249
+ ---
250
+
251
+ ` + `Blocking prompt: "${promptText}"
252
+
253
+ ` + `Decide how to handle this. Options:
254
+ ` + `- "respond" — send text or keys to unblock the agent
255
+ ` + `- "complete" — the task is fully done
256
+ ` + `- "escalate" — you need the user's input
257
+ ` + `- "ignore" — not actually blocking
258
+
259
+ ` + `Guidelines:
260
+ ` + `- For tool approvals / Y/n that align with the task, respond "y" or keys:["enter"].
261
+ ` + `- If the prompt asks for info NOT in the original task, escalate.
262
+ ` + `- Decline access to paths outside ${taskCtx.workdir}.
263
+ ` + `- If a PR was just created, respond to review & verify test plan items before completing.
264
+ ` + `- When in doubt, escalate.
128
265
 
129
- ` + `Guidelines:\n` + `- For tool approvals / Y/n that align with the task, respond "y" or keys:["enter"].\n` + `- If the prompt asks for info NOT in the original task, escalate.\n` + `- Decline access to paths outside ${taskCtx.workdir}.\n` + `- If a PR was just created, respond to review & verify test plan items before completing.\n` + `- When in doubt, escalate.\n\n` + `Include a JSON action block at the end of your response:\n` + "```json\n" + `{"action": "respond|complete|escalate|ignore", "response": "...", "useKeys": false, "keys": [], "reasoning": "..."}\n` + "```";
266
+ ` + `Include a JSON action block at the end of your response:
267
+ ` + "```json\n" + `{"action": "respond|complete|escalate|ignore", "response": "...", "useKeys": false, "keys": [], "reasoning": "..."}
268
+ ` + "```";
130
269
  }
131
270
  function buildTurnCompleteEventMessage(taskCtx, turnOutput, decisionHistory) {
132
- const historySection = decisionHistory.length > 0 ? `\nPrevious decisions:\n${decisionHistory.slice(-5).map((d, i) => ` ${i + 1}. [${d.event}] "${d.promptText}" \u2192 ${d.action}${d.response ? ` ("${d.response}")` : ""} \u2014 ${d.reasoning}`).join("\n")}\n` : "";
133
- return `[Coding Agent Event] A ${taskCtx.agentType} agent ("${taskCtx.label}") just finished a turn and is idle.\n\n` + `Task: "${taskCtx.originalTask}"\n` + `Workdir: ${taskCtx.workdir}\n` + `Repo: ${taskCtx.repo ?? "none (scratch directory)"}\n` + historySection + `\nTurn output:\n---\n${turnOutput.slice(-3000)}\n---\n\n` + `Decide if the overall task is done or if the agent needs more work.\n\n` + `Options:\n` + `- "respond" \u2014 send a follow-up instruction (DEFAULT for intermediate steps)
134
- ` + `- "complete" \u2014 ALL task objectives met (code written, committed, PR created & verified)
135
- ` + `- "escalate" \u2014 something looks wrong, ask the user
136
- ` + `- "ignore" \u2014 should not normally be used here
271
+ const historySection = decisionHistory.length > 0 ? `
272
+ Previous decisions:
273
+ ${decisionHistory.slice(-5).map((d, i) => ` ${i + 1}. [${d.event}] "${d.promptText}" ${d.action}${d.response ? ` ("${d.response}")` : ""} ${d.reasoning}`).join(`
274
+ `)}
275
+ ` : "";
276
+ return `[Coding Agent Event] A ${taskCtx.agentType} agent ("${taskCtx.label}") just finished a turn and is idle.
137
277
 
138
- ` + `Guidelines:\n` + `- Verify evidence for EVERY objective before using "complete".\n` + `- If code was written but not committed/pushed/PR'd, respond with next step.\n` + `- If a PR was just created, respond to review & verify test plan items.\n` + `- When asking agents to verify work, prefer CLI tools (gh, curl, cat, etc.) over browser automation.\n` + `- Default to "respond" \u2014 only "complete" when certain ALL work is done.
278
+ ` + `Task: "${taskCtx.originalTask}"
279
+ ` + `Workdir: ${taskCtx.workdir}
280
+ ` + `Repo: ${taskCtx.repo ?? "none (scratch directory)"}
281
+ ` + historySection + `
282
+ Turn output:
283
+ ---
284
+ ${turnOutput.slice(-3000)}
285
+ ---
139
286
 
140
- ` + `Include a JSON action block at the end of your response:\n` + "```json\n" + `{"action": "respond|complete|escalate|ignore", "response": "...", "useKeys": false, "keys": [], "reasoning": "..."}\n` + "```";
287
+ ` + `Decide if the overall task is done or if the agent needs more work.
288
+
289
+ ` + `Options:
290
+ ` + `- "respond" — send a follow-up instruction (DEFAULT for intermediate steps)
291
+ ` + `- "complete" — ALL task objectives met (code written, committed, PR created & verified)
292
+ ` + `- "escalate" — something looks wrong, ask the user
293
+ ` + `- "ignore" — should not normally be used here
294
+
295
+ ` + `Guidelines:
296
+ ` + `- Verify evidence for EVERY objective before using "complete".
297
+ ` + `- If code was written but not committed/pushed/PR'd, respond with next step.
298
+ ` + `- If a PR was just created, respond to review & verify test plan items.
299
+ ` + `- When asking agents to verify work, prefer CLI tools (gh, curl, cat, etc.) over browser automation.
300
+ ` + `- Default to "respond" — only "complete" when certain ALL work is done.
301
+
302
+ ` + `Include a JSON action block at the end of your response:
303
+ ` + "```json\n" + `{"action": "respond|complete|escalate|ignore", "response": "...", "useKeys": false, "keys": [], "reasoning": "..."}
304
+ ` + "```";
141
305
  }
142
306
  function parseCoordinationResponse(llmOutput) {
143
307
  const jsonMatch = llmOutput.match(/\{[\s\S]*\}/);
@@ -169,7 +333,7 @@ function parseCoordinationResponse(llmOutput) {
169
333
  }
170
334
 
171
335
  // src/services/swarm-event-triage.ts
172
- import {ModelType as ModelType2} from "@elizaos/core";
336
+ import { ModelType as ModelType2 } from "@elizaos/core";
173
337
  function classifyByHeuristic(ctx) {
174
338
  if (ctx.promptType) {
175
339
  if (ROUTINE_PROMPT_TYPES.has(ctx.promptType))
@@ -196,8 +360,17 @@ function classifyByHeuristic(ctx) {
196
360
  return null;
197
361
  }
198
362
  function buildTriagePrompt(ctx) {
199
- const eventDesc = ctx.eventType === "blocked" ? `BLOCKED prompt: "${ctx.promptText.slice(0, 300)}"` : `TURN COMPLETE. Recent output:\n${(ctx.recentOutput ?? "").slice(-500)}`;
200
- return `Classify this coding agent event as "routine" or "creative".\n\n` + `Task: ${ctx.originalTask.slice(0, 200)}\n` + `Event: ${eventDesc}\n\n` + `"routine" = simple approval, permission, config, yes/no, tool consent, obvious pass/fail.\n` + `"creative" = needs task context, error recovery, design choice, ambiguous situation, approach selection.\n\n` + `Respond with ONLY a JSON object: {"tier": "routine"} or {"tier": "creative"}`;
363
+ const eventDesc = ctx.eventType === "blocked" ? `BLOCKED prompt: "${ctx.promptText.slice(0, 300)}"` : `TURN COMPLETE. Recent output:
364
+ ${(ctx.recentOutput ?? "").slice(-500)}`;
365
+ return `Classify this coding agent event as "routine" or "creative".
366
+
367
+ ` + `Task: ${ctx.originalTask.slice(0, 200)}
368
+ ` + `Event: ${eventDesc}
369
+
370
+ ` + `"routine" = simple approval, permission, config, yes/no, tool consent, obvious pass/fail.
371
+ ` + `"creative" = needs task context, error recovery, design choice, ambiguous situation, approach selection.
372
+
373
+ ` + `Respond with ONLY a JSON object: {"tier": "routine"} or {"tier": "creative"}`;
201
374
  }
202
375
  function parseTriageResponse(llmOutput) {
203
376
  const matches = llmOutput.matchAll(/\{[\s\S]*?\}/g);
@@ -207,15 +380,14 @@ function parseTriageResponse(llmOutput) {
207
380
  if (parsed.tier === "routine" || parsed.tier === "creative") {
208
381
  return parsed.tier;
209
382
  }
210
- } catch {
211
- }
383
+ } catch {}
212
384
  }
213
385
  return null;
214
386
  }
215
387
  async function classifyEventTier(runtime, ctx, log) {
216
388
  const heuristicResult = classifyByHeuristic(ctx);
217
389
  if (heuristicResult) {
218
- log(`Triage: heuristic \u2192 ${heuristicResult}`);
390
+ log(`Triage: heuristic ${heuristicResult}`);
219
391
  return heuristicResult;
220
392
  }
221
393
  try {
@@ -223,12 +395,12 @@ async function classifyEventTier(runtime, ctx, log) {
223
395
  const result = await runtime.useModel(ModelType2.TEXT_SMALL, { prompt });
224
396
  const tier = parseTriageResponse(result);
225
397
  if (tier) {
226
- log(`Triage: LLM \u2192 ${tier}`);
398
+ log(`Triage: LLM ${tier}`);
227
399
  return tier;
228
400
  }
229
- log(`Triage: LLM returned unparseable response \u2014 defaulting to creative`);
401
+ log(`Triage: LLM returned unparseable response defaulting to creative`);
230
402
  } catch (err) {
231
- log(`Triage: LLM classifier failed: ${err} \u2014 defaulting to creative`);
403
+ log(`Triage: LLM classifier failed: ${err} defaulting to creative`);
232
404
  }
233
405
  return "creative";
234
406
  }
@@ -298,7 +470,7 @@ __export(exports_swarm_decision_loop, {
298
470
  checkAllTasksComplete: () => checkAllTasksComplete
299
471
  });
300
472
  import * as path from "node:path";
301
- import {ModelType as ModelType3} from "@elizaos/core";
473
+ import { ModelType as ModelType3 } from "@elizaos/core";
302
474
  function toContextSummary(taskCtx) {
303
475
  return {
304
476
  sessionId: taskCtx.sessionId,
@@ -422,9 +594,10 @@ async function executeDecision(ctx, sessionId, decision) {
422
594
  try {
423
595
  const rawOutput = await ctx.ptyService.getSessionOutput(sessionId, 50);
424
596
  summary = extractCompletionSummary(rawOutput);
425
- } catch {
426
- }
427
- ctx.sendChatMessage(summary ? `Finished "${taskCtx?.label ?? sessionId}".\n\n${summary}` : `Finished "${taskCtx?.label ?? sessionId}".`, "coding-agent");
597
+ } catch {}
598
+ ctx.sendChatMessage(summary ? `Finished "${taskCtx?.label ?? sessionId}".
599
+
600
+ ${summary}` : `Finished "${taskCtx?.label ?? sessionId}".`, "coding-agent");
428
601
  ctx.ptyService.stopSession(sessionId, true).catch((err) => {
429
602
  ctx.log(`Failed to stop session after LLM-detected completion: ${err}`);
430
603
  });
@@ -541,7 +714,7 @@ async function handleBlocked(ctx, sessionId, taskCtx, data) {
541
714
  event: "blocked",
542
715
  promptText,
543
716
  decision: "escalate",
544
- reasoning: "Supervision level is notify \u2014 broadcasting only"
717
+ reasoning: "Supervision level is notify broadcasting only"
545
718
  });
546
719
  break;
547
720
  }
@@ -553,7 +726,7 @@ async function handleTurnComplete(ctx, sessionId, taskCtx, data) {
553
726
  }
554
727
  ctx.inFlightDecisions.add(sessionId);
555
728
  try {
556
- ctx.log(`Turn complete for "${taskCtx.label}" \u2014 assessing whether task is done`);
729
+ ctx.log(`Turn complete for "${taskCtx.label}" assessing whether task is done`);
557
730
  const rawResponse = data.response ?? "";
558
731
  let turnOutput = cleanForChat(rawResponse);
559
732
  if (!turnOutput) {
@@ -588,7 +761,7 @@ async function handleTurnComplete(ctx, sessionId, taskCtx, data) {
588
761
  if (decision)
589
762
  decisionFromPipeline = true;
590
763
  } catch (err) {
591
- ctx.log(`Agent decision callback failed for turn-complete: ${err} \u2014 falling back to small LLM`);
764
+ ctx.log(`Agent decision callback failed for turn-complete: ${err} falling back to small LLM`);
592
765
  }
593
766
  }
594
767
  if (!decision) {
@@ -604,13 +777,13 @@ async function handleTurnComplete(ctx, sessionId, taskCtx, data) {
604
777
  }
605
778
  }
606
779
  if (!decision) {
607
- ctx.log(`Turn-complete for "${taskCtx.label}": all decision paths failed \u2014 escalating`);
780
+ ctx.log(`Turn-complete for "${taskCtx.label}": all decision paths failed escalating`);
608
781
  decision = {
609
782
  action: "escalate",
610
- reasoning: "All decision paths returned invalid response \u2014 escalating for human review"
783
+ reasoning: "All decision paths returned invalid response escalating for human review"
611
784
  };
612
785
  }
613
- ctx.log(`Turn assessment for "${taskCtx.label}": ${decision.action}${decision.action === "respond" ? ` \u2192 "${(decision.response ?? "").slice(0, 80)}"` : ""} \u2014 ${decision.reasoning.slice(0, 120)}`);
786
+ ctx.log(`Turn assessment for "${taskCtx.label}": ${decision.action}${decision.action === "respond" ? ` "${(decision.response ?? "").slice(0, 80)}"` : ""} ${decision.reasoning.slice(0, 120)}`);
614
787
  taskCtx.decisions.push({
615
788
  timestamp: Date.now(),
616
789
  event: "turn_complete",
@@ -634,7 +807,7 @@ async function handleTurnComplete(ctx, sessionId, taskCtx, data) {
634
807
  const preview = instruction.length > 120 ? `${instruction.slice(0, 120)}...` : instruction;
635
808
  ctx.sendChatMessage(`[${taskCtx.label}] Turn done, continuing: ${preview}`, "coding-agent");
636
809
  } else if (decision.action === "escalate") {
637
- ctx.sendChatMessage(`[${taskCtx.label}] Turn finished \u2014 needs your attention: ${decision.reasoning}`, "coding-agent");
810
+ ctx.sendChatMessage(`[${taskCtx.label}] Turn finished needs your attention: ${decision.reasoning}`, "coding-agent");
638
811
  }
639
812
  }
640
813
  await executeDecision(ctx, sessionId, decision);
@@ -674,7 +847,7 @@ async function handleAutonomousDecision(ctx, sessionId, taskCtx, promptText, rec
674
847
  if (decision)
675
848
  decisionFromPipeline = true;
676
849
  } catch (err) {
677
- ctx.log(`Agent decision callback failed: ${err} \u2014 falling back to small LLM`);
850
+ ctx.log(`Agent decision callback failed: ${err} falling back to small LLM`);
678
851
  }
679
852
  }
680
853
  if (!decision) {
@@ -703,7 +876,7 @@ async function handleAutonomousDecision(ctx, sessionId, taskCtx, promptText, rec
703
876
  if (decision.action === "respond" && isOutOfScopeAccess(promptText, taskCtx.workdir)) {
704
877
  decision = {
705
878
  action: "respond",
706
- response: `No \u2014 that path is outside your workspace. Use ${taskCtx.workdir} instead. Create any files or directories you need there.`,
879
+ response: `No that path is outside your workspace. Use ${taskCtx.workdir} instead. Create any files or directories you need there.`,
707
880
  reasoning: `Declined out-of-scope access (outside ${taskCtx.workdir}) and redirected agent to workspace.`
708
881
  };
709
882
  ctx.sendChatMessage(`[${taskCtx.label}] Declined out-of-scope access and redirected to workspace (${taskCtx.workdir}). If you intended broader access, send the agent an override.`, "coding-agent");
@@ -733,7 +906,7 @@ async function handleAutonomousDecision(ctx, sessionId, taskCtx, promptText, rec
733
906
  if (decision.action === "respond") {
734
907
  const actionDesc = decision.useKeys ? `Sent keys: ${decision.keys?.join(", ")}` : decision.response ? `Responded: ${decision.response.length > 100 ? `${decision.response.slice(0, 100)}...` : decision.response}` : "Responded";
735
908
  const reasonExcerpt = decision.reasoning.length > 150 ? `${decision.reasoning.slice(0, 150)}...` : decision.reasoning;
736
- ctx.sendChatMessage(`[${taskCtx.label}] ${actionDesc} \u2014 ${reasonExcerpt}`, "coding-agent");
909
+ ctx.sendChatMessage(`[${taskCtx.label}] ${actionDesc} ${reasonExcerpt}`, "coding-agent");
737
910
  } else if (decision.action === "escalate") {
738
911
  ctx.sendChatMessage(`[${taskCtx.label}] Needs your attention: ${decision.reasoning}`, "coding-agent");
739
912
  }
@@ -773,7 +946,7 @@ async function handleConfirmDecision(ctx, sessionId, taskCtx, promptText, recent
773
946
  if (decision)
774
947
  decisionFromPipeline = true;
775
948
  } catch (err) {
776
- ctx.log(`Agent decision callback failed (confirm): ${err} \u2014 falling back to small LLM`);
949
+ ctx.log(`Agent decision callback failed (confirm): ${err} falling back to small LLM`);
777
950
  }
778
951
  }
779
952
  if (!decision) {
@@ -787,7 +960,7 @@ async function handleConfirmDecision(ctx, sessionId, taskCtx, promptText, recent
787
960
  recentOutput: output,
788
961
  llmDecision: {
789
962
  action: "escalate",
790
- reasoning: "All decision paths returned invalid response \u2014 needs human review"
963
+ reasoning: "All decision paths returned invalid response needs human review"
791
964
  },
792
965
  taskContext: taskCtx,
793
966
  createdAt: Date.now()
@@ -911,7 +1084,9 @@ var finalizeWorkspaceAction = {
911
1084
  data: { workspaceId, status }
912
1085
  };
913
1086
  }
914
- const commitMessage = content.commitMessage ?? `feat: automated changes from coding agent\n\nGenerated by Milady coding agent plugin.`;
1087
+ const commitMessage = content.commitMessage ?? `feat: automated changes from coding agent
1088
+
1089
+ Generated by Milady coding agent plugin.`;
915
1090
  const commitHash = await workspaceService.commit(workspaceId, {
916
1091
  message: commitMessage,
917
1092
  all: true
@@ -920,7 +1095,15 @@ var finalizeWorkspaceAction = {
920
1095
  let prInfo = null;
921
1096
  if (!content.skipPR) {
922
1097
  const prTitle = content.prTitle ?? `[Milady] ${workspace.branch}`;
923
- const prBody = content.prBody ?? `## Summary\n\nAutomated changes generated by Milady coding agent.\n\n` + `**Branch:** ${workspace.branch}\n` + `**Commit:** ${commitHash}\n\n` + `---\n*Generated by @elizaos/plugin-agent-orchestrator*`;
1098
+ const prBody = content.prBody ?? `## Summary
1099
+
1100
+ Automated changes generated by Milady coding agent.
1101
+
1102
+ ` + `**Branch:** ${workspace.branch}
1103
+ ` + `**Commit:** ${commitHash}
1104
+
1105
+ ` + `---
1106
+ *Generated by @elizaos/plugin-agent-orchestrator*`;
924
1107
  prInfo = await workspaceService.createPR(workspaceId, {
925
1108
  title: prTitle,
926
1109
  body: prBody,
@@ -931,11 +1114,14 @@ var finalizeWorkspaceAction = {
931
1114
  if (callback) {
932
1115
  if (prInfo) {
933
1116
  await callback({
934
- text: `Workspace finalized!\n` + `Commit: ${commitHash.slice(0, 8)}\n` + `PR #${prInfo.number}: ${prInfo.url}`
1117
+ text: `Workspace finalized!
1118
+ ` + `Commit: ${commitHash.slice(0, 8)}
1119
+ ` + `PR #${prInfo.number}: ${prInfo.url}`
935
1120
  });
936
1121
  } else {
937
1122
  await callback({
938
- text: `Workspace changes committed and pushed.\n` + `Commit: ${commitHash.slice(0, 8)}`
1123
+ text: `Workspace changes committed and pushed.
1124
+ ` + `Commit: ${commitHash.slice(0, 8)}`
939
1125
  });
940
1126
  }
941
1127
  }
@@ -1079,18 +1265,23 @@ var listAgentsAction = {
1079
1265
  }));
1080
1266
  const lines = sessions.map((session, index) => {
1081
1267
  const statusEmoji = {
1082
- running: "\u25B6\uFE0F",
1083
- idle: "\u23F8\uFE0F",
1084
- blocked: "\u26A0\uFE0F",
1085
- completed: "\u2705",
1086
- error: "\u274C"
1087
- }[session.status] ?? "\u2753";
1268
+ running: "▶️",
1269
+ idle: "⏸️",
1270
+ blocked: "⚠️",
1271
+ completed: "",
1272
+ error: ""
1273
+ }[session.status] ?? "";
1088
1274
  return `${index + 1}. ${statusEmoji} ${session.agentType} (${session.id.slice(0, 8)}...)
1089
- \uD83D\uDCC1 ${session.workdir}\n Status: ${session.status}`;
1275
+ \uD83D\uDCC1 ${session.workdir}
1276
+ Status: ${session.status}`;
1090
1277
  });
1091
1278
  if (callback) {
1092
1279
  await callback({
1093
- text: `Active coding agents:\n\n${lines.join("\n\n")}`
1280
+ text: `Active coding agents:
1281
+
1282
+ ${lines.join(`
1283
+
1284
+ `)}`
1094
1285
  });
1095
1286
  }
1096
1287
  return {
@@ -1103,6 +1294,150 @@ var listAgentsAction = {
1103
1294
  };
1104
1295
 
1105
1296
  // src/actions/manage-issues.ts
1297
+ var manageIssuesAction = {
1298
+ name: "MANAGE_ISSUES",
1299
+ similes: [
1300
+ "CREATE_ISSUE",
1301
+ "LIST_ISSUES",
1302
+ "CLOSE_ISSUE",
1303
+ "COMMENT_ISSUE",
1304
+ "UPDATE_ISSUE",
1305
+ "GET_ISSUE"
1306
+ ],
1307
+ description: "Manage GitHub issues for a repository. " + "Supports creating issues, listing issues, getting issue details, " + "adding comments, updating, closing, and reopening issues.",
1308
+ examples: [
1309
+ [
1310
+ {
1311
+ name: "{{user1}}",
1312
+ content: {
1313
+ text: "Create an issue on the testbed repo to add a login page"
1314
+ }
1315
+ },
1316
+ {
1317
+ name: "{{agentName}}",
1318
+ content: {
1319
+ text: "I'll create that issue for you.",
1320
+ action: "MANAGE_ISSUES"
1321
+ }
1322
+ }
1323
+ ],
1324
+ [
1325
+ {
1326
+ name: "{{user1}}",
1327
+ content: {
1328
+ text: "List the open issues on HaruHunab1320/git-workspace-service-testbed"
1329
+ }
1330
+ },
1331
+ {
1332
+ name: "{{agentName}}",
1333
+ content: {
1334
+ text: "Let me check the open issues for that repo.",
1335
+ action: "MANAGE_ISSUES"
1336
+ }
1337
+ }
1338
+ ],
1339
+ [
1340
+ {
1341
+ name: "{{user1}}",
1342
+ content: { text: "Close issue #3 on the testbed repo" }
1343
+ },
1344
+ {
1345
+ name: "{{agentName}}",
1346
+ content: {
1347
+ text: "I'll close that issue.",
1348
+ action: "MANAGE_ISSUES"
1349
+ }
1350
+ }
1351
+ ]
1352
+ ],
1353
+ validate: async (runtime, _message) => {
1354
+ const workspaceService = runtime.getService("CODING_WORKSPACE_SERVICE");
1355
+ return workspaceService != null;
1356
+ },
1357
+ handler: async (runtime, message, _state, options, callback) => {
1358
+ const workspaceService = runtime.getService("CODING_WORKSPACE_SERVICE");
1359
+ if (!workspaceService) {
1360
+ if (callback) {
1361
+ await callback({ text: "Workspace Service is not available." });
1362
+ }
1363
+ return { success: false, error: "SERVICE_UNAVAILABLE" };
1364
+ }
1365
+ workspaceService.setAuthPromptCallback((prompt) => {
1366
+ if (callback) {
1367
+ callback({
1368
+ text: `I need GitHub access to manage issues. Please authorize me:
1369
+
1370
+ ` + `Go to: ${prompt.verificationUri}
1371
+ ` + `Enter code: **${prompt.userCode}**
1372
+
1373
+ ` + `This code expires in ${Math.floor(prompt.expiresIn / 60)} minutes. ` + `I'll wait for you to complete authorization...`
1374
+ });
1375
+ }
1376
+ });
1377
+ const params = options?.parameters;
1378
+ const content = message.content;
1379
+ const text = content.text ?? "";
1380
+ const operation = params?.operation ?? content.operation ?? inferOperation(text);
1381
+ const repo = params?.repo ?? content.repo;
1382
+ if (!repo) {
1383
+ const urlMatch = text?.match(/(?:https?:\/\/github\.com\/)?([a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+)/);
1384
+ if (!urlMatch) {
1385
+ if (callback) {
1386
+ await callback({
1387
+ text: "Please specify a repository (e.g., owner/repo or a GitHub URL)."
1388
+ });
1389
+ }
1390
+ return { success: false, error: "MISSING_REPO" };
1391
+ }
1392
+ return handleOperation(workspaceService, urlMatch[1], operation, params ?? content, text, callback);
1393
+ }
1394
+ return handleOperation(workspaceService, repo, operation, params ?? content, text, callback);
1395
+ },
1396
+ parameters: [
1397
+ {
1398
+ name: "operation",
1399
+ description: "The operation to perform: create, list, get, update, comment, close, reopen, add_labels",
1400
+ required: true,
1401
+ schema: { type: "string" }
1402
+ },
1403
+ {
1404
+ name: "repo",
1405
+ description: "Repository in owner/repo format or full GitHub URL.",
1406
+ required: true,
1407
+ schema: { type: "string" }
1408
+ },
1409
+ {
1410
+ name: "title",
1411
+ description: "Issue title (for create operation).",
1412
+ required: false,
1413
+ schema: { type: "string" }
1414
+ },
1415
+ {
1416
+ name: "body",
1417
+ description: "Issue body/description (for create or comment operations).",
1418
+ required: false,
1419
+ schema: { type: "string" }
1420
+ },
1421
+ {
1422
+ name: "issueNumber",
1423
+ description: "Issue number (for get, update, comment, close, reopen operations).",
1424
+ required: false,
1425
+ schema: { type: "number" }
1426
+ },
1427
+ {
1428
+ name: "labels",
1429
+ description: "Labels to add (comma-separated string or array).",
1430
+ required: false,
1431
+ schema: { type: "string" }
1432
+ },
1433
+ {
1434
+ name: "state",
1435
+ description: "Filter by state: open, closed, or all (for list operation).",
1436
+ required: false,
1437
+ schema: { type: "string" }
1438
+ }
1439
+ ]
1440
+ };
1106
1441
  async function handleOperation(service, repo, operation, params, originalText, callback) {
1107
1442
  try {
1108
1443
  switch (operation.toLowerCase()) {
@@ -1123,9 +1458,12 @@ async function handleOperation(service, repo, operation, params, originalText, c
1123
1458
  created.push(issue2);
1124
1459
  }
1125
1460
  if (callback) {
1126
- const summary = created.map((i) => `#${i.number}: ${i.title}\n ${i.url}`).join("\n");
1461
+ const summary = created.map((i) => `#${i.number}: ${i.title}
1462
+ ${i.url}`).join(`
1463
+ `);
1127
1464
  await callback({
1128
- text: `Created ${created.length} issues:\n${summary}`
1465
+ text: `Created ${created.length} issues:
1466
+ ${summary}`
1129
1467
  });
1130
1468
  }
1131
1469
  return { success: true, data: { issues: created } };
@@ -1142,7 +1480,8 @@ async function handleOperation(service, repo, operation, params, originalText, c
1142
1480
  });
1143
1481
  if (callback) {
1144
1482
  await callback({
1145
- text: `Created issue #${issue.number}: ${issue.title}\n${issue.url}`
1483
+ text: `Created issue #${issue.number}: ${issue.title}
1484
+ ${issue.url}`
1146
1485
  });
1147
1486
  }
1148
1487
  return { success: true, data: { issue } };
@@ -1160,8 +1499,10 @@ async function handleOperation(service, repo, operation, params, originalText, c
1160
1499
  text: `No ${stateFilter} issues found in ${repo}.`
1161
1500
  });
1162
1501
  } else {
1163
- const summary = issues.map((i) => `#${i.number} [${i.state}] ${i.title}${i.labels.length > 0 ? ` (${i.labels.join(", ")})` : ""}`).join("\n");
1164
- await callback({ text: `Issues in ${repo}:\n${summary}` });
1502
+ const summary = issues.map((i) => `#${i.number} [${i.state}] ${i.title}${i.labels.length > 0 ? ` (${i.labels.join(", ")})` : ""}`).join(`
1503
+ `);
1504
+ await callback({ text: `Issues in ${repo}:
1505
+ ${summary}` });
1165
1506
  }
1166
1507
  }
1167
1508
  return { success: true, data: { issues } };
@@ -1176,7 +1517,12 @@ async function handleOperation(service, repo, operation, params, originalText, c
1176
1517
  const issue = await service.getIssue(repo, issueNumber);
1177
1518
  if (callback) {
1178
1519
  await callback({
1179
- text: `Issue #${issue.number}: ${issue.title} [${issue.state}]\n\n${issue.body}\n\nLabels: ${issue.labels.join(", ") || "none"}\n${issue.url}`
1520
+ text: `Issue #${issue.number}: ${issue.title} [${issue.state}]
1521
+
1522
+ ${issue.body}
1523
+
1524
+ Labels: ${issue.labels.join(", ") || "none"}
1525
+ ${issue.url}`
1180
1526
  });
1181
1527
  }
1182
1528
  return { success: true, data: { issue } };
@@ -1345,145 +1691,6 @@ function parseLabels(input) {
1345
1691
  return input.split(",").map((s) => s.trim()).filter(Boolean);
1346
1692
  return [];
1347
1693
  }
1348
- var manageIssuesAction = {
1349
- name: "MANAGE_ISSUES",
1350
- similes: [
1351
- "CREATE_ISSUE",
1352
- "LIST_ISSUES",
1353
- "CLOSE_ISSUE",
1354
- "COMMENT_ISSUE",
1355
- "UPDATE_ISSUE",
1356
- "GET_ISSUE"
1357
- ],
1358
- description: "Manage GitHub issues for a repository. " + "Supports creating issues, listing issues, getting issue details, " + "adding comments, updating, closing, and reopening issues.",
1359
- examples: [
1360
- [
1361
- {
1362
- name: "{{user1}}",
1363
- content: {
1364
- text: "Create an issue on the testbed repo to add a login page"
1365
- }
1366
- },
1367
- {
1368
- name: "{{agentName}}",
1369
- content: {
1370
- text: "I'll create that issue for you.",
1371
- action: "MANAGE_ISSUES"
1372
- }
1373
- }
1374
- ],
1375
- [
1376
- {
1377
- name: "{{user1}}",
1378
- content: {
1379
- text: "List the open issues on HaruHunab1320/git-workspace-service-testbed"
1380
- }
1381
- },
1382
- {
1383
- name: "{{agentName}}",
1384
- content: {
1385
- text: "Let me check the open issues for that repo.",
1386
- action: "MANAGE_ISSUES"
1387
- }
1388
- }
1389
- ],
1390
- [
1391
- {
1392
- name: "{{user1}}",
1393
- content: { text: "Close issue #3 on the testbed repo" }
1394
- },
1395
- {
1396
- name: "{{agentName}}",
1397
- content: {
1398
- text: "I'll close that issue.",
1399
- action: "MANAGE_ISSUES"
1400
- }
1401
- }
1402
- ]
1403
- ],
1404
- validate: async (runtime, _message) => {
1405
- const workspaceService = runtime.getService("CODING_WORKSPACE_SERVICE");
1406
- return workspaceService != null;
1407
- },
1408
- handler: async (runtime, message, _state, options, callback) => {
1409
- const workspaceService = runtime.getService("CODING_WORKSPACE_SERVICE");
1410
- if (!workspaceService) {
1411
- if (callback) {
1412
- await callback({ text: "Workspace Service is not available." });
1413
- }
1414
- return { success: false, error: "SERVICE_UNAVAILABLE" };
1415
- }
1416
- workspaceService.setAuthPromptCallback((prompt) => {
1417
- if (callback) {
1418
- callback({
1419
- text: `I need GitHub access to manage issues. Please authorize me:\n\n` + `Go to: ${prompt.verificationUri}\n` + `Enter code: **${prompt.userCode}**\n\n` + `This code expires in ${Math.floor(prompt.expiresIn / 60)} minutes. ` + `I'll wait for you to complete authorization...`
1420
- });
1421
- }
1422
- });
1423
- const params = options?.parameters;
1424
- const content = message.content;
1425
- const text = content.text ?? "";
1426
- const operation = params?.operation ?? content.operation ?? inferOperation(text);
1427
- const repo = params?.repo ?? content.repo;
1428
- if (!repo) {
1429
- const urlMatch = text?.match(/(?:https?:\/\/github\.com\/)?([a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+)/);
1430
- if (!urlMatch) {
1431
- if (callback) {
1432
- await callback({
1433
- text: "Please specify a repository (e.g., owner/repo or a GitHub URL)."
1434
- });
1435
- }
1436
- return { success: false, error: "MISSING_REPO" };
1437
- }
1438
- return handleOperation(workspaceService, urlMatch[1], operation, params ?? content, text, callback);
1439
- }
1440
- return handleOperation(workspaceService, repo, operation, params ?? content, text, callback);
1441
- },
1442
- parameters: [
1443
- {
1444
- name: "operation",
1445
- description: "The operation to perform: create, list, get, update, comment, close, reopen, add_labels",
1446
- required: true,
1447
- schema: { type: "string" }
1448
- },
1449
- {
1450
- name: "repo",
1451
- description: "Repository in owner/repo format or full GitHub URL.",
1452
- required: true,
1453
- schema: { type: "string" }
1454
- },
1455
- {
1456
- name: "title",
1457
- description: "Issue title (for create operation).",
1458
- required: false,
1459
- schema: { type: "string" }
1460
- },
1461
- {
1462
- name: "body",
1463
- description: "Issue body/description (for create or comment operations).",
1464
- required: false,
1465
- schema: { type: "string" }
1466
- },
1467
- {
1468
- name: "issueNumber",
1469
- description: "Issue number (for get, update, comment, close, reopen operations).",
1470
- required: false,
1471
- schema: { type: "number" }
1472
- },
1473
- {
1474
- name: "labels",
1475
- description: "Labels to add (comma-separated string or array).",
1476
- required: false,
1477
- schema: { type: "string" }
1478
- },
1479
- {
1480
- name: "state",
1481
- description: "Filter by state: open, closed, or all (for list operation).",
1482
- required: false,
1483
- schema: { type: "string" }
1484
- }
1485
- ]
1486
- };
1487
1694
 
1488
1695
  // src/actions/provision-workspace.ts
1489
1696
  var provisionWorkspaceAction = {
@@ -1596,7 +1803,9 @@ var provisionWorkspaceAction = {
1596
1803
  }
1597
1804
  if (callback) {
1598
1805
  await callback({
1599
- text: `Created workspace at ${workspace.path}\n` + `Branch: ${workspace.branch}\n` + `Type: ${workspace.isWorktree ? "worktree" : "clone"}`
1806
+ text: `Created workspace at ${workspace.path}
1807
+ ` + `Branch: ${workspace.branch}
1808
+ ` + `Type: ${workspace.isWorktree ? "worktree" : "clone"}`
1600
1809
  });
1601
1810
  }
1602
1811
  return {
@@ -1805,19 +2014,19 @@ var sendToAgentAction = {
1805
2014
  import * as os from "node:os";
1806
2015
  import * as path2 from "node:path";
1807
2016
  import {
1808
- logger as logger3
2017
+ logger as logger3
1809
2018
  } from "@elizaos/core";
1810
2019
 
1811
2020
  // src/services/pty-service.ts
1812
- import {mkdir, readFile, writeFile} from "node:fs/promises";
1813
- import {dirname, join} from "node:path";
1814
- import {logger as logger2} from "@elizaos/core";
2021
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2022
+ import { dirname, join } from "node:path";
2023
+ import { logger as logger2 } from "@elizaos/core";
1815
2024
  import {
1816
- checkAdapters,
1817
- createAdapter,
1818
- generateApprovalConfig
2025
+ checkAdapters,
2026
+ createAdapter,
2027
+ generateApprovalConfig
1819
2028
  } from "coding-agent-adapters";
1820
- import {PTYConsoleBridge} from "pty-console";
2029
+ import { PTYConsoleBridge } from "pty-console";
1821
2030
 
1822
2031
  // src/services/agent-metrics.ts
1823
2032
  class AgentMetricsTracker {
@@ -1873,6 +2082,7 @@ function computeAgentScore(metrics) {
1873
2082
  const speedPenalty = Math.min(avgCompletionMs / 300000, 1) * 0.1;
1874
2083
  return Math.max(0, successRate - stallPenalty - speedPenalty);
1875
2084
  }
2085
+ var DEFAULT_ORDER = ["claude", "gemini", "codex", "aider"];
1876
2086
  function selectAgentType(ctx) {
1877
2087
  if (ctx.config.strategy === "fixed") {
1878
2088
  return ctx.config.fixedAgentType;
@@ -1894,7 +2104,6 @@ function selectAgentType(ctx) {
1894
2104
  }
1895
2105
  return bestAgent;
1896
2106
  }
1897
- var DEFAULT_ORDER = ["claude", "gemini", "codex", "aider"];
1898
2107
 
1899
2108
  // src/services/pty-auto-response.ts
1900
2109
  async function pushDefaultRules(ctx, sessionId, agentType) {
@@ -1972,20 +2181,25 @@ async function handleGeminiAuth(ctx, sessionId, sendKeysToSession) {
1972
2181
 
1973
2182
  // src/services/pty-init.ts
1974
2183
  init_ansi_utils();
1975
- import {createRequire as createRequire2} from "node:module";
1976
- import {createAllAdapters} from "coding-agent-adapters";
2184
+ import { createRequire as createRequire2 } from "node:module";
2185
+ import { createAllAdapters } from "coding-agent-adapters";
1977
2186
  import {
1978
- BunCompatiblePTYManager,
1979
- isBun,
1980
- PTYManager,
1981
- ShellAdapter
2187
+ BunCompatiblePTYManager,
2188
+ isBun,
2189
+ PTYManager,
2190
+ ShellAdapter
1982
2191
  } from "pty-manager";
2192
+ var _require = createRequire2(import.meta.url);
2193
+ var resolvedAdapterModule = "coding-agent-adapters";
2194
+ try {
2195
+ resolvedAdapterModule = _require.resolve("coding-agent-adapters");
2196
+ } catch {}
1983
2197
  function forwardReadyAsTaskComplete(ctx, session) {
1984
2198
  if (!ctx.hasActiveTask?.(session.id) || !ctx.hasTaskActivity?.(session.id)) {
1985
2199
  return;
1986
2200
  }
1987
2201
  const response = ctx.taskResponseMarkers.has(session.id) ? captureTaskResponse(session.id, ctx.sessionOutputBuffers, ctx.taskResponseMarkers) : "";
1988
- ctx.log(`session_ready for active task ${session.id} \u2014 forwarding as task_complete (stall classifier path, response: ${response.length} chars)`);
2202
+ ctx.log(`session_ready for active task ${session.id} forwarding as task_complete (stall classifier path, response: ${response.length} chars)`);
1989
2203
  ctx.emitEvent(session.id, "task_complete", { session, response });
1990
2204
  }
1991
2205
  async function initializePTYManager(ctx) {
@@ -2031,7 +2245,7 @@ async function initializePTYManager(ctx) {
2031
2245
  ctx.emitEvent(session.id, "task_complete", { session, response });
2032
2246
  });
2033
2247
  bunManager.on("tool_running", (session, info) => {
2034
- ctx.log(`tool_running for ${session.id}: ${info.toolName}${info.description ? ` \u2014 ${info.description}` : ""}`);
2248
+ ctx.log(`tool_running for ${session.id}: ${info.toolName}${info.description ? ` ${info.description}` : ""}`);
2035
2249
  ctx.emitEvent(session.id, "tool_running", { session, ...info });
2036
2250
  });
2037
2251
  bunManager.on("message", (message) => {
@@ -2102,7 +2316,7 @@ async function initializePTYManager(ctx) {
2102
2316
  ctx.emitEvent(session.id, "task_complete", { session, response });
2103
2317
  });
2104
2318
  nodeManager.on("tool_running", (session, info) => {
2105
- ctx.log(`tool_running for ${session.id}: ${info.toolName}${info.description ? ` \u2014 ${info.description}` : ""}`);
2319
+ ctx.log(`tool_running for ${session.id}: ${info.toolName}${info.description ? ` ${info.description}` : ""}`);
2106
2320
  ctx.emitEvent(session.id, "tool_running", { session, ...info });
2107
2321
  });
2108
2322
  nodeManager.on("session_stopped", (session, reason) => {
@@ -2116,12 +2330,6 @@ async function initializePTYManager(ctx) {
2116
2330
  });
2117
2331
  return { manager: nodeManager, usingBunWorker: false };
2118
2332
  }
2119
- var _require = createRequire2(import.meta.url);
2120
- var resolvedAdapterModule = "coding-agent-adapters";
2121
- try {
2122
- resolvedAdapterModule = _require.resolve("coding-agent-adapters");
2123
- } catch {
2124
- }
2125
2333
 
2126
2334
  // src/services/pty-session-io.ts
2127
2335
  async function sendToSession(ctx, sessionId, input) {
@@ -2176,8 +2384,7 @@ async function stopSession(ctx, sessionId, sessionMetadata, sessionWorkdirs, log
2176
2384
  if (unsubscribe) {
2177
2385
  unsubscribe();
2178
2386
  }
2179
- } catch {
2180
- }
2387
+ } catch {}
2181
2388
  ctx.outputUnsubscribers.delete(sessionId);
2182
2389
  sessionMetadata.delete(sessionId);
2183
2390
  sessionWorkdirs.delete(sessionId);
@@ -2207,7 +2414,8 @@ async function getSessionOutput(ctx, sessionId, lines) {
2207
2414
  if (!buffer)
2208
2415
  return "";
2209
2416
  const tail = lines ?? buffer.length;
2210
- return buffer.slice(-tail).join("\n");
2417
+ return buffer.slice(-tail).join(`
2418
+ `);
2211
2419
  }
2212
2420
  const output = [];
2213
2421
  for await (const line of ctx.manager.logs(sessionId, {
@@ -2215,10 +2423,26 @@ async function getSessionOutput(ctx, sessionId, lines) {
2215
2423
  })) {
2216
2424
  output.push(line);
2217
2425
  }
2218
- return output.join("\n");
2426
+ return output.join(`
2427
+ `);
2219
2428
  }
2220
2429
 
2221
2430
  // src/services/pty-spawn.ts
2431
+ var ENV_ALLOWLIST = [
2432
+ "PATH",
2433
+ "HOME",
2434
+ "USER",
2435
+ "SHELL",
2436
+ "LANG",
2437
+ "LC_ALL",
2438
+ "LC_CTYPE",
2439
+ "TERM",
2440
+ "TZ",
2441
+ "TMPDIR",
2442
+ "XDG_RUNTIME_DIR",
2443
+ "NODE_OPTIONS",
2444
+ "BUN_INSTALL"
2445
+ ];
2222
2446
  function buildSanitizedBaseEnv() {
2223
2447
  const env = {};
2224
2448
  for (const key of ENV_ALLOWLIST) {
@@ -2232,7 +2456,8 @@ function setupOutputBuffer(ctx, sessionId) {
2232
2456
  const buffer = [];
2233
2457
  ctx.sessionOutputBuffers.set(sessionId, buffer);
2234
2458
  const unsubscribe = ctx.manager.onSessionData(sessionId, (data) => {
2235
- const lines = data.split("\n");
2459
+ const lines = data.split(`
2460
+ `);
2236
2461
  buffer.push(...lines);
2237
2462
  while (buffer.length > (ctx.serviceConfig.maxLogLines ?? 1000)) {
2238
2463
  buffer.shift();
@@ -2255,26 +2480,30 @@ function setupDeferredTaskDelivery(ctx, session, task, agentType) {
2255
2480
  const sendTaskWithRetry = (attempt) => {
2256
2481
  const buffer = ctx.sessionOutputBuffers.get(sid);
2257
2482
  const baselineLength = buffer?.length ?? 0;
2258
- ctx.log(`Session ${sid} \u2014 sending task (attempt ${attempt + 1}, ${settleMs}ms settle, baseline ${baselineLength} lines)`);
2483
+ ctx.log(`Session ${sid} sending task (attempt ${attempt + 1}, ${settleMs}ms settle, baseline ${baselineLength} lines)`);
2259
2484
  ctx.sendToSession(sid, task).catch((err) => ctx.log(`Failed to send deferred task to ${sid}: ${err}`));
2260
2485
  if (attempt < MAX_RETRIES) {
2261
2486
  setTimeout(() => {
2262
2487
  const currentLength = buffer?.length ?? 0;
2263
2488
  const newLines = currentLength - baselineLength;
2264
2489
  if (newLines < MIN_NEW_LINES) {
2265
- ctx.log(`Session ${sid} \u2014 task may not have been accepted (only ${newLines} new lines after ${VERIFY_DELAY_MS}ms). Retrying (attempt ${attempt + 2}/${MAX_RETRIES + 1})`);
2490
+ ctx.log(`Session ${sid} task may not have been accepted (only ${newLines} new lines after ${VERIFY_DELAY_MS}ms). Retrying (attempt ${attempt + 2}/${MAX_RETRIES + 1})`);
2266
2491
  sendTaskWithRetry(attempt + 1);
2267
2492
  } else {
2268
- ctx.log(`Session ${sid} \u2014 task accepted (${newLines} new lines after ${VERIFY_DELAY_MS}ms)`);
2493
+ ctx.log(`Session ${sid} task accepted (${newLines} new lines after ${VERIFY_DELAY_MS}ms)`);
2269
2494
  }
2270
2495
  }, VERIFY_DELAY_MS);
2271
2496
  }
2272
2497
  };
2498
+ const READY_TIMEOUT_MS = 30000;
2273
2499
  let taskSent = false;
2500
+ let readyTimeout;
2274
2501
  const sendTask = () => {
2275
2502
  if (taskSent)
2276
2503
  return;
2277
2504
  taskSent = true;
2505
+ if (readyTimeout)
2506
+ clearTimeout(readyTimeout);
2278
2507
  setTimeout(() => sendTaskWithRetry(0), settleMs);
2279
2508
  if (ctx.usingBunWorker) {
2280
2509
  ctx.manager.removeListener("session_ready", onReady);
@@ -2295,6 +2524,12 @@ function setupDeferredTaskDelivery(ctx, session, task, agentType) {
2295
2524
  } else {
2296
2525
  ctx.manager.on("session_ready", onReady);
2297
2526
  }
2527
+ readyTimeout = setTimeout(() => {
2528
+ if (!taskSent) {
2529
+ ctx.log(`Session ${sid} — ready event not received within ${READY_TIMEOUT_MS}ms, forcing task delivery`);
2530
+ sendTask();
2531
+ }
2532
+ }, READY_TIMEOUT_MS);
2298
2533
  }
2299
2534
  }
2300
2535
  function buildSpawnConfig(sessionId, options, workdir) {
@@ -2329,21 +2564,6 @@ function buildSpawnConfig(sessionId, options, workdir) {
2329
2564
  }
2330
2565
  };
2331
2566
  }
2332
- var ENV_ALLOWLIST = [
2333
- "PATH",
2334
- "HOME",
2335
- "USER",
2336
- "SHELL",
2337
- "LANG",
2338
- "LC_ALL",
2339
- "LC_CTYPE",
2340
- "TERM",
2341
- "TZ",
2342
- "TMPDIR",
2343
- "XDG_RUNTIME_DIR",
2344
- "NODE_OPTIONS",
2345
- "BUN_INSTALL"
2346
- ];
2347
2567
 
2348
2568
  // src/services/pty-types.ts
2349
2569
  var PI_AGENT_ALIASES = new Set([
@@ -2388,17 +2608,39 @@ var toPiCommand = (task) => {
2388
2608
 
2389
2609
  // src/services/stall-classifier.ts
2390
2610
  init_ansi_utils();
2391
- import {ModelType} from "@elizaos/core";
2611
+ import { ModelType } from "@elizaos/core";
2392
2612
  import {
2393
- buildTaskCompletionTimeline,
2394
- extractTaskCompletionTraceRecords
2613
+ buildTaskCompletionTimeline,
2614
+ extractTaskCompletionTraceRecords
2395
2615
  } from "pty-manager";
2396
2616
  function buildStallClassificationPrompt(agentType, sessionId, output) {
2397
- return `You are Milady, an AI orchestrator managing coding agent sessions. ` + `A ${agentType} coding agent (session: ${sessionId}) appears to have stalled \u2014 ` + `it has stopped producing output while in a busy state.\n\n` + `Here is the recent terminal output:\n` + `---\n${output.slice(-1500)}\n---\n\n` + `Classify what's happening. Read the output carefully and choose the MOST specific match:\n\n` + `1. "task_complete" \u2014 The agent FINISHED its task and returned to its idle prompt. ` + `Strong indicators: a summary of completed work ("Done", "All done", "Here's what was completed"), ` + `timing info ("Baked for", "Churned for", "Crunched for", "Cooked for", "Worked for"), ` + `or the agent's main prompt symbol (\u276F) appearing AFTER completion output. ` + `If the output contains evidence of completed work followed by an idle prompt, this is ALWAYS task_complete, ` + `even though the agent is technically "waiting" \u2014 it is waiting for a NEW task, not asking a question.
2617
+ 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.
2618
+
2619
+ ` + `Here is the recent terminal output:
2620
+ ` + `---
2621
+ ${output.slice(-1500)}
2622
+ ---
2623
+
2624
+ ` + `Classify what's happening. Read the output carefully and choose the MOST specific match:
2625
+
2626
+ ` + `1. "task_complete" — The agent FINISHED its task and returned to its idle prompt. ` + `Strong indicators: a summary of completed work ("Done", "All done", "Here's what was completed"), ` + `timing info ("Baked for", "Churned for", "Crunched for", "Cooked for", "Worked for"), ` + `or the agent's main prompt symbol (❯) appearing AFTER completion output. ` + `If the output contains evidence of completed work followed by an idle prompt, this is ALWAYS task_complete, ` + `even though the agent is technically "waiting" — it is waiting for a NEW task, not asking a question.
2627
+
2628
+ ` + `2. "waiting_for_input" — The agent is MID-TASK and blocked on a specific question or permission prompt. ` + `The agent has NOT finished its work — it needs a response to continue. ` + `Examples: Y/n confirmation, file permission dialogs, "Do you want to proceed?", ` + `tool approval prompts, or interactive menus. ` + `This is NOT the same as the agent sitting at its idle prompt after finishing work.
2629
+
2630
+ ` + `3. "still_working" — The agent is actively processing (API call, compilation, thinking, etc.) ` + `and has not produced final output yet. No prompt or completion summary visible.
2631
+
2632
+ ` + `4. "error" — The agent hit an error state (crash, unrecoverable error, stack trace).
2633
+
2634
+ ` + `5. "tool_running" — The agent is using an external tool (browser automation, ` + `MCP tool, etc.). Indicators: "Claude in Chrome", "javascript_tool", ` + `"computer_tool", "screenshot", "navigate", tool execution output. ` + `The agent is actively working but the terminal may be quiet.
2398
2635
 
2399
- ` + `2. "waiting_for_input" \u2014 The agent is MID-TASK and blocked on a specific question or permission prompt. ` + `The agent has NOT finished its work \u2014 it needs a response to continue. ` + `Examples: Y/n confirmation, file permission dialogs, "Do you want to proceed?", ` + `tool approval prompts, or interactive menus. ` + `This is NOT the same as the agent sitting at its idle prompt after finishing work.\n\n` + `3. "still_working" \u2014 The agent is actively processing (API call, compilation, thinking, etc.) ` + `and has not produced final output yet. No prompt or completion summary visible.\n\n` + `4. "error" \u2014 The agent hit an error state (crash, unrecoverable error, stack trace).
2636
+ ` + `IMPORTANT: If you see BOTH completed work output AND an idle prompt (❯), choose "task_complete". ` + `Only choose "waiting_for_input" if the agent is clearly asking a question mid-task.
2400
2637
 
2401
- ` + `5. "tool_running" \u2014 The agent is using an external tool (browser automation, ` + `MCP tool, etc.). Indicators: "Claude in Chrome", "javascript_tool", ` + `"computer_tool", "screenshot", "navigate", tool execution output. ` + `The agent is actively working but the terminal may be quiet.\n\n` + `IMPORTANT: If you see BOTH completed work output AND an idle prompt (\u276F), choose "task_complete". ` + `Only choose "waiting_for_input" if the agent is clearly asking a question mid-task.\n\n` + `If "waiting_for_input", also provide:\n` + `- "prompt": the text of what it's asking\n` + `- "suggestedResponse": what to type/send. Use "keys:enter" for TUI menu confirmation, ` + `"keys:down,enter" to select a non-default option, or plain text like "y" for text prompts.\n\n` + `Respond with ONLY a JSON object:\n` + `{"state": "...", "prompt": "...", "suggestedResponse": "..."}`;
2638
+ ` + `If "waiting_for_input", also provide:
2639
+ ` + `- "prompt": the text of what it's asking
2640
+ ` + `- "suggestedResponse": what to type/send. Use "keys:enter" for TUI menu confirmation, ` + `"keys:down,enter" to select a non-default option, or plain text like "y" for text prompts.
2641
+
2642
+ ` + `Respond with ONLY a JSON object:
2643
+ ` + `{"state": "...", "prompt": "...", "suggestedResponse": "..."}`;
2402
2644
  }
2403
2645
  async function writeStallSnapshot(sessionId, agentType, recentOutput, effectiveOutput, buffers, traceEntries, log) {
2404
2646
  try {
@@ -2408,7 +2650,8 @@ async function writeStallSnapshot(sessionId, agentType, recentOutput, effectiveO
2408
2650
  const snapshotDir = path.join(os.homedir(), ".milady", "debug");
2409
2651
  fs.mkdirSync(snapshotDir, { recursive: true });
2410
2652
  const ourBuffer = buffers.get(sessionId);
2411
- const ourTail = ourBuffer ? ourBuffer.slice(-100).join("\n") : "(no buffer)";
2653
+ const ourTail = ourBuffer ? ourBuffer.slice(-100).join(`
2654
+ `) : "(no buffer)";
2412
2655
  let traceTimeline = "(no trace entries)";
2413
2656
  try {
2414
2657
  const records = extractTaskCompletionTraceRecords(traceEntries);
@@ -2431,14 +2674,15 @@ async function writeStallSnapshot(sessionId, agentType, recentOutput, effectiveO
2431
2674
  traceTimeline,
2432
2675
  ``,
2433
2676
  `--- raw trace entries (last 20 of ${traceEntries.length}) ---`,
2434
- traceEntries.slice(-20).join("\n"),
2677
+ traceEntries.slice(-20).join(`
2678
+ `),
2435
2679
  ``
2436
- ].join("\n");
2680
+ ].join(`
2681
+ `);
2437
2682
  const snapshotPath = path.join(snapshotDir, `stall-snapshot-${sessionId}.txt`);
2438
2683
  fs.writeFileSync(snapshotPath, snapshot);
2439
- log(`Stall snapshot \u2192 ${snapshotPath}`);
2440
- } catch (_) {
2441
- }
2684
+ log(`Stall snapshot ${snapshotPath}`);
2685
+ } catch (_) {}
2442
2686
  }
2443
2687
  async function classifyStallOutput(ctx) {
2444
2688
  const {
@@ -2457,7 +2701,8 @@ async function classifyStallOutput(ctx) {
2457
2701
  if (!recentOutput || recentOutput.trim().length < 200) {
2458
2702
  const ourBuffer = buffers.get(sessionId);
2459
2703
  if (ourBuffer && ourBuffer.length > 0) {
2460
- const rawTail = ourBuffer.slice(-100).join("\n");
2704
+ const rawTail = ourBuffer.slice(-100).join(`
2705
+ `);
2461
2706
  const stripped = stripAnsi(rawTail);
2462
2707
  if (stripped.length > effectiveOutput.length) {
2463
2708
  effectiveOutput = stripped;
@@ -2497,7 +2742,7 @@ async function classifyStallOutput(ctx) {
2497
2742
  prompt: parsed.prompt,
2498
2743
  suggestedResponse: parsed.suggestedResponse
2499
2744
  };
2500
- log(`Stall classification for ${sessionId}: ${classification.state}${classification.suggestedResponse ? ` \u2192 "${classification.suggestedResponse}"` : ""}`);
2745
+ log(`Stall classification for ${sessionId}: ${classification.state}${classification.suggestedResponse ? ` "${classification.suggestedResponse}"` : ""}`);
2501
2746
  if (classification.state === "task_complete") {
2502
2747
  const session = manager?.get(sessionId);
2503
2748
  const durationMs = session?.startedAt ? Date.now() - new Date(session.startedAt).getTime() : 0;
@@ -2513,12 +2758,14 @@ async function classifyStallOutput(ctx) {
2513
2758
  // src/services/swarm-coordinator.ts
2514
2759
  init_ansi_utils();
2515
2760
  init_swarm_decision_loop();
2516
- import {logger} from "@elizaos/core";
2761
+ import { logger } from "@elizaos/core";
2517
2762
 
2518
2763
  // src/services/swarm-idle-watchdog.ts
2519
2764
  init_ansi_utils();
2520
2765
  init_swarm_decision_loop();
2521
- import {ModelType as ModelType4} from "@elizaos/core";
2766
+ import { ModelType as ModelType4 } from "@elizaos/core";
2767
+ var IDLE_THRESHOLD_MS = 5 * 60 * 1000;
2768
+ var MAX_IDLE_CHECKS = 4;
2522
2769
  async function scanIdleSessions(ctx) {
2523
2770
  const now = Date.now();
2524
2771
  for (const taskCtx of ctx.tasks.values()) {
@@ -2527,7 +2774,7 @@ async function scanIdleSessions(ctx) {
2527
2774
  if (ctx.ptyService) {
2528
2775
  const session = ctx.ptyService.getSession(taskCtx.sessionId);
2529
2776
  if (!session) {
2530
- ctx.log(`Idle watchdog: "${taskCtx.label}" \u2014 PTY session no longer exists, marking as stopped`);
2777
+ ctx.log(`Idle watchdog: "${taskCtx.label}" PTY session no longer exists, marking as stopped`);
2531
2778
  taskCtx.status = "stopped";
2532
2779
  taskCtx.decisions.push({
2533
2780
  timestamp: now,
@@ -2542,7 +2789,7 @@ async function scanIdleSessions(ctx) {
2542
2789
  timestamp: now,
2543
2790
  data: { reason: "pty_session_gone" }
2544
2791
  });
2545
- ctx.sendChatMessage(`[${taskCtx.label}] Session lost \u2014 the agent process is no longer running (likely killed during a restart).`, "coding-agent");
2792
+ ctx.sendChatMessage(`[${taskCtx.label}] Session lost the agent process is no longer running (likely killed during a restart).`, "coding-agent");
2546
2793
  checkAllTasksComplete(ctx);
2547
2794
  continue;
2548
2795
  }
@@ -2561,11 +2808,10 @@ async function scanIdleSessions(ctx) {
2561
2808
  if (currentOutput !== lastSeen) {
2562
2809
  taskCtx.lastActivityAt = now;
2563
2810
  taskCtx.idleCheckCount = 0;
2564
- ctx.log(`Idle watchdog: "${taskCtx.label}" has fresh PTY output \u2014 not idle`);
2811
+ ctx.log(`Idle watchdog: "${taskCtx.label}" has fresh PTY output not idle`);
2565
2812
  continue;
2566
2813
  }
2567
- } catch {
2568
- }
2814
+ } catch {}
2569
2815
  }
2570
2816
  taskCtx.idleCheckCount++;
2571
2817
  const idleMinutes = Math.round(idleMs / 60000);
@@ -2590,7 +2836,7 @@ async function scanIdleSessions(ctx) {
2590
2836
  idleCheckCount: taskCtx.idleCheckCount
2591
2837
  }
2592
2838
  });
2593
- ctx.sendChatMessage(`[${taskCtx.label}] Session stopped \u2014 idle for ${idleMinutes} minutes with no progress.`, "coding-agent");
2839
+ ctx.sendChatMessage(`[${taskCtx.label}] Session stopped idle for ${idleMinutes} minutes with no progress.`, "coding-agent");
2594
2840
  if (ctx.ptyService) {
2595
2841
  try {
2596
2842
  await ctx.ptyService.stopSession(taskCtx.sessionId, true);
@@ -2649,8 +2895,8 @@ async function handleIdleCheck(ctx, taskCtx, idleMinutes) {
2649
2895
  ctx.log(`Idle check LLM call failed: ${err}`);
2650
2896
  }
2651
2897
  if (!decision) {
2652
- ctx.log(`Idle check for "${taskCtx.label}": LLM returned invalid response \u2014 escalating`);
2653
- ctx.sendChatMessage(`[${taskCtx.label}] Session idle for ${idleMinutes}m \u2014 couldn't determine status. Needs your attention.`, "coding-agent");
2898
+ ctx.log(`Idle check for "${taskCtx.label}": LLM returned invalid response escalating`);
2899
+ ctx.sendChatMessage(`[${taskCtx.label}] Session idle for ${idleMinutes}m couldn't determine status. Needs your attention.`, "coding-agent");
2654
2900
  return;
2655
2901
  }
2656
2902
  taskCtx.decisions.push({
@@ -2672,22 +2918,19 @@ async function handleIdleCheck(ctx, taskCtx, idleMinutes) {
2672
2918
  reasoning: decision.reasoning
2673
2919
  }
2674
2920
  });
2675
- if (decision.action === "complete") {
2676
- } else if (decision.action === "respond") {
2921
+ if (decision.action === "complete") {} else if (decision.action === "respond") {
2677
2922
  const actionDesc = decision.useKeys ? `Sent keys: ${decision.keys?.join(", ")}` : `Nudged: ${decision.response ?? ""}`;
2678
- ctx.sendChatMessage(`[${taskCtx.label}] Idle for ${idleMinutes}m \u2014 ${actionDesc}`, "coding-agent");
2923
+ ctx.sendChatMessage(`[${taskCtx.label}] Idle for ${idleMinutes}m ${actionDesc}`, "coding-agent");
2679
2924
  } else if (decision.action === "escalate") {
2680
- ctx.sendChatMessage(`[${taskCtx.label}] Idle for ${idleMinutes}m \u2014 needs your attention: ${decision.reasoning}`, "coding-agent");
2925
+ ctx.sendChatMessage(`[${taskCtx.label}] Idle for ${idleMinutes}m needs your attention: ${decision.reasoning}`, "coding-agent");
2681
2926
  } else if (decision.action === "ignore") {
2682
- ctx.log(`Idle check for "${taskCtx.label}": LLM says still working \u2014 ${decision.reasoning}`);
2927
+ ctx.log(`Idle check for "${taskCtx.label}": LLM says still working ${decision.reasoning}`);
2683
2928
  }
2684
2929
  await executeDecision(ctx, sessionId, decision);
2685
2930
  } finally {
2686
2931
  ctx.inFlightDecisions.delete(sessionId);
2687
2932
  }
2688
2933
  }
2689
- var IDLE_THRESHOLD_MS = 5 * 60 * 1000;
2690
- var MAX_IDLE_CHECKS = 4;
2691
2934
 
2692
2935
  // src/services/swarm-coordinator.ts
2693
2936
  var UNREGISTERED_BUFFER_MS = 2000;
@@ -2727,7 +2970,7 @@ class SwarmCoordinator {
2727
2970
  }
2728
2971
  setAgentDecisionCallback(cb) {
2729
2972
  this.agentDecisionCb = cb;
2730
- this.log("Agent decision callback wired \u2014 events will route through Milaidy");
2973
+ this.log("Agent decision callback wired events will route through Milaidy");
2731
2974
  }
2732
2975
  getAgentDecisionCallback() {
2733
2976
  return this.agentDecisionCb;
@@ -2790,7 +3033,7 @@ class SwarmCoordinator {
2790
3033
  if (this._paused)
2791
3034
  return;
2792
3035
  this._paused = true;
2793
- this.log("Coordinator paused \u2014 buffering LLM decisions until user message is processed");
3036
+ this.log("Coordinator paused buffering LLM decisions until user message is processed");
2794
3037
  this.broadcast({ type: "coordinator_paused", sessionId: "", timestamp: Date.now(), data: {} });
2795
3038
  this.pauseTimeout = setTimeout(() => {
2796
3039
  if (this._paused) {
@@ -2807,7 +3050,7 @@ class SwarmCoordinator {
2807
3050
  clearTimeout(this.pauseTimeout);
2808
3051
  this.pauseTimeout = null;
2809
3052
  }
2810
- this.log(`Coordinator resumed \u2014 replaying ${this.pauseBuffer.length} buffered events`);
3053
+ this.log(`Coordinator resumed replaying ${this.pauseBuffer.length} buffered events`);
2811
3054
  this.broadcast({ type: "coordinator_resumed", sessionId: "", timestamp: Date.now(), data: {} });
2812
3055
  const buffered = [...this.pauseBuffer];
2813
3056
  this.pauseBuffer = [];
@@ -2902,9 +3145,10 @@ class SwarmCoordinator {
2902
3145
  }
2903
3146
  writeSseEvent(res, event) {
2904
3147
  try {
2905
- res.write(`data: ${JSON.stringify(event)}\n\n`);
2906
- } catch {
2907
- }
3148
+ res.write(`data: ${JSON.stringify(event)}
3149
+
3150
+ `);
3151
+ } catch {}
2908
3152
  }
2909
3153
  async handleSessionEvent(sessionId, event, data) {
2910
3154
  const taskCtx = this.tasks.get(sessionId);
@@ -2923,8 +3167,7 @@ class SwarmCoordinator {
2923
3167
  if (ctx) {
2924
3168
  this.unregisteredBuffer.delete(sessionId);
2925
3169
  for (const entry of stillBuffered) {
2926
- this.handleSessionEvent(sessionId, entry.event, entry.data).catch(() => {
2927
- });
3170
+ this.handleSessionEvent(sessionId, entry.event, entry.data).catch(() => {});
2928
3171
  }
2929
3172
  } else {
2930
3173
  this.unregisteredBuffer.delete(sessionId);
@@ -3030,10 +3273,9 @@ class SwarmCoordinator {
3030
3273
  if (devUrl) {
3031
3274
  urlSuffix = ` Dev server running at ${devUrl}`;
3032
3275
  }
3033
- } catch {
3034
- }
3276
+ } catch {}
3035
3277
  }
3036
- this.sendChatMessage(`[${taskCtx.label}] Running ${toolDesc}.${urlSuffix} The agent is working outside the terminal \u2014 I'll let it finish.`, "coding-agent");
3278
+ this.sendChatMessage(`[${taskCtx.label}] Running ${toolDesc}.${urlSuffix} The agent is working outside the terminal I'll let it finish.`, "coding-agent");
3037
3279
  }
3038
3280
  break;
3039
3281
  }
@@ -3172,14 +3414,21 @@ class PTYService {
3172
3414
  const config = runtime.getSetting("PTY_SERVICE_CONFIG");
3173
3415
  const service = new PTYService(runtime, config ?? {});
3174
3416
  await service.initialize();
3175
- try {
3176
- const coordinator = new SwarmCoordinator(runtime);
3177
- coordinator.start(service);
3178
- service.coordinator = coordinator;
3179
- runtime.services.set("SWARM_COORDINATOR", [coordinator]);
3180
- logger2.info("[PTYService] SwarmCoordinator wired and started");
3181
- } catch (err) {
3182
- logger2.error(`[PTYService] Failed to wire SwarmCoordinator: ${err}`);
3417
+ const servicesMap = runtime.services;
3418
+ const existing = servicesMap?.get?.("SWARM_COORDINATOR");
3419
+ if (existing && existing.length > 0) {
3420
+ service.coordinator = existing[0];
3421
+ logger2.info("[PTYService] SwarmCoordinator already registered, skipping duplicate start");
3422
+ } else {
3423
+ try {
3424
+ const coordinator = new SwarmCoordinator(runtime);
3425
+ coordinator.start(service);
3426
+ service.coordinator = coordinator;
3427
+ servicesMap?.set?.("SWARM_COORDINATOR", [coordinator]);
3428
+ logger2.info("[PTYService] SwarmCoordinator wired and started");
3429
+ } catch (err) {
3430
+ logger2.error(`[PTYService] Failed to wire SwarmCoordinator: ${err}`);
3431
+ }
3183
3432
  }
3184
3433
  return service;
3185
3434
  }
@@ -3305,8 +3554,7 @@ class PTYService {
3305
3554
  let settings = {};
3306
3555
  try {
3307
3556
  settings = JSON.parse(await readFile(settingsPath, "utf-8"));
3308
- } catch {
3309
- }
3557
+ } catch {}
3310
3558
  const permissions = settings.permissions ?? {};
3311
3559
  permissions.allowedDirectories = [workdir];
3312
3560
  settings.permissions = permissions;
@@ -3678,7 +3926,9 @@ var spawnAgentAction = {
3678
3926
  if (preflight && !preflight.installed) {
3679
3927
  if (callback) {
3680
3928
  await callback({
3681
- text: `${preflight.adapter} CLI is not installed.\n` + `Install with: ${preflight.installCommand}\n` + `Docs: ${preflight.docsUrl}`
3929
+ text: `${preflight.adapter} CLI is not installed.
3930
+ ` + `Install with: ${preflight.installCommand}
3931
+ ` + `Docs: ${preflight.docsUrl}`
3682
3932
  });
3683
3933
  }
3684
3934
  return { success: false, error: "AGENT_NOT_INSTALLED" };
@@ -3804,16 +4054,16 @@ var spawnAgentAction = {
3804
4054
 
3805
4055
  // src/actions/coding-task-handlers.ts
3806
4056
  import {
3807
- logger as logger5
4057
+ logger as logger5
3808
4058
  } from "@elizaos/core";
3809
4059
 
3810
4060
  // src/actions/coding-task-helpers.ts
3811
- import {randomUUID} from "node:crypto";
4061
+ import { randomUUID } from "node:crypto";
3812
4062
  import * as fs from "node:fs";
3813
4063
  import * as os2 from "node:os";
3814
4064
  import * as path3 from "node:path";
3815
4065
  import {
3816
- logger as logger4
4066
+ logger as logger4
3817
4067
  } from "@elizaos/core";
3818
4068
  function createScratchDir() {
3819
4069
  const baseDir = path3.join(os2.homedir(), ".milady", "workspaces");
@@ -3852,7 +4102,9 @@ function registerSessionEvents(ptyService, runtime, sessionId, label, scratchDir
3852
4102
  const response = data.response ?? "";
3853
4103
  const preview = response.length > 500 ? `${response.slice(0, 500)}...` : response;
3854
4104
  callback({
3855
- text: preview ? `Agent "${label}" completed the task.\n\n${preview}` : `Agent "${label}" completed the task.`
4105
+ text: preview ? `Agent "${label}" completed the task.
4106
+
4107
+ ${preview}` : `Agent "${label}" completed the task.`
3856
4108
  });
3857
4109
  }
3858
4110
  ptyService.stopSession(sessionId, true).catch((err) => {
@@ -3877,6 +4129,7 @@ function registerSessionEvents(ptyService, runtime, sessionId, label, scratchDir
3877
4129
  }
3878
4130
 
3879
4131
  // src/actions/coding-task-handlers.ts
4132
+ var MAX_CONCURRENT_AGENTS = 8;
3880
4133
  async function handleMultiAgent(ctx, agentsParam) {
3881
4134
  const {
3882
4135
  runtime,
@@ -4057,7 +4310,8 @@ async function handleMultiAgent(ctx, agentsParam) {
4057
4310
  `Launched ${succeeded.length}/${agentSpecs.length} agents${repo ? ` on ${repo}` : ""}:`,
4058
4311
  ...succeeded.map((r) => ` - "${r.label}" (${r.agentType}) [session: ${r.sessionId}]`),
4059
4312
  ...failed.length > 0 ? [`Failed: ${failed.map((r) => `"${r.label}": ${r.error}`).join(", ")}`] : []
4060
- ].join("\n");
4313
+ ].join(`
4314
+ `);
4061
4315
  if (callback) {
4062
4316
  await callback({ text: summary });
4063
4317
  }
@@ -4138,7 +4392,9 @@ async function handleSingleAgent(ctx, task) {
4138
4392
  logger5.warn(`[START_CODING_TASK] ${preflight.adapter} CLI not installed`);
4139
4393
  if (callback) {
4140
4394
  await callback({
4141
- text: `${preflight.adapter} CLI is not installed.\nInstall with: ${preflight.installCommand}\nDocs: ${preflight.docsUrl}`
4395
+ text: `${preflight.adapter} CLI is not installed.
4396
+ Install with: ${preflight.installCommand}
4397
+ Docs: ${preflight.docsUrl}`
4142
4398
  });
4143
4399
  }
4144
4400
  return { success: false, error: "AGENT_NOT_INSTALLED" };
@@ -4191,7 +4447,8 @@ async function handleSingleAgent(ctx, task) {
4191
4447
  }
4192
4448
  const summary = repo ? `Cloned ${repo} and started ${displayType} agent as "${label}"${task ? ` with task: "${task}"` : ""}` : `Started ${displayType} agent as "${label}" in scratch workspace${task ? ` with task: "${task}"` : ""}`;
4193
4449
  if (callback) {
4194
- await callback({ text: `${summary}\nSession ID: ${session.id}` });
4450
+ await callback({ text: `${summary}
4451
+ Session ID: ${session.id}` });
4195
4452
  }
4196
4453
  return {
4197
4454
  success: true,
@@ -4217,7 +4474,6 @@ async function handleSingleAgent(ctx, task) {
4217
4474
  return { success: false, error: errorMessage };
4218
4475
  }
4219
4476
  }
4220
- var MAX_CONCURRENT_AGENTS = 8;
4221
4477
 
4222
4478
  // src/actions/start-coding-task.ts
4223
4479
  var startCodingTaskAction = {
@@ -4229,7 +4485,7 @@ var startCodingTaskAction = {
4229
4485
  "SPAWN_AND_PROVISION",
4230
4486
  "CODE_THIS"
4231
4487
  ],
4232
- description: "Start a coding task: optionally clone a repo, then spawn a coding agent (Claude Code, Codex, Gemini, Aider, Pi) " + "to work on it. If no repo is provided, the agent runs in a safe scratch directory. " + "Use this whenever the user asks to work on code, research something with an agent, or run any agent task. " + "IMPORTANT: If the user references a repository from conversation history (e.g. 'in the same repo', " + "'on that project', 'add a feature to it'), you MUST include the repo URL in the `repo` parameter. " + "If the task involves code changes to a real project but you don't know the repo URL, ASK the user for it " + "before calling this action \u2014 do not default to a scratch directory for real project work.",
4488
+ description: "Start a coding task: optionally clone a repo, then spawn a coding agent (Claude Code, Codex, Gemini, Aider, Pi) " + "to work on it. If no repo is provided, the agent runs in a safe scratch directory. " + "Use this whenever the user asks to work on code, research something with an agent, or run any agent task. " + "IMPORTANT: If the user references a repository from conversation history (e.g. 'in the same repo', " + "'on that project', 'add a feature to it'), you MUST include the repo URL in the `repo` parameter. " + "If the task involves code changes to a real project but you don't know the repo URL, ASK the user for it " + "before calling this action do not default to a scratch directory for real project work.",
4233
4489
  examples: [
4234
4490
  [
4235
4491
  {
@@ -4389,7 +4645,7 @@ var startCodingTaskAction = {
4389
4645
 
4390
4646
  // src/actions/stop-agent.ts
4391
4647
  import {
4392
- logger as logger6
4648
+ logger as logger6
4393
4649
  } from "@elizaos/core";
4394
4650
  var stopAgentAction = {
4395
4651
  name: "STOP_CODING_AGENT",
@@ -4552,15 +4808,6 @@ var stopAgentAction = {
4552
4808
  };
4553
4809
 
4554
4810
  // src/providers/action-examples.ts
4555
- function formatExample(ex) {
4556
- const actionTags = ex.actions.map((a) => ` <action>${a}</action>`).join("\n");
4557
- const paramBlocks = Object.entries(ex.params ?? {}).map(([actionName, params]) => {
4558
- const inner = Object.entries(params).map(([k, v]) => ` <${k}>${v}</${k}>`).join("\n");
4559
- return ` <${actionName}>\n${inner}\n </${actionName}>`;
4560
- }).join("\n");
4561
- const paramsSection = paramBlocks ? `\n<params>\n${paramBlocks}\n</params>` : "";
4562
- return `User: ${ex.user}\nAssistant:\n<actions>\n${actionTags}\n</actions>${paramsSection}`;
4563
- }
4564
4811
  var CODING_AGENT_EXAMPLES = [
4565
4812
  {
4566
4813
  user: "Can you set up a workspace for https://github.com/acme/my-app and have Claude fix the login bug?",
@@ -4569,7 +4816,7 @@ var CODING_AGENT_EXAMPLES = [
4569
4816
  START_CODING_TASK: {
4570
4817
  repo: "https://github.com/acme/my-app",
4571
4818
  agentType: "claude",
4572
- task: "Fix the login bug in src/auth.ts \u2014 users are getting 401 errors after token refresh"
4819
+ task: "Fix the login bug in src/auth.ts users are getting 401 errors after token refresh"
4573
4820
  }
4574
4821
  }
4575
4822
  },
@@ -4622,7 +4869,28 @@ var CODING_AGENT_EXAMPLES = [
4622
4869
  }
4623
4870
  }
4624
4871
  ];
4625
- var MULTI_AGENT_EXAMPLE = `User: Spin up 3 agents on https://github.com/acme/app \u2014 one to fix auth, one to write tests, one to update docs
4872
+ function formatExample(ex) {
4873
+ const actionTags = ex.actions.map((a) => ` <action>${a}</action>`).join(`
4874
+ `);
4875
+ const paramBlocks = Object.entries(ex.params ?? {}).map(([actionName, params]) => {
4876
+ const inner = Object.entries(params).map(([k, v]) => ` <${k}>${v}</${k}>`).join(`
4877
+ `);
4878
+ return ` <${actionName}>
4879
+ ${inner}
4880
+ </${actionName}>`;
4881
+ }).join(`
4882
+ `);
4883
+ const paramsSection = paramBlocks ? `
4884
+ <params>
4885
+ ${paramBlocks}
4886
+ </params>` : "";
4887
+ return `User: ${ex.user}
4888
+ Assistant:
4889
+ <actions>
4890
+ ${actionTags}
4891
+ </actions>${paramsSection}`;
4892
+ }
4893
+ var MULTI_AGENT_EXAMPLE = `User: Spin up 3 agents on https://github.com/acme/app — one to fix auth, one to write tests, one to update docs
4626
4894
  Assistant:
4627
4895
  <actions>
4628
4896
  <action>REPLY</action>
@@ -4631,7 +4899,7 @@ Assistant:
4631
4899
  <params>
4632
4900
  <START_CODING_TASK>
4633
4901
  <repo>https://github.com/acme/app</repo>
4634
- <agents>Fix the authentication bug in src/auth.ts \u2014 users get 401 after token refresh. Your unique identifier is "alpha". | Write comprehensive unit tests for the auth module in src/auth.ts. Your unique identifier is "beta". | Update the API documentation in docs/ to reflect the new auth flow. Your unique identifier is "gamma".</agents>
4902
+ <agents>Fix the authentication bug in src/auth.ts users get 401 after token refresh. Your unique identifier is "alpha". | Write comprehensive unit tests for the auth module in src/auth.ts. Your unique identifier is "beta". | Update the API documentation in docs/ to reflect the new auth flow. Your unique identifier is "gamma".</agents>
4635
4903
  </START_CODING_TASK>
4636
4904
  </params>`;
4637
4905
  var codingAgentExamplesProvider = {
@@ -4639,12 +4907,14 @@ var codingAgentExamplesProvider = {
4639
4907
  description: "Structured examples showing how to use coding agent actions with parameters",
4640
4908
  position: -1,
4641
4909
  get: async (_runtime, _message, _state) => {
4642
- const examples = CODING_AGENT_EXAMPLES.map(formatExample).join("\n\n");
4910
+ const examples = CODING_AGENT_EXAMPLES.map(formatExample).join(`
4911
+
4912
+ `);
4643
4913
  const text = [
4644
4914
  "# Coding Agent Action Call Examples",
4645
4915
  "When the user asks you to work on code, clone repos, spawn agents, or run agent tasks,",
4646
4916
  "you MUST select the appropriate actions and include parameters. Do NOT just describe",
4647
- "what you would do \u2014 actually select the actions.",
4917
+ "what you would do actually select the actions.",
4648
4918
  "",
4649
4919
  "IMPORTANT: Use START_CODING_TASK to launch coding agents. It handles workspace setup",
4650
4920
  "automatically. If a repo URL is provided, it clones it first. If no repo, the agent",
@@ -4662,7 +4932,8 @@ var codingAgentExamplesProvider = {
4662
4932
  "and unique identifiers so their work is clearly differentiated.",
4663
4933
  "",
4664
4934
  MULTI_AGENT_EXAMPLE
4665
- ].join("\n");
4935
+ ].join(`
4936
+ `);
4666
4937
  return {
4667
4938
  data: { codingAgentExamples: CODING_AGENT_EXAMPLES },
4668
4939
  values: { codingAgentExamples: text },
@@ -4695,7 +4966,7 @@ function formatWorkspaceLine(ws, sessions) {
4695
4966
  const label = ws.label || ws.id.slice(0, 8);
4696
4967
  const agents = sessions.filter((s) => s.workdir === ws.path);
4697
4968
  const agentSummary = agents.length > 0 ? agents.map((a) => `${a.agentType}:${formatStatus(a.status)}`).join(", ") : "no agents";
4698
- return ` - "${label}" \u2192 ${ws.repo} (branch: ${ws.branch}, ${agentSummary})`;
4969
+ return ` - "${label}" ${ws.repo} (branch: ${ws.branch}, ${agentSummary})`;
4699
4970
  }
4700
4971
  var activeWorkspaceContextProvider = {
4701
4972
  name: "ACTIVE_WORKSPACE_CONTEXT",
@@ -4724,7 +4995,8 @@ var activeWorkspaceContextProvider = {
4724
4995
  "# Active Workspaces & Agents",
4725
4996
  "No active workspaces or coding agent sessions.",
4726
4997
  "Use START_CODING_TASK to launch a new coding agent."
4727
- ].join("\n");
4998
+ ].join(`
4999
+ `);
4728
5000
  return {
4729
5001
  data: { activeWorkspaces: [], activeSessions: [] },
4730
5002
  values: { activeWorkspaceContext: text2 },
@@ -4754,9 +5026,9 @@ var activeWorkspaceContextProvider = {
4754
5026
  const supervisionLevel = coordinator.getSupervisionLevel();
4755
5027
  if (pending.length > 0) {
4756
5028
  lines.push("");
4757
- lines.push(`## Pending Confirmations (${pending.length}) \u2014 supervision: ${supervisionLevel}`);
5029
+ lines.push(`## Pending Confirmations (${pending.length}) supervision: ${supervisionLevel}`);
4758
5030
  for (const p of pending) {
4759
- lines.push(` - "${p.taskContext.label}" blocked: "${p.promptText}" \u2192 suggested: ${p.llmDecision.action}`);
5031
+ lines.push(` - "${p.taskContext.label}" blocked: "${p.promptText}" suggested: ${p.llmDecision.action}`);
4760
5032
  }
4761
5033
  } else if (supervisionLevel !== "autonomous") {
4762
5034
  lines.push("");
@@ -4767,7 +5039,8 @@ var activeWorkspaceContextProvider = {
4767
5039
  lines.push("");
4768
5040
  lines.push("You can interact with agents using SEND_TO_CODING_AGENT (pass sessionId), " + "stop them with STOP_CODING_AGENT, or finalize their work with FINALIZE_WORKSPACE.");
4769
5041
  }
4770
- const text = lines.join("\n");
5042
+ const text = lines.join(`
5043
+ `);
4771
5044
  return {
4772
5045
  data: {
4773
5046
  activeWorkspaces: workspaces.map((ws) => ({
@@ -4795,16 +5068,16 @@ var activeWorkspaceContextProvider = {
4795
5068
  import * as os3 from "node:os";
4796
5069
  import * as path5 from "node:path";
4797
5070
  import {
4798
- CredentialService,
4799
- GitHubPatClient as GitHubPatClient2,
4800
- MemoryTokenStore,
4801
- WorkspaceService
5071
+ CredentialService,
5072
+ GitHubPatClient as GitHubPatClient2,
5073
+ MemoryTokenStore,
5074
+ WorkspaceService
4802
5075
  } from "git-workspace-service";
4803
5076
 
4804
5077
  // src/services/workspace-github.ts
4805
5078
  import {
4806
- GitHubPatClient,
4807
- OAuthDeviceFlow
5079
+ GitHubPatClient,
5080
+ OAuthDeviceFlow
4808
5081
  } from "git-workspace-service";
4809
5082
  function parseOwnerRepo(repo) {
4810
5083
  const match = repo.match(/(?:github\.com\/)?([^/]+)\/([^/.]+)/);
@@ -4860,7 +5133,9 @@ async function performOAuthFlow(ctx, clientId) {
4860
5133
  expiresIn: deviceCode.expiresIn
4861
5134
  });
4862
5135
  } else {
4863
- console.log(`\n[GitHub Auth] Go to ${deviceCode.verificationUri} and enter code: ${deviceCode.userCode}\n`);
5136
+ console.log(`
5137
+ [GitHub Auth] Go to ${deviceCode.verificationUri} and enter code: ${deviceCode.userCode}
5138
+ `);
4864
5139
  }
4865
5140
  const token = await oauth.pollForToken(deviceCode);
4866
5141
  const client = new GitHubPatClient({ token: token.accessToken });
@@ -4929,7 +5204,8 @@ async function getStatus(workspacePath) {
4929
5204
  cwd: workspacePath,
4930
5205
  encoding: "utf-8"
4931
5206
  }).trim();
4932
- const lines = statusOutput.split("\n").filter(Boolean);
5207
+ const lines = statusOutput.split(`
5208
+ `).filter(Boolean);
4933
5209
  const modified = [];
4934
5210
  const staged = [];
4935
5211
  const untracked = [];
@@ -5637,6 +5913,7 @@ async function handleAgentRoutes(req, res, pathname, ctx) {
5637
5913
  }
5638
5914
 
5639
5915
  // src/api/coordinator-routes.ts
5916
+ var COORDINATOR_PREFIX = "/api/coding-agents/coordinator";
5640
5917
  async function handleCoordinatorRoutes(req, res, pathname, ctx) {
5641
5918
  if (!pathname.startsWith(COORDINATOR_PREFIX)) {
5642
5919
  return false;
@@ -5654,7 +5931,9 @@ async function handleCoordinatorRoutes(req, res, pathname, ctx) {
5654
5931
  "Cache-Control": "no-cache",
5655
5932
  Connection: "keep-alive"
5656
5933
  });
5657
- res.write(":ok\n\n");
5934
+ res.write(`:ok
5935
+
5936
+ `);
5658
5937
  const unsubscribe = coordinator.addSseClient(res);
5659
5938
  req.on("close", unsubscribe);
5660
5939
  const keepAlive = setInterval(() => {
@@ -5662,7 +5941,9 @@ async function handleCoordinatorRoutes(req, res, pathname, ctx) {
5662
5941
  clearInterval(keepAlive);
5663
5942
  return;
5664
5943
  }
5665
- res.write(":ping\n\n");
5944
+ res.write(`:ping
5945
+
5946
+ `);
5666
5947
  }, 30000);
5667
5948
  req.on("close", () => clearInterval(keepAlive));
5668
5949
  return true;
@@ -5747,7 +6028,6 @@ async function handleCoordinatorRoutes(req, res, pathname, ctx) {
5747
6028
  }
5748
6029
  return false;
5749
6030
  }
5750
- var COORDINATOR_PREFIX = "/api/coding-agents/coordinator";
5751
6031
 
5752
6032
  // src/api/issue-routes.ts
5753
6033
  async function handleIssueRoutes(req, res, pathname, ctx) {
@@ -5979,6 +6259,7 @@ async function handleWorkspaceRoutes(req, res, pathname, ctx) {
5979
6259
  }
5980
6260
 
5981
6261
  // src/api/routes.ts
6262
+ var MAX_BODY_SIZE = 1024 * 1024;
5982
6263
  async function parseBody(req) {
5983
6264
  return new Promise((resolve5, reject) => {
5984
6265
  let body = "";
@@ -6035,7 +6316,6 @@ function createCodingAgentRouteHandler(runtime, coordinator) {
6035
6316
  };
6036
6317
  return (req, res, pathname) => handleCodingAgentRoutes(req, res, pathname, ctx);
6037
6318
  }
6038
- var MAX_BODY_SIZE = 1024 * 1024;
6039
6319
 
6040
6320
  // src/index.ts
6041
6321
  var codingAgentPlugin = {
@@ -6080,5 +6360,5 @@ export {
6080
6360
  CodingWorkspaceService
6081
6361
  };
6082
6362
 
6083
- //# debugId=BA5A6C5F800D868764756E2164756E21
6363
+ //# debugId=7296EE1D6046456D64756E2164756E21
6084
6364
  //# sourceMappingURL=index.js.map