npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

agy-bridge-opencode

v0.6.1

Published

An [opencode](https://opencode.ai) custom provider + plugin that routes chat turns through the `agy` CLI. opencode keeps its UI and session flow; agy owns the conversation, tools, and permissions inside its workdir. Backed by `agy-bridge-engine` for spawn

Downloads

898

Readme

agy-bridge-opencode

An opencode custom provider + plugin that routes chat turns through the agy CLI. opencode keeps its UI and session flow; agy owns the conversation, tools, and permissions inside its workdir. Backed by agy-bridge-engine for spawn, classification, and error semantics. Live agent steps (tools, responses) stream into the reasoning panel as readable progress lines, e.g. ▸ tool view_file (path: src/index.ts)…, ✓ view_file (path: src/index.ts) (0.3s).

Host pin: implements LanguageModelV3 for opencode >=1.15.0 <2 (deps: @opencode-ai/plugin 1.18.30, @ai-sdk/provider 3.0.8). The provider id must stay agy — the plugin's session channel keys on it.

Install

From npm (≥0.2.1) — the package serves both the plugin and the provider factory from the same entrypoint (it re-exports createAgyProvider, which opencode's loader looks for):

// opencode.json
{
  "plugin": ["agy-bridge-opencode"],
  "provider": {
    "agy": {
      "npm": "agy-bridge-opencode",
      "options": { "workdirMode": "session", "timeoutMs": 600000 },
      "models": {
        "default": { "name": "Agy Default" },
        "gemini-3.8-flash": {
          "name": "Gemini 3.8 Flash",
          "variants": {
            "high": { "agyModelId": "gemini-3.8-flash-high" },
            "medium": { "agyModelId": "gemini-3.8-flash-medium" },
            "low": { "agyModelId": "gemini-3.8-flash-low" }
          }
        }
      }
    }
  }
}

Local development (from this repo) — npm must be a file:// URL to the entry file (dist/index.js), and models keys are BARE ids (no provider/ prefix — full ids cause ProviderModelNotFoundError). Verified against opencode 1.18.29: registry-style specs and directory paths are treated as npm coordinates and fail to initialize; file:// URLs to an entry file are imported directly:

{
  "plugin": ["file:///abs/path/to/agy-bridge/packages/opencode-adapter/dist/index.js"],
  "provider": {
    "agy": {
      "npm": "file:///abs/path/to/agy-bridge/packages/opencode-adapter/dist/index.js",
      "options": { "workdirMode": "session", "timeoutMs": 600000 }
    }
  }
}
  • Rebuild after source changes (bun run build in the package) — the host imports dist/, and the module is cached per server process (restart opencode to pick up a rebuild).
  • models in config is what the /model picker renders — and the reliable channel on verified hosts: opencode does NOT consult the plugin's provider.models hook for providers declared via provider.<id>.npm (verified on 1.18.30), so materialize the live agy models catalog into config with scripts/export-config-models.ts (see Effort variants). Legacy flat keys (e.g. gemini-3.8-flash-high) still work as pass-through ids; config entries override names/limits.
  • ⚠️ 0.2.0 is broken in the registry form (missing create* re-export on the entrypoint) — use ≥0.2.1, or the local file:// form above.

Workdir modes

  • scratch (default): each turn runs in a fresh agy-run-* dir under scratchRoot (default: system tmp); dirs older than 7 days are pruned, run.log kept.
  • session: the turn runs directly in the opencode worktree (requires an absolute, existing worktree — config error otherwise). Sessions in non-git directories get opencode's "global" worktree /; the plugin then uses the session directory instead, and the filesystem root itself is always rejected with a typed config error.

Image input (opt-in)

The agy CLI ingests text only, but its agent can inspect on-disk images via view_file. When image input is enabled, the adapter bridges the gap:

  • Extracts image parts from the last user turn (all-or-nothing).
  • Stages them under <workdir>/.agy-attachments/<sha16>.<ext>.
  • Prepends a directive telling the agent to inspect each staged image via view_file before responding.
  • Emits a notice when a staged image was not inspected.

Enable it by setting imageInput: true in the adapter options AND declaring image support on every model:

{
  "provider": {
    "agy": {
      "options": { "imageInput": true },
      "models": {
        "gemini-3.8-flash": {
          "name": "gemini-3.8-flash",
          "modalities": { "input": ["text", "image"], "output": ["text"] }
          // ...variants
        }
      }
    }
  }
}

When imageInput is false (default), image input stays undeclared and attaching an image surfaces an actionable error naming this opt-in and the text alternative. E2E: with it on, pasting an image stages the file and the agent answers about it via view_file; with it off, pasting an image errors and stages nothing.

Interactive session management: TUI modal dialog & shortcuts

The adapter provides a rich in-TUI modal dialog directly over OpenCode's interface, allowing you to inspect, manage, and open the connected Antigravity session without consuming LLM tokens.

Configuration (tui.json)

Register the TUI plugin entry in ~/.config/opencode/tui.json:

{
  "$schema": "https://opencode.ai/tui.json",
  "plugin": [
    "agy-bridge-opencode/tui"
  ]
}

(For local development against this repository, specify the absolute path: "<path-to-repo>/packages/opencode-adapter/dist/tui.js").

Keyboard shortcuts & commands

Inside OpenCode, you can open the session modal anytime via:

  • Keyboard shortcuts: Alt+A or Ctrl+Alt+A.
  • Slash command: /agy-open (aliases: /agy:open, /agy).
  • Ex command: :agy-open or :agy.

Available actions in the modal window

When triggered, a centered modal dialog (api.ui.dialog) appears over the OpenCode screen:

  1. ⚡ Split de terminal (Herdr / Tmux):
    • In Herdr (HERDR_ENV=1): Automatically splits the current pane to the right (herdr pane split --current --direction right --focus) and launches agy --conversation <id> inside the new pane.
    • In tmux ($TMUX): Runs tmux split-window -h "agy --conversation <id>".
    • Falls back gracefully to external terminals or CLI instructions if no multiplexer is active.
  2. 🪟 Abrir en ventana externa: Launches the session in an external terminal window (detects WezTerm, Windows Terminal wt.exe, or standard Linux desktop emulators).
  3. 📜 Ver mensajes recientes de la conversación (Transcript): Opens an in-TUI viewer dialog showing the recent prompts and responses extracted directly from Antigravity's local transcript.jsonl, with full message viewing upon selection.
  4. 📋 Copiar comando CLI: Copies agy --conversation <id> directly to the system clipboard (supports OSC 52, clip.exe on WSL, wl-copy, and xclip).
  5. 📋 Copiar Conversation ID: Copies the UUID of the conversation to clipboard.
  6. 🔄 Ver otras sesiones de agy guardadas: Allows browsing historical conversations from opencode-sessions.json and inspecting or re-linking them.
  7. Estado sin sesión: If the current OpenCode session has not spoken to agy yet, the modal opens informing you of the status and offering to browse and link previous conversations.

LLM tool: open_agy_session

In addition to the TUI modal, the plugin registers the open_agy_session tool (alias agy_open), which allows the assistant or prompt to trigger opening the active agy conversation in an interactive popup.

Effort variants

agy encodes the reasoning effort in model ids as a -high/-medium/-low suffix. The adapter collapses those suffixed ids into their base model: the /model picker shows ONE entry (e.g. agy/gemini-3.8-flash) whose variants (high, medium, low) carry the full agy id passed as --model at turn time. Switching effort mid-session takes effect on the next turn — --model is resolved per call, including on resumed conversations.

Per turn, the --model value is resolved in this order:

  1. Merged payload — opencode merges the selected variant's agyModelId into the call options (providerOptions.agy).
  2. Variant name — the selected variant key (providerOptions.agy.variant).
  3. Fallback — the base model's own default: the HIGHEST discovered effort (a collapsed base has no exact bare agy id to spawn).

Fallbacks are LOUD, not silent: when a base has variants but none was selected (or the variant name is unknown), the adapter emits a provider warning, e.g. model "agy/gemini-3.8-flash" has effort variants but none was selected; using "gemini-3.8-flash-high".

Directly selecting a suffixed id (agy/gemini-3.8-flash-high) still works for legacy pinned configs and existing sessions — it passes through as a flat model whose --model is the full id.

Materializing the catalog into config

The dynamic registry (plugin provider.models hook, backed by agy models with a 24h cache) only applies on hosts that consult that hook — verified opencode versions do not for providers declared via provider.<id>.npm. The reliable path is to regenerate the config fragment and merge it under provider.agy.models:

cd packages/opencode-adapter
bun run scripts/export-config-models.ts   # optional: --bin /path/to/agy

The script prints a JSON fragment to stdout (bare model ids as keys; collapsed bases carry name + variants; default stays flat). Merge it into your opencode.json and restart opencode.

History divergence (re-seeding)

agy owns the conversation: each turn forwards only the last user message and resumes via the stored conversation id. But opencode re-sends the full message array every turn, and if you edit, delete, or reorder earlier messages in the client, the visible thread no longer matches agy's server-side history — agy would answer with stale context silently.

The adapter detects this by keeping per-session baselines of ordered per-message hashes (first 16 hex of sha256 of each forwarded message, in opencode-sessions.json) and comparing them against the incoming array. Since 0.4.0, one opencode sessionID maps to a list of conversation bindings (each { conversationId, hashes?, updatedAt }, capped at 3 — oldest evicted). opencode issues several model calls under one sessionID (side agents, compaction), and each call continues the binding whose baseline is the longest prefix of the incoming hashes instead of overwriting one shared baseline:

  • Linear continuation (a binding's baseline is a prefix of the incoming hashes; longest prefix wins) or no baseline yet → resume as usual.
  • Unknown baseline (a binding stored without hashes — sessions mapped before this feature shipped; v1 single-entry files migrate automatically on load) → adopted as-is for one turn (resuming preserves agy's context), then protection is active from the stored hashes onward.
  • Divergence (no binding baseline is a prefix — earlier messages changed) → a fresh agy conversation is started and the prompt becomes a bounded re-seed: the last 20 text-bearing messages rendered as User: … / Assistant: … inside a guarded block, each text truncated to 4000 chars, followed by your actual message. The reasoning panel shows ⟲ history diverged — new agy conversation seeded. The new conversation id + incoming hashes are appended as a NEW binding — the other bindings (e.g. the main thread's) are untouched.

Responses are CRLF-normalized ( \r\n → \n ) and trailing whitespace at the very end of a response is stripped before it reaches opencode.

Reasoning progress panel

Live agent steps render as sanitized, single-line progress lines:

  • Tool ACTIVE → ▸ tool view_file (path: src/index.ts)…
  • Tool DONE → ✓ view_file (path: src/index.ts) (0.3s)
  • Tool ERROR → ✗ view_file (path: src/index.ts) failed
  • Response ACTIVE with a text delta → ▸ response: <sanitized preview>; without one → ▸ response…
  • Response DONE → ● response (10.0s)

Tool lines carry the first matching tool_info key by precedence (path, AbsolutePath, command, pattern, query, url, …), rendered as key: value; response previews collapse newlines and truncate to 60 chars (sanitizePreview / extractToolParam, both exported from language-model.ts). Rendering is strictly non-throwing: hostile getters, circular structures, or unexpected tool_info shapes degrade through a compact-JSON fallback and a final (step update) line — a bad payload can never crash the stream.

One-way data flow: the panel is presentation-only. The bridge forwards only the last user message text (all opencode history — reasoning parts included — is dropped), and agy owns the conversation in its own SQLite history, resumed via the stored conversation id. Nothing the panel shows is ever sent back to agy, so the upstream KV-cache is unaffected; the only footprint is opencode's local session storage on disk, kept small by the truncated one-line format.

Errors you will see

Missing agy → "install agy" (fatal). Transient outage → retried once automatically. Mid-turn timeout → one silent resume via the stored conversation id; a second failure is fatal and names the run.log path. Auth/quota failures are fatal with re-auth guidance / reset time; agy task failures surface agy's own text.

small_model must point at another provider

opencode uses small_model for titles/summaries on every turn. Those calls would each start an agy-owned conversation, polluting the very sessions your main model relies on (and burning quota). Point it at another provider, e.g. "small_model": "google/gemini-2.5-flash".

Debugging

Every attempt writes run.log next to its workdir: scratch mode → <scratchRoot>/agy-run-*/run.log; session mode → <worktree>/run.log. The session↔conversation store lives at ${XDG_STATE_HOME ?? ~/.local/state}/agy-bridge/opencode-sessions.json: { version: 2, sessions: { [sessionID]: [binding, …] } } with each binding { conversationId, hashes?, updatedAt } (v1 single-entry files migrate on load). Bindings older than 30 days are pruned automatically — a fire-and-forget sweep runs next to the scratch prune every turn, so the store no longer grows unbounded.