@earendil-works/pi-coding-agent 1.0.4 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/dist/bundle/chunks/{anthropic-messages-Y22YDKSW.js → anthropic-messages-KT2ID7DZ.js} +1 -1
  3. package/dist/bundle/chunks/anthropic.js +1 -1
  4. package/dist/bundle/chunks/{azure-openai-responses-YJDJIIDQ.js → azure-openai-responses-6A4PNZTH.js} +1 -1
  5. package/dist/bundle/chunks/bedrock-converse-stream.js +2 -2
  6. package/dist/bundle/chunks/chunk-33CRHCAK.js +2 -0
  7. package/dist/bundle/chunks/{chunk-F4TCZWQO.js → chunk-BK6FEHYX.js} +3 -3
  8. package/dist/bundle/chunks/chunk-CH4ZBH45.js +11 -0
  9. package/dist/bundle/chunks/{chunk-XXKX4I4Z.js → chunk-GXLAGZ3Z.js} +1 -1
  10. package/dist/bundle/chunks/{chunk-KIPSWGXN.js → chunk-HKYL362W.js} +1 -1
  11. package/dist/bundle/chunks/chunk-LPXNMA2W.js +2 -0
  12. package/dist/bundle/chunks/{chunk-TAOL3JFA.js → chunk-LTSARG5Q.js} +1 -1
  13. package/dist/bundle/chunks/{chunk-VLMAON6X.js → chunk-MXTZIDYW.js} +3 -3
  14. package/dist/bundle/chunks/chunk-O7YIWAPD.js +2 -0
  15. package/dist/bundle/chunks/{chunk-H33F2TZD.js → chunk-OIM2DMFI.js} +66 -59
  16. package/dist/bundle/chunks/{chunk-64GOPQSC.js → chunk-OTL6MGLJ.js} +3 -3
  17. package/dist/bundle/chunks/{chunk-LD2P6SFZ.js → chunk-PMRZO46Y.js} +1 -1
  18. package/dist/bundle/chunks/{chunk-FKGBRPJC.js → chunk-SYCGMWFH.js} +1 -1
  19. package/dist/bundle/chunks/{chunk-YKYP4C4A.js → chunk-UH5J7I7X.js} +3 -3
  20. package/dist/bundle/chunks/{chunk-WE4MXUQS.js → chunk-VLRCF7MR.js} +1 -1
  21. package/dist/bundle/chunks/chunk-W5MPVSHA.js +82 -0
  22. package/dist/bundle/chunks/chunk-Y4NYF362.js +2 -0
  23. package/dist/bundle/chunks/{cli-FRW732V6.js → cli-OQYLWLA3.js} +2 -2
  24. package/dist/bundle/chunks/cloudflare-workers-ai-system-one-U3AH4ODQ.js +2 -0
  25. package/dist/bundle/chunks/codemode-worker.js +2 -2
  26. package/dist/bundle/chunks/{easter-egg-3d-GBYHZI2E.js → easter-egg-3d-TQFQS342.js} +1 -1
  27. package/dist/bundle/chunks/{execute-MNHU53Y5.js → execute-7IRB5TUX.js} +11 -5
  28. package/dist/bundle/chunks/github-copilot.js +1 -1
  29. package/dist/bundle/chunks/{google-generative-ai-XMFHGQC5.js → google-generative-ai-X2VFFS4I.js} +1 -1
  30. package/dist/bundle/chunks/{google-vertex-4XRRNP74.js → google-vertex-KMGHFTSP.js} +1 -1
  31. package/dist/bundle/chunks/image-resize-worker.js +1 -1
  32. package/dist/bundle/chunks/{llama-cpp-classify-XT44PI3C.js → llama-cpp-classify-FYN623RH.js} +2 -2
  33. package/dist/bundle/chunks/{mistral-conversations-Q22M3IBG.js → mistral-conversations-PRZLM26U.js} +2 -2
  34. package/dist/bundle/chunks/openai-chatgpt.js +1 -1
  35. package/dist/bundle/chunks/{openai-codex-responses-4MYQSO5E.js → openai-codex-responses-ZSLJZYOE.js} +2 -2
  36. package/dist/bundle/chunks/openai-codex.js +1 -1
  37. package/dist/bundle/chunks/{openai-completions-OSRCWAAA.js → openai-completions-WWQW7S3V.js} +1 -1
  38. package/dist/bundle/chunks/openai-decisions-QLZKZLAK.js +5 -0
  39. package/dist/bundle/chunks/{openai-responses-UHF3DHNG.js → openai-responses-KV7QHJY6.js} +1 -1
  40. package/dist/bundle/chunks/{openrouter-images-ZVXWCSIM.js → openrouter-images-DUPG5DZO.js} +1 -1
  41. package/dist/bundle/chunks/{pi-messages-TM5SOOIW.js → pi-messages-YHKPQIHC.js} +1 -1
  42. package/dist/bundle/chunks/{runtime-FW2HXAJ6.js → runtime-ROGZ2EXI.js} +1 -1
  43. package/dist/bundle/chunks/typesafe-system-one-B4NNEG67.js +2 -0
  44. package/dist/bundle/chunks/{virtual-modules-LQIMBBGM.js → virtual-modules-4YN2GNVW.js} +1 -1
  45. package/dist/bundle/cli-runtime.js +1 -1
  46. package/dist/bundle/index.js +1 -1
  47. package/dist/bundle/rpc-entry.js +1 -1
  48. package/dist/cli/args.d.ts +1 -1
  49. package/dist/cli/args.d.ts.map +1 -1
  50. package/dist/cli/args.js +13 -1
  51. package/dist/cli/args.js.map +1 -1
  52. package/dist/core/agent-session.d.ts +7 -0
  53. package/dist/core/agent-session.d.ts.map +1 -1
  54. package/dist/core/agent-session.js +12 -7
  55. package/dist/core/agent-session.js.map +1 -1
  56. package/dist/core/bash-executor.d.ts.map +1 -1
  57. package/dist/core/bash-executor.js +40 -45
  58. package/dist/core/bash-executor.js.map +1 -1
  59. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  60. package/dist/core/export-html/tool-renderer.js +2 -0
  61. package/dist/core/export-html/tool-renderer.js.map +1 -1
  62. package/dist/core/extensions/types.d.ts +11 -0
  63. package/dist/core/extensions/types.d.ts.map +1 -1
  64. package/dist/core/extensions/types.js.map +1 -1
  65. package/dist/core/model-runtime.d.ts.map +1 -1
  66. package/dist/core/model-runtime.js +2 -1
  67. package/dist/core/model-runtime.js.map +1 -1
  68. package/dist/core/nested-tool-calls.d.ts +1 -0
  69. package/dist/core/nested-tool-calls.d.ts.map +1 -1
  70. package/dist/core/nested-tool-calls.js +1 -0
  71. package/dist/core/nested-tool-calls.js.map +1 -1
  72. package/dist/core/sdk.d.ts +4 -0
  73. package/dist/core/sdk.d.ts.map +1 -1
  74. package/dist/core/sdk.js +17 -5
  75. package/dist/core/sdk.js.map +1 -1
  76. package/dist/core/settings-manager.d.ts +13 -0
  77. package/dist/core/settings-manager.d.ts.map +1 -1
  78. package/dist/core/settings-manager.js +37 -13
  79. package/dist/core/settings-manager.js.map +1 -1
  80. package/dist/core/tools/renderers/bash.d.ts.map +1 -1
  81. package/dist/core/tools/renderers/bash.js +8 -3
  82. package/dist/core/tools/renderers/bash.js.map +1 -1
  83. package/dist/core/tools/renderers/edit.d.ts.map +1 -1
  84. package/dist/core/tools/renderers/edit.js +5 -4
  85. package/dist/core/tools/renderers/edit.js.map +1 -1
  86. package/dist/extensions/codemode/execute.d.ts.map +1 -1
  87. package/dist/extensions/codemode/execute.js +63 -7
  88. package/dist/extensions/codemode/execute.js.map +1 -1
  89. package/dist/extensions/codemode/tool.js +2 -2
  90. package/dist/extensions/codemode/tool.js.map +1 -1
  91. package/dist/extensions/llama/provider.d.ts.map +1 -1
  92. package/dist/extensions/llama/provider.js +34 -11
  93. package/dist/extensions/llama/provider.js.map +1 -1
  94. package/dist/extensions/mcp/cli.js +17 -23
  95. package/dist/extensions/mcp/cli.js.map +1 -1
  96. package/dist/extensions/mcp/index.d.ts.map +1 -1
  97. package/dist/extensions/mcp/index.js +165 -64
  98. package/dist/extensions/mcp/index.js.map +1 -1
  99. package/dist/extensions/mcp/oauth.d.ts +5 -3
  100. package/dist/extensions/mcp/oauth.d.ts.map +1 -1
  101. package/dist/extensions/mcp/oauth.js +36 -10
  102. package/dist/extensions/mcp/oauth.js.map +1 -1
  103. package/dist/extensions/mcp/ui.d.ts +3 -3
  104. package/dist/extensions/mcp/ui.d.ts.map +1 -1
  105. package/dist/extensions/mcp/ui.js +10 -2
  106. package/dist/extensions/mcp/ui.js.map +1 -1
  107. package/dist/modes/interactive/components/bash-execution.d.ts +5 -1
  108. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  109. package/dist/modes/interactive/components/bash-execution.js +17 -14
  110. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  111. package/dist/modes/interactive/components/branch-summary-message.d.ts +2 -1
  112. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  113. package/dist/modes/interactive/components/branch-summary-message.js +5 -2
  114. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  115. package/dist/modes/interactive/components/compaction-summary-message.d.ts +2 -1
  116. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  117. package/dist/modes/interactive/components/compaction-summary-message.js +5 -2
  118. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  119. package/dist/modes/interactive/components/custom-entry.d.ts +3 -1
  120. package/dist/modes/interactive/components/custom-entry.d.ts.map +1 -1
  121. package/dist/modes/interactive/components/custom-entry.js +8 -2
  122. package/dist/modes/interactive/components/custom-entry.js.map +1 -1
  123. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
  124. package/dist/modes/interactive/components/custom-message.js +1 -0
  125. package/dist/modes/interactive/components/custom-message.js.map +1 -1
  126. package/dist/modes/interactive/components/settings-selector.js +1 -1
  127. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  128. package/dist/modes/interactive/components/skill-invocation-message.d.ts +2 -1
  129. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  130. package/dist/modes/interactive/components/skill-invocation-message.js +5 -2
  131. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  132. package/dist/modes/interactive/components/tool-execution.d.ts +5 -0
  133. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  134. package/dist/modes/interactive/components/tool-execution.js +10 -0
  135. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  136. package/dist/modes/interactive/interactive-mode.d.ts +2 -0
  137. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  138. package/dist/modes/interactive/interactive-mode.js +50 -29
  139. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  140. package/dist/modes/interactive/program-status-reporter.d.ts +34 -0
  141. package/dist/modes/interactive/program-status-reporter.d.ts.map +1 -0
  142. package/dist/modes/interactive/program-status-reporter.js +110 -0
  143. package/dist/modes/interactive/program-status-reporter.js.map +1 -0
  144. package/dist/package-manager-cli.d.ts.map +1 -1
  145. package/dist/package-manager-cli.js +24 -0
  146. package/dist/package-manager-cli.js.map +1 -1
  147. package/dist/utils/ansi.d.ts +8 -0
  148. package/dist/utils/ansi.d.ts.map +1 -1
  149. package/dist/utils/ansi.js +30 -11
  150. package/dist/utils/ansi.js.map +1 -1
  151. package/dist/utils/clipboard.d.ts.map +1 -1
  152. package/dist/utils/clipboard.js +12 -11
  153. package/dist/utils/clipboard.js.map +1 -1
  154. package/dist/utils/image-resize-core.d.ts +12 -0
  155. package/dist/utils/image-resize-core.d.ts.map +1 -1
  156. package/dist/utils/image-resize-core.js +5 -0
  157. package/dist/utils/image-resize-core.js.map +1 -1
  158. package/dist/utils/image-resize-worker.js +3 -2
  159. package/dist/utils/image-resize-worker.js.map +1 -1
  160. package/dist/utils/image-resize.d.ts.map +1 -1
  161. package/dist/utils/image-resize.js +9 -10
  162. package/dist/utils/image-resize.js.map +1 -1
  163. package/docs/cli.md +4 -4
  164. package/docs/codemode.md +23 -3
  165. package/docs/environment-variables.md +1 -0
  166. package/docs/json.md +3 -3
  167. package/docs/llama-cpp.md +8 -1
  168. package/docs/message-types.md +22 -4
  169. package/docs/models.md +4 -1
  170. package/docs/providers.md +27 -0
  171. package/docs/settings.md +3 -3
  172. package/docs/terminal-setup.md +14 -0
  173. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  174. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  175. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  176. package/examples/extensions/gondolin/package-lock.json +2 -2
  177. package/examples/extensions/gondolin/package.json +1 -1
  178. package/examples/extensions/sandbox/package-lock.json +2 -2
  179. package/examples/extensions/sandbox/package.json +1 -1
  180. package/examples/extensions/with-deps/package-lock.json +2 -2
  181. package/examples/extensions/with-deps/package.json +1 -1
  182. package/package.json +12 -12
  183. package/dist/bundle/chunks/chunk-2QSWSXFK.js +0 -82
  184. package/dist/bundle/chunks/chunk-A3JYRWB6.js +0 -2
  185. package/dist/bundle/chunks/chunk-C47CLQZK.js +0 -2
  186. package/dist/bundle/chunks/chunk-HQHD6PZI.js +0 -2
  187. package/dist/bundle/chunks/chunk-KJUDFE6Y.js +0 -11
  188. package/dist/bundle/chunks/cloudflare-workers-ai-system-one-NM3JRRWQ.js +0 -2
  189. package/dist/bundle/chunks/typesafe-system-one-PRR5K6CI.js +0 -2
