@workermill/agent 0.7.3 → 0.7.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/planner.js +27 -0
- package/dist/poller.js +29 -0
- package/package.json +1 -1
package/dist/planner.js
CHANGED
|
@@ -103,6 +103,24 @@ function runClaudeCli(claudePath, model, prompt, env, taskId, startTime) {
|
|
|
103
103
|
let stderrOutput = "";
|
|
104
104
|
let charsReceived = 0;
|
|
105
105
|
let toolCallCount = 0;
|
|
106
|
+
// Buffered text streaming — flush complete lines to dashboard every 1s.
|
|
107
|
+
// LLM deltas are tiny fragments; we accumulate until we see '\n', then
|
|
108
|
+
// a 1s interval flushes all complete lines as log entries. On exit we
|
|
109
|
+
// flush whatever remains (including any incomplete trailing line).
|
|
110
|
+
let textBuffer = "";
|
|
111
|
+
function flushTextBuffer(final = false) {
|
|
112
|
+
if (!textBuffer)
|
|
113
|
+
return;
|
|
114
|
+
const parts = textBuffer.split("\n");
|
|
115
|
+
// Keep the incomplete trailing fragment unless this is the final flush
|
|
116
|
+
const incomplete = final ? "" : (parts.pop() || "");
|
|
117
|
+
for (const line of parts) {
|
|
118
|
+
if (line.trim()) {
|
|
119
|
+
postLog(taskId, `${PREFIX} ${line}`, "output");
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
textBuffer = incomplete;
|
|
123
|
+
}
|
|
106
124
|
// Phase detection state
|
|
107
125
|
let currentPhase = "initializing";
|
|
108
126
|
let firstTextSeen = false;
|
|
@@ -117,6 +135,8 @@ function runClaudeCli(claudePath, model, prompt, env, taskId, startTime) {
|
|
|
117
135
|
postLog(taskId, msg);
|
|
118
136
|
console.log(`${ts()} ${taskLabel} ${chalk.dim(msg)}`);
|
|
119
137
|
}
|
|
138
|
+
// Flush buffered LLM text to dashboard every 1s (complete lines only)
|
|
139
|
+
const textFlushInterval = setInterval(() => flushTextBuffer(), 1_000);
|
|
120
140
|
// SSE progress updates every 2s — drives PlanningTerminalBar in dashboard
|
|
121
141
|
// (same cadence as local dev's progressInterval in planning-agent-local.ts)
|
|
122
142
|
const sseProgressInterval = setInterval(() => {
|
|
@@ -193,6 +213,7 @@ function runClaudeCli(claudePath, model, prompt, env, taskId, startTime) {
|
|
|
193
213
|
// Fallback: raw API streaming format
|
|
194
214
|
fullText += event.delta.text;
|
|
195
215
|
charsReceived += event.delta.text.length;
|
|
216
|
+
textBuffer += event.delta.text;
|
|
196
217
|
if (!firstTextSeen) {
|
|
197
218
|
firstTextSeen = true;
|
|
198
219
|
if (toolCallCount > 0 && !milestoneSent.analyzing) {
|
|
@@ -230,6 +251,8 @@ function runClaudeCli(claudePath, model, prompt, env, taskId, startTime) {
|
|
|
230
251
|
const timeout = setTimeout(() => {
|
|
231
252
|
clearInterval(progressInterval);
|
|
232
253
|
clearInterval(sseProgressInterval);
|
|
254
|
+
clearInterval(textFlushInterval);
|
|
255
|
+
flushTextBuffer(true);
|
|
233
256
|
proc.kill("SIGTERM");
|
|
234
257
|
reject(new Error("Claude CLI timed out after 20 minutes"));
|
|
235
258
|
}, 1_200_000);
|
|
@@ -237,6 +260,8 @@ function runClaudeCli(claudePath, model, prompt, env, taskId, startTime) {
|
|
|
237
260
|
clearTimeout(timeout);
|
|
238
261
|
clearInterval(progressInterval);
|
|
239
262
|
clearInterval(sseProgressInterval);
|
|
263
|
+
clearInterval(textFlushInterval);
|
|
264
|
+
flushTextBuffer(true);
|
|
240
265
|
// Emit final "validating" phase to dashboard
|
|
241
266
|
const elapsedAtClose = Math.round((Date.now() - startTime) / 1000);
|
|
242
267
|
postProgress(taskId, "validating", elapsedAtClose, "Validating plan...", charsReceived, toolCallCount);
|
|
@@ -252,6 +277,8 @@ function runClaudeCli(claudePath, model, prompt, env, taskId, startTime) {
|
|
|
252
277
|
clearTimeout(timeout);
|
|
253
278
|
clearInterval(progressInterval);
|
|
254
279
|
clearInterval(sseProgressInterval);
|
|
280
|
+
clearInterval(textFlushInterval);
|
|
281
|
+
flushTextBuffer(true);
|
|
255
282
|
reject(err);
|
|
256
283
|
});
|
|
257
284
|
});
|
package/dist/poller.js
CHANGED
|
@@ -89,6 +89,7 @@ async function pollOnce(config) {
|
|
|
89
89
|
async function handlePlanningTask(task, config) {
|
|
90
90
|
// Claim the task (also returns org credentials for provider API keys)
|
|
91
91
|
let credentials;
|
|
92
|
+
let claimedTask;
|
|
92
93
|
try {
|
|
93
94
|
const claimResponse = await api.post("/api/agent/claim", {
|
|
94
95
|
taskId: task.id,
|
|
@@ -98,11 +99,39 @@ async function handlePlanningTask(task, config) {
|
|
|
98
99
|
return; // Another agent or cloud orchestrator claimed it
|
|
99
100
|
}
|
|
100
101
|
credentials = claimResponse.data.credentials;
|
|
102
|
+
claimedTask = claimResponse.data.task;
|
|
101
103
|
}
|
|
102
104
|
catch {
|
|
103
105
|
return;
|
|
104
106
|
}
|
|
105
107
|
const taskLabel = chalk.cyan(task.id.slice(0, 8));
|
|
108
|
+
// Check if this is a retry with an existing plan (resume scenario).
|
|
109
|
+
// The API preserves planJson/executionPlanV2 on retry when stories exist,
|
|
110
|
+
// so we can skip planning entirely and transition straight to queued.
|
|
111
|
+
const isRetryWithPlan = claimedTask &&
|
|
112
|
+
(claimedTask.retryCount ?? 0) > 0 &&
|
|
113
|
+
claimedTask.executionPlanV2 != null;
|
|
114
|
+
if (isRetryWithPlan) {
|
|
115
|
+
console.log();
|
|
116
|
+
console.log(`${ts()} ${chalk.magenta("◆ RESUME")} ${taskLabel} ${task.summary.substring(0, 60)}`);
|
|
117
|
+
console.log(`${ts()} ${taskLabel} Retry #${claimedTask.retryCount} with existing plan — skipping planning`);
|
|
118
|
+
planningInProgress.add(task.id);
|
|
119
|
+
// Tell the API to resume with the existing plan (planning → queued)
|
|
120
|
+
try {
|
|
121
|
+
await api.post("/api/agent/resume-plan", {
|
|
122
|
+
taskId: task.id,
|
|
123
|
+
agentId: config.agentId,
|
|
124
|
+
});
|
|
125
|
+
console.log(`${ts()} ${taskLabel} ${chalk.green("✓")} Resumed with existing plan → ${chalk.green("queued")}`);
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
const error = err;
|
|
129
|
+
const detail = error.response?.data?.error || error.message || String(err);
|
|
130
|
+
console.error(`${ts()} ${taskLabel} ${chalk.red("✗")} Resume failed: ${detail}`);
|
|
131
|
+
}
|
|
132
|
+
planningInProgress.delete(task.id);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
106
135
|
console.log();
|
|
107
136
|
console.log(`${ts()} ${chalk.magenta("◆ PLANNING")} ${taskLabel} ${task.summary.substring(0, 60)}`);
|
|
108
137
|
planningInProgress.add(task.id);
|