@@ -392,6 +392,21 @@ describe("compaction-safeguard tool failures", () => {
392392 expect ( section ) . toContain ( "exec (exitCode=2): failed" ) ;
393393 } ) ;
394394
395+ it ( "keeps bounded tool-failure text UTF-16 safe" , ( ) => {
396+ const failures = collectToolFailures ( [
397+ {
398+ role : "toolResult" ,
399+ toolCallId : "call-boundary" ,
400+ toolName : "exec" ,
401+ isError : true ,
402+ content : [ { type : "text" , text : `${ "x" . repeat ( 236 ) } 🚀tail` } ] ,
403+ timestamp : Date . now ( ) ,
404+ } ,
405+ ] ) ;
406+
407+ expect ( failures [ 0 ] ?. summary ) . toBe ( `${ "x" . repeat ( 236 ) } ...` ) ;
408+ } ) ;
409+
395410 it ( "caps the number of failures and adds overflow line" , ( ) => {
396411 const messages : AgentMessage [ ] = Array . from ( { length : 9 } , ( _ , idx ) => ( {
397412 role : "toolResult" ,
@@ -454,6 +469,18 @@ describe("compaction-safeguard summary budgets", () => {
454469 expect ( capped . endsWith ( SUMMARY_TRUNCATED_MARKER ) ) . toBe ( true ) ;
455470 } ) ;
456471
472+ it ( "keeps compaction summary prefixes UTF-16 safe" , ( ) => {
473+ const prefixBudget = MAX_COMPACTION_SUMMARY_CHARS - SUMMARY_TRUNCATED_MARKER . length ;
474+ const oversized = `${ "x" . repeat ( prefixBudget - 1 ) } 🚀${ "z" . repeat (
475+ SUMMARY_TRUNCATED_MARKER . length + 10 ,
476+ ) } `;
477+
478+ expect ( capCompactionSummary ( oversized ) ) . toBe (
479+ `${ "x" . repeat ( prefixBudget - 1 ) } ${ SUMMARY_TRUNCATED_MARKER } ` ,
480+ ) ;
481+ expect ( capCompactionSummary ( `${ "x" . repeat ( 9 ) } 🚀tail` , 10 ) ) . toBe ( "x" . repeat ( 9 ) ) ;
482+ } ) ;
483+
457484 it ( "preserves workspace critical rules suffix when capping" , ( ) => {
458485 const suffix =
459486 "\n\n<workspace-critical-rules>\n## Session Startup\nRead AGENTS.md\n</workspace-critical-rules>" ;
@@ -522,6 +549,10 @@ describe("compaction-safeguard summary budgets", () => {
522549 expect ( capped ) . toContain ( "<read-files>" ) ;
523550 expect ( capped ) . toContain ( "## Session Startup" ) ;
524551 } ) ;
552+
553+ it ( "keeps an oversized preserved suffix UTF-16 safe at its leading edge" , ( ) => {
554+ expect ( capCompactionSummaryPreservingSuffix ( "body" , "A🚀tail" , 5 ) ) . toBe ( "tail" ) ;
555+ } ) ;
525556} ) ;
526557
527558describe ( "computeAdaptiveChunkRatio" , ( ) => {
@@ -906,6 +937,18 @@ describe("compaction-safeguard recent-turn preservation", () => {
906937 expect ( section ) . toContain ( "[non-text content: image]" ) ;
907938 } ) ;
908939
940+ it ( "keeps bounded preserved-turn text UTF-16 safe" , ( ) => {
941+ const section = formatPreservedTurnsSection ( [
942+ {
943+ role : "user" ,
944+ content : `${ "x" . repeat ( 599 ) } 🚀tail` ,
945+ timestamp : 1 ,
946+ } ,
947+ ] ) ;
948+
949+ expect ( section ) . toContain ( `- User: ${ "x" . repeat ( 599 ) } ...` ) ;
950+ } ) ;
951+
909952 it ( "does not add non-text placeholders for text-only content blocks" , ( ) => {
910953 const section = formatPreservedTurnsSection ( [
911954 {
@@ -2692,6 +2735,14 @@ describe("readWorkspaceContextForSummary", () => {
26922735 expect ( result ) . not . toContain ( "Ignore me" ) ;
26932736 } ) ;
26942737
2738+ it ( "keeps bounded workspace rules UTF-16 safe" , async ( ) => {
2739+ const heading = "## Session Startup\n" ;
2740+ const safePrefix = `${ heading } ${ "x" . repeat ( 1_999 - heading . length ) } ` ;
2741+ const result = await withWorkspaceSummary ( `${ safePrefix } 🚀tail\n` , [ "Session Startup" ] ) ;
2742+
2743+ expect ( result ) . toContain ( `${ safePrefix } \n...[truncated]...` ) ;
2744+ } ) ;
2745+
26952746 it ( "reads workspace context from the configured workspace instead of process cwd" , async ( ) => {
26962747 const processRoot = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , "openclaw-compaction-cwd-" ) ) ;
26972748 const workspaceRoot = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , "openclaw-compaction-workspace-" ) ) ;
0 commit comments