@agentteams/runner 0.0.84-dev.206 → 0.0.84-dev.208

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.
Files changed (122) hide show
  1. package/dist/api-client.d.ts +2 -2
  2. package/dist/api-client.js +66 -66
  3. package/dist/api-client.js.map +1 -1
  4. package/dist/api-client.test.js +63 -54
  5. package/dist/api-client.test.js.map +1 -1
  6. package/dist/autostart.js +81 -81
  7. package/dist/autostart.js.map +1 -1
  8. package/dist/autostart.test.js +35 -35
  9. package/dist/autostart.test.js.map +1 -1
  10. package/dist/commands/cleanup.js +4 -4
  11. package/dist/commands/init.js +14 -14
  12. package/dist/commands/restart.js +3 -3
  13. package/dist/commands/start.js +10 -10
  14. package/dist/commands/start.js.map +1 -1
  15. package/dist/commands/status.js +7 -7
  16. package/dist/commands/stop.js +7 -7
  17. package/dist/commands/uninstall.js +6 -6
  18. package/dist/commands/update.d.ts +4 -4
  19. package/dist/commands/update.js +20 -22
  20. package/dist/commands/update.js.map +1 -1
  21. package/dist/commands/update.test.js +38 -42
  22. package/dist/commands/update.test.js.map +1 -1
  23. package/dist/config.d.ts +1 -1
  24. package/dist/config.js +12 -12
  25. package/dist/config.test.js +61 -61
  26. package/dist/config.test.js.map +1 -1
  27. package/dist/daemon-control.d.ts +5 -5
  28. package/dist/daemon-control.js +21 -22
  29. package/dist/daemon-control.js.map +1 -1
  30. package/dist/daemon-control.test.js +29 -29
  31. package/dist/executable.d.ts +3 -3
  32. package/dist/executable.js +46 -49
  33. package/dist/executable.js.map +1 -1
  34. package/dist/executable.test.js +62 -62
  35. package/dist/executable.test.js.map +1 -1
  36. package/dist/handlers/trigger-handler.d.ts +6 -6
  37. package/dist/handlers/trigger-handler.js +118 -124
  38. package/dist/handlers/trigger-handler.js.map +1 -1
  39. package/dist/handlers/trigger-handler.test.js +373 -365
  40. package/dist/handlers/trigger-handler.test.js.map +1 -1
  41. package/dist/index.js +25 -25
  42. package/dist/logger.js +1 -1
  43. package/dist/pid.js +6 -6
  44. package/dist/poller.d.ts +7 -7
  45. package/dist/poller.js +54 -53
  46. package/dist/poller.js.map +1 -1
  47. package/dist/poller.test.js +209 -181
  48. package/dist/poller.test.js.map +1 -1
  49. package/dist/runners/amp.d.ts +1 -1
  50. package/dist/runners/amp.js +74 -81
  51. package/dist/runners/amp.js.map +1 -1
  52. package/dist/runners/antigravity.d.ts +3 -3
  53. package/dist/runners/antigravity.js +116 -127
  54. package/dist/runners/antigravity.js.map +1 -1
  55. package/dist/runners/antigravity.replay.test.js +25 -25
  56. package/dist/runners/antigravity.replay.test.js.map +1 -1
  57. package/dist/runners/antigravity.test.js +98 -98
  58. package/dist/runners/antigravity.test.js.map +1 -1
  59. package/dist/runners/claude-code.d.ts +1 -1
  60. package/dist/runners/claude-code.js +85 -84
  61. package/dist/runners/claude-code.js.map +1 -1
  62. package/dist/runners/claude-code.test.js +37 -40
  63. package/dist/runners/claude-code.test.js.map +1 -1
  64. package/dist/runners/codex.d.ts +1 -1
  65. package/dist/runners/codex.js +87 -96
  66. package/dist/runners/codex.js.map +1 -1
  67. package/dist/runners/codex.test.js +68 -68
  68. package/dist/runners/codex.test.js.map +1 -1
  69. package/dist/runners/index.d.ts +1 -1
  70. package/dist/runners/index.js +10 -10
  71. package/dist/runners/index.test.js +16 -16
  72. package/dist/runners/log-reporter.d.ts +2 -2
  73. package/dist/runners/log-reporter.js +10 -10
  74. package/dist/runners/log-reporter.test.js +70 -70
  75. package/dist/runners/log-reporter.test.js.map +1 -1
  76. package/dist/runners/opencode.d.ts +1 -1
  77. package/dist/runners/opencode.js +68 -75
  78. package/dist/runners/opencode.js.map +1 -1
  79. package/dist/runners/process-control.d.ts +1 -1
  80. package/dist/runners/process-control.js +13 -11
  81. package/dist/runners/process-control.js.map +1 -1
  82. package/dist/runners/stream-json-parser.d.ts +1 -1
  83. package/dist/runners/stream-json-parser.js +70 -67
  84. package/dist/runners/stream-json-parser.js.map +1 -1
  85. package/dist/runners/stream-json-parser.replay.test.js +17 -17
  86. package/dist/runners/stream-json-parser.replay.test.js.map +1 -1
  87. package/dist/runners/stream-json-parser.test.js +76 -78
  88. package/dist/runners/stream-json-parser.test.js.map +1 -1
  89. package/dist/types.d.ts +4 -4
  90. package/dist/utils/auth-path-store.js +7 -7
  91. package/dist/utils/auth-path-store.test.js +24 -29
  92. package/dist/utils/auth-path-store.test.js.map +1 -1
  93. package/dist/utils/auto-update.d.ts +4 -4
  94. package/dist/utils/auto-update.js +30 -28
  95. package/dist/utils/auto-update.js.map +1 -1
  96. package/dist/utils/auto-update.test.js +30 -30
  97. package/dist/utils/convention-evaluator.d.ts +1 -1
  98. package/dist/utils/convention-evaluator.js +22 -21
  99. package/dist/utils/convention-evaluator.js.map +1 -1
  100. package/dist/utils/convention-evaluator.test.js +120 -110
  101. package/dist/utils/convention-evaluator.test.js.map +1 -1
  102. package/dist/utils/convention-sync.d.ts +3 -3
  103. package/dist/utils/convention-sync.js +10 -10
  104. package/dist/utils/convention-sync.test.js +39 -25
  105. package/dist/utils/convention-sync.test.js.map +1 -1
  106. package/dist/utils/git-worktree.js +20 -18
  107. package/dist/utils/git-worktree.js.map +1 -1
  108. package/dist/utils/git-worktree.test.js +98 -98
  109. package/dist/utils/git-worktree.test.js.map +1 -1
  110. package/dist/utils/origin-issue-safeguard.js +35 -30
  111. package/dist/utils/origin-issue-safeguard.js.map +1 -1
  112. package/dist/utils/power-save-blocker.d.ts +4 -4
  113. package/dist/utils/power-save-blocker.js +33 -33
  114. package/dist/utils/power-save-blocker.test.js +37 -37
  115. package/dist/utils/power-save-blocker.test.js.map +1 -1
  116. package/dist/utils/runner-cleanup.d.ts +3 -3
  117. package/dist/utils/runner-cleanup.js +13 -13
  118. package/dist/utils/runner-cleanup.test.js +35 -35
  119. package/dist/utils/runner-history.js +3 -3
  120. package/dist/utils/runner-history.test.js +17 -17
  121. package/package.json +4 -1
  122. package/readme.md +10 -10
