@ai-devkit/agent-manager 0.33.0 → 0.33.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 (112) hide show
  1. package/dist/AgentManager.d.ts +11 -2
  2. package/dist/AgentManager.d.ts.map +1 -1
  3. package/dist/AgentManager.js +37 -6
  4. package/dist/AgentManager.js.map +1 -1
  5. package/dist/__tests__/AgentManager.test.js +212 -5
  6. package/dist/__tests__/AgentManager.test.js.map +1 -1
  7. package/dist/__tests__/adapters/OpenCodeAdapter.test.js +1 -0
  8. package/dist/__tests__/adapters/OpenCodeAdapter.test.js.map +1 -1
  9. package/dist/__tests__/{adapters → providers/copilot}/CopilotAdapter.test.js +19 -14
  10. package/dist/__tests__/providers/copilot/CopilotAdapter.test.js.map +1 -0
  11. package/dist/__tests__/{adapters → providers/gemini}/GeminiCliAdapter.test.js +37 -29
  12. package/dist/__tests__/providers/gemini/GeminiCliAdapter.test.js.map +1 -0
  13. package/dist/__tests__/runtime/HerdrAgentDiscovery.test.js +164 -0
  14. package/dist/__tests__/runtime/HerdrAgentDiscovery.test.js.map +1 -0
  15. package/dist/__tests__/runtime/HerdrAgentRuntime.test.js +37 -1
  16. package/dist/__tests__/runtime/HerdrAgentRuntime.test.js.map +1 -1
  17. package/dist/__tests__/utils/AgentRegistry.test.js +20 -13
  18. package/dist/__tests__/utils/AgentRegistry.test.js.map +1 -1
  19. package/dist/adapters/OpenCodeAdapter.d.ts +1 -0
  20. package/dist/adapters/OpenCodeAdapter.d.ts.map +1 -1
  21. package/dist/adapters/OpenCodeAdapter.js +7 -4
  22. package/dist/adapters/OpenCodeAdapter.js.map +1 -1
  23. package/dist/adapters/index.d.ts +2 -2
  24. package/dist/adapters/index.d.ts.map +1 -1
  25. package/dist/adapters/index.js +2 -2
  26. package/dist/adapters/index.js.map +1 -1
  27. package/dist/index.d.ts +5 -3
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +3 -2
  30. package/dist/index.js.map +1 -1
  31. package/dist/{adapters → providers/copilot}/CopilotAdapter.d.ts +11 -25
  32. package/dist/providers/copilot/CopilotAdapter.d.ts.map +1 -0
  33. package/dist/providers/copilot/CopilotAdapter.js +108 -0
  34. package/dist/providers/copilot/CopilotAdapter.js.map +1 -0
  35. package/dist/providers/copilot/CopilotAgentMapper.d.ts +9 -0
  36. package/dist/providers/copilot/CopilotAgentMapper.d.ts.map +1 -0
  37. package/dist/providers/copilot/CopilotAgentMapper.js +37 -0
  38. package/dist/providers/copilot/CopilotAgentMapper.js.map +1 -0
  39. package/dist/providers/copilot/CopilotSessionLocator.d.ts +19 -0
  40. package/dist/providers/copilot/CopilotSessionLocator.d.ts.map +1 -0
  41. package/dist/providers/copilot/CopilotSessionLocator.js +41 -0
  42. package/dist/providers/copilot/CopilotSessionLocator.js.map +1 -0
  43. package/dist/providers/copilot/CopilotSessionParser.d.ts +30 -0
  44. package/dist/providers/copilot/CopilotSessionParser.d.ts.map +1 -0
  45. package/dist/providers/copilot/CopilotSessionParser.js +181 -0
  46. package/dist/providers/copilot/CopilotSessionParser.js.map +1 -0
  47. package/dist/providers/gemini/GeminiAgentMapper.d.ts +9 -0
  48. package/dist/providers/gemini/GeminiAgentMapper.d.ts.map +1 -0
  49. package/dist/providers/gemini/GeminiAgentMapper.js +37 -0
  50. package/dist/providers/gemini/GeminiAgentMapper.js.map +1 -0
  51. package/dist/providers/gemini/GeminiCliAdapter.d.ts +52 -0
  52. package/dist/providers/gemini/GeminiCliAdapter.d.ts.map +1 -0
  53. package/dist/providers/gemini/GeminiCliAdapter.js +188 -0
  54. package/dist/providers/gemini/GeminiCliAdapter.js.map +1 -0
  55. package/dist/providers/gemini/GeminiSessionLocator.d.ts +30 -0
  56. package/dist/providers/gemini/GeminiSessionLocator.d.ts.map +1 -0
  57. package/dist/providers/gemini/GeminiSessionLocator.js +142 -0
  58. package/dist/providers/gemini/GeminiSessionLocator.js.map +1 -0
  59. package/dist/providers/gemini/GeminiSessionParser.d.ts +94 -0
  60. package/dist/providers/gemini/GeminiSessionParser.d.ts.map +1 -0
  61. package/dist/providers/gemini/GeminiSessionParser.js +166 -0
  62. package/dist/providers/gemini/GeminiSessionParser.js.map +1 -0
  63. package/dist/runtime/herdr/HerdrAgentDiscovery.d.ts +18 -0
  64. package/dist/runtime/herdr/HerdrAgentDiscovery.d.ts.map +1 -0
  65. package/dist/runtime/herdr/HerdrAgentDiscovery.js +183 -0
  66. package/dist/runtime/herdr/HerdrAgentDiscovery.js.map +1 -0
  67. package/dist/runtime/herdr/HerdrAgentRuntime.d.ts +5 -0
  68. package/dist/runtime/herdr/HerdrAgentRuntime.d.ts.map +1 -1
  69. package/dist/runtime/herdr/HerdrAgentRuntime.js +26 -8
  70. package/dist/runtime/herdr/HerdrAgentRuntime.js.map +1 -1
  71. package/dist/runtime/herdr/HerdrResponseParsers.d.ts +2 -0
  72. package/dist/runtime/herdr/HerdrResponseParsers.d.ts.map +1 -1
  73. package/dist/runtime/herdr/HerdrResponseParsers.js +12 -1
  74. package/dist/runtime/herdr/HerdrResponseParsers.js.map +1 -1
  75. package/dist/utils/AgentRegistry.d.ts.map +1 -1
  76. package/dist/utils/AgentRegistry.js +5 -5
  77. package/dist/utils/AgentRegistry.js.map +1 -1
  78. package/package.json +1 -1
  79. package/src/AgentManager.ts +67 -5
  80. package/src/__tests__/AgentManager.test.ts +231 -5
  81. package/src/__tests__/adapters/OpenCodeAdapter.test.ts +1 -0
  82. package/src/__tests__/{adapters → providers/copilot}/CopilotAdapter.test.ts +13 -16
  83. package/src/__tests__/{adapters → providers/gemini}/GeminiCliAdapter.test.ts +39 -31
  84. package/src/__tests__/runtime/HerdrAgentDiscovery.test.ts +131 -0
  85. package/src/__tests__/runtime/HerdrAgentRuntime.test.ts +20 -1
  86. package/src/__tests__/utils/AgentRegistry.test.ts +20 -10
  87. package/src/adapters/OpenCodeAdapter.ts +8 -4
  88. package/src/adapters/index.ts +2 -2
  89. package/src/index.ts +10 -3
  90. package/src/providers/copilot/CopilotAdapter.ts +145 -0
  91. package/src/providers/copilot/CopilotAgentMapper.ts +36 -0
  92. package/src/providers/copilot/CopilotSessionLocator.ts +57 -0
  93. package/src/providers/copilot/CopilotSessionParser.ts +289 -0
  94. package/src/providers/gemini/GeminiAgentMapper.ts +36 -0
  95. package/src/providers/gemini/GeminiCliAdapter.ts +266 -0
  96. package/src/providers/gemini/GeminiSessionLocator.ts +183 -0
  97. package/src/providers/gemini/GeminiSessionParser.ts +271 -0
  98. package/src/runtime/herdr/HerdrAgentDiscovery.ts +178 -0
  99. package/src/runtime/herdr/HerdrAgentRuntime.ts +33 -7
  100. package/src/runtime/herdr/HerdrResponseParsers.ts +11 -1
  101. package/src/utils/AgentRegistry.ts +8 -6
  102. package/dist/__tests__/adapters/CopilotAdapter.test.js.map +0 -1
  103. package/dist/__tests__/adapters/GeminiCliAdapter.test.js.map +0 -1
  104. package/dist/adapters/CopilotAdapter.d.ts.map +0 -1
  105. package/dist/adapters/CopilotAdapter.js +0 -329
  106. package/dist/adapters/CopilotAdapter.js.map +0 -1
  107. package/dist/adapters/GeminiCliAdapter.d.ts +0 -98
  108. package/dist/adapters/GeminiCliAdapter.d.ts.map +0 -1
  109. package/dist/adapters/GeminiCliAdapter.js +0 -513
  110. package/dist/adapters/GeminiCliAdapter.js.map +0 -1
  111. package/src/adapters/CopilotAdapter.ts +0 -483
  112. package/src/adapters/GeminiCliAdapter.ts +0 -699
@@ -0,0 +1,164 @@
1
+ import { extractHerdrAgentPanes, fetchHerdrAgentPanes, findMatchingHerdrPane } from "../../runtime/herdr/HerdrAgentDiscovery.js";
2
+ function jsonResponse(value) {
3
+ return {
4
+ stdout: JSON.stringify(value),
5
+ stderr: ""
6
+ };
7
+ }
8
+ describe("HerdrAgentDiscovery", ()=>{
9
+ it("extracts agent panes from pane list responses", ()=>{
10
+ const panes = extractHerdrAgentPanes({
11
+ result: {
12
+ panes: [
13
+ {
14
+ agent: "codex",
15
+ agent_session: {
16
+ value: "session-1"
17
+ },
18
+ cwd: "/repo",
19
+ pane_id: "w32:p1",
20
+ workspace_id: "w32",
21
+ tab_id: "w32:t1"
22
+ }
23
+ ]
24
+ }
25
+ });
26
+ expect(panes).toEqual([
27
+ {
28
+ agent: "codex",
29
+ agentSessionId: "session-1",
30
+ cwd: "/repo",
31
+ paneId: "w32:p1",
32
+ workspaceId: "w32",
33
+ tabId: "w32:t1"
34
+ }
35
+ ]);
36
+ });
37
+ it("matches by foreground PID before falling back to ambiguous cwd and type", ()=>{
38
+ const pane = findMatchingHerdrPane({
39
+ type: "codex",
40
+ projectPath: "/repo",
41
+ sessionId: "pid-64904",
42
+ pid: 64904
43
+ }, [
44
+ {
45
+ agent: "codex",
46
+ cwd: "/repo",
47
+ paneId: "w31:p1",
48
+ foregroundPids: [
49
+ 44290
50
+ ]
51
+ },
52
+ {
53
+ agent: "codex",
54
+ cwd: "/repo",
55
+ paneId: "w32:p1",
56
+ foregroundPids: [
57
+ 64904
58
+ ]
59
+ }
60
+ ]);
61
+ expect(pane?.paneId).toBe("w32:p1");
62
+ });
63
+ it("prefers a foreground PID match over a shell PID match", ()=>{
64
+ const pane = findMatchingHerdrPane({
65
+ type: "codex",
66
+ projectPath: "/repo",
67
+ sessionId: "pid-64904",
68
+ pid: 64904
69
+ }, [
70
+ {
71
+ agent: "codex",
72
+ cwd: "/repo",
73
+ paneId: "w31:p1",
74
+ shellPid: 64904
75
+ },
76
+ {
77
+ agent: "codex",
78
+ cwd: "/repo",
79
+ paneId: "w32:p1",
80
+ foregroundPids: [
81
+ 64904
82
+ ]
83
+ }
84
+ ]);
85
+ expect(pane?.paneId).toBe("w32:p1");
86
+ });
87
+ it("enriches pane list output with foreground process IDs", async ()=>{
88
+ const runner = vi.fn().mockResolvedValueOnce(jsonResponse({
89
+ result: {
90
+ panes: [
91
+ {
92
+ agent: "codex",
93
+ cwd: "/repo",
94
+ pane_id: "w31:p1"
95
+ },
96
+ {
97
+ agent: "codex",
98
+ cwd: "/repo",
99
+ pane_id: "w32:p1"
100
+ }
101
+ ]
102
+ }
103
+ })).mockResolvedValueOnce(jsonResponse({
104
+ result: {
105
+ process_info: {
106
+ pane_id: "w31:p1",
107
+ shell_pid: 111,
108
+ foreground_processes: [
109
+ {
110
+ pid: 44290
111
+ }
112
+ ]
113
+ }
114
+ }
115
+ })).mockResolvedValueOnce(jsonResponse({
116
+ result: {
117
+ process_info: {
118
+ pane_id: "w32:p1",
119
+ shell_pid: 222,
120
+ foreground_processes: [
121
+ {
122
+ pid: 64904
123
+ }
124
+ ]
125
+ }
126
+ }
127
+ }));
128
+ const panes = await fetchHerdrAgentPanes(runner);
129
+ expect(runner).toHaveBeenNthCalledWith(1, "herdr", [
130
+ "pane",
131
+ "list"
132
+ ]);
133
+ expect(runner).toHaveBeenNthCalledWith(2, "herdr", [
134
+ "pane",
135
+ "process-info",
136
+ "--pane",
137
+ "w31:p1"
138
+ ]);
139
+ expect(runner).toHaveBeenNthCalledWith(3, "herdr", [
140
+ "pane",
141
+ "process-info",
142
+ "--pane",
143
+ "w32:p1"
144
+ ]);
145
+ expect(panes).toMatchObject([
146
+ {
147
+ paneId: "w31:p1",
148
+ foregroundPids: [
149
+ 44290
150
+ ],
151
+ shellPid: 111
152
+ },
153
+ {
154
+ paneId: "w32:p1",
155
+ foregroundPids: [
156
+ 64904
157
+ ],
158
+ shellPid: 222
159
+ }
160
+ ]);
161
+ });
162
+ });
163
+
164
+ //# sourceMappingURL=HerdrAgentDiscovery.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/__tests__/runtime/HerdrAgentDiscovery.test.ts"],"sourcesContent":["import {\n extractHerdrAgentPanes,\n fetchHerdrAgentPanes,\n findMatchingHerdrPane,\n} from \"../../runtime/herdr/HerdrAgentDiscovery.js\";\nimport type { HerdrCommandRunner } from \"../../runtime/herdr/HerdrCliClient.js\";\n\nfunction jsonResponse(value: unknown): { stdout: string; stderr: string } {\n return { stdout: JSON.stringify(value), stderr: \"\" };\n}\n\ndescribe(\"HerdrAgentDiscovery\", () => {\n it(\"extracts agent panes from pane list responses\", () => {\n const panes = extractHerdrAgentPanes({\n result: {\n panes: [\n {\n agent: \"codex\",\n agent_session: { value: \"session-1\" },\n cwd: \"/repo\",\n pane_id: \"w32:p1\",\n workspace_id: \"w32\",\n tab_id: \"w32:t1\",\n },\n ],\n },\n });\n\n expect(panes).toEqual([\n {\n agent: \"codex\",\n agentSessionId: \"session-1\",\n cwd: \"/repo\",\n paneId: \"w32:p1\",\n workspaceId: \"w32\",\n tabId: \"w32:t1\",\n },\n ]);\n });\n\n it(\"matches by foreground PID before falling back to ambiguous cwd and type\", () => {\n const pane = findMatchingHerdrPane(\n {\n type: \"codex\",\n projectPath: \"/repo\",\n sessionId: \"pid-64904\",\n pid: 64904,\n },\n [\n { agent: \"codex\", cwd: \"/repo\", paneId: \"w31:p1\", foregroundPids: [44290] },\n { agent: \"codex\", cwd: \"/repo\", paneId: \"w32:p1\", foregroundPids: [64904] },\n ],\n );\n\n expect(pane?.paneId).toBe(\"w32:p1\");\n });\n\n it(\"prefers a foreground PID match over a shell PID match\", () => {\n const pane = findMatchingHerdrPane(\n {\n type: \"codex\",\n projectPath: \"/repo\",\n sessionId: \"pid-64904\",\n pid: 64904,\n },\n [\n { agent: \"codex\", cwd: \"/repo\", paneId: \"w31:p1\", shellPid: 64904 },\n { agent: \"codex\", cwd: \"/repo\", paneId: \"w32:p1\", foregroundPids: [64904] },\n ],\n );\n\n expect(pane?.paneId).toBe(\"w32:p1\");\n });\n\n it(\"enriches pane list output with foreground process IDs\", async () => {\n const runner = vi\n .fn<HerdrCommandRunner>()\n .mockResolvedValueOnce(\n jsonResponse({\n result: {\n panes: [\n { agent: \"codex\", cwd: \"/repo\", pane_id: \"w31:p1\" },\n { agent: \"codex\", cwd: \"/repo\", pane_id: \"w32:p1\" },\n ],\n },\n }),\n )\n .mockResolvedValueOnce(\n jsonResponse({\n result: {\n process_info: {\n pane_id: \"w31:p1\",\n shell_pid: 111,\n foreground_processes: [{ pid: 44290 }],\n },\n },\n }),\n )\n .mockResolvedValueOnce(\n jsonResponse({\n result: {\n process_info: {\n pane_id: \"w32:p1\",\n shell_pid: 222,\n foreground_processes: [{ pid: 64904 }],\n },\n },\n }),\n );\n\n const panes = await fetchHerdrAgentPanes(runner);\n\n expect(runner).toHaveBeenNthCalledWith(1, \"herdr\", [\"pane\", \"list\"]);\n expect(runner).toHaveBeenNthCalledWith(2, \"herdr\", [\n \"pane\",\n \"process-info\",\n \"--pane\",\n \"w31:p1\",\n ]);\n expect(runner).toHaveBeenNthCalledWith(3, \"herdr\", [\n \"pane\",\n \"process-info\",\n \"--pane\",\n \"w32:p1\",\n ]);\n expect(panes).toMatchObject([\n { paneId: \"w31:p1\", foregroundPids: [44290], shellPid: 111 },\n { paneId: \"w32:p1\", foregroundPids: [64904], shellPid: 222 },\n ]);\n });\n});\n"],"names":["extractHerdrAgentPanes","fetchHerdrAgentPanes","findMatchingHerdrPane","jsonResponse","value","stdout","JSON","stringify","stderr","describe","it","panes","result","agent","agent_session","cwd","pane_id","workspace_id","tab_id","expect","toEqual","agentSessionId","paneId","workspaceId","tabId","pane","type","projectPath","sessionId","pid","foregroundPids","toBe","shellPid","runner","vi","fn","mockResolvedValueOnce","process_info","shell_pid","foreground_processes","toHaveBeenNthCalledWith","toMatchObject"],"mappings":"AAAA,SACEA,sBAAsB,EACtBC,oBAAoB,EACpBC,qBAAqB,QAChB,6CAA6C;AAGpD,SAASC,aAAaC,KAAc;IAClC,OAAO;QAAEC,QAAQC,KAAKC,SAAS,CAACH;QAAQI,QAAQ;IAAG;AACrD;AAEAC,SAAS,uBAAuB;IAC9BC,GAAG,iDAAiD;QAClD,MAAMC,QAAQX,uBAAuB;YACnCY,QAAQ;gBACND,OAAO;oBACL;wBACEE,OAAO;wBACPC,eAAe;4BAAEV,OAAO;wBAAY;wBACpCW,KAAK;wBACLC,SAAS;wBACTC,cAAc;wBACdC,QAAQ;oBACV;iBACD;YACH;QACF;QAEAC,OAAOR,OAAOS,OAAO,CAAC;YACpB;gBACEP,OAAO;gBACPQ,gBAAgB;gBAChBN,KAAK;gBACLO,QAAQ;gBACRC,aAAa;gBACbC,OAAO;YACT;SACD;IACH;IAEAd,GAAG,2EAA2E;QAC5E,MAAMe,OAAOvB,sBACX;YACEwB,MAAM;YACNC,aAAa;YACbC,WAAW;YACXC,KAAK;QACP,GACA;YACE;gBAAEhB,OAAO;gBAASE,KAAK;gBAASO,QAAQ;gBAAUQ,gBAAgB;oBAAC;iBAAM;YAAC;YAC1E;gBAAEjB,OAAO;gBAASE,KAAK;gBAASO,QAAQ;gBAAUQ,gBAAgB;oBAAC;iBAAM;YAAC;SAC3E;QAGHX,OAAOM,MAAMH,QAAQS,IAAI,CAAC;IAC5B;IAEArB,GAAG,yDAAyD;QAC1D,MAAMe,OAAOvB,sBACX;YACEwB,MAAM;YACNC,aAAa;YACbC,WAAW;YACXC,KAAK;QACP,GACA;YACE;gBAAEhB,OAAO;gBAASE,KAAK;gBAASO,QAAQ;gBAAUU,UAAU;YAAM;YAClE;gBAAEnB,OAAO;gBAASE,KAAK;gBAASO,QAAQ;gBAAUQ,gBAAgB;oBAAC;iBAAM;YAAC;SAC3E;QAGHX,OAAOM,MAAMH,QAAQS,IAAI,CAAC;IAC5B;IAEArB,GAAG,yDAAyD;QAC1D,MAAMuB,SAASC,GACZC,EAAE,GACFC,qBAAqB,CACpBjC,aAAa;YACXS,QAAQ;gBACND,OAAO;oBACL;wBAAEE,OAAO;wBAASE,KAAK;wBAASC,SAAS;oBAAS;oBAClD;wBAAEH,OAAO;wBAASE,KAAK;wBAASC,SAAS;oBAAS;iBACnD;YACH;QACF,IAEDoB,qBAAqB,CACpBjC,aAAa;YACXS,QAAQ;gBACNyB,cAAc;oBACZrB,SAAS;oBACTsB,WAAW;oBACXC,sBAAsB;wBAAC;4BAAEV,KAAK;wBAAM;qBAAE;gBACxC;YACF;QACF,IAEDO,qBAAqB,CACpBjC,aAAa;YACXS,QAAQ;gBACNyB,cAAc;oBACZrB,SAAS;oBACTsB,WAAW;oBACXC,sBAAsB;wBAAC;4BAAEV,KAAK;wBAAM;qBAAE;gBACxC;YACF;QACF;QAGJ,MAAMlB,QAAQ,MAAMV,qBAAqBgC;QAEzCd,OAAOc,QAAQO,uBAAuB,CAAC,GAAG,SAAS;YAAC;YAAQ;SAAO;QACnErB,OAAOc,QAAQO,uBAAuB,CAAC,GAAG,SAAS;YACjD;YACA;YACA;YACA;SACD;QACDrB,OAAOc,QAAQO,uBAAuB,CAAC,GAAG,SAAS;YACjD;YACA;YACA;YACA;SACD;QACDrB,OAAOR,OAAO8B,aAAa,CAAC;YAC1B;gBAAEnB,QAAQ;gBAAUQ,gBAAgB;oBAAC;iBAAM;gBAAEE,UAAU;YAAI;YAC3D;gBAAEV,QAAQ;gBAAUQ,gBAAgB;oBAAC;iBAAM;gBAAEE,UAAU;YAAI;SAC5D;IACH;AACF"}
@@ -222,12 +222,48 @@ describe("HerdrAgentRuntime", ()=>{
222
222
  ]);
