@ai-devkit/agent-manager 0.33.1 → 0.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. package/dist/AgentManager.d.ts +2 -0
  2. package/dist/AgentManager.d.ts.map +1 -1
  3. package/dist/AgentManager.js +25 -0
  4. package/dist/AgentManager.js.map +1 -1
  5. package/dist/__tests__/AgentManager.test.js +74 -0
  6. package/dist/__tests__/AgentManager.test.js.map +1 -1
  7. package/dist/__tests__/adapters/ClaudeCodeAdapter.test.js +28 -0
  8. package/dist/__tests__/adapters/ClaudeCodeAdapter.test.js.map +1 -1
  9. package/dist/__tests__/adapters/CodexAdapter.test.js +7 -0
  10. package/dist/__tests__/adapters/CodexAdapter.test.js.map +1 -1
  11. package/dist/__tests__/capacity/codex.test.js +2 -1
  12. package/dist/__tests__/capacity/codex.test.js.map +1 -1
  13. package/dist/__tests__/capacity/index.test.js +6 -3
  14. package/dist/__tests__/capacity/index.test.js.map +1 -1
  15. package/dist/__tests__/capacity/zai.test.js +213 -0
  16. package/dist/__tests__/capacity/zai.test.js.map +1 -0
  17. package/dist/__tests__/providers/copilot/CopilotAdapter.test.js +18 -0
  18. package/dist/__tests__/providers/copilot/CopilotAdapter.test.js.map +1 -1
  19. package/dist/__tests__/providers/gemini/GeminiCliAdapter.test.js +36 -0
  20. package/dist/__tests__/providers/gemini/GeminiCliAdapter.test.js.map +1 -1
  21. package/dist/__tests__/providers/grok/GrokAgentMapper.test.js +56 -0
  22. package/dist/__tests__/providers/grok/GrokAgentMapper.test.js.map +1 -0
  23. package/dist/__tests__/{adapters → providers/grok}/GrokCliAdapter.test.js +19 -6
  24. package/dist/__tests__/providers/grok/GrokCliAdapter.test.js.map +1 -0
  25. package/dist/__tests__/providers/grok/GrokSessionLocator.test.js +133 -0
  26. package/dist/__tests__/providers/grok/GrokSessionLocator.test.js.map +1 -0
  27. package/dist/__tests__/providers/opencode/OpenCodeAdapter.test.js +213 -0
  28. package/dist/__tests__/providers/opencode/OpenCodeAdapter.test.js.map +1 -0
  29. package/dist/__tests__/providers/opencode/OpenCodeAgentMapper.test.js +89 -0
  30. package/dist/__tests__/providers/opencode/OpenCodeAgentMapper.test.js.map +1 -0
  31. package/dist/__tests__/providers/opencode/OpenCodeSessionLocator.test.js +94 -0
  32. package/dist/__tests__/providers/opencode/OpenCodeSessionLocator.test.js.map +1 -0
  33. package/dist/__tests__/providers/opencode/OpenCodeSessionParser.test.js +147 -0
  34. package/dist/__tests__/providers/opencode/OpenCodeSessionParser.test.js.map +1 -0
  35. package/dist/__tests__/providers/pi/PiAdapter.test.js +30 -0
  36. package/dist/__tests__/providers/pi/PiAdapter.test.js.map +1 -1
  37. package/dist/__tests__/utils/session.test.js +18 -1
  38. package/dist/__tests__/utils/session.test.js.map +1 -1
  39. package/dist/adapters/AgentAdapter.d.ts +8 -0
  40. package/dist/adapters/AgentAdapter.d.ts.map +1 -1
  41. package/dist/adapters/AgentAdapter.js.map +1 -1
  42. package/dist/adapters/index.d.ts +2 -2
  43. package/dist/adapters/index.d.ts.map +1 -1
  44. package/dist/adapters/index.js +2 -2
  45. package/dist/adapters/index.js.map +1 -1
  46. package/dist/capacity/codex.d.ts +1 -0
  47. package/dist/capacity/codex.d.ts.map +1 -1
  48. package/dist/capacity/codex.js +9 -4
  49. package/dist/capacity/codex.js.map +1 -1
  50. package/dist/capacity/index.d.ts +8 -0
  51. package/dist/capacity/index.d.ts.map +1 -1
  52. package/dist/capacity/index.js +10 -9
  53. package/dist/capacity/index.js.map +1 -1
  54. package/dist/capacity/types.d.ts +8 -0
  55. package/dist/capacity/types.d.ts.map +1 -1
  56. package/dist/capacity/types.js.map +1 -1
  57. package/dist/capacity/zai.d.ts +14 -0
  58. package/dist/capacity/zai.d.ts.map +1 -0
  59. package/dist/capacity/zai.js +203 -0
  60. package/dist/capacity/zai.js.map +1 -0
  61. package/dist/index.d.ts +4 -4
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +3 -3
  64. package/dist/index.js.map +1 -1
  65. package/dist/providers/claude/ClaudeCodeAdapter.d.ts +2 -0
  66. package/dist/providers/claude/ClaudeCodeAdapter.d.ts.map +1 -1
  67. package/dist/providers/claude/ClaudeCodeAdapter.js +22 -19
  68. package/dist/providers/claude/ClaudeCodeAdapter.js.map +1 -1
  69. package/dist/providers/claude/ClaudeSessionLocator.d.ts +4 -0
  70. package/dist/providers/claude/ClaudeSessionLocator.d.ts.map +1 -1
  71. package/dist/providers/claude/ClaudeSessionLocator.js +18 -1
  72. package/dist/providers/claude/ClaudeSessionLocator.js.map +1 -1
  73. package/dist/providers/codex/CodexAdapter.d.ts +1 -0
  74. package/dist/providers/codex/CodexAdapter.d.ts.map +1 -1
  75. package/dist/providers/codex/CodexAdapter.js +8 -0
  76. package/dist/providers/codex/CodexAdapter.js.map +1 -1
  77. package/dist/providers/copilot/CopilotAdapter.d.ts +2 -0
  78. package/dist/providers/copilot/CopilotAdapter.d.ts.map +1 -1
  79. package/dist/providers/copilot/CopilotAdapter.js +24 -9
  80. package/dist/providers/copilot/CopilotAdapter.js.map +1 -1
  81. package/dist/providers/copilot/CopilotSessionLocator.d.ts +1 -0
  82. package/dist/providers/copilot/CopilotSessionLocator.d.ts.map +1 -1
  83. package/dist/providers/copilot/CopilotSessionLocator.js +9 -1
  84. package/dist/providers/copilot/CopilotSessionLocator.js.map +1 -1
  85. package/dist/providers/gemini/GeminiCliAdapter.d.ts +1 -0
  86. package/dist/providers/gemini/GeminiCliAdapter.d.ts.map +1 -1
  87. package/dist/providers/gemini/GeminiCliAdapter.js +9 -0
  88. package/dist/providers/gemini/GeminiCliAdapter.js.map +1 -1
  89. package/dist/providers/grok/GrokAgentMapper.d.ts +14 -0
  90. package/dist/providers/grok/GrokAgentMapper.d.ts.map +1 -0
  91. package/dist/providers/grok/GrokAgentMapper.js +36 -0
  92. package/dist/providers/grok/GrokAgentMapper.js.map +1 -0
  93. package/dist/providers/grok/GrokCliAdapter.d.ts +36 -0
  94. package/dist/providers/grok/GrokCliAdapter.d.ts.map +1 -0
  95. package/dist/providers/grok/GrokCliAdapter.js +98 -0
  96. package/dist/providers/grok/GrokCliAdapter.js.map +1 -0
  97. package/dist/providers/grok/GrokSessionLocator.d.ts +62 -0
  98. package/dist/providers/grok/GrokSessionLocator.d.ts.map +1 -0
  99. package/dist/providers/grok/GrokSessionLocator.js +140 -0
  100. package/dist/providers/grok/GrokSessionLocator.js.map +1 -0
  101. package/dist/providers/grok/GrokSessionParser.d.ts +54 -0
  102. package/dist/providers/grok/GrokSessionParser.d.ts.map +1 -0
  103. package/dist/providers/grok/GrokSessionParser.js +122 -0
  104. package/dist/providers/grok/GrokSessionParser.js.map +1 -0
  105. package/dist/{adapters → providers/opencode}/OpenCodeAdapter.d.ts +6 -12
  106. package/dist/providers/opencode/OpenCodeAdapter.d.ts.map +1 -0
  107. package/dist/providers/opencode/OpenCodeAdapter.js +89 -0
  108. package/dist/providers/opencode/OpenCodeAdapter.js.map +1 -0
  109. package/dist/providers/opencode/OpenCodeAgentMapper.d.ts +11 -0
  110. package/dist/providers/opencode/OpenCodeAgentMapper.d.ts.map +1 -0
  111. package/dist/providers/opencode/OpenCodeAgentMapper.js +50 -0
  112. package/dist/providers/opencode/OpenCodeAgentMapper.js.map +1 -0
  113. package/dist/providers/opencode/OpenCodeSessionLocator.d.ts +23 -0
  114. package/dist/providers/opencode/OpenCodeSessionLocator.d.ts.map +1 -0
  115. package/dist/providers/opencode/OpenCodeSessionLocator.js +118 -0
  116. package/dist/providers/opencode/OpenCodeSessionLocator.js.map +1 -0
  117. package/dist/providers/opencode/OpenCodeSessionParser.d.ts +18 -0
  118. package/dist/providers/opencode/OpenCodeSessionParser.d.ts.map +1 -0
  119. package/dist/providers/opencode/OpenCodeSessionParser.js +104 -0
  120. package/dist/providers/opencode/OpenCodeSessionParser.js.map +1 -0
  121. package/dist/providers/pi/PiAdapter.d.ts +1 -0
  122. package/dist/providers/pi/PiAdapter.d.ts.map +1 -1
  123. package/dist/providers/pi/PiAdapter.js +10 -0
  124. package/dist/providers/pi/PiAdapter.js.map +1 -1
  125. package/dist/providers/pi/PiSessionLocator.d.ts +1 -0
  126. package/dist/providers/pi/PiSessionLocator.d.ts.map +1 -1
  127. package/dist/providers/pi/PiSessionLocator.js +8 -1
  128. package/dist/providers/pi/PiSessionLocator.js.map +1 -1
  129. package/dist/utils/session.d.ts +2 -0
  130. package/dist/utils/session.d.ts.map +1 -1
  131. package/dist/utils/session.js +3 -0
  132. package/dist/utils/session.js.map +1 -1
  133. package/package.json +1 -1
  134. package/src/AgentManager.ts +34 -0
  135. package/src/__tests__/AgentManager.test.ts +60 -0
  136. package/src/__tests__/adapters/ClaudeCodeAdapter.test.ts +23 -0
  137. package/src/__tests__/adapters/CodexAdapter.test.ts +7 -0
  138. package/src/__tests__/capacity/codex.test.ts +94 -26
  139. package/src/__tests__/capacity/fixtures/zai-auth.json +6 -0
  140. package/src/__tests__/capacity/fixtures/zai-quota-invalid.json +7 -0
  141. package/src/__tests__/capacity/fixtures/zai-quota.json +38 -0
  142. package/src/__tests__/capacity/index.test.ts +10 -3
  143. package/src/__tests__/capacity/zai.test.ts +186 -0
  144. package/src/__tests__/providers/copilot/CopilotAdapter.test.ts +12 -0
  145. package/src/__tests__/providers/gemini/GeminiCliAdapter.test.ts +21 -0
  146. package/src/__tests__/providers/grok/GrokAgentMapper.test.ts +57 -0
  147. package/src/__tests__/{adapters → providers/grok}/GrokCliAdapter.test.ts +18 -9
  148. package/src/__tests__/providers/grok/GrokSessionLocator.test.ts +106 -0
  149. package/src/__tests__/providers/opencode/OpenCodeAdapter.test.ts +215 -0
  150. package/src/__tests__/providers/opencode/OpenCodeAgentMapper.test.ts +83 -0
  151. package/src/__tests__/providers/opencode/OpenCodeSessionLocator.test.ts +84 -0
  152. package/src/__tests__/providers/opencode/OpenCodeSessionParser.test.ts +148 -0
  153. package/src/__tests__/providers/pi/PiAdapter.test.ts +15 -0
  154. package/src/__tests__/utils/session.test.ts +11 -1
  155. package/src/adapters/AgentAdapter.ts +9 -0
  156. package/src/adapters/index.ts +2 -2
  157. package/src/capacity/codex.ts +96 -28
  158. package/src/capacity/index.ts +29 -11
  159. package/src/capacity/types.ts +8 -0
  160. package/src/capacity/zai.ts +237 -0
  161. package/src/index.ts +9 -4
  162. package/src/providers/claude/ClaudeCodeAdapter.ts +28 -23
  163. package/src/providers/claude/ClaudeSessionLocator.ts +20 -0
  164. package/src/providers/codex/CodexAdapter.ts +8 -0
  165. package/src/providers/copilot/CopilotAdapter.ts +26 -10
  166. package/src/providers/copilot/CopilotSessionLocator.ts +7 -1
  167. package/src/providers/gemini/GeminiCliAdapter.ts +8 -0
  168. package/src/providers/grok/GrokAgentMapper.ts +41 -0
  169. package/src/providers/grok/GrokCliAdapter.ts +125 -0
  170. package/src/providers/grok/GrokSessionLocator.ts +179 -0
  171. package/src/providers/grok/GrokSessionParser.ts +166 -0
  172. package/src/providers/opencode/OpenCodeAdapter.ts +116 -0
  173. package/src/providers/opencode/OpenCodeAgentMapper.ts +61 -0
  174. package/src/providers/opencode/OpenCodeSessionLocator.ts +151 -0
  175. package/src/providers/opencode/OpenCodeSessionParser.ts +145 -0
  176. package/src/providers/pi/PiAdapter.ts +12 -0
  177. package/src/providers/pi/PiSessionLocator.ts +16 -1
  178. package/src/utils/session.ts +11 -0
  179. package/dist/__tests__/adapters/GrokCliAdapter.test.js.map +0 -1
  180. package/dist/__tests__/adapters/OpenCodeAdapter.test.js +0 -636
  181. package/dist/__tests__/adapters/OpenCodeAdapter.test.js.map +0 -1
  182. package/dist/adapters/GrokCliAdapter.d.ts +0 -80
  183. package/dist/adapters/GrokCliAdapter.d.ts.map +0 -1
  184. package/dist/adapters/GrokCliAdapter.js +0 -310
  185. package/dist/adapters/GrokCliAdapter.js.map +0 -1
  186. package/dist/adapters/OpenCodeAdapter.d.ts.map +0 -1
  187. package/dist/adapters/OpenCodeAdapter.js +0 -289
  188. package/dist/adapters/OpenCodeAdapter.js.map +0 -1
  189. package/src/__tests__/adapters/OpenCodeAdapter.test.ts +0 -544
  190. package/src/adapters/GrokCliAdapter.ts +0 -408
  191. package/src/adapters/OpenCodeAdapter.ts +0 -373
