pi-jev

September 20, 2026 · View on GitHub

pi-jev

Selective context compaction and per-turn model routing for pi, powered by Jev.

English · 简体中文

Pi events trigger typed Jev requests: keep scores select context, and a difficulty score selects a model

checks pi extension Jev license

pi-jev uses Jev's typed judgments to decide which tool outputs still matter and how demanding a user request is. It retains useful text instead of generating a new compaction summary, and can switch models before each turn. Both features are optional; if Jev is unavailable, pi continues with its normal compaction or the current model.

ExtensionPurposePi integration
CompactionRemove obsolete tool calls, shorten unneeded results, and retain remaining text verbatimsession_before_compact
RoutingSend easy requests to a cheaper model and hard requests to a stronger modelbefore_agent_start
/jev settingsConfigure providers, compaction, and routing from an interactive menu/jev command

Each extension can be enabled or disabled independently through pi config. Configuration changes are read on the next hook, without restarting pi.

Quick start

You need pi installed and an API key for one Jev transport: TypeSafe or OpenRouter. Routing targets also need to be configured and authenticated in pi.

1. Configure a Jev API key

Choose one option in the shell where you will launch pi:

# Option A: TypeSafe
export TYPESAFE_API_KEY="your-typesafe-api-key"
# Option B: OpenRouter
export OPENROUTER_API_KEY="your-openrouter-api-key"

The provider is auto-detected when no provider is explicitly configured. If both keys are present, TypeSafe wins; set JEVC_PROVIDER=openrouter to select OpenRouter explicitly.

2. Install and launch

pi install npm:@alexlikevibe/pi-jev
pi config   # Review which pi-jev extensions are enabled
pi

If you install into an already running pi session, use /reload to load the extensions.

3. Configure inside pi

/jev

Open Routing, choose a cheap and/or strong target from pi's model list, then optionally choose a thinking level. Routing stays inactive until at least one target is set. Compaction needs no model-target configuration; it runs with /compact or pi's automatic compaction.

TaskCommand or action
Open settings/jev
Compact the current session/compact
Inspect available settings/jev keys
Read a routing target/jev get routing.cheap
Remove a routing target/jev unset routing.strong
Temporarily bypass both features/jev set disabled true
Re-enable both features/jev set disabled false
Write a project settingAppend -l, e.g. /jev set provider openrouter -l

Settings writes are global by default, including disabled; use -l for project scope. To bypass both features for one pi process, launch it with JEVC_DISABLED=1 pi.

Install from GitHub, a checkout, or load a single extension

To install directly from GitHub:

pi install https://github.com/iefnaf/pi-jev

From a local checkout:

git clone https://github.com/iefnaf/pi-jev.git
cd pi-jev
npm ci
npm run build
pi install /absolute/path/to/pi-jev

For a development run from the repository root:

pi -e ./extensions/compaction.ts
pi -e ./extensions/routing.ts
pi -e ./extensions/jev.ts

Each command loads one extension explicitly. The project's .pi/extensions/jev-* loaders are also auto-discovered once the repository is trusted.

Context compaction

Instead of asking a summarization model to rewrite the conversation, pi-jev asks Jev typed questions about each eligible tool call: whether to keep the call, whether to keep its full result (noul), and how stale the result is (score). It then renders the retained content as a transcript.

  • Keep: retain user and assistant text and useful tool calls/results. The previous compaction summary is included as-is.
  • Drop: remove obsolete tool calls together with their results. The transcript header reports the removal count.
  • Shorten: retain a tool call but shorten a long result to its first truncateHeadChars characters, followed by an explicit truncation marker. Short results may remain unchanged.
  • Rescue: preserve a borderline result if a confident staleness judgment says it is still needed.
  • Fall back: let pi generate its normal summary when Jev fails, the request is aborted, the span is empty, or the estimated reduction is below minReduction. Missing or malformed per-call answers conservatively retain the affected call.

