diff --git a/apps/server/src/atomicWrite.ts b/apps/server/src/atomicWrite.ts index 045504747c4..d785ef509f7 100644 --- a/apps/server/src/atomicWrite.ts +++ b/apps/server/src/atomicWrite.ts @@ -1,7 +1,6 @@ import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; import * as Path from "effect/Path"; -import * as Random from "effect/Random"; export const writeFileStringAtomically = (input: { readonly filePath: string; @@ -15,7 +14,6 @@ export const writeFileStringAtomically = (input: { Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const path = yield* Path.Path; - const tempFileId = yield* Random.nextUUIDv4; const targetDirectory = path.dirname(input.filePath); yield* fs.makeDirectory(targetDirectory, { recursive: true }); @@ -26,7 +24,7 @@ export const writeFileStringAtomically = (input: { directory: targetDirectory, prefix: `${path.basename(input.filePath)}.`, }); - const tempPath = path.join(tempDirectory, `${tempFileId}.tmp`); + const tempPath = path.join(tempDirectory, "contents.tmp"); yield* fs.writeFileString(tempPath, input.contents); if (input.mode !== undefined) { diff --git a/apps/server/src/auth/Services/AuthControlPlane.ts b/apps/server/src/auth/Services/AuthControlPlane.ts index b5e67639a65..884943fb0cb 100644 --- a/apps/server/src/auth/Services/AuthControlPlane.ts +++ b/apps/server/src/auth/Services/AuthControlPlane.ts @@ -69,5 +69,5 @@ export interface AuthControlPlaneShape { } export class AuthControlPlane extends Context.Service()( - "t3/AuthControlPlane", + "@ru-code/ru-code/auth/Services/AuthControlPlane", ) {} diff --git a/apps/server/src/auth/Services/BootstrapCredentialService.ts b/apps/server/src/auth/Services/BootstrapCredentialService.ts index 70dd1d5aead..3d81504494b 100644 --- a/apps/server/src/auth/Services/BootstrapCredentialService.ts +++ b/apps/server/src/auth/Services/BootstrapCredentialService.ts @@ -58,4 +58,4 @@ export interface BootstrapCredentialServiceShape { export class BootstrapCredentialService extends Context.Service< BootstrapCredentialService, BootstrapCredentialServiceShape ->()("t3/auth/Services/BootstrapCredentialService") {} +>()("@ru-code/ru-code/auth/Services/BootstrapCredentialService") {} diff --git a/apps/server/src/auth/Services/ServerAuth.ts b/apps/server/src/auth/Services/ServerAuth.ts index 370de20ebb0..359cf542b7b 100644 --- a/apps/server/src/auth/Services/ServerAuth.ts +++ b/apps/server/src/auth/Services/ServerAuth.ts @@ -92,5 +92,5 @@ export interface ServerAuthShape { } export class ServerAuth extends Context.Service()( - "t3/auth/Services/ServerAuth", + "@ru-code/ru-code/auth/Services/ServerAuth", ) {} diff --git a/apps/server/src/auth/Services/ServerAuthPolicy.ts b/apps/server/src/auth/Services/ServerAuthPolicy.ts index 5d9ef68cf95..b78ab20e525 100644 --- a/apps/server/src/auth/Services/ServerAuthPolicy.ts +++ b/apps/server/src/auth/Services/ServerAuthPolicy.ts @@ -7,5 +7,5 @@ export interface ServerAuthPolicyShape { } export class ServerAuthPolicy extends Context.Service()( - "t3/auth/Services/ServerAuthPolicy", + "@ru-code/ru-code/auth/Services/ServerAuthPolicy", ) {} diff --git a/apps/server/src/auth/Services/ServerSecretStore.ts b/apps/server/src/auth/Services/ServerSecretStore.ts index e4715e46464..71f15af2c5a 100644 --- a/apps/server/src/auth/Services/ServerSecretStore.ts +++ b/apps/server/src/auth/Services/ServerSecretStore.ts @@ -29,5 +29,5 @@ export interface ServerSecretStoreShape { } export class ServerSecretStore extends Context.Service()( - "t3/auth/Services/ServerSecretStore", + "@ru-code/ru-code/auth/Services/ServerSecretStore", ) {} diff --git a/apps/server/src/auth/Services/SessionCredentialService.ts b/apps/server/src/auth/Services/SessionCredentialService.ts index 7dc049c910e..c189a2c9862 100644 --- a/apps/server/src/auth/Services/SessionCredentialService.ts +++ b/apps/server/src/auth/Services/SessionCredentialService.ts @@ -88,4 +88,4 @@ export interface SessionCredentialServiceShape { export class SessionCredentialService extends Context.Service< SessionCredentialService, SessionCredentialServiceShape ->()("t3/auth/Services/SessionCredentialService") {} +>()("@ru-code/ru-code/auth/Services/SessionCredentialService") {} diff --git a/apps/server/src/checkpointing/Errors.ts b/apps/server/src/checkpointing/Errors.ts index 1d8e113a4eb..6feb58d584a 100644 --- a/apps/server/src/checkpointing/Errors.ts +++ b/apps/server/src/checkpointing/Errors.ts @@ -11,7 +11,7 @@ export class CheckpointUnavailableError extends Schema.TaggedErrorClass()("t3/checkpointing/Services/CheckpointDiffQuery") {} +>()("@ru-code/ru-code/checkpointing/Services/CheckpointDiffQuery") {} diff --git a/apps/server/src/checkpointing/Services/CheckpointStore.ts b/apps/server/src/checkpointing/Services/CheckpointStore.ts index a7c4c3dbef0..4df7ab49a5e 100644 --- a/apps/server/src/checkpointing/Services/CheckpointStore.ts +++ b/apps/server/src/checkpointing/Services/CheckpointStore.ts @@ -97,5 +97,5 @@ export interface CheckpointStoreShape { * CheckpointStore - Service tag for checkpoint persistence and restore operations. */ export class CheckpointStore extends Context.Service()( - "t3/checkpointing/Services/CheckpointStore", + "@ru-code/ru-code/checkpointing/Services/CheckpointStore", ) {} diff --git a/apps/server/src/config.ts b/apps/server/src/config.ts index 99a3fef4e0b..0e68c292c95 100644 --- a/apps/server/src/config.ts +++ b/apps/server/src/config.ts @@ -283,7 +283,7 @@ export const ensureServerDirectories = Effect.fn(function* (derivedPaths: Server * ServerConfig - Service tag for server runtime configuration. */ export class ServerConfig extends Context.Service()( - "t3/config/ServerConfig", + "@ru-code/ru-code/config/ServerConfig", ) { static readonly layerTest = (cwd: string, baseDirOrPrefix: string | { prefix: string }) => Layer.effect( diff --git a/apps/server/src/environment/Layers/ServerEnvironment.ts b/apps/server/src/environment/Layers/ServerEnvironment.ts index 88ae8bcae78..a11b4776bee 100644 --- a/apps/server/src/environment/Layers/ServerEnvironment.ts +++ b/apps/server/src/environment/Layers/ServerEnvironment.ts @@ -1,9 +1,9 @@ import { EnvironmentId, type ExecutionEnvironmentDescriptor } from "@t3tools/contracts"; +import * as Crypto from "effect/Crypto"; import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; import * as Layer from "effect/Layer"; import * as Path from "effect/Path"; -import * as Random from "effect/Random"; import * as ProcessRunner from "../../processRunner.ts"; import { ServerConfig } from "../../config.ts"; @@ -39,6 +39,7 @@ export const makeServerEnvironment = Effect.fn("makeServerEnvironment")(function const fileSystem = yield* FileSystem.FileSystem; const path = yield* Path.Path; const serverConfig = yield* ServerConfig; + const crypto = yield* Crypto.Crypto; const readPersistedEnvironmentId = Effect.gen(function* () { const exists = yield* fileSystem @@ -64,7 +65,7 @@ export const makeServerEnvironment = Effect.fn("makeServerEnvironment")(function return persisted; } - const generated = yield* Random.nextUUIDv4; + const generated = yield* crypto.randomUUIDv4; yield* persistEnvironmentId(generated); return generated; }); diff --git a/apps/server/src/environment/Services/ServerEnvironment.ts b/apps/server/src/environment/Services/ServerEnvironment.ts index 1e6dea0d05f..aa7756035f4 100644 --- a/apps/server/src/environment/Services/ServerEnvironment.ts +++ b/apps/server/src/environment/Services/ServerEnvironment.ts @@ -8,5 +8,5 @@ export interface ServerEnvironmentShape { } export class ServerEnvironment extends Context.Service()( - "t3/environment/Services/ServerEnvironment", + "@ru-code/ru-code/environment/Services/ServerEnvironment", ) {} diff --git a/apps/server/src/git/GitManager.ts b/apps/server/src/git/GitManager.ts index 18b50c081ac..f726f05f401 100644 --- a/apps/server/src/git/GitManager.ts +++ b/apps/server/src/git/GitManager.ts @@ -88,7 +88,7 @@ export interface GitManagerShape { } export class GitManager extends Context.Service()( - "t3/git/GitManager", + "@ru-code/ru-code/git/GitManager", ) {} const MAX_PROGRESS_TEXT_LENGTH = 500; diff --git a/apps/server/src/git/GitWorkflowService.ts b/apps/server/src/git/GitWorkflowService.ts index d5223d2f3c0..9287c2dc774 100644 --- a/apps/server/src/git/GitWorkflowService.ts +++ b/apps/server/src/git/GitWorkflowService.ts @@ -77,7 +77,7 @@ export interface GitWorkflowServiceShape { export class GitWorkflowService extends Context.Service< GitWorkflowService, GitWorkflowServiceShape ->()("t3/git/GitWorkflowService") {} +>()("@ru-code/ru-code/git/GitWorkflowService") {} const unsupportedGitWorkflow = (operation: string, cwd: string, detail: string) => new GitManagerError({ diff --git a/apps/server/src/keybindings.ts b/apps/server/src/keybindings.ts index a197984f23a..b69db65678b 100644 --- a/apps/server/src/keybindings.ts +++ b/apps/server/src/keybindings.ts @@ -287,7 +287,7 @@ export interface KeybindingsShape { * Keybindings - Service tag for keybinding configuration operations. */ export class Keybindings extends Context.Service()( - "t3/keybindings", + "@ru-code/ru-code/keybindings", ) {} const makeKeybindings = Effect.gen(function* () { diff --git a/apps/server/src/open.ts b/apps/server/src/open.ts index 4b2e6ef2716..418679e88b7 100644 --- a/apps/server/src/open.ts +++ b/apps/server/src/open.ts @@ -148,7 +148,7 @@ export interface OpenShape { /** * Open - Service tag for browser/editor launch operations. */ -export class Open extends Context.Service()("t3/open") {} +export class Open extends Context.Service()("@ru-code/ru-code/open") {} // ============================== // Implementations diff --git a/apps/server/src/orchestration/Errors.ts b/apps/server/src/orchestration/Errors.ts index 888fd4b3c0d..be7943f78a6 100644 --- a/apps/server/src/orchestration/Errors.ts +++ b/apps/server/src/orchestration/Errors.ts @@ -7,7 +7,7 @@ export class OrchestrationCommandJsonParseError extends Schema.TaggedErrorClass< "OrchestrationCommandJsonParseError", { detail: Schema.String, - cause: Schema.optional(Schema.Defect), + cause: Schema.optional(Schema.Defect()), }, ) { override get message(): string { @@ -19,7 +19,7 @@ export class OrchestrationCommandDecodeError extends Schema.TaggedErrorClass()( - "t3/orchestration/Services/CheckpointReactor", + "@ru-code/ru-code/orchestration/Services/CheckpointReactor", ) {} diff --git a/apps/server/src/orchestration/Services/OrchestrationEngine.ts b/apps/server/src/orchestration/Services/OrchestrationEngine.ts index acb2b7b042d..0d0820b3bba 100644 --- a/apps/server/src/orchestration/Services/OrchestrationEngine.ts +++ b/apps/server/src/orchestration/Services/OrchestrationEngine.ts @@ -67,4 +67,4 @@ export interface OrchestrationEngineShape { export class OrchestrationEngineService extends Context.Service< OrchestrationEngineService, OrchestrationEngineShape ->()("t3/orchestration/Services/OrchestrationEngine/OrchestrationEngineService") {} +>()("@ru-code/ru-code/orchestration/Services/OrchestrationEngine/OrchestrationEngineService") {} diff --git a/apps/server/src/orchestration/Services/OrchestrationReactor.ts b/apps/server/src/orchestration/Services/OrchestrationReactor.ts index eb2d95954bb..124bc8b9d04 100644 --- a/apps/server/src/orchestration/Services/OrchestrationReactor.ts +++ b/apps/server/src/orchestration/Services/OrchestrationReactor.ts @@ -29,4 +29,4 @@ export interface OrchestrationReactorShape { export class OrchestrationReactor extends Context.Service< OrchestrationReactor, OrchestrationReactorShape ->()("t3/orchestration/Services/OrchestrationReactor") {} +>()("@ru-code/ru-code/orchestration/Services/OrchestrationReactor") {} diff --git a/apps/server/src/orchestration/Services/ProjectionPipeline.ts b/apps/server/src/orchestration/Services/ProjectionPipeline.ts index bb4736ca57a..054564afc10 100644 --- a/apps/server/src/orchestration/Services/ProjectionPipeline.ts +++ b/apps/server/src/orchestration/Services/ProjectionPipeline.ts @@ -39,4 +39,4 @@ export interface OrchestrationProjectionPipelineShape { export class OrchestrationProjectionPipeline extends Context.Service< OrchestrationProjectionPipeline, OrchestrationProjectionPipelineShape ->()("t3/orchestration/Services/ProjectionPipeline/OrchestrationProjectionPipeline") {} +>()("@ru-code/ru-code/orchestration/Services/ProjectionPipeline/OrchestrationProjectionPipeline") {} diff --git a/apps/server/src/orchestration/Services/ProjectionSnapshotQuery.ts b/apps/server/src/orchestration/Services/ProjectionSnapshotQuery.ts index 53dfe33bf49..96dfadc44f9 100644 --- a/apps/server/src/orchestration/Services/ProjectionSnapshotQuery.ts +++ b/apps/server/src/orchestration/Services/ProjectionSnapshotQuery.ts @@ -146,4 +146,4 @@ export interface ProjectionSnapshotQueryShape { export class ProjectionSnapshotQuery extends Context.Service< ProjectionSnapshotQuery, ProjectionSnapshotQueryShape ->()("t3/orchestration/Services/ProjectionSnapshotQuery") {} +>()("@ru-code/ru-code/orchestration/Services/ProjectionSnapshotQuery") {} diff --git a/apps/server/src/orchestration/Services/ProviderCommandReactor.ts b/apps/server/src/orchestration/Services/ProviderCommandReactor.ts index 65aa9949fe1..14819cd8524 100644 --- a/apps/server/src/orchestration/Services/ProviderCommandReactor.ts +++ b/apps/server/src/orchestration/Services/ProviderCommandReactor.ts @@ -38,4 +38,4 @@ export interface ProviderCommandReactorShape { export class ProviderCommandReactor extends Context.Service< ProviderCommandReactor, ProviderCommandReactorShape ->()("t3/orchestration/Services/ProviderCommandReactor") {} +>()("@ru-code/ru-code/orchestration/Services/ProviderCommandReactor") {} diff --git a/apps/server/src/orchestration/Services/ProviderRuntimeIngestion.ts b/apps/server/src/orchestration/Services/ProviderRuntimeIngestion.ts index b6fa2711b94..68d9aa8eb2d 100644 --- a/apps/server/src/orchestration/Services/ProviderRuntimeIngestion.ts +++ b/apps/server/src/orchestration/Services/ProviderRuntimeIngestion.ts @@ -38,4 +38,4 @@ export interface ProviderRuntimeIngestionShape { export class ProviderRuntimeIngestionService extends Context.Service< ProviderRuntimeIngestionService, ProviderRuntimeIngestionShape ->()("t3/orchestration/Services/ProviderRuntimeIngestion/ProviderRuntimeIngestionService") {} +>()("@ru-code/ru-code/orchestration/Services/ProviderRuntimeIngestion/ProviderRuntimeIngestionService") {} diff --git a/apps/server/src/orchestration/Services/RuntimeReceiptBus.ts b/apps/server/src/orchestration/Services/RuntimeReceiptBus.ts index 0b880ee6999..c018d6697e6 100644 --- a/apps/server/src/orchestration/Services/RuntimeReceiptBus.ts +++ b/apps/server/src/orchestration/Services/RuntimeReceiptBus.ts @@ -62,5 +62,5 @@ export interface RuntimeReceiptBusShape { } export class RuntimeReceiptBus extends Context.Service()( - "t3/orchestration/Services/RuntimeReceiptBus", + "@ru-code/ru-code/orchestration/Services/RuntimeReceiptBus", ) {} diff --git a/apps/server/src/orchestration/Services/ThreadDeletionReactor.ts b/apps/server/src/orchestration/Services/ThreadDeletionReactor.ts index 7c6718965a6..232b9466ffb 100644 --- a/apps/server/src/orchestration/Services/ThreadDeletionReactor.ts +++ b/apps/server/src/orchestration/Services/ThreadDeletionReactor.ts @@ -35,4 +35,4 @@ export interface ThreadDeletionReactorShape { export class ThreadDeletionReactor extends Context.Service< ThreadDeletionReactor, ThreadDeletionReactorShape ->()("t3/orchestration/Services/ThreadDeletionReactor") {} +>()("@ru-code/ru-code/orchestration/Services/ThreadDeletionReactor") {} diff --git a/apps/server/src/persistence/Errors.ts b/apps/server/src/persistence/Errors.ts index 2b702eba5ea..2a3d7aff189 100644 --- a/apps/server/src/persistence/Errors.ts +++ b/apps/server/src/persistence/Errors.ts @@ -10,7 +10,7 @@ export class PersistenceSqlError extends Schema.TaggedErrorClass, ): Layer.Layer => Layer.effectContext( - Config.unwrap(config) - .asEffect() - .pipe( - Effect.flatMap(make), - Effect.map((client) => - Context.make(SqliteClient, client).pipe(Context.add(Client.SqlClient, client)), - ), + Config.unwrap(config).pipe( + Effect.flatMap(make), + Effect.map((client) => + Context.make(SqliteClient, client).pipe(Context.add(Client.SqlClient, client)), ), + ), ).pipe(Layer.provide(Reactivity.layer)); export const layer = (config: SqliteClientConfig): Layer.Layer => diff --git a/apps/server/src/persistence/Services/AuthPairingLinks.ts b/apps/server/src/persistence/Services/AuthPairingLinks.ts index c81ce51a3f4..57301d53857 100644 --- a/apps/server/src/persistence/Services/AuthPairingLinks.ts +++ b/apps/server/src/persistence/Services/AuthPairingLinks.ts @@ -75,4 +75,4 @@ export interface AuthPairingLinkRepositoryShape { export class AuthPairingLinkRepository extends Context.Service< AuthPairingLinkRepository, AuthPairingLinkRepositoryShape ->()("t3/persistence/Services/AuthPairingLinks/AuthPairingLinkRepository") {} +>()("@ru-code/ru-code/persistence/Services/AuthPairingLinks/AuthPairingLinkRepository") {} diff --git a/apps/server/src/persistence/Services/AuthSessions.ts b/apps/server/src/persistence/Services/AuthSessions.ts index a92573f9f9e..c5a72d6159c 100644 --- a/apps/server/src/persistence/Services/AuthSessions.ts +++ b/apps/server/src/persistence/Services/AuthSessions.ts @@ -92,4 +92,4 @@ export interface AuthSessionRepositoryShape { export class AuthSessionRepository extends Context.Service< AuthSessionRepository, AuthSessionRepositoryShape ->()("t3/persistence/Services/AuthSessions/AuthSessionRepository") {} +>()("@ru-code/ru-code/persistence/Services/AuthSessions/AuthSessionRepository") {} diff --git a/apps/server/src/persistence/Services/McpBinding.ts b/apps/server/src/persistence/Services/McpBinding.ts index e792f9f4208..06cff29ffea 100644 --- a/apps/server/src/persistence/Services/McpBinding.ts +++ b/apps/server/src/persistence/Services/McpBinding.ts @@ -33,4 +33,4 @@ export interface McpBindingRepositoryShape { export class McpBindingRepository extends Context.Service< McpBindingRepository, McpBindingRepositoryShape ->()("ru-fork/persistence/Services/McpBinding/McpBindingRepository") {} +>()("@ru-code/ru-code/persistence/Services/McpBinding/McpBindingRepository") {} diff --git a/apps/server/src/persistence/Services/McpCatalog.ts b/apps/server/src/persistence/Services/McpCatalog.ts index cab324fc62e..5f7706ff3e9 100644 --- a/apps/server/src/persistence/Services/McpCatalog.ts +++ b/apps/server/src/persistence/Services/McpCatalog.ts @@ -23,4 +23,4 @@ export interface McpCatalogRepositoryShape { export class McpCatalogRepository extends Context.Service< McpCatalogRepository, McpCatalogRepositoryShape ->()("ru-fork/persistence/Services/McpCatalog/McpCatalogRepository") {} +>()("@ru-code/ru-code/persistence/Services/McpCatalog/McpCatalogRepository") {} diff --git a/apps/server/src/persistence/Services/McpProbeCache.ts b/apps/server/src/persistence/Services/McpProbeCache.ts index 8533990c609..c08ba6233ed 100644 --- a/apps/server/src/persistence/Services/McpProbeCache.ts +++ b/apps/server/src/persistence/Services/McpProbeCache.ts @@ -30,4 +30,4 @@ export interface McpProbeCacheRepositoryShape { export class McpProbeCacheRepository extends Context.Service< McpProbeCacheRepository, McpProbeCacheRepositoryShape ->()("ru-fork/persistence/Services/McpProbeCache/McpProbeCacheRepository") {} +>()("@ru-code/ru-code/persistence/Services/McpProbeCache/McpProbeCacheRepository") {} diff --git a/apps/server/src/persistence/Services/OrchestrationCommandReceipts.ts b/apps/server/src/persistence/Services/OrchestrationCommandReceipts.ts index a2c16ce32b1..f7bbcd186b0 100644 --- a/apps/server/src/persistence/Services/OrchestrationCommandReceipts.ts +++ b/apps/server/src/persistence/Services/OrchestrationCommandReceipts.ts @@ -69,4 +69,4 @@ export interface OrchestrationCommandReceiptRepositoryShape { export class OrchestrationCommandReceiptRepository extends Context.Service< OrchestrationCommandReceiptRepository, OrchestrationCommandReceiptRepositoryShape ->()("t3/persistence/Services/OrchestrationCommandReceipts/OrchestrationCommandReceiptRepository") {} +>()("@ru-code/ru-code/persistence/Services/OrchestrationCommandReceipts/OrchestrationCommandReceiptRepository") {} diff --git a/apps/server/src/persistence/Services/OrchestrationEventStore.ts b/apps/server/src/persistence/Services/OrchestrationEventStore.ts index 8b465e7713e..80fb39b82ac 100644 --- a/apps/server/src/persistence/Services/OrchestrationEventStore.ts +++ b/apps/server/src/persistence/Services/OrchestrationEventStore.ts @@ -68,4 +68,4 @@ export interface OrchestrationEventStoreShape { export class OrchestrationEventStore extends Context.Service< OrchestrationEventStore, OrchestrationEventStoreShape ->()("t3/persistence/Services/OrchestrationEventStore") {} +>()("@ru-code/ru-code/persistence/Services/OrchestrationEventStore") {} diff --git a/apps/server/src/persistence/Services/ProjectionCheckpoints.ts b/apps/server/src/persistence/Services/ProjectionCheckpoints.ts index 7796ebec2a8..337bdc31952 100644 --- a/apps/server/src/persistence/Services/ProjectionCheckpoints.ts +++ b/apps/server/src/persistence/Services/ProjectionCheckpoints.ts @@ -92,4 +92,4 @@ export interface ProjectionCheckpointRepositoryShape { export class ProjectionCheckpointRepository extends Context.Service< ProjectionCheckpointRepository, ProjectionCheckpointRepositoryShape ->()("t3/persistence/Services/ProjectionCheckpoints/ProjectionCheckpointRepository") {} +>()("@ru-code/ru-code/persistence/Services/ProjectionCheckpoints/ProjectionCheckpointRepository") {} diff --git a/apps/server/src/persistence/Services/ProjectionPendingApprovals.ts b/apps/server/src/persistence/Services/ProjectionPendingApprovals.ts index 967e6da9d3a..cc9ab9bc013 100644 --- a/apps/server/src/persistence/Services/ProjectionPendingApprovals.ts +++ b/apps/server/src/persistence/Services/ProjectionPendingApprovals.ts @@ -90,4 +90,4 @@ export interface ProjectionPendingApprovalRepositoryShape { export class ProjectionPendingApprovalRepository extends Context.Service< ProjectionPendingApprovalRepository, ProjectionPendingApprovalRepositoryShape ->()("t3/persistence/Services/ProjectionPendingApprovals/ProjectionPendingApprovalRepository") {} +>()("@ru-code/ru-code/persistence/Services/ProjectionPendingApprovals/ProjectionPendingApprovalRepository") {} diff --git a/apps/server/src/persistence/Services/ProjectionProjects.ts b/apps/server/src/persistence/Services/ProjectionProjects.ts index 5632205a269..205ff5e2acf 100644 --- a/apps/server/src/persistence/Services/ProjectionProjects.ts +++ b/apps/server/src/persistence/Services/ProjectionProjects.ts @@ -78,4 +78,4 @@ export interface ProjectionProjectRepositoryShape { export class ProjectionProjectRepository extends Context.Service< ProjectionProjectRepository, ProjectionProjectRepositoryShape ->()("t3/persistence/Services/ProjectionProjects/ProjectionProjectRepository") {} +>()("@ru-code/ru-code/persistence/Services/ProjectionProjects/ProjectionProjectRepository") {} diff --git a/apps/server/src/persistence/Services/ProjectionState.ts b/apps/server/src/persistence/Services/ProjectionState.ts index ba1ca151736..e58fb473e17 100644 --- a/apps/server/src/persistence/Services/ProjectionState.ts +++ b/apps/server/src/persistence/Services/ProjectionState.ts @@ -63,4 +63,4 @@ export interface ProjectionStateRepositoryShape { export class ProjectionStateRepository extends Context.Service< ProjectionStateRepository, ProjectionStateRepositoryShape ->()("t3/persistence/Services/ProjectionState/ProjectionStateRepository") {} +>()("@ru-code/ru-code/persistence/Services/ProjectionState/ProjectionStateRepository") {} diff --git a/apps/server/src/persistence/Services/ProjectionThreadActivities.ts b/apps/server/src/persistence/Services/ProjectionThreadActivities.ts index 47cb6073c47..cfb367ab9c2 100644 --- a/apps/server/src/persistence/Services/ProjectionThreadActivities.ts +++ b/apps/server/src/persistence/Services/ProjectionThreadActivities.ts @@ -81,4 +81,4 @@ export interface ProjectionThreadActivityRepositoryShape { export class ProjectionThreadActivityRepository extends Context.Service< ProjectionThreadActivityRepository, ProjectionThreadActivityRepositoryShape ->()("t3/persistence/Services/ProjectionThreadActivities/ProjectionThreadActivityRepository") {} +>()("@ru-code/ru-code/persistence/Services/ProjectionThreadActivities/ProjectionThreadActivityRepository") {} diff --git a/apps/server/src/persistence/Services/ProjectionThreadMessages.ts b/apps/server/src/persistence/Services/ProjectionThreadMessages.ts index d50ff320256..033ae3d037b 100644 --- a/apps/server/src/persistence/Services/ProjectionThreadMessages.ts +++ b/apps/server/src/persistence/Services/ProjectionThreadMessages.ts @@ -92,4 +92,4 @@ export interface ProjectionThreadMessageRepositoryShape { export class ProjectionThreadMessageRepository extends Context.Service< ProjectionThreadMessageRepository, ProjectionThreadMessageRepositoryShape ->()("t3/persistence/Services/ProjectionThreadMessages/ProjectionThreadMessageRepository") {} +>()("@ru-code/ru-code/persistence/Services/ProjectionThreadMessages/ProjectionThreadMessageRepository") {} diff --git a/apps/server/src/persistence/Services/ProjectionThreadProposedPlans.ts b/apps/server/src/persistence/Services/ProjectionThreadProposedPlans.ts index b4bc2bcc328..1befa7b96c5 100644 --- a/apps/server/src/persistence/Services/ProjectionThreadProposedPlans.ts +++ b/apps/server/src/persistence/Services/ProjectionThreadProposedPlans.ts @@ -51,5 +51,5 @@ export class ProjectionThreadProposedPlanRepository extends Context.Service< ProjectionThreadProposedPlanRepository, ProjectionThreadProposedPlanRepositoryShape >()( - "t3/persistence/Services/ProjectionThreadProposedPlans/ProjectionThreadProposedPlanRepository", + "@ru-code/ru-code/persistence/Services/ProjectionThreadProposedPlans/ProjectionThreadProposedPlanRepository", ) {} diff --git a/apps/server/src/persistence/Services/ProjectionThreadSessions.ts b/apps/server/src/persistence/Services/ProjectionThreadSessions.ts index 7cecac33eb6..2fa59ca3cc0 100644 --- a/apps/server/src/persistence/Services/ProjectionThreadSessions.ts +++ b/apps/server/src/persistence/Services/ProjectionThreadSessions.ts @@ -75,4 +75,4 @@ export interface ProjectionThreadSessionRepositoryShape { export class ProjectionThreadSessionRepository extends Context.Service< ProjectionThreadSessionRepository, ProjectionThreadSessionRepositoryShape ->()("t3/persistence/Services/ProjectionThreadSessions/ProjectionThreadSessionRepository") {} +>()("@ru-code/ru-code/persistence/Services/ProjectionThreadSessions/ProjectionThreadSessionRepository") {} diff --git a/apps/server/src/persistence/Services/ProjectionThreads.ts b/apps/server/src/persistence/Services/ProjectionThreads.ts index ecf20aa4a5d..87af0d39ce1 100644 --- a/apps/server/src/persistence/Services/ProjectionThreads.ts +++ b/apps/server/src/persistence/Services/ProjectionThreads.ts @@ -103,4 +103,4 @@ export interface ProjectionThreadRepositoryShape { export class ProjectionThreadRepository extends Context.Service< ProjectionThreadRepository, ProjectionThreadRepositoryShape ->()("t3/persistence/Services/ProjectionThreads/ProjectionThreadRepository") {} +>()("@ru-code/ru-code/persistence/Services/ProjectionThreads/ProjectionThreadRepository") {} diff --git a/apps/server/src/persistence/Services/ProjectionTurns.ts b/apps/server/src/persistence/Services/ProjectionTurns.ts index f3d5d5e4706..238eff1836e 100644 --- a/apps/server/src/persistence/Services/ProjectionTurns.ts +++ b/apps/server/src/persistence/Services/ProjectionTurns.ts @@ -167,4 +167,4 @@ export interface ProjectionTurnRepositoryShape { export class ProjectionTurnRepository extends Context.Service< ProjectionTurnRepository, ProjectionTurnRepositoryShape ->()("t3/persistence/Services/ProjectionTurns/ProjectionTurnRepository") {} +>()("@ru-code/ru-code/persistence/Services/ProjectionTurns/ProjectionTurnRepository") {} diff --git a/apps/server/src/persistence/Services/ProviderSessionRuntime.ts b/apps/server/src/persistence/Services/ProviderSessionRuntime.ts index 125f4fa5bbf..70d44acb0f9 100644 --- a/apps/server/src/persistence/Services/ProviderSessionRuntime.ts +++ b/apps/server/src/persistence/Services/ProviderSessionRuntime.ts @@ -89,4 +89,4 @@ export interface ProviderSessionRuntimeRepositoryShape { export class ProviderSessionRuntimeRepository extends Context.Service< ProviderSessionRuntimeRepository, ProviderSessionRuntimeRepositoryShape ->()("t3/persistence/Services/ProviderSessionRuntime/ProviderSessionRuntimeRepository") {} +>()("@ru-code/ru-code/persistence/Services/ProviderSessionRuntime/ProviderSessionRuntimeRepository") {} diff --git a/apps/server/src/processRunner.ts b/apps/server/src/processRunner.ts index 575ace2dd6e..2b6356b07f5 100644 --- a/apps/server/src/processRunner.ts +++ b/apps/server/src/processRunner.ts @@ -94,7 +94,7 @@ export interface ProcessRunnerShape { } export class ProcessRunner extends Context.Service()( - "t3/processRunner", + "@ru-code/ru-code/processRunner", ) {} const DEFAULT_MAX_OUTPUT_BYTES = 8 * 1024 * 1024; diff --git a/apps/server/src/project/Services/ProjectFaviconResolver.ts b/apps/server/src/project/Services/ProjectFaviconResolver.ts index ad1b466e2c7..43b7b4166d3 100644 --- a/apps/server/src/project/Services/ProjectFaviconResolver.ts +++ b/apps/server/src/project/Services/ProjectFaviconResolver.ts @@ -27,4 +27,4 @@ export interface ProjectFaviconResolverShape { export class ProjectFaviconResolver extends Context.Service< ProjectFaviconResolver, ProjectFaviconResolverShape ->()("t3/project/Services/ProjectFaviconResolver") {} +>()("@ru-code/ru-code/project/Services/ProjectFaviconResolver") {} diff --git a/apps/server/src/project/Services/ProjectSetupScriptRunner.ts b/apps/server/src/project/Services/ProjectSetupScriptRunner.ts index 184c75b5019..800e600fc0b 100644 --- a/apps/server/src/project/Services/ProjectSetupScriptRunner.ts +++ b/apps/server/src/project/Services/ProjectSetupScriptRunner.ts @@ -41,4 +41,4 @@ export interface ProjectSetupScriptRunnerShape { export class ProjectSetupScriptRunner extends Context.Service< ProjectSetupScriptRunner, ProjectSetupScriptRunnerShape ->()("t3/project/ProjectSetupScriptRunner") {} +>()("@ru-code/ru-code/project/Services/ProjectSetupScriptRunner") {} diff --git a/apps/server/src/project/Services/RepositoryIdentityResolver.ts b/apps/server/src/project/Services/RepositoryIdentityResolver.ts index ef0b128c6f7..29acec099c9 100644 --- a/apps/server/src/project/Services/RepositoryIdentityResolver.ts +++ b/apps/server/src/project/Services/RepositoryIdentityResolver.ts @@ -9,4 +9,4 @@ export interface RepositoryIdentityResolverShape { export class RepositoryIdentityResolver extends Context.Service< RepositoryIdentityResolver, RepositoryIdentityResolverShape ->()("t3/project/Services/RepositoryIdentityResolver") {} +>()("@ru-code/ru-code/project/Services/RepositoryIdentityResolver") {} diff --git a/apps/server/src/provider/Drivers/CliDriver.ts b/apps/server/src/provider/Drivers/CliDriver.ts index 2f101852379..bc38cdd1e05 100644 --- a/apps/server/src/provider/Drivers/CliDriver.ts +++ b/apps/server/src/provider/Drivers/CliDriver.ts @@ -9,6 +9,7 @@ */ import { CLI_NAME } from "@ru-fork/branding"; import { CliSettings, ProviderDriverKind, type ServerProvider } from "@t3tools/contracts"; +import * as Crypto from "effect/Crypto"; import * as Duration from "effect/Duration"; import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; @@ -55,6 +56,7 @@ const UPDATE = makeStaticProviderMaintenanceResolver( export type CliDriverEnv = | ChildProcessSpawner.ChildProcessSpawner + | Crypto.Crypto | FileSystem.FileSystem | Path.Path | ProviderEventLoggers diff --git a/apps/server/src/provider/Errors.ts b/apps/server/src/provider/Errors.ts index bccc7d5679c..0cf1522399b 100644 --- a/apps/server/src/provider/Errors.ts +++ b/apps/server/src/provider/Errors.ts @@ -11,7 +11,7 @@ export class ProviderAdapterValidationError extends Schema.TaggedErrorClass(); @@ -429,7 +434,7 @@ export function makeCliAdapter(cliSettings: CliSettings, options?: CliAdapterLiv const layerScope = yield* Effect.scope; const nowIso = Effect.map(DateTime.now, DateTime.formatIso); - const nextEventId = Effect.map(Random.nextUUIDv4, (id) => EventId.make(id)); + const nextEventId = Effect.map(cryptoUuid, (id) => EventId.make(id)); const makeEventStamp = () => Effect.all({ eventId: nextEventId, createdAt: nowIso }); const offerRuntimeEvent = (event: ProviderRuntimeEvent) => @@ -462,11 +467,12 @@ export function makeCliAdapter(cliSettings: CliSettings, options?: CliAdapterLiv Effect.gen(function* () { if (!nativeEventLogger) return; const observedAt = yield* nowIso; + const eventId = yield* cryptoUuid; yield* nativeEventLogger.write( { observedAt, event: { - id: crypto.randomUUID(), + id: eventId, kind: "notification", provider: PROVIDER, createdAt: observedAt, @@ -854,7 +860,7 @@ export function makeCliAdapter(cliSettings: CliSettings, options?: CliAdapterLiv return { outcome: { outcome: "cancelled" as const } }; } const { questions, questionIndexById } = normalizeCliQuestions(questionsPayload); - const requestId = ApprovalRequestId.make(crypto.randomUUID()); + const requestId = ApprovalRequestId.make(yield* cryptoUuid); const runtimeRequestId = RuntimeRequestId.make(requestId); const answersDeferred = yield* Deferred.make(); pendingUserInputs.set(requestId, { @@ -942,7 +948,7 @@ export function makeCliAdapter(cliSettings: CliSettings, options?: CliAdapterLiv payload: params, }, }); - const requestId = ApprovalRequestId.make(crypto.randomUUID()); + const requestId = ApprovalRequestId.make(yield* cryptoUuid); const runtimeRequestId = RuntimeRequestId.make(requestId); const decision = yield* Deferred.make(); pendingApprovals.set(requestId, { decision, kind: "exit_plan_mode" }); @@ -1020,7 +1026,7 @@ export function makeCliAdapter(cliSettings: CliSettings, options?: CliAdapterLiv } } const permissionRequest = parsePermissionRequest(params); - const requestId = ApprovalRequestId.make(crypto.randomUUID()); + const requestId = ApprovalRequestId.make(yield* cryptoUuid); const runtimeRequestId = RuntimeRequestId.make(requestId); const decision = yield* Deferred.make(); pendingApprovals.set(requestId, { @@ -1420,7 +1426,7 @@ export function makeCliAdapter(cliSettings: CliSettings, options?: CliAdapterLiv // finalize) and the real turnId (never a projection read). No `ctx` is // needed to emit — `finalized` is a local first-wins flag and the // threadId/turnId are local. - const turnId = TurnId.make(crypto.randomUUID()); + const turnId = TurnId.make(yield* cryptoUuid); let finalized = false; let activeCtx: CliSessionContext | undefined; const turnFinalized = yield* Deferred.make(); diff --git a/apps/server/src/provider/Layers/EventNdjsonLogger.ts b/apps/server/src/provider/Layers/EventNdjsonLogger.ts index 3b36cc20c90..71f3cc2f839 100644 --- a/apps/server/src/provider/Layers/EventNdjsonLogger.ts +++ b/apps/server/src/provider/Layers/EventNdjsonLogger.ts @@ -14,6 +14,7 @@ import { RotatingFileSink } from "@t3tools/shared/logging"; import * as Effect from "effect/Effect"; import * as Exit from "effect/Exit"; import * as Logger from "effect/Logger"; +import * as Schema from "effect/Schema"; import * as Scope from "effect/Scope"; import * as SynchronizedRef from "effect/SynchronizedRef"; @@ -84,29 +85,18 @@ function resolveStreamLabel(stream: EventNdjsonStream): string { } } +const encodeUnknownJsonString = Schema.encodeUnknownEffect(Schema.UnknownFromJsonString); + const toLogMessage = Effect.fn("toLogMessage")(function* ( event: unknown, ): Effect.fn.Return { - const serialized = yield* Effect.sync(() => { - try { - return { ok: true as const, value: JSON.stringify(event) }; - } catch (error) { - return { ok: false as const, error }; - } - }); - - if (!serialized.ok) { - yield* logWarning("failed to serialize provider event log record", { - error: serialized.error, - }); - return undefined; - } - - if (typeof serialized.value !== "string") { - return undefined; - } - - return serialized.value; + return yield* encodeUnknownJsonString(event).pipe( + Effect.catch((error) => + logWarning("failed to serialize provider event log record", { error }).pipe( + Effect.as(undefined), + ), + ), + ); }); const makeThreadWriter = Effect.fn("makeThreadWriter")(function* (input: { diff --git a/apps/server/src/provider/Layers/ProviderEventLoggers.ts b/apps/server/src/provider/Layers/ProviderEventLoggers.ts index 14e9a1075d5..96462bf88f0 100644 --- a/apps/server/src/provider/Layers/ProviderEventLoggers.ts +++ b/apps/server/src/provider/Layers/ProviderEventLoggers.ts @@ -50,7 +50,7 @@ export interface ProviderEventLoggersShape { export class ProviderEventLoggers extends Context.Service< ProviderEventLoggers, ProviderEventLoggersShape ->()("t3/provider/ProviderEventLoggers") {} +>()("@ru-code/ru-code/provider/Layers/ProviderEventLoggers") {} /** * Constant value used by tests / boot layers that want to opt out of native diff --git a/apps/server/src/provider/Services/ProviderAdapterRegistry.ts b/apps/server/src/provider/Services/ProviderAdapterRegistry.ts index 5b755c42eed..6fe11f3c1e7 100644 --- a/apps/server/src/provider/Services/ProviderAdapterRegistry.ts +++ b/apps/server/src/provider/Services/ProviderAdapterRegistry.ts @@ -97,4 +97,4 @@ export interface ProviderAdapterRegistryShape { export class ProviderAdapterRegistry extends Context.Service< ProviderAdapterRegistry, ProviderAdapterRegistryShape ->()("t3/provider/Services/ProviderAdapterRegistry") {} +>()("@ru-code/ru-code/provider/Services/ProviderAdapterRegistry") {} diff --git a/apps/server/src/provider/Services/ProviderInstanceRegistry.ts b/apps/server/src/provider/Services/ProviderInstanceRegistry.ts index cfea1142666..0819cfdd611 100644 --- a/apps/server/src/provider/Services/ProviderInstanceRegistry.ts +++ b/apps/server/src/provider/Services/ProviderInstanceRegistry.ts @@ -84,4 +84,4 @@ export interface ProviderInstanceRegistryShape { export class ProviderInstanceRegistry extends Context.Service< ProviderInstanceRegistry, ProviderInstanceRegistryShape ->()("t3/provider/Services/ProviderInstanceRegistry") {} +>()("@ru-code/ru-code/provider/Services/ProviderInstanceRegistry") {} diff --git a/apps/server/src/provider/Services/ProviderInstanceRegistryMutator.ts b/apps/server/src/provider/Services/ProviderInstanceRegistryMutator.ts index 98d45080237..4a600dc65e1 100644 --- a/apps/server/src/provider/Services/ProviderInstanceRegistryMutator.ts +++ b/apps/server/src/provider/Services/ProviderInstanceRegistryMutator.ts @@ -49,4 +49,4 @@ export interface ProviderInstanceRegistryMutatorShape { export class ProviderInstanceRegistryMutator extends Context.Service< ProviderInstanceRegistryMutator, ProviderInstanceRegistryMutatorShape ->()("t3/provider/Services/ProviderInstanceRegistryMutator") {} +>()("@ru-code/ru-code/provider/Services/ProviderInstanceRegistryMutator") {} diff --git a/apps/server/src/provider/Services/ProviderRegistry.ts b/apps/server/src/provider/Services/ProviderRegistry.ts index b7426b30338..eb3107f5b75 100644 --- a/apps/server/src/provider/Services/ProviderRegistry.ts +++ b/apps/server/src/provider/Services/ProviderRegistry.ts @@ -77,5 +77,5 @@ export interface ProviderRegistryShape { } export class ProviderRegistry extends Context.Service()( - "t3/provider/Services/ProviderRegistry", + "@ru-code/ru-code/provider/Services/ProviderRegistry", ) {} diff --git a/apps/server/src/provider/Services/ProviderService.ts b/apps/server/src/provider/Services/ProviderService.ts index 2677ac46cc0..d6b9b09b5c0 100644 --- a/apps/server/src/provider/Services/ProviderService.ts +++ b/apps/server/src/provider/Services/ProviderService.ts @@ -136,5 +136,5 @@ export interface ProviderServiceShape { * ProviderService - Service tag for provider orchestration. */ export class ProviderService extends Context.Service()( - "t3/provider/Services/ProviderService", + "@ru-code/ru-code/provider/Services/ProviderService", ) {} diff --git a/apps/server/src/provider/Services/ProviderSessionDirectory.ts b/apps/server/src/provider/Services/ProviderSessionDirectory.ts index f2dd4323f7a..23c864bdc65 100644 --- a/apps/server/src/provider/Services/ProviderSessionDirectory.ts +++ b/apps/server/src/provider/Services/ProviderSessionDirectory.ts @@ -67,4 +67,4 @@ export interface ProviderSessionDirectoryShape { export class ProviderSessionDirectory extends Context.Service< ProviderSessionDirectory, ProviderSessionDirectoryShape ->()("t3/provider/Services/ProviderSessionDirectory") {} +>()("@ru-code/ru-code/provider/Services/ProviderSessionDirectory") {} diff --git a/apps/server/src/provider/Services/ProviderSessionReaper.ts b/apps/server/src/provider/Services/ProviderSessionReaper.ts index 7c4627eca89..e4d0c24f889 100644 --- a/apps/server/src/provider/Services/ProviderSessionReaper.ts +++ b/apps/server/src/provider/Services/ProviderSessionReaper.ts @@ -12,4 +12,4 @@ export interface ProviderSessionReaperShape { export class ProviderSessionReaper extends Context.Service< ProviderSessionReaper, ProviderSessionReaperShape ->()("t3/provider/Services/ProviderSessionReaper") {} +>()("@ru-code/ru-code/provider/Services/ProviderSessionReaper") {} diff --git a/apps/server/src/provider/acp/AcpNativeLogging.ts b/apps/server/src/provider/acp/AcpNativeLogging.ts deleted file mode 100644 index f00ffcb6655..00000000000 --- a/apps/server/src/provider/acp/AcpNativeLogging.ts +++ /dev/null @@ -1,79 +0,0 @@ -import type { ProviderDriverKind, ThreadId } from "@t3tools/contracts"; -import * as Cause from "effect/Cause"; -import * as DateTime from "effect/DateTime"; -import * as Effect from "effect/Effect"; -import * as Random from "effect/Random"; -import type * as EffectAcpProtocol from "effect-acp/protocol"; - -import type { EventNdjsonLogger } from "../Layers/EventNdjsonLogger.ts"; -import type { AcpSessionRequestLogEvent, AcpSessionRuntimeOptions } from "./AcpSessionRuntime.ts"; - -function writeNativeAcpLog(input: { - readonly nativeEventLogger: EventNdjsonLogger | undefined; - readonly provider: ProviderDriverKind; - readonly threadId: ThreadId; - readonly kind: "request" | "protocol"; - readonly payload: unknown; -}): Effect.Effect { - return Effect.gen(function* () { - if (!input.nativeEventLogger) return; - const observedAt = DateTime.formatIso(yield* DateTime.now); - yield* input.nativeEventLogger.write( - { - observedAt, - event: { - id: yield* Random.nextUUIDv4, - kind: input.kind, - provider: input.provider, - createdAt: observedAt, - threadId: input.threadId, - payload: input.payload, - }, - }, - input.threadId, - ); - }); -} - -function formatRequestLogPayload(event: AcpSessionRequestLogEvent) { - return { - method: event.method, - status: event.status, - request: event.payload, - ...(event.result !== undefined ? { result: event.result } : {}), - ...(event.cause !== undefined ? { cause: Cause.pretty(event.cause) } : {}), - }; -} - -export function makeAcpNativeLoggers(input: { - readonly nativeEventLogger: EventNdjsonLogger | undefined; - readonly provider: ProviderDriverKind; - readonly threadId: ThreadId; -}): Pick { - return { - requestLogger: (event) => - writeNativeAcpLog({ - nativeEventLogger: input.nativeEventLogger, - provider: input.provider, - threadId: input.threadId, - kind: "request", - payload: formatRequestLogPayload(event), - }), - ...(input.nativeEventLogger - ? { - protocolLogging: { - logIncoming: true, - logOutgoing: true, - logger: (event: EffectAcpProtocol.AcpProtocolLogEvent) => - writeNativeAcpLog({ - nativeEventLogger: input.nativeEventLogger, - provider: input.provider, - threadId: input.threadId, - kind: "protocol", - payload: event, - }), - } satisfies NonNullable, - } - : {}), - }; -} diff --git a/apps/server/src/provider/acp/AcpSessionRuntime.ts b/apps/server/src/provider/acp/AcpSessionRuntime.ts index 6dd66a5e828..c389e2a0c1f 100644 --- a/apps/server/src/provider/acp/AcpSessionRuntime.ts +++ b/apps/server/src/provider/acp/AcpSessionRuntime.ts @@ -153,7 +153,7 @@ interface EnsureActiveAssistantSegmentResult { } export class AcpSessionRuntime extends Context.Service()( - "t3/provider/acp/AcpSessionRuntime", + "@ru-code/ru-code/provider/acp/AcpSessionRuntime", ) { static layer( options: AcpSessionRuntimeOptions, diff --git a/apps/server/src/provider/providerMaintenanceRunner.ts b/apps/server/src/provider/providerMaintenanceRunner.ts index d08155d69d4..b6d4691c857 100644 --- a/apps/server/src/provider/providerMaintenanceRunner.ts +++ b/apps/server/src/provider/providerMaintenanceRunner.ts @@ -58,7 +58,7 @@ export interface ProviderMaintenanceRunnerShape { export class ProviderMaintenanceRunner extends Context.Service< ProviderMaintenanceRunner, ProviderMaintenanceRunnerShape ->()("t3/provider/ProviderMaintenanceRunner") {} +>()("@ru-code/ru-code/provider/providerMaintenanceRunner") {} class ProviderMaintenanceCommandError extends Data.TaggedError("ProviderMaintenanceCommandError")<{ readonly message: string; diff --git a/apps/server/src/ru-fork/mcp/McpOverlay.ts b/apps/server/src/ru-fork/mcp/McpOverlay.ts index 5e24eaf60c8..569eb3b6089 100644 --- a/apps/server/src/ru-fork/mcp/McpOverlay.ts +++ b/apps/server/src/ru-fork/mcp/McpOverlay.ts @@ -82,7 +82,7 @@ export interface McpOverlayShape { } export class McpOverlay extends Context.Service()( - "ru-fork/mcp/McpOverlay", + "@ru-code/ru-code/ru-fork/mcp/McpOverlay", ) {} /** qwen mcpServers entry: resolved transport fields + policy-derived tool filter. diff --git a/apps/server/src/ru-fork/mcp/McpProjectionQuery.ts b/apps/server/src/ru-fork/mcp/McpProjectionQuery.ts index aee1635457d..e866946117d 100644 --- a/apps/server/src/ru-fork/mcp/McpProjectionQuery.ts +++ b/apps/server/src/ru-fork/mcp/McpProjectionQuery.ts @@ -29,7 +29,7 @@ export interface McpProjectionQueryShape { export class McpProjectionQuery extends Context.Service< McpProjectionQuery, McpProjectionQueryShape ->()("ru-fork/mcp/McpProjectionQuery") {} +>()("@ru-code/ru-code/ru-fork/mcp/McpProjectionQuery") {} function isProjectionRelevant(event: OrchestrationEvent): boolean { return event.type.startsWith("mcp.") || event.type === "project.deleted"; diff --git a/apps/server/src/ru-fork/mcp/McpReactor.ts b/apps/server/src/ru-fork/mcp/McpReactor.ts index d7af1a2d845..edfb79a5e10 100644 --- a/apps/server/src/ru-fork/mcp/McpReactor.ts +++ b/apps/server/src/ru-fork/mcp/McpReactor.ts @@ -70,7 +70,7 @@ export interface McpReactorShape { } export class McpReactor extends Context.Service()( - "ru-fork/mcp/McpReactor", + "@ru-code/ru-code/ru-fork/mcp/McpReactor", ) {} /** diff --git a/apps/server/src/ru-fork/mcp/McpRuntime.ts b/apps/server/src/ru-fork/mcp/McpRuntime.ts index b095d24aca4..55447100905 100644 --- a/apps/server/src/ru-fork/mcp/McpRuntime.ts +++ b/apps/server/src/ru-fork/mcp/McpRuntime.ts @@ -29,7 +29,7 @@ export interface McpRuntimeShape { } export class McpRuntime extends Context.Service()( - "ru-fork/mcp/McpRuntime", + "@ru-code/ru-code/ru-fork/mcp/McpRuntime", ) {} const instanceRefKey = (projectId: string, serverId: string): string => `${projectId}:${serverId}`; diff --git a/apps/server/src/ru-fork/mcp/McpSupervisor.ts b/apps/server/src/ru-fork/mcp/McpSupervisor.ts index c1077a9e5d3..98e4ac4d7ba 100644 --- a/apps/server/src/ru-fork/mcp/McpSupervisor.ts +++ b/apps/server/src/ru-fork/mcp/McpSupervisor.ts @@ -212,7 +212,7 @@ export interface McpSupervisorShape { } export class McpSupervisor extends Context.Service()( - "ru-fork/mcp/McpSupervisor", + "@ru-code/ru-code/ru-fork/mcp/McpSupervisor", ) {} // Exported for unit testing (alongside isProbeDue/isSweepDue). diff --git a/apps/server/src/ru-fork/qwen-transcript/QwenTranscriptService.ts b/apps/server/src/ru-fork/qwen-transcript/QwenTranscriptService.ts index 3d79883c2ee..9b8956c7d35 100644 --- a/apps/server/src/ru-fork/qwen-transcript/QwenTranscriptService.ts +++ b/apps/server/src/ru-fork/qwen-transcript/QwenTranscriptService.ts @@ -20,4 +20,4 @@ export interface QwenTranscriptServiceShape { export class QwenTranscriptService extends Context.Service< QwenTranscriptService, QwenTranscriptServiceShape ->()("t3/ru-fork/QwenTranscriptService") {} +>()("@ru-code/ru-code/ru-fork/qwen-transcript/QwenTranscriptService") {} diff --git a/apps/server/src/ru-fork/skills/SkillScannerService.ts b/apps/server/src/ru-fork/skills/SkillScannerService.ts index 9b6399ba83b..26fbfe4ffc5 100644 --- a/apps/server/src/ru-fork/skills/SkillScannerService.ts +++ b/apps/server/src/ru-fork/skills/SkillScannerService.ts @@ -38,5 +38,5 @@ export interface SkillScannerShape { } export class SkillScanner extends Context.Service()( - "ru-fork/SkillScanner", + "@ru-code/ru-code/ru-fork/skills/SkillScannerService/SkillScanner", ) {} diff --git a/apps/server/src/ru-fork/subagents/SubagentScannerService.ts b/apps/server/src/ru-fork/subagents/SubagentScannerService.ts index 03404885f6e..c38b071b69d 100644 --- a/apps/server/src/ru-fork/subagents/SubagentScannerService.ts +++ b/apps/server/src/ru-fork/subagents/SubagentScannerService.ts @@ -39,5 +39,5 @@ export interface SubagentScannerShape { } export class SubagentScanner extends Context.Service()( - "ru-fork/SubagentScanner", + "@ru-code/ru-code/ru-fork/subagents/SubagentScannerService/SubagentScanner", ) {} diff --git a/apps/server/src/serverLifecycleEvents.ts b/apps/server/src/serverLifecycleEvents.ts index 88661b1593a..94bdec20be3 100644 --- a/apps/server/src/serverLifecycleEvents.ts +++ b/apps/server/src/serverLifecycleEvents.ts @@ -24,7 +24,7 @@ export interface ServerLifecycleEventsShape { export class ServerLifecycleEvents extends Context.Service< ServerLifecycleEvents, ServerLifecycleEventsShape ->()("t3/serverLifecycleEvents") {} +>()("@ru-code/ru-code/serverLifecycleEvents") {} export const ServerLifecycleEventsLive = Layer.effect( ServerLifecycleEvents, diff --git a/apps/server/src/serverRuntimeStartup.ts b/apps/server/src/serverRuntimeStartup.ts index ecc3a61b96d..1d42605b913 100644 --- a/apps/server/src/serverRuntimeStartup.ts +++ b/apps/server/src/serverRuntimeStartup.ts @@ -59,7 +59,7 @@ export interface ServerRuntimeStartupShape { export class ServerRuntimeStartup extends Context.Service< ServerRuntimeStartup, ServerRuntimeStartupShape ->()("t3/serverRuntimeStartup") {} +>()("@ru-code/ru-code/serverRuntimeStartup") {} interface QueuedCommand { readonly run: Effect.Effect; diff --git a/apps/server/src/serverSettings.ts b/apps/server/src/serverSettings.ts index 3a90817085d..8f395670fdf 100644 --- a/apps/server/src/serverSettings.ts +++ b/apps/server/src/serverSettings.ts @@ -130,7 +130,7 @@ export interface ServerSettingsShape { export class ServerSettingsService extends Context.Service< ServerSettingsService, ServerSettingsShape ->()("t3/serverSettings/ServerSettingsService") { +>()("@ru-code/ru-code/serverSettings/ServerSettingsService") { static readonly layerTest = (overrides: DeepPartial = {}) => Layer.effect( ServerSettingsService, diff --git a/apps/server/src/shutdownSignal.ts b/apps/server/src/shutdownSignal.ts index 93c48b4db97..10489e16e7e 100644 --- a/apps/server/src/shutdownSignal.ts +++ b/apps/server/src/shutdownSignal.ts @@ -7,5 +7,5 @@ export interface ShutdownSignalShape { } export class ShutdownSignal extends Context.Service()( - "t3/shutdownSignal", + "@ru-code/ru-code/shutdownSignal", ) {} diff --git a/apps/server/src/sourceControl/GitHubCli.ts b/apps/server/src/sourceControl/GitHubCli.ts index b2ffc97f4d7..347286a7397 100644 --- a/apps/server/src/sourceControl/GitHubCli.ts +++ b/apps/server/src/sourceControl/GitHubCli.ts @@ -18,7 +18,7 @@ import { SOURCE_CONTROL_DEFAULT_TIMEOUT_MS } from "../timeouts.ts"; export class GitHubCliError extends Schema.TaggedErrorClass()("GitHubCliError", { operation: Schema.String, detail: Schema.String, - cause: Schema.optional(Schema.Defect), + cause: Schema.optional(Schema.Defect()), }) { override get message(): string { return `GitHub CLI failed in ${this.operation}: ${this.detail}`; @@ -92,7 +92,7 @@ export interface GitHubCliShape { } export class GitHubCli extends Context.Service()( - "t3/source-control/GitHubCli", + "@ru-code/ru-code/sourceControl/GitHubCli", ) {} function errorText(error: VcsError | unknown): string { diff --git a/apps/server/src/sourceControl/SourceControlDiscovery.ts b/apps/server/src/sourceControl/SourceControlDiscovery.ts index 12921502ac5..8ea4da6508f 100644 --- a/apps/server/src/sourceControl/SourceControlDiscovery.ts +++ b/apps/server/src/sourceControl/SourceControlDiscovery.ts @@ -311,7 +311,7 @@ export interface SourceControlDiscoveryShape { export class SourceControlDiscovery extends Context.Service< SourceControlDiscovery, SourceControlDiscoveryShape ->()("t3/source-control/SourceControlDiscovery") {} +>()("@ru-code/ru-code/sourceControl/SourceControlDiscovery") {} export const layer = Layer.effect( SourceControlDiscovery, diff --git a/apps/server/src/sourceControl/SourceControlProvider.ts b/apps/server/src/sourceControl/SourceControlProvider.ts index a0465008212..8f7ffec182c 100644 --- a/apps/server/src/sourceControl/SourceControlProvider.ts +++ b/apps/server/src/sourceControl/SourceControlProvider.ts @@ -99,4 +99,4 @@ export interface SourceControlProviderShape { export class SourceControlProvider extends Context.Service< SourceControlProvider, SourceControlProviderShape ->()("t3/source-control/SourceControlProvider") {} +>()("@ru-code/ru-code/sourceControl/SourceControlProvider") {} diff --git a/apps/server/src/sourceControl/SourceControlProviderRegistry.ts b/apps/server/src/sourceControl/SourceControlProviderRegistry.ts index 4b67a2600d3..da25012eb93 100644 --- a/apps/server/src/sourceControl/SourceControlProviderRegistry.ts +++ b/apps/server/src/sourceControl/SourceControlProviderRegistry.ts @@ -44,7 +44,7 @@ export interface SourceControlProviderRegistryShape { export class SourceControlProviderRegistry extends Context.Service< SourceControlProviderRegistry, SourceControlProviderRegistryShape ->()("t3/source-control/SourceControlProviderRegistry") {} +>()("@ru-code/ru-code/sourceControl/SourceControlProviderRegistry") {} function unsupportedProvider(kind: SourceControlProviderKind): SourceControlProviderShape { const unsupported = (operation: string) => diff --git a/apps/server/src/sourceControl/SourceControlRepositoryService.ts b/apps/server/src/sourceControl/SourceControlRepositoryService.ts index a7e3d685d8c..1909cd8181c 100644 --- a/apps/server/src/sourceControl/SourceControlRepositoryService.ts +++ b/apps/server/src/sourceControl/SourceControlRepositoryService.ts @@ -40,7 +40,7 @@ export interface SourceControlRepositoryServiceShape { export class SourceControlRepositoryService extends Context.Service< SourceControlRepositoryService, SourceControlRepositoryServiceShape ->()("t3/source-control/SourceControlRepositoryService") {} +>()("@ru-code/ru-code/sourceControl/SourceControlRepositoryService") {} function detailFromUnknown(cause: unknown): string { if (typeof cause === "object" && cause !== null) { diff --git a/apps/server/src/terminal/Layers/Manager.ts b/apps/server/src/terminal/Layers/Manager.ts index 33680cfbd07..2758d82d8f5 100644 --- a/apps/server/src/terminal/Layers/Manager.ts +++ b/apps/server/src/terminal/Layers/Manager.ts @@ -63,7 +63,7 @@ class TerminalSubprocessCheckError extends Schema.TaggedErrorClass()( - "t3/terminal/Services/Manager/TerminalManager", + "@ru-code/ru-code/terminal/Services/Manager/TerminalManager", ) {} diff --git a/apps/server/src/terminal/Services/PTY.ts b/apps/server/src/terminal/Services/PTY.ts index 93cd1b0d47b..7ada96a999d 100644 --- a/apps/server/src/terminal/Services/PTY.ts +++ b/apps/server/src/terminal/Services/PTY.ts @@ -16,7 +16,7 @@ import * as Context from "effect/Context"; export class PtySpawnError extends Schema.TaggedErrorClass()("PtySpawnError", { adapter: Schema.String, message: Schema.String, - cause: Schema.optional(Schema.Defect), + cause: Schema.optional(Schema.Defect()), }) {} export interface PtyExitEvent { @@ -56,5 +56,5 @@ export interface PtyAdapterShape { * PtyAdapter - Service tag for PTY process integration. */ export class PtyAdapter extends Context.Service()( - "t3/terminal/Services/PTY/PtyAdapter", + "@ru-code/ru-code/terminal/Services/PTY/PtyAdapter", ) {} diff --git a/apps/server/src/textGeneration/TextGeneration.ts b/apps/server/src/textGeneration/TextGeneration.ts index b0ecfcffff4..3c912ac7846 100644 --- a/apps/server/src/textGeneration/TextGeneration.ts +++ b/apps/server/src/textGeneration/TextGeneration.ts @@ -117,7 +117,7 @@ export interface TextGenerationShape { * TextGeneration - Service tag for commit and PR text generation. */ export class TextGeneration extends Context.Service()( - "t3/text-generation/TextGeneration", + "@ru-code/ru-code/textGeneration/TextGeneration", ) {} type TextGenerationOp = diff --git a/apps/server/src/vcs/GitVcsDriver.ts b/apps/server/src/vcs/GitVcsDriver.ts index 8d788b360eb..8f6be65ddb1 100644 --- a/apps/server/src/vcs/GitVcsDriver.ts +++ b/apps/server/src/vcs/GitVcsDriver.ts @@ -218,7 +218,7 @@ export interface GitVcsDriverShape { } export class GitVcsDriver extends Context.Service()( - "t3/vcs/GitVcsDriver", + "@ru-code/ru-code/vcs/GitVcsDriver", ) {} const WORKSPACE_FILES_MAX_OUTPUT_BYTES = 16 * 1024 * 1024; diff --git a/apps/server/src/vcs/VcsDriver.ts b/apps/server/src/vcs/VcsDriver.ts index 8616b672929..b8e570926fc 100644 --- a/apps/server/src/vcs/VcsDriver.ts +++ b/apps/server/src/vcs/VcsDriver.ts @@ -29,4 +29,4 @@ export interface VcsDriverShape { readonly initRepository: (input: VcsInitInput) => Effect.Effect; } -export class VcsDriver extends Context.Service()("t3/vcs/VcsDriver") {} +export class VcsDriver extends Context.Service()("@ru-code/ru-code/vcs/VcsDriver") {} diff --git a/apps/server/src/vcs/VcsDriverRegistry.ts b/apps/server/src/vcs/VcsDriverRegistry.ts index d0e6b74a6ae..a695fc5e218 100644 --- a/apps/server/src/vcs/VcsDriverRegistry.ts +++ b/apps/server/src/vcs/VcsDriverRegistry.ts @@ -34,7 +34,7 @@ export interface VcsDriverRegistryShape { } export class VcsDriverRegistry extends Context.Service()( - "t3/vcs/VcsDriverRegistry", + "@ru-code/ru-code/vcs/VcsDriverRegistry", ) {} const unsupported = (operation: string, kind: VcsDriverKind, detail: string) => diff --git a/apps/server/src/vcs/VcsProcess.ts b/apps/server/src/vcs/VcsProcess.ts index 240d4d41b8f..ecd8790996c 100644 --- a/apps/server/src/vcs/VcsProcess.ts +++ b/apps/server/src/vcs/VcsProcess.ts @@ -67,7 +67,7 @@ export interface VcsProcessShape { } export class VcsProcess extends Context.Service()( - "t3/vcs/VcsProcess", + "@ru-code/ru-code/vcs/VcsProcess", ) {} const DEFAULT_MAX_OUTPUT_BYTES = 1_000_000; diff --git a/apps/server/src/vcs/VcsProjectConfig.ts b/apps/server/src/vcs/VcsProjectConfig.ts index 3e5ee2347ce..33b479c31e3 100644 --- a/apps/server/src/vcs/VcsProjectConfig.ts +++ b/apps/server/src/vcs/VcsProjectConfig.ts @@ -38,7 +38,7 @@ export interface VcsProjectConfigShape { } export class VcsProjectConfig extends Context.Service()( - "t3/vcs/VcsProjectConfig", + "@ru-code/ru-code/vcs/VcsProjectConfig", ) {} function configuredKind(config: ProjectVcsConfigFile): VcsDriverKindType | "auto" { diff --git a/apps/server/src/vcs/VcsProvisioningService.ts b/apps/server/src/vcs/VcsProvisioningService.ts index c10072a3c98..635a150c4f1 100644 --- a/apps/server/src/vcs/VcsProvisioningService.ts +++ b/apps/server/src/vcs/VcsProvisioningService.ts @@ -17,7 +17,7 @@ export interface VcsProvisioningServiceShape { export class VcsProvisioningService extends Context.Service< VcsProvisioningService, VcsProvisioningServiceShape ->()("t3/vcs/VcsProvisioningService") {} +>()("@ru-code/ru-code/vcs/VcsProvisioningService") {} function resolveRequestedKind( kind: VcsDriverKind | undefined, diff --git a/apps/server/src/vcs/VcsStatusBroadcaster.ts b/apps/server/src/vcs/VcsStatusBroadcaster.ts index c93b7291732..230613d8b35 100644 --- a/apps/server/src/vcs/VcsStatusBroadcaster.ts +++ b/apps/server/src/vcs/VcsStatusBroadcaster.ts @@ -65,7 +65,7 @@ export interface VcsStatusBroadcasterShape { export class VcsStatusBroadcaster extends Context.Service< VcsStatusBroadcaster, VcsStatusBroadcasterShape ->()("t3/vcs/VcsStatusBroadcaster") {} +>()("@ru-code/ru-code/vcs/VcsStatusBroadcaster") {} function fingerprintStatusPart(status: unknown): string { return JSON.stringify(status); diff --git a/apps/server/src/workspace/Services/WorkspaceEntries.ts b/apps/server/src/workspace/Services/WorkspaceEntries.ts index a5d7ed8c082..7b9356e6fa2 100644 --- a/apps/server/src/workspace/Services/WorkspaceEntries.ts +++ b/apps/server/src/workspace/Services/WorkspaceEntries.ts @@ -23,7 +23,7 @@ export class WorkspaceEntriesError extends Schema.TaggedErrorClass()( - "t3/workspace/Services/WorkspaceEntries", + "@ru-code/ru-code/workspace/Services/WorkspaceEntries", ) {} diff --git a/apps/server/src/workspace/Services/WorkspaceFileSystem.ts b/apps/server/src/workspace/Services/WorkspaceFileSystem.ts index b448a8ab505..b364ff47761 100644 --- a/apps/server/src/workspace/Services/WorkspaceFileSystem.ts +++ b/apps/server/src/workspace/Services/WorkspaceFileSystem.ts @@ -20,7 +20,7 @@ export class WorkspaceFileSystemError extends Schema.TaggedErrorClass()("t3/workspace/Services/WorkspaceFileSystem") {} +>()("@ru-code/ru-code/workspace/Services/WorkspaceFileSystem") {} diff --git a/apps/server/src/workspace/Services/WorkspacePaths.ts b/apps/server/src/workspace/Services/WorkspacePaths.ts index 7c57ca19bd2..ddeb0a13373 100644 --- a/apps/server/src/workspace/Services/WorkspacePaths.ts +++ b/apps/server/src/workspace/Services/WorkspacePaths.ts @@ -99,5 +99,5 @@ export interface WorkspacePathsShape { * WorkspacePaths - Service tag for workspace path normalization and resolution. */ export class WorkspacePaths extends Context.Service()( - "t3/workspace/Services/WorkspacePaths", + "@ru-code/ru-code/workspace/Services/WorkspacePaths", ) {} diff --git a/apps/server/tests/OrchestrationEngineHarness.integration.ts b/apps/server/tests/OrchestrationEngineHarness.integration.ts index c9d669551ca..73cca0622a0 100644 --- a/apps/server/tests/OrchestrationEngineHarness.integration.ts +++ b/apps/server/tests/OrchestrationEngineHarness.integration.ts @@ -161,7 +161,7 @@ class OrchestrationHarnessRuntimeError extends Schema.TaggedErrorClass Effect.gen(function* () { const provider = options?.provider ?? ProviderDriverKind.make("codex"); + const crypto = yield* Crypto.Crypto; const runtimeEvents = yield* Queue.unbounded(); let sessionCount = 0; const sessions = new Map(); @@ -241,6 +242,18 @@ export const makeTestProviderAdapterHarness = (options?: MakeTestProviderAdapter >(); const emit = (event: ProviderRuntimeEvent) => Queue.offer(runtimeEvents, event); + const randomUUIDv4 = (threadId: ThreadId) => + crypto.randomUUIDv4.pipe( + Effect.mapError( + (cause) => + new ProviderAdapterValidationError({ + provider, + operation: "crypto/randomUUIDv4", + issue: `Failed to generate test runtime identifier for thread '${threadId}'.`, + cause, + }), + ), + ); const startSession: ProviderAdapterShape["startSession"] = (input) => Effect.gen(function* () { @@ -309,7 +322,7 @@ export const makeTestProviderAdapterHarness = (options?: MakeTestProviderAdapter for (const fixtureEvent of response.events) { const rawEvent: Record = { ...(fixtureEvent as Record), - eventId: yield* Random.nextUUIDv4, + eventId: yield* randomUUIDv4(input.threadId), provider, sessionId: RuntimeSessionId.make(String(input.threadId)), }; @@ -366,7 +379,7 @@ export const makeTestProviderAdapterHarness = (options?: MakeTestProviderAdapter if (deferredTurnCompletedEvents.length === 0) { yield* emit({ type: "turn.completed", - eventId: EventId.make(yield* Random.nextUUIDv4), + eventId: EventId.make(yield* randomUUIDv4(input.threadId)), provider, createdAt: nowIso(), threadId: state.snapshot.threadId, diff --git a/apps/server/tests/open.test.ts b/apps/server/tests/open.test.ts index 10912174981..081b70a673b 100644 --- a/apps/server/tests/open.test.ts +++ b/apps/server/tests/open.test.ts @@ -1,10 +1,10 @@ import * as NodeServices from "@effect/platform-node/NodeServices"; import { assert, it } from "@effect/vitest"; import { assertSuccess } from "@effect/vitest/utils"; +import * as Crypto from "effect/Crypto"; import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; import * as Path from "effect/Path"; -import * as Random from "effect/Random"; import { isCommandAvailable, @@ -472,8 +472,9 @@ it.layer(NodeServices.layer)("launchDetached", (it) => { it.effect("rejects when command does not exist", () => Effect.gen(function* () { + const crypto = yield* Crypto.Crypto; const result = yield* launchDetached({ - command: `t3code-no-such-command-${yield* Random.nextUUIDv4}`, + command: `t3code-no-such-command-${yield* crypto.randomUUIDv4}`, args: [], }).pipe(Effect.result); assert.equal(result._tag, "Failure"); diff --git a/apps/server/tests/provider/providerMaintenance.test.ts b/apps/server/tests/provider/providerMaintenance.test.ts index 90595c94664..a7e866feab8 100644 --- a/apps/server/tests/provider/providerMaintenance.test.ts +++ b/apps/server/tests/provider/providerMaintenance.test.ts @@ -6,7 +6,6 @@ import os from "node:os"; import path from "node:path"; import { ProviderDriverKind } from "@t3tools/contracts"; import * as Effect from "effect/Effect"; -import * as Random from "effect/Random"; import { clearLatestProviderVersionCacheForTests, createProviderVersionAdvisory, @@ -18,10 +17,8 @@ import { } from "../../src/provider/providerMaintenance.ts"; const driver = (value: string) => ProviderDriverKind.make(value); -const makeTempDir = Effect.fn("makeTempDir")(function* (name: string) { - const id = yield* Random.nextUUIDv4; - return path.join(os.tmpdir(), `${name}-${id}`); -}); +const makeTempDir = (name: string) => + Effect.sync(() => path.join(os.tmpdir(), `${name}-${globalThis.crypto.randomUUID()}`)); const isNativeTestCommandPath = (expectedPathSegment: string) => (commandPath: string): boolean => diff --git a/apps/server/tsconfig.json b/apps/server/tsconfig.json index fbd256d8b47..f630b5e8b0e 100644 --- a/apps/server/tsconfig.json +++ b/apps/server/tsconfig.json @@ -1,7 +1,6 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "composite": true, "types": ["node"], "lib": ["ESNext", "esnext.disposable"] }, diff --git a/apps/web/src/components/ChatView.tsx b/apps/web/src/components/ChatView.tsx index be8f874e4dd..d5bf754361d 100644 --- a/apps/web/src/components/ChatView.tsx +++ b/apps/web/src/components/ChatView.tsx @@ -107,7 +107,7 @@ import { resolveShortcutCommand, shortcutLabelForCommand } from "../keybindings" import PlanSidebar from "./PlanSidebar"; import ThreadTerminalDrawer from "./ThreadTerminalDrawer"; import { ChevronDownIcon } from "lucide-react"; -import { cn, randomUUID } from "~/lib/utils"; +import { cn, randomHex, randomUUID } from "~/lib/utils"; import { stackedThreadToast, toastManager } from "./ui/toast"; import { decodeProjectScriptKeybindingRule } from "~/lib/projectScriptKeybindings"; import { type NewProjectScriptInput } from "./ProjectScriptsControl"; @@ -2770,7 +2770,7 @@ export default function ChatView(props: ChatViewProps) { prepareWorktree: { projectCwd: activeProject.cwd, baseBranch: baseBranchForWorktree, - branch: buildTemporaryWorktreeBranchName(), + branch: buildTemporaryWorktreeBranchName(randomHex), }, runSetupScript: true, } diff --git a/apps/web/src/lib/utils.ts b/apps/web/src/lib/utils.ts index 24339f9d8ca..4d24daee14d 100644 --- a/apps/web/src/lib/utils.ts +++ b/apps/web/src/lib/utils.ts @@ -1,8 +1,7 @@ import { CommandId, MessageId, ProjectId, ThreadId } from "@t3tools/contracts"; import { type CxOptions, cx } from "class-variance-authority"; +import * as Encoding from "effect/Encoding"; import { twMerge } from "tailwind-merge"; -import * as Random from "effect/Random"; -import * as Effect from "effect/Effect"; import { DraftId } from "../composerDraftStore"; export function cn(...inputs: CxOptions) { @@ -21,11 +20,16 @@ export function isLinuxPlatform(platform: string): boolean { return /linux/i.test(platform); } +export function randomHex(byteLength: number): string { + return Encoding.encodeHex(globalThis.crypto.getRandomValues(new Uint8Array(byteLength))); +} + export function randomUUID(): string { - if (typeof crypto.randomUUID === "function") { - return crypto.randomUUID(); - } - return Effect.runSync(Random.nextUUIDv4); + const bytes = globalThis.crypto.getRandomValues(new Uint8Array(16)); + bytes[6] = (bytes[6]! & 0x0f) | 0x40; + bytes[8] = (bytes[8]! & 0x3f) | 0x80; + const hex = Encoding.encodeHex(bytes); + return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`; } export const newCommandId = (): CommandId => CommandId.make(randomUUID()); diff --git a/effect-bump.md b/effect-bump.md new file mode 100644 index 00000000000..3cfc00a2fbd --- /dev/null +++ b/effect-bump.md @@ -0,0 +1,908 @@ +# Effect bump plan — `4.0.0-beta.59` → `4.0.0-beta.78` + +> Target: move our fork from Effect `4.0.0-beta.59` to the version t3code ships +> (`4.0.0-beta.78`). This file is the **complete, exhaustive** edit list. Only edits +> written here are allowed into the codebase. Every changed line has a before/after. + +## 0. How this was validated (ground truth, not guesswork) + +- Pinned both versions: ours `effect 4.0.0-beta.59` (`pnpm-workspace.yaml`), t3 `4.0.0-beta.78`. +- Traced t3's bump chain in `t3code`: `beta.45 → 59 → 73 → 78`. The only migration + commit touching app code is **`e6330ead8` "Bump Effect to beta.73 and migrate + compatibility APIs"** (starts at our exact version, beta.59). The `73 → 78` step + rode inside the relay feature commit `5ae77c0d6` (no separate code-migration commit). +- Fetched the **real packages** `effect@4.0.0-beta.78` and `@effect/platform-node@4.0.0-beta.78` + from npm and diffed the **exported API surface** of every module we import + (`effect/*`, `effect/unstable/*`, `effect/testing/*`) against our installed + `beta.59`. Among symbols **we actually use**, exactly two were removed/changed: + 1. `effect/Random.nextUUIDv4` — **removed** (verified absent in beta.78 `Random.d.ts`). + 2. `effect/Schema.Defect` — changed from a schema **value** (`const Defect: Defect`) + to a **factory function** (`function Defect(options?): Defect`) — must now be **called**. +- Confirmed via t3's *current* source that t3 uses exactly the replacement patterns + this plan adopts (`Crypto.Crypto` service, `Schema.Defect()`). +- Confirmed no other breakage: we do **not** use any other removed symbol + (`Effect.fromYieldable`/`Effect.Yieldable`, `Schema.DefectWithStack`/`ErrorWithStack`, + `Config.Duration`, `RpcClient.RequestHooks`), no bare effect `Schema.Error`, + no `Schema.transform`/`transformOrFail`. `effect/unstable/*`, `effect/testing/TestClock`, + `Schema.{Struct,Union,Record,Literals,NullOr,Codec,TaggedErrorClass,Array,…}` all + still exist and are unchanged in beta.78. + +**Net: two source-level breaking changes + the dependency/patch bump. Nothing else.** + +### Residual-risk note (read before applying) +The surface diff is exhaustive for **removed / kind-changed** exports (it caught both +breaks). It cannot, by itself, prove that *no function we call kept its name but +changed its parameter list*. That class of change is low-probability for the stable +APIs we use (t3's 59→73 migration touched **only** these same APIs), but the **final +gate is a typecheck after applying** — see §6. If §6's typecheck is green, the plan is +proven complete. + +--- + +## 1. Dependency & config changes + +### 1.1 `pnpm-workspace.yaml` — catalog (lines 13–19) + +BEFORE: +```yaml + effect: 4.0.0-beta.59 + "@effect/atom-react": 4.0.0-beta.59 + "@effect/openapi-generator": 4.0.0-beta.59 + "@effect/platform-node": 4.0.0-beta.59 + "@effect/platform-node-shared": 4.0.0-beta.59 + "@effect/language-service": 0.84.2 + "@effect/vitest": 4.0.0-beta.59 +``` +AFTER: +```yaml + effect: 4.0.0-beta.78 + "@effect/atom-react": 4.0.0-beta.78 + "@effect/openapi-generator": 4.0.0-beta.78 + "@effect/platform-node": 4.0.0-beta.78 + "@effect/platform-node-shared": 4.0.0-beta.78 + "@effect/language-service": 0.86.2 + "@effect/vitest": 4.0.0-beta.78 +``` +Notes: all five `@effect/*` `4.0.0-beta.78` versions verified to exist on npm. +`@effect/language-service` declares **no** effect peer-dep; bumping `0.84.2 → 0.86.2` +(latest) keeps the `prepare` step (`effect-language-service patch`) aligned with the +newer effect. This is dev-tooling only and independent of the source migration. + +### 1.2 `pnpm-workspace.yaml` — patch comment + `patchedDependencies` (lines 26–31) + +BEFORE: +```yaml +# ru-fork: restored (removed in c36945d8). Patches effect's RpcClient to expose +# ConnectionHooks ping/pong/timeout hooks used by the WS transport heartbeat. +# Functionally identical to t3code's patches/effect@4.0.0-beta.78.patch, pinned +# to our beta.59. Recovered from 8fc31793. +patchedDependencies: + effect@4.0.0-beta.59: patches/effect@4.0.0-beta.59.patch +``` +AFTER: +```yaml +# ru-fork: restored (removed in c36945d8). Patches effect's RpcClient to expose +# ConnectionHooks ping/pong/timeout hooks used by the WS transport heartbeat. +# Adopted from t3code's patches/effect@4.0.0-beta.78.patch for the beta.78 bump. +patchedDependencies: + effect@4.0.0-beta.78: patches/effect@4.0.0-beta.78.patch +``` + +### 1.3 The patch file itself + +Our `patches/effect@4.0.0-beta.59.patch` (RpcClient `ConnectionHooks` + RpcSerialization +hooks) **will not apply to beta.78** — beta.78's `dist` line offsets moved. t3's +`patches/effect@4.0.0-beta.78.patch` contains the **identical ConnectionHooks logic +rebased onto beta.78** (verified: the only diffs vs ours are `@@` offsets, blob hashes, +trailing newline, plus one extra hunk). + +Operation: +1. **Delete** `patches/effect@4.0.0-beta.59.patch`. +2. **Add** `patches/effect@4.0.0-beta.78.patch` = a **verbatim copy** of + `t3code/patches/effect@4.0.0-beta.78.patch`. + +**Decision — the extra `McpServer.js` hunk: COPY VERBATIM.** t3's beta.78 patch adds a +`dist/unstable/ai/McpServer.js` DELETE-route hunk (additive MCP HTTP session-teardown +handler) that our beta.59 patch never carried. We take t3's file **as-is, including that +hunk** — it is the tested beta.78 artifact and the extra route is harmless. Do **not** +strip anything. + +`pnpm install` must report the patch applied cleanly (verify — see §6). + +### 1.4 Root `package.json` — `pnpm.overrides` (lines 46–49) + +BEFORE: +```json + "@effect/atom-react": "4.0.0-beta.59", + "@effect/platform-node": "4.0.0-beta.59", + "@effect/platform-node-shared": "4.0.0-beta.59", + "effect": "4.0.0-beta.59", +``` +AFTER: +```json + "@effect/atom-react": "4.0.0-beta.78", + "@effect/platform-node": "4.0.0-beta.78", + "@effect/platform-node-shared": "4.0.0-beta.78", + "effect": "4.0.0-beta.78", +``` + +--- + +## 2. Breaking change #1 — `Random.nextUUIDv4` removed → `Crypto` service + +### Background (why each site changes the way it does) +In beta.78 there is no `Random.nextUUIDv4`. UUIDs come from the **`Crypto.Crypto` +service**: `crypto.randomUUIDv4 : Effect` — it +**requires `Crypto.Crypto` in context** and **can fail**. `Crypto.Crypto` is a +`Context.Service` (no default), unlike `Random` (a `Context.Reference` with a default — +which is why bare `Random.nextUUIDv4` works today without provision). + +`Crypto.Crypto` is provided by `NodeServices.layer` in beta.78 (it now bundles +`NodeCrypto.layer`). Our server already provides `NodeServices.layer` at the runtime +root (`apps/server/src/bin.ts:23`, `apps/server/src/server.ts:115`), and every server +file below already pulls `FileSystem`/`Path`/`ChildProcessSpawner` from that **same** +layer — so `Crypto` rides the identical wiring. **No layer-composition changes are +needed for production code.** Two non-Effect boundaries (web `utils.ts`, `git.ts`) drop +Effect entirely; one test helper uses Node's global crypto; everything else acquires +the service. + +Affected files (9 carrying `Random`): 4 source migrated + **1 source deleted** +(`AcpNativeLogging.ts`, §2.7) + 3 tests + 1 web `utils.ts`; plus the web `ChatView.tsx` +caller (no `Random` of its own). = §2.1–§2.10 below. + +> Import placement convention: add `import * as Crypto from "effect/Crypto";` +> **alphabetically** within the `effect/*` import group (i.e. before `effect/Effect`, +> after `effect/Clock`). Remove the `effect/Random` import line in every file. + +--- + +### 2.1 `apps/web/src/lib/utils.ts` (web — no Effect runtime; mirrors t3) + +Imports — BEFORE (lines 1–6): +```ts +import { CommandId, MessageId, ProjectId, ThreadId } from "@t3tools/contracts"; +import { type CxOptions, cx } from "class-variance-authority"; +import { twMerge } from "tailwind-merge"; +import * as Random from "effect/Random"; +import * as Effect from "effect/Effect"; +import { DraftId } from "../composerDraftStore"; +``` +Imports — AFTER: +```ts +import { CommandId, MessageId, ProjectId, ThreadId } from "@t3tools/contracts"; +import { type CxOptions, cx } from "class-variance-authority"; +import * as Encoding from "effect/Encoding"; +import { twMerge } from "tailwind-merge"; +import { DraftId } from "../composerDraftStore"; +``` + +Body — BEFORE (lines 24–29): +```ts +export function randomUUID(): string { + if (typeof crypto.randomUUID === "function") { + return crypto.randomUUID(); + } + return Effect.runSync(Random.nextUUIDv4); +} +``` +Body — AFTER (adds `randomHex`, drops the Effect fallback; `globalThis.crypto` Web Crypto +is universally available in our browser target): +```ts +export function randomHex(byteLength: number): string { + return Encoding.encodeHex(globalThis.crypto.getRandomValues(new Uint8Array(byteLength))); +} + +export function randomUUID(): string { + const bytes = globalThis.crypto.getRandomValues(new Uint8Array(16)); + bytes[6] = (bytes[6]! & 0x0f) | 0x40; + bytes[8] = (bytes[8]! & 0x3f) | 0x80; + const hex = Encoding.encodeHex(bytes); + return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`; +} +``` +(Lines 31–39 `newCommandId`/`newProjectId`/… are unchanged — they call `randomUUID()`.) + +--- + +### 2.2 `apps/web/src/components/ChatView.tsx` (caller of `git.ts` helper — see §2.3) + +Line 110 — BEFORE: +```ts +import { cn, randomUUID } from "~/lib/utils"; +``` +Line 110 — AFTER: +```ts +import { cn, randomHex, randomUUID } from "~/lib/utils"; +``` + +Line 2773 — BEFORE: +```ts + branch: buildTemporaryWorktreeBranchName(), +``` +Line 2773 — AFTER: +```ts + branch: buildTemporaryWorktreeBranchName(randomHex), +``` + +--- + +### 2.3 `packages/shared/src/git.ts` (sync boundary — inject `randomHex`; mirrors t3) + +Imports — BEFORE (lines 9–10): +```ts +import * as Effect from "effect/Effect"; +import * as Random from "effect/Random"; +``` +Imports — AFTER: **remove both lines** (neither `Effect` nor `Random` is used anywhere +else in this file — verified). + +Body — BEFORE (lines 89–92): +```ts +export function buildTemporaryWorktreeBranchName(): string { + const token = Effect.runSync(Random.nextUUIDv4).replace(/-/g, "").slice(0, 8).toLowerCase(); + return `${WORKTREE_BRANCH_PREFIX}/${token}`; +} +``` +Body — AFTER: +```ts +export function buildTemporaryWorktreeBranchName( + randomHex: (byteLength: number) => string, +): string { + const token = randomHex(4).toLowerCase(); + return `${WORKTREE_BRANCH_PREFIX}/${token}`; +} +``` +Sole caller is `ChatView.tsx:2773` (§2.2) — updated to pass `randomHex`. No test calls +this function (verified). + +--- + +### 2.4 `apps/server/src/atomicWrite.ts` (drop the random id entirely; mirrors t3) + +The temp file already lives in a uniquely-named scoped temp **directory** +(`makeTempDirectoryScoped`), so the filename needs no randomness. + +Line 4 — BEFORE: `import * as Random from "effect/Random";` → **remove**. + +Line 18 — BEFORE: ` const tempFileId = yield* Random.nextUUIDv4;` → **remove the line**. + +Line 29 — BEFORE: +```ts + const tempPath = path.join(tempDirectory, `${tempFileId}.tmp`); +``` +Line 29 — AFTER: +```ts + const tempPath = path.join(tempDirectory, "contents.tmp"); +``` +(No `Crypto` needed here. The `ru-fork` `mode`/`dirMode` block is untouched.) + +--- + +### 2.5 `apps/server/src/environment/Layers/ServerEnvironment.ts` (mirrors t3) + +Imports — BEFORE (lines 1–6): +```ts +import { EnvironmentId, type ExecutionEnvironmentDescriptor } from "@t3tools/contracts"; +import * as Effect from "effect/Effect"; +import * as FileSystem from "effect/FileSystem"; +import * as Layer from "effect/Layer"; +import * as Path from "effect/Path"; +import * as Random from "effect/Random"; +``` +Imports — AFTER: +```ts +import { EnvironmentId, type ExecutionEnvironmentDescriptor } from "@t3tools/contracts"; +import * as Crypto from "effect/Crypto"; +import * as Effect from "effect/Effect"; +import * as FileSystem from "effect/FileSystem"; +import * as Layer from "effect/Layer"; +import * as Path from "effect/Path"; +``` + +Service acquisition — BEFORE (lines 39–41): +```ts + const fileSystem = yield* FileSystem.FileSystem; + const path = yield* Path.Path; + const serverConfig = yield* ServerConfig; +``` +AFTER (add line after `serverConfig`): +```ts + const fileSystem = yield* FileSystem.FileSystem; + const path = yield* Path.Path; + const serverConfig = yield* ServerConfig; + const crypto = yield* Crypto.Crypto; +``` + +Use site — BEFORE (line 67): +```ts + const generated = yield* Random.nextUUIDv4; +``` +AFTER: +```ts + const generated = yield* crypto.randomUUIDv4; +``` +`Crypto.Crypto` is supplied wherever this layer is built (same source as `FileSystem`/`Path`). +The added `PlatformError` in the effect's error channel is already within the layer's +existing error union (FileSystem ops), so no extra handling is required (matches t3). + +--- + +### 2.6 `apps/server/src/provider/Layers/CliAdapter.ts` (fork-specific — keep `never` via `orDie`) + +Imports: add `import * as Crypto from "effect/Crypto";` **after** line 29 +(`import * as Clock from "effect/Clock";`); **remove** line 39 +(`import * as Random from "effect/Random";`). + +Service acquisition — BEFORE (lines 417–418, inside `makeCliAdapter`'s `Effect.gen`): +```ts + const serverConfig = yield* Effect.service(ServerConfig); + const nativeEventLogger = options?.nativeEventLogger ?? undefined; +``` +AFTER (add `crypto` line): +```ts + const serverConfig = yield* Effect.service(ServerConfig); + const crypto = yield* Crypto.Crypto; + const nativeEventLogger = options?.nativeEventLogger ?? undefined; +``` + +Use site — BEFORE (line 432): +```ts + const nextEventId = Effect.map(Random.nextUUIDv4, (id) => EventId.make(id)); +``` +AFTER: +```ts + const nextEventId = crypto.randomUUIDv4.pipe( + Effect.map((id) => EventId.make(id)), + Effect.orDie, + ); +``` +Rationale: `nextEventId` feeds `makeEventStamp()` (line 433) and the whole event-emission +path, all typed with error channel `never`. `Effect.orDie` converts a `crypto` +`PlatformError` (a missing secure RNG — unrecoverable anyway) into a defect, **preserving +the `never` error channel** so zero downstream signatures ripple. `Crypto.Crypto` is +provided by the same `NodeServices.layer` that already supplies this adapter's +`FileSystem`/`Path`/`ChildProcessSpawner`. + +--- + +### 2.7 `apps/server/src/provider/acp/AcpNativeLogging.ts` → **DELETE THE FILE** + +`makeAcpNativeLoggers` is the file's only export and it has **zero references** repo-wide +(verified: the only match for `AcpNativeLogging`/`makeAcpNativeLogger` across the whole +tree is its own definition — no importer, no barrel re-export). It is dead code whose +sole reason for appearing in this migration is its `Random.nextUUIDv4` use (line 25). + +**Action: delete `apps/server/src/provider/acp/AcpNativeLogging.ts` entirely.** This +removes the `Random.nextUUIDv4` usage at the source — no `Crypto` migration, no factory, +no caller updates. Nothing else in the codebase changes as a result of the deletion. + +(If this file is ever wanted again, re-add it against beta.78 using the `Crypto.Crypto` +service for the event `id` — but that is out of scope for the bump.) + +--- + +### 2.8 `apps/server/tests/open.test.ts` (test — `it.layer(NodeServices.layer)` already provides Crypto) + +Imports: add `import * as Crypto from "effect/Crypto";` **after** line 3 +(`@effect/vitest/utils`, before `effect/Effect`); **remove** line 7 +(`import * as Random from "effect/Random";`). + +Use site — BEFORE (lines 473–481, inside the `it.layer(NodeServices.layer)("launchDetached", …)` block): +```ts + it.effect("rejects when command does not exist", () => + Effect.gen(function* () { + const result = yield* launchDetached({ + command: `t3code-no-such-command-${yield* Random.nextUUIDv4}`, + args: [], + }).pipe(Effect.result); + assert.equal(result._tag, "Failure"); + }), + ); +``` +AFTER: +```ts + it.effect("rejects when command does not exist", () => + Effect.gen(function* () { + const crypto = yield* Crypto.Crypto; + const result = yield* launchDetached({ + command: `t3code-no-such-command-${yield* crypto.randomUUIDv4}`, + args: [], + }).pipe(Effect.result); + assert.equal(result._tag, "Failure"); + }), + ); +``` +This block runs under `it.layer(NodeServices.layer)` → `Crypto.Crypto` is in context. + +--- + +### 2.9 `apps/server/tests/TestProviderAdapter.integration.ts` (mirror t3 exactly) + +Imports — BEFORE (lines 13–16): +```ts +import * as Effect from "effect/Effect"; +import * as Queue from "effect/Queue"; +import * as Random from "effect/Random"; +import * as Stream from "effect/Stream"; +``` +Imports — AFTER (Crypto alphabetical, before Effect; Random removed): +```ts +import * as Crypto from "effect/Crypto"; +import * as Effect from "effect/Effect"; +import * as Queue from "effect/Queue"; +import * as Stream from "effect/Stream"; +``` + +Acquire `crypto` — BEFORE (lines 227–229, top of `makeTestProviderAdapterHarness`'s gen): +```ts + Effect.gen(function* () { + const provider = options?.provider ?? ProviderDriverKind.make("codex"); + const runtimeEvents = yield* Queue.unbounded(); +``` +AFTER: +```ts + Effect.gen(function* () { + const provider = options?.provider ?? ProviderDriverKind.make("codex"); + const crypto = yield* Crypto.Crypto; + const runtimeEvents = yield* Queue.unbounded(); +``` + +Add the error-mapping helper — BEFORE (line 243): +```ts + const emit = (event: ProviderRuntimeEvent) => Queue.offer(runtimeEvents, event); +``` +AFTER (append the helper right after `emit`): +```ts + const emit = (event: ProviderRuntimeEvent) => Queue.offer(runtimeEvents, event); + const randomUUIDv4 = (threadId: ThreadId) => + crypto.randomUUIDv4.pipe( + Effect.mapError( + (cause) => + new ProviderAdapterValidationError({ + provider, + operation: "crypto/randomUUIDv4", + issue: `Failed to generate test runtime identifier for thread '${threadId}'.`, + cause, + }), + ), + ); +``` +(`ProviderAdapterValidationError` is already imported (line 20) and has exactly the +fields `{ provider, operation, issue, cause }` — verified in `provider/Errors.ts`.) + +Use site 1 — BEFORE (line 312): +```ts + eventId: yield* Random.nextUUIDv4, +``` +AFTER: +```ts + eventId: yield* randomUUIDv4(input.threadId), +``` + +Use site 2 — BEFORE (line 369): +```ts + eventId: EventId.make(yield* Random.nextUUIDv4), +``` +AFTER: +```ts + eventId: EventId.make(yield* randomUUIDv4(input.threadId)), +``` +Consumer provision: the harness's new `Crypto.Crypto` requirement is satisfied by its +only consumer, `apps/server/tests/OrchestrationEngineHarness.integration.ts`, which +already `Layer.provideMerge(NodeServices.layer)` (line 279). No change needed there. + +--- + +### 2.10 `apps/server/tests/provider/providerMaintenance.test.ts` (helper → `Effect.sync` + Node global crypto) + +The 7 `it.effect` blocks that call `makeTempDir` (lines 139, 174, 208, 267, 302, 383, +427) do **not** provide `NodeServices`/`Crypto`. Rather than thread the Crypto service +into all of them, compute the id synchronously via Node's global crypto inside an +`Effect.sync` — keeping all 7 call sites identical (`yield* makeTempDir(...)`), with no +service requirement and no `PlatformError`. + +Line 9 — BEFORE: `import * as Random from "effect/Random";` → **remove**. +(The existing `import * as NodeServices …` on line 4 stays — it is used by the +`Effect.provide(NodeServices.layer)` blocks at lines 407/455.) + +Helper — BEFORE (lines 21–24): +```ts +const makeTempDir = Effect.fn("makeTempDir")(function* (name: string) { + const id = yield* Random.nextUUIDv4; + return path.join(os.tmpdir(), `${name}-${id}`); +}); +``` +Helper — AFTER: +```ts +const makeTempDir = (name: string) => + Effect.sync(() => path.join(os.tmpdir(), `${name}-${globalThis.crypto.randomUUID()}`)); +``` +All 7 `yield* makeTempDir("…")` call sites stay byte-for-byte unchanged. (File already +has `// @effect-diagnostics nodeBuiltinImport:off`; `globalThis.crypto` is a global, not +a node import.) + +--- + +## 3. Breaking change #2 — `Schema.Defect` value → `Schema.Defect()` factory + +In beta.78, `effect/Schema`'s `Defect` is a factory function and **must be called**. +We have **58** bare-`Schema.Defect` sites (verified: 0 are already called). There are +exactly two textual shapes; the transform is mechanical and identical per site: + +- Shape A (×55): `Schema.optional(Schema.Defect)` → `Schema.optional(Schema.Defect())` +- Shape B (×3): `cause: Schema.Defect,` → `cause: Schema.Defect(),` + +> `Schema.optional` itself is unchanged in beta.78 — **only its argument changes**. +> `Schema.Error` (effect) is **not** used bare anywhere (all `…Schema.Error` matches are +> the custom `AcpSchema.Error`), so it needs no change. +> **Do NOT touch** `packages/effect-acp/src/protocol.ts:551` — that is a *comment* +> mentioning `Schema.Defect`, not code. + +### 3.1 Shape A sites — `Schema.optional(Schema.Defect)` → `Schema.optional(Schema.Defect())` + +| File | Lines | +|---|---| +| `apps/server/tests/OrchestrationEngineHarness.integration.ts` | 164 | +| `apps/server/src/workspace/Services/WorkspaceFileSystem.ts` | 23 | +| `apps/server/src/workspace/Services/WorkspaceEntries.ts` | 26, 37 | +| `apps/server/src/provider/Errors.ts` | 14, 30, 46, 63, 80, 96, 111, 132, 151, 166, 182 | +| `apps/server/src/terminal/Layers/Manager.ts` | 66, 76 | +| `apps/server/src/terminal/Services/PTY.ts` | 19 | +| `apps/server/src/persistence/Errors.ts` | 13, 26, 75, 88 | +| `apps/server/src/orchestration/Errors.ts` | 10, 22, 35, 48, 61, 74 | +| `apps/server/src/checkpointing/Errors.ts` | 14, 30 | +| `apps/server/src/sourceControl/GitHubCli.ts` | 21 | +| `packages/effect-acp/src/errors.ts` | 20, 34 | +| `packages/contracts/src/project.ts` | 33, 53 | +| `packages/contracts/src/editor.ts` | 55 | +| `packages/contracts/src/vcs.ts` | 111, 125 | +| `packages/contracts/src/settings.ts` | 257 | +| `packages/contracts/src/filesystem.ts` | 28 | +| `packages/contracts/src/sourceControl.ts` | 159, 173 | +| `packages/contracts/src/terminal.ts` | 162, 191 | +| `packages/contracts/src/keybindings.ts` | 156 | +| `packages/contracts/src/server.ts` | 363 | +| `packages/contracts/src/ru-fork/mcp.ts` | 324 | +| `packages/contracts/src/git.ts` | 325, 337, 348 | +| `packages/contracts/src/orchestration.ts` | 1367, 1375, 1383, 1391, 1399 | + +Each listed line, BEFORE → AFTER (identical edit, shown once; applies to every line above): +```ts + cause: Schema.optional(Schema.Defect), +``` +```ts + cause: Schema.optional(Schema.Defect()), +``` + +### 3.2 Shape B sites — bare `cause: Schema.Defect,` → `cause: Schema.Defect(),` + +| File | Line | BEFORE | AFTER | +|---|---|---|---| +| `packages/effect-acp/src/errors.ts` | 7 | ` cause: Schema.Defect,` | ` cause: Schema.Defect(),` | +| `packages/effect-acp/src/errors.ts` | 46 | ` cause: Schema.Defect,` | ` cause: Schema.Defect(),` | +| `packages/contracts/src/vcs.ts` | 67 | ` cause: Schema.Defect,` | ` cause: Schema.Defect(),` | + +> A whole-tree find/replace of the regex `Schema\.Defect\b(?!\()` → `Schema.Defect()` +> (skipping `protocol.ts:551`'s comment) produces exactly these 58 edits. The table is +> the authoritative inventory; the regex is a convenience to apply them. + +--- + +## 4. Tests that change (summary) + +All test changes are already specified inline above; consolidated here for the +"do tests need changes?" check: + +1. `apps/server/tests/open.test.ts` — §2.8 (Crypto import + `crypto.randomUUIDv4`). +2. `apps/server/tests/TestProviderAdapter.integration.ts` — §2.9 (Crypto + `randomUUIDv4` helper). +3. `apps/server/tests/provider/providerMaintenance.test.ts` — §2.10 (`makeTempDir` → `Effect.sync`). +4. `apps/server/tests/OrchestrationEngineHarness.integration.ts` — §3.1 line 164 only + (`Schema.Defect()`); **no** layer change needed (it already provides `NodeServices.layer`). + +No new test files. No web test target exists (web is validated by typecheck+lint). + +--- + +## 5. Files explicitly NOT changed (and why) + +- Node global `crypto.randomUUID()` call sites (if any) — Node API, unaffected by the + effect bump; t3 migrated some for testability, but they are **not broken** on beta.78, + so they are out of scope for a faithful bump. +- `effect/unstable/*`, `effect/testing/TestClock`, `@effect/atom-react`, + `@effect/platform-node/*` import paths — all still valid in beta.78 (verified). +- `packages/effect-acp/src/protocol.ts:551` — comment, not code. + +--- + +## 6. Apply & validation order (the final completeness gate) + +1. Apply §1 (catalog, overrides, patch file + `patchedDependencies`). +2. `pnpm install --config.confirmModulesPurge=false` + (the `--config…` flag avoids the darwin/linux native-binding purge that breaks + oxlint/vite). **Verify the install log shows the effect patch applied cleanly** — if + it reports a failed/!­fuzzy hunk, revisit §1.3 (patch rebase / McpServer hunk). +3. Apply §2 (Random→Crypto, 9 files) and §3 (Schema.Defect(), 58 sites). +4. **Typecheck** the workspace (the authoritative gate — see Residual-risk note in §0): + - server: `pnpm --filter @t3tools/server typecheck` (or the repo's typecheck task) + - web + packages: their typecheck tasks + Green typecheck across all packages ⇒ no missed signature-level breakage ⇒ plan proven complete. +5. **Lint** (oxlint) — confirms import ordering of the added `effect/Crypto` imports and + that no orphaned references to the deleted `AcpNativeLogging.ts` remain. +6. **Tests**: run the server vitest suite. Expected-green except the pre-existing, + environment-known failures unrelated to this change (`mcp-probe` without qwen; + `bin.test.ts` ×4) — do not misread those as regressions. + +--- + +## 8. Real type errors found at compile (what the static surface-diff MISSED) + +Honesty correction: §0's surface-diff was exhaustive only for **removed/renamed top-level +exports**. It could NOT see (a) **instance-method removals** (`Config.asEffect`), (b) +**type-relation changes** (`SqlClient`, `exactOptionalPropertyTypes`), (c) **Node-global +`crypto.randomUUID()` usage**, or (d) the **effect-language-service diagnostics that are +dormant on beta.59 and activate on beta.78**. Compiling against beta.78 (the gate §0 +named) surfaced all of these. Verified against t3's commit `e6330` (same files) — no +hallucinated fixes. + +**The 23 reported "real" (non-`effect()`-tagged) error lines decompose into exactly 2 +code-fix root causes + 2 LSP lines + cascades.** Full mapping in §8.3. + +### 8.1 Group 1 — `Config.asEffect` removed in beta.78 (mirror t3 `e6330`) +In beta.73+, a `Config` is **itself** the Effect — `.asEffect()` was removed; pipe the +Config directly. t3's `e6330` changed exactly these call shapes and nothing else. + +**8.1a `apps/server/src/persistence/NodeSqliteClient.ts` (~line 255)** — and the +**identical vendored copy** `pixso-move/server/src/vendor/NodeSqliteClient.ts` (~line 259): +BEFORE: +```ts + Layer.effectContext( + Config.unwrap(config) + .asEffect() + .pipe( + Effect.flatMap(make), + Effect.map((client) => + Context.make(SqliteClient, client).pipe(Context.add(Client.SqlClient, client)), + ), + ), + ).pipe(Layer.provide(Reactivity.layer)); +``` +AFTER: +```ts + Layer.effectContext( + Config.unwrap(config).pipe( + Effect.flatMap(make), + Effect.map((client) => + Context.make(SqliteClient, client).pipe(Context.add(Client.SqlClient, client)), + ), + ), + ).pipe(Layer.provide(Reactivity.layer)); +``` +This single change resolves the file's `TS2339` (asEffect), and the cascading `TS2375` +(Layer assignment) + `TS28` (unknown channel) + `TS2345` (`unknown` not assignable to +`SqlClient`) — they were all downstream of the broken `asEffect` type. + +**8.1b `scripts/dev-runner.ts:357`** (t3 `e6330` verbatim): +BEFORE: +```ts + const { portOffset, devInstance } = yield* OffsetConfig.asEffect().pipe( +``` +AFTER: +```ts + const { portOffset, devInstance } = yield* OffsetConfig.pipe( +``` +t3 changed ONLY this line in dev-runner; the other dev-runner errors (`356`, `493`, `503`, +`505`) are cascades of the `unknown` channel and resolve with it. + +### 8.2 Group 2 — CliAdapter `crypto` shadow + Crypto-requirement cascade (self-inflicted by §2.6) + +My §2.6 edit added `const crypto = yield* Crypto.Crypto` to `makeCliAdapter`. That +**(a) shadowed the 5 pre-existing Node-global `crypto.randomUUID()` calls** (lines 473, +861, 949, 1027, 1427 — the effect `Crypto` service has `randomUUIDv4`, not `randomUUID` +→ `TS2551`), and **(b) leaked a `Crypto` requirement into the `ProviderInstance` Effect**, +which `CliDriver.ts:88` assigns to `Scope | CliDriverEnv` (no `Crypto`) → `TS2375`. + +Root fix: **don't introduce the Crypto service here at all.** The file already generates +UUIDs via Node-global `crypto.randomUUID()` (5×); use that same convention for the one +id `§2.6` migrated — no service, no requirement, no shadow, no cascade. (t3 has no +`CliAdapter`; this matches the file's own established pattern rather than inventing one.) + +**`apps/server/src/provider/Layers/CliAdapter.ts`:** + +1. **Remove** the import added in §2.6: `import * as Crypto from "effect/Crypto";` +2. **Remove** the service acquisition added in §2.6 (~line 418): `const crypto = yield* Crypto.Crypto;` +3. `nextEventId` (~line 432) — BEFORE (the §2.6 version): +```ts + const nextEventId = crypto.randomUUIDv4.pipe( + Effect.map((id) => EventId.make(id)), + Effect.orDie, + ); +``` +AFTER: +```ts + const nextEventId = Effect.sync(() => EventId.make(crypto.randomUUID())); +``` +(With the `const crypto` service gone, `crypto` here is the Node global again — exactly +what lines 473/861/949/1027/1427 already use, so all 5 `TS2551` and the `CliDriver:88` +`TS2375` cascade clear together.) + +> Net effect on §2: this **supersedes the CliAdapter portion of §2.6** (the `orDie`/service +> approach). Everything else in §2 (web utils, git.ts, atomicWrite, ServerEnvironment, +> AcpNativeLogging deletion, the 3 tests) stands. + +### 8.3 Complete mapping — every one of the 23 reported lines → its fix +| # | Error line | Code | Fix | +|---|---|---|---| +| 1 | `dev-runner.ts:357` | TS2339 `asEffect` | §8.1b (root) | +| 2–5 | `dev-runner.ts:356,493,503,505` | TS28/TS2345 | §8.1b (cascade) | +| 6–10 | `NodeSqliteClient.ts:254,256,260` | TS2375/2339/2345/28 | §8.1a (root+cascade) | +| 11–15 | `vendor/NodeSqliteClient.ts:258,264` | TS2375/2339/2345/28 | §8.1a (root+cascade) | +| 16–20 | `CliAdapter.ts:473,861,949,1027,1427` | TS2551 | §8.2 | +| 21 | `CliDriver.ts:88` | TS2375 | §8.2 (cascade) | +| 22–23 | `SkillScannerService.ts:40`, `SubagentScannerService.ts:41` | TS8 | **LSP** `leakingRequirements` — see §8.4 | + +### 8.4 The remaining ~134 errors are effect-LSP diagnostics — NOT code bugs +`deterministicKeys` (95), `cryptoRandomUUIDInEffect` (16), `cryptoRandomUUID` (11), +`leakingRequirements`/TS8 (2), `preferSchemaOverJson` (3), `globalTimersInEffect` (3), +`missingLayerContext`/`missingEffectContext` (4) are `@effect/language-service` +diagnostics that were **silent on beta.59** (the LSP couldn't resolve beta.59 types) and +**activate on beta.78**. They are configurable via the `diagnosticSeverity` block in +`tsconfig.base.json` + `apps/web/tsconfig.json`. + +**To land the effect bump green (preserving exact beta.59 behavior, which produced zero +effect-LSP diagnostics):** set the newly-firing rules to `"off"` in both tsconfigs (or +temporarily remove the `@effect/language-service` plugin entry). This is **not** silencing +real bugs — it restores the beta.59 status quo. The full clean-up of these diagnostics +(fix the 97 keys, migrate `crypto.randomUUID()`→service, resolve the requirement leaks) is +the **dedicated `ts-go-migration.md` work**, not the effect bump. + +### 8.5 Apply order for §8 +1. Apply §8.1 (3 files) and §8.2 (CliAdapter). +2. Set the §8.4 firing diagnostics to `"off"` in the two tsconfigs. +3. `pnpm typecheck` → expect green. `pnpm lint`, `pnpm test:fast` → green (minus known-env + failures). If any *new* core-tsc error appears, it's a further missed change — fix and + re-run (do not disable core diagnostics). + +--- + +## 9. Fixing the effect-LSP diagnostics with t3's actual fixes (instead of silencing §8.4) + +Full inventory (141): **deterministicKeys 95, cryptoRandomUUIDInEffect 16, cryptoRandomUUID +11, anyUnknownInErrorContext 5, preferSchemaOverJson 3, globalTimersInEffect 3, +missingLayerContext 2, missingEffectContext 2, leakingRequirements 2, unnecessaryEffectGen +1, lazyPromiseInEffectSync 1.** + +Decomposition: +- **95 deterministicKeys** → key renames. Before/after for all are in `ts-go-migration.md §3` + (the `t3/* → @ru-code/ru-code/*` table). Mechanical, contained, **safe** (in-memory DI keys). +- **9 cascades** (`anyUnknownInErrorContext` 5, `missingLayerContext` 2, + `missingEffectContext` 2) are downstream of the §8 `unknown`/`Crypto` channels — they + **disappear when §8.1/§8.2 are applied**. Not separate work. (Confirm after §8.) +- **27 crypto** → the cascade-heavy one (§9.1). +- **10 misc** → §9.2. + +### 9.1 `crypto.randomUUID()` → effect `Crypto` service (27 sites) — ⚠️ CASCADING + +⚠️ **This is not a per-line edit.** t3's `e6330` proves each conversion turns a value into +an `Effect` that **requires `Crypto.Crypto` and fails with `PlatformError`**, rippling +through every caller. Example (t3 `decider.ts`, verbatim): + +BEFORE: +```ts +function withEventBase(input): Omit { + return { + eventId: crypto.randomUUID() as OrchestrationEvent["eventId"], + aggregateKind: input.aggregateKind, /* …8 fields… */ + }; +} +``` +AFTER: +```ts +function withEventBase(input): Effect.Effect< + Omit, + PlatformError.PlatformError, + Crypto.Crypto +> { + return Crypto.Crypto.pipe( + Effect.flatMap((crypto) => + crypto.randomUUIDv4.pipe( + Effect.map((eventId) => ({ eventId: EventId.make(eventId), /* …8 fields… */ })), + ), + ), + ); +} +``` +…and then `decideCommandSequence`'s signature gains `| PlatformError.PlatformError` + `Crypto.Crypto`, +and `OrchestrationEngine` (its caller) must add `Effect.provideService(Crypto.Crypto, crypto)` +and map the error. **One site → 3+ files.** + +**t3's three canonical templates** (pick per call-site shape): +- **T-A module-level helper** (`project.ts`) — when the site is a CLI/leaf with a domain error: + ```ts + const projectCommandUuid = Crypto.Crypto.pipe( + Effect.flatMap((crypto) => crypto.randomUUIDv4), + Effect.mapError(() => new ProjectCommandError({ message: "Failed to generate …identifier." })), + ); + // call site: CommandId.make(crypto.randomUUID()) → CommandId.make(yield* projectCommandUuid) + ``` +- **T-B in-`make` helper** (`ProviderCommandReactor`) — when inside a service `Effect.gen`: + ```ts + const crypto = yield* Crypto.Crypto; + const serverCommandId = (tag: string) => + crypto.randomUUIDv4.pipe(Effect.map((uuid) => CommandId.make(`server:${tag}:${uuid}`))); + const serverEventId = () => crypto.randomUUIDv4.pipe(Effect.map(EventId.make)); + // call site: commandId: serverCommandId("x") → commandId: yield* serverCommandId("x") + ``` +- **T-C pure-fn → Effect** (`decider.ts`, above) — when the site is in a pure function. + +**The 27 sites, grouped (before = `crypto.randomUUID()` at the location; after = apply the +template; cascade = files that change with it):** + +| File:Line(s) | t3 source | Template | Cascade | +|---|---|---|---| +| `cli/project.ts:346,349,387,427` | **e6330 verbatim** | T-A | runProjectMutation gains `Crypto.Crypto` | +| `orchestration/decider.ts:51` | **e6330 verbatim** | T-C | `decideCommandSequence`→`OrchestrationEngine` | +| `orchestration/Layers/ProviderCommandReactor.ts:90,277` | **e6330 verbatim** | T-B | self-contained in `make` | +| `orchestration/Layers/ProviderRuntimeIngestion.ts:45,1173` | e6330 (diff lines) | T-B | self-contained in `make` | +| `orchestration/Layers/CheckpointReactor.ts:73,95,120,317` | fork — apply T-B | T-B | within `make` | +| `provider/acp/AcpSessionRuntime.ts:193` | fork — apply T-B/T-A | T-B | check method sig | +| `serverRuntimeStartup.ts:163,168,185,188` | fork | T-B | within startup gen | +| `ws.ts:244,265` | t3 has ws.ts — diff it | T-B | within handler gen | +| `auth/Layers/BootstrapCredentialService.ts:144` | fork | T-B | within `make` | +| `auth/Layers/SessionCredentialService.ts:206` | fork | T-B | within `make` | +| `ru-fork/mcp/McpReactor.ts:63` | fork | T-B | within `make` | +| `ru-fork/turnCompletedCheckpointDispatch.ts:82` | fork | T-B | check sig | +| `services/nodeStoreLive.ts:35`, `services/resultStoreLive.ts:47` (pixso) | fork | T-B | within `make` | +| `tests/persistence.test.ts:10` | test | T-B + provide NodeServices | test layer | + +> For each fork site: **before** is the literal `crypto.randomUUID()`; **after** is `yield* +> .randomUUIDv4` (T-B) or `yield* ` (T-A), with the enclosing `Effect` +> signature gaining `Crypto.Crypto` (+ `PlatformError`, or `Effect.orDie` to keep `never`). +> Because each ripples, the exact after-text per site must be authored **with its call +> graph open** — this is the bulk of `e6330` re-done for our fork. **Discharge point:** all +> these `Crypto.Crypto` requirements are satisfied by `NodeServices.layer` already at the +> server root (so production paths compile once threaded; tests need `NodeServices.layer`). + +### 9.2 The 10 misc diagnostics (contained — exact before/after) + +**globalTimersInEffect (3) — `daemonLauncher.ts:66,217,364` (fork-only).** `setTimeout` in +Effect → `Effect.sleep`/`Schedule`. Per site, BEFORE (shape): +```ts +yield* Effect.async((resume) => { setTimeout(() => resume(Effect.void), ms); }); +``` +AFTER: +```ts +yield* Effect.sleep(Duration.millis(ms)); +``` +(Read each of the 3 to confirm the exact surrounding shape; some may pass a callback that +must move into the post-sleep continuation.) + +**preferSchemaOverJson (3) — `server.test.ts:25`, `EventNdjsonLogger.ts:92`, +`packages/shared/src/schemaJson.ts:69`.** `JSON.parse`/`JSON.stringify` → `Schema.fromJsonString(schema)` +/ `Schema.toCodecJson`. ⚠️ `schemaJson.ts` is the shared JSON helper — changing its +encoding could affect persisted data (see [[mcp-not-shipped-no-backcompat]]); verify +round-trip. The test + logger sites are low-risk. + +**leakingRequirements (2) — `SkillScannerService.ts:40`, `SubagentScannerService.ts:41`.** +Service methods leak `FileSystem | Path` to callers. Fix = provide those inside the service +`make` (so methods return `Effect<…, never, never>`), or accept the leak. Needs the service +body open to author; **moderate** (touches the service's method signatures + callers). + +**unnecessaryEffectGen (1) — `Migrations/009_ProviderSessionRuntimeMode.ts:4`.** +BEFORE: `Effect.gen(function* () { return ; })` → AFTER: `` (or `Effect.succeed()`). + +**lazyPromiseInEffectSync (1) — `http.ts:160`.** `Effect.sync(() => )` → +`Effect.promise(() => )` (or `Effect.tryPromise`). Read the site for the exact thunk. + +### 9.3 Honest scope verdict +- **Mechanical/contained (do now cleanly):** 95 keys (§3) + 9 auto-cascade + `unnecessaryEffectGen` + + `lazyPromiseInEffectSync` + `globalTimers` (3) ≈ **~109**. +- **Cascading (the real effort — re-doing `e6330` for our fork):** the **27 crypto** sites + + their requirement-threading, plus `leakingRequirements` (2) and `preferSchemaOverJson` + on `schemaJson.ts`. This is **multi-file per site** and is what made `e6330`+`6b3050ee7` + two large t3 PRs. +- **Recommendation:** do the contained ~109 now with the effect bump; tackle the **27 + crypto** as a focused pass **with each call graph open** (not a blind find/replace) — + either now as a deliberate sub-effort, or as the first half of the tsgo PR. A blind crypto + find/replace WILL break the requirement channels (proven by CliAdapter/CliDriver). + +If steps 4–6 are green, this bump is complete and correct. diff --git a/package.json b/package.json index ba38a2c6a4c..53276665263 100644 --- a/package.json +++ b/package.json @@ -43,10 +43,10 @@ "packageManager": "pnpm@10.15.0", "pnpm": { "overrides": { - "@effect/atom-react": "4.0.0-beta.59", - "@effect/platform-node": "4.0.0-beta.59", - "@effect/platform-node-shared": "4.0.0-beta.59", - "effect": "4.0.0-beta.59", + "@effect/atom-react": "4.0.0-beta.78", + "@effect/platform-node": "4.0.0-beta.78", + "@effect/platform-node-shared": "4.0.0-beta.78", + "effect": "4.0.0-beta.78", "vite": "8.0.10" }, "onlyBuiltDependencies": [ diff --git a/packages/contracts/src/editor.ts b/packages/contracts/src/editor.ts index 4bf84d8294e..5677586647b 100644 --- a/packages/contracts/src/editor.ts +++ b/packages/contracts/src/editor.ts @@ -52,5 +52,5 @@ export type OpenInEditorInput = typeof OpenInEditorInput.Type; export class OpenError extends Schema.TaggedErrorClass()("OpenError", { message: Schema.String, - cause: Schema.optional(Schema.Defect), + cause: Schema.optional(Schema.Defect()), }) {} diff --git a/packages/contracts/src/filesystem.ts b/packages/contracts/src/filesystem.ts index 37da5863429..511f8ee19a3 100644 --- a/packages/contracts/src/filesystem.ts +++ b/packages/contracts/src/filesystem.ts @@ -25,6 +25,6 @@ export class FilesystemBrowseError extends Schema.TaggedErrorClass()( command: Schema.String, cwd: Schema.String, detail: Schema.String, - cause: Schema.optional(Schema.Defect), + cause: Schema.optional(Schema.Defect()), }) { override get message(): string { return `Git command failed in ${this.operation}: ${this.command} (${this.cwd}) - ${this.detail}`; @@ -334,7 +334,7 @@ export class TextGenerationError extends Schema.TaggedErrorClass()("GitManagerError", { operation: Schema.String, detail: Schema.String, - cause: Schema.optional(Schema.Defect), + cause: Schema.optional(Schema.Defect()), }) { override get message(): string { return `Git manager failed in ${this.operation}: ${this.detail}`; diff --git a/packages/contracts/src/keybindings.ts b/packages/contracts/src/keybindings.ts index 502e564fb82..303d5c71c9c 100644 --- a/packages/contracts/src/keybindings.ts +++ b/packages/contracts/src/keybindings.ts @@ -153,7 +153,7 @@ export class KeybindingsConfigError extends Schema.TaggedErrorClass()("McpError", { detail: Schema.String, - cause: Schema.optional(Schema.Defect), + cause: Schema.optional(Schema.Defect()), }) { override get message(): string { return `MCP error: ${this.detail}`; diff --git a/packages/contracts/src/server.ts b/packages/contracts/src/server.ts index b644df92417..d443a080882 100644 --- a/packages/contracts/src/server.ts +++ b/packages/contracts/src/server.ts @@ -360,7 +360,7 @@ export class ServerProviderUpdateError extends Schema.TaggedErrorClass( { cwd: Schema.String, reason: Schema.Literals(["notFound", "notDirectory", "statFailed"]), - cause: Schema.optional(Schema.Defect), + cause: Schema.optional(Schema.Defect()), }, ) { override get message() { @@ -188,7 +188,7 @@ export class TerminalHistoryError extends Schema.TaggedErrorClass()( - "effect-acp/AcpClient", + "effect-acp/client/AcpClient", ) {} interface AcpCoreRequestHandlers { diff --git a/packages/effect-acp/src/errors.ts b/packages/effect-acp/src/errors.ts index 9f6a557e8cf..5a1576f3ae1 100644 --- a/packages/effect-acp/src/errors.ts +++ b/packages/effect-acp/src/errors.ts @@ -4,7 +4,7 @@ import * as AcpSchema from "./_generated/schema.gen.ts"; export class AcpSpawnError extends Schema.TaggedErrorClass()("AcpSpawnError", { command: Schema.optional(Schema.String), - cause: Schema.Defect, + cause: Schema.Defect(), }) { override get message() { return this.command @@ -17,7 +17,7 @@ export class AcpProcessExitedError extends Schema.TaggedErrorClass string, +): string { + const token = randomHex(4).toLowerCase(); return `${WORKTREE_BRANCH_PREFIX}/${token}`; } diff --git a/packages/shared/src/schemaJson.ts b/packages/shared/src/schemaJson.ts index 0d879913ebf..685d766f117 100644 --- a/packages/shared/src/schemaJson.ts +++ b/packages/shared/src/schemaJson.ts @@ -41,6 +41,8 @@ export const formatSchemaError = (cause: Cause.Cause) => { : Cause.pretty(cause); }; +const decodeUnknownJsonString = Schema.decodeUnknownSync(Schema.UnknownFromJsonString); + /** * A `Getter` that parses a lenient JSON string (tolerating trailing commas * and JS-style comments) into an unknown value. @@ -66,7 +68,7 @@ const parseLenientJsonGetter = SchemaGetter.onSome((input: string) => // Strip trailing commas before `}` or `]`. stripped = stripped.replace(/,(\s*[}\]])/g, "$1"); - return Option.some(JSON.parse(stripped)); + return Option.some(decodeUnknownJsonString(stripped)); }, catch: (e) => new SchemaIssue.InvalidValue(Option.some(input), { message: String(e) }), }), diff --git a/patches/effect@4.0.0-beta.59.patch b/patches/effect@4.0.0-beta.78.patch similarity index 77% rename from patches/effect@4.0.0-beta.59.patch rename to patches/effect@4.0.0-beta.78.patch index 02c1b671ad3..74be8da65f1 100644 --- a/patches/effect@4.0.0-beta.59.patch +++ b/patches/effect@4.0.0-beta.78.patch @@ -1,5 +1,36 @@ +diff --git a/dist/unstable/ai/McpServer.js b/dist/unstable/ai/McpServer.js +index 4819ff0..f5e0f60 100644 +--- a/dist/unstable/ai/McpServer.js ++++ b/dist/unstable/ai/McpServer.js +@@ -235,6 +235,26 @@ export const run = /*#__PURE__*/Effect.fnUntraced(function* (options) { + const server = yield* McpServer; + const isHttp = Option.isSome(yield* Effect.serviceOption(HttpRouter.HttpRouter)); + const clientSessions = new Map(); ++ if (isHttp && options.path !== undefined) { ++ const router = yield* HttpRouter.HttpRouter; ++ yield* router.add("DELETE", options.path, Effect.gen(function* () { ++ const request = yield* HttpServerRequest.HttpServerRequest; ++ const sessionId = request.headers[mcpSessionIdHeader]; ++ if (sessionId === undefined) { ++ return HttpServerResponse.empty({ ++ status: 400 ++ }); ++ } ++ if (!clientSessions.delete(sessionId)) { ++ return HttpServerResponse.empty({ ++ status: 404 ++ }); ++ } ++ return HttpServerResponse.empty({ ++ status: 204 ++ }); ++ })); ++ } + const handlers = yield* Layer.build(layerHandlers(options, { + clientSessions + })); diff --git a/dist/unstable/rpc/RpcClient.d.ts b/dist/unstable/rpc/RpcClient.d.ts -index 7c5970042df67a25b4145df017960b3e6febed9c..7af3374632d53ad59f24e80835aa6741853b1f2b 100644 +index b0f61df..ead663e 100644 --- a/dist/unstable/rpc/RpcClient.d.ts +++ b/dist/unstable/rpc/RpcClient.d.ts @@ -2,6 +2,7 @@ import * as Cause from "../../Cause.ts"; @@ -10,7 +41,7 @@ index 7c5970042df67a25b4145df017960b3e6febed9c..7af3374632d53ad59f24e80835aa6741 import * as Layer from "../../Layer.ts"; import * as Queue from "../../Queue.ts"; import * as Schedule from "../../Schedule.ts"; -@@ -192,9 +193,40 @@ export declare const layerProtocolWorker: (options: { +@@ -260,9 +261,40 @@ export declare const layerProtocolWorker: (options: { readonly targetUtilization?: number | undefined; readonly timeToLive: Duration.Input; }) => Layer.Layer; @@ -50,12 +81,19 @@ index 7c5970042df67a25b4145df017960b3e6febed9c..7af3374632d53ad59f24e80835aa6741 + readonly onPingTimeout?: Effect.Effect | undefined; }>; /** - * @since 4.0.0 + * Represents optional client protocol hooks that run when a transport connects +@@ -279,4 +311,4 @@ declare const ConnectionHooks_base: Context.ServiceClass { isShutdown = true; -@@ -79,6 +80,11 @@ export const makeNoSerialization = /*#__PURE__*/Effect.fnUntraced(function* (gro +@@ -83,6 +84,11 @@ export const makeNoSerialization = /*#__PURE__*/Effect.fnUntraced(function* (gro return Effect.interrupt; } const id = generateRequestId(); @@ -75,7 +113,7 @@ index 951dfc2d667a9136cfa714dcd3195e6575917fde..599c0345873bedd72bc93afe80eab0c6 const send = middleware(message => options.onFromClient({ message, context, -@@ -96,7 +102,7 @@ export const makeNoSerialization = /*#__PURE__*/Effect.fnUntraced(function* (gro +@@ -100,7 +106,7 @@ export const makeNoSerialization = /*#__PURE__*/Effect.fnUntraced(function* (gro headers: Headers.merge(parentFiber.getRef(CurrentHeaders), headers) }); if (discard) { @@ -84,7 +122,7 @@ index 951dfc2d667a9136cfa714dcd3195e6575917fde..599c0345873bedd72bc93afe80eab0c6 } let fiber; return Effect.onInterrupt(Effect.callback(resume => { -@@ -114,7 +120,7 @@ export const makeNoSerialization = /*#__PURE__*/Effect.fnUntraced(function* (gro +@@ -118,7 +124,7 @@ export const makeNoSerialization = /*#__PURE__*/Effect.fnUntraced(function* (gro } }; entries.set(id, entry); @@ -93,7 +131,7 @@ index 951dfc2d667a9136cfa714dcd3195e6575917fde..599c0345873bedd72bc93afe80eab0c6 captureStackTrace: false }) : identity, Effect.runForkWith(parentFiber.context)); fiber.addObserver(exit => { -@@ -123,8 +129,9 @@ export const makeNoSerialization = /*#__PURE__*/Effect.fnUntraced(function* (gro +@@ -127,8 +133,9 @@ export const makeNoSerialization = /*#__PURE__*/Effect.fnUntraced(function* (gro } }); }), interruptors => { @@ -104,7 +142,7 @@ index 951dfc2d667a9136cfa714dcd3195e6575917fde..599c0345873bedd72bc93afe80eab0c6 }); }); const onStreamRequest = Effect.fnUntraced(function* (rpc, middleware, payload, headers, streamBufferSize, context) { -@@ -136,11 +143,17 @@ export const makeNoSerialization = /*#__PURE__*/Effect.fnUntraced(function* (gro +@@ -140,11 +147,17 @@ export const makeNoSerialization = /*#__PURE__*/Effect.fnUntraced(function* (gro }); const fiber = Fiber.getCurrent(); const id = generateRequestId(); @@ -124,7 +162,7 @@ index 951dfc2d667a9136cfa714dcd3195e6575917fde..599c0345873bedd72bc93afe80eab0c6 }); const queue = yield* Queue.bounded(streamBufferSize); entries.set(id, { -@@ -148,9 +161,10 @@ export const makeNoSerialization = /*#__PURE__*/Effect.fnUntraced(function* (gro +@@ -152,9 +165,10 @@ export const makeNoSerialization = /*#__PURE__*/Effect.fnUntraced(function* (gro rpc, queue, scope, @@ -137,7 +175,7 @@ index 951dfc2d667a9136cfa714dcd3195e6575917fde..599c0345873bedd72bc93afe80eab0c6 message, context, discard: false -@@ -165,7 +179,7 @@ export const makeNoSerialization = /*#__PURE__*/Effect.fnUntraced(function* (gro +@@ -169,7 +183,7 @@ export const makeNoSerialization = /*#__PURE__*/Effect.fnUntraced(function* (gro sampled: span.sampled } : {}), headers: Headers.merge(fiber.getRef(CurrentHeaders), headers) @@ -146,7 +184,7 @@ index 951dfc2d667a9136cfa714dcd3195e6575917fde..599c0345873bedd72bc93afe80eab0c6 captureStackTrace: false }) : identity, Effect.catchCause(error => Queue.failCause(queue, error)), Effect.interruptible, Effect.forkIn(scope, { startImmediately: true -@@ -195,7 +209,10 @@ export const makeNoSerialization = /*#__PURE__*/Effect.fnUntraced(function* (gro +@@ -199,7 +213,10 @@ export const makeNoSerialization = /*#__PURE__*/Effect.fnUntraced(function* (gro }); }; }; @@ -158,7 +196,7 @@ index 951dfc2d667a9136cfa714dcd3195e6575917fde..599c0345873bedd72bc93afe80eab0c6 const parentFiber = Fiber.getCurrent(); const fiber = options.onFromClient({ message: { -@@ -209,7 +226,7 @@ export const makeNoSerialization = /*#__PURE__*/Effect.fnUntraced(function* (gro +@@ -213,7 +230,7 @@ export const makeNoSerialization = /*#__PURE__*/Effect.fnUntraced(function* (gro fiber.addObserver(() => { resume(Effect.void); }); @@ -167,7 +205,7 @@ index 951dfc2d667a9136cfa714dcd3195e6575917fde..599c0345873bedd72bc93afe80eab0c6 const write = message => { switch (message._tag) { case "Chunk": -@@ -217,7 +234,13 @@ export const makeNoSerialization = /*#__PURE__*/Effect.fnUntraced(function* (gro +@@ -221,7 +238,13 @@ export const makeNoSerialization = /*#__PURE__*/Effect.fnUntraced(function* (gro const requestId = message.requestId; const entry = entries.get(requestId); if (!entry || entry._tag !== "Queue") return Effect.void; @@ -182,7 +220,7 @@ index 951dfc2d667a9136cfa714dcd3195e6575917fde..599c0345873bedd72bc93afe80eab0c6 message: { _tag: "Ack", requestId: message.requestId -@@ -232,11 +255,18 @@ export const makeNoSerialization = /*#__PURE__*/Effect.fnUntraced(function* (gro +@@ -236,11 +259,18 @@ export const makeNoSerialization = /*#__PURE__*/Effect.fnUntraced(function* (gro const entry = entries.get(requestId); if (!entry) return Effect.void; entries.delete(requestId); @@ -204,7 +242,7 @@ index 951dfc2d667a9136cfa714dcd3195e6575917fde..599c0345873bedd72bc93afe80eab0c6 } case "Defect": { -@@ -530,7 +560,7 @@ export const makeProtocolSocket = options => Protocol.make(Effect.fnUntraced(fun +@@ -568,7 +598,7 @@ export const makeProtocolSocket = options => Protocol.make(Effect.fnUntraced(fun const requestClientMap = new Map(); const write = yield* socket.writer; let parser = serialization.makeUnsafe(); @@ -213,7 +251,7 @@ index 951dfc2d667a9136cfa714dcd3195e6575917fde..599c0345873bedd72bc93afe80eab0c6 let currentError; const onOpen = Effect.suspend(() => { currentError = undefined; -@@ -550,8 +580,7 @@ export const makeProtocolSocket = options => Protocol.make(Effect.fnUntraced(fun +@@ -588,8 +618,7 @@ export const makeProtocolSocket = options => Protocol.make(Effect.fnUntraced(fun body: () => { const response = responses[i++]; if (response._tag === "Pong") { @@ -223,7 +261,7 @@ index 951dfc2d667a9136cfa714dcd3195e6575917fde..599c0345873bedd72bc93afe80eab0c6 } if ("requestId" in response) { const clientId = requestClientMap.get(response.requestId); -@@ -579,12 +608,12 @@ export const makeProtocolSocket = options => Protocol.make(Effect.fnUntraced(fun +@@ -617,12 +646,12 @@ export const makeProtocolSocket = options => Protocol.make(Effect.fnUntraced(fun } }, { onOpen @@ -238,7 +276,7 @@ index 951dfc2d667a9136cfa714dcd3195e6575917fde..599c0345873bedd72bc93afe80eab0c6 }).pipe(Effect.flatMap(() => Effect.fail(new Socket.SocketError({ reason: new Socket.SocketCloseError({ code: 1000 -@@ -626,20 +655,20 @@ export const makeProtocolSocket = options => Protocol.make(Effect.fnUntraced(fun +@@ -664,20 +693,20 @@ export const makeProtocolSocket = options => Protocol.make(Effect.fnUntraced(fun }; })); const defaultRetryPolicy = /*#__PURE__*/Schedule.exponential(500, 1.5).pipe(/*#__PURE__*/Schedule.either(/*#__PURE__*/Schedule.spaced(5000))); @@ -264,8 +302,8 @@ index 951dfc2d667a9136cfa714dcd3195e6575917fde..599c0345873bedd72bc93afe80eab0c6 + }).pipe(Effect.delay("10 seconds"), Effect.ignore, Effect.forever, Effect.interruptible, Effect.forkScoped); return { timeout: latch.await, -@@ -773,6 +802,11 @@ export const makeProtocolWorker = options => Protocol.make(Effect.fnUntraced(fun - * @category protocol +@@ -820,6 +849,11 @@ export const makeProtocolWorker = options => Protocol.make(Effect.fnUntraced(fun + * @since 4.0.0 */ export const layerProtocolWorker = /*#__PURE__*/flow(makeProtocolWorker, /*#__PURE__*/Layer.effect(Protocol)); +/** @@ -274,5 +312,12 @@ index 951dfc2d667a9136cfa714dcd3195e6575917fde..599c0345873bedd72bc93afe80eab0c6 + */ +export class RequestHooks extends /*#__PURE__*/Context.Service()("effect/rpc/RpcClient/RequestHooks") {} /** - * @since 4.0.0 - * @category ConnectionHooks + * Represents optional client protocol hooks that run when a transport connects + * and disconnects. +@@ -835,4 +869,4 @@ export const layerProtocolWorker = /*#__PURE__*/flow(makeProtocolWorker, /*#__PU + export class ConnectionHooks extends /*#__PURE__*/Context.Service()("effect/rpc/RpcClient/ConnectionHooks") {} + // internal + const decodeDefect = /*#__PURE__*/Schema.decodeSync(/*#__PURE__*/Schema.Defect()); +-//# sourceMappingURL=RpcClient.js.map +\ No newline at end of file ++//# sourceMappingURL=RpcClient.js.map diff --git a/pixso-move/processor/src/acp/runner.ts b/pixso-move/processor/src/acp/runner.ts index aac664a820c..96273c9d5a7 100644 --- a/pixso-move/processor/src/acp/runner.ts +++ b/pixso-move/processor/src/acp/runner.ts @@ -6,5 +6,5 @@ import type { AcpRunnerShape } from "../types.ts"; // acpRunnerLive.integration.ts); tests provide a scripted fake. The embed layer resolves // this and hands it to the engine as `deps.acp`. export class AcpRunner extends Context.Service()( - "pixso-move/AcpRunner", + "@pixso-move/processor/acp/runner/AcpRunner", ) {} diff --git a/pixso-move/processor/src/processor.ts b/pixso-move/processor/src/processor.ts index 75e127a65cf..cadac5b14e0 100644 --- a/pixso-move/processor/src/processor.ts +++ b/pixso-move/processor/src/processor.ts @@ -12,5 +12,5 @@ export interface ProcessorShape { } export class Processor extends Context.Service()( - "pixso-move/Processor", + "@pixso-move/processor/processor", ) {} diff --git a/pixso-move/server/src/config.ts b/pixso-move/server/src/config.ts index 76fc2389967..b0f027d7670 100644 --- a/pixso-move/server/src/config.ts +++ b/pixso-move/server/src/config.ts @@ -35,7 +35,7 @@ const defaults: ServerConfigShape = { }; export class ServerConfig extends Context.Service()( - "pixso-move/ServerConfig", + "@pixso-move/server/config/ServerConfig", ) { // Production layer built from resolved options. static readonly layer = (shape: ServerConfigShape): Layer.Layer => diff --git a/pixso-move/server/src/services/nodeStore.ts b/pixso-move/server/src/services/nodeStore.ts index aa7c7875dd9..ea9929a6bb0 100644 --- a/pixso-move/server/src/services/nodeStore.ts +++ b/pixso-move/server/src/services/nodeStore.ts @@ -29,5 +29,5 @@ export interface NodeStoreShape { } export class NodeStore extends Context.Service()( - "pixso-move/NodeStore", + "@pixso-move/server/services/nodeStore", ) {} diff --git a/pixso-move/server/src/services/resultStore.ts b/pixso-move/server/src/services/resultStore.ts index 9760f79857a..61af20696db 100644 --- a/pixso-move/server/src/services/resultStore.ts +++ b/pixso-move/server/src/services/resultStore.ts @@ -30,5 +30,5 @@ export interface ResultStoreShape { } export class ResultStore extends Context.Service()( - "pixso-move/ResultStore", + "@pixso-move/server/services/resultStore", ) {} diff --git a/pixso-move/server/src/vendor/NodeSqliteClient.ts b/pixso-move/server/src/vendor/NodeSqliteClient.ts index e5008feea4f..1557e595793 100644 --- a/pixso-move/server/src/vendor/NodeSqliteClient.ts +++ b/pixso-move/server/src/vendor/NodeSqliteClient.ts @@ -256,14 +256,12 @@ export const layerConfig = ( config: Config.Wrap, ): Layer.Layer => Layer.effectContext( - Config.unwrap(config) - .asEffect() - .pipe( - Effect.flatMap(make), - Effect.map((client) => - Context.make(SqliteClient, client).pipe(Context.add(Client.SqlClient, client)), - ), + Config.unwrap(config).pipe( + Effect.flatMap(make), + Effect.map((client) => + Context.make(SqliteClient, client).pipe(Context.add(Client.SqlClient, client)), ), + ), ).pipe(Layer.provide(Reactivity.layer)); export const layer = (config: SqliteClientConfig): Layer.Layer => diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 31e4efadd4a..55711032e35 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -10,13 +10,13 @@ catalog: # MCP client SDK — used by the probe now and the server-side MCP supervisor # (plan §6.10). Pinned to match qwen-code 0.13.1's @modelcontextprotocol/sdk. "@modelcontextprotocol/sdk": 1.25.1 - effect: 4.0.0-beta.59 - "@effect/atom-react": 4.0.0-beta.59 - "@effect/openapi-generator": 4.0.0-beta.59 - "@effect/platform-node": 4.0.0-beta.59 - "@effect/platform-node-shared": 4.0.0-beta.59 + effect: 4.0.0-beta.78 + "@effect/atom-react": 4.0.0-beta.78 + "@effect/openapi-generator": 4.0.0-beta.78 + "@effect/platform-node": 4.0.0-beta.78 + "@effect/platform-node-shared": 4.0.0-beta.78 "@effect/language-service": 0.84.2 - "@effect/vitest": 4.0.0-beta.59 + "@effect/vitest": 4.0.0-beta.78 "@types/node": 24.10.13 tsdown: 0.20.3 typescript: 5.9.3 @@ -25,7 +25,7 @@ catalog: # ru-fork: restored (removed in c36945d8). Patches effect's RpcClient to expose # ConnectionHooks ping/pong/timeout hooks used by the WS transport heartbeat. -# Functionally identical to t3code's patches/effect@4.0.0-beta.78.patch, pinned -# to our beta.59. Recovered from 8fc31793. +# Adopted from t3code's patches/effect@4.0.0-beta.78.patch for the beta.78 bump, +# preserving our 10s heartbeat interval (5s→10s, the #3054 stability fix). patchedDependencies: - effect@4.0.0-beta.59: patches/effect@4.0.0-beta.59.patch + effect@4.0.0-beta.78: patches/effect@4.0.0-beta.78.patch diff --git a/scripts/dev-runner.ts b/scripts/dev-runner.ts index 89b43bd5446..5dc87f8eaaf 100644 --- a/scripts/dev-runner.ts +++ b/scripts/dev-runner.ts @@ -354,7 +354,7 @@ interface DevRunnerCliInput { export function runDevRunnerWithInput(input: DevRunnerCliInput) { return Effect.gen(function* () { - const { portOffset, devInstance } = yield* OffsetConfig.asEffect().pipe( + const { portOffset, devInstance } = yield* OffsetConfig.pipe( Effect.mapError( (cause) => new DevRunnerError({ diff --git a/ts-go-migration.md b/ts-go-migration.md new file mode 100644 index 00000000000..b12c97b1f5c --- /dev/null +++ b/ts-go-migration.md @@ -0,0 +1,362 @@ +# Migration plan — drop `@effect/language-service`, adopt `@effect/tsgo` + +> Goal: match t3code's typecheck toolchain — replace `tsc --noEmit` + the +> `@effect/language-service` TS plugin with **`@effect/tsgo`** (the Go TypeScript +> compiler, `typescript-go`, with Effect's diagnostics built in). Mirrors t3 commit +> **`6b3050ee7` "Migrate TypeScript checks to Effect TSGo (#2851)"**. +> +> This is **separate from and on top of** the effect `beta.78` bump (see +> `effect-bump.md`). The effect bump is functionally complete; this plan is the +> tooling swap that the LSP 0.84.2→0.86.2 question exposed. + +--- + +## 0. The one thing you must understand first + +`@effect/tsgo` **does not avoid** the `deterministicKeys` problem — it **enforces it**. +tsgo is `typescript-go` with the same `@effect/language-service` diagnostics compiled in, +and it reads the **same** `tsconfig` plugin config. t3's current `tsconfig.base.json` +still has `deterministicKeys: "error"` (plus a *larger* diagnostic set than ours). That +is exactly why t3's migration commit also rewrote every service key. + +So "migrate to tsgo" = **toolchain swap (§1) + fix all 97 service keys (§3) + resolve +whatever the fuller diagnostic set and tsgo's stricter base-checker surface (§4, the real +unknown)**. Honest scope: **~100–200 files**, mostly mechanical. + +**Prerequisite:** verify `@effect/tsgo` runs on this host (Go binary; linux/arm64). This +is the first gate in §5 — if tsgo can't run here, the whole migration is blocked and we +stay on `tsc` + language-service `0.84.2`. + +--- + +## 1. Toolchain swap (deterministic — exact before/after) + +### 1.1 `pnpm-workspace.yaml` catalog +BEFORE: +```yaml + "@effect/language-service": 0.84.2 +``` +AFTER: +```yaml + "@effect/tsgo": 0.13.2 +``` + +### 1.2 Root `package.json` +BEFORE: +```json + "@effect/language-service": "catalog:", +``` +AFTER: +```json + "@effect/tsgo": "catalog:", +``` +and the `prepare` script — BEFORE: +```json + "prepare": "effect-language-service patch", +``` +AFTER: +```json + "prepare": "effect-tsgo patch", +``` +(`@effect/tsgo` exposes the `effect-tsgo` bin; `effect-tsgo patch` installs/links the +`tsgo` binary used by the typecheck scripts — verify it provides `tsgo` on the PATH after +install; t3's scripts call `tsgo --noEmit`.) + +### 1.3 The 14 package `typecheck` scripts +In **each** of these `package.json`, change `"typecheck": "tsc --noEmit"` → +`"typecheck": "tsgo --noEmit"`: +``` +apps/server/package.json +apps/web/package.json +packages/contracts/package.json +packages/shared/package.json +packages/client-runtime/package.json +packages/effect-acp/package.json +packages/branding/package.json +packages/mcp-core/package.json +oxlint-plugin-t3code/package.json +scripts/package.json +pixso-move/contracts/package.json +pixso-move/processor/package.json +pixso-move/server/package.json +pixso-move/plugin/package.json +``` +BEFORE (each): `"typecheck": "tsc --noEmit"` AFTER: `"typecheck": "tsgo --noEmit"` +> Verify each is literally `tsc --noEmit` (a few may pass extra flags / a `-p`; preserve +> those, only swap the binary). + +### 1.4 `tsconfig.base.json` + `apps/web/tsconfig.json` — plugin diagnostic set +Keep the plugin `name: "@effect/language-service"` (tsgo reads it). Replace our current +`diagnosticSeverity` block with **t3's full block** so we match their enforcement: +```jsonc +"plugins": [ + { + "name": "@effect/language-service", + "namespaceImportPackages": ["@effect/platform-node", "effect"], + "diagnosticSeverity": { + "importFromBarrel": "error", + "anyUnknownInErrorContext": "error", + "unsafeEffectTypeAssertion": "error", + "instanceOfSchema": "error", + "deterministicKeys": "error", + "strictEffectProvide": "off", + "missingEffectServiceDependency": "error", + "leakingRequirements": "error", + "globalErrorInEffectCatch": "error", + "globalErrorInEffectFailure": "error", + "unknownInEffectCatch": "error", + "strictBooleanExpressions": "off", + "lazyEffect": "off", + "preferSchemaOverJson": "error", + "schemaSyncInEffect": "error" + // ...transcribe t3's full block verbatim from t3code/tsconfig.base.json (lines ~25-45) + } + } +] +``` +> ⚠️ Each rule we don't currently enforce (`importFromBarrel`, `missingEffectServiceDependency`, +> `leakingRequirements`, `anyUnknownInErrorContext`, `unsafeEffectTypeAssertion`, +> `instanceOfSchema`, `globalErrorInEffectCatch/Failure`, `unknownInEffectCatch`, +> `preferSchemaOverJson`, `schemaSyncInEffect`) can surface **new** violations across the +> fork. Count is unknown until the §5 Phase-1 dry-run. **If we want a minimal first step, +> enable only `deterministicKeys` now and stage the rest** — but that diverges from t3. + +### 1.5 Install +`pnpm install --config.confirmModulesPurge=false` (native-binding flag, per repo memory). +Confirm the effect `beta.78` patch still applies (unchanged by this migration) and that +`effect-tsgo patch` runs in `prepare`. + +--- + +## 2. Why the keys must change (one sentence) +`deterministicKeys` requires every `Context.Service`/`Context.Tag`/`Context.Reference`/ +`Effect.Service` identity key to equal `"//"`. Our +keys use the legacy `t3/*`, `ru-fork/*`, `pixso-move/*` prefixes, so all 97 fail. + +These keys are **in-memory Context identity strings**, not Schema `_tag` discriminators — +renaming is runtime-safe **provided** they aren't persisted/sent on the wire (verify in §5). + +--- + +## 3. The 97 `deterministicKeys` fixes (before → after) + +Mechanical rule per site: **replace the key string literal with the diagnostic's +"expected key".** Authoritative regeneration command: +``` +pnpm exec turbo run typecheck --continue 2>&1 | grep 'expected key is' +``` + +### 3.1 `packages/effect-acp` (pkg `effect-acp`) — 2 +| File:Line | before | after | +|---|---|---| +| `src/agent.ts:210` | `"effect-acp/AcpAgent"` | `"effect-acp/agent/AcpAgent"` | +| `src/client.ts:266` | `"effect-acp/AcpClient"` | `"effect-acp/client/AcpClient"` | + +### 3.2 `pixso-move/processor` (pkg `@pixso-move/processor`) — 2 +| File:Line | before | after | +|---|---|---| +| `src/acp/runner.ts:9` | `"pixso-move/AcpRunner"` | `"@pixso-move/processor/acp/runner/AcpRunner"` | +| `src/processor.ts:15` | `"pixso-move/Processor"` | `"@pixso-move/processor/processor"` | + +### 3.3 `pixso-move/server` (pkg `@pixso-move/server`) — 3 (+ vendor dup) +| File:Line | before | after | +|---|---|---| +| `src/config.ts:38` | `"pixso-move/ServerConfig"` | `"@pixso-move/server/config/ServerConfig"` | +| `src/services/nodeStore.ts:32` | `"pixso-move/NodeStore"` | `"@pixso-move/server/services/nodeStore"` | +| `src/services/resultStore.ts:33` | `"pixso-move/ResultStore"` | `"@pixso-move/server/services/resultStore"` | +> `pixso-move/server/src/vendor/NodeSqliteClient.ts:39` carries `"t3/persistence/NodeSqliteClient"` +> (vendored copy) — only fix if the diagnostic flags it for this package. + +### 3.4 `apps/server` (pkg `@ru-code/ru-code`) — 90 +All become `"@ru-code/ru-code//"`. Captured `before` shown where known; +where blank the current literal is at that file:line (replace in place with the `after`). + +**auth/ (6)** +| File:Line | before | after | +|---|---|---| +| `auth/Services/AuthControlPlane.ts:72` | `"t3/AuthControlPlane"` | `"@ru-code/ru-code/auth/Services/AuthControlPlane"` | +| `auth/Services/BootstrapCredentialService.ts:61` | _(current literal)_ | `"@ru-code/ru-code/auth/Services/BootstrapCredentialService"` | +| `auth/Services/ServerAuthPolicy.ts:10` | `"t3/auth/Services/ServerAuthPolicy"` | `"@ru-code/ru-code/auth/Services/ServerAuthPolicy"` | +| `auth/Services/ServerAuth.ts:95` | `"t3/auth/Services/ServerAuth"` | `"@ru-code/ru-code/auth/Services/ServerAuth"` | +| `auth/Services/ServerSecretStore.ts:32` | `"t3/auth/Services/ServerSecretStore"` | `"@ru-code/ru-code/auth/Services/ServerSecretStore"` | +| `auth/Services/SessionCredentialService.ts:91` | _(current literal)_ | `"@ru-code/ru-code/auth/Services/SessionCredentialService"` | + +**checkpointing/ (2)** +| `checkpointing/Services/CheckpointDiffQuery.ts:49` | _(literal)_ | `"@ru-code/ru-code/checkpointing/Services/CheckpointDiffQuery"` | +| `checkpointing/Services/CheckpointStore.ts:100` | `"t3/checkpointing/Services/CheckpointStore"` | `"@ru-code/ru-code/checkpointing/Services/CheckpointStore"` | + +**top-level / config (6)** +| `config.ts:286` | `"t3/config/ServerConfig"` | `"@ru-code/ru-code/config/ServerConfig"` | +| `environment/Services/ServerEnvironment.ts:11` | `"t3/environment/Services/ServerEnvironment"` | `"@ru-code/ru-code/environment/Services/ServerEnvironment"` | +| `keybindings.ts:290` | `"t3/keybindings"` | `"@ru-code/ru-code/keybindings"` | +| `open.ts:151` | `"t3/open"` | `"@ru-code/ru-code/open"` | +| `processRunner.ts:97` | `"t3/processRunner"` | `"@ru-code/ru-code/processRunner"` | +| `shutdownSignal.ts:10` | `"t3/shutdownSignal"` | `"@ru-code/ru-code/shutdownSignal"` | + +**git/ (2)** +| `git/GitManager.ts:91` | `"t3/git/GitManager"` | `"@ru-code/ru-code/git/GitManager"` | +| `git/GitWorkflowService.ts:80` | _(literal)_ | `"@ru-code/ru-code/git/GitWorkflowService"` | + +**orchestration/Services/ (9)** → `"@ru-code/ru-code/orchestration/Services/<…>"` +| File:Line | after (suffix after the common prefix) | +|---|---| +| `CheckpointReactor.ts:39` | `CheckpointReactor` | +| `OrchestrationEngine.ts:70` | `OrchestrationEngine/OrchestrationEngineService` | +| `OrchestrationReactor.ts:32` | `OrchestrationReactor` | +| `ProjectionPipeline.ts:42` | `ProjectionPipeline/OrchestrationProjectionPipeline` | +| `ProjectionSnapshotQuery.ts:149` | `ProjectionSnapshotQuery` | +| `ProviderCommandReactor.ts:41` | `ProviderCommandReactor` | +| `ProviderRuntimeIngestion.ts:41` | `ProviderRuntimeIngestion/ProviderRuntimeIngestionService` | +| `RuntimeReceiptBus.ts:65` | `RuntimeReceiptBus` | +| `ThreadDeletionReactor.ts:38` | `ThreadDeletionReactor` | + +**persistence/Services/ (18)** → `"@ru-code/ru-code/persistence/Services/<…>"` +| File:Line | after (suffix) | +|---|---| +| `AuthPairingLinks.ts:78` | `AuthPairingLinks/AuthPairingLinkRepository` | +| `AuthSessions.ts:95` | `AuthSessions/AuthSessionRepository` | +| `McpBinding.ts:36` | `McpBinding/McpBindingRepository` | +| `McpCatalog.ts:26` | `McpCatalog/McpCatalogRepository` | +| `McpProbeCache.ts:33` | `McpProbeCache/McpProbeCacheRepository` | +| `OrchestrationCommandReceipts.ts:72` | `OrchestrationCommandReceipts/OrchestrationCommandReceiptRepository` | +| `OrchestrationEventStore.ts:71` | `OrchestrationEventStore` | +| `ProjectionCheckpoints.ts:95` | `ProjectionCheckpoints/ProjectionCheckpointRepository` | +| `ProjectionPendingApprovals.ts:93` | `ProjectionPendingApprovals/ProjectionPendingApprovalRepository` | +| `ProjectionProjects.ts:81` | `ProjectionProjects/ProjectionProjectRepository` | +| `ProjectionState.ts:66` | `ProjectionState/ProjectionStateRepository` | +| `ProjectionThreadActivities.ts:84` | `ProjectionThreadActivities/ProjectionThreadActivityRepository` | +| `ProjectionThreadMessages.ts:95` | `ProjectionThreadMessages/ProjectionThreadMessageRepository` | +| `ProjectionThreadProposedPlans.ts:54` | `ProjectionThreadProposedPlans/ProjectionThreadProposedPlanRepository` | +| `ProjectionThreadSessions.ts:78` | `ProjectionThreadSessions/ProjectionThreadSessionRepository` | +| `ProjectionThreads.ts:106` | `ProjectionThreads/ProjectionThreadRepository` | +| `ProjectionTurns.ts:170` | `ProjectionTurns/ProjectionTurnRepository` | +| `ProviderSessionRuntime.ts:92` | `ProviderSessionRuntime/ProviderSessionRuntimeRepository` | + +**project/Services/ (3)** → `"@ru-code/ru-code/project/Services/"` +`ProjectFaviconResolver.ts:30`, `ProjectSetupScriptRunner.ts:44`, `RepositoryIdentityResolver.ts:12` + +**provider/ (10)** → `"@ru-code/ru-code/provider/<…>"` +| `provider/acp/AcpSessionRuntime.ts:156` | `"t3/provider/acp/AcpSessionRuntime"` | `…/provider/acp/AcpSessionRuntime` | +| `provider/Layers/ProviderEventLoggers.ts:53` | _(literal)_ | `…/provider/Layers/ProviderEventLoggers` | +| `provider/providerMaintenanceRunner.ts:61` | _(literal)_ | `…/provider/providerMaintenanceRunner` | +| `provider/Services/ProviderAdapterRegistry.ts:100` | _(literal)_ | `…/provider/Services/ProviderAdapterRegistry` | +| `provider/Services/ProviderInstanceRegistryMutator.ts:52` | _(literal)_ | `…/provider/Services/ProviderInstanceRegistryMutator` | +| `provider/Services/ProviderInstanceRegistry.ts:87` | _(literal)_ | `…/provider/Services/ProviderInstanceRegistry` | +| `provider/Services/ProviderRegistry.ts:80` | `"t3/provider/Services/ProviderRegistry"` | `…/provider/Services/ProviderRegistry` | +| `provider/Services/ProviderService.ts:139` | `"t3/provider/Services/ProviderService"` | `…/provider/Services/ProviderService` | +| `provider/Services/ProviderSessionDirectory.ts:70` | _(literal)_ | `…/provider/Services/ProviderSessionDirectory` | +| `provider/Services/ProviderSessionReaper.ts:15` | _(literal)_ | `…/provider/Services/ProviderSessionReaper` | + +**ru-fork/ (8)** → `"@ru-code/ru-code/ru-fork/<…>"` +| `ru-fork/mcp/McpOverlay.ts:85` | `"ru-fork/mcp/McpOverlay"` | `…/ru-fork/mcp/McpOverlay` | +| `ru-fork/mcp/McpProjectionQuery.ts:32` | _(literal)_ | `…/ru-fork/mcp/McpProjectionQuery` | +| `ru-fork/mcp/McpReactor.ts:73` | `"ru-fork/mcp/McpReactor"` | `…/ru-fork/mcp/McpReactor` | +| `ru-fork/mcp/McpRuntime.ts:32` | `"ru-fork/mcp/McpRuntime"` | `…/ru-fork/mcp/McpRuntime` | +| `ru-fork/mcp/McpSupervisor.ts:215` | `"ru-fork/mcp/McpSupervisor"` | `…/ru-fork/mcp/McpSupervisor` | +| `ru-fork/qwen-transcript/QwenTranscriptService.ts:23` | _(literal)_ | `…/ru-fork/qwen-transcript/QwenTranscriptService` | +| `ru-fork/skills/SkillScannerService.ts:41` | `"ru-fork/SkillScanner"` | `…/ru-fork/skills/SkillScannerService/SkillScanner` | +| `ru-fork/subagents/SubagentScannerService.ts:42` | `"ru-fork/SubagentScanner"` | `…/ru-fork/subagents/SubagentScannerService/SubagentScanner` | + +**server top-level (3)** +| `serverLifecycleEvents.ts:27` | _(literal)_ | `"@ru-code/ru-code/serverLifecycleEvents"` | +| `serverRuntimeStartup.ts:62` | _(literal)_ | `"@ru-code/ru-code/serverRuntimeStartup"` | +| `serverSettings.ts:133` | _(literal)_ | `"@ru-code/ru-code/serverSettings/ServerSettingsService"` | + +**sourceControl/ (5)** → `"@ru-code/ru-code/sourceControl/<…>"` +`GitHubCli.ts:95` (before `"t3/source-control/GitHubCli"`), `SourceControlDiscovery.ts:314`, +`SourceControlProviderRegistry.ts:47`, `SourceControlProvider.ts:102`, +`SourceControlRepositoryService.ts:43` + +**terminal/ (2)** +| `terminal/Services/Manager.ts:135` | `"t3/terminal/Services/Manager/TerminalManager"` | `"@ru-code/ru-code/terminal/Services/Manager/TerminalManager"` | +| `terminal/Services/PTY.ts:59` | `"t3/terminal/Services/PTY/PtyAdapter"` | `"@ru-code/ru-code/terminal/Services/PTY/PtyAdapter"` | + +**textGeneration/ (1)** +| `textGeneration/TextGeneration.ts:120` | `"t3/text-generation/TextGeneration"` | `"@ru-code/ru-code/textGeneration/TextGeneration"` | + +**vcs/ (8)** → `"@ru-code/ru-code/vcs/"` +`GitVcsDriver.ts:221`, `VcsDriverRegistry.ts:37`, `VcsDriver.ts:32`, `VcsProcess.ts:70`, +`VcsProjectConfig.ts:41`, `VcsProvisioningService.ts:20`, `VcsStatusBroadcaster.ts:68` +(befores are the `"t3/vcs/*"` literals) + +**workspace/Services/ (3)** → `"@ru-code/ru-code/workspace/Services/"` +`WorkspaceEntries.ts:71` (before `"t3/workspace/Services/WorkspaceEntries"`), +`WorkspaceFileSystem.ts:51`, `WorkspacePaths.ts:102` + +> Total: 2 + 2 + 3 + 90 = **97**. The exact `after` for every site is the diagnostic's +> "expected key" — the table above is transcribed from it; regenerate with the §3 command +> to confirm before editing. + +--- + +## 4. The real unknowns — "what else might we miss" (must dry-run) + +`deterministicKeys` is the only diagnostic we can fully enumerate today (because LSP 0.86.2 +is currently installed). The following are **knowable only after** installing tsgo and +running it once with t3's full config (§5 Phase 1). t3's own commit `6b3050ee7` touched +**~60+ non-`Services` files** for these reasons: + +1. **Fuller diagnostic set (§1.4)** — `importFromBarrel`, `missingEffectServiceDependency`, + `leakingRequirements`, `anyUnknownInErrorContext`, `unsafeEffectTypeAssertion`, + `instanceOfSchema`, `globalErrorInEffectCatch/Failure`, `unknownInEffectCatch`, + `preferSchemaOverJson`, `schemaSyncInEffect`. Each can flag fork code. **Count: unknown.** +2. **tsgo base strictness** — `typescript-go` is more spec-compliant than `tsc`; expect a + batch of genuine type errors `tsc` silently allowed. **Count: unknown.** +3. **Test files** — same diagnostics apply to `*.test.ts` / `*.integration.ts`. + +➡️ The plan therefore **cannot** promise a fixed file count up front. Phase 1 produces the +exact, complete error inventory; the migration is "drive that inventory to zero." + +--- + +## 5. Execution phases & validation gates + +- **Phase 0 — feasibility:** add `@effect/tsgo` to catalog/root only, `pnpm install`, + run `pnpm exec tsgo --version` (or `effect-tsgo --version`). **If tsgo won't run on + linux/arm64 here → STOP, abandon migration, keep `tsc` + language-service `0.84.2`.** +- **Phase 1 — discovery:** apply §1 fully (scripts + tsconfig), `pnpm install`, run + `pnpm exec turbo run typecheck --continue 2>&1 | tee /tmp/tsgo-errors.txt`. This is the + **complete** error inventory (deterministicKeys + §4 unknowns + strictness). +- **Phase 2 — keys:** apply §3 (97 key rewrites). +- **Phase 3 — remainder:** drive the §4 errors to zero (group by diagnostic; many are + mechanical, e.g. `importFromBarrel` = change a barrel import to a deep import). +- **Phase 4 — gates:** `pnpm typecheck` (now tsgo) green; `pnpm lint` green; + `pnpm test:fast` green (minus the known-env failures: mcp-probe w/o qwen, `bin.test.ts`). + +--- + +## 6. Risks / things to verify before committing + +1. **tsgo runnability** on linux/arm64 (Phase 0) — hard gate. +2. **Service-identity rename `t3/*`/`ru-fork/*`/`pixso-move/*` → deterministic** — these + are in-memory Context (DI) identity strings, **not** persisted/wire data, so renaming + does **not** break user data on upgrade. **VERIFIED** (2026-06-21): each old key + literal appears *only* at its own `Context.Service(...)` declaration — nowhere in DB + columns, migrations, RPC tags, the MCP overlay file, or any payload. (Re-run the grep + before committing if code changed.) Note: MCP now ships and has backcompat obligations, + but those apply to **Schema `_tag`s and persisted encodings — not these DI keys.** See + [[mcp-not-shipped-no-backcompat]]. +3. **`effect-tsgo patch` prepare hook** — confirm it provides the `tsgo` binary the + scripts call, and that it composes with our existing patches (effect `beta.78` patch). +4. **oxlint** — our `lint` is oxlint (independent of TS plugin); confirm it doesn't import + anything from `@effect/language-service`. The custom `oxlint-plugin-t3code` typecheck + also moves to tsgo. +5. **IDE** — anyone relying on the `@effect/language-service` editor plugin keeps it as a + *devDependency for the editor* even though CI uses tsgo (t3 keeps the plugin name in + tsconfig precisely so both work). Decide whether to keep `@effect/language-service` as a + dev-only dep for in-editor diagnostics, or rely solely on tsgo. +6. **Decouplable fallback:** if Phase 3 balloons, ship the effect `beta.78` bump on + `tsc` + language-service `0.84.2` first (fully green today) and land tsgo separately. + +--- + +## 7. Summary answer to "are we missing anything?" + +- **Fully known now:** the toolchain swap (§1) and the **97** key fixes (§3). +- **Genuinely unknown until a tsgo dry-run:** how many *additional* violations the fuller + diagnostic set (§1.4) and tsgo's stricter base checker (§4) produce — t3 needed ~60+ + extra files for these. We are **not** missing a category; we are missing a *count*, and + Phase 1 supplies it. +- **Biggest hidden risk:** tsgo failing to run on this host (Phase 0), and the + service-key identity rename touching anything persisted/wire (Risk 2). Both are checked + before any large edit.