remote-codex 0.1.9 → 0.11.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.
- package/apps/supervisor-api/dist/index.js +11942 -6101
- package/apps/supervisor-web/dist/assets/{highlighted-body-OFNGDK62-BFD4Ytvg.js → highlighted-body-OFNGDK62-ChrwAL9u.js} +1 -1
- package/apps/supervisor-web/dist/assets/index-DHf2HOXx.js +381 -0
- package/apps/supervisor-web/dist/assets/index-DpWxXCgt.css +32 -0
- package/apps/supervisor-web/dist/assets/{xterm-CukFWbxr.js → xterm-D4sevve4.js} +1 -1
- package/apps/supervisor-web/dist/index.html +2 -2
- package/config/codex-model-pricing.json +63 -0
- package/package.json +5 -2
- package/packages/agent-runtime/src/index.ts +4 -0
- package/packages/agent-runtime/src/management-errors.ts +11 -0
- package/packages/agent-runtime/src/model-pricing.ts +312 -0
- package/packages/agent-runtime/src/registry.ts +19 -4
- package/packages/agent-runtime/src/runtime-errors.ts +97 -0
- package/packages/agent-runtime/src/types.ts +50 -4
- package/packages/agent-runtime/src/unavailable-runtime.ts +169 -0
- package/packages/claude/src/historyItems.ts +693 -0
- package/packages/claude/src/index.ts +2 -0
- package/packages/claude/src/runtimeAdapter.test.ts +2138 -0
- package/packages/claude/src/runtimeAdapter.ts +2145 -0
- package/packages/codex/src/appServerManager.ts +12 -3
- package/packages/codex/src/historyItems.test.ts +110 -0
- package/packages/codex/src/historyItems.ts +97 -16
- package/packages/codex/src/hookHistory.test.ts +59 -0
- package/packages/codex/src/index.ts +7 -0
- package/packages/codex/src/local-session-store.ts +390 -0
- package/packages/codex/src/management/codex-management-service.ts +454 -0
- package/packages/codex/src/management/codexHostConfig.test.ts +88 -0
- package/packages/codex/src/management/codexHostConfig.ts +188 -0
- package/packages/codex/src/management/errors.ts +20 -0
- package/packages/codex/src/modelPricing.test.ts +184 -0
- package/packages/codex/src/modelPricing.ts +9 -0
- package/packages/codex/src/runtime-errors.test.ts +72 -0
- package/packages/codex/src/runtime-errors.ts +37 -0
- package/packages/codex/src/runtimeAdapter.ts +25 -2
- package/packages/codex/src/thread-title.ts +1 -0
- package/packages/db/src/repositories.ts +30 -0
- package/packages/opencode/src/historyItems.test.ts +504 -0
- package/packages/opencode/src/historyItems.ts +896 -0
- package/packages/opencode/src/index.ts +2 -0
- package/packages/opencode/src/runtimeAdapter.test.ts +1355 -0
- package/packages/opencode/src/runtimeAdapter.ts +1469 -0
- package/packages/shared/src/agent-providers.ts +56 -0
- package/packages/shared/src/index.ts +174 -35
- package/apps/supervisor-web/dist/assets/index-CbIt0KnL.css +0 -32
- package/apps/supervisor-web/dist/assets/index-Rd2EBQac.js +0 -377
|
@@ -0,0 +1,693 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AgentHistoryItem,
|
|
3
|
+
AgentTurn,
|
|
4
|
+
} from '../../agent-runtime/src/index';
|
|
5
|
+
|
|
6
|
+
const CLAUDE_TOOL_LABELS: Record<string, string> = {
|
|
7
|
+
Agent: 'Agent',
|
|
8
|
+
Bash: 'Bash',
|
|
9
|
+
Edit: 'Edit file',
|
|
10
|
+
MultiEdit: 'Edit files',
|
|
11
|
+
Write: 'Write file',
|
|
12
|
+
NotebookEdit: 'Edit notebook',
|
|
13
|
+
Read: 'Read file',
|
|
14
|
+
Grep: 'Search files',
|
|
15
|
+
Glob: 'Find files',
|
|
16
|
+
LS: 'List files',
|
|
17
|
+
WebSearch: 'Web search',
|
|
18
|
+
WebFetch: 'Web fetch',
|
|
19
|
+
Skill: 'Skill',
|
|
20
|
+
Task: 'Agent',
|
|
21
|
+
ToolSearch: 'Tool search',
|
|
22
|
+
EnterPlanMode: 'Enter plan mode',
|
|
23
|
+
ExitPlanMode: 'Exit plan mode',
|
|
24
|
+
TodoWrite: 'Update todos',
|
|
25
|
+
};
|
|
26
|
+
const HIDDEN_ASK_USER_QUESTION_CONTINUATION_PREFIX =
|
|
27
|
+
'The user answered the clarification questions below. Continue from the same plan-mode task using these answers.';
|
|
28
|
+
const SUPPRESSED_ASSISTANT_TEXTS = new Set([
|
|
29
|
+
'No response requested.',
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
function normalizedToolName(toolName: string) {
|
|
33
|
+
return toolName.replace(/[\s_-]+/g, '').toLowerCase();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function isSuppressedClaudeToolName(toolName: string) {
|
|
37
|
+
const normalized = normalizedToolName(toolName);
|
|
38
|
+
return (
|
|
39
|
+
normalized === 'askuserquestion' ||
|
|
40
|
+
normalized === 'toolsearch' ||
|
|
41
|
+
normalized === 'enterplanmode'
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function isExitPlanModeToolName(toolName: string) {
|
|
46
|
+
return normalizedToolName(toolName) === 'exitplanmode';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function isWebToolName(toolName: string) {
|
|
50
|
+
const normalized = normalizedToolName(toolName);
|
|
51
|
+
return normalized === 'websearch' || normalized === 'webfetch';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function isFileInspectionToolName(toolName: string) {
|
|
55
|
+
const normalized = normalizedToolName(toolName);
|
|
56
|
+
return (
|
|
57
|
+
normalized === 'read' ||
|
|
58
|
+
normalized === 'grep' ||
|
|
59
|
+
normalized === 'glob' ||
|
|
60
|
+
normalized === 'ls'
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function isAgentToolName(toolName: string) {
|
|
65
|
+
const normalized = normalizedToolName(toolName);
|
|
66
|
+
return normalized === 'agent' || normalized === 'task';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function isSkillToolName(toolName: string) {
|
|
70
|
+
return normalizedToolName(toolName) === 'skill';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
74
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function stringValue(value: unknown): string | null {
|
|
78
|
+
return typeof value === 'string' && value.trim() ? value : null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function thinkingTextFromBlock(block: Record<string, unknown>): string | null {
|
|
82
|
+
return (
|
|
83
|
+
stringValue(block.thinking) ??
|
|
84
|
+
stringValue(block.text) ??
|
|
85
|
+
stringValue(block.content)
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function compactJson(value: unknown): string {
|
|
90
|
+
try {
|
|
91
|
+
return JSON.stringify(value, null, 2);
|
|
92
|
+
} catch {
|
|
93
|
+
return String(value);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function readableToolName(toolName: string) {
|
|
98
|
+
if (CLAUDE_TOOL_LABELS[toolName]) {
|
|
99
|
+
return CLAUDE_TOOL_LABELS[toolName];
|
|
100
|
+
}
|
|
101
|
+
if (toolName.startsWith('mcp__')) {
|
|
102
|
+
return toolName.split('__').filter(Boolean).join(' / ');
|
|
103
|
+
}
|
|
104
|
+
return toolName;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function commandFromInput(input: unknown) {
|
|
108
|
+
if (!isRecord(input)) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
return stringValue(input.command) ?? stringValue(input.cmd) ?? stringValue(input.description);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function pathFromInput(input: unknown) {
|
|
115
|
+
if (!isRecord(input)) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
return (
|
|
119
|
+
stringValue(input.file_path) ??
|
|
120
|
+
stringValue(input.filePath) ??
|
|
121
|
+
stringValue(input.path) ??
|
|
122
|
+
stringValue(input.notebook_path)
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function summarizeToolInput(input: unknown) {
|
|
127
|
+
if (!isRecord(input)) {
|
|
128
|
+
return compactJson(input);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const description = stringValue(input.description);
|
|
132
|
+
if (description) {
|
|
133
|
+
return description;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const command = commandFromInput(input);
|
|
137
|
+
if (command) {
|
|
138
|
+
return command;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const filePath = pathFromInput(input);
|
|
142
|
+
const pattern = stringValue(input.pattern);
|
|
143
|
+
if (filePath && pattern) {
|
|
144
|
+
return `${pattern} in ${filePath}`;
|
|
145
|
+
}
|
|
146
|
+
if (pattern) {
|
|
147
|
+
return pattern;
|
|
148
|
+
}
|
|
149
|
+
if (filePath) {
|
|
150
|
+
return filePath;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const query = stringValue(input.query) ?? stringValue(input.url) ?? stringValue(input.prompt);
|
|
154
|
+
if (query) {
|
|
155
|
+
return query;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const skill = stringValue(input.skill);
|
|
159
|
+
if (skill) {
|
|
160
|
+
const args = stringValue(input.args);
|
|
161
|
+
return args ? `${skill}: ${args}` : skill;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return compactJson(input);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function messageContentText(message: unknown): string {
|
|
168
|
+
if (!isRecord(message)) {
|
|
169
|
+
return '';
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const content = message.content;
|
|
173
|
+
if (typeof content === 'string') {
|
|
174
|
+
return content;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (!Array.isArray(content)) {
|
|
178
|
+
return '';
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return content
|
|
182
|
+
.map((block) => {
|
|
183
|
+
if (!isRecord(block)) {
|
|
184
|
+
return '';
|
|
185
|
+
}
|
|
186
|
+
if (typeof block.text === 'string') {
|
|
187
|
+
return block.text;
|
|
188
|
+
}
|
|
189
|
+
if (typeof block.content === 'string') {
|
|
190
|
+
return block.content;
|
|
191
|
+
}
|
|
192
|
+
return '';
|
|
193
|
+
})
|
|
194
|
+
.filter(Boolean)
|
|
195
|
+
.join('\n');
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function hiddenInitPrompt() {
|
|
199
|
+
return 'Initialize this Claude Code session for Remote Codex. Do not inspect or modify files. Reply with "Ready."';
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function isHiddenInitMessage(message: unknown) {
|
|
203
|
+
return messageContentText(message).trim() === hiddenInitPrompt();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function isHiddenContinuationMessage(message: unknown) {
|
|
207
|
+
return messageContentText(message)
|
|
208
|
+
.trim()
|
|
209
|
+
.startsWith(HIDDEN_ASK_USER_QUESTION_CONTINUATION_PREFIX);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function shouldSuppressAssistantText(text: string) {
|
|
213
|
+
return SUPPRESSED_ASSISTANT_TEXTS.has(text.trim());
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function userMessageToHistoryItem(id: string, message: unknown): AgentHistoryItem {
|
|
217
|
+
return {
|
|
218
|
+
id,
|
|
219
|
+
kind: 'userMessage',
|
|
220
|
+
text: messageContentText(message),
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function userMessageHistoryItem(id: string, text: string): AgentHistoryItem {
|
|
225
|
+
return {
|
|
226
|
+
id,
|
|
227
|
+
kind: 'userMessage',
|
|
228
|
+
text,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export function toolUseToHistoryItem(
|
|
233
|
+
input: {
|
|
234
|
+
id: string;
|
|
235
|
+
name: string;
|
|
236
|
+
toolInput: unknown;
|
|
237
|
+
status?: string | null;
|
|
238
|
+
result?: unknown;
|
|
239
|
+
serverName?: string | null;
|
|
240
|
+
},
|
|
241
|
+
): AgentHistoryItem | null {
|
|
242
|
+
if (isSuppressedClaudeToolName(input.name)) {
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const detailParts = [
|
|
247
|
+
`Tool: ${input.serverName ? `${input.serverName}/${input.name}` : input.name}`,
|
|
248
|
+
'',
|
|
249
|
+
'Input:',
|
|
250
|
+
compactJson(input.toolInput),
|
|
251
|
+
];
|
|
252
|
+
if (input.result !== undefined) {
|
|
253
|
+
detailParts.push('', 'Result:', compactJson(input.result));
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const summary = summarizeToolInput(input.toolInput);
|
|
257
|
+
const status = input.status ?? (input.result === undefined ? 'running' : 'completed');
|
|
258
|
+
const displayName = readableToolName(input.serverName ? `${input.serverName}/${input.name}` : input.name);
|
|
259
|
+
|
|
260
|
+
if (input.name === 'Bash') {
|
|
261
|
+
const command = commandFromInput(input.toolInput) ?? summary;
|
|
262
|
+
return {
|
|
263
|
+
id: input.id,
|
|
264
|
+
kind: 'commandExecution',
|
|
265
|
+
text: command || 'Bash command',
|
|
266
|
+
previewText: command || 'Bash command',
|
|
267
|
+
detailText: detailParts.join('\n'),
|
|
268
|
+
status,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (isExitPlanModeToolName(input.name)) {
|
|
273
|
+
const plan = isRecord(input.toolInput) ? stringValue(input.toolInput.plan) : null;
|
|
274
|
+
return {
|
|
275
|
+
id: input.id,
|
|
276
|
+
kind: 'plan',
|
|
277
|
+
text: plan ?? summary ?? 'Plan ready for review.',
|
|
278
|
+
previewText: 'Plan ready',
|
|
279
|
+
detailText: detailParts.join('\n'),
|
|
280
|
+
status: input.status ?? 'completed',
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (['Edit', 'MultiEdit', 'Write', 'NotebookEdit'].includes(input.name)) {
|
|
285
|
+
const filePath = pathFromInput(input.toolInput);
|
|
286
|
+
return {
|
|
287
|
+
id: input.id,
|
|
288
|
+
kind: 'fileChange',
|
|
289
|
+
text: filePath ?? displayName,
|
|
290
|
+
previewText: filePath ? `${displayName}: ${filePath}` : displayName,
|
|
291
|
+
detailText: detailParts.join('\n'),
|
|
292
|
+
changedFiles: filePath ? 1 : null,
|
|
293
|
+
addedLines: null,
|
|
294
|
+
removedLines: null,
|
|
295
|
+
status,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (isWebToolName(input.name)) {
|
|
300
|
+
return {
|
|
301
|
+
id: input.id,
|
|
302
|
+
kind: 'webSearch',
|
|
303
|
+
text: summary ? `${displayName}: ${summary}` : displayName,
|
|
304
|
+
previewText: summary ? `${displayName}: ${summary}` : displayName,
|
|
305
|
+
detailText: detailParts.join('\n'),
|
|
306
|
+
status,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (isFileInspectionToolName(input.name)) {
|
|
311
|
+
return {
|
|
312
|
+
id: input.id,
|
|
313
|
+
kind: 'fileRead',
|
|
314
|
+
text: summary ? `${displayName}: ${summary}` : displayName,
|
|
315
|
+
previewText: summary ? `${displayName}: ${summary}` : displayName,
|
|
316
|
+
detailText: detailParts.join('\n'),
|
|
317
|
+
status,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (isAgentToolName(input.name)) {
|
|
322
|
+
const summaryText = summary || displayName;
|
|
323
|
+
return {
|
|
324
|
+
id: input.id,
|
|
325
|
+
kind: 'agentToolCall',
|
|
326
|
+
text: summaryText === displayName ? displayName : `${displayName}: ${summaryText}`,
|
|
327
|
+
previewText: displayName,
|
|
328
|
+
detailText: detailParts.join('\n'),
|
|
329
|
+
status,
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (isSkillToolName(input.name)) {
|
|
334
|
+
const skill = isRecord(input.toolInput) ? stringValue(input.toolInput.skill) : null;
|
|
335
|
+
const summaryText = skill
|
|
336
|
+
? `Skill: ${skill}`
|
|
337
|
+
: summary
|
|
338
|
+
? `${displayName}: ${summary}`
|
|
339
|
+
: displayName;
|
|
340
|
+
return {
|
|
341
|
+
id: input.id,
|
|
342
|
+
kind: 'skillToolCall',
|
|
343
|
+
text: summaryText,
|
|
344
|
+
previewText: skill ?? displayName,
|
|
345
|
+
detailText: detailParts.join('\n'),
|
|
346
|
+
status,
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return {
|
|
351
|
+
id: input.id,
|
|
352
|
+
kind: 'toolCall',
|
|
353
|
+
text: summary ? `${displayName}: ${summary}` : displayName,
|
|
354
|
+
previewText: displayName,
|
|
355
|
+
detailText: detailParts.join('\n'),
|
|
356
|
+
status,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function contentBlocks(message: unknown): unknown[] {
|
|
361
|
+
if (!isRecord(message) || !Array.isArray(message.content)) {
|
|
362
|
+
return [];
|
|
363
|
+
}
|
|
364
|
+
return message.content;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export function toolResultBlocks(message: unknown): Array<{ toolUseId: string; result: unknown }> {
|
|
368
|
+
return contentBlocks(message)
|
|
369
|
+
.map((block) => {
|
|
370
|
+
if (!isRecord(block) || block.type !== 'tool_result') {
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
373
|
+
const toolUseId = stringValue(block.tool_use_id);
|
|
374
|
+
if (!toolUseId) {
|
|
375
|
+
return null;
|
|
376
|
+
}
|
|
377
|
+
return {
|
|
378
|
+
toolUseId,
|
|
379
|
+
result: block.content,
|
|
380
|
+
};
|
|
381
|
+
})
|
|
382
|
+
.filter((block): block is { toolUseId: string; result: unknown } => Boolean(block));
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
export function askUserQuestionToolUseIds(message: unknown): Set<string> {
|
|
386
|
+
const ids = new Set<string>();
|
|
387
|
+
for (const block of contentBlocks(message)) {
|
|
388
|
+
if (!isRecord(block) || block.type !== 'tool_use') {
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
const id = stringValue(block.id);
|
|
392
|
+
const name = stringValue(block.name);
|
|
393
|
+
if (id && name === 'AskUserQuestion') {
|
|
394
|
+
ids.add(id);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return ids;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
export function suppressedClaudeToolUseIds(message: unknown): Set<string> {
|
|
401
|
+
const ids = new Set<string>();
|
|
402
|
+
for (const block of contentBlocks(message)) {
|
|
403
|
+
if (!isRecord(block) || block.type !== 'tool_use') {
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
const id = stringValue(block.id);
|
|
407
|
+
const name = stringValue(block.name);
|
|
408
|
+
if (id && name && isSuppressedClaudeToolName(name)) {
|
|
409
|
+
ids.add(id);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return ids;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
export function shouldSuppressClaudeToolUse(toolName: string) {
|
|
416
|
+
return isSuppressedClaudeToolName(toolName);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
export function assistantMessageToHistoryItems(
|
|
420
|
+
input: {
|
|
421
|
+
messageId: string;
|
|
422
|
+
message: unknown;
|
|
423
|
+
skipTextBlockIds?: Set<string>;
|
|
424
|
+
},
|
|
425
|
+
): AgentHistoryItem[] {
|
|
426
|
+
const items: AgentHistoryItem[] = [];
|
|
427
|
+
for (const [index, block] of contentBlocks(input.message).entries()) {
|
|
428
|
+
if (!isRecord(block)) {
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
const type = block.type;
|
|
432
|
+
if (type === 'text') {
|
|
433
|
+
const itemId = `${input.messageId}:content:${index}`;
|
|
434
|
+
if (input.skipTextBlockIds?.has(itemId)) {
|
|
435
|
+
continue;
|
|
436
|
+
}
|
|
437
|
+
const text = stringValue(block.text);
|
|
438
|
+
if (text) {
|
|
439
|
+
if (shouldSuppressAssistantText(text)) {
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
items.push({
|
|
443
|
+
id: itemId,
|
|
444
|
+
kind: 'agentMessage',
|
|
445
|
+
text,
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
if (type === 'thinking') {
|
|
451
|
+
const thinking = thinkingTextFromBlock(block);
|
|
452
|
+
if (thinking) {
|
|
453
|
+
items.push({
|
|
454
|
+
id: `${input.messageId}:content:${index}`,
|
|
455
|
+
kind: 'reasoning',
|
|
456
|
+
text: thinking,
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
if (type === 'tool_use') {
|
|
462
|
+
const id = stringValue(block.id) ?? `${input.messageId}:tool:${index}`;
|
|
463
|
+
const name = stringValue(block.name) ?? 'Tool';
|
|
464
|
+
if (isSuppressedClaudeToolName(name)) {
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
const item = toolUseToHistoryItem({
|
|
468
|
+
id,
|
|
469
|
+
name,
|
|
470
|
+
toolInput: block.input,
|
|
471
|
+
status: 'running',
|
|
472
|
+
});
|
|
473
|
+
if (item) {
|
|
474
|
+
items.push(item);
|
|
475
|
+
}
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
if (type === 'mcp_tool_use') {
|
|
479
|
+
const id = stringValue(block.id) ?? `${input.messageId}:mcp:${index}`;
|
|
480
|
+
const name = stringValue(block.name) ?? 'MCP tool';
|
|
481
|
+
const item = toolUseToHistoryItem({
|
|
482
|
+
id,
|
|
483
|
+
name,
|
|
484
|
+
serverName: stringValue(block.server_name),
|
|
485
|
+
toolInput: block.input,
|
|
486
|
+
status: 'running',
|
|
487
|
+
});
|
|
488
|
+
if (item) {
|
|
489
|
+
items.push(item);
|
|
490
|
+
}
|
|
491
|
+
continue;
|
|
492
|
+
}
|
|
493
|
+
if (type === 'server_tool_use') {
|
|
494
|
+
const id = stringValue(block.id) ?? `${input.messageId}:server:${index}`;
|
|
495
|
+
const name = stringValue(block.name) ?? 'Server tool';
|
|
496
|
+
const item = toolUseToHistoryItem({
|
|
497
|
+
id,
|
|
498
|
+
name,
|
|
499
|
+
toolInput: block.input,
|
|
500
|
+
status: 'running',
|
|
501
|
+
});
|
|
502
|
+
if (item) {
|
|
503
|
+
items.push(item);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
return items;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
export function partialTextDelta(input: {
|
|
511
|
+
messageId: string;
|
|
512
|
+
event: unknown;
|
|
513
|
+
}): { itemId: string; delta: string } | null {
|
|
514
|
+
if (!isRecord(input.event)) {
|
|
515
|
+
return null;
|
|
516
|
+
}
|
|
517
|
+
if (input.event.type === 'content_block_start') {
|
|
518
|
+
const index = typeof input.event.index === 'number' ? input.event.index : 0;
|
|
519
|
+
const block = input.event.content_block;
|
|
520
|
+
if (!isRecord(block) || block.type !== 'text' || typeof block.text !== 'string' || !block.text) {
|
|
521
|
+
return null;
|
|
522
|
+
}
|
|
523
|
+
return {
|
|
524
|
+
itemId: `${input.messageId}:content:${index}`,
|
|
525
|
+
delta: block.text,
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
if (input.event.type !== 'content_block_delta') {
|
|
529
|
+
return null;
|
|
530
|
+
}
|
|
531
|
+
const index = typeof input.event.index === 'number' ? input.event.index : 0;
|
|
532
|
+
const delta = input.event.delta;
|
|
533
|
+
if (!isRecord(delta) || delta.type !== 'text_delta' || typeof delta.text !== 'string' || !delta.text) {
|
|
534
|
+
return null;
|
|
535
|
+
}
|
|
536
|
+
return {
|
|
537
|
+
itemId: `${input.messageId}:content:${index}`,
|
|
538
|
+
delta: delta.text,
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
export function partialReasoningDelta(input: {
|
|
543
|
+
messageId: string;
|
|
544
|
+
event: unknown;
|
|
545
|
+
}): AgentHistoryItem | null {
|
|
546
|
+
if (!isRecord(input.event)) {
|
|
547
|
+
return null;
|
|
548
|
+
}
|
|
549
|
+
const index = typeof input.event.index === 'number' ? input.event.index : 0;
|
|
550
|
+
const itemId = `${input.messageId}:content:${index}`;
|
|
551
|
+
|
|
552
|
+
if (input.event.type === 'content_block_start') {
|
|
553
|
+
const block = input.event.content_block;
|
|
554
|
+
if (!isRecord(block) || block.type !== 'thinking') {
|
|
555
|
+
return null;
|
|
556
|
+
}
|
|
557
|
+
const text = thinkingTextFromBlock(block) ?? '';
|
|
558
|
+
return {
|
|
559
|
+
id: itemId,
|
|
560
|
+
kind: 'reasoning',
|
|
561
|
+
text,
|
|
562
|
+
status: 'running',
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
if (input.event.type !== 'content_block_delta') {
|
|
567
|
+
return null;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
const delta = input.event.delta;
|
|
571
|
+
if (!isRecord(delta) || delta.type !== 'thinking_delta') {
|
|
572
|
+
return null;
|
|
573
|
+
}
|
|
574
|
+
const text = stringValue(delta.thinking) ?? stringValue(delta.text);
|
|
575
|
+
if (!text) {
|
|
576
|
+
return null;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
return {
|
|
580
|
+
id: itemId,
|
|
581
|
+
kind: 'reasoning',
|
|
582
|
+
text,
|
|
583
|
+
status: 'running',
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
export function toolUseFromPartialStart(input: {
|
|
588
|
+
messageId: string;
|
|
589
|
+
event: unknown;
|
|
590
|
+
}): AgentHistoryItem | null {
|
|
591
|
+
if (!isRecord(input.event) || input.event.type !== 'content_block_start') {
|
|
592
|
+
return null;
|
|
593
|
+
}
|
|
594
|
+
const index = typeof input.event.index === 'number' ? input.event.index : 0;
|
|
595
|
+
const block = input.event.content_block;
|
|
596
|
+
if (!isRecord(block)) {
|
|
597
|
+
return null;
|
|
598
|
+
}
|
|
599
|
+
if (block.type === 'tool_use') {
|
|
600
|
+
const name = stringValue(block.name) ?? 'Tool';
|
|
601
|
+
if (isSuppressedClaudeToolName(name)) {
|
|
602
|
+
return null;
|
|
603
|
+
}
|
|
604
|
+
return toolUseToHistoryItem({
|
|
605
|
+
id: stringValue(block.id) ?? `${input.messageId}:tool:${index}`,
|
|
606
|
+
name,
|
|
607
|
+
toolInput: block.input,
|
|
608
|
+
status: 'running',
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
if (block.type === 'mcp_tool_use') {
|
|
612
|
+
return toolUseToHistoryItem({
|
|
613
|
+
id: stringValue(block.id) ?? `${input.messageId}:mcp:${index}`,
|
|
614
|
+
name: stringValue(block.name) ?? 'MCP tool',
|
|
615
|
+
serverName: stringValue(block.server_name),
|
|
616
|
+
toolInput: block.input,
|
|
617
|
+
status: 'running',
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
if (block.type === 'server_tool_use') {
|
|
621
|
+
return toolUseToHistoryItem({
|
|
622
|
+
id: stringValue(block.id) ?? `${input.messageId}:server:${index}`,
|
|
623
|
+
name: stringValue(block.name) ?? 'Server tool',
|
|
624
|
+
toolInput: block.input,
|
|
625
|
+
status: 'running',
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
return null;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
export function resultForToolUse(
|
|
632
|
+
input: {
|
|
633
|
+
toolUseId: string;
|
|
634
|
+
result: unknown;
|
|
635
|
+
previous?: AgentHistoryItem | null;
|
|
636
|
+
},
|
|
637
|
+
): AgentHistoryItem {
|
|
638
|
+
if (input.previous) {
|
|
639
|
+
if (input.previous.kind === 'plan') {
|
|
640
|
+
const plan = isRecord(input.result) ? stringValue(input.result.plan) : null;
|
|
641
|
+
const nextItem: AgentHistoryItem = {
|
|
642
|
+
...input.previous,
|
|
643
|
+
text: plan ?? input.previous.text,
|
|
644
|
+
status: 'completed',
|
|
645
|
+
};
|
|
646
|
+
if (input.previous.detailText !== undefined) {
|
|
647
|
+
nextItem.detailText = input.previous.detailText;
|
|
648
|
+
}
|
|
649
|
+
return nextItem;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
return {
|
|
653
|
+
...input.previous,
|
|
654
|
+
detailText: [
|
|
655
|
+
input.previous.detailText?.trim() || input.previous.text,
|
|
656
|
+
'',
|
|
657
|
+
'Result:',
|
|
658
|
+
compactJson(input.result),
|
|
659
|
+
].join('\n'),
|
|
660
|
+
status: 'completed',
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
return {
|
|
665
|
+
id: input.toolUseId,
|
|
666
|
+
kind: 'toolCall',
|
|
667
|
+
text: 'Tool result',
|
|
668
|
+
previewText: 'Tool result',
|
|
669
|
+
detailText: compactJson(input.result),
|
|
670
|
+
status: 'completed',
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
export function buildAgentTurn(input: {
|
|
675
|
+
providerTurnId: string;
|
|
676
|
+
startedAt?: string | null;
|
|
677
|
+
status: AgentTurn['status'];
|
|
678
|
+
error?: string | null;
|
|
679
|
+
items: AgentHistoryItem[];
|
|
680
|
+
rawTurn?: unknown;
|
|
681
|
+
}): AgentTurn {
|
|
682
|
+
const turn: AgentTurn = {
|
|
683
|
+
providerTurnId: input.providerTurnId,
|
|
684
|
+
startedAt: input.startedAt ?? null,
|
|
685
|
+
status: input.status,
|
|
686
|
+
error: input.error ? { message: input.error } : null,
|
|
687
|
+
items: input.items,
|
|
688
|
+
};
|
|
689
|
+
if (input.rawTurn !== undefined) {
|
|
690
|
+
turn.rawTurn = input.rawTurn;
|
|
691
|
+
}
|
|
692
|
+
return turn;
|
|
693
|
+
}
|