@averyyy/pi-coding-agent 0.80.10-piclient.2 → 0.82.0-piclient.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 (185) hide show
  1. package/CHANGELOG.md +101 -0
  2. package/README.md +19 -3
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +2 -0
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/cli/config-selector.d.ts.map +1 -1
  7. package/dist/cli/config-selector.js +1 -1
  8. package/dist/cli/config-selector.js.map +1 -1
  9. package/dist/cli/startup-ui.d.ts.map +1 -1
  10. package/dist/cli/startup-ui.js +1 -1
  11. package/dist/cli/startup-ui.js.map +1 -1
  12. package/dist/core/agent-session-services.d.ts.map +1 -1
  13. package/dist/core/agent-session-services.js +13 -0
  14. package/dist/core/agent-session-services.js.map +1 -1
  15. package/dist/core/agent-session.d.ts +33 -3
  16. package/dist/core/agent-session.d.ts.map +1 -1
  17. package/dist/core/agent-session.js +91 -59
  18. package/dist/core/agent-session.js.map +1 -1
  19. package/dist/core/compaction/branch-summarization.d.ts +7 -1
  20. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  21. package/dist/core/compaction/branch-summarization.js +6 -2
  22. package/dist/core/compaction/branch-summarization.js.map +1 -1
  23. package/dist/core/compaction/compaction.d.ts +23 -4
  24. package/dist/core/compaction/compaction.d.ts.map +1 -1
  25. package/dist/core/compaction/compaction.js +83 -29
  26. package/dist/core/compaction/compaction.js.map +1 -1
  27. package/dist/core/compaction/utils.d.ts +1 -1
  28. package/dist/core/compaction/utils.d.ts.map +1 -1
  29. package/dist/core/compaction/utils.js +6 -17
  30. package/dist/core/compaction/utils.js.map +1 -1
  31. package/dist/core/extensions/loader.d.ts.map +1 -1
  32. package/dist/core/extensions/loader.js +13 -2
  33. package/dist/core/extensions/loader.js.map +1 -1
  34. package/dist/core/extensions/runner.d.ts +2 -1
  35. package/dist/core/extensions/runner.d.ts.map +1 -1
  36. package/dist/core/extensions/runner.js +35 -0
  37. package/dist/core/extensions/runner.js.map +1 -1
  38. package/dist/core/extensions/types.d.ts +20 -2
  39. package/dist/core/extensions/types.d.ts.map +1 -1
  40. package/dist/core/extensions/types.js.map +1 -1
  41. package/dist/core/model-config.d.ts +30 -0
  42. package/dist/core/model-config.d.ts.map +1 -1
  43. package/dist/core/model-config.js +6 -0
  44. package/dist/core/model-config.js.map +1 -1
  45. package/dist/core/model-registry.d.ts +5 -1
  46. package/dist/core/model-registry.d.ts.map +1 -1
  47. package/dist/core/model-registry.js +19 -4
  48. package/dist/core/model-registry.js.map +1 -1
  49. package/dist/core/model-resolver.d.ts.map +1 -1
  50. package/dist/core/model-resolver.js +9 -0
  51. package/dist/core/model-resolver.js.map +1 -1
  52. package/dist/core/model-runtime.d.ts +7 -3
  53. package/dist/core/model-runtime.d.ts.map +1 -1
  54. package/dist/core/model-runtime.js +42 -21
  55. package/dist/core/model-runtime.js.map +1 -1
  56. package/dist/core/prompt-templates.d.ts +1 -0
  57. package/dist/core/prompt-templates.d.ts.map +1 -1
  58. package/dist/core/prompt-templates.js +4 -4
  59. package/dist/core/prompt-templates.js.map +1 -1
  60. package/dist/core/remote-catalog-provider.d.ts +1 -1
  61. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  62. package/dist/core/remote-catalog-provider.js +21 -7
  63. package/dist/core/remote-catalog-provider.js.map +1 -1
  64. package/dist/core/resource-loader.d.ts.map +1 -1
  65. package/dist/core/resource-loader.js +1 -0
  66. package/dist/core/resource-loader.js.map +1 -1
  67. package/dist/core/sdk.d.ts.map +1 -1
  68. package/dist/core/sdk.js +6 -2
  69. package/dist/core/sdk.js.map +1 -1
  70. package/dist/core/session-manager.d.ts +9 -4
  71. package/dist/core/session-manager.d.ts.map +1 -1
  72. package/dist/core/session-manager.js +99 -23
  73. package/dist/core/session-manager.js.map +1 -1
  74. package/dist/core/settings-manager.d.ts +1 -1
  75. package/dist/core/settings-manager.d.ts.map +1 -1
  76. package/dist/core/settings-manager.js.map +1 -1
  77. package/dist/core/system-prompt.d.ts.map +1 -1
  78. package/dist/core/system-prompt.js +1 -1
  79. package/dist/core/system-prompt.js.map +1 -1
  80. package/dist/core/tools/bash.d.ts +2 -0
  81. package/dist/core/tools/bash.d.ts.map +1 -1
  82. package/dist/core/tools/bash.js +34 -5
  83. package/dist/core/tools/bash.js.map +1 -1
  84. package/dist/core/tools/read.d.ts.map +1 -1
  85. package/dist/core/tools/read.js +1 -1
  86. package/dist/core/tools/read.js.map +1 -1
  87. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
  88. package/dist/core/tools/tool-definition-wrapper.js +3 -1
  89. package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
  90. package/dist/core/usage-totals.d.ts +19 -0
  91. package/dist/core/usage-totals.d.ts.map +1 -0
  92. package/dist/core/usage-totals.js +52 -0
  93. package/dist/core/usage-totals.js.map +1 -0
  94. package/dist/extensions/index.d.ts +3 -0
  95. package/dist/extensions/index.d.ts.map +1 -0
  96. package/dist/extensions/index.js +3 -0
  97. package/dist/extensions/index.js.map +1 -0
  98. package/dist/extensions/llama/client.d.ts +61 -0
  99. package/dist/extensions/llama/client.d.ts.map +1 -0
  100. package/dist/extensions/llama/client.js +302 -0
  101. package/dist/extensions/llama/client.js.map +1 -0
  102. package/dist/extensions/llama/huggingface.d.ts +23 -0
  103. package/dist/extensions/llama/huggingface.d.ts.map +1 -0
  104. package/dist/extensions/llama/huggingface.js +141 -0
  105. package/dist/extensions/llama/huggingface.js.map +1 -0
  106. package/dist/extensions/llama/index.d.ts +3 -0
  107. package/dist/extensions/llama/index.d.ts.map +1 -0
  108. package/dist/extensions/llama/index.js +208 -0
  109. package/dist/extensions/llama/index.js.map +1 -0
  110. package/dist/extensions/llama/provider.d.ts +10 -0
  111. package/dist/extensions/llama/provider.d.ts.map +1 -0
  112. package/dist/extensions/llama/provider.js +101 -0
  113. package/dist/extensions/llama/provider.js.map +1 -0
  114. package/dist/extensions/llama/ui.d.ts +42 -0
  115. package/dist/extensions/llama/ui.d.ts.map +1 -0
  116. package/dist/extensions/llama/ui.js +416 -0
  117. package/dist/extensions/llama/ui.js.map +1 -0
  118. package/dist/index.d.ts +2 -2
  119. package/dist/index.d.ts.map +1 -1
  120. package/dist/index.js +1 -1
  121. package/dist/index.js.map +1 -1
  122. package/dist/main.d.ts.map +1 -1
  123. package/dist/main.js +10 -4
  124. package/dist/main.js.map +1 -1
  125. package/dist/modes/interactive/components/extension-editor.d.ts +1 -2
  126. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  127. package/dist/modes/interactive/components/extension-editor.js +16 -46
  128. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  129. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  130. package/dist/modes/interactive/components/footer.js +24 -23
  131. package/dist/modes/interactive/components/footer.js.map +1 -1
  132. package/dist/modes/interactive/external-editor.d.ts +12 -0
  133. package/dist/modes/interactive/external-editor.d.ts.map +1 -0
  134. package/dist/modes/interactive/external-editor.js +37 -0
  135. package/dist/modes/interactive/external-editor.js.map +1 -0
  136. package/dist/modes/interactive/interactive-mode.d.ts +2 -1
  137. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  138. package/dist/modes/interactive/interactive-mode.js +72 -68
  139. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  140. package/dist/modes/rpc/rpc-client.d.ts +4 -0
  141. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  142. package/dist/modes/rpc/rpc-client.js +7 -0
  143. package/dist/modes/rpc/rpc-client.js.map +1 -1
  144. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  145. package/dist/modes/rpc/rpc-mode.js +5 -0
  146. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  147. package/dist/modes/rpc/rpc-types.d.ts +11 -0
  148. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  149. package/dist/modes/rpc/rpc-types.js.map +1 -1
  150. package/dist/utils/clipboard.d.ts.map +1 -1
  151. package/dist/utils/clipboard.js +19 -8
  152. package/dist/utils/clipboard.js.map +1 -1
  153. package/dist/utils/version-check.d.ts.map +1 -1
  154. package/dist/utils/version-check.js +3 -1
  155. package/dist/utils/version-check.js.map +1 -1
  156. package/docs/compaction.md +9 -4
  157. package/docs/custom-provider.md +33 -2
  158. package/docs/environment-variables.md +88 -0
  159. package/docs/extensions.md +61 -11
  160. package/docs/index.md +2 -0
  161. package/docs/json.md +5 -1
  162. package/docs/llama-cpp.md +99 -0
  163. package/docs/models.md +6 -2
  164. package/docs/prompt-templates.md +1 -0
  165. package/docs/providers.md +19 -1
  166. package/docs/rpc.md +104 -8
  167. package/docs/sdk.md +3 -0
  168. package/docs/session-format.md +17 -3
  169. package/docs/settings.md +1 -1
  170. package/docs/tui.md +41 -26
  171. package/docs/usage.md +2 -14
  172. package/examples/extensions/README.md +1 -1
  173. package/examples/extensions/custom-compaction.ts +4 -0
  174. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  175. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  176. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  177. package/examples/extensions/gondolin/package-lock.json +2 -2
  178. package/examples/extensions/gondolin/package.json +1 -1
  179. package/examples/extensions/handoff.ts +9 -1
  180. package/examples/extensions/sandbox/package-lock.json +2 -2
  181. package/examples/extensions/sandbox/package.json +1 -1
  182. package/examples/extensions/summarize.ts +3 -0
  183. package/examples/extensions/with-deps/package-lock.json +2 -2
  184. package/examples/extensions/with-deps/package.json +1 -1
  185. package/package.json +5 -4
