@@ -7,6 +7,7 @@ import type {} from "@deepseek-ai/dsh-session";
77import type { } from "@deepseek-ai/dsh-tools" ;
88import z from "@deepseek-ai/schemastery" ;
99import {
10+ _internalCaptureAttachmentsEnabled ,
1011 Attachment ,
1112 initLogger ,
1213 NOOP_SPAN ,
@@ -49,6 +50,11 @@ export interface Config {
4950 orgName ?: string ;
5051 /** Optional Braintrust deployment URL. */
5152 appUrl ?: string ;
53+ /**
54+ * Capture inline Harness images as attachments.
55+ * Falls back to BRAINTRUST_CAPTURE_ATTACHMENTS when omitted.
56+ */
57+ captureAttachments ?: boolean ;
5258}
5359
5460export const Config : z < Config > = z . object ( {
@@ -57,6 +63,7 @@ export const Config: z<Config> = z.object({
5763 metadata : z . dict ( z . any ( ) ) ,
5864 orgName : z . string ( ) ,
5965 appUrl : z . string ( ) ,
66+ captureAttachments : z . boolean ( ) ,
6067} ) ;
6168
6269type TokenMetrics = Record < string , number > & {
@@ -112,56 +119,53 @@ async function normalizeBlocks(
112119 blocks : readonly HarnessContentBlock [ ] ,
113120 resolveImage : (
114121 ref : HarnessImageAttachmentRef ,
115- ) => Promise < Attachment | null | undefined > ,
122+ ) => Promise < Attachment | undefined > ,
116123) : Promise < unknown > {
117124 if ( blocks . length === 0 ) return "" ;
118125 if ( blocks . every ( ( block ) => block . type === "text" ) ) {
119126 return contentText ( blocks ) ;
120127 }
121- return (
122- await Promise . all (
123- blocks . map ( async ( block ) => {
124- switch ( block . type ) {
125- case "text" :
126- return { type : "text" , text : block . text ?? "" } ;
127- case "reasoning" :
128- return { type : "reasoning" , text : block . text ?? "" } ;
129- case "tool-call" :
130- return {
131- type : "tool_call" ,
132- id : block . id ,
133- name : block . name ,
134- arguments : block . arguments ,
135- } ;
136- case "tool-result" :
137- return {
138- type : "tool_result" ,
139- tool_call_id : block . toolCallId ,
140- content : await normalizeBlocks ( block . content ?? [ ] , resolveImage ) ,
141- ...( block . isError ? { is_error : true } : { } ) ,
142- } ;
143- case "image" : {
144- const attachment = block . attachment
145- ? await resolveImage ( block . attachment )
146- : undefined ;
147- if ( attachment === null ) return undefined ;
148- return attachment
149- ? { type : "image_url" , image_url : { url : attachment } }
150- : { type : "image" , attachment : block . attachment } ;
151- }
152- default :
153- return { type : block . type } ;
128+ return Promise . all (
129+ blocks . map ( async ( block ) => {
130+ switch ( block . type ) {
131+ case "text" :
132+ return { type : "text" , text : block . text ?? "" } ;
133+ case "reasoning" :
134+ return { type : "reasoning" , text : block . text ?? "" } ;
135+ case "tool-call" :
136+ return {
137+ type : "tool_call" ,
138+ id : block . id ,
139+ name : block . name ,
140+ arguments : block . arguments ,
141+ } ;
142+ case "tool-result" :
143+ return {
144+ type : "tool_result" ,
145+ tool_call_id : block . toolCallId ,
146+ content : await normalizeBlocks ( block . content ?? [ ] , resolveImage ) ,
147+ ...( block . isError ? { is_error : true } : { } ) ,
148+ } ;
149+ case "image" : {
150+ const attachment = block . attachment
151+ ? await resolveImage ( block . attachment )
152+ : undefined ;
153+ return attachment
154+ ? { type : "image_url" , image_url : { url : attachment } }
155+ : { type : "image" , attachment : block . attachment } ;
154156 }
155- } ) ,
156- )
157- ) . filter ( ( block ) => block !== undefined ) ;
157+ default :
158+ return { type : block . type } ;
159+ }
160+ } ) ,
161+ ) ;
158162}
159163
160164async function normalizeMessage (
161165 message : HarnessMessage ,
162166 resolveImage : (
163167 ref : HarnessImageAttachmentRef ,
164- ) => Promise < Attachment | null | undefined > ,
168+ ) => Promise < Attachment | undefined > ,
165169) : Promise < Record < string , unknown > > {
166170 const toolResult = message . content . find (
167171 ( block ) => block . type === "tool-result" ,
@@ -202,7 +206,7 @@ async function normalizeInput(
202206 options : HarnessGenerateOptions ,
203207 resolveImage : (
204208 ref : HarnessImageAttachmentRef ,
205- ) => Promise < Attachment | null | undefined > ,
209+ ) => Promise < Attachment | undefined > ,
206210) : Promise < unknown [ ] > {
207211 return [
208212 ...( options . system ? [ { role : "system" , content : options . system } ] : [ ] ) ,
@@ -376,6 +380,7 @@ export function apply(ctx: Context, config: Config = {}): void {
376380 projectName : config . projectName ?? DEFAULT_PROJECT_NAME ,
377381 ...( config . orgName ? { orgName : config . orgName } : { } ) ,
378382 ...( config . appUrl ? { appUrl : config . appUrl } : { } ) ,
383+ captureAttachments : config . captureAttachments ,
379384 setCurrent : false ,
380385 } ) ;
381386 const turns = new Map < string , Map < number , TurnState > > ( ) ;
@@ -396,9 +401,8 @@ export function apply(ctx: Context, config: Config = {}): void {
396401
397402 const resolveImage = async (
398403 ref : HarnessImageAttachmentRef ,
399- ) : Promise < Attachment | null | undefined > => {
400- if ( ! logger . loggingState . _internalCaptureAttachmentsEnabled ( logger ) )
401- return null ;
404+ ) : Promise < Attachment | undefined > => {
405+ if ( ! _internalCaptureAttachmentsEnabled ( logger ) ) return undefined ;
402406 const attachmentStore = (
403407 ctx as Context & { attachments ?: HarnessAttachmentStore }
404408 ) . attachments ;
0 commit comments