@zihanw/pi-forge 0.5.4 → 0.5.5

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 (228) hide show
  1. package/CHANGELOG.md +71 -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.map +1 -1
  32. package/dist/agent-profile.js +11 -0
  33. package/dist/agent-profile.js.map +1 -1
  34. package/dist/capabilities.d.ts +39 -0
  35. package/dist/capabilities.d.ts.map +1 -0
  36. package/dist/capabilities.js +160 -0
  37. package/dist/capabilities.js.map +1 -0
  38. package/dist/capability-anchors.d.ts +45 -0
  39. package/dist/capability-anchors.d.ts.map +1 -0
  40. package/dist/capability-anchors.js +263 -0
  41. package/dist/capability-anchors.js.map +1 -0
  42. package/dist/capability-command.d.ts +4 -0
  43. package/dist/capability-command.d.ts.map +1 -0
  44. package/dist/capability-command.js +164 -0
  45. package/dist/capability-command.js.map +1 -0
  46. package/dist/capability-events.d.ts +79 -0
  47. package/dist/capability-events.d.ts.map +1 -0
  48. package/dist/capability-events.js +478 -0
  49. package/dist/capability-events.js.map +1 -0
  50. package/dist/capability-projection.d.ts +22 -0
  51. package/dist/capability-projection.d.ts.map +1 -0
  52. package/dist/capability-projection.js +273 -0
  53. package/dist/capability-projection.js.map +1 -0
  54. package/dist/capability-protocol.d.ts +21 -0
  55. package/dist/capability-protocol.d.ts.map +1 -0
  56. package/dist/capability-protocol.js +15 -0
  57. package/dist/capability-protocol.js.map +1 -0
  58. package/dist/capability-state.d.ts +74 -0
  59. package/dist/capability-state.d.ts.map +1 -0
  60. package/dist/capability-state.js +43 -0
  61. package/dist/capability-state.js.map +1 -0
  62. package/dist/capability-tool.d.ts +11 -0
  63. package/dist/capability-tool.d.ts.map +1 -0
  64. package/dist/capability-tool.js +25 -0
  65. package/dist/capability-tool.js.map +1 -0
  66. package/dist/capability-web-host.d.ts +11 -0
  67. package/dist/capability-web-host.d.ts.map +1 -0
  68. package/dist/capability-web-host.js +105 -0
  69. package/dist/capability-web-host.js.map +1 -0
  70. package/dist/codecs/capability.d.ts +71 -0
  71. package/dist/codecs/capability.d.ts.map +1 -0
  72. package/dist/codecs/capability.js +363 -0
  73. package/dist/codecs/capability.js.map +1 -0
  74. package/dist/codecs/prompt-stack.d.ts +1 -1
  75. package/dist/codecs/prompt-stack.d.ts.map +1 -1
  76. package/dist/codecs/prompt-stack.js +140 -13
  77. package/dist/codecs/prompt-stack.js.map +1 -1
  78. package/dist/command-contribution/index.d.ts +21 -0
  79. package/dist/command-contribution/index.d.ts.map +1 -0
  80. package/dist/command-contribution/index.js +14 -0
  81. package/dist/command-contribution/index.js.map +1 -0
  82. package/dist/compile-cycle.d.ts +1 -0
  83. package/dist/compile-cycle.d.ts.map +1 -1
  84. package/dist/compile-cycle.js.map +1 -1
  85. package/dist/compiler.d.ts.map +1 -1
  86. package/dist/compiler.js +70 -17
  87. package/dist/compiler.js.map +1 -1
  88. package/dist/forge-command.d.ts +10 -0
  89. package/dist/forge-command.d.ts.map +1 -0
  90. package/dist/forge-command.js +106 -0
  91. package/dist/forge-command.js.map +1 -0
  92. package/dist/index.d.ts +2 -2
  93. package/dist/index.d.ts.map +1 -1
  94. package/dist/index.js +54 -5
  95. package/dist/index.js.map +1 -1
  96. package/dist/json-fingerprint.d.ts +11 -0
  97. package/dist/json-fingerprint.d.ts.map +1 -0
  98. package/dist/json-fingerprint.js +66 -0
  99. package/dist/json-fingerprint.js.map +1 -0
  100. package/dist/lifecycle.d.ts +14 -0
  101. package/dist/lifecycle.d.ts.map +1 -1
  102. package/dist/lifecycle.js +167 -67
  103. package/dist/lifecycle.js.map +1 -1
  104. package/dist/payload-command.d.ts +2 -2
  105. package/dist/payload-command.d.ts.map +1 -1
  106. package/dist/payload-command.js +80 -26
  107. package/dist/payload-command.js.map +1 -1
  108. package/dist/payload-state.d.ts +1 -0
  109. package/dist/payload-state.d.ts.map +1 -1
  110. package/dist/payload-state.js +1 -0
  111. package/dist/payload-state.js.map +1 -1
  112. package/dist/policy.d.ts +2 -1
  113. package/dist/policy.d.ts.map +1 -1
  114. package/dist/policy.js +3 -0
  115. package/dist/policy.js.map +1 -1
  116. package/dist/preset-command.d.ts.map +1 -1
  117. package/dist/preset-command.js +92 -35
  118. package/dist/preset-command.js.map +1 -1
  119. package/dist/preview-text.d.ts +5 -0
  120. package/dist/preview-text.d.ts.map +1 -0
  121. package/dist/preview-text.js +27 -0
  122. package/dist/preview-text.js.map +1 -0
  123. package/dist/preview.d.ts +18 -1
  124. package/dist/preview.d.ts.map +1 -1
  125. package/dist/preview.js +132 -97
  126. package/dist/preview.js.map +1 -1
  127. package/dist/profile-command.js +68 -9
  128. package/dist/profile-command.js.map +1 -1
  129. package/dist/regex.d.ts.map +1 -1
  130. package/dist/regex.js +5 -0
  131. package/dist/regex.js.map +1 -1
  132. package/dist/render-helpers.d.ts.map +1 -1
  133. package/dist/render-helpers.js +2 -0
  134. package/dist/render-helpers.js.map +1 -1
  135. package/dist/repositories/capability.d.ts +53 -0
  136. package/dist/repositories/capability.d.ts.map +1 -0
  137. package/dist/repositories/capability.js +294 -0
  138. package/dist/repositories/capability.js.map +1 -0
  139. package/dist/runtime/capability-runtime.d.ts +91 -0
  140. package/dist/runtime/capability-runtime.d.ts.map +1 -0
  141. package/dist/runtime/capability-runtime.js +967 -0
  142. package/dist/runtime/capability-runtime.js.map +1 -0
  143. package/dist/runtime/tool-policy-runtime.d.ts +8 -0
  144. package/dist/runtime/tool-policy-runtime.d.ts.map +1 -1
  145. package/dist/runtime/tool-policy-runtime.js +171 -30
  146. package/dist/runtime/tool-policy-runtime.js.map +1 -1
  147. package/dist/session-adapter.d.ts +13 -0
  148. package/dist/session-adapter.d.ts.map +1 -1
  149. package/dist/session-adapter.js +110 -0
  150. package/dist/session-adapter.js.map +1 -1
  151. package/dist/session-usage.d.ts +65 -0
  152. package/dist/session-usage.d.ts.map +1 -0
  153. package/dist/session-usage.js +134 -0
  154. package/dist/session-usage.js.map +1 -0
  155. package/dist/subagent/fingerprints.d.ts +3 -15
  156. package/dist/subagent/fingerprints.d.ts.map +1 -1
  157. package/dist/subagent/fingerprints.js +5 -69
  158. package/dist/subagent/fingerprints.js.map +1 -1
  159. package/dist/subagent/index.d.ts +2 -0
  160. package/dist/subagent/index.d.ts.map +1 -1
  161. package/dist/subagent/index.js +2 -0
  162. package/dist/subagent/index.js.map +1 -1
  163. package/dist/subagent-host.d.ts +2 -2
  164. package/dist/subagent-host.d.ts.map +1 -1
  165. package/dist/subagent-host.js +13 -1
  166. package/dist/subagent-host.js.map +1 -1
  167. package/dist/types.d.ts +6 -1
  168. package/dist/types.d.ts.map +1 -1
  169. package/dist/types.js.map +1 -1
  170. package/dist/web-editor/client-script.generated.d.ts.map +1 -1
  171. package/dist/web-editor/client-script.generated.js +1 -1
  172. package/dist/web-editor/client-script.generated.js.map +1 -1
  173. package/dist/web-editor/client-styles.generated.d.ts.map +1 -1
  174. package/dist/web-editor/client-styles.generated.js +1 -1
  175. package/dist/web-editor/client-styles.generated.js.map +1 -1
  176. package/dist/web-editor/server.d.ts.map +1 -1
  177. package/dist/web-editor/server.js +152 -4
  178. package/dist/web-editor/server.js.map +1 -1
  179. package/dist/web-editor/styles.d.ts.map +1 -1
  180. package/dist/web-editor/styles.js +476 -149
  181. package/dist/web-editor/styles.js.map +1 -1
  182. package/dist/web-editor/types.d.ts +81 -1
  183. package/dist/web-editor/types.d.ts.map +1 -1
  184. package/dist/web-host.d.ts +7 -0
  185. package/dist/web-host.d.ts.map +1 -1
  186. package/dist/web-host.js +168 -5
  187. package/dist/web-host.js.map +1 -1
  188. package/dist/workspace.d.ts +11 -0
  189. package/dist/workspace.d.ts.map +1 -1
  190. package/dist/workspace.js +63 -5
  191. package/dist/workspace.js.map +1 -1
  192. package/docs/README.md +4 -0
  193. package/docs/design/README.md +3 -1
  194. package/docs/design/architecture-0.5.md +22 -0
  195. package/docs/design/archive/2026-09-12-system-update-design.md +229 -0
  196. package/docs/design/pi-forge-system-update-design-notes.md +157 -229
  197. package/docs/development/release.md +20 -20
  198. package/docs/development/roadmap.md +17 -2
  199. package/docs/development/scoped-global-profiles-stacks.md +1 -1
  200. package/docs/development/setup.md +1 -1
  201. package/docs/getting-started.md +1 -1
  202. package/docs/guides/delegation.md +22 -20
  203. package/docs/guides/migrating-to-0.5.md +29 -0
  204. package/docs/guides/use-cases.md +6 -2
  205. package/docs/guides/web-editor.md +73 -5
  206. package/docs/reference/active-state.md +77 -0
  207. package/docs/reference/capabilities.md +259 -0
  208. package/docs/reference/commands.md +52 -24
  209. package/docs/reference/configuration.md +4 -2
  210. package/docs/reference/features.md +70 -4
  211. package/docs/reference/provider-support.md +67 -0
  212. package/docs/reference/public-api.md +49 -3
  213. package/docs/reference/session-cache.md +112 -0
  214. package/docs/reference/stack-schema.md +18 -4
  215. package/docs/reference/subagent-host-port.md +8 -0
  216. package/docs/zh-CN/README.md +3 -0
  217. package/docs/zh-CN/getting-started.md +1 -1
  218. package/docs/zh-CN/guides/delegation.md +22 -10
  219. package/docs/zh-CN/guides/migrating-to-0.5.md +29 -0
  220. package/docs/zh-CN/guides/web-editor.md +74 -7
  221. package/docs/zh-CN/reference/capabilities.md +259 -0
  222. package/docs/zh-CN/reference/commands.md +61 -33
  223. package/docs/zh-CN/reference/provider-support.md +67 -0
  224. package/docs/zh-CN/reference/session-cache.md +112 -0
  225. package/examples/capabilities/review.json +12 -0
  226. package/examples/capabilities/write-tools.json +15 -0
  227. package/examples/read-first-worker-prompt-stack.json +49 -0
  228. package/package.json +16 -9
