@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 @@
1
+ {"version":3,"file":"CopilotSessionParser.d.ts","sourceRoot":"","sources":["../../../src/providers/copilot/CopilotSessionParser.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAkC7D,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,IAAI,CAAC;IACnB,UAAU,EAAE,IAAI,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;CACxB;AA6BD,qBAAa,oBAAoB;IAC/B,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI;IAqCpF,eAAe,CAAC,eAAe,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,mBAAmB,EAAE;IAwBhG,eAAe,CAAC,OAAO,EAAE,cAAc,GAAG,WAAW;IAcrD,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,uBAAuB;IAqC/B,OAAO,CAAC,oBAAoB;IAM5B,OAAO,CAAC,qBAAqB;IA0B7B,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,eAAe;IAevB,OAAO,CAAC,YAAY;IAYpB,OAAO,CAAC,gBAAgB;IAiBxB,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,QAAQ;CAIjB"}
@@ -0,0 +1,181 @@
1
+ import * as path from "path";
2
+ import { AgentStatus } from "../../adapters/AgentAdapter.js";
3
+ import { safeReadFile, safeStat } from "../../utils/session.js";
4
+ const IDLE_THRESHOLD_MINUTES = 5;
5
+ const VERBOSE_SYSTEM_EVENTS = new Set([
6
+ "system.message",
7
+ "session.info",
8
+ "session.warning",
9
+ "tool.execution_start",
10
+ "tool.execution_complete",
11
+ "function",
12
+ "abort"
13
+ ]);
14
+ const WAITING_EVENTS = new Set([
15
+ "assistant.message",
16
+ "assistant.turn_end",
17
+ "session.shutdown",
18
+ "abort"
19
+ ]);
20
+ export class CopilotSessionParser {
21
+ readSessionDir(sessionDir, fallbackSessionId) {
22
+ const eventsFilePath = path.join(sessionDir, "events.jsonl");
23
+ const workspace = this.readWorkspaceMetadata(path.join(sessionDir, "workspace.yaml"));
24
+ const entries = this.readEventEntries(eventsFilePath);
25
+ if (entries.length === 0 && !this.hasWorkspaceMetadata(workspace)) {
26
+ return null;
27
+ }
28
+ const fileStat = safeStat(eventsFilePath);
29
+ const sessionStart = workspace.createdAt || fileStat?.birthtime || new Date();
30
+ const eventSummary = this.buildSessionFromEntries(entries, {
31
+ sessionId: workspace.id || fallbackSessionId,
32
+ projectPath: workspace.cwd || "",
33
+ sessionStart,
34
+ lastActive: workspace.updatedAt || fileStat?.mtime || sessionStart,
35
+ firstUserMessage: "",
36
+ lastText: ""
37
+ });
38
+ const summary = eventSummary.firstUserMessage || eventSummary.lastText || workspace.name || "Copilot session active";
39
+ return {
40
+ sessionId: eventSummary.sessionId,
41
+ projectPath: eventSummary.projectPath,
42
+ summary: this.truncate(summary, 120),
43
+ sessionStart: eventSummary.sessionStart,
44
+ lastActive: eventSummary.lastActive,
45
+ lastEventType: eventSummary.lastEventType,
46
+ firstUserMessage: eventSummary.firstUserMessage,
47
+ eventsFilePath
48
+ };
49
+ }
50
+ getConversation(sessionFilePath, options) {
51
+ const verbose = options?.verbose ?? false;
52
+ const content = safeReadFile(sessionFilePath);
53
+ if (content === undefined) return [];
54
+ const messages = [];
55
+ for (const entry of this.parseEventLines(content)){
56
+ const role = this.roleForEvent(entry.type, verbose);
57
+ if (!role) continue;
58
+ const text = this.extractEventText(entry, verbose);
59
+ if (!text) continue;
60
+ messages.push({
61
+ role,
62
+ content: text,
63
+ timestamp: entry.timestamp
64
+ });
65
+ }
66
+ return messages;
67
+ }
68
+ determineStatus(session) {
69
+ const diffMs = Date.now() - session.lastActive.getTime();
70
+ const diffMinutes = diffMs / 60000;
71
+ if (diffMinutes > IDLE_THRESHOLD_MINUTES) {
72
+ return AgentStatus.IDLE;
73
+ }
74
+ if (session.lastEventType !== undefined && WAITING_EVENTS.has(session.lastEventType)) {
75
+ return AgentStatus.WAITING;
76
+ }
77
+ return AgentStatus.RUNNING;
78
+ }
79
+ readEventEntries(eventsFilePath) {
80
+ const content = safeReadFile(eventsFilePath);
81
+ return content === undefined ? [] : this.parseEventLines(content);
82
+ }
83
+ buildSessionFromEntries(entries, initial) {
84
+ const summary = {
85
+ ...initial
86
+ };
87
+ for (const entry of entries){
88
+ const timestamp = this.parseTimestamp(entry.timestamp);
89
+ if (timestamp) {
90
+ summary.lastActive = timestamp;
91
+ }
92
+ if (entry.type) {
93
+ summary.lastEventType = entry.type;
94
+ }
95
+ if (entry.type === "session.start") {
96
+ summary.sessionId = entry.data?.sessionId || summary.sessionId;
97
+ summary.projectPath = entry.data?.context?.cwd || summary.projectPath;
98
+ summary.sessionStart = this.parseTimestamp(entry.data?.startTime) || timestamp || summary.sessionStart;
99
+ continue;
100
+ }
101
+ const text = this.extractEventText(entry, false);
102
+ if (!text) continue;
103
+ if (!summary.firstUserMessage && entry.type === "user.message") {
104
+ summary.firstUserMessage = text;
105
+ }
106
+ if (entry.type === "user.message" || entry.type === "assistant.message") {
107
+ summary.lastText = text;
108
+ }
109
+ }
110
+ return summary;
111
+ }
112
+ hasWorkspaceMetadata(workspace) {
113
+ return Boolean(workspace.id || workspace.cwd || workspace.name || workspace.createdAt || workspace.updatedAt);
114
+ }
115
+ readWorkspaceMetadata(filePath) {
116
+ const content = safeReadFile(filePath);
117
+ if (content === undefined) return {};
118
+ const values = new Map();
119
+ for (const rawLine of content.split("\n")){
120
+ const line = rawLine.trim();
121
+ if (!line || line.startsWith("#")) continue;
122
+ const idx = line.indexOf(":");
123
+ if (idx === -1) continue;
124
+ const key = line.slice(0, idx).trim();
125
+ const value = this.stripYamlScalar(line.slice(idx + 1).trim());
126
+ values.set(key, value);
127
+ }
128
+ return {
129
+ id: values.get("id"),
130
+ cwd: values.get("cwd"),
131
+ name: values.get("name"),
132
+ createdAt: this.parseTimestamp(values.get("created_at")) || undefined,
133
+ updatedAt: this.parseTimestamp(values.get("updated_at")) || undefined
134
+ };
135
+ }
136
+ stripYamlScalar(value) {
137
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
138
+ return value.slice(1, -1);
139
+ }
140
+ return value;
141
+ }
142
+ parseEventLines(content) {
143
+ const entries = [];
144
+ for (const line of content.trim().split("\n")){
145
+ const trimmed = line.trim();
146
+ if (!trimmed) continue;
147
+ try {
148
+ entries.push(JSON.parse(trimmed));
149
+ } catch {
150
+ continue;
151
+ }
152
+ }
153
+ return entries;
154
+ }
155
+ roleForEvent(type, verbose) {
156
+ if (type === "user.message") return "user";
157
+ if (type === "assistant.message") return "assistant";
158
+ if (verbose && type !== undefined && VERBOSE_SYSTEM_EVENTS.has(type)) {
159
+ return "system";
160
+ }
161
+ return null;
162
+ }
163
+ extractEventText(entry, verbose) {
164
+ const data = entry.data;
165
+ if (!data) return "";
166
+ const raw = data.content || data.message || data.text || data.result?.content || data.result?.detailedContent || (verbose ? data.output : "") || "";
167
+ if (typeof raw !== "string") return "";
168
+ return raw.trim();
169
+ }
170
+ parseTimestamp(value) {
171
+ if (!value) return null;
172
+ const timestamp = new Date(value);
173
+ return Number.isNaN(timestamp.getTime()) ? null : timestamp;
174
+ }
175
+ truncate(value, maxLength) {
176
+ if (value.length <= maxLength) return value;
177
+ return `${value.slice(0, maxLength - 3)}...`;
178
+ }
179
+ }
180
+
181
+ //# sourceMappingURL=CopilotSessionParser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/providers/copilot/CopilotSessionParser.ts"],"sourcesContent":["import * as path from \"path\";\nimport type { ConversationMessage } from \"../../adapters/AgentAdapter.js\";\nimport { AgentStatus } from \"../../adapters/AgentAdapter.js\";\nimport { safeReadFile, safeStat } from \"../../utils/session.js\";\n\ninterface CopilotEventEntry {\n type?: string;\n data?: {\n sessionId?: string;\n startTime?: string;\n context?: {\n cwd?: string;\n gitRoot?: string;\n branch?: string;\n };\n content?: string;\n transformedContent?: string;\n message?: string;\n text?: string;\n result?: {\n content?: string;\n detailedContent?: string;\n };\n output?: string;\n };\n timestamp?: string;\n}\n\ninterface CopilotWorkspace {\n id?: string;\n cwd?: string;\n name?: string;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\nexport interface CopilotSession {\n sessionId: string;\n projectPath: string;\n summary: string;\n sessionStart: Date;\n lastActive: Date;\n lastEventType?: string;\n firstUserMessage: string;\n eventsFilePath: string;\n}\n\ninterface CopilotEventSummary {\n sessionId: string;\n projectPath: string;\n sessionStart: Date;\n lastActive: Date;\n firstUserMessage: string;\n lastText: string;\n lastEventType?: string;\n}\n\nconst IDLE_THRESHOLD_MINUTES = 5;\nconst VERBOSE_SYSTEM_EVENTS = new Set([\n \"system.message\",\n \"session.info\",\n \"session.warning\",\n \"tool.execution_start\",\n \"tool.execution_complete\",\n \"function\",\n \"abort\",\n]);\nconst WAITING_EVENTS = new Set([\n \"assistant.message\",\n \"assistant.turn_end\",\n \"session.shutdown\",\n \"abort\",\n]);\n\nexport class CopilotSessionParser {\n readSessionDir(sessionDir: string, fallbackSessionId: string): CopilotSession | null {\n const eventsFilePath = path.join(sessionDir, \"events.jsonl\");\n const workspace = this.readWorkspaceMetadata(path.join(sessionDir, \"workspace.yaml\"));\n const entries = this.readEventEntries(eventsFilePath);\n if (entries.length === 0 && !this.hasWorkspaceMetadata(workspace)) {\n return null;\n }\n\n const fileStat = safeStat(eventsFilePath);\n const sessionStart = workspace.createdAt || fileStat?.birthtime || new Date();\n const eventSummary = this.buildSessionFromEntries(entries, {\n sessionId: workspace.id || fallbackSessionId,\n projectPath: workspace.cwd || \"\",\n sessionStart,\n lastActive: workspace.updatedAt || fileStat?.mtime || sessionStart,\n firstUserMessage: \"\",\n lastText: \"\",\n });\n\n const summary =\n eventSummary.firstUserMessage ||\n eventSummary.lastText ||\n workspace.name ||\n \"Copilot session active\";\n\n return {\n sessionId: eventSummary.sessionId,\n projectPath: eventSummary.projectPath,\n summary: this.truncate(summary, 120),\n sessionStart: eventSummary.sessionStart,\n lastActive: eventSummary.lastActive,\n lastEventType: eventSummary.lastEventType,\n firstUserMessage: eventSummary.firstUserMessage,\n eventsFilePath,\n };\n }\n\n getConversation(sessionFilePath: string, options?: { verbose?: boolean }): ConversationMessage[] {\n const verbose = options?.verbose ?? false;\n const content = safeReadFile(sessionFilePath);\n if (content === undefined) return [];\n\n const messages: ConversationMessage[] = [];\n\n for (const entry of this.parseEventLines(content)) {\n const role = this.roleForEvent(entry.type, verbose);\n if (!role) continue;\n\n const text = this.extractEventText(entry, verbose);\n if (!text) continue;\n\n messages.push({\n role,\n content: text,\n timestamp: entry.timestamp,\n });\n }\n\n return messages;\n }\n\n determineStatus(session: CopilotSession): AgentStatus {\n const diffMs = Date.now() - session.lastActive.getTime();\n const diffMinutes = diffMs / 60000;\n if (diffMinutes > IDLE_THRESHOLD_MINUTES) {\n return AgentStatus.IDLE;\n }\n\n if (session.lastEventType !== undefined && WAITING_EVENTS.has(session.lastEventType)) {\n return AgentStatus.WAITING;\n }\n\n return AgentStatus.RUNNING;\n }\n\n private readEventEntries(eventsFilePath: string): CopilotEventEntry[] {\n const content = safeReadFile(eventsFilePath);\n return content === undefined ? [] : this.parseEventLines(content);\n }\n\n private buildSessionFromEntries(\n entries: CopilotEventEntry[],\n initial: CopilotEventSummary,\n ): CopilotEventSummary {\n const summary = { ...initial };\n for (const entry of entries) {\n const timestamp = this.parseTimestamp(entry.timestamp);\n if (timestamp) {\n summary.lastActive = timestamp;\n }\n if (entry.type) {\n summary.lastEventType = entry.type;\n }\n\n if (entry.type === \"session.start\") {\n summary.sessionId = entry.data?.sessionId || summary.sessionId;\n summary.projectPath = entry.data?.context?.cwd || summary.projectPath;\n summary.sessionStart =\n this.parseTimestamp(entry.data?.startTime) || timestamp || summary.sessionStart;\n continue;\n }\n\n const text = this.extractEventText(entry, false);\n if (!text) continue;\n\n if (!summary.firstUserMessage && entry.type === \"user.message\") {\n summary.firstUserMessage = text;\n }\n\n if (entry.type === \"user.message\" || entry.type === \"assistant.message\") {\n summary.lastText = text;\n }\n }\n\n return summary;\n }\n\n private hasWorkspaceMetadata(workspace: CopilotWorkspace): boolean {\n return Boolean(\n workspace.id || workspace.cwd || workspace.name || workspace.createdAt || workspace.updatedAt,\n );\n }\n\n private readWorkspaceMetadata(filePath: string): CopilotWorkspace {\n const content = safeReadFile(filePath);\n if (content === undefined) return {};\n\n const values = new Map<string, string>();\n for (const rawLine of content.split(\"\\n\")) {\n const line = rawLine.trim();\n if (!line || line.startsWith(\"#\")) continue;\n\n const idx = line.indexOf(\":\");\n if (idx === -1) continue;\n\n const key = line.slice(0, idx).trim();\n const value = this.stripYamlScalar(line.slice(idx + 1).trim());\n values.set(key, value);\n }\n\n return {\n id: values.get(\"id\"),\n cwd: values.get(\"cwd\"),\n name: values.get(\"name\"),\n createdAt: this.parseTimestamp(values.get(\"created_at\")) || undefined,\n updatedAt: this.parseTimestamp(values.get(\"updated_at\")) || undefined,\n };\n }\n\n private stripYamlScalar(value: string): string {\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n return value.slice(1, -1);\n }\n return value;\n }\n\n private parseEventLines(content: string): CopilotEventEntry[] {\n const entries: CopilotEventEntry[] = [];\n for (const line of content.trim().split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n\n try {\n entries.push(JSON.parse(trimmed) as CopilotEventEntry);\n } catch {\n continue;\n }\n }\n return entries;\n }\n\n private roleForEvent(\n type: string | undefined,\n verbose: boolean,\n ): ConversationMessage[\"role\"] | null {\n if (type === \"user.message\") return \"user\";\n if (type === \"assistant.message\") return \"assistant\";\n if (verbose && type !== undefined && VERBOSE_SYSTEM_EVENTS.has(type)) {\n return \"system\";\n }\n return null;\n }\n\n private extractEventText(entry: CopilotEventEntry, verbose: boolean): string {\n const data = entry.data;\n if (!data) return \"\";\n\n const raw =\n data.content ||\n data.message ||\n data.text ||\n data.result?.content ||\n data.result?.detailedContent ||\n (verbose ? data.output : \"\") ||\n \"\";\n\n if (typeof raw !== \"string\") return \"\";\n return raw.trim();\n }\n\n private parseTimestamp(value?: string): Date | null {\n if (!value) return null;\n const timestamp = new Date(value);\n return Number.isNaN(timestamp.getTime()) ? null : timestamp;\n }\n\n private truncate(value: string, maxLength: number): string {\n if (value.length <= maxLength) return value;\n return `${value.slice(0, maxLength - 3)}...`;\n }\n}\n"],"names":["path","AgentStatus","safeReadFile","safeStat","IDLE_THRESHOLD_MINUTES","VERBOSE_SYSTEM_EVENTS","Set","WAITING_EVENTS","CopilotSessionParser","readSessionDir","sessionDir","fallbackSessionId","eventsFilePath","join","workspace","readWorkspaceMetadata","entries","readEventEntries","length","hasWorkspaceMetadata","fileStat","sessionStart","createdAt","birthtime","Date","eventSummary","buildSessionFromEntries","sessionId","id","projectPath","cwd","lastActive","updatedAt","mtime","firstUserMessage","lastText","summary","name","truncate","lastEventType","getConversation","sessionFilePath","options","verbose","content","undefined","messages","entry","parseEventLines","role","roleForEvent","type","text","extractEventText","push","timestamp","determineStatus","session","diffMs","now","getTime","diffMinutes","IDLE","has","WAITING","RUNNING","initial","parseTimestamp","data","context","startTime","Boolean","filePath","values","Map","rawLine","split","line","trim","startsWith","idx","indexOf","key","slice","value","stripYamlScalar","set","get","endsWith","trimmed","JSON","parse","raw","message","result","detailedContent","output","Number","isNaN","maxLength"],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { AgentInfo, ProcessInfo } from "../../adapters/AgentAdapter.js";
2
+ import { GeminiSessionParser, type GeminiSession } from "./GeminiSessionParser.js";
3
+ export declare class GeminiAgentMapper {
4
+ private readonly parser;
5
+ constructor(parser?: GeminiSessionParser);
6
+ mapSessionToAgent(session: GeminiSession, processInfo: ProcessInfo, filePath: string): AgentInfo;
7
+ mapProcessOnlyAgent(processInfo: ProcessInfo): AgentInfo;
8
+ }
9
+ //# sourceMappingURL=GeminiAgentMapper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GeminiAgentMapper.d.ts","sourceRoot":"","sources":["../../../src/providers/gemini/GeminiAgentMapper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAG7E,OAAO,EAAE,mBAAmB,EAAE,KAAK,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEnF,qBAAa,iBAAiB;IAChB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,GAAE,mBAA+C;IAEpF,iBAAiB,CAAC,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS;IAehG,mBAAmB,CAAC,WAAW,EAAE,WAAW,GAAG,SAAS;CAYzD"}
@@ -0,0 +1,37 @@
1
+ import { AgentStatus } from "../../adapters/AgentAdapter.js";
2
+ import { generateAgentName } from "../../utils/matching.js";
3
+ import { GeminiSessionParser } from "./GeminiSessionParser.js";
4
+ export class GeminiAgentMapper {
5
+ parser;
6
+ constructor(parser = new GeminiSessionParser()){
7
+ this.parser = parser;
8
+ }
9
+ mapSessionToAgent(session, processInfo, filePath) {
10
+ const projectPath = session.projectPath || processInfo.cwd || "";
11
+ return {
12
+ name: generateAgentName(projectPath, processInfo.pid),
13
+ type: "gemini_cli",
14
+ status: this.parser.determineStatus(session),
15
+ summary: session.summary || "Gemini CLI session active",
16
+ pid: processInfo.pid,
17
+ projectPath,
18
+ sessionId: session.sessionId,
19
+ lastActive: session.lastActive,
20
+ sessionFilePath: filePath
21
+ };
22
+ }
23
+ mapProcessOnlyAgent(processInfo) {
24
+ return {
25
+ name: generateAgentName(processInfo.cwd || "", processInfo.pid),
26
+ type: "gemini_cli",
27
+ status: AgentStatus.RUNNING,
28
+ summary: "Gemini CLI process running",
29
+ pid: processInfo.pid,
30
+ projectPath: processInfo.cwd || "",
31
+ sessionId: `pid-${processInfo.pid}`,
32
+ lastActive: new Date()
33
+ };
34
+ }
35
+ }
36
+
37
+ //# sourceMappingURL=GeminiAgentMapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/providers/gemini/GeminiAgentMapper.ts"],"sourcesContent":["import type { AgentInfo, ProcessInfo } from \"../../adapters/AgentAdapter.js\";\nimport { AgentStatus } from \"../../adapters/AgentAdapter.js\";\nimport { generateAgentName } from \"../../utils/matching.js\";\nimport { GeminiSessionParser, type GeminiSession } from \"./GeminiSessionParser.js\";\n\nexport class GeminiAgentMapper {\n constructor(private readonly parser: GeminiSessionParser = new GeminiSessionParser()) {}\n\n mapSessionToAgent(session: GeminiSession, processInfo: ProcessInfo, filePath: string): AgentInfo {\n const projectPath = session.projectPath || processInfo.cwd || \"\";\n return {\n name: generateAgentName(projectPath, processInfo.pid),\n type: \"gemini_cli\",\n status: this.parser.determineStatus(session),\n summary: session.summary || \"Gemini CLI session active\",\n pid: processInfo.pid,\n projectPath,\n sessionId: session.sessionId,\n lastActive: session.lastActive,\n sessionFilePath: filePath,\n };\n }\n\n mapProcessOnlyAgent(processInfo: ProcessInfo): AgentInfo {\n return {\n name: generateAgentName(processInfo.cwd || \"\", processInfo.pid),\n type: \"gemini_cli\",\n status: AgentStatus.RUNNING,\n summary: \"Gemini CLI process running\",\n pid: processInfo.pid,\n projectPath: processInfo.cwd || \"\",\n sessionId: `pid-${processInfo.pid}`,\n lastActive: new Date(),\n };\n }\n}\n"],"names":["AgentStatus","generateAgentName","GeminiSessionParser","GeminiAgentMapper","parser","mapSessionToAgent","session","processInfo","filePath","projectPath","cwd","name","pid","type","status","determineStatus","summary","sessionId","lastActive","sessionFilePath","mapProcessOnlyAgent","RUNNING","Date"],"mappings":"AACA,SAASA,WAAW,QAAQ,iCAAiC;AAC7D,SAASC,iBAAiB,QAAQ,0BAA0B;AAC5D,SAASC,mBAAmB,QAA4B,2BAA2B;AAEnF,OAAO,MAAMC;;IACX,YAAY,AAAiBC,SAA8B,IAAIF,qBAAqB,CAAE;aAAzDE,SAAAA;IAA0D;IAEvFC,kBAAkBC,OAAsB,EAAEC,WAAwB,EAAEC,QAAgB,EAAa;QAC/F,MAAMC,cAAcH,QAAQG,WAAW,IAAIF,YAAYG,GAAG,IAAI;QAC9D,OAAO;YACLC,MAAMV,kBAAkBQ,aAAaF,YAAYK,GAAG;YACpDC,MAAM;YACNC,QAAQ,IAAI,CAACV,MAAM,CAACW,eAAe,CAACT;YACpCU,SAASV,QAAQU,OAAO,IAAI;YAC5BJ,KAAKL,YAAYK,GAAG;YACpBH;YACAQ,WAAWX,QAAQW,SAAS;YAC5BC,YAAYZ,QAAQY,UAAU;YAC9BC,iBAAiBX;QACnB;IACF;IAEAY,oBAAoBb,WAAwB,EAAa;QACvD,OAAO;YACLI,MAAMV,kBAAkBM,YAAYG,GAAG,IAAI,IAAIH,YAAYK,GAAG;YAC9DC,MAAM;YACNC,QAAQd,YAAYqB,OAAO;YAC3BL,SAAS;YACTJ,KAAKL,YAAYK,GAAG;YACpBH,aAAaF,YAAYG,GAAG,IAAI;YAChCO,WAAW,CAAC,IAAI,EAAEV,YAAYK,GAAG,EAAE;YACnCM,YAAY,IAAII;QAClB;IACF;AACF"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Gemini CLI Adapter
3
+ *
4
+ * Detects running Gemini CLI agents by:
5
+ * 1. Filtering Gemini Node processes from a shared asynchronous process snapshot
6
+ * 2. Using snapshot CWD and start-time enrichment
7
+ * 3. Discovering session files from ~/.gemini/tmp/<shortId>/chats/session-*.json
8
+ * 4. Matching sessions to processes via shared matchProcessesToSessions()
9
+ * using sha256(cwd) === session.projectHash as the resolvedCwd source
10
+ * 5. Extracting summary from the most recent user message in the session JSON
11
+ */
12
+ import type { AgentAdapter, AgentInfo, ProcessInfo, ConversationMessage, SessionSummary, ListSessionsOptions, AgentDetectionContext } from "../../adapters/AgentAdapter.js";
13
+ import { AgentRegistry } from "../../utils/AgentRegistry.js";
14
+ export interface GeminiCliAdapterOptions {
15
+ geminiTmpDir?: string;
16
+ }
17
+ export declare class GeminiCliAdapter implements AgentAdapter {
18
+ readonly type: "gemini_cli";
19
+ readonly processNames: readonly ["node"];
20
+ private readonly registry;
21
+ private readonly parser;
22
+ private readonly mapper;
23
+ private readonly locator;
24
+ constructor(registry?: AgentRegistry, options?: GeminiCliAdapterOptions);
25
+ canHandle(processInfo: ProcessInfo): boolean;
26
+ /**
27
+ * Detect running Gemini CLI agents.
28
+ *
29
+ * Gemini CLI ships as a Node script (`bundle/gemini.js` with shebang
30
+ * `#!/usr/bin/env node`) — unlike Claude Code (native binary per
31
+ * platform) or Codex CLI (Node wrapper that execs a native Rust
32
+ * binary). The primary running process is therefore the Node runtime
33
+ * itself, and `ps aux` lists it as `node /path/to/gemini ...` with
34
+ * argv[0] = `node`. We scan the Node process pool via the shared
35
+ * snapshot and keep only those whose command line references the gemini
36
+ * executable or script via isGeminiExecutable().
37
+ */
38
+ detectAgents(context?: AgentDetectionContext): Promise<AgentInfo[]>;
39
+ getConversation(sessionFilePath: string, options?: {
40
+ verbose?: boolean;
41
+ }): ConversationMessage[];
42
+ listSessions(opts?: ListSessionsOptions): Promise<SessionSummary[]>;
43
+ private getGeminiProcesses;
44
+ private mapSessionDiscovery;
45
+ private mapProcessOnlyAgents;
46
+ private applyWrapperRegistryName;
47
+ private deduplicateSessionAgents;
48
+ private sessionDeduplicationKey;
49
+ private tryRegistryCache;
50
+ private isGeminiExecutable;
51
+ }
52
+ //# sourceMappingURL=GeminiCliAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GeminiCliAdapter.d.ts","sourceRoot":"","sources":["../../../src/providers/gemini/GeminiCliAdapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,SAAS,EACT,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,gCAAgC,CAAC;AASxC,OAAO,EAAE,aAAa,EAAsB,MAAM,8BAA8B,CAAC;AAKjF,MAAM,WAAW,uBAAuB;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,qBAAa,gBAAiB,YAAW,YAAY;IACnD,QAAQ,CAAC,IAAI,EAAG,YAAY,CAAU;IACtC,QAAQ,CAAC,YAAY,oBAAqB;IAE1C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAC3C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;gBAG7C,QAAQ,GAAE,aAAuC,EACjD,OAAO,GAAE,uBAA4B;IAQvC,SAAS,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO;IAI5C;;;;;;;;;;;OAWG;IACG,YAAY,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAoBzE,eAAe,CAAC,eAAe,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,mBAAmB,EAAE;IAI1F,YAAY,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;YAa3D,kBAAkB;IAMhC,OAAO,CAAC,mBAAmB;IAwC3B,OAAO,CAAC,oBAAoB;IAY5B,OAAO,CAAC,wBAAwB;IAahC,OAAO,CAAC,wBAAwB;IA4BhC,OAAO,CAAC,uBAAuB;IAQ/B,OAAO,CAAC,gBAAgB;IAmCxB,OAAO,CAAC,kBAAkB;CAa3B"}
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Gemini CLI Adapter
3
+ *
4
+ * Detects running Gemini CLI agents by:
5
+ * 1. Filtering Gemini Node processes from a shared asynchronous process snapshot
6
+ * 2. Using snapshot CWD and start-time enrichment
7
+ * 3. Discovering session files from ~/.gemini/tmp/<shortId>/chats/session-*.json
8
+ * 4. Matching sessions to processes via shared matchProcessesToSessions()
9
+ * using sha256(cwd) === session.projectHash as the resolvedCwd source
10
+ * 5. Extracting summary from the most recent user message in the session JSON
11
+ */ import { captureProcessSnapshot, executableBasename, filterByProcessNames, findWrapperProcess, findWrapperProcessPids } from "../../utils/process.js";
12
+ import { matchProcessesToSessions } from "../../utils/matching.js";
13
+ import { AgentRegistry } from "../../utils/AgentRegistry.js";
14
+ import { GeminiAgentMapper } from "./GeminiAgentMapper.js";
15
+ import { GeminiSessionLocator } from "./GeminiSessionLocator.js";
16
+ import { GeminiSessionParser } from "./GeminiSessionParser.js";
17
+ export class GeminiCliAdapter {
18
+ type = "gemini_cli";
19
+ processNames = [
20
+ "node"
21
+ ];
22
+ registry;
23
+ parser;
24
+ mapper;
25
+ locator;
26
+ constructor(registry = AgentRegistry.default(), options = {}){
27
+ this.registry = registry;
28
+ this.parser = new GeminiSessionParser();
29
+ this.mapper = new GeminiAgentMapper(this.parser);
30
+ this.locator = new GeminiSessionLocator(options);
31
+ }
32
+ canHandle(processInfo) {
33
+ return this.isGeminiExecutable(processInfo.command);
34
+ }
35
+ /**
36
+ * Detect running Gemini CLI agents.
37
+ *
38
+ * Gemini CLI ships as a Node script (`bundle/gemini.js` with shebang
39
+ * `#!/usr/bin/env node`) — unlike Claude Code (native binary per
40
+ * platform) or Codex CLI (Node wrapper that execs a native Rust
41
+ * binary). The primary running process is therefore the Node runtime
42
+ * itself, and `ps aux` lists it as `node /path/to/gemini ...` with
43
+ * argv[0] = `node`. We scan the Node process pool via the shared
44
+ * snapshot and keep only those whose command line references the gemini
45
+ * executable or script via isGeminiExecutable().
46
+ */ async detectAgents(context) {
47
+ const processes = await this.getGeminiProcesses(context);
48
+ if (processes.length === 0) return [];
49
+ const wrapperPids = findWrapperProcessPids(processes);
50
+ const { cachedAgents, remaining } = this.tryRegistryCache(processes, wrapperPids);
51
+ if (remaining.length === 0) return this.deduplicateSessionAgents(cachedAgents);
52
+ const candidateProcesses = remaining.filter((proc)=>!wrapperPids.has(proc.pid));
53
+ const registryEntriesByPid = new Map(this.registry.list().map((entry)=>[
54
+ entry.pid,
55
+ entry
56
+ ]));
57
+ const discovery = this.locator.discoverSessions(candidateProcesses);
58
+ const agents = discovery.sessions.length === 0 ? this.mapProcessOnlyAgents(candidateProcesses, processes, registryEntriesByPid) : this.mapSessionDiscovery(discovery, candidateProcesses, processes, registryEntriesByPid);
59
+ return this.deduplicateSessionAgents([
60
+ ...cachedAgents,
61
+ ...agents
62
+ ]);
63
+ }
64
+ getConversation(sessionFilePath, options) {
65
+ return this.parser.getConversation(sessionFilePath, options);
66
+ }
67
+ async listSessions(opts) {
68
+ const summaries = [];
69
+ for (const filePath of this.locator.discoverHistoricalSessionFiles()){
70
+ const summary = this.parser.fileToSessionSummary(filePath);
71
+ if (!summary) continue;
72
+ if (opts?.cwd !== undefined && summary.cwd !== opts.cwd) continue;
73
+ summaries.push(summary);
74
+ }
75
+ return summaries;
76
+ }
77
+ async getGeminiProcesses(context) {
78
+ const snapshot = context?.processes ?? await captureProcessSnapshot(this.processNames);
79
+ const relevant = filterByProcessNames(snapshot, this.processNames);
80
+ return relevant.filter((process)=>this.canHandle(process));
81
+ }
82
+ mapSessionDiscovery(discovery, candidateProcesses, allProcesses, registryEntriesByPid) {
83
+ const matches = matchProcessesToSessions(candidateProcesses, discovery.sessions);
84
+ const matchedPids = new Set(matches.map((m)=>m.process.pid));
85
+ const matchedProcesses = [];
86
+ const agents = [];
87
+ for (const match of matches){
88
+ const cachedContent = discovery.contentCache.get(match.session.filePath);
89
+ const sessionData = this.parser.parseSession(cachedContent, match.session.filePath);
90
+ if (!sessionData) {
91
+ matchedPids.delete(match.process.pid);
92
+ continue;
93
+ }
94
+ const agent = this.mapper.mapSessionToAgent(sessionData, match.process, match.session.filePath);
95
+ this.applyWrapperRegistryName(agent, match.process, allProcesses, registryEntriesByPid);
96
+ agents.push(agent);
97
+ matchedProcesses.push(match.process);
98
+ }
99
+ const matchedWrapperPids = findWrapperProcessPids(candidateProcesses, matchedProcesses);
100
+ const unmatchedProcesses = candidateProcesses.filter((process)=>!matchedPids.has(process.pid) && !matchedWrapperPids.has(process.pid));
101
+ agents.push(...this.mapProcessOnlyAgents(unmatchedProcesses, allProcesses, registryEntriesByPid));
102
+ return agents;
103
+ }
104
+ mapProcessOnlyAgents(processes, allProcesses, registryEntriesByPid) {
105
+ return processes.map((processInfo)=>{
106
+ const agent = this.mapper.mapProcessOnlyAgent(processInfo);
107
+ this.applyWrapperRegistryName(agent, processInfo, allProcesses, registryEntriesByPid);
108
+ return agent;
109
+ });
110
+ }
111
+ applyWrapperRegistryName(agent, processInfo, processes, registryEntriesByPid) {
112
+ const wrapper = findWrapperProcess(processes, processInfo);
113
+ const wrapperEntry = wrapper ? registryEntriesByPid.get(wrapper.pid) : undefined;
114
+ if (wrapperEntry?.type === this.type) {
115
+ agent.name = wrapperEntry.name;
116
+ }
117
+ }
118
+ deduplicateSessionAgents(agents) {
119
+ const bySession = new Map();
120
+ const result = [];
121
+ for (const agent of agents){
122
+ const sessionKey = this.sessionDeduplicationKey(agent);
123
+ if (!sessionKey) {
124
+ result.push(agent);
125
+ continue;
126
+ }
127
+ const existing = bySession.get(sessionKey);
128
+ if (!existing) {
129
+ bySession.set(sessionKey, agent);
130
+ result.push(agent);
131
+ continue;
132
+ }
133
+ if (agent.pid > existing.pid) {
134
+ bySession.set(sessionKey, agent);
135
+ const index = result.indexOf(existing);
136
+ if (index >= 0) result[index] = agent;
137
+ }
138
+ }
139
+ return result;
140
+ }
141
+ sessionDeduplicationKey(agent) {
142
+ if (agent.sessionFilePath) return `file:${agent.sessionFilePath}`;
143
+ if (agent.sessionId && !agent.sessionId.startsWith("pid-")) {
144
+ return `session:${agent.sessionId}`;
145
+ }
146
+ return null;
147
+ }
148
+ tryRegistryCache(processes, wrapperPids) {
149
+ const cachedAgents = [];
150
+ const remaining = [];
151
+ const byPid = new Map(this.registry.list().map((e)=>[
152
+ e.pid,
153
+ e
154
+ ]));
155
+ for (const proc of processes){
156
+ const entry = byPid.get(proc.pid);
157
+ if (wrapperPids.has(proc.pid) || !entry || entry.type !== this.type || !entry.sessionFilePath) {
158
+ remaining.push(proc);
159
+ continue;
160
+ }
161
+ const sessionData = this.parser.parseSession(undefined, entry.sessionFilePath);
162
+ if (!sessionData) {
163
+ remaining.push(proc);
164
+ continue;
165
+ }
166
+ cachedAgents.push(this.mapper.mapSessionToAgent(sessionData, proc, entry.sessionFilePath));
167
+ }
168
+ return {
169
+ cachedAgents,
170
+ remaining
171
+ };
172
+ }
173
+ isGeminiExecutable(command) {
174
+ // Accept any token in the command line whose basename matches a
175
+ // known gemini entrypoint. This is intentionally broader than the
176
+ // other adapters' argv[0]-only check because the Node-script
177
+ // distribution puts the real gemini path in argv[1..], not argv[0].
178
+ for (const token of command.trim().split(/\s+/)){
179
+ const base = executableBasename(token);
180
+ if (base === "gemini" || base === "gemini.exe" || base === "gemini.js") {
181
+ return true;
182
+ }
183
+ }
184
+ return false;
185
+ }
186
+ }
187
+
188
+ //# sourceMappingURL=GeminiCliAdapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/providers/gemini/GeminiCliAdapter.ts"],"sourcesContent":["/**\n * Gemini CLI Adapter\n *\n * Detects running Gemini CLI agents by:\n * 1. Filtering Gemini Node processes from a shared asynchronous process snapshot\n * 2. Using snapshot CWD and start-time enrichment\n * 3. Discovering session files from ~/.gemini/tmp/<shortId>/chats/session-*.json\n * 4. Matching sessions to processes via shared matchProcessesToSessions()\n * using sha256(cwd) === session.projectHash as the resolvedCwd source\n * 5. Extracting summary from the most recent user message in the session JSON\n */\n\nimport type {\n AgentAdapter,\n AgentInfo,\n ProcessInfo,\n ConversationMessage,\n SessionSummary,\n ListSessionsOptions,\n AgentDetectionContext,\n} from \"../../adapters/AgentAdapter.js\";\nimport {\n captureProcessSnapshot,\n executableBasename,\n filterByProcessNames,\n findWrapperProcess,\n findWrapperProcessPids,\n} from \"../../utils/process.js\";\nimport { matchProcessesToSessions } from \"../../utils/matching.js\";\nimport { AgentRegistry, type RegistryEntry } from \"../../utils/AgentRegistry.js\";\nimport { GeminiAgentMapper } from \"./GeminiAgentMapper.js\";\nimport { GeminiSessionLocator, type GeminiSessionDiscovery } from \"./GeminiSessionLocator.js\";\nimport { GeminiSessionParser } from \"./GeminiSessionParser.js\";\n\nexport interface GeminiCliAdapterOptions {\n geminiTmpDir?: string;\n}\n\nexport class GeminiCliAdapter implements AgentAdapter {\n readonly type = \"gemini_cli\" as const;\n readonly processNames = [\"node\"] as const;\n\n private readonly registry: AgentRegistry;\n private readonly parser: GeminiSessionParser;\n private readonly mapper: GeminiAgentMapper;\n private readonly locator: GeminiSessionLocator;\n\n constructor(\n registry: AgentRegistry = AgentRegistry.default(),\n options: GeminiCliAdapterOptions = {},\n ) {\n this.registry = registry;\n this.parser = new GeminiSessionParser();\n this.mapper = new GeminiAgentMapper(this.parser);\n this.locator = new GeminiSessionLocator(options);\n }\n\n canHandle(processInfo: ProcessInfo): boolean {\n return this.isGeminiExecutable(processInfo.command);\n }\n\n /**\n * Detect running Gemini CLI agents.\n *\n * Gemini CLI ships as a Node script (`bundle/gemini.js` with shebang\n * `#!/usr/bin/env node`) — unlike Claude Code (native binary per\n * platform) or Codex CLI (Node wrapper that execs a native Rust\n * binary). The primary running process is therefore the Node runtime\n * itself, and `ps aux` lists it as `node /path/to/gemini ...` with\n * argv[0] = `node`. We scan the Node process pool via the shared\n * snapshot and keep only those whose command line references the gemini\n * executable or script via isGeminiExecutable().\n */\n async detectAgents(context?: AgentDetectionContext): Promise<AgentInfo[]> {\n const processes = await this.getGeminiProcesses(context);\n if (processes.length === 0) return [];\n\n const wrapperPids = findWrapperProcessPids(processes);\n const { cachedAgents, remaining } = this.tryRegistryCache(processes, wrapperPids);\n if (remaining.length === 0) return this.deduplicateSessionAgents(cachedAgents);\n\n const candidateProcesses = remaining.filter((proc) => !wrapperPids.has(proc.pid));\n const registryEntriesByPid = new Map(this.registry.list().map((entry) => [entry.pid, entry]));\n const discovery = this.locator.discoverSessions(candidateProcesses);\n\n const agents =\n discovery.sessions.length === 0\n ? this.mapProcessOnlyAgents(candidateProcesses, processes, registryEntriesByPid)\n : this.mapSessionDiscovery(discovery, candidateProcesses, processes, registryEntriesByPid);\n\n return this.deduplicateSessionAgents([...cachedAgents, ...agents]);\n }\n\n getConversation(sessionFilePath: string, options?: { verbose?: boolean }): ConversationMessage[] {\n return this.parser.getConversation(sessionFilePath, options);\n }\n\n async listSessions(opts?: ListSessionsOptions): Promise<SessionSummary[]> {\n const summaries: SessionSummary[] = [];\n\n for (const filePath of this.locator.discoverHistoricalSessionFiles()) {\n const summary = this.parser.fileToSessionSummary(filePath);\n if (!summary) continue;\n if (opts?.cwd !== undefined && summary.cwd !== opts.cwd) continue;\n summaries.push(summary);\n }\n\n return summaries;\n }\n\n private async getGeminiProcesses(context?: AgentDetectionContext): Promise<ProcessInfo[]> {\n const snapshot = context?.processes ?? (await captureProcessSnapshot(this.processNames));\n const relevant = filterByProcessNames(snapshot, this.processNames);\n return relevant.filter((process) => this.canHandle(process));\n }\n\n private mapSessionDiscovery(\n discovery: GeminiSessionDiscovery,\n candidateProcesses: ProcessInfo[],\n allProcesses: ProcessInfo[],\n registryEntriesByPid: Map<number, RegistryEntry>,\n ): AgentInfo[] {\n const matches = matchProcessesToSessions(candidateProcesses, discovery.sessions);\n const matchedPids = new Set(matches.map((m) => m.process.pid));\n const matchedProcesses: ProcessInfo[] = [];\n const agents: AgentInfo[] = [];\n\n for (const match of matches) {\n const cachedContent = discovery.contentCache.get(match.session.filePath);\n const sessionData = this.parser.parseSession(cachedContent, match.session.filePath);\n if (!sessionData) {\n matchedPids.delete(match.process.pid);\n continue;\n }\n\n const agent = this.mapper.mapSessionToAgent(\n sessionData,\n match.process,\n match.session.filePath,\n );\n this.applyWrapperRegistryName(agent, match.process, allProcesses, registryEntriesByPid);\n agents.push(agent);\n matchedProcesses.push(match.process);\n }\n\n const matchedWrapperPids = findWrapperProcessPids(candidateProcesses, matchedProcesses);\n const unmatchedProcesses = candidateProcesses.filter(\n (process) => !matchedPids.has(process.pid) && !matchedWrapperPids.has(process.pid),\n );\n\n agents.push(\n ...this.mapProcessOnlyAgents(unmatchedProcesses, allProcesses, registryEntriesByPid),\n );\n return agents;\n }\n\n private mapProcessOnlyAgents(\n processes: ProcessInfo[],\n allProcesses: ProcessInfo[],\n registryEntriesByPid: Map<number, RegistryEntry>,\n ): AgentInfo[] {\n return processes.map((processInfo) => {\n const agent = this.mapper.mapProcessOnlyAgent(processInfo);\n this.applyWrapperRegistryName(agent, processInfo, allProcesses, registryEntriesByPid);\n return agent;\n });\n }\n\n private applyWrapperRegistryName(\n agent: AgentInfo,\n processInfo: ProcessInfo,\n processes: ProcessInfo[],\n registryEntriesByPid: Map<number, RegistryEntry>,\n ): void {\n const wrapper = findWrapperProcess(processes, processInfo);\n const wrapperEntry = wrapper ? registryEntriesByPid.get(wrapper.pid) : undefined;\n if (wrapperEntry?.type === this.type) {\n agent.name = wrapperEntry.name;\n }\n }\n\n private deduplicateSessionAgents(agents: AgentInfo[]): AgentInfo[] {\n const bySession = new Map<string, AgentInfo>();\n const result: AgentInfo[] = [];\n\n for (const agent of agents) {\n const sessionKey = this.sessionDeduplicationKey(agent);\n if (!sessionKey) {\n result.push(agent);\n continue;\n }\n\n const existing = bySession.get(sessionKey);\n if (!existing) {\n bySession.set(sessionKey, agent);\n result.push(agent);\n continue;\n }\n\n if (agent.pid > existing.pid) {\n bySession.set(sessionKey, agent);\n const index = result.indexOf(existing);\n if (index >= 0) result[index] = agent;\n }\n }\n\n return result;\n }\n\n private sessionDeduplicationKey(agent: AgentInfo): string | null {\n if (agent.sessionFilePath) return `file:${agent.sessionFilePath}`;\n if (agent.sessionId && !agent.sessionId.startsWith(\"pid-\")) {\n return `session:${agent.sessionId}`;\n }\n return null;\n }\n\n private tryRegistryCache(\n processes: ProcessInfo[],\n wrapperPids: Set<number>,\n ): {\n cachedAgents: AgentInfo[];\n remaining: ProcessInfo[];\n } {\n const cachedAgents: AgentInfo[] = [];\n const remaining: ProcessInfo[] = [];\n const byPid = new Map(this.registry.list().map((e) => [e.pid, e]));\n\n for (const proc of processes) {\n const entry = byPid.get(proc.pid);\n if (\n wrapperPids.has(proc.pid) ||\n !entry ||\n entry.type !== this.type ||\n !entry.sessionFilePath\n ) {\n remaining.push(proc);\n continue;\n }\n\n const sessionData = this.parser.parseSession(undefined, entry.sessionFilePath);\n if (!sessionData) {\n remaining.push(proc);\n continue;\n }\n\n cachedAgents.push(this.mapper.mapSessionToAgent(sessionData, proc, entry.sessionFilePath));\n }\n\n return { cachedAgents, remaining };\n }\n\n private isGeminiExecutable(command: string): boolean {\n // Accept any token in the command line whose basename matches a\n // known gemini entrypoint. This is intentionally broader than the\n // other adapters' argv[0]-only check because the Node-script\n // distribution puts the real gemini path in argv[1..], not argv[0].\n for (const token of command.trim().split(/\\s+/)) {\n const base = executableBasename(token);\n if (base === \"gemini\" || base === \"gemini.exe\" || base === \"gemini.js\") {\n return true;\n }\n }\n return false;\n }\n}\n"],"names":["captureProcessSnapshot","executableBasename","filterByProcessNames","findWrapperProcess","findWrapperProcessPids","matchProcessesToSessions","AgentRegistry","GeminiAgentMapper","GeminiSessionLocator","GeminiSessionParser","GeminiCliAdapter","type","processNames","registry","parser","mapper","locator","default","options","canHandle","processInfo","isGeminiExecutable","command","detectAgents","context","processes","getGeminiProcesses","length","wrapperPids","cachedAgents","remaining","tryRegistryCache","deduplicateSessionAgents","candidateProcesses","filter","proc","has","pid","registryEntriesByPid","Map","list","map","entry","discovery","discoverSessions","agents","sessions","mapProcessOnlyAgents","mapSessionDiscovery","getConversation","sessionFilePath","listSessions","opts","summaries","filePath","discoverHistoricalSessionFiles","summary","fileToSessionSummary","cwd","undefined","push","snapshot","relevant","process","allProcesses","matches","matchedPids","Set","m","matchedProcesses","match","cachedContent","contentCache","get","session","sessionData","parseSession","delete","agent","mapSessionToAgent","applyWrapperRegistryName","matchedWrapperPids","unmatchedProcesses","mapProcessOnlyAgent","wrapper","wrapperEntry","name","bySession","result","sessionKey","sessionDeduplicationKey","existing","set","index","indexOf","sessionId","startsWith","byPid","e","token","trim","split","base"],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import type { ProcessInfo } from "../../adapters/AgentAdapter.js";
2
+ import type { SessionFile } from "../../utils/session.js";
3
+ export interface GeminiSessionDiscovery {
4
+ sessions: SessionFile[];
5
+ contentCache: Map<string, string>;
6
+ }
7
+ export interface GeminiSessionLocatorOptions {
8
+ geminiTmpDir?: string;
9
+ }
10
+ export declare class GeminiSessionLocator {
11
+ private readonly geminiTmpDir;
12
+ constructor(options?: GeminiSessionLocatorOptions);
13
+ /**
14
+ * Discover session files for the given processes.
15
+ *
16
+ * Gemini CLI writes sessions to ~/.gemini/tmp/<shortId>/chats/session-*.json
17
+ * where <shortId> is opaque (managed by a project registry). We scan every
18
+ * shortId directory and filter by matching session.projectHash against
19
+ * sha256(process.cwd) to bind each session to a candidate process CWD.
20
+ */
21
+ discoverSessions(processes: ProcessInfo[]): GeminiSessionDiscovery;
22
+ discoverHistoricalSessionFiles(): string[];
23
+ private readCandidateSession;
24
+ private buildCwdHashMap;
25
+ private candidateProjectRoots;
26
+ private hashProjectRoot;
27
+ private isSessionFile;
28
+ private parseSessionMetadata;
29
+ }
30
+ //# sourceMappingURL=GeminiSessionLocator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GeminiSessionLocator.d.ts","sourceRoot":"","sources":["../../../src/providers/gemini/GeminiSessionLocator.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAG1D,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,2BAA2B;IAC1C,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAUD,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;gBAE1B,OAAO,GAAE,2BAAgC;IAKrD;;;;;;;OAOG;IACH,gBAAgB,CAAC,SAAS,EAAE,WAAW,EAAE,GAAG,sBAAsB;IAmClE,8BAA8B,IAAI,MAAM,EAAE;IAkB1C,OAAO,CAAC,oBAAoB;IAwC5B,OAAO,CAAC,eAAe;IAqBvB,OAAO,CAAC,qBAAqB;IAa7B,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,oBAAoB;CAO7B"}