@code-yeongyu/senpi 2026.11.2-3 → 2026.11.3-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 (307) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/dist/bundle/chunks/anthropic-messages-LJAKRKMX.js +11 -0
  3. package/dist/bundle/chunks/{args-AOI4NEA6.js → args-GQTXDYS3.js} +1 -1
  4. package/dist/bundle/chunks/{azure-openai-responses-YLBBQXI7.js → azure-openai-responses-HDEKTPMZ.js} +1 -1
  5. package/dist/bundle/chunks/chatgpt-subscription.js +10 -2
  6. package/dist/bundle/chunks/{chunk-ED7VJGL4.js → chunk-2PGSRQBF.js} +1 -1
  7. package/dist/bundle/chunks/{chunk-LMIETUPG.js → chunk-3LDASCJN.js} +1 -1
  8. package/dist/bundle/chunks/{chunk-PIAY636U.js → chunk-3QPE75AR.js} +1 -1
  9. package/dist/bundle/chunks/{chunk-FQZZGSYH.js → chunk-4ZGQUOBV.js} +1 -1
  10. package/dist/bundle/chunks/{chunk-S2M5D5GS.js → chunk-5BMIET5O.js} +2 -2
  11. package/dist/bundle/chunks/{chunk-XLLQJALL.js → chunk-67M7HKXO.js} +1 -1
  12. package/dist/bundle/chunks/chunk-6ISOXWH2.js +2 -0
  13. package/dist/bundle/chunks/{chunk-IWM5QRAE.js → chunk-6NPK2O3C.js} +2 -2
  14. package/dist/bundle/chunks/{chunk-FO56LSUK.js → chunk-7C4W5HMU.js} +1 -1
  15. package/dist/bundle/chunks/chunk-7KIHKE2T.js +2 -0
  16. package/dist/bundle/chunks/{chunk-BW4YXS7X.js → chunk-A4KOQUHT.js} +1 -1
  17. package/dist/bundle/chunks/{chunk-FJSJUVQQ.js → chunk-A5PZGQCP.js} +3 -3
  18. package/dist/bundle/chunks/{chunk-TBARMJ75.js → chunk-AVP6DDBF.js} +1 -1
  19. package/dist/bundle/chunks/{chunk-KVPRQ5KN.js → chunk-B3GTID4P.js} +1 -1
  20. package/dist/bundle/chunks/{chunk-2XBKE2BZ.js → chunk-BIWFWGUA.js} +1 -1
  21. package/dist/bundle/chunks/{chunk-6LDUM5CL.js → chunk-DCI4QOUX.js} +1 -1
  22. package/dist/bundle/chunks/{chunk-2YFPDNNP.js → chunk-DNZKKG4V.js} +1 -1
  23. package/dist/bundle/chunks/{chunk-W2V7RIBF.js → chunk-E7SA75PP.js} +1 -1
  24. package/dist/bundle/chunks/{chunk-YWD54RHU.js → chunk-EBZ6IP4V.js} +1 -1
  25. package/dist/bundle/chunks/{chunk-N4TSAHRL.js → chunk-EVTTLPIY.js} +1 -1
  26. package/dist/bundle/chunks/{chunk-J2OBN7TI.js → chunk-G5XUGK4B.js} +1 -1
  27. package/dist/bundle/chunks/{chunk-IHOQR7TU.js → chunk-GKP34H3C.js} +2 -2
  28. package/dist/bundle/chunks/{chunk-PP52OWBB.js → chunk-GLTOBXZY.js} +1 -1
  29. package/dist/bundle/chunks/{chunk-ZSVWKOAJ.js → chunk-IJGXAACA.js} +263 -237
  30. package/dist/bundle/chunks/{chunk-LGO25HPL.js → chunk-JCTODLUP.js} +1 -1
  31. package/dist/bundle/chunks/{chunk-3ESZE3DG.js → chunk-KHBCZ6TR.js} +1 -1
  32. package/dist/bundle/chunks/chunk-KJR3XXMI.js +2 -0
  33. package/dist/bundle/chunks/{chunk-TVMW2SUP.js → chunk-KY4V3TSO.js} +2 -2
  34. package/dist/bundle/chunks/{chunk-T5ZJ2Q4O.js → chunk-MCXOJHN2.js} +1 -1
  35. package/dist/bundle/chunks/{chunk-GIY7PGOH.js → chunk-NKI7L5UM.js} +3 -3
  36. package/dist/bundle/chunks/{chunk-L3V65XW7.js → chunk-OD7RT7G7.js} +1 -1
  37. package/dist/bundle/chunks/{chunk-NYXXKS6D.js → chunk-OOU3BRUY.js} +3 -2
  38. package/dist/bundle/chunks/{chunk-LPHUMR6R.js → chunk-OT6ALXSJ.js} +1 -1
  39. package/dist/bundle/chunks/{chunk-XGBRBCIQ.js → chunk-Q5CKFBQT.js} +1 -1
  40. package/dist/bundle/chunks/{chunk-F7GBSQRV.js → chunk-QPYDQ4QK.js} +1 -1
  41. package/dist/bundle/chunks/{chunk-7FUO655J.js → chunk-QSMT5KVJ.js} +1 -1
  42. package/dist/bundle/chunks/{chunk-SAZ6E74O.js → chunk-RUOTFZQR.js} +1 -1
  43. package/dist/bundle/chunks/chunk-RWF6IJQL.js +448 -0
  44. package/dist/bundle/chunks/{chunk-P4AT7SBE.js → chunk-S5Y7NEHB.js} +1 -1
  45. package/dist/bundle/chunks/{chunk-QLTQ3UUI.js → chunk-SCBTASM4.js} +1 -1
  46. package/dist/bundle/chunks/chunk-SJWTZ3Z2.js +2 -0
  47. package/dist/bundle/chunks/{chunk-ISZ4EWIS.js → chunk-SODMQEHO.js} +1 -1
  48. package/dist/bundle/chunks/{chunk-WEGTCUO3.js → chunk-SQAQEITV.js} +1 -1
  49. package/dist/bundle/chunks/{chunk-UFVZTQKN.js → chunk-TQQG5L4D.js} +1 -1
  50. package/dist/bundle/chunks/{chunk-662SCMTH.js → chunk-U6A4X3FV.js} +1 -1
  51. package/dist/bundle/chunks/{chunk-ACIFXWJ6.js → chunk-UHUM55TR.js} +1 -1
  52. package/dist/bundle/chunks/{chunk-L5OFGHDA.js → chunk-USKWGPA6.js} +1 -1
  53. package/dist/bundle/chunks/{chunk-7ORS33CE.js → chunk-VDDJADPV.js} +1 -1
  54. package/dist/bundle/chunks/{chunk-R65P6LAT.js → chunk-VIHXG53S.js} +1 -1
  55. package/dist/bundle/chunks/{chunk-SGS76663.js → chunk-VPVD5G32.js} +1 -1
  56. package/dist/bundle/chunks/chunk-WO6I5PRF.js +3 -0
  57. package/dist/bundle/chunks/{chunk-ETCYVVVO.js → chunk-WVEIJ437.js} +1 -1
  58. package/dist/bundle/chunks/{chunk-VRWJJMHO.js → chunk-XKFG4N3Z.js} +1 -1
  59. package/dist/bundle/chunks/{chunk-57MRFI6E.js → chunk-XMWPCJU6.js} +1 -1
  60. package/dist/bundle/chunks/{chunk-GDPWQJDQ.js → chunk-XPSXUKSD.js} +1 -1
  61. package/dist/bundle/chunks/{chunk-FNBEICAA.js → chunk-YI6KRJDC.js} +1 -1
  62. package/dist/bundle/chunks/{chunk-UURGXV5B.js → chunk-YP47G4YP.js} +1 -1
  63. package/dist/bundle/chunks/{chunk-TXOR5BQL.js → chunk-YRYDCKUI.js} +1 -1
  64. package/dist/bundle/chunks/{chunk-QAM6AXA2.js → chunk-YTKZSGMQ.js} +12 -9
  65. package/dist/bundle/chunks/{chunk-V36CXIIB.js → chunk-YXL3PNTL.js} +1 -1
  66. package/dist/bundle/chunks/chunk-Z7HTHT6K.js +2 -0
  67. package/dist/bundle/chunks/{chunk-27XEDYQA.js → chunk-ZTPG27EJ.js} +1 -1
  68. package/dist/bundle/chunks/{cli-main-534A3WSC.js → cli-main-RN7YB427.js} +1 -1
  69. package/dist/bundle/chunks/{cloudflare-workers-ai-system-one-FC7QLRU2.js → cloudflare-workers-ai-system-one-N5KFNKK4.js} +1 -1
  70. package/dist/bundle/chunks/credential-accounts-BJX55MTT.js +2 -0
  71. package/dist/bundle/chunks/devin.js +10 -2
  72. package/dist/bundle/chunks/{dispatch-UY6LBJJE.js → dispatch-M7UAPGVY.js} +1 -1
  73. package/dist/bundle/chunks/{google-generative-ai-7PQ4QRHJ.js → google-generative-ai-2TZSCSIS.js} +1 -1
  74. package/dist/bundle/chunks/{google-vertex-DY7EO4EJ.js → google-vertex-7NJJOI7V.js} +1 -1
  75. package/dist/bundle/chunks/{health-write-EB4CPHXN.js → health-write-YUSPVH7U.js} +1 -1
  76. package/dist/bundle/chunks/{help-fast-path-U2PSNNWO.js → help-fast-path-DWCA77QP.js} +1 -1
  77. package/dist/bundle/chunks/host-command-KJXUTSQ7.js +2 -0
  78. package/dist/bundle/chunks/{host-lifecycle-TGG7KGQH.js → host-lifecycle-QHIJ4G2K.js} +1 -1
  79. package/dist/bundle/chunks/host-lifecycle.js +1 -1
  80. package/dist/bundle/chunks/{host-session-command-PWVPQ2OV.js → host-session-command-IPH3WXSG.js} +1 -1
  81. package/dist/bundle/chunks/{host-socket-request-F2R5P34M.js → host-socket-request-XPXJV3CS.js} +1 -1
  82. package/dist/bundle/chunks/interactive-mode-XUXMDYLA.js +2 -0
  83. package/dist/bundle/chunks/{list-tips-3D3A6GFK.js → list-tips-XJY7EL5S.js} +1 -1
  84. package/dist/bundle/chunks/{login-health-7G2DZVOV.js → login-health-226NO44U.js} +1 -1
  85. package/dist/bundle/chunks/main-P7PKMJUG.js +2 -0
  86. package/dist/bundle/chunks/{mistral-conversations-6F2RAIB2.js → mistral-conversations-QMHMHSUF.js} +1 -1
  87. package/dist/bundle/chunks/{multi-session-host-7E26R5JF.js → multi-session-host-ZQY4AN46.js} +4 -4
  88. package/dist/bundle/chunks/{native-loader-NMDTMHMA.js → native-loader-A6DNMGCK.js} +1 -1
  89. package/dist/bundle/chunks/openai-chatgpt.js +11 -3
  90. package/dist/bundle/chunks/{openai-codex-responses-VNBMMIL3.js → openai-codex-responses-GUAHKQE2.js} +1 -1
  91. package/dist/bundle/chunks/{openai-completions-JLR32APM.js → openai-completions-UPRTFM54.js} +1 -1
  92. package/dist/bundle/chunks/{openai-images-LEBNERSM.js → openai-images-IKWNS6VB.js} +1 -1
  93. package/dist/bundle/chunks/{openai-responses-JCDUNMHA.js → openai-responses-AXN5NUHH.js} +1 -1
  94. package/dist/bundle/chunks/openrouter.js +10 -2
  95. package/dist/bundle/chunks/{package-manager-cli-CBSDV36M.js → package-manager-cli-PP7L2Q5M.js} +1 -1
  96. package/dist/bundle/chunks/radius.js +10 -2
  97. package/dist/bundle/chunks/{rg-engine-JDUYBM6L.js → rg-engine-VRSXT6HG.js} +1 -1
  98. package/dist/bundle/chunks/{role-LP6LVNCY.js → role-L5J7ZPCF.js} +1 -1
  99. package/dist/bundle/chunks/rotation-stream-6INZXQFC.js +2 -0
  100. package/dist/bundle/chunks/rpc-mode-W4UATRAT.js +2 -0
  101. package/dist/bundle/chunks/{rpc-monitor-source-5TUR27NY.js → rpc-monitor-source-RMMUPBMS.js} +1 -1
  102. package/dist/bundle/chunks/{run-attached-tui-PVLUHHJX.js → run-attached-tui-RLXFGCEP.js} +1 -1
  103. package/dist/bundle/chunks/{schedule-command-W45NYTTK.js → schedule-command-ZNFEJNTT.js} +1 -1
  104. package/dist/bundle/chunks/{session-control-endpoint-LFLUNALC.js → session-control-endpoint-TWTONJWF.js} +1 -1
  105. package/dist/bundle/chunks/{session-picker-OZUINLD7.js → session-picker-KRUVCHOV.js} +1 -1
  106. package/dist/bundle/chunks/session-slot-NJOZCOHQ.js +2 -0
  107. package/dist/bundle/chunks/session-worker.js +492 -461
  108. package/dist/bundle/chunks/{state-store-SZARKD36.js → state-store-ZAFZ4JOM.js} +1 -1
  109. package/dist/bundle/chunks/{typesafe-system-one-5JYDJK6S.js → typesafe-system-one-CICIRAYN.js} +1 -1
  110. package/dist/bundle/cli.js +2 -2
  111. package/dist/bundle/host-supervisor.js +1 -1
  112. package/dist/bundle/index.js +1 -1
  113. package/dist/bundle/rpc-entry.js +1 -1
  114. package/dist/bundle/runtime-manifest.json +1 -1
  115. package/dist/core/agent-session.d.ts +8 -0
  116. package/dist/core/agent-session.js +73 -63
  117. package/dist/core/auth-storage.d.ts +19 -0
  118. package/dist/core/auth-storage.js +99 -24
  119. package/dist/core/auth-store-load-error.d.ts +29 -0
  120. package/dist/core/auth-store-load-error.js +74 -0
  121. package/dist/core/brand.d.ts +2 -0
  122. package/dist/core/brand.js +1 -0
  123. package/dist/core/credential-pool/classify.d.ts +3 -2
  124. package/dist/core/credential-pool/classify.js +10 -3
  125. package/dist/core/credential-pool/failover.d.ts +8 -0
  126. package/dist/core/credential-pool/failover.js +18 -5
  127. package/dist/core/credential-pool/rotation-stream.d.ts +1 -1
  128. package/dist/core/credential-pool/rotation-stream.js +2 -7
  129. package/dist/core/credential-pool/session-slot.d.ts +20 -0
  130. package/dist/core/credential-pool/session-slot.js +26 -0
  131. package/dist/core/credential-recovery-revision.d.ts +10 -0
  132. package/dist/core/credential-recovery-revision.js +19 -0
  133. package/dist/core/dynamic-prompt/build.d.ts +2 -1
  134. package/dist/core/dynamic-prompt/build.js +1 -2
  135. package/dist/core/dynamic-prompt/handoff.d.ts +1 -1
  136. package/dist/core/dynamic-prompt/handoff.js +9 -9
  137. package/dist/core/dynamic-prompt/identity.js +1 -1
  138. package/dist/core/dynamic-prompt/intent-gate.d.ts +1 -1
  139. package/dist/core/dynamic-prompt/intent-gate.js +8 -8
  140. package/dist/core/dynamic-prompt/policies.d.ts +1 -1
  141. package/dist/core/dynamic-prompt/policies.js +5 -7
  142. package/dist/core/dynamic-prompt/style.js +5 -5
  143. package/dist/core/dynamic-prompt/tool-section.d.ts +5 -0
  144. package/dist/core/dynamic-prompt/tool-section.js +8 -6
  145. package/dist/core/dynamic-prompt/verification.d.ts +6 -6
  146. package/dist/core/dynamic-prompt/verification.js +12 -12
  147. package/dist/core/dynamic-prompt/working-task.d.ts +1 -1
  148. package/dist/core/dynamic-prompt/working-task.js +3 -3
  149. package/dist/core/dynamic-prompt/workstation.js +1 -1
  150. package/dist/core/execution/executor-body.d.ts +22 -0
  151. package/dist/core/execution/executor-body.js +73 -0
  152. package/dist/core/execution/executor-freeze.d.ts +17 -0
  153. package/dist/core/execution/executor-freeze.js +53 -0
  154. package/dist/core/execution/executor-pipeline.d.ts +9 -0
  155. package/dist/core/execution/executor-pipeline.js +25 -0
  156. package/dist/core/execution/executor-types.d.ts +82 -0
  157. package/dist/core/execution/executor-types.js +2 -0
  158. package/dist/core/execution/executor.d.ts +8 -0
  159. package/dist/core/execution/executor.js +216 -0
  160. package/dist/core/extensions/builtin/anthropic-subscription/account-block-log.d.ts +15 -0
  161. package/dist/core/extensions/builtin/anthropic-subscription/account-block-log.js +18 -0
  162. package/dist/core/extensions/builtin/anthropic-subscription/affinity.d.ts +9 -1
  163. package/dist/core/extensions/builtin/anthropic-subscription/affinity.js +15 -4
  164. package/dist/core/extensions/builtin/anthropic-subscription/failover.d.ts +7 -1
  165. package/dist/core/extensions/builtin/anthropic-subscription/failover.js +19 -2
  166. package/dist/core/extensions/builtin/anthropic-subscription/guidance.d.ts +8 -2
  167. package/dist/core/extensions/builtin/anthropic-subscription/guidance.js +25 -1
  168. package/dist/core/extensions/builtin/anthropic-subscription/provider-diagnostic.d.ts +9 -0
  169. package/dist/core/extensions/builtin/anthropic-subscription/provider-diagnostic.js +59 -0
  170. package/dist/core/extensions/builtin/anthropic-subscription/session-registry-pump.d.ts +13 -0
  171. package/dist/core/extensions/builtin/anthropic-subscription/session-registry-pump.js +64 -2
  172. package/dist/core/extensions/builtin/anthropic-subscription/session-registry-wiring.js +7 -0
  173. package/dist/core/extensions/builtin/anthropic-subscription/session-stream.js +5 -1
  174. package/dist/core/extensions/builtin/anthropic-subscription/session-turn-attempt.d.ts +7 -0
  175. package/dist/core/extensions/builtin/anthropic-subscription/session-turn-attempt.js +28 -1
  176. package/dist/core/extensions/builtin/anthropic-subscription/session-turn-types.d.ts +2 -0
  177. package/dist/core/extensions/builtin/anthropic-subscription/stream-guidance.js +5 -3
  178. package/dist/core/extensions/builtin/anthropic-subscription/stream.js +4 -0
  179. package/dist/core/extensions/builtin/bash-timeout/index.d.ts +1 -1
  180. package/dist/core/extensions/builtin/bash-timeout/index.js +3 -28
  181. package/dist/core/extensions/builtin/bash-timeout/timeout.d.ts +0 -11
  182. package/dist/core/extensions/builtin/bash-timeout/timeout.js +0 -11
  183. package/dist/core/extensions/builtin/btw/index.js +3 -1
  184. package/dist/core/extensions/builtin/cache-keepalive/index.js +3 -1
  185. package/dist/core/extensions/builtin/compaction/index.js +1 -1
  186. package/dist/core/extensions/builtin/compaction/openai-remote.d.ts +3 -1
  187. package/dist/core/extensions/builtin/compaction/openai-remote.js +1 -1
  188. package/dist/core/extensions/builtin/compaction/speculative.js +3 -2
  189. package/dist/core/extensions/builtin/cursor-cli-oauth/account-command.js +3 -1
  190. package/dist/core/extensions/builtin/cursor-cli-oauth/native-bootstrap.js +3 -1
  191. package/dist/core/extensions/builtin/imagegen/index.d.ts +0 -1
  192. package/dist/core/extensions/builtin/imagegen/index.js +0 -11
  193. package/dist/core/extensions/builtin/look-at/runner.js +3 -1
  194. package/dist/core/extensions/builtin/mcp/auth/callback.js +3 -4
  195. package/dist/core/extensions/builtin/mcp/auth/oauth-provider.js +2 -1
  196. package/dist/core/extensions/builtin/prompt-preset/execution-tooling.d.ts +5 -5
  197. package/dist/core/extensions/builtin/prompt-preset/execution-tooling.js +5 -5
  198. package/dist/core/extensions/builtin/prompt-preset/glm-5.d.ts +1 -1
  199. package/dist/core/extensions/builtin/prompt-preset/glm-5.js +1 -1
  200. package/dist/core/extensions/builtin/terminal/extension.js +8 -1
  201. package/dist/core/extensions/builtin/terminal/notify.js +9 -3
  202. package/dist/core/extensions/builtin/terminal/prompt.d.ts +7 -1
  203. package/dist/core/extensions/builtin/terminal/prompt.js +27 -60
  204. package/dist/core/extensions/builtin/terminal/tools/bash.js +10 -2
  205. package/dist/core/extensions/builtin/todotools/normalize.d.ts +20 -3
  206. package/dist/core/extensions/builtin/todotools/normalize.js +251 -5
  207. package/dist/core/extensions/builtin/todotools/prompt.d.ts +2 -2
  208. package/dist/core/extensions/builtin/todotools/prompt.js +5 -5
  209. package/dist/core/extensions/builtin/todotools/tools/todo.js +9 -8
  210. package/dist/core/extensions/builtin/webfetch/webfetch/tool.js +0 -5
  211. package/dist/core/extensions/builtin/websearch/index.js +9 -4
  212. package/dist/core/extensions/builtin/websearch/websearch/tool.d.ts +2 -0
  213. package/dist/core/extensions/builtin/websearch/websearch/tool.js +3 -1
  214. package/dist/core/model-registry.d.ts +10 -2
  215. package/dist/core/model-registry.js +12 -4
  216. package/dist/core/model-runtime.d.ts +8 -0
  217. package/dist/core/model-runtime.js +39 -1
  218. package/dist/core/provider-failure.d.ts +6 -0
  219. package/dist/core/provider-failure.js +6 -2
  220. package/dist/core/retry-fallback/controller-types.d.ts +7 -0
  221. package/dist/core/retry-fallback/controller.d.ts +2 -0
  222. package/dist/core/retry-fallback/controller.js +13 -4
  223. package/dist/core/retry-fallback/credential-recovery.d.ts +18 -0
  224. package/dist/core/retry-fallback/credential-recovery.js +32 -0
  225. package/dist/core/retry-fallback/exhaustion.d.ts +18 -0
  226. package/dist/core/retry-fallback/exhaustion.js +20 -0
  227. package/dist/core/retry-fallback/settings.d.ts +8 -1
  228. package/dist/core/retry-fallback/settings.js +12 -6
  229. package/dist/core/session-log.js +1 -1
  230. package/dist/core/settings-manager.js +4 -1
  231. package/dist/core/system-prompt.js +1 -1
  232. package/dist/core/tools/bash.d.ts +3 -1
  233. package/dist/core/tools/bash.js +14 -4
  234. package/dist/core/tools/edit.d.ts +1 -1
  235. package/dist/core/tools/edit.js +1 -6
  236. package/dist/core/tools/grep/index.js +2 -3
  237. package/dist/core/tools/powershell.d.ts +1 -1
  238. package/dist/core/tools/powershell.js +1 -1
  239. package/dist/core/tools/read.d.ts +1 -1
  240. package/dist/core/tools/read.js +1 -1
  241. package/dist/core/tools/write.d.ts +1 -1
  242. package/dist/core/tools/write.js +1 -1
  243. package/dist/engine-trust/shell-trust.d.ts +36 -0
  244. package/dist/engine-trust/shell-trust.js +44 -0
  245. package/dist/engine-trust/transport-failure.d.ts +12 -0
  246. package/dist/engine-trust/transport-failure.js +48 -0
  247. package/dist/modes/interactive/interactive-mode.js +9 -3
  248. package/dist/modes/print-mode.js +2 -1
  249. package/dist/modes/rpc/compact-catalog-families.d.ts +5 -2
  250. package/dist/modes/rpc/compact-catalog-families.js +5 -2
  251. package/dist/modes/rpc/compact-catalog-registry.d.ts +9 -1
  252. package/dist/modes/rpc/compact-catalog-registry.js +9 -2
  253. package/dist/modes/rpc/compact-recovery-manifest-kinds.d.ts +17 -0
  254. package/dist/modes/rpc/compact-recovery-manifest-kinds.js +21 -0
  255. package/dist/modes/rpc/compact-recovery-manifest-shape.d.ts +7 -0
  256. package/dist/modes/rpc/compact-recovery-manifest.js +11 -3
  257. package/dist/modes/rpc/compact-socket-commands.js +6 -0
  258. package/dist/modes/rpc/connection-handler.js +24 -6
  259. package/dist/modes/rpc/connection-question-bridge.d.ts +3 -1
  260. package/dist/modes/rpc/connection-question-bridge.js +4 -1
  261. package/dist/modes/rpc/connection-roles.d.ts +8 -0
  262. package/dist/modes/rpc/connection-roles.js +18 -0
  263. package/dist/modes/rpc/effect-commands.d.ts +1 -0
  264. package/dist/modes/rpc/loopless-execute-grant.d.ts +20 -0
  265. package/dist/modes/rpc/loopless-execute-grant.js +26 -0
  266. package/dist/modes/rpc/loopless-runtime-settings.d.ts +30 -0
  267. package/dist/modes/rpc/loopless-runtime-settings.js +69 -0
  268. package/dist/modes/rpc/loopless-tool-call.d.ts +12 -0
  269. package/dist/modes/rpc/loopless-tool-call.js +22 -0
  270. package/dist/modes/rpc/method-classes-schema-session.d.ts +4 -0
  271. package/dist/modes/rpc/method-classes-schema-session.js +1 -0
  272. package/dist/modes/rpc/method-classes-schema.d.ts +4 -0
  273. package/dist/modes/rpc/protocol-identity.js +2 -0
  274. package/dist/modes/rpc/remote-origin.d.ts +31 -0
  275. package/dist/modes/rpc/remote-origin.js +58 -0
  276. package/dist/modes/rpc/rpc-protocol-info-types.d.ts +8 -0
  277. package/dist/modes/rpc/rpc-question-types.d.ts +7 -0
  278. package/dist/modes/rpc/rpc-question-types.js +0 -3
  279. package/dist/modes/rpc/rpc-read-response-types.d.ts +2 -1
  280. package/dist/modes/rpc/rpc-session-command-types.d.ts +3 -0
  281. package/dist/modes/rpc/rpc-session-state-types.d.ts +10 -0
  282. package/dist/modes/rpc/rpc-types.d.ts +1 -1
  283. package/dist/modes/rpc/runtime-factory-loopless.d.ts +57 -0
  284. package/dist/modes/rpc/runtime-factory-loopless.js +131 -0
  285. package/dist/modes/rpc/session-registry.js +12 -3
  286. package/dist/modes/rpc/session-worker-client.js +2 -0
  287. package/dist/modes/rpc/session-worker-protocol.d.ts +3 -0
  288. package/dist/modes/rpc/session-worker.js +2 -0
  289. package/docs/development.md +1 -1
  290. package/docs/engine-socket/fixtures/admission-routes/c-baseline.json +3 -3
  291. package/docs/engine-socket/fixtures/admission-routes/h-legacy.json +1 -1
  292. package/docs/engine-socket/fixtures/admission-routes/i-reset-stream.json +3 -3
  293. package/docs/engine-socket/fixtures/recovery-manifest/index.json +32 -0
  294. package/docs/engine-socket/fixtures/recovery-manifest/one-partial-family.json +118 -0
  295. package/docs/rpc.md +95 -3
  296. package/package.json +6 -6
  297. package/dist/bundle/chunks/anthropic-messages-WUPLDYD6.js +0 -10
  298. package/dist/bundle/chunks/chunk-7UC4W2T2.js +0 -2
  299. package/dist/bundle/chunks/chunk-O4GRBX4R.js +0 -3
  300. package/dist/bundle/chunks/chunk-R23WXN4L.js +0 -448
  301. package/dist/bundle/chunks/chunk-W5NLCRB3.js +0 -2
  302. package/dist/bundle/chunks/credential-accounts-OAK2E5O2.js +0 -2
  303. package/dist/bundle/chunks/host-command-WJIMRBGN.js +0 -2
  304. package/dist/bundle/chunks/interactive-mode-NPHFDPGH.js +0 -2
  305. package/dist/bundle/chunks/main-I6FJAFUP.js +0 -2
  306. package/dist/bundle/chunks/rotation-stream-ZDCNHIBE.js +0 -2
  307. package/dist/bundle/chunks/rpc-mode-GBBTN2WP.js +0 -2
