pi-namespace-patch 1.2.0-namespace.2 → 1.4.0-namespace.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 (233) hide show
  1. package/CHANGELOG.md +102 -0
  2. package/README.md +5 -5
  3. package/dist/bundle/chunks/anthropic-messages-5FHV3XZG.js +6 -0
  4. package/dist/bundle/chunks/azure-openai-responses-KLTRI2FU.js +2 -0
  5. package/dist/bundle/chunks/bedrock-converse-stream.js +12 -9
  6. package/dist/bundle/chunks/{chunk-TRDNDS6A.js → chunk-3V45WARH.js} +6 -3
  7. package/dist/bundle/chunks/chunk-4GGCU36O.js +11 -0
  8. package/dist/bundle/chunks/{chunk-S3GGRYIO.js → chunk-5AGIVGSV.js} +1 -1
  9. package/dist/bundle/chunks/{chunk-F5ZABL6Z.js → chunk-7CBJINYJ.js} +2 -2
  10. package/dist/bundle/chunks/{chunk-DRQDKNXA.js → chunk-7DDKXS2Q.js} +1 -1
  11. package/dist/bundle/chunks/{chunk-CQ3XTLTY.js → chunk-A45IWOC6.js} +4 -4
  12. package/dist/bundle/chunks/{chunk-WJUNYZC5.js → chunk-BSS6T6KQ.js} +1 -1
  13. package/dist/bundle/chunks/{chunk-H3QXYIF7.js → chunk-CF5GWZHW.js} +1 -1
  14. package/dist/bundle/chunks/{chunk-DOST5PQW.js → chunk-EGE5YVUM.js} +1 -1
  15. package/dist/bundle/chunks/{chunk-C2AEIDJZ.js → chunk-FB6L7ZMV.js} +1 -1
  16. package/dist/bundle/chunks/chunk-H76KZMOI.js +2 -0
  17. package/dist/bundle/chunks/{chunk-GDR5JFDJ.js → chunk-INGZIYBC.js} +2 -2
  18. package/dist/bundle/chunks/{chunk-D7PHKNBM.js → chunk-IUWY2KTF.js} +11 -11
  19. package/dist/bundle/chunks/{chunk-4L3WN2XY.js → chunk-JXWJOIH4.js} +1 -1
  20. package/dist/bundle/chunks/{chunk-6CYCI3EJ.js → chunk-LVVZS2NO.js} +1 -1
  21. package/dist/bundle/chunks/{chunk-5GTPJSRI.js → chunk-N43LRJ4N.js} +1 -1
  22. package/dist/bundle/chunks/chunk-NFLURVK3.js +2 -0
  23. package/dist/bundle/chunks/chunk-QSDU2SDK.js +16 -0
  24. package/dist/bundle/chunks/chunk-RISAW7OQ.js +9 -0
  25. package/dist/bundle/chunks/{chunk-AKJ3ZOR3.js → chunk-RWO6Q3KK.js} +1 -1
  26. package/dist/bundle/chunks/{chunk-6MWTY2OB.js → chunk-T726RZZU.js} +10 -9
  27. package/dist/bundle/chunks/{chunk-ZBURYEWF.js → chunk-TMTOQ7H7.js} +1 -1
  28. package/dist/bundle/chunks/chunk-TRERZKRC.js +2 -0
  29. package/dist/bundle/chunks/{chunk-22CYIUS4.js → chunk-UKVNB6C4.js} +1 -1
  30. package/dist/bundle/chunks/chunk-Z6I32PHA.js +2 -0
  31. package/dist/bundle/chunks/{chunk-H7S5Z6OB.js → chunk-ZEIZZEGK.js} +42 -40
  32. package/dist/bundle/chunks/{cli-SWM4FQ6V.js → cli-MIMUTM5L.js} +1 -1
  33. package/dist/bundle/chunks/cloudflare-workers-ai-system-one-7J6REGDV.js +2 -0
  34. package/dist/bundle/chunks/{easter-egg-3d-OX6RUCIS.js → easter-egg-3d-RMLF7AC4.js} +1 -1
  35. package/dist/bundle/chunks/{execute-3CFUPDLS.js → execute-MT7QEDSI.js} +9 -3
  36. package/dist/bundle/chunks/github-copilot.js +1 -1
  37. package/dist/bundle/chunks/google-generative-ai-VPNFZDHO.js +2 -0
  38. package/dist/bundle/chunks/google-vertex-ZFM23MQ4.js +2 -0
  39. package/dist/bundle/chunks/https-proxy-agent-LVOO5TPL.js +13 -0
  40. package/dist/bundle/chunks/{jiti-loader-C3NU2SDH.js → jiti-loader-GL4HJFAD.js} +1 -1
  41. package/dist/bundle/chunks/{jiti-static-loader-E6ANQD5T.js → jiti-static-loader-2RCDI6B4.js} +1 -1
  42. package/dist/bundle/chunks/{lib-TEDK3E5I.js → lib-4AVRLBHM.js} +1 -1
  43. package/dist/bundle/chunks/{llama-cpp-classify-FYN623RH.js → llama-cpp-classify-U4EH6TAM.js} +2 -2
  44. package/dist/bundle/chunks/mistral-conversations-WCAJTBXO.js +5 -0
  45. package/dist/bundle/chunks/{multipart-parser-SQ6GDJL7.js → multipart-parser-FMGQ7342.js} +1 -1
  46. package/dist/bundle/chunks/{node-TWVVAGPO.js → node-AUOGOBCN.js} +1 -1
  47. package/dist/bundle/chunks/{node-N5BSDOA5.js → node-KCMANYC2.js} +1 -1
  48. package/dist/bundle/chunks/openai-codex-responses-JQR3IK2C.js +10 -0
  49. package/dist/bundle/chunks/openai-completions-27RKPL37.js +7 -0
  50. package/dist/bundle/chunks/{openai-decisions-WVPMQ4JD.js → openai-decisions-MJYPUCLX.js} +1 -1
  51. package/dist/bundle/chunks/openai-responses-I2TBW7OL.js +4 -0
  52. package/dist/bundle/chunks/openrouter-images-WBAVKHHY.js +2 -0
  53. package/dist/bundle/chunks/pi-messages-S7RKCD5K.js +8 -0
  54. package/dist/bundle/chunks/{runtime-366HE7CW.js → runtime-GJOZRI5I.js} +1 -1
  55. package/dist/bundle/chunks/{src-UK77SYZE.js → src-QKGKDFSU.js} +1 -1
  56. package/dist/bundle/chunks/typesafe-system-one-4C7ULOVP.js +2 -0
  57. package/dist/bundle/chunks/virtual-modules-6XWNE4NR.js +2 -0
  58. package/dist/bundle/cli-runtime.js +1 -1
  59. package/dist/bundle/index.js +1 -1
  60. package/dist/bundle/rpc-entry.js +1 -1
  61. package/dist/cli/credential-print.d.ts.map +1 -1
  62. package/dist/cli/credential-print.js +4 -4
  63. package/dist/cli/credential-print.js.map +1 -1
  64. package/dist/core/agent-session.d.ts +9 -3
  65. package/dist/core/agent-session.d.ts.map +1 -1
  66. package/dist/core/agent-session.js +45 -15
  67. package/dist/core/agent-session.js.map +1 -1
  68. package/dist/core/client-identity.d.ts +8 -0
  69. package/dist/core/client-identity.d.ts.map +1 -0
  70. package/dist/core/client-identity.js +15 -0
  71. package/dist/core/client-identity.js.map +1 -0
  72. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  73. package/dist/core/compaction/branch-summarization.js +1 -2
  74. package/dist/core/compaction/branch-summarization.js.map +1 -1
  75. package/dist/core/compaction/compaction.d.ts +2 -2
  76. package/dist/core/compaction/compaction.d.ts.map +1 -1
  77. package/dist/core/compaction/compaction.js +20 -15
  78. package/dist/core/compaction/compaction.js.map +1 -1
  79. package/dist/core/export-html/index.d.ts.map +1 -1
  80. package/dist/core/export-html/index.js +2 -0
  81. package/dist/core/export-html/index.js.map +1 -1
  82. package/dist/core/export-html/template.html +1 -0
  83. package/dist/core/export-html/template.js +23 -17
  84. package/dist/core/export-html/vendor/marked-cjk-friendly.min.js +6 -0
  85. package/dist/core/export-html/vendor/marked.min.js +30 -29
  86. package/dist/core/extensions/loader.d.ts.map +1 -1
  87. package/dist/core/extensions/loader.js +3 -0
  88. package/dist/core/extensions/loader.js.map +1 -1
  89. package/dist/core/extensions/virtual-modules.d.ts.map +1 -1
  90. package/dist/core/extensions/virtual-modules.js +3 -0
  91. package/dist/core/extensions/virtual-modules.js.map +1 -1
  92. package/dist/core/model-config.d.ts +939 -8
  93. package/dist/core/model-config.d.ts.map +1 -1
  94. package/dist/core/model-config.js +48 -24
  95. package/dist/core/model-config.js.map +1 -1
  96. package/dist/core/model-resolver.d.ts +56 -43
  97. package/dist/core/model-resolver.d.ts.map +1 -1
  98. package/dist/core/model-resolver.js +253 -358
  99. package/dist/core/model-resolver.js.map +1 -1
  100. package/dist/core/model-runtime.d.ts.map +1 -1
  101. package/dist/core/model-runtime.js +6 -3
  102. package/dist/core/model-runtime.js.map +1 -1
  103. package/dist/core/prompt-templates.d.ts.map +1 -1
  104. package/dist/core/prompt-templates.js +7 -2
  105. package/dist/core/prompt-templates.js.map +1 -1
  106. package/dist/core/provider-composer.d.ts +2 -1
  107. package/dist/core/provider-composer.d.ts.map +1 -1
  108. package/dist/core/provider-composer.js +7 -3
  109. package/dist/core/provider-composer.js.map +1 -1
  110. package/dist/core/sdk.d.ts.map +1 -1
  111. package/dist/core/sdk.js +8 -9
  112. package/dist/core/sdk.js.map +1 -1
  113. package/dist/core/settings-defaults.d.ts +1 -0
  114. package/dist/core/settings-defaults.d.ts.map +1 -1
  115. package/dist/core/settings-defaults.js +1 -0
  116. package/dist/core/settings-defaults.js.map +1 -1
  117. package/dist/core/settings-manager.d.ts +4 -2
  118. package/dist/core/settings-manager.d.ts.map +1 -1
  119. package/dist/core/settings-manager.js +11 -0
  120. package/dist/core/settings-manager.js.map +1 -1
  121. package/dist/core/settings-schema.d.ts +2 -0
  122. package/dist/core/settings-schema.d.ts.map +1 -1
  123. package/dist/core/settings-schema.js +4 -0
  124. package/dist/core/settings-schema.js.map +1 -1
  125. package/dist/core/slash-commands.js +1 -1
  126. package/dist/core/slash-commands.js.map +1 -1
  127. package/dist/core/tools/grep.d.ts.map +1 -1
  128. package/dist/core/tools/grep.js +3 -1
  129. package/dist/core/tools/grep.js.map +1 -1
  130. package/dist/core/tools/render-utils.d.ts +2 -0
  131. package/dist/core/tools/render-utils.d.ts.map +1 -1
  132. package/dist/core/tools/render-utils.js +21 -0
  133. package/dist/core/tools/render-utils.js.map +1 -1
  134. package/dist/core/tools/renderers/read.d.ts.map +1 -1
  135. package/dist/core/tools/renderers/read.js +7 -11
  136. package/dist/core/tools/renderers/read.js.map +1 -1
  137. package/dist/extensions/codemode/execute.d.ts.map +1 -1
  138. package/dist/extensions/codemode/execute.js +101 -14
  139. package/dist/extensions/codemode/execute.js.map +1 -1
  140. package/dist/extensions/mcp/config.d.ts.map +1 -1
  141. package/dist/extensions/mcp/config.js +3 -2
  142. package/dist/extensions/mcp/config.js.map +1 -1
  143. package/dist/index.d.ts +1 -0
  144. package/dist/index.d.ts.map +1 -1
  145. package/dist/index.js +1 -0
  146. package/dist/index.js.map +1 -1
  147. package/dist/main.d.ts.map +1 -1
  148. package/dist/main.js +1 -0
  149. package/dist/main.js.map +1 -1
  150. package/dist/modes/interactive/components/settings-selector.d.ts +3 -1
  151. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  152. package/dist/modes/interactive/components/settings-selector.js +10 -0
  153. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  154. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  155. package/dist/modes/interactive/components/tree-selector.js +2 -9
  156. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  157. package/dist/modes/interactive/interactive-mode.d.ts +11 -15
  158. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  159. package/dist/modes/interactive/interactive-mode.js +76 -111
  160. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  161. package/dist/modes/interactive/session-share.d.ts.map +1 -1
  162. package/dist/modes/interactive/session-share.js +9 -2
  163. package/dist/modes/interactive/session-share.js.map +1 -1
  164. package/dist/package-manager-cli.d.ts.map +1 -1
  165. package/dist/package-manager-cli.js +2 -1
  166. package/dist/package-manager-cli.js.map +1 -1
  167. package/dist/utils/clipboard-command.d.ts.map +1 -1
  168. package/dist/utils/clipboard-command.js +3 -0
  169. package/dist/utils/clipboard-command.js.map +1 -1
  170. package/dist/utils/pi-user-agent.d.ts +1 -0
  171. package/dist/utils/pi-user-agent.d.ts.map +1 -1
  172. package/dist/utils/pi-user-agent.js +3 -2
  173. package/dist/utils/pi-user-agent.js.map +1 -1
  174. package/dist/utils/shell.d.ts.map +1 -1
  175. package/dist/utils/shell.js +45 -13
  176. package/dist/utils/shell.js.map +1 -1
  177. package/dist/utils/tools-manager.d.ts.map +1 -1
  178. package/dist/utils/tools-manager.js +4 -3
  179. package/dist/utils/tools-manager.js.map +1 -1
  180. package/docs/cli.md +3 -3
  181. package/docs/codemode.md +3 -1
  182. package/docs/compaction.md +3 -3
  183. package/docs/custom-provider.md +53 -1
  184. package/docs/docs.json +8 -0
  185. package/docs/environment-variables.md +1 -0
  186. package/docs/extensions.md +133 -10
  187. package/docs/llama-cpp.md +1 -1
  188. package/docs/models-configuration.md +196 -0
  189. package/docs/models.md +21 -5
  190. package/docs/nvidia-rtx-llama-cpp.md +96 -0
  191. package/docs/sessions.md +1 -1
  192. package/docs/settings.md +2 -1
  193. package/docs/tui.md +70 -0
  194. package/docs/usage.md +1 -1
  195. package/docs/windows.md +16 -2
  196. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  197. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  198. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  199. package/examples/extensions/gondolin/package-lock.json +2 -2
  200. package/examples/extensions/gondolin/package.json +1 -1
  201. package/examples/extensions/minimal-mode.ts +4 -5
  202. package/examples/extensions/sandbox/package-lock.json +2 -2
  203. package/examples/extensions/sandbox/package.json +1 -1
  204. package/examples/extensions/subagent/index.ts +4 -6
  205. package/examples/extensions/with-deps/package-lock.json +2 -2
  206. package/examples/extensions/with-deps/package.json +1 -1
  207. package/package.json +7 -6
  208. package/schemas/models.schema.json +88 -34
  209. package/schemas/settings.schema.json +18 -0
  210. package/dist/bundle/chunks/anthropic-messages-Q7SXESRX.js +0 -6
  211. package/dist/bundle/chunks/azure-openai-responses-LM52U7CK.js +0 -2
  212. package/dist/bundle/chunks/chunk-3ZATHNCK.js +0 -9
  213. package/dist/bundle/chunks/chunk-HACVPRBJ.js +0 -11
  214. package/dist/bundle/chunks/chunk-ITZNWD4K.js +0 -2
  215. package/dist/bundle/chunks/chunk-TN4NLWHF.js +0 -11
  216. package/dist/bundle/chunks/chunk-WBQBCFWG.js +0 -2
  217. package/dist/bundle/chunks/chunk-XIX7LUWI.js +0 -2
  218. package/dist/bundle/chunks/cloudflare-workers-ai-system-one-UTBL2KOF.js +0 -2
  219. package/dist/bundle/chunks/google-generative-ai-DXKP72HC.js +0 -2
  220. package/dist/bundle/chunks/google-vertex-W6SIDOVV.js +0 -2
  221. package/dist/bundle/chunks/https-proxy-agent-2VXB7436.js +0 -13
  222. package/dist/bundle/chunks/mistral-conversations-GBMHTSYF.js +0 -5
  223. package/dist/bundle/chunks/openai-codex-responses-RN6B5UPP.js +0 -10
  224. package/dist/bundle/chunks/openai-completions-QW2B4SML.js +0 -7
  225. package/dist/bundle/chunks/openai-responses-KDJUSIU7.js +0 -3
  226. package/dist/bundle/chunks/openrouter-images-DUPG5DZO.js +0 -2
  227. package/dist/bundle/chunks/pi-messages-YHKPQIHC.js +0 -8
  228. package/dist/bundle/chunks/typesafe-system-one-ZSK3RN4B.js +0 -2
  229. package/dist/bundle/chunks/virtual-modules-KHZAOPVZ.js +0 -2
  230. package/dist/core/provider-attribution.d.ts +0 -4
  231. package/dist/core/provider-attribution.d.ts.map +0 -1
  232. package/dist/core/provider-attribution.js +0 -72
  233. package/dist/core/provider-attribution.js.map +0 -1
