@workermill/agent 0.7.1 → 0.7.2

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.
@@ -105,9 +105,23 @@ export async function startCommand(options) {
105
105
  const cleanup = await startAgent(config);
106
106
  // Write PID file for status command
107
107
  writeFileSync(getPidFile(), String(process.pid), "utf-8");
108
- // Graceful shutdown
108
+ // Graceful shutdown with force-exit safety net
109
+ let shuttingDown = false;
109
110
  const shutdown = async () => {
111
+ if (shuttingDown) {
112
+ // Double Ctrl+C → force exit immediately
113
+ console.log(chalk.red("\n Force exit."));
114
+ process.exit(1);
115
+ }
116
+ shuttingDown = true;
117
+ // Force exit after 10s if cleanup hangs
118
+ const forceTimer = setTimeout(() => {
119
+ console.log(chalk.red("\n Cleanup timed out. Force exit."));
120
+ process.exit(1);
121
+ }, 10_000);
122
+ forceTimer.unref(); // Don't keep process alive just for this timer
110
123
  await cleanup();
124
+ clearTimeout(forceTimer);
111
125
  try {
112
126
  unlinkSync(getPidFile());
113
127
  }
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import chalk from "chalk";
8
8
  import { initApi, api } from "./api.js";
9
- import { startPolling, startHeartbeat } from "./poller.js";
9
+ import { startPolling, startHeartbeat, stopPolling } from "./poller.js";
10
10
  import { stopAll } from "./spawner.js";
11
11
  import { AGENT_VERSION } from "./version.js";
12
12
  import { selfUpdate, restartAgent } from "./updater.js";
@@ -82,6 +82,8 @@ export async function startAgent(config) {
82
82
  return async () => {
83
83
  console.log();
84
84
  console.log(chalk.dim(" Shutting down..."));
85
+ // Stop poll/heartbeat loops first so nothing re-fires during cleanup
86
+ stopPolling();
85
87
  try {
86
88
  await api.post("/api/agent/deregister", { agentId: config.agentId });
87
89
  }
package/dist/planner.js CHANGED
@@ -374,11 +374,19 @@ function runAnalyst(name, claudePath, model, prompt, repoPath, env, timeoutMs =
374
374
  for (const block of content) {
375
375
  if (block.type === "text" && block.text) {
376
376
  fullText += block.text;
377
+ // Log analyst reasoning (first line, truncated)
378
+ const thought = block.text.trim().split("\n")[0].substring(0, 120);
379
+ if (thought) {
380
+ console.log(`${ts()} ${label} ${chalk.dim("💭")} ${chalk.dim(thought)}`);
381
+ }
377
382
  }
378
383
  else if (block.type === "tool_use") {
379
384
  toolCalls++;
380
385
  const toolName = block.name || "unknown";
381
- console.log(`${ts()} ${label} ${chalk.dim(`Tool: ${toolName}`)} (${toolCalls} total)`);
386
+ // Show tool name + input preview (file path, pattern, etc.)
387
+ const inputStr = block.input ? JSON.stringify(block.input) : "";
388
+ const inputPreview = inputStr.length > 80 ? inputStr.substring(0, 80) + "…" : inputStr;
389
+ console.log(`${ts()} ${label} ${chalk.dim(`Tool: ${toolName}`)}${inputPreview ? chalk.dim(` ${inputPreview}`) : ""} (${toolCalls} total)`);
382
390
  }
383
391
  }
384
392
  }
package/dist/poller.d.ts CHANGED
@@ -5,6 +5,11 @@
5
5
  * dispatch to planner or spawner based on task status.
6
6
  */
7
7
  import type { AgentConfig } from "./config.js";
8
+ /**
9
+ * Stop all polling and heartbeat loops.
10
+ * Called during graceful shutdown to prevent the event loop from staying alive.
11
+ */
12
+ export declare function stopPolling(): void;
8
13
  /**
9
14
  * Start the poll loop.
10
15
  */
package/dist/poller.js CHANGED
@@ -235,6 +235,23 @@ async function handleManagerTask(task, config) {
235
235
  console.error(`${ts()} ${taskLabel} ${chalk.red("✗")} Failed to claim manager task:`, error.message || String(err));
236
236
  }
237
237
  }
238
+ // Store interval IDs so they can be cleared on shutdown
239
+ let pollIntervalId = null;
240
+ let heartbeatIntervalId = null;
241
+ /**
242
+ * Stop all polling and heartbeat loops.
243
+ * Called during graceful shutdown to prevent the event loop from staying alive.
244
+ */
245
+ export function stopPolling() {
246
+ if (pollIntervalId) {
247
+ clearInterval(pollIntervalId);
248
+ pollIntervalId = null;
249
+ }
250
+ if (heartbeatIntervalId) {
251
+ clearInterval(heartbeatIntervalId);
252
+ heartbeatIntervalId = null;
253
+ }
254
+ }
238
255
  /**
239
256
  * Start the poll loop.
240
257
  */
@@ -243,13 +260,13 @@ export function startPolling(config) {
243
260
  // Initial poll
244
261
  pollOnce(config);
245
262
  // Recurring poll
246
- setInterval(() => pollOnce(config), config.pollIntervalMs);
263
+ pollIntervalId = setInterval(() => pollOnce(config), config.pollIntervalMs);
247
264
  }
248
265
  /**
249
266
  * Start the heartbeat loop.
250
267
  */
251
268
  export function startHeartbeat(config) {
252
- setInterval(async () => {
269
+ heartbeatIntervalId = setInterval(async () => {
253
270
  // Include BOTH running containers AND tasks being planned/managed
254
271
  const containerTaskIds = getActiveTaskIds();
255
272
  const planningTaskIds = Array.from(planningInProgress);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@workermill/agent",
3
- "version": "0.7.1",
3
+ "version": "0.7.2",
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",