@gethmy/agent 1.0.1 → 1.0.3

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/worker.js CHANGED
@@ -23,6 +23,7 @@ export class Worker {
23
23
  process = null;
24
24
  timeoutTimer = null;
25
25
  progressTracker = null;
26
+ lastSessionStats;
26
27
  aborted = false;
27
28
  constructor(id, config, client, _userEmail, onDone) {
28
29
  this.config = config;
@@ -111,7 +112,7 @@ export class Worker {
111
112
  progressPercent: 75,
112
113
  });
113
114
  this.state = "completing";
114
- await runCompletion(this.client, card, this.branchName, this.worktreePath, this.config, this.id);
115
+ await runCompletion(this.client, card, this.branchName, this.worktreePath, this.config, this.id, this.lastSessionStats);
115
116
  }
116
117
  catch (err) {
117
118
  this.state = "error";
@@ -133,6 +134,60 @@ export class Worker {
133
134
  this.onDone(this);
134
135
  }
135
136
  }
137
+ /**
138
+ * Pause the current work by suspending the Claude process (SIGSTOP).
139
+ */
140
+ async pause() {
141
+ if (!this.isActive || !this.process || this.process.killed)
142
+ return;
143
+ log.info(this.tag, `Pausing work on ${this.cardId}`);
144
+ this.process.kill("SIGSTOP");
145
+ // Pause the timeout timer
146
+ if (this.timeoutTimer) {
147
+ clearTimeout(this.timeoutTimer);
148
+ this.timeoutTimer = null;
149
+ }
150
+ // Update agent session so the UI reflects the paused state
151
+ if (this.cardId) {
152
+ try {
153
+ await this.client.updateAgentProgress(this.cardId, {
154
+ agentIdentifier: agentIdentifier(this.id),
155
+ agentName: AGENT_NAME,
156
+ status: "paused",
157
+ });
158
+ }
159
+ catch {
160
+ log.warn(this.tag, "Failed to update agent session to paused");
161
+ }
162
+ }
163
+ }
164
+ /**
165
+ * Resume the Claude process after a pause (SIGCONT).
166
+ */
167
+ async resume() {
168
+ if (!this.isActive || !this.process || this.process.killed)
169
+ return;
170
+ log.info(this.tag, `Resuming work on ${this.cardId}`);
171
+ this.process.kill("SIGCONT");
172
+ // Restart timeout timer with remaining time (use full timeout for simplicity)
173
+ this.timeoutTimer = setTimeout(() => {
174
+ log.warn(this.tag, `Timeout reached (${this.config.maxTimeout}ms), cancelling`);
175
+ this.cancel();
176
+ }, this.config.maxTimeout);
177
+ // Update agent session so the UI reflects the resumed state
178
+ if (this.cardId) {
179
+ try {
180
+ await this.client.updateAgentProgress(this.cardId, {
181
+ agentIdentifier: agentIdentifier(this.id),
182
+ agentName: AGENT_NAME,
183
+ status: "working",
184
+ });
185
+ }
186
+ catch {
187
+ log.warn(this.tag, "Failed to update agent session to working");
188
+ }
189
+ }
190
+ }
136
191
  /**
137
192
  * Cancel the current work. Sends escalating signals to the Claude process.
138
193
  */
@@ -143,6 +198,8 @@ export class Worker {
143
198
  this.state = "cancelling";
144
199
  log.info(this.tag, `Cancelling work on ${this.cardId}`);
145
200
  if (this.process && !this.process.killed) {
201
+ // Resume first in case the process is suspended
202
+ this.process.kill("SIGCONT");
146
203
  // Step 1: SIGINT (let Claude save state gracefully)
147
204
  this.process.kill("SIGINT");
148
205
  log.debug(this.tag, "Sent SIGINT");
@@ -196,11 +253,11 @@ export class Worker {
196
253
  // Progress tracker for phase-based updates
197
254
  this.progressTracker = new ProgressTracker(this.client, card.id, this.id, subtasks);
198
255
  this.progressTracker.attach(parser);
199
- // Pipe stdout through parser
256
+ // Attach stdout to parser
200
257
  if (this.process.stdout) {
201
- parser.pipe(this.process.stdout);
258
+ parser.attach(this.process.stdout);
202
259
  }
203
- parser.on("error", (msg) => {
260
+ parser.on("parse_error", (msg) => {
204
261
  log.debug(this.tag, `Stream parse error (non-fatal): ${msg}`);
205
262
  });
206
263
  let stderr = "";
@@ -212,6 +269,7 @@ export class Worker {
212
269
  });
213
270
  this.process.on("close", (code) => {
214
271
  this.process = null;
272
+ this.lastSessionStats = this.progressTracker?.stats;
215
273
  this.progressTracker?.stop();
216
274
  this.progressTracker = null;
217
275
  if (this.aborted) {
@@ -246,6 +304,7 @@ export class Worker {
246
304
  this.progressTracker.stop();
247
305
  this.progressTracker = null;
248
306
  }
307
+ this.lastSessionStats = undefined;
249
308
  if (this.timeoutTimer) {
250
309
  clearTimeout(this.timeoutTimer);
251
310
  this.timeoutTimer = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gethmy/agent",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Push-based agent daemon for Harmony — watches board assignments and spawns Claude CLI workers",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -32,7 +32,7 @@
32
32
  "automation"
33
33
  ],
34
34
  "engines": {
35
- "node": ">=18.0.0",
35
+ "node": ">=20.0.0",
36
36
  "bun": ">=1.0.0"
37
37
  },
38
38
  "scripts": {
@@ -47,7 +47,7 @@
47
47
  },
48
48
  "devDependencies": {
49
49
  "@harmony/shared": "workspace:*",
50
- "@types/node": "^25.2.0",
51
- "typescript": "^5.9.3"
50
+ "@types/node": "^25.5.0",
51
+ "typescript": "^6.0.1"
52
52
  }
53
53
  }