@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,25 +1,25 @@
1
- import assert from "node:assert/strict";
2
- import test, { mock } from "node:test";
3
- import { mkdtemp, rm, stat } from "node:fs/promises";
4
- import { tmpdir } from "node:os";
5
- import { join } from "node:path";
6
- import { logger } from "../logger.js";
7
- import { createTriggerHandler } from "./trigger-handler.js";
1
+ import assert from 'node:assert/strict';
2
+ import test, { mock } from 'node:test';
3
+ import { mkdtemp, rm, stat } from 'node:fs/promises';
4
+ import { tmpdir } from 'node:os';
5
+ import { join } from 'node:path';
6
+ import { logger } from '../logger.js';
7
+ import { createTriggerHandler } from './trigger-handler.js';
8
8
  const trigger = {
9
- id: "trigger-1",
10
- prompt: "Implement feature",
11
- runnerType: "CODEX",
12
- model: "o4-mini",
9
+ id: 'trigger-1',
10
+ prompt: 'Implement feature',
11
+ runnerType: 'CODEX',
12
+ model: 'o4-mini',
13
13
  fastMode: false,
14
- status: "PENDING",
15
- agentConfigId: "agent-1",
14
+ status: 'PENDING',
15
+ agentConfigId: 'agent-1',
16
16
  startedAt: null,
17
17
  errorMessage: null,
18
18
  worktreeError: null,
19
19
  lastHeartbeatAt: null,
20
20
  conversationId: null,
21
- parentTriggerId: "parent-1",
22
- createdByMemberId: "member-1",
21
+ parentTriggerId: 'parent-1',
22
+ createdByMemberId: 'member-1',
23
23
  planMode: false,
24
24
  targetDaemonId: null,
25
25
  claimedByDaemonId: null,
@@ -27,633 +27,631 @@ const trigger = {
27
27
  baseBranch: null,
28
28
  worktreeId: null,
29
29
  worktreeStatus: null,
30
- createdAt: "2026-01-01T00:00:00.000Z",
31
- updatedAt: "2026-01-01T00:00:00.000Z"
30
+ createdAt: '2026-01-01T00:00:00.000Z',
31
+ updatedAt: '2026-01-01T00:00:00.000Z',
32
32
  };
33
33
  const runtime = {
34
- triggerId: "trigger-1",
35
- agentConfigId: "agent-1",
36
- authPath: "/auth/path",
37
- apiKey: "api-key",
38
- teamId: "team-1",
39
- projectId: "project-1",
40
- runnerPrompt: "API runner prompt\n- History path: {{AGENTRUNNER_CURRENT_HISTORY_PATH}}\n- Previous history path: {{AGENTRUNNER_PARENT_HISTORY_PATH}}",
34
+ triggerId: 'trigger-1',
35
+ agentConfigId: 'agent-1',
36
+ authPath: '/auth/path',
37
+ apiKey: 'api-key',
38
+ teamId: 'team-1',
39
+ projectId: 'project-1',
40
+ runnerPrompt: 'API runner prompt\n- History path: {{AGENTRUNNER_CURRENT_HISTORY_PATH}}\n- Previous history path: {{AGENTRUNNER_PARENT_HISTORY_PATH}}',
41
41
  parentHistoryMarkdown: null,
42
42
  useWorktree: false,
43
43
  baseBranch: null,
44
- worktreeId: null
44
+ worktreeId: null,
45
45
  };
46
46
  test.afterEach(() => {
47
47
  mock.restoreAll();
48
48
  });
49
- test("createTriggerHandler runs the runner, reports history, and marks success", async () => {
49
+ test('createTriggerHandler runs the runner, reports history, and marks success', async () => {
50
50
  const clientCalls = [];
51
51
  const logEntries = [];
52
52
  const discoveredAuthPaths = [];
53
53
  const runnerInputs = [];
54
54
  const client = {
55
55
  fetchTriggerRuntime: async (...args) => {
56
- clientCalls.push({ method: "fetchTriggerRuntime", args });
56
+ clientCalls.push({ method: 'fetchTriggerRuntime', args });
57
57
  return runtime;
58
58
  },
59
59
  isTriggerCancelRequested: async (...args) => {
60
- clientCalls.push({ method: "isTriggerCancelRequested", args });
60
+ clientCalls.push({ method: 'isTriggerCancelRequested', args });
61
61
  return false;
62
62
  },
63
63
  updateTriggerHistory: async (...args) => {
64
- clientCalls.push({ method: "updateTriggerHistory", args });
64
+ clientCalls.push({ method: 'updateTriggerHistory', args });
65
65
  },
66
66
  updateTriggerStatus: async (...args) => {
67
- clientCalls.push({ method: "updateTriggerStatus", args });
68
- }
67
+ clientCalls.push({ method: 'updateTriggerStatus', args });
68
+ },
69
69
  };
70
70
  const runner = {
71
71
  run: async (input) => {
72
72
  runnerInputs.push({ prompt: input.prompt, authPath: input.authPath });
73
- input.onStdoutChunk?.("stdout");
74
- input.onStderrChunk?.("stderr");
73
+ input.onStdoutChunk?.('stdout');
74
+ input.onStderrChunk?.('stderr');
75
75
  return { exitCode: 0 };
76
- }
76
+ },
77
77
  };
78
78
  const handler = createTriggerHandler({
79
79
  config: {
80
- daemonToken: "daemon-token",
81
- apiUrl: "https://api.example",
80
+ daemonToken: 'daemon-token',
81
+ apiUrl: 'https://api.example',
82
82
  pollingIntervalMs: 5000,
83
83
  timeoutMs: 1500,
84
84
  idleTimeoutMs: 500,
85
- runnerCmd: "opencode",
86
- preventSleepWhileBusy: false
85
+ runnerCmd: 'opencode',
86
+ preventSleepWhileBusy: false,
87
87
  },
88
88
  client: client,
89
89
  onAuthPathDiscovered: (authPath) => {
90
90
  discoveredAuthPaths.push(authPath);
91
- }
91
+ },
92
92
  }, {
93
93
  createRunnerFactory: () => () => runner,
94
94
  createLogReporter: () => ({
95
95
  start: () => {
96
- logEntries.push({ level: "START", message: "started" });
96
+ logEntries.push({ level: 'START', message: 'started' });
97
97
  },
98
98
  append: (level, message) => {
99
99
  logEntries.push({ level, message });
100
100
  },
101
101
  stop: async () => {
102
- logEntries.push({ level: "STOP", message: "stopped" });
103
- }
102
+ logEntries.push({ level: 'STOP', message: 'stopped' });
103
+ },
104
104
  }),
105
- readHistoryFile: async () => "### Summary\n- done\n",
105
+ readHistoryFile: async () => '### Summary\n- done\n',
106
106
  resolveRunnerHistoryPaths: () => ({
107
- currentHistoryPath: "/auth/path/.agentteams/runner/history/trigger-1.md",
108
- parentHistoryPath: "/auth/path/.agentteams/runner/history/parent-1.md"
109
- })
107
+ currentHistoryPath: '/auth/path/.agentteams/runner/history/trigger-1.md',
108
+ parentHistoryPath: '/auth/path/.agentteams/runner/history/parent-1.md',
109
+ }),
110
110
  });
111
111
  await handler(trigger);
112
- assert.deepEqual(discoveredAuthPaths, ["/auth/path"]);
112
+ assert.deepEqual(discoveredAuthPaths, ['/auth/path']);
113
113
  assert.equal(runnerInputs.length, 1);
114
- assert.equal(runnerInputs[0]?.prompt, "API runner prompt\n- History path: /auth/path/.agentteams/runner/history/trigger-1.md\n- Previous history path: /auth/path/.agentteams/runner/history/parent-1.md");
115
- assert.equal(logEntries.some((entry) => entry.level === "INFO" && entry.message.includes("stdout")), true);
116
- assert.equal(logEntries.some((entry) => entry.level === "WARN" && entry.message.includes("stderr")), true);
117
- assert.deepEqual(clientCalls.map((entry) => entry.method), [
118
- "fetchTriggerRuntime",
119
- "isTriggerCancelRequested",
120
- "updateTriggerHistory",
121
- "updateTriggerStatus"
122
- ]);
123
- assert.deepEqual(clientCalls.at(-1)?.args, ["trigger-1", "DONE", undefined]);
114
+ assert.equal(runnerInputs[0]?.prompt, 'API runner prompt\n- History path: /auth/path/.agentteams/runner/history/trigger-1.md\n- Previous history path: /auth/path/.agentteams/runner/history/parent-1.md');
115
+ assert.equal(logEntries.some((entry) => entry.level === 'INFO' && entry.message.includes('stdout')), true);
116
+ assert.equal(logEntries.some((entry) => entry.level === 'WARN' && entry.message.includes('stderr')), true);
117
+ assert.deepEqual(clientCalls.map((entry) => entry.method), ['fetchTriggerRuntime', 'isTriggerCancelRequested', 'updateTriggerHistory', 'updateTriggerStatus']);
118
+ assert.deepEqual(clientCalls.at(-1)?.args, ['trigger-1', 'DONE', undefined]);
124
119
  });
