@@ -42,61 +42,76 @@ interface SessionState {
4242
4343export interface TestProviderAdapterHarness {
4444 readonly adapter : ProviderAdapterShape < ProviderAdapterError > ;
45+ readonly provider : "codex" | "claudeCode" ;
4546 readonly queueTurnResponse : (
4647 sessionId : string ,
4748 response : TestTurnResponse ,
4849 ) => Effect . Effect < void , ProviderAdapterSessionNotFoundError > ;
4950 readonly queueTurnResponseForNextSession : (
5051 response : TestTurnResponse ,
5152 ) => Effect . Effect < void , never > ;
53+ readonly getStartCount : ( ) => number ;
5254 readonly getRollbackCalls : ( sessionId : string ) => ReadonlyArray < number > ;
55+ readonly getInterruptCalls : ( sessionId : string ) => ReadonlyArray < ProviderTurnId | undefined > ;
56+ readonly listActiveSessionIds : ( ) => ReadonlyArray < ProviderSessionId > ;
5357 readonly getApprovalResponses : ( sessionId : string ) => ReadonlyArray < {
5458 readonly sessionId : ProviderSessionId ;
5559 readonly requestId : ApprovalRequestId ;
5660 readonly decision : ProviderApprovalDecision ;
5761 } > ;
5862}
5963
60- const PROVIDER = "codex" as const ;
64+ interface MakeTestProviderAdapterHarnessOptions {
65+ readonly provider ?: "codex" | "claudeCode" ;
66+ }
6167
6268function nowIso ( ) : string {
6369 return new Date ( ) . toISOString ( ) ;
6470}
6571
66- function sessionNotFound ( sessionId : string ) : ProviderAdapterSessionNotFoundError {
72+ function sessionNotFound (
73+ provider : "codex" | "claudeCode" ,
74+ sessionId : string ,
75+ ) : ProviderAdapterSessionNotFoundError {
6776 return new ProviderAdapterSessionNotFoundError ( {
68- provider : PROVIDER ,
77+ provider,
6978 sessionId,
7079 } ) ;
7180}
7281
73- function missingSessionEffect ( sessionId : string ) : Effect . Effect < never , ProviderAdapterError > {
74- return Effect . fail ( sessionNotFound ( sessionId ) ) ;
82+ function missingSessionEffect (
83+ provider : "codex" | "claudeCode" ,
84+ sessionId : string ,
85+ ) : Effect . Effect < never , ProviderAdapterError > {
86+ return Effect . fail ( sessionNotFound ( provider , sessionId ) ) ;
7587}
7688
77- export const makeTestProviderAdapterHarness = Effect . gen ( function * ( ) {
78- const runtimeEvents = yield * Queue . unbounded < ProviderRuntimeEvent > ( ) ;
79- let sessionCount = 0 ;
80- const sessions = new Map < string , SessionState > ( ) ;
81- const queuedResponsesForNextSession : TestTurnResponse [ ] = [ ] ;
82- const approvalResponsesBySession = new Map <
83- string ,
84- Array < {
85- readonly sessionId : ProviderSessionId ;
86- readonly requestId : ApprovalRequestId ;
87- readonly decision : ProviderApprovalDecision ;
88- } >
89- > ( ) ;
89+ export const makeTestProviderAdapterHarness = ( options ?: MakeTestProviderAdapterHarnessOptions ) =>
90+ Effect . gen ( function * ( ) {
91+ const provider = options ?. provider ?? "codex" ;
92+ const runtimeEvents = yield * Queue . unbounded < ProviderRuntimeEvent > ( ) ;
93+ let sessionCount = 0 ;
94+ const sessions = new Map < string , SessionState > ( ) ;
95+ const queuedResponsesForNextSession : TestTurnResponse [ ] = [ ] ;
96+ const interruptCallsBySession = new Map < string , Array < ProviderTurnId | undefined > > ( ) ;
97+ const approvalResponsesBySession = new Map <
98+ string ,
99+ Array < {
100+ readonly sessionId : ProviderSessionId ;
101+ readonly requestId : ApprovalRequestId ;
102+ readonly decision : ProviderApprovalDecision ;
103+ } >
104+ > ( ) ;
90105
91106 const emit = ( event : ProviderRuntimeEvent ) => Queue . offer ( runtimeEvents , event ) ;
92107
93108 const startSession : ProviderAdapterShape < ProviderAdapterError > [ "startSession" ] = ( input ) =>
94109 Effect . gen ( function * ( ) {
95- if ( input . provider !== undefined && input . provider !== PROVIDER ) {
110+ if ( input . provider !== undefined && input . provider !== provider ) {
96111 return yield * new ProviderAdapterValidationError ( {
97- provider : PROVIDER ,
112+ provider,
98113 operation : "startSession" ,
99- issue : `Expected provider '${ PROVIDER } ' but received '${ input . provider } '.` ,
114+ issue : `Expected provider '${ provider } ' but received '${ input . provider } '.` ,
100115 } ) ;
101116 }
102117
@@ -107,10 +122,11 @@ export const makeTestProviderAdapterHarness = Effect.gen(function* () {
107122
108123 const session : ProviderSession = {
109124 sessionId,
110- provider : PROVIDER ,
125+ provider,
111126 status : "ready" ,
112127 threadId,
113128 cwd : input . cwd ,
129+ resumeCursor : input . resumeCursor ?? { sessionId } ,
114130 createdAt,
115131 updatedAt : createdAt ,
116132 } ;
@@ -133,7 +149,7 @@ export const makeTestProviderAdapterHarness = Effect.gen(function* () {
133149 Effect . gen ( function * ( ) {
134150 const state = sessions . get ( input . sessionId ) ;
135151 if ( ! state ) {
136- return yield * missingSessionEffect ( input . sessionId ) ;
152+ return yield * missingSessionEffect ( provider , input . sessionId ) ;
137153 }
138154
139155 state . turnCount += 1 ;
@@ -143,7 +159,7 @@ export const makeTestProviderAdapterHarness = Effect.gen(function* () {
143159 const response = state . queuedResponses . shift ( ) ;
144160 if ( ! response ) {
145161 return yield * new ProviderAdapterValidationError ( {
146- provider : PROVIDER ,
162+ provider,
147163 operation : "sendTurn" ,
148164 issue : `No queued turn response for session ${ input . sessionId } .` ,
149165 } ) ;
@@ -155,7 +171,7 @@ export const makeTestProviderAdapterHarness = Effect.gen(function* () {
155171 const rawEvent : Record < string , unknown > = {
156172 ...( fixtureEvent as Record < string , unknown > ) ,
157173 eventId : randomUUID ( ) ,
158- provider : PROVIDER ,
174+ provider,
159175 sessionId : input . sessionId ,
160176 createdAt : nowIso ( ) ,
161177 } ;
@@ -206,7 +222,7 @@ export const makeTestProviderAdapterHarness = Effect.gen(function* () {
206222 yield * emit ( {
207223 type : "turn.completed" ,
208224 eventId : EventId . makeUnsafe ( randomUUID ( ) ) ,
209- provider : PROVIDER ,
225+ provider,
210226 sessionId : input . sessionId ,
211227 createdAt : nowIso ( ) ,
212228 threadId : state . snapshot . threadId ,
@@ -227,8 +243,15 @@ export const makeTestProviderAdapterHarness = Effect.gen(function* () {
227243
228244 const interruptTurn : ProviderAdapterShape < ProviderAdapterError > [ "interruptTurn" ] = (
229245 sessionId ,
230- _turnId ,
231- ) => ( sessions . has ( sessionId ) ? Effect . void : missingSessionEffect ( sessionId ) ) ;
246+ turnId ,
247+ ) =>
248+ sessions . has ( sessionId )
249+ ? Effect . sync ( ( ) => {
250+ const existing = interruptCallsBySession . get ( sessionId ) ?? [ ] ;
251+ existing . push ( turnId ) ;
252+ interruptCallsBySession . set ( sessionId , existing ) ;
253+ } )
254+ : missingSessionEffect ( provider , sessionId ) ;
232255
233256 const respondToRequest : ProviderAdapterShape < ProviderAdapterError > [ "respondToRequest" ] = (
234257 sessionId ,
@@ -245,7 +268,7 @@ export const makeTestProviderAdapterHarness = Effect.gen(function* () {
245268 } ) ;
246269 approvalResponsesBySession . set ( sessionId , existing ) ;
247270 } )
248- : missingSessionEffect ( sessionId ) ;
271+ : missingSessionEffect ( provider , sessionId ) ;
249272
250273 const stopSession : ProviderAdapterShape < ProviderAdapterError > [ "stopSession" ] = ( sessionId ) =>
251274 Effect . sync ( ( ) => {
@@ -261,7 +284,7 @@ export const makeTestProviderAdapterHarness = Effect.gen(function* () {
261284 const readThread : ProviderAdapterShape < ProviderAdapterError > [ "readThread" ] = ( sessionId ) => {
262285 const state = sessions . get ( sessionId ) ;
263286 if ( ! state ) {
264- return missingSessionEffect ( sessionId ) ;
287+ return missingSessionEffect ( provider , sessionId ) ;
265288 }
266289 return Effect . succeed ( state . snapshot ) ;
267290 } ;
@@ -272,12 +295,12 @@ export const makeTestProviderAdapterHarness = Effect.gen(function* () {
272295 ) => {
273296 const state = sessions . get ( sessionId ) ;
274297 if ( ! state ) {
275- return missingSessionEffect ( sessionId ) ;
298+ return missingSessionEffect ( provider , sessionId ) ;
276299 }
277300 if ( ! Number . isInteger ( numTurns ) || numTurns < 0 || numTurns > state . snapshot . turns . length ) {
278301 return Effect . fail (
279302 new ProviderAdapterValidationError ( {
280- provider : PROVIDER ,
303+ provider,
281304 operation : "rollbackThread" ,
282305 issue : "numTurns must be an integer between 0 and current turn count." ,
283306 } ) ,
@@ -301,7 +324,7 @@ export const makeTestProviderAdapterHarness = Effect.gen(function* () {
301324 } ) ;
302325
303326 const adapter : ProviderAdapterShape < ProviderAdapterError > = {
304- provider : PROVIDER ,
327+ provider,
305328 startSession,
306329 sendTurn,
307330 interruptTurn,
@@ -325,7 +348,7 @@ export const makeTestProviderAdapterHarness = Effect.gen(function* () {
325348 ? Effect . sync ( ( ) => {
326349 state . queuedResponses . push ( response ) ;
327350 } )
328- : Effect . fail ( sessionNotFound ( sessionId ) ) ,
351+ : Effect . fail ( sessionNotFound ( provider , sessionId ) ) ,
329352 ) ,
330353 ) ;
331354
@@ -344,6 +367,19 @@ export const makeTestProviderAdapterHarness = Effect.gen(function* () {
344367 return [ ...state . rollbackCalls ] ;
345368 } ;
346369
370+ const getStartCount = ( ) : number => sessionCount ;
371+
372+ const getInterruptCalls = ( sessionId : string ) : ReadonlyArray < ProviderTurnId | undefined > => {
373+ const calls = interruptCallsBySession . get ( sessionId ) ;
374+ if ( ! calls ) {
375+ return [ ] ;
376+ }
377+ return [ ...calls ] ;
378+ } ;
379+
380+ const listActiveSessionIds = ( ) : ReadonlyArray < ProviderSessionId > =>
381+ Array . from ( sessions . values ( ) , ( state ) => state . session . sessionId ) ;
382+
347383 const getApprovalResponses = (
348384 sessionId : string ,
349385 ) : ReadonlyArray < {
@@ -360,9 +396,13 @@ export const makeTestProviderAdapterHarness = Effect.gen(function* () {
360396
361397 return {
362398 adapter,
399+ provider,
363400 queueTurnResponse,
364401 queueTurnResponseForNextSession,
402+ getStartCount,
365403 getRollbackCalls,
404+ getInterruptCalls,
405+ listActiveSessionIds,
366406 getApprovalResponses,
367407 } satisfies TestProviderAdapterHarness ;
368408} ) ;
0 commit comments