@siteboon/claude-code-ui 1.25.2 → 1.26.2
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.
- package/README.de.md +239 -0
- package/README.ja.md +115 -230
- package/README.ko.md +116 -231
- package/README.md +2 -1
- package/README.ru.md +75 -54
- package/README.zh-CN.md +121 -238
- package/dist/assets/index-BenyXiE2.css +32 -0
- package/dist/assets/{index-DF_FFT3b.js → index-dyw-e9VE.js} +249 -237
- package/dist/index.html +2 -2
- package/dist/sw.js +102 -27
- package/package.json +3 -2
- package/server/claude-sdk.js +106 -62
- package/server/cli.js +10 -7
- package/server/cursor-cli.js +59 -73
- package/server/database/db.js +142 -1
- package/server/database/init.sql +28 -1
- package/server/gemini-cli.js +46 -48
- package/server/gemini-response-handler.js +12 -73
- package/server/index.js +82 -55
- package/server/middleware/auth.js +2 -2
- package/server/openai-codex.js +43 -28
- package/server/projects.js +1 -1
- package/server/providers/claude/adapter.js +278 -0
- package/server/providers/codex/adapter.js +248 -0
- package/server/providers/cursor/adapter.js +353 -0
- package/server/providers/gemini/adapter.js +186 -0
- package/server/providers/registry.js +44 -0
- package/server/providers/types.js +119 -0
- package/server/providers/utils.js +29 -0
- package/server/routes/agent.js +7 -5
- package/server/routes/cli-auth.js +38 -0
- package/server/routes/codex.js +1 -19
- package/server/routes/gemini.js +0 -30
- package/server/routes/git.js +48 -20
- package/server/routes/messages.js +61 -0
- package/server/routes/plugins.js +5 -1
- package/server/routes/settings.js +99 -1
- package/server/routes/taskmaster.js +2 -2
- package/server/services/notification-orchestrator.js +227 -0
- package/server/services/vapid-keys.js +35 -0
- package/server/utils/plugin-loader.js +53 -4
- package/shared/networkHosts.js +22 -0
- package/dist/assets/index-WNTmA_ug.css +0 -32
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude provider adapter.
|
|
3
|
+
*
|
|
4
|
+
* Normalizes Claude SDK session history into NormalizedMessage format.
|
|
5
|
+
* @module adapters/claude
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { getSessionMessages } from '../../projects.js';
|
|
9
|
+
import { createNormalizedMessage, generateMessageId } from '../types.js';
|
|
10
|
+
import { isInternalContent } from '../utils.js';
|
|
11
|
+
|
|
12
|
+
const PROVIDER = 'claude';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Normalize a raw JSONL message or realtime SDK event into NormalizedMessage(s).
|
|
16
|
+
* Handles both history entries (JSONL `{ message: { role, content } }`) and
|
|
17
|
+
* realtime streaming events (`content_block_delta`, `content_block_stop`, etc.).
|
|
18
|
+
* @param {object} raw - A single entry from JSONL or a live SDK event
|
|
19
|
+
* @param {string} sessionId
|
|
20
|
+
* @returns {import('../types.js').NormalizedMessage[]}
|
|
21
|
+
*/
|
|
22
|
+
export function normalizeMessage(raw, sessionId) {
|
|
23
|
+
// ── Streaming events (realtime) ──────────────────────────────────────────
|
|
24
|
+
if (raw.type === 'content_block_delta' && raw.delta?.text) {
|
|
25
|
+
return [createNormalizedMessage({ kind: 'stream_delta', content: raw.delta.text, sessionId, provider: PROVIDER })];
|
|
26
|
+
}
|
|
27
|
+
if (raw.type === 'content_block_stop') {
|
|
28
|
+
return [createNormalizedMessage({ kind: 'stream_end', sessionId, provider: PROVIDER })];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ── History / full-message events ────────────────────────────────────────
|
|
32
|
+
const messages = [];
|
|
33
|
+
const ts = raw.timestamp || new Date().toISOString();
|
|
34
|
+
const baseId = raw.uuid || generateMessageId('claude');
|
|
35
|
+
|
|
36
|
+
// User message
|
|
37
|
+
if (raw.message?.role === 'user' && raw.message?.content) {
|
|
38
|
+
if (Array.isArray(raw.message.content)) {
|
|
39
|
+
// Handle tool_result parts
|
|
40
|
+
for (const part of raw.message.content) {
|
|
41
|
+
if (part.type === 'tool_result') {
|
|
42
|
+
messages.push(createNormalizedMessage({
|
|
43
|
+
id: `${baseId}_tr_${part.tool_use_id}`,
|
|
44
|
+
sessionId,
|
|
45
|
+
timestamp: ts,
|
|
46
|
+
provider: PROVIDER,
|
|
47
|
+
kind: 'tool_result',
|
|
48
|
+
toolId: part.tool_use_id,
|
|
49
|
+
content: typeof part.content === 'string' ? part.content : JSON.stringify(part.content),
|
|
50
|
+
isError: Boolean(part.is_error),
|
|
51
|
+
subagentTools: raw.subagentTools,
|
|
52
|
+
toolUseResult: raw.toolUseResult,
|
|
53
|
+
}));
|
|
54
|
+
} else if (part.type === 'text') {
|
|
55
|
+
// Regular text parts from user
|
|
56
|
+
const text = part.text || '';
|
|
57
|
+
if (text && !isInternalContent(text)) {
|
|
58
|
+
messages.push(createNormalizedMessage({
|
|
59
|
+
id: `${baseId}_text`,
|
|
60
|
+
sessionId,
|
|
61
|
+
timestamp: ts,
|
|
62
|
+
provider: PROVIDER,
|
|
63
|
+
kind: 'text',
|
|
64
|
+
role: 'user',
|
|
65
|
+
content: text,
|
|
66
|
+
}));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// If no text parts were found, check if it's a pure user message
|
|
72
|
+
if (messages.length === 0) {
|
|
73
|
+
const textParts = raw.message.content
|
|
74
|
+
.filter(p => p.type === 'text')
|
|
75
|
+
.map(p => p.text)
|
|
76
|
+
.filter(Boolean)
|
|
77
|
+
.join('\n');
|
|
78
|
+
if (textParts && !isInternalContent(textParts)) {
|
|
79
|
+
messages.push(createNormalizedMessage({
|
|
80
|
+
id: `${baseId}_text`,
|
|
81
|
+
sessionId,
|
|
82
|
+
timestamp: ts,
|
|
83
|
+
provider: PROVIDER,
|
|
84
|
+
kind: 'text',
|
|
85
|
+
role: 'user',
|
|
86
|
+
content: textParts,
|
|
87
|
+
}));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
} else if (typeof raw.message.content === 'string') {
|
|
91
|
+
const text = raw.message.content;
|
|
92
|
+
if (text && !isInternalContent(text)) {
|
|
93
|
+
messages.push(createNormalizedMessage({
|
|
94
|
+
id: baseId,
|
|
95
|
+
sessionId,
|
|
96
|
+
timestamp: ts,
|
|
97
|
+
provider: PROVIDER,
|
|
98
|
+
kind: 'text',
|
|
99
|
+
role: 'user',
|
|
100
|
+
content: text,
|
|
101
|
+
}));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return messages;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Thinking message
|
|
108
|
+
if (raw.type === 'thinking' && raw.message?.content) {
|
|
109
|
+
messages.push(createNormalizedMessage({
|
|
110
|
+
id: baseId,
|
|
111
|
+
sessionId,
|
|
112
|
+
timestamp: ts,
|
|
113
|
+
provider: PROVIDER,
|
|
114
|
+
kind: 'thinking',
|
|
115
|
+
content: raw.message.content,
|
|
116
|
+
}));
|
|
117
|
+
return messages;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Tool use result (codex-style in Claude)
|
|
121
|
+
if (raw.type === 'tool_use' && raw.toolName) {
|
|
122
|
+
messages.push(createNormalizedMessage({
|
|
123
|
+
id: baseId,
|
|
124
|
+
sessionId,
|
|
125
|
+
timestamp: ts,
|
|
126
|
+
provider: PROVIDER,
|
|
127
|
+
kind: 'tool_use',
|
|
128
|
+
toolName: raw.toolName,
|
|
129
|
+
toolInput: raw.toolInput,
|
|
130
|
+
toolId: raw.toolCallId || baseId,
|
|
131
|
+
}));
|
|
132
|
+
return messages;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (raw.type === 'tool_result') {
|
|
136
|
+
messages.push(createNormalizedMessage({
|
|
137
|
+
id: baseId,
|
|
138
|
+
sessionId,
|
|
139
|
+
timestamp: ts,
|
|
140
|
+
provider: PROVIDER,
|
|
141
|
+
kind: 'tool_result',
|
|
142
|
+
toolId: raw.toolCallId || '',
|
|
143
|
+
content: raw.output || '',
|
|
144
|
+
isError: false,
|
|
145
|
+
}));
|
|
146
|
+
return messages;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Assistant message
|
|
150
|
+
if (raw.message?.role === 'assistant' && raw.message?.content) {
|
|
151
|
+
if (Array.isArray(raw.message.content)) {
|
|
152
|
+
let partIndex = 0;
|
|
153
|
+
for (const part of raw.message.content) {
|
|
154
|
+
if (part.type === 'text' && part.text) {
|
|
155
|
+
messages.push(createNormalizedMessage({
|
|
156
|
+
id: `${baseId}_${partIndex}`,
|
|
157
|
+
sessionId,
|
|
158
|
+
timestamp: ts,
|
|
159
|
+
provider: PROVIDER,
|
|
160
|
+
kind: 'text',
|
|
161
|
+
role: 'assistant',
|
|
162
|
+
content: part.text,
|
|
163
|
+
}));
|
|
164
|
+
} else if (part.type === 'tool_use') {
|
|
165
|
+
messages.push(createNormalizedMessage({
|
|
166
|
+
id: `${baseId}_${partIndex}`,
|
|
167
|
+
sessionId,
|
|
168
|
+
timestamp: ts,
|
|
169
|
+
provider: PROVIDER,
|
|
170
|
+
kind: 'tool_use',
|
|
171
|
+
toolName: part.name,
|
|
172
|
+
toolInput: part.input,
|
|
173
|
+
toolId: part.id,
|
|
174
|
+
}));
|
|
175
|
+
} else if (part.type === 'thinking' && part.thinking) {
|
|
176
|
+
messages.push(createNormalizedMessage({
|
|
177
|
+
id: `${baseId}_${partIndex}`,
|
|
178
|
+
sessionId,
|
|
179
|
+
timestamp: ts,
|
|
180
|
+
provider: PROVIDER,
|
|
181
|
+
kind: 'thinking',
|
|
182
|
+
content: part.thinking,
|
|
183
|
+
}));
|
|
184
|
+
}
|
|
185
|
+
partIndex++;
|
|
186
|
+
}
|
|
187
|
+
} else if (typeof raw.message.content === 'string') {
|
|
188
|
+
messages.push(createNormalizedMessage({
|
|
189
|
+
id: baseId,
|
|
190
|
+
sessionId,
|
|
191
|
+
timestamp: ts,
|
|
192
|
+
provider: PROVIDER,
|
|
193
|
+
kind: 'text',
|
|
194
|
+
role: 'assistant',
|
|
195
|
+
content: raw.message.content,
|
|
196
|
+
}));
|
|
197
|
+
}
|
|
198
|
+
return messages;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return messages;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* @type {import('../types.js').ProviderAdapter}
|
|
206
|
+
*/
|
|
207
|
+
export const claudeAdapter = {
|
|
208
|
+
normalizeMessage,
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Fetch session history from JSONL files, returning normalized messages.
|
|
212
|
+
*/
|
|
213
|
+
async fetchHistory(sessionId, opts = {}) {
|
|
214
|
+
const { projectName, limit = null, offset = 0 } = opts;
|
|
215
|
+
if (!projectName) {
|
|
216
|
+
return { messages: [], total: 0, hasMore: false, offset: 0, limit: null };
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
let result;
|
|
220
|
+
try {
|
|
221
|
+
result = await getSessionMessages(projectName, sessionId, limit, offset);
|
|
222
|
+
} catch (error) {
|
|
223
|
+
console.warn(`[ClaudeAdapter] Failed to load session ${sessionId}:`, error.message);
|
|
224
|
+
return { messages: [], total: 0, hasMore: false, offset: 0, limit: null };
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// getSessionMessages returns either an array (no limit) or { messages, total, hasMore }
|
|
228
|
+
const rawMessages = Array.isArray(result) ? result : (result.messages || []);
|
|
229
|
+
const total = Array.isArray(result) ? rawMessages.length : (result.total || 0);
|
|
230
|
+
const hasMore = Array.isArray(result) ? false : Boolean(result.hasMore);
|
|
231
|
+
|
|
232
|
+
// First pass: collect tool results for attachment to tool_use messages
|
|
233
|
+
const toolResultMap = new Map();
|
|
234
|
+
for (const raw of rawMessages) {
|
|
235
|
+
if (raw.message?.role === 'user' && Array.isArray(raw.message?.content)) {
|
|
236
|
+
for (const part of raw.message.content) {
|
|
237
|
+
if (part.type === 'tool_result') {
|
|
238
|
+
toolResultMap.set(part.tool_use_id, {
|
|
239
|
+
content: part.content,
|
|
240
|
+
isError: Boolean(part.is_error),
|
|
241
|
+
timestamp: raw.timestamp,
|
|
242
|
+
subagentTools: raw.subagentTools,
|
|
243
|
+
toolUseResult: raw.toolUseResult,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Second pass: normalize all messages
|
|
251
|
+
const normalized = [];
|
|
252
|
+
for (const raw of rawMessages) {
|
|
253
|
+
const entries = normalizeMessage(raw, sessionId);
|
|
254
|
+
normalized.push(...entries);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Attach tool results to their corresponding tool_use messages
|
|
258
|
+
for (const msg of normalized) {
|
|
259
|
+
if (msg.kind === 'tool_use' && msg.toolId && toolResultMap.has(msg.toolId)) {
|
|
260
|
+
const tr = toolResultMap.get(msg.toolId);
|
|
261
|
+
msg.toolResult = {
|
|
262
|
+
content: typeof tr.content === 'string' ? tr.content : JSON.stringify(tr.content),
|
|
263
|
+
isError: tr.isError,
|
|
264
|
+
toolUseResult: tr.toolUseResult,
|
|
265
|
+
};
|
|
266
|
+
msg.subagentTools = tr.subagentTools;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return {
|
|
271
|
+
messages: normalized,
|
|
272
|
+
total,
|
|
273
|
+
hasMore,
|
|
274
|
+
offset,
|
|
275
|
+
limit,
|
|
276
|
+
};
|
|
277
|
+
},
|
|
278
|
+
};
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex (OpenAI) provider adapter.
|
|
3
|
+
*
|
|
4
|
+
* Normalizes Codex SDK session history into NormalizedMessage format.
|
|
5
|
+
* @module adapters/codex
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { getCodexSessionMessages } from '../../projects.js';
|
|
9
|
+
import { createNormalizedMessage, generateMessageId } from '../types.js';
|
|
10
|
+
|
|
11
|
+
const PROVIDER = 'codex';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Normalize a raw Codex JSONL message into NormalizedMessage(s).
|
|
15
|
+
* @param {object} raw - A single parsed message from Codex JSONL
|
|
16
|
+
* @param {string} sessionId
|
|
17
|
+
* @returns {import('../types.js').NormalizedMessage[]}
|
|
18
|
+
*/
|
|
19
|
+
function normalizeCodexHistoryEntry(raw, sessionId) {
|
|
20
|
+
const ts = raw.timestamp || new Date().toISOString();
|
|
21
|
+
const baseId = raw.uuid || generateMessageId('codex');
|
|
22
|
+
|
|
23
|
+
// User message
|
|
24
|
+
if (raw.message?.role === 'user') {
|
|
25
|
+
const content = typeof raw.message.content === 'string'
|
|
26
|
+
? raw.message.content
|
|
27
|
+
: Array.isArray(raw.message.content)
|
|
28
|
+
? raw.message.content.map(p => typeof p === 'string' ? p : p?.text || '').filter(Boolean).join('\n')
|
|
29
|
+
: String(raw.message.content || '');
|
|
30
|
+
if (!content.trim()) return [];
|
|
31
|
+
return [createNormalizedMessage({
|
|
32
|
+
id: baseId,
|
|
33
|
+
sessionId,
|
|
34
|
+
timestamp: ts,
|
|
35
|
+
provider: PROVIDER,
|
|
36
|
+
kind: 'text',
|
|
37
|
+
role: 'user',
|
|
38
|
+
content,
|
|
39
|
+
})];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Assistant message
|
|
43
|
+
if (raw.message?.role === 'assistant') {
|
|
44
|
+
const content = typeof raw.message.content === 'string'
|
|
45
|
+
? raw.message.content
|
|
46
|
+
: Array.isArray(raw.message.content)
|
|
47
|
+
? raw.message.content.map(p => typeof p === 'string' ? p : p?.text || '').filter(Boolean).join('\n')
|
|
48
|
+
: '';
|
|
49
|
+
if (!content.trim()) return [];
|
|
50
|
+
return [createNormalizedMessage({
|
|
51
|
+
id: baseId,
|
|
52
|
+
sessionId,
|
|
53
|
+
timestamp: ts,
|
|
54
|
+
provider: PROVIDER,
|
|
55
|
+
kind: 'text',
|
|
56
|
+
role: 'assistant',
|
|
57
|
+
content,
|
|
58
|
+
})];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Thinking/reasoning
|
|
62
|
+
if (raw.type === 'thinking' || raw.isReasoning) {
|
|
63
|
+
return [createNormalizedMessage({
|
|
64
|
+
id: baseId,
|
|
65
|
+
sessionId,
|
|
66
|
+
timestamp: ts,
|
|
67
|
+
provider: PROVIDER,
|
|
68
|
+
kind: 'thinking',
|
|
69
|
+
content: raw.message?.content || '',
|
|
70
|
+
})];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Tool use
|
|
74
|
+
if (raw.type === 'tool_use' || raw.toolName) {
|
|
75
|
+
return [createNormalizedMessage({
|
|
76
|
+
id: baseId,
|
|
77
|
+
sessionId,
|
|
78
|
+
timestamp: ts,
|
|
79
|
+
provider: PROVIDER,
|
|
80
|
+
kind: 'tool_use',
|
|
81
|
+
toolName: raw.toolName || 'Unknown',
|
|
82
|
+
toolInput: raw.toolInput,
|
|
83
|
+
toolId: raw.toolCallId || baseId,
|
|
84
|
+
})];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Tool result
|
|
88
|
+
if (raw.type === 'tool_result') {
|
|
89
|
+
return [createNormalizedMessage({
|
|
90
|
+
id: baseId,
|
|
91
|
+
sessionId,
|
|
92
|
+
timestamp: ts,
|
|
93
|
+
provider: PROVIDER,
|
|
94
|
+
kind: 'tool_result',
|
|
95
|
+
toolId: raw.toolCallId || '',
|
|
96
|
+
content: raw.output || '',
|
|
97
|
+
isError: Boolean(raw.isError),
|
|
98
|
+
})];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return [];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Normalize a raw Codex event (history JSONL or transformed SDK event) into NormalizedMessage(s).
|
|
106
|
+
* @param {object} raw - A history entry (has raw.message.role) or transformed SDK event (has raw.type)
|
|
107
|
+
* @param {string} sessionId
|
|
108
|
+
* @returns {import('../types.js').NormalizedMessage[]}
|
|
109
|
+
*/
|
|
110
|
+
export function normalizeMessage(raw, sessionId) {
|
|
111
|
+
// History format: has message.role
|
|
112
|
+
if (raw.message?.role) {
|
|
113
|
+
return normalizeCodexHistoryEntry(raw, sessionId);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const ts = raw.timestamp || new Date().toISOString();
|
|
117
|
+
const baseId = raw.uuid || generateMessageId('codex');
|
|
118
|
+
|
|
119
|
+
// SDK event format (output of transformCodexEvent)
|
|
120
|
+
if (raw.type === 'item') {
|
|
121
|
+
switch (raw.itemType) {
|
|
122
|
+
case 'agent_message':
|
|
123
|
+
return [createNormalizedMessage({
|
|
124
|
+
id: baseId, sessionId, timestamp: ts, provider: PROVIDER,
|
|
125
|
+
kind: 'text', role: 'assistant', content: raw.message?.content || '',
|
|
126
|
+
})];
|
|
127
|
+
case 'reasoning':
|
|
128
|
+
return [createNormalizedMessage({
|
|
129
|
+
id: baseId, sessionId, timestamp: ts, provider: PROVIDER,
|
|
130
|
+
kind: 'thinking', content: raw.message?.content || '',
|
|
131
|
+
})];
|
|
132
|
+
case 'command_execution':
|
|
133
|
+
return [createNormalizedMessage({
|
|
134
|
+
id: baseId, sessionId, timestamp: ts, provider: PROVIDER,
|
|
135
|
+
kind: 'tool_use', toolName: 'Bash', toolInput: { command: raw.command },
|
|
136
|
+
toolId: baseId,
|
|
137
|
+
output: raw.output, exitCode: raw.exitCode, status: raw.status,
|
|
138
|
+
})];
|
|
139
|
+
case 'file_change':
|
|
140
|
+
return [createNormalizedMessage({
|
|
141
|
+
id: baseId, sessionId, timestamp: ts, provider: PROVIDER,
|
|
142
|
+
kind: 'tool_use', toolName: 'FileChanges', toolInput: raw.changes,
|
|
143
|
+
toolId: baseId, status: raw.status,
|
|
144
|
+
})];
|
|
145
|
+
case 'mcp_tool_call':
|
|
146
|
+
return [createNormalizedMessage({
|
|
147
|
+
id: baseId, sessionId, timestamp: ts, provider: PROVIDER,
|
|
148
|
+
kind: 'tool_use', toolName: raw.tool || 'MCP', toolInput: raw.arguments,
|
|
149
|
+
toolId: baseId, server: raw.server, result: raw.result,
|
|
150
|
+
error: raw.error, status: raw.status,
|
|
151
|
+
})];
|
|
152
|
+
case 'web_search':
|
|
153
|
+
return [createNormalizedMessage({
|
|
154
|
+
id: baseId, sessionId, timestamp: ts, provider: PROVIDER,
|
|
155
|
+
kind: 'tool_use', toolName: 'WebSearch', toolInput: { query: raw.query },
|
|
156
|
+
toolId: baseId,
|
|
157
|
+
})];
|
|
158
|
+
case 'todo_list':
|
|
159
|
+
return [createNormalizedMessage({
|
|
160
|
+
id: baseId, sessionId, timestamp: ts, provider: PROVIDER,
|
|
161
|
+
kind: 'tool_use', toolName: 'TodoList', toolInput: { items: raw.items },
|
|
162
|
+
toolId: baseId,
|
|
163
|
+
})];
|
|
164
|
+
case 'error':
|
|
165
|
+
return [createNormalizedMessage({
|
|
166
|
+
id: baseId, sessionId, timestamp: ts, provider: PROVIDER,
|
|
167
|
+
kind: 'error', content: raw.message?.content || 'Unknown error',
|
|
168
|
+
})];
|
|
169
|
+
default:
|
|
170
|
+
// Unknown item type — pass through as generic tool_use
|
|
171
|
+
return [createNormalizedMessage({
|
|
172
|
+
id: baseId, sessionId, timestamp: ts, provider: PROVIDER,
|
|
173
|
+
kind: 'tool_use', toolName: raw.itemType || 'Unknown',
|
|
174
|
+
toolInput: raw.item || raw, toolId: baseId,
|
|
175
|
+
})];
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (raw.type === 'turn_complete') {
|
|
180
|
+
return [createNormalizedMessage({
|
|
181
|
+
id: baseId, sessionId, timestamp: ts, provider: PROVIDER,
|
|
182
|
+
kind: 'complete',
|
|
183
|
+
})];
|
|
184
|
+
}
|
|
185
|
+
if (raw.type === 'turn_failed') {
|
|
186
|
+
return [createNormalizedMessage({
|
|
187
|
+
id: baseId, sessionId, timestamp: ts, provider: PROVIDER,
|
|
188
|
+
kind: 'error', content: raw.error?.message || 'Turn failed',
|
|
189
|
+
})];
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return [];
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* @type {import('../types.js').ProviderAdapter}
|
|
197
|
+
*/
|
|
198
|
+
export const codexAdapter = {
|
|
199
|
+
normalizeMessage,
|
|
200
|
+
/**
|
|
201
|
+
* Fetch session history from Codex JSONL files.
|
|
202
|
+
*/
|
|
203
|
+
async fetchHistory(sessionId, opts = {}) {
|
|
204
|
+
const { limit = null, offset = 0 } = opts;
|
|
205
|
+
|
|
206
|
+
let result;
|
|
207
|
+
try {
|
|
208
|
+
result = await getCodexSessionMessages(sessionId, limit, offset);
|
|
209
|
+
} catch (error) {
|
|
210
|
+
console.warn(`[CodexAdapter] Failed to load session ${sessionId}:`, error.message);
|
|
211
|
+
return { messages: [], total: 0, hasMore: false, offset: 0, limit: null };
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const rawMessages = Array.isArray(result) ? result : (result.messages || []);
|
|
215
|
+
const total = Array.isArray(result) ? rawMessages.length : (result.total || 0);
|
|
216
|
+
const hasMore = Array.isArray(result) ? false : Boolean(result.hasMore);
|
|
217
|
+
const tokenUsage = result.tokenUsage || null;
|
|
218
|
+
|
|
219
|
+
const normalized = [];
|
|
220
|
+
for (const raw of rawMessages) {
|
|
221
|
+
const entries = normalizeCodexHistoryEntry(raw, sessionId);
|
|
222
|
+
normalized.push(...entries);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Attach tool results to tool_use messages
|
|
226
|
+
const toolResultMap = new Map();
|
|
227
|
+
for (const msg of normalized) {
|
|
228
|
+
if (msg.kind === 'tool_result' && msg.toolId) {
|
|
229
|
+
toolResultMap.set(msg.toolId, msg);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
for (const msg of normalized) {
|
|
233
|
+
if (msg.kind === 'tool_use' && msg.toolId && toolResultMap.has(msg.toolId)) {
|
|
234
|
+
const tr = toolResultMap.get(msg.toolId);
|
|
235
|
+
msg.toolResult = { content: tr.content, isError: tr.isError };
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
messages: normalized,
|
|
241
|
+
total,
|
|
242
|
+
hasMore,
|
|
243
|
+
offset,
|
|
244
|
+
limit,
|
|
245
|
+
tokenUsage,
|
|
246
|
+
};
|
|
247
|
+
},
|
|
248
|
+
};
|