@astralyn/pi 0.82.7 → 0.84.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 (781) hide show
  1. package/CHANGELOG.md +78 -5099
  2. package/README.md +12 -111
  3. package/dist/bun/cli.d.ts.map +1 -1
  4. package/dist/bun/cli.js.map +1 -1
  5. package/dist/bun/register-bedrock.d.ts.map +1 -1
  6. package/dist/bun/restore-sandbox-env.d.ts.map +1 -1
  7. package/dist/bun/restore-sandbox-env.js.map +1 -1
  8. package/dist/cli/args.d.ts +2 -0
  9. package/dist/cli/args.d.ts.map +1 -1
  10. package/dist/cli/args.js +28 -2
  11. package/dist/cli/args.js.map +1 -1
  12. package/dist/cli/config-selector.d.ts.map +1 -1
  13. package/dist/cli/config-selector.js +2 -2
  14. package/dist/cli/config-selector.js.map +1 -1
  15. package/dist/cli/credential-print.d.ts +23 -0
  16. package/dist/cli/credential-print.d.ts.map +1 -0
  17. package/dist/cli/credential-print.js +117 -0
  18. package/dist/cli/credential-print.js.map +1 -0
  19. package/dist/cli/experimental/auth.d.ts +16 -0
  20. package/dist/cli/experimental/auth.d.ts.map +1 -0
  21. package/dist/cli/experimental/auth.js +13 -0
  22. package/dist/cli/experimental/auth.js.map +1 -0
  23. package/dist/cli/experimental/cli.d.ts +6 -0
  24. package/dist/cli/experimental/cli.d.ts.map +1 -0
  25. package/dist/cli/experimental/cli.js +5 -0
  26. package/dist/cli/experimental/cli.js.map +1 -0
  27. package/dist/cli/experimental/command-options.d.ts +17 -0
  28. package/dist/cli/experimental/command-options.d.ts.map +1 -0
  29. package/dist/cli/experimental/command-options.js +35 -0
  30. package/dist/cli/experimental/command-options.js.map +1 -0
  31. package/dist/cli/experimental/command.d.ts +63 -0
  32. package/dist/cli/experimental/command.d.ts.map +1 -0
  33. package/dist/cli/experimental/command.js +127 -0
  34. package/dist/cli/experimental/command.js.map +1 -0
  35. package/dist/cli/experimental/commands/client.d.ts +13 -0
  36. package/dist/cli/experimental/commands/client.d.ts.map +1 -0
  37. package/dist/cli/experimental/commands/client.js +25 -0
  38. package/dist/cli/experimental/commands/client.js.map +1 -0
  39. package/dist/cli/experimental/commands/pi.d.ts +15 -0
  40. package/dist/cli/experimental/commands/pi.d.ts.map +1 -0
  41. package/dist/cli/experimental/commands/pi.js +28 -0
  42. package/dist/cli/experimental/commands/pi.js.map +1 -0
  43. package/dist/cli/experimental/commands/server.d.ts +13 -0
  44. package/dist/cli/experimental/commands/server.d.ts.map +1 -0
  45. package/dist/cli/experimental/commands/server.js +25 -0
  46. package/dist/cli/experimental/commands/server.js.map +1 -0
  47. package/dist/cli/experimental/transport-address.d.ts +10 -0
  48. package/dist/cli/experimental/transport-address.d.ts.map +1 -0
  49. package/dist/cli/experimental/transport-address.js +38 -0
  50. package/dist/cli/experimental/transport-address.js.map +1 -0
  51. package/dist/cli/file-processor.d.ts.map +1 -1
  52. package/dist/cli/file-processor.js.map +1 -1
  53. package/dist/cli/initial-message.d.ts +1 -1
  54. package/dist/cli/initial-message.d.ts.map +1 -1
  55. package/dist/cli/initial-message.js.map +1 -1
  56. package/dist/cli/list-models.d.ts +1 -1
  57. package/dist/cli/list-models.d.ts.map +1 -1
  58. package/dist/cli/list-models.js +2 -2
  59. package/dist/cli/list-models.js.map +1 -1
  60. package/dist/cli/project-trust.d.ts.map +1 -1
  61. package/dist/cli/project-trust.js.map +1 -1
  62. package/dist/cli/session-picker.d.ts.map +1 -1
  63. package/dist/cli/session-picker.js.map +1 -1
  64. package/dist/cli/startup-ui.d.ts +1 -1
  65. package/dist/cli/startup-ui.d.ts.map +1 -1
  66. package/dist/cli/startup-ui.js +2 -2
  67. package/dist/cli/startup-ui.js.map +1 -1
  68. package/dist/cli.d.ts.map +1 -1
  69. package/dist/cli.js +1 -0
  70. package/dist/cli.js.map +1 -1
  71. package/dist/client/index.d.ts +3 -0
  72. package/dist/client/index.d.ts.map +1 -0
  73. package/dist/client/index.js +3 -0
  74. package/dist/client/index.js.map +1 -0
  75. package/dist/client/remote-session.d.ts +53 -0
  76. package/dist/client/remote-session.d.ts.map +1 -0
  77. package/dist/client/remote-session.js +340 -0
  78. package/dist/client/remote-session.js.map +1 -0
  79. package/dist/client/transcript.d.ts +12 -0
  80. package/dist/client/transcript.d.ts.map +1 -0
  81. package/dist/client/transcript.js +98 -0
  82. package/dist/client/transcript.js.map +1 -0
  83. package/dist/config.d.ts.map +1 -1
  84. package/dist/config.js.map +1 -1
  85. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  86. package/dist/core/agent-session-runtime.js +3 -8
  87. package/dist/core/agent-session-runtime.js.map +1 -1
  88. package/dist/core/agent-session-services.d.ts +1 -0
  89. package/dist/core/agent-session-services.d.ts.map +1 -1
  90. package/dist/core/agent-session-services.js +1 -0
  91. package/dist/core/agent-session-services.js.map +1 -1
  92. package/dist/core/agent-session.d.ts +31 -11
  93. package/dist/core/agent-session.d.ts.map +1 -1
  94. package/dist/core/agent-session.js +189 -192
  95. package/dist/core/agent-session.js.map +1 -1
  96. package/dist/core/auth-guidance.d.ts.map +1 -1
  97. package/dist/core/auth-guidance.js.map +1 -1
  98. package/dist/core/auth-storage.d.ts +15 -9
  99. package/dist/core/auth-storage.d.ts.map +1 -1
  100. package/dist/core/auth-storage.js +159 -41
  101. package/dist/core/auth-storage.js.map +1 -1
  102. package/dist/core/bash-executor.d.ts.map +1 -1
  103. package/dist/core/bash-executor.js.map +1 -1
  104. package/dist/core/cache-stats.d.ts.map +1 -1
  105. package/dist/core/cache-stats.js.map +1 -1
  106. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  107. package/dist/core/compaction/branch-summarization.js.map +1 -1
  108. package/dist/core/compaction/compaction.d.ts.map +1 -1
  109. package/dist/core/compaction/compaction.js.map +1 -1
  110. package/dist/core/compaction/index.d.ts.map +1 -1
  111. package/dist/core/compaction/utils.d.ts.map +1 -1
  112. package/dist/core/compaction/utils.js.map +1 -1
  113. package/dist/core/defaults.d.ts.map +1 -1
  114. package/dist/core/diagnostics.d.ts.map +1 -1
  115. package/dist/core/event-bus.d.ts.map +1 -1
  116. package/dist/core/event-bus.js.map +1 -1
  117. package/dist/core/exec.d.ts.map +1 -1
  118. package/dist/core/exec.js.map +1 -1
  119. package/dist/core/experimental.d.ts.map +1 -1
  120. package/dist/core/experimental.js.map +1 -1
  121. package/dist/core/export-html/ansi-to-html.d.ts.map +1 -1
  122. package/dist/core/export-html/ansi-to-html.js.map +1 -1
  123. package/dist/core/export-html/index.d.ts.map +1 -1
  124. package/dist/core/export-html/index.js.map +1 -1
  125. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  126. package/dist/core/export-html/tool-renderer.js.map +1 -1
  127. package/dist/core/extensions/index.d.ts +1 -1
  128. package/dist/core/extensions/index.d.ts.map +1 -1
  129. package/dist/core/extensions/index.js.map +1 -1
  130. package/dist/core/extensions/loader.d.ts.map +1 -1
  131. package/dist/core/extensions/loader.js +43 -20
  132. package/dist/core/extensions/loader.js.map +1 -1
  133. package/dist/core/extensions/runner.d.ts +3 -1
  134. package/dist/core/extensions/runner.d.ts.map +1 -1
  135. package/dist/core/extensions/runner.js +31 -28
  136. package/dist/core/extensions/runner.js.map +1 -1
  137. package/dist/core/extensions/types.d.ts +28 -3
  138. package/dist/core/extensions/types.d.ts.map +1 -1
  139. package/dist/core/extensions/types.js.map +1 -1
  140. package/dist/core/extensions/wrapper.d.ts.map +1 -1
  141. package/dist/core/extensions/wrapper.js.map +1 -1
  142. package/dist/core/footer-data-provider.d.ts +10 -0
  143. package/dist/core/footer-data-provider.d.ts.map +1 -1
  144. package/dist/core/footer-data-provider.js +18 -19
  145. package/dist/core/footer-data-provider.js.map +1 -1
  146. package/dist/core/http-dispatcher.d.ts.map +1 -1
  147. package/dist/core/http-dispatcher.js +5 -0
  148. package/dist/core/http-dispatcher.js.map +1 -1
  149. package/dist/core/index.d.ts.map +1 -1
  150. package/dist/core/keybindings.d.ts +157 -125
  151. package/dist/core/keybindings.d.ts.map +1 -1
  152. package/dist/core/keybindings.js +0 -1
  153. package/dist/core/keybindings.js.map +1 -1
  154. package/dist/core/messages.d.ts.map +1 -1
  155. package/dist/core/messages.js.map +1 -1
  156. package/dist/core/model-config.d.ts +37 -13
  157. package/dist/core/model-config.d.ts.map +1 -1
  158. package/dist/core/model-config.js +4 -2
  159. package/dist/core/model-config.js.map +1 -1
  160. package/dist/core/model-registry.d.ts +5 -3
  161. package/dist/core/model-registry.d.ts.map +1 -1
  162. package/dist/core/model-registry.js +13 -11
  163. package/dist/core/model-registry.js.map +1 -1
  164. package/dist/core/model-resolver.d.ts +4 -3
  165. package/dist/core/model-resolver.d.ts.map +1 -1
  166. package/dist/core/model-resolver.js +41 -10
  167. package/dist/core/model-resolver.js.map +1 -1
  168. package/dist/core/model-runtime.d.ts +30 -13
  169. package/dist/core/model-runtime.d.ts.map +1 -1
  170. package/dist/core/model-runtime.js +239 -91
  171. package/dist/core/model-runtime.js.map +1 -1
  172. package/dist/core/models-store.d.ts +12 -7
  173. package/dist/core/models-store.d.ts.map +1 -1
  174. package/dist/core/models-store.js +84 -16
  175. package/dist/core/models-store.js.map +1 -1
  176. package/dist/core/output-guard.d.ts.map +1 -1
  177. package/dist/core/output-guard.js.map +1 -1
  178. package/dist/core/package-manager.d.ts +4 -1
  179. package/dist/core/package-manager.d.ts.map +1 -1
  180. package/dist/core/package-manager.js +76 -48
  181. package/dist/core/package-manager.js.map +1 -1
  182. package/dist/core/pi-manifest.d.ts +8 -0
  183. package/dist/core/pi-manifest.d.ts.map +1 -0
  184. package/dist/core/pi-manifest.js +25 -0
  185. package/dist/core/pi-manifest.js.map +1 -0
  186. package/dist/core/project-trust.d.ts.map +1 -1
  187. package/dist/core/project-trust.js.map +1 -1
  188. package/dist/core/prompt-templates.d.ts.map +1 -1
  189. package/dist/core/prompt-templates.js.map +1 -1
  190. package/dist/core/provider-attribution.d.ts.map +1 -1
  191. package/dist/core/provider-attribution.js.map +1 -1
  192. package/dist/core/provider-composer.d.ts +4 -1
  193. package/dist/core/provider-composer.d.ts.map +1 -1
  194. package/dist/core/provider-composer.js +36 -15
  195. package/dist/core/provider-composer.js.map +1 -1
  196. package/dist/core/radius.d.ts.map +1 -1
  197. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  198. package/dist/core/remote-catalog-provider.js +73 -67
  199. package/dist/core/remote-catalog-provider.js.map +1 -1
  200. package/dist/core/resolve-config-value.d.ts.map +1 -1
  201. package/dist/core/resolve-config-value.js.map +1 -1
  202. package/dist/core/resource-loader.d.ts +15 -0
  203. package/dist/core/resource-loader.d.ts.map +1 -1
  204. package/dist/core/resource-loader.js +68 -51
  205. package/dist/core/resource-loader.js.map +1 -1
  206. package/dist/core/runtime-credentials.d.ts +5 -5
  207. package/dist/core/runtime-credentials.d.ts.map +1 -1
  208. package/dist/core/runtime-credentials.js +12 -10
  209. package/dist/core/runtime-credentials.js.map +1 -1
  210. package/dist/core/sdk.d.ts.map +1 -1
  211. package/dist/core/sdk.js.map +1 -1
  212. package/dist/core/session-cwd.d.ts.map +1 -1
  213. package/dist/core/session-cwd.js +0 -1
  214. package/dist/core/session-cwd.js.map +1 -1
  215. package/dist/core/session-manager.d.ts.map +1 -1
  216. package/dist/core/session-manager.js +10 -12
  217. package/dist/core/session-manager.js.map +1 -1
  218. package/dist/core/settings-manager.d.ts +12 -0
  219. package/dist/core/settings-manager.d.ts.map +1 -1
  220. package/dist/core/settings-manager.js +47 -33
  221. package/dist/core/settings-manager.js.map +1 -1
  222. package/dist/core/skills.d.ts.map +1 -1
  223. package/dist/core/skills.js.map +1 -1
  224. package/dist/core/slash-commands.d.ts.map +1 -1
  225. package/dist/core/source-info.d.ts.map +1 -1
  226. package/dist/core/source-info.js.map +1 -1
  227. package/dist/core/system-prompt.d.ts.map +1 -1
  228. package/dist/core/system-prompt.js.map +1 -1
  229. package/dist/core/telemetry.d.ts.map +1 -1
  230. package/dist/core/telemetry.js.map +1 -1
  231. package/dist/core/timings.d.ts.map +1 -1
  232. package/dist/core/timings.js.map +1 -1
  233. package/dist/core/tools/bash.d.ts.map +1 -1
  234. package/dist/core/tools/bash.js +12 -10
  235. package/dist/core/tools/bash.js.map +1 -1
  236. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  237. package/dist/core/tools/edit-diff.js.map +1 -1
  238. package/dist/core/tools/edit.d.ts.map +1 -1
  239. package/dist/core/tools/edit.js +4 -3
  240. package/dist/core/tools/edit.js.map +1 -1
  241. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
  242. package/dist/core/tools/file-mutation-queue.js.map +1 -1
  243. package/dist/core/tools/find.d.ts +3 -0
  244. package/dist/core/tools/find.d.ts.map +1 -1
  245. package/dist/core/tools/find.js +47 -20
  246. package/dist/core/tools/find.js.map +1 -1
  247. package/dist/core/tools/grep.d.ts.map +1 -1
  248. package/dist/core/tools/grep.js.map +1 -1
  249. package/dist/core/tools/index.d.ts.map +1 -1
  250. package/dist/core/tools/index.js.map +1 -1
  251. package/dist/core/tools/ls.d.ts.map +1 -1
  252. package/dist/core/tools/ls.js.map +1 -1
  253. package/dist/core/tools/output-accumulator.d.ts.map +1 -1
  254. package/dist/core/tools/output-accumulator.js +12 -18
  255. package/dist/core/tools/output-accumulator.js.map +1 -1
  256. package/dist/core/tools/path-utils.d.ts.map +1 -1
  257. package/dist/core/tools/path-utils.js.map +1 -1
  258. package/dist/core/tools/read.d.ts.map +1 -1
  259. package/dist/core/tools/read.js +1 -1
  260. package/dist/core/tools/read.js.map +1 -1
  261. package/dist/core/tools/render-utils.d.ts.map +1 -1
  262. package/dist/core/tools/render-utils.js.map +1 -1
  263. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
  264. package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
  265. package/dist/core/tools/truncate.d.ts.map +1 -1
  266. package/dist/core/tools/truncate.js.map +1 -1
  267. package/dist/core/tools/write.d.ts.map +1 -1
  268. package/dist/core/tools/write.js +0 -1
  269. package/dist/core/tools/write.js.map +1 -1
  270. package/dist/core/trust-manager.d.ts.map +1 -1
  271. package/dist/core/trust-manager.js +48 -10
  272. package/dist/core/trust-manager.js.map +1 -1
  273. package/dist/core/usage-totals.d.ts.map +1 -1
  274. package/dist/core/usage-totals.js.map +1 -1
  275. package/dist/extensions/biu/index.d.ts +19 -0
  276. package/dist/extensions/biu/index.d.ts.map +1 -0
  277. package/dist/extensions/biu/index.js +350 -0
  278. package/dist/extensions/biu/index.js.map +1 -0
  279. package/dist/extensions/biu/prompts.d.ts +26 -0
  280. package/dist/extensions/biu/prompts.d.ts.map +1 -0
  281. package/dist/extensions/biu/prompts.js +260 -0
  282. package/dist/extensions/biu/prompts.js.map +1 -0
  283. package/dist/extensions/biu/workspace.d.ts +122 -0
  284. package/dist/extensions/biu/workspace.d.ts.map +1 -0
  285. package/dist/extensions/biu/workspace.js +404 -0
  286. package/dist/extensions/biu/workspace.js.map +1 -0
  287. package/dist/extensions/deepwiki/client.d.ts.map +1 -1
  288. package/dist/extensions/deepwiki/client.js.map +1 -1
  289. package/dist/extensions/deepwiki/constants.d.ts.map +1 -1
  290. package/dist/extensions/deepwiki/constants.js.map +1 -1
  291. package/dist/extensions/deepwiki/contents.d.ts.map +1 -1
  292. package/dist/extensions/deepwiki/contents.js.map +1 -1
  293. package/dist/extensions/deepwiki/execute.d.ts.map +1 -1
  294. package/dist/extensions/deepwiki/execute.js.map +1 -1
  295. package/dist/extensions/deepwiki/http.d.ts.map +1 -1
  296. package/dist/extensions/deepwiki/http.js.map +1 -1
  297. package/dist/extensions/deepwiki/index.d.ts.map +1 -1
  298. package/dist/extensions/deepwiki/index.js.map +1 -1
  299. package/dist/extensions/deepwiki/render.d.ts.map +1 -1
  300. package/dist/extensions/deepwiki/render.js.map +1 -1
  301. package/dist/extensions/deepwiki/schema.d.ts +1 -1
  302. package/dist/extensions/deepwiki/schema.d.ts.map +1 -1
  303. package/dist/extensions/deepwiki/schema.js.map +1 -1
  304. package/dist/extensions/index.d.ts.map +1 -1
  305. package/dist/extensions/index.js +2 -2
  306. package/dist/extensions/index.js.map +1 -1
  307. package/dist/extensions/llama/client.d.ts.map +1 -1
  308. package/dist/extensions/llama/client.js +0 -2
  309. package/dist/extensions/llama/client.js.map +1 -1
  310. package/dist/extensions/llama/huggingface.d.ts.map +1 -1
  311. package/dist/extensions/llama/huggingface.js +0 -2
  312. package/dist/extensions/llama/huggingface.js.map +1 -1
  313. package/dist/extensions/llama/index.d.ts.map +1 -1
  314. package/dist/extensions/llama/index.js +11 -2
  315. package/dist/extensions/llama/index.js.map +1 -1
  316. package/dist/extensions/llama/provider.d.ts.map +1 -1
  317. package/dist/extensions/llama/provider.js +22 -8
  318. package/dist/extensions/llama/provider.js.map +1 -1
  319. package/dist/extensions/llama/ui.d.ts.map +1 -1
  320. package/dist/extensions/llama/ui.js +12 -28
  321. package/dist/extensions/llama/ui.js.map +1 -1
  322. package/dist/extensions/question/constants.d.ts.map +1 -1
  323. package/dist/extensions/question/constants.js.map +1 -1
  324. package/dist/extensions/question/dialog-primitives.d.ts.map +1 -1
  325. package/dist/extensions/question/dialog-primitives.js.map +1 -1
  326. package/dist/extensions/question/dialog.d.ts.map +1 -1
  327. package/dist/extensions/question/dialog.js.map +1 -1
  328. package/dist/extensions/question/index.d.ts.map +1 -1
  329. package/dist/extensions/question/index.js.map +1 -1
  330. package/dist/extensions/question/limits.d.ts.map +1 -1
  331. package/dist/extensions/question/render-cache.d.ts.map +1 -1
  332. package/dist/extensions/question/render-cache.js +3 -4
  333. package/dist/extensions/question/render-cache.js.map +1 -1
  334. package/dist/extensions/question/render.d.ts.map +1 -1
  335. package/dist/extensions/question/render.js.map +1 -1
  336. package/dist/extensions/question/results.d.ts.map +1 -1
  337. package/dist/extensions/question/results.js.map +1 -1
  338. package/dist/extensions/question/schema.d.ts.map +1 -1
  339. package/dist/extensions/question/schema.js.map +1 -1
  340. package/dist/extensions/question/state.d.ts.map +1 -1
  341. package/dist/extensions/question/state.js.map +1 -1
  342. package/dist/extensions/question/types.d.ts.map +1 -1
  343. package/dist/extensions/rewind/anchor.d.ts.map +1 -1
  344. package/dist/extensions/rewind/anchor.js.map +1 -1
  345. package/dist/extensions/rewind/config.d.ts.map +1 -1
  346. package/dist/extensions/rewind/config.js.map +1 -1
  347. package/dist/extensions/rewind/engine.d.ts.map +1 -1
  348. package/dist/extensions/rewind/engine.js.map +1 -1
  349. package/dist/extensions/rewind/gc.d.ts.map +1 -1
  350. package/dist/extensions/rewind/gc.js.map +1 -1
  351. package/dist/extensions/rewind/index.d.ts.map +1 -1
  352. package/dist/extensions/rewind/index.js.map +1 -1
  353. package/dist/extensions/rewind/menu.d.ts.map +1 -1
  354. package/dist/extensions/rewind/menu.js.map +1 -1
  355. package/dist/extensions/rewind/paths.d.ts.map +1 -1
  356. package/dist/extensions/rewind/paths.js.map +1 -1
  357. package/dist/extensions/rewind/restore.d.ts.map +1 -1
  358. package/dist/extensions/rewind/restore.js.map +1 -1
  359. package/dist/extensions/rewind/snapshot.d.ts.map +1 -1
  360. package/dist/extensions/rewind/snapshot.js.map +1 -1
  361. package/dist/extensions/rewind/storage.d.ts.map +1 -1
  362. package/dist/extensions/rewind/storage.js.map +1 -1
  363. package/dist/extensions/rewind/text.d.ts.map +1 -1
  364. package/dist/extensions/rewind/text.js.map +1 -1
  365. package/dist/extensions/rewind/tool-path.d.ts.map +1 -1
  366. package/dist/extensions/rewind/tool-path.js.map +1 -1
  367. package/dist/extensions/router/constants.d.ts +3 -0
  368. package/dist/extensions/router/constants.d.ts.map +1 -1
  369. package/dist/extensions/router/constants.js +3 -0
  370. package/dist/extensions/router/constants.js.map +1 -1
  371. package/dist/extensions/router/dialog.d.ts +14 -7
  372. package/dist/extensions/router/dialog.d.ts.map +1 -1
  373. package/dist/extensions/router/dialog.js +70 -132
  374. package/dist/extensions/router/dialog.js.map +1 -1
  375. package/dist/extensions/router/index.d.ts.map +1 -1
  376. package/dist/extensions/router/index.js.map +1 -1
  377. package/dist/extensions/router/presets.d.ts +8 -3
  378. package/dist/extensions/router/presets.d.ts.map +1 -1
  379. package/dist/extensions/router/presets.js +33 -17
  380. package/dist/extensions/router/presets.js.map +1 -1
  381. package/dist/extensions/router/probe.d.ts.map +1 -1
  382. package/dist/extensions/router/probe.js.map +1 -1
  383. package/dist/extensions/router/register.d.ts +3 -3
  384. package/dist/extensions/router/register.d.ts.map +1 -1
  385. package/dist/extensions/router/register.js.map +1 -1
  386. package/dist/extensions/router/store.d.ts.map +1 -1
  387. package/dist/extensions/router/store.js.map +1 -1
  388. package/dist/extensions/router/stream.d.ts.map +1 -1
  389. package/dist/extensions/router/stream.js.map +1 -1
  390. package/dist/extensions/router/types.d.ts.map +1 -1
  391. package/dist/extensions/router/ui.d.ts +11 -2
  392. package/dist/extensions/router/ui.d.ts.map +1 -1
  393. package/dist/extensions/router/ui.js +316 -171
  394. package/dist/extensions/router/ui.js.map +1 -1
  395. package/dist/extensions/statusline/index.d.ts.map +1 -1
  396. package/dist/extensions/statusline/index.js +17 -9
  397. package/dist/extensions/statusline/index.js.map +1 -1
  398. package/dist/extensions/subagent/activity.d.ts.map +1 -1
  399. package/dist/extensions/subagent/activity.js.map +1 -1
  400. package/dist/extensions/subagent/agents.d.ts.map +1 -1
  401. package/dist/extensions/subagent/agents.js.map +1 -1
  402. package/dist/extensions/subagent/constants.d.ts.map +1 -1
  403. package/dist/extensions/subagent/index.d.ts +3 -0
  404. package/dist/extensions/subagent/index.d.ts.map +1 -1
  405. package/dist/extensions/subagent/index.js +83 -12
  406. package/dist/extensions/subagent/index.js.map +1 -1
  407. package/dist/extensions/subagent/picker.d.ts.map +1 -1
  408. package/dist/extensions/subagent/picker.js +2 -9
  409. package/dist/extensions/subagent/picker.js.map +1 -1
  410. package/dist/extensions/subagent/render.d.ts.map +1 -1
  411. package/dist/extensions/subagent/render.js +33 -9
  412. package/dist/extensions/subagent/render.js.map +1 -1
  413. package/dist/extensions/subagent/resolve.d.ts.map +1 -1
  414. package/dist/extensions/subagent/resolve.js.map +1 -1
  415. package/dist/extensions/subagent/retry.d.ts.map +1 -1
  416. package/dist/extensions/subagent/retry.js.map +1 -1
  417. package/dist/extensions/subagent/runner.d.ts +2 -1
  418. package/dist/extensions/subagent/runner.d.ts.map +1 -1
  419. package/dist/extensions/subagent/runner.js +10 -12
  420. package/dist/extensions/subagent/runner.js.map +1 -1
  421. package/dist/extensions/subagent/schema.d.ts.map +1 -1
  422. package/dist/extensions/subagent/schema.js.map +1 -1
  423. package/dist/extensions/subagent/sdk-runner.d.ts.map +1 -1
  424. package/dist/extensions/subagent/sdk-runner.js +41 -1
  425. package/dist/extensions/subagent/sdk-runner.js.map +1 -1
  426. package/dist/extensions/subagent/settings.d.ts.map +1 -1
  427. package/dist/extensions/subagent/settings.js +46 -36
  428. package/dist/extensions/subagent/settings.js.map +1 -1
  429. package/dist/extensions/subagent/types.d.ts +0 -1
  430. package/dist/extensions/subagent/types.d.ts.map +1 -1
  431. package/dist/extensions/subagent/types.js.map +1 -1
  432. package/dist/extensions/todo/constants.d.ts.map +1 -1
  433. package/dist/extensions/todo/constants.js.map +1 -1
  434. package/dist/extensions/todo/index.d.ts.map +1 -1
  435. package/dist/extensions/todo/index.js.map +1 -1
  436. package/dist/extensions/todo/overlay.d.ts.map +1 -1
  437. package/dist/extensions/todo/overlay.js +6 -10
  438. package/dist/extensions/todo/overlay.js.map +1 -1
  439. package/dist/extensions/todo/schema.d.ts +2 -2
  440. package/dist/extensions/todo/schema.d.ts.map +1 -1
  441. package/dist/extensions/todo/state.d.ts.map +1 -1
  442. package/dist/extensions/todo/state.js.map +1 -1
  443. package/dist/extensions/todo/view.d.ts.map +1 -1
  444. package/dist/extensions/todo/view.js.map +1 -1
  445. package/dist/index.d.ts +4 -4
  446. package/dist/index.d.ts.map +1 -1
  447. package/dist/index.js +1 -1
  448. package/dist/index.js.map +1 -1
  449. package/dist/main.d.ts.map +1 -1
  450. package/dist/main.js +57 -5
  451. package/dist/main.js.map +1 -1
  452. package/dist/migrations.d.ts.map +1 -1
  453. package/dist/migrations.js.map +1 -1
  454. package/dist/modes/index.d.ts +1 -0
  455. package/dist/modes/index.d.ts.map +1 -1
  456. package/dist/modes/index.js.map +1 -1
  457. package/dist/modes/interactive/components/armin.d.ts.map +1 -1
  458. package/dist/modes/interactive/components/armin.js +6 -10
  459. package/dist/modes/interactive/components/armin.js.map +1 -1
  460. package/dist/modes/interactive/components/assistant-message.d.ts +5 -2
  461. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  462. package/dist/modes/interactive/components/assistant-message.js +13 -11
  463. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  464. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  465. package/dist/modes/interactive/components/bash-execution.js +4 -10
  466. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  467. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
  468. package/dist/modes/interactive/components/bordered-loader.js +0 -3
  469. package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
  470. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  471. package/dist/modes/interactive/components/branch-summary-message.js +1 -3
  472. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  473. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  474. package/dist/modes/interactive/components/compaction-summary-message.js +1 -3
  475. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  476. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  477. package/dist/modes/interactive/components/config-selector.js +5 -22
  478. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  479. package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -1
  480. package/dist/modes/interactive/components/countdown-timer.js +1 -7
  481. package/dist/modes/interactive/components/countdown-timer.js.map +1 -1
  482. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  483. package/dist/modes/interactive/components/custom-editor.js +8 -8
  484. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  485. package/dist/modes/interactive/components/custom-entry.d.ts.map +1 -1
  486. package/dist/modes/interactive/components/custom-entry.js +1 -4
  487. package/dist/modes/interactive/components/custom-entry.js.map +1 -1
  488. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
  489. package/dist/modes/interactive/components/custom-message.js +1 -7
  490. package/dist/modes/interactive/components/custom-message.js.map +1 -1
  491. package/dist/modes/interactive/components/daxnuts.d.ts.map +1 -1
  492. package/dist/modes/interactive/components/daxnuts.js +6 -8
  493. package/dist/modes/interactive/components/daxnuts.js.map +1 -1
  494. package/dist/modes/interactive/components/diff.d.ts.map +1 -1
  495. package/dist/modes/interactive/components/diff.js.map +1 -1
  496. package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  497. package/dist/modes/interactive/components/dynamic-border.js +0 -1
  498. package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  499. package/dist/modes/interactive/components/earendil-announcement.d.ts.map +1 -1
  500. package/dist/modes/interactive/components/earendil-announcement.js.map +1 -1
  501. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  502. package/dist/modes/interactive/components/extension-editor.js +1 -7
  503. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  504. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  505. package/dist/modes/interactive/components/extension-input.js +2 -8
  506. package/dist/modes/interactive/components/extension-input.js.map +1 -1
  507. package/dist/modes/interactive/components/extension-selector.d.ts +1 -1
  508. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  509. package/dist/modes/interactive/components/extension-selector.js +1 -9
  510. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  511. package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
  512. package/dist/modes/interactive/components/first-time-setup.js +2 -4
  513. package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
  514. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  515. package/dist/modes/interactive/components/footer.js +2 -4
  516. package/dist/modes/interactive/components/footer.js.map +1 -1
  517. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  518. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  519. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
  520. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  521. package/dist/modes/interactive/components/login-dialog.js +3 -9
  522. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  523. package/dist/modes/interactive/components/markdown-transform.d.ts +3 -0
  524. package/dist/modes/interactive/components/markdown-transform.d.ts.map +1 -0
  525. package/dist/modes/interactive/components/markdown-transform.js +19 -0
  526. package/dist/modes/interactive/components/markdown-transform.js.map +1 -0
  527. package/dist/modes/interactive/components/mermaid.d.ts +11 -0
  528. package/dist/modes/interactive/components/mermaid.d.ts.map +1 -0
  529. package/dist/modes/interactive/components/mermaid.js +75 -0
  530. package/dist/modes/interactive/components/mermaid.js.map +1 -0
  531. package/dist/modes/interactive/components/model-selector.d.ts +1 -1
  532. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  533. package/dist/modes/interactive/components/model-selector.js +32 -30
  534. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  535. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  536. package/dist/modes/interactive/components/oauth-selector.js +3 -11
  537. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  538. package/dist/modes/interactive/components/scoped-models-selector.d.ts +4 -0
  539. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  540. package/dist/modes/interactive/components/scoped-models-selector.js +34 -13
  541. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  542. package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
  543. package/dist/modes/interactive/components/session-selector-search.js.map +1 -1
  544. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  545. package/dist/modes/interactive/components/session-selector.js +35 -60
  546. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  547. package/dist/modes/interactive/components/settings-selector.d.ts +8 -2
  548. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  549. package/dist/modes/interactive/components/settings-selector.js +30 -14
  550. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  551. package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
  552. package/dist/modes/interactive/components/show-images-selector.js +0 -1
  553. package/dist/modes/interactive/components/show-images-selector.js.map +1 -1
  554. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  555. package/dist/modes/interactive/components/skill-invocation-message.js +1 -3
  556. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  557. package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -1
  558. package/dist/modes/interactive/components/status-indicator.js +0 -2
  559. package/dist/modes/interactive/components/status-indicator.js.map +1 -1
  560. package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
  561. package/dist/modes/interactive/components/theme-selector.js +0 -2
  562. package/dist/modes/interactive/components/theme-selector.js.map +1 -1
  563. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  564. package/dist/modes/interactive/components/thinking-selector.js +0 -1
  565. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  566. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  567. package/dist/modes/interactive/components/tool-execution.js +18 -36
  568. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  569. package/dist/modes/interactive/components/tool-group.d.ts.map +1 -1
  570. package/dist/modes/interactive/components/tool-group.js +2 -3
  571. package/dist/modes/interactive/components/tool-group.js.map +1 -1
  572. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  573. package/dist/modes/interactive/components/tree-selector.js +19 -39
  574. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  575. package/dist/modes/interactive/components/trust-selector.d.ts.map +1 -1
  576. package/dist/modes/interactive/components/trust-selector.js +0 -6
  577. package/dist/modes/interactive/components/trust-selector.js.map +1 -1
  578. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
  579. package/dist/modes/interactive/components/user-message-selector.js +3 -6
  580. package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
  581. package/dist/modes/interactive/components/user-message.d.ts +3 -1
  582. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  583. package/dist/modes/interactive/components/user-message.js +8 -5
  584. package/dist/modes/interactive/components/user-message.js.map +1 -1
  585. package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -1
  586. package/dist/modes/interactive/components/visual-truncate.js.map +1 -1
  587. package/dist/modes/interactive/external-editor.d.ts.map +1 -1
  588. package/dist/modes/interactive/external-editor.js +40 -3
  589. package/dist/modes/interactive/external-editor.js.map +1 -1
  590. package/dist/modes/interactive/interactive-mode.d.ts +46 -4
  591. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  592. package/dist/modes/interactive/interactive-mode.js +538 -275
  593. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  594. package/dist/modes/interactive/model-search.d.ts.map +1 -1
  595. package/dist/modes/interactive/model-search.js.map +1 -1
  596. package/dist/modes/interactive/theme/dark.json +1 -0
  597. package/dist/modes/interactive/theme/light.json +1 -0
  598. package/dist/modes/interactive/theme/theme-controller.d.ts +3 -0
  599. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
  600. package/dist/modes/interactive/theme/theme-controller.js +11 -8
  601. package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
  602. package/dist/modes/interactive/theme/theme-schema.json +5 -1
  603. package/dist/modes/interactive/theme/theme.d.ts +4 -4
  604. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  605. package/dist/modes/interactive/theme/theme.js +13 -9
  606. package/dist/modes/interactive/theme/theme.js.map +1 -1
  607. package/dist/modes/json-event.d.ts +28 -0
  608. package/dist/modes/json-event.d.ts.map +1 -0
  609. package/dist/modes/json-event.js +12 -0
  610. package/dist/modes/json-event.js.map +1 -0
  611. package/dist/modes/print-mode.d.ts.map +1 -1
  612. package/dist/modes/print-mode.js +12 -2
  613. package/dist/modes/print-mode.js.map +1 -1
  614. package/dist/modes/rpc/jsonl.d.ts.map +1 -1
  615. package/dist/modes/rpc/jsonl.js.map +1 -1
  616. package/dist/modes/rpc/rpc-client.d.ts +5 -4
  617. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  618. package/dist/modes/rpc/rpc-client.js +7 -8
  619. package/dist/modes/rpc/rpc-client.js.map +1 -1
  620. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  621. package/dist/modes/rpc/rpc-mode.js +17 -3
  622. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  623. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  624. package/dist/package-manager-cli.d.ts.map +1 -1
  625. package/dist/package-manager-cli.js +11 -9
  626. package/dist/package-manager-cli.js.map +1 -1
  627. package/dist/rpc-entry.d.ts.map +1 -1
  628. package/dist/rpc-entry.js +1 -0
  629. package/dist/rpc-entry.js.map +1 -1
  630. package/dist/utils/abort.d.ts +5 -0
  631. package/dist/utils/abort.d.ts.map +1 -0
  632. package/dist/utils/abort.js +48 -0
  633. package/dist/utils/abort.js.map +1 -0
  634. package/dist/utils/ansi.d.ts.map +1 -1
  635. package/dist/utils/ansi.js.map +1 -1
  636. package/dist/utils/changelog.d.ts.map +1 -1
  637. package/dist/utils/changelog.js.map +1 -1
  638. package/dist/utils/child-process.d.ts.map +1 -1
  639. package/dist/utils/child-process.js.map +1 -1
  640. package/dist/utils/clipboard-image.d.ts.map +1 -1
  641. package/dist/utils/clipboard-image.js.map +1 -1
  642. package/dist/utils/clipboard-native.d.ts.map +1 -1
  643. package/dist/utils/clipboard-native.js.map +1 -1
  644. package/dist/utils/clipboard.d.ts +1 -1
  645. package/dist/utils/clipboard.d.ts.map +1 -1
  646. package/dist/utils/clipboard.js +22 -2
  647. package/dist/utils/clipboard.js.map +1 -1
  648. package/dist/utils/coalescing-terminal.d.ts +1 -0
  649. package/dist/utils/coalescing-terminal.d.ts.map +1 -1
  650. package/dist/utils/coalescing-terminal.js +16 -8
  651. package/dist/utils/coalescing-terminal.js.map +1 -1
  652. package/dist/utils/deprecation.d.ts.map +1 -1
  653. package/dist/utils/deprecation.js.map +1 -1
  654. package/dist/utils/exif-orientation.d.ts.map +1 -1
  655. package/dist/utils/exif-orientation.js.map +1 -1
  656. package/dist/utils/frontmatter.d.ts.map +1 -1
  657. package/dist/utils/frontmatter.js.map +1 -1
  658. package/dist/utils/fs-watch.d.ts.map +1 -1
  659. package/dist/utils/fs-watch.js.map +1 -1
  660. package/dist/utils/git.d.ts.map +1 -1
  661. package/dist/utils/git.js.map +1 -1
  662. package/dist/utils/html.d.ts.map +1 -1
  663. package/dist/utils/html.js.map +1 -1
  664. package/dist/utils/image-convert.d.ts.map +1 -1
  665. package/dist/utils/image-convert.js.map +1 -1
  666. package/dist/utils/image-process.d.ts.map +1 -1
  667. package/dist/utils/image-process.js.map +1 -1
  668. package/dist/utils/image-resize-core.d.ts.map +1 -1
  669. package/dist/utils/image-resize-core.js.map +1 -1
  670. package/dist/utils/image-resize-worker.d.ts.map +1 -1
  671. package/dist/utils/image-resize-worker.js.map +1 -1
  672. package/dist/utils/image-resize.d.ts.map +1 -1
  673. package/dist/utils/image-resize.js.map +1 -1
  674. package/dist/utils/json.d.ts.map +1 -1
  675. package/dist/utils/json.js.map +1 -1
  676. package/dist/utils/management-http.d.ts +23 -0
  677. package/dist/utils/management-http.d.ts.map +1 -0
  678. package/dist/utils/management-http.js +50 -0
  679. package/dist/utils/management-http.js.map +1 -0
  680. package/dist/utils/mime.d.ts.map +1 -1
  681. package/dist/utils/mime.js.map +1 -1
  682. package/dist/utils/open-browser.d.ts.map +1 -1
  683. package/dist/utils/open-browser.js.map +1 -1
  684. package/dist/utils/paths.d.ts +5 -3
  685. package/dist/utils/paths.d.ts.map +1 -1
  686. package/dist/utils/paths.js +25 -4
  687. package/dist/utils/paths.js.map +1 -1
  688. package/dist/utils/photon.d.ts.map +1 -1
  689. package/dist/utils/photon.js.map +1 -1
  690. package/dist/utils/pi-user-agent.d.ts.map +1 -1
  691. package/dist/utils/pi-user-agent.js.map +1 -1
  692. package/dist/utils/shell.d.ts.map +1 -1
  693. package/dist/utils/shell.js.map +1 -1
  694. package/dist/utils/sleep.d.ts.map +1 -1
  695. package/dist/utils/sleep.js.map +1 -1
  696. package/dist/utils/syntax-highlight.d.ts.map +1 -1
  697. package/dist/utils/syntax-highlight.js.map +1 -1
  698. package/dist/utils/tool-result-images.d.ts +19 -0
  699. package/dist/utils/tool-result-images.d.ts.map +1 -0
  700. package/dist/utils/tool-result-images.js +45 -0
  701. package/dist/utils/tool-result-images.js.map +1 -0
  702. package/dist/utils/tools-manager.d.ts.map +1 -1
  703. package/dist/utils/tools-manager.js +4 -6
  704. package/dist/utils/tools-manager.js.map +1 -1
  705. package/dist/utils/version-check.d.ts +4 -0
  706. package/dist/utils/version-check.d.ts.map +1 -1
  707. package/dist/utils/version-check.js +20 -2
  708. package/dist/utils/version-check.js.map +1 -1
  709. package/dist/utils/windows-self-update.d.ts.map +1 -1
  710. package/dist/utils/windows-self-update.js.map +1 -1
  711. package/docs/bundled/README.md +14 -16
  712. package/docs/bundled/extensions/biu.md +63 -0
  713. package/docs/bundled/extensions/router.md +20 -14
  714. package/docs/bundled/extensions/statusline.md +7 -5
  715. package/docs/bundled/themes.md +1 -1
  716. package/docs/compaction.md +25 -30
  717. package/docs/custom-provider.md +303 -467
  718. package/docs/development.md +83 -0
  719. package/docs/docs.json +6 -10
  720. package/docs/extensions.md +139 -119
  721. package/docs/index.md +18 -74
  722. package/docs/json.md +40 -15
  723. package/docs/keybindings.md +38 -10
  724. package/docs/models.md +210 -150
  725. package/docs/providers.md +3 -1
  726. package/docs/quickstart.md +5 -3
  727. package/docs/rpc.md +177 -134
  728. package/docs/sdk.md +165 -178
  729. package/docs/security.md +2 -2
  730. package/docs/session-format.md +139 -119
  731. package/docs/settings.md +4 -1
  732. package/docs/skills.md +0 -1
  733. package/docs/themes.md +5 -3
  734. package/docs/tui.md +411 -549
  735. package/docs/usage.md +11 -11
  736. package/examples/README.md +6 -1
  737. package/examples/extensions/README.md +13 -33
  738. package/examples/extensions/custom-compaction.ts +1 -16
  739. package/examples/extensions/custom-provider-anthropic/index.ts +11 -4
  740. package/examples/extensions/custom-provider-gitlab-duo/index.ts +2 -1
  741. package/examples/extensions/doom-overlay/wad-finder.ts +7 -3
  742. package/examples/extensions/handoff.ts +2 -11
  743. package/examples/extensions/qna.ts +3 -7
  744. package/examples/extensions/summarize.ts +8 -18
  745. package/examples/extensions/truncated-tool.ts +2 -2
  746. package/examples/rpc-extension-ui.ts +11 -2
  747. package/examples/sdk/02-custom-model.ts +1 -2
  748. package/examples/sdk/09-api-keys-and-oauth.ts +1 -1
  749. package/examples/sdk/12-full-control.ts +3 -1
  750. package/examples/sdk/README.md +1 -0
  751. package/npm-shrinkwrap.json +199 -270
  752. package/package.json +16 -9
  753. package/dist/extensions/plan/constants.d.ts +0 -68
  754. package/dist/extensions/plan/constants.d.ts.map +0 -1
  755. package/dist/extensions/plan/constants.js +0 -100
  756. package/dist/extensions/plan/constants.js.map +0 -1
  757. package/dist/extensions/plan/index.d.ts +0 -30
  758. package/dist/extensions/plan/index.d.ts.map +0 -1
  759. package/dist/extensions/plan/index.js +0 -429
  760. package/dist/extensions/plan/index.js.map +0 -1
  761. package/dist/extensions/plan/schema.d.ts +0 -20
  762. package/dist/extensions/plan/schema.d.ts.map +0 -1
  763. package/dist/extensions/plan/schema.js +0 -13
  764. package/dist/extensions/plan/schema.js.map +0 -1
  765. package/dist/extensions/plan/state.d.ts +0 -38
  766. package/dist/extensions/plan/state.d.ts.map +0 -1
  767. package/dist/extensions/plan/state.js +0 -96
  768. package/dist/extensions/plan/state.js.map +0 -1
  769. package/dist/extensions/plan/storage.d.ts +0 -26
  770. package/dist/extensions/plan/storage.d.ts.map +0 -1
  771. package/dist/extensions/plan/storage.js +0 -83
  772. package/dist/extensions/plan/storage.js.map +0 -1
  773. package/dist/extensions/plan/view.d.ts +0 -44
  774. package/dist/extensions/plan/view.d.ts.map +0 -1
  775. package/dist/extensions/plan/view.js +0 -149
  776. package/dist/extensions/plan/view.js.map +0 -1
  777. package/docs/bundled/extensions/README.md +0 -19
  778. package/docs/bundled/extensions/plan.md +0 -34
  779. package/examples/extensions/plan-mode/README.md +0 -66
  780. package/examples/extensions/plan-mode/index.ts +0 -390
  781. package/examples/extensions/plan-mode/utils.ts +0 -168
