@oh-my-pi/pi-catalog 18.2.4 → 18.2.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/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [18.2.5] - 2026-09-17
6
+
7
+ ### Added
8
+
9
+ - Added the `stencil` authentication provider for `omp stream`, supporting OAuth code + PKCE sign-in with `auth.stencil.so`, configurable via `STENCIL_API_KEY`, `STENCIL_AUTH_URL`, and `STENCIL_BASE_URL`. This is an authentication-only provider, not a model provider; OAuth-code login configuration also supports `base-url` and `auth-url` nodes with `{base}` and `{auth}` URL placeholders.
10
+
11
+ ### Fixed
12
+
13
+ - Corrected Yolo-Auto metadata for Qwen Flash: `qwen3.8-flash` and the paid `yolo` route now report the documented 256K context window and use the Qwen chat-template reasoning dialect, with `qwen3.8-flash` as the provider default.
14
+
5
15
  ## [18.2.4] - 2026-09-17
6
16
 
7
17
  ### Added
@@ -1,4 +1,4 @@
1
1
  /** Every provider with an `auth/<id>.kdl` policy. */
2
- export type AuthProviderId = "abliteration" | "aiand" | "aimlapi" | "alibaba-coding-plan" | "alibaba-token-plan" | "amazon-bedrock" | "anthropic" | "azure" | "baseten" | "bedrock-mantle" | "cerebras" | "charm-hyper" | "cline-pass" | "cloudflare-ai-gateway" | "commandcode" | "coreweave" | "cursor" | "deepinfra" | "deepseek" | "devin" | "exa" | "firepass" | "fireworks" | "github-copilot" | "gitlab-duo" | "gitlab-duo-agent" | "gmi-cloud" | "google" | "google-antigravity" | "google-gemini-cli" | "google-vertex" | "groq" | "huggingface" | "kagi" | "kilo" | "kimi-code" | "litellm" | "llama.cpp" | "lm-studio" | "meta" | "minimax" | "minimax-code" | "minimax-code-cn" | "mistral" | "moonshot" | "muse-code" | "nanogpt" | "novita" | "nvidia" | "ollama" | "ollama-cloud" | "openai" | "openai-codex" | "openai-codex-device" | "opencode-go" | "opencode-zen" | "openrouter" | "parallel" | "perplexity" | "qianfan" | "qwen-portal" | "sakana" | "siliconflow" | "siliconflow-cn" | "synthetic" | "tavily" | "together" | "typesafe" | "umans" | "venice" | "vercel-ai-gateway" | "vllm" | "wafer-serverless" | "xai" | "xai-oauth" | "xiaomi" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-cn" | "xiaomi-token-plan-sgp" | "yolo-auto" | "zai" | "zai-coding-plan" | "zenmux" | "zhipu-coding-plan";
2
+ export type AuthProviderId = "abliteration" | "aiand" | "aimlapi" | "alibaba-coding-plan" | "alibaba-token-plan" | "amazon-bedrock" | "anthropic" | "azure" | "baseten" | "bedrock-mantle" | "cerebras" | "charm-hyper" | "cline-pass" | "cloudflare-ai-gateway" | "commandcode" | "coreweave" | "cursor" | "deepinfra" | "deepseek" | "devin" | "exa" | "firepass" | "fireworks" | "github-copilot" | "gitlab-duo" | "gitlab-duo-agent" | "gmi-cloud" | "google" | "google-antigravity" | "google-gemini-cli" | "google-vertex" | "groq" | "huggingface" | "kagi" | "kilo" | "kimi-code" | "litellm" | "llama.cpp" | "lm-studio" | "meta" | "minimax" | "minimax-code" | "minimax-code-cn" | "mistral" | "moonshot" | "muse-code" | "nanogpt" | "novita" | "nvidia" | "ollama" | "ollama-cloud" | "openai" | "openai-codex" | "openai-codex-device" | "opencode-go" | "opencode-zen" | "openrouter" | "parallel" | "perplexity" | "qianfan" | "qwen-portal" | "sakana" | "siliconflow" | "siliconflow-cn" | "stencil" | "synthetic" | "tavily" | "together" | "typesafe" | "umans" | "venice" | "vercel-ai-gateway" | "vllm" | "wafer-serverless" | "xai" | "xai-oauth" | "xiaomi" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-cn" | "xiaomi-token-plan-sgp" | "yolo-auto" | "zai" | "zai-coding-plan" | "zenmux" | "zhipu-coding-plan";
3
3
  /** Providers whose policy declares a `login` flow (the `/login` roster). */
