From 1789bba72aee1bbb4e8cb390a0f719373a6cd4af Mon Sep 17 00:00:00 2001 From: Evan Verma Date: Mon, 12 Oct 2026 19:08:20 -0700 Subject: [PATCH] feat(models): add Muse Wonder + Wonder Full support (v0.3.0) --- README.md | 8 +++++-- dist/index.js | 8 +++---- dist/src/baseline.models.js | 18 ++++++++++++++- dist/src/catalog.js | 8 +++---- index.ts | 8 +++---- openclaw.plugin.json | 46 ++++++++++++++++++++++++++++++++++++- package-lock.json | 4 ++-- package.json | 5 ++-- src/baseline.models.ts | 18 ++++++++++++++- src/catalog.ts | 8 +++---- test/catalog.test.ts | 16 ++++++++----- test/modelMapping.test.ts | 6 ++++- 12 files changed, 121 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 8f7cd3e..c57dc45 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # openclaw-muse-code -Muse Spark in OpenClaw, billed to the **Muse Code monthly subscription** +Muse Spark + Muse Wonder in OpenClaw, billed to the **Muse Code monthly subscription** instead of API credits. Meta device-code login, no API key, no third-party CLI. @@ -23,6 +23,10 @@ plugins can be installed side by side. | `muse-code/muse-spark-1.2` | Standard | 1,048,576 | | `muse-code/muse-spark-1.2-contributor` | Contributor | 1,048,576 | | `muse-code/muse-spark-1.1` | Standard | 1,048,576 | +| `muse-code/muse-wonder-1.0` | Standard | 1,048,576 | +| `muse-code/muse-wonder-1.0-contributor` | Contributor | 1,048,576 | +| `muse-code/muse-wonder-full-1.0` | Standard | 1,048,576 | +| `muse-code/muse-wonder-full-1.0-contributor` | Contributor | 1,048,576 | > [!WARNING] > Meta's [Terms of Service](https://dev.meta.ai/legal/terms-of-service) @@ -34,7 +38,7 @@ plugins can be installed side by side. > implicitly: the setup default is the standard `muse-spark-1.3`. Model discovery is **live**: the catalog is fetched from -`GET {baseUrl}/models` (60s in-memory TTL, only `muse-spark-*` ids +`GET {baseUrl}/models` (60s in-memory TTL, only `muse-spark-*` and `muse-wonder-*` ids admitted — other endpoint families such as `sam-*`, `muse-image-*` or `muse-voice-*` are not chat models), so newly published models appear automatically with no plugin update. The static table above is the diff --git a/dist/index.js b/dist/index.js index ab79e8f..aeb3a89 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1,5 +1,5 @@ /** - * Muse Spark model provider plugin for OpenClaw, billed to the Muse Code + * Muse Spark + Muse Wonder model provider plugin for OpenClaw, billed to the Muse Code * monthly subscription instead of API credits. * * Authentication is a Meta device-code login performed once via @@ -27,7 +27,7 @@ if (!process.env[ENV_VAR]?.trim()) { /** Meta Model API base URL for Responses API traffic. */ const META_BASE_URL = "https://api.meta.ai/v1"; /** - * Known Muse Spark context window (official catalog: input + output share + * Known Muse family context window (official catalog: input + output share * the budget). Used as the default when a row omits context_length. */ const MUSE_CONTEXT_WINDOW = 1_048_576; @@ -39,7 +39,7 @@ const MAX_OUTPUT_TOKENS = 131_072; /** Default model for setup flows: standard tier, never contributor. */ export const DEFAULT_MODEL_ID = "muse-spark-1.3"; /** - * Maps a Muse Spark baseline row to an OpenClaw model definition. + * Maps a Muse Spark / Muse Wonder baseline row to an OpenClaw model definition. * * Every model uses the Responses API transport. Contributor-tier variants * keep working when selected explicitly (see README for the training-data @@ -134,7 +134,7 @@ export function resolveMuseCodeDynamicModel(ctx) { export default defineSingleProviderPluginEntry({ id: "muse-code", name: "Muse Code", - description: "Muse Spark (api.meta.ai) model provider billed to the Muse Code monthly subscription.", + description: "Muse Spark + Muse Wonder (api.meta.ai) model provider billed to the Muse Code monthly subscription.", provider: { label: "Muse Code", docsPath: "/providers/muse-code", diff --git a/dist/src/baseline.models.js b/dist/src/baseline.models.js index e40ea36..d20feae 100644 --- a/dist/src/baseline.models.js +++ b/dist/src/baseline.models.js @@ -1,5 +1,5 @@ /** - * Static Muse Spark model catalog. + * Static Muse Spark + Muse Wonder model catalog. * * Hand-maintained (there is no public unauthenticated catalog endpoint for * the Meta Model API): model ids and context windows mirror the official @@ -21,4 +21,20 @@ export const museCodeBaselineModels = [ context_length: 1048576, }, { id: "muse-spark-1.1", name: "Muse Spark 1.1", context_length: 1048576 }, + { id: "muse-wonder-1.0", name: "Muse Wonder 1.0", context_length: 1048576 }, + { + id: "muse-wonder-1.0-contributor", + name: "Muse Wonder 1.0 Contributor", + context_length: 1048576, + }, + { + id: "muse-wonder-full-1.0", + name: "Muse Wonder Full 1.0", + context_length: 1048576, + }, + { + id: "muse-wonder-full-1.0-contributor", + name: "Muse Wonder Full 1.0 Contributor", + context_length: 1048576, + }, ]; diff --git a/dist/src/catalog.js b/dist/src/catalog.js index 3369075..14f3a8c 100644 --- a/dist/src/catalog.js +++ b/dist/src/catalog.js @@ -10,12 +10,12 @@ import { readCacheSync } from "./auth.js"; export const MODELS_PATH = "/models"; export const LIVE_CATALOG_TTL_MS = 60_000; export const FETCH_TIMEOUT_MS = 8_000; -// Allowlist: this provider serves the Muse Spark chat family. The live +// Allowlist: this provider serves the Muse Spark + Muse Wonder chat families. The live // endpoint also returns other families (sam-*, muse-image-*, muse-voice-*) // that are not usable as chat/completions models — a denylist would leak -// the next such family, so only muse-spark-* is admitted. Anything else +// the next such family, so only muse-spark-* and muse-wonder-* are admitted. Anything else // still resolves through the dynamic resolver if requested explicitly. -const CHAT_FAMILY_PREFIX = "muse-spark-"; +const CHAT_FAMILY_PREFIXES = ["muse-spark-", "muse-wonder-"]; let cache = null; /** Test-only cache reset. */ export function __clearLiveCatalogCache() { @@ -39,7 +39,7 @@ function parseModelIds(body) { (row.id.length > 0)); }) .map((row) => row.id) - .filter((id) => id.startsWith(CHAT_FAMILY_PREFIX)); + .filter((id) => CHAT_FAMILY_PREFIXES.some((p) => id.startsWith(p))); return ids.length > 0 ? ids : null; } /** diff --git a/index.ts b/index.ts index 18302b3..e925ddd 100644 --- a/index.ts +++ b/index.ts @@ -1,5 +1,5 @@ /** - * Muse Spark model provider plugin for OpenClaw, billed to the Muse Code + * Muse Spark + Muse Wonder model provider plugin for OpenClaw, billed to the Muse Code * monthly subscription instead of API credits. * * Authentication is a Meta device-code login performed once via @@ -37,7 +37,7 @@ if (!process.env[ENV_VAR]?.trim()) { const META_BASE_URL = "https://api.meta.ai/v1"; /** - * Known Muse Spark context window (official catalog: input + output share + * Known Muse family context window (official catalog: input + output share * the budget). Used as the default when a row omits context_length. */ const MUSE_CONTEXT_WINDOW = 1_048_576; @@ -59,7 +59,7 @@ export type MuseCodeModelRow = { }; /** - * Maps a Muse Spark baseline row to an OpenClaw model definition. + * Maps a Muse Spark / Muse Wonder baseline row to an OpenClaw model definition. * * Every model uses the Responses API transport. Contributor-tier variants * keep working when selected explicitly (see README for the training-data @@ -170,7 +170,7 @@ export default defineSingleProviderPluginEntry({ id: "muse-code", name: "Muse Code", description: - "Muse Spark (api.meta.ai) model provider billed to the Muse Code monthly subscription.", + "Muse Spark + Muse Wonder (api.meta.ai) model provider billed to the Muse Code monthly subscription.", provider: { label: "Muse Code", docsPath: "/providers/muse-code", diff --git a/openclaw.plugin.json b/openclaw.plugin.json index 7f9dba6..41619ba 100644 --- a/openclaw.plugin.json +++ b/openclaw.plugin.json @@ -1,7 +1,7 @@ { "id": "muse-code", "name": "Muse Code", - "description": "Muse Spark (api.meta.ai) model provider plugin billed to the Muse Code monthly subscription.", + "description": "Muse Spark + Muse Wonder (api.meta.ai) model provider plugin billed to the Muse Code monthly subscription.", "providers": ["muse-code"], "modelSupport": { "modelPrefixes": ["muse-code/"] @@ -95,6 +95,50 @@ "contextWindow": 1048576, "maxTokens": 131072, "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "muse-wonder-1.0", + "name": "Muse Wonder 1.0", + "api": "openai-responses", + "baseUrl": "https://api.meta.ai/v1", + "reasoning": true, + "input": ["text", "image"], + "contextWindow": 1048576, + "maxTokens": 131072, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "muse-wonder-1.0-contributor", + "name": "Muse Wonder 1.0 Contributor", + "api": "openai-responses", + "baseUrl": "https://api.meta.ai/v1", + "reasoning": true, + "input": ["text", "image"], + "contextWindow": 1048576, + "maxTokens": 131072, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "muse-wonder-full-1.0", + "name": "Muse Wonder Full 1.0", + "api": "openai-responses", + "baseUrl": "https://api.meta.ai/v1", + "reasoning": true, + "input": ["text", "image"], + "contextWindow": 1048576, + "maxTokens": 131072, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "muse-wonder-full-1.0-contributor", + "name": "Muse Wonder Full 1.0 Contributor", + "api": "openai-responses", + "baseUrl": "https://api.meta.ai/v1", + "reasoning": true, + "input": ["text", "image"], + "contextWindow": 1048576, + "maxTokens": 131072, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } } ] } diff --git a/package-lock.json b/package-lock.json index ee21db5..aaf87bb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@thestreamcode/openclaw-muse-code", - "version": "0.2.0", + "version": "0.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@thestreamcode/openclaw-muse-code", - "version": "0.2.0", + "version": "0.3.0", "license": "MIT", "devDependencies": { "openclaw": "2026.9.6", diff --git a/package.json b/package.json index 4217974..6f7dd7e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@thestreamcode/openclaw-muse-code", - "version": "0.2.0", - "description": "Muse Spark in OpenClaw billed to the Muse Code monthly subscription (Meta device login, no API key)", + "version": "0.3.0", + "description": "Muse Spark + Muse Wonder in OpenClaw billed to the Muse Code monthly subscription (Meta device login, no API key)", "type": "module", "license": "MIT", "private": false, @@ -37,6 +37,7 @@ "openclaw-plugin", "muse-code", "muse-spark", + "muse-wonder", "meta-ai", "llm", "provider" diff --git a/src/baseline.models.ts b/src/baseline.models.ts index 9160344..e12b27c 100644 --- a/src/baseline.models.ts +++ b/src/baseline.models.ts @@ -1,5 +1,5 @@ /** - * Static Muse Spark model catalog. + * Static Muse Spark + Muse Wonder model catalog. * * Hand-maintained (there is no public unauthenticated catalog endpoint for * the Meta Model API): model ids and context windows mirror the official @@ -28,4 +28,20 @@ export const museCodeBaselineModels: MuseCodeBaselineRow[] = [ context_length: 1048576, }, { id: "muse-spark-1.1", name: "Muse Spark 1.1", context_length: 1048576 }, + { id: "muse-wonder-1.0", name: "Muse Wonder 1.0", context_length: 1048576 }, + { + id: "muse-wonder-1.0-contributor", + name: "Muse Wonder 1.0 Contributor", + context_length: 1048576, + }, + { + id: "muse-wonder-full-1.0", + name: "Muse Wonder Full 1.0", + context_length: 1048576, + }, + { + id: "muse-wonder-full-1.0-contributor", + name: "Muse Wonder Full 1.0 Contributor", + context_length: 1048576, + }, ]; diff --git a/src/catalog.ts b/src/catalog.ts index 7b33332..55b2b44 100644 --- a/src/catalog.ts +++ b/src/catalog.ts @@ -13,12 +13,12 @@ export const MODELS_PATH = "/models"; export const LIVE_CATALOG_TTL_MS = 60_000; export const FETCH_TIMEOUT_MS = 8_000; -// Allowlist: this provider serves the Muse Spark chat family. The live +// Allowlist: this provider serves the Muse Spark + Muse Wonder chat families. The live // endpoint also returns other families (sam-*, muse-image-*, muse-voice-*) // that are not usable as chat/completions models — a denylist would leak -// the next such family, so only muse-spark-* is admitted. Anything else +// the next such family, so only muse-spark-* and muse-wonder-* are admitted. Anything else // still resolves through the dynamic resolver if requested explicitly. -const CHAT_FAMILY_PREFIX = "muse-spark-"; +const CHAT_FAMILY_PREFIXES = ["muse-spark-", "muse-wonder-"]; type ModelsResponse = { data?: unknown; @@ -55,7 +55,7 @@ function parseModelIds(body: unknown): string[] | null { ); }) .map((row) => row.id) - .filter((id) => id.startsWith(CHAT_FAMILY_PREFIX)); + .filter((id) => CHAT_FAMILY_PREFIXES.some((p) => id.startsWith(p))); return ids.length > 0 ? ids : null; } diff --git a/test/catalog.test.ts b/test/catalog.test.ts index c74895b..7394a9c 100644 --- a/test/catalog.test.ts +++ b/test/catalog.test.ts @@ -38,6 +38,8 @@ const LIVE_OK = { data: [ { id: "muse-spark-1.3" }, { id: "muse-spark-9.9" }, + { id: "muse-wonder-1.0" }, + { id: "muse-wonder-full-1.0" }, { id: "sam-3.1" }, { id: "muse-image-1.0" }, { id: "muse-voice-2.0" }, @@ -45,11 +47,13 @@ const LIVE_OK = { }; describe("fetchLiveModelIds", () => { - it("admits only the muse-spark chat family", async () => { + it("admits only the muse-spark + muse-wonder chat families", async () => { stubFetch(() => ({ status: 200, body: LIVE_OK })); expect(await fetchLiveModelIds("https://x.test/v1", "k")).toEqual([ "muse-spark-1.3", "muse-spark-9.9", + "muse-wonder-1.0", + "muse-wonder-full-1.0", ]); }); @@ -93,21 +97,21 @@ describe("fetchLiveModelIds", () => { describe("getLiveModelIdsCached", () => { it("serves warm entries without refetching", async () => { const counter = stubFetch(() => ({ status: 200, body: LIVE_OK })); - expect(await getLiveModelIdsCached("https://x.test/v1", "k", 60_000)).toHaveLength(2); - expect(await getLiveModelIdsCached("https://x.test/v1", "k", 60_000)).toHaveLength(2); + expect(await getLiveModelIdsCached("https://x.test/v1", "k", 60_000)).toHaveLength(4); + expect(await getLiveModelIdsCached("https://x.test/v1", "k", 60_000)).toHaveLength(4); expect(counter.calls()).toBe(1); }); it("refetches after expiry", async () => { const counter = stubFetch(() => ({ status: 200, body: LIVE_OK })); - expect(await getLiveModelIdsCached("https://x.test/v1", "k", 0)).toHaveLength(2); - expect(await getLiveModelIdsCached("https://x.test/v1", "k", 60_000)).toHaveLength(2); + expect(await getLiveModelIdsCached("https://x.test/v1", "k", 0)).toHaveLength(4); + expect(await getLiveModelIdsCached("https://x.test/v1", "k", 60_000)).toHaveLength(4); expect(counter.calls()).toBe(2); }); it("clears stale entries on failure", async () => { stubFetch(() => ({ status: 200, body: LIVE_OK })); - expect(await getLiveModelIdsCached("https://x.test/v1", "k", 60_000)).toHaveLength(2); + expect(await getLiveModelIdsCached("https://x.test/v1", "k", 60_000)).toHaveLength(4); stubFetch(() => ({ status: 500, body: {} })); __clearLiveCatalogCache(); expect(await getLiveModelIdsCached("https://x.test/v1", "k", 60_000)).toBeNull(); diff --git a/test/modelMapping.test.ts b/test/modelMapping.test.ts index f8626de..5fabf5d 100644 --- a/test/modelMapping.test.ts +++ b/test/modelMapping.test.ts @@ -48,7 +48,7 @@ describe("projectModel", () => { }); describe("static baseline (pre-credential discovery)", () => { - it("ships the five-model Muse Spark family", () => { + it("ships the nine-model Muse Spark + Wonder family", () => { const ids = museCodeBaselineModels.map((r) => r.id); expect(ids).toEqual([ "muse-spark-1.3", @@ -56,6 +56,10 @@ describe("static baseline (pre-credential discovery)", () => { "muse-spark-1.2", "muse-spark-1.2-contributor", "muse-spark-1.1", + "muse-wonder-1.0", + "muse-wonder-1.0-contributor", + "muse-wonder-full-1.0", + "muse-wonder-full-1.0-contributor", ]); });