@@ -158,6 +158,21 @@ class DoctorRedisRuntime {
158158
159159const textEncoder = new TextEncoder ( ) ;
160160
161+ const createOversizedSessionNoteText = ( ) : string => {
162+ const timestamp = "2026-04-11T10:00:00.000Z" ;
163+ const emptyPayloadBytes = textEncoder . encode ( JSON . stringify ( {
164+ note : {
165+ id : crypto . randomUUID ( ) ,
166+ text : "" ,
167+ created_at : timestamp ,
168+ updated_at : timestamp ,
169+ } ,
170+ } ) ) . byteLength ;
171+ return "x" . repeat (
172+ SESSION_MCP_RESPONSE_BUDGET_BYTES - emptyPayloadBytes + 1 ,
173+ ) ;
174+ } ;
175+
161176const toolContext = {
162177 sessionID : "session-123" ,
163178 messageID : "message-123" ,
@@ -973,6 +988,62 @@ describe("session-mcp-runtime", () => {
973988 }
974989 } ) ;
975990
991+ it ( "rejects oversized note writes before storage and suggests splitting notes" , async ( ) => {
992+ const redis = new RedisClient ( { endpoint : "redis://unused" } ) ;
993+ const runtime = createSessionMcpRuntime ( {
994+ redisClient : redis ,
995+ sessionTtlSeconds : 60 ,
996+ } as never ) ;
997+ const oversizedText = createOversizedSessionNoteText ( ) ;
998+
999+ try {
1000+ await assertRejects (
1001+ ( ) =>
1002+ runtime . tools . session_notes_write . execute (
1003+ {
1004+ text : oversizedText ,
1005+ } ,
1006+ toolContext ,
1007+ ) ,
1008+ Error ,
1009+ "multiple cross-referencing session notes" ,
1010+ ) ;
1011+ } finally {
1012+ await runtime . dispose ( ) ;
1013+ }
1014+ } ) ;
1015+
1016+ it ( "applies the shared response budget guard to session_notes_read" , async ( ) => {
1017+ const oversizedText = "x" . repeat ( SESSION_MCP_RESPONSE_BUDGET_BYTES + 1_024 ) ;
1018+ const runtime = createSessionMcpRuntime ( {
1019+ notesService : {
1020+ readNote : ( ) =>
1021+ Promise . resolve ( {
1022+ note : {
1023+ id : "note-oversized" ,
1024+ text : oversizedText ,
1025+ created_at : "2026-04-11T10:00:00.000Z" ,
1026+ updated_at : "2026-04-11T10:00:00.000Z" ,
1027+ } ,
1028+ } ) ,
1029+ } as never ,
1030+ } as never ) ;
1031+
1032+ try {
1033+ await assertRejects (
1034+ ( ) =>
1035+ runtime . tools . session_notes_read . execute (
1036+ { id : "note-oversized" } ,
1037+ toolContext ,
1038+ ) ,
1039+ Error ,
1040+ `session_notes_read response exceeded ${ SESSION_MCP_RESPONSE_BUDGET_BYTES } bytes` ,
1041+ ) ;
1042+ } finally {
1043+ await runtime . dispose ( ) ;
1044+ }
1045+ } ) ;
1046+
9761047 it ( "resolves rootless search and note writes from the canonical tool context session" , async ( ) => {
9771048 const redis = new RedisClient ( { endpoint : "redis://unused" } ) ;
9781049 const manager = new SessionManager (
@@ -2124,7 +2195,7 @@ describe("session-mcp-runtime", () => {
21242195 }
21252196 } ) ;
21262197
2127- it ( "caps serialized responses to the exact 8 KB budget" , async ( ) => {
2198+ it ( "caps serialized responses to the exact 32 KB budget" , async ( ) => {
21282199 const runtime = createSessionMcpRuntime ( ) ;
21292200
21302201 try {
@@ -2145,7 +2216,7 @@ describe("session-mcp-runtime", () => {
21452216 }
21462217 } ) ;
21472218
2148- it ( "falls back to a local artifact reference when inline output crosses 8 KB" , async ( ) => {
2219+ it ( "falls back to a local artifact reference when inline output crosses 32 KB" , async ( ) => {
21492220 const runtime = createSessionMcpRuntime ( {
21502221 handlers : {
21512222 session_execute : ( ) =>
@@ -2272,11 +2343,11 @@ describe("session-mcp-runtime", () => {
22722343 Promise . resolve ( {
22732344 status : "ok" ,
22742345 summary : "SESSION TTL REPORT\n" +
2275- "session ttl keeps local corpus search warm\n" . repeat ( 400 ) ,
2346+ "session ttl keeps local corpus search warm\n" . repeat ( 900 ) ,
22762347 exit_code : 0 ,
22772348 timed_out : false ,
22782349 truncated : false ,
2279- bytes_captured : SESSION_MCP_RESPONSE_BUDGET_BYTES + 4_096 ,
2350+ bytes_captured : SESSION_MCP_RESPONSE_BUDGET_BYTES + 8_192 ,
22802351 } ) ,
22812352 } ,
22822353 } as never ) ;
@@ -2668,11 +2739,11 @@ describe("session-mcp-runtime", () => {
26682739 session_execute : ( request : { command : string } ) =>
26692740 Promise . resolve ( {
26702741 status : "ok" ,
2671- summary : `${ request . command } : ` + "x" . repeat ( 6_000 ) ,
2742+ summary : `${ request . command } : ` + "x" . repeat ( 18_000 ) ,
26722743 exit_code : 0 ,
26732744 timed_out : false ,
26742745 truncated : false ,
2675- bytes_captured : 6_010 ,
2746+ bytes_captured : 18_010 ,
26762747 } ) ,
26772748 } ,
26782749 } as never ) ;
@@ -2819,11 +2890,11 @@ describe("session-mcp-runtime", () => {
28192890 session_execute : ( request : { command : string } ) =>
28202891 Promise . resolve ( {
28212892 status : "ok" ,
2822- summary : `${ request . command } : ` + "x" . repeat ( 7_000 ) ,
2893+ summary : `${ request . command } : ` + "x" . repeat ( 18_000 ) ,
28232894 exit_code : 0 ,
28242895 timed_out : false ,
28252896 truncated : false ,
2826- bytes_captured : 7_010 ,
2897+ bytes_captured : 18_010 ,
28272898 } ) ,
28282899 } ,
28292900 } as never ) ;
0 commit comments