@@ -83,6 +83,21 @@ function applyOutputSchemaPrompt(input: PromptInput, schema?: object): PromptInp
8383 ] ;
8484}
8585
86+ function shouldUseNativeStructuredOutput ( schema ?: object ) : boolean {
87+ return ! ! schema ;
88+ }
89+
90+ function extractNativeStructuredOutput ( result : any ) : unknown | undefined {
91+ if ( ! result ) return undefined ;
92+ if ( Object . prototype . hasOwnProperty . call ( result , 'structured_output' ) ) {
93+ return ( result as any ) . structured_output ;
94+ }
95+ if ( Object . prototype . hasOwnProperty . call ( result , 'structuredOutput' ) ) {
96+ return ( result as any ) . structuredOutput ;
97+ }
98+ return undefined ;
99+ }
100+
86101function extractJsonPayload ( text : string | undefined ) : unknown | undefined {
87102 if ( ! text ) return undefined ;
88103 const fenced = text . match ( / ` ` ` j s o n \s * ( [ \s \S ] + ?) ` ` ` / i) ;
@@ -176,11 +191,18 @@ export class ClaudeAdapter implements HeadlessCoder {
176191 * Returns:
177192 * Options ready for the Claude Agent SDK.
178193 */
179- private buildOptions ( state : ClaudeThreadState , runOpts ?: RunOpts ) : Options {
194+ private buildOptions ( state : ClaudeThreadState , runOpts ?: RunOpts , useNativeStructuredOutput ?: boolean ) : Options {
180195 const startOpts = state . opts ?? { } ;
181196 const resumeId = state . resume ? state . sessionId : undefined ;
182197 const permissionMode : PermissionMode | undefined =
183198 ( startOpts . permissionMode as PermissionMode | undefined ) ?? ( startOpts . yolo ? 'bypassPermissions' : undefined ) ;
199+ const outputFormat =
200+ useNativeStructuredOutput && runOpts ?. outputSchema
201+ ? {
202+ type : 'json_schema' as const ,
203+ schema : runOpts . outputSchema as Record < string , unknown > ,
204+ }
205+ : undefined ;
184206 return {
185207 cwd : startOpts . workingDirectory ,
186208 allowedTools : startOpts . allowedTools ,
@@ -192,6 +214,7 @@ export class ClaudeAdapter implements HeadlessCoder {
192214 model : startOpts . model ,
193215 permissionMode,
194216 permissionPromptToolName : startOpts . permissionPromptToolName ,
217+ outputFormat,
195218 } ;
196219 }
197220
@@ -213,9 +236,10 @@ export class ClaudeAdapter implements HeadlessCoder {
213236 ensureNodeRuntime ( 'run Claude' ) ;
214237 const state = thread . internal as ClaudeThreadState ;
215238 this . assertIdle ( state ) ;
216- const structuredPrompt = applyOutputSchemaPrompt ( toPrompt ( input ) , runOpts ?. outputSchema ) ;
217- const prompt = toPrompt ( structuredPrompt ) ;
218- const options = this . buildOptions ( state , runOpts ) ;
239+ const useNativeStructuredOutput = shouldUseNativeStructuredOutput ( runOpts ?. outputSchema ) ;
240+ const promptInput = useNativeStructuredOutput ? input : applyOutputSchemaPrompt ( input , runOpts ?. outputSchema ) ;
241+ const prompt = toPrompt ( promptInput ) ;
242+ const options = this . buildOptions ( state , runOpts , useNativeStructuredOutput ) ;
219243 const generator = query ( { prompt, options } ) ;
220244 const active = this . registerRun ( state , generator , runOpts ?. signal ) ;
221245 let lastAssistant = '' ;
@@ -255,7 +279,9 @@ export class ClaudeAdapter implements HeadlessCoder {
255279 if ( finalResult && claudeResultIndicatesError ( finalResult ) ) {
256280 throw new Error ( buildClaudeResultErrorMessage ( finalResult ) ) ;
257281 }
258- const structured = runOpts ?. outputSchema ? extractJsonPayload ( lastAssistant ) : undefined ;
282+ const structured = runOpts ?. outputSchema
283+ ? extractNativeStructuredOutput ( finalResult ) ?? extractJsonPayload ( lastAssistant )
284+ : undefined ;
259285 return { threadId : state . sessionId , text : lastAssistant , raw : finalResult , json : structured } ;
260286 }
261287
@@ -281,9 +307,10 @@ export class ClaudeAdapter implements HeadlessCoder {
281307 ensureNodeRuntime ( 'stream Claude events' ) ;
282308 const state = thread . internal as ClaudeThreadState ;
283309 this . assertIdle ( state ) ;
284- const structuredPrompt = applyOutputSchemaPrompt ( toPrompt ( input ) , runOpts ?. outputSchema ) ;
285- const prompt = toPrompt ( structuredPrompt ) ;
286- const options = this . buildOptions ( state , runOpts ) ;
310+ const useNativeStructuredOutput = shouldUseNativeStructuredOutput ( runOpts ?. outputSchema ) ;
311+ const promptInput = useNativeStructuredOutput ? input : applyOutputSchemaPrompt ( input , runOpts ?. outputSchema ) ;
312+ const prompt = toPrompt ( promptInput ) ;
313+ const options = this . buildOptions ( state , runOpts , useNativeStructuredOutput ) ;
287314 const generator = query ( { prompt, options } ) ;
288315 const adapter = this ;
289316
0 commit comments