@@ -0,0 +1,147 @@
1
+ import Database from "better-sqlite3";
2
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
3
+ import { OpenCodeSessionParser } from "../../../providers/opencode/OpenCodeSessionParser.js";
4
+ describe("OpenCodeSessionParser", ()=>{
5
+ let db;
6
+ let parser;
7
+ beforeEach(()=>{
8
+ db = new Database(":memory:");
9
+ db.exec(`
10
+ CREATE TABLE message (id TEXT, session_id TEXT, data TEXT, time_created INTEGER, time_updated INTEGER);
11
+ CREATE TABLE part (message_id TEXT, session_id TEXT, data TEXT, time_created INTEGER);
12
+ `);
13
+ parser = new OpenCodeSessionParser();
14
+ });
15
+ afterEach(()=>{
16
+ db.close();
17
+ });
18
+ it("extracts session stats from OpenCode messages and parts", ()=>{
19
+ insertMessage(db, {
20
+ id: "msg-user",
21
+ sessionId: "sess-1",
22
+ role: "user",
23
+ timeCreated: 1000,
24
+ timeUpdated: 1000
25
+ });
26
+ insertMessage(db, {
27
+ id: "msg-assistant",
28
+ sessionId: "sess-1",
29
+ role: "assistant",
30
+ timeCreated: 2000,
31
+ timeUpdated: 2500,
32
+ completed: 2400
33
+ });
34
+ insertPart(db, {
35
+ messageId: "msg-user",
36
+ sessionId: "sess-1",
37
+ data: {
38
+ type: "text",
39
+ text: "Plan the migration"
40
+ },
41
+ timeCreated: 1000
42
+ });
43
+ expect(parser.getSessionStats(db, "sess-1")).toEqual({
44
+ lastRole: "assistant",
45
+ lastTimeUpdated: 2500,
46
+ lastAssistantCompleted: true,
47
+ lastAssistantErrored: false,
48
+ summary: "Plan the migration"
49
+ });
50
+ });
51
+ it("orders stats by message creation time, not metadata update time", ()=>{
52
+ insertMessage(db, {
53
+ id: "msg-user",
54
+ sessionId: "sess-1",
55
+ role: "user",
56
+ timeCreated: 1000,
57
+ timeUpdated: 3000
58
+ });
59
+ insertMessage(db, {
60
+ id: "msg-assistant",
61
+ sessionId: "sess-1",
62
+ role: "assistant",
63
+ timeCreated: 2000,
64
+ timeUpdated: 2100,
65
+ completed: 2200
66
+ });
67
+ expect(parser.getSessionStats(db, "sess-1").lastRole).toBe("assistant");
68
+ });
69
+ it("reads visible conversation text and hides verbose-only parts by default", ()=>{
70
+ insertMessage(db, {
71
+ id: "msg-user",
72
+ sessionId: "sess-1",
73
+ role: "user",
74
+ timeCreated: 1000,
75
+ timeUpdated: 1000
76
+ });
77
+ insertMessage(db, {
78
+ id: "msg-assistant",
79
+ sessionId: "sess-1",
80
+ role: "assistant",
81
+ timeCreated: 2000,
82
+ timeUpdated: 2000
83
+ });
84
+ insertPart(db, {
85
+ messageId: "msg-user",
86
+ sessionId: "sess-1",
87
+ data: {
88
+ type: "text",
89
+ text: "Hello"
90
+ },
91
+ timeCreated: 1000
92
+ });
93
+ insertPart(db, {
94
+ messageId: "msg-assistant",
95
+ sessionId: "sess-1",
96
+ data: {
97
+ type: "reasoning",
98
+ reasoning: "private thought"
99
+ },
100
+ timeCreated: 2000
101
+ });
102
+ insertPart(db, {
103
+ messageId: "msg-assistant",
104
+ sessionId: "sess-1",
105
+ data: {
106
+ type: "tool",
107
+ tool: "read_file"
108
+ },
109
+ timeCreated: 3000
110
+ });
111
+ expect(parser.getConversation(db, "sess-1")).toEqual([
112
+ {
113
+ role: "user",
114
+ content: "Hello"
115
+ }
116
+ ]);
117
+ expect(parser.getConversation(db, "sess-1", {
118
+ verbose: true
119
+ })).toEqual([
120
+ {
121
+ role: "user",
122
+ content: "Hello"
123
+ },
124
+ {
125
+ role: "assistant",
126
+ content: "[thinking] private thought"
127
+ },
128
+ {
129
+ role: "assistant",
130
+ content: "[tool: read_file]"
131
+ }
132
+ ]);
133
+ });
134
+ });
135
+ function insertMessage(db, input) {
136
+ db.prepare("INSERT INTO message (id, session_id, data, time_created, time_updated) VALUES (?, ?, ?, ?, ?)").run(input.id, input.sessionId, JSON.stringify({
137
+ role: input.role,
138
+ time: input.completed === undefined ? {} : {
139
+ completed: input.completed
140
+ }
141
+ }), input.timeCreated, input.timeUpdated);
142
+ }
143
+ function insertPart(db, input) {
144
+ db.prepare("INSERT INTO part (message_id, session_id, data, time_created) VALUES (?, ?, ?, ?)").run(input.messageId, input.sessionId, JSON.stringify(input.data), input.timeCreated);
145
+ }
146
+
147
+ //# sourceMappingURL=OpenCodeSessionParser.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/__tests__/providers/opencode/OpenCodeSessionParser.test.ts"],"sourcesContent":["import Database from \"better-sqlite3\";\nimport { afterEach, beforeEach, describe, expect, it } from \"vitest\";\nimport { OpenCodeSessionParser } from \"../../../providers/opencode/OpenCodeSessionParser.js\";\n\ndescribe(\"OpenCodeSessionParser\", () => {\n let db: Database.Database;\n let parser: OpenCodeSessionParser;\n\n beforeEach(() => {\n db = new Database(\":memory:\");\n db.exec(`\n CREATE TABLE message (id TEXT, session_id TEXT, data TEXT, time_created INTEGER, time_updated INTEGER);\n CREATE TABLE part (message_id TEXT, session_id TEXT, data TEXT, time_created INTEGER);\n `);\n parser = new OpenCodeSessionParser();\n });\n\n afterEach(() => {\n db.close();\n });\n\n it(\"extracts session stats from OpenCode messages and parts\", () => {\n insertMessage(db, {\n id: \"msg-user\",\n sessionId: \"sess-1\",\n role: \"user\",\n timeCreated: 1000,\n timeUpdated: 1000,\n });\n insertMessage(db, {\n id: \"msg-assistant\",\n sessionId: \"sess-1\",\n role: \"assistant\",\n timeCreated: 2000,\n timeUpdated: 2500,\n completed: 2400,\n });\n insertPart(db, {\n messageId: \"msg-user\",\n sessionId: \"sess-1\",\n data: { type: \"text\", text: \"Plan the migration\" },\n timeCreated: 1000,\n });\n\n expect(parser.getSessionStats(db, \"sess-1\")).toEqual({\n lastRole: \"assistant\",\n lastTimeUpdated: 2500,\n lastAssistantCompleted: true,\n lastAssistantErrored: false,\n summary: \"Plan the migration\",\n });\n });\n\n it(\"orders stats by message creation time, not metadata update time\", () => {\n insertMessage(db, {\n id: \"msg-user\",\n sessionId: \"sess-1\",\n role: \"user\",\n timeCreated: 1000,\n timeUpdated: 3000,\n });\n insertMessage(db, {\n id: \"msg-assistant\",\n sessionId: \"sess-1\",\n role: \"assistant\",\n timeCreated: 2000,\n timeUpdated: 2100,\n completed: 2200,\n });\n\n expect(parser.getSessionStats(db, \"sess-1\").lastRole).toBe(\"assistant\");\n });\n\n it(\"reads visible conversation text and hides verbose-only parts by default\", () => {\n insertMessage(db, {\n id: \"msg-user\",\n sessionId: \"sess-1\",\n role: \"user\",\n timeCreated: 1000,\n timeUpdated: 1000,\n });\n insertMessage(db, {\n id: \"msg-assistant\",\n sessionId: \"sess-1\",\n role: \"assistant\",\n timeCreated: 2000,\n timeUpdated: 2000,\n });\n insertPart(db, {\n messageId: \"msg-user\",\n sessionId: \"sess-1\",\n data: { type: \"text\", text: \"Hello\" },\n timeCreated: 1000,\n });\n insertPart(db, {\n messageId: \"msg-assistant\",\n sessionId: \"sess-1\",\n data: { type: \"reasoning\", reasoning: \"private thought\" },\n timeCreated: 2000,\n });\n insertPart(db, {\n messageId: \"msg-assistant\",\n sessionId: \"sess-1\",\n data: { type: \"tool\", tool: \"read_file\" },\n timeCreated: 3000,\n });\n\n expect(parser.getConversation(db, \"sess-1\")).toEqual([{ role: \"user\", content: \"Hello\" }]);\n expect(parser.getConversation(db, \"sess-1\", { verbose: true })).toEqual([\n { role: \"user\", content: \"Hello\" },\n { role: \"assistant\", content: \"[thinking] private thought\" },\n { role: \"assistant\", content: \"[tool: read_file]\" },\n ]);\n });\n});\n\nfunction insertMessage(\n db: Database.Database,\n input: {\n id: string;\n sessionId: string;\n role: string;\n timeCreated: number;\n timeUpdated: number;\n completed?: number;\n },\n): void {\n db.prepare(\n \"INSERT INTO message (id, session_id, data, time_created, time_updated) VALUES (?, ?, ?, ?, ?)\",\n ).run(\n input.id,\n input.sessionId,\n JSON.stringify({\n role: input.role,\n time: input.completed === undefined ? {} : { completed: input.completed },\n }),\n input.timeCreated,\n input.timeUpdated,\n );\n}\n\nfunction insertPart(\n db: Database.Database,\n input: { messageId: string; sessionId: string; data: unknown; timeCreated: number },\n): void {\n db.prepare(\"INSERT INTO part (message_id, session_id, data, time_created) VALUES (?, ?, ?, ?)\")\n .run(input.messageId, input.sessionId, JSON.stringify(input.data), input.timeCreated);\n}\n"],"names":["Database","afterEach","beforeEach","describe","expect","it","OpenCodeSessionParser","db","parser","exec","close","insertMessage","id","sessionId","role","timeCreated","timeUpdated","completed","insertPart","messageId","data","type","text","getSessionStats","toEqual","lastRole","lastTimeUpdated","lastAssistantCompleted","lastAssistantErrored","summary","toBe","reasoning","tool","getConversation","content","verbose","input","prepare","run","JSON","stringify","time","undefined"],"mappings":"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"}
@@ -300,6 +300,36 @@ describe("PiAdapter", ()=>{
300
300
  })
301
301
  ]);
302
302
  });
