@pixelbyte-software/pixcode 1.30.2 → 1.31.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 (202) hide show
  1. package/LICENSE +718 -718
  2. package/README.de.md +248 -248
  3. package/README.ja.md +240 -240
  4. package/README.ko.md +240 -240
  5. package/README.md +295 -285
  6. package/README.ru.md +248 -248
  7. package/README.tr.md +250 -250
  8. package/README.zh-CN.md +240 -240
  9. package/dist/api-docs.html +879 -879
  10. package/dist/assets/index-BRRJ47XQ.css +32 -0
  11. package/dist/assets/index-EQohwyiC.js +837 -0
  12. package/dist/clear-cache.html +85 -85
  13. package/dist/convert-icons.md +52 -52
  14. package/dist/favicon.png +0 -0
  15. package/dist/favicon.svg +7 -8
  16. package/dist/generate-icons.js +48 -48
  17. package/dist/icons/codex-white.svg +3 -3
  18. package/dist/icons/codex.svg +3 -3
  19. package/dist/icons/cursor-white.svg +11 -11
  20. package/dist/icons/icon-128x128.png +0 -0
  21. package/dist/icons/icon-128x128.svg +9 -12
  22. package/dist/icons/icon-144x144.png +0 -0
  23. package/dist/icons/icon-144x144.svg +9 -12
  24. package/dist/icons/icon-152x152.png +0 -0
  25. package/dist/icons/icon-152x152.svg +9 -12
  26. package/dist/icons/icon-192x192.png +0 -0
  27. package/dist/icons/icon-192x192.svg +9 -12
  28. package/dist/icons/icon-384x384.png +0 -0
  29. package/dist/icons/icon-384x384.svg +9 -12
  30. package/dist/icons/icon-512x512.png +0 -0
  31. package/dist/icons/icon-512x512.svg +9 -12
  32. package/dist/icons/icon-72x72.png +0 -0
  33. package/dist/icons/icon-72x72.svg +9 -12
  34. package/dist/icons/icon-96x96.png +0 -0
  35. package/dist/icons/icon-96x96.svg +9 -12
  36. package/dist/icons/icon-template.svg +9 -12
  37. package/dist/icons/qwen-ai-icon.png +0 -0
  38. package/dist/index.html +59 -49
  39. package/dist/logo.png +0 -0
  40. package/dist/logo.svg +11 -16
  41. package/dist/manifest.json +60 -60
  42. package/dist/sw.js +124 -124
  43. package/dist-server/server/cli.js +100 -97
  44. package/dist-server/server/cli.js.map +1 -1
  45. package/dist-server/server/daemon/manager.js +33 -33
  46. package/dist-server/server/daemon-manager.js +62 -62
  47. package/dist-server/server/database/db.js +114 -22
  48. package/dist-server/server/database/db.js.map +1 -1
  49. package/dist-server/server/database/schema.js +122 -89
  50. package/dist-server/server/database/schema.js.map +1 -1
  51. package/dist-server/server/gemini-cli.js +6 -1
  52. package/dist-server/server/gemini-cli.js.map +1 -1
  53. package/dist-server/server/index.js +234 -64
  54. package/dist-server/server/index.js.map +1 -1
  55. package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js +29 -2
  56. package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js.map +1 -1
  57. package/dist-server/server/modules/providers/list/codex/codex-auth.provider.js +22 -2
  58. package/dist-server/server/modules/providers/list/codex/codex-auth.provider.js.map +1 -1
  59. package/dist-server/server/modules/providers/list/cursor/cursor-auth.provider.js +2 -2
  60. package/dist-server/server/modules/providers/list/cursor/cursor-auth.provider.js.map +1 -1
  61. package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js +14 -2
  62. package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js.map +1 -1
  63. package/dist-server/server/modules/providers/list/qwen/qwen-auth.provider.js +132 -0
  64. package/dist-server/server/modules/providers/list/qwen/qwen-auth.provider.js.map +1 -0
  65. package/dist-server/server/modules/providers/list/qwen/qwen-mcp.provider.js +87 -0
  66. package/dist-server/server/modules/providers/list/qwen/qwen-mcp.provider.js.map +1 -0
  67. package/dist-server/server/modules/providers/list/qwen/qwen-sessions.provider.js +201 -0
  68. package/dist-server/server/modules/providers/list/qwen/qwen-sessions.provider.js.map +1 -0
  69. package/dist-server/server/modules/providers/list/qwen/qwen.provider.js +19 -0
  70. package/dist-server/server/modules/providers/list/qwen/qwen.provider.js.map +1 -0
  71. package/dist-server/server/modules/providers/provider.registry.js +2 -0
  72. package/dist-server/server/modules/providers/provider.registry.js.map +1 -1
  73. package/dist-server/server/modules/providers/provider.routes.js +310 -1
  74. package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
  75. package/dist-server/server/projects.js +197 -6
  76. package/dist-server/server/projects.js.map +1 -1
  77. package/dist-server/server/qwen-code-cli.js +350 -0
  78. package/dist-server/server/qwen-code-cli.js.map +1 -0
  79. package/dist-server/server/qwen-response-handler.js +70 -0
  80. package/dist-server/server/qwen-response-handler.js.map +1 -0
  81. package/dist-server/server/routes/commands.js +25 -25
  82. package/dist-server/server/routes/git.js +17 -17
  83. package/dist-server/server/routes/network.js +116 -0
  84. package/dist-server/server/routes/network.js.map +1 -0
  85. package/dist-server/server/routes/projects.js +43 -0
  86. package/dist-server/server/routes/projects.js.map +1 -1
  87. package/dist-server/server/routes/qwen.js +23 -0
  88. package/dist-server/server/routes/qwen.js.map +1 -0
  89. package/dist-server/server/routes/taskmaster.js +419 -419
  90. package/dist-server/server/routes/telegram.js +119 -0
  91. package/dist-server/server/routes/telegram.js.map +1 -0
  92. package/dist-server/server/services/external-access.js +228 -0
  93. package/dist-server/server/services/external-access.js.map +1 -0
  94. package/dist-server/server/services/install-jobs.js +394 -0
  95. package/dist-server/server/services/install-jobs.js.map +1 -0
  96. package/dist-server/server/services/notification-orchestrator.js +19 -5
  97. package/dist-server/server/services/notification-orchestrator.js.map +1 -1
  98. package/dist-server/server/services/provider-credentials.js +154 -0
  99. package/dist-server/server/services/provider-credentials.js.map +1 -0
  100. package/dist-server/server/services/provider-models.js +218 -0
  101. package/dist-server/server/services/provider-models.js.map +1 -0
  102. package/dist-server/server/services/telegram/bot.js +259 -0
  103. package/dist-server/server/services/telegram/bot.js.map +1 -0
  104. package/dist-server/server/services/telegram/translations.js +160 -0
  105. package/dist-server/server/services/telegram/translations.js.map +1 -0
  106. package/dist-server/server/utils/port-access.js +196 -0
  107. package/dist-server/server/utils/port-access.js.map +1 -0
  108. package/dist-server/shared/modelConstants.js +18 -0
  109. package/dist-server/shared/modelConstants.js.map +1 -1
  110. package/package.json +177 -168
  111. package/scripts/fix-node-pty.js +67 -67
  112. package/server/claude-sdk.js +834 -834
  113. package/server/cli.js +940 -937
  114. package/server/constants/config.js +4 -4
  115. package/server/cursor-cli.js +342 -342
  116. package/server/daemon/manager.js +564 -564
  117. package/server/daemon-manager.js +920 -920
  118. package/server/database/db.js +696 -593
  119. package/server/database/schema.js +138 -102
  120. package/server/gemini-cli.js +475 -469
  121. package/server/gemini-response-handler.js +79 -79
  122. package/server/index.js +2730 -2556
  123. package/server/load-env.js +34 -34
  124. package/server/middleware/auth.js +132 -132
  125. package/server/modules/providers/list/claude/claude-auth.provider.ts +145 -123
  126. package/server/modules/providers/list/claude/claude-mcp.provider.ts +135 -135
  127. package/server/modules/providers/list/claude/claude-sessions.provider.ts +306 -306
  128. package/server/modules/providers/list/claude/claude.provider.ts +15 -15
  129. package/server/modules/providers/list/codex/codex-auth.provider.ts +115 -100
  130. package/server/modules/providers/list/codex/codex-mcp.provider.ts +135 -135
  131. package/server/modules/providers/list/codex/codex-sessions.provider.ts +319 -319
  132. package/server/modules/providers/list/codex/codex.provider.ts +15 -15
  133. package/server/modules/providers/list/cursor/cursor-auth.provider.ts +143 -143
  134. package/server/modules/providers/list/cursor/cursor-mcp.provider.ts +108 -108
  135. package/server/modules/providers/list/cursor/cursor-sessions.provider.ts +421 -421
  136. package/server/modules/providers/list/cursor/cursor.provider.ts +15 -15
  137. package/server/modules/providers/list/gemini/gemini-auth.provider.ts +163 -151
  138. package/server/modules/providers/list/gemini/gemini-mcp.provider.ts +110 -110
  139. package/server/modules/providers/list/gemini/gemini-sessions.provider.ts +227 -227
  140. package/server/modules/providers/list/gemini/gemini.provider.ts +15 -15
  141. package/server/modules/providers/list/qwen/qwen-auth.provider.ts +145 -0
  142. package/server/modules/providers/list/qwen/qwen-mcp.provider.ts +114 -0
  143. package/server/modules/providers/list/qwen/qwen-sessions.provider.ts +218 -0
  144. package/server/modules/providers/list/qwen/qwen.provider.ts +21 -0
  145. package/server/modules/providers/provider.registry.ts +38 -36
  146. package/server/modules/providers/provider.routes.ts +583 -217
  147. package/server/modules/providers/services/mcp.service.ts +94 -94
  148. package/server/modules/providers/services/provider-auth.service.ts +26 -26
  149. package/server/modules/providers/services/sessions.service.ts +45 -45
  150. package/server/modules/providers/shared/base/abstract.provider.ts +20 -20
  151. package/server/modules/providers/shared/mcp/mcp.provider.ts +151 -151
  152. package/server/modules/providers/tests/mcp.test.ts +293 -293
  153. package/server/openai-codex.js +426 -426
  154. package/server/projects.js +2993 -2792
  155. package/server/qwen-code-cli.js +392 -0
  156. package/server/qwen-response-handler.js +73 -0
  157. package/server/routes/agent.js +1245 -1245
  158. package/server/routes/auth.js +134 -134
  159. package/server/routes/codex.js +19 -19
  160. package/server/routes/commands.js +554 -554
  161. package/server/routes/cursor.js +52 -52
  162. package/server/routes/gemini.js +24 -24
  163. package/server/routes/git.js +1488 -1488
  164. package/server/routes/mcp-utils.js +31 -31
  165. package/server/routes/messages.js +61 -61
  166. package/server/routes/network.js +128 -0
  167. package/server/routes/plugins.js +307 -307
  168. package/server/routes/projects.js +675 -627
  169. package/server/routes/qwen.js +27 -0
  170. package/server/routes/settings.js +286 -286
  171. package/server/routes/taskmaster.js +1471 -1471
  172. package/server/routes/telegram.js +125 -0
  173. package/server/routes/user.js +123 -123
  174. package/server/services/external-access.js +240 -0
  175. package/server/services/install-jobs.js +410 -0
  176. package/server/services/notification-orchestrator.js +242 -227
  177. package/server/services/provider-credentials.js +151 -0
  178. package/server/services/provider-models.js +225 -0
  179. package/server/services/telegram/bot.js +280 -0
  180. package/server/services/telegram/translations.js +170 -0
  181. package/server/services/vapid-keys.js +35 -35
  182. package/server/sessionManager.js +225 -225
  183. package/server/shared/interfaces.ts +54 -54
  184. package/server/shared/types.ts +172 -172
  185. package/server/shared/utils.ts +193 -193
  186. package/server/tsconfig.json +36 -36
  187. package/server/utils/colors.js +21 -21
  188. package/server/utils/commandParser.js +303 -303
  189. package/server/utils/frontmatter.js +18 -18
  190. package/server/utils/gitConfig.js +34 -34
  191. package/server/utils/mcp-detector.js +147 -147
  192. package/server/utils/plugin-loader.js +457 -457
  193. package/server/utils/plugin-process-manager.js +184 -184
  194. package/server/utils/port-access.js +209 -0
  195. package/server/utils/runtime-paths.js +37 -37
  196. package/server/utils/taskmaster-websocket.js +128 -128
  197. package/server/utils/url-detection.js +71 -71
  198. package/server/vite-daemon.js +78 -78
  199. package/shared/modelConstants.js +117 -97
  200. package/shared/networkHosts.js +22 -22
  201. package/dist/assets/index-C2c9QNwK.css +0 -32
  202. package/dist/assets/index-DyXDZED-.js +0 -1277
