@@ -55,7 +55,7 @@ test("a Query streams tool and permission events before the terminal Result", as
5555 assert . ok ( client instanceof AgentClient ) ;
5656 assert . equal ( initializeRequests . length , 1 ) ;
5757 assert . deepEqual ( initializeRequests [ 0 ] , {
58- protocolVersion : 3 ,
58+ protocolVersion : 4 ,
5959 clientInfo : { name : "@bitfun/agent-sdk" , version : "0.0.0" } ,
6060 capabilities : {
6161 serverNotifications : true ,
@@ -187,6 +187,7 @@ test("an explicit Session starts Turns on the existing client connection", async
187187 } as SessionCreateInput ) ;
188188 assert . equal ( session . id , "session-explicit" ) ;
189189 assert . equal ( session . agent , "agentic" ) ;
190+ assert . equal ( session . lifetime , "durable" ) ;
190191
191192 const query = await session . startTurn ( { prompt : "continue" } ) ;
192193 assert . equal ( ( await query . result ( ) ) . outputText , "continued" ) ;
@@ -203,6 +204,37 @@ test("an explicit Session starts Turns on the existing client connection", async
203204 ] ) ;
204205} ) ;
205206
207+ test ( "a durable Session resumes on a new client connection" , async ( ) => {
208+ const clientToHost = new PassThrough ( ) ;
209+ const hostToClient = new PassThrough ( ) ;
210+ const methods : string [ ] = [ ] ;
211+ const host = runResumeFixtureHost ( clientToHost , hostToClient , methods ) ;
212+ const client = await createAgentClient (
213+ {
214+ readable : hostToClient ,
215+ writable : clientToHost ,
216+ close : async ( ) => {
217+ clientToHost . end ( ) ;
218+ await host ;
219+ } ,
220+ } ,
221+ clientOptions ,
222+ ) ;
223+
224+ const session = await client . sessions . resume ( "session-persisted" ) ;
225+ assert . equal ( session . id , "session-persisted" ) ;
226+ assert . equal ( session . lifetime , "durable" ) ;
227+ await session . close ( ) ;
228+ await client . close ( ) ;
229+
230+ assert . deepEqual ( methods , [
231+ "initialize" ,
232+ "session/resume" ,
233+ "session/close" ,
234+ "shutdown" ,
235+ ] ) ;
236+ } ) ;
237+
206238test ( "Query cancel and close are idempotent and the Host Result remains authoritative" , async ( ) => {
207239 const clientToHost = new PassThrough ( ) ;
208240 const hostToClient = new PassThrough ( ) ;
@@ -530,12 +562,13 @@ async function runFixtureHost(
530562 jsonrpc : "2.0" ,
531563 id : request . id ,
532564 result : {
533- protocolVersion : 3 ,
565+ protocolVersion : 4 ,
534566 runtimeVersion : "0.2.17" ,
535567 stability : "not_delivered" ,
536568 capabilities : {
537569 sessionCreate : true ,
538- sessionCreateLifetime : "connection" ,
570+ sessionCreateLifetime : "durable" ,
571+ sessionResume : true ,
539572 query : true ,
540573 queryCancel : true ,
541574 sessionClose : true ,
@@ -725,7 +758,7 @@ async function runSessionFixtureHost(
725758 sessionId : "session-explicit" ,
726759 sessionName : "Explicit" ,
727760 agent : "agentic" ,
728- lifetime : "connection " ,
761+ lifetime : "durable " ,
729762 } ,
730763 } ) ;
731764 continue ;
@@ -786,6 +819,59 @@ async function runSessionFixtureHost(
786819 }
787820}
788821
822+ async function runResumeFixtureHost (
823+ requests : PassThrough ,
824+ responses : PassThrough ,
825+ methods : string [ ] ,
826+ ) : Promise < void > {
827+ const lines = createInterface ( { input : requests , crlfDelay : Infinity } ) ;
828+ for await ( const line of lines ) {
829+ const request = JSON . parse ( line ) as {
830+ id : number ;
831+ method : string ;
832+ params : Record < string , unknown > ;
833+ } ;
834+ methods . push ( request . method ) ;
835+ if ( request . method === "initialize" ) {
836+ write ( responses , initializeResponse ( request . id ) ) ;
837+ continue ;
838+ }
839+ if ( request . method === "session/resume" ) {
840+ assert . deepEqual ( request . params , { sessionId : "session-persisted" } ) ;
841+ write ( responses , {
842+ jsonrpc : "2.0" ,
843+ id : request . id ,
844+ result : {
845+ sessionId : "session-persisted" ,
846+ sessionName : "Persisted" ,
847+ agent : "agentic" ,
848+ lifetime : "durable" ,
849+ workspacePath : "D:/workspace/project" ,
850+ } ,
851+ } ) ;
852+ continue ;
853+ }
854+ if ( request . method === "session/close" ) {
855+ write ( responses , {
856+ jsonrpc : "2.0" ,
857+ id : request . id ,
858+ result : { sessionId : "session-persisted" , unloaded : true } ,
859+ } ) ;
860+ continue ;
861+ }
862+ if ( request . method === "shutdown" ) {
863+ write ( responses , {
864+ jsonrpc : "2.0" ,
865+ id : request . id ,
866+ result : { accepted : true } ,
867+ } ) ;
868+ responses . end ( ) ;
869+ return ;
870+ }
871+ throw new Error ( `Unexpected fixture method: ${ request . method } ` ) ;
872+ }
873+ }
874+
789875async function runCancelFixtureHost (
790876 requests : PassThrough ,
791877 responses : PassThrough ,
@@ -1227,12 +1313,13 @@ function initializeResponse(id: number): unknown {
12271313 jsonrpc : "2.0" ,
12281314 id,
12291315 result : {
1230- protocolVersion : 3 ,
1316+ protocolVersion : 4 ,
12311317 runtimeVersion : "0.2.17" ,
12321318 stability : "not_delivered" ,
12331319 capabilities : {
12341320 sessionCreate : true ,
1235- sessionCreateLifetime : "connection" ,
1321+ sessionCreateLifetime : "durable" ,
1322+ sessionResume : true ,
12361323 query : true ,
12371324 queryCancel : true ,
12381325 sessionClose : true ,
0 commit comments