@@ -0,0 +1,259 @@
1
+ # 能力(`/capability`)
2
+
3
+ [中文文档](../README.md) · [English](../../reference/capabilities.md)
4
+
5
+ Pi-forge 引入了能力(Capabilities):会话级动态提示词指令与动态工具门控。`/capability` 是 CLI 命令名。本文档涵盖能力配置、所有权作用域、CLI 操作、Web 资源编辑、Preset 授权与 Agent 控制、投递模型、状态恢复边界与兼容性限制。
6
+
7
+ ## 环境要求与安装
8
+
9
+ - **宿主版本:** Pi `>=0.87.0 <0.88.0`(仓库开发 SDK 固定为 `0.87.0`,peer 范围为 `>=0.87.0 <0.88.0`;最低 SDK 0.87 保持不变;不声称对 0.86 的双重运行时支持)。能力功能需要 Forge 0.5.5。
10
+ - **项目信任:** 激活能力、预设绑定或添加手动指令需要项目处于受信任状态(`isProjectTrusted()`)。
11
+ - **兼容性限制:** 包含 `tools.initial` 的配置需要新版 Forge 支持;旧版 Forge 会忽略 `initial`,因而配置不具备向下降级兼容性。
12
+
13
+ 能力为 JSON 文件,存放在以下目录之一:
14
+
15
+ - **项目目录:** `.pi/forge/capabilities/<id>.json`
16
+ - **全局目录:** `~/.pi/forge/capabilities/<id>.json`
17
+
18
+ ```json
19
+ {
20
+ "schemaVersion": 1,
21
+ "type": "pi-forge.capability",
22
+ "id": "review",
23
+ "name": "Review",
24
+ "description": "Report findings and evidence before editing.",
25
+ "content": "List findings, evidence, and risks. Do not directly edit files.",
26
+ "tools": {
27
+ "add": [],
28
+ "remove": ["bash", "powershell", "write", "edit"]
29
+ }
30
+ }
31
+ ```
32
+
33
+ ### 资源规则
34
+
35
+ - **纯字面内容:** `content` 严格按字面文本处理(上限 100,000 字符),非文件路径、脚本或宏,不会展开模板变量。
36
+ - **工具名称:** `add` 与 `remove` 数组中的工具名必须为确切标识符(上限 128 字符,无空白符、控制符或 `*`/`?` 通配符,每个数组限 256 个工具)。
37
+ - **工具补丁约束:** 能力仅支持 `add` 与 `remove`;候选的 `only` 或能力级 allowlist 未实现。
38
+ - **解析与遮蔽:** 裸 ID 采用“项目优先于全局”的查找逻辑。若项目存在损坏或非法的 JSON,解析直接在本地报错(fail-closed),绝不静默回退到全局同名定义。可通过 `project:<id>` 或 `global:<id>` 指定明确 scope。
39
+
40
+ ## 所有权作用域与生命周期
41
+
42
+ - **所有权划分:** 能力定义(capabilities)为可复用资源,归属于项目或全局库;绑定与授权归属于预设(Preset JSON 顶层的 `capabilities` 数组);活跃状态与激活项严格归属于会话(Session)。
43
+ - **直接启用 vs 绑定启用:**
44
+ - 通过 CLI 直接激活(`/capability enable <[scope:]id>`)或 Web 能力库直接启用,创建的是会话中的**未绑定**活动项。
45
+ - 通过 CLI 绑定激活(`/capability enable-bound <id>`)或 Web 预设绑定启用,创建的是与当前 Preset 关联的**绑定**活动项。
46
+ - **不可变快照语义:** 激活时捕获不可变快照(包括内容、工具策略、内容指纹)。修改或删除磁盘上的 JSON 文件不会引发活跃会话漂移。
47
+ - **同 Preset 重载 vs 切换 Preset:** 重新加载同一 Preset 保持既有的冻结活动快照;切换 Preset 会自动停用(lifecycle 停用)旧 Preset 关联的绑定项,同时保留手动输入与未绑定的用户规则。
48
+ - **授权撤销行为:** 在 Preset 中撤销授权(将 `modelCallable` 设为 `false`)或删除绑定项,不会追溯抹除已激活的快照;用户通过 CLI(`/capability disable` 或 `/capability reset`)或 Web 面板停用是标准恢复路径。
49
+ - **用户与 Agent 所有权及去重:** 激活项记录归属主体(`user` 或 `agent`)。重复 `enable` 已激活的能力具备幂等性,绝不自动在用户与 Agent 之间转移所有权(takeover)。接管必须通过显式关闭后再重新启用。
50
+
51
+ ## Preset 授权的 Agent 控制
52
+
53
+ Preset 的 `capabilities` 绑定支持智能体自主选择与启用能力:
54
+
55
+ - **模型工具注册:** `forge_capability` 以固定 schema 注册一次,可见性遵循普通可执行工具选择;注册本身不授予能力权限。调用要求存在活跃 Preset,enable/disable 还会重查具体绑定的当前授权。
56
+ - **显式授权机制:** 仅当活跃 Preset 中的绑定显式声明 `modelCallable: true` 时,Agent 才能调用;缺省默认为 `false`。
57
+ - **固定参数契约:** 仅接受 `{ action: "list" | "status" | "enable" | "disable", id?: string }`(ID 上限 128 字符)。
58
+ - `list`:列出当前活跃 Preset 中具备调用资格的绑定能力;活动快照由 `status` 查看。
59
+ - `status`:返回当前会话的激活数量、呈现形式、投递状态及生效工具列表。
60
+ - `enable`:需要提供绑定的 `id`;仅可激活已授权且 `modelCallable: true` 的预设绑定能力。
61
+ - `disable`:需要提供 `id`(绑定 ID 或激活 UUID);仅可关闭归属于 `agent` 的激活项。
62
+ - **安全与权限边界:**
63
+ - 每次调用均重新校验项目信任、活跃 Preset 状态、绑定标识、`modelCallable: true` 以及当前工具策略。
64
+ - Agent 无法关闭用户启用的指令或手动指令。
65
+ - Agent 无法执行 `reset` 操作,也无法添加任意提示词文本。
66
+ - Agent 无法激活会移除 `forge_capability` 工具自身的能力。
67
+ - 在运行时已注销、正在恢复或会话上下文不一致时,调用直接 fail-closed 报错。
68
+
69
+ Agent 的 list/status 回复不复制完整规则正文:list 提供作者填写的描述与效果,status 提供活动元数据,避免把仅请求内投影的规则再次塞进普通工具历史和后续摘要。人类 CLI/Web 仍可查看完整冻结正文;真实对话及作者填写的描述不会被过滤。
70
+
71
+ ### Read-first Worker
72
+
73
+ 一个最小主包示例:[预设](../../../examples/read-first-worker-prompt-stack.json)+[Write tools 能力](../../../examples/capabilities/write-tools.json)。使用 Forge 0.5.5;旧发布版可能忽略 `tools.initial`。可选 subagent 执行仍是独立且尚未完成的 release。
74
+
75
+ 1. 在可信的临时项目中,将预设复制为 `.pi/forge/prompt-stacks/read-first-worker.json`,能力复制为 `.pi/forge/capabilities/write-tools.json`。先检查是否已有同名文件,不覆盖自己的资源。仅导入预设不会顺带安装引用的能力。
76
+ 2. 用已加载 Forge 的全新 Pi 会话,执行 `/preset reload`,再执行 `/preset use project:read-first-worker`。示例 `autoActivate: false`,绑定明确指向**项目作用域**;放到全局时,能力也须放全局并修改 `ref`。
77
+ 3. 没有其他活动能力时,默认工具只有 `read`、`ls` 与 `forge_capability`;后者是能力管理工具,不是文件写入工具。模型可用 `{ "action": "list" }` 列出授权绑定,以 `{ "action": "enable", "id": "write-tools" }` 启用命令/编辑能力,再以 `{ "action": "disable", "id": "write-tools" }` 停用自己的激活项。
78
+ 4. 在**当前会话**或 `/capability status` 观察:`read, ls, forge_capability` → 增加 `bash, edit` → 回到默认集。其他活动能力仍参与计算。若由人类启用,模型不能关闭该人类拥有的激活项,应从界面或 `/capability disable <activation-id>` 停用。
79
+
80
+ `allow` 是许可上限,`initial` 是默认集,`modelCallable: true` 明确允许模型选择该绑定,**不是每次启用都弹出人类审批**。若希望仅人类启用,将它改为 `false`。示例提示词要求任务结束后关闭,但这不是自动生命周期保证;Off 不撤销文件修改,也不终止运行中的工具。**Read-first 不是只读沙箱:** `bash` 能执行任意命令,而不只是写文件;这些配置不提供文件系统/进程隔离。为保持最小风格,替换提示词省略了 Pi 默认的项目上下文、技能和工具指导插槽;需要这些内容时请从默认 Pi mirror 改起。
81
+
82
+ ## 命令行操作
83
+
84
+ 通过 `/capability` 管理会话指令:
85
+
86
+ | 命令 | 行为 |
87
+ |---|---|
88
+ | `/capability list` | 显式重新发现能力库,并列出项目与全局能力及校验状态。 |
89
+ | `/capability bindings` | 列出当前活跃 Preset 的能力绑定,包括人类专用绑定。 |
90
+ | `/capability enable <[scope:]id>` | 激活指定的未绑定能力(裸 ID 或限定作用域)。直接 enable 保持未绑定。 |
91
+ | `/capability enable-bound <id>` | 按绑定 ID 激活当前 Preset 中已绑定的能力。 |
92
+ | `/capability status` | 显示激活能力数量、投递表现形式、投递状态以及当前选中的工具。 |
93
+ | `/capability disable <activation-or-capability-id>` | 通过激活 UUID 或能力 ID 关闭激活的能力。 |
94
+ | `/capability reset` | 关闭当前会话所有激活的能力与手动指令(仅限用户)。 |
95
+ | `/capability add <text>` | 向当前会话追加一条手动字面指令(无工具变更)。 |
96
+ | `/capability help` | 显示命令用法与兼容性说明。 |
97
+
98
+ 补全使用当前 session 与最近发布的 workspace snapshot,不会在每次按键时扫描资源。需要显式刷新发现时使用 `/capability list`。`/capability enable` 补全在唯一时提供裸 ID,在同名冲突或键入 `:` 作用域前缀时提供限定作用域的选择器(标签始终保留来源与名称限定)。`bindings` 仍显示人类专用绑定;`modelCallable: false` 只禁止 Agent 控制,不会把绑定从人类检查或启用列表中过滤掉。
99
+
100
+ **零推理成本:** 所有 `/capability` 斜杠命令与 Web 活动面板操作均在本地执行,仅更新内部会话状态并同步工具策略,操作本身不调用模型推理,不消耗付费 token。活动指令的规则正文仅在随后的真实模型请求中占用输入 token。
101
+
102
+ ## Web 资源编辑
103
+
104
+ ### 能力管理界面(Capabilities CRUD)
105
+
106
+ 顶部导航栏的**能力**(Capabilities)页面提供项目与全局能力的完整生命周期管理。能力库 API 为 `GET /api/capabilities`,选择器与 effective 变体为 `/api/capabilities/<selector>` 和 `/api/capabilities/effective`:
107
+
108
+ - **浏览与审查:** 查看能力 ID、显示名称、描述、指令正文及工具变更(`+add`、`-remove`),并显示校验诊断。
109
+ - **新建:** 在明确的项目作用域(`.pi/forge/capabilities/`)或全局作用域(`~/.pi/forge/capabilities/`)下创建新能力。
110
+ - **编辑与保存:** 编辑显示名称、描述、指令正文与工具增删项。能力 ID 在保存时不可修改。
111
+ - **分组工具选择器:** 能力工具选择(`+add` / `-remove`)提供基于 SDK `sourceInfo`(Pi 内置工具、扩展包工具及顶层入口点)的分组选择器。选择器保存具体确切的工具名称(EXACT concrete tool names):不持久化包引用,不自动安装依赖包,外部包新增的工具也不会自动加入。未激活但已注册的工具在选择器中可见;未加载的工具在当前会话不可用,但手动保存的引用不会被丢弃。
112
+ - **删除:** 支持在二次确认后删除磁盘上的能力 JSON 文件。
113
+ - **版本防脏写(`sourceRevision`):** 保存与删除操作强制校验界面加载时的源文件哈希版本(`sourceRevision`)。并发修改导致版本过期时返回 `409 Conflict` 并完整保留本地编辑草稿。
114
+ - **资源安全与保存执行影响:** 保存能力仅更新能力库定义,绝不自动将其激活到活跃会话中。非法 JSON fail-closed 本地报错;符号链接目录与目标不可写入。
115
+
116
+ ### Preset 能力绑定 tab
117
+
118
+ 在 Preset 编辑器中,能力绑定现已独立为同级的“**能力绑定**”(`bindings`)tab;Preset 元数据面板不再包含绑定:
119
+
120
+ - **限定引用:** 必须使用限定作用域引用(`project:<id>` 或 `global:<id>`)。
121
+ - **绑定 ID 与元数据:** 在 Preset 内唯一定义的绑定标识符(上限 128 字符)。
122
+ - **Agent 授权:** 勾选 `modelCallable` 开关(默认关闭),允许智能体通过 `forge_capability` 自主调用。
123
+ - **折叠的高级选项:** 覆盖项与源定义/有效值对比预览默认折叠在高级选项(Advanced)中,保持主列表清爽。
124
+ - **有限覆盖(Finite Overrides):**
125
+ - **正文覆盖:** 可选*无(沿用源内容)*、*替换*(`content`)或*追加*(`appendContent`,以双换行追加)。
126
+ - **工具覆盖:** `tools.add` 与 `tools.remove` 分别选择*继承*(省略字段)或*自定义覆盖*(字面工具列表,允许显式 `[]` 清空源定义该项),并使用集成分组工具选择器。
127
+ - 不支持注入任意自定义字段、脚本或继承链。
128
+ - **源定义与有效值对比预览:** 左右对比分栏实时展示源定义的正文/工具与覆盖生效后的有效正文/工具,复用服务端与运行时完全一致的解析器(`resolveCapabilityBindings`)。
129
+ - **防脏写保护:** 当 Preset 包含或修改绑定时,保存请求强制校验磁盘源版本(`sourceRevision`),防止覆盖外部并发编辑或新增的绑定(409 Conflict)。
130
+ - **Preset 保存执行影响:** 保存未激活的 Preset 仅更新其定义,绝不会选中或激活它;**关键**:保存当前已激活的 Preset 会立即重载并同步其实时工具策略与能力授权,但不会替换已冻结的活动能力快照。
131
+
132
+ ## Web 会话能力面板
133
+
134
+ Web 编辑器保留全局**会话能力**(Session capabilities)摘要,入口进入非模态的**当前会话**工作区:左侧能力/工具控制,右侧当前会话投影。预设编辑页仍保留独立的 Preview/Draft diff/Run diff;跨工作区保留未保存编辑和检查布局。
135
+
136
+ ### 面板功能与状态展示
137
+
138
+ - **活动能力列表:** 展示当前会话处于活动状态的所有能力与手动指令。每个卡片呈现:
139
+ - **标识与来源:** 激活 ID、显示名称以及解析来源(如 `project:review`、`global:review` 或 `manual`)。
140
+ - **开启者:** 标明由用户(`user`)还是智能体(`agent`)启用。
141
+ - **冻结正文快照:** 默认显示摘要,可展开查看激活时捕获的完整规则字面内容(frozen snapshot)。
142
+ - **工具调整与生效工具:** 卡片明确区分配置的工具补丁与当前生效工具;本页面最近一次成功操作显示实际净增减,多个能力重叠时生效集可能不变。
143
+ - **投递与呈现状态:**
144
+ - **投递状态:** 显示 `none`(无变更记录)、`pending`(等待下次请求生效)或 `prepared`(上下文已为本轮就绪)。面板特别注明:`prepared` 仅表示提示词上下文准备完毕,不代表模型实际遵从或确认送达。
145
+ - **文本呈现:** 显示当前模型使用 `native` 原生系统消息分段还是 `user` 带标记的时间线更新。
146
+ - **会话操作:**
147
+ - **单项停用:** 点击**停用**按钮,按 `activationId` 关闭指定项。
148
+ - **确认重置:** 点击**重置全部**后提供二次确认按钮(**确认重置** / **取消**),防止误操作清空会话指令。
149
+ - **提示词缓存用量:** 当当前分支存在提示词缓存数据时,面板展示本轮及整场会话的提示词缓存命中率与请求次数,并在界面上严格区分主会话请求与嵌套工具调用。详见[会话缓存用量](session-cache.md)。
150
+
151
+ ### 人类启用选择器(带守卫的预览与启用)
152
+
153
+ 面板内置了人类专用的能力启用选择器:
154
+
155
+ - **会话投影(`GET /api/capability-state/preview`):** 只读检查已启用的已保存预设及当前能力快照,编译前后核对 session/leaf/revision 及活动预设指纹。Preview 旁路元数据将真实投影更新块关联到 activation ID,不修改提示词消息。要求可信会话与已启用预设;缺失或过期明确报错,不准备请求、同步工具或证明提供商送达/缓存复用。
156
+ - **资源发现(`GET /api/capability-state/available`):** 纯本地只读接口,返回当前会话状态及可用能力选项,清晰分为*能力库项(未绑定)*与*当前预设(已绑定)*。读取操作绝不修改工具策略或产生会话事件。
157
+ - **即时预选预览:** 在下拉框中选择能力后,立即展开预选卡片呈现:
158
+ - 标签名称、ID、类别徽标(`能力库` 或 `预设绑定`)及正文指纹摘要(`#<hash>`)。
159
+ - 工具差异预览(`+add`、`-remove` 或 `无工具变更`)。
160
+ - 若该能力无法通过工具策略校验,显示异常警示条(Problem banner)。
161
+ - 完整的字面规则内容预览。
162
+ - **Bodyguard 安全启用(`POST /api/capability-state/enable`):**
163
+ - 提交载荷包含 `{ guard: { sessionId, leafId, revision }, kind, id, fingerprint }`。
164
+ - 服务端在执行前严格比对会话 Guard 与磁盘定义的最新内容指纹。
165
+ - 若会话、分支、版本或源文件发生变更,服务端返回 `409 Conflict` 拒绝操作。
166
+ - 绝不自动猜测或盲目重试。
167
+ - 启用操作需要项目已受信任(`isProjectTrusted() === true`),未信任会话返回 `403 Forbidden`。
168
+
169
+ ### 轮询策略、并发保护与安全机制
170
+
171
+ - **页面可见轮询与零推理开销:** 页面可见时每3秒静默读取 `GET /api/capability-state`,窗口获焦或主动刷新也会同步。能力目录只在进入工作区、主动刷新或相关操作后读取,不随每次轮询重载;无变化的后台检查不闪 loading 或禁用控件。会话投影由实际状态变化驱动,离开后忽略迟到回包。所有查询均为本地读取,不产生模型推理与 API token 开销。
172
+ - **错误与冲突时不盲目重试:** 遇到请求失败或 409 冲突时标记为 stale,需人工复核,不自动重试写操作。
173
+ - **项目信任要求:** 在 Web 端修改会话能力严格要求项目已受信任(`isProjectTrusted() === true`)。未受信任返回 `403 Forbidden`,命令行恢复通道保留。
174
+ - **Guard 防旧页与服务生命周期保护:** 每次修改均携带会话 Guard(`sessionId`、`leafId`、`revision`)。服务注销或不可用时返回 `503 Service Unavailable`。
175
+
176
+ ### 本地测试与宿主热加载
177
+
178
+ 在本地开发测试场(已有本地 build 接线)中调试时注意:
179
+ - 宿主热加载需要用户在 Pi 宿主控制台中手动执行 `/reload`。
180
+ - 若在 reload 之前已有 Web 服务器在运行,还需在宿主中执行 `/preset ui restart`,并使用新生成的带 token URL 打开。老 Web 服务器的闭包绑定在旧宿主生命周期上,不会随宿主刷新自动新增路由。切勿假定全局 host 已自动切换。
181
+
182
+ ## 生命周期与工具同步
183
+
184
+ - **统一的 `context_with_system` 流水线:** 在 Pi 0.87 中,标准 `context` 生命周期 hook 默认排除 System 消息。Forge 将完整编译器、基础提示词替换与能力投影流水线全部移至 `context_with_system`,无需内部两阶段拆分。
185
+ - **使用 `buildSessionProjection` 规范投影:** 运行时、预览与锚点定位基于 Pi 0.87 的规范 `buildSessionProjection`(支持 `context_edit` 的 omission、replacement 与 `sourceEntry`),确保瞬态请求拼装准确反映轮次上下文编辑,同时保持磁盘上的原始会话历史完全不变。
186
+ - **保持 Leading System 在首位:** SDK 传入的 leading System 始终保持在首位,Forge 自身的前缀纯元数据锚点紧跟其后插入。
187
+ - **续跑与结算生命周期:** `agent_end` 仍是安全的落锚提交时机,但编译周期与 busy fence 仅在 `agent_settled` 时重置。这确保了由 `agent_before_settle` 发起的继续执行(continuation)不会丢失已编译的 Preset 状态。
188
+ - **立即工具同步 vs. 下次请求提示词生效:**
189
+ - 可执行工具策略在能力激活时**立即同步**(`pi.setActiveTools()`),被策略禁用的工具在执行期立即被拦截。
190
+ - 提示词文本与原生 System 分段或 fallback 用户更新在**下一次模型请求边界**生效。
191
+ - 正在执行中的工具调用批次绝不会被中途终止。
192
+ - **顶层预设策略优先:** 能力不能越权启用已被当前预设 deny 策略禁用的工具。多个能力并存时,工具移除(remove)在当前会话的活动能力之间全局优先。
193
+ - **工具基线恢复:** 关闭能力后按预设默认工具与剩余能力重新计算;预设工具选择与能力叠加均不再生效时,恢复经过外部变更协调的会话基线。对缺乏记录的旧会话,Forge 采取保守策略恢复,而非盲目授予所有已注册工具。
194
+ - **Parent 生命周期与重入围栏:** 严格维护 `disposed` 标志、`lifecycleRevision` 计数器与 `sameContext` 会话校验,杜绝跨会话串号与注销后的非法重入。
195
+
196
+ ## 检查规则更新的预览
197
+
198
+ 现有 **Preview/预览** 按“选中的 Preset 草稿+当前会话能力快照”试算:
199
+
200
+ - 复用正常请求的纯预设/能力投影:显示原生 System 分段或带归属的 user 更新,包括停用通知和压缩检查点。
201
+ - 投递标记为纯元数据锚点,不作为对话消息展示。
202
+ - System 正文、原生命名分段与历史工具声明分开展示。代码区和分段复制只含正文/分段值,不把检查器生成的 `Added tool` 等提示混入原生文本。
203
+ - 历史工具声明折叠显示,并明确标注不是当前选择。
204
+ - 预览工具选择采用草稿策略+当前能力计算出的工具。
205
+ - 文本估算不含工具 schema 和检查器标签;投影后真正空掉的 System 卡片隐藏。
206
+ - 历史只按连续段分组:中途插入的能力更新保持在前后消息之间,不移到全部历史末尾。
207
+ - 纯只读检查:读取或复制预览内容不会发起推理、不会改变工具状态,也不会将 pending 标记为 prepared。全文不受原先消息布局 8,000 字符限制。
208
+
209
+ ## 投递模型:Native 与 Fallback
210
+
211
+ Forge 在每次发起模型请求时通过两阶段拼装动态投影能力增量:
212
+
213
+ 1. **编译前物化(Materialize):** 扫描通过 `buildSessionProjection` 规范投影后的会话条目中的纯 `custom` 元数据锚点(`pi-forge-capability-delivery`)。在请求上下文边界(`prepareCapabilityMessages`)校验元数据,并严格按其在投影转录中的序数位置物化为内存中的瞬态标记,绝不在磁盘转录中伪造对话消息或修改原始历史。
214
+ 2. **规则投影(Project):** 由 `projectCapabilityMessages` 将物化后的增量转化为适配当前模型的呈现形式:
215
+ - **Native 投递:** 当模型服务商声明支持会话中系统消息(`compat.supportsMidConvoSystemMessages === true`)时,增量以 `SystemMessage.sections`(以 `forge-capability-<id>` 为 key)注入,关闭时发送 null patch。Native 投递完全依赖服务商 capability 标记,并非所有提供商都支持。
216
+ - **Fallback 投递:** 对不支持原生系统更新的模型,增量以带来源标记的时间线用户消息(`[pi-forge capability update]`)投递。Forge 绝不折叠或篡改首条 leading system prompt,不把用户/工具对话提升为系统权限。
217
+ - **纯工具能力:** 正文为空或只有空白的能力在两条路径下都不发文字更新:启用时不发分段,停用时不发移除通知,compaction checkpoint 里也不包含它。只有工具变化进入请求,**当前会话**中也不提供“在上下文中定位”按钮。
218
+ - **如何判断走哪条路径:** 每次请求都按 Pi 模型目录中当前模型的 `compat` 条目决定;该目录由 Pi 拉取并缓存在本地,可能随更新变化。服务商名称、认证方式或认证扩展都不决定这一点,同一服务商的不同型号也可能不同。例如 2026-09-26 在 Pi 0.87.1 下观察到的目录中,`anthropic/claude-opus-4-8`、`claude-opus-5`、`claude-opus-5-5` 带有该标记,`anthropic/claude-sonnet-5` 没有。用 `/model` 切换后,之后的请求随之改变投递方式。`/capability status` 和 Agent 的 `status` 动作会显示 `native system sections` 或 `attributed user updates`;测试原生更新或缓存行为前请先确认。各 API 的行为和 Pi 0.87.1 时带标记的模型见[服务商支持情况](provider-support.md)。
219
+ - **Fallback 的附带影响:** 对未标记的模型,Pi 还会把它自己的工具变更声明折回首条 system 消息和顶层工具列表,因此工具变化会改写下一次请求的开头,这部分缓存前缀无法复用。部分模型可能把带标记的用户更新当作不可信文本,先质疑再使用新工具。
220
+
221
+ ## 状态恢复与验证边界
222
+
223
+ 能力状态由追加式会话事件与投递游标(`throughEventId`)推导恢复:
224
+
225
+ - **纯元数据投递锚点:** 投递标记以同名类型(`pi-forge-capability-delivery`)的纯 `custom` 会话条目持久化,仅包含 `{ schemaVersion: 1, throughEventId }` 游标元数据,不写入 `custom_message`,不使用 `sendMessage`。空闲变更立即落锚;运行中变更在工具批次完成后安全落锚;`agent_end` 在没有 Forge 上下文处理失败且批次完整时补齐未提交游标。UI 状态通知独立解耦,不向模型排入额外轮次。
226
+ - **SDK 离线验证:** 会话恢复、手动 compaction checkpoint 及分支切换(`session.navigateTree`)均已通过 SDK 本地测试套件离线验证。这验证了提示词拼装与工具门控逻辑,但不构成对远程模型实际遵从或服从程度的保证。
227
+ - **Compaction 与缓存:** 压缩断点(compaction checkpoint)位置保持不变(位于引导系统提示词之后、压缩摘要之前;上游 Pi 的元数据切分 bug 独立存在,当前尚未修复)。真实 SDK 压缩请求输入表征测试表明:新的控制元数据和仅请求内投影的规则**不会自动进入摘要输入**;选中摘要窗口内的真实用户、助手与 peer 对话不因这次修复被过滤,其中真实引用的规则文字仍保留。测试套件采用模拟假响应,仅用于验证请求组装与管线形状,不能等同于远程 LLM 实际语义压缩与遵从验收。
228
+ - **预发布破坏性边界:** 本次重命名不提供旧别名或旧读取器。使用旧指令模式键、Schema、目录或工具名的开发配置必须转换为新的能力名称;包含旧能力状态的会话不支持继续恢复该状态。Forge 不会改写旧 JSONL 或历史摘要;完成转换后请开启新会话,不要依赖部分恢复。
229
+ - **会话写盘时机:** 在仅输入斜杠命令的新会话中,在首个 assistant 回复产生前,Pi 可能尚未向磁盘写出 JSONL 会话条目。
230
+
231
+ ## 兼容性与安全边界
232
+
233
+ - **要求上游 Pi 0.87:** 必须使用上游 Pi `>=0.87.0 <0.88.0`。不提供对 0.86 的双重运行时支持。之前在 `context` hook 中操作或读取完整 System 消息的第三方扩展必须迁移到 `context_with_system` 完整 hook。
234
+ - **`before_agent_start` 注入时机:** `before_agent_start` 阶段强制注入的 System 提示词在 Pi 执行流中仍然晚于 `context_with_system` 生效。
235
+ - **前置扩展上下文改写:** Pi 允许 hook 改写消息。当可见元数据锚点或未锚定事件需要定位时,Forge 要求输入与规范会话投影有唯一的有序对应,否则中止而非猜测(fail-closed)。Pi 可能先保存排队的 custom 消息、但暂不放入工具续跑上下文:Forge 仅容许位置能唯一确定的 custom 消息缺省,忽略其重新生成的外层时间戳;保留传入对象,不擅自把缺省对话补回请求。前置改写因而可能与该定位方式冲突;简单后移不保证组合安全。通用插件、warming、自动 overflow 兼容性仍未全面验收。
236
+ - **上游缺陷与协议限制:** 上游 Pi 元数据分块及语义截断缺陷(semantic-cut defect)未被修复,压缩检查点位置保持不变。旧会话和旧投递载体保持原样,不支持继续恢复能力状态;Forge 不会迁移旧 JSONL 或改写历史摘要。不支持也不承诺 OMP(Oh My Pi)。
237
+ - **系统提示词 Getter:** `ctx.getSystemPrompt()` 和 SDK 接口返回 Pi 的原始基础提示词,而非 Forge 编译后的完整请求。请使用 `/forge payload`(裸 `/payload` 仍兼容)或 Run context diff 查看实际编译结果。Forge 不声称已同步 SDK getter。在生命周期 hook 中强行返回完整 `systemPrompt` 的第三方扩展会引发投影冲突,不被支持。
238
+ - **Provider 托管与缓存保守预警:** 工具传输序列化与提示词前缀缓存命中由下游提供商完全托管。工具策略变更、提示词前缀波动及会话压缩均会破坏缓存边界。支持追加的 Codex 传输在保留历史没有移除/重复声明时可追加全新工具以保留请求前缀,但不保证命中;移除或同名再声明会回退到当前全量工具表;Anthropic 原生工具变化等各 API 行为见[服务商支持情况](provider-support.md);pi-forge 提供保守的 Provider 托管与缓存预警,不提供权限绕过或缓存保障(不保证零 KV 缓存失效)。
239
+ - **沙盒免责:** 能力不提供操作系统级沙盒或权限隔离。示例 `review.json` 移除了 `bash`、`powershell`、`write` 和 `edit`,但未封禁外部 MCP 工具或 subagent,不能视为真正沙盒。请根据具体运行环境配置相应的执行工具移除列表。
240
+
241
+ ## 交付状态(0.5.5)
242
+
243
+ 0.5.5 核心功能源码已在所有规划的开发通道中全量交付:
244
+
245
+ - 能力基础编解码器、多范围解析、会话事件与不可变快照 Reducer。
246
+ - 人工 CLI 操作集(`/capability` add、list、bindings、enable、enable-bound、disable、status、reset)。
247
+ - 纯元数据锚点投影机制与编译前序数物化。
248
+ - Web 会话能力活动面板与带守卫的人类启用选择器(`GET /api/capability-state/available`, `POST /api/capability-state/enable`)。
249
+ - Live Preset 绑定在独立的同级“能力绑定”tab 中支持(`capabilities`),具备有限覆盖、折叠的高级面板及 `modelCallable: true` 显式授权。
250
+ - 限制级智能体控制工具 `forge_capability`(list、status、enable、disable,ID ≤ 128 字符)。
251
+ - Web 能力管理界面(Capabilities CRUD)支持 SDK 分组工具选择器与 `sourceRevision` 防脏写。
252
+ - Preset 策略新增自定义默认工具编辑器(`tools.initial?: string[]`),支持具体工具名、零默认工具(`[]`)以及缺省回退到传统逻辑。
253
+ - Parent 核心防护:源版本一致性保障、外部新增绑定防脏写检测、生命周期与重入安全围栏。
254
+ - 工具补丁仅支持 `add` 与 `remove`;候选的 `only`/allowlist 未实现。
255
+ - 提供保守的 Provider 托管与缓存预警;兼容的 Codex 传输在保留历史没有移除/重复声明时可追加全新工具以保留前缀(不保证缓存命中);移除/同名重加回退全量当前工具表;不进行自动旧数据迁移,不重写历史摘要;无 Pi split patch;不声称 forceprompt、warming、autooverflow 或远程模型验收保证。
256
+ - 2026-09-24 UI 收口时(提交 `89c6ba2`)本地构建、完整 Node/浏览器/打包验证已通过;这是有日期的本地结果,不是当前 CI 证据、远端提供商验收或 npm 已发布的证明。宿主仍要求 Pi `>=0.87.0 <0.88.0`;可选 runtime/subagent 集成及其生成者接入工作独立测试和发布。
257
+
258
+
259
+ 文件路径校验会拒绝检查时已存在的符号链接,但不能隔离另一个本地进程并发替换目录的攻击;源版本比较也不是跨进程锁。不要把资源编辑用于不可信进程可竞争修改的共享目录。
@@ -2,60 +2,88 @@
2
2
 
