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

@vheins/opencode-system-one

v0.2.0

Published

opencode plugin that routes TypeSafe Jev (System One) structured judgments through opencode's plugin hooks: tool guardrails, permission arbitration, cascade routing, and event triage.

Readme

@vheins/opencode-system-one

An opencode plugin that routes TypeSafe Jev (System One) structured judgments through opencode's built-in plugin hooks.

Jev is not a drop-in model replacement. It answers named questions about a piece of state and returns typed results (choice, score, noul) that the plugin branches on to make guardrail decisions: block a destructive tool call, classify a message, triage a session error, and so on.

All Jev calls go through the configured router provider, which exposes a pass-through route at POST /v1/systemone. The router forwards the body untouched and returns the native System One JSON.

How it works

The plugin sits between opencode events and the configured router provider. Each hook sends state plus a named question, receives a typed verdict, then branches. Every answer is validated before it is used; a missing key, missing base URL, timeout, transport error, or malformed verdict is reported as an explicit invalid/unavailable outcome rather than being treated as permission to proceed.

The plugin can only ever add a block to a tool call. It never auto-allows, so opencode's native approval flow remains the sole gate for execution.

Runtime flow

flowchart TD
  A["User message / tool call / event"] --> B["Hook intercepts"]
  B --> C["tool.execute.before / chat.message / compacting / event / jev_decide"]
  C --> D{"Key and base URL set?"}
  D -->|No| E["No-op + warn once"]
  D -->|Yes| F["POST /v1/systemone via router provider"]
  F --> G{"Valid answer within timeout?"}
  G -->|No| H["invalid / unavailable -> per-category policy"]
  G -->|Yes| I["Typed verdict: choice / score / noul"]
  I --> J{"Branch on verdict"}
  J -->|destructive + above threshold| K["Block tool (throw)"]
  J -->|alert level| L["Toast + log"]
  J -->|otherwise| M["No action; native flow decides"]
  H --> M

Verdict types map to hooks in the Hook / event map: choice drives risk, routing, and severity labels; noul drives compaction and file review probabilities; score is available through the tool.jev_decide custom tool.

With and without the plugin

flowchart TD
  subgraph VAN["opencode - no plugin"]
    V1["User message or tool call"] --> V2["Native approval flow"]
    V2 --> V3["Human decides / tool runs"]
    V3 --> V4["No Jev calls"]
  end
  subgraph PLU["opencode + plugin"]
    P1["User message or tool call"] --> P2["Hook consults Jev"]
    P2 --> P3["Adds a block or a toast; never auto-allows"]
    P3 --> P4["No-op on missing config or invalid answer"]
  end

Without the plugin, every decision stays in the native approval flow. With the plugin, the same points consult Jev first and apply an additional guardrail, while a Jev outage or an unconfigured install returns control to the native flow.

Install

From npm (recommended)

Add the package to your opencode config (opencode.json):

{
  "$schema": "https://opencode.ai/config.json",
  "plugin": ["@vheins/opencode-system-one"]
}

opencode installs the plugin with Bun at startup and caches it under ~/.cache/opencode/. Then set the JEV key and base URL (see Configuration) and restart opencode.

To configure the plugin inline instead of via environment variables, use the tuple form and pass an options object as the second array element:

{
  "$schema": "https://opencode.ai/config.json",
  "plugin": [
    [
      "@vheins/opencode-system-one",
      {
        "baseUrl": "https://your-router.example/v1/systemone",
        "model": "oc/jev-1.13-free",
        "blockThreshold": 0.85
      }
    ]
  ]
}

The object form {"package": "...", "options": {...}} is not supported by opencode 1.19.0 (it is dropped by the v1/v2 compatibility layer), and a custom top-level opencode.json key is silently stripped. Only the tuple form delivers options to the plugin.

From source

Clone this repository (or copy both the .opencode/ directory and the root src/ directory, preserving their relative layout). The local plugin entry .opencode/plugins/jev.ts is a thin re-export of ../../src/index, so src/ must sit beside .opencode/:

your-project/
  .opencode/
    plugins/jev.ts        # re-exports ../../src/index
    package.json
    .env.example
  src/
    index.ts              # canonical plugin source

opencode auto-discovers any *.ts file in .opencode/plugins/, so jev.ts loads automatically. Install the plugin's dependencies once:

cd .opencode && bun install   # or: npm install

Then restart opencode (config and plugins load at startup).

Configuration

Every value can be supplied three ways. Resolution precedence, highest first:

  1. opencode.json plugin options (the tuple form above), camelCase keys.
  2. Environment variables (JEV_*), exported in your shell or a .env.
  3. The key file ~/.config/opencode/.jev-key (for the API key only).

The JEV key and the base URL are both required; if neither source supplies one, the guardrails degrade to a no-op that warns once.