@@ -1 +1 @@
1
- {"version":3,"file":"version-check.js","sourceRoot":"","sources":["../../src/utils/version-check.ts"],"names":[],"mappings":"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","sourcesContent":["import { compare, valid } from \"semver\";\nimport { getPiUserAgent } from \"./pi-user-agent.ts\";\n\nconst LATEST_VERSION_URL = \"https://pi.dev/api/latest-version\";\nconst DEFAULT_VERSION_CHECK_TIMEOUT_MS = 10000;\n\nexport interface LatestPiRelease {\n\tversion: string;\n\tpackageName?: string;\n\tnote?: string;\n}\n\nexport function comparePackageVersions(leftVersion: string, rightVersion: string): number | undefined {\n\tconst left = valid(leftVersion.trim());\n\tconst right = valid(rightVersion.trim());\n\tif (!left || !right) {\n\t\treturn undefined;\n\t}\n\treturn compare(left, right);\n}\n\nexport function isNewerPackageVersion(candidateVersion: string, currentVersion: string): boolean {\n\tconst comparison = comparePackageVersions(candidateVersion, currentVersion);\n\tif (comparison !== undefined) {\n\t\treturn comparison > 0;\n\t}\n\treturn candidateVersion.trim() !== currentVersion.trim();\n}\n\nexport async function getLatestPiRelease(\n\tcurrentVersion: string,\n\toptions: { timeoutMs?: number } = {},\n): Promise<LatestPiRelease | undefined> {\n\tif (process.env.PI_SKIP_VERSION_CHECK || process.env.PI_OFFLINE) return undefined;\n\n\tconst response = await fetch(LATEST_VERSION_URL, {\n\t\theaders: {\n\t\t\t\"User-Agent\": getPiUserAgent(currentVersion),\n\t\t\taccept: \"application/json\",\n\t\t},\n\t\tsignal: AbortSignal.timeout(options.timeoutMs ?? DEFAULT_VERSION_CHECK_TIMEOUT_MS),\n\t});\n\tif (!response.ok) return undefined;\n\n\tconst data = (await response.json()) as {\n\t\tpackageName?: unknown;\n\t\tversion?: unknown;\n\t\tnote?: unknown;\n\t};\n\tif (typeof data.version !== \"string\" || !data.version.trim()) {\n\t\treturn undefined;\n\t}\n\tconst packageName =\n\t\ttypeof data.packageName === \"string\" && data.packageName.trim() ? data.packageName.trim() : undefined;\n\tconst note = typeof data.note === \"string\" && data.note.trim() ? data.note.trim() : undefined;\n\treturn {\n\t\tversion: data.version.trim(),\n\t\tpackageName,\n\t\t...(note ? { note } : {}),\n\t};\n}\n\nexport async function getLatestPiVersion(\n\tcurrentVersion: string,\n\toptions: { timeoutMs?: number } = {},\n): Promise<string | undefined> {\n\treturn (await getLatestPiRelease(currentVersion, options))?.version;\n}\n\nexport async function checkForNewPiVersion(currentVersion: string): Promise<LatestPiRelease | undefined> {\n\ttry {\n\t\tconst latestRelease = await getLatestPiRelease(currentVersion);\n\t\tif (latestRelease && isNewerPackageVersion(latestRelease.version, currentVersion)) {\n\t\t\treturn latestRelease;\n\t\t}\n\t\treturn undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n"]}
1
+ {"version":3,"file":"version-check.js","sourceRoot":"","sources":["../../src/utils/version-check.ts"],"names":[],"mappings":"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","sourcesContent":["import { compare, valid } from \"semver\";\nimport { getPiUserAgent } from \"./pi-user-agent.ts\";\n\nconst LATEST_VERSION_URL = \"https://pi.dev/api/latest-version\";\nconst DEFAULT_VERSION_CHECK_TIMEOUT_MS = 10000;\n\nexport interface LatestPiRelease {\n\tversion: string;\n\tpackageName?: string;\n\tnote?: string;\n}\n\nexport function comparePackageVersions(leftVersion: string, rightVersion: string): number | undefined {\n\tconst left = valid(leftVersion.trim());\n\tconst right = valid(rightVersion.trim());\n\tif (!left || !right) {\n\t\treturn undefined;\n\t}\n\treturn compare(left, right);\n}\n\nexport function isNewerPackageVersion(candidateVersion: string, currentVersion: string): boolean {\n\tconst comparison = comparePackageVersions(candidateVersion, currentVersion);\n\tif (comparison !== undefined) {\n\t\treturn comparison > 0;\n\t}\n\treturn candidateVersion.trim() !== currentVersion.trim();\n}\n\nexport async function getLatestPiRelease(\n\tcurrentVersion: string,\n\toptions: { timeoutMs?: number } = {},\n): Promise<LatestPiRelease | undefined> {\n\tif (process.env.PI_OFFLINE) return undefined;\n\n\tconst response = await fetch(LATEST_VERSION_URL, {\n\t\theaders: {\n\t\t\t\"User-Agent\": getPiUserAgent(currentVersion),\n\t\t\taccept: \"application/json\",\n\t\t},\n\t\tsignal: AbortSignal.timeout(options.timeoutMs ?? DEFAULT_VERSION_CHECK_TIMEOUT_MS),\n\t});\n\tif (!response.ok) return undefined;\n\n\tconst data = (await response.json()) as {\n\t\tpackageName?: unknown;\n\t\tversion?: unknown;\n\t\tnote?: unknown;\n\t};\n\tif (typeof data.version !== \"string\" || !data.version.trim()) {\n\t\treturn undefined;\n\t}\n\tconst packageName =\n\t\ttypeof data.packageName === \"string\" && data.packageName.trim() ? data.packageName.trim() : undefined;\n\tconst note = typeof data.note === \"string\" && data.note.trim() ? data.note.trim() : undefined;\n\treturn {\n\t\tversion: data.version.trim(),\n\t\tpackageName,\n\t\t...(note ? { note } : {}),\n\t};\n}\n\nexport async function getLatestPiVersion(\n\tcurrentVersion: string,\n\toptions: { timeoutMs?: number } = {},\n): Promise<string | undefined> {\n\treturn (await getLatestPiRelease(currentVersion, options))?.version;\n}\n\nexport async function checkForNewPiVersion(currentVersion: string): Promise<LatestPiRelease | undefined> {\n\tif (process.env.PI_SKIP_VERSION_CHECK) return undefined;\n\n\ttry {\n\t\tconst latestRelease = await getLatestPiRelease(currentVersion);\n\t\tif (latestRelease && isNewerPackageVersion(latestRelease.version, currentVersion)) {\n\t\t\treturn latestRelease;\n\t\t}\n\t\treturn undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n"]}
@@ -20,7 +20,7 @@ Pi has two summarization mechanisms:
20
20
  | Compaction | Context exceeds threshold, or `/compact` | Summarize old messages to free up context |