4
- export type LoginProviderId = "abliteration" | "aiand" | "alibaba-coding-plan" | "alibaba-token-plan" | "anthropic" | "baseten" | "cerebras" | "charm-hyper" | "cline-pass" | "cloudflare-ai-gateway" | "commandcode" | "coreweave" | "cursor" | "deepinfra" | "deepseek" | "devin" | "exa" | "firepass" | "fireworks" | "github-copilot" | "gitlab-duo" | "gitlab-duo-agent" | "gmi-cloud" | "google-antigravity" | "google-gemini-cli" | "huggingface" | "kagi" | "kilo" | "kimi-code" | "litellm" | "llama.cpp" | "lm-studio" | "meta" | "minimax-code" | "minimax-code-cn" | "moonshot" | "muse-code" | "nanogpt" | "novita" | "nvidia" | "ollama" | "ollama-cloud" | "openai-codex" | "openai-codex-device" | "opencode-go" | "opencode-zen" | "openrouter" | "parallel" | "perplexity" | "qianfan" | "qwen-portal" | "sakana" | "siliconflow" | "siliconflow-cn" | "synthetic" | "tavily" | "together" | "typesafe" | "umans" | "venice" | "vercel-ai-gateway" | "vllm" | "wafer-serverless" | "xai" | "xai-oauth" | "xiaomi" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-cn" | "xiaomi-token-plan-sgp" | "yolo-auto" | "zai" | "zai-coding-plan" | "zenmux" | "zhipu-coding-plan";
4
+ export type LoginProviderId = "abliteration" | "aiand" | "alibaba-coding-plan" | "alibaba-token-plan" | "anthropic" | "baseten" | "cerebras" | "charm-hyper" | "cline-pass" | "cloudflare-ai-gateway" | "commandcode" | "coreweave" | "cursor" | "deepinfra" | "deepseek" | "devin" | "exa" | "firepass" | "fireworks" | "github-copilot" | "gitlab-duo" | "gitlab-duo-agent" | "gmi-cloud" | "google-antigravity" | "google-gemini-cli" | "huggingface" | "kagi" | "kilo" | "kimi-code" | "litellm" | "llama.cpp" | "lm-studio" | "meta" | "minimax-code" | "minimax-code-cn" | "moonshot" | "muse-code" | "nanogpt" | "novita" | "nvidia" | "ollama" | "ollama-cloud" | "openai-codex" | "openai-codex-device" | "opencode-go" | "opencode-zen" | "openrouter" | "parallel" | "perplexity" | "qianfan" | "qwen-portal" | "sakana" | "siliconflow" | "siliconflow-cn" | "stencil" | "synthetic" | "tavily" | "together" | "typesafe" | "umans" | "venice" | "vercel-ai-gateway" | "vllm" | "wafer-serverless" | "xai" | "xai-oauth" | "xiaomi" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-cn" | "xiaomi-token-plan-sgp" | "yolo-auto" | "zai" | "zai-coding-plan" | "zenmux" | "zhipu-coding-plan";
@@ -12,10 +12,6 @@ export declare class AmbiguousOverlapError extends Error {
12
12
  readonly second: string;
13
13
  constructor(provider: string, model: string, axis: string, first: string, second: string);
14
14
  }
15
- /**
16
- * Anchored `*`-wildcard match; both sides must be pre-lowercased. `*` spans
17
- * any substring; non-wildcard text stays anchored in order.
18
- */
19
15
  export declare function globMatch(pattern: string, value: string): boolean;
20
16
  /**
21
17
  * Resolve wire, thinking, and catalog assignments for one structured target.
@@ -52,13 +52,6 @@ export declare function collapseVariantId(provider: string, model: string): Coll
52
52
  * `non-thinking` and `no-thinking` are not variants.
53
53
  */
54
54
  export declare function stripThinkingVariantSuffix(model: string): string | undefined;
55
- /**
56
- * Classifies a model into its structured identity: reviewed override first,
57
- * then suffix collapse, then class/family/revision ranks over the logical id.
58
- *
59
- * @throws AmbiguousIdentityError on equal-rank cross-class or cross-family
60
- * matches unless `opts.lenient`.
61
- */
62
55
  export declare function classifyModel(provider: string, modelId: string, opts?: ClassifyOptions): ModelIdentity;
63
56
  /**
64
57
  * Strips a declared billing-variant suffix (`-free`, `-contributor`) from a
@@ -439,6 +439,10 @@ export interface CompiledOAuthCodeLogin {
439
439
  kind: "oauth-code";
440
440
  clientId?: CompiledAuthValue;
441
441
  clientSecret?: CompiledAuthValue;
442
+ /** `{base}` placeholder source (the provider's API origin). */
443
+ baseUrl?: CompiledAuthValue;
444
+ /** `{auth}` placeholder source for the authorize, token, and userinfo URLs when the issuer is a separate host. */
445
+ authUrl?: CompiledAuthValue;
442
446
  authorizeUrl: CompiledAuthValue;
