@ai-devkit/agent-manager 0.34.0 → 0.36.0

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 (211) hide show
  1. package/dist/AgentManager.d.ts +2 -0
  2. package/dist/AgentManager.d.ts.map +1 -1
  3. package/dist/AgentManager.js +25 -0
  4. package/dist/AgentManager.js.map +1 -1
  5. package/dist/__tests__/AgentManager.test.js +74 -0
  6. package/dist/__tests__/AgentManager.test.js.map +1 -1
  7. package/dist/__tests__/adapters/AntigravityCliAdapter.test.js +381 -0
  8. package/dist/__tests__/adapters/AntigravityCliAdapter.test.js.map +1 -0
  9. package/dist/__tests__/adapters/ClaudeCodeAdapter.test.js +28 -0
  10. package/dist/__tests__/adapters/ClaudeCodeAdapter.test.js.map +1 -1
  11. package/dist/__tests__/adapters/CodexAdapter.test.js +7 -0
  12. package/dist/__tests__/adapters/CodexAdapter.test.js.map +1 -1
  13. package/dist/__tests__/adapters/KiroAdapter.test.js +607 -0
  14. package/dist/__tests__/adapters/KiroAdapter.test.js.map +1 -0
  15. package/dist/__tests__/capacity/claude.test.js +485 -0
  16. package/dist/__tests__/capacity/claude.test.js.map +1 -0
  17. package/dist/__tests__/capacity/openai.test.js +672 -0
  18. package/dist/__tests__/capacity/openai.test.js.map +1 -0
  19. package/dist/__tests__/capacity/wham.test.js +144 -0
  20. package/dist/__tests__/capacity/wham.test.js.map +1 -0
  21. package/dist/__tests__/providers/copilot/CopilotAdapter.test.js +18 -0
  22. package/dist/__tests__/providers/copilot/CopilotAdapter.test.js.map +1 -1
  23. package/dist/__tests__/providers/gemini/GeminiCliAdapter.test.js +36 -0
  24. package/dist/__tests__/providers/gemini/GeminiCliAdapter.test.js.map +1 -1
  25. package/dist/__tests__/providers/grok/GrokAgentMapper.test.js +56 -0
  26. package/dist/__tests__/providers/grok/GrokAgentMapper.test.js.map +1 -0
  27. package/dist/__tests__/{adapters → providers/grok}/GrokCliAdapter.test.js +19 -6
  28. package/dist/__tests__/providers/grok/GrokCliAdapter.test.js.map +1 -0
  29. package/dist/__tests__/providers/grok/GrokSessionLocator.test.js +133 -0
  30. package/dist/__tests__/providers/grok/GrokSessionLocator.test.js.map +1 -0
  31. package/dist/__tests__/providers/opencode/OpenCodeAdapter.test.js +24 -0
  32. package/dist/__tests__/providers/opencode/OpenCodeAdapter.test.js.map +1 -1
  33. package/dist/__tests__/providers/pi/PiAdapter.test.js +30 -0
  34. package/dist/__tests__/providers/pi/PiAdapter.test.js.map +1 -1
  35. package/dist/__tests__/utils/agents.test.js +6 -0
  36. package/dist/__tests__/utils/agents.test.js.map +1 -1
  37. package/dist/__tests__/utils/session.test.js +18 -1
  38. package/dist/__tests__/utils/session.test.js.map +1 -1
  39. package/dist/adapters/AgentAdapter.d.ts +9 -1
  40. package/dist/adapters/AgentAdapter.d.ts.map +1 -1
  41. package/dist/adapters/AgentAdapter.js.map +1 -1
  42. package/dist/adapters/AntigravityCliAdapter.d.ts +57 -0
  43. package/dist/adapters/AntigravityCliAdapter.d.ts.map +1 -0
  44. package/dist/adapters/AntigravityCliAdapter.js +273 -0
  45. package/dist/adapters/AntigravityCliAdapter.js.map +1 -0
  46. package/dist/adapters/KiroAdapter.d.ts +46 -0
  47. package/dist/adapters/KiroAdapter.d.ts.map +1 -0
  48. package/dist/adapters/KiroAdapter.js +344 -0
  49. package/dist/adapters/KiroAdapter.js.map +1 -0
  50. package/dist/adapters/index.d.ts +3 -1
  51. package/dist/adapters/index.d.ts.map +1 -1
  52. package/dist/adapters/index.js +3 -1
  53. package/dist/adapters/index.js.map +1 -1
  54. package/dist/capacity/claude.d.ts +17 -0
  55. package/dist/capacity/claude.d.ts.map +1 -0
  56. package/dist/capacity/claude.js +271 -0
  57. package/dist/capacity/claude.js.map +1 -0
  58. package/dist/capacity/codex.d.ts +1 -2
  59. package/dist/capacity/codex.d.ts.map +1 -1
  60. package/dist/capacity/codex.js +5 -47
  61. package/dist/capacity/codex.js.map +1 -1
  62. package/dist/capacity/index.d.ts +11 -0
  63. package/dist/capacity/index.d.ts.map +1 -1
  64. package/dist/capacity/index.js +17 -0
  65. package/dist/capacity/index.js.map +1 -1
  66. package/dist/capacity/openai.d.ts +48 -0
  67. package/dist/capacity/openai.d.ts.map +1 -0
  68. package/dist/capacity/openai.js +312 -0
  69. package/dist/capacity/openai.js.map +1 -0
  70. package/dist/capacity/wham.d.ts +16 -0
  71. package/dist/capacity/wham.d.ts.map +1 -0
  72. package/dist/capacity/wham.js +66 -0
  73. package/dist/capacity/wham.js.map +1 -0
  74. package/dist/index.d.ts +5 -3
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +4 -2
  77. package/dist/index.js.map +1 -1
  78. package/dist/providers/claude/ClaudeCodeAdapter.d.ts +2 -0
  79. package/dist/providers/claude/ClaudeCodeAdapter.d.ts.map +1 -1
  80. package/dist/providers/claude/ClaudeCodeAdapter.js +22 -19
  81. package/dist/providers/claude/ClaudeCodeAdapter.js.map +1 -1
  82. package/dist/providers/claude/ClaudeSessionLocator.d.ts +4 -0
  83. package/dist/providers/claude/ClaudeSessionLocator.d.ts.map +1 -1
  84. package/dist/providers/claude/ClaudeSessionLocator.js +18 -1
  85. package/dist/providers/claude/ClaudeSessionLocator.js.map +1 -1
  86. package/dist/providers/codex/CodexAdapter.d.ts +1 -0
  87. package/dist/providers/codex/CodexAdapter.d.ts.map +1 -1
  88. package/dist/providers/codex/CodexAdapter.js +8 -0
  89. package/dist/providers/codex/CodexAdapter.js.map +1 -1
  90. package/dist/providers/copilot/CopilotAdapter.d.ts +2 -0
  91. package/dist/providers/copilot/CopilotAdapter.d.ts.map +1 -1
  92. package/dist/providers/copilot/CopilotAdapter.js +24 -9
  93. package/dist/providers/copilot/CopilotAdapter.js.map +1 -1
  94. package/dist/providers/copilot/CopilotSessionLocator.d.ts +1 -0
  95. package/dist/providers/copilot/CopilotSessionLocator.d.ts.map +1 -1
  96. package/dist/providers/copilot/CopilotSessionLocator.js +9 -1
  97. package/dist/providers/copilot/CopilotSessionLocator.js.map +1 -1
  98. package/dist/providers/gemini/GeminiCliAdapter.d.ts +1 -0
  99. package/dist/providers/gemini/GeminiCliAdapter.d.ts.map +1 -1
  100. package/dist/providers/gemini/GeminiCliAdapter.js +9 -0
  101. package/dist/providers/gemini/GeminiCliAdapter.js.map +1 -1
  102. package/dist/providers/grok/GrokAgentMapper.d.ts +14 -0
  103. package/dist/providers/grok/GrokAgentMapper.d.ts.map +1 -0
  104. package/dist/providers/grok/GrokAgentMapper.js +36 -0
  105. package/dist/providers/grok/GrokAgentMapper.js.map +1 -0
  106. package/dist/providers/grok/GrokCliAdapter.d.ts +36 -0
  107. package/dist/providers/grok/GrokCliAdapter.d.ts.map +1 -0
  108. package/dist/providers/grok/GrokCliAdapter.js +98 -0
  109. package/dist/providers/grok/GrokCliAdapter.js.map +1 -0
  110. package/dist/providers/grok/GrokSessionLocator.d.ts +62 -0
  111. package/dist/providers/grok/GrokSessionLocator.d.ts.map +1 -0
  112. package/dist/providers/grok/GrokSessionLocator.js +140 -0
  113. package/dist/providers/grok/GrokSessionLocator.js.map +1 -0
  114. package/dist/providers/grok/GrokSessionParser.d.ts +54 -0
  115. package/dist/providers/grok/GrokSessionParser.d.ts.map +1 -0
  116. package/dist/providers/grok/GrokSessionParser.js +122 -0
  117. package/dist/providers/grok/GrokSessionParser.js.map +1 -0
  118. package/dist/providers/opencode/OpenCodeAdapter.d.ts +1 -0
  119. package/dist/providers/opencode/OpenCodeAdapter.d.ts.map +1 -1
  120. package/dist/providers/opencode/OpenCodeAdapter.js +3 -0
  121. package/dist/providers/opencode/OpenCodeAdapter.js.map +1 -1
  122. package/dist/providers/opencode/OpenCodeSessionLocator.d.ts +2 -0
  123. package/dist/providers/opencode/OpenCodeSessionLocator.d.ts.map +1 -1
  124. package/dist/providers/opencode/OpenCodeSessionLocator.js +31 -12
  125. package/dist/providers/opencode/OpenCodeSessionLocator.js.map +1 -1
  126. package/dist/providers/pi/PiAdapter.d.ts +1 -0
  127. package/dist/providers/pi/PiAdapter.d.ts.map +1 -1
  128. package/dist/providers/pi/PiAdapter.js +10 -0
  129. package/dist/providers/pi/PiAdapter.js.map +1 -1
  130. package/dist/providers/pi/PiSessionLocator.d.ts +1 -0
  131. package/dist/providers/pi/PiSessionLocator.d.ts.map +1 -1
  132. package/dist/providers/pi/PiSessionLocator.js +8 -1
  133. package/dist/providers/pi/PiSessionLocator.js.map +1 -1
  134. package/dist/readiness/AgentReadiness.d.ts +1 -1
  135. package/dist/readiness/AgentReadiness.d.ts.map +1 -1
  136. package/dist/readiness/AgentReadiness.js.map +1 -1
  137. package/dist/runtime/ManagedAgentRuntime.js +3 -1
  138. package/dist/runtime/ManagedAgentRuntime.js.map +1 -1
  139. package/dist/runtime/herdr/HerdrAgentDiscovery.js +10 -0
  140. package/dist/runtime/herdr/HerdrAgentDiscovery.js.map +1 -1
  141. package/dist/utils/agents.d.ts +1 -1
  142. package/dist/utils/agents.d.ts.map +1 -1
  143. package/dist/utils/agents.js +11 -0
  144. package/dist/utils/agents.js.map +1 -1
  145. package/dist/utils/session.d.ts +2 -0
  146. package/dist/utils/session.d.ts.map +1 -1
  147. package/dist/utils/session.js +3 -0
  148. package/dist/utils/session.js.map +1 -1
  149. package/package.json +1 -1
  150. package/src/AgentManager.ts +34 -0
  151. package/src/__tests__/AgentManager.test.ts +60 -0
  152. package/src/__tests__/adapters/AntigravityCliAdapter.test.ts +324 -0
  153. package/src/__tests__/adapters/ClaudeCodeAdapter.test.ts +23 -0
  154. package/src/__tests__/adapters/CodexAdapter.test.ts +7 -0
  155. package/src/__tests__/adapters/KiroAdapter.test.ts +534 -0
  156. package/src/__tests__/capacity/claude.test.ts +467 -0
  157. package/src/__tests__/capacity/fixtures/claude-credentials.json +7 -0
  158. package/src/__tests__/capacity/fixtures/claude-usage-malformed.json +1 -0
  159. package/src/__tests__/capacity/fixtures/claude-usage-partial.json +15 -0
  160. package/src/__tests__/capacity/fixtures/claude-usage.json +63 -0
  161. package/src/__tests__/capacity/fixtures/openai-auth.json +6 -0
  162. package/src/__tests__/capacity/fixtures/openai-codex-auth.json +9 -0
  163. package/src/__tests__/capacity/fixtures/openai-usage-completions.json +67 -0
  164. package/src/__tests__/capacity/fixtures/openai-usage-responses.json +20 -0
  165. package/src/__tests__/capacity/fixtures/wham-usage.json +32 -0
  166. package/src/__tests__/capacity/openai.test.ts +570 -0
  167. package/src/__tests__/capacity/wham.test.ts +104 -0
  168. package/src/__tests__/providers/copilot/CopilotAdapter.test.ts +12 -0
  169. package/src/__tests__/providers/gemini/GeminiCliAdapter.test.ts +21 -0
  170. package/src/__tests__/providers/grok/GrokAgentMapper.test.ts +57 -0
  171. package/src/__tests__/{adapters → providers/grok}/GrokCliAdapter.test.ts +18 -9
  172. package/src/__tests__/providers/grok/GrokSessionLocator.test.ts +106 -0
  173. package/src/__tests__/providers/opencode/OpenCodeAdapter.test.ts +18 -0
  174. package/src/__tests__/providers/pi/PiAdapter.test.ts +15 -0
  175. package/src/__tests__/utils/agents.test.ts +7 -0
  176. package/src/__tests__/utils/session.test.ts +11 -1
  177. package/src/adapters/AgentAdapter.ts +11 -0
  178. package/src/adapters/AntigravityCliAdapter.ts +356 -0
  179. package/src/adapters/KiroAdapter.ts +456 -0
  180. package/src/adapters/index.ts +3 -1
  181. package/src/capacity/claude.ts +313 -0
  182. package/src/capacity/codex.ts +25 -134
  183. package/src/capacity/index.ts +33 -4
  184. package/src/capacity/openai.ts +361 -0
  185. package/src/capacity/wham.ts +84 -0
  186. package/src/index.ts +11 -2
  187. package/src/providers/claude/ClaudeCodeAdapter.ts +28 -23
  188. package/src/providers/claude/ClaudeSessionLocator.ts +20 -0
  189. package/src/providers/codex/CodexAdapter.ts +8 -0
  190. package/src/providers/copilot/CopilotAdapter.ts +26 -10
  191. package/src/providers/copilot/CopilotSessionLocator.ts +7 -1
  192. package/src/providers/gemini/GeminiCliAdapter.ts +8 -0
  193. package/src/providers/grok/GrokAgentMapper.ts +41 -0
  194. package/src/providers/grok/GrokCliAdapter.ts +125 -0
  195. package/src/providers/grok/GrokSessionLocator.ts +179 -0
  196. package/src/providers/grok/GrokSessionParser.ts +166 -0
  197. package/src/providers/opencode/OpenCodeAdapter.ts +4 -0
  198. package/src/providers/opencode/OpenCodeSessionLocator.ts +43 -15
  199. package/src/providers/pi/PiAdapter.ts +12 -0
  200. package/src/providers/pi/PiSessionLocator.ts +16 -1
  201. package/src/readiness/AgentReadiness.ts +4 -1
  202. package/src/runtime/ManagedAgentRuntime.ts +2 -0
  203. package/src/runtime/herdr/HerdrAgentDiscovery.ts +5 -1
  204. package/src/utils/agents.ts +11 -1
  205. package/src/utils/session.ts +11 -0
  206. package/dist/__tests__/adapters/GrokCliAdapter.test.js.map +0 -1
  207. package/dist/adapters/GrokCliAdapter.d.ts +0 -80
  208. package/dist/adapters/GrokCliAdapter.d.ts.map +0 -1
  209. package/dist/adapters/GrokCliAdapter.js +0 -310
  210. package/dist/adapters/GrokCliAdapter.js.map +0 -1
  211. package/src/adapters/GrokCliAdapter.ts +0 -408
