Skip to content

Commit c855571

Browse files
committed
debugging opencode failures on gpt-5-codex
1 parent afa65d2 commit c855571

4 files changed

Lines changed: 34 additions & 119 deletions

File tree

.github/workflows/compare-models.yml

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,10 @@ name: Compare Models
22
on:
33
workflow_dispatch:
44
inputs:
5-
codex_gpt_5_codex:
6-
description: codex:gpt-5-codex
7-
type: boolean
8-
default: false
95
opencode_opencode_gpt_5_codex:
106
description: opencode:opencode/gpt-5-codex
117
type: boolean
128
default: false
13-
opencode_opencode_claude_sonnet_4_5:
14-
description: opencode:opencode/claude-sonnet-4-5
15-
type: boolean
16-
default: false
17-
opencode_opencode_big_pickle:
18-
description: opencode:opencode/big-pickle
19-
type: boolean
20-
default: false
21-
claude_code_claude_sonnet_4_5:
22-
description: claude-code:claude-sonnet-4-5
23-
type: boolean
24-
default: false
259
permissions:
2610
contents: read
2711
actions: read

agents/opencode.ts

Lines changed: 31 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -22,97 +22,39 @@ const DEFAULT_PERMISSION_CONFIG: NonNullable<OpencodeConfig["permission"]> = {
2222
webfetch: "allow",
2323
};
2424