@@ -1,34 +1,34 @@
1
- import assert from "node:assert/strict";
2
- import * as fsModule from "node:fs";
3
- import os from "node:os";
4
- import path from "node:path";
5
- import test, { mock } from "node:test";
6
- import { logger } from "./logger.js";
7
- import { startPolling } from "./poller.js";
1
+ import assert from 'node:assert/strict';
2
+ import * as fsModule from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import test, { mock } from 'node:test';
6
+ import { logger } from './logger.js';
7
+ import { startPolling } from './poller.js';
8
8
  const config = {
9
- daemonToken: "daemon-token",
10
- apiUrl: "https://api.example",
9
+ daemonToken: 'daemon-token',
10
+ apiUrl: 'https://api.example',
11
11
  pollingIntervalMs: 5000,
12
12
  timeoutMs: 1000,
13
13
  idleTimeoutMs: 500,
14
- runnerCmd: "opencode",
15
- preventSleepWhileBusy: false
14
+ runnerCmd: 'opencode',
15
+ preventSleepWhileBusy: false,
16
16
  };
17
17
  const trigger = {
18
- id: "trigger-1",
19
- prompt: "hello",
20
- runnerType: "CODEX",
21
- model: "o4-mini",
18
+ id: 'trigger-1',
19
+ prompt: 'hello',
20
+ runnerType: 'CODEX',
21
+ model: 'o4-mini',
22
22
  fastMode: false,
23
- status: "PENDING",
24
- agentConfigId: "agent-1",
23
+ status: 'PENDING',
24
+ agentConfigId: 'agent-1',
25
25
  startedAt: null,
26
26
  errorMessage: null,
27
27
  worktreeError: null,
28
28
  lastHeartbeatAt: null,
29
29
  conversationId: null,
30
30
  parentTriggerId: null,
31
- createdByMemberId: "member-1",
31
+ createdByMemberId: 'member-1',
32
32
  planMode: false,
33
33
  targetDaemonId: null,
34
34
  claimedByDaemonId: null,
@@ -36,8 +36,8 @@ const trigger = {
36
36
  baseBranch: null,
37
37
  worktreeId: null,
38
38
  worktreeStatus: null,
39
- createdAt: "2026-01-01T00:00:00.000Z",
40
- updatedAt: "2026-01-01T00:00:00.000Z"
39
+ createdAt: '2026-01-01T00:00:00.000Z',
40
+ updatedAt: '2026-01-01T00:00:00.000Z',
41
41
  };
42
42
  test.afterEach(() => {
43
43
  mock.restoreAll();
@@ -48,15 +48,15 @@ const createRecordingBlocker = () => {
48
48
  events,
49
49
  blocker: {
50
50
  acquire: (label) => {
51
- events.push(`acquire:${label ?? ""}`);
52
- return () => events.push(`release:${label ?? ""}`);
53
- }
54
- }
51
+ events.push(`acquire:${label ?? ''}`);
52
+ return () => events.push(`release:${label ?? ''}`);
53
+ },
54
+ },
55
55
  };
56
56
  };