223
223
  const runtime = new HerdrAgentRuntime({
224
224
  runner,
225
- env: {}
225
+ env: {},
226
+ processInfoPollMs: 0
226
227
  });
227
228
  await expect(runtime.startAgent(startInput)).resolves.toMatchObject({
228
229
  pid: 12000
229
230
  });
230
231
  });
232
+ it("waits for a foreground process pid before falling back to the pane shell pid", async ()=>{
233
+ const runner = createRunner([
234
+ workspaceResponse(),
235
+ agentResponse(),
236
+ processInfoResponse({
237
+ shellPid: 12000
238
+ }),
239
+ processInfoResponse({
240
+ shellPid: 12000,
241
+ pid: 12345,
242
+ processName: "node"
243
+ })
244
+ ]);
245
+ const runtime = new HerdrAgentRuntime({
246
+ runner,
247
+ env: {},
248
+ processInfoPollMs: 100,
249
+ processInfoPollIntervalMs: 0
250
+ });
251
+ await expect(runtime.startAgent(startInput)).resolves.toMatchObject({
252
+ pid: 12345
253
+ });
254
+ expect(runner).toHaveBeenNthCalledWith(3, "herdr", [
255
+ "pane",
256
+ "process-info",
257
+ "--pane",
258
+ "w1:p1"
259
+ ]);
260
+ expect(runner).toHaveBeenNthCalledWith(4, "herdr", [
261
+ "pane",
262
+ "process-info",
263
+ "--pane",
264
+ "w1:p1"
265
+ ]);
266
+ });
231
267
  it("passes only explicit extra agent args after the Herdr start separator", async ()=>{
232
268
  const runner = createRunner([
233
269
  workspaceResponse(),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/__tests__/runtime/HerdrAgentRuntime.test.ts"],"sourcesContent":["import { HerdrAgentRuntime } from \"../../runtime/herdr/HerdrAgentRuntime.js\";\nimport { HerdrRuntimeError } from \"../../runtime/herdr/HerdrErrors.js\";\nimport type { HerdrCommandRunner } from \"../../runtime/herdr/HerdrCliClient.js\";\n\nfunction createRunner(\n responses: Array<{ stdout?: string; stderr?: string; reject?: Error }> = [],\n): HerdrCommandRunner {\n const runner = vi.fn(async () => {\n const response = responses.shift();\n if (!response) return { stdout: \"{}\", stderr: \"\" };\n if (response.reject) throw response.reject;\n return { stdout: response.stdout ?? \"\", stderr: response.stderr ?? \"\" };\n });\n return runner;\n}\n\nfunction herdrCliError(response: unknown): Error {\n return Object.assign(new Error(\"herdr command failed\"), {\n stdout: JSON.stringify(response),\n stderr: \"\",\n code: 1,\n });\n}\n\nfunction jsonResponse(value: unknown): { stdout: string } {\n return { stdout: JSON.stringify(value) };\n}\n\nfunction workspaceResponse(workspaceId = \"w1\", paneId = \"w1:p1\"): { stdout: string } {\n return jsonResponse({\n result: {\n type: \"workspace_info\",\n workspace: { workspace_id: workspaceId },\n root_pane: { pane_id: paneId },\n },\n });\n}\n\nfunction agentResponse({\n name = \"reviewer\",\n paneId = \"w1:p1\",\n workspaceId,\n tabId,\n}: {\n name?: string;\n paneId?: string;\n workspaceId?: string;\n tabId?: string;\n} = {}): { stdout: string } {\n return jsonResponse({\n result: {\n type: \"agent_info\",\n agent: {\n name,\n agent: \"codex\",\n pane_id: paneId,\n ...(workspaceId ? { workspace_id: workspaceId } : {}),\n ...(tabId ? { tab_id: tabId } : {}),\n },\n },\n });\n}\n\nfunction processInfoResponse({\n paneId = \"w1:p1\",\n pid,\n shellPid,\n processName,\n}: {\n paneId?: string;\n pid?: number;\n shellPid?: number;\n processName?: string;\n} = {}): { stdout: string } {\n return jsonResponse({\n result: {\n type: \"pane_process_info\",\n process_info: {\n pane_id: paneId,\n ...(shellPid ? { shell_pid: shellPid } : {}),\n foreground_processes: pid ? [{ pid, ...(processName ? { name: processName } : {}) }] : [],\n },\n },\n });\n}\n\nconst startInput = {\n name: \"reviewer\",\n cwd: \"/repo\",\n kind: \"codex\",\n args: [],\n timeoutMs: 15000,\n};\n\ndescribe(\"HerdrAgentRuntime\", () => {\n it(\"reports unavailable when the herdr binary is missing\", async () => {\n const runner = createRunner([\n { reject: Object.assign(new Error(\"not found\"), { code: \"ENOENT\" }) },\n ]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n\n await expect(runtime.isAvailable()).resolves.toEqual({\n ok: false,\n reason: \"binary-missing\",\n detail: \"herdr command was not found in PATH.\",\n });\n });\n\n it(\"reports available outside Herdr when binary and snapshot work\", async () => {\n const runner = createRunner([\n { stdout: \"herdr 0.8.2\\n\" },\n jsonResponse({ id: \"cli:api:snapshot\", result: { type: \"session_snapshot\" } }),\n ]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n\n await expect(runtime.isAvailable()).resolves.toEqual({\n ok: true,\n insideRuntime: false,\n });\n expect(runner).toHaveBeenNthCalledWith(1, \"herdr\", [\"--version\"]);\n expect(runner).toHaveBeenNthCalledWith(2, \"herdr\", [\"api\", \"snapshot\"]);\n });\n\n it(\"includes Herdr environment context when present\", async () => {\n const runner = createRunner([\n { stdout: \"herdr 0.8.2\\n\" },\n jsonResponse({ id: \"cli:api:snapshot\", result: { type: \"session_snapshot\" } }),\n ]);\n const runtime = new HerdrAgentRuntime({\n runner,\n env: { HERDR_ENV: \"1\", HERDR_PANE_ID: \"w1:p1\" },\n });\n\n await expect(runtime.isAvailable()).resolves.toEqual({\n ok: true,\n insideRuntime: true,\n currentPaneId: \"w1:p1\",\n });\n });\n\n it(\"creates a workspace, starts the named agent, and returns an opaque Herdr ref\", async () => {\n const runner = createRunner([\n workspaceResponse(),\n agentResponse({ workspaceId: \"w1\", tabId: \"w1:t1\" }),\n processInfoResponse({ shellPid: 12000, pid: 12345, processName: \"codex\" }),\n ]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n\n const result = await runtime.startAgent(startInput);\n\n expect(runner).toHaveBeenNthCalledWith(1, \"herdr\", [\n \"workspace\",\n \"create\",\n \"--cwd\",\n \"/repo\",\n \"--label\",\n \"reviewer\",\n ]);\n expect(runner).toHaveBeenNthCalledWith(2, \"herdr\", [\n \"agent\",\n \"start\",\n \"reviewer\",\n \"--kind\",\n \"codex\",\n \"--pane\",\n \"w1:p1\",\n \"--timeout\",\n \"15000\",\n ]);\n expect(runner).toHaveBeenNthCalledWith(3, \"herdr\", [\"pane\", \"process-info\", \"--pane\", \"w1:p1\"]);\n expect(result).toEqual({\n pid: 12345,\n runtimeRef: {\n session: \"default\",\n workspaceId: \"w1\",\n tabId: \"w1:t1\",\n paneId: \"w1:p1\",\n agentName: \"reviewer\",\n },\n });\n });\n\n it(\"falls back to the pane shell pid when no foreground process pid is reported\", async () => {\n const runner = createRunner([\n workspaceResponse(),\n agentResponse(),\n processInfoResponse({ shellPid: 12000 }),\n ]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n\n await expect(runtime.startAgent(startInput)).resolves.toMatchObject({ pid: 12000 });\n });\n\n it(\"passes only explicit extra agent args after the Herdr start separator\", async () => {\n const runner = createRunner([\n workspaceResponse(),\n agentResponse(),\n processInfoResponse({ pid: 12345 }),\n ]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n\n await runtime.startAgent({\n ...startInput,\n args: [\"--model\", \"gpt-5-codex\"],\n });\n\n expect(runner).toHaveBeenNthCalledWith(2, \"herdr\", [\n \"agent\",\n \"start\",\n \"reviewer\",\n \"--kind\",\n \"codex\",\n \"--pane\",\n \"w1:p1\",\n \"--timeout\",\n \"15000\",\n \"--\",\n \"--model\",\n \"gpt-5-codex\",\n ]);\n });\n\n it(\"retries agent start when a freshly-created Herdr pane is temporarily busy\", async () => {\n const runner = createRunner([\n workspaceResponse(\"w15\", \"w15:p1\"),\n {\n reject: herdrCliError({\n error: {\n code: \"agent_pane_busy\",\n message: \"agent target pane w15:p1 is not an available shell\",\n },\n id: \"cli:agent:start\",\n }),\n },\n agentResponse({ paneId: \"w15:p1\", workspaceId: \"w15\", tabId: \"w15:t1\" }),\n processInfoResponse({ paneId: \"w15:p1\", pid: 12345 }),\n ]);\n const runtime = new HerdrAgentRuntime({\n runner,\n env: {},\n agentStartBusyRetryMs: 100,\n agentStartBusyRetryIntervalMs: 0,\n });\n\n await expect(runtime.startAgent(startInput)).resolves.toMatchObject({\n runtimeRef: {\n workspaceId: \"w15\",\n paneId: \"w15:p1\",\n },\n });\n\n expect(runner).toHaveBeenNthCalledWith(2, \"herdr\", [\n \"agent\",\n \"start\",\n \"reviewer\",\n \"--kind\",\n \"codex\",\n \"--pane\",\n \"w15:p1\",\n \"--timeout\",\n \"15000\",\n ]);\n expect(runner).toHaveBeenNthCalledWith(3, \"herdr\", [\n \"agent\",\n \"start\",\n \"reviewer\",\n \"--kind\",\n \"codex\",\n \"--pane\",\n \"w15:p1\",\n \"--timeout\",\n \"15000\",\n ]);\n });\n\n it(\"surfaces non-retryable Herdr agent start errors without retrying\", async () => {\n const runner = createRunner([\n workspaceResponse(),\n {\n reject: herdrCliError({\n error: {\n code: \"duplicate_agent_name\",\n message: \"agent name reviewer already exists\",\n },\n id: \"cli:agent:start\",\n }),\n },\n ]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n\n await expect(runtime.startAgent(startInput)).rejects.toMatchObject({\n code: \"duplicate_agent_name\",\n message: \"agent name reviewer already exists\",\n });\n\n expect(runner).toHaveBeenCalledTimes(2);\n });\n\n it(\"rejects start responses when Herdr process info does not include a pid\", async () => {\n const runner = createRunner([workspaceResponse(), agentResponse(), processInfoResponse()]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n\n await expect(runtime.startAgent(startInput)).rejects.toThrow(\n new HerdrRuntimeError(\"Herdr pane process-info response did not include process pid.\"),\n );\n });\n\n it(\"rejects start responses that do not include a pane id\", async () => {\n const runner = createRunner([{ stdout: '{\"result\":{\"workspace\":{\"workspace_id\":\"w1\"}}}' }]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n\n await expect(runtime.startAgent(startInput)).rejects.toThrow(\n new HerdrRuntimeError(\"Herdr workspace create response did not include root pane id.\"),\n );\n });\n\n it(\"sends, waits, reads, focuses, and stops by Herdr pane ref\", async () => {\n const runner = createRunner([\n { stdout: \"{}\" },\n { stdout: '{\"result\":{\"agent\":{\"agent_status\":\"done\"}}}' },\n { stdout: \"done\\n\" },\n { stdout: \"{}\" },\n { stdout: \"{}\" },\n ]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n const runtimeRef = { paneId: \"w1:p2\", agentName: \"reviewer\" };\n\n await runtime.send({ runtimeRef, prompt: \"continue\" });\n await runtime.wait({ runtimeRef, timeoutMs: 2000 });\n await expect(runtime.readOutput({ runtimeRef, lines: 80 })).resolves.toBe(\"done\\n\");\n await expect(runtime.focus({ runtimeRef })).resolves.toBe(true);\n await runtime.stop({ runtimeRef });\n\n expect(runner).toHaveBeenNthCalledWith(1, \"herdr\", [\"agent\", \"prompt\", \"w1:p2\", \"continue\"]);\n expect(runner).toHaveBeenNthCalledWith(2, \"herdr\", [\n \"agent\",\n \"wait\",\n \"w1:p2\",\n \"--until\",\n \"done\",\n \"--until\",\n \"blocked\",\n \"--timeout\",\n \"2000\",\n ]);\n expect(runner).toHaveBeenNthCalledWith(3, \"herdr\", [\n \"agent\",\n \"read\",\n \"w1:p2\",\n \"--source\",\n \"recent-unwrapped\",\n \"--lines\",\n \"80\",\n ]);\n expect(runner).toHaveBeenNthCalledWith(4, \"herdr\", [\"agent\", \"focus\", \"w1:p2\"]);\n expect(runner).toHaveBeenNthCalledWith(5, \"herdr\", [\"pane\", \"close\", \"w1:p2\"]);\n });\n});\n"],"names":["HerdrAgentRuntime","HerdrRuntimeError","createRunner","responses","runner","vi","fn","response","shift","stdout","stderr","reject","herdrCliError","Object","assign","Error","JSON","stringify","code","jsonResponse","value","workspaceResponse","workspaceId","paneId","result","type","workspace","workspace_id","root_pane","pane_id","agentResponse","name","tabId","agent","tab_id","processInfoResponse","pid","shellPid","processName","process_info","shell_pid","foreground_processes","startInput","cwd","kind","args","timeoutMs","describe","it","runtime","env","expect","isAvailable","resolves","toEqual","ok","reason","detail","id","insideRuntime","toHaveBeenNthCalledWith","HERDR_ENV","HERDR_PANE_ID","currentPaneId","startAgent","runtimeRef","session","agentName","toMatchObject","error","message","agentStartBusyRetryMs","agentStartBusyRetryIntervalMs","rejects","toHaveBeenCalledTimes","toThrow","send","prompt","wait","readOutput","lines","toBe","focus","stop"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/__tests__/runtime/HerdrAgentRuntime.test.ts"],"sourcesContent":["import { HerdrAgentRuntime } from \"../../runtime/herdr/HerdrAgentRuntime.js\";\nimport { HerdrRuntimeError } from \"../../runtime/herdr/HerdrErrors.js\";\nimport type { HerdrCommandRunner } from \"../../runtime/herdr/HerdrCliClient.js\";\n\nfunction createRunner(\n responses: Array<{ stdout?: string; stderr?: string; reject?: Error }> = [],\n): HerdrCommandRunner {\n const runner = vi.fn(async () => {\n const response = responses.shift();\n if (!response) return { stdout: \"{}\", stderr: \"\" };\n if (response.reject) throw response.reject;\n return { stdout: response.stdout ?? \"\", stderr: response.stderr ?? \"\" };\n });\n return runner;\n}\n\nfunction herdrCliError(response: unknown): Error {\n return Object.assign(new Error(\"herdr command failed\"), {\n stdout: JSON.stringify(response),\n stderr: \"\",\n code: 1,\n });\n}\n\nfunction jsonResponse(value: unknown): { stdout: string } {\n return { stdout: JSON.stringify(value) };\n}\n\nfunction workspaceResponse(workspaceId = \"w1\", paneId = \"w1:p1\"): { stdout: string } {\n return jsonResponse({\n result: {\n type: \"workspace_info\",\n workspace: { workspace_id: workspaceId },\n root_pane: { pane_id: paneId },\n },\n });\n}\n\nfunction agentResponse({\n name = \"reviewer\",\n paneId = \"w1:p1\",\n workspaceId,\n tabId,\n}: {\n name?: string;\n paneId?: string;\n workspaceId?: string;\n tabId?: string;\n} = {}): { stdout: string } {\n return jsonResponse({\n result: {\n type: \"agent_info\",\n agent: {\n name,\n agent: \"codex\",\n pane_id: paneId,\n ...(workspaceId ? { workspace_id: workspaceId } : {}),\n ...(tabId ? { tab_id: tabId } : {}),\n },\n },\n });\n}\n\nfunction processInfoResponse({\n paneId = \"w1:p1\",\n pid,\n shellPid,\n processName,\n}: {\n paneId?: string;\n pid?: number;\n shellPid?: number;\n processName?: string;\n} = {}): { stdout: string } {\n return jsonResponse({\n result: {\n type: \"pane_process_info\",\n process_info: {\n pane_id: paneId,\n ...(shellPid ? { shell_pid: shellPid } : {}),\n foreground_processes: pid ? [{ pid, ...(processName ? { name: processName } : {}) }] : [],\n },\n },\n });\n}\n\nconst startInput = {\n name: \"reviewer\",\n cwd: \"/repo\",\n kind: \"codex\",\n args: [],\n timeoutMs: 15000,\n};\n\ndescribe(\"HerdrAgentRuntime\", () => {\n it(\"reports unavailable when the herdr binary is missing\", async () => {\n const runner = createRunner([\n { reject: Object.assign(new Error(\"not found\"), { code: \"ENOENT\" }) },\n ]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n\n await expect(runtime.isAvailable()).resolves.toEqual({\n ok: false,\n reason: \"binary-missing\",\n detail: \"herdr command was not found in PATH.\",\n });\n });\n\n it(\"reports available outside Herdr when binary and snapshot work\", async () => {\n const runner = createRunner([\n { stdout: \"herdr 0.8.2\\n\" },\n jsonResponse({ id: \"cli:api:snapshot\", result: { type: \"session_snapshot\" } }),\n ]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n\n await expect(runtime.isAvailable()).resolves.toEqual({\n ok: true,\n insideRuntime: false,\n });\n expect(runner).toHaveBeenNthCalledWith(1, \"herdr\", [\"--version\"]);\n expect(runner).toHaveBeenNthCalledWith(2, \"herdr\", [\"api\", \"snapshot\"]);\n });\n\n it(\"includes Herdr environment context when present\", async () => {\n const runner = createRunner([\n { stdout: \"herdr 0.8.2\\n\" },\n jsonResponse({ id: \"cli:api:snapshot\", result: { type: \"session_snapshot\" } }),\n ]);\n const runtime = new HerdrAgentRuntime({\n runner,\n env: { HERDR_ENV: \"1\", HERDR_PANE_ID: \"w1:p1\" },\n });\n\n await expect(runtime.isAvailable()).resolves.toEqual({\n ok: true,\n insideRuntime: true,\n currentPaneId: \"w1:p1\",\n });\n });\n\n it(\"creates a workspace, starts the named agent, and returns an opaque Herdr ref\", async () => {\n const runner = createRunner([\n workspaceResponse(),\n agentResponse({ workspaceId: \"w1\", tabId: \"w1:t1\" }),\n processInfoResponse({ shellPid: 12000, pid: 12345, processName: \"codex\" }),\n ]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n\n const result = await runtime.startAgent(startInput);\n\n expect(runner).toHaveBeenNthCalledWith(1, \"herdr\", [\n \"workspace\",\n \"create\",\n \"--cwd\",\n \"/repo\",\n \"--label\",\n \"reviewer\",\n ]);\n expect(runner).toHaveBeenNthCalledWith(2, \"herdr\", [\n \"agent\",\n \"start\",\n \"reviewer\",\n \"--kind\",\n \"codex\",\n \"--pane\",\n \"w1:p1\",\n \"--timeout\",\n \"15000\",\n ]);\n expect(runner).toHaveBeenNthCalledWith(3, \"herdr\", [\"pane\", \"process-info\", \"--pane\", \"w1:p1\"]);\n expect(result).toEqual({\n pid: 12345,\n runtimeRef: {\n session: \"default\",\n workspaceId: \"w1\",\n tabId: \"w1:t1\",\n paneId: \"w1:p1\",\n agentName: \"reviewer\",\n },\n });\n });\n\n it(\"falls back to the pane shell pid when no foreground process pid is reported\", async () => {\n const runner = createRunner([\n workspaceResponse(),\n agentResponse(),\n processInfoResponse({ shellPid: 12000 }),\n ]);\n const runtime = new HerdrAgentRuntime({ runner, env: {}, processInfoPollMs: 0 });\n\n await expect(runtime.startAgent(startInput)).resolves.toMatchObject({ pid: 12000 });\n });\n\n it(\"waits for a foreground process pid before falling back to the pane shell pid\", async () => {\n const runner = createRunner([\n workspaceResponse(),\n agentResponse(),\n processInfoResponse({ shellPid: 12000 }),\n processInfoResponse({ shellPid: 12000, pid: 12345, processName: \"node\" }),\n ]);\n const runtime = new HerdrAgentRuntime({\n runner,\n env: {},\n processInfoPollMs: 100,\n processInfoPollIntervalMs: 0,\n });\n\n await expect(runtime.startAgent(startInput)).resolves.toMatchObject({ pid: 12345 });\n expect(runner).toHaveBeenNthCalledWith(3, \"herdr\", [\"pane\", \"process-info\", \"--pane\", \"w1:p1\"]);\n expect(runner).toHaveBeenNthCalledWith(4, \"herdr\", [\"pane\", \"process-info\", \"--pane\", \"w1:p1\"]);\n });\n\n it(\"passes only explicit extra agent args after the Herdr start separator\", async () => {\n const runner = createRunner([\n workspaceResponse(),\n agentResponse(),\n processInfoResponse({ pid: 12345 }),\n ]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n\n await runtime.startAgent({\n ...startInput,\n args: [\"--model\", \"gpt-5-codex\"],\n });\n\n expect(runner).toHaveBeenNthCalledWith(2, \"herdr\", [\n \"agent\",\n \"start\",\n \"reviewer\",\n \"--kind\",\n \"codex\",\n \"--pane\",\n \"w1:p1\",\n \"--timeout\",\n \"15000\",\n \"--\",\n \"--model\",\n \"gpt-5-codex\",\n ]);\n });\n\n it(\"retries agent start when a freshly-created Herdr pane is temporarily busy\", async () => {\n const runner = createRunner([\n workspaceResponse(\"w15\", \"w15:p1\"),\n {\n reject: herdrCliError({\n error: {\n code: \"agent_pane_busy\",\n message: \"agent target pane w15:p1 is not an available shell\",\n },\n id: \"cli:agent:start\",\n }),\n },\n agentResponse({ paneId: \"w15:p1\", workspaceId: \"w15\", tabId: \"w15:t1\" }),\n processInfoResponse({ paneId: \"w15:p1\", pid: 12345 }),\n ]);\n const runtime = new HerdrAgentRuntime({\n runner,\n env: {},\n agentStartBusyRetryMs: 100,\n agentStartBusyRetryIntervalMs: 0,\n });\n\n await expect(runtime.startAgent(startInput)).resolves.toMatchObject({\n runtimeRef: {\n workspaceId: \"w15\",\n paneId: \"w15:p1\",\n },\n });\n\n expect(runner).toHaveBeenNthCalledWith(2, \"herdr\", [\n \"agent\",\n \"start\",\n \"reviewer\",\n \"--kind\",\n \"codex\",\n \"--pane\",\n \"w15:p1\",\n \"--timeout\",\n \"15000\",\n ]);\n expect(runner).toHaveBeenNthCalledWith(3, \"herdr\", [\n \"agent\",\n \"start\",\n \"reviewer\",\n \"--kind\",\n \"codex\",\n \"--pane\",\n \"w15:p1\",\n \"--timeout\",\n \"15000\",\n ]);\n });\n\n it(\"surfaces non-retryable Herdr agent start errors without retrying\", async () => {\n const runner = createRunner([\n workspaceResponse(),\n {\n reject: herdrCliError({\n error: {\n code: \"duplicate_agent_name\",\n message: \"agent name reviewer already exists\",\n },\n id: \"cli:agent:start\",\n }),\n },\n ]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n\n await expect(runtime.startAgent(startInput)).rejects.toMatchObject({\n code: \"duplicate_agent_name\",\n message: \"agent name reviewer already exists\",\n });\n\n expect(runner).toHaveBeenCalledTimes(2);\n });\n\n it(\"rejects start responses when Herdr process info does not include a pid\", async () => {\n const runner = createRunner([workspaceResponse(), agentResponse(), processInfoResponse()]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n\n await expect(runtime.startAgent(startInput)).rejects.toThrow(\n new HerdrRuntimeError(\"Herdr pane process-info response did not include process pid.\"),\n );\n });\n\n it(\"rejects start responses that do not include a pane id\", async () => {\n const runner = createRunner([{ stdout: '{\"result\":{\"workspace\":{\"workspace_id\":\"w1\"}}}' }]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n\n await expect(runtime.startAgent(startInput)).rejects.toThrow(\n new HerdrRuntimeError(\"Herdr workspace create response did not include root pane id.\"),\n );\n });\n\n it(\"sends, waits, reads, focuses, and stops by Herdr pane ref\", async () => {\n const runner = createRunner([\n { stdout: \"{}\" },\n { stdout: '{\"result\":{\"agent\":{\"agent_status\":\"done\"}}}' },\n { stdout: \"done\\n\" },\n { stdout: \"{}\" },\n { stdout: \"{}\" },\n ]);\n const runtime = new HerdrAgentRuntime({ runner, env: {} });\n const runtimeRef = { paneId: \"w1:p2\", agentName: \"reviewer\" };\n\n await runtime.send({ runtimeRef, prompt: \"continue\" });\n await runtime.wait({ runtimeRef, timeoutMs: 2000 });\n await expect(runtime.readOutput({ runtimeRef, lines: 80 })).resolves.toBe(\"done\\n\");\n await expect(runtime.focus({ runtimeRef })).resolves.toBe(true);\n await runtime.stop({ runtimeRef });\n\n expect(runner).toHaveBeenNthCalledWith(1, \"herdr\", [\"agent\", \"prompt\", \"w1:p2\", \"continue\"]);\n expect(runner).toHaveBeenNthCalledWith(2, \"herdr\", [\n \"agent\",\n \"wait\",\n \"w1:p2\",\n \"--until\",\n \"done\",\n \"--until\",\n \"blocked\",\n \"--timeout\",\n \"2000\",\n ]);\n expect(runner).toHaveBeenNthCalledWith(3, \"herdr\", [\n \"agent\",\n \"read\",\n \"w1:p2\",\n \"--source\",\n \"recent-unwrapped\",\n \"--lines\",\n \"80\",\n ]);\n expect(runner).toHaveBeenNthCalledWith(4, \"herdr\", [\"agent\", \"focus\", \"w1:p2\"]);\n expect(runner).toHaveBeenNthCalledWith(5, \"herdr\", [\"pane\", \"close\", \"w1:p2\"]);\n });\n});\n"],"names":["HerdrAgentRuntime","HerdrRuntimeError","createRunner","responses","runner","vi","fn","response","shift","stdout","stderr","reject","herdrCliError","Object","assign","Error","JSON","stringify","code","jsonResponse","value","workspaceResponse","workspaceId","paneId","result","type","workspace","workspace_id","root_pane","pane_id","agentResponse","name","tabId","agent","tab_id","processInfoResponse","pid","shellPid","processName","process_info","shell_pid","foreground_processes","startInput","cwd","kind","args","timeoutMs","describe","it","runtime","env","expect","isAvailable","resolves","toEqual","ok","reason","detail","id","insideRuntime","toHaveBeenNthCalledWith","HERDR_ENV","HERDR_PANE_ID","currentPaneId","startAgent","runtimeRef","session","agentName","processInfoPollMs","toMatchObject","processInfoPollIntervalMs","error","message","agentStartBusyRetryMs","agentStartBusyRetryIntervalMs","rejects","toHaveBeenCalledTimes","toThrow","send","prompt","wait","readOutput","lines","toBe","focus","stop"],"mappings":"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"}
@@ -96,7 +96,7 @@ describe("AgentRegistry", ()=>{
96
96
  runtimeRef
97
97
  });
98
98
  });
