@psnext/slingcli 2.4.20260526-1 → 2.4.20260527-2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. package/bin/sling.js +4 -1
  2. package/node_modules/@aws-sdk/core/package.json +2 -2
  3. package/node_modules/@aws-sdk/credential-provider-env/package.json +2 -2
  4. package/node_modules/@aws-sdk/credential-provider-http/dist-cjs/fromHttp/fromHttp.js +12 -6
  5. package/node_modules/@aws-sdk/credential-provider-http/dist-es/fromHttp/fromHttp.js +12 -6
  6. package/node_modules/@aws-sdk/credential-provider-http/package.json +3 -2
  7. package/node_modules/@aws-sdk/credential-provider-ini/package.json +9 -9
  8. package/node_modules/@aws-sdk/credential-provider-login/package.json +3 -3
  9. package/node_modules/@aws-sdk/credential-provider-node/package.json +7 -7
  10. package/node_modules/@aws-sdk/credential-provider-process/package.json +2 -2
  11. package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/package.json +3 -3
  12. package/node_modules/@aws-sdk/credential-provider-sso/package.json +4 -4
  13. package/node_modules/@aws-sdk/credential-provider-web-identity/package.json +3 -3
  14. package/node_modules/@aws-sdk/middleware-websocket/package.json +2 -2
  15. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/cognito-identity/index.js +1 -1
  16. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/signin/index.js +1 -1
  17. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sso/index.js +1 -1
  18. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sso-oidc/index.js +1 -1
  19. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sts/index.js +1 -1
  20. package/node_modules/@aws-sdk/nested-clients/package.json +3 -3
  21. package/node_modules/@aws-sdk/signature-v4-multi-region/package.json +1 -2
  22. package/node_modules/@aws-sdk/xml-builder/package.json +2 -2
  23. package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.js +18 -24
  24. package/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
  25. package/node_modules/@earendil-works/pi-ai/dist/cli.js +14 -0
  26. package/node_modules/@earendil-works/pi-ai/dist/models.generated.js +73 -58
  27. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.js +2 -1
  28. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.js +8 -22
  29. package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.js +1 -1
  30. package/node_modules/@earendil-works/pi-ai/dist/providers/images/openrouter.js +1 -1
  31. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex-responses.js +60 -40
  32. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.js +1 -1
  33. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses.js +1 -1
  34. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/device-code.js +55 -0
  35. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/github-copilot.js +45 -69
  36. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/index.js +1 -0
  37. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js +5 -2
  38. package/node_modules/@earendil-works/pi-ai/package.json +4 -3
  39. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/file-processor.js +2 -3
  40. package/node_modules/@earendil-works/pi-coding-agent/dist/config.js +18 -12
  41. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session-runtime.js +2 -1
  42. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session-services.js +3 -2
  43. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js +8 -2
  44. package/node_modules/@earendil-works/pi-coding-agent/dist/core/auth-storage.js +2 -1
  45. package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.js +18 -24
  46. package/node_modules/@earendil-works/pi-coding-agent/dist/core/export-html/index.js +8 -6
  47. package/node_modules/@earendil-works/pi-coding-agent/dist/core/export-html/template.js +6 -3
  48. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/loader.js +12 -29
  49. package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-registry.js +5 -1
  50. package/node_modules/@earendil-works/pi-coding-agent/dist/core/output-guard.js +52 -22
  51. package/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.js +63 -36
  52. package/node_modules/@earendil-works/pi-coding-agent/dist/core/prompt-templates.js +6 -20
  53. package/node_modules/@earendil-works/pi-coding-agent/dist/core/resource-loader.js +38 -31
  54. package/node_modules/@earendil-works/pi-coding-agent/dist/core/sdk.js +9 -4
  55. package/node_modules/@earendil-works/pi-coding-agent/dist/core/session-manager.js +32 -24
  56. package/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.js +6 -13
  57. package/node_modules/@earendil-works/pi-coding-agent/dist/core/skills.js +8 -22
  58. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/bash.js +54 -53
  59. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/edit-diff.js +8 -1
  60. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/edit.js +44 -81
  61. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/file-mutation-queue.js +27 -12
  62. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/find.js +2 -3
  63. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/grep.js +3 -3
  64. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/ls.js +5 -5
  65. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/output-accumulator.js +9 -3
  66. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/path-utils.js +39 -21
  67. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/read.js +9 -8
  68. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/truncate.js +12 -2
  69. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/write.js +20 -35
  70. package/node_modules/@earendil-works/pi-coding-agent/dist/main.js +5 -6
  71. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/config-selector.js +1 -1
  72. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/footer.js +14 -5
  73. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +29 -4
  74. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/user-message.js +1 -1
  75. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js +4 -4
  76. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/theme/theme.js +37 -28
  77. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-client.js +64 -7
  78. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-mode.js +15 -3
  79. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/clipboard-native.js +14 -8
  80. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/image-resize-core.js +124 -0
  81. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/image-resize-worker.js +31 -0
  82. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/image-resize.js +75 -115
  83. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/paths.js +33 -7
  84. package/node_modules/@earendil-works/pi-coding-agent/npm-shrinkwrap.json +13 -26
  85. package/node_modules/@earendil-works/pi-coding-agent/package.json +6 -6
  86. package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +40 -55
  87. package/node_modules/@earendil-works/pi-tui/dist/components/input.js +2 -2
  88. package/node_modules/@earendil-works/pi-tui/dist/components/markdown.js +12 -2
  89. package/node_modules/@earendil-works/pi-tui/dist/native-modifiers.js +53 -0
  90. package/node_modules/@earendil-works/pi-tui/dist/terminal-image.js +4 -1
  91. package/node_modules/@earendil-works/pi-tui/dist/terminal.js +38 -16
  92. package/node_modules/@earendil-works/pi-tui/dist/utils.js +19 -12
  93. package/node_modules/@earendil-works/pi-tui/native/darwin/prebuilds/darwin-arm64/darwin-modifiers.node +0 -0
  94. package/node_modules/@earendil-works/pi-tui/native/darwin/prebuilds/darwin-x64/darwin-modifiers.node +0 -0
  95. package/node_modules/@earendil-works/pi-tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
  96. package/node_modules/@earendil-works/pi-tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
  97. package/node_modules/@earendil-works/pi-tui/package.json +3 -5
  98. package/node_modules/@smithy/node-http-handler/package.json +2 -2
  99. package/package.json +6 -6
  100. package/slingshot/index.js +246 -244
  101. package/node_modules/koffi/CHANGELOG.md +0 -1093
  102. package/node_modules/koffi/LICENSE.txt +0 -22
  103. package/node_modules/koffi/README.md +0 -43
  104. package/node_modules/koffi/build/koffi/darwin_arm64/koffi.node +0 -0
  105. package/node_modules/koffi/build/koffi/darwin_x64/koffi.node +0 -0
  106. package/node_modules/koffi/build/koffi/freebsd_arm64/koffi.node +0 -0
  107. package/node_modules/koffi/build/koffi/freebsd_ia32/koffi.node +0 -0
  108. package/node_modules/koffi/build/koffi/freebsd_x64/koffi.node +0 -0
  109. package/node_modules/koffi/build/koffi/linux_arm64/koffi.node +0 -0
  110. package/node_modules/koffi/build/koffi/linux_armhf/koffi.node +0 -0
  111. package/node_modules/koffi/build/koffi/linux_ia32/koffi.node +0 -0
  112. package/node_modules/koffi/build/koffi/linux_loong64/koffi.node +0 -0
  113. package/node_modules/koffi/build/koffi/linux_riscv64d/koffi.node +0 -0
  114. package/node_modules/koffi/build/koffi/linux_x64/koffi.node +0 -0
  115. package/node_modules/koffi/build/koffi/musl_arm64/koffi.node +0 -0
  116. package/node_modules/koffi/build/koffi/musl_x64/koffi.node +0 -0
  117. package/node_modules/koffi/build/koffi/openbsd_ia32/koffi.node +0 -0
  118. package/node_modules/koffi/build/koffi/openbsd_x64/koffi.node +0 -0
  119. package/node_modules/koffi/build/koffi/win32_arm64/koffi.exp +0 -0
  120. package/node_modules/koffi/build/koffi/win32_arm64/koffi.lib +0 -0
  121. package/node_modules/koffi/build/koffi/win32_arm64/koffi.node +0 -0
  122. package/node_modules/koffi/build/koffi/win32_ia32/koffi.exp +0 -0
  123. package/node_modules/koffi/build/koffi/win32_ia32/koffi.lib +0 -0
  124. package/node_modules/koffi/build/koffi/win32_ia32/koffi.node +0 -0
  125. package/node_modules/koffi/build/koffi/win32_x64/koffi.exp +0 -0
  126. package/node_modules/koffi/build/koffi/win32_x64/koffi.lib +0 -0
  127. package/node_modules/koffi/build/koffi/win32_x64/koffi.node +0 -0
  128. package/node_modules/koffi/doc/benchmarks.md +0 -126
  129. package/node_modules/koffi/doc/callbacks.md +0 -210
  130. package/node_modules/koffi/doc/contribute.md +0 -148
  131. package/node_modules/koffi/doc/functions.md +0 -250
  132. package/node_modules/koffi/doc/index.md +0 -61
  133. package/node_modules/koffi/doc/input.md +0 -471
  134. package/node_modules/koffi/doc/migration.md +0 -159
  135. package/node_modules/koffi/doc/misc.md +0 -180
  136. package/node_modules/koffi/doc/output.md +0 -305
  137. package/node_modules/koffi/doc/packaging.md +0 -88
  138. package/node_modules/koffi/doc/platforms.md +0 -36
  139. package/node_modules/koffi/doc/pointers.md +0 -328
  140. package/node_modules/koffi/doc/start.md +0 -118
  141. package/node_modules/koffi/doc/unions.md +0 -186
  142. package/node_modules/koffi/doc/variables.md +0 -102
  143. package/node_modules/koffi/index.d.ts +0 -288
  144. package/node_modules/koffi/index.js +0 -634
  145. package/node_modules/koffi/indirect.js +0 -533
  146. package/node_modules/koffi/lib/native/base/base.cc +0 -11015
  147. package/node_modules/koffi/lib/native/base/base.hh +0 -6003
  148. package/node_modules/koffi/lib/native/base/crc.inc +0 -2214
  149. package/node_modules/koffi/lib/native/base/crc_gen.py +0 -72
  150. package/node_modules/koffi/lib/native/base/mimetypes.inc +0 -1248
  151. package/node_modules/koffi/lib/native/base/mimetypes_gen.py +0 -58
  152. package/node_modules/koffi/lib/native/base/tower.cc +0 -821
  153. package/node_modules/koffi/lib/native/base/tower.hh +0 -81
  154. package/node_modules/koffi/lib/native/base/unicode.inc +0 -408
  155. package/node_modules/koffi/lib/native/base/unicode_gen.py +0 -152
  156. package/node_modules/koffi/package.json +0 -38
  157. package/node_modules/koffi/src/cnoke/LICENSE.txt +0 -22
  158. package/node_modules/koffi/src/cnoke/README.md +0 -99
  159. package/node_modules/koffi/src/cnoke/assets/FindCNoke.cmake +0 -127
  160. package/node_modules/koffi/src/cnoke/assets/toolchains.json +0 -126
  161. package/node_modules/koffi/src/cnoke/assets/win_delay_hook.c +0 -36
  162. package/node_modules/koffi/src/cnoke/cnoke.js +0 -170
  163. package/node_modules/koffi/src/cnoke/package.json +0 -24
  164. package/node_modules/koffi/src/cnoke/src/builder.js +0 -511
  165. package/node_modules/koffi/src/cnoke/src/index.js +0 -10
  166. package/node_modules/koffi/src/cnoke/src/tools.js +0 -407
  167. package/node_modules/koffi/src/koffi/CMakeLists.txt +0 -182
  168. package/node_modules/koffi/src/koffi/src/abi_arm32.cc +0 -1018
  169. package/node_modules/koffi/src/koffi/src/abi_arm32_asm.S +0 -169
  170. package/node_modules/koffi/src/koffi/src/abi_arm64.cc +0 -1295
  171. package/node_modules/koffi/src/koffi/src/abi_arm64_asm.S +0 -195
  172. package/node_modules/koffi/src/koffi/src/abi_arm64_asm.asm +0 -174
  173. package/node_modules/koffi/src/koffi/src/abi_loong64.cc +0 -5
  174. package/node_modules/koffi/src/koffi/src/abi_loong64_asm.S +0 -204
  175. package/node_modules/koffi/src/koffi/src/abi_riscv64.cc +0 -915
  176. package/node_modules/koffi/src/koffi/src/abi_riscv64_asm.S +0 -203
  177. package/node_modules/koffi/src/koffi/src/abi_x64_sysv.cc +0 -939
  178. package/node_modules/koffi/src/koffi/src/abi_x64_sysv_asm.S +0 -231
  179. package/node_modules/koffi/src/koffi/src/abi_x64_win.cc +0 -715
  180. package/node_modules/koffi/src/koffi/src/abi_x64_win_asm.S +0 -166
  181. package/node_modules/koffi/src/koffi/src/abi_x64_win_asm.asm +0 -192
  182. package/node_modules/koffi/src/koffi/src/abi_x86.cc +0 -860
  183. package/node_modules/koffi/src/koffi/src/abi_x86_asm.S +0 -193
  184. package/node_modules/koffi/src/koffi/src/abi_x86_asm.asm +0 -177
  185. package/node_modules/koffi/src/koffi/src/call.cc +0 -1326
  186. package/node_modules/koffi/src/koffi/src/call.hh +0 -179
  187. package/node_modules/koffi/src/koffi/src/errno.inc +0 -462
  188. package/node_modules/koffi/src/koffi/src/ffi.cc +0 -2702
  189. package/node_modules/koffi/src/koffi/src/ffi.hh +0 -354
  190. package/node_modules/koffi/src/koffi/src/init.js +0 -105
  191. package/node_modules/koffi/src/koffi/src/parser.cc +0 -220
  192. package/node_modules/koffi/src/koffi/src/parser.hh +0 -54
  193. package/node_modules/koffi/src/koffi/src/util.cc +0 -1807
  194. package/node_modules/koffi/src/koffi/src/util.hh +0 -221
  195. package/node_modules/koffi/src/koffi/src/uv.cc +0 -193
  196. package/node_modules/koffi/src/koffi/src/uv.def +0 -10
  197. package/node_modules/koffi/src/koffi/src/uv.hh +0 -40
  198. package/node_modules/koffi/src/koffi/src/win32.cc +0 -198
  199. package/node_modules/koffi/src/koffi/src/win32.hh +0 -119
  200. package/node_modules/koffi/src/koffi/tools/write_trampolines.js +0 -77
  201. package/node_modules/koffi/vendor/node-addon-api/LICENSE.md +0 -9
  202. package/node_modules/koffi/vendor/node-addon-api/README.md +0 -95
  203. package/node_modules/koffi/vendor/node-addon-api/napi-inl.deprecated.h +0 -186
  204. package/node_modules/koffi/vendor/node-addon-api/napi-inl.h +0 -7033
  205. package/node_modules/koffi/vendor/node-addon-api/napi.h +0 -3309
  206. package/node_modules/koffi/vendor/node-api-headers/LICENSE +0 -21
  207. package/node_modules/koffi/vendor/node-api-headers/README.md +0 -95
  208. package/node_modules/koffi/vendor/node-api-headers/def/js_native_api.def +0 -125
  209. package/node_modules/koffi/vendor/node-api-headers/def/node_api.def +0 -157
  210. package/node_modules/koffi/vendor/node-api-headers/include/js_native_api.h +0 -591
  211. package/node_modules/koffi/vendor/node-api-headers/include/js_native_api_types.h +0 -210
  212. package/node_modules/koffi/vendor/node-api-headers/include/node_api.h +0 -265
  213. package/node_modules/koffi/vendor/node-api-headers/include/node_api_types.h +0 -58
  214. package/node_modules/koffi/vendor/node-api-headers/include/uv/aix.h +0 -32
  215. package/node_modules/koffi/vendor/node-api-headers/include/uv/bsd.h +0 -34
  216. package/node_modules/koffi/vendor/node-api-headers/include/uv/darwin.h +0 -61
  217. package/node_modules/koffi/vendor/node-api-headers/include/uv/errno.h +0 -483
  218. package/node_modules/koffi/vendor/node-api-headers/include/uv/linux.h +0 -34
  219. package/node_modules/koffi/vendor/node-api-headers/include/uv/os390.h +0 -33
  220. package/node_modules/koffi/vendor/node-api-headers/include/uv/posix.h +0 -31
  221. package/node_modules/koffi/vendor/node-api-headers/include/uv/sunos.h +0 -44
  222. package/node_modules/koffi/vendor/node-api-headers/include/uv/threadpool.h +0 -37
  223. package/node_modules/koffi/vendor/node-api-headers/include/uv/tree.h +0 -521
  224. package/node_modules/koffi/vendor/node-api-headers/include/uv/unix.h +0 -512
  225. package/node_modules/koffi/vendor/node-api-headers/include/uv/version.h +0 -43
  226. package/node_modules/koffi/vendor/node-api-headers/include/uv/win.h +0 -698
  227. package/node_modules/koffi/vendor/node-api-headers/include/uv.h +0 -1990