303
+ it("finds an exact session ID from the filename suffix", async ()=>{
304
+ const wanted = writePiSession("/repo/wanted", [
305
+ {
306
+ timestamp: "2026-06-10T08:58:20.754Z",
307
+ sessionId: "sess-wanted",
308
+ cwd: "/repo/wanted"
309
+ },
310
+ {
311
+ role: "user",
312
+ content: "wanted"
313
+ }
314
+ ]);
315
+ writePiSession("/repo/other", [
316
+ {
317
+ timestamp: "2026-06-10T08:58:20.754Z",
318
+ sessionId: "sess-other",
319
+ cwd: "/repo/other"
320
+ },
321
+ {
322
+ role: "user",
323
+ content: "other"
324
+ }
325
+ ]);
326
+ await expect(adapter.findSessionsById("sess-wanted")).resolves.toEqual([
327
+ expect.objectContaining({
328
+ sessionId: "sess-wanted",
329
+ sessionFilePath: wanted
330
+ })
331
+ ]);
332
+ });
303
333
  function makeProcess(overrides) {
304
334
  return {
305
335
  pid: 1,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/__tests__/providers/pi/PiAdapter.test.ts"],"sourcesContent":["/**\n * Tests for PiAdapter provider orchestration.\n */\n\nimport type { MockedFunction } from \"vitest\";\nimport * as fs from \"fs\";\nimport * as os from \"os\";\nimport * as path from \"path\";\n\nimport { AgentStatus, type ProcessInfo } from \"../../../adapters/AgentAdapter.js\";\nimport { PiAdapter } from \"../../../providers/pi/PiAdapter.js\";\nimport { AgentRegistry } from \"../../../utils/AgentRegistry.js\";\nimport { captureProcessSnapshot } from \"../../../utils/process.js\";\nimport { generateAgentName, matchProcessesToSessions } from \"../../../utils/matching.js\";\n\nvi.mock(\"../../../utils/process.js\", async (importOriginal) => {\n const actual = (await importOriginal()) as typeof import(\"../../../utils/process.js\");\n return {\n ...actual,\n captureProcessSnapshot: vi.fn(),\n };\n});\n\nvi.mock(\"../../../utils/matching.js\", () => ({\n matchProcessesToSessions: vi.fn(),\n generateAgentName: vi.fn(),\n}));\n\nconst mockedCaptureProcessSnapshot = captureProcessSnapshot as MockedFunction<\n typeof captureProcessSnapshot\n>;\nconst mockedMatchProcessesToSessions = matchProcessesToSessions as MockedFunction<\n typeof matchProcessesToSessions\n>;\nconst mockedGenerateAgentName = generateAgentName as MockedFunction<typeof generateAgentName>;\n\ndescribe(\"PiAdapter\", () => {\n let adapter: PiAdapter;\n let tmpHome: string;\n let sessionsDir: string;\n\n beforeEach(() => {\n tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), \"pi-adapter-test-\"));\n process.env.HOME = tmpHome;\n sessionsDir = path.join(tmpHome, \".pi\", \"agent\", \"sessions\");\n fs.mkdirSync(sessionsDir, { recursive: true });\n\n adapter = new PiAdapter(new AgentRegistry(path.join(tmpHome, \"agents.json\")));\n mockedCaptureProcessSnapshot.mockReset();\n mockedMatchProcessesToSessions.mockReset();\n mockedGenerateAgentName.mockReset();\n\n mockedCaptureProcessSnapshot.mockResolvedValue([]);\n mockedMatchProcessesToSessions.mockReturnValue([]);\n mockedGenerateAgentName.mockImplementation((cwd: string, pid: number) => {\n const folder = path.basename(cwd) || \"unknown\";\n return `${folder} (${pid})`;\n });\n });\n\n afterEach(() => {\n fs.rmSync(tmpHome, { recursive: true, force: true });\n });\n\n it(\"exposes pi type\", () => {\n expect(adapter.type).toBe(\"pi\");\n });\n\n it(\"identifies Pi commands without matching unrelated paths\", () => {\n expect(adapter.canHandle({ pid: 1, command: \"pi\", cwd: \"/repo\", tty: \"ttys001\" })).toBe(true);\n expect(\n adapter.canHandle({\n pid: 2,\n command: \"/usr/local/bin/PI --model x\",\n cwd: \"/repo\",\n tty: \"ttys002\",\n }),\n ).toBe(true);\n expect(\n adapter.canHandle({\n pid: 3,\n command: \"node /opt/pi/bin/pi.js\",\n cwd: \"/repo\",\n tty: \"ttys003\",\n }),\n ).toBe(true);\n expect(\n adapter.canHandle({\n pid: 4,\n command: \"node /repo/feature-pi-adapter/script.js\",\n cwd: \"/repo\",\n tty: \"ttys004\",\n }),\n ).toBe(false);\n expect(\n adapter.canHandle({\n pid: 5,\n command: \"C:\\\\tools\\\\node.exe C:\\\\lib\\\\pi.js\",\n cwd: \"/repo\",\n tty: \"ttys005\",\n }),\n ).toBe(true);\n });\n\n it(\"rejects foreign executables from a hand-built snapshot context\", async () => {\n const foreign: ProcessInfo = {\n pid: 6,\n command: \"claude --resume /Users/x/repos/pi/session.jsonl\",\n cwd: \"/repo\",\n tty: \"ttys006\",\n };\n\n expect(await adapter.detectAgents({ processes: [foreign] })).toEqual([]);\n });\n\n it(\"maps a running Pi process to the tracker session for its PID\", async () => {\n const cwd = \"/repo/project-a\";\n const proc = makeProcess({ pid: 101, cwd });\n const sessionFile = writePiSession(cwd, [\n { type: \"session_meta\", timestamp: \"2026-06-10T08:58:20.754Z\", sessionId: \"sess-101\", cwd },\n { role: \"user\", timestamp: \"2026-06-10T08:58:21.000Z\", content: \"implement Pi adapter\" },\n { role: \"assistant\", timestamp: new Date().toISOString(), content: \"working on it\" },\n ]);\n writeTracker({ 101: sessionFile });\n mockedCaptureProcessSnapshot.mockResolvedValue([proc]);\n\n const agents = await adapter.detectAgents();\n\n expect(agents).toHaveLength(1);\n expect(agents[0]).toMatchObject({\n type: \"pi\",\n pid: 101,\n projectPath: cwd,\n sessionId: \"sess-101\",\n summary: \"implement Pi adapter\",\n status: AgentStatus.WAITING,\n sessionFilePath: sessionFile,\n });\n expect(mockedMatchProcessesToSessions).not.toHaveBeenCalled();\n });\n\n it(\"falls back to legacy matching when sessions.json is missing\", async () => {\n const cwd = \"/repo/project-b\";\n const proc = makeProcess({ pid: 202, cwd });\n const sessionFile = writePiSession(cwd, [\n { timestamp: \"2026-06-10T08:58:20.754Z\", sessionId: \"sess-202\" },\n { role: \"user\", timestamp: \"2026-06-10T08:58:21.000Z\", content: \"fallback matching please\" },\n ]);\n mockedCaptureProcessSnapshot.mockResolvedValue([proc]);\n mockedMatchProcessesToSessions.mockReturnValue([\n {\n process: proc,\n session: {\n sessionId: \"sess-202\",\n filePath: sessionFile,\n projectDir: path.dirname(sessionFile),\n birthtimeMs: Date.now(),\n resolvedCwd: cwd,\n },\n deltaMs: 0,\n },\n ]);\n\n const agents = await adapter.detectAgents();\n\n expect(agents).toHaveLength(1);\n expect(agents[0]).toMatchObject({\n type: \"pi\",\n pid: 202,\n projectPath: cwd,\n sessionId: \"sess-202\",\n summary: \"fallback matching please\",\n });\n expect(mockedMatchProcessesToSessions).toHaveBeenCalledWith(\n [proc],\n expect.arrayContaining([\n expect.objectContaining({ filePath: sessionFile, resolvedCwd: cwd }),\n ]),\n );\n });\n\n it(\"falls back to legacy matching when a trusted tracker session is unparseable\", async () => {\n const cwd = \"/repo/project-bad-tracker-session\";\n const proc = makeProcess({ pid: 304, cwd });\n const badSessionFile = writePiSessionWithFileName(cwd, \"bad.jsonl\", [\"{not json\"]);\n const fallbackSessionFile = writePiSession(cwd, [\n { timestamp: \"2026-06-10T08:58:20.754Z\", sessionId: \"sess-304\", cwd },\n {\n role: \"user\",\n timestamp: \"2026-06-10T08:58:21.000Z\",\n content: \"fallback after bad tracker session\",\n },\n ]);\n writeTracker({ 304: badSessionFile });\n mockedCaptureProcessSnapshot.mockResolvedValue([proc]);\n mockedMatchProcessesToSessions.mockReturnValue([\n {\n process: proc,\n session: {\n sessionId: \"sess-304\",\n filePath: fallbackSessionFile,\n projectDir: path.dirname(fallbackSessionFile),\n birthtimeMs: Date.now(),\n resolvedCwd: cwd,\n },\n deltaMs: 0,\n },\n ]);\n\n const agents = await adapter.detectAgents();\n\n expect(agents).toHaveLength(1);\n expect(agents[0]).toMatchObject({\n sessionId: \"sess-304\",\n summary: \"fallback after bad tracker session\",\n sessionFilePath: fallbackSessionFile,\n });\n expect(mockedMatchProcessesToSessions).toHaveBeenCalled();\n });\n\n it(\"returns a process-only agent when no session can be matched\", async () => {\n const proc = makeProcess({ pid: 505, cwd: \"/repo/project-e\" });\n mockedCaptureProcessSnapshot.mockResolvedValue([proc]);\n\n const agents = await adapter.detectAgents();\n\n expect(agents).toHaveLength(1);\n expect(agents[0]).toMatchObject({\n type: \"pi\",\n status: AgentStatus.RUNNING,\n pid: 505,\n projectPath: \"/repo/project-e\",\n sessionId: \"pid-505\",\n summary: \"Pi process running\",\n });\n });\n\n it(\"lists historical sessions and applies cwd filtering\", async () => {\n const matchingCwd = \"/repo/project-g\";\n const otherCwd = \"/repo/project-h\";\n const matchingSession = writePiSession(matchingCwd, [\n { timestamp: \"2026-06-10T08:58:20.754Z\", sessionId: \"sess-g\", cwd: matchingCwd },\n { role: \"user\", timestamp: \"2026-06-10T08:58:21.000Z\", content: \"first matching message\" },\n ]);\n writePiSession(otherCwd, [\n { timestamp: \"2026-06-10T08:58:20.754Z\", sessionId: \"sess-h\", cwd: otherCwd },\n { role: \"user\", timestamp: \"2026-06-10T08:58:21.000Z\", content: \"other message\" },\n ]);\n\n const sessions = await adapter.listSessions({ cwd: matchingCwd });\n\n expect(sessions).toEqual([\n expect.objectContaining({\n type: \"pi\",\n sessionId: \"sess-g\",\n cwd: matchingCwd,\n firstUserMessage: \"first matching message\",\n sessionFilePath: matchingSession,\n }),\n ]);\n });\n\n function makeProcess(overrides: Partial<ProcessInfo>): ProcessInfo {\n return {\n pid: 1,\n command: \"pi\",\n cwd: \"/repo\",\n tty: \"ttys001\",\n startTime: new Date(\"2026-06-10T08:58:20.000Z\"),\n ...overrides,\n };\n }\n\n function writeTracker(entries: Record<number, string>): void {\n fs.writeFileSync(path.join(tmpHome, \".pi\", \"agent\", \"sessions.json\"), JSON.stringify(entries));\n }\n\n function writePiSession(cwd: string, entries: Array<Record<string, unknown> | string>): string {\n const sessionId =\n entries\n .map((entry) => (typeof entry === \"string\" ? undefined : entry.sessionId))\n .find((value): value is string => typeof value === \"string\") ?? cryptoRandomSessionId();\n return writePiSessionWithFileName(cwd, `2026-06-10T08-58-20-754Z_${sessionId}.jsonl`, entries);\n }\n\n function writePiSessionWithFileName(\n cwd: string,\n fileName: string,\n entries: Array<Record<string, unknown> | string>,\n ): string {\n const projectDir = path.join(sessionsDir, encodeProjectDir(cwd));\n fs.mkdirSync(projectDir, { recursive: true });\n const filePath = path.join(projectDir, fileName);\n fs.writeFileSync(\n filePath,\n entries\n .map((entry) => (typeof entry === \"string\" ? entry : JSON.stringify(entry)))\n .join(\"\\n\"),\n );\n return filePath;\n }\n});\n\nfunction encodeProjectDir(cwd: string): string {\n return cwd.replace(/\\//g, \"-\").replace(/^-?/, \"--\") + \"--\";\n}\n\nfunction cryptoRandomSessionId(): string {\n return `019eb0c1-06d2-71ed-90ee-${Math.random().toString(16).slice(2, 14).padEnd(12, \"0\")}`;\n}\n"],"names":["fs","os","path","AgentStatus","PiAdapter","AgentRegistry","captureProcessSnapshot","generateAgentName","matchProcessesToSessions","vi","mock","importOriginal","actual","fn","mockedCaptureProcessSnapshot","mockedMatchProcessesToSessions","mockedGenerateAgentName","describe","adapter","tmpHome","sessionsDir","beforeEach","mkdtempSync","join","tmpdir","process","env","HOME","mkdirSync","recursive","mockReset","mockResolvedValue","mockReturnValue","mockImplementation","cwd","pid","folder","basename","afterEach","rmSync","force","it","expect","type","toBe","canHandle","command","tty","foreign","detectAgents","processes","toEqual","proc","makeProcess","sessionFile","writePiSession","timestamp","sessionId","role","content","Date","toISOString","writeTracker","agents","toHaveLength","toMatchObject","projectPath","summary","status","WAITING","sessionFilePath","not","toHaveBeenCalled","session","filePath","projectDir","dirname","birthtimeMs","now","resolvedCwd","deltaMs","toHaveBeenCalledWith","arrayContaining","objectContaining","badSessionFile","writePiSessionWithFileName","fallbackSessionFile","RUNNING","matchingCwd","otherCwd","matchingSession","sessions","listSessions","firstUserMessage","overrides","startTime","entries","writeFileSync","JSON","stringify","map","entry","undefined","find","value","cryptoRandomSessionId","fileName","encodeProjectDir","replace","Math","random","toString","slice","padEnd"],"mappings":"AAAA;;CAEC,GAGD,YAAYA,QAAQ,KAAK;AACzB,YAAYC,QAAQ,KAAK;AACzB,YAAYC,UAAU,OAAO;AAE7B,SAASC,WAAW,QAA0B,oCAAoC;AAClF,SAASC,SAAS,QAAQ,qCAAqC;AAC/D,SAASC,aAAa,QAAQ,kCAAkC;AAChE,SAASC,sBAAsB,QAAQ,4BAA4B;AACnE,SAASC,iBAAiB,EAAEC,wBAAwB,QAAQ,6BAA6B;AAEzFC,GAAGC,IAAI,CAAC,6BAA6B,OAAOC;IAC1C,MAAMC,SAAU,MAAMD;IACtB,OAAO;QACL,GAAGC,MAAM;QACTN,wBAAwBG,GAAGI,EAAE;IAC/B;AACF;AAEAJ,GAAGC,IAAI,CAAC,8BAA8B,IAAO,CAAA;QAC3CF,0BAA0BC,GAAGI,EAAE;QAC/BN,mBAAmBE,GAAGI,EAAE;IAC1B,CAAA;AAEA,MAAMC,+BAA+BR;AAGrC,MAAMS,iCAAiCP;AAGvC,MAAMQ,0BAA0BT;AAEhCU,SAAS,aAAa;IACpB,IAAIC;IACJ,IAAIC;IACJ,IAAIC;IAEJC,WAAW;QACTF,UAAUnB,GAAGsB,WAAW,CAACpB,KAAKqB,IAAI,CAACtB,GAAGuB,MAAM,IAAI;QAChDC,QAAQC,GAAG,CAACC,IAAI,GAAGR;QACnBC,cAAclB,KAAKqB,IAAI,CAACJ,SAAS,OAAO,SAAS;QACjDnB,GAAG4B,SAAS,CAACR,aAAa;YAAES,WAAW;QAAK;QAE5CX,UAAU,IAAId,UAAU,IAAIC,cAAcH,KAAKqB,IAAI,CAACJ,SAAS;QAC7DL,6BAA6BgB,SAAS;QACtCf,+BAA+Be,SAAS;QACxCd,wBAAwBc,SAAS;QAEjChB,6BAA6BiB,iBAAiB,CAAC,EAAE;QACjDhB,+BAA+BiB,eAAe,CAAC,EAAE;QACjDhB,wBAAwBiB,kBAAkB,CAAC,CAACC,KAAaC;YACvD,MAAMC,SAASlC,KAAKmC,QAAQ,CAACH,QAAQ;YACrC,OAAO,GAAGE,OAAO,EAAE,EAAED,IAAI,CAAC,CAAC;QAC7B;IACF;IAEAG,UAAU;QACRtC,GAAGuC,MAAM,CAACpB,SAAS;YAAEU,WAAW;YAAMW,OAAO;QAAK;IACpD;IAEAC,GAAG,mBAAmB;QACpBC,OAAOxB,QAAQyB,IAAI,EAAEC,IAAI,CAAC;IAC5B;IAEAH,GAAG,2DAA2D;QAC5DC,OAAOxB,QAAQ2B,SAAS,CAAC;YAAEV,KAAK;YAAGW,SAAS;YAAMZ,KAAK;YAASa,KAAK;QAAU,IAAIH,IAAI,CAAC;QACxFF,OACExB,QAAQ2B,SAAS,CAAC;YAChBV,KAAK;YACLW,SAAS;YACTZ,KAAK;YACLa,KAAK;QACP,IACAH,IAAI,CAAC;QACPF,OACExB,QAAQ2B,SAAS,CAAC;YAChBV,KAAK;YACLW,SAAS;YACTZ,KAAK;YACLa,KAAK;QACP,IACAH,IAAI,CAAC;QACPF,OACExB,QAAQ2B,SAAS,CAAC;YAChBV,KAAK;YACLW,SAAS;YACTZ,KAAK;YACLa,KAAK;QACP,IACAH,IAAI,CAAC;QACPF,OACExB,QAAQ2B,SAAS,CAAC;YAChBV,KAAK;YACLW,SAAS;YACTZ,KAAK;YACLa,KAAK;QACP,IACAH,IAAI,CAAC;IACT;IAEAH,GAAG,kEAAkE;QACnE,MAAMO,UAAuB;YAC3Bb,KAAK;YACLW,SAAS;YACTZ,KAAK;YACLa,KAAK;QACP;QAEAL,OAAO,MAAMxB,QAAQ+B,YAAY,CAAC;YAAEC,WAAW;gBAACF;aAAQ;QAAC,IAAIG,OAAO,CAAC,EAAE;IACzE;IAEAV,GAAG,gEAAgE;QACjE,MAAMP,MAAM;QACZ,MAAMkB,OAAOC,YAAY;YAAElB,KAAK;YAAKD;QAAI;QACzC,MAAMoB,cAAcC,eAAerB,KAAK;YACtC;gBAAES,MAAM;gBAAgBa,WAAW;gBAA4BC,WAAW;gBAAYvB;YAAI;YAC1F;gBAAEwB,MAAM;gBAAQF,WAAW;gBAA4BG,SAAS;YAAuB;YACvF;gBAAED,MAAM;gBAAaF,WAAW,IAAII,OAAOC,WAAW;gBAAIF,SAAS;YAAgB;SACpF;QACDG,aAAa;YAAE,KAAKR;QAAY;QAChCxC,6BAA6BiB,iBAAiB,CAAC;YAACqB;SAAK;QAErD,MAAMW,SAAS,MAAM7C,QAAQ+B,YAAY;QAEzCP,OAAOqB,QAAQC,YAAY,CAAC;QAC5BtB,OAAOqB,MAAM,CAAC,EAAE,EAAEE,aAAa,CAAC;YAC9BtB,MAAM;YACNR,KAAK;YACL+B,aAAahC;YACbuB,WAAW;YACXU,SAAS;YACTC,QAAQjE,YAAYkE,OAAO;YAC3BC,iBAAiBhB;QACnB;QACAZ,OAAO3B,gCAAgCwD,GAAG,CAACC,gBAAgB;IAC7D;IAEA/B,GAAG,+DAA+D;QAChE,MAAMP,MAAM;QACZ,MAAMkB,OAAOC,YAAY;YAAElB,KAAK;YAAKD;QAAI;QACzC,MAAMoB,cAAcC,eAAerB,KAAK;YACtC;gBAAEsB,WAAW;gBAA4BC,WAAW;YAAW;YAC/D;gBAAEC,MAAM;gBAAQF,WAAW;gBAA4BG,SAAS;YAA2B;SAC5F;QACD7C,6BAA6BiB,iBAAiB,CAAC;YAACqB;SAAK;QACrDrC,+BAA+BiB,eAAe,CAAC;YAC7C;gBACEP,SAAS2B;gBACTqB,SAAS;oBACPhB,WAAW;oBACXiB,UAAUpB;oBACVqB,YAAYzE,KAAK0E,OAAO,CAACtB;oBACzBuB,aAAajB,KAAKkB,GAAG;oBACrBC,aAAa7C;gBACf;gBACA8C,SAAS;YACX;SACD;QAED,MAAMjB,SAAS,MAAM7C,QAAQ+B,YAAY;QAEzCP,OAAOqB,QAAQC,YAAY,CAAC;QAC5BtB,OAAOqB,MAAM,CAAC,EAAE,EAAEE,aAAa,CAAC;YAC9BtB,MAAM;YACNR,KAAK;YACL+B,aAAahC;YACbuB,WAAW;YACXU,SAAS;QACX;QACAzB,OAAO3B,gCAAgCkE,oBAAoB,CACzD;YAAC7B;SAAK,EACNV,OAAOwC,eAAe,CAAC;YACrBxC,OAAOyC,gBAAgB,CAAC;gBAAET,UAAUpB;gBAAayB,aAAa7C;YAAI;SACnE;IAEL;IAEAO,GAAG,+EAA+E;QAChF,MAAMP,MAAM;QACZ,MAAMkB,OAAOC,YAAY;YAAElB,KAAK;YAAKD;QAAI;QACzC,MAAMkD,iBAAiBC,2BAA2BnD,KAAK,aAAa;YAAC;SAAY;QACjF,MAAMoD,sBAAsB/B,eAAerB,KAAK;YAC9C;gBAAEsB,WAAW;gBAA4BC,WAAW;gBAAYvB;YAAI;YACpE;gBACEwB,MAAM;gBACNF,WAAW;gBACXG,SAAS;YACX;SACD;QACDG,aAAa;YAAE,KAAKsB;QAAe;QACnCtE,6BAA6BiB,iBAAiB,CAAC;YAACqB;SAAK;QACrDrC,+BAA+BiB,eAAe,CAAC;YAC7C;gBACEP,SAAS2B;gBACTqB,SAAS;oBACPhB,WAAW;oBACXiB,UAAUY;oBACVX,YAAYzE,KAAK0E,OAAO,CAACU;oBACzBT,aAAajB,KAAKkB,GAAG;oBACrBC,aAAa7C;gBACf;gBACA8C,SAAS;YACX;SACD;QAED,MAAMjB,SAAS,MAAM7C,QAAQ+B,YAAY;QAEzCP,OAAOqB,QAAQC,YAAY,CAAC;QAC5BtB,OAAOqB,MAAM,CAAC,EAAE,EAAEE,aAAa,CAAC;YAC9BR,WAAW;YACXU,SAAS;YACTG,iBAAiBgB;QACnB;QACA5C,OAAO3B,gCAAgCyD,gBAAgB;IACzD;IAEA/B,GAAG,+DAA+D;QAChE,MAAMW,OAAOC,YAAY;YAAElB,KAAK;YAAKD,KAAK;QAAkB;QAC5DpB,6BAA6BiB,iBAAiB,CAAC;YAACqB;SAAK;QAErD,MAAMW,SAAS,MAAM7C,QAAQ+B,YAAY;QAEzCP,OAAOqB,QAAQC,YAAY,CAAC;QAC5BtB,OAAOqB,MAAM,CAAC,EAAE,EAAEE,aAAa,CAAC;YAC9BtB,MAAM;YACNyB,QAAQjE,YAAYoF,OAAO;YAC3BpD,KAAK;YACL+B,aAAa;YACbT,WAAW;YACXU,SAAS;QACX;IACF;IAEA1B,GAAG,uDAAuD;QACxD,MAAM+C,cAAc;QACpB,MAAMC,WAAW;QACjB,MAAMC,kBAAkBnC,eAAeiC,aAAa;YAClD;gBAAEhC,WAAW;gBAA4BC,WAAW;gBAAUvB,KAAKsD;YAAY;YAC/E;gBAAE9B,MAAM;gBAAQF,WAAW;gBAA4BG,SAAS;YAAyB;SAC1F;QACDJ,eAAekC,UAAU;YACvB;gBAAEjC,WAAW;gBAA4BC,WAAW;gBAAUvB,KAAKuD;YAAS;YAC5E;gBAAE/B,MAAM;gBAAQF,WAAW;gBAA4BG,SAAS;YAAgB;SACjF;QAED,MAAMgC,WAAW,MAAMzE,QAAQ0E,YAAY,CAAC;YAAE1D,KAAKsD;QAAY;QAE/D9C,OAAOiD,UAAUxC,OAAO,CAAC;YACvBT,OAAOyC,gBAAgB,CAAC;gBACtBxC,MAAM;gBACNc,WAAW;gBACXvB,KAAKsD;gBACLK,kBAAkB;gBAClBvB,iBAAiBoB;YACnB;SACD;IACH;IAEA,SAASrC,YAAYyC,SAA+B;QAClD,OAAO;YACL3D,KAAK;YACLW,SAAS;YACTZ,KAAK;YACLa,KAAK;YACLgD,WAAW,IAAInC,KAAK;YACpB,GAAGkC,SAAS;QACd;IACF;IAEA,SAAShC,aAAakC,OAA+B;QACnDhG,GAAGiG,aAAa,CAAC/F,KAAKqB,IAAI,CAACJ,SAAS,OAAO,SAAS,kBAAkB+E,KAAKC,SAAS,CAACH;IACvF;IAEA,SAASzC,eAAerB,GAAW,EAAE8D,OAAgD;QACnF,MAAMvC,YACJuC,QACGI,GAAG,CAAC,CAACC,QAAW,OAAOA,UAAU,WAAWC,YAAYD,MAAM5C,SAAS,EACvE8C,IAAI,CAAC,CAACC,QAA2B,OAAOA,UAAU,aAAaC;QACpE,OAAOpB,2BAA2BnD,KAAK,CAAC,yBAAyB,EAAEuB,UAAU,MAAM,CAAC,EAAEuC;IACxF;IAEA,SAASX,2BACPnD,GAAW,EACXwE,QAAgB,EAChBV,OAAgD;QAEhD,MAAMrB,aAAazE,KAAKqB,IAAI,CAACH,aAAauF,iBAAiBzE;QAC3DlC,GAAG4B,SAAS,CAAC+C,YAAY;YAAE9C,WAAW;QAAK;QAC3C,MAAM6C,WAAWxE,KAAKqB,IAAI,CAACoD,YAAY+B;QACvC1G,GAAGiG,aAAa,CACdvB,UACAsB,QACGI,GAAG,CAAC,CAACC,QAAW,OAAOA,UAAU,WAAWA,QAAQH,KAAKC,SAAS,CAACE,QACnE9E,IAAI,CAAC;QAEV,OAAOmD;IACT;AACF;AAEA,SAASiC,iBAAiBzE,GAAW;IACnC,OAAOA,IAAI0E,OAAO,CAAC,OAAO,KAAKA,OAAO,CAAC,OAAO,QAAQ;AACxD;AAEA,SAASH;IACP,OAAO,CAAC,wBAAwB,EAAEI,KAAKC,MAAM,GAAGC,QAAQ,CAAC,IAAIC,KAAK,CAAC,GAAG,IAAIC,MAAM,CAAC,IAAI,MAAM;AAC7F"}
