@earendil-works/pi-coding-agent 0.86.1 → 0.87.1

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 (160) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.md +25 -675
  3. package/dist/bundle/chunks/{anthropic-messages-MYU5ZMRF.js → anthropic-messages-J5WXPPPC.js} +1 -1
  4. package/dist/bundle/chunks/chunk-65HAU2C5.js +2 -0
  5. package/dist/bundle/chunks/{chunk-CMRUVXTE.js → chunk-OJP47DM6.js} +48 -42
  6. package/dist/bundle/chunks/github-copilot.js +1 -1
  7. package/dist/bundle/chunks/{openai-completions-CYGM3XXP.js → openai-completions-OBX42CLD.js} +2 -2
  8. package/dist/bundle/chunks/{virtual-modules-MGTKWDID.js → virtual-modules-VHMJYYWQ.js} +1 -1
  9. package/dist/bundle/cli-runtime.js +1 -1
  10. package/dist/bundle/index.js +1 -1
  11. package/dist/bundle/rpc-entry.js +1 -1
  12. package/dist/cli/args.d.ts.map +1 -1
  13. package/dist/cli/args.js +14 -4
  14. package/dist/cli/args.js.map +1 -1
  15. package/dist/cli/file-processor.d.ts +1 -1
  16. package/dist/cli/file-processor.d.ts.map +1 -1
  17. package/dist/cli/file-processor.js.map +1 -1
  18. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  19. package/dist/core/agent-session-runtime.js +1 -1
  20. package/dist/core/agent-session-runtime.js.map +1 -1
  21. package/dist/core/agent-session.d.ts +27 -3
  22. package/dist/core/agent-session.d.ts.map +1 -1
  23. package/dist/core/agent-session.js +389 -119
  24. package/dist/core/agent-session.js.map +1 -1
  25. package/dist/core/cache-warmer.d.ts +1 -0
  26. package/dist/core/cache-warmer.d.ts.map +1 -1
  27. package/dist/core/cache-warmer.js +15 -1
  28. package/dist/core/cache-warmer.js.map +1 -1
  29. package/dist/core/compaction/compaction.d.ts +3 -1
  30. package/dist/core/compaction/compaction.d.ts.map +1 -1
  31. package/dist/core/compaction/compaction.js +155 -57
  32. package/dist/core/compaction/compaction.js.map +1 -1
  33. package/dist/core/crash-log.d.ts +5 -0
  34. package/dist/core/crash-log.d.ts.map +1 -1
  35. package/dist/core/crash-log.js +68 -0
  36. package/dist/core/crash-log.js.map +1 -1
  37. package/dist/core/export-html/template.js +6 -1
  38. package/dist/core/extensions/index.d.ts +1 -1
  39. package/dist/core/extensions/index.d.ts.map +1 -1
  40. package/dist/core/extensions/index.js.map +1 -1
  41. package/dist/core/extensions/runner.d.ts +16 -3
  42. package/dist/core/extensions/runner.d.ts.map +1 -1
  43. package/dist/core/extensions/runner.js +110 -5
  44. package/dist/core/extensions/runner.js.map +1 -1
  45. package/dist/core/extensions/types.d.ts +77 -6
  46. package/dist/core/extensions/types.d.ts.map +1 -1
  47. package/dist/core/extensions/types.js.map +1 -1
  48. package/dist/core/index.d.ts +1 -1
  49. package/dist/core/index.d.ts.map +1 -1
  50. package/dist/core/index.js.map +1 -1
  51. package/dist/core/model-config.d.ts +52 -0
  52. package/dist/core/model-config.d.ts.map +1 -1
  53. package/dist/core/model-config.js +16 -0
  54. package/dist/core/model-config.js.map +1 -1
  55. package/dist/core/model-resolver.d.ts.map +1 -1
  56. package/dist/core/model-resolver.js +1 -1
  57. package/dist/core/model-resolver.js.map +1 -1
  58. package/dist/core/prompt-templates.d.ts +6 -1
  59. package/dist/core/prompt-templates.d.ts.map +1 -1
  60. package/dist/core/prompt-templates.js +61 -35
  61. package/dist/core/prompt-templates.js.map +1 -1
  62. package/dist/core/provider-composer.d.ts +1 -0
  63. package/dist/core/provider-composer.d.ts.map +1 -1
  64. package/dist/core/provider-composer.js +19 -0
  65. package/dist/core/provider-composer.js.map +1 -1
  66. package/dist/core/resource-loader.d.ts.map +1 -1
  67. package/dist/core/resource-loader.js +6 -2
  68. package/dist/core/resource-loader.js.map +1 -1
  69. package/dist/core/sdk.d.ts.map +1 -1
  70. package/dist/core/sdk.js +3 -4
  71. package/dist/core/sdk.js.map +1 -1
  72. package/dist/core/session-manager.d.ts +36 -9
  73. package/dist/core/session-manager.d.ts.map +1 -1
  74. package/dist/core/session-manager.js +97 -7
  75. package/dist/core/session-manager.js.map +1 -1
  76. package/dist/core/tools/read.d.ts +4 -1
  77. package/dist/core/tools/read.d.ts.map +1 -1
  78. package/dist/core/tools/read.js +5 -1
  79. package/dist/core/tools/read.js.map +1 -1
  80. package/dist/index.d.ts +2 -2
  81. package/dist/index.d.ts.map +1 -1
  82. package/dist/index.js +1 -1
  83. package/dist/index.js.map +1 -1
  84. package/dist/main.d.ts.map +1 -1
  85. package/dist/main.js +4 -3
  86. package/dist/main.js.map +1 -1
  87. package/dist/modes/interactive/bug-report.d.ts.map +1 -1
  88. package/dist/modes/interactive/bug-report.js +4 -0
  89. package/dist/modes/interactive/bug-report.js.map +1 -1
  90. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  91. package/dist/modes/interactive/components/tree-selector.js +7 -0
  92. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  93. package/dist/modes/interactive/interactive-mode.d.ts +3 -0
  94. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  95. package/dist/modes/interactive/interactive-mode.js +64 -2
  96. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  97. package/dist/utils/mime.d.ts.map +1 -1
  98. package/dist/utils/mime.js +1 -1
  99. package/dist/utils/mime.js.map +1 -1
  100. package/dist/utils/tool-result-images.d.ts +3 -1
  101. package/dist/utils/tool-result-images.d.ts.map +1 -1
  102. package/dist/utils/tool-result-images.js +4 -1
  103. package/dist/utils/tool-result-images.js.map +1 -1
  104. package/docs/cli-integration.md +106 -0
  105. package/docs/cli.md +268 -0
  106. package/docs/compaction.md +45 -26
  107. package/docs/configuration.md +45 -0
  108. package/docs/containerization.md +109 -82
  109. package/docs/custom-provider.md +132 -784
  110. package/docs/docs.json +139 -99
  111. package/docs/environment-variables.md +3 -5
  112. package/docs/extensions.md +134 -2956
  113. package/docs/how-pi-works.md +49 -0
  114. package/docs/images/interactive-mode.png +0 -0
  115. package/docs/index.md +24 -69
  116. package/docs/json.md +193 -65
  117. package/docs/keybindings.md +57 -102
  118. package/docs/llama-cpp.md +3 -3
  119. package/docs/message-types.md +261 -0
  120. package/docs/models.md +64 -546
  121. package/docs/packages.md +66 -167
  122. package/docs/prompt-templates.md +31 -68
  123. package/docs/providers.md +102 -240
  124. package/docs/quickstart.md +61 -106
  125. package/docs/rpc-commands.md +854 -0
  126. package/docs/rpc-extension-ui.md +200 -0
  127. package/docs/rpc.md +129 -1556
  128. package/docs/sdk.md +76 -1160
  129. package/docs/security.md +70 -32
  130. package/docs/session-format.md +25 -216
  131. package/docs/sessions.md +35 -141
  132. package/docs/settings.md +109 -387
  133. package/docs/shell-aliases.md +85 -5
  134. package/docs/skills.md +51 -190
  135. package/docs/slash-commands.md +60 -0
  136. package/docs/terminal-setup.md +105 -78
  137. package/docs/termux.md +74 -83
  138. package/docs/themes.md +68 -280
  139. package/docs/tmux.md +31 -39
  140. package/docs/tui.md +69 -923
  141. package/docs/usage.md +54 -272
  142. package/docs/windows.md +43 -17
  143. package/examples/README.md +13 -2
  144. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  145. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  146. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  147. package/examples/extensions/gondolin/package-lock.json +2 -2
  148. package/examples/extensions/gondolin/package.json +1 -1
  149. package/examples/extensions/sandbox/package-lock.json +2 -2
  150. package/examples/extensions/sandbox/package.json +1 -1
  151. package/examples/extensions/with-deps/package-lock.json +2 -2
  152. package/examples/extensions/with-deps/package.json +1 -1
  153. package/examples/plugins/pi-example-plugin/src/session.ts +3 -2
  154. package/examples/rpc-client.ts +35 -0
  155. package/examples/rpc-extension-ui.ts +25 -5
  156. package/examples/sdk/README.md +1 -1
  157. package/npm-shrinkwrap.json +20 -20
  158. package/package.json +8 -8
  159. package/dist/bundle/chunks/chunk-HTEQD2HM.js +0 -2
  160. package/docs/development.md +0 -90