3
3
  [中文文档](../README.md) · [English](../../reference/commands.md)
4
4
 
5
- 方括号参数可选。写项目文件的命令要求项目已被信任。未限定的 `<id>` 使用项目优先的有效查找;需要精确选择时使用 `project:<id>` 或 `global:<id>`。
5
+ 方括号参数可选。主 pi-forge 命令会严格检查参数:未知 flag 和多余参数都会被拒绝。写入项目文件的命令要求项目已受信任。未限定的 `<id>` 使用项目优先查找;需要精确选择时使用 `project:<id>` 或 `global:<id>`。
6
6
 
7
- ## Prompt stack
7
+ ## Forge 命令
8
+
9
+ `/forge` 是推荐的根命令。不带参数时显示 Forge 帮助;`/forge help` 也一样。
10
+
11
+ | 命令 | 行为 |
12
+ |---|---|
13
+ | `/forge ui [stop\|restart]` | 打开、停止或明确重启本地 Web 编辑器。 |
14
+ | `/forge payload next [save=<path> [--overwrite]]` | 捕获下一个 provider hook payload,显示并可选保存。路径含空格时要加引号,例如 `save="path with spaces.json"`。 |
15
+ | `/forge payload status` | 显示是否有待处理捕获,以及是否已有最近捕获。 |
16
+ | `/forge payload cancel` | 只取消待处理的下一次捕获;不会删除或重置已保留的捕获或 context-diff 历史。 |
17
+ | `/forge payload help` | 显示 payload 语法和捕获边界。 |
18
+ | `/forge subagent ...` | 由匹配的 `@zihanw/pi-forge-subagents` 可选包提供的子命令。重复贡献者会 fail closed。 |
19
+
20
+ `/forge payload` 和裸 `/payload` 都会布置下一次捕获。`/intercept` 仍是同一“捕获但不保存”行为的兼容快捷方式。只有在 `save=<path>` 中显式提供 `--overwrite` 才会覆盖已有文件;布置捕获本身不会调用模型。捕获发生在 `before_provider_request`,所以后续插件改写可能使最终 wire body 不同。即使脱敏了类似 credential 的字段,保存内容仍可能包含 prompt 和对话文本。
21
+
22
+ 旧的 `/subagent` 命令是独立的底层 smoke helper,不是 `/forge subagent` 的执行计划命令。
23
+
24
+ ## Prompt preset
8
25
 