125
- test("createTriggerHandler preserves the local history file and still marks success when history upload fails", async () => {
120
+ test('createTriggerHandler preserves the local history file and still marks success when history upload fails', async () => {
126
121
  const clientCalls = [];
127
122
  const writeHistoryCalls = [];
128
123
  const client = {
129
124
  fetchTriggerRuntime: async () => runtime,
130
125
  isTriggerCancelRequested: async () => false,
131
126
  updateTriggerHistory: async (...args) => {
132
- clientCalls.push({ method: "updateTriggerHistory", args });
127
+ clientCalls.push({ method: 'updateTriggerHistory', args });
133
128
  // 서버가 보고를 거부하는 상황(예: 공유 러너 404, 일시적 네트워크 오류)을 모사.
134
- throw new Error("Failed to update trigger history (404)");
129
+ throw new Error('Failed to update trigger history (404)');
135
130
  },
136
131
  updateTriggerStatus: async (...args) => {
137
- clientCalls.push({ method: "updateTriggerStatus", args });
138
- }
132
+ clientCalls.push({ method: 'updateTriggerStatus', args });
133
+ },
139
134
  };
140
135
  const handler = createTriggerHandler({
141
136
  config: {
142
- daemonToken: "daemon-token",
143
- apiUrl: "https://api.example",
137
+ daemonToken: 'daemon-token',
138
+ apiUrl: 'https://api.example',
144
139
  pollingIntervalMs: 5000,
145
140
  timeoutMs: 1500,
146
141
  idleTimeoutMs: 500,
147
- runnerCmd: "opencode",
148
- preventSleepWhileBusy: false
142
+ runnerCmd: 'opencode',
143
+ preventSleepWhileBusy: false,
149
144
  },
150
- client: client
145
+ client: client,
151
146
  }, {
152
147
  createRunnerFactory: () => () => ({
153
- run: async () => ({ exitCode: 0, outputText: "{\"type\":\"result\"}" })
148
+ run: async () => ({ exitCode: 0, outputText: '{"type":"result"}' }),
154
149
  }),
155
150
  createLogReporter: () => ({
156
151
  start: () => undefined,
157
152
  append: () => undefined,
158
- stop: async () => undefined
153
+ stop: async () => undefined,
159
154
  }),
160
155
  // 러너가 정상 히스토리 파일을 작성한 상태(Questions 섹션 포함 → 정규화 없음).
161
- readHistoryFile: async () => "### Summary\n- done\n\n### Questions for User\nNone",
156
+ readHistoryFile: async () => '### Summary\n- done\n\n### Questions for User\nNone',
162
157
  writeHistoryFile: async (path, content) => {
163
158
  writeHistoryCalls.push({ path, content });
164
159
  },
165
160
  resolveRunnerHistoryPaths: () => ({
166
- currentHistoryPath: "/auth/path/.agentteams/runner/history/trigger-1.md",
167
- parentHistoryPath: null
168
- })
161
+ currentHistoryPath: '/auth/path/.agentteams/runner/history/trigger-1.md',
162
+ parentHistoryPath: null,
163
+ }),
169
164
  });
170
165
  await handler(trigger);
171
166
  // 업로드는 1회 시도됐고(실패), 핵심: 좋은 히스토리 파일을 stdout 폴백으로 덮어쓰지 않는다.
172
- assert.equal(clientCalls.filter((entry) => entry.method === "updateTriggerHistory").length, 1);
167
+ assert.equal(clientCalls.filter((entry) => entry.method === 'updateTriggerHistory').length, 1);
173
168
  assert.deepEqual(writeHistoryCalls, []);
174
169
  // 업로드 실패가 러너 성공을 뒤집지 않고 DONE으로 보고된다(FAILED 아님).
175
- const statusCall = clientCalls.find((entry) => entry.method === "updateTriggerStatus");
176
- assert.deepEqual(statusCall?.args, ["trigger-1", "DONE", undefined]);
170
+ const statusCall = clientCalls.find((entry) => entry.method === 'updateTriggerStatus');
171
+ assert.deepEqual(statusCall?.args, ['trigger-1', 'DONE', undefined]);
177
172
  });
178
- test("createTriggerHandler strips a UTF-8 BOM before reporting history to the database", async () => {
173
+ test('createTriggerHandler strips a UTF-8 BOM before reporting history to the database', async () => {
179
174
  const clientCalls = [];
180
175
  const logEntries = [];
181
176
  const client = {
182
177
  fetchTriggerRuntime: async () => runtime,
183
178
  isTriggerCancelRequested: async () => false,
184
179
  updateTriggerHistory: async (...args) => {
185
- clientCalls.push({ method: "updateTriggerHistory", args });
180
+ clientCalls.push({ method: 'updateTriggerHistory', args });
186
181
  },
187
182
  updateTriggerStatus: async (...args) => {
188
- clientCalls.push({ method: "updateTriggerStatus", args });
189
- }
183
+ clientCalls.push({ method: 'updateTriggerStatus', args });
184
+ },
190
185
  };
191
186
  const handler = createTriggerHandler({
192
187
  config: {
193
- daemonToken: "daemon-token",
194
- apiUrl: "https://api.example",
188
+ daemonToken: 'daemon-token',
189
+ apiUrl: 'https://api.example',
195
190
  pollingIntervalMs: 5000,
196
191
  timeoutMs: 1500,
197
192
  idleTimeoutMs: 500,
198
- runnerCmd: "opencode",
199
- preventSleepWhileBusy: false
193
+ runnerCmd: 'opencode',
194
+ preventSleepWhileBusy: false,
200
195
  },
201
- client: client
196
+ client: client,
202
197
  }, {
203
198
  createRunnerFactory: () => () => ({
204
- run: async () => ({ exitCode: 0 })
199
+ run: async () => ({ exitCode: 0 }),
205
200
  }),
206
201
  createLogReporter: () => ({
207
202
  start: () => undefined,
208
203
  append: (level, message) => {
209
204
  logEntries.push({ level, message });
210
205
  },
211
- stop: async () => undefined
206
+ stop: async () => undefined,
212
207
  }),
213
- readHistoryFile: async () => "\uFEFF### Summary\n- done\n",
208
+ readHistoryFile: async () => '\uFEFF### Summary\n- done\n',
214
209
  resolveRunnerHistoryPaths: () => ({
215
- currentHistoryPath: "/auth/path/.agentteams/runner/history/trigger-1.md",
216
- parentHistoryPath: "/auth/path/.agentteams/runner/history/parent-1.md"
217
- })
210
+ currentHistoryPath: '/auth/path/.agentteams/runner/history/trigger-1.md',
211
+ parentHistoryPath: '/auth/path/.agentteams/runner/history/parent-1.md',
212
+ }),
218
213
  });
219
214
  await handler(trigger);
220
215
  // BOM stripped, and the missing Questions for User section is appended by normalization
221
- assert.deepEqual(clientCalls.at(0)?.args, ["trigger-1", "### Summary\n- done\n\n### Questions for User\nNone"]);
216
+ assert.deepEqual(clientCalls.at(0)?.args, ['trigger-1', '### Summary\n- done\n\n### Questions for User\nNone']);
222
217
  // observation signal: normalization is surfaced to the trigger log (web UI logs tab)
223
- assert.ok(logEntries.some((entry) => entry.level === "WARN" && entry.message.includes("Questions for User")));
218
+ assert.ok(logEntries.some((entry) => entry.level === 'WARN' && entry.message.includes('Questions for User')));
224
219
  });