57
- test("startPolling handles a claimed trigger, registers auth paths, and runs scheduled cleanup", async () => {
57
+ test('startPolling handles a claimed trigger, registers auth paths, and runs scheduled cleanup', async () => {
58
58
  const infos = [];
59
- mock.method(logger, "info", (message, meta) => {
59
+ mock.method(logger, 'info', (message, meta) => {
60
60
  infos.push({ message, meta });
61
61
  });
62
62
  const signals = new Map();
@@ -74,10 +74,10 @@ test("startPolling handles a claimed trigger, registers auth paths, and runs sch
74
74
  fetchPendingWorktreeRemovals: async () => [],
75
75
  reportWorktreeStatus: async () => undefined,
76
76
  notifyUpdate: async () => undefined,
77
- ackRestartRequest: async () => undefined
77
+ ackRestartRequest: async () => undefined,
78
78
  };
79
79
  const createHandler = (onAuthPathDiscovered) => {
80
- onAuthPathDiscovered("/auth/path");
80
+ onAuthPathDiscovered('/auth/path');
81
81
  return async (value) => {
82
82
  handledTriggers.push(value.id);
83
83
  };
@@ -97,39 +97,40 @@ test("startPolling handles a claimed trigger, registers auth paths, and runs sch
97
97
  signals.set(event, listener);
98
98
  }),
99
99
  processExit: (() => {
100
- throw new Error("should not exit");
100
+ throw new Error('should not exit');
101
101
  }),
102
102
  now: () => nowValue,
103
103
  loadAuthPaths: () => [],
104
104
  saveAuthPath: (authPath) => {
105
105
  savedAuthPaths.push(authPath);
106
- return "/tmp/auth-paths.json";
106
+ return '/tmp/auth-paths.json';
107
107
  },
108
108
  keepAlive: () => new Promise((resolve) => {
109
109
  keepAliveResolve = resolve;
110
- })
110
+ }),
111
111
  });
112
112
  await new Promise((resolve) => setImmediate(resolve));
113
- assert.deepEqual(handledTriggers, ["trigger-1"]);
113
+ assert.deepEqual(handledTriggers, ['trigger-1']);
114
114
  assert.deepEqual(cleanupCalls, []);
115
115
  assert.equal(intervalCallbacks.length, 1);
116
116
  nowValue = 24 * 60 * 60 * 1000 + 1;
117
117
  await intervalCallbacks[0]?.();
118
118
  await new Promise((resolve) => setImmediate(resolve));
119
- assert.deepEqual(cleanupCalls, ["/auth/path"]);
120
- assert.deepEqual(savedAuthPaths, ["/auth/path"]);
121
- assert.equal(signals.has("SIGINT"), true);
122
- assert.equal(signals.has("SIGTERM"), true);
123
- assert.equal(infos.some((entry) => entry.message === "Daemon polling started"), true);
124
- const resolveKeepAlive = keepAliveResolve ?? (() => {
125
- throw new Error("keepAlive resolver was not registered");
126
- });
119
+ assert.deepEqual(cleanupCalls, ['/auth/path']);
120
+ assert.deepEqual(savedAuthPaths, ['/auth/path']);
121
+ assert.equal(signals.has('SIGINT'), true);
122
+ assert.equal(signals.has('SIGTERM'), true);
123
+ assert.equal(infos.some((entry) => entry.message === 'Daemon polling started'), true);
124
+ const resolveKeepAlive = keepAliveResolve ??
125
+ (() => {
126
+ throw new Error('keepAlive resolver was not registered');
127
+ });
127
128
  resolveKeepAlive();
128
129
  await pollingPromise;
129
130
  });
130
- test("startPolling logs conflicts and suppresses overlapping polling cycles", async () => {
131
+ test('startPolling logs conflicts and suppresses overlapping polling cycles', async () => {
131
132
  const infos = [];
132
- mock.method(logger, "info", (message, meta) => {
133
+ mock.method(logger, 'info', (message, meta) => {
133
134
  infos.push({ message, meta });
134
135
  });
135
136
  let releaseFetch = null;
@@ -143,12 +144,12 @@ test("startPolling logs conflicts and suppresses overlapping polling cycles", as
143
144
  fetchPendingWorktreeRemovals: async () => [],
144
145
  reportWorktreeStatus: async () => undefined,
145
146
  notifyUpdate: async () => undefined,
146
- ackRestartRequest: async () => undefined
147
+ ackRestartRequest: async () => undefined,
147
148
  };
148
149
  const intervalCallbacks = [];
149
150
  let keepAliveResolve = null;
150
151
  const pollingPromise = startPolling(config, () => async () => {
151
- throw new Error("handler should not run");
152
+ throw new Error('handler should not run');
152
153
  }, {
153
154
  createClient: () => client,
154
155
  runCleanup: async () => undefined,
@@ -159,30 +160,32 @@ test("startPolling logs conflicts and suppresses overlapping polling cycles", as
159
160
  clearInterval: (() => undefined),
160
161
  processOn: (() => undefined),
161
162
  processExit: (() => {
162
- throw new Error("should not exit");
163
+ throw new Error('should not exit');
163
164
  }),
164
165
  now: () => 0,
165
166
  loadAuthPaths: () => [],
166
- saveAuthPath: () => "/tmp/auth-paths.json",
167
+ saveAuthPath: () => '/tmp/auth-paths.json',
167
168
  keepAlive: () => new Promise((resolve) => {
168
169
  keepAliveResolve = resolve;
169
- })
170
+ }),
170
171
  });
171
172
  await new Promise((resolve) => setImmediate(resolve));
172
173
  await intervalCallbacks[0]?.();
173
- const releasePendingFetch = releaseFetch ?? (() => {
174
- throw new Error("fetch release was not registered");
175
- });
174
+ const releasePendingFetch = releaseFetch ??
175
+ (() => {
176
+ throw new Error('fetch release was not registered');
177
+ });
176
178
  releasePendingFetch();
177
179
  await new Promise((resolve) => setImmediate(resolve));
178
- assert.equal(infos.some((entry) => entry.message === "Trigger already claimed by another daemon"), true);
179
- const resolveKeepAlive = keepAliveResolve ?? (() => {
180
- throw new Error("keepAlive resolver was not registered");
181
- });
180
+ assert.equal(infos.some((entry) => entry.message === 'Trigger already claimed by another daemon'), true);
181
+ const resolveKeepAlive = keepAliveResolve ??
182
+ (() => {
183
+ throw new Error('keepAlive resolver was not registered');
184
+ });
182
185
  resolveKeepAlive();
183
186
  await pollingPromise;
184
187
  });
185
- test("startPolling clears the interval and exits on shutdown signals", async () => {
188
+ test('startPolling clears the interval and exits on shutdown signals', async () => {
186
189
  const signals = new Map();
187
190
  const cleared = [];
188
191
  let keepAliveResolve = null;
@@ -197,7 +200,7 @@ test("startPolling clears the interval and exits on shutdown signals", async ()
197
200
  fetchPendingWorktreeRemovals: async () => [],
198
201
  reportWorktreeStatus: async () => undefined,
199
202
  notifyUpdate: async () => undefined,
200
- ackRestartRequest: async () => undefined
203
+ ackRestartRequest: async () => undefined,
201
204
  }),
202
205
  runCleanup: async () => undefined,
203
206
  setInterval: (() => intervalHandle),
@@ -213,36 +216,37 @@ test("startPolling clears the interval and exits on shutdown signals", async ()
213
216
  }),
214
217
  now: () => 0,
215
218
  loadAuthPaths: () => [],
216
- saveAuthPath: () => "/tmp/auth-paths.json",
219
+ saveAuthPath: () => '/tmp/auth-paths.json',
217
220
  keepAlive: () => new Promise((resolve) => {
218
221
  keepAliveResolve = resolve;
219
- })
222
+ }),
220
223
  }).catch((error) => {
221
- if (error.message !== "exit") {
224
+ if (error.message !== 'exit') {
222
225
  throw error;
223
226
  }
224
227
  });
225
228
  await new Promise((resolve) => setImmediate(resolve));
226
- assert.ok(signals.get("SIGTERM"));
227
- const sigterm = signals.get("SIGTERM");
229
+ assert.ok(signals.get('SIGTERM'));
230
+ const sigterm = signals.get('SIGTERM');
228
231
  assert.ok(sigterm);
229
232
  sigterm();
230
233
  assert.equal(exitCode, 0);
231
234
  assert.deepEqual(cleared, [intervalHandle]);
232
- const resolveKeepAlive = keepAliveResolve ?? (() => {
233
- throw new Error("keepAlive resolver was not registered");
234
- });
235
+ const resolveKeepAlive = keepAliveResolve ??
236
+ (() => {
237
+ throw new Error('keepAlive resolver was not registered');
238
+ });
235
239
  resolveKeepAlive();
236
240
  await pollingPromise;
237
241
  });
