omp packages

omp-discord-presence0.4.0

Discord Rich Presence for omp (oh-my-pi): live status, model logos, GitHub button, subagent party and privacy controls. Works on Windows, macOS, Linux and WSL.

GitHub Actions Published 3 hours ago MIT ~0 downloads/mo

Built for omp

The author tagged this package omp-package.

Did it work for you?

Result

One report per person. A new report replaces your old one.

Reports 0

No reports yet.

Readme

omp-discord-presence

npm license

Discord Rich Presence extension for omp (oh-my-pi) coding agent. See CHANGELOG.md for what changed in each release.

Discord shows the installed omp version, the project name, any active omp mode (plan, goal, loop, vibe), the current state (idle / thinking / using tool), the active tool, the model and its effort (thinking level), with the model maker's logo as a small badge:

OH MY PI
omp v18.15.0 · my-project · plan mode
using tool · bash · anthropic/claude-opus-4 · effort: high
[ View on GitHub ]

Features

  • Live status: idle, thinking, using a tool, waiting for you (approval, ask, plan review), compacting context, retrying, and away after a quiet period.
  • Session details: omp version, project name, active omp modes (plan, goal, loop, vibe), and the model with its effort. A model or effort switch shows within seconds, even without a prompt.
  • Elapsed timer since the latest prompt or since the session started (your choice), and a hover on the omp icon showing how many omp sessions are open.
  • Model logo: the model maker's logo as a small badge, from lobe-icons (156 logos, matched on the model id first, then the provider).
  • GitHub button: View on GitHub for public repos.
  • Subagents as a party: running task/eval subagents show as (3 of 4).
  • Optional extras: git branch (project @ main) and context-window usage (context 42%), both off by default.
  • Privacy:
    • hide any project's name with /discord-presence hide;
    • private GitHub repos are hidden automatically and fail closed;
    • the model, button and branch can each be turned off;
    • goal objectives, plan paths, approval reasons and retry errors are never sent.
  • Settings menu in the omp TUI (/discord-presence settings), with per-project overrides. Every change applies live to all running sessions.
  • Tab completion for every /discord-presence subcommand.
  • Status report (/discord-presence status): exactly what Discord receives, and why a logo or button is missing.
  • Works everywhere: Windows, macOS, Linux and WSL. On WSL, a bundled bridge means no socat relay is needed.

What Discord shows

Status changes reach Discord within about 4 seconds. Updates are spaced out because Discord accepts only 5 activity updates per 20 seconds, and the latest state is always sent last. While tools run in parallel, the status stays on "using tool" until the last one finishes. Automatic context compaction and provider retries get their own status (compacting context, retrying · attempt 2/5). The retry's error message is never shown. The model and effort follow the selection in the omp session: a switch (/model, the model picker, ⇧⇥ for effort) shows within a few seconds, even without a prompt.

The elapsed time on Discord counts from the latest prompt by default. Set Timer to since session start in /discord-presence settings to count from when the omp session opened instead.

Hovering the large omp icon shows the omp version and how many omp sessions are open on this machine, e.g. oh-my-pi v18.15.0 · 2 sessions open. Every omp process counts. Each main session leaves a marker file in discord-presence-sessions/ in omp's agent directory and removes it on exit. Markers left by a crashed process are dropped the next time the sessions are counted.

Active modes are joined with +, and paused modes get (paused), e.g. plan mode + goal mode (paused). Only the mode name is shown: the goal objective and the plan file path stay private.

The current git branch can be added after the project name (my-project @ main; a detached HEAD shows the short commit). It is off by default, because branch names often reveal what you're working on. Turn it on in /discord-presence settings. A branch switch, by the agent or by you in another terminal, shows up after the next turn or within 15 seconds. Hidden projects never show their branch.

After 15 minutes without a prompt (configurable in /discord-presence settings, or never), idle turns into away. The next prompt switches it back to thinking.

Context-window usage can be shown at the end of the status (… · effort: high · context 42%). It is off by default. Turn it on in /discord-presence settings or with /discord-presence context on. The value updates after every turn.