9
26
  | 命令 | 行为 |
10
27
  |---|---|
11
- | `/preset list` | 列出 stack 和状态 |
12
- | `/preset status` | 显示当前 stack 和诊断摘要 |
13
- | `/preset use <id>` | 校验并选择 stack |
14
- | `/preset use none` | 在当前 session branch 禁用 stack |
15
- | `/preset preview [id]` | 编译但不发送 provider 请求 |
16
- | `/preset validate [id]` | 校验一个或全部 stack |
17
- | `/preset diagnostics` | 显示 loader、runtime、policy、regex 和 extension 诊断 |
18
- | `/preset reload` | 重新加载 stack 和可信 macro/slot registration |
19
- | `/preset ui [stop\|restart]` | 打开或管理 Web 编辑器 |
20
-
21
- ## 迁移与导入
28
+ | `/preset list` | 列出 preset 以及启用/校验状态。 |
29
+ | `/preset status` | 显示选中的 preset 和诊断摘要。 |
30
+ | `/preset use <id>` | 校验并选择 preset。 |
31
+ | `/preset use none` | 在当前 session branch 禁用 preset;也接受 `disable`。 |
32
+ | `/preset preview [id]` | 编译并显示 preset,不发送 provider 请求;省略时使用选中的 preset。 |
33
+ | `/preset validate [id]` | 省略参数时校验当前选中的 preset;指定参数时只校验该 preset,默认不会校验全部 preset。 |
34
+ | `/preset diagnostics` | 显示 loader、runtime、policy、regex 和可信 extension 诊断。 |
35
+ | `/preset reload` | 重新加载 preset 及可信 macro/slot registration。 |
36
+ | `/preset ui [stop\|restart]` | 本地 Web 编辑器的兼容入口;推荐使用 `/forge ui`。 |
37
+ | `/preset help` | 显示 preset 命令帮助。 |
38
+
39
+ ## 存储迁移
22
40
 