21
21
  | Branch summarization | `/tree` navigation | Preserve context when switching branches |
22
22
 
23
- Both use the same structured summary format and track file operations cumulatively.
23
+ Both use the same structured summary format and track file operations cumulatively. Compaction and branch-summary requests use fresh routing session IDs and, where supported by the provider, disable prompt-cache writes because these one-off prompts are unlikely to be reused.
24
24
 
25
25
  ## Compaction
26
26
 
@@ -129,6 +129,7 @@ interface CompactionEntry<T = unknown> {
129
129
  summary: string;
130
130
  firstKeptEntryId: string;
131
131
  tokensBefore: number;
132
+ usage?: Usage; // LLM usage that generated the summary
132
133
  fromHook?: boolean; // true if provided by extension (legacy field name)
133
134
  details?: T; // implementation-specific data
134
135
  }
@@ -140,9 +141,9 @@ interface CompactionDetails {
140
141
  }
141
142
  ```
142
143
 
143
- Extensions can store any JSON-serializable data in `details`. The default compaction tracks file operations, but custom extension implementations can use their own structure.
144
+ Extensions can store any JSON-serializable data in `details`. The default compaction tracks file operations, but custom extension implementations can use their own structure. Generated and extension-provided summaries store their LLM `usage` when available so session totals include summarization work.
144
145
 
145
- See [`prepareCompaction()`](https://github.com/earendil-works/pi-mono/blob/main/packages/coding-agent/src/core/compaction/compaction.ts) and [`compact()`](https://github.com/earendil-works/pi-mono/blob/main/packages/coding-agent/src/core/compaction/compaction.ts) for the implementation.
146
+ See [`prepareCompaction()`](https://github.com/earendil-works/pi-mono/blob/main/packages/coding-agent/src/core/compaction/compaction.ts) and [`compact()`](https://github.com/earendil-works/pi-mono/blob/main/packages/coding-agent/src/core/compaction/compaction.ts) for the implementation. For direct programmatic summarization, `generateSummary()` returns the summary text and `generateSummaryWithUsage()` returns `{ text, usage }`.
146
147
 
147
148
  ## Branch Summarization
148
149
 
@@ -195,6 +196,7 @@ interface BranchSummaryEntry<T = unknown> {
195
196
  timestamp: number;
196
197
  summary: string;
197
198
  fromId: string; // Entry we navigated from
199
+ usage?: Usage; // LLM usage that generated the summary
198
200
  fromHook?: boolean; // true if provided by extension (legacy field name)
199
201
  details?: T; // implementation-specific data
200
202
  }
@@ -300,6 +302,7 @@ pi.on("session_before_compact", async (event, ctx) => {
300
302
  summary: "Your summary...",
301
303
  firstKeptEntryId: preparation.firstKeptEntryId,
302
304
  tokensBefore: preparation.tokensBefore,
305
+ // usage: summaryResponse.usage, // Optional; included in session totals
303
306
  details: { /* custom data */ },
304
307
  }
305
308
  };
@@ -328,13 +331,14 @@ pi.on("session_before_compact", async (event, ctx) => {
328
331
  // [Tool result]: output text
329
332
 
330
333
  // Now send to your model for summarization
331
- const summary = await myModel.summarize(conversationText);
334
+ const { summary, usage } = await myModel.summarize(conversationText);
332
335
 
333
336
  return {
334
337
  compaction: {
335
338
  summary,
336
339
  firstKeptEntryId: preparation.firstKeptEntryId,
337
340
  tokensBefore: preparation.tokensBefore,
341
+ usage,
338
342
  }
339
343
  };
340
344
  });
@@ -364,6 +368,7 @@ pi.on("session_before_tree", async (event, ctx) => {
364
368
  return {
365
369
  summary: {
366
370
  summary: "Your summary...",
371
+ // usage: summaryResponse.usage, // Optional; included in session totals
367
372
  details: { /* custom data */ },
368
373
  }
369
374
  };
@@ -30,10 +30,38 @@ See these complete provider examples:
30
30
 
31
31
  ## Quick Reference
32
32
 
33
+ Extensions can register either a complete pi-ai `Provider` or use the legacy provider-config form. Prefer a complete provider when custom authentication, filtering, refresh, or streaming behavior is required. Pi composes `models.json` overrides above registered native providers.
34
+
33
35
  ```typescript