@@ -29,9 +29,10 @@ import { buildBaseOptions } from "./simple-options.js";
29
29
  // ============================================================================
30
30
  const DEFAULT_CODEX_BASE_URL = "https://chatgpt.com/backend-api";
31
31
  const JWT_CLAIM_PATH = "https://api.openai.com/auth";
32
- const MAX_RETRIES = 3;
32
+ const DEFAULT_MAX_RETRIES = 0;
33
33
  const BASE_DELAY_MS = 1000;
34
- const CODEX_TOOL_CALL_PROVIDERS = new Set(["openai", "openai-codex", "opencode", "slingshot"]);
34
+ const DEFAULT_MAX_RETRY_DELAY_MS = 60_000;
35
+ const CODEX_TOOL_CALL_PROVIDERS = new Set(["openai", "openai-codex", "opencode"]);
35
36
  const WEBSOCKET_MESSAGE_TOO_BIG_CLOSE_CODE = 1009;
36
37
  const CODEX_RESPONSE_STATUSES = new Set([
37
38
  "completed",
@@ -44,12 +45,44 @@ const CODEX_RESPONSE_STATUSES = new Set([
44
45
  // ============================================================================
45
46
  // Retry Helpers
46
47
  // ============================================================================
48
+ function isTerminalRateLimitError(errorText) {
49
+ return /GoUsageLimitError|FreeUsageLimitError|Monthly usage limit reached|available balance|insufficient_quota|out of budget|quota exceeded|billing/i.test(errorText);
50
+ }
47
51
  function isRetryableError(status, errorText) {
52
+ if (status === 429 && isTerminalRateLimitError(errorText)) {
53
+ return false;
54
+ }
48
55
  if (status === 429 || status === 500 || status === 502 || status === 503 || status === 504) {
49
56
  return true;
50
57
  }
51
58
  return /rate.?limit|overloaded|service.?unavailable|upstream.?connect|connection.?refused/i.test(errorText);
52
59
  }
60
+ function getRetryAfterDelayMs(headers) {
61
+ const retryAfterMs = headers.get("retry-after-ms");
62
+ if (retryAfterMs !== null) {
63
+ const millis = Number(retryAfterMs);
64
+ if (Number.isFinite(millis)) {
65
+ return Math.max(0, millis);
66
+ }
67
+ }
68
+ const retryAfter = headers.get("retry-after");
69
+ if (!retryAfter) {
70
+ return undefined;
71
+ }
72
+ const seconds = Number(retryAfter);
73
+ if (Number.isFinite(seconds)) {
74
+ return Math.max(0, seconds * 1000);
75
+ }
76
+ const date = Date.parse(retryAfter);
77
+ if (!Number.isNaN(date)) {
78
+ return Math.max(0, date - Date.now());
79
+ }
80
+ return undefined;
81
+ }
82
+ function capRetryDelayMs(delayMs, options) {
83
+ const maxRetryDelayMs = options?.maxRetryDelayMs ?? DEFAULT_MAX_RETRY_DELAY_MS;
84
+ return maxRetryDelayMs > 0 ? Math.min(delayMs, maxRetryDelayMs) : delayMs;
85
+ }
53
86
  function sleep(ms, signal) {
54
87
  return new Promise((resolve, reject) => {
55
88
  if (signal?.aborted) {
@@ -91,7 +124,7 @@ export const streamOpenAICodexResponses = (model, context, options) => {
91
124
  if (!apiKey) {
92
125
  throw new Error(`No API key for provider: ${model.provider}`);
93
126
  }
94
- const accountId = extractAccountId(apiKey) || "";
127
+ const accountId = extractAccountId(apiKey);
95
128
  let body = buildRequestBody(model, context, options);
96
129
  const nextBody = await options?.onPayload?.(body, model);
97
130
  if (nextBody !== undefined) {
@@ -145,7 +178,8 @@ export const streamOpenAICodexResponses = (model, context, options) => {
145
178
  // Fetch with retry logic for rate limits and transient errors
146
179
  let response;
147
180
  let lastError;
148
- for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
181
+ const maxRetries = options?.maxRetries ?? DEFAULT_MAX_RETRIES;
182
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
149
183
  if (options?.signal?.aborted) {
150
184
  throw new Error("Request was aborted");
151
185
  }
@@ -161,30 +195,13 @@ export const streamOpenAICodexResponses = (model, context, options) => {
161
195
  break;
162
196
  }
163
197
  const errorText = await response.text();
164
- if (attempt < MAX_RETRIES && isRetryableError(response.status, errorText)) {
165
- let delayMs = BASE_DELAY_MS * 2 ** attempt;
166
- const retryAfterMs = response.headers.get("retry-after-ms");
167
- if (retryAfterMs !== null) {
168
- const millis = Number(retryAfterMs);
169
- if (Number.isFinite(millis)) {
170
- delayMs = Math.max(0, millis);
171
- }
172
- }
173
- else {
174
- const retryAfter = response.headers.get("retry-after");
175
- if (retryAfter) {
176
- const seconds = Number(retryAfter);
177
- if (Number.isFinite(seconds)) {
178
- delayMs = Math.max(0, seconds * 1000);
179
- }
180
- else {
181
- const date = Date.parse(retryAfter);
182
- if (!Number.isNaN(date)) {
183
- delayMs = Math.max(0, date - Date.now());
184
- }
185
- }
186
- }
187
- }
198
+ if (attempt < maxRetries && isRetryableError(response.status, errorText)) {
199
+ const retryAfterDelayMs = getRetryAfterDelayMs(response.headers);
200
+ const delayMs = retryAfterDelayMs === undefined
201
+ ? BASE_DELAY_MS * 2 ** attempt
202
+ : response.status === 429
203
+ ? capRetryDelayMs(retryAfterDelayMs, options)
204
+ : retryAfterDelayMs;
188
205
  await sleep(delayMs, options?.signal);
189
206
  continue;
190
207
  }
@@ -204,7 +221,7 @@ export const streamOpenAICodexResponses = (model, context, options) => {
204
221
  }
205
222
  lastError = error instanceof Error ? error : new Error(String(error));
206
223
  // Network errors are retryable
207
- if (attempt < MAX_RETRIES && !lastError.message.includes("usage limit")) {
224
+ if (attempt < maxRetries && !lastError.message.includes("usage limit")) {
208
225
  const delayMs = BASE_DELAY_MS * 2 ** attempt;
209
226
  await sleep(delayMs, options?.signal);
210
227
  continue;
@@ -265,7 +282,7 @@ function buildRequestBody(model, context, options) {
265
282
  stream: true,
266
283
  instructions: context.systemPrompt || "You are a helpful assistant.",
267
284
  input: messages,
268
- text: { verbosity: options?.textVerbosity || "medium" },
285
+ text: { verbosity: options?.textVerbosity || "low" },
269
286
  include: ["reasoning.encrypted_content"],
270
287
  prompt_cache_key: clampOpenAIPromptCacheKey(options?.sessionId),
271
288
  tool_choice: "auto",
@@ -322,7 +339,11 @@ function resolveCodexServiceTier(responseServiceTier, requestServiceTier) {
322
339
  function resolveCodexUrl(baseUrl) {
323
340
  const raw = baseUrl && baseUrl.trim().length > 0 ? baseUrl : DEFAULT_CODEX_BASE_URL;
324
341
  const normalized = raw.replace(/\/+$/, "");
325
- return `${normalized}/responses`;
342
+ if (normalized.endsWith("/codex/responses"))
343
+ return normalized;
344
+ if (normalized.endsWith("/codex"))
345
+ return `${normalized}/responses`;
346
+ return `${normalized}/codex/responses`;
326
347
  }
327
348
  function resolveCodexWebSocketUrl(baseUrl) {
328
349
  const url = new URL(resolveCodexUrl(baseUrl));
@@ -1031,13 +1052,15 @@ function extractAccountId(token) {
1031
1052
  try {
1032
1053
  const parts = token.split(".");
1033
1054
  if (parts.length !== 3)
1034
- return null;
1055
+ throw new Error("Invalid token");
1035
1056
  const payload = JSON.parse(atob(parts[1]));
1036
1057
  const accountId = payload?.[JWT_CLAIM_PATH]?.chatgpt_account_id;
1037
- return accountId || null;
1058
+ if (!accountId)
1059
+ throw new Error("No account ID in token");
1060
+ return accountId;
1038
1061
  }
1039
1062
  catch {
1040
- return null;
1063
+ throw new Error("Failed to extract accountId from token");
1041
1064
  }
1042
1065
  }
1043
1066
  function createCodexRequestId() {
@@ -1052,10 +1075,7 @@ function buildBaseCodexHeaders(initHeaders, additionalHeaders, accountId, token)
1052
1075
  headers.set(key, value);
1053
1076
  }
1054
1077
  headers.set("Authorization", `Bearer ${token}`);
1055
- // Only set chatgpt-account-id if we have one (OpenAI-specific)
1056
- if (accountId) {
1057
- headers.set("chatgpt-account-id", accountId);
1058
- }
1078
+ headers.set("chatgpt-account-id", accountId);
1059
1079
  headers.set("originator", "pi");
1060
1080
  const userAgent = _os ? `pi (${_os.platform()} ${_os.release()}; ${_os.arch()})` : "pi (browser)";
1061
1081
  headers.set("User-Agent", userAgent);
@@ -1067,7 +1087,7 @@ function buildSSEHeaders(initHeaders, additionalHeaders, accountId, token, sessi
1067
1087
  headers.set("accept", "text/event-stream");
1068
1088
  headers.set("content-type", "application/json");
1069
1089
  if (sessionId) {
1070
- headers.set("session_id", sessionId);
1090
+ headers.set("session-id", sessionId);
1071
1091
  headers.set("x-client-request-id", sessionId);
1072
1092
  }
1073
1093
  return headers;
@@ -1080,7 +1100,7 @@ function buildWebSocketHeaders(initHeaders, additionalHeaders, accountId, token,
1080
1100
  headers.delete("openai-beta");
1081
1101
  headers.set("OpenAI-Beta", OPENAI_BETA_RESPONSES_WEBSOCKETS);
1082
1102
  headers.set("x-client-request-id", requestId);
1083
- headers.set("session_id", requestId);
1103
+ headers.set("session-id", requestId);
1084
1104
  return headers;
1085
1105
  }
1086
1106
  //# sourceMappingURL=openai-codex-responses.js.map
@@ -83,7 +83,7 @@ export const streamOpenAICompletions = (model, context, options) => {
83
83
  const requestOptions = {
84
84
  ...(options?.signal ? { signal: options.signal } : {}),
85
85
  ...(options?.timeoutMs !== undefined ? { timeout: options.timeoutMs } : {}),
86
- ...(options?.maxRetries !== undefined ? { maxRetries: options.maxRetries } : {}),
86
+ maxRetries: options?.maxRetries ?? 0,
87
87
  };
88
88
  const { data: openaiStream, response } = await client.chat.completions
89
89
  .create(params, requestOptions)
@@ -85,7 +85,7 @@ export const streamOpenAIResponses = (model, context, options) => {
85
85
  const requestOptions = {
86
86
  ...(options?.signal ? { signal: options.signal } : {}),
87
87
  ...(options?.timeoutMs !== undefined ? { timeout: options.timeoutMs } : {}),
88
- ...(options?.maxRetries !== undefined ? { maxRetries: options.maxRetries } : {}),
88
+ maxRetries: options?.maxRetries ?? 0,
89
89
  };
90
90
  const { data: openaiStream, response } = await client.responses.create(params, requestOptions).withResponse();
91
91
  await options?.onResponse?.({ status: response.status, headers: headersToRecord(response.headers) }, model);
@@ -0,0 +1,55 @@
1
+ const CANCEL_MESSAGE = "Login cancelled";
2
+ const TIMEOUT_MESSAGE = "Device flow timed out";
3
+ const SLOW_DOWN_TIMEOUT_MESSAGE = "Device flow timed out after one or more slow_down responses. This is often caused by clock drift in WSL or VM environments. Please sync or restart the VM clock and try again.";
4
+ const MINIMUM_INTERVAL_MS = 1000;
5
+ // RFC 8628 section 3.2: if the authorization server omits `interval`, the client must use 5 seconds.
6
+ const DEFAULT_POLL_INTERVAL_SECONDS = 5;
7
+ // RFC 8628 section 3.5: `slow_down` means the polling interval must increase by 5 seconds.
8
+ const SLOW_DOWN_INTERVAL_INCREMENT_MS = 5000;
9
+ function abortableSleep(ms, signal, cancelMessage) {
10
+ return new Promise((resolve, reject) => {
11
+ if (signal?.aborted) {
12
+ reject(new Error(cancelMessage));
13
+ return;
14
+ }
15
+ const onAbort = () => {
16
+ clearTimeout(timeout);
17
+ reject(new Error(cancelMessage));
18
+ };
19
+ const timeout = setTimeout(() => {
20
+ signal?.removeEventListener("abort", onAbort);
21
+ resolve();
22
+ }, ms);
23
+ signal?.addEventListener("abort", onAbort, { once: true });
24
+ });
25
+ }
26
+ export async function pollOAuthDeviceCodeFlow(options) {
27
+ const deadline = typeof options.expiresInSeconds === "number"
28
+ ? Date.now() + options.expiresInSeconds * 1000
29
+ : Number.POSITIVE_INFINITY;
30
+ let intervalMs = Math.max(MINIMUM_INTERVAL_MS, Math.floor((options.intervalSeconds ?? DEFAULT_POLL_INTERVAL_SECONDS) * 1000));
31
+ let slowDownResponses = 0;
32
+ while (Date.now() < deadline) {
33
+ if (options.signal?.aborted) {
34
+ throw new Error(CANCEL_MESSAGE);
35
+ }
36
+ const remainingMs = deadline - Date.now();
37
+ await abortableSleep(Math.min(intervalMs, remainingMs), options.signal, CANCEL_MESSAGE);
38
+ const result = await options.poll();
39
+ if (result.status === "complete") {
40
+ return result.accessToken;
41
+ }
42
+ if (result.status === "pending") {
43
+ continue;
44
+ }
45
+ if (result.status === "slow_down") {
46
+ slowDownResponses += 1;
47
+ // RFC 8628 section 3.5: apply this increase to this and all subsequent requests.
48
+ intervalMs = Math.max(MINIMUM_INTERVAL_MS, intervalMs + SLOW_DOWN_INTERVAL_INCREMENT_MS);
49
+ continue;
50
+ }
51
+ throw new Error(result.message);
52
+ }
53
+ throw new Error(slowDownResponses > 0 ? SLOW_DOWN_TIMEOUT_MESSAGE : TIMEOUT_MESSAGE);
54
+ }
55
+ //# sourceMappingURL=device-code.js.map
@@ -2,6 +2,7 @@
2
2
  * GitHub Copilot OAuth flow
3
3
  */
4
4
  import { getModels } from "../../models.js";
5
+ import { pollOAuthDeviceCodeFlow } from "./device-code.js";
5
6
  const decode = (s) => atob(s);
6
7
  const CLIENT_ID = decode("SXYxLmI1MDdhMDhjODdlY2ZlOTg=");
7
8
  const COPILOT_HEADERS = {
@@ -10,8 +11,6 @@ const COPILOT_HEADERS = {
10
11
  "Editor-Plugin-Version": "copilot-chat/0.35.0",
11
12
  "Copilot-Integration-Id": "vscode-chat",
12
13
  };
13
- const INITIAL_POLL_INTERVAL_MULTIPLIER = 1.2;
14
- const SLOW_DOWN_POLL_INTERVAL_MULTIPLIER = 1.4;
15
14
  export function normalizeDomain(input) {
16
15
  const trimmed = input.trim();
17
16
  if (!trimmed)
@@ -90,7 +89,7 @@ async function startDeviceFlow(domain) {
90
89
  if (typeof deviceCode !== "string" ||
91
90
  typeof userCode !== "string" ||
92
91
  typeof verificationUri !== "string" ||
93
- typeof interval !== "number" ||
92
+ (interval !== undefined && typeof interval !== "number") ||
94
93
  typeof expiresIn !== "number") {
95
94
  throw new Error("Invalid device code response fields");
96
95
  }
@@ -102,71 +101,43 @@ async function startDeviceFlow(domain) {
102
101
  expires_in: expiresIn,
103
102
  };
104
103
  }
105
- /**
106
- * Sleep that can be interrupted by an AbortSignal
107
- */
108
- function abortableSleep(ms, signal) {
109
- return new Promise((resolve, reject) => {
110
- if (signal?.aborted) {
111
- reject(new Error("Login cancelled"));
112
- return;
113
- }
114
- const timeout = setTimeout(resolve, ms);
115
- signal?.addEventListener("abort", () => {
116
- clearTimeout(timeout);
117
- reject(new Error("Login cancelled"));
118
- }, { once: true });
119
- });
120
- }
121
- async function pollForGitHubAccessToken(domain, deviceCode, intervalSeconds, expiresIn, signal) {
104
+ async function pollForGitHubAccessToken(domain, device, signal) {
122
105
  const urls = getUrls(domain);
123
- const deadline = Date.now() + expiresIn * 1000;
124
- let intervalMs = Math.max(1000, Math.floor(intervalSeconds * 1000));
125
- let intervalMultiplier = INITIAL_POLL_INTERVAL_MULTIPLIER;
126
- let slowDownResponses = 0;
127
- while (Date.now() < deadline) {
128
- if (signal?.aborted) {
129
- throw new Error("Login cancelled");
130
- }
131
- const remainingMs = deadline - Date.now();
132
- const waitMs = Math.min(Math.ceil(intervalMs * intervalMultiplier), remainingMs);
133
- await abortableSleep(waitMs, signal);
134
- const raw = await fetchJson(urls.accessTokenUrl, {
135
- method: "POST",
136
- headers: {
137
- Accept: "application/json",
138
- "Content-Type": "application/x-www-form-urlencoded",
139
- "User-Agent": "GitHubCopilotChat/0.35.0",
140
- },
141
- body: new URLSearchParams({
142
- client_id: CLIENT_ID,
143
- device_code: deviceCode,
144
- grant_type: "urn:ietf:params:oauth:grant-type:device_code",
145
- }),
146
- });
147
- if (raw && typeof raw === "object" && typeof raw.access_token === "string") {
148
- return raw.access_token;
149
- }
150
- if (raw && typeof raw === "object" && typeof raw.error === "string") {
151
- const { error, error_description: description, interval } = raw;
152
- if (error === "authorization_pending") {
153
- continue;
106
+ return pollOAuthDeviceCodeFlow({
107
+ intervalSeconds: device.interval,
108
+ expiresInSeconds: device.expires_in,
109
+ signal,
110
+ poll: async () => {
111
+ const raw = await fetchJson(urls.accessTokenUrl, {
112
+ method: "POST",
113
+ headers: {
114
+ Accept: "application/json",
115
+ "Content-Type": "application/x-www-form-urlencoded",
116
+ "User-Agent": "GitHubCopilotChat/0.35.0",
117
+ },
118
+ body: new URLSearchParams({
119
+ client_id: CLIENT_ID,
120
+ device_code: device.device_code,
121
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
122
+ }),
123
+ });
124
+ if (raw && typeof raw === "object" && typeof raw.access_token === "string") {
125
+ return { status: "complete", accessToken: raw.access_token };
154
126
  }
155
- if (error === "slow_down") {
156
- slowDownResponses += 1;
157
- intervalMs =
158
- typeof interval === "number" && interval > 0 ? interval * 1000 : Math.max(1000, intervalMs + 5000);
159
- intervalMultiplier = SLOW_DOWN_POLL_INTERVAL_MULTIPLIER;
160
- continue;
127
+ if (raw && typeof raw === "object" && typeof raw.error === "string") {
128
+ const { error, error_description: description } = raw;
129
+ if (error === "authorization_pending") {
130
+ return { status: "pending" };
131
+ }
132
+ if (error === "slow_down") {
133
+ return { status: "slow_down" };
134
+ }
135
+ const descriptionSuffix = description ? `: ${description}` : "";
136
+ return { status: "failed", message: `Device flow failed: ${error}${descriptionSuffix}` };
161
137
  }
162
- const descriptionSuffix = description ? `: ${description}` : "";
163
- throw new Error(`Device flow failed: ${error}${descriptionSuffix}`);
164
- }
165
- }
166
- if (slowDownResponses > 0) {
167
- throw new Error("Device flow timed out after one or more slow_down responses. This is often caused by clock drift in WSL or VM environments. Please sync or restart the VM clock and try again.");
168
- }
169
- throw new Error("Device flow timed out");
138
+ return { status: "failed", message: "Invalid device token response" };
139
+ },
140
+ });
170
141
  }
171
142
  /**
172
143
  * Refresh GitHub Copilot token
@@ -235,7 +206,7 @@ async function enableAllGitHubCopilotModels(token, enterpriseDomain, onProgress)
235
206
  /**
236
207
  * Login with GitHub Copilot OAuth (device code flow)
237
208
  *
238
- * @param options.onAuth - Callback with URL and optional instructions (user code)
209
+ * @param options.onDeviceCode - Callback with URL and user code
239
210
  * @param options.onPrompt - Callback to prompt user for input
240
211
  * @param options.onProgress - Optional progress callback
241
212
  * @param options.signal - Optional AbortSignal for cancellation
@@ -256,8 +227,13 @@ export async function loginGitHubCopilot(options) {
256
227
  }
257
228
  const domain = enterpriseDomain || "github.com";
258
229
  const device = await startDeviceFlow(domain);
259
- options.onAuth(device.verification_uri, `Enter code: ${device.user_code}`);
260
- const githubAccessToken = await pollForGitHubAccessToken(domain, device.device_code, device.interval, device.expires_in, options.signal);
230
+ options.onDeviceCode({
231
+ userCode: device.user_code,
232
+ verificationUri: device.verification_uri,
233
+ intervalSeconds: device.interval,
234
+ expiresInSeconds: device.expires_in,
235
+ });
236
+ const githubAccessToken = await pollForGitHubAccessToken(domain, device, options.signal);
261
237
  const credentials = await refreshGitHubCopilotToken(githubAccessToken, enterpriseDomain ?? undefined);
262
238
  // Enable all models after successful login
263
239
  options.onProgress?.("Enabling models...");
@@ -269,7 +245,7 @@ export const githubCopilotOAuthProvider = {
269
245
  name: "GitHub Copilot",
270
246
  async login(callbacks) {
271
247
  return loginGitHubCopilot({
272
- onAuth: (url, instructions) => callbacks.onAuth({ url, instructions }),
248
+ onDeviceCode: callbacks.onDeviceCode,
273
249
  onPrompt: callbacks.onPrompt,
274
250
  onProgress: callbacks.onProgress,
275
251
  signal: callbacks.signal,
@@ -8,6 +8,7 @@
8
8
  */
9
9
  // Anthropic
10
10
  export { anthropicOAuthProvider, loginAnthropic, refreshAnthropicToken } from "./anthropic.js";
11
+ export * from "./device-code.js";
11
12
  // GitHub Copilot
12
13
  export { getGitHubCopilotBaseUrl, githubCopilotOAuthProvider, loginGitHubCopilot, normalizeDomain, refreshGitHubCopilotToken, } from "./github-copilot.js";
13
14
  // OpenAI Codex (ChatGPT OAuth)
@@ -14,6 +14,7 @@
14
14
  * - xAI: "This model's maximum prompt length is 131072 but the request contains 537812 tokens"
15
15
  * - Groq: "Please reduce the length of the messages or completion"
16
16
  * - OpenRouter: "This endpoint's maximum context length is X tokens. However, you requested about Y tokens"
17
+ * - OpenRouter/Poolside: "Input length X exceeds the maximum allowed input length of Y tokens."
17
18
  * - Together AI: "The input (X tokens) is longer than the model's context length (Y tokens)."
18
19
  * - llama.cpp: "the request exceeds the available context size, try increasing it"
19
20
  * - LM Studio: "tokens to keep from the initial prompt is greater than the context length"
@@ -37,7 +38,8 @@ const OVERFLOW_PATTERNS = [
37
38
  /input token count.*exceeds the maximum/i, // Google (Gemini)
38
39
  /maximum prompt length is \d+/i, // xAI (Grok)
39
40
  /reduce the length of the messages/i, // Groq
40
- /maximum context length is \d+ tokens/i, // OpenRouter (all backends)
41
+ /maximum context length is \d+ tokens/i, // OpenRouter (most backends)
42
+ /exceeds (?:the )?maximum allowed input length of [\d,]+ tokens?/i, // OpenRouter/Poolside
41
43
  /input \(\d+ tokens\) is longer than the model'?s context length \(\d+ tokens\)/i, // Together AI
42
44
  /exceeds the limit of \d+/i, // GitHub Copilot
43
45
  /exceeds the available context size/i, // llama.cpp server
@@ -85,7 +87,8 @@ const NON_OVERFLOW_PATTERNS = [
85
87
  * - Groq: "reduce the length of the messages"
86
88
  * - Cerebras: 400/413 status code (no body)
87
89
  * - Mistral: "Prompt contains X tokens ... too large for model with Y maximum context length"
88
- * - OpenRouter (all backends): "maximum context length is X tokens"
90
+ * - OpenRouter (most backends): "maximum context length is X tokens"
91
+ * - OpenRouter/Poolside: "Input length X exceeds the maximum allowed input length of Y tokens."
89
92
  * - Together AI: "The input (X tokens) is longer than the model's context length (Y tokens)."
90
93
  * - llama.cpp: "exceeds the available context size"
91
94
  * - LM Studio: "greater than the context length"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@earendil-works/pi-ai",
3
- "version": "0.75.4",
3
+ "version": "0.75.5",
4
4
  "description": "Unified LLM API with automatic model discovery and provider configuration",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -60,8 +60,8 @@
60
60
  ],
61
61
  "scripts": {
62
62
  "clean": "shx rm -rf dist",
63
- "generate-models": "tsx scripts/generate-models.ts",
64
- "generate-image-models": "tsx scripts/generate-image-models.ts",
63
+ "generate-models": "node scripts/generate-models.ts",
64
+ "generate-image-models": "node scripts/generate-image-models.ts",
65
65
  "build": "npm run generate-models && npm run generate-image-models && tsgo -p tsconfig.build.json",
66
66
  "test": "vitest --run",
67
67
  "prepublishOnly": "npm run clean && npm run build"
@@ -69,6 +69,7 @@
69
69
  "dependencies": {
70
70
  "@anthropic-ai/sdk": "0.91.1",
71
71
  "@aws-sdk/client-bedrock-runtime": "3.1048.0",
72
+ "@smithy/node-http-handler": "4.7.3",
72
73
  "@google/genai": "1.52.0",
73
74
  "@mistralai/mistralai": "2.2.1",
74
75
  "http-proxy-agent": "7.0.2",
@@ -33,11 +33,10 @@ export async function processFileArguments(fileArgs, options) {
33
33
  if (mimeType) {
34
34
  // Handle image file
35
35
  const content = await readFile(absolutePath);
36
- const base64Content = content.toString("base64");
37
36
  let attachment;
38
37
  let dimensionNote;
39
38
  if (autoResizeImages) {
40
- const resized = await resizeImage({ type: "image", data: base64Content, mimeType });
39
+ const resized = await resizeImage(content, mimeType);
41
40
  if (!resized) {
42
41
  text += `<file name="${absolutePath}">[Image omitted: could not be resized below the inline image size limit.]</file>\n`;
43
42
  continue;
@@ -53,7 +52,7 @@ export async function processFileArguments(fileArgs, options) {
53
52
  attachment = {
54
53
  type: "image",
55
54
  mimeType,
56
- data: base64Content,
55
+ data: content.toString("base64"),
57
56
  };
58
57
  }
59
58
  images.push(attachment);
@@ -3,6 +3,7 @@ import { homedir } from "os";
3
3
  import { basename, dirname, join, resolve, sep, win32 } from "path";
4
4
  import { fileURLToPath } from "url";
5
5
  import { spawnProcessSync } from "./utils/child-process.js";
6
+ import { normalizePath } from "./utils/paths.js";
6
7
  // =============================================================================
7
8
  // Package Detection
8
9
  // =============================================================================
@@ -76,7 +77,13 @@ function getSelfUpdateCommandForMethod(method, installedPackageName, updatePacka
76
77
  case "bun-binary":
77
78
  return undefined;
78
79
  case "pnpm":
79
- return makeSelfUpdateCommand(makeSelfUpdateCommandStep("pnpm", ["install", "-g", "--ignore-scripts", updatePackageName]), updatePackageName === installedPackageName
80
+ return makeSelfUpdateCommand(makeSelfUpdateCommandStep("pnpm", [
81
+ "install",
82
+ "-g",
83
+ "--ignore-scripts",
84
+ "--config.minimumReleaseAge=0",
85
+ updatePackageName,
86
+ ]), updatePackageName === installedPackageName
80
87
  ? undefined
81
88
  : makeSelfUpdateCommandStep("pnpm", ["remove", "-g", installedPackageName]));
82
89
  case "yarn":
@@ -84,7 +91,13 @@ function getSelfUpdateCommandForMethod(method, installedPackageName, updatePacka
84
91
  ? undefined
85
92
  : makeSelfUpdateCommandStep("yarn", ["global", "remove", installedPackageName]));
86
93
  case "bun":
87
- return makeSelfUpdateCommand(makeSelfUpdateCommandStep("bun", ["install", "-g", "--ignore-scripts", updatePackageName]), updatePackageName === installedPackageName
94
+ return makeSelfUpdateCommand(makeSelfUpdateCommandStep("bun", [
95
+ "install",
96
+ "-g",
97
+ "--ignore-scripts",
98
+ "--minimum-release-age=0",
99
+ updatePackageName,
100
+ ]), updatePackageName === installedPackageName
88
101
  ? undefined
89
102
  : makeSelfUpdateCommandStep("bun", ["uninstall", "-g", installedPackageName]));
90
103
  case "npm": {
@@ -96,6 +109,7 @@ function getSelfUpdateCommandForMethod(method, installedPackageName, updatePacka
96
109
  "install",
97
110
  "-g",
98
111
  "--ignore-scripts",
112
+ "--min-release-age=0",
99
113
  updatePackageName,
100
114
  ]);
101
115
  const uninstallStep = updatePackageName === installedPackageName
@@ -261,11 +275,7 @@ export function getPackageDir() {
261
275
  // Allow override via environment variable (useful for Nix/Guix where store paths tokenize poorly)
262
276
  const envDir = process.env.PI_PACKAGE_DIR;
263
277
  if (envDir) {
264
- if (envDir === "~")
265
- return homedir();
266
- if (envDir.startsWith("~/"))
267
- return homedir() + envDir.slice(1);
268
- return envDir;
278
+ return normalizePath(envDir);
269
279
  }
270
280
  if (isBunBinary) {
271
281
  // Bun binary: process.execPath points to the compiled executable
@@ -360,11 +370,7 @@ export const VERSION = pkg.version || "0.0.0";
360
370
  export const ENV_AGENT_DIR = `${APP_NAME.toUpperCase()}_CODING_AGENT_DIR`;
361
371
  export const ENV_SESSION_DIR = `${APP_NAME.toUpperCase()}_CODING_AGENT_SESSION_DIR`;
362
372
  export function expandTildePath(path) {
363
- if (path === "~")
364
- return homedir();
365
- if (path.startsWith("~/"))
366
- return homedir() + path.slice(1);
367
- return path;
373
+ return normalizePath(path);
368
374
  }
369
375
  const DEFAULT_SHARE_VIEWER_URL = "https://pi.dev/session/";
370
376
  /** Get the share viewer URL for a gist ID */
@@ -1,5 +1,6 @@
1
1
  import { copyFileSync, existsSync, mkdirSync } from "node:fs";
2
2
  import { basename, join, resolve } from "node:path";
3
+ import { resolvePath } from "../utils/paths.js";
3
4
  import { emitSessionShutdownEvent } from "./extensions/runner.js";
4
5
  import { assertSessionCwdExists } from "./session-cwd.js";
5
6
  import { SessionManager } from "./session-manager.js";
@@ -246,7 +247,7 @@ export class AgentSessionRuntime {
246
247
  * @throws {MissingSessionCwdError} When the imported session cwd cannot be resolved and no override is provided.
247
248
  */
248
249
  async importFromJsonl(inputPath, cwdOverride) {
249
- const resolvedPath = resolve(inputPath);
250
+ const resolvedPath = resolvePath(inputPath);
250
251
  if (!existsSync(resolvedPath)) {
251
252
  throw new SessionImportFileNotFoundError(resolvedPath);
252
253
  }
@@ -1,5 +1,6 @@
1
1
  import { join } from "node:path";
2
2
  import { getAgentDir } from "../config.js";
3
+ import { resolvePath } from "../utils/paths.js";
3
4
  import { AuthStorage } from "./auth-storage.js";
4
5
  import { ModelRegistry } from "./model-registry.js";
5
6
  import { DefaultResourceLoader } from "./resource-loader.js";
@@ -51,8 +52,8 @@ function applyExtensionFlagValues(resourceLoader, extensionFlagValues) {
51
52
  * Returns services plus diagnostics. It does not create an AgentSession.
52
53
  */
53
54
  export async function createAgentSessionServices(options) {
54
- const cwd = options.cwd;
55
- const agentDir = options.agentDir ?? getAgentDir();
55
+ const cwd = resolvePath(options.cwd);
56
+ const agentDir = options.agentDir ? resolvePath(options.agentDir) : getAgentDir();
56
57
  const authStorage = options.authStorage ?? AuthStorage.create(join(agentDir, "auth.json"));
57
58
  const settingsManager = options.settingsManager ?? SettingsManager.create(cwd, agentDir);
58
59
  const modelRegistry = options.modelRegistry ?? ModelRegistry.create(authStorage, join(agentDir, "models.json"));