Requests are batched to fit the configured state and request budgets; a compaction can use multiple Jev requests. Recency is pi's own job: pi keeps the newest compaction.keepRecentTokens tokens out of the span before the hook runs, so every converted message is scored. The one exception is a split turn: when pi cuts inside a turn and the span ends on a tool result, that boundary call is never dropped outright — pi-jev keeps the call metadata and a bounded head of its result, so the retained tail keeps its causal context without carrying a large output verbatim.

Verbatim applies to retained text, not every original message field: images become [image] placeholders and assistant thinking blocks are omitted during conversion. Tool inputs are serialized into the transcript.

Example result

An example run recorded in this project reduced a converted span from 32 messages / 39,379 characters to 16 messages / 9,553 characters in 757 ms, using one Jev request and 652 output tokens. This is an illustrative result, not a latency or reduction guarantee.

Successful compactions store audit data in the session's compaction entry:

{
  "engine": "jev",
  "stats": {
    "messagesBefore": 32,
    "messagesAfter": 16,
    "charsBefore": 39379,
    "charsAfter": 9553,
    "calls": 15,
    "boundary": 1,
    "callsDropped": 12,
    "ms": 757,
    "requests": 1,
    "jevUsage": { "input": 9784, "output": 652 }
  }
}

This abbreviated example is the entry's details object. Inspect details.engine, details.stats, and details.decisions for the full outcome. The hook preserves firstKeptEntryId so pi can retain the rest of the session correctly.

Model routing

Before a turn, Jev evaluates the current user prompt on a three-level difficulty rubric:

LevelTypical request
0 — trivialGreetings, quick questions, formatting, mechanical single-file edits
1 — moderateEveryday coding tasks
2 — complexMulti-file refactors, subtle debugging, architecture decisions

With the default thresholds:

ConditionAction
Difficulty ≤ 0.5, confidence ≥ 0.6, and routing.cheap is setSwitch to the cheap model
Difficulty ≥ 1.5, confidence ≥ 0.6, and routing.strong is setSwitch to the strong model
Middle band, low confidence, missing answer, or unset targetKeep the current model

A middle-band request keeps the current model, including a model selected by an earlier turn. It does not reset to an initial default. Routing uses the prompt rather than the full conversation history.

Invalid model references, unknown models, missing provider authentication, and Jev failures keep the current model. A cheap target that only accepts text is skipped when the prompt includes images. Switching to the model already in use is a no-op. Successful switches and routing errors appear as pi UI notifications.

Targets use provider/model-id, with an optional :thinking suffix, such as :high or :max. Use /jev to select models actually configured in your pi installation; thinking settings take effect when a model switch occurs.

Configuration

Settings resolve in this order, highest priority first:

  1. Environment variables
  2. Project file: .pi/jev.json
  3. Global file: ~/.pi/agent/jev.json
  4. Built-in defaults

API keys are environment-only and are never written to configuration files. Hooks reload configuration on every event, so changes apply to the next turn or compaction. Environment overrides continue to win over settings changed through /jev.

Interactive menu and commands

Bare /jev opens the settings menu in interactive pi:

pi-jev
├─ Toggle scope (global ⇄ project)
├─ General        provider · model · baseUrl · disabled
├─ Compaction     thresholds and token budgets
├─ Routing        cheap · strong → model → thinking level
└─ Show resolved config

Typed commands support completion for actions, keys, and model references:

/jev set provider openrouter -l
/jev get routing.cheap
/jev unset routing.strong
/jev keys
/jev path -l

Configuration file example

A project .pi/jev.json can set the transport and compaction policy:

{
  "provider": "openrouter",
  "disabled": false,
  "compaction": {
    "keepThreshold": 0.5,
    "minReduction": 0.15
  }
}

Add routing targets through /jevRouting, or set routing.cheap / routing.strong to your configured model references. Either target can enable routing independently.

Shell CLI

From a built checkout, the CLI manages the same settings files:

node bin/pi-jev.js config
node bin/pi-jev.js config set provider openrouter -l
node bin/pi-jev.js config get routing.cheap
node bin/pi-jev.js config unset routing.strong
node bin/pi-jev.js config keys
node bin/pi-jev.js config path -l