@@ -51,6 +51,7 @@ import { CompactionLifecycleCoordinator } from "./compaction/lifecycle.js";
51
51
  import { isTurnStuckOnContextOverflow } from "./compaction/stuck-overflow.js";
52
52
  import { isWarmSummaryAnchorValid } from "./compaction/warm-anchor.js";
53
53
  import { CONTINUE_FROM_LEAF_CUSTOM_TYPE, CONTINUE_FROM_LEAF_DIRECTIVE, ContinueFromLeafError, trackTurnAdmission, } from "./continue-from-leaf.js";
54
+ import { credentialRecoveryRevision } from "./credential-recovery-revision.js";
54
55
  import { admitCursorHistory, cursorAdmissionBudgetBytes } from "./cursor-history-admission.js";
55
56
  import { DEFAULT_THINKING_LEVEL } from "./defaults.js";
56
57
  import { resolveDiscoveredResourcePaths } from "./discovered-resource-scope.js";
@@ -128,6 +129,7 @@ import { createModelConfigErrorReporter, isModelConfigFailure, } from "./retry-f
128
129
  import { ProbeBackScheduler } from "./retry-fallback/probe-scheduler.js";
129
130
  import { REQUEST_SHAPE_FAILURE_TEXT, REQUEST_SHAPE_RECOVERY_ENTRY_TYPE, REQUEST_SHAPE_RECOVERY_NOTE, RequestShapeRecovery, } from "./retry-fallback/request-shape.js";