While subagents (task tool, eval agents) are working, Discord shows them as a party after the status line, e.g. using tool · wait · … (3 of 4). The party counts agents, the main one included:

  • First number: agents still working. Second number: all agents in the current batch.
  • Example: a task with 3 subagents starts at (4 of 4), then drops to (3 of 4) and (2 of 4) as they finish, and disappears when the last one is done.
  • Nested subagents are included.
  • The next batch starts a new count.

When omp is blocked on you, the status says so instead of thinking:

  • waiting for approval · bash: a tool call needs your approval. This only happens when tools.approvalMode prompts, e.g. always-ask or write. The approval reason is not shown, because it can quote the command.
  • waiting for your answer: the ask tool is open.
  • waiting for plan review: the plan-mode review is open.

Waiting never turns into away.

GitHub button

When the project is a git repository whose origin remote points to a public GitHub repo, the status gets a View on GitHub button linking to it.

  • When there's no button: non-GitHub remotes, private repos and folders without git get none. Visibility is checked once per session with an anonymous GitHub API request, so a private repo's owner/name is never published.
  • Hidden projects: /discord-presence hide removes the button too.
  • Your own view: Discord never shows your own activity buttons to you. Other people see them on your profile.

Private repos are hidden automatically

When origin points to a private GitHub repo, the project name is replaced with private project on its own; no /discord-presence hide needed. The button is left out too. It works from the same anonymous GitHub check:

  • Fails closed: if the visibility can't be checked (offline, GitHub rate limit), the project is hidden too.
  • No early leak: nothing is sent to Discord until the check finishes, at most 5 seconds.
  • Not covered: origins that aren't recognizably GitHub, such as SSH host aliases like git@github-work:owner/repo or GitHub Enterprise hosts. Hide those with /discord-presence hide.
  • To show a private repo's name anyway: turn off Auto-hide private repos in /discord-presence settings. /discord-presence show says so when a project is auto-hidden.

Works out of the box on Windows, macOS, Linux and WSL. No Discord setup, relay, or socat needed on the user's side.

Requirements

  • The Discord desktop app running on the same machine (the browser version has no Rich Presence).
  • Discord → Settings → Activity Privacy → Share your detected activities with others turned on.

Install

omp install omp-discord-presence

Restart omp and the status appears; on WSL the bundled bridge starts by itself. Run /discord-presence status inside omp to check that it is connected.

Other ways to load it:

omp -e /path/to/omp-discord-presence        # this session only, nothing installed
omp install /path/to/omp-discord-presence   # link a local checkout (run `pnpm install` in it first)

Only the main session talks to Discord. Subagent sessions (task tool, eval agents) never connect; they only report how many of them are running, which is shown as the Discord party.

Update

omp plugin upgrade omp-discord-presence

Then restart omp. omp doesn't check plugins for updates on its own, so run this whenever you want the latest release. To move to a specific version instead:

omp install omp-discord-presence@0.2.0
  • What's kept: settings, hidden projects and per-project overrides live in ~/.omp/agent/discord-presence-state.json, outside the plugin, so updates keep them. The plugin also stays enabled.
  • Linked checkouts (omp install /path) can't be upgraded. Run git pull && pnpm install in the folder, then restart omp.

Uninstall

omp plugin uninstall omp-discord-presence

Restart omp. When the plugin's last session ends, it clears its activity from Discord. This also removes a linked checkout from omp; the folder itself stays.

  • Pause without uninstalling:
    • /discord-presence off stops the presence in every session, and /discord-presence on resumes it;
    • omp plugin disable omp-discord-presence (and enable) stops omp from loading the plugin at all.
  • Remove the saved settings too: rm ~/.omp/agent/discord-presence-state.json.

Platform support

Where omp runs How it reaches Discord
Windows Directly, via Discord's named pipe
macOS / Linux Directly, via Discord's Unix socket
WSL (Discord on Windows) Built-in bridge, nothing to install

WSL

Under WSL, Discord runs on Windows and only exposes the named pipe \\.\pipe\discord-ipc-0. Linux programs can't use that pipe, so the plugin bridges it itself:

  1. On connect, it checks whether $XDG_RUNTIME_DIR/discord-ipc-0 already answers. That happens when Linux Discord is running, you run your own socat relay, or another omp session already started the bridge. If it answers, the plugin uses it.
  2. Otherwise it serves that socket itself and relays each connection through the bundled bin/npiperelay.exe (npiperelay v0.1.0, MIT, see bin/npiperelay.LICENSE).
  3. When the session that owns the bridge exits, other running sessions reconnect within 15 seconds and one of them takes over.