1
+ {"version":3,"sources":["../../../../src/__tests__/providers/pi/PiAdapter.test.ts"],"sourcesContent":["/**\n * Tests for PiAdapter provider orchestration.\n */\n\nimport type { MockedFunction } from \"vitest\";\nimport * as fs from \"fs\";\nimport * as os from \"os\";\nimport * as path from \"path\";\n\nimport { AgentStatus, type ProcessInfo } from \"../../../adapters/AgentAdapter.js\";\nimport { PiAdapter } from \"../../../providers/pi/PiAdapter.js\";\nimport { AgentRegistry } from \"../../../utils/AgentRegistry.js\";\nimport { captureProcessSnapshot } from \"../../../utils/process.js\";\nimport { generateAgentName, matchProcessesToSessions } from \"../../../utils/matching.js\";\n\nvi.mock(\"../../../utils/process.js\", async (importOriginal) => {\n const actual = (await importOriginal()) as typeof import(\"../../../utils/process.js\");\n return {\n ...actual,\n captureProcessSnapshot: vi.fn(),\n };\n});\n\nvi.mock(\"../../../utils/matching.js\", () => ({\n matchProcessesToSessions: vi.fn(),\n generateAgentName: vi.fn(),\n}));\n\nconst mockedCaptureProcessSnapshot = captureProcessSnapshot as MockedFunction<\n typeof captureProcessSnapshot\n>;\nconst mockedMatchProcessesToSessions = matchProcessesToSessions as MockedFunction<\n typeof matchProcessesToSessions\n>;\nconst mockedGenerateAgentName = generateAgentName as MockedFunction<typeof generateAgentName>;\n\ndescribe(\"PiAdapter\", () => {\n let adapter: PiAdapter;\n let tmpHome: string;\n let sessionsDir: string;\n\n beforeEach(() => {\n tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), \"pi-adapter-test-\"));\n process.env.HOME = tmpHome;\n sessionsDir = path.join(tmpHome, \".pi\", \"agent\", \"sessions\");\n fs.mkdirSync(sessionsDir, { recursive: true });\n\n adapter = new PiAdapter(new AgentRegistry(path.join(tmpHome, \"agents.json\")));\n mockedCaptureProcessSnapshot.mockReset();\n mockedMatchProcessesToSessions.mockReset();\n mockedGenerateAgentName.mockReset();\n\n mockedCaptureProcessSnapshot.mockResolvedValue([]);\n mockedMatchProcessesToSessions.mockReturnValue([]);\n mockedGenerateAgentName.mockImplementation((cwd: string, pid: number) => {\n const folder = path.basename(cwd) || \"unknown\";\n return `${folder} (${pid})`;\n });\n });\n\n afterEach(() => {\n fs.rmSync(tmpHome, { recursive: true, force: true });\n });\n\n it(\"exposes pi type\", () => {\n expect(adapter.type).toBe(\"pi\");\n });\n\n it(\"identifies Pi commands without matching unrelated paths\", () => {\n expect(adapter.canHandle({ pid: 1, command: \"pi\", cwd: \"/repo\", tty: \"ttys001\" })).toBe(true);\n expect(\n adapter.canHandle({\n pid: 2,\n command: \"/usr/local/bin/PI --model x\",\n cwd: \"/repo\",\n tty: \"ttys002\",\n }),\n ).toBe(true);\n expect(\n adapter.canHandle({\n pid: 3,\n command: \"node /opt/pi/bin/pi.js\",\n cwd: \"/repo\",\n tty: \"ttys003\",\n }),\n ).toBe(true);\n expect(\n adapter.canHandle({\n pid: 4,\n command: \"node /repo/feature-pi-adapter/script.js\",\n cwd: \"/repo\",\n tty: \"ttys004\",\n }),\n ).toBe(false);\n expect(\n adapter.canHandle({\n pid: 5,\n command: \"C:\\\\tools\\\\node.exe C:\\\\lib\\\\pi.js\",\n cwd: \"/repo\",\n tty: \"ttys005\",\n }),\n ).toBe(true);\n });\n\n it(\"rejects foreign executables from a hand-built snapshot context\", async () => {\n const foreign: ProcessInfo = {\n pid: 6,\n command: \"claude --resume /Users/x/repos/pi/session.jsonl\",\n cwd: \"/repo\",\n tty: \"ttys006\",\n };\n\n expect(await adapter.detectAgents({ processes: [foreign] })).toEqual([]);\n });\n\n it(\"maps a running Pi process to the tracker session for its PID\", async () => {\n const cwd = \"/repo/project-a\";\n const proc = makeProcess({ pid: 101, cwd });\n const sessionFile = writePiSession(cwd, [\n { type: \"session_meta\", timestamp: \"2026-06-10T08:58:20.754Z\", sessionId: \"sess-101\", cwd },\n { role: \"user\", timestamp: \"2026-06-10T08:58:21.000Z\", content: \"implement Pi adapter\" },\n { role: \"assistant\", timestamp: new Date().toISOString(), content: \"working on it\" },\n ]);\n writeTracker({ 101: sessionFile });\n mockedCaptureProcessSnapshot.mockResolvedValue([proc]);\n\n const agents = await adapter.detectAgents();\n\n expect(agents).toHaveLength(1);\n expect(agents[0]).toMatchObject({\n type: \"pi\",\n pid: 101,\n projectPath: cwd,\n sessionId: \"sess-101\",\n summary: \"implement Pi adapter\",\n status: AgentStatus.WAITING,\n sessionFilePath: sessionFile,\n });\n expect(mockedMatchProcessesToSessions).not.toHaveBeenCalled();\n });\n\n it(\"falls back to legacy matching when sessions.json is missing\", async () => {\n const cwd = \"/repo/project-b\";\n const proc = makeProcess({ pid: 202, cwd });\n const sessionFile = writePiSession(cwd, [\n { timestamp: \"2026-06-10T08:58:20.754Z\", sessionId: \"sess-202\" },\n { role: \"user\", timestamp: \"2026-06-10T08:58:21.000Z\", content: \"fallback matching please\" },\n ]);\n mockedCaptureProcessSnapshot.mockResolvedValue([proc]);\n mockedMatchProcessesToSessions.mockReturnValue([\n {\n process: proc,\n session: {\n sessionId: \"sess-202\",\n filePath: sessionFile,\n projectDir: path.dirname(sessionFile),\n birthtimeMs: Date.now(),\n resolvedCwd: cwd,\n },\n deltaMs: 0,\n },\n ]);\n\n const agents = await adapter.detectAgents();\n\n expect(agents).toHaveLength(1);\n expect(agents[0]).toMatchObject({\n type: \"pi\",\n pid: 202,\n projectPath: cwd,\n sessionId: \"sess-202\",\n summary: \"fallback matching please\",\n });\n expect(mockedMatchProcessesToSessions).toHaveBeenCalledWith(\n [proc],\n expect.arrayContaining([\n expect.objectContaining({ filePath: sessionFile, resolvedCwd: cwd }),\n ]),\n );\n });\n\n it(\"falls back to legacy matching when a trusted tracker session is unparseable\", async () => {\n const cwd = \"/repo/project-bad-tracker-session\";\n const proc = makeProcess({ pid: 304, cwd });\n const badSessionFile = writePiSessionWithFileName(cwd, \"bad.jsonl\", [\"{not json\"]);\n const fallbackSessionFile = writePiSession(cwd, [\n { timestamp: \"2026-06-10T08:58:20.754Z\", sessionId: \"sess-304\", cwd },\n {\n role: \"user\",\n timestamp: \"2026-06-10T08:58:21.000Z\",\n content: \"fallback after bad tracker session\",\n },\n ]);\n writeTracker({ 304: badSessionFile });\n mockedCaptureProcessSnapshot.mockResolvedValue([proc]);\n mockedMatchProcessesToSessions.mockReturnValue([\n {\n process: proc,\n session: {\n sessionId: \"sess-304\",\n filePath: fallbackSessionFile,\n projectDir: path.dirname(fallbackSessionFile),\n birthtimeMs: Date.now(),\n resolvedCwd: cwd,\n },\n deltaMs: 0,\n },\n ]);\n\n const agents = await adapter.detectAgents();\n\n expect(agents).toHaveLength(1);\n expect(agents[0]).toMatchObject({\n sessionId: \"sess-304\",\n summary: \"fallback after bad tracker session\",\n sessionFilePath: fallbackSessionFile,\n });\n expect(mockedMatchProcessesToSessions).toHaveBeenCalled();\n });\n\n it(\"returns a process-only agent when no session can be matched\", async () => {\n const proc = makeProcess({ pid: 505, cwd: \"/repo/project-e\" });\n mockedCaptureProcessSnapshot.mockResolvedValue([proc]);\n\n const agents = await adapter.detectAgents();\n\n expect(agents).toHaveLength(1);\n expect(agents[0]).toMatchObject({\n type: \"pi\",\n status: AgentStatus.RUNNING,\n pid: 505,\n projectPath: \"/repo/project-e\",\n sessionId: \"pid-505\",\n summary: \"Pi process running\",\n });\n });\n\n it(\"lists historical sessions and applies cwd filtering\", async () => {\n const matchingCwd = \"/repo/project-g\";\n const otherCwd = \"/repo/project-h\";\n const matchingSession = writePiSession(matchingCwd, [\n { timestamp: \"2026-06-10T08:58:20.754Z\", sessionId: \"sess-g\", cwd: matchingCwd },\n { role: \"user\", timestamp: \"2026-06-10T08:58:21.000Z\", content: \"first matching message\" },\n ]);\n writePiSession(otherCwd, [\n { timestamp: \"2026-06-10T08:58:20.754Z\", sessionId: \"sess-h\", cwd: otherCwd },\n { role: \"user\", timestamp: \"2026-06-10T08:58:21.000Z\", content: \"other message\" },\n ]);\n\n const sessions = await adapter.listSessions({ cwd: matchingCwd });\n\n expect(sessions).toEqual([\n expect.objectContaining({\n type: \"pi\",\n sessionId: \"sess-g\",\n cwd: matchingCwd,\n firstUserMessage: \"first matching message\",\n sessionFilePath: matchingSession,\n }),\n ]);\n });\n\n it(\"finds an exact session ID from the filename suffix\", async () => {\n const wanted = writePiSession(\"/repo/wanted\", [\n { timestamp: \"2026-06-10T08:58:20.754Z\", sessionId: \"sess-wanted\", cwd: \"/repo/wanted\" },\n { role: \"user\", content: \"wanted\" },\n ]);\n writePiSession(\"/repo/other\", [\n { timestamp: \"2026-06-10T08:58:20.754Z\", sessionId: \"sess-other\", cwd: \"/repo/other\" },\n { role: \"user\", content: \"other\" },\n ]);\n\n await expect(adapter.findSessionsById(\"sess-wanted\")).resolves.toEqual([\n expect.objectContaining({ sessionId: \"sess-wanted\", sessionFilePath: wanted }),\n ]);\n });\n\n function makeProcess(overrides: Partial<ProcessInfo>): ProcessInfo {\n return {\n pid: 1,\n command: \"pi\",\n cwd: \"/repo\",\n tty: \"ttys001\",\n startTime: new Date(\"2026-06-10T08:58:20.000Z\"),\n ...overrides,\n };\n }\n\n function writeTracker(entries: Record<number, string>): void {\n fs.writeFileSync(path.join(tmpHome, \".pi\", \"agent\", \"sessions.json\"), JSON.stringify(entries));\n }\n\n function writePiSession(cwd: string, entries: Array<Record<string, unknown> | string>): string {\n const sessionId =\n entries\n .map((entry) => (typeof entry === \"string\" ? undefined : entry.sessionId))\n .find((value): value is string => typeof value === \"string\") ?? cryptoRandomSessionId();\n return writePiSessionWithFileName(cwd, `2026-06-10T08-58-20-754Z_${sessionId}.jsonl`, entries);\n }\n\n function writePiSessionWithFileName(\n cwd: string,\n fileName: string,\n entries: Array<Record<string, unknown> | string>,\n ): string {\n const projectDir = path.join(sessionsDir, encodeProjectDir(cwd));\n fs.mkdirSync(projectDir, { recursive: true });\n const filePath = path.join(projectDir, fileName);\n fs.writeFileSync(\n filePath,\n entries\n .map((entry) => (typeof entry === \"string\" ? entry : JSON.stringify(entry)))\n .join(\"\\n\"),\n );\n return filePath;\n }\n});\n\nfunction encodeProjectDir(cwd: string): string {\n return cwd.replace(/\\//g, \"-\").replace(/^-?/, \"--\") + \"--\";\n}\n\nfunction cryptoRandomSessionId(): string {\n return `019eb0c1-06d2-71ed-90ee-${Math.random().toString(16).slice(2, 14).padEnd(12, \"0\")}`;\n}\n"],"names":["fs","os","path","AgentStatus","PiAdapter","AgentRegistry","captureProcessSnapshot","generateAgentName","matchProcessesToSessions","vi","mock","importOriginal","actual","fn","mockedCaptureProcessSnapshot","mockedMatchProcessesToSessions","mockedGenerateAgentName","describe","adapter","tmpHome","sessionsDir","beforeEach","mkdtempSync","join","tmpdir","process","env","HOME","mkdirSync","recursive","mockReset","mockResolvedValue","mockReturnValue","mockImplementation","cwd","pid","folder","basename","afterEach","rmSync","force","it","expect","type","toBe","canHandle","command","tty","foreign","detectAgents","processes","toEqual","proc","makeProcess","sessionFile","writePiSession","timestamp","sessionId","role","content","Date","toISOString","writeTracker","agents","toHaveLength","toMatchObject","projectPath","summary","status","WAITING","sessionFilePath","not","toHaveBeenCalled","session","filePath","projectDir","dirname","birthtimeMs","now","resolvedCwd","deltaMs","toHaveBeenCalledWith","arrayContaining","objectContaining","badSessionFile","writePiSessionWithFileName","fallbackSessionFile","RUNNING","matchingCwd","otherCwd","matchingSession","sessions","listSessions","firstUserMessage","wanted","findSessionsById","resolves","overrides","startTime","entries","writeFileSync","JSON","stringify","map","entry","undefined","find","value","cryptoRandomSessionId","fileName","encodeProjectDir","replace","Math","random","toString","slice","padEnd"],"mappings":"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"}
@@ -1,13 +1,30 @@
1
1
  /**
2
2
  * Tests for utils/session.ts
3
3
  */ import * as fs from "fs";