130
131
  import { AccountUsageLimitError, describeUsageLimitEntry, describeUsageLimitMessage, usageLimitScope, } from "./retry-fallback/usage-limit.js";
132
+ import { recordRungFailure } from "./retry-fallback/exhaustion.js";
131
133
  import { validateFallbackChains } from "./retry-fallback/validate.js";
132
134
  import { RunHolds } from "./run-holds.js";
133
135
  import { startRunResumeTurn } from "./run-resume-turn.js";
@@ -610,6 +612,8 @@ export class AgentSession {
610
612
  this._userActivity = createUserActivitySource();
611
613
  this._extensionMode = "print";
612
614
  this._providerRequiredNoticed = false;
615
+ // Every rung that failed this turn, in order: the exhaustion event leads with the first (senpi#4084).
616
+ this._fallbackTurnFailures = [];
613
617
  this._probeBackLaneSeq = 0;
614
618
  // Tool registry for extension getTools/setTools
615
619
  this._toolRegistry = new Map();
@@ -901,6 +905,7 @@ export class AgentSession {
901
905
  emit: (event) => this._emit(event),
902
906
  getCurrentSelector: () => (this.model ? { model: this.model, thinkingLevel: this.thinkingLevel } : undefined),
903
907
  isAuthAvailable: (provider) => this._modelRuntime.hasConfiguredAuth(provider),
908
+ recoveryRevision: (provider) => credentialRecoveryRevision(this._modelRuntime.authStorage, provider),
904
909
  });
