@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.
- package/CHANGELOG.md +10 -0
- package/LICENSE +21 -0
- package/README.md +14 -3
- package/dist/core/agent-session.d.ts +4 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +193 -43
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +6 -13
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/tools/tool-definition-wrapper.d.ts +7 -2
- package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
- package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +25 -12
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/docs/compaction.md +8 -2
- package/docs/containerization.md +1 -1
- package/docs/development.md +32 -35
- package/docs/distribution/README.md +34 -0
- package/docs/distribution/architecture.md +102 -0
- package/docs/distribution/extensions/README.md +17 -0
- package/docs/distribution/extensions/deepwiki.md +62 -0
- package/docs/distribution/extensions/question.md +59 -0
- package/docs/distribution/extensions/rewind.md +85 -0
- package/docs/distribution/extensions/router.md +139 -0
- package/docs/distribution/extensions/statusline.md +110 -0
- package/docs/distribution/extensions/subagent.md +102 -0
- package/docs/distribution/maintenance.md +121 -0
- package/docs/distribution/release.md +85 -0
- package/docs/distribution/themes.md +58 -0
- package/docs/distribution/tool-presentation.md +91 -0
- package/docs/extensions.md +4 -2
- package/docs/index.md +1 -1
- package/docs/rpc.md +2 -2
- package/docs/themes.md +1 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +3 -3
- package/examples/extensions/custom-provider-gitlab-duo/test.ts +1 -1
- package/examples/extensions/doom-overlay/doom/build/doom.wasm +0 -0
- package/examples/extensions/doom-overlay/doom/build.sh +0 -0
- package/examples/extensions/sandbox/package-lock.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/npm-shrinkwrap.json +3465 -624
- package/package.json +30 -18
package/docs/compaction.md
CHANGED
|
@@ -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**:
|
|
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
|
|
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:
|
package/docs/containerization.md
CHANGED
|
@@ -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
|
|
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
|
```
|
package/docs/development.md
CHANGED
|
@@ -1,71 +1,68 @@
|
|
|
1
1
|
# Development
|
|
2
2
|
|
|
3
|
-
See [AGENTS.md](
|
|
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/
|
|
9
|
-
cd pi
|
|
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
|
-
|
|
20
|
+
./pi-test.sh
|
|
18
21
|
```
|
|
19
22
|
|
|
20
|
-
The script can be
|
|
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
|
-
##
|
|
25
|
+
## Package identity
|
|
23
26
|
|
|
24
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
57
|
+
Release CI runs the full suite on Ubuntu. Interactive UI changes should also be verified in a real terminal.
|
|
64
58
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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 |
|