@cloudcli-ai/cloudcli 1.36.0 → 1.36.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 (217) hide show
  1. package/README.de.md +17 -18
  2. package/README.ja.md +16 -17
  3. package/README.ko.md +17 -18
  4. package/README.md +19 -20
  5. package/README.ru.md +17 -18
  6. package/README.tr.md +17 -18
  7. package/README.zh-CN.md +17 -18
  8. package/README.zh-TW.md +17 -18
  9. package/dist/api-docs.html +2 -3
  10. package/dist/assets/{index-S6cIy7hj.js → index-Bz9vXV2m.js} +264 -259
  11. package/dist/assets/index-DLmhQ15L.css +32 -0
  12. package/dist/assets/{vendor-xterm-CJZjLICi.js → vendor-xterm-CS4rQ5Mr.js} +12 -12
  13. package/dist/index.html +3 -3
  14. package/dist-server/server/claude-sdk.js +37 -90
  15. package/dist-server/server/claude-sdk.js.map +1 -1
  16. package/dist-server/server/cli.js +1 -4
  17. package/dist-server/server/cli.js.map +1 -1
  18. package/dist-server/server/cursor-cli.js +12 -11
  19. package/dist-server/server/cursor-cli.js.map +1 -1
  20. package/dist-server/server/index.js +10 -141
  21. package/dist-server/server/index.js.map +1 -1
  22. package/dist-server/server/modules/assets/assets.routes.js +89 -0
  23. package/dist-server/server/modules/assets/assets.routes.js.map +1 -0
  24. package/dist-server/server/modules/assets/index.js +4 -0
  25. package/dist-server/server/modules/assets/index.js.map +1 -0
  26. package/dist-server/server/modules/assets/services/image-assets.service.js +57 -0
  27. package/dist-server/server/modules/assets/services/image-assets.service.js.map +1 -0
  28. package/dist-server/server/modules/assets/tests/image-assets.service.test.js +36 -0
  29. package/dist-server/server/modules/assets/tests/image-assets.service.test.js.map +1 -0
  30. package/dist-server/server/modules/browser-use/browser-use.service.js +6 -3
  31. package/dist-server/server/modules/browser-use/browser-use.service.js.map +1 -1
  32. package/dist-server/server/modules/database/tests/sessions-provider-mapping.test.js +2 -2
  33. package/dist-server/server/modules/database/tests/sessions-provider-mapping.test.js.map +1 -1
  34. package/dist-server/server/modules/notifications/services/notification-orchestrator.service.js +0 -1
  35. package/dist-server/server/modules/notifications/services/notification-orchestrator.service.js.map +1 -1
  36. package/dist-server/server/modules/projects/services/project-clone.service.js +2 -1
  37. package/dist-server/server/modules/projects/services/project-clone.service.js.map +1 -1
  38. package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js +28 -0
  39. package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js.map +1 -1
  40. package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js +54 -1
  41. package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js.map +1 -1
  42. package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js +36 -1
  43. package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js.map +1 -1
  44. package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js +3 -17
  45. package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js.map +1 -1
  46. package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js +7 -1
  47. package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js.map +1 -1
  48. package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js +49 -13
  49. package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js.map +1 -1
  50. package/dist-server/server/modules/providers/list/opencode/opencode-auth.provider.js +0 -1
  51. package/dist-server/server/modules/providers/list/opencode/opencode-auth.provider.js.map +1 -1
  52. package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js +8 -14
  53. package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js.map +1 -1
  54. package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js +24 -5
  55. package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js.map +1 -1
  56. package/dist-server/server/modules/providers/list/opencode/opencode-sessions.provider.js +10 -21
  57. package/dist-server/server/modules/providers/list/opencode/opencode-sessions.provider.js.map +1 -1
  58. package/dist-server/server/modules/providers/provider.registry.js +0 -2
  59. package/dist-server/server/modules/providers/provider.registry.js.map +1 -1
  60. package/dist-server/server/modules/providers/provider.routes.js +0 -1
  61. package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
  62. package/dist-server/server/modules/providers/services/provider-capabilities.service.js +7 -14
  63. package/dist-server/server/modules/providers/services/provider-capabilities.service.js.map +1 -1
  64. package/dist-server/server/modules/providers/services/provider-models.service.js +1 -1
  65. package/dist-server/server/modules/providers/services/provider-models.service.js.map +1 -1
  66. package/dist-server/server/modules/providers/services/session-conversations-search.service.js +3 -77
  67. package/dist-server/server/modules/providers/services/session-conversations-search.service.js.map +1 -1
  68. package/dist-server/server/modules/providers/services/session-synchronizer.service.js +0 -1
  69. package/dist-server/server/modules/providers/services/session-synchronizer.service.js.map +1 -1
  70. package/dist-server/server/modules/providers/services/sessions-watcher.service.js +0 -13
  71. package/dist-server/server/modules/providers/services/sessions-watcher.service.js.map +1 -1
  72. package/dist-server/server/modules/providers/tests/codex-sessions.test.js +96 -0
  73. package/dist-server/server/modules/providers/tests/codex-sessions.test.js.map +1 -0
  74. package/dist-server/server/modules/providers/tests/mcp.test.js +3 -29
  75. package/dist-server/server/modules/providers/tests/mcp.test.js.map +1 -1
  76. package/dist-server/server/modules/providers/tests/opencode-models.test.js +7 -0
  77. package/dist-server/server/modules/providers/tests/opencode-models.test.js.map +1 -1
  78. package/dist-server/server/modules/providers/tests/opencode-sessions.test.js +117 -0
  79. package/dist-server/server/modules/providers/tests/opencode-sessions.test.js.map +1 -1
  80. package/dist-server/server/modules/providers/tests/provider-image-history.test.js +147 -0
  81. package/dist-server/server/modules/providers/tests/provider-image-history.test.js.map +1 -0
  82. package/dist-server/server/modules/providers/tests/provider-models.service.test.js +8 -8
  83. package/dist-server/server/modules/providers/tests/skills.test.js +3 -26
  84. package/dist-server/server/modules/providers/tests/skills.test.js.map +1 -1
  85. package/dist-server/server/modules/websocket/services/chat-run-registry.service.js +14 -0
  86. package/dist-server/server/modules/websocket/services/chat-run-registry.service.js.map +1 -1
  87. package/dist-server/server/modules/websocket/services/chat-websocket.service.js +36 -2
  88. package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -1
  89. package/dist-server/server/modules/websocket/services/shell-websocket.service.js +3 -12
  90. package/dist-server/server/modules/websocket/services/shell-websocket.service.js.map +1 -1
  91. package/dist-server/server/modules/websocket/tests/chat-image-filter.test.js +33 -0
  92. package/dist-server/server/modules/websocket/tests/chat-image-filter.test.js.map +1 -0
  93. package/dist-server/server/modules/websocket/tests/chat-run-registry.test.js +37 -4
  94. package/dist-server/server/modules/websocket/tests/chat-run-registry.test.js.map +1 -1
  95. package/dist-server/server/openai-codex.js +8 -2
  96. package/dist-server/server/openai-codex.js.map +1 -1
  97. package/dist-server/server/opencode-cli.js +43 -6
  98. package/dist-server/server/opencode-cli.js.map +1 -1
  99. package/dist-server/server/opencode-cli.test.js +103 -3
  100. package/dist-server/server/opencode-cli.test.js.map +1 -1
  101. package/dist-server/server/routes/agent.js +9 -19
  102. package/dist-server/server/routes/agent.js.map +1 -1
  103. package/dist-server/server/routes/commands.js +1 -2
  104. package/dist-server/server/routes/commands.js.map +1 -1
  105. package/dist-server/server/routes/git.js +179 -53
  106. package/dist-server/server/routes/git.js.map +1 -1
  107. package/dist-server/server/routes/git.test.js +80 -0
  108. package/dist-server/server/routes/git.test.js.map +1 -0
  109. package/dist-server/server/routes/taskmaster.js +3 -1
  110. package/dist-server/server/routes/taskmaster.js.map +1 -1
  111. package/dist-server/server/routes/user.js +2 -1
  112. package/dist-server/server/routes/user.js.map +1 -1
  113. package/dist-server/server/shared/image-attachments.js +270 -0
  114. package/dist-server/server/shared/image-attachments.js.map +1 -0
  115. package/dist-server/server/shared/tests/image-attachments.test.js +220 -0
  116. package/dist-server/server/shared/tests/image-attachments.test.js.map +1 -0
  117. package/dist-server/server/shared/utils.js +43 -0
  118. package/dist-server/server/shared/utils.js.map +1 -1
  119. package/dist-server/server/utils/gitConfig.js +2 -1
  120. package/dist-server/server/utils/gitConfig.js.map +1 -1
  121. package/dist-server/server/utils/plugin-process-manager.js +2 -1
  122. package/dist-server/server/utils/plugin-process-manager.js.map +1 -1
  123. package/electron/launcher/launcher.js +1 -1
  124. package/package.json +3 -3
  125. package/public/api-docs.html +2 -3
  126. package/server/claude-sdk.js +37 -105
  127. package/server/cli.js +1 -4
  128. package/server/cursor-cli.js +13 -12
  129. package/server/index.js +11 -159
  130. package/server/modules/assets/assets.routes.ts +103 -0
  131. package/server/modules/assets/index.ts +3 -0
  132. package/server/modules/assets/services/image-assets.service.ts +82 -0
  133. package/server/modules/assets/tests/image-assets.service.test.ts +46 -0
  134. package/server/modules/browser-use/browser-use.service.ts +7 -3
  135. package/server/modules/database/tests/sessions-provider-mapping.test.ts +2 -2
  136. package/server/modules/notifications/services/notification-orchestrator.service.js +0 -1
  137. package/server/modules/projects/services/project-clone.service.ts +3 -1
  138. package/server/modules/providers/README.md +1 -7
  139. package/server/modules/providers/list/claude/claude-sessions.provider.ts +30 -0
  140. package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +60 -1
  141. package/server/modules/providers/list/codex/codex-sessions.provider.ts +41 -1
  142. package/server/modules/providers/list/cursor/cursor-models.provider.ts +3 -17
  143. package/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.ts +7 -1
  144. package/server/modules/providers/list/cursor/cursor-sessions.provider.ts +56 -15
  145. package/server/modules/providers/list/opencode/opencode-auth.provider.ts +0 -1
  146. package/server/modules/providers/list/opencode/opencode-models.provider.ts +15 -19
  147. package/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.ts +25 -4
  148. package/server/modules/providers/list/opencode/opencode-sessions.provider.ts +10 -21
  149. package/server/modules/providers/provider.registry.ts +0 -2
  150. package/server/modules/providers/provider.routes.ts +0 -1
  151. package/server/modules/providers/services/provider-capabilities.service.ts +7 -14
  152. package/server/modules/providers/services/provider-models.service.ts +1 -1
  153. package/server/modules/providers/services/session-conversations-search.service.ts +3 -93
  154. package/server/modules/providers/services/session-synchronizer.service.ts +0 -1
  155. package/server/modules/providers/services/sessions-watcher.service.ts +0 -14
  156. package/server/modules/providers/tests/codex-sessions.test.ts +111 -0
  157. package/server/modules/providers/tests/mcp.test.ts +3 -34
  158. package/server/modules/providers/tests/opencode-models.test.ts +7 -0
  159. package/server/modules/providers/tests/opencode-sessions.test.ts +139 -0
  160. package/server/modules/providers/tests/provider-image-history.test.ts +180 -0
  161. package/server/modules/providers/tests/provider-models.service.test.ts +8 -8
  162. package/server/modules/providers/tests/skills.test.ts +3 -42
  163. package/server/modules/websocket/services/chat-run-registry.service.ts +16 -0
  164. package/server/modules/websocket/services/chat-websocket.service.ts +41 -2
  165. package/server/modules/websocket/services/shell-websocket.service.ts +1 -11
  166. package/server/modules/websocket/tests/chat-image-filter.test.ts +44 -0
  167. package/server/modules/websocket/tests/chat-run-registry.test.ts +42 -4
  168. package/server/openai-codex.js +9 -1
  169. package/server/opencode-cli.js +44 -6
  170. package/server/opencode-cli.test.js +112 -3
  171. package/server/routes/agent.js +9 -19
  172. package/server/routes/commands.js +1 -2
  173. package/server/routes/git.js +201 -60
  174. package/server/routes/git.test.js +106 -0
  175. package/server/routes/taskmaster.js +3 -1
  176. package/server/routes/user.js +2 -1
  177. package/server/shared/image-attachments.ts +341 -0
  178. package/server/shared/tests/image-attachments.test.ts +298 -0
  179. package/server/shared/types.ts +1 -1
  180. package/server/shared/utils.ts +45 -0
  181. package/server/utils/gitConfig.js +2 -1
  182. package/server/utils/plugin-process-manager.js +3 -1
  183. package/dist/assets/index-uLUE6Qt1.css +0 -32
  184. package/dist/icons/gemini-ai-icon.svg +0 -1
  185. package/dist-server/server/gemini-cli.js +0 -551
  186. package/dist-server/server/gemini-cli.js.map +0 -1
  187. package/dist-server/server/gemini-response-handler.js +0 -106
  188. package/dist-server/server/gemini-response-handler.js.map +0 -1
  189. package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js +0 -254
  190. package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js.map +0 -1
  191. package/dist-server/server/modules/providers/list/gemini/gemini-mcp.provider.js +0 -82
  192. package/dist-server/server/modules/providers/list/gemini/gemini-mcp.provider.js.map +0 -1
  193. package/dist-server/server/modules/providers/list/gemini/gemini-models.provider.js +0 -24
  194. package/dist-server/server/modules/providers/list/gemini/gemini-models.provider.js.map +0 -1
  195. package/dist-server/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.js +0 -312
  196. package/dist-server/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.js.map +0 -1
  197. package/dist-server/server/modules/providers/list/gemini/gemini-sessions.provider.js +0 -474
  198. package/dist-server/server/modules/providers/list/gemini/gemini-sessions.provider.js.map +0 -1
  199. package/dist-server/server/modules/providers/list/gemini/gemini-skills.provider.js +0 -40
  200. package/dist-server/server/modules/providers/list/gemini/gemini-skills.provider.js.map +0 -1
  201. package/dist-server/server/modules/providers/list/gemini/gemini.provider.js +0 -19
  202. package/dist-server/server/modules/providers/list/gemini/gemini.provider.js.map +0 -1
  203. package/dist-server/server/routes/gemini.js +0 -21
  204. package/dist-server/server/routes/gemini.js.map +0 -1
  205. package/dist-server/server/sessionManager.js +0 -194
  206. package/dist-server/server/sessionManager.js.map +0 -1
  207. package/server/gemini-cli.js +0 -638
  208. package/server/gemini-response-handler.js +0 -117
  209. package/server/modules/providers/list/gemini/gemini-auth.provider.ts +0 -307
  210. package/server/modules/providers/list/gemini/gemini-mcp.provider.ts +0 -110
  211. package/server/modules/providers/list/gemini/gemini-models.provider.ts +0 -39
  212. package/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.ts +0 -405
  213. package/server/modules/providers/list/gemini/gemini-sessions.provider.ts +0 -540
  214. package/server/modules/providers/list/gemini/gemini-skills.provider.ts +0 -44
  215. package/server/modules/providers/list/gemini/gemini.provider.ts +0 -27
  216. package/server/routes/gemini.js +0 -25
  217. package/server/sessionManager.js +0 -226