package/docs/security.md CHANGED
@@ -1,59 +1,97 @@
1
- # Security
1
+ # Run Pi safely
2
2
 
3
- Pi is a local coding agent. It runs with the permissions of the user account that starts it, and it treats files writable by that user as inside the same local trust boundary.
3
+ Treat model-generated commands and code as untrusted. Pi can read, change, and execute files with the permissions of the account that started it, and it does not ask for approval before every tool call. Extensions, package installers, language servers, and other child processes run with those same permissions unless an operating-system or virtualization boundary restricts them.
4
4
 
5
- ## Project Trust
5
+ Files, comments, instructions, command output, and model responses can steer the model through prompt injection. Project trust controls which project resources load at startup, but it does not make that content or the resulting actions safe.
6
6
 
7
- Project trust controls whether pi loads project-local settings, resources, packages, and extensions. It is not a sandbox and it does not restrict what the model can ask tools to do after you start working in a directory.
7
+ Safety comes from limiting the files, credentials, processes, and network services Pi can access and affect if a generated action is wrong or hostile. Watching the transcript, using project trust, and reviewing changes do not create a security boundary.
8
8
 
9
- Pi considers a project to have resources that require trust when it finds any of these from the current working directory:
9
+ ## Choose how to run Pi
10
+
11
+ Different ways of running Pi place different limits on what generated commands can access:
12
+
13
+ | How Pi runs | What remains protected |
14
+ |---|---|
15
+ | Directly, with the permissions of its operating-system user | Anything that user cannot access. A dedicated user account can narrow those permissions, but Pi still shares the operating system and network with other users. |
16
+ | Entirely inside a container, virtual machine, or sandbox | Host files and processes that you do not expose to the environment. Credentials and network services remain accessible if you make them available inside it. This is usually the strongest practical option. |
17
+ | Outside the isolated environment, with only its built-in tools running inside | Host resources are protected from actions performed through those tools. Pi itself and other extensions remain outside the boundary, so this is a narrower form of isolation. |
18
+
19
+ The working folder controls resource discovery and the default location for tools, but it does not prevent commands from accessing other paths available to the Pi process.
20
+
21
+ Whichever option you choose, only provide the files and services required for the task. Keep credentials outside the environment where possible, or use narrowly scoped, short-lived credentials. Restrict network access when commands do not need it.
22
+
23
+ For setup instructions and the limitations of each isolation method, see [Run Pi in an isolated environment](containerization.md).
24
+
25
+ <a id="project-trust"></a>
26
+
27
+ ## Understand project trust
28
+
29
+ Project trust controls whether Pi loads most settings and resources supplied by a working folder. It prevents a folder from silently loading executable extensions before you approve it.
30
+
31
+ Project trust is not a complete startup boundary. Pi reads the project `sessionDir` setting while selecting or creating a session, before it resolves project trust. Declining trust prevents the remaining project settings and protected resources from loading, but it cannot undo that initial session-directory lookup.
32
+
33
+ Project trust does not limit what tool calls can access or affect. After Pi starts, enabled tools still use the operating-system permissions of the Pi process. Instructions and other content in the folder can also influence the model.
34
+
35
+ ### Resources protected by project trust
36
+
37
+ Pi requires a project-trust decision when it finds any of these resources from the current working directory:
10
38
 
