Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
5 changes: 4 additions & 1 deletion apps/server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,10 @@ const PtyAdapterLive = Layer.unwrap(
}),
);

const ServerSettingsLayerLive = ServerSettings.layer.pipe(Layer.provide(ServerSecretStore.layer));
const ServerSettingsLayerLive = ServerSettings.layer.pipe(
Layer.provide(ServerSecretStore.layer),
Layer.provideMerge(SqlitePersistenceLayerLive),
);

const NativeTelemetryLayerLive = NativeTelemetryClient.layer.pipe(
Layer.provide(ResourceMonitorBinary.layer),
Expand Down
221 changes: 220 additions & 1 deletion apps/server/src/serverSettings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
DEFAULT_SERVER_SETTINGS,
ProviderDriverKind,
ProviderInstanceId,
resolveProviderInstanceEnabled,
ServerSettings,
ServerSettingsPatch,
} from "@t3tools/contracts";
Expand All @@ -16,8 +17,10 @@ import * as Option from "effect/Option";
import * as PlatformError from "effect/PlatformError";
import * as Schema from "effect/Schema";
import * as Stream from "effect/Stream";
import * as SqlClient from "effect/unstable/sql/SqlClient";
import * as ServerSecretStore from "./auth/ServerSecretStore.ts";
import * as ServerConfig from "./config.ts";
import { SqlitePersistenceMemory } from "./persistence/Layers/Sqlite.ts";
import * as ServerSettingsModule from "./serverSettings.ts";

