pi-meta-oauth 0.7.4 → 0.7.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -48,7 +48,7 @@ Prefer a static key instead? Set `META_API_KEY` (or `MODEL_API_KEY`) and skip
48
48
 
49
49
  ## Models
50
50
 
51
- Fallback models use a 1,048,576-token context window, up to 256K output tokens, image input, and reasoning levels `minimal`, `low`, `medium`, `high`, and `xhigh` (`muse-spark-1.3` additionally supports `max`; `muse-spark-1.3-contributor` does too when [opted in](#contributor-max-opt-in)). The catalog-discovered `muse-wonder-1.0` and `muse-wonder-full-1.0` also support `max`, as do their `-contributor` variants when opted in.
51
+ Bundled models use a 1,048,576-token context window, up to 256K output tokens, image input, and reasoning levels `minimal`, `low`, `medium`, `high`, and `xhigh`. `muse-spark-1.3`, `muse-wonder-1.0`, and `muse-wonder-full-1.0` additionally support `max`; their `-contributor` variants do too when [opted in](#contributor-max-opt-in). Meta's catalog endpoint has returned bare model IDs without names, prices, or modalities since at least 2026-10-23, so these bundled definitions supply that metadata for known IDs; an unknown ID still appears with default values.
52
52
 
53
53
  | id | pricing (input/output/cached) $/M |
54
54
  | --- | --- |
@@ -57,6 +57,12 @@ Fallback models use a 1,048,576-token context window, up to 256K output tokens,
57
57
  | `muse-spark-1.2` | 1.25 / 4.25 / 0.15 |
58
58
  | `muse-spark-1.2-contributor` | 0.10 / 0.20 / 0.002 |
59
59
  | `muse-spark-1.1` | 1.25 / 4.25 / 0.15 |
60
+ | `muse-wonder-1.0` | 4.00 / 20.00 / 0.20 |
61
+ | `muse-wonder-1.0-contributor` | no published per-token rate (Muse Code subscription); Pi shows $0 |
62
+ | `muse-wonder-full-1.0` | no published per-token rate (Muse Code subscription); Pi shows $0 |
63
+ | `muse-wonder-full-1.0-contributor` | no published per-token rate (Muse Code subscription); Pi shows $0 |
64
+
65
+ The catalog also lists Meta's non-chat models, which are billed per generated image, per hour of audio, or per video frame rather than per token: the `muse-image-*`, `muse-voice-*`, and `sam-*` families. The extension hides them from the chat model list, including entries an earlier release cached in `models-store.json`.
60
66
 
61
67
  > **Contributor-model privacy:** discounted contributor models allow Meta to use prompts and completions for product improvement, including training future Meta models. Use a standard model such as `muse-spark-1.3` if you do not want the contributor terms. See [Meta's model documentation](https://dev.meta.ai/docs/models).
62
68
 
@@ -135,10 +141,11 @@ also bypasses isolation and can run these probes when credentials are available.
135
141
  The cache probe makes two billable Responses requests plus one retry on a cache
136
142
  miss. The fingerprint probe makes two tiny Contributor-`max` requests; it sends
137
143
  the header directly, independently of `META_MUSE_USER_AGENT`. The catalog probe
138
- runs when `PI_META_LIVE_API_KEY` is set. Cache and fingerprint probes resolve
139
- credentials from `PI_META_LIVE_API_KEY`, `META_API_KEY`, `MODEL_API_KEY`, or an
140
- unexpired minted key in `~/.pi/agent/auth.json`, in that order. Probes skip when
141
- no usable credential exists.
144
+ makes one catalog request and fails when a live model ID has neither a bundled
145
+ definition nor a non-chat prefix. All three probes resolve credentials from
146
+ `PI_META_LIVE_API_KEY`, `META_API_KEY`, `MODEL_API_KEY`, or an unexpired minted
147
+ key in `~/.pi/agent/auth.json`, in that order, and skip when no usable
148
+ credential exists.
142
149
 
143
150
  ```bash
144
151
  bun run test:live tests/meta-cache.test.ts
@@ -15,7 +15,11 @@ export {
15
15
  MUSE_USER_AGENT_ENV_VAR,
16
16
  } from "../src/meta/constants.ts";
17
17
  export { refreshMetaModels } from "../src/meta/model-store.ts";
18
- export { metaFallbackCost, toProviderModels } from "../src/meta/models.ts";
18
+ export {
19
+ isNonChatModelId,
20
+ metaFallbackCost,
21
+ toProviderModels,
22
+ } from "../src/meta/models.ts";
19
23
  export { isMuseUserAgentEnabled } from "../src/meta/muse-policy.ts";
20
24
  export {
21
25
  loginMeta,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-meta-oauth",
3
- "version": "0.7.4",
3
+ "version": "0.7.5",
4
4
  "description": "Meta Model API OAuth provider for pi",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -21,6 +21,24 @@ const CONTRIBUTOR_COST = {
21
21
  cacheRead: 0.002,
22
22
  cacheWrite: 0,
23
23
  };
24
+ /** Meta Model API list price for muse-wonder-1.0 (dev.meta.ai pricing page, 2026-10-23). */
25
+ const WONDER_COST = {
26
+ input: 4,
27
+ output: 20,
28
+ cacheRead: 0.2,
29
+ cacheWrite: 0,
30
+ };
31
+ /**
32
+ * Muse Wonder Full and the Wonder Contributor variants are reachable only
33
+ * through a Muse Code subscription; Meta publishes no per-token rate for
34
+ * them, so Pi shows $0. This is a representation of "unpriced", not a fact.
35
+ */
36
+ const SUBSCRIPTION_ONLY_COST = {
37
+ input: 0,
38
+ output: 0,
39
+ cacheRead: 0,
40
+ cacheWrite: 0,
41
+ };
24
42
  const UNKNOWN_MODEL_COST = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
25
43
  const THINKING_LEVELS = [
26
44
  "minimal",
@@ -32,7 +50,13 @@ const THINKING_LEVELS = [
32
50
  ] as const;
33
51
  type ThinkingLevelMap = NonNullable<MetaProviderModel["thinkingLevelMap"]>;
34
52
 
35
- /** Data-only definitions; callers always receive fresh model objects. */
53
+ /**
54
+ * Data-only definitions; callers always receive fresh model objects. The live
55
+ * catalog has carried no `metadata["muse-code"]` block since at least
56
+ * 2026-10-23, so names, prices, and image input for known IDs come from here.
57
+ * Spark first keeps the long-standing default model; after the first refresh
58
+ * the catalog's own order wins anyway.
59
+ */
36
60
  const FALLBACK_DEFINITIONS = [
37
61
  {
38
62
  id: "muse-spark-1.3",
@@ -64,8 +88,54 @@ const FALLBACK_DEFINITIONS = [
64
88
  cost: STANDARD_COST,
65
89
  max: false,
66
90
  },
91
+ {
92
+ id: "muse-wonder-1.0",
93
+ name: "Muse Wonder 1.0",
94
+ cost: WONDER_COST,
95
+ max: true,
96
+ },
97
+ {
98
+ id: "muse-wonder-1.0-contributor",
99
+ name: "Muse Wonder 1.0 Contributor",
100
+ cost: SUBSCRIPTION_ONLY_COST,
101
+ max: false,
102
+ },
103
+ {
104
+ id: "muse-wonder-full-1.0",
105
+ name: "Muse Wonder Full 1.0",
106
+ cost: SUBSCRIPTION_ONLY_COST,
107
+ max: true,
108
+ },
109
+ {
110
+ id: "muse-wonder-full-1.0-contributor",
111
+ name: "Muse Wonder Full 1.0 Contributor",
112
+ cost: SUBSCRIPTION_ONLY_COST,
113
+ max: false,
114
+ },
67
115
  ] as const;
68
116
 
117
+ /**
118
+ * Catalog families Meta bills per generated image, per hour of audio, or per
119
+ * video frame instead of per token (dev.meta.ai pricing page, 2026-10-23):
120
+ * Muse Image, Muse Voice Transcribe, and Segment Anything. They share the
121
+ * catalog with the chat models but are not chat models, so neither the live
122
+ * catalog nor a cached entry may list them as one. Prefixes, not exact IDs,
123
+ * so a new version of a family stays hidden without a code change.
124
+ */
125
+ export const NON_CHAT_MODEL_ID_PREFIXES = [
126
+ "muse-image-",
127
+ "muse-voice-",
128
+ "sam-",
129
+ ] as const;
130
+
131
+ export function isNonChatModelId(id: string): boolean {
132
+ return NON_CHAT_MODEL_ID_PREFIXES.some((prefix) => id.startsWith(prefix));
133
+ }
134
+
135
+ function isBundledModelId(id: string): boolean {
136
+ return FALLBACK_DEFINITIONS.some((model) => model.id === id);
137
+ }
138
+
69
139
  function nonemptyString(value: unknown): string | undefined {
70
140
  return typeof value === "string" && value.trim() ? value.trim() : undefined;
71
141
  }
@@ -166,7 +236,7 @@ export function toProviderModels(catalog: unknown): MetaProviderModel[] {
166
236
  for (const value of data) {
167
237
  const entry = asRecord(value);
168
238
  const id = nonemptyString(entry?.id);
169
- if (!id || seen.has(id)) continue;
239
+ if (!id || seen.has(id) || isNonChatModelId(id)) continue;
170
240
  const metadata = asRecord(asRecord(entry?.metadata)?.["muse-code"]);
171
241
  if (metadata?.is_hidden === true) continue;
172
242
  const fallback = defaultModel(id);
@@ -308,7 +378,14 @@ function storedCost(
308
378
  return cost;
309
379
  }
310
380
 
311
- /** Cache contents cross the same untrusted-data boundary as the remote catalog. */
381
+ /**
382
+ * Cache contents cross the same untrusted-data boundary as the remote catalog.
383
+ * For bundled IDs the name, price, and input come from FALLBACK_DEFINITIONS:
384
+ * the bare catalog cannot have advertised them, so a cached copy is at best an
385
+ * older release's bundled value and at worst its unknown-model default (0.7.4
386
+ * persisted Wonder as a $0 text-only model). Cached limits, effort names,
387
+ * headers, and compat flags are kept; unknown IDs restore every cached field.
388
+ */
312
389
  export function restoreProviderModels(value: unknown): MetaProviderModel[] {
313
390
  if (!Array.isArray(value)) return [];
314
391
  const models: MetaProviderModel[] = [];
@@ -321,12 +398,16 @@ export function restoreProviderModels(value: unknown): MetaProviderModel[] {
321
398
  )
322
399
  continue;
323
400
  const id = nonemptyString(entry.id);
324
- if (!id || seen.has(id)) continue;
401
+ // A catalog persisted by an earlier release may hold non-chat IDs.
402
+ if (!id || seen.has(id) || isNonChatModelId(id)) continue;
325
403
  const fallback = defaultModel(id);
404
+ const bundled = isBundledModelId(id);
326
405
  models.push(
327
406
  gateMuseMaxEffort({
328
407
  ...fallback,
329
- name: nonemptyString(entry.name) ?? fallback.name,
408
+ name: bundled
409
+ ? fallback.name
410
+ : (nonemptyString(entry.name) ?? fallback.name),
330
411
  api: "openai-responses",
331
412
  // The extension only persists the direct endpoint; a cached URL must never redirect the key.
332
413
  baseUrl: META_API_BASE_URL,
@@ -338,8 +419,10 @@ export function restoreProviderModels(value: unknown): MetaProviderModel[] {
338
419
  entry.thinkingLevelMap,
339
420
  fallback.thinkingLevelMap ?? {},
340
421
  ),
341
- input: modelInput(entry.input, fallback.input),
342
- cost: storedCost(entry.cost, fallback.cost),
422
+ input: bundled
423
+ ? fallback.input
424
+ : modelInput(entry.input, fallback.input),
425
+ cost: bundled ? fallback.cost : storedCost(entry.cost, fallback.cost),
343
426
  contextWindow: positiveInteger(
344
427
  entry.contextWindow,
345
428
  fallback.contextWindow,
@@ -9,11 +9,10 @@ export const MUSE_USER_AGENT_MODEL_IDS: ReadonlySet<string> = new Set([
9
9
  ]);
10
10
 
11
11
  /**
12
- * Models known to accept literal max effort with any User-Agent. The Wonder
13
- * models have no bundled definition in models.ts, so max originates here;
14
- * muse-spark-1.3 has one, but is listed so the gate — which runs on every
15
- * model list — is the single authority and a stale cached `max: null`
16
- * cannot mask its fallback-declared support.
12
+ * Models known to accept literal max effort with any User-Agent. Each also
13
+ * has a bundled definition in models.ts that declares max, but they are
14
+ * listed here so the gate — which runs on every model list — is the single
15
+ * authority and a stale cached `max: null` cannot mask that support.
17
16
  */
18
17
  export const MAX_EFFORT_MODEL_IDS: ReadonlySet<string> = new Set([
19
18
  "muse-spark-1.3",