specpi 0.25.0 → 0.27.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 (31) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/README.md +51 -15
  3. package/SECURITY_MODEL.md +44 -4
  4. package/THIRD_PARTY.md +11 -2
  5. package/extensions/jev-advisor/broker.mjs +276 -0
  6. package/extensions/jev-advisor/client.mjs +182 -0
  7. package/extensions/jev-advisor/config.mjs +254 -0
  8. package/extensions/jev-advisor/consent.mjs +133 -0
  9. package/extensions/jev-advisor/gate.mjs +249 -0
  10. package/extensions/jev-advisor/guard.mjs +140 -0
  11. package/extensions/jev-advisor/index.ts +849 -0
  12. package/extensions/jev-advisor/ledger.mjs +138 -0
  13. package/extensions/jev-advisor/questions/capabilities.mjs +124 -0
  14. package/extensions/jev-advisor/questions/compaction.mjs +153 -0
  15. package/extensions/jev-advisor/questions/gap.mjs +140 -0
  16. package/extensions/jev-advisor/questions/progress.mjs +195 -0
  17. package/extensions/jev-advisor/questions/retention.mjs +188 -0
  18. package/extensions/jev-advisor/questions/sources.mjs +91 -0
  19. package/extensions/jev-advisor/questions/untrusted.mjs +69 -0
  20. package/extensions/jev-advisor/sanitize.mjs +0 -0
  21. package/extensions/jev-advisor/usage.mjs +92 -0
  22. package/extensions/tool-wishlist/authoring-tools.mjs +42 -0
  23. package/extensions/tool-wishlist/index.ts +11 -0
  24. package/extensions/workflow-controls/capabilities.mjs +130 -0
  25. package/extensions/workflow-controls/capability-policy.mjs +110 -0
  26. package/extensions/workflow-controls/index.ts +214 -2
  27. package/package.json +1 -1
  28. package/scripts/packages.mjs +1 -1
  29. package/scripts/specpi.mjs +36 -2
  30. package/templates/AGENTS.md +1 -1
  31. package/templates/settings.json +3 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,58 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.27.0 - 2026-09-18
