agent-reflex · git:20261104.aa26404 · 2026-11-04 · sha256 7fed695792cbc3f6
agent-reflex git:20261104.aa26404B
Immutable. This exact content never changes and is served at /api/v1/blob/7fed695792cbc3f6.
---
name: agent-reflex
description: Brief on the agent-reflex plugin's capabilities, and install or update the plugin by running its install script (update mode needs no answers)
---
# agent-reflex plugin
An OpenCode plugin that adds a small System One decision model (Jev, nimble, d1,
liquid-d1...) as a classifier to the agent loop. This skill briefs the user on
its capabilities and installs or updates it. The installer also installs the
Claude Code, Codex, Hermes, Qwen Code, Antigravity, Pi and MiMo Code ports when
their CLIs are present.
## Locate the installer
When this skill runs from the repository, the installer is at
`../../../install.sh` relative to this skill's directory, the README at
`../../../README.md`, example configs at `../../../opencode.example.jsonc`,
the source at `../../../plugins/opencode/index.ts`.
When this skill was installed standalone (e.g. via
`npx skills add psygreg/agent-reflex`), the repository is not present: clone
it first and use `<clone>/install.sh` instead:
```sh
git clone https://github.com/psygreg/agent-reflex /tmp/agent-reflex
```
## Requirement
The plugin needs a System One decision model as its classifier, served at
`<baseURL>/systemone`: local llama.cpp (0.6+)/Ollama (0.35+)/LM Studio
(0.4.26+), or TypeSafe's Jev through the TypeSafe API, OpenRouter, the Vercel
AI Gateway or OpenCode Zen. A `fallbackModel` option retries the classifier when
the primary model rejects requests (e.g. a free-model window closing). Every
capability below is driven by this classifier.
## Capabilities (brief the user with this)
- **Decision-driven history compaction**: when the history grows long, the
recent messages are kept verbatim (a keep-window that widens with the task's
complexity), the most load-bearing older messages are kept too (bounded, so a
long session cannot preserve its whole tail), and the rest are folded into a
compact structured digest of the facts they established — so detail is
compressed rather than discarded. Structural messages (tool calls/results) are
scored like any other and only survive when load-bearing.
- **Complexity classification**: every prompt is scored *simple*, *medium* or
*high*; `maxTokens` is set per level and medium/high prompts get a system
note suggesting a confidence check before committing to a solution.
- **Automatic effort control**: when the agent has no non-default model variant
or explicit effort setting, the classified level is written into each primary
request in the dialect the requested model actually speaks, and sets the
matching `maxTokens` cap. User-selected variants and effort settings take
precedence. Wired dialects:
Claude (`output_config.effort`), GPT reasoning models (`reasoning.effort` /
`reasoning_effort`), GLM-5.2+ (`thinking.type` + `reasoning_effort`), Xiaomi
MiMo V2.5+ (`thinking.type`, or `reasoning.effort` / `reasoning_effort` on
the Responses API and gateway routes), Google Gemini 3+ (`thinkingLevel`, or
`reasoning_effort` on OpenAI-compatible routes), Qwen 3.5+ / qwen-plus /
qwen-max (`enable_thinking` + `reasoning_effort` on Chat Completions,
`reasoning.effort` on Responses, `thinking.type` + `output_config.effort` on
the Anthropic surface), Moonshot Kimi 2.7+/K3 (`reasoning_effort` on K3,
`thinking.type` toggle on K2.7/K2.6), xAI Grok 4.3+ (`reasoning_effort`, or
`reasoning.effort` on Responses; `none` disables on grok-4.3 only, `xhigh`
tops out on grok-4.3/4.6+), MiniMax M3+ (`thinking.type: adaptive` stays on
for MiniMax-M3 agent turns because disabling it can yield empty responses;
`reasoning_effort` / `output_config.effort` tunes M3.1-Flash, and
`reasoning.effort` is used on Responses; `none` is rejected), local
llama.cpp/Ollama (`reasoning_effort`, plus `chat_template_kwargs` on llama.cpp). Dialects are
detected per request, so multimodel setups never send a foreign field;
models without a wired dialect stay untouched.
- **Confidence tool**: the `systemone_check_confidence` tool scores the
model's current line of thought and returns CONTINUE or RESTART.
- **Tool exposure**: for each prompt the classifier decides which tool *groups*
the task needs (tools are judged by purpose — web, shell, edit, read, agents,
other — one question per group, not per tool) and the model is given only
those, so the request carries fewer tool schemas and saves input tokens; a
kept group keeps its whole family of tools. The always-keep set is the common
core (read/search/edit/shell/task) plus `reflex_run` and
`systemone_check_confidence`, so the model can always follow the confidence
guidance on complex tasks. Only the long tail (web, exec, subagents…) is
trimmed to what the task clearly needs or the model already used this session.
Everything is exposed if the classifier is unavailable. If filtering hides a
needed tool, `systemone_reveal_tools` lists the agent's tool pool and restores
all its schemas for the current user request.
OpenCode batches complexity, tool-relevance, and compaction-relevance decisions
per user request and reuses them across tool continuations; history-relevance
verdicts persist for the whole session, so each old message is judged by the
classifier at most once. Parallel safety/edit checks share a batch when their
hooks arrive together. Sequential safety and edit checks remain per action
because their inputs are only available then.
- **Tool dispatch** (`reflex_run`): an optional fallback tool for when the model
cannot decide which underlying tool a task needs. It asks the decision model
which tool to use (a `choice` decision), runs it, and returns its real output.
The model calls tools directly whenever it already knows which one fits and
reaches for `reflex_run` only when the correct tool is unclear. The shell
guardrail runs before dispatch. Tool dispatch uses the same configured
classifier model as the rest of the plugin.
- **Shell guardrail**: shell commands are scored for damage potential;
dangerous ones are denied.
- **Syntax checks**: after `edit`/`write` the file is checked with language
parsers (plus a classifier bug check on medium/high), with one retry and
then an ask-the-user stop.
Which features actually reach the agent — and with what caveat — is per agent:
see the support table below.
## Agent support
Eight agents are supported: OpenCode (the reference) plus the ports below. The
same features run in Claude Code (`plugins/claude-code`), Codex
(`plugins/codex`), Hermes (`plugins/hermes`), Qwen Code (`plugins/qwen-code`),
Antigravity (`plugins/antigravity`), Pi (`plugins/pi`) and MiMo Code
(`plugins/mimocode`), which the installer sets up when each CLI is
present. The classifier config is shared (`agent-reflex.jsonc`, same option
names), so the user answers the classifier questions once.
Which features reach each agent — and with what caveat — is in the table below.
Dynamic effort needs the plugin to rewrite the model request, and history
compaction needs it to rewrite the message list — both only reach agents whose hook
API allows that rewrite. OpenCode, Pi and MiMo Code all share the same
decision-driven compaction (Pi and MiMo Code gate it behind `AGENT_REFLEX_PRUNE=1`):
| Agent | Guardrail | Post-edit checks | Compaction guidance | Dynamic effort | History compaction | Tool exposure | Tool dispatch | Key caveat |
|---|---|---|---|---|---|---|---|---|
| OpenCode | hard block | inline | built-in | full (per-model dialects) | decision-driven | yes | yes (`reflex_run`) | Reference implementation; everything works. |
| Pi | hard block | inline | via `context` | full (`setThinkingLevel`) | opt-in decision-driven | yes (`setActiveTools`) | yes (`reflex_run`) | Pi translates the effort level per model itself. |
| MiMo Code | hard block | inline | compacting hook | token cap only | opt-in decision-driven | no | no | `chat.params` has `maxTokens` but no `reasoning_effort`. |
| Claude Code | hard block | inline | fact-extraction guidance | no | digest guidance only | no | no | `PreCompact` extracts fact buckets from the transcript to steer the summary, but cannot rewrite the message list. |
| Qwen Code | hard block | inline | fact-extraction guidance | no | digest guidance only | no | no | Same hook limits as Claude Code. |
| Codex | hard block | inline | inert | no | no | no | no | Compaction hook can only cancel compaction; edits via `apply_patch` (V4A). |
| Antigravity | hard block | one turn late | inert | no | no | no | no | `PostToolUse` cannot inject context (antigravity-cli#808). |
| Hermes | wrapper tool | inline | none | no | no | no | no | Hooks cannot block a tool call; guardrail is a `safe_run` wrapper. |
When installing or updating, tell the user which caveats apply to their agent and
point them at the port's own README (`plugins/<agent>/README.md`) for details.
## Updating an existing installation
`install.sh` detects an existing installation (plugin copy or config entry)
**before asking anything**, so updating needs no answers. Run it with stdin
closed:
```sh
bash ../../../install.sh </dev/null
```
- Only the agent-port plugin files are refreshed: the Claude Code plugin
files under `~/.claude/plugins/agent-reflex/`, the Codex plugin files
under `~/.codex/plugins/agent-reflex/`, the Hermes plugin files under
`~/.hermes/plugins/agent-reflex/`, the Qwen Code extension files under
`~/.qwen/extensions/agent-reflex/`, the Antigravity plugin files under
`~/.gemini/config/plugins/agent-reflex/`, the Pi extension files under
`~/.pi/agent/extensions/agent-reflex/`, and the MiMo Code hook files
under `~/.config/mimocode/hooks/` and `~/.config/mimocode/tools/` — and
their `agent-reflex.jsonc` is left untouched.
- The OpenCode side is NOT copied on updates: with the standard install the
plugin lives in the opencode CLI's package install (`opencode plugin add
github:psygreg/agent-reflex`), so the CLI tracks it (unpinned package
plugins refresh on server startup; `opencode plugin update agent-reflex`
forces it). A legacy local-copy install (`~/.config/opencode/plugins/reflex/`
+ a `"./plugins/reflex"` entry) IS migrated to the standard package, with
its configured options carried over and the stale copy removed. In every
case the configured classifier options are left untouched.
- If it instead prints the setup menu and ends with "No input available
(stdin closed)", there is no installation yet — follow the fresh-install
section below.
- After updating, **tell the user to restart OpenCode** (`opencode service
restart`). Do not restart it yourself: it would terminate this session.
## Fresh install
The installer asks questions in a fixed order and reads the answers from
stdin, so collect them from the user in chat first, then pipe them in one
line each. The exact order:
1. Provider: `1` llamacpp, `2` ollama, `3` lmstudio, `4` typesafe,
`5` openrouter, `6` vercel, `7` opencode (OpenCode Zen), `8` custom.
2. Providers 1-3 only: "Server runs on this machine?" — `Y` or `n`. If `n`,
the next answer is the remote base URL including `/v1`
(e.g. `http://192.168.1.10:8080/v1`). Provider 8 (custom) takes the base
URL here directly.
3. Classifier model. An empty answer takes the preset default (`nimble`,
`jev-latest`, `~typesafe/jev-latest`, `typesafe-ai/jev`, `jev-1.13`);
required for llamacpp and lmstudio (their models are free-form ids).
4. API key, optional, `{env:NAME}` substitution supported and preferred over
literal keys.
5. Fallback classifier model on the same endpoint, optional.
6. Debug log file path, optional.
Example — Jev through the TypeSafe API with the key from the environment:
```sh
printf '4\n\n{env:TYPESAFE_API_KEY}\n\n\n' | bash ../../../install.sh
```
(Colors are automatic and turn off by themselves when output is not a
terminal, so nothing extra is needed when running it from an agent.)
Fresh installs verify the classifier before writing anything: after the
answers are collected the installer asks the endpoint one real System One
question at `<baseURL>/systemone` and refuses to continue when the endpoint
does not answer with the System One decision shape (`answers.<id>.noul`, a
number in [0, 1]). A plain chat/completion server on the same port, an
outdated local runtime, a model that is not a decision model, a wrong port,
or the classic field mix-ups (the model name in the apiKey field, a bare
port as the baseURL, an api key identical to the model id) stop the install
with the exact fix named. When that happens, correct the answers with the
user and re-run — do NOT bypass the check (`AGENT_REFLEX_SKIP_VERIFY=1`)
unless the user asks for it and no other environment can reach the endpoint.
The plugin installs through the OpenCode plugin CLI itself as a standard
package (`opencode plugin add github:psygreg/agent-reflex`; the installer
runs it and then merges the classifier options into the entry the CLI
wrote). Older CLIs without the plugin command fall back to the legacy
local-copy layout — that fallback needs the merge (bun, node bundled with
npm or deno) for `opencode.json(c)`; without any of them the script prints
the entry for a manual paste — in that case, show the printed entry to the
user instead of editing the config blindly.
Report the script's `[agent-reflex]` messages to the user, confirm the merged
entry looks right, and remind them to restart OpenCode — again, do not run
`opencode service restart` from inside the session.