@@ -1,433 +1,328 @@
1
1
  # Custom Providers
2
2
 
3
- Extensions can register custom model providers via `pi.registerProvider()`. This enables:
4
-
5
- - **Proxies** - Route requests through corporate proxies or API gateways
6
- - **Custom endpoints** - Use self-hosted or private model deployments
7
- - **OAuth/SSO** - Add authentication flows for enterprise providers
8
- - **Custom APIs** - Implement streaming for non-standard LLM APIs
3
+ Use an extension when a provider needs runtime behavior: an OAuth flow, dynamic catalog, proxy-specific headers, payload rewriting, or a streaming protocol that Pi does not already support. For built-in provider setup, see [Providers](providers.md); for a static OpenAI-compatible endpoint, `models.json` is usually simpler; see [Custom Models](models.md).
9
4
 
10
5
  ## Example Extensions
11
6
 
12
- See these complete provider examples:
7
+ These published extensions are the maintained end-to-end references:
13
8
 
14
- - [`examples/extensions/custom-provider-anthropic/`](../examples/extensions/custom-provider-anthropic/)
15
- - [`examples/extensions/custom-provider-gitlab-duo/`](../examples/extensions/custom-provider-gitlab-duo/)
9
+ - [`custom-provider-anthropic`](../examples/extensions/custom-provider-anthropic/) implements OAuth and a custom event stream.
10
+ - [`custom-provider-gitlab-duo`](../examples/extensions/custom-provider-gitlab-duo/) obtains provider-specific credentials, then delegates to Pi AI's public Anthropic and OpenAI stream APIs.
16
11
 