@@ -1,306 +1,306 @@
1
- import { getSessionMessages } from '@/projects.js';
2
- import type { IProviderSessions } from '@/shared/interfaces.js';
3
- import type { AnyRecord, FetchHistoryOptions, FetchHistoryResult, NormalizedMessage } from '@/shared/types.js';
4
- import { createNormalizedMessage, generateMessageId, readObjectRecord } from '@/shared/utils.js';
5
-
6
- const PROVIDER = 'claude';
7
-
8
- type ClaudeToolResult = {
9
- content: unknown;
10
- isError: boolean;
11
- subagentTools?: unknown;
12
- toolUseResult?: unknown;
13
- };
14
-
15
- type ClaudeHistoryResult =
16
- | AnyRecord[]
17
- | {
18
- messages?: AnyRecord[];
19
- total?: number;
20
- hasMore?: boolean;
21
- };
22
-
23
- const loadClaudeSessionMessages = getSessionMessages as unknown as (
24
- projectName: string,
25
- sessionId: string,
26
- limit: number | null,
27
- offset: number,
28
- ) => Promise<ClaudeHistoryResult>;
29
-
30
- /**
31
- * Claude writes internal command and system reminder entries into history.
32
- * Those are useful for the CLI but should not appear in the user-facing chat.
33
- */
34
- const INTERNAL_CONTENT_PREFIXES = [
35
- '<command-name>',
36
- '<command-message>',
37
- '<command-args>',
38
- '<local-command-stdout>',
39
- '<system-reminder>',
40
- 'Caveat:',
41
- 'This session is being continued from a previous',
42
- '[Request interrupted',
43
- ] as const;
44
-
45
- function isInternalContent(content: string): boolean {
46
- return INTERNAL_CONTENT_PREFIXES.some((prefix) => content.startsWith(prefix));
47
- }
48
-
49
- export class ClaudeSessionsProvider implements IProviderSessions {
50
- /**
51
- * Normalizes one Claude JSONL entry or live SDK stream event into the shared
52
- * message shape consumed by REST and WebSocket clients.
53
- */
54
- normalizeMessage(rawMessage: unknown, sessionId: string | null): NormalizedMessage[] {
55
- const raw = readObjectRecord(rawMessage);
56
- if (!raw) {
57
- return [];
58
- }
59
-
60
- if (raw.type === 'content_block_delta' && raw.delta?.text) {
61
- return [createNormalizedMessage({ kind: 'stream_delta', content: raw.delta.text, sessionId, provider: PROVIDER })];
62
- }
63
- if (raw.type === 'content_block_stop') {
64
- return [createNormalizedMessage({ kind: 'stream_end', sessionId, provider: PROVIDER })];
65
- }
66
-
67
- const messages: NormalizedMessage[] = [];
68
- const ts = raw.timestamp || new Date().toISOString();
69
- const baseId = raw.uuid || generateMessageId('claude');
70
-
71
- if (raw.message?.role === 'user' && raw.message?.content) {
72
- if (Array.isArray(raw.message.content)) {
73
- for (let partIndex = 0; partIndex < raw.message.content.length; partIndex++) {
74
- const part = raw.message.content[partIndex];
75
- if (part.type === 'tool_result') {
76
- messages.push(createNormalizedMessage({
77
- id: `${baseId}_tr_${part.tool_use_id}`,
78
- sessionId,
79
- timestamp: ts,
80
- provider: PROVIDER,
81
- kind: 'tool_result',
82
- toolId: part.tool_use_id,
83
- content: typeof part.content === 'string' ? part.content : JSON.stringify(part.content),
84
- isError: Boolean(part.is_error),
85
- subagentTools: raw.subagentTools,
86
- toolUseResult: raw.toolUseResult,
87
- }));
88
- } else if (part.type === 'text') {
89
- const text = part.text || '';
90
- if (text && !isInternalContent(text)) {
91
- messages.push(createNormalizedMessage({
92
- id: `${baseId}_text_${partIndex}`,
93
- sessionId,
94
- timestamp: ts,
95
- provider: PROVIDER,
96
- kind: 'text',
97
- role: 'user',
98
- content: text,
99
- }));
100
- }
101
- }
102
- }
103
-
104
- if (messages.length === 0) {
105
- const textParts = raw.message.content
106
- .filter((part: AnyRecord) => part.type === 'text')
107
- .map((part: AnyRecord) => part.text)
108
- .filter(Boolean)
109
- .join('\n');
110
- if (textParts && !isInternalContent(textParts)) {
111
- messages.push(createNormalizedMessage({
112
- id: `${baseId}_text`,
113
- sessionId,
114
- timestamp: ts,
115
- provider: PROVIDER,
116
- kind: 'text',
117
- role: 'user',
118
- content: textParts,
119
- }));
120
- }
121
- }
122
- } else if (typeof raw.message.content === 'string') {
123
- const text = raw.message.content;
124
- if (text && !isInternalContent(text)) {
125
- messages.push(createNormalizedMessage({
126
- id: baseId,
127
- sessionId,
128
- timestamp: ts,
129
- provider: PROVIDER,
130
- kind: 'text',
131
- role: 'user',
132
- content: text,
133
- }));
134
- }
135
- }
136
- return messages;
137
- }
138
-
139
- if (raw.type === 'thinking' && raw.message?.content) {
140
- messages.push(createNormalizedMessage({
141
- id: baseId,
142
- sessionId,
143
- timestamp: ts,
144
- provider: PROVIDER,
145
- kind: 'thinking',
146
- content: raw.message.content,
147
- }));
148
- return messages;
149
- }
150
-
151
- if (raw.type === 'tool_use' && raw.toolName) {
152
- messages.push(createNormalizedMessage({
153
- id: baseId,
154
- sessionId,
155
- timestamp: ts,
156
- provider: PROVIDER,
157
- kind: 'tool_use',
158
- toolName: raw.toolName,
159
- toolInput: raw.toolInput,
160
- toolId: raw.toolCallId || baseId,
161
- }));
162
- return messages;
163
- }
164
-
165
- if (raw.type === 'tool_result') {
166
- messages.push(createNormalizedMessage({
167
- id: baseId,
168
- sessionId,
169
- timestamp: ts,
170
- provider: PROVIDER,
171
- kind: 'tool_result',
172
- toolId: raw.toolCallId || '',
173
- content: raw.output || '',
174
- isError: false,
175
- }));
176
- return messages;
177
- }
178
-
179
- if (raw.message?.role === 'assistant' && raw.message?.content) {
180
- if (Array.isArray(raw.message.content)) {
181
- let partIndex = 0;
182
- for (const part of raw.message.content) {
183
- if (part.type === 'text' && part.text) {
184
- messages.push(createNormalizedMessage({
185
- id: `${baseId}_${partIndex}`,
186
- sessionId,
187
- timestamp: ts,
188
- provider: PROVIDER,
189
- kind: 'text',
190
- role: 'assistant',
191
- content: part.text,
192
- }));
193
- } else if (part.type === 'tool_use') {
194
- messages.push(createNormalizedMessage({
195
- id: `${baseId}_${partIndex}`,
196
- sessionId,
197
- timestamp: ts,
198
- provider: PROVIDER,
199
- kind: 'tool_use',
200
- toolName: part.name,
201
- toolInput: part.input,
202
- toolId: part.id,
203
- }));
204
- } else if (part.type === 'thinking' && part.thinking) {
205
- messages.push(createNormalizedMessage({
206
- id: `${baseId}_${partIndex}`,
207
- sessionId,
208
- timestamp: ts,
209
- provider: PROVIDER,
210
- kind: 'thinking',
211
- content: part.thinking,
212
- }));
213
- }
214
- partIndex++;
215
- }
216
- } else if (typeof raw.message.content === 'string') {
217
- messages.push(createNormalizedMessage({
218
- id: baseId,
219
- sessionId,
220
- timestamp: ts,
221
- provider: PROVIDER,
222
- kind: 'text',
223
- role: 'assistant',
224
- content: raw.message.content,
225
- }));
226
- }
227
- return messages;
228
- }
229
-
230
- return messages;
231
- }
232
-
233
- /**
234
- * Loads Claude JSONL history for a project/session and returns normalized
235
- * messages, preserving the existing pagination behavior from projects.js.
236
- */
237
- async fetchHistory(
238
- sessionId: string,
239
- options: FetchHistoryOptions = {},
240
- ): Promise<FetchHistoryResult> {
241
- const { projectName, limit = null, offset = 0 } = options;
242
- if (!projectName) {
243
- return { messages: [], total: 0, hasMore: false, offset: 0, limit: null };
244
- }
245
-
246
- let result: ClaudeHistoryResult;
247
- try {
248
- result = await loadClaudeSessionMessages(projectName, sessionId, limit, offset);
249
- } catch (error) {
250
- const message = error instanceof Error ? error.message : String(error);
251
- console.warn(`[ClaudeProvider] Failed to load session ${sessionId}:`, message);
252
- return { messages: [], total: 0, hasMore: false, offset: 0, limit: null };
253
- }
254
-
255
- const rawMessages = Array.isArray(result) ? result : (result.messages || []);
256
- const total = Array.isArray(result) ? rawMessages.length : (result.total || 0);
257
- const hasMore = Array.isArray(result) ? false : Boolean(result.hasMore);
258
-
259
- const toolResultMap = new Map<string, ClaudeToolResult>();
260
- for (const raw of rawMessages) {
261
- if (raw.message?.role === 'user' && Array.isArray(raw.message?.content)) {
262
- for (const part of raw.message.content) {
263
- if (part.type === 'tool_result' && part.tool_use_id) {
264
- toolResultMap.set(part.tool_use_id, {
265
- content: part.content,
266
- isError: Boolean(part.is_error),
267
- subagentTools: raw.subagentTools,
268
- toolUseResult: raw.toolUseResult,
269
- });
270
- }
271
- }
272
- }
273
- }
274
-
275
- const normalized: NormalizedMessage[] = [];
276
- for (const raw of rawMessages) {
277
- normalized.push(...this.normalizeMessage(raw, sessionId));
278
- }
279
-
280
- for (const msg of normalized) {
281
- if (msg.kind === 'tool_use' && msg.toolId && toolResultMap.has(msg.toolId)) {
282
- const toolResult = toolResultMap.get(msg.toolId);
283
- if (!toolResult) {
284
- continue;
285
- }
286
-
287
- msg.toolResult = {
288
- content: typeof toolResult.content === 'string'
289
- ? toolResult.content
290
- : JSON.stringify(toolResult.content),
291
- isError: toolResult.isError,
292
- toolUseResult: toolResult.toolUseResult,
293
- };
294
- msg.subagentTools = toolResult.subagentTools;
295
- }
296
- }
297
-
298
- return {
299
- messages: normalized,
300
- total,
301
- hasMore,
302
- offset,
303
- limit,
304
- };
305
- }
306
- }
1
+ import { getSessionMessages } from '@/projects.js';
2
+ import type { IProviderSessions } from '@/shared/interfaces.js';
3
+ import type { AnyRecord, FetchHistoryOptions, FetchHistoryResult, NormalizedMessage } from '@/shared/types.js';
4
+ import { createNormalizedMessage, generateMessageId, readObjectRecord } from '@/shared/utils.js';
5
+
6
+ const PROVIDER = 'claude';
7
+
8
+ type ClaudeToolResult = {
9
+ content: unknown;
10
+ isError: boolean;
11
+ subagentTools?: unknown;
12
+ toolUseResult?: unknown;
13
+ };
14
+
15
+ type ClaudeHistoryResult =
16
+ | AnyRecord[]
17
+ | {
18
+ messages?: AnyRecord[];
19
+ total?: number;
20
+ hasMore?: boolean;
21
+ };
22
+
23
+ const loadClaudeSessionMessages = getSessionMessages as unknown as (
24
+ projectName: string,
25
+ sessionId: string,
26
+ limit: number | null,
27
+ offset: number,
28
+ ) => Promise<ClaudeHistoryResult>;
29
+
30
+ /**
31
+ * Claude writes internal command and system reminder entries into history.
32
+ * Those are useful for the CLI but should not appear in the user-facing chat.
33
+ */
34
+ const INTERNAL_CONTENT_PREFIXES = [
35
+ '<command-name>',
36
+ '<command-message>',
37
+ '<command-args>',
38
+ '<local-command-stdout>',
39
+ '<system-reminder>',
40
+ 'Caveat:',
41
+ 'This session is being continued from a previous',
42
+ '[Request interrupted',
43
+ ] as const;
44
+
45
+ function isInternalContent(content: string): boolean {
46
+ return INTERNAL_CONTENT_PREFIXES.some((prefix) => content.startsWith(prefix));
47
+ }
48
+
49
+ export class ClaudeSessionsProvider implements IProviderSessions {
50
+ /**
51
+ * Normalizes one Claude JSONL entry or live SDK stream event into the shared
52
+ * message shape consumed by REST and WebSocket clients.
53
+ */
54
+ normalizeMessage(rawMessage: unknown, sessionId: string | null): NormalizedMessage[] {
55
+ const raw = readObjectRecord(rawMessage);
56
+ if (!raw) {
57
+ return [];
58
+ }
59
+
60
+ if (raw.type === 'content_block_delta' && raw.delta?.text) {
61
+ return [createNormalizedMessage({ kind: 'stream_delta', content: raw.delta.text, sessionId, provider: PROVIDER })];
62
+ }
63
+ if (raw.type === 'content_block_stop') {
64
+ return [createNormalizedMessage({ kind: 'stream_end', sessionId, provider: PROVIDER })];
65
+ }
66
+
67
+ const messages: NormalizedMessage[] = [];
68
+ const ts = raw.timestamp || new Date().toISOString();
69
+ const baseId = raw.uuid || generateMessageId('claude');
70
+
71
+ if (raw.message?.role === 'user' && raw.message?.content) {
72
+ if (Array.isArray(raw.message.content)) {
73
+ for (let partIndex = 0; partIndex < raw.message.content.length; partIndex++) {
74
+ const part = raw.message.content[partIndex];
75
+ if (part.type === 'tool_result') {
76
+ messages.push(createNormalizedMessage({
77
+ id: `${baseId}_tr_${part.tool_use_id}`,
78
+ sessionId,
79
+ timestamp: ts,
80
+ provider: PROVIDER,
81
+ kind: 'tool_result',
82
+ toolId: part.tool_use_id,
83
+ content: typeof part.content === 'string' ? part.content : JSON.stringify(part.content),
84
+ isError: Boolean(part.is_error),
85
+ subagentTools: raw.subagentTools,
86
+ toolUseResult: raw.toolUseResult,
87
+ }));
88
+ } else if (part.type === 'text') {
89
+ const text = part.text || '';
90
+ if (text && !isInternalContent(text)) {
91
+ messages.push(createNormalizedMessage({
92
+ id: `${baseId}_text_${partIndex}`,
93
+ sessionId,
94
+ timestamp: ts,
95
+ provider: PROVIDER,
96
+ kind: 'text',
97
+ role: 'user',
98
+ content: text,
99
+ }));
100
+ }
101
+ }
102
+ }
103
+
104
+ if (messages.length === 0) {
105
+ const textParts = raw.message.content
106
+ .filter((part: AnyRecord) => part.type === 'text')
107
+ .map((part: AnyRecord) => part.text)
108
+ .filter(Boolean)
109
+ .join('\n');
110
+ if (textParts && !isInternalContent(textParts)) {
111
+ messages.push(createNormalizedMessage({
112
+ id: `${baseId}_text`,
113
+ sessionId,
114
+ timestamp: ts,
115
+ provider: PROVIDER,
116
+ kind: 'text',
117
+ role: 'user',
118
+ content: textParts,
119
+ }));
120
+ }
121
+ }
122
+ } else if (typeof raw.message.content === 'string') {
123
+ const text = raw.message.content;
124
+ if (text && !isInternalContent(text)) {
125
+ messages.push(createNormalizedMessage({
126
+ id: baseId,
127
+ sessionId,
128
+ timestamp: ts,
129
+ provider: PROVIDER,
130
+ kind: 'text',
131
+ role: 'user',
132
+ content: text,
133
+ }));
134
+ }
135
+ }
136
+ return messages;
137
+ }
138
+
139
+ if (raw.type === 'thinking' && raw.message?.content) {
140
+ messages.push(createNormalizedMessage({
141
+ id: baseId,
142
+ sessionId,
143
+ timestamp: ts,
144
+ provider: PROVIDER,
145
+ kind: 'thinking',
146
+ content: raw.message.content,
147
+ }));
148
+ return messages;
149
+ }
150
+
151
+ if (raw.type === 'tool_use' && raw.toolName) {
152
+ messages.push(createNormalizedMessage({
153
+ id: baseId,
154
+ sessionId,
155
+ timestamp: ts,
156
+ provider: PROVIDER,
157
+ kind: 'tool_use',
158
+ toolName: raw.toolName,
159
+ toolInput: raw.toolInput,
160
+ toolId: raw.toolCallId || baseId,
161
+ }));
162
+ return messages;
163
+ }
164
+
165
+ if (raw.type === 'tool_result') {
166
+ messages.push(createNormalizedMessage({
167
+ id: baseId,
168
+ sessionId,
169
+ timestamp: ts,
170
+ provider: PROVIDER,
171
+ kind: 'tool_result',
172
+ toolId: raw.toolCallId || '',
173
+ content: raw.output || '',
174
+ isError: false,
175
+ }));
176
+ return messages;
177
+ }
178
+
179
+ if (raw.message?.role === 'assistant' && raw.message?.content) {
180
+ if (Array.isArray(raw.message.content)) {
181
+ let partIndex = 0;
182
+ for (const part of raw.message.content) {
183
+ if (part.type === 'text' && part.text) {
184
+ messages.push(createNormalizedMessage({
185
+ id: `${baseId}_${partIndex}`,
186
+ sessionId,
187
+ timestamp: ts,
188
+ provider: PROVIDER,
189
+ kind: 'text',
190
+ role: 'assistant',
191
+ content: part.text,
192
+ }));
193
+ } else if (part.type === 'tool_use') {
194
+ messages.push(createNormalizedMessage({
195
+ id: `${baseId}_${partIndex}`,
196
+ sessionId,
197
+ timestamp: ts,
198
+ provider: PROVIDER,
199
+ kind: 'tool_use',
200
+ toolName: part.name,
201
+ toolInput: part.input,
202
+ toolId: part.id,
203
+ }));
204
+ } else if (part.type === 'thinking' && part.thinking) {
205
+ messages.push(createNormalizedMessage({
206
+ id: `${baseId}_${partIndex}`,
207
+ sessionId,
208
+ timestamp: ts,
209
+ provider: PROVIDER,
210
+ kind: 'thinking',
211
+ content: part.thinking,
212
+ }));
213
+ }
214
+ partIndex++;
215
+ }
216
+ } else if (typeof raw.message.content === 'string') {
217
+ messages.push(createNormalizedMessage({
218
+ id: baseId,
219
+ sessionId,
220
+ timestamp: ts,
221
+ provider: PROVIDER,
222
+ kind: 'text',
223
+ role: 'assistant',
224
+ content: raw.message.content,
225
+ }));
226
+ }
227
+ return messages;
228
+ }
229
+
230
+ return messages;
231
+ }
232
+
233
+ /**
234
+ * Loads Claude JSONL history for a project/session and returns normalized
235
+ * messages, preserving the existing pagination behavior from projects.js.
236
+ */
237
+ async fetchHistory(
238
+ sessionId: string,
239
+ options: FetchHistoryOptions = {},
240
+ ): Promise<FetchHistoryResult> {
241
+ const { projectName, limit = null, offset = 0 } = options;
242
+ if (!projectName) {
243
+ return { messages: [], total: 0, hasMore: false, offset: 0, limit: null };
244
+ }
245
+
246
+ let result: ClaudeHistoryResult;
247
+ try {
248
+ result = await loadClaudeSessionMessages(projectName, sessionId, limit, offset);
249
+ } catch (error) {
250
+ const message = error instanceof Error ? error.message : String(error);
251
+ console.warn(`[ClaudeProvider] Failed to load session ${sessionId}:`, message);
252
+ return { messages: [], total: 0, hasMore: false, offset: 0, limit: null };
253
+ }
254
+
255
+ const rawMessages = Array.isArray(result) ? result : (result.messages || []);
256
+ const total = Array.isArray(result) ? rawMessages.length : (result.total || 0);
257
+ const hasMore = Array.isArray(result) ? false : Boolean(result.hasMore);
258
+
259
+ const toolResultMap = new Map<string, ClaudeToolResult>();
260
+ for (const raw of rawMessages) {
261
+ if (raw.message?.role === 'user' && Array.isArray(raw.message?.content)) {
262
+ for (const part of raw.message.content) {
263
+ if (part.type === 'tool_result' && part.tool_use_id) {
264
+ toolResultMap.set(part.tool_use_id, {
265
+ content: part.content,
266
+ isError: Boolean(part.is_error),
267
+ subagentTools: raw.subagentTools,
268
+ toolUseResult: raw.toolUseResult,
269
+ });
270
+ }
271
+ }
272
+ }
273
+ }
274
+
275
+ const normalized: NormalizedMessage[] = [];
276
+ for (const raw of rawMessages) {
277
+ normalized.push(...this.normalizeMessage(raw, sessionId));
278
+ }
279
+
280
+ for (const msg of normalized) {
281
+ if (msg.kind === 'tool_use' && msg.toolId && toolResultMap.has(msg.toolId)) {
282
+ const toolResult = toolResultMap.get(msg.toolId);
283
+ if (!toolResult) {
284
+ continue;
285
+ }
286
+
287
+ msg.toolResult = {
288
+ content: typeof toolResult.content === 'string'
289
+ ? toolResult.content
290
+ : JSON.stringify(toolResult.content),
291
+ isError: toolResult.isError,
292
+ toolUseResult: toolResult.toolUseResult,
293
+ };
294
+ msg.subagentTools = toolResult.subagentTools;
295
+ }
296
+ }
297
+
298
+ return {
299
+ messages: normalized,
300
+ total,
301
+ hasMore,
302
+ offset,
303
+ limit,
304
+ };
305
+ }
306
+ }
@@ -1,15 +1,15 @@
1
- import { AbstractProvider } from '@/modules/providers/shared/base/abstract.provider.js';
2
- import { ClaudeProviderAuth } from '@/modules/providers/list/claude/claude-auth.provider.js';
3
- import { ClaudeMcpProvider } from '@/modules/providers/list/claude/claude-mcp.provider.js';
4
- import { ClaudeSessionsProvider } from '@/modules/providers/list/claude/claude-sessions.provider.js';
5
- import type { IProviderAuth, IProviderSessions } from '@/shared/interfaces.js';
6
-
7
- export class ClaudeProvider extends AbstractProvider {
8
- readonly mcp = new ClaudeMcpProvider();
9
- readonly auth: IProviderAuth = new ClaudeProviderAuth();
10
- readonly sessions: IProviderSessions = new ClaudeSessionsProvider();
11
-
12
- constructor() {
13
- super('claude');
14
- }
15
- }
1
+ import { AbstractProvider } from '@/modules/providers/shared/base/abstract.provider.js';
2
+ import { ClaudeProviderAuth } from '@/modules/providers/list/claude/claude-auth.provider.js';
3
+ import { ClaudeMcpProvider } from '@/modules/providers/list/claude/claude-mcp.provider.js';
4
+ import { ClaudeSessionsProvider } from '@/modules/providers/list/claude/claude-sessions.provider.js';
5
+ import type { IProviderAuth, IProviderSessions } from '@/shared/interfaces.js';
6
+
7
+ export class ClaudeProvider extends AbstractProvider {
8
+ readonly mcp = new ClaudeMcpProvider();
9
+ readonly auth: IProviderAuth = new ClaudeProviderAuth();
10
+ readonly sessions: IProviderSessions = new ClaudeSessionsProvider();
11
+
12
+ constructor() {
13
+ super('claude');
14
+ }
15
+ }