@mariozechner/pi-coding-agent 0.30.1 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +251 -2
- package/README.md +105 -84
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +5 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/file-processor.d.ts +3 -3
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js +7 -10
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/config.d.ts +9 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +18 -0
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +73 -34
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +464 -210
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts +2 -7
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +4 -52
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/bash-executor.d.ts +2 -2
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +2 -2
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts +84 -0
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -0
- package/dist/core/compaction/branch-summarization.js +233 -0
- package/dist/core/compaction/branch-summarization.js.map +1 -0
- package/dist/core/{compaction.d.ts → compaction/compaction.d.ts} +38 -19
- package/dist/core/compaction/compaction.d.ts.map +1 -0
- package/dist/core/compaction/compaction.js +558 -0
- package/dist/core/compaction/compaction.js.map +1 -0
- package/dist/core/compaction/index.d.ts +7 -0
- package/dist/core/compaction/index.d.ts.map +1 -0
- package/dist/core/compaction/index.js +7 -0
- package/dist/core/compaction/index.js.map +1 -0
- package/dist/core/compaction/utils.d.ts +35 -0
- package/dist/core/compaction/utils.d.ts.map +1 -0
- package/dist/core/compaction/utils.js +138 -0
- package/dist/core/compaction/utils.js.map +1 -0
- package/dist/core/custom-tools/index.d.ts +2 -1
- package/dist/core/custom-tools/index.d.ts.map +1 -1
- package/dist/core/custom-tools/index.js +1 -0
- package/dist/core/custom-tools/index.js.map +1 -1
- package/dist/core/custom-tools/loader.d.ts.map +1 -1
- package/dist/core/custom-tools/loader.js +13 -80
- package/dist/core/custom-tools/loader.js.map +1 -1
- package/dist/core/custom-tools/types.d.ts +84 -59
- package/dist/core/custom-tools/types.d.ts.map +1 -1
- package/dist/core/custom-tools/types.js.map +1 -1
- package/dist/core/custom-tools/wrapper.d.ts +15 -0
- package/dist/core/custom-tools/wrapper.d.ts.map +1 -0
- package/dist/core/custom-tools/wrapper.js +23 -0
- package/dist/core/custom-tools/wrapper.js.map +1 -0
- package/dist/core/exec.d.ts +29 -0
- package/dist/core/exec.d.ts.map +1 -0
- package/dist/core/exec.js +71 -0
- package/dist/core/exec.js.map +1 -0
- package/dist/core/export-html/index.d.ts +17 -0
- package/dist/core/export-html/index.d.ts.map +1 -0
- package/dist/core/export-html/index.js +171 -0
- package/dist/core/export-html/index.js.map +1 -0
- package/dist/core/export-html/template.css +781 -0
- package/dist/core/export-html/template.html +54 -0
- package/dist/core/export-html/template.js +1185 -0
- package/dist/core/export-html/vendor/highlight.min.js +1213 -0
- package/dist/core/export-html/vendor/marked.min.js +6 -0
- package/dist/core/hooks/index.d.ts +4 -4
- package/dist/core/hooks/index.d.ts.map +1 -1
- package/dist/core/hooks/index.js +3 -3
- package/dist/core/hooks/index.js.map +1 -1
- package/dist/core/hooks/loader.d.ts +40 -5
- package/dist/core/hooks/loader.d.ts.map +1 -1
- package/dist/core/hooks/loader.js +43 -10
- package/dist/core/hooks/loader.js.map +1 -1
- package/dist/core/hooks/runner.d.ts +94 -18
- package/dist/core/hooks/runner.d.ts.map +1 -1
- package/dist/core/hooks/runner.js +199 -120
- package/dist/core/hooks/runner.js.map +1 -1
- package/dist/core/hooks/tool-wrapper.d.ts +1 -1
- package/dist/core/hooks/tool-wrapper.d.ts.map +1 -1
- package/dist/core/hooks/tool-wrapper.js +36 -19
- package/dist/core/hooks/tool-wrapper.js.map +1 -1
- package/dist/core/hooks/types.d.ts +407 -96
- package/dist/core/hooks/types.d.ts.map +1 -1
- package/dist/core/hooks/types.js.map +1 -1
- package/dist/core/index.d.ts +4 -3
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/messages.d.ts +44 -12
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js +82 -34
- package/dist/core/messages.js.map +1 -1
- package/dist/core/model-registry.d.ts +5 -5
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +7 -7
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts +7 -7
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +45 -14
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/sdk.d.ts +7 -10
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +88 -32
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +202 -36
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +565 -133
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +9 -3
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +13 -12
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +6 -3
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/bash.d.ts +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit-diff.d.ts +33 -0
- package/dist/core/tools/edit-diff.d.ts.map +1 -0
- package/dist/core/tools/edit-diff.js +171 -0
- package/dist/core/tools/edit-diff.js.map +1 -0
- package/dist/core/tools/edit.d.ts +7 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +20 -95
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/find.d.ts +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts +1 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/index.d.ts +1 -1
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/ls.d.ts +1 -1
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/read.d.ts +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/write.d.ts +1 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js.map +1 -1
- package/dist/index.d.ts +8 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +25 -25
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts +28 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/migrations.js +125 -0
- package/dist/migrations.js.map +1 -0
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +3 -4
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js +6 -2
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.d.ts +12 -0
- package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -0
- package/dist/modes/interactive/components/bordered-loader.js +30 -0
- package/dist/modes/interactive/components/bordered-loader.js.map +1 -0
- package/dist/modes/interactive/components/branch-summary-message.d.ts +14 -0
- package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/branch-summary-message.js +35 -0
- package/dist/modes/interactive/components/branch-summary-message.js.map +1 -0
- package/dist/modes/interactive/components/compaction-summary-message.d.ts +14 -0
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/compaction-summary-message.js +36 -0
- package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -0
- package/dist/modes/interactive/components/dynamic-border.d.ts +5 -1
- package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
- package/dist/modes/interactive/components/dynamic-border.js +5 -1
- package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts +12 -6
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +57 -25
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/hook-editor.d.ts +15 -0
- package/dist/modes/interactive/components/hook-editor.d.ts.map +1 -0
- package/dist/modes/interactive/components/hook-editor.js +95 -0
- package/dist/modes/interactive/components/hook-editor.js.map +1 -0
- package/dist/modes/interactive/components/hook-message.d.ts +18 -0
- package/dist/modes/interactive/components/hook-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/hook-message.js +80 -0
- package/dist/modes/interactive/components/hook-message.js.map +1 -0
- package/dist/modes/interactive/components/model-selector.d.ts +3 -3
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +1 -1
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +15 -2
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +70 -21
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts +52 -0
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/tree-selector.js +745 -0
- package/dist/modes/interactive/components/tree-selector.js.map +1 -0
- package/dist/modes/interactive/components/user-message-selector.d.ts +3 -3
- package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message-selector.js +1 -1
- package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message.d.ts +1 -1
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message.js +2 -5
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +29 -12
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +589 -208
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/dark.json +13 -1
- package/dist/modes/interactive/theme/light.json +13 -1
- package/dist/modes/interactive/theme/theme-schema.json +34 -0
- package/dist/modes/interactive/theme/theme.d.ts +20 -2
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +135 -2
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/print-mode.d.ts +3 -3
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +26 -20
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +13 -10
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +11 -10
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +88 -35
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +30 -11
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/utils/shell.d.ts +4 -2
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +36 -7
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/tools-manager.d.ts +1 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +2 -2
- package/dist/utils/tools-manager.js.map +1 -1
- package/docs/compaction.md +388 -0
- package/docs/custom-tools.md +146 -43
- package/docs/extension-loading.md +1004 -0
- package/docs/hooks.md +562 -596
- package/docs/rpc.md +33 -19
- package/docs/sdk.md +93 -21
- package/docs/session-tree-plan.md +441 -0
- package/docs/session.md +172 -21
- package/docs/skills.md +2 -0
- package/docs/theme.md +31 -2
- package/docs/tree.md +197 -0
- package/docs/tui.md +343 -0
- package/examples/README.md +1 -9
- package/examples/custom-tools/hello/index.ts +4 -3
- package/examples/custom-tools/question/index.ts +4 -4
- package/examples/custom-tools/subagent/index.ts +7 -6
- package/examples/custom-tools/todo/index.ts +11 -5
- package/examples/hooks/README.md +29 -71
- package/examples/hooks/auto-commit-on-exit.ts +8 -9
- package/examples/hooks/confirm-destructive.ts +29 -30
- package/examples/hooks/custom-compaction.ts +20 -21
- package/examples/hooks/dirty-repo-guard.ts +41 -40
- package/examples/hooks/file-trigger.ts +10 -5
- package/examples/hooks/git-checkpoint.ts +16 -12
- package/examples/hooks/handoff.ts +150 -0
- package/examples/hooks/permission-gate.ts +1 -1
- package/examples/hooks/protected-paths.ts +1 -1
- package/examples/hooks/qna.ts +119 -0
- package/examples/hooks/snake.ts +343 -0
- package/examples/hooks/status-line.ts +40 -0
- package/examples/sdk/01-minimal.ts +1 -1
- package/examples/sdk/02-custom-model.ts +1 -1
- package/examples/sdk/03-custom-prompt.ts +1 -1
- package/examples/sdk/04-skills.ts +1 -1
- package/examples/sdk/05-tools.ts +4 -4
- package/examples/sdk/06-hooks.ts +1 -1
- package/examples/sdk/07-context-files.ts +1 -1
- package/examples/sdk/08-slash-commands.ts +6 -1
- package/examples/sdk/09-api-keys-and-oauth.ts +1 -1
- package/examples/sdk/10-settings.ts +1 -1
- package/examples/sdk/11-sessions.ts +1 -1
- package/examples/sdk/12-full-control.ts +4 -7
- package/package.json +6 -6
- package/dist/core/compaction.d.ts.map +0 -1
- package/dist/core/compaction.js +0 -412
- package/dist/core/compaction.js.map +0 -1
- package/dist/core/export-html.d.ts +0 -23
- package/dist/core/export-html.d.ts.map +0 -1
- package/dist/core/export-html.js +0 -1185
- package/dist/core/export-html.js.map +0 -1
- package/dist/modes/interactive/components/compaction.d.ts +0 -15
- package/dist/modes/interactive/components/compaction.d.ts.map +0 -1
- package/dist/modes/interactive/components/compaction.js +0 -41
- package/dist/modes/interactive/components/compaction.js.map +0 -1
- package/docs/hooks-v2.md +0 -385
- package/docs/session-tree.md +0 -452
package/docs/session.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Session File Format
|
|
2
2
|
|
|
3
|
-
Sessions are stored as JSONL (JSON Lines) files. Each line is a JSON object with a `type` field.
|
|
3
|
+
Sessions are stored as JSONL (JSON Lines) files. Each line is a JSON object with a `type` field. Session entries form a tree structure via `id`/`parentId` fields, enabling in-place branching without creating new files.
|
|
4
4
|
|
|
5
5
|
## File Location
|
|
6
6
|
|
|
@@ -10,47 +10,66 @@ Sessions are stored as JSONL (JSON Lines) files. Each line is a JSON object with
|
|
|
10
10
|
|
|
11
11
|
Where `<path>` is the working directory with `/` replaced by `-`.
|
|
12
12
|
|
|
13
|
+
## Session Version
|
|
14
|
+
|
|
15
|
+
Sessions have a version field in the header:
|
|
16
|
+
|
|
17
|
+
- **Version 1**: Linear entry sequence (legacy, auto-migrated on load)
|
|
18
|
+
- **Version 2**: Tree structure with `id`/`parentId` linking
|
|
19
|
+
|
|
20
|
+
Existing v1 sessions are automatically migrated to v2 when loaded.
|
|
21
|
+
|
|
13
22
|
## Type Definitions
|
|
14
23
|
|
|
15
|
-
- [`src/session-manager.ts`](../src/session-manager.ts) - Session entry types
|
|
16
|
-
- [`packages/agent/src/types.ts`](../../agent/src/types.ts) - `
|
|
24
|
+
- [`src/core/session-manager.ts`](../src/core/session-manager.ts) - Session entry types
|
|
25
|
+
- [`packages/agent/src/types.ts`](../../agent/src/types.ts) - `AgentMessage`, `Attachment`, `ThinkingLevel`
|
|
17
26
|
- [`packages/ai/src/types.ts`](../../ai/src/types.ts) - `UserMessage`, `AssistantMessage`, `ToolResultMessage`, `Usage`, `ToolCall`
|
|
18
27
|
|
|
28
|
+
## Entry Base
|
|
29
|
+
|
|
30
|
+
All entries (except `SessionHeader`) extend `SessionEntryBase`:
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
interface SessionEntryBase {
|
|
34
|
+
type: string;
|
|
35
|
+
id: string; // 8-char hex ID
|
|
36
|
+
parentId: string | null; // Parent entry ID (null for first entry)
|
|
37
|
+
timestamp: string; // ISO timestamp
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
19
41
|
## Entry Types
|
|
20
42
|
|
|
21
43
|
### SessionHeader
|
|
22
44
|
|
|
23
|
-
First line of the file.
|
|
45
|
+
First line of the file. Metadata only, not part of the tree (no `id`/`parentId`).
|
|
24
46
|
|
|
25
47
|
```json
|
|
26
|
-
{"type":"session","id":"uuid","timestamp":"2024-12-03T14:00:00.000Z","cwd":"/path/to/project"
|
|
48
|
+
{"type":"session","version":2,"id":"uuid","timestamp":"2024-12-03T14:00:00.000Z","cwd":"/path/to/project"}
|
|
27
49
|
```
|
|
28
50
|
|
|
29
|
-
For
|
|
51
|
+
For sessions with a parent (created via `/branch` or `newSession({ parentSession })`):
|
|
30
52
|
|
|
31
53
|
```json
|
|
32
|
-
{"type":"session","id":"uuid","timestamp":"2024-12-03T14:00:00.000Z","cwd":"/path/to/project","
|
|
54
|
+
{"type":"session","version":2,"id":"uuid","timestamp":"2024-12-03T14:00:00.000Z","cwd":"/path/to/project","parentSession":"/path/to/original/session.jsonl"}
|
|
33
55
|
```
|
|
34
56
|
|
|
35
57
|
### SessionMessageEntry
|
|
36
58
|
|
|
37
|
-
A message in the conversation. The `message` field contains an `
|
|
59
|
+
A message in the conversation. The `message` field contains an `AgentMessage`.
|
|
38
60
|
|
|
39
61
|
```json
|
|
40
|
-
{"type":"message","timestamp":"2024-12-03T14:00:01.000Z","message":{"role":"user","content":"Hello"
|
|
41
|
-
{"type":"message","timestamp":"2024-12-03T14:00:02.000Z","message":{"role":"assistant","content":[{"type":"text","text":"Hi!"}],"
|
|
42
|
-
{"type":"message","timestamp":"2024-12-03T14:00:03.000Z","message":{"role":"toolResult","toolCallId":"call_123","toolName":"bash","content":[{"type":"text","text":"output"}],"isError":false
|
|
43
|
-
{"type":"message","timestamp":"2024-12-03T14:00:04.000Z","message":{"role":"bashExecution","command":"ls -la","output":"total 48\n...","exitCode":0,"cancelled":false,"truncated":false,"timestamp":1733234567950}}
|
|
62
|
+
{"type":"message","id":"a1b2c3d4","parentId":"prev1234","timestamp":"2024-12-03T14:00:01.000Z","message":{"role":"user","content":"Hello"}}
|
|
63
|
+
{"type":"message","id":"b2c3d4e5","parentId":"a1b2c3d4","timestamp":"2024-12-03T14:00:02.000Z","message":{"role":"assistant","content":[{"type":"text","text":"Hi!"}],"provider":"anthropic","model":"claude-sonnet-4-5","usage":{...},"stopReason":"stop"}}
|
|
64
|
+
{"type":"message","id":"c3d4e5f6","parentId":"b2c3d4e5","timestamp":"2024-12-03T14:00:03.000Z","message":{"role":"toolResult","toolCallId":"call_123","toolName":"bash","content":[{"type":"text","text":"output"}],"isError":false}}
|
|
44
65
|
```
|
|
45
66
|
|
|
46
|
-
The `bashExecution` role is a custom message type for user-executed bash commands (via `!` in TUI or `bash` RPC command). See [rpc.md](./rpc.md#bashexecutionmessage) for the full schema.
|
|
47
|
-
|
|
48
67
|
### ModelChangeEntry
|
|
49
68
|
|
|
50
69
|
Emitted when the user switches models mid-session.
|
|
51
70
|
|
|
52
71
|
```json
|
|
53
|
-
{"type":"model_change","timestamp":"2024-12-03T14:05:00.000Z","provider":"openai","modelId":"gpt-4o"}
|
|
72
|
+
{"type":"model_change","id":"d4e5f6g7","parentId":"c3d4e5f6","timestamp":"2024-12-03T14:05:00.000Z","provider":"openai","modelId":"gpt-4o"}
|
|
54
73
|
```
|
|
55
74
|
|
|
56
75
|
### ThinkingLevelChangeEntry
|
|
@@ -58,9 +77,92 @@ Emitted when the user switches models mid-session.
|
|
|
58
77
|
Emitted when the user changes the thinking/reasoning level.
|
|
59
78
|
|
|
60
79
|
```json
|
|
61
|
-
{"type":"thinking_level_change","timestamp":"2024-12-03T14:06:00.000Z","thinkingLevel":"high"}
|
|
80
|
+
{"type":"thinking_level_change","id":"e5f6g7h8","parentId":"d4e5f6g7","timestamp":"2024-12-03T14:06:00.000Z","thinkingLevel":"high"}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### CompactionEntry
|
|
84
|
+
|
|
85
|
+
Created when context is compacted. Stores a summary of earlier messages.
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{"type":"compaction","id":"f6g7h8i9","parentId":"e5f6g7h8","timestamp":"2024-12-03T14:10:00.000Z","summary":"User discussed X, Y, Z...","firstKeptEntryId":"c3d4e5f6","tokensBefore":50000}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Optional fields:
|
|
92
|
+
- `details`: Compaction-implementation specific data (e.g., file operations for default implementation, or custom data for custom hook implementations)
|
|
93
|
+
- `fromHook`: `true` if generated by a hook, `false`/`undefined` if pi-generated
|
|
94
|
+
|
|
95
|
+
### BranchSummaryEntry
|
|
96
|
+
|
|
97
|
+
Created when switching branches via `/tree` with an LLM generated summary of the left branch up to the common ancestor. Captures context from the abandoned path.
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
{"type":"branch_summary","id":"g7h8i9j0","parentId":"a1b2c3d4","timestamp":"2024-12-03T14:15:00.000Z","fromId":"f6g7h8i9","summary":"Branch explored approach A..."}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Optional fields:
|
|
104
|
+
- `details`: File tracking data (`{ readFiles: string[], modifiedFiles: string[] }`) for default implementation, arbitrary for custom implementation
|
|
105
|
+
- `fromHook`: `true` if generated by a hook
|
|
106
|
+
|
|
107
|
+
### CustomEntry
|
|
108
|
+
|
|
109
|
+
Hook state persistence. Does NOT participate in LLM context.
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
{"type":"custom","id":"h8i9j0k1","parentId":"g7h8i9j0","timestamp":"2024-12-03T14:20:00.000Z","customType":"my-hook","data":{"count":42}}
|
|
62
113
|
```
|
|
63
114
|
|
|
115
|
+
Use `customType` to identify your hook's entries on reload.
|
|
116
|
+
|
|
117
|
+
### CustomMessageEntry
|
|
118
|
+
|
|
119
|
+
Hook-injected messages that DO participate in LLM context.
|
|
120
|
+
|
|
121
|
+
```json
|
|
122
|
+
{"type":"custom_message","id":"i9j0k1l2","parentId":"h8i9j0k1","timestamp":"2024-12-03T14:25:00.000Z","customType":"my-hook","content":"Injected context...","display":true}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Fields:
|
|
126
|
+
- `content`: String or `(TextContent | ImageContent)[]` (same as UserMessage)
|
|
127
|
+
- `display`: `true` = show in TUI with purple styling, `false` = hidden
|
|
128
|
+
- `details`: Optional hook-specific metadata (not sent to LLM)
|
|
129
|
+
|
|
130
|
+
### LabelEntry
|
|
131
|
+
|
|
132
|
+
User-defined bookmark/marker on an entry.
|
|
133
|
+
|
|
134
|
+
```json
|
|
135
|
+
{"type":"label","id":"j0k1l2m3","parentId":"i9j0k1l2","timestamp":"2024-12-03T14:30:00.000Z","targetId":"a1b2c3d4","label":"checkpoint-1"}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Set `label` to `undefined` to clear a label.
|
|
139
|
+
|
|
140
|
+
## Tree Structure
|
|
141
|
+
|
|
142
|
+
Entries form a tree:
|
|
143
|
+
- First entry has `parentId: null`
|
|
144
|
+
- Each subsequent entry points to its parent via `parentId`
|
|
145
|
+
- Branching creates new children from an earlier entry
|
|
146
|
+
- The "leaf" is the current position in the tree
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
[user msg] ─── [assistant] ─── [user msg] ─── [assistant] ─┬─ [user msg] ← current leaf
|
|
150
|
+
│
|
|
151
|
+
└─ [branch_summary] ─── [user msg] ← alternate branch
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Context Building
|
|
155
|
+
|
|
156
|
+
`buildSessionContext()` walks from the current leaf to the root, producing the message list for the LLM:
|
|
157
|
+
|
|
158
|
+
1. Collects all entries on the path
|
|
159
|
+
2. Extracts current model and thinking level settings
|
|
160
|
+
3. If a `CompactionEntry` is on the path:
|
|
161
|
+
- Emits the summary first
|
|
162
|
+
- Then messages from `firstKeptEntryId` to compaction
|
|
163
|
+
- Then messages after compaction
|
|
164
|
+
4. Converts `BranchSummaryEntry` and `CustomMessageEntry` to appropriate message formats
|
|
165
|
+
|
|
64
166
|
## Parsing Example
|
|
65
167
|
|
|
66
168
|
```typescript
|
|
@@ -70,20 +172,69 @@ const lines = readFileSync("session.jsonl", "utf8").trim().split("\n");
|
|
|
70
172
|
|
|
71
173
|
for (const line of lines) {
|
|
72
174
|
const entry = JSON.parse(line);
|
|
73
|
-
|
|
175
|
+
|
|
74
176
|
switch (entry.type) {
|
|
75
177
|
case "session":
|
|
76
|
-
console.log(`Session
|
|
178
|
+
console.log(`Session v${entry.version ?? 1}: ${entry.id}`);
|
|
77
179
|
break;
|
|
78
180
|
case "message":
|
|
79
|
-
console.log(
|
|
181
|
+
console.log(`[${entry.id}] ${entry.message.role}: ${JSON.stringify(entry.message.content)}`);
|
|
182
|
+
break;
|
|
183
|
+
case "compaction":
|
|
184
|
+
console.log(`[${entry.id}] Compaction: ${entry.tokensBefore} tokens summarized`);
|
|
185
|
+
break;
|
|
186
|
+
case "branch_summary":
|
|
187
|
+
console.log(`[${entry.id}] Branch from ${entry.fromId}`);
|
|
188
|
+
break;
|
|
189
|
+
case "custom":
|
|
190
|
+
console.log(`[${entry.id}] Custom (${entry.customType}): ${JSON.stringify(entry.data)}`);
|
|
191
|
+
break;
|
|
192
|
+
case "custom_message":
|
|
193
|
+
console.log(`[${entry.id}] Hook message (${entry.customType}): ${entry.content}`);
|
|
194
|
+
break;
|
|
195
|
+
case "label":
|
|
196
|
+
console.log(`[${entry.id}] Label "${entry.label}" on ${entry.targetId}`);
|
|
80
197
|
break;
|
|
81
198
|
case "model_change":
|
|
82
|
-
console.log(`
|
|
199
|
+
console.log(`[${entry.id}] Model: ${entry.provider}/${entry.modelId}`);
|
|
83
200
|
break;
|
|
84
201
|
case "thinking_level_change":
|
|
85
|
-
console.log(`Thinking: ${entry.thinkingLevel}`);
|
|
202
|
+
console.log(`[${entry.id}] Thinking: ${entry.thinkingLevel}`);
|
|
86
203
|
break;
|
|
87
204
|
}
|
|
88
205
|
}
|
|
89
206
|
```
|
|
207
|
+
|
|
208
|
+
## SessionManager API
|
|
209
|
+
|
|
210
|
+
Key methods for working with sessions programmatically:
|
|
211
|
+
|
|
212
|
+
### Creation
|
|
213
|
+
- `SessionManager.create(cwd, sessionDir?)` - New session
|
|
214
|
+
- `SessionManager.open(path, sessionDir?)` - Open existing
|
|
215
|
+
- `SessionManager.continueRecent(cwd, sessionDir?)` - Continue most recent or create new
|
|
216
|
+
- `SessionManager.inMemory(cwd?)` - No file persistence
|
|
217
|
+
|
|
218
|
+
### Appending (all return entry ID)
|
|
219
|
+
- `appendMessage(message)` - Add message
|
|
220
|
+
- `appendThinkingLevelChange(level)` - Record thinking change
|
|
221
|
+
- `appendModelChange(provider, modelId)` - Record model change
|
|
222
|
+
- `appendCompaction(summary, firstKeptEntryId, tokensBefore, details?, fromHook?)` - Add compaction
|
|
223
|
+
- `appendCustomEntry(customType, data?)` - Hook state (not in context)
|
|
224
|
+
- `appendCustomMessageEntry(customType, content, display, details?)` - Hook message (in context)
|
|
225
|
+
- `appendLabelChange(targetId, label)` - Set/clear label
|
|
226
|
+
|
|
227
|
+
### Tree Navigation
|
|
228
|
+
- `getLeafId()` - Current position
|
|
229
|
+
- `getEntry(id)` - Get entry by ID
|
|
230
|
+
- `getPath(fromId?)` - Walk from entry to root
|
|
231
|
+
- `getTree()` - Get full tree structure
|
|
232
|
+
- `getChildren(parentId)` - Get direct children
|
|
233
|
+
- `getLabel(id)` - Get label for entry
|
|
234
|
+
- `branch(entryId)` - Move leaf to earlier entry
|
|
235
|
+
- `branchWithSummary(entryId, summary, details?, fromHook?)` - Branch with context summary
|
|
236
|
+
|
|
237
|
+
### Context
|
|
238
|
+
- `buildSessionContext()` - Get messages for LLM
|
|
239
|
+
- `getEntries()` - All entries (excluding header)
|
|
240
|
+
- `getHeader()` - Session metadata
|
package/docs/skills.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
> pi can create skills. Ask it to build one for your use case.
|
|
2
|
+
|
|
1
3
|
# Skills
|
|
2
4
|
|
|
3
5
|
Skills are self-contained capability packages that the agent loads on-demand. A skill provides specialized workflows, setup instructions, helper scripts, and reference documentation for specific tasks.
|
package/docs/theme.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
> pi can create themes. Ask it to build one for your use case.
|
|
2
|
+
|
|
1
3
|
# Pi Coding Agent Themes
|
|
2
4
|
|
|
3
5
|
Themes allow you to customize the colors used throughout the coding agent TUI.
|
|
@@ -20,13 +22,18 @@ Every theme must define all color tokens. There are no optional colors.
|
|
|
20
22
|
| `muted` | Secondary/dimmed text | Metadata, descriptions, output |
|
|
21
23
|
| `dim` | Very dimmed text | Less important info, placeholders |
|
|
22
24
|
| `text` | Default text color | Main content (usually `""`) |
|
|
25
|
+
| `thinkingText` | Thinking block text | Assistant reasoning traces |
|
|
23
26
|
|
|
24
|
-
### Backgrounds & Content Text (
|
|
27
|
+
### Backgrounds & Content Text (11 colors)
|
|
25
28
|
|
|
26
29
|
| Token | Purpose |
|
|
27
30
|
|-------|---------|
|
|
31
|
+
| `selectedBg` | Selected/active line background (e.g., tree selector) |
|
|
28
32
|
| `userMessageBg` | User message background |
|
|
29
33
|
| `userMessageText` | User message text color |
|
|
34
|
+
| `customMessageBg` | Hook custom message background |
|
|
35
|
+
| `customMessageText` | Hook custom message text color |
|
|
36
|
+
| `customMessageLabel` | Hook custom message label/type text |
|
|
30
37
|
| `toolPendingBg` | Tool execution box (pending state) |
|
|
31
38
|
| `toolSuccessBg` | Tool execution box (success state) |
|
|
32
39
|
| `toolErrorBg` | Tool execution box (error state) |
|
|
@@ -95,7 +102,28 @@ These create a visual hierarchy: off → minimal → low → medium → high →
|
|
|
95
102
|
|-------|---------|
|
|
96
103
|
| `bashMode` | Editor border color when in bash mode (! prefix) |
|
|
97
104
|
|
|
98
|
-
**Total:
|
|
105
|
+
**Total: 50 color tokens** (all required)
|
|
106
|
+
|
|
107
|
+
### HTML Export Colors (optional)
|
|
108
|
+
|
|
109
|
+
The `export` section is optional and controls colors used when exporting sessions to HTML via `/export`. If not specified, these colors are automatically derived from `userMessageBg` based on luminance detection.
|
|
110
|
+
|
|
111
|
+
| Token | Purpose |
|
|
112
|
+
|-------|---------|
|
|
113
|
+
| `pageBg` | Page background color |
|
|
114
|
+
| `cardBg` | Card/container background (headers, stats boxes) |
|
|
115
|
+
| `infoBg` | Info sections background (system prompt, notices, compaction) |
|
|
116
|
+
|
|
117
|
+
Example:
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"export": {
|
|
121
|
+
"pageBg": "#18181e",
|
|
122
|
+
"cardBg": "#1e1e24",
|
|
123
|
+
"infoBg": "#3c3728"
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
99
127
|
|
|
100
128
|
## Theme Format
|
|
101
129
|
|
|
@@ -113,6 +141,7 @@ Themes are defined in JSON files with the following structure:
|
|
|
113
141
|
"colors": {
|
|
114
142
|
"accent": "blue",
|
|
115
143
|
"muted": "gray",
|
|
144
|
+
"thinkingText": "gray",
|
|
116
145
|
"text": "",
|
|
117
146
|
...
|
|
118
147
|
}
|
package/docs/tree.md
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# Session Tree Navigation
|
|
2
|
+
|
|
3
|
+
The `/tree` command provides tree-based navigation of the session history.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Sessions are stored as trees where each entry has an `id` and `parentId`. The "leaf" pointer tracks the current position. `/tree` lets you navigate to any point and optionally summarize the branch you're leaving.
|
|
8
|
+
|
|
9
|
+
### Comparison with `/branch`
|
|
10
|
+
|
|
11
|
+
| Feature | `/branch` | `/tree` |
|
|
12
|
+
|---------|-----------|---------|
|
|
13
|
+
| View | Flat list of user messages | Full tree structure |
|
|
14
|
+
| Action | Extracts path to **new session file** | Changes leaf in **same session** |
|
|
15
|
+
| Summary | Never | Optional (user prompted) |
|
|
16
|
+
| Events | `session_before_branch` / `session_branch` | `session_before_tree` / `session_tree` |
|
|
17
|
+
|
|
18
|
+
## Tree UI
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
├─ user: "Hello, can you help..."
|
|
22
|
+
│ └─ assistant: "Of course! I can..."
|
|
23
|
+
│ ├─ user: "Let's try approach A..."
|
|
24
|
+
│ │ └─ assistant: "For approach A..."
|
|
25
|
+
│ │ └─ [compaction: 12k tokens]
|
|
26
|
+
│ │ └─ user: "That worked..." ← active
|
|
27
|
+
│ └─ user: "Actually, approach B..."
|
|
28
|
+
│ └─ assistant: "For approach B..."
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Controls
|
|
32
|
+
|
|
33
|
+
| Key | Action |
|
|
34
|
+
|-----|--------|
|
|
35
|
+
| ↑/↓ | Navigate (depth-first order) |
|
|
36
|
+
| Enter | Select node |
|
|
37
|
+
| Escape/Ctrl+C | Cancel |
|
|
38
|
+
| Ctrl+U | Toggle: user messages only |
|
|
39
|
+
| Ctrl+O | Toggle: show all (including custom/label entries) |
|
|
40
|
+
|
|
41
|
+
### Display
|
|
42
|
+
|
|
43
|
+
- Height: half terminal height
|
|
44
|
+
- Current leaf marked with `← active`
|
|
45
|
+
- Labels shown inline: `[label-name]`
|
|
46
|
+
- Default filter hides `label` and `custom` entries (shown in Ctrl+O mode)
|
|
47
|
+
- Children sorted by timestamp (oldest first)
|
|
48
|
+
|
|
49
|
+
## Selection Behavior
|
|
50
|
+
|
|
51
|
+
### User Message or Custom Message
|
|
52
|
+
1. Leaf set to **parent** of selected node (or `null` if root)
|
|
53
|
+
2. Message text placed in **editor** for re-submission
|
|
54
|
+
3. User edits and submits, creating a new branch
|
|
55
|
+
|
|
56
|
+
### Non-User Message (assistant, compaction, etc.)
|
|
57
|
+
1. Leaf set to **selected node**
|
|
58
|
+
2. Editor stays empty
|
|
59
|
+
3. User continues from that point
|
|
60
|
+
|
|
61
|
+
### Selecting Root User Message
|
|
62
|
+
If user selects the very first message (has no parent):
|
|
63
|
+
1. Leaf reset to `null` (empty conversation)
|
|
64
|
+
2. Message text placed in editor
|
|
65
|
+
3. User effectively restarts from scratch
|
|
66
|
+
|
|
67
|
+
## Branch Summarization
|
|
68
|
+
|
|
69
|
+
When switching, user is prompted: "Summarize the branch you're leaving?"
|
|
70
|
+
|
|
71
|
+
### What Gets Summarized
|
|
72
|
+
|
|
73
|
+
Path from old leaf back to common ancestor with target:
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
A → B → C → D → E → F ← old leaf
|
|
77
|
+
↘ G → H ← target
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Abandoned path: D → E → F (summarized)
|
|
81
|
+
|
|
82
|
+
Summarization stops at:
|
|
83
|
+
1. Common ancestor (always)
|
|
84
|
+
2. Compaction node (if encountered first)
|
|
85
|
+
|
|
86
|
+
### Summary Storage
|
|
87
|
+
|
|
88
|
+
Stored as `BranchSummaryEntry`:
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
interface BranchSummaryEntry {
|
|
92
|
+
type: "branch_summary";
|
|
93
|
+
id: string;
|
|
94
|
+
parentId: string; // New leaf position
|
|
95
|
+
timestamp: string;
|
|
96
|
+
fromId: string; // Old leaf we abandoned
|
|
97
|
+
summary: string; // LLM-generated summary
|
|
98
|
+
details?: unknown; // Optional hook data
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Implementation
|
|
103
|
+
|
|
104
|
+
### AgentSession.navigateTree()
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
async navigateTree(
|
|
108
|
+
targetId: string,
|
|
109
|
+
options?: { summarize?: boolean; customInstructions?: string }
|
|
110
|
+
): Promise<{ editorText?: string; cancelled: boolean }>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Flow:
|
|
114
|
+
1. Validate target, check no-op (target === current leaf)
|
|
115
|
+
2. Find common ancestor between old leaf and target
|
|
116
|
+
3. Collect entries to summarize (if requested)
|
|
117
|
+
4. Fire `session_before_tree` event (hook can cancel or provide summary)
|
|
118
|
+
5. Run default summarizer if needed
|
|
119
|
+
6. Switch leaf via `branch()` or `branchWithSummary()`
|
|
120
|
+
7. Update agent: `agent.replaceMessages(sessionManager.buildSessionContext().messages)`
|
|
121
|
+
8. Fire `session_tree` event
|
|
122
|
+
9. Notify custom tools via session event
|
|
123
|
+
10. Return result with `editorText` if user message was selected
|
|
124
|
+
|
|
125
|
+
### SessionManager
|
|
126
|
+
|
|
127
|
+
- `getLeafUuid(): string | null` - Current leaf (null if empty)
|
|
128
|
+
- `resetLeaf(): void` - Set leaf to null (for root user message navigation)
|
|
129
|
+
- `getTree(): SessionTreeNode[]` - Full tree with children sorted by timestamp
|
|
130
|
+
- `branch(id)` - Change leaf pointer
|
|
131
|
+
- `branchWithSummary(id, summary)` - Change leaf and create summary entry
|
|
132
|
+
|
|
133
|
+
### InteractiveMode
|
|
134
|
+
|
|
135
|
+
`/tree` command shows `TreeSelectorComponent`, then:
|
|
136
|
+
1. Prompt for summarization
|
|
137
|
+
2. Call `session.navigateTree()`
|
|
138
|
+
3. Clear and re-render chat
|
|
139
|
+
4. Set editor text if applicable
|
|
140
|
+
|
|
141
|
+
## Hook Events
|
|
142
|
+
|
|
143
|
+
### `session_before_tree`
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
interface TreePreparation {
|
|
147
|
+
targetId: string;
|
|
148
|
+
oldLeafId: string | null;
|
|
149
|
+
commonAncestorId: string | null;
|
|
150
|
+
entriesToSummarize: SessionEntry[];
|
|
151
|
+
userWantsSummary: boolean;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
interface SessionBeforeTreeEvent {
|
|
155
|
+
type: "session_before_tree";
|
|
156
|
+
preparation: TreePreparation;
|
|
157
|
+
model: Model;
|
|
158
|
+
signal: AbortSignal;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
interface SessionBeforeTreeResult {
|
|
162
|
+
cancel?: boolean;
|
|
163
|
+
summary?: { summary: string; details?: unknown };
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### `session_tree`
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
interface SessionTreeEvent {
|
|
171
|
+
type: "session_tree";
|
|
172
|
+
newLeafId: string | null;
|
|
173
|
+
oldLeafId: string | null;
|
|
174
|
+
summaryEntry?: BranchSummaryEntry;
|
|
175
|
+
fromHook?: boolean;
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Example: Custom Summarizer
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
export default function(pi: HookAPI) {
|
|
183
|
+
pi.on("session_before_tree", async (event, ctx) => {
|
|
184
|
+
if (!event.preparation.userWantsSummary) return;
|
|
185
|
+
if (event.preparation.entriesToSummarize.length === 0) return;
|
|
186
|
+
|
|
187
|
+
const summary = await myCustomSummarizer(event.preparation.entriesToSummarize);
|
|
188
|
+
return { summary: { summary, details: { custom: true } } };
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Error Handling
|
|
194
|
+
|
|
195
|
+
- Summarization failure: cancels navigation, shows error
|
|
196
|
+
- User abort (Escape): cancels navigation
|
|
197
|
+
- Hook returns `cancel: true`: cancels navigation silently
|