238
- test("startPolling delegates auto-update via injected dependency", async () => {
242
+ test('startPolling delegates auto-update via injected dependency', async () => {
239
243
  let autoUpdateCallCount = 0;
240
244
  let keepAliveResolve = null;
241
245
  const pollingPromise = startPolling(config, () => async () => undefined, {
242
246
  createClient: () => ({
243
247
  fetchPendingTrigger: async () => ({
244
248
  data: null,
245
- meta: { cliLatestVersion: null, runnerLatestVersion: "99.0.0" }
249
+ meta: { cliLatestVersion: null, runnerLatestVersion: '99.0.0' },
246
250
  }),
247
251
  claimTrigger: async () => ({ ok: true, conflict: false }),
248
252
  fetchOrphanedCancelRequested: async () => [],
@@ -250,7 +254,7 @@ test("startPolling delegates auto-update via injected dependency", async () => {
250
254
  fetchPendingWorktreeRemovals: async () => [],
251
255
  reportWorktreeStatus: async () => undefined,
252
256
  notifyUpdate: async () => undefined,
253
- ackRestartRequest: async () => undefined
257
+ ackRestartRequest: async () => undefined,
254
258
  }),
255
259
  runCleanup: async () => undefined,
256
260
  maybeAutoUpdate: async () => {
@@ -263,33 +267,34 @@ test("startPolling delegates auto-update via injected dependency", async () => {
263
267
  processExit: (() => undefined),
264
268
  now: () => 0,
265
269
  loadAuthPaths: () => [],
266
- saveAuthPath: () => "/tmp/auth-paths.json",
270
+ saveAuthPath: () => '/tmp/auth-paths.json',
267
271
  keepAlive: () => new Promise((resolve) => {
268
272
  keepAliveResolve = resolve;
269
- })
273
+ }),
270
274
  });
271
275
  await new Promise((resolve) => setImmediate(resolve));
272
- assert.equal(autoUpdateCallCount, 1, "should call injected maybeAutoUpdate on idle poll");
273
- const resolveKeepAlive = keepAliveResolve ?? (() => {
274
- throw new Error("keepAlive resolver was not registered");
275
- });
276
+ assert.equal(autoUpdateCallCount, 1, 'should call injected maybeAutoUpdate on idle poll');
277
+ const resolveKeepAlive = keepAliveResolve ??
278
+ (() => {
279
+ throw new Error('keepAlive resolver was not registered');
280
+ });
276
281
  resolveKeepAlive();
277
282
  await pollingPromise;
278
283
  });
279
- test("startPolling restores persisted auth paths for worktree removals after restart", async () => {
284
+ test('startPolling restores persisted auth paths for worktree removals after restart', async () => {
280
285
  const removedWorktrees = [];
281
286
  const reportedStatuses = [];
282
- const tempRoot = fsModule.mkdtempSync(path.join(os.tmpdir(), "agentrunner-poller-"));
283
- const authPath = path.join(tempRoot, "path");
284
- const worktreePath = path.join(tempRoot, ".path-worktrees", "wt-worktree-1");
287
+ const tempRoot = fsModule.mkdtempSync(path.join(os.tmpdir(), 'agentrunner-poller-'));
288
+ const authPath = path.join(tempRoot, 'path');
289
+ const worktreePath = path.join(tempRoot, '.path-worktrees', 'wt-worktree-1');
285
290
  fsModule.mkdirSync(authPath, { recursive: true });
286
291
  fsModule.mkdirSync(worktreePath, { recursive: true });
287
292
  const worktreeRemovalTrigger = {
288
293
  ...trigger,
289
- id: "trigger-remove",
294
+ id: 'trigger-remove',
290
295
  useWorktree: true,
291
- worktreeId: "worktree-1",
292
- worktreeStatus: "REMOVE_REQUESTED"
296
+ worktreeId: 'worktree-1',
297
+ worktreeStatus: 'REMOVE_REQUESTED',
293
298
  };
294
299
  let keepAliveResolve = null;
295
300
  const pollingPromise = startPolling(config, () => async () => undefined, {
@@ -303,7 +308,7 @@ test("startPolling restores persisted auth paths for worktree removals after res
303
308
  reportedStatuses.push({ triggerId, status, worktreeError });
304
309
  },
305
310
  notifyUpdate: async () => undefined,
306
- ackRestartRequest: async () => undefined
311
+ ackRestartRequest: async () => undefined,
307
312
  }),
308
313
  runCleanup: async () => undefined,
309
314
  removeWorktree: (authPath, worktreePath, worktreeId) => {
@@ -313,37 +318,40 @@ test("startPolling restores persisted auth paths for worktree removals after res
313
318
  clearInterval: (() => undefined),
314
319
  processOn: (() => undefined),
315
320
  processExit: (() => {
316
- throw new Error("should not exit");
321
+ throw new Error('should not exit');
317
322
  }),
318
323
  now: () => 0,
319
324
  loadAuthPaths: () => [authPath],
320
- saveAuthPath: () => "/tmp/auth-paths.json",
325
+ saveAuthPath: () => '/tmp/auth-paths.json',
321
326
  keepAlive: () => new Promise((resolve) => {
322
327
  keepAliveResolve = resolve;
323
- })
328
+ }),
324
329
  });
325
330
  await new Promise((resolve) => setImmediate(resolve));
326
- assert.deepEqual(removedWorktrees, [{
331
+ assert.deepEqual(removedWorktrees, [
332
+ {
327
333
  authPath,
328
334
  worktreePath,
329
- worktreeId: "worktree-1"
330
- }]);
331
- assert.deepEqual(reportedStatuses, [{ triggerId: "trigger-remove", status: "REMOVED", worktreeError: undefined }]);
332
- const resolveKeepAlive = keepAliveResolve ?? (() => {
333
- throw new Error("keepAlive resolver was not registered");
334
- });
335
+ worktreeId: 'worktree-1',
336
+ },
337
+ ]);
338
+ assert.deepEqual(reportedStatuses, [{ triggerId: 'trigger-remove', status: 'REMOVED', worktreeError: undefined }]);
339
+ const resolveKeepAlive = keepAliveResolve ??
340
+ (() => {
341
+ throw new Error('keepAlive resolver was not registered');
342
+ });
335
343
  resolveKeepAlive();
336
344
  await pollingPromise;
337
345
  fsModule.rmSync(tempRoot, { recursive: true, force: true });
338
346
  });
339
- test("startPolling reports FAILED when no persisted auth path matches the worktree", async () => {
347
+ test('startPolling reports FAILED when no persisted auth path matches the worktree', async () => {
340
348
  const reportedStatuses = [];
341
349
  const worktreeRemovalTrigger = {
342
350
  ...trigger,
343
- id: "trigger-remove-missing",
351
+ id: 'trigger-remove-missing',
344
352
  useWorktree: true,
345
- worktreeId: "worktree-missing",
346
- worktreeStatus: "REMOVE_REQUESTED"
353
+ worktreeId: 'worktree-missing',
354
+ worktreeStatus: 'REMOVE_REQUESTED',
347
355
  };
348
356
  let keepAliveResolve = null;
349
357
  const pollingPromise = startPolling(config, () => async () => undefined, {
@@ -357,50 +365,53 @@ test("startPolling reports FAILED when no persisted auth path matches the worktr
357
365
  reportedStatuses.push({ triggerId, status, worktreeError });
358
366
  },
359
367
  notifyUpdate: async () => undefined,
360
- ackRestartRequest: async () => undefined
368
+ ackRestartRequest: async () => undefined,
361
369
  }),
362
370
  runCleanup: async () => undefined,
363
371
  removeWorktree: () => {
364
- throw new Error("removeWorktree should not be called");
372
+ throw new Error('removeWorktree should not be called');
365
373
  },
366
374
  setInterval: (() => ({ ref() { }, unref() { } })),
367
375
  clearInterval: (() => undefined),
368
376
  processOn: (() => undefined),
369
377
  processExit: (() => {
370
- throw new Error("should not exit");
378
+ throw new Error('should not exit');
371
379
  }),
372
380
  now: () => 0,
373
- loadAuthPaths: () => ["/persisted/auth/path"],
374
- saveAuthPath: () => "/tmp/auth-paths.json",
381
+ loadAuthPaths: () => ['/persisted/auth/path'],
382
+ saveAuthPath: () => '/tmp/auth-paths.json',
375
383
  keepAlive: () => new Promise((resolve) => {
376
384
  keepAliveResolve = resolve;
377
- })
385
+ }),
378
386
  });
379
387
  await new Promise((resolve) => setImmediate(resolve));
380
- assert.deepEqual(reportedStatuses, [{
381
- triggerId: "trigger-remove-missing",
382
- status: "FAILED",
383
- worktreeError: "Failed to remove RunnerBox: worktree path was not found for worktree-missing"
384
- }]);
385
- const resolveKeepAlive = keepAliveResolve ?? (() => {
386
- throw new Error("keepAlive resolver was not registered");
387
- });
388
+ assert.deepEqual(reportedStatuses, [
389
+ {
390
+ triggerId: 'trigger-remove-missing',
391
+ status: 'FAILED',
392
+ worktreeError: 'Failed to remove RunnerBox: worktree path was not found for worktree-missing',
393
+ },
394
+ ]);
395
+ const resolveKeepAlive = keepAliveResolve ??
396
+ (() => {
397
+ throw new Error('keepAlive resolver was not registered');
398
+ });
388
399
  resolveKeepAlive();
389
400
  await pollingPromise;
390
401
  });
391
- test("startPolling reports FAILED when worktree removal throws after matching a path", async () => {
402
+ test('startPolling reports FAILED when worktree removal throws after matching a path', async () => {
392
403
  const reportedStatuses = [];
393
- const tempRoot = fsModule.mkdtempSync(path.join(os.tmpdir(), "agentrunner-poller-"));
394
- const authPath = path.join(tempRoot, "path");
395
- const worktreePath = path.join(tempRoot, ".path-worktrees", "wt-worktree-failed");
404
+ const tempRoot = fsModule.mkdtempSync(path.join(os.tmpdir(), 'agentrunner-poller-'));
405
+ const authPath = path.join(tempRoot, 'path');
406
+ const worktreePath = path.join(tempRoot, '.path-worktrees', 'wt-worktree-failed');
396
407
  fsModule.mkdirSync(authPath, { recursive: true });
397
408
  fsModule.mkdirSync(worktreePath, { recursive: true });
398
409
  const worktreeRemovalTrigger = {
399
410
  ...trigger,
400
- id: "trigger-remove-failed",
411
+ id: 'trigger-remove-failed',
401
412
  useWorktree: true,
402
- worktreeId: "worktree-failed",
403
- worktreeStatus: "REMOVE_REQUESTED"
413
+ worktreeId: 'worktree-failed',
414
+ worktreeStatus: 'REMOVE_REQUESTED',
404
415
  };
405
416
  let keepAliveResolve = null;
406
417
  const pollingPromise = startPolling(config, () => async () => undefined, {
@@ -414,45 +425,51 @@ test("startPolling reports FAILED when worktree removal throws after matching a
414
425
  reportedStatuses.push({ triggerId, status, worktreeError });
415
426
  },
416
427
  notifyUpdate: async () => undefined,
417
- ackRestartRequest: async () => undefined
428
+ ackRestartRequest: async () => undefined,
418
429
  }),
419
430
  runCleanup: async () => undefined,
420
431
  removeWorktree: () => {
421
- throw new Error("permission denied");
432
+ throw new Error('permission denied');
422
433
  },
423
434
  setInterval: (() => ({ ref() { }, unref() { } })),
424
435
  clearInterval: (() => undefined),
425
436
  processOn: (() => undefined),
426
437
  processExit: (() => {
427
- throw new Error("should not exit");
438
+ throw new Error('should not exit');
428
439
  }),
429
440
  now: () => 0,
430
441
  loadAuthPaths: () => [authPath],
431
- saveAuthPath: () => "/tmp/auth-paths.json",
442
+ saveAuthPath: () => '/tmp/auth-paths.json',
432
443
  keepAlive: () => new Promise((resolve) => {
433
444
  keepAliveResolve = resolve;
434
- })
445
+ }),
435
446
  });
436
447
  await new Promise((resolve) => setImmediate(resolve));
437
- assert.deepEqual(reportedStatuses, [{
438
- triggerId: "trigger-remove-failed",
439
- status: "FAILED",
440
- worktreeError: "permission denied"
441
- }]);
442
- const resolveKeepAlive = keepAliveResolve ?? (() => {
443
- throw new Error("keepAlive resolver was not registered");
444
- });
448
+ assert.deepEqual(reportedStatuses, [
449
+ {
450
+ triggerId: 'trigger-remove-failed',
451
+ status: 'FAILED',
452
+ worktreeError: 'permission denied',
453
+ },
454
+ ]);
455
+ const resolveKeepAlive = keepAliveResolve ??
456
+ (() => {
457
+ throw new Error('keepAlive resolver was not registered');
458
+ });
445
459
  resolveKeepAlive();
446
460
  await pollingPromise;
447
461
  fsModule.rmSync(tempRoot, { recursive: true, force: true });
448
462
  });
449
- test("startPolling acks and runs restart when meta.restartRequested is true with no pending trigger", async () => {
463
+ test('startPolling acks and runs restart when meta.restartRequested is true with no pending trigger', async () => {
450
464
  let ackCalled = 0;
451
465
  let restartCalled = 0;
452
466
  let keepAliveResolve = null;
453
467
  const pollingPromise = startPolling(config, () => async () => undefined, {
454
468
  createClient: () => ({
455
- fetchPendingTrigger: async () => ({ data: null, meta: { cliLatestVersion: null, runnerLatestVersion: null, restartRequested: true } }),
469
+ fetchPendingTrigger: async () => ({
470
+ data: null,
471
+ meta: { cliLatestVersion: null, runnerLatestVersion: null, restartRequested: true },
472
+ }),
456
473
  claimTrigger: async () => ({ ok: true, conflict: false }),
457
474
  fetchOrphanedCancelRequested: async () => [],
458
475
  updateTriggerStatus: async () => undefined,
@@ -461,11 +478,11 @@ test("startPolling acks and runs restart when meta.restartRequested is true with
461
478
  notifyUpdate: async () => undefined,
462
479
  ackRestartRequest: async () => {
463
480
  ackCalled += 1;
464
- }
481
+ },
465
482
  }),
466
483
  runCleanup: async () => undefined,
467
484
  maybeAutoUpdate: async () => {
468
- throw new Error("auto-update must be skipped while restarting");
485
+ throw new Error('auto-update must be skipped while restarting');
469
486
  },
470
487
  executeRestartRequest: () => {
471
488
  restartCalled += 1;
@@ -476,30 +493,34 @@ test("startPolling acks and runs restart when meta.restartRequested is true with
476
493
  processExit: (() => undefined),
477
494
  now: () => 0,
478
495
  loadAuthPaths: () => [],
479
- saveAuthPath: () => "/tmp/auth-paths.json",
496
+ saveAuthPath: () => '/tmp/auth-paths.json',
480
497
  keepAlive: () => new Promise((resolve) => {
481
498
  keepAliveResolve = resolve;
482
- })
499
+ }),
483
500
  });
484
501
  await new Promise((resolve) => setImmediate(resolve));
485
- assert.equal(ackCalled, 1, "ackRestartRequest should be called exactly once");
486
- assert.equal(restartCalled, 1, "executeRestartRequest should be called exactly once");
487
- const resolveKeepAlive = keepAliveResolve ?? (() => {
488
- throw new Error("keepAlive resolver was not registered");
489
- });
502
+ assert.equal(ackCalled, 1, 'ackRestartRequest should be called exactly once');
503
+ assert.equal(restartCalled, 1, 'executeRestartRequest should be called exactly once');
504
+ const resolveKeepAlive = keepAliveResolve ??
505
+ (() => {
506
+ throw new Error('keepAlive resolver was not registered');
507
+ });
490
508
  resolveKeepAlive();
491
509
  await pollingPromise;
492
510
  });
493
- test("startPolling restarts even when a pending trigger is present, ignoring the trigger", async () => {
511
+ test('startPolling restarts even when a pending trigger is present, ignoring the trigger', async () => {
494
512
  let ackCalled = 0;
495
513
  let restartCalled = 0;
496
514
  let claimCalled = 0;
497
515
  let keepAliveResolve = null;
498
516
  const pollingPromise = startPolling(config, () => async () => {
499
- throw new Error("trigger handler must not run when restart wins");
517
+ throw new Error('trigger handler must not run when restart wins');
500
518
  }, {
501
519
  createClient: () => ({
502
- fetchPendingTrigger: async () => ({ data: trigger, meta: { cliLatestVersion: null, runnerLatestVersion: null, restartRequested: true } }),
520
+ fetchPendingTrigger: async () => ({
521
+ data: trigger,
522
+ meta: { cliLatestVersion: null, runnerLatestVersion: null, restartRequested: true },
523
+ }),
503
524
  claimTrigger: async () => {
504
525
  claimCalled += 1;
505
526
  return { ok: true, conflict: false };
@@ -511,7 +532,7 @@ test("startPolling restarts even when a pending trigger is present, ignoring the
511
532
  notifyUpdate: async () => undefined,
512
533
  ackRestartRequest: async () => {
513
534
  ackCalled += 1;
514
- }
535
+ },
515
536
  }),
516
537
  runCleanup: async () => undefined,
517
538
  executeRestartRequest: () => {
@@ -523,27 +544,31 @@ test("startPolling restarts even when a pending trigger is present, ignoring the
523
544
  processExit: (() => undefined),
524
545
  now: () => 0,
525
546
  loadAuthPaths: () => [],
526
- saveAuthPath: () => "/tmp/auth-paths.json",
547
+ saveAuthPath: () => '/tmp/auth-paths.json',
527
548
  keepAlive: () => new Promise((resolve) => {
528
549
  keepAliveResolve = resolve;
529
- })
550
+ }),
530
551
  });
531
552
  await new Promise((resolve) => setImmediate(resolve));
532
- assert.equal(ackCalled, 1, "ack should run before restart");
533
- assert.equal(restartCalled, 1, "restart should run even with a pending trigger");
534
- assert.equal(claimCalled, 0, "trigger must not be claimed while restarting");
535
- const resolveKeepAlive = keepAliveResolve ?? (() => {
536
- throw new Error("keepAlive resolver was not registered");
537
- });
553
+ assert.equal(ackCalled, 1, 'ack should run before restart');
554
+ assert.equal(restartCalled, 1, 'restart should run even with a pending trigger');
555
+ assert.equal(claimCalled, 0, 'trigger must not be claimed while restarting');
556
+ const resolveKeepAlive = keepAliveResolve ??
557
+ (() => {
558
+ throw new Error('keepAlive resolver was not registered');
559
+ });
538
560
  resolveKeepAlive();
539
561
  await pollingPromise;
540
562
  });
541
- test("startPolling skips restart and leaves the flag for retry when ack fails", async () => {
563
+ test('startPolling skips restart and leaves the flag for retry when ack fails', async () => {
542
564
  let restartCalled = 0;
543
565
  let keepAliveResolve = null;
544
566
  const pollingPromise = startPolling(config, () => async () => undefined, {
545
567
  createClient: () => ({
546
- fetchPendingTrigger: async () => ({ data: null, meta: { cliLatestVersion: null, runnerLatestVersion: null, restartRequested: true } }),
568
+ fetchPendingTrigger: async () => ({
569
+ data: null,
570
+ meta: { cliLatestVersion: null, runnerLatestVersion: null, restartRequested: true },
571
+ }),
547
572
  claimTrigger: async () => ({ ok: true, conflict: false }),
548
573
  fetchOrphanedCancelRequested: async () => [],
549
574
  updateTriggerStatus: async () => undefined,
@@ -551,8 +576,8 @@ test("startPolling skips restart and leaves the flag for retry when ack fails",
551
576
  reportWorktreeStatus: async () => undefined,
552
577
  notifyUpdate: async () => undefined,
553
578
  ackRestartRequest: async () => {
554
- throw new Error("network down");
555
- }
579
+ throw new Error('network down');
580
+ },
556
581
  }),
557
582
  runCleanup: async () => undefined,
558
583
  executeRestartRequest: () => {
@@ -564,20 +589,21 @@ test("startPolling skips restart and leaves the flag for retry when ack fails",
564
589
  processExit: (() => undefined),
565
590
  now: () => 0,
566
591
  loadAuthPaths: () => [],
567
- saveAuthPath: () => "/tmp/auth-paths.json",
592
+ saveAuthPath: () => '/tmp/auth-paths.json',
568
593
  keepAlive: () => new Promise((resolve) => {
569
594
  keepAliveResolve = resolve;
570
- })
595
+ }),
571
596
  });
572
597
  await new Promise((resolve) => setImmediate(resolve));
573
- assert.equal(restartCalled, 0, "restart must not run when ack fails");
574
- const resolveKeepAlive = keepAliveResolve ?? (() => {
575
- throw new Error("keepAlive resolver was not registered");
576
- });
598
+ assert.equal(restartCalled, 0, 'restart must not run when ack fails');
599
+ const resolveKeepAlive = keepAliveResolve ??
600
+ (() => {
601
+ throw new Error('keepAlive resolver was not registered');
602
+ });
577
603
  resolveKeepAlive();
578
604
  await pollingPromise;
579
605
  });
580
- test("startPolling acquires the power save blocker on start and releases it on shutdown", async () => {
606
+ test('startPolling acquires the power save blocker on start and releases it on shutdown', async () => {
581
607
  const { events, blocker } = createRecordingBlocker();
582
608
  const signals = new Map();
583
609
  let keepAliveResolve = null;
@@ -590,7 +616,7 @@ test("startPolling acquires the power save blocker on start and releases it on s
590
616
  fetchPendingWorktreeRemovals: async () => [],
591
617
  reportWorktreeStatus: async () => undefined,
592
618
  notifyUpdate: async () => undefined,
593
- ackRestartRequest: async () => undefined
619
+ ackRestartRequest: async () => undefined,
594
620
  }),
595
621
  runCleanup: async () => undefined,
596
622
  setInterval: (() => ({})),
@@ -601,25 +627,26 @@ test("startPolling acquires the power save blocker on start and releases it on s
601
627
  processExit: (() => undefined),
602
628
  now: () => 0,
603
629
  loadAuthPaths: () => [],
604
- saveAuthPath: () => "/tmp/auth-paths.json",
630
+ saveAuthPath: () => '/tmp/auth-paths.json',
605
631
  powerSaveBlocker: blocker,
606
632
  keepAlive: () => new Promise((resolve) => {
607
633
  keepAliveResolve = resolve;
608
- })
634
+ }),
609
635
  });
610
636
  await new Promise((resolve) => setImmediate(resolve));
611
- assert.deepEqual(events, ["acquire:daemon-polling"], "blocker should be acquired exactly once on start");
612
- const sigterm = signals.get("SIGTERM");
637
+ assert.deepEqual(events, ['acquire:daemon-polling'], 'blocker should be acquired exactly once on start');
638
+ const sigterm = signals.get('SIGTERM');
613
639
  assert.ok(sigterm);
614
640
  sigterm();
615
- assert.deepEqual(events, ["acquire:daemon-polling", "release:daemon-polling"], "blocker should be released on shutdown");
616
- const resolveKeepAlive = keepAliveResolve ?? (() => {
617
- throw new Error("keepAlive resolver was not registered");
618
- });
641
+ assert.deepEqual(events, ['acquire:daemon-polling', 'release:daemon-polling'], 'blocker should be released on shutdown');
642
+ const resolveKeepAlive = keepAliveResolve ??
643
+ (() => {
644
+ throw new Error('keepAlive resolver was not registered');
645
+ });
619
646
  resolveKeepAlive();
620
647
  await pollingPromise;
621
648
  });
622
- test("startPolling releases the power save blocker when keepAlive resolves", async () => {
649
+ test('startPolling releases the power save blocker when keepAlive resolves', async () => {
623
650
  const { events, blocker } = createRecordingBlocker();
624
651
  let keepAliveResolve = null;
625
652
  const pollingPromise = startPolling(config, () => async () => undefined, {
@@ -631,7 +658,7 @@ test("startPolling releases the power save blocker when keepAlive resolves", asy
631
658
  fetchPendingWorktreeRemovals: async () => [],
632
659
  reportWorktreeStatus: async () => undefined,
633
660
  notifyUpdate: async () => undefined,
634
- ackRestartRequest: async () => undefined
661
+ ackRestartRequest: async () => undefined,
635
662
  }),
636
663
  runCleanup: async () => undefined,
637
664
  setInterval: (() => ({})),
@@ -640,19 +667,20 @@ test("startPolling releases the power save blocker when keepAlive resolves", asy
640
667
  processExit: (() => undefined),
641
668
  now: () => 0,
642
669
  loadAuthPaths: () => [],
643
- saveAuthPath: () => "/tmp/auth-paths.json",
670
+ saveAuthPath: () => '/tmp/auth-paths.json',
644
671
  powerSaveBlocker: blocker,
645
672
  keepAlive: () => new Promise((resolve) => {
646
673
  keepAliveResolve = resolve;
647
- })
674
+ }),
648
675
  });
649
676
  await new Promise((resolve) => setImmediate(resolve));
650
- assert.deepEqual(events, ["acquire:daemon-polling"]);
651
- const resolveKeepAlive = keepAliveResolve ?? (() => {
652
- throw new Error("keepAlive resolver was not registered");
653
- });
677
+ assert.deepEqual(events, ['acquire:daemon-polling']);
678
+ const resolveKeepAlive = keepAliveResolve ??
679
+ (() => {
680
+ throw new Error('keepAlive resolver was not registered');
681
+ });
654
682
  resolveKeepAlive();
655
683
  await pollingPromise;
656
- assert.deepEqual(events, ["acquire:daemon-polling", "release:daemon-polling"], "blocker should be released after keepAlive resolves");
684
+ assert.deepEqual(events, ['acquire:daemon-polling', 'release:daemon-polling'], 'blocker should be released after keepAlive resolves');
657
685
  });
658
686
  //# sourceMappingURL=poller.test.js.map