specpi 0.23.0 → 0.25.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.
- package/CHANGELOG.md +23 -0
- package/NPM_RELEASE.md +1 -1
- package/README.md +49 -103
- package/SECURITY_MODEL.md +7 -5
- package/THIRD_PARTY.md +11 -6
- package/extensions/tool-wishlist/verification.mjs +4 -0
- package/extensions/workflow-controls/index.ts +99 -2
- package/extensions/workflow-controls/web-access.mjs +107 -0
- package/package.json +3 -1
- package/scripts/packages.mjs +1 -1
- package/scripts/specpi.mjs +2 -1
- package/templates/AGENTS.md +1 -0
- package/templates/settings.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.25.0 - 2026-09-17
|
|
4
|
+
|
|
5
|
+
- 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.
|
|
6
|
+
- Re-measure first-call context from the complete installed base instead of first-party extensions alone. A default session sends 15,069 characters and the enabled profile (`/browser on`, `/delegate on`, `/webaccess on`) sends 40,203. The earlier 10,536-character figure omitted installed guidance and third-party pins and is corrected on the research page. The measurement, pins and method are published as `site/research/context-measurement.json`.
|
|
7
|
+
- Add a capability chart that partitions the enabled profile's tool schema group by group: web access is the largest at 11,298 characters, ahead of Browser QA's fourteen tools at 8,046. Leaving the three opt-in groups hidden keeps 23,797 characters of tool schema out of every request.
|
|
8
|
+
- Re-measure Oh My Pi on the same terms at 65,816 characters and keep it a measured row: HarnessTax covers Claude Code, Codex CLI and Pi only, so the fork was never a study figure.
|
|
9
|
+
- Measure OpenCode on the same terms and add it to the first-call chart. Its default build-agent session sends 31,043 characters across 10 tools, about 5.6x stock Pi and about twice a default SpecPi, landing between a default SpecPi and the enabled profile. `scripts/measure-context.mjs --oc=<path to OpenCode's binary>` takes that row; the session title is pinned so the turn sends exactly one model call. The research page is rewritten in plainer language and the "For this base" section is restructured around subheads.
|
|
10
|
+
- Stop `/scope` from racing the session restore. Restore retires scope immediately but only learns the repository root once `git rev-parse` returns; a contract declared in that window was recorded against the session cwd, and the replay that followed rejected its own entry as belonging elsewhere and silently turned scope off after reporting it set. Scope commands now wait for the restore in flight.
|
|
11
|
+
|
|
12
|
+
## 0.24.0 - 2026-09-16
|
|
13
|
+
|
|
14
|
+
- Replace `pi-subagents@0.67.0` in the default base with first-party `specpi-delegation@0.2.0`, and add `specpi-experiments@0.1.0`. The base is now seven pinned packages; the other five are unchanged.
|
|
15
|
+
- Correct the delegation documentation: the package activates at Pi startup whenever a model is configured, and `/delegate off` turns it off. Earlier drafts of this entry and of the package's own README, security notes and guide described it as opt-in, which the extension, its tool description and its startup test all contradict.
|
|
16
|
+
- Publish SpecPi's own bounded delegation as an independent Pi package. Child sessions get three read-only tools over a source snapshot frozen when the batch starts — no shell, edits, network or nested delegation — under fixed ceilings that local settings may lower and never raise. Delegation activates at Pi startup whenever a model is configured, and `/delegate off` turns it off for the session. The extraction drops the Command Guard admission path, which SpecPi no longer ships, so the reported guard posture is `absent`.
|
|
17
|
+
- Publish the retired `/experiment` command as an independent Pi package. An experiment is a detached Git worktree created at `HEAD`, closed by exporting a patch or discarding it; the base worktree, its index and its uncommitted changes are never touched. `/experiment recover` reconciles records against what Git tracks and never deletes a directory Git still tracks.
|
|
18
|
+
- Both packages carry no production dependencies and were extracted under MIT from SpecPi 0.20.1, immediately before commit `4f5461d`.
|
|
19
|
+
- Add the HarnessTax study (Pan, Yang, Arabzadeh, Chiang, Stoica and Zaharia; UC Berkeley Sky Lab and Arena, 16 September 2026) to the research page, credited and linked, with all 21 model-harness pairs drawn as cost-success figures from the data published with the study.
|
|
20
|
+
- Measure what SpecPi adds to Pi's first model call rather than assuming Pi's economy survives configuration. `scripts/measure-context.mjs` reads the request a real Pi process sends and counts tool definitions, tool-schema characters and instruction characters the way the study defines them. The base sends 23,710 characters against stock Pi's 5,521, about 4.3x, still under half of Codex and about a quarter of Claude Code. The figure omits the four third-party pins, so it is a floor.
|
|
21
|
+
- Gate the two heaviest optional packages behind a saved preference that ships off. Pi sends every active tool's schema on every request of a session, so Browser QA's fourteen tools (about 8.7 KB) and delegation's one (about 4.4 KB) were charged to projects that never used them. `/browser on` and `/delegate on` enable them for a session; `/browser startup on` and `/delegate startup on` save that choice. A default session's first call falls from 23,710 characters to 10,536, from 4.3x stock Pi to 1.9x.
|
|
22
|
+
- Bump `specpi-browser-qa` to 0.2.0 and `specpi-delegation` to 0.2.0 for that change, and move the base pins to match. `specpi-experiments` is unchanged at 0.1.0.
|
|
23
|
+
- Measure Oh My Pi on the same terms and add it to the first-call chart. The fork sends 65,843 characters across 11 tools, about 11.9x stock Pi and six times a default SpecPi, which puts a configured Pi fork between Codex and Claude Code. `scripts/measure-context.mjs --omp=<path to its cli.js>` takes that row; it needs Bun and is skipped without the flag, so Oh My Pi is not a dependency of this repository.
|
|
24
|
+
- Remove Chat's `pi-subagents` frontend: the fleet adapter, its RPC bridge, its result cards and its configuration UI. Chat's existing delegation panel now covers the default base, including per-worker Stop. Package settings cover web access alone.
|
|
25
|
+
|
|
3
26
|
## 0.23.0 - 2026-09-14
|
|
4
27
|
|
|
5
28
|
- Replace the BetterWright default with independently published `specpi-browser-qa@0.1.0`: 14 QA-focused interaction, accessibility, diagnostic, and visual-regression tools, not general-browser feature parity. The other five package pins and Chat 0.8.1 are unchanged.
|
package/NPM_RELEASE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# npm release
|
|
2
2
|
|
|
3
|
-
This guide covers the root `specpi` package.
|
|
3
|
+
This guide covers the root `specpi` package. Each package under `packages/` releases independently, with its own release process, tag prefix and publish workflow: [Browser QA](packages/browser-qa/NPM_RELEASE.md) (`browser-qa-v*`), [Delegation](packages/delegation/NPM_RELEASE.md) (`delegation-v*`) and [Experiments](packages/experiments/NPM_RELEASE.md) (`experiments-v*`). Package-only releases do not invoke the root publisher.
|
|
4
4
|
|
|
5
5
|
Publishing, tags, deprecation, ownership changes and GitHub Releases require explicit human approval. Publication is a post-merge operation: never publish from an unmerged commit. npm versions are immutable.
|
|
6
6
|
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
<h1 align="center">SpecPi</h1>
|
|
6
6
|
|
|
7
|
-
<p align="center">Pi
|
|
7
|
+
<p align="center">A Pi harness setup, built to specification.</p>
|
|
8
8
|
|
|
9
9
|
<p align="center">
|
|
10
10
|
<a href="https://www.npmjs.com/package/specpi"><img src="https://img.shields.io/npm/v/specpi?style=flat-square&color=084bdb" alt="npm version"></a>
|
|
@@ -13,135 +13,85 @@
|
|
|
13
13
|
</p>
|
|
14
14
|
|
|
15
15
|
<p align="center">
|
|
16
|
-
<a href="
|
|
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>
|
|
17
17
|
</p>
|
|
18
18
|
|
|
19
19
|
<p align="center">
|
|
20
20
|
<a href="https://tannermidd.github.io/SpecPi/#vscode-chat">
|
|
21
21
|
<picture>
|
|
22
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://tannermidd.github.io/SpecPi/media/specpi-chat.png">
|
|
23
|
-
<img src="https://tannermidd.github.io/SpecPi/media/specpi-chat-light.png" width="1100" alt="SpecPi Chat
|
|
22
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://tannermidd.github.io/SpecPi/media/specpi-chat-showcase-dark.png">
|
|
23
|
+
<img src="https://tannermidd.github.io/SpecPi/media/specpi-chat-showcase-light.png" width="1100" alt="SpecPi Chat in VS Code: an open file beside the chat panel discussing a focused change.">
|
|
24
24
|
</picture>
|
|
25
25
|
</a>
|
|
26
26
|
</p>
|
|
27
27
|
<p align="center"><sub>SpecPi Chat · Example workspace</sub></p>
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
---
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
| --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- |
|
|
33
|
-
| Declare files and directories with `/scope`. Review drift as the task progresses. | Record recurring problems locally. Choose a change through `/harness-improvement` and verify it. | Chat, attach files, follow subagents, review approvals, and inspect changes in VS Code. |
|
|
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.
|
|
34
32
|
|
|
35
|
-
|
|
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.
|
|
36
34
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
Requires Node.js 22.19+, Git, npm, and an existing Pi installation on PATH. The complete base is tested with Pi **0.84.4**; `pi-goal-x` currently declares Pi `>=0.83.0 <0.85.0` compatibility.
|
|
40
|
-
|
|
41
|
-
```sh
|
|
42
|
-
npm install --global specpi@latest
|
|
43
|
-
specpi plan
|
|
44
|
-
specpi install
|
|
45
|
-
specpi doctor
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
Inspect the plan, confirm the install, then restart Pi. SpecPi installs two first-party extensions, the improvement skill, a marked working agreement, and the packages below using `pi install`. Provider and model settings are preserved. Confirmed install/update also runs the installed Browser QA package's Node setup to download Chromium and verify readiness. No Bun or OS libraries are installed. There are no additional harness extensions, themes, shell profiles, or tool wrappers.
|
|
49
|
-
|
|
50
|
-
<details>
|
|
51
|
-
<summary>Source checkouts, alternate Pi paths, and core-only installs</summary>
|
|
52
|
-
|
|
53
|
-
For this checkout, run `node scripts/specpi.mjs` in place of `specpi`. `PI_CODING_AGENT_DIR` selects an alternate destination; `SPECPI_PI` selects a Pi CLI path. `specpi install --skip-package-install` installs only the first-party core for offline use and testing. `--skip-browser-install` skips Chromium setup while still acquiring all six packages; `doctor` still checks browser readiness. `--skip-package-install` skips both package acquisition and Chromium setup. A plain `pi install npm:specpi` loads only the packaged first-party resources; use the SpecPi installer above for the complete base, and avoid installing the same first-party resources both ways.
|
|
54
|
-
|
|
55
|
-
</details>
|
|
56
|
-
|
|
57
|
-
## Default packages
|
|
58
|
-
|
|
59
|
-
These are installed on every normal install and update. Exact versions live in [`templates/settings.json`](templates/settings.json); SpecPi merges only the package entries, preserving unrelated configuration and existing resource filters.
|
|
60
|
-
|
|
61
|
-
| Package | Pinned version | Purpose |
|
|
62
|
-
| ----------------------------------------------------------------------------------------------------------------- | -------------- | ------------------------------------------------------ |
|
|
63
|
-
| [pi-web-access](https://github.com/nicobailon/pi-web-access) | 0.29.0 | Web search and page retrieval |
|
|
64
|
-
| [specpi-browser-qa](https://www.npmjs.com/package/specpi-browser-qa) | 0.1.0 | Browser interaction, accessibility and visual QA |
|
|
65
|
-
| [pi-subagents](https://github.com/nicobailon/pi-subagents) | 0.67.0 | Subagents and delegation |
|
|
66
|
-
| [pi-goal-x](https://github.com/tmonk/pi-goal-x) | 0.31.2 | Persistent goals and progress |
|
|
67
|
-
| [@sreetej510/pi-usage](https://github.com/Sreetej510/pi-extensions/tree/main/extensions/pi-usage) | 0.10.0 | Provider usage reporting |
|
|
68
|
-
| [@gotgenes/pi-permission-system](https://github.com/gotgenes/pi-packages/tree/main/packages/pi-permission-system) | 32.0.2 | Tool permission policies |
|
|
69
|
-
|
|
70
|
-
The effective commands are `pi install npm:<package>@<version>` for each row, including the scoped names. These packages supply their own extensions, tools, skills, and prompts according to their upstream defaults. SpecPi does not add a second implementation or configure their policies.
|
|
71
|
-
|
|
72
|
-
SpecPi requests exact npm dependency saves for these installs and verifies installed versions before completing the transaction. This keeps later package installs from advancing an earlier pin through npm's default version ranges.
|
|
73
|
-
|
|
74
|
-
Browser QA setup uses the installed package's pinned Playwright and the standard browser cache (or `PLAYWRIGHT_BROWSERS_PATH`). If setup fails, managed configuration rolls back; install missing OS libraries manually and retry `specpi install` (or `specpi update` for an existing installation). `specpi doctor` runs offline rendering, pixel-comparison, and accessibility checks without downloading anything. Provider credentials, web-service configuration, and permission rules remain governed by each package's documentation. Package installation and extension loading do not prove that every external service or tool is ready.
|
|
75
|
-
|
|
76
|
-
## Browser QA
|
|
77
|
-
|
|
78
|
-
[`specpi-browser-qa@0.1.0`](https://www.npmjs.com/package/specpi-browser-qa/v/0.1.0) is independently published and now included in the default base. Its 14 tools cover interactions, responsive screenshots, visual comparison, diagnostics, and accessibility checks in an ephemeral browser context. It is QA-focused, not general-browser feature parity with BetterWright. No personal browser, profile, cookie, or private Pi data is migrated.
|
|
79
|
-
|
|
80
|
-
BetterWright remains an optional manual install, with its own [Bun-based setup](https://github.com/BetterWright/betterwright/blob/main/SETUP.md). SpecPi does not uninstall Bun or user-owned tools.
|
|
35
|
+
The setup optimizes for four things:
|
|
81
36
|
|
|
82
|
-
|
|
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
|
|
83
42
|
|
|
84
|
-
|
|
43
|
+
Everything it manages is declared, versioned, and reversible.
|
|
85
44
|
|
|
86
|
-
|
|
45
|
+
## Measured context
|
|
87
46
|
|
|
88
|
-
|
|
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.
|
|
89
48
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
<
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
-
|
|
99
|
-
- `/scope add <path>` or `/scope remove <path>`: change the declared scope.
|
|
100
|
-
- `/scope accept <path>`: acknowledge a finding without adding that path to scope.
|
|
101
|
-
- `/scope recheck`: deliberately refresh the baseline after an uncertain snapshot.
|
|
102
|
-
- `/scope clear`: turn monitoring off.
|
|
103
|
-
- `/scope task`: import the active improvement contract's paths explicitly.
|
|
104
|
-
|
|
105
|
-
</details>
|
|
106
|
-
|
|
107
|
-
Interactive writes and edits outside scope ask before proceeding. In headless mode they are recorded as pending. Other tools are checked afterward against bounded Git snapshots. Scope is a drift monitor, not a sandbox: shell commands and custom tools can already have changed files when drift is reported. State follows the current Pi session branch.
|
|
108
|
-
|
|
109
|
-
## Harness improvement loop
|
|
49
|
+
<p align="center">
|
|
50
|
+
<a href="https://tannermidd.github.io/SpecPi/research/#specpi">
|
|
51
|
+
<picture>
|
|
52
|
+
<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.">
|
|
54
|
+
</picture>
|
|
55
|
+
</a>
|
|
56
|
+
</p>
|
|
57
|
+
<p align="center"><sub>Measured tool schemas + system/developer instructions · <code>node scripts/measure-context.mjs --chart --omp=<path to Oh My Pi's cli.js> --oc=<path to OpenCode's binary></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>
|
|
110
58
|
|
|
111
|
-
|
|
112
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://tannermidd.github.io/SpecPi/media/improvement-workflow-dark.svg">
|
|
113
|
-
<img src="https://tannermidd.github.io/SpecPi/media/improvement-workflow.svg" width="1200" alt="Observe a recurring gap, select one change, modify the harness, test it, retire with evidence, and review later outcomes.">
|
|
114
|
-
</picture>
|
|
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:
|
|
115
60
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
61
|
+
<p align="center">
|
|
62
|
+
<a href="https://tannermidd.github.io/SpecPi/research/#specpi">
|
|
63
|
+
<picture>
|
|
64
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://tannermidd.github.io/SpecPi/media/capability-chart-dark.svg">
|
|
65
|
+
<img src="https://tannermidd.github.io/SpecPi/media/capability-chart-light.svg" width="880" alt="Bar chart of tool-schema characters each capability adds: Pi built-ins 2,896, Improvement loop 4,214, Goals 1,315, Browser QA 8,046, Delegation 4,453, Web access 11,298. Browser QA, Delegation, Web access are hidden until switched on.">
|
|
66
|
+
</picture>
|
|
67
|
+
</a>
|
|
68
|
+
</p>
|
|
69
|
+
<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>
|
|
121
70
|
|
|
122
|
-
|
|
71
|
+
## Install
|
|
123
72
|
|
|
124
|
-
|
|
73
|
+
Requires Node.js 22.19+, Git, npm, and an existing Pi installation on PATH.
|
|
125
74
|
|
|
126
75
|
```sh
|
|
127
76
|
npm install --global specpi@latest
|
|
128
77
|
specpi plan
|
|
129
|
-
specpi
|
|
78
|
+
specpi install
|
|
130
79
|
specpi doctor
|
|
131
|
-
specpi uninstall
|
|
132
80
|
```
|
|
133
81
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
BetterWright is no longer a default package. Normal updates use recorded ownership to remove only unchanged SpecPi-added entries or restore pre-existing entries. User-modified entries and downloaded bytes survive; `--skip-package-install` preserves the old base. Restart Pi and each Chat connection to unload retired extensions. Independently retained BetterWright installs require separate user management.
|
|
82
|
+
`plan` shows what will change without modifying anything. Restart Pi after install.
|
|
137
83
|
|
|
138
|
-
|
|
84
|
+
Full setup options, package details, and requirements: [website](https://tannermidd.github.io/SpecPi/#install).
|
|
139
85
|
|
|
140
|
-
|
|
86
|
+
## Where things live
|
|
141
87
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
88
|
+
| | |
|
|
89
|
+
| --- | --- |
|
|
90
|
+
| [Packages](https://tannermidd.github.io/SpecPi/#packages) | The seven pinned packages and what each provides |
|
|
91
|
+
| [Scope control](https://tannermidd.github.io/SpecPi/wiki/#scope) | `/scope` commands and drift monitoring |
|
|
92
|
+
| [Improvement loop](https://tannermidd.github.io/SpecPi/#loop) | Local wishlist, `/harness-improvement`, and retirement with evidence |
|
|
93
|
+
| [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) |
|
|
94
|
+
| [Updating](https://tannermidd.github.io/SpecPi/#updating) | Update, uninstall, and migration notes |
|
|
145
95
|
|
|
146
96
|
## Development
|
|
147
97
|
|
|
@@ -149,12 +99,8 @@ Uninstall restores package entries that still match SpecPi's recorded changes an
|
|
|
149
99
|
npm install --ignore-scripts --omit=peer --no-package-lock
|
|
150
100
|
node --test tests/workflow-controls.test.mjs tests/workflow-controls-extension.test.mjs
|
|
151
101
|
npm run check
|
|
152
|
-
npm run check:pi-package
|
|
153
|
-
npm run check:base
|
|
154
102
|
```
|
|
155
103
|
|
|
156
|
-
Installer tests use disposable Pi directories
|
|
157
|
-
|
|
158
|
-
The website is static HTML and CSS in `site/`. After installing the pinned Playwright browser with `npx --no-install playwright install chromium`, run `npm run check:site` to check versions, links, and desktop, tablet, and mobile layouts. GitHub Pages deploys it from `main` after those checks pass.
|
|
104
|
+
Installer tests use disposable Pi directories — never test against a live Pi installation. Publication follows the [release procedure](NPM_RELEASE.md).
|
|
159
105
|
|
|
160
106
|
[Security model](SECURITY_MODEL.md) · [Third-party components](THIRD_PARTY.md) · [Release notes](CHANGELOG.md) · [MIT License](LICENSE)
|
package/SECURITY_MODEL.md
CHANGED
|
@@ -10,7 +10,7 @@ Scope records use Pi's current session branch. Restoring a branch does not creat
|
|
|
10
10
|
|
|
11
11
|
## Improvement authority and evidence
|
|
12
12
|
|
|
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.
|
|
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`.
|
|
14
14
|
|
|
15
15
|
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
16
|
|
|
@@ -28,17 +28,19 @@ Legacy migration restores only recorded settings ownership, preserves differing
|
|
|
28
28
|
|
|
29
29
|
## Browser QA package
|
|
30
30
|
|
|
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.
|
|
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.
|
|
32
32
|
|
|
33
33
|
## VS Code frontend
|
|
34
34
|
|
|
35
|
-
SpecPi Chat is a separate VSIX, retained alongside the npm harness. It launches Pi only in a trusted filesystem workspace and communicates over local RPC. Provider credentials stay with Pi. The frontend manages only its own workspace-storage conversation catalog and sessions for user-directed history, branching, and export; it does not import unrelated Pi histories. Attachments, images, webview messages, and file navigation are validated and bounded. Rendered model and tool output is untrusted text, never executable HTML.
|
|
35
|
+
SpecPi Chat is a separate VSIX, retained alongside the npm harness. It launches Pi only in a trusted filesystem workspace and communicates over local RPC. Provider credentials stay with Pi. Sign-in is delegated rather than implemented: Chat can start the configured Pi executable in a user-visible VS Code terminal, without RPC flags or a session, and Pi alone prompts, runs any OAuth flow, and writes `auth.json`. Chat sends no input to that terminal, reads no credential store, runs no Pi auth subcommand, and accepts no credential over RPC; it observes only the terminal's closure, and restarts the connection so Pi re-resolves its catalogue. Its sign-in prompt is derived from Pi's own available-model list and missing-credential error text. The frontend manages only its own workspace-storage conversation catalog and sessions for user-directed history, branching, and export; it does not import unrelated Pi histories. Attachments, images, webview messages, and file navigation are validated and bounded. Rendered model and tool output is untrusted text, never executable HTML.
|
|
36
36
|
|
|
37
|
-
Approval replies are tied to the current conversation, client, and request ID. Disconnect, cancellation, and expiry never grant permission. Multiline package approval context is shown in the dialog body; requests exceeding the display budget are cancelled rather than approved against incomplete context. The Permissions editor can change only the package's documented global/project `config.json`, with bounded UTF-8/schema validation, explicit native confirmation, connection/scope binding, stale-revision checks, private backups, and atomic replacement. It reads no permission logs, trust decisions, credentials, or agent frontmatter. Symlinks, hardlinks, and special files are refused. Backups remain local and may contain sensitive policy; another same-user process can still race filesystem operations. Saving does not claim runtime activation: the user can restart the selected chat and inspect `/permission-system show`. Global/project changes may affect other chats as upstream reloads them; session approvals are cleared by restart.
|
|
37
|
+
Approval replies are tied to the current conversation, client, and request ID. Disconnect, cancellation, and expiry never grant permission. Multiline package approval context is shown in the dialog body; requests exceeding the display budget are cancelled rather than approved against incomplete context. The Permissions editor can change only the package's documented global/project `config.json`, with bounded UTF-8/schema validation, explicit native confirmation, connection/scope binding, stale-revision checks, private backups, and atomic replacement. It reads no permission logs, trust decisions, credentials, or agent frontmatter. Symlinks, hardlinks, and special files are refused. Backups remain local and may contain sensitive policy; another same-user process can still race filesystem operations. Saving does not claim runtime activation: the user can restart the selected chat and inspect `/permission-system show`. Global/project changes may affect other chats as upstream reloads them; session approvals are cleared by restart. Delegated agent activity comes from a read-only, connection-local widget the delegation package publishes; Chat registers no tools or commands of its own for it. The VSIX does not install packages or duplicate their enforcement; only Permission System configuration has an explicit editing UI. TUI-only custom components are outside Pi's RPC rendering support.
|
|
38
|
+
|
|
39
|
+
The optional **Destructive guard** preset replaces the complete global configuration draft; it never merges with the old global rules or options. The user can preview/edit the replacement or undo it without a write. Saving uses the same native confirmation, bound global destination, backup, and atomic replacement as ordinary settings edits. The preset asks by default, adds explicit destructive-command denials, disables YOLO and logging, and clears the authorizer chain. It performs no project-policy or agent-definition inspection and no additional directory enumeration. This is a configuration template, not independent enforcement or a claim that global settings override every runtime scope. Project/per-agent policies and session approvals remain upstream-controlled, including 32.0.2's ordered-map merging behavior. Case-sensitive patterns can block benign uses and miss scripts, alternative executable spellings, or unconfigured shell tools. Restart after saving and inspect effective policy; the template is not a sandbox.
|
|
38
40
|
|
|
39
41
|
## Upstream package boundary
|
|
40
42
|
|
|
41
|
-
The
|
|
43
|
+
The seven packages add their own extensions, tools, prompts, skills, network connections, filesystem operations, and subprocesses under their upstream defaults. They are not confined by the improvement loop's selection requirement. Permission System owns tool policies; SpecPi does not inject a duplicate guard or claim its coverage. Delegation starts real Pi child sessions in the same process tree, restricted to a frozen source snapshot and three read-only tools; experiments run the user's own `git` and create worktrees on disk. Neither is an OS sandbox. See [delegation](packages/delegation/SECURITY.md) and [experiments](packages/experiments/SECURITY.md). `pi-lens` and `pi-background-tasks` (including its Anthropic provider wrapper) are no longer part of the default base. Normal updates remove only unchanged entries originally added by SpecPi; pre-existing or modified entries and downloaded bytes remain. `--skip-package-install` preserves the old base. Restart Pi and Chat connections to unload retired extensions. Independently retained installations remain trusted upstream code; retained Pi Lens can still apply configured formatting/autofixes. Web access and usage reporting can contact services and use credentials through their upstream implementations. SpecPi's local-only wishlist collection policy does not describe all activity of those packages.
|
|
42
44
|
|
|
43
45
|
Top-level versions are pinned; upstream transitive dependency ranges are not frozen by SpecPi. `doctor` reads configured pins and installed package metadata, then invokes the installed Browser QA bin for real offline rendering, pixel-comparison, and accessibility checks when the managed base includes it. Doctor never downloads a browser, and missing Chromium or OS libraries fail with recovery guidance. It does not validate provider access or browser readiness for independently retained BetterWright. Core-only installations do not run browser checks. `check:base` acquires the packages, checks combined resource loading and Chat RPC startup, and exercises upstream approval, denial, and cancellation with synthetic context. It uses temporary home/configuration directories and sends no model prompt. The base check also exercises Node-only Chromium setup and offline Browser QA readiness; authenticated services, OS isolation, and every upstream tool's behavior remain outside that check. See [THIRD_PARTY.md](THIRD_PARTY.md) for sources and compatibility limits.
|
|
44
46
|
|
package/THIRD_PARTY.md
CHANGED
|
@@ -11,24 +11,29 @@ 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-
|
|
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`.
|
|
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.
|
|
22
|
-
|
|
|
21
|
+
| specpi-browser-qa | 0.2.0 | [TannerMidd/SpecPi](https://github.com/TannerMidd/SpecPi/tree/browser-qa-v0.1.0/packages/browser-qa) |
|
|
22
|
+
| specpi-delegation | 0.2.0 | [TannerMidd/SpecPi](https://github.com/TannerMidd/SpecPi/tree/main/packages/delegation) |
|
|
23
|
+
| specpi-experiments | 0.1.0 | [TannerMidd/SpecPi](https://github.com/TannerMidd/SpecPi/tree/main/packages/experiments) |
|
|
23
24
|
| pi-goal-x | 0.31.2 | [tmonk/pi-goal-x](https://github.com/tmonk/pi-goal-x) |
|
|
24
25
|
| @sreetej510/pi-usage | 0.10.0 | [Sreetej510/pi-extensions](https://github.com/Sreetej510/pi-extensions) |
|
|
25
26
|
| @gotgenes/pi-permission-system | 32.0.2 | [gotgenes/pi-packages](https://github.com/gotgenes/pi-packages) |
|
|
26
27
|
|
|
27
|
-
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.
|
|
28
|
+
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.
|
|
28
29
|
|
|
29
30
|
Pi Lens is no longer a default package. Normal updates retire only unchanged entries added by SpecPi; independent or modified entries and downloaded bytes remain, with their upstream notices. Restart Pi to unload Lens.
|
|
30
31
|
|
|
31
|
-
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,
|
|
32
|
+
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
|
+
## Standalone delegation and experiments
|
|
35
|
+
|
|
36
|
+
`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).
|
|
32
37
|
|
|
33
38
|
## Standalone browser QA
|
|
34
39
|
|
|
@@ -38,4 +43,4 @@ The separately released `packages/browser-qa` source reuses the retired QA imple
|
|
|
38
43
|
|
|
39
44
|
The website uses the bundled IBM Plex Sans and Plex Mono fonts under the SIL Open Font License 1.1; see [`site/fonts/LICENSE.txt`](https://github.com/TannerMidd/SpecPi/blob/main/site/fonts/LICENSE.txt). The website's scripts and diagrams are first-party code and assets. The README loads public package/license badges from Shields.io and build status from GitHub. Website fonts and media are not included in the npm package.
|
|
40
45
|
|
|
41
|
-
SpecPi Chat 0.8.
|
|
46
|
+
SpecPi Chat 0.8.3 is a separately packaged VS Code extension with no bundled runtime dependencies. It uses VS Code's host APIs and Pi's RPC protocol. Generic tool output, visible custom messages, widgets, and dialogs stay owned by their upstream packages. Playwright **1.62.1** (Apache-2.0, [Microsoft Playwright](https://github.com/microsoft/playwright)) is a pinned development dependency for Chat's rendering tests; its browser is used for those checks and is not shipped in either artifact. The default Browser QA package independently uses the same pinned Playwright version for its runtime. Chat's global Destructive guard preset is a full replacement configuration reviewed against Permission System 32.0.2's schema and native pattern semantics. No upstream patch, new dependency, policy-layer merger, or independent command evaluator is included. See [the security model](SECURITY_MODEL.md) for its limits.
|
|
@@ -48,6 +48,10 @@ export const SOURCE_ROOT_FILES = Object.freeze([
|
|
|
48
48
|
const EXCLUDED_DIRECTORY_NAMES = new Set([
|
|
49
49
|
".git",
|
|
50
50
|
".next",
|
|
51
|
+
// Test and packaging scratch output. It lands at the repository root, and
|
|
52
|
+
// also under vscode/ whenever a check runs with that as its cwd, so it does
|
|
53
|
+
// fall inside the scanned SOURCE_DIRECTORIES.
|
|
54
|
+
".specpi-test",
|
|
51
55
|
"build",
|
|
52
56
|
"coverage",
|
|
53
57
|
"desktop",
|
|
@@ -12,6 +12,13 @@ import {
|
|
|
12
12
|
} from "./scope.mjs";
|
|
13
13
|
|
|
14
14
|
import { readTaskContract, renderTaskContract } from "./task-contract.mjs";
|
|
15
|
+
import {
|
|
16
|
+
WEB_TOOL_NAMES,
|
|
17
|
+
loadStartupActivation,
|
|
18
|
+
saveStartupActivation,
|
|
19
|
+
settingsPath,
|
|
20
|
+
syncActiveTools as syncWebAccessTools,
|
|
21
|
+
} from "./web-access.mjs";
|
|
15
22
|
const SCOPE_ENTRY = "specpi-scope-state";
|
|
16
23
|
const SCOPE_STATUS = "specpi-scope";
|
|
17
24
|
const MAX_PENDING_SCOPE = 40;
|
|
@@ -272,6 +279,11 @@ export default function workflowControls(pi: ExtensionAPI) {
|
|
|
272
279
|
persistScope(ctx);
|
|
273
280
|
};
|
|
274
281
|
|
|
282
|
+
// Restore empties scope first and only learns the real root once `git rev-parse` returns, so between those two
|
|
283
|
+
// moments `scope.root` is a guess at the session cwd: not yet canonical, and not yet the enclosing repository.
|
|
284
|
+
// A human command that declared scope in that gap recorded it under the guessed root, and the replay below then
|
|
285
|
+
// rejected its own branch entry as belonging elsewhere, silently retiring a contract the human was told was set.
|
|
286
|
+
// Scope commands therefore wait for `sessionRestore` rather than racing it.
|
|
275
287
|
const restoreSession = async (ctx: ExtensionContext) => {
|
|
276
288
|
sessionGeneration += 1;
|
|
277
289
|
const origin = captureSession(ctx);
|
|
@@ -337,8 +349,24 @@ export default function workflowControls(pi: ExtensionAPI) {
|
|
|
337
349
|
emitScopeStatus(ctx);
|
|
338
350
|
};
|
|
339
351
|
|
|
340
|
-
|
|
341
|
-
|
|
352
|
+
let sessionRestore: Promise<void> = Promise.resolve();
|
|
353
|
+
const beginRestore = (ctx: ExtensionContext) => {
|
|
354
|
+
// Waiters only need to know the restore is over; restoreSession reports its own failures.
|
|
355
|
+
sessionRestore = restoreSession(ctx).catch(() => {});
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
// Web access ships hidden. A missing or unreadable preference means off, and the
|
|
359
|
+
// gate only ever touches its own four tool names.
|
|
360
|
+
let webAccessEnabled = loadStartupActivation();
|
|
361
|
+
const applyWebAccess = () => syncWebAccessTools(pi, WEB_TOOL_NAMES, webAccessEnabled);
|
|
362
|
+
|
|
363
|
+
pi.on("session_start", (_event, ctx) => {
|
|
364
|
+
webAccessEnabled = loadStartupActivation();
|
|
365
|
+
applyWebAccess();
|
|
366
|
+
beginRestore(ctx);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
pi.on("session_tree", (_event, ctx) => beginRestore(ctx));
|
|
342
370
|
|
|
343
371
|
pi.on("session_shutdown", (_event, ctx) => {
|
|
344
372
|
sessionGeneration += 1;
|
|
@@ -517,6 +545,9 @@ export default function workflowControls(pi: ExtensionAPI) {
|
|
|
517
545
|
.filter((value) => value.startsWith(prefix.trim().toLowerCase()))
|
|
518
546
|
.map((value) => ({ value, label: value })),
|
|
519
547
|
handler: async (args, ctx) => {
|
|
548
|
+
// Read scope only after any restore in flight has settled: before that, both the default action and the
|
|
549
|
+
// root every branch below records are taken from a provisional, pre-Git-lookup guess.
|
|
550
|
+
await sessionRestore;
|
|
520
551
|
const origin = captureSession(ctx);
|
|
521
552
|
const [actionRaw, ...rest] = args.trim().split(/\s+/u).filter(Boolean);
|
|
522
553
|
const action = actionRaw?.toLowerCase() || (scope.active ? "status" : "set");
|
|
@@ -749,4 +780,70 @@ export default function workflowControls(pi: ExtensionAPI) {
|
|
|
749
780
|
}
|
|
750
781
|
},
|
|
751
782
|
});
|
|
783
|
+
|
|
784
|
+
pi.registerCommand("webaccess", {
|
|
785
|
+
description: "Offer or withdraw the web access tools, or choose whether they start offered",
|
|
786
|
+
getArgumentCompletions: (prefix: string) =>
|
|
787
|
+
["on", "off", "status", "startup", "startup on", "startup off"]
|
|
788
|
+
.filter((value) => value.startsWith(prefix.trim().toLowerCase()))
|
|
789
|
+
.map((value) => ({ value, label: value })),
|
|
790
|
+
handler: async (args: string, ctx: ExtensionContext) => {
|
|
791
|
+
const [action = "status", choice, ...rest] = args.trim().split(/\s+/u).filter(Boolean);
|
|
792
|
+
if (rest.length || (choice && action.toLowerCase() !== "startup")) {
|
|
793
|
+
throw new Error("Usage: /webaccess [on|off|status|startup [on|off]]");
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
const verb = action.toLowerCase();
|
|
797
|
+
if (verb === "on" || verb === "off") {
|
|
798
|
+
webAccessEnabled = verb === "on";
|
|
799
|
+
applyWebAccess();
|
|
800
|
+
ctx.ui.notify(
|
|
801
|
+
webAccessEnabled
|
|
802
|
+
? `Web access offered ${WEB_TOOL_NAMES.length} tools to this session (web_search, source_check, fetch_content, get_search_content). They add about 11 KB of tool schema to each request until /webaccess off.`
|
|
803
|
+
: "Web access withdrew its tools from this session. Search and fetch now require /webaccess on.",
|
|
804
|
+
"info",
|
|
805
|
+
);
|
|
806
|
+
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
if (verb === "startup") {
|
|
811
|
+
if (!choice) {
|
|
812
|
+
ctx.ui.notify(
|
|
813
|
+
`Web access starts ${loadStartupActivation() ? "offered" : "withdrawn"}. Preference: ${settingsPath()}`,
|
|
814
|
+
"info",
|
|
815
|
+
);
|
|
816
|
+
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
if (!ctx.hasUI) {
|
|
821
|
+
throw new Error("Startup changes require a human interactive command");
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
if (!["on", "off"].includes(choice.toLowerCase())) {
|
|
825
|
+
throw new Error("Usage: /webaccess startup [on|off]");
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
saveStartupActivation(choice.toLowerCase() === "on");
|
|
829
|
+
ctx.ui.notify(
|
|
830
|
+
choice.toLowerCase() === "on"
|
|
831
|
+
? "Web access tools will be offered in new Pi sessions, adding their schemas to every request. This session is unchanged."
|
|
832
|
+
: "Web access tools will start withdrawn in new Pi sessions and their schemas will not be sent. This session is unchanged.",
|
|
833
|
+
"info",
|
|
834
|
+
);
|
|
835
|
+
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
if (verb !== "status") {
|
|
840
|
+
throw new Error("Usage: /webaccess [on|off|status|startup [on|off]]");
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
ctx.ui.notify(
|
|
844
|
+
`Web access is ${webAccessEnabled ? "offering" : "not offering"} its ${WEB_TOOL_NAMES.length} tools to this session, and starts ${loadStartupActivation() ? "offered" : "withdrawn"}.`,
|
|
845
|
+
"info",
|
|
846
|
+
);
|
|
847
|
+
},
|
|
848
|
+
});
|
|
752
849
|
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// pi-web-access registers four network tools (web_search, source_check, fetch_content,
|
|
2
|
+
// get_search_content). Pi sends every active tool's schema on every request, so a project
|
|
3
|
+
// that never searches would pay for them on every call. SpecPi therefore hides them behind
|
|
4
|
+
// a saved preference that ships off, and `/webaccess on` turns them on for a session.
|
|
5
|
+
//
|
|
6
|
+
// This file is SpecPi's own control: it never modifies the installed package, never reads
|
|
7
|
+
// Pi settings, credentials or session state, and a missing or unreadable file means off.
|
|
8
|
+
|
|
9
|
+
import fs from "node:fs";
|
|
10
|
+
import os from "node:os";
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
import { randomUUID } from "node:crypto";
|
|
13
|
+
|
|
14
|
+
export const WEB_TOOL_NAMES = Object.freeze(["web_search", "source_check", "fetch_content", "get_search_content"]);
|
|
15
|
+
|
|
16
|
+
const MAX_SETTINGS_BYTES = 4096;
|
|
17
|
+
|
|
18
|
+
function agentDirectory() {
|
|
19
|
+
const configured = process.env.PI_CODING_AGENT_DIR;
|
|
20
|
+
|
|
21
|
+
return path.resolve(configured && configured.length > 0 ? configured : path.join(os.homedir(), ".pi", "agent"));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function settingsFile() {
|
|
25
|
+
return path.join(agentDirectory(), "specpi", "web-access", "settings.json");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Refuses links and irregular files so the preference cannot redirect a write. */
|
|
29
|
+
function regularFile(file) {
|
|
30
|
+
const stat = fs.lstatSync(file, { throwIfNoEntry: false });
|
|
31
|
+
if (!stat) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1 || stat.size > MAX_SETTINGS_BYTES) {
|
|
36
|
+
throw new Error("Unsupported web access settings file");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function loadStartupActivation() {
|
|
43
|
+
try {
|
|
44
|
+
const file = settingsFile();
|
|
45
|
+
if (!regularFile(file)) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const settings = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
50
|
+
|
|
51
|
+
return settings?.schema === 1 && settings.startupActivation === true;
|
|
52
|
+
} catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function saveStartupActivation(enabled) {
|
|
58
|
+
if (typeof enabled !== "boolean") {
|
|
59
|
+
throw new Error("Web access startup activation must be on or off.");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const file = settingsFile();
|
|
63
|
+
const directory = path.dirname(file);
|
|
64
|
+
fs.mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
65
|
+
if (fs.existsSync(file)) {
|
|
66
|
+
regularFile(file);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const temporary = path.join(directory, `.settings.${randomUUID()}.tmp`);
|
|
70
|
+
try {
|
|
71
|
+
fs.writeFileSync(temporary, `${JSON.stringify({ schema: 1, startupActivation: enabled })}\n`, {
|
|
72
|
+
mode: 0o600,
|
|
73
|
+
flag: "wx",
|
|
74
|
+
});
|
|
75
|
+
fs.renameSync(temporary, file);
|
|
76
|
+
} finally {
|
|
77
|
+
fs.rmSync(temporary, { force: true });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function settingsPath() {
|
|
82
|
+
return settingsFile();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Add or remove the web tools from Pi's active set without disturbing any other
|
|
87
|
+
* extension's tools, including built-ins and the other first-party gates.
|
|
88
|
+
*/
|
|
89
|
+
export function syncActiveTools(pi, names, enabled) {
|
|
90
|
+
if (typeof pi?.getActiveTools !== "function" || typeof pi?.setActiveTools !== "function") {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const owned = new Set(names);
|
|
95
|
+
const active = pi.getActiveTools();
|
|
96
|
+
const present = active.filter((name) => owned.has(name));
|
|
97
|
+
if (enabled && present.length === owned.size) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!enabled && present.length === 0) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const others = active.filter((name) => !owned.has(name));
|
|
106
|
+
pi.setActiveTools(enabled ? [...others, ...names] : others);
|
|
107
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specpi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"description": "Scope control and a human-selected harness improvement loop for Pi",
|
|
5
5
|
"author": "Tanner Middleton",
|
|
6
6
|
"repository": {
|
|
@@ -84,6 +84,8 @@
|
|
|
84
84
|
"check:base": "node scripts/check-base-packages.mjs",
|
|
85
85
|
"check:site": "node scripts/check-site.mjs",
|
|
86
86
|
"check:browser-qa": "npm --prefix packages/browser-qa run check",
|
|
87
|
+
"check:delegation": "npm --prefix packages/delegation run check",
|
|
88
|
+
"check:experiments": "npm --prefix packages/experiments run check",
|
|
87
89
|
"check:vscode": "npm --prefix vscode run check",
|
|
88
90
|
"prepublishOnly": "npm run check",
|
|
89
91
|
"check": "npm run format:check && npm run check:syntax && npm test && npm run check:package && npm run check:vscode"
|
package/scripts/packages.mjs
CHANGED
|
@@ -19,7 +19,7 @@ export function runBrowserQA(agentDir, command) {
|
|
|
19
19
|
const installed = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
|
|
20
20
|
if (
|
|
21
21
|
installed.name !== "specpi-browser-qa" ||
|
|
22
|
-
installed.version !== "0.
|
|
22
|
+
installed.version !== "0.2.0" ||
|
|
23
23
|
installed.bin?.["specpi-browser-qa"] !== "./bin/browser-qa.mjs"
|
|
24
24
|
) {
|
|
25
25
|
throw new Error("Missing or changed pinned Browser QA bin metadata");
|
package/scripts/specpi.mjs
CHANGED
|
@@ -37,6 +37,7 @@ const resourcePaths = [
|
|
|
37
37
|
"extensions/workflow-controls/scope.mjs",
|
|
38
38
|
"extensions/workflow-controls/task-contract.mjs",
|
|
39
39
|
"extensions/workflow-controls/smoke.mjs",
|
|
40
|
+
"extensions/workflow-controls/web-access.mjs",
|
|
40
41
|
"extensions/tool-wishlist/index.ts",
|
|
41
42
|
"extensions/tool-wishlist/core.mjs",
|
|
42
43
|
"extensions/tool-wishlist/verification.mjs",
|
|
@@ -469,7 +470,7 @@ async function doctor() {
|
|
|
469
470
|
const errors = [];
|
|
470
471
|
if (manifest.basePackages?.length) {
|
|
471
472
|
errors.push(...checkBasePackages(agentDir, readJson(settingsPath, {})));
|
|
472
|
-
if (manifest.basePackages.includes("npm:specpi-browser-qa@0.
|
|
473
|
+
if (manifest.basePackages.includes("npm:specpi-browser-qa@0.2.0")) {
|
|
473
474
|
try {
|
|
474
475
|
runBrowserQA(agentDir, "doctor");
|
|
475
476
|
} catch (error) {
|
package/templates/AGENTS.md
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
- Write commits and pull requests the same way: a short, clear title and a brief explanation of what changed, why, and what was tested. Avoid boilerplate, inflated claims, and unnecessary implementation detail.
|
|
7
7
|
- When `/scope` is active, keep outside-scope findings pending until the human allows once, acknowledges them with `/scope accept`, expands scope with `/scope add`, or clears it. Acknowledgement does not widen scope.
|
|
8
8
|
- Treat wishlist observations as leads, not authorization. Start a wishlist-sourced change only from an exact `/harness-improvement` selection and follow `specpi-improve`. Record its contract before editing and retire only after its verification gate passes.
|
|
9
|
+
- Web access tools (`web_search`, `source_check`, `fetch_content`, `get_search_content`) ship hidden. When the task needs search or fetch, ask the human to run `/webaccess on`; do not attempt a hidden tool.
|
|
9
10
|
- Never inspect Pi authentication, provider credentials, trust decisions, sessions, missions, or history to improve the harness. Use only the active extension context and intended local improvement records.
|
|
10
11
|
- Use observed files, diffs, tests, and runtime behavior as evidence. Run relevant checks, inspect the final diff, and obtain fresh read-only review when risk warrants it. Report results and residual risks without claiming more than the checks prove.
|
|
11
12
|
- Do not commit, push, publish, deploy, or alter remote state unless explicitly requested.
|
package/templates/settings.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"packages": [
|
|
3
3
|
"npm:pi-web-access@0.29.0",
|
|
4
|
-
"npm:specpi-browser-qa@0.
|
|
5
|
-
"npm:
|
|
4
|
+
"npm:specpi-browser-qa@0.2.0",
|
|
5
|
+
"npm:specpi-delegation@0.2.0",
|
|
6
|
+
"npm:specpi-experiments@0.1.0",
|
|
6
7
|
"npm:pi-goal-x@0.31.2",
|
|
7
8
|
"npm:@sreetej510/pi-usage@0.10.0",
|
|
8
9
|
"npm:@gotgenes/pi-permission-system@32.0.2"
|