Requirements: WSL interop must be enabled (the default), and Discord must be the stable client. PTB and Canary use a different pipe and aren't bridged.

How it works

Overview

flowchart TD
    subgraph omp["omp session"]
        E1["session_start"]
        E2["agent_start / turn_end / agent_end"]
        E3["tool_execution_start / end"]
        E8["tool_approval / plan_review<br/>requested / resolved"]
        E6["auto_compaction / auto_retry<br/>start / end"]
        E7["mode_changed<br/>(plan / goal / loop / vibe)"]
        E4["/discord-presence commands<br/>+ settings menu"]
        E5["session_shutdown"]
    end

    subgraph subs["subagent sessions (same process)"]
        SA["agent_start / agent_end"]
    end

    subgraph plugin["discord-presence plugin (main session only)"]
        P["presence state<br/>status · detail · model · context"]
        SC["shared subagent counter<br/>running · batch"]
        S[("discord-presence-state.json<br/>global settings · hiddenProjects<br/>per-project overrides")]
        EF["settings for this project<br/>(global + overrides)"]
        T["throttle<br/>0.75 s debounce, 4 s spacing"]
        RC{"GitHub check<br/>finished? (≤ 5 s)"}
        B["build activity"]
    end

    subgraph inputs["read when building"]
        V["omp version"]
        M["live model + effort<br/>(polled every 3 s)"]
        L["lobe-icons logo key"]
        G["GitHub visibility<br/>public → button<br/>private → auto-hide"]
        BR["git branch<br/>(polled every 15 s while shown)"]
        A["app name + icon<br/>(Discord API)"]
        OS["open omp sessions<br/>(marker files, all processes)"]
    end

    E1 & E2 & E3 & E6 & E7 & E8 --> P
    SA --> SC --> P
    E4 -- "on / off / hide / show / settings" --> S
    S -- "file watcher<br/>(all sessions)" --> EF --> P
    P --> T --> RC -- "yes" --> B
    V & M & L & G & BR & A & OS --> B
    B -- "SET_ACTIVITY<br/>details · state · logo · button · party" --> RPC["discord-rpc client"]
    RPC --> D["Discord desktop app"]
    E5 -- "clear activity, close bridge" --> RPC

Subagent sessions (task tool, eval agents) also load the plugin, but they never connect, so they can't overwrite or clear the main session's status. They run in the same process and share the plugin module. Each one bumps a shared counter while its agent loop runs, and the main session shows that count as the Discord party size.

Project name and privacy

What goes in the first line (omp v… · <project> · <modes>) is decided like this; the button follows the same decision:

flowchart TD
    S["session cwd"] --> H{"in hiddenProjects?<br/>(/discord-presence hide)"}
    H -- "yes" --> PP["private project<br/>no button · no branch"]
    H -- "no" --> O{"git origin<br/>on GitHub?"}
    O -- "no" --> N["folder name<br/>no button"]
    O -- "yes" --> API{"anonymous GitHub API"}
    API -- "public (200)" --> NB["folder name<br/>+ View on GitHub"]
    API -- "private (404)<br/>or unknown (offline, rate limit)" --> AH{"Auto-hide private repos<br/>on? (default)"}
    AH -- "yes" --> PP
    AH -- "no" --> N
    N & NB --> BRQ{"Git branch shown<br/>for this project?"}
    BRQ -- "yes" --> NBR["name @ branch"]
    BRQ -- "no" --> NN["name"]

Connecting to Discord

