@zihanw/pi-forge 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (341) hide show
  1. package/CHANGELOG.md +49 -1
  2. package/README.md +11 -11
  3. package/README.zh-CN.md +4 -6
  4. package/SUBAGENT_ADAPTER_CONTRACT.md +3 -1
  5. package/dist/agent-profile.d.ts +21 -42
  6. package/dist/agent-profile.d.ts.map +1 -1
  7. package/dist/agent-profile.js +82 -194
  8. package/dist/agent-profile.js.map +1 -1
  9. package/dist/catalog.d.ts +27 -0
  10. package/dist/catalog.d.ts.map +1 -0
  11. package/dist/catalog.js +59 -0
  12. package/dist/catalog.js.map +1 -0
  13. package/dist/codecs/agent-profile.d.ts +53 -0
  14. package/dist/codecs/agent-profile.d.ts.map +1 -0
  15. package/dist/codecs/agent-profile.js +176 -0
  16. package/dist/codecs/agent-profile.js.map +1 -0
  17. package/dist/codecs/index.d.ts +5 -0
  18. package/dist/codecs/index.d.ts.map +1 -0
  19. package/dist/codecs/index.js +3 -0
  20. package/dist/codecs/index.js.map +1 -0
  21. package/dist/codecs/prompt-stack.d.ts +18 -0
  22. package/dist/codecs/prompt-stack.d.ts.map +1 -0
  23. package/dist/codecs/prompt-stack.js +479 -0
  24. package/dist/codecs/prompt-stack.js.map +1 -0
  25. package/dist/compile-cycle.d.ts +18 -0
  26. package/dist/compile-cycle.d.ts.map +1 -0
  27. package/dist/compile-cycle.js +13 -0
  28. package/dist/compile-cycle.js.map +1 -0
  29. package/dist/compiler.d.ts +10 -4
  30. package/dist/compiler.d.ts.map +1 -1
  31. package/dist/compiler.js +53 -19
  32. package/dist/compiler.js.map +1 -1
  33. package/dist/extension-registry.d.ts.map +1 -1
  34. package/dist/extension-registry.js +5 -2
  35. package/dist/extension-registry.js.map +1 -1
  36. package/dist/forge-config.d.ts +2 -81
  37. package/dist/forge-config.d.ts.map +1 -1
  38. package/dist/forge-config.js +5 -280
  39. package/dist/forge-config.js.map +1 -1
  40. package/dist/forge-v1/analyzer.d.ts +3 -0
  41. package/dist/forge-v1/analyzer.d.ts.map +1 -0
  42. package/dist/forge-v1/analyzer.js +50 -0
  43. package/dist/forge-v1/analyzer.js.map +1 -0
  44. package/dist/forge-v1/index.d.ts +6 -0
  45. package/dist/forge-v1/index.d.ts.map +1 -0
  46. package/dist/forge-v1/index.js +13 -0
  47. package/dist/forge-v1/index.js.map +1 -0
  48. package/dist/forge-v1/parser.d.ts +3 -0
  49. package/dist/forge-v1/parser.d.ts.map +1 -0
  50. package/dist/forge-v1/parser.js +186 -0
  51. package/dist/forge-v1/parser.js.map +1 -0
  52. package/dist/forge-v1/renderer.d.ts +9 -0
  53. package/dist/forge-v1/renderer.d.ts.map +1 -0
  54. package/dist/forge-v1/renderer.js +181 -0
  55. package/dist/forge-v1/renderer.js.map +1 -0
  56. package/dist/forge-v1/types.d.ts +87 -0
  57. package/dist/forge-v1/types.d.ts.map +1 -0
  58. package/dist/forge-v1/types.js +4 -0
  59. package/dist/forge-v1/types.js.map +1 -0
  60. package/dist/index.d.ts +12 -9
  61. package/dist/index.d.ts.map +1 -1
  62. package/dist/index.js +49 -59
  63. package/dist/index.js.map +1 -1
  64. package/dist/lifecycle.d.ts +7 -5
  65. package/dist/lifecycle.d.ts.map +1 -1
  66. package/dist/lifecycle.js +44 -110
  67. package/dist/lifecycle.js.map +1 -1
  68. package/dist/loader.d.ts +16 -3
  69. package/dist/loader.d.ts.map +1 -1
  70. package/dist/loader.js +42 -440
  71. package/dist/loader.js.map +1 -1
  72. package/dist/macro-engine.d.ts +9 -14
  73. package/dist/macro-engine.d.ts.map +1 -1
  74. package/dist/macro-engine.js +5 -238
  75. package/dist/macro-engine.js.map +1 -1
  76. package/dist/payload-command.d.ts +6 -6
  77. package/dist/payload-command.d.ts.map +1 -1
  78. package/dist/payload-command.js.map +1 -1
  79. package/dist/payload-state.d.ts +16 -0
  80. package/dist/payload-state.d.ts.map +1 -0
  81. package/dist/payload-state.js +14 -0
  82. package/dist/payload-state.js.map +1 -0
  83. package/dist/preset-command.d.ts +4 -4
  84. package/dist/preset-command.d.ts.map +1 -1
  85. package/dist/preset-command.js +55 -98
  86. package/dist/preset-command.js.map +1 -1
  87. package/dist/preview.d.ts +4 -4
  88. package/dist/preview.d.ts.map +1 -1
  89. package/dist/preview.js +17 -8
  90. package/dist/preview.js.map +1 -1
  91. package/dist/profile-command.d.ts +2 -2
  92. package/dist/profile-command.d.ts.map +1 -1
  93. package/dist/profile-command.js +52 -26
  94. package/dist/profile-command.js.map +1 -1
  95. package/dist/profile-service.d.ts +5 -2
  96. package/dist/profile-service.d.ts.map +1 -1
  97. package/dist/profile-service.js +63 -42
  98. package/dist/profile-service.js.map +1 -1
  99. package/dist/prompt-analysis.d.ts +24 -0
  100. package/dist/prompt-analysis.d.ts.map +1 -0
  101. package/dist/prompt-analysis.js +84 -0
  102. package/dist/prompt-analysis.js.map +1 -0
  103. package/dist/prompt-runtime.d.ts +19 -0
  104. package/dist/prompt-runtime.d.ts.map +1 -0
  105. package/dist/prompt-runtime.js +30 -0
  106. package/dist/prompt-runtime.js.map +1 -0
  107. package/dist/regex.js +6 -12
  108. package/dist/regex.js.map +1 -1
  109. package/dist/render-helpers.d.ts +2 -10
  110. package/dist/render-helpers.d.ts.map +1 -1
  111. package/dist/render-helpers.js +3 -53
  112. package/dist/render-helpers.js.map +1 -1
  113. package/dist/repositories/agent-profile.d.ts +36 -0
  114. package/dist/repositories/agent-profile.d.ts.map +1 -0
  115. package/dist/repositories/agent-profile.js +153 -0
  116. package/dist/repositories/agent-profile.js.map +1 -0
  117. package/dist/repositories/index.d.ts +3 -0
  118. package/dist/repositories/index.d.ts.map +1 -0
  119. package/dist/repositories/index.js +3 -0
  120. package/dist/repositories/index.js.map +1 -0
  121. package/dist/repositories/prompt-stack.d.ts +57 -0
  122. package/dist/repositories/prompt-stack.d.ts.map +1 -0
  123. package/dist/repositories/prompt-stack.js +175 -0
  124. package/dist/repositories/prompt-stack.js.map +1 -0
  125. package/dist/resource-identity.d.ts +33 -0
  126. package/dist/resource-identity.d.ts.map +1 -0
  127. package/dist/resource-identity.js +56 -0
  128. package/dist/resource-identity.js.map +1 -0
  129. package/dist/runtime/profile-runtime.d.ts +2 -2
  130. package/dist/runtime/profile-runtime.d.ts.map +1 -1
  131. package/dist/runtime/profile-runtime.js +18 -10
  132. package/dist/runtime/profile-runtime.js.map +1 -1
  133. package/dist/runtime/prompt-stack-runtime.d.ts +5 -4
  134. package/dist/runtime/prompt-stack-runtime.d.ts.map +1 -1
  135. package/dist/runtime/prompt-stack-runtime.js +47 -52
  136. package/dist/runtime/prompt-stack-runtime.js.map +1 -1
  137. package/dist/runtime/tool-policy-runtime.d.ts +2 -2
  138. package/dist/runtime/tool-policy-runtime.d.ts.map +1 -1
  139. package/dist/runtime/tool-policy-runtime.js +3 -3
  140. package/dist/runtime/tool-policy-runtime.js.map +1 -1
  141. package/dist/session-adapter.d.ts +17 -0
  142. package/dist/session-adapter.d.ts.map +1 -0
  143. package/dist/session-adapter.js +57 -0
  144. package/dist/session-adapter.js.map +1 -0
  145. package/dist/slot-renderers.d.ts +16 -3
  146. package/dist/slot-renderers.d.ts.map +1 -1
  147. package/dist/slot-renderers.js +15 -63
  148. package/dist/slot-renderers.js.map +1 -1
  149. package/dist/stack-migration.d.ts +6 -0
  150. package/dist/stack-migration.d.ts.map +1 -1
  151. package/dist/stack-migration.js +49 -52
  152. package/dist/stack-migration.js.map +1 -1
  153. package/dist/storage.d.ts +10 -0
  154. package/dist/storage.d.ts.map +1 -1
  155. package/dist/storage.js +45 -10
  156. package/dist/storage.js.map +1 -1
  157. package/dist/subagent/fingerprints.d.ts +24 -0
  158. package/dist/subagent/fingerprints.d.ts.map +1 -0
  159. package/dist/subagent/fingerprints.js +81 -0
  160. package/dist/subagent/fingerprints.js.map +1 -0
  161. package/dist/subagent/host-port.d.ts +296 -0
  162. package/dist/subagent/host-port.d.ts.map +1 -0
  163. package/dist/subagent/host-port.js +560 -0
  164. package/dist/subagent/host-port.js.map +1 -0
  165. package/dist/subagent/index.d.ts +10 -10
  166. package/dist/subagent/index.d.ts.map +1 -1
  167. package/dist/subagent/index.js +9 -10
  168. package/dist/subagent/index.js.map +1 -1
  169. package/dist/subagent-host.d.ts +99 -18
  170. package/dist/subagent-host.d.ts.map +1 -1
  171. package/dist/subagent-host.js +185 -162
  172. package/dist/subagent-host.js.map +1 -1
  173. package/dist/template-render.d.ts +18 -0
  174. package/dist/template-render.d.ts.map +1 -0
  175. package/dist/template-render.js +232 -0
  176. package/dist/template-render.js.map +1 -0
  177. package/dist/types.d.ts +43 -27
  178. package/dist/types.d.ts.map +1 -1
  179. package/dist/types.js.map +1 -1
  180. package/dist/web-editor/client-script.generated.d.ts.map +1 -1
  181. package/dist/web-editor/client-script.generated.js +1 -1
  182. package/dist/web-editor/client-script.generated.js.map +1 -1
  183. package/dist/web-editor/client-styles.generated.d.ts.map +1 -1
  184. package/dist/web-editor/client-styles.generated.js +1 -1
  185. package/dist/web-editor/client-styles.generated.js.map +1 -1
  186. package/dist/web-editor/server.d.ts.map +1 -1
  187. package/dist/web-editor/server.js +32 -72
  188. package/dist/web-editor/server.js.map +1 -1
  189. package/dist/web-editor/styles.d.ts.map +1 -1
  190. package/dist/web-editor/styles.js +13 -0
  191. package/dist/web-editor/styles.js.map +1 -1
  192. package/dist/web-editor/types.d.ts +10 -37
  193. package/dist/web-editor/types.d.ts.map +1 -1
  194. package/dist/web-host.d.ts +4 -2
  195. package/dist/web-host.d.ts.map +1 -1
  196. package/dist/web-host.js +110 -138
  197. package/dist/web-host.js.map +1 -1
  198. package/dist/workspace.d.ts +74 -0
  199. package/dist/workspace.d.ts.map +1 -0
  200. package/dist/workspace.js +351 -0
  201. package/dist/workspace.js.map +1 -0
  202. package/docs/README.md +6 -4
  203. package/docs/concepts/agent-profiles.md +2 -2
  204. package/docs/concepts/prompt-stacks.md +12 -7
  205. package/docs/design/README.md +21 -3
  206. package/docs/design/architecture-0.5.md +211 -0
  207. package/docs/design/archive/0.5-full-proposal/0.5-consumer-audit.md +62 -0
  208. package/docs/design/archive/0.5-full-proposal/0.5-inventory.md +208 -0
  209. package/docs/design/archive/0.5-full-proposal/0.5-phase0-decision-drafts.md +237 -0
  210. package/docs/design/archive/0.5-full-proposal/README.md +12 -0
  211. package/docs/design/archive/0.5-full-proposal/architecture-0.5.md +423 -0
  212. package/docs/design/archive/0.5-full-proposal/host-discovery-spike.md +103 -0
  213. package/docs/design/archive/0.5-full-proposal/template-language-spike.md +156 -0
  214. package/docs/design/context-diff-plan.md +72 -0
  215. package/docs/design/decision-template.md +33 -0
  216. package/docs/design/roadmap-0.4-archive.md +1 -1
  217. package/docs/design/subagents/interface-design.md +1 -1
  218. package/docs/development/architecture-rules.md +148 -0
  219. package/docs/development/roadmap.md +39 -13
  220. package/docs/development/scoped-global-profiles-stacks.md +325 -0
  221. package/docs/development/setup.md +6 -4
  222. package/docs/getting-started.md +9 -6
  223. package/docs/guides/custom-macros-and-slots.md +8 -7
  224. package/docs/guides/debugging.md +1 -1
  225. package/docs/guides/delegation.md +25 -21
  226. package/docs/guides/migrating-to-0.5.md +107 -0
  227. package/docs/guides/use-cases.md +7 -11
  228. package/docs/guides/web-editor.md +7 -11
  229. package/docs/reference/commands.md +4 -3
  230. package/docs/reference/configuration.md +18 -17
  231. package/docs/reference/features.md +34 -79
  232. package/docs/reference/macros-and-slots.md +70 -49
  233. package/docs/reference/public-api.md +64 -17
  234. package/docs/reference/stack-schema.md +14 -9
  235. package/docs/reference/subagent-host-port.md +49 -0
  236. package/docs/zh-CN/README.md +3 -3
  237. package/docs/zh-CN/concepts/agent-profiles.md +4 -2
  238. package/docs/zh-CN/concepts/prompt-stacks.md +9 -7
  239. package/docs/zh-CN/getting-started.md +10 -7
  240. package/docs/zh-CN/guides/delegation.md +11 -13
  241. package/docs/zh-CN/guides/migrating-to-0.5.md +92 -0
  242. package/docs/zh-CN/guides/web-editor.md +7 -7
  243. package/docs/zh-CN/reference/commands.md +6 -3
  244. package/examples/custom-system-status-extension/README.md +3 -3
  245. package/examples/custom-system-status-extension/index.ts +2 -1
  246. package/examples/custom-system-status-extension/prompt-stack.json +4 -3
  247. package/examples/default-prompt-stack.json +9 -4
  248. package/examples/image-reader-prompt-stack.json +16 -26
  249. package/examples/reviewer-prompt-stack.json +13 -24
  250. package/package.json +101 -120
  251. package/dist/runtime/subagent-runtime.d.ts +0 -45
  252. package/dist/runtime/subagent-runtime.d.ts.map +0 -1
  253. package/dist/runtime/subagent-runtime.js +0 -335
  254. package/dist/runtime/subagent-runtime.js.map +0 -1
  255. package/dist/runtime-state.d.ts +0 -30
  256. package/dist/runtime-state.d.ts.map +0 -1
  257. package/dist/runtime-state.js +0 -17
  258. package/dist/runtime-state.js.map +0 -1
  259. package/dist/sillytavern-importer/items.d.ts +0 -3
  260. package/dist/sillytavern-importer/items.d.ts.map +0 -1
  261. package/dist/sillytavern-importer/items.js +0 -88
  262. package/dist/sillytavern-importer/items.js.map +0 -1
  263. package/dist/sillytavern-importer/macros.d.ts +0 -15
  264. package/dist/sillytavern-importer/macros.d.ts.map +0 -1
  265. package/dist/sillytavern-importer/macros.js +0 -141
  266. package/dist/sillytavern-importer/macros.js.map +0 -1
  267. package/dist/sillytavern-importer/prompt-order.d.ts +0 -6
  268. package/dist/sillytavern-importer/prompt-order.d.ts.map +0 -1
  269. package/dist/sillytavern-importer/prompt-order.js +0 -38
  270. package/dist/sillytavern-importer/prompt-order.js.map +0 -1
  271. package/dist/sillytavern-importer/regex.d.ts +0 -3
  272. package/dist/sillytavern-importer/regex.d.ts.map +0 -1
  273. package/dist/sillytavern-importer/regex.js +0 -275
  274. package/dist/sillytavern-importer/regex.js.map +0 -1
  275. package/dist/sillytavern-importer/report.d.ts +0 -21
  276. package/dist/sillytavern-importer/report.d.ts.map +0 -1
  277. package/dist/sillytavern-importer/report.js +0 -166
  278. package/dist/sillytavern-importer/report.js.map +0 -1
  279. package/dist/sillytavern-importer/types.d.ts +0 -106
  280. package/dist/sillytavern-importer/types.d.ts.map +0 -1
  281. package/dist/sillytavern-importer/types.js +0 -2
  282. package/dist/sillytavern-importer/types.js.map +0 -1
  283. package/dist/sillytavern-importer.d.ts +0 -5
  284. package/dist/sillytavern-importer.d.ts.map +0 -1
  285. package/dist/sillytavern-importer.js +0 -117
  286. package/dist/sillytavern-importer.js.map +0 -1
  287. package/dist/subagent/canonical.d.ts +0 -22
  288. package/dist/subagent/canonical.d.ts.map +0 -1
  289. package/dist/subagent/canonical.js +0 -24
  290. package/dist/subagent/canonical.js.map +0 -1
  291. package/dist/subagent/context.d.ts +0 -8
  292. package/dist/subagent/context.d.ts.map +0 -1
  293. package/dist/subagent/context.js +0 -125
  294. package/dist/subagent/context.js.map +0 -1
  295. package/dist/subagent/contract.d.ts +0 -10
  296. package/dist/subagent/contract.d.ts.map +0 -1
  297. package/dist/subagent/contract.js +0 -10
  298. package/dist/subagent/contract.js.map +0 -1
  299. package/dist/subagent/plan.d.ts +0 -18
  300. package/dist/subagent/plan.d.ts.map +0 -1
  301. package/dist/subagent/plan.js +0 -157
  302. package/dist/subagent/plan.js.map +0 -1
  303. package/dist/subagent/preflight.d.ts +0 -4
  304. package/dist/subagent/preflight.d.ts.map +0 -1
  305. package/dist/subagent/preflight.js +0 -108
  306. package/dist/subagent/preflight.js.map +0 -1
  307. package/dist/subagent/request.d.ts +0 -4
  308. package/dist/subagent/request.d.ts.map +0 -1
  309. package/dist/subagent/request.js +0 -122
  310. package/dist/subagent/request.js.map +0 -1
  311. package/dist/subagent/response.d.ts +0 -8
  312. package/dist/subagent/response.d.ts.map +0 -1
  313. package/dist/subagent/response.js +0 -155
  314. package/dist/subagent/response.js.map +0 -1
  315. package/dist/subagent/tools.d.ts +0 -4
  316. package/dist/subagent/tools.d.ts.map +0 -1
  317. package/dist/subagent/tools.js +0 -42
  318. package/dist/subagent/tools.js.map +0 -1
  319. package/dist/subagent/types.d.ts +0 -268
  320. package/dist/subagent/types.d.ts.map +0 -1
  321. package/dist/subagent/types.js +0 -3
  322. package/dist/subagent/types.js.map +0 -1
  323. package/dist/subagent/validation.d.ts +0 -35
  324. package/dist/subagent/validation.d.ts.map +0 -1
  325. package/dist/subagent/validation.js +0 -314
  326. package/dist/subagent/validation.js.map +0 -1
  327. package/dist/subagent-command.d.ts +0 -4
  328. package/dist/subagent-command.d.ts.map +0 -1
  329. package/dist/subagent-command.js +0 -246
  330. package/dist/subagent-command.js.map +0 -1
  331. package/dist/subagent-profile-tool.d.ts +0 -49
  332. package/dist/subagent-profile-tool.d.ts.map +0 -1
  333. package/dist/subagent-profile-tool.js +0 -124
  334. package/dist/subagent-profile-tool.js.map +0 -1
  335. package/dist/subagent-tool.d.ts +0 -53
  336. package/dist/subagent-tool.d.ts.map +0 -1
  337. package/dist/subagent-tool.js +0 -456
  338. package/dist/subagent-tool.js.map +0 -1
  339. package/docs/guides/sillytavern-import.md +0 -47
  340. package/docs/reference/subagent-adapter.md +0 -204
  341. package/examples/sillytavern-dm-writer-prompt-stack.json +0 -190