36
+ import { createProvider, openAICompletionsApi } from "@earendil-works/pi-ai";
34
37
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
35
38
 
36
39
  export default function (pi: ExtensionAPI) {
40
+ pi.registerProvider(createProvider({
41
+ id: "native-local",
42
+ name: "Native Local",
43
+ baseUrl: "http://localhost:8080/v1",
44
+ auth: {
45
+ apiKey: {
46
+ name: "Local server API key",
47
+ async login(interaction) {
48
+ return {
49
+ type: "api_key",
50
+ key: await interaction.prompt({ type: "secret", message: "API key" })
51
+ };
52
+ },
53
+ async resolve({ credential }) {
54
+ return credential?.key
55
+ ? { auth: { apiKey: credential.key }, source: "stored API key" }
56
+ : undefined;
57
+ }
58
+ }
59
+ },
60
+ models: [],
61
+ api: openAICompletionsApi()
62
+ }));
63
+
64
+ // Legacy provider-config form:
37
65
  // Override baseUrl for existing provider
38
66
  pi.registerProvider("anthropic", {
39
67
  baseUrl: "https://proxy.example.com"
@@ -231,7 +259,7 @@ models: [{
231
259
  ```
232
260
 
233
261
  Use `openrouter` for OpenRouter-style `reasoning: { effort }` controls. Use `together` for Together-style `reasoning: { enabled }` controls; with `supportsReasoningEffort`, it also sends `reasoning_effort`. Use `qwen-chat-template` for local Qwen-compatible servers that read `chat_template_kwargs.enable_thinking` and need `preserve_thinking`.
234
- Use `cacheControlFormat: "anthropic"` for OpenAI-compatible providers that expose Anthropic-style prompt caching via `cache_control` on the system prompt, last tool definition, and last user/assistant text content.
262
+ Use `cacheControlFormat: "anthropic"` for OpenAI-compatible providers that expose Anthropic-style prompt caching via `cache_control` on the system prompt, last tool definition, and last user, assistant, or tool-result text content.
235
263
 
236
264
  For Anthropic-compatible providers using `api: "anthropic-messages"`, set `compat.forceAdaptiveThinking: true` on models or providers whose upstream model requires adaptive thinking (`thinking.type: "adaptive"` plus `output_config.effort`). Built-in adaptive Claude models set this automatically. Set `compat.allowEmptySignature: true` only for providers that emit empty thinking signatures and expect `signature: ""` on replay.
237
265
 
@@ -709,6 +737,8 @@ interface ProviderModelConfig {
709
737
  supportsDeveloperRole?: boolean;
710
738
  supportsReasoningEffort?: boolean;
711
739
  supportsUsageInStreaming?: boolean;
740
+ supportsStrictMode?: boolean;
741
+ supportsOpenAIGrammarTools?: boolean; // openai-completions/openai-responses; false falls back to normal function tools
712
742
  maxTokensField?: "max_completion_tokens" | "max_tokens";
713
743
  requiresToolResultName?: boolean;
714
744
  requiresAssistantAfterToolResult?: boolean;
@@ -727,9 +757,10 @@ interface ProviderModelConfig {
727
757
  supportsCacheControlOnTools?: boolean;
728
758
  forceAdaptiveThinking?: boolean;
729
759
  allowEmptySignature?: boolean;
760
+ supportsStrictTools?: boolean;
730
761
  };
731
762
  }
732
763
  ```
733
764
 
734
765
  `openrouter` sends `reasoning: { effort }`. `deepseek` sends `thinking: { type: "enabled" | "disabled" }` and `reasoning_effort` when enabled. `together` sends `reasoning: { enabled }` and also `reasoning_effort` when `supportsReasoningEffort` is enabled. `qwen` is for DashScope-style top-level `enable_thinking`. Use `qwen-chat-template` for local Qwen-compatible servers that read `chat_template_kwargs.enable_thinking` and need `preserve_thinking`. Use `chat-template` for configurable `chat_template_kwargs`, for example DeepSeek V3.x behind vLLM with `chatTemplateKwargs: { "thinking": { "$var": "thinking.enabled" } }`.
735
- `cacheControlFormat: "anthropic"` applies Anthropic-style `cache_control` markers to the system prompt, last tool definition, and last user/assistant text content.
766
+ `cacheControlFormat: "anthropic"` applies Anthropic-style `cache_control` markers to the system prompt, last tool definition, and last user, assistant, or tool-result text content.
@@ -0,0 +1,88 @@
1
+ # Environment Variables
2
+
3
+ Pi uses environment variables in three ways:
4
+
5
+ - Variables such as `PI_OFFLINE` configure the Pi process.
6
+ - Pi sets `PI_CODING_AGENT` so child processes can detect that they run inside Pi.
7
+ - Commands run by the LLM-callable bash tool receive `PI_*` variables describing the current session.
8
+
9
+ Provider API-key variables are documented separately in [Providers](providers.md#environment-variables-or-auth-file).
10
+
11
+ ## Process Marker
12
+
13
+ The CLI and RPC entry points set `PI_CODING_AGENT=true`. Child processes inherit it and can use it to detect that they run inside Pi. It is not session-specific and is not set automatically when Pi is embedded through the SDK.
14
+
15
+ ## Bash Tool Session Environment
16
+
17
+ Commands run by the bash tool receive the current Pi session state:
18
+
19
+ | Variable | Description |
20
+ |----------|-------------|
21
+ | `PI_SESSION_ID` | Current session ID |
22
+ | `PI_SESSION_FILE` | Absolute path to the current session JSONL file; unset for ephemeral sessions |
23
+ | `PI_PROVIDER` | Currently selected model provider |
24
+ | `PI_MODEL` | Currently selected model ID |
25
+ | `PI_REASONING_LEVEL` | Current effective reasoning level: `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, or `max` |
26
+
27
+ The values are resolved when each command starts. Switching models or changing the reasoning level therefore affects the next bash command without restarting Pi. `PI_PROVIDER` and `PI_MODEL` identify the selected Pi model, not a different upstream model that a router may choose internally.
28
+
29
+ When asked which model or provider is running, inspect these variables instead of inferring the answer from the system prompt:
30
+
31
+ ```bash
32
+ printf '%s/%s\n' "$PI_PROVIDER" "$PI_MODEL"
33
+ printf 'reasoning=%s session=%s\n' "$PI_REASONING_LEVEL" "$PI_SESSION_ID"
34
+ ```
35
+
36
+ The session file can be inspected directly when the session is persistent:
37
+
38
+ ```bash
39
+ if [ -n "$PI_SESSION_FILE" ]; then
40
+ tail -n 1 "$PI_SESSION_FILE"
41
+ fi
42
+ ```
43
+
44
+ These variables are injected into the LLM-callable bash tool. They are not injected into user-entered `!` or `!!` commands.
45
+
46
+ ### Custom Bash Tools
47
+
48
+ Bash tools created with `createBashTool()` expose the session environment by default when registered with Pi. Injection happens before `spawnHook`, so a hook receives the variables in `ctx.env`:
49
+
50
+ ```typescript
51
+ const bashTool = createBashTool(cwd, {
52
+ spawnHook: (ctx) => ({
53
+ ...ctx,
54
+ env: { ...ctx.env, CI: "1" },
55
+ }),
56
+ });
57
+ ```
58
+
59
+ Disable session metadata independently of the spawn hook:
60
+
61
+ ```typescript
62
+ const bashTool = createBashTool(cwd, {
63
+ exposeSessionEnvironment: false,
64
+ spawnHook: (ctx) => ctx,
65
+ });
66
+ ```
67
+
68
+ When disabled, Pi removes inherited values for these variables so nested Pi processes do not expose stale parent-session metadata.
69
+
70
+ ## Pi Process Configuration
71
+
72
+ These variables are read by Pi itself:
73
+
74
+ | Variable | Description |
75
+ |----------|-------------|
76
+ | `PI_CODING_AGENT_DIR` | Override the config directory; default is `~/.pi/agent` |
77
+ | `PI_CODING_AGENT_SESSION_DIR` | Override session storage; overridden by `--session-dir` |
78
+ | `PI_PACKAGE_DIR` | Override the package directory, useful for Nix/Guix store paths |
79
+ | `PI_OFFLINE` | Disable startup network operations, including update checks, package updates, and install/update telemetry |
80
+ | `PI_SKIP_VERSION_CHECK` | Disable the `pi.dev` latest-version request |
81
+ | `PI_TELEMETRY` | Override install/update telemetry and provider attribution headers: `1`/`true`/`yes` or `0`/`false`/`no` |
82
+ | `PI_CACHE_RETENTION` | Set to `long` for extended provider prompt caching where supported |
83
+ | `PI_SHARE_VIEWER_URL` | Override the base URL used by `/share` |
84
+ | `PI_HARDWARE_CURSOR` | Set to `1` to show the hardware cursor; see [Terminal setup](terminal-setup.md) |
85
+ | `VISUAL`, `EDITOR` | External editor fallback when `externalEditor` is unset |
86
+ | `HTTP_PROXY`, `HTTPS_PROXY` | Proxy outbound HTTP requests |
87
+
88
+ Provider credentials such as `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, and cloud-provider configuration are listed in [Providers](providers.md#environment-variables-or-auth-file).
@@ -468,6 +468,7 @@ pi.on("session_before_compact", async (event, ctx) => {
468
468
  summary: "...",
469
469
  firstKeptEntryId: preparation.firstKeptEntryId,
470
470
  tokensBefore: preparation.tokensBefore,
471
+ // usage: summaryResponse.usage, // Optional; included in session totals
471
472
  }
472
473
  };
473
474
  });
@@ -489,7 +490,13 @@ pi.on("session_before_tree", async (event, ctx) => {
489
490
  const { preparation, signal } = event;
490
491
  return { cancel: true };
491
492
  // OR provide custom summary:
492
- return { summary: { summary: "...", details: {} } };
493
+ return {
494
+ summary: {
495
+ summary: "...",
496
+ // usage: summaryResponse.usage, // Optional; included in session totals
497
+ details: {},
498
+ },
499
+ };
493
500
  });
494
501
 
495
502
  pi.on("session_tree", async (event, ctx) => {
@@ -813,7 +820,7 @@ In parallel tool mode, `tool_result` and `tool_execution_end` may interleave in
813
820
  `tool_result` handlers chain like middleware:
814
821
  - Handlers run in extension load order
815
822
  - Each handler sees the latest result after previous handler changes
816
- - Handlers can return partial patches (`content`, `details`, or `isError`); omitted fields keep their current values
823
+ - Handlers can return partial patches (`content`, `details`, `isError`, or `usage`); omitted fields keep their current values
817
824
 
818
825
  Use `ctx.signal` for nested async work inside the handler. This lets Esc cancel model calls, `fetch()`, and other abort-aware operations started by the extension.
819
826
 
@@ -822,7 +829,7 @@ import { isBashToolResult } from "@earendil-works/pi-coding-agent";
822
829
 
823
830
  pi.on("tool_result", async (event, ctx) => {
824
831
  // event.toolName, event.toolCallId, event.input
825
- // event.content, event.details, event.isError
832
+ // event.content, event.details, event.isError, event.usage
826
833
 
827
834
  if (isBashToolResult(event)) {
828
835
  // event.details is typed as BashToolDetails
@@ -835,7 +842,7 @@ pi.on("tool_result", async (event, ctx) => {
835
842
  });
836
843
 
837
844
  // Modify result:
838
- return { content: [...], details: {...}, isError: false };
845
+ return { content: [...], details: {...}, isError: false, usage: nestedModelUsage };
839
846
  });
840
847
  ```
841
848
 
@@ -975,9 +982,9 @@ ctx.sessionManager.buildContextEntries() // Active branch entries with compac
975
982
  ctx.sessionManager.getLeafId() // Current leaf entry ID
976
983
  ```
977
984
 
978
- ### ctx.modelRegistry / ctx.model
985
+ ### ctx.modelRegistry / ctx.model / ctx.thinkingLevel
979
986
 
980
- Access to models and API keys.
987
+ Access to models, providers, and resolved authentication. `ctx.modelRegistry.getProvider(id)` returns the effective pi-ai provider, while `getProviderAuth(id)` resolves its current API key, headers, base URL, and provider-scoped environment without requiring a loaded model. `ctx.model` is the active model, and `ctx.thinkingLevel` is its current effective thinking level.
981
988
 
982
989
  ### ctx.signal
983
990
 
@@ -1679,7 +1686,37 @@ Calls made during the extension factory function are queued and applied once the
1679
1686
 
1680
1687
  Dynamic providers can implement `refreshModels`. Pi calls it during model refresh, publishes the returned list synchronously through the provider, and passes the canonical credential/store/network/signal context. The extension decides whether to persist the catalog through `context.store`; live servers such as llama.cpp can ignore it.
1681
1688
 
1689
+ Extensions that need native provider auth, filtering, refresh, or stream behavior can register a complete `Provider` from `@earendil-works/pi-ai`. The provider becomes the composition base and `models.json` overrides still apply above it.
1690
+
1682
1691
  ```typescript
1692
+ import { createProvider, openAICompletionsApi } from "@earendil-works/pi-ai";
1693
+
1694
+ const provider = createProvider({
1695
+ id: "local-server",
1696
+ name: "Local Server",
1697
+ baseUrl: "http://localhost:8080/v1",
1698
+ auth: {
1699
+ apiKey: {
1700
+ name: "Local server setup",
1701
+ async login(interaction) {
1702
+ return {
1703
+ type: "api_key",
1704
+ key: await interaction.prompt({ type: "secret", message: "API key" }),
1705
+ };
1706
+ },
1707
+ async resolve({ credential }) {
1708
+ return credential?.key
1709
+ ? { auth: { apiKey: credential.key }, source: "stored API key" }
1710
+ : undefined;
1711
+ },
1712
+ },
1713
+ },
1714
+ models: [],
1715
+ api: openAICompletionsApi(),
1716
+ });
1717
+
1718
+ pi.registerProvider(provider);
1719
+
1683
1720
  // Register a new provider with custom models
1684
1721
  pi.registerProvider("my-proxy", {
1685
1722
  name: "My Proxy",
@@ -1748,7 +1785,9 @@ pi.registerProvider("corporate-ai", {
1748
1785
  });
1749
1786
  ```
1750
1787
 
1751
- **Config options:**
1788
+ The object form accepts a complete pi-ai `Provider`, including native `auth`, `getModels`, `refreshModels`, `filterModels`, `stream`, and `streamSimple` behavior.
1789
+
1790
+ **Legacy config options:**
1752
1791
  - `name` - Display name for the provider in UI such as `/login`.
1753
1792
  - `baseUrl` - API endpoint URL. Required when defining models.
1754
1793
  - `apiKey` - API key literal, environment interpolation (`$ENV_VAR` or `${ENV_VAR}`), or leading `!command`. Required when defining models (unless `oauth` provided). `$$` escapes `$`, and `$!` escapes a literal `!` without triggering command execution.
@@ -1900,6 +1939,7 @@ pi.registerTool({
1900
1939
  return {
1901
1940
  content: [{ type: "text", text: "Done" }], // Sent to LLM
1902
1941
  details: { data: result }, // For rendering & state
1942
+ // usage: nestedModelResponse.usage, // Optional nested LLM usage
1903
1943
  // Optional: stop after this tool batch when every finalized tool result
1904
1944
  // in the batch also returns terminate: true.
1905
1945
  terminate: true,
@@ -1912,6 +1952,8 @@ pi.registerTool({
1912
1952
  });
1913
1953
  ```
1914
1954
 
1955
+ **Usage accounting:** If a tool makes nested LLM calls, return their combined `Usage` as `usage`. Pi persists it on the tool result and includes it in footer, `/session`, and RPC session totals. `tool_result` handlers can inspect or replace this value.
1956
+
1915
1957
  **Signaling errors:** To mark a tool execution as failed (sets `isError: true` on the result and reports it to the LLM), throw an error from `execute`. Returning a value never sets the error flag regardless of what properties you include in the return object.
1916
1958
 
1917
1959
  **Early termination:** Return `terminate: true` from `execute()` to hint that the automatic follow-up LLM call should be skipped after the current tool batch. This only takes effect when every finalized tool result in that batch is terminating. See [examples/extensions/structured-output.ts](../examples/extensions/structured-output.ts) for a minimal example where the agent ends on a final structured-output tool call.
@@ -2054,7 +2096,15 @@ const bashTool = createBashTool(cwd, {
2054
2096
  });
2055
2097
  ```
2056
2098
 
2057
- See [examples/extensions/ssh.ts](../examples/extensions/ssh.ts) for a complete SSH example with `--ssh` flag.
2099
+ `createBashTool()` exposes the current session to commands through `PI_SESSION_ID`, `PI_SESSION_FILE`, `PI_PROVIDER`, `PI_MODEL`, and `PI_REASONING_LEVEL`. Injection happens before `spawnHook`, so hooks receive these values in `env` and preserve them when they spread the existing environment as above. Set `exposeSessionEnvironment: false` to disable them:
2100
+
2101
+ ```typescript
2102
+ const bashTool = createBashTool(cwd, {
2103
+ exposeSessionEnvironment: false,
2104
+ });
2105
+ ```
2106
+
2107
+ See [Bash tool session environment](environment-variables.md#bash-tool-session-environment) for variable semantics. See [examples/extensions/ssh.ts](../examples/extensions/ssh.ts) for a complete SSH example with `--ssh` flag.
2058
2108
 
2059
2109
  ### Output Truncation
2060
2110
 
@@ -2708,8 +2758,8 @@ class VimEditor extends CustomEditor {
2708
2758
 
2709
2759
  export default function (pi: ExtensionAPI) {
2710
2760
  pi.on("session_start", (_event, ctx) => {
2711
- ctx.ui.setEditorComponent((_tui, theme, keybindings) =>
2712
- new VimEditor(theme, keybindings)
2761
+ ctx.ui.setEditorComponent((tui, theme, keybindings) =>
2762
+ new VimEditor(tui, theme, keybindings)
2713
2763
  );
2714
2764
  });
2715
2765
  }
@@ -2718,7 +2768,7 @@ export default function (pi: ExtensionAPI) {
2718
2768
  **Key points:**
2719
2769
  - Extend `CustomEditor` (not base `Editor`) to get app keybindings (escape to abort, ctrl+d, model switching)
2720
2770
  - Call `super.handleInput(data)` for keys you don't handle
2721
- - Factory receives `theme` and `keybindings` from the app
2771
+ - Factory receives `tui`, `theme`, and `keybindings` from the app
2722
2772
  - Use `ctx.ui.getEditorComponent()` before `setEditorComponent()` to wrap the previously configured custom editor
2723
2773
  - Pass `undefined` to restore default: `ctx.ui.setEditorComponent(undefined)`
2724
2774
 
package/docs/index.md CHANGED
@@ -41,6 +41,7 @@ For the full first-run flow, see [Quickstart](quickstart.md).
41
41
  - [Quickstart](quickstart.md) - install, authenticate, and run a first session.
42
42
  - [Using Pi](usage.md) - interactive mode, slash commands, context files, and CLI reference.
43
43
  - [Providers](providers.md) - subscription and API-key setup for built-in providers.
44
+ - [llama.cpp](llama-cpp.md) - run a local router and manage models with `/llama`.
44
45
  - [Security](security.md) - project trust, sandbox boundaries, and vulnerability reporting.
45
46
  - [Containerization](containerization.md) - sandbox pi with Gondolin, Docker, or OpenShell.
46
47
  - [Settings](settings.md) - global and project settings.
@@ -67,6 +68,7 @@ For the full first-run flow, see [Quickstart](quickstart.md).
67
68
 
68
69
  ## Reference
69
70
 
71
+ - [Environment variables](environment-variables.md) - Pi process configuration and session metadata available to bash tools.
70
72
  - [Session format](session-format.md) - JSONL session file format, entry types, and SessionManager API.
71
73
 
72
74
  ## Platform setup
package/docs/json.md CHANGED
@@ -17,7 +17,11 @@ type AgentSessionEvent =
17
17
  | { type: "compaction_start"; reason: "manual" | "threshold" | "overflow" }
18
18
  | { type: "compaction_end"; reason: "manual" | "threshold" | "overflow"; result: CompactionResult | undefined; aborted: boolean; willRetry: boolean; errorMessage?: string }
19
19
  | { type: "auto_retry_start"; attempt: number; maxAttempts: number; delayMs: number; errorMessage: string }
20
- | { type: "auto_retry_end"; success: boolean; attempt: number; finalError?: string };
20
+ | { type: "auto_retry_end"; success: boolean; attempt: number; finalError?: string }
21
+ | { type: "summarization_retry_scheduled"; attempt: number; maxAttempts: number; delayMs: number; errorMessage: string }
22
+ | { type: "summarization_retry_attempt_start"; source: "branchSummary" }
23
+ | { type: "summarization_retry_attempt_start"; source: "compaction"; reason: "manual" | "threshold" | "overflow" }
24
+ | { type: "summarization_retry_finished" };
21
25
  ```
22
26
 
23
27
  `queue_update` emits the full pending steering and follow-up queues whenever they change. `compaction_start` and `compaction_end` cover both manual and automatic compaction.
@@ -0,0 +1,99 @@
1
+ # llama.cpp
2
+
3
+ Pi supports the [llama.cpp](https://github.com/ggml-org/llama.cpp) router server. The router discovers multiple GGUF models and loads or unloads them on demand.
4
+
5
+ Use a current llama.cpp build with router support. Follow the [build instructions](https://github.com/ggml-org/llama.cpp/blob/master/docs/build.md) or install a [prebuilt release](https://github.com/ggml-org/llama.cpp/releases) for your platform.
6
+
7
+ ## Start the router
8
+
9
+ Start `llama-server` without `--model` or `-m`. Passing a model starts single-model mode instead of router mode.
10
+
11
+ ```bash
12
+ llama-server \
13
+ --models-dir ~/models \
14
+ --no-models-autoload \
15
+ --jinja \
16
+ --host 127.0.0.1 \
17
+ --port 8080 \
18
+ -ngl 999 \
19
+ -c 32768
20
+ ```
21
+
22
+ Important options:
23
+
24
+ - `--models-dir ~/models` discovers local GGUF files.
25
+ - `--no-models-autoload` keeps loading explicit through `/llama`.
26
+ - `--jinja` enables compatible chat templates and tool calling.
27
+ - `-ngl 999` offloads as many layers as possible to the GPU.
28
+ - `-c 32768` sets the context window for each loaded model. Omit it to use the model's native context, which may require substantially more memory.
29
+
30
+ A single-file model can sit directly in the model directory. Put multimodal and multi-shard models in separate subdirectories:
31
+
32
+ ```text
33
+ ~/models/
34
+ ├── llama-3.2-1b-Q4_K_M.gguf
35
+ ├── gemma-3-4b-it-Q4_K_M/
36
+ │ ├── gemma-3-4b-it-Q4_K_M.gguf
37
+ │ └── mmproj-F16.gguf
38
+ └── large-model-Q4_K_M/
39
+ ├── large-model-Q4_K_M-00001-of-00003.gguf
40
+ ├── large-model-Q4_K_M-00002-of-00003.gguf
41
+ └── large-model-Q4_K_M-00003-of-00003.gguf
42
+ ```
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).
45
+
46
+ ## Configure Pi
47
+
48
+ Start Pi and configure the provider:
49
+
50
+ ```text
51
+ /login llama.cpp
52
+ ```
53
+
54
+ Enter the router URL and optional API key. The default URL is `http://127.0.0.1:8080`.
55
+
56
+ Environment variables can configure the same values without `/login`:
57
+
58
+ ```bash
59
+ export LLAMA_BASE_URL=http://127.0.0.1:8080
60
+ export LLAMA_API_KEY=optional-secret
61
+ pi
62
+ ```
63
+
64
+ If the server uses an API key, start `llama-server` with the matching `--api-key` value. Keep `--host 127.0.0.1` for local-only access.
65
+
66
+ ## Manage models
67
+
68
+ Run:
69
+
70
+ ```text
71
+ /llama
72
+ ```
73
+
74
+ - Select an unloaded model to load it.
75
+ - Select a loaded model to unload it.
76
+ - Select **Download model…**, search Hugging Face, then choose a repository and quantization. Exact `owner/repository[:quant]` values also work.
77
+ - Press Escape during a load or download to confirm cancellation.
78
+
79
+ Hugging Face search uses `HF_TOKEN` when set, then checks `$HF_TOKEN_PATH`, `$HF_HOME/token`, `$XDG_CACHE_HOME/huggingface/token`, and `~/.cache/huggingface/token`. Search also works without authentication, subject to lower rate limits. Pi warns before downloading gated repositories and links to their access page. The llama.cpp server performs the download, so its process must also have `HF_TOKEN` when the selected repository requires access.
80
+
81
+ If other models are loaded, Pi asks whether to unload them first or keep them loaded. Pi does not silently unload models and never deletes model files. The router may be shared with other clients, so `/llama` always displays the router's current state.
82
+
83
+ Only loaded models appear in `/model`. After loading a model, run `/model` to select it for the current Pi session.
84
+
85
+ If the router disconnects, `/llama` shows **Retry** and **Close**. Retry reconnects and refreshes model state without replaying the interrupted operation.
86
+
87
+ ## Troubleshooting
88
+
89
+ Check that the router is reachable:
90
+
91
+ ```bash
92
+ curl http://127.0.0.1:8080/health
93
+ curl http://127.0.0.1:8080/models
94
+ ```
95
+
96
+ - **No models in `/llama`:** Check `--models-dir`, the directory layout, and restart the router.
97
+ - **Model missing from `/model`:** Load it with `/llama` first.
98
+ - **Load fails or uses too much memory:** Lower `-c` or unload another model.
99
+ - **Server is not in router mode:** Start it without `--model`, `-m`, or `-hf`.
package/docs/models.md CHANGED
@@ -375,6 +375,8 @@ Some Anthropic models require adaptive thinking (`thinking.type: "adaptive"` plu
375
375
 
376
376
  Some Anthropic-compatible providers emit thinking blocks with empty signatures and still expect them on replay. Set `allowEmptySignature` to `true` only for those providers; real Anthropic rejects empty thinking signatures.
377
377
 
378
+ Built-in Anthropic models enable `supportsStrictTools` in their model metadata. Custom Anthropic-compatible models must set it to `true` when their endpoint accepts strict JSON-schema tool definitions.
379
+
378
380
  ```json
379
381
  {
380
382
  "providers": {
@@ -408,6 +410,7 @@ Some Anthropic-compatible providers emit thinking blocks with empty signatures a
408
410
  | `supportsCacheControlOnTools` | Whether the provider accepts Anthropic-style `cache_control` markers on tool definitions. Default: `true`. |
409
411
  | `forceAdaptiveThinking` | Whether to send adaptive thinking (`thinking.type: "adaptive"` plus `output_config.effort`) for this model. Built-in adaptive models set this automatically. Default: `false`. |
410
412
  | `allowEmptySignature` | Whether to replay empty thinking signatures as `signature: ""` instead of converting thinking to text. Default: `false`. |
413
+ | `supportsStrictTools` | Whether the provider accepts strict JSON-schema tool definitions. Default: `false`; built-in Anthropic models enable it in generated metadata. |
411
414
 
412
415
  ## OpenAI Compatibility
413
416
 
@@ -445,10 +448,11 @@ For providers with partial OpenAI compatibility, use the `compat` field.
445
448
  | `requiresReasoningContentOnAssistantMessages` | Include empty `reasoning_content` on all replayed assistant messages when reasoning is enabled |
446
449
  | `thinkingFormat` | Use `reasoning_effort`, `openrouter`, `deepseek`, `together`, `zai`, `qwen`, `chat-template`, or `qwen-chat-template` thinking parameters |
447
450
  | `chatTemplateKwargs` | `chat_template_kwargs` values for `thinkingFormat: "chat-template"`; use `{ "$var": "thinking.enabled" }` or `{ "$var": "thinking.effort" }` for pi-controlled thinking values |
448
- | `cacheControlFormat` | Use Anthropic-style `cache_control` markers on the system prompt, last tool definition, and last user/assistant text content. Currently only `anthropic` is supported. |
451
+ | `cacheControlFormat` | Use Anthropic-style `cache_control` markers on the system prompt, last tool definition, and last user, assistant, or tool-result text content. Currently only `anthropic` is supported. |
449
452
  | `sendSessionAffinityHeaders` | For `openai-completions`, send session-affinity headers from the session id when caching is enabled. Default: `false`. |
450
453
  | `sessionAffinityFormat` | For `openai-completions` and `openai-responses`, the session-affinity header format: `openai` sends `session_id`/`x-client-request-id` (completions also `x-session-affinity`), `openai-nosession` omits the underscore-containing `session_id` header, `openrouter` sends `x-session-id`. Does not affect the `prompt_cache_key` body param. Default: auto-detected. |
451
- | `supportsStrictMode` | Include the `strict` field in tool definitions |
454
+ | `supportsStrictMode` | Whether the provider accepts strict JSON-schema function tool definitions. Defaults depend on the API; built-in OpenAI models carry explicit capability metadata. |
455
+ | `supportsOpenAIGrammarTools` | Whether OpenAI-compatible APIs emit custom Lark/regex grammar tools. When `false`, grammar-constrained tools fall back to normal function tools. Default: `false`; the built-in model catalog enables it for GPT-5+ models on OpenAI, OpenAI Codex, Azure OpenAI, GitHub Copilot, opencode, and Cloudflare AI Gateway. |
452
456
  | `deferredToolsMode` | Use provider-specific deferred tool serialization. Currently only `"kimi"` is supported for Kimi's OpenAI-compatible Chat Completions format. |
453
457
  | `supportsLongCacheRetention` | Whether the provider accepts long cache retention when cache retention is `long`: `prompt_cache_retention: "24h"` for OpenAI prompt caching, or `cache_control.ttl: "1h"` when `cacheControlFormat` is `anthropic`. Default: `true`. |
454
458
  | `openRouterRouting` | OpenRouter provider routing preferences. This object is sent as-is in the `provider` field of the [OpenRouter API request](https://openrouter.ai/docs/guides/routing/provider-selection). |
@@ -69,6 +69,7 @@ Templates support positional arguments, defaults, and simple slicing:
69
69
  - `$1`, `$2`, ... positional args
70
70
  - `$@` or `$ARGUMENTS` for all args joined
71
71
  - `${1:-default}` uses arg 1 when present/non-empty, otherwise `default`
72
+ - `${@:-default}` or `${ARGUMENTS:-default}` uses all arguments when present/non-empty, otherwise `default`
72
73
  - `${@:N}` for args from the Nth position (1-indexed)
73
74
  - `${@:N:L}` for `L` args starting at N
74
75
 
package/docs/providers.md CHANGED
@@ -8,6 +8,7 @@ Pi supports subscription-based providers via OAuth and API key providers via env
8
8
  - [API Keys](#api-keys)
9
9
  - [Auth File](#auth-file)
10
10
  - [Cloud Providers](#cloud-providers)
11
+ - [llama.cpp](#llamacpp)
11
12
  - [Custom Providers](#custom-providers)
12
13
  - [Resolution Order](#resolution-order)
13
14
 
@@ -19,9 +20,10 @@ Use `/login` in interactive mode, then select a provider:
19
20
  - Claude Pro/Max
20
21
  - GitHub Copilot
21
22
  - xAI (Grok/X subscription)
23
+ - OpenRouter (OAuth-minted API key billed from OpenRouter credits)
22
24
  - Radius
23
25
 
24
- Use `/logout` to clear credentials. Tokens are stored in `~/.pi/agent/auth.json` and auto-refresh when expired.
26
+ Use `/logout` to clear credentials. Tokens are stored in `~/.pi/agent/auth.json` and auto-refresh when expired. OpenRouter instead mints a user-controlled API key that does not expire automatically.
25
27
 
26
28
  ### OpenAI Codex
27
29
 
@@ -42,6 +44,12 @@ Anthropic subscription auth is active for Claude Pro/Max accounts. Third-party h
42
44
  - Run `/login xai`, then select **Use a subscription**
43
45
  - `XAI_API_KEY` remains available through **Use an API key**
44
46
 
47
+ ### OpenRouter
48
+
49
+ - Run `/login openrouter`, then select **Sign in with OpenRouter** to open the OpenRouter PKCE authorization flow
50
+ - The authorization creates a user-controlled OpenRouter API key billed from your OpenRouter credits
51
+ - `OPENROUTER_API_KEY` remains available through **Use an API key**
52
+
45
53
  ### Radius
46
54
 
47
55
  Radius is a dynamic `pi-messages` gateway. `/login radius` stores OAuth tokens in `auth.json`; the gateway catalog is refreshed independently and cached in `models-store.json`. Custom Radius gateways can be declared in `models.json` with `"oauth": "radius"` and a gateway `baseUrl`.
@@ -86,6 +94,8 @@ pi
86
94
  | Kimi For Coding | `KIMI_API_KEY` | `kimi-coding` |
87
95
  | MiniMax | `MINIMAX_API_KEY` | `minimax` |
88
96
  | MiniMax (China) | `MINIMAX_CN_API_KEY` | `minimax-cn` |
97
+ | Qwen Token Plan | `QWEN_TOKEN_PLAN_API_KEY` | `qwen-token-plan` |
98
+ | Qwen Token Plan (China) | `QWEN_TOKEN_PLAN_CN_API_KEY` | `qwen-token-plan-cn` |
89
99
  | Xiaomi MiMo | `XIAOMI_API_KEY` | `xiaomi` |
90
100
  | Xiaomi MiMo Token Plan (China) | `XIAOMI_TOKEN_PLAN_CN_API_KEY` | `xiaomi-token-plan-cn` |
91
101
  | Xiaomi MiMo Token Plan (Amsterdam) | `XIAOMI_TOKEN_PLAN_AMS_API_KEY` | `xiaomi-token-plan-ams` |
@@ -108,6 +118,8 @@ Store credentials in `~/.pi/agent/auth.json`:
108
118
  "opencode": { "type": "api_key", "key": "..." },
109
119
  "opencode-go": { "type": "api_key", "key": "..." },
110
120
  "together": { "type": "api_key", "key": "..." },
121
+ "qwen-token-plan": { "type": "api_key", "key": "sk-sp-..." },
122
+ "qwen-token-plan-cn": { "type": "api_key", "key": "sk-sp-..." },
111
123
  "xiaomi": { "type": "api_key", "key": "..." },
112
124
  "xiaomi-token-plan-cn": { "type": "api_key", "key": "..." },
113
125
  "xiaomi-token-plan-ams": { "type": "api_key", "key": "..." },
@@ -274,6 +286,12 @@ export GOOGLE_CLOUD_LOCATION=us-central1
274
286
 
275
287
  Or set `GOOGLE_APPLICATION_CREDENTIALS` to a service account key file.
276
288
 
289
+ ## llama.cpp
290
+
291
+ Pi supports the llama.cpp router server. Configure it with `/login llama.cpp`, manage loaded models with `/llama`, and select a loaded model with `/model`.
292
+
293
+ See [llama.cpp](llama-cpp.md) for server setup, model directory layout, environment variables, and command usage.
294
+
277
295
  ## Custom Providers
278
296
 
279
297
  **Via models.json:** Add Ollama, LM Studio, vLLM, or any provider that speaks a supported API (OpenAI Completions, OpenAI Responses, Anthropic Messages, Google Generative AI). See [models.md](models.md).