@@ -100,12 +100,11 @@ interface SystemMessage {
100
100
  sections?: Record<string, string | null>;
101
101
  toolsAdded?: Tool[];
102
102
  toolsRemoved?: ToolReference[];
103
- replace?: boolean;
104
103
  timestamp: number;
105
104
  }
106
105
  ```
107
106
 
108
- The leading system message declares the initial prompt and tools. Later system messages can append instructions, replace or remove named prompt sections, and add or remove tools. Replaying them in order yields the current state. A message with `replace: true` discards the earlier state and establishes a complete new baseline.
107
+ The leading system message declares the initial prompt and tools. Later system messages can append instructions, replace or remove named prompt sections, and add or remove tools. Replaying them in order yields the current state.
109
108
 
110
109
  ### UserMessage
111
110
 
@@ -129,6 +128,7 @@ interface AssistantMessage {
129
128
  responseModel?: string;
130
129
  responseId?: string;
131
130
  providerThinkingLevel?: string;
131
+ thinkingLevel?: ModelThinkingLevel;
132
132
  diagnostics?: AssistantMessageDiagnostic[];
133
133
  usage: Usage;
134
134
  stopReason: "pending" | "stop" | "length" | "toolUse" | "error" | "aborted" | "deferred";
@@ -140,7 +140,7 @@ interface AssistantMessage {
140
140
  }
141
141
  ```
142
142
 
143
- `responseModel` records a concrete provider response model when it differs from the requested model. `responseId`, `providerThinkingLevel`, `diagnostics`, and `rawStopReason` preserve provider or runtime details.
143
+ `responseModel` records a concrete provider response model when it differs from the requested model. `responseId`, `providerThinkingLevel`, `thinkingLevel`, `diagnostics`, and `rawStopReason` preserve provider or runtime details.
144
144
 
145
145
  `"pending"` is used for a partial assistant message while it streams. The completed message in `message_end` has a terminal stop reason, and Pi does not persist `"pending"` assistant messages in session JSONL.
146
146
 
@@ -168,12 +168,30 @@ interface ToolResultMessage<TDetails = any> {
168
168
  content: (TextContent | ImageContent)[];
169
169
  details?: TDetails;
170
170
  usage?: Usage;
171
+ nestedCalls?: NestedToolCalls;
171
172
  isError: boolean;
172
173
  timestamp: number;
173
174
  }
174
175
  ```
175
176
 
176
- `details` is tool-specific. Optional `usage` reports nested model work performed by the tool and contributes to full-session statistics, but it is not part of the main model-call usage.
177
+ `details` is tool-specific. Optional `usage` reports nested model work performed by the tool and contributes to full-session statistics, but it is not part of the main model-call usage. `nestedCalls` records bounded metadata about calls this tool made to other tools:
178
+
179
+ ```typescript
180
+ interface NestedToolCalls {
181
+ calls: NestedToolCallRecord[];
182
+ complete: boolean;
183
+ }
184
+
185
+ interface NestedToolCallRecord {
186
+ id: string;
187
+ name: string;
188
+ arguments?: JsonObject;
189
+ argumentsBytes?: number;
190
+ status: "ok" | "error" | "unfinished";
191
+ durationMs?: number;
192
+ error?: string;
193
+ }
194
+ ```
177
195
 
178
196
  ## Coding-agent messages
179
197
 
package/docs/models.md CHANGED
@@ -128,7 +128,7 @@ Compatibility settings should describe verified differences in the endpoint's re
128
128
 
129
129
  ## Use classifier models
130
130
 
131
- 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, and Cloudflare's Clef and Clef Flash models from Workers AI:
131
+ 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):
132
132
 
133
133
  | Provider | Model IDs | Authentication |
134
134
  |---|---|---|
@@ -137,9 +137,12 @@ Classifier models do not chat. They answer typed questions about JSON state: pic
137
137
  | `cloudflare-workers-ai` | `typesafe/jev`, `@cf/cloudflare/clef`, `@cf/cloudflare/clef-flash` | `CLOUDFLARE_API_KEY` and `CLOUDFLARE_ACCOUNT_ID` |
138
138
  | `vercel-ai-gateway` | `typesafe-ai/jev` | `AI_GATEWAY_API_KEY` |
139
139
  | `opencode` | `jev-1.13`, `jev-1.13-free` | `OPENCODE_API_KEY` |
140
+ | `openai` | `gpt-6-luna` | `OPENAI_API_KEY` |
140
141
 
141
142
  Chat models on a [llama.cpp router](llama-cpp.md#classification) are also listed as classifier models.
142
143
 
144
+ 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.
145
+
143
146
  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 })`:
144
147
 
145
148
  ```js
package/docs/providers.md CHANGED
@@ -115,6 +115,10 @@ Radius authentication uses its gateway catalog and caches refreshed model metada
115
115
 
116
116
  ### Azure OpenAI
117
117
 
118
+ The provider ID is `azure` (formerly `azure-openai-responses`). Use it as the key in `auth.json`, `models.json`, and `settings.json`, and in model references such as `--model azure/gpt-5.4`.
119
+
120
+ The `azure` provider serves OpenAI models through the Responses API and Microsoft Foundry models through Chat Completions, such as `azure/deepseek-v4-pro`.
121
+
118
122
  Set an API key plus either a base URL or resource name:
119
123
 
120
124
  ```bash
@@ -126,6 +130,29 @@ export AZURE_OPENAI_RESOURCE_NAME=your-resource
126
130
 
127
131
  Resource root URLs under `ai.azure.com`, `cognitiveservices.azure.com`, and `openai.azure.com` are normalized to the OpenAI API path.
128
132
 