@@ -0,0 +1,325 @@
1
+ # Scoped global profiles and prompt stacks
2
+
3
+ [Documentation](../README.md) · [Roadmap](roadmap.md)
4
+
5
+ Status: accepted design; implementation complete for 0.4.1 (identity, catalogs, scoped stacks/profiles, persistence, auto-activation, delegation, zh-CN docs, and web-editor global create/fork/import with explicit scope). Historical record: the delegation-policy references to `.pi/forge/config.json` `subagents.*` below are superseded in 0.5 — delegation configuration moved to the optional package's dedicated `subagents.json` files (see the [0.5 migration guide](../guides/migrating-to-0.5.md)).
6
+
7
+ ## Confirmed design decisions
8
+
9
+ Recorded before implementation started; these resolve the options left open by the analysis above.
10
+
11
+ - **D1** Unified resource ID grammar: `/^[A-Za-z0-9][A-Za-z0-9._-]*$/` for both profiles and stacks. `:` is reserved for scope qualification and rejected in JSON IDs (with a diagnostic, not a load failure).
12
+ - **D2** When capturing/saving a profile, `promptStack` is serialized relative to the target profile scope: same scope writes a bare ID, a different scope writes a qualified selector (`global:<id>`), and a global profile is never allowed to reference a project stack (the save is rejected).
13
+ - **D3** Global profiles/stacks are loaded and can be browsed/previewed in untrusted projects, but applying a profile or stack still requires project trust.
14
+ - **D4** Subagent delegation remains entirely disabled in untrusted projects, including for global profiles.
15
+ - **D5** `default.json` has no special auto-activation role anymore. Only `autoActivate: true` participates in standalone stack activation. A `default.json` without an explicit `autoActivate` field receives a one-time migration warning; `autoActivate: false` remains an explicit opt-out. Multiple `autoActivate: true` stacks in the same scope fail closed.
16
+ - **D6** Legacy provenance records without a scope field are interpreted as project-scoped.
17
+ - **D7** `forge_subagent` accepts a bare `profileId` through effective lookup (project first) and `global:<id>` for exact global access; tool descriptions and `forge_subagent_profiles` explain the canonical selectors.
18
+ - **D8** New session entries and provenance persist the string selector form (`project:<id>` / `global:<id>`); `none` stays a scope-independent bare opt-out.
19
+ - **D9** Runtime state keeps flat `Loaded*[]` arrays whose entries carry `scope`/`key`; a shared catalog module owns `all`/`effective`/exact resolution.
20
+ - **D10** Web editor API routes encode scope as a single path segment (`scope:id`, e.g. `/api/stacks/global:reviewer`).
21
+ - **D11** A new `PI_FORGE_GLOBAL_DIR` environment override replaces the real `~/.pi/forge` for tests and embedders; loaders accept an explicit global Forge directory, and `globalForgeDir()` is only the default.
22
+ - **D12** Slice 5 is split into 5a (forge-config data model, scoped authorization, and unit tests) and 5b (discovery, tool descriptions, approval display, commands, docs, and browser verification).
23
+ - **D13** The web editor may edit/delete global resources when the route uses an explicit scope; unqualified routes remain project-only.
24
+
25
+
26
+ This note defines the resource identity, lookup, activation, persistence, and delegation model for adding user-global agent profiles and prompt stacks without weakening project shadowing or authorization boundaries.
27
+
28
+ ## Goals
29
+
30
+ - Reuse profiles and prompt stacks across projects.
31
+ - Keep common commands concise through project-over-global shadowing.
32
+ - Preserve an explicit way to address either scope when IDs collide.
33
+ - Make stored profile dependencies deterministic rather than dependent on later shadowing.
34
+ - Keep global and project delegation authorization separate.
35
+ - Preserve existing project-only files and unqualified commands where no collision exists.
36
+
37
+ ## Non-goals
38
+
39
+ - Do not add inheritance or merging between two profile or stack definitions.
40
+ - Do not make profiles continuously own runtime state.
41
+ - Do not move tool or skill policy from prompt stacks into profiles.
42
+ - Do not let a global profile depend on project-controlled prompt content.
43
+ - Do not add writable subagents, compaction settings, or a concurrency cap as part of this feature.
44
+
45
+ ## Storage and identity
46
+
47
+ Global resources use the existing user-owned Forge root:
48
+
49
+ ```text
50
+ ~/.pi/forge/prompt-stacks/*.json
51
+ ~/.pi/forge/agent-profiles/*.json
52
+ ```
53
+
54
+ Project resources keep their current locations:
55
+
56
+ ```text
57
+ <project>/.pi/forge/prompt-stacks/*.json
58
+ <project>/.pi/forge/agent-profiles/*.json
59
+ ```
60
+
61
+ The JSON `id` remains an unqualified value such as `reviewer`. Scope comes from the storage location and must not be duplicated as an editable field in the file.
62
+
63
+ Every loaded resource must carry a first-class scoped identity:
64
+
65
+ ```ts
66
+ type ResourceScope = "global" | "project";
67
+
68
+ interface ResourceKey {
69
+ scope: ResourceScope;
70
+ id: string;
71
+ }
72
+ ```
73
+
74
+ Do not repeatedly infer scope from `filePath`. Loaded profiles and stacks should expose their scope/key directly, and runtime state should compare keys rather than bare IDs.
75
+
76
+ Duplicate IDs are errors only within one scope. A project and global resource with the same ID form a valid shadow pair.
77
+
78
+ ## Selector syntax
79
+
80
+ Commands, completion candidates, profile stack references, persisted diagnostics, and delegation discovery use one selector grammar:
81
+
82
+ ```text
83
+ reviewer
84
+ project:reviewer
85
+ global:reviewer
86
+ ```
87
+
88
+ Existing resource IDs cannot contain `:`, so qualification is unambiguous. Reject unknown scope prefixes, empty IDs, and malformed selectors with a specific diagnostic.
89
+
90
+ The canonical formatter should return:
91
+
92
+ - `reviewer` only when presenting the effective unqualified resource;
93
+ - `project:reviewer` or `global:reviewer` when exact identity matters;
94
+ - qualified selectors for both resources when a collision must be shown.
95
+
96
+ Internally, parse selectors into `{ scope?: ResourceScope; id: string }` and resolve them to a required `ResourceKey` before mutation, application, persistence, or delegation.
97
+
98
+ ## Two lookup modes
99
+
100
+ Unqualified interactive selection and unqualified profile dependencies deliberately use different lookup rules.
101
+
102
+ ### Effective lookup
103
+
104
+ Commands such as `/preset use` and `/profile use` use effective lookup:
105
+
106
+ 1. A qualified selector resolves only the named scope.
107
+ 2. An unqualified selector resolves the project resource when present.
108
+ 3. Otherwise it resolves the global resource.
109
+ 4. A project definition shadows the same-ID global definition even when the project definition is invalid. The invalid shadow fails closed; it must not silently fall back to the global resource.
110
+
111
+ Examples:
112
+
113
+ ```text
114
+ /profile use reviewer # project first, otherwise global
115
+ /profile use global:reviewer # exact global profile
116
+ /preset use project:reviewer # exact project stack
117
+ ```
118
+
119
+ Both colliding resources remain inspectable and explicitly selectable. Shadowing is an ergonomic default, not deletion of the global definition.
120
+
121
+ ### Profile dependency lookup
122
+
123
+ A profile's `promptStack` is a stored dependency and must remain stable when unrelated files are later added. Its unqualified reference therefore resolves relative to the profile's own scope rather than through effective lookup.
124
+
125
+ | Profile scope | `"reviewer"` | `"project:reviewer"` | `"global:reviewer"` |
126
+ |---|---|---|---|
127
+ | Project | project stack | project stack | global stack |
128
+ | Global | global stack | rejected | global stack |
129
+
130
+ Consequences:
131
+
132
+ - A project profile may use either a project stack or an explicitly qualified global stack.
133
+ - A project profile with `"promptStack": "reviewer"` does not fall back to a global stack when the project stack is missing. The diagnostic should suggest `global:reviewer` when that global stack exists.
134
+ - A global profile may use only global stacks. `project:*` is rejected even if the project is trusted.
135
+ - Adding a project stack later cannot silently change the dependency of a project profile that explicitly uses `global:reviewer`.
136
+ - A project cannot replace the prompt or tool policy of a user-authorized global profile.
137
+
138
+ The string form remains sufficient for profile schema v1 because qualification expands the accepted reference syntax without adding a new field or object shape. Existing project profiles keep resolving their unqualified stack IDs to project stacks.
139
+
140
+ ## Loading and catalogs
141
+
142
+ Global definitions are user-owned and may load independently of project trust. Project definitions load only when the project is trusted.
143
+
144
+ Pure loaders and tests must not accidentally read the developer's real home directory. Prefer an explicit catalog/repository input for the global Forge directory over relying on a process-wide environment variable. Existing project-only public loader functions may remain as compatibility wrappers while the extension runtime adopts scoped catalog loaders.
145
+
146
+ Maintain both views:
147
+
148
+ - `all`: every loaded scoped definition, including shadowed resources;
149
+ - `effective`: one resource per unqualified ID after project-over-global shadowing.
150
+
151
+ Centralize resolution helpers instead of continuing to use bare `array.find(candidate.id === id)` calls. Application, preview, mutation, auto-activation, subagent preparation, and the editor must resolve through the same catalog semantics.
152
+
153
+ ## Selection and persistence
154
+
155
+ New session entries must persist the exact scoped key, not just the bare ID. This prevents a restored global selection from changing to a newly created project shadow after reload or tree navigation.
156
+
157
+ For compatibility:
158
+
159
+ - Read legacy branch entries containing only `activeStackId` using effective lookup.
160
+ - Write new entries with a scoped active-stack reference.
161
+ - Preserve the explicit `none`/`off` selection as a scope-independent opt-out.
162
+ - Profile provenance should add the profile scope/key while continuing to accept older provenance that has only `profileId` and `sourcePath`.
163
+ - Drift snapshots should store the resolved scoped stack reference so status can distinguish definition changes from a scope change.
164
+
165
+ Profile application remains transactional and one-shot. Later manual model/thinking changes remain respected; the selected stack continues enforcing its existing policy exactly as it does now.
166
+
167
+ ## Auto-activation
168
+
169
+ Restored branch state and explicit opt-out continue to take precedence over all fresh-session defaults.
170
+
171
+ For agent profiles:
172
+
173
+ 1. Inspect project `autoActivate: true` candidates first.
174
+ 2. If any project candidate exists, do not fall back to a global candidate.
175
+ 3. Exactly one usable project candidate applies; an invalid or ambiguous project selection fails closed.
176
+ 4. If no project candidate exists, apply the same rules to global candidates.
177
+ 5. A project and global auto-activation candidate are not a conflict because project scope has explicit precedence.
178
+
179
+ For standalone prompt-stack activation, apply the same scope precedence after profile auto-activation has declined to select a profile:
180
+
181
+ 1. Only stacks with `autoActivate: true` participate; `default.json` has no filename-based activation role (D5).
182
+ 2. Resolve project candidates before global candidates.
183
+ 3. A same-ID project stack shadows the global stack, including an invalid shadow or `autoActivate: false` opt-out.
184
+ 4. Exactly one usable candidate applies; multiple `autoActivate: true` stacks in the same scope fail closed.
185
+ 5. Do not silently activate the global same-ID stack through an invalid or opted-out project shadow.
186
+ 6. Preserve the existing rule that agent-profile auto-activation takes precedence over standalone stack activation, including profiles whose `promptStack` is `null`.
187
+
188
+ ## Commands and editor behavior
189
+
190
+ Unqualified commands use effective lookup. Qualified commands address exact scope.
191
+
192
+ Recommended command behavior:
193
+
194
+ ```text
195
+ /preset use reviewer
196
+ /preset use global:reviewer
197
+ /profile use project:reviewer
198
+ /profile preview global:reviewer
199
+ /profile save reviewer # project by default
200
+ /profile save global:reviewer # explicit global mutation
201
+ ```
202
+
203
+ Mutations must resolve the target scope before computing a path. Existing overwrite, changed-file, containment, and symbolic-link protections apply independently to each storage root. Global mutation must always be explicit; an unqualified save/fork/import continues to target project storage.
204
+
205
+ The web editor should:
206
+
207
+ - show global/project badges on profiles and stacks;
208
+ - show `shadows global:<id>` and `shadowed by project:<id>` states;
209
+ - retain both resources in navigation when IDs collide;
210
+ - require explicit scope for create, fork, import, save-as, and delete targets;
211
+ - prevent editing a shadowed global resource through an unqualified project route;
212
+ - keep the current token, trust, idle-state, overwrite, and browser-test protections for mutations.
213
+
214
+ Completions may prefer concise unqualified effective selectors, but must also offer qualified selectors when a collision exists or when the user has begun typing a scope prefix.
215
+
216
+ ## Delegation boundary
217
+
218
+ Delegation authorization follows the profile's scope, not the effective unqualified ID:
219
+
220
+ - `~/.pi/forge/config.json` `subagents.profiles.<id>` authorizes only `global:<id>`.
221
+ - `<project>/.pi/forge/config.json` `subagents.profiles.<id>` authorizes only `project:<id>`.
222
+ - Same-ID global and project profiles never inherit enablement, backend, or timeout policy from one another.
223
+ - General backend, timeout, and summary defaults retain their current global-then-project layering.
224
+ - Deleting a profile clears delegation policy only from the matching scope's config.
225
+ - A global profile remains bound to global prompt content even when a project profile or stack shadows the same ID.
226
+
227
+ `forge_subagent_profiles` should return canonical callable selectors. When both scopes expose the same ID, the project profile may use the concise effective selector and the global profile must remain callable as `global:<id>`. The `forge_subagent` parameter, `/forge-agent` commands, embedded description summary, preparation lookup, approval display, fingerprints, and reports must retain exact scope.
228
+
229
+ Global profiles are user-owned, but ordinary delegation safety does not change: profiles remain disabled unless authorized in the matching config, unattended invocation remains a separate trusted-project decision, and read-only still does not imply an OS sandbox.
230
+
231
+ ## Compatibility and migration
232
+
233
+ - No files move automatically.
234
+ - Existing `.pi/forge` and legacy `.pi/prompt-stacks` resources remain project-scoped.
235
+ - Existing project profile `promptStack` strings remain project-relative.
236
+ - Existing unqualified commands behave the same when no global collision exists.
237
+ - Legacy bare-ID session entries restore through effective lookup; all new entries record scope.
238
+ - Same-ID cross-scope definitions are valid and must not receive duplicate-ID errors.
239
+ - Duplicate IDs within one scope keep the current fail-closed diagnostics.
240
+ - Global `subagents.profiles` entries, which currently warn and are ignored, become scoped authorization for global profiles only. Document this behavior change prominently.
241
+ - Public loaded-resource types may add scope/key metadata without removing existing fields; compatibility helpers should continue accepting legacy bare IDs where persisted data requires them.
242
+
243
+ ## Implementation slices
244
+
245
+ Each slice should land with focused tests and leave the full verification surface green.
246
+
247
+ ### Slice 1: scoped identity and storage
248
+
249
+ - Add `ResourceScope`, `ResourceKey`, selector parsing/formatting, and exact/effective resolution primitives.
250
+ - Add global profile/stack directory and safe mutation-path helpers.
251
+ - Add scope/key metadata to loaded resources.
252
+ - Keep JSON IDs unqualified and validate selector syntax separately from ID syntax.
253
+ - Add table-driven parser, formatter, storage-containment, and same-ID-key tests.
254
+
255
+ Exit criterion: scoped resources can be represented and addressed without changing runtime loading behavior.
256
+
257
+ ### Slice 2: scoped stack catalog and persistence
258
+
259
+ - Load global stacks plus trusted project/legacy stacks into `all` and `effective` views.
260
+ - Implement project shadowing, exact qualified selection, and invalid-shadow fail-closed behavior.
261
+ - Replace bare-ID runtime lookups with catalog resolution.
262
+ - Persist scoped active-stack references and restore legacy bare-ID entries.
263
+ - Apply scoped standalone stack auto-activation and explicit opt-out rules.
264
+
265
+ Exit criterion: `/preset` runtime behavior, branch restoration, tool policy, preview, and stack diagnostics operate correctly across both scopes before profiles are migrated.
266
+
267
+ ### Slice 3: scoped profiles and dependency resolution
268
+
269
+ - Load global and trusted-project profiles with duplicate and auto-activation diagnostics scoped correctly.
270
+ - Implement the profile-to-stack resolution matrix.
271
+ - Extend preview, application, rollback, provenance, drift, save, delete, and reload with exact scoped keys.
272
+ - Preserve profile auto-activation precedence over standalone stacks.
273
+ - Extend host/subagent profile snapshots and fingerprints with resolved scope where required without changing legacy provenance fingerprint interpretation.
274
+
275
+ Exit criterion: global and project profiles apply deterministically, including explicit project-to-global stack reuse and rejection of global-to-project references.
276
+
277
+ ### Slice 4: commands and web editor
278
+
279
+ - Add qualified parsing and completions to `/preset`, `/profile`, and editor APIs.
280
+ - Make unqualified mutations project-scoped and global mutations explicit.
281
+ - Show scope, collisions, shadow relationships, exact dependencies, and scoped drift in human surfaces.
282
+ - Add real-browser coverage for browsing, applying, editing, forking, deleting, collision handling, and failed scoped preflight.
283
+
284
+ Exit criterion: users can understand and manage both scopes without an unqualified action mutating a global resource.
285
+
286
+ ### Slice 5: scoped delegation, documentation, and release verification
287
+
288
+ - Accept global profile authorization from global config and project profile authorization from project config.
289
+ - Update discovery, embedded summaries, callable selectors, `/forge-agent`, preparation, approval, reports, and policy cleanup.
290
+ - Test same-ID profiles with independent enablement/backend/timeout settings and no authority inheritance.
291
+ - Update English and Chinese concepts, guides, commands, configuration, schema/reference, feature inventory, changelog, and roadmap.
292
+ - Regenerate tracked browser assets and `dist`, then run `npm run verify` and a packed-install smoke test.
293
+
294
+ Exit criterion: scoped ordinary use and scoped delegation are documented, fail closed, package-clean, and verified end to end.
295
+
296
+ ## Required scenario coverage
297
+
298
+ At minimum, automated tests should cover:
299
+
300
+ - Global-only, project-only, and same-ID collision lookup.
301
+ - Exact global access while shadowed.
302
+ - Invalid project shadow without global fallback.
303
+ - Global profile to global stack.
304
+ - Project profile to unqualified project stack.
305
+ - Project profile to explicitly qualified global stack.
306
+ - Missing project stack with a same-ID global suggestion but no fallback.
307
+ - Rejected global profile to project stack.
308
+ - Project auto-activation precedence, global fallback, ambiguity, invalid candidate, restored branch, and explicit opt-out.
309
+ - `default.json` no longer auto-activates by filename; missing `autoActivate` yields a migration warning (D5).
310
+ - Scoped session restoration after a new shadow appears.
311
+ - Scoped provenance and drift after source edits or scope changes.
312
+ - Independent global/project delegation authorization for the same ID.
313
+ - Deletion clearing only matching-scope delegation policy.
314
+ - Untrusted projects loading global resources but not project resources.
315
+ - Web-editor collision navigation and explicit-scope mutation safeguards.
316
+
317
+ ## Architectural guardrails
318
+
319
+ - Keep resolution in shared catalog/services; do not duplicate shadow rules across commands, the editor, and subagent code.
320
+ - Never use project-over-global effective lookup for a stored global profile dependency.
321
+ - Never infer delegation authority from a bare ID after profile resolution.
322
+ - Never persist a new active selection or provenance record without exact scope.
323
+ - Never silently fall back across scopes after an explicit selector or an invalid shadow.
324
+ - Keep profile application transactional and tool/skill policy owned by the resolved prompt stack.
325
+ - Require the complete unit, browser, type, generated-output, documentation, and package verification surface for the final slice.
@@ -14,6 +14,8 @@ pi
14
14
 