11
39
  - `.pi/settings.json`
12
40
  - `.pi/extensions`, `.pi/skills`, `.pi/prompts`, or `.pi/themes`
13
41
  - `.pi/SYSTEM.md` or `.pi/APPEND_SYSTEM.md`
14
42
  - project `.agents/skills` in the current directory or an ancestor directory
15
43
 
16
- A bare `.pi` directory does not count as a project resource that requires trust.
44
+ A bare `.pi` directory does not require project trust.
17
45
 
18
- When an interactive session starts in a project with resources that require trust and no saved decision for the current directory or a parent directory, pi follows `defaultProjectTrust` from global settings. The default value is `"ask"`, which asks whether to trust the project when UI is available. Saved decisions are stored by canonical directory in `~/.pi/agent/trust.json`, and the closest saved decision on the current or parent path applies before the global default.
46
+ Granting project trust allows Pi to load:
19
47
 
20
- Trusting a project allows pi to load project resources that require trust, including:
48
+ - project settings
49
+ - extensions, skills, prompt templates, themes, and system-prompt files under `.pi`
50
+ - missing packages configured through project settings
51
+ - project-local and project-package extensions
21
52
 
22
- - `.pi/settings.json`
23
- - `.pi` resources such as extensions, skills, prompt templates, themes, and system prompt files
24
- - missing project packages configured through project settings
25
- - project-local extensions and project package-managed extensions
53
+ Declining project trust skips those protected resources, except for the initial `sessionDir` lookup described above.
54
+
55
+ Context files such as `AGENTS.override.md`, `AGENTS.md`, and `CLAUDE.md` load regardless of project trust unless you disable context loading. Treat instructions in a folder as untrusted input even when you decline project trust.
56
+
57
+ ### How Pi chooses a trust decision
58
+
59
+ A command-line `--approve` or `--no-approve` override applies first. When protected resources exist and there is no command-line override:
26
60
 
27
- Declining trust skips protected resources. Context files such as `AGENTS.override.md`, `AGENTS.md`, and `CLAUDE.md` are loaded regardless of project trust unless context loading is disabled. Before trust is resolved, pi only loads context files, user/global extensions, and CLI `-e` extensions. User/global and CLI extensions can handle the `project_trust` event; the first extension that returns a yes/no decision owns the decision.
61
+ 1. User-level and command-line extensions can handle the `project_trust` event. The first extension that returns yes or no owns the decision.
62
+ 2. If no extension decides, Pi looks for a saved decision for the current directory or one of its parents. The closest decision applies.
63
+ 3. If no saved decision applies, Pi follows the global `defaultProjectTrust` setting, whose default is `"ask"`.
28
64
 
29
- Non-interactive modes (`-p`, `--mode json`, and `--mode rpc`) do not show a trust prompt. Without an applicable saved trust decision, `defaultProjectTrust: "ask"` and `"never"` ignore such resources, while `"always"` trusts them. Use `--approve`/`-a` or `--no-approve`/`-na` to override project trust for one run.
65
+ Saved decisions use canonical directory paths and live in:
30
66
 
31
- ## No Built-in Sandbox
67
+ ```text
68
+ ~/.pi/agent/trust.json
69
+ ```
32
70
 
33
- Pi does not include a built-in sandbox. Built-in tools can read files, write files, edit files, and run shell commands with the permissions of the pi process. Extensions are TypeScript modules that run with the same permissions. Package installs, shell commands, language servers, test commands, and other developer tools behave as ordinary local processes.
71
+ Use `/trust` to save a decision for future Pi processes.
34
72
 
35
- This is intentional. Pi is designed to operate on local source trees, invoke project toolchains, and integrate with the user's existing development environment. A partial in-process sandbox would be easy to misunderstand as a security boundary while still depending on the host shell, filesystem, package managers, credentials, and extension code. Real isolation needs to come from the operating system or a virtualization/container boundary.
73
+ ### Project trust without an interactive prompt
36
74
 
37
- Project trust is only an input-loading guard. It prevents a repository from silently changing pi's settings or extensions before you approve it. It does not make untrusted code, untrusted prompts, or untrusted model output safe. Prompt injection from repository files, comments, documentation, context files, or build output is expected local-agent risk and cannot be reliably prevented by pi.
75
+ Print, JSON, and RPC modes cannot show the built-in trust prompt. If no command-line override, extension, or saved decision applies:
38
76
 
39
- ## Running Untrusted or Unmonitored Work
77
+ - `defaultProjectTrust: "always"` loads protected project resources.
78
+ - `defaultProjectTrust: "ask"` or `"never"` skips them.
40
79
 
41
- For untrusted repositories, generated code you do not intend to monitor closely, or unattended automation, run pi in a contained environment. Use a container, VM, micro-VM, remote sandbox, or policy-controlled sandbox with only the files and credentials required for the task.
80
+ Use `--approve` or `--no-approve` when an automated run needs an explicit one-time decision.
42
81
 