225
- test("createTriggerHandler keeps history unchanged when the Questions for User section exists", async () => {
220
+ test('createTriggerHandler keeps history unchanged when the Questions for User section exists', async () => {
226
221
  const clientCalls = [];
227
222
  const logEntries = [];
228
223
  const client = {
229
224
  fetchTriggerRuntime: async () => runtime,
230
225
  isTriggerCancelRequested: async () => false,
231
226
  updateTriggerHistory: async (...args) => {
232
- clientCalls.push({ method: "updateTriggerHistory", args });
227
+ clientCalls.push({ method: 'updateTriggerHistory', args });
233
228
  },
234
229
  updateTriggerStatus: async (...args) => {
235
- clientCalls.push({ method: "updateTriggerStatus", args });
236
- }
230
+ clientCalls.push({ method: 'updateTriggerStatus', args });
231
+ },
237
232
  };
238
- const historyWithQuestions = "### Summary\n- done\n\n### Questions for User\n- Should we ship this now?";
233
+ const historyWithQuestions = '### Summary\n- done\n\n### Questions for User\n- Should we ship this now?';
239
234
  const handler = createTriggerHandler({
240
235
  config: {
241
- daemonToken: "daemon-token",
242
- apiUrl: "https://api.example",
236
+ daemonToken: 'daemon-token',
237
+ apiUrl: 'https://api.example',
243
238
  pollingIntervalMs: 5000,
244
239
  timeoutMs: 1500,
245
240
  idleTimeoutMs: 500,
246
- runnerCmd: "opencode",
247
- preventSleepWhileBusy: false
241
+ runnerCmd: 'opencode',
242
+ preventSleepWhileBusy: false,
248
243
  },
249
- client: client
244
+ client: client,
250
245
  }, {
251
246
  createRunnerFactory: () => () => ({
252
- run: async () => ({ exitCode: 0 })
247
+ run: async () => ({ exitCode: 0 }),
253
248
  }),
254
249
  createLogReporter: () => ({
255
250
  start: () => undefined,
256
251
  append: (level, message) => {
257
252
  logEntries.push({ level, message });
258
253
  },
259
- stop: async () => undefined
254
+ stop: async () => undefined,
260
255
  }),
261
256
  readHistoryFile: async () => historyWithQuestions,
262
257
  resolveRunnerHistoryPaths: () => ({
263
- currentHistoryPath: "/auth/path/.agentteams/runner/history/trigger-1.md",
264
- parentHistoryPath: "/auth/path/.agentteams/runner/history/parent-1.md"
265
- })
258
+ currentHistoryPath: '/auth/path/.agentteams/runner/history/trigger-1.md',
259
+ parentHistoryPath: '/auth/path/.agentteams/runner/history/parent-1.md',
260
+ }),
266
261
  });
267
262
  await handler(trigger);
268
- assert.deepEqual(clientCalls.at(0)?.args, ["trigger-1", historyWithQuestions]);
263
+ assert.deepEqual(clientCalls.at(0)?.args, ['trigger-1', historyWithQuestions]);
269
264
  // no normalization → no observation warning
270
- assert.ok(!logEntries.some((entry) => entry.message.includes("Questions for User")));
265
+ assert.ok(!logEntries.some((entry) => entry.message.includes('Questions for User')));
271
266
  });
272
- test("createTriggerHandler restores parent history from server-side coaction content", async () => {
267
+ test('createTriggerHandler restores parent history from server-side coaction content', async () => {
273
268
  const runnerInputs = [];
274
269
  const writtenFiles = [];
275
270
  const client = {
276
271
  fetchTriggerRuntime: async () => ({
277
272
  ...runtime,
278
- parentHistoryMarkdown: "### Summary\n- restored from coaction\n"
273
+ parentHistoryMarkdown: '### Summary\n- restored from coaction\n',
279
274
  }),
280
275
  isTriggerCancelRequested: async () => false,
281
276
  updateTriggerHistory: async () => undefined,
282
- updateTriggerStatus: async () => undefined
277
+ updateTriggerStatus: async () => undefined,
283
278
  };
284
279
  const handler = createTriggerHandler({
285
280
  config: {
286
- daemonToken: "daemon-token",
287
- apiUrl: "https://api.example",
281
+ daemonToken: 'daemon-token',
282
+ apiUrl: 'https://api.example',
288
283
  pollingIntervalMs: 5000,
289
284
  timeoutMs: 1500,
290
285
  idleTimeoutMs: 500,
291
- runnerCmd: "opencode",
292
- preventSleepWhileBusy: false
286
+ runnerCmd: 'opencode',
287
+ preventSleepWhileBusy: false,
293
288
  },
294
- client: client
289
+ client: client,
295
290
  }, {
296
291
  createRunnerFactory: () => () => ({
297
292
  run: async (input) => {
298
293
  runnerInputs.push({ prompt: input.prompt, authPath: input.authPath });
299
294
  return { exitCode: 0 };
300
- }
295
+ },
301
296
  }),
302
297
  createLogReporter: () => ({
303
298
  start: () => undefined,
304
299
  append: () => undefined,
305
- stop: async () => undefined
300
+ stop: async () => undefined,
306
301
  }),
307
302
  readHistoryFile: async (path) => {
308
- if (String(path).endsWith("parent-1.md")) {
309
- throw new Error("ENOENT");
303
+ if (String(path).endsWith('parent-1.md')) {
304
+ throw new Error('ENOENT');
310
305
  }
311
- return "### Summary\n- current\n";
306
+ return '### Summary\n- current\n';
312
307
  },
313
308
  writeHistoryFile: async (path, content) => {
314
309
  writtenFiles.push({ path, content });
315
310
  },
316
311
  resolveRunnerHistoryPaths: () => ({
317
- currentHistoryPath: "/auth/path/.agentteams/runner/history/trigger-1.md",
318
- parentHistoryPath: "/auth/path/.agentteams/runner/history/parent-1.md"
319
- })
312
+ currentHistoryPath: '/auth/path/.agentteams/runner/history/trigger-1.md',
313
+ parentHistoryPath: '/auth/path/.agentteams/runner/history/parent-1.md',
314
+ }),
320
315
  });
321
316
  await handler(trigger);
322
317
  assert.equal(runnerInputs.length, 1);
323
- assert.deepEqual(writtenFiles, [{
324
- path: "/auth/path/.agentteams/runner/history/parent-1.md",
325
- content: "### Summary\n- restored from coaction"
326
- }]);
318
+ assert.deepEqual(writtenFiles, [
319
+ {
320
+ path: '/auth/path/.agentteams/runner/history/parent-1.md',
321
+ content: '### Summary\n- restored from coaction',
322
+ },
323
+ ]);
327
324
  });
328
- test("createTriggerHandler strips a UTF-8 BOM before restoring parent history from the server", async () => {
325
+ test('createTriggerHandler strips a UTF-8 BOM before restoring parent history from the server', async () => {
329
326
  const writtenFiles = [];
330
327
  const client = {
331
328
  fetchTriggerRuntime: async () => ({
332
329
  ...runtime,
333
- parentHistoryMarkdown: "\uFEFF### Summary\n- restored from coaction\n"
330
+ parentHistoryMarkdown: '\uFEFF### Summary\n- restored from coaction\n',
334
331
  }),
335
332
  isTriggerCancelRequested: async () => false,
336
333
  updateTriggerHistory: async () => undefined,
337
- updateTriggerStatus: async () => undefined
334
+ updateTriggerStatus: async () => undefined,
338
335
  };
339
336
  const handler = createTriggerHandler({
340
337
  config: {
341
- daemonToken: "daemon-token",
342
- apiUrl: "https://api.example",
338
+ daemonToken: 'daemon-token',
339
+ apiUrl: 'https://api.example',
343
340
  pollingIntervalMs: 5000,
344
341
  timeoutMs: 1500,
345
342
  idleTimeoutMs: 500,
346
- runnerCmd: "opencode",
347
- preventSleepWhileBusy: false
343
+ runnerCmd: 'opencode',
344
+ preventSleepWhileBusy: false,
348
345
  },
349
- client: client
346
+ client: client,
350
347
  }, {
351
348
  createRunnerFactory: () => () => ({
352
- run: async () => ({ exitCode: 0 })
349
+ run: async () => ({ exitCode: 0 }),
353
350
  }),
354
351
  createLogReporter: () => ({
355
352
  start: () => undefined,
356
353
  append: () => undefined,
357
- stop: async () => undefined
354
+ stop: async () => undefined,
358
355
  }),
359
- readHistoryFile: async () => "### Summary\n- current\n",
356
+ readHistoryFile: async () => '### Summary\n- current\n',
360
357
  writeHistoryFile: async (path, content) => {
361
358
  writtenFiles.push({ path, content });
362
359
  },
363
360
  resolveRunnerHistoryPaths: () => ({
364
- currentHistoryPath: "/auth/path/.agentteams/runner/history/trigger-1.md",
365
- parentHistoryPath: "/auth/path/.agentteams/runner/history/parent-1.md"
366
- })
361
+ currentHistoryPath: '/auth/path/.agentteams/runner/history/trigger-1.md',
362
+ parentHistoryPath: '/auth/path/.agentteams/runner/history/parent-1.md',
363
+ }),
367
364
  });
368
365
  await handler(trigger);
369
- assert.deepEqual(writtenFiles, [{
370
- path: "/auth/path/.agentteams/runner/history/parent-1.md",
371
- content: "### Summary\n- restored from coaction"
372
- }]);
366
+ assert.deepEqual(writtenFiles, [
367
+ {
368
+ path: '/auth/path/.agentteams/runner/history/parent-1.md',
369
+ content: '### Summary\n- restored from coaction',
370
+ },
371
+ ]);
373
372
  });
