@zihanw/pi-forge 0.5.4 → 0.5.6

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 (237) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/README.md +67 -103
  3. package/README.zh-CN.md +67 -95
  4. package/assets/pi-forge-header-concept-1.png +0 -0
  5. package/assets/readme/PROVENANCE.md +95 -0
  6. package/assets/readme/en/capability-tools.gif +0 -0
  7. package/assets/readme/en/context-composition.gif +0 -0
  8. package/assets/readme/en/context-toggle.gif +0 -0
  9. package/assets/readme/en/draft-diff.png +0 -0
  10. package/assets/readme/en/edit-draft-diff.gif +0 -0
  11. package/assets/readme/en/editor-overview-v3.png +0 -0
  12. package/assets/readme/en/editor-overview.png +0 -0
  13. package/assets/readme/en/mode-tools.gif +0 -0
  14. package/assets/readme/en/regex-transforms.gif +0 -0
  15. package/assets/readme/en/tool-selection.gif +0 -0
  16. package/assets/readme/tui-quickstart.gif +0 -0
  17. package/assets/readme/zh-CN/capability-tools.gif +0 -0
  18. package/assets/readme/zh-CN/context-composition.gif +0 -0
  19. package/assets/readme/zh-CN/context-toggle.gif +0 -0
  20. package/assets/readme/zh-CN/draft-diff.png +0 -0
  21. package/assets/readme/zh-CN/edit-draft-diff.gif +0 -0
  22. package/assets/readme/zh-CN/editor-overview-v3.png +0 -0
  23. package/assets/readme/zh-CN/editor-overview.png +0 -0
  24. package/assets/readme/zh-CN/mode-tools.gif +0 -0
  25. package/assets/readme/zh-CN/regex-transforms.gif +0 -0
  26. package/assets/readme/zh-CN/tool-selection.gif +0 -0
  27. package/dist/active-state.d.ts +148 -0
  28. package/dist/active-state.d.ts.map +1 -0
  29. package/dist/active-state.js +374 -0
  30. package/dist/active-state.js.map +1 -0
  31. package/dist/agent-profile.d.ts +1 -0
  32. package/dist/agent-profile.d.ts.map +1 -1
  33. package/dist/agent-profile.js +20 -14
  34. package/dist/agent-profile.js.map +1 -1
  35. package/dist/capabilities.d.ts +39 -0
  36. package/dist/capabilities.d.ts.map +1 -0
  37. package/dist/capabilities.js +160 -0
  38. package/dist/capabilities.js.map +1 -0
  39. package/dist/capability-anchors.d.ts +45 -0
  40. package/dist/capability-anchors.d.ts.map +1 -0
  41. package/dist/capability-anchors.js +263 -0
  42. package/dist/capability-anchors.js.map +1 -0
  43. package/dist/capability-command.d.ts +4 -0
  44. package/dist/capability-command.d.ts.map +1 -0
  45. package/dist/capability-command.js +164 -0
  46. package/dist/capability-command.js.map +1 -0
  47. package/dist/capability-events.d.ts +79 -0
  48. package/dist/capability-events.d.ts.map +1 -0
  49. package/dist/capability-events.js +478 -0
  50. package/dist/capability-events.js.map +1 -0
  51. package/dist/capability-projection.d.ts +22 -0
  52. package/dist/capability-projection.d.ts.map +1 -0
  53. package/dist/capability-projection.js +273 -0
  54. package/dist/capability-projection.js.map +1 -0
  55. package/dist/capability-protocol.d.ts +21 -0
  56. package/dist/capability-protocol.d.ts.map +1 -0
  57. package/dist/capability-protocol.js +15 -0
  58. package/dist/capability-protocol.js.map +1 -0
  59. package/dist/capability-state.d.ts +74 -0
  60. package/dist/capability-state.d.ts.map +1 -0
  61. package/dist/capability-state.js +43 -0
  62. package/dist/capability-state.js.map +1 -0
  63. package/dist/capability-tool.d.ts +11 -0
  64. package/dist/capability-tool.d.ts.map +1 -0
  65. package/dist/capability-tool.js +25 -0
  66. package/dist/capability-tool.js.map +1 -0
  67. package/dist/capability-web-host.d.ts +11 -0
  68. package/dist/capability-web-host.d.ts.map +1 -0
  69. package/dist/capability-web-host.js +105 -0
  70. package/dist/capability-web-host.js.map +1 -0
  71. package/dist/codecs/capability.d.ts +71 -0
  72. package/dist/codecs/capability.d.ts.map +1 -0
  73. package/dist/codecs/capability.js +363 -0
  74. package/dist/codecs/capability.js.map +1 -0
  75. package/dist/codecs/prompt-stack.d.ts +1 -1
  76. package/dist/codecs/prompt-stack.d.ts.map +1 -1
  77. package/dist/codecs/prompt-stack.js +140 -13
  78. package/dist/codecs/prompt-stack.js.map +1 -1
  79. package/dist/command-contribution/index.d.ts +21 -0
  80. package/dist/command-contribution/index.d.ts.map +1 -0
  81. package/dist/command-contribution/index.js +14 -0
  82. package/dist/command-contribution/index.js.map +1 -0
  83. package/dist/compile-cycle.d.ts +1 -0
  84. package/dist/compile-cycle.d.ts.map +1 -1
  85. package/dist/compile-cycle.js.map +1 -1
  86. package/dist/compiler.d.ts.map +1 -1
  87. package/dist/compiler.js +81 -17
  88. package/dist/compiler.js.map +1 -1
  89. package/dist/forge-command.d.ts +10 -0
  90. package/dist/forge-command.d.ts.map +1 -0
  91. package/dist/forge-command.js +106 -0
  92. package/dist/forge-command.js.map +1 -0
  93. package/dist/index.d.ts +2 -2
  94. package/dist/index.d.ts.map +1 -1
  95. package/dist/index.js +58 -5
  96. package/dist/index.js.map +1 -1
  97. package/dist/json-fingerprint.d.ts +11 -0
  98. package/dist/json-fingerprint.d.ts.map +1 -0
  99. package/dist/json-fingerprint.js +66 -0
  100. package/dist/json-fingerprint.js.map +1 -0
  101. package/dist/lifecycle.d.ts +14 -0
  102. package/dist/lifecycle.d.ts.map +1 -1
  103. package/dist/lifecycle.js +167 -67
  104. package/dist/lifecycle.js.map +1 -1
  105. package/dist/payload-command.d.ts +2 -2
  106. package/dist/payload-command.d.ts.map +1 -1
  107. package/dist/payload-command.js +80 -26
  108. package/dist/payload-command.js.map +1 -1
  109. package/dist/payload-state.d.ts +1 -0
  110. package/dist/payload-state.d.ts.map +1 -1
  111. package/dist/payload-state.js +1 -0
  112. package/dist/payload-state.js.map +1 -1
  113. package/dist/policy.d.ts +2 -1
  114. package/dist/policy.d.ts.map +1 -1
  115. package/dist/policy.js +3 -0
  116. package/dist/policy.js.map +1 -1
  117. package/dist/preset-command.d.ts +1 -0
  118. package/dist/preset-command.d.ts.map +1 -1
  119. package/dist/preset-command.js +94 -36
  120. package/dist/preset-command.js.map +1 -1
  121. package/dist/preview-text.d.ts +5 -0
  122. package/dist/preview-text.d.ts.map +1 -0
  123. package/dist/preview-text.js +27 -0
  124. package/dist/preview-text.js.map +1 -0
  125. package/dist/preview.d.ts +18 -1
  126. package/dist/preview.d.ts.map +1 -1
  127. package/dist/preview.js +132 -97
  128. package/dist/preview.js.map +1 -1
  129. package/dist/profile-command.js +68 -9
  130. package/dist/profile-command.js.map +1 -1
  131. package/dist/regex.d.ts.map +1 -1
  132. package/dist/regex.js +5 -0
  133. package/dist/regex.js.map +1 -1
  134. package/dist/render-helpers.d.ts.map +1 -1
  135. package/dist/render-helpers.js +2 -0
  136. package/dist/render-helpers.js.map +1 -1
  137. package/dist/repositories/capability.d.ts +53 -0
  138. package/dist/repositories/capability.d.ts.map +1 -0
  139. package/dist/repositories/capability.js +294 -0
  140. package/dist/repositories/capability.js.map +1 -0
  141. package/dist/runtime/capability-runtime.d.ts +93 -0
  142. package/dist/runtime/capability-runtime.d.ts.map +1 -0
  143. package/dist/runtime/capability-runtime.js +987 -0
  144. package/dist/runtime/capability-runtime.js.map +1 -0
  145. package/dist/runtime/profile-runtime.d.ts.map +1 -1
  146. package/dist/runtime/profile-runtime.js +10 -9
  147. package/dist/runtime/profile-runtime.js.map +1 -1
  148. package/dist/runtime/prompt-stack-runtime.d.ts +7 -4
  149. package/dist/runtime/prompt-stack-runtime.d.ts.map +1 -1
  150. package/dist/runtime/prompt-stack-runtime.js +47 -11
  151. package/dist/runtime/prompt-stack-runtime.js.map +1 -1
  152. package/dist/runtime/tool-policy-runtime.d.ts +10 -0
  153. package/dist/runtime/tool-policy-runtime.d.ts.map +1 -1
  154. package/dist/runtime/tool-policy-runtime.js +171 -30
  155. package/dist/runtime/tool-policy-runtime.js.map +1 -1
  156. package/dist/session-adapter.d.ts +13 -0
  157. package/dist/session-adapter.d.ts.map +1 -1
  158. package/dist/session-adapter.js +110 -0
  159. package/dist/session-adapter.js.map +1 -1
  160. package/dist/session-usage.d.ts +65 -0
  161. package/dist/session-usage.d.ts.map +1 -0
  162. package/dist/session-usage.js +134 -0
  163. package/dist/session-usage.js.map +1 -0
  164. package/dist/subagent/fingerprints.d.ts +3 -15
  165. package/dist/subagent/fingerprints.d.ts.map +1 -1
  166. package/dist/subagent/fingerprints.js +5 -69
  167. package/dist/subagent/fingerprints.js.map +1 -1
  168. package/dist/subagent/index.d.ts +2 -0
  169. package/dist/subagent/index.d.ts.map +1 -1
  170. package/dist/subagent/index.js +2 -0
  171. package/dist/subagent/index.js.map +1 -1
  172. package/dist/subagent-host.d.ts +2 -2
  173. package/dist/subagent-host.d.ts.map +1 -1
  174. package/dist/subagent-host.js +13 -1
  175. package/dist/subagent-host.js.map +1 -1
  176. package/dist/types.d.ts +6 -1
  177. package/dist/types.d.ts.map +1 -1
  178. package/dist/types.js.map +1 -1
  179. package/dist/web-editor/client-script.generated.d.ts.map +1 -1
  180. package/dist/web-editor/client-script.generated.js +1 -1
  181. package/dist/web-editor/client-script.generated.js.map +1 -1
  182. package/dist/web-editor/client-styles.generated.d.ts.map +1 -1
  183. package/dist/web-editor/client-styles.generated.js +1 -1
  184. package/dist/web-editor/client-styles.generated.js.map +1 -1
  185. package/dist/web-editor/server.d.ts.map +1 -1
  186. package/dist/web-editor/server.js +152 -4
  187. package/dist/web-editor/server.js.map +1 -1
  188. package/dist/web-editor/styles.d.ts.map +1 -1
  189. package/dist/web-editor/styles.js +573 -165
  190. package/dist/web-editor/styles.js.map +1 -1
  191. package/dist/web-editor/types.d.ts +81 -1
  192. package/dist/web-editor/types.d.ts.map +1 -1
  193. package/dist/web-host.d.ts +9 -0
  194. package/dist/web-host.d.ts.map +1 -1
  195. package/dist/web-host.js +210 -11
  196. package/dist/web-host.js.map +1 -1
  197. package/dist/workspace.d.ts +11 -0
  198. package/dist/workspace.d.ts.map +1 -1
  199. package/dist/workspace.js +63 -5
  200. package/dist/workspace.js.map +1 -1
  201. package/docs/README.md +4 -0
  202. package/docs/design/README.md +3 -1
  203. package/docs/design/architecture-0.5.md +22 -0
  204. package/docs/design/archive/2026-09-12-system-update-design.md +229 -0
  205. package/docs/design/pi-forge-system-update-design-notes.md +157 -229
  206. package/docs/development/release.md +20 -20
  207. package/docs/development/roadmap.md +21 -2
  208. package/docs/development/scoped-global-profiles-stacks.md +1 -1
  209. package/docs/development/setup.md +1 -1
  210. package/docs/getting-started.md +1 -1
  211. package/docs/guides/delegation.md +22 -20
  212. package/docs/guides/migrating-to-0.5.md +29 -0
  213. package/docs/guides/use-cases.md +6 -2
  214. package/docs/guides/web-editor.md +74 -5
  215. package/docs/reference/active-state.md +77 -0
  216. package/docs/reference/capabilities.md +259 -0
  217. package/docs/reference/commands.md +52 -24
  218. package/docs/reference/configuration.md +4 -2
  219. package/docs/reference/features.md +70 -4
  220. package/docs/reference/provider-support.md +67 -0
  221. package/docs/reference/public-api.md +49 -3
  222. package/docs/reference/session-cache.md +112 -0
  223. package/docs/reference/stack-schema.md +18 -4
  224. package/docs/reference/subagent-host-port.md +8 -0
  225. package/docs/zh-CN/README.md +3 -0
  226. package/docs/zh-CN/getting-started.md +1 -1
  227. package/docs/zh-CN/guides/delegation.md +22 -10
  228. package/docs/zh-CN/guides/migrating-to-0.5.md +29 -0
  229. package/docs/zh-CN/guides/web-editor.md +75 -7
  230. package/docs/zh-CN/reference/capabilities.md +259 -0
  231. package/docs/zh-CN/reference/commands.md +61 -33
  232. package/docs/zh-CN/reference/provider-support.md +67 -0
  233. package/docs/zh-CN/reference/session-cache.md +112 -0
  234. package/examples/capabilities/review.json +12 -0
  235. package/examples/capabilities/write-tools.json +15 -0
  236. package/examples/read-first-worker-prompt-stack.json +49 -0
  237. package/package.json +16 -9