43
- Common patterns are documented in [Containerization](containerization.md):
82
+ ## Reduce impact and improve recovery
44
83
 
45
- - run the whole `pi` process inside a container/sandbox
46
- - run host pi while routing built-in tool execution into a Gondolin micro-VM
47
- - mount only the workspace paths the agent should access
48
- - avoid mounting host `~/.pi/agent` unless the container should access host sessions, settings, and credentials
49
- - pass the minimum required API keys or use short-lived credentials
50
- - restrict network access when the task does not need it
51
- - review diffs and outputs before copying results back to trusted systems
84
+ These practices do not replace isolation, but they reduce exposure or make recovery easier:
52
85
 
53
- If you bind-mount a host workspace read/write, writes from inside the container or VM can still modify host files. Use read-only mounts or copy files into and out of the sandbox when you need stronger protection from unintended writes.
86
+ - Give Pi access only to files and services required for the task.
87
+ - Use snapshots, backups, or version control before substantial changes.
88
+ - Review extensions and packages before loading them. Extensions execute inside the Pi process.
89
+ - Prefer narrowly scoped, short-lived credentials.
90
+ - Review diffs and generated output before applying results to another system.
91
+ - Review sessions before exporting or sharing them. They can contain prompts, tool arguments, command output, file contents, and credentials exposed during the conversation.
54
92
 
55
- ## Reporting Security Issues
93
+ ## Report a security issue
56
94
 