23
41
  | 命令 | 行为 |
24
42
  |---|---|
25
- | `/preset migrate-stacks [--dry-run] [--overwrite] [--delete-legacy]` | 把旧 stack 复制到 `.pi/forge/prompt-stacks` |
43
+ | `/preset migrate-stacks [--dry-run] [--overwrite] [--delete-legacy]` | 将旧 `.pi/prompt-stacks` 文件复制到 `.pi/forge/prompt-stacks`。 |
26
44
 
27
- 覆盖或删除之前请先使用 `--dry-run`。
45
+ 覆盖或删除前请先使用 `--dry-run`。
28
46
 
29
47
  ## Agent profile
30
48
 
31
49
  | 命令 | 行为 |
32
50
  |---|---|
33
- | `/profile list` | 列出 profile 和解析诊断 |
34
- | `/profile use <id>` | preflight 并一次性应用 |
35
- | `/profile save <id\|global:id> [--overwrite]` | 捕获当前模型、thinking 和 stack;`global:<id>` 写入用户全局目录 |
36
- | `/profile status` | 比较当前 runtime 和 last-applied provenance |
37
- | `/profile preview <id>` | 不应用地解析模型/auth/thinking/stack/tools |
38
- | `/profile validate [id]` | 校验一个或全部 profile |
39
- | `/profile reload` | 重新加载定义,但不应用 |
40
- | `/profile forget` | 删除 provenance,不改变 runtime |
51
+ | `/profile list` | 列出 project profile 和解析诊断。 |
52
+ | `/profile use <id>` | preflight 并一次性应用 profile。 |
53
+ | `/profile save <id\|global:id> [--overwrite]` | 捕获当前模型、thinking 和 preset。 |
54
+ | `/profile status` | 比较当前 runtime 与 last-applied provenance。 |
55
+ | `/profile preview <id>` | 不应用地解析 model/auth/thinking/preset/tools。 |
56
+ | `/profile validate [id]` | 省略参数时校验全部已加载 profile;指定参数时只校验该 profile。 |
57
+ | `/profile reload` | 重新加载定义但不应用。 |
58
+ | `/profile forget` | 删除 last-applied provenance,不改变 runtime。 |
59
+ | `/profile help` | 显示 profile 命令帮助。 |
41
60
 
