Files
command-center/agent/managed-skills/omp-provider-row-sources/SKILL.md
T

5.7 KiB

name, description
name description
omp-provider-row-sources Add a provider/model row to ~/.omp/agent/models.yml by reading the harness's own catalog tables (provider name, api, window, cost, compat) instead of guessing or web-searching, then prove it with a scratch-HOME call.

Scope

Adding (or correcting) a provider/model row in ~/.omp/agent/models.yml for a provider the harness may already know: an upstream vendor API (DeepSeek, OpenAI, Anthropic, Moonshot, …) or a new local endpoint. The rule this skill exists for: almost every field you are about to research on the web already exists in the installed bundle — research there first, and the web only confirms it.

Canonical install root (resolve the symlink first if the repo lives under a worktree): /home/blank/.bun/install/global/node_modules/@oh-my-pi/.

The three tables that own the facts

You want Read Why
The legal provider name, its URL marker, and which quirks the harness gates on it pi-catalog/src/hosts.ts (minified in dist/cli.js as the VIn map; consumed by pi-catalog/src/compat/resolve.ts) The provider key is a lookup key, not a display name. e.g. deepseekDirect: { providers: ["deepseek"], urlMarkers: ["api.deepseek.com"] }, anthropic: { providers: ["anthropic"], urlMarkers: ["api.anthropic.com"] }. A label matching neither loses the behaviour set silently (the max_tokens field name, the thinking extraBody, the thinking-disable mode).
Model rows: ids, contextWindow, maxTokens, cost rates, per-model compat pi-catalog/src/provider-models/<api>.ts — openai-compat.ts, anthropic-messages.ts, …; factory helpers openAiCompletionsDescriptor(...) / anthropicMessagesDescriptor(...) Rows are fetched from the provider's models-dev key, so the bundle carries the same numbers the vendor publishes (and often rates the vendor page hides). The descriptor's compat: block is the authored truth for that provider's transport shape — copy it, never simplify it.
The schema itself (which keys are legal on a provider vs a model, and which are required) pi-coding-agent/src/config/models-config-schema-bundle.ts (and the identical dist/ copy) Provider level and model level have different field sets. Cheaper and more correct than reading the minified bundle.

Also in pi-coding-agent/src/config/: model-registry.ts (how models.yml merges onto the built-in catalog — #mergeCustomModels → mergeByModelKey), resolve-config-value.ts (how apiKey values resolve).

Two traps this table pays for

  • Provider-level api only accepts chat/judge transports. Naming a runner API (openai-speech, openai-images, openai-embeddings) at provider level fails whole-file validation and disables every custom provider, not just yours (omp models prints "models.yml validation failed", lists only built-ins). Runner api belongs on the model entry; see the managed skill omp-models-yml-runner-api-placement.
  • A models.yml entry is an overlay, not a replacement. Adding one model to a provider the catalog already ships does not remove the provider's other rows — omp models <provider> will show yours (yours wins on shared fields, including cost and contextWindow) plus the catalog rows. Same-id rows patch with the replace transport policy; a row that omits contextWindow inherits the bundled row's window and its policy tiers.

Secret handling (non-prose tracked file)

Never inline the credential. apiKey resolution order is: leading ! → command (stdout trimmed, cached for the process lifetime) → exact env-var name → literal. So write apiKey: "!cat ~/.config/secrets/<name>_api_key" with mode 600.

  • A bare sk-… trips the pre-commit shape gate (every file type).
  • An unquoted ! is a YAML tag — quote it.
  • A quoted command value also dodges the assignment gate: the gate's value charset rejects ~ and spaces, so only a literal key matches.

Procedure

  1. Read the three tables above for the provider you are adding. Take the exact provider name, base URL, api, and compat from them; take contextWindow / maxTokens / cost from the model row and cite the source in a comment.
  2. Write the secret to ~/.config/secrets/<name>_api_key, chmod 600.
  3. Edit models.yml; for a repo-tracked config, follow that repo's own branch/log/index rules (in dank/command-center: branch → issue → PR).
  4. Verify with a scratch HOME, because models.yml loads at process start and a live session keeps the old registry:
    mkdir -p /tmp/ompx/.omp/agent /tmp/ompx/.config
    cp agent/models.yml agent/config.yml /tmp/ompx/.omp/agent/
    ln -sfn ~/.config/secrets /tmp/ompx/.config/secrets   # ~ in a !cmd value = $HOME
    HOME=/tmp/ompx omp models --no-extensions | grep -c 'validation failed'   # must be 0
    HOME=/tmp/ompx omp --model '<provider>/<id>:off' --tools bash --no-session \
      --no-skills --no-rules --no-extensions --no-pty --no-lsp --no-title \
      --cwd /tmp -p 'Reply with exactly the word OK and nothing else.'
    
    Then the negative control, the step that separates a real auth success from a cached/built-in answer: sed the baseUrl to a dead port in the scratch copy and repeat — it must hang or error, not answer. Then rm -rf /tmp/ompx (it holds endpoint URLs).

Voice notes

  • Prefer the bundled schema file over grepping dist/cli.js; grep the bundle only when the source and the bundle disagree, or for a runtime default the schema does not state (e.g. reasoningDisableMode per provider family).
  • cost is optional in the schema. Omitting it makes the status line report $0 forever; one rate per direction is all the schema models, so a peak/off-peak vendor table can only carry the peak value.