diff --git a/apps/server/package.json b/apps/server/package.json index 3f142bfc0d7..82f6d749869 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -19,7 +19,6 @@ "build": "node scripts/cli.ts build", "build:bundle": "tsdown", "start": "node dist/bin.mjs", - "prepare": "effect-language-service patch", "typecheck": "tsc --noEmit", "test": "vitest run", "test:process-reaper": "vitest run src/server.test.ts src/provider/Layers/ClaudeAdapter.test.ts src/provider/Layers/ProviderSessionDirectory.test.ts src/provider/Layers/ProviderSessionReaper.test.ts src/provider/Layers/CodexAdapter.test.ts" diff --git a/apps/server/src/geminiCliServerManager.test.ts b/apps/server/src/geminiCliServerManager.test.ts index c934d1d99a1..91d93c3639d 100644 --- a/apps/server/src/geminiCliServerManager.test.ts +++ b/apps/server/src/geminiCliServerManager.test.ts @@ -104,7 +104,10 @@ describe("GeminiCliServerManager", () => { provider: ProviderDriverKind.make("geminiCli"), runtimeMode: "full-access", cwd: "/tmp", - modelSelection: { instanceId: ProviderInstanceId.make("geminiCli"), model: "gemini-2.5-pro" }, + modelSelection: { + instanceId: ProviderInstanceId.make("geminiCli"), + model: "gemini-2.5-pro", + }, }); expect(session.provider).toBe("geminiCli"); @@ -288,13 +291,19 @@ describe("GeminiCliServerManager", () => { threadId: asThreadId("thread-1"), provider: ProviderDriverKind.make("geminiCli"), runtimeMode: "full-access", - modelSelection: { instanceId: ProviderInstanceId.make("geminiCli"), model: "gemini-3-flash" }, + modelSelection: { + instanceId: ProviderInstanceId.make("geminiCli"), + model: "gemini-3-flash", + }, }); await manager.startSession({ threadId: asThreadId("thread-2"), provider: ProviderDriverKind.make("geminiCli"), runtimeMode: "full-access", - modelSelection: { instanceId: ProviderInstanceId.make("geminiCli"), model: "gemini-2.5-pro" }, + modelSelection: { + instanceId: ProviderInstanceId.make("geminiCli"), + model: "gemini-2.5-pro", + }, }); const sessions = manager.listSessions(); @@ -717,7 +726,10 @@ describe.skipIf(!hasGemini || process.env.RUN_GEMINI_LIVE_TESTS !== "1")( threadId: asThreadId("live-thread"), provider: ProviderDriverKind.make("geminiCli"), runtimeMode: "full-access", - modelSelection: { instanceId: ProviderInstanceId.make("geminiCli"), model: "gemini-2.5-flash" }, + modelSelection: { + instanceId: ProviderInstanceId.make("geminiCli"), + model: "gemini-2.5-flash", + }, }); const result = await manager.sendTurn({ diff --git a/apps/server/src/kiloServerManager.test.ts b/apps/server/src/kiloServerManager.test.ts index aff45303f72..bb180c9bd2d 100644 --- a/apps/server/src/kiloServerManager.test.ts +++ b/apps/server/src/kiloServerManager.test.ts @@ -11,10 +11,9 @@ import { } from "./kilo/types.ts"; vi.mock("./kilo/serverLifecycle.ts", async () => { - const actual = - await vi.importActual( - "./kilo/serverLifecycle.ts", - ); + const actual = await vi.importActual( + "./kilo/serverLifecycle.ts", + ); return { ...actual, ensureServer: vi.fn(), diff --git a/apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts b/apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts index 10639c1b42d..59a396c926d 100644 --- a/apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts +++ b/apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts @@ -274,7 +274,6 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () { has_actionable_proposed_plan AS "hasActionableProposedPlan", deleted_at AS "deletedAt" FROM projection_threads - WHERE json_extract(model_selection_json, '$.provider') IN ('codex','copilot','claudeAgent','cursor','opencode','geminiCli','amp','kilo') ORDER BY created_at ASC, thread_id ASC `, }); diff --git a/apps/server/src/persistence/Migrations/021_RepairProjectionThreadProposedPlanImplementationColumns.test.ts b/apps/server/src/persistence/Migrations/021_RepairProjectionThreadProposedPlanImplementationColumns.test.ts index e14344bc485..d3f5c798e36 100644 --- a/apps/server/src/persistence/Migrations/021_RepairProjectionThreadProposedPlanImplementationColumns.test.ts +++ b/apps/server/src/persistence/Migrations/021_RepairProjectionThreadProposedPlanImplementationColumns.test.ts @@ -39,7 +39,13 @@ layer("021_RepairProjectionThreadProposedPlanImplementationColumns", (it) => { !columnsBeforeRepair.some((column) => column.name === "implementation_thread_id"), ); - yield* runMigrations(); + // Only run through the repair migration itself (registered id 24). + // Running the rest of the chain (e.g. migration #28 touches + // `model_selection_json`) would fail because the faked-as-ran + // migration 16 never actually executed in this scenario, so the + // column doesn't exist. The test only asserts the repair migration's + // own behavior, so stopping after it is safe and accurate. + yield* runMigrations({ toMigrationInclusive: 24 }); const columnsAfterRepair = yield* sql<{ readonly name: string }>` PRAGMA table_info(projection_thread_proposed_plans) diff --git a/apps/server/src/persistence/Migrations/026_CanonicalizeModelSelectionOptions.test.ts b/apps/server/src/persistence/Migrations/026_CanonicalizeModelSelectionOptions.test.ts index ffc42521c90..3f32b0d2a29 100644 --- a/apps/server/src/persistence/Migrations/026_CanonicalizeModelSelectionOptions.test.ts +++ b/apps/server/src/persistence/Migrations/026_CanonicalizeModelSelectionOptions.test.ts @@ -12,7 +12,7 @@ layer("026_CanonicalizeModelSelectionOptions", (it) => { Effect.gen(function* () { const sql = yield* SqlClient.SqlClient; - yield* runMigrations({ toMigrationInclusive: 25 }); + yield* runMigrations({ toMigrationInclusive: 27 }); yield* sql` INSERT INTO projection_projects ( @@ -276,7 +276,7 @@ layer("026_CanonicalizeModelSelectionOptions", (it) => { ) `; - yield* runMigrations({ toMigrationInclusive: 26 }); + yield* runMigrations({ toMigrationInclusive: 28 }); // Projection projects const projectRows = yield* sql<{ diff --git a/apps/server/src/persistence/Migrations/027_028_ProviderInstanceIdColumns.test.ts b/apps/server/src/persistence/Migrations/027_028_ProviderInstanceIdColumns.test.ts index 3233f5043af..e91f689d504 100644 --- a/apps/server/src/persistence/Migrations/027_028_ProviderInstanceIdColumns.test.ts +++ b/apps/server/src/persistence/Migrations/027_028_ProviderInstanceIdColumns.test.ts @@ -12,13 +12,13 @@ layer("027_028_ProviderInstanceIdColumns", (it) => { Effect.gen(function* () { const sql = yield* SqlClient.SqlClient; - yield* runMigrations({ toMigrationInclusive: 26 }); + yield* runMigrations({ toMigrationInclusive: 28 }); yield* sql` ALTER TABLE provider_session_runtime ADD COLUMN provider_instance_id TEXT `; - yield* runMigrations({ toMigrationInclusive: 28 }); + yield* runMigrations({ toMigrationInclusive: 30 }); const migrations = yield* sql<{ readonly migration_id: number; @@ -26,16 +26,16 @@ layer("027_028_ProviderInstanceIdColumns", (it) => { }>` SELECT migration_id, name FROM effect_sql_migrations - WHERE migration_id IN (27, 28) + WHERE migration_id IN (29, 30) ORDER BY migration_id `; assert.deepStrictEqual(migrations, [ { - migration_id: 27, + migration_id: 29, name: "ProviderSessionRuntimeInstanceId", }, { - migration_id: 28, + migration_id: 30, name: "ProjectionThreadSessionInstanceId", }, ]); diff --git a/apps/server/src/persistence/Migrations/029_BackfillForkProviderInstanceIds.test.ts b/apps/server/src/persistence/Migrations/029_BackfillForkProviderInstanceIds.test.ts index 271459f4041..e5ec8011f0b 100644 --- a/apps/server/src/persistence/Migrations/029_BackfillForkProviderInstanceIds.test.ts +++ b/apps/server/src/persistence/Migrations/029_BackfillForkProviderInstanceIds.test.ts @@ -8,22 +8,20 @@ import * as NodeSqliteClient from "../NodeSqliteClient.ts"; const layer = it.layer(Layer.mergeAll(NodeSqliteClient.layerMemory())); layer("029_BackfillForkProviderInstanceIds", (it) => { - it.effect( - "backfills provider_instance_id for fork drivers across both routing tables", - () => - Effect.gen(function* () { - const sql = yield* SqlClient.SqlClient; + it.effect("backfills provider_instance_id for fork drivers across both routing tables", () => + Effect.gen(function* () { + const sql = yield* SqlClient.SqlClient; - // Run all migrations up to (and including) 028 so the - // `provider_instance_id` column exists but is left NULL for - // historical rows. - yield* runMigrations({ toMigrationInclusive: 28 }); + // Run all migrations up to (and including) 028 so the + // `provider_instance_id` column exists but is left NULL for + // historical rows. + yield* runMigrations({ toMigrationInclusive: 30 }); - // Seed a project + thread for each fork driver to satisfy the - // foreign-key relationships used by the projection tables. - const forkKinds = ["amp", "copilot", "geminiCli", "kilo"] as const; - for (const kind of forkKinds) { - yield* sql` + // Seed a project + thread for each fork driver to satisfy the + // foreign-key relationships used by the projection tables. + const forkKinds = ["amp", "copilot", "geminiCli", "kilo"] as const; + for (const kind of forkKinds) { + yield* sql` INSERT INTO projection_projects ( project_id, title, @@ -46,7 +44,7 @@ layer("029_BackfillForkProviderInstanceIds", (it) => { ) `; - yield* sql` + yield* sql` INSERT INTO projection_threads ( thread_id, project_id, @@ -79,7 +77,7 @@ layer("029_BackfillForkProviderInstanceIds", (it) => { ) `; - yield* sql` + yield* sql` INSERT INTO projection_thread_sessions ( thread_id, status, @@ -106,7 +104,7 @@ layer("029_BackfillForkProviderInstanceIds", (it) => { ) `; - yield* sql` + yield* sql` INSERT INTO provider_session_runtime ( thread_id, provider_name, @@ -130,11 +128,11 @@ layer("029_BackfillForkProviderInstanceIds", (it) => { NULL ) `; - } + } - // Also seed a non-fork (upstream) driver row to verify the - // backfill leaves it untouched. - yield* sql` + // Also seed a non-fork (upstream) driver row to verify the + // backfill leaves it untouched. + yield* sql` INSERT INTO projection_projects ( project_id, title, @@ -156,7 +154,7 @@ layer("029_BackfillForkProviderInstanceIds", (it) => { NULL ) `; - yield* sql` + yield* sql` INSERT INTO projection_threads ( thread_id, project_id, @@ -188,7 +186,7 @@ layer("029_BackfillForkProviderInstanceIds", (it) => { NULL ) `; - yield* sql` + yield* sql` INSERT INTO projection_thread_sessions ( thread_id, status, @@ -214,7 +212,7 @@ layer("029_BackfillForkProviderInstanceIds", (it) => { NULL ) `; - yield* sql` + yield* sql` INSERT INTO provider_session_runtime ( thread_id, provider_name, @@ -239,53 +237,53 @@ layer("029_BackfillForkProviderInstanceIds", (it) => { ) `; - // Run migration 029. - yield* runMigrations({ toMigrationInclusive: 31 }); + // Run migration 029. + yield* runMigrations({ toMigrationInclusive: 31 }); - // Each fork row should now have its `provider_instance_id` set - // to the matching driver kind (the default instance id). - for (const kind of forkKinds) { - const sessionRows = yield* sql<{ readonly providerInstanceId: string }>` + // Each fork row should now have its `provider_instance_id` set + // to the matching driver kind (the default instance id). + for (const kind of forkKinds) { + const sessionRows = yield* sql<{ readonly providerInstanceId: string }>` SELECT provider_instance_id AS "providerInstanceId" FROM projection_thread_sessions WHERE thread_id = ${`thread-${kind}`} `; - assert.deepStrictEqual(sessionRows, [{ providerInstanceId: kind }]); + assert.deepStrictEqual(sessionRows, [{ providerInstanceId: kind }]); - const runtimeRows = yield* sql<{ readonly providerInstanceId: string }>` + const runtimeRows = yield* sql<{ readonly providerInstanceId: string }>` SELECT provider_instance_id AS "providerInstanceId" FROM provider_session_runtime WHERE thread_id = ${`thread-${kind}`} `; - assert.deepStrictEqual(runtimeRows, [{ providerInstanceId: kind }]); - } + assert.deepStrictEqual(runtimeRows, [{ providerInstanceId: kind }]); + } - // The upstream-driver row must be untouched (still NULL). - const claudeSession = yield* sql<{ - readonly providerInstanceId: string | null; - }>` + // The upstream-driver row must be untouched (still NULL). + const claudeSession = yield* sql<{ + readonly providerInstanceId: string | null; + }>` SELECT provider_instance_id AS "providerInstanceId" FROM projection_thread_sessions WHERE thread_id = 'thread-claude' `; - assert.deepStrictEqual(claudeSession, [{ providerInstanceId: null }]); + assert.deepStrictEqual(claudeSession, [{ providerInstanceId: null }]); - const claudeRuntime = yield* sql<{ - readonly providerInstanceId: string | null; - }>` + const claudeRuntime = yield* sql<{ + readonly providerInstanceId: string | null; + }>` SELECT provider_instance_id AS "providerInstanceId" FROM provider_session_runtime WHERE thread_id = 'thread-claude' `; - assert.deepStrictEqual(claudeRuntime, [{ providerInstanceId: null }]); - }), + assert.deepStrictEqual(claudeRuntime, [{ providerInstanceId: null }]); + }), ); it.effect("is idempotent — re-running does not overwrite already-set ids", () => Effect.gen(function* () { const sql = yield* SqlClient.SqlClient; - yield* runMigrations({ toMigrationInclusive: 28 }); + yield* runMigrations({ toMigrationInclusive: 30 }); yield* sql` INSERT INTO projection_projects ( diff --git a/apps/server/src/provider/Drivers/GeminiCliDriver.ts b/apps/server/src/provider/Drivers/GeminiCliDriver.ts index 774883c14e7..71e44adafa9 100644 --- a/apps/server/src/provider/Drivers/GeminiCliDriver.ts +++ b/apps/server/src/provider/Drivers/GeminiCliDriver.ts @@ -25,10 +25,7 @@ import { ServerConfig } from "../../config.ts"; import { makeGeminiCliTextGeneration } from "../../textGeneration/GeminiCliTextGeneration.ts"; import { ProviderDriverError } from "../Errors.ts"; import { makeGeminiCliAdapter } from "../Layers/GeminiCliAdapter.ts"; -import { - checkGeminiCliStatus, - makePendingGeminiCliProvider, -} from "../Layers/GeminiCliProvider.ts"; +import { checkGeminiCliStatus, makePendingGeminiCliProvider } from "../Layers/GeminiCliProvider.ts"; import { ProviderEventLoggers } from "../Layers/ProviderEventLoggers.ts"; import { makeManagedServerProvider } from "../makeManagedServerProvider.ts"; import { diff --git a/apps/server/src/provider/Layers/AmpProvider.ts b/apps/server/src/provider/Layers/AmpProvider.ts index d6cf0ea94de..13e431c49d1 100644 --- a/apps/server/src/provider/Layers/AmpProvider.ts +++ b/apps/server/src/provider/Layers/AmpProvider.ts @@ -189,7 +189,9 @@ export const checkAmpStatus = Effect.fn("checkAmpStatus")(function* ( }); }); -export const makePendingAmpProvider = (ampSettings: GenericProviderSettings): ServerProviderDraft => { +export const makePendingAmpProvider = ( + ampSettings: GenericProviderSettings, +): ServerProviderDraft => { const checkedAt = new Date().toISOString(); const models = providerModelsFromSettings( BUILT_IN_MODELS, diff --git a/apps/server/src/provider/Layers/CodexAdapter.test.ts b/apps/server/src/provider/Layers/CodexAdapter.test.ts index 4df4fb5d32f..11f6cc552f3 100644 --- a/apps/server/src/provider/Layers/CodexAdapter.test.ts +++ b/apps/server/src/provider/Layers/CodexAdapter.test.ts @@ -1123,7 +1123,7 @@ it.effect("flushes managed native logs when the adapter layer shuts down", () => const threadLogPath = path.join(tempDir, "thread-logger.log"); assert.equal(fs.existsSync(threadLogPath), true); const contents = fs.readFileSync(threadLogPath, "utf8"); - assert.match(contents, /NTIVE: .*"message":"native flush test"/); + assert.match(contents, /NATIVE: .*"message":"native flush test"/); } finally { if (!scopeClosed) { yield* Scope.close(scope, Exit.void); diff --git a/apps/server/src/provider/Layers/CopilotAdapter.ts b/apps/server/src/provider/Layers/CopilotAdapter.ts index 9195a936b0b..2912dd81aff 100644 --- a/apps/server/src/provider/Layers/CopilotAdapter.ts +++ b/apps/server/src/provider/Layers/CopilotAdapter.ts @@ -78,10 +78,7 @@ import { recordTurnUsage, type CopilotTurnTrackingState, } from "./copilotTurnTracking.ts"; -import { - resolveBundledCopilotCliPath, - withSanitizedCopilotDesktopEnv, -} from "./copilotCliPath.ts"; +import { resolveBundledCopilotCliPath, withSanitizedCopilotDesktopEnv } from "./copilotCliPath.ts"; import { CopilotAdapter, type CopilotAdapterShape } from "../Services/CopilotAdapter.ts"; import { toMessage } from "../toMessage.ts"; import type { @@ -259,9 +256,7 @@ function getCopilotReasoningEffort( const copilot = asRecord(record?.copilot); raw = normalizeString(copilot?.reasoningEffort); } - return raw === "low" || raw === "medium" || raw === "high" || raw === "xhigh" - ? raw - : undefined; + return raw === "low" || raw === "medium" || raw === "high" || raw === "xhigh" ? raw : undefined; } function extractResumeSessionId(resumeCursor: unknown): string | undefined { @@ -590,9 +585,7 @@ export const makeCopilotAdapter = Effect.fn("makeCopilotAdapter")(function* ( ): ReadonlyArray => { const currentTurnId = record.currentTurnId; const currentProviderTurnId = record.currentProviderTurnId; - const resolveOrchestrationTurnId = ( - providerTurnId: TurnId | undefined, - ): TurnId | undefined => { + const resolveOrchestrationTurnId = (providerTurnId: TurnId | undefined): TurnId | undefined => { if (providerTurnId && currentProviderTurnId && providerTurnId === currentProviderTurnId) { return currentTurnId ?? providerTurnId; } @@ -881,7 +874,9 @@ export const makeCopilotAdapter = Effect.fn("makeCopilotAdapter")(function* ( const outputTokens = event.data.outputTokens; const cachedInputTokens = event.data.cacheReadTokens; const durationMs = - event.data.duration !== undefined ? Math.max(0, Math.floor(event.data.duration)) : undefined; + event.data.duration !== undefined + ? Math.max(0, Math.floor(event.data.duration)) + : undefined; const usedTokens = Math.max(0, (inputTokens ?? 0) + (outputTokens ?? 0)); return [ { @@ -908,9 +903,7 @@ export const makeCopilotAdapter = Effect.fn("makeCopilotAdapter")(function* ( case "abort": { const abortedTurnRefs = completionTurnRefs(record); const abortedBase = - abortedTurnRefs.turnId || abortedTurnRefs.providerTurnId - ? base(abortedTurnRefs) - : base(); + abortedTurnRefs.turnId || abortedTurnRefs.providerTurnId ? base(abortedTurnRefs) : base(); return [ { ...abortedBase, @@ -1009,8 +1002,7 @@ export const makeCopilotAdapter = Effect.fn("makeCopilotAdapter")(function* ( type: "task.started", payload: { taskId: - toRuntimeTaskId(event.data.toolCallId) ?? - RuntimeTaskId.make(event.data.toolCallId), + toRuntimeTaskId(event.data.toolCallId) ?? RuntimeTaskId.make(event.data.toolCallId), description: trimToUndefined(event.data.agentDescription), taskType: "subagent", }, @@ -1023,8 +1015,7 @@ export const makeCopilotAdapter = Effect.fn("makeCopilotAdapter")(function* ( type: "task.completed", payload: { taskId: - toRuntimeTaskId(event.data.toolCallId) ?? - RuntimeTaskId.make(event.data.toolCallId), + toRuntimeTaskId(event.data.toolCallId) ?? RuntimeTaskId.make(event.data.toolCallId), status: "completed", ...(trimToUndefined(event.data.agentDisplayName) ? { summary: event.data.agentDisplayName } @@ -1039,8 +1030,7 @@ export const makeCopilotAdapter = Effect.fn("makeCopilotAdapter")(function* ( type: "task.completed", payload: { taskId: - toRuntimeTaskId(event.data.toolCallId) ?? - RuntimeTaskId.make(event.data.toolCallId), + toRuntimeTaskId(event.data.toolCallId) ?? RuntimeTaskId.make(event.data.toolCallId), status: "failed", ...(trimToUndefined(event.data.error) ? { summary: event.data.error } : {}), }, @@ -1174,8 +1164,7 @@ export const makeCopilotAdapter = Effect.fn("makeCopilotAdapter")(function* ( return yield* new ProviderAdapterValidationError({ provider: PROVIDER, operation: "session.reasoningEffort", - issue: - "GitHub Copilot reasoning effort requires an explicit supported model selection.", + issue: "GitHub Copilot reasoning effort requires an explicit supported model selection.", }); } @@ -1308,9 +1297,7 @@ export const makeCopilotAdapter = Effect.fn("makeCopilotAdapter")(function* ( const getSessionRecord = (threadId: ThreadId) => { const record = sessions.get(threadId); if (!record) { - return Effect.fail( - new ProviderAdapterSessionNotFoundError({ provider: PROVIDER, threadId }), - ); + return Effect.fail(new ProviderAdapterSessionNotFoundError({ provider: PROVIDER, threadId })); } return Effect.succeed(record); }; diff --git a/apps/server/src/provider/Layers/CopilotProvider.ts b/apps/server/src/provider/Layers/CopilotProvider.ts index dc72eb4010a..e325a2fd441 100644 --- a/apps/server/src/provider/Layers/CopilotProvider.ts +++ b/apps/server/src/provider/Layers/CopilotProvider.ts @@ -29,10 +29,7 @@ import { spawnAndCollect, type ServerProviderDraft, } from "../providerSnapshot.ts"; -import { - resolveBundledCopilotCliPath, - withSanitizedCopilotDesktopEnv, -} from "./copilotCliPath.ts"; +import { resolveBundledCopilotCliPath, withSanitizedCopilotDesktopEnv } from "./copilotCliPath.ts"; import type { CopilotSettings } from "../Drivers/CopilotSettings.ts"; const PROVIDER = ProviderDriverKind.make("copilot"); diff --git a/apps/server/src/provider/Layers/GeminiCliAdapter.test.ts b/apps/server/src/provider/Layers/GeminiCliAdapter.test.ts index e4d05de30cc..9aa310a38d2 100644 --- a/apps/server/src/provider/Layers/GeminiCliAdapter.test.ts +++ b/apps/server/src/provider/Layers/GeminiCliAdapter.test.ts @@ -18,10 +18,7 @@ import { Context, Effect, Layer, Schema, Stream } from "effect"; import { vi } from "vitest"; import { GeminiCliServerManager } from "../../geminiCliServerManager.ts"; -import { - makeGeminiCliAdapter, - type GeminiCliAdapterShape, -} from "./GeminiCliAdapter.ts"; +import { makeGeminiCliAdapter, type GeminiCliAdapterShape } from "./GeminiCliAdapter.ts"; const asThreadId = (value: string): ThreadId => ThreadId.make(value); const asTurnId = (value: string): TurnId => TurnId.make(value); @@ -121,9 +118,9 @@ const disabledConfig = Schema.decodeSync(GenericProviderSettings)({ enabled: fal const makeAdapterLayer = (manager: FakeGeminiCliManager, config = enabledConfig) => Layer.effect(GeminiCliAdapter, makeGeminiCliAdapter(config, { manager })); -it.effect("delegates session startup to the manager", () => - Effect.gen(function* () { - const manager = new FakeGeminiCliManager(); +it.effect("delegates session startup to the manager", () => { + const manager = new FakeGeminiCliManager(); + return Effect.gen(function* () { const adapter = yield* GeminiCliAdapter; const session = yield* adapter.startSession({ @@ -133,8 +130,8 @@ it.effect("delegates session startup to the manager", () => assert.equal(session.provider, "geminiCli"); assert.equal(manager.startSessionImpl.mock.calls[0]?.[0], asThreadId("thread-1")); - }).pipe(Effect.provide(makeAdapterLayer(new FakeGeminiCliManager())), Effect.scoped), -); + }).pipe(Effect.provide(makeAdapterLayer(manager)), Effect.scoped); +}); it.effect("returns validation error when the provider is disabled", () => Effect.gen(function* () { @@ -175,10 +172,9 @@ it.effect("rejects attachments until Gemini CLI attachment wiring exists", () => }).pipe(Effect.provide(makeAdapterLayer(new FakeGeminiCliManager())), Effect.scoped), ); -it.effect("forwards manager runtime events through the adapter stream", () => - Effect.gen(function* () { - const manager = new FakeGeminiCliManager(); - const layer = makeAdapterLayer(manager); +it.effect("forwards manager runtime events through the adapter stream", () => { + const manager = new FakeGeminiCliManager(); + return Effect.gen(function* () { const adapter = yield* GeminiCliAdapter; const event = { @@ -211,7 +207,5 @@ it.effect("forwards manager runtime events through the adapter stream", () => return; } assert.equal(received.value.payload.delta, "hello"); - - void layer; // keep ref so eslint doesn't complain - }).pipe(Effect.provide(makeAdapterLayer(new FakeGeminiCliManager())), Effect.scoped), -); + }).pipe(Effect.provide(makeAdapterLayer(manager)), Effect.scoped); +}); diff --git a/apps/server/src/provider/Layers/GeminiCliAdapter.ts b/apps/server/src/provider/Layers/GeminiCliAdapter.ts index a1804c80b56..e3ad4fd8dce 100644 --- a/apps/server/src/provider/Layers/GeminiCliAdapter.ts +++ b/apps/server/src/provider/Layers/GeminiCliAdapter.ts @@ -21,10 +21,7 @@ import { import { Effect, Queue, Stream } from "effect"; import { GeminiCliServerManager } from "../../geminiCliServerManager.ts"; -import { - ProviderAdapterValidationError, - type ProviderAdapterError, -} from "../Errors.ts"; +import { ProviderAdapterValidationError, type ProviderAdapterError } from "../Errors.ts"; import type { ProviderAdapterShape } from "../Services/ProviderAdapter.ts"; import type { EventNdjsonLogger } from "./EventNdjsonLogger.ts"; import { makeErrorHelpers } from "./ProviderAdapterUtils.ts"; diff --git a/apps/server/src/provider/Layers/KiloAdapter.ts b/apps/server/src/provider/Layers/KiloAdapter.ts index 4a7e417a639..c0673b3963b 100644 --- a/apps/server/src/provider/Layers/KiloAdapter.ts +++ b/apps/server/src/provider/Layers/KiloAdapter.ts @@ -20,10 +20,7 @@ import { Effect, Queue, Stream } from "effect"; import { KiloServerManager } from "../../kiloServerManager.ts"; import type { KiloSessionStartInput } from "../../kilo/types.ts"; import type { OpenCodeAdapterShape } from "../Services/OpenCodeAdapter.ts"; -import { - ProviderAdapterRequestError, - ProviderAdapterValidationError, -} from "../Errors.ts"; +import { ProviderAdapterRequestError, ProviderAdapterValidationError } from "../Errors.ts"; import { makeErrorHelpers } from "./ProviderAdapterUtils.ts"; import type { KiloSettings } from "./KiloProvider.ts"; @@ -51,8 +48,7 @@ export const makeKiloAdapter = Effect.fn("makeKiloAdapter")(function* ( ) { const _instanceId = options?.instanceId ?? ProviderInstanceId.make("kilo"); void _instanceId; // reserved for future per-instance tagging - const manager = - options?.manager ?? options?.makeManager?.() ?? new KiloServerManager(); + const manager = options?.manager ?? options?.makeManager?.() ?? new KiloServerManager(); const runtimeEventQueue = yield* Queue.unbounded(); // Acquire the manager event listener at scope start, release at scope close. diff --git a/apps/server/src/provider/Layers/KiloProvider.ts b/apps/server/src/provider/Layers/KiloProvider.ts index 768297b54c3..8dba8e3e802 100644 --- a/apps/server/src/provider/Layers/KiloProvider.ts +++ b/apps/server/src/provider/Layers/KiloProvider.ts @@ -167,9 +167,7 @@ export const checkKiloProviderStatus = Effect.fn("checkKiloProviderStatus")(func status: "authenticated", type: "kilo", }, - message: parsedVersion - ? `Kilo v${parsedVersion} detected.` - : "Kilo CLI detected.", + message: parsedVersion ? `Kilo v${parsedVersion} detected.` : "Kilo CLI detected.", }, }); }); diff --git a/apps/server/src/provider/Layers/ProviderRegistry.test.ts b/apps/server/src/provider/Layers/ProviderRegistry.test.ts index 75f9c42936b..8c56f06a8b9 100644 --- a/apps/server/src/provider/Layers/ProviderRegistry.test.ts +++ b/apps/server/src/provider/Layers/ProviderRegistry.test.ts @@ -1027,9 +1027,13 @@ it.layer(Layer.mergeAll(NodeServices.layer, ServerSettingsService.layerTest()))( ); assert.deepStrictEqual(providers.map((provider) => provider.instanceId).toSorted(), [ + "amp", "claudeAgent", "codex", + "copilot", "cursor", + "geminiCli", + "kilo", "opencode", ]); assert.strictEqual(cursorProvider?.enabled, false); diff --git a/apps/server/src/textGeneration/CopilotTextGeneration.ts b/apps/server/src/textGeneration/CopilotTextGeneration.ts index 30911721431..627b301119e 100644 --- a/apps/server/src/textGeneration/CopilotTextGeneration.ts +++ b/apps/server/src/textGeneration/CopilotTextGeneration.ts @@ -32,7 +32,13 @@ export const makeCopilotTextGeneration = Effect.fn("makeCopilotTextGeneration")( _copilotSettings: CopilotSettings, _environment: NodeJS.ProcessEnv = process.env, ) { - const fail = ( + const fail = < + Op extends + | "generateCommitMessage" + | "generatePrContent" + | "generateBranchName" + | "generateThreadTitle", + >( operation: Op, ) => Effect.fail( diff --git a/apps/server/src/textGeneration/KiloTextGeneration.ts b/apps/server/src/textGeneration/KiloTextGeneration.ts index fef1da33142..079b1b5d918 100644 --- a/apps/server/src/textGeneration/KiloTextGeneration.ts +++ b/apps/server/src/textGeneration/KiloTextGeneration.ts @@ -14,16 +14,10 @@ */ import { Effect, Schema } from "effect"; -import { - TextGenerationError, - type ChatAttachment, - type ModelSelection, -} from "@t3tools/contracts"; +import { TextGenerationError, type ChatAttachment, type ModelSelection } from "@t3tools/contracts"; import { sanitizeBranchFragment, sanitizeFeatureBranchName } from "@t3tools/shared/git"; -import { - KiloServerManager, -} from "../kiloServerManager.ts"; +import { KiloServerManager } from "../kiloServerManager.ts"; import { parseKiloModel, readJsonData } from "../kilo/utils.ts"; import { createClient } from "../kilo/serverLifecycle.ts"; import type { KiloProviderOptions, SharedServerState } from "../kilo/types.ts"; @@ -99,7 +93,10 @@ export const makeKiloTextGeneration = Effect.fn("makeKiloTextGeneration")(functi // because `getOrStartServer` is internally serialized. const manager = new KiloServerManager(); - yield* Effect.acquireRelease(Effect.sync(() => manager), (m) => Effect.sync(() => m.stopAll())); + yield* Effect.acquireRelease( + Effect.sync(() => manager), + (m) => Effect.sync(() => m.stopAll()), + ); const resolveBinaryPath = (): string => kiloSettings.binaryPath.trim() || "kilo"; @@ -130,9 +127,7 @@ export const makeKiloTextGeneration = Effect.fn("makeKiloTextGeneration")(functi directory: input.cwd, responseStyle: "data", throwOnError: true, - ...(shared.authHeader - ? { headers: { Authorization: shared.authHeader } } - : {}), + ...(shared.authHeader ? { headers: { Authorization: shared.authHeader } } : {}), }); const created = (await readJsonData( diff --git a/apps/web/package.json b/apps/web/package.json index 66e1f6d017d..253a19334f2 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -6,7 +6,6 @@ "scripts": { "dev": "vite", "build": "vite build", - "prepare": "effect-language-service patch", "preview": "vite preview", "typecheck": "tsc --noEmit", "test": "vitest run --passWithNoTests", diff --git a/apps/web/src/components/ChatView.browser.tsx b/apps/web/src/components/ChatView.browser.tsx index 578dc7c045d..2fb277e3fab 100644 --- a/apps/web/src/components/ChatView.browser.tsx +++ b/apps/web/src/components/ChatView.browser.tsx @@ -2064,7 +2064,7 @@ describe("ChatView timeline estimator parity (full app)", () => { try { await waitForServerConfigToApply(); const menuButton = await waitForElement( - () => document.querySelector('button[aria-label="Copy options"]'), + () => document.querySelector('button[aria-label="Select editor"]'), "Unable to find Open picker button.", ); (menuButton as HTMLButtonElement).click(); @@ -2113,7 +2113,7 @@ describe("ChatView timeline estimator parity (full app)", () => { try { await waitForServerConfigToApply(); const menuButton = await waitForElement( - () => document.querySelector('button[aria-label="Copy options"]'), + () => document.querySelector('button[aria-label="Select editor"]'), "Unable to find Open picker button.", ); (menuButton as HTMLButtonElement).click(); diff --git a/apps/web/src/components/Icons.tsx b/apps/web/src/components/Icons.tsx index cd583f0c2aa..15de42b7df3 100644 --- a/apps/web/src/components/Icons.tsx +++ b/apps/web/src/components/Icons.tsx @@ -910,10 +910,7 @@ export const KiloIcon: Icon = ({ className, ...props }) => ( className={cn(className)} > - + ); diff --git a/bun.lock b/bun.lock index dd137cf65fd..a679c182f43 100644 --- a/bun.lock +++ b/bun.lock @@ -8,10 +8,12 @@ "@anthropic-ai/claude-agent-sdk": "^0.2.104", }, "devDependencies": { + "@effect/language-service": "catalog:", "@types/node": "catalog:", "oxfmt": "^0.40.0", "oxlint": "^1.55.0", "turbo": "^2.8.14", + "typescript": "catalog:", "vitest": "catalog:", }, }, diff --git a/package.json b/package.json index 2a320ec9244..75775461753 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ }, "type": "module", "scripts": { + "prepare": "effect-language-service patch", "dev": "node scripts/dev-runner.ts dev", "dev:server": "node scripts/dev-runner.ts dev:server", "dev:web": "node scripts/dev-runner.ts dev:web", @@ -63,10 +64,12 @@ "@anthropic-ai/claude-agent-sdk": "^0.2.104" }, "devDependencies": { + "@effect/language-service": "catalog:", "@types/node": "catalog:", "oxfmt": "^0.40.0", "oxlint": "^1.55.0", "turbo": "^2.8.14", + "typescript": "catalog:", "vitest": "catalog:" }, "overrides": { diff --git a/packages/client-runtime/package.json b/packages/client-runtime/package.json index fbe97d0dd69..d0da49212ff 100644 --- a/packages/client-runtime/package.json +++ b/packages/client-runtime/package.json @@ -10,7 +10,6 @@ } }, "scripts": { - "prepare": "effect-language-service patch", "typecheck": "tsc --noEmit", "test": "vitest run" }, diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 0e436d64d5e..346125575e6 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -24,7 +24,6 @@ "scripts": { "dev": "tsdown src/index.ts --format esm,cjs --dts --watch --clean", "build": "tsdown src/index.ts --format esm,cjs --dts --clean", - "prepare": "effect-language-service patch", "typecheck": "tsc --noEmit", "test": "vitest run" }, diff --git a/packages/contracts/src/settings.ts b/packages/contracts/src/settings.ts index a8d5d8b4f55..6463515006a 100644 --- a/packages/contracts/src/settings.ts +++ b/packages/contracts/src/settings.ts @@ -340,8 +340,7 @@ export const AmpSettings = makeProviderSettingsSchema( Schema.withDecodingDefault(Effect.succeed("")), Schema.annotateKey({ title: "Config directory", - description: - "Custom Amp configuration directory. Leave blank to use the Amp default.", + description: "Custom Amp configuration directory. Leave blank to use the Amp default.", providerSettingsForm: { placeholder: "~/.config/amp", clearWhenEmpty: "omit", @@ -401,8 +400,7 @@ export const GeminiCliSettings = makeProviderSettingsSchema( binaryPath: makeBinaryPathSetting("gemini").pipe( Schema.annotateKey({ title: "Binary path", - description: - "Path to the `gemini` executable. Leave blank to resolve from PATH.", + description: "Path to the `gemini` executable. Leave blank to resolve from PATH.", providerSettingsForm: { placeholder: "gemini", clearWhenEmpty: "omit" }, }), ), @@ -410,8 +408,7 @@ export const GeminiCliSettings = makeProviderSettingsSchema( Schema.withDecodingDefault(Effect.succeed("")), Schema.annotateKey({ title: "Config directory", - description: - "Override `GEMINI_HOME` (Gemini CLI honours `~/.gemini` by default).", + description: "Override `GEMINI_HOME` (Gemini CLI honours `~/.gemini` by default).", providerSettingsForm: { placeholder: "~/.gemini", clearWhenEmpty: "omit" }, }), ), @@ -443,8 +440,7 @@ export const KiloSettings = makeProviderSettingsSchema( Schema.withDecodingDefault(Effect.succeed("")), Schema.annotateKey({ title: "Config directory", - description: - "Custom Kilo configuration directory. Leave blank to use the Kilo default.", + description: "Custom Kilo configuration directory. Leave blank to use the Kilo default.", providerSettingsForm: { placeholder: "~/.config/kilo", clearWhenEmpty: "omit", diff --git a/packages/effect-acp/package.json b/packages/effect-acp/package.json index 296d64fc4fc..1edd8877540 100644 --- a/packages/effect-acp/package.json +++ b/packages/effect-acp/package.json @@ -35,7 +35,6 @@ "scripts": { "dev": "tsdown src/client.ts src/agent.ts src/_generated/schema.gen.ts src/rpc.ts src/protocol.ts src/terminal.ts --format esm,cjs --dts --watch --clean", "build": "tsdown src/client.ts src/agent.ts src/_generated/schema.gen.ts src/rpc.ts src/protocol.ts src/terminal.ts --format esm,cjs --dts --clean", - "prepare": "effect-language-service patch", "typecheck": "tsc --noEmit", "test": "vitest run", "generate": "bun run scripts/generate.ts" diff --git a/packages/effect-codex-app-server/package.json b/packages/effect-codex-app-server/package.json index 01249f56087..3d69c18f8bb 100644 --- a/packages/effect-codex-app-server/package.json +++ b/packages/effect-codex-app-server/package.json @@ -27,7 +27,6 @@ "scripts": { "dev": "tsdown src/client.ts src/rpc.ts src/protocol.ts src/schema.ts --format esm,cjs --dts --watch --clean", "build": "tsdown src/client.ts src/rpc.ts src/protocol.ts src/schema.ts --format esm,cjs --dts --clean", - "prepare": "effect-language-service patch", "typecheck": "tsc --noEmit", "test": "vitest run", "generate": "bun run scripts/generate.ts", diff --git a/packages/shared/package.json b/packages/shared/package.json index d407a78af58..d42c2039a2b 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -82,7 +82,6 @@ } }, "scripts": { - "prepare": "effect-language-service patch", "typecheck": "tsc --noEmit", "test": "vitest run" }, diff --git a/packages/ssh/package.json b/packages/ssh/package.json index 08793e67c1c..dd66e328874 100644 --- a/packages/ssh/package.json +++ b/packages/ssh/package.json @@ -26,7 +26,6 @@ } }, "scripts": { - "prepare": "effect-language-service patch", "typecheck": "tsc --noEmit", "test": "vitest run" }, diff --git a/packages/tailscale/package.json b/packages/tailscale/package.json index 7088559c74a..c046851fb27 100644 --- a/packages/tailscale/package.json +++ b/packages/tailscale/package.json @@ -10,7 +10,6 @@ } }, "scripts": { - "prepare": "effect-language-service patch", "typecheck": "tsc --noEmit", "test": "vitest run" }, diff --git a/scripts/package.json b/scripts/package.json index 157763b3986..dd801521387 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -3,7 +3,6 @@ "private": true, "type": "module", "scripts": { - "prepare": "effect-language-service patch", "typecheck": "tsc --noEmit", "test": "vitest run" }, diff --git a/scripts/release-smoke.ts b/scripts/release-smoke.ts index 0bdf8b32afa..45c08e8d8c6 100644 --- a/scripts/release-smoke.ts +++ b/scripts/release-smoke.ts @@ -197,6 +197,8 @@ try { }, ); + rmSync(resolve(tempRoot, "bun.lock"), { force: true }); + execFileSync("bun", ["install", "--ignore-scripts"], { cwd: tempRoot, stdio: "inherit",