443
447
  scopes: string[];
444
448
  scopeSeparator: string;
@@ -5,4 +5,4 @@ import type { ModelTokenizer } from "./types.js";
5
5
  * This is catalog policy, not a runtime caller heuristic: [`buildModel`](./build.ts)
6
6
  * materializes the result as `Model.tokenizer`; consumers read that property.
7
7
  */
8
- export declare function resolveModelTokenizer(modelId: string): ModelTokenizer | undefined;
8
+ export declare function resolveModelTokenizer(modelId: string, provider?: string): ModelTokenizer | undefined;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-catalog",
4
- "version": "18.2.4",
4
+ "version": "18.2.5",
5
5
  "description": "Model catalog for omp: bundled model database, provider discovery descriptors, model identity, classification, and equivalence",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Stencil Labs, Inc.",
@@ -35,12 +35,12 @@
35
35
  "gen:proto": "bun scripts/generate-protocols.ts"
36
36
  },
37
37
  "dependencies": {
38
- "@oh-my-pi/omptype": "18.2.4",
39
- "@oh-my-pi/pi-utils": "18.2.4"
38
+ "@oh-my-pi/omptype": "18.2.5",
39
+ "@oh-my-pi/pi-utils": "18.2.5"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@bgotink/kdl": "0.4.0",
43
- "@oh-my-pi/pi-ai": "18.2.4",
43
+ "@oh-my-pi/pi-ai": "18.2.5",
44
44
  "@types/bun": "^1.3.14"
45
45
  },