42
- ## 实验性 delegation
61
+ ## 能力
43
62
 
44
- 以下命令由可选包 `@zihanw/pi-forge-subagents` 提供。
63
+ `/capability` 用于管理会话能力。
45
64
 
46
65
  | 命令 | 行为 |
47
66
  |---|---|
48
- | `/forge-agent backends` | 列出 backend、capabilities 和默认值 |
49
- | `/forge-agent plan <profile> [--backend <id>] <task>` | 准备、显示并丢弃计划,不联系 provider |
50
- | `/forge-agent run <profile> [--backend <id>] <task>` | 审批并执行前台只读任务 |
67
+ | `/capability add <text>` | 添加字面手动指令。 |
68
+ | `/capability list` | 显式重新发现并列出能力库。 |
69
+ | `/capability bindings` | 列出当前 preset 的绑定,包括人类专用绑定(`modelCallable: false`)。 |
70
+ | `/capability enable <[scope:]id>` | 启用未绑定的能力库项。 |
71
+ | `/capability enable-bound <id>` | 启用当前 preset 的一个绑定。 |
72
+ | `/capability disable <activation-id>` | 停用一个活动能力。 |
73
+ | `/capability status` | 显示活动能力和生效工具。 |
74
+ | `/capability reset` | 停用全部活动能力和手动指令。 |
75
+ | `/capability help` | 显示用法和兼容性说明。 |
76
+
77
+ 补全使用当前 session 与最近发布的 workspace snapshot,不会在每次按键时扫描资源。需要显式刷新发现时使用 `/capability list`。`bindings` 仍显示人类专用绑定;`modelCallable: false` 只禁止 Agent 控制,不会把它从人类列表中过滤掉。
51
78
 
52
- 只接受明确 scope 的授权:请在 `subagents.json` 中使用 `project:<id>` 或 `global:<id>` key。裸授权 key 始终表示 `project:<id>`,即使它位于全局配置中;`.pi/forge/config.json.subagents` 仅作为只读兼容来源。模型工具为 `forge_subagent_profiles` 和 `forge_subagent`。见[安全说明](../guides/delegation.md)。
79
+ ## 可选前台委派
53
80
 
54
- ## Payload
81
+ 以下命令由匹配的可选包 `@zihanw/pi-forge-subagents` 提供,不属于主 pi-forge:
55
82
 