4
- import { batchGetSessionFileBirthtimes } from "../../utils/session.js";
4
+ import { batchGetSessionFileBirthtimes, isSafePathSegment } from "../../utils/session.js";
5
5
  vi.mock("fs", ()=>({
6
6
  readdirSync: vi.fn(),
7
7
  statSync: vi.fn()
8
8
  }));
9
9
  const mockedReaddirSync = fs.readdirSync;
10
10
  const mockedStatSync = fs.statSync;
11
+ describe("isSafePathSegment", ()=>{
12
+ it.each([
13
+ "",
14
+ ".",
15
+ "..",
16
+ "nested/session",
17
+ "bad\0id"
18
+ ])("rejects unsafe segment %j", (value)=>{
19
+ expect(isSafePathSegment(value)).toBe(false);
20
+ });
21
+ it.each([
22
+ "session-id",
23
+ ".hidden-session"
24
+ ])("accepts safe segment %j", (value)=>{
25
+ expect(isSafePathSegment(value)).toBe(true);
26
+ });
27
+ });
11
28
  describe("batchGetSessionFileBirthtimes", ()=>{
12
29
  beforeEach(()=>{
13
30
  mockedReaddirSync.mockReset();
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/__tests__/utils/session.test.ts"],"sourcesContent":["/**\n * Tests for utils/session.ts\n */\n\nimport type { MockedFunction } from \"vitest\";\n\nimport * as fs from \"fs\";\nimport { batchGetSessionFileBirthtimes } from \"../../utils/session.js\";\n\nvi.mock(\"fs\", () => ({\n readdirSync: vi.fn(),\n statSync: vi.fn(),\n}));\n\nconst mockedReaddirSync = fs.readdirSync as MockedFunction<typeof fs.readdirSync>;\nconst mockedStatSync = fs.statSync as MockedFunction<typeof fs.statSync>;\n\ndescribe(\"batchGetSessionFileBirthtimes\", () => {\n beforeEach(() => {\n mockedReaddirSync.mockReset();\n mockedStatSync.mockReset();\n });\n\n it(\"should parse session files correctly\", () => {\n mockedReaddirSync.mockReturnValue([\"abc123.jsonl\", \"def456.jsonl\"] as unknown as ReturnType<\n typeof fs.readdirSync\n >);\n mockedStatSync\n .mockReturnValueOnce({ birthtimeMs: 1710800324000 } as fs.Stats)\n .mockReturnValueOnce({ birthtimeMs: 1710800500000 } as fs.Stats);\n\n const results = batchGetSessionFileBirthtimes([\"/home/.claude/projects/my-app\"]);\n\n expect(results).toHaveLength(2);\n expect(results[0]).toEqual({\n sessionId: \"abc123\",\n filePath: \"/home/.claude/projects/my-app/abc123.jsonl\",\n projectDir: \"/home/.claude/projects/my-app\",\n birthtimeMs: 1710800324000,\n resolvedCwd: \"\",\n });\n expect(results[1].sessionId).toBe(\"def456\");\n expect(results[1].birthtimeMs).toBe(1710800500000);\n });\n\n it(\"should return empty array for empty dirs list\", () => {\n expect(batchGetSessionFileBirthtimes([])).toEqual([]);\n expect(mockedReaddirSync).not.toHaveBeenCalled();\n });\n\n it(\"should return empty array on readdir failure\", () => {\n mockedReaddirSync.mockImplementation(() => {\n throw new Error(\"ENOENT\");\n });\n\n expect(batchGetSessionFileBirthtimes([\"/some/dir\"])).toEqual([]);\n });\n\n it(\"should skip files with invalid birthtime (0 or negative)\", () => {\n mockedReaddirSync.mockReturnValue([\n \"bad.jsonl\",\n \"negative.jsonl\",\n \"good.jsonl\",\n ] as unknown as ReturnType<typeof fs.readdirSync>);\n mockedStatSync\n .mockReturnValueOnce({ birthtimeMs: 0 } as fs.Stats)\n .mockReturnValueOnce({ birthtimeMs: -1 } as fs.Stats)\n .mockReturnValueOnce({ birthtimeMs: 1710800324000 } as fs.Stats);\n\n const results = batchGetSessionFileBirthtimes([\"/dir\"]);\n expect(results).toHaveLength(1);\n expect(results[0].sessionId).toBe(\"good\");\n });\n\n it(\"should skip non-jsonl files\", () => {\n mockedReaddirSync.mockReturnValue([\n \"sessions-index.json\",\n \"abc123.jsonl\",\n ] as unknown as ReturnType<typeof fs.readdirSync>);\n mockedStatSync.mockReturnValueOnce({ birthtimeMs: 1710800500000 } as fs.Stats);\n\n const results = batchGetSessionFileBirthtimes([\"/dir\"]);\n expect(results).toHaveLength(1);\n expect(results[0].sessionId).toBe(\"abc123\");\n });\n\n it(\"should handle empty directory\", () => {\n mockedReaddirSync.mockReturnValue([] as unknown as ReturnType<typeof fs.readdirSync>);\n expect(batchGetSessionFileBirthtimes([\"/dir\"])).toEqual([]);\n });\n\n it(\"should handle UUID session IDs\", () => {\n mockedReaddirSync.mockReturnValue([\n \"068e7b1f-cff5-4c94-bf69-b9acd32d765c.jsonl\",\n ] as unknown as ReturnType<typeof fs.readdirSync>);\n mockedStatSync.mockReturnValueOnce({ birthtimeMs: 1710800324000 } as fs.Stats);\n\n const results = batchGetSessionFileBirthtimes([\"/dir\"]);\n expect(results).toHaveLength(1);\n expect(results[0].sessionId).toBe(\"068e7b1f-cff5-4c94-bf69-b9acd32d765c\");\n });\n\n it(\"should leave resolvedCwd empty\", () => {\n mockedReaddirSync.mockReturnValue([\"abc.jsonl\"] as unknown as ReturnType<\n typeof fs.readdirSync\n >);\n mockedStatSync.mockReturnValueOnce({ birthtimeMs: 1710800324000 } as fs.Stats);\n\n const results = batchGetSessionFileBirthtimes([\"/dir\"]);\n expect(results[0].resolvedCwd).toBe(\"\");\n });\n\n it(\"should enumerate multiple directories\", () => {\n mockedReaddirSync\n .mockReturnValueOnce([\"sess1.jsonl\", \"sess3.jsonl\"] as unknown as ReturnType<\n typeof fs.readdirSync\n >)\n .mockReturnValueOnce([\"sess2.jsonl\"] as unknown as ReturnType<typeof fs.readdirSync>);\n mockedStatSync\n .mockReturnValueOnce({ birthtimeMs: 1710800324000 } as fs.Stats)\n .mockReturnValueOnce({ birthtimeMs: 1710800500000 } as fs.Stats)\n .mockReturnValueOnce({ birthtimeMs: 1710800400000 } as fs.Stats);\n\n const results = batchGetSessionFileBirthtimes([\"/projects/app-a\", \"/projects/app-b\"]);\n\n expect(mockedReaddirSync).toHaveBeenCalledTimes(2);\n\n expect(results).toHaveLength(3);\n expect(results[0].projectDir).toBe(\"/projects/app-a\");\n expect(results[1].projectDir).toBe(\"/projects/app-a\");\n expect(results[2].projectDir).toBe(\"/projects/app-b\");\n });\n\n it(\"should skip files where statSync fails\", () => {\n mockedReaddirSync.mockReturnValue([\"good.jsonl\", \"gone.jsonl\"] as unknown as ReturnType<\n typeof fs.readdirSync\n >);\n mockedStatSync\n .mockReturnValueOnce({ birthtimeMs: 1710800324000 } as fs.Stats)\n .mockImplementationOnce(() => {\n throw new Error(\"ENOENT\");\n });\n\n const results = batchGetSessionFileBirthtimes([\"/dir\"]);\n expect(results).toHaveLength(1);\n expect(results[0].sessionId).toBe(\"good\");\n });\n});\n"],"names":["fs","batchGetSessionFileBirthtimes","vi","mock","readdirSync","fn","statSync","mockedReaddirSync","mockedStatSync","describe","beforeEach","mockReset","it","mockReturnValue","mockReturnValueOnce","birthtimeMs","results","expect","toHaveLength","toEqual","sessionId","filePath","projectDir","resolvedCwd","toBe","not","toHaveBeenCalled","mockImplementation","Error","toHaveBeenCalledTimes","mockImplementationOnce"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/__tests__/utils/session.test.ts"],"sourcesContent":["/**\n * Tests for utils/session.ts\n */\n\nimport type { MockedFunction } from \"vitest\";\n\nimport * as fs from \"fs\";\nimport { batchGetSessionFileBirthtimes, isSafePathSegment } from \"../../utils/session.js\";\n\nvi.mock(\"fs\", () => ({\n readdirSync: vi.fn(),\n statSync: vi.fn(),\n}));\n\nconst mockedReaddirSync = fs.readdirSync as MockedFunction<typeof fs.readdirSync>;\nconst mockedStatSync = fs.statSync as MockedFunction<typeof fs.statSync>;\n\ndescribe(\"isSafePathSegment\", () => {\n it.each([\"\", \".\", \"..\", \"nested/session\", \"bad\\0id\"])(\"rejects unsafe segment %j\", (value) => {\n expect(isSafePathSegment(value)).toBe(false);\n });\n\n it.each([\"session-id\", \".hidden-session\"])(\"accepts safe segment %j\", (value) => {\n expect(isSafePathSegment(value)).toBe(true);\n });\n});\n\ndescribe(\"batchGetSessionFileBirthtimes\", () => {\n beforeEach(() => {\n mockedReaddirSync.mockReset();\n mockedStatSync.mockReset();\n });\n\n it(\"should parse session files correctly\", () => {\n mockedReaddirSync.mockReturnValue([\"abc123.jsonl\", \"def456.jsonl\"] as unknown as ReturnType<\n typeof fs.readdirSync\n >);\n mockedStatSync\n .mockReturnValueOnce({ birthtimeMs: 1710800324000 } as fs.Stats)\n .mockReturnValueOnce({ birthtimeMs: 1710800500000 } as fs.Stats);\n\n const results = batchGetSessionFileBirthtimes([\"/home/.claude/projects/my-app\"]);\n\n expect(results).toHaveLength(2);\n expect(results[0]).toEqual({\n sessionId: \"abc123\",\n filePath: \"/home/.claude/projects/my-app/abc123.jsonl\",\n projectDir: \"/home/.claude/projects/my-app\",\n birthtimeMs: 1710800324000,\n resolvedCwd: \"\",\n });\n expect(results[1].sessionId).toBe(\"def456\");\n expect(results[1].birthtimeMs).toBe(1710800500000);\n });\n\n it(\"should return empty array for empty dirs list\", () => {\n expect(batchGetSessionFileBirthtimes([])).toEqual([]);\n expect(mockedReaddirSync).not.toHaveBeenCalled();\n });\n\n it(\"should return empty array on readdir failure\", () => {\n mockedReaddirSync.mockImplementation(() => {\n throw new Error(\"ENOENT\");\n });\n\n expect(batchGetSessionFileBirthtimes([\"/some/dir\"])).toEqual([]);\n });\n\n it(\"should skip files with invalid birthtime (0 or negative)\", () => {\n mockedReaddirSync.mockReturnValue([\n \"bad.jsonl\",\n \"negative.jsonl\",\n \"good.jsonl\",\n ] as unknown as ReturnType<typeof fs.readdirSync>);\n mockedStatSync\n .mockReturnValueOnce({ birthtimeMs: 0 } as fs.Stats)\n .mockReturnValueOnce({ birthtimeMs: -1 } as fs.Stats)\n .mockReturnValueOnce({ birthtimeMs: 1710800324000 } as fs.Stats);\n\n const results = batchGetSessionFileBirthtimes([\"/dir\"]);\n expect(results).toHaveLength(1);\n expect(results[0].sessionId).toBe(\"good\");\n });\n\n it(\"should skip non-jsonl files\", () => {\n mockedReaddirSync.mockReturnValue([\n \"sessions-index.json\",\n \"abc123.jsonl\",\n ] as unknown as ReturnType<typeof fs.readdirSync>);\n mockedStatSync.mockReturnValueOnce({ birthtimeMs: 1710800500000 } as fs.Stats);\n\n const results = batchGetSessionFileBirthtimes([\"/dir\"]);\n expect(results).toHaveLength(1);\n expect(results[0].sessionId).toBe(\"abc123\");\n });\n\n it(\"should handle empty directory\", () => {\n mockedReaddirSync.mockReturnValue([] as unknown as ReturnType<typeof fs.readdirSync>);\n expect(batchGetSessionFileBirthtimes([\"/dir\"])).toEqual([]);\n });\n\n it(\"should handle UUID session IDs\", () => {\n mockedReaddirSync.mockReturnValue([\n \"068e7b1f-cff5-4c94-bf69-b9acd32d765c.jsonl\",\n ] as unknown as ReturnType<typeof fs.readdirSync>);\n mockedStatSync.mockReturnValueOnce({ birthtimeMs: 1710800324000 } as fs.Stats);\n\n const results = batchGetSessionFileBirthtimes([\"/dir\"]);\n expect(results).toHaveLength(1);\n expect(results[0].sessionId).toBe(\"068e7b1f-cff5-4c94-bf69-b9acd32d765c\");\n });\n\n it(\"should leave resolvedCwd empty\", () => {\n mockedReaddirSync.mockReturnValue([\"abc.jsonl\"] as unknown as ReturnType<\n typeof fs.readdirSync\n >);\n mockedStatSync.mockReturnValueOnce({ birthtimeMs: 1710800324000 } as fs.Stats);\n\n const results = batchGetSessionFileBirthtimes([\"/dir\"]);\n expect(results[0].resolvedCwd).toBe(\"\");\n });\n\n it(\"should enumerate multiple directories\", () => {\n mockedReaddirSync\n .mockReturnValueOnce([\"sess1.jsonl\", \"sess3.jsonl\"] as unknown as ReturnType<\n typeof fs.readdirSync\n >)\n .mockReturnValueOnce([\"sess2.jsonl\"] as unknown as ReturnType<typeof fs.readdirSync>);\n mockedStatSync\n .mockReturnValueOnce({ birthtimeMs: 1710800324000 } as fs.Stats)\n .mockReturnValueOnce({ birthtimeMs: 1710800500000 } as fs.Stats)\n .mockReturnValueOnce({ birthtimeMs: 1710800400000 } as fs.Stats);\n\n const results = batchGetSessionFileBirthtimes([\"/projects/app-a\", \"/projects/app-b\"]);\n\n expect(mockedReaddirSync).toHaveBeenCalledTimes(2);\n\n expect(results).toHaveLength(3);\n expect(results[0].projectDir).toBe(\"/projects/app-a\");\n expect(results[1].projectDir).toBe(\"/projects/app-a\");\n expect(results[2].projectDir).toBe(\"/projects/app-b\");\n });\n\n it(\"should skip files where statSync fails\", () => {\n mockedReaddirSync.mockReturnValue([\"good.jsonl\", \"gone.jsonl\"] as unknown as ReturnType<\n typeof fs.readdirSync\n >);\n mockedStatSync\n .mockReturnValueOnce({ birthtimeMs: 1710800324000 } as fs.Stats)\n .mockImplementationOnce(() => {\n throw new Error(\"ENOENT\");\n });\n\n const results = batchGetSessionFileBirthtimes([\"/dir\"]);\n expect(results).toHaveLength(1);\n expect(results[0].sessionId).toBe(\"good\");\n });\n});\n"],"names":["fs","batchGetSessionFileBirthtimes","isSafePathSegment","vi","mock","readdirSync","fn","statSync","mockedReaddirSync","mockedStatSync","describe","it","each","value","expect","toBe","beforeEach","mockReset","mockReturnValue","mockReturnValueOnce","birthtimeMs","results","toHaveLength","toEqual","sessionId","filePath","projectDir","resolvedCwd","not","toHaveBeenCalled","mockImplementation","Error","toHaveBeenCalledTimes","mockImplementationOnce"],"mappings":"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"}
@@ -167,5 +167,13 @@ export interface AgentAdapter {
167
167
  * @returns Array of sessions discovered on disk
168
168
  */
169
169
  listSessions(opts?: ListSessionsOptions): Promise<SessionSummary[]>;
170
+ /**
171
+ * Resolve exact historical session matches without enumerating every summary.
172
+ *
173
+ * Built-in adapters implement this using their provider-native storage shape.
174
+ * The method remains optional so external adapters can fall back to
175
+ * {@link listSessions} until they adopt direct lookup.
176
+ */
177
+ findSessionsById?(sessionId: string): Promise<SessionSummary[]>;
170
178
  }
171
179
  //# sourceMappingURL=AgentAdapter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AgentAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/AgentAdapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AgentAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/AgentAdapter.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/adapters/AgentAdapter.ts"],"sourcesContent":["/**\n * Agent Adapter Interface\n *\n * Defines the contract for detecting and managing different types of AI agents.\n * Each adapter is responsible for detecting agents of a specific type (e.g., claude).\n */\n\n/**\n * Type of AI agent\n */\nexport type AgentType =\n | \"claude\"\n | \"gemini_cli\"\n | \"grok_cli\"\n | \"codex\"\n | \"opencode\"\n | \"copilot\"\n | \"pi\"\n | \"other\";\n\n/**\n * Current status of an agent\n */\nexport enum AgentStatus {\n RUNNING = \"running\",\n WAITING = \"waiting\",\n IDLE = \"idle\",\n UNKNOWN = \"unknown\",\n}\n\n/**\n * Information about a detected agent\n */\nexport interface AgentInfo {\n /** Project-based name (e.g., \"ai-devkit\" or \"ai-devkit (merry)\") */\n name: string;\n\n /** Type of agent */\n type: AgentType;\n\n /** Current status */\n status: AgentStatus;\n\n /** Last user prompt from history */\n summary: string;\n\n /** Process ID */\n pid: number;\n\n /** Working directory/project path */\n projectPath: string;\n\n /** Session UUID */\n sessionId: string;\n\n /** Timestamp of last activity */\n lastActive: Date;\n\n /** Whether the live process is pinned in the agent console */\n pinned?: boolean;\n\n /** Path to the session JSONL file on disk */\n sessionFilePath?: string;\n}\n\n/**\n * Information about a running process\n */\nexport interface ProcessInfo {\n /** Process ID */\n pid: number;\n\n /** Parent process ID, populated by process discovery when available */\n ppid?: number;\n\n /** Process command */\n command: string;\n\n /** Working directory */\n cwd: string;\n\n /** Terminal TTY (e.g., \"ttys030\") */\n tty: string;\n\n /** Process start time, populated by process enrichment */\n startTime?: Date;\n}\n\n/**\n * A single message in a conversation\n */\nexport interface ConversationMessage {\n role: \"user\" | \"assistant\" | \"system\";\n content: string;\n timestamp?: string;\n}\n\n/**\n * A historical session discovered on disk (running or not).\n *\n * Used by `listSessions` to surface enough context for a user to identify\n * a session and resume it via the originating tool's resume command.\n */\nexport interface SessionSummary {\n /** Tool that produced this session */\n type: AgentType;\n\n /**\n * ID accepted by the tool's resume command. Adapters MUST pass this\n * through verbatim — no normalization, no encoding/decoding — so it\n * round-trips into `claude --resume <id>` (and equivalents).\n */\n sessionId: string;\n\n /** Working directory the session was started in (best-known value) */\n cwd: string;\n\n /**\n * Trimmed first user message; empty string if none. Adapters apply\n * the same noise-filter their existing parsers use (skip tool_result\n * blocks, request-interruption notices, system-injected skill\n * content). The CLI table renderer substitutes a placeholder for\n * empty values; JSON output keeps the empty string raw.\n */\n firstUserMessage: string;\n\n /** Last activity timestamp (from session content; falls back to file mtime) */\n lastActive: Date;\n\n /** Session start time (from session content; falls back to file birthtime/mtime) */\n startedAt: Date;\n\n /** Absolute path to the session file on disk (debug/diagnostics) */\n sessionFilePath: string;\n}\n\n/**\n * Filters passed by the CLI to {@link AgentAdapter.listSessions}.\n *\n * The CLI is the source of truth for filter defaults and semantics\n * (e.g. cwd defaults to process.cwd(); --all clears it). Adapters apply\n * the values they receive — they don't invent defaults.\n */\nexport interface ListSessionsOptions {\n /**\n * Filter to sessions whose recorded cwd matches this path using strict\n * equality (no prefix/ancestor matching in v1). Undefined = no cwd\n * filter.\n */\n cwd?: string;\n\n /**\n * Filter to a single tool. Enforced by `AgentManager.listSessions`,\n * which skips adapters whose `type` doesn't match. Adapters MAY\n * ignore this field — by the time their `listSessions` runs, the\n * type filter is already satisfied. Undefined = include every\n * registered adapter.\n */\n type?: AgentType;\n}\n\nexport interface AgentDetectionContext {\n /** One enriched process snapshot shared across this manager refresh. */\n readonly processes: readonly ProcessInfo[];\n}\n\n/**\n * Agent Adapter Interface\n *\n * Implementations must provide detection logic for a specific agent type.\n */\nexport interface AgentAdapter {\n /** Type of agent this adapter handles */\n readonly type: AgentType;\n\n /** Executable basenames required for shared process discovery. */\n readonly processNames?: readonly string[];\n\n /**\n * Detect running agents of this type\n * @returns List of detected agents\n */\n detectAgents(context?: AgentDetectionContext): Promise<AgentInfo[]>;\n\n /**\n * Check if this adapter can handle the given process\n * @param processInfo Process information\n * @returns True if this adapter can handle the process\n */\n canHandle(processInfo: ProcessInfo): boolean;\n\n /**\n * Read the full conversation from a session file\n * @param sessionFilePath Path to the session JSONL file\n * @param options.verbose Include tool call/result details\n * @returns Array of conversation messages\n */\n getConversation(sessionFilePath: string, options?: { verbose?: boolean }): ConversationMessage[];\n\n /**\n * Enumerate historical sessions for this tool from disk.\n *\n * Applies `opts.cwd` as a strict-equality filter when set. Returns\n * {@link SessionSummary} entries unsorted; sorting and global filters\n * are handled by `AgentManager` and the CLI.\n *\n * @param opts Filter options computed by the CLI\n * @returns Array of sessions discovered on disk\n */\n listSessions(opts?: ListSessionsOptions): Promise<SessionSummary[]>;\n}\n"],"names":["AgentStatus"],"mappings":"AAAA;;;;;CAKC,GAED;;CAEC,GAWD;;CAEC,GACD,OAAO,IAAA,AAAKA,qCAAAA;;;;;WAAAA;MAKX"}
1
+ {"version":3,"sources":["../../src/adapters/AgentAdapter.ts"],"sourcesContent":["/**\n * Agent Adapter Interface\n *\n * Defines the contract for detecting and managing different types of AI agents.\n * Each adapter is responsible for detecting agents of a specific type (e.g., claude).\n */\n\n/**\n * Type of AI agent\n */\nexport type AgentType =\n | \"claude\"\n | \"gemini_cli\"\n | \"grok_cli\"\n | \"codex\"\n | \"opencode\"\n | \"copilot\"\n | \"pi\"\n | \"other\";\n\n/**\n * Current status of an agent\n */\nexport enum AgentStatus {\n RUNNING = \"running\",\n WAITING = \"waiting\",\n IDLE = \"idle\",\n UNKNOWN = \"unknown\",\n}\n\n/**\n * Information about a detected agent\n */\nexport interface AgentInfo {\n /** Project-based name (e.g., \"ai-devkit\" or \"ai-devkit (merry)\") */\n name: string;\n\n /** Type of agent */\n type: AgentType;\n\n /** Current status */\n status: AgentStatus;\n\n /** Last user prompt from history */\n summary: string;\n\n /** Process ID */\n pid: number;\n\n /** Working directory/project path */\n projectPath: string;\n\n /** Session UUID */\n sessionId: string;\n\n /** Timestamp of last activity */\n lastActive: Date;\n\n /** Whether the live process is pinned in the agent console */\n pinned?: boolean;\n\n /** Path to the session JSONL file on disk */\n sessionFilePath?: string;\n}\n\n/**\n * Information about a running process\n */\nexport interface ProcessInfo {\n /** Process ID */\n pid: number;\n\n /** Parent process ID, populated by process discovery when available */\n ppid?: number;\n\n /** Process command */\n command: string;\n\n /** Working directory */\n cwd: string;\n\n /** Terminal TTY (e.g., \"ttys030\") */\n tty: string;\n\n /** Process start time, populated by process enrichment */\n startTime?: Date;\n}\n\n/**\n * A single message in a conversation\n */\nexport interface ConversationMessage {\n role: \"user\" | \"assistant\" | \"system\";\n content: string;\n timestamp?: string;\n}\n\n/**\n * A historical session discovered on disk (running or not).\n *\n * Used by `listSessions` to surface enough context for a user to identify\n * a session and resume it via the originating tool's resume command.\n */\nexport interface SessionSummary {\n /** Tool that produced this session */\n type: AgentType;\n\n /**\n * ID accepted by the tool's resume command. Adapters MUST pass this\n * through verbatim — no normalization, no encoding/decoding — so it\n * round-trips into `claude --resume <id>` (and equivalents).\n */\n sessionId: string;\n\n /** Working directory the session was started in (best-known value) */\n cwd: string;\n\n /**\n * Trimmed first user message; empty string if none. Adapters apply\n * the same noise-filter their existing parsers use (skip tool_result\n * blocks, request-interruption notices, system-injected skill\n * content). The CLI table renderer substitutes a placeholder for\n * empty values; JSON output keeps the empty string raw.\n */\n firstUserMessage: string;\n\n /** Last activity timestamp (from session content; falls back to file mtime) */\n lastActive: Date;\n\n /** Session start time (from session content; falls back to file birthtime/mtime) */\n startedAt: Date;\n\n /** Absolute path to the session file on disk (debug/diagnostics) */\n sessionFilePath: string;\n}\n\n/**\n * Filters passed by the CLI to {@link AgentAdapter.listSessions}.\n *\n * The CLI is the source of truth for filter defaults and semantics\n * (e.g. cwd defaults to process.cwd(); --all clears it). Adapters apply\n * the values they receive — they don't invent defaults.\n */\nexport interface ListSessionsOptions {\n /**\n * Filter to sessions whose recorded cwd matches this path using strict\n * equality (no prefix/ancestor matching in v1). Undefined = no cwd\n * filter.\n */\n cwd?: string;\n\n /**\n * Filter to a single tool. Enforced by `AgentManager.listSessions`,\n * which skips adapters whose `type` doesn't match. Adapters MAY\n * ignore this field — by the time their `listSessions` runs, the\n * type filter is already satisfied. Undefined = include every\n * registered adapter.\n */\n type?: AgentType;\n}\n\nexport interface AgentDetectionContext {\n /** One enriched process snapshot shared across this manager refresh. */\n readonly processes: readonly ProcessInfo[];\n}\n\n/**\n * Agent Adapter Interface\n *\n * Implementations must provide detection logic for a specific agent type.\n */\nexport interface AgentAdapter {\n /** Type of agent this adapter handles */\n readonly type: AgentType;\n\n /** Executable basenames required for shared process discovery. */\n readonly processNames?: readonly string[];\n\n /**\n * Detect running agents of this type\n * @returns List of detected agents\n */\n detectAgents(context?: AgentDetectionContext): Promise<AgentInfo[]>;\n\n /**\n * Check if this adapter can handle the given process\n * @param processInfo Process information\n * @returns True if this adapter can handle the process\n */\n canHandle(processInfo: ProcessInfo): boolean;\n\n /**\n * Read the full conversation from a session file\n * @param sessionFilePath Path to the session JSONL file\n * @param options.verbose Include tool call/result details\n * @returns Array of conversation messages\n */\n getConversation(sessionFilePath: string, options?: { verbose?: boolean }): ConversationMessage[];\n\n /**\n * Enumerate historical sessions for this tool from disk.\n *\n * Applies `opts.cwd` as a strict-equality filter when set. Returns\n * {@link SessionSummary} entries unsorted; sorting and global filters\n * are handled by `AgentManager` and the CLI.\n *\n * @param opts Filter options computed by the CLI\n * @returns Array of sessions discovered on disk\n */\n listSessions(opts?: ListSessionsOptions): Promise<SessionSummary[]>;\n\n /**\n * Resolve exact historical session matches without enumerating every summary.\n *\n * Built-in adapters implement this using their provider-native storage shape.\n * The method remains optional so external adapters can fall back to\n * {@link listSessions} until they adopt direct lookup.\n */\n findSessionsById?(sessionId: string): Promise<SessionSummary[]>;\n}\n"],"names":["AgentStatus"],"mappings":"AAAA;;;;;CAKC,GAED;;CAEC,GAWD;;CAEC,GACD,OAAO,IAAA,AAAKA,qCAAAA;;;;;WAAAA;MAKX"}
@@ -2,8 +2,8 @@ export { ClaudeCodeAdapter } from "../providers/claude/ClaudeCodeAdapter.js";
2
2
  export { CodexAdapter } from "../providers/codex/CodexAdapter.js";
3
3
  export { CopilotAdapter } from "../providers/copilot/CopilotAdapter.js";
4
4
  export { GeminiCliAdapter } from "../providers/gemini/GeminiCliAdapter.js";
5
- export { GrokCliAdapter } from "./GrokCliAdapter.js";
6
- export { OpenCodeAdapter } from "./OpenCodeAdapter.js";
5
+ export { GrokCliAdapter } from "../providers/grok/GrokCliAdapter.js";
6
+ export { OpenCodeAdapter } from "../providers/opencode/OpenCodeAdapter.js";
7
7
  export { PiAdapter } from "../providers/pi/PiAdapter.js";
8
8
  export { AgentStatus } from "./AgentAdapter.js";
9
9
  export type { AgentAdapter, AgentType, AgentInfo, ProcessInfo, AgentDetectionContext, } from "./AgentAdapter.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,0CAA0C,CAAC;AAC7E,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AACxE,OAAO,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAC3E,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EACV,YAAY,EACZ,SAAS,EACT,SAAS,EACT,WAAW,EACX,qBAAqB,GACtB,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,0CAA0C,CAAC;AAC7E,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AACxE,OAAO,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAC3E,OAAO,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,0CAA0C,CAAC;AAC3E,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EACV,YAAY,EACZ,SAAS,EACT,SAAS,EACT,WAAW,EACX,qBAAqB,GACtB,MAAM,mBAAmB,CAAC"}
@@ -2,8 +2,8 @@ export { ClaudeCodeAdapter } from "../providers/claude/ClaudeCodeAdapter.js";
2
2
  export { CodexAdapter } from "../providers/codex/CodexAdapter.js";
3
3
  export { CopilotAdapter } from "../providers/copilot/CopilotAdapter.js";
4
4
  export { GeminiCliAdapter } from "../providers/gemini/GeminiCliAdapter.js";
5
- export { GrokCliAdapter } from "./GrokCliAdapter.js";
6
- export { OpenCodeAdapter } from "./OpenCodeAdapter.js";
5
+ export { GrokCliAdapter } from "../providers/grok/GrokCliAdapter.js";
6
+ export { OpenCodeAdapter } from "../providers/opencode/OpenCodeAdapter.js";
7
7
  export { PiAdapter } from "../providers/pi/PiAdapter.js";
8
8
  export { AgentStatus } from "./AgentAdapter.js";
9
9
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/adapters/index.ts"],"sourcesContent":["export { ClaudeCodeAdapter } from \"../providers/claude/ClaudeCodeAdapter.js\";\nexport { CodexAdapter } from \"../providers/codex/CodexAdapter.js\";\nexport { CopilotAdapter } from \"../providers/copilot/CopilotAdapter.js\";\nexport { GeminiCliAdapter } from \"../providers/gemini/GeminiCliAdapter.js\";\nexport { GrokCliAdapter } from \"./GrokCliAdapter.js\";\nexport { OpenCodeAdapter } from \"./OpenCodeAdapter.js\";\nexport { PiAdapter } from \"../providers/pi/PiAdapter.js\";\nexport { AgentStatus } from \"./AgentAdapter.js\";\nexport type {\n AgentAdapter,\n AgentType,\n AgentInfo,\n ProcessInfo,\n AgentDetectionContext,\n} from \"./AgentAdapter.js\";\n"],"names":["ClaudeCodeAdapter","CodexAdapter","CopilotAdapter","GeminiCliAdapter","GrokCliAdapter","OpenCodeAdapter","PiAdapter","AgentStatus"],"mappings":"AAAA,SAASA,iBAAiB,QAAQ,2CAA2C;AAC7E,SAASC,YAAY,QAAQ,qCAAqC;AAClE,SAASC,cAAc,QAAQ,yCAAyC;AACxE,SAASC,gBAAgB,QAAQ,0CAA0C;AAC3E,SAASC,cAAc,QAAQ,sBAAsB;AACrD,SAASC,eAAe,QAAQ,uBAAuB;AACvD,SAASC,SAAS,QAAQ,+BAA+B;AACzD,SAASC,WAAW,QAAQ,oBAAoB"}
1
+ {"version":3,"sources":["../../src/adapters/index.ts"],"sourcesContent":["export { ClaudeCodeAdapter } from \"../providers/claude/ClaudeCodeAdapter.js\";\nexport { CodexAdapter } from \"../providers/codex/CodexAdapter.js\";\nexport { CopilotAdapter } from \"../providers/copilot/CopilotAdapter.js\";\nexport { GeminiCliAdapter } from \"../providers/gemini/GeminiCliAdapter.js\";\nexport { GrokCliAdapter } from \"../providers/grok/GrokCliAdapter.js\";\nexport { OpenCodeAdapter } from \"../providers/opencode/OpenCodeAdapter.js\";\nexport { PiAdapter } from \"../providers/pi/PiAdapter.js\";\nexport { AgentStatus } from \"./AgentAdapter.js\";\nexport type {\n AgentAdapter,\n AgentType,\n AgentInfo,\n ProcessInfo,\n AgentDetectionContext,\n} from \"./AgentAdapter.js\";\n"],"names":["ClaudeCodeAdapter","CodexAdapter","CopilotAdapter","GeminiCliAdapter","GrokCliAdapter","OpenCodeAdapter","PiAdapter","AgentStatus"],"mappings":"AAAA,SAASA,iBAAiB,QAAQ,2CAA2C;AAC7E,SAASC,YAAY,QAAQ,qCAAqC;AAClE,SAASC,cAAc,QAAQ,yCAAyC;AACxE,SAASC,gBAAgB,QAAQ,0CAA0C;AAC3E,SAASC,cAAc,QAAQ,sCAAsC;AACrE,SAASC,eAAe,QAAQ,2CAA2C;AAC3E,SAASC,SAAS,QAAQ,+BAA+B;AACzD,SAASC,WAAW,QAAQ,oBAAoB"}
@@ -31,6 +31,7 @@ export declare function resolveCodexAuthPath(env?: NodeJS.ProcessEnv): string;
31
31
  export declare function toRateWindow(value: unknown, id: string, label: string): CapacityWindow | null;