@@ -1,540 +0,0 @@
1
- import fsSync from 'node:fs';
2
- import fs from 'node:fs/promises';
3
- import readline from 'node:readline';
4
-
5
- import { sessionsDb } from '@/modules/database/index.js';
6
- import type { IProviderSessions } from '@/shared/interfaces.js';
7
- import type { AnyRecord, FetchHistoryOptions, FetchHistoryResult, NormalizedMessage } from '@/shared/types.js';
8
- import { createNormalizedMessage, generateMessageId, readObjectRecord, sliceTailPage } from '@/shared/utils.js';
9
-
10
- const PROVIDER = 'gemini';
11
-
12
- type GeminiHistoryResult = {
13
- messages: AnyRecord[];
14
- tokenUsage?: unknown;
15
- };
16
-
17
- function mapGeminiRole(value: unknown): 'user' | 'assistant' | null {
18
- if (value === 'user') {
19
- return 'user';
20
- }
21
-
22
- if (value === 'gemini' || value === 'assistant') {
23
- return 'assistant';
24
- }
25
-
26
- return null;
27
- }
28
-
29
- function extractGeminiTextContent(content: unknown): string {
30
- if (typeof content === 'string') {
31
- return content;
32
- }
33
-
34
- if (!Array.isArray(content)) {
35
- return '';
36
- }
37
-
38
- return content
39
- .map((part) => {
40
- if (typeof part === 'string') {
41
- return part;
42
- }
43
- if (!part || typeof part !== 'object') {
44
- return '';
45
- }
46
-
47
- const record = part as AnyRecord;
48
- if (typeof record.text === 'string') {
49
- return record.text;
50
- }
51
-
52
- return '';
53
- })
54
- .filter(Boolean)
55
- .join('\n');
56
- }
57
-
58
- function extractGeminiThoughts(thoughts: unknown): string {
59
- if (!Array.isArray(thoughts)) {
60
- return '';
61
- }
62
-
63
- return thoughts
64
- .map((item) => {
65
- if (!item || typeof item !== 'object') {
66
- return '';
67
- }
68
-
69
- const record = item as AnyRecord;
70
- const subject = typeof record.subject === 'string' ? record.subject.trim() : '';
71
- const description = typeof record.description === 'string' ? record.description.trim() : '';
72
-
73
- if (subject && description) {
74
- return `${subject}: ${description}`;
75
- }
76
-
77
- return description || subject;
78
- })
79
- .filter(Boolean)
80
- .join('\n');
81
- }
82
-
83
- function buildGeminiTokenUsage(tokens: unknown): AnyRecord | undefined {
84
- if (!tokens || typeof tokens !== 'object') {
85
- return undefined;
86
- }
87
-
88
- const record = tokens as AnyRecord;
89
- const input = Number(record.input || 0);
90
- const output = Number(record.output || 0);
91
- const total = Number(record.total || input + output || 0);
92
-
93
- return {
94
- used: total,
95
- inputTokens: input,
96
- outputTokens: output,
97
- breakdown: {
98
- input,
99
- output,
100
- },
101
- };
102
- }
103
-
104
- async function getGeminiLegacySessionMessages(sessionFilePath: string): Promise<GeminiHistoryResult> {
105
- try {
106
- const data = await fs.readFile(sessionFilePath, 'utf8');
107
- const session = JSON.parse(data) as AnyRecord;
108
- const sourceMessages = Array.isArray(session.messages) ? session.messages : [];
109
-
110
- const messages: AnyRecord[] = [];
111
- for (const msg of sourceMessages) {
112
- const role = mapGeminiRole(msg.type ?? msg.role);
113
- if (!role) {
114
- continue;
115
- }
116
-
117
- messages.push({
118
- type: 'message',
119
- uuid: typeof msg.id === 'string' ? msg.id : undefined,
120
- message: { role, content: msg.content },
121
- timestamp: msg.timestamp || null,
122
- });
123
- }
124
-
125
- return { messages };
126
- } catch {
127
- return { messages: [] };
128
- }
129
- }
130
-
131
- async function getGeminiJsonlSessionMessages(sessionFilePath: string): Promise<GeminiHistoryResult> {
132
- const messages: AnyRecord[] = [];
133
- let tokenUsage: AnyRecord | undefined;
134
-
135
- try {
136
- const fileStream = fsSync.createReadStream(sessionFilePath);
137
- const lineReader = readline.createInterface({
138
- input: fileStream,
139
- crlfDelay: Infinity,
140
- });
141
-
142
- for await (const line of lineReader) {
143
- const trimmed = line.trim();
144
- if (!trimmed) {
145
- continue;
146
- }
147
-
148
- let entry: AnyRecord;
149
- try {
150
- entry = JSON.parse(trimmed) as AnyRecord;
151
- } catch {
152
- continue;
153
- }
154
-
155
- // Metadata/update lines (e.g. {$set:{lastUpdated:...}}) do not represent chat messages.
156
- if (entry.$set) {
157
- continue;
158
- }
159
-
160
- const role = mapGeminiRole(entry.type);
161
- if (role) {
162
- const textContent = extractGeminiTextContent(entry.content);
163
- if (textContent.trim()) {
164
- messages.push({
165
- type: 'message',
166
- uuid: typeof entry.id === 'string' ? entry.id : undefined,
167
- message: { role, content: textContent },
168
- timestamp: entry.timestamp || null,
169
- });
170
- }
171
-
172
- const thinkingContent = extractGeminiThoughts(entry.thoughts);
173
- if (thinkingContent.trim()) {
174
- messages.push({
175
- type: 'thinking',
176
- uuid: typeof entry.id === 'string' ? `${entry.id}_thinking` : undefined,
177
- message: { role: 'assistant', content: thinkingContent },
178
- timestamp: entry.timestamp || null,
179
- isReasoning: true,
180
- });
181
- }
182
-
183
- if (role === 'assistant') {
184
- const usage = buildGeminiTokenUsage(entry.tokens);
185
- if (usage) {
186
- tokenUsage = usage;
187
- }
188
- }
189
-
190
- continue;
191
- }
192
-
193
- if (entry.type === 'tool_use') {
194
- messages.push({
195
- type: 'tool_use',
196
- uuid: typeof entry.id === 'string' ? entry.id : undefined,
197
- timestamp: entry.timestamp || null,
198
- toolName: entry.tool_name || entry.name || 'Tool',
199
- toolInput: entry.parameters ?? entry.input ?? entry.arguments ?? '',
200
- toolCallId: entry.tool_id || entry.toolCallId || entry.id,
201
- });
202
- continue;
203
- }
204
-
205
- if (entry.type === 'tool_result') {
206
- messages.push({
207
- type: 'tool_result',
208
- uuid: typeof entry.id === 'string' ? entry.id : undefined,
209
- timestamp: entry.timestamp || null,
210
- toolCallId: entry.tool_id || entry.toolCallId || entry.id || '',
211
- output: entry.output ?? entry.result ?? '',
212
- isError: Boolean(entry.error) || entry.status === 'error',
213
- });
214
- }
215
- }
216
- } catch {
217
- return { messages: [] };
218
- }
219
-
220
- messages.sort(
221
- (a, b) => new Date(a.timestamp || 0).getTime() - new Date(b.timestamp || 0).getTime(),
222
- );
223
-
224
- return { messages, tokenUsage };
225
- }
226
-
227
- async function getGeminiCliSessionMessages(sessionId: string): Promise<GeminiHistoryResult> {
228
- const sessionFilePath = sessionsDb.getSessionById(sessionId)?.jsonl_path;
229
- if (!sessionFilePath) {
230
- return { messages: [] };
231
- }
232
-
233
- if (sessionFilePath.endsWith('.jsonl')) {
234
- return getGeminiJsonlSessionMessages(sessionFilePath);
235
- }
236
-
237
- return getGeminiLegacySessionMessages(sessionFilePath);
238
- }
239
-
240
- export class GeminiSessionsProvider implements IProviderSessions {
241
- /**
242
- * Normalizes live Gemini stream-json events into the shared message shape.
243
- *
244
- * Gemini history uses a different session file shape, so fetchHistory handles
245
- * that separately after loading raw persisted messages.
246
- */
247
- normalizeMessage(rawMessage: unknown, sessionId: string | null): NormalizedMessage[] {
248
- const raw = readObjectRecord(rawMessage);
249
- if (!raw) {
250
- return [];
251
- }
252
-
253
- const ts = raw.timestamp || new Date().toISOString();
254
- const baseId = raw.uuid || generateMessageId('gemini');
255
-
256
- if (raw.type === 'message' && raw.role === 'assistant') {
257
- const content = raw.content || '';
258
- const messages: NormalizedMessage[] = [];
259
- if (content) {
260
- messages.push(createNormalizedMessage({
261
- id: baseId,
262
- sessionId,
263
- timestamp: ts,
264
- provider: PROVIDER,
265
- kind: 'stream_delta',
266
- content,
267
- }));
268
- }
269
- if (raw.delta !== true) {
270
- messages.push(createNormalizedMessage({
271
- sessionId,
272
- timestamp: ts,
273
- provider: PROVIDER,
274
- kind: 'stream_end',
275
- }));
276
- }
277
- return messages;
278
- }
279
-
280
- if (raw.type === 'tool_use') {
281
- return [createNormalizedMessage({
282
- id: baseId,
283
- sessionId,
284
- timestamp: ts,
285
- provider: PROVIDER,
286
- kind: 'tool_use',
287
- toolName: raw.tool_name,
288
- toolInput: raw.parameters || {},
289
- toolId: raw.tool_id || baseId,
290
- })];
291
- }
292
-
293
- if (raw.type === 'tool_result') {
294
- return [createNormalizedMessage({
295
- id: baseId,
296
- sessionId,
297
- timestamp: ts,
298
- provider: PROVIDER,
299
- kind: 'tool_result',
300
- toolId: raw.tool_id || '',
301
- content: raw.output === undefined ? '' : String(raw.output),
302
- isError: raw.status === 'error',
303
- })];
304
- }
305
-
306
- if (raw.type === 'result') {
307
- const messages = [createNormalizedMessage({
308
- sessionId,
309
- timestamp: ts,
310
- provider: PROVIDER,
311
- kind: 'stream_end',
312
- })];
313
- if (raw.stats?.total_tokens) {
314
- messages.push(createNormalizedMessage({
315
- sessionId,
316
- timestamp: ts,
317
- provider: PROVIDER,
318
- kind: 'status',
319
- text: 'Complete',
320
- tokens: raw.stats.total_tokens,
321
- canInterrupt: false,
322
- }));
323
- }
324
- return messages;
325
- }
326
-
327
- if (raw.type === 'error') {
328
- return [createNormalizedMessage({
329
- id: baseId,
330
- sessionId,
331
- timestamp: ts,
332
- provider: PROVIDER,
333
- kind: 'error',
334
- content: raw.error || raw.message || 'Unknown Gemini streaming error',
335
- })];
336
- }
337
-
338
- return [];
339
- }
340
-
341
- /**
342
- * Loads Gemini history from Gemini CLI session files on disk.
343
- */
344
- async fetchHistory(
345
- sessionId: string,
346
- options: FetchHistoryOptions = {},
347
- ): Promise<FetchHistoryResult> {
348
- const { limit = null, offset = 0 } = options;
349
-
350
- let result: GeminiHistoryResult;
351
- try {
352
- result = await getGeminiCliSessionMessages(sessionId);
353
- } catch (error) {
354
- const message = error instanceof Error ? error.message : String(error);
355
- console.warn(`[GeminiProvider] Failed to load session ${sessionId}:`, message);
356
- return { messages: [], total: 0, hasMore: false, offset: 0, limit: null };
357
- }
358
-
359
- const rawMessages = result.messages;
360
- const normalized: NormalizedMessage[] = [];
361
-
362
- for (let i = 0; i < rawMessages.length; i++) {
363
- const raw = rawMessages[i];
364
- const ts = raw.timestamp || new Date().toISOString();
365
- const baseId = raw.uuid || generateMessageId('gemini');
366
-
367
- if (raw.type === 'thinking' || raw.isReasoning) {
368
- const thinkingContent = typeof raw.message?.content === 'string'
369
- ? raw.message.content
370
- : typeof raw.content === 'string'
371
- ? raw.content
372
- : '';
373
-
374
- if (thinkingContent.trim()) {
375
- normalized.push(createNormalizedMessage({
376
- id: baseId,
377
- sessionId,
378
- timestamp: ts,
379
- provider: PROVIDER,
380
- kind: 'thinking',
381
- content: thinkingContent,
382
- }));
383
- }
384
- continue;
385
- }
386
-
387
- if (raw.type === 'tool_use' || raw.toolName) {
388
- normalized.push(createNormalizedMessage({
389
- id: baseId,
390
- sessionId,
391
- timestamp: ts,
392
- provider: PROVIDER,
393
- kind: 'tool_use',
394
- toolName: raw.toolName || 'Tool',
395
- toolInput: raw.toolInput,
396
- toolId: raw.toolCallId || baseId,
397
- }));
398
- continue;
399
- }
400
-
401
- if (raw.type === 'tool_result') {
402
- normalized.push(createNormalizedMessage({
403
- id: baseId,
404
- sessionId,
405
- timestamp: ts,
406
- provider: PROVIDER,
407
- kind: 'tool_result',
408
- toolId: raw.toolCallId || '',
409
- content: raw.output === undefined ? '' : String(raw.output),
410
- isError: Boolean(raw.isError),
411
- }));
412
- continue;
413
- }
414
-
415
- const role = raw.message?.role || raw.role;
416
- const content = raw.message?.content || raw.content;
417
- if (!role || !content) {
418
- continue;
419
- }
420
-
421
- const normalizedRole = role === 'user' ? 'user' : 'assistant';
422
-
423
- if (Array.isArray(content)) {
424
- for (let partIdx = 0; partIdx < content.length; partIdx++) {
425
- const part = content[partIdx] as AnyRecord | string;
426
-
427
- if (typeof part === 'string' && part.trim()) {
428
- normalized.push(createNormalizedMessage({
429
- id: `${baseId}_${partIdx}`,
430
- sessionId,
431
- timestamp: ts,
432
- provider: PROVIDER,
433
- kind: 'text',
434
- role: normalizedRole,
435
- content: part,
436
- }));
437
- continue;
438
- }
439
-
440
- if (!part || typeof part !== 'object') {
441
- continue;
442
- }
443
-
444
- if ((part.type === 'text' || !part.type) && typeof part.text === 'string' && part.text.trim()) {
445
- normalized.push(createNormalizedMessage({
446
- id: `${baseId}_${partIdx}`,
447
- sessionId,
448
- timestamp: ts,
449
- provider: PROVIDER,
450
- kind: 'text',
451
- role: normalizedRole,
452
- content: part.text,
453
- }));
454
- } else if (part.type === 'tool_use') {
455
- normalized.push(createNormalizedMessage({
456
- id: `${baseId}_${partIdx}`,
457
- sessionId,
458
- timestamp: ts,
459
- provider: PROVIDER,
460
- kind: 'tool_use',
461
- toolName: part.name,
462
- toolInput: part.input,
463
- toolId: part.id || generateMessageId('gemini_tool'),
464
- }));
465
- } else if (part.type === 'tool_result') {
466
- normalized.push(createNormalizedMessage({
467
- id: `${baseId}_${partIdx}`,
468
- sessionId,
469
- timestamp: ts,
470
- provider: PROVIDER,
471
- kind: 'tool_result',
472
- toolId: part.tool_use_id || '',
473
- content: part.content === undefined ? '' : String(part.content),
474
- isError: Boolean(part.is_error),
475
- }));
476
- }
477
- }
478
- } else if (typeof content === 'string' && content.trim()) {
479
- normalized.push(createNormalizedMessage({
480
- id: baseId,
481
- sessionId,
482
- timestamp: ts,
483
- provider: PROVIDER,
484
- kind: 'text',
485
- role: normalizedRole,
486
- content,
487
- }));
488
- } else {
489
- const textContent = extractGeminiTextContent(content);
490
- if (textContent.trim()) {
491
- normalized.push(createNormalizedMessage({
492
- id: baseId,
493
- sessionId,
494
- timestamp: ts,
495
- provider: PROVIDER,
496
- kind: 'text',
497
- role: normalizedRole,
498
- content: textContent,
499
- }));
500
- }
501
- }
502
- }
503
-
504
- const toolResultMap = new Map<string, NormalizedMessage>();
505
- for (const msg of normalized) {
506
- if (msg.kind === 'tool_result' && msg.toolId) {
507
- toolResultMap.set(msg.toolId, msg);
508
- }
509
- }
510
- for (const msg of normalized) {
511
- if (msg.kind === 'tool_use' && msg.toolId && toolResultMap.has(msg.toolId)) {
512
- const toolResult = toolResultMap.get(msg.toolId);
513
- if (toolResult) {
514
- msg.toolResult = { content: toolResult.content, isError: toolResult.isError };
515
- }
516
- }
517
- }
518
-
519
- const start = Math.max(0, offset);
520
- const pageLimit = limit === null ? null : Math.max(0, limit);
521
- // Tail pagination via the shared contract: offset 0 returns the most
522
- // recent page, matching every other provider.
523
- const { page, hasMore } = sliceTailPage(normalized, pageLimit, start);
524
- let total = 0;
525
- for (const msg of normalized) {
526
- if (msg.kind !== 'tool_result') {
527
- total += 1;
528
- }
529
- }
530
-
531
- return {
532
- messages: page,
533
- total,
534
- hasMore,
535
- offset: start,
536
- limit: pageLimit,
537
- tokenUsage: result.tokenUsage,
538
- };
539
- }
540
- }
@@ -1,44 +0,0 @@
1
- import os from 'node:os';
2
- import path from 'node:path';
3
-
4
- import { SkillsProvider } from '@/modules/providers/shared/skills/skills.provider.js';
5
- import type { ProviderSkillSource } from '@/shared/types.js';
6
-
7
- export class GeminiSkillsProvider extends SkillsProvider {
8
- constructor() {
9
- super('gemini');
10
- }
11
-
12
- protected async getSkillSources(workspacePath: string): Promise<ProviderSkillSource[]> {
13
- return [
14
- {
15
- scope: 'user',
16
- rootDir: path.join(os.homedir(), '.gemini', 'skills'),
17
- commandPrefix: '/',
18
- },
19
- {
20
- scope: 'user',
21
- rootDir: path.join(os.homedir(), '.agents', 'skills'),
22
- commandPrefix: '/',
23
- },
24
- {
25
- scope: 'project',
26
- rootDir: path.join(workspacePath, '.gemini', 'skills'),
27
- commandPrefix: '/',
28
- },
29
- {
30
- scope: 'project',
31
- rootDir: path.join(workspacePath, '.agents', 'skills'),
32
- commandPrefix: '/',
33
- },
34
- ];
35
- }
36
-
37
- protected async getGlobalSkillSource(): Promise<ProviderSkillSource> {
38
- return {
39
- scope: 'user',
40
- rootDir: path.join(os.homedir(), '.gemini', 'skills'),
41
- commandPrefix: '/',
42
- };
43
- }
44
- }
@@ -1,27 +0,0 @@
1
- import { AbstractProvider } from '@/modules/providers/shared/base/abstract.provider.js';
2
- import { GeminiProviderAuth } from '@/modules/providers/list/gemini/gemini-auth.provider.js';
3
- import { GeminiProviderModels } from '@/modules/providers/list/gemini/gemini-models.provider.js';
4
- import { GeminiMcpProvider } from '@/modules/providers/list/gemini/gemini-mcp.provider.js';
5
- import { GeminiSessionSynchronizer } from '@/modules/providers/list/gemini/gemini-session-synchronizer.provider.js';
6
- import { GeminiSessionsProvider } from '@/modules/providers/list/gemini/gemini-sessions.provider.js';
7
- import { GeminiSkillsProvider } from '@/modules/providers/list/gemini/gemini-skills.provider.js';
8
- import type {
9
- IProviderAuth,
10
- IProviderModels,
11
- IProviderSessionSynchronizer,
12
- IProviderSkills,
13
- IProviderSessions,
14
- } from '@/shared/interfaces.js';
15
-
16
- export class GeminiProvider extends AbstractProvider {
17
- readonly models: IProviderModels = new GeminiProviderModels();
18
- readonly mcp = new GeminiMcpProvider();
19
- readonly auth: IProviderAuth = new GeminiProviderAuth();
20
- readonly skills: IProviderSkills = new GeminiSkillsProvider();
21
- readonly sessions: IProviderSessions = new GeminiSessionsProvider();
22
- readonly sessionSynchronizer: IProviderSessionSynchronizer = new GeminiSessionSynchronizer();
23
-
24
- constructor() {
25
- super('gemini');
26
- }
27
- }
@@ -1,25 +0,0 @@
1
- import express from 'express';
2
-
3
- import sessionManager from '../sessionManager.js';
4
- import { sessionsDb } from '../modules/database/index.js';
5
-
6
- const router = express.Router();
7
-
8
- router.delete('/sessions/:sessionId', async (req, res) => {
9
- try {
10
- const { sessionId } = req.params;
11
-
12
- if (!sessionId || typeof sessionId !== 'string' || !/^[a-zA-Z0-9_.-]{1,100}$/.test(sessionId)) {
13
- return res.status(400).json({ success: false, error: 'Invalid session ID format' });
14
- }
15
-
16
- await sessionManager.deleteSession(sessionId);
17
- sessionsDb.deleteSessionById(sessionId);
18
- res.json({ success: true });
19
- } catch (error) {
20
- console.error(`Error deleting Gemini session ${req.params.sessionId}:`, error);
21
- res.status(500).json({ success: false, error: error.message });
22
- }
23
- });
24
-
25
- export default router;