374
- test("createTriggerHandler overwrites existing parent history with server cumulative context", async () => {
373
+ test('createTriggerHandler overwrites existing parent history with server cumulative context', async () => {
375
374
  const writtenFiles = [];
376
375
  const client = {
377
376
  fetchTriggerRuntime: async () => ({
378
377
  ...runtime,
379
- parentHistoryMarkdown: "### Summary\n- cumulative from server\n"
378
+ parentHistoryMarkdown: '### Summary\n- cumulative from server\n',
380
379
  }),
381
380
  isTriggerCancelRequested: async () => false,
382
381
  updateTriggerHistory: async () => undefined,
383
- updateTriggerStatus: async () => undefined
382
+ updateTriggerStatus: async () => undefined,
384
383
  };
385
384
  const handler = createTriggerHandler({
386
385
  config: {
387
- daemonToken: "daemon-token",
388
- apiUrl: "https://api.example",
386
+ daemonToken: 'daemon-token',
387
+ apiUrl: 'https://api.example',
389
388
  pollingIntervalMs: 5000,
390
389
  timeoutMs: 1500,
391
390
  idleTimeoutMs: 500,
392
- runnerCmd: "opencode",
393
- preventSleepWhileBusy: false
391
+ runnerCmd: 'opencode',
392
+ preventSleepWhileBusy: false,
394
393
  },
395
- client: client
394
+ client: client,
396
395
  }, {
397
396
  createRunnerFactory: () => () => ({
398
- run: async () => ({ exitCode: 0 })
397
+ run: async () => ({ exitCode: 0 }),
399
398
  }),
400
399
  createLogReporter: () => ({
401
400
  start: () => undefined,
402
401
  append: () => undefined,
403
- stop: async () => undefined
402
+ stop: async () => undefined,
404
403
  }),
405
- readHistoryFile: async () => "### Summary\n- stale local parent history\n",
404
+ readHistoryFile: async () => '### Summary\n- stale local parent history\n',
406
405
  writeHistoryFile: async (path, content) => {
407
406
  writtenFiles.push({ path, content });
408
407
  },
409
408
  resolveRunnerHistoryPaths: () => ({
410
- currentHistoryPath: "/auth/path/.agentteams/runner/history/trigger-1.md",
411
- parentHistoryPath: "/auth/path/.agentteams/runner/history/parent-1.md"
412
- })
409
+ currentHistoryPath: '/auth/path/.agentteams/runner/history/trigger-1.md',
410
+ parentHistoryPath: '/auth/path/.agentteams/runner/history/parent-1.md',
411
+ }),
413
412
  });
414
413
  await handler(trigger);
415
- assert.deepEqual(writtenFiles, [{
416
- path: "/auth/path/.agentteams/runner/history/parent-1.md",
417
- content: "### Summary\n- cumulative from server"
418
- }]);
414
+ assert.deepEqual(writtenFiles, [
415
+ {
416
+ path: '/auth/path/.agentteams/runner/history/parent-1.md',
417
+ content: '### Summary\n- cumulative from server',
418
+ },
419
+ ]);
419
420
  });
420
- test("createTriggerHandler reports runner failures and falls back to last output", async () => {
421
+ test('createTriggerHandler reports runner failures and falls back to last output', async () => {
421
422
  const clientCalls = [];
422
423
  const client = {
423
424
  fetchTriggerRuntime: async () => runtime,
424
425
  isTriggerCancelRequested: async () => false,
425
426
  updateTriggerHistory: async (...args) => {
426
- clientCalls.push({ method: "updateTriggerHistory", args });
427
+ clientCalls.push({ method: 'updateTriggerHistory', args });
427
428
  },
428
429
  updateTriggerStatus: async (...args) => {
429
- clientCalls.push({ method: "updateTriggerStatus", args });
430
- }
430
+ clientCalls.push({ method: 'updateTriggerStatus', args });
431
+ },
431
432
  };
432
433
  const handler = createTriggerHandler({
433
434
  config: {
434
- daemonToken: "daemon-token",
435
- apiUrl: "https://api.example",
435
+ daemonToken: 'daemon-token',
436
+ apiUrl: 'https://api.example',
436
437
  pollingIntervalMs: 5000,
437
438
  timeoutMs: 1500,
438
439
  idleTimeoutMs: 500,
439
- runnerCmd: "opencode",
440
- preventSleepWhileBusy: false
440
+ runnerCmd: 'opencode',
441
+ preventSleepWhileBusy: false,
441
442
  },
442
- client: client
443
+ client: client,
443
444
  }, {
444
445
  createRunnerFactory: () => () => ({
445
- run: async () => ({ exitCode: 1, lastOutput: "last output" })
446
+ run: async () => ({ exitCode: 1, lastOutput: 'last output' }),
446
447
  }),
447
448
  createLogReporter: () => ({
448
449
  start: () => undefined,
449
450
  append: () => undefined,
450
- stop: async () => undefined
451
+ stop: async () => undefined,
451
452
  }),
452
- readHistoryFile: async () => "",
453
+ readHistoryFile: async () => '',
453
454
  resolveRunnerHistoryPaths: () => ({
454
- currentHistoryPath: "/auth/path/.agentteams/runner/history/trigger-1.md",
455
- parentHistoryPath: null
456
- })
455
+ currentHistoryPath: '/auth/path/.agentteams/runner/history/trigger-1.md',
456
+ parentHistoryPath: null,
457
+ }),
457
458
  });
458
459
  await handler({ ...trigger, parentTriggerId: null });
459
- assert.deepEqual(clientCalls.at(-1)?.args, ["trigger-1", "FAILED", "last output"]);
460
+ assert.deepEqual(clientCalls.at(-1)?.args, ['trigger-1', 'FAILED', 'last output']);
460
461
  });
461
- test("createTriggerHandler stores stdout as fallback history when the runner omits the history file", async () => {
462
+ test('createTriggerHandler stores stdout as fallback history when the runner omits the history file', async () => {
462
463
  const clientCalls = [];
463
464
  const writtenFiles = [];
464
465
  const client = {
465
466
  fetchTriggerRuntime: async () => runtime,
466
467
  isTriggerCancelRequested: async () => false,
467
468
  updateTriggerHistory: async (...args) => {
468
- clientCalls.push({ method: "updateTriggerHistory", args });
469
+ clientCalls.push({ method: 'updateTriggerHistory', args });
469
470
  },
470
471
  updateTriggerStatus: async (...args) => {
471
- clientCalls.push({ method: "updateTriggerStatus", args });
472
- }
472
+ clientCalls.push({ method: 'updateTriggerStatus', args });
473
+ },
473
474
  };
474
475
  const handler = createTriggerHandler({
475
476
  config: {
476
- daemonToken: "daemon-token",
477
- apiUrl: "https://api.example",
477
+ daemonToken: 'daemon-token',
478
+ apiUrl: 'https://api.example',
478
479
  pollingIntervalMs: 5000,
479
480
  timeoutMs: 1500,
480
481
  idleTimeoutMs: 500,
481
- runnerCmd: "opencode",
482
- preventSleepWhileBusy: false
482
+ runnerCmd: 'opencode',
483
+ preventSleepWhileBusy: false,
483
484
  },
484
- client: client
485
+ client: client,
485
486
  }, {
486
487
  createRunnerFactory: () => () => ({
487
488
  run: async () => ({
488
489
  exitCode: 0,
489
- outputText: "agentrunner version 0.0.11"
490
- })
490
+ outputText: 'agentrunner version 0.0.11',
491
+ }),
491
492
  }),
492
493
  createLogReporter: () => ({
493
494
  start: () => undefined,
494
495
  append: () => undefined,
495
- stop: async () => undefined
496
+ stop: async () => undefined,
496
497
  }),
497
498
  writeHistoryFile: async (path, content) => {
498
499
  writtenFiles.push({ path, content });
499
500
  },
500
501
  readHistoryFile: async () => {
501
- throw new Error("ENOENT");
502
+ throw new Error('ENOENT');
502
503
  },
503
504
  resolveRunnerHistoryPaths: () => ({
504
- currentHistoryPath: "/auth/path/.agentteams/runner/history/trigger-1.md",
505
- parentHistoryPath: null
506
- })
505
+ currentHistoryPath: '/auth/path/.agentteams/runner/history/trigger-1.md',
506
+ parentHistoryPath: null,
507
+ }),
507
508
  });
