@workermill/agent 0.7.19 → 0.7.20

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 CHANGED
@@ -70,20 +70,50 @@ const MAX_ITERATIONS = 3;
70
70
  function ts() {
71
71
  return chalk.dim(new Date().toLocaleTimeString());
72
72
  }
73
+ /**
74
+ * Log queue — sends entries sequentially instead of N concurrent POSTs.
75
+ * During planning, flushTextBuffer() can fire 15-30 postLog() calls in a burst.
76
+ * Without queuing, those concurrent POSTs saturate the API's DB connection pool
77
+ * (max 10), causing poll timeouts, transient 401s, and multi-second stalls.
78
+ */
79
+ const logQueue = [];
80
+ let logDrainPromise = null;
81
+ async function drainLogQueue() {
82
+ while (logQueue.length > 0) {
83
+ const entry = logQueue.shift();
84
+ try {
85
+ await api.post("/api/control-center/logs", entry, { timeout: 5_000 });
86
+ }
87
+ catch {
88
+ // Best-effort — drop on failure
89
+ }
90
+ }
91
+ }
73
92
  /**
74
93
  * Post a log message to the cloud dashboard for real-time visibility.
94
+ * Entries are queued and drained sequentially (max 1 in-flight POST).
75
95
  */
76
96
  async function postLog(taskId, message, type = "system", severity = "info") {
77
- try {
78
- await api.post("/api/control-center/logs", {
79
- taskId,
80
- type,
81
- message,
82
- severity,
97
+ if (logQueue.length >= 200)
98
+ logQueue.shift(); // drop oldest
99
+ logQueue.push({ taskId, message, type, severity });
100
+ if (!logDrainPromise) {
101
+ logDrainPromise = drainLogQueue().finally(() => {
102
+ logDrainPromise = null;
83
103
  });
84
104
  }
85
- catch {
86
- // Fire and forget — don't block planning on log failures
105
+ }
106
+ /**
107
+ * Flush remaining log entries (call before cleanup).
108
+ */
109
+ async function flushLogQueue() {
110
+ if (logDrainPromise)
111
+ await logDrainPromise;
112
+ if (logQueue.length > 0) {
113
+ logDrainPromise = drainLogQueue().finally(() => {
114
+ logDrainPromise = null;
115
+ });
116
+ await logDrainPromise;
87
117
  }
88
118
  }
89
119
  /**
@@ -1038,6 +1068,8 @@ export async function planTask(task, config, credentials) {
1038
1068
  return false;
1039
1069
  }
1040
1070
  finally {
1071
+ // Drain any remaining log entries before cleanup
1072
+ await flushLogQueue();
1041
1073
  // Cleanup temp clone
1042
1074
  if (repoPath) {
1043
1075
  try {
package/dist/poller.js CHANGED
@@ -75,12 +75,17 @@ async function pollOnce(config) {
75
75
  }
76
76
  catch (error) {
77
77
  const err = error;
78
+ const busy = planningInProgress.size > 0 || getActiveCount() > 0 || managerInProgress.size > 0;
78
79
  if (err.response?.status === 401) {
79
- console.error(`${ts()} ${chalk.red("✗")} Authentication failed. Check your API key.`);
80
+ if (!busy) {
81
+ console.error(`${ts()} ${chalk.red("✗")} Authentication failed. Check your API key.`);
82
+ }
83
+ // Silent when busy — transient DB pool exhaustion on server
80
84
  }
81
- else {
82
- console.error(`${ts()} ${chalk.red("")} Poll error: ${err.message || String(error)}`);
85
+ else if (!busy) {
86
+ console.warn(`${ts()} ${chalk.yellow("")} Poll error: ${err.message || String(error)}`);
83
87
  }
88
+ // Silent when busy — expected during heavy planning/execution
84
89
  }
85
90
  }
86
91
  /**
@@ -181,7 +186,7 @@ async function handleQueuedTask(task, config) {
181
186
  }
182
187
  catch (err) {
183
188
  const taskLabel = chalk.cyan(task.id.slice(0, 8));
184
- console.error(`${ts()} ${chalk.red("")} Failed to report started for ${taskLabel}`);
189
+ console.warn(`${ts()} ${chalk.yellow("")} Failed to report started for ${taskLabel}`);
185
190
  }
186
191
  const taskLabel = chalk.cyan(task.id.slice(0, 8));
187
192
  console.log();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@workermill/agent",
3
- "version": "0.7.19",
3
+ "version": "0.7.20",
4
4
  "description": "WorkerMill Remote Agent - Run AI workers locally with your Claude Max subscription",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",