| ... | @@ -32,6 +32,7 @@ import { | ... | @@ -32,6 +32,7 @@ import { |
| 32 | } from "@t3tools/shared/assistantCitations"; | 32 | } from "@t3tools/shared/assistantCitations"; |
| 33 | import { createModelSelection } from "@t3tools/shared/model"; | 33 | import { createModelSelection } from "@t3tools/shared/model"; |
| 34 | import { it, assert, describe, vi } from "@effect/vitest"; | 34 | import { it, assert, describe, vi } from "@effect/vitest"; |
| | 35 | import { afterAll } from "vite-plus/test"; |
| 35 | | 36 | |
| 36 | import * as Cause from "effect/Cause"; | 37 | import * as Cause from "effect/Cause"; |
| 37 | import * as Deferred from "effect/Deferred"; | 38 | import * as Deferred from "effect/Deferred"; |
| ... | @@ -54,6 +55,7 @@ import { | ... | @@ -54,6 +55,7 @@ import { |
| 54 | ProviderAdapterSessionNotFoundError, | 55 | ProviderAdapterSessionNotFoundError, |
| 55 | ProviderUnsupportedError, | 56 | ProviderUnsupportedError, |
| 56 | ProviderValidationError, | 57 | ProviderValidationError, |
| | 58 | ProviderWorkspaceMissingError, |
| 57 | type ProviderAdapterError, | 59 | type ProviderAdapterError, |
| 58 | } from "../Errors.ts"; | 60 | } from "../Errors.ts"; |
| 59 | import type { ProviderAdapterShape } from "../Services/ProviderAdapter.ts"; | 61 | import type { ProviderAdapterShape } from "../Services/ProviderAdapter.ts"; |
| ... | @@ -79,6 +81,16 @@ const serverConfigTestLayer = ServerConfig.layerTest(process.cwd(), process.cwd( | ... | @@ -79,6 +81,16 @@ const serverConfigTestLayer = ServerConfig.layerTest(process.cwd(), process.cwd( |
| 79 | Layer.provide(NodeServices.layer), | 81 | Layer.provide(NodeServices.layer), |
| 80 | ); | 82 | ); |
| 81 | | 83 | |
| | 84 | // startSession verifies the workspace folder exists before dispatching to an |
| | 85 | // adapter, so session cwd fixtures must be real directories. |
| | 86 | const fixtureCwdRoot = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "provider-service-test-")); |
| | 87 | afterAll(() => NodeFS.rmSync(fixtureCwdRoot, { recursive: true, force: true })); |
| | 88 | function fixtureCwd(name: string): string { |
| | 89 | const dir = NodePath.join(fixtureCwdRoot, name); |
| | 90 | NodeFS.mkdirSync(dir, { recursive: true }); |
| | 91 | return dir; |
| | 92 | } |
| | 93 | |
| 82 | const asRequestId = (value: string): ApprovalRequestId => ApprovalRequestId.make(value); | 94 | const asRequestId = (value: string): ApprovalRequestId => ApprovalRequestId.make(value); |
| 83 | const asEventId = (value: string): EventId => EventId.make(value); | 95 | const asEventId = (value: string): EventId => EventId.make(value); |
| 84 | const asThreadId = (value: string): ThreadId => ThreadId.make(value); | 96 | const asThreadId = (value: string): ThreadId => ThreadId.make(value); |
| ... | @@ -425,6 +437,7 @@ function makeProviderServiceLayer( | ... | @@ -425,6 +437,7 @@ function makeProviderServiceLayer( |
| 425 | const layer = it.layer( | 437 | const layer = it.layer( |
| 426 | Layer.mergeAll( | 438 | Layer.mergeAll( |
| 427 | makeProviderServiceLive().pipe( | 439 | makeProviderServiceLive().pipe( |
| | 440 | Layer.provide(NodeServices.layer), |
| 428 | Layer.provide(providerAdapterLayer), | 441 | Layer.provide(providerAdapterLayer), |
| 429 | Layer.provide(directoryLayer), | 442 | Layer.provide(directoryLayer), |
| 430 | Layer.provide(defaultServerSettingsLayer), | 443 | Layer.provide(defaultServerSettingsLayer), |
| ... | @@ -477,6 +490,7 @@ for (const [enabled, completed] of [ | ... | @@ -477,6 +490,7 @@ for (const [enabled, completed] of [ |
| 477 | const scope = yield* Scope.make(); | 490 | const scope = yield* Scope.make(); |
| 478 | const services = yield* Layer.build( | 491 | const services = yield* Layer.build( |
| 479 | makeProviderServiceLive().pipe( | 492 | makeProviderServiceLive().pipe( |
| | 493 | Layer.provide(NodeServices.layer), |
| 480 | Layer.provide( | 494 | Layer.provide( |
| 481 | Layer.succeed(ProviderSessionDirectory.ProviderSessionDirectory, directory), | 495 | Layer.succeed(ProviderSessionDirectory.ProviderSessionDirectory, directory), |
| 482 | ), | 496 | ), |
| ... | @@ -596,6 +610,7 @@ it.effect("ProviderServiceLive catches stopAll failures during shutdown", () => | ... | @@ -596,6 +610,7 @@ it.effect("ProviderServiceLive catches stopAll failures during shutdown", () => |
| 596 | const directoryLayer = ProviderSessionDirectoryLive.pipe(Layer.provide(runtimeRepositoryLayer)); | 610 | const directoryLayer = ProviderSessionDirectoryLive.pipe(Layer.provide(runtimeRepositoryLayer)); |
| 597 | const providerLayer = Layer.mergeAll( | 611 | const providerLayer = Layer.mergeAll( |
| 598 | makeProviderServiceLive().pipe( | 612 | makeProviderServiceLive().pipe( |
| | 613 | Layer.provide(NodeServices.layer), |
| 599 | Layer.provide(providerAdapterLayer), | 614 | Layer.provide(providerAdapterLayer), |
| 600 | Layer.provide(directoryLayer), | 615 | Layer.provide(directoryLayer), |
| 601 | Layer.provide(defaultServerSettingsLayer), | 616 | Layer.provide(defaultServerSettingsLayer), |
| ... | @@ -638,6 +653,7 @@ it.effect("ProviderServiceLive flushes deferred completions during shutdown", () | ... | @@ -638,6 +653,7 @@ it.effect("ProviderServiceLive flushes deferred completions during shutdown", () |
| 638 | const directoryLayer = ProviderSessionDirectoryLive.pipe(Layer.provide(runtimeRepositoryLayer)); | 653 | const directoryLayer = ProviderSessionDirectoryLive.pipe(Layer.provide(runtimeRepositoryLayer)); |
| 639 | const providerLayer = Layer.mergeAll( | 654 | const providerLayer = Layer.mergeAll( |
| 640 | makeProviderServiceLive().pipe( | 655 | makeProviderServiceLive().pipe( |
| | 656 | Layer.provide(NodeServices.layer), |
| 641 | Layer.provide(providerAdapterLayer), | 657 | Layer.provide(providerAdapterLayer), |
| 642 | Layer.provide(directoryLayer), | 658 | Layer.provide(directoryLayer), |
| 643 | Layer.provide(defaultServerSettingsLayer), | 659 | Layer.provide(defaultServerSettingsLayer), |
| ... | @@ -774,6 +790,7 @@ it.effect("ProviderServiceLive rejects new sessions for disabled providers", () | ... | @@ -774,6 +790,7 @@ it.effect("ProviderServiceLive rejects new sessions for disabled providers", () |
| 774 | ); | 790 | ); |
| 775 | const directoryLayer = ProviderSessionDirectoryLive.pipe(Layer.provide(runtimeRepositoryLayer)); | 791 | const directoryLayer = ProviderSessionDirectoryLive.pipe(Layer.provide(runtimeRepositoryLayer)); |
| 776 | const providerLayer = makeProviderServiceLive().pipe( | 792 | const providerLayer = makeProviderServiceLive().pipe( |
| | 793 | Layer.provide(NodeServices.layer), |
| 777 | Layer.provide(providerAdapterLayer), | 794 | Layer.provide(providerAdapterLayer), |
| 778 | Layer.provide(directoryLayer), | 795 | Layer.provide(directoryLayer), |
| 779 | Layer.provide(defaultServerSettingsLayer), | 796 | Layer.provide(defaultServerSettingsLayer), |
| ... | @@ -857,6 +874,7 @@ it.effect( | ... | @@ -857,6 +874,7 @@ it.effect( |
| 857 | Layer.provide(runtimeRepositoryLayer), | 874 | Layer.provide(runtimeRepositoryLayer), |
| 858 | ); | 875 | ); |
| 859 | const providerLayer = makeProviderServiceLive().pipe( | 876 | const providerLayer = makeProviderServiceLive().pipe( |
| | 877 | Layer.provide(NodeServices.layer), |
| 860 | Layer.provide(providerAdapterLayer), | 878 | Layer.provide(providerAdapterLayer), |
| 861 | Layer.provide(directoryLayer), | 879 | Layer.provide(directoryLayer), |
| 862 | Layer.provide(serverSettingsLayer), | 880 | Layer.provide(serverSettingsLayer), |
| ... | @@ -926,6 +944,7 @@ it.effect("ProviderServiceLive rejects new sessions for disabled custom instance | ... | @@ -926,6 +944,7 @@ it.effect("ProviderServiceLive rejects new sessions for disabled custom instance |
| 926 | ); | 944 | ); |
| 927 | const directoryLayer = ProviderSessionDirectoryLive.pipe(Layer.provide(runtimeRepositoryLayer)); | 945 | const directoryLayer = ProviderSessionDirectoryLive.pipe(Layer.provide(runtimeRepositoryLayer)); |
| 928 | const providerLayer = makeProviderServiceLive().pipe( | 946 | const providerLayer = makeProviderServiceLive().pipe( |
| | 947 | Layer.provide(NodeServices.layer), |
| 929 | Layer.provide(providerAdapterLayer), | 948 | Layer.provide(providerAdapterLayer), |
| 930 | Layer.provide(directoryLayer), | 949 | Layer.provide(directoryLayer), |
| 931 | Layer.provide(defaultServerSettingsLayer), | 950 | Layer.provide(defaultServerSettingsLayer), |
| ... | @@ -1044,7 +1063,7 @@ unsupportedRollback.layer("ProviderServiceLive unsupported rewind", (it) => { | ... | @@ -1044,7 +1063,7 @@ unsupportedRollback.layer("ProviderServiceLive unsupported rewind", (it) => { |
| 1044 | yield* provider.startSession(threadId, { | 1063 | yield* provider.startSession(threadId, { |
| 1045 | providerInstanceId: codexInstanceId, | 1064 | providerInstanceId: codexInstanceId, |
| 1046 | threadId, | 1065 | threadId, |
| 1047 | cwd: "/tmp/project", | 1066 | cwd: fixtureCwd("project"), |
| 1048 | runtimeMode: "approval-required", | 1067 | runtimeMode: "approval-required", |
| 1049 | }); | 1068 | }); |
| 1050 | if (!active) { | 1069 | if (!active) { |
| ... | @@ -1098,6 +1117,7 @@ it.effect( | ... | @@ -1098,6 +1117,7 @@ it.effect( |
| 1098 | Layer.provide(runtimeRepositoryLayer), | 1117 | Layer.provide(runtimeRepositoryLayer), |
| 1099 | ); | 1118 | ); |
| 1100 | const providerLayer = makeProviderServiceLive().pipe( | 1119 | const providerLayer = makeProviderServiceLive().pipe( |
| | 1120 | Layer.provide(NodeServices.layer), |
| 1101 | Layer.provide(Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, registry)), | 1121 | Layer.provide(Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, registry)), |
| 1102 | Layer.provide(directoryLayer), | 1122 | Layer.provide(directoryLayer), |
| 1103 | Layer.provide(defaultServerSettingsLayer), | 1123 | Layer.provide(defaultServerSettingsLayer), |
| ... | @@ -1218,6 +1238,7 @@ it.effect("ProviderServiceLive keeps persisted resumable sessions on startup", ( | ... | @@ -1218,6 +1238,7 @@ it.effect("ProviderServiceLive keeps persisted resumable sessions on startup", ( |
| 1218 | }).pipe(Effect.provide(directoryLayer)); | 1238 | }).pipe(Effect.provide(directoryLayer)); |
| 1219 | | 1239 | |
| 1220 | const providerLayer = makeProviderServiceLive().pipe( | 1240 | const providerLayer = makeProviderServiceLive().pipe( |
| | 1241 | Layer.provide(NodeServices.layer), |
| 1221 | Layer.provide(Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, registry)), | 1242 | Layer.provide(Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, registry)), |
| 1222 | Layer.provide(directoryLayer), | 1243 | Layer.provide(directoryLayer), |
| 1223 | Layer.provide(defaultServerSettingsLayer), | 1244 | Layer.provide(defaultServerSettingsLayer), |
| ... | @@ -1283,6 +1304,7 @@ it.effect( | ... | @@ -1283,6 +1304,7 @@ it.effect( |
| 1283 | Layer.provide(runtimeRepositoryLayer), | 1304 | Layer.provide(runtimeRepositoryLayer), |
| 1284 | ); | 1305 | ); |
| 1285 | const firstProviderLayer = makeProviderServiceLive().pipe( | 1306 | const firstProviderLayer = makeProviderServiceLive().pipe( |
| | 1307 | Layer.provide(NodeServices.layer), |
| 1286 | Layer.provide( | 1308 | Layer.provide( |
| 1287 | Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, firstRegistry), | 1309 | Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, firstRegistry), |
| 1288 | ), | 1310 | ), |
| ... | @@ -1310,7 +1332,7 @@ it.effect( | ... | @@ -1310,7 +1332,7 @@ it.effect( |
| 1310 | const session = yield* provider.startSession(threadId, { | 1332 | const session = yield* provider.startSession(threadId, { |
| 1311 | provider: ProviderDriverKind.make("codex"), | 1333 | provider: ProviderDriverKind.make("codex"), |
| 1312 | providerInstanceId: codexInstanceId, | 1334 | providerInstanceId: codexInstanceId, |
| 1313 | cwd: "/tmp/project", | 1335 | cwd: fixtureCwd("project"), |
| 1314 | runtimeMode: "full-access", | 1336 | runtimeMode: "full-access", |
| 1315 | threadId, | 1337 | threadId, |
| 1316 | }); | 1338 | }); |
| ... | @@ -1343,6 +1365,7 @@ it.effect( | ... | @@ -1343,6 +1365,7 @@ it.effect( |
| 1343 | Layer.provide(runtimeRepositoryLayer), | 1365 | Layer.provide(runtimeRepositoryLayer), |
| 1344 | ); | 1366 | ); |
| 1345 | const secondProviderLayer = makeProviderServiceLive().pipe( | 1367 | const secondProviderLayer = makeProviderServiceLive().pipe( |
| | 1368 | Layer.provide(NodeServices.layer), |
| 1346 | Layer.provide( | 1369 | Layer.provide( |
| 1347 | Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, secondRegistry), | 1370 | Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, secondRegistry), |
| 1348 | ), | 1371 | ), |
| ... | @@ -1380,7 +1403,7 @@ it.effect( | ... | @@ -1380,7 +1403,7 @@ it.effect( |
| 1380 | threadId?: string; | 1403 | threadId?: string; |
| 1381 | }; | 1404 | }; |
| 1382 | assert.equal(startPayload.provider, "codex"); | 1405 | assert.equal(startPayload.provider, "codex"); |
| 1383 | assert.equal(startPayload.cwd, "/tmp/project"); | 1406 | assert.equal(startPayload.cwd, fixtureCwd("project")); |
| 1384 | assert.deepEqual(startPayload.resumeCursor, updatedResumeCursor); | 1407 | assert.deepEqual(startPayload.resumeCursor, updatedResumeCursor); |
| 1385 | assert.equal(startPayload.threadId, startedSession.threadId); | 1408 | assert.equal(startPayload.threadId, startedSession.threadId); |
| 1386 | } | 1409 | } |
| ... | @@ -1394,6 +1417,61 @@ it.effect( | ... | @@ -1394,6 +1417,61 @@ it.effect( |
| 1394 | ); | 1417 | ); |
| 1395 | | 1418 | |
| 1396 | routing.layer("ProviderServiceLive routing", (it) => { | 1419 | routing.layer("ProviderServiceLive routing", (it) => { |
| | 1420 | it.effect.each([CODEX_DRIVER, CLAUDE_AGENT_DRIVER, CURSOR_DRIVER])( |
| | 1421 | "rejects missing, file, and saved workspace paths before starting %s", |
| | 1422 | (driver) => |
| | 1423 | Effect.gen(function* () { |
| | 1424 | const provider = yield* ProviderService.ProviderService; |
| | 1425 | const adapter = |
| | 1426 | driver === CODEX_DRIVER |
| | 1427 | ? routing.codex |
| | 1428 | : driver === CLAUDE_AGENT_DRIVER |
| | 1429 | ? routing.claude |
| | 1430 | : routing.cursor; |
| | 1431 | const cwd = fixtureCwd(`missing-workspace-${driver}`); |
| | 1432 | const movedCwd = `${cwd}-moved`; |
| | 1433 | const threadId = asThreadId(`missing-workspace-${driver}`); |
| | 1434 | const input = { |
| | 1435 | provider: driver, |
| | 1436 | providerInstanceId: ProviderInstanceId.make(driver), |
| | 1437 | threadId, |
| | 1438 | runtimeMode: "full-access" as const, |
| | 1439 | cwd, |
| | 1440 | }; |
| | 1441 | |
| | 1442 | yield* provider.startSession(threadId, input); |
| | 1443 | yield* provider.stopSession({ threadId }); |
| | 1444 | adapter.startSession.mockClear(); |
| | 1445 | NodeFS.renameSync(cwd, movedCwd); |
| | 1446 | |
| | 1447 | const failure = yield* provider.startSession(threadId, input).pipe(Effect.flip); |
| | 1448 | assert.instanceOf(failure, ProviderWorkspaceMissingError); |
| | 1449 | assert.include(failure.message, cwd); |
| | 1450 | assert.equal(adapter.startSession.mock.calls.length, 0); |
| | 1451 | |
| | 1452 | const { cwd: _cwd, ...savedInput } = input; |
| | 1453 | const savedFailure = yield* provider.startSession(threadId, savedInput).pipe(Effect.flip); |
| | 1454 | assert.instanceOf(savedFailure, ProviderWorkspaceMissingError); |
| | 1455 | assert.include(savedFailure.message, cwd); |
| | 1456 | assert.equal(adapter.startSession.mock.calls.length, 0); |
| | 1457 | |
| | 1458 | NodeFS.writeFileSync(cwd, "not a directory"); |
| | 1459 | const fileFailure = yield* provider.startSession(threadId, input).pipe(Effect.flip); |
| | 1460 | assert.instanceOf(fileFailure, ProviderWorkspaceMissingError); |
| | 1461 | assert.include(fileFailure.message, cwd); |
| | 1462 | assert.equal(adapter.startSession.mock.calls.length, 0); |
| | 1463 | |
| | 1464 | NodeFS.unlinkSync(cwd); |
| | 1465 | NodeFS.renameSync(movedCwd, cwd); |
| | 1466 | const restored = yield* provider.startSession(threadId, savedInput); |
| | 1467 | assert.equal(restored.cwd, cwd); |
| | 1468 | assert.equal(adapter.startSession.mock.calls.length, 1); |
| | 1469 | yield* provider.stopSession({ threadId }); |
| | 1470 | adapter.startSession.mockClear(); |
| | 1471 | adapter.stopSession.mockClear(); |
| | 1472 | }), |
| | 1473 | ); |
| | 1474 | |
| 1397 | it.effect("allows promptless continuation only for capable providers", () => | 1475 | it.effect("allows promptless continuation only for capable providers", () => |
| 1398 | Effect.gen(function* () { | 1476 | Effect.gen(function* () { |
| 1399 | const provider = yield* ProviderService.ProviderService; | 1477 | const provider = yield* ProviderService.ProviderService; |
| ... | @@ -1452,7 +1530,7 @@ routing.layer("ProviderServiceLive routing", (it) => { | ... | @@ -1452,7 +1530,7 @@ routing.layer("ProviderServiceLive routing", (it) => { |
| 1452 | provider: ProviderDriverKind.make("codex"), | 1530 | provider: ProviderDriverKind.make("codex"), |
| 1453 | providerInstanceId: codexInstanceId, | 1531 | providerInstanceId: codexInstanceId, |
| 1454 | threadId: asThreadId("thread-1"), | 1532 | threadId: asThreadId("thread-1"), |
| 1455 | cwd: "/tmp/project", | 1533 | cwd: fixtureCwd("project"), |
| 1456 | runtimeMode: "full-access", | 1534 | runtimeMode: "full-access", |
| 1457 | }); | 1535 | }); |
| 1458 | assert.equal(session.provider, "codex"); | 1536 | assert.equal(session.provider, "codex"); |
| ... | @@ -1522,7 +1600,7 @@ routing.layer("ProviderServiceLive routing", (it) => { | ... | @@ -1522,7 +1600,7 @@ routing.layer("ProviderServiceLive routing", (it) => { |
| 1522 | threadId?: string; | 1600 | threadId?: string; |
| 1523 | }; | 1601 | }; |
| 1524 | assert.equal(startPayload.provider, "codex"); | 1602 | assert.equal(startPayload.provider, "codex"); |
| 1525 | assert.equal(startPayload.cwd, "/tmp/project"); | 1603 | assert.equal(startPayload.cwd, fixtureCwd("project")); |
| 1526 | assert.deepEqual(startPayload.resumeCursor, session.resumeCursor); | 1604 | assert.deepEqual(startPayload.resumeCursor, session.resumeCursor); |
| 1527 | assert.equal(startPayload.threadId, session.threadId); | 1605 | assert.equal(startPayload.threadId, session.threadId); |
| 1528 | } | 1606 | } |
| ... | @@ -1799,7 +1877,7 @@ routing.layer("ProviderServiceLive routing", (it) => { | ... | @@ -1799,7 +1877,7 @@ routing.layer("ProviderServiceLive routing", (it) => { |
| 1799 | provider: CODEX_DRIVER, | 1877 | provider: CODEX_DRIVER, |
| 1800 | providerInstanceId: codexInstanceId, | 1878 | providerInstanceId: codexInstanceId, |
| 1801 | threadId, | 1879 | threadId, |
| 1802 | cwd: "/tmp/feedback-project", | 1880 | cwd: fixtureCwd("feedback-project"), |
| 1803 | runtimeMode: "full-access", | 1881 | runtimeMode: "full-access", |
| 1804 | }); | 1882 | }); |
| 1805 | yield* routing.codex.stopSession(threadId); | 1883 | yield* routing.codex.stopSession(threadId); |
| ... | @@ -1862,7 +1940,7 @@ routing.layer("ProviderServiceLive routing", (it) => { | ... | @@ -1862,7 +1940,7 @@ routing.layer("ProviderServiceLive routing", (it) => { |
| 1862 | provider: ProviderDriverKind.make("codex"), | 1940 | provider: ProviderDriverKind.make("codex"), |
| 1863 | providerInstanceId: codexInstanceId, | 1941 | providerInstanceId: codexInstanceId, |
| 1864 | threadId: asThreadId("thread-attach"), | 1942 | threadId: asThreadId("thread-attach"), |
| 1865 | cwd: "/tmp/project", | 1943 | cwd: fixtureCwd("project"), |
| 1866 | runtimeMode: "full-access", | 1944 | runtimeMode: "full-access", |
| 1867 | }); | 1945 | }); |
| 1868 | | 1946 | |
| ... | @@ -1937,7 +2015,7 @@ routing.layer("ProviderServiceLive routing", (it) => { | ... | @@ -1937,7 +2015,7 @@ routing.layer("ProviderServiceLive routing", (it) => { |
| 1937 | provider: ProviderDriverKind.make("codex"), | 2015 | provider: ProviderDriverKind.make("codex"), |
| 1938 | providerInstanceId: codexInstanceId, | 2016 | providerInstanceId: codexInstanceId, |
| 1939 | threadId: asThreadId("thread-1"), | 2017 | threadId: asThreadId("thread-1"), |
| 1940 | cwd: "/tmp/project", | 2018 | cwd: fixtureCwd("project"), |
| 1941 | runtimeMode: "full-access", | 2019 | runtimeMode: "full-access", |
| 1942 | }); | 2020 | }); |
| 1943 | yield* routing.codex.stopSession(initial.threadId); | 2021 | yield* routing.codex.stopSession(initial.threadId); |
| ... | @@ -1963,7 +2041,7 @@ routing.layer("ProviderServiceLive routing", (it) => { | ... | @@ -1963,7 +2041,7 @@ routing.layer("ProviderServiceLive routing", (it) => { |
| 1963 | threadId?: string; | 2041 | threadId?: string; |
| 1964 | }; | 2042 | }; |
| 1965 | assert.equal(startPayload.provider, "codex"); | 2043 | assert.equal(startPayload.provider, "codex"); |
| 1966 | assert.equal(startPayload.cwd, "/tmp/project"); | 2044 | assert.equal(startPayload.cwd, fixtureCwd("project")); |
| 1967 | assert.deepEqual(startPayload.resumeCursor, initial.resumeCursor); | 2045 | assert.deepEqual(startPayload.resumeCursor, initial.resumeCursor); |
| 1968 | assert.equal(startPayload.threadId, initial.threadId); | 2046 | assert.equal(startPayload.threadId, initial.threadId); |
| 1969 | } | 2047 | } |
| ... | @@ -1982,7 +2060,7 @@ routing.layer("ProviderServiceLive routing", (it) => { | ... | @@ -1982,7 +2060,7 @@ routing.layer("ProviderServiceLive routing", (it) => { |
| 1982 | provider: ProviderDriverKind.make("codex"), | 2060 | provider: ProviderDriverKind.make("codex"), |
| 1983 | providerInstanceId: codexInstanceId, | 2061 | providerInstanceId: codexInstanceId, |
| 1984 | threadId: asThreadId("thread-reap-preserve"), | 2062 | threadId: asThreadId("thread-reap-preserve"), |
| 1985 | cwd: "/tmp/project-reap-preserve", | 2063 | cwd: fixtureCwd("project-reap-preserve"), |
| 1986 | runtimeMode: "full-access", | 2064 | runtimeMode: "full-access", |
| 1987 | }); | 2065 | }); |
| 1988 | | 2066 | |
| ... | @@ -2017,7 +2095,7 @@ routing.layer("ProviderServiceLive routing", (it) => { | ... | @@ -2017,7 +2095,7 @@ routing.layer("ProviderServiceLive routing", (it) => { |
| 2017 | threadId?: string; | 2095 | threadId?: string; |
| 2018 | }; | 2096 | }; |
| 2019 | assert.equal(startPayload.provider, "codex"); | 2097 | assert.equal(startPayload.provider, "codex"); |
| 2020 | assert.equal(startPayload.cwd, "/tmp/project-reap-preserve"); | 2098 | assert.equal(startPayload.cwd, fixtureCwd("project-reap-preserve")); |
| 2021 | assert.deepEqual(startPayload.resumeCursor, initial.resumeCursor); | 2099 | assert.deepEqual(startPayload.resumeCursor, initial.resumeCursor); |
| 2022 | assert.equal(startPayload.threadId, initial.threadId); | 2100 | assert.equal(startPayload.threadId, initial.threadId); |
| 2023 | } | 2101 | } |
| ... | @@ -2033,7 +2111,7 @@ routing.layer("ProviderServiceLive routing", (it) => { | ... | @@ -2033,7 +2111,7 @@ routing.layer("ProviderServiceLive routing", (it) => { |
| 2033 | provider: ProviderDriverKind.make("claudeAgent"), | 2111 | provider: ProviderDriverKind.make("claudeAgent"), |
| 2034 | providerInstanceId: claudeAgentInstanceId, | 2112 | providerInstanceId: claudeAgentInstanceId, |
| 2035 | threadId: asThreadId("thread-claude"), | 2113 | threadId: asThreadId("thread-claude"), |
| 2036 | cwd: "/tmp/project-claude", | 2114 | cwd: fixtureCwd("project-claude"), |
| 2037 | runtimeMode: "full-access", | 2115 | runtimeMode: "full-access", |
| 2038 | }); | 2116 | }); |
| 2039 | | 2117 | |
| ... | @@ -2049,7 +2127,7 @@ routing.layer("ProviderServiceLive routing", (it) => { | ... | @@ -2049,7 +2127,7 @@ routing.layer("ProviderServiceLive routing", (it) => { |
| 2049 | }; | 2127 | }; |
| 2050 | assert.equal(startPayload.provider, "claudeAgent"); | 2128 | assert.equal(startPayload.provider, "claudeAgent"); |
| 2051 | assert.equal(startPayload.providerInstanceId, claudeAgentInstanceId); | 2129 | assert.equal(startPayload.providerInstanceId, claudeAgentInstanceId); |
| 2052 | assert.equal(startPayload.cwd, "/tmp/project-claude"); | 2130 | assert.equal(startPayload.cwd, fixtureCwd("project-claude")); |
| 2053 | } | 2131 | } |
| 2054 | }), | 2132 | }), |
| 2055 | ); | 2133 | ); |
| ... | @@ -2064,7 +2142,7 @@ routing.layer("ProviderServiceLive routing", (it) => { | ... | @@ -2064,7 +2142,7 @@ routing.layer("ProviderServiceLive routing", (it) => { |
| 2064 | provider: ProviderDriverKind.make("codex"), | 2142 | provider: ProviderDriverKind.make("codex"), |
| 2065 | providerInstanceId: codexInstanceId, | 2143 | providerInstanceId: codexInstanceId, |
| 2066 | threadId, | 2144 | threadId, |
| 2067 | cwd: "/tmp/project-binding-mismatch", | 2145 | cwd: fixtureCwd("project-binding-mismatch"), |
| 2068 | runtimeMode: "full-access", | 2146 | runtimeMode: "full-access", |
| 2069 | }); | 2147 | }); |
| 2070 | yield* directory.upsert({ | 2148 | yield* directory.upsert({ |
| ... | @@ -2094,7 +2172,7 @@ routing.layer("ProviderServiceLive routing", (it) => { | ... | @@ -2094,7 +2172,7 @@ routing.layer("ProviderServiceLive routing", (it) => { |
| 2094 | provider: ProviderDriverKind.make("codex"), | 2172 | provider: ProviderDriverKind.make("codex"), |
| 2095 | providerInstanceId: codexInstanceId, | 2173 | providerInstanceId: codexInstanceId, |
| 2096 | threadId, | 2174 | threadId, |
| 2097 | cwd: "/tmp/project-provider-replacement", | 2175 | cwd: fixtureCwd("project-provider-replacement"), |
| 2098 | runtimeMode: "full-access", | 2176 | runtimeMode: "full-access", |
| 2099 | }); | 2177 | }); |
| 2100 | | 2178 | |
| ... | @@ -2105,7 +2183,7 @@ routing.layer("ProviderServiceLive routing", (it) => { | ... | @@ -2105,7 +2183,7 @@ routing.layer("ProviderServiceLive routing", (it) => { |
| 2105 | provider: ProviderDriverKind.make("claudeAgent"), | 2183 | provider: ProviderDriverKind.make("claudeAgent"), |
| 2106 | providerInstanceId: claudeAgentInstanceId, | 2184 | providerInstanceId: claudeAgentInstanceId, |
| 2107 | threadId, | 2185 | threadId, |
| 2108 | cwd: "/tmp/project-provider-replacement", | 2186 | cwd: fixtureCwd("project-provider-replacement"), |
| 2109 | runtimeMode: "full-access", | 2187 | runtimeMode: "full-access", |
| 2110 | }); | 2188 | }); |
| 2111 | | 2189 | |
| ... | @@ -2132,7 +2210,7 @@ routing.layer("ProviderServiceLive routing", (it) => { | ... | @@ -2132,7 +2210,7 @@ routing.layer("ProviderServiceLive routing", (it) => { |
| 2132 | provider: ProviderDriverKind.make("codex"), | 2210 | provider: ProviderDriverKind.make("codex"), |
| 2133 | providerInstanceId: codexInstanceId, | 2211 | providerInstanceId: codexInstanceId, |
| 2134 | threadId: asThreadId("thread-1"), | 2212 | threadId: asThreadId("thread-1"), |
| 2135 | cwd: "/tmp/project-send-turn", | 2213 | cwd: fixtureCwd("project-send-turn"), |
| 2136 | runtimeMode: "full-access", | 2214 | runtimeMode: "full-access", |
| 2137 | }); | 2215 | }); |
| 2138 | | 2216 | |
| ... | @@ -2157,7 +2235,7 @@ routing.layer("ProviderServiceLive routing", (it) => { | ... | @@ -2157,7 +2235,7 @@ routing.layer("ProviderServiceLive routing", (it) => { |
| 2157 | threadId?: string; | 2235 | threadId?: string; |
| 2158 | }; | 2236 | }; |
| 2159 | assert.equal(startPayload.provider, "codex"); | 2237 | assert.equal(startPayload.provider, "codex"); |
| 2160 | assert.equal(startPayload.cwd, "/tmp/project-send-turn"); | 2238 | assert.equal(startPayload.cwd, fixtureCwd("project-send-turn")); |
| 2161 | assert.deepEqual(startPayload.resumeCursor, initial.resumeCursor); | 2239 | assert.deepEqual(startPayload.resumeCursor, initial.resumeCursor); |
| 2162 | assert.equal(startPayload.threadId, initial.threadId); | 2240 | assert.equal(startPayload.threadId, initial.threadId); |
| 2163 | } | 2241 | } |
| ... | @@ -2173,7 +2251,7 @@ routing.layer("ProviderServiceLive routing", (it) => { | ... | @@ -2173,7 +2251,7 @@ routing.layer("ProviderServiceLive routing", (it) => { |
| 2173 | provider: ProviderDriverKind.make("claudeAgent"), | 2251 | provider: ProviderDriverKind.make("claudeAgent"), |
| 2174 | providerInstanceId: claudeAgentInstanceId, | 2252 | providerInstanceId: claudeAgentInstanceId, |
| 2175 | threadId: asThreadId("thread-claude-send-turn"), | 2253 | threadId: asThreadId("thread-claude-send-turn"), |
| 2176 | cwd: "/tmp/project-claude-send-turn", | 2254 | cwd: fixtureCwd("project-claude-send-turn"), |
| 2177 | modelSelection: createModelSelection( | 2255 | modelSelection: createModelSelection( |
| 2178 | ProviderInstanceId.make("claudeAgent"), | 2256 | ProviderInstanceId.make("claudeAgent"), |
| 2179 | "claude-opus-4-6", | 2257 | "claude-opus-4-6", |
| ... | @@ -2204,7 +2282,7 @@ routing.layer("ProviderServiceLive routing", (it) => { | ... | @@ -2204,7 +2282,7 @@ routing.layer("ProviderServiceLive routing", (it) => { |
| 2204 | threadId?: string; | 2282 | threadId?: string; |
| 2205 | }; | 2283 | }; |
| 2206 | assert.equal(startPayload.provider, "claudeAgent"); | 2284 | assert.equal(startPayload.provider, "claudeAgent"); |
| 2207 | assert.equal(startPayload.cwd, "/tmp/project-claude-send-turn"); | 2285 | assert.equal(startPayload.cwd, fixtureCwd("project-claude-send-turn")); |
| 2208 | assert.deepEqual( | 2286 | assert.deepEqual( |
| 2209 | startPayload.modelSelection, | 2287 | startPayload.modelSelection, |
| 2210 | createModelSelection(ProviderInstanceId.make("claudeAgent"), "claude-opus-4-6", [ | 2288 | createModelSelection(ProviderInstanceId.make("claudeAgent"), "claude-opus-4-6", [ |
| ... | @@ -2368,6 +2446,7 @@ routing.layer("ProviderServiceLive routing", (it) => { | ... | @@ -2368,6 +2446,7 @@ routing.layer("ProviderServiceLive routing", (it) => { |
| 2368 | Layer.provide(runtimeRepositoryLayer), | 2446 | Layer.provide(runtimeRepositoryLayer), |
| 2369 | ); | 2447 | ); |
| 2370 | const firstProviderLayer = makeProviderServiceLive().pipe( | 2448 | const firstProviderLayer = makeProviderServiceLive().pipe( |
| | 2449 | Layer.provide(NodeServices.layer), |
| 2371 | Layer.provide( | 2450 | Layer.provide( |
| 2372 | Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, firstRegistry), | 2451 | Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, firstRegistry), |
| 2373 | ), | 2452 | ), |
| ... | @@ -2389,7 +2468,7 @@ routing.layer("ProviderServiceLive routing", (it) => { | ... | @@ -2389,7 +2468,7 @@ routing.layer("ProviderServiceLive routing", (it) => { |
| 2389 | provider: ProviderDriverKind.make("claudeAgent"), | 2468 | provider: ProviderDriverKind.make("claudeAgent"), |
| 2390 | providerInstanceId: claudeAgentInstanceId, | 2469 | providerInstanceId: claudeAgentInstanceId, |
| 2391 | threadId: asThreadId("thread-claude-start"), | 2470 | threadId: asThreadId("thread-claude-start"), |
| 2392 | cwd: "/tmp/project-claude-start", | 2471 | cwd: fixtureCwd("project-claude-start"), |
| 2393 | runtimeMode: "full-access", | 2472 | runtimeMode: "full-access", |
| 2394 | }); | 2473 | }); |
| 2395 | }).pipe(Effect.provide(firstProviderLayer)); | 2474 | }).pipe(Effect.provide(firstProviderLayer)); |
| ... | @@ -2407,6 +2486,7 @@ routing.layer("ProviderServiceLive routing", (it) => { | ... | @@ -2407,6 +2486,7 @@ routing.layer("ProviderServiceLive routing", (it) => { |
| 2407 | Layer.provide(runtimeRepositoryLayer), | 2486 | Layer.provide(runtimeRepositoryLayer), |
| 2408 | ); | 2487 | ); |
| 2409 | const secondProviderLayer = makeProviderServiceLive().pipe( | 2488 | const secondProviderLayer = makeProviderServiceLive().pipe( |
| | 2489 | Layer.provide(NodeServices.layer), |
| 2410 | Layer.provide( | 2490 | Layer.provide( |
| 2411 | Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, secondRegistry), | 2491 | Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, secondRegistry), |
| 2412 | ), | 2492 | ), |
| ... | @@ -2430,7 +2510,7 @@ routing.layer("ProviderServiceLive routing", (it) => { | ... | @@ -2430,7 +2510,7 @@ routing.layer("ProviderServiceLive routing", (it) => { |
| 2430 | provider: ProviderDriverKind.make("claudeAgent"), | 2510 | provider: ProviderDriverKind.make("claudeAgent"), |
| 2431 | providerInstanceId: claudeAgentInstanceId, | 2511 | providerInstanceId: claudeAgentInstanceId, |
| 2432 | threadId: initial.threadId, | 2512 | threadId: initial.threadId, |
| 2433 | cwd: "/tmp/project-claude-start", | 2513 | cwd: fixtureCwd("project-claude-start"), |
| 2434 | runtimeMode: "full-access", | 2514 | runtimeMode: "full-access", |
| 2435 | }); | 2515 | }); |
| 2436 | }).pipe(Effect.provide(secondProviderLayer)); | 2516 | }).pipe(Effect.provide(secondProviderLayer)); |
| ... | @@ -2446,7 +2526,7 @@ routing.layer("ProviderServiceLive routing", (it) => { | ... | @@ -2446,7 +2526,7 @@ routing.layer("ProviderServiceLive routing", (it) => { |
| 2446 | threadId?: string; | 2526 | threadId?: string; |
| 2447 | }; | 2527 | }; |
| 2448 | assert.equal(startPayload.provider, "claudeAgent"); | 2528 | assert.equal(startPayload.provider, "claudeAgent"); |
| 2449 | assert.equal(startPayload.cwd, "/tmp/project-claude-start"); | 2529 | assert.equal(startPayload.cwd, fixtureCwd("project-claude-start")); |
| 2450 | assert.deepEqual(startPayload.resumeCursor, initial.resumeCursor); | 2530 | assert.deepEqual(startPayload.resumeCursor, initial.resumeCursor); |
| 2451 | assert.equal(startPayload.threadId, initial.threadId); | 2531 | assert.equal(startPayload.threadId, initial.threadId); |
| 2452 | } | 2532 | } |
| ... | @@ -2476,6 +2556,7 @@ routing.layer("ProviderServiceLive routing", (it) => { | ... | @@ -2476,6 +2556,7 @@ routing.layer("ProviderServiceLive routing", (it) => { |
| 2476 | Layer.provide(runtimeRepositoryLayer), | 2556 | Layer.provide(runtimeRepositoryLayer), |
| 2477 | ); | 2557 | ); |
| 2478 | const firstProviderLayer = makeProviderServiceLive().pipe( | 2558 | const firstProviderLayer = makeProviderServiceLive().pipe( |
| | 2559 | Layer.provide(NodeServices.layer), |
| 2479 | Layer.provide( | 2560 | Layer.provide( |
| 2480 | Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, firstRegistry), | 2561 | Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, firstRegistry), |
| 2481 | ), | 2562 | ), |
| ... | @@ -2497,7 +2578,7 @@ routing.layer("ProviderServiceLive routing", (it) => { | ... | @@ -2497,7 +2578,7 @@ routing.layer("ProviderServiceLive routing", (it) => { |
| 2497 | provider: ProviderDriverKind.make("claudeAgent"), | 2578 | provider: ProviderDriverKind.make("claudeAgent"), |
| 2498 | providerInstanceId: claudeAgentInstanceId, | 2579 | providerInstanceId: claudeAgentInstanceId, |
| 2499 | threadId: asThreadId("thread-claude-cwd"), | 2580 | threadId: asThreadId("thread-claude-cwd"), |
| 2500 | cwd: "/tmp/project-claude-cwd", | 2581 | cwd: fixtureCwd("project-claude-cwd"), |
| 2501 | runtimeMode: "full-access", | 2582 | runtimeMode: "full-access", |
| 2502 | }); | 2583 | }); |
| 2503 | }).pipe(Effect.provide(firstProviderLayer)); | 2584 | }).pipe(Effect.provide(firstProviderLayer)); |
| ... | @@ -2510,6 +2591,7 @@ routing.layer("ProviderServiceLive routing", (it) => { | ... | @@ -2510,6 +2591,7 @@ routing.layer("ProviderServiceLive routing", (it) => { |
| 2510 | Layer.provide(runtimeRepositoryLayer), | 2591 | Layer.provide(runtimeRepositoryLayer), |
| 2511 | ); | 2592 | ); |
| 2512 | const secondProviderLayer = makeProviderServiceLive().pipe( | 2593 | const secondProviderLayer = makeProviderServiceLive().pipe( |
| | 2594 | Layer.provide(NodeServices.layer), |
| 2513 | Layer.provide( | 2595 | Layer.provide( |
| 2514 | Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, secondRegistry), | 2596 | Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, secondRegistry), |
| 2515 | ), | 2597 | ), |
| ... | @@ -2548,7 +2630,7 @@ routing.layer("ProviderServiceLive routing", (it) => { | ... | @@ -2548,7 +2630,7 @@ routing.layer("ProviderServiceLive routing", (it) => { |
| 2548 | threadId?: string; | 2630 | threadId?: string; |
| 2549 | }; | 2631 | }; |
| 2550 | assert.equal(startPayload.provider, "claudeAgent"); | 2632 | assert.equal(startPayload.provider, "claudeAgent"); |
| 2551 | assert.equal(startPayload.cwd, "/tmp/project-claude-cwd"); | 2633 | assert.equal(startPayload.cwd, fixtureCwd("project-claude-cwd")); |
| 2552 | assert.deepEqual(startPayload.resumeCursor, initial.resumeCursor); | 2634 | assert.deepEqual(startPayload.resumeCursor, initial.resumeCursor); |
| 2553 | assert.equal(startPayload.threadId, initial.threadId); | 2635 | assert.equal(startPayload.threadId, initial.threadId); |
| 2554 | } | 2636 | } |
| ... | @@ -2742,7 +2824,7 @@ fanout.layer("ProviderServiceLive fanout", (it) => { | ... | @@ -2742,7 +2824,7 @@ fanout.layer("ProviderServiceLive fanout", (it) => { |
| 2742 | provider: ProviderDriverKind.make("claudeAgent"), | 2824 | provider: ProviderDriverKind.make("claudeAgent"), |
| 2743 | providerInstanceId: claudeAgentInstanceId, | 2825 | providerInstanceId: claudeAgentInstanceId, |
| 2744 | threadId: asThreadId("thread-metrics"), | 2826 | threadId: asThreadId("thread-metrics"), |
| 2745 | cwd: "/tmp/project", | 2827 | cwd: fixtureCwd("project"), |
| 2746 | runtimeMode: "full-access", | 2828 | runtimeMode: "full-access", |
| 2747 | }); | 2829 | }); |
| 2748 | | 2830 | |
| ... | @@ -2820,7 +2902,7 @@ fanout.layer("ProviderServiceLive fanout", (it) => { | ... | @@ -2820,7 +2902,7 @@ fanout.layer("ProviderServiceLive fanout", (it) => { |
| 2820 | provider: ProviderDriverKind.make("claudeAgent"), | 2902 | provider: ProviderDriverKind.make("claudeAgent"), |
| 2821 | providerInstanceId: claudeAgentInstanceId, | 2903 | providerInstanceId: claudeAgentInstanceId, |
| 2822 | threadId: asThreadId("thread-send-metrics"), | 2904 | threadId: asThreadId("thread-send-metrics"), |
| 2823 | cwd: "/tmp/project-send-metrics", | 2905 | cwd: fixtureCwd("project-send-metrics"), |
| 2824 | runtimeMode: "full-access", | 2906 | runtimeMode: "full-access", |
| 2825 | }); | 2907 | }); |
| 2826 | | 2908 | |
| ... | @@ -4155,7 +4237,7 @@ validation.layer("ProviderServiceLive validation", (it) => { | ... | @@ -4155,7 +4237,7 @@ validation.layer("ProviderServiceLive validation", (it) => { |
| 4155 | provider: ProviderDriverKind.make("codex"), | 4237 | provider: ProviderDriverKind.make("codex"), |
| 4156 | providerInstanceId: codexInstanceId, | 4238 | providerInstanceId: codexInstanceId, |
| 4157 | threadId: asThreadId("thread-missing"), | 4239 | threadId: asThreadId("thread-missing"), |
| 4158 | cwd: "/tmp/project", | 4240 | cwd: fixtureCwd("project"), |
| 4159 | runtimeMode: "full-access", | 4241 | runtimeMode: "full-access", |
| 4160 | }); | 4242 | }); |
| 4161 | | 4243 | |
| ... | @@ -4204,7 +4286,7 @@ boundedListing.layer("ProviderServiceLive session listing", (it) => { | ... | @@ -4204,7 +4286,7 @@ boundedListing.layer("ProviderServiceLive session listing", (it) => { |
| 4204 | provider: CODEX_DRIVER, | 4286 | provider: CODEX_DRIVER, |
| 4205 | providerInstanceId: codexInstanceId, | 4287 | providerInstanceId: codexInstanceId, |
| 4206 | threadId: activeSessionThreadId, | 4288 | threadId: activeSessionThreadId, |
| 4207 | cwd: "/tmp/project-active-session", | 4289 | cwd: fixtureCwd("project-active-session"), |
| 4208 | runtimeMode: "full-access", | 4290 | runtimeMode: "full-access", |
| 4209 | }); | 4291 | }); |
| 4210 | listThreadIds.mockClear(); | 4292 | listThreadIds.mockClear(); |