55 */
66
77import {
8+ findDuplicateAttachment ,
89 findImagePathMentions ,
910 type AttachImageResult ,
1011 type PendingImageAttachment ,
@@ -20,13 +21,15 @@ export interface PathMentionIngestion {
2021
2122/**
2223 * Replace image paths written inline in the prompt with attachment markers and
23- * return the loaded attachments. `load` is injected so this stays testable
24- * without touching the filesystem.
24+ * return only attachments whose content hash is not already in `pending` or
25+ * earlier in this batch. Duplicate tokens still rewrite to the kept name.
26+ * `load` is injected so this stays testable without touching the filesystem.
2527 */
2628export async function ingestPathMentions (
2729 text : string ,
2830 cwd : string ,
2931 load : ( path : string ) => Promise < AttachImageResult > ,
32+ pending : readonly PendingImageAttachment [ ] = [ ] ,
3033) : Promise < PathMentionIngestion > {
3134 const mentions = findImagePathMentions ( text , cwd ) ;
3235 if ( mentions . length === 0 ) return { text, attachments : [ ] } ;
@@ -37,8 +40,12 @@ export async function ingestPathMentions(
3740 for ( const [ index , mention ] of mentions . entries ( ) ) {
3841 const result = loaded [ index ] ;
3942 if ( result === undefined || ! result . ok ) continue ;
40- attachments . push ( result . attachment ) ;
41- out = out . replace ( mention . raw , `[Attached image: ${ result . attachment . name } ]` ) ;
43+ const kept =
44+ findDuplicateAttachment ( pending , result . attachment ) ??
45+ findDuplicateAttachment ( attachments , result . attachment ) ??
46+ result . attachment ;
47+ if ( kept === result . attachment ) attachments . push ( result . attachment ) ;
48+ out = out . replace ( mention . raw , `[Attached image: ${ kept . name } ]` ) ;
4249 }
4350 return { text : out , attachments } ;
4451}
@@ -53,7 +60,7 @@ export async function ingestOperatorPrompt(
5360 load : ( path : string ) => Promise < AttachImageResult > ,
5461 pending : readonly PendingImageAttachment [ ] = [ ] ,
5562) : Promise < PathMentionIngestion > {
56- const ingested = await ingestPathMentions ( text , cwd , load ) ;
63+ const ingested = await ingestPathMentions ( text , cwd , load , pending ) ;
5764 const resolved = await resolveAtMentions ( ingested . text , cwd ) ;
5865 return { text : resolved , attachments : [ ...pending , ...ingested . attachments ] } ;
5966}
0 commit comments