32
32
  export declare function parseUsage(raw: unknown, source: "pat" | "oauth"): UsageSnapshot;
33
33
  export declare function parseCliUsage(raw: unknown): UsageSnapshot;
34
+ export declare function codexUnavailableReport(checkedAt: string): CapacityReport;
34
35
  export declare function probeCodexCapacity(options: CodexProbeOptions): Promise<CapacityReport>;
35
36
  export {};
36
37
  //# sourceMappingURL=codex.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"codex.d.ts","sourceRoot":"","sources":["../../src/capacity/codex.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjE,KAAK,gBAAgB,GAAG,KAAK,GAAG,OAAO,GAAG,KAAK,CAAC;AAChD,KAAK,aAAa,GAAG;IACnB,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,MAAM,EAAE,gBAAgB,CAAC;CAC1B,CAAC;AAEF,KAAK,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAC7C,KAAK,UAAU,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,aAAa,CAAA;CAAE,CAAC;AAC1E,KAAK,YAAY,GAAG;IAAE,UAAU,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAC9D,KAAK,QAAQ,GAAG,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;AAElE,eAAO,MAAM,qBAAqB,+DAAgE,CAAC;AAEnG,KAAK,iBAAiB,GAAG;IACvB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACvE,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC,GAAG,CAAC,EAAE,QAAQ,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB,CAAC;AA+BF,wBAAgB,oBAAoB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAGjF;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAY7F;AAgBD,wBAAgB,UAAU,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,GAAG,OAAO,GAAG,aAAa,CAa/E;AAwBD,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,aAAa,CAWzD;AAoLD,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC,CAoD5F"}
