@@ -647,6 +647,105 @@ 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 ( store . updateTaskStatus ( task . taskId , 'cancelled' , undefined , 'session-b' ) ) . rejects . toThrow ( 'not found' ) ;
672+ } ) ;
673+
674+ it ( 'should not allow session-b to store a result on session-a task' , async ( ) => {
675+ const task = await store . createTask ( { } , 1 , baseRequest , 'session-a' ) ;
676+
677+ await expect ( store . storeTaskResult ( task . taskId , 'completed' , { content : [ ] } , 'session-b' ) ) . rejects . toThrow ( 'not found' ) ;
678+ } ) ;
679+
680+ it ( 'should not allow session-b to get the result of session-a task' , async ( ) => {
681+ const task = await store . createTask ( { } , 1 , baseRequest , 'session-a' ) ;
682+ await store . storeTaskResult ( task . taskId , 'completed' , { content : [ { type : 'text' , text : 'secret' } ] } , 'session-a' ) ;
683+
684+ await expect ( store . getTaskResult ( task . taskId , 'session-b' ) ) . rejects . toThrow ( 'not found' ) ;
685+ } ) ;
686+
687+ it ( 'should allow the owning session to access its own tasks' , async ( ) => {
688+ const task = await store . createTask ( { } , 1 , baseRequest , 'session-a' ) ;
689+
690+ const retrieved = await store . getTask ( task . taskId , 'session-a' ) ;
691+ expect ( retrieved ) . toBeDefined ( ) ;
692+ expect ( retrieved ?. taskId ) . toBe ( task . taskId ) ;
693+ } ) ;
694+
695+ it ( 'should list only tasks belonging to the requesting session' , async ( ) => {
696+ await store . createTask ( { } , 1 , baseRequest , 'session-a' ) ;
697+ await store . createTask ( { } , 2 , baseRequest , 'session-b' ) ;
698+ await store . createTask ( { } , 3 , baseRequest , 'session-a' ) ;
699+
700+ const resultA = await store . listTasks ( undefined , 'session-a' ) ;
701+ expect ( resultA . tasks ) . toHaveLength ( 2 ) ;
702+
703+ const resultB = await store . listTasks ( undefined , 'session-b' ) ;
704+ expect ( resultB . tasks ) . toHaveLength ( 1 ) ;
705+ } ) ;
706+
707+ it ( 'should allow access when no sessionId is provided (backward compatibility)' , async ( ) => {
708+ const task = await store . createTask ( { } , 1 , baseRequest , 'session-a' ) ;
709+
710+ // No sessionId on read = no filtering
711+ const retrieved = await store . getTask ( task . taskId ) ;
712+ expect ( retrieved ) . toBeDefined ( ) ;
713+ } ) ;
714+
715+ it ( 'should allow access when task was created without sessionId' , async ( ) => {
716+ const task = await store . createTask ( { } , 1 , baseRequest ) ;
717+
718+ // Any sessionId on read should still see the task
719+ const retrieved = await store . getTask ( task . taskId , 'session-b' ) ;
720+ expect ( retrieved ) . toBeDefined ( ) ;
721+ } ) ;
722+
723+ it ( 'should paginate correctly within a session' , async ( ) => {
724+ // Create 15 tasks for session-a, 5 for session-b
725+ for ( let i = 1 ; i <= 15 ; i ++ ) {
726+ await store . createTask ( { } , i , baseRequest , 'session-a' ) ;
727+ }
728+ for ( let i = 16 ; i <= 20 ; i ++ ) {
729+ await store . createTask ( { } , i , baseRequest , 'session-b' ) ;
730+ }
731+
732+ // First page for session-a should have 10
733+ const page1 = await store . listTasks ( undefined , 'session-a' ) ;
734+ expect ( page1 . tasks ) . toHaveLength ( 10 ) ;
735+ expect ( page1 . nextCursor ) . toBeDefined ( ) ;
736+
737+ // Second page for session-a should have 5
738+ const page2 = await store . listTasks ( page1 . nextCursor , 'session-a' ) ;
739+ expect ( page2 . tasks ) . toHaveLength ( 5 ) ;
740+ expect ( page2 . nextCursor ) . toBeUndefined ( ) ;
741+
742+ // session-b should only see its 5
743+ const resultB = await store . listTasks ( undefined , 'session-b' ) ;
744+ expect ( resultB . tasks ) . toHaveLength ( 5 ) ;
745+ expect ( resultB . nextCursor ) . toBeUndefined ( ) ;
746+ } ) ;
747+ } ) ;
748+
650749 describe ( 'cleanup' , ( ) => {
651750 it ( 'should clear all timers and tasks' , async ( ) => {
652751 await store . createTask ( { ttl : 1000 } , 1 , {
0 commit comments