Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion apps/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
20 changes: 16 additions & 4 deletions apps/server/src/geminiCliServerManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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({
Expand Down
7 changes: 3 additions & 4 deletions apps/server/src/kiloServerManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ import {
} from "./kilo/types.ts";

vi.mock("./kilo/serverLifecycle.ts", async () => {
const actual =
await vi.importActual<typeof import("./kilo/serverLifecycle.ts")>(
"./kilo/serverLifecycle.ts",
);
const actual = await vi.importActual<typeof import("./kilo/serverLifecycle.ts")>(
"./kilo/serverLifecycle.ts",
);
return {
...actual,
ensureServer: vi.fn(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
`,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -276,7 +276,7 @@ layer("026_CanonicalizeModelSelectionOptions", (it) => {
)
`;

yield* runMigrations({ toMigrationInclusive: 26 });
yield* runMigrations({ toMigrationInclusive: 28 });

// Projection projects
const projectRows = yield* sql<{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,30 @@ 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;
readonly name: string;
}>`
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",
},
]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -46,7 +44,7 @@ layer("029_BackfillForkProviderInstanceIds", (it) => {
)
`;

yield* sql`
yield* sql`
INSERT INTO projection_threads (
thread_id,
project_id,
Expand Down Expand Up @@ -79,7 +77,7 @@ layer("029_BackfillForkProviderInstanceIds", (it) => {
)
`;

yield* sql`
yield* sql`
INSERT INTO projection_thread_sessions (
thread_id,
status,
Expand All @@ -106,7 +104,7 @@ layer("029_BackfillForkProviderInstanceIds", (it) => {
)
`;

yield* sql`
yield* sql`
INSERT INTO provider_session_runtime (
thread_id,
provider_name,
Expand All @@ -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,
Expand All @@ -156,7 +154,7 @@ layer("029_BackfillForkProviderInstanceIds", (it) => {
NULL
)
`;
yield* sql`
yield* sql`
INSERT INTO projection_threads (
thread_id,
project_id,
Expand Down Expand Up @@ -188,7 +186,7 @@ layer("029_BackfillForkProviderInstanceIds", (it) => {
NULL
)
`;
yield* sql`
yield* sql`
INSERT INTO projection_thread_sessions (
thread_id,
status,
Expand All @@ -214,7 +212,7 @@ layer("029_BackfillForkProviderInstanceIds", (it) => {
NULL
)
`;
yield* sql`
yield* sql`
INSERT INTO provider_session_runtime (
thread_id,
provider_name,
Expand All @@ -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 (
Expand Down
5 changes: 1 addition & 4 deletions apps/server/src/provider/Drivers/GeminiCliDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 3 additions & 1 deletion apps/server/src/provider/Layers/AmpProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/provider/Layers/CodexAdapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading
Loading