@@ -93,6 +93,7 @@ process.env.ALERT_EMAIL_TRANSPORT = "smtp";
9393const {
9494 armDashboardAgentWatchBatch,
9595 authorizeWatchEnvironment,
96+ cancelDashboardAgentWatch,
9697 createDashboardAgentWatch,
9798 deleteChatWithWatches,
9899 listActiveWatchesForChats,
@@ -784,6 +785,68 @@ describe("the chat cascade and the list view", () => {
784785 }
785786 ) ;
786787
788+ postgresTest (
789+ "a user's own cancel leaves one neutral line in the chat, and only one" ,
790+ async ( { prisma, postgresContainer } ) => {
791+ await boot ( prisma , postgresContainer . getConnectionUri ( ) ) ;
792+ const seeded = await seed ( prisma , "usercancel" ) ;
793+ await seedChat ( seeded , "chat_1" ) ;
794+
795+ const created = await create ( { seeded, chatId : "chat_1" } ) ;
796+ expect ( created . ok ) . toBe ( true ) ;
797+ if ( ! created . ok ) return ;
798+
799+ const cancel = ( ) =>
800+ cancelDashboardAgentWatch ( {
801+ watchId : created . watchId ,
802+ userId : seeded . user . id ,
803+ organizationId : seeded . organization . id ,
804+ } ) ;
805+
806+ expect ( await cancel ( ) ) . toMatchObject ( {
807+ cancelled : true ,
808+ messages : [
809+ {
810+ id : `watch-cancelled:${ created . watchId } ` ,
811+ role : "assistant" ,
812+ parts : [ { type : "text" , text : "Stopped watching run run_1." } ] ,
813+ } ,
814+ ] ,
815+ } ) ;
816+ expect ( await getWatch ( ctx . agentDb , { id : created . watchId } ) ) . toMatchObject ( {
817+ status : "cancelled" ,
818+ cancelReason : "user" ,
819+ deliveryStatus : "not_required" ,
820+ } ) ;
821+ expect ( await storedMessages ( seeded , "chat_1" ) ) . toMatchObject ( [
822+ { id : `watch-cancelled:${ created . watchId } ` , role : "assistant" } ,
823+ ] ) ;
824+
825+ // The row is no longer active, so the second cancel writes nothing at all.
826+ expect ( await cancel ( ) ) . toEqual ( { cancelled : false , messages : [ ] } ) ;
827+ expect ( await storedMessages ( seeded , "chat_1" ) ) . toHaveLength ( 1 ) ;
828+ }
829+ ) ;
830+
831+ postgresTest (
832+ "a chat delete cancels its watches without a line in the chat" ,
833+ async ( { prisma, postgresContainer } ) => {
834+ await boot ( prisma , postgresContainer . getConnectionUri ( ) ) ;
835+ const seeded = await seed ( prisma , "silentcancel" ) ;
836+ await seedChat ( seeded , "chat_1" ) ;
837+
838+ const created = await create ( { seeded, chatId : "chat_1" } ) ;
839+ expect ( created . ok ) . toBe ( true ) ;
840+
841+ await deleteChatWithWatches ( { chatId : "chat_1" , userId : seeded . user . id } ) ;
842+
843+ const rows = await ctx . prisma . $queryRawUnsafe < { message_id : string } [ ] > (
844+ `select message_id from trigger_dashboard_agent.chat_messages where chat_id = 'chat_1'`
845+ ) ;
846+ expect ( rows ) . toEqual ( [ ] ) ;
847+ }
848+ ) ;
849+
787850 postgresTest (
788851 "aggregates active watches per chat in one query" ,
789852 async ( { prisma, postgresContainer } ) => {
0 commit comments