4
+
5
+ - Put a measured number on the rule the whole layer is built around. "Any tool-set decision happens once, before the first request, or not at all" rested on a cache argument that was reasoned about here for months and never weighed. Three arms on `t3-cascade-ledger` differing only in when Browser QA's fourteen tools reach the request settle it: flipping them on at turn 6 collapsed cached tokens to 3,200 at the very next request in three attempts out of three — from 25,984, 17,792 and 21,120 — while the prompt kept climbing, one request going from 3,673 fresh tokens to 31,337. The re-warm cost 14.6%, 21.6% and 23.9% of the attempt, against a 10% threshold written down before the run. Arming the same group from turn 1 cost 16% more than never arming it, against 47% for flipping mid-session, so paying up front is about three times cheaper than paying when the need appears. Recorded to `evals/runs/cache-probe/`; `scripts/cache-probe.mjs` reproduces the analysis.
6
+ - Calibrate the Jev gate against recorded evidence and pin it there. Every threshold was a placeholder carrying a comment that said so; the layer shipped ahead of its own gate. `scripts/jev-calibrate.mjs` now measures each primitive against a label this repository already owns and, separately, checks whether a gate can fire at all — and the second check found that it could not. Retention demanded a Score confidence of 0.80 with the value within 0.15 of a level; on a deliberately obvious spent result it answers 0.10 to 0.18 at confidence 0.73 to 0.85. So retention could gate through to "keep this result" and essentially never to "this result is spent", and running the layer could never have shown it, because a system that never fires looks exactly like one whose advice was always to do nothing. The same check caught compaction's open-thread gate and, the same day, a threshold in new code written for this release.
7
+ - Report honestly that the confidence field carries little signal on hard questions. Predicting an attempt's outcome, its task category or its tier from behavioural metadata alone scores at the base rate for a Noul and a Choice, and about two-thirds exact for a Score against a 43% majority class. No system's pre-registered precision target is met anywhere on any of those curves, and `evals/runs/jev-calibration.json` records UNMET rather than a number chosen to fill the gap. On the production questions, where the state carries the material being judged, separation is wide: a planted credential scores 0.96 against 0.04 for a clean report.
8
+ - Find out, and say, that retention does not fire. Once the ledger could record outcomes rather than only calls, five live runs said the system asks three or four times per attempt and has never shortened anything, every decline being the same: Jev answered and reported a confidence below the calibrated bar. On a contrived case it is confident; on the real reads of a repair chain, where each result feeds the next step, it is genuinely unsure whether the output is spent. The threshold is not lowered to compensate, because firing on a confidence the model did not have is the one thing a system whose wrong answer costs the task must not do. The point of the instrumentation was to make that statement checkable instead of inferable, and it does.
9
+ - Instrument what retention actually drops. The ledger recorded bytes sent and never whether the advice was taken, so a system's effect could only be inferred from a cost delta it may not have caused — disqualifying for a layer whose claim is that it pays for itself. `request()` takes a `decide` callback that runs before the ledger write, so one line now records `applied` and `savedBytes`, and the eval adapter folds `elisions` and `bytesDropped` out of the disposable home into each attempt's report.
10
+ - Re-run the layer's own row and publish what it says, which is that the layer does not pay for itself. 37 attempts across five tiers with the calibrated gate and five systems on: **43 calls, none failed, none changed anything**. Retention asked 38 times and elided nothing, every decline the same confidence below the bar — and this time against a threshold that demonstrably can fire, which is what turns the earlier suspicion into a result. Progress asked 5 times and was right every time that the session was not stuck, down from spending an entire budget on one healthy session. The layer costs about 9% more per attempt and scores within noise either way, and the one figure that had looked like the mechanism working did not survive: context growth per turn went from 590 to 631 against a control of 628, so the earlier 6% reading was variance. `scripts/jev-effect.mjs` reproduces every published figure from the stored reports, using the same aggregates the evaluations page is built from rather than a second definition that could drift.
11
+ - Say that the untrusted-content system has never been called. It rides on a web or browser tool result; no task in any tier produces one, and the run that would exercise Browser QA keeps it withdrawn because capability arming needs an interactive human. `SECURITY_MODEL.md` claimed its false-positive rate was measurable on tier 5, which is not true and is corrected. A system that ships unmeasured is a gap worth naming, because an absent row reads as a zero and a zero reads as a result.
12
+ - Record a payload that outlives its session. The proxy saw 44 calls leave the machine and the ledger held 43, which is how this was found. A system that is deliberately not awaited — the normal shape of a turn-level one — can still be in flight when the session ends, and the answer was being discarded along with its audit line. The answer still is, because it belongs to a session that no longer exists and must never be acted on; the line is now written either way and says which, since the ledger's whole claim is that every transmission appears in it.
13
+ - Correct what the Jev page said the sanitiser refuses. It still carried the "refuses file contents and command output outright" wording that `SECURITY_MODEL.md` had already retracted, in prose and in a diagram. The page now states the real bound: at most twelve short redacted lines of the material being judged — six from the head, four through the middle, two from the tail — inside the same 1 KB budget. Its measured table also disagreed with the dataset it summarised, and is replaced by figures the script derives.
14
+ - Publish the session's call count where another process can read it. `/jev status` could always print what the layer had spent, and nothing outside the advisor's own process could: the ledger is an append-only audit trail with no session boundary in it, so counting *this* session out of it meant knowing something only the advisor knew. The advisor now keeps one small `usage.json` beside its settings, rewritten as it goes, holding counts and nothing else — no state, no questions, no answers, not even the ledger's digests, which is what makes it safe for a second process to read. It is written only while the master switch is on, so a layer nobody has enabled still leaves no trace, and the last session's counts survive shutdown rather than being deleted, because "this has never run" and "the session that just ended spent its whole budget" are different facts.
15
+ - Size the call budget for a session that runs for a day. The total was 120, which was sized against eval attempts — and an attempt runs for two minutes while a session runs until you close it. A turn-level system at one call every four turns reached that ceiling somewhere in the afternoon and then went quiet without having found anything wrong, which is not protection, only a later failure. The default total is now 512 with per-system ceilings that still sum past it, so the total remains a real constraint: measured, a full tier-3 task spends 4 to 7 calls and the busiest attempt ever recorded spent 12, so 512 is only reachable by a loop, and reaching it is therefore information. Cost was never the reason for a ceiling — a call is about $0.00003, so the whole total is about a cent and a half — they bound how much digest leaves the machine and how much awaited latency a runaway loop can add.
16
+ - Say so when a budget runs out. Exhaustion and "nothing to say" both produce silence, and silence is this layer's normal state, so a session could run for an hour with a system switched on and quietly dead. Each system now announces its own exhaustion once, where there is a human to read it.
17
+ - Replace one shared call budget with per-system budgets under a session total. A turn-level system firing thirty times would have reached a shared ceiling of 8 within a few turns and left every other system dead for the rest of the session, with event ordering rather than policy deciding which one won. Schema 2; a schema 1 file is migrated forward rather than read as unrecognised, because collapsing to all-off is a rule for corrupt input and applying it to our own previous version would silently disable a layer the user had switched on.
18
+ - Stop the progress system spending its budget on healthy sessions. Its first local gate asked whenever any single signal fired, and a live run spent all twelve calls on a session that scored 0.978: a 120-step repair chain re-runs its verification command constantly, so a repeated tool signature is that task's normal condition rather than a symptom, and a long read looks identical to a stuck session on the quiet-stretch signal alone. Two weak signals are now required together, a run of three consecutive errors still stands alone, and a verdict is not re-asked for four turns because the situation that produced it has not changed. The same run afterwards: one call instead of twelve, same score.
19
+ - Add three systems. **Progress** is the first aimed at turns rather than input tokens, which is where the money is on the hard tiers: it watches for a repeated tool call, a run of errors or several turns with nothing written, and only then asks whether the session is stuck. It ships set to tell the person rather than the model. **Untrusted content** prepends a fixed warning to a fetched page that confidently reads as instructions addressed to an agent, and costs no extra call while retention is on because one digest answers both questions. **Capability arming** reads the request once, before anything is sent, and offers a withdrawn tool group at turn 0 instead of turn 6 — which the cache probe prices at about a third of the cost.
20
+ - Widen retention to the results that are actually large. `fetch_content`, `get_search_content`, `web_search`, `browser_snapshot`, `browser_accessibility`, `browser_diagnostics` and `delegate` produce the biggest outputs anything in SpecPi generates and are the least likely to be load-bearing twice, and the plan said they fell out of this system for free. They did not: the names were simply not in the set.
21
+ - Serve branch summarisation, which was unserved. It is the same problem as compaction at the same discarded boundary, so it shares the compaction switch rather than adding another. `label` from a fixed enum makes `/tree`'s labelled-only filter worth having, and no model-written text reaches the session file.
22
+ - Give `request_capability` a documented per-invocation cost. Its dialog stated the standing schema weight and said nothing about the one-off, which the cache probe now prices at about 20% of a mid-length attempt. The tool description and the confirmation both say so, and the plan's claim that activating Browser QA also rebuilds the system prompt is removed rather than left standing: the pinned 0.3.0 release moved that guidance into tool descriptions, and the measurement confirms the system prompt is byte-identical between arms.
23
+ - Publish the failure-mode distribution on the evaluations page. A pass rate says how often a harness finished, not whether the failures ran out of clock, solved the wrong problem or repeated a failing call — and those need different fixes. Every verdict goes through the same gate a live session would apply, and 14 of 24 did not clear it; those are published as ungated rather than folded into the nearest category, because a report file records what a harness did and not what it was trying to do.
24
+ - Correct what the security model says leaves the machine. It claimed file contents and command output were "refused outright". That was never true: deciding whether a result is spent cannot be done from byte counts, so a bounded sample of the result's own lines has always been sent. The accurate bound is at most twelve short redacted lines inside a 1 KB budget, and it is now stated as such in both `SECURITY_MODEL.md` and `THIRD_PARTY.md`.
25
+
26
+ - Score tiers 1 to 3 on effort as well as correctness. Across 182 recorded attempts every score was exactly 0 or 1, and 9 of the 14 failures were one harness with disclosed platform problems, so thirteen tasks carried about one bit between them; a two-line deliverable is genuinely binary and no grading scheme rescues it. What did vary at identical results was the work taken — 2 tool calls against 9 on the same task — so a task may now declare an `effort` reference and the runner scores `correctness x (1 - weight + weight x min(1, floor / calls))`. Correctness multiplies, so a wrong answer still scores zero however cheap it was; the floor is the fewest calls a real harness used on a passing attempt, named per task, because reference solutions hardcode their answers and a floor derived from them would punish any agent that honestly reads its input. Beating the floor caps at 1.0, so a better harness never lowers anyone else's recorded score. The checker still returns correctness alone and never sees tool calls, so the fake/failing-fake contract is untouched, and stored attempts are rescored by `attemptScore` the same way `priceAttempt` reprices stored usage.
27
+ - Give every tier 1 and 2 task a decoy. Scope was clean on all 182 recorded attempts because most workspaces held only the file being worked on, so restraint was measuring nothing. Each task now ships a neighbour carrying the same class of defect as the in-scope file, and the prompts do not mention them: `t1-no-touch` and `t2-scoped-edit` name their forbidden file, which tests instruction-following, while these test whether a harness stays on its task when something adjacent looks broken. Across 168 attempts no harness touched an unrelated decoy; all seven edited the one that was a consequence of the change they were asked to make, so that decoy was replaced with an unrelated one rather than left measuring good engineering instinct as overreach.
28
+ - Publish efficiency rather than only spend. Cost is the sum of everything else and the least diagnostic figure of the set, so the evaluations page now reports tool calls, turns, tool-error rate, repeated calls, cache hit rate, context growth per turn and compactions beside the score. All of it was already recorded per attempt and aggregated nowhere. One harness compacted 37 times across the run and no other compacted at all, which no cost column would have shown.
29
+ - Add a "The Jev layer" page to the site, with diagrams for where the layer sits, the six gates in front of every call, and why deciding on arrival is worth five times what rewriting history is worth. It states plainly that the layer has not yet paid for itself: across 259 attempts it cost about 9% more per attempt and scored fractionally lower, with context growth and tool-error rate the only measures moving the right way, and neither attributable yet because nothing records how many bytes retention actually dropped.
30
+ - Add a Jev layer panel to SpecPi Chat. The four systems, the master switch, the call budget and the command guard are toggles that write `<agent-dir>/specpi/jev/settings.json` &mdash; the same file the extension reads. The panel holds the flattened shape and the host translates, because the systems nest under `systems` and the guard under `guard` on disk and a nested object renders as a JSON textarea. A test asserts the panel offers exactly the advisor's own systems, so the two cannot drift apart.
31
+ - Key the Jev consent grant to the host it was given for. The dialog named `api.typesafe.ai` from a fixed constant, and when the default backend became OpenRouter it went on naming a host the data no longer went to; because the grant was keyed on the same constant it was written with, nothing failed and the mismatch was invisible. The label is derived from the live base URL now, so each destination names itself and switching backends asks again rather than carrying an old answer to a new destination.
32
+ - Refuse to render an evaluations page spanning two models. Metadata was read from whichever report sorted last, so a mixed set would have published one model name over rows measured on two &mdash; the same shape already caught once for attempts per cell, which had a guard where the model did not.
33
+
34
+ ## Unreleased
35
+
36
+ - Route and price Jev traffic through the eval proxy. `TYPESAFE_BASE_URL` points the advisor at the proxy, which forwards `/v1/systemone` upstream and records what it cost, so advisor spend lands inside `modelCost` — the figure the harness comparison actually uses — instead of being invisible because it went somewhere else. It is also reported separately as `advisorCost`. Jev prices input only and reports no usage block, so tokens are estimated from the payload at chars/4 and marked as an estimate. Advisor records are filtered out of the model series, which counts turns and context growth and would be wrong if they were mixed in.
37
+ - Write the Jev guard's inert settings at install time, not only at session start. The guard's own default is `enabled: true` and it re-reads its settings on every tool call, so an absent file means an active guard — and with no key it fails closed, which is a fresh install that refuses to run commands. The advisor rewrites the file every session, but that only helps if the advisor loads; establishing it during `install` removes the dependency.
38
+ - Withdraw the two harness-improvement authoring tools when no improvement is selected. Measured across the recorded eval runs, six of SpecPi's ten offered tools were offered on 31 of 31 attempts and called zero times; `record_harness_contract` and `finish_harness_improvement` are only usable after a human selection, and whether one exists is already a fact in local state. They are now withdrawn until one is made and restored the moment it is, which needs no model and cannot be wrong. `report_capability_gap` and `request_capability` are never withdrawn: one is how friction gets reported at all, the other is the escape hatch that makes every other withdrawal safe.
39
+ - Add the Jev advisor, off by default. It asks TypeSafe's Jev classifier typed questions about session state and gets calibrated probabilities back, then gates them in one place. Four systems: shorten a spent read-only tool result before it is appended, steer compaction's summary at the one boundary where the prompt cache is discarded anyway, deduplicate and re-score capability-gap reports, and order the sources a delegation batch will freeze. It holds no authority — it never grants a capability, calls a tool or allows one — and every failure is silent, so a timeout, missing key, refused consent or unconfident answer simply produces no advice. `/jev` shows and changes it; `/jev ledger` reads back a local hash of every payload ever sent.
40
+ - Condense tool results on arrival rather than rewriting history. Simulated over the recorded token series, batching several results and rewriting them afterwards is worth about -12% of long-attempt cost, against -61% for condensing each result before it is appended: a rewrite invalidates the cached prefix, and 94% of SpecPi's prompt tokens are cache reads. Coverage matters far more than compression ratio, so the replacement is a plain deterministic head-and-tail digest that says the output can be recovered by re-running.
41
+ - Pin `specpi-jev-guard@0.1.0` beside the permission system rather than in place of it, and ship it inert. Its own default is `enabled: true`, so left alone a fresh install would start gating shell and file calls through a third-party service on day one; SpecPi writes `enabled: false`, and while it is off `@gotgenes/pi-permission-system` decides every call exactly as before. `/jev guard on` enables it for a session, `/jev guard startup on` defaults it on. Once on it is fail-closed by design — no key, an unreachable endpoint, or a middle-band verdict with no UI all block the call, and no setting hands that decision back to the permission system — so switching it on accepts that an outage stops gated work. `/jev status` and `doctor` say which posture is in force.
42
+ - Use one key for the whole Jev layer, and reach Jev through OpenRouter. Jev is published there, the guard already defaulted to that backend, and an OpenRouter key is rejected by the direct TypeSafe API with a bare 401 &mdash; so the advisor now defaults to OpenRouter too and both halves read `OPENROUTER_API_KEY`. `JEV_BACKEND=typesafe` selects the direct API for a TypeSafe key. Only `enabled`, `backend` and `uncertain` are asserted on the guard, merged into the existing file, so a user's own thresholds, safe-command globs and protected paths survive.
43
+ - Nothing in the Jev layer is on by default. The advisor's master switch, all four of its systems and the guard each ship off, and each has a `startup` preference so a user can default on whatever they want. Session toggles never write those preferences.
44
+ - Read development keys from the existing `evals/.env`, reusing the eval suite's own loader and file rather than adding a second mechanism beside it. `scripts/jev-calibrate.mjs` and `scripts/jev-triage.mjs` load it automatically; a shell variable always wins, values are never printed, and `--env-file=<path>` or `--no-env-file` override it. `--probe` sends one fixed synthetic question so a key and endpoint can be verified before a full run is spent on them. `evals/.env.example` documents the two Jev variables alongside the eval provider ones. The file is for this repository's scripts only; an installed extension reads the environment Pi was started with.
45
+ - Add `scripts/jev-calibrate.mjs` and `scripts/jev-triage.mjs`. The eval checkers are deterministic, so Jev is not a grader here and would be worse as one; calibration inverts the relationship instead and uses those objective verdicts as free labels, printing reliability bins and precision at each candidate threshold so the gate thresholds can be read off a curve rather than guessed. Triage classifies why recorded attempts failed, which is manual transcript reading today. Both are offline and touch no session.
46
+ - Add a `specpi-jev` eval harness beside `specpi-default`, price `jev-1.13.0` in the frozen list, and record `stderrTail` on every attempt. The adapter refuses to run unless `TYPESAFE_BASE_URL` points somewhere loggable, so advisor spend is priced rather than hidden. `eval-run.mjs --keep-transcripts` writes per-attempt request transcripts for triage.
47
+
48
+ - Measure the DeepSeek Harness on the same terms and add it to the first-call chart. Its default session sends 31,743 characters across 25 tools, about 5.7x stock Pi and within 700 characters of OpenCode. `scripts/measure-context.mjs --dsh=<path to its bin>` takes that row, declared through the harness's own patch layer; its auxiliary session-title request is excluded because it carries no tool schema. The Oh My Pi row is carried forward from the same-terms run while its runtime's dependency resolution stays broken upstream.
49
+
50
+ ## 0.26.0 - 2026-09-17
51
+
52
+ - Let the agent ask for a withdrawn tool group instead of working around it. Hiding web access and Browser QA keeps 19,344 characters of tool schema out of every request, but it also hides them from the agent, so a session that turns out to need one had no way to say so. The new `request_capability` tool names the withdrawn groups and asks the human, who may decline; accepting offers that group's tools from the agent's next message, for the rest of the session. It grants nothing on its own: it refuses without an interactive human, and a decline leaves the session unchanged. `/webaccess on` and `/browser on` are unchanged. Delegation is not requestable, because its own package requires a human command to bind a model.
53
+ - Stop asking about a capability you always allow. `/capability allow <name>` records a standing grant so `request_capability` offers that group without a prompt, `/capability ask <name>` restores the prompt, and `/capability` shows which groups are offered and which are granted. Recording a grant needs an interactive command, a grant that fires announces itself, and headless sessions are still refused: the grant removes the prompt, not the human.
54
+ - Bump `specpi-browser-qa` to 0.3.0 and move the base pin to match. Its fourteen tools no longer carry `promptSnippet` or `promptGuidelines`; that guidance moved into the tool descriptions, which travel with the schema. Pi rebuilds the system prompt when an activated tool carries prompt metadata, and that rebuild invalidates the provider's cached prefix even where deferred tool schemas are supported — so the metadata made every mid-session activation more expensive than it needed to be. Behavior is unchanged. `pi-web-access` is a third-party package and still carries its own prompt metadata, so activating web access mid-session continues to rebuild the prompt.
55
+
3
56
  ## 0.25.0 - 2026-09-17