@@ -51,7 +51,7 @@ Reload replaces the extension runtime, so code after `await ctx.reload()` must n
51
51
 
52
52
  <a id="understand-the-lifecycle"></a>
53
53
 
54
- ## Respect the runtime lifecycle
54
+ ## Understand the runtime lifecycle
55
55
 
56
56
  The factory can be synchronous or asynchronous. Pi waits for an asynchronous factory before startup continues, allowing it to fetch configuration or register providers needed during startup.
57
57
 
@@ -59,13 +59,112 @@ Do not start processes, sockets, watchers, or timers in the factory because some
59
59
  Start long-lived resources from `session_start` or from the command or tool that needs them.
60
60
  Close session-scoped resources from an idempotent `session_shutdown` handler.
61
61
 
62
- A run proceeds from input and `before_agent_start`, through model, message, and tool events, to `agent_end`.
63
- Pi counts as busy while a run starts, including the compaction check and `before_agent_start`, so messages sent meanwhile go into the starting run.
64
- Automatic retries, recovery, compaction, or queued work can continue afterward.
62
+ A normal prompt starts a run. The run repeats turns, then checks whether more work starts another low-level agent run:
63
+
64
+ ```mermaid
65
+ flowchart TD
66
+ Input["input"] --> Before["before_agent_start"]
67
+ Before --> AgentStart["agent_start"]
68
+ AgentStart --> Turns[["turns"]]
69
+ Turns --> AgentEnd["agent_end"]
70
+ AgentEnd --> MoreRuns{"More work?"}
71
+ MoreRuns -->|yes| AgentStart
72
+ MoreRuns -->|no| BeforeSettle["agent_before_settle"]
73
+ BeforeSettle --> Continue{"Continuation requested?"}
74
+ Continue -->|yes| AgentStart
75
+ Continue -->|no| Settled["agent_settled"]
76
+ ```
77
+
78
+ More work means a retry, recovery, compaction retry, or queued work.
79
+
80
+ Each turn sends one model request and executes the tool calls in the assistant response:
81
+
82
+ ```mermaid
83
+ flowchart TD
84
+ TurnStart["turn_start"] --> Request[["model request"]]
85
+ Request --> ToolDecision{"Tool calls?"}
86
+ ToolDecision -->|yes| Tools[["tool execution"]]
87
+ Tools --> TurnEnd["turn_end"]
88
+ ToolDecision -->|no| TurnEnd
89
+ TurnEnd --> MoreTurns{"Another turn?"}
90
+ MoreTurns -->|yes| TurnStart
91
+ MoreTurns -->|no| AgentEnd(["agent_end"])
92
+ ```
93
+
94
+ The model request runs these hooks in order:
95
+
96
+ 1. `context`
97
+ 2. `context_with_system`
98
+ 3. `before_provider_headers`
99
+ 4. `before_provider_request`
100
+ 5. `after_provider_response`
101
+ 6. `provider_stream_event` and assistant message events
102
+
103
+ Tool execution runs these hooks in order for each tool call:
104
+
105
+ 1. `tool_execution_start`
106
+ 2. `tool_call`
107
+ 3. `tool_execution_update` while the tool executes
108
+ 4. `tool_result`
109
+ 5. `tool_execution_end`
110
+
111
+ The lifecycle involves these event groups:
112
+
113
+ | Area | Events |
114
+ |---|---|
115
+ | Input preparation | `input`, `before_agent_start` |
116
+ | Agent run | `agent_start`, `agent_end` |
117
+ | Model turn | `turn_start`, `context`, `context_with_system`, `turn_end` |
118
+ | Provider call | `before_provider_headers`, `before_provider_request`, `after_provider_response`, `provider_stream_event` |
119
+ | Message lifecycle | `message_start`, `message_update`, `message_end` |
120
+ | Tool execution | `tool_execution_start`, `tool_call`, `tool_execution_update`, `tool_result`, `tool_execution_end` |
121
+ | Settlement | `agent_before_settle`, `agent_settled` |
122
+
123
+ Pi counts as busy while a run starts, including the compaction check and `before_agent_start`, so messages sent meanwhile go into the starting run. Message lifecycle events also cover user and tool-result messages; the model request list places only the assistant stream where it occurs in each turn.
124
+
125
+ Tool hooks run for each tool call, and one assistant response can start another turn by returning tool calls. Provider hooks run only when that transport exposes the corresponding stage. See the exported declarations in [`extensions/types.ts`](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/src/core/extensions/types.ts) for exact event payloads and result types.
126
+
127
+ Session startup and replacement follow a separate lifecycle:
128
+
129
+ ```mermaid
130
+ flowchart TD
131
+ Startup["Process startup"] --> Trust["project_trust when needed"]
132
+ Trust --> SessionStart["session_start"]
133
+ Switch["/new or /resume"] --> BeforeSwitch["session_before_switch"]
134
+ Fork["/fork or /clone"] --> BeforeFork["session_before_fork"]
135
+ BeforeSwitch --> Shutdown["session_shutdown"]
136
+ BeforeFork --> Shutdown
137
+ Reload["reload"] --> Shutdown
138
+ Shutdown --> Replacing{"Replacing the runtime?"}
139
+ Replacing -->|yes| Replacement["load replacement extension runtime"]
140
+ Replacement --> SessionStart
141
+ Replacing -->|no| Stop["process exits"]
142
+ SessionStart --> Discover["resources_discover"]
143
+ ```
144
+
145
+ `project_trust` runs before trusted project resources load and can run again when session replacement enters a new working directory. Retries, recovery, compaction, and queued messages can start another agent run after `agent_end`, so `agent_end` is not a final-idle signal. Reload and session replacement invalidate the old runtime. Code after `await ctx.reload()` still belongs to the old runtime and must not reuse its session-bound state.
146
+
147
+ ### Choose an event by intent
148
+
149
+ | Goal | Event |
150
+ |---|---|
151
+ | Inspect, transform, or consume raw user input | `input` |
152
+ | Change prompt sections, active tools, or add context before a run | `before_agent_start` |
153
+ | Observe one low-level agent run | `agent_start`, `agent_end` |
154
+ | Act after one model response and its tool calls | `turn_end` |
155
+ | Filter conversation messages for one provider request | `context` |
156
+ | Transform the complete transcript, including system messages | `context_with_system` |
157
+ | Change request headers or the serialized provider payload | `before_provider_headers`, `before_provider_request` |
158
+ | Observe provider response metadata or parsed stream events | `after_provider_response`, `provider_stream_event` |
159
+ | Authorize or modify a tool call | `tool_call` |
160
+ | Redact, annotate, or replace a tool result | `tool_result` |
161
+ | Append final entries or request one last continuation | `agent_before_settle` |
162
+ | Run notification work after automatic processing has stopped | `agent_settled` |
163
+ | Initialize or release session-scoped resources | `session_start`, `session_shutdown` |
164
+
65
165
  <a id="agent_start--agent_end--agent_before_settle--agent_settled"></a>