905
910
  this._unfollowProviderAccountChanges = followProviderAccountChanges(this._retryFallback);
906
911
  this._probeBackScheduler = new ProbeBackScheduler({
@@ -1749,6 +1754,16 @@ export class AgentSession {
1749
1754
  this.agent.state.messages = this.sessionManager.buildSessionContext().messages;
1750
1755
  this._emitEntryAppended(entry.id);
1751
1756
  }
1757
+ _emitFallbackExhausted(chainKey, lastError) {
1758
+ const failures = [...this._fallbackTurnFailures];
1759
+ const primaryError = failures[0];
1760
+ this._emit({
1761
+ type: "retry_fallback_exhausted",
1762
+ chainKey,
1763
+ lastError,
1764
+ ...(primaryError === undefined ? {} : { primaryError, failures }),
1765
+ });
1766
+ }
1752
1767
  _emit(event, captureSource = event) {
1753
1768
  this._recoveryIdentity?.observe(event);
1754
1769
  const produced = event;
@@ -1761,11 +1776,8 @@ export class AgentSession {
1761
1776
  if (event.type === "entry_appended") {
1762
1777
  event = { ...event, entry: describeUsageLimitEntry(event.entry) };
1763
1778
  }
1764
- if (event.type === "retry_fallback_exhausted" && usageLimitScope(event.lastError) === "account") {
1765
- event = {
1766
- ...event,
1767
- lastError: new AccountUsageLimitError(this.model?.provider ?? "", event.lastError).message,
1768
- };
1779
+ if (event.type === "retry_fallback_exhausted") {
1780
+ event = describeExhaustedFailures(event, this.model?.provider ?? "");
1769
1781
  }
1770
1782
  if (event.type === "agent_end") {
1771
1783
  event = {
@@ -2556,6 +2568,7 @@ export class AgentSession {
2556
2568
  if (event.type === "message_start" && event.message.role === "user") {
2557
2569
  this._overflowRecoveryRungs = 0;
2558
2570
  this._retryFallback.resetTurn();
2571
+ this._fallbackTurnFailures = [];
2559
2572
  const messageText = userMessageText(event.message.content);
2560
2573
  // Image-only input has no text to match: find it by the message the queue handed to the agent (#3592).
2561
2574
  const imageOnlyQueued = messageText
@@ -4107,7 +4120,10 @@ export class AgentSession {
4107
4120
  const promptGuidelines = [];
4108
4121
  for (const name of contributingToolNames) {
4109
4122
  const snippet = this._toolPromptSnippets.get(name);
4110
- if (snippet) {
4123
+ // A model-facing tool whose schema description the model already reads gets no snippet line;
4124
+ // withheld eval-only tools keep theirs (senpi#5335).
4125
+ const describedToModel = validToolNames.includes(name) && (this._toolRegistry.get(name)?.description.trim() ?? "") !== "";
4126
+ if (snippet && !describedToModel) {
4111
4127
  toolSnippets[name] = snippet;
4112
4128
  }
4113
4129
  const toolGuidelines = this._toolPromptGuidelines.get(name);
@@ -4130,38 +4146,40 @@ export class AgentSession {
4130
4146
  customPrompt: loaderSystemPrompt,
4131
4147
  appendSystemPrompt: loaderAppendSystemPrompt.length > 0 ? loaderAppendSystemPrompt.join("\n\n") : undefined,
4132
4148
  };
4149
+ const evalOnlyArmed = this._isEvalOnlyPolicyArmed();
4150
+ const armed = evalOnlyArmed ? (this._evalOnlyToolNames ?? new Set()) : new Set();
4151
+ const registered = new Set([...armed].filter((name) => this._toolRegistry.has(name)));
4152
+ // The eval-only paragraph below states the grep rule for this render, so the grep snippet that
4153
+ // renders the eval-only grep guideline stays out of it (senpi#5335).
4154
+ const { grep: _grepSnippet, ...snippetsWithoutGrep } = toolSnippets;
4133
4155
  const prompt = buildDynamicSystemPrompt({
4134
4156
  ...this._baseSystemPromptOptions,
4157
+ ...(registered.has("grep") ? { toolSnippets: snippetsWithoutGrep } : {}),
4135
4158
  onParts: (parts) => {
4136
4159
  this._baseSystemPromptParts = parts;
4137
4160
  },
4138
4161
  });
4139
- if (!this._isEvalOnlyPolicyArmed())
4162
+ if (!evalOnlyArmed)
4140
4163
  return prompt;
4141
- const armed = this._evalOnlyToolNames ?? new Set();
4142
- const registered = new Set([...armed].filter((name) => this._toolRegistry.has(name)));
4143
- const sentences = [];
4164
+ const helpers = [];
4144
4165
  const shellHelpers = ["bash", "powershell"].filter((name) => registered.has(name)).map(evalHelperCall);
4145
- if (shellHelpers.length > 0) {
4146
- sentences.push(`Shell commands run ONLY inside eval cells via ${shellHelpers.join(" or ")}; hooks and permissions still apply.`);
4147
- }
4148
- if (registered.has("workflow")) {
4149
- sentences.push(`The workflow tool runs ONLY inside eval cells via ${evalHelperCall("workflow")}; hooks and permissions still apply.`);
4150
- }
4166
+ if (shellHelpers.length > 0)
4167
+ helpers.push(`${shellHelpers.join(" / ")} shell`);
4168
+ if (registered.has("workflow"))
4169
+ helpers.push(evalHelperCall("workflow"));
4151
4170
  // Declared tools and SDK overrides outside the built-in groups still need eval guidance.
4152
- const otherHelpers = [...registered]
4171
+ helpers.push(...[...registered]
4153
4172
  .filter((name) => name !== "bash" && name !== "powershell" && name !== "workflow" && name !== "grep" && name !== "glob")
4154
- .map(evalHelperCall);
4155
- if (otherHelpers.length > 0) {
4156
- sentences.push(`These tools run ONLY inside eval cells via ${otherHelpers.join(" or ")}; hooks and permissions still apply.`);
4157
- }
4173
+ .map(evalHelperCall));
4158
4174
  if (registered.has("glob")) {
4159
- sentences.push(`File-name search runs ONLY inside eval cells via ${evalHelperCall("glob")}; it matches a glob such as **/*.ts, respects .gitignore, and applies hooks and permissions - prefer it over find/ls in tool.bash.`);
4175
+ helpers.push(`${evalHelperCall("glob")} file-name search (glob like **/*.ts, respects .gitignore; over find/ls)`);
4160
4176
  }
4161
4177
  if (registered.has("grep")) {
4162
- sentences.push(`Text search runs ONLY inside eval cells via ${evalHelperCall("grep")}; it returns structured matches, respects .gitignore, and applies hooks and permissions - prefer it over rg/grep in tool.bash.`);
4178
+ helpers.push(`${evalHelperCall("grep")} text search (structured, respects .gitignore; over rg/grep; page full -> skip, narrow with glob/path)`);
4163
4179
  }
4164
- return sentences.length > 0 ? `${prompt}\n\n${sentences.join("\n\n")}` : prompt;
4180
+ return helpers.length > 0
4181
+ ? `${prompt}\n\nEval-only tools (hooks + permissions still apply): ${helpers.join("; ")}.`
4182
+ : prompt;
4165
4183
  }
4166
4184
  /**
4167
4185
  * Run `input` extension handlers for queued (steer / follow-up) input.
@@ -4481,6 +4499,7 @@ export class AgentSession {
4481
4499
  const model = this.model;
4482
4500
  if (model && !this._pendingModelSwitch && this._retryFallback.isProviderSpent(model.provider)) {
4483
4501
  this._retryFallback.resetTurn();
4502
+ this._fallbackTurnFailures = [];
4484
4503
  const errorMessage = this._retryFallback.accountError(model.provider);
4485
4504
  // Routing around a remembered failure did not observe another provider failure.
4486
4505
  if (!(await this._retryFallback.tryFallback("transient", { errorMessage }, false))) {
@@ -10427,11 +10446,7 @@ export class AgentSession {
10427
10446
  const finishRetryAttempt = (attempt, finalError) => {
10428
10447
  const exhaustedChainKey = this._retryFallback.exhaustedChainKey;
10429
10448
  if (exhaustedChainKey) {
10430
- this._emit({
10431
- type: "retry_fallback_exhausted",
10432
- chainKey: exhaustedChainKey,
10433
- lastError: errorMessage,
10434
- });
10449
+ this._emitFallbackExhausted(exhaustedChainKey, errorMessage);
10435
10450
  }
10436
10451
  this._emit({
10437
10452
  type: "auto_retry_end",
@@ -10494,6 +10509,11 @@ export class AgentSession {
10494
10509
  let is429TierRouted = false;
10495
10510
  let hintTierDelayMs;
10496
10511
  const tryFallback = async (reason, failure) => {
10512
+ recordRungFailure(this._fallbackTurnFailures, {
10513
+ model: `${message.provider}/${message.model}`,
10514
+ reason,
10515
+ error: errorMessage,
10516
+ });
10497
10517
  try {
10498
10518
  return await this._retryFallback.tryFallback(reason, failure);
10499
10519
  }
@@ -10513,7 +10533,7 @@ export class AgentSession {
10513
10533
  if (!switchedFallback) {
10514
10534
  const chainKey = this._retryFallback.exhaustedChainKey;
10515
10535
  if (chainKey)
10516
- this._emit({ type: "retry_fallback_exhausted", chainKey, lastError: errorMessage });
10536
+ this._emitFallbackExhausted(chainKey, errorMessage);
10517
10537
  if (this._retryAttempt > 0) {
10518
10538
  this._emit({
10519
10539
  type: "auto_retry_end",
@@ -10570,11 +10590,7 @@ export class AgentSession {
10570
10590
  if (!switchedFallback) {
10571
10591
  const exhaustedChainKey = this._retryFallback.exhaustedChainKey;
10572
10592
  if (exhaustedChainKey) {
10573
- this._emit({
10574
- type: "retry_fallback_exhausted",
10575
- chainKey: exhaustedChainKey,
10576
- lastError: errorMessage,
10577
- });
10593
+ this._emitFallbackExhausted(exhaustedChainKey, errorMessage);
10578
10594
  }
10579
10595
  this._resolveRetry();
10580
10596
  return "not-handled";
@@ -10604,11 +10620,7 @@ export class AgentSession {
10604
10620
  if (!switchedFallback) {
10605
10621
  const exhaustedChainKey = this._retryFallback.exhaustedChainKey;
10606
10622
  if (exhaustedChainKey) {
10607
- this._emit({
10608
- type: "retry_fallback_exhausted",
10609
- chainKey: exhaustedChainKey,
10610
- lastError: errorMessage,
10611
- });
10623
+ this._emitFallbackExhausted(exhaustedChainKey, errorMessage);
10612
10624
  }
10613
10625
  if (this._retryAttempt > 0) {
10614
10626
  this._emit({
@@ -10669,11 +10681,7 @@ export class AgentSession {
10669
10681
  else {
10670
10682
  const exhaustedChainKey = this._retryFallback.exhaustedChainKey;
10671
10683
  if (exhaustedChainKey) {
10672
- this._emit({
10673
- type: "retry_fallback_exhausted",
10674
- chainKey: exhaustedChainKey,
10675
- lastError: errorMessage,
10676
- });
10684
+ this._emitFallbackExhausted(exhaustedChainKey, errorMessage);
10677
10685
  }
10678
10686
  this._emit({
10679
10687
  type: "auto_retry_end",
@@ -10721,11 +10729,7 @@ export class AgentSession {
10721
10729
  else {
10722
10730
  const exhaustedChainKey = this._retryFallback.exhaustedChainKey;
10723
10731
  if (exhaustedChainKey) {
10724
- this._emit({
10725
- type: "retry_fallback_exhausted",
10726
- chainKey: exhaustedChainKey,
10727
- lastError: errorMessage,
10728
- });
10732
+ this._emitFallbackExhausted(exhaustedChainKey, errorMessage);
10729
10733
  }
10730
10734
  this._emit({
10731
10735
  type: "auto_retry_end",
@@ -10762,11 +10766,7 @@ export class AgentSession {
10762
10766
  else {
10763
10767
  const exhaustedChainKey = this._retryFallback.exhaustedChainKey;
10764
10768
  if (exhaustedChainKey) {
10765
- this._emit({
10766
- type: "retry_fallback_exhausted",
10767
- chainKey: exhaustedChainKey,
10768
- lastError: errorMessage,
10769
- });
10769
+ this._emitFallbackExhausted(exhaustedChainKey, errorMessage);
10770
10770
  }
10771
10771
  this._emit({
10772
10772
  type: "auto_retry_end",
@@ -10822,11 +10822,7 @@ export class AgentSession {
10822
10822
  else {
10823
10823
  const exhaustedChainKey = this._retryFallback.exhaustedChainKey;
10824
10824
  if (exhaustedChainKey) {
10825
- this._emit({
10826
- type: "retry_fallback_exhausted",
10827
- chainKey: exhaustedChainKey,
10828
- lastError: errorMessage,
10829
- });
10825
+ this._emitFallbackExhausted(exhaustedChainKey, errorMessage);
10830
10826
  }
10831
10827
  this._emit({
10832
10828
  type: "auto_retry_end",
@@ -11109,8 +11105,8 @@ export class AgentSession {
11109
11105
  else {
11110
11106
  // Add to agent state immediately
11111
11107
  this.agent.state.messages.push(bashMessage);
11112
- // Save to session
11113
- this.sessionManager.appendMessage(bashMessage);
11108
+ // Save to session; every RPC observer learns of it the way it learns of any persisted entry (senpi#5576).
11109
+ this._emitEntryAppended(this.sessionManager.appendMessage(bashMessage));
11114
11110
  this._incrementMessageRevision();
11115
11111
  }
11116
11112
  }
@@ -11145,7 +11141,7 @@ export class AgentSession {
11145
11141
  // Add to agent state
11146
11142
  this.agent.state.messages.push(bashMessage);
11147
11143
  // Save to session
11148
- this.sessionManager.appendMessage(bashMessage);
11144
+ this._emitEntryAppended(this.sessionManager.appendMessage(bashMessage));
11149
11145
  this._incrementMessageRevision();
11150
11146
  }
11151
11147
  this._pendingBashMessages = [];
@@ -11802,4 +11798,18 @@ function messageFingerprint(message) {
11802
11798
  const requestContext = usage?.contextUsage;
11803
11799
  return `${size}:${usage?.input ?? ""}:${usage?.output ?? ""}:${usage?.totalTokens ?? ""}:${requestContext?.input}:${requestContext?.output}:${requestContext === null}`;
11804
11800
  }
11801
+ // Clients see each rung's provider text through the same account-limit description as `lastError`.
11802
+ function describeExhaustedFailures(event, currentProvider) {
11803
+ const describe = (error, provider) => usageLimitScope(error) === "account" ? new AccountUsageLimitError(provider, error).message : error;
11804
+ const failures = event.failures?.map((failure) => ({
11805
+ ...failure,
11806
+ error: describe(failure.error, failure.model.slice(0, Math.max(0, failure.model.indexOf("/")))),
11807
+ }));
11808
+ const primaryError = failures?.[0];
11809
+ return {
11810
+ ...event,
11811
+ lastError: describe(event.lastError, currentProvider),
11812
+ ...(failures === undefined || primaryError === undefined ? {} : { failures, primaryError }),
11813
+ };
11814
+ }
11805
11815
  //# sourceMappingURL=agent-session.js.map
@@ -5,6 +5,7 @@
5
5
  import type { ApiKeyCredential, AuthOperationOptions, Credential, CredentialInfo, CredentialStore, OAuthAuth, OAuthCredential, OAuthLoginCallbacks } from "@earendil-works/pi-ai";
6
6
  import { type CredentialKind } from "@earendil-works/pi-ai/auth/pool/kinds";
7
7
  import { type CredentialSlot } from "@earendil-works/pi-ai/auth/pool/slots";
8
+ import { type AuthStoreLoadError } from "./auth-store-load-error.ts";
8
9
  type AuthStorageData = Record<string, Credential>;
9
10
  /** A credential problem the user should see once per change: `id` changes when the problem does. */
10
11
  export type AuthNotice = {
@@ -101,6 +102,7 @@ export declare class AuthStorage implements CredentialStore {
101
102
  private busyReads;
102
103
  private dataLoaded;
103
104
  private dataFromBusyRead;
105
+ private loadError;
104
106
  private constructor();
105
107
  static create(authPath?: string): AuthStorage;
106
108
  getStoragePath(): string | undefined;
@@ -125,6 +127,10 @@ export declare class AuthStorage implements CredentialStore {
125
127
  */
126
128
  private backupAuthFile;
127
129
  private updateReadState;
130
+ /** The shared read state holds the store's current contents: this instance answers from it again. */
131
+ private adoptReadState;
132
+ /** A sync write that parsed and saved the locked store proves it loads again. */
133
+ private writeLocked;
128
134
  /**
129
135
  * Reload credentials from storage.
130
136
  */
@@ -135,6 +141,17 @@ export declare class AuthStorage implements CredentialStore {
135
141
  * store, or a busy read after a successful load, is not busy.
136
142
  */
137
143
  isCredentialStoreBusy(): boolean;
144
+ /**
145
+ * Why the store could not be loaded on its last read, or undefined once any read or write of this
146
+ * instance finds it good again (senpi#4413). While set, the in-memory credentials are not the store's
147
+ * contents; a store that cannot be read or parsed also refuses every write.
148
+ */
149
+ getLoadError(): AuthStoreLoadError | undefined;
150
+ /**
151
+ * Records a failed read; returns false when it repeats the load error already reported, so a store
152
+ * that stays broken is not re-recorded on every status read that retries it.
153
+ */
154
+ private noteLoadFailure;
138
155
  /**
139
156
  * Monotonic count of reads (sync or async) that found the store locked before it ever
140
157
  * loaded and answered with the empty placeholder. A change across an operation means
@@ -176,6 +193,7 @@ export declare class AuthStorage implements CredentialStore {
176
193
  private noteReplacement;
177
194
  remove(provider: string): void;
178
195
  listSlots(provider: string): CredentialSlot[];
196
+ /** Adding a new slot is a sign-in and stamps a fresh `loginId`; updating an existing slot keeps it (senpi#4099). */
179
197
  setSlot(provider: string, slot: CredentialSlot): void;
180
198
  removeSlot(provider: string, name: string): void;
181
199
  has(provider: string): boolean;
@@ -184,6 +202,7 @@ export declare class AuthStorage implements CredentialStore {
184
202
  getAll(): AuthStorageData;
185
203
  drainErrors(): Error[];
186
204
  private reloadFromStorageAsync;
205
+ private reloadLockedAsync;
187
206
  private readLatestData;
188
207
  read(provider: string, options?: AuthOperationOptions): Promise<Credential | undefined>;
189
208
  readSlot(provider: string, slotName: string, options?: AuthOperationOptions): Promise<Credential | undefined>;
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { findEnvKeys, getEnvApiKey, readByProviderId } from "@earendil-works/pi-ai";
6
6
  import { slotKind, unusedSlotKinds } from "@earendil-works/pi-ai/auth/pool/kinds";
7
+ import { stampLogin } from "@earendil-works/pi-ai/auth/pool/login-id";
7
8
  import { appendLoginSlot, listSlots, projectSlot, removeSlot, repairManagedSentinelSlots, upsertSlot, } from "@earendil-works/pi-ai/auth/pool/slots";
8
9
  import { builtinProviders } from "@earendil-works/pi-ai/providers/all";
9
10
  import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "fs";
@@ -16,6 +17,7 @@ import { stripBom } from "../utils/text.js";
16
17
  import { signOutLegacyClaudeLogins } from "./anthropic-legacy-sign-out.js";
17
18
  import { resolveReadOnlyCredential, resolveStoredCredential, resolveStoredSlot, validateReadOnlyAuthData, } from "./auth-credential-values.js";
18
19
  import { migrateLegacyProviderKeys } from "./auth-provider-key-migration.js";
20
+ import { describeAuthStoreLoadFailure, failureAfterRead, parseAuthDocument, } from "./auth-store-load-error.js";
19
21
  import { ANTHROPIC_SUBSCRIPTION_PROVIDER_ID } from "./extensions/builtin/anthropic-subscription/provider-id.js";
20
22
  import { signInAgainMessage, signInRequiredNames } from "./extensions/builtin/anthropic-subscription/sign-in-again.js";
21
23
  import { lockWithLossHandling } from "./file-lock.js";
@@ -392,7 +394,7 @@ export class AuthStorage {
392
394
  // Mutation paths keep the stored keys as-is: the key migration (and its
393
395
  // pre-write backup) belongs to the load seam, so a write can never drop a
394
396
  // legacy entry without the backup that preserves it.
395
- return repairPoisonedPoolSlots(JSON.parse(stripBom(content))).data;
397
+ return repairPoisonedPoolSlots(parseAuthDocument(content)).data;
396
398
  }
397
399
  /**
398
400
  * Reports whether the parse healed poisoned pool slots or moved legacy
@@ -406,7 +408,7 @@ export class AuthStorage {
406
408
  if (!content) {
407
409
  return { data: {}, repaired: false, migrated: false };
408
410
  }
409
- const repaired = repairPoisonedPoolSlots(JSON.parse(stripBom(content)));
411
+ const repaired = repairPoisonedPoolSlots(parseAuthDocument(content));
410
412
  const migrated = migrateLegacyProviderKeys(repaired.data);
411
413
  const moved = signOutLegacyClaudeLogins(migrated.data);
412
414
  if (moved.skipped && !this.reportedMigrationSkips.has(moved.skipped)) {
@@ -444,19 +446,37 @@ export class AuthStorage {
444
446
  this.data = data;
445
447
  this.dataLoaded = true;
446
448
  this.dataFromBusyRead = false;
449
+ this.loadError = undefined;
447
450
  this.readState.data = data;
448
451
  this.readState.loaded = true;
449
452
  this.readState.revision = revision;
450
453
  }
454
+ /** The shared read state holds the store's current contents: this instance answers from it again. */
455
+ adoptReadState(data) {
456
+ this.data = data;
457
+ this.dataLoaded = true;
458
+ this.dataFromBusyRead = false;
459
+ this.loadError = undefined;
460
+ return data;
461
+ }
462
+ /** A sync write that parsed and saved the locked store proves it loads again. */
463
+ writeLocked(fn) {
464
+ const result = this.storage.withLock(fn);
465
+ this.loadError = undefined;
466
+ return result;
467
+ }
451
468
  /**
452
469
  * Reload credentials from storage.
453
470
  */
454
471
  reload() {
455
472
  let data = {};
456
473
  let revision;
474
+ const load = { parsed: false, writeBack: false };
457
475
  try {
458
476
  this.storage.withLock((current) => {
459
477
  const parsed = this.parseStorageContent(current);
478
+ load.parsed = true;
479
+ load.writeBack = parsed.repaired || parsed.migrated;
460
480
  data = parsed.data;
461
481
  // A written repair or migration invalidates the revision read before
462
482
  // it; leaving it unset makes the next reader re-read instead of
@@ -472,9 +492,17 @@ export class AuthStorage {
472
492
  this.updateReadState(data, revision);
473
493
  return "loaded";
474
494
  }
475
- catch (error) {
476
- // Preserve the last valid in-memory snapshot.
477
- this.recordError(error instanceof Error ? error : new Error(String(error)));
495
+ catch (rawError) {
496
+ const error = failureAfterRead(rawError, load);
497
+ const recorded = this.noteLoadFailure(error);
498
+ // auth.json parsed and only a later step failed (a write-back or a lost lock): its parsed
499
+ // credentials stand, as on the async path. A failure before the parse keeps the last snapshot.
500
+ if (load.parsed) {
501
+ this.updateReadState(data, revision);
502
+ this.loadError = describeAuthStoreLoadFailure(error);
503
+ }
504
+ if (!recorded)
505
+ return "failed";
478
506
  if (!(error instanceof CredentialStoreBusyError))
479
507
  return "failed";
480
508
  if (!this.dataLoaded) {
@@ -492,6 +520,30 @@ export class AuthStorage {
492
520
  isCredentialStoreBusy() {
493
521
  return this.dataFromBusyRead;
494
522
  }
523
+ /**
524
+ * Why the store could not be loaded on its last read, or undefined once any read or write of this
525
+ * instance finds it good again (senpi#4413). While set, the in-memory credentials are not the store's
526
+ * contents; a store that cannot be read or parsed also refuses every write.
527
+ */
528
+ getLoadError() {
529
+ return this.loadError;
530
+ }
531
+ /**
532
+ * Records a failed read; returns false when it repeats the load error already reported, so a store
533
+ * that stays broken is not re-recorded on every status read that retries it.
534
+ */
535
+ noteLoadFailure(error) {
536
+ const loadError = describeAuthStoreLoadFailure(error);
537
+ const repeated = loadError !== undefined &&
538
+ this.loadError?.kind === loadError.kind &&
539
+ this.loadError.reason === loadError.reason;
540
+ if (loadError)
541
+ this.loadError = loadError;
542
+ if (repeated)
543
+ return false;
544
+ this.recordError(error);
545
+ return true;
546
+ }
495
547
  /**
496
548
  * Monotonic count of reads (sync or async) that found the store locked before it ever
497
549
  * loaded and answered with the empty placeholder. A change across an operation means
@@ -524,12 +576,12 @@ export class AuthStorage {
524
576
  * `takeReplacement`, so no credential disappears without the user being told where it went.
525
577
  */
526
578
  set(provider, credential) {
527
- return this.storage.withLock((content) => {
579
+ return this.writeLocked((content) => {
528
580
  const currentData = this.parseStorageData(content);
529
581
  const replacement = this.noteReplacement(provider, currentData[provider], credential, content);
530
- const next = appendLoginSlot(currentData[provider], credential, undefined, {
582
+ const next = stampLogin(appendLoginSlot(currentData[provider], credential, undefined, {
531
583
  accountIdentity: this.oauthAuth(provider)?.accountIdentity,
532
- });
584
+ }));
533
585
  const nextData = { ...currentData, [provider]: next };
534
586
  this.data = nextData;
535
587
  return { result: replacement, next: JSON.stringify(nextData, null, 2) };
@@ -609,7 +661,7 @@ export class AuthStorage {
609
661
  return replacement;
610
662
  }
611
663
  remove(provider) {
612
- this.storage.withLock((content) => {
664
+ this.writeLocked((content) => {
613
665
  const nextData = { ...this.parseStorageData(content) };
614
666
  delete nextData[provider];
615
667
  this.data = nextData;
@@ -619,17 +671,21 @@ export class AuthStorage {
619
671
  listSlots(provider) {
620
672
  return listSlots(readByProviderId(this.data, provider));
621
673
  }
674
+ /** Adding a new slot is a sign-in and stamps a fresh `loginId`; updating an existing slot keeps it (senpi#4099). */
622
675
  setSlot(provider, slot) {
623
- this.storage.withLock((content) => {
676
+ this.writeLocked((content) => {
624
677
  const currentData = this.parseStorageData(content);
625
- const next = upsertSlot(currentData[provider], slot);
678
+ const current = currentData[provider];
679
+ const upserted = upsertSlot(current, slot);
680
+ const adds = !listSlots(current).some((existing) => existing.name === slot.name);
681
+ const next = adds ? stampLogin(upserted) : upserted;
626
682
  const nextData = { ...currentData, [provider]: next };
627
683
  this.data = nextData;
628
684
  return { result: undefined, next: JSON.stringify(nextData, null, 2) };
629
685
  });
630
686
  }
631
687
  removeSlot(provider, name) {
632
- this.storage.withLock((content) => {
688
+ this.writeLocked((content) => {
633
689
  const currentData = this.parseStorageData(content);
634
690
  const next = removeSlot(currentData[provider], name);
635
691
  const nextData = { ...currentData };
@@ -666,16 +722,34 @@ export class AuthStorage {
666
722
  return errors;
667
723
  }
668
724
  async reloadFromStorageAsync(options) {
669
- return this.storage.withLockAsync(async (content) => {
670
- const parsed = this.parseStorageContent(content);
671
- const revision = parsed.repaired || parsed.migrated || !this.authPath ? undefined : getFileContentRevision(this.authPath);
672
- this.updateReadState(parsed.data, revision);
673
- if (parsed.migrated)
674
- this.backupAuthFile(content);
675
- return parsed.repaired || parsed.migrated
676
- ? { result: parsed.data, next: JSON.stringify(parsed.data, null, 2) }
677
- : { result: parsed.data };
678
- }, options);
725
+ try {
726
+ return await this.reloadLockedAsync(options);
727
+ }
728
+ catch (error) {
729
+ if (!options?.signal?.aborted)
730
+ this.loadError = describeAuthStoreLoadFailure(error) ?? this.loadError;
731
+ throw error;
732
+ }
733
+ }
734
+ async reloadLockedAsync(options) {
735
+ const load = { parsed: false, writeBack: false };
736
+ try {
737
+ return await this.storage.withLockAsync(async (content) => {
738
+ const parsed = this.parseStorageContent(content);
739
+ load.parsed = true;
740
+ load.writeBack = parsed.repaired || parsed.migrated;
741
+ const revision = parsed.repaired || parsed.migrated || !this.authPath ? undefined : getFileContentRevision(this.authPath);
742
+ this.updateReadState(parsed.data, revision);
743
+ if (parsed.migrated)
744
+ this.backupAuthFile(content);
745
+ return parsed.repaired || parsed.migrated
746
+ ? { result: parsed.data, next: JSON.stringify(parsed.data, null, 2) }
747
+ : { result: parsed.data };
748
+ }, options);
749
+ }
750
+ catch (error) {
751
+ throw failureAfterRead(error, load);
752
+ }
679
753
  }
680
754
  async readLatestData(options) {
681
755
  options?.signal?.throwIfAborted();
@@ -692,7 +766,7 @@ export class AuthStorage {
692
766
  }
693
767
  const revision = getFileContentRevision(this.authPath);
694
768
  if (revision !== undefined && revision === this.readState.revision)
695
- return this.readState.data;
769
+ return this.adoptReadState(this.readState.data);
696
770
  if (!this.readState.reload) {
697
771
  const controller = new AbortController();
698
772
  const reload = {
@@ -715,10 +789,11 @@ export class AuthStorage {
715
789
  reload.readers++;
716
790
  try {
717
791
  try {
718
- return await raceWithAbortSignal(reload.promise, options?.signal);
792
+ return this.adoptReadState(await raceWithAbortSignal(reload.promise, options?.signal));
719
793
  }
720
794
  catch (error) {
721
795
  options?.signal?.throwIfAborted();
796
+ this.loadError = describeAuthStoreLoadFailure(error) ?? this.loadError;
722
797
  this.noteBusyFallback(error);
723
798
  return this.readState.data;
724
799
  }
@@ -0,0 +1,29 @@
1
+ export type AuthStoreLoadErrorKind = "malformed_json" | "invalid_format" | "unreadable" | "write_back_failed";
2
+ /**
3
+ * Why an existing auth.json could not be loaded (senpi#4413). `reason` is safe to show and send to
4
+ * clients: it never carries the file's path or contents.
5
+ */
6
+ export interface AuthStoreLoadError {
7
+ readonly kind: AuthStoreLoadErrorKind;
8
+ readonly reason: string;
9
+ }
10
+ export declare class AuthStoreUnreadableError extends Error {
11
+ readonly loadError: AuthStoreLoadError;
12
+ constructor(loadError: AuthStoreLoadError);
13
+ }
14
+ /** The top-level object of an auth.json document; anything else is refused rather than read as empty. */
15
+ export declare function parseAuthDocument(content: string): Record<string, unknown>;
16
+ /**
17
+ * The load error a failed store read stands for, or undefined when the failure says nothing about
18
+ * the file: a lock held by another writer, a read the caller aborted, or a lock lost after a clean read.
19
+ */
20
+ export declare function describeAuthStoreLoadFailure(error: unknown): AuthStoreLoadError | undefined;
21
+ /**
22
+ * The error a store load surfaces: once auth.json parsed, a later failure (a write-back or a lost lock)
23
+ * becomes an AuthStoreAfterReadError so it is never reported as an unreadable file. Aborts pass through.
24
+ */
25
+ export declare function failureAfterRead(error: unknown, load: {
26
+ parsed: boolean;
27
+ writeBack: boolean;
28
+ }): unknown;
29
+ //# sourceMappingURL=auth-store-load-error.d.ts.map