@astralyn/pi 0.82.1 → 0.82.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/LICENSE +21 -0
  3. package/README.md +14 -3
  4. package/dist/core/agent-session.d.ts +4 -0
  5. package/dist/core/agent-session.d.ts.map +1 -1
  6. package/dist/core/agent-session.js +193 -43
  7. package/dist/core/agent-session.js.map +1 -1
  8. package/dist/core/extensions/loader.d.ts.map +1 -1
  9. package/dist/core/extensions/loader.js +6 -13
  10. package/dist/core/extensions/loader.js.map +1 -1
  11. package/dist/core/tools/tool-definition-wrapper.d.ts +7 -2
  12. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
  13. package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
  14. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  15. package/dist/modes/interactive/interactive-mode.js +25 -12
  16. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  17. package/docs/compaction.md +8 -2
  18. package/docs/containerization.md +1 -1
  19. package/docs/development.md +32 -35
  20. package/docs/distribution/README.md +34 -0
  21. package/docs/distribution/architecture.md +102 -0
  22. package/docs/distribution/extensions/README.md +17 -0
  23. package/docs/distribution/extensions/deepwiki.md +62 -0
  24. package/docs/distribution/extensions/question.md +59 -0
  25. package/docs/distribution/extensions/rewind.md +85 -0
  26. package/docs/distribution/extensions/router.md +139 -0
  27. package/docs/distribution/extensions/statusline.md +110 -0
  28. package/docs/distribution/extensions/subagent.md +102 -0
  29. package/docs/distribution/maintenance.md +121 -0
  30. package/docs/distribution/release.md +85 -0
  31. package/docs/distribution/themes.md +58 -0
  32. package/docs/distribution/tool-presentation.md +91 -0
  33. package/docs/extensions.md +4 -2
  34. package/docs/index.md +1 -1
  35. package/docs/rpc.md +2 -2
  36. package/docs/themes.md +1 -1
  37. package/examples/extensions/custom-provider-anthropic/package-lock.json +3 -3
  38. package/examples/extensions/custom-provider-gitlab-duo/test.ts +1 -1
  39. package/examples/extensions/doom-overlay/doom/build/doom.wasm +0 -0
  40. package/examples/extensions/doom-overlay/doom/build.sh +0 -0
  41. package/examples/extensions/sandbox/package-lock.json +1 -1
  42. package/examples/extensions/with-deps/package-lock.json +2 -2
  43. package/npm-shrinkwrap.json +3465 -624
  44. package/package.json +30 -18
@@ -34,6 +34,12 @@ contextTokens > contextWindow - reserveTokens
34
34
 
35
35
  By default, `reserveTokens` is 16384 tokens (configurable in `~/.pi/agent/settings.json` or `<project-dir>/.pi/settings.json`). This leaves room for the LLM's response.
36
36
 
37
+ During an active tool-calling run, Pi evaluates this threshold after each complete tool batch, including the newly appended tool results, and before another provider request. If compaction succeeds, Pi rebuilds the context and continues the same agent run without inserting a continuation prompt. If compaction is cancelled, fails, has no safe cut point, or leaves the estimated retained context above the threshold, Pi fails closed before queue polling or another provider request. Because the upstream stateful Agent does not expose its low-level graceful turn-stop hook, this exceptional path ends with an explicit error/aborted assistant lifecycle; the preceding `compaction_end` event contains the underlying warning or failure.
38
+
39
+ This between-tool-batch trigger is separate from **split-turn compaction** below: the trigger determines *when* compaction runs, while split-turn support determines *where* a very long turn can be cut.
40
+
41
+ Before a new user prompt, Pi also refuses to send an existing context whose estimate remains above the threshold after compaction. This check happens before adding the new prompt itself. Reduce `keepRecentTokens`, remove unusually large retained content, or switch to a model with a larger context window before continuing.
42
+
37
43
  You can also trigger manually with `/compact [instructions]`, where optional instructions focus the summary.
38
44
 
39
45
  ### How It Works
@@ -42,7 +48,7 @@ You can also trigger manually with `/compact [instructions]`, where optional ins
42
48
  2. **Extract messages**: Collect messages from the previous kept boundary (or session start) up to the cut point
43
49
  3. **Generate summary**: Call LLM to summarize with structured format, passing the previous summary as iterative context when present
44
50
  4. **Append entry**: Save `CompactionEntry` with summary and `firstKeptEntryId`
45
- 5. **Reload**: Session reloads, using summary + messages from `firstKeptEntryId` onwards
51
+ 5. **Reload and verify**: Rebuild context using the summary plus messages from `firstKeptEntryId` onwards, then verify the estimated retained context is below the auto-compaction threshold before continuing automatically
46
52
 
