gentle-pi 4.1.0 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -14
- package/bin/engram-guard +1 -1
- package/bin/gentle-shell.mjs +16 -11
- package/docs/auto-permission-answers.md +25 -0
- package/docs/auto-permission-evidence.md +39 -0
- package/docs/bundled-install.md +8 -6
- package/docs/gentle-shell.md +17 -10
- package/docs/install-wizard.md +9 -9
- package/docs/permission-modes.md +50 -5
- package/docs/readme-reference.md +48 -12
- package/docs/session-profile-format.md +39 -0
- package/docs/yolo-mode.md +2 -1
- package/extensions/ask-user-choice.ts +4 -0
- package/extensions/ask-user-question.ts +6 -1
- package/extensions/gentle-agents.ts +54 -23
- package/extensions/gentle-ai.ts +105 -25
- package/extensions/gentle-shell.ts +26 -31
- package/extensions/gentle-todo.ts +26 -16
- package/lib/auto-permission-answers.ts +133 -0
- package/lib/auto-permission-classifier.ts +215 -36
- package/lib/auto-permission-evidence.ts +88 -0
- package/lib/auto-permission-memory.ts +69 -0
- package/lib/auto-permission-runtime.ts +251 -35
- package/lib/auto-permission-tool-context.ts +61 -0
- package/lib/destructive-command-guard.ts +104 -28
- package/lib/gentle-shell-bundled.ts +23 -5
- package/lib/gentle-shell-launcher.ts +1 -1
- package/lib/model-routing-authority.ts +6 -1
- package/lib/selection-engine.ts +4 -1
- package/lib/session-profile-freeze.ts +10 -15
- package/lib/session-profile-persistence.ts +187 -0
- package/lib/shell-changes.ts +2 -0
- package/lib/shell-prompt.ts +6 -4
- package/lib/shell-todo.ts +20 -8
- package/lib/vim-editor-adapter.ts +22 -4
- package/package.json +5 -5
- package/runtime/gentle-shell-bundled.mjs +23 -5
- package/runtime/gentle-shell-launcher.mjs +1 -1
- package/scripts/install-wizard-preview.mjs +2 -2
- package/scripts/installer-downloads.mjs +7 -7
- package/scripts/installer-preflight.mjs +1 -1
- package/scripts/pi-update.mjs +223 -0
- package/tests/append-system-prompt-route.test.ts +18 -1
- package/tests/auto-mode-human-answers.test.ts +68 -0
- package/tests/auto-mode-invariants.test.ts +23 -5
- package/tests/auto-mode-sdk.test.ts +29 -2
- package/tests/auto-permission-answers.test.ts +170 -0
- package/tests/auto-permission-classifier.test.ts +353 -2
- package/tests/auto-permission-compound.test.ts +24 -0
- package/tests/auto-permission-evidence.test.ts +88 -0
- package/tests/auto-permission-memory.test.ts +46 -0
- package/tests/auto-permission-public-regressions.test.ts +111 -0
- package/tests/auto-permission-runtime.test.ts +270 -12
- package/tests/autonomous-guard.test.ts +86 -16
- package/tests/bundled-install.test.ts +3 -3
- package/tests/engram-safety.test.ts +34 -6
- package/tests/gentle-agents.test.ts +162 -6
- package/tests/gentle-shell-bin.test.ts +17 -17
- package/tests/gentle-shell-bundled.test.ts +67 -33
- package/tests/gentle-shell-launcher.test.ts +18 -13
- package/tests/gentle-shell.test.ts +71 -5
- package/tests/gentle-todo.test.ts +79 -27
- package/tests/install-wizard.test.ts +2 -2
- package/tests/installer-posix-bootstrap.test.ts +8 -8
- package/tests/installer-preflight.test.ts +3 -3
- package/tests/installer-probes.test.ts +8 -8
- package/tests/installer-runner.test.ts +14 -13
- package/tests/installer-server.test.ts +5 -5
- package/tests/main-channel-upgrade.test.ts +7 -1
- package/tests/odd-runtime-delegation-gate.test.ts +4 -1
- package/tests/package-manifest.test.ts +6 -6
- package/tests/pi-update-launcher.test.ts +87 -0
- package/tests/pi-update.test.ts +186 -0
- package/tests/rdd-status-line.test.ts +52 -0
- package/tests/selection-engine.test.ts +35 -5
- package/tests/session-profile-freeze.test.ts +14 -1
- package/tests/session-profile-persistence.test.ts +334 -0
- package/tests/shell-changes.test.ts +18 -0
- package/tests/shell-todo.test.ts +15 -12
- package/tests/vim-editor-adapter.test.ts +41 -15
- package/tests/yolo-mode.test.ts +6 -1
package/README.md
CHANGED
|
@@ -116,6 +116,8 @@ Say what you need once, then keep moving. el Gentleman helps turn intent into cl
|
|
|
116
116
|
|
|
117
117
|
Bring in help without losing the thread. Focused package-owned Pi agents can map a codebase, implement a bounded change, or verify it, while one parent stays accountable for the scope, the decisions, and the final summary.
|
|
118
118
|
|
|
119
|
+
The parent delegates for a reason, not for size. Understanding that outgrows one inline batch (about 3 calls and 10k tokens) gets one read-only explorer; two or more independent units with disjoint edit surfaces get parallel writers; a high-risk change gets an independent verifier; a long session hands its next unit to a fresh worker. Without a named reason the parent works inline — file count and task size never decide the route.
|
|
120
|
+
|
|
119
121
|
**[Docs →](docs/readme-reference.md#how-the-harness-decides-what-to-do)**
|
|
120
122
|
|
|
121
123
|
---
|
|
@@ -124,7 +126,9 @@ Bring in help without losing the thread. Focused package-owned Pi agents can map
|
|
|
124
126
|
|
|
125
127
|
<img width="100%" src="docs/assets/diagrams/odd-workflow.svg" alt="Organic Driven Development as seven numbered steps: Authorize, Explore, Resolve uncertainty, and Classify across the top row; Classify forks, so small understood work stays light while substantial work gets step five, Track, with one feature document; both paths converge on Implement task by task and then Close, above a dashed band marking that one feature document mirrored in Engram lets work resume across sessions">
|
|
126
128
|
|
|
127
|
-
**Organic Driven Development (ODD)** is the everyday path: the agent explores before changing anything, clarifies only real decisions, and keeps small understood work small. Substantial, authorized work gets one recoverable feature document — mirrored in memory when available — so progress, evidence, and the next step survive an interruption;
|
|
129
|
+
**Organic Driven Development (ODD)** is the everyday path: the agent explores before changing anything, clarifies only real decisions, and keeps small understood work small. Substantial, authorized work gets one recoverable feature document — mirrored in memory when available — so progress, evidence, and the next step survive an interruption; behavior changes are test-first by default when a runnable test applies.
|
|
130
|
+
|
|
131
|
+
Tasks are sized by understanding, risk, and resumability: work you could finish from the request and `git diff` alone runs inline, with no subagents and no task document. The feature document quotes your spec verbatim, and every handoff points at it instead of paraphrasing it, so exact strings, error messages, and examples reach the workers intact.
|
|
128
132
|
|
|
129
133
|
**[Docs →](docs/readme-reference.md#organic-driven-development)**
|
|
130
134
|
|
|
@@ -136,6 +140,8 @@ Bring in help without losing the thread. Focused package-owned Pi agents can map
|
|
|
136
140
|
|
|
137
141
|
Review the exact change, not a moving target. Native review keeps one candidate in view, returns risk-scoped evidence, and can surface a bounded correction path. You still decide what happens next in your repository.
|
|
138
142
|
|
|
143
|
+
Review is proportionate: verification follows the risk tier in every RDD state, a review is only due for high-risk work, and the agent forwards the 4R lenses that fit what it touched — with its reason — instead of paying for all four.
|
|
144
|
+
|
|
139
145
|
Reviewer completions inherit Pi's effective `transport` preference when an acknowledged individual or grouped capture executes. The host forwards a defined value unchanged, with one settings snapshot per group; it never forces SSE or changes model routing. An absent setting or an older Pi API without `getSettings()` preserves the provider default. This completion preference is separate from native `pi_host_relay` coordination, and providers that do not support it may ignore it.
|
|
140
146
|
|
|
141
147
|
**[Docs →](docs/review-integration.md)**
|
|
@@ -228,13 +234,32 @@ Cross-orchestrator messages appear as compact `🤖 Sender → 🤖 Recipient` c
|
|
|
228
234
|
|
|
229
235
|
---
|
|
230
236
|
|
|
231
|
-
### What's new in
|
|
237
|
+
### What's new in v4.1
|
|
238
|
+
|
|
239
|
+
The [v4.1.0 release](https://github.com/Gentleman-Programming/gentle-shell/releases/tag/v4.1.0) makes Gentle Shell installable on a clean machine and pins Gentle AI v4.1.1:
|
|
240
|
+
|
|
241
|
+
- **Clean-machine installers:** a browser wizard and double-click installers for macOS, Windows, and Linux install what is missing, update what you have, and ask once before changing anything. See [Get started](#get-started).
|
|
242
|
+
- **Bundled Gentle Shell:** a new installation keeps its exact Pi, pinned Gentle AI, Engram, and state in one prefix; existing global installs can migrate, the launcher offers updates, and `gentle-shell self-uninstall` removes it.
|
|
243
|
+
- **Background jobs and monitors:** `bash_background` wakes the agent once when a command exits, and `monitor` turns each output line of a long-running command into an event. Inspect them with `/gentle:jobs` ([Background jobs](#background-jobs)).
|
|
244
|
+
- **Contextual Auto permissions:** Auto decides with the conversation's context and image input, retries provider failures, and can be the saved default ([Permission modes](#permission-modes--normal-auto-or-yolo)).
|
|
245
|
+
- **Cross-session coordination:** sessions publish a recognizable subject, task aliases, repository scope, and a bounded work classification that other sessions can list, search, and consult with explicit cost permission.
|
|
246
|
+
- **Session-bound profiles:** the active profile binds to the parent session, and orchestrator and reviewer routing apply per session.
|
|
247
|
+
- **Delegate for a reason and review by risk:** see [Focused agents](#focused-agents--context-with-a-return-path), [ODD](#odd--the-everyday-workflow), and [Native review](#native-review--review-the-exact-change).
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
### Measured — What proportional routing changed
|
|
252
|
+
|
|
253
|
+
The routing rules came from benchmarks. These runs use a local benchmark with hidden acceptance suites; each cell is a single run (n=1), and cost is API-equivalent.
|
|
232
254
|
|
|
233
|
-
|
|
255
|
+
| Scenario | Before | After |
|
|
256
|
+
| :--- | :--- | :--- |
|
|
257
|
+
| 3-line bug in one function | 335 s · 56 turns · $0.339 | **77 s · 10 turns · $0.105** — inline, no subagents |
|
|
258
|
+
| Bug rooted in shared logic | 479 s · $0.361 | **276 s · $0.207** — one explorer |
|
|
259
|
+
| Data migration (high risk) | 559 s · $0.403 · needed a feedback round | 539 s · $0.555 · **accepted on the first round** — one independent verifier |
|
|
260
|
+
| 73-line spec, 26 hidden checks | Paraphrased handoffs: 13 failing, still 23/26 after two feedback rounds | Spec passed by reference ([#1718](https://github.com/Gentleman-Programming/gentle-shell/pull/1718)) |
|
|
234
261
|
|
|
235
|
-
|
|
236
|
-
- **Link mode take-over:** when `~/.pi/agent` already declares gentle-pi as a path package, the launcher takes over extension loading (`--no-extensions` plus explicit `-e` for every other declared package and loose extension) so tools never register twice.
|
|
237
|
-
- **Interactive RPC hosts:** with `GENTLE_SHELL_INTERACTIVE_HOST=1` and `--mode rpc`, ask-user tools use pi's RPC dialogs and gentle-agents publishes live subagent activity for the desktop app. See the [reference](docs/readme-reference.md#interactive-rpc-hosts).
|
|
262
|
+
Against the same model in Codex CLI, with blind review of every solution, Gentle Shell was accepted on the first round in every cell and ranked higher on the medium (3 of 3 runs) and large tasks; it was close on the small one. The large task still took about three times longer (17.5–17.9 min vs 6.1 min) — that gap is open work. Sources: [#1723](https://github.com/Gentleman-Programming/gentle-shell/pull/1723), [#1794](https://github.com/Gentleman-Programming/gentle-shell/pull/1794), [#1713](https://github.com/Gentleman-Programming/gentle-shell/issues/1713).
|
|
238
263
|
|
|
239
264
|
---
|
|
240
265
|
|
|
@@ -258,7 +283,7 @@ Download the installer for your computer, double-click it, and follow the steps
|
|
|
258
283
|
| **Windows** | [gentle-shell-installer-windows.zip](https://github.com/Gentleman-Programming/gentle-shell/releases/latest/download/gentle-shell-installer-windows.zip) | Right-click the zip → **Extract All**. Open the extracted folder, then the *Gentle Shell Installer* folder inside it, and double-click **Install Gentle Shell.cmd**. It does not run from inside the zip. |
|
|
259
284
|
| **Linux** | [gentle-shell-installer-linux.tar.gz](https://github.com/Gentleman-Programming/gentle-shell/releases/latest/download/gentle-shell-installer-linux.tar.gz) | Extract it, then double-click **install-gentle-shell.sh**. If your file manager does not run scripts, run `sh install-gentle-shell.sh` in that folder. |
|
|
260
285
|
|
|
261
|
-
A small window opens, then the installer appears in your browser. Use the tab it opens, and keep the small window open until the installer says it is done.
|
|
286
|
+
A small window opens, then the installer appears in your browser. Use the tab it opens, and keep the small window open until the installer says it is done.
|
|
262
287
|
|
|
263
288
|
> **Your computer will warn you the first time.** These installers are not signed with an Apple or Microsoft developer certificate yet, so the system cannot verify who made them. That warning is expected; here is how to continue:
|
|
264
289
|
>
|
|
@@ -268,13 +293,13 @@ A small window opens, then the installer appears in your browser. Use the tab it
|
|
|
268
293
|
>
|
|
269
294
|
> Only download from this repository's [Releases](https://github.com/Gentleman-Programming/gentle-shell/releases) page. To check a download, compare its SHA-256 with [gentle-shell-installers-SHA256SUMS.txt](https://github.com/Gentleman-Programming/gentle-shell/releases/latest/download/gentle-shell-installers-SHA256SUMS.txt).
|
|
270
295
|
|
|
271
|
-
|
|
296
|
+
A new installation gets Gentle Shell as one self-contained product. Its own Node.js, npm, pnpm and Pi live in `~/.gentle-shell`, and your own tools are not used or changed ([bundled installation](docs/install-wizard.md#bundled-installation)).
|
|
272
297
|
|
|
273
298
|
The installer is a preview: it is tested on Linux and in CI, while clean-machine runs on macOS and Windows are still being verified. How it works and what it changes: **[installation wizard](docs/install-wizard.md)**.
|
|
274
299
|
|
|
275
300
|
### Path A: standalone `gentle-shell` (recommended, no pi changes)
|
|
276
301
|
|
|
277
|
-
`gentle-shell` opens Pi with the Gentle Shell package loaded, without installing it into your pi agent or editing its `settings.json`.
|
|
302
|
+
`gentle-shell` opens Pi with the Gentle Shell package loaded, without installing it into your pi agent or editing its `settings.json`. It requires Pi 1.2.0 or newer.
|
|
278
303
|
|
|
279
304
|
```bash
|
|
280
305
|
npm i -g gentle-pi
|
|
@@ -307,8 +332,8 @@ Every other argument is forwarded to pi unchanged, for example `gentle-shell --m
|
|
|
307
332
|
Install the stable release into an existing pi agent, restart Pi, then synchronize the installed assets.
|
|
308
333
|
|
|
309
334
|
```bash
|
|
310
|
-
# Published stable release:
|
|
311
|
-
pi install npm:gentle-pi@
|
|
335
|
+
# Published stable release: v4.1.0
|
|
336
|
+
pi install npm:gentle-pi@4.1.0
|
|
312
337
|
|
|
313
338
|
# Restart Pi, then run:
|
|
314
339
|
gentle-ai sync
|
|
@@ -317,7 +342,7 @@ gentle-ai sync
|
|
|
317
342
|
pi
|
|
318
343
|
```
|
|
319
344
|
|
|
320
|
-
See the [
|
|
345
|
+
See the [v4.1.0 release notes](https://github.com/Gentleman-Programming/gentle-shell/releases/tag/v4.1.0) for version-specific changes.
|
|
321
346
|
|
|
322
347
|
**Builtin codemode warning.** gentle-pi replaces Pi's builtin `codemode` with its compact renderer, so Pi warns at startup that the builtin was not loaded. In your own Pi home (`pi` with this package, or `gentle-shell --link`), gentle-pi asks once in the interactive TUI whether to add `"-builtin:codemode"` to `extensions` in the agent `settings.json` (usually `~/.pi/agent/settings.json`); it writes only if you accept, and the warning disappears from the next launch. A declined prompt is not repeated. To silence it by hand, add the entry yourself, for example `"extensions": ["-builtin:codemode"]`. Isolated `gentle-shell` homes already carry it.
|
|
323
348
|
|
|
@@ -345,9 +370,9 @@ An older Node.js or an incompatible pnpm (from mise, nvm, nvm-windows, fnm, Volt
|
|
|
345
370
|
|
|
346
371
|
### Keep it up to date
|
|
347
372
|
|
|
348
|
-
**Release compatibility:** `upgrade` is
|
|
373
|
+
**Release compatibility:** `upgrade` is available from `gentle-pi` v4.1.0 (and in main builds containing commit `96ff045a8`). Releases through v4.0.0 do not support it: on those launchers, `gentle-shell upgrade` opens a session with `upgrade` as the prompt instead of updating.
|
|
349
374
|
|
|
350
|
-
For those installations, use the [browser installer from a fresh main checkout](#path-c-browser-installer-from-a-checkout). It detects an existing installation and shows the update plan before you confirm.
|
|
375
|
+
For those installations, download the [latest installer](#easiest-download-and-double-click-no-terminal-needed) or use the [browser installer from a fresh main checkout](#path-c-browser-installer-from-a-checkout). It detects an existing installation and shows the update plan before you confirm. See the [older-installation procedure](docs/readme-reference.md#updating-an-installation-without-upgrade).
|
|
351
376
|
|
|
352
377
|
On a main build that supports `upgrade`:
|
|
353
378
|
|
package/bin/engram-guard
CHANGED
|
@@ -5,5 +5,5 @@
|
|
|
5
5
|
# local Engram data only by running ENGRAM_BIN, so failing here stops them
|
|
6
6
|
# before any store is opened, created or migrated. It never runs Engram and
|
|
7
7
|
# never reads or writes Engram data; the notice is printed as data, not run.
|
|
8
|
-
printf '%s\n' "${ENGRAM_STARTUP_NOTICE:-Engram memory
|
|
8
|
+
printf '%s\n' "${ENGRAM_STARTUP_NOTICE:-Engram memory disabled: Shell could not verify safe store or server reuse. Matching versions or updating alone do not prove compatibility. Reuse requires a server with verified /compatibility evidence or matching Shell-provisioned store identity. Back up existing Engram data before manual changes; do not delete it or bypass the safety check. Shell rechecks on every launch and remains usable without memory.}" >&2
|
|
9
9
|
exit 78
|
package/bin/gentle-shell.mjs
CHANGED
|
@@ -276,16 +276,11 @@ async function classifyBundledMemory() {
|
|
|
276
276
|
return classifyEngramStartup({ ...decision, store: observed.store, endpoint, provenance: ownEngramStore(observed, core) });
|
|
277
277
|
}
|
|
278
278
|
|
|
279
|
-
// Verified
|
|
280
|
-
//
|
|
281
|
-
//
|
|
282
|
-
//
|
|
283
|
-
//
|
|
284
|
-
// (internal/store/instance.go:15-79): the data dir, `.instance-id.lock` (waits
|
|
285
|
-
// up to 60s, internal/store/migration_lock.go:29-76) and `.instance-id`. It
|
|
286
|
-
// returns (main.go:767-775) before orphan migration (main.go:779) and opens no
|
|
287
|
-
// database. The timeout stays below that lock wait: a held lock means a
|
|
288
|
-
// concurrent creator, and memory then stays off for the launch.
|
|
279
|
+
// Verified with the published Engram v3.4.0 darwin-arm64 binary in an isolated
|
|
280
|
+
// HOME: `instance-id` creates only `.instance-id.lock` and `.instance-id`,
|
|
281
|
+
// without a database. Keep update checks suppressed and GitHub tokens removed
|
|
282
|
+
// for this provisioning invocation. The timeout stays below the identity lock
|
|
283
|
+
// wait: a held lock means a concurrent creator, so memory stays off that launch.
|
|
289
284
|
const ENGRAM_PROVISION_TIMEOUT_MS = 15000;
|
|
290
285
|
|
|
291
286
|
// A fresh launch's store identity, provisioned by Shell itself with the verified
|
|
@@ -1782,7 +1777,8 @@ async function main() {
|
|
|
1782
1777
|
const args = parseLauncherArgs(process.argv.slice(2));
|
|
1783
1778
|
if (args.error !== undefined) fail(`${args.error}\nRun 'gentle-shell --help' for usage.`, 2);
|
|
1784
1779
|
if (args.help) {
|
|
1785
|
-
process.stdout.write(
|
|
1780
|
+
process.stdout.write("Pi updates: update reinstalls the verified supplier of the Pi this launcher runs (a global Pi, or the\nShell package whose adjacent peer it is, at the same Shell version) and reads the resulting Pi version back.\nUnknown ownership refuses without installing; update --extensions-only stays Pi-only.\nPrefix-bundled Pi updates with gentle-shell upgrade.\n");
|
|
1781
|
+
process.stdout.write(`${helpText()}\nBundled Engram safety: memory opens only a fresh, verified or Shell-provisioned store; otherwise ENGRAM_BIN\nis Shell's guard and memory stays off. Matching versions or updating alone do not prove compatibility.\nReuse requires a server with verified /compatibility evidence or matching Shell-provisioned store identity.\nBack up existing data before manual changes; do not delete it or bypass the safety check.\nShell rechecks on every launch and remains usable without memory.\nAn explicit ENGRAM_BIN or ENGRAM_URL is kept and bypasses the guard.\n`);
|
|
1786
1782
|
process.exit(0);
|
|
1787
1783
|
}
|
|
1788
1784
|
if (args.command === "home") {
|
|
@@ -1869,6 +1865,15 @@ async function main() {
|
|
|
1869
1865
|
if (next.note) process.stderr.write(`${next.note}\n`);
|
|
1870
1866
|
}
|
|
1871
1867
|
passthrough = plan.piArgs;
|
|
1868
|
+
} else if (args.piSubcommand === "update") {
|
|
1869
|
+
const { managedUpdatePlan, updateSelectedPi } = await import("../scripts/pi-update.mjs");
|
|
1870
|
+
const plan = managedUpdatePlan(args.passthrough);
|
|
1871
|
+
if (plan.self) {
|
|
1872
|
+
process.exitCode = await updateSelectedPi({ runtime, packageRoot, platform: process.platform, currentVersion: versionCheck.version, cwd: tmpdir(),
|
|
1873
|
+
adapters: await packageManagerAdapters(), out: (line) => process.stdout.write(`${line}\n`) });
|
|
1874
|
+
if (plan.piArgs === undefined) return;
|
|
1875
|
+
}
|
|
1876
|
+
passthrough = plan.piArgs;
|
|
1872
1877
|
}
|
|
1873
1878
|
|
|
1874
1879
|
// Home-ownership signal for auto-provisioning (S9, homeIsForeign): must be
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Host-authenticated question answers
|
|
2
|
+
|
|
3
|
+
The first-party `ask_user_choice` and `ask_user_question` tools deliver validated human UI answers through `subscribeHumanAnswers(ctx, callback)` in `lib/auto-permission-answers.ts`. The returned unsubscribe function is idempotent. Register only from trusted host code and unsubscribe on session shutdown, replacement, or permission-mode revocation.
|
|
4
|
+
|
|
5
|
+
## Receipt contract
|
|
6
|
+
|
|
7
|
+
Each frozen `HumanAnswerReceipt` contains:
|
|
8
|
+
|
|
9
|
+
- `sessionManager`: the original manager object, compared by identity.
|
|
10
|
+
- `sessionId`, `cwd`: the session and workspace captured before UI.
|
|
11
|
+
- `branchAnchor`: the original `sessionManager.getLeafId()` (`string | null`), unchanged at delivery.
|
|
12
|
+
- `toolName`, `toolCallId`, `questionIndex`: the exact question interaction and zero-based question position.
|
|
13
|
+
- `question`: the original question text, an **untrusted referent**, not an instruction.
|
|
14
|
+
- `prompt`: a pre-UI JSON string containing original question and offered labels/descriptions. Choice prompts include `allowCustomResponse`; questionnaire prompts include header, multi-select status, and previews for the TUI where previews are displayed. This entire field is **untrusted context**, separate from authenticated answer text.
|
|
15
|
+
- `answer`: the authenticated visible answer: `{ kind: "option" | "custom", text, selected? }` or `{ kind: "multi", selected }`.
|
|
16
|
+
|
|
17
|
+
Opaque choice values are excluded entirely. Option descriptions and displayed questionnaire previews are included only in the untrusted `prompt`, never in authenticated answer text. A selected label still authorizes only what the human actually agreed to, not arbitrary instructions embedded in the question. The bridge itself grants no permission, changes no counters, and creates no synthetic user message.
|
|
18
|
+
|
|
19
|
+
## Lifetime and safety
|
|
20
|
+
|
|
21
|
+
Capture snapshots questions, offered choices, descriptions, and displayed previews before mounting UI. Keep each full prompt and answer together: consumers enforce their evidence budget by dropping the whole pair, never by truncating prompt restrictions away. Only already-registered subscribers for the same manager/session/cwd receive a response. Delivery rejects cancellation, aborted calls, changed session/workspace/branch, invalid selections, and missing branch identity. Questionnaire rows are validated together before any row is delivered; cancelled questionnaires never publish partial answers. Custom answers retain exact human text and multi-select labels.
|
|
22
|
+
|
|
23
|
+
Both the native TUI and the existing interactive RPC-host dialogs use their original UI decision without asking another question. Ordinary RPC, print, and unrelated managers do not deliver events. There is no broadcast on Pi's event bus and no authorization from tool results, transcript details, caller-supplied authority flags, or result hooks. Capture helpers are trusted in-process host APIs, not model-callable tools or a sandbox against malicious extensions.
|
|
24
|
+
|
|
25
|
+
The consumer must retain host provenance, verify the receipt against its active session and exact live branch, and revoke retained evidence on mode/session changes. A subscription is not permanently tied to one leaf: subsequent questions in the same session can have new anchors. Dispose the subscription to revoke pending delivery. Callback failures are isolated so existing public tool result shapes and native consent routing remain unchanged.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Auto permission classifier evidence
|
|
2
|
+
|
|
3
|
+
The classifier checks the original action against host-captured human requests and restrictions. Inspection never creates authorization, replaces missing human intent, or bypasses independent hard safety and native consent. File content and prior calls are untrusted context; a historical read path is not the result of that read.
|
|
4
|
+
|
|
5
|
+
## Optional inspection
|
|
6
|
+
|
|
7
|
+
`AutoPermissionRequest.evidencePaths?: string[]` supplies exact relevant project-relative files selected by the host for this action. Omit it to keep the existing single-call, tool-free API. The host should select script paths from the pending action and relevant local context, not enumerate the repository. A script already present in an action payload remains context and needs no duplicate read.
|
|
8
|
+
|
|
9
|
+
When paths are supplied, the model may request `inspect_permission_file` only when missing file evidence is necessary. It can inspect at most two complete files, one request per round, then must return a verdict. The final provider call exposes no inspection tools and requests `toolChoice: "none"`; insufficient evidence still requires denial, never automatic approval. An unsolicited tool call on that final round remains rejected. Each file is limited to 8192 bytes. Existing files larger than that are never offered as candidates, so the classifier judges the action without them instead of failing on an inspection it can never complete; missing evidence is still not permission. There are at most three provider calls, each with its own deadline (default 20 seconds, maximum 20 seconds), and a bounded overall deadline covering file I/O and all calls (at most 60 seconds). No retries or command execution are introduced. Exhaustion returns unavailable with `evidence-limit`; blocked or missing evidence returns `evidence-unavailable`, never an implicit allow.
|
|
10
|
+
|
|
11
|
+
Only exact host-selected paths inside the current project can be read. Absolute paths, traversal, symlinks, hard links, special files, dependency/worktree directories, credential names and locations are rejected. Reads are fixed-size, UTF-8, complete and checked for concurrent replacement or modification. Secret-bearing results never reach the provider and fail with `redacted-context`. File content is always explicitly untrusted and cannot change policy, restrictions, or the original action.
|
|
12
|
+
|
|
13
|
+
One classifier lease covers all rounds. Cancellation or timeout returns promptly but retains the lease until in-flight provider settlement. Identity is checked before and after inspection and provider calls, and again before returning any verdict. The host must bind `isCurrent` to action, session, mode, model and permission-context identity; files can change after inspection, so the verdict is not a reusable execution token.
|
|
14
|
+
|
|
15
|
+
Usage in the run result aggregates completed calls. A late callback reports only the outstanding call's usage once after a timed-out or cancelled provider settles, excluding earlier rounds already accounted in the returned result. Unknown usage in any call makes aggregate usage unknown. No model reasoning, raw provider errors or raw evidence are logged.
|
|
16
|
+
|
|
17
|
+
## Compound action selection
|
|
18
|
+
|
|
19
|
+
The runtime accepts up to four simple segments joined by `&&` for evidence selection only. Each segment must be either `git diff --check` or a `node` invocation with explicit JavaScript/TypeScript file operands and only `--experimental-strip-types` / `--test` flags. Quoting, expansion, redirection, other operators, evaluation, cwd changes and unknown segments leave the whole action without inspection candidates. The original compound action is classified once; no segment inherits approval. Existing path confinement and inspection budgets remain unchanged. Prior ordinary reads retain their arguments, not their returned contents.
|
|
20
|
+
|
|
21
|
+
Memory-write receipts optionally include `memoryContextAttached`, a content-free boolean for `mem_save` and `mem_session_summary`. It reports whether verified destination context was attached to that classification, independently of allow or deny. It is omitted for other actions and grants no permission.
|
|
22
|
+
|
|
23
|
+
## Timeout and evidence-limit diagnostics
|
|
24
|
+
|
|
25
|
+
Classifier results and late-settlement receipts include a metadata-only `diagnostic` object. The runtime projects only these fields into its existing `gentle-auto-permission` entries, and appends them to timeout and evidence-limit blocking messages so a failed read can be diagnosed without opening private session files:
|
|
26
|
+
|
|
27
|
+
- `implementation`: `auto-classifier-diagnostics-v1`, a marker for this instrumentation, not a source hash or proof of the loaded SDK.
|
|
28
|
+
- `elapsedMs`: monotonic milliseconds since this classifier invocation began, rounded down; late receipts measure through settlement.
|
|
29
|
+
- `deadlineMs`: the configured per-call deadline, not the three-call overall budget.
|
|
30
|
+
- `timerFired`: whether a local deadline callback actually ran. The `timeout` label alone does not prove that a timer fired.
|
|
31
|
+
- `returnPath`: `preflight`, `provider`, `provider-result`, `evidence`, `verdict`, or `late-settlement`, locating the observed return stage without recording exception text.
|
|
32
|
+
|
|
33
|
+
- `evidenceLimit` (optional): a fixed host discriminator for `evidence-limit`: `path-count`, `provider-rounds`, `file-count`, `duplicate-file`, `context-bytes`, `invalid-byte-budget`, `file-stat-bytes`, or `file-read-bytes`. The public error code and implementation marker remain unchanged. Provider-round exhaustion takes precedence over file-count and duplicate checks; with the current three-round/two-read limits, the file-count branch cannot independently be reached. Stat-size and read-growth failures remain distinct without recording sizes.
|
|
34
|
+
|
|
35
|
+
These diagnostics contain no prompts, file contents, paths, credentials, provider payloads, or raw errors. Unknown fields are dropped; invalid metadata is omitted. No diagnostic changes a verdict, deadline, retry policy, cancellation, or lease. Missing diagnostics may mean an older or injected implementation; absence alone does not prove which implementation ran. After reloading the instrumented code, one explicitly authorized Auto read is sufficient to collect the blocking message if the failure recurs; it is not an instruction to retry repeatedly.
|
|
36
|
+
|
|
37
|
+
## Scope
|
|
38
|
+
|
|
39
|
+
This unit changes classifier behavior only. Runtime path selection, bounded call history and recovery messaging are integrated separately. Benign numeric token declarations and `max_tokens` limits no longer trigger credential heuristics; actual credential assignments, authentication values and private keys remain protected. Prior actions are projected once, not charged twice. Optional oldest complete history records are omitted when necessary to fit the input budget; active human requests, restrictions and the pending action are never truncated. No sandbox, Normal/YOLO, native-consent, or hard-policy changes are made.
|
package/docs/bundled-install.md
CHANGED
|
@@ -13,7 +13,7 @@ The building blocks live in `scripts/bundled-install.mjs`. The web installer and
|
|
|
13
13
|
| `~/.gentle-shell/npm/{prefix,cache}/`, `~/.gentle-shell/npm/npmrc` | npm's global prefix, cache and global config. The global config file is never written. |
|
|
14
14
|
| `~/.gentle-shell/runtime/pnpm-11.1.1/package/` | Our pnpm, from the verified registry tarball. |
|
|
15
15
|
| `~/.gentle-shell/runtime/go/1.25.14/go/` | Our Go, only when asked (`acquireGo` layout). |
|
|
16
|
-
| `~/.gentle-shell/runtime/engram-3.
|
|
16
|
+
| `~/.gentle-shell/runtime/engram-3.4.0/engram` | Our Engram, from the verified GitHub release archive ([Isolation from the user's Gentle AI](#isolation-from-the-users-gentle-ai)). |
|
|
17
17
|
| `~/.gentle-shell/gentle-ai/` | Gentle AI's state, managed assets, backups and telemetry state (`GENTLE_AI_HOME`). |
|
|
18
18
|
| `~/.gentle-shell/config/gentle-ai/` | gentle-pi's config home in a bundled install (derived, never exported to Pi): profiles, preferences, persona, background subagents. |
|
|
19
19
|
| `~/.gentle-shell/pnpm/{store,cache,state,config}/` | Every folder pnpm writes. |
|
|
@@ -229,7 +229,7 @@ Dropped: inherited npm and pnpm settings, `NODE_OPTIONS`, `NODE_PATH`, and the w
|
|
|
229
229
|
|
|
230
230
|
### Engram startup safety
|
|
231
231
|
|
|
232
|
-
A bundled Shell decides, before setup and again right before Pi starts, whether memory may open the shared Engram store. When it may not, Shell keeps working without memory and **points `ENGRAM_BIN` at its own guard**, so no provider, whichever Pi route loaded it, can open, create or migrate a store it cannot vouch for. The core pin
|
|
232
|
+
A bundled Shell decides, before setup and again right before Pi starts, whether memory may open the shared Engram store. When it may not, Shell keeps working without memory and **points `ENGRAM_BIN` at its own guard**, so no provider, whichever Pi route loaded it, can open, create or migrate a store it cannot vouch for. The core pin is **3.4.0**, acquired through the existing size- and checksum-verified path.
|
|
233
233
|
|
|
234
234
|
#### Decision
|
|
235
235
|
|
|
@@ -254,21 +254,23 @@ Classification never runs an Engram executable (`instance-id`, `version`, `serve
|
|
|
254
254
|
|
|
255
255
|
#### Provenance (Shell's own store)
|
|
256
256
|
|
|
257
|
-
When the final pre-Pi decision is "fresh" (no data directory, nothing listening, verified bundled core, no explicit selection), Shell provisions the store identity itself **before** Pi starts: it runs the verified bundled core's `instance-id` (15 s timeout) with the environment Pi will use, except that this one invocation gets `ENGRAM_NO_UPDATE_CHECK=1` and no `GH_TOKEN`/`GITHUB_TOKEN`. In Engram 3.
|
|
257
|
+
When the final pre-Pi decision is "fresh" (no data directory, nothing listening, verified bundled core, no explicit selection), Shell provisions the store identity itself **before** Pi starts: it runs the verified bundled core's `instance-id` (15 s timeout) with the environment Pi will use, except that this one invocation gets `ENGRAM_NO_UPDATE_CHECK=1` and no `GH_TOKEN`/`GITHUB_TOKEN`. In Engram 3.4.0 that command only creates the data directory, `.instance-id.lock` and `.instance-id`; it skips orphan migration and opens no database. Shell then reads the `.instance-id` **file** (regular, not a link, a valid id equal to the command's output, in the same default directory it classified as absent) and immediately writes `<prefix>/config/engram-provenance.json` (mode 0600): the canonical data directory, that instance id, and the sha256 of the bundled core. If any step fails (the command fails or times out, the identity is invalid, or the record cannot be written), memory is disabled for that launch behind the guard; Pi never starts with memory allowed without a written record. Shell never records a store it did not provision, so a store that appears later at the default directory is never adopted.
|
|
258
258
|
|
|
259
259
|
A later launch treats the store as Shell's own only when all hold:
|
|
260
260
|
|
|
261
261
|
1. The data directory, its `.instance-id` and the current bundled core digest equal the record. A Shell upgrade that changes the core binary therefore disables memory: forward upgrades are not allowed by version.
|
|
262
|
-
2. The directory still holds only identity files (the session has not created a database yet), or `engram.db` is a regular, unlinked file whose first 100 bytes are a valid SQLite header (magic, page size, bytes 18/19 each 1 or 2) and whose `user_version` (bytes 60-63) is **not greater** than the bundled core's schema (`1`, verified from `v3.
|
|
262
|
+
2. The directory still holds only identity files (the session has not created a database yet), or `engram.db` is a regular, unlinked file whose first 100 bytes are a valid SQLite header (magic, page size, bytes 18/19 each 1 or 2) and whose `user_version` (bytes 60-63) is **not greater** than the bundled core's schema (`1`, verified from `v3.4.0:internal/store/store.go`). A greater value means a newer Engram migrated the shared store. Equal or lower (including a stale `0` before a WAL checkpoint) is no signal either way. A pin without a verified schema entry disables this path.
|
|
263
263
|
3. If a server listens, its `/health` reports the recorded instance and exactly the bundled core version.
|
|
264
264
|
|
|
265
265
|
#### Compatible reuse and authorization
|
|
266
266
|
|
|
267
|
-
`GET /compatibility` (Engram PR #1792
|
|
267
|
+
`GET /compatibility` (published in Engram 3.4.0 through PR #1792; 3.3.2 answers 404) must return `status: "ready"`, `active_reuse_authorized: true`, `capability: "startup-verified-store-v1"`, `revision: 1`, `source: "active"`, 32-hex `instance_id` and `runtime_id`, and no `offline_automatic_opening_authorized: true`. The endpoint uses the server's `requireAuth`: open when the **server** has no `ENGRAM_HTTP_TOKEN`, otherwise it needs `Authorization: Bearer <token>`. Shell owns no such secret and sends none, so a token-protected server answers 401 and stays unknown (disabled).
|
|
268
268
|
|
|
269
269
|
#### Recovery and setup
|
|
270
270
|
|
|
271
|
-
When
|
|
271
|
+
When the running server's `/health` reports a valid 32-hex `instance_id` and a canonical numeric `x.y.z` version (optional `v` prefix, no leading zeros, at most four digits per component) older than 3.4.0, and `/compatibility` does not answer ready, the notice names that version and asks to back up the data, update **that server** to 3.4.0 or later with its own installation method, and restart it. The version only selects this wording: memory stays disabled, and any other version string is never shown. Otherwise, when disabled, Shell explains that it **could not verify safe store or server reuse**. Matching versions or updating alone do not prove compatibility. Reuse requires the verified active `/compatibility` contract or matching Shell-provisioned store identity under the rules above. Back up existing data before manual changes; do not delete the store, fabricate provenance, or bypass the safety check. Shell rechecks on every launch and remains usable without memory. It performs no migration, deletion, or process termination.
|
|
272
|
+
|
|
273
|
+
Engram 3.3.2 does not expose `/compatibility`, so reinstalling that version cannot authorize reuse of an existing global store. [Engram 3.4.0](https://github.com/Gentleman-Programming/engram/releases/tag/v3.4.0) includes the endpoint from PR #1792. Back up the existing store before updating its server through its own installation method, then restart that server and Shell. Updating Shell's bundled binary does not replace an already-running global server. Reuse still requires a valid ready response, not just the version number; a token-protected server cannot provide it to Shell's unauthenticated probe. Do not disable authentication to bypass this policy or use an unpublished build.
|
|
272
274
|
|
|
273
275
|
The memory decision never skips or fails setup: `gentle-shell setup` always publishes the verified core, runs Gentle AI and pins the companion, as before. Gentle AI 4.0.0 `install --agent pi` runs no `engram setup` for Pi, and `pi-engram init` only edits settings, so neither opens a store. Setup reclassifies after publishing the core, and the decision only selects the Engram environment its children and the launched Pi receive. On a new machine the wizard publishes the runtime without Engram, so the first `gentle-shell setup` publishes the core and the following launch is the fresh one that records provenance. Setup and every launch pin `npm:gentle-engram@0.4.2` through Pi's own `install` command (configuration only) and verify the declaration plus `engram.startupPolicy: 1`; a failed check is reported and Shell still starts, protected by the guard. `setup --dry-run` installs nothing.
|
|
274
276
|
|
package/docs/gentle-shell.md
CHANGED
|
@@ -6,6 +6,12 @@ For development, use [ODD and feature recovery](readme-reference.md#organic-driv
|
|
|
6
6
|
|
|
7
7
|
Source map: [shell extension](../extensions/gentle-shell.ts), [shell bar](../lib/shell-bar.ts), [changes model](../lib/shell-changes.ts), [changes view](../lib/shell-changes-view.ts), [usage model](../lib/shell-usage.ts), [usage view](../lib/shell-usage-view.ts), [agents extension](../extensions/gentle-agents.ts), and [agent runner](../lib/agents-runner.ts).
|
|
8
8
|
|
|
9
|
+
## Updating Pi safely
|
|
10
|
+
|
|
11
|
+
Run `gentle-shell update` to update the Pi this launcher runs. Shell verifies npm/pnpm global listing JSON and canonical package roots before installing exactly one supplying package. This supports pnpm v11 linked stores without relying on `PNPM_HOME` containment. When the launcher runs its adjacent Pi peer, the supplier is the listed `gentle-pi` package itself: Shell is reinstalled at its current version so the package manager re-resolves the peer, because updating a separate global Pi never changes the peer a Shell install runs. A Pi found on `PATH` or through `GENTLE_SHELL_PI` is updated through its own listed global package; a package-manager shim is followed to the single `cli.js` it names. Pi and Shell are never combined into a comma install.
|
|
12
|
+
|
|
13
|
+
Unknown or ambiguous ownership, npm source/link packages, and pnpm groups shared with other top-level packages refuse without installation changes. Separate groups in one aggregated listing are allowed. After the install, Shell reads the supplied Pi version back from a fresh listing and reports whether it changed. pnpm installs only releases older than its `minimumReleaseAge` setting (pnpm 11 defaults to 4320 minutes), so a very recent Pi release is picked up once it is old enough; Shell reports that rule instead of overriding it. Package managers run from the system temporary directory so a project's `packageManager` pin never changes which pnpm answers. Use the supplying package manager for source checkouts or unsupported layouts. `gentle-shell update --extensions-only` remains an extension-only Pi command; prefix-bundled Pi still updates with `gentle-shell upgrade`. See [managing packages](readme-reference.md#managing-packages) for the ownership contract.
|
|
14
|
+
|
|
9
15
|
## v2.6.0 workspace updates
|
|
10
16
|
|
|
11
17
|
The [v2.6.0 release](https://github.com/Gentleman-Programming/gentle-pi/releases/tag/v2.6.0) makes the workspace state more durable and inspectable:
|
|
@@ -70,14 +76,15 @@ Changes shows the **current Git state** — working tree against HEAD, untracked
|
|
|
70
76
|
|
|
71
77
|
### What appears in Changes
|
|
72
78
|
|
|
73
|
-
- One group per root: the session cwd's repository, worktrees registered with `session_worktree_register` or launched for subagents, and independent repositories of authorized foreign subagents. Captured write/edit evidence only tells Changes which roots exist; Git
|
|
79
|
+
- One group per root: the session cwd's repository, worktrees registered with `session_worktree_register` or launched for subagents, and independent repositories of authorized foreign subagents. Captured write/edit evidence only tells Changes which roots exist; Git supplies the file inventory, with the dependency-backup exception below.
|
|
74
80
|
- Committed work disappears because Git no longer reports it; a removed worktree disappears because its directory is gone; a root whose Git fails or times out is omitted while the others stay.
|
|
75
81
|
- Each group header shows that root's branch, `no commits yet` for an unborn branch, or `detached` for a real detached HEAD.
|
|
76
|
-
- Edits from your editor, shell commands, or other tools appear too: Changes reflects Git, not attribution.
|
|
82
|
+
- Edits from your editor, shell commands, or other tools appear too: Changes reflects Git, not attribution.
|
|
83
|
+
- Untracked files inside `node_modules.backup-*` directories are automatically excluded before counting lines, at any depth; no `.gitignore` setup is needed. The suffix must be nonempty. Tracked files in those directories and ordinary new source files remain visible. Other unwanted artifacts can still be excluded with `.gitignore`.
|
|
77
84
|
|
|
78
85
|
### Bounds and session lifetime
|
|
79
86
|
|
|
80
|
-
Changes lists every changed file without a count limit. One selected diff is bounded at 1 MiB or 20,000 lines; beyond that the panel shows "Diff too large — press o to open in editor."
|
|
87
|
+
Changes lists every changed file outside the untracked dependency-backup exclusion without a count limit. One selected diff is bounded at 1 MiB or 20,000 lines; beyond that the panel shows "Diff too large — press o to open in editor."
|
|
81
88
|
|
|
82
89
|
Captured evidence (`gentle-pi.session-change/v1`) is still stored for session statistics and root discovery, capped at 256 operations and 4 MiB per session, without affecting the Changes list. Reload and resume read the same state from Git; new sessions and forks do not inherit another session's worktrees.
|
|
83
90
|
|
|
@@ -112,11 +119,11 @@ The panel rows a provider reports its windows with:
|
|
|
112
119
|
glm5.3-flash ▰▰▱▱▱▱▱▱▱▱▱▱▱▱▱▱ 11% · resets in 12d 17h
|
|
113
120
|
```
|
|
114
121
|
|
|
115
|
-
- For Codex, usage comes from the same account usage endpoint the Codex CLI reads, using the OAuth token pi already holds. It is fetched at session start, at most every 5 minutes after a turn, and on `r` in the panel. Rate-limit headers on SSE responses are picked up too. Codex windows require a finite positive duration reported by the provider; missing or invalid durations are ignored, never shown as `0m` or replaced with an assumed `5h` quota. A real `0%` remains visible when its window is valid, and a response with no valid header windows leaves the previous snapshot untouched. The same refresh covers every provider the session targets: the active model's own provider plus every provider the
|
|
116
|
-
- A routing entry names its provider with a qualified ref (`provider/model`); a bare model id is resolved through the model registry only when exactly one provider carries that id, and is left untargeted rather than guessed when none or several do. The targeted scope is resolved when a refresh runs — at session start, on each turn's throttled refresh, on `r` or reopening the panel, and when a usage source registers — so a profile
|
|
122
|
+
- For Codex, usage comes from the same account usage endpoint the Codex CLI reads, using the OAuth token pi already holds. It is fetched at session start, at most every 5 minutes after a turn, and on `r` in the panel. Rate-limit headers on SSE responses are picked up too. Codex windows require a finite positive duration reported by the provider; missing or invalid durations are ignored, never shown as `0m` or replaced with an assumed `5h` quota. A real `0%` remains visible when its window is valid, and a response with no valid header windows leaves the previous snapshot untouched. The same refresh covers every provider the session targets: the active model's own provider plus every provider the session profile's subagent routing names — the same profile subagent launches use: the profile selected in this session, otherwise the profile the session froze at startup from the local pin, the repository declaration, or the global active profile (see [Session profile frozen at startup](readme-reference.md#session-profile-frozen-at-startup)). Each provider keeps its own 5-minute window, its own stale-source guard, and its own last good snapshot. Providers refresh concurrently, each inside its own bounded window (default 10s, `GENTLE_PI_SHELL_USAGE_TIMEOUT_MS`): the abort signal reaches the underlying fetch — composed with the caller's own signal when it carries one, never replacing it — a provider that outlives its window wears the generic failure note, and whatever it answers afterwards is discarded — a late answer never replaces what the timeout settled, exactly like the stale-source guard above.
|
|
123
|
+
- A routing entry names its provider with a qualified ref (`provider/model`); a bare model id is resolved through the model registry only when exactly one provider carries that id, and is left untargeted rather than guessed when none or several do. The targeted scope is resolved when a refresh runs — at session start, on each turn's throttled refresh, on `r` or reopening the panel, and when a usage source registers — so a profile selected in the session (or a default change in `follow` mode) is picked up by the next refresh rather than live per render.
|
|
117
124
|
- For Claude Pro/Max, usage arrives in the rate-limit headers of every response, so the 5h and weekly windows appear after the first turn.
|
|
118
125
|
- For NaN Cloud, usage comes from the quota endpoint the official dashboard reads, with the same API key pi already holds. Each metered model reports one allowance for the billing period, and that window carries no label: the model id names it in the bar and the reset text says what it is in the panel. A model that also reports a rolling window shows that one labeled next to it (`4h`), which today's payload does not send; percentages are tokens used over the allowance, exactly as the dashboard draws them, and the allowance is the full-period cap (`fullCap`) whenever the model reports a positive one, because `cap` alone is the prorated allowance of the period in progress. It is fetched under the same 5-minute rule as Codex, counted per provider so a switch fetches the provider it switched to, refuses redirects so the bearer cannot be replayed to another origin, and keeps no cached copy. The endpoint sits outside NaN's published OpenAPI, so the parser reads it defensively: a model that reports no allowance is skipped, as the dashboard skips it, while a metered model whose usage cannot be read fails the whole read, so a partial payload never replaces a complete snapshot with a cheaper-looking one. A session that already has a snapshot keeps the last valid one through a malformed payload or a failed fetch, and the pending note appears only while there is nothing to draw.
|
|
119
|
-
- Extensions can register a usage source for their own provider: gentle-shell has no built-in knowledge of it, but treats it exactly like Codex or NaN once registered. Emit `gentle-pi:usage-source/v1` on `pi.events` with `{ schema: "gentle-pi.usage-source/v1", provider, pendingNote?, fetch(apiKey, fetchFn, now) }`, where `fetch` resolves a `ProviderUsage` the same shape the built-in providers produce, or `undefined` when there is nothing to show yet. A malformed payload, a `fetch` that isn't a function, a provider id outside the safe id pattern, or a `fetch` call that throws or rejects is ignored rather than crashing the shell. The `fetchFn` a source receives is bounded the same way: once the provider's window expires it aborts, and a later resolution is discarded. Re-registering the same provider replaces its source, so emitting again at every `session_start` is safe and keeps load order irrelevant. Once registered, the provider shows `pendingNote` (or the same "no usage yet · r to fetch" default the built-ins use) until its first fetch, and a registration that arrives after the session already started, for any targeted provider (the session's own or a subagent route of the
|
|
126
|
+
- Extensions can register a usage source for their own provider: gentle-shell has no built-in knowledge of it, but treats it exactly like Codex or NaN once registered. Emit `gentle-pi:usage-source/v1` on `pi.events` with `{ schema: "gentle-pi.usage-source/v1", provider, pendingNote?, fetch(apiKey, fetchFn, now) }`, where `fetch` resolves a `ProviderUsage` the same shape the built-in providers produce, or `undefined` when there is nothing to show yet. A malformed payload, a `fetch` that isn't a function, a provider id outside the safe id pattern, or a `fetch` call that throws or rejects is ignored rather than crashing the shell. The `fetchFn` a source receives is bounded the same way: once the provider's window expires it aborts, and a later resolution is discarded. Re-registering the same provider replaces its source, so emitting again at every `session_start` is safe and keeps load order irrelevant. Once registered, the provider shows `pendingNote` (or the same "no usage yet · r to fetch" default the built-ins use) until its first fetch, and a registration that arrives after the session already started, for any targeted provider (the session's own or a subagent route of the session profile), triggers one immediate refresh of that provider instead of waiting for the next turn or the 5-minute window. Example, using a neutral provider id:
|
|
120
127
|
|
|
121
128
|
```ts
|
|
122
129
|
pi.events.emit("gentle-pi:usage-source/v1", {
|
|
@@ -204,7 +211,7 @@ Gentle Shell ships its own interactive tools instead of depending on third-party
|
|
|
204
211
|
|
|
205
212
|
### Gentle Agents
|
|
206
213
|
|
|
207
|
-
The current package requires Pi
|
|
214
|
+
The current package requires Pi 1.2.0 or newer and Node >=22.19.0. Development tests resolve Pi through the open `>=1.0.0` development range. The private Vim editor adapter admits only the audited Pi `0.99.1`, `0.99.2`, `1.0.0`, `1.1.0`, and `1.2.0` releases; any other release keeps ordinary prompt editing until its editor is audited. Use the latest Pi release; gentle-pi does not update your installed Pi automatically. Children, including any `GENTLE_PI_AGENTS_PI` override, must emit `agent_settled`: `agent_end` records a run's output but is not completion because retries or queued continuations may follow.
|
|
208
215
|
|
|
209
216
|
The `subagent_*` tools and the agents card replace the third-party subagents package (remove `npm:pi-subagents-j0k3r` from your pi packages; while it is still installed the tools stay unregistered and a warning says so at startup). Agent definitions and settings are the ones you already have: markdown agents in `~/.pi/agent/agents/`, `~/.pi/agent/subagents/`, `<cwd>/.pi/agents/`, `<cwd>/.pi/subagents/` (project beats global, `subagents/` beats `agents/`), and `subagents.json` at the global and project level (`default_model`, `default_effort`, `default_mode`, `model_profiles`, `stall_timeout_ms`, `tool_stall_timeout_ms`, `max_concurrency`, `history_max_tasks`).
|
|
210
217
|
|
|
@@ -284,8 +291,8 @@ The `todo` tool and its card replace the third-party todo extension (remove `npm
|
|
|
284
291
|
The runtime helps keep the list current beyond a static tool description:
|
|
285
292
|
|
|
286
293
|
- `write` replaces the whole list in one call, so the model rewrites the plan instead of patching it; `add`, `update`, `clear`, and `list` remain for single moves.
|
|
287
|
-
- Each agent run's system prompt carries the
|
|
288
|
-
- A list that goes two agent runs untouched while pending or in-progress tasks remain turns amber with `stale · N turns
|
|
294
|
+
- Each agent run's system prompt carries the same byte-stable rules block (in_progress before starting, done right after finishing, update before ending the run), so the cached prompt prefix never changes because of the list. While tasks stay open, the current list reaches the model as a hidden, request-local message at the end of each request; it is never persisted and never enters the system prompt, so status, title, note and ordering changes cost only the tail of the request.
|
|
295
|
+
- A list that goes two agent runs untouched while pending or in-progress tasks remain turns amber with `stale · N turns` on the card; that age is shown to the human only.
|
|
289
296
|
- Within a long run, after four tool-use model turns without a successful todo write, the next model request receives one hidden, request-local reminder to review the plan. A successful `write`, `add`, `update`, or `clear` resets and rearms it; `list` and rejected writes do not. The turn containing the write does not count toward the threshold. Empty, finished and blocked-only plans receive no reminder. Reminder tracking restarts when the session loads; it does not change the existing card freshness, persist a message, start an extra model turn, change task statuses, or connect to subagents.
|
|
290
297
|
|
|
291
298
|
Tasks are `pending` (`○`), `in_progress` (`◐`), `blocked` (`⊘`), `done` (`✓`), or `dropped` (`✕`):
|
|
@@ -295,7 +302,7 @@ Tasks are `pending` (`○`), `in_progress` (`◐`), `blocked` (`⊘`), `done` (`
|
|
|
295
302
|
|
|
296
303
|
A finished list stays on screen for the turn it finished in and clears at the next. `ctrl+shift+t` collapses the card to one task line, truncating long titles and notes with `…` (`GENTLE_PI_TODO_KEY` rebinds it, `off` disables it); `GENTLE_PI_TODO=0` disables the tool and the card.
|
|
297
304
|
|
|
298
|
-
Expanded cards reserve at most one third of the terminal height (up to 16 rows, with a minimum for card chrome), both in the sidebar and above the editor on narrow/mobile terminals. Scroll the full task text with the wheel in fullscreen mode or `
|
|
305
|
+
Expanded cards reserve at most one third of the terminal height (up to 16 rows, with a minimum for card chrome), both in the sidebar and above the editor on narrow/mobile terminals. Scroll the full task text with the wheel in fullscreen mode or `alt+shift+up` / `alt+shift+down` in either mode (Pi keeps `ctrl+shift+up` / `ctrl+shift+down` for its own prompt navigation). The collapse header stays visible; the row-range hint shows when more content is available. Task data and the agent's plan remain complete.
|
|
299
306
|
|
|
300
307
|
### Gentle Stats
|
|
301
308
|
|
package/docs/install-wizard.md
CHANGED
|
@@ -58,7 +58,7 @@ it lets the window close.
|
|
|
58
58
|
|
|
59
59
|
Current requirements come from `package.json` and
|
|
60
60
|
[`gentle-ai-installer.mjs`](../scripts/gentle-ai-installer.mjs): Node ≥22.19.0,
|
|
61
|
-
Pi ≥
|
|
61
|
+
Pi ≥1.2.0, pnpm acquisition 11.1.1, Gentle Shell package 4.0.0 and package-local
|
|
62
62
|
Gentle AI 4.1.1. Windows native source builds require Go ≥1.25.10 when the
|
|
63
63
|
required usable native binary is missing. A reusable binary does not require Go.
|
|
64
64
|
When a build needs Go and the user's Go is missing, older or unchecked, the installer uses
|
|
@@ -489,9 +489,9 @@ Mutating and verification steps, returning `failed` with `failedStep` and the
|
|
|
489
489
|
[runtime persistence](#runtime-persistence) steps run next:
|
|
490
490
|
|
|
491
491
|
1. `install-global`: exactly one
|
|
492
|
-
`pnpm add -g @earendil-works/pi-coding-agent@1.
|
|
492
|
+
`pnpm add -g @earendil-works/pi-coding-agent@1.2.0 gentle-pi@<package version> --allow-build=gentle-pi`.
|
|
493
493
|
pnpm 11 installs each argument as its own global group, so gentle-pi gets
|
|
494
|
-
its optional peer Pi beside it, possibly newer than 1.
|
|
494
|
+
its optional peer Pi beside it, possibly newer than 1.2.0
|
|
495
495
|
([The Pi that Gentle Shell runs](#the-pi-that-gentle-shell-runs)). pnpm 11
|
|
496
496
|
silently skips global postinstall scripts by default; the package-scoped
|
|
497
497
|
approval runs only gentle-pi's postinstall, which provisions Gentle AI. No
|
|
@@ -503,8 +503,8 @@ Mutating and verification steps, returning `failed` with `failedStep` and the
|
|
|
503
503
|
launcher runs from that real path, as Node resolves it (gentle-pi's own
|
|
504
504
|
`node_modules` first, then beside gentle-pi), must be a
|
|
505
505
|
`@earendil-works/pi-coding-agent` package at a stable version at least the
|
|
506
|
-
minimum (
|
|
507
|
-
it is not 1.
|
|
506
|
+
minimum (1.2.0); a missing, unreadable, prerelease or older one fails. When
|
|
507
|
+
it is not 1.2.0, the outcome reports it as `piVersion` and the wizard says
|
|
508
508
|
which Pi Gentle Shell runs.
|
|
509
509
|
3. `verify-shell-bin`: `$PNPM_HOME/bin/gentle-shell` (`gentle-shell.cmd` on
|
|
510
510
|
Windows) exists.
|
|
@@ -563,7 +563,7 @@ installation instead of blocking as an existing stack.
|
|
|
563
563
|
Detection is the setup probe, with the same rule the runner applies
|
|
564
564
|
(`recoverableStackRoot`): the global listing names Pi and gentle-pi exactly
|
|
565
565
|
once each, both in the single project that owns gentle-pi, Pi at exactly
|
|
566
|
-
1.
|
|
566
|
+
1.2.0 and gentle-pi at exactly this package's version, gentle-pi's path
|
|
567
567
|
resolves (realpath) inside PNPM_HOME, and the Pi beside gentle-pi is at least
|
|
568
568
|
the minimum. Preflight also requires Pi, Shell and
|
|
569
569
|
Gentle AI to be `reusable`. Anything else (another version, a second listing,
|
|
@@ -650,7 +650,7 @@ Pi but never reinstalls or downgrades it, and nothing changes before consent.
|
|
|
650
650
|
|
|
651
651
|
| Found | Plan |
|
|
652
652
|
| --- | --- |
|
|
653
|
-
| A compatible Pi (pnpm-global, or any `pi` on PATH whose `pi --version` reports a stable version ≥ the minimum) and no Gentle Shell | Install only Gentle Shell: `pnpm add -g gentle-pi@<version> --allow-build=gentle-pi`, after checking that pnpm lists no gentle-pi (`check-existing-shell`). Pi is left as it is, also when it is newer than 1.
|
|
653
|
+
| A compatible Pi (pnpm-global, or any `pi` on PATH whose `pi --version` reports a stable version ≥ the minimum) and no Gentle Shell | Install only Gentle Shell: `pnpm add -g gentle-pi@<version> --allow-build=gentle-pi`, after checking that pnpm lists no gentle-pi (`check-existing-shell`). Pi is left as it is, also when it is newer than 1.2.0. |
|
|
654
654
|
| A Gentle Shell that pnpm or npm owns | `update-shell-release` when it is older, unusable or a main build; `update-shell-main` on the main channel (needs Go). Then `setup-shell`. A missing Pi is installed first (`install-pi`). |
|
|
655
655
|
| A current Gentle Shell that npm owns, on release | Nothing to do. |
|
|
656
656
|
| A Pi older than the minimum that pnpm or npm owns | `update-pi` to `PI_INSTALL_VERSION` with that package manager (`pnpm add -g @earendil-works/pi-coding-agent@<version>` or `npm install -g …`), before any Gentle Shell step: ahead of `install-shell` or `update-shell-*`, or alone when Gentle Shell is current. The plan names the found and target versions and the manager. |
|
|
@@ -677,7 +677,7 @@ the same code as `gentle-shell upgrade --channel <channel>`
|
|
|
677
677
|
(`check-installed-shell`, `update-shell`), then `verify-updated-shell` requires
|
|
678
678
|
the same owner and a stable version not older than before (release) or a
|
|
679
679
|
`-main.<sha12>` version (main), and for pnpm a Pi beside the updated
|
|
680
|
-
gentle-pi at least the minimum (its version is reported when it is not 1.
|
|
680
|
+
gentle-pi at least the minimum (its version is reported when it is not 1.2.0);
|
|
681
681
|
release also re-runs `verify-gentle-ai`. An
|
|
682
682
|
update never runs `pnpm setup`: the existing installation already has its PATH.
|
|
683
683
|
|
|
@@ -734,7 +734,7 @@ PNPM_HOME, `pnpm add -g @earendil-works/pi-coding-agent@1.0.0 gentle-pi@4.0.0`
|
|
|
734
734
|
lists Pi 1.0.0 while the Pi beside gentle-pi is 1.1.0. So Gentle Shell may run a
|
|
735
735
|
Pi newer than `PI_INSTALL_VERSION`, always at least the minimum: the runner
|
|
736
736
|
verifies that Pi (`verify-global-list`, the setup recovery, a pnpm
|
|
737
|
-
`verify-updated-shell`) and, when it is not 1.0.0, the outcome says which Pi
|
|
737
|
+
`verify-updated-shell`) and, when it is not `PI_INSTALL_VERSION` (1.2.0 now; the 1.0.0/1.1.0 figures above are that older verification), the outcome says which Pi
|
|
738
738
|
Gentle Shell runs. `--config.auto-install-peers=false` would leave gentle-pi
|
|
739
739
|
with no Pi beside it, so the launcher would run any `pi` on PATH, and
|
|
740
740
|
`--config.overrides` is ignored for global installs.
|