@@ -22,23 +22,58 @@ const DEFAULT_PERMISSION_CONFIG: NonNullable<OpencodeConfig["permission"]> = {
2222 webfetch : "allow" ,
2323} ;
2424
25+ // Custom fetch with 25-minute timeout
26+ const customFetch = async ( request : Request ) : Promise < Response > => {
27+ const startTime = Date . now ( ) ;
28+
29+ try {
30+ const response = await fetch ( request , {
31+ signal : AbortSignal . timeout ( 1_500_000 ) ,
32+ } ) ;
33+ const duration = Date . now ( ) - startTime ;
34+ console . error ( `[opencode] Request completed - Duration: ${ duration } ms` ) ;
35+ return response ;
36+ } catch ( error ) {
37+ const duration = Date . now ( ) - startTime ;
38+ console . error ( `[opencode] Request failed - Duration: ${ duration } ms` ) ;
39+ throw error ;
40+ }
41+ } ;
42+
2543const opencodePort = await detectPort ( 4096 ) ;
2644
45+ // Set OpenCode config before server starts to ensure timeout is applied
46+ const opencodeConfig = {
47+ permission : DEFAULT_PERMISSION_CONFIG ,
48+ provider : {
49+ opencode : {
50+ options : {
51+ timeout : false as false , // Disable timeout for OpenCode provider requests
52+ } ,
53+ } ,
54+ } ,
55+ } satisfies OpencodeConfig ;
56+
57+ // CRITICAL: Set via environment variable BEFORE importing/creating anything
58+ // The SDK reads this when spawning the server process
59+ const configJson = JSON . stringify ( opencodeConfig ) ;
60+ process . env . OPENCODE_CONFIG_CONTENT = configJson ;
61+
62+ console . error ( `[opencode] Setting config: ${ configJson } ` ) ;
63+
2764const opencode = await createOpencode ( {
2865 port : opencodePort ,
29- config : {
30- permission : DEFAULT_PERMISSION_CONFIG ,
31- } ,
66+ timeout : 1_500_000 , // 25 minutes timeout for server startup
67+ config : opencodeConfig ,
3268} ) ;
33- process . once ( "beforeExit" , ( ) => opencode . server . close ( ) ) ;
3469
3570const sessionCache = new Map < string , string > ( ) ;
3671
3772export const models : string [ ] = [
3873 // "opencode/gpt-5",
3974 "opencode/gpt-5-codex" ,
40- "opencode/claude-sonnet-4-5" ,
41- "opencode/big-pickle" ,
75+ // "opencode/claude-sonnet-4-5",
76+ // "opencode/big-pickle",
4277 // "opencode/claude-sonnet-4",
4378 // "opencode/claude-3-5-haiku",
4479 // "opencode/claude-opus-4-1",
@@ -73,33 +108,36 @@ function writeLog(
73108
74109function logJson ( value : unknown , options : AgentRunOptions | undefined ) : void {
75110 try {
76- writeLog ( process . stdout , JSON . stringify ( value ) , options ?. logPrefix ) ;
111+ const message = JSON . stringify ( value ) ;
112+ writeLog ( process . stdout , message , options ?. logPrefix ) ;
77113 } catch ( error ) {
78114 const reason = error instanceof Error ? error . message : String ( error ) ;
79- writeLog (
80- process . stdout ,
81- JSON . stringify ( { error : "serialization_failed" , reason } ) ,
82- options ?. logPrefix ,
83- ) ;
115+ const errorMessage = JSON . stringify ( {
116+ error : "serialization_failed" ,
117+ reason,
118+ } ) ;
119+ writeLog ( process . stdout , errorMessage , options ?. logPrefix ) ;
84120 }
85121}
86122
87123function logError ( value : unknown , options : AgentRunOptions | undefined ) : void {
88124 try {
89- writeLog ( process . stderr , JSON . stringify ( value ) , options ?. logPrefix ) ;
125+ const message = JSON . stringify ( value ) ;
126+ writeLog ( process . stderr , message , options ?. logPrefix ) ;
90127 } catch ( error ) {
91128 const reason = error instanceof Error ? error . message : String ( error ) ;
92- writeLog (
93- process . stderr ,
94- JSON . stringify ( { error : "serialization_failed" , reason } ) ,
95- options ?. logPrefix ,
96- ) ;
129+ const errorMessage = JSON . stringify ( {
130+ error : "serialization_failed" ,
131+ reason,
132+ } ) ;
133+ writeLog ( process . stderr , errorMessage , options ?. logPrefix ) ;
97134 }
98135}
99136
100137function logPromptResult (
101138 result : { info : AssistantMessage ; parts : Part [ ] } ,
102139 options : AgentRunOptions | undefined ,
140+ logs ?: string [ ] ,
103141) : void {
104142 logJson ( { info : result . info } , options ) ;
105143 if ( Array . isArray ( result . parts ) ) {
@@ -167,8 +205,14 @@ const opencodeAgent: AgentDefinition = {
167205 sessionCache . set ( cacheKey , sessionID ) ;
168206 }
169207
208+ const actions : string [ ] = [ ] ;
209+ const usage = {
210+ input : 0 ,
211+ output : 0 ,
212+ } ;
170213 try {
171214 const [ providerID , modelID ] = model . split ( "/" ) ;
215+
172216 const { data } = await opencode . client . session . prompt ( {
173217 path : { id : sessionID ! } ,
174218 query : { directory : cwd } ,
@@ -180,10 +224,29 @@ const opencodeAgent: AgentDefinition = {
180224 parts : [ { type : "text" , text : prompt } ] ,
181225 } ,
182226 throwOnError : true ,
227+ fetch : customFetch ,
183228 } ) ;
184229
230+ if ( data . info ?. tokens ) {
231+ usage . input = data . info . tokens . input || 0 ;
232+ usage . output = data . info . tokens . output || 0 ;
233+ } else {
234+ console . error (
235+ `[opencode] WARNING: No token usage in response. Available fields: ${ Object . keys ( data . info || { } ) . join ( ", " ) } ` ,
236+ ) ;
237+ }
238+
239+ actions . push ( JSON . stringify ( data . info ) ) ;
240+ if ( Array . isArray ( data . parts ) ) {
241+ data . parts . forEach ( ( part ) => actions . push ( JSON . stringify ( part ) ) ) ;
242+ }
243+
185244 logPromptResult ( data , options ) ;
186245 } catch ( error ) {
246+ console . error (
247+ `[opencode] Error in ${ model } :` ,
248+ error instanceof Error ? error . message : String ( error ) ,
249+ ) ;
187250 sessionCache . delete ( cacheKey ) ;
188251 logError (
189252 {
@@ -195,7 +258,10 @@ const opencodeAgent: AgentDefinition = {
195258 throw error ;
196259 }
197260
198- return { command : displayCommand } ;
261+ return { command : displayCommand , actions, usage } ;
262+ } ,
263+ cleanup ( ) {
264+ opencode . server . close ( ) ;
199265 } ,
200266} ;
201267
0 commit comments