flowchart TD
    C["connect()"] --> Q{"does $XDG_RUNTIME_DIR/discord-ipc-0<br/>answer?"}
    Q -- "yes: Linux Discord, socat,<br/>or another omp session" --> R["discord-rpc login"]
    Q -- "no" --> W{"running in WSL?"}
    W -- "no: Windows / macOS / Linux" --> R
    W -- "yes" --> BR["remove stale socket,<br/>serve discord-ipc-0"]
    BR --> R
    BR -. "each connection" .-> NP["bin/npiperelay.exe"]
    NP -. "\\.\pipe\discord-ipc-0" .-> DW["Discord on Windows"]
    R -- "READY" --> OK["connected: send activity"]
    R -- "error / timeout" --> RT["close client,<br/>retry in 15 s"]
    OK -- "disconnected" --> RT
    RT --> C

If the session that owns the WSL bridge exits, the other sessions lose their connection, retry, and one of them starts a new bridge.

Status states

stateDiagram-v2
    [*] --> offline: plugin loaded
    offline --> idle: session_start
    idle --> working: agent_start
    working --> tool: tool_execution_start
    tool --> tool: another tool starts / ends<br/>while one is still running
    tool --> working: last running tool ends
    working --> idle: agent_end
    tool --> idle: agent_end
    working --> retrying: auto_retry_start
    retrying --> working: auto_retry_end
    idle --> away: no prompt for the away time<br/>(15 min by default)
    away --> working: agent_start
    tool --> waiting: approval requested,<br/>or the ask tool runs
    waiting --> tool: approval resolved
    waiting --> working: ask answered
    working --> waiting: plan review opened
    waiting --> idle: plan review closed
    idle --> compacting: auto_compaction_start
    working --> compacting: auto_compaction_start
    tool --> compacting: auto_compaction_start
    compacting --> idle: auto_compaction_end<br/>(agent not running)
    compacting --> working: auto_compaction_end<br/>(agent running)
    compacting --> tool: auto_compaction_end<br/>(a tool is still running)
Status Text on Discord
offline starting
idle idle
away away (idle for the away time, 15 minutes by default)
working thinking
tool using tool · <tool name>
waiting waiting for approval · <tool> / your answer / plan review
compacting compacting context
retrying retrying · attempt <n>/<max>

Commands

Inside omp:

/discord-presence settings # interactive settings menu
/discord-presence          # toggle on/off globally
/discord-presence status
/discord-presence off
/discord-presence on
/discord-presence refresh
/discord-presence hide     # hide this project's name (alias: private)
/discord-presence show     # show this project's name again (alias: public)
/discord-presence context on|off   # show context-window usage (off by default)

Press Tab after /discord-presence to pick a subcommand from a list with short descriptions. Partial input narrows it, e.g. context o offers context on and context off.

Settings menu

/discord-presence settings opens a menu in the omp TUI. Press Enter on a row to change it; the menu stays open, so several settings can be changed in a row. Esc or Done closes it.

╭─ Discord presence settings ─────────────────────────╮
│  ❯ Editing: all projects                            │
│      Enter switches between global defaults and…    │
│    This project: visible                            │
│    Presence: on                                     │
│    Auto-hide private repos: on                      │
│    Model, effort & logo: shown                      │
│    GitHub button: shown                             │
│    Git branch: hidden                               │
│    Context usage: hidden                            │
│    Timer: since latest prompt                       │
│    Away after: 15 min                               │
│    Done                                             │
╰─────────────────────────────────────────────────────╯
Setting Default Effect
Presence on Show omp activity on Discord, for every session.
This project visible Show the folder name, or private project (same as hide / show). Shows hidden (private repo) when auto-hidden.
Auto-hide private repos on private project when origin is a private GitHub repo, or its visibility can't be checked.
Model, effort & logo shown When hidden, the model, its effort and the model logo are left out; the logo would reveal the model's maker.
GitHub button shown The View on GitHub button for public repos.
Git branch hidden Append @ <branch> to the project name. git is only polled while this is on.
Context usage hidden Append context N%.
Timer since latest prompt What the elapsed time counts from: the latest prompt, or the session start.
Away after 15 min Idle time before away: 5, 10, 15, 30 or 60 minutes, or never.

Every change is saved and applied live to all running sessions. Outside the TUI (print, RPC), settings points to the settings file instead.

Per-project settings

Press Enter on Editing to switch from the global defaults to this project. There, the display settings can be overridden for the current project and its subdirectories, e.g. no GitHub button and no model in a work repo, while other projects keep the global values:

╭─ Discord presence settings ─────────────────────────╮
│  ❯ Editing: this project (work-repo)                │
│    This project: visible                            │
│    Model, effort & logo: hidden · this project      │
│    GitHub button: hidden · this project             │
│    Git branch: shown · this project                 │
│    Context usage: hidden · global                   │
│    Reset this project to the global settings        │
│    Done                                             │
╰─────────────────────────────────────────────────────╯
  • Which settings: Model, effort & logo; GitHub button; Git branch; Context usage. Presence, auto-hide and the away time stay global.
  • · global / · this project: whether the row follows the global value or is overridden here. Enter flips an overridden row back to the global value.
  • Subdirectories: a session in a subdirectory uses the overrides of the closest project above it.
  • Reset: Reset this project to the global settings removes all of the project's overrides.
  • Status report: /discord-presence status lists the overrides in force.

Status report

status shows exactly what Discord receives, and why parts may be missing:

Discord presence: enabled · connected (WSL bridge started by this session)
App: OH MY PI (1566329796068384808)

Sent to Discord:
  omp v18.15.0 · omp-discord-presence
  idle · anthropic/claude-opus-5-5 · effort: high
  logo: claude ✓ uploaded
  button: View on GitHub → https://github.com/hiiruki/omp-discord-presence
  last update 9s ago

Project: omp-discord-presence · visible · branch hidden
Context: hidden (now 2%) · idle 10s, away in 14m 50s
Timer: since the latest prompt · 2 omp sessions open
Project overrides: none
Settings: /home/me/.omp/agent/discord-presence-state.json
  • Connection: direct IPC, the WSL bridge, or a socket another omp session or relay already serves.
  • Logo: checked against the application's public Art Asset list. ✗ not uploaded means the key is missing in the Developer Portal.
  • Button: the GitHub URL, or why there is none (project hidden, no git origin, not on GitHub, private repo, GitHub API unreachable).
  • Away timer: how long the session has been idle and when it turns away.
  • Private data: goal objectives, plan paths, retry errors and the hidden-project list are never shown.

Hidden projects and storage

hide replaces the project name with private project on Discord. It applies to the current directory and everything below it, so a session started in a subdirectory of a hidden project stays hidden. show removes every hidden entry that covers the current directory.

All settings, per-project overrides and the hidden project list are shared by every omp session, and changes apply live to every running session. They are stored in omp's agent directory:

~/.omp/agent/discord-presence-state.json

With omp --profile <name>, that is the profile's agent directory (~/.omp/profiles/<name>/agent/ by default), so each profile keeps its own settings. PI_CODING_AGENT_DIR moves it too.

{
  "enabled": true,
  "hiddenProjects": ["/home/me/work/secret-client"],
  "showContext": false,
  "showModel": true,
  "showButton": true,
  "awayMinutes": 15,
  "timer": "prompt",
  "autoHidePrivate": true,
  "showBranch": false,
  "projects": {
    "/home/me/work/client-app": { "showModel": false, "showButton": false }
  }
}

Troubleshooting

  • /discord-presence status says "not connected": check that the Discord desktop app is running and that activity sharing is on. The plugin retries every 15 seconds.
  • Status stays "not connected" right after restarting omp many times in a row: Discord briefly stops answering when clients reconnect rapidly. Wait a minute.
  • Logo badge is blank: run /discord-presence status. logo: … ✗ not uploaded means that Art Asset is missing from the Discord application (see below). logo: none means no lobe-icons rule matches the model.

Development

The development and release conventions are in AGENTS.md: tooling, code rules, tests, commit format, changelog, versioning and the release steps. omp and other coding agents load it automatically.

pnpm install
pnpm typecheck   # tsc (TypeScript 7)
pnpm lint        # oxlint
pnpm test        # bun test, about 30 s
omp -e .         # try the checkout in a real omp session

The tests drive the extension the way omp does, against a fake Discord IPC server and a stubbed network:

  • Unit tests: GitHub remote parsing (credentials are never captured), model-logo matching, per-project setting precedence.
  • Logo assets: every logo key has a PNG, every PNG is reachable, Discord's 300-asset and 512 px limits are respected.
  • Behaviour: the status flow with parallel tools, approval waiting that never sends the reason, a private repo's name never being sent, the GitHub button and hide, per-project overrides reaching subdirectory sessions live, the subagent party, presence off, and Tab completion.