@@ -0,0 +1,67 @@
1
+ # Provider support for mid-conversation updates (Pi 0.87.1 snapshot)
2
+
3
+ [Documentation](../README.md) · [中文](../zh-CN/reference/provider-support.md)
4
+
5
+ This page records how Pi 0.87.1 sends mid-conversation system updates and tool changes for each API, and which models in the Pi model catalog were flagged for them. It explains why a [capability](capabilities.md#delivery-models-native-vs-fallback) may arrive as a native system update on one model and as a labeled user message on another.
6
+
7
+ > **Snapshot status:** Pi 0.87.1 (`@earendil-works/pi-ai` 0.87.1), local model catalog last checked 2026-09-21 to 2026-09-25, recorded 2026-09-26. Pi fetches the model catalog remotely and caches it in `~/.pi/agent/models-store.json`, so flags can change without a Pi upgrade. Run `/capability status` to see which path the current model uses before relying on this table.
8
+
9
+ ## How the choice is made
10
+
11
+ Pi and Forge both read the current model's `compat` flags on every request:
12
+
13
+ | Flag | Effect when `true` | Effect when absent or `false` |
14
+ |---|---|---|
15
+ | `supportsMidConvoSystemMessages` | Later system messages stay at their position in the conversation. Forge sends instruction text as native system sections. | Pi folds all system messages into the leading system prompt and sends the current tool list at request level. Forge sends instruction text as a labeled `[pi-forge capability update]` user message. |
16
+ | `supportsMidConvoToolChanges` (Anthropic Messages) | Tool additions and removals are sent as `tool_addition` / `tool_removal` blocks inside the system update. | The whole current tool list is sent at request level. |
17
+ | `supportsAdditionalTools` / `supportsToolSearch` (OpenAI Responses, Codex, Azure) | New tools are loaded in place (`additional_tools`, or a client-side tool search call and output). | The whole current tool list is sent at request level. |
18
+ | `supportsMidConvoToolAdditions` (OpenAI Completions) | New tools are loaded in place by a system message that carries `tools`. | The whole current tool list is sent at request level. |
19
+
20
+ Tool flags only apply when `supportsMidConvoSystemMessages` is also enabled. The provider name, the authentication method, and auth extensions do not decide the path. The same model can be flagged differently under different providers.
21
+
22
+ ## Behavior by API
23
+
24
+ | API | Instruction text | Tool changes |
25
+ |---|---|---|
26
+ | `anthropic-messages` | With the flag: a `role: "system"` message. Pi holds it until just before the next assistant message, so it never separates a `tool_use` from its `tool_result`; an update recorded before a user message is sent after it. | With `supportsMidConvoToolChanges`, at least one initial tool, and no same-name redefinition: initial tools stay first, later tools are appended with `defer_loading`, and changes are `tool_addition` / `tool_removal` blocks. The request-level list only grows. Otherwise: the current list at request level. |
27
+ | `openai-responses`, `openai-codex-responses`, `azure-openai-responses` | With the flag: a `developer` message (reasoning models that accept the developer role) or a `system` message, at its position. | Additions load in place while retained history contains only additions. Any removal or same-name redeclaration anywhere in retained history switches that request to the full current list at request level. |
28
+ | `openai-completions` | With the flag: a `developer` or `system` message at its position. | Additions load in place with `supportsMidConvoToolAdditions`; removals and redeclarations switch to the full current list. |
29
+ | `mistral-conversations` | With the flag: a `system` message at its position. | Always the full current list at request level. |
30
+ | Google Generative AI / Vertex | Always folded into `systemInstruction`; no mid-conversation path. | Full current list. |
31
+ | Bedrock Converse | Always folded into the request-level system prompt. | Full current tool configuration. |
32
+ | `pi-messages` | Passes the context to its backend unchanged; behavior depends on that backend. | Backend-defined. |
33
+
34
+ Updates are rendered as `Updated system prompt section "<name>": ...` or `Removed system prompt section "<name>".`. [Tool-only capabilities](capabilities.md#delivery-models-native-vs-fallback) send no text update.
35
+
36
+ ## Flagged models in the 2026-09 catalog
37
+
38
+ Only models with `supportsMidConvoSystemMessages: true` are listed.
39
+
40
+ | Provider | Models | Tool changes |
41
+ |---|---|---|
42
+ | `anthropic` | `claude-fable-5`, `claude-fable-5-1`, `claude-opus-4-8`, `claude-opus-5`, `claude-opus-5-5` | Native additions and removals |
43
+ | `openai-codex` | `gpt-5.6-luna`, `gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-6-astra`, `gpt-6-luna`, `gpt-6-sol` | Additions in place (`additional_tools`) |
44
+ | `openai-codex` | `gpt-5.5` | Additions in place via tool search |
45
+ | `opencode` | `gpt-5.4`, `gpt-5.4-mini`, `gpt-5.4-pro`, `gpt-5.5`, `gpt-5.6-luna`, `gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-6-astra` | Additions in place (`additional_tools`) |
46
+ | `opencode` | `claude-fable-5`, `claude-fable-5-1`, `claude-opus-4-8`, `claude-opus-5` | Text only; any tool change sends the full list |
47
+ | `opencode`, `opencode-go` | `kimi-k3` | Additions in place |
48
+ | `opencode-go` | `gpt-5.6-luna` | Additions in place (`additional_tools`) |
49
+ | `deepseek` | `deepseek-v4-pro` | Text only; any tool change sends the full list |
50
+
51
+ Not flagged in the same catalog, among others: `anthropic/claude-sonnet-5`, `claude-sonnet-4-5`, `claude-sonnet-4-6`, `claude-opus-4-5` to `claude-opus-4-7`, `claude-haiku-4-5`; `openai-codex/gpt-5.3-codex-spark`; `deepseek/deepseek-flash`; every `google` and `kimi-coding` model.
52
+
53
+ ## Observed cache behavior
54
+
55
+ These are single-session observations, not guarantees. Cache reuse is decided by the provider.
56
+
57
+ - **`anthropic/claude-opus-5-5`, native path (OAuth through an auth extension):** adding, removing, and re-adding tools, including a capability the model enabled itself, kept the full cached prefix on all 18 follow-up requests. Requests with lower hit rates were writing new content, such as new tool definitions or large file reads, not losing earlier cache.
58
+ - **`anthropic/claude-sonnet-5`, fallback path:** the request after a tool change read nothing from cache, because Pi rewrote the leading system prompt and tool list. The model also questioned the labeled user update before using the new tool.
59
+ - **OpenAI Responses / Codex:** in earlier tests, a removal switched to the full tool list and cache reads dropped to zero; requests with only additions kept the prefix.
60
+
61
+ To keep caches stable, prefer a flagged model with native tool changes. On Responses-family models, avoid removing tools in a session where cache reuse matters.
62
+
63
+ ## Checking your own setup
64
+
65
+ 1. Run `/capability status`. It reports `native system sections` or `attributed user updates` for the current model.
66
+ 2. To see the flags directly, look up the model under its provider in `~/.pi/agent/models-store.json` and read its `compat` object.
67
+ 3. Use `/forge payload next` to capture the actual next request if you need to confirm the wire format.
@@ -2,9 +2,9 @@
2
2
 
3
3
  [Documentation](../README.md)
4
4
 
5
- pi-forge is pre-1.0. This document defines the intentional integration surfaces of the 0.5.0 line.
5
+ pi-forge is pre-1.0. This document defines the intentional integration surfaces of the 0.5 development line.
6
6
 
7
- ## The four intentional entry points
7
+ ## The five intentional entry points
8
8
 
9
9
  `check-package` enforces this allowlist; nothing else is importable from the package.
10
10
 
@@ -60,6 +60,24 @@ The experimental host port over the Pi event bus: discovery, profile listing/sna
60
60
 
61
61
  The optional `@zihanw/pi-forge-subagents` package consumes this port and owns subagent execution and configuration.
62
62
 
63
+ #### Experimental nested-usage contract exports
64
+
65
+ The entry point also exports helper constants, validators, and types for tools and subagents reporting inner model usage under `toolResult.details[FORGE_NESTED_USAGE_KEY]`:
66
+
67
+ ```ts
68
+ import {
69
+ FORGE_NESTED_USAGE_KEY,
70
+ parseForgeNestedUsage,
71
+ type ForgeNestedUsage,
72
+ } from "@zihanw/pi-forge/subagent";
73
+ ```
74
+
75
+ - `FORGE_NESTED_USAGE_KEY`: constant string key (`"forgeNestedUsage"`).
76
+ - `parseForgeNestedUsage(value: unknown): ForgeNestedUsage | undefined`: strict schema parser that returns typed usage or `undefined` for malformed objects.
77
+ - `type ForgeNestedUsage`: typed schemaVersion 1 contract (`schemaVersion`, `requests`, `input`, `output`, optional `cacheRead` and `cacheWrite`).
78
+
79
+ See the [session cache usage reference](session-cache.md) for full contract rules, ingestion boundaries, and hit-rate aggregation semantics.
80
+
63
81
  ### 4. `@zihanw/pi-forge/ui-contribution`: versioned settings port
64
82
 
65
83
  ```ts
@@ -72,10 +90,38 @@ import {
72
90
 
73
91
  The experimental generic Settings integration surface. Optional packages contribute recursively validated, JSON-compatible schemas and values over the Pi event bus; pi-forge owns only the renderer and web proxy. Providers own validation and persistence, may resolve operations asynchronously, and receive an abort signal tied to provider generation so stale requests can stop before side effects. The full contract is documented in the [UI contribution port reference](ui-contribution-port.md).
74
92
 
93
+ ### 5. `@zihanw/pi-forge/command-contribution`: Forge child-command contribution
94
+
95
+ ```ts
96
+ import {
97
+ FORGE_COMMAND_DISCOVERY_EVENT,
98
+ contributeForgeCommand,
99
+ } from "@zihanw/pi-forge/command-contribution";
100
+ import type {
101
+ ForgeCommandContribution,
102
+ ForgeCommandDiscovery,
103
+ ForgeCommandEvents,
104
+ } from "@zihanw/pi-forge/command-contribution";
105
+ ```
106
+
107
+ This is a local, synchronous discovery surface for contributing one child command below the main `/forge` root. A contribution supplies callback metadata (`name`, `description`, `handler`, and optional argument completions); `contributeForgeCommand` returns an explicit unsubscribe function. Discovery is an in-process callback exchange, not an RPC, authorization boundary, execution sandbox, singleton registry, or persistent format. The main package owns the sole `/forge` root. Reserved names are rejected and duplicate child contributors fail closed rather than being selected arbitrarily.
108
+
109
+ The optional subagent package can use this surface for `/forge subagent plan`, while its backend policy remains separate: `/forge-agent run` retains mandatory human approval and a selected backend may write; model-callable `forge_subagent` unattended authorization is a different path.
110
+
111
+ Forge 0.5.5 provides this main-package subpath. The optional package remains an independent unfinished release and must raise its Forge dependency floor to `^0.5.5` before consuming it as a paired release; this documentation does not claim optional-package publication or compatibility with an older main package.
112
+
113
+ ## Event bus contracts (no import entry point)
114
+
115
+ Optional cosmetic consumers integrate over the Pi event bus instead of importing the package:
116
+
117
+ - Active-state snapshot/change uses `@zihanw/pi-forge/active-state/v1` and `@zihanw/pi-forge/active-state/request/v1`. See the [active-state bus contract](active-state.md).
118
+
119
+ These channels carry only plain JSON scalars. They are not `@zihanw/pi-forge` import surfaces and consumers must not depend on package internals.
120
+
75
121
  ## Compatibility policy
76
122
 
77
123
  - **Stable** surfaces (root factory, macro/slot registration) preserve source compatibility within the documented release range unless a changelog entry announces a breaking release.
78
- - **Experimental** surfaces (the `/subagent` and `/ui-contribution` ports) are typed, tested, and documented, but may change deliberately as integration experience exposes missing semantics.
124
+ - **Experimental** surfaces (the `/subagent`, `/ui-contribution`, and `/command-contribution` ports) are typed and documented, but may change deliberately as integration experience exposes missing semantics. Forge 0.5.5 provides the command-contribution surface; optional consumers remain pending their independent version/floor bump and release.
79
125
  - Everything not listed above is internal and may change without notice. In particular: no `src/*` subpath aliases exist, `./examples/*` is not an import surface (examples ship as browsable files), and removed 0.4 surfaces (the execution contract re-exports, loader/profile/catalog helpers) now live either nowhere or in `@zihanw/pi-forge-subagents`.
80
126
 
81
127
  ## Removed in 0.5.0
@@ -0,0 +1,112 @@
1
+ # Session cache usage
2
+
3
+ [Documentation](../README.md) · [简体中文](../zh-CN/reference/session-cache.md)
4
+
5
+ Available in Forge 0.5.5. The nested-usage contract is experimental; producer integration is owned and released independently by optional tools. Forge tracks and summarizes read-only prompt-cache metrics for the active session branch. These metrics appear in the Web editor's **Current session** capabilities panel and are exposed in the capability runtime state.
6
+
7
+ ## Read-only architecture
8
+
9
+ Cache usage metrics are computed on demand from provider-reported usage that Pi has already persisted in messages along the active branch:
10
+
11
+ - **Zero side effects:** Forge never initiates model inference, appends session entries, edits prompt text, inserts `cache_control` breakpoints, or triggers prompt warming for usage tracking.
12
+ - **Reused polling:** The Web client receives cache metrics through its existing visibility-based polling (`GET /api/capability-state`); no extra network requests or timers are introduced.
13
+ - **Root-to-leaf branch traversal:** Metrics reflect the active branch from root to leaf.
14
+ - **Main session requests:** Counted from `assistant` messages carrying non-zero persisted usage (`input + output + cacheRead + cacheWrite > 0`). Requests with zero reported usage (such as aborted turns or failed calls without provider usage) are excluded.
15
+ - **Latest reported request:** The latest assistant message with non-zero usage; a later aborted or unreported request does not replace it.
16
+ - **Current turn:** Aggregates assistant requests appearing after the latest `user` message on the branch. Tool loops within the turn aggregate together.
17
+ - **Session scope:** Aggregates all qualifying requests on the active branch, including pre-compaction conversation history that remains on the branch.
18
+ - **Exclusions and persistence boundaries:**
19
+ - Excludes standalone Pi `usage` events, compaction entries, and `branch_summary` records.
20
+ - Pi can persist warming usage separately; these metrics intentionally count conversational requests, not warming or summarization overhead.
21
+ - Not equivalent to Pi's overall session billing or total bill statistics.
22
+
23
+ ## Hit rate formula and aggregation
24
+
25
+ ### Hit rate formula
26
+
27
+ Prompt cache hit rate measures prompt prefix reuse:
28
+
29
+ ```
30
+ cacheHitRate = cacheRead / (input + cacheRead + cacheWrite)
31
+ ```
32
+
33
+ - `input`: normalized uncached prompt tokens, following the `pi-ai` `Usage` convention.
34
+ - `cacheRead`: prompt tokens served from the provider prompt cache.
35
+ - `cacheWrite`: prompt tokens written to provider prompt cache checkpoints.
36
+ - `output`: model completion tokens are strictly excluded from the prompt cache hit rate denominator.
37
+ - **Undefined denominator:** When total prompt tokens (`input + cacheRead + cacheWrite`) equal zero, the hit rate is undefined and displayed as an em dash (`—`).
38
+
39
+ ### Aggregation semantics
40
+
41
+ - **Mixed models:** When multiple models run on the same branch or turn, rates are aggregated by summing raw token counts across requests (sum of cache reads / sum of prompt tokens), never as an arithmetic mean of percentages.
42
+ - **Provider reporting caveats:** Zero reported cache counts (`cacheRead: 0`, `cacheWrite: 0`) can indicate that the provider does not report cache metrics. They do not prove that prompt caching is unsupported or that no caching occurred.
43
+
44
+ ## Main session vs. nested tool separation
45
+
46
+ Usage is tracked in two distinct streams:
47
+ 1. **Main (`main`):** Direct assistant turns initiated by the primary session model.
48
+ 2. **Nested (`nested`):** Usage reported by tools that execute model requests internally (such as subagents). Nested usage is never merged into `main`.
49
+
50
+ In the Web UI:
51
+ - Main session and nested tools are displayed in separate visible rows (**Cache hit** and **Nested tools**).
52
+ - Tooltips display combined totals for **known** data only, with a permanent note that absent, invalid, or cache-unknown reports are excluded. The combined value is not a complete bill.
53
+
54
+ ## Experimental nested-usage contract (`toolResult.details.forgeNestedUsage`)
55
+
56
+ Tools that make inner model calls can report aggregate token and cache metrics by attaching an object to `toolResult.details.forgeNestedUsage` (`FORGE_NESTED_USAGE_KEY`).
57
+
58
+ ### Typed import
59
+
60
+ ```ts
61
+ import {
62
+ FORGE_NESTED_USAGE_KEY,
63
+ parseForgeNestedUsage,
64
+ type ForgeNestedUsage,
65
+ } from "@zihanw/pi-forge/subagent";
66
+ ```
67
+
68
+ ### JSON payload example
69
+
70
+ ```json
71
+ {
72
+ "role": "toolResult",
73
+ "toolCallId": "call_subagent_abc123",
74
+ "content": [{ "type": "text", "text": "Subagent task completed." }],
75
+ "details": {
76
+ "forgeNestedUsage": {
77
+ "schemaVersion": 1,
78
+ "requests": 2,
79
+ "input": 1500,
80
+ "output": 420,
81
+ "cacheRead": 3200,
82
+ "cacheWrite": 0
83
+ }
84
+ }
85
+ }
86
+ ```
87
+
88
+ ### Contract specification
89
+
90
+ | Field | Type | Required | Notes |
91
+ |---|---|---|---|
92
+ | `schemaVersion` | `1` | Yes | Contract version. Must be literal `1`. |
93
+ | `requests` | `number` | Yes | Non-negative safe integer. Total model requests executed during the tool invocation. `0` is allowed **only** when all supplied token counts are zero. |
94
+ | `input` | `number` | Yes | Non-negative safe integer. Normalized uncached input tokens. |
95
+ | `output` | `number` | Yes | Non-negative safe integer. Generated completion tokens. |
96
+ | `cacheRead` | `number` | Optional | Non-negative safe integer. Prompt tokens read from cache. Must be paired with `cacheWrite`. |
97
+ | `cacheWrite` | `number` | Optional | Non-negative safe integer. Prompt tokens written to cache. Must be paired with `cacheRead`. |
98
+
99
+ - **Strict schema:** No extra keys are permitted.
100
+ - **Cache pair rule:** `cacheRead` and `cacheWrite` must either both be present or both omitted. Omit both when the producer cannot provide cache numbers.
101
+ - **Invocation aggregation:** A final tool result must supply exactly **one** aggregate per tool invocation covering all its internal model requests. Intermediate progress snapshots or repeated lifetime totals across calls are prohibited.
102
+ - **Rollup ownership:** The tool producer owns deduplication and recursive rollup across child agents. Nested totals must not include main session tokens.
103
+ - **Ingestion and error handling:**
104
+ - **Unknown cache metrics:** Valid records omitting `cacheRead`/`cacheWrite` are excluded from all request and token totals; the call is counted in `cacheUnknownCalls` (`N calls reported no cache data`).
105
+ - **Malformed records:** Payloads failing schema validation are ignored; the call is counted in `invalidCalls` (`N malformed reports ignored`).
106
+ - **Absent key:** When `forgeNestedUsage` is absent, the tool call is treated as having no nested usage. Forge does not mine legacy output or parse logs.
107
+
108
+ ### Attribution and Pi integration boundary
109
+
110
+ - **Forge attribution only:** `toolResult.details.forgeNestedUsage` is used solely for Forge UI attribution and cache tracking. It does **not** modify Pi's built-in session totals.
111
+ - **No duplicate counting:** Pi supports `toolResult.usage` natively. A future producer can emit the same underlying usage to `toolResult.usage` for Pi's session totals and to `toolResult.details.forgeNestedUsage` for Forge's cache display. Forge does not re-add top-level `toolResult.usage`, preventing duplication.
112
+ - **Ecosystem status:** Runtime and subagent producer integration is deferred. Existing subagent calls without this field display no data; no historical backfill is performed.
@@ -47,7 +47,7 @@ Item IDs must be unique. Unsupported slots and missing required custom registrat
47
47
 
48
48
  Item position only matters within each channel: all `system` items join the system prompt in their relative order, and all non-system items become messages in their relative order. A `system` item placed after non-system items therefore has no effect on placement and produces a validation warning; roles are never silently converted. Use a `user` item for in-conversation injection.
49
49
 
50
- ## Modes
50
+ ## Capabilities
51
51
 
52
52
  - `replace` replaces Pi's base system prompt; empty output falls back to the base.
53
53
  - `append` places stack system text after Pi's base.
@@ -76,7 +76,7 @@ Item position only matters within each channel: all `system` items join the syst
76
76
  "includeLastUserMessage": false,
77
77
  "stripAssistantThinking": true,
78
78
  "includeSummaries": true,
79
- "toolMode": "keep",
79
+ "toolCapability": "keep",
80
80
  "roles": ["user", "assistant"],
81
81
  "maxMessages": 40,
82
82
  "maxChars": 20000
@@ -87,7 +87,7 @@ Item position only matters within each channel: all `system` items join the syst
87
87
  - `stripAssistantThinking` removes prior thinking blocks but preserves visible assistant text, tool calls, and results. It does not change the live loop or stored transcript.
88
88
  - `includeSummaries: false` excludes branch/compaction summaries.
89
89
  - `roles` keeps only selected roles.
90
- - `toolMode: "drop"` removes prior tool traffic.
90
+ - `toolCapability: "drop"` removes prior tool traffic.
91
91
  - `maxMessages` and `maxChars` keep recent history within limits.
92
92
 
93
93
  When filtering would separate a tool call from its result, pi-forge removes dangling entries rather than sending inconsistent provider history.
@@ -105,7 +105,8 @@ Patterns are exact by default and support `*` wildcards:
105
105
  ```json
106
106
  {
107
107
  "tools": {
108
- "allow": ["read", "grep", "find", "ls"]
108
+ "allow": ["read", "grep", "find", "ls"],
109
+ "initial": ["read", "grep"]
109
110
  },
110
111
  "skills": {
111
112
  "deny": ["browser-danger"]
@@ -115,6 +116,19 @@ Patterns are exact by default and support `*` wildcards:
115
116
 
116
117
  Each resource may have a non-empty `allow` list or `deny` list, never both. A selective tool `allow` list chooses matching tools from Pi's complete registered tool catalog, so it can activate a registered tool that was inactive when the stack was selected. A tool `deny` list removes matching tools from the active baseline. `allow: ["*"]` remains unrestricted and does not activate every registered tool. Unmatched allow patterns are surfaced during validation/preflight.
117
118
 
119
+ ### Initial active tools (`tools.initial`)
120
+
121
+ The `tools` policy optionally accepts an `initial` list:
122
+
123
+ - **Concrete names only:** `tools.initial` must be an array of valid, concrete tool name strings (up to 128 characters each). Wildcards (`*`, `?`) are rejected; duplicate names produce a warning and are deduplicated on parsing.
124
+ - **Omission vs. empty array:**
125
+ - When `initial` is omitted, pi-forge preserves legacy behavior: a selective allow chooses matching registered tools; unrestricted/deny policies retain or filter the restorable session baseline, not the entire catalog.
126
+ - When `initial: []` is set explicitly, zero tools are active initially.
127
+ - **Ceiling enforcement:** The `allow`/`deny` ceiling remains authoritative and exclusive. Initial tools must fall within permitted bounds: listing a tool that is blocked by allow/deny produces a validation error.
128
+ - **Extension and mod tools:** Dynamically registered extension tools are allowed by default if they satisfy the allow/deny policy, but when `initial` is specified, they remain registered and inactive until explicitly added by `initial`, by an active capability, or through runtime tooling.
129
+ - **Preset baseline behavior:** Configured initial tools serve as the active base for as long as the preset remains active—not a one-time reset per turn. Calling `/capability disable` or `/capability reset` returns the session to the preset defaults (plus any remaining active capabilities). Disabling restores the reconciled session baseline; switching recomputes under the new Preset and remaining unbound capabilities.
130
+ - **Compatibility:** Stacks declaring `tools.initial` require Forge 0.5.5 or newer. Older Forge versions may ignore `tools.initial` and revert to legacy selection behavior (selective allow selects catalog matches; unrestricted/deny retains or filters the session baseline), so the field is not downgrade-compatible. The host requirement remains Pi `>=0.87.0 <0.88.0`.
131
+
118
132
  Tool policy changes Pi's active tool list, is reasserted before input/turns, and has a tool-call guard. It preserves external additions in the restorable baseline and restores that baseline when policy no longer applies or the extension shuts down.
119
133
 
120
134
  Skill policy filters only pi-forge-rendered skill slots. It does not disable explicit invocation and is not a capability boundary. `append`/`prepend` may retain Pi's unfiltered base skill text, so validation warns.
@@ -40,6 +40,14 @@ Request: `{ profile: string }` — a scoped selector (`reviewer`, `project:revie
40
40
 
41
41
  Request: `{ profile, task: { text }, access: ForgePromptAccessFacts, backend: ForgeBackendFacts }`. The workspace resolves the profile and stack from its snapshot, filters the client tool catalog through stack tool policy and the access facts, and compiles through the same compilation context as runtime and preview. Response: `{ profileId, model, thinkingLevel, systemPrompt, messages, effectiveToolIds, effectiveToolNames, diagnostics, profileSnapshot, preparedAt }`. `messages` ends with the protected delegated task (`protectedTask: true`, `source: "delegated-task"`); stack-compiled messages carry `source: "prompt-stack"`. The base system prompt is host-owned and intentionally empty for delegated subagents — the prompt stack composes the system prompt.
42
42
 
43
+ ## Tool-selection compatibility
44
+
45
+ For Presets containing `tools.initial`, both the host and the optional package must understand the field. The host filters the registered backend catalog to those concrete names (including an explicitly empty set), applies the existing allow/deny ceiling, then applies request access. The optional package independently recomputes this selection when validating the execution plan; `plan.tool-negotiation` must continue to reject disagreement. Omitting `initial` preserves legacy selection.
46
+
47
+ Forge 0.5.5 provides the host-side `tools.initial` support. Published `pi-forge-subagents` 0.5.3 is not compatible with `tools.initial`: it ignores the field during its independent negotiation. Use matching local optional-package checkouts until the optional package raises its Forge dependency floor to `^0.5.5` and completes its own release; the existing broad package dependency range is not a feature-compatibility guarantee.
48
+
49
+ This is an optional-package release gate, not a gate on the main Forge 0.5.5 release. The optional package must update its own manifests, runtime sequencing, backend/continuation coverage, lockfiles, and cross-package packed tests through its separately authorized process. No optional package or runtime publication is claimed here.
50
+
43
51
  ## Fingerprints
44
52
 
45
53
  `canonicalSubagentJson`, `subagentFingerprint`, `subagentSourceProfileFingerprint`, `subagentPromptStackFingerprint`, and `SUBAGENT_FINGERPRINT_PREFIX` are Forge-owned and vendored in the main package; golden vectors pin byte compatibility with the runtime's canonical serialization. Conversation and execution fingerprints are never host-computed — they are issued by `@zihanw/pi-subagent-runtime` during plan sealing in the optional package.
@@ -23,6 +23,9 @@
23
23
  ## 参考
24
24
 
25
25
  - [命令参考](reference/commands.md)
26
+ - [能力](reference/capabilities.md)
27
+ - [会话缓存用量](reference/session-cache.md):提示词缓存命中率与嵌套工具用量契约
28
+ - [各服务商对会话中更新的支持情况](reference/provider-support.md):Pi 0.87.1 快照
26
29
  - [Stack schema 与策略(英文)](../reference/stack-schema.md)
27
30
  - [Macros 与 slots(英文)](../reference/macros-and-slots.md)
28
31
  - [配置(英文)](../reference/configuration.md)
@@ -21,7 +21,7 @@ mkdir -p .pi/forge/prompt-stacks
21
21
  cp examples/default-prompt-stack.json .pi/forge/prompt-stacks/default.json
22
22
  ```
23
23
 
24
- 如果不是在仓库 clone 中,可以打开 `/preset ui` 新建 stack;编辑器使用同样的默认布局。
24
+ 无需复制文件,也可以打开 `/forge ui` 新建预设;Forge 0.5.5 支持选择默认 Pi 提示词镜像、空白预设(Pi 仍保留基础提示词与历史)或极简工作者模板(仅限 `bash` 与 `edit`)。
25
25
 
26
26
  ```text
27
27
  /preset reload
@@ -4,7 +4,10 @@
4
4
 
5
5
  > **实验性:** 此 API 和 backend 可能独立于稳定的 prompt stack/profile 功能发生变化。
6
6
 
7
- 可选包 `@zihanw/pi-forge-subagents` 可以把明确授权的 agent profile 作为独立、干净、一次性的 Pi 进程执行。它在前台运行,并向父对话返回有界报告。
7
+ 可选包 `@zihanw/pi-forge-subagents` 会通过选定的 backend 执行明确授权的 agent profile。默认只读 backend 使用独立、干净、一次性的 Pi 子进程;可写 backend 的边界见下文。本文档流程在前台运行,并向父对话返回有界报告。
8
+
9
+ > **默认工具兼容要求:** Forge 0.5.5 主包已提供 `tools.initial`;独立发布的可选 subagents 0.5.3 不兼容该字段。在可选包将 Forge floor 提升到 `^0.5.5` 并单独发布修复前,请使用匹配的本地源码;版本与发布门槛见[工具选择兼容说明(英文)](../../reference/subagent-host-port.md#tool-selection-compatibility)。这项可选包兼容工作不是主包 release gate。
10
+
8
11
 
9
12
  ## 启用 profile
10
13
 
@@ -33,11 +36,20 @@ Profile 默认不能委派。请在可信项目的 `.pi/forge/subagents.json`
33
36
  /forge-agent run reviewer 检查这个 API 设计。
34
37
  ```
35
38
 
36
- `plan` 会解析 profile/stack、编译并校验不可变的实际 provider-bound 计划,然后在不联系 provider 的情况下丢弃。Profile selector 在所有入口使用同一语法:`reviewer`(项目优先)、`project:reviewer` 或 `global:reviewer`。同 ID 冲突时,项目 profile 保持简写 selector,全局 profile 仍通过 `global:<id>` 调用。
39
+ `plan` 会解析 profile/stack、编译并校验不可变的实际 provider-bound 计划,然后在不联系 provider 的情况下丢弃。Profile selector 在所有入口使用同一语法:`reviewer`、`project:reviewer` 或 `global:reviewer`。在 delegation 中,裸 ID 选择 `project:<id>`;调用全局 profile 请使用明确的 `global:<id>`。同 ID 的 profile 仍彼此独立,不会互相继承 delegation policy。
37
40
 
38
41
  父模型使用无数据外发的 `forge_subagent_profiles` 做 discovery,再用 `forge_subagent` 执行。限制严格的父 stack 必须允许这两个工具名。
39
42
 
40
- 当前有两个 fresh-process backend:默认 `pi-subprocess-readonly` 使用 text/print,`pi-rpc-readonly` 使用 RPC。两者执行同一密封 prompt 和 shared-user 只读策略。所选 backend 不可用时会 fail closed,不会自动 fallback。
43
+ 匹配的可选 subagents 包与 runtime 开发版本提供四个 backend ID。以下说明基于尚未完成的可选包源码,不代表配套 release 已定稿;Forge 0.5.5 主包不依赖它们。请同时遵守上方 Forge 兼容要求:
44
+
45
+ - `pi-subprocess-readonly` 是默认 backend,使用 `pi --mode text --print`。它提供 `read`、`grep`、`find`、`ls` allowlist,属于 shared-user;allowlist 不是 OS 沙箱。
46
+ - `pi-rpc-readonly` 使用 `pi --mode rpc`,采用相同的 shared-user 只读策略,只改变进程协议。
47
+ - `pi-inprocess` 在 host model runtime 中运行,拥有启动用户的完整权限。它是 workspace-write backend;在 sealed access level 和 stack policy 允许时可提供 `read`/`grep`/`find`/`ls`/`edit`/`write`/`bash`,但没有 OS 沙箱。需要 extension-registered provider 时应使用它。
48
+ - `pi-bwrap-write` 是仅限 Linux 的可选 Bubblewrap backend。它为选定 workspace 提供隔离的 `workspace-write` 挂载,并可在允许时提供 `read`/`grep`/`find`/`ls`/`edit`/`write`/`bash`。写入会直接落到该 workspace,不是另行审批再 apply 的 staged patch;需要 Bubblewrap,默认还要求 git workspace。
49
+
50
+ 所选 backend 不可用时会 fail closed,不会自动 fallback。Fresh-process backend 对 extension-registered provider 会报告不可移植;此时改用 `pi-inprocess`。不要把此开发矩阵或 `tools.initial` 修复理解为配套 companion release 已发布。
51
+
52
+ Human 运行的 backend 优先级是:显式 per-run `--backend`、匹配 profile override、可信项目默认值、全局默认值、内置 `pi-subprocess-readonly`。交互式模型调用也可以提供 per-call backend override;无人值守的模型调用会固定使用生效的 profile/config backend,并拒绝该 override。Timeout 依次采用匹配 profile override、可信项目默认值、全局默认值,再到内置 60 秒;有效范围为 1,000–3,600,000 ms,host timeout 仅为 best effort。
41
53
 
42
54
  ## 审批
43
55
 
@@ -47,18 +59,18 @@ Profile 默认不能委派。请在可信项目的 `.pi/forge/subagents.json`
47
59
 
48
60
  ```json
49
61
  {
50
- "subagents": {
51
- "allowAgentInvocationWithoutApproval": true
52
- }
62
+ "allowAgentInvocationWithoutApproval": true
53
63
  }
54
64
  ```
55
65
 
56
- 它只影响 `forge_subagent`;`/forge-agent run` 仍需要交互审批。格式错误或不可信项目会 fail closed。请把此 `subagents.json` 当作授权文件:除非所有可调用父 agent 都可以无需再次询问就把编译 prompt 和可读文件发给 provider,否则不要启用或提交此设置。
66
+ 在专用 `subagents.json` 中该 flag 必须位于顶层;只有旧版 `config.json` 的嵌套 `subagents` 段落使用嵌套形式。
67
+
68
+ 它只影响 `forge_subagent`;`/forge-agent run` 仍需要交互审批。该 flag 可以来自全局默认或可信项目文件,项目值优先。尚未发布的配套修复中,某一层省略时会继承;某一层明确写入非 boolean 时,该层设为 `false` 并发出 warning;更高优先级层的有效 boolean 仍会正常覆盖较低层。若整个配置文件不可读、格式错误或不是 JSON object,则该文件会带 warning 被忽略,之前有效的层仍可能继续生效。不可信项目的项目配置会被忽略,execution trust gate 也会阻止该项目运行 delegation。请把 `subagents.json` 当作授权文件:除非所有可调用父 agent 都可以无需再次询问就把编译 prompt 和可读文件发给 provider,否则不要启用或提交此设置。
57
69
 
58
70
  ## Child 边界
59
71
 
60
- Child 从干净对话开始,不会自动继承父 history。候选工具只有 `read`、`grep`、`find`、`ls`,并继续受到 stack policy 限制。它不会加载 write/edit/shell、skills、prompt templates、context files 或第三方 extensions。
72
+ 普通的新 child 从干净对话开始,不会自动继承父 history。显式保留 child 后续聊与后台运行属于独立的开发中特性,详见[配套包文档](https://github.com/MacroSony/pi-forge-subagents)。对 `pi-subprocess-readonly` 和 `pi-rpc-readonly`,候选工具只有 `read`、`grep`、`find`、`ls`,并继续受到 stack policy 限制;这些 child 不加载 write/edit/shell、skills、prompt templates、context files 或第三方 extensions。`pi-inprocess` 和 `pi-bwrap-write` 仅在 access level 与 stack policy 允许时提供上文所述可写工具。
61
73
 
62
- > **当前 backend 是 shared-user,不是操作系统沙箱。** “只读”只是模型工具策略,进程仍拥有启动用户的 OS 权限。该用户可读的绝对路径可能被读取并发送给 provider;文本可能保留在父 tool-result 和 Pi session JSONL。Timeout/取消仅为 best effort。`/tree` 不能撤销 provider 请求、计费或外部影响,也不保证删除磁盘上的 abandoned entry。
74
+ > **边界取决于 backend:** subprocess/RPC 只读 backend 和 `pi-inprocess` 都是 shared-user,不是 OS 沙箱;后者在 host 进程中拥有启动用户完整权限。`pi-bwrap-write` 是 Linux 隔离例外:选定 workspace 是可写的项目挂载,另有只读运行时挂载与沙箱临时存储。它不是 staged patch/apply 流程,也不提供 network isolation。Shared-user backend 中,该用户可读的绝对路径可能被读取并发送给 provider;文本可能保留在父 tool-result 和 Pi session JSONL。Timeout/取消仅为 best effort。`/tree` 不能撤销 provider 请求、计费或外部影响,也不保证删除磁盘上的 abandoned entry。
63
75
 
64
- 不要给此 shared-user 设计添加 write/edit/shell。操作系统隔离和需要第二次审批的 staged write 属于未来工作。
76
+ 选择只读 backend 时不要授予 mutation path;选择可写 backend 则应视为明确授权其修改选定 workspace。
@@ -86,6 +86,35 @@ Subagent 执行功能从主包移入可选包 `@zihanw/pi-forge-subagents`(要
86
86
  | `@zihanw/pi-forge/src/*` 别名 | 已移除;无替代(内部实现) |
87
87
  | 根部的 loader/profile/catalog/engine 再导出 | 已移除;无替代(内部实现) |
88
88
 
89
+ ## 上游 Pi 0.87 迁移
90
+
91
+ Forge 0.5.5 要求上游 Pi 版本 `>=0.87.0 <0.88.0`。不提供对 0.86 的双重运行时支持(仓库开发 SDK 固定为 `0.87.0`,peer 范围为 `>=0.87.0 <0.88.0`)。主包和可选包独立发布。
92
+
93
+ **升级后重启 Pi 进程:** 更新全局安装不会替换已运行进程的核心。若当前会话在升级前启动,请退出并重新启动 Pi,再恢复会话;仅 `/reload` 扩展不足以切换 Pi 核心。重新打开 `/preset ui` 给出的新链接,旧服务器 token 不沿用。
94
+
95
+ ### 上下文 Hook 迁移(`context_with_system`)
96
+
97
+ - **标准 `context` 排除 System 消息:** 在 Pi 0.87 中,标准 `context` 生命周期 hook 默认排除 System 消息。之前依赖或操作完整 System 上下文的第三方扩展必须迁移至完整的 `context_with_system` hook。
98
+ - **统一的 Forge 流水线:** Forge 将其整个编译器、基础提示词替换与能力投影流水线完整移至 `context_with_system`,在完整转录边界上统一运行,无需内部两阶段切分。
99
+ - **`before_agent_start` 注入时机:** 任何通过 `before_agent_start` 强制注入的 System 提示词在 Pi 执行流中仍然晚于 `context_with_system` 执行。
100
+
101
+ ### 规范会话投影与转录隔离
102
+
103
+ - **集成 `buildSessionProjection`:** 运行时执行、Preview 预览与锚点定位器均采用 Pi 0.87 的 `buildSessionProjection`。轮次级的 `context_edit` 忽略(omission)、替换(replacement)以及 `sourceEntry` 引用均能正确反映在瞬态请求中,而磁盘上的原始 JSONL 会话历史完全保持不变。
104
+ - **首条 System 消息顺序:** SDK 传入的 leading System 提示词在请求转录中始终保持在第一位;Forge 自身的前缀纯元数据锚点紧随其后插入,绝不会置换请求头部或用户回退位置。
105
+ - **前置任意改写 Fail-Closed:** 若第三方扩展在 Forge 之前执行了破坏与规范投影唯一有序对齐的改写,Forge 依然 fail-closed 报错中止。
106
+
107
+ ### 续跑与生命周期结算
108
+
109
+ - **`agent_end` 与 `agent_settled` 的职责划分:** `agent_end` 在低层运行结束后提供落锚机会,前提是没有 Forge 上下文失败或未完成的工具批次。但编译周期与 busy fence 仅在 `agent_settled` 时重置。这保证了由 `agent_before_settle` 发起的继续执行(continuation)不会丢失已编译的 Preset 输入与活跃能力。
110
+
111
+ ### 防护机制与语义保持
112
+
113
+ - **项目信任:** 激活能力或执行 Agent 控制工具必须处于受信任项目(`isProjectTrusted()`);人类 CLI 的 disable/reset 恢复入口仍可用。
114
+ - **`sourceRevision` 防脏写:** 已有能力的更新/删除与带绑定的预设更新需要匹配原始字节的源版本;创建能力不得覆盖已有文件。并发冲突返回 `409 Conflict`。
115
+ - **Save ≠ Enable:** 保存能力库定义仅更新磁盘文件,绝不在当前会话中启用该能力;保存当前生效的预设会立即刷新其策略(更新实时工具与能力授权),但不会替换已冻结的活动能力快照。
116
+ - **上游缺陷状态:** 上游 Pi 元数据切分与语义截断缺陷未修复;压缩检查点位置保持不变;旧会话中的 carrier 保持原样不自动迁移;不支持也不承诺 OMP(Oh My Pi)。
117
+
89
118
  ## 兼容性说明
90
119
 
91
120
  - Host port 的 wire 结构在 `FORGE_HOST_PORT_VERSION = 1` 内只做增量扩展;未知操作会以普通的 `{ ok: false, error }` 结果拒绝(`"Unknown Forge host operation: …"`),而不是抛出异常。可选包必须把任何操作失败视为该请求的终态。
@@ -10,7 +10,7 @@
10
10
 
11
11
  `/preset ui restart` 会替换 server,`/preset ui stop` 会关闭它。
12
12
 
13
- 编辑器绑定在带 session token 的可用 `127.0.0.1` 端口;多个项目可以同时运行。读取、预览和 payload 检查在合适范围内可用;写入要求 Pi 信任项目,并且文件被限制在 Pi Forge 的预设/Profile 存储内。可以在 `.pi/forge/config.json` 中设置偏好端口:
13
+ 编辑器绑定在带 session token 的可用 `127.0.0.1` 端口;多个项目可以同时运行。读取、预览和 payload 检查在合适范围内可用;写入要求 Pi 信任项目,内置 Web 写入被限制在 Pi Forge 的预设/Profile、Capability 和可信 Forge 配置存储内。可以在 `.pi/forge/config.json` 中设置偏好端口:
14
14
 
15
15
  ```json
16
16
  {
@@ -28,22 +28,90 @@
28
28
 
29
29
  支持:
30
30
 
31
- - 从默认 Pi mirror 新建预设;
32
- - 在 **堆栈** tab 中编排有序的 Block/Slot;
33
- - 结构化和原始 JSON 编辑;
31
+ - 从模板新建预设(默认 Pi 提示词镜像、空白预设、极简工作者);
32
+ - 在 **堆栈**(Stack)tab 中编排有序的 Block/Slot;
33
+ - 在 **策略**(Policy)tab 中配置工具与技能的 allow/deny 资源策略及自定义默认工具(`tools.initial`);
34
+ - 在独立的同级 **能力绑定**(Capability bindings)tab 中关联能力(Preset 元数据面板不再包含绑定);
35
+ - 结构化元数据、参数、上下文以及 **Regex** 规则编辑;
34
36
  - 拖拽排序、启用/禁用、校验和完整编译预览;
35
37
  - 工具/skill 搜索、精确名称 chips 和通配符策略;
36
- - variables、context 和 regex 规则;
37
38
  - 原生 pi-forge JSON 导入;
38
39
  - 导出、fork、删除和 payload 捕获。
39
40
 
40
- 已有 ID 在编辑时不可修改;需要新 ID 时使用 **Fork**,避免破坏 Profile 引用和当前选择。工具栏的 scope 下拉(默认 `project`)决定新建、导入和 fork 的写入位置:选择 `global` 写入用户全局 `~/.pi/forge/prompt-stacks`,选择 `project` 写入项目 `.pi/forge/prompt-stacks`;这些目录名在 0.5.3 中为兼容性暂时保留。列表会为全局预设显示 `global` badge;保存和删除通过 `global:<id>` 路由精确作用于全局文件。保存、导入、fork 和删除后会重新加载当前 Pi session。
41
+ ### 编辑与检查
42
+
43
+ 资源标题区区分“正在编辑哪份预设”与“它是否已在会话中启用”。切换页面时,全局会话指令摘要仍然可见。打开预设属性、条目属性、规则卡片或绑定详情,不会修改草稿。**预设属性**从资源标题区打开,不再推挤正文;字段修改仍归同一份预设草稿,须显式保存。全局会话指令保留紧凑摘要,操作区进入独立、非模态的**当前会话**工作区,不与编辑中的资源混淆。生效工具、指令摘要、活动能力与启用操作优先;完整指令正文、会话身份和传输详情按需展开;信任/过期/错误和 pending/prepared 警告仍直接可见。预设状态反馈放在页脚;其中的停用仍针对当前生效预设,不是选中的草稿。
44
+
45
+ - **堆栈:** 列表显示名称/开关与一行摘要,如 `user · report` 或 `slot · chat-history`。完整身份保留在提示与**属性**内,可复制 ID;Role、插槽选择与属性同排,正文紧接其后。支持 Enter/空格选择及拖拽;标题旁保留低强调的直接删除按钮与确认。插槽参数按内容高度排列,不再把几行选项纵向撑满。
46
+ - **关闭状态与栏宽:** 关闭条目整行使用中性背景、弱化文字和灰色侧线;选中的细框只表示正在编辑,不代表开启。拖拽预设栏或堆栈栏右边界可调宽,双击或在分隔线按 Enter 恢复默认,方向键微调,Escape 取消当前拖拽。宽度只记在当前浏览器站点内,不写入预设 JSON、不产生草稿改动或保存请求。窗口变小时临时限制宽度,为正文留空间;窄屏仍采用上下布局。
47
+ - **正则:** 先浏览规则名称、启用状态、阶段/效果与表达式摘要,再展开编辑。表达式与替换正文直接可见,频率、目标、角色和范围限制在高级区。列表末尾的虚线入口添加并聚焦新规则;规则/绑定移除需确认,仍是草稿改动,通过预设的**保存**按钮写入。
48
+ - **策略:** 许可上限与默认工具分成两张卡;字面量/通配符输入收在高级区,技能列表可见性另列,不能当作执行沙箱。
49
+ - **预览/草稿差异/运行差异:** 预览是独立工作区开关,开关或切换编辑页签都保留当前编辑页。边界尖角把侧栏加宽;加宽时分别提供“收窄”和“专注”两个按钮,退出专注恢复之前的非专注布局并保留检查页签,不替换草稿。专注时点击其他编辑页签会露出编辑区并保留预览。面板首次默认侧栏。切换预览、草稿差异或运行差异时只切换内容,保留当前宽度和专注状态;专注阅读需手动进入。窄窗口改为上下布局,避免硬挤四列;专注阅读时暂时隐藏编辑区,返回或选择编辑页签后恢复。草稿差异比较草稿编译结果与已保存定义;运行差异比较捕获的提供商轮次快照。查看本身不请求模型,也不证明缓存命中。
50
+
51
+ Profile 编辑按 Provider/Model、Thinking/Preset 分组,不可变身份在标题处显示;Profile 与 Capability 创建时仍明确选择 ID 与作用域。预设高级页把可编辑参数放在折叠的扩展参考目录之前。预览工具详情按需展开,段落统计可在标题提示中查看。
52
+
53
+ 插件贡献的**设置**页自动保存,并反馈待保存/保存中/已保存/失败;保存失败的编辑会留在表单。预设、能力与 Profile 仍使用显式保存。
54
+
55
+ ### 检查能力对上下文的影响
56
+
57
+ **当前会话**工作区左侧是能力与工具控制,右侧是会话投影,窄屏上下排列。当前生效工具和活动指令摘要直接可见。在本页面成功启用、停用或重置后,还显示操作前后实际观测到的工具增减;这不是持久历史。能力**配置的工具调整**另行标注:多个能力重叠作用时,实际工具可能完全不变。
58
+
59
+ 点击**在上下文中定位**,控制区保持可见,右侧高亮关联更新并滚到最近匹配;普通更新不抢阅读位置。这里检查的是**已启用的已保存预设+当前会话能力**,不是编辑器选中的任意预设。原预设编辑页仍能一边编辑,一边查看**预览/草稿差异/运行差异**;跨工作区保留草稿、选择、检查页签及宽度/专注状态。
60
+
61
+ 后台状态检查静默进行,不再每轮读取能力目录或让可用控件闪成禁用态。目录在进入工作区、主动刷新及相关操作后读取;返回时废弃上一轮迟到读取,但不丢弃正在进行的写操作回执。投影由状态的实际变化驱动,不另开轮询;同分支刷新暂保留并明确标注上次投影,过期/不可信或会话/分支切换时使旧结果失效。
62
+
63
+ 块序号只对该次预览有效。原生 System 分段与归属明确的 user fallback 保留实际角色;停用不会抹去此前的更新。没有可靠匹配时会明确提示,不猜测位置。第一版要求可信会话与已启用预设,尚不提供完整的启停事件历史浏览。
64
+
65
+ 检查只是本地读取,不准备/投递请求,不改变工具或会话历史。它不是已捕获的提供商 payload,也不能证明缓存复用;实际请求差异与 usage 请分别看 Run diff/Payload 和提供商返回的数据。
66
+
67
+ ### 工具选择与默认工具编辑器
68
+
69
+ **策略**(Policy)tab 提供可选的自定义默认工具编辑器(`tools.initial?: string[]`):
70
+
71
+ - **默认工具选择器:** 提供基于 SDK `sourceInfo`(Pi 内置工具、扩展包工具及顶层入口点)的可折叠、可搜索分组选择器。分组复选框批量选择/取消当前筛选结果;**手动输入工具名**保留字面量/离线名称入口,能力编辑器也使用同一入口。
72
+ - **确切工具名称:** 选择器保存具体的工具名称;不持久化包引用,不自动安装依赖包,扩展包后续新增的工具也不会被自动加入。未激活但已注册的工具在选择器中可见;未加载的工具在当前会话不可用,但手动保存的引用不会被丢弃。
73
+ - **缺省与零默认工具:** 缺省不配置 `tools.initial` 时保持传统行为(选择性 allow 匹配已注册目录;不限/deny 保留或过滤会话基线,不会全选目录);显式配置为空列表 `[]` 时,默认激活零个工具。
74
+ - **权威上限:** 保留高级字面量与通配符 allow/deny 策略作为权威上限;被 allow/deny 拦截的工具无法作为默认工具生效。
75
+ - **运行时生命周期:** 配置的默认工具在预设处于激活状态期间作为常态基准(并非每轮一次性重置)。停用能力后按默认工具与剩余能力重新计算;停用预设恢复经外部变更协调的会话基线,切换预设则按新预设与保留的非绑定能力计算。
76
+
77
+ ### 能力绑定 tab
78
+
79
+ 能力绑定在独立的同级 **能力绑定**(Capability bindings)tab 中管理:
80
+
81
+ - 每张绑定卡片只显示一次带作用域的能力引用,以及“允许模型启用”授权开关;绑定 ID 收入详情,不再常驻卡头。
82
+ - **高级选项**(Advanced)展开绑定 ID 与覆盖项(正文替换/追加、工具增加/移除);源定义/生效值实时对比有独立展开入口。
83
+ - 工具覆盖选择**继承**或**自定义覆盖**,配备与策略页面一致的分组选择器。自定义允许空列表:`[]` 将原能力对应的添加/移除列表覆盖为空;继承则不写覆盖字段。这不是清空所有会话工具,也不绕过其他权限限制。
84
+ - 绑定排列只决定目录顺序,不是执行优先级,因此编辑器不再提供上下按钮。直接移除仍需确认,并随外层预设保存;删除其他绑定不会让当前展开的详情跑到别的条目。
85
+
86
+ ### 保存行为与执行影响
87
+
88
+ **激活**使用已保存的预设;草稿有未保存修改时暂不可用,须先显式保存。保存与激活不是一个合并事务。
89
+
90
+ - **能力界面:** 保存能力仅更新能力库文件定义,绝不会在当前会话中自动启用该能力。
91
+ - **预设编辑:** 保存**未激活**的 Preset 仅更新其磁盘文件,不会选中或激活它;**关键**:保存**当前已激活**的 Preset 会立即刷新其实时工具策略与能力授权,但不会替换已冻结的活动能力快照。
92
+
93
+ 已有 ID 在编辑时不可修改;需要新 ID 时使用 **Fork**,避免破坏 Profile 引用和当前选择。新建、导入和 fork 的小表单在写入前一起确认名称、ID 与目标 scope(默认 `project`):选择 `global` 写入用户全局 `~/.pi/forge/prompt-stacks`, 选择 `project` 写入项目 `.pi/forge/prompt-stacks`;这些目录名在 0.5.3 中为兼容性暂时保留。
94
+
95
+ **新建预设**对话框提供三种起始模板:
96
+ - **默认 Pi 提示词:** 完整镜像所有默认块与插槽,保留可移动的工具、规范、文档、项目上下文、技能与聊天历史;
97
+ - **空白预设:** 条目为空(`items: []`),不设置工具或技能策略;此时 Pi 会自动保留其基础系统提示词与历史记录(并非零上下文或无工具);
98
+ - **极简工作者:** 匹配 `examples/minimal-prompt-stack.json` 的极简结构:包含单行系统提示词(“You are a helpful software engineer assistant.”)、关闭摘要的聊天历史,并仅限使用 `bash` 与 `edit` 工具。
99
+
100
+ 选择模板会自动同步更新建议名称(只要用户未手动修改过名称)。导入与 Fork 对话框不显示模板选择器,继续使用导入内容或当前载入的源预设,不套用新建模板。
101
+
102
+ 预设列表下方放置虚线新建入口,条目列表下方通过“添加内容/插槽”选择 Block 或 Slot。列表会为全局预设显示 `global` badge;保存和删除通过 `global:<id>` 路由精确作用于全局文件。保存、导入、fork 和删除后会刷新当前 Pi 会话中的 Forge 资源,不重启 Pi 进程。
103
+
104
+ ### 兼容性说明
105
+
106
+ 使用 `tools.initial` 的预设需要新版 Forge 支持。旧版 Forge 可能忽略 `tools.initial` 并恢复旧选择逻辑(选择性 allow 匹配目录,不限/deny 保留或过滤会话基线)(不支持向下降级兼容)。这些变化需要 Forge 0.5.5;宿主要求保持 Pi `>=0.87.0 <0.88.0` 不变。
41
107
 
42
108
  ## Agent profile 工作区
43
109
 
44
110
  列表显示 Profile ID、名称、模型、思考等级、预设、校验状态、auto-activation 和 last-applied provenance。每个 Profile 都带 `project` / `global` scope badge;同 ID 的 shadow 对会显示 `shadows global:<id>` 或 `shadowed by project:<id>`。
45
111
 
46
- 可信项目通过 **New profile** 旁的 scope 下拉(默认 `project`)选择目标 scope:选择 `global` 写入用户全局 `~/.pi/forge/agent-profiles`,选择 `project` 写入项目 `.pi/forge/agent-profiles`。全局 Profile 可通过显式 `global:<id>` 路由编辑、校验、保存、一次性应用和删除;未限定路由始终只作用于项目资源。编辑全局 Profile 时,预设下拉只显示全局预设。Model 选项来自 Pi registry,thinking 选项反映模型支持,预设选项来自同一个 repository。编辑器会拒绝同 scope 内第二个 auto-activation Profile。
112
+ Profile/Capability 库把虚线新建入口放在资源列表下方;创建编辑器名称优先,作用域只在表单内选择一次,保留必要的模型/正文编辑,不为列表占位提前写入不完整资源。
113
+
114
+ 可信项目通过创建表单内的 scope 字段(默认 `project`)选择目标 scope:选择 `global` 写入用户全局 `~/.pi/forge/agent-profiles`,选择 `project` 写入项目 `.pi/forge/agent-profiles`。全局 Profile 可通过显式 `global:<id>` 路由编辑、校验、保存、一次性应用和删除;未限定路由始终只作用于项目资源。编辑全局 Profile 时,预设下拉只显示全局预设。Model 选项来自 Pi registry,thinking 选项反映模型支持,预设选项来自同一个 repository。编辑器会拒绝同 scope 内第二个 auto-activation Profile。
47
115
 
48
116
  ## Delegation
49
117