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",