If the package's executable is on your PATH, use pi-jev config … instead. get reads the resolved value; set, unset, and path use global scope unless -l / --project is supplied.

Jev transports

These are the defaults implemented by this project:

SettingTypeSafeOpenRouter
Providertypesafeopenrouter
Endpointhttps://api.typesafe.ai/v1/systemonehttps://openrouter.ai/api/alpha/decisions (alpha)
API keyTYPESAFE_API_KEYOPENROUTER_API_KEY
Jev modeljev-latesttypesafe/jev-1.13

Both use the { model, state, questions }{ answers } protocol. The Jev model is separate from the pi models chosen as routing targets.

JEVC_API_KEY overrides the selected provider's key. It does not select the provider by itself; pair it with JEVC_PROVIDER=openrouter when using an OpenRouter key.

Environment variables

General:

VariableDefaultPurpose
TYPESAFE_API_KEYUnsetTypeSafe authentication
OPENROUTER_API_KEYUnsetOpenRouter authentication
JEVC_API_KEYUnsetOverride the selected provider's API key
JEVC_PROVIDERAuto-detectedtypesafe or openrouter
JEVC_MODELPer transportJev model slug
JEVC_BASE_URLPer transportJev endpoint URL
JEVC_DISABLEDfalse1, true, or yes bypasses both hooks

Compaction:

VariableConfig keyDefault
JEVC_KEEP_THRESHOLDcompaction.keepThreshold0.5
JEVC_BORDERLINEcompaction.borderline0.1
JEVC_TRUNCATE_HEADcompaction.truncateHeadChars300
JEVC_MIN_REDUCTIONcompaction.minReduction0.15
JEVC_MAX_STATE_TOKENScompaction.maxStateTokens25000
JEVC_MAX_REQUEST_TOKENScompaction.maxRequestTokens30000

keepThreshold controls verbatim retention, while borderline defines the band below it where a staleness answer can rescue a result. truncateHeadChars limits shortened result heads. minReduction is the estimated reduction required to replace pi's summary. The token ceilings bound Jev state and state-plus-questions requests.

Routing:

VariableDefaultPurpose
JEVC_ROUTE_CHEAPUnsetEasy-request model reference
JEVC_ROUTE_STRONGUnsetHard-request model reference
JEVC_ROUTE_EASY_MAX0.5Maximum difficulty for the cheap target
JEVC_ROUTE_HARD_MIN1.5Minimum difficulty for the strong target
JEVC_ROUTE_MIN_CONFIDENCE0.6Minimum confidence to switch models

The routing thresholds are advanced overrides supported by the config loader; they are not exposed in the settings menu or CLI key list.

Troubleshooting

SymptomWhat to check
/jev is unavailableEnable extensions/jev.ts through pi config, then /reload if needed
Compaction uses pi's normal summaryCheck the selected provider's API key, disabled, Jev errors, and minReduction
Routing never switches modelsSet at least one target, verify pi model authentication, and check difficulty/confidence gates
A setting change has no effectCheck environment overrides and project settings, which take priority over global settings
The checkout CLI cannot find dist/cli/main.jsRun npm run build

Development

Node.js 22 is used in CI. From the repository root:

npm ci
npm run typecheck
npm test
npm run build

Tests use fixtures and fake JevAsker implementations and do not call the Jev API. They cover compaction decisions and conversion, routing, configuration precedence, the CLI, completions, model picking, and menu screens.

PathResponsibility
src/compactionMessage conversion, Jev batching, retention rules, transcript rendering, and the compaction hook
src/routingDifficulty decisions and model switching
src/commands/jev, settings menus, completion, and model selection
src/cliShared configuration command engine
src/shared/config.tsConfiguration layers, defaults, and key metadata
extensionsExtension entry points
src/vendor/fast-jev-compactionVendored Jev client and compaction primitives
testOffline test suite

To add a feature, create src/<feature>/ with a default-export extension factory and pure helper modules, add a thin entry point in extensions/, define configuration in src/shared/config.ts, and cover the behavior in test/. Jev request failures must allow pi to continue.

Acknowledgments

License

MIT.