508
509
  await handler({ ...trigger, parentTriggerId: null });
509
- assert.deepEqual(clientCalls.map((entry) => entry.method), [
510
- "updateTriggerHistory",
511
- "updateTriggerStatus"
512
- ]);
510
+ assert.deepEqual(clientCalls.map((entry) => entry.method), ['updateTriggerHistory', 'updateTriggerStatus']);
513
511
  assert.equal(writtenFiles.length, 1);
514
- assert.equal(writtenFiles[0]?.path, "/auth/path/.agentteams/runner/history/trigger-1.md");
512
+ assert.equal(writtenFiles[0]?.path, '/auth/path/.agentteams/runner/history/trigger-1.md');
515
513
  assert.match(String(clientCalls[0]?.args[1]), /Agent output \(history file not written\)/);
516
514
  assert.match(String(clientCalls[0]?.args[1]), /agentrunner version 0\.0\.11/);
517
- assert.deepEqual(clientCalls.at(-1)?.args, ["trigger-1", "DONE", undefined]);
515
+ assert.deepEqual(clientCalls.at(-1)?.args, ['trigger-1', 'DONE', undefined]);
518
516
  });
519
- test("createTriggerHandler truncates long agent output in fallback history", async () => {
517
+ test('createTriggerHandler truncates long agent output in fallback history', async () => {
520
518
  const clientCalls = [];
521
519
  const writtenFiles = [];
522
- const longOutput = "x".repeat(9000);
520
+ const longOutput = 'x'.repeat(9000);
523
521
  const client = {
524
522
  fetchTriggerRuntime: async () => runtime,
525
523
  isTriggerCancelRequested: async () => false,
526
524
  updateTriggerHistory: async (...args) => {
527
- clientCalls.push({ method: "updateTriggerHistory", args });
525
+ clientCalls.push({ method: 'updateTriggerHistory', args });
528
526
  },
529
527
  updateTriggerStatus: async (...args) => {
530
- clientCalls.push({ method: "updateTriggerStatus", args });
531
- }
528
+ clientCalls.push({ method: 'updateTriggerStatus', args });
529
+ },
532
530
  };
533
531
  const handler = createTriggerHandler({
534
532
  config: {
535
- daemonToken: "daemon-token",
536
- apiUrl: "https://api.example",
533
+ daemonToken: 'daemon-token',
534
+ apiUrl: 'https://api.example',
537
535
  pollingIntervalMs: 5000,
538
536
  timeoutMs: 1500,
539
537
  idleTimeoutMs: 500,
540
- runnerCmd: "opencode",
541
- preventSleepWhileBusy: false
538
+ runnerCmd: 'opencode',
539
+ preventSleepWhileBusy: false,
542
540
  },
543
- client: client
541
+ client: client,
544
542
  }, {
545
543
  createRunnerFactory: () => () => ({
546
544
  run: async () => ({
547
545
  exitCode: 0,
548
- outputText: longOutput
549
- })
546
+ outputText: longOutput,
547
+ }),
550
548
  }),
551
549
  createLogReporter: () => ({
552
550
  start: () => undefined,
553
551
  append: () => undefined,
554
- stop: async () => undefined
552
+ stop: async () => undefined,
555
553
  }),
556
554
  writeHistoryFile: async (path, content) => {
557
555
  writtenFiles.push({ path, content });
558
556
  },
559
557
  readHistoryFile: async () => {
560
- throw new Error("ENOENT");
558
+ throw new Error('ENOENT');
561
559
  },
562
560
  resolveRunnerHistoryPaths: () => ({
563
- currentHistoryPath: "/auth/path/.agentteams/runner/history/trigger-1.md",
564
- parentHistoryPath: null
565
- })
561
+ currentHistoryPath: '/auth/path/.agentteams/runner/history/trigger-1.md',
562
+ parentHistoryPath: null,
563
+ }),
566
564
  });
567
565
  await handler({ ...trigger, parentTriggerId: null });
568
566
  const fallbackContent = String(clientCalls[0]?.args[1]);
569
567
  assert.match(fallbackContent, /\*\(truncated\)\*/);
570
- assert.ok(fallbackContent.length < 9000, "Fallback history should be truncated");
571
- assert.ok(fallbackContent.includes("x".repeat(8000)), "Fallback history should retain output up to the 8,000 character limit");
568
+ assert.ok(fallbackContent.length < 9000, 'Fallback history should be truncated');
569
+ assert.ok(fallbackContent.includes('x'.repeat(8000)), 'Fallback history should retain output up to the 8,000 character limit');
572
570
  });
573
- test("createTriggerHandler preserves fallback agent output within history limit", async () => {
571
+ test('createTriggerHandler preserves fallback agent output within history limit', async () => {
574
572
  const clientCalls = [];
575
573
  const writtenFiles = [];
576
- const output = "x".repeat(8000);
574
+ const output = 'x'.repeat(8000);
577
575
  const client = {
578
576
  fetchTriggerRuntime: async () => runtime,
579
577
  isTriggerCancelRequested: async () => false,
580
578
  updateTriggerHistory: async (...args) => {
581
- clientCalls.push({ method: "updateTriggerHistory", args });
579
+ clientCalls.push({ method: 'updateTriggerHistory', args });
582
580
  },
583
581
  updateTriggerStatus: async (...args) => {
584
- clientCalls.push({ method: "updateTriggerStatus", args });
585
- }
582
+ clientCalls.push({ method: 'updateTriggerStatus', args });
583
+ },
586
584
  };
587
585
  const handler = createTriggerHandler({
588
586
  config: {
589
- daemonToken: "daemon-token",
590
- apiUrl: "https://api.example",
587
+ daemonToken: 'daemon-token',
588
+ apiUrl: 'https://api.example',
591
589
  pollingIntervalMs: 5000,
592
590
  timeoutMs: 1500,
593
591
  idleTimeoutMs: 500,
594
- runnerCmd: "opencode",
595
- preventSleepWhileBusy: false
592
+ runnerCmd: 'opencode',
593
+ preventSleepWhileBusy: false,
596
594
  },
597
- client: client
595
+ client: client,
598
596
  }, {
599
597
  createRunnerFactory: () => () => ({
600
598
  run: async () => ({
601
599
  exitCode: 0,
602
- outputText: output
603
- })
600
+ outputText: output,
601
+ }),
604
602
  }),
605
603
  createLogReporter: () => ({
606
604
  start: () => undefined,
607
605
  append: () => undefined,
608
- stop: async () => undefined
606
+ stop: async () => undefined,
609
607
  }),
610
608
  writeHistoryFile: async (path, content) => {
611
609
  writtenFiles.push({ path, content });
612
610
  },
613
611
  readHistoryFile: async () => {
614
- throw new Error("ENOENT");
612
+ throw new Error('ENOENT');
615
613
  },
616
614
  resolveRunnerHistoryPaths: () => ({
617
- currentHistoryPath: "/auth/path/.agentteams/runner/history/trigger-1.md",
618
- parentHistoryPath: null
619
- })
615
+ currentHistoryPath: '/auth/path/.agentteams/runner/history/trigger-1.md',
616
+ parentHistoryPath: null,
617
+ }),
620
618
  });
621
619
  await handler({ ...trigger, parentTriggerId: null });
622
620
  const fallbackContent = String(clientCalls[0]?.args[1]);
623
621
  assert.doesNotMatch(fallbackContent, /\*\(truncated\)\*/);
624
622
  assert.match(fallbackContent, new RegExp(`x{${output.length}}`));
625
623
  });
