@psnext/slingcli 2.4.20260525-4 → 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 (243) 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 +78 -63
  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/node_modules/undici/lib/api/api-request.js +1 -1
  100. package/node_modules/undici/lib/cache/sqlite-cache-store.js +3 -3
  101. package/node_modules/undici/lib/core/connect.js +16 -0
  102. package/node_modules/undici/lib/core/request.js +17 -2
  103. package/node_modules/undici/lib/core/socks5-client.js +10 -5
  104. package/node_modules/undici/lib/core/socks5-utils.js +17 -22
  105. package/node_modules/undici/lib/dispatcher/client-h1.js +64 -20
  106. package/node_modules/undici/lib/dispatcher/client.js +6 -2
  107. package/node_modules/undici/lib/dispatcher/h2c-client.js +1 -1
  108. package/node_modules/undici/lib/mock/mock-call-history.js +15 -15
  109. package/node_modules/undici/lib/util/cache.js +8 -7
  110. package/node_modules/undici/lib/web/fetch/formdata-parser.js +17 -6
  111. package/node_modules/undici/lib/web/fetch/index.js +5 -2
  112. package/node_modules/undici/lib/web/webidl/index.js +5 -5
  113. package/node_modules/undici/lib/web/websocket/stream/websocketstream.js +1 -7
  114. package/node_modules/undici/package.json +1 -1
  115. package/package.json +6 -6
  116. package/slingshot/index.js +269 -266
  117. package/node_modules/koffi/CHANGELOG.md +0 -1093
  118. package/node_modules/koffi/LICENSE.txt +0 -22
  119. package/node_modules/koffi/README.md +0 -43
  120. package/node_modules/koffi/build/koffi/darwin_arm64/koffi.node +0 -0
  121. package/node_modules/koffi/build/koffi/darwin_x64/koffi.node +0 -0
  122. package/node_modules/koffi/build/koffi/freebsd_arm64/koffi.node +0 -0
  123. package/node_modules/koffi/build/koffi/freebsd_ia32/koffi.node +0 -0
  124. package/node_modules/koffi/build/koffi/freebsd_x64/koffi.node +0 -0
  125. package/node_modules/koffi/build/koffi/linux_arm64/koffi.node +0 -0
  126. package/node_modules/koffi/build/koffi/linux_armhf/koffi.node +0 -0
  127. package/node_modules/koffi/build/koffi/linux_ia32/koffi.node +0 -0
  128. package/node_modules/koffi/build/koffi/linux_loong64/koffi.node +0 -0
  129. package/node_modules/koffi/build/koffi/linux_riscv64d/koffi.node +0 -0
  130. package/node_modules/koffi/build/koffi/linux_x64/koffi.node +0 -0
  131. package/node_modules/koffi/build/koffi/musl_arm64/koffi.node +0 -0
  132. package/node_modules/koffi/build/koffi/musl_x64/koffi.node +0 -0
  133. package/node_modules/koffi/build/koffi/openbsd_ia32/koffi.node +0 -0
  134. package/node_modules/koffi/build/koffi/openbsd_x64/koffi.node +0 -0
  135. package/node_modules/koffi/build/koffi/win32_arm64/koffi.exp +0 -0
  136. package/node_modules/koffi/build/koffi/win32_arm64/koffi.lib +0 -0
  137. package/node_modules/koffi/build/koffi/win32_arm64/koffi.node +0 -0
  138. package/node_modules/koffi/build/koffi/win32_ia32/koffi.exp +0 -0
  139. package/node_modules/koffi/build/koffi/win32_ia32/koffi.lib +0 -0
  140. package/node_modules/koffi/build/koffi/win32_ia32/koffi.node +0 -0
  141. package/node_modules/koffi/build/koffi/win32_x64/koffi.exp +0 -0
  142. package/node_modules/koffi/build/koffi/win32_x64/koffi.lib +0 -0
  143. package/node_modules/koffi/build/koffi/win32_x64/koffi.node +0 -0
  144. package/node_modules/koffi/doc/benchmarks.md +0 -126
  145. package/node_modules/koffi/doc/callbacks.md +0 -210
  146. package/node_modules/koffi/doc/contribute.md +0 -148
  147. package/node_modules/koffi/doc/functions.md +0 -250
  148. package/node_modules/koffi/doc/index.md +0 -61
  149. package/node_modules/koffi/doc/input.md +0 -471
  150. package/node_modules/koffi/doc/migration.md +0 -159
  151. package/node_modules/koffi/doc/misc.md +0 -180
  152. package/node_modules/koffi/doc/output.md +0 -305
  153. package/node_modules/koffi/doc/packaging.md +0 -88
  154. package/node_modules/koffi/doc/platforms.md +0 -36
  155. package/node_modules/koffi/doc/pointers.md +0 -328
  156. package/node_modules/koffi/doc/start.md +0 -118
  157. package/node_modules/koffi/doc/unions.md +0 -186
  158. package/node_modules/koffi/doc/variables.md +0 -102
  159. package/node_modules/koffi/index.d.ts +0 -288
  160. package/node_modules/koffi/index.js +0 -634
  161. package/node_modules/koffi/indirect.js +0 -533
  162. package/node_modules/koffi/lib/native/base/base.cc +0 -11015
  163. package/node_modules/koffi/lib/native/base/base.hh +0 -6003
  164. package/node_modules/koffi/lib/native/base/crc.inc +0 -2214
  165. package/node_modules/koffi/lib/native/base/crc_gen.py +0 -72
  166. package/node_modules/koffi/lib/native/base/mimetypes.inc +0 -1248
  167. package/node_modules/koffi/lib/native/base/mimetypes_gen.py +0 -58
  168. package/node_modules/koffi/lib/native/base/tower.cc +0 -821
  169. package/node_modules/koffi/lib/native/base/tower.hh +0 -81
  170. package/node_modules/koffi/lib/native/base/unicode.inc +0 -408
  171. package/node_modules/koffi/lib/native/base/unicode_gen.py +0 -152
  172. package/node_modules/koffi/package.json +0 -38
  173. package/node_modules/koffi/src/cnoke/LICENSE.txt +0 -22
  174. package/node_modules/koffi/src/cnoke/README.md +0 -99
  175. package/node_modules/koffi/src/cnoke/assets/FindCNoke.cmake +0 -127
  176. package/node_modules/koffi/src/cnoke/assets/toolchains.json +0 -126
  177. package/node_modules/koffi/src/cnoke/assets/win_delay_hook.c +0 -36
  178. package/node_modules/koffi/src/cnoke/cnoke.js +0 -170
  179. package/node_modules/koffi/src/cnoke/package.json +0 -24
  180. package/node_modules/koffi/src/cnoke/src/builder.js +0 -511
  181. package/node_modules/koffi/src/cnoke/src/index.js +0 -10
  182. package/node_modules/koffi/src/cnoke/src/tools.js +0 -407
  183. package/node_modules/koffi/src/koffi/CMakeLists.txt +0 -182
  184. package/node_modules/koffi/src/koffi/src/abi_arm32.cc +0 -1018
  185. package/node_modules/koffi/src/koffi/src/abi_arm32_asm.S +0 -169
  186. package/node_modules/koffi/src/koffi/src/abi_arm64.cc +0 -1295
  187. package/node_modules/koffi/src/koffi/src/abi_arm64_asm.S +0 -195
  188. package/node_modules/koffi/src/koffi/src/abi_arm64_asm.asm +0 -174
  189. package/node_modules/koffi/src/koffi/src/abi_loong64.cc +0 -5
  190. package/node_modules/koffi/src/koffi/src/abi_loong64_asm.S +0 -204
  191. package/node_modules/koffi/src/koffi/src/abi_riscv64.cc +0 -915
  192. package/node_modules/koffi/src/koffi/src/abi_riscv64_asm.S +0 -203
  193. package/node_modules/koffi/src/koffi/src/abi_x64_sysv.cc +0 -939
  194. package/node_modules/koffi/src/koffi/src/abi_x64_sysv_asm.S +0 -231
  195. package/node_modules/koffi/src/koffi/src/abi_x64_win.cc +0 -715
  196. package/node_modules/koffi/src/koffi/src/abi_x64_win_asm.S +0 -166
  197. package/node_modules/koffi/src/koffi/src/abi_x64_win_asm.asm +0 -192
  198. package/node_modules/koffi/src/koffi/src/abi_x86.cc +0 -860
  199. package/node_modules/koffi/src/koffi/src/abi_x86_asm.S +0 -193
  200. package/node_modules/koffi/src/koffi/src/abi_x86_asm.asm +0 -177
  201. package/node_modules/koffi/src/koffi/src/call.cc +0 -1326
  202. package/node_modules/koffi/src/koffi/src/call.hh +0 -179
  203. package/node_modules/koffi/src/koffi/src/errno.inc +0 -462
  204. package/node_modules/koffi/src/koffi/src/ffi.cc +0 -2702
  205. package/node_modules/koffi/src/koffi/src/ffi.hh +0 -354
  206. package/node_modules/koffi/src/koffi/src/init.js +0 -105
  207. package/node_modules/koffi/src/koffi/src/parser.cc +0 -220
  208. package/node_modules/koffi/src/koffi/src/parser.hh +0 -54
  209. package/node_modules/koffi/src/koffi/src/util.cc +0 -1807
  210. package/node_modules/koffi/src/koffi/src/util.hh +0 -221
  211. package/node_modules/koffi/src/koffi/src/uv.cc +0 -193
  212. package/node_modules/koffi/src/koffi/src/uv.def +0 -10
  213. package/node_modules/koffi/src/koffi/src/uv.hh +0 -40
  214. package/node_modules/koffi/src/koffi/src/win32.cc +0 -198
  215. package/node_modules/koffi/src/koffi/src/win32.hh +0 -119
  216. package/node_modules/koffi/src/koffi/tools/write_trampolines.js +0 -77
  217. package/node_modules/koffi/vendor/node-addon-api/LICENSE.md +0 -9
  218. package/node_modules/koffi/vendor/node-addon-api/README.md +0 -95
  219. package/node_modules/koffi/vendor/node-addon-api/napi-inl.deprecated.h +0 -186
  220. package/node_modules/koffi/vendor/node-addon-api/napi-inl.h +0 -7033
  221. package/node_modules/koffi/vendor/node-addon-api/napi.h +0 -3309
  222. package/node_modules/koffi/vendor/node-api-headers/LICENSE +0 -21
  223. package/node_modules/koffi/vendor/node-api-headers/README.md +0 -95
  224. package/node_modules/koffi/vendor/node-api-headers/def/js_native_api.def +0 -125
  225. package/node_modules/koffi/vendor/node-api-headers/def/node_api.def +0 -157
  226. package/node_modules/koffi/vendor/node-api-headers/include/js_native_api.h +0 -591
  227. package/node_modules/koffi/vendor/node-api-headers/include/js_native_api_types.h +0 -210
  228. package/node_modules/koffi/vendor/node-api-headers/include/node_api.h +0 -265
  229. package/node_modules/koffi/vendor/node-api-headers/include/node_api_types.h +0 -58
  230. package/node_modules/koffi/vendor/node-api-headers/include/uv/aix.h +0 -32
  231. package/node_modules/koffi/vendor/node-api-headers/include/uv/bsd.h +0 -34
  232. package/node_modules/koffi/vendor/node-api-headers/include/uv/darwin.h +0 -61
  233. package/node_modules/koffi/vendor/node-api-headers/include/uv/errno.h +0 -483
  234. package/node_modules/koffi/vendor/node-api-headers/include/uv/linux.h +0 -34
  235. package/node_modules/koffi/vendor/node-api-headers/include/uv/os390.h +0 -33
  236. package/node_modules/koffi/vendor/node-api-headers/include/uv/posix.h +0 -31
  237. package/node_modules/koffi/vendor/node-api-headers/include/uv/sunos.h +0 -44
  238. package/node_modules/koffi/vendor/node-api-headers/include/uv/threadpool.h +0 -37
  239. package/node_modules/koffi/vendor/node-api-headers/include/uv/tree.h +0 -521
  240. package/node_modules/koffi/vendor/node-api-headers/include/uv/unix.h +0 -512
  241. package/node_modules/koffi/vendor/node-api-headers/include/uv/version.h +0 -43
  242. package/node_modules/koffi/vendor/node-api-headers/include/uv/win.h +0 -698
  243. package/node_modules/koffi/vendor/node-api-headers/include/uv.h +0 -1990
