diff --git a/apps/mobile/package.json b/apps/mobile/package.json index 35fec0c2769a..08307ad65c2a 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -70,6 +70,7 @@ "@t3tools/mobile-review-diff-native": "file:./modules/t3-review-diff", "@t3tools/mobile-terminal-native": "file:./modules/t3-terminal", "@t3tools/provider-core": "workspace:*", + "@t3tools/provider-muse": "workspace:*", "@t3tools/provider-pi": "workspace:*", "@t3tools/shared": "workspace:*", "@tabler/icons-react-native": "^3.44.0", diff --git a/apps/mobile/src/components/ProviderIcon.tsx b/apps/mobile/src/components/ProviderIcon.tsx index 1675b15461d0..0645e6739818 100644 --- a/apps/mobile/src/components/ProviderIcon.tsx +++ b/apps/mobile/src/components/ProviderIcon.tsx @@ -120,18 +120,6 @@ export function ProviderIcon(props: ProviderIconProps) { ); } - if (props.provider === "muse") { - return ( - - - - ); - } - const packageIcon = getProviderClient(props.provider)?.icon; if (packageIcon) { const themeFill = (fill: { readonly light: string; readonly dark: string }) => diff --git a/apps/mobile/src/lib/providerClients.ts b/apps/mobile/src/lib/providerClients.ts index c8c1c05c0158..2d3ec1224992 100644 --- a/apps/mobile/src/lib/providerClients.ts +++ b/apps/mobile/src/lib/providerClients.ts @@ -1,9 +1,10 @@ import { isProviderDriverKind } from "@t3tools/contracts"; import { makeProviderClientRegistry } from "@t3tools/provider-core/client"; +import { museClient } from "@t3tools/provider-muse/client"; import { piClient } from "@t3tools/provider-pi/client"; /** The provider client definitions this mobile build ships. */ -const providerClients = makeProviderClientRegistry([piClient]); +const providerClients = makeProviderClientRegistry([museClient, piClient]); /** The client definition for a driver kind, or `undefined` for drivers drawn by hand. */ export function getProviderClient(driver: string | null | undefined) { diff --git a/apps/server/package.json b/apps/server/package.json index 7672fbf534e3..bb776149e4cd 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -42,6 +42,7 @@ "@opencode/protocol": "2.0.23", "@opencode/schema": "2.0.23", "@t3tools/provider-core": "workspace:*", + "@t3tools/provider-muse": "workspace:*", "@t3tools/provider-pi": "workspace:*", "diff": "8.0.3", "effect": "catalog:", diff --git a/apps/server/scripts/record-muse-msp-replay-fixture.ts b/apps/server/scripts/record-muse-msp-replay-fixture.ts index dde469487be0..a3cbfc6f9d14 100644 --- a/apps/server/scripts/record-muse-msp-replay-fixture.ts +++ b/apps/server/scripts/record-muse-msp-replay-fixture.ts @@ -33,8 +33,11 @@ import { MuseOrchestratorReplayHarness, } from "../src/orchestration-v2/Adapters/MuseAdapterV2.testkit.ts"; import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; -import { parseMuseVersion } from "../src/provider/museMaintenance.ts"; -import { makeMuseEnvironment, museServeArgs } from "../src/provider/museSdk.ts"; +import { + makeMuseEnvironment, + museServeArgs, + parseMuseVersion, +} from "@t3tools/provider-muse/testing"; import { provideDeterministicTestRuntime } from "../src/orchestration-v2/testkit/DeterministicRuntime.ts"; import { ORCHESTRATOR_REPLAY_FIXTURES } from "../src/orchestration-v2/testkit/fixtures/index.ts"; import { materializeFixtureInput } from "../src/orchestration-v2/testkit/fixtures/shared.ts"; diff --git a/apps/server/src/orchestration-v2/Adapters/MuseAdapterV2.testkit.ts b/apps/server/src/orchestration-v2/Adapters/MuseAdapterV2.testkit.ts index cce14853f6fd..f36d2c5959f5 100644 --- a/apps/server/src/orchestration-v2/Adapters/MuseAdapterV2.testkit.ts +++ b/apps/server/src/orchestration-v2/Adapters/MuseAdapterV2.testkit.ts @@ -16,33 +16,31 @@ import * as NodeServices from "@effect/platform-node/NodeServices"; import { Connection, type DuplexTransport } from "@muse-code/sdk"; import { - MuseSettings, ProviderDriverKind, ProviderInstanceId, ProviderReplayEntry, type ProviderReplayEntry as ProviderReplayEntryType, type ProviderReplayTranscript, } from "@t3tools/contracts"; +import { MuseSettings } from "@t3tools/provider-muse/settings"; import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; import * as Layer from "effect/Layer"; import * as Schema from "effect/Schema"; -import * as ServerConfig from "../../config.ts"; +import { ProviderHost } from "@t3tools/provider-core/server/ProviderHost"; +import { layerTestProviderHost } from "@t3tools/provider-testing/host"; import { museInitializeParams, museServeArgs, type MuseSdkHost, type MuseSdkHostOptions, -} from "../../provider/museSdk.ts"; +} from "@t3tools/provider-muse/testing"; import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts"; import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests"; -import { - makeReplayServerConfig, - type OrchestratorV2ProviderReplayHarness, -} from "../testkit/ProviderReplayHarness.ts"; -import { makeMuseAdapterV2 } from "./MuseAdapterV2.ts"; +import type { OrchestratorV2ProviderReplayHarness } from "../testkit/ProviderReplayHarness.ts"; +import { makeMuseAdapterV2 } from "@t3tools/provider-muse/server"; export const MUSE_PROVIDER_KIND = "muse"; export const MUSE_MSP_REPLAY_PROTOCOL = "muse.msp-jsonl"; @@ -448,10 +446,6 @@ export function layer(input: { /** Replay needs none; a live recording passes the host environment. */ readonly environment?: NodeJS.ProcessEnv; }) { - const layerServerConfig = Layer.effect( - ServerConfig.ServerConfig, - makeReplayServerConfig(`muse-${input.scenario}`).pipe(Effect.orDie), - ).pipe(Layer.provide(NodeServices.layer)); return ProviderAdapterRegistry.layerFromAdaptersEffect( Effect.gen(function* () { return [ @@ -460,7 +454,7 @@ export function layer(input: { settings: DEFAULT_MUSE_SETTINGS, environment: input.environment ?? {}, idAllocator: yield* IdAllocator.IdAllocatorV2, - serverConfig: yield* ServerConfig.ServerConfig, + host: yield* ProviderHost, fileSystem: yield* FileSystem.FileSystem, createHost: input.createHost, // Same queue the continuation worker drains when the fixture runs it. @@ -468,7 +462,15 @@ export function layer(input: { }), ]; }), - ).pipe(Layer.provide(Layer.mergeAll(layerServerConfig, NodeServices.layer, IdAllocator.layer))); + ).pipe( + Layer.provide( + Layer.mergeAll( + layerTestProviderHost().pipe(Layer.provide(NodeServices.layer)), + NodeServices.layer, + IdAllocator.layer, + ), + ), + ); } export const MuseOrchestratorReplayHarness: OrchestratorV2ProviderReplayHarness< diff --git a/apps/server/src/provider/GrokProvider.test.ts b/apps/server/src/provider/GrokProvider.test.ts index f77ee10465e2..000176e9bb23 100644 --- a/apps/server/src/provider/GrokProvider.test.ts +++ b/apps/server/src/provider/GrokProvider.test.ts @@ -18,7 +18,7 @@ import { grokSlashCommandsFromInitialize, parseGrokModelsCliOutput, } from "./GrokProvider.ts"; -import { execScriptSource, writeFakeCli } from "../testUtils/fakeCli.ts"; +import { execScriptSource, writeFakeCli } from "@t3tools/provider-testing/fakeCli"; import { grokUsageResponseToLimits, readGrokAccount } from "./grokUsageLimits.ts"; const decodeGrokSettings = Schema.decodeSync(GrokSettings); diff --git a/apps/server/src/provider/builtInDrivers.ts b/apps/server/src/provider/builtInDrivers.ts index 08a403b70e04..1690a998c5f4 100644 --- a/apps/server/src/provider/builtInDrivers.ts +++ b/apps/server/src/provider/builtInDrivers.ts @@ -27,7 +27,7 @@ import { CodexDriver, type CodexDriverEnv } from "./Drivers/CodexDriver.ts"; import { CursorDriver, type CursorDriverEnv } from "./Drivers/CursorDriver.ts"; import { GrokDriver, type GrokDriverEnv } from "./Drivers/GrokDriver.ts"; import { OpenCodeDriver, type OpenCodeDriverEnv } from "./Drivers/OpenCodeDriver.ts"; -import { MuseDriver, type MuseDriverEnv } from "./Drivers/MuseDriver.ts"; +import { MuseDriver, type MuseDriverEnv } from "@t3tools/provider-muse/server"; import { PiDriver, type PiDriverEnv } from "@t3tools/provider-pi/server"; import type { AnyProviderDriver } from "@t3tools/provider-core/server/driver"; diff --git a/apps/server/src/textGeneration/ClaudeTextGeneration.test.ts b/apps/server/src/textGeneration/ClaudeTextGeneration.test.ts index 46e015123df2..29c0f950f7c8 100644 --- a/apps/server/src/textGeneration/ClaudeTextGeneration.test.ts +++ b/apps/server/src/textGeneration/ClaudeTextGeneration.test.ts @@ -21,7 +21,7 @@ import { import * as TextGeneration from "./TextGeneration.ts"; import { sanitizeThreadTitle } from "@t3tools/provider-core/server/textGenerationUtils"; import { makeClaudeTextGeneration } from "./ClaudeTextGeneration.ts"; -import { writeFakeCli } from "../testUtils/fakeCli.ts"; +import { writeFakeCli } from "@t3tools/provider-testing/fakeCli"; const decodeClaudeSettings = Schema.decodeSync(ClaudeSettings); const layerClaudeTextGenerationTest = ServerConfig.ServerConfig.layerTest(process.cwd(), { diff --git a/apps/server/src/textGeneration/CodexTextGeneration.test.ts b/apps/server/src/textGeneration/CodexTextGeneration.test.ts index fbfbe909f962..e991ddf17fdf 100644 --- a/apps/server/src/textGeneration/CodexTextGeneration.test.ts +++ b/apps/server/src/textGeneration/CodexTextGeneration.test.ts @@ -16,7 +16,7 @@ import { CodexSettings, ProviderInstanceId, TextGenerationError } from "@t3tools import * as ServerConfig from "../config.ts"; import * as TextGeneration from "./TextGeneration.ts"; import { makeCodexTextGeneration } from "./CodexTextGeneration.ts"; -import { writeFakeCli } from "../testUtils/fakeCli.ts"; +import { writeFakeCli } from "@t3tools/provider-testing/fakeCli"; const decodeCodexSettings = Schema.decodeSync(CodexSettings); const DEFAULT_TEST_MODEL_SELECTION = createModelSelection( diff --git a/apps/server/src/textGeneration/GrokTextGeneration.test.ts b/apps/server/src/textGeneration/GrokTextGeneration.test.ts index a577065913ab..8165683f62cf 100644 --- a/apps/server/src/textGeneration/GrokTextGeneration.test.ts +++ b/apps/server/src/textGeneration/GrokTextGeneration.test.ts @@ -16,7 +16,7 @@ import { GrokSettings, ProviderInstanceId } from "@t3tools/contracts"; import * as ServerConfig from "../config.ts"; import * as TextGeneration from "./TextGeneration.ts"; import { makeGrokTextGeneration } from "./GrokTextGeneration.ts"; -import { execScriptSource, writeFakeCli } from "../testUtils/fakeCli.ts"; +import { execScriptSource, writeFakeCli } from "@t3tools/provider-testing/fakeCli"; const decodeGrokSettings = Schema.decodeSync(GrokSettings); const __dirname = NodePath.dirname(NodeURL.fileURLToPath(import.meta.url)); diff --git a/apps/web/package.json b/apps/web/package.json index ab2ae7b35ea3..63a5f5c2ad35 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -29,6 +29,7 @@ "@t3tools/client-runtime": "workspace:*", "@t3tools/contracts": "workspace:*", "@t3tools/provider-core": "workspace:*", + "@t3tools/provider-muse": "workspace:*", "@t3tools/provider-pi": "workspace:*", "@t3tools/shared": "workspace:*", "@tanstack/react-pacer": "^0.19.4", diff --git a/apps/web/src/components/Icons.tsx b/apps/web/src/components/Icons.tsx index 66e170dacf61..7f38d65f2a14 100644 --- a/apps/web/src/components/Icons.tsx +++ b/apps/web/src/components/Icons.tsx @@ -815,13 +815,3 @@ export const ComputerUseAppIcon: Icon = (props) => { ); }; - -export const MuseIcon: Icon = (props) => ( - - - -); diff --git a/apps/web/src/components/chat/ProviderInstanceIcon.tsx b/apps/web/src/components/chat/ProviderInstanceIcon.tsx index ddfffd4fa9d3..e9e54575c910 100644 --- a/apps/web/src/components/chat/ProviderInstanceIcon.tsx +++ b/apps/web/src/components/chat/ProviderInstanceIcon.tsx @@ -8,7 +8,6 @@ import { ClaudeAI, CursorIcon, GrokIcon, - MuseIcon, Icon, OpenAI, OpenCodeIcon, @@ -29,7 +28,6 @@ const PROVIDER_ICON_BY_PROVIDER: Partial> = { [ProviderDriverKind.make("opencode")]: OpenCodeIcon, [ProviderDriverKind.make("cursor")]: CursorIcon, [ProviderDriverKind.make("grok")]: GrokIcon, - [ProviderDriverKind.make("muse")]: MuseIcon, [ProviderDriverKind.make("antigravity")]: AntigravityIcon, }; diff --git a/apps/web/src/components/settings/providerDriverMeta.ts b/apps/web/src/components/settings/providerDriverMeta.ts index 59b42d0f6751..c967100888c8 100644 --- a/apps/web/src/components/settings/providerDriverMeta.ts +++ b/apps/web/src/components/settings/providerDriverMeta.ts @@ -6,10 +6,10 @@ import { CursorSettings, GrokSettings, OpenCodeSettings, - MuseSettings, ProviderDriverKind, } from "@t3tools/contracts"; import { makeProviderClientRegistry } from "@t3tools/provider-core/client"; +import { museClient } from "@t3tools/provider-muse/client"; import { piClient } from "@t3tools/provider-pi/client"; /** The provider client definitions this web build ships, in presentation order. */ @@ -53,12 +53,7 @@ export const providerClients = makeProviderClientRegistry([ label: "Antigravity", settingsSchema: AntigravitySettings, }, - { - driverKind: ProviderDriverKind.make("muse"), - label: "Muse Code", - settingsSchema: MuseSettings, - badgeLabel: "Beta", - }, + museClient, piClient, { driverKind: ProviderDriverKind.make("acpRegistry"), diff --git a/package.json b/package.json index eece70e89ef5..a345b307273d 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "tc": "vp run -r --concurrency-limit 2 typecheck", "lint": "vp lint --report-unused-disable-directives", "knip": "knip --preprocessor ./scripts/knip-schemas.ts", - "knip:check": "knip --include files,dependencies --no-config-hints && knip --workspace apps/server --workspace apps/desktop --workspace apps/web --workspace packages/client-runtime --workspace packages/contracts --workspace packages/effect-acp --workspace packages/effect-codex-app-server --workspace packages/shared --workspace packages/ssh --workspace packages/tailscale --workspace packages/provider-core --workspace packages/provider-testing --workspace packages/provider-pi --exports --preprocessor ./scripts/knip-schemas.ts --no-config-hints", + "knip:check": "knip --include files,dependencies --no-config-hints && knip --workspace apps/server --workspace apps/desktop --workspace apps/web --workspace packages/client-runtime --workspace packages/contracts --workspace packages/effect-acp --workspace packages/effect-codex-app-server --workspace packages/shared --workspace packages/ssh --workspace packages/tailscale --workspace packages/provider-core --workspace packages/provider-testing --workspace packages/provider-pi --workspace packages/provider-muse --exports --preprocessor ./scripts/knip-schemas.ts --no-config-hints", "knip:production": "knip --production --preprocessor ./scripts/knip-schemas.ts", "lint:mobile": "node scripts/mobile-native-static-check.ts", "test": "vp run -r test", diff --git a/packages/contracts/src/settings.ts b/packages/contracts/src/settings.ts index c9b3c00136dc..e62f4ff578b7 100644 --- a/packages/contracts/src/settings.ts +++ b/packages/contracts/src/settings.ts @@ -840,29 +840,6 @@ export const AntigravitySettings = makeProviderSettingsSchema( ); export type AntigravitySettings = typeof AntigravitySettings.Type; -export const MuseSettings = makeProviderSettingsSchema( - { - enabled: Schema.Boolean.pipe( - Schema.withDecodingDefault(Effect.succeed(false)), - Schema.annotateKey({ providerSettingsForm: { hidden: true } }), - ), - binaryPath: makeBinaryPathSetting("muse").pipe( - Schema.annotateKey({ - title: "Binary path", - description: - "Path to the Muse Code binary on this environment. Sign in with muse login on that host.", - providerSettingsForm: { placeholder: "muse", clearWhenEmpty: "omit" }, - }), - ), - customModels: Schema.Array(CustomModelSetting).pipe( - Schema.withDecodingDefault(Effect.succeed([])), - Schema.annotateKey({ providerSettingsForm: { hidden: true } }), - ), - }, - { order: ["binaryPath"] }, -); -export type MuseSettings = typeof MuseSettings.Type; - export const AcpRegistryDistributionPreference = Schema.Literals(["auto", "binary", "npx", "uvx"]); export type AcpRegistryDistributionPreference = typeof AcpRegistryDistributionPreference.Type; diff --git a/packages/provider-muse/package.json b/packages/provider-muse/package.json new file mode 100644 index 000000000000..a59c6cd20c8d --- /dev/null +++ b/packages/provider-muse/package.json @@ -0,0 +1,41 @@ +{ + "name": "@t3tools/provider-muse", + "private": true, + "type": "module", + "exports": { + "./client": { + "types": "./src/client.ts", + "import": "./src/client.ts" + }, + "./settings": { + "types": "./src/settings.ts", + "import": "./src/settings.ts" + }, + "./server": { + "types": "./src/server.ts", + "import": "./src/server.ts" + }, + "./testing": { + "types": "./src/testing.ts", + "import": "./src/testing.ts" + } + }, + "scripts": { + "typecheck": "tsc --noEmit", + "test": "vp test run --config ../../vite.config.ts --dir ." + }, + "dependencies": { + "@muse-code/sdk": "1.4.2", + "@t3tools/contracts": "workspace:*", + "@t3tools/provider-core": "workspace:*", + "@t3tools/shared": "workspace:*", + "effect": "catalog:" + }, + "devDependencies": { + "@effect/platform-node": "catalog:", + "@effect/vitest": "catalog:", + "@t3tools/provider-testing": "workspace:*", + "@types/node": "catalog:", + "vite-plus": "catalog:" + } +} diff --git a/packages/provider-muse/src/client.ts b/packages/provider-muse/src/client.ts new file mode 100644 index 000000000000..d709e8689395 --- /dev/null +++ b/packages/provider-muse/src/client.ts @@ -0,0 +1,27 @@ +/** + * Muse Code's client definition: settings form, label, badge, and glyph. + * Browser- and React Native-safe. + * + * @module provider-muse/client + */ +import { ProviderDriverKind } from "@t3tools/contracts"; +import { defineProviderClient } from "@t3tools/provider-core/client"; + +import { MuseSettings } from "./settings.ts"; + +export const museClient = defineProviderClient({ + driverKind: ProviderDriverKind.make("muse"), + label: "Muse Code", + settingsSchema: MuseSettings, + badgeLabel: "Beta", + icon: { + viewBox: "0 0 150.4828 100", + fill: { light: "#171717", dark: "#e5e5e5" }, + paths: [ + { + d: "M107.9999847,0.0000013c-12.3391113,0-21.984787,9.2935991-30.7164001,21.0993118 C65.2847366,5.821527,55.2499847,0,43.2413788,0C18.7586193,0,0,31.8620682,0,65.5862122 C0,86.6896515,10.2095623,100,27.3103466,100c12.3080502,0,21.1600361-5.8025894,36.8965454-33.3103409 c0,0,6.5597382-11.5841255,11.0725021-19.563858c1.5813522,2.5531845,3.2431107,5.3001404,4.9964676,8.2535095 l7.379303,12.4137955C102.0298843,91.8478851,110.039032,100,124.5517273,100 c16.6595764,0,25.9310303-13.4923325,25.9310303-35.0344849 C150.4827576,29.6551723,131.301178,0.0000013,107.9999847,0.0000013z M52.2068977,59.2413788 c-12.7586212,20-17.1724167,24.4827576-24.2758656,24.4827576c-7.3103428,0-11.6551704-6.4178314-11.6551704-17.8620682 c0-24.4827576,12.2068977-49.5172424,26.7586231-49.5172424c7.880127,0,14.4653816,4.5510426,24.5522308,18.9913158 C58.0087967,50.0272293,52.2068977,59.2413788,52.2068977,59.2413788z M100.3597336,56.7235756L91.5367355,42.00877 c-2.3876724-3.8832092-4.6757278-7.451458-6.8838348-10.7230263 c7.951973-12.273531,14.5113449-18.3891907,22.3126144-18.3891907c16.2069016,0,29.1724243,23.8620682,29.1724243,53.1724091 c0,11.1724167-3.6593475,17.6551743-11.2413864,17.6551743 C117.6294937,83.7241364,114.1578674,78.924736,100.3597336,56.7235756z", + fillRule: "evenodd", + }, + ], + }, +}); diff --git a/packages/provider-muse/src/server.ts b/packages/provider-muse/src/server.ts new file mode 100644 index 000000000000..7100802d7e13 --- /dev/null +++ b/packages/provider-muse/src/server.ts @@ -0,0 +1,8 @@ +/** + * Muse Code's server entry: the driver the server registers, plus the + * adapter factory the replay testkit and fixture recorder build on. + * + * @module provider-muse/server + */ +export { MuseDriver, type MuseDriverEnv } from "./server/driver.ts"; +export { makeMuseAdapterV2, type MuseAdapterV2Options } from "./server/adapter.ts"; diff --git a/apps/server/src/orchestration-v2/Adapters/MuseAdapterV2.test.ts b/packages/provider-muse/src/server/adapter.test.ts similarity index 98% rename from apps/server/src/orchestration-v2/Adapters/MuseAdapterV2.test.ts rename to packages/provider-muse/src/server/adapter.test.ts index 77e12726a183..59f8adec4026 100644 --- a/apps/server/src/orchestration-v2/Adapters/MuseAdapterV2.test.ts +++ b/packages/provider-muse/src/server/adapter.test.ts @@ -5,7 +5,6 @@ import { MUSE_DEFAULT_MODEL, EnvironmentId, MessageId, - MuseSettings, NodeId, ProjectId, ProviderInstanceId, @@ -19,6 +18,7 @@ import { type OrchestrationV2AppThread, type OrchestrationV2ProviderThread, } from "@t3tools/contracts"; +import { MuseSettings } from "../settings.ts"; import * as DateTime from "effect/DateTime"; import * as Deferred from "effect/Deferred"; import * as Effect from "effect/Effect"; @@ -31,10 +31,11 @@ import * as Queue from "effect/Queue"; import * as Schema from "effect/Schema"; import * as Stream from "effect/Stream"; -import * as ServerConfig from "../../config.ts"; +import { ProviderHost } from "@t3tools/provider-core/server/ProviderHost"; +import { layerTestProviderHost } from "@t3tools/provider-testing/host"; import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession"; -import type { MuseItem } from "../../provider/museProtocol.ts"; -import type { MuseSdkHost } from "../../provider/museSdk.ts"; +import type { MuseItem } from "./protocol.ts"; +import type { MuseSdkHost } from "./sdk.ts"; import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; import { ProviderAdapterV2RuntimePolicy, @@ -42,14 +43,12 @@ import { type ProviderAdapterV2TurnInput, } from "@t3tools/provider-core/server/ProviderAdapter"; import type { ProviderContinuationRequest } from "@t3tools/provider-core/server/continuationRequests"; -import { makeMuseAdapterV2, type MuseAdapterV2Options } from "./MuseAdapterV2.ts"; +import { makeMuseAdapterV2, type MuseAdapterV2Options } from "./adapter.ts"; const testLayer = Layer.mergeAll( NodeServices.layer, IdAllocator.layer, - ServerConfig.layerTest(process.cwd(), { prefix: "t3-muse-v2-adapter-" }).pipe( - Layer.provide(NodeServices.layer), - ), + layerTestProviderHost().pipe(Layer.provide(NodeServices.layer)), ); const MUSE_PROVIDER = ProviderDriverKind.make("muse"); const INSTANCE_ID = ProviderInstanceId.make("muse_work"); @@ -205,7 +204,7 @@ const makeHarness = Effect.fnUntraced(function* ( settings: museSettings, environment: { PATH: "/fake/bin" }, idAllocator: yield* IdAllocator.IdAllocatorV2, - serverConfig: yield* ServerConfig.ServerConfig, + host: yield* ProviderHost, fileSystem: yield* FileSystem.FileSystem, createHost: async () => (hostCount++ === 0 ? fake.host : (replacement ?? fake).host), ...overrides, @@ -437,7 +436,7 @@ describe("MuseAdapterV2", () => { settings: museSettings, environment: { PATH: "/fake/bin" }, idAllocator: yield* IdAllocator.IdAllocatorV2, - serverConfig: yield* ServerConfig.ServerConfig, + host: yield* ProviderHost, fileSystem: yield* FileSystem.FileSystem, createHost: async () => fake.host, }); diff --git a/apps/server/src/orchestration-v2/Adapters/MuseAdapterV2.ts b/packages/provider-muse/src/server/adapter.ts similarity index 98% rename from apps/server/src/orchestration-v2/Adapters/MuseAdapterV2.ts rename to packages/provider-muse/src/server/adapter.ts index 30d7073463a7..bf1699055534 100644 --- a/apps/server/src/orchestration-v2/Adapters/MuseAdapterV2.ts +++ b/packages/provider-muse/src/server/adapter.ts @@ -1,7 +1,6 @@ import { MspError, type SendUserTurnOptions } from "@muse-code/sdk"; import { MUSE_DEFAULT_MODEL, - type MuseSettings, ProviderDriverKind, type ModelSelection, type OrchestrationV2ConversationMessage, @@ -22,6 +21,7 @@ import { type RuntimeRequestId, type ServerProviderModel, } from "@t3tools/contracts"; +import type { MuseSettings } from "../settings.ts"; import { getModelSelectionStringOptionValue } from "@t3tools/shared/model"; import * as Cause from "effect/Cause"; import * as DateTime from "effect/DateTime"; @@ -35,14 +35,10 @@ import * as Semaphore from "effect/Semaphore"; import * as Scope from "effect/Scope"; import * as Stream from "effect/Stream"; -import { resolveAttachmentPath } from "../../attachmentStore.ts"; -import * as ServerConfig from "../../config.ts"; +import type { ProviderHostShape } from "@t3tools/provider-core/server/ProviderHost"; import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession"; import { buildRuntimeInstructions } from "@t3tools/provider-core/server/runtimeInstructions"; -import { - museModelCapabilities, - resolveMuseReasoningEffort, -} from "../../provider/museModelCatalog.ts"; +import { museModelCapabilities, resolveMuseReasoningEffort } from "./modelCatalog.ts"; import { MuseApproval, MuseCompactResult, @@ -60,14 +56,14 @@ import { museApprovalDecision, museApprovalOptions, type MuseItem, -} from "../../provider/museProtocol.ts"; +} from "./protocol.ts"; import { createMuseSdkHostEffect, museApprovalMode, type createMuseSdkHost, type MuseSdkHost, -} from "../../provider/museSdk.ts"; -import type { EventNdjsonLogger } from "../../provider/EventNdjsonLogger.ts"; +} from "./sdk.ts"; +import type { EventNdjsonLogger } from "@t3tools/provider-core/server/ProviderEventLoggers"; import { providerMessageTextWithAttachmentPaths, isProviderNativeImageAttachment, @@ -103,7 +99,7 @@ import { import type * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests"; import { makeProviderFailure } from "@t3tools/provider-core/server/failure"; import { turnScopedSelectionTransition } from "@t3tools/provider-core/server/selectionTransition"; -import { museItemStatus, museToolPresentation } from "./MuseItemPresentation.ts"; +import { museItemStatus, museToolPresentation } from "./itemPresentation.ts"; const MUSE_PROVIDER = ProviderDriverKind.make("muse"); const isOpenSessionError = Schema.is(ProviderAdapterOpenSessionError); @@ -208,7 +204,7 @@ export interface MuseAdapterV2Options { readonly settings: MuseSettings; readonly environment: NodeJS.ProcessEnv; readonly idAllocator: IdAllocator.IdAllocatorV2["Service"]; - readonly serverConfig: ServerConfig.ServerConfig["Service"]; + readonly host: ProviderHostShape; readonly fileSystem: FileSystem.FileSystem; readonly modelCatalog?: Effect.Effect>; readonly createHost?: typeof createMuseSdkHost; @@ -293,7 +289,7 @@ export function makeMuseAdapterV2(options: MuseAdapterV2Options): ProviderAdapte const scope = yield* Effect.scope; // Muse rejects non-canonical workspace roots (for example macOS /tmp) and // reports canonical paths in approvals, so resolve symlinks once here. - const requestedCwd = input.runtimePolicy.cwd ?? options.serverConfig.cwd; + const requestedCwd = input.runtimePolicy.cwd ?? options.host.paths.cwd; const cwd = yield* options.fileSystem .realPath(requestedCwd) .pipe(Effect.orElseSucceed(() => requestedCwd)); @@ -1601,19 +1597,12 @@ export function makeMuseAdapterV2(options: MuseAdapterV2Options): ProviderAdapte const text = providerMessageTextWithAttachmentPaths({ text: message.text, attachments: message.attachments, - resolveAttachmentPath: (attachment) => - resolveAttachmentPath({ - attachmentsDir: options.serverConfig.attachmentsDir, - attachment, - }), + resolveAttachmentPath: options.host.resolveAttachmentPath, }); if (text) parts.push({ type: "text", text }); for (const attachment of message.attachments) if (isProviderNativeImageAttachment(attachment)) { - const path = resolveAttachmentPath({ - attachmentsDir: options.serverConfig.attachmentsDir, - attachment, - }); + const path = options.host.resolveAttachmentPath(attachment); if (!path) return yield* protocolError("Muse image attachment is missing"); const bytes = yield* options.fileSystem .readFile(path) diff --git a/apps/server/src/provider/Drivers/MuseDriver.ts b/packages/provider-muse/src/server/driver.ts similarity index 91% rename from apps/server/src/provider/Drivers/MuseDriver.ts rename to packages/provider-muse/src/server/driver.ts index 16bed1c3dab2..87f6b2818907 100644 --- a/apps/server/src/provider/Drivers/MuseDriver.ts +++ b/packages/provider-muse/src/server/driver.ts @@ -1,4 +1,5 @@ -import { MuseSettings, ProviderDriverKind } from "@t3tools/contracts"; +import { ProviderDriverKind } from "@t3tools/contracts"; +import { MuseSettings } from "../settings.ts"; import { HostProcessEnvironment } from "@t3tools/shared/hostProcess"; import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; @@ -8,18 +9,17 @@ import { HttpClient } from "effect/http"; import { ChildProcessSpawner } from "effect/process"; import { ProviderHost } from "@t3tools/provider-core/server/ProviderHost"; -import * as ServerConfig from "../../config.ts"; import { expandHomePath } from "@t3tools/provider-core/server/pathExpansion"; -import { makeMuseTextGeneration } from "../../textGeneration/MuseTextGeneration.ts"; -import { ProviderDriverError } from "../Errors.ts"; -import { makeMuseAdapterV2 } from "../../orchestration-v2/Adapters/MuseAdapterV2.ts"; +import { makeMuseTextGeneration } from "./textGeneration.ts"; +import { ProviderDriverError } from "@t3tools/provider-core/server/errors"; +import { makeMuseAdapterV2 } from "./adapter.ts"; import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests"; -import { checkMuseProviderStatus, makePendingMuseProvider } from "../MuseProvider.ts"; -import * as ProviderEventLoggers from "../ProviderEventLoggers.ts"; +import { checkMuseProviderStatus, makePendingMuseProvider } from "./status.ts"; +import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; import { makeManagedServerProvider } from "@t3tools/provider-core/server/managedProvider"; -import { enrichMuseSnapshot, latestMuseVersion, museMaintenance } from "../museMaintenance.ts"; -import { makeMuseEnvironment } from "../museSdk.ts"; +import { enrichMuseSnapshot, latestMuseVersion, museMaintenance } from "./maintenance.ts"; +import { makeMuseEnvironment } from "./sdk.ts"; import { defaultProviderContinuationIdentity, type ProviderDriver, @@ -47,8 +47,7 @@ export type MuseDriverEnv = | FileSystem.FileSystem | HttpClient.HttpClient | Path.Path - | ProviderEventLoggers.ProviderEventLoggers - | ServerConfig.ServerConfig; + | ProviderEventLoggers.ProviderEventLoggers; export const MuseDriver: ProviderDriver = { driverKind: DRIVER_KIND, @@ -63,8 +62,7 @@ export const MuseDriver: ProviderDriver = { const httpClient = yield* HttpClient.HttpClient; const host = yield* ProviderHost; const eventLoggers = yield* ProviderEventLoggers.ProviderEventLoggers; - const serverConfig = yield* ServerConfig.ServerConfig; - const { cwd } = serverConfig; + const { cwd } = host.paths; const idAllocator = yield* IdAllocator.IdAllocatorV2; const continuationRequests = yield* ProviderContinuationRequests.ProviderContinuationRequests; const hostEnvironment = yield* HostProcessEnvironment; @@ -155,7 +153,7 @@ export const MuseDriver: ProviderDriver = { settings: effectiveConfig, environment: processEnvironment, idAllocator, - serverConfig, + host, fileSystem, modelCatalog, ...(eventLoggers.native ? { nativeEventLogger: eventLoggers.native } : {}), diff --git a/apps/server/src/orchestration-v2/Adapters/MuseItemPresentation.test.ts b/packages/provider-muse/src/server/itemPresentation.test.ts similarity index 94% rename from apps/server/src/orchestration-v2/Adapters/MuseItemPresentation.test.ts rename to packages/provider-muse/src/server/itemPresentation.test.ts index 7e4f489b5b00..59e48db95fd1 100644 --- a/apps/server/src/orchestration-v2/Adapters/MuseItemPresentation.test.ts +++ b/packages/provider-muse/src/server/itemPresentation.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from "@effect/vitest"; -import type { MuseItem } from "../../provider/museProtocol.ts"; -import { museItemStatus, museToolPresentation } from "./MuseItemPresentation.ts"; +import type { MuseItem } from "./protocol.ts"; +import { museItemStatus, museToolPresentation } from "./itemPresentation.ts"; const item = (fields: Partial): MuseItem => ({ itemId: "native-item", diff --git a/apps/server/src/orchestration-v2/Adapters/MuseItemPresentation.ts b/packages/provider-muse/src/server/itemPresentation.ts similarity index 98% rename from apps/server/src/orchestration-v2/Adapters/MuseItemPresentation.ts rename to packages/provider-muse/src/server/itemPresentation.ts index 93df7761a3eb..e05490698bef 100644 --- a/apps/server/src/orchestration-v2/Adapters/MuseItemPresentation.ts +++ b/packages/provider-muse/src/server/itemPresentation.ts @@ -1,7 +1,7 @@ import type { OrchestrationV2TurnItem, OrchestrationV2TurnItemStatus } from "@t3tools/contracts"; import * as Schema from "effect/Schema"; -import type { MuseItem } from "../../provider/museProtocol.ts"; +import type { MuseItem } from "./protocol.ts"; const decodeToolArgs = Schema.decodeUnknownSync( Schema.fromJsonString(Schema.Record(Schema.String, Schema.Unknown)), diff --git a/apps/server/src/provider/museMaintenance.test.ts b/packages/provider-muse/src/server/maintenance.test.ts similarity index 99% rename from apps/server/src/provider/museMaintenance.test.ts rename to packages/provider-muse/src/server/maintenance.test.ts index 59d0868e768d..1ac4a48cae60 100644 --- a/apps/server/src/provider/museMaintenance.test.ts +++ b/packages/provider-muse/src/server/maintenance.test.ts @@ -11,7 +11,7 @@ import { museMaintenance, latestMuseVersion, parseMuseVersion, -} from "./museMaintenance.ts"; +} from "./maintenance.ts"; import { ProviderVersionCache, makeManualOnlyProviderMaintenanceCapabilities, diff --git a/apps/server/src/provider/museMaintenance.ts b/packages/provider-muse/src/server/maintenance.ts similarity index 100% rename from apps/server/src/provider/museMaintenance.ts rename to packages/provider-muse/src/server/maintenance.ts diff --git a/apps/server/src/provider/museModelCatalog.test.ts b/packages/provider-muse/src/server/modelCatalog.test.ts similarity index 96% rename from apps/server/src/provider/museModelCatalog.test.ts rename to packages/provider-muse/src/server/modelCatalog.test.ts index 2679be85e853..628628a161bd 100644 --- a/apps/server/src/provider/museModelCatalog.test.ts +++ b/packages/provider-muse/src/server/modelCatalog.test.ts @@ -1,14 +1,14 @@ import { describe, expect, it } from "@effect/vitest"; -import { MuseSettings } from "@t3tools/contracts"; +import { MuseSettings } from "../settings.ts"; import * as Effect from "effect/Effect"; import * as Schema from "effect/Schema"; -import type { MuseSdkHost } from "./museSdk.ts"; +import type { MuseSdkHost } from "./sdk.ts"; import { discoverMuseModels, museModelCapabilities, resolveMuseReasoningEffort, -} from "./museModelCatalog.ts"; +} from "./modelCatalog.ts"; const settings = Schema.decodeSync(MuseSettings)({}); const host = (catalog: Record): MuseSdkHost => ({ diff --git a/apps/server/src/provider/museModelCatalog.ts b/packages/provider-muse/src/server/modelCatalog.ts similarity index 97% rename from apps/server/src/provider/museModelCatalog.ts rename to packages/provider-muse/src/server/modelCatalog.ts index 7a75cf26f6d5..f390d062803c 100644 --- a/apps/server/src/provider/museModelCatalog.ts +++ b/packages/provider-muse/src/server/modelCatalog.ts @@ -1,9 +1,10 @@ import type { SendUserTurnOptions } from "@muse-code/sdk"; -import type { ModelCapabilities, MuseSettings, ServerProviderModel } from "@t3tools/contracts"; +import { ModelCapabilities, ServerProviderModel } from "@t3tools/contracts"; +import { MuseSettings } from "../settings.ts"; import { createModelCapabilities, getProviderOptionDescriptors } from "@t3tools/shared/model"; import * as Effect from "effect/Effect"; import * as Schema from "effect/Schema"; -import { createMuseSdkHost, createMuseSdkHostEffect, type MuseSdkHost } from "./museSdk.ts"; +import { createMuseSdkHost, createMuseSdkHostEffect, type MuseSdkHost } from "./sdk.ts"; type MuseReasoningEffort = NonNullable["reasoningEffort"]>; diff --git a/apps/server/src/provider/museProtocol.test.ts b/packages/provider-muse/src/server/protocol.test.ts similarity index 91% rename from apps/server/src/provider/museProtocol.test.ts rename to packages/provider-muse/src/server/protocol.test.ts index 479da19a4fdb..d583d84de495 100644 --- a/apps/server/src/provider/museProtocol.test.ts +++ b/packages/provider-muse/src/server/protocol.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vite-plus/test"; -import { museApprovalChoices } from "./museProtocol.ts"; +import { museApprovalChoices } from "./protocol.ts"; // Muse 1.4.3 shell approvals offer only `abort`; the muse_permission replay covers that case. describe("Muse approval choices", () => { diff --git a/apps/server/src/provider/museProtocol.ts b/packages/provider-muse/src/server/protocol.ts similarity index 100% rename from apps/server/src/provider/museProtocol.ts rename to packages/provider-muse/src/server/protocol.ts diff --git a/apps/server/src/provider/museSdk.test.ts b/packages/provider-muse/src/server/sdk.test.ts similarity index 99% rename from apps/server/src/provider/museSdk.test.ts rename to packages/provider-muse/src/server/sdk.test.ts index ff8de0c1abf9..8c92498ca517 100644 --- a/apps/server/src/provider/museSdk.test.ts +++ b/packages/provider-muse/src/server/sdk.test.ts @@ -9,7 +9,7 @@ import { makeMuseEnvironment, museApprovalMode, type MuseSdkHost, -} from "./museSdk.ts"; +} from "./sdk.ts"; vi.mock("@muse-code/sdk", () => ({ spawnMspConnection: vi.fn() })); diff --git a/apps/server/src/provider/museSdk.ts b/packages/provider-muse/src/server/sdk.ts similarity index 100% rename from apps/server/src/provider/museSdk.ts rename to packages/provider-muse/src/server/sdk.ts diff --git a/apps/server/src/provider/MuseProvider.test.ts b/packages/provider-muse/src/server/status.test.ts similarity index 96% rename from apps/server/src/provider/MuseProvider.test.ts rename to packages/provider-muse/src/server/status.test.ts index be7cafdbd6a1..8f7031de0ad7 100644 --- a/apps/server/src/provider/MuseProvider.test.ts +++ b/packages/provider-muse/src/server/status.test.ts @@ -1,15 +1,15 @@ import * as NodeServices from "@effect/platform-node/NodeServices"; import { expect, it } from "@effect/vitest"; -import { MuseSettings } from "@t3tools/contracts"; +import { MuseSettings } from "../settings.ts"; import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; import * as Schema from "effect/Schema"; import { vi } from "vite-plus/test"; -import { writeFakeCli } from "../testUtils/fakeCli.ts"; -import type { MuseSdkHost } from "./museSdk.ts"; +import { writeFakeCli } from "@t3tools/provider-testing/fakeCli"; +import type { MuseSdkHost } from "./sdk.ts"; import { COMPACT_SLASH_COMMAND } from "@t3tools/provider-core/server/snapshotProbe"; -import { checkMuseProviderStatus } from "./MuseProvider.ts"; +import { checkMuseProviderStatus } from "./status.ts"; const settings = Schema.decodeSync(MuseSettings); const makeHost = (catalog: Record) => { diff --git a/apps/server/src/provider/MuseProvider.ts b/packages/provider-muse/src/server/status.ts similarity index 94% rename from apps/server/src/provider/MuseProvider.ts rename to packages/provider-muse/src/server/status.ts index 3d44431d0564..2cb4e5360ff2 100644 --- a/apps/server/src/provider/MuseProvider.ts +++ b/packages/provider-muse/src/server/status.ts @@ -1,4 +1,5 @@ -import type { MuseSettings, ServerProviderModel } from "@t3tools/contracts"; +import { ServerProviderModel } from "@t3tools/contracts"; +import { MuseSettings } from "../settings.ts"; import { resolveSpawnCommand } from "@t3tools/shared/shell"; import * as DateTime from "effect/DateTime"; import * as Effect from "effect/Effect"; @@ -6,9 +7,9 @@ import * as Option from "effect/Option"; import * as Result from "effect/Result"; import { ChildProcess } from "effect/process"; -import { createMuseSdkHost, makeMuseEnvironment } from "./museSdk.ts"; -import { parseMuseVersion } from "./museMaintenance.ts"; -import { discoverMuseModels, museModelCapabilities } from "./museModelCatalog.ts"; +import { createMuseSdkHost, makeMuseEnvironment } from "./sdk.ts"; +import { parseMuseVersion } from "./maintenance.ts"; +import { discoverMuseModels, museModelCapabilities } from "./modelCatalog.ts"; import { buildServerProvider, COMPACT_SLASH_COMMAND, diff --git a/apps/server/src/textGeneration/MuseTextGeneration.test.ts b/packages/provider-muse/src/server/textGeneration.test.ts similarity index 98% rename from apps/server/src/textGeneration/MuseTextGeneration.test.ts rename to packages/provider-muse/src/server/textGeneration.test.ts index 5f3be0548705..41e84dae23ec 100644 --- a/apps/server/src/textGeneration/MuseTextGeneration.test.ts +++ b/packages/provider-muse/src/server/textGeneration.test.ts @@ -3,19 +3,19 @@ import { expect, it } from "@effect/vitest"; import type { NotificationHandler } from "@muse-code/sdk"; import { MUSE_DEFAULT_MODEL, - MuseSettings, ProviderInstanceId, type ServerProviderModel, } from "@t3tools/contracts"; +import { MuseSettings } from "../settings.ts"; import { createModelSelection } from "@t3tools/shared/model"; import * as Effect from "effect/Effect"; import * as Fiber from "effect/Fiber"; import * as Schema from "effect/Schema"; import { vi } from "vite-plus/test"; -import type { MuseSdkHost } from "../provider/museSdk.ts"; -import { museModelCapabilities } from "../provider/museModelCatalog.ts"; -import { makeMuseTextGeneration } from "./MuseTextGeneration.ts"; +import type { MuseSdkHost } from "./sdk.ts"; +import { museModelCapabilities } from "./modelCatalog.ts"; +import { makeMuseTextGeneration } from "./textGeneration.ts"; const settings = Schema.decodeSync(MuseSettings)({ enabled: true }); const modelSelection = createModelSelection( diff --git a/apps/server/src/textGeneration/MuseTextGeneration.ts b/packages/provider-muse/src/server/textGeneration.ts similarity index 83% rename from apps/server/src/textGeneration/MuseTextGeneration.ts rename to packages/provider-muse/src/server/textGeneration.ts index e0d4f3f3afa5..8a13cf30260c 100644 --- a/apps/server/src/textGeneration/MuseTextGeneration.ts +++ b/packages/provider-muse/src/server/textGeneration.ts @@ -2,10 +2,10 @@ import { DEFAULT_TEXT_GENERATION_REASONING_EFFORT, MUSE_DEFAULT_MODEL, type ModelSelection, - type MuseSettings, type ServerProviderModel, TextGenerationError, } from "@t3tools/contracts"; +import type { MuseSettings } from "../settings.ts"; import { sanitizeBranchFragment, sanitizeFeatureBranchName } from "@t3tools/shared/git"; import { getModelSelectionStringOptionValue } from "@t3tools/shared/model"; import { extractJsonObject } from "@t3tools/shared/schemaJson"; @@ -14,13 +14,9 @@ import * as FileSystem from "effect/FileSystem"; import * as Option from "effect/Option"; import * as Schema from "effect/Schema"; -import { - createMuseSdkHost, - createMuseSdkHostEffect, - type MuseSdkHost, -} from "../provider/museSdk.ts"; -import { museModelCapabilities, resolveMuseReasoningEffort } from "../provider/museModelCatalog.ts"; -import type * as TextGeneration from "./TextGeneration.ts"; +import { createMuseSdkHost, createMuseSdkHostEffect, type MuseSdkHost } from "./sdk.ts"; +import { museModelCapabilities, resolveMuseReasoningEffort } from "./modelCatalog.ts"; +import type { ProviderTextGeneration } from "@t3tools/provider-core/server/textGeneration"; import { buildBranchNamePrompt, buildCommitMessagePrompt, @@ -73,7 +69,7 @@ const decodeUserInput = Schema.decodeUnknownSync(UserInputRequested); const encodeJson = Schema.encodeEffect(Schema.fromJsonString(Schema.Unknown)); const decodeJson = Schema.decodeEffect(Schema.fromJsonString(Schema.Unknown)); -type Operation = keyof TextGeneration.TextGeneration["Service"]; +type Operation = keyof ProviderTextGeneration; async function generateMuseText( host: MuseSdkHost, @@ -299,52 +295,56 @@ export const makeMuseTextGeneration = Effect.fn("makeMuseTextGeneration")(functi ); }); - const generateCommitMessage: TextGeneration.TextGeneration["Service"]["generateCommitMessage"] = - Effect.fn("MuseTextGeneration.generateCommitMessage")(function* (input) { - const generated = yield* runMuseJson({ - operation: "generateCommitMessage", - ...buildCommitMessagePrompt({ ...input, includeBranch: input.includeBranch === true }), - modelSelection: input.modelSelection, - }); - return { - subject: sanitizeCommitSubject(generated.subject), - body: generated.body.trim(), - ...("branch" in generated && typeof generated.branch === "string" - ? { branch: sanitizeFeatureBranchName(generated.branch) } - : {}), - }; + const generateCommitMessage: ProviderTextGeneration["generateCommitMessage"] = Effect.fn( + "MuseTextGeneration.generateCommitMessage", + )(function* (input) { + const generated = yield* runMuseJson({ + operation: "generateCommitMessage", + ...buildCommitMessagePrompt({ ...input, includeBranch: input.includeBranch === true }), + modelSelection: input.modelSelection, }); - const generatePrContent: TextGeneration.TextGeneration["Service"]["generatePrContent"] = - Effect.fn("MuseTextGeneration.generatePrContent")(function* (input) { - const generated = yield* runMuseJson({ - operation: "generatePrContent", - ...buildPrContentPrompt(input), - modelSelection: input.modelSelection, - }); - return { title: sanitizePrTitle(generated.title), body: generated.body.trim() }; + return { + subject: sanitizeCommitSubject(generated.subject), + body: generated.body.trim(), + ...("branch" in generated && typeof generated.branch === "string" + ? { branch: sanitizeFeatureBranchName(generated.branch) } + : {}), + }; + }); + const generatePrContent: ProviderTextGeneration["generatePrContent"] = Effect.fn( + "MuseTextGeneration.generatePrContent", + )(function* (input) { + const generated = yield* runMuseJson({ + operation: "generatePrContent", + ...buildPrContentPrompt(input), + modelSelection: input.modelSelection, }); - const generateBranchName: TextGeneration.TextGeneration["Service"]["generateBranchName"] = - Effect.fn("MuseTextGeneration.generateBranchName")(function* (input) { - const generated = yield* runMuseJson({ - operation: "generateBranchName", - ...buildBranchNamePrompt(input), - modelSelection: input.modelSelection, - }); - return { branch: sanitizeBranchFragment(generated.branch) }; + return { title: sanitizePrTitle(generated.title), body: generated.body.trim() }; + }); + const generateBranchName: ProviderTextGeneration["generateBranchName"] = Effect.fn( + "MuseTextGeneration.generateBranchName", + )(function* (input) { + const generated = yield* runMuseJson({ + operation: "generateBranchName", + ...buildBranchNamePrompt(input), + modelSelection: input.modelSelection, }); - const generateThreadTitle: TextGeneration.TextGeneration["Service"]["generateThreadTitle"] = - Effect.fn("MuseTextGeneration.generateThreadTitle")(function* (input) { - const generated = yield* runMuseJson({ - operation: "generateThreadTitle", - ...buildThreadTitlePrompt(input), - modelSelection: input.modelSelection, - }); - return { title: sanitizeThreadTitle(generated.title) }; + return { branch: sanitizeBranchFragment(generated.branch) }; + }); + const generateThreadTitle: ProviderTextGeneration["generateThreadTitle"] = Effect.fn( + "MuseTextGeneration.generateThreadTitle", + )(function* (input) { + const generated = yield* runMuseJson({ + operation: "generateThreadTitle", + ...buildThreadTitlePrompt(input), + modelSelection: input.modelSelection, }); + return { title: sanitizeThreadTitle(generated.title) }; + }); return { generateCommitMessage, generatePrContent, generateBranchName, generateThreadTitle, - } satisfies TextGeneration.TextGeneration["Service"]; + } satisfies ProviderTextGeneration; }); diff --git a/packages/provider-muse/src/settings.ts b/packages/provider-muse/src/settings.ts new file mode 100644 index 000000000000..85a781dc6608 --- /dev/null +++ b/packages/provider-muse/src/settings.ts @@ -0,0 +1,36 @@ +/** + * Muse Code instance settings. Shared by the server driver and the client + * settings form, so it holds only browser-safe schema code. + * + * @module provider-muse/settings + */ +import { + CustomModelSetting, + makeBinaryPathSetting, + makeProviderSettingsSchema, +} from "@t3tools/contracts"; +import * as Effect from "effect/Effect"; +import * as Schema from "effect/Schema"; + +export const MuseSettings = makeProviderSettingsSchema( + { + enabled: Schema.Boolean.pipe( + Schema.withDecodingDefault(Effect.succeed(false)), + Schema.annotateKey({ providerSettingsForm: { hidden: true } }), + ), + binaryPath: makeBinaryPathSetting("muse").pipe( + Schema.annotateKey({ + title: "Binary path", + description: + "Path to the Muse Code binary on this environment. Sign in with muse login on that host.", + providerSettingsForm: { placeholder: "muse", clearWhenEmpty: "omit" }, + }), + ), + customModels: Schema.Array(CustomModelSetting).pipe( + Schema.withDecodingDefault(Effect.succeed([])), + Schema.annotateKey({ providerSettingsForm: { hidden: true } }), + ), + }, + { order: ["binaryPath"] }, +); +export type MuseSettings = typeof MuseSettings.Type; diff --git a/packages/provider-muse/src/testing.ts b/packages/provider-muse/src/testing.ts new file mode 100644 index 000000000000..810388c5afe9 --- /dev/null +++ b/packages/provider-muse/src/testing.ts @@ -0,0 +1,14 @@ +/** + * Muse Code internals for replay tests and the fixture recorder: the MSP + * launch arguments, initialize parameters, environment, and version parser. + * + * @module provider-muse/testing + */ +export { + makeMuseEnvironment, + museInitializeParams, + museServeArgs, + type MuseSdkHost, + type MuseSdkHostOptions, +} from "./server/sdk.ts"; +export { parseMuseVersion } from "./server/maintenance.ts"; diff --git a/packages/provider-muse/tsconfig.json b/packages/provider-muse/tsconfig.json new file mode 100644 index 000000000000..374bac55202d --- /dev/null +++ b/packages/provider-muse/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "types": ["node"] + }, + "include": ["src"] +} diff --git a/packages/provider-testing/package.json b/packages/provider-testing/package.json index 35e3941ba87d..39c6c1f644aa 100644 --- a/packages/provider-testing/package.json +++ b/packages/provider-testing/package.json @@ -18,6 +18,10 @@ "./host": { "types": "./src/host.ts", "import": "./src/host.ts" + }, + "./fakeCli": { + "types": "./src/fakeCli.ts", + "import": "./src/fakeCli.ts" } }, "scripts": { @@ -28,6 +32,7 @@ "@effect/platform-node": "catalog:", "@t3tools/contracts": "workspace:*", "@t3tools/provider-core": "workspace:*", + "@t3tools/shared": "workspace:*", "effect": "catalog:" }, "devDependencies": { diff --git a/apps/server/src/testUtils/fakeCli.ts b/packages/provider-testing/src/fakeCli.ts similarity index 100% rename from apps/server/src/testUtils/fakeCli.ts rename to packages/provider-testing/src/fakeCli.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b948baba7c65..9316e7f35f53 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -356,6 +356,9 @@ importers: '@t3tools/provider-core': specifier: workspace:* version: link:../../packages/provider-core + '@t3tools/provider-muse': + specifier: workspace:* + version: link:../../packages/provider-muse '@t3tools/provider-pi': specifier: workspace:* version: link:../../packages/provider-pi @@ -603,6 +606,9 @@ importers: '@t3tools/provider-core': specifier: workspace:* version: link:../../packages/provider-core + '@t3tools/provider-muse': + specifier: workspace:* + version: link:../../packages/provider-muse '@t3tools/provider-pi': specifier: workspace:* version: link:../../packages/provider-pi @@ -733,6 +739,9 @@ importers: '@t3tools/provider-core': specifier: workspace:* version: link:../../packages/provider-core + '@t3tools/provider-muse': + specifier: workspace:* + version: link:../../packages/provider-muse '@t3tools/provider-pi': specifier: workspace:* version: link:../../packages/provider-pi @@ -1099,6 +1108,40 @@ importers: specifier: 1.0.0 version: 1.0.0(@types/node@24.12.4)(@vitest/ui@5.0.1(vitest@5.0.1))(bufferutil@4.1.0)(esbuild@0.28.2)(jiti@2.7.0)(jsdom@30.1.0(@noble/hashes@1.8.0))(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) + packages/provider-muse: + dependencies: + '@muse-code/sdk': + specifier: 1.4.2 + version: 1.4.2(@types/node@24.12.4) + '@t3tools/contracts': + specifier: workspace:* + version: link:../contracts + '@t3tools/provider-core': + specifier: workspace:* + version: link:../provider-core + '@t3tools/shared': + specifier: workspace:* + version: link:../shared + effect: + specifier: 4.0.1 + version: 4.0.1(patch_hash=a33cba07c41f32374c2aaa86ea4a84d3ab9a872a78c94b65e929b19f8361856f) + devDependencies: + '@effect/platform-node': + specifier: 4.0.1 + version: 4.0.1(bufferutil@4.1.0)(effect@4.0.1(patch_hash=a33cba07c41f32374c2aaa86ea4a84d3ab9a872a78c94b65e929b19f8361856f))(redis@6.2.1)(utf-8-validate@6.0.6) + '@effect/vitest': + specifier: 4.0.1 + version: 4.0.1(patch_hash=359f6fb2f7b3ec145bb72208edb9034f02489791aa2491a55cdbd69bd56ee0d2)(@types/node@24.12.4)(@vitest/ui@5.0.1)(bufferutil@4.1.0)(effect@4.0.1(patch_hash=a33cba07c41f32374c2aaa86ea4a84d3ab9a872a78c94b65e929b19f8361856f))(esbuild@0.28.2)(jiti@2.7.0)(jsdom@30.1.0(@noble/hashes@1.8.0))(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) + '@t3tools/provider-testing': + specifier: workspace:* + version: link:../provider-testing + '@types/node': + specifier: 24.12.4 + version: 24.12.4 + vite-plus: + specifier: 1.0.0 + version: 1.0.0(@types/node@24.12.4)(@vitest/ui@5.0.1(vitest@5.0.1))(bufferutil@4.1.0)(esbuild@0.28.2)(jiti@2.7.0)(jsdom@30.1.0(@noble/hashes@1.8.0))(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) + packages/provider-pi: dependencies: '@t3tools/contracts': @@ -1141,6 +1184,9 @@ importers: '@t3tools/provider-core': specifier: workspace:* version: link:../provider-core + '@t3tools/shared': + specifier: workspace:* + version: link:../shared effect: specifier: 4.0.1 version: 4.0.1(patch_hash=a33cba07c41f32374c2aaa86ea4a84d3ab9a872a78c94b65e929b19f8361856f) diff --git a/scripts/release-smoke.ts b/scripts/release-smoke.ts index 8fb9ab883995..2f39d7a61e93 100644 --- a/scripts/release-smoke.ts +++ b/scripts/release-smoke.ts @@ -31,6 +31,7 @@ const workspaceFiles = [ "packages/provider-core/package.json", "packages/provider-testing/package.json", "packages/provider-pi/package.json", + "packages/provider-muse/package.json", "packages/effect-acp/package.json", "packages/effect-codex-app-server/package.json", "scripts/package.json",