@zihanw/pi-forge 0.4.0-beta.1 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. package/CHANGELOG.md +45 -1
  2. package/PUBLIC_API.md +3 -26
  3. package/README.md +90 -601
  4. package/README.zh-CN.md +86 -585
  5. package/SUBAGENT_ADAPTER_CONTRACT.md +3 -197
  6. package/dist/agent-profile.d.ts +24 -1
  7. package/dist/agent-profile.d.ts.map +1 -1
  8. package/dist/agent-profile.js +146 -36
  9. package/dist/agent-profile.js.map +1 -1
  10. package/dist/catalog.d.ts +27 -0
  11. package/dist/catalog.d.ts.map +1 -0
  12. package/dist/catalog.js +59 -0
  13. package/dist/catalog.js.map +1 -0
  14. package/dist/forge-config.d.ts +106 -0
  15. package/dist/forge-config.d.ts.map +1 -1
  16. package/dist/forge-config.js +305 -18
  17. package/dist/forge-config.js.map +1 -1
  18. package/dist/index.d.ts +5 -5
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +55 -8
  21. package/dist/index.js.map +1 -1
  22. package/dist/lifecycle.d.ts +2 -1
  23. package/dist/lifecycle.d.ts.map +1 -1
  24. package/dist/lifecycle.js +7 -4
  25. package/dist/lifecycle.js.map +1 -1
  26. package/dist/loader.d.ts +17 -1
  27. package/dist/loader.d.ts.map +1 -1
  28. package/dist/loader.js +84 -25
  29. package/dist/loader.js.map +1 -1
  30. package/dist/preset-command.d.ts +1 -1
  31. package/dist/preset-command.d.ts.map +1 -1
  32. package/dist/preset-command.js +38 -10
  33. package/dist/preset-command.js.map +1 -1
  34. package/dist/profile-command.d.ts.map +1 -1
  35. package/dist/profile-command.js +46 -21
  36. package/dist/profile-command.js.map +1 -1
  37. package/dist/profile-service.d.ts +6 -3
  38. package/dist/profile-service.d.ts.map +1 -1
  39. package/dist/profile-service.js +55 -13
  40. package/dist/profile-service.js.map +1 -1
  41. package/dist/resource-identity.d.ts +33 -0
  42. package/dist/resource-identity.d.ts.map +1 -0
  43. package/dist/resource-identity.js +56 -0
  44. package/dist/resource-identity.js.map +1 -0
  45. package/dist/runtime/profile-runtime.d.ts.map +1 -1
  46. package/dist/runtime/profile-runtime.js +7 -2
  47. package/dist/runtime/profile-runtime.js.map +1 -1
  48. package/dist/runtime/prompt-stack-runtime.d.ts +1 -1
  49. package/dist/runtime/prompt-stack-runtime.d.ts.map +1 -1
  50. package/dist/runtime/prompt-stack-runtime.js +22 -12
  51. package/dist/runtime/prompt-stack-runtime.js.map +1 -1
  52. package/dist/runtime/subagent-runtime.d.ts +23 -8
  53. package/dist/runtime/subagent-runtime.d.ts.map +1 -1
  54. package/dist/runtime/subagent-runtime.js +296 -67
  55. package/dist/runtime/subagent-runtime.js.map +1 -1
  56. package/dist/storage.d.ts +11 -0
  57. package/dist/storage.d.ts.map +1 -1
  58. package/dist/storage.js +51 -2
  59. package/dist/storage.js.map +1 -1
  60. package/dist/subagent/canonical.d.ts +19 -7
  61. package/dist/subagent/canonical.d.ts.map +1 -1
  62. package/dist/subagent/canonical.js +19 -47
  63. package/dist/subagent/canonical.js.map +1 -1
  64. package/dist/subagent/contract.d.ts +1 -2
  65. package/dist/subagent/contract.d.ts.map +1 -1
  66. package/dist/subagent/contract.js +1 -2
  67. package/dist/subagent/contract.js.map +1 -1
  68. package/dist/subagent/index.d.ts +4 -3
  69. package/dist/subagent/index.d.ts.map +1 -1
  70. package/dist/subagent/index.js +4 -3
  71. package/dist/subagent/index.js.map +1 -1
  72. package/dist/subagent/plan.d.ts +5 -1
  73. package/dist/subagent/plan.d.ts.map +1 -1
  74. package/dist/subagent/plan.js +29 -32
  75. package/dist/subagent/plan.js.map +1 -1
  76. package/dist/subagent/request.d.ts.map +1 -1
  77. package/dist/subagent/request.js +36 -4
  78. package/dist/subagent/request.js.map +1 -1
  79. package/dist/subagent/types.d.ts +66 -178
  80. package/dist/subagent/types.d.ts.map +1 -1
  81. package/dist/subagent/types.js +1 -1
  82. package/dist/subagent/types.js.map +1 -1
  83. package/dist/subagent/validation.d.ts +14 -14
  84. package/dist/subagent/validation.d.ts.map +1 -1
  85. package/dist/subagent/validation.js +52 -238
  86. package/dist/subagent/validation.js.map +1 -1
  87. package/dist/subagent-command.d.ts +2 -1
  88. package/dist/subagent-command.d.ts.map +1 -1
  89. package/dist/subagent-command.js +115 -19
  90. package/dist/subagent-command.js.map +1 -1
  91. package/dist/subagent-host.d.ts.map +1 -1
  92. package/dist/subagent-host.js +43 -16
  93. package/dist/subagent-host.js.map +1 -1
  94. package/dist/subagent-profile-tool.d.ts +25 -2
  95. package/dist/subagent-profile-tool.d.ts.map +1 -1
  96. package/dist/subagent-profile-tool.js +41 -9
  97. package/dist/subagent-profile-tool.js.map +1 -1
  98. package/dist/subagent-tool.d.ts +31 -4
  99. package/dist/subagent-tool.d.ts.map +1 -1
  100. package/dist/subagent-tool.js +311 -137
  101. package/dist/subagent-tool.js.map +1 -1
  102. package/dist/types.d.ts +3 -0
  103. package/dist/types.d.ts.map +1 -1
  104. package/dist/types.js.map +1 -1
  105. package/dist/web-editor/client-script.generated.d.ts +1 -1
  106. package/dist/web-editor/client-script.generated.d.ts.map +1 -1
  107. package/dist/web-editor/client-script.generated.js +1 -1
  108. package/dist/web-editor/client-script.generated.js.map +1 -1
  109. package/dist/web-editor/client-styles.d.ts +2 -0
  110. package/dist/web-editor/client-styles.d.ts.map +1 -0
  111. package/dist/web-editor/client-styles.generated.d.ts +2 -0
  112. package/dist/web-editor/client-styles.generated.d.ts.map +1 -0
  113. package/dist/web-editor/client-styles.generated.js +3 -0
  114. package/dist/web-editor/client-styles.generated.js.map +1 -0
  115. package/dist/web-editor/client-styles.js +2 -0
  116. package/dist/web-editor/client-styles.js.map +1 -0
  117. package/dist/web-editor/page.d.ts +2 -0
  118. package/dist/web-editor/page.d.ts.map +1 -1
  119. package/dist/web-editor/page.js +11 -73
  120. package/dist/web-editor/page.js.map +1 -1
  121. package/dist/web-editor/server.d.ts.map +1 -1
  122. package/dist/web-editor/server.js +176 -1
  123. package/dist/web-editor/server.js.map +1 -1
  124. package/dist/web-editor/styles.d.ts.map +1 -1
  125. package/dist/web-editor/styles.js +60 -3
  126. package/dist/web-editor/styles.js.map +1 -1
  127. package/dist/web-editor/types.d.ts +87 -0
  128. package/dist/web-editor/types.d.ts.map +1 -1
  129. package/dist/web-host.d.ts +13 -2
  130. package/dist/web-host.d.ts.map +1 -1
  131. package/dist/web-host.js +371 -27
  132. package/dist/web-host.js.map +1 -1
  133. package/docs/README.md +41 -0
  134. package/docs/concepts/agent-profiles.md +60 -0
  135. package/docs/concepts/prompt-stacks.md +95 -0
  136. package/docs/design/README.md +17 -0
  137. package/docs/design/roadmap-0.4-archive.md +216 -0
  138. package/docs/design/subagents/design-review.md +220 -0
  139. package/docs/design/subagents/interface-design.md +274 -0
  140. package/docs/design/subagents/sdk-spike-findings.md +117 -0
  141. package/docs/development/complexity-review.md +86 -0
  142. package/docs/development/release.md +31 -0
  143. package/docs/development/roadmap.md +36 -0
  144. package/docs/development/scoped-global-profiles-stacks.md +325 -0
  145. package/docs/development/setup.md +75 -0
  146. package/docs/getting-started.md +95 -0
  147. package/docs/guides/custom-macros-and-slots.md +68 -0
  148. package/docs/guides/debugging.md +39 -0
  149. package/docs/guides/delegation.md +107 -0
  150. package/docs/guides/sillytavern-import.md +47 -0
  151. package/docs/guides/use-cases.md +65 -0
  152. package/docs/guides/web-editor.md +75 -0
  153. package/docs/reference/commands.md +60 -0
  154. package/docs/reference/configuration.md +66 -0
  155. package/docs/reference/features.md +280 -0
  156. package/docs/reference/macros-and-slots.md +82 -0
  157. package/docs/reference/public-api.md +28 -0
  158. package/docs/reference/stack-schema.md +167 -0
  159. package/docs/reference/subagent-adapter.md +204 -0
  160. package/docs/zh-CN/README.md +37 -0
  161. package/docs/zh-CN/concepts/agent-profiles.md +46 -0
  162. package/docs/zh-CN/concepts/prompt-stacks.md +42 -0
  163. package/docs/zh-CN/getting-started.md +81 -0
  164. package/docs/zh-CN/guides/delegation.md +66 -0
  165. package/docs/zh-CN/guides/web-editor.md +45 -0
  166. package/docs/zh-CN/reference/commands.md +60 -0
  167. package/package.json +29 -15
  168. package/dist/subagent/backend-registry.d.ts +0 -75
  169. package/dist/subagent/backend-registry.d.ts.map +0 -1
  170. package/dist/subagent/backend-registry.js +0 -463
  171. package/dist/subagent/backend-registry.js.map +0 -1
  172. package/dist/subagent/diagnostics.d.ts +0 -3
  173. package/dist/subagent/diagnostics.d.ts.map +0 -1
  174. package/dist/subagent/diagnostics.js +0 -5
  175. package/dist/subagent/diagnostics.js.map +0 -1
  176. package/dist/subagent/pi-model-runtime.d.ts +0 -8
  177. package/dist/subagent/pi-model-runtime.d.ts.map +0 -1
  178. package/dist/subagent/pi-model-runtime.js +0 -22
  179. package/dist/subagent/pi-model-runtime.js.map +0 -1
  180. package/dist/subagent/pi-sdk-backend.d.ts +0 -23
  181. package/dist/subagent/pi-sdk-backend.d.ts.map +0 -1
  182. package/dist/subagent/pi-sdk-backend.js +0 -383
  183. package/dist/subagent/pi-sdk-backend.js.map +0 -1
  184. package/dist/subagent/pi-subprocess-backend.d.ts +0 -72
  185. package/dist/subagent/pi-subprocess-backend.d.ts.map +0 -1
  186. package/dist/subagent/pi-subprocess-backend.js +0 -756
  187. package/dist/subagent/pi-subprocess-backend.js.map +0 -1
  188. package/dist/subagent/subprocess-bridge.d.ts +0 -21
  189. package/dist/subagent/subprocess-bridge.d.ts.map +0 -1
  190. package/dist/subagent/subprocess-bridge.js +0 -87
  191. package/dist/subagent/subprocess-bridge.js.map +0 -1
  192. package/dist/subagent/subprocess-report.d.ts +0 -4
  193. package/dist/subagent/subprocess-report.d.ts.map +0 -1
  194. package/dist/subagent/subprocess-report.js +0 -55
  195. package/dist/subagent/subprocess-report.js.map +0 -1
  196. package/dist/subagent-contract.d.ts +0 -8
  197. package/dist/subagent-contract.d.ts.map +0 -1
  198. package/dist/subagent-contract.js +0 -8
  199. package/dist/subagent-contract.js.map +0 -1
  200. package/dist/web-editor/client/api.d.ts +0 -9
  201. package/dist/web-editor/client/api.d.ts.map +0 -1
  202. package/dist/web-editor/client/api.js +0 -26
  203. package/dist/web-editor/client/api.js.map +0 -1
  204. package/dist/web-editor/client/dom.d.ts +0 -13
  205. package/dist/web-editor/client/dom.d.ts.map +0 -1
  206. package/dist/web-editor/client/dom.js +0 -30
  207. package/dist/web-editor/client/dom.js.map +0 -1
  208. package/dist/web-editor/client/inspector.d.ts +0 -22
  209. package/dist/web-editor/client/inspector.d.ts.map +0 -1
  210. package/dist/web-editor/client/inspector.js +0 -226
  211. package/dist/web-editor/client/inspector.js.map +0 -1
  212. package/dist/web-editor/client/main.d.ts +0 -2
  213. package/dist/web-editor/client/main.d.ts.map +0 -1
  214. package/dist/web-editor/client/main.js +0 -1468
  215. package/dist/web-editor/client/main.js.map +0 -1
  216. package/dist/web-editor/client/policy-editor.d.ts +0 -16
  217. package/dist/web-editor/client/policy-editor.d.ts.map +0 -1
  218. package/dist/web-editor/client/policy-editor.js +0 -330
  219. package/dist/web-editor/client/policy-editor.js.map +0 -1
  220. package/dist/web-editor/client/regex-editor.d.ts +0 -19
  221. package/dist/web-editor/client/regex-editor.d.ts.map +0 -1
  222. package/dist/web-editor/client/regex-editor.js +0 -281
  223. package/dist/web-editor/client/regex-editor.js.map +0 -1
  224. package/dist/web-editor/client/types.d.ts +0 -60
  225. package/dist/web-editor/client/types.d.ts.map +0 -1
  226. package/dist/web-editor/client/types.js +0 -2
  227. package/dist/web-editor/client/types.js.map +0 -1
