@@ -647,6 +647,111 @@ describe('InMemoryTaskStore', () => {
647647 } ) ;
648648 } ) ;
649649
650+ describe ( 'session isolation' , ( ) => {
651+ const baseRequest : Request = { method : 'tools/call' , params : { name : 'demo' } } ;
652+
653+ it ( 'should not allow session-b to list tasks created by session-a' , async ( ) => {
654+ await store . createTask ( { } , 1 , baseRequest , 'session-a' ) ;
655+ await store . createTask ( { } , 2 , baseRequest , 'session-a' ) ;
656+
657+ const result = await store . listTasks ( undefined , 'session-b' ) ;
658+ expect ( result . tasks ) . toHaveLength ( 0 ) ;
659+ } ) ;
660+
661+ it ( 'should not allow session-b to read a task created by session-a' , async ( ) => {
662+ const task = await store . createTask ( { } , 1 , baseRequest , 'session-a' ) ;
663+
664+ const result = await store . getTask ( task . taskId , 'session-b' ) ;
665+ expect ( result ) . toBeNull ( ) ;
666+ } ) ;
667+
668+ it ( 'should not allow session-b to update a task created by session-a' , async ( ) => {
669+ const task = await store . createTask ( { } , 1 , baseRequest , 'session-a' ) ;
670+
671+ await expect (
672+ store . updateTaskStatus ( task . taskId , 'cancelled' , undefined , 'session-b' )
673+ ) . rejects . toThrow ( 'not found' ) ;
674+ } ) ;
675+
676+ it ( 'should not allow session-b to store a result on session-a task' , async ( ) => {
677+ const task = await store . createTask ( { } , 1 , baseRequest , 'session-a' ) ;
678+
679+ await expect (
680+ store . storeTaskResult ( task . taskId , 'completed' , { content : [ ] } , 'session-b' )
681+ ) . rejects . toThrow ( 'not found' ) ;
682+ } ) ;
683+
684+ it ( 'should not allow session-b to get the result of session-a task' , async ( ) => {
685+ const task = await store . createTask ( { } , 1 , baseRequest , 'session-a' ) ;
686+ await store . storeTaskResult ( task . taskId , 'completed' , { content : [ { type : 'text' , text : 'secret' } ] } , 'session-a' ) ;
687+
688+ await expect (
689+ store . getTaskResult ( task . taskId , 'session-b' )
690+ ) . rejects . toThrow ( 'not found' ) ;
691+ } ) ;
692+
693+ it ( 'should allow the owning session to access its own tasks' , async ( ) => {
694+ const task = await store . createTask ( { } , 1 , baseRequest , 'session-a' ) ;
695+
696+ const retrieved = await store . getTask ( task . taskId , 'session-a' ) ;
697+ expect ( retrieved ) . toBeDefined ( ) ;
698+ expect ( retrieved ?. taskId ) . toBe ( task . taskId ) ;
699+ } ) ;
700+
701+ it ( 'should list only tasks belonging to the requesting session' , async ( ) => {
702+ await store . createTask ( { } , 1 , baseRequest , 'session-a' ) ;
703+ await store . createTask ( { } , 2 , baseRequest , 'session-b' ) ;
704+ await store . createTask ( { } , 3 , baseRequest , 'session-a' ) ;
705+
706+ const resultA = await store . listTasks ( undefined , 'session-a' ) ;
707+ expect ( resultA . tasks ) . toHaveLength ( 2 ) ;
708+
709+ const resultB = await store . listTasks ( undefined , 'session-b' ) ;
710+ expect ( resultB . tasks ) . toHaveLength ( 1 ) ;
711+ } ) ;
712+
713+ it ( 'should allow access when no sessionId is provided (backward compatibility)' , async ( ) => {
714+ const task = await store . createTask ( { } , 1 , baseRequest , 'session-a' ) ;
715+
716+ // No sessionId on read = no filtering
717+ const retrieved = await store . getTask ( task . taskId ) ;
718+ expect ( retrieved ) . toBeDefined ( ) ;
719+ } ) ;
720+
721+ it ( 'should allow access when task was created without sessionId' , async ( ) => {
722+ const task = await store . createTask ( { } , 1 , baseRequest ) ;
723+
724+ // Any sessionId on read should still see the task
725+ const retrieved = await store . getTask ( task . taskId , 'session-b' ) ;
726+ expect ( retrieved ) . toBeDefined ( ) ;
727+ } ) ;
728+
729+ it ( 'should paginate correctly within a session' , async ( ) => {
730+ // Create 15 tasks for session-a, 5 for session-b
731+ for ( let i = 1 ; i <= 15 ; i ++ ) {
732+ await store . createTask ( { } , i , baseRequest , 'session-a' ) ;
733+ }
734+ for ( let i = 16 ; i <= 20 ; i ++ ) {
735+ await store . createTask ( { } , i , baseRequest , 'session-b' ) ;
736+ }
737+
738+ // First page for session-a should have 10
739+ const page1 = await store . listTasks ( undefined , 'session-a' ) ;
740+ expect ( page1 . tasks ) . toHaveLength ( 10 ) ;
741+ expect ( page1 . nextCursor ) . toBeDefined ( ) ;
742+
743+ // Second page for session-a should have 5
744+ const page2 = await store . listTasks ( page1 . nextCursor , 'session-a' ) ;
745+ expect ( page2 . tasks ) . toHaveLength ( 5 ) ;
746+ expect ( page2 . nextCursor ) . toBeUndefined ( ) ;
747+
748+ // session-b should only see its 5
749+ const resultB = await store . listTasks ( undefined , 'session-b' ) ;
750+ expect ( resultB . tasks ) . toHaveLength ( 5 ) ;
751+ expect ( resultB . nextCursor ) . toBeUndefined ( ) ;
752+ } ) ;
753+ } ) ;
754+
650755 describe ( 'cleanup' , ( ) => {
651756 it ( 'should clear all timers and tasks' , async ( ) => {
652757 await store . createTask ( { ttl : 1000 } , 1 , {
0 commit comments