const decodeSettingsPatch = Schema.decodeUnknownEffect(ServerSettingsPatch);
Expand All @@ -26,6 +29,7 @@ const decodeServerSettings = Schema.decodeUnknownEffect(ServerSettings);
const makeServerSettingsLayer = () =>
ServerSettingsModule.layer.pipe(
Layer.provide(ServerSecretStore.layer),
Layer.provideMerge(Layer.fresh(SqlitePersistenceMemory)),
Layer.provideMerge(
Layer.fresh(
ServerConfig.layerTest(process.cwd(), {
Expand All @@ -47,6 +51,27 @@ const makeFailingSecretStoreLayer = (cause: ServerSecretStore.SecretStoreError)
}),
);

const recordProviderUsage = (provider: string, instanceId: string | null = provider) =>
Effect.gen(function* () {
const sql = yield* SqlClient.SqlClient;
yield* sql`
INSERT INTO projection_thread_sessions (
thread_id,
status,
provider_name,
provider_instance_id,
updated_at
)
VALUES (
${`thread-${instanceId ?? provider}`},
${"ready"},
${provider},
${instanceId},
${"2026-08-25T00:00:00.000Z"}
)
`;
});

it.layer(NodeServices.layer)("server settings", (it) => {
it.effect("preserves context when reading a provider environment secret fails", () => {
const platformCause = PlatformError.systemError({
Expand All @@ -67,6 +92,7 @@ it.layer(NodeServices.layer)("server settings", (it) => {
);
const settingsLayer = ServerSettingsModule.layer.pipe(
Layer.provide(makeFailingSecretStoreLayer(cause)),
Layer.provideMerge(Layer.fresh(SqlitePersistenceMemory)),
Layer.provideMerge(configLayer),
);

Expand Down Expand Up @@ -487,6 +513,192 @@ it.layer(NodeServices.layer)("server settings", (it) => {
}).pipe(Effect.provide(makeServerSettingsLayer())),
);

it.effect("enables previously used providers from sparse settings files", () =>
Effect.gen(function* () {
const serverConfig = yield* ServerConfig.ServerConfig;
const fileSystem = yield* FileSystem.FileSystem;
const serverSettings = yield* ServerSettingsModule.ServerSettingsService;
yield* fileSystem.writeFileString(
serverConfig.settingsPath,
'{"providers":{"opencode":{"serverUrl":"http://127.0.0.1:4096"}}}',
);
yield* recordProviderUsage("opencode");

const settings = yield* serverSettings.getSettings;

assert.isFalse(settings.providers.grok.enabled);
assert.isTrue(settings.providers.opencode.enabled);
assert.isFalse(settings.providers.cursor.enabled);
assert.equal(settings.providers.opencode.serverUrl, "http://127.0.0.1:4096");
}).pipe(Effect.provide(makeServerSettingsLayer())),
);

it.effect("preserves existing provider instances without explicit enabled flags", () =>
Effect.gen(function* () {
const serverConfig = yield* ServerConfig.ServerConfig;
const fileSystem = yield* FileSystem.FileSystem;
const serverSettings = yield* ServerSettingsModule.ServerSettingsService;
yield* fileSystem.writeFileString(
serverConfig.settingsPath,
'{"providerInstances":{"cursor_work":{"driver":"cursor","config":{}},"grok":{"driver":"grok","config":{}},"opencode_work":{"driver":"opencode","config":{"serverUrl":"http://127.0.0.1:4096"}},"opencode_unused":{"driver":"opencode","config":{}}}}',
);
yield* recordProviderUsage("cursor", "cursor_work");
yield* recordProviderUsage("grok", null);
yield* recordProviderUsage("opencode", "opencode_work");

const settings = yield* serverSettings.getSettings;

assert.isTrue(settings.providers.cursor.enabled);
assert.isTrue(settings.providerInstances[ProviderInstanceId.make("cursor_work")]?.enabled);
assert.isTrue(settings.providerInstances[ProviderInstanceId.make("grok")]?.enabled);
assert.isTrue(settings.providerInstances[ProviderInstanceId.make("opencode_work")]?.enabled);
const unused = settings.providerInstances[ProviderInstanceId.make("opencode_unused")];
assert.isDefined(unused);
assert.isFalse(resolveProviderInstanceEnabled(unused));
}).pipe(Effect.provide(makeServerSettingsLayer())),
);

it.effect("preserves explicit provider disables in existing settings files", () =>
Effect.gen(function* () {
const serverConfig = yield* ServerConfig.ServerConfig;
const fileSystem = yield* FileSystem.FileSystem;
const serverSettings = yield* ServerSettingsModule.ServerSettingsService;
yield* fileSystem.writeFileString(
serverConfig.settingsPath,
'{"providers":{"grok":{"enabled":false},"opencode":{"enabled":false},"cursor":{"enabled":false}},"providerInstances":{"grok":{"driver":"grok","enabled":false,"config":{}},"opencode":{"driver":"opencode","config":{"enabled":false}},"cursor":{"driver":"cursor","enabled":false,"config":{}}}}',
);
yield* recordProviderUsage("grok");
yield* recordProviderUsage("opencode");
yield* recordProviderUsage("cursor");

const settings = yield* serverSettings.getSettings;

assert.isFalse(settings.providers.grok.enabled);
assert.isFalse(settings.providers.opencode.enabled);
assert.isFalse(settings.providers.cursor.enabled);
assert.isFalse(settings.providerInstances[ProviderInstanceId.make("grok")]?.enabled);
assert.isFalse(settings.providerInstances[ProviderInstanceId.make("opencode")]?.enabled);
assert.isFalse(settings.providerInstances[ProviderInstanceId.make("cursor")]?.enabled);
}).pipe(Effect.provide(makeServerSettingsLayer())),
);

it.effect("keeps unused providers disabled in existing sparse settings files", () =>
Effect.gen(function* () {
const serverConfig = yield* ServerConfig.ServerConfig;
const fileSystem = yield* FileSystem.FileSystem;
const serverSettings = yield* ServerSettingsModule.ServerSettingsService;
yield* fileSystem.writeFileString(serverConfig.settingsPath, "{}");

const settings = yield* serverSettings.getSettings;

assert.isFalse(settings.providers.grok.enabled);
assert.isFalse(settings.providers.opencode.enabled);
assert.isFalse(settings.providers.cursor.enabled);
}).pipe(Effect.provide(makeServerSettingsLayer())),
);

it.effect("preserves provider history when no settings file exists", () =>
Effect.gen(function* () {
const serverSettings = yield* ServerSettingsModule.ServerSettingsService;
yield* recordProviderUsage("grok");

const settings = yield* serverSettings.getSettings;

assert.isTrue(settings.providers.grok.enabled);
assert.isFalse(settings.providers.opencode.enabled);
assert.isFalse(settings.providers.cursor.enabled);
}).pipe(Effect.provide(makeServerSettingsLayer())),
);

it.effect("restores providers from persisted runtime sessions", () =>
Effect.gen(function* () {
const serverSettings = yield* ServerSettingsModule.ServerSettingsService;
const sql = yield* SqlClient.SqlClient;
yield* sql`
INSERT INTO provider_session_runtime (
thread_id,
provider_name,
provider_instance_id,
adapter_key,
status,
last_seen_at
)
VALUES (
${"thread-opencode-runtime"},
${"opencode"},
${"opencode"},
${"opencode"},
${"ready"},
${"2026-08-25T00:00:00.000Z"}
)
`;

const settings = yield* serverSettings.getSettings;

assert.isFalse(settings.providers.grok.enabled);
assert.isTrue(settings.providers.opencode.enabled);
assert.isFalse(settings.providers.cursor.enabled);
}).pipe(Effect.provide(makeServerSettingsLayer())),
);

it.effect("persists explicit disables after a provider has been used", () =>
Effect.gen(function* () {
const serverConfig = yield* ServerConfig.ServerConfig;
const fileSystem = yield* FileSystem.FileSystem;
const serverSettings = yield* ServerSettingsModule.ServerSettingsService;
yield* recordProviderUsage("grok");

assert.isTrue((yield* serverSettings.getSettings).providers.grok.enabled);

const settings = yield* serverSettings.updateSettings({
providers: { grok: { enabled: false } },
});
assert.isFalse(settings.providers.grok.enabled);

const raw = yield* fileSystem.readFileString(serverConfig.settingsPath);
// @effect-diagnostics-next-line preferSchemaOverJson:off
assert.isFalse(JSON.parse(raw).providers.grok.enabled);
}).pipe(Effect.provide(makeServerSettingsLayer())),
);

it.effect("keeps optional providers disabled after a new installation writes settings", () =>
Effect.gen(function* () {
const serverConfig = yield* ServerConfig.ServerConfig;
const fileSystem = yield* FileSystem.FileSystem;
const serverSettings = yield* ServerSettingsModule.ServerSettingsService;

const initial = yield* serverSettings.getSettings;
assert.isFalse(initial.providers.grok.enabled);
assert.isFalse(initial.providers.opencode.enabled);
assert.isFalse(initial.providers.cursor.enabled);

const next = yield* serverSettings.updateSettings({
addProjectBaseDirectory: "~/Development",
providerInstances: {
[ProviderInstanceId.make("grok")]: {
driver: ProviderDriverKind.make("grok"),
config: {},
},
},
});

assert.isFalse(next.providers.grok.enabled);
assert.isFalse(next.providers.opencode.enabled);
assert.isFalse(next.providers.cursor.enabled);
const grok = next.providerInstances[ProviderInstanceId.make("grok")];
assert.isDefined(grok);
assert.isFalse(resolveProviderInstanceEnabled(grok));

const raw = yield* fileSystem.readFileString(serverConfig.settingsPath);
// @effect-diagnostics-next-line preferSchemaOverJson:off
const persisted = JSON.parse(raw);
assert.isFalse(persisted.providers.cursor.enabled);
assert.isFalse(persisted.providers.grok.enabled);
assert.isFalse(persisted.providers.opencode.enabled);
assert.isUndefined(persisted.providerInstances.grok.enabled);
}).pipe(Effect.provide(makeServerSettingsLayer())),
);

it.effect("folds a legacy in-config enabled flag into the envelope on load", () =>
Effect.gen(function* () {
const serverConfig = yield* ServerConfig.ServerConfig;
Expand Down Expand Up @@ -633,7 +845,7 @@ it.layer(NodeServices.layer)("server settings", (it) => {
}).pipe(Effect.provide(makeServerSettingsLayer())),
);

it.effect("writes only non-default server settings to disk", () =>
it.effect("writes non-default settings and explicit optional provider defaults to disk", () =>
Effect.gen(function* () {
const serverSettings = yield* ServerSettingsModule.ServerSettingsService;
const serverConfig = yield* ServerConfig.ServerConfig;
Expand Down Expand Up @@ -670,7 +882,14 @@ it.layer(NodeServices.layer)("server settings", (it) => {
codex: {
binaryPath: "/opt/homebrew/bin/codex",
},
cursor: {
enabled: false,
},
grok: {
enabled: false,
},
opencode: {
enabled: false,
serverUrl: "http://127.0.0.1:4096",
serverPassword: "secret-password",
},
Expand Down
Loading
Loading