57
- To report a security issue, follow the repository [Security Policy](https://github.com/earendil-works/pi/blob/main/SECURITY.md). Do not open a public issue for security-sensitive reports.
95
+ Follow the repository [Security Policy](https://github.com/earendil-works/pi/blob/main/SECURITY.md). Do not open a public issue for a security-sensitive report.
58
96
 
59
- Expected local-agent behavior, lack of a built-in sandbox, prompt injection from untrusted content, and behavior of user-installed extensions or skills are generally outside the security boundary unless the report demonstrates a real privilege-boundary bypass or shows how pi grants access that the local user did not already have.
97
+ Expected local-agent behavior, prompt injection from untrusted content, lack of a built-in sandbox, and behavior from user-installed extensions or skills are generally outside the security boundary unless the report demonstrates a privilege-boundary bypass or access that the local user did not already have.
@@ -2,6 +2,9 @@
2
2
 
3
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
+ For programmatic creation, persistence, and tree navigation, see the [`SessionManager` API](sdk.md#sessionmanager-api).
6
+
7
+
5
8
  ## File Location
6
9
 
7
10
  ```
@@ -30,169 +33,18 @@ Existing sessions are automatically migrated to the current version (v3) when lo
30
33
 
31
34
  Source on GitHub ([pi](https://github.com/earendil-works/pi)):
32
35
  - [`packages/coding-agent/src/core/session-manager.ts`](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/src/core/session-manager.ts) - Session entry types and SessionManager
33
- - [`packages/coding-agent/src/core/messages.ts`](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/src/core/messages.ts) - Extended message types (BashExecutionMessage, CustomMessage, etc.)
34
- - [`packages/ai/src/types.ts`](https://github.com/earendil-works/pi/blob/main/packages/ai/src/types.ts) - Base message types (UserMessage, AssistantMessage, ToolResultMessage)
35
- - [`packages/agent/src/types.ts`](https://github.com/earendil-works/pi/blob/main/packages/agent/src/types.ts) - AgentMessage union type
36
+ - [Message Types](message-types.md) - Shared message and content-block reference
37
+ - [`packages/coding-agent/src/core/messages.ts`](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/src/core/messages.ts) - Extended message types
38
+ - [`packages/ai/src/types.ts`](https://github.com/earendil-works/pi/blob/main/packages/ai/src/types.ts) - Base message and content-block types
39
+ - [`packages/agent/src/types.ts`](https://github.com/earendil-works/pi/blob/main/packages/agent/src/types.ts) - Extensible `AgentMessage` union
36
40
 
37
41
  For TypeScript definitions in your project, inspect `node_modules/@earendil-works/pi-coding-agent/dist/` and `node_modules/@earendil-works/pi-ai/dist/`.
38
42
 
39
- ## Message Types
40
-
41
- Session entries contain `AgentMessage` objects. Understanding these types is essential for parsing sessions and writing extensions.
42
-
43
- ### Content Blocks
44
-
45
- Messages contain arrays of typed content blocks:
46
-
47
- ```typescript
48
- interface TextContent {
49
- type: "text";
50
- text: string;
51
- textSignature?: string;
52
- }
53
-
54
- interface ImageContent {
55
- type: "image";
56
- data: string; // base64 encoded
57
- mimeType: string; // e.g., "image/jpeg", "image/png"
58
- }
59
-
60
- interface ThinkingContent {
61
- type: "thinking";
62
- thinking: string;
63
- thinkingSignature?: string;
64
- redacted?: boolean;
65
- }
66
-
67
- interface ToolCall {
68
- type: "toolCall";
69
- id: string;
70
- name: string;
71
- arguments: Record<string, any>;
72
- thoughtSignature?: string;
73
- namespace?: string;
74
- }
75
- ```
76
-
77
- ### Base Message Types (from pi-ai)
78
-
79
- ```typescript
80
- interface SystemMessage {
81
- role: "system";
82
- content: string | TextContent[];
83
- toolsAdded?: Tool[];
84
- toolsRemoved?: Array<{ name: string }>;
85
- timestamp: number; // Unix ms
86
- }
87
-
88
- interface UserMessage {
89
- role: "user";
90
- content: string | (TextContent | ImageContent)[];
91
- timestamp: number; // Unix ms
92
- }
93
-
94
- interface AssistantMessage {
95
- role: "assistant";
96
- content: (TextContent | ThinkingContent | ToolCall)[];
97
- api: string;
98
- provider: string;
99
- model: string;
100
- responseModel?: string;
101
- responseId?: string;
102
- providerThinkingLevel?: string;
103
- diagnostics?: AssistantMessageDiagnostic[];
104
- usage: Usage;
105
- stopReason: "pending" | "stop" | "length" | "toolUse" | "error" | "aborted" | "deferred";
106
- deferred?: DeferredHandle;
107
- errorMessage?: string;
108
- rawStopReason?: string;
109
- endTurn?: boolean;
110
- timestamp: number;
111
- }
112
-
113
- interface ToolResultMessage {
114
- role: "toolResult";
115
- toolCallId: string;
116
- toolName: string;
117
- content: (TextContent | ImageContent)[];
118
- details?: any; // Tool-specific metadata
119
- usage?: Usage; // Nested LLM work performed by the tool
120
- isError: boolean;
121
- timestamp: number;
122
- }
123
-
124
- interface Usage {
125
- input: number;
126
- output: number;
127
- cacheRead: number;
128
- cacheWrite: number;
129
- cacheWrite1h?: number;
130
- reasoning?: number;
131
- totalTokens: number;
132
- cost: {
133
- input: number;
134
- output: number;
135
- cacheRead: number;
136
- cacheWrite: number;
137
- total: number;
138
- };
139
- }
140
- ```
141
-
142
- `"pending"` is reserved for partial messages in streaming events. Terminal events replace it with a completion reason before Pi persists the assistant message, so `"pending"` should never appear in session JSONL. `"deferred"` is a terminal reason for a provider response that will complete later; its `deferred` handle contains the provider data needed to retrieve that response.
43
+ ## Messages
143
44
 
144
- ### Extended Message Types (from pi-coding-agent)
145
-
146
- ```typescript
147
- interface BashExecutionMessage {
148
- role: "bashExecution";
149
- command: string;
150
- output: string;
151
- exitCode: number | undefined;
152
- cancelled: boolean;
153
- truncated: boolean;
154
- fullOutputPath?: string;
155
- excludeFromContext?: boolean; // true for !! prefix commands
156
- timestamp: number;
157
- }
45
+ A `message` entry stores an [`AgentMessage`](message-types.md). Message content blocks, roles, usage, and message timestamps are defined in [Message Types](message-types.md).
158
46
 
159
- interface CustomMessage {
160
- role: "custom";
161
- customType: string; // Extension identifier
162
- content: string | (TextContent | ImageContent)[];
163
- display: boolean; // Show in TUI
164
- details?: any; // Extension-specific metadata
165
- timestamp: number;
166
- }
167
-
168
- interface BranchSummaryMessage {
169
- role: "branchSummary";
170
- summary: string;
171
- fromId: string | null; // Previous leaf whose abandoned path was summarized
172
- timestamp: number;
173
- }
174
-
175
- interface CompactionSummaryMessage {
176
- role: "compactionSummary";
177
- summary: string;
178
- tokensBefore: number;
179
- timestamp: number;
180
- }
181
- ```
182
-
183
- ### AgentMessage Union
184
-
185
- ```typescript
186
- type AgentMessage =
187
- | SystemMessage
188
- | UserMessage
189
- | AssistantMessage
190
- | ToolResultMessage
191
- | BashExecutionMessage
192
- | CustomMessage
193
- | BranchSummaryMessage
194
- | CompactionSummaryMessage;
195
- ```
47
+ Session entry timestamps are ISO 8601 strings. The nested message timestamp is a Unix timestamp in milliseconds.
196
48
 
197
49
  ## Entry Base
198
50
 
@@ -274,7 +126,7 @@ Created when context is compacted. Stores a summary of earlier messages and a co
274
126
  {"type":"compaction","id":"f6g7h8i9","parentId":"e5f6g7h8","timestamp":"2024-12-03T14:10:00.000Z","summary":"User discussed X, Y, Z...","firstKeptEntryId":"c3d4e5f6","tokensBefore":50000,"systemMessage":{"role":"system","content":"You are a coding assistant.","toolsAdded":[],"timestamp":1733235000000}}
275
127
  ```
276
128
 
277
- `firstKeptEntryId` is required. It identifies the first entry retained from before the compaction entry. When rebuilding context, Pi replaces older summarized entries with the compaction summary and keeps the range beginning at this entry.
129
+ `firstKeptEntryId` is required. It identifies the first entry retained from before the compaction entry. When rebuilding context, Pi replaces older summarized entries with the compaction summary and keeps the range beginning at this entry. A retain-none compaction stores its own ID in this field, so no preceding entries are retained.
278
130
 
279
131
  Optional fields:
280
132
  - `systemMessage`: The replayed prompt sections and tool declarations at the compaction boundary; it becomes the leading system message of the compacted context, and system messages among the kept entries are dropped in its favor. It is absent on older session entries.
@@ -282,6 +134,16 @@ Optional fields:
282
134
  - `details`: Implementation-specific data (e.g., `{ readFiles: string[], modifiedFiles: string[] }` for default, or custom data for extensions)
283
135
  - `fromHook`: `true` if generated by an extension, `false`/`undefined` if pi-generated (legacy field name)
284
136
 
137
+ ### ContextEditEntry
138
+
139
+ Append-only edit of one earlier context-producing entry. It changes only future model context; the target entry and its metadata remain unchanged in raw history, UI, exports, and session accounting.
140
+
141
+ ```json
142
+ {"type":"context_edit","id":"g6h7i8j9","parentId":"f6g7h8i9","timestamp":"2024-12-03T14:11:00.000Z","targetId":"c3d4e5f6","replacement":null}
143
+ ```
144
+
145
+ Targets may be user, assistant, tool-result, or custom-message entries. `replacement: null` omits the target from model context. A non-null `replacement` replaces only the target message content. String replacements for assistant and tool-result entries are normalized to one text block because those roles require content arrays. If several edits target the same entry, the latest edit on the active branch wins. Edits are branch-relative: navigating to a point before the edit reveals the target's original contribution again.
146
+
285
147
  ### BranchSummaryEntry
286
148
 
287
149
  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.
@@ -366,7 +228,9 @@ Entries normally form one tree, but navigation APIs can create multiple roots:
366
228
  - Includes entries after the compaction entry
367
229
  3. Preserves non-message entries in the selected range so interactive mode can render them
368
230
 
369
- `buildSessionContext()` builds on that entry list to produce the message list for the LLM:
231
+ `buildSessionProjection()` then applies the latest `context_edit` for each selected target. It returns the model-visible messages together with their source entries. Omitted targets produce no message; replacements retain the source entry's role and metadata while changing only content. The raw selected entries are not modified.
232
+
233
+ `buildSessionContext()` builds on that projection to produce the message list for the LLM:
370
234
 
371
235
  1. Extracts current model and thinking level settings from the full path
372
236
  2. Converts selected entries to messages:
@@ -374,6 +238,7 @@ Entries normally form one tree, but navigation APIs can create multiple roots:
374
238
  - `compaction` -> complete system checkpoint followed by `compactionSummary`
375
239
  - `branch_summary` -> `branchSummary`
376
240
  - `custom_message` -> `CustomMessage`
241
+ - `context_edit` -> no context message of its own
377
242
  - `usage` and `custom` -> no context message
378
243
 
379
244
  The compaction summary replaces entries before `firstKeptEntryId`. Pre-compaction system messages are folded into the complete checkpoint rather than replayed from the retained range. Retained non-system entries and all entries after the compaction remain available to the LLM.
@@ -422,59 +287,3 @@ for (const line of lines) {
422
287
  }
423
288
  }
424
289
  ```
425
-
426
- ## SessionManager API
427
-
428
- Key methods for working with sessions programmatically.
429
-
430
- ### Static Creation Methods
431
- - `SessionManager.create(cwd, sessionDir?, options?)` - New session; `options` can set `id` and `parentSession`
432
- - `SessionManager.open(path, sessionDir?, cwdOverride?)` - Open existing session file
433
- - `SessionManager.continueRecent(cwd, sessionDir?)` - Continue most recent or create new
434
- - `SessionManager.inMemory(cwd?, options?, entries?)` - No file persistence, optionally initialized from entries
435
- - `SessionManager.forkFrom(sourcePath, targetCwd, sessionDir?, options?)` - Fork session from another project
436
-
437
- ### Static Listing Methods
438
- - `SessionManager.list(cwd, sessionDir?, onProgress?)` - List sessions for a directory
439
- - `SessionManager.listAll(onProgress?)` - List all sessions across all projects
440
- - `SessionManager.listAll(sessionDir?, onProgress?)` - List sessions from a custom session root
441
-
442
- ### Instance Methods - Session Management
443
- - `newSession(options?)` - Start a new session (options: `{ id?: string, parentSession?: string }`)
444
- - `setSessionFile(path)` - Switch to a different session file
445
- - `createBranchedSession(leafId)` - Extract branch to new session file
446
-
447
- ### Instance Methods - Appending (all return entry ID)
448
- - `appendMessage(message)` - Add message
449
- - `appendThinkingLevelChange(level)` - Record thinking change
450
- - `appendModelChange(provider, modelId)` - Record model change
451
- - `appendUsage(kind, provider, model, usage)` - Record model-attributed usage outside the conversation
452
- - `appendCompaction(summary, firstKeptEntryId, tokensBefore, details?, fromHook?, usage?)` - Add compaction
453
- - `appendCustomEntry(customType, data?)` - Extension state (not in context)
454
- - `appendSessionInfo(name)` - Set session display name
455
- - `appendCustomMessageEntry(customType, content, display, details?)` - Extension message (in context)
456
- - `appendLabelChange(targetId, label)` - Set/clear label
457
-
458
- ### Instance Methods - Tree Navigation
459
- - `getLeafId()` - Current position
460
- - `getLeafEntry()` - Get current leaf entry
461
- - `getEntry(id)` - Get entry by ID
462
- - `getBranch(fromId?)` - Walk from entry to root
463
- - `getTree()` - Get full tree structure
464
- - `getChildren(parentId)` - Get direct children
465
- - `getLabel(id)` - Get label for entry
466
- - `branch(entryId)` - Move leaf to earlier entry
467
- - `resetLeaf()` - Reset leaf to null (before any entries)
468
- - `branchWithSummary(entryId, summary, details?, fromHook?, usage?)` - Branch with context summary; `entryId` may be `null` to branch from the root
469
-
470
- ### Instance Methods - Context & Info
471
- - `buildContextEntries()` - Get active branch entries with compaction applied
472
- - `buildSessionContext()` - Get messages, thinkingLevel, and model for LLM
473
- - `getEntries()` - All entries (excluding header)
474
- - `getHeader()` - Session header metadata
475
- - `getSessionName()` - Get display name from latest session_info entry
476
- - `getCwd()` - Working directory
477
- - `getSessionDir()` - Session storage directory
478
- - `getSessionId()` - Session UUID
479
- - `getSessionFile()` - Session file path (undefined for in-memory)
480
- - `isPersisted()` - Whether session is saved to disk
package/docs/sessions.md CHANGED
@@ -1,172 +1,66 @@
1
- # Sessions
1
+ # Sessions and Context
2
2
 
3
- Pi saves conversations as sessions so you can continue work, branch from earlier turns, and revisit previous paths.
3
+ Pi saves a conversation as a session. The active branch of that session supplies conversation history for the next model request. Use session commands to continue work, explore another branch, or reduce the amount of history sent to the model.
4
4
 
5
- ## Session Storage
5
+ ## Continue or switch sessions
6
6
 
7
- Sessions auto-save to `~/.pi/agent/sessions/`, organized by working directory. Each session is a JSONL file with a tree structure.
7
+ Pi saves sessions automatically unless you start it with `--no-session`.
8
8
 
9
9
  ```bash
10
- pi -c # Continue most recent session
11
- pi -r # Browse and select from past sessions
12
- pi --no-session # Ephemeral mode; do not save
13
- pi --name "my task" # Set session display name at startup
14
- pi --session <path|id> # Use a specific session file or partial session ID
15
- pi --fork <path|id> # Fork a session file or partial session ID into a new session
10
+ pi --continue
11
+ pi --resume
16
12
  ```
17
13
 
18
- Use `/session` in interactive mode to see the current session file, session ID, message count, tokens, and cost.
14
+ `--continue` opens the most recent session for the current working directory. `--resume` opens the session picker. In interactive mode, `/resume` opens the same picker and `/new` starts a new session.
19
15
 
20
- For the JSONL file format and SessionManager API, see [Session Format](session-format.md).
16
+ Use `/name` or `--name` to assign a recognizable session name. Run `/session` to verify the current session file, ID, message count, token usage, and cost.
21
17
 
22
- ## Session Commands
18
+ The session picker lets you search, rename, and delete sessions. It can also show paths, change sorting, and limit results to named sessions. See [Keybindings](keybindings.md#sessions) for its shortcuts.
23
19
 
24
- | Command | Description |
25
- |---------|-------------|
26
- | `/resume` | Browse and select previous sessions |
27
- | `/new` | Start a new session |
28
- | `/name <name>` | Set the current session display name |
29
- | `/session` | Show session info |
30
- | `/tree` | Navigate the current session tree |
31
- | `/fork` | Create a new session from a previous user message |
32
- | `/clone` | Duplicate the current active branch into a new session |
33
- | `/compact [prompt]` | Summarize older context; see [Compaction](compaction.md) |
34
- | `/export [file]` | Export session to HTML |
35
- | `/share` | Upload as private GitHub gist with shareable HTML link |
36
- | `/bug [description]` | Report a bug to the Pi developers; see [Reporting Bugs](#reporting-bugs) |
20
+ ## Choose how to branch
37
21
 
38
- ## Resuming and Deleting Sessions
22
+ Pi stores entries as a tree, so returning to an earlier point does not erase the branch you leave.
39
23
 
40
- `/resume` opens an interactive session picker for the current project. `pi -r` opens the same picker at startup.
24
+ | Action | Result | Use it when |
25
+ |---|---|---|
26
+ | `/tree` | Moves within the current session file | Related alternatives should stay together |
27
+ | `/fork` | Creates a new session from an earlier user message | The alternative should become separate work |
28
+ | `/clone` | Copies the active branch into a new session | You want a separate copy of the current state |
41
29
 
42
- In the picker you can:
30
+ In `/tree`, select a user message to put its text back in the editor. Edit and submit it to create another branch. Selecting an assistant response or another entry continues after that entry with an empty editor.
43
31
 
44
- - search by typing
45
- - toggle path display with Ctrl+P
46
- - toggle sort mode with Ctrl+S
47
- - filter to named sessions with Ctrl+N
48
- - rename with Ctrl+R
49
- - delete with Ctrl+D, then confirm
32
+ When you leave a branch, Pi can summarize it and attach that summary to the branch you enter. This preserves relevant work from the abandoned path without including every message from it.
50
33
 
51
- When available, pi uses the `trash` CLI for deletion instead of permanently removing files.
34
+ For the persisted tree and entry types, see [Session Format](session-format.md).
52
35
 
53
- ## Naming Sessions
36
+ ## Manage conversation context
54
37
 
55
- Use `/name <name>` to set a human-readable session name:
38
+ The model receives the active branch, not every branch in the session file. Pi combines that history with the system prompt, discovered context files, available tools, and loaded skill descriptions. [How Pi Works](how-pi-works.md#context) describes how those inputs are assembled.
56
39
 
57
- ```text
58
- /name Refactor auth module
59
- ```
60
-
61
- Set the name at startup with `--name` or `-n`:
62
-
63
- ```bash
64
- pi --name "Refactor auth module"
65
- pi --name "CI audit" -p "Review this build failure"
66
- ```
67
-
68
- Named sessions are easier to find in `/resume` and `pi -r`.
69
-
70
- ## Branching with `/tree`
71
-
72
- Sessions are stored as trees. Every entry has an `id` and `parentId`, and the current position is the active leaf. `/tree` lets you jump to any previous point and continue from there without creating a new file.
73
-
74
- <p align="center"><img src="images/tree-view.png" alt="Tree View" width="600"></p>
75
-
76
- Example shape:
77
-
78
- ```text
79
- ├─ user: "Hello, can you help..."
80
- │ └─ assistant: "Of course! I can..."
81
- │ ├─ user: "Let's try approach A..."
82
- │ │ └─ assistant: "For approach A..."
83
- │ │ └─ user: "That worked..." ← active
84
- │ └─ user: "Actually, approach B..."
85
- │ └─ assistant: "For approach B..."
86
- ```
87
-
88
- ### Tree Controls
89
-
90
- | Key | Action |
91
- |-----|--------|
92
- | ↑/↓ | Navigate visible entries |
93
- | ←/→ | Page up/down |
94
- | Ctrl+←/Ctrl+→ or Alt+←/Alt+→ | Fold/unfold or jump between branch segments |
95
- | Shift+L | Set or clear a label on the selected entry |
96
- | Shift+T | Toggle label timestamps |
97
- | Enter | Select entry |
98
- | Escape/Ctrl+C | Cancel |
99
- | Ctrl+O | Cycle filter mode |
100
-
101
- Filter modes are: default, no-tools, user-only, labeled-only, and all. Configure the default with `treeFilterMode` in [Settings](settings.md).
102
-
103
- ### Selection Behavior
104
-
105
- Selecting a user or custom message:
106
-
107
- 1. Moves the leaf to the selected message's parent.
108
- 2. Places the selected message text in the editor.
109
- 3. Lets you edit and resubmit, creating a new branch.
110
-
111
- Selecting an assistant, tool, compaction, or other non-user entry:
112
-
113
- 1. Moves the leaf to that entry.
114
- 2. Leaves the editor empty.
115
- 3. Lets you continue from that point.
116
-
117
- Selecting the root user message resets the leaf to an empty conversation and places the original prompt in the editor.
118
-
119
- ## `/tree`, `/fork`, and `/clone`
120
-
121
- | Feature | `/tree` | `/fork` | `/clone` |
122
- |---------|---------|---------|----------|
123
- | Output | Same session file | New session file | New session file |
124
- | View | Full tree | User-message selector | Current active branch |
125
- | Typical use | Explore alternatives in place | Start a new session from an earlier prompt | Duplicate current work before continuing |
126
- | Summary | Optional branch summary | None | None |
127
-
128
- Use `/tree` when you want to keep alternatives together. Use `/fork` or `/clone` when you want a separate session file.
129
-
130
- ## Branch Summaries
131
-
132
- When `/tree` switches away from one branch to another, pi can summarize the abandoned branch and attach that summary at the new position. This preserves important context from the path you left without replaying the whole branch.
133
-
134
- When prompted, choose one of:
135
-
136
- 1. no summary
137
- 2. summarize with the default prompt
138
- 3. summarize with custom focus instructions
139
-
140
- See [Compaction](compaction.md) for branch summarization internals and extension hooks.
40
+ The footer shows current context usage. When the active context approaches the model's limit, Pi normally compacts older history automatically. Compaction adds a summary and keeps recent messages. It does not delete the original session entries.
141
41
 
142
- ## Reporting Bugs
42
+ Run `/compact` to compact manually. You can add instructions when the summary should preserve a particular topic or decision. Configure automatic compaction and retained history through [Settings](settings.md#compaction).
143
43
 
144
- `/bug [description]` collects a bug report for the Pi developers. The report is not shared publicly. The dialog asks for an optional description and whether to include the session transcript. If you decline the transcript, pi offers to have the current model write a summary of what went wrong instead; the transcript is sent to your provider with your credentials, and only the summary is attached.
44
+ Compaction can fail if the provider is unavailable or cannot accept the summarization request. Correct the provider problem and run `/compact` again. Disabling automatic compaction does not disable the manual command.
145
45
 
146
- The last step chooses where the report goes:
46
+ See [Compaction Reference](compaction.md) for thresholds, retained boundaries, branch-summary behavior, and extension hooks.
147
47
 
148
- - **Upload Report** sends it to the Pi developers through `radius.pi.dev`. No login is required; if you are logged into Radius, the report is attributed to your account so the developers can follow up. If the upload fails, pi offers to export the zip instead.
149
- - **Export as Zip** writes a zip archive to the current directory. Attach it to an issue or send it to the developers yourself.
48
+ ## Control session storage
150
49
 
151
- Both contain the same files:
50
+ By default, Pi stores sessions under `~/.pi/agent/sessions/`, grouped by working directory. Use `--session-dir`, `PI_CODING_AGENT_SESSION_DIR`, or the `sessionDir` setting to choose another location. The CLI option has highest precedence.
152
51
 
153
- | File | Content |
154
- |------|---------|
155
- | `report.json` | pi version, runtime, OS, terminal, current model and provider configuration, loaded extensions, and settings. API keys, header values, URL credentials, and the analytics tracking id are never included. |
156
- | `diagnostics.json` | Provider and runtime error diagnostics attached to assistant messages across the whole session (failed or aborted turns, retries, error messages), plus any recorded crashes. Always included; message content is not. |
157
- | `session.jsonl` | The current branch of the session, only when you chose to include it. It contains file contents and command output read during the session. |
158
- | `summary.md` | The model-written summary, only when you chose to generate one. |
52
+ Use `--no-session` for an ephemeral run. An ephemeral session cannot be resumed after Pi exits.
159
53
 
160
- Each report has a UUID. pi shows it after upload or export and records it in the session as a `pi.bug-report` entry so you can refer to it later.
54
+ Use `--session` when you already know the session path or ID. Use `--fork` to create a new session from an existing session before interactive mode starts.
161
55
 
162
- Set `PI_RADIUS_GATEWAY` to upload to a different Radius deployment.
56
+ ## Export or share a session
163
57
 
164
- ### Crashes
58
+ Use `/export` to write the current session as HTML or JSONL. Use `/share` to upload it and get a viewer link. Pi uses a Radius artifact when Radius authentication is configured; otherwise, it uses a private GitHub gist.
165
59
 
166
- When pi exits because of an uncaught exception or a fatal runtime error, it stores the error message and stack trace in `~/.pi/agent/crashes.json` (the newest five). The next interactive start shows a warning once; running `/bug` attaches the stored crashes to `diagnostics.json` and removes the file after the report is uploaded or exported. Resume the crashed session with `pi -r` first if you want the transcript in the report.
60
+ Review exported or shared sessions first. They can contain prompts, model responses, tool arguments, command output, file contents, and extension messages.
167
61
 
168
- ## Session Format
62
+ ## Report a bug
169
63
 
170
- Session files are JSONL and contain message entries, model changes, thinking-level changes, labels, compactions, branch summaries, and extension entries.
64
+ Run `/bug [description]` to prepare a private report for the Pi developers. You can include the session transcript, omit it, or ask the current model to summarize the problem. Review any transcript or generated summary because it can contain sensitive conversation data.
171
65
 
172
- For parsers, extensions, SDK usage, and the full SessionManager API, see [Session Format](session-format.md).
66
+ The report includes environment and provider configuration without credential values, plus recorded error diagnostics. Upload it through `radius.pi.dev` or export the same report as a zip to inspect and share yourself. Uploads do not require a login; Radius authentication attributes the report to your account so the developers can follow up. If an upload fails, Pi offers to export the zip.