import * as NodeHttpServer from "@effect/platform-node/NodeHttpServer"; import * as NodeSocket from "@effect/platform-node/NodeSocket"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { CommandId, DEFAULT_SERVER_SETTINGS, EnvironmentId, EventId, GitCommandError, KeybindingRule, MessageId, OpenError, type OrchestrationThreadShell, TerminalNotRunningError, type OrchestrationCommand, type OrchestrationEvent, ORCHESTRATION_WS_METHODS, ProjectId, ProviderDriverKind, ProviderInstanceId, ResolvedKeybindingRule, ThreadId, WS_METHODS, WsRpcGroup, EditorId, } from "@t3tools/contracts"; import { assert, it } from "@effect/vitest"; import { assertFailure, assertInclude, assertTrue } from "@effect/vitest/utils"; import { Deferred, DateTime, Duration, Effect, FileSystem, Layer, ManagedRuntime, Option, Path, Stream, } from "effect"; import { ChildProcessSpawner } from "effect/unstable/process"; import { FetchHttpClient, HttpBody, HttpClient, HttpRouter, HttpServer, } from "effect/unstable/http"; import { OtlpSerialization, OtlpTracer } from "effect/unstable/observability"; import { RpcClient, RpcSerialization } from "effect/unstable/rpc"; import * as Socket from "effect/unstable/socket/Socket"; import { vi } from "vitest"; const TEST_EPOCH = DateTime.makeUnsafe("1970-01-01T00:00:00.000Z"); import type { ServerConfigShape } from "./config.ts"; import { deriveServerPaths, ServerConfig } from "./config.ts"; import { makeRoutesLayer } from "./server.ts"; import { resolveAttachmentRelativePath } from "./attachmentPaths.ts"; import { CheckpointDiffQuery, type CheckpointDiffQueryShape, } from "./checkpointing/Services/CheckpointDiffQuery.ts"; import { GitManager, type GitManagerShape } from "./git/GitManager.ts"; import { Keybindings, type KeybindingsShape } from "./keybindings.ts"; import { Open, type OpenShape } from "./open.ts"; import { OrchestrationEngineService, type OrchestrationEngineShape, } from "./orchestration/Services/OrchestrationEngine.ts"; import { OrchestrationListenerCallbackError } from "./orchestration/Errors.ts"; import { ProjectionSnapshotQuery, type ProjectionSnapshotQueryShape, } from "./orchestration/Services/ProjectionSnapshotQuery.ts"; import { SqlitePersistenceMemory } from "./persistence/Layers/Sqlite.ts"; import { PersistenceSqlError } from "./persistence/Errors.ts"; import { ProviderRegistry, type ProviderRegistryShape, } from "./provider/Services/ProviderRegistry.ts"; import { ServerLifecycleEvents, type ServerLifecycleEventsShape } from "./serverLifecycleEvents.ts"; import { ServerRuntimeStartup, type ServerRuntimeStartupShape } from "./serverRuntimeStartup.ts"; import { ServerSettingsService, type ServerSettingsShape } from "./serverSettings.ts"; import { TerminalManager, type TerminalManagerShape } from "./terminal/Services/Manager.ts"; import { BrowserTraceCollector, type BrowserTraceCollectorShape, } from "./observability/Services/BrowserTraceCollector.ts"; import { ProjectFaviconResolverLive } from "./project/Layers/ProjectFaviconResolver.ts"; import { ProjectSetupScriptRunner, type ProjectSetupScriptRunnerShape, } from "./project/Services/ProjectSetupScriptRunner.ts"; import { RepositoryIdentityResolver, type RepositoryIdentityResolverShape, } from "./project/Services/RepositoryIdentityResolver.ts"; import { ServerEnvironment, type ServerEnvironmentShape, } from "./environment/Services/ServerEnvironment.ts"; import { WorkspaceEntriesLive } from "./workspace/Layers/WorkspaceEntries.ts"; import { WorkspaceFileSystemLive } from "./workspace/Layers/WorkspaceFileSystem.ts"; import { WorkspacePathsLive } from "./workspace/Layers/WorkspacePaths.ts"; import * as GitVcsDriver from "./vcs/GitVcsDriver.ts"; import * as VcsDriver from "./vcs/VcsDriver.ts"; import * as VcsStatusBroadcaster from "./vcs/VcsStatusBroadcaster.ts"; import * as VcsDriverRegistry from "./vcs/VcsDriverRegistry.ts"; import * as VcsProvisioningService from "./vcs/VcsProvisioningService.ts"; import * as GitWorkflowService from "./git/GitWorkflowService.ts"; import * as SourceControlRepositoryService from "./sourceControl/SourceControlRepositoryService.ts"; import { ServerSecretStoreLive } from "./auth/Layers/ServerSecretStore.ts"; import { ServerAuthLive } from "./auth/Layers/ServerAuth.ts"; const defaultProjectId = ProjectId.make("project-default"); const defaultThreadId = ThreadId.make("thread-default"); const defaultDesktopBootstrapToken = "test-desktop-bootstrap-token"; const defaultModelSelection = { instanceId: ProviderInstanceId.make("codex"), model: "gpt-5-codex", } as const; const testEnvironmentDescriptor = { environmentId: EnvironmentId.make("environment-test"), label: "Test environment", platform: { os: "darwin" as const, arch: "arm64" as const, }, serverVersion: "0.0.0-test", capabilities: { repositoryIdentity: true, }, }; const makeDefaultOrchestrationReadModel = () => { const now = new Date().toISOString(); return { snapshotSequence: 0, updatedAt: now, projects: [ { id: defaultProjectId, title: "Default Project", workspaceRoot: "/tmp/default-project", defaultModelSelection, scripts: [], createdAt: now, updatedAt: now, deletedAt: null, }, ], threads: [ { id: defaultThreadId, projectId: defaultProjectId, title: "Default Thread", modelSelection: defaultModelSelection, interactionMode: "default" as const, runtimeMode: "full-access" as const, branch: null, worktreePath: null, createdAt: now, updatedAt: now, archivedAt: null, latestTurn: null, messages: [], session: null, activities: [], proposedPlans: [], checkpoints: [], deletedAt: null, }, ], }; }; const makeDefaultOrchestrationThreadShell = ( overrides: Partial = {}, ): OrchestrationThreadShell => { const now = new Date().toISOString(); return { id: defaultThreadId, projectId: defaultProjectId, title: "Default Thread", modelSelection: defaultModelSelection, runtimeMode: "full-access", interactionMode: "default", branch: null, worktreePath: null, latestTurn: null, createdAt: now, updatedAt: now, archivedAt: null, session: null, latestUserMessageAt: null, hasPendingApprovals: false, hasPendingUserInput: false, hasActionableProposedPlan: false, ...overrides, }; }; const browserOtlpTracingLayer = Layer.mergeAll( FetchHttpClient.layer, OtlpSerialization.layerJson, Layer.succeed(HttpClient.TracerDisabledWhen, () => true), ); const makeAuthTestLayer = () => ServerAuthLive.pipe(Layer.provide(SqlitePersistenceMemory), Layer.provide(ServerSecretStoreLive)); const makeBrowserOtlpPayload = (spanName: string) => Effect.gen(function* () { const collector = yield* Effect.acquireRelease( Effect.promise(async () => { const NodeHttp = await import("node:http"); return await new Promise<{ readonly close: () => Promise; readonly firstRequest: Promise<{ readonly body: string; readonly contentType: string | null; }>; readonly url: string; }>((resolve, reject) => { let resolveFirstRequest: | ((request: { readonly body: string; readonly contentType: string | null }) => void) | undefined; const firstRequest = new Promise<{ readonly body: string; readonly contentType: string | null; }>((resolveRequest) => { resolveFirstRequest = resolveRequest; }); const server = NodeHttp.createServer((request, response) => { const chunks: Buffer[] = []; request.on("data", (chunk) => { chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)); }); request.on("end", () => { resolveFirstRequest?.({ body: Buffer.concat(chunks).toString("utf8"), contentType: request.headers["content-type"] ?? null, }); resolveFirstRequest = undefined; response.statusCode = 204; response.end(); }); }); server.on("error", reject); server.listen(0, "127.0.0.1", () => { const address = server.address(); if (!address || typeof address === "string") { reject(new Error("Expected TCP collector address")); return; } resolve({ url: `http://127.0.0.1:${address.port}/v1/traces`, firstRequest, close: () => new Promise((resolveClose, rejectClose) => { server.close((error) => { if (error) { rejectClose(error); return; } resolveClose(); }); }), }); }); }); }), ({ close }) => Effect.promise(close), ); const runtime = ManagedRuntime.make( OtlpTracer.layer({ url: collector.url, exportInterval: "10 millis", resource: { serviceName: "t3-web", attributes: { "service.runtime": "t3-web", "service.mode": "browser", "service.version": "test", }, }, }).pipe(Layer.provide(browserOtlpTracingLayer)), ); try { yield* Effect.promise(() => runtime.runPromise(Effect.void.pipe(Effect.withSpan(spanName)))); } finally { yield* Effect.promise(() => runtime.dispose()); } const request = yield* Effect.promise(() => Promise.race([ collector.firstRequest, new Promise((_, reject) => { setTimeout(() => reject(new Error("Timed out waiting for OTLP trace export")), 1_000); }), ]), ); return JSON.parse(request.body) as OtlpTracer.TraceData; }); const buildAppUnderTest = (options?: { config?: Partial; layers?: { keybindings?: Partial; providerRegistry?: Partial; serverSettings?: Partial; open?: Partial; vcsDriver?: Partial; vcsDriverRegistry?: Partial; gitVcsDriver?: Partial; gitManager?: Partial; sourceControlRepositoryService?: Partial; vcsStatusBroadcaster?: Partial; projectSetupScriptRunner?: Partial; terminalManager?: Partial; orchestrationEngine?: Partial; projectionSnapshotQuery?: Partial; checkpointDiffQuery?: Partial; browserTraceCollector?: Partial; serverLifecycleEvents?: Partial; serverRuntimeStartup?: Partial; serverEnvironment?: Partial; repositoryIdentityResolver?: Partial; }; }) => Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const tempBaseDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-router-test-" }); const baseDir = options?.config?.baseDir ?? tempBaseDir; const devUrl = options?.config?.devUrl; const derivedPaths = yield* deriveServerPaths(baseDir, devUrl); const config: ServerConfigShape = { logLevel: "Info", traceMinLevel: "Info", traceTimingEnabled: true, traceBatchWindowMs: 200, traceMaxBytes: 10 * 1024 * 1024, traceMaxFiles: 10, otlpTracesUrl: undefined, otlpMetricsUrl: undefined, otlpExportIntervalMs: 10_000, otlpServiceName: "t3-server", mode: "desktop", port: 0, host: "127.0.0.1", cwd: process.cwd(), baseDir, ...derivedPaths, staticDir: undefined, devUrl, noBrowser: true, startupPresentation: "browser", desktopBootstrapToken: defaultDesktopBootstrapToken, autoBootstrapProjectFromCwd: false, logWebSocketEvents: false, tailscaleServeEnabled: false, tailscaleServePort: 443, ...options?.config, }; const layerConfig = Layer.succeed(ServerConfig, config); const defaultVcsDriver: VcsDriver.VcsDriverShape = { capabilities: { kind: "git", supportsWorktrees: true, supportsBookmarks: false, supportsAtomicSnapshot: false, supportsPushDefaultRemote: true, ignoreClassifier: "native", }, execute: () => Effect.succeed({ exitCode: ChildProcessSpawner.ExitCode(0), stdout: "", stderr: "", stdoutTruncated: false, stderrTruncated: false, }), detectRepository: () => Effect.succeed(null), isInsideWorkTree: () => Effect.succeed(false), listWorkspaceFiles: () => Effect.succeed({ paths: [], truncated: false, freshness: { source: "live-local", observedAt: TEST_EPOCH, expiresAt: Option.none(), }, }), listRemotes: () => Effect.succeed({ remotes: [], freshness: { source: "live-local", observedAt: TEST_EPOCH, expiresAt: Option.none(), }, }), filterIgnoredPaths: (_cwd, relativePaths) => Effect.succeed(relativePaths), initRepository: () => Effect.void, ...options?.layers?.vcsDriver, }; const vcsDriverRegistryLayer = Layer.mock(VcsDriverRegistry.VcsDriverRegistry)({ get: () => Effect.succeed(defaultVcsDriver), detect: (input) => defaultVcsDriver.detectRepository(input.cwd).pipe( Effect.flatMap((repository) => repository ? Effect.succeed(repository) : defaultVcsDriver.isInsideWorkTree(input.cwd).pipe( Effect.map((isInsideWorkTree) => isInsideWorkTree ? { kind: "git" as const, rootPath: input.cwd, metadataPath: null, freshness: { source: "live-local" as const, observedAt: TEST_EPOCH, expiresAt: Option.none(), }, } : null, ), ), ), Effect.map((repository) => repository ? ({ kind: repository.kind, repository, driver: defaultVcsDriver, } satisfies VcsDriverRegistry.VcsDriverHandle) : null, ), ), resolve: (input) => Effect.succeed({ kind: input.requestedKind === "auto" || !input.requestedKind ? "git" : input.requestedKind, repository: { kind: input.requestedKind === "auto" || !input.requestedKind ? "git" : input.requestedKind, rootPath: input.cwd, metadataPath: null, freshness: { source: "live-local", observedAt: TEST_EPOCH, expiresAt: Option.none(), }, }, driver: defaultVcsDriver, }), ...options?.layers?.vcsDriverRegistry, }); const gitVcsDriverLayer = Layer.mock(GitVcsDriver.GitVcsDriver)({ ...options?.layers?.gitVcsDriver, }); const gitManagerLayer = Layer.mock(GitManager)({ ...options?.layers?.gitManager, }); const workspaceEntriesLayer = WorkspaceEntriesLive.pipe( Layer.provide(WorkspacePathsLive), Layer.provideMerge(vcsDriverRegistryLayer), ); const workspaceAndProjectServicesLayer = Layer.mergeAll( WorkspacePathsLive, workspaceEntriesLayer, WorkspaceFileSystemLive.pipe( Layer.provide(WorkspacePathsLive), Layer.provide(workspaceEntriesLayer), ), ProjectFaviconResolverLive, ); const gitWorkflowLayer = GitWorkflowService.layer.pipe( Layer.provideMerge(vcsDriverRegistryLayer), Layer.provideMerge(gitVcsDriverLayer), Layer.provideMerge(gitManagerLayer), ); const vcsProvisioningLayer = VcsProvisioningService.layer.pipe( Layer.provide(vcsDriverRegistryLayer), ); const vcsStatusBroadcasterLayer = options?.layers?.vcsStatusBroadcaster ? Layer.mock(VcsStatusBroadcaster.VcsStatusBroadcaster)({ ...options.layers.vcsStatusBroadcaster, }) : VcsStatusBroadcaster.layer.pipe(Layer.provide(gitWorkflowLayer)); const servedRoutesLayer = HttpRouter.serve(makeRoutesLayer, { disableListenLog: true, disableLogger: true, }).pipe( Layer.provide( Layer.mock(Keybindings)({ loadConfigState: Effect.succeed({ keybindings: [], issues: [], }), streamChanges: Stream.empty, ...options?.layers?.keybindings, }), ), Layer.provide( Layer.mock(ProviderRegistry)({ getProviders: Effect.succeed([]), refresh: () => Effect.succeed([]), streamChanges: Stream.empty, ...options?.layers?.providerRegistry, }), ), Layer.provide( Layer.mock(ServerSettingsService)({ start: Effect.void, ready: Effect.void, getSettings: Effect.succeed(DEFAULT_SERVER_SETTINGS), updateSettings: () => Effect.succeed(DEFAULT_SERVER_SETTINGS), streamChanges: Stream.empty, ...options?.layers?.serverSettings, }), ), Layer.provide( Layer.mock(Open)({ ...options?.layers?.open, }), ), Layer.provide(gitManagerLayer), Layer.provide(gitVcsDriverLayer), Layer.provide(gitWorkflowLayer), Layer.provide(vcsProvisioningLayer), Layer.provide( Layer.mock(SourceControlRepositoryService.SourceControlRepositoryService)({ ...options?.layers?.sourceControlRepositoryService, }), ), Layer.provideMerge(vcsStatusBroadcasterLayer), Layer.provide( Layer.mock(ProjectSetupScriptRunner)({ runForThread: () => Effect.succeed({ status: "no-script" as const }), ...options?.layers?.projectSetupScriptRunner, }), ), Layer.provide( Layer.mock(TerminalManager)({ ...options?.layers?.terminalManager, }), ), Layer.provide( Layer.mock(OrchestrationEngineService)({ readEvents: () => Stream.empty, dispatch: () => Effect.succeed({ sequence: 0 }), streamDomainEvents: Stream.empty, ...options?.layers?.orchestrationEngine, }), ), Layer.provide( Layer.mock(ProjectionSnapshotQuery)({ getCommandReadModel: () => Effect.succeed(makeDefaultOrchestrationReadModel()), getSnapshot: () => Effect.succeed(makeDefaultOrchestrationReadModel()), getShellSnapshot: () => Effect.succeed({ snapshotSequence: 0, projects: [], threads: [], updatedAt: new Date(0).toISOString(), }), getSnapshotSequence: () => Effect.succeed({ snapshotSequence: 0 }), getProjectShellById: () => Effect.succeed(Option.none()), getThreadShellById: () => Effect.succeed(Option.none()), getThreadDetailById: () => Effect.succeed(Option.none()), getCounts: () => Effect.succeed({ projectCount: 0, threadCount: 0 }), getActiveProjectByWorkspaceRoot: () => Effect.succeed(Option.none()), getFirstActiveThreadIdByProjectId: () => Effect.succeed(Option.none()), getThreadCheckpointContext: () => Effect.succeed(Option.none()), ...options?.layers?.projectionSnapshotQuery, }), ), Layer.provide( Layer.mock(CheckpointDiffQuery)({ getTurnDiff: () => Effect.succeed({ threadId: defaultThreadId, fromTurnCount: 0, toTurnCount: 0, diff: "", }), getFullThreadDiff: () => Effect.succeed({ threadId: defaultThreadId, fromTurnCount: 0, toTurnCount: 0, diff: "", }), ...options?.layers?.checkpointDiffQuery, }), ), ); const appLayer = servedRoutesLayer.pipe( Layer.provide( Layer.mock(BrowserTraceCollector)({ record: () => Effect.void, ...options?.layers?.browserTraceCollector, }), ), Layer.provide( Layer.mock(ServerLifecycleEvents)({ publish: (event) => Effect.succeed({ ...(event as any), sequence: 1 }), snapshot: Effect.succeed({ sequence: 0, events: [] }), stream: Stream.empty, ...options?.layers?.serverLifecycleEvents, }), ), Layer.provide( Layer.mock(ServerRuntimeStartup)({ awaitCommandReady: Effect.void, markHttpListening: Effect.void, enqueueCommand: (effect) => effect, ...options?.layers?.serverRuntimeStartup, }), ), Layer.provide( Layer.mock(ServerEnvironment)({ getEnvironmentId: Effect.succeed(testEnvironmentDescriptor.environmentId), getDescriptor: Effect.succeed(testEnvironmentDescriptor), ...options?.layers?.serverEnvironment, }), ), Layer.provide( Layer.mock(RepositoryIdentityResolver)({ resolve: () => Effect.succeed(null), ...options?.layers?.repositoryIdentityResolver, }), ), Layer.provideMerge(makeAuthTestLayer()), Layer.provide(workspaceAndProjectServicesLayer), Layer.provideMerge(FetchHttpClient.layer), Layer.provide(layerConfig), ); yield* Layer.build(appLayer); return config; }); const parseSessionCookieFromWsUrl = ( wsUrl: string, ): { readonly cookie: string | null; readonly url: string } => { const next = new URL(wsUrl); const cookie = next.hash.startsWith("#cookie=") ? decodeURIComponent(next.hash.slice("#cookie=".length)) : null; next.hash = ""; return { cookie, url: next.toString(), }; }; const wsRpcProtocolLayer = (wsUrl: string) => { const { cookie, url } = parseSessionCookieFromWsUrl(wsUrl); const webSocketConstructorLayer = Layer.succeed( Socket.WebSocketConstructor, (socketUrl, protocols) => new NodeSocket.NodeWS.WebSocket( socketUrl, protocols, cookie ? { headers: { cookie } } : undefined, ) as unknown as globalThis.WebSocket, ); return RpcClient.layerProtocolSocket().pipe( Layer.provide(Socket.layerWebSocket(url).pipe(Layer.provide(webSocketConstructorLayer))), Layer.provide(RpcSerialization.layerJson), ); }; const makeWsRpcClient = RpcClient.make(WsRpcGroup); type WsRpcClient = typeof makeWsRpcClient extends Effect.Effect ? Client : never; const withWsRpcClient = ( wsUrl: string, f: (client: WsRpcClient) => Effect.Effect, ) => makeWsRpcClient.pipe(Effect.flatMap(f), Effect.provide(wsRpcProtocolLayer(wsUrl))); const appendSessionCookieToWsUrl = (url: string, sessionCookieHeader: string) => { const isAbsoluteUrl = /^[a-zA-Z][a-zA-Z\d+.-]*:/.test(url); const next = new URL(url, "http://localhost"); next.hash = `cookie=${encodeURIComponent(sessionCookieHeader)}`; return isAbsoluteUrl ? next.toString() : `${next.pathname}${next.search}${next.hash}`; }; const getHttpServerUrl = (pathname = "") => Effect.gen(function* () { const server = yield* HttpServer.HttpServer; const address = server.address as HttpServer.TcpAddress; return `http://127.0.0.1:${address.port}${pathname}`; }); const bootstrapBrowserSession = ( credential = defaultDesktopBootstrapToken, options?: { readonly headers?: Record; }, ) => Effect.gen(function* () { const bootstrapUrl = yield* getHttpServerUrl("/api/auth/bootstrap"); const response = yield* Effect.promise(() => fetch(bootstrapUrl, { method: "POST", headers: { "content-type": "application/json", ...options?.headers, }, body: JSON.stringify({ credential, }), }), ); const body = (yield* Effect.promise(() => response.json())) as { readonly authenticated: boolean; readonly sessionMethod: string; readonly expiresAt: string; }; return { response, body, cookie: response.headers.get("set-cookie"), }; }); const bootstrapBearerSession = (credential = defaultDesktopBootstrapToken) => Effect.gen(function* () { const bootstrapUrl = yield* getHttpServerUrl("/api/auth/bootstrap/bearer"); const response = yield* Effect.promise(() => fetch(bootstrapUrl, { method: "POST", headers: { "content-type": "application/json", }, body: JSON.stringify({ credential, }), }), ); const body = (yield* Effect.promise(() => response.json())) as { readonly authenticated: boolean; readonly sessionMethod: string; readonly expiresAt: string; readonly sessionToken?: string; readonly error?: string; }; return { response, body, }; }); const getAuthenticatedSessionCookieHeader = (credential = defaultDesktopBootstrapToken) => Effect.gen(function* () { const { response, cookie } = yield* bootstrapBrowserSession(credential); if (!response.ok) { return yield* Effect.fail( new Error(`Expected bootstrap session response to succeed, got ${response.status}`), ); } if (!cookie) { return yield* Effect.fail(new Error("Expected bootstrap session response to set a cookie.")); } return cookie.split(";")[0] ?? cookie; }); const getAuthenticatedBearerSessionToken = (credential = defaultDesktopBootstrapToken) => Effect.gen(function* () { const { response, body } = yield* bootstrapBearerSession(credential); if (!response.ok) { return yield* Effect.fail( new Error(`Expected bearer bootstrap response to succeed, got ${response.status}`), ); } if (!body.sessionToken) { return yield* Effect.fail( new Error("Expected bearer bootstrap response to include a session token."), ); } return body.sessionToken; }); const extractSessionTokenFromSetCookie = (cookieHeader: string): string => { const [nameValue] = cookieHeader.split(";", 1); const token = nameValue?.split("=", 2)[1]; if (!token) { throw new Error("Expected session cookie header to contain a token value."); } return token; }; const splitHeaderTokens = (value: string | null) => (value ?? "") .split(",") .map((token) => token.trim()) .filter((token) => token.length > 0) .toSorted(); const getWsServerUrl = ( pathname = "", options?: { authenticated?: boolean; credential?: string }, ) => Effect.gen(function* () { const server = yield* HttpServer.HttpServer; const address = server.address as HttpServer.TcpAddress; const baseUrl = `ws://127.0.0.1:${address.port}${pathname}`; if (options?.authenticated === false) { return baseUrl; } return appendSessionCookieToWsUrl( baseUrl, yield* getAuthenticatedSessionCookieHeader(options?.credential), ); }); it.layer(NodeServices.layer)("server router seam", (it) => { it.effect("serves static index content for GET / when staticDir is configured", () => Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const path = yield* Path.Path; const staticDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-router-static-" }); const indexPath = path.join(staticDir, "index.html"); yield* fileSystem.writeFileString(indexPath, "router-static-ok"); yield* buildAppUnderTest({ config: { staticDir } }); const response = yield* HttpClient.get("/"); assert.equal(response.status, 200); assert.include(yield* response.text, "router-static-ok"); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("redirects to dev URL when configured", () => Effect.gen(function* () { yield* buildAppUnderTest({ config: { devUrl: new URL("http://127.0.0.1:5173") }, }); const url = yield* getHttpServerUrl("/foo/bar?token=test-token"); const response = yield* Effect.promise(() => fetch(url, { redirect: "manual" })); assert.equal(response.status, 302); assert.equal( response.headers.get("location"), "http://127.0.0.1:5173/foo/bar?token=test-token", ); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("serves project favicon requests before the dev URL redirect", () => Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const path = yield* Path.Path; const projectDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-router-project-favicon-", }); yield* fileSystem.writeFileString( path.join(projectDir, "favicon.svg"), "router-project-favicon", ); yield* buildAppUnderTest({ config: { devUrl: new URL("http://127.0.0.1:5173") }, }); const response = yield* HttpClient.get( `/api/project-favicon?cwd=${encodeURIComponent(projectDir)}`, { headers: { cookie: yield* getAuthenticatedSessionCookieHeader(), }, }, ); assert.equal(response.status, 200); assert.equal(yield* response.text, "router-project-favicon"); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("serves the fallback project favicon when no icon exists", () => Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const projectDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-router-project-favicon-fallback-", }); yield* buildAppUnderTest({ config: { devUrl: new URL("http://127.0.0.1:5173") }, }); const response = yield* HttpClient.get( `/api/project-favicon?cwd=${encodeURIComponent(projectDir)}`, { headers: { cookie: yield* getAuthenticatedSessionCookieHeader(), }, }, ); assert.equal(response.status, 200); assert.include(yield* response.text, 'data-fallback="project-favicon"'); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("serves the public environment descriptor without requiring auth", () => Effect.gen(function* () { yield* buildAppUnderTest(); const url = yield* getHttpServerUrl("/.well-known/t3/environment"); const response = yield* Effect.promise(() => fetch(url)); const body = (yield* Effect.promise(() => response.json(), )) as typeof testEnvironmentDescriptor; assert.equal(response.status, 200); assert.deepEqual(body, testEnvironmentDescriptor); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("reports unauthenticated session state without requiring auth", () => Effect.gen(function* () { yield* buildAppUnderTest(); const url = yield* getHttpServerUrl("/api/auth/session"); const response = yield* Effect.promise(() => fetch(url)); const body = (yield* Effect.promise(() => response.json())) as { readonly authenticated: boolean; readonly auth: { readonly policy: string; readonly bootstrapMethods: ReadonlyArray; readonly sessionMethods: ReadonlyArray; readonly sessionCookieName: string; }; }; assert.equal(response.status, 200); assert.equal(body.authenticated, false); assert.equal(body.auth.policy, "desktop-managed-local"); assert.deepEqual(body.auth.bootstrapMethods, ["desktop-bootstrap"]); assert.deepEqual(body.auth.sessionMethods, [ "browser-session-cookie", "bearer-session-token", ]); assert.isTrue(body.auth.sessionCookieName.startsWith("t3_session_")); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("bootstraps a browser session and authenticates the session endpoint via cookie", () => Effect.gen(function* () { yield* buildAppUnderTest(); const { response: bootstrapResponse, body: bootstrapBody, cookie: setCookie, } = yield* bootstrapBrowserSession(); assert.equal(bootstrapResponse.status, 200); assert.equal(bootstrapBody.authenticated, true); assert.equal(bootstrapBody.sessionMethod, "browser-session-cookie"); assert.isUndefined((bootstrapBody as { readonly sessionToken?: string }).sessionToken); assert.isDefined(setCookie); const sessionUrl = yield* getHttpServerUrl("/api/auth/session"); const sessionResponse = yield* Effect.promise(() => fetch(sessionUrl, { headers: { cookie: setCookie?.split(";")[0] ?? "", }, }), ); const sessionBody = (yield* Effect.promise(() => sessionResponse.json())) as { readonly authenticated: boolean; readonly sessionMethod?: string; }; assert.equal(sessionResponse.status, 200); assert.equal(sessionBody.authenticated, true); assert.equal(sessionBody.sessionMethod, "browser-session-cookie"); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect( "bootstraps a bearer session and authenticates the session endpoint via authorization header", () => Effect.gen(function* () { yield* buildAppUnderTest(); const { response: bootstrapResponse, body: bootstrapBody } = yield* bootstrapBearerSession(); assert.equal(bootstrapResponse.status, 200); assert.equal(bootstrapBody.authenticated, true); assert.equal(bootstrapBody.sessionMethod, "bearer-session-token"); assert.equal(typeof bootstrapBody.sessionToken, "string"); assert.isTrue((bootstrapBody.sessionToken?.length ?? 0) > 0); const sessionUrl = yield* getHttpServerUrl("/api/auth/session"); const sessionResponse = yield* Effect.promise(() => fetch(sessionUrl, { headers: { authorization: `Bearer ${bootstrapBody.sessionToken ?? ""}`, }, }), ); const sessionBody = (yield* Effect.promise(() => sessionResponse.json())) as { readonly authenticated: boolean; readonly sessionMethod?: string; }; assert.equal(sessionResponse.status, 200); assert.equal(sessionBody.authenticated, true); assert.equal(sessionBody.sessionMethod, "bearer-session-token"); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("issues short-lived websocket tokens for authenticated bearer sessions", () => Effect.gen(function* () { yield* buildAppUnderTest(); const bearerToken = yield* getAuthenticatedBearerSessionToken(); const wsTokenUrl = yield* getHttpServerUrl("/api/auth/ws-token"); const wsTokenResponse = yield* Effect.promise(() => fetch(wsTokenUrl, { method: "POST", headers: { authorization: `Bearer ${bearerToken}`, }, }), ); const wsTokenBody = (yield* Effect.promise(() => wsTokenResponse.json())) as { readonly token: string; readonly expiresAt: string; }; assert.equal(wsTokenResponse.status, 200); assert.equal(typeof wsTokenBody.token, "string"); assert.isTrue(wsTokenBody.token.length > 0); assert.equal(typeof wsTokenBody.expiresAt, "string"); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect( "responds to remote auth websocket-token preflight requests with authorization CORS headers", () => Effect.gen(function* () { yield* buildAppUnderTest(); const wsTokenUrl = yield* getHttpServerUrl("/api/auth/ws-token"); const response = yield* Effect.promise(() => fetch(wsTokenUrl, { method: "OPTIONS", headers: { origin: "http://192.168.86.35:3773", "access-control-request-method": "POST", "access-control-request-headers": "authorization", }, }), ); assert.equal(response.status, 204); assert.equal(response.headers.get("access-control-allow-origin"), "*"); assert.deepEqual(splitHeaderTokens(response.headers.get("access-control-allow-methods")), [ "GET", "OPTIONS", "POST", ]); assert.deepEqual(splitHeaderTokens(response.headers.get("access-control-allow-headers")), [ "authorization", "b3", "content-type", "traceparent", ]); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("includes CORS headers on remote websocket-token auth failures", () => Effect.gen(function* () { yield* buildAppUnderTest(); const wsTokenUrl = yield* getHttpServerUrl("/api/auth/ws-token"); const response = yield* Effect.promise(() => fetch(wsTokenUrl, { method: "POST", headers: { origin: "http://192.168.86.35:3773", }, }), ); const body = (yield* Effect.promise(() => response.json())) as { readonly error?: string; }; assert.equal(response.status, 401); assert.equal(response.headers.get("access-control-allow-origin"), "*"); assert.equal(body.error, "Authentication required."); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("issues authenticated one-time pairing credentials for additional clients", () => Effect.gen(function* () { yield* buildAppUnderTest(); const response = yield* HttpClient.post("/api/auth/pairing-token", { headers: { cookie: yield* getAuthenticatedSessionCookieHeader(), }, }); const body = (yield* response.json) as { readonly credential: string; readonly expiresAt: string; }; assert.equal(response.status, 200); assert.equal(typeof body.credential, "string"); assert.isTrue(body.credential.length > 0); assert.equal(typeof body.expiresAt, "string"); const bootstrapResult = yield* bootstrapBrowserSession(body.credential); assert.equal(bootstrapResult.response.status, 200); const reusedResult = yield* bootstrapBrowserSession(body.credential); assert.equal(reusedResult.response.status, 401); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("rejects unauthenticated pairing credential requests", () => Effect.gen(function* () { yield* buildAppUnderTest(); const response = yield* HttpClient.post("/api/auth/pairing-token"); assert.equal(response.status, 401); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("lists and revokes pairing links for owner sessions", () => Effect.gen(function* () { yield* buildAppUnderTest({ config: { host: "0.0.0.0", }, }); const ownerCookie = yield* getAuthenticatedSessionCookieHeader(); const createdResponse = yield* HttpClient.post("/api/auth/pairing-token", { headers: { cookie: ownerCookie, }, }); const createdBody = (yield* createdResponse.json) as { readonly id: string; readonly credential: string; }; const listResponse = yield* HttpClient.get("/api/auth/pairing-links", { headers: { cookie: ownerCookie, }, }); const listedLinks = (yield* listResponse.json) as ReadonlyArray<{ readonly id: string; readonly credential: string; }>; const revokeUrl = yield* getHttpServerUrl("/api/auth/pairing-links/revoke"); const revokeResponse = yield* Effect.promise(() => fetch(revokeUrl, { method: "POST", headers: { cookie: ownerCookie, "content-type": "application/json", }, body: JSON.stringify({ id: createdBody.id }), }), ); const revokedBootstrap = yield* bootstrapBrowserSession(createdBody.credential); assert.equal(createdResponse.status, 200); assert.equal(listResponse.status, 200); assert.isTrue(listedLinks.some((entry) => entry.id === createdBody.id)); assert.equal(revokeResponse.status, 200); assert.equal(revokedBootstrap.response.status, 401); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("rejects pairing credential requests from non-owner paired sessions", () => Effect.gen(function* () { yield* buildAppUnderTest({ config: { host: "0.0.0.0", }, }); const ownerResponse = yield* HttpClient.post("/api/auth/pairing-token", { headers: { cookie: yield* getAuthenticatedSessionCookieHeader(), }, }); const ownerBody = (yield* ownerResponse.json) as { readonly credential: string; }; assert.equal(ownerResponse.status, 200); const pairedSessionCookie = yield* getAuthenticatedSessionCookieHeader(ownerBody.credential); const pairedResponse = yield* HttpClient.post("/api/auth/pairing-token", { headers: { cookie: pairedSessionCookie, }, }); const pairedBody = (yield* pairedResponse.json) as { readonly error: string; }; assert.equal(pairedResponse.status, 403); assert.equal(pairedBody.error, "Only owner sessions can create pairing credentials."); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("lists paired clients and revokes other sessions while keeping the owner", () => Effect.gen(function* () { yield* buildAppUnderTest({ config: { host: "0.0.0.0", }, }); const ownerCookie = yield* getAuthenticatedSessionCookieHeader(); const pairingTokenUrl = yield* getHttpServerUrl("/api/auth/pairing-token"); const ownerPairingResponse = yield* Effect.promise(() => fetch(pairingTokenUrl, { method: "POST", headers: { cookie: ownerCookie, "content-type": "application/json", }, body: JSON.stringify({ label: "Julius iPhone", }), }), ); const ownerPairingBody = (yield* Effect.promise(() => ownerPairingResponse.json())) as { readonly credential: string; readonly label?: string; }; assert.equal(ownerPairingResponse.status, 200); const pairedSessionBootstrap = yield* bootstrapBrowserSession(ownerPairingBody.credential, { headers: { "user-agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Mobile/15E148 Safari/604.1", }, }); const pairedSessionCookie = pairedSessionBootstrap.cookie?.split(";")[0]; assert.isDefined(pairedSessionCookie); const pairedSessionCookieHeader = pairedSessionCookie ?? ""; const listClientsUrl = yield* getHttpServerUrl("/api/auth/clients"); const listBeforeResponse = yield* Effect.promise(() => fetch(listClientsUrl, { headers: { cookie: ownerCookie, }, }), ); const clientsBefore = (yield* Effect.promise(() => listBeforeResponse.json(), )) as ReadonlyArray<{ readonly sessionId: string; readonly current: boolean; readonly client: { readonly label?: string; readonly deviceType: string; readonly ipAddress?: string; readonly os?: string; readonly browser?: string; }; }>; const pairedClientBefore = clientsBefore.find((entry) => !entry.current); const pairedSessionId = clientsBefore.find((entry) => !entry.current)?.sessionId; const revokeOthersResponse = yield* HttpClient.post("/api/auth/clients/revoke-others", { headers: { cookie: ownerCookie, }, }); const revokeOthersBody = (yield* revokeOthersResponse.json) as { readonly revokedCount: number; }; const listAfterResponse = yield* HttpClient.get("/api/auth/clients", { headers: { cookie: ownerCookie, }, }); const clientsAfter = (yield* listAfterResponse.json) as ReadonlyArray<{ readonly sessionId: string; readonly current: boolean; }>; const pairedClientPairingResponse = yield* HttpClient.post("/api/auth/pairing-token", { headers: { cookie: pairedSessionCookieHeader, }, }); const pairedClientPairingBody = (yield* pairedClientPairingResponse.json) as { readonly error: string; }; assert.equal(listBeforeResponse.status, 200); assert.equal(ownerPairingBody.label, "Julius iPhone"); assert.lengthOf(clientsBefore, 2); assert.isDefined(pairedSessionId); assert.isDefined(pairedClientBefore); assert.deepInclude(pairedClientBefore?.client, { label: "Julius iPhone", deviceType: "mobile", os: "iOS", browser: "Safari", ipAddress: "127.0.0.1", }); assert.equal(revokeOthersResponse.status, 200); assert.equal(revokeOthersBody.revokedCount, 1); assert.equal(listAfterResponse.status, 200); assert.lengthOf(clientsAfter, 1); assert.equal(clientsAfter[0]?.current, true); assert.equal(pairedClientPairingResponse.status, 401); assert.equal(pairedClientPairingBody.error, "Unauthorized request."); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("revokes an individual paired client session", () => Effect.gen(function* () { yield* buildAppUnderTest({ config: { host: "0.0.0.0", }, }); const ownerCookie = yield* getAuthenticatedSessionCookieHeader(); const pairingResponse = yield* HttpClient.post("/api/auth/pairing-token", { headers: { cookie: ownerCookie, }, }); const pairingBody = (yield* pairingResponse.json) as { readonly credential: string; }; const pairedSessionCookie = yield* getAuthenticatedSessionCookieHeader( pairingBody.credential, ); const clientsResponse = yield* HttpClient.get("/api/auth/clients", { headers: { cookie: ownerCookie, }, }); const clients = (yield* clientsResponse.json) as ReadonlyArray<{ readonly sessionId: string; readonly current: boolean; }>; const pairedSessionId = clients.find((entry) => !entry.current)?.sessionId; assert.isDefined(pairedSessionId); const revokeUrl = yield* getHttpServerUrl("/api/auth/clients/revoke"); const revokeResponse = yield* Effect.promise(() => fetch(revokeUrl, { method: "POST", headers: { cookie: ownerCookie, "content-type": "application/json", }, body: JSON.stringify({ sessionId: pairedSessionId }), }), ); const pairedClientPairingResponse = yield* HttpClient.post("/api/auth/pairing-token", { headers: { cookie: pairedSessionCookie, }, }); assert.equal(revokeResponse.status, 200); assert.equal(pairedClientPairingResponse.status, 401); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("rejects reusing the same bootstrap credential after it has been exchanged", () => Effect.gen(function* () { yield* buildAppUnderTest(); const first = yield* bootstrapBrowserSession(); const second = yield* bootstrapBrowserSession(); assert.equal(first.response.status, 200); assert.equal(second.response.status, 401); assert.equal( (second.body as { readonly error?: string }).error, "Invalid bootstrap credential.", ); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect( "does not accept session tokens via query parameters on authenticated HTTP routes", () => Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const projectDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-router-project-favicon-query-token-", }); yield* buildAppUnderTest(); const { cookie } = yield* bootstrapBrowserSession(); assert.isDefined(cookie); const sessionToken = extractSessionTokenFromSetCookie(cookie ?? ""); const response = yield* HttpClient.get( `/api/project-favicon?cwd=${encodeURIComponent(projectDir)}&token=${encodeURIComponent(sessionToken)}`, ); assert.equal(response.status, 401); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("accepts websocket rpc handshake with a bootstrapped browser session cookie", () => Effect.gen(function* () { yield* buildAppUnderTest(); const { response: bootstrapResponse, cookie } = yield* bootstrapBrowserSession(); assert.equal(bootstrapResponse.status, 200); assert.isDefined(cookie); const wsUrl = appendSessionCookieToWsUrl( yield* getWsServerUrl("/ws", { authenticated: false }), cookie?.split(";")[0] ?? "", ); const response = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.serverGetConfig]({})), ); assert.equal(response.environment.environmentId, testEnvironmentDescriptor.environmentId); assert.equal(response.auth.policy, "desktop-managed-local"); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect( "rejects websocket rpc handshake when a session token is only provided via query string", () => Effect.gen(function* () { yield* buildAppUnderTest(); const { cookie } = yield* bootstrapBrowserSession(); assert.isDefined(cookie); const sessionToken = extractSessionTokenFromSetCookie(cookie ?? ""); const wsUrl = `${yield* getWsServerUrl("/ws", { authenticated: false })}?token=${encodeURIComponent(sessionToken)}`; const error = yield* Effect.flip( Effect.scoped(withWsRpcClient(wsUrl, (client) => client[WS_METHODS.serverGetConfig]({}))), ); assert.equal(error._tag, "RpcClientError"); assertInclude(String(error), "SocketOpenError"); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect( "accepts websocket rpc handshake with a dedicated websocket token in the query string", () => Effect.gen(function* () { yield* buildAppUnderTest(); const bearerToken = yield* getAuthenticatedBearerSessionToken(); const wsTokenUrl = yield* getHttpServerUrl("/api/auth/ws-token"); const wsTokenResponse = yield* Effect.promise(() => fetch(wsTokenUrl, { method: "POST", headers: { authorization: `Bearer ${bearerToken}`, }, }), ); const wsTokenBody = (yield* Effect.promise(() => wsTokenResponse.json())) as { readonly token: string; }; const wsUrl = `${yield* getWsServerUrl("/ws", { authenticated: false })}?wsToken=${encodeURIComponent(wsTokenBody.token)}`; const response = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.serverGetConfig]({})), ); assert.equal(response.environment.environmentId, testEnvironmentDescriptor.environmentId); assert.equal(response.auth.policy, "desktop-managed-local"); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("serves attachment files from state dir", () => Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const path = yield* Path.Path; const attachmentId = "thread-11111111-1111-4111-8111-111111111111"; const config = yield* buildAppUnderTest(); const attachmentPath = resolveAttachmentRelativePath({ attachmentsDir: config.attachmentsDir, relativePath: `${attachmentId}.bin`, }); assert.isNotNull(attachmentPath, "Attachment path should be resolvable"); yield* fileSystem.makeDirectory(path.dirname(attachmentPath), { recursive: true }); yield* fileSystem.writeFileString(attachmentPath, "attachment-ok"); const response = yield* HttpClient.get(`/attachments/${attachmentId}`, { headers: { cookie: yield* getAuthenticatedSessionCookieHeader(), }, }); assert.equal(response.status, 200); assert.equal(yield* response.text, "attachment-ok"); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("serves attachment files for URL-encoded paths", () => Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const path = yield* Path.Path; const config = yield* buildAppUnderTest(); const attachmentPath = resolveAttachmentRelativePath({ attachmentsDir: config.attachmentsDir, relativePath: "thread%20folder/message%20folder/file%20name.png", }); assert.isNotNull(attachmentPath, "Attachment path should be resolvable"); yield* fileSystem.makeDirectory(path.dirname(attachmentPath), { recursive: true }); yield* fileSystem.writeFileString(attachmentPath, "attachment-encoded-ok"); const response = yield* HttpClient.get( "/attachments/thread%20folder/message%20folder/file%20name.png", { headers: { cookie: yield* getAuthenticatedSessionCookieHeader(), }, }, ); assert.equal(response.status, 200); assert.equal(yield* response.text, "attachment-encoded-ok"); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("proxies browser OTLP trace exports through the server", () => Effect.gen(function* () { const upstreamRequests: Array<{ readonly body: string; readonly contentType: string | null; }> = []; const localTraceRecords: Array = []; const payload = { resourceSpans: [ { resource: { attributes: [ { key: "service.name", value: { stringValue: "t3-web" }, }, ], }, scopeSpans: [ { scope: { name: "effect", version: "4.0.0-beta.43", }, spans: [ { traceId: "11111111111111111111111111111111", spanId: "2222222222222222", parentSpanId: "3333333333333333", name: "RpcClient.server.getSettings", kind: 3, startTimeUnixNano: "1000000", endTimeUnixNano: "2000000", attributes: [ { key: "rpc.method", value: { stringValue: "server.getSettings" }, }, ], events: [ { name: "http.request", timeUnixNano: "1500000", attributes: [ { key: "http.status_code", value: { intValue: "200" }, }, ], }, ], links: [], status: { code: "STATUS_CODE_OK", }, flags: 1, }, ], }, ], }, ], }; const collector = yield* Effect.acquireRelease( Effect.promise(async () => { const NodeHttp = await import("node:http"); return await new Promise<{ readonly close: () => Promise; readonly url: string; }>((resolve, reject) => { const server = NodeHttp.createServer((request, response) => { const chunks: Buffer[] = []; request.on("data", (chunk) => { chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)); }); request.on("end", () => { upstreamRequests.push({ body: Buffer.concat(chunks).toString("utf8"), contentType: request.headers["content-type"] ?? null, }); response.statusCode = 204; response.end(); }); }); server.on("error", reject); server.listen(0, "127.0.0.1", () => { const address = server.address(); if (!address || typeof address === "string") { reject(new Error("Expected TCP collector address")); return; } resolve({ url: `http://127.0.0.1:${address.port}/v1/traces`, close: () => new Promise((resolveClose, rejectClose) => { server.close((error) => { if (error) { rejectClose(error); return; } resolveClose(); }); }), }); }); }); }), ({ close }) => Effect.promise(close), ); yield* buildAppUnderTest({ config: { otlpTracesUrl: collector.url, }, layers: { browserTraceCollector: { record: (records) => Effect.sync(() => { localTraceRecords.push(...records); }), }, }, }); const response = yield* HttpClient.post("/api/observability/v1/traces", { headers: { cookie: yield* getAuthenticatedSessionCookieHeader(), "content-type": "application/json", origin: "http://localhost:5733", }, body: HttpBody.text(JSON.stringify(payload), "application/json"), }); assert.equal(response.status, 204); assert.equal(response.headers["access-control-allow-origin"], "*"); assert.deepEqual(localTraceRecords, [ { type: "otlp-span", name: "RpcClient.server.getSettings", traceId: "11111111111111111111111111111111", spanId: "2222222222222222", parentSpanId: "3333333333333333", sampled: true, kind: "client", startTimeUnixNano: "1000000", endTimeUnixNano: "2000000", durationMs: 1, attributes: { "rpc.method": "server.getSettings", }, resourceAttributes: { "service.name": "t3-web", }, scope: { name: "effect", version: "4.0.0-beta.43", attributes: {}, }, events: [ { name: "http.request", timeUnixNano: "1500000", attributes: { "http.status_code": "200", }, }, ], links: [], status: { code: "STATUS_CODE_OK", }, }, ]); assert.deepEqual(upstreamRequests, [ { body: JSON.stringify(payload), contentType: "application/json", }, ]); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("responds to browser OTLP trace preflight requests with CORS headers", () => Effect.gen(function* () { yield* buildAppUnderTest(); const url = yield* getHttpServerUrl("/api/observability/v1/traces"); const response = yield* Effect.promise(() => fetch(url, { method: "OPTIONS", headers: { origin: "http://localhost:5733", "access-control-request-method": "POST", "access-control-request-headers": "content-type", }, }), ); assert.equal(response.status, 204); assert.equal(response.headers.get("access-control-allow-origin"), "*"); assert.deepEqual(splitHeaderTokens(response.headers.get("access-control-allow-methods")), [ "GET", "OPTIONS", "POST", ]); assert.deepEqual(splitHeaderTokens(response.headers.get("access-control-allow-headers")), [ "authorization", "b3", "content-type", "traceparent", ]); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect( "stores browser OTLP trace exports locally when no upstream collector is configured", () => Effect.gen(function* () { const localTraceRecords: Array = []; const payload = yield* makeBrowserOtlpPayload("client.test"); const resourceSpan = payload.resourceSpans[0]; const scopeSpan = resourceSpan?.scopeSpans[0]; const span = scopeSpan?.spans[0]; assert.notEqual(resourceSpan, undefined); assert.notEqual(scopeSpan, undefined); assert.notEqual(span, undefined); if (!resourceSpan || !scopeSpan || !span) { return; } yield* buildAppUnderTest({ layers: { browserTraceCollector: { record: (records) => Effect.sync(() => { localTraceRecords.push(...records); }), }, }, }); const response = yield* HttpClient.post("/api/observability/v1/traces", { headers: { cookie: yield* getAuthenticatedSessionCookieHeader(), "content-type": "application/json", }, body: HttpBody.text(JSON.stringify(payload), "application/json"), }); assert.equal(response.status, 204); assert.equal(localTraceRecords.length, 1); const record = localTraceRecords[0] as { readonly type: string; readonly name: string; readonly traceId: string; readonly spanId: string; readonly kind: string; readonly attributes: Readonly>; readonly events: ReadonlyArray; readonly links: ReadonlyArray; readonly scope: { readonly name?: string; readonly attributes: Readonly>; }; readonly resourceAttributes: Readonly>; readonly status?: { readonly code?: string; }; }; assert.equal(record.type, "otlp-span"); assert.equal(record.name, span.name); assert.equal(record.traceId, span.traceId); assert.equal(record.spanId, span.spanId); assert.equal(record.kind, "internal"); assert.deepEqual(record.attributes, {}); assert.deepEqual(record.events, []); assert.deepEqual(record.links, []); assert.equal(record.scope.name, scopeSpan.scope.name); assert.deepEqual(record.scope.attributes, {}); assert.equal(record.resourceAttributes["service.name"], "t3-web"); assert.equal(record.status?.code, String(span.status.code)); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("returns 404 for missing attachment id lookups", () => Effect.gen(function* () { yield* buildAppUnderTest(); const response = yield* HttpClient.get( "/attachments/missing-11111111-1111-4111-8111-111111111111", { headers: { cookie: yield* getAuthenticatedSessionCookieHeader(), }, }, ); assert.equal(response.status, 404); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("routes websocket rpc server.upsertKeybinding", () => Effect.gen(function* () { const rule: KeybindingRule = { command: "terminal.toggle", key: "ctrl+k", }; const resolved: ResolvedKeybindingRule = { command: "terminal.toggle", shortcut: { key: "k", metaKey: false, ctrlKey: true, shiftKey: false, altKey: false, modKey: true, }, }; yield* buildAppUnderTest({ layers: { keybindings: { upsertKeybindingRule: () => Effect.succeed([resolved]), }, }, }); const wsUrl = yield* getWsServerUrl("/ws"); const response = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.serverUpsertKeybinding](rule)), ); assert.deepEqual(response.issues, []); assert.deepEqual(response.keybindings, [resolved]); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("rejects websocket rpc handshake when session authentication is missing", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const path = yield* Path.Path; const workspaceDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-ws-auth-required-" }); yield* fs.writeFileString( path.join(workspaceDir, "needle-file.ts"), "export const needle = 1;", ); yield* buildAppUnderTest(); const wsUrl = yield* getWsServerUrl("/ws", { authenticated: false }); const result = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.projectsSearchEntries]({ cwd: workspaceDir, query: "needle", limit: 10, }), ).pipe(Effect.result), ); assertTrue(result._tag === "Failure"); const failureMessage = String(result.failure); assertTrue( failureMessage.includes("SocketOpenError") || failureMessage.includes("SocketCloseError"), ); assertTrue( failureMessage.includes("Unauthorized") || failureMessage.includes("An error occurred during Open"), ); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("routes websocket rpc subscribeServerConfig streams snapshot then update", () => Effect.gen(function* () { const providers = [ { instanceId: ProviderInstanceId.make("codex"), driver: ProviderDriverKind.make("codex"), enabled: true, installed: true, version: "1.0.0", status: "ready" as const, auth: { status: "authenticated" as const }, checkedAt: "2026-04-11T00:00:00.000Z", models: [], slashCommands: [], skills: [], }, ] as const; const changeEvent = { keybindings: [], issues: [], } as const; yield* buildAppUnderTest({ config: { otlpTracesUrl: "http://localhost:4318/v1/traces", otlpMetricsUrl: "http://localhost:4318/v1/metrics", }, layers: { keybindings: { loadConfigState: Effect.succeed({ keybindings: [], issues: [], }), streamChanges: Stream.succeed(changeEvent), }, providerRegistry: { getProviders: Effect.succeed(providers), }, }, }); const wsUrl = yield* getWsServerUrl("/ws"); const events = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.subscribeServerConfig]({}).pipe(Stream.take(2), Stream.runCollect), ), ); const [first, second] = Array.from(events); assert.equal(first?.type, "snapshot"); if (first?.type === "snapshot") { assert.equal(first.version, 1); assert.deepEqual(first.config.keybindings, []); assert.deepEqual(first.config.issues, []); assert.deepEqual(first.config.providers, providers); assert.equal(first.config.observability.logsDirectoryPath.endsWith("/logs"), true); assert.equal(first.config.observability.localTracingEnabled, true); assert.equal(first.config.observability.otlpTracesUrl, "http://localhost:4318/v1/traces"); assert.equal(first.config.observability.otlpTracesEnabled, true); assert.equal(first.config.observability.otlpMetricsUrl, "http://localhost:4318/v1/metrics"); assert.equal(first.config.observability.otlpMetricsEnabled, true); assert.deepEqual(first.config.settings, DEFAULT_SERVER_SETTINGS); } assert.deepEqual(second, { version: 1, type: "keybindingsUpdated", payload: { keybindings: [], issues: [] }, }); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("routes websocket rpc subscribeServerConfig emits provider status updates", () => Effect.gen(function* () { const nextProviders = [ { instanceId: ProviderInstanceId.make("codex"), driver: ProviderDriverKind.make("codex"), enabled: true, installed: true, version: "1.0.0", status: "ready" as const, auth: { status: "authenticated" as const }, checkedAt: "2026-04-11T00:00:00.000Z", models: [], slashCommands: [], skills: [], }, ] as const; yield* buildAppUnderTest({ layers: { keybindings: { loadConfigState: Effect.succeed({ keybindings: [], issues: [], }), streamChanges: Stream.empty, }, providerRegistry: { getProviders: Effect.succeed([]), streamChanges: Stream.succeed(nextProviders), }, }, }); const wsUrl = yield* getWsServerUrl("/ws"); const events = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.subscribeServerConfig]({}).pipe(Stream.take(2), Stream.runCollect), ), ); const [first, second] = Array.from(events); assert.equal(first?.type, "snapshot"); if (first?.type === "snapshot") { assert.deepEqual(first.config.providers, []); } assert.deepEqual(second, { version: 1, type: "providerStatuses", payload: { providers: nextProviders }, }); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect( "routes websocket rpc subscribeServerLifecycle replays snapshot and streams updates", () => Effect.gen(function* () { const lifecycleEvents = [ { version: 1 as const, sequence: 1, type: "welcome" as const, payload: { environment: testEnvironmentDescriptor, cwd: "/tmp/project", projectName: "project", }, }, ] as const; const liveEvents = Stream.make({ version: 1 as const, sequence: 2, type: "ready" as const, payload: { at: new Date().toISOString(), environment: testEnvironmentDescriptor }, }); yield* buildAppUnderTest({ layers: { serverLifecycleEvents: { snapshot: Effect.succeed({ sequence: 1, events: lifecycleEvents, }), stream: liveEvents, }, }, }); const wsUrl = yield* getWsServerUrl("/ws"); const events = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.subscribeServerLifecycle]({}).pipe(Stream.take(2), Stream.runCollect), ), ); const [first, second] = Array.from(events); assert.equal(first?.type, "welcome"); assert.equal(first?.sequence, 1); assert.equal(second?.type, "ready"); assert.equal(second?.sequence, 2); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("routes websocket rpc projects.searchEntries", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const path = yield* Path.Path; const workspaceDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-ws-project-search-" }); yield* fs.writeFileString( path.join(workspaceDir, "needle-file.ts"), "export const needle = 1;", ); yield* buildAppUnderTest(); const wsUrl = yield* getWsServerUrl("/ws"); const response = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.projectsSearchEntries]({ cwd: workspaceDir, query: "needle", limit: 10, }), ), ); assert.isAtLeast(response.entries.length, 1); assert.isTrue(response.entries.some((entry) => entry.path === "needle-file.ts")); assert.equal(response.truncated, false); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("routes websocket rpc projects.searchEntries excludes gitignored files", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const path = yield* Path.Path; const workspaceDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-ws-project-search-gitignored-", }); yield* fs.writeFileString(path.join(workspaceDir, ".gitignore"), ".venv/\n"); yield* fs.makeDirectory(path.join(workspaceDir, ".venv", "lib"), { recursive: true }); yield* fs.writeFileString( path.join(workspaceDir, ".venv", "lib", "ignored-search-target.ts"), "export const ignored = true;", ); yield* fs.makeDirectory(path.join(workspaceDir, "src"), { recursive: true }); yield* fs.writeFileString( path.join(workspaceDir, "src", "tracked.ts"), "export const ok = 1;", ); yield* buildAppUnderTest({ layers: { vcsDriver: { isInsideWorkTree: () => Effect.succeed(true), listWorkspaceFiles: () => Effect.succeed({ paths: ["src/tracked.ts"], truncated: false, freshness: { source: "live-local", observedAt: TEST_EPOCH, expiresAt: Option.none(), }, }), filterIgnoredPaths: (_cwd, relativePaths) => Effect.succeed( relativePaths.filter((relativePath) => !relativePath.startsWith(".venv/")), ), }, }, }); const wsUrl = yield* getWsServerUrl("/ws"); const response = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.projectsSearchEntries]({ cwd: workspaceDir, query: "ignored-search-target", limit: 10, }), ), ); assert.equal(response.entries.length, 0); assert.equal(response.truncated, false); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("routes websocket rpc projects.searchEntries errors", () => Effect.gen(function* () { yield* buildAppUnderTest(); const wsUrl = yield* getWsServerUrl("/ws"); const result = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.projectsSearchEntries]({ cwd: "/definitely/not/a/real/workspace/path", query: "needle", limit: 10, }), ).pipe(Effect.result), ); assertTrue(result._tag === "Failure"); assertTrue(result.failure._tag === "ProjectSearchEntriesError"); assertInclude( result.failure.message, "Workspace root does not exist: /definitely/not/a/real/workspace/path", ); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("routes websocket rpc projects.writeFile", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const path = yield* Path.Path; const workspaceDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-ws-project-write-" }); yield* buildAppUnderTest(); const wsUrl = yield* getWsServerUrl("/ws"); const response = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.projectsWriteFile]({ cwd: workspaceDir, relativePath: "nested/created.txt", contents: "written-by-rpc", }), ), ); assert.equal(response.relativePath, "nested/created.txt"); const persisted = yield* fs.readFileString(path.join(workspaceDir, "nested", "created.txt")); assert.equal(persisted, "written-by-rpc"); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("creates a missing workspace root during websocket project.create dispatch", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const path = yield* Path.Path; const parentDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-ws-project-create-" }); const missingWorkspaceRoot = path.join(parentDir, "nested", "new-project"); yield* buildAppUnderTest(); const wsUrl = yield* getWsServerUrl("/ws"); const response = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[ORCHESTRATION_WS_METHODS.dispatchCommand]({ type: "project.create", commandId: CommandId.make("cmd-project-create-missing-root"), projectId: ProjectId.make("project-create-missing-root"), title: "New Project", workspaceRoot: missingWorkspaceRoot, createWorkspaceRootIfMissing: true, defaultModelSelection: { instanceId: ProviderInstanceId.make("codex"), model: "gpt-5-codex", }, createdAt: new Date().toISOString(), }), ), ); const stat = yield* fs.stat(missingWorkspaceRoot); assert.isAtLeast(response.sequence, 0); assert.equal(stat.type, "Directory"); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("routes websocket rpc projects.writeFile errors", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const workspaceDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-ws-project-write-" }); yield* buildAppUnderTest(); const wsUrl = yield* getWsServerUrl("/ws"); const result = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.projectsWriteFile]({ cwd: workspaceDir, relativePath: "../escape.txt", contents: "nope", }), ).pipe(Effect.result), ); assertTrue(result._tag === "Failure"); assertTrue(result.failure._tag === "ProjectWriteFileError"); assert.equal( result.failure.message, "Workspace file path must stay within the project root.", ); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("routes websocket rpc shell.openInEditor", () => Effect.gen(function* () { let openedInput: { cwd: string; editor: EditorId } | null = null; yield* buildAppUnderTest({ layers: { open: { openInEditor: (input) => Effect.sync(() => { openedInput = input; }), }, }, }); const wsUrl = yield* getWsServerUrl("/ws"); yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.shellOpenInEditor]({ cwd: "/tmp/project", editor: "cursor", }), ), ); assert.deepEqual(openedInput, { cwd: "/tmp/project", editor: "cursor" }); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("routes websocket rpc shell.openInEditor errors", () => Effect.gen(function* () { const openError = new OpenError({ message: "Editor command not found: cursor" }); yield* buildAppUnderTest({ layers: { open: { openInEditor: () => Effect.fail(openError), }, }, }); const wsUrl = yield* getWsServerUrl("/ws"); const result = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.shellOpenInEditor]({ cwd: "/tmp/project", editor: "cursor", }), ).pipe(Effect.result), ); assertFailure(result, openError); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("routes websocket rpc git methods", () => Effect.gen(function* () { yield* buildAppUnderTest({ layers: { gitManager: { invalidateLocalStatus: () => Effect.void, invalidateRemoteStatus: () => Effect.void, invalidateStatus: () => Effect.void, localStatus: () => Effect.succeed({ isRepo: true, hasPrimaryRemote: true, isDefaultRef: true, refName: "main", hasWorkingTreeChanges: false, workingTree: { files: [], insertions: 0, deletions: 0 }, }), remoteStatus: () => Effect.succeed({ hasUpstream: true, aheadCount: 0, behindCount: 0, pr: null, }), status: () => Effect.succeed({ isRepo: true, hasPrimaryRemote: true, isDefaultRef: true, refName: "main", hasWorkingTreeChanges: false, workingTree: { files: [], insertions: 0, deletions: 0 }, hasUpstream: true, aheadCount: 0, behindCount: 0, pr: null, }), runStackedAction: (input, options) => Effect.gen(function* () { const result = { action: "commit" as const, branch: { status: "skipped_not_requested" as const }, commit: { status: "created" as const, commitSha: "abc123", subject: "feat: demo", }, push: { status: "skipped_not_requested" as const }, pr: { status: "skipped_not_requested" as const }, toast: { title: "Committed abc123", description: "feat: demo", cta: { kind: "run_action" as const, label: "Push", action: { kind: "push" as const, }, }, }, }; yield* ( options?.progressReporter?.publish({ actionId: options.actionId ?? input.actionId, cwd: input.cwd, action: input.action, kind: "phase_started", phase: "commit", label: "Committing...", }) ?? Effect.void ); yield* ( options?.progressReporter?.publish({ actionId: options.actionId ?? input.actionId, cwd: input.cwd, action: input.action, kind: "action_finished", result, }) ?? Effect.void ); return result; }), resolvePullRequest: () => Effect.succeed({ pullRequest: { number: 1, title: "Demo PR", url: "https://example.com/pr/1", baseBranch: "main", headBranch: "feature/demo", state: "open", }, }), preparePullRequestThread: () => Effect.succeed({ pullRequest: { number: 1, title: "Demo PR", url: "https://example.com/pr/1", baseBranch: "main", headBranch: "feature/demo", state: "open", }, branch: "feature/demo", worktreePath: null, }), }, gitVcsDriver: { pullCurrentBranch: () => Effect.succeed({ status: "pulled", refName: "main", upstreamRef: "origin/main", }), listRefs: () => Effect.succeed({ refs: [ { name: "main", current: true, isDefault: true, worktreePath: null, }, ], isRepo: true, hasPrimaryRemote: true, nextCursor: null, totalCount: 1, }), createWorktree: () => Effect.succeed({ worktree: { path: "/tmp/wt", refName: "feature/demo" }, }), removeWorktree: () => Effect.void, createRef: (input) => Effect.succeed({ refName: input.refName }), switchRef: (input) => Effect.succeed({ refName: input.refName }), }, vcsDriver: { isInsideWorkTree: () => Effect.succeed(true), }, }, }); const wsUrl = yield* getWsServerUrl("/ws"); const pull = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.vcsPull]({ cwd: "/tmp/repo" })), ); assert.equal(pull.status, "pulled"); const refreshedStatus = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.vcsRefreshStatus]({ cwd: "/tmp/repo" }), ), ); assert.equal(refreshedStatus.isRepo, true); const stackedEvents = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.gitRunStackedAction]({ actionId: "action-1", cwd: "/tmp/repo", action: "commit", }).pipe( Stream.runCollect, Effect.map((events) => Array.from(events)), ), ), ); const lastStackedEvent = stackedEvents.at(-1); assert.equal(lastStackedEvent?.kind, "action_finished"); if (lastStackedEvent?.kind === "action_finished") { assert.equal(lastStackedEvent.result.action, "commit"); } const resolvedPr = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.gitResolvePullRequest]({ cwd: "/tmp/repo", reference: "1", }), ), ); assert.equal(resolvedPr.pullRequest.number, 1); const prepared = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.gitPreparePullRequestThread]({ cwd: "/tmp/repo", reference: "1", mode: "local", }), ), ); assert.equal(prepared.branch, "feature/demo"); const refs = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.vcsListRefs]({ cwd: "/tmp/repo" })), ); assert.equal(refs.refs[0]?.name, "main"); const worktree = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.vcsCreateWorktree]({ cwd: "/tmp/repo", refName: "main", path: null, }), ), ); assert.equal(worktree.worktree.refName, "feature/demo"); yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.vcsRemoveWorktree]({ cwd: "/tmp/repo", path: "/tmp/wt", }), ), ); yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.vcsCreateRef]({ cwd: "/tmp/repo", refName: "feature/new", }), ), ); yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.vcsSwitchRef]({ cwd: "/tmp/repo", refName: "main", }), ), ); yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.vcsInit]({ cwd: "/tmp/repo", }), ), ); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("routes websocket rpc git.pull errors", () => Effect.gen(function* () { const gitError = new GitCommandError({ operation: "pull", command: "git pull --ff-only", cwd: "/tmp/repo", detail: "upstream missing", }); let invalidationCalls = 0; let statusCalls = 0; yield* buildAppUnderTest({ layers: { gitVcsDriver: { pullCurrentBranch: () => Effect.fail(gitError), }, gitManager: { invalidateLocalStatus: () => Effect.sync(() => { invalidationCalls += 1; }), invalidateRemoteStatus: () => Effect.sync(() => { invalidationCalls += 1; }), invalidateStatus: () => Effect.sync(() => { invalidationCalls += 1; }), localStatus: () => Effect.succeed({ isRepo: true, hasPrimaryRemote: true, isDefaultRef: true, refName: "main", hasWorkingTreeChanges: true, workingTree: { files: [], insertions: 0, deletions: 0 }, }), remoteStatus: () => Effect.sync(() => { statusCalls += 1; return { hasUpstream: true, aheadCount: 0, behindCount: 0, pr: null, }; }), status: () => Effect.sync(() => { statusCalls += 1; return { isRepo: true, hasPrimaryRemote: true, isDefaultRef: true, refName: "main", hasWorkingTreeChanges: true, workingTree: { files: [], insertions: 0, deletions: 0 }, hasUpstream: true, aheadCount: 0, behindCount: 0, pr: null, }; }), }, }, }); const wsUrl = yield* getWsServerUrl("/ws"); const result = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.vcsPull]({ cwd: "/tmp/repo" })).pipe( Effect.result, ), ); assertFailure(result, gitError); assert.equal(invalidationCalls, 0); assert.equal(statusCalls, 0); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("routes websocket rpc git.runStackedAction errors after refreshing git status", () => Effect.gen(function* () { const gitError = new GitCommandError({ operation: "commit", command: "git commit", cwd: "/tmp/repo", detail: "nothing to commit", }); let invalidationCalls = 0; let statusCalls = 0; yield* buildAppUnderTest({ layers: { gitManager: { invalidateLocalStatus: () => Effect.sync(() => { invalidationCalls += 1; }), invalidateRemoteStatus: () => Effect.sync(() => { invalidationCalls += 1; }), invalidateStatus: () => Effect.sync(() => { invalidationCalls += 1; }), localStatus: () => Effect.succeed({ isRepo: true, hasPrimaryRemote: true, isDefaultRef: false, refName: "feature/demo", hasWorkingTreeChanges: true, workingTree: { files: [], insertions: 0, deletions: 0 }, }), remoteStatus: () => Effect.sync(() => { statusCalls += 1; return { hasUpstream: true, aheadCount: 0, behindCount: 0, pr: null, }; }), status: () => Effect.sync(() => { statusCalls += 1; return { isRepo: true, hasPrimaryRemote: true, isDefaultRef: false, refName: "feature/demo", hasWorkingTreeChanges: true, workingTree: { files: [], insertions: 0, deletions: 0 }, hasUpstream: true, aheadCount: 0, behindCount: 0, pr: null, }; }), runStackedAction: () => Effect.fail(gitError), }, }, }); const wsUrl = yield* getWsServerUrl("/ws"); const result = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.gitRunStackedAction]({ actionId: "action-1", cwd: "/tmp/repo", action: "commit", }).pipe(Stream.runCollect, Effect.result), ), ); assertFailure(result, gitError); assert.equal(invalidationCalls, 0); assert.equal(statusCalls, 0); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("completes websocket rpc git.pull before background git status refresh finishes", () => Effect.gen(function* () { yield* buildAppUnderTest({ layers: { gitVcsDriver: { pullCurrentBranch: () => Effect.succeed({ status: "pulled" as const, refName: "main", upstreamRef: "origin/main", }), }, gitManager: { invalidateLocalStatus: () => Effect.void, invalidateRemoteStatus: () => Effect.void, localStatus: () => Effect.succeed({ isRepo: true, hasPrimaryRemote: true, isDefaultRef: true, refName: "main", hasWorkingTreeChanges: false, workingTree: { files: [], insertions: 0, deletions: 0 }, }), remoteStatus: () => Effect.sleep(Duration.seconds(2)).pipe( Effect.as({ hasUpstream: true, aheadCount: 0, behindCount: 0, pr: null, }), ), }, }, }); const wsUrl = yield* getWsServerUrl("/ws"); const startedAt = Date.now(); const result = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.vcsPull]({ cwd: "/tmp/repo" })), ); const elapsedMs = Date.now() - startedAt; assert.equal(result.status, "pulled"); assertTrue(elapsedMs < 1_000); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect( "completes websocket rpc git.runStackedAction before background git status refresh finishes", () => Effect.gen(function* () { yield* buildAppUnderTest({ layers: { vcsDriver: { isInsideWorkTree: () => Effect.succeed(true), }, gitManager: { invalidateLocalStatus: () => Effect.void, invalidateRemoteStatus: () => Effect.void, localStatus: () => Effect.succeed({ isRepo: true, hasPrimaryRemote: true, isDefaultRef: false, refName: "feature/demo", hasWorkingTreeChanges: false, workingTree: { files: [], insertions: 0, deletions: 0 }, }), remoteStatus: () => Effect.sleep(Duration.seconds(2)).pipe( Effect.as({ hasUpstream: true, aheadCount: 0, behindCount: 0, pr: null, }), ), runStackedAction: () => Effect.succeed({ action: "commit" as const, branch: { status: "skipped_not_requested" as const }, commit: { status: "created" as const, commitSha: "abc123", subject: "feat: demo", }, push: { status: "skipped_not_requested" as const }, pr: { status: "skipped_not_requested" as const }, toast: { title: "Committed abc123", description: "feat: demo", cta: { kind: "run_action" as const, label: "Push", action: { kind: "push" as const, }, }, }, }), }, }, }); const wsUrl = yield* getWsServerUrl("/ws"); const startedAt = Date.now(); yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.gitRunStackedAction]({ actionId: "action-1", cwd: "/tmp/repo", action: "commit", }).pipe(Stream.runCollect), ), ); const elapsedMs = Date.now() - startedAt; assertTrue(elapsedMs < 1_000); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect( "starts a background local git status refresh after a successful git.runStackedAction", () => Effect.gen(function* () { const localRefreshStarted = yield* Deferred.make(); yield* buildAppUnderTest({ layers: { vcsDriver: { isInsideWorkTree: () => Effect.succeed(true), }, gitManager: { invalidateLocalStatus: () => Effect.void, invalidateRemoteStatus: () => Effect.void, localStatus: () => Deferred.succeed(localRefreshStarted, undefined).pipe( Effect.ignore, Effect.andThen( Effect.succeed({ isRepo: true, hasPrimaryRemote: true, isDefaultRef: false, refName: "feature/demo", hasWorkingTreeChanges: false, workingTree: { files: [], insertions: 0, deletions: 0 }, }), ), ), remoteStatus: () => Effect.sleep(Duration.seconds(2)).pipe( Effect.as({ hasUpstream: true, aheadCount: 0, behindCount: 0, pr: null, }), ), runStackedAction: () => Effect.succeed({ action: "commit" as const, branch: { status: "skipped_not_requested" as const }, commit: { status: "created" as const, commitSha: "abc123", subject: "feat: demo", }, push: { status: "skipped_not_requested" as const }, pr: { status: "skipped_not_requested" as const }, toast: { title: "Committed abc123", description: "feat: demo", cta: { kind: "run_action" as const, label: "Push", action: { kind: "push" as const, }, }, }, }), }, }, }); const wsUrl = yield* getWsServerUrl("/ws"); yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.gitRunStackedAction]({ actionId: "action-1", cwd: "/tmp/repo", action: "commit", }).pipe(Stream.runCollect), ), ); yield* Deferred.await(localRefreshStarted); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("routes websocket rpc orchestration methods", () => Effect.gen(function* () { const now = new Date().toISOString(); const snapshot = { snapshotSequence: 1, updatedAt: now, projects: [ { id: ProjectId.make("project-a"), title: "Project A", workspaceRoot: "/tmp/project-a", defaultModelSelection, scripts: [], createdAt: now, updatedAt: now, deletedAt: null, }, ], threads: [ { id: ThreadId.make("thread-1"), projectId: ProjectId.make("project-a"), title: "Thread A", modelSelection: defaultModelSelection, interactionMode: "default" as const, runtimeMode: "full-access" as const, branch: null, worktreePath: null, createdAt: now, updatedAt: now, archivedAt: null, latestTurn: null, messages: [], session: null, activities: [], proposedPlans: [], checkpoints: [], deletedAt: null, }, ], }; yield* buildAppUnderTest({ layers: { projectionSnapshotQuery: { getSnapshot: () => Effect.succeed(snapshot), }, orchestrationEngine: { dispatch: () => Effect.succeed({ sequence: 7 }), readEvents: () => Stream.empty, }, checkpointDiffQuery: { getTurnDiff: () => Effect.succeed({ threadId: ThreadId.make("thread-1"), fromTurnCount: 0, toTurnCount: 1, diff: "turn-diff", }), getFullThreadDiff: () => Effect.succeed({ threadId: ThreadId.make("thread-1"), fromTurnCount: 0, toTurnCount: 1, diff: "full-diff", }), }, }, }); const wsUrl = yield* getWsServerUrl("/ws"); const dispatchResult = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[ORCHESTRATION_WS_METHODS.dispatchCommand]({ type: "thread.session.stop", commandId: CommandId.make("cmd-1"), threadId: ThreadId.make("thread-1"), createdAt: now, }), ), ); assert.equal(dispatchResult.sequence, 7); const turnDiffResult = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[ORCHESTRATION_WS_METHODS.getTurnDiff]({ threadId: ThreadId.make("thread-1"), fromTurnCount: 0, toTurnCount: 1, }), ), ); assert.equal(turnDiffResult.diff, "turn-diff"); const fullDiffResult = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[ORCHESTRATION_WS_METHODS.getFullThreadDiff]({ threadId: ThreadId.make("thread-1"), toTurnCount: 1, }), ), ); assert.equal(fullDiffResult.diff, "full-diff"); const replayResult = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[ORCHESTRATION_WS_METHODS.replayEvents]({ fromSequenceExclusive: 0, }), ), ); assert.deepEqual(replayResult, []); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("routes websocket rpc orchestration shell snapshot errors", () => Effect.gen(function* () { const projectionError = new PersistenceSqlError({ operation: "ProjectionSnapshotQuery.getShellSnapshot:test", detail: "failed to read projection shell snapshot", }); yield* buildAppUnderTest({ layers: { projectionSnapshotQuery: { getShellSnapshot: () => Effect.fail(projectionError), }, }, }); const wsUrl = yield* getWsServerUrl("/ws"); const result = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[ORCHESTRATION_WS_METHODS.subscribeShell]({}).pipe(Stream.runCollect), ).pipe(Effect.result), ); assertTrue(result._tag === "Failure"); assertTrue(result.failure._tag === "OrchestrationGetSnapshotError"); assertTrue(result.failure.cause instanceof Error); assert.include(result.failure.cause.message, projectionError.message); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("enriches replayed project events with repository identity metadata", () => Effect.gen(function* () { const repositoryIdentity = { canonicalKey: "github.com/t3tools/t3code", locator: { source: "git-remote" as const, remoteName: "origin", remoteUrl: "git@github.com:T3Tools/t3code.git", }, displayName: "T3Tools/t3code", provider: "github", owner: "T3Tools", name: "t3code", }; yield* buildAppUnderTest({ layers: { orchestrationEngine: { readEvents: (_fromSequenceExclusive) => Stream.make({ sequence: 1, eventId: EventId.make("event-1"), aggregateKind: "project", aggregateId: defaultProjectId, occurredAt: "2026-04-05T00:00:00.000Z", commandId: null, causationEventId: null, correlationId: null, metadata: {}, type: "project.created", payload: { projectId: defaultProjectId, title: "Default Project", workspaceRoot: "/tmp/default-project", defaultModelSelection, scripts: [], createdAt: "2026-04-05T00:00:00.000Z", updatedAt: "2026-04-05T00:00:00.000Z", }, } satisfies Extract), }, repositoryIdentityResolver: { resolve: () => Effect.succeed(repositoryIdentity), }, }, }); const wsUrl = yield* getWsServerUrl("/ws"); const replayResult = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[ORCHESTRATION_WS_METHODS.replayEvents]({ fromSequenceExclusive: 0, }), ), ); const replayedEvent = replayResult[0]; assert.equal(replayedEvent?.type, "project.created"); assert.deepEqual( replayedEvent && replayedEvent.type === "project.created" ? replayedEvent.payload.repositoryIdentity : null, repositoryIdentity, ); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("stops the provider session and closes thread terminals after archive", () => Effect.gen(function* () { const threadId = ThreadId.make("thread-archive"); const effects: string[] = []; const dispatchedCommands: Array = []; const now = new Date().toISOString(); yield* buildAppUnderTest({ layers: { terminalManager: { close: (input) => Effect.sync(() => { effects.push(`terminal.close:${input.threadId}`); }), }, orchestrationEngine: { dispatch: (command) => Effect.sync(() => { dispatchedCommands.push(command); effects.push(`dispatch:${command.type}`); return { sequence: dispatchedCommands.length }; }), }, projectionSnapshotQuery: { getThreadShellById: () => Effect.succeed( Option.some( makeDefaultOrchestrationThreadShell({ id: threadId, updatedAt: now, session: { threadId, status: "ready", providerName: "claudeAgent", runtimeMode: "full-access", activeTurnId: null, lastError: null, updatedAt: now, }, }), ), ), }, }, }); const wsUrl = yield* getWsServerUrl("/ws"); const dispatchResult = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[ORCHESTRATION_WS_METHODS.dispatchCommand]({ type: "thread.archive", commandId: CommandId.make("cmd-thread-archive"), threadId, }), ), ); assert.equal(dispatchResult.sequence, 1); assert.deepEqual(effects, [ "dispatch:thread.archive", "dispatch:thread.session.stop", `terminal.close:${threadId}`, ]); const sessionStopCommand = dispatchedCommands[1]; assert.equal(sessionStopCommand?.type, "thread.session.stop"); if (sessionStopCommand?.type === "thread.session.stop") { assert.equal(sessionStopCommand.threadId, threadId); } }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("checks session status before archiving removes the thread from active lookups", () => Effect.gen(function* () { const threadId = ThreadId.make("thread-archive-precheck"); const effects: string[] = []; const dispatchedCommands: Array = []; const now = new Date().toISOString(); let archived = false; yield* buildAppUnderTest({ layers: { terminalManager: { close: (input) => Effect.sync(() => { effects.push(`terminal.close:${input.threadId}`); }), }, orchestrationEngine: { dispatch: (command) => Effect.sync(() => { dispatchedCommands.push(command); effects.push(`dispatch:${command.type}`); if (command.type === "thread.archive") { archived = true; } return { sequence: dispatchedCommands.length }; }), }, projectionSnapshotQuery: { getThreadShellById: () => Effect.sync(() => { effects.push(`query:thread-shell:${archived ? "archived" : "active"}`); return archived ? Option.none() : Option.some( makeDefaultOrchestrationThreadShell({ id: threadId, updatedAt: now, session: { threadId, status: "ready", providerName: "claudeAgent", runtimeMode: "full-access", activeTurnId: null, lastError: null, updatedAt: now, }, }), ); }), }, }, }); const wsUrl = yield* getWsServerUrl("/ws"); const dispatchResult = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[ORCHESTRATION_WS_METHODS.dispatchCommand]({ type: "thread.archive", commandId: CommandId.make("cmd-thread-archive-precheck"), threadId, }), ), ); assert.equal(dispatchResult.sequence, 1); assert.deepEqual(effects, [ "query:thread-shell:active", "dispatch:thread.archive", "dispatch:thread.session.stop", `terminal.close:${threadId}`, ]); assert.deepEqual( dispatchedCommands.map((command) => command.type), ["thread.archive", "thread.session.stop"], ); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("archives without dispatching session stop when the thread has no session", () => Effect.gen(function* () { const threadId = ThreadId.make("thread-archive-no-session"); const effects: string[] = []; const dispatchedCommands: Array = []; yield* buildAppUnderTest({ layers: { terminalManager: { close: (input) => Effect.sync(() => { effects.push(`terminal.close:${input.threadId}`); }), }, orchestrationEngine: { dispatch: (command) => Effect.sync(() => { dispatchedCommands.push(command); effects.push(`dispatch:${command.type}`); return { sequence: dispatchedCommands.length }; }), }, projectionSnapshotQuery: { getThreadShellById: () => Effect.succeed( Option.some(makeDefaultOrchestrationThreadShell({ id: threadId, session: null })), ), }, }, }); const wsUrl = yield* getWsServerUrl("/ws"); const dispatchResult = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[ORCHESTRATION_WS_METHODS.dispatchCommand]({ type: "thread.archive", commandId: CommandId.make("cmd-thread-archive-no-session"), threadId, }), ), ); assert.equal(dispatchResult.sequence, 1); assert.deepEqual(effects, ["dispatch:thread.archive", `terminal.close:${threadId}`]); assert.deepEqual( dispatchedCommands.map((command) => command.type), ["thread.archive"], ); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect( "archives without dispatching session stop when the thread session is already stopped", () => Effect.gen(function* () { const threadId = ThreadId.make("thread-archive-stopped-session"); const effects: string[] = []; const dispatchedCommands: Array = []; const now = new Date().toISOString(); yield* buildAppUnderTest({ layers: { terminalManager: { close: (input) => Effect.sync(() => { effects.push(`terminal.close:${input.threadId}`); }), }, orchestrationEngine: { dispatch: (command) => Effect.sync(() => { dispatchedCommands.push(command); effects.push(`dispatch:${command.type}`); return { sequence: dispatchedCommands.length }; }), }, projectionSnapshotQuery: { getThreadShellById: () => Effect.succeed( Option.some( makeDefaultOrchestrationThreadShell({ id: threadId, updatedAt: now, session: { threadId, status: "stopped", providerName: "claudeAgent", runtimeMode: "full-access", activeTurnId: null, lastError: null, updatedAt: now, }, }), ), ), }, }, }); const wsUrl = yield* getWsServerUrl("/ws"); const dispatchResult = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[ORCHESTRATION_WS_METHODS.dispatchCommand]({ type: "thread.archive", commandId: CommandId.make("cmd-thread-archive-stopped-session"), threadId, }), ), ); assert.equal(dispatchResult.sequence, 1); assert.deepEqual(effects, ["dispatch:thread.archive", `terminal.close:${threadId}`]); assert.deepEqual( dispatchedCommands.map((command) => command.type), ["thread.archive"], ); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("archives and still closes terminals when session stop fails", () => Effect.gen(function* () { const threadId = ThreadId.make("thread-archive-stop-failure"); const effects: string[] = []; const dispatchedCommands: Array = []; const now = new Date().toISOString(); yield* buildAppUnderTest({ layers: { terminalManager: { close: (input) => Effect.sync(() => { effects.push(`terminal.close:${input.threadId}`); }), }, orchestrationEngine: { dispatch: (command) => { dispatchedCommands.push(command); effects.push(`dispatch:${command.type}`); if (command.type === "thread.session.stop") { return Effect.fail( new OrchestrationListenerCallbackError({ listener: "domain-event", detail: "simulated archive stop failure", }), ); } return Effect.succeed({ sequence: dispatchedCommands.length }); }, }, projectionSnapshotQuery: { getThreadShellById: () => Effect.succeed( Option.some( makeDefaultOrchestrationThreadShell({ id: threadId, updatedAt: now, session: { threadId, status: "ready", providerName: "claudeAgent", runtimeMode: "full-access", activeTurnId: null, lastError: null, updatedAt: now, }, }), ), ), }, }, }); const wsUrl = yield* getWsServerUrl("/ws"); const dispatchResult = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[ORCHESTRATION_WS_METHODS.dispatchCommand]({ type: "thread.archive", commandId: CommandId.make("cmd-thread-archive-stop-failure"), threadId, }), ), ); assert.equal(dispatchResult.sequence, 1); assert.deepEqual(effects, [ "dispatch:thread.archive", "dispatch:thread.session.stop", `terminal.close:${threadId}`, ]); assert.deepEqual( dispatchedCommands.map((command) => command.type), ["thread.archive", "thread.session.stop"], ); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("archives and still closes terminals when session stop defects", () => Effect.gen(function* () { const threadId = ThreadId.make("thread-archive-stop-defect"); const effects: string[] = []; const dispatchedCommands: Array = []; const now = new Date().toISOString(); yield* buildAppUnderTest({ layers: { terminalManager: { close: (input) => Effect.sync(() => { effects.push(`terminal.close:${input.threadId}`); }), }, orchestrationEngine: { dispatch: (command) => { dispatchedCommands.push(command); effects.push(`dispatch:${command.type}`); if (command.type === "thread.session.stop") { return Effect.die(new Error("simulated archive stop defect")); } return Effect.succeed({ sequence: dispatchedCommands.length }); }, }, projectionSnapshotQuery: { getThreadShellById: () => Effect.succeed( Option.some( makeDefaultOrchestrationThreadShell({ id: threadId, updatedAt: now, session: { threadId, status: "ready", providerName: "claudeAgent", runtimeMode: "full-access", activeTurnId: null, lastError: null, updatedAt: now, }, }), ), ), }, }, }); const wsUrl = yield* getWsServerUrl("/ws"); const dispatchResult = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[ORCHESTRATION_WS_METHODS.dispatchCommand]({ type: "thread.archive", commandId: CommandId.make("cmd-thread-archive-stop-defect"), threadId, }), ), ); assert.equal(dispatchResult.sequence, 1); assert.deepEqual(effects, [ "dispatch:thread.archive", "dispatch:thread.session.stop", `terminal.close:${threadId}`, ]); assert.deepEqual( dispatchedCommands.map((command) => command.type), ["thread.archive", "thread.session.stop"], ); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect( "bootstraps first-send worktree turns on the server before dispatching turn start", () => Effect.gen(function* () { const dispatchedCommands: Array = []; const refreshStatus = vi.fn((_: string) => Effect.succeed({ isRepo: true, hasPrimaryRemote: true, isDefaultRef: false, refName: "t3code/bootstrap-refName", hasWorkingTreeChanges: false, workingTree: { files: [], insertions: 0, deletions: 0, }, hasUpstream: true, aheadCount: 0, behindCount: 0, pr: null, }), ); const createWorktree = vi.fn( (_: Parameters[0]) => Effect.succeed({ worktree: { refName: "t3code/bootstrap-refName", path: "/tmp/bootstrap-worktree", }, }), ); const runForThread = vi.fn( (_: Parameters[0]) => Effect.succeed({ status: "started" as const, scriptId: "setup", scriptName: "Setup", terminalId: "setup-setup", cwd: "/tmp/bootstrap-worktree", }), ); yield* buildAppUnderTest({ layers: { gitVcsDriver: { createWorktree, }, vcsStatusBroadcaster: { refreshStatus, }, orchestrationEngine: { dispatch: (command) => Effect.sync(() => { dispatchedCommands.push(command); return { sequence: dispatchedCommands.length }; }), readEvents: () => Stream.empty, }, projectSetupScriptRunner: { runForThread, }, }, }); const createdAt = new Date().toISOString(); const wsUrl = yield* getWsServerUrl("/ws"); const response = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[ORCHESTRATION_WS_METHODS.dispatchCommand]({ type: "thread.turn.start", commandId: CommandId.make("cmd-bootstrap-turn-start"), threadId: ThreadId.make("thread-bootstrap"), message: { messageId: MessageId.make("msg-bootstrap"), role: "user", text: "hello", attachments: [], }, modelSelection: defaultModelSelection, runtimeMode: "full-access", interactionMode: "default", bootstrap: { createThread: { projectId: defaultProjectId, title: "Bootstrap Thread", modelSelection: defaultModelSelection, runtimeMode: "full-access", interactionMode: "default", branch: "main", worktreePath: null, createdAt, }, prepareWorktree: { projectCwd: "/tmp/project", baseBranch: "main", branch: "t3code/bootstrap-refName", }, runSetupScript: true, }, createdAt, }), ), ); assert.equal(response.sequence, 5); assert.deepEqual( dispatchedCommands.map((command) => command.type), [ "thread.create", "thread.meta.update", "thread.activity.append", "thread.activity.append", "thread.turn.start", ], ); assert.deepEqual(createWorktree.mock.calls[0]?.[0], { cwd: "/tmp/project", refName: "main", newRefName: "t3code/bootstrap-refName", path: null, }); assert.deepEqual(runForThread.mock.calls[0]?.[0], { threadId: ThreadId.make("thread-bootstrap"), projectId: defaultProjectId, projectCwd: "/tmp/project", worktreePath: "/tmp/bootstrap-worktree", }); assert.deepEqual(refreshStatus.mock.calls[0]?.[0], "/tmp/bootstrap-worktree"); const setupActivities = dispatchedCommands.filter( (command): command is Extract => command.type === "thread.activity.append", ); assert.deepEqual( setupActivities.map((command) => command.activity.kind), ["setup-script.requested", "setup-script.started"], ); const finalCommand = dispatchedCommands[4]; assertTrue(finalCommand?.type === "thread.turn.start"); if (finalCommand?.type === "thread.turn.start") { assert.equal(finalCommand.bootstrap, undefined); } }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("records setup-script failures without aborting bootstrap turn start", () => Effect.gen(function* () { const dispatchedCommands: Array = []; const createWorktree = vi.fn( (_: Parameters[0]) => Effect.succeed({ worktree: { refName: "t3code/bootstrap-refName", path: "/tmp/bootstrap-worktree", }, }), ); const runForThread = vi.fn( (_: Parameters[0]) => Effect.fail(new Error("pty unavailable")), ); yield* buildAppUnderTest({ layers: { gitVcsDriver: { createWorktree, }, orchestrationEngine: { dispatch: (command) => Effect.sync(() => { dispatchedCommands.push(command); return { sequence: dispatchedCommands.length }; }), readEvents: () => Stream.empty, }, projectSetupScriptRunner: { runForThread, }, }, }); const createdAt = new Date().toISOString(); const wsUrl = yield* getWsServerUrl("/ws"); const response = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[ORCHESTRATION_WS_METHODS.dispatchCommand]({ type: "thread.turn.start", commandId: CommandId.make("cmd-bootstrap-turn-start-setup-failure"), threadId: ThreadId.make("thread-bootstrap-setup-failure"), message: { messageId: MessageId.make("msg-bootstrap-setup-failure"), role: "user", text: "hello", attachments: [], }, modelSelection: defaultModelSelection, runtimeMode: "full-access", interactionMode: "default", bootstrap: { createThread: { projectId: defaultProjectId, title: "Bootstrap Thread", modelSelection: defaultModelSelection, runtimeMode: "full-access", interactionMode: "default", branch: "main", worktreePath: null, createdAt, }, prepareWorktree: { projectCwd: "/tmp/project", baseBranch: "main", branch: "t3code/bootstrap-refName", }, runSetupScript: true, }, createdAt, }), ), ); assert.equal(response.sequence, 4); assert.deepEqual( dispatchedCommands.map((command) => command.type), ["thread.create", "thread.meta.update", "thread.activity.append", "thread.turn.start"], ); const setupFailureActivity = dispatchedCommands.find( (command): command is Extract => command.type === "thread.activity.append", ); assert.equal(setupFailureActivity?.activity.kind, "setup-script.failed"); assert.deepEqual(setupFailureActivity?.activity.payload, { detail: "pty unavailable", worktreePath: "/tmp/bootstrap-worktree", }); assertTrue(dispatchedCommands.every((command) => command.type !== "thread.delete")); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("does not misattribute setup activity dispatch failures as setup launch failures", () => Effect.gen(function* () { const dispatchedCommands: Array = []; const createWorktree = vi.fn( (_: Parameters[0]) => Effect.succeed({ worktree: { refName: "t3code/bootstrap-refName", path: "/tmp/bootstrap-worktree", }, }), ); const runForThread = vi.fn( (_: Parameters[0]) => Effect.succeed({ status: "started" as const, scriptId: "setup", scriptName: "Setup", terminalId: "setup-setup", cwd: "/tmp/bootstrap-worktree", }), ); let setupActivityAppendAttempt = 0; yield* buildAppUnderTest({ layers: { gitVcsDriver: { createWorktree, }, orchestrationEngine: { dispatch: (command) => { if ( command.type === "thread.activity.append" && command.activity.kind.startsWith("setup-script.") ) { setupActivityAppendAttempt += 1; if (setupActivityAppendAttempt === 2) { return Effect.fail( new OrchestrationListenerCallbackError({ listener: "domain-event", detail: "failed to append setup-script.started activity", }), ); } } return Effect.sync(() => { dispatchedCommands.push(command); return { sequence: dispatchedCommands.length }; }); }, readEvents: () => Stream.empty, }, projectSetupScriptRunner: { runForThread, }, }, }); const createdAt = new Date().toISOString(); const wsUrl = yield* getWsServerUrl("/ws"); const response = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[ORCHESTRATION_WS_METHODS.dispatchCommand]({ type: "thread.turn.start", commandId: CommandId.make("cmd-bootstrap-turn-start-setup-activity-failure"), threadId: ThreadId.make("thread-bootstrap-setup-activity-failure"), message: { messageId: MessageId.make("msg-bootstrap-setup-activity-failure"), role: "user", text: "hello", attachments: [], }, modelSelection: defaultModelSelection, runtimeMode: "full-access", interactionMode: "default", bootstrap: { createThread: { projectId: defaultProjectId, title: "Bootstrap Thread", modelSelection: defaultModelSelection, runtimeMode: "full-access", interactionMode: "default", branch: "main", worktreePath: null, createdAt, }, prepareWorktree: { projectCwd: "/tmp/project", baseBranch: "main", branch: "t3code/bootstrap-refName", }, runSetupScript: true, }, createdAt, }), ), ); assert.equal(response.sequence, 4); assert.deepEqual( dispatchedCommands.map((command) => command.type), ["thread.create", "thread.meta.update", "thread.activity.append", "thread.turn.start"], ); const setupActivities = dispatchedCommands.filter( (command): command is Extract => command.type === "thread.activity.append", ); assert.deepEqual( setupActivities.map((command) => command.activity.kind), ["setup-script.requested"], ); assertTrue( setupActivities.every((command) => command.activity.kind !== "setup-script.failed"), ); assertTrue(dispatchedCommands.every((command) => command.type !== "thread.delete")); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("cleans up created bootstrap threads when worktree creation defects", () => Effect.gen(function* () { const dispatchedCommands: Array = []; const createWorktree = vi.fn( (_: Parameters[0]) => Effect.die(new Error("worktree exploded")), ); yield* buildAppUnderTest({ layers: { gitVcsDriver: { createWorktree, }, orchestrationEngine: { dispatch: (command) => Effect.sync(() => { dispatchedCommands.push(command); return { sequence: dispatchedCommands.length }; }), readEvents: () => Stream.empty, }, }, }); const createdAt = new Date().toISOString(); const wsUrl = yield* getWsServerUrl("/ws"); const result = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[ORCHESTRATION_WS_METHODS.dispatchCommand]({ type: "thread.turn.start", commandId: CommandId.make("cmd-bootstrap-turn-start-defect"), threadId: ThreadId.make("thread-bootstrap-defect"), message: { messageId: MessageId.make("msg-bootstrap-defect"), role: "user", text: "hello", attachments: [], }, modelSelection: defaultModelSelection, runtimeMode: "full-access", interactionMode: "default", bootstrap: { createThread: { projectId: defaultProjectId, title: "Bootstrap Thread", modelSelection: defaultModelSelection, runtimeMode: "full-access", interactionMode: "default", branch: "main", worktreePath: null, createdAt, }, prepareWorktree: { projectCwd: "/tmp/project", baseBranch: "main", branch: "t3code/bootstrap-refName", }, runSetupScript: false, }, createdAt, }), ).pipe(Effect.result), ); assertTrue(result._tag === "Failure"); assertTrue(result.failure._tag === "OrchestrationDispatchCommandError"); assert.include(result.failure.message, "worktree exploded"); assert.deepEqual( dispatchedCommands.map((command) => command.type), ["thread.create", "thread.delete"], ); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("routes websocket rpc terminal methods", () => Effect.gen(function* () { const snapshot = { threadId: "thread-1", terminalId: "default", cwd: "/tmp/project", worktreePath: null, status: "running" as const, pid: 1234, history: "", exitCode: null, exitSignal: null, updatedAt: new Date().toISOString(), }; yield* buildAppUnderTest({ layers: { terminalManager: { open: () => Effect.succeed(snapshot), write: () => Effect.void, resize: () => Effect.void, clear: () => Effect.void, restart: () => Effect.succeed(snapshot), close: () => Effect.void, }, }, }); const wsUrl = yield* getWsServerUrl("/ws"); const opened = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.terminalOpen]({ threadId: "thread-1", terminalId: "default", cwd: "/tmp/project", }), ), ); assert.equal(opened.terminalId, "default"); yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.terminalWrite]({ threadId: "thread-1", terminalId: "default", data: "echo hi\n", }), ), ); yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.terminalResize]({ threadId: "thread-1", terminalId: "default", cols: 120, rows: 40, }), ), ); yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.terminalClear]({ threadId: "thread-1", terminalId: "default", }), ), ); const restarted = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.terminalRestart]({ threadId: "thread-1", terminalId: "default", cwd: "/tmp/project", cols: 120, rows: 40, }), ), ); assert.equal(restarted.terminalId, "default"); yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.terminalClose]({ threadId: "thread-1", terminalId: "default", }), ), ); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("routes websocket rpc terminal.write errors", () => Effect.gen(function* () { const terminalError = new TerminalNotRunningError({ threadId: "thread-1", terminalId: "default", }); yield* buildAppUnderTest({ layers: { terminalManager: { write: () => Effect.fail(terminalError), }, }, }); const wsUrl = yield* getWsServerUrl("/ws"); const result = yield* Effect.scoped( withWsRpcClient(wsUrl, (client) => client[WS_METHODS.terminalWrite]({ threadId: "thread-1", terminalId: "default", data: "echo fail\n", }), ).pipe(Effect.result), ); assertFailure(result, terminalError); }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); });