133
+ Pi sends the model ID as the deployment name. If a deployment has a different name, map it with `AZURE_OPENAI_DEPLOYMENT_NAME_MAP`:
134
+
135
+ ```bash
136
+ export AZURE_OPENAI_DEPLOYMENT_NAME_MAP=gpt-5.4=my-gpt-deployment,deepseek-v4-pro=my-deepseek
137
+ ```
138
+
139
+ `AZURE_OPENAI_API_VERSION` overrides the API version for OpenAI models (default `v1`).
140
+
141
+ To use a Foundry model that Pi does not include, add it under `azure` in [`models.json`](models.md#configure-a-compatible-endpoint) with `api: "openai-completions"`. Custom models require a `baseUrl`; `AZURE_OPENAI_BASE_URL` and `AZURE_OPENAI_RESOURCE_NAME` take priority over it when set:
142
+
143
+ ```json
144
+ {
145
+ "providers": {
146
+ "azure": {
147
+ "baseUrl": "https://your-resource.services.ai.azure.com",
148
+ "models": [
149
+ { "id": "your-deployment", "api": "openai-completions" }
150
+ ]
151
+ }
152
+ }
153
+ }
154
+ ```
155
+
129
156
  ### Amazon Bedrock
130
157
 
131
158
  Bedrock can use a bearer token or an ambient AWS credential source:
package/docs/settings.md CHANGED
@@ -53,9 +53,9 @@ A list of only `+name` and `-name` entries changes the inherited selection inste
53
53
 
54
54
  This replaces `bash` with `powershell` and enables `grep`: `["-bash", "+powershell", "+grep"]`. Project settings apply on top of user settings: a project list with only `+name` and `-name` entries changes the user's selection, and a project list with a plain name replaces it. In one list, plain names form the selection, and `+name` and `-name` then apply in order.
55
55
 
56
- `/reload` enables tools newly added to `defaultTools`. It does not disable tools removed from it or re-enable unchanged tools you turned off. `--tools`, `--no-tools`, and `--no-builtin-tools` override `defaultTools`, also on reload.
56
+ `/reload` enables tools newly added to `defaultTools`. It does not disable tools removed from it or re-enable unchanged tools you turned off. `--tools` with plain names, `--no-tools`, and `--no-builtin-tools` override `defaultTools`, also on reload.
57
57
 
58
- CLI tool options override this setting for one invocation; `--tools` does not accept `+name` or `-name`. See [Command Line](cli.md#tools).
58
+ CLI tool options override this setting for one invocation. `--tools` with only `+name` and `-name` entries changes the resolved `defaultTools` selection instead, for example `pi --tools +codemode`. On `/reload`, these entries apply to the reloaded setting too, so a tool removed with `-name` stays removed. See [Command Line](cli.md#tools).
59
59
 
60
60
  ## Sessions and context
61
61
 
@@ -97,7 +97,7 @@ See [Compaction Reference](compaction.md) for trigger, summarization, and valida
97
97
  | `fullscreenCopyOnSelect` | boolean | `true` | Copy selected text automatically in fullscreen mode. |
98
98
  | `fullscreenWheelScrollLines` | `"auto"` \| number | `"auto"` | Lines per mouse-wheel event in fullscreen mode, from 1 to 100. `"auto"` moves one line per event in local macOS terminals, which already accelerate wheel and trackpad input; elsewhere, and over SSH, it speeds up fast wheel spins to at most 6 lines per event. Alt+wheel moves five times as far. |
99
99
  | `editorPaddingX` | number | `0` | Horizontal editor padding from 0 to 3 cells. |
100
- | `outputPad` | `0 \| 1` | `1` | Horizontal transcript padding. |
100
+ | `outputPad` | `0 \| 1` | `1` | Horizontal transcript padding for messages, tool output, `!` command output, and summary blocks. |
101
101
  | `autocompleteMaxVisible` | number | `5` | Visible autocomplete entries, from 3 to 20. |
102
102
  | `showHardwareCursor` | boolean | `false` | Show the terminal cursor while Pi positions it for input methods. |
103
103
  | `terminal.showImages` | boolean | `true` | Display inline images when supported. |
@@ -217,3 +217,17 @@ Pi automatically detects OSC 8 hyperlinks, inline image protocols, and truecolor
217
217
  Settings take precedence over environment variables. An unset value or `auto` preserves automatic detection.
218
218
 
219
219
  Only force a capability supported by the complete terminal path. Unsupported escape sequences can corrupt rendering. See [Environment Variables](environment-variables.md#pi-process-configuration) and [Settings](settings.md) for the canonical value definitions.
220
+
221
+ ## Program status
222
+
223
+ Pi reports its state with the [Program Status Protocol (OSC 7501)](https://www.superlogical.com/rex/docs/build/program-status), so terminals and agent dashboards can show whether it is working, waiting for you, done, or failed:
224
+
225
+ | State | When |
226
+ |---|---|
227
+ | `working` | An agent run or compaction is in progress. The message is the session name. |
228
+ | `blocked` | An extension dialog or login waits for you. The message is the dialog title. |
229
+ | `done` | A run finished. The message is the session name. |
230
+ | `error` | A run ended with an error that is not retried. The message is the first line of the error. |
231
+ | `idle` | Pi started, or you cancelled the run. |
232
+
233
+ Reports never contain prompts or model output. Pi sends them only after the terminal answers the protocol's support query; tmux and screen do not forward them. Set `PI_PROGRAM_STATUS=1` to send reports without asking, or `PI_PROGRAM_STATUS=0` to turn them off.
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider",
3
- "version": "1.0.4",
3
+ "version": "1.1.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-custom-provider",
9
- "version": "1.0.4",
9
+ "version": "1.1.0",
10
10
  "dependencies": {
11
11
  "@anthropic-ai/sdk": "^0.52.0"
12
12
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider-anthropic",
3
3
  "private": true,
4
- "version": "1.0.4",
4
+ "version": "1.1.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider-gitlab-duo",
3
3
  "private": true,
4
- "version": "1.0.4",
4
+ "version": "1.1.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-gondolin",
3
- "version": "1.0.4",
3
+ "version": "1.1.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-gondolin",
9
- "version": "1.0.4",
9
+ "version": "1.1.0",
10
10
  "dependencies": {
11
11
  "@earendil-works/gondolin": "0.12.0"
12
12
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-gondolin",
3
3
  "private": true,
4
- "version": "1.0.4",
4
+ "version": "1.1.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-sandbox",
3
- "version": "1.0.4",
3
+ "version": "1.1.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-sandbox",
9
- "version": "1.0.4",
9
+ "version": "1.1.0",
10
10
  "dependencies": {
11
11
  "@anthropic-ai/sandbox-runtime": "^0.0.26"
12
12
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-sandbox",
3
3
  "private": true,
4
- "version": "1.0.4",
4
+ "version": "1.1.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-with-deps",
3
- "version": "1.0.4",
3
+ "version": "1.1.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-with-deps",
9
- "version": "1.0.4",
9
+ "version": "1.1.0",
10
10
  "dependencies": {
11
11
  "ms": "^2.1.3"
12
12
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-with-deps",
3
3
  "private": true,
4
- "version": "1.0.4",
4
+ "version": "1.1.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@earendil-works/pi-coding-agent",
3
- "version": "1.0.4",
3
+ "version": "1.1.0",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "piConfig": {
@@ -40,19 +40,19 @@
40
40
  "clean": "shx rm -rf dist",
41
41
  "build": "npm run build:unbundled && node ../../scripts/build-coding-agent-bundle.mjs",
42
42
  "build:unbundled": "tsc -p tsconfig.build.json && shx chmod +x dist/cli.js dist/rpc-entry.js && npm run copy-assets",
43
- "build:binary": "npm --prefix ../tui run build && npm --prefix ../telemetry run build && npm --prefix ../ai run build && npm --prefix ../agent run build && npm --prefix ../protocol run build && npm --prefix ../client run build && npm run build && bun build --compile --no-compile-autoload-bunfig ./dist/bun/cli.js ./src/utils/image-resize-worker.ts ./src/extensions/codemode/worker.ts --outfile dist/pi && npm run copy-binary-assets",
43
+ "build:binary": "npm --prefix ../tui run build && npm --prefix ../telemetry run build && npm --prefix ../ai run build && npm --prefix ../agent run build && npm --prefix ../protocol run build && npm --prefix ../client run build && npm run build && bun build --compile --no-compile-autoload-bunfig --no-compile-autoload-dotenv ./dist/bun/cli.js ./src/utils/image-resize-worker.ts ./src/extensions/codemode/worker.ts --outfile dist/pi && npm run copy-binary-assets",
44
44
  "copy-assets": "shx mkdir -p dist/modes/interactive/theme && shx cp src/modes/interactive/theme/*.json dist/modes/interactive/theme/ && shx mkdir -p dist/modes/interactive/assets && shx cp src/modes/interactive/assets/*.png dist/modes/interactive/assets/ && shx mkdir -p dist/core/export-html/vendor && shx cp src/core/export-html/template.html src/core/export-html/template.css src/core/export-html/template.js dist/core/export-html/ && shx cp src/core/export-html/vendor/*.js dist/core/export-html/vendor/",
45
45
  "copy-binary-assets": "shx cp package.json dist/ && shx cp README.md dist/ && shx cp CHANGELOG.md dist/ && shx mkdir -p dist/theme && shx cp src/modes/interactive/theme/*.json dist/theme/ && shx mkdir -p dist/assets && shx cp src/modes/interactive/assets/*.png dist/assets/ && shx mkdir -p dist/export-html/vendor && shx cp src/core/export-html/template.html dist/export-html/ && shx cp src/core/export-html/vendor/*.js dist/export-html/vendor/ && shx cp -r docs dist/ && shx cp -r examples dist/ && shx cp ../../node_modules/@silvia-odwyer/photon-node/photon_rs_bg.wasm dist/",
46
46
  "test": "vitest --run",
47
47
  "prepublishOnly": "npm run clean && npm run build"
48
48
  },
49
49
  "dependencies": {
50
- "@earendil-works/chord": "^1.0.4",
51
- "@earendil-works/pi-agent-core": "^1.0.4",
52
- "@earendil-works/pi-ai": "^1.0.4",
53
- "@earendil-works/pi-codemode": "^1.0.4",
54
- "@earendil-works/pi-mcp": "^1.0.4",
55
- "@earendil-works/pi-tui": "^1.0.4",
50
+ "@earendil-works/chord": "^1.1.0",
51
+ "@earendil-works/pi-agent-core": "^1.1.0",
52
+ "@earendil-works/pi-ai": "^1.1.0",
53
+ "@earendil-works/pi-codemode": "^1.1.0",
54
+ "@earendil-works/pi-mcp": "^1.1.0",
55
+ "@earendil-works/pi-tui": "^1.1.0",
56
56
  "@silvia-odwyer/photon-node": "0.3.4",
57
57
  "brace-expansion": "5.0.12",
58
58
  "chalk": "6.0.0",
@@ -75,16 +75,16 @@
75
75
  "protobufjs": "7.6.6"
76
76
  },
77
77
  "devDependencies": {
78
- "@earendil-works/pi-client": "^1.0.4",
79
- "@earendil-works/pi-protocol": "^1.0.4",
80
- "@earendil-works/pi-server": "^1.0.4",
78
+ "@earendil-works/pi-client": "^1.1.0",
79
+ "@earendil-works/pi-protocol": "^1.1.0",
80
+ "@earendil-works/pi-server": "^1.1.0",
81
81
  "@types/cross-spawn": "6.0.6",
82
82
  "@types/hosted-git-info": "3.0.5",
83
83
  "@types/node": "22.19.19",
84
84
  "@types/proper-lockfile": "4.1.4",
85
85
  "@types/semver": "7.7.1",
86
86
  "marked": "18.0.11",
87
- "shx": "0.4.0",
87
+ "shx": "0.3.4",
88
88
  "vitest": "4.1.11"
89
89
  },
90
90
  "keywords": [
@@ -1,82 +0,0 @@
1
- import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url);
2
- import{FileAuthStorageBackend,JSON_RPC_ERROR_CODES,LATEST_PROTOCOL_VERSION,McpAuthRequiredError,McpClient,McpError,McpHttpError,McpSessionExpiredError,StdioTransport,StreamableHttpTransport,isMcpAppResource,isObject,mcpNamespace,require_proper_lockfile,resolveConfigValueOrThrow,resolveHeadersOrThrow}from"./chunk-VLMAON6X.js";import{APP_NAME,VERSION,getAgentDir}from"./chunk-64GOPQSC.js";import{__toESM}from"./chunk-4L3WN2XY.js";import{homedir}from"node:os";import{basename,join as join2,resolve}from"node:path";import{setTimeout as sleep}from"node:timers/promises";import{pathToFileURL}from"node:url";import{createServer}from"node:http";function plainText(page){return page.ok?"Authorization complete. You may close this window.":page.details?`${page.message}
3
-
4
- ${page.details}`:page.message}var OAuthCallbackServer=class _OAuthCallbackServer{redirectUrl;server;paths;timeoutMs;renderPage;pending=new Map;constructor(server,redirectUrl,paths,timeoutMs,renderPage2){this.server=server,this.redirectUrl=redirectUrl,this.paths=paths,this.timeoutMs=timeoutMs,this.renderPage=renderPage2}static async listen(options={}){let host=options.host??"127.0.0.1",redirectHost=options.redirectHost??host,path=options.path??"/callback",instance,server=createServer((request,response)=>instance?.handle(request.url??"/",response));await new Promise((resolve2,reject)=>{server.once("error",reject),server.listen(options.port??0,host,()=>{server.off("error",reject),resolve2()})});let address=server.address();if(!address||typeof address=="string")throw new Error("OAuth callback server did not bind to TCP");return instance=new _OAuthCallbackServer(server,`http://${redirectHost.includes(":")?`[${redirectHost}]`:redirectHost}:${address.port}${path}`,[path,...options.extraPaths??[]],options.timeoutMs??5*6e4,options.renderPage),instance}waitForCallback(state,path){if(this.pending.has(state))throw new Error("OAuth state is already pending");return new Promise((resolve2,reject)=>{let timer=setTimeout(()=>{this.pending.delete(state),reject(new Error("OAuth callback timed out"))},this.timeoutMs);this.pending.set(state,{resolve:resolve2,reject,timer,path})})}async close(){for(let pending of this.pending.values())clearTimeout(pending.timer),pending.reject(new Error("OAuth callback server closed"));this.pending.clear(),await new Promise((resolve2,reject)=>{this.server.close(error=>error?reject(error):resolve2())})}reply(response,status,page){this.renderPage?(response.writeHead(status,{"content-type":"text/html; charset=utf-8","cache-control":"no-store"}),response.end(this.renderPage(page))):response.writeHead(status,{"content-type":"text/plain; charset=utf-8"}).end(plainText(page))}handle(rawUrl,response){let url=new URL(rawUrl,this.redirectUrl);if(!this.paths.includes(url.pathname)){this.reply(response,404,{ok:!1,message:"Not found"});return}let state=url.searchParams.get("state"),pending=state?this.pending.get(state):void 0;if(!state||!pending){this.reply(response,400,{ok:!1,message:"Invalid or expired OAuth state"});return}if(clearTimeout(pending.timer),this.pending.delete(state),pending.path!==void 0&&url.pathname!==pending.path){pending.reject(new Error("The authorization response arrived on another redirect URI")),this.reply(response,400,{ok:!1,message:"Unexpected redirect URI"});return}let error=url.searchParams.get("error");if(error){let description=url.searchParams.get("error_description")??error;pending.reject(new Error(description)),this.reply(response,200,{ok:!1,message:"Authorization failed. You may close this window.",details:description});return}let code=url.searchParams.get("code");if(!code){pending.reject(new Error("OAuth callback did not include an authorization code")),this.reply(response,400,{ok:!1,message:"Missing authorization code"});return}let iss=url.searchParams.get("iss");pending.resolve({code,state,...iss?{iss}:{}}),this.reply(response,200,{ok:!0})}};var OAuthError=class extends Error{code;errorUri;constructor(code,message,errorUri){super(message||code),this.name="OAuthError",this.code=code,this.errorUri=errorUri}},OAuthIssuerMismatchError=class extends Error{expected;received;constructor(expected,received){super(`OAuth issuer mismatch: expected ${JSON.stringify(expected)}, received ${received===void 0?"none":JSON.stringify(received)}`),this.name="OAuthIssuerMismatchError",this.expected=expected,this.received=received}},OAuthInsecureEndpointError=class extends Error{endpoint;constructor(endpoint){super(`Refusing to send OAuth credentials to non-HTTPS endpoint ${endpoint}`),this.name="OAuthInsecureEndpointError",this.endpoint=endpoint}},OAuthRegistrationError=class extends Error{status;body;constructor(status,body){super(`OAuth dynamic client registration failed with status ${status}: ${body}`),this.name="OAuthRegistrationError",this.status=status,this.body=body}},McpOAuthAuthorizationRequiredError=class extends Error{constructor(){super("MCP OAuth authorization requires user interaction"),this.name="McpOAuthAuthorizationRequiredError"}};function object(value,name){if(!isObject(value))throw new Error(`Invalid ${name}`);return value}function compact(value){return Object.fromEntries(Object.entries(value).filter(([,item])=>item!==void 0))}function requiredString(value,name){if(typeof value!="string"||value.length===0)throw new Error(`Invalid ${name}`);return value}function absent(value){return value==null||value===""}function optionalString(value,name){if(!absent(value))return requiredString(value,name)}function optionalStrings(value,name){if(value!=null){if(!Array.isArray(value)||value.some(item=>typeof item!="string"))throw new Error(`Invalid ${name}`);return[...value]}}function safeUrl(value,name){let text=requiredString(value,name);if(!URL.canParse(text))throw new Error(`Invalid ${name}`);let url=new URL(text);if(["javascript:","data:","vbscript:"].includes(url.protocol))throw new Error(`Invalid ${name}`);return text}function optionalUrl(value,name){return absent(value)?void 0:safeUrl(value,name)}function parseProtectedResourceMetadata(value){let input=object(value,"OAuth protected resource metadata");return compact({...input,resource:safeUrl(input.resource,"OAuth protected resource metadata resource"),authorization_servers:optionalStrings(input.authorization_servers,"authorization_servers")?.map(url=>safeUrl(url,"authorization server URL")),scopes_supported:optionalStrings(input.scopes_supported,"scopes_supported")})}function parseAuthorizationServerMetadata(value){let input=object(value,"authorization server metadata"),responseTypes=optionalStrings(input.response_types_supported,"response_types_supported");if(!responseTypes)throw new Error("Invalid response_types_supported");return compact({...input,issuer:safeUrl(input.issuer,"authorization server issuer"),authorization_endpoint:safeUrl(input.authorization_endpoint,"authorization endpoint"),token_endpoint:safeUrl(input.token_endpoint,"token endpoint"),registration_endpoint:optionalUrl(input.registration_endpoint,"registration endpoint"),scopes_supported:optionalStrings(input.scopes_supported,"scopes_supported"),response_types_supported:responseTypes,grant_types_supported:optionalStrings(input.grant_types_supported,"grant_types_supported"),token_endpoint_auth_methods_supported:optionalStrings(input.token_endpoint_auth_methods_supported,"token_endpoint_auth_methods_supported"),code_challenge_methods_supported:optionalStrings(input.code_challenge_methods_supported,"code_challenge_methods_supported"),client_id_metadata_document_supported:typeof input.client_id_metadata_document_supported=="boolean"?input.client_id_metadata_document_supported:void 0,authorization_response_iss_parameter_supported:typeof input.authorization_response_iss_parameter_supported=="boolean"?input.authorization_response_iss_parameter_supported:void 0})}function parseOAuthTokens(value){let input=object(value,"OAuth token response"),expires=absent(input.expires_in)?void 0:Number(input.expires_in);if(expires!==void 0&&!Number.isFinite(expires))throw new Error("Invalid expires_in");return compact({access_token:requiredString(input.access_token,"access_token"),token_type:requiredString(input.token_type,"token_type"),expires_in:expires,scope:optionalString(input.scope,"scope"),refresh_token:optionalString(input.refresh_token,"refresh_token"),id_token:optionalString(input.id_token,"id_token")})}function parseClientInformation(value){let input=object(value,"OAuth client registration response");return compact({...input,client_id:requiredString(input.client_id,"client_id"),client_secret:optionalString(input.client_secret,"client_secret"),client_id_issued_at:typeof input.client_id_issued_at=="number"?input.client_id_issued_at:void 0,client_secret_expires_at:typeof input.client_secret_expires_at=="number"?input.client_secret_expires_at:void 0,redirect_uris:optionalStrings(input.redirect_uris,"redirect_uris")??[]})}function discard(response){response?.body?.cancel().catch(()=>{})}function isDiscoveryMiss(status){return status>=400&&status<500||status===502}function pathSuffix(pathname){return pathname.endsWith("/")?pathname.slice(0,-1):pathname}function field(header,name){let match=header.match(new RegExp(String.raw`(?:^|[,\s])${name}=(?:"([^"]*)"|([^\s,]+))`,"i"));return match?.[1]||match?.[2]||void 0}function parseWwwAuthenticate(header){if(!header)return{};let scheme=header.trimStart().split(/\s+/,1)[0]?.toLowerCase();if(scheme!=="bearer"&&scheme!=="dpop")return{};let resourceMetadata=field(header,"resource_metadata"),resourceMetadataUrl;if(resourceMetadata)try{resourceMetadataUrl=new URL(resourceMetadata)}catch{}return{resourceMetadataUrl,scope:field(header,"scope"),error:field(header,"error"),errorDescription:field(header,"error_description")}}async function fetchMetadata(url,fetch,protocolVersion){return fetch(url,{headers:{Accept:"application/json","MCP-Protocol-Version":protocolVersion}})}async function discoverProtectedResourceMetadata(serverUrl,options={}){let server=new URL(serverUrl),fetch=options.fetch??globalThis.fetch,version=options.protocolVersion??LATEST_PROTOCOL_VERSION,response=await fetchMetadata(options.resourceMetadataUrl?new URL(options.resourceMetadataUrl):new URL(`/.well-known/oauth-protected-resource${pathSuffix(server.pathname)}`,server.origin),fetch,version);if(!options.resourceMetadataUrl&&server.pathname!=="/"&&isDiscoveryMiss(response.status)&&(discard(response),response=await fetchMetadata(new URL("/.well-known/oauth-protected-resource",server.origin),fetch,version)),!response.ok)throw discard(response),new Error(`HTTP ${response.status} loading OAuth protected resource metadata`);return parseProtectedResourceMetadata(await response.json())}function buildAuthorizationServerDiscoveryUrls(authorizationServerUrl){let issuer=new URL(authorizationServerUrl),path=pathSuffix(issuer.pathname),urls=[{url:new URL(`/.well-known/oauth-authorization-server${path}`,issuer.origin),type:"oauth"},{url:new URL(`/.well-known/openid-configuration${path}`,issuer.origin),type:"oidc"}];return path&&urls.push({url:new URL(`${path}/.well-known/openid-configuration`,issuer.origin),type:"oidc"}),urls}async function discoverAuthorizationServerMetadata(authorizationServerUrl,options={}){let fetch=options.fetch??globalThis.fetch;for(let{url}of buildAuthorizationServerDiscoveryUrls(authorizationServerUrl)){let response=await fetchMetadata(url,fetch,options.protocolVersion??LATEST_PROTOCOL_VERSION);if(!response.ok){if(discard(response),isDiscoveryMiss(response.status))continue;throw new Error(`HTTP ${response.status} loading authorization server metadata from ${url}`)}let metadata=parseAuthorizationServerMetadata(await response.json());if(!options.skipIssuerValidation){let expected=String(authorizationServerUrl),trim=value=>value.endsWith("/")?value.slice(0,-1):value;if(trim(metadata.issuer)!==trim(expected))throw new OAuthIssuerMismatchError(expected,metadata.issuer)}return metadata}}async function discoverOAuthServerInfo(serverUrl,options={}){let resourceMetadata;try{resourceMetadata=await discoverProtectedResourceMetadata(serverUrl,{resourceMetadataUrl:options.resourceMetadataUrl,fetch:options.fetch})}catch(error){if(error instanceof TypeError)throw error}if(options.authorizationServerMetadataUrl){let url=options.authorizationServerMetadataUrl,response=await fetchMetadata(url,options.fetch??globalThis.fetch,LATEST_PROTOCOL_VERSION);if(!response.ok)throw discard(response),new Error(`HTTP ${response.status} loading authorization server metadata from ${url}`);let metadata=parseAuthorizationServerMetadata(await response.json());return{authorizationServerUrl:metadata.issuer,authorizationServerMetadata:metadata,resourceMetadata}}let authorizationServerUrl=resourceMetadata?.authorization_servers?.[0]??String(new URL("/",serverUrl));return{authorizationServerUrl,authorizationServerMetadata:await discoverAuthorizationServerMetadata(authorizationServerUrl,{fetch:options.fetch,skipIssuerValidation:options.skipIssuerValidation}),resourceMetadata}}function resourceUrlFromServerUrl(value){let url=new URL(value);return url.hash="",url}function selectResource(serverUrl,metadata){if(!metadata)return;let requested=resourceUrlFromServerUrl(serverUrl),configured=new URL(metadata.resource);if(requested.origin!==configured.origin)throw new Error(`Protected resource ${metadata.resource} does not match MCP server ${requested}`);let requestedPath=requested.pathname.endsWith("/")?requested.pathname:`${requested.pathname}/`,configuredPath=configured.pathname.endsWith("/")?configured.pathname:`${configured.pathname}/`;if(!requestedPath.startsWith(configuredPath))throw new Error(`Protected resource ${metadata.resource} does not match MCP server ${requested}`);return metadata.resource}function loopback(hostname){return hostname==="localhost"||hostname==="127.0.0.1"||hostname==="[::1]"||hostname==="::1"}function applicationType(redirectUris){return redirectUris.some(uri=>{if(!URL.canParse(uri))return!1;let url=new URL(uri);return url.protocol!=="http:"&&url.protocol!=="https:"||loopback(url.hostname)})?"native":"web"}function secureEndpoint(value){let url=new URL(value);if(url.protocol!=="https:"&&!loopback(url.hostname))throw new OAuthInsecureEndpointError(url.href);return url}function selectClientAuthMethod(information,supported){let hinted="token_endpoint_auth_method"in information?information.token_endpoint_auth_method:void 0;return hinted&&["client_secret_basic","client_secret_post","none"].includes(hinted)&&(supported.length===0||supported.includes(hinted))?hinted:supported.length===0?information.client_secret?"client_secret_basic":"none":information.client_secret&&supported.includes("client_secret_basic")?"client_secret_basic":information.client_secret&&supported.includes("client_secret_post")?"client_secret_post":supported.includes("none")?"none":information.client_secret?"client_secret_post":"none"}function applyClientAuthentication(method,information,headers,params){if(method==="client_secret_basic"){if(!information.client_secret)throw new Error("client_secret_basic requires a client secret");headers.set("Authorization",`Basic ${Buffer.from(`${information.client_id}:${information.client_secret}`).toString("base64")}`)}else params.set("client_id",information.client_id),method==="client_secret_post"&&information.client_secret&&params.set("client_secret",information.client_secret)}async function pkce(){let bytes=crypto.getRandomValues(new Uint8Array(32)),verifier=Buffer.from(bytes).toString("base64url"),digest=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(verifier));return{verifier,challenge:Buffer.from(digest).toString("base64url")}}async function startAuthorization(authorizationServerUrl,options){let metadata=options.metadata;if(metadata&&!metadata.response_types_supported.includes("code"))throw new Error("Authorization server does not support authorization codes");if(metadata?.code_challenge_methods_supported&&!metadata.code_challenge_methods_supported.includes("S256"))throw new Error("Authorization server does not support PKCE S256");let url=new URL(metadata?.authorization_endpoint??new URL("/authorize",authorizationServerUrl)),{verifier,challenge}=await pkce();return url.searchParams.set("response_type","code"),url.searchParams.set("client_id",options.clientInformation.client_id),url.searchParams.set("code_challenge",challenge),url.searchParams.set("code_challenge_method","S256"),url.searchParams.set("redirect_uri",String(options.redirectUrl)),options.state&&url.searchParams.set("state",options.state),options.scope&&url.searchParams.set("scope",options.scope),options.scope?.split(/\s+/).includes("offline_access")&&url.searchParams.set("prompt","consent"),options.resource&&url.searchParams.set("resource",options.resource),{authorizationUrl:url,codeVerifier:verifier}}async function tokenRequest(authorizationServerUrl,options,params){let url=secureEndpoint(options.metadata?.token_endpoint??new URL("/token",authorizationServerUrl)),headers=new Headers({Accept:"application/json","content-type":"application/x-www-form-urlencoded"});options.resource&&params.set("resource",options.resource),options.addClientAuthentication?await options.addClientAuthentication(headers,params,url,options.metadata):applyClientAuthentication(selectClientAuthMethod(options.clientInformation,options.metadata?.token_endpoint_auth_methods_supported??[]),options.clientInformation,headers,params);let response=await(options.fetch??globalThis.fetch)(url,{method:"POST",headers,body:params}),text=await response.text(),value;try{value=JSON.parse(text)}catch{}if(isObject(value)&&typeof value.error=="string")throw new OAuthError(value.error,typeof value.error_description=="string"?value.error_description:value.error,typeof value.error_uri=="string"?value.error_uri:void 0);if(!response.ok)throw new OAuthError("server_error",`HTTP ${response.status}: ${text}`);return parseOAuthTokens(value)}async function registerClient(authorizationServerUrl,options){let endpoint=options.metadata?.registration_endpoint;if(options.metadata&&!endpoint)throw new Error("Authorization server does not support dynamic client registration");let response=await(options.fetch??globalThis.fetch)(new URL(endpoint??new URL("/register",authorizationServerUrl)),{method:"POST",headers:{Accept:"application/json","content-type":"application/json"},body:JSON.stringify({...options.clientMetadata,application_type:options.clientMetadata.application_type??applicationType(options.clientMetadata.redirect_uris),...options.scope?{scope:options.scope}:{}})});if(!response.ok)throw new OAuthRegistrationError(response.status,await response.text());return parseClientInformation(await response.json())}async function exchangeAuthorizationCode(authorizationServerUrl,options){return tokenRequest(authorizationServerUrl,options,new URLSearchParams({grant_type:"authorization_code",code:options.code,code_verifier:options.codeVerifier,redirect_uri:String(options.redirectUrl)}))}async function refreshAuthorization(authorizationServerUrl,options){let tokens=await tokenRequest(authorizationServerUrl,options,new URLSearchParams({grant_type:"refresh_token",refresh_token:options.refreshToken}));return{refresh_token:options.refreshToken,...tokens}}function withScope(tokens,scope){return tokens.scope===void 0&&scope?{...tokens,scope}:tokens}function stepUpScope(granted,challenged){if(!challenged)return;let scopes=[granted,challenged].flatMap(scope=>scope?.split(/\s+/).filter(Boolean)??[]);return[...new Set(scopes)].join(" ")}async function runFlow(provider,options){let metadataUrl=options.authorizationServerMetadataUrl&&secureEndpoint(options.authorizationServerMetadataUrl),cached=metadataUrl?void 0:await provider.discoveryState?.(),discovered=cached?.authorizationServerUrl?{authorizationServerUrl:cached.authorizationServerUrl,authorizationServerMetadata:cached.authorizationServerMetadata??await discoverAuthorizationServerMetadata(cached.authorizationServerUrl,{fetch:options.fetch,skipIssuerValidation:options.skipIssuerValidation}),resourceMetadata:cached.resourceMetadata}:await discoverOAuthServerInfo(options.serverUrl,{resourceMetadataUrl:options.resourceMetadataUrl,authorizationServerMetadataUrl:metadataUrl,fetch:options.fetch,skipIssuerValidation:options.skipIssuerValidation});metadataUrl||await provider.saveDiscoveryState?.({...discovered,...options.resourceMetadataUrl?{resourceMetadataUrl:options.resourceMetadataUrl.href}:{}});let metadata=discovered.authorizationServerMetadata,resource=selectResource(options.serverUrl,discovered.resourceMetadata),scope=options.scope||discovered.resourceMetadata?.scopes_supported?.join(" ")||provider.clientMetadata.scope,stored=await provider.clientInformation(),clientDocument=stored?void 0:provider.clientMetadataDocument?.(metadata);if(clientDocument){let url=new URL(clientDocument.url);if(url.protocol!=="https:"||url.pathname==="/")throw new Error("Invalid OAuth client metadata URL")}let client=stored??(clientDocument&&{client_id:clientDocument.url});if(!client){if(options.authorizationCode)throw new Error("OAuth client information is missing during code exchange");if(!provider.saveClientInformation)throw new Error("OAuth client information cannot be persisted");client=await registerClient(discovered.authorizationServerUrl,{metadata,clientMetadata:provider.clientMetadata,scope,fetch:options.fetch}),await provider.saveClientInformation(client)}let redirectUrl=clientDocument?.redirectUrl??provider.redirectUrl,tokenOptions={metadata,clientInformation:client,resource,addClientAuthentication:provider.addClientAuthentication,fetch:options.fetch};if(options.authorizationCode){let iss=options.iss;if(metadata&&(iss!==void 0||metadata.authorization_response_iss_parameter_supported)&&iss!==metadata.issuer)throw new OAuthIssuerMismatchError(metadata.issuer,iss);let tokens=await exchangeAuthorizationCode(discovered.authorizationServerUrl,{...tokenOptions,code:options.authorizationCode,codeVerifier:await provider.codeVerifier(),redirectUrl});return await provider.saveTokens(withScope(tokens,scope)),"AUTHORIZED"}let existing=options.skipRefresh?void 0:await provider.tokens();if(existing?.refresh_token)try{let tokens=await refreshAuthorization(discovered.authorizationServerUrl,{...tokenOptions,refreshToken:existing.refresh_token});return await provider.saveTokens(withScope(tokens,existing.scope)),"AUTHORIZED"}catch(error){if(error instanceof OAuthInsecureEndpointError||error instanceof OAuthError&&error.code!=="server_error")throw error}let state=await provider.state?.(),authorization=await startAuthorization(discovered.authorizationServerUrl,{metadata,clientInformation:client,redirectUrl,scope,state,resource});return await provider.saveCodeVerifier(authorization.codeVerifier),await provider.redirectToAuthorization(authorization.authorizationUrl),"REDIRECT"}async function authorizeMcp(provider,options){try{return await runFlow(provider,options)}catch(error){if(error instanceof OAuthError&&["invalid_client","unauthorized_client"].includes(error.code))return await provider.invalidateCredentials?.("all"),runFlow(provider,options);if(error instanceof OAuthError&&error.code==="invalid_grant")return await provider.invalidateCredentials?.("tokens"),runFlow(provider,options);throw error}}var MemoryOAuthStateStore=class{value;load(){return this.value===void 0?void 0:structuredClone(this.value)}save(state){this.value=structuredClone(state)}},McpOAuthProvider=class{redirectUrl;clientMetadata;clientMetadataDocument;serverUrl;configuredClient;store;onRedirect;writes=Promise.resolve();constructor(options){this.serverUrl=String(new URL(options.serverUrl)),this.redirectUrl=String(options.redirectUrl),this.clientMetadata={...options.clientMetadata,redirect_uris:options.clientMetadata.redirect_uris??[this.redirectUrl],grant_types:options.clientMetadata.grant_types??["authorization_code","refresh_token"],response_types:options.clientMetadata.response_types??["code"],token_endpoint_auth_method:options.clientMetadata.token_endpoint_auth_method??(options.clientSecret?"client_secret_post":"none")},this.clientMetadataDocument=options.clientMetadataDocument,this.configuredClient=options.clientId?{client_id:options.clientId,...options.clientSecret?{client_secret:options.clientSecret}:{}}:void 0,this.store=options.store??new MemoryOAuthStateStore,this.onRedirect=options.onRedirect}async state(){let existing=(await this.load()).oauthState;if(existing)return existing;let state=Buffer.from(crypto.getRandomValues(new Uint8Array(32))).toString("hex");return await this.update(value=>({...value,oauthState:state})),state}async clientInformation(){return this.configuredClient??(await this.load()).clientInformation}async saveClientInformation(information){this.configuredClient||await this.update(value=>({...value,clientInformation:information}))}async tokens(){return(await this.load()).tokens}async saveTokens(tokens){let expiresAt=tokens.expires_in===void 0?void 0:Date.now()+tokens.expires_in*1e3;await this.update(value=>{let next={...value,tokens};return expiresAt===void 0?delete next.tokensExpireAt:next.tokensExpireAt=expiresAt,next})}async redirectToAuthorization(url){await this.onRedirect(url)}async saveCodeVerifier(verifier){await this.update(value=>({...value,codeVerifier:verifier}))}async codeVerifier(){let verifier=(await this.load()).codeVerifier;if(!verifier)throw new Error("No OAuth PKCE code verifier is stored");return verifier}async invalidateCredentials(kind){await this.update(value=>{let next={...value};return(kind==="all"||kind==="client")&&delete next.clientInformation,(kind==="all"||kind==="tokens")&&(delete next.tokens,delete next.tokensExpireAt),(kind==="all"||kind==="verifier")&&delete next.codeVerifier,(kind==="all"||kind==="discovery")&&delete next.discovery,kind==="all"&&delete next.oauthState,next})}async saveDiscoveryState(discovery){await this.update(value=>({...value,discovery}))}async discoveryState(){return(await this.load()).discovery}async load(){return await this.writes,this.own(await this.store.load())}async update(update){this.writes=this.writes.then(async()=>{await this.store.save(update(this.own(await this.store.load())))}),await this.writes}own(state){return state?.serverUrl===this.serverUrl?state:{serverUrl:this.serverUrl}}};import{createHash}from"node:crypto";import{mkdirSync}from"node:fs";import{join}from"node:path";var LOGO_SVG='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800" aria-hidden="true"><path fill="#F09082" d="M165.29 165.29H517.36V400H400V282.65H165.29Z"/><path fill="#4D9ABF" d="M165.29 282.65H282.65V400H400V517.36H282.65V634.72H165.29Z"/><path fill="#F1BE58" d="M517.36 400H634.72V634.72H517.36Z"/></svg>';function escapeHtml(value){return value.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;")}function renderPage(options){let title=escapeHtml(options.title),heading=escapeHtml(options.heading),message=escapeHtml(options.message),details=options.details?escapeHtml(options.details):void 0;return`<!doctype html>
5
- <html lang="en">
6
- <head>
7
- <meta charset="utf-8" />
8
- <meta name="viewport" content="width=device-width, initial-scale=1" />
9
- <title>${title}</title>
10
- <style>
11
- :root {
12
- --text: #fafafa;
13
- --text-dim: #a1a1aa;
14
- --page-bg: #09090b;
15
- --font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
16
- --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
17
- }
18
- * { box-sizing: border-box; }
19
- html { color-scheme: dark; }
20
- body {
21
- margin: 0;
22
- min-height: 100vh;
23
- display: flex;
24
- align-items: center;
25
- justify-content: center;
26
- padding: 24px;
27
- background: var(--page-bg);
28
- color: var(--text);
29
- font-family: var(--font-sans);
30
- text-align: center;
31
- }
32
- main {
33
- width: 100%;
34
- max-width: 560px;
35
- display: flex;
36
- flex-direction: column;
37
- align-items: center;
38
- justify-content: center;
39
- }
40
- .logo {
41
- width: 72px;
42
- height: 72px;
43
- display: block;
44
- margin-bottom: 24px;
45
- }
46
- h1 {
47
- margin: 0 0 10px;
48
- font-size: 28px;
49
- line-height: 1.15;
50
- font-weight: 650;
51
- color: var(--text);
52
- }
53
- p {
54
- margin: 0;
55
- line-height: 1.7;
56
- color: var(--text-dim);
57
- font-size: 15px;
58
- }
59
- .details {
60
- margin-top: 16px;
61
- font-family: var(--font-mono);
62
- font-size: 13px;
63
- color: var(--text-dim);
64
- white-space: pre-wrap;
65
- word-break: break-word;
66
- }
67
- </style>
68
- </head>
69
- <body>
70
- <main>
71
- <div class="logo">${LOGO_SVG}</div>
72
- <h1>${heading}</h1>
73
- <p>${message}</p>
74
- ${details?`<div class="details">${details}</div>`:""}
75
- </main>
76
- </body>
77
- </html>`}function oauthSuccessHtml(message){return renderPage({title:"Authentication successful",heading:"Authentication successful",message})}function oauthErrorHtml(message,details){return renderPage({title:"Authentication failed",heading:"Authentication failed",message,details})}var import_proper_lockfile=__toESM(require_proper_lockfile(),1);var CALLBACK_HOST="127.0.0.1",CALLBACK_PATH="/callback",CLIENT_METADATA_BASE_URL="https://pi.dev/oauth",FALLBACK_REDIRECT_URL=`http://${CALLBACK_HOST}${CALLBACK_PATH}`,REFRESH_SKEW_MS=3e4,REFRESH_REQUEST_TIMEOUT_MS=15e3,REFRESH_LOCK_STALE_MS=2e4,REFRESH_LOCK_WAIT_MS=25e3,REFRESH_LOCK_RETRY_MS=100;function callbackSettings(settings){let url=new URL(settings.callbackUrl??`http://${CALLBACK_HOST}${CALLBACK_PATH}`),address=url.hostname.replace(/^\[|\]$/g,""),port=url.port?Number(url.port):settings.callbackPort,fixedRedirectUrl;return url.port?fixedRedirectUrl=settings.callbackUrl:port!==void 0&&(url.port=String(port),fixedRedirectUrl=url.href),{host:address==="localhost"?CALLBACK_HOST:address,redirectHost:address,port,path:url.pathname,fixedRedirectUrl}}function mergeScopes(...scopes){let merged=[...new Set(scopes.flatMap(scope=>scope?.split(/\s+/).filter(Boolean)??[]))];return merged.length>0?merged.join(" "):void 0}function parseStates(content){if(!content?.trim())return{};let parsed=JSON.parse(content);return typeof parsed=="object"&&parsed!==null&&!Array.isArray(parsed)?parsed:{}}function serializeStates(states){return`${JSON.stringify(states,null,2)}
78
- `}function storeKeys(name,serverUrl){let legacyKey=String(new URL(serverUrl));return{key:`${mcpNamespace(name)}|${legacyKey}`,legacyKey}}var McpOAuthCredentialStore=class{backend;lockDir;constructor(backend,lockDir){this.backend=backend??new FileAuthStorageBackend(join(getAgentDir(),"mcp-auth.json")),this.lockDir=backend?lockDir:getAgentDir()}forServer(name,serverUrl){let{key,legacyKey}=storeKeys(name,serverUrl);return{load:()=>this.backend.withLock(current=>{let states=parseStates(current);return states[key]||!states[legacyKey]?{result:states[key]}:(states[key]=states[legacyKey],delete states[legacyKey],{result:states[key],next:serializeStates(states)})}),save:state=>this.write(states=>{states[key]=state}),withRefreshLock:fn=>this.withRefreshLock(key,fn)}}async withRefreshLock(key,fn){if(!this.lockDir)return fn();mkdirSync(this.lockDir,{recursive:!0,mode:448});let hash=createHash("sha256").update(key).digest("hex").slice(0,16),release=await import_proper_lockfile.default.lock(join(this.lockDir,`mcp-auth-refresh-${hash}`),{realpath:!1,stale:REFRESH_LOCK_STALE_MS,retries:{retries:REFRESH_LOCK_WAIT_MS/REFRESH_LOCK_RETRY_MS,factor:1,minTimeout:REFRESH_LOCK_RETRY_MS,maxTimeout:REFRESH_LOCK_RETRY_MS},onCompromised:()=>{}});try{return await fn()}finally{await release().catch(()=>{})}}tokens(name,serverUrl){let{key,legacyKey}=storeKeys(name,serverUrl),states=this.backend.withLock(current=>({result:parseStates(current)}));return(states[key]??states[legacyKey])?.tokens}remove(name,serverUrl){let{key,legacyKey}=storeKeys(name,serverUrl);return this.backend.withLock(current=>{let states=parseStates(current),stored=key in states?key:legacyKey in states?legacyKey:void 0;return stored?(delete states[stored],{result:!0,next:serializeStates(states)}):{result:!1}})}write(update){this.backend.withLock(current=>{let states=parseStates(current);return update(states),{result:void 0,next:serializeStates(states)}})}};function registeredRedirectUrls(client){return client&&"redirect_uris"in client?client.redirect_uris:[]}function callbackId(serverUrl){let url=new URL(serverUrl);return url.hash="",createHash("sha256").update(url.href).digest().subarray(0,9).toString("base64url")}function clientMetadataDocument(serverUrl,redirectUrl,metadata){if(!metadata?.client_id_metadata_document_supported||!metadata.token_endpoint_auth_methods_supported?.includes("none"))throw new Error('The authorization server does not support Client ID Metadata Documents for public clients; remove oauth.clientRegistration "cimd"');if(metadata.authorization_response_iss_parameter_supported)return{url:`${CLIENT_METADATA_BASE_URL}/client.json`,redirectUrl};let id=callbackId(serverUrl),redirect=new URL(redirectUrl);return redirect.pathname=`${CALLBACK_PATH}/${id}`,{url:`${CLIENT_METADATA_BASE_URL}/${id}/client.json`,redirectUrl:redirect.href}}function createProvider(serverUrl,store,settings,redirectUrl,onRedirect){return new McpOAuthProvider({serverUrl,redirectUrl,clientMetadata:{client_name:settings.clientName??APP_NAME},clientMetadataDocument:settings.clientRegistration==="cimd"?metadata=>clientMetadataDocument(serverUrl,redirectUrl,metadata):void 0,clientId:settings.clientId,clientSecret:settings.clientSecret,store,onRedirect})}function createMcpAuthProvider(options){let{serverUrl,store}=options,refreshing,refresh=(staleToken,fetch=globalThis.fetch,challenge)=>(refreshing??=store.withRefreshLock(async()=>{let state=await store.load();if(state?.tokens?.access_token!==staleToken)return;if(!state?.tokens?.refresh_token)throw new McpOAuthAuthorizationRequiredError;let settings=options.settings(),redirectUrl=callbackSettings(settings).fixedRedirectUrl??registeredRedirectUrls(state.clientInformation)[0]??FALLBACK_REDIRECT_URL,provider=createProvider(serverUrl,store,settings,redirectUrl,()=>{});if(await authorizeMcp(provider,{serverUrl,resourceMetadataUrl:challenge?.resourceMetadataUrl,authorizationServerMetadataUrl:settings.authServerMetadataUrl,scope:challenge?.scope,fetch:(input,init)=>fetch(input,{...init,signal:AbortSignal.timeout(REFRESH_REQUEST_TIMEOUT_MS)})})==="REDIRECT")throw new McpOAuthAuthorizationRequiredError}).finally(()=>{refreshing=void 0}),refreshing);return{token:async()=>{await refreshing?.catch(()=>{});let state=await store.load(),token=state?.tokens?.access_token;return!(state?.tokensExpireAt!==void 0&&state.tokensExpireAt-REFRESH_SKEW_MS<=Date.now())||!state?.tokens?.refresh_token?token:(await refresh(token).catch(()=>{}),(await store.load())?.tokens?.access_token)},onUnauthorized:async context=>{let challenge=parseWwwAuthenticate(context.response.headers.get("www-authenticate"));if(options.onChallenge(challenge),challenge.error==="insufficient_scope")throw new McpOAuthAuthorizationRequiredError;await refresh(context.token,context.fetch,challenge)},settled:async()=>{await refreshing?.catch(()=>{})}}}var McpSignInCancelledError=class extends Error{constructor(){super("Sign-in cancelled"),this.name="McpSignInCancelledError"}};function responseFromRedirectUrl(input,state,redirectUrl){let url;try{url=new URL(input.trim())}catch{throw new Error("Expected the full redirect URL from the browser address bar")}if(url.origin!==redirectUrl.origin||url.pathname!==redirectUrl.pathname)throw new Error("The redirect URL does not match this sign-in's redirect URI");let error=url.searchParams.get("error");if(error)throw new Error(url.searchParams.get("error_description")??error);if(url.searchParams.get("state")!==state)throw new Error("The redirect URL belongs to a different sign-in");let code=url.searchParams.get("code");if(!code)throw new Error("The redirect URL does not contain an authorization code");return{code,iss:url.searchParams.get("iss")??void 0}}async function waitForAuthorizationResponse(callback,state,redirectUrl,prompt){let controller=new AbortController,fromBrowser=callback.waitForCallback(state,redirectUrl.pathname),fromUser=prompt.promptForRedirectUrl(controller.signal).then(input=>{if(!input?.trim())throw new McpSignInCancelledError;return responseFromRedirectUrl(input,state,redirectUrl)});try{return await Promise.race([fromBrowser,fromUser])}finally{controller.abort(),fromBrowser.catch(()=>{}),fromUser.catch(()=>{})}}async function listenForCallback(settings,extraPaths,port,required){let options={host:settings.host,redirectHost:settings.redirectHost,path:settings.path,extraPaths,renderPage:page=>page.ok?oauthSuccessHtml("Signed in to the MCP server. You may now close this page."):oauthErrorHtml(page.message,page.details)};try{return await OAuthCallbackServer.listen({...options,port:port??0})}catch(error){if(required||port===void 0)throw error;return OAuthCallbackServer.listen(options)}}async function signInMcpServer(options){let{serverUrl,store,settings}=options,stored=await store.load(),stepUp=options.challenge?.error==="insufficient_scope",callbackOptions=callbackSettings(settings),registered=registeredRedirectUrls(stored?.clientInformation)[0],preferredPort=callbackOptions.port??(registered&&Number(new URL(registered).port)||void 0),cimd=settings.clientRegistration==="cimd",callback=await listenForCallback(callbackOptions,cimd?[`${CALLBACK_PATH}/${callbackId(serverUrl)}`]:[],preferredPort,callbackOptions.port!==void 0),redirectUrl=callbackOptions.fixedRedirectUrl??callback.redirectUrl;try{if(stored){let next={...stored};delete next.oauthState,settings.clientId||(cimd?!stored.clientInformation:registeredRedirectUrls(stored.clientInformation).includes(redirectUrl))||(delete next.clientInformation,delete next.tokens,delete next.tokensExpireAt),await store.save(next)}let authorizationUrl,provider=createProvider(serverUrl,store,settings,redirectUrl,url=>{authorizationUrl=url}),flow={serverUrl,resourceMetadataUrl:options.challenge?.resourceMetadataUrl,authorizationServerMetadataUrl:settings.authServerMetadataUrl,scope:mergeScopes(settings.scope,stepUp?stepUpScope(stored?.tokens?.scope,options.challenge?.scope):options.challenge?.scope)};if(await authorizeMcp(provider,{...flow,skipRefresh:stepUp})==="AUTHORIZED")return;if(!authorizationUrl)throw new Error("OAuth flow did not produce an authorization URL");let state=await provider.state(),authorizationRedirectUrl=new URL(authorizationUrl.searchParams.get("redirect_uri")??redirectUrl);options.prompt.showAuthorizationUrl(authorizationUrl);let{code,iss}=await waitForAuthorizationResponse(callback,state,authorizationRedirectUrl,options.prompt);await authorizeMcp(provider,{...flow,authorizationCode:code,iss})}finally{await callback.close()}}import{appendFileSync,mkdirSync as mkdirSync2,renameSync,statSync}from"node:fs";import{dirname}from"node:path";var MAX_LOG_BYTES=5*1024*1024;function isRecord(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}function formatData(data){if(typeof data=="string")return data;try{return JSON.stringify(data)??String(data)}catch{return String(data)}}function formatMcpLogMessage(server,params,now=new Date){let message=isRecord(params)?params:{data:params},level=typeof message.level=="string"?message.level:"info",logger=typeof message.logger=="string"&&message.logger?` ${message.logger}:`:"",text=formatData(message.data).replace(/\r?\n/g,`
79
- `);return`${now.toISOString()} [${server}] ${level}${logger} ${text}
80
- `}var McpServerLog=class{path;size;constructor(path){this.path=path}write(server,params){let line=formatMcpLogMessage(server,params);try{this.size===void 0&&(mkdirSync2(dirname(this.path),{recursive:!0}),this.size=this.currentSize()),this.size>MAX_LOG_BYTES&&(this.currentSize()>MAX_LOG_BYTES&&renameSync(this.path,`${this.path}.1`),this.size=this.currentSize()),appendFileSync(this.path,line),this.size+=Buffer.byteLength(line)}catch{}}currentSize(){try{return statSync(this.path).size}catch{return 0}}};var DEFAULT_TIMEOUT_SECONDS=60,STDERR_TAIL_CHARS=2e3,CONNECT_RETRY_DELAYS_MS=[250,1e3];function errorMessage(error){return error instanceof Error?error.message:String(error)}function isTransientError(error){return error instanceof McpHttpError?error.status===408||error.status===429||error.status>=500&&error.status!==501:error instanceof TypeError}function signInRequiredMessage(entry){let provider="url"in entry.config?entry.config.auth?.provider:void 0;return`MCP server "${entry.name}" requires sign-in. Run ${provider?`/login ${provider}`:"/mcp"} to sign in.`}function usesOAuth(entry){let{config}=entry;return!("url"in config)||config.auth?!1:!Object.keys(config.headers??{}).some(header=>header.toLowerCase()==="authorization")}function expandHome(value){return value==="~"?homedir():value.startsWith("~/")||process.platform==="win32"&&value.startsWith("~\\")?join2(homedir(),value.slice(2)):value}function createDefaultTransport(entry,cwd,authProvider){let{config,name}=entry;if("url"in config)return new StreamableHttpTransport({url:config.url,headers:resolveHeadersOrThrow(config.headers,`MCP server "${name}"`),authProvider});let env={};for(let[key,value]of Object.entries(config.env??{}))env[key]=resolveConfigValueOrThrow(value,`MCP server "${name}" env "${key}"`);return new StdioTransport({command:expandHome(config.command),args:config.args?.map(expandHome),cwd:resolve(cwd,expandHome(config.cwd??".")),env,stderr:"pipe"})}async function withoutTemplates(list,empty){try{return await list()}catch(error){if(error instanceof McpError&&error.code===JSON_RPC_ERROR_CODES.methodNotFound)return empty;throw error}}function listTemplates(client,options={}){return withoutTemplates(()=>client.listResourceTemplates(options),[])}async function fetchResources(client){let[resources,resourceTemplates]=await Promise.all([client.listResources().catch(()=>[]),listTemplates(client).catch(()=>[])]);return{resources:resources.filter(resource=>!isMcpAppResource(resource)),resourceTemplates:resourceTemplates.filter(template=>!isMcpAppResource(template))}}var McpServerConnection=class{entry;state="connecting";error;tools=[];hasResources=!1;resources=[];resourceTemplates=[];instructions;challenge;client;opening;shutdown=new AbortController;stderrTail;cwd;createTransport;authProvider;onTools;onChange;log;constructor(options){this.entry=options.entry,this.cwd=options.cwd,this.createTransport=options.createTransport,this.onTools=options.onTools,this.onChange=options.onChange,this.log=options.log;let url=this.oauthUrl,provider="url"in this.entry.config?this.entry.config.auth?.provider:void 0;this.authProvider=url?createMcpAuthProvider({serverUrl:url,store:options.credentials.forServer(this.entry.name,url),settings:()=>this.oauthSettings(),onChallenge:challenge=>{this.challenge=challenge}}):provider?{token:async()=>options.providerToken?.(provider),settled:async()=>{}}:void 0}get name(){return this.entry.name}get closed(){return this.shutdown.signal.aborted}get timeoutMs(){return(this.entry.config.timeout??DEFAULT_TIMEOUT_SECONDS)*1e3}get oauthUrl(){return usesOAuth(this.entry)&&"url"in this.entry.config?this.entry.config.url:void 0}oauthSettings(){let oauth="url"in this.entry.config?this.entry.config.oauth:void 0;return oauth?{clientId:oauth.clientId,clientSecret:oauth.clientSecret===void 0?void 0:resolveConfigValueOrThrow(oauth.clientSecret,`MCP server "${this.entry.name}" oauth.clientSecret`),callbackPort:oauth.callbackPort,callbackUrl:oauth.callbackUrl,scope:oauth.scope,clientName:oauth.clientName,clientRegistration:oauth.clientRegistration,authServerMetadataUrl:oauth.authServerMetadataUrl?new URL(oauth.authServerMetadataUrl):void 0}:{}}getClient(){return this.closed?Promise.reject(new Error(`MCP server "${this.entry.name}" is shut down`)):this.client?.connectionState==="connected"?Promise.resolve(this.client):(this.opening??=this.open().finally(()=>{this.opening=void 0}),this.opening)}callTool(name,args,options){return this.withClient(client=>client.callTool(name,args,options))}readResource(uri,options){return this.withClient(client=>client.readResource(uri,options),!0)}resourcesPage(cursor,options){return this.withClient(client=>client.listResourcesPage(cursor,options),!0)}resourceTemplatesPage(cursor,options){return this.withClient(client=>withoutTemplates(()=>client.listResourceTemplatesPage(cursor,options),{resourceTemplates:[]}),!0)}allResources(options){return this.withClient(client=>client.listResources(options),!0)}allResourceTemplates(options){return this.withClient(client=>listTemplates(client,options),!0)}async withClient(run,readOnly=!1){for(let attempt=1;;attempt++){let client=await this.getClient();try{return await run(client)}catch(error){if(readOnly&&attempt===1&&error instanceof McpHttpError&&isTransientError(error)){await new Promise(resolve2=>setTimeout(resolve2,CONNECT_RETRY_DELAYS_MS[0]));continue}if(error instanceof McpSessionExpiredError&&attempt===1){this.client===client&&(this.client=void 0);continue}throw this.needsSignIn(error)?(await this.dropClient(client),this.markNeedsAuth(),new Error(signInRequiredMessage(this.entry))):error}}}async reconnect(){await this.opening?.catch(()=>{}),this.client&&await this.dropClient(this.client),await this.getClient()}async signOut(){await this.opening?.catch(()=>{}),this.client&&await this.dropClient(this.client),this.closed||this.markNeedsAuth()}needsSignIn(error){return error instanceof McpOAuthAuthorizationRequiredError||this.authProvider!==void 0&&error instanceof McpAuthRequiredError}markNeedsAuth(){this.state="needs-auth",this.error=void 0,this.changed()}changed(){this.onChange?.(this)}async dropClient(client){this.client===client&&(this.client=void 0),await client.close().catch(()=>{})}async open(){this.state="connecting",this.changed();let retries="url"in this.entry.config?CONNECT_RETRY_DELAYS_MS:[];for(let attempt=0;;attempt++){this.stderrTail=void 0;try{return await this.connectOnce()}catch(error){let delay=retries[attempt];if(this.closed||delay===void 0||!isTransientError(error))throw this.connectFailed(error);if(await sleep(delay,void 0,{signal:this.shutdown.signal}).catch(()=>{}),this.closed)throw this.connectFailed(error)}}}async connectOnce(){let client=new McpClient({name:"pi",version:VERSION,requestTimeoutMs:this.timeoutMs,roots:[{uri:pathToFileURL(this.cwd).href,name:basename(this.cwd)}]}),log=this.log;log&&client.onNotification("notifications/message",params=>log.write(this.entry.name,params));let closing,closeClient=()=>(closing??=client.close().catch(()=>{}),closing);this.shutdown.signal.addEventListener("abort",closeClient,{once:!0});let transport;try{transport=this.createTransport(this.entry,this.cwd,this.authProvider),await client.connect(transport),client.onNotification("notifications/tools/list_changed",()=>{this.refreshTools(client)}),client.onNotification("notifications/resources/list_changed",()=>{this.refreshResources(client)});let stdio=transport instanceof StdioTransport?transport:void 0;client.onClose(()=>this.handleClientClose(client,stdio));let hasResources=client.serverCapabilities?.resources!==void 0,[tools,resources]=await Promise.all([client.serverCapabilities?.tools?client.listTools():[],hasResources?fetchResources(client):{resources:[],resourceTemplates:[]}]);if(this.closed)throw new Error("shut down while connecting");if(client.connectionState!=="connected")throw new Error("connection closed during setup");return this.client=client,this.tools=tools,this.hasResources=hasResources,this.resources=resources.resources,this.resourceTemplates=resources.resourceTemplates,this.instructions=client.instructions?.trim()||void 0,this.state="connected",this.error=void 0,this.onTools(this),this.changed(),client}catch(error){throw await closeClient(),transport instanceof StdioTransport&&(this.stderrTail=transport.stderr.trim().slice(-STDERR_TAIL_CHARS)||void 0),error}finally{this.shutdown.signal.removeEventListener("abort",closeClient)}}connectFailed(error){return this.needsSignIn(error)&&!this.closed?(this.markNeedsAuth(),new Error(signInRequiredMessage(this.entry))):(this.state=this.closed?"closed":"failed",this.error=this.stderrTail?`${errorMessage(error)}
81
- ${this.stderrTail}`:errorMessage(error),this.changed(),new Error(`MCP server "${this.entry.name}" failed to connect: ${this.error}`))}handleClientClose(client,stdio){if(this.client!==client||this.closed)return;this.client=void 0,this.state="disconnected";let stderr=stdio?.stderr.trim().slice(-STDERR_TAIL_CHARS);this.error=stderr?`Connection closed
82
- ${stderr}`:"Connection closed",this.changed()}async refreshTools(client){try{let tools=await client.listTools();if(this.client!==client||this.closed)return;this.tools=tools,this.onTools(this)}catch(error){this.error=`Failed to refresh tools: ${errorMessage(error)}`}this.changed()}async refreshResources(client){let{resources,resourceTemplates}=await fetchResources(client);this.client!==client||this.closed||(this.resources=resources,this.resourceTemplates=resourceTemplates,this.onTools(this),this.changed())}async close(){this.shutdown.abort(),this.state="closed",this.changed();let client=this.client;this.client=void 0,await Promise.all([client?.close().catch(()=>{}),this.opening?.catch(()=>{})]),await this.authProvider?.settled()}};export{McpOAuthCredentialStore,McpSignInCancelledError,signInMcpServer,McpServerLog,createDefaultTransport,McpServerConnection};
@@ -1,2 +0,0 @@
1
- import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url);
2
- var FifoQueue=class{incoming=[];outgoing=[];get length(){return this.incoming.length+this.outgoing.length}enqueue(value){this.incoming.push(value)}dequeue(){if(this.outgoing.length===0)for(;this.incoming.length>0;)this.outgoing.push(this.incoming.pop());return this.outgoing.pop()}},EventStream=class{queue=new FifoQueue;waiting=new FifoQueue;done=!1;finalResultPromise;resolveFinalResult;isComplete;extractResult;constructor(isComplete,extractResult){this.isComplete=isComplete,this.extractResult=extractResult,this.finalResultPromise=new Promise(resolve=>{this.resolveFinalResult=resolve})}push(event){if(this.done)return;this.isComplete(event)&&(this.done=!0,this.resolveFinalResult(this.extractResult(event)));let waiter=this.waiting.dequeue();waiter?waiter({value:event,done:!1}):this.queue.enqueue(event)}end(result){for(this.done=!0,result!==void 0&&this.resolveFinalResult(result);this.waiting.length>0;)this.waiting.dequeue()({value:void 0,done:!0})}async*[Symbol.asyncIterator](){for(;;)if(this.queue.length>0)yield this.queue.dequeue();else{if(this.done)return;{let result=await new Promise(resolve=>this.waiting.enqueue(resolve));if(result.done)return;yield result.value}}}result(){return this.finalResultPromise}},AssistantMessageEventStream=class extends EventStream{constructor(){super(event=>event.type==="done"||event.type==="error",event=>{if(event.type==="done")return event.message;if(event.type==="error")return event.error;throw new Error("Unexpected event type for final result")})}};function createAssistantMessageEventStream(){return new AssistantMessageEventStream}function formatThrownValue(value){return value instanceof Error?value.message||value.name:typeof value=="string"?value:String(value)}function extractDiagnosticError(error){if(!(error instanceof Error))return{name:"ThrownValue",message:formatThrownValue(error)};let code=error.code;return{name:error.name||void 0,message:error.message||error.name,stack:error.stack,code:typeof code=="string"||typeof code=="number"?code:void 0}}function createAssistantMessageDiagnostic(type,error,details){return{type,timestamp:Date.now(),error:extractDiagnosticError(error),details}}function appendAssistantMessageDiagnostic(message,diagnostic){message.diagnostics=[...message.diagnostics??[],diagnostic]}export{EventStream,AssistantMessageEventStream,createAssistantMessageEventStream,formatThrownValue,extractDiagnosticError,createAssistantMessageDiagnostic,appendAssistantMessageDiagnostic};
@@ -1,2 +0,0 @@
1
- import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url);
2
- import{formatProviderError,normalizeProviderError}from"./chunk-TEPMHNKQ.js";import{retryProviderRequest}from"./chunk-LD2P6SFZ.js";import{headersToRecord,providerHeadersToRecord}from"./chunk-UTB4DTYQ.js";import{calculateCost}from"./chunk-KJUDFE6Y.js";function httpError(label,response,body){let error=new Error(`${label} returned ${response.status}`);return error.status=response.status,error.headers=response.headers,error.body=body,error}function timeoutError(timeoutMs){let error=new Error(`Request timed out after ${timeoutMs}ms`);return error.name="TimeoutError",error.status=void 0,error.headers=void 0,error.body="",error}function isRecord(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}function requiredNumber(label,value,field){if(typeof value!="number"||!Number.isFinite(value))throw new Error(`${label} returned an invalid ${field}`);return value}function probabilities(label,value,id){if(!isRecord(value))throw new Error(`${label} returned invalid probabilities for ${id}`);return Object.fromEntries(Object.entries(value).map(([key,probability])=>[key,requiredNumber(label,probability,`probability for ${id}.${key}`)]))}function parseAnswers(label,value,context){if(!isRecord(value))throw new Error(`${label} returned an unexpected response`);let answers=[];for(let[id,question]of Object.entries(context.questions)){let answer=value[id];if(!isRecord(answer))throw new Error(`${label} did not return an answer for ${id}`);if(question.type==="choice"){if(answer.type!=="choice"||typeof answer.choice!="string")throw new Error(`${label} did not return a choice answer for ${id}`);answers.push([id,{type:"choice",choice:answer.choice,probabilities:probabilities(label,answer.probabilities,id),confidence:requiredNumber(label,answer.confidence,`confidence for ${id}`)}])}else if(question.type==="score"){if(answer.type!=="score")throw new Error(`${label} did not return a score answer for ${id}`);answers.push([id,{type:"score",score:requiredNumber(label,answer.score,`score for ${id}`),confidence:requiredNumber(label,answer.confidence,`confidence for ${id}`)}])}else{if(answer.type!=="noul")throw new Error(`${label} did not return a bool answer for ${id}`);answers.push([id,{type:"bool",probability:requiredNumber(label,answer.noul,`probability for ${id}`)}])}}return Object.fromEntries(answers)}function tokenCount(value){return typeof value=="number"&&Number.isFinite(value)&&value>0?value:0}function parseUsage(value,model){if(!isRecord(value)||value.input_tokens===void 0&&value.output_tokens===void 0)return;let input=tokenCount(value.input_tokens),output=tokenCount(value.output_tokens),usage={input,output,cacheRead:0,cacheWrite:0,totalTokens:input+output,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}};return calculateCost(model,usage),usage}function wireRequest(context){return{state:context.state,questions:Object.fromEntries(Object.entries(context.questions).map(([id,question])=>[id,question.type==="bool"?{...question,type:"noul"}:question]))}}function requestHeaders(model,apiKey,optionsHeaders){return providerHeadersToRecord({authorization:`Bearer ${apiKey}`,"content-type":"application/json"},model.headers,optionsHeaders)??{}}async function classifySystemOne(transport,model,context,options){let output={api:model.api,provider:model.provider,model:model.id,answers:{},stopReason:"stop",timestamp:Date.now()};try{if(model.api!==transport.api)throw new Error(`Unsupported classifier API: ${model.api}`);if(!options?.apiKey)throw new Error(`No API key for provider: ${model.provider}`);let apiKey=options.apiKey,payload=transport.payload(model,wireRequest(context)),transformed=await options.onPayload?.(payload,model);transformed!==void 0&&(payload=transformed);let requestFetch=options.fetch??globalThis.fetch,{response,body}=await retryProviderRequest(async()=>{let timeoutSignal=options.timeoutMs!==void 0?AbortSignal.timeout(options.timeoutMs):void 0,signal=options.signal&&timeoutSignal?AbortSignal.any([options.signal,timeoutSignal]):options.signal??timeoutSignal;try{let next=await requestFetch(transport.url(model),{method:"POST",headers:requestHeaders(model,apiKey,options.headers),body:JSON.stringify(payload),signal});if(!next.ok)throw httpError(transport.label,next,await next.text());return{response:next,body:await next.json()}}catch(error){throw timeoutSignal?.aborted&&!options.signal?.aborted?timeoutError(options.timeoutMs):error}},{maxRetries:options.maxRetries??2,maxRetryDelayMs:options.maxRetryDelayMs,signal:options.signal});await options.onResponse?.({status:response.status,headers:headersToRecord(response.headers)},model);let result=transport.output(body),usage=parseUsage(result.usage,model);return usage&&(output.usage=usage),output.answers=parseAnswers(transport.label,result.answers,context),output}catch(error){return output.stopReason=options?.signal?.aborted?"aborted":"error",output.errorMessage=formatProviderError(normalizeProviderError(error),`${transport.label} error`),output}}export{isRecord,classifySystemOne};
@@ -1,2 +0,0 @@
1
- import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url);
2
- import{clampThinkingLevel,getSystemMessageText}from"./chunk-KJUDFE6Y.js";function loadNodeOs(){return typeof process>"u"||!(process.versions?.node||process.versions?.bun)?null:process.getBuiltinModule?.("node:os")??null}var nodeOs=loadNodeOs();function getPiUserAgent(){return nodeOs?`pi (${nodeOs.platform()} ${nodeOs.release()}; ${nodeOs.arch()})`:"pi (browser)"}var UnsupportedStrictJsonSchemaError=class extends Error{},UNSUPPORTED_STRICT_SCHEMA_KEYS=["$ref","$defs","definitions","allOf","oneOf","patternProperties","dependentSchemas","dependencies","unevaluatedProperties","propertyNames","contains","prefixItems","not","if","then","else"];function isJsonSchemaObject(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}function isStructuredSchema(schema){if(!isJsonSchemaObject(schema))return!1;let types=typeof schema.type=="string"?[schema.type]:Array.isArray(schema.type)?schema.type:[];return types.includes("object")||types.includes("array")||schema.properties!==void 0||schema.items!==void 0}function schemaAllowsNull(schema){return isJsonSchemaObject(schema)?schema.type==="null"||Array.isArray(schema.type)&&schema.type.includes("null")||schema.const===null||Array.isArray(schema.enum)&&schema.enum.includes(null)?!0:Array.isArray(schema.anyOf)&&schema.anyOf.some(variant=>schemaAllowsNull(variant)):!1}function makeJsonSchemaNodeStrict(schema,isUnsupportedKeyword){if(!isJsonSchemaObject(schema))throw new UnsupportedStrictJsonSchemaError("boolean schemas are unsupported");for(let key of UNSUPPORTED_STRICT_SCHEMA_KEYS)if(schema[key]!==void 0)throw new UnsupportedStrictJsonSchemaError(`${key} schemas are unsupported`);if(isUnsupportedKeyword){for(let[key,value]of Object.entries(schema))if(isUnsupportedKeyword(key,value))throw new UnsupportedStrictJsonSchemaError(`${key}: ${JSON.stringify(value)} is unsupported`)}if(schema.anyOf!==void 0){if(!Array.isArray(schema.anyOf)||schema.anyOf.length===0)throw new UnsupportedStrictJsonSchemaError("anyOf must contain at least one schema");for(let variant of schema.anyOf){if(isStructuredSchema(variant))throw new UnsupportedStrictJsonSchemaError("object and array unions are unsupported");makeJsonSchemaNodeStrict(variant,isUnsupportedKeyword)}}if(schema.items!==void 0){if(Array.isArray(schema.items))throw new UnsupportedStrictJsonSchemaError("tuple schemas are unsupported");makeJsonSchemaNodeStrict(schema.items,isUnsupportedKeyword)}let isObjectSchema=schema.type==="object";if(schema.properties!==void 0&&!isObjectSchema)throw new UnsupportedStrictJsonSchemaError("properties require type object");if(!isObjectSchema)return;if(schema.additionalProperties!==void 0&&schema.additionalProperties!==!1)throw new UnsupportedStrictJsonSchemaError("schema-valued or true additionalProperties is unsupported");if(schema.properties!==void 0&&!isJsonSchemaObject(schema.properties))throw new UnsupportedStrictJsonSchemaError("object properties must be a schema map");if(schema.required!==void 0&&(!Array.isArray(schema.required)||schema.required.some(key=>typeof key!="string")))throw new UnsupportedStrictJsonSchemaError("object required must be a string array");let properties=schema.properties??{},propertyNames=Object.keys(properties),required=new Set(Array.isArray(schema.required)?schema.required:[]);if([...required].some(key=>!propertyNames.includes(key)))throw new UnsupportedStrictJsonSchemaError("required contains an unknown property");for(let[key,property]of Object.entries(properties))makeJsonSchemaNodeStrict(property,isUnsupportedKeyword),!required.has(key)&&!schemaAllowsNull(property)&&(properties[key]={anyOf:[property,{type:"null"}]});schema.required=propertyNames,schema.additionalProperties=!1}function makeStrictJsonSchema(schema,isUnsupportedKeyword){let cloned=structuredClone(schema);if(!isJsonSchemaObject(cloned))throw new UnsupportedStrictJsonSchemaError("root schema must have type object");if(makeJsonSchemaNodeStrict(cloned,isUnsupportedKeyword),cloned.type!=="object")throw new UnsupportedStrictJsonSchemaError("root schema must have type object");return cloned}function getJsonSchemaToolParameters(tool,strict){return strict===!0?makeStrictJsonSchema(tool.parameters):tool.parameters}function getGrammarToolInput(toolName,arguments_,inputProperty){let input=arguments_[inputProperty];if(typeof input!="string")throw new Error(`Grammar tool call "${toolName}" requires argument "${inputProperty}" to be a string.`);return input}function appendGrammarToolInputJsonDelta(buffer,inputProperty,nextInput,close){if(buffer.closed){if(close&&nextInput===buffer.input)return;throw new Error(`grammar tool input for property "${inputProperty}" changed after it was closed`)}if(!nextInput.startsWith(buffer.input))throw new Error(`grammar tool input for property "${inputProperty}" changed non-monotonically`);let inputDelta=nextInput.slice(buffer.input.length);if(!close&&inputDelta.length===0)return;let delta="";return buffer.started||(delta+=`{${JSON.stringify(inputProperty)}:"`,buffer.started=!0),delta+=JSON.stringify(inputDelta).slice(1,-1),buffer.input=nextInput,close&&(delta+='"}',buffer.closed=!0),delta}function inferGrammarInputProperty(tool){let schema=tool.parameters;if(schema.type!=="object")throw new Error("grammar constrained sampling requires an object parameter schema");if(!Array.isArray(schema.required)||schema.required.length!==1||typeof schema.required[0]!="string")throw new Error("grammar constrained sampling requires exactly one required string property");let inputProperty=schema.required[0];if(!schema.properties?.[inputProperty])throw new Error(`grammar constrained sampling requires a properties entry for ${inputProperty}`);if(schema.properties[inputProperty]?.type!=="string")throw new Error(`grammar constrained sampling property ${inputProperty} must have type string`);return inputProperty}function resolveJsonSchemaStrictSampling(tool,supportsStrictMode,isUnsupportedKeyword){let config=tool.constrainedSampling;if(!(!config||config.type!=="json_schema")){if(supportsStrictMode)try{return makeStrictJsonSchema(tool.parameters,isUnsupportedKeyword),!0}catch(error){if(!(error instanceof UnsupportedStrictJsonSchemaError))throw error;if(config.strict!=="require")return;throw new Error(`Tool "${tool.name}" requires JSON-schema constrained sampling, but ${error.message}.`)}if(config.strict==="require")throw new Error(`Tool "${tool.name}" requires JSON-schema constrained sampling, but strict tools are unsupported.`)}}function resolveGrammarConstrainedSampling(tool,supportsOpenAIGrammarTools){let config=tool.constrainedSampling;if(!config||config.type!=="grammar"||!supportsOpenAIGrammarTools)return;let larkDefinition=config.variants.openai_lark,regexDefinition=config.variants.openai_regex,hasLarkDefinition=typeof larkDefinition=="string"&&larkDefinition.trim().length>0,hasRegexDefinition=typeof regexDefinition=="string"&&regexDefinition.trim().length>0;if(!hasLarkDefinition&&!hasRegexDefinition)throw new Error(`Tool "${tool.name}" cannot use grammar constrained sampling: no supported grammar variant was provided.`);try{return{format:hasLarkDefinition?"lark":"regex",definition:hasLarkDefinition?larkDefinition:regexDefinition,inputProperty:inferGrammarInputProperty(tool)}}catch(error){let message=error instanceof Error?error.message:String(error);throw new Error(`Tool "${tool.name}" cannot use grammar constrained sampling: ${message}.`)}}function createGrammarToolInputProperties(tools,supportsOpenAIGrammarTools){let properties=new Map;for(let tool of tools??[]){let grammar=resolveGrammarConstrainedSampling(tool,supportsOpenAIGrammarTools);grammar&&properties.set(tool.name,grammar.inputProperty)}return properties}var CHARS_PER_TOKEN=4,ESTIMATED_IMAGE_CHARS=4800;function calculateContextTokens(usage){return usage.totalTokens||usage.input+usage.output+usage.cacheRead+usage.cacheWrite}function safeJsonStringify(value){try{return JSON.stringify(value)??"undefined"}catch{return"[unserializable]"}}function estimateTextAndImageContentChars(content){if(typeof content=="string")return content.length;let chars=0;for(let block of content)chars+=block.type==="text"?block.text.length:ESTIMATED_IMAGE_CHARS;return chars}function estimateTextTokens(text){return Math.ceil(text.length/CHARS_PER_TOKEN)}function estimateTextAndImageContentTokens(content){return Math.ceil(estimateTextAndImageContentChars(content)/CHARS_PER_TOKEN)}function estimateMessageTokens(message){let chars=0;if(message.role==="system")return estimateTextTokens(getSystemMessageText(message))+estimateToolsTokens(message.toolsAdded)+estimateToolsTokens(message.toolsRemoved);if(message.role==="user"||message.role==="toolResult")return estimateTextAndImageContentTokens(message.content);for(let block of message.content)block.type==="text"?chars+=block.text.length:block.type==="thinking"?chars+=block.thinking.length:chars+=block.name.length+safeJsonStringify(block.arguments).length;return Math.ceil(chars/CHARS_PER_TOKEN)}function getLastAssistantUsageInfo(messages){let latestPrefixTimestamp=Number.NEGATIVE_INFINITY,usageInfo;for(let i=0;i<messages.length;i++){let message=messages[i];if(message.role==="assistant"){let assistant=message;assistant.timestamp>=latestPrefixTimestamp&&assistant.stopReason!=="aborted"&&assistant.stopReason!=="error"&&calculateContextTokens(assistant.usage)>0&&(usageInfo={usage:assistant.usage,index:i})}latestPrefixTimestamp=Math.max(latestPrefixTimestamp,message.timestamp)}return usageInfo}function estimateContextTokens(context){let messages="messages"in context?context.messages:context,usageInfo=getLastAssistantUsageInfo(messages);if(usageInfo){let usageTokens=calculateContextTokens(usageInfo.usage),trailingTokens=0;for(let i=usageInfo.index+1;i<messages.length;i++)trailingTokens+=estimateMessageTokens(messages[i]);return{tokens:usageTokens+trailingTokens,usageTokens,trailingTokens,lastUsageIndex:usageInfo.index}}let tokens=0;for(let message of messages)tokens+=estimateMessageTokens(message);return{tokens,usageTokens:0,trailingTokens:tokens,lastUsageIndex:null}}function estimateToolsTokens(tools){return!tools||tools.length===0?0:estimateTextTokens(safeJsonStringify(tools))}var CONTEXT_SAFETY_TOKENS=4096,MIN_MAX_TOKENS=1;function clampMaxTokensToContext(model,context,maxTokens){if(model.contextWindow<=0)return Math.max(MIN_MAX_TOKENS,maxTokens);let available=model.contextWindow-estimateContextTokens(context).tokens-CONTEXT_SAFETY_TOKENS;return Math.min(maxTokens,Math.max(MIN_MAX_TOKENS,available))}function resolveSamplingParams(model,thinkingLevel,requestParams){let effectiveThinkingLevel=clampThinkingLevel(model,thinkingLevel),thinkingLevelParams=model.samplingParamsByThinkingLevel?.[effectiveThinkingLevel];return model.samplingParams||thinkingLevelParams||requestParams?{...model.samplingParams,...thinkingLevelParams,...requestParams}:void 0}function buildBaseOptions(model,context,options,apiKey){let samplingParams=resolveSamplingParams(model,options?.reasoning??"off",options?.samplingParams);return{temperature:options?.temperature,samplingParams,maxTokens:clampMaxTokensToContext(model,context,options?.maxTokens??model.maxTokens),signal:options?.signal,telemetryContext:options?.telemetryContext,apiKey:apiKey||options?.apiKey,fetch:options?.fetch,transport:options?.transport,cacheRetention:options?.cacheRetention,sessionId:options?.sessionId,headers:options?.headers,onPayload:options?.onPayload,onResponse:options?.onResponse,onProviderStreamEvent:options?.onProviderStreamEvent,timeoutMs:options?.timeoutMs,websocketConnectTimeoutMs:options?.websocketConnectTimeoutMs,maxRetries:options?.maxRetries,maxRetryDelayMs:options?.maxRetryDelayMs,metadata:options?.metadata,env:options?.env}}var MIN_ANSWER_TOKENS=1024,DEFAULT_THINKING_BUDGETS={minimal:1024,low:2048,medium:8192,high:16384};function clampReasoning(effort){return effort==="xhigh"||effort==="max"?"high":effort}function thinkingBudgetForLevel(reasoningLevel,customBudgets){let budgets={...DEFAULT_THINKING_BUDGETS,...customBudgets},level=clampReasoning(reasoningLevel);return budgets[level]}function clampThinkingBudgetToAnswerRoom(thinkingBudget,ceiling){return Math.min(thinkingBudget,Math.max(0,ceiling-MIN_ANSWER_TOKENS))}function adjustMaxTokensForThinking(baseMaxTokens,modelMaxTokens,reasoningLevel,customBudgets){let thinkingBudget=thinkingBudgetForLevel(reasoningLevel,customBudgets),maxTokens=baseMaxTokens===void 0?modelMaxTokens:Math.min(baseMaxTokens+thinkingBudget,modelMaxTokens);return maxTokens<=thinkingBudget&&(thinkingBudget=clampThinkingBudgetToAnswerRoom(thinkingBudget,maxTokens)),{maxTokens,thinkingBudget}}var NON_VISION_USER_IMAGE_PLACEHOLDER="(image omitted: model does not support images)",NON_VISION_TOOL_IMAGE_PLACEHOLDER="(tool image omitted: model does not support images)";function replaceImagesWithPlaceholder(content,placeholder){let result=[],previousWasPlaceholder=!1;for(let block of content){if(block.type==="image"){previousWasPlaceholder||result.push({type:"text",text:placeholder}),previousWasPlaceholder=!0;continue}result.push(block),previousWasPlaceholder=block.text===placeholder}return result}function downgradeUnsupportedImages(messages,model){return model.input.includes("image")?messages:messages.map(msg=>msg.role==="user"&&Array.isArray(msg.content)?{...msg,content:replaceImagesWithPlaceholder(msg.content,NON_VISION_USER_IMAGE_PLACEHOLDER)}:msg.role==="toolResult"?{...msg,content:replaceImagesWithPlaceholder(msg.content,NON_VISION_TOOL_IMAGE_PLACEHOLDER)}:msg)}function transformMessages(messages,model,normalizeToolCallId){let toolCallIdMap=new Map,normalizedMessages=messages.map(msg=>msg.content==null?{...msg,content:[]}:msg),transformed=downgradeUnsupportedImages(normalizedMessages,model).map(msg=>{if(msg.role==="system"||msg.role==="user")return msg;if(msg.role==="toolResult"){let normalizedId=toolCallIdMap.get(msg.toolCallId);return normalizedId&&normalizedId!==msg.toolCallId?{...msg,toolCallId:normalizedId}:msg}if(msg.role==="assistant"){let assistantMsg=msg,isSameModel=assistantMsg.provider===model.provider&&assistantMsg.api===model.api&&assistantMsg.model===model.id,transformedContent=assistantMsg.content.flatMap(block=>{if(block.type==="thinking")return block.redacted?isSameModel?block:[]:isSameModel&&block.thinkingSignature?block:!block.thinking||block.thinking.trim()===""?[]:isSameModel?block:{type:"text",text:block.thinking};if(block.type==="text")return isSameModel?block:{type:"text",text:block.text};if(block.type==="toolCall"){let toolCall=block,normalizedToolCall=toolCall;if(!isSameModel&&toolCall.thoughtSignature&&(normalizedToolCall={...toolCall},delete normalizedToolCall.thoughtSignature),!isSameModel&&normalizeToolCallId){let normalizedId=normalizeToolCallId(toolCall.id,model,assistantMsg);normalizedId!==toolCall.id&&(toolCallIdMap.set(toolCall.id,normalizedId),normalizedToolCall={...normalizedToolCall,id:normalizedId})}return normalizedToolCall}return block});return{...assistantMsg,content:transformedContent}}return msg}),result=[],pendingToolCalls=[],existingToolResultIds=new Set,heldSystemMessages=[],closePendingToolCalls=()=>{if(pendingToolCalls.length>0){for(let tc of pendingToolCalls)existingToolResultIds.has(tc.id)||result.push({role:"toolResult",toolCallId:tc.id,toolName:tc.name,content:[{type:"text",text:"No result provided"}],isError:!0,timestamp:Date.now()});pendingToolCalls=[],existingToolResultIds=new Set}result.push(...heldSystemMessages),heldSystemMessages.length=0};for(let i=0;i<transformed.length;i++){let msg=transformed[i];if(msg.role==="assistant"){closePendingToolCalls();let assistantMsg=msg;if(assistantMsg.stopReason==="error"||assistantMsg.stopReason==="aborted")continue;let toolCalls=assistantMsg.content.filter(b=>b.type==="toolCall");toolCalls.length>0&&(pendingToolCalls=toolCalls,existingToolResultIds=new Set),result.push(msg)}else msg.role==="toolResult"?(existingToolResultIds.add(msg.toolCallId),result.push(msg)):msg.role==="system"?pendingToolCalls.length>0?heldSystemMessages.push(msg):result.push(msg):(msg.role==="user"&&closePendingToolCalls(),result.push(msg))}return closePendingToolCalls(),result}export{getPiUserAgent,getJsonSchemaToolParameters,getGrammarToolInput,appendGrammarToolInputJsonDelta,resolveJsonSchemaStrictSampling,resolveGrammarConstrainedSampling,createGrammarToolInputProperties,clampMaxTokensToContext,resolveSamplingParams,buildBaseOptions,thinkingBudgetForLevel,clampThinkingBudgetToAnswerRoom,adjustMaxTokensForThinking,transformMessages};