Skip to content

Commit 3ad2958

Browse files
authored
Merge pull request #37 from aaditagrawal/upstream-sync-server-settings
Integrate upstream: server-authoritative settings (pingdotgg#1421)
2 parents 2a6235c + 96d9271 commit 3ad2958

119 files changed

Lines changed: 6446 additions & 3124 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/server/integration/OrchestrationEngineHarness.integration.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import { ProjectionPendingApprovalRepository } from "../src/persistence/Services
3838
import { ProviderUnsupportedError } from "../src/provider/Errors.ts";
3939
import { ProviderAdapterRegistry } from "../src/provider/Services/ProviderAdapterRegistry.ts";
4040
import { ProviderSessionDirectoryLive } from "../src/provider/Layers/ProviderSessionDirectory.ts";
41+
import { ServerSettingsService } from "../src/serverSettings.ts";
4142
import { makeProviderServiceLive } from "../src/provider/Layers/ProviderService.ts";
4243
import { makeCodexAdapterLive } from "../src/provider/Layers/CodexAdapter.ts";
4344
import { CodexAdapter } from "../src/provider/Services/CodexAdapter.ts";
@@ -295,8 +296,10 @@ export const makeOrchestrationIntegrationHarness = (
295296
providerLayer,
296297
RuntimeReceiptBusLive,
297298
);
299+
const serverSettingsLayer = ServerSettingsService.layerTest();
298300
const runtimeIngestionLayer = ProviderRuntimeIngestionLive.pipe(
299301
Layer.provideMerge(runtimeServicesLayer),
302+
Layer.provideMerge(serverSettingsLayer),
300303
);
301304
const gitCoreLayer = Layer.succeed(GitCore, {
302305
renameBranch: (input: Parameters<GitCoreShape["renameBranch"]>[0]) =>
@@ -309,6 +312,7 @@ export const makeOrchestrationIntegrationHarness = (
309312
Layer.provideMerge(runtimeServicesLayer),
310313
Layer.provideMerge(gitCoreLayer),
311314
Layer.provideMerge(textGenerationLayer),
315+
Layer.provideMerge(serverSettingsLayer),
312316
);
313317
const checkpointReactorLayer = CheckpointReactorLive.pipe(
314318
Layer.provideMerge(runtimeServicesLayer),
@@ -320,6 +324,7 @@ export const makeOrchestrationIntegrationHarness = (
320324
);
321325
const layer = orchestrationReactorLayer.pipe(
322326
Layer.provide(persistenceLayer),
327+
Layer.provideMerge(ServerSettingsService.layerTest()),
323328
Layer.provideMerge(ServerConfig.layerTest(workspaceDir, rootDir)),
324329
Layer.provideMerge(NodeServices.layer),
325330
);

apps/server/integration/providerService.integration.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { ProviderRuntimeEvent } from "@t3tools/contracts";
22
import { ThreadId } from "@t3tools/contracts";
3+
import { DEFAULT_SERVER_SETTINGS } from "@t3tools/contracts/settings";
34
import * as NodeServices from "@effect/platform-node/NodeServices";
45
import { it, assert } from "@effect/vitest";
56
import { Effect, FileSystem, Layer, Path, Queue, Stream } from "effect";
@@ -12,6 +13,7 @@ import {
1213
ProviderService,
1314
type ProviderServiceShape,
1415
} from "../src/provider/Services/ProviderService.ts";
16+
import { ServerSettingsService } from "../src/serverSettings.ts";
1517
import { AnalyticsService } from "../src/telemetry/Services/AnalyticsService.ts";
1618
import { SqlitePersistenceMemory } from "../src/persistence/Layers/Sqlite.ts";
1719
import { ProviderSessionRuntimeRepositoryLive } from "../src/persistence/Layers/ProviderSessionRuntime.ts";
@@ -60,6 +62,7 @@ const makeIntegrationFixture = Effect.gen(function* () {
6062
const shared = Layer.mergeAll(
6163
directoryLayer,
6264
Layer.succeed(ProviderAdapterRegistry, registry),
65+
ServerSettingsService.layerTest(DEFAULT_SERVER_SETTINGS),
6366
AnalyticsService.layerTest,
6467
).pipe(Layer.provide(SqlitePersistenceMemory));
6568

apps/server/src/ampServerManager.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,7 @@ export class AmpServerManager extends EventEmitter<{
187187
}
188188
}
189189

190-
const ampOpts = input.providerOptions?.amp as AmpProviderOptions | undefined;
191-
const binaryPath = ampOpts?.binaryPath ?? defaultBinaryPath();
190+
const binaryPath = defaultBinaryPath();
192191
const cwd = input.cwd ?? process.cwd();
193192
const model = input.modelSelection?.model;
194193
const now = new Date().toISOString();

apps/server/src/codexAppServerManager.test.ts

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,7 @@ describe("startSession", () => {
371371
manager.startSession({
372372
threadId: asThreadId("thread-1"),
373373
provider: "codex",
374+
binaryPath: "codex",
374375
runtimeMode: "full-access",
375376
}),
376377
).rejects.toThrow("cwd missing");
@@ -419,6 +420,7 @@ describe("startSession", () => {
419420
manager.startSession({
420421
threadId: asThreadId("thread-1"),
421422
provider: "codex",
423+
binaryPath: "codex",
422424
runtimeMode: "full-access",
423425
}),
424426
).rejects.toThrow(
@@ -1011,12 +1013,8 @@ describe.skipIf(!process.env.CODEX_BINARY_PATH)("startSession live Codex resume"
10111013
provider: "codex",
10121014
cwd: workspaceDir,
10131015
runtimeMode: "full-access",
1014-
providerOptions: {
1015-
codex: {
1016-
...(process.env.CODEX_BINARY_PATH ? { binaryPath: process.env.CODEX_BINARY_PATH } : {}),
1017-
...(process.env.CODEX_HOME_PATH ? { homePath: process.env.CODEX_HOME_PATH } : {}),
1018-
},
1019-
},
1016+
binaryPath: process.env.CODEX_BINARY_PATH!,
1017+
...(process.env.CODEX_HOME_PATH ? { homePath: process.env.CODEX_HOME_PATH } : {}),
10201018
});
10211019

10221020
const firstTurn = await manager.sendTurn({
@@ -1046,12 +1044,8 @@ describe.skipIf(!process.env.CODEX_BINARY_PATH)("startSession live Codex resume"
10461044
cwd: workspaceDir,
10471045
runtimeMode: "approval-required",
10481046
resumeCursor: firstSession.resumeCursor,
1049-
providerOptions: {
1050-
codex: {
1051-
...(process.env.CODEX_BINARY_PATH ? { binaryPath: process.env.CODEX_BINARY_PATH } : {}),
1052-
...(process.env.CODEX_HOME_PATH ? { homePath: process.env.CODEX_HOME_PATH } : {}),
1053-
},
1054-
},
1047+
binaryPath: process.env.CODEX_BINARY_PATH!,
1048+
...(process.env.CODEX_HOME_PATH ? { homePath: process.env.CODEX_HOME_PATH } : {}),
10551049
});
10561050

10571051
expect(resumedSession.threadId).toBe(originalThreadId);

apps/server/src/codexAppServerManager.ts

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import {
1414
type ProviderApprovalDecision,
1515
type ProviderEvent,
1616
type ProviderSession,
17-
type ProviderSessionStartInput,
1817
type ProviderTurnStartResult,
1918
RuntimeMode,
2019
ProviderInteractionMode,
@@ -132,7 +131,8 @@ export interface CodexAppServerStartSessionInput {
132131
readonly model?: string;
133132
readonly serviceTier?: string;
134133
readonly resumeCursor?: unknown;
135-
readonly providerOptions?: ProviderSessionStartInput["providerOptions"];
134+
readonly binaryPath: string;
135+
readonly homePath?: string;
136136
readonly runtimeMode: RuntimeMode;
137137
}
138138

@@ -543,9 +543,8 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve
543543
updatedAt: now,
544544
};
545545

546-
const codexOptions = readCodexProviderOptions(input);
547-
const codexBinaryPath = codexOptions.binaryPath ?? "codex";
548-
const codexHomePath = codexOptions.homePath;
546+
const codexBinaryPath = input.binaryPath;
547+
const codexHomePath = input.homePath;
549548
this.assertSupportedCodexCliVersion({
550549
binaryPath: codexBinaryPath,
551550
cwd: resolvedCwd,
@@ -1642,20 +1641,6 @@ function normalizeProviderThreadId(value: string | undefined): string | undefine
16421641
return brandIfNonEmpty(value, (normalized) => normalized);
16431642
}
16441643

1645-
function readCodexProviderOptions(input: CodexAppServerStartSessionInput): {
1646-
readonly binaryPath?: string;
1647-
readonly homePath?: string;
1648-
} {
1649-
const options = input.providerOptions?.codex;
1650-
if (!options) {
1651-
return {};
1652-
}
1653-
return {
1654-
...(options.binaryPath ? { binaryPath: options.binaryPath } : {}),
1655-
...(options.homePath ? { homePath: options.homePath } : {}),
1656-
};
1657-
}
1658-
16591644
function assertSupportedCodexCliVersion(input: {
16601645
readonly binaryPath: string;
16611646
readonly cwd: string;
@@ -1709,7 +1694,11 @@ function readResumeCursorThreadId(resumeCursor: unknown): string | undefined {
17091694
return typeof rawThreadId === "string" ? normalizeProviderThreadId(rawThreadId) : undefined;
17101695
}
17111696

1712-
function readResumeThreadId(input: CodexAppServerStartSessionInput): string | undefined {
1697+
function readResumeThreadId(input: {
1698+
readonly resumeCursor?: unknown;
1699+
readonly threadId?: ThreadId;
1700+
readonly runtimeMode?: RuntimeMode;
1701+
}): string | undefined {
17131702
return readResumeCursorThreadId(input.resumeCursor);
17141703
}
17151704

apps/server/src/config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export interface ServerDerivedPaths {
1919
readonly stateDir: string;
2020
readonly dbPath: string;
2121
readonly keybindingsConfigPath: string;
22+
readonly settingsPath: string;
2223
readonly worktreesDir: string;
2324
readonly attachmentsDir: string;
2425
readonly logsDir: string;
@@ -60,6 +61,7 @@ export const deriveServerPaths = Effect.fn(function* (
6061
stateDir,
6162
dbPath,
6263
keybindingsConfigPath: join(stateDir, "keybindings.json"),
64+
settingsPath: join(stateDir, "settings.json"),
6365
worktreesDir: join(baseDir, "worktrees"),
6466
attachmentsDir,
6567
logsDir,

apps/server/src/geminiCliServerManager.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,8 +217,7 @@ export class GeminiCliServerManager extends EventEmitter<{
217217
throw new Error(`Gemini CLI session already exists for thread ${threadId}`);
218218
}
219219

220-
const geminiOpts = input.providerOptions?.geminiCli as GeminiCliProviderOptions | undefined;
221-
const binaryPath = geminiOpts?.binaryPath ?? defaultBinaryPath();
220+
const binaryPath = defaultBinaryPath();
222221
const cwd = input.cwd ?? process.cwd();
223222
const resumeSessionId = readGeminiResumeSessionId(input.resumeCursor);
224223
const now = new Date().toISOString();

apps/server/src/git/Layers/ClaudeTextGeneration.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ import { expect } from "vitest";
66
import { ServerConfig } from "../../config.ts";
77
import { TextGeneration } from "../Services/TextGeneration.ts";
88
import { ClaudeTextGenerationLive } from "./ClaudeTextGeneration.ts";
9+
import { ServerSettingsService } from "../../serverSettings.ts";
910

1011
const ClaudeTextGenerationTestLayer = ClaudeTextGenerationLive.pipe(
12+
Layer.provideMerge(ServerSettingsService.layerTest()),
1113
Layer.provideMerge(
1214
ServerConfig.layerTest(process.cwd(), {
1315
prefix: "t3code-claude-text-generation-test-",

apps/server/src/git/Layers/ClaudeTextGeneration.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { Effect, Layer, Option, Schema, Stream } from "effect";
1111
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";
1212

1313
import { ClaudeModelSelection } from "@t3tools/contracts";
14-
import { normalizeClaudeModelOptions } from "@t3tools/shared/model";
14+
import { resolveApiModelId } from "@t3tools/shared/model";
1515
import { sanitizeBranchFragment, sanitizeFeatureBranchName } from "@t3tools/shared/git";
1616

1717
import { TextGenerationError } from "../Errors.ts";
@@ -27,6 +27,8 @@ import {
2727
sanitizePrTitle,
2828
toJsonSchemaObject,
2929
} from "../Utils.ts";
30+
import { normalizeClaudeModelOptions } from "../../provider/Layers/ClaudeProvider.ts";
31+
import { ServerSettingsService } from "../../serverSettings.ts";
3032

3133
const CLAUDE_TIMEOUT_MS = 180_000;
3234

@@ -40,6 +42,7 @@ const ClaudeOutputEnvelope = Schema.Struct({
4042

4143
const makeClaudeTextGeneration = Effect.gen(function* () {
4244
const commandSpawner = yield* ChildProcessSpawner.ChildProcessSpawner;
45+
const serverSettingsService = yield* Effect.service(ServerSettingsService);
4346

4447
const readStreamAsString = <E>(
4548
operation: string,
@@ -86,17 +89,22 @@ const makeClaudeTextGeneration = Effect.gen(function* () {
8689
...(normalizedOptions?.fastMode ? { fastMode: true } : {}),
8790
};
8891

92+
const claudeSettings = yield* Effect.map(
93+
serverSettingsService.getSettings,
94+
(settings) => settings.providers.claudeAgent,
95+
).pipe(Effect.catch(() => Effect.undefined));
96+
8997
const runClaudeCommand = Effect.gen(function* () {
9098
const command = ChildProcess.make(
91-
"claude",
99+
claudeSettings?.binaryPath || "claude",
92100
[
93101
"-p",
94102
"--output-format",
95103
"json",
96104
"--json-schema",
97105
jsonSchemaStr,
98106
"--model",
99-
modelSelection.model,
107+
resolveApiModelId(modelSelection),
100108
...(normalizedOptions?.effort ? ["--effort", normalizedOptions.effort] : []),
101109
...(Object.keys(settings).length > 0 ? ["--settings", JSON.stringify(settings)] : []),
102110
"--dangerously-skip-permissions",

0 commit comments

Comments
 (0)