66
166
 
67
- `agent_before_settle` is the final actionable boundary: it can append entries and request one continuation.
68
- `agent_settled` is final and notification-only; use it when an integration needs to know Pi will not continue automatically.
167
+ Use `agent_before_settle` when an extension must append entries or request one final continuation. Use `agent_settled` for notification-only work that should run after Pi has no automatic continuation remaining.
69
168
 
70
169
  <a id="extensionapi-methods"></a>
71
170
 
@@ -101,6 +200,19 @@ Use each event’s declared result type rather than assuming every return value
101
200
 
102
201
  Events cover resource discovery, sessions, agent and message lifecycle, providers, tools, and raw input.
103
202
 
203
+ #### Understand handler composition
204
+
205
+ | Pattern | Behavior |
206
+ |---|---|
207
+ | Notification | Return values are ignored. Use the event for observation only. |
208
+ | In-place mutation | Later handlers see changes to values such as `before_provider_headers.headers`, `before_agent_start.systemPromptOptions`, and `tool_call.input`. |
209
+ | Chained transformation | Later handlers receive the value returned by earlier `input`, `context`, `context_with_system`, `message_end`, `tool_result`, and `before_provider_request` handlers. |
210
+ | Gate | A handled input, blocked tool call, cancelled session operation, or handled `user_bash` command stops that operation's normal fallback path. |
211
+ | Actionable boundary | Each `turn_end` or `agent_before_settle` handler sees the entries and continuation decision proposed so far. |
212
+ | Last override | The last `cache_warming_decision` handler that returns an action wins. |
213
+
214
+ Notification handlers should not perform slow work on a streaming event. Transform handlers should preserve fields they do not intentionally own, and gate handlers should fail closed when their safety decision cannot be made.
215
+
104
216
  `before_agent_start` exposes both the current prompt and its structured `systemPromptOptions`. `event.messageType` says what started the run: `"user"` for a prompt or `pi.sendUserMessage()`, with `prompt` and `images`; `"custom"` for `pi.sendMessage(..., { triggerTurn: true })`, with the starting `message` and no `prompt`. A returned `message` is added to either kind of run; check `messageType` to skip runs started by custom messages. Prefer changing prompt sections, selected tools, or guidelines so Pi can append a transcript delta. Returning `systemPrompt`, or setting `forceSystemPrompt`, replaces the whole prompt for that run while the transcript continues recording the structured sections. Providers receive the forced text as their leading system prompt.