@@ -13,10 +13,11 @@
13
13
  * Modes use this class and add their own I/O layer on top.
14
14
  */
15
15
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
16
- import { basename, dirname, resolve } from "node:path";
16
+ import { basename, dirname } from "node:path";
17
17
  import { clampThinkingLevel, cleanupSessionResources, getSupportedThinkingLevels, isContextOverflow, modelsAreEqual, resetApiProviders, streamSimple, } from "@earendil-works/pi-ai";
18
18
  import { theme } from "../modes/interactive/theme/theme.js";
19
19
  import { stripFrontmatter } from "../utils/frontmatter.js";
20
+ import { resolvePath } from "../utils/paths.js";
20
21
  import { sleep } from "../utils/sleep.js";
21
22
  import { formatNoApiKeyFoundMessage, formatNoModelSelectedMessage } from "./auth-guidance.js";
22
23
  import { executeBashWithOperations } from "./bash-executor.js";
@@ -1925,6 +1926,9 @@ export class AgentSession {
1925
1926
  // =========================================================================
1926
1927
  // Auto-Retry
1927
1928
  // =========================================================================
1929
+ _isNonRetryableProviderLimitError(errorMessage) {
1930
+ return /GoUsageLimitError|FreeUsageLimitError|Monthly usage limit reached|available balance|insufficient_quota|out of budget|quota exceeded|billing/i.test(errorMessage);
1931
+ }
1928
1932
  /**
1929
1933
  * Check if an error is retryable (overloaded, rate limit, server errors).
1930
1934
  * Context overflow errors are NOT retryable (handled by compaction instead).
@@ -1937,6 +1941,8 @@ export class AgentSession {
1937
1941
  if (isContextOverflow(message, contextWindow))
1938
1942
  return false;
1939
1943
  const err = message.errorMessage;
1944
+ if (this._isNonRetryableProviderLimitError(err))
1945
+ return false;
1940
1946
  // Match: overloaded_error, provider returned error, rate limit, 429, 500, 502, 503, 504, service unavailable, network/connection errors (including connection lost), WebSocket transport closes/errors, fetch failed, premature stream endings, HTTP/2 closed before response, terminated, retry delay exceeded
1941
1947
  return /overloaded|provider.?returned.?error|rate.?limit|too many requests|429|500|502|503|504|service.?unavailable|server.?error|internal.?error|network.?error|connection.?error|connection.?refused|connection.?lost|websocket.?closed|websocket.?error|other side closed|fetch failed|upstream.?connect|reset before headers|socket hang up|ended without|stream ended before message_stop|http2 request did not get a response|timed? out|timeout|terminated|retry delay/i.test(err);
1942
1948
  }
@@ -2417,7 +2423,7 @@ export class AgentSession {
2417
2423
  * @returns The resolved output file path.
2418
2424
  */
2419
2425
  exportToJsonl(outputPath) {
2420
- const filePath = resolve(outputPath ?? `session-${new Date().toISOString().replace(/[:.]/g, "-")}.jsonl`);
2426
+ const filePath = resolvePath(outputPath ?? `session-${new Date().toISOString().replace(/[:.]/g, "-")}.jsonl`, process.cwd());
2421
2427
  const dir = dirname(filePath);
2422
2428
  if (!existsSync(dir)) {
2423
2429
  mkdirSync(dir, { recursive: true });
@@ -11,11 +11,12 @@ import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "f
11
11
  import { dirname, join } from "path";
12
12
  import lockfile from "proper-lockfile";
13
13
  import { getAgentDir } from "../config.js";
14
+ import { normalizePath } from "../utils/paths.js";
14
15
  import { resolveConfigValue } from "./resolve-config-value.js";
15
16
  export class FileAuthStorageBackend {
16
17
  authPath;
17
18
  constructor(authPath = join(getAgentDir(), "auth.json")) {
18
- this.authPath = authPath;
19
+ this.authPath = normalizePath(authPath);
19
20
  }
20
21
  ensureParentDir() {
21
22
  const dir = dirname(this.authPath);
@@ -154,6 +154,22 @@ export function shouldCompact(contextTokens, contextWindow, settings) {
154
154
  // ============================================================================
155
155
  // Cut point detection
156
156
  // ============================================================================
157
+ const ESTIMATED_IMAGE_CHARS = 4800;
158
+ function estimateTextAndImageContentChars(content) {
159
+ if (typeof content === "string") {
160
+ return content.length;
161
+ }
162
+ let chars = 0;
163
+ for (const block of content) {
164
+ if (block.type === "text" && block.text) {
165
+ chars += block.text.length;
166
+ }
167
+ else if (block.type === "image") {
168
+ chars += ESTIMATED_IMAGE_CHARS;
169
+ }
170
+ }
171
+ return chars;
172
+ }
157
173
  /**
158
174
  * Estimate token count for a message using chars/4 heuristic.
159
175
  * This is conservative (overestimates tokens).
@@ -162,17 +178,7 @@ export function estimateTokens(message) {
162
178
  let chars = 0;
163
179
  switch (message.role) {
164
180
  case "user": {
165
- const content = message.content;
166
- if (typeof content === "string") {
167
- chars = content.length;
168
- }
169
- else if (Array.isArray(content)) {
170
- for (const block of content) {
171
- if (block.type === "text" && block.text) {
172
- chars += block.text.length;
173
- }
174
- }
175
- }
181
+ chars = estimateTextAndImageContentChars(message.content);
176
182
  return Math.ceil(chars / 4);
177
183
  }
178
184
  case "assistant": {
@@ -192,19 +198,7 @@ export function estimateTokens(message) {
192
198
  }
193
199
  case "custom":
194
200
  case "toolResult": {
195
- if (typeof message.content === "string") {
196
- chars = message.content.length;
197
- }
198
- else {
199
- for (const block of message.content) {
200
- if (block.type === "text" && block.text) {
201
- chars += block.text.length;
202
- }
203
- if (block.type === "image") {
204
- chars += 4800; // Estimate images as 4000 chars, or 1200 tokens
205
- }
206
- }
207
- }
201
+ chars = estimateTextAndImageContentChars(message.content);
208
202
  return Math.ceil(chars / 4);
209
203
  }
210
204
  case "bashExecution": {
@@ -2,6 +2,7 @@ import { existsSync, readFileSync, writeFileSync } from "fs";
2
2
  import { basename, join } from "path";
3
3
  import { APP_NAME, getExportTemplateDir } from "../../config.js";
4
4
  import { getResolvedThemeColors, getThemeExportColors } from "../../modes/interactive/theme/theme.js";
5
+ import { normalizePath, resolvePath } from "../../utils/paths.js";
5
6
  import { SessionManager } from "../session-manager.js";
6
7
  /** Parse a color string to RGB values. Supports hex (#RRGGBB) and rgb(r,g,b) formats. */
7
8
  function parseColor(color) {
@@ -187,7 +188,7 @@ export async function exportSessionToHtml(sm, state, options) {
187
188
  renderedTools,
188
189
  };
189
190
  const html = generateHtml(sessionData, opts.themeName);
190
- let outputPath = opts.outputPath;
191
+ let outputPath = opts.outputPath ? normalizePath(opts.outputPath) : undefined;
191
192
  if (!outputPath) {
192
193
  const sessionBasename = basename(sessionFile, ".jsonl");
193
194
  outputPath = `${APP_NAME}-session-${sessionBasename}.html`;
@@ -201,10 +202,11 @@ export async function exportSessionToHtml(sm, state, options) {
201
202
  */
202
203
  export async function exportFromFile(inputPath, options) {
203
204
  const opts = typeof options === "string" ? { outputPath: options } : options || {};
204
- if (!existsSync(inputPath)) {
205
- throw new Error(`File not found: ${inputPath}`);
205
+ const resolvedInputPath = resolvePath(inputPath);
206
+ if (!existsSync(resolvedInputPath)) {
207
+ throw new Error(`File not found: ${resolvedInputPath}`);
206
208
  }
207
- const sm = SessionManager.open(inputPath);
209
+ const sm = SessionManager.open(resolvedInputPath);
208
210
  const sessionData = {
209
211
  header: sm.getHeader(),
210
212
  entries: sm.getEntries(),
@@ -213,9 +215,9 @@ export async function exportFromFile(inputPath, options) {
213
215
  tools: undefined,
214
216
  };
215
217
  const html = generateHtml(sessionData, opts.themeName);
216
- let outputPath = opts.outputPath;
218
+ let outputPath = opts.outputPath ? normalizePath(opts.outputPath) : undefined;
217
219
  if (!outputPath) {
218
- const inputBasename = basename(inputPath, ".jsonl");
220
+ const inputBasename = basename(resolvedInputPath, ".jsonl");
219
221
  outputPath = `${APP_NAME}-session-${inputBasename}.html`;
220
222
  }
221
223
  writeFileSync(outputPath, html, "utf8");
@@ -605,9 +605,12 @@
605
605
  }
606
606
 
607
607
  function escapeHtml(text) {
608
- const div = document.createElement('div');
609
- div.textContent = text;
610
- return div.innerHTML;
608
+ return String(text)
609
+ .replace(/&/g, '&')
610
+ .replace(/</g, '&lt;')
611
+ .replace(/>/g, '&gt;')
612
+ .replace(/"/g, '&quot;')
613
+ .replace(/'/g, '&#39;');
611
614
  }
612
615
 
613
616
  /**
@@ -4,7 +4,6 @@
4
4
  */
5
5
  import * as fs from "node:fs";
6
6
  import { createRequire } from "node:module";
7
- import * as os from "node:os";
8
7
  import * as path from "node:path";
9
8
  import { fileURLToPath } from "node:url";
10
9
  import * as _bundledPiAgentCore from "@earendil-works/pi-agent-core";
@@ -22,6 +21,7 @@ import { CONFIG_DIR_NAME, getAgentDir, isBunBinary } from "../../config.js";
22
21
  // NOTE: This import works because loader.ts exports are NOT re-exported from index.ts,
23
22
  // avoiding a circular dependency. Extensions can import from @earendil-works/pi-coding-agent.
24
23
  import * as _bundledPiCodingAgent from "../../index.js";
24
+ import { resolvePath } from "../../utils/paths.js";
25
25
  import { createEventBus } from "../event-bus.js";
26
26
  import { execCommand } from "../exec.js";
27
27
  import { createSyntheticSourceInfo } from "../source-info.js";
@@ -91,27 +91,6 @@ function getAliases() {
91
91
  };
92
92
  return _aliases;
93
93
  }
94
- const UNICODE_SPACES = /[\u00A0\u2000-\u200A\u202F\u205F\u3000]/g;
95
- function normalizeUnicodeSpaces(str) {
96
- return str.replace(UNICODE_SPACES, " ");
97
- }
98
- function expandPath(p) {
99
- const normalized = normalizeUnicodeSpaces(p);
100
- if (normalized.startsWith("~/")) {
101
- return path.join(os.homedir(), normalized.slice(2));
102
- }
103
- if (normalized.startsWith("~")) {
104
- return path.join(os.homedir(), normalized.slice(1));
105
- }
106
- return normalized;
107
- }
108
- function resolvePath(extPath, cwd) {
109
- const expanded = expandPath(extPath);
110
- if (path.isAbsolute(expanded)) {
111
- return expanded;
112
- }
113
- return path.resolve(cwd, expanded);
114
- }
115
94
  /**
116
95
  * Create a runtime with throwing stubs for action methods.
117
96
  * Runner.bindCore() replaces these with real implementations.
@@ -315,7 +294,7 @@ function createExtension(extensionPath, resolvedPath) {
315
294
  };
316
295
  }
317
296
  async function loadExtension(extensionPath, cwd, eventBus, runtime) {
318
- const resolvedPath = resolvePath(extensionPath, cwd);
297
+ const resolvedPath = resolvePath(extensionPath, cwd, { normalizeUnicodeSpaces: true });
319
298
  try {
320
299
  const factory = await loadExtensionModule(resolvedPath);
321
300
  if (!factory) {
@@ -336,7 +315,8 @@ async function loadExtension(extensionPath, cwd, eventBus, runtime) {
336
315
  */
337
316
  export async function loadExtensionFromFactory(factory, cwd, eventBus, runtime, extensionPath = "<inline>") {
338
317
  const extension = createExtension(extensionPath, extensionPath);
339
- const api = createExtensionAPI(extension, runtime, cwd, eventBus);
318
+ const resolvedCwd = resolvePath(cwd);
319
+ const api = createExtensionAPI(extension, runtime, resolvedCwd, eventBus);
340
320
  await factory(api);
341
321
  return extension;
342
322
  }
@@ -346,10 +326,11 @@ export async function loadExtensionFromFactory(factory, cwd, eventBus, runtime,
346
326
  export async function loadExtensions(paths, cwd, eventBus) {
347
327
  const extensions = [];
348
328
  const errors = [];
329
+ const resolvedCwd = resolvePath(cwd);
349
330
  const resolvedEventBus = eventBus ?? createEventBus();
350
331
  const runtime = createExtensionRuntime();
351
332
  for (const extPath of paths) {
352
- const { extension, error } = await loadExtension(extPath, cwd, resolvedEventBus, runtime);
333
+ const { extension, error } = await loadExtension(extPath, resolvedCwd, resolvedEventBus, runtime);
353
334
  if (error) {
354
335
  errors.push({ path: extPath, error });
355
336
  continue;
@@ -460,6 +441,8 @@ function discoverExtensionsInDir(dir) {
460
441
  * Discover and load extensions from standard locations.
461
442
  */
462
443
  export async function discoverAndLoadExtensions(configuredPaths, cwd, agentDir = getAgentDir(), eventBus) {
444
+ const resolvedCwd = resolvePath(cwd);
445
+ const resolvedAgentDir = resolvePath(agentDir);
463
446
  const allPaths = [];
464
447
  const seen = new Set();
465
448
  const addPaths = (paths) => {
@@ -472,14 +455,14 @@ export async function discoverAndLoadExtensions(configuredPaths, cwd, agentDir =
472
455
  }
473
456
  };
474
457
  // 1. Project-local extensions: cwd/${CONFIG_DIR_NAME}/extensions/
475
- const localExtDir = path.join(cwd, CONFIG_DIR_NAME, "extensions");
458
+ const localExtDir = path.join(resolvedCwd, CONFIG_DIR_NAME, "extensions");
476
459
  addPaths(discoverExtensionsInDir(localExtDir));
477
460
  // 2. Global extensions: agentDir/extensions/
478
- const globalExtDir = path.join(agentDir, "extensions");
461
+ const globalExtDir = path.join(resolvedAgentDir, "extensions");
479
462
  addPaths(discoverExtensionsInDir(globalExtDir));
480
463
  // 3. Explicitly configured paths
481
464
  for (const p of configuredPaths) {
482
- const resolved = resolvePath(p, cwd);
465
+ const resolved = resolvePath(p, resolvedCwd, { normalizeUnicodeSpaces: true });
483
466
  if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory()) {
484
467
  // Check for package.json with pi manifest or index.ts
485
468
  const entries = resolveExtensionEntries(resolved);
@@ -493,6 +476,6 @@ export async function discoverAndLoadExtensions(configuredPaths, cwd, agentDir =
493
476
  }
494
477
  addPaths([resolved]);
495
478
  }
496
- return loadExtensions(allPaths, cwd, eventBus);
479
+ return loadExtensions(allPaths, resolvedCwd, eventBus);
497
480
  }
498
481
  //# sourceMappingURL=loader.js.map
@@ -8,6 +8,7 @@ import { join } from "path";
8
8
  import { Type } from "typebox";
9
9
  import { Compile } from "typebox/compile";
10
10
  import { getAgentDir } from "../config.js";
11
+ import { normalizePath } from "../utils/paths.js";
11
12
  import { BUILT_IN_PROVIDER_DISPLAY_NAMES } from "./provider-display-names.js";
12
13
  import { clearConfigValueCache, resolveConfigValueOrThrow, resolveConfigValueUncached, resolveHeadersOrThrow, } from "./resolve-config-value.js";
13
14
  // Schema for OpenRouter routing preferences
@@ -91,6 +92,9 @@ const OpenAIResponsesCompatSchema = Type.Object({
91
92
  const AnthropicMessagesCompatSchema = Type.Object({
92
93
  supportsEagerToolInputStreaming: Type.Optional(Type.Boolean()),
93
94
  supportsLongCacheRetention: Type.Optional(Type.Boolean()),
95
+ sendSessionAffinityHeaders: Type.Optional(Type.Boolean()),
96
+ supportsCacheControlOnTools: Type.Optional(Type.Boolean()),
97
+ forceAdaptiveThinking: Type.Optional(Type.Boolean()),
94
98
  });
95
99
  const ProviderCompatSchema = Type.Union([
96
100
  OpenAICompletionsCompatSchema,
@@ -242,7 +246,7 @@ export class ModelRegistry {
242
246
  modelsJsonPath;
243
247
  constructor(authStorage, modelsJsonPath) {
244
248
  this.authStorage = authStorage;
245
- this.modelsJsonPath = modelsJsonPath;
249
+ this.modelsJsonPath = modelsJsonPath ? normalizePath(modelsJsonPath) : undefined;
246
250
  this.loadModels();
247
251
  }
248
252
  static create(authStorage, modelsJsonPath = join(getAgentDir(), "models.json")) {
@@ -1,4 +1,40 @@
1
1
  let stdoutTakeoverState;
2
+ const RAW_STDOUT_RETRY_DELAY_MS = 10;
3
+ let rawStdoutWriteTail = Promise.resolve();
4
+ function getRawStdoutWrite() {
5
+ if (stdoutTakeoverState) {
6
+ return stdoutTakeoverState.rawStdoutWrite;
7
+ }
8
+ return process.stdout.write.bind(process.stdout);
9
+ }
10
+ async function writeRawStdoutChunk(text) {
11
+ while (true) {
12
+ try {
13
+ await new Promise((resolve, reject) => {
14
+ try {
15
+ getRawStdoutWrite()(text, (error) => {
16
+ if (error)
17
+ reject(error);
18
+ else
19
+ resolve();
20
+ });
21
+ }
22
+ catch (error) {
23
+ reject(error instanceof Error ? error : new Error(String(error)));
24
+ }
25
+ });
26
+ return;
27
+ }
28
+ catch (error) {
29
+ const writeError = error instanceof Error ? error : new Error(String(error));
30
+ const code = writeError.code;
31
+ if (code !== "ENOBUFS" && code !== "EAGAIN" && code !== "EWOULDBLOCK") {
32
+ throw writeError;
33
+ }
34
+ await new Promise((resolve) => setTimeout(resolve, RAW_STDOUT_RETRY_DELAY_MS));
35
+ }
36
+ }
37
+ }
2
38
  export function takeOverStdout() {
3
39
  if (stdoutTakeoverState) {
4
40
  return;
@@ -29,31 +65,25 @@ export function isStdoutTakenOver() {
29
65
  return stdoutTakeoverState !== undefined;
30
66
  }
31
67
  export function writeRawStdout(text) {
32
- if (stdoutTakeoverState) {
33
- stdoutTakeoverState.rawStdoutWrite(text);
68
+ if (text.length === 0) {
34
69
  return;
35
70
  }
36
- process.stdout.write(text);
71
+ rawStdoutWriteTail = rawStdoutWriteTail.then(() => writeRawStdoutChunk(text));
72
+ void rawStdoutWriteTail.catch(() => {
73
+ process.exit(1);
74
+ });
37
75
  }
38
- export async function flushRawStdout() {
39
- if (stdoutTakeoverState) {
40
- await new Promise((resolve, reject) => {
41
- stdoutTakeoverState?.rawStdoutWrite("", (err) => {
42
- if (err)
43
- reject(err);
44
- else
45
- resolve();
46
- });
47
- });
48
- return;
76
+ export async function waitForRawStdoutBackpressure() {
77
+ while (true) {
78
+ const tail = rawStdoutWriteTail;
79
+ await tail;
80
+ if (tail === rawStdoutWriteTail) {
81
+ return;
82
+ }
49
83
  }
50
- await new Promise((resolve, reject) => {
51
- process.stdout.write("", (err) => {
52
- if (err)
53
- reject(err);
54
- else
55
- resolve();
56
- });
57
- });
84
+ }
85
+ export async function flushRawStdout() {
86
+ await waitForRawStdoutBackpressure();
87
+ await writeRawStdoutChunk("");
58
88
  }
59
89
  //# sourceMappingURL=output-guard.js.map
@@ -27,7 +27,7 @@ import { minimatch } from "minimatch";
27
27
  import { CONFIG_DIR_NAME } from "../config.js";
28
28
  import { spawnProcess, spawnProcessSync } from "../utils/child-process.js";
29
29
  import { parseGitUrl } from "../utils/git.js";
30
- import { canonicalizePath, isLocalPath, markPathIgnoredByCloudSync } from "../utils/paths.js";
30
+ import { canonicalizePath, isLocalPath, markPathIgnoredByCloudSync, resolvePath } from "../utils/paths.js";
31
31
  import { isStdoutTakenOver } from "./output-guard.js";
32
32
  const NETWORK_TIMEOUT_MS = 10000;
33
33
  const UPDATE_CHECK_CONCURRENCY = 4;
@@ -577,8 +577,8 @@ export class DefaultPackageManager {
577
577
  globalNpmRootCommandKey;
578
578
  progressCallback;
579
579
  constructor(options) {
580
- this.cwd = options.cwd;
581
- this.agentDir = options.agentDir;
580
+ this.cwd = resolvePath(options.cwd);
581
+ this.agentDir = resolvePath(options.agentDir);
582
582
  this.settingsManager = options.settingsManager;
583
583
  }
584
584
  setProgressCallback(callback) {
@@ -589,9 +589,22 @@ export class DefaultPackageManager {
589
589
  const currentSettings = scope === "project" ? this.settingsManager.getProjectSettings() : this.settingsManager.getGlobalSettings();
590
590
  const currentPackages = currentSettings.packages ?? [];
591
591
  const normalizedSource = this.normalizePackageSourceForSettings(source, scope);
592
- const exists = currentPackages.some((existing) => this.packageSourcesMatch(existing, source, scope));
593
- if (exists) {
594
- return false;
592
+ const matchIndex = currentPackages.findIndex((existing) => this.packageSourcesMatch(existing, source, scope));
593
+ if (matchIndex !== -1) {
594
+ const existing = currentPackages[matchIndex];
595
+ if (this.getPackageSourceString(existing) === normalizedSource) {
596
+ return false;
597
+ }
598
+ const nextPackages = [...currentPackages];
599
+ nextPackages[matchIndex] =
600
+ typeof existing === "string" ? normalizedSource : { ...existing, source: normalizedSource };
601
+ if (scope === "project") {
602
+ this.settingsManager.setProjectPackages(nextPackages);
603
+ }
604
+ else {
605
+ this.settingsManager.setPackages(nextPackages);
606
+ }
607
+ return true;
595
608
  }
596
609
  const nextPackages = [...currentPackages, normalizedSource];
597
610
  if (scope === "project") {
@@ -801,14 +814,16 @@ export class DefaultPackageManager {
801
814
  const gitCandidates = [];
802
815
  for (const entry of sources) {
803
816
  const parsed = this.parseSource(entry.source);
804
- if (parsed.type === "local" || parsed.pinned) {
805
- continue;
806
- }
817
+ // Pinned npm versions are fixed. Pinned git refs are configured checkout targets,
818
+ // so include them to reconcile an existing clone when the configured ref changes.
807
819
  if (parsed.type === "npm") {
808
- npmCandidates.push({ ...entry, parsed });
809
- continue;
820
+ if (!parsed.pinned) {
821
+ npmCandidates.push({ ...entry, parsed });
822
+ }
823
+ }
824
+ else if (parsed.type === "git") {
825
+ gitCandidates.push({ ...entry, parsed });
810
826
  }
811
- gitCandidates.push({ ...entry, parsed });
812
827
  }
813
828
  const npmCheckTasks = npmCandidates.map((entry) => async () => ({
814
829
  entry,
@@ -1383,13 +1398,25 @@ export class DefaultPackageManager {
1383
1398
  }
1384
1399
  getNpmInstallArgs(specs, installRoot) {
1385
1400
  const packageManagerName = this.getPackageManagerName();
1401
+ // Extension packages run inside pi and resolve pi APIs through loader aliases/virtual modules.
1402
+ // Disable peer dependency resolution for managed installs (npm's --legacy-peer-deps, and
1403
+ // equivalent bun/pnpm settings) so package managers do not install or solve host-provided
1404
+ // @earendil-works/pi-* peers. Stale auto-installed pi peers can otherwise block updates.
1386
1405
  if (packageManagerName === "bun") {
1387
- return ["install", ...specs, "--cwd", installRoot];
1406
+ return ["install", ...specs, "--cwd", installRoot, "--omit=peer"];
1388
1407
  }
1389
1408
  if (packageManagerName === "pnpm") {
1390
- return ["install", ...specs, "--prefix", installRoot, "--config.strict-dep-builds=false"];
1391
- }
1392
- return ["install", ...specs, "--prefix", installRoot];
1409
+ return [
1410
+ "install",
1411
+ ...specs,
1412
+ "--prefix",
1413
+ installRoot,
1414
+ "--config.auto-install-peers=false",
1415
+ "--config.strict-peer-dependencies=false",
1416
+ "--config.strict-dep-builds=false",
1417
+ ];
1418
+ }
1419
+ return ["install", ...specs, "--prefix", installRoot, "--legacy-peer-deps"];
1393
1420
  }
1394
1421
  async installNpm(source, scope, temporary) {
1395
1422
  const installRoot = this.getNpmInstallRoot(scope, temporary);
@@ -1410,6 +1437,12 @@ export class DefaultPackageManager {
1410
1437
  async installGit(source, scope) {
1411
1438
  const targetDir = this.getGitInstallPath(source, scope);
1412
1439
  if (existsSync(targetDir)) {
1440
+ if (source.ref) {
1441
+ await this.ensureGitRef(targetDir, ["fetch", "origin", source.ref], "FETCH_HEAD");
1442
+ return;
1443
+ }
1444
+ const target = await this.getLocalGitUpdateTarget(targetDir);
1445
+ await this.ensureGitRef(targetDir, target.fetchArgs, target.ref);
1413
1446
  return;
1414
1447
  }
1415
1448
  const gitRoot = this.getGitInstallRoot(scope);
@@ -1432,21 +1465,29 @@ export class DefaultPackageManager {
1432
1465
  await this.installGit(source, scope);
1433
1466
  return;
1434
1467
  }
1468
+ if (source.ref) {
1469
+ await this.ensureGitRef(targetDir, ["fetch", "origin", source.ref], "FETCH_HEAD");
1470
+ return;
1471
+ }
1435
1472
  const target = await this.getLocalGitUpdateTarget(targetDir);
1473
+ await this.ensureGitRef(targetDir, target.fetchArgs, target.ref);
1474
+ }
1475
+ async ensureGitRef(targetDir, fetchArgs, ref) {
1436
1476
  // Fetch only the ref we will reset to, avoiding unrelated branch/tag noise.
1437
- await this.runCommand("git", target.fetchArgs, { cwd: targetDir });
1477
+ await this.runCommand("git", fetchArgs, { cwd: targetDir });
1438
1478
  const localHead = await this.runCommandCapture("git", ["rev-parse", "HEAD"], {
1439
1479
  cwd: targetDir,
1440
1480
  timeoutMs: NETWORK_TIMEOUT_MS,
1441
1481
  });
1442
- const refreshedTargetHead = await this.runCommandCapture("git", ["rev-parse", target.ref], {
1482
+ const commitRef = `${ref}^{commit}`;
1483
+ const targetHead = await this.runCommandCapture("git", ["rev-parse", commitRef], {
1443
1484
  cwd: targetDir,
1444
1485
  timeoutMs: NETWORK_TIMEOUT_MS,
1445
1486
  });
1446
- if (localHead.trim() === refreshedTargetHead.trim()) {
1487
+ if (localHead.trim() === targetHead.trim()) {
1447
1488
  return;
1448
1489
  }
1449
- await this.runCommand("git", ["reset", "--hard", target.ref], { cwd: targetDir });
1490
+ await this.runCommand("git", ["reset", "--hard", commitRef], { cwd: targetDir });
1450
1491
  // Clean untracked files (extensions should be pristine)
1451
1492
  await this.runCommand("git", ["clean", "-fdx"], { cwd: targetDir });
1452
1493
  const packageJsonPath = join(targetDir, "package.json");
@@ -1616,24 +1657,10 @@ export class DefaultPackageManager {
1616
1657
  return this.cwd;
1617
1658
  }
1618
1659
  resolvePath(input) {
1619
- const trimmed = input.trim();
1620
- if (trimmed === "~")
1621
- return getHomeDir();
1622
- if (trimmed.startsWith("~/"))
1623
- return join(getHomeDir(), trimmed.slice(2));
1624
- if (trimmed.startsWith("~"))
1625
- return join(getHomeDir(), trimmed.slice(1));
1626
- return resolve(this.cwd, trimmed);
1660
+ return resolvePath(input, this.cwd, { homeDir: getHomeDir(), trim: true });
1627
1661
  }
1628
1662
  resolvePathFromBase(input, baseDir) {
1629
- const trimmed = input.trim();
1630
- if (trimmed === "~")
1631
- return getHomeDir();
1632
- if (trimmed.startsWith("~/"))
1633
- return join(getHomeDir(), trimmed.slice(2));
1634
- if (trimmed.startsWith("~"))
1635
- return join(getHomeDir(), trimmed.slice(1));
1636
- return resolve(baseDir, trimmed);
1663
+ return resolvePath(input, baseDir, { homeDir: getHomeDir(), trim: true });
1637
1664
  }
1638
1665
  collectPackageResources(packageRoot, accumulator, filter, metadata) {
1639
1666
  if (filter) {
@@ -1,8 +1,8 @@
1
1
  import { existsSync, readdirSync, readFileSync, statSync } from "fs";
2
- import { homedir } from "os";
3
- import { basename, dirname, isAbsolute, join, resolve, sep } from "path";
2
+ import { basename, dirname, join, resolve, sep } from "path";
4
3
  import { CONFIG_DIR_NAME } from "../config.js";
5
4
  import { parseFrontmatter } from "../utils/frontmatter.js";
5
+ import { resolvePath } from "../utils/paths.js";
6
6
  import { createSyntheticSourceInfo } from "./source-info.js";
7
7
  /**
8
8
  * Parse command arguments respecting quoted strings (bash-style)
@@ -146,20 +146,6 @@ function loadTemplatesFromDir(dir, getSourceInfo) {
146
146
  }
147
147
  return templates;
148
148
  }
149
- function normalizePath(input) {
150
- const trimmed = input.trim();
151
- if (trimmed === "~")
152
- return homedir();
153
- if (trimmed.startsWith("~/"))
154
- return join(homedir(), trimmed.slice(2));
155
- if (trimmed.startsWith("~"))
156
- return join(homedir(), trimmed.slice(1));
157
- return trimmed;
158
- }
159
- function resolvePromptPath(p, cwd) {
160
- const normalized = normalizePath(p);
161
- return isAbsolute(normalized) ? normalized : resolve(cwd, normalized);
162
- }
163
149
  /**
164
150
  * Load all prompt templates from:
165
151
  * 1. Global: agentDir/prompts/
@@ -167,12 +153,12 @@ function resolvePromptPath(p, cwd) {
167
153
  * 3. Explicit prompt paths
168
154
  */
169
155
  export function loadPromptTemplates(options) {
170
- const resolvedCwd = options.cwd;
171
- const resolvedAgentDir = options.agentDir;
156
+ const resolvedCwd = resolvePath(options.cwd);
157
+ const resolvedAgentDir = resolvePath(options.agentDir);
172
158
  const promptPaths = options.promptPaths;
173
159
  const includeDefaults = options.includeDefaults;
174
160
  const templates = [];
175
- const globalPromptsDir = options.agentDir ? join(options.agentDir, "prompts") : resolvedAgentDir;
161
+ const globalPromptsDir = join(resolvedAgentDir, "prompts");
176
162
  const projectPromptsDir = resolve(resolvedCwd, CONFIG_DIR_NAME, "prompts");
177
163
  const isUnderPath = (target, root) => {
178
164
  const normalizedRoot = resolve(root);
@@ -208,7 +194,7 @@ export function loadPromptTemplates(options) {
208
194
  }
209
195
  // 3. Load explicit prompt paths
210
196
  for (const rawPath of promptPaths) {
211
- const resolvedPath = resolvePromptPath(rawPath, resolvedCwd);
197
+ const resolvedPath = resolvePath(rawPath, resolvedCwd, { trim: true });
212
198
  if (!existsSync(resolvedPath)) {
213
199
  continue;
214
200
  }