1
+ {"version":3,"file":"codex.d.ts","sourceRoot":"","sources":["../../src/capacity/codex.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjE,KAAK,gBAAgB,GAAG,KAAK,GAAG,OAAO,GAAG,KAAK,CAAC;AAChD,KAAK,aAAa,GAAG;IACnB,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,MAAM,EAAE,gBAAgB,CAAC;CAC1B,CAAC;AAEF,KAAK,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAC7C,KAAK,UAAU,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,aAAa,CAAA;CAAE,CAAC;AAC1E,KAAK,YAAY,GAAG;IAAE,UAAU,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAC9D,KAAK,QAAQ,GAAG,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;AAElE,eAAO,MAAM,qBAAqB,+DAMxB,CAAC;AAEX,KAAK,iBAAiB,GAAG;IACvB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACvE,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC,GAAG,CAAC,EAAE,QAAQ,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB,CAAC;AA+BF,wBAAgB,oBAAoB,CAClC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,CAGR;AAED,wBAAgB,YAAY,CAC1B,KAAK,EAAE,OAAO,EACd,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,GACZ,cAAc,GAAG,IAAI,CAYvB;AAwBD,wBAAgB,UAAU,CACxB,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,KAAK,GAAG,OAAO,GACtB,aAAa,CAaf;AAgCD,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,aAAa,CAazD;AAyJD,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,CAUxE;AA+CD,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,cAAc,CAAC,CAuDzB"}
@@ -119,7 +119,8 @@ function capacityFromSnapshot(snapshot, context, raw) {
119
119
  const reached = nonEmptyText(rateLimits?.rateLimitReachedType);
120
120
  const resetCredits = record(record(raw)?.rateLimitResetCredits) ?? record(record(raw)?.usageLimitResetCredits);
121
121
  return {
122
- provider: "codex",
122
+ harness: "codex",
123
+ provider: "openai",
123
124
  generatedAt: context.checkedAt,
124
125
  authenticated: true,
125
126
  available: reached ? "no" : hasUsage ? "yes" : "unknown",
@@ -225,16 +226,20 @@ function appServerRpc(messages, timeoutMs = 5000) {
225
226
  child.stdin.write(`${JSON.stringify(messages[0])}\n`);
226
227
  });
227
228
  }
228
- function unavailable(options) {
229
+ export function codexUnavailableReport(checkedAt) {
229
230
  return {
230
- provider: "codex",
231
- generatedAt: options.checkedAt,
231
+ harness: "codex",
232
+ provider: "openai",
233
+ generatedAt: checkedAt,
232
234
  authenticated: null,
233
235
  available: "unknown",
234
236
  windows: [],
235
237
  creditsRemaining: null
236
238
  };
237
239
  }
240
+ function unavailable(options) {
241
+ return codexUnavailableReport(options.checkedAt);
242
+ }
238
243
  async function cliFallback(options) {
239
244
  if (!options.installed) return unavailable(options);
240
245
  const messages = [