46
46
  "engines": {
package/src/build.ts CHANGED
@@ -290,7 +290,7 @@ export function buildModel<TApi extends Api>(spec: ModelSpec<TApi>): Model<TApi>
290
290
  name: cleanModelName(spec.name),
291
291
  identity: policy.identity,
292
292
  requiresGlyphTokenization: policy.identity.class === "anthropic",
293
- tokenizer: spec.tokenizer ?? resolveModelTokenizer(spec.requestModelId ?? spec.id),
293
+ tokenizer: spec.tokenizer ?? resolveModelTokenizer(spec.requestModelId ?? spec.id, spec.provider),
294
294
  thinking: policy.thinking,
295
295
  supportsComputerUse: supportsOpenAIGAComputerUse(spec, policy.identity, supportsComputerUseConfig),
296
296
  supportsComputerUseConfig,
@@ -66,6 +66,7 @@ export type AuthProviderId =
66
66
  | "sakana"
67
67
  | "siliconflow"
68
68
  | "siliconflow-cn"
69
+ | "stencil"
69
70
  | "synthetic"
70
71
  | "tavily"
71
72
  | "together"
@@ -143,6 +144,7 @@ export type LoginProviderId =
143
144
  | "sakana"
144
145
  | "siliconflow"
145
146
  | "siliconflow-cn"
147
+ | "stencil"
146
148
  | "synthetic"
147
149
  | "tavily"
148
150
  | "together"
@@ -38,21 +38,36 @@ export class AmbiguousOverlapError extends Error {
38
38
  /**
39
39
  * Anchored `*`-wildcard match; both sides must be pre-lowercased. `*` spans
40
40
  * any substring; non-wildcard text stays anchored in order.
41
+ *
42
+ * Rule-owned patterns are static after index build, so the split is memoized
43
+ * per pattern string; derived/live patterns share the same bounded cache.
41
44
  */
42
- export function globMatch(pattern: string, value: string): boolean {
45
+ const globSegmentsCache = new Map<string, readonly string[]>();
46
+ const GLOB_SEGMENTS_MAX = 4096;
47
+
48
+ function globSegments(pattern: string): readonly string[] {
49
+ const cached = globSegmentsCache.get(pattern);
50
+ if (cached !== undefined) return cached;
43
51
  const segments = pattern.split("*");
52
+ if (globSegmentsCache.size >= GLOB_SEGMENTS_MAX) globSegmentsCache.clear();
53
+ globSegmentsCache.set(pattern, segments);
54
+ return segments;
55
+ }
56
+
57
+ export function globMatch(pattern: string, value: string): boolean {
58
+ const segments = globSegments(pattern);
44
59
  if (segments.length === 1) return value === pattern;
45
- const head = segments[0];
60
+ const head = segments[0] ?? "";
46
61
  if (!value.startsWith(head)) return false;
47
62
  let remainder = value.slice(head.length);
48
63
  for (let i = 1; i < segments.length - 1; i++) {
49
- const segment = segments[i];
64
+ const segment = segments[i] ?? "";
50
65
  if (!segment) continue;
51
66
  const found = remainder.indexOf(segment);
52
67
  if (found === -1) return false;
53
68
  remainder = remainder.slice(found + segment.length);
54
69
  }
55
- const last = segments[segments.length - 1];
70
+ const last = segments[segments.length - 1] ?? "";
56
71
  return last === "" || remainder.endsWith(last);
57
72
  }
58
73
 
@@ -310,6 +310,8 @@ auth "anthropic" {
310
310
  env hook="anthropic-foundry" // or: env "ANTHROPIC_OAUTH_TOKEN" "ANTHROPIC_API_KEY"
311
311
  login "oauth-code" {
312
312
  client-id "OWQxYzI1…" encoding="base64" // env="VAR" adds an override; child `env "A" "B"` an ordered list
313
+ base-url "https://api.example" { env "X_BASE_URL" } // optional; `{base}` placeholder (API origin)
314
+ auth-url "https://auth.example" { env "X_AUTH_URL" } // optional; `{auth}` placeholder for authorize/token/userinfo
313
315
  authorize-url "https://claude.ai/oauth/authorize"
314
316
  scopes "org:create_api_key" "user:profile" // separator=" " default
315
317
  pkce #true
@@ -10,4 +10,4 @@ login-order "openai-codex" "anthropic" "zai" "zai-coding-plan" "kimi-code" "open
10
10
  "qianfan" "venice" "siliconflow" "siliconflow-cn" "synthetic" "nanogpt" "wafer-serverless" "coreweave" \
11
11
  "vercel-ai-gateway" "cloudflare-ai-gateway" "litellm" "kilo" "zenmux" "opencode-zen" "opencode-go" \
12
12
  "yolo-auto" "tavily" "kagi" "exa" "parallel" "typesafe" "ollama" "ollama-cloud" "lm-studio" "llama.cpp" "vllm" \
13
- "gmi-cloud"
13
+ "gmi-cloud" "stencil"
@@ -0,0 +1,36 @@
1
+ // stencil.so account. Not a model provider: the credential authorizes
2
+ // omp-hosted Stencil services such as `omp stream` (live.omp.sh).
3
+ //
4
+ // Two hosts: the Stencil API at api.stencil.so (`{base}`, STENCIL_BASE_URL)
5
+ // and the OIDC issuer at auth.stencil.so (`{auth}`, STENCIL_AUTH_URL) which
6
+ // serves the PKCE-mandatory authorization codes under /oauth/* and mints
7
+ // ES256 access tokens. The public client id `omp` is code-defined in the
8
+ // issuer with a port-flexible 127.0.0.1 loopback callback.
9
+ auth "stencil" {
10
+ name "Stencil (invite only)"
11
+ env "STENCIL_API_KEY"
12
+ login "oauth-code" {
13
+ client-id "omp" env="STENCIL_CLIENT_ID"
14
+ base-url "https://api.stencil.so" {
15
+ env "STENCIL_BASE_URL"
16
+ }
17
+ auth-url "https://auth.stencil.so" {
18
+ env "STENCIL_AUTH_URL"
19
+ }
20
+ authorize-url "{auth}/oauth/authorize"
21
+ scopes "openid" "profile" "email" "offline_access"
22
+ pkce #true
23
+ instructions "Sign in to your stencil.so account in the browser. If the browser cannot reach this machine, paste the final redirect URL when prompted."
24
+ callback port=54547 path="/callback" hostname="127.0.0.1" redirect-uri-env="STENCIL_REDIRECT_URI"
25
+ token url="{auth}/oauth/token" body="form"
26
+ credential {
27
+ access "access_token"
28
+ refresh "refresh_token"
29
+ expires "seconds" path="expires_in" skew-ms=300000
30
+ }
31
+ userinfo url="{auth}/oauth/userinfo" email="email" account-id="sub"
32
+ }
33
+ refresh {
34
+ token url="{auth}/oauth/token" body="form"
35
+ }
36
+ }
@@ -1,17 +1,22 @@
1
1
  // Provider-wire compat for "yolo-auto"; wire constraints apply to every discovered model.
2
2
 
3
3
  provider "yolo-auto" {
4
- default-model "deepseek-flash-v4"
4
+ default-model "qwen3.8-flash"
5
5
  env "YOLO_AUTO_API_KEY"
6
6
  dynamic-models-authoritative #true
7
7
  discovery label="Yolo-Auto"
8
8
 
9
- // Documented Yolo-Auto catalog (yolo-auto.com/docs, 2026-08) bundled so the
10
- // provider is usable when generation and first boot have no live key. The
11
- // flat-rate `/v1/models` response is authoritative once discovery runs.
12
- // The compat block mirrors the provider's documented wire surface: the API
13
- // speaks the generic chat template with `reasoning_effort` support and rejects
14
- // the `developer` role and `store` param.
9
+ // Documented Yolo-Auto catalog (yolo-auto.com/models, yolo-auto.com/docs):
10
+ // `qwen3.8-flash` is the default model on Free and paid plans at a 256K
11
+ // (262144) context window, and the paid `yolo` route is initially backed by
12
+ // the same Flash deployment. The flat-rate `/v1/models` response carries
13
+ // bare ids with no limit fields and is authoritative once discovery runs.
14
+ // Without provider-local seeds the bare `qwen3.8-flash` slug resolves
15
+ // through the global reference index, which prefers the largest bundled
16
+ // window (1M) and misreports the deployment cap. The compat block mirrors
17
+ // the provider's documented wire surface: the API speaks the Qwen
18
+ // chat-template dialect with `reasoning_effort` support and rejects the
19
+ // `developer` role and `store` param.
15
20
  seed api="openai-completions" base-url="https://yolo-auto.com/v1" bundle="fallback" {
16
21
  model "deepseek-flash-v4" name="DeepSeek Flash V4" {
17
22
  reasoning #true
@@ -26,6 +31,32 @@ provider "yolo-auto" {
26
31
  supports-reasoning-effort #true
27
32
  thinking-format "chat-template"
28
33
  }
34
+ model "qwen3.8-flash" name="Qwen3.8 Flash" {
35
+ reasoning #true
36
+ input "text" "image"
37
+ cost input=0 output=0 cache-read=0 cache-write=0
38
+ limits context=262144 max-tokens=131072
39
+ thinking-mode "effort"
40
+ thinking-efforts "minimal" "low" "medium" "high"
41
+ supports-developer-role #false
42
+ supports-store #false
43
+ supports-reasoning-effort #true
44
+ thinking-format "qwen-chat-template"
45
+ template-reasoning-effort #true
46
+ }
47
+ model "yolo" name="Yolo" {
48
+ reasoning #true
49
+ input "text" "image"
50
+ cost input=0 output=0 cache-read=0 cache-write=0
51
+ limits context=262144 max-tokens=131072
52
+ thinking-mode "effort"
53
+ thinking-efforts "minimal" "low" "medium" "high"
54
+ supports-developer-role #false
55
+ supports-store #false
56
+ supports-reasoning-effort #true
57
+ thinking-format "qwen-chat-template"
58
+ template-reasoning-effort #true
59
+ }
29
60
  }
30
61
 
31
62
  supports-developer-role #false
@@ -12,6 +12,7 @@ class "qwen" {
12
12
 
13
13
  revision prefix="qwen"
14
14
 
15
+ override id="yolo-auto-yolo-flash-identity" provider="yolo-auto" model="yolo" class="qwen" revision="3.8" rationale="Yolo-Auto's paid yolo route is initially backed by the same Qwen3.8 Flash deployment as qwen3.8-flash; the opaque alias carries no lineage tokens" provenance="yolo-auto.com/models (2026-09)"
15
16
  override id="kilo-qwq-32b-family" provider="kilo" model="qwq-32b" logical="qwen/qwq-32b" class="qwen" rationale="The reviewed Kilo QwQ deployment belongs to the Qwen family despite its opaque product spelling" provenance="fixtures/llm-oracle/catalog/models.normalized.json"
16
17
  override id="nanogpt-eva-qwen-2-5-family" provider="nanogpt" model="EVA-Qwen2.5-32B-v0.2" logical="EVA-UNIT-01/EVA-Qwen2.5-32B-v0.2" class="qwen" rationale="The reviewed EVA deployment is a Qwen 2.5 derivative despite its opaque product namespace" provenance="fixtures/llm-oracle/catalog/models.normalized.json"
17
18
  override id="nanogpt-eva-qwen-2-5-72b-family" provider="nanogpt" model="EVA-Qwen2.5-72B-v0.2" logical="EVA-UNIT-01/EVA-Qwen2.5-72B-v0.2" class="qwen" rationale="The reviewed EVA 72B deployment is a Qwen 2.5 derivative despite its opaque product namespace" provenance="fixtures/llm-oracle/catalog/models.normalized.json"