105
217
 
106
218
  `message_end` can replace a finalized message while preserving its role. `tool_call` can mutate input or block execution. `tool_result` handlers compose, with each handler seeing prior changes.
@@ -271,7 +383,18 @@ Use `ctx.shutdown()` to request an orderly process shutdown.
271
383
 
272
384
  ## Examples and reference
273
385
 
274
- The checked [extension examples](../examples/extensions/) cover tools, lifecycle events, commands, flags, shortcuts, state, rendering, providers, OAuth, remote execution, and terminal components.
275
- Start with the smallest example matching your integration point.
276
-
277
- Use [Custom Providers](custom-provider.md) for model-service integrations, [Terminal UI](tui.md) for custom components, and [Pi Packages](packages.md) to install or distribute extensions with other resources.
386
+ The checked [extension examples](../examples/extensions/) are the implementation reference. Start with the smallest example matching the behavior you need:
387
+
388
+ | Goal | Start with | Why |
389
+ |---|---|---|
390
+ | Confirm or block tool calls | [`permission-gate.ts`](../examples/extensions/permission-gate.ts) | Combines `tool_call`, mode-aware UI, and fail-closed behavior. |
391
+ | Modify prompt sections for a run | [`prompt-customizer.ts`](../examples/extensions/prompt-customizer.ts) | Uses structured prompt options instead of replacing the complete prompt. |
392
+ | Keep branch-aware tool state | [`todo.ts`](../examples/extensions/todo.ts) | Reconstructs state from persisted entries and renders it in the TUI. |
393
+ | Register tools after startup | [`dynamic-tools.ts`](../examples/extensions/dynamic-tools.ts) | Shares registration logic between lifecycle and command handlers. |
394
+ | Run cancellable model work from a command | [`qna.ts`](../examples/extensions/qna.ts) | Combines nested model calls, cancellation, and custom UI. |
395
+ | Replace the main editor | [`modal-editor.ts`](../examples/extensions/modal-editor.ts) | Extends `CustomEditor` while preserving application controls. |
396
+ | Run finalization at shutdown | [`auto-commit-on-exit.ts`](../examples/extensions/auto-commit-on-exit.ts) | Uses the session shutdown hook for finalization work. |
397
+ | Route built-in tools to another machine | [`ssh.ts`](../examples/extensions/ssh.ts) | Reuses tool operation interfaces instead of recreating tool contracts. |
398
+ | Add OAuth and provider-specific streaming | [`custom-provider-gitlab-duo/`](../examples/extensions/custom-provider-gitlab-duo/) | Shows a complete provider integration while delegating supported protocols. |
399
+
400
+ Use [Custom Providers](custom-provider.md) for model-service integrations, [Terminal UI](tui.md) for custom components, and [Pi Packages](packages.md) to install or distribute extensions with other resources. Use the exported declarations for exact signatures, payloads, and result schemas; this guide focuses on lifecycle choices, composition, and durable implementation patterns.
package/docs/llama-cpp.md CHANGED
@@ -41,7 +41,7 @@ A single-file model can sit directly in the model directory. Put multimodal and
41
41
  └── large-model-Q4_K_M-00003-of-00003.gguf
