| ... | ... | @@ -55,11 +55,15 @@ import { |
| 55 | 55 | makeSqlitePersistenceLive, |
| 56 | 56 | SqlitePersistenceMemory, |
| 57 | 57 | } from "../../persistence/Layers/Sqlite.ts"; |
| 58 | import * as ServerConfig from "../../config.ts"; |
| 58 | 59 | import * as ServerSettings from "../../serverSettings.ts"; |
| 59 | 60 | import * as AnalyticsService from "../../telemetry/AnalyticsService.ts"; |
| 60 | 61 | import { makeAdapterRegistryMock } from "../testUtils/providerAdapterRegistryMock.ts"; |
| 61 | 62 | |
| 62 | 63 | const defaultServerSettingsLayer = ServerSettings.ServerSettingsService.layerTest(); |
| 64 | const serverConfigTestLayer = ServerConfig.layerTest(process.cwd(), process.cwd()).pipe( |
| 65 | Layer.provide(NodeServices.layer), |
| 66 | ); |
| 63 | 67 | |
| 64 | 68 | const asRequestId = (value: string): ApprovalRequestId => ApprovalRequestId.make(value); |
| 65 | 69 | const asEventId = (value: string): EventId => EventId.make(value); |
| ... | ... | @@ -292,6 +296,7 @@ function makeProviderServiceLayer() { |
| 292 | 296 | Layer.provide(providerAdapterLayer), |
| 293 | 297 | Layer.provide(directoryLayer), |
| 294 | 298 | Layer.provide(defaultServerSettingsLayer), |
| 299 | Layer.provide(serverConfigTestLayer), |
| 295 | 300 | Layer.provideMerge(AnalyticsService.layerTest), |
| 296 | 301 | Layer.provide( |
| 297 | 302 | Layer.succeed( |
| ... | ... | @@ -343,6 +348,7 @@ it.effect("ProviderServiceLive catches stopAll failures during shutdown", () => |
| 343 | 348 | Layer.provide(providerAdapterLayer), |
| 344 | 349 | Layer.provide(directoryLayer), |
| 345 | 350 | Layer.provide(defaultServerSettingsLayer), |
| 351 | Layer.provide(serverConfigTestLayer), |
| 346 | 352 | Layer.provideMerge(AnalyticsService.layerTest), |
| 347 | 353 | Layer.provide( |
| 348 | 354 | Layer.succeed( |
| ... | ... | @@ -402,6 +408,7 @@ it.effect("ProviderServiceLive rejects new sessions for disabled providers", () |
| 402 | 408 | Layer.provide(providerAdapterLayer), |
| 403 | 409 | Layer.provide(directoryLayer), |
| 404 | 410 | Layer.provide(defaultServerSettingsLayer), |
| 411 | Layer.provide(serverConfigTestLayer), |
| 405 | 412 | Layer.provide(AnalyticsService.layerTest), |
| 406 | 413 | Layer.provide( |
| 407 | 414 | Layer.succeed( |
| ... | ... | @@ -486,6 +493,7 @@ it.effect( |
| 486 | 493 | Layer.provide(providerAdapterLayer), |
| 487 | 494 | Layer.provide(directoryLayer), |
| 488 | 495 | Layer.provide(serverSettingsLayer), |
| 496 | Layer.provide(serverConfigTestLayer), |
| 489 | 497 | Layer.provide(AnalyticsService.layerTest), |
| 490 | 498 | Layer.provide( |
| 491 | 499 | Layer.succeed( |
| ... | ... | @@ -556,6 +564,7 @@ it.effect("ProviderServiceLive rejects new sessions for disabled custom instance |
| 556 | 564 | Layer.provide(providerAdapterLayer), |
| 557 | 565 | Layer.provide(directoryLayer), |
| 558 | 566 | Layer.provide(defaultServerSettingsLayer), |
| 567 | Layer.provide(serverConfigTestLayer), |
| 559 | 568 | Layer.provide(AnalyticsService.layerTest), |
| 560 | 569 | Layer.provide( |
| 561 | 570 | Layer.succeed( |
| ... | ... | @@ -611,6 +620,7 @@ it.effect("ProviderServiceLive writes canonical events to the emitting thread se |
| 611 | 620 | Layer.provide(Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, registry)), |
| 612 | 621 | Layer.provide(directoryLayer), |
| 613 | 622 | Layer.provide(defaultServerSettingsLayer), |
| 623 | Layer.provide(serverConfigTestLayer), |
| 614 | 624 | Layer.provide(AnalyticsService.layerTest), |
| 615 | 625 | Layer.provide( |
| 616 | 626 | Layer.succeed( |
| ... | ... | @@ -671,6 +681,7 @@ it.effect("ProviderServiceLive keeps persisted resumable sessions on startup", ( |
| 671 | 681 | Layer.provide(Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, registry)), |
| 672 | 682 | Layer.provide(directoryLayer), |
| 673 | 683 | Layer.provide(defaultServerSettingsLayer), |
| 684 | Layer.provide(serverConfigTestLayer), |
| 674 | 685 | Layer.provide(AnalyticsService.layerTest), |
| 675 | 686 | Layer.provide( |
| 676 | 687 | Layer.succeed( |
| ... | ... | @@ -737,6 +748,7 @@ it.effect( |
| 737 | 748 | ), |
| 738 | 749 | Layer.provide(firstDirectoryLayer), |
| 739 | 750 | Layer.provide(defaultServerSettingsLayer), |
| 751 | Layer.provide(serverConfigTestLayer), |
| 740 | 752 | Layer.provide(AnalyticsService.layerTest), |
| 741 | 753 | Layer.provide( |
| 742 | 754 | Layer.succeed( |
| ... | ... | @@ -796,6 +808,7 @@ it.effect( |
| 796 | 808 | ), |
| 797 | 809 | Layer.provide(secondDirectoryLayer), |
| 798 | 810 | Layer.provide(defaultServerSettingsLayer), |
| 811 | Layer.provide(serverConfigTestLayer), |
| 799 | 812 | Layer.provide(AnalyticsService.layerTest), |
| 800 | 813 | Layer.provide( |
| 801 | 814 | Layer.succeed( |
| ... | ... | @@ -927,6 +940,54 @@ routing.layer("ProviderServiceLive routing", (it) => { |
| 927 | 940 | }), |
| 928 | 941 | ); |
| 929 | 942 | |
| 943 | it.effect("appends attachment file paths to the turn input text", () => |
| 944 | Effect.gen(function* () { |
| 945 | const provider = yield* ProviderService.ProviderService; |
| 946 | |
| 947 | const session = yield* provider.startSession(asThreadId("thread-attach"), { |
| 948 | provider: ProviderDriverKind.make("codex"), |
| 949 | providerInstanceId: codexInstanceId, |
| 950 | threadId: asThreadId("thread-attach"), |
| 951 | cwd: "/tmp/project", |
| 952 | runtimeMode: "full-access", |
| 953 | }); |
| 954 | |
| 955 | const attachment = { |
| 956 | type: "image" as const, |
| 957 | id: "thread-attach-12345678-1234-1234-1234-123456789abc", |
| 958 | name: "screenshot.png", |
| 959 | mimeType: "image/png", |
| 960 | sizeBytes: 123, |
| 961 | }; |
| 962 | |
| 963 | routing.codex.sendTurn.mockClear(); |
| 964 | yield* provider.sendTurn({ |
| 965 | threadId: session.threadId, |
| 966 | input: "use this screenshot", |
| 967 | attachments: [attachment], |
| 968 | }); |
| 969 | |
| 970 | const turnInput = routing.codex.sendTurn.mock.calls[0]?.[0] as ProviderSendTurnInput; |
| 971 | assert.equal(typeof turnInput.input, "string"); |
| 972 | const turnText = turnInput.input ?? ""; |
| 973 | assert.equal(turnText.startsWith("use this screenshot"), true); |
| 974 | assert.include(turnText, '[Attached image "screenshot.png" is saved at: '); |
| 975 | assert.equal(turnText.endsWith(`${attachment.id}.png]`), true); |
| 976 | |
| 977 | // An attachment-only turn stays valid and the injected line becomes the |
| 978 | // whole input text, so the agent still learns the path. |
| 979 | routing.codex.sendTurn.mockClear(); |
| 980 | yield* provider.sendTurn({ |
| 981 | threadId: session.threadId, |
| 982 | attachments: [attachment], |
| 983 | }); |
| 984 | const imageOnlyInput = routing.codex.sendTurn.mock.calls[0]?.[0] as ProviderSendTurnInput; |
| 985 | assert.equal(imageOnlyInput.input?.startsWith('[Attached image "screenshot.png"'), true); |
| 986 | |
| 987 | yield* provider.stopSession({ threadId: session.threadId }); |
| 988 | }), |
| 989 | ); |
| 990 | |
| 930 | 991 | it.effect("recovers stale persisted sessions for rollback by resuming thread identity", () => |
| 931 | 992 | Effect.gen(function* () { |
| 932 | 993 | const provider = yield* ProviderService.ProviderService; |
| ... | ... | @@ -1307,6 +1368,7 @@ routing.layer("ProviderServiceLive routing", (it) => { |
| 1307 | 1368 | ), |
| 1308 | 1369 | Layer.provide(firstDirectoryLayer), |
| 1309 | 1370 | Layer.provide(defaultServerSettingsLayer), |
| 1371 | Layer.provide(serverConfigTestLayer), |
| 1310 | 1372 | Layer.provide(AnalyticsService.layerTest), |
| 1311 | 1373 | Layer.provide( |
| 1312 | 1374 | Layer.succeed( |
| ... | ... | @@ -1345,6 +1407,7 @@ routing.layer("ProviderServiceLive routing", (it) => { |
| 1345 | 1407 | ), |
| 1346 | 1408 | Layer.provide(secondDirectoryLayer), |
| 1347 | 1409 | Layer.provide(defaultServerSettingsLayer), |
| 1410 | Layer.provide(serverConfigTestLayer), |
| 1348 | 1411 | Layer.provide(AnalyticsService.layerTest), |
| 1349 | 1412 | Layer.provide( |
| 1350 | 1413 | Layer.succeed( |
| ... | ... | @@ -1413,6 +1476,7 @@ routing.layer("ProviderServiceLive routing", (it) => { |
| 1413 | 1476 | ), |
| 1414 | 1477 | Layer.provide(firstDirectoryLayer), |
| 1415 | 1478 | Layer.provide(defaultServerSettingsLayer), |
| 1479 | Layer.provide(serverConfigTestLayer), |
| 1416 | 1480 | Layer.provide(AnalyticsService.layerTest), |
| 1417 | 1481 | Layer.provide( |
| 1418 | 1482 | Layer.succeed( |
| ... | ... | @@ -1446,6 +1510,7 @@ routing.layer("ProviderServiceLive routing", (it) => { |
| 1446 | 1510 | ), |
| 1447 | 1511 | Layer.provide(secondDirectoryLayer), |
| 1448 | 1512 | Layer.provide(defaultServerSettingsLayer), |
| 1513 | Layer.provide(serverConfigTestLayer), |
| 1449 | 1514 | Layer.provide(AnalyticsService.layerTest), |
| 1450 | 1515 | Layer.provide( |
| 1451 | 1516 | Layer.succeed( |