| author | |
| committer | |
| log | 568c9bc4d0ef0dddf74d813619c9877bd35624d7 |
| tree | 3da47ab6e2055fa589b848c137d72096e5e2095b |
| parent | 3e2370fbbf1efacb341a68e6f28efb06b695e148 |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>174 files changed, 1152 insertions(+), 1311 deletions(-)
apps/desktop/src/app/DesktopAppIdentity.test.ts+1-1| ... | ... | @@ -76,7 +76,7 @@ const makeAssetsLayer = (png: Option.Option<string>) => |
| 76 | 76 | icns: Option.none(), |
| 77 | 77 | png, |
| 78 | 78 | }), |
| 79 | resolveResourcePath: () => Effect.succeed(Option.none()), | |
| 79 | resolveResourcePath: () => Effect.succeedNone, | |
| 80 | 80 | } satisfies DesktopAssets.DesktopAssets["Service"]); |
| 81 | 81 | |
| 82 | 82 | const makeEnvironmentLayer = (overrides: TestEnvironmentInput = {}) => { |
apps/desktop/src/app/DesktopConnectionCatalogStore.test.ts+1-1| ... | ... | @@ -46,7 +46,7 @@ function makeSafeStorageLayer(available: boolean, failDecrypt: Ref.Ref<boolean> |
| 46 | 46 | return decoded.slice("encrypted:".length); |
| 47 | 47 | }); |
| 48 | 48 | }, |
| 49 | selectedStorageBackend: Effect.succeed(Option.none()), | |
| 49 | selectedStorageBackend: Effect.succeedNone, | |
| 50 | 50 | } satisfies ElectronSafeStorage.ElectronSafeStorage["Service"]); |
| 51 | 51 | } |
| 52 | 52 |
apps/desktop/src/app/DesktopConnectionCatalogStore.ts+1-1| ... | ... | @@ -205,7 +205,7 @@ const readDocument = ( |
| 205 | 205 | raw === null |
| 206 | 206 | ? Effect.succeed(Option.none<EncryptedConnectionCatalogDocument>()) |
| 207 | 207 | : decodeEncryptedConnectionCatalogDocumentJson(raw).pipe( |
| 208 | Effect.map(Option.some), | |
| 208 | Effect.asSome, | |
| 209 | 209 | Effect.mapError( |
| 210 | 210 | (cause) => |
| 211 | 211 | new DesktopConnectionCatalogStoreDocumentDecodeError({ |
apps/desktop/src/backend/DesktopBackendConfiguration.ts+2-2| ... | ... | @@ -199,11 +199,11 @@ const readPersistedBackendObservabilitySettings = Effect.gen(function* () { |
| 199 | 199 | const fileSystem = yield* FileSystem.FileSystem; |
| 200 | 200 | const environment = yield* DesktopEnvironment.DesktopEnvironment; |
| 201 | 201 | const raw = yield* fileSystem.readFileString(environment.serverSettingsPath).pipe( |
| 202 | Effect.map(Option.some), | |
| 202 | Effect.asSome, | |
| 203 | 203 | Effect.catchTags({ |
| 204 | 204 | PlatformError: (cause) => |
| 205 | 205 | cause.reason._tag === "NotFound" |
| 206 | ? Effect.succeed(Option.none()) | |
| 206 | ? Effect.succeedNone | |
| 207 | 207 | : logBackendObservabilitySettingsReadFailure(environment.serverSettingsPath, cause).pipe( |
| 208 | 208 | Effect.as(Option.none()), |
| 209 | 209 | ), |
apps/desktop/src/backend/DesktopBackendManager.test.ts+2-2| ... | ... | @@ -162,7 +162,7 @@ function makeTestInstance(input: MakeInstanceInput) { |
| 162 | 162 | forInstance: () => Effect.succeed(stubLog), |
| 163 | 163 | } satisfies DesktopObservability.DesktopBackendOutputLogFactory["Service"]), |
| 164 | 164 | Layer.succeed(DesktopTelemetryPublisher.DesktopTelemetryPublisher, { |
| 165 | latest: Effect.succeed(Option.none()), | |
| 165 | latest: Effect.succeedNone, | |
| 166 | 166 | changes: Stream.empty, |
| 167 | 167 | encoded: input.desktopTelemetryStream ?? Stream.empty, |
| 168 | 168 | handleControlForSource: () => Effect.void, |
| ... | ... | @@ -1545,7 +1545,7 @@ describe("DesktopBackendManager", () => { |
| 1545 | 1545 | |
| 1546 | 1546 | const mockPool = Layer.succeed(DesktopBackendPool.DesktopBackendPool, { |
| 1547 | 1547 | list: Effect.succeed([instance1, instance2]), |
| 1548 | get: () => Effect.succeed(Option.none()), | |
| 1548 | get: () => Effect.succeedNone, | |
| 1549 | 1549 | primary: Effect.die(new Error("primary not implemented")), |
| 1550 | 1550 | register: () => Effect.die(new Error("register not implemented")), |
| 1551 | 1551 | unregister: () => Effect.die(new Error("unregister not implemented")), |
apps/desktop/src/backend/DesktopBackendPool.test.ts+2-4| ... | ... | @@ -67,7 +67,7 @@ function makePoolLayer( |
| 67 | 67 | } satisfies DesktopObservability.DesktopBackendOutputLogShape), |
| 68 | 68 | } satisfies DesktopObservability.DesktopBackendOutputLogFactory["Service"]), |
| 69 | 69 | Layer.succeed(DesktopTelemetryPublisher.DesktopTelemetryPublisher, { |
| 70 | latest: Effect.succeed(Option.none()), | |
| 70 | latest: Effect.succeedNone, | |
| 71 | 71 | changes: Stream.empty, |
| 72 | 72 | encoded: Stream.empty, |
| 73 | 73 | handleControlForSource: () => Effect.void, |
| ... | ... | @@ -135,9 +135,7 @@ describe("DesktopBackendPool", () => { |
| 135 | 135 | |
| 136 | 136 | it.effect("layerTest dies when no instances are supplied", () => |
| 137 | 137 | Effect.exit( |
| 138 | Effect.gen(function* () { | |
| 139 | yield* DesktopBackendPool.DesktopBackendPool; | |
| 140 | }).pipe(Effect.provide(DesktopBackendPool.layerTest([]))), | |
| 138 | DesktopBackendPool.DesktopBackendPool.pipe(Effect.provide(DesktopBackendPool.layerTest([]))), | |
| 141 | 139 | ).pipe(Effect.map((exit) => assert.equal(exit._tag, "Failure"))), |
| 142 | 140 | ); |
| 143 | 141 |
apps/desktop/src/backend/DesktopLocalEnvironmentAuth.test.ts+1-2| ... | ... | @@ -1,7 +1,6 @@ |
| 1 | 1 | import { assert, describe, it } from "@effect/vitest"; |
| 2 | 2 | import * as Effect from "effect/Effect"; |
| 3 | 3 | import * as Layer from "effect/Layer"; |
| 4 | import * as Option from "effect/Option"; | |
| 5 | 4 | import * as Ref from "effect/Ref"; |
| 6 | 5 | import * as HttpClient from "effect/unstable/http/HttpClient"; |
| 7 | 6 | import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse"; |
| ... | ... | @@ -60,7 +59,7 @@ describe("DesktopLocalEnvironmentAuth", () => { |
| 60 | 59 | { |
| 61 | 60 | id: PRIMARY_LOCAL_ENVIRONMENT_ID, |
| 62 | 61 | label: Effect.succeed("Windows"), |
| 63 | currentConfig: Effect.succeed(Option.some(config)), | |
| 62 | currentConfig: Effect.succeedSome(config), | |
| 64 | 63 | }, |
| 65 | 64 | ]), |
| 66 | 65 | } as unknown as DesktopBackendPool.DesktopBackendPool["Service"]); |
apps/desktop/src/electron/ElectronMenu.ts+1-1| ... | ... | @@ -226,7 +226,7 @@ export const make = Effect.gen(function* () { |
| 226 | 226 | Effect.callback<Option.Option<string>>((resume) => { |
| 227 | 227 | const normalizedItems = normalizeContextMenuItems(input.items); |
| 228 | 228 | if (normalizedItems.length === 0) { |
| 229 | resume(Effect.succeed(Option.none())); | |
| 229 | resume(Effect.succeedNone); | |
| 230 | 230 | return; |
| 231 | 231 | } |
| 232 | 232 |
apps/desktop/src/ipc/methods/snapShot.test.ts+13-15| ... | ... | @@ -51,7 +51,7 @@ describe("window capture IPC", () => { |
| 51 | 51 | Effect.provide( |
| 52 | 52 | Layer.mergeAll( |
| 53 | 53 | Layer.succeed(ElectronWindow.ElectronWindow, { |
| 54 | main: Effect.succeed(Option.some({ webContents: { id: 7 } })), | |
| 54 | main: Effect.succeedSome({ webContents: { id: 7 } }), | |
| 55 | 55 | } as ElectronWindow.ElectronWindow["Service"]), |
| 56 | 56 | Layer.succeed(DesktopSnapShot.DesktopSnapShot, { |
| 57 | 57 | previewConfig: () => |
| ... | ... | @@ -87,7 +87,7 @@ describe("window capture IPC", () => { |
| 87 | 87 | Effect.provide( |
| 88 | 88 | Layer.mergeAll( |
| 89 | 89 | Layer.succeed(ElectronWindow.ElectronWindow, { |
| 90 | main: Effect.succeed(Option.some({ webContents: { id: 7 } })), | |
| 90 | main: Effect.succeedSome({ webContents: { id: 7 } }), | |
| 91 | 91 | } as ElectronWindow.ElectronWindow["Service"]), |
| 92 | 92 | Layer.succeed(DesktopSnapShot.DesktopSnapShot, { |
| 93 | 93 | state: Effect.succeed({ |
| ... | ... | @@ -120,7 +120,7 @@ describe("window capture IPC", () => { |
| 120 | 120 | Effect.provide( |
| 121 | 121 | Layer.mergeAll( |
| 122 | 122 | Layer.succeed(ElectronWindow.ElectronWindow, { |
| 123 | main: Effect.succeed(Option.some({ webContents: { id: 7 } })), | |
| 123 | main: Effect.succeedSome({ webContents: { id: 7 } }), | |
| 124 | 124 | } as ElectronWindow.ElectronWindow["Service"]), |
| 125 | 125 | Layer.succeed(DesktopSnapShot.DesktopSnapShot, { |
| 126 | 126 | state: Effect.succeed({ linuxBackend: "niri" }), |
| ... | ... | @@ -156,13 +156,11 @@ describe("window capture IPC", () => { |
| 156 | 156 | Layer.succeed( |
| 157 | 157 | ElectronWindow.ElectronWindow, |
| 158 | 158 | ElectronWindow.ElectronWindow.of({ |
| 159 | main: Effect.succeed( | |
| 160 | Option.some({ | |
| 161 | getBounds: () => ({ x: 100, y: 80, width: 1_000, height: 700 }), | |
| 162 | getContentBounds: () => ({ x: 100, y: 118, width: 1_000, height: 662 }), | |
| 163 | webContents, | |
| 164 | }), | |
| 165 | ), | |
| 159 | main: Effect.succeedSome({ | |
| 160 | getBounds: () => ({ x: 100, y: 80, width: 1_000, height: 700 }), | |
| 161 | getContentBounds: () => ({ x: 100, y: 118, width: 1_000, height: 662 }), | |
| 162 | webContents, | |
| 163 | }), | |
| 166 | 164 | } as ElectronWindow.ElectronWindow["Service"]), |
| 167 | 165 | ), |
| 168 | 166 | Layer.succeed( |
| ... | ... | @@ -220,7 +218,7 @@ describe("window capture IPC", () => { |
| 220 | 218 | Layer.succeed( |
| 221 | 219 | ElectronWindow.ElectronWindow, |
| 222 | 220 | ElectronWindow.ElectronWindow.of({ |
| 223 | main: Effect.succeed(Option.some({ webContents })), | |
| 221 | main: Effect.succeedSome({ webContents }), | |
| 224 | 222 | } as ElectronWindow.ElectronWindow["Service"]), |
| 225 | 223 | ), |
| 226 | 224 | Layer.succeed( |
| ... | ... | @@ -256,7 +254,7 @@ describe("window capture IPC", () => { |
| 256 | 254 | Effect.provideService( |
| 257 | 255 | ElectronWindow.ElectronWindow, |
| 258 | 256 | ElectronWindow.ElectronWindow.of({ |
| 259 | main: Effect.succeed(Option.some({ webContents: { id: 7 } })), | |
| 257 | main: Effect.succeedSome({ webContents: { id: 7 } }), | |
| 260 | 258 | } as ElectronWindow.ElectronWindow["Service"]), |
| 261 | 259 | ), |
| 262 | 260 | Effect.provideService(DesktopSnapShot.DesktopSnapShot, null as never), |
| ... | ... | @@ -277,7 +275,7 @@ describe("window capture IPC", () => { |
| 277 | 275 | Effect.provide( |
| 278 | 276 | Layer.mergeAll( |
| 279 | 277 | Layer.succeed(ElectronWindow.ElectronWindow, { |
| 280 | main: Effect.succeed(Option.some({ webContents: { id: 7 } })), | |
| 278 | main: Effect.succeedSome({ webContents: { id: 7 } }), | |
| 281 | 279 | } as ElectronWindow.ElectronWindow["Service"]), |
| 282 | 280 | Layer.succeed(DesktopSnapShot.DesktopSnapShot, { |
| 283 | 281 | setup: (action: string) => |
| ... | ... | @@ -295,7 +293,7 @@ describe("window capture IPC", () => { |
| 295 | 293 | Layer.succeed( |
| 296 | 294 | ElectronWindow.ElectronWindow, |
| 297 | 295 | ElectronWindow.ElectronWindow.of({ |
| 298 | main: Effect.succeed(Option.some({ webContents: { id: 7 } })), | |
| 296 | main: Effect.succeedSome({ webContents: { id: 7 } }), | |
| 299 | 297 | } as ElectronWindow.ElectronWindow["Service"]), |
| 300 | 298 | ), |
| 301 | 299 | Layer.succeed( |
| ... | ... | @@ -320,7 +318,7 @@ describe("window capture IPC", () => { |
| 320 | 318 | Layer.succeed( |
| 321 | 319 | ElectronWindow.ElectronWindow, |
| 322 | 320 | ElectronWindow.ElectronWindow.of({ |
| 323 | main: Effect.succeed(Option.some({ webContents: { id: 7 } })), | |
| 321 | main: Effect.succeedSome({ webContents: { id: 7 } }), | |
| 324 | 322 | } as ElectronWindow.ElectronWindow["Service"]), |
| 325 | 323 | ), |
| 326 | 324 | Layer.succeed( |
apps/desktop/src/ipc/methods/sshEnvironment.ts+5-7| ... | ... | @@ -137,13 +137,11 @@ export const ensureSshEnvironment = DesktopIpc.makeIpcMethod({ |
| 137 | 137 | }) { |
| 138 | 138 | const sshEnvironment = yield* DesktopSshEnvironment.DesktopSshEnvironment; |
| 139 | 139 | return yield* sshEnvironment.ensureEnvironment(target, options).pipe( |
| 140 | Effect.catch((error) => | |
| 141 | DesktopSshEnvironment.isDesktopSshPasswordPromptCancellation(error) | |
| 142 | ? Effect.succeed({ | |
| 143 | type: DesktopSshPasswordPromptCancelledType, | |
| 144 | message: error.message, | |
| 145 | }) | |
| 146 | : Effect.fail(error), | |
| 140 | Effect.catchIf(DesktopSshEnvironment.isDesktopSshPasswordPromptCancellation, (error) => | |
| 141 | Effect.succeed({ | |
| 142 | type: DesktopSshPasswordPromptCancelledType, | |
| 143 | message: error.message, | |
| 144 | }), | |
| 147 | 145 | ), |
| 148 | 146 | ); |
| 149 | 147 | }), |
apps/desktop/src/ipc/methods/window.test.ts+12-14| ... | ... | @@ -61,7 +61,7 @@ const defaultWslInstance: DesktopBackendManager.DesktopBackendInstance = { |
| 61 | 61 | label: Effect.succeed("WSL (default distro)"), |
| 62 | 62 | start: Effect.void, |
| 63 | 63 | stop: () => Effect.void, |
| 64 | currentConfig: Effect.succeed(Option.some(readyWslConfig)), | |
| 64 | currentConfig: Effect.succeedSome(readyWslConfig), | |
| 65 | 65 | snapshot: Effect.succeed({ |
| 66 | 66 | desiredRunning: true, |
| 67 | 67 | ready: true, |
| ... | ... | @@ -101,7 +101,7 @@ describe("getLocalEnvironmentBootstraps", () => { |
| 101 | 101 | }; |
| 102 | 102 | const retryingInstance: DesktopBackendManager.DesktopBackendInstance = { |
| 103 | 103 | ...defaultWslInstance, |
| 104 | currentConfig: Effect.succeed(Option.some(retryingConfig)), | |
| 104 | currentConfig: Effect.succeedSome(retryingConfig), | |
| 105 | 105 | snapshot: Effect.succeed({ |
| 106 | 106 | desiredRunning: true, |
| 107 | 107 | ready: false, |
| ... | ... | @@ -128,16 +128,14 @@ describe("getLocalEnvironmentBootstraps", () => { |
| 128 | 128 | it.effect("omits a bounded transient bootstrap after retries stop", () => { |
| 129 | 129 | const stoppedInstance: DesktopBackendManager.DesktopBackendInstance = { |
| 130 | 130 | ...defaultWslInstance, |
| 131 | currentConfig: Effect.succeed( | |
| 132 | Option.some({ | |
| 133 | ...readyWslConfig, | |
| 134 | preflightFailure: Option.some({ | |
| 135 | reason: "WSL probe timed out", | |
| 136 | fatal: false, | |
| 137 | retryLimit: 12, | |
| 138 | }), | |
| 131 | currentConfig: Effect.succeedSome({ | |
| 132 | ...readyWslConfig, | |
| 133 | preflightFailure: Option.some({ | |
| 134 | reason: "WSL probe timed out", | |
| 135 | fatal: false, | |
| 136 | retryLimit: 12, | |
| 139 | 137 | }), |
| 140 | ), | |
| 138 | }), | |
| 141 | 139 | snapshot: Effect.succeed({ |
| 142 | 140 | desiredRunning: false, |
| 143 | 141 | ready: false, |
| ... | ... | @@ -163,7 +161,7 @@ describe("getWindowFullscreenState", () => { |
| 163 | 161 | }).pipe( |
| 164 | 162 | Effect.provide( |
| 165 | 163 | Layer.mock(ElectronWindow.ElectronWindow)({ |
| 166 | currentMainOrFirst: Effect.succeed(Option.some(window)), | |
| 164 | currentMainOrFirst: Effect.succeedSome(window), | |
| 167 | 165 | }), |
| 168 | 166 | ), |
| 169 | 167 | ); |
| ... | ... | @@ -206,7 +204,7 @@ describe("pasteAsText", () => { |
| 206 | 204 | }).pipe( |
| 207 | 205 | Effect.provide( |
| 208 | 206 | Layer.mock(ElectronWindow.ElectronWindow)({ |
| 209 | main: Effect.succeed(Option.some(window)), | |
| 207 | main: Effect.succeedSome(window), | |
| 210 | 208 | }), |
| 211 | 209 | ), |
| 212 | 210 | ); |
| ... | ... | @@ -219,7 +217,7 @@ describe("pickProjectFavicon", () => { |
| 219 | 217 | Layer.mergeAll( |
| 220 | 218 | Layer.mock(ElectronDialog.ElectronDialog)({ pickFiles }), |
| 221 | 219 | Layer.mock(ElectronWindow.ElectronWindow)({ |
| 222 | focusedMainOrFirst: Effect.succeed(Option.none()), | |
| 220 | focusedMainOrFirst: Effect.succeedNone, | |
| 223 | 221 | }), |
| 224 | 222 | DesktopAppSettings.layerTest(settings), |
| 225 | 223 | ); |
apps/desktop/src/ipc/methods/wsl.test.ts+1-2| ... | ... | @@ -2,7 +2,6 @@ import { DesktopWslStateSchema } from "@t3tools/contracts"; |
| 2 | 2 | import { assert, describe, it } from "@effect/vitest"; |
| 3 | 3 | import * as Effect from "effect/Effect"; |
| 4 | 4 | import * as Layer from "effect/Layer"; |
| 5 | import * as Option from "effect/Option"; | |
| 6 | 5 | import * as Schema from "effect/Schema"; |
| 7 | 6 | |
| 8 | 7 | import * as DesktopEnvironment from "../../app/DesktopEnvironment.ts"; |
| ... | ... | @@ -34,7 +33,7 @@ function makeWslBackendLayer(input: { readonly onReconcile?: Effect.Effect<void> |
| 34 | 33 | DesktopWslBackend.DesktopWslBackend, |
| 35 | 34 | DesktopWslBackend.DesktopWslBackend.of({ |
| 36 | 35 | reconcile: input.onReconcile ?? Effect.void, |
| 37 | lastPreflightError: Effect.succeed(Option.none()), | |
| 36 | lastPreflightError: Effect.succeedNone, | |
| 38 | 37 | }), |
| 39 | 38 | ); |
| 40 | 39 | } |
apps/desktop/src/preview/BrowserImport/ChromiumKeys.ts+3-2| ... | ... | @@ -315,8 +315,9 @@ export const resolveChromiumKeys = Effect.fn("ChromiumKeys.resolveChromiumKeys") |
| 315 | 315 | // v10 remains importable when Secret Service is absent or does not |
| 316 | 316 | // contain a key. An explicit denial/lock/cancel remains a consent |
| 317 | 317 | // failure rather than being silently downgraded. |
| 318 | Effect.catch((error) => | |
| 319 | error.reason === "needsKeychainApproval" ? Effect.fail(error) : Effect.succeed(error), | |
| 318 | Effect.catchIf( | |
| 319 | (error) => error.reason !== "needsKeychainApproval", | |
| 320 | (error) => Effect.succeed(error), | |
| 320 | 321 | ), |
| 321 | 322 | ) |
| 322 | 323 | : undefined; |
apps/desktop/src/preview/BrowserImport/FirefoxCookies.ts+41-42| ... | ... | @@ -114,56 +114,55 @@ const expiryToSeconds = (expiry: number, schemaVersion: number): number | undefi |
| 114 | 114 | return schemaVersion >= FIREFOX_EXPIRY_MILLISECONDS_SCHEMA ? Math.floor(expiry / 1000) : expiry; |
| 115 | 115 | }; |
| 116 | 116 | |
| 117 | export const readFirefoxCookies = Effect.fn("FirefoxCookies.readFirefoxCookies")(function* ( | |
| 118 | cookieDatabasePath: string, | |
| 119 | ) { | |
| 120 | const snapshotPath = yield* snapshotCookieDatabase(cookieDatabasePath).pipe( | |
| 121 | Effect.mapError((cause) => new FirefoxCookieReadError({ cookieDatabasePath, cause })), | |
| 122 | ); | |
| 117 | export const readFirefoxCookies = Effect.fn("FirefoxCookies.readFirefoxCookies")( | |
| 118 | function* (cookieDatabasePath: string) { | |
| 119 | const snapshotPath = yield* snapshotCookieDatabase(cookieDatabasePath); | |
| 123 | 120 | |
| 124 | const { rows, schemaVersion } = yield* Effect.gen(function* () { | |
| 125 | const sql = yield* SqlClient.SqlClient; | |
| 126 | const [versionRow] = yield* decodeUserVersion(yield* sql`pragma user_version`); | |
| 127 | const schemaVersion = versionRow?.user_version ?? 0; | |
| 128 | const hasRawSameSite = | |
| 129 | schemaVersion >= FIREFOX_RAW_SAMESITE_FIRST_SCHEMA && | |
| 130 | schemaVersion <= FIREFOX_RAW_SAMESITE_LAST_SCHEMA; | |
| 131 | // Only the default container. Firefox isolates cookies per container and | |
| 132 | // per private window via `originAttributes` (`^userContextId=2`, | |
| 133 | // `^privateBrowsingId=1`); Electron has no equivalent, so importing them | |
| 134 | // all would collapse several identities onto one host/name/path and hand | |
| 135 | // the profile an arbitrary container's session. | |
| 136 | const raw = hasRawSameSite | |
| 137 | ? yield* sql` | |
| 121 | const { rows, schemaVersion } = yield* Effect.gen(function* () { | |
| 122 | const sql = yield* SqlClient.SqlClient; | |
| 123 | const [versionRow] = yield* decodeUserVersion(yield* sql`pragma user_version`); | |
| 124 | const schemaVersion = versionRow?.user_version ?? 0; | |
| 125 | const hasRawSameSite = | |
| 126 | schemaVersion >= FIREFOX_RAW_SAMESITE_FIRST_SCHEMA && | |
| 127 | schemaVersion <= FIREFOX_RAW_SAMESITE_LAST_SCHEMA; | |
| 128 | // Only the default container. Firefox isolates cookies per container and | |
| 129 | // per private window via `originAttributes` (`^userContextId=2`, | |
| 130 | // `^privateBrowsingId=1`); Electron has no equivalent, so importing them | |
| 131 | // all would collapse several identities onto one host/name/path and hand | |
| 132 | // the profile an arbitrary container's session. | |
| 133 | const raw = hasRawSameSite | |
| 134 | ? yield* sql` | |
| 138 | 135 | select host, name, value, path, expiry, isSecure, isHttpOnly, sameSite, rawSameSite |
| 139 | 136 | from moz_cookies |
| 140 | 137 | where originAttributes = '' |
| 141 | 138 | ` |
| 142 | : yield* sql` | |
| 139 | : yield* sql` | |
| 143 | 140 | select host, name, value, path, expiry, isSecure, isHttpOnly, sameSite, |
| 144 | 141 | null as rawSameSite |
| 145 | 142 | from moz_cookies |
| 146 | 143 | where originAttributes = '' |
| 147 | 144 | `; |
| 148 | return { rows: yield* decodeCookieRows(raw), schemaVersion }; | |
| 149 | }).pipe( | |
| 150 | Effect.provide(NodeSqliteClient.layer({ filename: snapshotPath, readonly: true })), | |
| 151 | Effect.mapError((cause) => new FirefoxCookieReadError({ cookieDatabasePath, cause })), | |
| 152 | ); | |
| 145 | return { rows: yield* decodeCookieRows(raw), schemaVersion }; | |
| 146 | }).pipe(Effect.provide(NodeSqliteClient.layer({ filename: snapshotPath, readonly: true }))); | |
| 153 | 147 | |
| 154 | return rows.map((row) => { | |
| 155 | const secure = row.isSecure === 1; | |
| 156 | const scope = cookieScope(row.host, row.path, secure); | |
| 157 | return { | |
| 158 | url: scope.url, | |
| 159 | name: row.name, | |
| 160 | value: row.value, | |
| 161 | domain: scope.domain, | |
| 162 | path: row.path, | |
| 163 | secure, | |
| 164 | httpOnly: row.isHttpOnly === 1, | |
| 165 | expirationDate: expiryToSeconds(row.expiry, schemaVersion), | |
| 166 | sameSite: sameSiteFromColumn(row.sameSite, row.rawSameSite), | |
| 167 | } satisfies ImportedCookie; | |
| 168 | }); | |
| 169 | }); | |
| 148 | return rows.map((row) => { | |
| 149 | const secure = row.isSecure === 1; | |
| 150 | const scope = cookieScope(row.host, row.path, secure); | |
| 151 | return { | |
| 152 | url: scope.url, | |
| 153 | name: row.name, | |
| 154 | value: row.value, | |
| 155 | domain: scope.domain, | |
| 156 | path: row.path, | |
| 157 | secure, | |
| 158 | httpOnly: row.isHttpOnly === 1, | |
| 159 | expirationDate: expiryToSeconds(row.expiry, schemaVersion), | |
| 160 | sameSite: sameSiteFromColumn(row.sameSite, row.rawSameSite), | |
| 161 | } satisfies ImportedCookie; | |
| 162 | }); | |
| 163 | }, | |
| 164 | (effect, cookieDatabasePath) => | |
| 165 | effect.pipe( | |
| 166 | Effect.mapError((cause) => new FirefoxCookieReadError({ cookieDatabasePath, cause })), | |
| 167 | ), | |
| 168 | ); |
apps/desktop/src/preview/BrowserSession.ts+6-6| ... | ... | @@ -231,8 +231,9 @@ export const make = Effect.gen(function* BrowserSessionMake() { |
| 231 | 231 | getSession, |
| 232 | 232 | clearCookies: Effect.fn("BrowserSession.clearCookies")(function* (partitions?) { |
| 233 | 233 | const sessions = yield* SynchronizedRef.get(sessionsRef); |
| 234 | yield* Effect.all( | |
| 235 | selectSessions(sessions, partitions).map(([partition, browserSession]) => | |
| 234 | yield* Effect.forEach( | |
| 235 | selectSessions(sessions, partitions), | |
| 236 | ([partition, browserSession]) => | |
| 236 | 237 | Effect.tryPromise({ |
| 237 | 238 | try: () => |
| 238 | 239 | browserSession.clearStorageData({ |
| ... | ... | @@ -244,14 +245,14 @@ export const make = Effect.gen(function* BrowserSessionMake() { |
| 244 | 245 | cause, |
| 245 | 246 | }), |
| 246 | 247 | }), |
| 247 | ), | |
| 248 | 248 | { concurrency: "unbounded", discard: true }, |
| 249 | 249 | ); |
| 250 | 250 | }), |
| 251 | 251 | clearCache: Effect.fn("BrowserSession.clearCache")(function* (partitions?) { |
| 252 | 252 | const sessions = yield* SynchronizedRef.get(sessionsRef); |
| 253 | yield* Effect.all( | |
| 254 | selectSessions(sessions, partitions).map(([partition, browserSession]) => | |
| 253 | yield* Effect.forEach( | |
| 254 | selectSessions(sessions, partitions), | |
| 255 | ([partition, browserSession]) => | |
| 255 | 256 | Effect.tryPromise({ |
| 256 | 257 | try: () => browserSession.clearCache(), |
| 257 | 258 | catch: (cause) => |
| ... | ... | @@ -260,7 +261,6 @@ export const make = Effect.gen(function* BrowserSessionMake() { |
| 260 | 261 | cause, |
| 261 | 262 | }), |
| 262 | 263 | }), |
| 263 | ), | |
| 264 | 264 | { concurrency: "unbounded", discard: true }, |
| 265 | 265 | ); |
| 266 | 266 | }), |
apps/desktop/src/preview/Manager.ts+25-28| ... | ... | @@ -942,14 +942,14 @@ const makeNativeOperations = Effect.fn("PreviewManager.makeOperations")(function |
| 942 | 942 | delivery: () => Effect.Effect<void>, |
| 943 | 943 | ) => |
| 944 | 944 | Effect.suspend(delivery).pipe( |
| 945 | Effect.catchCause((cause) => | |
| 946 | Cause.hasInterrupts(cause) | |
| 947 | ? Effect.failCause(cause) | |
| 948 | : Effect.logWarning("Desktop preview event listener failed.", { | |
| 949 | eventKind, | |
| 950 | tabId, | |
| 951 | cause, | |
| 952 | }), | |
| 945 | Effect.catchCauseIf( | |
| 946 | (cause) => !Cause.hasInterrupts(cause), | |
| 947 | (cause) => | |
| 948 | Effect.logWarning("Desktop preview event listener failed.", { | |
| 949 | eventKind, | |
| 950 | tabId, | |
| 951 | cause, | |
| 952 | }), | |
| 953 | 953 | ), |
| 954 | 954 | ); |
| 955 | 955 | |
| ... | ... | @@ -1115,15 +1115,13 @@ const makeNativeOperations = Effect.fn("PreviewManager.makeOperations")(function |
| 1115 | 1115 | } |
| 1116 | 1116 | return resolvedPath; |
| 1117 | 1117 | }).pipe( |
| 1118 | Effect.flatMap((resolvedPath) => | |
| 1119 | resolvedPath === null | |
| 1120 | ? Effect.fail( | |
| 1121 | new PreviewArtifactPathOutsideDirectoryError({ | |
| 1122 | artifactPath, | |
| 1123 | artifactDirectory: resolvedArtifactDirectory, | |
| 1124 | }), | |
| 1125 | ) | |
| 1126 | : Effect.succeed(resolvedPath), | |
| 1118 | Effect.filterOrFail( | |
| 1119 | (resolvedPath) => resolvedPath !== null, | |
| 1120 | () => | |
| 1121 | new PreviewArtifactPathOutsideDirectoryError({ | |
| 1122 | artifactPath, | |
| 1123 | artifactDirectory: resolvedArtifactDirectory, | |
| 1124 | }), | |
| 1127 | 1125 | ), |
| 1128 | 1126 | ); |
| 1129 | 1127 | |
| ... | ... | @@ -1404,14 +1402,13 @@ const makeNativeOperations = Effect.fn("PreviewManager.makeOperations")(function |
| 1404 | 1402 | wcDebugger.on("message", onMessage); |
| 1405 | 1403 | wcDebugger.attach("1.3"); |
| 1406 | 1404 | }); |
| 1407 | yield* Effect.all( | |
| 1408 | ["Runtime.enable", "Accessibility.enable", "Network.enable", "Log.enable"].map( | |
| 1409 | (method) => | |
| 1410 | attemptPromise( | |
| 1411 | { operation: `initializeDebugger.${method}`, webContentsId: wc.id }, | |
| 1412 | () => wcDebugger.sendCommand(method), | |
| 1413 | ), | |
| 1414 | ), | |
| 1405 | yield* Effect.forEach( | |
| 1406 | ["Runtime.enable", "Accessibility.enable", "Network.enable", "Log.enable"], | |
| 1407 | (method) => | |
| 1408 | attemptPromise( | |
| 1409 | { operation: `initializeDebugger.${method}`, webContentsId: wc.id }, | |
| 1410 | () => wcDebugger.sendCommand(method), | |
| 1411 | ), | |
| 1415 | 1412 | { concurrency: "unbounded", discard: true }, |
| 1416 | 1413 | ); |
| 1417 | 1414 | return [ |
| ... | ... | @@ -4056,13 +4053,13 @@ const makeNativeOperations = Effect.fn("PreviewManager.makeOperations")(function |
| 4056 | 4053 | receiptKey: JSON.stringify(`__t3NativeKey_${NodeCrypto.randomUUID()}`), |
| 4057 | 4054 | })), |
| 4058 | 4055 | ({ frames, receiptKey }) => |
| 4059 | Effect.all( | |
| 4060 | frames.map((frame) => | |
| 4056 | Effect.forEach( | |
| 4057 | frames, | |
| 4058 | (frame) => | |
| 4061 | 4059 | evaluate(frame, `globalThis[${receiptKey}]?.dispose()`).pipe( |
| 4062 | 4060 | Effect.timeoutOption(1_000), |
| 4063 | 4061 | Effect.ignore, |
| 4064 | 4062 | ), |
| 4065 | ), | |
| 4066 | 4063 | { concurrency: "unbounded", discard: true }, |
| 4067 | 4064 | ), |
| 4068 | 4065 | ); |
apps/desktop/src/settings/DesktopClientSettings.ts+2-2| ... | ... | @@ -75,7 +75,7 @@ const readClientSettings = ( |
| 75 | 75 | settingsPath: string, |
| 76 | 76 | ): Effect.Effect<Option.Option<ClientSettings>, DesktopClientSettingsReadError> => |
| 77 | 77 | fileSystem.readFileString(settingsPath).pipe( |
| 78 | Effect.map(Option.some), | |
| 78 | Effect.asSome, | |
| 79 | 79 | Effect.catchTags({ |
| 80 | 80 | PlatformError: (cause) => |
| 81 | 81 | cause.reason._tag === "NotFound" |
| ... | ... | @@ -98,7 +98,7 @@ const readClientSettings = ( |
| 98 | 98 | onNone: () => Effect.succeed(Option.none<ClientSettings>()), |
| 99 | 99 | onSome: (raw) => |
| 100 | 100 | decodeClientSettingsJson(raw).pipe( |
| 101 | Effect.map((settings) => Option.some(settings)), | |
| 101 | Effect.asSome, | |
| 102 | 102 | Effect.catchTags({ |
| 103 | 103 | SchemaError: (cause) => |
| 104 | 104 | Effect.logWarning("Could not decode desktop client settings.", cause).pipe( |
apps/desktop/src/settings/DesktopSavedEnvironments.test.ts+1-1| ... | ... | @@ -94,7 +94,7 @@ function makeSafeStorageLayer(input: { |
| 94 | 94 | } |
| 95 | 95 | return Effect.succeed(decoded.slice("enc:".length)); |
| 96 | 96 | }, |
| 97 | selectedStorageBackend: Effect.succeed(Option.none()), | |
| 97 | selectedStorageBackend: Effect.succeedNone, | |
| 98 | 98 | } satisfies ElectronSafeStorage.ElectronSafeStorage["Service"]); |
| 99 | 99 | } |
| 100 | 100 |
apps/desktop/src/snapShot/DesktopSnapShot.ts+6-7| ... | ... | @@ -820,7 +820,7 @@ export const make = Effect.gen(function* () { |
| 820 | 820 | }; |
| 821 | 821 | |
| 822 | 822 | const emit = (event: DesktopSnapShotEvent) => |
| 823 | desktopWindow.dispatchSnapShotEvent(event).pipe(Effect.catchCause(() => Effect.void)); | |
| 823 | desktopWindow.dispatchSnapShotEvent(event).pipe(Effect.ignoreCause); | |
| 824 | 824 | const setFailure = (message: string, captureId?: string) => |
| 825 | 825 | Ref.update(stateRef, (state) => ({ ...state, message })).pipe( |
| 826 | 826 | Effect.andThen( |
| ... | ... | @@ -846,10 +846,9 @@ export const make = Effect.gen(function* () { |
| 846 | 846 | const discardCapture = Effect.fn("desktop.snapShot.discardCapture")(function* (id: string) { |
| 847 | 847 | closeLinuxFeedback(id); |
| 848 | 848 | transition.dismiss(id); |
| 849 | yield* Effect.all( | |
| 850 | [`${id}.png`, `${id}.tmp.png`, `${id}.json`, `${id}.json.tmp`].map((name) => | |
| 851 | fileSystem.remove(path.join(captureDirectory, name), { force: true }), | |
| 852 | ), | |
| 849 | yield* Effect.forEach( | |
| 850 | [`${id}.png`, `${id}.tmp.png`, `${id}.json`, `${id}.json.tmp`], | |
| 851 | (name) => fileSystem.remove(path.join(captureDirectory, name), { force: true }), | |
| 853 | 852 | { concurrency: "unbounded", discard: true }, |
| 854 | 853 | ).pipe(Effect.ignore); |
| 855 | 854 | }); |
| ... | ... | @@ -904,7 +903,7 @@ export const make = Effect.gen(function* () { |
| 904 | 903 | if (snapshot.animationStarted) { |
| 905 | 904 | yield* emit({ type: "started", id: id as DesktopSnapShotId }); |
| 906 | 905 | } else { |
| 907 | yield* desktopWindow.activate.pipe(Effect.catchCause(() => Effect.void)); | |
| 906 | yield* desktopWindow.activate.pipe(Effect.ignoreCause); | |
| 908 | 907 | } |
| 909 | 908 | return { id, capturedAt, ...snapshot }; |
| 910 | 909 | }).pipe(Effect.mapError((cause) => captureFailure(cause, id))); |
| ... | ... | @@ -1511,7 +1510,7 @@ export const make = Effect.gen(function* () { |
| 1511 | 1510 | null, |
| 1512 | 1511 | ), |
| 1513 | 1512 | ), |
| 1514 | Effect.catch(() => Effect.void), | |
| 1513 | Effect.ignore, | |
| 1515 | 1514 | ), |
| 1516 | 1515 | ), |
| 1517 | 1516 | configure, |
apps/desktop/src/ssh/DesktopSshPasswordPrompts.test.ts+3-4| ... | ... | @@ -4,7 +4,6 @@ import * as Duration from "effect/Duration"; |
| 4 | 4 | import * as Effect from "effect/Effect"; |
| 5 | 5 | import * as Fiber from "effect/Fiber"; |
| 6 | 6 | import * as Layer from "effect/Layer"; |
| 7 | import * as Option from "effect/Option"; | |
| 8 | 7 | import * as TestClock from "effect/testing/TestClock"; |
| 9 | 8 | import type * as Electron from "electron"; |
| 10 | 9 | |
| ... | ... | @@ -92,9 +91,9 @@ function makeElectronWindowLayer(window: ReturnType<typeof makeTestWindow>["wind |
| 92 | 91 | ElectronWindow.ElectronWindow, |
| 93 | 92 | ElectronWindow.ElectronWindow.of({ |
| 94 | 93 | create: () => Effect.die("unexpected BrowserWindow creation"), |
| 95 | main: Effect.succeed(Option.some(window as Electron.BrowserWindow)), | |
| 96 | currentMainOrFirst: Effect.succeed(Option.some(window as Electron.BrowserWindow)), | |
| 97 | focusedMainOrFirst: Effect.succeed(Option.some(window as Electron.BrowserWindow)), | |
| 94 | main: Effect.succeedSome(window as Electron.BrowserWindow), | |
| 95 | currentMainOrFirst: Effect.succeedSome(window as Electron.BrowserWindow), | |
| 96 | focusedMainOrFirst: Effect.succeedSome(window as Electron.BrowserWindow), | |
| 98 | 97 | setMain: () => Effect.void, |
| 99 | 98 | clearMain: () => Effect.void, |
| 100 | 99 | prepareReveal: () => Effect.succeed(false), |
apps/desktop/src/telemetry/DesktopTelemetryPublisher.ts+7-6| ... | ... | @@ -289,12 +289,12 @@ export const make = Effect.fn("desktop.telemetryPublisher.make")(function* () { |
| 289 | 289 | yield* Ref.set(latest, Option.some(snapshot)); |
| 290 | 290 | yield* PubSub.publish(changes, snapshot); |
| 291 | 291 | }).pipe( |
| 292 | Effect.catchCause((cause) => | |
| 293 | Cause.hasInterrupts(cause) | |
| 294 | ? Effect.failCause(cause) | |
| 295 | : Effect.logWarning("Failed to sample Electron telemetry", { | |
| 296 | cause: String(cause), | |
| 297 | }), | |
| 292 | Effect.catchCauseIf( | |
| 293 | (cause) => !Cause.hasInterrupts(cause), | |
| 294 | (cause) => | |
| 295 | Effect.logWarning("Failed to sample Electron telemetry", { | |
| 296 | cause: String(cause), | |
| 297 | }), | |
| 298 | 298 | ), |
| 299 | 299 | ); |
| 300 | 300 | |
| ... | ... | @@ -306,6 +306,7 @@ export const make = Effect.fn("desktop.telemetryPublisher.make")(function* () { |
| 306 | 306 | Ref.get(diagnosticsDemandSources).pipe(Effect.map((sources) => sources.size > 0)), |
| 307 | 307 | Ref.get(hostPowerIntervals), |
| 308 | 308 | ]); |
| 309 | // @effect-diagnostics-next-line raceFirstWithSleepToTimeout:off - races a trigger queue against the interval; both arms are real outcomes, not a timeout | |
| 309 | 310 | const allowSuspendRecovery = yield* Effect.raceFirst( |
| 310 | 311 | Queue.take(sampleTriggers).pipe(Effect.as(false)), |
| 311 | 312 | Effect.sleep(sampleInterval(currentPower, demand, intervals)).pipe(Effect.as(true)), |
apps/desktop/src/updates/DesktopUpdates.ts+16-15| ... | ... | @@ -668,24 +668,25 @@ export const make = Effect.gen(function* () { |
| 668 | 668 | }), |
| 669 | 669 | ).pipe(Effect.withSpan("desktop.updates.installDownloadedUpdate")); |
| 670 | 670 | |
| 671 | const installWithExpectedVersion = (expectedVersion?: string) => | |
| 672 | Effect.gen(function* () { | |
| 673 | if (yield* Ref.get(desktopState.quitting)) { | |
| 674 | return { | |
| 675 | accepted: false, | |
| 676 | completed: false, | |
| 677 | failed: false, | |
| 678 | state: yield* Ref.get(updateStateRef), | |
| 679 | }; | |
| 680 | } | |
| 681 | const result = yield* installDownloadedUpdate(expectedVersion); | |
| 671 | const installWithExpectedVersion = Effect.fn("desktop.updates.install")(function* ( | |
| 672 | expectedVersion?: string, | |
| 673 | ) { | |
| 674 | if (yield* Ref.get(desktopState.quitting)) { | |
| 682 | 675 | return { |
| 683 | accepted: result.accepted, | |
| 684 | completed: result.completed, | |
| 685 | failed: result.failed, | |
| 676 | accepted: false, | |
| 677 | completed: false, | |
| 678 | failed: false, | |
| 686 | 679 | state: yield* Ref.get(updateStateRef), |
| 687 | 680 | }; |
| 688 | }).pipe(Effect.withSpan("desktop.updates.install")); | |
| 681 | } | |
| 682 | const result = yield* installDownloadedUpdate(expectedVersion); | |
| 683 | return { | |
| 684 | accepted: result.accepted, | |
| 685 | completed: result.completed, | |
| 686 | failed: result.failed, | |
| 687 | state: yield* Ref.get(updateStateRef), | |
| 688 | }; | |
| 689 | }); | |
| 689 | 690 | |
| 690 | 691 | const startUpdatePollers: Effect.Effect<void, never, Scope.Scope> = Effect.gen(function* () { |
| 691 | 692 | yield* Effect.sleep(AUTO_UPDATE_STARTUP_DELAY).pipe( |
apps/desktop/src/updates/updatesTestHarness.ts+5-5| ... | ... | @@ -107,9 +107,9 @@ export function makeHarness(options: UpdatesHarnessOptions = {}) { |
| 107 | 107 | |
| 108 | 108 | const windowLayer = Layer.succeed(ElectronWindow.ElectronWindow, { |
| 109 | 109 | create: () => Effect.die("unexpected BrowserWindow creation"), |
| 110 | main: Effect.succeed(Option.none()), | |
| 111 | currentMainOrFirst: Effect.succeed(Option.none()), | |
| 112 | focusedMainOrFirst: Effect.succeed(Option.none()), | |
| 110 | main: Effect.succeedNone, | |
| 111 | currentMainOrFirst: Effect.succeedNone, | |
| 112 | focusedMainOrFirst: Effect.succeedNone, | |
| 113 | 113 | setMain: () => Effect.void, |
| 114 | 114 | clearMain: () => Effect.void, |
| 115 | 115 | prepareReveal: () => Effect.succeed(false), |
| ... | ... | @@ -131,7 +131,7 @@ export function makeHarness(options: UpdatesHarnessOptions = {}) { |
| 131 | 131 | installSteps.push("startBackend"); |
| 132 | 132 | }).pipe(Effect.andThen(options.startBackend ?? Effect.void)), |
| 133 | 133 | stop: () => options.stopBackend ?? Effect.void, |
| 134 | currentConfig: Effect.succeed(Option.none()), | |
| 134 | currentConfig: Effect.succeedNone, | |
| 135 | 135 | snapshot: Effect.succeed({ |
| 136 | 136 | desiredRunning: false, |
| 137 | 137 | ready: false, |
| ... | ... | @@ -245,7 +245,7 @@ export function makeHarness(options: UpdatesHarnessOptions = {}) { |
| 245 | 245 | installSteps, |
| 246 | 246 | updateRestartMarkers, |
| 247 | 247 | downloadCount: () => downloadCount, |
| 248 | feedUrls: () => feedUrls, | |
| 248 | feedUrls: (): ElectronUpdater.ElectronUpdaterFeedUrl[] => feedUrls, | |
| 249 | 249 | fullChangelog: () => fullChangelog, |
| 250 | 250 | listenerCount: () => |
| 251 | 251 | Array.from(listeners.values()).reduce( |
apps/desktop/src/window/DesktopApplicationMenu.test.ts+3-4| ... | ... | @@ -3,7 +3,6 @@ import { assert, describe, it } from "@effect/vitest"; |
| 3 | 3 | import * as Deferred from "effect/Deferred"; |
| 4 | 4 | import * as Effect from "effect/Effect"; |
| 5 | 5 | import * as Layer from "effect/Layer"; |
| 6 | import * as Option from "effect/Option"; | |
| 7 | 6 | |
| 8 | 7 | import type * as Electron from "electron"; |
| 9 | 8 | |
| ... | ... | @@ -51,7 +50,7 @@ const electronAppLayer = Layer.succeed(ElectronApp.ElectronApp, { |
| 51 | 50 | } satisfies ElectronApp.ElectronApp["Service"]); |
| 52 | 51 | |
| 53 | 52 | const electronDialogLayer = Layer.succeed(ElectronDialog.ElectronDialog, { |
| 54 | pickFolder: () => Effect.succeed(Option.none()), | |
| 53 | pickFolder: () => Effect.succeedNone, | |
| 55 | 54 | pickFiles: () => Effect.succeed([]), |
| 56 | 55 | showMessageBox: () => Effect.succeed({ response: 0, checkboxChecked: false }), |
| 57 | 56 | showErrorBox: () => Effect.void, |
| ... | ... | @@ -63,7 +62,7 @@ const desktopUpdatesLayer = Layer.succeed(DesktopUpdates.DesktopUpdates, { |
| 63 | 62 | isInstallActive: Effect.succeed(false), |
| 64 | 63 | subscribe: Effect.die("unexpected subscribe"), |
| 65 | 64 | emitState: Effect.void, |
| 66 | disabledReason: Effect.succeed(Option.none()), | |
| 65 | disabledReason: Effect.succeedNone, | |
| 67 | 66 | configure: Effect.void, |
| 68 | 67 | setChannel: () => Effect.die("unexpected setChannel"), |
| 69 | 68 | check: () => Effect.die("unexpected check"), |
| ... | ... | @@ -98,7 +97,7 @@ const makeElectronMenuLayer = ( |
| 98 | 97 | setApplicationMenu: (template) => |
| 99 | 98 | Deferred.succeed(applicationMenuTemplate, template).pipe(Effect.asVoid), |
| 100 | 99 | popupTemplate: () => Effect.void, |
| 101 | showContextMenu: () => Effect.succeed(Option.none()), | |
| 100 | showContextMenu: () => Effect.succeedNone, | |
| 102 | 101 | } satisfies ElectronMenu.ElectronMenu["Service"]); |
| 103 | 102 | |
| 104 | 103 | const configureMenu = ( |
apps/desktop/src/window/DesktopWindow.test.ts+3-3| ... | ... | @@ -151,7 +151,7 @@ function makeFakeBrowserWindow() { |
| 151 | 151 | } |
| 152 | 152 | |
| 153 | 153 | const desktopClientSettingsLayer = Layer.mock(DesktopClientSettings.DesktopClientSettings)({ |
| 154 | get: Effect.succeed(Option.none()), | |
| 154 | get: Effect.succeedNone, | |
| 155 | 155 | }); |
| 156 | 156 | |
| 157 | 157 | const electronAppLayer = Layer.mock(ElectronApp.ElectronApp)({ |
| ... | ... | @@ -185,7 +185,7 @@ const desktopServerExposureLayer = Layer.succeed(DesktopServerExposure.DesktopSe |
| 185 | 185 | const electronMenuLayer = Layer.succeed(ElectronMenu.ElectronMenu, { |
| 186 | 186 | setApplicationMenu: () => Effect.void, |
| 187 | 187 | popupTemplate: () => Effect.void, |
| 188 | showContextMenu: () => Effect.succeed(Option.none()), | |
| 188 | showContextMenu: () => Effect.succeedNone, | |
| 189 | 189 | } satisfies ElectronMenu.ElectronMenu["Service"]); |
| 190 | 190 | |
| 191 | 191 | const electronThemeLayer = Layer.succeed(ElectronTheme.ElectronTheme, { |
| ... | ... | @@ -294,7 +294,7 @@ function makeTestLayer(input: { |
| 294 | 294 | electronAppLayer, |
| 295 | 295 | Layer.succeed(ElectronMenu.ElectronMenu, { |
| 296 | 296 | setApplicationMenu: () => Effect.void, |
| 297 | showContextMenu: () => Effect.succeed(Option.none()), | |
| 297 | showContextMenu: () => Effect.succeedNone, | |
| 298 | 298 | popupTemplate: input.onPopupTemplate ?? (() => Effect.void), |
| 299 | 299 | }), |
| 300 | 300 | Layer.succeed(ElectronShell.ElectronShell, { |
apps/desktop/src/wsl/DesktopWslBackend.ts+2-2| ... | ... | @@ -138,7 +138,7 @@ export const layer = Layer.effect( |
| 138 | 138 | const primaryConfig = yield* serverExposure.backendConfig; |
| 139 | 139 | const port = yield* scanForWslPort(primaryConfig.port + 1).pipe( |
| 140 | 140 | Effect.provideService(NetService.NetService, net), |
| 141 | Effect.map((value) => Option.some(value)), | |
| 141 | Effect.asSome, | |
| 142 | 142 | Effect.catch((error) => |
| 143 | 143 | logWslBackendWarning("could not allocate port for WSL backend", { |
| 144 | 144 | error: error.message, |
| ... | ... | @@ -171,7 +171,7 @@ export const layer = Layer.effect( |
| 171 | 171 | onReady: () => Ref.set(preflightErrorRef, Option.none()), |
| 172 | 172 | }) |
| 173 | 173 | .pipe( |
| 174 | Effect.map((registered) => Option.some(registered)), | |
| 174 | Effect.asSome, | |
| 175 | 175 | Effect.catch((error) => |
| 176 | 176 | logWslBackendWarning("WSL backend already registered, skipping start", { |
| 177 | 177 | id: targetId, |
apps/desktop/src/wsl/DesktopWslEnvironment.ts+9-14| ... | ... | @@ -1040,11 +1040,7 @@ const preWarmImpl = ( |
| 1040 | 1040 | const handle = yield* spawner.spawn(command); |
| 1041 | 1041 | yield* handle.exitCode; |
| 1042 | 1042 | }), |
| 1043 | ).pipe( | |
| 1044 | Effect.timeoutOption(PRE_WARM_TIMEOUT), | |
| 1045 | Effect.asVoid, | |
| 1046 | Effect.catch(() => Effect.void), | |
| 1047 | ); | |
| 1043 | ).pipe(Effect.timeoutOption(PRE_WARM_TIMEOUT), Effect.ignore); | |
| 1048 | 1044 | |
| 1049 | 1045 | const windowsToWslPathImpl = ( |
| 1050 | 1046 | distro: string | null, |
| ... | ... | @@ -1272,15 +1268,14 @@ export const layer = Layer.effect( |
| 1272 | 1268 | // distro. Negative results aren't cached so a transient wsl.exe failure |
| 1273 | 1269 | // doesn't permanently disable tilde expansion. |
| 1274 | 1270 | const userHomeCache = new Map<string, string>(); |
| 1275 | const getUserHome = (distro: string | null) => | |
| 1276 | Effect.gen(function* () { | |
| 1277 | const key = distro ?? "__default__"; | |
| 1278 | const cached = userHomeCache.get(key); | |
| 1279 | if (cached !== undefined) return Option.some(cached); | |
| 1280 | const resolved = yield* provideSpawner(getUserHomeImpl(distro)); | |
| 1281 | if (Option.isSome(resolved)) userHomeCache.set(key, resolved.value); | |
| 1282 | return resolved; | |
| 1283 | }).pipe(Effect.withSpan("desktop.wsl.getUserHome")); | |
| 1271 | const getUserHome = Effect.fn("desktop.wsl.getUserHome")(function* (distro: string | null) { | |
| 1272 | const key = distro ?? "__default__"; | |
| 1273 | const cached = userHomeCache.get(key); | |
| 1274 | if (cached !== undefined) return Option.some(cached); | |
| 1275 | const resolved = yield* provideSpawner(getUserHomeImpl(distro)); | |
| 1276 | if (Option.isSome(resolved)) userHomeCache.set(key, resolved.value); | |
| 1277 | return resolved; | |
| 1278 | }); | |
| 1284 | 1279 | |
| 1285 | 1280 | const getDistroIp = (distro: string | null) => |
| 1286 | 1281 | provideSpawner(getDistroIpImpl(distro)).pipe(Effect.withSpan("desktop.wsl.getDistroIp")); |
apps/server/integration/orphanedProviderSessionStartup.integration.test.ts+1-1| ... | ... | @@ -100,7 +100,7 @@ const startupDependencies = Layer.mergeAll( |
| 100 | 100 | Layer.succeed(ServiceLauncherClient.ServiceLauncherClient, { |
| 101 | 101 | managed: false, |
| 102 | 102 | requestUpdate: () => Effect.die("unused"), |
| 103 | prepareTrial: Effect.sync(() => undefined), | |
| 103 | prepareTrial: Effect.undefined, | |
| 104 | 104 | }), |
| 105 | 105 | Layer.succeed( |
| 106 | 106 | HttpServer.HttpServer, |
apps/server/src/assets/AssetAccess.ts+3-3| ... | ... | @@ -190,7 +190,7 @@ const optionOnNotFound = <A, R>( |
| 190 | 190 | effect: Effect.Effect<A, PlatformError.PlatformError, R>, |
| 191 | 191 | ): Effect.Effect<Option.Option<A>, PlatformError.PlatformError, R> => |
| 192 | 192 | effect.pipe( |
| 193 | Effect.map(Option.some), | |
| 193 | Effect.asSome, | |
| 194 | 194 | Effect.catchTags({ |
| 195 | 195 | PlatformError: (error) => |
| 196 | 196 | error.reason._tag === "NotFound" ? Effect.succeed(Option.none<A>()) : Effect.fail(error), |
| ... | ... | @@ -213,9 +213,9 @@ const resolveCanonicalWorkspaceFile = Effect.fn("AssetAccess.resolveCanonicalWor |
| 213 | 213 | const fileSystem = yield* FileSystem.FileSystem; |
| 214 | 214 | const workspacePaths = yield* WorkspacePaths.WorkspacePaths; |
| 215 | 215 | const resolved = yield* workspacePaths.resolveRelativePathWithinRoot(input).pipe( |
| 216 | Effect.map(Option.some), | |
| 216 | Effect.asSome, | |
| 217 | 217 | Effect.catchTags({ |
| 218 | WorkspacePathOutsideRootError: () => Effect.succeed(Option.none()), | |
| 218 | WorkspacePathOutsideRootError: () => Effect.succeedNone, | |
| 219 | 219 | }), |
| 220 | 220 | ); |
| 221 | 221 | if (Option.isNone(resolved)) return null; |
apps/server/src/assets/NativeAppIconResolver.ts+2-2| ... | ... | @@ -43,10 +43,10 @@ function appFromCacheKey(key: string): ToolActivityNativeAppReference { |
| 43 | 43 | const existingFile = Effect.fn("NativeAppIconResolver.existingFile")(function* (filePath: string) { |
| 44 | 44 | const fileSystem = yield* FileSystem.FileSystem; |
| 45 | 45 | const info = yield* fileSystem.stat(filePath).pipe( |
| 46 | Effect.map(Option.some), | |
| 46 | Effect.asSome, | |
| 47 | 47 | Effect.catchTags({ |
| 48 | 48 | PlatformError: (error) => |
| 49 | error.reason._tag === "NotFound" ? Effect.succeed(Option.none()) : Effect.fail(error), | |
| 49 | error.reason._tag === "NotFound" ? Effect.succeedNone : Effect.fail(error), | |
| 50 | 50 | }), |
| 51 | 51 | ); |
| 52 | 52 | return Option.isSome(info) && info.value.type === "File" ? filePath : null; |
apps/server/src/auth/PairingGrantStore.test.ts+2-3| ... | ... | @@ -3,7 +3,6 @@ import { expect, it } from "@effect/vitest"; |
| 3 | 3 | import * as Duration from "effect/Duration"; |
| 4 | 4 | import * as Effect from "effect/Effect"; |
| 5 | 5 | import * as Layer from "effect/Layer"; |
| 6 | import * as Option from "effect/Option"; | |
| 7 | 6 | import * as Stream from "effect/Stream"; |
| 8 | 7 | import * as Queue from "effect/Queue"; |
| 9 | 8 | import * as TestClock from "effect/testing/TestClock"; |
| ... | ... | @@ -47,10 +46,10 @@ const makePairingGrantStoreTestLayer = ( |
| 47 | 46 | AuthPairingLinks.AuthPairingLinkRepository, |
| 48 | 47 | AuthPairingLinks.AuthPairingLinkRepository.of({ |
| 49 | 48 | create: () => Effect.void, |
| 50 | consumeAvailable: () => Effect.succeed(Option.none()), | |
| 49 | consumeAvailable: () => Effect.succeedNone, | |
| 51 | 50 | listActive: () => Effect.succeed([]), |
| 52 | 51 | revoke: () => Effect.succeed(false), |
| 53 | getByCredential: () => Effect.succeed(Option.none()), | |
| 52 | getByCredential: () => Effect.succeedNone, | |
| 54 | 53 | ...overrides, |
| 55 | 54 | }), |
| 56 | 55 | ), |
apps/server/src/auth/ServerSecretStore.ts+1-1| ... | ... | @@ -174,7 +174,7 @@ export const make = Effect.gen(function* () { |
| 174 | 174 | Effect.map((bytes) => Option.some(Uint8Array.from(bytes))), |
| 175 | 175 | Effect.catch((cause) => |
| 176 | 176 | cause.reason._tag === "NotFound" |
| 177 | ? Effect.succeed(Option.none()) | |
| 177 | ? Effect.succeedNone | |
| 178 | 178 | : Effect.fail( |
| 179 | 179 | new SecretStoreReadError({ |
| 180 | 180 | resource: `secret ${name}`, |
apps/server/src/auth/dpop.ts+1-1| ... | ... | @@ -115,7 +115,7 @@ export const verifyRequestDpopProof = (input: { |
| 115 | 115 | "environment.dpop.failure_code": mapped.dpopFailureReason, |
| 116 | 116 | }); |
| 117 | 117 | } |
| 118 | return yield* Effect.fail(mapped); | |
| 118 | return yield* mapped; | |
| 119 | 119 | }), |
| 120 | 120 | ), |
| 121 | 121 | ); |
apps/server/src/auth/http.test.ts+1-2| ... | ... | @@ -5,7 +5,6 @@ import * as Context from "effect/Context"; |
| 5 | 5 | import * as Crypto from "effect/Crypto"; |
| 6 | 6 | import * as Effect from "effect/Effect"; |
| 7 | 7 | import * as Layer from "effect/Layer"; |
| 8 | import * as Option from "effect/Option"; | |
| 9 | 8 | import * as Redacted from "effect/Redacted"; |
| 10 | 9 | import * as Schema from "effect/Schema"; |
| 11 | 10 | import * as Etag from "effect/unstable/http/Etag"; |
| ... | ... | @@ -69,7 +68,7 @@ it.effect("sets the selected browser session cookies through the HTTP route", () |
| 69 | 68 | Effect.gen(function* () { |
| 70 | 69 | const crypto = yield* Crypto.Crypto; |
| 71 | 70 | const unusedSecretStore = ServerSecretStore.ServerSecretStore.of({ |
| 72 | get: () => Effect.succeed(Option.none()), | |
| 71 | get: () => Effect.succeedNone, | |
| 73 | 72 | set: () => Effect.void, |
| 74 | 73 | create: () => Effect.void, |
| 75 | 74 | getOrCreateRandom: () => Effect.die("Not used by these routes."), |
apps/server/src/checkpointing/CheckpointDiffQuery.test.ts+35-35| ... | ... | @@ -89,10 +89,10 @@ describe("CheckpointDiffQuery.layer", () => { |
| 89 | 89 | getSnapshotSequence: () => Effect.succeed({ snapshotSequence: 0 }), |
| 90 | 90 | getCounts: () => Effect.succeed({ projectCount: 0, threadCount: 0 }), |
| 91 | 91 | getEventReplayStats: () => Effect.die("unused"), |
| 92 | getActiveProjectByWorkspaceRoot: () => Effect.succeed(Option.none()), | |
| 92 | getActiveProjectByWorkspaceRoot: () => Effect.succeedNone, | |
| 93 | 93 | getProjectShells: () => Effect.die("unused"), |
| 94 | getProjectShellById: () => Effect.succeed(Option.none()), | |
| 95 | getFirstActiveThreadIdByProjectId: () => Effect.succeed(Option.none()), | |
| 94 | getProjectShellById: () => Effect.succeedNone, | |
| 95 | getFirstActiveThreadIdByProjectId: () => Effect.succeedNone, | |
| 96 | 96 | getImportedAgentSessionSources: () => Effect.die("unused"), |
| 97 | 97 | getThreadCheckpointContext: () => |
| 98 | 98 | Effect.sync(() => { |
| ... | ... | @@ -113,9 +113,9 @@ describe("CheckpointDiffQuery.layer", () => { |
| 113 | 113 | }), |
| 114 | 114 | getThreadRuntimeContext: () => Effect.die("unused"), |
| 115 | 115 | getTurnStartMessage: () => Effect.die("unused"), |
| 116 | getThreadShellById: () => Effect.succeed(Option.none()), | |
| 117 | getThreadDetailById: () => Effect.succeed(Option.none()), | |
| 118 | getThreadDetailSnapshot: () => Effect.succeed(Option.none()), | |
| 116 | getThreadShellById: () => Effect.succeedNone, | |
| 117 | getThreadDetailById: () => Effect.succeedNone, | |
| 118 | getThreadDetailSnapshot: () => Effect.succeedNone, | |
| 119 | 119 | searchThreads: () => Effect.succeed({ matches: [] }), |
| 120 | 120 | }), |
| 121 | 121 | ), |
| ... | ... | @@ -206,18 +206,18 @@ describe("CheckpointDiffQuery.layer", () => { |
| 206 | 206 | getSnapshotSequence: () => Effect.succeed({ snapshotSequence: 0 }), |
| 207 | 207 | getCounts: () => Effect.succeed({ projectCount: 0, threadCount: 0 }), |
| 208 | 208 | getEventReplayStats: () => Effect.die("unused"), |
| 209 | getActiveProjectByWorkspaceRoot: () => Effect.succeed(Option.none()), | |
| 209 | getActiveProjectByWorkspaceRoot: () => Effect.succeedNone, | |
| 210 | 210 | getProjectShells: () => Effect.die("unused"), |
| 211 | getProjectShellById: () => Effect.succeed(Option.none()), | |
| 212 | getFirstActiveThreadIdByProjectId: () => Effect.succeed(Option.none()), | |
| 211 | getProjectShellById: () => Effect.succeedNone, | |
| 212 | getFirstActiveThreadIdByProjectId: () => Effect.succeedNone, | |
| 213 | 213 | getImportedAgentSessionSources: () => Effect.die("unused"), |
| 214 | getThreadCheckpointContext: () => Effect.succeed(Option.some(threadCheckpointContext)), | |
| 214 | getThreadCheckpointContext: () => Effect.succeedSome(threadCheckpointContext), | |
| 215 | 215 | getFullThreadDiffContext: () => Effect.die("unused"), |
| 216 | 216 | getThreadRuntimeContext: () => Effect.die("unused"), |
| 217 | 217 | getTurnStartMessage: () => Effect.die("unused"), |
| 218 | getThreadShellById: () => Effect.succeed(Option.none()), | |
| 219 | getThreadDetailById: () => Effect.succeed(Option.none()), | |
| 220 | getThreadDetailSnapshot: () => Effect.succeed(Option.none()), | |
| 218 | getThreadShellById: () => Effect.succeedNone, | |
| 219 | getThreadDetailById: () => Effect.succeedNone, | |
| 220 | getThreadDetailSnapshot: () => Effect.succeedNone, | |
| 221 | 221 | searchThreads: () => Effect.succeed({ matches: [] }), |
| 222 | 222 | }), |
| 223 | 223 | ), |
| ... | ... | @@ -298,18 +298,18 @@ describe("CheckpointDiffQuery.layer", () => { |
| 298 | 298 | getSnapshotSequence: () => Effect.succeed({ snapshotSequence: 0 }), |
| 299 | 299 | getCounts: () => Effect.succeed({ projectCount: 0, threadCount: 0 }), |
| 300 | 300 | getEventReplayStats: () => Effect.die("unused"), |
| 301 | getActiveProjectByWorkspaceRoot: () => Effect.succeed(Option.none()), | |
| 301 | getActiveProjectByWorkspaceRoot: () => Effect.succeedNone, | |
| 302 | 302 | getProjectShells: () => Effect.die("unused"), |
| 303 | getProjectShellById: () => Effect.succeed(Option.none()), | |
| 304 | getFirstActiveThreadIdByProjectId: () => Effect.succeed(Option.none()), | |
| 303 | getProjectShellById: () => Effect.succeedNone, | |
| 304 | getFirstActiveThreadIdByProjectId: () => Effect.succeedNone, | |
| 305 | 305 | getImportedAgentSessionSources: () => Effect.die("unused"), |
| 306 | getThreadCheckpointContext: () => Effect.succeed(Option.some(threadCheckpointContext)), | |
| 306 | getThreadCheckpointContext: () => Effect.succeedSome(threadCheckpointContext), | |
| 307 | 307 | getFullThreadDiffContext: () => Effect.die("unused"), |
| 308 | 308 | getThreadRuntimeContext: () => Effect.die("unused"), |
| 309 | 309 | getTurnStartMessage: () => Effect.die("unused"), |
| 310 | getThreadShellById: () => Effect.succeed(Option.none()), | |
| 311 | getThreadDetailById: () => Effect.succeed(Option.none()), | |
| 312 | getThreadDetailSnapshot: () => Effect.succeed(Option.none()), | |
| 310 | getThreadShellById: () => Effect.succeedNone, | |
| 311 | getThreadDetailById: () => Effect.succeedNone, | |
| 312 | getThreadDetailSnapshot: () => Effect.succeedNone, | |
| 313 | 313 | searchThreads: () => Effect.succeed({ matches: [] }), |
| 314 | 314 | }), |
| 315 | 315 | ), |
| ... | ... | @@ -375,18 +375,18 @@ describe("CheckpointDiffQuery.layer", () => { |
| 375 | 375 | getSnapshotSequence: () => Effect.succeed({ snapshotSequence: 0 }), |
| 376 | 376 | getCounts: () => Effect.succeed({ projectCount: 0, threadCount: 0 }), |
| 377 | 377 | getEventReplayStats: () => Effect.die("unused"), |
| 378 | getActiveProjectByWorkspaceRoot: () => Effect.succeed(Option.none()), | |
| 378 | getActiveProjectByWorkspaceRoot: () => Effect.succeedNone, | |
| 379 | 379 | getProjectShells: () => Effect.die("unused"), |
| 380 | getProjectShellById: () => Effect.succeed(Option.none()), | |
| 381 | getFirstActiveThreadIdByProjectId: () => Effect.succeed(Option.none()), | |
| 380 | getProjectShellById: () => Effect.succeedNone, | |
| 381 | getFirstActiveThreadIdByProjectId: () => Effect.succeedNone, | |
| 382 | 382 | getImportedAgentSessionSources: () => Effect.die("unused"), |
| 383 | getThreadCheckpointContext: () => Effect.succeed(Option.some(threadCheckpointContext)), | |
| 383 | getThreadCheckpointContext: () => Effect.succeedSome(threadCheckpointContext), | |
| 384 | 384 | getFullThreadDiffContext: () => Effect.die("unused"), |
| 385 | 385 | getThreadRuntimeContext: () => Effect.die("unused"), |
| 386 | 386 | getTurnStartMessage: () => Effect.die("unused"), |
| 387 | getThreadShellById: () => Effect.succeed(Option.none()), | |
| 388 | getThreadDetailById: () => Effect.succeed(Option.none()), | |
| 389 | getThreadDetailSnapshot: () => Effect.succeed(Option.none()), | |
| 387 | getThreadShellById: () => Effect.succeedNone, | |
| 388 | getThreadDetailById: () => Effect.succeedNone, | |
| 389 | getThreadDetailSnapshot: () => Effect.succeedNone, | |
| 390 | 390 | searchThreads: () => Effect.succeed({ matches: [] }), |
| 391 | 391 | }), |
| 392 | 392 | ), |
| ... | ... | @@ -437,18 +437,18 @@ describe("CheckpointDiffQuery.layer", () => { |
| 437 | 437 | getSnapshotSequence: () => Effect.succeed({ snapshotSequence: 0 }), |
| 438 | 438 | getCounts: () => Effect.succeed({ projectCount: 0, threadCount: 0 }), |
| 439 | 439 | getEventReplayStats: () => Effect.die("unused"), |
| 440 | getActiveProjectByWorkspaceRoot: () => Effect.succeed(Option.none()), | |
| 440 | getActiveProjectByWorkspaceRoot: () => Effect.succeedNone, | |
| 441 | 441 | getProjectShells: () => Effect.die("unused"), |
| 442 | getProjectShellById: () => Effect.succeed(Option.none()), | |
| 443 | getFirstActiveThreadIdByProjectId: () => Effect.succeed(Option.none()), | |
| 442 | getProjectShellById: () => Effect.succeedNone, | |
| 443 | getFirstActiveThreadIdByProjectId: () => Effect.succeedNone, | |
| 444 | 444 | getImportedAgentSessionSources: () => Effect.die("unused"), |
| 445 | getThreadCheckpointContext: () => Effect.succeed(Option.none()), | |
| 446 | getFullThreadDiffContext: () => Effect.succeed(Option.none()), | |
| 445 | getThreadCheckpointContext: () => Effect.succeedNone, | |
| 446 | getFullThreadDiffContext: () => Effect.succeedNone, | |
| 447 | 447 | getThreadRuntimeContext: () => Effect.die("unused"), |
| 448 | 448 | getTurnStartMessage: () => Effect.die("unused"), |
| 449 | getThreadShellById: () => Effect.succeed(Option.none()), | |
| 450 | getThreadDetailById: () => Effect.succeed(Option.none()), | |
| 451 | getThreadDetailSnapshot: () => Effect.succeed(Option.none()), | |
| 449 | getThreadShellById: () => Effect.succeedNone, | |
| 450 | getThreadDetailById: () => Effect.succeedNone, | |
| 451 | getThreadDetailSnapshot: () => Effect.succeedNone, | |
| 452 | 452 | searchThreads: () => Effect.succeed({ matches: [] }), |
| 453 | 453 | }), |
| 454 | 454 | ), |
apps/server/src/cli/theme.ts+17-20| ... | ... | @@ -260,12 +260,10 @@ const writeDefaultTheme = Effect.fn(function* (input: { |
| 260 | 260 | // Falling through here would overwrite whatever landed in between, which |
| 261 | 261 | // is exactly the loss this loop exists to prevent. |
| 262 | 262 | if (attempt >= CONCURRENT_WRITE_ATTEMPTS) { |
| 263 | return yield* Effect.fail( | |
| 264 | new ThemeSettingsBusyError({ | |
| 265 | settingsPath: input.settingsPath, | |
| 266 | attempts: CONCURRENT_WRITE_ATTEMPTS, | |
| 267 | }), | |
| 268 | ); | |
| 263 | return yield* new ThemeSettingsBusyError({ | |
| 264 | settingsPath: input.settingsPath, | |
| 265 | attempts: CONCURRENT_WRITE_ATTEMPTS, | |
| 266 | }); | |
| 269 | 267 | } |
| 270 | 268 | continue; |
| 271 | 269 | } |
| ... | ... | @@ -299,12 +297,13 @@ const publishThemeFile = Effect.fn(function* (input: { |
| 299 | 297 | Effect.mapError((cause) => new ThemeFileUnreadableError({ filePath: input.filePath, cause })), |
| 300 | 298 | ); |
| 301 | 299 | if (info.type !== "File") { |
| 302 | return yield* Effect.fail(new ThemeFileUnreadableError({ filePath: input.filePath })); | |
| 300 | return yield* new ThemeFileUnreadableError({ filePath: input.filePath }); | |
| 303 | 301 | } |
| 304 | 302 | if (Number(info.size) > MAX_THEME_FILE_BYTES) { |
| 305 | return yield* Effect.fail( | |
| 306 | new ThemeFileTooLargeError({ filePath: input.filePath, limit: MAX_THEME_FILE_BYTES }), | |
| 307 | ); | |
| 303 | return yield* new ThemeFileTooLargeError({ | |
| 304 | filePath: input.filePath, | |
| 305 | limit: MAX_THEME_FILE_BYTES, | |
| 306 | }); | |
| 308 | 307 | } |
| 309 | 308 | |
| 310 | 309 | // An explicit source path is the user's own input, and a symlink there is a |
| ... | ... | @@ -320,17 +319,15 @@ const publishThemeFile = Effect.fn(function* (input: { |
| 320 | 319 | ); |
| 321 | 320 | const raw = readThemeFileGuarded(resolvedSource, MAX_THEME_FILE_BYTES); |
| 322 | 321 | if (raw === null) { |
| 323 | return yield* Effect.fail(new ThemeFileUnreadableError({ filePath: input.filePath })); | |
| 322 | return yield* new ThemeFileUnreadableError({ filePath: input.filePath }); | |
| 324 | 323 | } |
| 325 | 324 | |
| 326 | 325 | const decoded = decodeThemeFileJsonExit(raw); |
| 327 | 326 | if (decoded._tag === "Failure") { |
| 328 | return yield* Effect.fail( | |
| 329 | new ThemeFileInvalidError({ filePath: input.filePath, cause: decoded.cause }), | |
| 330 | ); | |
| 327 | return yield* new ThemeFileInvalidError({ filePath: input.filePath, cause: decoded.cause }); | |
| 331 | 328 | } |
| 332 | 329 | if (!environmentThemeFileHasColors(decoded.value)) { |
| 333 | return yield* Effect.fail(new ThemeFileColorlessError({ filePath: input.filePath })); | |
| 330 | return yield* new ThemeFileColorlessError({ filePath: input.filePath }); | |
| 334 | 331 | } |
| 335 | 332 | |
| 336 | 333 | const fileBasename = path.basename(input.filePath, ".json"); |
| ... | ... | @@ -338,7 +335,7 @@ const publishThemeFile = Effect.fn(function* (input: { |
| 338 | 335 | // The same rules the watcher applies when it reads the directory back, so a |
| 339 | 336 | // publish cannot report success for a file that will then be skipped. |
| 340 | 337 | if (!isEnvironmentThemeId(themeId) || UNPUBLISHABLE_THEME_IDS.has(themeId)) { |
| 341 | return yield* Effect.fail(new ThemeFileIdInvalidError({ themeId, filePath: input.filePath })); | |
| 338 | return yield* new ThemeFileIdInvalidError({ themeId, filePath: input.filePath }); | |
| 342 | 339 | } |
| 343 | 340 | |
| 344 | 341 | const destinationPath = path.join(input.themesDir, `${themeId}.json`); |
| ... | ... | @@ -479,7 +476,7 @@ const themeSetCommand = Command.make("set", { |
| 479 | 476 | const fs = yield* FileSystem.FileSystem; |
| 480 | 477 | const target = yield* expandHomePath(flags.theme.trim()); |
| 481 | 478 | if (target.length === 0) { |
| 482 | return yield* Effect.fail(new ThemeTargetMissingError()); | |
| 479 | return yield* new ThemeTargetMissingError(); | |
| 483 | 480 | } |
| 484 | 481 | const paths = yield* resolveThemePaths(flags.baseDir); |
| 485 | 482 | |
| ... | ... | @@ -514,15 +511,15 @@ const themeSetCommand = Command.make("set", { |
| 514 | 511 | revertPublish = published.revert; |
| 515 | 512 | cleanupPublish = published.cleanup; |
| 516 | 513 | } else if (looksLikePath) { |
| 517 | return yield* Effect.fail(new ThemeFileUnreadableError({ filePath: target })); | |
| 514 | return yield* new ThemeFileUnreadableError({ filePath: target }); | |
| 518 | 515 | } else if (isEnvironmentThemeId(target)) { |
| 519 | 516 | const known = yield* resolvableThemeIds(paths.themesDir); |
| 520 | 517 | if (!known.includes(target)) { |
| 521 | return yield* Effect.fail(new ThemeIdUnknownError({ themeId: target, known })); | |
| 518 | return yield* new ThemeIdUnknownError({ themeId: target, known }); | |
| 522 | 519 | } |
| 523 | 520 | themeId = target; |
| 524 | 521 | } else { |
| 525 | return yield* Effect.fail(new ThemeIdInvalidError({ themeId: target })); | |
| 522 | return yield* new ThemeIdInvalidError({ themeId: target }); | |
| 526 | 523 | } |
| 527 | 524 | |
| 528 | 525 | // set means set: if the default cannot be written, the publish that |
apps/server/src/cloud/CliTokenManager.ts+1-1| ... | ... | @@ -341,7 +341,7 @@ const pollDeviceToken = Effect.fn("cloud.cli_token.poll_device_token")(function* |
| 341 | 341 | const response = yield* HttpClientRequest.post(metadata.tokenEndpoint).pipe( |
| 342 | 342 | HttpClientRequest.bodyUrlParams(params), |
| 343 | 343 | httpClient.execute, |
| 344 | Effect.map(Option.some), | |
| 344 | Effect.asSome, | |
| 345 | 345 | Effect.catchIf(isTransportError, () => Effect.succeedNone), |
| 346 | 346 | ); |
| 347 | 347 | // Transport failures and upstream 5xx are transient while the device code |
apps/server/src/cloud/ManagedEndpointRuntime.test.ts+1-1| ... | ... | @@ -40,7 +40,7 @@ const runtimeDependencies = ( |
| 40 | 40 | Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner), |
| 41 | 41 | relayClientLayer, |
| 42 | 42 | Layer.mock(ServerSecretStore.ServerSecretStore)({ |
| 43 | get: () => Effect.succeed(Option.none()), | |
| 43 | get: () => Effect.succeedNone, | |
| 44 | 44 | }), |
| 45 | 45 | ); |
| 46 | 46 |
apps/server/src/cloud/http.test.ts+2-2| ... | ... | @@ -252,7 +252,7 @@ describe("reconcileDesiredCloudLink", () => { |
| 252 | 252 | CliTokenManager.CloudCliTokenManager, |
| 253 | 253 | CliTokenManager.CloudCliTokenManager.of({ |
| 254 | 254 | get: unusedSecretStoreOperation(), |
| 255 | getExisting: Effect.succeed(Option.none()), | |
| 255 | getExisting: Effect.succeedNone, | |
| 256 | 256 | hasCredential: unusedSecretStoreOperation(), |
| 257 | 257 | store: () => unusedSecretStoreOperation(), |
| 258 | 258 | clear: unusedSecretStoreOperation(), |
| ... | ... | @@ -411,7 +411,7 @@ describe("releaseManagedTunnelOnShutdown", () => { |
| 411 | 411 | CliTokenManager.CloudCliTokenManager, |
| 412 | 412 | CliTokenManager.CloudCliTokenManager.of({ |
| 413 | 413 | get: unusedSecretStoreOperation(), |
| 414 | getExisting: Effect.succeed(Option.some(cliToken)), | |
| 414 | getExisting: Effect.succeedSome(cliToken), | |
| 415 | 415 | hasCredential: unusedSecretStoreOperation(), |
| 416 | 416 | store: () => unusedSecretStoreOperation(), |
| 417 | 417 | clear: unusedSecretStoreOperation(), |
apps/server/src/cloud/http.ts+3-3| ... | ... | @@ -152,8 +152,9 @@ export function consumeCloudReplayGuards(input: { |
| 152 | 152 | readonly names: ReadonlyArray<string>; |
| 153 | 153 | readonly value: Uint8Array; |
| 154 | 154 | }) { |
| 155 | return Effect.all( | |
| 156 | input.names.map((name) => | |
| 155 | return Effect.forEach( | |
| 156 | input.names, | |
| 157 | (name) => | |
| 157 | 158 | input.secrets.create(name, input.value).pipe( |
| 158 | 159 | Effect.as(true), |
| 159 | 160 | Effect.catchIf(ServerSecretStore.isSecretStoreError, (error) => |
| ... | ... | @@ -162,7 +163,6 @@ export function consumeCloudReplayGuards(input: { |
| 162 | 163 | : Effect.fail(error), |
| 163 | 164 | ), |
| 164 | 165 | ), |
| 165 | ), | |
| 166 | 166 | { concurrency: input.names.length }, |
| 167 | 167 | ).pipe(Effect.map((created) => created.every(Boolean))); |
| 168 | 168 | } |
apps/server/src/cloud/managedTunnelStartup.ts+8-7| ... | ... | @@ -49,19 +49,20 @@ export const retryManagedTunnelRegistration = <A, E, R>( |
| 49 | 49 | ), |
| 50 | 50 | Schedule.jittered, |
| 51 | 51 | ); |
| 52 | return registration.pipe( | |
| 52 | const withinWindow = registration.pipe( | |
| 53 | 53 | Effect.retry({ |
| 54 | 54 | while: isRetryable, |
| 55 | 55 | schedule: schedule.pipe( |
| 56 | 56 | Schedule.upTo({ duration: MANAGED_TUNNEL_REGISTRATION_RETRY_WINDOW }), |
| 57 | 57 | ), |
| 58 | 58 | }), |
| 59 | Effect.catch((error) => | |
| 60 | onRetryWindowExhausted !== undefined && isRetryable(error) | |
| 61 | ? onRetryWindowExhausted.pipe( | |
| 62 | Effect.andThen(registration.pipe(Effect.retry({ while: isRetryable, schedule }))), | |
| 63 | ) | |
| 64 | : Effect.fail(error), | |
| 59 | ); | |
| 60 | if (onRetryWindowExhausted === undefined) return withinWindow; | |
| 61 | return withinWindow.pipe( | |
| 62 | Effect.catchIf(isRetryable, () => | |
| 63 | onRetryWindowExhausted.pipe( | |
| 64 | Effect.andThen(registration.pipe(Effect.retry({ while: isRetryable, schedule }))), | |
| 65 | ), | |
| 65 | 66 | ), |
| 66 | 67 | ); |
| 67 | 68 | }; |
apps/server/src/cloud/selfUpdate.test.ts+1-1| ... | ... | @@ -105,7 +105,7 @@ const makeHarness = Effect.fn("test.make_self_update_harness")(function* ( |
| 105 | 105 | order.push("accept"); |
| 106 | 106 | return "launcher-id"; |
| 107 | 107 | })), |
| 108 | prepareTrial: Effect.sync((): undefined => undefined), | |
| 108 | prepareTrial: Effect.undefined, | |
| 109 | 109 | }); |
| 110 | 110 | const config = yield* ServerConfig.ServerConfig.pipe( |
| 111 | 111 | Effect.provide(ServerConfig.layerTest(process.cwd(), baseDir)), |
apps/server/src/desktopUpdate/DesktopAppUpdate.ts+1-1| ... | ... | @@ -106,7 +106,7 @@ export const make = Effect.fn("desktopUpdate.desktopAppUpdate.make")(function* ( |
| 106 | 106 | ? emitStage(desktopUpdateProgressStage(report.state)).pipe( |
| 107 | 107 | Effect.as(Option.none<DesktopUpdateStatusReport>()), |
| 108 | 108 | ) |
| 109 | : Effect.succeed(Option.some(report)), | |
| 109 | : Effect.succeedSome(report), | |
| 110 | 110 | ), |
| 111 | 111 | Stream.filterMap( |
| 112 | 112 | Option.match({ |
apps/server/src/device/DeviceHubProxy.ts+1-1| ... | ... | @@ -142,7 +142,7 @@ const proxyWebSocket = Effect.fn("DeviceHubProxy.proxyWebSocket")(function* ( |
| 142 | 142 | pumpFrames(client, writeToUpstream), |
| 143 | 143 | ); |
| 144 | 144 | }), |
| 145 | ).pipe(Effect.catchCause(() => Effect.void)); | |
| 145 | ).pipe(Effect.ignoreCause); | |
| 146 | 146 | return HttpServerResponse.empty(); |
| 147 | 147 | }); |
| 148 | 148 |
apps/server/src/device/DeviceService.ts+5-7| ... | ... | @@ -906,13 +906,11 @@ export const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* |
| 906 | 906 | lifecycleLock.withPermit( |
| 907 | 907 | Effect.gen(function* () { |
| 908 | 908 | if (!installTool) |
| 909 | return yield* Effect.fail( | |
| 910 | new DeviceOperationError({ | |
| 911 | operation: "update device tool", | |
| 912 | reason: "request_failed", | |
| 913 | cause: new Error("Tool installation is unavailable in this device service."), | |
| 914 | }), | |
| 915 | ); | |
| 909 | return yield* new DeviceOperationError({ | |
| 910 | operation: "update device tool", | |
| 911 | reason: "request_failed", | |
| 912 | cause: new Error("Tool installation is unavailable in this device service."), | |
| 913 | }); | |
| 916 | 914 | yield* installTool(tool); |
| 917 | 915 | return yield* inspect; |
| 918 | 916 | }), |
apps/server/src/device/LocalDeviceHost.ts+2-2| ... | ... | @@ -326,7 +326,7 @@ export const make = Effect.fn("LocalDeviceHost.make")(function* () { |
| 326 | 326 | } |
| 327 | 327 | } |
| 328 | 328 | yield* fs.remove(hubStatePath(), { force: true }).pipe(Effect.ignore); |
| 329 | }).pipe(Effect.catchCause(() => Effect.void)); | |
| 329 | }).pipe(Effect.ignoreCause); | |
| 330 | 330 | |
| 331 | 331 | const recordHub = (hub: HubProcess, hubTool: DeviceToolPaths) => |
| 332 | 332 | encodeHubStateFile({ |
| ... | ... | @@ -422,7 +422,7 @@ export const make = Effect.fn("LocalDeviceHost.make")(function* () { |
| 422 | 422 | Stream.runForEach((line) => |
| 423 | 423 | Effect.logDebug("Device hub output", { pid: Number(hub.child.pid), output: line }), |
| 424 | 424 | ), |
| 425 | Effect.catchCause(() => Effect.void), | |
| 425 | Effect.ignoreCause, | |
| 426 | 426 | ); |
| 427 | 427 | |
| 428 | 428 | /** |
apps/server/src/device/deviceToolMaintenance.ts+6-3| ... | ... | @@ -128,9 +128,12 @@ const runMaintenance = Effect.fn("DeviceToolchain.maintenance")(function* ( |
| 128 | 128 | ], |
| 129 | 129 | }); |
| 130 | 130 | if (result.code !== 0) |
| 131 | return yield* Effect.fail( | |
| 132 | new DeviceToolMaintenanceError({ operation, tool, exitCode: result.code, cause: result }), | |
| 133 | ); | |
| 131 | return yield* new DeviceToolMaintenanceError({ | |
| 132 | operation, | |
| 133 | tool, | |
| 134 | exitCode: result.code, | |
| 135 | cause: result, | |
| 136 | }); | |
| 134 | 137 | }); |
| 135 | 138 | |
| 136 | 139 | export const pruneLocalDeviceTools = Effect.fn("DeviceToolchain.prune")(function* ( |
apps/server/src/diagnostics/TraceDiagnostics.ts+3-3| ... | ... | @@ -420,8 +420,9 @@ export const make = Effect.gen(function* () { |
| 420 | 420 | const readAt = options.readAt ?? (yield* DateTime.now); |
| 421 | 421 | const slowSpanThresholdMs = options.slowSpanThresholdMs ?? DEFAULT_SLOW_SPAN_THRESHOLD_MS; |
| 422 | 422 | const paths = toRotatedTracePaths(options.traceFilePath, options.maxFiles); |
| 423 | const results = yield* Effect.all( | |
| 424 | paths.map((path) => | |
| 423 | const results = yield* Effect.forEach( | |
| 424 | paths, | |
| 425 | (path) => | |
| 425 | 426 | readTraceFile(fileSystem, path).pipe( |
| 426 | 427 | Effect.tapError((cause) => |
| 427 | 428 | Effect.logWarning("Failed to read local trace file.").pipe( |
| ... | ... | @@ -434,7 +435,6 @@ export const make = Effect.gen(function* () { |
| 434 | 435 | ), |
| 435 | 436 | Effect.result, |
| 436 | 437 | ), |
| 437 | ), | |
| 438 | 438 | { |
| 439 | 439 | concurrency: 1, |
| 440 | 440 | }, |
apps/server/src/environment/ServerEnvironment.test.ts+1-2| ... | ... | @@ -6,7 +6,6 @@ import * as Deferred from "effect/Deferred"; |
| 6 | 6 | import * as Effect from "effect/Effect"; |
| 7 | 7 | import * as FileSystem from "effect/FileSystem"; |
| 8 | 8 | import * as Layer from "effect/Layer"; |
| 9 | import * as Option from "effect/Option"; | |
| 10 | 9 | import * as PlatformError from "effect/PlatformError"; |
| 11 | 10 | import * as Schema from "effect/Schema"; |
| 12 | 11 | |
| ... | ... | @@ -35,7 +34,7 @@ const makeServerEnvironmentLayer = (baseDir: string) => |
| 35 | 34 | const emptySecretStoreLayer = Layer.succeed( |
| 36 | 35 | ServerSecretStore.ServerSecretStore, |
| 37 | 36 | ServerSecretStore.ServerSecretStore.of({ |
| 38 | get: () => Effect.succeed(Option.none()), | |
| 37 | get: () => Effect.succeedNone, | |
| 39 | 38 | set: () => Effect.void, |
| 40 | 39 | create: () => Effect.void, |
| 41 | 40 | getOrCreateRandom: () => Effect.succeed(new Uint8Array()), |
apps/server/src/environment/ServerEnvironment.ts+6-7| ... | ... | @@ -127,13 +127,12 @@ const makeIdentity = Effect.gen(function* () { |
| 127 | 127 | }); |
| 128 | 128 | yield* fileSystem.writeFileString(tempPath, `${value}\n`); |
| 129 | 129 | // Publish the completed file without replacing an ID created by another process. |
| 130 | yield* fileSystem | |
| 131 | .link(tempPath, destinationPath) | |
| 132 | .pipe( | |
| 133 | Effect.catch((cause) => | |
| 134 | cause.reason._tag === "AlreadyExists" ? Effect.void : Effect.fail(cause), | |
| 135 | ), | |
| 136 | ); | |
| 130 | yield* fileSystem.link(tempPath, destinationPath).pipe( | |
| 131 | Effect.catchIf( | |
| 132 | (cause) => cause.reason._tag === "AlreadyExists", | |
| 133 | () => Effect.void, | |
| 134 | ), | |
| 135 | ); | |
| 137 | 136 | if (mode === "recover") { |
| 138 | 137 | // Keep the recovery ID so delayed initializers also publish the same winner. |
| 139 | 138 | yield* fileSystem.remove(tempPath); |
apps/server/src/environment/ServerEnvironmentLabel.ts+1-1| ... | ... | @@ -128,7 +128,7 @@ const runFriendlyLabelCommand = Effect.fn("runFriendlyLabelCommand")(function* ( |
| 128 | 128 | cause, |
| 129 | 129 | }), |
| 130 | 130 | ), |
| 131 | Effect.map(Option.some), | |
| 131 | Effect.asSome, | |
| 132 | 132 | Effect.catchTags({ |
| 133 | 133 | ServerEnvironmentLabelCommandError: (error) => |
| 134 | 134 | Effect.logDebug(error.message).pipe( |
apps/server/src/environment/ServerEnvironmentMachine.ts+2-2| ... | ... | @@ -107,7 +107,7 @@ const readOptionalFile = Effect.fn("readOptionalFile")(function* (path: string) |
| 107 | 107 | const fileSystem = yield* FileSystem.FileSystem; |
| 108 | 108 | return yield* fileSystem.readFileString(path).pipe( |
| 109 | 109 | Effect.map(normalize), |
| 110 | Effect.catch(() => Effect.succeed(null)), | |
| 110 | Effect.orElseSucceed(() => null), | |
| 111 | 111 | ); |
| 112 | 112 | }); |
| 113 | 113 | |
| ... | ... | @@ -125,7 +125,7 @@ const runProbe = Effect.fn("runMachineProbe")(function* (input: { |
| 125 | 125 | }) |
| 126 | 126 | .pipe( |
| 127 | 127 | Effect.map((result) => (result.code === 0 ? normalize(result.stdout) : null)), |
| 128 | Effect.catch(() => Effect.succeed(null)), | |
| 128 | Effect.orElseSucceed(() => null), | |
| 129 | 129 | ); |
| 130 | 130 | }); |
| 131 | 131 |
apps/server/src/git/GitManager.ts+1-1| ... | ... | @@ -2097,7 +2097,7 @@ export const make = Effect.gen(function* () { |
| 2097 | 2097 | title: generated.title, |
| 2098 | 2098 | bodyFile, |
| 2099 | 2099 | }) |
| 2100 | .pipe(Effect.ensuring(fileSystem.remove(bodyFile).pipe(Effect.catch(() => Effect.void)))); | |
| 2100 | .pipe(Effect.ensuring(fileSystem.remove(bodyFile).pipe(Effect.ignore))); | |
| 2101 | 2101 | |
| 2102 | 2102 | const created = yield* findOpenPr(cwd, headContext); |
| 2103 | 2103 | if (!created) { |
apps/server/src/git/linkCreatedPullRequest.test.ts+1-1| ... | ... | @@ -83,7 +83,7 @@ const makeDependencies = ( |
| 83 | 83 | Layer.mergeAll( |
| 84 | 84 | Layer.mock(ProjectionSnapshotQuery)({ |
| 85 | 85 | getThreadShellById: () => Effect.succeed(Option.fromNullishOr(threadShell)), |
| 86 | getProjectShellById: () => Effect.succeed(Option.some(project)), | |
| 86 | getProjectShellById: () => Effect.succeedSome(project), | |
| 87 | 87 | }), |
| 88 | 88 | Layer.mock(OrchestrationEngineService)({ |
| 89 | 89 | readEvents: () => Stream.empty, |
apps/server/src/keybindings.test.ts+3-3| ... | ... | @@ -567,13 +567,13 @@ it.layer(NodeServices.layer)("keybindings", (it) => { |
| 567 | 567 | ); |
| 568 | 568 | yield* Effect.gen(function* () { |
| 569 | 569 | const keybindings = yield* Keybindings.Keybindings; |
| 570 | yield* Effect.all( | |
| 571 | commands.map((command, index) => | |
| 570 | yield* Effect.forEach( | |
| 571 | commands, | |
| 572 | (command, index) => | |
| 572 | 573 | keybindings.upsertKeybindingRule({ |
| 573 | 574 | key: `mod+${String.fromCharCode(97 + index)}`, |
| 574 | 575 | command, |
| 575 | 576 | }), |
| 576 | ), | |
| 577 | 577 | { concurrency: "unbounded", discard: true }, |
| 578 | 578 | ); |
| 579 | 579 | }); |
apps/server/src/mcp/McpHttpServer.test.ts+1-2| ... | ... | @@ -6,7 +6,6 @@ import * as Deferred from "effect/Deferred"; |
| 6 | 6 | import * as Effect from "effect/Effect"; |
| 7 | 7 | import * as FileSystem from "effect/FileSystem"; |
| 8 | 8 | import * as Layer from "effect/Layer"; |
| 9 | import * as Option from "effect/Option"; | |
| 10 | 9 | import * as Path from "effect/Path"; |
| 11 | 10 | import * as Schema from "effect/Schema"; |
| 12 | 11 | import * as Stream from "effect/Stream"; |
| ... | ... | @@ -56,7 +55,7 @@ const PullRequestsTestLayer = McpHttpServer.PullRequestsToolkitRegistrationLive. |
| 56 | 55 | Layer.provide( |
| 57 | 56 | Layer.mergeAll( |
| 58 | 57 | Layer.mock(ProjectionSnapshotQuery)({ |
| 59 | getThreadShellById: () => Effect.succeed(Option.none()), | |
| 58 | getThreadShellById: () => Effect.succeedNone, | |
| 60 | 59 | }), |
| 61 | 60 | Layer.mock(OrchestrationEngineService)({}), |
| 62 | 61 | NodeServices.layer, |
apps/server/src/mcp/McpInvocationContext.ts+8-6| ... | ... | @@ -47,9 +47,11 @@ const missingCapability = ( |
| 47 | 47 | export const requireMcpCapability = <const C extends McpCapability>( |
| 48 | 48 | capability: C, |
| 49 | 49 | ): Effect.Effect<McpInvocationScope, McpCapabilityError<C>, McpInvocationContext> => |
| 50 | Effect.flatMap(McpInvocationContext, (invocation) => | |
| 51 | invocation.capabilities.has(capability) | |
| 52 | ? Effect.succeed(invocation) | |
| 53 | : // The conditional type narrows what the literal argument decided at runtime. | |
| 54 | Effect.fail(missingCapability(invocation, capability) as McpCapabilityError<C>), | |
| 55 | ).pipe(Effect.withSpan("mcp.requireCapability")); | |
| 50 | McpInvocationContext.pipe( | |
| 51 | Effect.filterOrFail( | |
| 52 | (invocation) => invocation.capabilities.has(capability), | |
| 53 | // The conditional type narrows what the literal argument decided at runtime. | |
| 54 | (invocation) => missingCapability(invocation, capability) as McpCapabilityError<C>, | |
| 55 | ), | |
| 56 | Effect.withSpan("mcp.requireCapability"), | |
| 57 | ); |
apps/server/src/mcp/McpSessionRegistry.ts+1-1| ... | ... | @@ -230,7 +230,7 @@ export const issueActiveMcpCredential = ( |
| 230 | 230 | ? activeMcpSessionRegistry |
| 231 | 231 | .revokeThread(request.threadId) |
| 232 | 232 | .pipe(Effect.andThen(activeMcpSessionRegistry.issue(request))) |
| 233 | : Effect.sync((): McpIssuedCredential | undefined => undefined); | |
| 233 | : Effect.undefined; | |
| 234 | 234 | |
| 235 | 235 | /** |
| 236 | 236 | * Refreshes the liveness of a thread's MCP credential. Called on every provider |
apps/server/src/mcp/toolkits/preview/handlers.ts+6-5| ... | ... | @@ -87,7 +87,7 @@ const invoke = Effect.fn("PreviewToolkit.invoke")(function* <A>( |
| 87 | 87 | updateCurrentTab: false, |
| 88 | 88 | ...(statusTabId === undefined ? {} : { tabId: statusTabId }), |
| 89 | 89 | }) |
| 90 | .pipe(Effect.catch(() => Effect.succeed(null))); | |
| 90 | .pipe(Effect.orElseSucceed(() => null)); | |
| 91 | 91 | return { |
| 92 | 92 | result, |
| 93 | 93 | ...(page?.url && /^https?:\/\//i.test(page.url) && page.url.length <= 4096 |
| ... | ... | @@ -175,10 +175,11 @@ export const claimPreviewRecording = Effect.fn("PreviewToolkit.claimRecording")( |
| 175 | 175 | yield* fileSystem.rename(currentPath, finalPath); |
| 176 | 176 | }).pipe( |
| 177 | 177 | // Another stop may already have claimed this exact upload for this thread. |
| 178 | Effect.catch((cause) => | |
| 179 | cause._tag !== "PreviewAutomationRecordingTransferError" && cause.reason._tag === "NotFound" | |
| 180 | ? validateFile(finalPath) | |
| 181 | : Effect.fail(cause), | |
| 178 | Effect.catchIf( | |
| 179 | (cause) => | |
| 180 | cause._tag !== "PreviewAutomationRecordingTransferError" && | |
| 181 | cause.reason._tag === "NotFound", | |
| 182 | () => validateFile(finalPath), | |
| 182 | 183 | ), |
| 183 | 184 | Effect.mapError((cause) => new PreviewAutomationRecordingTransferError({ threadId, cause })), |
| 184 | 185 | ); |
apps/server/src/orchestration/Layers/CheckpointReactor.ts+31-37| ... | ... | @@ -97,12 +97,12 @@ const make = Effect.gen(function* () { |
| 97 | 97 | const entryRefreshWorker = yield* makeDrainableWorker((cwd: string) => |
| 98 | 98 | Effect.sync(() => queuedEntryRefreshes.delete(cwd)).pipe( |
| 99 | 99 | Effect.andThen(workspaceEntries.refresh(cwd)), |
| 100 | Effect.catchCause((cause) => | |
| 101 | Cause.hasInterruptsOnly(cause) | |
| 102 | ? Effect.failCause(cause) | |
| 103 | : Effect.logWarning("failed to refresh checkpoint workspace entries", { | |
| 104 | cwd, | |
| 105 | }), | |
| 100 | Effect.catchCauseIf( | |
| 101 | (cause) => !Cause.hasInterruptsOnly(cause), | |
| 102 | () => | |
| 103 | Effect.logWarning("failed to refresh checkpoint workspace entries", { | |
| 104 | cwd, | |
| 105 | }), | |
| 106 | 106 | ), |
| 107 | 107 | ), |
| 108 | 108 | ); |
| ... | ... | @@ -625,15 +625,14 @@ const make = Effect.gen(function* () { |
| 625 | 625 | branch: checkedOutBranch, |
| 626 | 626 | }); |
| 627 | 627 | }).pipe( |
| 628 | Effect.catchCause((cause) => { | |
| 629 | if (Cause.hasInterruptsOnly(cause)) { | |
| 630 | return Effect.failCause(cause); | |
| 631 | } | |
| 632 | return Effect.logWarning("failed to follow worktree branch drift", { | |
| 633 | threadId: input.threadId, | |
| 634 | cause: Cause.pretty(cause), | |
| 635 | }); | |
| 636 | }), | |
| 628 | Effect.catchCauseIf( | |
| 629 | (cause) => !Cause.hasInterruptsOnly(cause), | |
| 630 | (cause) => | |
| 631 | Effect.logWarning("failed to follow worktree branch drift", { | |
| 632 | threadId: input.threadId, | |
| 633 | cause: Cause.pretty(cause), | |
| 634 | }), | |
| 635 | ), | |
| 637 | 636 | ); |
| 638 | 637 | }); |
| 639 | 638 | |
| ... | ... | @@ -643,12 +642,12 @@ const make = Effect.gen(function* () { |
| 643 | 642 | const statusRefreshWorker = yield* makeDrainableWorker( |
| 644 | 643 | (event: Extract<ProviderRuntimeEvent, { type: "turn.completed" }>) => |
| 645 | 644 | refreshLocalGitStatusFromTurnCompletion(event).pipe( |
| 646 | Effect.catchCause((cause) => | |
| 647 | Cause.hasInterruptsOnly(cause) | |
| 648 | ? Effect.failCause(cause) | |
| 649 | : Effect.logWarning("failed to refresh git status after turn completion", { | |
| 650 | threadId: event.threadId, | |
| 651 | }), | |
| 645 | Effect.catchCauseIf( | |
| 646 | (cause) => !Cause.hasInterruptsOnly(cause), | |
| 647 | () => | |
| 648 | Effect.logWarning("failed to refresh git status after turn completion", { | |
| 649 | threadId: event.threadId, | |
| 650 | }), | |
| 652 | 651 | ), |
| 653 | 652 | ), |
| 654 | 653 | ); |
| ... | ... | @@ -749,11 +748,7 @@ const make = Effect.gen(function* () { |
| 749 | 748 | for (const candidate of paths) { |
| 750 | 749 | const otherCwd = yield* fileSystem |
| 751 | 750 | .realPath(candidate) |
| 752 | .pipe( | |
| 753 | Effect.catch((error) => | |
| 754 | error.reason._tag === "NotFound" ? Effect.succeed(null) : Effect.fail(error), | |
| 755 | ), | |
| 756 | ); | |
| 751 | .pipe(Effect.catchReason("PlatformError", "NotFound", () => Effect.succeed(null))); | |
| 757 | 752 | if (otherCwd === null) continue; |
| 758 | 753 | const isWithin = (parent: string, child: string) => { |
| 759 | 754 | const relative = path.relative(parent, child); |
| ... | ... | @@ -791,7 +786,7 @@ const make = Effect.gen(function* () { |
| 791 | 786 | preferSessionRuntime: true, |
| 792 | 787 | }).pipe( |
| 793 | 788 | Effect.catch((error) => |
| 794 | event.payload.restoreFiles === false ? Effect.succeed(undefined) : Effect.fail(error), | |
| 789 | event.payload.restoreFiles === false ? Effect.undefined : Effect.fail(error), | |
| 795 | 790 | ), |
| 796 | 791 | ); |
| 797 | 792 | |
| ... | ... | @@ -1014,16 +1009,15 @@ const make = Effect.gen(function* () { |
| 1014 | 1009 | |
| 1015 | 1010 | const processInputSafely = (input: ReactorInput) => |
| 1016 | 1011 | processInput(input).pipe( |
| 1017 | Effect.catchCause((cause) => { | |
| 1018 | if (Cause.hasInterruptsOnly(cause)) { | |
| 1019 | return Effect.failCause(cause); | |
| 1020 | } | |
| 1021 | return Effect.logWarning("checkpoint reactor failed to process input", { | |
| 1022 | source: input.source, | |
| 1023 | eventType: input.event.type, | |
| 1024 | cause: Cause.pretty(cause), | |
| 1025 | }); | |
| 1026 | }), | |
| 1012 | Effect.catchCauseIf( | |
| 1013 | (cause) => !Cause.hasInterruptsOnly(cause), | |
| 1014 | (cause) => | |
| 1015 | Effect.logWarning("checkpoint reactor failed to process input", { | |
| 1016 | source: input.source, | |
| 1017 | eventType: input.event.type, | |
| 1018 | cause: Cause.pretty(cause), | |
| 1019 | }), | |
| 1020 | ), | |
| 1027 | 1021 | ); |
| 1028 | 1022 | |
| 1029 | 1023 | const worker = yield* makeDrainableWorker(processInputSafely); |
apps/server/src/orchestration/Layers/OrchestrationEngine.test.ts+8-8| ... | ... | @@ -446,18 +446,18 @@ describe("OrchestrationEngine", () => { |
| 446 | 446 | Effect.succeed({ snapshotSequence: projectionSnapshot.snapshotSequence }), |
| 447 | 447 | getCounts: () => Effect.succeed({ projectCount: 1, threadCount: 1 }), |
| 448 | 448 | getEventReplayStats: () => Effect.die("unused"), |
| 449 | getActiveProjectByWorkspaceRoot: () => Effect.succeed(Option.none()), | |
| 450 | getProjectShellById: () => Effect.succeed(Option.none()), | |
| 449 | getActiveProjectByWorkspaceRoot: () => Effect.succeedNone, | |
| 450 | getProjectShellById: () => Effect.succeedNone, | |
| 451 | 451 | getProjectShells: () => Effect.succeed([]), |
| 452 | getFirstActiveThreadIdByProjectId: () => Effect.succeed(Option.none()), | |
| 452 | getFirstActiveThreadIdByProjectId: () => Effect.succeedNone, | |
| 453 | 453 | getImportedAgentSessionSources: () => Effect.die("unused"), |
| 454 | getThreadCheckpointContext: () => Effect.succeed(Option.none()), | |
| 455 | getFullThreadDiffContext: () => Effect.succeed(Option.none()), | |
| 454 | getThreadCheckpointContext: () => Effect.succeedNone, | |
| 455 | getFullThreadDiffContext: () => Effect.succeedNone, | |
| 456 | 456 | getThreadRuntimeContext: () => Effect.die("unused"), |
| 457 | 457 | getTurnStartMessage: () => Effect.die("unused"), |
| 458 | getThreadShellById: () => Effect.succeed(Option.none()), | |
| 459 | getThreadDetailById: () => Effect.succeed(Option.none()), | |
| 460 | getThreadDetailSnapshot: () => Effect.succeed(Option.none()), | |
| 458 | getThreadShellById: () => Effect.succeedNone, | |
| 459 | getThreadDetailById: () => Effect.succeedNone, | |
| 460 | getThreadDetailSnapshot: () => Effect.succeedNone, | |
| 461 | 461 | searchThreads: () => Effect.succeed({ matches: [] }), |
| 462 | 462 | }), |
| 463 | 463 | ), |
apps/server/src/orchestration/Layers/OrchestrationEngine.ts+1-1| ... | ... | @@ -400,7 +400,7 @@ const makeOrchestrationEngine = Effect.gen(function* () { |
| 400 | 400 | status: "rejected", |
| 401 | 401 | error: error.message, |
| 402 | 402 | }) |
| 403 | .pipe(Effect.catch(() => Effect.void)); | |
| 403 | .pipe(Effect.ignore); | |
| 404 | 404 | } |
| 405 | 405 | } |
| 406 | 406 |
apps/server/src/orchestration/Layers/ProviderCommandReactor.ts+29-38| ... | ... | @@ -511,14 +511,14 @@ const make = Effect.gen(function* () { |
| 511 | 511 | Effect.andThen( |
| 512 | 512 | gitWorkflow.createWorktree({ cwd, refName: branch, path: worktreePath }, { submodules }), |
| 513 | 513 | ), |
| 514 | Effect.catchCause((cause) => | |
| 515 | Cause.hasInterruptsOnly(cause) | |
| 516 | ? Effect.failCause(cause) | |
| 517 | : Effect.logWarning("provider command reactor failed to recreate worktree", { | |
| 518 | threadId: thread.id, | |
| 519 | worktreePath, | |
| 520 | cause: Cause.pretty(cause), | |
| 521 | }), | |
| 514 | Effect.catchCauseIf( | |
| 515 | (cause) => !Cause.hasInterruptsOnly(cause), | |
| 516 | (cause) => | |
| 517 | Effect.logWarning("provider command reactor failed to recreate worktree", { | |
| 518 | threadId: thread.id, | |
| 519 | worktreePath, | |
| 520 | cause: Cause.pretty(cause), | |
| 521 | }), | |
| 522 | 522 | ), |
| 523 | 523 | ); |
| 524 | 524 | }); |
| ... | ... | @@ -1154,15 +1154,14 @@ const make = Effect.gen(function* () { |
| 1154 | 1154 | return; |
| 1155 | 1155 | } |
| 1156 | 1156 | const result = yield* regenerateThreadTitle(event, requestId).pipe( |
| 1157 | Effect.catchCause((cause) => { | |
| 1158 | if (Cause.hasInterruptsOnly(cause)) { | |
| 1159 | return Effect.failCause(cause); | |
| 1160 | } | |
| 1161 | return Effect.logWarning("provider command reactor failed to regenerate thread title", { | |
| 1162 | threadId: event.payload.threadId, | |
| 1163 | cause: Cause.pretty(cause), | |
| 1164 | }).pipe(Effect.as({ _tag: "Completed", title: undefined } as const)); | |
| 1165 | }), | |
| 1157 | Effect.catchCauseIf( | |
| 1158 | (cause) => !Cause.hasInterruptsOnly(cause), | |
| 1159 | (cause) => | |
| 1160 | Effect.logWarning("provider command reactor failed to regenerate thread title", { | |
| 1161 | threadId: event.payload.threadId, | |
| 1162 | cause: Cause.pretty(cause), | |
| 1163 | }).pipe(Effect.as({ _tag: "Completed", title: undefined } as const)), | |
| 1164 | ), | |
| 1166 | 1165 | ); |
| 1167 | 1166 | if (result._tag === "Superseded") { |
| 1168 | 1167 | return; |
| ... | ... | @@ -1174,34 +1173,26 @@ const make = Effect.gen(function* () { |
| 1174 | 1173 | ...(result.title !== undefined ? { title: result.title } : {}), |
| 1175 | 1174 | }; |
| 1176 | 1175 | yield* dispatchThreadTitleRegenerationCompletion(completion).pipe( |
| 1177 | Effect.catchCause((cause) => { | |
| 1178 | if (Cause.hasInterruptsOnly(cause)) { | |
| 1179 | return Effect.failCause(cause); | |
| 1180 | } | |
| 1181 | return Effect.logWarning( | |
| 1182 | "provider command reactor retrying title regeneration completion", | |
| 1183 | { | |
| 1176 | Effect.catchCauseIf( | |
| 1177 | (cause) => !Cause.hasInterruptsOnly(cause), | |
| 1178 | (cause) => | |
| 1179 | Effect.logWarning("provider command reactor retrying title regeneration completion", { | |
| 1184 | 1180 | threadId: event.payload.threadId, |
| 1185 | 1181 | cause: Cause.pretty(cause), |
| 1186 | }, | |
| 1187 | ).pipe(Effect.andThen(dispatchThreadTitleRegenerationCompletion(completion))); | |
| 1188 | }), | |
| 1182 | }).pipe(Effect.andThen(dispatchThreadTitleRegenerationCompletion(completion))), | |
| 1183 | ), | |
| 1189 | 1184 | ); |
| 1190 | 1185 | }, |
| 1191 | 1186 | (effect, event) => |
| 1192 | 1187 | effect.pipe( |
| 1193 | Effect.catchCause((cause) => { | |
| 1194 | if (Cause.hasInterruptsOnly(cause)) { | |
| 1195 | return Effect.failCause(cause); | |
| 1196 | } | |
| 1197 | return Effect.logWarning( | |
| 1198 | "provider command reactor failed to complete title regeneration", | |
| 1199 | { | |
| 1188 | Effect.catchCauseIf( | |
| 1189 | (cause) => !Cause.hasInterruptsOnly(cause), | |
| 1190 | (cause) => | |
| 1191 | Effect.logWarning("provider command reactor failed to complete title regeneration", { | |
| 1200 | 1192 | threadId: event.payload.threadId, |
| 1201 | 1193 | cause: Cause.pretty(cause), |
| 1202 | }, | |
| 1203 | ); | |
| 1204 | }), | |
| 1194 | }), | |
| 1195 | ), | |
| 1205 | 1196 | ), |
| 1206 | 1197 | ); |
| 1207 | 1198 | const threadTitleRegenerationWorker = yield* makeDrainableWorker( |
| ... | ... | @@ -1496,7 +1487,7 @@ const make = Effect.gen(function* () { |
| 1496 | 1487 | interactionMode: event.payload.interactionMode, |
| 1497 | 1488 | createdAt: event.payload.createdAt, |
| 1498 | 1489 | }).pipe( |
| 1499 | Effect.map(Option.some), | |
| 1490 | Effect.asSome, | |
| 1500 | 1491 | Effect.catchCause((cause) => handleTurnStartFailure(cause).pipe(Effect.as(Option.none()))), |
| 1501 | 1492 | ); |
| 1502 | 1493 |
apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts+10-11| ... | ... | @@ -2671,17 +2671,16 @@ const make = Effect.gen(function* () { |
| 2671 | 2671 | (source: string, event: { readonly eventId: string; readonly type: string }) => |
| 2672 | 2672 | <E, R>(effect: Effect.Effect<void, E, R>) => |
| 2673 | 2673 | effect.pipe( |
| 2674 | Effect.catchCause((cause) => { | |
| 2675 | if (Cause.hasInterruptsOnly(cause)) { | |
| 2676 | return Effect.failCause(cause); | |
| 2677 | } | |
| 2678 | return Effect.logWarning("provider runtime ingestion failed to process event", { | |
| 2679 | source, | |
| 2680 | eventId: event.eventId, | |
| 2681 | eventType: event.type, | |
| 2682 | cause: Cause.pretty(cause), | |
| 2683 | }); | |
| 2684 | }), | |
| 2674 | Effect.catchCauseIf( | |
| 2675 | (cause) => !Cause.hasInterruptsOnly(cause), | |
| 2676 | (cause) => | |
| 2677 | Effect.logWarning("provider runtime ingestion failed to process event", { | |
| 2678 | source, | |
| 2679 | eventId: event.eventId, | |
| 2680 | eventType: event.type, | |
| 2681 | cause: Cause.pretty(cause), | |
| 2682 | }), | |
| 2683 | ), | |
| 2685 | 2684 | ); |
| 2686 | 2685 | |
| 2687 | 2686 | const worker = yield* makeDrainableWorker((input: RuntimeIngestionInput) => |
apps/server/src/orchestration/Layers/ThreadDeletionReactor.ts+17-19| ... | ... | @@ -27,15 +27,14 @@ export const logCleanupCauseUnlessInterrupted = <R, E>({ |
| 27 | 27 | readonly threadId: ThreadDeletedEvent["payload"]["threadId"]; |
| 28 | 28 | }): Effect.Effect<void, E, R> => |
| 29 | 29 | effect.pipe( |
| 30 | Effect.catchCause((cause) => { | |
| 31 | if (Cause.hasInterruptsOnly(cause)) { | |
| 32 | return Effect.failCause(cause); | |
| 33 | } | |
| 34 | return Effect.logDebug(message, { | |
| 35 | threadId, | |
| 36 | cause: Cause.pretty(cause), | |
| 37 | }); | |
| 38 | }), | |
| 30 | Effect.catchCauseIf( | |
| 31 | (cause) => !Cause.hasInterruptsOnly(cause), | |
| 32 | (cause) => | |
| 33 | Effect.logDebug(message, { | |
| 34 | threadId, | |
| 35 | cause: Cause.pretty(cause), | |
| 36 | }), | |
| 37 | ), | |
| 39 | 38 | ); |
| 40 | 39 | |
| 41 | 40 | const make = Effect.gen(function* () { |
| ... | ... | @@ -67,16 +66,15 @@ const make = Effect.gen(function* () { |
| 67 | 66 | |
| 68 | 67 | const processThreadDeletedSafely = (event: ThreadDeletedEvent) => |
| 69 | 68 | processThreadDeleted(event).pipe( |
| 70 | Effect.catchCause((cause) => { | |
| 71 | if (Cause.hasInterruptsOnly(cause)) { | |
| 72 | return Effect.failCause(cause); | |
| 73 | } | |
| 74 | return Effect.logWarning("thread deletion reactor failed to process event", { | |
| 75 | eventType: event.type, | |
| 76 | threadId: event.payload.threadId, | |
| 77 | cause: Cause.pretty(cause), | |
| 78 | }); | |
| 79 | }), | |
| 69 | Effect.catchCauseIf( | |
| 70 | (cause) => !Cause.hasInterruptsOnly(cause), | |
| 71 | (cause) => | |
| 72 | Effect.logWarning("thread deletion reactor failed to process event", { | |
| 73 | eventType: event.type, | |
| 74 | threadId: event.payload.threadId, | |
| 75 | cause: Cause.pretty(cause), | |
| 76 | }), | |
| 77 | ), | |
| 80 | 78 | ); |
| 81 | 79 | |
| 82 | 80 | const worker = yield* makeDrainableWorker(processThreadDeletedSafely); |
apps/server/src/orchestration/PullRequestSyncReactor.ts+6-6| ... | ... | @@ -259,12 +259,12 @@ export const make = Effect.gen(function* () { |
| 259 | 259 | Effect.map((stack) => ({ |
| 260 | 260 | stack: stack === null ? null : ({ kind: "native", ...stack } as const), |
| 261 | 261 | })), |
| 262 | Effect.catchCause((cause) => | |
| 263 | Cause.hasInterruptsOnly(cause) | |
| 264 | ? Effect.failCause(cause) | |
| 265 | : Effect.logWarning("pull request stack lookup failed", { | |
| 266 | key, | |
| 267 | }).pipe(Effect.as(null)), | |
| 262 | Effect.catchCauseIf( | |
| 263 | (cause) => !Cause.hasInterruptsOnly(cause), | |
| 264 | () => | |
| 265 | Effect.logWarning("pull request stack lookup failed", { | |
| 266 | key, | |
| 267 | }).pipe(Effect.as(null)), | |
| 268 | 268 | ), |
| 269 | 269 | ) |
| 270 | 270 | : null; |
apps/server/src/orchestration/ThreadPullRequestReactor.ts+30-30| ... | ... | @@ -207,16 +207,16 @@ export const make = Effect.gen(function* () { |
| 207 | 207 | } |
| 208 | 208 | return { thread, branchPullRequest, replacement }; |
| 209 | 209 | }).pipe( |
| 210 | Effect.catchCause((cause) => | |
| 211 | Cause.hasInterruptsOnly(cause) | |
| 212 | ? Effect.failCause(cause) | |
| 213 | : Effect.logWarning("thread pull request discovery failed", { | |
| 214 | threadId: thread.id, | |
| 215 | cause: Cause.pretty(cause), | |
| 216 | }).pipe( | |
| 217 | Effect.tap(() => Effect.sync(() => failBackfill([thread]))), | |
| 218 | Effect.as(null), | |
| 219 | ), | |
| 210 | Effect.catchCauseIf( | |
| 211 | (cause) => !Cause.hasInterruptsOnly(cause), | |
| 212 | (cause) => | |
| 213 | Effect.logWarning("thread pull request discovery failed", { | |
| 214 | threadId: thread.id, | |
| 215 | cause: Cause.pretty(cause), | |
| 216 | }).pipe( | |
| 217 | Effect.tap(() => Effect.sync(() => failBackfill([thread]))), | |
| 218 | Effect.as(null), | |
| 219 | ), | |
| 220 | 220 | ), |
| 221 | 221 | ), |
| 222 | 222 | ); |
| ... | ... | @@ -273,25 +273,25 @@ export const make = Effect.gen(function* () { |
| 273 | 273 | OrchestrationCommandInvariantError: () => |
| 274 | 274 | Effect.sync(() => finishBackfill([thread])), |
| 275 | 275 | }), |
| 276 | Effect.catchCause((cause) => | |
| 277 | Cause.hasInterruptsOnly(cause) | |
| 278 | ? Effect.failCause(cause) | |
| 279 | : Effect.logWarning("thread pull request update failed", { | |
| 280 | threadId: thread.id, | |
| 281 | cause: Cause.pretty(cause), | |
| 282 | }).pipe(Effect.tap(() => Effect.sync(() => failBackfill([thread])))), | |
| 276 | Effect.catchCauseIf( | |
| 277 | (cause) => !Cause.hasInterruptsOnly(cause), | |
| 278 | (cause) => | |
| 279 | Effect.logWarning("thread pull request update failed", { | |
| 280 | threadId: thread.id, | |
| 281 | cause: Cause.pretty(cause), | |
| 282 | }).pipe(Effect.tap(() => Effect.sync(() => failBackfill([thread])))), | |
| 283 | 283 | ), |
| 284 | 284 | ), |
| 285 | 285 | { discard: true }, |
| 286 | 286 | ); |
| 287 | 287 | }).pipe( |
| 288 | Effect.catchCause((cause) => | |
| 289 | Cause.hasInterruptsOnly(cause) | |
| 290 | ? Effect.failCause(cause) | |
| 291 | : Effect.logWarning("thread branch pull request lookup failed", { | |
| 292 | threadIds: group.map((thread) => thread.id), | |
| 293 | cause: Cause.pretty(cause), | |
| 294 | }).pipe(Effect.tap(() => Effect.sync(() => failBackfill(group)))), | |
| 288 | Effect.catchCauseIf( | |
| 289 | (cause) => !Cause.hasInterruptsOnly(cause), | |
| 290 | (cause) => | |
| 291 | Effect.logWarning("thread branch pull request lookup failed", { | |
| 292 | threadIds: group.map((thread) => thread.id), | |
| 293 | cause: Cause.pretty(cause), | |
| 294 | }).pipe(Effect.tap(() => Effect.sync(() => failBackfill(group)))), | |
| 295 | 295 | ), |
| 296 | 296 | ), |
| 297 | 297 | { concurrency: 8, discard: true }, |
| ... | ... | @@ -300,12 +300,12 @@ export const make = Effect.gen(function* () { |
| 300 | 300 | |
| 301 | 301 | const worker = yield* makeDrainableWorker((request: RefreshRequest) => |
| 302 | 302 | synchronize(request).pipe( |
| 303 | Effect.catchCause((cause) => | |
| 304 | Cause.hasInterruptsOnly(cause) | |
| 305 | ? Effect.failCause(cause) | |
| 306 | : Effect.logWarning("thread pull request refresh failed", { | |
| 307 | cause: Cause.pretty(cause), | |
| 308 | }), | |
| 303 | Effect.catchCauseIf( | |
| 304 | (cause) => !Cause.hasInterruptsOnly(cause), | |
| 305 | (cause) => | |
| 306 | Effect.logWarning("thread pull request refresh failed", { | |
| 307 | cause: Cause.pretty(cause), | |
| 308 | }), | |
| 309 | 309 | ), |
| 310 | 310 | ), |
| 311 | 311 | ); |
apps/server/src/orchestration/ThreadSettlementReactor.ts+20-20| ... | ... | @@ -137,13 +137,13 @@ export const make = Effect.gen(function* () { |
| 137 | 137 | }, |
| 138 | 138 | (effect, thread) => |
| 139 | 139 | effect.pipe( |
| 140 | Effect.catchCause((cause) => | |
| 141 | Cause.hasInterruptsOnly(cause) | |
| 142 | ? Effect.failCause(cause) | |
| 143 | : Effect.logWarning("automatic thread settlement skipped", { | |
| 144 | threadId: thread.id, | |
| 145 | cause: Cause.pretty(cause), | |
| 146 | }).pipe(Effect.as(null)), | |
| 140 | Effect.catchCauseIf( | |
| 141 | (cause) => !Cause.hasInterruptsOnly(cause), | |
| 142 | (cause) => | |
| 143 | Effect.logWarning("automatic thread settlement skipped", { | |
| 144 | threadId: thread.id, | |
| 145 | cause: Cause.pretty(cause), | |
| 146 | }).pipe(Effect.as(null)), | |
| 147 | 147 | ), |
| 148 | 148 | ), |
| 149 | 149 | ); |
| ... | ... | @@ -305,13 +305,13 @@ export const make = Effect.gen(function* () { |
| 305 | 305 | discard: true, |
| 306 | 306 | }); |
| 307 | 307 | }).pipe( |
| 308 | Effect.catchCause((cause) => | |
| 309 | Cause.hasInterruptsOnly(cause) | |
| 310 | ? Effect.failCause(cause) | |
| 311 | : Effect.logWarning("automatic thread settlement skipped", { | |
| 312 | threadIds: group.map((thread) => thread.id), | |
| 313 | cause: Cause.pretty(cause), | |
| 314 | }), | |
| 308 | Effect.catchCauseIf( | |
| 309 | (cause) => !Cause.hasInterruptsOnly(cause), | |
| 310 | (cause) => | |
| 311 | Effect.logWarning("automatic thread settlement skipped", { | |
| 312 | threadIds: group.map((thread) => thread.id), | |
| 313 | cause: Cause.pretty(cause), | |
| 314 | }), | |
| 315 | 315 | ), |
| 316 | 316 | ), |
| 317 | 317 | { concurrency: 8, discard: true }, |
| ... | ... | @@ -323,12 +323,12 @@ export const make = Effect.gen(function* () { |
| 323 | 323 | threadId?: ThreadId, |
| 324 | 324 | ) => |
| 325 | 325 | sweep(mergedPullRequest, threadId).pipe( |
| 326 | Effect.catchCause((cause) => | |
| 327 | Cause.hasInterruptsOnly(cause) | |
| 328 | ? Effect.failCause(cause) | |
| 329 | : Effect.logWarning("automatic thread settlement sweep failed", { | |
| 330 | cause: Cause.pretty(cause), | |
| 331 | }), | |
| 326 | Effect.catchCauseIf( | |
| 327 | (cause) => !Cause.hasInterruptsOnly(cause), | |
| 328 | (cause) => | |
| 329 | Effect.logWarning("automatic thread settlement sweep failed", { | |
| 330 | cause: Cause.pretty(cause), | |
| 331 | }), | |
| 332 | 332 | ), |
| 333 | 333 | ); |
| 334 | 334 | const worker = yield* makeDrainableWorker((threadId: ThreadId | undefined) => |
apps/server/src/orchestration/commandInvariants.ts+14-18| ... | ... | @@ -119,15 +119,13 @@ export function requireThreadArchived(input: { |
| 119 | 119 | readonly threadId: ThreadId; |
| 120 | 120 | }): Effect.Effect<OrchestrationThread, OrchestrationCommandInvariantError> { |
| 121 | 121 | return requireThread(input).pipe( |
| 122 | Effect.flatMap((thread) => | |
| 123 | thread.archivedAt !== null | |
| 124 | ? Effect.succeed(thread) | |
| 125 | : Effect.fail( | |
| 126 | invariantError( | |
| 127 | input.command.type, | |
| 128 | `Thread '${input.threadId}' is not archived for command '${input.command.type}'.`, | |
| 129 | ), | |
| 130 | ), | |
| 122 | Effect.filterOrFail( | |
| 123 | (thread) => thread.archivedAt !== null, | |
| 124 | () => | |
| 125 | invariantError( | |
| 126 | input.command.type, | |
| 127 | `Thread '${input.threadId}' is not archived for command '${input.command.type}'.`, | |
| 128 | ), | |
| 131 | 129 | ), |
| 132 | 130 | ); |
| 133 | 131 | } |
| ... | ... | @@ -138,15 +136,13 @@ export function requireThreadNotArchived(input: { |
| 138 | 136 | readonly threadId: ThreadId; |
| 139 | 137 | }): Effect.Effect<OrchestrationThread, OrchestrationCommandInvariantError> { |
| 140 | 138 | return requireThread(input).pipe( |
| 141 | Effect.flatMap((thread) => | |
| 142 | thread.archivedAt === null | |
| 143 | ? Effect.succeed(thread) | |
| 144 | : Effect.fail( | |
| 145 | invariantError( | |
| 146 | input.command.type, | |
| 147 | `Thread '${input.threadId}' is already archived and cannot handle command '${input.command.type}'.`, | |
| 148 | ), | |
| 149 | ), | |
| 139 | Effect.filterOrFail( | |
| 140 | (thread) => thread.archivedAt === null, | |
| 141 | () => | |
| 142 | invariantError( | |
| 143 | input.command.type, | |
| 144 | `Thread '${input.threadId}' is already archived and cannot handle command '${input.command.type}'.`, | |
| 145 | ), | |
| 150 | 146 | ), |
| 151 | 147 | ); |
| 152 | 148 | } |
apps/server/src/orchestration/decider.pullRequests.test.ts+1-1| ... | ... | @@ -295,7 +295,7 @@ it.layer(NodeServices.layer)("pull request link decider", (it) => { |
| 295 | 295 | for (const planned of events) { |
| 296 | 296 | const event = { ...planned, sequence: model.snapshotSequence + 1 }; |
| 297 | 297 | const encoded = yield* Schema.encodeEffect(OrchestrationEvent)(event); |
| 298 | const decoded = yield* Schema.decodeUnknownEffect(OrchestrationEvent)(encoded); | |
| 298 | const decoded = yield* Schema.decodeEffect(OrchestrationEvent)(encoded); | |
| 299 | 299 | // Older detail-event unions must never receive the new PR discriminants. |
| 300 | 300 | expect(isThreadDetailEvent(decoded)).toBe(false); |
| 301 | 301 | model = yield* projectEvent(model, decoded); |
apps/server/src/orchestration/decider.ts+28-42| ... | ... | @@ -488,12 +488,10 @@ export const decideOrchestrationCommand = Effect.fn("decideOrchestrationCommand" |
| 488 | 488 | command.type === "thread.auto-settle" && |
| 489 | 489 | (thread.settledOverride !== null || thread.autoSettleDisabledAt != null) |
| 490 | 490 | ) { |
| 491 | return yield* Effect.fail( | |
| 492 | new OrchestrationCommandInvariantError({ | |
| 493 | commandType: command.type, | |
| 494 | detail: `thread ${command.threadId} changed before automatic settlement`, | |
| 495 | }), | |
| 496 | ); | |
| 491 | return yield* new OrchestrationCommandInvariantError({ | |
| 492 | commandType: command.type, | |
| 493 | detail: `thread ${command.threadId} changed before automatic settlement`, | |
| 494 | }); | |
| 497 | 495 | } |
| 498 | 496 | // The server owns settle eligibility. A stale command must not settle |
| 499 | 497 | // a thread whose session is coming alive or working. |
| ... | ... | @@ -645,36 +643,30 @@ export const decideOrchestrationCommand = Effect.fn("decideOrchestrationCommand" |
| 645 | 643 | // structurally just a string): NaN fails every comparison, and an |
| 646 | 644 | // unparseable snoozedUntil must never persist. |
| 647 | 645 | if (!(Date.parse(command.snoozedUntil) > Date.parse(occurredAt))) { |
| 648 | return yield* Effect.fail( | |
| 649 | new OrchestrationCommandInvariantError({ | |
| 650 | commandType: command.type, | |
| 651 | detail: `thread ${command.threadId} snooze wake time ${command.snoozedUntil} is not in the future`, | |
| 652 | }), | |
| 653 | ); | |
| 646 | return yield* new OrchestrationCommandInvariantError({ | |
| 647 | commandType: command.type, | |
| 648 | detail: `thread ${command.threadId} snooze wake time ${command.snoozedUntil} is not in the future`, | |
| 649 | }); | |
| 654 | 650 | } |
| 655 | 651 | // Blocked-on-you work must not be snoozed away: a pending approval or |
| 656 | 652 | // user-input request is the agent waiting on the user, and hiding it |
| 657 | 653 | // defeats the request. (A running session IS snoozable — snooze only |
| 658 | 654 | // affects visibility, never the agent.) |
| 659 | 655 | if (openRequests(thread).size > 0) { |
| 660 | return yield* Effect.fail( | |
| 661 | new OrchestrationCommandInvariantError({ | |
| 662 | commandType: command.type, | |
| 663 | detail: `thread ${command.threadId} has a pending approval or user-input request and cannot be snoozed`, | |
| 664 | }), | |
| 665 | ); | |
| 656 | return yield* new OrchestrationCommandInvariantError({ | |
| 657 | commandType: command.type, | |
| 658 | detail: `thread ${command.threadId} has a pending approval or user-input request and cannot be snoozed`, | |
| 659 | }); | |
| 666 | 660 | } |
| 667 | 661 | // A queued turn start — a user message no turn has adopted yet — is |
| 668 | 662 | // invisible pending work: no session, no pending flags. Snoozing in |
| 669 | 663 | // that window would hide a just-requested turn exactly the way settle |
| 670 | 664 | // would. |
| 671 | 665 | if (hasQueuedTurnStartForThread(thread, occurredAt)) { |
| 672 | return yield* Effect.fail( | |
| 673 | new OrchestrationCommandInvariantError({ | |
| 674 | commandType: command.type, | |
| 675 | detail: `thread ${command.threadId} has a queued turn start and cannot be snoozed`, | |
| 676 | }), | |
| 677 | ); | |
| 666 | return yield* new OrchestrationCommandInvariantError({ | |
| 667 | commandType: command.type, | |
| 668 | detail: `thread ${command.threadId} has a queued turn start and cannot be snoozed`, | |
| 669 | }); | |
| 678 | 670 | } |
| 679 | 671 | // Re-snoozing an already-snoozed thread to the SAME wake time is a |
| 680 | 672 | // duplicate (double-click, raced clients): re-emit with the original |
| ... | ... | @@ -836,12 +828,10 @@ export const decideOrchestrationCommand = Effect.fn("decideOrchestrationCommand" |
| 836 | 828 | // (rather than silently pinning) keeps a raced reorder-after-unpin |
| 837 | 829 | // from resurrecting a pin the user just cleared. |
| 838 | 830 | if (thread.pinnedAt == null) { |
| 839 | return yield* Effect.fail( | |
| 840 | new OrchestrationCommandInvariantError({ | |
| 841 | commandType: command.type, | |
| 842 | detail: `thread ${command.threadId} is not pinned and cannot be reordered`, | |
| 843 | }), | |
| 844 | ); | |
| 831 | return yield* new OrchestrationCommandInvariantError({ | |
| 832 | commandType: command.type, | |
| 833 | detail: `thread ${command.threadId} is not pinned and cannot be reordered`, | |
| 834 | }); | |
| 845 | 835 | } |
| 846 | 836 | // Idempotent by re-emission (see thread.settle): a duplicate drop on |
| 847 | 837 | // the same slot keeps the existing updatedAt so it projects as a no-op. |
| ... | ... | @@ -953,12 +943,10 @@ export const decideOrchestrationCommand = Effect.fn("decideOrchestrationCommand" |
| 953 | 943 | if (command.linkedPullRequest != null) { |
| 954 | 944 | const { linkedPullRequest: linked, ...metadata } = command; |
| 955 | 945 | const project = readModel.projects.find((project) => project.id === thread.projectId); |
| 956 | let host = project?.repositoryIdentity?.canonicalKey.split("/")[0] ?? "unknown"; | |
| 957 | try { | |
| 958 | host = new URL(linked.url).hostname; | |
| 959 | } catch { | |
| 960 | // Historical clients can send links without a parseable URL. | |
| 961 | } | |
| 946 | // Historical clients can send links without a parseable URL. | |
| 947 | const host = URL.canParse(linked.url) | |
| 948 | ? new URL(linked.url).hostname | |
| 949 | : (project?.repositoryIdentity?.canonicalKey.split("/")[0] ?? "unknown"); | |
| 962 | 950 | const hasMetadata = Object.entries(metadata).some( |
| 963 | 951 | ([key, value]) => !["type", "commandId", "threadId"].includes(key) && value !== undefined, |
| 964 | 952 | ); |
| ... | ... | @@ -1872,12 +1860,10 @@ export const decideOrchestrationCommand = Effect.fn("decideOrchestrationCommand" |
| 1872 | 1860 | sessionComingAlive || |
| 1873 | 1861 | hasQueuedTurnStartForThread(thread, command.createdAt) |
| 1874 | 1862 | ) { |
| 1875 | return yield* Effect.fail( | |
| 1876 | new OrchestrationCommandInvariantError({ | |
| 1877 | commandType: command.type, | |
| 1878 | detail: `thread ${command.threadId} was re-engaged after settle; skipping session stop`, | |
| 1879 | }), | |
| 1880 | ); | |
| 1863 | return yield* new OrchestrationCommandInvariantError({ | |
| 1864 | commandType: command.type, | |
| 1865 | detail: `thread ${command.threadId} was re-engaged after settle; skipping session stop`, | |
| 1866 | }); | |
| 1881 | 1867 | } |
| 1882 | 1868 | } |
| 1883 | 1869 | return { |
apps/server/src/orchestration/workflowScriptQuery.ts+12-9| ... | ... | @@ -32,9 +32,10 @@ export const readWorkflowScript = Effect.fn("orchestration.readWorkflowScript")( |
| 32 | 32 | const requested = input.scriptPath; |
| 33 | 33 | |
| 34 | 34 | if (!NodePath.isAbsolute(requested) || NodePath.extname(requested) !== ".js") { |
| 35 | return yield* Effect.fail( | |
| 36 | new OrchestrationGetWorkflowScriptError({ reason: "invalid-path", scriptPath: requested }), | |
| 37 | ); | |
| 35 | return yield* new OrchestrationGetWorkflowScriptError({ | |
| 36 | reason: "invalid-path", | |
| 37 | scriptPath: requested, | |
| 38 | }); | |
| 38 | 39 | } |
| 39 | 40 | |
| 40 | 41 | const root = yield* Effect.tryPromise({ |
| ... | ... | @@ -60,14 +61,16 @@ export const readWorkflowScript = Effect.fn("orchestration.readWorkflowScript")( |
| 60 | 61 | }); |
| 61 | 62 | |
| 62 | 63 | if (resolved !== root && !resolved.startsWith(`${root}${NodePath.sep}`)) { |
| 63 | return yield* Effect.fail( | |
| 64 | new OrchestrationGetWorkflowScriptError({ reason: "outside-root", scriptPath: resolved }), | |
| 65 | ); | |
| 64 | return yield* new OrchestrationGetWorkflowScriptError({ | |
| 65 | reason: "outside-root", | |
| 66 | scriptPath: resolved, | |
| 67 | }); | |
| 66 | 68 | } |
| 67 | 69 | if (NodePath.extname(resolved) !== ".js") { |
| 68 | return yield* Effect.fail( | |
| 69 | new OrchestrationGetWorkflowScriptError({ reason: "not-js", scriptPath: resolved }), | |
| 70 | ); | |
| 70 | return yield* new OrchestrationGetWorkflowScriptError({ | |
| 71 | reason: "not-js", | |
| 72 | scriptPath: resolved, | |
| 73 | }); | |
| 71 | 74 | } |
| 72 | 75 | |
| 73 | 76 | // TOCTOU-safe read (review finding): open FIRST, then verify what was |
apps/server/src/persistence/AuthPairingLinks.ts+4-4| ... | ... | @@ -268,7 +268,7 @@ export const make = Effect.gen(function* () { |
| 268 | 268 | ), |
| 269 | 269 | Effect.flatMap((rowOption) => |
| 270 | 270 | Option.match(rowOption, { |
| 271 | onNone: () => Effect.succeed(Option.none()), | |
| 271 | onNone: () => Effect.succeedNone, | |
| 272 | 272 | onSome: (row) => |
| 273 | 273 | decodeAuthPairingLinkDbRow(row).pipe( |
| 274 | 274 | Effect.mapError((cause) => |
| ... | ... | @@ -278,7 +278,7 @@ export const make = Effect.gen(function* () { |
| 278 | 278 | { pairingLinkId: row.id }, |
| 279 | 279 | ), |
| 280 | 280 | ), |
| 281 | Effect.map(Option.some), | |
| 281 | Effect.asSome, | |
| 282 | 282 | ), |
| 283 | 283 | }), |
| 284 | 284 | ), |
| ... | ... | @@ -329,7 +329,7 @@ export const make = Effect.gen(function* () { |
| 329 | 329 | ), |
| 330 | 330 | Effect.flatMap((rowOption) => |
| 331 | 331 | Option.match(rowOption, { |
| 332 | onNone: () => Effect.succeed(Option.none()), | |
| 332 | onNone: () => Effect.succeedNone, | |
| 333 | 333 | onSome: (row) => |
| 334 | 334 | decodeAuthPairingLinkDbRow(row).pipe( |
| 335 | 335 | Effect.mapError((cause) => |
| ... | ... | @@ -339,7 +339,7 @@ export const make = Effect.gen(function* () { |
| 339 | 339 | { pairingLinkId: row.id }, |
| 340 | 340 | ), |
| 341 | 341 | ), |
| 342 | Effect.map(Option.some), | |
| 342 | Effect.asSome, | |
| 343 | 343 | ), |
| 344 | 344 | }), |
| 345 | 345 | ), |
apps/server/src/persistence/AuthSessions.ts+1-1| ... | ... | @@ -422,7 +422,7 @@ export const make = Effect.gen(function* () { |
| 422 | 422 | ), |
| 423 | 423 | Effect.flatMap((rowOption) => |
| 424 | 424 | Option.match(rowOption, { |
| 425 | onNone: () => Effect.succeed(Option.none()), | |
| 425 | onNone: () => Effect.succeedNone, | |
| 426 | 426 | onSome: (row) => |
| 427 | 427 | decodeAuthSessionDbRow(row).pipe( |
| 428 | 428 | Effect.mapError((cause) => |
apps/server/src/persistence/Layers/ProjectionTurns.ts+2-3| ... | ... | @@ -317,9 +317,8 @@ const makeProjectionTurnRepository = Effect.gen(function* () { |
| 317 | 317 | ), |
| 318 | 318 | Effect.flatMap((rowOption) => |
| 319 | 319 | Option.match(rowOption, { |
| 320 | onNone: () => Effect.succeed(Option.none()), | |
| 321 | onSome: (row) => | |
| 322 | Effect.succeed(Option.some(row as Schema.Schema.Type<typeof ProjectionTurnById>)), | |
| 320 | onNone: () => Effect.succeedNone, | |
| 321 | onSome: (row) => Effect.succeedSome(row as Schema.Schema.Type<typeof ProjectionTurnById>), | |
| 323 | 322 | }), |
| 324 | 323 | ), |
| 325 | 324 | ); |
apps/server/src/persistence/ProviderSessionRuntime.ts+3-3| ... | ... | @@ -398,7 +398,7 @@ export const make = Effect.gen(function* () { |
| 398 | 398 | ), |
| 399 | 399 | Effect.flatMap((runtimeRowOption) => |
| 400 | 400 | Option.match(runtimeRowOption, { |
| 401 | onNone: () => Effect.succeed(Option.none()), | |
| 401 | onNone: () => Effect.succeedNone, | |
| 402 | 402 | onSome: (row) => |
| 403 | 403 | decodeRuntimeRow(row).pipe( |
| 404 | 404 | Effect.mapError((cause) => |
| ... | ... | @@ -408,7 +408,7 @@ export const make = Effect.gen(function* () { |
| 408 | 408 | { threadId: input.threadId }, |
| 409 | 409 | ), |
| 410 | 410 | ), |
| 411 | Effect.map((runtime) => Option.some(runtime)), | |
| 411 | Effect.asSome, | |
| 412 | 412 | ), |
| 413 | 413 | }), |
| 414 | 414 | ), |
| ... | ... | @@ -428,7 +428,7 @@ export const make = Effect.gen(function* () { |
| 428 | 428 | // every consumer that enumerates sessions, such as the reaper. |
| 429 | 429 | Effect.forEach(rows, (row) => |
| 430 | 430 | decodeRuntimeRow(row).pipe( |
| 431 | Effect.map(Option.some), | |
| 431 | Effect.asSome, | |
| 432 | 432 | Effect.catch((cause) => |
| 433 | 433 | Effect.logWarning("provider.session.runtime.row-skipped", { |
| 434 | 434 | threadId: row.threadId, |
apps/server/src/project/AgentSessionImporter.test.ts+3-3| ... | ... | @@ -232,7 +232,7 @@ it.layer(NodeServices.layer)("AgentSessionImporter", (it) => { |
| 232 | 232 | upsert: (binding) => Effect.sync(() => void bindings.push(binding)), |
| 233 | 233 | getProvider: () => Effect.die("unused"), |
| 234 | 234 | recordImportedTranscript: () => Effect.void, |
| 235 | getBinding: () => Effect.succeed(Option.none()), | |
| 235 | getBinding: () => Effect.succeedNone, | |
| 236 | 236 | listThreadIds: () => Effect.die("unused"), |
| 237 | 237 | listBindings: () => Effect.die("unused"), |
| 238 | 238 | }); |
| ... | ... | @@ -456,7 +456,7 @@ it.layer(NodeServices.layer)("AgentSessionImporter", (it) => { |
| 456 | 456 | upsert: () => Effect.die("must not replace an active binding"), |
| 457 | 457 | getProvider: () => Effect.die("unused"), |
| 458 | 458 | recordImportedTranscript: () => Effect.void, |
| 459 | getBinding: () => Effect.succeed(Option.some(runningBinding)), | |
| 459 | getBinding: () => Effect.succeedSome(runningBinding), | |
| 460 | 460 | listThreadIds: () => Effect.die("unused"), |
| 461 | 461 | listBindings: () => Effect.die("unused"), |
| 462 | 462 | }); |
| ... | ... | @@ -511,7 +511,7 @@ it.layer(NodeServices.layer)("AgentSessionImporter", (it) => { |
| 511 | 511 | upsert: () => Effect.die("must not bind malformed or wrong-project sessions"), |
| 512 | 512 | getProvider: () => Effect.die("unused"), |
| 513 | 513 | recordImportedTranscript: () => Effect.die("unused"), |
| 514 | getBinding: () => Effect.succeed(Option.none()), | |
| 514 | getBinding: () => Effect.succeedNone, | |
| 515 | 515 | listThreadIds: () => Effect.die("unused"), |
| 516 | 516 | listBindings: () => Effect.die("unused"), |
| 517 | 517 | }); |
apps/server/src/project/AgentSessionScanner.ts+1-1| ... | ... | @@ -662,7 +662,7 @@ export const make = Effect.gen(function* () { |
| 662 | 662 | fileSystem.readDirectory(directory).pipe(Effect.orElseSucceed((): ReadonlyArray<string> => [])); |
| 663 | 663 | |
| 664 | 664 | const statOption = (target: string) => |
| 665 | fileSystem.stat(target).pipe(Effect.map(Option.some), Effect.orElseSucceed(Option.none)); | |
| 665 | fileSystem.stat(target).pipe(Effect.asSome, Effect.orElseSucceed(Option.none)); | |
| 666 | 666 | |
| 667 | 667 | /** Match directory aliases without assuming the host volume is case-insensitive. */ |
| 668 | 668 | const directoryIdentity = Effect.fn("AgentSessionScanner.directoryIdentity")(function* ( |
apps/server/src/project/ProjectFaviconResolver.ts+2-2| ... | ... | @@ -143,7 +143,7 @@ const optionOnNotFound = <A, R>( |
| 143 | 143 | effect: Effect.Effect<A, PlatformError.PlatformError, R>, |
| 144 | 144 | ): Effect.Effect<Option.Option<A>, PlatformError.PlatformError, R> => |
| 145 | 145 | effect.pipe( |
| 146 | Effect.map(Option.some), | |
| 146 | Effect.asSome, | |
| 147 | 147 | Effect.catchTags({ |
| 148 | 148 | PlatformError: (error) => |
| 149 | 149 | error.reason._tag === "NotFound" ? Effect.succeed(Option.none<A>()) : Effect.fail(error), |
| ... | ... | @@ -175,7 +175,7 @@ export const make = Effect.gen(function* () { |
| 175 | 175 | relativePath, |
| 176 | 176 | }) |
| 177 | 177 | ).pipe( |
| 178 | Effect.map(Option.some), | |
| 178 | Effect.asSome, | |
| 179 | 179 | Effect.catchTags({ |
| 180 | 180 | WorkspacePathOutsideRootError: () => |
| 181 | 181 | Effect.succeed( |
apps/server/src/project/RepositoryIdentityResolver.ts+4-4| ... | ... | @@ -141,6 +141,7 @@ export const make = Effect.fn("RepositoryIdentityResolver.make")(function* ( |
| 141 | 141 | ) { |
| 142 | 142 | const processRunner = yield* ProcessRunner.ProcessRunner; |
| 143 | 143 | const cacheCapacity = options.cacheCapacity ?? DEFAULT_REPOSITORY_IDENTITY_CACHE_CAPACITY; |
| 144 | const refine = options.refine ?? Effect.succeed; | |
| 144 | 145 | |
| 145 | 146 | const repositoryRootCache = yield* Cache.makeWith<string, string | null>( |
| 146 | 147 | (cwd) => |
| ... | ... | @@ -161,10 +162,9 @@ export const make = Effect.fn("RepositoryIdentityResolver.make")(function* ( |
| 161 | 162 | (cacheKey) => |
| 162 | 163 | resolveRepositoryIdentityFromCacheKey(cacheKey).pipe( |
| 163 | 164 | Effect.provideService(ProcessRunner.ProcessRunner, processRunner), |
| 164 | Effect.flatMap((identity) => | |
| 165 | identity !== null && options.refine | |
| 166 | ? options.refine(identity).pipe(Effect.catch(() => Effect.succeed(identity))) | |
| 167 | : Effect.succeed(identity), | |
| 165 | Effect.filterOrElse( | |
| 166 | (identity): identity is null => identity === null, | |
| 167 | (identity) => refine(identity).pipe(Effect.orElseSucceed(() => identity)), | |
| 168 | 168 | ), |
| 169 | 169 | ), |
| 170 | 170 | { |
apps/server/src/project/T3ProjectFileLoader.ts+2-2| ... | ... | @@ -68,7 +68,7 @@ export const make = Effect.gen(function* () { |
| 68 | 68 | function* (workspaceRoot) { |
| 69 | 69 | const filePath = path.join(workspaceRoot, T3_PROJECT_FILE_NAME); |
| 70 | 70 | const raw = yield* fileSystem.readFileString(filePath).pipe( |
| 71 | Effect.map(Option.some), | |
| 71 | Effect.asSome, | |
| 72 | 72 | Effect.catchTags({ |
| 73 | 73 | PlatformError: (error) => |
| 74 | 74 | error.reason._tag === "NotFound" |
| ... | ... | @@ -87,7 +87,7 @@ export const make = Effect.gen(function* () { |
| 87 | 87 | return Option.none<T3ProjectFile>(); |
| 88 | 88 | } |
| 89 | 89 | return yield* decodeT3ProjectFileJson(raw.value).pipe( |
| 90 | Effect.map(Option.some), | |
| 90 | Effect.asSome, | |
| 91 | 91 | Effect.catchTags({ |
| 92 | 92 | SchemaError: (error) => |
| 93 | 93 | logT3ProjectFileLoadError( |
apps/server/src/provider/AntigravityInstallation.ts+1-1| ... | ... | @@ -315,7 +315,7 @@ export const makeAntigravityInstallation = Effect.fn("AntigravityInstallation.ma |
| 315 | 315 | ); |
| 316 | 316 | } |
| 317 | 317 | const contents = yield* fs.readFileString(filePath); |
| 318 | return yield* Schema.decodeUnknownEffect(Schema.fromJsonString(schema))(contents); | |
| 318 | return yield* Schema.decodeEffect(Schema.fromJsonString(schema))(contents); | |
| 319 | 319 | }); |
| 320 | 320 | |
| 321 | 321 | const executableFile = Effect.fn("AntigravityInstallation.executableFile")(function* ( |
apps/server/src/provider/Drivers/AntigravitySkills.ts+1-1| ... | ... | @@ -86,7 +86,7 @@ const readIfPresent = <A, R>( |
| 86 | 86 | Effect.catchTags({ |
| 87 | 87 | PlatformError: (cause) => |
| 88 | 88 | cause.reason._tag === "NotFound" |
| 89 | ? Effect.succeed(undefined) | |
| 89 | ? Effect.undefined | |
| 90 | 90 | : Effect.fail( |
| 91 | 91 | new AntigravitySkillsProbeError({ reason: "filesystem-error", path, cause }), |
| 92 | 92 | ), |
apps/server/src/provider/Drivers/CursorDriver.ts+11-6| ... | ... | @@ -140,12 +140,17 @@ export const CursorDriver: ProviderDriver<CursorSettings, CursorDriverEnv> = { |
| 140 | 140 | processEnv, |
| 141 | 141 | modelDiscovery.discover, |
| 142 | 142 | ).pipe( |
| 143 | Effect.flatMap((snapshot) => | |
| 144 | effectiveConfig.enabled && snapshot.installed && snapshot.auth.status === "authenticated" | |
| 145 | ? readCursorUsageLimits(effectiveConfig, processEnv).pipe( | |
| 146 | Effect.map((usageLimits) => ({ ...snapshot, usageLimits })), | |
| 147 | ) | |
| 148 | : Effect.succeed(snapshot), | |
| 143 | Effect.filterOrElse( | |
| 144 | (snapshot) => | |
| 145 | !( | |
| 146 | effectiveConfig.enabled && | |
| 147 | snapshot.installed && | |
| 148 | snapshot.auth.status === "authenticated" | |
| 149 | ), | |
| 150 | (snapshot) => | |
| 151 | readCursorUsageLimits(effectiveConfig, processEnv).pipe( | |
| 152 | Effect.map((usageLimits) => ({ ...snapshot, usageLimits })), | |
| 153 | ), | |
| 149 | 154 | ), |
| 150 | 155 | Effect.map(stampIdentity), |
| 151 | 156 | Effect.provideService(HttpClient.HttpClient, httpClient), |
apps/server/src/provider/Drivers/GrokDriver.ts+16-11| ... | ... | @@ -131,17 +131,22 @@ export const GrokDriver: ProviderDriver<GrokSettings, GrokDriverEnv> = { |
| 131 | 131 | const textGeneration = yield* makeGrokTextGeneration(effectiveConfig, processEnv); |
| 132 | 132 | |
| 133 | 133 | const checkProvider = checkGrokProviderStatus(effectiveConfig, processEnv, cwd).pipe( |
| 134 | Effect.flatMap((snapshot) => | |
| 135 | effectiveConfig.enabled && snapshot.installed && snapshot.auth.status === "authenticated" | |
| 136 | ? readGrokAccount(processEnv).pipe( | |
| 137 | // The email lets clients recognize one account signed in on several environments. | |
| 138 | Effect.map(({ email, usageLimits }) => ({ | |
| 139 | ...snapshot, | |
| 140 | auth: email ? { ...snapshot.auth, email } : snapshot.auth, | |
| 141 | usageLimits, | |
| 142 | })), | |
| 143 | ) | |
| 144 | : Effect.succeed(snapshot), | |
| 134 | Effect.filterOrElse( | |
| 135 | (snapshot) => | |
| 136 | !( | |
| 137 | effectiveConfig.enabled && | |
| 138 | snapshot.installed && | |
| 139 | snapshot.auth.status === "authenticated" | |
| 140 | ), | |
| 141 | (snapshot) => | |
| 142 | readGrokAccount(processEnv).pipe( | |
| 143 | // The email lets clients recognize one account signed in on several environments. | |
| 144 | Effect.map(({ email, usageLimits }) => ({ | |
| 145 | ...snapshot, | |
| 146 | auth: email ? { ...snapshot.auth, email } : snapshot.auth, | |
| 147 | usageLimits, | |
| 148 | })), | |
| 149 | ), | |
| 145 | 150 | ), |
| 146 | 151 | Effect.map(stampIdentity), |
| 147 | 152 | Effect.provideService(HttpClient.HttpClient, httpClient), |
apps/server/src/provider/Layers/AntigravityAdapter.ts+2-2| ... | ... | @@ -844,7 +844,7 @@ export const makeAntigravityAdapter = Effect.fn("makeAntigravityAdapter")(functi |
| 844 | 844 | const model = yield* applyAntigravityAcpModelSelection({ |
| 845 | 845 | runtime, |
| 846 | 846 | model: input.modelSelection?.model, |
| 847 | defaultModel: yield* options.defaultModel ?? Effect.succeed(undefined), | |
| 847 | defaultModel: yield* options.defaultModel ?? Effect.undefined, | |
| 848 | 848 | mapError: (cause) => cause, |
| 849 | 849 | }); |
| 850 | 850 | yield* runtime.setMode(antigravityPermissionMode(input.runtimeMode)); |
| ... | ... | @@ -1035,7 +1035,7 @@ export const makeAntigravityAdapter = Effect.fn("makeAntigravityAdapter")(functi |
| 1035 | 1035 | const model = resolveAntigravityModel({ |
| 1036 | 1036 | configOptions, |
| 1037 | 1037 | model: requestedModel, |
| 1038 | defaultModel: yield* options.defaultModel ?? Effect.succeed(undefined), | |
| 1038 | defaultModel: yield* options.defaultModel ?? Effect.undefined, | |
| 1039 | 1039 | }); |
| 1040 | 1040 | const availableModels = antigravityModelOptions(configOptions); |
| 1041 | 1041 | if (model && !availableModels.some((option) => option.value === model)) { |
apps/server/src/provider/Layers/ClaudeAdapter.test.ts+1| ... | ... | @@ -1,3 +1,4 @@ |
| 1 | // @effect-diagnostics abortControllerInEffect:off - Tests hand-built AbortSignals to the SDK query stub to exercise cancellation. | |
| 1 | 2 | import { buildRuntimeInstructions } from "../RuntimeInstructions.ts"; |
| 2 | 3 | // @effect-diagnostics nodeBuiltinImport:off |
| 3 | 4 | import * as NodeFS from "node:fs"; |
apps/server/src/provider/Layers/ClaudeAdapter.ts+2-1| ... | ... | @@ -5357,6 +5357,7 @@ export const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* ( |
| 5357 | 5357 | ) => { |
| 5358 | 5358 | // SDK history helpers read process.env. Isolate the provider's home instead |
| 5359 | 5359 | // of changing the server's environment while other providers are running. |
| 5360 | // @effect-diagnostics-next-line runEffectInsideEffect:off - SDK callback runs outside the fiber; the spawn is self-contained | |
| 5360 | 5361 | const result = await Effect.runPromise( |
| 5361 | 5362 | spawnAndCollect( |
| 5362 | 5363 | process.execPath, |
| ... | ... | @@ -5547,7 +5548,7 @@ export const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* ( |
| 5547 | 5548 | |
| 5548 | 5549 | for (const result of results) { |
| 5549 | 5550 | if (result._tag === "Failure") { |
| 5550 | return yield* Effect.fail(result.failure); | |
| 5551 | return yield* result.failure; | |
| 5551 | 5552 | } |
| 5552 | 5553 | } |
| 5553 | 5554 | }); |
apps/server/src/provider/Layers/CodexAdapter.test.ts+1-1| ... | ... | @@ -225,7 +225,7 @@ const providerSessionDirectoryTestLayer = Layer.succeed(ProviderSessionDirectory |
| 225 | 225 | recordImportedTranscript: () => Effect.die("unused"), |
| 226 | 226 | getProvider: () => |
| 227 | 227 | Effect.die(new Error("ProviderSessionDirectory.getProvider is not used in test")), |
| 228 | getBinding: () => Effect.succeed(Option.none()), | |
| 228 | getBinding: () => Effect.succeedNone, | |
| 229 | 229 | listThreadIds: () => Effect.succeed([]), |
| 230 | 230 | listBindings: () => Effect.succeed([]), |
| 231 | 231 | }); |
apps/server/src/provider/Layers/CodexSessionRuntime.ts+1-1| ... | ... | @@ -1518,7 +1518,7 @@ export const makeCodexSessionRuntime = ( |
| 1518 | 1518 | } |
| 1519 | 1519 | }), |
| 1520 | 1520 | ), |
| 1521 | Effect.catch(() => Effect.void), | |
| 1521 | Effect.ignore, | |
| 1522 | 1522 | Effect.forkIn(runtimeScope), |
| 1523 | 1523 | ); |
| 1524 | 1524 | }); |
apps/server/src/provider/Layers/GrokAdapter.ts+37-38| ... | ... | @@ -776,46 +776,45 @@ export function makeGrokAdapter(grokSettings: GrokSettings, options?: GrokAdapte |
| 776 | 776 | ); |
| 777 | 777 | }); |
| 778 | 778 | |
| 779 | const runTurnLivenessWatchdog = Effect.fn("GrokAdapter.runTurnLivenessWatchdog")( | |
| 780 | function* (ctx: GrokSessionContext) { | |
| 781 | while (true) { | |
| 782 | if (ctx.stopped) { | |
| 783 | return; | |
| 784 | } | |
| 785 | const turnId = ctx.livenessTurnId; | |
| 786 | if ( | |
| 787 | turnId === undefined || | |
| 788 | ctx.interruptedTurnIds.has(turnId) || | |
| 789 | !isLiveTurn(ctx, turnId) || | |
| 790 | hasLivenessPause(ctx) | |
| 791 | ) { | |
| 792 | yield* Queue.take(ctx.livenessSignals); | |
| 793 | continue; | |
| 794 | } | |
| 779 | const runTurnLivenessWatchdog = Effect.fn("GrokAdapter.runTurnLivenessWatchdog")(function* ( | |
| 780 | ctx: GrokSessionContext, | |
| 781 | ) { | |
| 782 | while (true) { | |
| 783 | if (ctx.stopped) { | |
| 784 | return; | |
| 785 | } | |
| 786 | const turnId = ctx.livenessTurnId; | |
| 787 | if ( | |
| 788 | turnId === undefined || | |
| 789 | ctx.interruptedTurnIds.has(turnId) || | |
| 790 | !isLiveTurn(ctx, turnId) || | |
| 791 | hasLivenessPause(ctx) | |
| 792 | ) { | |
| 793 | yield* Queue.take(ctx.livenessSignals); | |
| 794 | continue; | |
| 795 | } | |
| 795 | 796 | |
| 796 | const lastActivityAtNanos = ctx.lastTurnActivityAtNanos; | |
| 797 | if (lastActivityAtNanos === undefined) { | |
| 798 | yield* Queue.take(ctx.livenessSignals); | |
| 799 | continue; | |
| 800 | } | |
| 801 | const nowNanos = yield* Clock.monotonicTimeNanos; | |
| 802 | const remainingNanos = livenessTimeoutFor(ctx).nanos - (nowNanos - lastActivityAtNanos); | |
| 803 | if (remainingNanos <= 0n) { | |
| 804 | yield* settleStalledTurn(ctx, turnId); | |
| 805 | continue; | |
| 806 | } | |
| 797 | const lastActivityAtNanos = ctx.lastTurnActivityAtNanos; | |
| 798 | if (lastActivityAtNanos === undefined) { | |
| 799 | yield* Queue.take(ctx.livenessSignals); | |
| 800 | continue; | |
| 801 | } | |
| 802 | const nowNanos = yield* Clock.monotonicTimeNanos; | |
| 803 | const remainingNanos = livenessTimeoutFor(ctx).nanos - (nowNanos - lastActivityAtNanos); | |
| 804 | if (remainingNanos <= 0n) { | |
| 805 | yield* settleStalledTurn(ctx, turnId); | |
| 806 | continue; | |
| 807 | } | |
| 807 | 808 | |
| 808 | const wakeReason = yield* Effect.raceFirst( | |
| 809 | Effect.sleep(Duration.nanos(remainingNanos)).pipe(Effect.as("timeout" as const)), | |
| 810 | Queue.take(ctx.livenessSignals).pipe(Effect.as("activity" as const)), | |
| 811 | ); | |
| 812 | if (wakeReason === "timeout") { | |
| 813 | yield* settleStalledTurn(ctx, turnId); | |
| 814 | } | |
| 809 | const wakeReason = yield* Effect.raceFirst( | |
| 810 | Effect.sleep(Duration.nanos(remainingNanos)).pipe(Effect.as("timeout" as const)), | |
| 811 | Queue.take(ctx.livenessSignals).pipe(Effect.as("activity" as const)), | |
| 812 | ); | |
| 813 | if (wakeReason === "timeout") { | |
| 814 | yield* settleStalledTurn(ctx, turnId); | |
| 815 | 815 | } |
| 816 | }, | |
| 817 | Effect.catch(() => Effect.void), | |
| 818 | ); | |
| 816 | } | |
| 817 | }, Effect.ignore()); | |
| 819 | 818 | |
| 820 | 819 | const logNative = (threadId: ThreadId, method: string, payload: unknown) => |
| 821 | 820 | Effect.gen(function* () { |
| ... | ... | @@ -2008,7 +2007,7 @@ export function makeGrokAdapter(grokSettings: GrokSettings, options?: GrokAdapte |
| 2008 | 2007 | errorMessage: errorMessage ?? "Grok prompt request failed.", |
| 2009 | 2008 | }), |
| 2010 | 2009 | ); |
| 2011 | }).pipe(Effect.catch(() => Effect.void)), | |
| 2010 | }).pipe(Effect.ignore), | |
| 2012 | 2011 | ), |
| 2013 | 2012 | ); |
| 2014 | 2013 | }); |
apps/server/src/provider/Layers/OpenCodeAdapter.test.ts+1-1| ... | ... | @@ -578,7 +578,7 @@ const providerSessionDirectoryTestLayer = Layer.succeed(ProviderSessionDirectory |
| 578 | 578 | recordImportedTranscript: () => Effect.die("unused"), |
| 579 | 579 | getProvider: () => |
| 580 | 580 | Effect.die(new Error("ProviderSessionDirectory.getProvider is not used in test")), |
| 581 | getBinding: () => Effect.succeed(Option.none()), | |
| 581 | getBinding: () => Effect.succeedNone, | |
| 582 | 582 | listThreadIds: () => Effect.succeed([]), |
| 583 | 583 | listBindings: () => Effect.succeed([]), |
| 584 | 584 | }); |
apps/server/src/provider/Layers/OpenCodeAdapter.ts+8-7| ... | ... | @@ -822,7 +822,7 @@ const abortOpenCodeDescendants = Effect.fn("abortOpenCodeDescendants")(function* |
| 822 | 822 | .pipe( |
| 823 | 823 | Effect.catchIf( |
| 824 | 824 | (cause) => isOpenCodeNotFound(cause), |
| 825 | () => Effect.void, | |
| 825 | () => Effect.undefined, | |
| 826 | 826 | ), |
| 827 | 827 | Effect.result, |
| 828 | 828 | ); |
| ... | ... | @@ -1097,7 +1097,7 @@ export function makeOpenCodeAdapter( |
| 1097 | 1097 | readonly observedAt: string; |
| 1098 | 1098 | readonly event: Record<string, unknown>; |
| 1099 | 1099 | }, |
| 1100 | ) => writeNativeEvent(threadId, event).pipe(Effect.catchCause(() => Effect.void)); | |
| 1100 | ) => writeNativeEvent(threadId, event).pipe(Effect.ignoreCause); | |
| 1101 | 1101 | |
| 1102 | 1102 | const cancelIdleReconciliation = Effect.fn("cancelIdleReconciliation")(function* ( |
| 1103 | 1103 | context: OpenCodeSessionContext, |
| ... | ... | @@ -1262,7 +1262,7 @@ export function makeOpenCodeAdapter( |
| 1262 | 1262 | yield* Effect.sleep(`${delayMs} millis`); |
| 1263 | 1263 | } |
| 1264 | 1264 | }).pipe( |
| 1265 | Effect.catchCause(() => Effect.void), | |
| 1265 | Effect.ignoreCause, | |
| 1266 | 1266 | Effect.ensuring( |
| 1267 | 1267 | Effect.sync(() => { |
| 1268 | 1268 | if (context.pendingIdleReconciliation === pending) { |
| ... | ... | @@ -1492,7 +1492,7 @@ export function makeOpenCodeAdapter( |
| 1492 | 1492 | } |
| 1493 | 1493 | yield* failPromptAdmissionRecovery(context, promptAdmission); |
| 1494 | 1494 | }).pipe( |
| 1495 | Effect.catchCause(() => Effect.void), | |
| 1495 | Effect.ignoreCause, | |
| 1496 | 1496 | Effect.ensuring( |
| 1497 | 1497 | Effect.sync(() => { |
| 1498 | 1498 | delete promptAdmission.recoveryFiber; |
| ... | ... | @@ -1699,7 +1699,7 @@ export function makeOpenCodeAdapter( |
| 1699 | 1699 | }), |
| 1700 | 1700 | Effect.catchIf( |
| 1701 | 1701 | (cause) => isOpenCodeNotFound(cause), |
| 1702 | () => Effect.succeed(undefined), | |
| 1702 | () => Effect.undefined, | |
| 1703 | 1703 | ), |
| 1704 | 1704 | ); |
| 1705 | 1705 | let sessionId: string | undefined = candidateSessionId; |
| ... | ... | @@ -2044,7 +2044,7 @@ export function makeOpenCodeAdapter( |
| 2044 | 2044 | yield* Effect.sleep(`${delayMs} millis`); |
| 2045 | 2045 | } |
| 2046 | 2046 | }).pipe( |
| 2047 | Effect.catchCause(() => Effect.void), | |
| 2047 | Effect.ignoreCause, | |
| 2048 | 2048 | Effect.ensuring( |
| 2049 | 2049 | Effect.sync(() => { |
| 2050 | 2050 | if (context.requestRelationRetries.get(requestId) === retry) { |
| ... | ... | @@ -2163,7 +2163,7 @@ export function makeOpenCodeAdapter( |
| 2163 | 2163 | return; |
| 2164 | 2164 | } |
| 2165 | 2165 | }).pipe( |
| 2166 | Effect.catchCause(() => Effect.void), | |
| 2166 | Effect.ignoreCause, | |
| 2167 | 2167 | Effect.ensuring( |
| 2168 | 2168 | Effect.sync(() => { |
| 2169 | 2169 | if (context.pendingRequestRecovery === recovery) { |
| ... | ... | @@ -2730,6 +2730,7 @@ export function makeOpenCodeAdapter( |
| 2730 | 2730 | // the scope closes (explicit stop, unexpected exit, or layer |
| 2731 | 2731 | // shutdown) and cancels the in-flight `event.subscribe` fetch so |
| 2732 | 2732 | // the async iterable unwinds cleanly. |
| 2733 | // @effect-diagnostics-next-line abortControllerInEffect:off - aborted by a scope finalizer to cancel the SDK's event.subscribe fetch | |
| 2733 | 2734 | const eventsAbortController = new AbortController(); |
| 2734 | 2735 | let lastStreamError: unknown; |
| 2735 | 2736 | let warnedAboutDisconnect = false; |
apps/server/src/provider/Layers/ProviderService.test.ts+5-7| ... | ... | @@ -4894,13 +4894,11 @@ const listThreadIds = vi.fn(() => |
| 4894 | 4894 | Effect.succeed([activeSessionThreadId, historicalSessionThreadId]), |
| 4895 | 4895 | ); |
| 4896 | 4896 | const getBinding = vi.fn((threadId: ThreadId) => |
| 4897 | Effect.succeed( | |
| 4898 | Option.some({ | |
| 4899 | threadId, | |
| 4900 | provider: CODEX_DRIVER, | |
| 4901 | providerInstanceId: codexInstanceId, | |
| 4902 | }), | |
| 4903 | ), | |
| 4897 | Effect.succeedSome({ | |
| 4898 | threadId, | |
| 4899 | provider: CODEX_DRIVER, | |
| 4900 | providerInstanceId: codexInstanceId, | |
| 4901 | }), | |
| 4904 | 4902 | ); |
| 4905 | 4903 | const boundedListing = makeProviderServiceLayer({ |
| 4906 | 4904 | directory: { |
apps/server/src/provider/Layers/ProviderService.ts+1-1| ... | ... | @@ -2298,7 +2298,7 @@ const makeProviderService = Effect.fn("makeProviderService")(function* ( |
| 2298 | 2298 | // Continuation is project-scopable, so decide it per session's project; |
| 2299 | 2299 | // without orchestration the environment value is all there is. |
| 2300 | 2300 | const stopSettings = yield* serverSettings.getSettings.pipe( |
| 2301 | Effect.map(Option.some), | |
| 2301 | Effect.asSome, | |
| 2302 | 2302 | Effect.orElseSucceed(() => Option.none<ServerSettingsValue>()), |
| 2303 | 2303 | ); |
| 2304 | 2304 | const continueAfterRestartFor = Effect.fn("continueAfterRestartFor")(function* ( |
apps/server/src/provider/Layers/ProviderSessionDirectory.ts+1-3| ... | ... | @@ -93,9 +93,7 @@ const makeProviderSessionDirectory = Effect.gen(function* () { |
| 93 | 93 | Option.match(runtime, { |
| 94 | 94 | onNone: () => Effect.succeed(Option.none<ProviderRuntimeBinding>()), |
| 95 | 95 | onSome: (value) => |
| 96 | toRuntimeBinding(value, "ProviderSessionDirectory.getBinding").pipe( | |
| 97 | Effect.map((binding) => Option.some(binding)), | |
| 98 | ), | |
| 96 | toRuntimeBinding(value, "ProviderSessionDirectory.getBinding").pipe(Effect.asSome), | |
| 99 | 97 | }), |
| 100 | 98 | ), |
| 101 | 99 | ); |
apps/server/src/provider/Layers/claudeResetCredits.ts+1-1| ... | ... | @@ -186,7 +186,7 @@ export const readClaudeResetCredits = Effect.fn("readClaudeResetCredits")( |
| 186 | 186 | ); |
| 187 | 187 | }, |
| 188 | 188 | Effect.timeout("10 seconds"), |
| 189 | Effect.catch(() => Effect.succeed(undefined)), | |
| 189 | Effect.orElseSucceed(() => undefined), | |
| 190 | 190 | ); |
| 191 | 191 | |
| 192 | 192 | /** The CLI keeps the account record beside its settings, or in the home directory by default. */ |
apps/server/src/provider/Layers/cursorUsageLimits.ts+6-8| ... | ... | @@ -127,14 +127,12 @@ export const readCursorUsageLimits = Effect.fn("readCursorUsageLimits")(function |
| 127 | 127 | return cursorUsageResponseToLimits(body, checkedAt); |
| 128 | 128 | }).pipe( |
| 129 | 129 | Effect.timeout("10 seconds"), |
| 130 | Effect.catch(() => | |
| 131 | Effect.succeed( | |
| 132 | makeUnavailableUsageLimits({ | |
| 133 | checkedAt, | |
| 134 | reason: "probeFailed", | |
| 135 | message: "Cursor could not read usage limits.", | |
| 136 | }), | |
| 137 | ), | |
| 130 | Effect.orElseSucceed(() => | |
| 131 | makeUnavailableUsageLimits({ | |
| 132 | checkedAt, | |
| 133 | reason: "probeFailed", | |
| 134 | message: "Cursor could not read usage limits.", | |
| 135 | }), | |
| 138 | 136 | ), |
| 139 | 137 | ); |
| 140 | 138 | }); |
apps/server/src/provider/Layers/grokUsageLimits.ts+1-1| ... | ... | @@ -169,7 +169,7 @@ export const readGrokAccount = Effect.fn("readGrokAccount")(function* ( |
| 169 | 169 | return grokUsageResponseToLimits(body, checkedAt); |
| 170 | 170 | }).pipe( |
| 171 | 171 | Effect.timeout("10 seconds"), |
| 172 | Effect.catch(() => Effect.succeed(probeFailed)), | |
| 172 | Effect.orElseSucceed(() => probeFailed), | |
| 173 | 173 | ); |
| 174 | 174 | return { email, usageLimits }; |
| 175 | 175 | }); |
apps/server/src/provider/ModelManifest.ts+1-1| ... | ... | @@ -408,7 +408,7 @@ export const make = Effect.gen(function* () { |
| 408 | 408 | fetchedAtMs = now; |
| 409 | 409 | yield* encodeManifestCache({ fetchedAtMs: now, manifest: fetched }).pipe( |
| 410 | 410 | Effect.flatMap((serialized) => fileSystem.writeFileString(cachePath, serialized)), |
| 411 | Effect.catchCause(() => Effect.void), | |
| 411 | Effect.ignoreCause, | |
| 412 | 412 | ); |
| 413 | 413 | return manifest; |
| 414 | 414 | }); |
apps/server/src/provider/acp/AcpSessionRuntime.ts+27-30| ... | ... | @@ -780,17 +780,16 @@ export const make = ( |
| 780 | 780 | resumePayload, |
| 781 | 781 | acp.agent.resumeSession(resumePayload).pipe( |
| 782 | 782 | Effect.timeoutOption(options.sessionLoadTimeout ?? defaultSessionLoadTimeout), |
| 783 | Effect.flatMap((result) => | |
| 784 | Option.isSome(result) | |
| 785 | ? Effect.succeed(result.value) | |
| 786 | : Effect.fail( | |
| 787 | new EffectAcpErrors.AcpTransportError({ | |
| 788 | operation: "call-rpc", | |
| 789 | method: "session/resume", | |
| 790 | detail: "session/resume timed out waiting for the agent response.", | |
| 791 | cause: undefined, | |
| 792 | }), | |
| 793 | ), | |
| 783 | Effect.flatMap( | |
| 784 | Effect.fromOption( | |
| 785 | () => | |
| 786 | new EffectAcpErrors.AcpTransportError({ | |
| 787 | operation: "call-rpc", | |
| 788 | method: "session/resume", | |
| 789 | detail: "session/resume timed out waiting for the agent response.", | |
| 790 | cause: undefined, | |
| 791 | }), | |
| 792 | ), | |
| 794 | 793 | ), |
| 795 | 794 | ), |
| 796 | 795 | ); |
| ... | ... | @@ -834,19 +833,16 @@ export const make = ( |
| 834 | 833 | ).pipe( |
| 835 | 834 | Effect.ensuring(Fiber.interrupt(idleFiber).pipe(Effect.ignore)), |
| 836 | 835 | Effect.timeoutOption(sessionLoadTimeout), |
| 837 | Effect.flatMap((result) => | |
| 838 | Option.match(result, { | |
| 839 | onNone: () => | |
| 840 | Effect.fail( | |
| 841 | new EffectAcpErrors.AcpTransportError({ | |
| 842 | operation: "call-rpc", | |
| 843 | method: "session/load", | |
| 844 | detail: "session/load timed out waiting for RPC response or replay idle gap", | |
| 845 | cause: undefined, | |
| 846 | }), | |
| 847 | ), | |
| 848 | onSome: Effect.succeed, | |
| 849 | }), | |
| 836 | Effect.flatMap( | |
| 837 | Effect.fromOption( | |
| 838 | () => | |
| 839 | new EffectAcpErrors.AcpTransportError({ | |
| 840 | operation: "call-rpc", | |
| 841 | method: "session/load", | |
| 842 | detail: "session/load timed out waiting for RPC response or replay idle gap", | |
| 843 | cause: undefined, | |
| 844 | }), | |
| 845 | ), | |
| 850 | 846 | ), |
| 851 | 847 | Effect.tap((result) => |
| 852 | 848 | logRequest({ |
| ... | ... | @@ -1058,12 +1054,13 @@ export const make = ( |
| 1058 | 1054 | ), |
| 1059 | 1055 | (activePrompt) => |
| 1060 | 1056 | Fiber.join(activePrompt.fiber).pipe( |
| 1061 | Effect.catchCause((cause) => | |
| 1062 | options.cancelBehavior !== "wait-for-prompt" && Cause.hasInterruptsOnly(cause) | |
| 1063 | ? Effect.succeed({ | |
| 1064 | stopReason: "cancelled", | |
| 1065 | } satisfies EffectAcpSchema.PromptResponse) | |
| 1066 | : Effect.failCause(cause), | |
| 1057 | Effect.catchCauseIf( | |
| 1058 | (cause) => | |
| 1059 | options.cancelBehavior !== "wait-for-prompt" && Cause.hasInterruptsOnly(cause), | |
| 1060 | () => | |
| 1061 | Effect.succeed({ | |
| 1062 | stopReason: "cancelled", | |
| 1063 | } satisfies EffectAcpSchema.PromptResponse), | |
| 1067 | 1064 | ), |
| 1068 | 1065 | Effect.tap(() => |
| 1069 | 1066 | closeActiveAssistantSegment({ queue: eventQueue, assistantSegmentRef }), |
apps/server/src/provider/antigravityAuthSupport.ts+1-3| ... | ... | @@ -277,9 +277,7 @@ const linkAntigravityUserSkills = Effect.fn("linkAntigravityUserSkills")(functio |
| 277 | 277 | yield* Effect.gen(function* () { |
| 278 | 278 | const existing = yield* fs.readLink(link).pipe( |
| 279 | 279 | Effect.map((value): string | undefined => path.resolve(path.dirname(link), value)), |
| 280 | Effect.catch((error) => | |
| 281 | error.reason._tag === "NotFound" ? Effect.succeed(undefined) : Effect.fail(error), | |
| 282 | ), | |
| 280 | Effect.catchReason("PlatformError", "NotFound", () => Effect.undefined), | |
| 283 | 281 | ); |
| 284 | 282 | if (existing === target) return; |
| 285 | 283 | if (existing !== undefined) { |
apps/server/src/provider/makeManagedServerProvider.ts+1| ... | ... | @@ -252,6 +252,7 @@ export const makeManagedServerProvider = Effect.fn("makeManagedServerProvider")( |
| 252 | 252 | yield* Effect.forever( |
| 253 | 253 | getRefreshInterval.pipe( |
| 254 | 254 | Effect.flatMap((refreshInterval) => |
| 255 | // @effect-diagnostics-next-line raceFirstWithSleepToTimeout:off - races the interval against a settings-change signal, not a timeout | |
| 255 | 256 | Effect.raceFirst( |
| 256 | 257 | Effect.sleep( |
| 257 | 258 | Duration.toMillis(Duration.fromInputUnsafe(refreshInterval)) <= 0 |
apps/server/src/provider/providerInstallation.ts+1-1| ... | ... | @@ -118,7 +118,7 @@ export const makeProviderInstallation = Effect.fn("makeProviderInstallation")(fu |
| 118 | 118 | env: mergeProviderInstanceEnvironment(entry.environment), |
| 119 | 119 | }).pipe( |
| 120 | 120 | Effect.map((resolved) => [binaryPath, resolved]), |
| 121 | Effect.catch(() => Effect.succeed([binaryPath])), | |
| 121 | Effect.orElseSucceed(() => [binaryPath]), | |
| 122 | 122 | ); |
| 123 | 123 | }); |
| 124 | 124 | yield* installation |
apps/server/src/pullRequest/GitHubPullRequestCli.ts+26-27| ... | ... | @@ -1595,31 +1595,30 @@ export const make = Effect.gen(function* () { |
| 1595 | 1595 | decode: decodePullRequestCoreJson, |
| 1596 | 1596 | }), |
| 1597 | 1597 | ), |
| 1598 | ).pipe( | |
| 1599 | Effect.flatMap((core) => { | |
| 1600 | if (!core.checksTruncated) return Effect.succeed(core); | |
| 1601 | // gh already pages check contexts. Keep its complete, deduplicated result for | |
| 1602 | // large check suites instead of letting the first 100 checks imply success. | |
| 1603 | return readLegacyDetail(input).pipe( | |
| 1604 | Effect.flatMap((detail) => | |
| 1605 | detail.headSha !== core.headSha | |
| 1606 | ? Effect.fail( | |
| 1607 | new GitHubPullRequestReadError({ | |
| 1608 | command: "gh", | |
| 1609 | cwd: input.cwd, | |
| 1610 | operation: "getPullRequestDetail", | |
| 1611 | cause: new Error("Pull request head changed while reading checks."), | |
| 1612 | }), | |
| 1613 | ) | |
| 1614 | : Effect.succeed({ | |
| 1615 | ...core, | |
| 1616 | checks: detail.checks, | |
| 1617 | checksState: detail.checksState, | |
| 1618 | checksTruncated: false, | |
| 1598 | // gh already pages check contexts. Keep its complete, deduplicated result for | |
| 1599 | // large check suites instead of letting the first 100 checks imply success. | |
| 1600 | Effect.filterOrElse( | |
| 1601 | (core) => !core.checksTruncated, | |
| 1602 | (core) => | |
| 1603 | readLegacyDetail(input).pipe( | |
| 1604 | Effect.filterOrFail( | |
| 1605 | (detail) => detail.headSha === core.headSha, | |
| 1606 | () => | |
| 1607 | new GitHubPullRequestReadError({ | |
| 1608 | command: "gh", | |
| 1609 | cwd: input.cwd, | |
| 1610 | operation: "getPullRequestDetail", | |
| 1611 | cause: new Error("Pull request head changed while reading checks."), | |
| 1619 | 1612 | }), |
| 1613 | ), | |
| 1614 | Effect.map((detail) => ({ | |
| 1615 | ...core, | |
| 1616 | checks: detail.checks, | |
| 1617 | checksState: detail.checksState, | |
| 1618 | checksTruncated: false, | |
| 1619 | })), | |
| 1620 | 1620 | ), |
| 1621 | ); | |
| 1622 | }), | |
| 1621 | ), | |
| 1623 | 1622 | ); |
| 1624 | 1623 | }; |
| 1625 | 1624 | |
| ... | ... | @@ -1967,10 +1966,9 @@ export const make = Effect.gen(function* () { |
| 1967 | 1966 | // the fallback out: an empty answer under one is already the answer. |
| 1968 | 1967 | const hasQuery = (input.query?.trim().length ?? 0) > 0; |
| 1969 | 1968 | return read(true).pipe( |
| 1970 | Effect.flatMap((batch) => | |
| 1971 | batch.items.length === 0 && input.cursor === undefined && !hasQuery | |
| 1972 | ? read(false) | |
| 1973 | : Effect.succeed(batch), | |
| 1969 | Effect.filterOrElse( | |
| 1970 | (batch) => batch.items.length > 0 || input.cursor !== undefined || hasQuery, | |
| 1971 | () => read(false), | |
| 1974 | 1972 | ), |
| 1975 | 1973 | Effect.flatMap((batch) => { |
| 1976 | 1974 | // Match the search query's host support, and enrich only rows that survived paging. |
| ... | ... | @@ -2131,6 +2129,7 @@ export const make = Effect.gen(function* () { |
| 2131 | 2129 | }), |
| 2132 | 2130 | ); |
| 2133 | 2131 | }), |
| 2132 | // @effect-diagnostics-next-line flatMapConditionalToFilterOrFail:off - the fallback needs a non-null stack, which a predicate that also reads includeDetails cannot refine. | |
| 2134 | 2133 | Effect.flatMap((stack) => { |
| 2135 | 2134 | if (!input.includeDetails || stack === null) return Effect.succeed(stack); |
| 2136 | 2135 | return github |
apps/server/src/pullRequest/GitLabPullRequestCli.ts+1-1| ... | ... | @@ -1250,7 +1250,7 @@ export const make = Effect.gen(function* () { |
| 1250 | 1250 | getMergeRequestDiffFileContents: (input) => |
| 1251 | 1251 | Effect.gen(function* () { |
| 1252 | 1252 | if (input.commit !== undefined && !isCommitSha(input.commit)) { |
| 1253 | return yield* Effect.fail(new GitLabDiffCommitError({ command: "glab", cwd: input.cwd })); | |
| 1253 | return yield* new GitLabDiffCommitError({ command: "glab", cwd: input.cwd }); | |
| 1254 | 1254 | } |
| 1255 | 1255 | const refs = yield* input.commit === undefined |
| 1256 | 1256 | ? getDiffRefs(input) |
apps/server/src/pullRequest/PullRequestReadCache.ts+1-1| ... | ... | @@ -77,7 +77,7 @@ export const make = Effect.gen(function* () { |
| 77 | 77 | () => |
| 78 | 78 | backing |
| 79 | 79 | .get("revisions") |
| 80 | .pipe(Effect.flatMap((raw) => Schema.decodeUnknownEffect(revisionCodec)(raw ?? "{}"))), | |
| 80 | .pipe(Effect.flatMap((raw) => Schema.decodeEffect(revisionCodec)(raw ?? "{}"))), | |
| 81 | 81 | { |
| 82 | 82 | capacity: 1, |
| 83 | 83 | timeToLive: (exit) => (Exit.isSuccess(exit) ? Duration.infinity : Duration.zero), |
apps/server/src/pullRequest/PullRequestService.test.ts+5-3| ... | ... | @@ -3251,13 +3251,13 @@ it.effect("shares one cold viewer lookup across distinct concurrent lists", () = |
| 3251 | 3251 | ], |
| 3252 | 3252 | }); |
| 3253 | 3253 | |
| 3254 | yield* Effect.all( | |
| 3255 | ["all", "authored", "reviewing"].map((involvement) => | |
| 3254 | yield* Effect.forEach( | |
| 3255 | ["all", "authored", "reviewing"], | |
| 3256 | (involvement) => | |
| 3256 | 3257 | service.list({ |
| 3257 | 3258 | state: "open", |
| 3258 | 3259 | involvement: involvement as "all" | "authored" | "reviewing", |
| 3259 | 3260 | }), |
| 3260 | ), | |
| 3261 | 3261 | { concurrency: "unbounded" }, |
| 3262 | 3262 | ); |
| 3263 | 3263 | |
| ... | ... | @@ -4328,6 +4328,7 @@ it.effect("keeps routed reads separate when the GitHub account changes", () => |
| 4328 | 4328 | ], |
| 4329 | 4329 | }); |
| 4330 | 4330 | const readOperation = (input: Parameters<typeof service.diff>[0]) => |
| 4331 | // @effect-diagnostics-next-line unnecessaryEffectGen:off - the generator unifies the per-operation union of Effect types, which Effect.asVoid cannot infer through. | |
| 4331 | 4332 | Effect.gen(function* () { |
| 4332 | 4333 | yield* service[operation](input); |
| 4333 | 4334 | }); |
| ... | ... | @@ -4391,6 +4392,7 @@ it.effect("isolates routed caches for two credentials belonging to the same acco |
| 4391 | 4392 | ], |
| 4392 | 4393 | }); |
| 4393 | 4394 | const readOperation = (input: Parameters<typeof service.diff>[0]) => |
| 4395 | // @effect-diagnostics-next-line unnecessaryEffectGen:off - the generator unifies the per-operation union of Effect types, which Effect.asVoid cannot infer through. | |
| 4394 | 4396 | Effect.gen(function* () { |
| 4395 | 4397 | yield* service[operation](input); |
| 4396 | 4398 | }); |
apps/server/src/pullRequest/PullRequestService.ts+1-1| ... | ... | @@ -2751,7 +2751,7 @@ export const make = Effect.gen(function* () { |
| 2751 | 2751 | `project:${input.projectId}`, |
| 2752 | 2752 | refScope(input), |
| 2753 | 2753 | ]); |
| 2754 | const decoded = yield* Schema.decodeUnknownEffect(codec)(payload).pipe(Effect.option); | |
| 2754 | const decoded = yield* Schema.decodeEffect(codec)(payload).pipe(Effect.option); | |
| 2755 | 2755 | return Option.isSome(decoded) ? decoded.value : yield* lookup; |
| 2756 | 2756 | }); |
| 2757 | 2757 | const summaryCodec = Schema.fromJsonString(PullRequestSummary); |
apps/server/src/relay/AgentAwarenessRelay.test.ts+4-4| ... | ... | @@ -576,7 +576,7 @@ describe.sequential("signRelayAgentActivityPublishProof", () => { |
| 576 | 576 | yield* Deferred.await(releaseThreadShell); |
| 577 | 577 | return Option.some(thread); |
| 578 | 578 | }), |
| 579 | getProjectShellById: () => Effect.succeed(Option.some(project)), | |
| 579 | getProjectShellById: () => Effect.succeedSome(project), | |
| 580 | 580 | } as unknown as ProjectionSnapshotQueryShape; |
| 581 | 581 | |
| 582 | 582 | const descriptor = { |
| ... | ... | @@ -793,8 +793,8 @@ describe.sequential("signRelayAgentActivityPublishProof", () => { |
| 793 | 793 | threads: [thread], |
| 794 | 794 | updatedAt: now, |
| 795 | 795 | } satisfies OrchestrationShellSnapshot), |
| 796 | getThreadShellById: () => Effect.succeed(Option.some(thread)), | |
| 797 | getProjectShellById: () => Effect.succeed(Option.some(project)), | |
| 796 | getThreadShellById: () => Effect.succeedSome(thread), | |
| 797 | getProjectShellById: () => Effect.succeedSome(project), | |
| 798 | 798 | } as unknown as ProjectionSnapshotQueryShape), |
| 799 | 799 | ); |
| 800 | 800 | |
| ... | ... | @@ -915,7 +915,7 @@ describe.sequential("signRelayAgentActivityPublishProof", () => { |
| 915 | 915 | Layer.succeed(OrchestrationEngineService, {} as OrchestrationEngineShape), |
| 916 | 916 | Layer.succeed(ProjectionSnapshotQuery, { |
| 917 | 917 | getThreadShellById: () => Effect.sync(() => Option.fromNullishOr(currentThread)), |
| 918 | getProjectShellById: () => Effect.succeed(Option.some(project)), | |
| 918 | getProjectShellById: () => Effect.succeedSome(project), | |
| 919 | 919 | } as unknown as ProjectionSnapshotQueryShape), |
| 920 | 920 | ); |
| 921 | 921 |
apps/server/src/resourceTelemetry/HostResources.ts+2-2| ... | ... | @@ -60,7 +60,7 @@ export const make = Effect.fn("makeHostResources")(function* () { |
| 60 | 60 | if (platform === "linux") { |
| 61 | 61 | const meminfo = yield* fs |
| 62 | 62 | .readFileString("/proc/meminfo") |
| 63 | .pipe(Effect.catch(() => Effect.succeed(""))); | |
| 63 | .pipe(Effect.orElseSucceed(() => "")); | |
| 64 | 64 | const available = /^MemAvailable:\s+(\d+)\s+kB$/m.exec(meminfo)?.[1]; |
| 65 | 65 | if (available) availableMemoryBytes = Number(available) * 1024; |
| 66 | 66 | } else if (platform === "darwin") { |
| ... | ... | @@ -68,7 +68,7 @@ export const make = Effect.fn("makeHostResources")(function* () { |
| 68 | 68 | .string(ChildProcess.make("/usr/bin/vm_stat", [], { stdin: "ignore", stderr: "ignore" })) |
| 69 | 69 | .pipe( |
| 70 | 70 | Effect.timeout("1 second"), |
| 71 | Effect.catch(() => Effect.succeed("")), | |
| 71 | Effect.orElseSucceed(() => ""), | |
| 72 | 72 | ); |
| 73 | 73 | availableMemoryBytes = darwinAvailableMemory(output) ?? availableMemoryBytes; |
| 74 | 74 | } |
apps/server/src/server.test.ts+111-129| ... | ... | @@ -655,25 +655,25 @@ const buildAppUnderTest = (options?: { |
| 655 | 655 | get: () => Effect.succeed(defaultVcsDriver), |
| 656 | 656 | detect: (input) => |
| 657 | 657 | defaultVcsDriver.detectRepository(input.cwd).pipe( |
| 658 | Effect.flatMap((repository) => | |
| 659 | repository | |
| 660 | ? Effect.succeed(repository) | |
| 661 | : defaultVcsDriver.isInsideWorkTree(input.cwd).pipe( | |
| 662 | Effect.map((isInsideWorkTree) => | |
| 663 | isInsideWorkTree | |
| 664 | ? { | |
| 665 | kind: "git" as const, | |
| 666 | rootPath: input.cwd, | |
| 667 | metadataPath: null, | |
| 668 | freshness: { | |
| 669 | source: "live-local" as const, | |
| 670 | observedAt: TEST_EPOCH, | |
| 671 | expiresAt: Option.none(), | |
| 672 | }, | |
| 673 | } | |
| 674 | : null, | |
| 675 | ), | |
| 658 | Effect.filterOrElse( | |
| 659 | (repository) => repository !== null, | |
| 660 | () => | |
| 661 | defaultVcsDriver.isInsideWorkTree(input.cwd).pipe( | |
| 662 | Effect.map((isInsideWorkTree) => | |
| 663 | isInsideWorkTree | |
| 664 | ? { | |
| 665 | kind: "git" as const, | |
| 666 | rootPath: input.cwd, | |
| 667 | metadataPath: null, | |
| 668 | freshness: { | |
| 669 | source: "live-local" as const, | |
| 670 | observedAt: TEST_EPOCH, | |
| 671 | expiresAt: Option.none(), | |
| 672 | }, | |
| 673 | } | |
| 674 | : null, | |
| 676 | 675 | ), |
| 676 | ), | |
| 677 | 677 | ), |
| 678 | 678 | Effect.map((repository) => |
| 679 | 679 | repository |
| ... | ... | @@ -822,7 +822,7 @@ const buildAppUnderTest = (options?: { |
| 822 | 822 | ...options?.layers?.providerAuth, |
| 823 | 823 | }), |
| 824 | 824 | Layer.mock(ProviderInstanceRegistry)({ |
| 825 | getInstance: () => Effect.succeed(undefined), | |
| 825 | getInstance: () => Effect.undefined, | |
| 826 | 826 | listInstances: Effect.succeed([]), |
| 827 | 827 | ...options?.layers?.providerInstanceRegistry, |
| 828 | 828 | }), |
| ... | ... | @@ -832,7 +832,7 @@ const buildAppUnderTest = (options?: { |
| 832 | 832 | }), |
| 833 | 833 | Layer.mock(ProviderSessionDirectory.ProviderSessionDirectory)({ |
| 834 | 834 | upsert: () => Effect.void, |
| 835 | getBinding: () => Effect.succeed(Option.none()), | |
| 835 | getBinding: () => Effect.succeedNone, | |
| 836 | 836 | listThreadIds: () => Effect.succeed([]), |
| 837 | 837 | listBindings: () => Effect.succeed([]), |
| 838 | 838 | ...options?.layers?.providerSessionDirectory, |
| ... | ... | @@ -858,7 +858,7 @@ const buildAppUnderTest = (options?: { |
| 858 | 858 | Layer.mergeAll( |
| 859 | 859 | Layer.mock(ExternalLauncher.ExternalLauncher)({ |
| 860 | 860 | resolveAvailableEditors: () => Effect.succeed([]), |
| 861 | resolveFileManagerRevealKind: () => Effect.sync((): undefined => undefined), | |
| 861 | resolveFileManagerRevealKind: () => Effect.undefined, | |
| 862 | 862 | ...options?.layers?.externalLauncher, |
| 863 | 863 | }), |
| 864 | 864 | Layer.mock(RemoteOpenTargets.RemoteOpenTargets)({ |
| ... | ... | @@ -1034,20 +1034,20 @@ const buildAppUnderTest = (options?: { |
| 1034 | 1034 | }), |
| 1035 | 1035 | searchThreads: () => Effect.succeed({ matches: [] }), |
| 1036 | 1036 | getSnapshotSequence: () => Effect.succeed({ snapshotSequence: 0 }), |
| 1037 | getProjectShellById: () => Effect.succeed(Option.none()), | |
| 1038 | getThreadShellById: () => Effect.succeed(Option.none()), | |
| 1039 | getThreadDetailById: () => Effect.succeed(Option.none()), | |
| 1040 | getThreadDetailSnapshot: () => Effect.succeed(Option.none()), | |
| 1037 | getProjectShellById: () => Effect.succeedNone, | |
| 1038 | getThreadShellById: () => Effect.succeedNone, | |
| 1039 | getThreadDetailById: () => Effect.succeedNone, | |
| 1040 | getThreadDetailSnapshot: () => Effect.succeedNone, | |
| 1041 | 1041 | getCounts: () => Effect.succeed({ projectCount: 0, threadCount: 0 }), |
| 1042 | 1042 | getEventReplayStats: ({ fromSequenceExclusive, toSequenceInclusive }) => |
| 1043 | 1043 | Effect.succeed({ |
| 1044 | 1044 | eventCount: Math.max(0, toSequenceInclusive - fromSequenceExclusive), |
| 1045 | 1045 | payloadBytes: 0, |
| 1046 | 1046 | }), |
| 1047 | getActiveProjectByWorkspaceRoot: () => Effect.succeed(Option.none()), | |
| 1048 | getFirstActiveThreadIdByProjectId: () => Effect.succeed(Option.none()), | |
| 1047 | getActiveProjectByWorkspaceRoot: () => Effect.succeedNone, | |
| 1048 | getFirstActiveThreadIdByProjectId: () => Effect.succeedNone, | |
| 1049 | 1049 | getImportedAgentSessionSources: () => Effect.succeed([]), |
| 1050 | getThreadCheckpointContext: () => Effect.succeed(Option.none()), | |
| 1050 | getThreadCheckpointContext: () => Effect.succeedNone, | |
| 1051 | 1051 | ...options?.layers?.projectionSnapshotQuery, |
| 1052 | 1052 | }), |
| 1053 | 1053 | ), |
| ... | ... | @@ -1201,7 +1201,7 @@ const buildAppUnderTest = (options?: { |
| 1201 | 1201 | Layer.provide( |
| 1202 | 1202 | Layer.mock(CloudCliTokenManager.CloudCliTokenManager)({ |
| 1203 | 1203 | get: Effect.die(new Error("Unexpected T3 Connect CLI authorization request.")), |
| 1204 | getExisting: Effect.succeed(Option.none()), | |
| 1204 | getExisting: Effect.succeedNone, | |
| 1205 | 1205 | hasCredential: Effect.succeed(false), |
| 1206 | 1206 | clear: Effect.void, |
| 1207 | 1207 | ...options?.layers?.cloudCliTokenManager, |
| ... | ... | @@ -8972,8 +8972,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { |
| 8972 | 8972 | yield* buildAppUnderTest({ |
| 8973 | 8973 | layers: { |
| 8974 | 8974 | projectionSnapshotQuery: { |
| 8975 | getThreadDetailSnapshot: () => | |
| 8976 | Effect.succeed(Option.some({ snapshotSequence: 1, thread })), | |
| 8975 | getThreadDetailSnapshot: () => Effect.succeedSome({ snapshotSequence: 1, thread }), | |
| 8977 | 8976 | }, |
| 8978 | 8977 | }, |
| 8979 | 8978 | }); |
| ... | ... | @@ -9146,16 +9145,13 @@ it.layer(NodeServices.layer)("server router seam", (it) => { |
| 9146 | 9145 | streamDomainEvents: Stream.concat(Stream.make(event), Stream.never), |
| 9147 | 9146 | }, |
| 9148 | 9147 | projectionSnapshotQuery: { |
| 9149 | getThreadDetailSnapshot: () => | |
| 9150 | Effect.succeed(Option.some({ snapshotSequence: 1, thread })), | |
| 9148 | getThreadDetailSnapshot: () => Effect.succeedSome({ snapshotSequence: 1, thread }), | |
| 9151 | 9149 | getThreadShellById: (threadId) => |
| 9152 | Effect.succeed( | |
| 9153 | Option.some({ | |
| 9154 | ...makeDefaultOrchestrationThreadShell(), | |
| 9155 | id: threadId, | |
| 9156 | title: "Build complete", | |
| 9157 | }), | |
| 9158 | ), | |
| 9150 | Effect.succeedSome({ | |
| 9151 | ...makeDefaultOrchestrationThreadShell(), | |
| 9152 | id: threadId, | |
| 9153 | title: "Build complete", | |
| 9154 | }), | |
| 9159 | 9155 | }, |
| 9160 | 9156 | }, |
| 9161 | 9157 | }); |
| ... | ... | @@ -9433,7 +9429,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { |
| 9433 | 9429 | }, |
| 9434 | 9430 | projectionSnapshotQuery: { |
| 9435 | 9431 | getThreadDetailSnapshot: () => |
| 9436 | Effect.succeed(Option.some({ snapshotSequence: 100_000, thread })), | |
| 9432 | Effect.succeedSome({ snapshotSequence: 100_000, thread }), | |
| 9437 | 9433 | }, |
| 9438 | 9434 | }, |
| 9439 | 9435 | }); |
| ... | ... | @@ -9751,8 +9747,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { |
| 9751 | 9747 | }), |
| 9752 | 9748 | }, |
| 9753 | 9749 | projectionSnapshotQuery: { |
| 9754 | getThreadDetailSnapshot: () => | |
| 9755 | Effect.succeed(Option.some({ snapshotSequence: 5, thread })), | |
| 9750 | getThreadDetailSnapshot: () => Effect.succeedSome({ snapshotSequence: 5, thread }), | |
| 9756 | 9751 | }, |
| 9757 | 9752 | }, |
| 9758 | 9753 | }); |
| ... | ... | @@ -9840,7 +9835,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { |
| 9840 | 9835 | projectionSnapshotQuery: { |
| 9841 | 9836 | getThreadDetailSnapshot: (_threadId, options) => { |
| 9842 | 9837 | requestedTurnLimit = options?.turnLimit; |
| 9843 | return Effect.succeed(Option.some({ snapshotSequence: 5, thread })); | |
| 9838 | return Effect.succeedSome({ snapshotSequence: 5, thread }); | |
| 9844 | 9839 | }, |
| 9845 | 9840 | }, |
| 9846 | 9841 | }, |
| ... | ... | @@ -9933,7 +9928,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { |
| 9933 | 9928 | readEvents: store.readFromSequence, |
| 9934 | 9929 | }, |
| 9935 | 9930 | projectionSnapshotQuery: { |
| 9936 | getThreadDetailSnapshot: () => Effect.succeed(Option.none()), | |
| 9931 | getThreadDetailSnapshot: () => Effect.succeedNone, | |
| 9937 | 9932 | }, |
| 9938 | 9933 | }, |
| 9939 | 9934 | }); |
| ... | ... | @@ -10163,8 +10158,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { |
| 10163 | 10158 | replayStatsCalls += 1; |
| 10164 | 10159 | return { eventCount: 5, payloadBytes: 8 * 1024 * 1024 + 1 }; |
| 10165 | 10160 | }), |
| 10166 | getThreadDetailSnapshot: () => | |
| 10167 | Effect.succeed(Option.some({ snapshotSequence: 5, thread })), | |
| 10161 | getThreadDetailSnapshot: () => Effect.succeedSome({ snapshotSequence: 5, thread }), | |
| 10168 | 10162 | getShellSnapshot: () => |
| 10169 | 10163 | Effect.succeed({ |
| 10170 | 10164 | snapshotSequence: 5, |
| ... | ... | @@ -10468,7 +10462,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { |
| 10468 | 10462 | ]), |
| 10469 | 10463 | }, |
| 10470 | 10464 | projectionSnapshotQuery: { |
| 10471 | getThreadShellById: () => Effect.succeed(Option.none()), | |
| 10465 | getThreadShellById: () => Effect.succeedNone, | |
| 10472 | 10466 | }, |
| 10473 | 10467 | }, |
| 10474 | 10468 | }); |
| ... | ... | @@ -10526,9 +10520,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { |
| 10526 | 10520 | detail: "transient failure", |
| 10527 | 10521 | }), |
| 10528 | 10522 | ) |
| 10529 | : Effect.succeed( | |
| 10530 | Option.some(makeDefaultOrchestrationThreadShell({ id: threadId })), | |
| 10531 | ); | |
| 10523 | : Effect.succeedSome(makeDefaultOrchestrationThreadShell({ id: threadId })); | |
| 10532 | 10524 | }), |
| 10533 | 10525 | }, |
| 10534 | 10526 | }, |
| ... | ... | @@ -10588,7 +10580,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { |
| 10588 | 10580 | ]), |
| 10589 | 10581 | }, |
| 10590 | 10582 | projectionSnapshotQuery: { |
| 10591 | getProjectShellById: () => Effect.succeed(Option.none()), | |
| 10583 | getProjectShellById: () => Effect.succeedNone, | |
| 10592 | 10584 | }, |
| 10593 | 10585 | }, |
| 10594 | 10586 | }); |
| ... | ... | @@ -10634,22 +10626,20 @@ it.layer(NodeServices.layer)("server router seam", (it) => { |
| 10634 | 10626 | }, |
| 10635 | 10627 | projectionSnapshotQuery: { |
| 10636 | 10628 | getThreadShellById: () => |
| 10637 | Effect.succeed( | |
| 10638 | Option.some( | |
| 10639 | makeDefaultOrchestrationThreadShell({ | |
| 10640 | id: threadId, | |
| 10629 | Effect.succeedSome( | |
| 10630 | makeDefaultOrchestrationThreadShell({ | |
| 10631 | id: threadId, | |
| 10632 | updatedAt: now, | |
| 10633 | session: { | |
| 10634 | threadId, | |
| 10635 | status: "ready", | |
| 10636 | providerName: "claudeAgent", | |
| 10637 | runtimeMode: "full-access", | |
| 10638 | activeTurnId: null, | |
| 10639 | lastError: null, | |
| 10641 | 10640 | updatedAt: now, |
| 10642 | session: { | |
| 10643 | threadId, | |
| 10644 | status: "ready", | |
| 10645 | providerName: "claudeAgent", | |
| 10646 | runtimeMode: "full-access", | |
| 10647 | activeTurnId: null, | |
| 10648 | lastError: null, | |
| 10649 | updatedAt: now, | |
| 10650 | }, | |
| 10651 | }), | |
| 10652 | ), | |
| 10641 | }, | |
| 10642 | }), | |
| 10653 | 10643 | ), |
| 10654 | 10644 | }, |
| 10655 | 10645 | }, |
| ... | ... | @@ -10782,8 +10772,8 @@ it.layer(NodeServices.layer)("server router seam", (it) => { |
| 10782 | 10772 | }, |
| 10783 | 10773 | projectionSnapshotQuery: { |
| 10784 | 10774 | getThreadShellById: () => |
| 10785 | Effect.succeed( | |
| 10786 | Option.some(makeDefaultOrchestrationThreadShell({ id: threadId, session: null })), | |
| 10775 | Effect.succeedSome( | |
| 10776 | makeDefaultOrchestrationThreadShell({ id: threadId, session: null }), | |
| 10787 | 10777 | ), |
| 10788 | 10778 | }, |
| 10789 | 10779 | }, |
| ... | ... | @@ -10836,22 +10826,20 @@ it.layer(NodeServices.layer)("server router seam", (it) => { |
| 10836 | 10826 | }, |
| 10837 | 10827 | projectionSnapshotQuery: { |
| 10838 | 10828 | getThreadShellById: () => |
| 10839 | Effect.succeed( | |
| 10840 | Option.some( | |
| 10841 | makeDefaultOrchestrationThreadShell({ | |
| 10842 | id: threadId, | |
| 10829 | Effect.succeedSome( | |
| 10830 | makeDefaultOrchestrationThreadShell({ | |
| 10831 | id: threadId, | |
| 10832 | updatedAt: now, | |
| 10833 | session: { | |
| 10834 | threadId, | |
| 10835 | status: "stopped", | |
| 10836 | providerName: "claudeAgent", | |
| 10837 | runtimeMode: "full-access", | |
| 10838 | activeTurnId: null, | |
| 10839 | lastError: null, | |
| 10843 | 10840 | updatedAt: now, |
| 10844 | session: { | |
| 10845 | threadId, | |
| 10846 | status: "stopped", | |
| 10847 | providerName: "claudeAgent", | |
| 10848 | runtimeMode: "full-access", | |
| 10849 | activeTurnId: null, | |
| 10850 | lastError: null, | |
| 10851 | updatedAt: now, | |
| 10852 | }, | |
| 10853 | }), | |
| 10854 | ), | |
| 10841 | }, | |
| 10842 | }), | |
| 10855 | 10843 | ), |
| 10856 | 10844 | }, |
| 10857 | 10845 | }, |
| ... | ... | @@ -10902,22 +10890,20 @@ it.layer(NodeServices.layer)("server router seam", (it) => { |
| 10902 | 10890 | }, |
| 10903 | 10891 | projectionSnapshotQuery: { |
| 10904 | 10892 | getThreadShellById: () => |
| 10905 | Effect.succeed( | |
| 10906 | Option.some( | |
| 10907 | makeDefaultOrchestrationThreadShell({ | |
| 10908 | id: threadId, | |
| 10893 | Effect.succeedSome( | |
| 10894 | makeDefaultOrchestrationThreadShell({ | |
| 10895 | id: threadId, | |
| 10896 | updatedAt: now, | |
| 10897 | session: { | |
| 10898 | threadId, | |
| 10899 | status: "ready", | |
| 10900 | providerName: "claudeAgent", | |
| 10901 | runtimeMode: "full-access", | |
| 10902 | activeTurnId: null, | |
| 10903 | lastError: null, | |
| 10909 | 10904 | updatedAt: now, |
| 10910 | session: { | |
| 10911 | threadId, | |
| 10912 | status: "ready", | |
| 10913 | providerName: "claudeAgent", | |
| 10914 | runtimeMode: "full-access", | |
| 10915 | activeTurnId: null, | |
| 10916 | lastError: null, | |
| 10917 | updatedAt: now, | |
| 10918 | }, | |
| 10919 | }), | |
| 10920 | ), | |
| 10905 | }, | |
| 10906 | }), | |
| 10921 | 10907 | ), |
| 10922 | 10908 | }, |
| 10923 | 10909 | }, |
| ... | ... | @@ -11005,22 +10991,20 @@ it.layer(NodeServices.layer)("server router seam", (it) => { |
| 11005 | 10991 | }, |
| 11006 | 10992 | projectionSnapshotQuery: { |
| 11007 | 10993 | getThreadShellById: () => |
| 11008 | Effect.succeed( | |
| 11009 | Option.some( | |
| 11010 | makeDefaultOrchestrationThreadShell({ | |
| 11011 | id: threadId, | |
| 10994 | Effect.succeedSome( | |
| 10995 | makeDefaultOrchestrationThreadShell({ | |
| 10996 | id: threadId, | |
| 10997 | updatedAt: now, | |
| 10998 | session: { | |
| 10999 | threadId, | |
| 11000 | status: "ready", | |
| 11001 | providerName: "claudeAgent", | |
| 11002 | runtimeMode: "full-access", | |
| 11003 | activeTurnId: null, | |
| 11004 | lastError: null, | |
| 11012 | 11005 | updatedAt: now, |
| 11013 | session: { | |
| 11014 | threadId, | |
| 11015 | status: "ready", | |
| 11016 | providerName: "claudeAgent", | |
| 11017 | runtimeMode: "full-access", | |
| 11018 | activeTurnId: null, | |
| 11019 | lastError: null, | |
| 11020 | updatedAt: now, | |
| 11021 | }, | |
| 11022 | }), | |
| 11023 | ), | |
| 11006 | }, | |
| 11007 | }), | |
| 11024 | 11008 | ), |
| 11025 | 11009 | }, |
| 11026 | 11010 | }, |
| ... | ... | @@ -11077,22 +11061,20 @@ it.layer(NodeServices.layer)("server router seam", (it) => { |
| 11077 | 11061 | }, |
| 11078 | 11062 | projectionSnapshotQuery: { |
| 11079 | 11063 | getThreadShellById: () => |
| 11080 | Effect.succeed( | |
| 11081 | Option.some( | |
| 11082 | makeDefaultOrchestrationThreadShell({ | |
| 11083 | id: threadId, | |
| 11064 | Effect.succeedSome( | |
| 11065 | makeDefaultOrchestrationThreadShell({ | |
| 11066 | id: threadId, | |
| 11067 | updatedAt: now, | |
| 11068 | session: { | |
| 11069 | threadId, | |
| 11070 | status: "ready", | |
| 11071 | providerName: "claudeAgent", | |
| 11072 | runtimeMode: "full-access", | |
| 11073 | activeTurnId: null, | |
| 11074 | lastError: null, | |
| 11084 | 11075 | updatedAt: now, |
| 11085 | session: { | |
| 11086 | threadId, | |
| 11087 | status: "ready", | |
| 11088 | providerName: "claudeAgent", | |
| 11089 | runtimeMode: "full-access", | |
| 11090 | activeTurnId: null, | |
| 11091 | lastError: null, | |
| 11092 | updatedAt: now, | |
| 11093 | }, | |
| 11094 | }), | |
| 11095 | ), | |
| 11076 | }, | |
| 11077 | }), | |
| 11096 | 11078 | ), |
| 11097 | 11079 | }, |
| 11098 | 11080 | }, |
apps/server/src/serverRuntimeStartup.reconcile.test.ts+39-49| ... | ... | @@ -399,18 +399,16 @@ it.effect("does not continue archived or deleted marked sessions", () => { |
| 399 | 399 | directory: { |
| 400 | 400 | getBinding: (threadId) => { |
| 401 | 401 | const thread = threadId === archived.id ? archived : deleted; |
| 402 | return Effect.succeed( | |
| 403 | Option.some({ | |
| 404 | threadId, | |
| 405 | provider: ProviderDriverKind.make("codex"), | |
| 406 | providerInstanceId, | |
| 407 | status: "running" as const, | |
| 408 | resumeCursor: { cursor: threadId }, | |
| 409 | runtimePayload: { | |
| 410 | continueAfterServerUpdate: thread.session.activeTurnId, | |
| 411 | }, | |
| 412 | }), | |
| 413 | ); | |
| 402 | return Effect.succeedSome({ | |
| 403 | threadId, | |
| 404 | provider: ProviderDriverKind.make("codex"), | |
| 405 | providerInstanceId, | |
| 406 | status: "running" as const, | |
| 407 | resumeCursor: { cursor: threadId }, | |
| 408 | runtimePayload: { | |
| 409 | continueAfterServerUpdate: thread.session.activeTurnId, | |
| 410 | }, | |
| 411 | }); | |
| 414 | 412 | }, |
| 415 | 413 | upsert: () => Effect.void, |
| 416 | 414 | recordImportedTranscript: () => Effect.die("unused"), |
| ... | ... | @@ -456,18 +454,16 @@ it.effect("retries continuation preparation before settling a persistent failure |
| 456 | 454 | threads: [thread], |
| 457 | 455 | directory: { |
| 458 | 456 | getBinding: () => |
| 459 | Effect.succeed( | |
| 460 | Option.some({ | |
| 461 | threadId: thread.id, | |
| 462 | provider: ProviderDriverKind.make("codex"), | |
| 463 | providerInstanceId, | |
| 464 | status: "running" as const, | |
| 465 | resumeCursor: { cursor: thread.id }, | |
| 466 | runtimePayload: { | |
| 467 | continueAfterServerUpdate: thread.session.activeTurnId, | |
| 468 | }, | |
| 469 | }), | |
| 470 | ), | |
| 457 | Effect.succeedSome({ | |
| 458 | threadId: thread.id, | |
| 459 | provider: ProviderDriverKind.make("codex"), | |
| 460 | providerInstanceId, | |
| 461 | status: "running" as const, | |
| 462 | resumeCursor: { cursor: thread.id }, | |
| 463 | runtimePayload: { | |
| 464 | continueAfterServerUpdate: thread.session.activeTurnId, | |
| 465 | }, | |
| 466 | }), | |
| 471 | 467 | upsert: () => Effect.void, |
| 472 | 468 | recordImportedTranscript: () => Effect.die("unused"), |
| 473 | 469 | getProvider: () => Effect.die("unused"), |
| ... | ... | @@ -610,16 +606,14 @@ it.effect( |
| 610 | 606 | directory: { |
| 611 | 607 | getBinding: (candidate) => |
| 612 | 608 | candidate === absent.id |
| 613 | ? Effect.succeed(Option.none()) | |
| 609 | ? Effect.succeedNone | |
| 614 | 610 | : candidate === corrupt.id |
| 615 | 611 | ? Effect.fail(corruptFailure) |
| 616 | : Effect.succeed( | |
| 617 | Option.some({ | |
| 618 | threadId: candidate, | |
| 619 | provider: ProviderDriverKind.make("codex"), | |
| 620 | providerInstanceId, | |
| 621 | }), | |
| 622 | ), | |
| 612 | : Effect.succeedSome({ | |
| 613 | threadId: candidate, | |
| 614 | provider: ProviderDriverKind.make("codex"), | |
| 615 | providerInstanceId, | |
| 616 | }), | |
| 623 | 617 | upsert: () => Effect.fail(writeFailure), |
| 624 | 618 | recordImportedTranscript: () => Effect.die("unused"), |
| 625 | 619 | getProvider: () => Effect.die("unused"), |
| ... | ... | @@ -657,7 +651,7 @@ it.effect("retries failed projections and continues after a persistent failure", |
| 657 | 651 | return runReconciliation({ |
| 658 | 652 | threads: [transient, persistent, later], |
| 659 | 653 | directory: { |
| 660 | getBinding: () => Effect.succeed(Option.none()), | |
| 654 | getBinding: () => Effect.succeedNone, | |
| 661 | 655 | upsert: () => Effect.void, |
| 662 | 656 | recordImportedTranscript: () => Effect.die("unused"), |
| 663 | 657 | getProvider: () => Effect.die("unused"), |
| ... | ... | @@ -758,19 +752,17 @@ for (const scenario of [ |
| 758 | 752 | continueAfterRestart: scenario !== "disabled", |
| 759 | 753 | directory: { |
| 760 | 754 | getBinding: () => |
| 761 | Effect.succeed( | |
| 762 | Option.some({ | |
| 763 | threadId: thread.id, | |
| 764 | provider: ProviderDriverKind.make("codex"), | |
| 765 | providerInstanceId, | |
| 766 | status: scenario === "stopped binding" ? "stopped" : "running", | |
| 767 | ...(scenario.includes("cursor") ? {} : { resumeCursor: { threadId: thread.id } }), | |
| 768 | runtimePayload: { | |
| 769 | activeTurnId: scenario === "marked superseded turn" ? "another-turn" : turnId, | |
| 770 | ...(scenario.startsWith("marked") ? { continueAfterServerUpdate: turnId } : {}), | |
| 771 | }, | |
| 772 | }), | |
| 773 | ), | |
| 755 | Effect.succeedSome({ | |
| 756 | threadId: thread.id, | |
| 757 | provider: ProviderDriverKind.make("codex"), | |
| 758 | providerInstanceId, | |
| 759 | status: scenario === "stopped binding" ? "stopped" : "running", | |
| 760 | ...(scenario.includes("cursor") ? {} : { resumeCursor: { threadId: thread.id } }), | |
| 761 | runtimePayload: { | |
| 762 | activeTurnId: scenario === "marked superseded turn" ? "another-turn" : turnId, | |
| 763 | ...(scenario.startsWith("marked") ? { continueAfterServerUpdate: turnId } : {}), | |
| 764 | }, | |
| 765 | }), | |
| 774 | 766 | upsert: (binding) => |
| 775 | 767 | Effect.sync(() => { |
| 776 | 768 | upserts.push(binding); |
| ... | ... | @@ -944,9 +936,7 @@ it.effect("settles failed opt-in recovery without retrying the provider turn", ( |
| 944 | 936 | Effect.gen(function* () { |
| 945 | 937 | sends.push(input); |
| 946 | 938 | preparedPayloads.push(binding.runtimePayload); |
| 947 | return yield* Effect.fail( | |
| 948 | new ProviderSessionNotFoundError({ threadId: input.threadId }), | |
| 949 | ); | |
| 939 | return yield* new ProviderSessionNotFoundError({ threadId: input.threadId }); | |
| 950 | 940 | }), |
| 951 | 941 | }, |
| 952 | 942 | directory: { |
apps/server/src/serverRuntimeStartup.test.ts+26-28| ... | ... | @@ -174,27 +174,25 @@ it.effect("resolveAutoBootstrapWelcomeTargets returns existing project and threa |
| 174 | 174 | getCounts: () => Effect.die("unused"), |
| 175 | 175 | getEventReplayStats: () => Effect.die("unused"), |
| 176 | 176 | getActiveProjectByWorkspaceRoot: () => |
| 177 | Effect.succeed( | |
| 178 | Option.some({ | |
| 179 | id: bootstrapProjectId, | |
| 180 | title: "Startup Project", | |
| 181 | workspaceRoot: "/tmp/startup-project", | |
| 182 | defaultModelSelection: { | |
| 183 | instanceId: ProviderInstanceId.make("codex"), | |
| 184 | model: DEFAULT_MODEL, | |
| 185 | }, | |
| 186 | scripts: [], | |
| 187 | createdAt: "2026-01-01T00:00:00.000Z", | |
| 188 | updatedAt: "2026-01-01T00:00:00.000Z", | |
| 189 | deletedAt: null, | |
| 190 | }), | |
| 191 | ), | |
| 177 | Effect.succeedSome({ | |
| 178 | id: bootstrapProjectId, | |
| 179 | title: "Startup Project", | |
| 180 | workspaceRoot: "/tmp/startup-project", | |
| 181 | defaultModelSelection: { | |
| 182 | instanceId: ProviderInstanceId.make("codex"), | |
| 183 | model: DEFAULT_MODEL, | |
| 184 | }, | |
| 185 | scripts: [], | |
| 186 | createdAt: "2026-01-01T00:00:00.000Z", | |
| 187 | updatedAt: "2026-01-01T00:00:00.000Z", | |
| 188 | deletedAt: null, | |
| 189 | }), | |
| 192 | 190 | getProjectShells: () => Effect.die("unused"), |
| 193 | 191 | getProjectShellById: () => Effect.die("unused"), |
| 194 | getFirstActiveThreadIdByProjectId: () => Effect.succeed(Option.some(bootstrapThreadId)), | |
| 192 | getFirstActiveThreadIdByProjectId: () => Effect.succeedSome(bootstrapThreadId), | |
| 195 | 193 | getImportedAgentSessionSources: () => Effect.die("unused"), |
| 196 | getThreadCheckpointContext: () => Effect.succeed(Option.none()), | |
| 197 | getFullThreadDiffContext: () => Effect.succeed(Option.none()), | |
| 194 | getThreadCheckpointContext: () => Effect.succeedNone, | |
| 195 | getFullThreadDiffContext: () => Effect.succeedNone, | |
| 198 | 196 | getThreadRuntimeContext: () => Effect.die("unused"), |
| 199 | 197 | getTurnStartMessage: () => Effect.die("unused"), |
| 200 | 198 | getThreadShellById: () => Effect.die("unused"), |
| ... | ... | @@ -321,10 +319,10 @@ it.effect.each([ |
| 321 | 319 | ), |
| 322 | 320 | getProjectShells: () => Effect.die("unused"), |
| 323 | 321 | getProjectShellById: () => Effect.die("unused"), |
| 324 | getFirstActiveThreadIdByProjectId: () => Effect.succeed(Option.none()), | |
| 322 | getFirstActiveThreadIdByProjectId: () => Effect.succeedNone, | |
| 325 | 323 | getImportedAgentSessionSources: () => Effect.die("unused"), |
| 326 | getThreadCheckpointContext: () => Effect.succeed(Option.none()), | |
| 327 | getFullThreadDiffContext: () => Effect.succeed(Option.none()), | |
| 324 | getThreadCheckpointContext: () => Effect.succeedNone, | |
| 325 | getFullThreadDiffContext: () => Effect.succeedNone, | |
| 328 | 326 | getThreadRuntimeContext: () => Effect.die("unused"), |
| 329 | 327 | getTurnStartMessage: () => Effect.die("unused"), |
| 330 | 328 | getThreadShellById: () => Effect.die("unused"), |
| ... | ... | @@ -391,13 +389,13 @@ it.effect( |
| 391 | 389 | getSnapshotSequence: () => Effect.die("unused"), |
| 392 | 390 | getCounts: () => Effect.die("unused"), |
| 393 | 391 | getEventReplayStats: () => Effect.die("unused"), |
| 394 | getActiveProjectByWorkspaceRoot: () => Effect.succeed(Option.none()), | |
| 392 | getActiveProjectByWorkspaceRoot: () => Effect.succeedNone, | |
| 395 | 393 | getProjectShells: () => Effect.die("unused"), |
| 396 | 394 | getProjectShellById: () => Effect.die("unused"), |
| 397 | 395 | getFirstActiveThreadIdByProjectId: () => Effect.die("thread lookup failed"), |
| 398 | 396 | getImportedAgentSessionSources: () => Effect.die("unused"), |
| 399 | getThreadCheckpointContext: () => Effect.succeed(Option.none()), | |
| 400 | getFullThreadDiffContext: () => Effect.succeed(Option.none()), | |
| 397 | getThreadCheckpointContext: () => Effect.succeedNone, | |
| 398 | getFullThreadDiffContext: () => Effect.succeedNone, | |
| 401 | 399 | getThreadRuntimeContext: () => Effect.die("unused"), |
| 402 | 400 | getTurnStartMessage: () => Effect.die("unused"), |
| 403 | 401 | getThreadShellById: () => Effect.die("unused"), |
| ... | ... | @@ -456,13 +454,13 @@ it.effect("resolveAutoBootstrapWelcomeTargets preserves typed UUID generation fa |
| 456 | 454 | getSnapshotSequence: () => Effect.die("unused"), |
| 457 | 455 | getCounts: () => Effect.die("unused"), |
| 458 | 456 | getEventReplayStats: () => Effect.die("unused"), |
| 459 | getActiveProjectByWorkspaceRoot: () => Effect.succeed(Option.none()), | |
| 457 | getActiveProjectByWorkspaceRoot: () => Effect.succeedNone, | |
| 460 | 458 | getProjectShells: () => Effect.die("unused"), |
| 461 | 459 | getProjectShellById: () => Effect.die("unused"), |
| 462 | getFirstActiveThreadIdByProjectId: () => Effect.succeed(Option.none()), | |
| 460 | getFirstActiveThreadIdByProjectId: () => Effect.succeedNone, | |
| 463 | 461 | getImportedAgentSessionSources: () => Effect.die("unused"), |
| 464 | getThreadCheckpointContext: () => Effect.succeed(Option.none()), | |
| 465 | getFullThreadDiffContext: () => Effect.succeed(Option.none()), | |
| 462 | getThreadCheckpointContext: () => Effect.succeedNone, | |
| 463 | getFullThreadDiffContext: () => Effect.succeedNone, | |
| 466 | 464 | getThreadRuntimeContext: () => Effect.die("unused"), |
| 467 | 465 | getTurnStartMessage: () => Effect.die("unused"), |
| 468 | 466 | getThreadShellById: () => Effect.die("unused"), |
apps/server/src/serverRuntimeStartup.ts+45-49| ... | ... | @@ -264,13 +264,13 @@ export const resolveAutoBootstrapWelcomeTargets = Effect.gen(function* () { |
| 264 | 264 | bootstrapThreadId = existingThreadId.value; |
| 265 | 265 | } |
| 266 | 266 | }).pipe( |
| 267 | Effect.catchCause((cause) => | |
| 268 | Cause.hasInterrupts(cause) | |
| 269 | ? Effect.failCause(cause) | |
| 270 | : Effect.logWarning("startup thread auto-bootstrap failed", { | |
| 271 | bootstrapProjectId: nextProjectId, | |
| 272 | cause, | |
| 273 | }), | |
| 267 | Effect.catchCauseIf( | |
| 268 | (cause) => !Cause.hasInterrupts(cause), | |
| 269 | (cause) => | |
| 270 | Effect.logWarning("startup thread auto-bootstrap failed", { | |
| 271 | bootstrapProjectId: nextProjectId, | |
| 272 | cause, | |
| 273 | }), | |
| 274 | 274 | ), |
| 275 | 275 | ); |
| 276 | 276 | }); |
| ... | ... | @@ -312,11 +312,9 @@ const resolveStartupBrowserTarget = Effect.gen(function* () { |
| 312 | 312 | ? `http://${formatHostForUrl(serverConfig.host)}:${serverConfig.port}` |
| 313 | 313 | : localUrl; |
| 314 | 314 | const baseTarget = serverConfig.devUrl?.toString() ?? bindUrl; |
| 315 | return yield* Effect.succeed(serverConfig.mode === "desktop" ? baseTarget : undefined).pipe( | |
| 316 | Effect.flatMap((target) => | |
| 317 | target ? Effect.succeed(target) : serverAuth.issueStartupPairingUrl(baseTarget), | |
| 318 | ), | |
| 319 | ); | |
| 315 | return serverConfig.mode === "desktop" | |
| 316 | ? baseTarget | |
| 317 | : yield* serverAuth.issueStartupPairingUrl(baseTarget); | |
| 320 | 318 | }); |
| 321 | 319 | |
| 322 | 320 | const maybeOpenBrowser = (target: string) => |
| ... | ... | @@ -487,7 +485,7 @@ export const reconcileProviderSessions = Effect.gen(function* () { |
| 487 | 485 | const query = yield* ProjectionSnapshotQuery.ProjectionSnapshotQuery; |
| 488 | 486 | const settings = yield* ServerSettings.ServerSettingsService; |
| 489 | 487 | const restartSettings = yield* settings.getSettings.pipe( |
| 490 | Effect.map(Option.some), | |
| 488 | Effect.asSome, | |
| 491 | 489 | Effect.catch((cause) => |
| 492 | 490 | Effect.logWarning("could not read restart continuation preference", { cause }).pipe( |
| 493 | 491 | Effect.as(Option.none()), |
| ... | ... | @@ -549,13 +547,13 @@ export const reconcileProviderSessions = Effect.gen(function* () { |
| 549 | 547 | continue; |
| 550 | 548 | } |
| 551 | 549 | const binding = yield* directory.getBinding(thread.id).pipe( |
| 552 | Effect.catchCause((cause) => | |
| 553 | Cause.hasInterrupts(cause) | |
| 554 | ? Effect.failCause(cause) | |
| 555 | : Effect.logWarning("failed to read orphaned provider session directory binding", { | |
| 556 | threadId: thread.id, | |
| 557 | cause, | |
| 558 | }).pipe(Effect.as(Option.none())), | |
| 550 | Effect.catchCauseIf( | |
| 551 | (cause) => !Cause.hasInterrupts(cause), | |
| 552 | (cause) => | |
| 553 | Effect.logWarning("failed to read orphaned provider session directory binding", { | |
| 554 | threadId: thread.id, | |
| 555 | cause, | |
| 556 | }).pipe(Effect.as(Option.none())), | |
| 559 | 557 | ), |
| 560 | 558 | ); |
| 561 | 559 | const continuationMarkerPresent = |
| ... | ... | @@ -606,13 +604,13 @@ export const reconcileProviderSessions = Effect.gen(function* () { |
| 606 | 604 | }); |
| 607 | 605 | } |
| 608 | 606 | }).pipe( |
| 609 | Effect.catchCause((cause) => | |
| 610 | Cause.hasInterrupts(cause) | |
| 611 | ? Effect.failCause(cause) | |
| 612 | : Effect.logWarning( | |
| 613 | "failed to reconcile orphaned provider session directory binding", | |
| 614 | { threadId: thread.id, cause }, | |
| 615 | ), | |
| 607 | Effect.catchCauseIf( | |
| 608 | (cause) => !Cause.hasInterrupts(cause), | |
| 609 | (cause) => | |
| 610 | Effect.logWarning("failed to reconcile orphaned provider session directory binding", { | |
| 611 | threadId: thread.id, | |
| 612 | cause, | |
| 613 | }), | |
| 616 | 614 | ), |
| 617 | 615 | ); |
| 618 | 616 | |
| ... | ... | @@ -633,13 +631,13 @@ export const reconcileProviderSessions = Effect.gen(function* () { |
| 633 | 631 | }); |
| 634 | 632 | }).pipe( |
| 635 | 633 | Effect.retry({ times: 1 }), |
| 636 | Effect.catchCause((cause) => | |
| 637 | Cause.hasInterrupts(cause) | |
| 638 | ? Effect.failCause(cause) | |
| 639 | : Effect.logWarning("failed to settle orphaned provider session projection", { | |
| 640 | threadId: thread.id, | |
| 641 | cause, | |
| 642 | }), | |
| 634 | Effect.catchCauseIf( | |
| 635 | (cause) => !Cause.hasInterrupts(cause), | |
| 636 | (cause) => | |
| 637 | Effect.logWarning("failed to settle orphaned provider session projection", { | |
| 638 | threadId: thread.id, | |
| 639 | cause, | |
| 640 | }), | |
| 643 | 641 | ), |
| 644 | 642 | ); |
| 645 | 643 | }); |
| ... | ... | @@ -739,10 +737,9 @@ export const reconcileProviderSessions = Effect.gen(function* () { |
| 739 | 737 | yield* settleAsError(ORPHANED_PROVIDER_SESSION_ERROR); |
| 740 | 738 | } |
| 741 | 739 | }).pipe( |
| 742 | Effect.catchCause((cause) => | |
| 743 | Cause.hasInterrupts(cause) | |
| 744 | ? Effect.failCause(cause) | |
| 745 | : Effect.logWarning("provider session startup reconciliation failed", { cause }), | |
| 740 | Effect.catchCauseIf( | |
| 741 | (cause) => !Cause.hasInterrupts(cause), | |
| 742 | (cause) => Effect.logWarning("provider session startup reconciliation failed", { cause }), | |
| 746 | 743 | ), |
| 747 | 744 | ); |
| 748 | 745 | |
| ... | ... | @@ -813,21 +810,20 @@ export const reconcileWorktreeSetups = Effect.gen(function* () { |
| 813 | 810 | createdAt: interruptedAt, |
| 814 | 811 | }) |
| 815 | 812 | .pipe( |
| 816 | Effect.catchCause((cause) => | |
| 817 | Cause.hasInterrupts(cause) | |
| 818 | ? Effect.failCause(cause) | |
| 819 | : Effect.logWarning("failed to settle interrupted worktree setup", { | |
| 820 | threadId, | |
| 821 | cause, | |
| 822 | }), | |
| 813 | Effect.catchCauseIf( | |
| 814 | (cause) => !Cause.hasInterrupts(cause), | |
| 815 | (cause) => | |
| 816 | Effect.logWarning("failed to settle interrupted worktree setup", { | |
| 817 | threadId, | |
| 818 | cause, | |
| 819 | }), | |
| 823 | 820 | ), |
| 824 | 821 | ); |
| 825 | 822 | } |
| 826 | 823 | }).pipe( |
| 827 | Effect.catchCause((cause) => | |
| 828 | Cause.hasInterrupts(cause) | |
| 829 | ? Effect.failCause(cause) | |
| 830 | : Effect.logWarning("worktree setup startup reconciliation failed", { cause }), | |
| 824 | Effect.catchCauseIf( | |
| 825 | (cause) => !Cause.hasInterrupts(cause), | |
| 826 | (cause) => Effect.logWarning("worktree setup startup reconciliation failed", { cause }), | |
| 831 | 827 | ), |
| 832 | 828 | ); |
| 833 | 829 |
apps/server/src/serverRuntimeState.ts+2-2| ... | ... | @@ -142,7 +142,7 @@ export const readPersistedServerRuntimeState = (path: string) => |
| 142 | 142 | cause, |
| 143 | 143 | }), |
| 144 | 144 | ), |
| 145 | onSuccess: (contents) => Effect.succeed(Option.some(contents)), | |
| 145 | onSuccess: (contents) => Effect.succeedSome(contents), | |
| 146 | 146 | }), |
| 147 | 147 | ); |
| 148 | 148 | if (Option.isNone(raw)) { |
| ... | ... | @@ -155,7 +155,7 @@ export const readPersistedServerRuntimeState = (path: string) => |
| 155 | 155 | } |
| 156 | 156 | |
| 157 | 157 | return yield* decodePersistedServerRuntimeState(trimmed).pipe( |
| 158 | Effect.map(Option.some), | |
| 158 | Effect.asSome, | |
| 159 | 159 | Effect.mapError( |
| 160 | 160 | (cause) => |
| 161 | 161 | new ServerRuntimeStateError({ |
apps/server/src/sourceControl/GitHubCli.test.ts+3-3| ... | ... | @@ -155,8 +155,9 @@ describe("GitHubCli.layer", () => { |
| 155 | 155 | capacity: 2, |
| 156 | 156 | timeToLive: "1 minute", |
| 157 | 157 | }); |
| 158 | const results = yield* Effect.all( | |
| 159 | ["github.com", "github.example.test"].map((host, index) => | |
| 158 | const results = yield* Effect.forEach( | |
| 159 | ["github.com", "github.example.test"], | |
| 160 | (host, index) => | |
| 160 | 161 | Cache.get(cache, host).pipe( |
| 161 | 162 | Effect.provideService(GitHubCli.PinnedGitHubCredential, { |
| 162 | 163 | host, |
| ... | ... | @@ -164,7 +165,6 @@ describe("GitHubCli.layer", () => { |
| 164 | 165 | credentialFingerprint: `fingerprint-${index}`, |
| 165 | 166 | }), |
| 166 | 167 | ), |
| 167 | ), | |
| 168 | 168 | { concurrency: 2 }, |
| 169 | 169 | ); |
| 170 | 170 | expect(results.map((result) => result.stdout)).toEqual(["credential-0", "credential-1"]); |
apps/server/src/sourceControl/SourceControlProviderRegistry.ts+3-3| ... | ... | @@ -289,14 +289,14 @@ export const makeWithProviders = Effect.fn("makeSourceControlProviderRegistryWit |
| 289 | 289 | get, |
| 290 | 290 | resolveHandle, |
| 291 | 291 | resolve: (input) => resolveHandle(input).pipe(Effect.map((handle) => handle.provider)), |
| 292 | discover: Effect.all( | |
| 293 | discoverySpecs.map((spec) => | |
| 292 | discover: Effect.forEach( | |
| 293 | discoverySpecs, | |
| 294 | (spec) => | |
| 294 | 295 | probeSourceControlProvider({ |
| 295 | 296 | spec, |
| 296 | 297 | process, |
| 297 | 298 | cwd: config.cwd, |
| 298 | 299 | }), |
| 299 | ), | |
| 300 | 300 | { concurrency: "unbounded" }, |
| 301 | 301 | ), |
| 302 | 302 | }); |
apps/server/src/storageCleanup.ts+3-4| ... | ... | @@ -419,10 +419,9 @@ export const make = Effect.gen(function* () { |
| 419 | 419 | }); |
| 420 | 420 | const worker = yield* makeDrainableWorker(() => |
| 421 | 421 | sweep().pipe( |
| 422 | Effect.catchCause((cause) => | |
| 423 | Cause.hasInterruptsOnly(cause) | |
| 424 | ? Effect.failCause(cause) | |
| 425 | : Effect.logWarning("storage cleanup failed", { cause }), | |
| 422 | Effect.catchCauseIf( | |
| 423 | (cause) => !Cause.hasInterruptsOnly(cause), | |
| 424 | (cause) => Effect.logWarning("storage cleanup failed", { cause }), | |
| 426 | 425 | ), |
| 427 | 426 | ), |
| 428 | 427 | ); |
apps/server/src/telemetry/Identify.ts+2-2| ... | ... | @@ -132,7 +132,7 @@ const readIdentityFile = ( |
| 132 | 132 | filePath: string, |
| 133 | 133 | ) => |
| 134 | 134 | fileSystem.readFileString(filePath).pipe( |
| 135 | Effect.map(Option.some), | |
| 135 | Effect.asSome, | |
| 136 | 136 | Effect.catchTags({ |
| 137 | 137 | PlatformError: (cause) => |
| 138 | 138 | isNotFoundError(cause) |
| ... | ... | @@ -278,7 +278,7 @@ export const getTelemetryIdentifierForHome = Effect.fn("getTelemetryIdentifierFo |
| 278 | 278 | } |
| 279 | 279 | |
| 280 | 280 | const anonymousId = yield* upsertAnonymousId.pipe( |
| 281 | Effect.map(Option.some), | |
| 281 | Effect.asSome, | |
| 282 | 282 | Effect.catchTags({ |
| 283 | 283 | TelemetryIdentityReadError: (error) => |
| 284 | 284 | logTelemetryIdentityError(error).pipe(Effect.as(Option.none<string>())), |
apps/server/src/terminal/Manager.ts+3-12| ... | ... | @@ -1934,16 +1934,7 @@ export const makeWithOptions = Effect.fn("TerminalManager.makeWithOptions")(func |
| 1934 | 1934 | terminalId: string, |
| 1935 | 1935 | ): Effect.fn.Return<TerminalSessionState, TerminalSessionLookupError> { |
| 1936 | 1936 | return yield* Effect.flatMap(getSession(threadId, terminalId), (session) => |
| 1937 | Option.match(session, { | |
| 1938 | onNone: () => | |
| 1939 | Effect.fail( | |
| 1940 | new TerminalSessionLookupError({ | |
| 1941 | threadId, | |
| 1942 | terminalId, | |
| 1943 | }), | |
| 1944 | ), | |
| 1945 | onSome: Effect.succeed, | |
| 1946 | }), | |
| 1937 | Effect.fromOption(session, () => new TerminalSessionLookupError({ threadId, terminalId })), | |
| 1947 | 1938 | ); |
| 1948 | 1939 | }); |
| 1949 | 1940 | |
| ... | ... | @@ -2367,7 +2358,7 @@ export const makeWithOptions = Effect.fn("TerminalManager.makeWithOptions")(func |
| 2367 | 2358 | } |
| 2368 | 2359 | |
| 2369 | 2360 | const inspectorOption = yield* acquireSubprocessInspector.pipe( |
| 2370 | Effect.map(Option.some), | |
| 2361 | Effect.asSome, | |
| 2371 | 2362 | Effect.catch((reason) => |
| 2372 | 2363 | Effect.logWarning("failed to snapshot processes for terminal subprocess polling", { |
| 2373 | 2364 | reason, |
| ... | ... | @@ -2393,7 +2384,7 @@ export const makeWithOptions = Effect.fn("TerminalManager.makeWithOptions")(func |
| 2393 | 2384 | ) { |
| 2394 | 2385 | const terminalPid = session.pid; |
| 2395 | 2386 | const inspectResult = yield* subprocessInspector(terminalPid).pipe( |
| 2396 | Effect.map(Option.some), | |
| 2387 | Effect.asSome, | |
| 2397 | 2388 | Effect.catch((reason) => |
| 2398 | 2389 | Effect.logWarning("failed to check terminal subprocess activity", { |
| 2399 | 2390 | threadId: session.threadId, |
apps/server/src/textGeneration/AntigravityTextGeneration.ts+1-1| ... | ... | @@ -238,7 +238,7 @@ export const makeAntigravityTextGeneration = Effect.fn("makeAntigravityTextGener |
| 238 | 238 | yield* applyAntigravityAcpModelSelection({ |
| 239 | 239 | runtime, |
| 240 | 240 | model: input.modelSelection.model, |
| 241 | defaultModel: yield* options.defaultModel ?? Effect.succeed(undefined), | |
| 241 | defaultModel: yield* options.defaultModel ?? Effect.undefined, | |
| 242 | 242 | mapError: (cause) => |
| 243 | 243 | new TextGenerationError({ |
| 244 | 244 | operation, |
apps/server/src/textGeneration/CodexTextGeneration.test.ts+2-2| ... | ... | @@ -556,7 +556,7 @@ it.layer(CodexTextGenerationTestLayer)("CodexTextGeneration", (it) => { |
| 556 | 556 | }), |
| 557 | 557 | ), |
| 558 | 558 | ), |
| 559 | Effect.ensuring(fs.remove(imagePath).pipe(Effect.catch(() => Effect.void))), | |
| 559 | Effect.ensuring(fs.remove(imagePath).pipe(Effect.ignore)), | |
| 560 | 560 | ); |
| 561 | 561 | |
| 562 | 562 | expect(generated.branch).toBe("fix/ui-regression"); |
| ... | ... | @@ -579,7 +579,7 @@ it.layer(CodexTextGenerationTestLayer)("CodexTextGeneration", (it) => { |
| 579 | 579 | const { attachmentsDir } = yield* ServerConfig.ServerConfig; |
| 580 | 580 | const missingAttachmentId = "thread-missing-attachment"; |
| 581 | 581 | const missingPath = path.join(attachmentsDir, `${missingAttachmentId}.png`); |
| 582 | yield* fs.remove(missingPath).pipe(Effect.catch(() => Effect.void)); | |
| 582 | yield* fs.remove(missingPath).pipe(Effect.ignore); | |
| 583 | 583 | |
| 584 | 584 | const result = yield* textGeneration |
| 585 | 585 | .generateBranchName({ |
apps/server/src/textGeneration/CodexTextGeneration.ts+4-3| ... | ... | @@ -96,7 +96,7 @@ export const makeCodexTextGeneration = Effect.fn("makeCodexTextGeneration")(func |
| 96 | 96 | ); |
| 97 | 97 | |
| 98 | 98 | const safeUnlink = (filePath: string): Effect.Effect<void, never> => |
| 99 | fileSystem.remove(filePath).pipe(Effect.catch(() => Effect.void)); | |
| 99 | fileSystem.remove(filePath).pipe(Effect.ignore); | |
| 100 | 100 | |
| 101 | 101 | const encodeJsonForOperation = ( |
| 102 | 102 | operation: |
| ... | ... | @@ -263,8 +263,9 @@ export const makeCodexTextGeneration = Effect.fn("makeCodexTextGeneration")(func |
| 263 | 263 | } |
| 264 | 264 | }); |
| 265 | 265 | |
| 266 | const cleanup = Effect.all( | |
| 267 | [schemaPath, outputPath, ...cleanupPaths].map((filePath) => safeUnlink(filePath)), | |
| 266 | const cleanup = Effect.forEach( | |
| 267 | [schemaPath, outputPath, ...cleanupPaths], | |
| 268 | (filePath) => safeUnlink(filePath), | |
| 268 | 269 | { |
| 269 | 270 | concurrency: "unbounded", |
| 270 | 271 | }, |
apps/server/src/textGeneration/ThreadTitleLinks.ts+4-7| ... | ... | @@ -14,12 +14,9 @@ export const resolveThreadTitleLinks = Effect.fn("resolveThreadTitleLinks")(func |
| 14 | 14 | const providers = yield* SourceControlProviderRegistry.SourceControlProviderRegistry; |
| 15 | 15 | const links = new Map<string, NonNullable<ReturnType<typeof providers.resolveLink>>>(); |
| 16 | 16 | for (const match of input.message.matchAll(/https:\/\/[^\s<>"')\]`]+/g)) { |
| 17 | let url: URL; | |
| 18 | try { | |
| 19 | url = new URL(match[0].replace(/[.,;!?]+$/, "")); | |
| 20 | } catch { | |
| 21 | continue; | |
| 22 | } | |
| 17 | const candidate = match[0].replace(/[.,;!?]+$/, ""); | |
| 18 | if (!URL.canParse(candidate)) continue; | |
| 19 | const url = new URL(candidate); | |
| 23 | 20 | url.hash = ""; |
| 24 | 21 | url.search = ""; |
| 25 | 22 | if (links.has(url.href)) continue; |
| ... | ... | @@ -40,7 +37,7 @@ export const resolveThreadTitleLinks = Effect.fn("resolveThreadTitleLinks")(func |
| 40 | 37 | ), |
| 41 | 38 | Effect.map((summary) => `${url}\n${summary}`), |
| 42 | 39 | Effect.timeout("3 seconds"), |
| 43 | Effect.catch(() => Effect.succeed(`${url}: unavailable`)), | |
| 40 | Effect.orElseSucceed(() => `${url}: unavailable`), | |
| 44 | 41 | ), |
| 45 | 42 | { concurrency: 2 }, |
| 46 | 43 | ); |
apps/server/src/usage/UsageService.ts+2-2| ... | ... | @@ -225,7 +225,7 @@ export const make = Effect.gen(function* () { |
| 225 | 225 | |
| 226 | 226 | yield* encodeRatesCache({ fetchedAtMs: now, document: fetched }).pipe( |
| 227 | 227 | Effect.flatMap((serialized) => fileSystem.writeFileString(ratesCachePath, serialized)), |
| 228 | Effect.catchCause(() => Effect.void), | |
| 228 | Effect.ignoreCause, | |
| 229 | 229 | ); |
| 230 | 230 | }); |
| 231 | 231 | |
| ... | ... | @@ -372,7 +372,7 @@ export const make = Effect.gen(function* () { |
| 372 | 372 | cacheDirty = false; |
| 373 | 373 | }), |
| 374 | 374 | // A cache we cannot write is a slower next start, not a failed read. |
| 375 | Effect.catchCause(() => Effect.void), | |
| 375 | Effect.ignoreCause, | |
| 376 | 376 | ); |
| 377 | 377 | }); |
| 378 | 378 |
apps/server/src/vcs/GitVcsDriver.ts+1-1| ... | ... | @@ -1101,7 +1101,7 @@ export const makeVcsDriverShape = Effect.fn("makeGitVcsDriverShape")(function* ( |
| 1101 | 1101 | /^warning: failed to remove \.\/: [^\n]+$/.test(cleaned.stderr.trim()) && |
| 1102 | 1102 | (yield* fileSystem.readDirectory(input.cwd).pipe( |
| 1103 | 1103 | Effect.map((entries) => entries.length === 0), |
| 1104 | Effect.catch(() => Effect.succeed(false)), | |
| 1104 | Effect.orElseSucceed(() => false), | |
| 1105 | 1105 | )); |
| 1106 | 1106 | if (!emptiedWorkspace) |
| 1107 | 1107 | return yield* new VcsProcessExitError({ |
apps/server/src/vcs/GitVcsDriverCore.ts+12-17| ... | ... | @@ -946,16 +946,14 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* |
| 946 | 946 | return yield* execution.pipe( |
| 947 | 947 | Effect.timeoutOption(timeoutMs), |
| 948 | 948 | Effect.flatMap((result) => |
| 949 | Option.match(result, { | |
| 950 | onNone: () => | |
| 951 | Effect.fail( | |
| 952 | new GitCommandError({ | |
| 953 | ...gitCommandContext(commandInput), | |
| 954 | detail: "Git command timed out.", | |
| 955 | }), | |
| 956 | ), | |
| 957 | onSome: Effect.succeed, | |
| 958 | }), | |
| 949 | Effect.fromOption( | |
| 950 | result, | |
| 951 | () => | |
| 952 | new GitCommandError({ | |
| 953 | ...gitCommandContext(commandInput), | |
| 954 | detail: "Git command timed out.", | |
| 955 | }), | |
| 956 | ), | |
| 959 | 957 | ), |
| 960 | 958 | ); |
| 961 | 959 | }, |
| ... | ... | @@ -1004,11 +1002,9 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* |
| 1004 | 1002 | : {}), |
| 1005 | 1003 | ...(options.progress ? { progress: options.progress } : {}), |
| 1006 | 1004 | }).pipe( |
| 1007 | Effect.flatMap((result) => { | |
| 1008 | if (options.allowNonZeroExit || result.exitCode === 0) { | |
| 1009 | return Effect.succeed(result); | |
| 1010 | } | |
| 1011 | return Effect.fail( | |
| 1005 | Effect.filterOrFail( | |
| 1006 | (result) => options.allowNonZeroExit || result.exitCode === 0, | |
| 1007 | (result) => | |
| 1012 | 1008 | new GitCommandError({ |
| 1013 | 1009 | ...gitCommandContext({ operation, cwd, args }), |
| 1014 | 1010 | detail: options.fallbackErrorDetail ?? "Git command exited with a non-zero status.", |
| ... | ... | @@ -1016,8 +1012,7 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* |
| 1016 | 1012 | stdoutLength: result.stdout.length, |
| 1017 | 1013 | stderrLength: result.stderr.length, |
| 1018 | 1014 | }), |
| 1019 | ); | |
| 1020 | }), | |
| 1015 | ), | |
| 1021 | 1016 | ); |
| 1022 | 1017 | |
| 1023 | 1018 | const executeGitWithStableDiagnostics = ( |
apps/server/src/vcs/VcsStatusBroadcaster.ts+3-3| ... | ... | @@ -541,13 +541,13 @@ export const make = Effect.gen(function* () { |
| 541 | 541 | const demandCwds = yield* Ref.get(demandCwdsRef); |
| 542 | 542 | const shouldRun = |
| 543 | 543 | needsInitialRefresh || |
| 544 | (yield* Effect.all( | |
| 545 | [...demandCwds.keys()].map((demandCwd) => | |
| 544 | (yield* Effect.forEach( | |
| 545 | [...demandCwds.keys()], | |
| 546 | (demandCwd) => | |
| 546 | 547 | backgroundPolicy.shouldRunScopeWork({ |
| 547 | 548 | type: "vcs-status", |
| 548 | 549 | cwd: demandCwd, |
| 549 | 550 | }), |
| 550 | ), | |
| 551 | 551 | { concurrency: "unbounded" }, |
| 552 | 552 | )).some(Boolean); |
| 553 | 553 | if (!shouldRun) { |
apps/server/src/ws.ts+12-16| ... | ... | @@ -854,27 +854,23 @@ const makeWsRpcLayer = ( |
| 854 | 854 | case "project.meta-updated": |
| 855 | 855 | return projectUpsertOrRemove(ProjectId.make(event.aggregateId), event.sequence); |
| 856 | 856 | case "project.deleted": |
| 857 | return Effect.succeed( | |
| 858 | Option.some({ | |
| 859 | kind: "project-removed" as const, | |
| 860 | sequence: event.sequence, | |
| 861 | projectId: ProjectId.make(event.aggregateId), | |
| 862 | }), | |
| 863 | ); | |
| 857 | return Effect.succeedSome({ | |
| 858 | kind: "project-removed" as const, | |
| 859 | sequence: event.sequence, | |
| 860 | projectId: ProjectId.make(event.aggregateId), | |
| 861 | }); | |
| 864 | 862 | case "thread.deleted": |
| 865 | 863 | case "thread.archived": |
| 866 | return Effect.succeed( | |
| 867 | Option.some({ | |
| 868 | kind: "thread-removed" as const, | |
| 869 | sequence: event.sequence, | |
| 870 | threadId: ThreadId.make(event.aggregateId), | |
| 871 | }), | |
| 872 | ); | |
| 864 | return Effect.succeedSome({ | |
| 865 | kind: "thread-removed" as const, | |
| 866 | sequence: event.sequence, | |
| 867 | threadId: ThreadId.make(event.aggregateId), | |
| 868 | }); | |
| 873 | 869 | case "thread.unarchived": |
| 874 | 870 | return threadUpsertOrRemove(ThreadId.make(event.aggregateId), event.sequence); |
| 875 | 871 | default: |
| 876 | 872 | if (event.aggregateKind !== "thread") { |
| 877 | return Effect.succeed(Option.none()); | |
| 873 | return Effect.succeedNone; | |
| 878 | 874 | } |
| 879 | 875 | return threadUpsertOrRemove(ThreadId.make(event.aggregateId), event.sequence); |
| 880 | 876 | } |
| ... | ... | @@ -892,7 +888,7 @@ const makeWsRpcLayer = ( |
| 892 | 888 | ): Effect.Effect<Option.Option<A>, never, never> => |
| 893 | 889 | read.pipe( |
| 894 | 890 | Effect.retry({ times: 1 }), |
| 895 | Effect.map(Option.some), | |
| 891 | Effect.asSome, | |
| 896 | 892 | Effect.tapError((error) => |
| 897 | 893 | Effect.logWarning("orchestration shell projection refetch failed", { |
| 898 | 894 | aggregateKind, |
apps/web/src/connection/platform.ts+1-1| ... | ... | @@ -213,7 +213,7 @@ const capabilitiesLayer = Layer.effectContext( |
| 213 | 213 | }), |
| 214 | 214 | }); |
| 215 | 215 | const identity = RelayDeviceIdentity.of({ |
| 216 | deviceId: Effect.succeed(Option.none()), | |
| 216 | deviceId: Effect.succeedNone, | |
| 217 | 217 | }); |
| 218 | 218 | const primaryAuth = PrimaryEnvironmentAuth.of({ |
| 219 | 219 | bearerToken: Effect.tryPromise({ |
apps/web/src/connection/storage.ts+4-4| ... | ... | @@ -587,7 +587,7 @@ export const connectionStorageLayer = Layer.effectContext( |
| 587 | 587 | Effect.tap(() => Effect.promise(() => projectFaviconCache.hydrate())), |
| 588 | 588 | Effect.flatMap((raw) => { |
| 589 | 589 | if (typeof raw !== "string") { |
| 590 | return Effect.succeed(Option.none()); | |
| 590 | return Effect.succeedNone; | |
| 591 | 591 | } |
| 592 | 592 | return decodeStoredShellSnapshot(raw).pipe( |
| 593 | 593 | Effect.mapError((cause) => persistenceError("load-shell", cause)), |
| ... | ... | @@ -623,7 +623,7 @@ export const connectionStorageLayer = Layer.effectContext( |
| 623 | 623 | readDatabaseValue(database, SERVER_CONFIG_STORE_NAME, environmentId).pipe( |
| 624 | 624 | Effect.flatMap((raw) => { |
| 625 | 625 | if (typeof raw !== "string") { |
| 626 | return Effect.succeed(Option.none()); | |
| 626 | return Effect.succeedNone; | |
| 627 | 627 | } |
| 628 | 628 | return decodeStoredServerConfig(raw).pipe( |
| 629 | 629 | Effect.mapError((cause) => persistenceError("load-server-config", cause)), |
| ... | ... | @@ -661,7 +661,7 @@ export const connectionStorageLayer = Layer.effectContext( |
| 661 | 661 | ).pipe( |
| 662 | 662 | Effect.flatMap((raw) => { |
| 663 | 663 | if (typeof raw !== "string") { |
| 664 | return Effect.succeed(Option.none()); | |
| 664 | return Effect.succeedNone; | |
| 665 | 665 | } |
| 666 | 666 | return decodeStoredThreadSnapshot(raw).pipe( |
| 667 | 667 | Effect.mapError((cause) => persistenceError("load-thread", cause)), |
| ... | ... | @@ -703,7 +703,7 @@ export const connectionStorageLayer = Layer.effectContext( |
| 703 | 703 | readDatabaseValue(database, VCS_REFS_STORE_NAME, vcsRefsCacheKey(environmentId, cwd)).pipe( |
| 704 | 704 | Effect.flatMap((raw) => { |
| 705 | 705 | if (typeof raw !== "string") { |
| 706 | return Effect.succeed(Option.none()); | |
| 706 | return Effect.succeedNone; | |
| 707 | 707 | } |
| 708 | 708 | return decodeStoredVcsRefs(raw).pipe( |
| 709 | 709 | Effect.mapError((cause) => persistenceError("load-vcs-refs", cause)), |
infra/relay/src/agentActivity/LiveActivities.test.ts+3-2| ... | ... | @@ -276,7 +276,7 @@ describe("LiveActivities", () => { |
| 276 | 276 | liveActivities.register({ userId: "user-1", registration }), |
| 277 | 277 | ); |
| 278 | 278 | const targetListError = yield* Effect.flip(liveActivities.listTargets({ userId: "user-1" })); |
| 279 | const deliveryErrors = yield* Effect.all( | |
| 279 | const deliveryErrors = yield* Effect.forEach( | |
| 280 | 280 | [ |
| 281 | 281 | liveActivities.markDelivery({ |
| 282 | 282 | userId: "user-1", |
| ... | ... | @@ -297,7 +297,8 @@ describe("LiveActivities", () => { |
| 297 | 297 | kind: "push_notification", |
| 298 | 298 | invalidatedAt: "2026-05-25T00:00:10.000Z", |
| 299 | 299 | }), |
| 300 | ].map(Effect.flip), | |
| 300 | ], | |
| 301 | (effect) => Effect.flip(effect), | |
| 301 | 302 | { concurrency: 1 }, |
| 302 | 303 | ); |
| 303 | 304 |
infra/relay/src/clientConfig.test.ts+7-31| ... | ... | @@ -82,13 +82,7 @@ describe("PublishClientConfig", () => { |
| 82 | 82 | ), |
| 83 | 83 | ); |
| 84 | 84 | |
| 85 | yield* stack | |
| 86 | .deploy( | |
| 87 | Effect.gen(function* () { | |
| 88 | return yield* PublishClientConfig(clientConfig("v1")); | |
| 89 | }), | |
| 90 | ) | |
| 91 | .pipe(configured); | |
| 85 | yield* stack.deploy(PublishClientConfig(clientConfig("v1"))).pipe(configured); | |
| 92 | 86 | const first = yield* fs.readFileString(target); |
| 93 | 87 | expect(first).toContain("KEEP=yes\n"); |
| 94 | 88 | expect(first).toContain("T3CODE_RELAY_URL=https://relay.example.com\n"); |
| ... | ... | @@ -97,23 +91,11 @@ describe("PublishClientConfig", () => { |
| 97 | 91 | |
| 98 | 92 | // Same input: the action is skipped, so a change made by hand survives. |
| 99 | 93 | yield* fs.writeFileString(target, `${first}MANUAL=1\n`); |
| 100 | yield* stack | |
| 101 | .deploy( | |
| 102 | Effect.gen(function* () { | |
| 103 | return yield* PublishClientConfig(clientConfig("v1")); | |
| 104 | }), | |
| 105 | ) | |
| 106 | .pipe(configured); | |
| 94 | yield* stack.deploy(PublishClientConfig(clientConfig("v1"))).pipe(configured); | |
| 107 | 95 | expect(yield* fs.readFileString(target)).toContain("MANUAL=1\n"); |
| 108 | 96 | |
| 109 | 97 | // A rotated token changes the input, so it runs again and replaces the line. |
| 110 | yield* stack | |
| 111 | .deploy( | |
| 112 | Effect.gen(function* () { | |
| 113 | return yield* PublishClientConfig(clientConfig("v2")); | |
| 114 | }), | |
| 115 | ) | |
| 116 | .pipe(configured); | |
| 98 | yield* stack.deploy(PublishClientConfig(clientConfig("v2"))).pipe(configured); | |
| 117 | 99 | const third = yield* fs.readFileString(target); |
| 118 | 100 | expect(third).toContain("T3CODE_RELAY_CLIENT_OTLP_TRACES_TOKEN=client-v2\n"); |
| 119 | 101 | expect(third).not.toContain("client-v1"); |
| ... | ... | @@ -128,11 +110,7 @@ describe("PublishClientConfig", () => { |
| 128 | 110 | const dir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-relay-client-config-" }); |
| 129 | 111 | const target = path.join(dir, "client.env"); |
| 130 | 112 | const exit = yield* stack |
| 131 | .deploy( | |
| 132 | Effect.gen(function* () { | |
| 133 | return yield* PublishClientConfig({ ...clientConfig("v1"), url: undefined }); | |
| 134 | }), | |
| 135 | ) | |
| 113 | .deploy(PublishClientConfig({ ...clientConfig("v1"), url: undefined })) | |
| 136 | 114 | .pipe( |
| 137 | 115 | Effect.provide( |
| 138 | 116 | ConfigProvider.layer( |
| ... | ... | @@ -155,11 +133,9 @@ describe("PublishClientConfig", () => { |
| 155 | 133 | const target = path.join(dir, "client.env"); |
| 156 | 134 | const exit = yield* stack |
| 157 | 135 | .deploy( |
| 158 | Effect.gen(function* () { | |
| 159 | return yield* PublishClientConfig({ | |
| 160 | ...clientConfig("v1"), | |
| 161 | clientTracingDataset: "relay-traces\nINJECTED=1", | |
| 162 | }); | |
| 136 | PublishClientConfig({ | |
| 137 | ...clientConfig("v1"), | |
| 138 | clientTracingDataset: "relay-traces\nINJECTED=1", | |
| 163 | 139 | }), |
| 164 | 140 | ) |
| 165 | 141 | .pipe( |
infra/relay/src/environments/ManagedEndpointAllocations.ts+1-3| ... | ... | @@ -567,9 +567,7 @@ export const make = Effect.gen(function* () { |
| 567 | 567 | .pipe( |
| 568 | 568 | Effect.mapError(lockError), |
| 569 | 569 | Effect.flatMap((rows) => |
| 570 | rows.length === 0 | |
| 571 | ? Effect.succeed(Option.none()) | |
| 572 | : effect.pipe(Effect.map(Option.some)), | |
| 570 | rows.length === 0 ? Effect.succeedNone : Effect.asSome(effect), | |
| 573 | 571 | ), |
| 574 | 572 | ), |
| 575 | 573 | ) |
infra/relay/src/environments/ManagedEndpointProvider.test.ts+5-8| ... | ... | @@ -6,7 +6,6 @@ import * as Alchemy from "alchemy"; |
| 6 | 6 | import * as Cloudflare from "alchemy/Cloudflare"; |
| 7 | 7 | import * as Effect from "effect/Effect"; |
| 8 | 8 | import * as Layer from "effect/Layer"; |
| 9 | import * as Option from "effect/Option"; | |
| 10 | 9 | import * as Redacted from "effect/Redacted"; |
| 11 | 10 | |
| 12 | 11 | import * as RelayConfiguration from "../Config.ts"; |
| ... | ... | @@ -302,8 +301,8 @@ function makeAllocations(calls: AllocationCall[] = []) { |
| 302 | 301 | Effect.suspend(() => { |
| 303 | 302 | const current = allocations.get(allocationKey(input)); |
| 304 | 303 | return current?.tunnelId === input.tunnelId && current.generation === input.generation |
| 305 | ? effect.pipe(Effect.map(Option.some)) | |
| 306 | : Effect.succeed(Option.none()); | |
| 304 | ? Effect.asSome(effect) | |
| 305 | : Effect.succeedNone; | |
| 307 | 306 | }), |
| 308 | 307 | claimDeprovision: (input) => |
| 309 | 308 | Effect.sync(() => { |
| ... | ... | @@ -1189,7 +1188,7 @@ describe("ManagedEndpointProvider", () => { |
| 1189 | 1188 | const changed = ManagedEndpointAllocations.ManagedEndpointAllocations.of({ |
| 1190 | 1189 | ...allocations, |
| 1191 | 1190 | withClaimedTunnel: (input, effect) => |
| 1192 | loseClaim ? Effect.succeed(Option.none()) : allocations.withClaimedTunnel(input, effect), | |
| 1191 | loseClaim ? Effect.succeedNone : allocations.withClaimedTunnel(input, effect), | |
| 1193 | 1192 | }); |
| 1194 | 1193 | const layer = providerLayer(makePersistentTunnelClient(), makeDnsClient(), changed); |
| 1195 | 1194 | |
| ... | ... | @@ -1320,7 +1319,7 @@ describe("ManagedEndpointProvider", () => { |
| 1320 | 1319 | const allocations = makeAllocations(); |
| 1321 | 1320 | const changed = ManagedEndpointAllocations.ManagedEndpointAllocations.of({ |
| 1322 | 1321 | ...allocations, |
| 1323 | withClaimedTunnel: () => Effect.succeed(Option.none()), | |
| 1322 | withClaimedTunnel: () => Effect.succeedNone, | |
| 1324 | 1323 | }); |
| 1325 | 1324 | const layer = providerLayer(makePersistentTunnelClient(tunnelCalls), makeDnsClient(), changed); |
| 1326 | 1325 | |
| ... | ... | @@ -1349,9 +1348,7 @@ describe("ManagedEndpointProvider", () => { |
| 1349 | 1348 | const changed = ManagedEndpointAllocations.ManagedEndpointAllocations.of({ |
| 1350 | 1349 | ...allocations, |
| 1351 | 1350 | withClaimedTunnel: (input, effect) => |
| 1352 | ++lockCount === 1 | |
| 1353 | ? allocations.withClaimedTunnel(input, effect) | |
| 1354 | : Effect.succeed(Option.none()), | |
| 1351 | ++lockCount === 1 ? allocations.withClaimedTunnel(input, effect) : Effect.succeedNone, | |
| 1355 | 1352 | }); |
| 1356 | 1353 | const layer = providerLayer(makePersistentTunnelClient(), makeDnsClient(dnsCalls), changed); |
| 1357 | 1354 |
infra/relay/src/environments/ManagedEndpointProvider.ts+17-16| ... | ... | @@ -471,8 +471,9 @@ export const make = Effect.gen(function* () { |
| 471 | 471 | ? updateExistingDnsRecords(records, preferredDnsRecordId, dnsRecord) |
| 472 | 472 | : Effect.fail(createError), |
| 473 | 473 | ), |
| 474 | Effect.flatMap((dnsRecordId) => | |
| 475 | dnsRecordId === null ? Effect.fail(createError) : Effect.succeed(dnsRecordId), | |
| 474 | Effect.filterOrFail( | |
| 475 | (dnsRecordId) => dnsRecordId !== null, | |
| 476 | () => createError, | |
| 476 | 477 | ), |
| 477 | 478 | ), |
| 478 | 479 | }), |
| ... | ... | @@ -578,19 +579,19 @@ export const make = Effect.gen(function* () { |
| 578 | 579 | ? Effect.succeed("missing" as const) |
| 579 | 580 | : Effect.fail(error), |
| 580 | 581 | }), |
| 581 | Effect.flatMap((result) => | |
| 582 | result === "missing" | |
| 583 | ? Effect.succeed(result) | |
| 584 | : allocations | |
| 585 | .markReady({ | |
| 586 | ...input, | |
| 587 | generation: allocation.generation, | |
| 588 | }) | |
| 589 | .pipe( | |
| 590 | Effect.map((updated) => | |
| 591 | updated ? ("configured" as const) : ("stale" as const), | |
| 592 | ), | |
| 582 | Effect.filterOrElse( | |
| 583 | (result): result is "missing" => result === "missing", | |
| 584 | () => | |
| 585 | allocations | |
| 586 | .markReady({ | |
| 587 | ...input, | |
| 588 | generation: allocation.generation, | |
| 589 | }) | |
| 590 | .pipe( | |
| 591 | Effect.map((updated) => | |
| 592 | updated ? ("configured" as const) : ("stale" as const), | |
| 593 | 593 | ), |
| 594 | ), | |
| 594 | 595 | ), |
| 595 | 596 | ), |
| 596 | 597 | ) |
| ... | ... | @@ -791,11 +792,11 @@ export const make = Effect.gen(function* () { |
| 791 | 792 | const expectedStatus = input.expectedStatus; |
| 792 | 793 | const inactiveBefore = input.expectedInactiveBefore; |
| 793 | 794 | const currentTunnel = yield* tunnels.get(tunnelId).pipe( |
| 794 | Effect.map(Option.some), | |
| 795 | Effect.asSome, | |
| 795 | 796 | Effect.catchTags({ |
| 796 | 797 | ManagedEndpointTunnelClientError: (cause) => |
| 797 | 798 | isManagedEndpointNotFound(cause.cause) |
| 798 | ? Effect.succeed(Option.none()) | |
| 799 | ? Effect.succeedNone | |
| 799 | 800 | : Effect.fail( |
| 800 | 801 | new ManagedEndpointDeprovisioningFailed({ |
| 801 | 802 | ...input, |
packages/client-runtime/src/authorization/layer.test.ts+1-1| ... | ... | @@ -199,7 +199,7 @@ const makeHarness = Effect.fn("TestRemoteAuthorization.makeHarness")(function* ( |
| 199 | 199 | clerkToken: input.clerkToken ?? Effect.succeed("clerk-session"), |
| 200 | 200 | }), |
| 201 | 201 | Layer.succeed(ClientCapabilities.RelayDeviceIdentity, { |
| 202 | deviceId: Effect.succeed(Option.some("device-1")), | |
| 202 | deviceId: Effect.succeedSome("device-1"), | |
| 203 | 203 | }), |
| 204 | 204 | Layer.succeed(TokenStore.RemoteDpopAccessTokenStore, tokenStore), |
| 205 | 205 | Layer.succeed( |
packages/client-runtime/src/connection/registry.test.ts+3-3| ... | ... | @@ -275,12 +275,12 @@ const makeHarness = Effect.fn("TestEnvironmentRegistry.makeHarness")(function* ( |
| 275 | 275 | next.set(environmentId, snapshot); |
| 276 | 276 | return next; |
| 277 | 277 | }), |
| 278 | loadThread: (_environmentId, _threadId) => Effect.succeed(Option.none()), | |
| 278 | loadThread: (_environmentId, _threadId) => Effect.succeedNone, | |
| 279 | 279 | saveThread: (_environmentId, _thread) => Effect.void, |
| 280 | 280 | removeThread: (_environmentId, _threadId) => Effect.void, |
| 281 | loadServerConfig: () => Effect.succeed(Option.none()), | |
| 281 | loadServerConfig: () => Effect.succeedNone, | |
| 282 | 282 | saveServerConfig: () => Effect.void, |
| 283 | loadVcsRefs: () => Effect.succeed(Option.none()), | |
| 283 | loadVcsRefs: () => Effect.succeedNone, | |
| 284 | 284 | saveVcsRefs: () => Effect.void, |
| 285 | 285 | removeVcsRefs: () => Effect.void, |
| 286 | 286 | clearVcsRefs: () => Effect.void, |
packages/client-runtime/src/connection/resolver.ts+6-8| ... | ... | @@ -110,10 +110,9 @@ const makeBearerBroker = Effect.fn("clientRuntime.connection.broker.makeBearer") |
| 110 | 110 | entry: ConnectionCatalogEntry & { readonly target: BearerConnectionTarget }, |
| 111 | 111 | ) { |
| 112 | 112 | const target = entry.target; |
| 113 | const profile = yield* Option.match(entry.profile, { | |
| 114 | onNone: () => Effect.fail(profileMissingError(target.connectionId)), | |
| 115 | onSome: Effect.succeed, | |
| 116 | }); | |
| 113 | const profile = yield* Effect.fromOption(entry.profile, () => | |
| 114 | profileMissingError(target.connectionId), | |
| 115 | ); | |
| 117 | 116 | if (!isBearerProfile(profile)) { |
| 118 | 117 | return yield* new ConnectionBlockedError({ |
| 119 | 118 | reason: "configuration", |
| ... | ... | @@ -186,10 +185,9 @@ const makeSshBroker = Effect.fn("clientRuntime.connection.broker.makeSsh")(funct |
| 186 | 185 | entry: ConnectionCatalogEntry & { readonly target: SshConnectionTarget }, |
| 187 | 186 | ) { |
| 188 | 187 | const target = entry.target; |
| 189 | const profile = yield* Option.match(entry.profile, { | |
| 190 | onNone: () => Effect.fail(profileMissingError(target.connectionId)), | |
| 191 | onSome: Effect.succeed, | |
| 192 | }); | |
| 188 | const profile = yield* Effect.fromOption(entry.profile, () => | |
| 189 | profileMissingError(target.connectionId), | |
| 190 | ); | |
| 193 | 191 | if (!isSshProfile(profile)) { |
| 194 | 192 | return yield* new ConnectionBlockedError({ |
| 195 | 193 | reason: "configuration", |
packages/client-runtime/src/connection/supervisor.test.ts+2-2| ... | ... | @@ -1290,11 +1290,11 @@ describe("EnvironmentSupervisor", () => { |
| 1290 | 1290 | Layer.succeed(ManagedRelayDpopSigner, signer), |
| 1291 | 1291 | Layer.succeed(ManagedRelayClient, relay), |
| 1292 | 1292 | Layer.succeed(ClientCapabilities.CloudSession, { |
| 1293 | identity: Effect.succeed(Option.some({ accountId: "test-account" })), | |
| 1293 | identity: Effect.succeedSome({ accountId: "test-account" }), | |
| 1294 | 1294 | clerkToken: Effect.succeed("clerk-token"), |
| 1295 | 1295 | }), |
| 1296 | 1296 | Layer.succeed(ClientCapabilities.RelayDeviceIdentity, { |
| 1297 | deviceId: Effect.succeed(Option.none()), | |
| 1297 | deviceId: Effect.succeedNone, | |
| 1298 | 1298 | }), |
| 1299 | 1299 | TokenStore.layer({ |
| 1300 | 1300 | get: () => Ref.get(token), |
packages/client-runtime/src/connection/supervisor.ts+1| ... | ... | @@ -606,6 +606,7 @@ export const make = Effect.fn("EnvironmentSupervisor.make")(function* ( |
| 606 | 606 | }, Effect.ensuring(clearLease)); |
| 607 | 607 | |
| 608 | 608 | const waitForRetrySignal = Effect.fnUntraced(function* (delayMs: number) { |
| 609 | // @effect-diagnostics-next-line raceFirstWithSleepToTimeout:off - the sleep is the retry delay (false), not a timeout around the signal loop | |
| 609 | 610 | return yield* Effect.raceFirst( |
| 610 | 611 | Effect.sleep(delayMs).pipe(Effect.as(false)), |
| 611 | 612 | Effect.gen(function* () { |
packages/client-runtime/src/platform/storageDocument.test.ts+1-1| ... | ... | @@ -207,7 +207,7 @@ describe("ConnectionCatalogDocument", () => { |
| 207 | 207 | remoteDpopTokens: [token], |
| 208 | 208 | }; |
| 209 | 209 | const schema = Schema.fromJsonString(ConnectionCatalogDocument); |
| 210 | const restored = Schema.decodeUnknownSync(schema)(Schema.encodeSync(schema)(document)); | |
| 210 | const restored = Schema.decodeSync(schema)(Schema.encodeSync(schema)(document)); | |
| 211 | 211 | |
| 212 | 212 | expect(restored).toEqual(document); |
| 213 | 213 | expect(restored.remoteDpopTokens[0]?.accountId).toBe(accountId); |
packages/client-runtime/src/relay/discovery.test.ts+8-10| ... | ... | @@ -134,15 +134,13 @@ const makeHarness = Effect.fn("RelayDiscoveryTest.makeHarness")(function* () { |
| 134 | 134 | ), |
| 135 | 135 | ), |
| 136 | 136 | clerkToken: Ref.get(clerkToken).pipe( |
| 137 | Effect.flatMap((token) => | |
| 138 | token === null | |
| 139 | ? Effect.fail( | |
| 140 | new ConnectionBlockedError({ | |
| 141 | reason: "authentication", | |
| 142 | detail: "Signed out.", | |
| 143 | }), | |
| 144 | ) | |
| 145 | : Effect.succeed(token), | |
| 137 | Effect.filterOrFail( | |
| 138 | (token) => token !== null, | |
| 139 | () => | |
| 140 | new ConnectionBlockedError({ | |
| 141 | reason: "authentication", | |
| 142 | detail: "Signed out.", | |
| 143 | }), | |
| 146 | 144 | ), |
| 147 | 145 | ), |
| 148 | 146 | }), |
| ... | ... | @@ -289,7 +287,7 @@ describe("RelayEnvironmentDiscovery", () => { |
| 289 | 287 | Layer.mergeAll( |
| 290 | 288 | Layer.succeed(ManagedRelay.ManagedRelayClient, client), |
| 291 | 289 | Layer.succeed(ClientCapabilities.CloudSession, { |
| 292 | identity: Effect.succeed(Option.some({ accountId: "account-1" })), | |
| 290 | identity: Effect.succeedSome({ accountId: "account-1" }), | |
| 293 | 291 | clerkToken: Effect.succeed("clerk-token"), |
| 294 | 292 | }), |
| 295 | 293 | Layer.succeed(Connectivity.Connectivity, { |
packages/client-runtime/src/relay/discovery.ts+1-1| ... | ... | @@ -241,7 +241,7 @@ export const make = Effect.fn("RelayEnvironmentDiscovery.make")(function* () { |
| 241 | 241 | })); |
| 242 | 242 | return; |
| 243 | 243 | } |
| 244 | return yield* Effect.fail(failure); | |
| 244 | return yield* failure; | |
| 245 | 245 | } |
| 246 | 246 | const clerkToken = tokenResult.success; |
| 247 | 247 | if ((yield* Ref.get(accountGeneration)) !== generation) { |
packages/client-runtime/src/relay/managedRelay.ts+4-7| ... | ... | @@ -666,11 +666,8 @@ export const make = Effect.fn("ManagedRelayClient.make")(function* ( |
| 666 | 666 | authorize(input).pipe( |
| 667 | 667 | Effect.flatMap((authorization) => |
| 668 | 668 | request(authorization).pipe( |
| 669 | Effect.catch((error) => { | |
| 670 | if (!isRejectedDpopAccessToken(error)) { | |
| 671 | return Effect.fail(error); | |
| 672 | } | |
| 673 | return invalidateAccessToken(authorization.accessToken).pipe( | |
| 669 | Effect.catchIf(isRejectedDpopAccessToken, (error) => | |
| 670 | invalidateAccessToken(authorization.accessToken).pipe( | |
| 674 | 671 | Effect.tap((invalidated) => |
| 675 | 672 | Effect.annotateCurrentSpan({ |
| 676 | 673 | "relay.token_cache.invalidated": invalidated, |
| ... | ... | @@ -686,8 +683,8 @@ export const make = Effect.fn("ManagedRelayClient.make")(function* ( |
| 686 | 683 | : Effect.void, |
| 687 | 684 | ), |
| 688 | 685 | Effect.andThen(refreshRejectedToken ? attempt(false) : Effect.fail(error)), |
| 689 | ); | |
| 690 | }), | |
| 686 | ), | |
| 687 | ), | |
| 691 | 688 | ), |
| 692 | 689 | ), |
| 693 | 690 | ); |
packages/client-runtime/src/relay/managedRelayState.ts+6-8| ... | ... | @@ -180,14 +180,12 @@ function readSessionClerkToken( |
| 180 | 180 | session: ManagedRelaySession, |
| 181 | 181 | ): Effect.Effect<string, ManagedRelaySessionError> { |
| 182 | 182 | return session.readClerkToken().pipe( |
| 183 | Effect.flatMap((token) => | |
| 184 | token | |
| 185 | ? Effect.succeed(token) | |
| 186 | : Effect.fail( | |
| 187 | new ManagedRelaySessionError({ | |
| 188 | message: "The T3 Connect session token is unavailable.", | |
| 189 | }), | |
| 190 | ), | |
| 183 | Effect.filterOrFail( | |
| 184 | (token): token is string => Boolean(token), | |
| 185 | () => | |
| 186 | new ManagedRelaySessionError({ | |
| 187 | message: "The T3 Connect session token is unavailable.", | |
| 188 | }), | |
| 191 | 189 | ), |
| 192 | 190 | ); |
| 193 | 191 | } |
packages/client-runtime/src/rpc/session.test.ts+5-5| ... | ... | @@ -161,7 +161,7 @@ const RpcRequest = Schema.TaggedStruct("Request", { |
| 161 | 161 | }); |
| 162 | 162 | const decodeJson = Schema.decodeUnknownSync(Schema.fromJsonString(Schema.Unknown)); |
| 163 | 163 | const isRpcRequest = Schema.is(RpcRequest); |
| 164 | const isPing = Schema.is(Schema.Struct({ _tag: Schema.Literal("Ping") })); | |
| 164 | const isPing = Schema.is(Schema.TaggedStruct("Ping", {})); | |
| 165 | 165 | const encodeJson = Schema.encodeUnknownSync(Schema.fromJsonString(Schema.Unknown)); |
| 166 | 166 | const encodeServerConfig = Schema.encodeSync(ServerConfig); |
| 167 | 167 | const encodeServerConfigStreamEvent = Schema.encodeSync(ServerConfigStreamEvent); |
| ... | ... | @@ -823,14 +823,14 @@ describe("RpcSessionFactory", () => { |
| 823 | 823 | retryNow: Effect.void, |
| 824 | 824 | } satisfies EnvironmentSupervisor.EnvironmentSupervisor["Service"]); |
| 825 | 825 | const cache = Persistence.EnvironmentCacheStore.of({ |
| 826 | loadShell: () => Effect.succeed(Option.none()), | |
| 826 | loadShell: () => Effect.succeedNone, | |
| 827 | 827 | saveShell: () => Effect.void, |
| 828 | loadThread: () => Effect.succeed(Option.none()), | |
| 828 | loadThread: () => Effect.succeedNone, | |
| 829 | 829 | saveThread: () => Effect.void, |
| 830 | 830 | removeThread: () => Effect.void, |
| 831 | loadServerConfig: () => Effect.succeed(Option.none()), | |
| 831 | loadServerConfig: () => Effect.succeedNone, | |
| 832 | 832 | saveServerConfig: () => Effect.void, |
| 833 | loadVcsRefs: () => Effect.succeed(Option.none()), | |
| 833 | loadVcsRefs: () => Effect.succeedNone, | |
| 834 | 834 | saveVcsRefs: () => Effect.void, |
| 835 | 835 | removeVcsRefs: () => Effect.void, |
| 836 | 836 | clearVcsRefs: () => Effect.void, |
packages/client-runtime/src/rpc/session.ts+7-7| ... | ... | @@ -291,13 +291,13 @@ export const make = Effect.fn("RpcSessionFactory.make")(function* ( |
| 291 | 291 | Effect.flatMap(() => Effect.fail(configSubscriptionEndedError)), |
| 292 | 292 | ), |
| 293 | 293 | ).pipe( |
| 294 | Effect.flatMap((config) => | |
| 295 | config.environment.environmentId === connection.environmentId | |
| 296 | ? Effect.succeed(config) | |
| 297 | : environmentMismatchError({ | |
| 298 | expected: connection.environmentId, | |
| 299 | actual: config.environment.environmentId, | |
| 300 | }), | |
| 294 | Effect.filterOrElse( | |
| 295 | (config) => config.environment.environmentId === connection.environmentId, | |
| 296 | (config) => | |
| 297 | environmentMismatchError({ | |
| 298 | expected: connection.environmentId, | |
| 299 | actual: config.environment.environmentId, | |
| 300 | }), | |
| 301 | 301 | ), |
| 302 | 302 | Effect.withSpan("environment.initialSync"), |
| 303 | 303 | ); |
packages/client-runtime/src/state/pullRequestRouting.ts+5-9| ... | ... | @@ -339,15 +339,11 @@ export function createPullRequestRouter() { |
| 339 | 339 | } |
| 340 | 340 | const operation = run(id); |
| 341 | 341 | return yield* (reads.has(tag) ? operation.pipe(readTimeout(id)) : operation).pipe( |
| 342 | Effect.catch((error) => { | |
| 343 | if ( | |
| 344 | (reads.has(tag) || rejectedBeforeDispatch(error)) && | |
| 345 | index + 1 < candidates.length | |
| 346 | ) { | |
| 347 | return visit(index + 1); | |
| 348 | } | |
| 349 | return Effect.fail(error); | |
| 350 | }), | |
| 342 | Effect.catchIf( | |
| 343 | (error) => | |
| 344 | (reads.has(tag) || rejectedBeforeDispatch(error)) && index + 1 < candidates.length, | |
| 345 | () => visit(index + 1), | |
| 346 | ), | |
| 351 | 347 | Effect.map((result) => |
| 352 | 348 | typeof result === "object" && result !== null && "projectId" in result |
| 353 | 349 | ? { |
packages/client-runtime/src/state/pullRequests.test.ts+1-1| ... | ... | @@ -1171,7 +1171,7 @@ it.effect("updates cached labels after successful edits without rereading the ho |
| 1171 | 1171 | if (failDetail) { |
| 1172 | 1172 | yield* detailRefreshStarted.open; |
| 1173 | 1173 | yield* releaseDetailRefresh.await; |
| 1174 | return yield* Effect.fail(new MutationRefused()); | |
| 1174 | return yield* new MutationRefused(); | |
| 1175 | 1175 | } |
| 1176 | 1176 | return { title: "keep this title", labels: [existing] }; |
| 1177 | 1177 | }), |
packages/client-runtime/src/state/server.test.ts+8-8| ... | ... | @@ -778,14 +778,14 @@ describe("server state projection", () => { |
| 778 | 778 | } satisfies EnvironmentSupervisor.EnvironmentSupervisor["Service"]); |
| 779 | 779 | const savedConfigs = yield* Queue.unbounded<ServerConfig>(); |
| 780 | 780 | const cache = Persistence.EnvironmentCacheStore.of({ |
| 781 | loadShell: () => Effect.succeed(Option.none()), | |
| 781 | loadShell: () => Effect.succeedNone, | |
| 782 | 782 | saveShell: () => Effect.void, |
| 783 | loadThread: () => Effect.succeed(Option.none()), | |
| 783 | loadThread: () => Effect.succeedNone, | |
| 784 | 784 | saveThread: () => Effect.void, |
| 785 | 785 | removeThread: () => Effect.void, |
| 786 | loadServerConfig: () => Effect.succeed(Option.some(CONFIG)), | |
| 786 | loadServerConfig: () => Effect.succeedSome(CONFIG), | |
| 787 | 787 | saveServerConfig: (_environmentId, config) => Queue.offer(savedConfigs, config), |
| 788 | loadVcsRefs: () => Effect.succeed(Option.none()), | |
| 788 | loadVcsRefs: () => Effect.succeedNone, | |
| 789 | 789 | saveVcsRefs: () => Effect.void, |
| 790 | 790 | removeVcsRefs: () => Effect.void, |
| 791 | 791 | clearVcsRefs: () => Effect.void, |
| ... | ... | @@ -839,14 +839,14 @@ describe("server state projection", () => { |
| 839 | 839 | } satisfies EnvironmentSupervisor.EnvironmentSupervisor["Service"]); |
| 840 | 840 | const savedConfigs = yield* Queue.unbounded<ServerConfig>(); |
| 841 | 841 | const cache = Persistence.EnvironmentCacheStore.of({ |
| 842 | loadShell: () => Effect.succeed(Option.none()), | |
| 842 | loadShell: () => Effect.succeedNone, | |
| 843 | 843 | saveShell: () => Effect.void, |
| 844 | loadThread: () => Effect.succeed(Option.none()), | |
| 844 | loadThread: () => Effect.succeedNone, | |
| 845 | 845 | saveThread: () => Effect.void, |
| 846 | 846 | removeThread: () => Effect.void, |
| 847 | loadServerConfig: () => Effect.succeed(Option.some(CONFIG)), | |
| 847 | loadServerConfig: () => Effect.succeedSome(CONFIG), | |
| 848 | 848 | saveServerConfig: (_environmentId, config) => Queue.offer(savedConfigs, config), |
| 849 | loadVcsRefs: () => Effect.succeed(Option.none()), | |
| 849 | loadVcsRefs: () => Effect.succeedNone, | |
| 850 | 850 | saveVcsRefs: () => Effect.void, |
| 851 | 851 | removeVcsRefs: () => Effect.void, |
| 852 | 852 | clearVcsRefs: () => Effect.void, |
packages/client-runtime/src/state/serverUsage.test.ts+4-4| ... | ... | @@ -125,14 +125,14 @@ const makeHarness = Effect.fn("ServerUsageTest.makeHarness")(function* ( |
| 125 | 125 | Stream.provideService(stream, EnvironmentSupervisor, supervisor), |
| 126 | 126 | } as EnvironmentRegistry["Service"]); |
| 127 | 127 | const cache = EnvironmentCacheStore.of({ |
| 128 | loadShell: () => Effect.succeed(Option.none()), | |
| 128 | loadShell: () => Effect.succeedNone, | |
| 129 | 129 | saveShell: () => Effect.void, |
| 130 | loadThread: () => Effect.succeed(Option.none()), | |
| 130 | loadThread: () => Effect.succeedNone, | |
| 131 | 131 | saveThread: () => Effect.void, |
| 132 | 132 | removeThread: () => Effect.void, |
| 133 | loadServerConfig: () => Effect.succeed(Option.none()), | |
| 133 | loadServerConfig: () => Effect.succeedNone, | |
| 134 | 134 | saveServerConfig: () => Effect.void, |
| 135 | loadVcsRefs: () => Effect.succeed(Option.none()), | |
| 135 | loadVcsRefs: () => Effect.succeedNone, | |
| 136 | 136 | saveVcsRefs: () => Effect.void, |
| 137 | 137 | removeVcsRefs: () => Effect.void, |
| 138 | 138 | clearVcsRefs: () => Effect.void, |
packages/client-runtime/src/state/session.ts+1-1| ... | ... | @@ -20,7 +20,7 @@ function initialConfigOption<E>( |
| 20 | 20 | initialConfig: Effect.Effect<ServerConfig, E>, |
| 21 | 21 | ): Effect.Effect<Option.Option<ServerConfig>> { |
| 22 | 22 | return initialConfig.pipe( |
| 23 | Effect.map(Option.some), | |
| 23 | Effect.asSome, | |
| 24 | 24 | Effect.catch((error) => |
| 25 | 25 | Effect.logWarning("Could not load the initial environment configuration.").pipe( |
| 26 | 26 | Effect.annotateLogs({ ...safeErrorLogAttributes(error) }), |
packages/client-runtime/src/state/shell-sync.test.ts+18-18| ... | ... | @@ -80,14 +80,14 @@ describe("environment shell synchronization", () => { |
| 80 | 80 | retryNow: Effect.void, |
| 81 | 81 | } satisfies EnvironmentSupervisor.EnvironmentSupervisor["Service"]); |
| 82 | 82 | const cache = Persistence.EnvironmentCacheStore.of({ |
| 83 | loadShell: () => Effect.succeed(Option.none()), | |
| 83 | loadShell: () => Effect.succeedNone, | |
| 84 | 84 | saveShell: () => Effect.never, |
| 85 | loadThread: () => Effect.succeed(Option.none()), | |
| 85 | loadThread: () => Effect.succeedNone, | |
| 86 | 86 | saveThread: () => Effect.void, |
| 87 | 87 | removeThread: () => Effect.void, |
| 88 | loadServerConfig: () => Effect.succeed(Option.none()), | |
| 88 | loadServerConfig: () => Effect.succeedNone, | |
| 89 | 89 | saveServerConfig: () => Effect.void, |
| 90 | loadVcsRefs: () => Effect.succeed(Option.none()), | |
| 90 | loadVcsRefs: () => Effect.succeedNone, | |
| 91 | 91 | saveVcsRefs: () => Effect.void, |
| 92 | 92 | removeVcsRefs: () => Effect.void, |
| 93 | 93 | clearVcsRefs: () => Effect.void, |
| ... | ... | @@ -96,7 +96,7 @@ describe("environment shell synchronization", () => { |
| 96 | 96 | // Cold cache with no HTTP snapshot available → falls back to the |
| 97 | 97 | // socket-embedded snapshot. |
| 98 | 98 | const snapshotLoader = ShellSnapshotLoader.of({ |
| 99 | load: () => Effect.succeed(Option.none()), | |
| 99 | load: () => Effect.succeedNone, | |
| 100 | 100 | }); |
| 101 | 101 | const shellState = yield* makeEnvironmentShellState().pipe( |
| 102 | 102 | Effect.provideService(EnvironmentSupervisor.EnvironmentSupervisor, supervisor), |
| ... | ... | @@ -176,14 +176,14 @@ describe("environment shell synchronization", () => { |
| 176 | 176 | retryNow: Effect.void, |
| 177 | 177 | } satisfies EnvironmentSupervisor.EnvironmentSupervisor["Service"]); |
| 178 | 178 | const cache = Persistence.EnvironmentCacheStore.of({ |
| 179 | loadShell: () => Effect.succeed(Option.none()), | |
| 179 | loadShell: () => Effect.succeedNone, | |
| 180 | 180 | saveShell: () => Effect.void, |
| 181 | loadThread: () => Effect.succeed(Option.none()), | |
| 181 | loadThread: () => Effect.succeedNone, | |
| 182 | 182 | saveThread: () => Effect.void, |
| 183 | 183 | removeThread: () => Effect.void, |
| 184 | loadServerConfig: () => Effect.succeed(Option.none()), | |
| 184 | loadServerConfig: () => Effect.succeedNone, | |
| 185 | 185 | saveServerConfig: () => Effect.void, |
| 186 | loadVcsRefs: () => Effect.succeed(Option.none()), | |
| 186 | loadVcsRefs: () => Effect.succeedNone, | |
| 187 | 187 | saveVcsRefs: () => Effect.void, |
| 188 | 188 | removeVcsRefs: () => Effect.void, |
| 189 | 189 | clearVcsRefs: () => Effect.void, |
| ... | ... | @@ -194,7 +194,7 @@ describe("environment shell synchronization", () => { |
| 194 | 194 | Effect.provideService(Persistence.EnvironmentCacheStore, cache), |
| 195 | 195 | Effect.provideService( |
| 196 | 196 | ShellSnapshotLoader, |
| 197 | ShellSnapshotLoader.of({ load: () => Effect.succeed(Option.none()) }), | |
| 197 | ShellSnapshotLoader.of({ load: () => Effect.succeedNone }), | |
| 198 | 198 | ), |
| 199 | 199 | ); |
| 200 | 200 | yield* SubscriptionRef.set(supervisorState, { |
| ... | ... | @@ -284,14 +284,14 @@ describe("environment shell synchronization", () => { |
| 284 | 284 | retryNow: Effect.void, |
| 285 | 285 | } satisfies EnvironmentSupervisor.EnvironmentSupervisor["Service"]); |
| 286 | 286 | const cache = Persistence.EnvironmentCacheStore.of({ |
| 287 | loadShell: () => Effect.succeed(Option.some(cachedSnapshot)), | |
| 287 | loadShell: () => Effect.succeedSome(cachedSnapshot), | |
| 288 | 288 | saveShell: () => Effect.void, |
| 289 | loadThread: () => Effect.succeed(Option.none()), | |
| 289 | loadThread: () => Effect.succeedNone, | |
| 290 | 290 | saveThread: () => Effect.void, |
| 291 | 291 | removeThread: () => Effect.void, |
| 292 | loadServerConfig: () => Effect.succeed(Option.none()), | |
| 292 | loadServerConfig: () => Effect.succeedNone, | |
| 293 | 293 | saveServerConfig: () => Effect.void, |
| 294 | loadVcsRefs: () => Effect.succeed(Option.none()), | |
| 294 | loadVcsRefs: () => Effect.succeedNone, | |
| 295 | 295 | saveVcsRefs: () => Effect.void, |
| 296 | 296 | removeVcsRefs: () => Effect.void, |
| 297 | 297 | clearVcsRefs: () => Effect.void, |
| ... | ... | @@ -364,14 +364,14 @@ describe("environment shell synchronization", () => { |
| 364 | 364 | retryNow: Effect.void, |
| 365 | 365 | } satisfies EnvironmentSupervisor.EnvironmentSupervisor["Service"]); |
| 366 | 366 | const cache = Persistence.EnvironmentCacheStore.of({ |
| 367 | loadShell: () => Effect.succeed(Option.some(LIVE_SHELL_SNAPSHOT)), | |
| 367 | loadShell: () => Effect.succeedSome(LIVE_SHELL_SNAPSHOT), | |
| 368 | 368 | saveShell: () => Effect.void, |
| 369 | loadThread: () => Effect.succeed(Option.none()), | |
| 369 | loadThread: () => Effect.succeedNone, | |
| 370 | 370 | saveThread: () => Effect.void, |
| 371 | 371 | removeThread: () => Effect.void, |
| 372 | loadServerConfig: () => Effect.succeed(Option.none()), | |
| 372 | loadServerConfig: () => Effect.succeedNone, | |
| 373 | 373 | saveServerConfig: () => Effect.void, |
| 374 | loadVcsRefs: () => Effect.succeed(Option.none()), | |
| 374 | loadVcsRefs: () => Effect.succeedNone, | |
| 375 | 375 | saveVcsRefs: () => Effect.void, |
| 376 | 376 | removeVcsRefs: () => Effect.void, |
| 377 | 377 | clearVcsRefs: () => Effect.void, |
packages/client-runtime/src/state/sourceControl.test.ts+4-4| ... | ... | @@ -101,14 +101,14 @@ describe("source control environment atoms", () => { |
| 101 | 101 | } as unknown as EnvironmentRegistry.EnvironmentRegistry["Service"]); |
| 102 | 102 | const removed = new Array<string>(); |
| 103 | 103 | const cache = Persistence.EnvironmentCacheStore.of({ |
| 104 | loadShell: () => Effect.succeed(Option.none()), | |
| 104 | loadShell: () => Effect.succeedNone, | |
| 105 | 105 | saveShell: () => Effect.void, |
| 106 | loadThread: () => Effect.succeed(Option.none()), | |
| 106 | loadThread: () => Effect.succeedNone, | |
| 107 | 107 | saveThread: () => Effect.void, |
| 108 | 108 | removeThread: () => Effect.void, |
| 109 | loadServerConfig: () => Effect.succeed(Option.none()), | |
| 109 | loadServerConfig: () => Effect.succeedNone, | |
| 110 | 110 | saveServerConfig: () => Effect.void, |
| 111 | loadVcsRefs: () => Effect.succeed(Option.none()), | |
| 111 | loadVcsRefs: () => Effect.succeedNone, | |
| 112 | 112 | saveVcsRefs: () => Effect.void, |
| 113 | 113 | removeVcsRefs: (environmentId, cwd) => |
| 114 | 114 | Effect.sync(() => { |
packages/client-runtime/src/state/threads-atoms.test.ts+3-3| ... | ... | @@ -200,7 +200,7 @@ const makeHarness = Effect.fn("TestThreadAtoms.makeHarness")(function* (options? |
| 200 | 200 | Layer.succeed( |
| 201 | 201 | EnvironmentCacheStore, |
| 202 | 202 | EnvironmentCacheStore.of({ |
| 203 | loadShell: () => Effect.succeed(Option.none()), | |
| 203 | loadShell: () => Effect.succeedNone, | |
| 204 | 204 | saveShell: () => Effect.void, |
| 205 | 205 | loadThread: () => |
| 206 | 206 | Effect.sync(() => { |
| ... | ... | @@ -209,9 +209,9 @@ const makeHarness = Effect.fn("TestThreadAtoms.makeHarness")(function* (options? |
| 209 | 209 | }), |
| 210 | 210 | saveThread: () => Effect.void, |
| 211 | 211 | removeThread: () => Effect.void, |
| 212 | loadServerConfig: () => Effect.succeed(Option.none()), | |
| 212 | loadServerConfig: () => Effect.succeedNone, | |
| 213 | 213 | saveServerConfig: () => Effect.void, |
| 214 | loadVcsRefs: () => Effect.succeed(Option.none()), | |
| 214 | loadVcsRefs: () => Effect.succeedNone, | |
| 215 | 215 | saveVcsRefs: () => Effect.void, |
| 216 | 216 | removeVcsRefs: () => Effect.void, |
| 217 | 217 | clearVcsRefs: () => Effect.void, |
packages/client-runtime/src/state/threads-pagination.test.ts+3-3| ... | ... | @@ -197,16 +197,16 @@ const makeHarness = Effect.fn("TestThreadPagination.makeHarness")(function* (opt |
| 197 | 197 | retryNow: Effect.void, |
| 198 | 198 | } satisfies EnvironmentSupervisor.EnvironmentSupervisor["Service"]); |
| 199 | 199 | const cache = Persistence.EnvironmentCacheStore.of({ |
| 200 | loadShell: () => Effect.succeed(Option.none()), | |
| 200 | loadShell: () => Effect.succeedNone, | |
| 201 | 201 | saveShell: () => Effect.void, |
| 202 | 202 | loadThread: () => |
| 203 | 203 | Effect.succeed(options?.cached !== undefined ? Option.some(options.cached) : Option.none()), |
| 204 | 204 | saveThread: (_environmentId, thread) => |
| 205 | 205 | Ref.update(savedThreads, (current) => [...current, thread]), |
| 206 | 206 | removeThread: () => Effect.void, |
| 207 | loadServerConfig: () => Effect.succeed(Option.none()), | |
| 207 | loadServerConfig: () => Effect.succeedNone, | |
| 208 | 208 | saveServerConfig: () => Effect.void, |
| 209 | loadVcsRefs: () => Effect.succeed(Option.none()), | |
| 209 | loadVcsRefs: () => Effect.succeedNone, | |
| 210 | 210 | saveVcsRefs: () => Effect.void, |
| 211 | 211 | removeVcsRefs: () => Effect.void, |
| 212 | 212 | clearVcsRefs: () => Effect.void, |
packages/client-runtime/src/state/threads-sync.test.ts+3-3| ... | ... | @@ -216,7 +216,7 @@ const makeHarness = Effect.fn("TestEnvironmentThreads.makeHarness")(function* (o |
| 216 | 216 | retryNow: Ref.update(retryCount, (count) => count + 1), |
| 217 | 217 | } satisfies EnvironmentSupervisor.EnvironmentSupervisor["Service"]); |
| 218 | 218 | const cache = Persistence.EnvironmentCacheStore.of({ |
| 219 | loadShell: () => Effect.succeed(Option.none()), | |
| 219 | loadShell: () => Effect.succeedNone, | |
| 220 | 220 | saveShell: () => Effect.void, |
| 221 | 221 | loadThread: (_environmentId, threadId) => |
| 222 | 222 | options?.loadCached ?? |
| ... | ... | @@ -234,9 +234,9 @@ const makeHarness = Effect.fn("TestEnvironmentThreads.makeHarness")(function* (o |
| 234 | 234 | ), |
| 235 | 235 | removeThread: (_environmentId, threadId) => |
| 236 | 236 | Ref.update(removedThreads, (current) => [...current, threadId]), |
| 237 | loadServerConfig: () => Effect.succeed(Option.none()), | |
| 237 | loadServerConfig: () => Effect.succeedNone, | |
| 238 | 238 | saveServerConfig: () => Effect.void, |
| 239 | loadVcsRefs: () => Effect.succeed(Option.none()), | |
| 239 | loadVcsRefs: () => Effect.succeedNone, | |
| 240 | 240 | saveVcsRefs: () => Effect.void, |
| 241 | 241 | removeVcsRefs: () => Effect.void, |
| 242 | 242 | clearVcsRefs: () => Effect.void, |
packages/client-runtime/src/state/vcs.test.ts+3-3| ... | ... | @@ -98,12 +98,12 @@ function cacheWithRefs( |
| 98 | 98 | overrides: Partial<Persistence.EnvironmentCacheStore["Service"]> = {}, |
| 99 | 99 | ) { |
| 100 | 100 | return Persistence.EnvironmentCacheStore.of({ |
| 101 | loadShell: () => Effect.succeed(Option.none()), | |
| 101 | loadShell: () => Effect.succeedNone, | |
| 102 | 102 | saveShell: () => Effect.void, |
| 103 | loadThread: () => Effect.succeed(Option.none()), | |
| 103 | loadThread: () => Effect.succeedNone, | |
| 104 | 104 | saveThread: () => Effect.void, |
| 105 | 105 | removeThread: () => Effect.void, |
| 106 | loadServerConfig: () => Effect.succeed(Option.none()), | |
| 106 | loadServerConfig: () => Effect.succeedNone, | |
| 107 | 107 | saveServerConfig: () => Effect.void, |
| 108 | 108 | loadVcsRefs: () => Effect.succeed(refs), |
| 109 | 109 | saveVcsRefs: () => Effect.void, |
packages/client-runtime/src/state/vcsAction.test.ts+4-4| ... | ... | @@ -99,14 +99,14 @@ function progress<T extends GitActionProgressEvent>(event: T): T { |
| 99 | 99 | |
| 100 | 100 | function cacheStore(onClearVcsRefs: (environmentId: EnvironmentId) => void) { |
| 101 | 101 | return Persistence.EnvironmentCacheStore.of({ |
| 102 | loadShell: () => Effect.succeed(Option.none()), | |
| 102 | loadShell: () => Effect.succeedNone, | |
| 103 | 103 | saveShell: () => Effect.void, |
| 104 | loadThread: () => Effect.succeed(Option.none()), | |
| 104 | loadThread: () => Effect.succeedNone, | |
| 105 | 105 | saveThread: () => Effect.void, |
| 106 | 106 | removeThread: () => Effect.void, |
| 107 | loadServerConfig: () => Effect.succeed(Option.none()), | |
| 107 | loadServerConfig: () => Effect.succeedNone, | |
| 108 | 108 | saveServerConfig: () => Effect.void, |
| 109 | loadVcsRefs: () => Effect.succeed(Option.none()), | |
| 109 | loadVcsRefs: () => Effect.succeedNone, | |
| 110 | 110 | saveVcsRefs: () => Effect.void, |
| 111 | 111 | removeVcsRefs: () => Effect.void, |
| 112 | 112 | clearVcsRefs: (environmentId) => Effect.sync(() => onClearVcsRefs(environmentId)), |
packages/contracts/src/pullRequest.test.ts+1-1| ... | ... | @@ -92,7 +92,7 @@ describe("PullRequestListResult", () => { |
| 92 | 92 | it("round-trips through the JSON codec the RPC serializes with", () => { |
| 93 | 93 | const codec = Schema.toCodecJson(PullRequestListResult); |
| 94 | 94 | |
| 95 | const decoded = Schema.decodeUnknownSync(codec)(Schema.encodeUnknownSync(codec)(LIST_RESULT)); | |
| 95 | const decoded = Schema.decodeSync(codec)(Schema.encodeUnknownSync(codec)(LIST_RESULT)); | |
| 96 | 96 | |
| 97 | 97 | expect(decoded).toStrictEqual(LIST_RESULT); |
| 98 | 98 | }); |
packages/contracts/src/rpc.test.ts+3-3| ... | ... | @@ -13,19 +13,19 @@ import { WsSubscribeServerConfigRpc } from "./rpc.ts"; |
| 13 | 13 | describe("subscribeServerConfig payload compatibility", () => { |
| 14 | 14 | it("is accepted by a server whose schema predates the field", () => { |
| 15 | 15 | const oldServerPayload = Schema.Struct({}); |
| 16 | const decoded = Schema.decodeUnknownExit(oldServerPayload)({ environmentThemes: true }); | |
| 16 | const decoded = Schema.decodeExit(oldServerPayload)({ environmentThemes: true }); | |
| 17 | 17 | expect(Exit.isSuccess(decoded)).toBe(true); |
| 18 | 18 | }); |
| 19 | 19 | |
| 20 | 20 | it("is carried by a server that declares it", () => { |
| 21 | const decoded = Schema.decodeUnknownSync(WsSubscribeServerConfigRpc.payloadSchema)({ | |
| 21 | const decoded = Schema.decodeSync(WsSubscribeServerConfigRpc.payloadSchema)({ | |
| 22 | 22 | environmentThemes: true, |
| 23 | 23 | }); |
| 24 | 24 | expect(decoded).toEqual({ environmentThemes: true }); |
| 25 | 25 | }); |
| 26 | 26 | |
| 27 | 27 | it("stays optional, so a client that never sends it still subscribes", () => { |
| 28 | const decoded = Schema.decodeUnknownSync(WsSubscribeServerConfigRpc.payloadSchema)({}); | |
| 28 | const decoded = Schema.decodeSync(WsSubscribeServerConfigRpc.payloadSchema)({}); | |
| 29 | 29 | expect(decoded).toEqual({}); |
| 30 | 30 | }); |
| 31 | 31 | }); |
packages/contracts/src/settings.ts+1-3| ... | ... | @@ -1082,9 +1082,7 @@ export type StorageCleanupSettings = typeof StorageCleanupSettings.Type; |
| 1082 | 1082 | export const ServerSettings = Schema.Struct({ |
| 1083 | 1083 | worktreeCleanup: WorktreeCleanup.pipe(Schema.withDecodingDefault(Effect.succeed(null))), |
| 1084 | 1084 | storageCleanup: StorageCleanupSettings.pipe( |
| 1085 | Schema.withDecodingDefault( | |
| 1086 | Effect.succeed(Schema.decodeUnknownSync(StorageCleanupSettings)({})), | |
| 1087 | ), | |
| 1085 | Schema.withDecodingDefault(Effect.succeed(Schema.decodeSync(StorageCleanupSettings)({}))), | |
| 1088 | 1086 | ), |
| 1089 | 1087 | // How assistant text reaches clients during a turn. Deliberately a fresh |
| 1090 | 1088 | // key (was `enableLegacyTokenStreaming`, before that |
packages/effect-acp/src/client.ts+3-5| ... | ... | @@ -346,11 +346,9 @@ export const make = Effect.fn("effect-acp/AcpClient.make")(function* ( |
| 346 | 346 | registration: BufferedNotificationHandler<A>, |
| 347 | 347 | notification: A, |
| 348 | 348 | ) => |
| 349 | Effect.forEach( | |
| 350 | registration.handlers, | |
| 351 | (handler) => handler(notification).pipe(Effect.catch(() => Effect.void)), | |
| 352 | { discard: true }, | |
| 353 | ); | |
| 349 | Effect.forEach(registration.handlers, (handler) => handler(notification).pipe(Effect.ignore), { | |
| 350 | discard: true, | |
| 351 | }); | |
| 354 | 352 | |
| 355 | 353 | const flushBufferedNotifications = <A>(registration: BufferedNotificationHandler<A>) => |
| 356 | 354 | Effect.suspend(() => { |
packages/effect-acp/src/protocol.ts+1-1| ... | ... | @@ -216,7 +216,7 @@ export const makeAcpPatchedProtocol = Effect.fn("makeAcpPatchedProtocol")(functi |
| 216 | 216 | Queue.offer(notificationQueue, notification).pipe( |
| 217 | 217 | Effect.andThen( |
| 218 | 218 | options.onNotification |
| 219 | ? options.onNotification(notification).pipe(Effect.catch(() => Effect.void)) | |
| 219 | ? options.onNotification(notification).pipe(Effect.ignore) | |
| 220 | 220 | : Effect.void, |
| 221 | 221 | ), |
| 222 | 222 | Effect.asVoid, |
packages/effect-codex-app-server/src/_internal/shared.ts+1-1| ... | ... | @@ -45,7 +45,7 @@ export const encodeOptionalPayload = <A, I>( |
| 45 | 45 | ): Effect.Effect<I | undefined, CodexError.CodexAppServerRequestError> => { |
| 46 | 46 | if (!schema) { |
| 47 | 47 | if (payload === undefined) { |
| 48 | return Effect.sync(() => undefined); | |
| 48 | return Effect.undefined; | |
| 49 | 49 | } |
| 50 | 50 | return Effect.fail( |
| 51 | 51 | CodexError.CodexAppServerRequestError.unexpectedPayload(method, "encode-payload", payload), |
packages/effect-codex-app-server/src/client.ts+2-4| ... | ... | @@ -153,14 +153,12 @@ const make = Effect.fn("effect-codex-app-server/CodexAppServerClient.make")(func |
| 153 | 153 | Effect.flatMap((decoded) => |
| 154 | 154 | Effect.forEach(handlers, (handler) => handler(decoded), { discard: true }), |
| 155 | 155 | ), |
| 156 | Effect.catch(() => Effect.void), | |
| 156 | Effect.ignore, | |
| 157 | 157 | ); |
| 158 | 158 | } |
| 159 | 159 | |
| 160 | 160 | return unknownNotificationHandler |
| 161 | ? unknownNotificationHandler(notification.method, notification.params).pipe( | |
| 162 | Effect.catch(() => Effect.void), | |
| 163 | ) | |
| 161 | ? unknownNotificationHandler(notification.method, notification.params).pipe(Effect.ignore) | |
| 164 | 162 | : Effect.void; |
| 165 | 163 | }; |
| 166 | 164 |
packages/effect-codex-app-server/src/schema.test.ts+1-1| ... | ... | @@ -29,7 +29,7 @@ it("keeps async questions in live notifications and thread history", () => { |
| 29 | 29 | CodexSchema.V2ThreadReadResponse__ThreadItem, |
| 30 | 30 | CodexSchema.V2ThreadResumeResponse__ThreadItem, |
| 31 | 31 | ]) { |
| 32 | assert.deepEqual(Schema.decodeUnknownSync(schema)(item), item); | |
| 32 | assert.deepEqual(Schema.decodeSync(schema)(item), item); | |
| 33 | 33 | } |
| 34 | 34 | }); |
| 35 | 35 |
packages/shared/src/httpReadiness.ts+7-11| ... | ... | @@ -106,17 +106,13 @@ export const waitForHttpReady = Effect.fn("shared.httpReadiness.waitForHttpReady |
| 106 | 106 | Effect.timeoutOption(Duration.millis(probeTimeoutMs)), |
| 107 | 107 | Effect.mapError((cause) => fail(cause)), |
| 108 | 108 | ); |
| 109 | return yield* Option.match(responseOption, { | |
| 110 | onSome: Effect.succeed, | |
| 111 | onNone: () => | |
| 112 | Effect.fail( | |
| 113 | fail({ | |
| 114 | kind: "probe-timeout", | |
| 115 | attempt, | |
| 116 | probeTimeoutMs, | |
| 117 | }), | |
| 118 | ), | |
| 119 | }); | |
| 109 | return yield* Effect.fromOption(responseOption, () => | |
| 110 | fail({ | |
| 111 | kind: "probe-timeout", | |
| 112 | attempt, | |
| 113 | probeTimeoutMs, | |
| 114 | }), | |
| 115 | ); | |
| 120 | 116 | }).pipe( |
| 121 | 117 | Effect.mapError((cause) => (isMadeError(cause) ? cause : fail(cause))), |
| 122 | 118 | Effect.tapError((cause) => |
packages/shared/src/relayClient.ts+11-13| ... | ... | @@ -331,9 +331,7 @@ export const makeCloudflaredRelayClient = Effect.fn("cloudflared.make")(function |
| 331 | 331 | for (let attempt = 0; attempt < INSTALL_LOCK_RETRY_COUNT; attempt += 1) { |
| 332 | 332 | const acquired = yield* fileSystem.writeFileString(lockPath, "", { flag: "wx" }).pipe( |
| 333 | 333 | Effect.as(true), |
| 334 | Effect.catch((error) => | |
| 335 | isAlreadyExists(error) ? Effect.succeed(false) : Effect.fail(error), | |
| 336 | ), | |
| 334 | Effect.catchIf(isAlreadyExists, () => Effect.succeed(false)), | |
| 337 | 335 | ); |
| 338 | 336 | if (acquired) return; |
| 339 | 337 | |
| ... | ... | @@ -445,16 +443,16 @@ export const makeCloudflaredRelayClient = Effect.fn("cloudflared.make")(function |
| 445 | 443 | }).pipe( |
| 446 | 444 | Effect.scoped, |
| 447 | 445 | Effect.ensuring(fileSystem.remove(lockPath, { force: true }).pipe(Effect.ignore)), |
| 448 | Effect.catch((cause) => | |
| 449 | cause instanceof RelayClientInstallError | |
| 450 | ? Effect.fail(cause) | |
| 451 | : Effect.fail( | |
| 452 | new RelayClientInstallError({ | |
| 453 | reason: "write_failed", | |
| 454 | message: "Could not install the relay client.", | |
| 455 | cause, | |
| 456 | }), | |
| 457 | ), | |
| 446 | Effect.catchIf( | |
| 447 | (cause) => !(cause instanceof RelayClientInstallError), | |
| 448 | (cause) => | |
| 449 | Effect.fail( | |
| 450 | new RelayClientInstallError({ | |
| 451 | reason: "write_failed", | |
| 452 | message: "Could not install the relay client.", | |
| 453 | cause, | |
| 454 | }), | |
| 455 | ), | |
| 458 | 456 | ), |
| 459 | 457 | ); |
| 460 | 458 | }); |
packages/shared/src/relayTracing.ts+1-1| ... | ... | @@ -151,6 +151,6 @@ export function makeRelayClientTracingLayer( |
| 151 | 151 | |
| 152 | 152 | return Layer.effect( |
| 153 | 153 | RelayClientTracer, |
| 154 | Tracer.Tracer.pipe(Effect.map(nonInterferingTracer), Effect.map(Option.some)), | |
| 154 | Tracer.Tracer.pipe(Effect.map(nonInterferingTracer), Effect.asSome), | |
| 155 | 155 | ).pipe(Layer.provide(tracerLayer)); |
| 156 | 156 | } |
packages/shared/src/schemaJson.ts+1-2| ... | ... | @@ -1,7 +1,6 @@ |
| 1 | 1 | import * as Cause from "effect/Cause"; |
| 2 | 2 | import * as Effect from "effect/Effect"; |
| 3 | 3 | import * as Exit from "effect/Exit"; |
| 4 | import * as Option from "effect/Option"; | |
| 5 | 4 | import * as Result from "effect/Result"; |
| 6 | 5 | import * as Schema from "effect/Schema"; |
| 7 | 6 | import * as SchemaGetter from "effect/SchemaGetter"; |
| ... | ... | @@ -187,7 +186,7 @@ const parseLenientJsonGetter = SchemaGetter.onSome((input: string) => { |
| 187 | 186 | ); |
| 188 | 187 | |
| 189 | 188 | return decodeJsonString(stripped).pipe( |
| 190 | Effect.map(Option.some), | |
| 189 | Effect.asSome, | |
| 191 | 190 | Effect.mapError((error) => error.issue), |
| 192 | 191 | ); |
| 193 | 192 | }); |
tsconfig.base.json+2-1| ... | ... | @@ -50,7 +50,8 @@ |
| 50 | 50 | "globalTimers": "error", |
| 51 | 51 | "globalTimersInEffect": "error", |
| 52 | 52 | "globalFetch": "error", |
| 53 | "globalFetchInEffect": "error" | |
| 53 | "globalFetchInEffect": "error", | |
| 54 | "schemaNumber": "off" | |
| 54 | 55 | } |
| 55 | 56 | } |
| 56 | 57 | ] |