42
42
  ```
43
43
 
44
- Restart the router after manually adding files. For per-model context sizes and other options, use [llama.cpp model presets](https://github.com/ggml-org/llama.cpp/blob/master/tools/server/README.md#model-presets).
44
+ Restart the router after manually adding files. For per-model context sizes and other options, use [llama.cpp model presets](https://github.com/ggml-org/llama.cpp/blob/master/tools/server/README.md#model-presets). For settings tuned to a specific NVIDIA GPU and model, see [Run Pi with NVIDIA RTX and llama.cpp](nvidia-rtx-llama-cpp.md).
45
45
 
46
46
  ## Configure Pi
47
47
 
@@ -0,0 +1,196 @@
1
+ # Models Configuration
2
+
3
+ <!-- This file is generated from TypeScript source. Do not edit it manually; run npm run generate:references. -->
4
+
5
+ This reference lists the fields of `models.json`. See [Configure a compatible endpoint](models.md#configure-a-compatible-endpoint) for examples and [Configuration](configuration.md#agent-directory) for the file location.
6
+
7
+ Add `"$schema": "https://pi.dev/schemas/models.schema.json"` to `models.json` for editor validation and completion.
8
+
9
+ ## Providers
10
+
11
+ Each key under `providers` is a provider ID. A built-in provider ID changes that provider. Any other ID adds a provider, which needs `baseUrl` and an `api` at provider or model level.
12
+
13
+ | Field | Type | Default | Description |
14
+ |---|---|---|---|
15
+ | `name` | string | | Display name. |
16
+ | `baseUrl` | string | | API base URL. Also applies to the provider's built-in models. |
17
+ | `apiKey` | string | | Literal key, "$NAME" environment interpolation, or "!command" output. |
18
+ | `api` | string | | API for the provider's models, such as "openai-completions", "openai-responses", "anthropic-messages", or "google-generative-ai". |
19
+ | `oauth` | `"radius"` | | OAuth login flow. Requires baseUrl. |
20
+ | `headers` | object | | Request headers. Values resolve like apiKey. |
21
+ | `compat` | [object](#compatibility) | | Provider and model compatibility overrides. |
22
+ | `authHeader` | boolean | `false` | Whether to send the resolved API key as Authorization: Bearer. |
23
+ | `models` | [`object[]`](#models) | | |
24
+ | `modelOverrides` | [object](#model-overrides) | | |
25
+
26
+ ## Models
27
+
28
+ Each `models` entry adds a model to the provider. An entry whose `id` matches an existing model replaces it.
29
+
30
+ | Field | Type | Default | Description |
31
+ |---|---|---|---|
32
+ | `id` | string | | Required. Model ID sent to the API. |
33
+ | `name` | string | | Display name, also matched by --model patterns. |
34
+ | `api` | string | | Overrides the provider api. |
35
+ | `baseUrl` | string | | Overrides the provider baseUrl. |
36
+ | `reasoning` | boolean | `false` | Whether the model supports thinking. |
37
+ | `thinkingLevelMap` | object | | Provider value sent for each thinking level. null marks a level unsupported. Levels the model does not map use the provider's default value through high; xhigh and max are unsupported. |
38
+ | `input` | `("text" \| "image")[]` | `["text"]` | |
39
+ | `inputLimits` | [object](#input-limits) | | |
40
+ | `cost` | object | | |
41
+ | `cost.input` | number | | Required. Input cost in USD per million tokens. |
42
+ | `cost.output` | number | | Required. Output cost in USD per million tokens. |
43
+ | `cost.cacheRead` | number | | Required. Cache-read cost in USD per million tokens. |
44
+ | `cost.cacheWrite` | number | | Required. Cache-write cost in USD per million tokens. |
45
+ | `cost.tiers` | `object[]` | | Request-wide pricing tiers. The highest matching input threshold applies to the full request. |
46
+ | `cost.tiers[].inputTokensAbove` | number | | Required. Use this tier when total request input exceeds this token count. |
47
+ | `cost.tiers[].input` | number | | Required. Input cost in USD per million tokens. |
48
+ | `cost.tiers[].output` | number | | Required. Output cost in USD per million tokens. |
49
+ | `cost.tiers[].cacheRead` | number | | Required. Cache-read cost in USD per million tokens. |
50
+ | `cost.tiers[].cacheWrite` | number | | Required. Cache-write cost in USD per million tokens. |
51
+ | `promptCache` | object | | Best-effort prompt cache lifetime in seconds for the short and long retention tiers. Pi warms only tiers with a known lifetime. |
52
+ | `contextWindow` | number | `128000` | Context window in tokens. |
53
+ | `maxTokens` | number | `16384` | Maximum output tokens. |
54
+ | `samplingParams` | object | | Fields merged into every request body. Applies only to OpenAI-compatible APIs. |
55
+ | `samplingParamsByThinkingLevel` | object | | samplingParams per thinking level, merged over samplingParams. |
56
+ | `headers` | object | | Request headers. Values resolve like apiKey. |
57
+ | `compat` | [object](#compatibility) | | Provider and model compatibility overrides. |
58
+
59
+ ## Model overrides
60
+
61
+ Each `modelOverrides` key is the ID of a built-in or extension model. Unknown IDs are ignored. Object fields, such as `cost`, merge with the model's value field by field; other fields replace it.
62
+
63
+ | Field | Type | Default | Description |
64
+ |---|---|---|---|
65
+ | `name` | string | | Display name, also matched by --model patterns. |
66
+ | `reasoning` | boolean | | Whether the model supports thinking. |
67
+ | `thinkingLevelMap` | object | | Provider value sent for each thinking level. null marks a level unsupported. Levels the model does not map use the provider's default value through high; xhigh and max are unsupported. |
68
+ | `input` | `("text" \| "image")[]` | | |
69
+ | `inputLimits` | [object](#input-limits) | | |
70
+ | `cost` | object | | |
71
+ | `cost.input` | number | | Input cost in USD per million tokens. |
72
+ | `cost.output` | number | | Output cost in USD per million tokens. |
73
+ | `cost.cacheRead` | number | | Cache-read cost in USD per million tokens. |
74
+ | `cost.cacheWrite` | number | | Cache-write cost in USD per million tokens. |
75
+ | `cost.tiers` | `object[]` | | Request-wide pricing tiers. The highest matching input threshold applies to the full request. |
76
+ | `cost.tiers[].inputTokensAbove` | number | | Required. Use this tier when total request input exceeds this token count. |
77
+ | `cost.tiers[].input` | number | | Required. Input cost in USD per million tokens. |
78
+ | `cost.tiers[].output` | number | | Required. Output cost in USD per million tokens. |
79
+ | `cost.tiers[].cacheRead` | number | | Required. Cache-read cost in USD per million tokens. |
80
+ | `cost.tiers[].cacheWrite` | number | | Required. Cache-write cost in USD per million tokens. |
81
+ | `promptCache` | object | | Best-effort prompt cache lifetime in seconds for the short and long retention tiers. Pi warms only tiers with a known lifetime. |
82
+ | `contextWindow` | number | | Context window in tokens. |
83
+ | `maxTokens` | number | | Maximum output tokens. |
84
+ | `samplingParams` | object | | Fields merged into every request body. Applies only to OpenAI-compatible APIs. |
85
+ | `samplingParamsByThinkingLevel` | object | | samplingParams per thinking level, merged over samplingParams. |
86
+ | `headers` | object | | Request headers. Values resolve like apiKey. |
87
+ | `compat` | [object](#compatibility) | | Provider and model compatibility overrides. |
88
+
89
+ ## Input limits
90
+
91
+ See [Describe model input and caching](models.md#describe-model-input-and-caching).
92
+
93
+ | Field | Type | Default | Description |
94
+ |---|---|---|---|
95
+ | `maxRequestBytes` | integer | | Maximum serialized provider request size in bytes. |
96
+ | `images` | object | | |
97
+ | `images.resize` | object | | Cache-safe resize profile applied before a new image enters conversation history. |
98
+ | `images.resize.maxWidth` | integer | | |
99
+ | `images.resize.maxHeight` | integer | | |
100
+ | `images.resize.maxBytes` | integer | | Maximum base64-encoded payload size in bytes. |
101
+ | `images.resize.jpegQuality` | integer | | |
102
+ | `images.maxPerMessage` | integer | | Maximum images accepted in one provider message. |
103
+ | `images.maxPerRequest` | integer | | Maximum images accepted across one provider request. |
104
+
105
+ ## Compatibility
106
+
107
+ `compat` describes verified differences from an API's standard request and response behavior. Model values override provider values per field. The fields that apply depend on the model's `api`.
108
+
109
+ ### `openai-completions`
110
+
111
+ | Field | Type | Default | Description |
112
+ |---|---|---|---|
113
+ | `supportsStore` | boolean | | Whether the provider supports the store field. Default: auto-detected from URL. |
114
+ | `supportsDeveloperRole` | boolean | | Whether the provider supports the developer role instead of system. Default: auto-detected from URL. |
115
+ | `supportsReasoningEffort` | boolean | | Whether the provider supports reasoning_effort. Default: auto-detected from URL. |
116
+ | `supportsUsageInStreaming` | boolean | `true` | Whether the provider supports stream_options.include_usage for token usage in streaming responses. |
117
+ | `supportsFinishReason` | boolean | `true` | Whether streamed responses include finish_reason. When false, pi infers stop or toolUse when the stream ends. |
118
+ | `maxTokensField` | `"max_completion_tokens" \| "max_tokens"` | | Which field to use for max tokens. Default: auto-detected from URL. |
119
+ | `requiresToolResultName` | boolean | | Whether tool results require the name field. Default: auto-detected from URL. |
120
+ | `requiresAssistantAfterToolResult` | boolean | | Whether a user message after tool results requires an assistant message in between. Default: auto-detected from URL. |
121
+ | `requiresThinkingAsText` | boolean | | Whether thinking blocks must be converted to text blocks with &lt;thinking> delimiters. Default: auto-detected from URL. |
122
+ | `requiresReasoningContentOnAssistantMessages` | boolean | | Whether all replayed assistant messages must include an empty reasoning_content field when reasoning is enabled. Default: auto-detected from URL. |
123
+ | `thinkingFormat` | `"openai" \| "openrouter" \| "deepseek" \| "together" \| "baseten" \| "zai" \| "qwen" \| "chat-template" \| "qwen-chat-template" \| "string-thinking" \| "ant-ling"` | | Format for reasoning or thinking parameters. When omitted, Pi auto-detects the format from the provider URL. "openai" uses reasoning_effort, "openrouter" uses reasoning.effort, "deepseek" uses thinking.type plus reasoning_effort when supported, "together" uses reasoning.enabled plus reasoning_effort when supported, "baseten" uses configurable chat_template_args plus reasoning_effort when supported, "zai" uses thinking.type, "qwen" uses top-level enable_thinking, "qwen-chat-template" uses chat_template_kwargs.enable_thinking and preserve_thinking, "chat-template" uses configurable chat_template_kwargs, "string-thinking" uses top-level thinking, and "ant-ling" uses reasoning.effort only when the mapped effort is non-null. |
124
+ | `chatTemplateKwargs` | object | | Kwargs sent as chat_template_kwargs when thinkingFormat is "chat-template". Use $var with "thinking.enabled", "thinking.effort", or "thinking.budget" for pi-controlled values. |
125
+ | `chatTemplateArgs` | object | | Arguments sent as chat_template_args when thinkingFormat is "baseten". Use $var with "thinking.enabled", "thinking.effort", or "thinking.budget" for pi-controlled values. |
126
+ | `openRouterRouting` | object | | OpenRouter provider routing preferences. Controls which upstream providers OpenRouter routes requests to. Sent as the provider field in the OpenRouter API request body. See https://openrouter.ai/docs/guides/routing/provider-selection. |
127
+ | `vercelGatewayRouting` | object | | Vercel AI Gateway routing preferences. Controls which upstream providers the gateway routes requests to. See https://vercel.com/docs/ai-gateway/models-and-providers/provider-options. |
128
+ | `zaiToolStream` | boolean | `false` | Whether z.ai supports top-level tool_stream for streaming tool call deltas. |
129
+ | `thinkingTokenBudgetField` | `"thinking_token_budget" \| "thinking_budget" \| "thinking_budget_tokens"` | | Top-level request field used to cap reasoning tokens from thinkingBudgets. Reasoning and the answer share max_tokens on these endpoints. "thinking_token_budget" is vLLM, "thinking_budget" is Qwen, DashScope, or SGLang, and "thinking_budget_tokens" is llama.cpp. Off by default and not set on the generated catalog. |
130
+ | `supportsThinkingTokenBudget` | boolean | `false` | Alias for thinkingTokenBudgetField: "thinking_token_budget" (vLLM). Prefer thinkingTokenBudgetField. |
131
+ | `supportsOpenAIGrammarTools` | boolean | `false` | Whether the provider supports OpenAI custom tools with Lark or regex grammar formats. When false, grammar-constrained tools fall back to normal function tools. The generated catalog enables this for capable models. |
132
+ | `supportsMidConvoSystemMessages` | boolean | `false` | Whether the exact model accepts system or developer messages after the conversation has started. When false, later system messages are folded into the leading system message. The generated catalog enables this for verified models. |
133
+ | `supportsMidConvoToolAdditions` | boolean | `false` | Whether system messages can introduce additional tools mid-conversation. Requires supportsMidConvoSystemMessages. The generated catalog enables this for capable models. |
134
+ | `supportsStrictMode` | boolean | `false` | Whether the provider supports the strict field in tool definitions. Generated capable models enable it explicitly. |
135
+ | `cacheControlFormat` | `"anthropic"` | | Cache control convention for prompt caching. Anthropic applies cache_control markers to the system prompt, last tool definition, and last user, assistant, or tool-result text content. |
136
+ | `sendSessionAffinityHeaders` | boolean | | Whether to send session-affinity data from options.sessionId. Default: true for OpenRouter endpoints, false otherwise. |
137
+ | `sessionAffinityFormat` | `"openai" \| "openai-nosession" \| "openrouter"` | | Session-affinity header format. openai sends session_id, x-client-request-id, and x-session-affinity; openai-nosession sends x-client-request-id and x-session-affinity; openrouter sends x-session-id. Does not affect prompt_cache_key. Default: auto-detected. |
138
+ | `supportsLongCacheRetention` | boolean | | Whether the provider supports long prompt cache retention (prompt_cache_retention: "24h" or Anthropic-style cache_control.ttl: "1h", depending on format). Default: auto-detected from provider and URL. |
139
+ | `vllmPriority` | number | | vLLM scheduler priority sent as the top-level priority request field. Lower values are handled earlier and the server default is 0. Only meaningful with --scheduling-policy priority. Off by default and not set on the generated catalog. |
140
+
141
+ ### `openai-responses`
142
+
143
+ Also applies to `azure-openai-responses` and `openai-codex-responses`.
144
+
145
+ | Field | Type | Default | Description |
146
+ |---|---|---|---|
147
+ | `supportsDeveloperRole` | boolean | `true` | Whether the provider supports the developer role instead of system. |
148
+ | `supportsMidConvoSystemMessages` | boolean | `false` | Whether the exact model accepts developer or system messages after the conversation has started. When false, later system messages are folded into the leading system message. The generated catalog enables this for verified models. |
149
+ | `sessionAffinityFormat` | `"openai" \| "openai-nosession" \| "openrouter"` | | Session-affinity header format. openai sends session_id and x-client-request-id; openai-nosession sends x-client-request-id; openrouter sends x-session-id. Does not affect prompt_cache_key. Default: auto-detected. |
150
+ | `supportsLongCacheRetention` | boolean | `true` | Whether the provider supports long prompt cache retention. This uses prompt_cache_options.ttl: "30m" on GPT-5.6+ and prompt_cache_retention: "24h" on earlier models. |
151
+ | `supportsStrictMode` | boolean | | Whether the provider supports strict JSON-schema function tools. Defaults are API-specific; generated OpenAI models enable it explicitly. |
152
+ | `supportsOpenAIGrammarTools` | boolean | `false` | Whether to emit OpenAI custom tools with Lark or regex grammar formats. When false, grammar-constrained tools fall back to normal function tools. The generated catalog enables this for capable models. |
153
+ | `supportsAdditionalTools` | boolean | `false` | Whether the model supports message-anchored additional_tools input items. |
154
+ | `supportsToolSearch` | boolean | `false` | Whether the model supports client-executed tool search for transcript-anchored additions. |
155
+ | `supportsExplicitPromptCacheMode` | boolean | `false` | Whether the model accepts prompt_cache_options. Older OpenAI models reject the parameter. |
156
+ | `supportsMidConvoEffort` | boolean | `false` | Whether thinking level changes are sent as configuration_update input items instead of changing reasoning.effort, so a change keeps the prompt cache. Only used by the openai-responses API, and ignored with Sign in with ChatGPT on the openai provider. |
157
+ | `supportsMaxOutputTokens` | boolean | `true` | Whether the provider accepts max_output_tokens. Some Codex-protocol gateways reject it. |
158
+
159
+ ### `anthropic-messages`
160
+
161
+ | Field | Type | Default | Description |
162
+ |---|---|---|---|
163
+ | `supportsEagerToolInputStreaming` | boolean | `true` | Whether the provider accepts per-tool eager_input_streaming. When false, the Anthropic provider omits tools[].eager_input_streaming and sends the legacy fine-grained-tool-streaming-2025-05-14 beta header for tool-enabled requests. |
164
+ | `supportsLongCacheRetention` | boolean | `true` | Whether the provider supports Anthropic long cache retention through cache_control.ttl. |
165
+ | `sendSessionAffinityHeaders` | boolean | | Whether to send x-session-affinity from options.sessionId when caching is enabled. Required for providers like Fireworks that use session affinity for prompt cache routing; requests to the same replica maximize cache hits. Default: true for OpenRouter endpoints, false otherwise. |
166
+ | `sessionAffinityFormat` | `"openrouter"` | | Session-affinity format. openrouter sends x-session-id; when unset, sends x-session-affinity. |
167
+ | `supportsCacheControlOnTools` | boolean | `true` | Whether the provider supports Anthropic-style cache_control markers on tool definitions. When false, cache_control is omitted from tool parameters. Some Anthropic-compatible providers, such as Fireworks, do not support this field on tools and may reject or ignore it. |
168
+ | `supportsTemperature` | boolean | `true` | Whether the model accepts the Anthropic temperature request field. Claude Opus 4.7+ rejects non-default values. |
169
+ | `forceAdaptiveThinking` | boolean | `false` | Whether to force adaptive thinking (thinking.type: adaptive plus output_config.effort) regardless of model ID. Built-in models that require adaptive thinking set this in generated metadata. Custom Anthropic-compatible providers can set this to true for any model whose upstream requires the adaptive format. Set false to opt out on overridden built-in models. |
170
+ | `allowEmptySignature` | boolean | `false` | Whether to replay empty thinking signatures instead of converting thinking to text. |
171
+ | `supportsStrictTools` | boolean | `false` | Whether the provider supports Anthropic strict tool schemas. Generated Anthropic models enable it explicitly. |
172
+ | `supportsMidConvoEffort` | boolean | `false` | Whether the exact model transport supports effort-only system messages and thinking binding controls. |
173
+ | `supportsMidConvoSystemMessages` | boolean | `false` | Whether the exact model accepts system-role messages inside the conversation. When false, later system messages are folded into the top-level system prompt. |
174
+ | `supportsMidConvoToolChanges` | boolean | `false` | Whether the exact model accepts mid-conversation tool_addition and tool_removal blocks. Requires supportsMidConvoSystemMessages. |
175
+ | `allowedFallbackModels` | `object[]` | | Models Anthropic accepts for server-side refusal fallback, with local pricing metadata for returned fallback responses. When absent or empty, callers must omit fallbacks; Anthropic rejects the field for models with no permitted fallback targets. |
176
+
177
+ ### `bedrock-converse-stream`
178
+
179
+ | Field | Type | Default | Description |
180
+ |---|---|---|---|
181
+ | `supportsStrictMode` | boolean | `false` | Whether the model supports Bedrock strict tool schemas. |
182
+
183
+ ### `mistral-conversations`
184
+
185
+ | Field | Type | Default | Description |
186
+ |---|---|---|---|
187
+ | `supportsMidConvoSystemMessages` | boolean | `false` | Whether the exact model accepts system messages after the conversation has started. When false, later system messages are folded into the leading system message. |
188
+
189
+ ### `typesafe-system-one`
190
+
191
+ Also applies to `cloudflare-workers-ai-system-one`.
192
+
193
+ | Field | Type | Default | Description |
194
+ |---|---|---|---|
195
+ | `imageInput` | `"assets" \| "images" \| "state-image-urls"` | | How the service takes classifier images. "assets" sends TypeSafe's assets map (PNG and JPEG) and references the images from the state, as TypeSafe and Jev on Workers AI expect. "images" sends Clef's images list of data URLs (PNG, JPEG, and WebP, at most four), as Clef on Workers AI expects. "state-image-urls" places image_url parts before the state, as OpenRouter expects; OpenRouter drops assets without an error. Auto-detected from the provider, baseUrl, and model ID. Classifier models read it from the provider-level compat; models and modelOverrides entries apply to chat models only. |
196
+ | `sendImageOptInHeader` | boolean | | Whether requests with images send the TypeSafe-Danger-Zone-Images: true header, which TypeSafe's own API requires for its experimental image input. Defaults to true for the typesafe-system-one API unless the model is detected as OpenRouter. |
package/docs/models.md CHANGED
@@ -62,10 +62,22 @@ Use [`models.json`](configuration.md#agent-directory) when an endpoint speaks an
62
62
  }
63
63
  ```