15
15
  Trust the project in Pi and use `/reload` when needed.
16
16
 
17
+ Before making structural changes, read the [architecture and development rules](architecture-rules.md) and the active [0.5 architecture plan](../design/architecture-0.5.md). Boundary, schema, persistence, public API, and product changes require an accepted architecture decision before implementation.
18
+
17
19
  The npm package loads compiled `dist/index.js` and intentionally omits physical `src/` files. Clone or fork the repository to inspect or modify source; do not edit generated files in `node_modules`.
18
20
 
19
21
  ## Load the extension
@@ -67,9 +69,9 @@ Document the tested range separately from peer constraints. Pi-coupled experimen
67
69
 
68
70
  ## Package boundaries
69
71
 
70
- - `@zihanw/pi-forge` is the Pi extension and stable macro/slot registration surface.
71
- - `@zihanw/pi-forge/subagent` is the preferred experimental host-contract entry point.
72
- - `@zihanw/pi-subagent-runtime` owns execution lifecycle and fresh-process backends.
73
- - Legacy `@zihanw/pi-forge/src/*` aliases resolve to compiled compatibility modules and are not a promise that internals are public.
72
+ - `@zihanw/pi-forge` is the Pi extension (default export) and the stable macro/slot registration surface; no other root exports or `src/*` aliases exist.
73
+ - `@zihanw/pi-forge/subagent` is the experimental versioned host-port entry point (wire DTOs, validators, transport, client/host lifecycle, canonical fingerprints).
74
+ - `@zihanw/pi-forge-subagents` is the optional package owning subagent execution, configuration, and the 0.4 execution contract.
75
+ - `@zihanw/pi-subagent-runtime` owns execution lifecycle and fresh-process backends (a dependency of the optional package, not of the main package).
74
76
 