They run with a throwaway omp agent directory (PI_CODING_AGENT_DIR) and Discord socket (XDG_RUNTIME_DIR). test/preload.ts refuses to run if the override doesn't take, so your real settings and Discord are never touched.

CI (.github/workflows/ci.yml) runs typecheck, lint, the tests and an npm pack dry run on every push to main and every pull request.

Publishing a release (maintainer)

Releases are published by GitHub Actions (.github/workflows/release.yml) when a version tag is pushed:

  1. Add the changes under ## [Unreleased] in CHANGELOG.md, then rename that heading to the new version and date and update the compare links at the bottom.

  2. Bump version in package.json: patch for fixes, minor for features (while below 1.0).

  3. Commit as chore(release): x.y.z, tag vx.y.z, and push both:

    git tag -s vx.y.z -m vx.y.z
    git push origin main vx.y.z
    
  4. The workflow then:

    1. reruns CI;
    2. checks that the tag matches package.json and that CHANGELOG.md has the version;
    3. runs npm publish with provenance;
    4. creates the GitHub release from that changelog section.
  5. Users pick it up with omp plugin upgrade omp-discord-presence.

One-time setup: npm trusted publishing. The workflow publishes without a token, through OIDC. On npmjs.com, open the package → Settings → Trusted Publisher, choose GitHub Actions, and enter:

Field Value
Organization or user hiiruki
Repository omp-discord-presence
Workflow filename release.yml
Environment name npm

The npm environment is created in the GitHub repo on the first run. Add required reviewers to it under Settings → Environments if releases should wait for approval. Publishing by hand still works with npm publish.

The package ships only extensions/, bin/, assets/ and CHANGELOG.md (plus README.md, LICENSE and package.json, which npm always includes), about 1.9 MB. @oh-my-pi/pi-coding-agent and @oh-my-pi/pi-utils are optional peers resolved from the running omp, so installing the plugin doesn't pull a second copy of omp.

Changes to the Discord application (name, icon, Art Assets) reach every user immediately, with no release. New logo rules in extensions/model-icons.generated.ts still need a release.

Discord application (maintainer setup)

Users don't need any of this. Everything below lives in the Discord application whose ID the plugin uses (default 1566329796068384808), so it applies to every user at once.

Name

The activity title ("OH MY PI") is the application name in the Discord Developer Portal. RPC can't change it. After renaming, restart the Discord client so it drops the cached name.

Images

  • Large image: the application's own icon, fetched from Discord at startup.
  • Small image: the logo for the model selected in the omp session, from lobe-icons. The plugin uses lobe's own model rules (modelMappings) in lobe's order, matched against the model id, so Claude through OpenRouter, Copilot or a gateway still shows the Claude logo. If no model rule matches, lobe's provider rules (providerMappings) are tried. Hover the logo to see the model and effort. Models without a logo show no small image.

The logos live in assets/models/ (PNGs from @lobehub/icons-static-png, MIT; the colored variant when lobe has one). Discord can't read local files, so upload them as Art Assets:

  1. Open the Developer Portal, select the app, then go to Rich Presence → Art Assets.
  2. Click Add Image(s) and select the files listed by the sync script below (the first time: every file in assets/models/).
  3. Keep each asset name equal to its file name without .png (for example claude, deepseek), then Save Changes. New assets can take a few minutes to appear.
Supported logos

156 logos from @lobehub/icons@6.13.0. They cover 5309 of the 5949 models in omp's bundled catalog.