64
64
 
65
- The dummy key makes the model available to Pi; Ollama ignores it. For an authenticated endpoint, `apiKey` and header values can use `$NAME` or `${NAME}` environment interpolation, a literal value, or a leading `!command`. Commands in `models.json` run at request time and are not cached by Pi.
65
+ The dummy key makes the model available to Pi; Ollama ignores it. For an authenticated endpoint, `apiKey` and header values can use `$NAME` or `${NAME}` environment interpolation, a literal value, or a leading `!command`. `$$` and `$!` produce a literal `$` and `!`. Commands in `models.json` run at request time and are not cached by Pi.
66
66
 
67
67
  Opening `/model` reloads the file. A `models` entry adds or replaces a model with the same ID on that provider. Use `modelOverrides` to change metadata for an existing built-in or extension-provided model without replacing the provider's model list. Unknown override IDs are ignored.
68
68
 
69
+ To route a built-in provider through a proxy, set only its `baseUrl`. Its models and authentication stay unchanged:
70
+
71
+ ```json
72
+ {
73
+ "providers": {
74
+ "anthropic": { "baseUrl": "https://proxy.example.com/v1" }
75
+ }
76
+ }
77
+ ```
78
+
79
+ [Models Configuration](models-configuration.md) lists every field.
80
+
69
81
  ### Describe model input and caching
