@agentmeshhq/agent 0.1.17 → 0.2.1

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 (143) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +39 -0
  3. package/dist/__tests__/orphan-process.test.d.ts +11 -0
  4. package/dist/__tests__/orphan-process.test.js +286 -0
  5. package/dist/__tests__/orphan-process.test.js.map +1 -0
  6. package/dist/__tests__/runner.test.js +16 -0
  7. package/dist/__tests__/runner.test.js.map +1 -1
  8. package/dist/__tests__/watchdog.test.js +138 -12
  9. package/dist/__tests__/watchdog.test.js.map +1 -1
  10. package/dist/cli/index.js +2 -1
  11. package/dist/cli/index.js.map +1 -1
  12. package/dist/cli/start.d.ts +2 -1
  13. package/dist/cli/start.js +6 -3
  14. package/dist/cli/start.js.map +1 -1
  15. package/dist/cli/status.js +11 -0
  16. package/dist/cli/status.js.map +1 -1
  17. package/dist/cli/stop.js +7 -2
  18. package/dist/cli/stop.js.map +1 -1
  19. package/dist/config/schema.d.ts +4 -2
  20. package/dist/core/daemon/assignment-message.d.ts +12 -0
  21. package/dist/core/daemon/assignment-message.js +36 -0
  22. package/dist/core/daemon/assignment-message.js.map +1 -0
  23. package/dist/core/daemon/bootstrap.d.ts +35 -0
  24. package/dist/core/daemon/bootstrap.js +52 -0
  25. package/dist/core/daemon/bootstrap.js.map +1 -0
  26. package/dist/core/daemon/crash-log.d.ts +16 -0
  27. package/dist/core/daemon/crash-log.js +24 -0
  28. package/dist/core/daemon/crash-log.js.map +1 -0
  29. package/dist/core/daemon/health-policy.d.ts +21 -0
  30. package/dist/core/daemon/health-policy.js +32 -0
  31. package/dist/core/daemon/health-policy.js.map +1 -0
  32. package/dist/core/daemon/sandbox-config.d.ts +9 -0
  33. package/dist/core/daemon/sandbox-config.js +17 -0
  34. package/dist/core/daemon/sandbox-config.js.map +1 -0
  35. package/dist/core/daemon/state.d.ts +33 -0
  36. package/dist/core/daemon/state.js +77 -0
  37. package/dist/core/daemon/state.js.map +1 -0
  38. package/dist/core/daemon/tmux-session.d.ts +17 -0
  39. package/dist/core/daemon/tmux-session.js +34 -0
  40. package/dist/core/daemon/tmux-session.js.map +1 -0
  41. package/dist/core/daemon/workspace.d.ts +10 -0
  42. package/dist/core/daemon/workspace.js +51 -0
  43. package/dist/core/daemon/workspace.js.map +1 -0
  44. package/dist/core/daemon.d.ts +4 -7
  45. package/dist/core/daemon.js +143 -259
  46. package/dist/core/daemon.js.map +1 -1
  47. package/dist/core/injector.js +6 -0
  48. package/dist/core/injector.js.map +1 -1
  49. package/dist/core/registry.js +1 -1
  50. package/dist/core/registry.js.map +1 -1
  51. package/dist/core/runner/build.d.ts +9 -0
  52. package/dist/core/runner/build.js +53 -0
  53. package/dist/core/runner/build.js.map +1 -0
  54. package/dist/core/runner/detect.d.ts +5 -0
  55. package/dist/core/runner/detect.js +14 -0
  56. package/dist/core/runner/detect.js.map +1 -0
  57. package/dist/core/runner/index.d.ts +5 -0
  58. package/dist/core/runner/index.js +5 -0
  59. package/dist/core/runner/index.js.map +1 -0
  60. package/dist/core/runner/model.d.ts +5 -0
  61. package/dist/core/runner/model.js +7 -0
  62. package/dist/core/runner/model.js.map +1 -0
  63. package/dist/core/runner/opencode-models.d.ts +15 -0
  64. package/dist/core/runner/opencode-models.js +70 -0
  65. package/dist/core/runner/opencode-models.js.map +1 -0
  66. package/dist/core/runner/types.d.ts +19 -0
  67. package/dist/core/runner/types.js +8 -0
  68. package/dist/core/runner/types.js.map +1 -0
  69. package/dist/core/runner.d.ts +5 -47
  70. package/dist/core/runner.js +5 -167
  71. package/dist/core/runner.js.map +1 -1
  72. package/dist/core/tmux-runtime.d.ts +13 -0
  73. package/dist/core/tmux-runtime.js +72 -0
  74. package/dist/core/tmux-runtime.js.map +1 -0
  75. package/dist/core/tmux.d.ts +7 -1
  76. package/dist/core/tmux.js +75 -45
  77. package/dist/core/tmux.js.map +1 -1
  78. package/dist/core/watchdog.d.ts +18 -1
  79. package/dist/core/watchdog.js +78 -29
  80. package/dist/core/watchdog.js.map +1 -1
  81. package/package.json +30 -11
  82. package/dist/cli/inbox.d.ts +0 -5
  83. package/dist/cli/inbox.js +0 -123
  84. package/dist/cli/inbox.js.map +0 -1
  85. package/dist/cli/issue.d.ts +0 -42
  86. package/dist/cli/issue.js +0 -297
  87. package/dist/cli/issue.js.map +0 -1
  88. package/dist/cli/ready.d.ts +0 -5
  89. package/dist/cli/ready.js +0 -131
  90. package/dist/cli/ready.js.map +0 -1
  91. package/dist/cli/sync.d.ts +0 -8
  92. package/dist/cli/sync.js +0 -154
  93. package/dist/cli/sync.js.map +0 -1
  94. package/dist/core/issue-cache.d.ts +0 -44
  95. package/dist/core/issue-cache.js +0 -75
  96. package/dist/core/issue-cache.js.map +0 -1
  97. package/src/__tests__/context.test.ts +0 -464
  98. package/src/__tests__/injector.test.ts +0 -29
  99. package/src/__tests__/jwt.test.ts +0 -112
  100. package/src/__tests__/loader.test.ts +0 -239
  101. package/src/__tests__/runner.test.ts +0 -104
  102. package/src/__tests__/sandbox.test.ts +0 -435
  103. package/src/__tests__/watchdog.test.ts +0 -368
  104. package/src/cli/attach.ts +0 -22
  105. package/src/cli/build.ts +0 -145
  106. package/src/cli/config.ts +0 -148
  107. package/src/cli/context.ts +0 -231
  108. package/src/cli/deploy.ts +0 -155
  109. package/src/cli/index.ts +0 -375
  110. package/src/cli/init.ts +0 -75
  111. package/src/cli/list.ts +0 -70
  112. package/src/cli/local.ts +0 -183
  113. package/src/cli/logs.ts +0 -64
  114. package/src/cli/migrate.ts +0 -212
  115. package/src/cli/nudge.ts +0 -81
  116. package/src/cli/restart.ts +0 -59
  117. package/src/cli/slack.ts +0 -70
  118. package/src/cli/start.ts +0 -115
  119. package/src/cli/status.ts +0 -91
  120. package/src/cli/stop.ts +0 -48
  121. package/src/cli/test.ts +0 -143
  122. package/src/cli/token.ts +0 -188
  123. package/src/cli/whoami.ts +0 -142
  124. package/src/config/loader.ts +0 -121
  125. package/src/config/schema.ts +0 -68
  126. package/src/context/handoff.ts +0 -122
  127. package/src/context/index.ts +0 -8
  128. package/src/context/schema.ts +0 -111
  129. package/src/context/storage.ts +0 -197
  130. package/src/core/daemon.ts +0 -1308
  131. package/src/core/heartbeat.ts +0 -129
  132. package/src/core/injector.ts +0 -292
  133. package/src/core/registry.ts +0 -159
  134. package/src/core/runner.ts +0 -225
  135. package/src/core/sandbox.ts +0 -547
  136. package/src/core/session-id.ts +0 -111
  137. package/src/core/tmux.ts +0 -405
  138. package/src/core/watchdog.ts +0 -238
  139. package/src/core/websocket.ts +0 -94
  140. package/src/index.ts +0 -10
  141. package/src/utils/jwt.ts +0 -87
  142. package/tsconfig.json +0 -8
  143. package/vitest.config.ts +0 -12
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 therajushahi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -115,6 +115,45 @@ The AI assistant sees the injected message as if you typed it, maintaining full
115
115
  - Node.js 18+