@@ -0,0 +1,220 @@
1
+ # Historical subagent design review
2
+
3
+ Status: historical pre-implementation review. Its blocking findings were resolved by the exported contract, validated backend registry, immutable preparation binding, concrete backends, and approval-gated foreground integration; the text below records the issues that shaped that design.
4
+
5
+ ## Verdict
6
+
7
+ `NEXT_STEPS.md` is directionally sound and mostly suits pi-forge's current architecture. The profile CLI to shared profile service to profile UI progression is appropriate, and deferring an owned runner is the right call.
8
+
9
+ `SUBAGENT_INTERFACE_DESIGN.md` is a good requirements draft, but it is not yet an implementable backend-neutral contract. Its status should remain draft rather than accepted until the blocking issues below are resolved.
10
+
11
+ ## Findings
12
+
13
+ ### 1. Blocker: a serialized prompt-stack definition is not an executable snapshot
14
+
15
+ The proposed `AgentProfileSnapshot` carries only the declarative `PromptStack`. Current compilation also depends on:
16
+
17
+ - Dynamic `BuildSystemPromptOptions`, the current model, time, variables, and the base system prompt supplied through Pi's lifecycle.
18
+ - Executable custom slot and macro registrations.
19
+ - Pi tool snippets, guidelines, skills, and context files represented by `PromptRuntime`.
20
+ - Pi's default system prompt when the stack is `null`, `append`, or `prepend`.
21
+
22
+ Two backends receiving the same proposed snapshot can therefore compile different prompts or fail to compile custom slots entirely.
23
+
24
+ Recommended design: separate two artifacts:
25
+
26
+ - `AgentProfileSnapshot`: immutable declarative provenance.
27
+ - `AgentExecutionPlan`: a host-prepared system prompt, initial messages, effective tool names, runtime inputs, diagnostics, and an execution fingerprint.
28
+
29
+ The alternative is to require every backend to run an identical Pi/pi-forge runtime and custom-extension set, but that would not be genuinely backend-neutral.
30
+
31
+ Relevant code:
32
+
33
+ - `SUBAGENT_INTERFACE_DESIGN.md:86`
34
+ - `src/lifecycle.ts:86`
35
+ - `src/types.ts:178`
36
+ - `src/slot-renderers.ts:125`
37
+ - `src/macro-engine.ts:122`
38
+
39
+ ### 2. Blocker: profile resolution currently means valid in the parent Pi runtime
40
+
41
+ The current resolver checks models and authentication against the active Pi model registry. A remote, subprocess, or package backend may have different models and credentials.
42
+
43
+ Resolution should be split into:
44
+
45
+ - Host resolution: profile syntax, stack lookup, and trusted extension dependencies.
46
+ - Backend preflight: model availability/authentication, tool availability, media support, policy enforcement, and supported limits.
47
+
48
+ Without that split, the advertised native/subprocess/remote portability is not achievable.
49
+
50
+ Relevant code:
51
+
52
+ - `src/agent-profile.ts:156`
53
+ - `src/agent-profile.ts:172`
54
+ - `src/index.ts:179`
55
+
56
+ ### 3. High: tool negotiation and enforcement are underspecified
57
+
58
+ `NEXT_STEPS.md` says the runner supplies a baseline and the stack filters it, but the request contains neither the backend's tool catalog nor the resolved effective tools. Current pi-forge filters a dynamic active-tool baseline.
59
+
60
+ The backend contract needs a discovery or preflight phase that exposes:
61
+
62
+ - Available tool names or stable capability IDs.
63
+ - The access and limit policies it can enforce.
64
+ - Exact effective tools after stack policy.
65
+ - Required tool patterns that matched nothing.
66
+ - Adapter-specific tool-name mappings, if any.
67
+
68
+ A stack tool allowlist is selection policy, not filesystem or network isolation.
69
+
70
+ Relevant code:
71
+
72
+ - `NEXT_STEPS.md:62`
73
+ - `SUBAGENT_INTERFACE_DESIGN.md:86`
74
+ - `SUBAGENT_INTERFACE_DESIGN.md:167`
75
+ - `src/index.ts:270`
76
+
77
+ ### 4. High: the access contract is not portable or sufficiently enforceable
78
+
79
+ The contract combines remote backends with host absolute paths in `access.cwd` and `access.roots`, while validation requires host-side canonicalization and symlink checks. Those paths may not exist in a remote or container namespace, and host preflight cannot prevent backend-side symlink races.
80
+
81
+ The capability booleans are too coarse to determine whether roots, read-only mounts, output limits, token budgets, or turn limits are enforceable.
82
+
83
+ Recommended changes:
84
+
85
+ - Define access using backend-visible mounts or opaque workspace handles.
86
+ - Require the backend to canonicalize paths within its own namespace immediately before access.
87
+ - Specify whether `cwd` must be contained in an allowed root.
88
+ - Define the behavior of `level: "none"` when roots are also supplied.
89
+ - Represent granular enforcement support rather than broad booleans.
90
+ - Distinguish model/control-plane network traffic from agent-accessible network tools.
91
+ - Treat remote-backend data egress as a separate trust/consent decision.
92
+
93
+ Project trust is not, by itself, consent to send project context to a remote backend.
94
+
95
+ Relevant design sections:
96
+
97
+ - `SUBAGENT_INTERFACE_DESIGN.md:108`
98
+ - `SUBAGENT_INTERFACE_DESIGN.md:167`
99
+ - `SUBAGENT_INTERFACE_DESIGN.md:216`
100
+
101
+ ### 5. Medium: clean-context assembly conflicts with unrestricted prompt-stack layouts
102
+
103
+ The design requires the final task to survive truncation, while existing `compileMessages()` behavior permits a stack to remove the latest user message, filter user roles, or omit history. Such a stack can accidentally remove the delegated task.
104
+
105
+ The contract needs one explicit rule:
106
+
107
+ - Reject stacks whose compiled delegation layout omits the task; or
108
+ - Reserve a delegation-task insertion point that cannot be filtered; or
109
+ - State that intentional omission is allowed, weakening the guarantee that the actionable task is always preserved.
110
+
111
+ Additional context concerns:
112
+
113
+ - "Selected context is data" is an instruction-hierarchy convention, not a security guarantee. Delimiters do not prevent prompt injection.
114
+ - Tool-result excerpts should normally be rendered as quoted background evidence rather than native tool-result messages without matching tool calls.
115
+ - A summary should carry provenance just as other selected context does.
116
+ - `maxTokens` cannot be deterministically portable without defining the tokenizer or estimator. A byte/character bound or recorded estimator/version would be clearer.
117
+
118
+ Relevant code and design:
119
+
120
+ - `SUBAGENT_INTERFACE_DESIGN.md:28`
121
+ - `SUBAGENT_INTERFACE_DESIGN.md:41`
122
+ - `SUBAGENT_INTERFACE_DESIGN.md:223`
123
+ - `src/compiler.ts:88`
124
+ - `src/compiler.ts:161`
125
+
126
+ ### 6. Medium: the real adapter spike comes too late
127
+
128
+ The implementation plan first publishes contract types and builds four slices around fake backends, then validates the design against a real runner in Slice 6. Fake conformance tests can prove internal consistency, but cannot establish that the boundary fits a real backend.
129
+
130
+ The installed Pi SDK already supports session construction with model, thinking level, tools, custom tools, and resource loaders. A minimal in-memory or subprocess prototype should happen before the contract becomes a public export.
131
+
132
+ Move the current concrete-adapter decision/spike before contract stabilization. Keep experimental types internal until that spike validates the boundary.
133
+
134
+ Relevant design and API:
135
+
136
+ - `SUBAGENT_INTERFACE_DESIGN.md:227`
137
+ - `SUBAGENT_INTERFACE_DESIGN.md:262`
138
+ - `node_modules/@earendil-works/pi-coding-agent/dist/core/sdk.d.ts:11`
139
+
140
+ ### 7. Medium: profile UI needs a shared service extraction first
141
+
142
+ The profile UI priority is appropriate, but save, status/drift calculation, deletion, and application are not yet presented as one shared typed service. Save and drift rendering are still command-oriented.
143
+
144
+ Before UI work, extract shared operations for:
145
+
146
+ - Save/update/delete.
147
+ - Resolution and typed preview.
148
+ - Application.
149
+ - Typed provenance and runtime-drift status.
150
+
151
+ This prevents the UI from duplicating command behavior and directly supports the stated done criteria.
152
+
153
+ Relevant code:
154
+
155
+ - `NEXT_STEPS.md:34`
156
+ - `src/profile-command.ts:258`
157
+ - `src/profile-command.ts:455`
158
+
159
+ ### 8. Medium: fingerprints need explicit stable semantics
160
+
161
+ Execution-facing fingerprints should use canonical serialization and a named digest algorithm/version, for example `sha256:v1:<digest>`. A raw `JSON.stringify()` value is useful for current provenance comparison but is not an ideal portable fingerprint.
162
+
163
+ The execution fingerprint should cover all behavior-affecting prepared inputs, not only the source profile and stack. This includes the compiled prompt/messages, effective tools, relevant runtime data, and required custom registrations.
164
+
165
+ Avoid silently changing the semantics of existing stored provenance fingerprints; introduce a separate execution/snapshot fingerprint format if compatibility matters.
166
+
167
+ Relevant code:
168
+
169
+ - `src/agent-profile.ts:243`
170
+ - `SUBAGENT_INTERFACE_DESIGN.md:222`
171
+
172
+ ### 9. Medium: response, trace, and limit semantics need tightening
173
+
174
+ Before parent integration, clarify:
175
+
176
+ - Which statuses require or forbid `error`.
177
+ - Whether `output` may be empty for failures and cancellations.
178
+ - Whether `effectiveAccess` is merely echoed or is a backend-produced enforcement receipt.
179
+ - Effective tool and enforced-limit metadata in the response.
180
+ - Currency and units for `usage.cost`.
181
+ - Artifact and change-path namespace, authorization, lifetime, and cleanup.
182
+ - How an opaque trace reference routes back to the correct backend and how inspection is authorized.
183
+ - Cancellation before a remote backend has exposed its `runId`.
184
+ - Whether limits are hard execution limits or only response/projection truncation.
185
+
186
+ The current capability interface cannot indicate support for `maxTurns`, `maxOutputBytes`, or `tokenBudget`, so the dispatcher cannot reliably apply the rule that unsupported required policies must be rejected.
187
+
188
+ ## Recommended ordering
189
+
190
+ 1. Complete real-provider and real-extension-tool profile CLI compatibility testing.
191
+ 2. Extract the shared profile repository/application/status service.
192
+ 3. Build profile UI on that service with browser workflow coverage.
193
+ 4. Prototype one actual Pi SDK or subprocess backend.
194
+ 5. Revise the subagent design around backend preflight plus a host-prepared execution plan.
195
+ 6. Add pure validators, stable fingerprints, fake-backend conformance tests, and optional backend registration.
196
+ 7. Add parent run/inspect tools only after enforcement and task-preservation semantics are demonstrated.
197
+ 8. Continue deferring an owned full runner, resumable agents, chains, pipelines, retries, queues, and concurrency orchestration.
198
+
199
+ Custom macro/slot portability can remain broadly deferred, but dependency detection cannot. A subagent export must identify required registrations or reject stacks that cannot be reproduced by the chosen backend.
200
+
201
+ ## Parts of the plan worth retaining
202
+
203
+ - Profiles remain reusable one-shot presets rather than continuously owning runtime state.
204
+ - Prompt stacks remain the profile-level source of tool-selection policy.
205
+ - Per-run access and limits remain outside reusable profiles.
206
+ - Parent context selection is explicit, bounded, and provenance-preserving.
207
+ - Parent-visible results remain compact, with full normalized history behind an inspectable trace.
208
+ - No concrete subagent package is required to install or use pi-forge.
209
+ - A full owned runner remains deferred.
210
+ - Initial runner work avoids chains and pipelines.
211
+ - Browser workflows and public contract behavior receive dedicated tests.
212
+
213
+ ## Validation performed during review
214
+
215
+ - Core tests: 126 passed.
216
+ - TypeScript typecheck: passed.
217
+ - Real-browser editor smoke test: passed.
218
+ - Tracked `dist/` consistency check: passed.
219
+
220
+ The review itself did not modify implementation code or either source design document.
@@ -0,0 +1,274 @@
1
+ # Subagent request/response design
2
+
3
+ Status: historical design and implementation record for the completed 0.4 path. Execution ownership (backend registry, preflight binding, plan sealing, conversation/execution fingerprints, lifecycle, and the fresh-process `pi-subprocess-readonly`/`pi-rpc-readonly` backends) lives in `@zihanw/pi-subagent-runtime`; the former in-package registry and `pi-sdk-isolated` compatibility backend were removed in that migration. The broader Pi SDK spike is recorded in the [SDK spike findings](sdk-spike-findings.md) (harness since removed), and the concrete exported semantics are documented in the [subagent adapter contract](../../reference/subagent-adapter.md).
4
+
5
+ ## Goals
6
+
7
+ - Make pi-forge profiles usable by native, subprocess, package-provided, or remote backends without requiring every backend to reproduce the parent Pi runtime.
8
+ - Start every subagent with a clean conversational context while allowing explicit, bounded context seeding.
9
+ - Keep prompt stacks as the profile-level source of prompt layout, visible-tool policy, and model-visible skills.
10
+ - Keep access, limits, cancellation, trace storage, and result reporting outside reusable profiles.
11
+ - Return a compact parent-visible tool result while retaining normalized execution history for authorized inspection.
12
+ - Export the demonstrated request/resolution/preflight/plan/response boundary while keeping a full owned runner, orchestration, and automatic delegation out of the package surface.
13
+
14
+ ## Execution Flow
15
+
16
+ The portable caller request is not itself executable. Execution has five stages:
17
+
18
+ ```text
19
+ AgentRequest
20
+ -> host profile/stack/dependency resolution
21
+ -> backend discovery and preflight
22
+ -> backend-assisted host plan preparation
23
+ -> backend execution
24
+ -> AgentResponse
25
+ ```
26
+
27
+ ### 1. AgentRequest
28
+
29
+ The parent supplies intent: profile selection, task/media, explicit selected context, access requirements, limits, and parent-depth provenance. It does not contain parent runtime model objects, loaded files, credentials, raw session entries, or a compiled prompt.
30
+
31
+ ### 2. Host resolution
32
+
33
+ pi-forge validates profile syntax, resolves the referenced prompt stack, identifies required custom macro/slot registrations, resolves selected parent-context references, and creates immutable declarative provenance. This stage does not claim that the target backend has the model, credentials, tools, isolation, or limit support needed to execute it.
34
+
35
+ Current parent-runtime resolution used by `/profile use` remains valid for applying profiles to the parent. Subagent host resolution must be a separate operation rather than reusing parent model/auth diagnostics as backend truth.
36
+
37
+ ### 3. Backend discovery and preflight
38
+
39
+ Before prompt compilation, the selected backend reports:
40
+
41
+ - Model availability, authentication, thinking-level support, and media support.
42
+ - A stable tool catalog with backend tool IDs, policy-facing names, prompt snippets, and optional adapter mappings.
43
+ - Prompt-runtime inputs needed for Pi-compatible base prompts, tool guidance, skills, and context resources.
44
+ - Workspace/mount materialization support and granular access enforcement.
45
+ - Supported hard limits, cancellation behavior, trace support, and artifact behavior.
46
+ - Remote data-egress requirements.
47
+
48
+ The host applies the prompt stack's tool policy to the backend catalog. An allow pattern that matches nothing remains a warning unless separate dependency metadata marks that tool or capability as required.
49
+
50
+ ### 4. Backend-assisted plan preparation and AgentExecutionPlan
51
+
52
+ The host combines the resolved profile, backend preflight receipt, selected context, and prompt-stack compiler into an immutable execution plan. The plan contains prepared system text/messages, exact model and thinking level, effective backend tool IDs, materialized workspace handles, enforced limits, diagnostics, provenance, and a stable execution fingerprint.
53
+
54
+ Some backends cannot expose exact prompt-runtime inputs during passive preflight. Pi SDK 0.80.6, for example, exposes its exact base prompt, tool snippets, skills, and context options through `before_agent_start`, immediately before provider execution. Such an adapter may supply those inputs to a trusted host preparation callback after accepting the prompt, provided it blocks provider transport until host compilation, protected-task validation, limit checks, and plan finalization succeed. A partial dry preflight must declare its prompt-runtime fidelity and cannot masquerade as the exact execution plan.
55
+
56
+ Custom macros and slots execute during plan preparation in the trusted host. Backends receive their rendered result and dependency receipt, not executable registration code. A raw `PromptStack` remains snapshot provenance and is not treated as an executable backend artifact.
57
+
58
+ For a null stack or append/prepend stack mode, the backend preflight must provide the base-prompt inputs required by the selected adapter. If the host cannot reproduce the intended base prompt, plan preparation fails.
59
+
60
+ ### 5. AgentResponse
61
+
62
+ The backend returns normalized terminal status, output, enforcement receipts, effective tools/limits, artifacts, usage, and a routed trace handle. A separate projection determines what enters the parent model's context.
63
+
64
+ ## Responsibility Boundaries
65
+
66
+ Effective permissions are the intersection of:
67
+
68
+ 1. Profile prompt-stack tool selection.
69
+ 2. Request access and limit requirements.
70
+ 3. Backend-advertised capabilities and enforcement.
71
+
72
+ A backend must reject a required policy it cannot enforce. Prompt tool filtering is not a filesystem, process, or network sandbox.
73
+
74
+ pi-forge owns profile/stack resolution, trusted prompt compilation, context selection, plan creation, stable fingerprints, diagnostics, and parent-visible response projection. Backends own target-runtime preflight, execution, cancellation, backend-namespace canonicalization, enforcement receipts, usage collection, artifacts, and normalized traces.
75
+
76
+ ## Context Model and Task Preservation
77
+
78
+ Subagents inherit no parent conversation automatically. Their prepared context contains:
79
+
80
+ 1. System instructions compiled from the resolved profile and backend prompt inputs.
81
+ 2. Optional parent-selected context rendered as quoted background evidence with provenance.
82
+ 3. A protected final user task containing all request text and media.
83
+
84
+ Selected context may contain a provenance-bearing summary, visible user/assistant excerpts, quoted tool-result excerpts, and resource/artifact references. It never automatically contains the parent system prompt, full chat history, hidden reasoning, raw provider payloads, secrets, or environment variables.
85
+
86
+ Tool-result excerpts are background content, not native tool-result messages, because they do not have matching tool calls in the clean subagent history. Delimiters communicate instruction priority but are not a prompt-injection security boundary.
87
+
88
+ The delegated task is appended as a protected final user message after unrestricted prompt-stack message layout has been compiled. A backend adapter may combine adjacent user messages for provider compatibility, but it must preserve the delegated-context/task boundary and all structured media parts. Subagent preparation fails if the final normalized plan does not contain the complete task.
89
+
90
+ Context budgeting uses a required character/byte ceiling in v1. Any optional token estimate records the estimator/tokenizer name and version. Truncation removes optional context before required context and never truncates the final task or required media.
91
+
92
+ ## Contract Artifacts
93
+
94
+ The TypeScript shapes and pure validators for the following artifacts are exported. Backend registration, explicit backend selection, and execution lifecycle are owned by `@zihanw/pi-subagent-runtime`; the extension composition root registers local `forge_subagent_profiles` discovery and drives the runtime's `pi-subprocess-readonly` (default) and `pi-rpc-readonly` backends for `forge_subagent` and `/forge-agent`.
95
+
96
+ ### AgentRequest
97
+
98
+ - Schema version and host-generated request ID.
99
+ - Profile ID plus an optional expected source fingerprint for optimistic consistency.
100
+ - Structured text/media input.
101
+ - Optional selected-context summary/items with provenance and a character budget.
102
+ - Access requirements expressed through opaque workspace/resource handles, not backend-specific absolute paths.
103
+ - Hard execution limits and a separate parent-result projection limit.
104
+ - Parent run/session provenance and bounded delegation depth.
105
+ - Explicit remote-egress consent when a backend would transmit local project data.
106
+
107
+ ### AgentProfileSnapshot
108
+
109
+ - Normalized profile fields and canonical source-profile digest.
110
+ - Normalized prompt-stack definition and canonical stack digest, or null.
111
+ - Required custom macro/slot dependency identities when available.
112
+ - No model-registry object, credential/auth result, loaded file path, diagnostics, session state, or secret.
113
+
114
+ This snapshot is immutable provenance. It is not sent to a backend as a promise that the profile is executable there.
115
+
116
+ ### BackendPreflightResult
117
+
118
+ - Accepted/rejected state with structured diagnostics.
119
+ - Resolved target model and thinking level.
120
+ - Stable tool catalog and adapter mappings.
121
+ - Prompt-runtime/base-prompt inputs.
122
+ - Workspace materialization/mount mappings in the backend namespace.
123
+ - Granular enforcement capabilities and accepted hard limits.
124
+ - Media, cancellation, trace, artifact, and remote-egress behavior.
125
+
126
+ ### AgentExecutionPlan
127
+
128
+ - Host-generated run ID, request ID, and backend ID.
129
+ - Prepared system prompt and normalized initial messages.
130
+ - Exact model, thinking level, effective backend tool IDs, and unmatched policy diagnostics.
131
+ - Materialized workspace handles, relative working directory, network policy, and enforcement requirements.
132
+ - Enforced execution limits and separate result-projection limits.
133
+ - Profile/stack provenance, dependency receipt, and backend preflight receipt.
134
+ - Canonical execution fingerprint covering all behavior-affecting prepared inputs.
135
+
136
+ The run ID exists before backend execution begins so cancellation and trace correlation do not depend on a remote backend first returning its own ID.
137
+
138
+ ### AgentResponse
139
+
140
+ Response status is a discriminated union:
141
+
142
+ - `completed`: no error; output may be empty.
143
+ - `failed`: structured error required; optional partial output must be marked partial.
144
+ - `cancelled`: cancellation reason required; output is absent or explicitly partial.
145
+ - `timed-out`: timeout reason and enforced timeout required; output is absent or explicitly partial.
146
+ - `limit-reached`: the reached hard limit is required; output is absent or explicitly partial.
147
+
148
+ Every response includes request/run/backend correlation, model/profile/execution fingerprints, backend-produced enforcement receipt, effective tools and limits, duration, and artifact/trace routing metadata. `effectiveAccess` is never a request echo.
149
+
150
+ Token usage records tokenizer/model provenance when known. Cost uses an amount plus ISO currency code rather than an unqualified number. Artifact/change paths live in a named workspace namespace and include authorization, lifetime, and cleanup metadata.
151
+
152
+ Opaque backend trace IDs are registered behind host trace handles. Inspection authorizes the caller and routes through the backend/host trace registry. Hidden provider reasoning remains excluded by default.
153
+
154
+ Hard execution limits are distinct from response projection truncation. A backend that cannot enforce a required timeout, turn, token, output, filesystem, or network constraint rejects preflight.
155
+
156
+ ## Access and Data-Egress Model
157
+
158
+ Requests identify host resources with opaque workspace/resource handles. Backend preflight maps them into backend-visible mounts or uploaded resources. The execution plan uses mount IDs plus normalized relative paths rather than assuming host absolute paths exist remotely.
159
+
160
+ - `none` permits no filesystem mounts and no filesystem working directory.
161
+ - `read-only` permits only read-only mounts.
162
+ - `workspace-write` permits explicitly identified read-write mounts; all other mounts remain read-only or absent.
163
+ - An access receipt distinguishes an `isolated` execution boundary from an explicitly unsafe `shared-user` process. At the shared-user boundary, access levels describe the model-visible tool policy rather than the child process's operating-system permissions.
164
+ - A working directory, when present, must be contained within an accepted mount.
165
+ - The backend canonicalizes and checks paths in its own namespace immediately before access and must enforce containment against symlink races.
166
+ - Model/provider transport is distinct from agent-accessible network tools.
167
+ - Sending project content to a remote backend requires an explicit egress decision; project trust alone is insufficient consent.
168
+
169
+ Capabilities are granular claims such as read-only mount isolation, read-write mount isolation, symlink-safe containment, agent-network isolation, timeout enforcement, turn enforcement, token-budget enforcement, output enforcement, cancellation, media transport, artifact retention, and trace inspection. Broad booleans are insufficient.
170
+
171
+ ## Fingerprints and Compatibility
172
+
173
+ New portable fingerprints use canonical serialization and a named algorithm/version such as `sha256:v1:<digest>`.
174
+
175
+ - Source-profile fingerprint: normalized declarative profile.
176
+ - Prompt-stack fingerprint: normalized declarative stack.
177
+ - Execution fingerprint: compiled system/messages, exact model/thinking, effective tool IDs/mappings, relevant prompt-runtime inputs, selected-context/resource digests, dependency receipt, materialized access policy, enforced limits, and adapter/preflight version.
178
+
179
+ The existing `agentProfileFingerprint()` JSON string remains unchanged for stored branch provenance and drift compatibility. Portable snapshot/execution digests are new fields with separate semantics.
180
+
181
+ ## Parent-Visible Result
182
+
183
+ The complete response is control-plane data. The main agent receives a bounded tool-result projection containing run ID, terminal status, output, and compact errors/warnings when applicable.
184
+
185
+ The human can expand the same tool result to inspect the plan summary, approval receipt, diagnostics, usage, normalized response, and complete captured subprocess transcript/tool events. Those details stay out of the normal model-visible projection. The full prepared prompt is shown only on demand before approval and is not persisted in result details by default. `/tree` changes the active conversation branch; it cannot undo provider egress, billing, or external effects. Contract trace inspection, artifacts, and resumable sessions remain separate and deferred.
186
+
187
+ ## Revised Implementation Plan
188
+
189
+ ### Integration checkpoint: require a user-visible walking skeleton
190
+
191
+ Fake-backend conformance proves contract and registry behavior, while the standalone SDK spike proved broader Pi provider behavior. The shipped walking skeleton sends a human-requested task through the complete profile-resolution, runtime-preflight, prompt-preparation, plan-validation, provider-execution, and response-projection path.
192
+
193
+ The first concrete integration was a pi-forge-owned `pi-sdk-isolated` adapter plus a human-operated plan/run command. The current integration drives the runtime package's foreground `pi-subprocess-readonly` (default) and `pi-rpc-readonly` backends for the command and model-callable tool. It exposes only stack-filtered read/list/search tools, declares a shared-user boundary rather than filesystem isolation, requires explicit approval of an immutable plan bound to runtime-issued fingerprints, and returns bounded output plus expandable reports.
194
+
195
+ This integration also exposed a contract seam hidden by the fake backend: backend-assisted preparation cannot require the caller to fabricate the prompt runtime that the backend is responsible for discovering. The backend must supply complete, canonical compiler inputs; the host must compile from them; and the registry must return the exact runtime/preparation pair used for execution-plan construction.
196
+
197
+ ### Iteration 1: Shared profile service
198
+
199
+ - Extract shared typed profile repository operations for load, capture, save/update, and delete.
200
+ - Extract profile application and rollback from command rendering.
201
+ - Extract typed resolution preview and runtime/provenance drift status.
202
+ - Refactor commands to use the service without changing current CLI behavior.
203
+ - Add service-level tests so profile UI and subagent preparation share one implementation.
204
+
205
+ ### Iteration 2: Internal real-backend spike (completed)
206
+
207
+ - Prototype one in-memory Pi SDK backend using a fresh `SessionManager`, explicit model/thinking, a controlled tool baseline, cancellation, and compact output.
208
+ - Exercise a real profile, restrictive prompt stack, custom macro/slot dependency, media input, and current local provider.
209
+ - Record actual SDK/runtime requirements and failure points.
210
+ - Keep all spike types internal and expose no run tool.
211
+
212
+ Implemented as the opt-in `scripts/subagent-sdk-spike.ts` (removed in the 0.4 cleanup after its findings were productized in the runtime's shared preparation gate); results and limitations are recorded in `SUBAGENT_SDK_SPIKE_FINDINGS.md`.
213
+
214
+ ### Iteration 3: Resolve/preflight/plan boundary (completed)
215
+
216
+ - Split parent application resolution from backend-independent host resolution.
217
+ - Define internal request, backend descriptor/preflight, backend-assisted preparation callback, execution-plan, response, enforcement-receipt, and diagnostic types based on the spike.
218
+ - Implement protected task/context preparation and tool negotiation.
219
+ - Validate null/replace/append/prepend stacks and missing custom dependencies.
220
+
221
+ Implemented in `src/subagent-host.ts` and focused modules under `src/subagent/`, with `src/subagent-contract.ts` retained as a compatibility barrel. The SDK spike consumes the shared protected-task and host-resolution helpers.
222
+
223
+ ### Iteration 4: Stable validation and fingerprints (completed)
224
+
225
+ - Add canonical snapshot/stack/execution digests without changing legacy provenance fingerprints.
226
+ - Add pure validators and the full status/error/access/limit matrices.
227
+ - Add deterministic context budgeting and artifact/trace namespace validation.
228
+ - Export only the portions demonstrated by the real spike.
229
+
230
+ Implemented with canonical `sha256:v1` fingerprints, deterministic UTF-8 context budgeting, access/limit/status matrices, artifact/trace validation, and package-root exports. Legacy profile provenance fingerprints are unchanged.
231
+
232
+ ### Iteration 5: Backend registration and conformance (completed)
233
+
234
+ - Added an optional backend registry/dispatcher with validated descriptor discovery and granular capability negotiation.
235
+ - Added a deterministic fake backend and reusable conformance fixtures shaped by the real spike.
236
+ - Normalized cancellation races, host-abort timeouts, provider failures, malformed responses, enforcement receipts, and authorization-scoped trace routing.
237
+ - pi-forge remains fully functional with no registered backend; the registry starts empty.
238
+
239
+ ### Iteration 6: Concrete Pi SDK walking skeleton (completed)
240
+
241
+ - Correct backend-assisted prompt-runtime ownership and add complete compiler-input validation/fingerprints.
242
+ - Extract an access-none, no-tool, in-memory Pi SDK backend from the completed spike.
243
+ - Add human-operated backend discovery, dry plan, and explicit real-run commands through the registry.
244
+ - Cover pre-transport refusal, provider failure, host timeout, cancellation, response normalization, and cleanup without live provider calls in ordinary tests.
245
+
246
+ Implemented as the exported experimental `PiSdkIsolatedBackend` and `/forge-agent backends|plan|run` command path. Ordinary tests use Pi's faux provider through a real in-memory `AgentSession`, proving provider gating and final context without network traffic.
247
+
248
+ ### Iteration 7: Foreground parent integration (completed)
249
+
250
+ - Added no-egress `forge_subagent_profiles` discovery so the main agent can select only from profile IDs explicitly enabled by trusted-project delegation policy rather than guessing.
251
+ - Added the sequential `forge_subagent` tool on the shared command/runtime path rather than creating a second runner.
252
+ - Added project-only `subagents.profiles.<id>` policy so ordinary profiles remain portable and independently usable while delegation eligibility, backend, and timeout are resolved per profile; global config remains limited to general defaults until global profile and stack storage have explicit scope semantics.
253
+ - Resolve the profile and prepare the exact immutable plan before asking for interactive approval or permitting provider transport.
254
+ - Show a compact default review, with full prompt inspection on demand, and bind approval to the execution fingerprint.
255
+ - Insert only bounded response text into parent context while retaining complete transcript/tool-event details for expanded human inspection.
256
+ - Keep execution text-only, foreground, clean-context, read-only, and shared-user; cancellation, rejection, provider failure, and missing-UI cases fail closed.
257
+
258
+ ### Iteration 8: Sandbox and staged-write decision
259
+
260
+ - Evaluate an optional bubblewrap-style backend that can honestly enforce allowed roots, network policy, and subprocess restrictions without changing the portable contract.
261
+ - Design writes as a separately approved staged patch/change set with clear inspection and undo semantics; do not simply add `write`, `edit`, or shell to the shared-user child.
262
+ - Continue deferring resumable agents, retries, queues, chains, pipelines, background work, and concurrency orchestration until concrete demand exists.
263
+
264
+ Profile UI can proceed independently on the completed Iteration 1 service; it does not need to block parent integration or a concrete adapter decision. Each iteration remains independently reviewable and revertible.
265
+
266
+ ## Deferred Decisions
267
+
268
+ - Whether the experimental Pi SDK backend remains built in, becomes separately configurable, or moves to an optional integration after 0.4 feedback.
269
+ - Trace/artifact storage location, retention defaults, redaction, and cleanup implementation.
270
+ - Resumable sessions and continuation references.
271
+ - Automatic retries, fallbacks, priorities, queues, and concurrency.
272
+ - Secret/environment injection.
273
+ - Structured JSON-schema output.
274
+ - Automatic parent-context selection beyond explicit summaries and references.
@@ -0,0 +1,117 @@
1
+ # Historical Pi SDK subagent spike findings
2
+
3
+ Status: completed internal spike against `@earendil-works/pi-coding-agent` 0.80.6 on 2026-07-12. Its contract findings are productized in `@zihanw/pi-subagent-runtime`'s shared SDK preparation gate, which both fresh-process backends use. The spike harness (`scripts/subagent-sdk-spike*.ts`) was removed in the 0.4 cleanup; with it went the only live media-transport and trusted-extension preparation diagnostic, to be re-established when delegated media tasks are productized. The 0.80.6 observations and terminology below remain a historical record.
4
+
5
+ ## Deliverable
6
+
7
+ The spike was available through `npm run spike:subagent -- --cwd /path/to/project --profile PROFILE_ID` before its removal in the 0.4 cleanup.
8
+
9
+ Dry-run is the default. A provider call requires `--execute`; that explicit flag is also the spike's consent to transmit the supplied task/media to the selected provider. The other spike-only flags are:
10
+
11
+ - `--task TEXT`
12
+ - `--timeout MILLISECONDS`
13
+ - `--access none|read-only|workspace-write`
14
+ - `--image PATH` (repeatable)
15
+ - `--load-forge-extensions` to execute trusted global/project pi-forge registration code
16
+
17
+ The command emits a JSON preflight/execution report. It creates an in-memory `SessionManager`, uses an isolated SDK resource directory and working directory for `access=none`, loads only the inline compiler bridge, does not write a Pi session file, and disposes the session and trusted registrations when finished.
18
+
19
+ Execution currently rejects `read-only` and `workspace-write`. The SDK can filter tools, but this adapter cannot produce an allowed-root, symlink-safe filesystem/process/network isolation receipt. `access=none` is enforceable by setting the active tool set to empty.
20
+
21
+ ## Validated Behavior
22
+
23
+ ### Real profile, model, auth, and stack
24
+
25
+ The dry and execute paths resolved `/home/bruhw/programming`'s `default` profile to:
26
+
27
+ - Model: `opencode-go/glm-5.2`
28
+ - Thinking: `high`
29
+ - Stack: `qiqi-assistant`
30
+ - Auth: configured through Pi's real `AuthStorage` and `ModelRegistry`
31
+
32
+ The SDK session used those exact values, remained in memory, and exposed no session file. A real no-tool turn completed with `SPIKE_OK`.
33
+
34
+ ### Tool discovery and policy
35
+
36
+ The isolated Pi 0.80.6 SDK session advertised seven built-ins: `read`, `bash`, `edit`, `write`, `grep`, `find`, and `ls`. This confirms that adapter discovery must be dynamic; neither pi-forge nor the contract should hard-code the interactive UI's historical four-tool baseline.
37
+
38
+ The spike computes:
39
+
40
+ ```text
41
+ backend catalog ∩ prompt-stack policy ∩ request access
42
+ ```
43
+
44
+ It records the backend catalog, stack-selected names, effective names, unmatched allow patterns, and whether the requested access can actually be enforced. Tool selection worked for dry preflight, and `access=none` produced an empty effective set in real execution.
45
+
46
+ Pi's public `ToolInfo` includes names, descriptions, parameter schemas, prompt guidelines, and source metadata, but not the one-line `toolSnippets` used by the base prompt. Those snippets are available in `before_agent_start.systemPromptOptions`. A dry plan built only from the public catalog is therefore intentionally marked `partial-dry-run`.
47
+
48
+ ### Exact prompt preparation and protected task
49
+
50
+ The inline SDK extension receives the exact base system prompt and `BuildSystemPromptOptions` in `before_agent_start`, then calls the existing pi-forge compiler. On the first `context` event it:
51
+
52
+ 1. Removes the delegated task from unrestricted history compilation.
53
+ 2. Compiles optional history and synthetic prompt-stack messages.
54
+ 3. Appends a structured clone of the delegated text and media as the protected final user message.
55
+
56
+ This preserved the task through `qiqi-assistant` and `image-viewer`, including layouts that add synthetic user messages. Offline tests also cover a `chat-history` slot configured to omit the latest user message and prove that it cannot remove the protected task.
57
+
58
+ The exact `BuildSystemPromptOptions` are not publicly obtainable during passive SDK preflight. For a Pi SDK adapter, exact plan preparation must therefore be backend-assisted inside the accepted prompt lifecycle, after capability preflight but before the provider request. The adapter must fail the turn before transport if compilation or task-preservation validation fails.
59
+
60
+ ### Trusted custom macros and slots
61
+
62
+ `--load-forge-extensions` explicitly loads trusted global/project pi-forge registration modules in the host. A dry fixture using `examples/custom-system-status-extension` validated that:
63
+
64
+ - The extension module was discovered and registered.
65
+ - Prompt stacks were reloaded and revalidated after registration.
66
+ - The `cpuLoad` macro and `machine-status` slot compiled without stale unsupported-slot diagnostics.
67
+ - Registrations were unloaded during cleanup.
68
+
69
+ This supports keeping executable dependencies in the trusted host. It also confirms that host resolution must load required registrations before final stack validation. Dependency identity and missing-dependency receipts remain future work.
70
+
71
+ ### Media
72
+
73
+ A real `opencode-go/qwen3.7-plus` turn used the `image-viewer` profile and a 1.6 MB PNG. The model received the image, completed successfully, and identified the visible Pi symbol. The final structured task and image were preserved by the prompt-stack layout.
74
+
75
+ Preflight rejects image input when the selected model does not advertise `image` support. File paths are converted to SDK `ImageContent` only for explicitly supplied `--image` references.
76
+
77
+ ### Timeout, cancellation, response, and trace
78
+
79
+ A real `glm-5.2` turn with a 1 ms host deadline called `session.abort()`, settled in roughly 70 ms, reported `timed-out`, and recorded zero provider tokens/cost. A normal run reported completion, output, duration, session statistics, and a compact normalized lifecycle trace.
80
+
81
+ The SDK does not enforce hard turn, token, or output limits through `createAgentSession`. Its statistics report numeric cost without a currency code. Future response normalization must mark those limits unsupported and treat cost currency as unknown unless the adapter can supply it.
82
+
83
+ The spike trace includes lifecycle and tool start/end metadata only. It excludes streaming content, provider payloads, credentials, and hidden reasoning.
84
+
85
+ ## Contract Revisions Implemented
86
+
87
+ 1. Split backend work into discovery/preflight, backend-assisted plan preparation, and execution. Preparation may occur inside an adapter-controlled pre-provider hook when the backend cannot expose exact prompt-runtime inputs earlier.
88
+ 2. Keep the host responsible for profile/stack resolution, trusted compiler execution, protected-task assembly, diagnostics, and plan validation, even when the adapter supplies runtime inputs through a callback.
89
+ 3. Do not require a passive dry preflight to reproduce the exact prompt when the backend cannot expose all inputs. Record prompt-runtime fidelity and reject execution if exact preparation cannot be completed before provider transport.
90
+ 4. Make tool discovery dynamic and include a separate prompt-runtime receipt; `ToolInfo` alone is insufficient to recreate Pi's prompt.
91
+ 5. Treat tool filtering and access isolation as separate receipts. The current Pi SDK adapter supports only `access=none` honestly.
92
+ 6. Distinguish backend-native limits from host best-effort controls. The observed timeout uses host abort and is not proof of process-level hard isolation.
93
+ 7. Load trusted macro/slot registrations before final stack validation and later add explicit dependency identities/fingerprints.
94
+ 8. Normalize assistant error/aborted stop reasons because `session.prompt()` may settle with a terminal assistant message rather than throw.
95
+
96
+ ## Remaining Concrete-Adapter Gaps
97
+
98
+ - No allowed-root filesystem, subprocess, or agent-network isolation.
99
+ - No hard turn, token, or output-byte enforcement.
100
+ - The retained SDK adapter exposes an empty tool catalog. The default subprocess adapter adds only stack-filtered `read`, `grep`, `find`, and `ls`, under an explicit shared-user rather than sandboxed boundary.
101
+ - The registry, model-callable foreground tool, and text-only subprocess backend now exist, but there is still no artifact store, contract trace storage, media resolution in the shipped path, sandbox enforcement, or staged write workflow.
102
+ - Custom dependency scanning records macro/slot names and registration sources, but it cannot fingerprint executable registration code.
103
+ - The command/tool is foreground-only and does not expose a separate cancel command; Pi/host abort signals and host timeouts terminate the active SDK session or subprocess on a best-effort basis.
104
+
105
+ These gaps do not block the approval-gated read-only foreground workflow. They do require the UI and receipts to keep saying shared-user, and they rule out adding direct write/shell tools until stronger sandboxing or a separately approved staged-change design exists.
106
+
107
+ ## Verification Evidence
108
+
109
+ - Offline suite covers the contract matrix, fake-backend conformance, command consent/dry-plan behavior, and concrete SDK execution through Pi's faux provider and a real in-memory `AgentSession` without network traffic.
110
+ - TypeScript typecheck: passed.
111
+ - Real profile dry preflight: passed.
112
+ - Real `glm-5.2` no-tool completion: passed.
113
+ - Real host-timeout/abort case: passed.
114
+ - Real `qwen3.7-plus` PNG input: passed.
115
+ - Trusted custom macro/slot dry fixture: passed.
116
+
117
+ The generated reports were inspected during the spike and were not committed because they contain machine-specific paths, run IDs, timing, and provider usage.