Matched against the model id (first matching rule in lobe's order wins; keywords are case-insensitive regexes):

Logo Key Model id contains omp catalog models
openai gpt-3, gpt-4, gpt-5, gpt-oss, o1-, ^o1, /o1, o3-, ^o3, /o3, o4-, ^o4, /o4, text-embedding-, tts-, whisper-, codex, davinci, babbage, omni-moderation, text-moderation, text-adb, text-ada, computer-use, ^gpt-, /gpt-, openai 879
sora sora 1
dalle dalle, dall-e 0
glmv ^glm-(.*)v, /glm-(.*)v, -glm-(.*)v 41
zai ^glm-5, /glm-5, /glm5, -glm-4, ^glm-4, /glm-4, /glm4, -glm-5 316
chatglm ^glm-, /glm-, chatglm, -glm- 12
codegeex ^codegeex, /codegeex 0
claude claude 550
anthropic anthropic 0
aws titan 0
fireworks accounts/fireworks/models/fire 0
internlm internlm, internvl 1
nousresearch deephermes, hermes, genstruct, minos 19
nvidia nemotron, openreasoning, nemoretriever, neva-, nv- 170
meta llama, /l3 193
llava llava 0
nanobanana gemini-3.1-flash-image-preview, gemini-3-pro-image-preview, gemini-\d+(?:\.\d+)?-(?:flash(?:-lite)?|pro)-image(?:-preview)?(?::|$), nanobanana, nano-banana 27
gemini gemini 277
deepmind ^imagen-, /imagen-, ^imagen\d/, /imagen\d 0
gemma gemma 197
moonshot kimi, moonshot 189
qiniu qiniu 0
qwen qwen, qwq, qvq, wanx, wan\d/, wan\d\.\d-, tongyi, gte-rerank 657
minimax minimax, abab, ^image- 186
mistral mistral, mixtral, codestral, mathstral, /mn-, pixtral, ministral, magistral, devstral, voxtral 213
perplexity pplx, sonar 21
yi ^yi-, /yi-, -yi- 4
openrouter ^openrouter 19
relace ^relace-, /relace- 3
arcee ^trinity-, /trinity-, afm-4.5b, caller-large, spotlight, maestro-reasoning, virtuoso-medium-v2, virtuoso-large, coder-large, arcee-blitz 23
essentialai ^rnj-, /rnj- 5
deepcogito ^deepcogito-, /deepcogito-, ^cogito-, /cogito- 3
morph ^morph-, /morph- 3
ai2 ^olmo-, /olmo- 10
inception ^mercury, /mercury 28
openchat ^openchat 0
aya aya 0
cohere command 16
dbrx dbrx 1
stepfun step 47
aimass taichu 0
ai360 360gpt, 360zhinao 0
baichuan baichuan 4
rwkv rwkv, /eagle- 0
wenxin ernie, irag 35
jina ^jina, /jina 1
jimeng ^jimeng-, /jimeng-, seedream, seededit, seedance- 21
doubao ^ep-, doubao- 32
kling ^kling, kling-, klingai 19
hunyuan hunyuan, hy3, hy4 37
fishaudio ^d_, ^g_, ^wd_ 0
bytedance skylark, seed-, bytedance 34
burncloud burncloud 0
stability stable-diffusion, stable-video, stable-cascade, sdxl, stablelm, ^stable-, ^sd3, ^sd2, ^sd1 2
flux flux 12
suno suno 0
microsoft wizardlm, /phi-, ^phi-, -phi-, mai-, microsoft 31
adobe firefly 0
ai21 jamba, ^j2-, ai21 9
upstage ^solar-, /solar 17
palm palm 11
sensenova SenseChat, SenseNova 0
grok ^grok-, /grok- 212
ideogram ideogram, ^v_1, ^v_2, ^v3$, ^upscale$, ^describe$ 0
muse (^|/)muse-(spark|wonder|image|video)($|-) 75
spark spark, general$, generalv3$, generalv3.5$, 4.0ultra$, pro-128k$, ^max-32k$, ^lite$, ^x1$ 2
udio udio 5
unionalpha union-alpha, unionalpha, pareto 15
deepseek deepseek 281
voyage voyage 16
assemblyai assemblyai 0
liquid liquid, lfm 19
inflection inflection- 4
aionlabs aion- 26
aihubmix aihubmix 0
v0 ^v0- 3
vertexai ^veo-, /veo-, ^veo3 3
google google, learnlm, nano-banana 5
cogview cogview 0
kolors kolors 0
baiducloud baidu, qianfan 5
phind phind 0
dolphin dolphin 0
ibm ibm, granite 15
skywork skywork 0
bilibiliindex bilibili-index, index-tts 0
bilibili bilibili 0
lg kmmlu, exaone, lgai 0
tii falcon 1
menlo menlo, lucy, jan-nano 0
longcat longcat 20
kwaipilot kat- 24
nova ^nova-, /nova- 28
xiaomimimo ^mimo-, /mimo- 112
baai ^baai, ^bge-, /bge-, touchd, robobrain 4
ace ace-step 0
cursor ^composer, /composer, -composer 4
typesafeai (^|/)jev($|-), typesafe 14

Fallback matched against the provider id (only when no model rule matches):

Logo Key Provider id contains omp catalog models
alibaba alibaba 0
alephalpha alephalpha 0
antgroup antgroup 0
anyscale anyscale 0
apple apple 0
baidu baidu 0
bailian bailian, bailiancodingplan 0
baseten baseten 0
bilibili bilibili 0
bytedance bytedance 34
centml centml 0
civitai civitai 0
crusoe crusoe 0
deepinfra deepinfra 6
deepmind deepmind 0
empiriolabs empiriolabs 0
exa exa 0
featherless featherless 0
friendli friendli 0
googlecloud googlecloud 0
huawei huawei 0
huaweicloud huaweicloud 0
hyperbolic hyperbolic 0
ibm ibm 15
iflytekcloud iflytekcloud 0
inference inference 0
infermatic infermatic 0
kluster kluster 0
lambda lambda 0
leptonai leptonai 0
llmapi llmapi 0
llmgateway llmgateway 0
lg lg 0
menlo menlo 0
metaai meta 0
microsoft microsoft 31
nplcloud nplcloud 0
nousresearch nousresearch 19
parasail parasail 0
searchapi searchapi 0
snowflake snowflake 0
stability stability 2
statecloud statecloud 0
streamlake streamlake 0
sub2api sub2api 0
submodel submodel 0
targon targon 0
tencent tencent 0
tii tii 1
typesafeai typesafe, typesafe-ai, typesafeai 14
unsloth unsloth 0
yandex yandex 0
aimass aimass 0
aistudio aistudio 0
novita novita 12
openai chatgpt, openai 879
opencode opencode, opencodecodingplan, opencodego, opencodezen 20
perplexity perplexity 21
mistral mistral 213
groq groq 2
together togetherai 0
doubao doubao 32
hunyuan hunyuan 37
nvidia nvidia 170
tencentcloud tencentcloud 0
wenxin wenxin 35
lmstudio lmstudio 0
magpie magpie, magpie 0
grok supergrok 212
higress higress 0
vertexai vertexai 3
jina jina 1
azureai azureai 0
volcengine volcengine, volcenginecodingplan 0
cohere cohere 16
comfyui comfyui 0
cometapi cometapi 0
vercel v0 0
bfl bfl 0
flexai flexai 0
ollama ollamacloud 0
Updating the logos

extensions/model-icons.generated.ts, assets/models/ and the tables above are generated. Don't edit them by hand. Regenerate them after bumping LOBE_ICONS_VERSION / STATIC_PNG_VERSION in scripts/sync-model-icons.ts, or after updating @oh-my-pi/pi-catalog to refresh the omp catalog counts:

pnpm sync-icons

The script:

  1. Reads lobe's modelMappings / providerMappings from @lobehub/icons (pinned version).
  2. Downloads a PNG for every icon those rules can select into assets/models/, not only the ones omp's catalog uses today, so custom gateway models and future models already have a logo. Lobe's other icons (apps and tools no rule points to) are left out; Discord allows 300 Art Assets per application.
  3. Writes the rules in lobe's order. A rule whose icon has no PNG maps to null: no badge rather than a wrong one.
  4. Regenerates the tables above. It resolves every model in omp's bundled catalog (@oh-my-pi/pi-catalog) the same way the extension does, to count coverage.
  5. Compares the result with the application's public Art Asset list and prints which keys to upload and which to delete in the portal.

Environment overrides

# Use your own Discord application (it needs its own Art Assets)
export OMP_DISCORD_CLIENT_ID=1566329796068384808
# Large image: an Art Asset key (at least 512×512) or any image URL
export OMP_DISCORD_LARGE_IMAGE_KEY=omp-logo