@@ -38,12 +38,31 @@ vi.mock("../../../plugins/host-hook-state.js", () => hostHookStateMocks);
3838
3939import {
4040 forgetPromptBuildDrainCacheForRun ,
41+ mergeOrphanedTrailingUserPrompt ,
4142 resolvePromptSubmissionSkipReason ,
4243 resolveAttemptMediaTaskSystemPromptAddition ,
4344 resolvePromptBuildHookResult ,
4445 shouldInjectHeartbeatPrompt ,
4546} from "./attempt.prompt-helpers.js" ;
4647
48+ function hasLoneSurrogate ( value : string ) : boolean {
49+ for ( let index = 0 ; index < value . length ; index += 1 ) {
50+ const code = value . charCodeAt ( index ) ;
51+ if ( code >= 0xd800 && code <= 0xdbff ) {
52+ const next = value . charCodeAt ( index + 1 ) ;
53+ if ( ! ( next >= 0xdc00 && next <= 0xdfff ) ) {
54+ return true ;
55+ }
56+ index += 1 ;
57+ continue ;
58+ }
59+ if ( code >= 0xdc00 && code <= 0xdfff ) {
60+ return true ;
61+ }
62+ }
63+ return false ;
64+ }
65+
4766describe ( "shouldInjectHeartbeatPrompt" , ( ) => {
4867 it ( "keeps global heartbeat guidance out of commitment-only runs" , ( ) => {
4968 const heartbeatParams = {
@@ -64,6 +83,31 @@ describe("shouldInjectHeartbeatPrompt", () => {
6483 } ) ;
6584} ) ;
6685
86+ describe ( "mergeOrphanedTrailingUserPrompt" , ( ) => {
87+ it ( "keeps structured media and JSON summaries on UTF-16 boundaries" , ( ) => {
88+ const result = mergeOrphanedTrailingUserPrompt ( {
89+ prompt : "Continue." ,
90+ trigger : "user" ,
91+ leafMessage : {
92+ content : [
93+ {
94+ type : "image_url" ,
95+ image_url : { url : `${ "u" . repeat ( 299 ) } 😀tail` } ,
96+ } ,
97+ {
98+ [ `${ "k" . repeat ( 997 ) } 😀tail` ] : 1 ,
99+ } ,
100+ ] ,
101+ } ,
102+ } ) ;
103+
104+ expect ( result . merged ) . toBe ( true ) ;
105+ expect ( hasLoneSurrogate ( result . prompt ) ) . toBe ( false ) ;
106+ expect ( result . prompt ) . toContain ( "[image_url]" ) ;
107+ expect ( result . prompt ) . toContain ( "chars)" ) ;
108+ } ) ;
109+ } ) ;
110+
67111describe ( "resolveAttemptMediaTaskSystemPromptAddition" , ( ) => {
68112 it ( "joins active media task guidance for user triggers" , ( ) => {
69113 imageGenerationTaskStatusMocks . buildActiveImageGenerationTaskPromptContextForSession . mockReturnValue (
0 commit comments