4
57
 
5
58
  - Hide the web access tools until needed. `web_search`, `source_check`, `fetch_content` and `get_search_content` are no longer offered to a session until `/webaccess on`; `/webaccess startup on` saves that choice. The working agreement, security model and wiki say so, and the agent asks the human to run it rather than attempting a hidden tool.
package/README.md CHANGED
@@ -13,7 +13,7 @@
13
13
  </p>
14
14
 
15
15
  <p align="center">
16
- <a href="https://tannermidd.github.io/SpecPi/">Website</a> · <a href="https://tannermidd.github.io/SpecPi/wiki/">Documentation</a> · <a href="https://github.com/TannerMidd/SpecPi/releases">Releases</a>
16
+ <a href="https://tannermidd.github.io/SpecPi/">Website</a> · <a href="https://tannermidd.github.io/SpecPi/wiki/">Documentation</a> · <a href="https://tannermidd.github.io/SpecPi/evaluations/">Evaluations</a> · <a href="https://github.com/TannerMidd/SpecPi/releases">Releases</a>
17
17
  </p>
18
18
 
19
19
  <p align="center">
@@ -28,35 +28,37 @@
28
28
 
29
29
  ---
30
30
 
31
- SpecPi is a small base for the [Pi coding agent](https://pi.dev/), assembled from deliberate choices about how the agent should work not a curated marketplace.
31
+ SpecPi is a small starting point for the [Pi coding agent](https://pi.dev/). It is one opinionated setup for how the agent should work, not a marketplace of plugins.
32
32
 
33
- Two first-party extensions set the terms: **scope control**, which holds each task to the files it declared, and an **improvement loop**, which turns recurring friction into tested, evidence-backed harness changes instead of accumulated prompts and workarounds. Around them sit seven hand-picked packages, each pinned to an exact version and verified before any transaction completes, and **SpecPi Chat**, a VS Code frontend for working beside the agent.
33
+ At the center are two built-in extensions. **Scope control** keeps each task to the files it said it would touch. The **improvement loop** turns repeated friction into small, tested changes to the setup, instead of letting prompts and workarounds pile up. Around those are eight hand-picked packages, each locked to an exact version and checked before anything installs, plus **SpecPi Chat**, a VS Code panel for working alongside the agent.
34
34
 
35
- The setup optimizes for four things:
35
+ It focuses on five things:
36
36
 
37
- - **Control** declared scope, tool permissions, and confirmation-gated lifecycle commands
38
- - **Accuracy** exact pins, checksum-tracked state, rollback on failure, and evidence over claims
39
- - **Improvement** local observations become bounded, verified changes through `/harness-improvement`
40
- - **Efficiency** — subagent delegation, persistent goals, and browser QA handled by the right tools
41
- - **Lean default** web access, browser QA, and delegation ship hidden; `/webaccess on`, `/browser on`, and `/delegate on` offer them per session
37
+ - **Control:** clear scope, tool permissions, and lifecycle commands that ask before they change anything
38
+ - **Accuracy:** exact version pins, checksums on state, rollback on failure, and proof over promises
39
+ - **Improvement:** local notes become small, checked changes through `/harness-improvement`
40
+ - **Efficiency:** delegation, persistent goals, and browser QA handled by the right tool for the job
41
+ - **Lean default:** web access, browser QA, and delegation stay off until you need them. Turn them on for a session with `/webaccess on`, `/browser on`, and `/delegate on` or let the agent ask when it hits the need, and answer the prompt
42
42
 
43
- Everything it manages is declared, versioned, and reversible.
43
+ Everything it touches is written down, versioned, and easy to undo.
44
44
 
45
45
  ## Measured context
46
46
 
47
- The chart measures first-call context from a clean installation: all seven pinned packages, the installed working agreement, and discovered skills. “Enabled” means browser QA, delegation, and web access are switched on; no goal, scope, or improvement selection is active. The solid rows — including OpenCode and Oh My Pi, both measured as installed — are ours, taken from the request each harness actually sends through one local synthetic provider. The dimmed Codex CLI and Claude Code rows are HarnessTax's published figures, taken under the study's configuration: a reference, not a matched comparison. These are character counts, not tokens, spending, or task-quality scores. The research page [breaks the enabled profile down by capability](https://tannermidd.github.io/SpecPi/research/#specpi), so the cost of each switch is visible on its own.
47
+ This chart shows first-call context from a clean install: all seven pinned packages, the working agreement, and the skills Pi finds. It predates `specpi-jev-guard`, the eighth pin, and has not been re-measured since. "Enabled" means browser QA, delegation, and web access are switched on, with no goal, scope, or improvement selection active.
48
+
49
+ The solid rows are measured by us, from the request each setup actually sends through one local test provider. That includes OpenCode, the DeepSeek Harness, and Oh My Pi, all measured as installed. The faded Codex CLI and Claude Code rows come from HarnessTax's published numbers, measured under their own setup. Treat those as a rough reference, not a head-to-head test. These are character counts. They say nothing about tokens, cost, or how well each tool does the job. The research page [breaks down the enabled setup by feature](https://tannermidd.github.io/SpecPi/research/#specpi), so you can see what each switch costs on its own.
48
50
 
49
51
  <p align="center">
50
52
  <a href="https://tannermidd.github.io/SpecPi/research/#specpi">
51
53
  <picture>
52
54
  <source media="(prefers-color-scheme: dark)" srcset="https://tannermidd.github.io/SpecPi/media/context-chart-dark.svg">
53
- <img src="https://tannermidd.github.io/SpecPi/media/context-chart-light.svg" width="880" alt="Bar chart of characters sent on the first model call: Pi stock 5,521, SpecPi default 15,069, OpenCode 31,043, SpecPi enabled 40,203, Codex CLI 41,616, Oh My Pi 65,816, Claude Code 90,460.">
55
+ <img src="https://tannermidd.github.io/SpecPi/media/context-chart-light.svg" width="880" alt="Bar chart of characters sent on the first model call: Pi stock 5,521, SpecPi default 15,069, OpenCode 31,043, DeepSeek Harness 31,743, SpecPi enabled 40,203, Codex CLI 41,616, Oh My Pi 65,816, Claude Code 90,460.">
54
56
  </picture>
55
57
  </a>
56
58
  </p>
57
- <p align="center"><sub>Measured tool schemas + system/developer instructions · <code>node scripts/measure-context.mjs --chart --omp=&lt;path to Oh My Pi's cli.js&gt; --oc=&lt;path to OpenCode's binary&gt;</code> · <a href="site/research/context-measurement.json">Recorded measurements and package pins</a> · <a href="https://tannermidd.github.io/SpecPi/research/#specpi">Method and caveats</a></sub></p>
59
+ <p align="center"><sub>Measured tool schemas + system/developer instructions · <code>node scripts/measure-context.mjs --chart --omp=&lt;path to Oh My Pi's cli.js&gt; --oc=&lt;path to OpenCode's binary&gt; --dsh=&lt;path to the DeepSeek Harness bin&gt;</code> · <a href="site/research/context-measurement.json">Recorded measurements and package pins</a> · <a href="https://tannermidd.github.io/SpecPi/research/#specpi">Method and caveats</a></sub></p>
58
60
 
59
- The gap between the two SpecPi bars is not one decision, so the enabled profile's tool schema is also measured group by group. Browser QA's fourteen tools cost less together than web access's four:
61
+ The gap between the two SpecPi bars comes from a few separate switches, so the enabled tools are also measured group by group. For example, the fourteen browser QA tools add up to less than the four web access tools:
60
62
 
61
63
  <p align="center">
62
64
  <a href="https://tannermidd.github.io/SpecPi/research/#specpi">
@@ -68,6 +70,40 @@ The gap between the two SpecPi bars is not one decision, so the enabled profile'
68
70
  </p>
69
71
  <p align="center"><sub>Every tool in the measured request belongs to exactly one group · Leaving all three opt-in groups hidden keeps 23,797 characters of tool schema out of every request</sub></p>
70
72
 
73
+ ## Harness evaluations
74
+
75
+ The chart above counts characters. It says nothing about what a harness costs to
76
+ actually use, or whether it finishes the job. That is what the eval suite is for:
77
+ the same tasks, the same model and the same frozen price list, with only the
78
+ harness changing.
79
+
80
+ <!-- eval-summary -->
81
+
82
+ **259 attempts across 7 harnesses and 24 tasks**, all on `deepseek-v4.1-flash`.
83
+
84
+ | Harness | Solved | Cost/attempt | Prompt tokens | Sent before any work |
85
+ | --- | --- | --- | --- | --- |
86
+ | Pi | 34/37 | $0.0038 | 112,681 | 5,522 |
87
+ | SpecPi | 35/37 | $0.0042 | 134,859 | 13,799 |
88
+ | OpenCode | 31/37 | $0.0043 | 143,830 | 38,097 |
89
+ | SpecPi + Jev | 35/37 | $0.0046 | 153,077 | 13,799 |
90
+ | Codex CLI | 30/37 | $0.0067 | 242,031 | 38,148 |
91
+ | Oh My Pi | 35/37 | $0.0070 | 257,783 | 62,612 |
92
+ | DeepSeek Harness | 35/37 | $0.0078 | 281,676 | 31,750 |
93
+
94
+ Cost is the harness's own model spend, priced from recorded usage against a
95
+ dated price file. The last column is the tool schema plus system instructions
96
+ riding every single request, which is the fixed toll a harness charges before
97
+ the model does anything.
98
+
99
+ <!-- /eval-summary -->
100
+
101
+ Read the method, the charts and the caveats on the
102
+ [evaluations page](https://tannermidd.github.io/SpecPi/evaluations/). The runner,
103
+ the tasks and the recorded results are in [evals/](evals/), and this table is
104
+ regenerated from those reports by `node scripts/eval-site.mjs`, so it cannot
105
+ drift from the published figures.
106
+
71
107
  ## Install
72
108
 
73
109
  Requires Node.js 22.19+, Git, npm, and an existing Pi installation on PATH.
@@ -87,7 +123,7 @@ Full setup options, package details, and requirements: [website](https://tannerm
87
123
 
88
124
  | | |
89
125
  | --- | --- |
90
- | [Packages](https://tannermidd.github.io/SpecPi/#packages) | The seven pinned packages and what each provides |
126
+ | [Packages](https://tannermidd.github.io/SpecPi/#packages) | The eight pinned packages and what each provides |
91
127
  | [Scope control](https://tannermidd.github.io/SpecPi/wiki/#scope) | `/scope` commands and drift monitoring |
92
128
  | [Improvement loop](https://tannermidd.github.io/SpecPi/#loop) | Local wishlist, `/harness-improvement`, and retirement with evidence |
93
129
  | [SpecPi Chat](https://tannermidd.github.io/SpecPi/#vscode-chat) | VS Code frontend and VSIX install · [Chat guide](https://github.com/TannerMidd/SpecPi/blob/main/vscode/README.md) |
package/SECURITY_MODEL.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Security model
2
2
 
3
- SpecPi provides scope monitoring and an explicit harness improvement loop, and installs six pinned packages as its default base. Extensions run as trusted code with Pi's permissions. Scope is not an OS sandbox or a general command guard. Use OS isolation for hostile code.
3
+ SpecPi provides scope monitoring, an explicit harness improvement loop and an optional Jev advisor, and installs eight pinned packages as its default base. Extensions run as trusted code with Pi's permissions. Scope is not an OS sandbox or a general command guard. Use OS isolation for hostile code.
4
4
 
5
5
  ## Scope monitoring
6
6
 
@@ -8,9 +8,49 @@ The human declares project-relative paths. Interactive `write` and `edit` calls
8
8
 
9
9
  Scope records use Pi's current session branch. Restoring a branch does not create new authority or accept pending drift. An improvement contract can supply paths only through the human's `/scope task` command.
10
10
 
11
+ ## Capability requests
12
+
13
+ Optional tool groups ship withdrawn. The `request_capability` tool lets the model name a withdrawn group instead of silently working around it; it never grants one. Activation requires a human decision, either accepted in the moment or recorded earlier as a standing grant, so the tool refuses in headless sessions and a declined prompt leaves the session unchanged. Activation is additive, offers only the named group's tools, and lasts for the session: it writes no startup preference, and the next session starts from the saved preference as before. Delegation is not requestable, because its own package requires a human command to bind a model. Acceptance offers the tools; it is not a per-call permission, and the Permission System continues to govern what those tools may do.
14
+
15
+ `/capability allow <name>` records a standing grant so that group is offered without a prompt, and `/capability ask <name>` returns it to prompting. Recording either requires an interactive human command, and a grant that fires still announces itself in the session. Grants live in `<agent-dir>/specpi/capabilities/settings.json`, written atomically with owner-only permissions; links and irregular files are refused, and an unreadable file, a foreign schema or an unknown capability name reads as no grant. A standing grant removes the prompt, not the requirement for a human: headless sessions are still refused, so a granted capability cannot be activated by an unattended run. It also does not offer the group at startup — the model must still ask, and the group stays withdrawn until it does.
16
+
17
+ ## Jev advisor
18
+
19
+ The advisor is the only part of SpecPi that sends anything off this machine, and it ships off. With the master switch off there is no network call, no key read, no consent read and no prompt injection: the harness behaves exactly as it did before the extension existed.
20
+
21
+ What is sent: a summary object of at most 1 KB per call to `openrouter.ai`, which is where Jev is published and the default route, or to `api.typesafe.ai` when `JEV_BACKEND=typesafe` selects the direct API. Either way, over HTTPS. It carries tool names, byte counts, relative paths, entry counts, short descriptions and a bounded sample of lines from the material being judged. `sanitize.mjs` is the only module permitted to build one, so the rule is enforced in one place rather than promised in several: credentials, tokens, JWTs, emails and URLs are redacted, absolute paths are relativized, and anything still over budget is truncated rather than sent. Redaction reduces exposure; it does not guarantee that a model-authored string carries nothing sensitive.
22
+
23
+ Earlier versions of this document said file contents and command output were "refused outright". That was never accurate and is corrected here. Deciding whether a tool result is spent, or whether a fetched page is addressing the agent, cannot be done from byte counts alone, so `outline()` has always sent a sample of the result's own lines: up to six from the head and two from the tail, each collapsed to at most 80 characters and passed through the same redaction as everything else. As of 0.27.0 it also samples up to four lines evenly spaced through the middle, because an instruction planted in a fetched page is rarely in its first six lines and a digest that could never contain one would ask system 7 a question its own state made unanswerable. So the accurate statement is: **a bounded, redacted sample of at most twelve short lines per result, inside a 1 KB total budget** &mdash; not the whole content, and not none of it. What the sample cannot include is anything beyond that budget, which is enforced by truncation rather than by intention.
24
+
25
+ Consent is separate from the switch. The first time any system would transmit, an interactive dialog names the endpoint, the shape of the data and the byte budget. Without an interactive human nothing is sent, so an unattended run cannot be the thing that starts transmitting. The grant lives in `<agent-dir>/specpi/jev/consent.json` and is bound to the host it was given for, so switching backends asks again rather than carrying an old answer to a new destination. `/jev forget` revokes it.
26
+
27
+ Every call appends one line to `<agent-dir>/specpi/jev/transmissions.jsonl`: timestamp, system, question keys, state byte count, a SHA-256 of the exact payload, latency and the gated outcome. The payload itself is never written. `/jev ledger` reads it back, so "only digests are sent" is checkable rather than asserted. The ledger is user data and survives uninstall.
28
+
29
+ The advisor also keeps `<agent-dir>/specpi/jev/usage.json`, the running call count for the current session, written atomically with owner-only permissions. It exists because the ledger has no session boundary in it, so nothing outside the advisor's own process could say what *this* session had spent, and the count is what SpecPi Chat shows beside each budget. It is the one file in this layer meant to be read by another process, and it is deliberately the least interesting one: counts per system, the budgets they are counted against, a session identifier and two timestamps. No state, no questions, no answers, not even the ledger's digests. It is written only while the master switch is on, so a layer nobody has enabled leaves no trace of having been installed, and the last session's counts are kept rather than deleted at shutdown, because "this has never run" and "the session that just ended spent its whole budget" are different facts and a reader should be able to tell them apart. One file serves the directory, as the settings file does, so where several Pi sessions share an agent directory it describes whichever wrote to it last; it carries a session identifier, two timestamps and an active flag so a reader can say which rather than having to assume.
30
+
31
+ The advisor holds no authority. It never grants a capability, never calls a tool and never allows one. Its only blocking action is to refuse a capability-gap report that looks like it carries a credential, which asks the model to rewrite its own text and discards nothing. Failure is silent, not closed: a timeout, HTTP error, missing key, missing consent, an exhausted per-system or session budget, or ungated confidence produces no advice, and the existing code path runs unchanged. Tool-result retention can shorten a large read-only result before it is appended; it never alters a write, edit or error result, and the replacement says the output is recoverable by re-running.
32
+
33
+ Two of the seven systems do something the other five do not, and both are off by default like everything else here.
34
+
35
+ **System 5, progress detection**, is the only one that can change what the model does. On a confident verdict that a session has stopped making progress it adds one fixed, code-written line at a turn boundary. It ships set to `notify`, which tells the person and cannot change the session; `message`, which appends the line for the model to read, is a deliberate opt-in. It is written once per session and never retracted, it never aborts or blocks, and it is suppressed entirely when the same call reports that a person would have to answer something first. Nothing the model produced ever enters the transcript: the line is chosen from a fixed table by failure mode.
36
+
37
+ **System 7, untrusted-content classification**, prepends a fixed warning line to externally fetched content that confidently reads as instructions addressed to an agent. It applies only to web and browser tool results, never to the agent's own shell output or file reads. It is defence in depth and explicitly not a control: it never blocks, it has no authority over what the model then does, and it should not be relied on to stop prompt injection. Its value is that untrusted content in a fetched page is currently owned by nothing at all.
38
+
39
+ An earlier version of this document said its false-positive rate was measurable on tier 5 of the eval suite. It is not, and the re-run that checked found out why: **this system has never been called once**. It rides on a web or browser tool result, no task in any tier produces one, and the run that would have exercised Browser QA keeps it withdrawn because capability arming needs an interactive human. So the honest statement is that system 7 ships unmeasured, and the eval suite as it stands cannot measure it. That is a gap in the suite, not a property of the system, and it is stated here rather than left as an absent number that would read as a zero.
40
+
41
+ Settings live in `<agent-dir>/specpi/jev/settings.json`, written atomically with owner-only permissions. Links, irregular files, oversize files, an unrecognised schema and unparseable contents all read as off. The one exception is the layer's own previous schema: a `schema: 1` file is migrated forward rather than read as unrecognised, because "collapse to all-off" is a rule for corrupt input and applying it to our own earlier version would silently disable a layer the user had switched on. Call budgets are per system under a session total, so one busy system cannot exhaust the allowance of the others and leave them dead for the rest of the session with event ordering deciding which one won. Session toggles write no startup preference. The key is read from the environment only; Pi authentication, credentials, trust decisions, sessions and history are never read.
42
+
43
+ ## Command policy and the Jev guard
44
+
45
+ `specpi-jev-guard` is pinned in the base set but ships **inert**. Its own default is `enabled: true`, so left alone a fresh install would begin gating shell and file calls through a third-party service immediately; SpecPi writes `enabled: false` and `/jev guard on` is how a human opts in. `@gotgenes/pi-permission-system` stays pinned and, while the guard is off, decides every tool call exactly as it did before the guard existed.
46
+
47
+ Be precise about what the guard does once it is on, because it cannot be configured away. The guard is **fail-closed by design**: with no key, an unreachable endpoint, or a middle-band verdict in a session with no UI, it blocks the call and reports why. There is no setting that hands the decision back to the permission system instead. So switching it on accepts that an outage stops gated work until it is switched off again. That trade is the user's to make, which is why it ships off and why `/jev status` and `specpi doctor` state plainly whether it is on, which backend it uses and whether that backend's key is present.
48
+
49
+ SpecPi asserts only three fields in `<home>/.pi/jev-guard.json` and merges them into whatever is already there, so a user's own thresholds, safe-command globs and protected paths survive: `enabled`, `uncertain: "ask"` so a middle-band verdict asks a human wherever there is one to ask, and `backend: "typesafe"` so the guard and the advisor read the same `TYPESAFE_API_KEY` rather than the guard needing a separate `OPENROUTER_API_KEY`. Those fields are rewritten at every session start, outside the advisor's master switch, because whether the guard is inert is a property of the install rather than a feature of the advisor — and off is a written configuration, not an absence of one. SpecPi writes only the global file; a project-local override under a workspace's `.pi/` remains the user's to make. Neither the guard nor the advisor is an OS sandbox.
50
+
11
51
  ## Improvement authority and evidence
12
52
 
13
- Collection is off by default. Enabling it permits sanitized gap observations, not implementation. Only an exact human `/harness-improvement` selection authorizes a wishlist-sourced change. The selected contract is bound to the gap, source checkout, session, and selection generation. Web access tools (`web_search`, `source_check`, `fetch_content`, `get_search_content`) are hidden until `/webaccess on`.
53
+ Collection is off by default. Enabling it permits sanitized gap observations, not implementation. Only an exact human `/harness-improvement` selection authorizes a wishlist-sourced change. The selected contract is bound to the gap, source checkout, session, and selection generation. Web access tools (`web_search`, `source_check`, `fetch_content`, `get_search_content`) are hidden until a human offers them, through `/webaccess on` or by accepting a `request_capability` prompt.
14
54
 
15
55
  Retirement requires source registry integration, unchanged verification policy, a matching contract, bounded source snapshots, `npm run check`, and closed registered validators. Receipts distinguish machine-observed gates from model-reported acceptance evidence. Stale selections, changed source, missing evidence, and failed checks reject retirement. A validator proves only the behavior it exercises; the human remains responsible for accepting the result. The loop never commits, publishes, or installs a resulting change automatically.
16
56
 
@@ -20,7 +60,7 @@ Wishlist records remain local under `<agent-dir>/specpi/`. Sanitization and salt
20
60
 
21
61
  Package acquisition requests exact npm dependency saves through the child process environment and verifies the installed top-level versions before completing the transaction. A mismatch fails the operation and triggers managed-state rollback. Upstream transitive dependency ranges remain outside this pinning guarantee.
22
62
 
23
- `plan` is read-only. Installation, updates, and removal require confirmation or `--yes`. SpecPi manages its two first-party extension families, improvement skill, manifest, AGENTS marker block, and the six package entries listed in `templates/settings.json`. Normal install/update runs `pi install npm:<name>@<pin>` for every default package. Only package entries are merged; existing resource filters and unrelated settings are retained. `--skip-package-install` allows a core-only install or preserves an already configured base during update, skipping Chromium setup too. Confirmed install/update otherwise invokes the installed Browser QA Node bin with bounded subprocesses to download Chromium and check readiness. `--skip-browser-install` skips that setup only; doctor still checks readiness. SpecPi never passes `--with-deps`, auto-installs OS libraries, or installs/removes Bun. No new shell profile integration is installed.
63
+ `plan` is read-only. Installation, updates, and removal require confirmation or `--yes`. SpecPi manages its two first-party extension families, improvement skill, manifest, AGENTS marker block, and the eight package entries listed in `templates/settings.json`. Normal install/update runs `pi install npm:<name>@<pin>` for every default package. Only package entries are merged; existing resource filters and unrelated settings are retained. `--skip-package-install` allows a core-only install or preserves an already configured base during update, skipping Chromium setup too. Confirmed install/update otherwise invokes the installed Browser QA Node bin with bounded subprocesses to download Chromium and check readiness. `--skip-browser-install` skips that setup only; doctor still checks readiness. SpecPi never passes `--with-deps`, auto-installs OS libraries, or installs/removes Bun. No new shell profile integration is installed.
24
64
 
25
65
  Managed configuration and files are locked and backed up before mutation; first-party writes are atomic and checksum-tracked. Failure restores the saved configuration and first-party files. Package acquisition runs upstream package-manager scripts and may leave downloads, dependency changes, or external script effects even after configuration rollback. Those effects, including downloaded browser-cache bytes, are outside SpecPi's transaction and survive uninstall. Updates require `--force` before replacing modified retained resources. Retired resources are backed up before deactivation; pre-install files are restored where ownership records identify them. Old runtime directories are moved into backups without inspecting their contents. Backups and private evidence remain after uninstall and can contain sensitive local material.
26
66
 
@@ -28,7 +68,7 @@ Legacy migration restores only recorded settings ownership, preserves differing
28
68
 
29
69
  ## Browser QA package
30
70
 
31
- `packages/browser-qa` is an independently released Node-native Pi package extracted from the retired browser tools. The installer acquires the immutable `specpi-browser-qa@0.2.0` release through Pi; its source and dependencies are not bundled in SpecPi's npm artifact. Its fourteen tools ship withdrawn: until a human runs `/browser on`, or saves `/browser startup on`, Pi is offered no browser tool and the model cannot launch a browser at all. Delegation ships off on the same basis, so neither package's tools reach a default session. Its explicit setup downloads Playwright Chromium, and its doctor performs offline rendering, image-comparison and accessibility smoke checks. It uses package-local dependency resolution and the standard Playwright browser cache rather than SpecPi's retired managed runtime. No Pi settings, credentials or personal profiles are migrated. The ephemeral context is not OS/network isolation; pages can reach localhost and private networks. See the package's [security documentation](packages/browser-qa/SECURITY.md) for artifact retention, best-effort redaction, permission and cleanup limits. This is QA tooling, not general-browser feature parity. BetterWright remains optional/manual. Normal updates restore recorded pre-existing entries and remove only unchanged SpecPi-added BetterWright entries; modified entries, personal browsers, profiles, cookies, and user-owned tools are not migrated or deleted.
71
+ `packages/browser-qa` is an independently released Node-native Pi package extracted from the retired browser tools. The installer acquires the immutable `specpi-browser-qa@0.3.0` release through Pi; its source and dependencies are not bundled in SpecPi's npm artifact. Its fourteen tools ship withdrawn: until a human runs `/browser on`, saves `/browser startup on`, or accepts a `request_capability` prompt, Pi is offered no browser tool and the model cannot launch a browser at all. Delegation ships off on the same basis, so neither package's tools reach a default session. Its explicit setup downloads Playwright Chromium, and its doctor performs offline rendering, image-comparison and accessibility smoke checks. It uses package-local dependency resolution and the standard Playwright browser cache rather than SpecPi's retired managed runtime. No Pi settings, credentials or personal profiles are migrated. The ephemeral context is not OS/network isolation; pages can reach localhost and private networks. See the package's [security documentation](packages/browser-qa/SECURITY.md) for artifact retention, best-effort redaction, permission and cleanup limits. This is QA tooling, not general-browser feature parity. BetterWright remains optional/manual. Normal updates restore recorded pre-existing entries and remove only unchanged SpecPi-added BetterWright entries; modified entries, personal browsers, profiles, cookies, and user-owned tools are not migrated or deleted.
32
72
 
33
73
  ## VS Code frontend
34
74
 
package/THIRD_PARTY.md CHANGED
@@ -11,19 +11,20 @@ Development formatting uses Prettier **3.9.6** (MIT), ESLint **10.9.1** (MIT), `
11
11
 
12
12
  ## Default packages
13
13
 
14
- Reviewed on 2026-09-16 against published npm metadata and integrity-verified source archives. All seven top-level packages declare the MIT license. Pins are authoritative in `templates/settings.json`.
14
+ Reviewed on 2026-09-16 against published npm metadata and integrity-verified source archives. All eight top-level packages declare the MIT license. Pins are authoritative in `templates/settings.json`.
15
15
 
16
16
  SpecPi requests exact npm dependency saves for these pins and checks installed top-level versions before completing installation. The override applies to package acquisition without changing the user's global npm configuration.
17
17
 
18
18
  | Package | Version | Upstream |
19
19
  | ------------------------------ | ------- | ------------------------------------------------------------------------- |
20
20
  | pi-web-access | 0.29.0 | [nicobailon/pi-web-access](https://github.com/nicobailon/pi-web-access) |
21
- | specpi-browser-qa | 0.2.0 | [TannerMidd/SpecPi](https://github.com/TannerMidd/SpecPi/tree/browser-qa-v0.1.0/packages/browser-qa) |
21
+ | specpi-browser-qa | 0.3.0 | [TannerMidd/SpecPi](https://github.com/TannerMidd/SpecPi/tree/browser-qa-v0.3.0/packages/browser-qa) |
22
22
  | specpi-delegation | 0.2.0 | [TannerMidd/SpecPi](https://github.com/TannerMidd/SpecPi/tree/main/packages/delegation) |
23
23
  | specpi-experiments | 0.1.0 | [TannerMidd/SpecPi](https://github.com/TannerMidd/SpecPi/tree/main/packages/experiments) |
24
24
  | pi-goal-x | 0.31.2 | [tmonk/pi-goal-x](https://github.com/tmonk/pi-goal-x) |
25
25
  | @sreetej510/pi-usage | 0.10.0 | [Sreetej510/pi-extensions](https://github.com/Sreetej510/pi-extensions) |
26
26
  | @gotgenes/pi-permission-system | 32.0.2 | [gotgenes/pi-packages](https://github.com/gotgenes/pi-packages) |
27
+ | specpi-jev-guard | 0.1.0 | [TannerMidd/specpi-jev-guard](https://github.com/TannerMidd/specpi-jev-guard) |
27
28
 
28
29
  Transitive dependencies and their notices remain in Pi's npm installation tree. Top-level pins do not freeze upstream dependency ranges or constitute a full transitive security audit. Pi invokes npm with its upstream package-management semantics, including dependency lifecycle scripts. Browser QA has no install hook: confirmed SpecPi install/update explicitly invokes its installed Node setup bin, unless acquisition or browser setup is skipped. It downloads package-pinned Chromium without installing OS libraries. BetterWright is optional/manual and retains its own Bun-based setup; SpecPi neither removes Bun nor deletes user-owned tools. Usage reporting and web packages make their own provider/service connections. Consult upstream licenses and security policies before redistributing their components.
29
30
 
@@ -31,6 +32,14 @@ Pi Lens is no longer a default package. Normal updates retire only unchanged ent
31
32
 
32
33
  The combined base is tested with Pi 0.84.4. Pi Goal X declares Pi `>=0.83.0 <0.85.0`; compatibility with newer hosts is not assumed. SpecPi's former custom browser, structural-search, background-task, and command-guard implementations, DonSeTch, and Pi themes have been removed. Its delegation and experiment implementations were not discarded: they now ship as the independent `specpi-delegation` and `specpi-experiments` packages described below. Removal restores owned package settings but does not delete downloaded upstream packages or tools. Retired private runtimes remain in local backups with their notices.
33
34
 
35
+ ## Network services
36
+
37
+ The optional Jev advisor and `specpi-jev-guard` both reach TypeSafe's System One endpoint. Neither is a bundled dependency: the advisor is first-party source that uses Node's built-in `fetch` and declares no package, and the guard is a pinned MIT package with no declared dependencies. Both require a key supplied by the user through the environment -- `OPENROUTER_API_KEY` on the default OpenRouter route, `TYPESAFE_API_KEY` when `JEV_BACKEND=typesafe` selects the direct API. The two are not interchangeable: the other service rejects the wrong one with a bare 401. SpecPi never provisions, stores or reads provider credentials for them.
38
+
39
+ Requests go to `openrouter.ai` by default, or to `api.typesafe.ai` on the direct backend, unless `TYPESAFE_BASE_URL` redirects them, which exists so tests never reach the network and the eval proxy can price advisor traffic. Retention and processing of what is sent are governed by TypeSafe and, on the default route, by OpenRouter as well -- not by SpecPi; the advisor ships off, asks before its first transmission and records a hash of every payload locally. The frozen eval price list carries `jev-1.13.0` at $0.042/MTok input and free output so a measured SpecPi + Jev row never reports a cost that excludes its own advisor.
40
+
41
+ Worth stating plainly, because it is the part a reader is most likely to assume otherwise: what leaves the machine is a bounded sample of the material being judged, not only counts about it. A tool result contributes up to twelve short lines of its own text, redacted and inside a 1 KB budget, because whether a result is spent or whether a fetched page is addressing the agent cannot be decided from byte counts. When the untrusted-content system is enabled, that sample is drawn from externally fetched pages and browser snapshots as well as from local output. [`SECURITY_MODEL.md`](SECURITY_MODEL.md#jev-advisor) states the exact bound and where it is enforced.
42
+
34
43
  ## Standalone delegation and experiments
35
44
 
36
45
  `packages/delegation` and `packages/experiments` are first-party packages extracted from SpecPi's own retired harness code under MIT. Neither bundles third-party runtime code or declares a production dependency; every runtime import is a Node builtin or a Pi-supplied optional peer. Experiments invokes the user's own `git` as an external program through Pi's `exec` seam; Git is not bundled, vendored or version-pinned. Delegation's extraction drops the Command Guard admission path, which SpecPi no longer ships, and reports its guard posture as `absent`. See their dependency notices and security boundaries: [delegation](packages/delegation/THIRD_PARTY.md) / [boundary](packages/delegation/SECURITY.md), [experiments](packages/experiments/THIRD_PARTY.md) / [boundary](packages/experiments/SECURITY.md).