@alexlikevibe/pi-jev 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +340 -0
  3. package/README.zh-CN.md +340 -0
  4. package/bin/pi-jev.js +13 -0
  5. package/dist/cli/main.js +223 -0
  6. package/dist/commands/completions.js +87 -0
  7. package/dist/commands/extension.js +58 -0
  8. package/dist/commands/menu.js +245 -0
  9. package/dist/commands/models.js +23 -0
  10. package/dist/compaction/convert.js +87 -0
  11. package/dist/compaction/decision.js +195 -0
  12. package/dist/compaction/extension.js +150 -0
  13. package/dist/compaction/jev.js +72 -0
  14. package/dist/compaction/summarize.js +68 -0
  15. package/dist/routing/decide.js +57 -0
  16. package/dist/routing/extension.js +81 -0
  17. package/dist/shared/config.js +157 -0
  18. package/dist/vendor/fast-jev-compaction/client.js +25 -0
  19. package/dist/vendor/fast-jev-compaction/compact.js +233 -0
  20. package/dist/vendor/fast-jev-compaction/index.js +7 -0
  21. package/dist/vendor/fast-jev-compaction/request.js +50 -0
  22. package/dist/vendor/fast-jev-compaction/state.js +255 -0
  23. package/dist/vendor/fast-jev-compaction/types.js +1 -0
  24. package/extensions/compaction.ts +1 -0
  25. package/extensions/jev.ts +1 -0
  26. package/extensions/routing.ts +1 -0
  27. package/media/banner.svg +198 -0
  28. package/package.json +55 -0
  29. package/src/cli/main.ts +241 -0
  30. package/src/commands/completions.ts +107 -0
  31. package/src/commands/extension.ts +61 -0
  32. package/src/commands/menu.ts +291 -0
  33. package/src/commands/models.ts +43 -0
  34. package/src/compaction/convert.ts +95 -0
  35. package/src/compaction/decision.ts +262 -0
  36. package/src/compaction/extension.ts +235 -0
  37. package/src/compaction/jev.ts +133 -0
  38. package/src/compaction/summarize.ts +80 -0
  39. package/src/routing/decide.ts +81 -0
  40. package/src/routing/extension.ts +92 -0
  41. package/src/shared/config.ts +280 -0
  42. package/src/vendor/fast-jev-compaction/LICENSE +21 -0
  43. package/src/vendor/fast-jev-compaction/client.ts +43 -0
  44. package/src/vendor/fast-jev-compaction/compact.ts +309 -0
  45. package/src/vendor/fast-jev-compaction/index.ts +7 -0
  46. package/src/vendor/fast-jev-compaction/request.ts +80 -0
  47. package/src/vendor/fast-jev-compaction/state.ts +304 -0
  48. package/src/vendor/fast-jev-compaction/types.ts +202 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Fan Fei
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,340 @@
1
+ <div align="center">
2
+
3
+ # pi-jev
4
+
5
+ **Selective context compaction and per-turn model routing for [pi](https://github.com/earendil-works/pi-coding-agent), powered by [Jev](https://typesafe.ai).**
6
+
7
+ **English** · [简体中文](README.zh-CN.md)
8
+
9
+ <img src="media/banner.svg" alt="Pi events trigger typed Jev requests: keep scores select context, and a difficulty score selects a model" width="100%">
10
+
11
+ [![checks](https://img.shields.io/github/actions/workflow/status/iefnaf/pi-jev/test.yml?branch=main&style=for-the-badge&label=checks)](https://github.com/iefnaf/pi-jev/actions/workflows/test.yml)
12
+ [![pi extension](https://img.shields.io/badge/pi-extension-8b5cf6?style=for-the-badge)](https://github.com/earendil-works/pi-coding-agent)
13
+ [![Jev](https://img.shields.io/badge/Jev-TypeSafe%20%7C%20OpenRouter-0ea5e9?style=for-the-badge)](https://typesafe.ai)
14
+ [![license](https://img.shields.io/badge/license-MIT-f4c430?style=for-the-badge)](LICENSE)
15
+
16
+ </div>
17
+
18
+ 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.
19
+
20
+ | Extension | Purpose | Pi integration |
21
+ | --- | --- | --- |
22
+ | **Compaction** | Remove obsolete tool calls, shorten unneeded results, and retain remaining text verbatim | `session_before_compact` |
23
+ | **Routing** | Send easy requests to a cheaper model and hard requests to a stronger model | `before_agent_start` |
24
+ | **`/jev` settings** | Configure providers, compaction, and routing from an interactive menu | `/jev` command |
25
+
26
+ Each extension can be enabled or disabled independently through `pi config`. Configuration changes are read on the next hook, without restarting pi.
27
+
28
+ ## Quick start
29
+
30
+ 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.
31
+
32
+ ### 1. Configure a Jev API key
33
+
34
+ Choose one option in the shell where you will launch pi:
35
+
36
+ ```sh
37
+ # Option A: TypeSafe
38
+ export TYPESAFE_API_KEY="your-typesafe-api-key"
39
+ ```
40
+
41
+ ```sh
42
+ # Option B: OpenRouter
43
+ export OPENROUTER_API_KEY="your-openrouter-api-key"
44
+ ```
45
+
46
+ 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.
47
+
48
+ ### 2. Install and launch
49
+
50
+ ```sh
51
+ pi install npm:@alexlikevibe/pi-jev
52
+ pi config # Review which pi-jev extensions are enabled
53
+ pi
54
+ ```
55
+
56
+ If you install into an already running pi session, use `/reload` to load the extensions.
57
+
58
+ ### 3. Configure inside pi
59
+
60
+ ```text
61
+ /jev
62
+ ```
63
+
64
+ 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.
65
+
66
+ | Task | Command or action |
67
+ | --- | --- |
68
+ | Open settings | `/jev` |
69
+ | Compact the current session | `/compact` |
70
+ | Inspect available settings | `/jev keys` |
71
+ | Read a routing target | `/jev get routing.cheap` |
72
+ | Remove a routing target | `/jev unset routing.strong` |
73
+ | Temporarily bypass both features | `/jev set disabled true` |
74
+ | Re-enable both features | `/jev set disabled false` |
75
+ | Write a project setting | Append `-l`, e.g. `/jev set provider openrouter -l` |
76
+
77
+ 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`.
78
+
79
+ <details>
80
+ <summary>Install from GitHub, a checkout, or load a single extension</summary>
81
+
82
+ To install directly from GitHub:
83
+
84
+ ```sh
85
+ pi install https://github.com/iefnaf/pi-jev
86
+ ```
87
+
88
+ From a local checkout:
89
+
90
+ ```sh
91
+ git clone https://github.com/iefnaf/pi-jev.git
92
+ cd pi-jev
93
+ npm ci
94
+ npm run build
95
+ pi install /absolute/path/to/pi-jev
96
+ ```
97
+
98
+ For a development run from the repository root:
99
+
100
+ ```sh
101
+ pi -e ./extensions/compaction.ts
102
+ pi -e ./extensions/routing.ts
103
+ pi -e ./extensions/jev.ts
104
+ ```
105
+
106
+ Each command loads one extension explicitly. The project's `.pi/extensions/jev-*` loaders are also auto-discovered once the repository is trusted.
107
+
108
+ </details>
109
+
110
+ ## Context compaction
111
+
112
+ 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.
113
+
114
+ - **Keep:** retain user and assistant text, pinned recent messages, and useful tool calls/results. The previous compaction summary is included as-is.
115
+ - **Drop:** remove obsolete tool calls together with their results. The transcript header reports the removal count.
116
+ - **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.
117
+ - **Rescue:** preserve a borderline result if a confident staleness judgment says it is still needed.
118
+ - **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.
119
+
120
+ Requests are batched to fit the configured state and request budgets; a compaction can use multiple Jev requests. Recent-message protection applies to the converted messages within the span pi supplies for compaction.
121
+
122
+ **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.
123
+
124
+ ### Example result
125
+
126
+ 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.
127
+
128
+ Successful compactions store audit data in the session's `compaction` entry:
129
+
130
+ ```json
131
+ {
132
+ "engine": "jev",
133
+ "stats": {
134
+ "messagesBefore": 32,
135
+ "messagesAfter": 16,
136
+ "charsBefore": 39379,
137
+ "charsAfter": 9553,
138
+ "calls": 15,
139
+ "pinned": 3,
140
+ "callsDropped": 12,
141
+ "ms": 757,
142
+ "requests": 1,
143
+ "jevUsage": { "input": 9784, "output": 652 }
144
+ }
145
+ }
146
+ ```
147
+
148
+ 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.
149
+
150
+ ## Model routing
151
+
152
+ Before a turn, Jev evaluates the **current user prompt** on a three-level difficulty rubric:
153
+
154
+ | Level | Typical request |
155
+ | --- | --- |
156
+ | `0` — trivial | Greetings, quick questions, formatting, mechanical single-file edits |
157
+ | `1` — moderate | Everyday coding tasks |
158
+ | `2` — complex | Multi-file refactors, subtle debugging, architecture decisions |
159
+
160
+ With the default thresholds:
161
+
162
+ | Condition | Action |
163
+ | --- | --- |
164
+ | Difficulty ≤ `0.5`, confidence ≥ `0.6`, and `routing.cheap` is set | Switch to the cheap model |
165
+ | Difficulty ≥ `1.5`, confidence ≥ `0.6`, and `routing.strong` is set | Switch to the strong model |
166
+ | Middle band, low confidence, missing answer, or unset target | Keep the current model |
167
+
168
+ **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.
169
+
170
+ 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.
171
+
172
+ 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.
173
+
174
+ ## Configuration
175
+
176
+ Settings resolve in this order, highest priority first:
177
+
178
+ 1. Environment variables
179
+ 2. Project file: `.pi/jev.json`
180
+ 3. Global file: `~/.pi/agent/jev.json`
181
+ 4. Built-in defaults
182
+
183
+ 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`.
184
+
185
+ ### Interactive menu and commands
186
+
187
+ Bare `/jev` opens the settings menu in interactive pi:
188
+
189
+ ```text
190
+ pi-jev
191
+ ├─ Toggle scope (global ⇄ project)
192
+ ├─ General provider · model · baseUrl · disabled
193
+ ├─ Compaction thresholds and token budgets
194
+ ├─ Routing cheap · strong → model → thinking level
195
+ └─ Show resolved config
196
+ ```
197
+
198
+ Typed commands support completion for actions, keys, and model references:
199
+
200
+ ```text
201
+ /jev set provider openrouter -l
202
+ /jev get routing.cheap
203
+ /jev unset routing.strong
204
+ /jev keys
205
+ /jev path -l
206
+ ```
207
+
208
+ ### Configuration file example
209
+
210
+ A project `.pi/jev.json` can set the transport and compaction policy:
211
+
212
+ ```json
213
+ {
214
+ "provider": "openrouter",
215
+ "disabled": false,
216
+ "compaction": {
217
+ "keepThreshold": 0.5,
218
+ "preserveRecentMessages": 3,
219
+ "minReduction": 0.15
220
+ }
221
+ }
222
+ ```
223
+
224
+ Add routing targets through `/jev` → **Routing**, or set `routing.cheap` / `routing.strong` to your configured model references. Either target can enable routing independently.
225
+
226
+ ### Shell CLI
227
+
228
+ From a built checkout, the CLI manages the same settings files:
229
+
230
+ ```sh
231
+ node bin/pi-jev.js config
232
+ node bin/pi-jev.js config set provider openrouter -l
233
+ node bin/pi-jev.js config get routing.cheap
234
+ node bin/pi-jev.js config unset routing.strong
235
+ node bin/pi-jev.js config keys
236
+ node bin/pi-jev.js config path -l
237
+ ```
238
+
239
+ 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.
240
+
241
+ ### Jev transports
242
+
243
+ These are the defaults implemented by this project:
244
+
245
+ | Setting | TypeSafe | OpenRouter |
246
+ | --- | --- | --- |
247
+ | Provider | `typesafe` | `openrouter` |
248
+ | Endpoint | `https://api.typesafe.ai/v1/systemone` | `https://openrouter.ai/api/alpha/decisions` (alpha) |
249
+ | API key | `TYPESAFE_API_KEY` | `OPENROUTER_API_KEY` |
250
+ | Jev model | `jev-latest` | `typesafe/jev-1.13` |
251
+
252
+ Both use the `{ model, state, questions }` → `{ answers }` protocol. The Jev model is separate from the pi models chosen as routing targets.
253
+
254
+ `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.
255
+
256
+ ### Environment variables
257
+
258
+ General:
259
+
260
+ | Variable | Default | Purpose |
261
+ | --- | --- | --- |
262
+ | `TYPESAFE_API_KEY` | Unset | TypeSafe authentication |
263
+ | `OPENROUTER_API_KEY` | Unset | OpenRouter authentication |
264
+ | `JEVC_API_KEY` | Unset | Override the selected provider's API key |
265
+ | `JEVC_PROVIDER` | Auto-detected | `typesafe` or `openrouter` |
266
+ | `JEVC_MODEL` | Per transport | Jev model slug |
267
+ | `JEVC_BASE_URL` | Per transport | Jev endpoint URL |
268
+ | `JEVC_DISABLED` | `false` | `1`, `true`, or `yes` bypasses both hooks |
269
+
270
+ Compaction:
271
+
272
+ | Variable | Config key | Default |
273
+ | --- | --- | --- |
274
+ | `JEVC_KEEP_THRESHOLD` | `compaction.keepThreshold` | `0.5` |
275
+ | `JEVC_BORDERLINE` | `compaction.borderline` | `0.1` |
276
+ | `JEVC_PRESERVE_RECENT` | `compaction.preserveRecentMessages` | `3` |
277
+ | `JEVC_TRUNCATE_HEAD` | `compaction.truncateHeadChars` | `300` |
278
+ | `JEVC_MIN_REDUCTION` | `compaction.minReduction` | `0.15` |
279
+ | `JEVC_MAX_STATE_TOKENS` | `compaction.maxStateTokens` | `25000` |
280
+ | `JEVC_MAX_REQUEST_TOKENS` | `compaction.maxRequestTokens` | `30000` |
281
+
282
+ `keepThreshold` controls verbatim retention, while `borderline` defines the band below it where a staleness answer can rescue a result. `preserveRecentMessages` pins recent converted messages; `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.
283
+
284
+ Routing:
285
+
286
+ | Variable | Default | Purpose |
287
+ | --- | --- | --- |
288
+ | `JEVC_ROUTE_CHEAP` | Unset | Easy-request model reference |
289
+ | `JEVC_ROUTE_STRONG` | Unset | Hard-request model reference |
290
+ | `JEVC_ROUTE_EASY_MAX` | `0.5` | Maximum difficulty for the cheap target |
291
+ | `JEVC_ROUTE_HARD_MIN` | `1.5` | Minimum difficulty for the strong target |
292
+ | `JEVC_ROUTE_MIN_CONFIDENCE` | `0.6` | Minimum confidence to switch models |
293
+
294
+ The routing thresholds are advanced overrides supported by the config loader; they are not exposed in the settings menu or CLI key list.
295
+
296
+ ## Troubleshooting
297
+
298
+ | Symptom | What to check |
299
+ | --- | --- |
300
+ | `/jev` is unavailable | Enable `extensions/jev.ts` through `pi config`, then `/reload` if needed |
301
+ | Compaction uses pi's normal summary | Check the selected provider's API key, `disabled`, Jev errors, and `minReduction` |
302
+ | Routing never switches models | Set at least one target, verify pi model authentication, and check difficulty/confidence gates |
303
+ | A setting change has no effect | Check environment overrides and project settings, which take priority over global settings |
304
+ | The checkout CLI cannot find `dist/cli/main.js` | Run `npm run build` |
305
+
306
+ ## Development
307
+
308
+ Node.js 22 is used in CI. From the repository root:
309
+
310
+ ```sh
311
+ npm ci
312
+ npm run typecheck
313
+ npm test
314
+ npm run build
315
+ ```
316
+
317
+ 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.
318
+
319
+ | Path | Responsibility |
320
+ | --- | --- |
321
+ | [src/compaction](src/compaction) | Message conversion, Jev batching, retention rules, transcript rendering, and the compaction hook |
322
+ | [src/routing](src/routing) | Difficulty decisions and model switching |
323
+ | [src/commands](src/commands) | `/jev`, settings menus, completion, and model selection |
324
+ | [src/cli](src/cli) | Shared configuration command engine |
325
+ | [src/shared/config.ts](src/shared/config.ts) | Configuration layers, defaults, and key metadata |
326
+ | [extensions](extensions) | Extension entry points |
327
+ | [src/vendor/fast-jev-compaction](src/vendor/fast-jev-compaction) | Vendored Jev client and compaction primitives |
328
+ | [test](test) | Offline test suite |
329
+
330
+ 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.
331
+
332
+ ## Acknowledgments
333
+
334
+ - [tamaratran/fast-jev-compaction](https://github.com/tamaratran/fast-jev-compaction): vendored MIT Jev client and compaction primitives; its license is retained alongside the source.
335
+ - [@narumitw/pi-tui-kit](https://www.npmjs.com/package/@narumitw/pi-tui-kit): the interactive `/jev` settings menu.
336
+ - [TypeSafe](https://typesafe.ai): Jev System One; OpenRouter provides the alternative Decisions API transport.
337
+
338
+ ## License
339
+
340
+ [MIT](LICENSE).