47
53
  ```
48
54
  Before compaction:
@@ -290,7 +296,7 @@ pi.on("session_before_compact", async (event, ctx) => {
290
296
 
291
297
  // branchEntries - all entries on current branch (for custom state)
292
298
  // reason - "manual" (/compact), "threshold", or "overflow"
293
- // willRetry - whether the aborted turn is retried after compaction (overflow recovery)
299
+ // willRetry - whether overflow recovery intends to retry if compaction produces a safe retained context
294
300
  // signal - AbortSignal (pass to LLM calls)
295
301
 
296
302
  // Cancel:
@@ -24,7 +24,7 @@ Use the [example extension](../examples/extensions/gondolin) when you want `pi`
24
24
  Setup:
25
25
 
26
26
  ```bash
27
- cp -R packages/coding-agent/examples/extensions/gondolin ~/.pi/agent/extensions/gondolin
27
+ cp -R examples/extensions/gondolin ~/.pi/agent/extensions/gondolin
28
28
  cd ~/.pi/agent/extensions/gondolin
29
29
  npm install --ignore-scripts
30
30
  ```
@@ -1,71 +1,68 @@
1
1
  # Development
2
2
 
3
- See [AGENTS.md](https://github.com/earendil-works/pi-mono/blob/main/AGENTS.md) for additional guidelines.
3
+ See the repository [AGENTS.md](../AGENTS.md) for the distribution contract and coding conventions.
4
4
 
5
5
  ## Setup
6
6
 
7
7
  ```bash
8
- git clone https://github.com/earendil-works/pi-mono
9
- cd pi-mono
10
- npm install
8
+ git clone https://github.com/ming-kang/pi
9
+ cd pi
10
+ npm install --ignore-scripts
11
11
  npm run build
12
+ npm run check
12
13
  ```
13
14
 
15
+ The repository is the standalone `@astralyn/pi` package. AI, Agent core, and TUI are installed as exact upstream npm dependencies; no sibling workspace build is required.
16
+
14
17
  Run from source:
15
18
 
16
19
  ```bash
17
- /path/to/pi-mono/pi-test.sh
20
+ ./pi-test.sh
18
21
  ```
19
22
 
20
- The script can be run from any directory. Pi keeps the caller's current working directory.
23
+ The script can be invoked from any directory. Pi keeps the caller's current working directory. Use `./pi-test.sh --no-env` to clear provider credentials for the source run.
21
24
 
22
- ## Forking / Rebranding
25
+ ## Package identity
23
26
 
24
- Configure via `package.json`:
27
+ Distribution identity and configuration directory defaults are defined in `package.json`:
25
28
 
26
29
  ```json
27
30
  {
31
+ "name": "@astralyn/pi",
28
32
  "piConfig": {
29
- "name": "pi",
30
33
  "configDir": ".pi"
34
+ },
35
+ "bin": {
36
+ "pi": "dist/cli.js"
31
37
  }
32
38
  }
33
39
  ```
34
40
 
35
- Change `name`, `configDir`, and `bin` field for your fork. Affects CLI banner, config paths, and environment variable names.
36
-
37
- ## Path Resolution
38
-
39
- Three execution modes: npm install, standalone binary, tsx from source.
40
-
41
- **Always use `src/config.ts`** for package assets:
41
+ ## Path resolution
42
42
 
43
- ```typescript
44
- import { getPackageDir, getThemeDir } from "./config.js";
45
- ```
46
-
47
- Never use `__dirname` directly for package assets.
43
+ Pi supports npm installation and `tsx` source execution. Always use `src/config.ts` helpers such as `getPackageDir()` and `getThemeDir()` for package assets; do not derive asset paths directly from `__dirname`.
48
44
 
49
- ## Debug Command
45
+ ## Debug command
50
46
 
51
- `/debug` (hidden) writes to `~/.pi/agent/pi-debug.log`:
52
- - Rendered TUI lines with ANSI codes
53
- - Last messages sent to the LLM
47
+ `/debug` (hidden) writes rendered TUI lines and the latest model messages to `~/.pi/agent/pi-debug.log`.
54
48
 
55
49
  ## Testing
56
50
 
57
51
  ```bash
58
- ./test.sh # Run non-LLM tests (no API keys needed)
59
- npm test # Run all tests
60
- npm test -- test/specific.test.ts # Run specific test
52
+ npm run check
53
+ node node_modules/vitest/dist/cli.js --run test/specific.test.ts
54
+ ./test.sh # complete isolated test suite
61
55
  ```
62
56
 
63
- ## Project Structure
57
+ Release CI runs the full suite on Ubuntu. Interactive UI changes should also be verified in a real terminal.
64
58
 
65
- ```
66
- packages/
67
- ai/ # LLM provider abstraction
68
- agent/ # Agent loop and message types
69
- tui/ # Terminal UI components
70
- coding-agent/ # CLI and interactive mode
59
+ ## Project structure
60
+
61
+ ```text
62
+ src/ runtime source
63
+ test/ automated tests and local test helpers
64
+ docs/ product and public API documentation
65
+ examples/ SDK and extension examples
66
+ docs/distribution/ standalone distribution documentation
67
+ scripts/ package maintenance scripts
71
68
  ```