626
- test("createTriggerHandler cancels the runner when the server reports a cancel request", async () => {
624
+ test('createTriggerHandler cancels the runner when the server reports a cancel request', async () => {
627
625
  const clientCalls = [];
628
626
  const client = {
629
627
  fetchTriggerRuntime: async () => runtime,
630
628
  isTriggerCancelRequested: async (...args) => {
631
- clientCalls.push({ method: "isTriggerCancelRequested", args });
629
+ clientCalls.push({ method: 'isTriggerCancelRequested', args });
632
630
  return true;
633
631
  },
634
632
  updateTriggerHistory: async (...args) => {
635
- clientCalls.push({ method: "updateTriggerHistory", args });
633
+ clientCalls.push({ method: 'updateTriggerHistory', args });
636
634
  },
637
635
  updateTriggerStatus: async (...args) => {
638
- clientCalls.push({ method: "updateTriggerStatus", args });
639
- }
636
+ clientCalls.push({ method: 'updateTriggerStatus', args });
637
+ },
640
638
  };
641
639
  const handler = createTriggerHandler({
642
640
  config: {
643
- daemonToken: "daemon-token",
644
- apiUrl: "https://api.example",
641
+ daemonToken: 'daemon-token',
642
+ apiUrl: 'https://api.example',
645
643
  pollingIntervalMs: 5000,
646
644
  timeoutMs: 1500,
647
645
  idleTimeoutMs: 500,
648
- runnerCmd: "opencode",
649
- preventSleepWhileBusy: false
646
+ runnerCmd: 'opencode',
647
+ preventSleepWhileBusy: false,
650
648
  },
651
- client: client
649
+ client: client,
652
650
  }, {
653
651
  createRunnerFactory: () => () => ({
654
652
  run: async ({ signal }) => {
655
653
  await new Promise((resolve) => {
656
- signal?.addEventListener("abort", () => resolve(), { once: true });
654
+ signal?.addEventListener('abort', () => resolve(), { once: true });
657
655
  if (signal?.aborted) {
658
656
  resolve();
659
657
  }
@@ -661,70 +659,70 @@ test("createTriggerHandler cancels the runner when the server reports a cancel r
661
659
  return {
662
660
  exitCode: 1,
663
661
  cancelled: true,
664
- errorMessage: "Runner cancelled by user"
662
+ errorMessage: 'Runner cancelled by user',
665
663
  };
666
- }
664
+ },
667
665
  }),
668
666
  createLogReporter: () => ({
669
667
  start: () => undefined,
670
668
  append: () => undefined,
671
- stop: async () => undefined
669
+ stop: async () => undefined,
672
670
  }),
673
- readHistoryFile: async () => "",
671
+ readHistoryFile: async () => '',
674
672
  cancelPollIntervalMs: 1,
675
673
  resolveRunnerHistoryPaths: () => ({
676
- currentHistoryPath: "/auth/path/.agentteams/runner/history/trigger-1.md",
677
- parentHistoryPath: null
678
- })
674
+ currentHistoryPath: '/auth/path/.agentteams/runner/history/trigger-1.md',
675
+ parentHistoryPath: null,
676
+ }),
679
677
  });
680
678
  await handler({ ...trigger, parentTriggerId: null });
681
- assert.deepEqual(clientCalls.at(-1)?.args, ["trigger-1", "CANCELLED", "Runner cancelled by user"]);
679
+ assert.deepEqual(clientCalls.at(-1)?.args, ['trigger-1', 'CANCELLED', 'Runner cancelled by user']);
682
680
  });
683
- test("createTriggerHandler marks the trigger as failed when runtime loading throws", async () => {
681
+ test('createTriggerHandler marks the trigger as failed when runtime loading throws', async () => {
684
682
  const errors = [];
685
- mock.method(logger, "error", (message, meta) => {
683
+ mock.method(logger, 'error', (message, meta) => {
686
684
  errors.push({ message, meta });
687
685
  });
688
686
  const clientCalls = [];
689
687
  const client = {
690
688
  fetchTriggerRuntime: async () => {
691
- throw new Error("runtime boom");
689
+ throw new Error('runtime boom');
692
690
  },
693
691
  isTriggerCancelRequested: async () => false,
694
692
  updateTriggerHistory: async (...args) => {
695
- clientCalls.push({ method: "updateTriggerHistory", args });
693
+ clientCalls.push({ method: 'updateTriggerHistory', args });
696
694
  },
697
695
  updateTriggerStatus: async (...args) => {
698
- clientCalls.push({ method: "updateTriggerStatus", args });
699
- }
696
+ clientCalls.push({ method: 'updateTriggerStatus', args });
697
+ },
700
698
  };
701
699
  const handler = createTriggerHandler({
702
700
  config: {
703
- daemonToken: "daemon-token",
704
- apiUrl: "https://api.example",
701
+ daemonToken: 'daemon-token',
702
+ apiUrl: 'https://api.example',
705
703
  pollingIntervalMs: 5000,
706
704
  timeoutMs: 1500,
707
705
  idleTimeoutMs: 500,
708
- runnerCmd: "opencode",
709
- preventSleepWhileBusy: false
706
+ runnerCmd: 'opencode',
707
+ preventSleepWhileBusy: false,
710
708
  },
711
- client: client
709
+ client: client,
712
710
  }, {
713
711
  createLogReporter: () => ({
714
712
  start: () => undefined,
715
713
  append: () => undefined,
716
- stop: async () => undefined
717
- })
714
+ stop: async () => undefined,
715
+ }),
718
716
  });
719
717
  await handler({ ...trigger, parentTriggerId: null });
720
- assert.deepEqual(clientCalls.at(-1)?.args, ["trigger-1", "FAILED", "runtime boom"]);
721
- assert.equal(errors.some((entry) => entry.message === "Trigger handling failed"), true);
718
+ assert.deepEqual(clientCalls.at(-1)?.args, ['trigger-1', 'FAILED', 'runtime boom']);
719
+ assert.equal(errors.some((entry) => entry.message === 'Trigger handling failed'), true);
722
720
  });
723
721
  // ---------------------------------------------------------------------------
724
722
  // Pre-execution hook tests
725
723
  // ---------------------------------------------------------------------------
726
724
  const withTempDir = async (run) => {
727
- const dir = await mkdtemp(join(tmpdir(), "trigger-handler-hook-test-"));
725
+ const dir = await mkdtemp(join(tmpdir(), 'trigger-handler-hook-test-'));
728
726
  try {
729
727
  await run(dir);
730
728
  }
@@ -732,14 +730,14 @@ const withTempDir = async (run) => {
732
730
  await rm(dir, { recursive: true, force: true });
733
731
  }
734
732
  };
735
- test("createTriggerHandler passes the API-provided runner prompt unchanged", async () => {
733
+ test('createTriggerHandler passes the API-provided runner prompt unchanged', async () => {
736
734
  await withTempDir(async (dir) => {
737
735
  const runnerInputs = [];
738
736
  const client = {
739
737
  fetchTriggerRuntime: async () => ({
740
738
  ...runtime,
741
739
  authPath: dir,
742
- runnerPrompt: "Prompt generated by API\n\nDo exactly this.",
740
+ runnerPrompt: 'Prompt generated by API\n\nDo exactly this.',
743
741
  }),
744
742
  isTriggerCancelRequested: async () => false,
745
743
  updateTriggerHistory: async () => undefined,
@@ -747,12 +745,12 @@ test("createTriggerHandler passes the API-provided runner prompt unchanged", asy
747
745
  };
748
746
  const handler = createTriggerHandler({
749
747
  config: {
750
- daemonToken: "daemon-token",
751
- apiUrl: "https://api.example",
748
+ daemonToken: 'daemon-token',
749
+ apiUrl: 'https://api.example',
752
750
  pollingIntervalMs: 5000,
753
751
  timeoutMs: 1500,
754
752
  idleTimeoutMs: 500,
755
- runnerCmd: "opencode",
753
+ runnerCmd: 'opencode',
756
754
  preventSleepWhileBusy: false,
757
755
  },
758
756
  client: client,
@@ -768,18 +766,18 @@ test("createTriggerHandler passes the API-provided runner prompt unchanged", asy
768
766
  append: () => undefined,
769
767
  stop: async () => undefined,
770
768
  }),
771
- readHistoryFile: async () => "### Summary\n- done\n",
769
+ readHistoryFile: async () => '### Summary\n- done\n',
772
770
  resolveRunnerHistoryPaths: () => ({
773
- currentHistoryPath: join(dir, ".agentteams/runner/history/trigger-1.md"),
771
+ currentHistoryPath: join(dir, '.agentteams/runner/history/trigger-1.md'),
774
772
  parentHistoryPath: null,
775
773
  }),
776
774
  });
777
775
  await handler({ ...trigger, parentTriggerId: null });
778
776
  assert.equal(runnerInputs.length, 1);
779
- assert.equal(runnerInputs[0]?.prompt, "Prompt generated by API\n\nDo exactly this.");
777
+ assert.equal(runnerInputs[0]?.prompt, 'Prompt generated by API\n\nDo exactly this.');
780
778
  });
781
779
  });
782
- test("createTriggerHandler downloads attachments into the runner workspace and injects local paths", async () => {
780
+ test('createTriggerHandler downloads attachments into the runner workspace and injects local paths', async () => {
783
781
  await withTempDir(async (dir) => {
784
782
  const runnerInputs = [];
785
783
  const downloadedUrls = [];
@@ -787,15 +785,17 @@ test("createTriggerHandler downloads attachments into the runner workspace and i
787
785
  fetchTriggerRuntime: async () => ({
788
786
  ...runtime,
789
787
  authPath: dir,
790
- runnerPrompt: "Use the attached file.",
791
- attachments: [{
792
- id: "attachment-1",
793
- originalName: "../notes file.md",
794
- mimeType: "text/markdown",
788
+ runnerPrompt: 'Use the attached file.',
789
+ attachments: [
790
+ {
791
+ id: 'attachment-1',
792
+ originalName: '../notes file.md',
793
+ mimeType: 'text/markdown',
795
794
  size: 12,
796
- downloadUrl: "https://storage.example/attachment-1",
795
+ downloadUrl: 'https://storage.example/attachment-1',
797
796
  expiresInSeconds: 300,
798
- }],
797
+ },
798
+ ],
799
799
  }),
800
800
  isTriggerCancelRequested: async () => false,
801
801
  updateTriggerHistory: async () => undefined,
@@ -803,12 +803,12 @@ test("createTriggerHandler downloads attachments into the runner workspace and i
803
803
  };
804
804
  const handler = createTriggerHandler({
805
805
  config: {
806
- daemonToken: "daemon-token",
807
- apiUrl: "https://api.example",
806
+ daemonToken: 'daemon-token',
807
+ apiUrl: 'https://api.example',
808
808
  pollingIntervalMs: 5000,
809
809
  timeoutMs: 1500,
810
810
  idleTimeoutMs: 500,
811
- runnerCmd: "opencode",
811
+ runnerCmd: 'opencode',
812
812
  preventSleepWhileBusy: false,
813
813
  },
814
814
  client: client,
@@ -826,18 +826,18 @@ test("createTriggerHandler downloads attachments into the runner workspace and i
826
826
  }),
827
827
  fetchAttachmentFile: async (downloadUrl) => {
828
828
  downloadedUrls.push(downloadUrl);
829
- return new TextEncoder().encode("hello world\n");
829
+ return new TextEncoder().encode('hello world\n');
830
830
  },
831
- readHistoryFile: async () => "### Summary\n- done\n",
831
+ readHistoryFile: async () => '### Summary\n- done\n',
832
832
  resolveRunnerHistoryPaths: () => ({
833
- currentHistoryPath: join(dir, ".agentteams/runner/history/trigger-1.md"),
833
+ currentHistoryPath: join(dir, '.agentteams/runner/history/trigger-1.md'),
834
834
  parentHistoryPath: null,
835
835
  }),
836
836
  });
837
837
  await handler({ ...trigger, parentTriggerId: null });
838
- assert.deepEqual(downloadedUrls, ["https://storage.example/attachment-1"]);
838
+ assert.deepEqual(downloadedUrls, ['https://storage.example/attachment-1']);
839
839
  assert.equal(runnerInputs.length, 1);
840
- const prompt = runnerInputs[0]?.prompt ?? "";
840
+ const prompt = runnerInputs[0]?.prompt ?? '';
841
841
  assert.match(prompt, /## Attached Files/);
842
842
  assert.match(prompt, /MIME type: text\/markdown/);
843
843
  assert.match(prompt, /Size: 12 bytes/);
@@ -845,26 +845,28 @@ test("createTriggerHandler downloads attachments into the runner workspace and i
845
845
  assert.doesNotMatch(prompt, /https:\/\/storage\.example/);
846
846
  const localPathMatch = prompt.match(/Local path: (.+01-attachme-notes-file\.md)$/m);
847
847
  assert.ok(localPathMatch?.[1]);
848
- assert.equal(localPathMatch[1].startsWith(join(dir, ".agentteams", "runner", "attachments", "trigger-1")), true);
849
- const attachmentDir = join(dir, ".agentteams", "runner", "attachments", "trigger-1");
848
+ assert.equal(localPathMatch[1].startsWith(join(dir, '.agentteams', 'runner', 'attachments', 'trigger-1')), true);
849
+ const attachmentDir = join(dir, '.agentteams', 'runner', 'attachments', 'trigger-1');
850
850
  await assert.rejects(stat(attachmentDir), /ENOENT/);
851
851
  });
852
852
  });
853
- test("createTriggerHandler removes the attachment directory after runner failure", async () => {
853
+ test('createTriggerHandler removes the attachment directory after runner failure', async () => {
854
854
  await withTempDir(async (dir) => {
855
855
  const client = {
856
856
  fetchTriggerRuntime: async () => ({
857
857
  ...runtime,
858
858
  authPath: dir,
859
- runnerPrompt: "Use the attached file.",
860
- attachments: [{
861
- id: "attachment-1",
862
- originalName: "notes.md",
863
- mimeType: "text/markdown",
859
+ runnerPrompt: 'Use the attached file.',
860
+ attachments: [
861
+ {
862
+ id: 'attachment-1',
863
+ originalName: 'notes.md',
864
+ mimeType: 'text/markdown',
864
865
  size: 12,
865
- downloadUrl: "https://storage.example/attachment-1",
866
+ downloadUrl: 'https://storage.example/attachment-1',
866
867
  expiresInSeconds: 300,
867
- }],
868
+ },
869
+ ],
868
870
  }),
869
871
  isTriggerCancelRequested: async () => false,
870
872
  updateTriggerHistory: async () => undefined,
@@ -872,19 +874,19 @@ test("createTriggerHandler removes the attachment directory after runner failure
872
874
  };
873
875
  const handler = createTriggerHandler({
874
876
  config: {
875
- daemonToken: "daemon-token",
876
- apiUrl: "https://api.example",
877
+ daemonToken: 'daemon-token',
878
+ apiUrl: 'https://api.example',
877
879
  pollingIntervalMs: 5000,
878
880
  timeoutMs: 1500,
879
881
  idleTimeoutMs: 500,
880
- runnerCmd: "opencode",
882
+ runnerCmd: 'opencode',
881
883
  preventSleepWhileBusy: false,
882
884
  },
883
885
  client: client,
884
886
  }, {
885
887
  createRunnerFactory: () => () => ({
886
888
  run: async () => {
887
- throw new Error("runner crashed");
889
+ throw new Error('runner crashed');
888
890
  },
889
891
  }),
890
892
  createLogReporter: () => ({
@@ -892,34 +894,36 @@ test("createTriggerHandler removes the attachment directory after runner failure
892
894
  append: () => undefined,
893
895
  stop: async () => undefined,
894
896
  }),
895
- fetchAttachmentFile: async () => new TextEncoder().encode("hello world\n"),
896
- readHistoryFile: async () => "",
897
+ fetchAttachmentFile: async () => new TextEncoder().encode('hello world\n'),
898
+ readHistoryFile: async () => '',
897
899
  resolveRunnerHistoryPaths: () => ({
898
- currentHistoryPath: join(dir, ".agentteams/runner/history/trigger-1.md"),
900
+ currentHistoryPath: join(dir, '.agentteams/runner/history/trigger-1.md'),
899
901
  parentHistoryPath: null,
900
902
  }),
901
903
  });
902
904
  await handler({ ...trigger, parentTriggerId: null });
903
- const attachmentDir = join(dir, ".agentteams", "runner", "attachments", "trigger-1");
905
+ const attachmentDir = join(dir, '.agentteams', 'runner', 'attachments', 'trigger-1');
904
906
  await assert.rejects(stat(attachmentDir), /ENOENT/);
905
907
  });
906
908
  });
907
- test("createTriggerHandler logs but does not throw when attachment cleanup fails", async () => {
909
+ test('createTriggerHandler logs but does not throw when attachment cleanup fails', async () => {
908
910
  await withTempDir(async (dir) => {
909
911
  const cleanupCalls = [];
910
912
  const client = {
911
913
  fetchTriggerRuntime: async () => ({
912
914
  ...runtime,
913
915
  authPath: dir,
914
- runnerPrompt: "Use the attached file.",
915
- attachments: [{
916
- id: "attachment-1",
917
- originalName: "notes.md",
918
- mimeType: "text/markdown",
916
+ runnerPrompt: 'Use the attached file.',
917
+ attachments: [
918
+ {
919
+ id: 'attachment-1',
920
+ originalName: 'notes.md',
921
+ mimeType: 'text/markdown',
919
922
  size: 12,
920
- downloadUrl: "https://storage.example/attachment-1",
923
+ downloadUrl: 'https://storage.example/attachment-1',
921
924
  expiresInSeconds: 300,
922
- }],
925
+ },
926
+ ],
923
927
  }),
924
928
  isTriggerCancelRequested: async () => false,
925
929
  updateTriggerHistory: async () => undefined,
@@ -927,12 +931,12 @@ test("createTriggerHandler logs but does not throw when attachment cleanup fails
927
931
  };
928
932
  const handler = createTriggerHandler({
929
933
  config: {
930
- daemonToken: "daemon-token",
931
- apiUrl: "https://api.example",
934
+ daemonToken: 'daemon-token',
935
+ apiUrl: 'https://api.example',
932
936
  pollingIntervalMs: 5000,
933
937
  timeoutMs: 1500,
934
938
  idleTimeoutMs: 500,
935
- runnerCmd: "opencode",
939
+ runnerCmd: 'opencode',
936
940
  preventSleepWhileBusy: false,
937
941
  },
938
942
  client: client,
@@ -945,53 +949,55 @@ test("createTriggerHandler logs but does not throw when attachment cleanup fails
945
949
  append: () => undefined,
946
950
  stop: async () => undefined,
947
951
  }),
948
- fetchAttachmentFile: async () => new TextEncoder().encode("hello world\n"),
952
+ fetchAttachmentFile: async () => new TextEncoder().encode('hello world\n'),
949
953
  removeAttachmentDirectory: async (path) => {
950
954
  cleanupCalls.push(path);
951
- throw new Error("cleanup boom");
955
+ throw new Error('cleanup boom');
952
956
  },
953
- readHistoryFile: async () => "### Summary\n- done\n",
957
+ readHistoryFile: async () => '### Summary\n- done\n',
954
958
  resolveRunnerHistoryPaths: () => ({
955
- currentHistoryPath: join(dir, ".agentteams/runner/history/trigger-1.md"),
959
+ currentHistoryPath: join(dir, '.agentteams/runner/history/trigger-1.md'),
956
960
  parentHistoryPath: null,
957
961
  }),
958
962
  });
959
963
  await handler({ ...trigger, parentTriggerId: null });
960
- assert.deepEqual(cleanupCalls, [join(dir, ".agentteams", "runner", "attachments", "trigger-1")]);
964
+ assert.deepEqual(cleanupCalls, [join(dir, '.agentteams', 'runner', 'attachments', 'trigger-1')]);
961
965
  });
962
966
  });
963
- test("createTriggerHandler fails before runner execution when attachments have no runner workspace", async () => {
967
+ test('createTriggerHandler fails before runner execution when attachments have no runner workspace', async () => {
964
968
  const clientCalls = [];
965
969
  let runnerCalled = false;
966
970
  const client = {
967
971
  fetchTriggerRuntime: async () => ({
968
972
  ...runtime,
969
973
  authPath: null,
970
- attachments: [{
971
- id: "attachment-1",
972
- originalName: "notes.md",
973
- mimeType: "text/markdown",
974
+ attachments: [
975
+ {
976
+ id: 'attachment-1',
977
+ originalName: 'notes.md',
978
+ mimeType: 'text/markdown',
974
979
  size: 12,
975
- downloadUrl: "https://storage.example/attachment-1",
980
+ downloadUrl: 'https://storage.example/attachment-1',
976
981
  expiresInSeconds: 300,
977
- }],
982
+ },
983
+ ],
978
984
  }),
979
985
  isTriggerCancelRequested: async () => false,
980
986
  updateTriggerHistory: async (...args) => {
981
- clientCalls.push({ method: "updateTriggerHistory", args });
987
+ clientCalls.push({ method: 'updateTriggerHistory', args });
982
988
  },
983
989
  updateTriggerStatus: async (...args) => {
984
- clientCalls.push({ method: "updateTriggerStatus", args });
990
+ clientCalls.push({ method: 'updateTriggerStatus', args });
985
991
  },
986
992
  };
987
993
  const handler = createTriggerHandler({
988
994
  config: {
989
- daemonToken: "daemon-token",
990
- apiUrl: "https://api.example",
995
+ daemonToken: 'daemon-token',
996
+ apiUrl: 'https://api.example',
991
997
  pollingIntervalMs: 5000,
992
998
  timeoutMs: 1500,
993
999
  idleTimeoutMs: 500,
994
- runnerCmd: "opencode",
1000
+ runnerCmd: 'opencode',
995
1001
  preventSleepWhileBusy: false,
996
1002
  },
997
1003
  client: client,
@@ -1012,28 +1018,30 @@ test("createTriggerHandler fails before runner execution when attachments have n
1012
1018
  await handler({ ...trigger, parentTriggerId: null });
1013
1019
  assert.equal(runnerCalled, false);
1014
1020
  assert.deepEqual(clientCalls.at(-1)?.args, [
1015
- "trigger-1",
1016
- "FAILED",
1017
- "Cannot deliver attachments because runner workspace path is not configured."
1021
+ 'trigger-1',
1022
+ 'FAILED',
1023
+ 'Cannot deliver attachments because runner workspace path is not configured.',
1018
1024
  ]);
1019
1025
  });
1020
- test("createTriggerHandler does not append history or convention text to the API prompt", async () => {
1026
+ test('createTriggerHandler does not append history or convention text to the API prompt', async () => {
1021
1027
  await withTempDir(async (dir) => {
1022
1028
  const runnerInputs = [];
1023
- const conventions = [{
1024
- id: "conv-feature",
1025
- filePath: ".agentteams/rules/feature.md",
1026
- trigger: "task:FEATURE",
1027
- title: "Feature Convention",
1028
- description: "Rules for feature tasks",
1029
- }];
1029
+ const conventions = [
1030
+ {
1031
+ id: 'conv-feature',
1032
+ filePath: '.agentteams/rules/feature.md',
1033
+ trigger: 'task:FEATURE',
1034
+ title: 'Feature Convention',
1035
+ description: 'Rules for feature tasks',
1036
+ },
1037
+ ];
1030
1038
  const client = {
1031
1039
  fetchTriggerRuntime: async () => ({
1032
1040
  ...runtime,
1033
1041
  authPath: dir,
1034
1042
  conventions,
1035
- planType: "BUG_FIX",
1036
- runnerPrompt: "Only the API prompt",
1043
+ planType: 'BUG_FIX',
1044
+ runnerPrompt: 'Only the API prompt',
1037
1045
  }),
1038
1046
  isTriggerCancelRequested: async () => false,
1039
1047
  updateTriggerHistory: async () => undefined,
@@ -1041,12 +1049,12 @@ test("createTriggerHandler does not append history or convention text to the API
1041
1049
  };
1042
1050
  const handler = createTriggerHandler({
1043
1051
  config: {
1044
- daemonToken: "daemon-token",
1045
- apiUrl: "https://api.example",
1052
+ daemonToken: 'daemon-token',
1053
+ apiUrl: 'https://api.example',
1046
1054
  pollingIntervalMs: 5000,
1047
1055
  timeoutMs: 1500,
1048
1056
  idleTimeoutMs: 500,
1049
- runnerCmd: "opencode",
1057
+ runnerCmd: 'opencode',
1050
1058
  preventSleepWhileBusy: false,
1051
1059
  },
1052
1060
  client: client,
@@ -1062,16 +1070,16 @@ test("createTriggerHandler does not append history or convention text to the API
1062
1070
  append: () => undefined,
1063
1071
  stop: async () => undefined,
1064
1072
  }),
1065
- readHistoryFile: async () => "### Summary\n- done\n",
1073
+ readHistoryFile: async () => '### Summary\n- done\n',
1066
1074
  resolveRunnerHistoryPaths: () => ({
1067
- currentHistoryPath: join(dir, ".agentteams/runner/history/trigger-1.md"),
1075
+ currentHistoryPath: join(dir, '.agentteams/runner/history/trigger-1.md'),
1068
1076
  parentHistoryPath: null,
1069
1077
  }),
1070
1078
  });
1071
1079
  await handler({ ...trigger, parentTriggerId: null });
1072
1080
  assert.equal(runnerInputs.length, 1);
1073
- assert.equal(runnerInputs[0]?.prompt, "Only the API prompt");
1074
- assert.doesNotMatch(runnerInputs[0]?.prompt ?? "", /Context-Matched Conventions \(AUTO-LOADED\)/);
1081
+ assert.equal(runnerInputs[0]?.prompt, 'Only the API prompt');
1082
+ assert.doesNotMatch(runnerInputs[0]?.prompt ?? '', /Context-Matched Conventions \(AUTO-LOADED\)/);
1075
1083
  });
1076
1084
  });
1077
1085
  //# sourceMappingURL=trigger-handler.test.js.map