@@ -0,0 +1,534 @@
1
+ /**
2
+ * Tests for KiroAdapter
3
+ */
4
+
5
+ import type { MockedFunction } from "vitest";
6
+ import * as fs from "fs";
7
+ import * as os from "os";
8
+ import * as path from "path";
9
+
10
+ import { KiroAdapter } from "../../adapters/KiroAdapter.js";
11
+ import type { ProcessInfo } from "../../adapters/AgentAdapter.js";
12
+ import { AgentStatus } from "../../adapters/AgentAdapter.js";
13
+ import {
14
+ listAgentProcesses,
15
+ enrichProcesses,
16
+ captureProcessSnapshot,
17
+ } from "../../utils/process.js";
18
+ import { generateAgentName } from "../../utils/matching.js";
19
+
20
+ vi.mock("../../utils/process.js", async (importOriginal) => {
21
+ const actual = (await importOriginal()) as typeof import("../../utils/process.js");
22
+ return {
23
+ ...actual,
24
+ listAgentProcesses: vi.fn(),
25
+ enrichProcesses: vi.fn(),
26
+ captureProcessSnapshot: vi.fn(),
27
+ };
28
+ });
29
+
30
+ vi.mock("../../utils/matching.js", async (importOriginal) => {
31
+ const actual = (await importOriginal()) as typeof import("../../utils/matching.js");
32
+ return {
33
+ ...actual,
34
+ generateAgentName: vi.fn(),
35
+ };
36
+ });
37
+
38
+ const mockedListAgentProcesses = listAgentProcesses as MockedFunction<typeof listAgentProcesses>;
39
+ const mockedEnrichProcesses = enrichProcesses as MockedFunction<typeof enrichProcesses>;
40
+ const mockedCaptureProcessSnapshot = captureProcessSnapshot as MockedFunction<
41
+ typeof captureProcessSnapshot
42
+ >;
43
+ const mockedGenerateAgentName = generateAgentName as MockedFunction<typeof generateAgentName>;
44
+
45
+ describe("KiroAdapter", () => {
46
+ let adapter: KiroAdapter;
47
+ let tmpHome: string;
48
+ let sessionsDir: string;
49
+
50
+ beforeEach(() => {
51
+ tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), "kiro-adapter-test-"));
52
+ process.env.HOME = tmpHome;
53
+ sessionsDir = path.join(tmpHome, ".kiro", "sessions", "cli");
54
+ fs.mkdirSync(sessionsDir, { recursive: true });
55
+
56
+ adapter = new KiroAdapter();
57
+
58
+ mockedListAgentProcesses.mockReset();
59
+ mockedEnrichProcesses.mockReset();
60
+ mockedCaptureProcessSnapshot.mockReset();
61
+ mockedGenerateAgentName.mockReset();
62
+
63
+ mockedEnrichProcesses.mockImplementation((procs) => procs);
64
+ // Compatibility shim for standalone adapter discovery; the manager captures once and slices by name.
65
+ mockedCaptureProcessSnapshot.mockImplementation(async (names) =>
66
+ enrichProcesses(names.flatMap((name) => listAgentProcesses(name))),
67
+ );
68
+ mockedGenerateAgentName.mockImplementation((cwd: string, pid: number) => {
69
+ const folder = path.basename(cwd) || "unknown";
70
+ return `${folder} (${pid})`;
71
+ });
72
+ });
73
+
74
+ afterEach(() => {
75
+ fs.rmSync(tmpHome, { recursive: true, force: true });
76
+ });
77
+
78
+ describe("initialization", () => {
79
+ it("exposes the kiro type and process names", () => {
80
+ expect(adapter.type).toBe("kiro");
81
+ expect(adapter.processNames).toEqual(["kiro-cli", "kiro", "kiro-cli-chat", "node"]);
82
+ });
83
+ });
84
+
85
+ describe("canHandle", () => {
86
+ it("identifies Kiro commands without matching unrelated paths", () => {
87
+ expect(adapter.canHandle({ pid: 1, command: "kiro-cli", cwd: "/repo", tty: "ttys001" })).toBe(
88
+ true,
89
+ );
90
+ expect(
91
+ adapter.canHandle({
92
+ pid: 2,
93
+ command: "/usr/local/bin/kiro --model x",
94
+ cwd: "/repo",
95
+ tty: "ttys002",
96
+ }),
97
+ ).toBe(true);
98
+ expect(
99
+ adapter.canHandle({
100
+ pid: 3,
101
+ command: "node /opt/kiro/bin/kiro-cli.js",
102
+ cwd: "/repo",
103
+ tty: "ttys003",
104
+ }),
105
+ ).toBe(true);
106
+ expect(
107
+ adapter.canHandle({
108
+ pid: 4,
109
+ command: "node /repo/feature-kiro-adapter/script.js",
110
+ cwd: "/repo",
111
+ tty: "ttys004",
112
+ }),
113
+ ).toBe(false);
114
+ expect(
115
+ adapter.canHandle({
116
+ pid: 5,
117
+ command: "kiro-cli chat",
118
+ cwd: "/repo",
119
+ tty: "ttys005",
120
+ }),
121
+ ).toBe(true);
122
+ expect(
123
+ adapter.canHandle({
124
+ pid: 6,
125
+ command: "bun /opt/kiro/bin/kiro-cli.js",
126
+ cwd: "/repo",
127
+ tty: "ttys006",
128
+ }),
129
+ ).toBe(true);
130
+ expect(
131
+ adapter.canHandle({
132
+ pid: 7,
133
+ command: "node /usr/local/bin/ai-devkit agent start --type kiro",
134
+ cwd: "/repo",
135
+ tty: "ttys007",
136
+ }),
137
+ ).toBe(false);
138
+ expect(
139
+ adapter.canHandle({
140
+ pid: 8,
141
+ command: "node server.js --name kiro",
142
+ cwd: "/repo",
143
+ tty: "ttys008",
144
+ }),
145
+ ).toBe(false);
146
+ expect(
147
+ adapter.canHandle({
148
+ pid: 9,
149
+ command: "kiro-cli-chat acp",
150
+ cwd: "/repo",
151
+ tty: "ttys009",
152
+ }),
153
+ ).toBe(false);
154
+ });
155
+ });
156
+
157
+ describe("detectAgents", () => {
158
+ it("returns [] when there are no Kiro processes", async () => {
159
+ mockedListAgentProcesses.mockReturnValue([]);
160
+ expect(await adapter.detectAgents()).toEqual([]);
161
+ });
162
+
163
+ it("maps a running Kiro process through its session lock and metadata", async () => {
164
+ const cwd = "/repo/project-a";
165
+ const proc = makeProcess({ pid: 101, cwd: "/process/cwd" });
166
+ const updatedAt = new Date().toISOString();
167
+ const sessionFile = writeKiroSession(
168
+ "sess-101",
169
+ cwd,
170
+ [prompt("implement Kiro adapter", 1781098057), assistantText("working on it")],
171
+ 101,
172
+ updatedAt,
173
+ );
174
+ mockedCaptureProcessSnapshot.mockResolvedValue([proc]);
175
+
176
+ const agents = await adapter.detectAgents();
177
+
178
+ expect(agents).toHaveLength(1);
179
+ expect(agents[0]).toMatchObject({
180
+ type: "kiro",
181
+ pid: 101,
182
+ projectPath: cwd,
183
+ sessionId: "sess-101",
184
+ summary: "implement Kiro adapter",
185
+ status: AgentStatus.WAITING,
186
+ sessionFilePath: sessionFile,
187
+ lastActive: new Date(updatedAt),
188
+ });
189
+ });
190
+
191
+ it("matches a lock held by a kiro-cli-chat acp descendant when bun is not collected", async () => {
192
+ const cwd = "/repo/project-a";
193
+ const sessionFile = writeKiroSession(
194
+ "sess-acp",
195
+ cwd,
196
+ [prompt("implement Kiro adapter", 1781098057), assistantText("working on it")],
197
+ 55236,
198
+ );
199
+ const kiro = makeProcess({ pid: 55111, command: "kiro-cli", cwd: "/process/cwd", ppid: 1 });
200
+ const chat = makeProcess({
201
+ pid: 55168,
202
+ command: "kiro-cli-chat chat",
203
+ ppid: 55111,
204
+ tty: "ttys001",
205
+ });
206
+ const acp = makeProcess({
207
+ pid: 55236,
208
+ command: "kiro-cli-chat acp",
209
+ ppid: 55198,
210
+ tty: "ttys001",
211
+ });
212
+ mockedCaptureProcessSnapshot.mockResolvedValue([kiro, chat, acp]);
213
+
214
+ const agents = await adapter.detectAgents();
215
+
216
+ expect(agents).toEqual([
217
+ expect.objectContaining({
218
+ type: "kiro",
219
+ pid: 55111,
220
+ projectPath: cwd,
221
+ sessionId: "sess-acp",
222
+ summary: "implement Kiro adapter",
223
+ sessionFilePath: sessionFile,
224
+ }),
225
+ ]);
226
+ });
227
+
228
+ it("walks a collected kiro-cli-chat parent chain to the owning kiro-cli", async () => {
229
+ const sessionFile = writeKiroSession(
230
+ "sess-owner",
231
+ "/repo/owner",
232
+ [prompt("owned session", 1781098057)],
233
+ 55236,
234
+ );
235
+ const owner = makeProcess({ pid: 55111, command: "kiro-cli", cwd: "/repo/owner", ppid: 1 });
236
+ const other = makeProcess({ pid: 66111, command: "kiro-cli", cwd: "/repo/other", ppid: 1 });
237
+ const chat = makeProcess({ pid: 55168, command: "kiro-cli-chat chat", ppid: 55111 });
238
+ const acp = makeProcess({ pid: 55236, command: "kiro-cli-chat acp", ppid: 55168 });
239
+ mockedCaptureProcessSnapshot.mockResolvedValue([owner, other, chat, acp]);
240
+
241
+ const agents = await adapter.detectAgents();
242
+
243
+ expect(agents).toEqual([
244
+ expect.objectContaining({
245
+ pid: 55111,
246
+ projectPath: "/repo/owner",
247
+ sessionId: "sess-owner",
248
+ sessionFilePath: sessionFile,
249
+ }),
250
+ expect.objectContaining({ pid: 66111, sessionId: "pid-66111" }),
251
+ ]);
252
+ });
253
+
254
+ it("does not attach a session through tty ?? or an ambiguous tty", async () => {
255
+ writeKiroSession("sess-unknown-tty", "/repo/unknown", [prompt("hello", 1781098057)], 55236);
256
+ const unknownTty = makeProcess({ pid: 55111, command: "kiro-cli", tty: "??", ppid: 1 });
257
+ const unknownAcp = makeProcess({
258
+ pid: 55236,
259
+ command: "kiro-cli-chat acp",
260
+ tty: "??",
261
+ ppid: 55198,
262
+ });
263
+ mockedCaptureProcessSnapshot.mockResolvedValue([unknownTty, unknownAcp]);
264
+
265
+ expect(await adapter.detectAgents()).toEqual([
266
+ expect.objectContaining({ pid: 55111, sessionId: "pid-55111" }),
267
+ ]);
268
+
269
+ writeKiroSession("sess-shared-tty", "/repo/shared", [prompt("hello", 1781098057)], 66236);
270
+ const first = makeProcess({ pid: 55111, command: "kiro-cli", tty: "ttys001", ppid: 1 });
271
+ const second = makeProcess({ pid: 66111, command: "kiro-cli", tty: "ttys001", ppid: 1 });
272
+ const sharedAcp = makeProcess({
273
+ pid: 66236,
274
+ command: "kiro-cli-chat acp",
275
+ tty: "ttys001",
276
+ ppid: 55198,
277
+ });
278
+ mockedCaptureProcessSnapshot.mockResolvedValue([first, second, sharedAcp]);
279
+
280
+ expect(await adapter.detectAgents()).toEqual([
281
+ expect.objectContaining({ pid: 55111, sessionId: "pid-55111" }),
282
+ expect.objectContaining({ pid: 66111, sessionId: "pid-66111" }),
283
+ ]);
284
+ });
285
+
286
+ it("uses a supplied process snapshot instead of scanning again", async () => {
287
+ const proc = makeProcess({ pid: 606, cwd: "/repo/context" });
288
+ mockedCaptureProcessSnapshot.mockResolvedValue([makeProcess({ pid: 999 })]);
289
+
290
+ const agents = await adapter.detectAgents({ processes: [proc] });
291
+
292
+ expect(mockedCaptureProcessSnapshot).not.toHaveBeenCalled();
293
+ expect(agents).toEqual([
294
+ expect.objectContaining({
295
+ pid: 606,
296
+ projectPath: "/repo/context",
297
+ sessionId: "pid-606",
298
+ }),
299
+ ]);
300
+ });
301
+
302
+ it("uses only a lock whose PID belongs to a running Kiro process", async () => {
303
+ writeKiroSession("ended-session", "/repo/ended", [prompt("old conversation", 1781098057)]);
304
+ writeKiroSession(
305
+ "other-process",
306
+ "/repo/other",
307
+ [prompt("other conversation", 1781098057)],
308
+ 999,
309
+ );
310
+ const proc = makeProcess({ pid: 202, cwd: "/repo/current" });
311
+ mockedCaptureProcessSnapshot.mockResolvedValue([proc]);
312
+
313
+ const agents = await adapter.detectAgents();
314
+
315
+ expect(agents).toEqual([
316
+ expect.objectContaining({
317
+ pid: 202,
318
+ projectPath: "/repo/current",
319
+ sessionId: "pid-202",
320
+ summary: "Kiro process running",
321
+ }),
322
+ ]);
323
+ });
324
+
325
+ it("ignores malformed lock files", async () => {
326
+ writeKiroSession("bad-lock", "/repo/project", [prompt("hello", 1781098057)]);
327
+ fs.writeFileSync(path.join(sessionsDir, "bad-lock.lock"), "{bad json");
328
+ const proc = makeProcess({ pid: 303, cwd: "/repo/project" });
329
+ mockedCaptureProcessSnapshot.mockResolvedValue([proc]);
330
+
331
+ const agents = await adapter.detectAgents();
332
+
333
+ expect(agents[0]).toMatchObject({ sessionId: "pid-303" });
334
+ });
335
+
336
+ it("reports running while the latest assistant event invokes a tool", async () => {
337
+ writeKiroSession(
338
+ "tool-session",
339
+ "/repo/project",
340
+ [
341
+ prompt("inspect the file", Math.floor(Date.now() / 1000)),
342
+ assistantTool("fs_read", { path: "/repo/project/file.ts" }),
343
+ ],
344
+ 404,
345
+ new Date().toISOString(),
346
+ );
347
+ const proc = makeProcess({ pid: 404, cwd: "/repo/project" });
348
+ mockedCaptureProcessSnapshot.mockResolvedValue([proc]);
349
+
350
+ const agents = await adapter.detectAgents();
351
+
352
+ expect(agents[0].status).toBe(AgentStatus.RUNNING);
353
+ });
354
+
355
+ it("returns a process-only agent when the locked transcript is missing", async () => {
356
+ fs.writeFileSync(path.join(sessionsDir, "missing.lock"), JSON.stringify({ pid: 505 }));
357
+ const proc = makeProcess({ pid: 505, cwd: "/repo/project-e" });
358
+ mockedCaptureProcessSnapshot.mockResolvedValue([proc]);
359
+
360
+ const agents = await adapter.detectAgents();
361
+
362
+ expect(agents).toEqual([
363
+ expect.objectContaining({
364
+ type: "kiro",
365
+ status: AgentStatus.RUNNING,
366
+ pid: 505,
367
+ projectPath: "/repo/project-e",
368
+ sessionId: "pid-505",
369
+ summary: "Kiro process running",
370
+ }),
371
+ ]);
372
+ });
373
+ });
374
+
375
+ describe("getConversation", () => {
376
+ it("reads real Kiro prompt and assistant message envelopes", () => {
377
+ const sessionFile = writeKiroSession("conversation", "/repo/project-f", [
378
+ prompt("hello kiro", 1781098057),
379
+ assistantText("Hello! How can I help?"),
380
+ "{not json",
381
+ ]);
382
+
383
+ expect(adapter.getConversation(sessionFile)).toEqual([
384
+ { role: "user", content: "hello kiro", timestamp: "2026-06-10T13:27:37.000Z" },
385
+ { role: "assistant", content: "Hello! How can I help?", timestamp: undefined },
386
+ ]);
387
+ });
388
+
389
+ it("includes Kiro tool use and results only in verbose conversation mode", () => {
390
+ const sessionFile = writeKiroSession("tools", "/repo/project-tools", [
391
+ prompt("read package.json", 1781098057),
392
+ assistantTool("fs_read", { path: "package.json" }),
393
+ toolResult("contents", "success"),
394
+ assistantText("Done."),
395
+ ]);
396
+
397
+ expect(adapter.getConversation(sessionFile)).toEqual([
398
+ { role: "user", content: "read package.json", timestamp: "2026-06-10T13:27:37.000Z" },
399
+ { role: "assistant", content: "Done.", timestamp: undefined },
400
+ ]);
401
+ expect(adapter.getConversation(sessionFile, { verbose: true })).toEqual([
402
+ { role: "user", content: "read package.json", timestamp: "2026-06-10T13:27:37.000Z" },
403
+ {
404
+ role: "assistant",
405
+ content: '[Tool: fs_read] {"path":"package.json"}',
406
+ timestamp: undefined,
407
+ },
408
+ { role: "system", content: "[Tool Result] contents", timestamp: undefined },
409
+ { role: "assistant", content: "Done.", timestamp: undefined },
410
+ ]);
411
+ });
412
+ });
413
+
414
+ describe("listSessions", () => {
415
+ it("lists historical sessions using metadata and applies cwd filtering", async () => {
416
+ const matchingCwd = "/repo/project-g";
417
+ const matchingSession = writeKiroSession("sess-g", matchingCwd, [
418
+ prompt("first matching message", 1781098057),
419
+ assistantText("response"),
420
+ ]);
421
+ writeKiroSession("sess-h", "/repo/project-h", [prompt("other message", 1781098057)]);
422
+
423
+ const sessions = await adapter.listSessions({ cwd: matchingCwd });
424
+
425
+ expect(sessions).toEqual([
426
+ expect.objectContaining({
427
+ type: "kiro",
428
+ sessionId: "sess-g",
429
+ cwd: matchingCwd,
430
+ firstUserMessage: "first matching message",
431
+ startedAt: new Date("2026-06-10T13:27:17.000Z"),
432
+ lastActive: new Date("2026-06-10T13:27:40.000Z"),
433
+ sessionFilePath: matchingSession,
434
+ }),
435
+ ]);
436
+ });
437
+ });
438
+
439
+ function makeProcess(overrides: Partial<ProcessInfo>): ProcessInfo {
440
+ return {
441
+ pid: 1,
442
+ command: "kiro-cli chat",
443
+ cwd: "/repo",
444
+ tty: "ttys001",
445
+ startTime: new Date("2026-06-10T13:27:17.000Z"),
446
+ ...overrides,
447
+ };
448
+ }
449
+
450
+ function writeKiroSession(
451
+ sessionId: string,
452
+ cwd: string,
453
+ entries: Array<Record<string, unknown> | string>,
454
+ pid?: number,
455
+ updatedAt = "2026-06-10T13:27:40.000Z",
456
+ ): string {
457
+ fs.writeFileSync(
458
+ path.join(sessionsDir, `${sessionId}.json`),
459
+ JSON.stringify({
460
+ session_id: sessionId,
461
+ cwd,
462
+ created_at: "2026-06-10T13:27:17.000Z",
463
+ updated_at: updatedAt,
464
+ title: "Session title",
465
+ }),
466
+ );
467
+ const filePath = path.join(sessionsDir, `${sessionId}.jsonl`);
468
+ fs.writeFileSync(
469
+ filePath,
470
+ entries
471
+ .map((entry) => (typeof entry === "string" ? entry : JSON.stringify(entry)))
472
+ .join("\n"),
473
+ );
474
+ if (pid !== undefined) {
475
+ fs.writeFileSync(
476
+ path.join(sessionsDir, `${sessionId}.lock`),
477
+ JSON.stringify({
478
+ pid,
479
+ started_at: "2026-06-10T13:27:17.000Z",
480
+ }),
481
+ );
482
+ }
483
+ return filePath;
484
+ }
485
+
486
+ function prompt(text: string, timestamp: number): Record<string, unknown> {
487
+ return {
488
+ version: "v1",
489
+ kind: "Prompt",
490
+ data: {
491
+ content: [{ kind: "text", data: text }],
492
+ meta: { timestamp },
493
+ },
494
+ };
495
+ }
496
+
497
+ function assistantText(text: string): Record<string, unknown> {
498
+ return {
499
+ version: "v1",
500
+ kind: "AssistantMessage",
501
+ data: { content: [{ kind: "text", data: text }] },
502
+ };
503
+ }
504
+
505
+ function assistantTool(name: string, input: Record<string, unknown>): Record<string, unknown> {
506
+ return {
507
+ version: "v1",
508
+ kind: "AssistantMessage",
509
+ data: {
510
+ content: [
511
+ {
512
+ kind: "toolUse",
513
+ data: { toolUseId: "tool-1", name, input },
514
+ },
515
+ ],
516
+ },
517
+ };
518
+ }
519
+
520
+ function toolResult(result: string, status: string): Record<string, unknown> {
521
+ return {
522
+ version: "v1",
523
+ kind: "ToolResults",
524
+ data: {
525
+ content: [
526
+ {
527
+ kind: "toolResult",
528
+ data: { toolUseId: "tool-1", status, result },
529
+ },
530
+ ],
531
+ },
532
+ };
533
+ }
534
+ });