25-
// Custom fetch with extended timeout and better error handling
26-
const customFetch: typeof fetch = async (input, init) => {
27-
const url = typeof input === "string" ? input : input.url;
28-
const method = init?.method || "GET";
25+
// Custom fetch with focused error logging
26+
const customFetch = async (request: Request): Promise<Response> => {
2927
const startTime = Date.now();
3028

31-
// Log request details
32-
console.error(`[opencode] ============ FETCH REQUEST START ============`);
33-
console.error(`[opencode] ${method} ${url}`);
34-
console.error(`[opencode] Timeout: 600000ms (10 minutes)`);
35-
36-
if (init?.headers) {
37-
console.error(`[opencode] Request Headers:`);
38-
const headers = init.headers;
39-
if (headers instanceof Headers) {
40-
headers.forEach((value, key) => {
41-
console.error(`[opencode] ${key}: ${key.toLowerCase().includes('auth') || key.toLowerCase().includes('key') ? '***REDACTED***' : value}`);
42-
});
43-
} else if (typeof headers === "object") {
44-
Object.entries(headers).forEach(([key, value]) => {
45-
console.error(`[opencode] ${key}: ${key.toLowerCase().includes('auth') || key.toLowerCase().includes('key') ? '***REDACTED***' : value}`);
46-
});
47-
}
48-
} else {
49-
console.error(`[opencode] Request Headers: none`);
50-
}
51-
52-
if (init?.body) {
53-
const bodyStr = typeof init.body === "string" ? init.body : String(init.body);
54-
console.error(`[opencode] Request Body Length: ${bodyStr.length} bytes`);
55-
// Don't log full body to avoid sensitive data, but show a preview
56-
if (bodyStr.length < 500) {
57-
console.error(`[opencode] Request Body Preview: ${bodyStr.substring(0, 200)}...`);
58-
}
59-
}
60-
6129
try {
62-
// Extend timeout to 10 minutes for long-running LLM requests
63-
const controller = new AbortController();
64-
const timeoutId = setTimeout(() => {
65-
const duration = Date.now() - startTime;
66-
console.error(`[opencode] ⏱️ REQUEST TIMEOUT after ${duration}ms`);
67-
controller.abort();
68-
}, 600_000); // 10 minutes
69-
70-
const response = await fetch(input, {
71-
...init,
72-
signal: controller.signal,
73-
});
74-
75-
clearTimeout(timeoutId);
30+
const response = await fetch(request);
7631
const duration = Date.now() - startTime;
7732

78-
console.error(`[opencode] ============ FETCH RESPONSE ============`);
79-
console.error(`[opencode] Status: ${response.status} ${response.statusText}`);
80-
console.error(`[opencode] Duration: ${duration}ms`);
81-
82-
// Log response headers
83-
console.error(`[opencode] Response Headers:`);
84-
response.headers.forEach((value, key) => {
85-
console.error(`[opencode] ${key}: ${value}`);
86-
});
87-
88-
if (!response.ok) {
89-
const responseText = await response.clone().text().catch(() => "Unable to read body");
90-
console.error(`[opencode] ❌ HTTP ${response.status} Response Body:`);
91-
console.error(responseText.substring(0, 1000));
92-
} else {
93-
console.error(`[opencode] ✅ Request successful`);
33+
// Only log non-OK responses or slow requests
34+
if (!response.ok || duration > 60000) {
35+
console.error(`[opencode] Request to ${request.url} - Status: ${response.status}, Duration: ${duration}ms`);
36+
37+
if (!response.ok) {
38+
try {
39+
const clonedResponse = response.clone();
40+
const responseText = await clonedResponse.text();
41+
console.error(`[opencode] Full error response body:`, responseText);
42+
} catch (e) {
43+
console.error(`[opencode] Could not read error response body`);
44+
}
45+
}
9446
}
9547

96-
console.error(`[opencode] ============ FETCH REQUEST END ============`);
9748
return response;
9849
} catch (error) {
9950
const duration = Date.now() - startTime;
100-
console.error(`[opencode] ============ FETCH ERROR ============`);
101-
console.error(`[opencode] ❌ Fetch failed for ${method} ${url}`);
102-
console.error(`[opencode] Duration: ${duration}ms`);
103-
console.error(`[opencode] Error Type: ${error instanceof Error ? error.name : typeof error}`);
104-
console.error(`[opencode] Error Message: ${error instanceof Error ? error.message : String(error)}`);
51+
console.error(`[opencode] FETCH FAILED - URL: ${request.url}, Duration: ${duration}ms`);
52+
console.error(`[opencode] Error: ${error instanceof Error ? error.message : String(error)}`);
10553

10654
if (error instanceof Error && error.stack) {
107-
console.error(`[opencode] Error Stack:`);
108-
console.error(error.stack);
109-
}
110-
111-
if (error && typeof error === "object" && "cause" in error) {
112-
console.error(`[opencode] Error Cause:`, error.cause);
55+
console.error(`[opencode] Stack:`, error.stack);
11356
}
11457

115-
console.error(`[opencode] ============ FETCH ERROR END ============`);
11658
throw error;
11759
}
11860
};
@@ -121,11 +63,12 @@ const opencodePort = await detectPort(4096);
12163

12264
const opencode = await createOpencode({
12365
port: opencodePort,
66+
timeout: 600_000, // 10 minutes timeout for long-running LLM requests
12467
config: {
12568
permission: DEFAULT_PERMISSION_CONFIG,
12669
},
127-
fetch: customFetch,
12870
});
71+
12972
process.once("beforeExit", () => opencode.server.close());
13073

13174
const sessionCache = new Map<string, string>();
@@ -258,18 +201,12 @@ const opencodeAgent: AgentDefinition = {
258201

259202
let sessionID = sessionCache.get(cacheKey);
260203
if (!sessionID) {
261-
console.error(`[opencode] Creating new session for ${cacheKey} in directory ${cwd}`);
262-
const sessionStartTime = Date.now();
263204
const { data: session } = await opencode.client.session.create({
264205
query: { directory: cwd },
265206
throwOnError: true,
266207
});
267-
const sessionDuration = Date.now() - sessionStartTime;
268208
sessionID = session.id;
269209
sessionCache.set(cacheKey, sessionID);
270-
console.error(`[opencode] Session created with ID ${sessionID} in ${sessionDuration}ms`);
271-
} else {
272-
console.error(`[opencode] Reusing cached session ${sessionID} for ${cacheKey}`);
273210
}
274211

275212
const actions: string[] = [];
@@ -279,11 +216,7 @@ const opencodeAgent: AgentDefinition = {
279216
};
280217
try {
281218
const [providerID, modelID] = model.split("/");
282-
console.error(`[opencode] Sending prompt to session ${sessionID} with model ${providerID}/${modelID}`);
283-
console.error(`[opencode] Prompt length: ${prompt.length} characters`);
284-
console.error(`[opencode] Working directory: ${cwd}`);
285219

286-
const promptStartTime = Date.now();
287220
const { data } = await opencode.client.session.prompt({
288221
path: { id: sessionID! },
289222
query: { directory: cwd },
@@ -295,15 +228,17 @@ const opencodeAgent: AgentDefinition = {
295228
parts: [{ type: "text", text: prompt }],
296229
},
297230
throwOnError: true,
231+
fetch: customFetch,
298232
});
299-
const promptDuration = Date.now() - promptStartTime;
300-
301-
console.error(`[opencode] Prompt completed in ${promptDuration}ms`);
302-
console.error(`[opencode] Response parts count: ${Array.isArray(data.parts) ? data.parts.length : 'invalid'}`);
303-
console.error(`[opencode] Token usage: input=${data.info.tokens.input}, output=${data.info.tokens.output}`);
304233

305-
usage.input = data.info.tokens.input;
306-
usage.output = data.info.tokens.output;
234+
if (data.info?.tokens) {
235+
usage.input = data.info.tokens.input || 0;
236+
usage.output = data.info.tokens.output || 0;
237+
} else {
238+
console.error(
239+
`[opencode] WARNING: No token usage in response. Available fields: ${Object.keys(data.info || {}).join(", ")}`,
240+
);
241+
}
307242

308243
actions.push(JSON.stringify(data.info));
309244
if (Array.isArray(data.parts)) {
@@ -312,11 +247,7 @@ const opencodeAgent: AgentDefinition = {
312247

313248
logPromptResult(data, options);
314249
} catch (error) {
315-
console.error(`[opencode] Error occurred during prompt execution for session ${sessionID}`);
316-
console.error(`[opencode] Model: ${model}, CWD: ${cwd}`);
317-
console.error(`[opencode] Error details:`, serializeError(error));
318-
console.error(`[opencode] Clearing session cache for ${cacheKey}`);
319-
250+
console.error(`[opencode] Error in ${model}:`, error instanceof Error ? error.message : String(error));
320251
sessionCache.delete(cacheKey);
321252
logError(
322253
{

bun.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"@anthropic-ai/claude-agent-sdk": "^0.1.0",
3131
"@octokit/request": "^10.0.5",
3232
"@openai/codex-sdk": "^0.47.0",
33-
"@opencode-ai/sdk": "^0.15.8",
33+
"@opencode-ai/sdk": "^0.15.31",
3434
"ai": "^5.0.64",
3535
"detect-port": "^2.1.0",
3636
"yaml": "^2.4.5",

0 commit comments

Comments
 (0)