99
- it("lets a Herdr managed start replace an existing same-pid name", ()=>{
99
+ it("preserves existing identity and routing fields for same-pid refreshes", ()=>{
100
100
  const runtimeRef = {
101
101
  session: "default",
102
102
  paneId: "w1:p2",
@@ -104,19 +104,28 @@ describe("AgentRegistry", ()=>{
104
104
  };
105
105
  registry.register(makeEntry({
106
106
  name: "old-name",
107
- pid: process.pid
107
+ pid: process.pid,
108
+ runtime: "tmux",
109
+ runtimeRef: {
110
+ session: "old-name"
111
+ },
112
+ cwd: "/original"
108
113
  }));
109
114
  registry.register(makeEntry({
110
115
  name: "requested-name",
111
116
  pid: process.pid,
112
117
  runtime: "herdr",
113
- runtimeRef
118
+ runtimeRef,
119
+ cwd: "/incoming"
114
120
  }));
115
- expect(registry.lookup("old-name")).toBeNull();
116
- expect(registry.lookup("requested-name")).toMatchObject({
121
+ expect(registry.lookup("requested-name")).toBeNull();
122
+ expect(registry.lookup("old-name")).toMatchObject({
117
123
  pid: process.pid,
118
- runtime: "herdr",
119
- runtimeRef
124
+ runtime: "tmux",
125
+ runtimeRef: {
126
+ session: "old-name"
127
+ },
128
+ cwd: "/original"
120
129
  });
121
130
  });
122
131
  it("preserves existing runtime ref when incoming runtime ref is empty", ()=>{
@@ -144,7 +153,7 @@ describe("AgentRegistry", ()=>{
144
153
  }));
145
154
  expect(registry.lookup("a")?.runtimeRef).toBeNull();
146
155
  });
147
- it("lets a managed start entry replace a generated fallback for the same pid", ()=>{
156
+ it("preserves an existing generated fallback for the same pid", ()=>{
148
157
  registry.register(makeEntry({
149
158
  name: `ai-devkit-${process.pid}`,
150
159
  runtimeRef: null
@@ -155,10 +164,8 @@ describe("AgentRegistry", ()=>{
155
164
  session: "custom-name"
156
165
  }
157
166
  }));
158
- expect(registry.lookup("custom-name")?.runtimeRef).toEqual({
159
- session: "custom-name"
160
- });
161
- expect(registry.lookup(`ai-devkit-${process.pid}`)).toBeNull();
167
+ expect(registry.lookup("custom-name")).toBeNull();
168
+ expect(registry.lookup(`ai-devkit-${process.pid}`)?.runtimeRef).toBeNull();
162
169
  expect(registry.list()).toHaveLength(1);
163
170
  });
164
171
  it("preserves an existing name conflict when its probe fails with EPERM", ()=>{
@@ -241,7 +248,7 @@ describe("AgentRegistry", ()=>{
241
248
  runtimeRef: null
242
249
  }));
243
250
  expect(registry.list()).toHaveLength(1);
244
- expect(registry.lookup("custom-name")?.pid).toBe(process.pid);
251
+ expect(registry.lookup(`ai-devkit-${process.pid}`)?.pid).toBe(process.pid);
245
252
  });
246
253
  it("cleans up a cross-type row when its pid has been reused", ()=>{
247
254
  registry.register(makeEntry({
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/__tests__/utils/AgentRegistry.test.ts"],"sourcesContent":["import fs from \"fs\";\nimport os from \"os\";\nimport path from \"path\";\nimport Database from \"better-sqlite3\";\nimport {\n AgentRegistry,\n RenameNotFoundError,\n RenameConflictError,\n type RegistryEntry,\n} from \"../../utils/AgentRegistry.js\";\n\nfunction makeEntry(over: Partial<RegistryEntry> = {}): RegistryEntry {\n return {\n name: \"agent1\",\n type: \"claude\",\n pid: process.pid,\n cwd: \"/tmp\",\n startedAt: \"2026-05-30T00:00:00.000Z\",\n sessionId: \"sid-1\",\n sessionFilePath: \"/tmp/session.jsonl\",\n pinned: false,\n runtime: \"tmux\",\n runtimeRef: { session: \"agent1\" },\n ...over,\n };\n}\n\ndescribe(\"AgentRegistry\", () => {\n let tmpDir: string;\n let regPath: string;\n let registry: AgentRegistry;\n\n beforeEach(() => {\n tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), \"agent-registry-\"));\n regPath = path.join(tmpDir, \"nested\", \"agents.json\");\n registry = new AgentRegistry(regPath);\n });\n\n afterEach(() => {\n vi.restoreAllMocks();\n fs.rmSync(tmpDir, { recursive: true, force: true });\n });\n\n describe(\"register\", () => {\n it(\"creates the SQLite database and parent directory if missing\", () => {\n registry.register(makeEntry());\n expect(fs.existsSync(regPath.replace(/\\.json$/, \".db\"))).toBe(true);\n expect(registry.list()[0].name).toBe(\"agent1\");\n });\n\n it(\"appends a new entry when name is unique\", () => {\n registry.register(makeEntry({ name: \"a\" }));\n registry.register(makeEntry({ name: \"b\", pid: process.ppid }));\n expect(registry.list()).toHaveLength(2);\n });\n\n it(\"upserts in place when type and pid already exist\", () => {\n registry.register(makeEntry({ name: \"a\", pid: process.pid }));\n registry.register(makeEntry({ name: \"fallback\", pid: process.pid, runtimeRef: null }));\n const all = registry.list();\n expect(all).toHaveLength(1);\n expect(all[0].pid).toBe(process.pid);\n expect(all[0].name).toBe(\"a\");\n });\n\n it(\"does not write through the legacy fixed .tmp path\", () => {\n registry.register(makeEntry());\n expect(fs.existsSync(`${regPath}.tmp`)).toBe(false);\n });\n\n it(\"persists session fields\", () => {\n registry.register(makeEntry({ sessionId: \"sid-xyz\", sessionFilePath: \"/foo/bar.jsonl\" }));\n const saved = registry.list()[0];\n expect(saved.sessionId).toBe(\"sid-xyz\");\n expect(saved.sessionFilePath).toBe(\"/foo/bar.jsonl\");\n });\n\n it(\"persists runtime metadata for Herdr-backed entries\", () => {\n const runtimeRef = { session: \"default\", paneId: \"w1:p2\", agentName: \"reviewer\" };\n\n registry.register(\n makeEntry({\n name: \"reviewer\",\n runtime: \"herdr\",\n runtimeRef,\n }),\n );\n\n expect(registry.lookup(\"reviewer\")).toMatchObject({\n runtime: \"herdr\",\n runtimeRef,\n });\n });\n\n it(\"lets a Herdr managed start replace an existing same-pid name\", () => {\n const runtimeRef = { session: \"default\", paneId: \"w1:p2\", agentName: \"requested-name\" };\n\n registry.register(makeEntry({ name: \"old-name\", pid: process.pid }));\n registry.register(\n makeEntry({\n name: \"requested-name\",\n pid: process.pid,\n runtime: \"herdr\",\n runtimeRef,\n }),\n );\n\n expect(registry.lookup(\"old-name\")).toBeNull();\n expect(registry.lookup(\"requested-name\")).toMatchObject({\n pid: process.pid,\n runtime: \"herdr\",\n runtimeRef,\n });\n });\n\n it(\"preserves existing runtime ref when incoming runtime ref is empty\", () => {\n registry.register(makeEntry({ name: \"a\", runtimeRef: { session: \"pinned\" } }));\n registry.register(makeEntry({ name: \"fallback\", runtimeRef: null, pid: process.pid }));\n const saved = registry.lookup(\"a\");\n expect(saved?.runtimeRef).toEqual({ session: \"pinned\" });\n expect(saved?.pid).toBe(process.pid);\n });\n\n it(\"defaults an empty tmux runtime ref from the tmux session\", () => {\n registry.register(makeEntry({ name: \"a\", runtimeRef: null }));\n\n expect(registry.lookup(\"a\")?.runtimeRef).toBeNull();\n });\n\n it(\"lets a managed start entry replace a generated fallback for the same pid\", () => {\n registry.register(makeEntry({ name: `ai-devkit-${process.pid}`, runtimeRef: null }));\n registry.register(makeEntry({ name: \"custom-name\", runtimeRef: { session: \"custom-name\" } }));\n expect(registry.lookup(\"custom-name\")?.runtimeRef).toEqual({ session: \"custom-name\" });\n expect(registry.lookup(`ai-devkit-${process.pid}`)).toBeNull();\n expect(registry.list()).toHaveLength(1);\n });\n\n it(\"preserves an existing name conflict when its probe fails with EPERM\", () => {\n registry.register(makeEntry({ name: \"claimed-name\", pid: process.pid }));\n vi.spyOn(process, \"kill\").mockImplementation(() => {\n throw Object.assign(new Error(\"operation not permitted\"), { code: \"EPERM\" });\n });\n\n expect(() =>\n registry.register(\n makeEntry({\n name: \"claimed-name\",\n pid: process.pid + 1,\n }),\n ),\n ).toThrow();\n expect(registry.lookup(\"claimed-name\")?.pid).toBe(process.pid);\n });\n });\n\n describe(\"registerBatch\", () => {\n it(\"is a no-op on empty array\", () => {\n registry.registerBatch([]);\n expect(fs.existsSync(regPath)).toBe(false);\n });\n\n it(\"upserts multiple entries in a single batch\", () => {\n registry.registerBatch([\n makeEntry({ name: \"a\" }),\n makeEntry({ name: \"b\", pid: process.pid + 1 }),\n makeEntry({ name: \"c\", pid: process.pid + 2 }),\n ]);\n expect(registry.list()).toHaveLength(3);\n });\n\n it(\"applies the tmux runtime ref merge per entry\", () => {\n registry.register(makeEntry({ name: \"a\", runtimeRef: { session: \"pinned\" } }));\n registry.registerBatch([\n makeEntry({ name: \"fallback\", runtimeRef: null, pid: process.pid }),\n makeEntry({ name: \"b\", runtimeRef: null, pid: process.pid + 1 }),\n ]);\n expect(registry.lookup(\"a\")?.runtimeRef).toEqual({ session: \"pinned\" });\n expect(registry.lookup(\"a\")?.pid).toBe(process.pid);\n expect(registry.lookup(\"b\")?.runtimeRef).toBeNull();\n });\n\n it(\"handles concurrent registry instances without duplicate pid rows\", () => {\n const other = new AgentRegistry(regPath);\n registry.register(makeEntry({ name: `ai-devkit-${process.pid}`, runtimeRef: null }));\n other.register(makeEntry({ name: \"custom-name\", runtimeRef: { session: \"custom-name\" } }));\n registry.register(makeEntry({ name: `ai-devkit-${process.pid}`, runtimeRef: null }));\n\n expect(registry.list()).toHaveLength(1);\n expect(registry.lookup(\"custom-name\")?.pid).toBe(process.pid);\n });\n\n it(\"cleans up a cross-type row when its pid has been reused\", () => {\n registry.register(makeEntry({ name: \"old-claude\", type: \"claude\", pid: process.pid }));\n\n registry.register(\n makeEntry({\n name: \"new-codex\",\n type: \"codex\",\n pid: process.pid,\n runtimeRef: null,\n }),\n );\n\n expect(registry.lookup(\"old-claude\")).toBeNull();\n expect(registry.lookup(\"new-codex\")).toMatchObject({ type: \"codex\", pid: process.pid });\n expect(registry.list()).toHaveLength(1);\n });\n\n it(\"rolls back the whole batch when a live name conflict rejects one entry\", () => {\n registry.register(makeEntry({ name: \"taken\", pid: process.pid }));\n\n expect(() =>\n registry.registerBatch([\n makeEntry({ name: \"fresh\", pid: 999998 }),\n makeEntry({ name: \"taken\", type: \"codex\", pid: 999997 }),\n ]),\n ).toThrow(/UNIQUE constraint failed/);\n\n expect(registry.lookup(\"fresh\")).toBeNull();\n expect(registry.lookup(\"taken\")?.pid).toBe(process.pid);\n });\n });\n\n describe(\"lookup\", () => {\n it(\"returns null when name not found\", () => {\n expect(registry.lookup(\"missing\")).toBeNull();\n });\n\n it(\"returns the entry when name matches\", () => {\n registry.register(makeEntry({ name: \"a\" }));\n expect(registry.lookup(\"a\")?.name).toBe(\"a\");\n });\n });\n\n describe(\"pinning\", () => {\n it(\"defaults new rows to unpinned and toggles the persisted state\", () => {\n registry.register(makeEntry());\n\n expect(registry.lookup(\"agent1\")?.pinned).toBe(false);\n expect(registry.togglePin(\"claude\", process.pid)).toBe(true);\n expect(registry.lookup(\"agent1\")?.pinned).toBe(true);\n expect(registry.togglePin(\"claude\", process.pid)).toBe(false);\n expect(registry.lookup(\"agent1\")?.pinned).toBe(false);\n });\n\n it(\"updates existing recency when toggled\", () => {\n let now = new Date(\"2026-08-16T10:00:00.000Z\");\n const clocked = new AgentRegistry(regPath, { now: () => now });\n clocked.register(makeEntry());\n now = new Date(\"2026-08-16T10:01:00.000Z\");\n\n clocked.togglePin(\"claude\", process.pid);\n\n expect(clocked.lookup(\"agent1\")?.updatedAt).toBe(now.toISOString());\n const db = new Database(regPath.replace(/\\.json$/, \".db\"), { readonly: true });\n const row = db\n .prepare(\"SELECT updated_at FROM agents WHERE type = ? AND pid = ?\")\n .get(\"claude\", process.pid) as { updated_at: string };\n db.close();\n expect(row.updated_at).toBe(now.toISOString());\n });\n\n it(\"returns null when the process row has disappeared\", () => {\n expect(registry.togglePin(\"claude\", 999999)).toBeNull();\n });\n\n it(\"preserves a pin when poll registration updates the row\", () => {\n registry.register(makeEntry({ sessionId: \"before\" }));\n registry.togglePin(\"claude\", process.pid);\n\n registry.register(makeEntry({ sessionId: \"after\" }));\n\n expect(registry.lookup(\"agent1\")).toMatchObject({ sessionId: \"after\", pinned: true });\n });\n\n it(\"preserves a pin through rename\", () => {\n registry.register(makeEntry({ name: \"before\" }));\n registry.togglePin(\"claude\", process.pid);\n\n registry.rename(\"before\", \"after\");\n\n expect(registry.lookup(\"after\")?.pinned).toBe(true);\n });\n\n it(\"removes the pin with a pruned process row\", () => {\n registry.register(makeEntry({ pid: 999999 }));\n registry.togglePin(\"claude\", 999999);\n\n registry.prune();\n\n expect(registry.lookup(\"agent1\")).toBeNull();\n });\n\n it(\"reports a clear error when a readonly registry toggles a pin\", () => {\n registry.register(makeEntry());\n const readonlyRegistry = new AgentRegistry(regPath, { readonly: true });\n\n expect(() => readonlyRegistry.togglePin(\"claude\", process.pid)).toThrow(/readonly/i);\n });\n });\n\n describe(\"list\", () => {\n it(\"returns empty array when database does not contain entries\", () => {\n expect(registry.list()).toEqual([]);\n });\n\n it(\"ignores existing legacy agents.json entries\", () => {\n const legacyEntry = makeEntry({ name: \"legacy\", runtimeRef: { session: \"legacy\" } });\n fs.mkdirSync(path.dirname(regPath), { recursive: true });\n fs.writeFileSync(regPath, JSON.stringify({ entries: [legacyEntry] }), \"utf8\");\n\n const legacyRegistry = new AgentRegistry(regPath);\n\n expect(legacyRegistry.lookup(\"legacy\")).toBeNull();\n expect(legacyRegistry.list()).toEqual([]);\n expect(fs.existsSync(regPath.replace(/\\.json$/, \".db\"))).toBe(true);\n });\n\n it(\"reads pre-runtime SQLite rows as tmux-backed records\", () => {\n const dbPath = regPath.replace(/\\.json$/, \".db\");\n fs.mkdirSync(path.dirname(dbPath), { recursive: true });\n fs.rmSync(dbPath, { force: true });\n fs.rmSync(`${dbPath}-wal`, { force: true });\n fs.rmSync(`${dbPath}-shm`, { force: true });\n const db = new Database(dbPath);\n db.exec(`\n CREATE TABLE agents (\n type TEXT NOT NULL,\n pid INTEGER NOT NULL,\n name TEXT NOT NULL UNIQUE,\n tmux_session TEXT NOT NULL DEFAULT '',\n cwd TEXT NOT NULL DEFAULT '',\n started_at TEXT NOT NULL,\n session_id TEXT NOT NULL DEFAULT '',\n session_file_path TEXT NOT NULL DEFAULT '',\n updated_at TEXT NOT NULL,\n PRIMARY KEY (type, pid)\n );\n INSERT INTO agents (\n type, pid, name, tmux_session, cwd, started_at, session_id, session_file_path, updated_at\n ) VALUES (\n 'claude', ${process.pid}, 'legacy-sqlite', 'legacy-sqlite', '/tmp',\n '2026-05-30T00:00:00.000Z', 'sid-1', '/tmp/session.jsonl', '2026-05-30T00:00:00.000Z'\n );\n PRAGMA user_version = 1;\n `);\n db.close();\n\n const migratedRegistry = new AgentRegistry(regPath);\n\n expect(migratedRegistry.lookup(\"legacy-sqlite\")).toMatchObject({\n runtime: \"tmux\",\n runtimeRef: { session: \"legacy-sqlite\" },\n });\n });\n });\n\n describe(\"isAlive\", () => {\n it(\"returns true for the current process\", () => {\n expect(registry.isAlive(makeEntry({ pid: process.pid }))).toBe(true);\n });\n\n it(\"returns false for a PID that does not exist\", () => {\n expect(registry.isAlive(makeEntry({ pid: 999999 }))).toBe(false);\n });\n\n it(\"returns true when the process probe is forbidden with EPERM\", () => {\n vi.spyOn(process, \"kill\").mockImplementation(() => {\n throw Object.assign(new Error(\"operation not permitted\"), { code: \"EPERM\" });\n });\n\n expect(registry.isAlive(makeEntry())).toBe(true);\n });\n\n it(\"returns false when the process probe reports ESRCH\", () => {\n vi.spyOn(process, \"kill\").mockImplementation(() => {\n throw Object.assign(new Error(\"no such process\"), { code: \"ESRCH\" });\n });\n\n expect(registry.isAlive(makeEntry())).toBe(false);\n });\n\n it(\"returns true when the process probe fails without a definitive error code\", () => {\n vi.spyOn(process, \"kill\").mockImplementation(() => {\n throw new Error(\"indeterminate probe failure\");\n });\n\n expect(registry.isAlive(makeEntry())).toBe(true);\n });\n });\n\n describe(\"prune\", () => {\n it(\"removes entries whose PIDs are dead\", () => {\n registry.register(makeEntry({ name: \"alive\", pid: process.pid }));\n registry.register(makeEntry({ name: \"dead\", pid: 999999 }));\n registry.prune();\n const remaining = registry.list();\n expect(remaining).toHaveLength(1);\n expect(remaining[0].name).toBe(\"alive\");\n });\n\n it(\"is a no-op when all entries are alive\", () => {\n registry.register(makeEntry({ pid: process.pid }));\n const before = registry.list();\n registry.prune();\n const after = registry.list();\n expect(after).toEqual(before);\n });\n\n it(\"preserves entries when liveness probing fails with EPERM\", () => {\n registry.register(makeEntry({ name: \"custom-name\", runtimeRef: { session: \"tmux-custom\" } }));\n vi.spyOn(process, \"kill\").mockImplementation(() => {\n throw Object.assign(new Error(\"operation not permitted\"), { code: \"EPERM\" });\n });\n\n registry.prune();\n\n expect(registry.lookup(\"custom-name\")).toMatchObject({\n name: \"custom-name\",\n runtimeRef: { session: \"tmux-custom\" },\n });\n });\n\n it(\"removes entries when liveness probing fails with ESRCH\", () => {\n registry.register(makeEntry({ name: \"dead\" }));\n vi.spyOn(process, \"kill\").mockImplementation(() => {\n throw Object.assign(new Error(\"no such process\"), { code: \"ESRCH\" });\n });\n\n registry.prune();\n\n expect(registry.lookup(\"dead\")).toBeNull();\n });\n\n it(\"does nothing when file is missing\", () => {\n expect(() => registry.prune()).not.toThrow();\n });\n\n it(\"keeps forced prune available before the passive cadence is due\", () => {\n let nowMs = Date.parse(\"2026-08-14T10:00:00.000Z\");\n const clocked = new AgentRegistry(regPath, {\n now: () => new Date(nowMs),\n pruneIntervalMs: 30_000,\n });\n clocked.register(makeEntry({ name: \"forced\", pid: process.pid }));\n const alive = vi.spyOn(clocked, \"isAlive\").mockReturnValue(true);\n clocked.pruneIfDue();\n alive.mockReturnValue(false);\n nowMs += 1;\n\n clocked.prune();\n\n expect(alive).toHaveBeenCalledTimes(2);\n expect(clocked.lookup(\"forced\")).toBeNull();\n });\n });\n\n describe(\"default()\", () => {\n it(\"returns a singleton instance\", () => {\n expect(AgentRegistry.default()).toBe(AgentRegistry.default());\n });\n });\n\n describe(\"rename\", () => {\n it(\"updates the name of an existing entry\", () => {\n registry.register(makeEntry({ name: \"old-name\", pid: process.pid }));\n registry.rename(\"old-name\", \"new-name\");\n expect(registry.lookup(\"new-name\")?.name).toBe(\"new-name\");\n expect(registry.lookup(\"old-name\")).toBeNull();\n });\n\n it(\"preserves all other fields on the renamed entry\", () => {\n registry.register(\n makeEntry({\n name: \"old-name\",\n pid: process.pid,\n runtimeRef: { session: \"old-name\" },\n cwd: \"/my/cwd\",\n }),\n );\n registry.rename(\"old-name\", \"new-name\");\n const entry = registry.lookup(\"new-name\");\n expect(entry?.runtimeRef).toEqual({ session: \"old-name\" });\n expect(entry?.cwd).toBe(\"/my/cwd\");\n expect(entry?.pid).toBe(process.pid);\n });\n\n it(\"throws RenameNotFoundError when current name does not exist\", () => {\n expect(() => registry.rename(\"ghost\", \"new-name\")).toThrow(RenameNotFoundError);\n });\n\n it(\"throws RenameConflictError when new name is already in use by a live entry\", () => {\n registry.register(makeEntry({ name: \"agent-a\", pid: process.pid }));\n registry.register(makeEntry({ name: \"agent-b\", pid: process.ppid }));\n expect(() => registry.rename(\"agent-a\", \"agent-b\")).toThrow(RenameConflictError);\n });\n\n it(\"throws RenameConflictError when the conflicting entry probe fails with EPERM\", () => {\n registry.register(makeEntry({ name: \"agent-a\", pid: process.pid }));\n registry.register(makeEntry({ name: \"agent-b\", pid: process.ppid }));\n vi.spyOn(process, \"kill\").mockImplementation(() => {\n throw Object.assign(new Error(\"operation not permitted\"), { code: \"EPERM\" });\n });\n\n expect(() => registry.rename(\"agent-a\", \"agent-b\")).toThrow(RenameConflictError);\n expect(registry.lookup(\"agent-b\")?.pid).toBe(process.ppid);\n });\n\n it(\"succeeds when new name exists only as a stale (dead) entry\", () => {\n registry.register(makeEntry({ name: \"agent-a\", pid: process.pid }));\n registry.register(makeEntry({ name: \"agent-b\", pid: 999999 }));\n expect(() => registry.rename(\"agent-a\", \"agent-b\")).not.toThrow();\n expect(registry.lookup(\"agent-b\")?.pid).toBe(process.pid);\n });\n\n it(\"does not create the legacy fixed .tmp path on rename\", () => {\n registry.register(makeEntry({ name: \"old-name\", pid: process.pid }));\n registry.rename(\"old-name\", \"new-name\");\n expect(fs.existsSync(`${regPath}.tmp`)).toBe(false);\n });\n });\n});\n"],"names":["fs","os","path","Database","AgentRegistry","RenameNotFoundError","RenameConflictError","makeEntry","over","name","type","pid","process","cwd","startedAt","sessionId","sessionFilePath","pinned","runtime","runtimeRef","session","describe","tmpDir","regPath","registry","beforeEach","mkdtempSync","join","tmpdir","afterEach","vi","restoreAllMocks","rmSync","recursive","force","it","register","expect","existsSync","replace","toBe","list","ppid","toHaveLength","all","saved","paneId","agentName","lookup","toMatchObject","toBeNull","toEqual","spyOn","mockImplementation","Object","assign","Error","code","toThrow","registerBatch","other","togglePin","now","Date","clocked","updatedAt","toISOString","db","readonly","row","prepare","get","close","updated_at","rename","prune","readonlyRegistry","legacyEntry","mkdirSync","dirname","writeFileSync","JSON","stringify","entries","legacyRegistry","dbPath","exec","migratedRegistry","isAlive","remaining","before","after","not","nowMs","parse","pruneIntervalMs","alive","mockReturnValue","pruneIfDue","toHaveBeenCalledTimes","default","entry"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/__tests__/utils/AgentRegistry.test.ts"],"sourcesContent":["import fs from \"fs\";\nimport os from \"os\";\nimport path from \"path\";\nimport Database from \"better-sqlite3\";\nimport {\n AgentRegistry,\n RenameNotFoundError,\n RenameConflictError,\n type RegistryEntry,\n} from \"../../utils/AgentRegistry.js\";\n\nfunction makeEntry(over: Partial<RegistryEntry> = {}): RegistryEntry {\n return {\n name: \"agent1\",\n type: \"claude\",\n pid: process.pid,\n cwd: \"/tmp\",\n startedAt: \"2026-05-30T00:00:00.000Z\",\n sessionId: \"sid-1\",\n sessionFilePath: \"/tmp/session.jsonl\",\n pinned: false,\n runtime: \"tmux\",\n runtimeRef: { session: \"agent1\" },\n ...over,\n };\n}\n\ndescribe(\"AgentRegistry\", () => {\n let tmpDir: string;\n let regPath: string;\n let registry: AgentRegistry;\n\n beforeEach(() => {\n tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), \"agent-registry-\"));\n regPath = path.join(tmpDir, \"nested\", \"agents.json\");\n registry = new AgentRegistry(regPath);\n });\n\n afterEach(() => {\n vi.restoreAllMocks();\n fs.rmSync(tmpDir, { recursive: true, force: true });\n });\n\n describe(\"register\", () => {\n it(\"creates the SQLite database and parent directory if missing\", () => {\n registry.register(makeEntry());\n expect(fs.existsSync(regPath.replace(/\\.json$/, \".db\"))).toBe(true);\n expect(registry.list()[0].name).toBe(\"agent1\");\n });\n\n it(\"appends a new entry when name is unique\", () => {\n registry.register(makeEntry({ name: \"a\" }));\n registry.register(makeEntry({ name: \"b\", pid: process.ppid }));\n expect(registry.list()).toHaveLength(2);\n });\n\n it(\"upserts in place when type and pid already exist\", () => {\n registry.register(makeEntry({ name: \"a\", pid: process.pid }));\n registry.register(makeEntry({ name: \"fallback\", pid: process.pid, runtimeRef: null }));\n const all = registry.list();\n expect(all).toHaveLength(1);\n expect(all[0].pid).toBe(process.pid);\n expect(all[0].name).toBe(\"a\");\n });\n\n it(\"does not write through the legacy fixed .tmp path\", () => {\n registry.register(makeEntry());\n expect(fs.existsSync(`${regPath}.tmp`)).toBe(false);\n });\n\n it(\"persists session fields\", () => {\n registry.register(makeEntry({ sessionId: \"sid-xyz\", sessionFilePath: \"/foo/bar.jsonl\" }));\n const saved = registry.list()[0];\n expect(saved.sessionId).toBe(\"sid-xyz\");\n expect(saved.sessionFilePath).toBe(\"/foo/bar.jsonl\");\n });\n\n it(\"persists runtime metadata for Herdr-backed entries\", () => {\n const runtimeRef = { session: \"default\", paneId: \"w1:p2\", agentName: \"reviewer\" };\n\n registry.register(\n makeEntry({\n name: \"reviewer\",\n runtime: \"herdr\",\n runtimeRef,\n }),\n );\n\n expect(registry.lookup(\"reviewer\")).toMatchObject({\n runtime: \"herdr\",\n runtimeRef,\n });\n });\n\n it(\"preserves existing identity and routing fields for same-pid refreshes\", () => {\n const runtimeRef = { session: \"default\", paneId: \"w1:p2\", agentName: \"requested-name\" };\n\n registry.register(\n makeEntry({\n name: \"old-name\",\n pid: process.pid,\n runtime: \"tmux\",\n runtimeRef: { session: \"old-name\" },\n cwd: \"/original\",\n }),\n );\n registry.register(\n makeEntry({\n name: \"requested-name\",\n pid: process.pid,\n runtime: \"herdr\",\n runtimeRef,\n cwd: \"/incoming\",\n }),\n );\n\n expect(registry.lookup(\"requested-name\")).toBeNull();\n expect(registry.lookup(\"old-name\")).toMatchObject({\n pid: process.pid,\n runtime: \"tmux\",\n runtimeRef: { session: \"old-name\" },\n cwd: \"/original\",\n });\n });\n\n it(\"preserves existing runtime ref when incoming runtime ref is empty\", () => {\n registry.register(makeEntry({ name: \"a\", runtimeRef: { session: \"pinned\" } }));\n registry.register(makeEntry({ name: \"fallback\", runtimeRef: null, pid: process.pid }));\n const saved = registry.lookup(\"a\");\n expect(saved?.runtimeRef).toEqual({ session: \"pinned\" });\n expect(saved?.pid).toBe(process.pid);\n });\n\n it(\"defaults an empty tmux runtime ref from the tmux session\", () => {\n registry.register(makeEntry({ name: \"a\", runtimeRef: null }));\n\n expect(registry.lookup(\"a\")?.runtimeRef).toBeNull();\n });\n\n it(\"preserves an existing generated fallback for the same pid\", () => {\n registry.register(makeEntry({ name: `ai-devkit-${process.pid}`, runtimeRef: null }));\n registry.register(makeEntry({ name: \"custom-name\", runtimeRef: { session: \"custom-name\" } }));\n expect(registry.lookup(\"custom-name\")).toBeNull();\n expect(registry.lookup(`ai-devkit-${process.pid}`)?.runtimeRef).toBeNull();\n expect(registry.list()).toHaveLength(1);\n });\n\n it(\"preserves an existing name conflict when its probe fails with EPERM\", () => {\n registry.register(makeEntry({ name: \"claimed-name\", pid: process.pid }));\n vi.spyOn(process, \"kill\").mockImplementation(() => {\n throw Object.assign(new Error(\"operation not permitted\"), { code: \"EPERM\" });\n });\n\n expect(() =>\n registry.register(\n makeEntry({\n name: \"claimed-name\",\n pid: process.pid + 1,\n }),\n ),\n ).toThrow();\n expect(registry.lookup(\"claimed-name\")?.pid).toBe(process.pid);\n });\n });\n\n describe(\"registerBatch\", () => {\n it(\"is a no-op on empty array\", () => {\n registry.registerBatch([]);\n expect(fs.existsSync(regPath)).toBe(false);\n });\n\n it(\"upserts multiple entries in a single batch\", () => {\n registry.registerBatch([\n makeEntry({ name: \"a\" }),\n makeEntry({ name: \"b\", pid: process.pid + 1 }),\n makeEntry({ name: \"c\", pid: process.pid + 2 }),\n ]);\n expect(registry.list()).toHaveLength(3);\n });\n\n it(\"applies the tmux runtime ref merge per entry\", () => {\n registry.register(makeEntry({ name: \"a\", runtimeRef: { session: \"pinned\" } }));\n registry.registerBatch([\n makeEntry({ name: \"fallback\", runtimeRef: null, pid: process.pid }),\n makeEntry({ name: \"b\", runtimeRef: null, pid: process.pid + 1 }),\n ]);\n expect(registry.lookup(\"a\")?.runtimeRef).toEqual({ session: \"pinned\" });\n expect(registry.lookup(\"a\")?.pid).toBe(process.pid);\n expect(registry.lookup(\"b\")?.runtimeRef).toBeNull();\n });\n\n it(\"handles concurrent registry instances without duplicate pid rows\", () => {\n const other = new AgentRegistry(regPath);\n registry.register(makeEntry({ name: `ai-devkit-${process.pid}`, runtimeRef: null }));\n other.register(makeEntry({ name: \"custom-name\", runtimeRef: { session: \"custom-name\" } }));\n registry.register(makeEntry({ name: `ai-devkit-${process.pid}`, runtimeRef: null }));\n\n expect(registry.list()).toHaveLength(1);\n expect(registry.lookup(`ai-devkit-${process.pid}`)?.pid).toBe(process.pid);\n });\n\n it(\"cleans up a cross-type row when its pid has been reused\", () => {\n registry.register(makeEntry({ name: \"old-claude\", type: \"claude\", pid: process.pid }));\n\n registry.register(\n makeEntry({\n name: \"new-codex\",\n type: \"codex\",\n pid: process.pid,\n runtimeRef: null,\n }),\n );\n\n expect(registry.lookup(\"old-claude\")).toBeNull();\n expect(registry.lookup(\"new-codex\")).toMatchObject({ type: \"codex\", pid: process.pid });\n expect(registry.list()).toHaveLength(1);\n });\n\n it(\"rolls back the whole batch when a live name conflict rejects one entry\", () => {\n registry.register(makeEntry({ name: \"taken\", pid: process.pid }));\n\n expect(() =>\n registry.registerBatch([\n makeEntry({ name: \"fresh\", pid: 999998 }),\n makeEntry({ name: \"taken\", type: \"codex\", pid: 999997 }),\n ]),\n ).toThrow(/UNIQUE constraint failed/);\n\n expect(registry.lookup(\"fresh\")).toBeNull();\n expect(registry.lookup(\"taken\")?.pid).toBe(process.pid);\n });\n });\n\n describe(\"lookup\", () => {\n it(\"returns null when name not found\", () => {\n expect(registry.lookup(\"missing\")).toBeNull();\n });\n\n it(\"returns the entry when name matches\", () => {\n registry.register(makeEntry({ name: \"a\" }));\n expect(registry.lookup(\"a\")?.name).toBe(\"a\");\n });\n });\n\n describe(\"pinning\", () => {\n it(\"defaults new rows to unpinned and toggles the persisted state\", () => {\n registry.register(makeEntry());\n\n expect(registry.lookup(\"agent1\")?.pinned).toBe(false);\n expect(registry.togglePin(\"claude\", process.pid)).toBe(true);\n expect(registry.lookup(\"agent1\")?.pinned).toBe(true);\n expect(registry.togglePin(\"claude\", process.pid)).toBe(false);\n expect(registry.lookup(\"agent1\")?.pinned).toBe(false);\n });\n\n it(\"updates existing recency when toggled\", () => {\n let now = new Date(\"2026-08-16T10:00:00.000Z\");\n const clocked = new AgentRegistry(regPath, { now: () => now });\n clocked.register(makeEntry());\n now = new Date(\"2026-08-16T10:01:00.000Z\");\n\n clocked.togglePin(\"claude\", process.pid);\n\n expect(clocked.lookup(\"agent1\")?.updatedAt).toBe(now.toISOString());\n const db = new Database(regPath.replace(/\\.json$/, \".db\"), { readonly: true });\n const row = db\n .prepare(\"SELECT updated_at FROM agents WHERE type = ? AND pid = ?\")\n .get(\"claude\", process.pid) as { updated_at: string };\n db.close();\n expect(row.updated_at).toBe(now.toISOString());\n });\n\n it(\"returns null when the process row has disappeared\", () => {\n expect(registry.togglePin(\"claude\", 999999)).toBeNull();\n });\n\n it(\"preserves a pin when poll registration updates the row\", () => {\n registry.register(makeEntry({ sessionId: \"before\" }));\n registry.togglePin(\"claude\", process.pid);\n\n registry.register(makeEntry({ sessionId: \"after\" }));\n\n expect(registry.lookup(\"agent1\")).toMatchObject({ sessionId: \"after\", pinned: true });\n });\n\n it(\"preserves a pin through rename\", () => {\n registry.register(makeEntry({ name: \"before\" }));\n registry.togglePin(\"claude\", process.pid);\n\n registry.rename(\"before\", \"after\");\n\n expect(registry.lookup(\"after\")?.pinned).toBe(true);\n });\n\n it(\"removes the pin with a pruned process row\", () => {\n registry.register(makeEntry({ pid: 999999 }));\n registry.togglePin(\"claude\", 999999);\n\n registry.prune();\n\n expect(registry.lookup(\"agent1\")).toBeNull();\n });\n\n it(\"reports a clear error when a readonly registry toggles a pin\", () => {\n registry.register(makeEntry());\n const readonlyRegistry = new AgentRegistry(regPath, { readonly: true });\n\n expect(() => readonlyRegistry.togglePin(\"claude\", process.pid)).toThrow(/readonly/i);\n });\n });\n\n describe(\"list\", () => {\n it(\"returns empty array when database does not contain entries\", () => {\n expect(registry.list()).toEqual([]);\n });\n\n it(\"ignores existing legacy agents.json entries\", () => {\n const legacyEntry = makeEntry({ name: \"legacy\", runtimeRef: { session: \"legacy\" } });\n fs.mkdirSync(path.dirname(regPath), { recursive: true });\n fs.writeFileSync(regPath, JSON.stringify({ entries: [legacyEntry] }), \"utf8\");\n\n const legacyRegistry = new AgentRegistry(regPath);\n\n expect(legacyRegistry.lookup(\"legacy\")).toBeNull();\n expect(legacyRegistry.list()).toEqual([]);\n expect(fs.existsSync(regPath.replace(/\\.json$/, \".db\"))).toBe(true);\n });\n\n it(\"reads pre-runtime SQLite rows as tmux-backed records\", () => {\n const dbPath = regPath.replace(/\\.json$/, \".db\");\n fs.mkdirSync(path.dirname(dbPath), { recursive: true });\n fs.rmSync(dbPath, { force: true });\n fs.rmSync(`${dbPath}-wal`, { force: true });\n fs.rmSync(`${dbPath}-shm`, { force: true });\n const db = new Database(dbPath);\n db.exec(`\n CREATE TABLE agents (\n type TEXT NOT NULL,\n pid INTEGER NOT NULL,\n name TEXT NOT NULL UNIQUE,\n tmux_session TEXT NOT NULL DEFAULT '',\n cwd TEXT NOT NULL DEFAULT '',\n started_at TEXT NOT NULL,\n session_id TEXT NOT NULL DEFAULT '',\n session_file_path TEXT NOT NULL DEFAULT '',\n updated_at TEXT NOT NULL,\n PRIMARY KEY (type, pid)\n );\n INSERT INTO agents (\n type, pid, name, tmux_session, cwd, started_at, session_id, session_file_path, updated_at\n ) VALUES (\n 'claude', ${process.pid}, 'legacy-sqlite', 'legacy-sqlite', '/tmp',\n '2026-05-30T00:00:00.000Z', 'sid-1', '/tmp/session.jsonl', '2026-05-30T00:00:00.000Z'\n );\n PRAGMA user_version = 1;\n `);\n db.close();\n\n const migratedRegistry = new AgentRegistry(regPath);\n\n expect(migratedRegistry.lookup(\"legacy-sqlite\")).toMatchObject({\n runtime: \"tmux\",\n runtimeRef: { session: \"legacy-sqlite\" },\n });\n });\n });\n\n describe(\"isAlive\", () => {\n it(\"returns true for the current process\", () => {\n expect(registry.isAlive(makeEntry({ pid: process.pid }))).toBe(true);\n });\n\n it(\"returns false for a PID that does not exist\", () => {\n expect(registry.isAlive(makeEntry({ pid: 999999 }))).toBe(false);\n });\n\n it(\"returns true when the process probe is forbidden with EPERM\", () => {\n vi.spyOn(process, \"kill\").mockImplementation(() => {\n throw Object.assign(new Error(\"operation not permitted\"), { code: \"EPERM\" });\n });\n\n expect(registry.isAlive(makeEntry())).toBe(true);\n });\n\n it(\"returns false when the process probe reports ESRCH\", () => {\n vi.spyOn(process, \"kill\").mockImplementation(() => {\n throw Object.assign(new Error(\"no such process\"), { code: \"ESRCH\" });\n });\n\n expect(registry.isAlive(makeEntry())).toBe(false);\n });\n\n it(\"returns true when the process probe fails without a definitive error code\", () => {\n vi.spyOn(process, \"kill\").mockImplementation(() => {\n throw new Error(\"indeterminate probe failure\");\n });\n\n expect(registry.isAlive(makeEntry())).toBe(true);\n });\n });\n\n describe(\"prune\", () => {\n it(\"removes entries whose PIDs are dead\", () => {\n registry.register(makeEntry({ name: \"alive\", pid: process.pid }));\n registry.register(makeEntry({ name: \"dead\", pid: 999999 }));\n registry.prune();\n const remaining = registry.list();\n expect(remaining).toHaveLength(1);\n expect(remaining[0].name).toBe(\"alive\");\n });\n\n it(\"is a no-op when all entries are alive\", () => {\n registry.register(makeEntry({ pid: process.pid }));\n const before = registry.list();\n registry.prune();\n const after = registry.list();\n expect(after).toEqual(before);\n });\n\n it(\"preserves entries when liveness probing fails with EPERM\", () => {\n registry.register(makeEntry({ name: \"custom-name\", runtimeRef: { session: \"tmux-custom\" } }));\n vi.spyOn(process, \"kill\").mockImplementation(() => {\n throw Object.assign(new Error(\"operation not permitted\"), { code: \"EPERM\" });\n });\n\n registry.prune();\n\n expect(registry.lookup(\"custom-name\")).toMatchObject({\n name: \"custom-name\",\n runtimeRef: { session: \"tmux-custom\" },\n });\n });\n\n it(\"removes entries when liveness probing fails with ESRCH\", () => {\n registry.register(makeEntry({ name: \"dead\" }));\n vi.spyOn(process, \"kill\").mockImplementation(() => {\n throw Object.assign(new Error(\"no such process\"), { code: \"ESRCH\" });\n });\n\n registry.prune();\n\n expect(registry.lookup(\"dead\")).toBeNull();\n });\n\n it(\"does nothing when file is missing\", () => {\n expect(() => registry.prune()).not.toThrow();\n });\n\n it(\"keeps forced prune available before the passive cadence is due\", () => {\n let nowMs = Date.parse(\"2026-08-14T10:00:00.000Z\");\n const clocked = new AgentRegistry(regPath, {\n now: () => new Date(nowMs),\n pruneIntervalMs: 30_000,\n });\n clocked.register(makeEntry({ name: \"forced\", pid: process.pid }));\n const alive = vi.spyOn(clocked, \"isAlive\").mockReturnValue(true);\n clocked.pruneIfDue();\n alive.mockReturnValue(false);\n nowMs += 1;\n\n clocked.prune();\n\n expect(alive).toHaveBeenCalledTimes(2);\n expect(clocked.lookup(\"forced\")).toBeNull();\n });\n });\n\n describe(\"default()\", () => {\n it(\"returns a singleton instance\", () => {\n expect(AgentRegistry.default()).toBe(AgentRegistry.default());\n });\n });\n\n describe(\"rename\", () => {\n it(\"updates the name of an existing entry\", () => {\n registry.register(makeEntry({ name: \"old-name\", pid: process.pid }));\n registry.rename(\"old-name\", \"new-name\");\n expect(registry.lookup(\"new-name\")?.name).toBe(\"new-name\");\n expect(registry.lookup(\"old-name\")).toBeNull();\n });\n\n it(\"preserves all other fields on the renamed entry\", () => {\n registry.register(\n makeEntry({\n name: \"old-name\",\n pid: process.pid,\n runtimeRef: { session: \"old-name\" },\n cwd: \"/my/cwd\",\n }),\n );\n registry.rename(\"old-name\", \"new-name\");\n const entry = registry.lookup(\"new-name\");\n expect(entry?.runtimeRef).toEqual({ session: \"old-name\" });\n expect(entry?.cwd).toBe(\"/my/cwd\");\n expect(entry?.pid).toBe(process.pid);\n });\n\n it(\"throws RenameNotFoundError when current name does not exist\", () => {\n expect(() => registry.rename(\"ghost\", \"new-name\")).toThrow(RenameNotFoundError);\n });\n\n it(\"throws RenameConflictError when new name is already in use by a live entry\", () => {\n registry.register(makeEntry({ name: \"agent-a\", pid: process.pid }));\n registry.register(makeEntry({ name: \"agent-b\", pid: process.ppid }));\n expect(() => registry.rename(\"agent-a\", \"agent-b\")).toThrow(RenameConflictError);\n });\n\n it(\"throws RenameConflictError when the conflicting entry probe fails with EPERM\", () => {\n registry.register(makeEntry({ name: \"agent-a\", pid: process.pid }));\n registry.register(makeEntry({ name: \"agent-b\", pid: process.ppid }));\n vi.spyOn(process, \"kill\").mockImplementation(() => {\n throw Object.assign(new Error(\"operation not permitted\"), { code: \"EPERM\" });\n });\n\n expect(() => registry.rename(\"agent-a\", \"agent-b\")).toThrow(RenameConflictError);\n expect(registry.lookup(\"agent-b\")?.pid).toBe(process.ppid);\n });\n\n it(\"succeeds when new name exists only as a stale (dead) entry\", () => {\n registry.register(makeEntry({ name: \"agent-a\", pid: process.pid }));\n registry.register(makeEntry({ name: \"agent-b\", pid: 999999 }));\n expect(() => registry.rename(\"agent-a\", \"agent-b\")).not.toThrow();\n expect(registry.lookup(\"agent-b\")?.pid).toBe(process.pid);\n });\n\n it(\"does not create the legacy fixed .tmp path on rename\", () => {\n registry.register(makeEntry({ name: \"old-name\", pid: process.pid }));\n registry.rename(\"old-name\", \"new-name\");\n expect(fs.existsSync(`${regPath}.tmp`)).toBe(false);\n });\n });\n});\n"],"names":["fs","os","path","Database","AgentRegistry","RenameNotFoundError","RenameConflictError","makeEntry","over","name","type","pid","process","cwd","startedAt","sessionId","sessionFilePath","pinned","runtime","runtimeRef","session","describe","tmpDir","regPath","registry","beforeEach","mkdtempSync","join","tmpdir","afterEach","vi","restoreAllMocks","rmSync","recursive","force","it","register","expect","existsSync","replace","toBe","list","ppid","toHaveLength","all","saved","paneId","agentName","lookup","toMatchObject","toBeNull","toEqual","spyOn","mockImplementation","Object","assign","Error","code","toThrow","registerBatch","other","togglePin","now","Date","clocked","updatedAt","toISOString","db","readonly","row","prepare","get","close","updated_at","rename","prune","readonlyRegistry","legacyEntry","mkdirSync","dirname","writeFileSync","JSON","stringify","entries","legacyRegistry","dbPath","exec","migratedRegistry","isAlive","remaining","before","after","not","nowMs","parse","pruneIntervalMs","alive","mockReturnValue","pruneIfDue","toHaveBeenCalledTimes","default","entry"],"mappings":"AAAA,OAAOA,QAAQ,KAAK;AACpB,OAAOC,QAAQ,KAAK;AACpB,OAAOC,UAAU,OAAO;AACxB,OAAOC,cAAc,iBAAiB;AACtC,SACEC,aAAa,EACbC,mBAAmB,EACnBC,mBAAmB,QAEd,+BAA+B;AAEtC,SAASC,UAAUC,OAA+B,CAAC,CAAC;IAClD,OAAO;QACLC,MAAM;QACNC,MAAM;QACNC,KAAKC,QAAQD,GAAG;QAChBE,KAAK;QACLC,WAAW;QACXC,WAAW;QACXC,iBAAiB;QACjBC,QAAQ;QACRC,SAAS;QACTC,YAAY;YAAEC,SAAS;QAAS;QAChC,GAAGZ,IAAI;IACT;AACF;AAEAa,SAAS,iBAAiB;IACxB,IAAIC;IACJ,IAAIC;IACJ,IAAIC;IAEJC,WAAW;QACTH,SAAStB,GAAG0B,WAAW,CAACxB,KAAKyB,IAAI,CAAC1B,GAAG2B,MAAM,IAAI;QAC/CL,UAAUrB,KAAKyB,IAAI,CAACL,QAAQ,UAAU;QACtCE,WAAW,IAAIpB,cAAcmB;IAC/B;IAEAM,UAAU;QACRC,GAAGC,eAAe;QAClB/B,GAAGgC,MAAM,CAACV,QAAQ;YAAEW,WAAW;YAAMC,OAAO;QAAK;IACnD;IAEAb,SAAS,YAAY;QACnBc,GAAG,+DAA+D;YAChEX,SAASY,QAAQ,CAAC7B;YAClB8B,OAAOrC,GAAGsC,UAAU,CAACf,QAAQgB,OAAO,CAAC,WAAW,SAASC,IAAI,CAAC;YAC9DH,OAAOb,SAASiB,IAAI,EAAE,CAAC,EAAE,CAAChC,IAAI,EAAE+B,IAAI,CAAC;QACvC;QAEAL,GAAG,2CAA2C;YAC5CX,SAASY,QAAQ,CAAC7B,UAAU;gBAAEE,MAAM;YAAI;YACxCe,SAASY,QAAQ,CAAC7B,UAAU;gBAAEE,MAAM;gBAAKE,KAAKC,QAAQ8B,IAAI;YAAC;YAC3DL,OAAOb,SAASiB,IAAI,IAAIE,YAAY,CAAC;QACvC;QAEAR,GAAG,oDAAoD;YACrDX,SAASY,QAAQ,CAAC7B,UAAU;gBAAEE,MAAM;gBAAKE,KAAKC,QAAQD,GAAG;YAAC;YAC1Da,SAASY,QAAQ,CAAC7B,UAAU;gBAAEE,MAAM;gBAAYE,KAAKC,QAAQD,GAAG;gBAAEQ,YAAY;YAAK;YACnF,MAAMyB,MAAMpB,SAASiB,IAAI;YACzBJ,OAAOO,KAAKD,YAAY,CAAC;YACzBN,OAAOO,GAAG,CAAC,EAAE,CAACjC,GAAG,EAAE6B,IAAI,CAAC5B,QAAQD,GAAG;YACnC0B,OAAOO,GAAG,CAAC,EAAE,CAACnC,IAAI,EAAE+B,IAAI,CAAC;QAC3B;QAEAL,GAAG,qDAAqD;YACtDX,SAASY,QAAQ,CAAC7B;YAClB8B,OAAOrC,GAAGsC,UAAU,CAAC,GAAGf,QAAQ,IAAI,CAAC,GAAGiB,IAAI,CAAC;QAC/C;QAEAL,GAAG,2BAA2B;YAC5BX,SAASY,QAAQ,CAAC7B,UAAU;gBAAEQ,WAAW;gBAAWC,iBAAiB;YAAiB;YACtF,MAAM6B,QAAQrB,SAASiB,IAAI,EAAE,CAAC,EAAE;YAChCJ,OAAOQ,MAAM9B,SAAS,EAAEyB,IAAI,CAAC;YAC7BH,OAAOQ,MAAM7B,eAAe,EAAEwB,IAAI,CAAC;QACrC;QAEAL,GAAG,sDAAsD;YACvD,MAAMhB,aAAa;gBAAEC,SAAS;gBAAW0B,QAAQ;gBAASC,WAAW;YAAW;YAEhFvB,SAASY,QAAQ,CACf7B,UAAU;gBACRE,MAAM;gBACNS,SAAS;gBACTC;YACF;YAGFkB,OAAOb,SAASwB,MAAM,CAAC,aAAaC,aAAa,CAAC;gBAChD/B,SAAS;gBACTC;YACF;QACF;QAEAgB,GAAG,yEAAyE;YAC1E,MAAMhB,aAAa;gBAAEC,SAAS;gBAAW0B,QAAQ;gBAASC,WAAW;YAAiB;YAEtFvB,SAASY,QAAQ,CACf7B,UAAU;gBACRE,MAAM;gBACNE,KAAKC,QAAQD,GAAG;gBAChBO,SAAS;gBACTC,YAAY;oBAAEC,SAAS;gBAAW;gBAClCP,KAAK;YACP;YAEFW,SAASY,QAAQ,CACf7B,UAAU;gBACRE,MAAM;gBACNE,KAAKC,QAAQD,GAAG;gBAChBO,SAAS;gBACTC;gBACAN,KAAK;YACP;YAGFwB,OAAOb,SAASwB,MAAM,CAAC,mBAAmBE,QAAQ;YAClDb,OAAOb,SAASwB,MAAM,CAAC,aAAaC,aAAa,CAAC;gBAChDtC,KAAKC,QAAQD,GAAG;gBAChBO,SAAS;gBACTC,YAAY;oBAAEC,SAAS;gBAAW;gBAClCP,KAAK;YACP;QACF;QAEAsB,GAAG,qEAAqE;YACtEX,SAASY,QAAQ,CAAC7B,UAAU;gBAAEE,MAAM;gBAAKU,YAAY;oBAAEC,SAAS;gBAAS;YAAE;YAC3EI,SAASY,QAAQ,CAAC7B,UAAU;gBAAEE,MAAM;gBAAYU,YAAY;gBAAMR,KAAKC,QAAQD,GAAG;YAAC;YACnF,MAAMkC,QAAQrB,SAASwB,MAAM,CAAC;YAC9BX,OAAOQ,OAAO1B,YAAYgC,OAAO,CAAC;gBAAE/B,SAAS;YAAS;YACtDiB,OAAOQ,OAAOlC,KAAK6B,IAAI,CAAC5B,QAAQD,GAAG;QACrC;QAEAwB,GAAG,4DAA4D;YAC7DX,SAASY,QAAQ,CAAC7B,UAAU;gBAAEE,MAAM;gBAAKU,YAAY;YAAK;YAE1DkB,OAAOb,SAASwB,MAAM,CAAC,MAAM7B,YAAY+B,QAAQ;QACnD;QAEAf,GAAG,6DAA6D;YAC9DX,SAASY,QAAQ,CAAC7B,UAAU;gBAAEE,MAAM,CAAC,UAAU,EAAEG,QAAQD,GAAG,EAAE;gBAAEQ,YAAY;YAAK;YACjFK,SAASY,QAAQ,CAAC7B,UAAU;gBAAEE,MAAM;gBAAeU,YAAY;oBAAEC,SAAS;gBAAc;YAAE;YAC1FiB,OAAOb,SAASwB,MAAM,CAAC,gBAAgBE,QAAQ;YAC/Cb,OAAOb,SAASwB,MAAM,CAAC,CAAC,UAAU,EAAEpC,QAAQD,GAAG,EAAE,GAAGQ,YAAY+B,QAAQ;YACxEb,OAAOb,SAASiB,IAAI,IAAIE,YAAY,CAAC;QACvC;QAEAR,GAAG,uEAAuE;YACxEX,SAASY,QAAQ,CAAC7B,UAAU;gBAAEE,MAAM;gBAAgBE,KAAKC,QAAQD,GAAG;YAAC;YACrEmB,GAAGsB,KAAK,CAACxC,SAAS,QAAQyC,kBAAkB,CAAC;gBAC3C,MAAMC,OAAOC,MAAM,CAAC,IAAIC,MAAM,4BAA4B;oBAAEC,MAAM;gBAAQ;YAC5E;YAEApB,OAAO,IACLb,SAASY,QAAQ,CACf7B,UAAU;oBACRE,MAAM;oBACNE,KAAKC,QAAQD,GAAG,GAAG;gBACrB,KAEF+C,OAAO;YACTrB,OAAOb,SAASwB,MAAM,CAAC,iBAAiBrC,KAAK6B,IAAI,CAAC5B,QAAQD,GAAG;QAC/D;IACF;IAEAU,SAAS,iBAAiB;QACxBc,GAAG,6BAA6B;YAC9BX,SAASmC,aAAa,CAAC,EAAE;YACzBtB,OAAOrC,GAAGsC,UAAU,CAACf,UAAUiB,IAAI,CAAC;QACtC;QAEAL,GAAG,8CAA8C;YAC/CX,SAASmC,aAAa,CAAC;gBACrBpD,UAAU;oBAAEE,MAAM;gBAAI;gBACtBF,UAAU;oBAAEE,MAAM;oBAAKE,KAAKC,QAAQD,GAAG,GAAG;gBAAE;gBAC5CJ,UAAU;oBAAEE,MAAM;oBAAKE,KAAKC,QAAQD,GAAG,GAAG;gBAAE;aAC7C;YACD0B,OAAOb,SAASiB,IAAI,IAAIE,YAAY,CAAC;QACvC;QAEAR,GAAG,gDAAgD;YACjDX,SAASY,QAAQ,CAAC7B,UAAU;gBAAEE,MAAM;gBAAKU,YAAY;oBAAEC,SAAS;gBAAS;YAAE;YAC3EI,SAASmC,aAAa,CAAC;gBACrBpD,UAAU;oBAAEE,MAAM;oBAAYU,YAAY;oBAAMR,KAAKC,QAAQD,GAAG;gBAAC;gBACjEJ,UAAU;oBAAEE,MAAM;oBAAKU,YAAY;oBAAMR,KAAKC,QAAQD,GAAG,GAAG;gBAAE;aAC/D;YACD0B,OAAOb,SAASwB,MAAM,CAAC,MAAM7B,YAAYgC,OAAO,CAAC;gBAAE/B,SAAS;YAAS;YACrEiB,OAAOb,SAASwB,MAAM,CAAC,MAAMrC,KAAK6B,IAAI,CAAC5B,QAAQD,GAAG;YAClD0B,OAAOb,SAASwB,MAAM,CAAC,MAAM7B,YAAY+B,QAAQ;QACnD;QAEAf,GAAG,oEAAoE;YACrE,MAAMyB,QAAQ,IAAIxD,cAAcmB;YAChCC,SAASY,QAAQ,CAAC7B,UAAU;gBAAEE,MAAM,CAAC,UAAU,EAAEG,QAAQD,GAAG,EAAE;gBAAEQ,YAAY;YAAK;YACjFyC,MAAMxB,QAAQ,CAAC7B,UAAU;gBAAEE,MAAM;gBAAeU,YAAY;oBAAEC,SAAS;gBAAc;YAAE;YACvFI,SAASY,QAAQ,CAAC7B,UAAU;gBAAEE,MAAM,CAAC,UAAU,EAAEG,QAAQD,GAAG,EAAE;gBAAEQ,YAAY;YAAK;YAEjFkB,OAAOb,SAASiB,IAAI,IAAIE,YAAY,CAAC;YACrCN,OAAOb,SAASwB,MAAM,CAAC,CAAC,UAAU,EAAEpC,QAAQD,GAAG,EAAE,GAAGA,KAAK6B,IAAI,CAAC5B,QAAQD,GAAG;QAC3E;QAEAwB,GAAG,2DAA2D;YAC5DX,SAASY,QAAQ,CAAC7B,UAAU;gBAAEE,MAAM;gBAAcC,MAAM;gBAAUC,KAAKC,QAAQD,GAAG;YAAC;YAEnFa,SAASY,QAAQ,CACf7B,UAAU;gBACRE,MAAM;gBACNC,MAAM;gBACNC,KAAKC,QAAQD,GAAG;gBAChBQ,YAAY;YACd;YAGFkB,OAAOb,SAASwB,MAAM,CAAC,eAAeE,QAAQ;YAC9Cb,OAAOb,SAASwB,MAAM,CAAC,cAAcC,aAAa,CAAC;gBAAEvC,MAAM;gBAASC,KAAKC,QAAQD,GAAG;YAAC;YACrF0B,OAAOb,SAASiB,IAAI,IAAIE,YAAY,CAAC;QACvC;QAEAR,GAAG,0EAA0E;YAC3EX,SAASY,QAAQ,CAAC7B,UAAU;gBAAEE,MAAM;gBAASE,KAAKC,QAAQD,GAAG;YAAC;YAE9D0B,OAAO,IACLb,SAASmC,aAAa,CAAC;oBACrBpD,UAAU;wBAAEE,MAAM;wBAASE,KAAK;oBAAO;oBACvCJ,UAAU;wBAAEE,MAAM;wBAASC,MAAM;wBAASC,KAAK;oBAAO;iBACvD,GACD+C,OAAO,CAAC;YAEVrB,OAAOb,SAASwB,MAAM,CAAC,UAAUE,QAAQ;YACzCb,OAAOb,SAASwB,MAAM,CAAC,UAAUrC,KAAK6B,IAAI,CAAC5B,QAAQD,GAAG;QACxD;IACF;IAEAU,SAAS,UAAU;QACjBc,GAAG,oCAAoC;YACrCE,OAAOb,SAASwB,MAAM,CAAC,YAAYE,QAAQ;QAC7C;QAEAf,GAAG,uCAAuC;YACxCX,SAASY,QAAQ,CAAC7B,UAAU;gBAAEE,MAAM;YAAI;YACxC4B,OAAOb,SAASwB,MAAM,CAAC,MAAMvC,MAAM+B,IAAI,CAAC;QAC1C;IACF;IAEAnB,SAAS,WAAW;QAClBc,GAAG,iEAAiE;YAClEX,SAASY,QAAQ,CAAC7B;YAElB8B,OAAOb,SAASwB,MAAM,CAAC,WAAW/B,QAAQuB,IAAI,CAAC;YAC/CH,OAAOb,SAASqC,SAAS,CAAC,UAAUjD,QAAQD,GAAG,GAAG6B,IAAI,CAAC;YACvDH,OAAOb,SAASwB,MAAM,CAAC,WAAW/B,QAAQuB,IAAI,CAAC;YAC/CH,OAAOb,SAASqC,SAAS,CAAC,UAAUjD,QAAQD,GAAG,GAAG6B,IAAI,CAAC;YACvDH,OAAOb,SAASwB,MAAM,CAAC,WAAW/B,QAAQuB,IAAI,CAAC;QACjD;QAEAL,GAAG,yCAAyC;YAC1C,IAAI2B,MAAM,IAAIC,KAAK;YACnB,MAAMC,UAAU,IAAI5D,cAAcmB,SAAS;gBAAEuC,KAAK,IAAMA;YAAI;YAC5DE,QAAQ5B,QAAQ,CAAC7B;YACjBuD,MAAM,IAAIC,KAAK;YAEfC,QAAQH,SAAS,CAAC,UAAUjD,QAAQD,GAAG;YAEvC0B,OAAO2B,QAAQhB,MAAM,CAAC,WAAWiB,WAAWzB,IAAI,CAACsB,IAAII,WAAW;YAChE,MAAMC,KAAK,IAAIhE,SAASoB,QAAQgB,OAAO,CAAC,WAAW,QAAQ;gBAAE6B,UAAU;YAAK;YAC5E,MAAMC,MAAMF,GACTG,OAAO,CAAC,4DACRC,GAAG,CAAC,UAAU3D,QAAQD,GAAG;YAC5BwD,GAAGK,KAAK;YACRnC,OAAOgC,IAAII,UAAU,EAAEjC,IAAI,CAACsB,IAAII,WAAW;QAC7C;QAEA/B,GAAG,qDAAqD;YACtDE,OAAOb,SAASqC,SAAS,CAAC,UAAU,SAASX,QAAQ;QACvD;QAEAf,GAAG,0DAA0D;YAC3DX,SAASY,QAAQ,CAAC7B,UAAU;gBAAEQ,WAAW;YAAS;YAClDS,SAASqC,SAAS,CAAC,UAAUjD,QAAQD,GAAG;YAExCa,SAASY,QAAQ,CAAC7B,UAAU;gBAAEQ,WAAW;YAAQ;YAEjDsB,OAAOb,SAASwB,MAAM,CAAC,WAAWC,aAAa,CAAC;gBAAElC,WAAW;gBAASE,QAAQ;YAAK;QACrF;QAEAkB,GAAG,kCAAkC;YACnCX,SAASY,QAAQ,CAAC7B,UAAU;gBAAEE,MAAM;YAAS;YAC7Ce,SAASqC,SAAS,CAAC,UAAUjD,QAAQD,GAAG;YAExCa,SAASkD,MAAM,CAAC,UAAU;YAE1BrC,OAAOb,SAASwB,MAAM,CAAC,UAAU/B,QAAQuB,IAAI,CAAC;QAChD;QAEAL,GAAG,6CAA6C;YAC9CX,SAASY,QAAQ,CAAC7B,UAAU;gBAAEI,KAAK;YAAO;YAC1Ca,SAASqC,SAAS,CAAC,UAAU;YAE7BrC,SAASmD,KAAK;YAEdtC,OAAOb,SAASwB,MAAM,CAAC,WAAWE,QAAQ;QAC5C;QAEAf,GAAG,gEAAgE;YACjEX,SAASY,QAAQ,CAAC7B;YAClB,MAAMqE,mBAAmB,IAAIxE,cAAcmB,SAAS;gBAAE6C,UAAU;YAAK;YAErE/B,OAAO,IAAMuC,iBAAiBf,SAAS,CAAC,UAAUjD,QAAQD,GAAG,GAAG+C,OAAO,CAAC;QAC1E;IACF;IAEArC,SAAS,QAAQ;QACfc,GAAG,8DAA8D;YAC/DE,OAAOb,SAASiB,IAAI,IAAIU,OAAO,CAAC,EAAE;QACpC;QAEAhB,GAAG,+CAA+C;YAChD,MAAM0C,cAActE,UAAU;gBAAEE,MAAM;gBAAUU,YAAY;oBAAEC,SAAS;gBAAS;YAAE;YAClFpB,GAAG8E,SAAS,CAAC5E,KAAK6E,OAAO,CAACxD,UAAU;gBAAEU,WAAW;YAAK;YACtDjC,GAAGgF,aAAa,CAACzD,SAAS0D,KAAKC,SAAS,CAAC;gBAAEC,SAAS;oBAACN;iBAAY;YAAC,IAAI;YAEtE,MAAMO,iBAAiB,IAAIhF,cAAcmB;YAEzCc,OAAO+C,eAAepC,MAAM,CAAC,WAAWE,QAAQ;YAChDb,OAAO+C,eAAe3C,IAAI,IAAIU,OAAO,CAAC,EAAE;YACxCd,OAAOrC,GAAGsC,UAAU,CAACf,QAAQgB,OAAO,CAAC,WAAW,SAASC,IAAI,CAAC;QAChE;QAEAL,GAAG,wDAAwD;YACzD,MAAMkD,SAAS9D,QAAQgB,OAAO,CAAC,WAAW;YAC1CvC,GAAG8E,SAAS,CAAC5E,KAAK6E,OAAO,CAACM,SAAS;gBAAEpD,WAAW;YAAK;YACrDjC,GAAGgC,MAAM,CAACqD,QAAQ;gBAAEnD,OAAO;YAAK;YAChClC,GAAGgC,MAAM,CAAC,GAAGqD,OAAO,IAAI,CAAC,EAAE;gBAAEnD,OAAO;YAAK;YACzClC,GAAGgC,MAAM,CAAC,GAAGqD,OAAO,IAAI,CAAC,EAAE;gBAAEnD,OAAO;YAAK;YACzC,MAAMiC,KAAK,IAAIhE,SAASkF;YACxBlB,GAAGmB,IAAI,CAAC,CAAC;;;;;;;;;;;;;;;;8BAgBe,EAAE1E,QAAQD,GAAG,CAAC;;;;YAIhC,CAAC;YACPwD,GAAGK,KAAK;YAER,MAAMe,mBAAmB,IAAInF,cAAcmB;YAE3Cc,OAAOkD,iBAAiBvC,MAAM,CAAC,kBAAkBC,aAAa,CAAC;gBAC7D/B,SAAS;gBACTC,YAAY;oBAAEC,SAAS;gBAAgB;YACzC;QACF;IACF;IAEAC,SAAS,WAAW;QAClBc,GAAG,wCAAwC;YACzCE,OAAOb,SAASgE,OAAO,CAACjF,UAAU;gBAAEI,KAAKC,QAAQD,GAAG;YAAC,KAAK6B,IAAI,CAAC;QACjE;QAEAL,GAAG,+CAA+C;YAChDE,OAAOb,SAASgE,OAAO,CAACjF,UAAU;gBAAEI,KAAK;YAAO,KAAK6B,IAAI,CAAC;QAC5D;QAEAL,GAAG,+DAA+D;YAChEL,GAAGsB,KAAK,CAACxC,SAAS,QAAQyC,kBAAkB,CAAC;gBAC3C,MAAMC,OAAOC,MAAM,CAAC,IAAIC,MAAM,4BAA4B;oBAAEC,MAAM;gBAAQ;YAC5E;YAEApB,OAAOb,SAASgE,OAAO,CAACjF,cAAciC,IAAI,CAAC;QAC7C;QAEAL,GAAG,sDAAsD;YACvDL,GAAGsB,KAAK,CAACxC,SAAS,QAAQyC,kBAAkB,CAAC;gBAC3C,MAAMC,OAAOC,MAAM,CAAC,IAAIC,MAAM,oBAAoB;oBAAEC,MAAM;gBAAQ;YACpE;YAEApB,OAAOb,SAASgE,OAAO,CAACjF,cAAciC,IAAI,CAAC;QAC7C;QAEAL,GAAG,6EAA6E;YAC9EL,GAAGsB,KAAK,CAACxC,SAAS,QAAQyC,kBAAkB,CAAC;gBAC3C,MAAM,IAAIG,MAAM;YAClB;YAEAnB,OAAOb,SAASgE,OAAO,CAACjF,cAAciC,IAAI,CAAC;QAC7C;IACF;IAEAnB,SAAS,SAAS;QAChBc,GAAG,uCAAuC;YACxCX,SAASY,QAAQ,CAAC7B,UAAU;gBAAEE,MAAM;gBAASE,KAAKC,QAAQD,GAAG;YAAC;YAC9Da,SAASY,QAAQ,CAAC7B,UAAU;gBAAEE,MAAM;gBAAQE,KAAK;YAAO;YACxDa,SAASmD,KAAK;YACd,MAAMc,YAAYjE,SAASiB,IAAI;YAC/BJ,OAAOoD,WAAW9C,YAAY,CAAC;YAC/BN,OAAOoD,SAAS,CAAC,EAAE,CAAChF,IAAI,EAAE+B,IAAI,CAAC;QACjC;QAEAL,GAAG,yCAAyC;YAC1CX,SAASY,QAAQ,CAAC7B,UAAU;gBAAEI,KAAKC,QAAQD,GAAG;YAAC;YAC/C,MAAM+E,SAASlE,SAASiB,IAAI;YAC5BjB,SAASmD,KAAK;YACd,MAAMgB,QAAQnE,SAASiB,IAAI;YAC3BJ,OAAOsD,OAAOxC,OAAO,CAACuC;QACxB;QAEAvD,GAAG,4DAA4D;YAC7DX,SAASY,QAAQ,CAAC7B,UAAU;gBAAEE,MAAM;gBAAeU,YAAY;oBAAEC,SAAS;gBAAc;YAAE;YAC1FU,GAAGsB,KAAK,CAACxC,SAAS,QAAQyC,kBAAkB,CAAC;gBAC3C,MAAMC,OAAOC,MAAM,CAAC,IAAIC,MAAM,4BAA4B;oBAAEC,MAAM;gBAAQ;YAC5E;YAEAjC,SAASmD,KAAK;YAEdtC,OAAOb,SAASwB,MAAM,CAAC,gBAAgBC,aAAa,CAAC;gBACnDxC,MAAM;gBACNU,YAAY;oBAAEC,SAAS;gBAAc;YACvC;QACF;QAEAe,GAAG,0DAA0D;YAC3DX,SAASY,QAAQ,CAAC7B,UAAU;gBAAEE,MAAM;YAAO;YAC3CqB,GAAGsB,KAAK,CAACxC,SAAS,QAAQyC,kBAAkB,CAAC;gBAC3C,MAAMC,OAAOC,MAAM,CAAC,IAAIC,MAAM,oBAAoB;oBAAEC,MAAM;gBAAQ;YACpE;YAEAjC,SAASmD,KAAK;YAEdtC,OAAOb,SAASwB,MAAM,CAAC,SAASE,QAAQ;QAC1C;QAEAf,GAAG,qCAAqC;YACtCE,OAAO,IAAMb,SAASmD,KAAK,IAAIiB,GAAG,CAAClC,OAAO;QAC5C;QAEAvB,GAAG,kEAAkE;YACnE,IAAI0D,QAAQ9B,KAAK+B,KAAK,CAAC;YACvB,MAAM9B,UAAU,IAAI5D,cAAcmB,SAAS;gBACzCuC,KAAK,IAAM,IAAIC,KAAK8B;gBACpBE,iBAAiB;YACnB;YACA/B,QAAQ5B,QAAQ,CAAC7B,UAAU;gBAAEE,MAAM;gBAAUE,KAAKC,QAAQD,GAAG;YAAC;YAC9D,MAAMqF,QAAQlE,GAAGsB,KAAK,CAACY,SAAS,WAAWiC,eAAe,CAAC;YAC3DjC,QAAQkC,UAAU;YAClBF,MAAMC,eAAe,CAAC;YACtBJ,SAAS;YAET7B,QAAQW,KAAK;YAEbtC,OAAO2D,OAAOG,qBAAqB,CAAC;YACpC9D,OAAO2B,QAAQhB,MAAM,CAAC,WAAWE,QAAQ;QAC3C;IACF;IAEA7B,SAAS,aAAa;QACpBc,GAAG,gCAAgC;YACjCE,OAAOjC,cAAcgG,OAAO,IAAI5D,IAAI,CAACpC,cAAcgG,OAAO;QAC5D;IACF;IAEA/E,SAAS,UAAU;QACjBc,GAAG,yCAAyC;YAC1CX,SAASY,QAAQ,CAAC7B,UAAU;gBAAEE,MAAM;gBAAYE,KAAKC,QAAQD,GAAG;YAAC;YACjEa,SAASkD,MAAM,CAAC,YAAY;YAC5BrC,OAAOb,SAASwB,MAAM,CAAC,aAAavC,MAAM+B,IAAI,CAAC;YAC/CH,OAAOb,SAASwB,MAAM,CAAC,aAAaE,QAAQ;QAC9C;QAEAf,GAAG,mDAAmD;YACpDX,SAASY,QAAQ,CACf7B,UAAU;gBACRE,MAAM;gBACNE,KAAKC,QAAQD,GAAG;gBAChBQ,YAAY;oBAAEC,SAAS;gBAAW;gBAClCP,KAAK;YACP;YAEFW,SAASkD,MAAM,CAAC,YAAY;YAC5B,MAAM2B,QAAQ7E,SAASwB,MAAM,CAAC;YAC9BX,OAAOgE,OAAOlF,YAAYgC,OAAO,CAAC;gBAAE/B,SAAS;YAAW;YACxDiB,OAAOgE,OAAOxF,KAAK2B,IAAI,CAAC;YACxBH,OAAOgE,OAAO1F,KAAK6B,IAAI,CAAC5B,QAAQD,GAAG;QACrC;QAEAwB,GAAG,+DAA+D;YAChEE,OAAO,IAAMb,SAASkD,MAAM,CAAC,SAAS,aAAahB,OAAO,CAACrD;QAC7D;QAEA8B,GAAG,8EAA8E;YAC/EX,SAASY,QAAQ,CAAC7B,UAAU;gBAAEE,MAAM;gBAAWE,KAAKC,QAAQD,GAAG;YAAC;YAChEa,SAASY,QAAQ,CAAC7B,UAAU;gBAAEE,MAAM;gBAAWE,KAAKC,QAAQ8B,IAAI;YAAC;YACjEL,OAAO,IAAMb,SAASkD,MAAM,CAAC,WAAW,YAAYhB,OAAO,CAACpD;QAC9D;QAEA6B,GAAG,gFAAgF;YACjFX,SAASY,QAAQ,CAAC7B,UAAU;gBAAEE,MAAM;gBAAWE,KAAKC,QAAQD,GAAG;YAAC;YAChEa,SAASY,QAAQ,CAAC7B,UAAU;gBAAEE,MAAM;gBAAWE,KAAKC,QAAQ8B,IAAI;YAAC;YACjEZ,GAAGsB,KAAK,CAACxC,SAAS,QAAQyC,kBAAkB,CAAC;gBAC3C,MAAMC,OAAOC,MAAM,CAAC,IAAIC,MAAM,4BAA4B;oBAAEC,MAAM;gBAAQ;YAC5E;YAEApB,OAAO,IAAMb,SAASkD,MAAM,CAAC,WAAW,YAAYhB,OAAO,CAACpD;YAC5D+B,OAAOb,SAASwB,MAAM,CAAC,YAAYrC,KAAK6B,IAAI,CAAC5B,QAAQ8B,IAAI;QAC3D;QAEAP,GAAG,8DAA8D;YAC/DX,SAASY,QAAQ,CAAC7B,UAAU;gBAAEE,MAAM;gBAAWE,KAAKC,QAAQD,GAAG;YAAC;YAChEa,SAASY,QAAQ,CAAC7B,UAAU;gBAAEE,MAAM;gBAAWE,KAAK;YAAO;YAC3D0B,OAAO,IAAMb,SAASkD,MAAM,CAAC,WAAW,YAAYkB,GAAG,CAAClC,OAAO;YAC/DrB,OAAOb,SAASwB,MAAM,CAAC,YAAYrC,KAAK6B,IAAI,CAAC5B,QAAQD,GAAG;QAC1D;QAEAwB,GAAG,wDAAwD;YACzDX,SAASY,QAAQ,CAAC7B,UAAU;gBAAEE,MAAM;gBAAYE,KAAKC,QAAQD,GAAG;YAAC;YACjEa,SAASkD,MAAM,CAAC,YAAY;YAC5BrC,OAAOrC,GAAGsC,UAAU,CAAC,GAAGf,QAAQ,IAAI,CAAC,GAAGiB,IAAI,CAAC;QAC/C;IACF;AACF"}
@@ -16,6 +16,7 @@ export declare class OpenCodeAdapter implements AgentAdapter {
16
16
  readonly processNames: readonly ["opencode"];
17
17
  private static readonly IDLE_THRESHOLD_MINUTES;
18
18
  private readonly dbPath;
19
+ private readonly cleanup;
19
20
  private db;
20
21
  constructor();
21
22
  close(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"OpenCodeAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/OpenCodeAdapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,KAAK,EACV,YAAY,EACZ,SAAS,EACT,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,mBAAmB,CAAC;AAoC3B,qBAAa,eAAgB,YAAW,YAAY;IAClD,QAAQ,CAAC,IAAI,EAAG,UAAU,CAAU;IACpC,QAAQ,CAAC,YAAY,wBAAyB;IAE9C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CAAK;IAEnD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,EAAE,CAAkC;;IAU5C,KAAK,IAAI,IAAI;IAWb,OAAO,CAAC,MAAM,CAAC,aAAa;IAO5B,SAAS,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO;IAKtC,YAAY,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IA6BzE,eAAe,CAAC,eAAe,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,mBAAmB,EAAE;IAmD1F,YAAY,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAyCzE,OAAO,CAAC,uBAAuB;IAsB/B,OAAO,CAAC,eAAe;IAwEvB,OAAO,CAAC,iBAAiB;IAqBzB,OAAO,CAAC,mBAAmB;IAa3B,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,MAAM;CAUf"}
1
+ {"version":3,"file":"OpenCodeAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/OpenCodeAdapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,KAAK,EACV,YAAY,EACZ,SAAS,EACT,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,mBAAmB,CAAC;AAoC3B,qBAAa,eAAgB,YAAW,YAAY;IAClD,QAAQ,CAAC,IAAI,EAAG,UAAU,CAAU;IACpC,QAAQ,CAAC,YAAY,wBAAyB;IAE9C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CAAK;IAEnD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;IACpD,OAAO,CAAC,EAAE,CAAkC;;IAS5C,KAAK,IAAI,IAAI;IAeb,OAAO,CAAC,MAAM,CAAC,aAAa;IAO5B,SAAS,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO;IAKtC,YAAY,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IA6BzE,eAAe,CAAC,eAAe,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,mBAAmB,EAAE;IAmD1F,YAAY,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAyCzE,OAAO,CAAC,uBAAuB;IAsB/B,OAAO,CAAC,eAAe;IAwEvB,OAAO,CAAC,iBAAiB;IAqBzB,OAAO,CAAC,mBAAmB;IAa3B,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,MAAM;CAUf"}
@@ -34,15 +34,18 @@ export class OpenCodeAdapter {
34
34
  ];
35
35
  static IDLE_THRESHOLD_MINUTES = 5;
36
36
  dbPath;
37
+ cleanup = ()=>this.close();
37
38
  db = null;
38
39
  constructor(){
39
40
  this.dbPath = OpenCodeAdapter.resolveDbPath();
40
- const cleanup = ()=>this.close();
41
- process.once("exit", cleanup);
42
- process.once("SIGINT", cleanup);
43
- process.once("SIGTERM", cleanup);
41
+ process.once("exit", this.cleanup);
42
+ process.once("SIGINT", this.cleanup);
43
+ process.once("SIGTERM", this.cleanup);
44
44
  }
45
45
  close() {
46
+ process.off("exit", this.cleanup);
47
+ process.off("SIGINT", this.cleanup);
48
+ process.off("SIGTERM", this.cleanup);
46
49
  if (this.db) {
47
50
  try {
48
51
  this.db.close();