116
116
  - An AgentMesh HQ account with API key
117
117
 
118
+ ## Versioning
119
+
120
+ This package uses [Semantic Versioning](https://semver.org). Releases are fully automated — **do not publish manually**.
121
+
122
+ ### How to release
123
+
124
+ 1. Update `version` in `packages/agent/package.json` on the `dev` branch (via PR).
125
+ 2. Push a tag matching `agent-v<version>` from the commit where the version was bumped:
126
+
127
+ ```bash
128
+ # After your version-bump PR is merged to dev and pulled locally:
129
+ git tag agent-v0.2.1 <merge-commit-sha>
130
+ git push origin agent-v0.2.1
131
+ ```
132
+
133
+ 3. The `publish-agent.yml` GitHub Actions workflow triggers automatically and:
134
+ - Builds and tests the package
135
+ - Verifies `package.json` version matches the tag
136
+ - Publishes to npm as `@agentmeshhq/agent`
137
+ - Creates a GitHub Release
138
+ - Updates the Homebrew formula in `therajushahi/homebrew-agentmesh`
139
+
140
+ ### Rules
141
+
142
+ - **Never run `npm publish` manually.** It bypasses the version check and can publish a mismatched version (e.g. a `package.json` that says `0.1.2` getting published as `0.2.0`).
143
+ - **Never skip a version number.** The Homebrew formula uses the version string; if the npm registry already has a higher version, `brew upgrade` will not install the new formula.
144
+ - **The tag drives everything.** The tag name (`agent-v0.2.1`) is the source of truth — the workflow validates that `package.json` matches before publishing.
145
+ - **All work targets `dev`.** `main` is for releases only, managed by the Product Owner. PRs must target `dev`.
146
+
147
+ ### Version history incident
148
+
149
+ In Feb 2026, `0.2.0` was published manually with `npm publish` from a commit whose `package.json` said `0.1.2`. This caused:
150
+ - npm registry to have `0.2.0` as the latest version
151
+ - The Homebrew formula to point at `0.2.0`
152
+ - All subsequent CI-published versions (`0.1.3`, `0.1.4`) to be *lower* than `0.2.0`, so `brew upgrade` ignored them
153
+ - Workers already on `0.2.0` unable to receive fixes
154
+
155
+ The fix was to bump past `0.2.0` → `0.2.1` using the correct automated pipeline.
156
+
118
157
  ## License
119
158
 
120
159
  MIT
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Tests for orphaned process prevention (P0 fix for the daemon process leak).
3
+ *
4
+ * Covers:
5
+ * - killProcessTree: SIGTERM → SIGKILL escalation
6
+ * - getTmuxPanePid: reads PID from tmux pane
7
+ * - getProcessTree: recursive child enumeration
8
+ * - captureAgentChildPids: captures all PIDs for a given agent
9
+ * - destroySession: kills child PIDs before killing tmux session
10
+ */
11
+ export {};
@@ -0,0 +1,286 @@
1
+ /**
2
+ * Tests for orphaned process prevention (P0 fix for the daemon process leak).
3
+ *
4
+ * Covers:
5
+ * - killProcessTree: SIGTERM → SIGKILL escalation
6
+ * - getTmuxPanePid: reads PID from tmux pane
7
+ * - getProcessTree: recursive child enumeration
8
+ * - captureAgentChildPids: captures all PIDs for a given agent
9
+ * - destroySession: kills child PIDs before killing tmux session
10
+ */
11
+ import { execSync } from "node:child_process";
12
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
13
+ import { captureAgentChildPids, getProcessTree, getTmuxPanePid } from "../core/daemon/state.js";
14
+ import { destroySession, killProcessTree } from "../core/tmux.js";
15
+ // ─── Mocks ────────────────────────────────────────────────────────────────────
16
+ vi.mock("node:child_process", () => ({
17
+ execSync: vi.fn(),
18
+ execFileSync: vi.fn(),
19
+ spawnSync: vi.fn(),
20
+ }));
21
+ vi.mock("../core/tmux-runtime.js", () => ({
22
+ buildInteractiveCommand: vi.fn().mockReturnValue("opencode"),
23
+ detectRunnerCommandKind: vi.fn().mockReturnValue("opencode"),
24
+ prepareOpenCodeRuntime: vi.fn().mockReturnValue("/tmp/test-runtime"),
25
+ }));
26
+ // Helper to mock process.kill without type gymnastics
27
+ function mockProcessKill(impl) {
28
+ return vi.spyOn(process, "kill").mockImplementation(impl);
29
+ }
30
+ const mockExecSync = vi.mocked(execSync);
31
+ // ─── killProcessTree ──────────────────────────────────────────────────────────
32
+ describe("killProcessTree", () => {
33
+ let killSpy;
34
+ beforeEach(() => {
35
+ killSpy = mockProcessKill(() => true);
36
+ vi.useFakeTimers();
37
+ });
38
+ afterEach(() => {
39
+ killSpy.mockRestore();
40
+ vi.useRealTimers();
41
+ });
42
+ it("returns true immediately if pids list is empty", () => {
43
+ const result = killProcessTree([]);
44
+ expect(result).toBe(true);
45
+ expect(killSpy).not.toHaveBeenCalled();
46
+ });
47
+ it("sends SIGTERM to all provided PIDs", () => {
48
+ // Track whether SIGTERM has been sent to each PID
49
+ const terminatedPids = new Set();
50
+ killSpy.mockImplementation((_pid, sig) => {
51
+ if (sig === "SIGTERM") {
52
+ terminatedPids.add(_pid);
53
+ return true;
54
+ }
55
+ if (sig === 0) {
56
+ // Alive until SIGTERM has been sent
57
+ if (terminatedPids.has(_pid))
58
+ throw new Error("no such process");
59
+ return true;
60
+ }
61
+ return true;
62
+ });
63
+ mockExecSync.mockReturnValue("");
64
+ killProcessTree([1234, 5678]);
65
+ expect(killSpy).toHaveBeenCalledWith(1234, "SIGTERM");
66
+ expect(killSpy).toHaveBeenCalledWith(5678, "SIGTERM");
67
+ });
68
+ it("escalates to SIGKILL after timeout when processes remain alive", () => {
69
+ // Process stays alive through SIGTERM, only dies after SIGKILL check
70
+ let killedWithSigkill = false;
71
+ killSpy.mockImplementation((_pid, sig) => {
72
+ if (sig === "SIGKILL") {
73
+ killedWithSigkill = true;
74
+ return true;
75
+ }
76
+ if (sig === 0) {
77
+ // Stay alive until SIGKILL has been sent
78
+ if (killedWithSigkill)
79
+ throw new Error("no such process");
80
+ return true;
81
+ }
82
+ return true;
83
+ });
84
+ mockExecSync.mockImplementation((cmd) => {
85
+ if (cmd === "sleep 0.1")
86
+ vi.advanceTimersByTime(100);
87
+ return "";
88
+ });
89
+ killProcessTree([1234], 200);
90
+ expect(killSpy).toHaveBeenCalledWith(1234, "SIGTERM");
91
+ expect(killSpy).toHaveBeenCalledWith(1234, "SIGKILL");
92
+ });
93
+ it("does not SIGKILL when processes exit gracefully before timeout", () => {
94
+ let sigkillCalled = false;
95
+ let terminatedPid = null;
96
+ killSpy.mockImplementation((_pid, sig) => {
97
+ if (sig === "SIGKILL") {
98
+ sigkillCalled = true;
99
+ return true;
100
+ }
101
+ if (sig === "SIGTERM") {
102
+ terminatedPid = _pid;
103
+ return true;
104
+ }
105
+ if (sig === 0) {
106
+ // Dead after SIGTERM
107
+ if (terminatedPid === _pid)
108
+ throw new Error("no such process");
109
+ return true;
110
+ }
111
+ return true;
112
+ });
113
+ mockExecSync.mockReturnValue("");
114
+ killProcessTree([1234]);
115
+ expect(sigkillCalled).toBe(false);
116
+ });
117
+ });
118
+ // ─── getTmuxPanePid ────────────────────────────────────────────────────────────
119
+ describe("getTmuxPanePid", () => {
120
+ beforeEach(() => {
121
+ vi.clearAllMocks();
122
+ });
123
+ it("returns parsed PID from tmux list-panes output", () => {
124
+ mockExecSync.mockReturnValue("42000\n");
125
+ const pid = getTmuxPanePid("agentmesh-test-agent");
126
+ expect(pid).toBe(42000);
127
+ });
128
+ it("returns null when tmux command fails", () => {
129
+ mockExecSync.mockImplementation(() => {
130
+ throw new Error("no server running");
131
+ });
132
+ const pid = getTmuxPanePid("agentmesh-nonexistent");
133
+ expect(pid).toBeNull();
134
+ });
135
+ it("returns null when output is not a valid number", () => {
136
+ mockExecSync.mockReturnValue("not-a-pid\n");
137
+ const pid = getTmuxPanePid("agentmesh-test-agent");
138
+ expect(pid).toBeNull();
139
+ });
140
+ });
141
+ // ─── getProcessTree ────────────────────────────────────────────────────────────
142
+ describe("getProcessTree", () => {
143
+ beforeEach(() => {
144
+ vi.clearAllMocks();
145
+ });
146
+ it("returns direct children of a process", () => {
147
+ mockExecSync.mockImplementation((cmd) => {
148
+ const c = cmd;
149
+ if (c.includes("pgrep -P 1000"))
150
+ return "1001\n1002\n";
151
+ return "";
152
+ });
153
+ const tree = getProcessTree(1000);
154
+ expect(tree).toContain(1001);
155
+ expect(tree).toContain(1002);
156
+ });
157
+ it("recursively includes grandchildren", () => {
158
+ mockExecSync.mockImplementation((cmd) => {
159
+ const c = cmd;
160
+ if (c.includes("pgrep -P 1000"))
161
+ return "1001\n";
162
+ if (c.includes("pgrep -P 1001"))
163
+ return "1002\n";
164
+ return "";
165
+ });
166
+ const tree = getProcessTree(1000);
167
+ expect(tree).toContain(1001);
168
+ expect(tree).toContain(1002);
169
+ });
170
+ it("returns empty array when process has no children", () => {
171
+ mockExecSync.mockReturnValue("");
172
+ const tree = getProcessTree(9999);
173
+ expect(tree).toEqual([]);
174
+ });
175
+ it("returns empty array when pgrep fails", () => {
176
+ mockExecSync.mockImplementation(() => {
177
+ throw new Error("pgrep: no matching processes");
178
+ });
179
+ const tree = getProcessTree(9999);
180
+ expect(tree).toEqual([]);
181
+ });
182
+ });
183
+ // ─── captureAgentChildPids ─────────────────────────────────────────────────────
184
+ describe("captureAgentChildPids", () => {
185
+ beforeEach(() => {
186
+ vi.clearAllMocks();
187
+ });
188
+ it("returns empty array when tmux session has no pane PID", () => {
189
+ mockExecSync.mockImplementation(() => {
190
+ throw new Error("no server");
191
+ });
192
+ const pids = captureAgentChildPids("test-agent");
193
+ expect(pids).toEqual([]);
194
+ });
195
+ it("returns pane PID and all descendants", () => {
196
+ mockExecSync.mockImplementation((cmd) => {
197
+ const c = cmd;
198
+ if (c.includes("list-panes"))
199
+ return "5000\n";
200
+ if (c.includes("pgrep -P 5000"))
201
+ return "5001\n5002\n";
202
+ return "";
203
+ });
204
+ const pids = captureAgentChildPids("test-agent");
205
+ expect(pids).toContain(5000);
206
+ expect(pids).toContain(5001);
207
+ expect(pids).toContain(5002);
208
+ });
209
+ it("deduplicates PIDs", () => {
210
+ mockExecSync.mockImplementation((cmd) => {
211
+ const c = cmd;
212
+ if (c.includes("list-panes"))
213
+ return "5000\n";
214
+ if (c.includes("pgrep -P 5000"))
215
+ return "5001\n5001\n"; // duplicate
216
+ return "";
217
+ });
218
+ const pids = captureAgentChildPids("test-agent");
219
+ const uniquePids = [...new Set(pids)];
220
+ expect(pids.length).toBe(uniquePids.length);
221
+ });
222
+ });
223
+ // ─── destroySession with childPids ────────────────────────────────────────────
224
+ describe("destroySession with childPids", () => {
225
+ let killSpy;
226
+ beforeEach(() => {
227
+ killSpy = mockProcessKill(() => true);
228
+ vi.clearAllMocks();
229
+ });
230
+ afterEach(() => {
231
+ killSpy.mockRestore();
232
+ });
233
+ it("kills child PIDs before killing the tmux session", () => {
234
+ const callOrder = [];
235
+ const terminatedPids = new Set();
236
+ killSpy.mockImplementation((_pid, sig) => {
237
+ if (sig === "SIGTERM") {
238
+ callOrder.push(`kill-${_pid}`);
239
+ terminatedPids.add(_pid);
240
+ return true;
241
+ }
242
+ if (sig === 0) {
243
+ if (terminatedPids.has(_pid))
244
+ throw new Error("dead");
245
+ return true;
246
+ }
247
+ return true;
248
+ });
249
+ mockExecSync.mockImplementation((cmd) => {
250
+ const c = cmd;
251
+ if (c.includes("has-session"))
252
+ return "";
253
+ if (c.includes("kill-session"))
254
+ callOrder.push("tmux-kill");
255
+ return "";
256
+ });
257
+ destroySession("test-agent", [1234, 5678]);
258
+ const childKillIndex = callOrder.findIndex((c) => c.startsWith("kill-"));
259
+ const tmuxKillIndex = callOrder.indexOf("tmux-kill");
260
+ expect(childKillIndex).toBeGreaterThanOrEqual(0);
261
+ expect(tmuxKillIndex).toBeGreaterThan(childKillIndex);
262
+ });
263
+ it("still destroys tmux session when no childPids provided", () => {
264
+ mockExecSync.mockImplementation((cmd) => {
265
+ const c = cmd;
266
+ if (c.includes("has-session"))
267
+ return "";
268
+ return "";
269
+ });
270
+ destroySession("test-agent");
271
+ expect(mockExecSync).toHaveBeenCalledWith(expect.stringContaining("kill-session"));
272
+ expect(killSpy).not.toHaveBeenCalled();
273
+ });
274
+ it("still destroys tmux session when childPids is empty array", () => {
275
+ mockExecSync.mockImplementation((cmd) => {
276
+ const c = cmd;
277
+ if (c.includes("has-session"))
278
+ return "";
279
+ return "";
280
+ });
281
+ destroySession("test-agent", []);
282
+ expect(mockExecSync).toHaveBeenCalledWith(expect.stringContaining("kill-session"));
283
+ expect(killSpy).not.toHaveBeenCalled();
284
+ });
285
+ });
286
+ //# sourceMappingURL=orphan-process.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orphan-process.test.js","sourceRoot":"","sources":["../../src/__tests__/orphan-process.test.ts"],"names":[],"mappings":"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"}
@@ -12,6 +12,11 @@ describe("Runner Module", () => {
12
12
  expect(detectRunner("claude --some-flag")).toBe("claude");
13
13
  expect(detectRunner("CLAUDE")).toBe("claude");
14
14
  });
15
+ it("should detect codex runner", () => {
16
+ expect(detectRunner("codex")).toBe("codex");
17
+ expect(detectRunner("codex --some-flag")).toBe("codex");
18
+ expect(detectRunner("CODEX")).toBe("codex");
19
+ });
15
20
  it("should detect custom runner for unknown commands", () => {
16
21
  expect(detectRunner("aider")).toBe("custom");
17
22
  expect(detectRunner("cursor")).toBe("custom");
@@ -75,11 +80,22 @@ describe("Runner Module", () => {
75
80
  expect(config.model).toBe("gpt-4");
76
81
  expect(Object.keys(config.env)).toHaveLength(0);
77
82
  });
83
+ it("should build config for codex with CODEX_MODEL env", () => {
84
+ const config = buildRunnerConfig({
85
+ cliModel: "openai/gpt-5-codex",
86
+ defaultModel: "anthropic/claude-sonnet-4-5",
87
+ command: "codex",
88
+ });
89
+ expect(config.type).toBe("codex");
90
+ expect(config.model).toBe("openai/gpt-5-codex");
91
+ expect(config.env.CODEX_MODEL).toBe("openai/gpt-5-codex");
92
+ });
78
93
  });
79
94
  describe("getRunnerDisplayName", () => {
80
95
  it("should return correct display names", () => {
81
96
  expect(getRunnerDisplayName("opencode")).toBe("OpenCode");
82
97
  expect(getRunnerDisplayName("claude")).toBe("Claude CLI");
98
+ expect(getRunnerDisplayName("codex")).toBe("Codex CLI");
83
99
  expect(getRunnerDisplayName("custom")).toBe("Custom");
84
100
  });
85
101
  });
@@ -1 +1 @@
1
- {"version":3,"file":"runner.test.js","sourceRoot":"","sources":["../../src/__tests__/runner.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runner.test.js","sourceRoot":"","sources":["../../src/__tests__/runner.test.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,10 @@
1
1
  import { execSync, spawnSync } from "node:child_process";
2
+ import fs from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
2
5
  import { beforeEach, describe, expect, it, vi } from "vitest";
3
6
  import * as tmux from "../core/tmux.js";
4
- import { checkAgentProgress, cleanupOrphanContainers, detectPermissionPrompt, findOrphanContainers, getLastActivityTime, isProcessRunning, sendNudge, } from "../core/watchdog.js";
7
+ import { checkAgentProgress, cleanupOrphanContainers, clearWaitingForHuman, detectPermissionPrompt, findOrphanContainers, getLastActivityTime, isProcessRunning, isWaitingForHuman, sendNudge, setWaitingForHuman, } from "../core/watchdog.js";
5
8
  // Mock child_process
6
9
  vi.mock("node:child_process", () => ({
7
10
  execSync: vi.fn(),
@@ -11,6 +14,19 @@ vi.mock("node:child_process", () => ({
11
14
  vi.mock("../core/tmux.js", () => ({
12
15
  captureSessionOutput: vi.fn(),
13
16
  }));
17
+ // Mock node:fs for waiting signal file tests
18
+ vi.mock("node:fs", () => ({
19
+ default: {
20
+ mkdirSync: vi.fn(),
21
+ writeFileSync: vi.fn(),
22
+ unlinkSync: vi.fn(),
23
+ readFileSync: vi.fn(),
24
+ },
25
+ mkdirSync: vi.fn(),
26
+ writeFileSync: vi.fn(),
27
+ unlinkSync: vi.fn(),
28
+ readFileSync: vi.fn(),
29
+ }));
14
30
  describe("Watchdog Module", () => {
15
31
  beforeEach(() => {
16
32
  vi.resetAllMocks();
@@ -48,20 +64,36 @@ describe("Watchdog Module", () => {
48
64
  const result = detectPermissionPrompt("test-agent");
49
65
  expect(result).toBeNull();
50
66
  });
67
+ it("should not treat standalone 'Reject' text as a permission prompt", () => {
68
+ vi.mocked(tmux.captureSessionOutput).mockReturnValue("Unit test failed\nReject this PR until CI is green");
69
+ const result = detectPermissionPrompt("test-agent");
70
+ expect(result).toBeNull();
71
+ });
51
72
  });
52
73
  describe("getLastActivityTime", () => {
53
74
  it("should parse timestamp from local log file", () => {
54
- vi.mocked(spawnSync).mockReturnValue({
55
- status: 0,
56
- stdout: "INFO 2026-02-26T00:14:42 +0ms service=opencode message=test",
57
- stderr: "",
58
- pid: 123,
59
- signal: null,
60
- output: [],
61
- });
62
- const result = getLastActivityTime("test-agent");
63
- expect(result).toBeInstanceOf(Date);
64
- expect(result?.toISOString()).toContain("2026-02-26");
75
+ const originalHome = process.env.HOME;
76
+ try {
77
+ process.env.HOME = "/tmp/agent-home";
78
+ vi.mocked(spawnSync).mockReturnValue({
79
+ status: 0,
80
+ stdout: "INFO 2026-02-26T00:14:42 +0ms service=opencode message=test",
81
+ stderr: "",
82
+ pid: 123,
83
+ signal: null,
84
+ output: [],
85
+ });
86
+ const result = getLastActivityTime("test-agent");
87
+ expect(result).toBeInstanceOf(Date);
88
+ expect(result?.toISOString()).toContain("2026-02-26");
89
+ expect(spawnSync).toHaveBeenCalledWith("sh", [
90
+ "-c",
91
+ "ls -t /tmp/agent-home/.agentmesh/opencode-data/test-agent/opencode/log/*.log 2>/dev/null | head -1 | xargs tail -1 2>/dev/null",
92
+ ], { encoding: "utf-8", timeout: 5000 });
93
+ }
94
+ finally {
95
+ process.env.HOME = originalHome;
96
+ }
65
97
  });
66
98
  it("should parse timestamp from container log file", () => {
67
99
  vi.mocked(spawnSync).mockReturnValue({
@@ -286,5 +318,99 @@ describe("Watchdog Module", () => {
286
318
  expect(execSync).toHaveBeenCalledWith(expect.stringContaining('\\"quotes\\"'), expect.any(Object));
287
319
  });
288
320
  });
321
+ describe("waiting-for-human signal", () => {
322
+ const agentName = "test-agent";
323
+ const waitingFilePath = path.join(os.homedir(), ".agentmesh", "agents", `${agentName}.waiting`);
324
+ beforeEach(() => {
325
+ vi.resetAllMocks();
326
+ });
327
+ describe("setWaitingForHuman", () => {
328
+ it("should create the agents directory and write the signal file", () => {
329
+ setWaitingForHuman(agentName, "Waiting for approval");
330
+ expect(fs.mkdirSync).toHaveBeenCalledWith(path.join(os.homedir(), ".agentmesh", "agents"), {
331
+ recursive: true,
332
+ });
333
+ expect(fs.writeFileSync).toHaveBeenCalledWith(waitingFilePath, expect.stringContaining("Waiting for approval"), { mode: 0o600 });
334
+ });
335
+ it("should use default reason when none provided", () => {
336
+ setWaitingForHuman(agentName);
337
+ expect(fs.writeFileSync).toHaveBeenCalledWith(waitingFilePath, expect.stringContaining("Waiting for human input"), { mode: 0o600 });
338
+ });
339
+ it("should write valid JSON with reason and since fields", () => {
340
+ setWaitingForHuman(agentName, "Test reason");
341
+ const writeCall = vi.mocked(fs.writeFileSync).mock.calls[0];
342
+ const written = JSON.parse(writeCall[1]);
343
+ expect(written).toHaveProperty("reason", "Test reason");
344
+ expect(written).toHaveProperty("since");
345
+ expect(() => new Date(written.since)).not.toThrow();
346
+ });
347
+ });
348
+ describe("clearWaitingForHuman", () => {
349
+ it("should delete the signal file", () => {
350
+ clearWaitingForHuman(agentName);
351
+ expect(fs.unlinkSync).toHaveBeenCalledWith(waitingFilePath);
352
+ });
353
+ it("should not throw when the signal file does not exist", () => {
354
+ vi.mocked(fs.unlinkSync).mockImplementation(() => {
355
+ throw new Error("ENOENT: no such file");
356
+ });
357
+ expect(() => clearWaitingForHuman(agentName)).not.toThrow();
358
+ });
359
+ });
360
+ describe("isWaitingForHuman", () => {
361
+ it("should return waiting=true with reason and since when signal file exists", () => {
362
+ const payload = { reason: "Awaiting handoff reply", since: "2026-02-28T10:00:00.000Z" };
363
+ vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify(payload));
364
+ const result = isWaitingForHuman(agentName);
365
+ expect(result.waiting).toBe(true);
366
+ expect(result.reason).toBe("Awaiting handoff reply");
367
+ expect(result.since).toBe("2026-02-28T10:00:00.000Z");
368
+ });
369
+ it("should return waiting=false when signal file does not exist", () => {
370
+ vi.mocked(fs.readFileSync).mockImplementation(() => {
371
+ throw new Error("ENOENT: no such file");
372
+ });
373
+ const result = isWaitingForHuman(agentName);
374
+ expect(result.waiting).toBe(false);
375
+ });
376
+ it("should return waiting=false on malformed JSON", () => {
377
+ vi.mocked(fs.readFileSync).mockReturnValue("not-json");
378
+ const result = isWaitingForHuman(agentName);
379
+ expect(result.waiting).toBe(false);
380
+ });
381
+ });
382
+ describe("checkAgentProgress with waiting_for_human", () => {
383
+ it("should return waiting_for_human when signal file exists, before any other check", () => {
384
+ const payload = {
385
+ reason: "Waiting for Terraform decision",
386
+ since: "2026-02-28T10:00:00.000Z",
387
+ };
388
+ vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify(payload));
389
+ // Even with a permission prompt visible in tmux, waiting_for_human takes priority
390
+ vi.mocked(tmux.captureSessionOutput).mockReturnValue("Permission required\nAllow once");
391
+ const result = checkAgentProgress(agentName);
392
+ expect(result.status).toBe("waiting_for_human");
393
+ expect(result.details).toBe("Waiting for Terraform decision");
394
+ });
395
+ it("should fall through to normal checks when no signal file", () => {
396
+ vi.mocked(fs.readFileSync).mockImplementation(() => {
397
+ throw new Error("ENOENT");
398
+ });
399
+ vi.mocked(tmux.captureSessionOutput).mockReturnValue("Normal output");
400
+ // Mock recent activity so it returns active
401
+ const recentTime = new Date(Date.now() - 30 * 1000);
402
+ vi.mocked(spawnSync).mockReturnValue({
403
+ status: 0,
404
+ stdout: `INFO ${recentTime.toISOString().slice(0, 19)} +0ms service=opencode`,
405
+ stderr: "",
406
+ pid: 123,
407
+ signal: null,
408
+ output: [],
409
+ });
410
+ const result = checkAgentProgress(agentName);
411
+ expect(result.status).toBe("active");
412
+ });
413
+ });
414
+ });
289
415
  });
290
416
  //# sourceMappingURL=watchdog.test.js.map