56
83
  | 命令 | 行为 |
57
84
  |---|---|
58
- | `/intercept` | 显示下一个脱敏 provider payload |
59
- | `/payload next [save=<path>]` | 显示并可选保存 payload,同时提供给 Web 编辑器 |
85
+ | `/forge-agent backends` | 列出已注册 backend、能力和有效默认值。 |
86
+ | `/forge-agent plan <profile> [--backend <id>] <task>` | 准备、显示并丢弃精确计划,不发送 provider 请求。 |
87
+ | `/forge-agent run <profile> [--backend <id>] <task>` | 准备前台运行,始终请求人类审批,然后通过所选 backend 执行。backend 可能写文件;它并非天然只读。 |
60
88
 
61
- 即使 credentials 字段被脱敏,保存的 payload 仍可能包含 prompt 和对话内容,请按敏感数据处理。
89
+ 可选包只接受明确 scope 的 profile:在其 `subagents.json` 中使用 `project:<id>` 或 `global:<id>` key。模型工具对应为 `forge_subagent_profiles`(本地发现)和 `forge_subagent`(执行)。模型可调用的 `forge_subagent` 工具是另一条路径。它是否可无人值守由可信项目中的显式授权控制;不能用来绕过 `/forge-agent run` 必须审批的要求。见[委派安全说明](../guides/delegation.md)。
@@ -0,0 +1,67 @@
1
+ # 各服务商对会话中更新的支持情况(Pi 0.87.1 快照)
2
+
3
+ [中文文档](../README.md) · [English](../../reference/provider-support.md)
4
+
5
+ 本页记录 Pi 0.87.1 在各 API 下如何发送会话中的 system 更新和工具变化,以及 Pi 模型目录中哪些模型带有相应标记。它解释了为什么同一个[能力](capabilities.md#投递模型native-与-fallback)在一个模型上以原生 system 更新送达,换到另一个模型却变成带标记的用户消息。
6
+
7
+ > **快照状态:** Pi 0.87.1(`@earendil-works/pi-ai` 0.87.1),本地模型目录最近检查于 2026-09-21 至 2026-09-25,记录于 2026-09-26。模型目录由 Pi 远程拉取并缓存在 `~/.pi/agent/models-store.json`,标记可能在不升级 Pi 的情况下变化。依赖下表之前,请先用 `/capability status` 确认当前模型走哪条路径。
8
+
9
+ ## 如何决定
10
+
11
+ Pi 和 Forge 在每次请求时都读取当前模型的 `compat` 标记:
12
+
13
+ | 标记 | 为 `true` 时 | 缺失或为 `false` 时 |
14
+ |---|---|---|
15
+ | `supportsMidConvoSystemMessages` | 后续 system 消息保留在对话中的原位置;Forge 以原生 system 分段发送指令正文。 | Pi 把所有 system 消息折回首条 system 提示词,并在请求级别发送当前工具列表;Forge 以带标记的 `[pi-forge capability update]` 用户消息发送指令正文。 |
16
+ | `supportsMidConvoToolChanges`(Anthropic Messages) | 工具增删以 `tool_addition` / `tool_removal` 块放在 system 更新中发送。 | 在请求级别发送完整的当前工具列表。 |
17
+ | `supportsAdditionalTools` / `supportsToolSearch`(OpenAI Responses、Codex、Azure) | 新工具在原位置加载(`additional_tools`,或客户端 tool search 的调用与结果)。 | 在请求级别发送完整的当前工具列表。 |
18
+ | `supportsMidConvoToolAdditions`(OpenAI Completions) | 新工具由一条带 `tools` 的 system 消息在原位置加载。 | 在请求级别发送完整的当前工具列表。 |
19
+
20
+ 工具相关标记只有在 `supportsMidConvoSystemMessages` 也启用时才生效。服务商名称、认证方式和认证扩展都不决定走哪条路径;同一个模型挂在不同服务商下,标记也可能不同。
21
+
22
+ ## 各 API 的行为
23
+
24
+ | API | 指令正文 | 工具变化 |
25
+ |---|---|---|
26
+ | `anthropic-messages` | 有标记时为 `role: "system"` 消息。Pi 会把它留到下一条 assistant 消息之前再发,因此不会插在 `tool_use` 和对应的 `tool_result` 之间;记录在用户消息之前的更新,实际会发在该用户消息之后。 | 有 `supportsMidConvoToolChanges`、至少一个初始工具且没有同名重定义时:初始工具保持在最前,之后的工具带 `defer_loading` 追加,变化以 `tool_addition` / `tool_removal` 块表示,请求级工具列表只增不减。否则在请求级别发送当前列表。 |
27
+ | `openai-responses`、`openai-codex-responses`、`azure-openai-responses` | 有标记时,在原位置发送 `developer` 消息(支持 developer 角色的推理模型)或 `system` 消息。 | 保留的历史中只有新增时,新工具在原位置加载;历史中任何位置出现过移除或同名重声明,这次请求就改为在请求级别发送完整的当前列表。 |
28
+ | `openai-completions` | 有标记时,在原位置发送 `developer` 或 `system` 消息。 | 有 `supportsMidConvoToolAdditions` 时新增在原位置加载;移除和重声明改为发送完整的当前列表。 |
29
+ | `mistral-conversations` | 有标记时,在原位置发送 `system` 消息。 | 始终在请求级别发送完整的当前列表。 |
30
+ | Google Generative AI / Vertex | 始终折入 `systemInstruction`,没有会话中路径。 | 完整的当前列表。 |
31
+ | Bedrock Converse | 始终折入请求级 system 提示词。 | 完整的当前工具配置。 |
32
+ | `pi-messages` | 原样把上下文交给后端,行为取决于该后端。 | 由后端决定。 |
33
+
34
+ 更新正文渲染为 `Updated system prompt section "<name>": ...` 或 `Removed system prompt section "<name>".`。[纯工具能力](capabilities.md#投递模型native-与-fallback)不发送文字更新。
35
+
36
+ ## 2026-09 目录中带标记的模型
37
+
38
+ 只列出 `supportsMidConvoSystemMessages: true` 的模型。
39
+
40
+ | 服务商 | 模型 | 工具变化 |
41
+ |---|---|---|
42
+ | `anthropic` | `claude-fable-5`、`claude-fable-5-1`、`claude-opus-4-8`、`claude-opus-5`、`claude-opus-5-5` | 原生增删 |
43
+ | `openai-codex` | `gpt-5.6-luna`、`gpt-5.6-sol`、`gpt-5.6-terra`、`gpt-6-astra`、`gpt-6-luna`、`gpt-6-sol` | 新增在原位置加载(`additional_tools`) |
44
+ | `openai-codex` | `gpt-5.5` | 新增通过 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` | 新增在原位置加载(`additional_tools`) |
46
+ | `opencode` | `claude-fable-5`、`claude-fable-5-1`、`claude-opus-4-8`、`claude-opus-5` | 仅正文;任何工具变化都发送完整列表 |
47
+ | `opencode`、`opencode-go` | `kimi-k3` | 新增在原位置加载 |
48
+ | `opencode-go` | `gpt-5.6-luna` | 新增在原位置加载(`additional_tools`) |
49
+ | `deepseek` | `deepseek-v4-pro` | 仅正文;任何工具变化都发送完整列表 |
50
+
51
+ 同一目录中没有标记的模型包括:`anthropic/claude-sonnet-5`、`claude-sonnet-4-5`、`claude-sonnet-4-6`、`claude-opus-4-5` 至 `claude-opus-4-7`、`claude-haiku-4-5`;`openai-codex/gpt-5.3-codex-spark`;`deepseek/deepseek-flash`;以及所有 `google` 和 `kimi-coding` 模型。
52
+
53
+ ## 实测缓存表现
54
+
55
+ 以下是单个会话中的观察,不构成保证。缓存是否复用由服务商决定。
56
+
57
+ - **`anthropic/claude-opus-5-5`,原生路径(通过认证扩展使用 OAuth):** 新增、移除、重新添加工具,包括模型自己启用的能力,18 次续轮请求全部完整复用了已缓存的前缀。命中率偏低的请求是在写入新内容,例如新的工具定义或大文件读取结果,并没有丢失之前的缓存。
58
+ - **`anthropic/claude-sonnet-5`,fallback 路径:** 工具变化后的那次请求没有读到任何缓存,因为 Pi 改写了首条 system 提示词和工具列表。模型还对带标记的用户更新产生了怀疑,确认后才使用新工具。
59
+ - **OpenAI Responses / Codex:** 之前的测试中,一次移除就改为发送完整工具列表,缓存读取降为 0;只有新增的请求保住了前缀。
60
+
61
+ 想让缓存稳定,优先选用带原生工具变化标记的模型;在 Responses 系列模型上,如果在意缓存复用,尽量不要在同一会话中移除工具。
62
+
63
+ ## 检查自己的环境
64
+
65
+ 1. 运行 `/capability status`,它会显示当前模型使用 `native system sections` 还是 `attributed user updates`。
66
+ 2. 如需直接查看标记,在 `~/.pi/agent/models-store.json` 中找到对应服务商下的模型,读取其 `compat` 对象。
67
+ 3. 如需确认实际发送格式,用 `/forge payload next` 捕获下一次真实请求。
@@ -0,0 +1,112 @@
1
+ # 会话缓存用量
2
+
3
+ [中文文档](../README.md) · [English](../../reference/session-cache.md)
4
+
5
+ 适用版本:Forge 0.5.5。嵌套用量契约为实验性,生成者接入由可选工具独立实现和发布。Forge 为当前活动会话分支汇总只读的提示词缓存(prompt-cache)用量指标。这些指标展示在 Web 编辑器的**当前会话**能力面板中,并包含在能力运行时的状态数据内。
6
+
7
+ ## 只读架构
8
+
9
+ 缓存统计数据完全由模型服务商已报告、且 Pi 已持久化在当前分支消息中的用量数据推导而来:
10
+
11
+ - **零副作用:** Forge 绝不为了用量统计发起模型推理、写入会话条目、修改提示词文本、插入 `cache_control` 断点或触发提示词预热(warming)。
12
+ - **复用现有轮询:** Web 客户端通过现有的页面可见性轮询(`GET /api/capability-state`)获取缓存指标,不增加任何额外轮询循环或网络定时器。
13
+ - **从根到叶的分支遍历:** 统计覆盖当前活动分支从根节点到当前叶节点的完整路径。
14
+ - **主会话请求(main):** 统计当前分支上带有非零持久化用量(`input + output + cacheRead + cacheWrite > 0`)的 `assistant` 消息。无用量报告的请求(如中止的操作或未返回用量数据的报错请求)予以忽略。
15
+ - **最近一次有用量的请求:** 最近一条带非零用量的 assistant 消息;之后中止或未报告用量的请求不会替换它。
16
+ - **本轮请求(turn):** 统计当前分支最新一条 `user` 消息之后出现的 assistant 请求。本轮内多步工具调用的多次模型请求合并累计。
17
+ - **会话范围(session):** 聚合当前活动分支上所有合规请求,包括分支上仍然保留的压缩前历史对话。
18
+ - **边界与统计排除:**
19
+ - 明确排除独立的 Pi 顶层 `usage` 事件、压缩记录(compaction)以及分支摘要(`branch_summary`)用量。
20
+ - Pi 可以单独持久化预热用量;这里有意只统计对话请求,不计预热或摘要开销。
21
+ - 本指标反映分支消息的提示词缓存效率,**不等于** Pi 的会话账单总额或全局账单统计。
22
+
23
+ ## 缓存命中率计算公式与聚合语义
24
+
25
+ ### 计算公式
26
+
27
+ 提示词缓存命中率衡量提示词前缀复用效率:
28
+
29
+ ```
30
+ cacheHitRate = cacheRead / (input + cacheRead + cacheWrite)
31
+ ```
32
+
33
+ - `input`:归一化的未缓存输入 Token(遵循 `pi-ai` 的 `Usage` 规范)。
34
+ - `cacheRead`:从服务商提示词缓存中读取的 Token 数。
35
+ - `cacheWrite`:写入服务商提示词缓存断点的 Token 数。
36
+ - `output`:模型输出补全 Token 严格排除在命中率分母之外。
37
+ - **分母为空 / 零:** 当提示词 Token 总和(`input + cacheRead + cacheWrite`)为 0 时,命中率未定义,界面显示破折号(`—`)。
38
+
39
+ ### 聚合规则
40
+
41
+ - **多模型混合:** 当同一轮或同一会话中运行不同模型时,整体命中率按 Token 绝对数值累加计算(缓存读取总数 / 提示词 Token 总数),**绝非**对各请求的命中百分比取算术平均值。
42
+ - **服务商报告差异:** 服务商返回的缓存计数为 0(`cacheRead: 0`, `cacheWrite: 0`)可能仅代表该服务商 API 不报告缓存统计,并不能作为服务商不支持缓存或未发生缓存的证据。
43
+
44
+ ## 主会话与嵌套工具严格分离
45
+
46
+ 用量严格划分为两个独立流:
47
+ 1. **主会话模型(`main`):** 当前分支上由主模型发起的直接请求。
48
+ 2. **嵌套工具(`nested`):** 内部发起模型调用的工具(如分身 subagent)所报告的用量。嵌套用量绝不混入 `main`。
49
+
50
+ 在 Web 界面中:
51
+ - 主会话与嵌套工具在独立行中显示(**缓存命中**与**分身/嵌套工具**)。
52
+ - 浮层提示(tooltip)仅展示**已知**数据的合并总计,并始终注明缺失、无效及缓存未知的报告不计入,不代表完整账单。
53
+
54
+ ## 实验性嵌套用量契约(`toolResult.details.forgeNestedUsage`)
55
+
56
+ 内部调用模型的工具可在其工具结果的 `toolResult.details.forgeNestedUsage`(常量 `FORGE_NESTED_USAGE_KEY`)中上报聚合 Token 与缓存指标。
57
+
58
+ ### 类型化导入
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 载荷示例
69
+
70
+ ```json
71
+ {
72
+ "role": "toolResult",
73
+ "toolCallId": "call_subagent_abc123",
74
+ "content": [{ "type": "text", "text": "分身任务完成。" }],
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
+ ### 契约规范与字段定义
89
+
90
+ | 字段 | 类型 | 必选 | 说明 |
91
+ |---|---|---|---|
92
+ | `schemaVersion` | `1` | 是 | 契约版本。必须为字面量数字 `1`。 |
93
+ | `requests` | `number` | 是 | 非负安全整数。工具调用期间内部发起的模型请求总数。仅当传入的所有 Token 计数均为 0 时,才允许为 `0`。 |
94
+ | `input` | `number` | 是 | 非负安全整数。归一化的未缓存输入 Token 数。 |
95
+ | `output` | `number` | 是 | 非负安全整数。生成的输出补全 Token 数。 |
96
+ | `cacheRead` | `number` | 可选 | 非负安全整数。从缓存读取的提示词 Token 数。必须与 `cacheWrite` 配对出现。 |
97
+ | `cacheWrite` | `number` | 可选 | 非负安全整数。写入缓存的提示词 Token 数。必须与 `cacheRead` 配对出现。 |
98
+
99
+ - **严格模式:** 不允许包含任何额外属性字段。
100
+ - **缓存字段配对规则:** `cacheRead` 与 `cacheWrite` 必须同时提供或同时省略。若生成者无法获知缓存数据,必须将两者同时省略。
101
+ - **单次调用聚合:** 最终工具结果必须针对该次工具调用提供单次聚合数据(包含其内部所有模型请求)。严禁分段进度快照或跨调用的重复累计终身总计。
102
+ - **递归汇总与去重职责:** 工具生成者自行负责多层嵌套子 agent 的去重与汇总。嵌套用量中绝对不能包含父级或主会话的 Token。
103
+ - **数据摄入与容错处理:**
104
+ - **未知缓存数据:** 省略 `cacheRead`/`cacheWrite` 的合法记录从所有请求数及 Token 合计中排除,并在 `cacheUnknownCalls` 中计数提示(`N 次调用未报告缓存数据`)。
105
+ - **格式无效记录:** 校验失败的载荷直接忽略,并在 `invalidCalls` 中计数提示(`N 条格式无效的报告已忽略`)。
106
+ - **字段缺失:** 未提供 `forgeNestedUsage` 视作无嵌套用量,Forge 不做旧格式挖掘或文本日志爬取。
107
+
108
+ ### 归属统计与 Pi 原生总计边界(重要)
109
+
110
+ - **仅供 Forge 归属展示:** `toolResult.details.forgeNestedUsage` 仅供 Forge 界面展示与用量归属统计,**绝不修改** Pi 内置的会话总计。
111
+ - **避免重复统计:** Pi 原生已支持 `toolResult.usage`。未来的生成者可在 `toolResult.usage` 填报底层用量供 Pi 计入总账,同时在 `toolResult.details.forgeNestedUsage` 填报供 Forge 缓存归属展示。Forge 汇总时不再累加顶层的 `toolResult.usage`,因而不会重复记账。
112
+ - **生态与运行时状态:** 运行时与 subagent 生成者接入留待后续更新。旧分身调用没有此字段时显示暂无数据,不回填历史。
@@ -0,0 +1,12 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "type": "pi-forge.capability",
4
+ "id": "review",
5
+ "name": "Review",
6
+ "description": "Report findings and evidence before editing.",
7
+ "content": "List findings, evidence, and risks. Do not directly edit files.",
8
+ "tools": {
9
+ "add": [],
10
+ "remove": ["bash", "powershell", "write", "edit"]
11
+ }
12
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "type": "pi-forge.capability",
4
+ "id": "write-tools",
5
+ "name": "Write tools",
6
+ "description": "Enable bash and edit for task commands/edits.",
7
+ "content": "Use bash and edit only as needed for the current task. Disable this capability when finished.",
8
+ "tools": {
9
+ "add": [
10
+ "bash",
11
+ "edit"
12
+ ],
13
+ "remove": []
14
+ }
15
+ }