70
82
 
71
83
  Use `inputLimits.images.resize` to control how Pi encodes new image attachments, `read` results, and tool-result images before storing them in conversation history:
@@ -129,20 +141,20 @@ Compatibility settings should describe verified differences in the endpoint's re
129
141
 
130
142
  ## Use classifier models
131
143
 
132
- Classifier models do not chat. They answer typed questions about JSON state: pick one of several choices, answer yes or no, or give a score, each with probabilities. Pi includes TypeSafe's Jev model from these providers, Cloudflare's Clef and Clef Flash models from Workers AI, and OpenAI's GPT-6 Luna through the [Decisions API](https://developers.openai.com/api/docs/guides/decisions):
144
+ Classifier models do not chat. They answer typed questions about JSON state: pick one of several choices, answer yes or no, or give a score, each with probabilities. Pi includes TypeSafe's Jev model from these providers, Cloudflare's Clef, Clef Flash, and Clef Omni models from Workers AI and OpenRouter, and OpenAI's GPT-6 Luna through the [Decisions API](https://developers.openai.com/api/docs/guides/decisions):
133
145
 
134
146
  | Provider | Model IDs | Authentication |
135
147
  |---|---|---|
136
148
  | `typesafe` | `jev-latest` | `TYPESAFE_API_KEY` |
137
- | `openrouter` | `typesafe/jev-1.13`, `~typesafe/jev-latest` | `OPENROUTER_API_KEY` or `/login` |
138
- | `cloudflare-workers-ai` | `typesafe/jev`, `@cf/cloudflare/clef`, `@cf/cloudflare/clef-flash` | `CLOUDFLARE_API_KEY` and `CLOUDFLARE_ACCOUNT_ID` |
149
+ | `openrouter` | `typesafe/jev-1.13`, `typesafe/jev-1.14`, `~typesafe/jev-latest`, `cloudflare/clef`, `cloudflare/clef-flash`, `cloudflare/clef-omni` | `OPENROUTER_API_KEY` or `/login` |
150
+ | `cloudflare-workers-ai` | `typesafe/jev`, `typesafe/jev-1.14.0`, `@cf/cloudflare/clef`, `@cf/cloudflare/clef-flash`, `@cf/cloudflare/clef-omni` | `CLOUDFLARE_API_KEY` and `CLOUDFLARE_ACCOUNT_ID` |
139
151
  | `vercel-ai-gateway` | `typesafe-ai/jev` | `AI_GATEWAY_API_KEY` |
140
152
  | `opencode` | `jev-1.13`, `jev-1.13-free` | `OPENCODE_API_KEY` |
141
153
  | `openai` | `gpt-6-luna` | `OPENAI_API_KEY` |
142
154
 