| Option (opencode.json) | Environment variable | Default | Purpose | | --- | --- | --- | --- | | apiKey | JEV_API_KEY | — | Bearer key for the router provider. If unset, the plugin falls back to ~/.config/opencode/.jev-key, then no-ops with a warning. | | baseUrl | JEV_BASE_URL | — (required) | Router pass-through route for System One judgments. No default host is shipped; an unset or blank value disables Jev (no-op + warn) rather than calling any host. | | model | JEV_MODEL | oc/jev-1.13-free | Jev model id. | | blockThreshold | JEV_BLOCK_THRESHOLD | 0.85 | Block a destructive tool call when confidence exceeds this value. | | routeThreshold | JEV_ROUTE_THRESHOLD | 0.8 | Toast when chat.message classifies a message as needs_human at or above this confidence. | | severityThreshold | JEV_SEVERITY_THRESHOLD | 0.8 | Toast when a session.error severity resolves to real_bug/fatal at or above this confidence. | | compactThreshold | JEV_COMPACT_THRESHOLD | 0.75 | Append a preservation reminder to compaction context at or above this probability. | | fileReviewThreshold | JEV_FILE_REVIEW_THRESHOLD | 0.8 | Toast when an edited file is judged security-sensitive at or above this probability. |

Thresholds are per category; there is no single global threshold across unrelated decisions. A value outside [0, 1] is ignored and the default is used, so a typo cannot widen a guardrail silently. A wrong-typed option is likewise ignored (the environment value then applies) and logs a one-time warning naming the field, never its value.

Security: prefer env or the key file for the secret

opencode.json is often committed to a repository. Putting apiKey there risks leaking the secret with the config file. Prefer JEV_API_KEY in your shell or ~/.config/opencode/.jev-key for the key, and reserve the options object for non-secret settings (baseUrl, model, thresholds). An option-supplied key is never written to a log line.

Key resolution order: the apiKey option, then the JEV_API_KEY environment variable, then the file ~/.config/opencode/.jev-key. Never commit a real key. The repository ignores .env, *.key, and similar files.

Hook / event map

| Hook or event | Jev question | Behavior | | --- | --- | --- | | tool.execute.before | Choice: read_only | mutating | destructive | Blocks (throws) when the verdict is destructive and confidence > JEV_BLOCK_THRESHOLD. Known read-only tools skip the call. It never auto-allows; opencode's native flow still decides everything else. | | chat.message | Choice: code_only | needs_model | needs_human | Classifies each user message, logs the verdict, and raises a TUI toast on a confident needs_human. This hook is observe-only: it cannot reroute the message, so the verdict is a classification, not routing. | | experimental.session.compacting | Noul: "does this session hold unrecoverable decisions?" | Pushes a preservation reminder line into the summary context when the probability is high. Never replaces the compaction prompt. | | event → session.error | Choice: transient | real_bug | fatal | Logs the severity; raises a toast on a confident non-transient verdict. A MessageAbortedError is ignored. | | event → file.edited | Noul: "is this path security-sensitive / needs review?" | Logs a high verdict and raises a review toast. | | tool.jev_decide (custom tool) | Choice / Score / Noul over arbitrary state | Exposes Jev directly to the agent; returns the typed answer as JSON. |

The plugin does not register a permission.ask handler. That hook is declared in the plugin types but is not triggered by opencode 1.19.0, so any handler would be dead code that appears to arbitrate permissions without doing so.

Safety

The plugin is designed so it can never break opencode's flow and never weakens the native approval gate:

  • Missing key or base URL — every hook degrades to a no-op and logs a warning once. opencode startup never crashes. No default host is ever called.
  • Invalid or unavailable verdict — an absent, malformed, contradictory, out-of-range, timed-out, or disabled answer is reported as invalid/unavailable and carries no verdict. It is never treated as permission to proceed.
  • Per-category failure policy — a non-actionable verdict for a high-impact action (destructive or high-impact category) surfaces a warning; low-risk categories stay silent. Neither case relaxes opencode's native approval flow.
  • Block only — tool.execute.before either throws (deny) or returns (defer). The plugin cannot auto-allow, so a model verdict never bypasses opencode's permission controls.
  • Bounded network — each request carries an AbortSignal.timeout and at most one retry, and only for transient failures (timeouts, connection errors, 429, and 5xx). A deterministic 4xx is never retried.
  • No secret leakage — the API key and request bodies are never written to logs.
  • Validated answers — a choice verdict must carry a known label, a finite confidence in [0, 1], and a probability map that does not contradict the selected label. A noul verdict must carry a probability in [0, 1].

Development

Typecheck and tests run against the source with no build step (Node 24 strips TypeScript):

./node_modules/.bin/tsc --noEmit          # strict typecheck
node --test "test/**/*.test.ts"           # unit + hook integration tests

License

MIT. See LICENSE.