| ... | ... | @@ -284,7 +284,11 @@ const hasMetricSnapshot = ( |
| 284 | 284 | Object.entries(attributes).every(([key, value]) => snapshot.attributes?.[key] === value), |
| 285 | 285 | ); |
| 286 | 286 | |
| 287 | | function makeProviderServiceLayer() { |
| 287 | function makeProviderServiceLayer( |
| 288 | input: { |
| 289 | readonly directory?: ProviderSessionDirectory.ProviderSessionDirectory["Service"]; |
| 290 | } = {}, |
| 291 | ) { |
| 288 | 292 | const codex = makeFakeCodexAdapter(); |
| 289 | 293 | const claude = makeFakeCodexAdapter(CLAUDE_AGENT_DRIVER); |
| 290 | 294 | const cursor = makeFakeCodexAdapter(CURSOR_DRIVER); |
| ... | ... | @@ -301,7 +305,10 @@ function makeProviderServiceLayer() { |
| 301 | 305 | const runtimeRepositoryLayer = ProviderSessionRuntime.layer.pipe( |
| 302 | 306 | Layer.provide(SqlitePersistenceMemory), |
| 303 | 307 | ); |
| 304 | | const directoryLayer = ProviderSessionDirectoryLive.pipe(Layer.provide(runtimeRepositoryLayer)); |
| 308 | const directoryLayer = |
| 309 | input.directory === undefined |
| 310 | ? ProviderSessionDirectoryLive.pipe(Layer.provide(runtimeRepositoryLayer)) |
| 311 | : Layer.succeed(ProviderSessionDirectory.ProviderSessionDirectory, input.directory); |
| 305 | 312 | |
| 306 | 313 | const layer = it.layer( |
| 307 | 314 | Layer.mergeAll( |
| ... | ... | @@ -2208,6 +2215,53 @@ validation.layer("ProviderServiceLive validation", (it) => { |
| 2208 | 2215 | ); |
| 2209 | 2216 | }); |
| 2210 | 2217 | |
| 2218 | const activeSessionThreadId = asThreadId("thread-active-session"); |
| 2219 | const historicalSessionThreadId = asThreadId("thread-historical-session"); |
| 2220 | const listThreadIds = vi.fn(() => |
| 2221 | Effect.succeed([activeSessionThreadId, historicalSessionThreadId]), |
| 2222 | ); |
| 2223 | const getBinding = vi.fn((threadId: ThreadId) => |
| 2224 | Effect.succeed( |
| 2225 | Option.some({ |
| 2226 | threadId, |
| 2227 | provider: CODEX_DRIVER, |
| 2228 | providerInstanceId: codexInstanceId, |
| 2229 | }), |
| 2230 | ), |
| 2231 | ); |
| 2232 | const boundedListing = makeProviderServiceLayer({ |
| 2233 | directory: { |
| 2234 | upsert: () => Effect.void, |
| 2235 | getProvider: () => Effect.die("ProviderService.listSessions does not use getProvider"), |
| 2236 | getBinding, |
| 2237 | listThreadIds, |
| 2238 | listBindings: () => Effect.die("ProviderService.listSessions does not use listBindings"), |
| 2239 | }, |
| 2240 | }); |
| 2241 | |
| 2242 | boundedListing.layer("ProviderServiceLive session listing", (it) => { |
| 2243 | it.effect("looks up bindings for active sessions without scanning historical threads", () => |
| 2244 | Effect.gen(function* () { |
| 2245 | const provider = yield* ProviderService.ProviderService; |
| 2246 | yield* boundedListing.codex.startSession({ |
| 2247 | provider: CODEX_DRIVER, |
| 2248 | providerInstanceId: codexInstanceId, |
| 2249 | threadId: activeSessionThreadId, |
| 2250 | cwd: "/tmp/project-active-session", |
| 2251 | runtimeMode: "full-access", |
| 2252 | }); |
| 2253 | listThreadIds.mockClear(); |
| 2254 | getBinding.mockClear(); |
| 2255 | |
| 2256 | const sessions = yield* provider.listSessions(); |
| 2257 | |
| 2258 | assert.equal(sessions.length, 1); |
| 2259 | assert.equal(listThreadIds.mock.calls.length, 0); |
| 2260 | assert.deepEqual(getBinding.mock.calls, [[activeSessionThreadId]]); |
| 2261 | }), |
| 2262 | ); |
| 2263 | }); |
| 2264 | |
| 2211 | 2265 | describe("agent browser access", () => { |
| 2212 | 2266 | const revokedThreads: Array<ThreadId> = []; |
| 2213 | 2267 | |