17
12
  ## Table of Contents
18
13
 
19
- - [Example Extensions](#example-extensions)
20
- - [Quick Reference](#quick-reference)
21
- - [Override Existing Provider](#override-existing-provider)
22
- - [Register New Provider](#register-new-provider)
23
- - [Unregister Provider](#unregister-provider)
24
- - [OAuth Support](#oauth-support)
25
- - [Custom Streaming API](#custom-streaming-api)
26
- - [Context Overflow Errors](#context-overflow-errors)
27
- - [Testing Your Implementation](#testing-your-implementation)
28
- - [Config Reference](#config-reference)
29
- - [Model Definition Reference](#model-definition-reference)
14
+ - [Provider Registration](#provider-registration)
15
+ - [Configuration Form](#configuration-form)
16
+ - [Complete pi-ai Provider](#complete-pi-ai-provider)
17
+ - [Composition, Updates, and Removal](#composition-updates-and-removal)
18
+ - [Dynamic Model Catalogs](#dynamic-model-catalogs)
19
+ - [API and Model Configuration](#api-and-model-configuration)
20
+ - [Authentication, Headers, and Request Hooks](#authentication-headers-and-request-hooks)
21
+ - [API Keys and Registered Headers](#api-keys-and-registered-headers)
22
+ - [OAuth](#oauth)
23
+ - [Payload and Header Hooks](#payload-and-header-hooks)
24
+ - [Custom Streaming APIs](#custom-streaming-apis)
25
+ - [Stream Contract](#stream-contract)
26
+ - [Forwarding Hooks from a Custom Stream](#forwarding-hooks-from-a-custom-stream)
27
+ - [Context Overflow Recovery](#context-overflow-recovery)
28
+ - [Testing](#testing)
29
+
30
+ ## Provider Registration
31
+
32
+ `pi.registerProvider()` has two forms:
33
+
34
+ 1. `pi.registerProvider(name, config)` configures a provider, models, optional OAuth, and an optional custom `streamSimple` handler.
35
+ 2. `pi.registerProvider(provider)` registers a complete public pi-ai `Provider` when the extension owns native auth, catalog, filtering, or both stream methods.
30
36
 
31
- ## Quick Reference
37
+ An extension factory may be `async`; Pi waits for it before startup continues. Register during the factory when models must be present in `/model` and `pi --list-models`. See [Writing an Extension](extensions.md#writing-an-extension).
32
38
 
33
- Extensions can register either a complete pi-ai `Provider` or use the legacy provider-config form. Prefer a complete provider when custom authentication, filtering, refresh, or streaming behavior is required. Pi composes `models.json` overrides above registered native providers.
39
+ ### Configuration Form
40
+
41
+ Use the configuration form for a standard supported API or to override a built-in provider:
34
42
 
35
43
  ```typescript
36
- import { createProvider, openAICompletionsApi } from "@earendil-works/pi-ai";
37
44
  import type { ExtensionAPI } from "@astralyn/pi";
38
45
 
39
46
  export default function (pi: ExtensionAPI) {
40
- pi.registerProvider(createProvider({
41
- id: "native-local",
42
- name: "Native Local",
43
- baseUrl: "http://localhost:8080/v1",
44
- auth: {
45
- apiKey: {
46
- name: "Local server API key",
47
- async login(interaction) {
48
- return {
49
- type: "api_key",
50
- key: await interaction.prompt({ type: "secret", message: "API key" })
51
- };
52
- },
53
- async resolve({ credential }) {
54
- return credential?.key
55
- ? { auth: { apiKey: credential.key }, source: "stored API key" }
56
- : undefined;
57
- }
58
- }
59
- },
60
- models: [],
61
- api: openAICompletionsApi()
62
- }));
63
-
64
- // Legacy provider-config form:
65
- // Override baseUrl for existing provider
47
+ // Keeps Anthropic's existing models and auth; only the endpoint changes.
66
48
  pi.registerProvider("anthropic", {
67
- baseUrl: "https://proxy.example.com"
49
+ baseUrl: "https://proxy.example.com/anthropic",
50
+ headers: { "X-Proxy-Tenant": "$PROXY_TENANT" },
68
51
  });
69
52
 
70
- // Register new provider with models
71
- pi.registerProvider("my-provider", {
72
- name: "My Provider",
73
- baseUrl: "https://api.example.com",
74
- apiKey: "$MY_API_KEY",
53
+ // A new OpenAI-compatible provider.
54
+ pi.registerProvider("local-openai", {
55
+ name: "Local OpenAI",
56
+ baseUrl: "http://127.0.0.1:8080/v1",
57
+ apiKey: "local", // A literal placeholder for a server that ignores keys.
75
58
  api: "openai-completions",
76
59
  models: [
77
60
  {
78
- id: "my-model",
79
- name: "My Model",
61
+ id: "local-model",
62
+ name: "Local Model",
80
63
  reasoning: false,
81
- input: ["text", "image"],
64
+ input: ["text"],
82
65
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
83
66
  contextWindow: 128000,
84
- maxTokens: 4096
85
- }
86
- ]
67
+ maxTokens: 4096,
68
+ },
69
+ ],
87
70
  });
88
71
  }
89
72
  ```
90
73
 
91
- The extension factory can also be `async`. For dynamic model discovery, fetch and register models in the factory instead of `session_start`. pi waits for the factory before startup continues, so the provider is available during interactive startup and to `pi --list-models`.
74
+ When `models` is omitted, existing models are retained. When it is supplied, it replaces the models supplied by the built-in/native provider and earlier extension registration for that provider. A model can set its own `api` or `baseUrl` to override the provider default.
92
75
 
93
- ## Override Existing Provider
76
+ A `streamSimple` registration must also set the provider-level `api`. Pi invokes that handler only for models whose effective `api` matches the registered API tag. A custom tag such as `"my-company-api"` is valid; do **not** call the compatibility API registry's `registerApiProvider()` merely to use it with `pi.registerProvider()`.
94
77
 
95
- The simplest use case: redirect an existing provider through a proxy.
78
+ ### Complete pi-ai Provider
79
+
80
+ For a provider with native behavior, construct a public pi-ai `Provider` and register it directly. `createProvider`, `envApiKeyAuth`, and the `Provider`/`Model` types are public root exports. The lazy built-in API factories, including `openAICompletionsApi`, are public from `@earendil-works/pi-ai/compat`.
96
81
 
97
82
  ```typescript
98
- // All Anthropic requests now go through your proxy
99
- pi.registerProvider("anthropic", {
100
- baseUrl: "https://proxy.example.com"
101
- });
83
+ import type { ExtensionAPI } from "@astralyn/pi";
84
+ import { createProvider, envApiKeyAuth, type Model } from "@earendil-works/pi-ai";
85
+ import { openAICompletionsApi } from "@earendil-works/pi-ai/compat";
102
86
 
103
- // Add custom headers to OpenAI requests
104
- pi.registerProvider("openai", {
105
- headers: {
106
- "X-Custom-Header": "value"
107
- }
108
- });
87
+ const model: Model<"openai-completions"> = {
88
+ id: "local-model",
89
+ name: "Local Model",
90
+ api: "openai-completions",
91
+ provider: "native-local",
92
+ baseUrl: "http://127.0.0.1:8080/v1",
93
+ reasoning: false,
94
+ input: ["text"],
95
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
96
+ contextWindow: 128000,
97
+ maxTokens: 4096,
98
+ };
109
99
 
110
- // Both baseUrl and headers
111
- pi.registerProvider("google", {
112
- baseUrl: "https://ai-gateway.corp.com/google",
113
- headers: {
114
- "X-Corp-Auth": "$CORP_AUTH_TOKEN" // env var or literal
115
- }
116
- });
100
+ export default function (pi: ExtensionAPI) {
101
+ pi.registerProvider(
102
+ createProvider({
103
+ id: "native-local",
104
+ name: "Native Local",
105
+ baseUrl: model.baseUrl,
106
+ auth: {
107
+ apiKey: envApiKeyAuth("Local server API key", ["LOCAL_OPENAI_API_KEY"]),
108
+ },
109
+ models: [model],
110
+ api: openAICompletionsApi(),
111
+ }),
112
+ );
113
+ }
117
114
  ```
118
115
 
119
- When only `baseUrl` and/or `headers` are provided (no `models`), all existing models for that provider are preserved with the new endpoint.
120
-
121
- ## Register New Provider
116
+ A complete `Provider` must have auth, `getModels()`, `stream()`, and `streamSimple()` behavior. `createProvider()` supplies the model/catalog plumbing and both stream methods from the public `ProviderStreams` value passed as `api`; use a hand-written `Provider` only when that helper does not fit. For hand-written API-key auth, `login(interaction)` returns an `ApiKeyCredential` and `resolve({ ctx, credential })` returns an `AuthResult` or `undefined`; `envApiKeyAuth()` implements that standard pattern.
122
117
 
123
- To add a completely new provider, specify `models` along with the required configuration.
118
+ ### Composition, Updates, and Removal
124
119
 
125
- If the model list comes from a remote endpoint, use an async extension factory:
126
-
127
- ```typescript
128
- import type { ExtensionAPI } from "@astralyn/pi";
129
-
130
- export default async function (pi: ExtensionAPI) {
131
- const response = await fetch("http://localhost:1234/v1/models");
132
- const payload = (await response.json()) as {
133
- data: Array<{
134
- id: string;
135
- name?: string;
136
- context_window?: number;
137
- max_tokens?: number;
138
- }>;
139
- };
120
+ A complete registered `Provider` is the base provider for its ID. Pi applies `models.json` configuration above that base, including final `modelOverrides`; see [Per-model Overrides](models.md#per-model-overrides). A named configuration registration is instead applied over the native/built-in provider and `models.json` layers.
140
121
 
141
- pi.registerProvider("local-openai", {
142
- baseUrl: "http://localhost:1234/v1",
143
- apiKey: "$LOCAL_OPENAI_API_KEY",
144
- api: "openai-completions",
145
- models: payload.data.map((model) => ({
146
- id: model.id,
147
- name: model.name ?? model.id,
148
- reasoning: false,
149
- input: ["text"],
150
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
151
- contextWindow: model.context_window ?? 128000,
152
- maxTokens: model.max_tokens ?? 4096,
153
- })),
154
- });
155
- }
156
- ```
122
+ A later named registration for the same ID merges its defined configuration values with the prior named registration. Registering a complete `Provider` for that ID replaces the named registration; registering by name replaces a complete extension provider for that ID.
157
123
 
158
- This registers the fetched models before startup finishes.
124
+ Provider calls made from the factory are queued until the extension runner is ready. Calls made later, such as from a command, take effect immediately; no `/reload` is needed.
159
125
 
160
126
  ```typescript
161
- pi.registerProvider("my-llm", {
162
- baseUrl: "https://api.my-llm.com/v1",
163
- apiKey: "$MY_LLM_API_KEY", // env var reference
164
- api: "openai-completions", // which streaming API to use
165
- models: [
166
- {
167
- id: "my-llm-large",
168
- name: "My LLM Large",
169
- reasoning: true, // supports extended thinking
170
- input: ["text", "image"],
171
- cost: {
172
- input: 3.0, // $/million tokens
173
- output: 15.0,
174
- cacheRead: 0.3,
175
- cacheWrite: 3.75
176
- },
177
- contextWindow: 200000,
178
- maxTokens: 16384
179
- }
180
- ]
181
- });
127
+ pi.unregisterProvider("local-openai");
182
128
  ```
183
129
 
184
- When `models` is provided, it **replaces** all existing models for that provider.
130
+ `unregisterProvider()` removes either form registered by the extension and recomposes the remaining layers. A built-in provider is restored when one exists; `models.json` configuration remains, while a provider defined only by the extension disappears.
185
131
 
186
- `apiKey` and custom header values use the same config value syntax as `models.json`: `!command` at the start executes a command for the whole value, `$ENV_VAR` and `${ENV_VAR}` interpolate environment variables, `$$` emits a literal `$`, and `$!` emits a literal `!`.
132
+ ### Dynamic Model Catalogs
187
133
 
188
- ## Unregister Provider
189
-
190
- Use `pi.unregisterProvider(name)` to remove a provider that was previously registered via `pi.registerProvider(name, ...)`:
134
+ The configuration form can refresh an extension-owned model list. Pi calls `refreshModels` during model refresh. Its returned list replaces the extension-provided models. Use `context.signal` for requests and `context.store` only when the catalog should persist across sessions.
191
135
 
192
136
  ```typescript
193
- // Register
194
- pi.registerProvider("my-llm", {
195
- baseUrl: "https://api.my-llm.com/v1",
196
- apiKey: "$MY_LLM_API_KEY",
137
+ pi.registerProvider("local-openai", {
138
+ baseUrl: "http://127.0.0.1:8080/v1",
139
+ apiKey: "local",
197
140
  api: "openai-completions",
198
- models: [
199
- {
200
- id: "my-llm-large",
201
- name: "My LLM Large",
202
- reasoning: true,
203
- input: ["text", "image"],
204
- cost: { input: 3.0, output: 15.0, cacheRead: 0.3, cacheWrite: 3.75 },
205
- contextWindow: 200000,
206
- maxTokens: 16384
207
- }
208
- ]
141
+ async refreshModels({ signal }) {
142
+ const response = await fetch("http://127.0.0.1:8080/v1/models", { signal });
143
+ if (!response.ok) throw new Error(`Model discovery failed: ${response.status}`);
144
+
145
+ const payload = (await response.json()) as { data: Array<{ id: string }> };
146
+ return payload.data.map((entry) => ({
147
+ id: entry.id,
148
+ name: entry.id,
149
+ reasoning: false,
150
+ input: ["text"],
151
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
152
+ contextWindow: 128000,
153
+ maxTokens: 4096,
154
+ }));
155
+ },
209
156
  });
210
-
211
- // Later, remove it
212
- pi.unregisterProvider("my-llm");
213
157
  ```
214
158
 
215
- Unregistering removes that provider's dynamic models, API key fallback, OAuth provider registration, and custom stream handler registrations. Any built-in models or provider behavior that were overridden are restored.
159
+ For one-time discovery that must finish before model selection, make the extension factory `async`, fetch the catalog there, and then call `registerProvider()` with `models`.
216
160
 
217
- Calls made after the initial extension load phase are applied immediately, so no `/reload` is required.
161
+ ## API and Model Configuration
218
162
 
219
- ### API Types
163
+ Set `api` on the provider as the default, or on a model when one provider has models served by different APIs. The built-in API tags are:
220
164
 
221
- The `api` field determines which streaming implementation is used:
165
+ | API | Use |
166
+ | --- | --- |
167
+ | `anthropic-messages` | Anthropic Messages-compatible APIs |
168
+ | `openai-completions` | OpenAI Chat Completions-compatible APIs |
169
+ | `openai-responses` | OpenAI Responses APIs |
170
+ | `azure-openai-responses` | Azure OpenAI Responses APIs |
171
+ | `openai-codex-responses` | OpenAI Codex Responses APIs |
172
+ | `mistral-conversations` | Mistral Conversations APIs |
173
+ | `google-generative-ai` | Google Generative AI APIs |
174
+ | `google-vertex` | Google Vertex AI APIs |
175
+ | `bedrock-converse-stream` | Amazon Bedrock Converse Stream APIs |
176
+ | `pi-messages` | Pi Messages APIs |
222
177
 
223
- | API | Use for |
224
- |-----|---------|
225
- | `anthropic-messages` | Anthropic Claude API and compatibles |
226
- | `openai-completions` | OpenAI Chat Completions API and compatibles |
227
- | `openai-responses` | OpenAI Responses API |
228
- | `azure-openai-responses` | Azure OpenAI Responses API |
229
- | `openai-codex-responses` | OpenAI Codex Responses API |
230
- | `mistral-conversations` | Mistral SDK Conversations/Chat streaming |
231
- | `google-generative-ai` | Google Generative AI API |
232
- | `google-vertex` | Google Vertex AI API |
233
- | `bedrock-converse-stream` | Amazon Bedrock Converse API |
178
+ A configuration-form model requires `id`, `name`, `reasoning`, `input`, `cost`, `contextWindow`, and `maxTokens`. It may also set `api`, `baseUrl`, `headers`, `thinkingLevelMap`, and `compat`. `cost` rates are per million tokens. `thinkingLevelMap` maps Pi's `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max` levels; `null` hides an unsupported level.
234
179
 
235
- Most OpenAI-compatible providers work with `openai-completions`. Use model-level `thinkingLevelMap` for model-specific thinking levels, and `compat` for provider quirks. The `xhigh` and `max` levels are opt-in, require non-null map entries, and may be separated by unsupported holes:
236
-
237
- ```typescript
238
- models: [{
239
- id: "custom-model",
240
- // ...
241
- reasoning: true,
242
- thinkingLevelMap: { // map pi levels to provider values; null hides unsupported levels
243
- minimal: null,
244
- low: null,
245
- medium: null,
246
- high: "default",
247
- xhigh: null,
248
- max: "max"
249
- },
250
- compat: {
251
- supportsDeveloperRole: false, // use "system" instead of "developer"
252
- supportsReasoningEffort: true,
253
- maxTokensField: "max_tokens", // instead of "max_completion_tokens"
254
- requiresToolResultName: true, // tool results need name field
255
- thinkingFormat: "qwen", // top-level enable_thinking: true
256
- cacheControlFormat: "anthropic" // Anthropic-style cache_control markers
257
- }
258
- }]
259
- ```
180
+ Keep endpoint-specific compatibility settings in `compat` rather than reimplementing a standard protocol. The full model fields, thinking maps, cost tiers, and Anthropic/OpenAI compatibility options are documented in [Custom Models](models.md#model-configuration), [Anthropic Messages Compatibility](models.md#anthropic-messages-compatibility), and [OpenAI Compatibility](models.md#openai-compatibility).
260
181
 
261
- Use `openrouter` for OpenRouter-style `reasoning: { effort }` controls. Use `together` for Together-style `reasoning: { enabled }` controls; with `supportsReasoningEffort`, it also sends `reasoning_effort`. Use `qwen-chat-template` for local Qwen-compatible servers that read `chat_template_kwargs.enable_thinking` and need `preserve_thinking`.
262
- Use `cacheControlFormat: "anthropic"` for OpenAI-compatible providers that expose Anthropic-style prompt caching via `cache_control` on the system prompt, last tool definition, and last user, assistant, or tool-result text content.
182
+ ## Authentication, Headers, and Request Hooks
263
183
 
264
- For Anthropic-compatible providers using `api: "anthropic-messages"`, set `compat.forceAdaptiveThinking: true` on models or providers whose upstream model requires adaptive thinking (`thinking.type: "adaptive"` plus `output_config.effort`). Built-in adaptive Claude models set this automatically. Set `compat.allowEmptySignature: true` only for providers that emit empty thinking signatures and expect `signature: ""` on replay.
184
+ ### API Keys and Registered Headers
265
185
 
266
- > Migration note: Mistral moved from `openai-completions` to `mistral-conversations`.
267
- > Use `mistral-conversations` for native Mistral models.
268
- > If you intentionally route Mistral-compatible/custom endpoints through `openai-completions`, set `compat` flags explicitly as needed.
186
+ For a new config-form provider, supply `apiKey` unless it has OAuth or a complete native provider supplies auth. `apiKey`, provider `headers`, and model `headers` use the same value syntax as `models.json`:
269
187
 
270
- ### Auth Header
188
+ - `$NAME` and `${NAME}` interpolate environment variables.
189
+ - A value beginning with `!` runs a command for the whole value.
190
+ - `$$` and `$!` emit literal `$` and `!`.
191
+ - Other text, including uppercase text, is literal.
271
192
 
272
- If your provider expects `Authorization: Bearer <key>` but doesn't use a standard API, set `authHeader: true`:
193
+ Values are resolved at request time. See [Value Resolution](models.md#value-resolution) for command behavior and security considerations.
273
194
 
274
195
  ```typescript
275
- pi.registerProvider("custom-api", {
276
- baseUrl: "https://api.example.com",
277
- apiKey: "$MY_API_KEY",
278
- authHeader: true, // adds Authorization: Bearer header
196
+ pi.registerProvider("company-api", {
197
+ baseUrl: "https://api.example.com/v1",
198
+ apiKey: "$COMPANY_API_KEY",
279
199
  api: "openai-completions",
280
- models: [...]
200
+ headers: {
201
+ "X-Tenant": "$COMPANY_TENANT",
202
+ "X-Proxy-Token": "!company-token print",
203
+ },
204
+ authHeader: true, // Adds Authorization: Bearer <resolved API key>.
205
+ models: [/* ... */],
281
206
  });
282
207
  ```
283
208
 
284
- The key is resolved for each request. An explicit request `Authorization` header takes precedence over the generated value.
209
+ `authHeader` is useful for a custom API that expects a bearer token but does not create that header itself. Standard Pi AI API implementations already apply their own authentication. A custom stream receives the resolved key as `options.apiKey` and the final headers as `options.headers`; forward both as appropriate for its protocol.
285
210
 
286
- ## OAuth Support
211
+ ### OAuth
287
212
 
288
- Add OAuth/SSO authentication that integrates with `/login`:
213
+ The configuration form adapts the extension OAuth callbacks to Pi AI's provider auth and exposes the provider through `/login`. This callback shape is specific to `ProviderConfig.oauth`: a complete native `Provider` instead supplies `auth.oauth.login(interaction)`, `refresh(credential, signal?)`, and `toAuth(credential)`. Do not use the configuration form's `refreshToken` or `getApiKey` names on native `auth.oauth`.
289
214
 
290
215
  ```typescript
216
+ import type { ExtensionAPI } from "@astralyn/pi";
291
217
  import type { OAuthCredentials, OAuthLoginCallbacks } from "@earendil-works/pi-ai";
292
218
 
293
- pi.registerProvider("corporate-ai", {
294
- baseUrl: "https://ai.corp.com/v1",
295
- api: "openai-responses",
296
- models: [...],
297
- oauth: {
298
- name: "Corporate AI (SSO)",
299
-
300
- async login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {
301
- const method = await callbacks.onSelect({
302
- message: "Select login method:",
303
- options: [
304
- { id: "browser", label: "Browser OAuth" },
305
- { id: "device", label: "Device code" }
306
- ]
307
- });
308
- if (!method) throw new Error("Login cancelled");
309
-
310
- let code: string;
311
- if (method === "device") {
312
- callbacks.onDeviceCode({
313
- userCode: "ABCD-1234",
314
- verificationUri: "https://sso.corp.com/device",
315
- intervalSeconds: 5,
316
- expiresInSeconds: 900
317
- });
318
- code = await pollDeviceCodeUntilComplete();
319
- } else {
320
- callbacks.onAuth({ url: "https://sso.corp.com/authorize?..." });
321
- code = await callbacks.onPrompt({ message: "Enter SSO code:" });
322
- }
219
+ async function login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {
220
+ callbacks.onAuth({
221
+ url: "https://sso.example.com/authorize?...",
222
+ instructions: "Sign in, then paste the authorization code.",
223
+ });
224
+ const code = await callbacks.onPrompt({ message: "Authorization code" });
323
225
 
324
- // Exchange for tokens (your implementation)
325
- const tokens = await exchangeCodeForTokens(code);
226
+ // Exchange the code with this provider's OAuth server. Pass callbacks.signal
227
+ // to provider requests when supported.
228
+ return exchangeAuthorizationCode(code, callbacks.signal);
229
+ }
326
230
 
327
- return {
328
- refresh: tokens.refreshToken,
329
- access: tokens.accessToken,
330
- expires: Date.now() + tokens.expiresIn * 1000
331
- };
332
- },
231
+ async function refreshToken(credentials: OAuthCredentials, signal: AbortSignal): Promise<OAuthCredentials> {
232
+ return refreshAuthorization(credentials.refresh, signal);
233
+ }
333
234
 
334
- async refreshToken(credentials: OAuthCredentials): Promise<OAuthCredentials> {
335
- const tokens = await refreshAccessToken(credentials.refresh);
336
- return {
337
- refresh: tokens.refreshToken ?? credentials.refresh,
338
- access: tokens.accessToken,
339
- expires: Date.now() + tokens.expiresIn * 1000
340
- };
235
+ export default function (pi: ExtensionAPI) {
236
+ pi.registerProvider("company-api", {
237
+ baseUrl: "https://api.example.com/v1",
238
+ api: "openai-responses",
239
+ models: [/* ... */],
240
+ oauth: {
241
+ name: "Company AI",
242
+ login,
243
+ refreshToken,
244
+ getApiKey: (credentials) => credentials.access,
341
245
  },
342
-
343
- getApiKey(credentials: OAuthCredentials): string {
344
- return credentials.access;
345
- }
346
- }
347
- });
246
+ });
247
+ }
348
248
  ```
349
249
 
350
- After registration, users can authenticate via `/login corporate-ai`.
250
+ Return credentials with `refresh`, `access`, and `expires` (a millisecond timestamp). Pi adds the stored OAuth credential's internal type tag; extension callbacks return `OAuthCredentials`, not a credential with `type: "oauth"`.
351
251
 
352
- ### OAuthLoginCallbacks
252
+ `OAuthLoginCallbacks` provides these UI-neutral operations:
353
253
 
354
- The `callbacks` object provides UI-neutral interactions for the provider-owned flow:
254
+ - `onAuth({ url, instructions? })` opens/displays an authorization URL.
255
+ - `onDeviceCode({ userCode, verificationUri, intervalSeconds?, expiresInSeconds? })` displays a device flow code.
256
+ - `onPrompt({ message, placeholder?, allowEmpty? })` asks for text and resolves to the entered string.
257
+ - `onSelect({ message, options: [{ id, label }] })` resolves to an option ID or `undefined`.
258
+ - `onManualCodeInput?.()` optionally requests a manually pasted code.
259
+ - `onProgress?.(message)` reports transient progress, and `signal` is an optional `AbortSignal` for the login flow.
355
260
 
356
- ```typescript
357
- interface OAuthLoginCallbacks {
358
- // Open URL in browser (for OAuth redirects)
359
- onAuth(params: { url: string }): void;
360
-
361
- // Show device code (for device authorization flow)
362
- onDeviceCode(params: {
363
- userCode: string;
364
- verificationUri: string;
365
- intervalSeconds?: number;
366
- expiresInSeconds?: number;
367
- }): void;
368
-
369
- // Show transient progress
370
- onProgress?(message: string): void;
371
-
372
- // Prompt user for input (for manual token entry)
373
- onPrompt(params: { message: string }): Promise<string>;
374
-
375
- // Show an interactive selector, e.g. to choose browser OAuth vs device code
376
- onSelect(params: {
377
- message: string;
378
- options: { id: string; label: string }[];
379
- }): Promise<string | undefined>;
380
- }
381
- ```
261
+ Do not use `usesCallbackServer`; it is deprecated and ignored by the canonical OAuth flow. Keep OAuth tokens out of logs and error messages.
262
+
263
+ ### Payload and Header Hooks
264
+
265
+ For a standard Pi AI API stream, Pi's extension hooks work automatically:
382
266
 
383
- ### OAuthCredentials
267
+ - `before_provider_headers` mutates the fully assembled headers. A `null` header value removes that header.
268
+ - `before_provider_request` inspects or replaces the serialized payload.
269
+ - `after_provider_response` sees the status and normalized headers before the response body is consumed.
384
270
 
385
- Credentials are persisted in `~/.pi/agent/auth.json`:
271
+ See [provider request events](extensions.md#before_provider_headers) for ordering and examples. `onPayload`, `onResponse`, and `transformHeaders` are **not** `ProviderConfig` fields. Pi passes the first two to a stream as `SimpleStreamOptions`; header transformation has already happened before a custom `streamSimple` is called.
272
+
273
+ A custom stream that sends HTTP itself must call `options.onPayload` and `options.onResponse` to preserve the payload/response hooks. It must also use the supplied headers. For example, immediately before consuming a custom HTTP response:
386
274
 
387
275
  ```typescript
388
- interface OAuthCredentials {
389
- refresh: string; // Refresh token (for refreshToken())
390
- access: string; // Access token (returned by getApiKey())
391
- expires: number; // Expiration timestamp in milliseconds
392
- }
276
+ const payload = { model: model.id, stream: true };
277
+ const replacement = await options?.onPayload?.(payload, model);
278
+ const requestPayload = replacement === undefined ? payload : replacement;
279
+
280
+ const headers = Object.fromEntries(
281
+ Object.entries(options?.headers ?? {}).filter((entry): entry is [string, string] => entry[1] !== null),
282
+ );
283
+ const response = await fetch(`${model.baseUrl}/stream`, {
284
+ method: "POST",
285
+ headers: { "content-type": "application/json", ...headers },
286
+ body: JSON.stringify(requestPayload),
287
+ signal: options?.signal,
288
+ });
289
+ await options?.onResponse?.(
290
+ { status: response.status, headers: Object.fromEntries(response.headers.entries()) },
291
+ model,
292
+ );
293
+ if (!response.ok) throw new Error(`Request failed: ${response.status}`);
393
294
  ```
394
295
 
395
- ## Custom Streaming API
296
+ When a custom stream delegates to a public Pi AI API stream, pass through `options` (and merge rather than discard `options.headers`) so those hooks continue to run. The GitLab Duo example follows this pattern.
396
297
 
397
- For providers with non-standard APIs, implement `streamSimple`. Study the existing provider implementations before writing your own:
298
+ ## Custom Streaming APIs
398
299
 
399
- **Reference implementations:**
400
- - [anthropic.ts](https://github.com/earendil-works/pi/blob/v0.82.1/packages/ai/src/providers/anthropic.ts) - Anthropic Messages API
401
- - [mistral.ts](https://github.com/earendil-works/pi/blob/v0.82.1/packages/ai/src/providers/mistral.ts) - Mistral Conversations API
402
- - [openai-completions.ts](https://github.com/earendil-works/pi/blob/v0.82.1/packages/ai/src/providers/openai-completions.ts) - OpenAI Chat Completions
403
- - [openai-responses.ts](https://github.com/earendil-works/pi/blob/v0.82.1/packages/ai/src/providers/openai-responses.ts) - OpenAI Responses API
404
- - [google.ts](https://github.com/earendil-works/pi/blob/v0.82.1/packages/ai/src/providers/google.ts) - Google Generative AI
405
- - [amazon-bedrock.ts](https://github.com/earendil-works/pi/blob/v0.82.1/packages/ai/src/providers/amazon-bedrock.ts) - AWS Bedrock
300
+ Implement `streamSimple` only for a protocol that cannot use a built-in API tag or a public Pi AI API stream. The custom Anthropic example implements the event protocol directly; the GitLab Duo example shows the preferred delegation approach when an endpoint is still Anthropic- or OpenAI-compatible.
406
301
 
407
- ### Stream Pattern
302
+ ### Stream Contract
408
303
 
409
- All providers follow the same pattern:
304
+ The configuration-form handler signature is exactly:
410
305
 
411
306
  ```typescript
412
307
  import {
308
+ type Api,
413
309
  type AssistantMessage,
414
310
  type AssistantMessageEventStream,
311
+ calculateCost,
415
312
  type Context,
313
+ createAssistantMessageEventStream,
416
314
  type Model,
417
315
  type SimpleStreamOptions,
418
- calculateCost,
419
- createAssistantMessageEventStream,
420
316
  } from "@earendil-works/pi-ai";
421
317
 
422
318
  function streamMyProvider(
423
- model: Model<any>,
319
+ model: Model<Api>,
424
320
  context: Context,
425
- options?: SimpleStreamOptions
321
+ options?: SimpleStreamOptions,
426
322
  ): AssistantMessageEventStream {
427
323
  const stream = createAssistantMessageEventStream();
428
324
 
429
- (async () => {
430
- // Initialize output message
325
+ void (async () => {
431
326
  const output: AssistantMessage = {
432
327
  role: "assistant",
433
328
  content: [],
@@ -442,28 +337,51 @@ function streamMyProvider(
442
337
  totalTokens: 0,
443
338
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
444
339
  },
445
- stopReason: "stop",
340
+ stopReason: "pending",
446
341
  timestamp: Date.now(),
447
342
  };
448
343
 
449
344
  try {
450
- // Push start event
451
345
  stream.push({ type: "start", partial: output });
452
346
 
453
- // Make API request and process response...
454
- // Push content events as they arrive...
347
+ // Send the request, consume the provider stream, and update output.
348
+ const contentIndex = output.content.length;
349
+ output.content.push({ type: "text", text: "" });
350
+ stream.push({ type: "text_start", contentIndex, partial: output });
351
+
352
+ // For each provider text delta:
353
+ const delta = "...";
354
+ const block = output.content[contentIndex];
355
+ if (block.type === "text") {
356
+ block.text += delta;
357
+ stream.push({ type: "text_delta", contentIndex, delta, partial: output });
358
+ }
455
359
 
456
- // Push done event
457
- stream.push({
458
- type: "done",
459
- reason: output.stopReason as "stop" | "length" | "toolUse",
460
- message: output
461
- });
462
- stream.end();
360
+ // After the provider completes this block:
361
+ if (block.type === "text") {
362
+ stream.push({ type: "text_end", contentIndex, content: block.text, partial: output });
363
+ }
364
+
365
+ // Update usage whenever the provider reports it, then recalculate cost.
366
+ output.usage.totalTokens =
367
+ output.usage.input + output.usage.output + output.usage.cacheRead + output.usage.cacheWrite;
368
+ calculateCost(model, output.usage);
369
+
370
+ // The omitted provider loop must map its terminal event by replacing
371
+ // "pending" with "stop", "length", "toolUse", "error", or "aborted".
372
+ if (output.stopReason === "pending") {
373
+ throw new Error("Provider stream ended without a stop reason");
374
+ }
375
+ if (output.stopReason === "error" || output.stopReason === "aborted") {
376
+ throw new Error(output.errorMessage || "An unknown error occurred");
377
+ }
378
+ stream.push({ type: "done", reason: output.stopReason, message: output });
463
379
  } catch (error) {
464
- output.stopReason = options?.signal?.aborted ? "aborted" : "error";
380
+ const reason = options?.signal?.aborted ? "aborted" : "error";
381
+ output.stopReason = reason;
465
382
  output.errorMessage = error instanceof Error ? error.message : String(error);
466
- stream.push({ type: "error", reason: output.stopReason, error: output });
383
+ stream.push({ type: "error", reason, error: output });
384
+ } finally {
467
385
  stream.end();
468
386
  }
469
387
  })();
@@ -472,122 +390,68 @@ function streamMyProvider(
472
390
  }
473
391
  ```
474
392
 
475
- ### Event Types
393
+ Emit `start` before partial content events, update `output.content` before each event's `partial`, and end exactly once after either terminal event. The terminal event shapes are deliberately narrow: `done.reason` is `"stop" | "length" | "toolUse"`; `error.reason` is `"error" | "aborted"`. Map the upstream stop reason to one of those values and keep it equal to `output.stopReason`.
476
394
 
477
- Push events via `stream.push()` in this order:
395
+ Use the index of each block in `output.content` as `contentIndex` and emit matching start, delta, and end events:
478
396
 
479
- 1. `{ type: "start", partial: output }` - Stream started
397
+ | Block | Events | Final content field |
398
+ | --- | --- | --- |
399
+ | Text | `text_start`, `text_delta`, `text_end` | `text` |
400
+ | Thinking | `thinking_start`, `thinking_delta`, `thinking_end` | `thinking` |
401
+ | Tool call | `toolcall_start`, `toolcall_delta`, `toolcall_end` | `ToolCall` |
480
402
 
481
- 2. Content events (repeatable, track `contentIndex` for each block):
482
- - `{ type: "text_start", contentIndex, partial }` - Text block started
483
- - `{ type: "text_delta", contentIndex, delta, partial }` - Text chunk
484
- - `{ type: "text_end", contentIndex, content, partial }` - Text block ended
485
- - `{ type: "thinking_start", contentIndex, partial }` - Thinking started
486
- - `{ type: "thinking_delta", contentIndex, delta, partial }` - Thinking chunk
487
- - `{ type: "thinking_end", contentIndex, content, partial }` - Thinking ended
488
- - `{ type: "toolcall_start", contentIndex, partial }` - Tool call started
489
- - `{ type: "toolcall_delta", contentIndex, delta, partial }` - Tool call JSON chunk
490
- - `{ type: "toolcall_end", contentIndex, toolCall, partial }` - Tool call ended
403
+ Tool calls need an `id`, `name`, and parsed `arguments` object in the final `toolcall_end`. Accumulate streamed JSON before parsing it. For replayable thinking, preserve any provider signature in the block's `thinkingSignature` field. Populate `usage` with provider-reported values (`input`, `output`, `cacheRead`, `cacheWrite`, and optional `cacheWrite1h`/`reasoning`) before calling `calculateCost()`.
491
404
 
492
- 3. `{ type: "done", reason, message }` or `{ type: "error", reason, error }` - Stream ended
493
-
494
- The `partial` field in each event contains the current `AssistantMessage` state. Update `output.content` as you receive data, then include `output` as the `partial`.
495
-
496
- ### Content Blocks
497
-
498
- Add content blocks to `output.content` as they arrive:
405
+ Register the handler with a matching custom API tag:
499
406
 
500
407
  ```typescript
501
- // Text block
502
- output.content.push({ type: "text", text: "" });
503
- stream.push({ type: "text_start", contentIndex: output.content.length - 1, partial: output });
504
-
505
- // As text arrives
506
- const block = output.content[contentIndex];
507
- if (block.type === "text") {
508
- block.text += delta;
509
- stream.push({ type: "text_delta", contentIndex, delta, partial: output });
510
- }
511
-
512
- // When block completes
513
- stream.push({ type: "text_end", contentIndex, content: block.text, partial: output });
408
+ pi.registerProvider("my-provider", {
409
+ baseUrl: "https://api.example.com",
410
+ apiKey: "$MY_PROVIDER_API_KEY",
411
+ api: "my-provider-stream",
412
+ models: [/* models whose effective api is my-provider-stream */],
413
+ streamSimple: streamMyProvider,
414
+ });
514
415
  ```
515
416
 
516
- ### Tool Calls
417
+ ### Forwarding Hooks from a Custom Stream
517
418
 
518
- Tool calls require accumulating JSON and parsing:
419
+ `SimpleStreamOptions` also carries cancellation, the resolved API key, reasoning level, thinking budgets, cache/session settings, retry/timeout options, headers, and the payload/response callbacks. Honor the options your protocol supports, especially `signal`, `apiKey`, and `headers`. Do not silently replace the supplied options when delegating:
519
420
 
520
421
  ```typescript
521
- // Start tool call
522
- output.content.push({
523
- type: "toolCall",
524
- id: toolCallId,
525
- name: toolName,
526
- arguments: {}
527
- });
528
- stream.push({ type: "toolcall_start", contentIndex: output.content.length - 1, partial: output });
529
-
530
- // Accumulate JSON
531
- let partialJson = "";
532
- partialJson += jsonDelta;
533
- try {
534
- block.arguments = JSON.parse(partialJson);
535
- } catch {}
536
- stream.push({ type: "toolcall_delta", contentIndex, delta: jsonDelta, partial: output });
537
-
538
- // Complete
539
- stream.push({
540
- type: "toolcall_end",
541
- contentIndex,
542
- toolCall: { type: "toolCall", id, name, arguments: block.arguments },
543
- partial: output
544
- });
545
- ```
422
+ import { openAIResponsesApi } from "@earendil-works/pi-ai/compat";
546
423
 
547
- ### Usage and Cost
424
+ const responsesApi = openAIResponsesApi();
548
425
 
549
- Update usage from API response and calculate cost:
550
-
551
- ```typescript
552
- output.usage.input = response.usage.input_tokens;
553
- output.usage.output = response.usage.output_tokens;
554
- output.usage.cacheRead = response.usage.cache_read_tokens ?? 0;
555
- output.usage.cacheWrite = response.usage.cache_write_tokens ?? 0;
556
- output.usage.totalTokens = output.usage.input + output.usage.output +
557
- output.usage.cacheRead + output.usage.cacheWrite;
558
- calculateCost(model, output.usage);
426
+ // Inside a custom handler, after adapting the model/API as necessary:
427
+ return responsesApi.streamSimple(adaptedModel, context, {
428
+ ...options,
429
+ headers: { ...options?.headers, "X-Relay": "company" },
430
+ });
559
431
  ```
560
432
 
561
- ### Context Overflow Errors
433
+ This is a public API. It avoids depending on pi-ai implementation files and preserves Pi's request hooks.
562
434
 
563
- When a request exceeds the model's context window, pi can recover automatically by compacting the conversation and retrying. This recovery only kicks in if pi recognizes the failure as an overflow.
435
+ ## Context Overflow Recovery
564
436
 
565
- Detection runs on the finalized assistant message:
437
+ Pi uses the public `isContextOverflow(message, contextWindow)` helper when deciding whether to auto-compact. It recognizes its built-in error patterns and also detects a few silent-overflow usage shapes. A custom stream should emit `stopReason: "error"` with the provider's real error in `errorMessage`.
566
438
 
567
- - `stopReason === "error"`
568
- - `errorMessage` matches one of pi's known overflow patterns (see [`packages/ai/src/utils/overflow.ts`](https://github.com/earendil-works/pi/blob/v0.82.1/packages/ai/src/utils/overflow.ts))
569
-
570
- If your provider returns overflow errors with a message pi does not recognize, normalize the error from the same extension that registers the provider. Use a `message_end` handler to rewrite the assistant message so its `errorMessage` starts with a phrase pi recognizes. The generic fallback `context_length_exceeded` is the safest choice.
439
+ If an upstream uses an unrecognized overflow message, normalize only that provider's known overflow error. The generic `context_length_exceeded` prefix is recognized. A `message_end` replacement occurs before Pi's overflow recovery check:
571
440
 
572
441
  ```typescript
573
- const MY_PROVIDER_OVERFLOW_PATTERN = /your provider's overflow phrase/i;
442
+ import type { ExtensionAPI } from "@astralyn/pi";
574
443
 
575
- export default function (pi: ExtensionAPI) {
576
- pi.registerProvider("my-provider", { /* ... */ });
444
+ const PROVIDER_OVERFLOW = /request exceeds the acme input limit/i;
577
445
 
578
- pi.on("message_end", (event, ctx) => {
446
+ export default function (pi: ExtensionAPI) {
447
+ pi.on("message_end", (event) => {
579
448
  const message = event.message;
580
- if (message.role !== "assistant") return;
449
+ if (message.role !== "assistant" || message.provider !== "my-provider") return;
581
450
  if (message.stopReason !== "error") return;
582
- if (
583
- message.provider !== "my-provider" &&
584
- ctx.model?.provider !== "my-provider"
585
- )
586
- return;
587
451
 
588
452
  const errorMessage = message.errorMessage ?? "";
589
453
  if (errorMessage.includes("context_length_exceeded")) return;
590
- if (!MY_PROVIDER_OVERFLOW_PATTERN.test(errorMessage)) return;
454
+ if (!PROVIDER_OVERFLOW.test(errorMessage)) return;
591
455
 
592
456
  return {
593
457
  message: {
@@ -599,52 +463,22 @@ export default function (pi: ExtensionAPI) {
599
463
  }
600
464
  ```
601
465
 
602
- `message_end` runs before pi tracks the assistant message for auto-compaction, so the rewritten `errorMessage` is what pi checks. With this in place, pi will:
603
-
604
- 1. Detect the overflow from `errorMessage`.
605
- 2. Drop the failed assistant message from live context.
606
- 3. Run compaction.
607
- 4. Retry the request once.
608
-
609
- Guard the rewrite carefully:
610
-
611
- - Scope it to your provider (`message.provider` and `ctx.model?.provider`) so unrelated errors from other providers are untouched.
612
- - Match a provider-specific pattern, not pi's generic overflow patterns. Rewriting rate-limit or throttling errors (`rate limit`, `too many requests`) would falsely trigger compaction instead of pi's normal retry-with-backoff path.
613
- - Skip when `errorMessage` already includes `context_length_exceeded` so the handler is idempotent.
466
+ Do not rewrite rate limits, authentication failures, or generic server errors as overflows. With auto-compaction enabled, Pi removes the failed assistant message from live context, compacts, and retries once. Test the normalized message with the public `isContextOverflow()` helper and the model's configured `contextWindow`.
614
467
 
615
- ### Registration
616
-
617
- Register your stream function:
618
-
619
- ```typescript
620
- pi.registerProvider("my-provider", {
621
- baseUrl: "https://api.example.com",
622
- apiKey: "$MY_API_KEY",
623
- api: "my-custom-api",
624
- models: [...],
625
- streamSimple: streamMyProvider
626
- });
627
- ```
468
+ ## Testing
628
469
 
629
- ## Testing Your Implementation
470
+ Use the local examples as references rather than copying pi-ai's private implementation or test sources. Test a custom stream directly by iterating its public `AssistantMessageEventStream`, then awaiting `stream.result()` for the final message.
630
471
 
631
- Test your provider against the same test suites used by built-in providers. Copy and adapt these test files from [packages/ai/test/](https://github.com/earendil-works/pi/tree/v0.82.1/packages/ai/test):
472
+ At minimum, cover:
632
473
 
633
- | Test | Purpose |
634
- |------|---------|
635
- | `stream.test.ts` | Basic streaming, text output |
636
- | `tokens.test.ts` | Token counting and usage |
637
- | `abort.test.ts` | AbortSignal handling |
638
- | `empty.test.ts` | Empty/minimal responses |
639
- | `context-overflow.test.ts` | Context window limits |
640
- | `image-limits.test.ts` | Image input handling |
641
- | `unicode-surrogate.test.ts` | Unicode edge cases |
642
- | `tool-call-without-result.test.ts` | Tool call edge cases |
643
- | `image-tool-result.test.ts` | Images in tool results |
644
- | `total-tokens.test.ts` | Total token calculation |
645
- | `cross-provider-handoff.test.ts` | Context handoff between providers |
474
+ - registration and discovery with `pi -e path/to/extension --list-models`;
475
+ - normal text, thinking, and tool-call event ordering, including fragmented tool JSON;
476
+ - final usage/cost values, empty responses, provider errors, and aborts;
477
+ - resolved API keys and headers, plus payload/response hook forwarding for a stream that makes HTTP itself;
478
+ - the configured context window and overflow normalization with `isContextOverflow(message, model.contextWindow)`;
479
+ - one interactive request with the selected provider, including a tool call if the provider claims tool support.
646
480
 
647
- Run tests with your provider/model pairs to verify compatibility.
481
+ The two example extensions above cover the two main test seams: direct custom event production and delegation to Pi AI's public streaming APIs.
648
482
 
649
483
  ## Config Reference
650
484
 
@@ -682,7 +516,7 @@ interface ProviderConfig {
682
516
  oauth?: {
683
517
  name: string;
684
518
  login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials>;
685
- refreshToken(credentials: OAuthCredentials): Promise<OAuthCredentials>;
519
+ refreshToken(credentials: OAuthCredentials, signal: AbortSignal): Promise<OAuthCredentials>;
686
520
  getApiKey(credentials: OAuthCredentials): string;
687
521
  };
688
522
  }
@@ -737,6 +571,7 @@ interface ProviderModelConfig {
737
571
  supportsDeveloperRole?: boolean;
738
572
  supportsReasoningEffort?: boolean;
739
573
  supportsUsageInStreaming?: boolean;
574
+ supportsFinishReason?: boolean;
740
575
  supportsStrictMode?: boolean;
741
576
  supportsOpenAIGrammarTools?: boolean; // openai-completions/openai-responses; false falls back to normal function tools
742
577
  maxTokensField?: "max_completion_tokens" | "max_tokens";
@@ -744,8 +579,9 @@ interface ProviderModelConfig {
744
579
  requiresAssistantAfterToolResult?: boolean;
745
580
  requiresThinkingAsText?: boolean;
746
581
  requiresReasoningContentOnAssistantMessages?: boolean;
747
- thinkingFormat?: "openai" | "openrouter" | "deepseek" | "together" | "zai" | "qwen" | "chat-template" | "qwen-chat-template" | "string-thinking" | "ant-ling";
582
+ thinkingFormat?: "openai" | "openrouter" | "deepseek" | "together" | "baseten" | "zai" | "qwen" | "chat-template" | "qwen-chat-template" | "string-thinking" | "ant-ling";
748
583
  chatTemplateKwargs?: Record<string, string | number | boolean | null | { "$var": "thinking.enabled" | "thinking.effort"; omitWhenOff?: boolean }>;
584
+ chatTemplateArgs?: Record<string, string | number | boolean | null | { "$var": "thinking.enabled" | "thinking.effort"; omitWhenOff?: boolean }>;
749
585
  cacheControlFormat?: "anthropic";
750
586
  sessionAffinityFormat?: "openai" | "openai-nosession" | "openrouter";
751
587
  sendSessionAffinityHeaders?: boolean;
@@ -762,5 +598,5 @@ interface ProviderModelConfig {
762
598
  }
763
599
  ```
764
600
 
765
- `openrouter` sends `reasoning: { effort }`. `deepseek` sends `thinking: { type: "enabled" | "disabled" }` and `reasoning_effort` when enabled. `together` sends `reasoning: { enabled }` and also `reasoning_effort` when `supportsReasoningEffort` is enabled. `qwen` is for DashScope-style top-level `enable_thinking`. Use `qwen-chat-template` for local Qwen-compatible servers that read `chat_template_kwargs.enable_thinking` and need `preserve_thinking`. Use `chat-template` for configurable `chat_template_kwargs`, for example DeepSeek V3.x behind vLLM with `chatTemplateKwargs: { "thinking": { "$var": "thinking.enabled" } }`.
601
+ `openrouter` sends `reasoning: { effort }`. `deepseek` sends `thinking: { type: "enabled" | "disabled" }` and `reasoning_effort` when enabled. `together` sends `reasoning: { enabled }` and also `reasoning_effort` when `supportsReasoningEffort` is enabled. `qwen` is for DashScope-style top-level `enable_thinking`. Use `qwen-chat-template` for local Qwen-compatible servers that read `chat_template_kwargs.enable_thinking` and need `preserve_thinking`. Use `chat-template` for configurable `chat_template_kwargs`, for example DeepSeek V3.x behind vLLM with `chatTemplateKwargs: { "thinking": { "$var": "thinking.enabled" } }`. Use `thinkingFormat: "baseten"` with `chatTemplateArgs` when the provider expects toggle values under `chat_template_args` and optionally supports top-level `reasoning_effort`.
766
602
  `cacheControlFormat: "anthropic"` applies Anthropic-style `cache_control` markers to the system prompt, last tool definition, and last user, assistant, or tool-result text content.