@@ -0,0 +1,34 @@
1
+ # Distribution documentation
2
+
3
+ This directory contains documentation owned by the standalone `@astralyn/pi` distribution. Product usage and public API documentation inherited from upstream coding-agent lives one level above under `docs/`.
4
+
5
+ ## User documentation
6
+
7
+ - [Distribution overview](../../README.md) — package identity, installation, bundled features, and development entry points.
8
+ - [Quickstart](../quickstart.md) — install, authenticate, and run a first session.
9
+ - [User and API documentation](../index.md) — interactive mode, settings, customization, SDK, RPC, and platform notes.
10
+ - [Extension API](../extensions.md) — write external TypeScript extensions.
11
+ - [Theme API](../themes.md) — create and load custom themes.
12
+
13
+ ## Architecture and design
14
+
15
+ - [Architecture and dependency boundaries](architecture.md)
16
+ - [Native tool presentation](tool-presentation.md)
17
+ - [Bundled theme design](themes.md)
18
+
19
+ ## Bundled extensions
20
+
21
+ - [Catalog](extensions/README.md)
22
+ - [deepwiki](extensions/deepwiki.md)
23
+ - [question](extensions/question.md)
24
+ - [rewind](extensions/rewind.md)
25
+ - [router](extensions/router.md)
26
+ - [statusline](extensions/statusline.md)
27
+ - [subagent](extensions/subagent.md)
28
+ - [todo](extensions/todo.md)
29
+
30
+ ## Maintainer documentation
31
+
32
+ - [Upstream synchronization](maintenance.md)
33
+ - [OIDC npm release checklist](release.md)
34
+ - [Repository contract](../../AGENTS.md)
@@ -0,0 +1,102 @@
1
+ # Distribution architecture
2
+
3
+ ## Purpose
4
+
5
+ This repository is a standalone personal distribution of Pi's coding-agent package. Its goals are to:
6
+
7
+ - maintain only code that ships in `@astralyn/pi`;
8
+ - consume AI, Agent core, and TUI from upstream npm releases;
9
+ - keep native tool presentation, bundled workflow extensions, themes, and context-safety behavior local to this package;
10
+ - avoid cross-workspace source changes and multi-package release coordination.
11
+
12
+ ## Package boundary
13
+
14
+ The repository root is the npm package:
15
+
16
+ ```text
17
+ src/ coding-agent runtime
18
+ test/ coding-agent tests
19
+ docs/ product and API documentation
20
+ examples/ SDK and extension examples
21
+ docs/distribution/ distribution-owned documentation
22
+ package.json @astralyn/pi
23
+ npm-shrinkwrap.json development and published dependency lock
24
+ ```
25
+
26
+ The runtime boundary is explicit and versioned in `package.json`:
27
+
28
+ - `@earendil-works/pi-ai`
29
+ - `@earendil-works/pi-agent-core`
30
+ - `@earendil-works/pi-tui`
31
+
32
+ These are exact registry dependencies. Their source is not copied into this repository and local verification resolves the same published packages that npm users install. A coding-agent synchronization that needs new upstream APIs must update all compatible dependency versions in the same change.
33
+
34
+ Presentation-only metadata such as `toolGroup` uses coding-agent-local intersection types rather than changing `AgentTool`. The long-tool-loop compaction implementation uses the upstream stateful Agent's public `prepareNextTurnWithContext` callback for successful same-run continuation. Exceptional fail-closed paths are represented locally as explicit error/aborted lifecycle boundaries because the stateful Agent does not expose its low-level graceful turn-stop callback.
35
+
36
+ ## Core and extension boundary
37
+
38
+ Pi core changes are reserved for behavior that must be global:
39
+
40
+ - native tool call/result chrome and generic renderer fallback;
41
+ - built-in renderer integration;
42
+ - context-safety checks such as between-tool-batch auto-compaction;
43
+ - globally configurable keybindings required by bundled UI.
44
+
45
+ Personal workflow features remain self-contained extensions:
46
+
47
+ ```text
48
+ src/extensions/
49
+ ├── deepwiki/
50
+ ├── question/
51
+ ├── todo/
52
+ ├── rewind/
53
+ ├── router/
54
+ ├── statusline/
55
+ └── subagent/
56
+ ```
57
+
58
+ They are registered as hidden `InlineExtension` entries in `src/extensions/index.ts`. No shared Fork framework, feature registry, or cross-extension internal API is used.
59
+
60
+ ## Configuration compatibility
61
+
62
+ The command remains `pi`, and settings and sessions remain under `~/.pi/agent`. Built-in extensions store their first-class data alongside the normal agent data, including:
63
+
64
+ ```text
65
+ ~/.pi/agent/router.json
66
+ ~/.pi/agent/rewind/
67
+ ```
68
+
69
+ No separate Fork configuration layer is introduced.
70
+
71
+ ## Themes
72
+
73
+ `ice-cream-dark` and `ice-cream-light` are native theme assets under `src/modes/interactive/theme/`. The normal package build copies all theme JSON files into `dist`.
74
+
75
+ ## Upstream synchronization
76
+
77
+ The `upstream` remote still points to `earendil-works/pi`, but upstream monorepo tags are not merged into this standalone branch. Synchronization extracts and reviews only `packages/coding-agent/**` from an upstream release tag, maps that subtree to the repository root, and updates exact registry dependency versions. See [maintenance.md](maintenance.md).
78
+
79
+ Recurring review hotspots are:
80
+
81
+ ```text
82
+ src/extensions/index.ts
83
+ src/core/agent-session.ts
84
+ src/core/keybindings.ts
85
+ src/core/tools/*.ts
86
+ src/modes/interactive/components/tool-execution.ts
87
+ src/modes/interactive/interactive-mode.ts
88
+ src/modes/interactive/theme/theme.ts
89
+ package.json
90
+ CHANGELOG.md
91
+ ```
92
+
93
+ ## Release model
94
+
95
+ Only `@astralyn/pi` is published. Fork releases track the upstream `major.minor` line while using the patch number for this distribution's release sequence:
96
+
97
+ ```text
98
+ upstream 0.82.x -> Fork 0.82.0, 0.82.1, 0.82.2, ...
99
+ upstream 0.83.x -> Fork 0.83.0, 0.83.1, 0.83.2, ...
100
+ ```
101
+
102
+ Tags use `pi-v<full-version>` so they do not collide with upstream `v<version>` tags. Ubuntu CI verifies the standalone package, and the manually dispatched publish workflow uploads the repository root with npm Trusted Publishing. No other package, binary release flow, model catalog, or update service is maintained here.
@@ -0,0 +1,17 @@
1
+ # Bundled extensions
2
+
3
+ These are extensions shipped by the distribution. For the public API used to write external extensions, see the [Extension API documentation](../../extensions.md).
4
+
5
+ The bundled extensions are maintained as self-contained TypeScript modules under `src/extensions/` and registered as hidden built-in extensions.
6
+
7
+ | Extension | Tool or command | Documentation |
8
+ |---|---|---|
9
+ | `deepwiki` | `deepwiki` | [deepwiki.md](deepwiki.md) |
10
+ | `question` | `question` | [question.md](question.md) |
11
+ | `todo` | `todo`, `/todos` | [todo.md](todo.md) |
12
+ | `rewind` | `/rewind`, `/tree` lifecycle | [rewind.md](rewind.md) |
13
+ | `router` | `/router` | [router.md](router.md) |
14
+ | `statusline` | Footer | [statusline.md](statusline.md) |
15
+ | `subagent` | `subagent`, `/agents` | [subagent.md](subagent.md) |
16
+
17
+ The extensions continue to use Pi's public Extension API. Keeping their distribution notes here does not merge their implementations into the coding-agent core.
@@ -0,0 +1,62 @@
1
+ # DeepWiki
2
+
3
+ `deepwiki` is a Pi-native tool for querying DeepWiki's generated documentation for GitHub repositories.
4
+
5
+ It is intentionally not a general MCP bridge. The extension exposes one dedicated tool with three fixed actions:
6
+
7
+ | Action | Behavior |
8
+ |---|---|
9
+ | `structure` | List the generated wiki topics for a repository. |
10
+ | `contents` | Read the generated wiki — one page via `page` (recommended), or everything when `page` is omitted (truncated past ~120k chars). |
11
+ | `question` | Ask a focused question about a repository. |
12
+
13
+ ## Usage
14
+
15
+ ### Parameters
16
+
17
+ - `action`: `structure`, `contents`, or `question`
18
+ - `repoName`: one `owner/repo` string for `structure` and `contents`; for `question`, one repo as a string, or 2–10 repos as an array (comma-separated string also accepted)
19
+ - `question`: required only for `action: "question"`
20
+ - `page`: optional, only for `action: "contents"` — 1-based index or page title from `structure` (plain titles such as `Extension System`, not numbered outline lines like `4.4 …`). Leading outline numbers on titles are stripped when matching. Unknown `page` fails with available titles listed.
21
+
22
+ `repoName` is normalized before validation (`owner/repo`, GitHub/DeepWiki URLs, JSON-array strings for multi-repo mistakes). `pageName`/`pageTitle` are accepted as `page` aliases. If `action` is omitted, defaults to `question` when a question is present, `contents` when `page` is present, else `structure`. Repositories not indexed on DeepWiki return repository-not-found errors — run `structure` first to detect that.
23
+
24
+ ### Strategy
25
+
26
+ Recommended flow for studying a repo:
27
+
28
+ 1. `structure` — get the page outline.
29
+ 2. `contents` + `page` — read the specific pages you need, one at a time.
30
+ 3. Avoid `contents` without `page` (the full wiki) unless the wiki is genuinely small; oversized output is truncated at ~120k chars.
31
+ 4. `question` — when the wiki pages don't cover what you need, or directly for a quick answer without reading.
32
+
33
+ Use DeepWiki when generated docs for public repositories are useful context:
34
+
35
+ - Understanding an unfamiliar public repo's architecture, module layout, APIs, data flow, or extension points.
36
+ - Researching reference implementations while designing or building a new feature or project.
37
+ - Asking a focused question about how a public repo implements something.
38
+ - Comparing patterns or extension surfaces across multiple public repos with `question` and a repo array.
39
+ - Pulling a broad topic map with `structure` before deciding which part of a repo matters.
40
+
41
+ The prompt is model-facing: when these conditions apply, the model should call the tool itself instead of telling the user to open DeepWiki manually.
42
+
43
+ Avoid DeepWiki when the authoritative source is local or time-sensitive:
44
+
45
+ - Local workspace files, private code, uncommitted changes, or exact current checkout state.
46
+ - Latest releases, pricing, security advisories, schedules, or facts that need current primary-source verification.
47
+ - Package names without a known GitHub repo; resolve the repo first instead of guessing.
48
+
49
+ ## Limits
50
+
51
+ - A repository may not be indexed. DeepWiki can return that as normal text, so the extension treats repository-not-found messages as tool errors.
52
+ - `contents` responses are truncated at a ~120k-character budget on `# Page:` boundaries (whole pages kept in order; at least the first page survives, cut mid-page if it alone exceeds the budget). A trailing notice reports shown/total pages, the full length, up to 20 omitted page titles, and points at `page` reads and `action: "question"` for the rest. Details carry `shownPages` / `truncatedChars` when this happens; `outputLength` always reflects the full untruncated response. Single-page reads set `requestedPage` / `pageIndex` instead and share the same budget.
53
+ - Network requests time out after 45 seconds and surface as Pi tool errors.
54
+ - Use local Pi tools (`read`, `grep`, `find`, `ls`) for workspace state. DeepWiki answers describe repository snapshots indexed by DeepWiki, not local uncommitted files.
55
+
56
+ ## Implementation notes
57
+
58
+ DeepWiki's public MCP endpoint exposes exactly three operations: list a wiki's page titles, return the whole generated wiki as one document, and answer a question. There is no per-page fetch upstream — `page` is implemented by this extension: it fetches the full wiki (or reuses the 10-minute cache) and slices out the requested page locally. The first `contents` call for a repo pays the network cost; subsequent page reads within the cache window are instant and free.
59
+
60
+ DeepWiki may return generated explanations with source-file citations and "related pages" links. The TUI uses a local renderer (`render.ts`) for a one-line collapsed summary and Markdown when expanded; Pi's default fallback would dump the full (often large) `content` text.
61
+
62
+ Successful responses are cached in-process for 10 minutes, keyed by action, repo, and question. Failed, aborted, and timed-out requests are not cached. The cache stores the full response; `contents` truncation is recomputed per call, so cached and fresh calls return identical text.
@@ -0,0 +1,59 @@
1
+ # question — structured user questions
2
+
3
+ Adds a `question` tool for asking one to four multiple-choice questions when the agent needs a user decision. A Pi-native take on AskUserQuestion: a lightweight custom dialog with concise transcript summaries and a bounded model-facing result.
4
+
5
+ ## Behavior
6
+
7
+ - Each option requires a concise description of its consequence or trade-off. Questions, labels, descriptions, and previews have input-length limits so the dialog stays usable.
8
+ - A single single-select question submits as soon as the user selects an option.
9
+ - A multi-question flow advances after each single-select answer; multi-select uses `Space` to toggle choices and Enter to continue. Once every question is answered, a **Review answers** view requires explicit submission.
10
+ - `←` / `→` switch between adjacent questions without wrapping. The tab bar marks answered questions and the Review state.
11
+ - `Chat about this` is available after the choices. It returns a `needs_clarification` outcome so the model explains or reformulates instead of treating the user as having declined.
12
+ - `Type something.` is appended automatically for custom answers. Authored options may not use reserved labels (`Other`, `Type something.`, `Chat about this`, `Next`).
13
+ - `Tab` opens a note editor for the focused option; in multi-select, the option must be selected first. On the custom-answer row, it opens custom-answer input.
14
+ - Multi-select custom answers stay on the `Type something.` row, are selected when saved, and can be toggled with `Space` without losing text.
15
+ - `preview` shows focused single-select content beside the choices on wide terminals and beneath them on narrow terminals. Preview height is capped and reports hidden lines.
16
+ - The dialog is available only in TUI mode. RPC, JSON, and print calls return a structured `no_ui` error rather than attempting a custom component.
17
+
18
+ ## Result contract
19
+
20
+ `details` preserves structured state for rendering and session history:
21
+
22
+ ```ts
23
+ {
24
+ answers,
25
+ outcome: "answered" | "cancelled" | "needs_clarification" | "error",
26
+ cancelled,
27
+ error?,
28
+ }
29
+ ```
30
+
31
+ Only `content` reaches the model. Successful results are numbered, clearly identify single/custom/multi answers, retain notes, and state when a preview was selected without echoing its full source. Model-facing output is capped at 12,000 characters; if it is truncated, the result instructs the model to ask a focused follow-up question. Notes and custom answers are capped at 4,000 characters in the dialog.
32
+
33
+ The transcript uses a private `renderCall` / `renderResult` only to replace raw question JSON and model-oriented result text with concise user summaries. Pi retains its native tool shell, pending/error state, and collapsed/expanded behavior.
34
+
35
+ ## Limits
36
+
37
+ - Questions, labels, descriptions, and previews have input-length limits so the dialog stays usable.
38
+ - Notes and custom answers are capped at 4,000 characters.
39
+ - Model-facing output is capped at 12,000 characters; if truncated, the result instructs the model to ask a focused follow-up question.
40
+ - Only available in TUI mode. RPC, JSON, and print calls return a structured `no_ui` error.
41
+
42
+ ## Implementation notes
43
+
44
+ - Uses Pi's native `ctx.ui.custom()` lifecycle; no state is shared with another extension.
45
+ - The render cache is keyed by terminal width **and height**, because preview height depends on available rows and Pi resize only requests a render.
46
+ - `validateQuestions` enforces uniqueness, reserved-label rejection, and text budgets beyond what the JSON schema can express.
47
+ - Dialog navigation follows Pi's injected select/input keybindings where applicable; custom actions such as Space-to-toggle remain explicit in the footer.
48
+
49
+ **Files:**
50
+
51
+ - `constants.ts` — tool identity and model-facing prompt copy
52
+ - `index.ts` — tool registration and execution
53
+ - `dialog.ts` — interactive dialog, review, and discussion flow
54
+ - `schema.ts` — params and semantic validation
55
+ - `state.ts` — per-question dialog state helpers and answer ordering
56
+ - `results.ts` — bounded model-result builders
57
+ - `render.ts` — compact transcript summaries within Pi's native tool frame
58
+ - `limits.ts` — UI and model-output budgets
59
+ - `types.ts` — shared interfaces and the `OTHER_OPTION` sentinel
@@ -0,0 +1,85 @@
1
+ # rewind — checkpoints & rewind
2
+
3
+ Per-edit file backups plus a `/rewind` settings menu. Instead of snapshotting the
4
+ whole working tree, rewind backs up **only the files Pi's `edit`/`write` tools are
5
+ about to change** — one `copyFile` before each edit. Cost is proportional to how
6
+ many files Pi changed, never to project size, so it never blocks the session
7
+ lifecycle (the old shadow-git design froze the UI in large multi-project
8
+ directories) and storage stays tiny.
9
+
10
+ ## Behavior
11
+
12
+ Per turn: `before_agent_start` opens a snapshot frame (re-recording every tracked
13
+ file at its turn-start state, reusing the latest backup when unchanged);
14
+ `tool_call(edit|write)` backs up each newly edited file *before* it lands;
15
+ `agent_settled` persists the frame to the session JSONL as a `pi-rewind-snapshot`
16
+ custom entry **only when files changed**. Using `agent_settled` (not `agent_end`)
17
+ keeps auto-retry, overflow compaction-retry, and queued follow-ups in one logical
18
+ turn — `agent_end` can fire while Pi still continues. Requires **Pi ≥ 0.80.4**
19
+ (when `agent_settled` was added); older hosts never fire that event, so frames
20
+ would not finalize. Entries survive `/reload` and compaction; the index is
21
+ rebuilt from them on `session_start`.
22
+
23
+ - **Scope (deliberate trade-off):** rewind covers edits made through Pi's built-in
24
+ `edit` and `write` tools. Files written by `bash` (redirects, codegen, `mv`) or
25
+ edited by hand outside Pi are **not** tracked — same boundary as Claude Code's
26
+ file-history. Rewind undoes *Pi's edits*, not arbitrary filesystem state.
27
+ - **Time-travel is via `/tree`.** Navigating to a node whose turn changed files
28
+ prompts to restore them, listing the affected files (cwd-relative, up to 8
29
+ then *"+N more"*) under *"Restore 3 files to this point? (+120 / −40)"*
30
+ when coarse line stats are available; choosing yes restores the work tree to
31
+ that turn's start state. Nodes with no file changes navigate silently. Only
32
+ files that actually differ are rewritten. Line totals are a bag-of-lines
33
+ estimate (capped at 1 MB per file), not a full Myers diff.
34
+ - **`/rewind` is a settings + storage menu**, not a restore picker:
35
+ - toggle rewind on/off,
36
+ - set the auto-clean retention window,
37
+ - inspect storage and prune (clean aged + orphaned / remove orphaned / remove
38
+ all except current).
39
+ - **New files** created by `write` are tracked with a "did not exist" marker, so
40
+ rewinding deletes them.
41
+ - **Resume/fork** hard-links the prior session's backup blobs into the new
42
+ session (falling back to copy), so rewind keeps working without duplicating
43
+ storage.
44
+
45
+ ## Storage & cleanup
46
+
47
+ - **Layout:** `~/.pi/agent/rewind/`
48
+ - `config.json` — `{ enabled, retentionDays, maxSnapshots }`
49
+ - `backups/<sessionId>/<sha256(relpath)[:16]>@v<n>` — backup blobs (flat, hashed
50
+ names keep paths short on Windows; mode preserved)
51
+ - **Auto-clean:** at `session_start`, backup directories older than
52
+ `retentionDays` (default **30**, set in `/rewind`) are reclaimed, plus an
53
+ **orphan sweep** of directories whose session id has no session JSONL (crashed
54
+ sessions). The GC is time-boxed and deletion-capped so it never slows startup.
55
+ - The former `pi-config` storage layout is **not** read or migrated by this
56
+ engine. Remove any old `~/.pi/agent/pi-config/` data manually if it is no
57
+ longer needed.
58
+
59
+ ## Limits
60
+
61
+ - Covers edits made through Pi's built-in `edit` and `write` tools only. Files written by `bash` or edited outside Pi are not tracked.
62
+ - A backup failure is swallowed — the edit proceeds without checkpointing rather than blocking the tool.
63
+ - `/rewind` on its own does not restore files. Restoration uses `/tree` — navigate to a turn and confirm the prompt.
64
+ - Restore is coarse: only files that differ are rewritten, but line totals are a bag-of-lines estimate (capped at 1 MB per file), not a full Myers diff.
65
+
66
+ ## Implementation notes
67
+
68
+ - **Never blocks the edit.** Backup runs before the write lands; a backup failure is swallowed so the session never stalls.
69
+ - **Restore safety.** `applySnapshot` only rewrites files that differ and degrades gracefully on unreadable backups.
70
+ - **Change detection.** A file is re-backed-up only when its stat or content differs from the latest backup. Content comparison streams in 64 KiB chunks instead of buffering whole files up to the 25 MB cap.
71
+ - **Config is memory-cached.** `config.json` is re-read on `session_start` and updated in memory when `/rewind` saves.
72
+ - **Restore path is single-scan.** The `/tree` confirm pass caches changed absolute paths; IO is concurrency-capped (16).
73
+
74
+ **Files:**
75
+
76
+ - `index.ts` — lifecycle hooks (`tool_call`, `before_agent_start`, `agent_settled`, `session_start`, `session_before_tree`/`session_tree`, `session_shutdown`) and the `/rewind` command
77
+ - `engine.ts` — the file-history backup engine (track / snapshot / apply / resume-migrate), ported from Claude Code's file-history
78
+ - `snapshot.ts` — persisted snapshot data shapes (pure)
79
+ - `config.ts` — load/save `rewind/config.json`
80
+ - `gc.ts` — age + orphan storage reclamation; storage inventory for the menu
81
+ - `menu.ts` — the `/rewind` settings + storage menu
82
+ - `restore.ts` — `/tree`-target → snapshot matching and restore
83
+ - `paths.ts`, `text.ts`, `tool-path.ts` — private helpers owned by this plugin
84
+
85
+ Architecture informed by oh-my-pi (GPL-3.0) and Claude Code's file-history; independent implementation.
@@ -0,0 +1,139 @@
1
+ # `router` — Codex-style API relays
2
+
3
+ `/router` connects Pi to OpenAI-compatible API relays — self-hosted gateways such as sub2api, CPA, or codex2api, and any similar proxy — using a **Codex-shaped** Responses client.
4
+
5
+ Configuration lives at `~/.pi/agent/router.json`.
6
+
7
+ Providers are registered at extension load via `pi.registerProvider` (config form + `streamSimple`). They do **not** go through `models.json`. This follows Pi's documented custom-provider path ([providers.md](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/providers.md), [custom-provider.md](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/custom-provider.md)): the stream wraps the built-in `openAIResponsesApi()` from `@earendil-works/pi-ai/compat` (same approach as the GitLab Duo example), then reshapes the request for Codex-style relays.
8
+
9
+ ---
10
+
11
+ ## Usage
12
+
13
+ ```
14
+ /router Browse relays
15
+ /router add Add a relay
16
+ /router reload Re-register from disk
17
+ /router <id> Open a relay
18
+ ```
19
+
20
+ ### UI map
21
+
22
+ ```
23
+ API relays ← relays first; add / reload at bottom
24
+ └─ Relay · {id} ← models · base URL · API key · remove
25
+ └─ Models ← Fetch catalog + one row per configured model
26
+ └─ {model id} ← display name · thinking levels
27
+ ```
28
+
29
+ Edits **auto-save** to `router.json` and re-register the provider. There is no separate Save step — Back never discards committed field or model changes.
30
+
31
+ Nested multi-select (catalog) and the thinking-level editor require **Ctrl+S to apply** that screen's working set. If you press Esc with unsaved toggles, the footer warns once; pressing Esc again discards that screen only.
32
+
33
+ ### Add flow
34
+
35
+ 1. **Name** — provider id (e.g. `my-relay`); appears as `my-relay/gpt-5.6-sol` in `/model`
36
+ 2. **Base URL** — usually ends with `/v1`
37
+ 3. **API key** — literal `sk-…` or `$ENV_VAR`
38
+ 4. **Fetch models** — `GET {baseUrl}/models`
39
+ 5. **Select** — Space to toggle, Ctrl+S to apply (TUI); the relay is then written immediately
40
+
41
+ Each selected model gets these defaults:
42
+
43
+ | Field | Default |
44
+ |:-:|:-:|
45
+ | `name` | **Omitted** — `/model` shows the model **id** |
46
+ | `reasoning` | `true` |
47
+ | `input` | `text` + `image` |
48
+ | `contextWindow` | 272000 |
49
+ | `maxTokens` | 128000 |
50
+ | `thinkingLevelMap` | off…medium hidden; high / xhigh / max on |
51
+
52
+ ### Customize models
53
+
54
+ Relay → **Models** → pick a model:
55
+
56
+ - **Display name** — optional label (e.g. `Luna`). Leave empty to show the id. Saved on confirm.
57
+ - **Thinking levels** — toggle each Pi level between **on** and **hidden** (`null`). Ctrl+S applies, then auto-saves the relay.
58
+
59
+ ---
60
+
61
+ ## Config shape
62
+
63
+ ```jsonc
64
+ {
65
+ "version": 1,
66
+ "relays": [
67
+ {
68
+ "id": "my-relay",
69
+ "baseUrl": "https://relay.example/v1",
70
+ "apiKey": "sk-…",
71
+ "models": [
72
+ {
73
+ "id": "gpt-5.6-sol",
74
+ "reasoning": true,
75
+ "input": ["text", "image"],
76
+ "contextWindow": 272000,
77
+ "maxTokens": 128000,
78
+ "thinkingLevelMap": {
79
+ "off": null,
80
+ "minimal": null,
81
+ "low": null,
82
+ "medium": null,
83
+ "high": "high",
84
+ "xhigh": "xhigh",
85
+ "max": "max"
86
+ }
87
+ },
88
+ {
89
+ "id": "gpt-5.6-luna",
90
+ "name": "Luna",
91
+ "reasoning": true,
92
+ "input": ["text", "image"],
93
+ "contextWindow": 272000,
94
+ "maxTokens": 128000
95
+ }
96
+ ]
97
+ }
98
+ ]
99
+ }
100
+ ```
101
+
102
+ There is **no migration** from any older models-manager config. Add relays with `/router add` (or edit `router.json` and run `/router reload`).
103
+
104
+ ---
105
+
106
+ ## Limits
107
+
108
+ - SSE only — no Codex WebSocket or zstd-compressed request body.
109
+ - Catalog probe expects OpenAI-style `{ data: [{ id }] }` response format.
110
+ - Empty model list → provider is not registered (nothing appears in `/model`).
111
+ - Body is Codex-oriented for transparent gateways; auth and URL remain Platform Responses (`sk-` + `/responses`).
112
+ - Same relay + same model: tool/reasoning multi-turn matches Codex-style Responses. Switching model or provider mid-session may normalize tool-call ids more strictly (upstream allow-list); this is not worked around here.
113
+ - Interactive `/router` requires a TUI (`ctx.hasUI`); otherwise a warning is shown and no dialog opens.
114
+ - Catalog multi-select and thinking editor are apply-on-Ctrl+S screens; only those screens can be discarded with double Esc.
115
+
116
+ ## Implementation notes
117
+
118
+ **Request shape.** `stream.ts` calls `openAIResponsesApi().streamSimple` with a model configured as Platform Responses (`api: "openai-responses"`). The `onPayload` callback reshapes the body toward Pi's built-in `openai-codex-responses` format, with `store: false`, system prompt as `instructions`, `parallel_tool_calls: true`, and rejected fields such as `prompt_cache_retention` and `temperature` dropped.
119
+
120
+ Session affinity headers use hyphenated form (`session-id`, `x-client-request-id`). Compat settings: `sessionAffinityFormat: "openai-nosession"`, `supportsLongCacheRetention: false`. Originator header: `codex`.
121
+
122
+ **Differences from ChatGPT Codex OAuth.** Auth uses Bearer `sk-…` (not OAuth JWT), endpoint is `{baseUrl}/responses` (not `/codex/responses`), transport is SSE only (no WebSocket or zstd), and `OpenAI-Beta` is not set. These match **sk- relays**, not the official ChatGPT backend.
123
+
124
+ **Multi-turn tool calls.** On a fixed relay + fixed model, tool-call ids preserve the Responses form and replay like Codex. Pi only rewrites ids across model boundaries. Staying on one model for long tool + reasoning sessions avoids id sanitization.
125
+
126
+ **Files:**
127
+
128
+ | File | Role |
129
+ |:-:|:-:|
130
+ | `index.ts` | Async factory: load config, register providers, handle `/router` commands |
131
+ | `store.ts` | `router.json` read / write |
132
+ | `register.ts` | `registerProvider` / unregister |
133
+ | `stream.ts` | Wraps `openAIResponsesApi` + payload reshape |
134
+ | `probe.ts` | `GET …/models` catalog fetch |
135
+ | `presets.ts` | 272k defaults + thinking-level map helpers |
136
+ | `dialog.ts` | Selectors, multi-select, thinking editor (dirty Esc warning) |
137
+ | `ui.ts` | Command flows; auto-save on relay mutations |
138
+ | `constants.ts` | Command name, defaults, `router-codex` API tag |
139
+ | `types.ts` | Config types |