143
155
  Chat models on a [llama.cpp router](llama-cpp.md#classification) are also listed as classifier models.
144
156
 
145
- OpenAI's Decisions API needs an API key. Sign in with ChatGPT credentials do not work with it, so `gpt-6-luna` is not listed as available while `openai` is logged in through `/login`, even when `OPENAI_API_KEY` is set; log out of `openai` to use the key. GPT-6 Luna also judges images passed in `images` (see [Codemode](codemode.md#classify)); other classifier models return an error for them. The API rejects inputs above 922K tokens, but requests that run longer than about five seconds, currently above roughly 600K input tokens, fail with a gateway timeout.
157
+ OpenAI's Decisions API needs an API key. Sign in with ChatGPT credentials do not work with it, so `gpt-6-luna` is not listed as available while `openai` is logged in through `/login`, even when `OPENAI_API_KEY` is set; log out of `openai` to use the key. The API rejects inputs above 922K tokens, but requests that run longer than about five seconds, currently above roughly 600K input tokens, fail with a gateway timeout.
146
158
 
147
159
  Classifier models do not appear in `/model`. The model reaches them through the [`codemode`](cli.md#enable-codemode) tool, which is off unless an MCP server turned it on. Enable it with `"defaultTools": ["+codemode"]` in [settings](settings.md#tools). Scripts then list classifier models with `models.getAvailableOfType("classifier")` and call `models.classify(model, { state, questions })`:
148
160
 
@@ -163,6 +175,10 @@ return result.answers;
163
175
 
164
176
  [Codemode](codemode.md#classify) describes the question and answer types.
165
177
 
178
+ Classifier models whose `input` includes `"image"` also judge images passed in `images`; other classifier models return an error for them. Image input works with GPT-6 Luna, with Jev from `typesafe` and `cloudflare-workers-ai`, with Clef from `cloudflare-workers-ai`, and with the OpenRouter models whose catalog lists image input. Jev takes PNG and JPEG images, and image input is experimental on TypeSafe's own API. Clef takes PNG, JPEG, and WebP images, at most four per request. Jev from `vercel-ai-gateway` and `opencode` takes text only.
179
+
180
+ Each System One service takes images in its own request format, set by the model's `compat.imageInput`. Built-in models set it, so overriding a provider's `baseUrl` keeps it. A custom provider that proxies another service's classifiers sets it in its provider-level `compat` in `models.json` (see [System One compatibility](models-configuration.md#typesafe-system-one)). OpenRouter in particular answers requests in TypeSafe's format without an error but drops the images.
181
+
166
182
  When the service reports token counts, as all System One services do, `result.usage` carries them with their cost. Pi adds the usage of a script's classifier calls to the `codemode` tool result, so it counts toward the session cost in the footer and `/session`. The cost uses the model's catalog price; models without one, such as TypeSafe's direct `jev-latest`, report tokens at no cost.
167
183
 
168
184
  Extensions call classifiers through `ctx.modelRegistry.classify()`, without codemode. [Virtual models](virtual-models.md#route-requests) can use them to route requests; see the `jev-router.ts` example.
@@ -0,0 +1,96 @@
1
+ # Run Pi with NVIDIA RTX and llama.cpp
2
+
3
+ [NVIDIA Local AI Inference Recipes](https://nvidia.github.io/local-ai-inference-recipes/index.html) provides llama.cpp settings tuned for specific NVIDIA hardware and models. Each recipe gives a single-model `llama-server` command. Pi works with the llama.cpp router, which can serve and manage several models, so this guide converts the recipe command into a router preset.
4
+
5
+ The examples below use Windows PowerShell. See [Local Models with llama.cpp](llama-cpp.md) for Pi's complete router and model-management reference.
6
+
7
+ ## Before you begin
8
+
9
+ 1. Open the [recipe catalog](https://nvidia.github.io/local-ai-inference-recipes/index.html), select your hardware, operating system, and model, then choose **llama.cpp**. Check the prerequisites and memory requirements. Keep the recipe's launch command for the conversion below.
10
+ 2. Install llama.cpp with the recipe's setup instructions. The recipe's settings depend on its build, for example CUDA support or speculative decoding options.
11
+ 3. Run `llama-server --help` and check that it lists `--models-preset`. Without it, the build cannot load the recipe as a router preset. Use a newer build that still meets the recipe's requirements.
12
+ 4. Install Pi with the [Quickstart](quickstart.md). On native Windows, also see [Run Pi on Windows](windows.md).
13
+
14
+ ## Convert the recipe to a preset
15
+
16
+ A preset lists the settings for one or more models. Each section name becomes the model ID shown in Pi. Save the preset next to `llama-server`, or pass its full path when starting the router.
17
+
18
+ Convert each option from the recipe command into one preset line:
19
+
20
+ - Remove the leading dashes: `--ctx-size 65536` becomes `ctx-size = 65536`.
21
+ - Write options without a value as `true`: `--jinja` becomes `jinja = true`.
22
+ - Remove quotes and PowerShell line-continuation backticks around values.
23
+ - Leave out `--host` and `--port`. They belong on the router command line.
24
+ - Add `load-on-startup = true` so the router loads the model when it starts.
25
+ - Replace short model-file options with their long preset keys:
26
+
27
+ | Recipe option | Preset key |
28
+ |---|---|
29
+ | `-hf` | `hf-repo` |
30
+ | `-hff` | `hf-file` |
31
+ | `-hfrd` | `hf-repo-draft` |
32
+ | `--spec-draft-model` | `spec-draft-model` |
33
+
34
+ For example, this recipe command:
35
+
36
+ ```powershell
37
+ llama-server.exe -hf "unsloth/Qwen3.8-27B-GGUF" `
38
+ -hff "Qwen3.8-27B-UD-Q4_K_M.gguf" `
39
+ -hfrd "z-lab/Qwen3.8-27B-DFlash2-GGUF" `
40
+ --spec-draft-model "Qwen3.8-27B-DFlash2-Q4_K_M.gguf" `
41
+ --no-mmproj `
42
+ --host 127.0.0.1 `
43
+ --port 9931 `
44
+ --batch-size 4096 `
45
+ --ubatch-size 1024 `
46
+ --ctx-size 65536 `
47
+ --parallel 1 `
48
+ -ngl 999 `
49
+ --jinja `
50
+ --chat-template-kwargs '{"preserve_thinking":true}' `
51
+ --spec-type draft-dflash `
52
+ --spec-draft-n-max 7 `
53
+ --spec-draft-backend-sampling
54
+ ```
55
+
56
+ becomes `qwen3.8-27b.ini`:
57
+
58
+ ```ini
59
+ version = 1
60
+
61
+ [qwen3.8-27b]
62
+ load-on-startup = true
63
+ hf-repo = unsloth/Qwen3.8-27B-GGUF
64
+ hf-file = Qwen3.8-27B-UD-Q4_K_M.gguf
65
+ hf-repo-draft = z-lab/Qwen3.8-27B-DFlash2-GGUF
66
+ spec-draft-model = Qwen3.8-27B-DFlash2-Q4_K_M.gguf
67
+ no-mmproj = true
68
+ batch-size = 4096
69
+ ubatch-size = 1024
70
+ ctx-size = 65536
71
+ parallel = 1
72
+ ngl = 999
73
+ jinja = true
74
+ chat-template-kwargs = {"preserve_thinking":true}
75
+ spec-type = draft-dflash
76
+ spec-draft-n-max = 7
77
+ spec-draft-backend-sampling = true
78
+ ```
79
+
80
+ ## Start the router
81
+
82
+ Open PowerShell in the llama.cpp folder and run:
83
+
84
+ ```powershell
85
+ .\llama-server.exe --models-preset qwen3.8-27b.ini --host 127.0.0.1 --port 8080
86
+ ```
87
+
88
+ If the recipe requires a Hugging Face token, set `$env:HF_TOKEN` in the same PowerShell window before starting the router. On its first start, the router downloads the model files.
89
+
90
+ Use the [router health checks](llama-cpp.md#troubleshooting) to confirm that the server is ready. In Windows PowerShell, use `curl.exe` rather than `curl`, which PowerShell maps to a different command. The model is ready when `/models` lists `qwen3.8-27b` with the status `loaded`.
91
+
92
+ ## Use the model in Pi
93
+
94
+ Follow [Configure Pi](llama-cpp.md#configure-pi) and connect to `http://127.0.0.1:8080`. The preset section name is the model ID, so select `qwen3.8-27b` in `/model` for this example.
95
+
96
+ NVIDIA recipe memory settings assume that only the selected model uses the GPU. If `/llama` shows other loaded models, unload them before using the recipe model.
package/docs/sessions.md CHANGED
@@ -55,7 +55,7 @@ Use `--session` when you already know the session path or ID. Use `--fork` to cr
55
55
 
56
56
  ## Export or share a session
57
57
 
58
- Use `/export` to write the current session as HTML or JSONL. Use `/share` to upload it and get a viewer link. Pi uses a Radius artifact when Radius authentication is configured; otherwise, it uses a private GitHub gist.
58
+ Use `/export` to write the current session as HTML or JSONL. Use `/share` to upload it and get a viewer link. By default, Pi uses a Radius artifact when Radius authentication is configured; otherwise, it uses a private GitHub gist. Set [`sessionShareDestination`](settings.md#sessions-and-context) to `gist` or `radius` to force one destination.
59
59
 
60
60
  Review exported or shared sessions first. They can contain prompts, model responses, tool arguments, command output, file contents, and extension messages.
61
61
 
package/docs/settings.md CHANGED
@@ -64,13 +64,14 @@ CLI tool options override this setting for one invocation. `--tools` with only `
64
64
  | Setting | Type | Default | Description |
65
65
  |---|---|---|---|
66
66
  | `sessionDir` | string | Agent session directory | Session storage directory. Relative paths resolve from the working directory. `PI_CODING_AGENT_SESSION_DIR` and `--session-dir` override this setting. |
67
+ | `sessionShareDestination` | `"auto" \| "gist" \| "radius"` | `"auto"` | Destination used by `/share`. `auto` uses Radius when authenticated and otherwise creates a GitHub gist. `gist` always uses a gist. `radius` requires Radius authentication and never falls back to a gist. |
67
68
 
68
69
  ### Compaction
69
70
 
70
71
  | Setting | Type | Default | Description |
71
72
  |---|---|---|---|
72
73
  | `compaction.enabled` | boolean | `true` | Enable automatic compaction. |
73
- | `compaction.reserveTokens` | number | `16384` | Tokens reserved for the model response. |
74
+ | `compaction.reserveTokens` | number | `16384` | Free context tokens below which automatic compaction starts. |
74
75
  | `compaction.keepRecentTokens` | number | `20000` | Recent tokens retained without summarization. |
75
76
  | `compaction.modelOverrides` | object | None | Per-model token settings keyed by exact `provider/modelId`. |
76
77