75
77
  See the [public API policy](../reference/public-api.md).
@@ -62,7 +62,7 @@ Select the model, thinking level, and prompt stack you want in Pi, then save the
62
62
  /profile use reviewer
63
63
  ```
64
64
 
65
- Profiles live in `.pi/forge/agent-profiles/*.json`. Applying one is a preflighted, one-shot operation: later manual model/thinking changes remain until you apply the profile again. Read [agent-profile concepts](concepts/agent-profiles.md) for validation, auto-activation, and drift semantics.
65
+ Profiles live in `.pi/forge/agent-profiles/*.json` by default; `/profile save global:<id>` writes to `~/.pi/forge/agent-profiles`. Applying one is a preflighted, one-shot operation: later manual model/thinking changes remain until you apply the profile again. Read [agent-profile concepts](concepts/agent-profiles.md) for validation, auto-activation, and drift semantics.
66
66
 
67
67
  ## Storage and migration
68
68
 
@@ -70,12 +70,15 @@ Profiles live in `.pi/forge/agent-profiles/*.json`. Applying one is a preflighte
70
70
  |---|---|
71
71
  | `.pi/forge/prompt-stacks/` | Project prompt stacks |
72
72
  | `.pi/forge/agent-profiles/` | Project agent profiles |
73
- | `.pi/forge/config.json` | Trusted project configuration and delegation authorization |
73
+ | `.pi/forge/config.json` | Trusted project configuration (`webEditor.*`) |
74
74
  | `.pi/forge/extensions/` | Trusted project macro/slot registration code |
75
- | `~/.pi/forge/config.json` | User defaults |
75
+ | `~/.pi/forge/prompt-stacks/` | User-global prompt stacks |
76
+ | `~/.pi/forge/agent-profiles/` | User-global agent profiles |
77
+ | `~/.pi/forge/config.json` | User defaults (`webEditor.*`) |
78
+ | `.pi/forge/subagents.json` / `~/.pi/forge/subagents.json` | Delegation authorization owned by the optional `@zihanw/pi-forge-subagents` package |
76
79
  | `~/.pi/forge/extensions/` | Trusted user macro/slot registration code |
77
80
 
78
- Legacy `.pi/prompt-stacks/*.json` files remain readable. New stacks go to `.pi/forge/prompt-stacks`, and same-named new-location files shadow legacy ones. Migrate safely with:
81
+ Legacy `.pi/prompt-stacks/*.json` files remain readable. Command-created stacks go to `.pi/forge/prompt-stacks`; use the web editor's `global` scope selector to create stacks in `~/.pi/forge/prompt-stacks`. Same-named new-location files shadow legacy ones. Migrate safely with:
79
82
 
80
83
  ```text
81
84
  /preset migrate-stacks --dry-run
@@ -87,7 +90,7 @@ Add `--overwrite` only when target replacement is intended. Add `--delete-legacy
87
90
  ## Where to go next
88
91
 
89
92
  - Learn the [prompt-stack model](concepts/prompt-stacks.md).
90
- - Fork a [focused reviewer](../examples/reviewer-prompt-stack.json) or [DM writer](../examples/sillytavern-dm-writer-prompt-stack.json).
91
- - Use the [SillyTavern importer](guides/sillytavern-import.md).
93
+ - Fork a [focused reviewer](../examples/reviewer-prompt-stack.json).
92
94
  - Learn [macros and slots](reference/macros-and-slots.md).
95
+ - Read the [0.5 migration guide](guides/migrating-to-0.5.md).
93
96
  - Inspect the [complete commands](reference/commands.md) and [stack schema](reference/stack-schema.md).
@@ -15,8 +15,9 @@ Modules receive the pi-forge API directly, so they do not need to import the pac
15
15
  export default function register(api) {
16
16
  api.registerMacro({
17
17
  name: "ticketId",
18
- description: "Current ticket id from session variables.",
19
- render: (ctx) => ctx.variables.toMacroText(ctx.variables.get("ticket.id")),
18
+ description: "Current ticket id from static parameters.",
19
+ dependencies: ["parameters.ticket.id"],
20
+ render: ({ env, helpers }) => String(env.parameters["ticket.id"]),
20
21
  });
21
22
 
22
23
  api.registerSlot({
@@ -25,10 +26,10 @@ export default function register(api) {
25
26
  options: {
26
27
  heading: { type: "string", default: "Ticket context" },
27
28
  },
28
- render: (ctx) => [
29
- String(ctx.options.heading ?? "Ticket context") + ":",
30
- "- Ticket: " + ctx.variables.toMacroText(ctx.variables.get("ticket.id")),
31
- "- Project: " + ctx.helpers.normalizePath(ctx.runtime.options.cwd),
29
+ render: ({ item, options, env, helpers }) => [
30
+ String(options.heading ?? "Ticket context") + ":",
31
+ "- Ticket: " + String(env.parameters["ticket.id"]),
32
+ "- Project: " + helpers.normalizePath(String(env.runtime.cwd)),
32
33
  ].join("\n"),
33
34
  });
34
35
  }
@@ -57,7 +58,7 @@ Use the slot declaratively:
57
58
  - Missing custom slots are validation warnings until their module is loaded.
58
59
  - Registration ownership is disposed when the runtime shuts down.
59
60
 
60
- The API provides `cwd`, `forgeDir`, `extensionPath`, helpers, registration functions, and `getRegisteredMacros()` / `getRegisteredSlots()`. Global `forgeDir` is `~/.pi/forge`; project `forgeDir` is `<project>/.pi/forge`.
61
+ The API provides `forgeDir`, `extensionPath`, helpers, registration functions, and `getRegisteredMacros()` / `getRegisteredSlots()`. Custom macro renderers receive a frozen `{ env, helpers }` and dependencies from the declaration. Global `forgeDir` is `~/.pi/forge`; project `forgeDir` is `<project>/.pi/forge`.
61
62
 
62
63
  Reusable Pi packages may import `registerMacro` and `registerSlot` from `@zihanw/pi-forge`. The directory loaders are intended for small trusted customizations without package boilerplate.
63
64
 
@@ -30,7 +30,7 @@ Saved payloads can contain prompt and conversation content. Keep them out of ver
30
30
 
31
31
  ## Common checks
32
32
 
33
- - Unexpected duplicate task: set the `chat-history` slot's `includeLastUserMessage` to `false` when a later block uses `{{lastUserMessage}}`.
33
+ - Unexpected duplicate task: set the `chat-history` slot's `includeLastUserMessage` to `false` when a later block uses `{{ runtime.lastUserMessage }}`.
34
34
  - Missing tool: inspect stack `allow`/`deny` policy and `/preset status`; the tool-call guard enforces the selected stack even if another extension modifies active tools.
35
35
  - Skill still visible: use `replace` mode when Pi's base prompt must not include its own skill listing.
36
36
  - Missing custom slot: trust the project, check `.pi/forge/extensions`, reload, and inspect diagnostics.
@@ -4,33 +4,31 @@
4
4
 
5
5
  > **Experimental:** This API and its backends may change independently of stable prompt-stack and profile behavior.
6
6
 
7
- pi-forge can execute an explicitly authorized agent profile as a separate, clean, one-shot Pi process. It runs in the foreground and returns a bounded report to the parent conversation.
7
+ The optional `@zihanw/pi-forge-subagents` package executes an explicitly authorized agent profile as a separate, clean, one-shot Pi process. It runs in the foreground and returns a bounded report to the parent conversation.
8
8
 
9
9
  ## Enable a profile
10
10
 
11
- Profiles are not delegatable by default. Enable each eligible ID in the trusted project's `.pi/forge/config.json`, or use the profile's delegation card in `/preset ui`:
11
+ Profiles are not delegatable by default. Enable each eligible ID in the trusted project's `.pi/forge/subagents.json`:
12
12
 
13
13
  ```json
14
14
  {
15
- "subagents": {
16
- "backend": "pi-subprocess-readonly",
17
- "timeoutMs": 60000,
18
- "profiles": {
19
- "reviewer": {
20
- "enabled": true,
21
- "timeoutMs": 300000
22
- },
23
- "rpc-reviewer": {
24
- "enabled": true,
25
- "backend": "pi-rpc-readonly",
26
- "timeoutMs": 180000
27
- }
15
+ "backend": "pi-subprocess-readonly",
16
+ "timeoutMs": 60000,
17
+ "profiles": {
18
+ "reviewer": {
19
+ "enabled": true,
20
+ "timeoutMs": 300000
21
+ },
22
+ "rpc-reviewer": {
23
+ "enabled": true,
24
+ "backend": "pi-rpc-readonly",
25
+ "timeoutMs": 180000
28
26
  }
29
27
  }
30
28
  }
31
29
  ```
32
30
 
33
- Enablement and per-profile overrides are project-only because profiles are project-local. A global `~/.pi/forge/config.json` may define general `backend` and `timeoutMs` defaults; global `profiles` entries warn and are ignored. Disabled or unlisted IDs are hidden from discovery and rejected even if guessed.
31
+ Legacy `.pi/forge/config.json.subagents` is accepted as read-only fallback with a warning. Enablement follows the profile's scope. A global `~/.pi/forge/subagents.json` may define general `backend` and `timeoutMs` defaults and may authorize `global:<id>` profiles through its own `profiles` map. The trusted project's `subagents.json` authorizes `project:<id>` profiles. Same-ID global and project profiles never inherit enablement, backend, or timeout policy from one another. Disabled or unlisted profiles are hidden from discovery and rejected even if guessed.
34
32
 
35
33
  ## Discover, plan, and run
36
34
 
@@ -45,8 +43,16 @@ Humans use:
45
43
 
46
44
  `plan` resolves the profile and stack, compiles and validates the exact immutable provider-bound plan, displays it, and discards it without provider transport.
47
45
 
46
+ Profile selectors accept the same grammar everywhere: `reviewer` (project first), `project:reviewer`, or `global:reviewer`. When both scopes expose the same ID, the project profile keeps the concise selector and the global profile remains callable as `global:<id>`.
47
+
48
48
  The parent model uses `forge_subagent_profiles` to discover enabled profiles and `forge_subagent` to invoke one. A restrictive parent stack must allow both tool names. Discovery is local/no-egress and reports metadata, resolution readiness, effective backend/timeout, approval mode, and whether parent tool policy permits invocation.
49
49
 
50
+ Projects with only a few frequently used profiles can set `summaryInToolDescription: true` (global or trusted-project `subagents.json`). The `forge_subagent` tool description then carries a compact summary of enabled profiles—id, model, thinking level, stack, backend, and timeout—so the parent model does not need a discovery call to pick a profile. Ready profiles appear first; unavailable enabled profiles remain visible with their first resolution error so the model knows not to invoke them. The summary rides in every request, is capped at 8 profiles and 1,000 characters, and refreshes with profiles, stacks, and configuration; `forge_subagent_profiles` remains the authoritative full-detail surface.
51
+
52
+ ## Parallel invocation
53
+
54
+ `forge_subagent` is a parallel-execution tool: the parent model may issue several calls in one turn, and they prepare and run concurrently. Interactive approval dialogs are serialized one at a time because Pi's selector/editor UI is a single slot—a second concurrent dialog would clear the first and leave it unresolved—so each call waits its turn for the dialog and then executes immediately, letting approved runs overlap. Unattended invocation needs no dialog and is fully concurrent. Each run is an independent `pi` subprocess and provider request; a burst of parallel calls multiplies provider cost and process load, so keep the parent tool policy conservative until a configurable concurrency cap lands.
55
+
50
56
  ## Backends and precedence
51
57
 
52
58
  Two fresh-process backends are registered:
@@ -66,13 +72,11 @@ To authorize the parent model without per-run approval:
66
72
 
67
73
  ```json
68
74
  {
69
- "subagents": {
70
- "allowAgentInvocationWithoutApproval": true
71
- }
75
+ "allowAgentInvocationWithoutApproval": true
72
76
  }
73
77
  ```
74
78
 
75
- This affects only `forge_subagent`; `/forge-agent run` remains interactive. It is ignored in untrusted projects and malformed values fail closed. Treat this project config as an authorization file: do not enable or commit it unless every parent agent allowed to call `forge_subagent` may send the compiled prompt and readable file contents to the selected provider without asking again.
79
+ This affects only `forge_subagent`; `/forge-agent run` remains interactive. It is ignored in untrusted projects and malformed values fail closed. Treat this project `subagents.json` as an authorization file: do not enable or commit it unless every parent agent allowed to call `forge_subagent` may send the compiled prompt and readable file contents to the selected provider without asking again.
76
80
 
77
81
  ## Child context and output
78
82
 
@@ -96,4 +100,4 @@ The current backends are **shared-user, not operating-system sandboxes**.
96
100
 
97
101
  The default tools intentionally provide no mutation path. Do not add write, edit, or shell access to this shared-user design. OS isolation and separately approved staged writes remain future work.
98
102
 
99
- For integration authors, see the [experimental adapter contract](../reference/subagent-adapter.md).
103
+ For integration authors, see the [subagent host port contract](../reference/subagent-host-port.md).
@@ -0,0 +1,107 @@
1
+ # Migrating to pi-forge 0.5
2
+
3
+ [Documentation](../README.md)
4
+
5
+ 0.5.0 is a breaking cleanup release. This page is the migration note for the
6
+ changes landed so far (Lane 1a-1c plus the compiler/extension conformance
7
+ pass in Lane 1d).
8
+
9
+ ## What was removed
10
+
11
+ - SillyTavern importer (`/preset import-silly`), its reports, guide, example,
12
+ and tests. Convert SillyTavern presets with pi-forge 0.4 before upgrading.
13
+ - Mutable turn/session variables, variable mutation macros,
14
+ `pi-forge-variable-state` session entries, and the `variables` slot.
15
+ - Regex `display` and `both` effects; `outgoing` and `finalize` remain.
16
+
17
+ ## Template syntax changes
18
+
19
+ Prompt text now compiles with the closed `forge-v1` grammar.
20
+
21
+ | 0.4 construct | 0.5 forge-v1 |
22
+ |---|---|
23
+ | `{{name}}` (static) | `{{ parameters.name }}` |
24
+ | `{{lastUserMessage}}` | `{{ runtime.lastUserMessage }}` |
25
+ | `{{date}}` / `{{time}}` / `{{cwd}}` | `{{ runtime.date }}` / `{{ runtime.time }}` / `{{ runtime.cwd }}` |
26
+ | `{{tools}}` | `{{ runtime.selectedToolsText }}` |
27
+ | `{{upper::x}}` | `{{ x \| upper }}` |
28
+ | `{{iftools::bash::A::B}}` | `{% if runtime.tool.bash %}A{% else %}B{% endif %}` |
29
+ | custom `{{myMacro}}` | `{{ extensions.myMacro }}` |
30
+
31
+ Unknown paths, unknown filters, parse errors, cycles, and output-limit breaches
32
+ are compile errors; a failing block is omitted instead of re-injecting raw
33
+ template text.
34
+
35
+ ## Schema v2
36
+
37
+ Schema v2 stacks store immutable static values in `parameters` (JSON-compatible)
38
+ instead of the legacy string-only `variables` field:
39
+
40
+ ```json
41
+ {
42
+ "schemaVersion": 2,
43
+ "parameters": { "char": "Konata" }
44
+ }
45
+ ```
46
+
47
+ Unversioned / v1 stacks continue to load through the legacy `variables` reader.
48
+
49
+ ## Running the migration utility
50
+
51
+ A mechanical, diagnostics-first script converts a saved stack file:
52
+
53
+ ```bash
54
+ node scripts/migrate-stack-v2.mjs .pi/forge/prompt-stacks/default.json --dry-run
55
+ node scripts/migrate-stack-v2.mjs .pi/forge/prompt-stacks/default.json --write
56
+ ```
57
+
58
+ It renames `variables` to `parameters`, maps runtime/parameter paths, and
59
+ converts simple filter pipelines. Non-mechanical constructs are reported and
60
+ the file is only written when they are absent. A schema v2 file that still
61
+ contains a legacy `variables` field is left untouched and only warned about.
62
+
63
+ ## Preview and finalize
64
+
65
+ Preview never applies `finalize`; it now reports an informational diagnostic.
66
+ `finalize` remains a destructive, lifecycle-owned transform that replaces the
67
+ stored assistant message.
68
+
69
+ ## Lane 2: scoped global profiles and prompt stacks
70
+
71
+ - User-global resources now live in `~/.pi/forge/prompt-stacks` and `~/.pi/forge/agent-profiles` alongside the project stores. Profile and stack selectors accept explicit scopes: `project:<id>`, `global:<id>`, with bare IDs resolving project-first.
72
+ - Same-ID project resources shadow their global counterparts; global stacks cannot reference project resources (and vice versa) — references resolve within the referenced resource's scope.
73
+ - Untrusted projects fail closed: only global resources load, all mutation routes are rejected, and `session_shutdown` no longer disposes a trusted workspace while sessions may still be active (idle workspaces are swept after an hour; hosts stop independently).
74
+ - The web editor gained scope selectors for creating stacks/profiles into either store and routes global mutations through explicit `global:<id>` selectors.
75
+
76
+ ## Lane 3: subagent package split
77
+
78
+ Subagent execution moved out of the main package into the optional `@zihanw/pi-forge-subagents` package (requires `@zihanw/pi-forge@^0.5.0`). Install it separately to keep foreground delegation.
79
+
80
+ - **Commands:** `/subagents` and `/subagent-run` were removed from the main package. The optional package registers `/forge-agent backends|plan|run`.
81
+ - **Model tools:** `forge_subagent_profiles` and `forge_subagent` are registered by the optional package.
82
+ - **Configuration:** `subagents.*` moved out of `.pi/forge/config.json` into dedicated `.pi/forge/subagents.json` (trusted project) and `~/.pi/forge/subagents.json` (user defaults). The optional package reads legacy `config.json.subagents` sections as a read-only fallback with a warning; it never writes them. Copy the values into `subagents.json` to silence the warning.
83
+ - **Web editor:** the delegation card was removed from the main editor; edit `subagents.json` directly.
84
+ - **Architecture:** the main package's prompt compiler is host-neutral (no subagent assumptions), and `ForgeWorkspace` is the single owner of resource state and compilation contexts.
85
+
86
+ ## Lane 4: Forge-native host contract and public surface
87
+
88
+ - The main package no longer depends on `@zihanw/pi-subagent-runtime` and no longer exports the 0.4 execution contract. That contract (`AgentRequest`, `createAgentExecutionPlan`, `validateAgentRequest`, `negotiateSubagentTools`, preflight/plan/response validators, …) now lives in `@zihanw/pi-forge-subagents` for its own runtime wiring; it is not a public surface of either package for third-party consumers.
89
+ - `@zihanw/pi-forge/subagent` now exports only the versioned host port: wire DTOs and validators, `ForgeHostTransport`, `ForgeHost`/`ForgeHostClient`, lifecycle constants, and the Forge-owned canonical fingerprint helpers. `resolveSubagentHostProfile` / `prepareSubagentHostPlan` were replaced by the host-port operations `resolveProfile` and `prepare`.
90
+ - The package root exports only the default extension factory, `registerMacro`/`registerSlot`, and their contract types. All other root re-exports (loader, agent-profile, profile-service, catalog, resource-identity, render-helper values, the `forge-v1` engine, registry readers) were removed.
91
+ - All `@zihanw/pi-forge/src/*` subpath aliases and the `./examples/*` export were removed; `check-package` rejects them.
92
+
93
+ ### Import migration table
94
+
95
+ | 0.4 import | 0.5 replacement |
96
+ |---|---|
97
+ | `@zihanw/pi-forge` (default, `registerMacro`, `registerSlot`) | unchanged |
98
+ | `@zihanw/pi-forge/subagent` host-port names (`ForgeHost*`, validators, fingerprints) | unchanged |
99
+ | `@zihanw/pi-forge/subagent` execution contract (`AgentRequest`, `createAgentExecutionPlan`, …) | internal to `@zihanw/pi-forge-subagents`; no public replacement |
100
+ | `@zihanw/pi-forge/subagent` `resolveSubagentHostProfile` / `prepareSubagentHostPlan` | host-port operations via `ForgeHostClient` |
101
+ | `@zihanw/pi-forge/src/*` aliases | removed; no replacement (internals) |
102
+ | root loader/profile/catalog/engine re-exports | removed; no replacement (internals) |
103
+
104
+ ## Compatibility notes
105
+
106
+ - The wire shape of the host port is additive across `FORGE_HOST_PORT_VERSION = 1`; unknown operations are rejected with a plain `{ ok: false, error }` result (`"Unknown Forge host operation: …"`), not a thrown error, and optional packages must treat any operation failure as terminal for that request.
107
+ - Vendored fingerprint helpers in the main package are pinned by golden vectors to stay byte-compatible with `@zihanw/pi-subagent-runtime` canonical serialization; plan sealing still happens only in the optional package.